162306a36Sopenharmony_ci/* Broadcom NetXtreme-C/E network driver. 262306a36Sopenharmony_ci * 362306a36Sopenharmony_ci * Copyright (c) 2016-2017 Broadcom Limited 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * This program is free software; you can redistribute it and/or modify 662306a36Sopenharmony_ci * it under the terms of the GNU General Public License as published by 762306a36Sopenharmony_ci * the Free Software Foundation. 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci#include <linux/pci.h> 1062306a36Sopenharmony_ci#include <linux/netdevice.h> 1162306a36Sopenharmony_ci#include <linux/etherdevice.h> 1262306a36Sopenharmony_ci#include <linux/rtnetlink.h> 1362306a36Sopenharmony_ci#include <linux/jhash.h> 1462306a36Sopenharmony_ci#include <net/pkt_cls.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include "bnxt_hsi.h" 1762306a36Sopenharmony_ci#include "bnxt.h" 1862306a36Sopenharmony_ci#include "bnxt_hwrm.h" 1962306a36Sopenharmony_ci#include "bnxt_vfr.h" 2062306a36Sopenharmony_ci#include "bnxt_devlink.h" 2162306a36Sopenharmony_ci#include "bnxt_tc.h" 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#ifdef CONFIG_BNXT_SRIOV 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#define CFA_HANDLE_INVALID 0xffff 2662306a36Sopenharmony_ci#define VF_IDX_INVALID 0xffff 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic int hwrm_cfa_vfr_alloc(struct bnxt *bp, u16 vf_idx, 2962306a36Sopenharmony_ci u16 *tx_cfa_action, u16 *rx_cfa_code) 3062306a36Sopenharmony_ci{ 3162306a36Sopenharmony_ci struct hwrm_cfa_vfr_alloc_output *resp; 3262306a36Sopenharmony_ci struct hwrm_cfa_vfr_alloc_input *req; 3362306a36Sopenharmony_ci int rc; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci rc = hwrm_req_init(bp, req, HWRM_CFA_VFR_ALLOC); 3662306a36Sopenharmony_ci if (!rc) { 3762306a36Sopenharmony_ci req->vf_id = cpu_to_le16(vf_idx); 3862306a36Sopenharmony_ci sprintf(req->vfr_name, "vfr%d", vf_idx); 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci resp = hwrm_req_hold(bp, req); 4162306a36Sopenharmony_ci rc = hwrm_req_send(bp, req); 4262306a36Sopenharmony_ci if (!rc) { 4362306a36Sopenharmony_ci *tx_cfa_action = le16_to_cpu(resp->tx_cfa_action); 4462306a36Sopenharmony_ci *rx_cfa_code = le16_to_cpu(resp->rx_cfa_code); 4562306a36Sopenharmony_ci netdev_dbg(bp->dev, "tx_cfa_action=0x%x, rx_cfa_code=0x%x", 4662306a36Sopenharmony_ci *tx_cfa_action, *rx_cfa_code); 4762306a36Sopenharmony_ci } 4862306a36Sopenharmony_ci hwrm_req_drop(bp, req); 4962306a36Sopenharmony_ci } 5062306a36Sopenharmony_ci if (rc) 5162306a36Sopenharmony_ci netdev_info(bp->dev, "%s error rc=%d\n", __func__, rc); 5262306a36Sopenharmony_ci return rc; 5362306a36Sopenharmony_ci} 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic int hwrm_cfa_vfr_free(struct bnxt *bp, u16 vf_idx) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci struct hwrm_cfa_vfr_free_input *req; 5862306a36Sopenharmony_ci int rc; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci rc = hwrm_req_init(bp, req, HWRM_CFA_VFR_FREE); 6162306a36Sopenharmony_ci if (!rc) { 6262306a36Sopenharmony_ci sprintf(req->vfr_name, "vfr%d", vf_idx); 6362306a36Sopenharmony_ci rc = hwrm_req_send(bp, req); 6462306a36Sopenharmony_ci } 6562306a36Sopenharmony_ci if (rc) 6662306a36Sopenharmony_ci netdev_info(bp->dev, "%s error rc=%d\n", __func__, rc); 6762306a36Sopenharmony_ci return rc; 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic int bnxt_hwrm_vfr_qcfg(struct bnxt *bp, struct bnxt_vf_rep *vf_rep, 7162306a36Sopenharmony_ci u16 *max_mtu) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci struct hwrm_func_qcfg_output *resp; 7462306a36Sopenharmony_ci struct hwrm_func_qcfg_input *req; 7562306a36Sopenharmony_ci u16 mtu; 7662306a36Sopenharmony_ci int rc; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci rc = hwrm_req_init(bp, req, HWRM_FUNC_QCFG); 7962306a36Sopenharmony_ci if (rc) 8062306a36Sopenharmony_ci return rc; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci req->fid = cpu_to_le16(bp->pf.vf[vf_rep->vf_idx].fw_fid); 8362306a36Sopenharmony_ci resp = hwrm_req_hold(bp, req); 8462306a36Sopenharmony_ci rc = hwrm_req_send(bp, req); 8562306a36Sopenharmony_ci if (!rc) { 8662306a36Sopenharmony_ci mtu = le16_to_cpu(resp->max_mtu_configured); 8762306a36Sopenharmony_ci if (!mtu) 8862306a36Sopenharmony_ci *max_mtu = BNXT_MAX_MTU; 8962306a36Sopenharmony_ci else 9062306a36Sopenharmony_ci *max_mtu = mtu; 9162306a36Sopenharmony_ci } 9262306a36Sopenharmony_ci hwrm_req_drop(bp, req); 9362306a36Sopenharmony_ci return rc; 9462306a36Sopenharmony_ci} 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistatic int bnxt_vf_rep_open(struct net_device *dev) 9762306a36Sopenharmony_ci{ 9862306a36Sopenharmony_ci struct bnxt_vf_rep *vf_rep = netdev_priv(dev); 9962306a36Sopenharmony_ci struct bnxt *bp = vf_rep->bp; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci /* Enable link and TX only if the parent PF is open. */ 10262306a36Sopenharmony_ci if (netif_running(bp->dev)) { 10362306a36Sopenharmony_ci netif_carrier_on(dev); 10462306a36Sopenharmony_ci netif_tx_start_all_queues(dev); 10562306a36Sopenharmony_ci } 10662306a36Sopenharmony_ci return 0; 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_cistatic int bnxt_vf_rep_close(struct net_device *dev) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci netif_carrier_off(dev); 11262306a36Sopenharmony_ci netif_tx_disable(dev); 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci return 0; 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic netdev_tx_t bnxt_vf_rep_xmit(struct sk_buff *skb, 11862306a36Sopenharmony_ci struct net_device *dev) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci struct bnxt_vf_rep *vf_rep = netdev_priv(dev); 12162306a36Sopenharmony_ci int rc, len = skb->len; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci skb_dst_drop(skb); 12462306a36Sopenharmony_ci dst_hold((struct dst_entry *)vf_rep->dst); 12562306a36Sopenharmony_ci skb_dst_set(skb, (struct dst_entry *)vf_rep->dst); 12662306a36Sopenharmony_ci skb->dev = vf_rep->dst->u.port_info.lower_dev; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci rc = dev_queue_xmit(skb); 12962306a36Sopenharmony_ci if (!rc) { 13062306a36Sopenharmony_ci vf_rep->tx_stats.packets++; 13162306a36Sopenharmony_ci vf_rep->tx_stats.bytes += len; 13262306a36Sopenharmony_ci } 13362306a36Sopenharmony_ci return rc; 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cistatic void 13762306a36Sopenharmony_cibnxt_vf_rep_get_stats64(struct net_device *dev, 13862306a36Sopenharmony_ci struct rtnl_link_stats64 *stats) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci struct bnxt_vf_rep *vf_rep = netdev_priv(dev); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci stats->rx_packets = vf_rep->rx_stats.packets; 14362306a36Sopenharmony_ci stats->rx_bytes = vf_rep->rx_stats.bytes; 14462306a36Sopenharmony_ci stats->tx_packets = vf_rep->tx_stats.packets; 14562306a36Sopenharmony_ci stats->tx_bytes = vf_rep->tx_stats.bytes; 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_cistatic int bnxt_vf_rep_setup_tc_block_cb(enum tc_setup_type type, 14962306a36Sopenharmony_ci void *type_data, 15062306a36Sopenharmony_ci void *cb_priv) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci struct bnxt_vf_rep *vf_rep = cb_priv; 15362306a36Sopenharmony_ci struct bnxt *bp = vf_rep->bp; 15462306a36Sopenharmony_ci int vf_fid = bp->pf.vf[vf_rep->vf_idx].fw_fid; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci if (!bnxt_tc_flower_enabled(vf_rep->bp) || 15762306a36Sopenharmony_ci !tc_cls_can_offload_and_chain0(bp->dev, type_data)) 15862306a36Sopenharmony_ci return -EOPNOTSUPP; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci switch (type) { 16162306a36Sopenharmony_ci case TC_SETUP_CLSFLOWER: 16262306a36Sopenharmony_ci return bnxt_tc_setup_flower(bp, vf_fid, type_data); 16362306a36Sopenharmony_ci default: 16462306a36Sopenharmony_ci return -EOPNOTSUPP; 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_cistatic LIST_HEAD(bnxt_vf_block_cb_list); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_cistatic int bnxt_vf_rep_setup_tc(struct net_device *dev, enum tc_setup_type type, 17162306a36Sopenharmony_ci void *type_data) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci struct bnxt_vf_rep *vf_rep = netdev_priv(dev); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci switch (type) { 17662306a36Sopenharmony_ci case TC_SETUP_BLOCK: 17762306a36Sopenharmony_ci return flow_block_cb_setup_simple(type_data, 17862306a36Sopenharmony_ci &bnxt_vf_block_cb_list, 17962306a36Sopenharmony_ci bnxt_vf_rep_setup_tc_block_cb, 18062306a36Sopenharmony_ci vf_rep, vf_rep, true); 18162306a36Sopenharmony_ci default: 18262306a36Sopenharmony_ci return -EOPNOTSUPP; 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci} 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_cistruct net_device *bnxt_get_vf_rep(struct bnxt *bp, u16 cfa_code) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci u16 vf_idx; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci if (cfa_code && bp->cfa_code_map && BNXT_PF(bp)) { 19162306a36Sopenharmony_ci vf_idx = bp->cfa_code_map[cfa_code]; 19262306a36Sopenharmony_ci if (vf_idx != VF_IDX_INVALID) 19362306a36Sopenharmony_ci return bp->vf_reps[vf_idx]->dev; 19462306a36Sopenharmony_ci } 19562306a36Sopenharmony_ci return NULL; 19662306a36Sopenharmony_ci} 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_civoid bnxt_vf_rep_rx(struct bnxt *bp, struct sk_buff *skb) 19962306a36Sopenharmony_ci{ 20062306a36Sopenharmony_ci struct bnxt_vf_rep *vf_rep = netdev_priv(skb->dev); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci vf_rep->rx_stats.bytes += skb->len; 20362306a36Sopenharmony_ci vf_rep->rx_stats.packets++; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci netif_receive_skb(skb); 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_cistatic int bnxt_vf_rep_get_phys_port_name(struct net_device *dev, char *buf, 20962306a36Sopenharmony_ci size_t len) 21062306a36Sopenharmony_ci{ 21162306a36Sopenharmony_ci struct bnxt_vf_rep *vf_rep = netdev_priv(dev); 21262306a36Sopenharmony_ci struct pci_dev *pf_pdev = vf_rep->bp->pdev; 21362306a36Sopenharmony_ci int rc; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci rc = snprintf(buf, len, "pf%dvf%d", PCI_FUNC(pf_pdev->devfn), 21662306a36Sopenharmony_ci vf_rep->vf_idx); 21762306a36Sopenharmony_ci if (rc >= len) 21862306a36Sopenharmony_ci return -EOPNOTSUPP; 21962306a36Sopenharmony_ci return 0; 22062306a36Sopenharmony_ci} 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_cistatic void bnxt_vf_rep_get_drvinfo(struct net_device *dev, 22362306a36Sopenharmony_ci struct ethtool_drvinfo *info) 22462306a36Sopenharmony_ci{ 22562306a36Sopenharmony_ci strscpy(info->driver, DRV_MODULE_NAME, sizeof(info->driver)); 22662306a36Sopenharmony_ci} 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_cistatic int bnxt_vf_rep_get_port_parent_id(struct net_device *dev, 22962306a36Sopenharmony_ci struct netdev_phys_item_id *ppid) 23062306a36Sopenharmony_ci{ 23162306a36Sopenharmony_ci struct bnxt_vf_rep *vf_rep = netdev_priv(dev); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci /* as only PORT_PARENT_ID is supported currently use common code 23462306a36Sopenharmony_ci * between PF and VF-rep for now. 23562306a36Sopenharmony_ci */ 23662306a36Sopenharmony_ci return bnxt_get_port_parent_id(vf_rep->bp->dev, ppid); 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_cistatic const struct ethtool_ops bnxt_vf_rep_ethtool_ops = { 24062306a36Sopenharmony_ci .get_drvinfo = bnxt_vf_rep_get_drvinfo 24162306a36Sopenharmony_ci}; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_cistatic const struct net_device_ops bnxt_vf_rep_netdev_ops = { 24462306a36Sopenharmony_ci .ndo_open = bnxt_vf_rep_open, 24562306a36Sopenharmony_ci .ndo_stop = bnxt_vf_rep_close, 24662306a36Sopenharmony_ci .ndo_start_xmit = bnxt_vf_rep_xmit, 24762306a36Sopenharmony_ci .ndo_get_stats64 = bnxt_vf_rep_get_stats64, 24862306a36Sopenharmony_ci .ndo_setup_tc = bnxt_vf_rep_setup_tc, 24962306a36Sopenharmony_ci .ndo_get_port_parent_id = bnxt_vf_rep_get_port_parent_id, 25062306a36Sopenharmony_ci .ndo_get_phys_port_name = bnxt_vf_rep_get_phys_port_name 25162306a36Sopenharmony_ci}; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_cibool bnxt_dev_is_vf_rep(struct net_device *dev) 25462306a36Sopenharmony_ci{ 25562306a36Sopenharmony_ci return dev->netdev_ops == &bnxt_vf_rep_netdev_ops; 25662306a36Sopenharmony_ci} 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci/* Called when the parent PF interface is closed: 25962306a36Sopenharmony_ci * As the mode transition from SWITCHDEV to LEGACY 26062306a36Sopenharmony_ci * happens under the rtnl_lock() this routine is safe 26162306a36Sopenharmony_ci * under the rtnl_lock() 26262306a36Sopenharmony_ci */ 26362306a36Sopenharmony_civoid bnxt_vf_reps_close(struct bnxt *bp) 26462306a36Sopenharmony_ci{ 26562306a36Sopenharmony_ci struct bnxt_vf_rep *vf_rep; 26662306a36Sopenharmony_ci u16 num_vfs, i; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci if (bp->eswitch_mode != DEVLINK_ESWITCH_MODE_SWITCHDEV) 26962306a36Sopenharmony_ci return; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci num_vfs = pci_num_vf(bp->pdev); 27262306a36Sopenharmony_ci for (i = 0; i < num_vfs; i++) { 27362306a36Sopenharmony_ci vf_rep = bp->vf_reps[i]; 27462306a36Sopenharmony_ci if (netif_running(vf_rep->dev)) 27562306a36Sopenharmony_ci bnxt_vf_rep_close(vf_rep->dev); 27662306a36Sopenharmony_ci } 27762306a36Sopenharmony_ci} 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci/* Called when the parent PF interface is opened (re-opened): 28062306a36Sopenharmony_ci * As the mode transition from SWITCHDEV to LEGACY 28162306a36Sopenharmony_ci * happen under the rtnl_lock() this routine is safe 28262306a36Sopenharmony_ci * under the rtnl_lock() 28362306a36Sopenharmony_ci */ 28462306a36Sopenharmony_civoid bnxt_vf_reps_open(struct bnxt *bp) 28562306a36Sopenharmony_ci{ 28662306a36Sopenharmony_ci int i; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci if (bp->eswitch_mode != DEVLINK_ESWITCH_MODE_SWITCHDEV) 28962306a36Sopenharmony_ci return; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci for (i = 0; i < pci_num_vf(bp->pdev); i++) { 29262306a36Sopenharmony_ci /* Open the VF-Rep only if it is allocated in the FW */ 29362306a36Sopenharmony_ci if (bp->vf_reps[i]->tx_cfa_action != CFA_HANDLE_INVALID) 29462306a36Sopenharmony_ci bnxt_vf_rep_open(bp->vf_reps[i]->dev); 29562306a36Sopenharmony_ci } 29662306a36Sopenharmony_ci} 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_cistatic void __bnxt_free_one_vf_rep(struct bnxt *bp, struct bnxt_vf_rep *vf_rep) 29962306a36Sopenharmony_ci{ 30062306a36Sopenharmony_ci if (!vf_rep) 30162306a36Sopenharmony_ci return; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci if (vf_rep->dst) { 30462306a36Sopenharmony_ci dst_release((struct dst_entry *)vf_rep->dst); 30562306a36Sopenharmony_ci vf_rep->dst = NULL; 30662306a36Sopenharmony_ci } 30762306a36Sopenharmony_ci if (vf_rep->tx_cfa_action != CFA_HANDLE_INVALID) { 30862306a36Sopenharmony_ci hwrm_cfa_vfr_free(bp, vf_rep->vf_idx); 30962306a36Sopenharmony_ci vf_rep->tx_cfa_action = CFA_HANDLE_INVALID; 31062306a36Sopenharmony_ci } 31162306a36Sopenharmony_ci} 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_cistatic void __bnxt_vf_reps_destroy(struct bnxt *bp) 31462306a36Sopenharmony_ci{ 31562306a36Sopenharmony_ci u16 num_vfs = pci_num_vf(bp->pdev); 31662306a36Sopenharmony_ci struct bnxt_vf_rep *vf_rep; 31762306a36Sopenharmony_ci int i; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci for (i = 0; i < num_vfs; i++) { 32062306a36Sopenharmony_ci vf_rep = bp->vf_reps[i]; 32162306a36Sopenharmony_ci if (vf_rep) { 32262306a36Sopenharmony_ci __bnxt_free_one_vf_rep(bp, vf_rep); 32362306a36Sopenharmony_ci if (vf_rep->dev) { 32462306a36Sopenharmony_ci /* if register_netdev failed, then netdev_ops 32562306a36Sopenharmony_ci * would have been set to NULL 32662306a36Sopenharmony_ci */ 32762306a36Sopenharmony_ci if (vf_rep->dev->netdev_ops) 32862306a36Sopenharmony_ci unregister_netdev(vf_rep->dev); 32962306a36Sopenharmony_ci free_netdev(vf_rep->dev); 33062306a36Sopenharmony_ci } 33162306a36Sopenharmony_ci } 33262306a36Sopenharmony_ci } 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci kfree(bp->vf_reps); 33562306a36Sopenharmony_ci bp->vf_reps = NULL; 33662306a36Sopenharmony_ci} 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_civoid bnxt_vf_reps_destroy(struct bnxt *bp) 33962306a36Sopenharmony_ci{ 34062306a36Sopenharmony_ci bool closed = false; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci if (bp->eswitch_mode != DEVLINK_ESWITCH_MODE_SWITCHDEV) 34362306a36Sopenharmony_ci return; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci if (!bp->vf_reps) 34662306a36Sopenharmony_ci return; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci /* Ensure that parent PF's and VF-reps' RX/TX has been quiesced 34962306a36Sopenharmony_ci * before proceeding with VF-rep cleanup. 35062306a36Sopenharmony_ci */ 35162306a36Sopenharmony_ci rtnl_lock(); 35262306a36Sopenharmony_ci if (netif_running(bp->dev)) { 35362306a36Sopenharmony_ci bnxt_close_nic(bp, false, false); 35462306a36Sopenharmony_ci closed = true; 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci /* un-publish cfa_code_map so that RX path can't see it anymore */ 35762306a36Sopenharmony_ci kfree(bp->cfa_code_map); 35862306a36Sopenharmony_ci bp->cfa_code_map = NULL; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci if (closed) { 36162306a36Sopenharmony_ci /* Temporarily set legacy mode to avoid re-opening 36262306a36Sopenharmony_ci * representors and restore switchdev mode after that. 36362306a36Sopenharmony_ci */ 36462306a36Sopenharmony_ci bp->eswitch_mode = DEVLINK_ESWITCH_MODE_LEGACY; 36562306a36Sopenharmony_ci bnxt_open_nic(bp, false, false); 36662306a36Sopenharmony_ci bp->eswitch_mode = DEVLINK_ESWITCH_MODE_SWITCHDEV; 36762306a36Sopenharmony_ci } 36862306a36Sopenharmony_ci rtnl_unlock(); 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci /* Need to call vf_reps_destroy() outside of rntl_lock 37162306a36Sopenharmony_ci * as unregister_netdev takes rtnl_lock 37262306a36Sopenharmony_ci */ 37362306a36Sopenharmony_ci __bnxt_vf_reps_destroy(bp); 37462306a36Sopenharmony_ci} 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci/* Free the VF-Reps in firmware, during firmware hot-reset processing. 37762306a36Sopenharmony_ci * Note that the VF-Rep netdevs are still active (not unregistered) during 37862306a36Sopenharmony_ci * this process. As the mode transition from SWITCHDEV to LEGACY happens 37962306a36Sopenharmony_ci * under the rtnl_lock() this routine is safe under the rtnl_lock(). 38062306a36Sopenharmony_ci */ 38162306a36Sopenharmony_civoid bnxt_vf_reps_free(struct bnxt *bp) 38262306a36Sopenharmony_ci{ 38362306a36Sopenharmony_ci u16 num_vfs = pci_num_vf(bp->pdev); 38462306a36Sopenharmony_ci int i; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci if (bp->eswitch_mode != DEVLINK_ESWITCH_MODE_SWITCHDEV) 38762306a36Sopenharmony_ci return; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci for (i = 0; i < num_vfs; i++) 39062306a36Sopenharmony_ci __bnxt_free_one_vf_rep(bp, bp->vf_reps[i]); 39162306a36Sopenharmony_ci} 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_cistatic int bnxt_alloc_vf_rep(struct bnxt *bp, struct bnxt_vf_rep *vf_rep, 39462306a36Sopenharmony_ci u16 *cfa_code_map) 39562306a36Sopenharmony_ci{ 39662306a36Sopenharmony_ci /* get cfa handles from FW */ 39762306a36Sopenharmony_ci if (hwrm_cfa_vfr_alloc(bp, vf_rep->vf_idx, &vf_rep->tx_cfa_action, 39862306a36Sopenharmony_ci &vf_rep->rx_cfa_code)) 39962306a36Sopenharmony_ci return -ENOLINK; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci cfa_code_map[vf_rep->rx_cfa_code] = vf_rep->vf_idx; 40262306a36Sopenharmony_ci vf_rep->dst = metadata_dst_alloc(0, METADATA_HW_PORT_MUX, GFP_KERNEL); 40362306a36Sopenharmony_ci if (!vf_rep->dst) 40462306a36Sopenharmony_ci return -ENOMEM; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci /* only cfa_action is needed to mux a packet while TXing */ 40762306a36Sopenharmony_ci vf_rep->dst->u.port_info.port_id = vf_rep->tx_cfa_action; 40862306a36Sopenharmony_ci vf_rep->dst->u.port_info.lower_dev = bp->dev; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci return 0; 41162306a36Sopenharmony_ci} 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci/* Allocate the VF-Reps in firmware, during firmware hot-reset processing. 41462306a36Sopenharmony_ci * Note that the VF-Rep netdevs are still active (not unregistered) during 41562306a36Sopenharmony_ci * this process. As the mode transition from SWITCHDEV to LEGACY happens 41662306a36Sopenharmony_ci * under the rtnl_lock() this routine is safe under the rtnl_lock(). 41762306a36Sopenharmony_ci */ 41862306a36Sopenharmony_ciint bnxt_vf_reps_alloc(struct bnxt *bp) 41962306a36Sopenharmony_ci{ 42062306a36Sopenharmony_ci u16 *cfa_code_map = bp->cfa_code_map, num_vfs = pci_num_vf(bp->pdev); 42162306a36Sopenharmony_ci struct bnxt_vf_rep *vf_rep; 42262306a36Sopenharmony_ci int rc, i; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci if (bp->eswitch_mode != DEVLINK_ESWITCH_MODE_SWITCHDEV) 42562306a36Sopenharmony_ci return 0; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci if (!cfa_code_map) 42862306a36Sopenharmony_ci return -EINVAL; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci for (i = 0; i < MAX_CFA_CODE; i++) 43162306a36Sopenharmony_ci cfa_code_map[i] = VF_IDX_INVALID; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci for (i = 0; i < num_vfs; i++) { 43462306a36Sopenharmony_ci vf_rep = bp->vf_reps[i]; 43562306a36Sopenharmony_ci vf_rep->vf_idx = i; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci rc = bnxt_alloc_vf_rep(bp, vf_rep, cfa_code_map); 43862306a36Sopenharmony_ci if (rc) 43962306a36Sopenharmony_ci goto err; 44062306a36Sopenharmony_ci } 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci return 0; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_cierr: 44562306a36Sopenharmony_ci netdev_info(bp->dev, "%s error=%d\n", __func__, rc); 44662306a36Sopenharmony_ci bnxt_vf_reps_free(bp); 44762306a36Sopenharmony_ci return rc; 44862306a36Sopenharmony_ci} 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci/* Use the OUI of the PF's perm addr and report the same mac addr 45162306a36Sopenharmony_ci * for the same VF-rep each time 45262306a36Sopenharmony_ci */ 45362306a36Sopenharmony_cistatic void bnxt_vf_rep_eth_addr_gen(u8 *src_mac, u16 vf_idx, u8 *mac) 45462306a36Sopenharmony_ci{ 45562306a36Sopenharmony_ci u32 addr; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci ether_addr_copy(mac, src_mac); 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci addr = jhash(src_mac, ETH_ALEN, 0) + vf_idx; 46062306a36Sopenharmony_ci mac[3] = (u8)(addr & 0xFF); 46162306a36Sopenharmony_ci mac[4] = (u8)((addr >> 8) & 0xFF); 46262306a36Sopenharmony_ci mac[5] = (u8)((addr >> 16) & 0xFF); 46362306a36Sopenharmony_ci} 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_cistatic void bnxt_vf_rep_netdev_init(struct bnxt *bp, struct bnxt_vf_rep *vf_rep, 46662306a36Sopenharmony_ci struct net_device *dev) 46762306a36Sopenharmony_ci{ 46862306a36Sopenharmony_ci struct net_device *pf_dev = bp->dev; 46962306a36Sopenharmony_ci u16 max_mtu; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci SET_NETDEV_DEV(dev, &bp->pdev->dev); 47262306a36Sopenharmony_ci dev->netdev_ops = &bnxt_vf_rep_netdev_ops; 47362306a36Sopenharmony_ci dev->ethtool_ops = &bnxt_vf_rep_ethtool_ops; 47462306a36Sopenharmony_ci /* Just inherit all the featues of the parent PF as the VF-R 47562306a36Sopenharmony_ci * uses the RX/TX rings of the parent PF 47662306a36Sopenharmony_ci */ 47762306a36Sopenharmony_ci dev->hw_features = pf_dev->hw_features; 47862306a36Sopenharmony_ci dev->gso_partial_features = pf_dev->gso_partial_features; 47962306a36Sopenharmony_ci dev->vlan_features = pf_dev->vlan_features; 48062306a36Sopenharmony_ci dev->hw_enc_features = pf_dev->hw_enc_features; 48162306a36Sopenharmony_ci dev->features |= pf_dev->features; 48262306a36Sopenharmony_ci bnxt_vf_rep_eth_addr_gen(bp->pf.mac_addr, vf_rep->vf_idx, 48362306a36Sopenharmony_ci dev->perm_addr); 48462306a36Sopenharmony_ci eth_hw_addr_set(dev, dev->perm_addr); 48562306a36Sopenharmony_ci /* Set VF-Rep's max-mtu to the corresponding VF's max-mtu */ 48662306a36Sopenharmony_ci if (!bnxt_hwrm_vfr_qcfg(bp, vf_rep, &max_mtu)) 48762306a36Sopenharmony_ci dev->max_mtu = max_mtu; 48862306a36Sopenharmony_ci dev->min_mtu = ETH_ZLEN; 48962306a36Sopenharmony_ci} 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ciint bnxt_vf_reps_create(struct bnxt *bp) 49262306a36Sopenharmony_ci{ 49362306a36Sopenharmony_ci u16 *cfa_code_map = NULL, num_vfs = pci_num_vf(bp->pdev); 49462306a36Sopenharmony_ci struct bnxt_vf_rep *vf_rep; 49562306a36Sopenharmony_ci struct net_device *dev; 49662306a36Sopenharmony_ci int rc, i; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci if (!(bp->flags & BNXT_FLAG_DSN_VALID)) 49962306a36Sopenharmony_ci return -ENODEV; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci bp->vf_reps = kcalloc(num_vfs, sizeof(vf_rep), GFP_KERNEL); 50262306a36Sopenharmony_ci if (!bp->vf_reps) 50362306a36Sopenharmony_ci return -ENOMEM; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci /* storage for cfa_code to vf-idx mapping */ 50662306a36Sopenharmony_ci cfa_code_map = kmalloc_array(MAX_CFA_CODE, sizeof(*bp->cfa_code_map), 50762306a36Sopenharmony_ci GFP_KERNEL); 50862306a36Sopenharmony_ci if (!cfa_code_map) { 50962306a36Sopenharmony_ci rc = -ENOMEM; 51062306a36Sopenharmony_ci goto err; 51162306a36Sopenharmony_ci } 51262306a36Sopenharmony_ci for (i = 0; i < MAX_CFA_CODE; i++) 51362306a36Sopenharmony_ci cfa_code_map[i] = VF_IDX_INVALID; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci for (i = 0; i < num_vfs; i++) { 51662306a36Sopenharmony_ci dev = alloc_etherdev(sizeof(*vf_rep)); 51762306a36Sopenharmony_ci if (!dev) { 51862306a36Sopenharmony_ci rc = -ENOMEM; 51962306a36Sopenharmony_ci goto err; 52062306a36Sopenharmony_ci } 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci vf_rep = netdev_priv(dev); 52362306a36Sopenharmony_ci bp->vf_reps[i] = vf_rep; 52462306a36Sopenharmony_ci vf_rep->dev = dev; 52562306a36Sopenharmony_ci vf_rep->bp = bp; 52662306a36Sopenharmony_ci vf_rep->vf_idx = i; 52762306a36Sopenharmony_ci vf_rep->tx_cfa_action = CFA_HANDLE_INVALID; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci rc = bnxt_alloc_vf_rep(bp, vf_rep, cfa_code_map); 53062306a36Sopenharmony_ci if (rc) 53162306a36Sopenharmony_ci goto err; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci bnxt_vf_rep_netdev_init(bp, vf_rep, dev); 53462306a36Sopenharmony_ci rc = register_netdev(dev); 53562306a36Sopenharmony_ci if (rc) { 53662306a36Sopenharmony_ci /* no need for unregister_netdev in cleanup */ 53762306a36Sopenharmony_ci dev->netdev_ops = NULL; 53862306a36Sopenharmony_ci goto err; 53962306a36Sopenharmony_ci } 54062306a36Sopenharmony_ci } 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci /* publish cfa_code_map only after all VF-reps have been initialized */ 54362306a36Sopenharmony_ci bp->cfa_code_map = cfa_code_map; 54462306a36Sopenharmony_ci netif_keep_dst(bp->dev); 54562306a36Sopenharmony_ci return 0; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_cierr: 54862306a36Sopenharmony_ci netdev_info(bp->dev, "%s error=%d\n", __func__, rc); 54962306a36Sopenharmony_ci kfree(cfa_code_map); 55062306a36Sopenharmony_ci __bnxt_vf_reps_destroy(bp); 55162306a36Sopenharmony_ci return rc; 55262306a36Sopenharmony_ci} 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci/* Devlink related routines */ 55562306a36Sopenharmony_ciint bnxt_dl_eswitch_mode_get(struct devlink *devlink, u16 *mode) 55662306a36Sopenharmony_ci{ 55762306a36Sopenharmony_ci struct bnxt *bp = bnxt_get_bp_from_dl(devlink); 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci *mode = bp->eswitch_mode; 56062306a36Sopenharmony_ci return 0; 56162306a36Sopenharmony_ci} 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ciint bnxt_dl_eswitch_mode_set(struct devlink *devlink, u16 mode, 56462306a36Sopenharmony_ci struct netlink_ext_ack *extack) 56562306a36Sopenharmony_ci{ 56662306a36Sopenharmony_ci struct bnxt *bp = bnxt_get_bp_from_dl(devlink); 56762306a36Sopenharmony_ci int ret = 0; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci if (bp->eswitch_mode == mode) { 57062306a36Sopenharmony_ci netdev_info(bp->dev, "already in %s eswitch mode\n", 57162306a36Sopenharmony_ci mode == DEVLINK_ESWITCH_MODE_LEGACY ? 57262306a36Sopenharmony_ci "legacy" : "switchdev"); 57362306a36Sopenharmony_ci return -EINVAL; 57462306a36Sopenharmony_ci } 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci switch (mode) { 57762306a36Sopenharmony_ci case DEVLINK_ESWITCH_MODE_LEGACY: 57862306a36Sopenharmony_ci bnxt_vf_reps_destroy(bp); 57962306a36Sopenharmony_ci break; 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci case DEVLINK_ESWITCH_MODE_SWITCHDEV: 58262306a36Sopenharmony_ci if (bp->hwrm_spec_code < 0x10803) { 58362306a36Sopenharmony_ci netdev_warn(bp->dev, "FW does not support SRIOV E-Switch SWITCHDEV mode\n"); 58462306a36Sopenharmony_ci return -ENOTSUPP; 58562306a36Sopenharmony_ci } 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci /* Create representors for existing VFs */ 58862306a36Sopenharmony_ci if (pci_num_vf(bp->pdev) > 0) 58962306a36Sopenharmony_ci ret = bnxt_vf_reps_create(bp); 59062306a36Sopenharmony_ci break; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci default: 59362306a36Sopenharmony_ci return -EINVAL; 59462306a36Sopenharmony_ci } 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci if (!ret) 59762306a36Sopenharmony_ci bp->eswitch_mode = mode; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci return ret; 60062306a36Sopenharmony_ci} 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci#endif 603