/* SPDX-License-Identifier: BSD-3-Clause * * Copyright(c) 2019-2021 Xilinx, Inc. * Copyright(c) 2019 Solarflare Communications Inc. * * This software was jointly developed between OKTET Labs (under contract * for Solarflare) and Solarflare Communications, Inc. */ #include #include #include "sfc.h" #include "sfc_log.h" #include "efx.h" /* * Check if a MAC address is already assigned to one of previously * configured vPorts (either PF itself or one of already configured VFs). * * Typically the first vPort which corresponds to PF has globally * administered unicast address, but it still could be locally * administered if user assigned it or in the case of unconfigured NIC. * So, it is safer to include it as well in uniqueness check. */ static bool sriov_mac_addr_assigned(const efx_vport_config_t *vport_config, unsigned int num, const uint8_t *mac_addr) { unsigned int i; /* Check PF's MAC address as well as explained above */ for (i = 0; i < num; ++i) { if (memcmp(mac_addr, vport_config[i].evc_mac_addr, sizeof(vport_config[i].evc_mac_addr)) == 0) return true; } return false; } int sfc_sriov_attach(struct sfc_adapter *sa) { const struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(sa->eth_dev); struct sfc_sriov *sriov = &sa->sriov; efx_vport_config_t *vport_config; unsigned int i; int rc; sfc_log_init(sa, "entry"); sriov->num_vfs = pci_dev->max_vfs; if (sa->switchdev || sriov->num_vfs == 0) goto done; vport_config = calloc(sriov->num_vfs + 1, sizeof(*vport_config)); if (vport_config == NULL) { rc = ENOMEM; goto fail_alloc_vport_config; } vport_config[0].evc_function = 0xffff; vport_config[0].evc_vid = EFX_VF_VID_DEFAULT; vport_config[0].evc_vlan_restrict = B_FALSE; for (i = 1; i <= sriov->num_vfs; ++i) { vport_config[i].evc_function = i - 1; vport_config[i].evc_vid = EFX_VF_VID_DEFAULT; vport_config[i].evc_vlan_restrict = B_FALSE; do { rte_eth_random_addr(vport_config[i].evc_mac_addr); } while (sriov_mac_addr_assigned(vport_config, i, vport_config[i].evc_mac_addr)); } sriov->vport_config = vport_config; done: sfc_log_init(sa, "done"); return 0; fail_alloc_vport_config: sriov->num_vfs = 0; return rc; } void sfc_sriov_detach(struct sfc_adapter *sa) { struct sfc_sriov *sriov = &sa->sriov; sfc_log_init(sa, "entry"); free(sriov->vport_config); sriov->vport_config = NULL; sriov->num_vfs = 0; sfc_log_init(sa, "done"); } int sfc_sriov_vswitch_create(struct sfc_adapter *sa) { struct sfc_sriov *sriov = &sa->sriov; efx_vport_config_t *vport_config = sriov->vport_config; int rc; sfc_log_init(sa, "entry"); if (sa->switchdev) { sfc_log_init(sa, "don't create vswitch in switchdev mode"); goto done; } if (sriov->num_vfs == 0) { sfc_log_init(sa, "no VFs enabled"); goto done; } rc = efx_evb_init(sa->nic); if (rc != 0) { sfc_err(sa, "EVB init failed %d", rc); goto fail_evb_init; } RTE_BUILD_BUG_ON(sizeof(sa->port.default_mac_addr) != sizeof(vport_config[0].evc_mac_addr)); rte_ether_addr_copy(&sa->port.default_mac_addr, (struct rte_ether_addr *)vport_config[0].evc_mac_addr); rc = efx_evb_vswitch_create(sa->nic, sriov->num_vfs + 1, vport_config, &sriov->vswitch); if (rc != 0) { sfc_err(sa, "EVB vSwitch create failed %d", rc); goto fail_evb_vswitch_create; } done: sfc_log_init(sa, "done"); return 0; fail_evb_vswitch_create: efx_evb_fini(sa->nic); fail_evb_init: return rc; } void sfc_sriov_vswitch_destroy(struct sfc_adapter *sa) { struct sfc_sriov *sriov = &sa->sriov; int rc; sfc_log_init(sa, "entry"); if (sa->switchdev || sriov->num_vfs == 0) goto done; rc = efx_evb_vswitch_destroy(sa->nic, sriov->vswitch); if (rc != 0) sfc_err(sa, "efx_evb_vswitch_destroy() failed %d", rc); sriov->vswitch = NULL; efx_evb_fini(sa->nic); done: sfc_log_init(sa, "done"); }