18c2ecf20Sopenharmony_ci/* Broadcom NetXtreme-C/E network driver. 28c2ecf20Sopenharmony_ci * 38c2ecf20Sopenharmony_ci * Copyright (c) 2016-2017 Broadcom Limited 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify 68c2ecf20Sopenharmony_ci * it under the terms of the GNU General Public License as published by 78c2ecf20Sopenharmony_ci * the Free Software Foundation. 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci#include <linux/pci.h> 108c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 118c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 128c2ecf20Sopenharmony_ci#include <linux/rtnetlink.h> 138c2ecf20Sopenharmony_ci#include <linux/jhash.h> 148c2ecf20Sopenharmony_ci#include <net/pkt_cls.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include "bnxt_hsi.h" 178c2ecf20Sopenharmony_ci#include "bnxt.h" 188c2ecf20Sopenharmony_ci#include "bnxt_vfr.h" 198c2ecf20Sopenharmony_ci#include "bnxt_devlink.h" 208c2ecf20Sopenharmony_ci#include "bnxt_tc.h" 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#ifdef CONFIG_BNXT_SRIOV 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#define CFA_HANDLE_INVALID 0xffff 258c2ecf20Sopenharmony_ci#define VF_IDX_INVALID 0xffff 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic int hwrm_cfa_vfr_alloc(struct bnxt *bp, u16 vf_idx, 288c2ecf20Sopenharmony_ci u16 *tx_cfa_action, u16 *rx_cfa_code) 298c2ecf20Sopenharmony_ci{ 308c2ecf20Sopenharmony_ci struct hwrm_cfa_vfr_alloc_output *resp = bp->hwrm_cmd_resp_addr; 318c2ecf20Sopenharmony_ci struct hwrm_cfa_vfr_alloc_input req = { 0 }; 328c2ecf20Sopenharmony_ci int rc; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_CFA_VFR_ALLOC, -1, -1); 358c2ecf20Sopenharmony_ci req.vf_id = cpu_to_le16(vf_idx); 368c2ecf20Sopenharmony_ci sprintf(req.vfr_name, "vfr%d", vf_idx); 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci mutex_lock(&bp->hwrm_cmd_lock); 398c2ecf20Sopenharmony_ci rc = _hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); 408c2ecf20Sopenharmony_ci if (!rc) { 418c2ecf20Sopenharmony_ci *tx_cfa_action = le16_to_cpu(resp->tx_cfa_action); 428c2ecf20Sopenharmony_ci *rx_cfa_code = le16_to_cpu(resp->rx_cfa_code); 438c2ecf20Sopenharmony_ci netdev_dbg(bp->dev, "tx_cfa_action=0x%x, rx_cfa_code=0x%x", 448c2ecf20Sopenharmony_ci *tx_cfa_action, *rx_cfa_code); 458c2ecf20Sopenharmony_ci } else { 468c2ecf20Sopenharmony_ci netdev_info(bp->dev, "%s error rc=%d\n", __func__, rc); 478c2ecf20Sopenharmony_ci } 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci mutex_unlock(&bp->hwrm_cmd_lock); 508c2ecf20Sopenharmony_ci return rc; 518c2ecf20Sopenharmony_ci} 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic int hwrm_cfa_vfr_free(struct bnxt *bp, u16 vf_idx) 548c2ecf20Sopenharmony_ci{ 558c2ecf20Sopenharmony_ci struct hwrm_cfa_vfr_free_input req = { 0 }; 568c2ecf20Sopenharmony_ci int rc; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_CFA_VFR_FREE, -1, -1); 598c2ecf20Sopenharmony_ci sprintf(req.vfr_name, "vfr%d", vf_idx); 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci rc = hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); 628c2ecf20Sopenharmony_ci if (rc) 638c2ecf20Sopenharmony_ci netdev_info(bp->dev, "%s error rc=%d\n", __func__, rc); 648c2ecf20Sopenharmony_ci return rc; 658c2ecf20Sopenharmony_ci} 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic int bnxt_hwrm_vfr_qcfg(struct bnxt *bp, struct bnxt_vf_rep *vf_rep, 688c2ecf20Sopenharmony_ci u16 *max_mtu) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci struct hwrm_func_qcfg_output *resp = bp->hwrm_cmd_resp_addr; 718c2ecf20Sopenharmony_ci struct hwrm_func_qcfg_input req = {0}; 728c2ecf20Sopenharmony_ci u16 mtu; 738c2ecf20Sopenharmony_ci int rc; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_FUNC_QCFG, -1, -1); 768c2ecf20Sopenharmony_ci req.fid = cpu_to_le16(bp->pf.vf[vf_rep->vf_idx].fw_fid); 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci mutex_lock(&bp->hwrm_cmd_lock); 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci rc = _hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); 818c2ecf20Sopenharmony_ci if (!rc) { 828c2ecf20Sopenharmony_ci mtu = le16_to_cpu(resp->max_mtu_configured); 838c2ecf20Sopenharmony_ci if (!mtu) 848c2ecf20Sopenharmony_ci *max_mtu = BNXT_MAX_MTU; 858c2ecf20Sopenharmony_ci else 868c2ecf20Sopenharmony_ci *max_mtu = mtu; 878c2ecf20Sopenharmony_ci } 888c2ecf20Sopenharmony_ci mutex_unlock(&bp->hwrm_cmd_lock); 898c2ecf20Sopenharmony_ci return rc; 908c2ecf20Sopenharmony_ci} 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistatic int bnxt_vf_rep_open(struct net_device *dev) 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci struct bnxt_vf_rep *vf_rep = netdev_priv(dev); 958c2ecf20Sopenharmony_ci struct bnxt *bp = vf_rep->bp; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci /* Enable link and TX only if the parent PF is open. */ 988c2ecf20Sopenharmony_ci if (netif_running(bp->dev)) { 998c2ecf20Sopenharmony_ci netif_carrier_on(dev); 1008c2ecf20Sopenharmony_ci netif_tx_start_all_queues(dev); 1018c2ecf20Sopenharmony_ci } 1028c2ecf20Sopenharmony_ci return 0; 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic int bnxt_vf_rep_close(struct net_device *dev) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci netif_carrier_off(dev); 1088c2ecf20Sopenharmony_ci netif_tx_disable(dev); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci return 0; 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cistatic netdev_tx_t bnxt_vf_rep_xmit(struct sk_buff *skb, 1148c2ecf20Sopenharmony_ci struct net_device *dev) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci struct bnxt_vf_rep *vf_rep = netdev_priv(dev); 1178c2ecf20Sopenharmony_ci int rc, len = skb->len; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci skb_dst_drop(skb); 1208c2ecf20Sopenharmony_ci dst_hold((struct dst_entry *)vf_rep->dst); 1218c2ecf20Sopenharmony_ci skb_dst_set(skb, (struct dst_entry *)vf_rep->dst); 1228c2ecf20Sopenharmony_ci skb->dev = vf_rep->dst->u.port_info.lower_dev; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci rc = dev_queue_xmit(skb); 1258c2ecf20Sopenharmony_ci if (!rc) { 1268c2ecf20Sopenharmony_ci vf_rep->tx_stats.packets++; 1278c2ecf20Sopenharmony_ci vf_rep->tx_stats.bytes += len; 1288c2ecf20Sopenharmony_ci } 1298c2ecf20Sopenharmony_ci return rc; 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic void 1338c2ecf20Sopenharmony_cibnxt_vf_rep_get_stats64(struct net_device *dev, 1348c2ecf20Sopenharmony_ci struct rtnl_link_stats64 *stats) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci struct bnxt_vf_rep *vf_rep = netdev_priv(dev); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci stats->rx_packets = vf_rep->rx_stats.packets; 1398c2ecf20Sopenharmony_ci stats->rx_bytes = vf_rep->rx_stats.bytes; 1408c2ecf20Sopenharmony_ci stats->tx_packets = vf_rep->tx_stats.packets; 1418c2ecf20Sopenharmony_ci stats->tx_bytes = vf_rep->tx_stats.bytes; 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_cistatic int bnxt_vf_rep_setup_tc_block_cb(enum tc_setup_type type, 1458c2ecf20Sopenharmony_ci void *type_data, 1468c2ecf20Sopenharmony_ci void *cb_priv) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci struct bnxt_vf_rep *vf_rep = cb_priv; 1498c2ecf20Sopenharmony_ci struct bnxt *bp = vf_rep->bp; 1508c2ecf20Sopenharmony_ci int vf_fid = bp->pf.vf[vf_rep->vf_idx].fw_fid; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci if (!bnxt_tc_flower_enabled(vf_rep->bp) || 1538c2ecf20Sopenharmony_ci !tc_cls_can_offload_and_chain0(bp->dev, type_data)) 1548c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci switch (type) { 1578c2ecf20Sopenharmony_ci case TC_SETUP_CLSFLOWER: 1588c2ecf20Sopenharmony_ci return bnxt_tc_setup_flower(bp, vf_fid, type_data); 1598c2ecf20Sopenharmony_ci default: 1608c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 1618c2ecf20Sopenharmony_ci } 1628c2ecf20Sopenharmony_ci} 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistatic LIST_HEAD(bnxt_vf_block_cb_list); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic int bnxt_vf_rep_setup_tc(struct net_device *dev, enum tc_setup_type type, 1678c2ecf20Sopenharmony_ci void *type_data) 1688c2ecf20Sopenharmony_ci{ 1698c2ecf20Sopenharmony_ci struct bnxt_vf_rep *vf_rep = netdev_priv(dev); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci switch (type) { 1728c2ecf20Sopenharmony_ci case TC_SETUP_BLOCK: 1738c2ecf20Sopenharmony_ci return flow_block_cb_setup_simple(type_data, 1748c2ecf20Sopenharmony_ci &bnxt_vf_block_cb_list, 1758c2ecf20Sopenharmony_ci bnxt_vf_rep_setup_tc_block_cb, 1768c2ecf20Sopenharmony_ci vf_rep, vf_rep, true); 1778c2ecf20Sopenharmony_ci default: 1788c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 1798c2ecf20Sopenharmony_ci } 1808c2ecf20Sopenharmony_ci} 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_cistruct net_device *bnxt_get_vf_rep(struct bnxt *bp, u16 cfa_code) 1838c2ecf20Sopenharmony_ci{ 1848c2ecf20Sopenharmony_ci u16 vf_idx; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci if (cfa_code && bp->cfa_code_map && BNXT_PF(bp)) { 1878c2ecf20Sopenharmony_ci vf_idx = bp->cfa_code_map[cfa_code]; 1888c2ecf20Sopenharmony_ci if (vf_idx != VF_IDX_INVALID) 1898c2ecf20Sopenharmony_ci return bp->vf_reps[vf_idx]->dev; 1908c2ecf20Sopenharmony_ci } 1918c2ecf20Sopenharmony_ci return NULL; 1928c2ecf20Sopenharmony_ci} 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_civoid bnxt_vf_rep_rx(struct bnxt *bp, struct sk_buff *skb) 1958c2ecf20Sopenharmony_ci{ 1968c2ecf20Sopenharmony_ci struct bnxt_vf_rep *vf_rep = netdev_priv(skb->dev); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci vf_rep->rx_stats.bytes += skb->len; 1998c2ecf20Sopenharmony_ci vf_rep->rx_stats.packets++; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci netif_receive_skb(skb); 2028c2ecf20Sopenharmony_ci} 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_cistatic int bnxt_vf_rep_get_phys_port_name(struct net_device *dev, char *buf, 2058c2ecf20Sopenharmony_ci size_t len) 2068c2ecf20Sopenharmony_ci{ 2078c2ecf20Sopenharmony_ci struct bnxt_vf_rep *vf_rep = netdev_priv(dev); 2088c2ecf20Sopenharmony_ci struct pci_dev *pf_pdev = vf_rep->bp->pdev; 2098c2ecf20Sopenharmony_ci int rc; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci rc = snprintf(buf, len, "pf%dvf%d", PCI_FUNC(pf_pdev->devfn), 2128c2ecf20Sopenharmony_ci vf_rep->vf_idx); 2138c2ecf20Sopenharmony_ci if (rc >= len) 2148c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 2158c2ecf20Sopenharmony_ci return 0; 2168c2ecf20Sopenharmony_ci} 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_cistatic void bnxt_vf_rep_get_drvinfo(struct net_device *dev, 2198c2ecf20Sopenharmony_ci struct ethtool_drvinfo *info) 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci strlcpy(info->driver, DRV_MODULE_NAME, sizeof(info->driver)); 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_cistatic int bnxt_vf_rep_get_port_parent_id(struct net_device *dev, 2258c2ecf20Sopenharmony_ci struct netdev_phys_item_id *ppid) 2268c2ecf20Sopenharmony_ci{ 2278c2ecf20Sopenharmony_ci struct bnxt_vf_rep *vf_rep = netdev_priv(dev); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci /* as only PORT_PARENT_ID is supported currently use common code 2308c2ecf20Sopenharmony_ci * between PF and VF-rep for now. 2318c2ecf20Sopenharmony_ci */ 2328c2ecf20Sopenharmony_ci return bnxt_get_port_parent_id(vf_rep->bp->dev, ppid); 2338c2ecf20Sopenharmony_ci} 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_cistatic const struct ethtool_ops bnxt_vf_rep_ethtool_ops = { 2368c2ecf20Sopenharmony_ci .get_drvinfo = bnxt_vf_rep_get_drvinfo 2378c2ecf20Sopenharmony_ci}; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_cistatic const struct net_device_ops bnxt_vf_rep_netdev_ops = { 2408c2ecf20Sopenharmony_ci .ndo_open = bnxt_vf_rep_open, 2418c2ecf20Sopenharmony_ci .ndo_stop = bnxt_vf_rep_close, 2428c2ecf20Sopenharmony_ci .ndo_start_xmit = bnxt_vf_rep_xmit, 2438c2ecf20Sopenharmony_ci .ndo_get_stats64 = bnxt_vf_rep_get_stats64, 2448c2ecf20Sopenharmony_ci .ndo_setup_tc = bnxt_vf_rep_setup_tc, 2458c2ecf20Sopenharmony_ci .ndo_get_port_parent_id = bnxt_vf_rep_get_port_parent_id, 2468c2ecf20Sopenharmony_ci .ndo_get_phys_port_name = bnxt_vf_rep_get_phys_port_name 2478c2ecf20Sopenharmony_ci}; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_cibool bnxt_dev_is_vf_rep(struct net_device *dev) 2508c2ecf20Sopenharmony_ci{ 2518c2ecf20Sopenharmony_ci return dev->netdev_ops == &bnxt_vf_rep_netdev_ops; 2528c2ecf20Sopenharmony_ci} 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci/* Called when the parent PF interface is closed: 2558c2ecf20Sopenharmony_ci * As the mode transition from SWITCHDEV to LEGACY 2568c2ecf20Sopenharmony_ci * happens under the rtnl_lock() this routine is safe 2578c2ecf20Sopenharmony_ci * under the rtnl_lock() 2588c2ecf20Sopenharmony_ci */ 2598c2ecf20Sopenharmony_civoid bnxt_vf_reps_close(struct bnxt *bp) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci struct bnxt_vf_rep *vf_rep; 2628c2ecf20Sopenharmony_ci u16 num_vfs, i; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci if (bp->eswitch_mode != DEVLINK_ESWITCH_MODE_SWITCHDEV) 2658c2ecf20Sopenharmony_ci return; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci num_vfs = pci_num_vf(bp->pdev); 2688c2ecf20Sopenharmony_ci for (i = 0; i < num_vfs; i++) { 2698c2ecf20Sopenharmony_ci vf_rep = bp->vf_reps[i]; 2708c2ecf20Sopenharmony_ci if (netif_running(vf_rep->dev)) 2718c2ecf20Sopenharmony_ci bnxt_vf_rep_close(vf_rep->dev); 2728c2ecf20Sopenharmony_ci } 2738c2ecf20Sopenharmony_ci} 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci/* Called when the parent PF interface is opened (re-opened): 2768c2ecf20Sopenharmony_ci * As the mode transition from SWITCHDEV to LEGACY 2778c2ecf20Sopenharmony_ci * happen under the rtnl_lock() this routine is safe 2788c2ecf20Sopenharmony_ci * under the rtnl_lock() 2798c2ecf20Sopenharmony_ci */ 2808c2ecf20Sopenharmony_civoid bnxt_vf_reps_open(struct bnxt *bp) 2818c2ecf20Sopenharmony_ci{ 2828c2ecf20Sopenharmony_ci int i; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci if (bp->eswitch_mode != DEVLINK_ESWITCH_MODE_SWITCHDEV) 2858c2ecf20Sopenharmony_ci return; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci for (i = 0; i < pci_num_vf(bp->pdev); i++) 2888c2ecf20Sopenharmony_ci bnxt_vf_rep_open(bp->vf_reps[i]->dev); 2898c2ecf20Sopenharmony_ci} 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_cistatic void __bnxt_vf_reps_destroy(struct bnxt *bp) 2928c2ecf20Sopenharmony_ci{ 2938c2ecf20Sopenharmony_ci u16 num_vfs = pci_num_vf(bp->pdev); 2948c2ecf20Sopenharmony_ci struct bnxt_vf_rep *vf_rep; 2958c2ecf20Sopenharmony_ci int i; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci for (i = 0; i < num_vfs; i++) { 2988c2ecf20Sopenharmony_ci vf_rep = bp->vf_reps[i]; 2998c2ecf20Sopenharmony_ci if (vf_rep) { 3008c2ecf20Sopenharmony_ci dst_release((struct dst_entry *)vf_rep->dst); 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci if (vf_rep->tx_cfa_action != CFA_HANDLE_INVALID) 3038c2ecf20Sopenharmony_ci hwrm_cfa_vfr_free(bp, vf_rep->vf_idx); 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci if (vf_rep->dev) { 3068c2ecf20Sopenharmony_ci /* if register_netdev failed, then netdev_ops 3078c2ecf20Sopenharmony_ci * would have been set to NULL 3088c2ecf20Sopenharmony_ci */ 3098c2ecf20Sopenharmony_ci if (vf_rep->dev->netdev_ops) 3108c2ecf20Sopenharmony_ci unregister_netdev(vf_rep->dev); 3118c2ecf20Sopenharmony_ci free_netdev(vf_rep->dev); 3128c2ecf20Sopenharmony_ci } 3138c2ecf20Sopenharmony_ci } 3148c2ecf20Sopenharmony_ci } 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci kfree(bp->vf_reps); 3178c2ecf20Sopenharmony_ci bp->vf_reps = NULL; 3188c2ecf20Sopenharmony_ci} 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_civoid bnxt_vf_reps_destroy(struct bnxt *bp) 3218c2ecf20Sopenharmony_ci{ 3228c2ecf20Sopenharmony_ci bool closed = false; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci if (bp->eswitch_mode != DEVLINK_ESWITCH_MODE_SWITCHDEV) 3258c2ecf20Sopenharmony_ci return; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci if (!bp->vf_reps) 3288c2ecf20Sopenharmony_ci return; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci /* Ensure that parent PF's and VF-reps' RX/TX has been quiesced 3318c2ecf20Sopenharmony_ci * before proceeding with VF-rep cleanup. 3328c2ecf20Sopenharmony_ci */ 3338c2ecf20Sopenharmony_ci rtnl_lock(); 3348c2ecf20Sopenharmony_ci if (netif_running(bp->dev)) { 3358c2ecf20Sopenharmony_ci bnxt_close_nic(bp, false, false); 3368c2ecf20Sopenharmony_ci closed = true; 3378c2ecf20Sopenharmony_ci } 3388c2ecf20Sopenharmony_ci /* un-publish cfa_code_map so that RX path can't see it anymore */ 3398c2ecf20Sopenharmony_ci kfree(bp->cfa_code_map); 3408c2ecf20Sopenharmony_ci bp->cfa_code_map = NULL; 3418c2ecf20Sopenharmony_ci bp->eswitch_mode = DEVLINK_ESWITCH_MODE_LEGACY; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci if (closed) 3448c2ecf20Sopenharmony_ci bnxt_open_nic(bp, false, false); 3458c2ecf20Sopenharmony_ci rtnl_unlock(); 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci /* Need to call vf_reps_destroy() outside of rntl_lock 3488c2ecf20Sopenharmony_ci * as unregister_netdev takes rtnl_lock 3498c2ecf20Sopenharmony_ci */ 3508c2ecf20Sopenharmony_ci __bnxt_vf_reps_destroy(bp); 3518c2ecf20Sopenharmony_ci} 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci/* Use the OUI of the PF's perm addr and report the same mac addr 3548c2ecf20Sopenharmony_ci * for the same VF-rep each time 3558c2ecf20Sopenharmony_ci */ 3568c2ecf20Sopenharmony_cistatic void bnxt_vf_rep_eth_addr_gen(u8 *src_mac, u16 vf_idx, u8 *mac) 3578c2ecf20Sopenharmony_ci{ 3588c2ecf20Sopenharmony_ci u32 addr; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci ether_addr_copy(mac, src_mac); 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci addr = jhash(src_mac, ETH_ALEN, 0) + vf_idx; 3638c2ecf20Sopenharmony_ci mac[3] = (u8)(addr & 0xFF); 3648c2ecf20Sopenharmony_ci mac[4] = (u8)((addr >> 8) & 0xFF); 3658c2ecf20Sopenharmony_ci mac[5] = (u8)((addr >> 16) & 0xFF); 3668c2ecf20Sopenharmony_ci} 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_cistatic void bnxt_vf_rep_netdev_init(struct bnxt *bp, struct bnxt_vf_rep *vf_rep, 3698c2ecf20Sopenharmony_ci struct net_device *dev) 3708c2ecf20Sopenharmony_ci{ 3718c2ecf20Sopenharmony_ci struct net_device *pf_dev = bp->dev; 3728c2ecf20Sopenharmony_ci u16 max_mtu; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci dev->netdev_ops = &bnxt_vf_rep_netdev_ops; 3758c2ecf20Sopenharmony_ci dev->ethtool_ops = &bnxt_vf_rep_ethtool_ops; 3768c2ecf20Sopenharmony_ci /* Just inherit all the featues of the parent PF as the VF-R 3778c2ecf20Sopenharmony_ci * uses the RX/TX rings of the parent PF 3788c2ecf20Sopenharmony_ci */ 3798c2ecf20Sopenharmony_ci dev->hw_features = pf_dev->hw_features; 3808c2ecf20Sopenharmony_ci dev->gso_partial_features = pf_dev->gso_partial_features; 3818c2ecf20Sopenharmony_ci dev->vlan_features = pf_dev->vlan_features; 3828c2ecf20Sopenharmony_ci dev->hw_enc_features = pf_dev->hw_enc_features; 3838c2ecf20Sopenharmony_ci dev->features |= pf_dev->features; 3848c2ecf20Sopenharmony_ci bnxt_vf_rep_eth_addr_gen(bp->pf.mac_addr, vf_rep->vf_idx, 3858c2ecf20Sopenharmony_ci dev->perm_addr); 3868c2ecf20Sopenharmony_ci ether_addr_copy(dev->dev_addr, dev->perm_addr); 3878c2ecf20Sopenharmony_ci /* Set VF-Rep's max-mtu to the corresponding VF's max-mtu */ 3888c2ecf20Sopenharmony_ci if (!bnxt_hwrm_vfr_qcfg(bp, vf_rep, &max_mtu)) 3898c2ecf20Sopenharmony_ci dev->max_mtu = max_mtu; 3908c2ecf20Sopenharmony_ci dev->min_mtu = ETH_ZLEN; 3918c2ecf20Sopenharmony_ci} 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_cistatic int bnxt_vf_reps_create(struct bnxt *bp) 3948c2ecf20Sopenharmony_ci{ 3958c2ecf20Sopenharmony_ci u16 *cfa_code_map = NULL, num_vfs = pci_num_vf(bp->pdev); 3968c2ecf20Sopenharmony_ci struct bnxt_vf_rep *vf_rep; 3978c2ecf20Sopenharmony_ci struct net_device *dev; 3988c2ecf20Sopenharmony_ci int rc, i; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci if (!(bp->flags & BNXT_FLAG_DSN_VALID)) 4018c2ecf20Sopenharmony_ci return -ENODEV; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci bp->vf_reps = kcalloc(num_vfs, sizeof(vf_rep), GFP_KERNEL); 4048c2ecf20Sopenharmony_ci if (!bp->vf_reps) 4058c2ecf20Sopenharmony_ci return -ENOMEM; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci /* storage for cfa_code to vf-idx mapping */ 4088c2ecf20Sopenharmony_ci cfa_code_map = kmalloc_array(MAX_CFA_CODE, sizeof(*bp->cfa_code_map), 4098c2ecf20Sopenharmony_ci GFP_KERNEL); 4108c2ecf20Sopenharmony_ci if (!cfa_code_map) { 4118c2ecf20Sopenharmony_ci rc = -ENOMEM; 4128c2ecf20Sopenharmony_ci goto err; 4138c2ecf20Sopenharmony_ci } 4148c2ecf20Sopenharmony_ci for (i = 0; i < MAX_CFA_CODE; i++) 4158c2ecf20Sopenharmony_ci cfa_code_map[i] = VF_IDX_INVALID; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci for (i = 0; i < num_vfs; i++) { 4188c2ecf20Sopenharmony_ci dev = alloc_etherdev(sizeof(*vf_rep)); 4198c2ecf20Sopenharmony_ci if (!dev) { 4208c2ecf20Sopenharmony_ci rc = -ENOMEM; 4218c2ecf20Sopenharmony_ci goto err; 4228c2ecf20Sopenharmony_ci } 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci vf_rep = netdev_priv(dev); 4258c2ecf20Sopenharmony_ci bp->vf_reps[i] = vf_rep; 4268c2ecf20Sopenharmony_ci vf_rep->dev = dev; 4278c2ecf20Sopenharmony_ci vf_rep->bp = bp; 4288c2ecf20Sopenharmony_ci vf_rep->vf_idx = i; 4298c2ecf20Sopenharmony_ci vf_rep->tx_cfa_action = CFA_HANDLE_INVALID; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci /* get cfa handles from FW */ 4328c2ecf20Sopenharmony_ci rc = hwrm_cfa_vfr_alloc(bp, vf_rep->vf_idx, 4338c2ecf20Sopenharmony_ci &vf_rep->tx_cfa_action, 4348c2ecf20Sopenharmony_ci &vf_rep->rx_cfa_code); 4358c2ecf20Sopenharmony_ci if (rc) { 4368c2ecf20Sopenharmony_ci rc = -ENOLINK; 4378c2ecf20Sopenharmony_ci goto err; 4388c2ecf20Sopenharmony_ci } 4398c2ecf20Sopenharmony_ci cfa_code_map[vf_rep->rx_cfa_code] = vf_rep->vf_idx; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci vf_rep->dst = metadata_dst_alloc(0, METADATA_HW_PORT_MUX, 4428c2ecf20Sopenharmony_ci GFP_KERNEL); 4438c2ecf20Sopenharmony_ci if (!vf_rep->dst) { 4448c2ecf20Sopenharmony_ci rc = -ENOMEM; 4458c2ecf20Sopenharmony_ci goto err; 4468c2ecf20Sopenharmony_ci } 4478c2ecf20Sopenharmony_ci /* only cfa_action is needed to mux a packet while TXing */ 4488c2ecf20Sopenharmony_ci vf_rep->dst->u.port_info.port_id = vf_rep->tx_cfa_action; 4498c2ecf20Sopenharmony_ci vf_rep->dst->u.port_info.lower_dev = bp->dev; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci bnxt_vf_rep_netdev_init(bp, vf_rep, dev); 4528c2ecf20Sopenharmony_ci rc = register_netdev(dev); 4538c2ecf20Sopenharmony_ci if (rc) { 4548c2ecf20Sopenharmony_ci /* no need for unregister_netdev in cleanup */ 4558c2ecf20Sopenharmony_ci dev->netdev_ops = NULL; 4568c2ecf20Sopenharmony_ci goto err; 4578c2ecf20Sopenharmony_ci } 4588c2ecf20Sopenharmony_ci } 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci /* publish cfa_code_map only after all VF-reps have been initialized */ 4618c2ecf20Sopenharmony_ci bp->cfa_code_map = cfa_code_map; 4628c2ecf20Sopenharmony_ci bp->eswitch_mode = DEVLINK_ESWITCH_MODE_SWITCHDEV; 4638c2ecf20Sopenharmony_ci netif_keep_dst(bp->dev); 4648c2ecf20Sopenharmony_ci return 0; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_cierr: 4678c2ecf20Sopenharmony_ci netdev_info(bp->dev, "%s error=%d\n", __func__, rc); 4688c2ecf20Sopenharmony_ci kfree(cfa_code_map); 4698c2ecf20Sopenharmony_ci __bnxt_vf_reps_destroy(bp); 4708c2ecf20Sopenharmony_ci return rc; 4718c2ecf20Sopenharmony_ci} 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci/* Devlink related routines */ 4748c2ecf20Sopenharmony_ciint bnxt_dl_eswitch_mode_get(struct devlink *devlink, u16 *mode) 4758c2ecf20Sopenharmony_ci{ 4768c2ecf20Sopenharmony_ci struct bnxt *bp = bnxt_get_bp_from_dl(devlink); 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci *mode = bp->eswitch_mode; 4798c2ecf20Sopenharmony_ci return 0; 4808c2ecf20Sopenharmony_ci} 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ciint bnxt_dl_eswitch_mode_set(struct devlink *devlink, u16 mode, 4838c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 4848c2ecf20Sopenharmony_ci{ 4858c2ecf20Sopenharmony_ci struct bnxt *bp = bnxt_get_bp_from_dl(devlink); 4868c2ecf20Sopenharmony_ci int rc = 0; 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci mutex_lock(&bp->sriov_lock); 4898c2ecf20Sopenharmony_ci if (bp->eswitch_mode == mode) { 4908c2ecf20Sopenharmony_ci netdev_info(bp->dev, "already in %s eswitch mode\n", 4918c2ecf20Sopenharmony_ci mode == DEVLINK_ESWITCH_MODE_LEGACY ? 4928c2ecf20Sopenharmony_ci "legacy" : "switchdev"); 4938c2ecf20Sopenharmony_ci rc = -EINVAL; 4948c2ecf20Sopenharmony_ci goto done; 4958c2ecf20Sopenharmony_ci } 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci switch (mode) { 4988c2ecf20Sopenharmony_ci case DEVLINK_ESWITCH_MODE_LEGACY: 4998c2ecf20Sopenharmony_ci bnxt_vf_reps_destroy(bp); 5008c2ecf20Sopenharmony_ci break; 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci case DEVLINK_ESWITCH_MODE_SWITCHDEV: 5038c2ecf20Sopenharmony_ci if (bp->hwrm_spec_code < 0x10803) { 5048c2ecf20Sopenharmony_ci netdev_warn(bp->dev, "FW does not support SRIOV E-Switch SWITCHDEV mode\n"); 5058c2ecf20Sopenharmony_ci rc = -ENOTSUPP; 5068c2ecf20Sopenharmony_ci goto done; 5078c2ecf20Sopenharmony_ci } 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci if (pci_num_vf(bp->pdev) == 0) { 5108c2ecf20Sopenharmony_ci netdev_info(bp->dev, "Enable VFs before setting switchdev mode\n"); 5118c2ecf20Sopenharmony_ci rc = -EPERM; 5128c2ecf20Sopenharmony_ci goto done; 5138c2ecf20Sopenharmony_ci } 5148c2ecf20Sopenharmony_ci rc = bnxt_vf_reps_create(bp); 5158c2ecf20Sopenharmony_ci break; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci default: 5188c2ecf20Sopenharmony_ci rc = -EINVAL; 5198c2ecf20Sopenharmony_ci goto done; 5208c2ecf20Sopenharmony_ci } 5218c2ecf20Sopenharmony_cidone: 5228c2ecf20Sopenharmony_ci mutex_unlock(&bp->sriov_lock); 5238c2ecf20Sopenharmony_ci return rc; 5248c2ecf20Sopenharmony_ci} 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci#endif 527