diff options
Diffstat (limited to 'drivers/net/ethernet/sfc/ef10_sriov.c')
-rw-r--r-- | drivers/net/ethernet/sfc/ef10_sriov.c | 155 |
1 files changed, 103 insertions, 52 deletions
diff --git a/drivers/net/ethernet/sfc/ef10_sriov.c b/drivers/net/ethernet/sfc/ef10_sriov.c index 14393767ef9f..9aae0d8b713f 100644 --- a/drivers/net/ethernet/sfc/ef10_sriov.c +++ b/drivers/net/ethernet/sfc/ef10_sriov.c @@ -122,8 +122,7 @@ static void efx_ef10_sriov_free_vf_vports(struct efx_nic *efx) struct ef10_vf *vf = nic_data->vf + i; /* If VF is assigned, do not free the vport */ - if (vf->pci_dev && - vf->pci_dev->dev_flags & PCI_DEV_FLAGS_ASSIGNED) + if (vf->pci_dev && pci_is_dev_assigned(vf->pci_dev)) continue; if (vf->vport_assigned) { @@ -207,9 +206,7 @@ static int efx_ef10_sriov_alloc_vf_vswitching(struct efx_nic *efx) return 0; fail: - efx_ef10_sriov_free_vf_vports(efx); - kfree(nic_data->vf); - nic_data->vf = NULL; + efx_ef10_sriov_free_vf_vswitching(efx); return rc; } @@ -232,15 +229,14 @@ fail: static int efx_ef10_vadaptor_alloc_set_features(struct efx_nic *efx) { - struct efx_ef10_nic_data *nic_data = efx->nic_data; u32 port_flags; int rc; - rc = efx_ef10_vadaptor_alloc(efx, nic_data->vport_id); + rc = efx_ef10_vadaptor_alloc(efx, efx->vport_id); if (rc) goto fail_vadaptor_alloc; - rc = efx_ef10_vadaptor_query(efx, nic_data->vport_id, + rc = efx_ef10_vadaptor_query(efx, efx->vport_id, &port_flags, NULL, NULL); if (rc) goto fail_vadaptor_query; @@ -281,11 +277,11 @@ int efx_ef10_vswitching_probe_pf(struct efx_nic *efx) rc = efx_ef10_vport_alloc(efx, EVB_PORT_ID_ASSIGNED, MC_CMD_VPORT_ALLOC_IN_VPORT_TYPE_NORMAL, - EFX_EF10_NO_VLAN, &nic_data->vport_id); + EFX_EF10_NO_VLAN, &efx->vport_id); if (rc) goto fail2; - rc = efx_ef10_vport_add_mac(efx, nic_data->vport_id, net_dev->dev_addr); + rc = efx_ef10_vport_add_mac(efx, efx->vport_id, net_dev->dev_addr); if (rc) goto fail3; ether_addr_copy(nic_data->vport_mac, net_dev->dev_addr); @@ -296,11 +292,11 @@ int efx_ef10_vswitching_probe_pf(struct efx_nic *efx) return 0; fail4: - efx_ef10_vport_del_mac(efx, nic_data->vport_id, nic_data->vport_mac); + efx_ef10_vport_del_mac(efx, efx->vport_id, nic_data->vport_mac); eth_zero_addr(nic_data->vport_mac); fail3: - efx_ef10_vport_free(efx, nic_data->vport_id); - nic_data->vport_id = EVB_PORT_ID_ASSIGNED; + efx_ef10_vport_free(efx, efx->vport_id); + efx->vport_id = EVB_PORT_ID_ASSIGNED; fail2: efx_ef10_vswitch_free(efx, EVB_PORT_ID_ASSIGNED); fail1: @@ -355,22 +351,22 @@ void efx_ef10_vswitching_remove_pf(struct efx_nic *efx) efx_ef10_sriov_free_vf_vswitching(efx); - efx_ef10_vadaptor_free(efx, nic_data->vport_id); + efx_ef10_vadaptor_free(efx, efx->vport_id); - if (nic_data->vport_id == EVB_PORT_ID_ASSIGNED) + if (efx->vport_id == EVB_PORT_ID_ASSIGNED) return; /* No vswitch was ever created */ if (!is_zero_ether_addr(nic_data->vport_mac)) { - efx_ef10_vport_del_mac(efx, nic_data->vport_id, + efx_ef10_vport_del_mac(efx, efx->vport_id, efx->net_dev->dev_addr); eth_zero_addr(nic_data->vport_mac); } - efx_ef10_vport_free(efx, nic_data->vport_id); - nic_data->vport_id = EVB_PORT_ID_ASSIGNED; + efx_ef10_vport_free(efx, efx->vport_id); + efx->vport_id = EVB_PORT_ID_ASSIGNED; /* Only free the vswitch if no VFs are assigned */ if (!pci_vfs_assigned(efx->pci_dev)) - efx_ef10_vswitch_free(efx, nic_data->vport_id); + efx_ef10_vswitch_free(efx, efx->vport_id); } void efx_ef10_vswitching_remove_vf(struct efx_nic *efx) @@ -403,12 +399,18 @@ fail1: return rc; } +/* Disable SRIOV and remove VFs + * If some VFs are attached to a guest (using Xen, only) nothing is + * done if force=false, and vports are freed if force=true (for the non + * attachedc ones, only) but SRIOV is not disabled and VFs are not + * removed in either case. + */ static int efx_ef10_pci_sriov_disable(struct efx_nic *efx, bool force) { struct pci_dev *dev = efx->pci_dev; - unsigned int vfs_assigned = 0; - - vfs_assigned = pci_vfs_assigned(dev); + struct efx_ef10_nic_data *nic_data = efx->nic_data; + unsigned int vfs_assigned = pci_vfs_assigned(dev); + int i, rc = 0; if (vfs_assigned && !force) { netif_info(efx, drv, efx->net_dev, "VFs are assigned to guests; " @@ -416,12 +418,17 @@ static int efx_ef10_pci_sriov_disable(struct efx_nic *efx, bool force) return -EBUSY; } - if (!vfs_assigned) + if (!vfs_assigned) { + for (i = 0; i < efx->vf_count; i++) + nic_data->vf[i].pci_dev = NULL; pci_disable_sriov(dev); + } else { + rc = -EBUSY; + } efx_ef10_sriov_free_vf_vswitching(efx); efx->vf_count = 0; - return 0; + return rc; } int efx_ef10_sriov_configure(struct efx_nic *efx, int num_vfs) @@ -440,24 +447,18 @@ int efx_ef10_sriov_init(struct efx_nic *efx) void efx_ef10_sriov_fini(struct efx_nic *efx) { struct efx_ef10_nic_data *nic_data = efx->nic_data; - unsigned int i; int rc; if (!nic_data->vf) { - /* Remove any un-assigned orphaned VFs */ + /* Remove any un-assigned orphaned VFs. This can happen if the PF driver + * was unloaded while any VF was assigned to a guest (using Xen, only). + */ if (pci_num_vf(efx->pci_dev) && !pci_vfs_assigned(efx->pci_dev)) pci_disable_sriov(efx->pci_dev); return; } - /* Remove any VFs in the host */ - for (i = 0; i < efx->vf_count; ++i) { - struct efx_nic *vf_efx = nic_data->vf[i].efx; - - if (vf_efx) - vf_efx->pci_dev->driver->remove(vf_efx->pci_dev); - } - + /* Disable SRIOV and remove any VFs in the host */ rc = efx_ef10_pci_sriov_disable(efx, true); if (rc) netif_dbg(efx, drv, efx->net_dev, @@ -483,7 +484,7 @@ static int efx_ef10_vport_del_vf_mac(struct efx_nic *efx, unsigned int port_id, return rc; } -int efx_ef10_sriov_set_vf_mac(struct efx_nic *efx, int vf_i, u8 *mac) +int efx_ef10_sriov_set_vf_mac(struct efx_nic *efx, int vf_i, const u8 *mac) { struct efx_ef10_nic_data *nic_data = efx->nic_data; struct ef10_vf *vf; @@ -500,14 +501,11 @@ int efx_ef10_sriov_set_vf_mac(struct efx_nic *efx, int vf_i, u8 *mac) efx_device_detach_sync(vf->efx); efx_net_stop(vf->efx->net_dev); - down_write(&vf->efx->filter_sem); vf->efx->type->filter_table_remove(vf->efx); rc = efx_ef10_vadaptor_free(vf->efx, EVB_PORT_ID_ASSIGNED); - if (rc) { - up_write(&vf->efx->filter_sem); + if (rc) return rc; - } } rc = efx_ef10_evb_port_assign(efx, EVB_PORT_ID_NULL, vf_i); @@ -526,7 +524,7 @@ int efx_ef10_sriov_set_vf_mac(struct efx_nic *efx, int vf_i, u8 *mac) goto fail; if (vf->efx) - ether_addr_copy(vf->efx->net_dev->dev_addr, mac); + eth_hw_addr_set(vf->efx->net_dev, mac); } ether_addr_copy(vf->mac, mac); @@ -538,12 +536,9 @@ int efx_ef10_sriov_set_vf_mac(struct efx_nic *efx, int vf_i, u8 *mac) if (vf->efx) { /* VF cannot use the vport_id that the PF created */ rc = efx_ef10_vadaptor_alloc(vf->efx, EVB_PORT_ID_ASSIGNED); - if (rc) { - up_write(&vf->efx->filter_sem); + if (rc) return rc; - } vf->efx->type->filter_table_probe(vf->efx); - up_write(&vf->efx->filter_sem); efx_net_open(vf->efx->net_dev); efx_device_attach_if_not_resetting(vf->efx); } @@ -579,7 +574,6 @@ int efx_ef10_sriov_set_vf_vlan(struct efx_nic *efx, int vf_i, u16 vlan, efx_net_stop(vf->efx->net_dev); mutex_lock(&vf->efx->mac_lock); - down_write(&vf->efx->filter_sem); vf->efx->type->filter_table_remove(vf->efx); rc = efx_ef10_vadaptor_free(vf->efx, EVB_PORT_ID_ASSIGNED); @@ -653,7 +647,6 @@ restore_filters: if (rc2) goto reset_nic_up_write; - up_write(&vf->efx->filter_sem); mutex_unlock(&vf->efx->mac_lock); rc2 = efx_net_open(vf->efx->net_dev); @@ -665,10 +658,8 @@ restore_filters: return rc; reset_nic_up_write: - if (vf->efx) { - up_write(&vf->efx->filter_sem); + if (vf->efx) mutex_unlock(&vf->efx->mac_lock); - } reset_nic: if (vf->efx) { netif_err(efx, drv, efx->net_dev, @@ -685,10 +676,70 @@ reset_nic: return rc ? rc : rc2; } -int efx_ef10_sriov_set_vf_spoofchk(struct efx_nic *efx, int vf_i, - bool spoofchk) +static int efx_ef10_sriov_set_privilege_mask(struct efx_nic *efx, int vf_i, + u32 mask, u32 value) +{ + MCDI_DECLARE_BUF(pm_outbuf, MC_CMD_PRIVILEGE_MASK_OUT_LEN); + MCDI_DECLARE_BUF(pm_inbuf, MC_CMD_PRIVILEGE_MASK_IN_LEN); + struct efx_ef10_nic_data *nic_data = efx->nic_data; + u32 old_mask, new_mask; + size_t outlen; + int rc; + + EFX_WARN_ON_PARANOID((value & ~mask) != 0); + + /* Get privilege mask */ + MCDI_POPULATE_DWORD_2(pm_inbuf, PRIVILEGE_MASK_IN_FUNCTION, + PRIVILEGE_MASK_IN_FUNCTION_PF, nic_data->pf_index, + PRIVILEGE_MASK_IN_FUNCTION_VF, vf_i); + + rc = efx_mcdi_rpc(efx, MC_CMD_PRIVILEGE_MASK, + pm_inbuf, sizeof(pm_inbuf), + pm_outbuf, sizeof(pm_outbuf), &outlen); + + if (rc != 0) + return rc; + if (outlen != MC_CMD_PRIVILEGE_MASK_OUT_LEN) + return -EIO; + + old_mask = MCDI_DWORD(pm_outbuf, PRIVILEGE_MASK_OUT_OLD_MASK); + + new_mask = old_mask & ~mask; + new_mask |= value; + + if (new_mask == old_mask) + return 0; + + new_mask |= MC_CMD_PRIVILEGE_MASK_IN_DO_CHANGE; + + /* Set privilege mask */ + MCDI_SET_DWORD(pm_inbuf, PRIVILEGE_MASK_IN_NEW_MASK, new_mask); + + rc = efx_mcdi_rpc(efx, MC_CMD_PRIVILEGE_MASK, + pm_inbuf, sizeof(pm_inbuf), + pm_outbuf, sizeof(pm_outbuf), &outlen); + + if (rc != 0) + return rc; + if (outlen != MC_CMD_PRIVILEGE_MASK_OUT_LEN) + return -EIO; + + return 0; +} + +int efx_ef10_sriov_set_vf_spoofchk(struct efx_nic *efx, int vf_i, bool spoofchk) { - return spoofchk ? -EOPNOTSUPP : 0; + struct efx_ef10_nic_data *nic_data = efx->nic_data; + + /* Can't enable spoofchk if firmware doesn't support it. */ + if (!(nic_data->datapath_caps & + BIT(MC_CMD_GET_CAPABILITIES_OUT_TX_MAC_SECURITY_FILTERING_LBN)) && + spoofchk) + return -EOPNOTSUPP; + + return efx_ef10_sriov_set_privilege_mask(efx, vf_i, + MC_CMD_PRIVILEGE_MASK_IN_GRP_MAC_SPOOFING_TX, + spoofchk ? 0 : MC_CMD_PRIVILEGE_MASK_IN_GRP_MAC_SPOOFING_TX); } int efx_ef10_sriov_set_vf_link_state(struct efx_nic *efx, int vf_i, |