From 8ca353da9c10461c91565de51c1bf375c49a820f Mon Sep 17 00:00:00 2001 From: Edward Cree Date: Wed, 20 Jul 2022 19:29:24 +0100 Subject: [PATCH 1/9] sfc: update EF100 register descriptions Signed-off-by: Edward Cree Signed-off-by: David S. Miller --- drivers/net/ethernet/sfc/ef100_regs.h | 83 +++++++++++++++++++-------- 1 file changed, 60 insertions(+), 23 deletions(-) diff --git a/drivers/net/ethernet/sfc/ef100_regs.h b/drivers/net/ethernet/sfc/ef100_regs.h index 710bbdb19885..982b6ab1eb62 100644 --- a/drivers/net/ethernet/sfc/ef100_regs.h +++ b/drivers/net/ethernet/sfc/ef100_regs.h @@ -2,7 +2,7 @@ /**************************************************************************** * Driver for Solarflare network controllers and boards * Copyright 2018 Solarflare Communications Inc. - * Copyright 2019-2020 Xilinx Inc. + * Copyright 2019-2022 Xilinx Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published @@ -181,12 +181,6 @@ /* RHEAD_BASE_EVENT */ #define ESF_GZ_E_TYPE_LBN 60 #define ESF_GZ_E_TYPE_WIDTH 4 -#define ESE_GZ_EF100_EV_DRIVER 5 -#define ESE_GZ_EF100_EV_MCDI 4 -#define ESE_GZ_EF100_EV_CONTROL 3 -#define ESE_GZ_EF100_EV_TX_TIMESTAMP 2 -#define ESE_GZ_EF100_EV_TX_COMPLETION 1 -#define ESE_GZ_EF100_EV_RX_PKTS 0 #define ESF_GZ_EV_EVQ_PHASE_LBN 59 #define ESF_GZ_EV_EVQ_PHASE_WIDTH 1 #define ESE_GZ_RHEAD_BASE_EVENT_STRUCT_SIZE 64 @@ -369,14 +363,18 @@ #define ESF_GZ_RX_PREFIX_VLAN_STRIP_TCI_WIDTH 16 #define ESF_GZ_RX_PREFIX_CSUM_FRAME_LBN 144 #define ESF_GZ_RX_PREFIX_CSUM_FRAME_WIDTH 16 -#define ESF_GZ_RX_PREFIX_INGRESS_VPORT_LBN 128 -#define ESF_GZ_RX_PREFIX_INGRESS_VPORT_WIDTH 16 +#define ESF_GZ_RX_PREFIX_INGRESS_MPORT_LBN 128 +#define ESF_GZ_RX_PREFIX_INGRESS_MPORT_WIDTH 16 #define ESF_GZ_RX_PREFIX_USER_MARK_LBN 96 #define ESF_GZ_RX_PREFIX_USER_MARK_WIDTH 32 #define ESF_GZ_RX_PREFIX_RSS_HASH_LBN 64 #define ESF_GZ_RX_PREFIX_RSS_HASH_WIDTH 32 -#define ESF_GZ_RX_PREFIX_PARTIAL_TSTAMP_LBN 32 -#define ESF_GZ_RX_PREFIX_PARTIAL_TSTAMP_WIDTH 32 +#define ESF_GZ_RX_PREFIX_PARTIAL_TSTAMP_LBN 34 +#define ESF_GZ_RX_PREFIX_PARTIAL_TSTAMP_WIDTH 30 +#define ESF_GZ_RX_PREFIX_VSWITCH_STATUS_LBN 33 +#define ESF_GZ_RX_PREFIX_VSWITCH_STATUS_WIDTH 1 +#define ESF_GZ_RX_PREFIX_VLAN_STRIPPED_LBN 32 +#define ESF_GZ_RX_PREFIX_VLAN_STRIPPED_WIDTH 1 #define ESF_GZ_RX_PREFIX_CLASS_LBN 16 #define ESF_GZ_RX_PREFIX_CLASS_WIDTH 16 #define ESF_GZ_RX_PREFIX_USER_FLAG_LBN 15 @@ -454,12 +452,8 @@ #define ESF_GZ_M2M_TRANSLATE_ADDR_WIDTH 1 #define ESF_GZ_M2M_RSVD_LBN 120 #define ESF_GZ_M2M_RSVD_WIDTH 2 -#define ESF_GZ_M2M_ADDR_SPC_LBN 108 -#define ESF_GZ_M2M_ADDR_SPC_WIDTH 12 -#define ESF_GZ_M2M_ADDR_SPC_PASID_LBN 86 -#define ESF_GZ_M2M_ADDR_SPC_PASID_WIDTH 22 -#define ESF_GZ_M2M_ADDR_SPC_MODE_LBN 84 -#define ESF_GZ_M2M_ADDR_SPC_MODE_WIDTH 2 +#define ESF_GZ_M2M_ADDR_SPC_ID_LBN 84 +#define ESF_GZ_M2M_ADDR_SPC_ID_WIDTH 36 #define ESF_GZ_M2M_LEN_MINUS_1_LBN 64 #define ESF_GZ_M2M_LEN_MINUS_1_WIDTH 20 #define ESF_GZ_M2M_ADDR_LBN 0 @@ -492,12 +486,8 @@ #define ESF_GZ_TX_SEG_TRANSLATE_ADDR_WIDTH 1 #define ESF_GZ_TX_SEG_RSVD2_LBN 120 #define ESF_GZ_TX_SEG_RSVD2_WIDTH 2 -#define ESF_GZ_TX_SEG_ADDR_SPC_LBN 108 -#define ESF_GZ_TX_SEG_ADDR_SPC_WIDTH 12 -#define ESF_GZ_TX_SEG_ADDR_SPC_PASID_LBN 86 -#define ESF_GZ_TX_SEG_ADDR_SPC_PASID_WIDTH 22 -#define ESF_GZ_TX_SEG_ADDR_SPC_MODE_LBN 84 -#define ESF_GZ_TX_SEG_ADDR_SPC_MODE_WIDTH 2 +#define ESF_GZ_TX_SEG_ADDR_SPC_ID_LBN 84 +#define ESF_GZ_TX_SEG_ADDR_SPC_ID_WIDTH 36 #define ESF_GZ_TX_SEG_RSVD_LBN 80 #define ESF_GZ_TX_SEG_RSVD_WIDTH 4 #define ESF_GZ_TX_SEG_LEN_LBN 64 @@ -583,6 +573,12 @@ #define ESE_GZ_SF_TX_TSO_DSC_FMT_STRUCT_SIZE 124 +/* Enum D2VIO_MSG_OP */ +#define ESE_GZ_QUE_JBDNE 3 +#define ESE_GZ_QUE_EVICT 2 +#define ESE_GZ_QUE_EMPTY 1 +#define ESE_GZ_NOP 0 + /* Enum DESIGN_PARAMS */ #define ESE_EF100_DP_GZ_RX_MAX_RUNT 17 #define ESE_EF100_DP_GZ_VI_STRIDES 16 @@ -630,6 +626,19 @@ #define ESE_GZ_PCI_BASE_CONFIG_SPACE_SIZE 256 #define ESE_GZ_PCI_EXPRESS_XCAP_HDR_SIZE 4 +/* Enum RH_DSC_TYPE */ +#define ESE_GZ_TX_TOMB 0xF +#define ESE_GZ_TX_VIO 0xE +#define ESE_GZ_TX_TSO_OVRRD 0x8 +#define ESE_GZ_TX_D2CMP 0x7 +#define ESE_GZ_TX_DATA 0x6 +#define ESE_GZ_TX_D2M 0x5 +#define ESE_GZ_TX_M2M 0x4 +#define ESE_GZ_TX_SEG 0x3 +#define ESE_GZ_TX_TSO 0x2 +#define ESE_GZ_TX_OVRRD 0x1 +#define ESE_GZ_TX_SEND 0x0 + /* Enum RH_HCLASS_L2_CLASS */ #define ESE_GZ_RH_HCLASS_L2_CLASS_E2_0123VLAN 1 #define ESE_GZ_RH_HCLASS_L2_CLASS_OTHER 0 @@ -666,6 +675,25 @@ #define ESE_GZ_RH_HCLASS_TUNNEL_CLASS_VXLAN 1 #define ESE_GZ_RH_HCLASS_TUNNEL_CLASS_NONE 0 +/* Enum SF_CTL_EVENT_SUBTYPE */ +#define ESE_GZ_EF100_CTL_EV_EVQ_TIMEOUT 0x3 +#define ESE_GZ_EF100_CTL_EV_FLUSH 0x2 +#define ESE_GZ_EF100_CTL_EV_TIME_SYNC 0x1 +#define ESE_GZ_EF100_CTL_EV_UNSOL_OVERFLOW 0x0 + +/* Enum SF_EVENT_TYPE */ +#define ESE_GZ_EF100_EV_DRIVER 0x5 +#define ESE_GZ_EF100_EV_MCDI 0x4 +#define ESE_GZ_EF100_EV_CONTROL 0x3 +#define ESE_GZ_EF100_EV_TX_TIMESTAMP 0x2 +#define ESE_GZ_EF100_EV_TX_COMPLETION 0x1 +#define ESE_GZ_EF100_EV_RX_PKTS 0x0 + +/* Enum SF_EW_EVENT_TYPE */ +#define ESE_GZ_EF100_EWEV_VIRTQ_DESC 0x2 +#define ESE_GZ_EF100_EWEV_TXQ_DESC 0x1 +#define ESE_GZ_EF100_EWEV_64BIT 0x0 + /* Enum TX_DESC_CSO_PARTIAL_EN */ #define ESE_GZ_TX_DESC_CSO_PARTIAL_EN_TCP 2 #define ESE_GZ_TX_DESC_CSO_PARTIAL_EN_UDP 1 @@ -681,6 +709,15 @@ #define ESE_GZ_TX_DESC_IP4_ID_INC_MOD16 2 #define ESE_GZ_TX_DESC_IP4_ID_INC_MOD15 1 #define ESE_GZ_TX_DESC_IP4_ID_NO_OP 0 + +/* Enum VIRTIO_NET_HDR_F */ +#define ESE_GZ_NEEDS_CSUM 0x1 + +/* Enum VIRTIO_NET_HDR_GSO */ +#define ESE_GZ_TCPV6 0x4 +#define ESE_GZ_UDP 0x3 +#define ESE_GZ_TCPV4 0x1 +#define ESE_GZ_NONE 0x0 /**************************************************************************/ #define ESF_GZ_EV_DEBUG_EVENT_GEN_FLAGS_LBN 44 From 95287e1b4e5c656ad3abfbf27f0249792251dd9e Mon Sep 17 00:00:00 2001 From: Edward Cree Date: Wed, 20 Jul 2022 19:29:26 +0100 Subject: [PATCH 2/9] sfc: detect ef100 MAE admin privilege/capability at probe time One PCIe function per network port (more precisely, per m-port group) is responsible for configuring the Match-Action Engine which performs switching and packet modification in the slice to support flower/OVS offload. The GRP_MAE bit in the privilege mask indicates whether a given function has this capability. At probe time, call MCDIs to read the calling function's privilege mask, and store the GRP_MAE bit in a new ef100_nic_data member. Signed-off-by: Edward Cree Signed-off-by: David S. Miller --- drivers/net/ethernet/sfc/ef100_nic.c | 7 +++++ drivers/net/ethernet/sfc/ef100_nic.h | 1 + drivers/net/ethernet/sfc/mcdi.c | 46 ++++++++++++++++++++++++++++ drivers/net/ethernet/sfc/mcdi.h | 1 + 4 files changed, 55 insertions(+) diff --git a/drivers/net/ethernet/sfc/ef100_nic.c b/drivers/net/ethernet/sfc/ef100_nic.c index f89e695cf8ac..4625d35269e6 100644 --- a/drivers/net/ethernet/sfc/ef100_nic.c +++ b/drivers/net/ethernet/sfc/ef100_nic.c @@ -946,6 +946,7 @@ static int ef100_probe_main(struct efx_nic *efx) unsigned int bar_size = resource_size(&efx->pci_dev->resource[efx->mem_bar]); struct ef100_nic_data *nic_data; char fw_version[32]; + u32 priv_mask = 0; int i, rc; if (WARN_ON(bar_size == 0)) @@ -1027,6 +1028,12 @@ static int ef100_probe_main(struct efx_nic *efx) efx_mcdi_print_fwver(efx, fw_version, sizeof(fw_version)); pci_dbg(efx->pci_dev, "Firmware version %s\n", fw_version); + rc = efx_mcdi_get_privilege_mask(efx, &priv_mask); + if (rc) /* non-fatal, and priv_mask will still be 0 */ + pci_info(efx->pci_dev, + "Failed to get privilege mask from FW, rc %d\n", rc); + nic_data->grp_mae = !!(priv_mask & MC_CMD_PRIVILEGE_MASK_IN_GRP_MAE); + if (compare_versions(fw_version, "1.1.0.1000") < 0) { pci_info(efx->pci_dev, "Firmware uses old event descriptors\n"); rc = -EINVAL; diff --git a/drivers/net/ethernet/sfc/ef100_nic.h b/drivers/net/ethernet/sfc/ef100_nic.h index 744dbbdb4adc..40f84a275057 100644 --- a/drivers/net/ethernet/sfc/ef100_nic.h +++ b/drivers/net/ethernet/sfc/ef100_nic.h @@ -72,6 +72,7 @@ struct ef100_nic_data { u8 port_id[ETH_ALEN]; DECLARE_BITMAP(evq_phases, EFX_MAX_CHANNELS); u64 stats[EF100_STAT_COUNT]; + bool grp_mae; /* MAE Privilege */ u16 tso_max_hdr_len; u16 tso_max_payload_num_segs; u16 tso_max_frames; diff --git a/drivers/net/ethernet/sfc/mcdi.c b/drivers/net/ethernet/sfc/mcdi.c index 3225fe64c397..af338208eae9 100644 --- a/drivers/net/ethernet/sfc/mcdi.c +++ b/drivers/net/ethernet/sfc/mcdi.c @@ -2129,6 +2129,52 @@ fail: return rc; } +/* Failure to read a privilege mask is never fatal, because we can always + * carry on as though we didn't have the privilege we were interested in. + * So use efx_mcdi_rpc_quiet(). + */ +int efx_mcdi_get_privilege_mask(struct efx_nic *efx, u32 *mask) +{ + MCDI_DECLARE_BUF(fi_outbuf, MC_CMD_GET_FUNCTION_INFO_OUT_LEN); + MCDI_DECLARE_BUF(pm_inbuf, MC_CMD_PRIVILEGE_MASK_IN_LEN); + MCDI_DECLARE_BUF(pm_outbuf, MC_CMD_PRIVILEGE_MASK_OUT_LEN); + size_t outlen; + u16 pf, vf; + int rc; + + if (!efx || !mask) + return -EINVAL; + + /* Get our function number */ + rc = efx_mcdi_rpc_quiet(efx, MC_CMD_GET_FUNCTION_INFO, NULL, 0, + fi_outbuf, MC_CMD_GET_FUNCTION_INFO_OUT_LEN, + &outlen); + if (rc != 0) + return rc; + if (outlen < MC_CMD_GET_FUNCTION_INFO_OUT_LEN) + return -EIO; + + pf = MCDI_DWORD(fi_outbuf, GET_FUNCTION_INFO_OUT_PF); + vf = MCDI_DWORD(fi_outbuf, GET_FUNCTION_INFO_OUT_VF); + + MCDI_POPULATE_DWORD_2(pm_inbuf, PRIVILEGE_MASK_IN_FUNCTION, + PRIVILEGE_MASK_IN_FUNCTION_PF, pf, + PRIVILEGE_MASK_IN_FUNCTION_VF, vf); + + rc = efx_mcdi_rpc_quiet(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; + + *mask = MCDI_DWORD(pm_outbuf, PRIVILEGE_MASK_OUT_OLD_MASK); + + return 0; +} + #ifdef CONFIG_SFC_MTD #define EFX_MCDI_NVRAM_LEN_MAX 128 diff --git a/drivers/net/ethernet/sfc/mcdi.h b/drivers/net/ethernet/sfc/mcdi.h index 69c2924a147c..f74f6ce8b27d 100644 --- a/drivers/net/ethernet/sfc/mcdi.h +++ b/drivers/net/ethernet/sfc/mcdi.h @@ -366,6 +366,7 @@ int efx_mcdi_set_workaround(struct efx_nic *efx, u32 type, bool enabled, unsigned int *flags); int efx_mcdi_get_workarounds(struct efx_nic *efx, unsigned int *impl_out, unsigned int *enabled_out); +int efx_mcdi_get_privilege_mask(struct efx_nic *efx, u32 *mask); #ifdef CONFIG_SFC_MCDI_MON int efx_mcdi_mon_probe(struct efx_nic *efx); From 08135eecd07fbf48e819bdf3ec2a7717e76346ee Mon Sep 17 00:00:00 2001 From: Edward Cree Date: Wed, 20 Jul 2022 19:29:28 +0100 Subject: [PATCH 3/9] sfc: add skeleton ef100 VF representors No net_device_ops yet, just a placeholder netdev created per VF. Signed-off-by: Edward Cree Signed-off-by: David S. Miller --- drivers/net/ethernet/sfc/Makefile | 2 +- drivers/net/ethernet/sfc/ef100_netdev.c | 2 +- drivers/net/ethernet/sfc/ef100_rep.c | 126 ++++++++++++++++++++++++ drivers/net/ethernet/sfc/ef100_rep.h | 37 +++++++ drivers/net/ethernet/sfc/ef100_sriov.c | 32 ++++-- drivers/net/ethernet/sfc/ef100_sriov.h | 2 +- drivers/net/ethernet/sfc/efx_common.c | 2 + drivers/net/ethernet/sfc/net_driver.h | 4 + 8 files changed, 196 insertions(+), 11 deletions(-) create mode 100644 drivers/net/ethernet/sfc/ef100_rep.c create mode 100644 drivers/net/ethernet/sfc/ef100_rep.h diff --git a/drivers/net/ethernet/sfc/Makefile b/drivers/net/ethernet/sfc/Makefile index b9298031ea51..7a6772bfde06 100644 --- a/drivers/net/ethernet/sfc/Makefile +++ b/drivers/net/ethernet/sfc/Makefile @@ -8,7 +8,7 @@ sfc-y += efx.o efx_common.o efx_channels.o nic.o \ ef100.o ef100_nic.o ef100_netdev.o \ ef100_ethtool.o ef100_rx.o ef100_tx.o sfc-$(CONFIG_SFC_MTD) += mtd.o -sfc-$(CONFIG_SFC_SRIOV) += sriov.o ef10_sriov.o ef100_sriov.o +sfc-$(CONFIG_SFC_SRIOV) += sriov.o ef10_sriov.o ef100_sriov.o ef100_rep.o obj-$(CONFIG_SFC) += sfc.o diff --git a/drivers/net/ethernet/sfc/ef100_netdev.c b/drivers/net/ethernet/sfc/ef100_netdev.c index 060392ddd612..f4a124b8ffbe 100644 --- a/drivers/net/ethernet/sfc/ef100_netdev.c +++ b/drivers/net/ethernet/sfc/ef100_netdev.c @@ -312,7 +312,7 @@ void ef100_remove_netdev(struct efx_probe_data *probe_data) unregister_netdevice_notifier(&efx->netdev_notifier); #if defined(CONFIG_SFC_SRIOV) if (!efx->type->is_vf) - efx_ef100_pci_sriov_disable(efx); + efx_ef100_pci_sriov_disable(efx, true); #endif ef100_unregister_netdev(efx); diff --git a/drivers/net/ethernet/sfc/ef100_rep.c b/drivers/net/ethernet/sfc/ef100_rep.c new file mode 100644 index 000000000000..f10c25d6f134 --- /dev/null +++ b/drivers/net/ethernet/sfc/ef100_rep.c @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: GPL-2.0-only +/**************************************************************************** + * Driver for Solarflare network controllers and boards + * Copyright 2019 Solarflare Communications Inc. + * Copyright 2020-2022 Xilinx Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +#include "ef100_rep.h" +#include "ef100_nic.h" + +static int efx_ef100_rep_init_struct(struct efx_nic *efx, struct efx_rep *efv) +{ + efv->parent = efx; + INIT_LIST_HEAD(&efv->list); + efv->msg_enable = NETIF_MSG_DRV | NETIF_MSG_PROBE | + NETIF_MSG_LINK | NETIF_MSG_IFDOWN | + NETIF_MSG_IFUP | NETIF_MSG_RX_ERR | + NETIF_MSG_TX_ERR | NETIF_MSG_HW; + return 0; +} + +static const struct net_device_ops efx_ef100_rep_netdev_ops = { +}; + +static const struct ethtool_ops efx_ef100_rep_ethtool_ops = { +}; + +static struct efx_rep *efx_ef100_rep_create_netdev(struct efx_nic *efx, + unsigned int i) +{ + struct net_device *net_dev; + struct efx_rep *efv; + int rc; + + net_dev = alloc_etherdev_mq(sizeof(*efv), 1); + if (!net_dev) + return ERR_PTR(-ENOMEM); + + efv = netdev_priv(net_dev); + rc = efx_ef100_rep_init_struct(efx, efv); + if (rc) + goto fail1; + efv->net_dev = net_dev; + rtnl_lock(); + spin_lock_bh(&efx->vf_reps_lock); + list_add_tail(&efv->list, &efx->vf_reps); + spin_unlock_bh(&efx->vf_reps_lock); + netif_carrier_off(net_dev); + netif_tx_stop_all_queues(net_dev); + rtnl_unlock(); + + net_dev->netdev_ops = &efx_ef100_rep_netdev_ops; + net_dev->ethtool_ops = &efx_ef100_rep_ethtool_ops; + net_dev->min_mtu = EFX_MIN_MTU; + net_dev->max_mtu = EFX_MAX_MTU; + return efv; +fail1: + free_netdev(net_dev); + return ERR_PTR(rc); +} + +static void efx_ef100_rep_destroy_netdev(struct efx_rep *efv) +{ + struct efx_nic *efx = efv->parent; + + spin_lock_bh(&efx->vf_reps_lock); + list_del(&efv->list); + spin_unlock_bh(&efx->vf_reps_lock); + free_netdev(efv->net_dev); +} + +int efx_ef100_vfrep_create(struct efx_nic *efx, unsigned int i) +{ + struct efx_rep *efv; + int rc; + + efv = efx_ef100_rep_create_netdev(efx, i); + if (IS_ERR(efv)) { + rc = PTR_ERR(efv); + pci_err(efx->pci_dev, + "Failed to create representor for VF %d, rc %d\n", i, + rc); + return rc; + } + rc = register_netdev(efv->net_dev); + if (rc) { + pci_err(efx->pci_dev, + "Failed to register representor for VF %d, rc %d\n", + i, rc); + goto fail; + } + pci_dbg(efx->pci_dev, "Representor for VF %d is %s\n", i, + efv->net_dev->name); + return 0; +fail: + efx_ef100_rep_destroy_netdev(efv); + return rc; +} + +void efx_ef100_vfrep_destroy(struct efx_nic *efx, struct efx_rep *efv) +{ + struct net_device *rep_dev; + + rep_dev = efv->net_dev; + if (!rep_dev) + return; + netif_dbg(efx, drv, rep_dev, "Removing VF representor\n"); + unregister_netdev(rep_dev); + efx_ef100_rep_destroy_netdev(efv); +} + +void efx_ef100_fini_vfreps(struct efx_nic *efx) +{ + struct ef100_nic_data *nic_data = efx->nic_data; + struct efx_rep *efv, *next; + + if (!nic_data->grp_mae) + return; + + list_for_each_entry_safe(efv, next, &efx->vf_reps, list) + efx_ef100_vfrep_destroy(efx, efv); +} diff --git a/drivers/net/ethernet/sfc/ef100_rep.h b/drivers/net/ethernet/sfc/ef100_rep.h new file mode 100644 index 000000000000..7d85811f0adb --- /dev/null +++ b/drivers/net/ethernet/sfc/ef100_rep.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/**************************************************************************** + * Driver for Solarflare network controllers and boards + * Copyright 2019 Solarflare Communications Inc. + * Copyright 2020-2022 Xilinx Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +/* Handling for ef100 representor netdevs */ +#ifndef EF100_REP_H +#define EF100_REP_H + +#include "net_driver.h" + +/** + * struct efx_rep - Private data for an Efx representor + * + * @parent: the efx PF which manages this representor + * @net_dev: representor netdevice + * @msg_enable: log message enable flags + * @list: entry on efx->vf_reps + */ +struct efx_rep { + struct efx_nic *parent; + struct net_device *net_dev; + u32 msg_enable; + struct list_head list; +}; + +int efx_ef100_vfrep_create(struct efx_nic *efx, unsigned int i); +void efx_ef100_vfrep_destroy(struct efx_nic *efx, struct efx_rep *efv); +void efx_ef100_fini_vfreps(struct efx_nic *efx); + +#endif /* EF100_REP_H */ diff --git a/drivers/net/ethernet/sfc/ef100_sriov.c b/drivers/net/ethernet/sfc/ef100_sriov.c index 664578176bfe..94bdbfcb47e8 100644 --- a/drivers/net/ethernet/sfc/ef100_sriov.c +++ b/drivers/net/ethernet/sfc/ef100_sriov.c @@ -11,46 +11,62 @@ #include "ef100_sriov.h" #include "ef100_nic.h" +#include "ef100_rep.h" static int efx_ef100_pci_sriov_enable(struct efx_nic *efx, int num_vfs) { + struct ef100_nic_data *nic_data = efx->nic_data; struct pci_dev *dev = efx->pci_dev; - int rc; + struct efx_rep *efv, *next; + int rc, i; efx->vf_count = num_vfs; rc = pci_enable_sriov(dev, num_vfs); if (rc) - goto fail; + goto fail1; + if (!nic_data->grp_mae) + return 0; + + for (i = 0; i < num_vfs; i++) { + rc = efx_ef100_vfrep_create(efx, i); + if (rc) + goto fail2; + } return 0; -fail: +fail2: + list_for_each_entry_safe(efv, next, &efx->vf_reps, list) + efx_ef100_vfrep_destroy(efx, efv); + pci_disable_sriov(dev); +fail1: netif_err(efx, probe, efx->net_dev, "Failed to enable SRIOV VFs\n"); efx->vf_count = 0; return rc; } -int efx_ef100_pci_sriov_disable(struct efx_nic *efx) +int efx_ef100_pci_sriov_disable(struct efx_nic *efx, bool force) { struct pci_dev *dev = efx->pci_dev; unsigned int vfs_assigned; vfs_assigned = pci_vfs_assigned(dev); - if (vfs_assigned) { + if (vfs_assigned && !force) { netif_info(efx, drv, efx->net_dev, "VFs are assigned to guests; " "please detach them before disabling SR-IOV\n"); return -EBUSY; } - pci_disable_sriov(dev); - + efx_ef100_fini_vfreps(efx); + if (!vfs_assigned) + pci_disable_sriov(dev); return 0; } int efx_ef100_sriov_configure(struct efx_nic *efx, int num_vfs) { if (num_vfs == 0) - return efx_ef100_pci_sriov_disable(efx); + return efx_ef100_pci_sriov_disable(efx, false); else return efx_ef100_pci_sriov_enable(efx, num_vfs); } diff --git a/drivers/net/ethernet/sfc/ef100_sriov.h b/drivers/net/ethernet/sfc/ef100_sriov.h index c48fccd46c57..8ffdf464dd1d 100644 --- a/drivers/net/ethernet/sfc/ef100_sriov.h +++ b/drivers/net/ethernet/sfc/ef100_sriov.h @@ -11,4 +11,4 @@ #include "net_driver.h" int efx_ef100_sriov_configure(struct efx_nic *efx, int num_vfs); -int efx_ef100_pci_sriov_disable(struct efx_nic *efx); +int efx_ef100_pci_sriov_disable(struct efx_nic *efx, bool force); diff --git a/drivers/net/ethernet/sfc/efx_common.c b/drivers/net/ethernet/sfc/efx_common.c index 56eb717bb07a..fb6b66b8707b 100644 --- a/drivers/net/ethernet/sfc/efx_common.c +++ b/drivers/net/ethernet/sfc/efx_common.c @@ -1021,6 +1021,8 @@ int efx_init_struct(struct efx_nic *efx, struct pci_dev *pci_dev) efx->rps_hash_table = kcalloc(EFX_ARFS_HASH_TABLE_SIZE, sizeof(*efx->rps_hash_table), GFP_KERNEL); #endif + spin_lock_init(&efx->vf_reps_lock); + INIT_LIST_HEAD(&efx->vf_reps); INIT_WORK(&efx->mac_work, efx_mac_work); init_waitqueue_head(&efx->flush_wq); diff --git a/drivers/net/ethernet/sfc/net_driver.h b/drivers/net/ethernet/sfc/net_driver.h index 2228c88a7f31..037cfa184764 100644 --- a/drivers/net/ethernet/sfc/net_driver.h +++ b/drivers/net/ethernet/sfc/net_driver.h @@ -966,6 +966,8 @@ enum efx_xdp_tx_queues_mode { * @vf_count: Number of VFs intended to be enabled. * @vf_init_count: Number of VFs that have been fully initialised. * @vi_scale: log2 number of vnics per VF. + * @vf_reps_lock: Protects vf_reps list + * @vf_reps: local VF reps * @ptp_data: PTP state data * @ptp_warned: has this NIC seen and warned about unexpected PTP events? * @vpd_sn: Serial number read from VPD @@ -1145,6 +1147,8 @@ struct efx_nic { unsigned vf_init_count; unsigned vi_scale; #endif + spinlock_t vf_reps_lock; + struct list_head vf_reps; struct efx_ptp_data *ptp_data; bool ptp_warned; From 5687eb3466a90a72d13f2aab20b4b801289dbda9 Mon Sep 17 00:00:00 2001 From: Edward Cree Date: Wed, 20 Jul 2022 19:29:30 +0100 Subject: [PATCH 4/9] sfc: add basic ethtool ops to ef100 reps Signed-off-by: Edward Cree Signed-off-by: David S. Miller --- drivers/net/ethernet/sfc/ef100_rep.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/drivers/net/ethernet/sfc/ef100_rep.c b/drivers/net/ethernet/sfc/ef100_rep.c index f10c25d6f134..1121bf162b2f 100644 --- a/drivers/net/ethernet/sfc/ef100_rep.c +++ b/drivers/net/ethernet/sfc/ef100_rep.c @@ -12,6 +12,8 @@ #include "ef100_rep.h" #include "ef100_nic.h" +#define EFX_EF100_REP_DRIVER "efx_ef100_rep" + static int efx_ef100_rep_init_struct(struct efx_nic *efx, struct efx_rep *efv) { efv->parent = efx; @@ -26,7 +28,31 @@ static int efx_ef100_rep_init_struct(struct efx_nic *efx, struct efx_rep *efv) static const struct net_device_ops efx_ef100_rep_netdev_ops = { }; +static void efx_ef100_rep_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *drvinfo) +{ + strscpy(drvinfo->driver, EFX_EF100_REP_DRIVER, sizeof(drvinfo->driver)); +} + +static u32 efx_ef100_rep_ethtool_get_msglevel(struct net_device *net_dev) +{ + struct efx_rep *efv = netdev_priv(net_dev); + + return efv->msg_enable; +} + +static void efx_ef100_rep_ethtool_set_msglevel(struct net_device *net_dev, + u32 msg_enable) +{ + struct efx_rep *efv = netdev_priv(net_dev); + + efv->msg_enable = msg_enable; +} + static const struct ethtool_ops efx_ef100_rep_ethtool_ops = { + .get_drvinfo = efx_ef100_rep_get_drvinfo, + .get_msglevel = efx_ef100_rep_ethtool_get_msglevel, + .set_msglevel = efx_ef100_rep_ethtool_set_msglevel, }; static struct efx_rep *efx_ef100_rep_create_netdev(struct efx_nic *efx, From e1479556f808b1702a0cb83e823784ccf67c305d Mon Sep 17 00:00:00 2001 From: Edward Cree Date: Wed, 20 Jul 2022 19:29:33 +0100 Subject: [PATCH 5/9] sfc: phys port/switch identification for ef100 reps Requires storing VF index in struct efx_rep. Signed-off-by: Edward Cree Signed-off-by: David S. Miller --- drivers/net/ethernet/sfc/ef100_rep.c | 39 ++++++++++++++++++++++++++-- drivers/net/ethernet/sfc/ef100_rep.h | 2 ++ 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/sfc/ef100_rep.c b/drivers/net/ethernet/sfc/ef100_rep.c index 1121bf162b2f..0b4f7d536ae6 100644 --- a/drivers/net/ethernet/sfc/ef100_rep.c +++ b/drivers/net/ethernet/sfc/ef100_rep.c @@ -14,9 +14,11 @@ #define EFX_EF100_REP_DRIVER "efx_ef100_rep" -static int efx_ef100_rep_init_struct(struct efx_nic *efx, struct efx_rep *efv) +static int efx_ef100_rep_init_struct(struct efx_nic *efx, struct efx_rep *efv, + unsigned int i) { efv->parent = efx; + efv->idx = i; INIT_LIST_HEAD(&efv->list); efv->msg_enable = NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK | NETIF_MSG_IFDOWN | @@ -25,7 +27,40 @@ static int efx_ef100_rep_init_struct(struct efx_nic *efx, struct efx_rep *efv) return 0; } +static int efx_ef100_rep_get_port_parent_id(struct net_device *dev, + struct netdev_phys_item_id *ppid) +{ + struct efx_rep *efv = netdev_priv(dev); + struct efx_nic *efx = efv->parent; + struct ef100_nic_data *nic_data; + + nic_data = efx->nic_data; + /* nic_data->port_id is a u8[] */ + ppid->id_len = sizeof(nic_data->port_id); + memcpy(ppid->id, nic_data->port_id, sizeof(nic_data->port_id)); + return 0; +} + +static int efx_ef100_rep_get_phys_port_name(struct net_device *dev, + char *buf, size_t len) +{ + struct efx_rep *efv = netdev_priv(dev); + struct efx_nic *efx = efv->parent; + struct ef100_nic_data *nic_data; + int ret; + + nic_data = efx->nic_data; + ret = snprintf(buf, len, "p%upf%uvf%u", efx->port_num, + nic_data->pf_index, efv->idx); + if (ret >= len) + return -EOPNOTSUPP; + + return 0; +} + static const struct net_device_ops efx_ef100_rep_netdev_ops = { + .ndo_get_port_parent_id = efx_ef100_rep_get_port_parent_id, + .ndo_get_phys_port_name = efx_ef100_rep_get_phys_port_name, }; static void efx_ef100_rep_get_drvinfo(struct net_device *dev, @@ -67,7 +102,7 @@ static struct efx_rep *efx_ef100_rep_create_netdev(struct efx_nic *efx, return ERR_PTR(-ENOMEM); efv = netdev_priv(net_dev); - rc = efx_ef100_rep_init_struct(efx, efv); + rc = efx_ef100_rep_init_struct(efx, efv, i); if (rc) goto fail1; efv->net_dev = net_dev; diff --git a/drivers/net/ethernet/sfc/ef100_rep.h b/drivers/net/ethernet/sfc/ef100_rep.h index 7d85811f0adb..235565869619 100644 --- a/drivers/net/ethernet/sfc/ef100_rep.h +++ b/drivers/net/ethernet/sfc/ef100_rep.h @@ -21,12 +21,14 @@ * @parent: the efx PF which manages this representor * @net_dev: representor netdevice * @msg_enable: log message enable flags + * @idx: VF index * @list: entry on efx->vf_reps */ struct efx_rep { struct efx_nic *parent; struct net_device *net_dev; u32 msg_enable; + unsigned int idx; struct list_head list; }; From da56552d04c54b68788fb3700b5150814e1de3d1 Mon Sep 17 00:00:00 2001 From: Edward Cree Date: Wed, 20 Jul 2022 19:29:34 +0100 Subject: [PATCH 6/9] sfc: determine representee m-port for EF100 representors An MAE port, or m-port, is a port (source/destination for traffic) on the Match-Action Engine (the internal switch on EF100). Representors will use their representee's m-port for two purposes: as a destination override on TX from the representor, and as a source match in 'default rules' to steer representee traffic (when not matched by e.g. a TC flower rule) to representor RX via the parent PF's receive queue. Signed-off-by: Edward Cree Signed-off-by: David S. Miller --- drivers/net/ethernet/sfc/Makefile | 2 +- drivers/net/ethernet/sfc/ef100_rep.c | 27 +++++++++++++++++ drivers/net/ethernet/sfc/ef100_rep.h | 2 ++ drivers/net/ethernet/sfc/mae.c | 44 ++++++++++++++++++++++++++++ drivers/net/ethernet/sfc/mae.h | 22 ++++++++++++++ 5 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 drivers/net/ethernet/sfc/mae.c create mode 100644 drivers/net/ethernet/sfc/mae.h diff --git a/drivers/net/ethernet/sfc/Makefile b/drivers/net/ethernet/sfc/Makefile index 7a6772bfde06..4c759488fc77 100644 --- a/drivers/net/ethernet/sfc/Makefile +++ b/drivers/net/ethernet/sfc/Makefile @@ -8,7 +8,7 @@ sfc-y += efx.o efx_common.o efx_channels.o nic.o \ ef100.o ef100_nic.o ef100_netdev.o \ ef100_ethtool.o ef100_rx.o ef100_tx.o sfc-$(CONFIG_SFC_MTD) += mtd.o -sfc-$(CONFIG_SFC_SRIOV) += sriov.o ef10_sriov.o ef100_sriov.o ef100_rep.o +sfc-$(CONFIG_SFC_SRIOV) += sriov.o ef10_sriov.o ef100_sriov.o ef100_rep.o mae.o obj-$(CONFIG_SFC) += sfc.o diff --git a/drivers/net/ethernet/sfc/ef100_rep.c b/drivers/net/ethernet/sfc/ef100_rep.c index 0b4f7d536ae6..cf0eac920592 100644 --- a/drivers/net/ethernet/sfc/ef100_rep.c +++ b/drivers/net/ethernet/sfc/ef100_rep.c @@ -11,6 +11,7 @@ #include "ef100_rep.h" #include "ef100_nic.h" +#include "mae.h" #define EFX_EF100_REP_DRIVER "efx_ef100_rep" @@ -124,6 +125,25 @@ fail1: return ERR_PTR(rc); } +static int efx_ef100_configure_rep(struct efx_rep *efv) +{ + struct efx_nic *efx = efv->parent; + u32 selector; + int rc; + + /* Construct mport selector for corresponding VF */ + efx_mae_mport_vf(efx, efv->idx, &selector); + /* Look up actual mport ID */ + rc = efx_mae_lookup_mport(efx, selector, &efv->mport); + if (rc) + return rc; + pci_dbg(efx->pci_dev, "VF %u has mport ID %#x\n", efv->idx, efv->mport); + /* mport label should fit in 16 bits */ + WARN_ON(efv->mport >> 16); + + return 0; +} + static void efx_ef100_rep_destroy_netdev(struct efx_rep *efv) { struct efx_nic *efx = efv->parent; @@ -147,6 +167,13 @@ int efx_ef100_vfrep_create(struct efx_nic *efx, unsigned int i) rc); return rc; } + rc = efx_ef100_configure_rep(efv); + if (rc) { + pci_err(efx->pci_dev, + "Failed to configure representor for VF %d, rc %d\n", + i, rc); + goto fail; + } rc = register_netdev(efv->net_dev); if (rc) { pci_err(efx->pci_dev, diff --git a/drivers/net/ethernet/sfc/ef100_rep.h b/drivers/net/ethernet/sfc/ef100_rep.h index 235565869619..1d17aaf6cd5c 100644 --- a/drivers/net/ethernet/sfc/ef100_rep.h +++ b/drivers/net/ethernet/sfc/ef100_rep.h @@ -21,6 +21,7 @@ * @parent: the efx PF which manages this representor * @net_dev: representor netdevice * @msg_enable: log message enable flags + * @mport: m-port ID of corresponding VF * @idx: VF index * @list: entry on efx->vf_reps */ @@ -28,6 +29,7 @@ struct efx_rep { struct efx_nic *parent; struct net_device *net_dev; u32 msg_enable; + u32 mport; unsigned int idx; struct list_head list; }; diff --git a/drivers/net/ethernet/sfc/mae.c b/drivers/net/ethernet/sfc/mae.c new file mode 100644 index 000000000000..011ebd46ada5 --- /dev/null +++ b/drivers/net/ethernet/sfc/mae.c @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: GPL-2.0-only +/**************************************************************************** + * Driver for Solarflare network controllers and boards + * Copyright 2019 Solarflare Communications Inc. + * Copyright 2020-2022 Xilinx Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +#include "mae.h" +#include "mcdi.h" +#include "mcdi_pcol.h" + +void efx_mae_mport_vf(struct efx_nic *efx __always_unused, u32 vf_id, u32 *out) +{ + efx_dword_t mport; + + EFX_POPULATE_DWORD_3(mport, + MAE_MPORT_SELECTOR_TYPE, MAE_MPORT_SELECTOR_TYPE_FUNC, + MAE_MPORT_SELECTOR_FUNC_PF_ID, MAE_MPORT_SELECTOR_FUNC_PF_ID_CALLER, + MAE_MPORT_SELECTOR_FUNC_VF_ID, vf_id); + *out = EFX_DWORD_VAL(mport); +} + +/* id is really only 24 bits wide */ +int efx_mae_lookup_mport(struct efx_nic *efx, u32 selector, u32 *id) +{ + MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_MPORT_LOOKUP_OUT_LEN); + MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_MPORT_LOOKUP_IN_LEN); + size_t outlen; + int rc; + + MCDI_SET_DWORD(inbuf, MAE_MPORT_LOOKUP_IN_MPORT_SELECTOR, selector); + rc = efx_mcdi_rpc(efx, MC_CMD_MAE_MPORT_LOOKUP, inbuf, sizeof(inbuf), + outbuf, sizeof(outbuf), &outlen); + if (rc) + return rc; + if (outlen < sizeof(outbuf)) + return -EIO; + *id = MCDI_DWORD(outbuf, MAE_MPORT_LOOKUP_OUT_MPORT_ID); + return 0; +} diff --git a/drivers/net/ethernet/sfc/mae.h b/drivers/net/ethernet/sfc/mae.h new file mode 100644 index 000000000000..27e69e8a54b6 --- /dev/null +++ b/drivers/net/ethernet/sfc/mae.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/**************************************************************************** + * Driver for Solarflare network controllers and boards + * Copyright 2019 Solarflare Communications Inc. + * Copyright 2020-2022 Xilinx Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +#ifndef EF100_MAE_H +#define EF100_MAE_H +/* MCDI interface for the ef100 Match-Action Engine */ + +#include "net_driver.h" + +void efx_mae_mport_vf(struct efx_nic *efx, u32 vf_id, u32 *out); + +int efx_mae_lookup_mport(struct efx_nic *efx, u32 selector, u32 *id); + +#endif /* EF100_MAE_H */ From 02443ab8c9314134c9cd58946121726e4cd9a5c1 Mon Sep 17 00:00:00 2001 From: Edward Cree Date: Wed, 20 Jul 2022 19:33:47 +0100 Subject: [PATCH 7/9] sfc: support passing a representor to the EF100 TX path A non-null efv in __ef100_enqueue_skb() indicates that the packet is from that representor, should be transmitted with a suitable option descriptor (to instruct the switch to deliver it to the representee), and should not be accounted to the parent PF's stats or BQL. Signed-off-by: Edward Cree Signed-off-by: David S. Miller --- drivers/net/ethernet/sfc/ef100_rep.h | 8 +++ drivers/net/ethernet/sfc/ef100_tx.c | 84 +++++++++++++++++++++++++-- drivers/net/ethernet/sfc/ef100_tx.h | 3 + drivers/net/ethernet/sfc/net_driver.h | 1 + drivers/net/ethernet/sfc/tx.c | 6 +- drivers/net/ethernet/sfc/tx_common.c | 35 +++++++---- drivers/net/ethernet/sfc/tx_common.h | 3 +- 7 files changed, 123 insertions(+), 17 deletions(-) diff --git a/drivers/net/ethernet/sfc/ef100_rep.h b/drivers/net/ethernet/sfc/ef100_rep.h index 1d17aaf6cd5c..d47fd8ff6220 100644 --- a/drivers/net/ethernet/sfc/ef100_rep.h +++ b/drivers/net/ethernet/sfc/ef100_rep.h @@ -15,6 +15,12 @@ #include "net_driver.h" +struct efx_rep_sw_stats { + atomic64_t rx_packets, tx_packets; + atomic64_t rx_bytes, tx_bytes; + atomic64_t rx_dropped, tx_errors; +}; + /** * struct efx_rep - Private data for an Efx representor * @@ -24,6 +30,7 @@ * @mport: m-port ID of corresponding VF * @idx: VF index * @list: entry on efx->vf_reps + * @stats: software traffic counters for netdev stats */ struct efx_rep { struct efx_nic *parent; @@ -32,6 +39,7 @@ struct efx_rep { u32 mport; unsigned int idx; struct list_head list; + struct efx_rep_sw_stats stats; }; int efx_ef100_vfrep_create(struct efx_nic *efx, unsigned int i); diff --git a/drivers/net/ethernet/sfc/ef100_tx.c b/drivers/net/ethernet/sfc/ef100_tx.c index 26ef51d6b542..102ddc7e206a 100644 --- a/drivers/net/ethernet/sfc/ef100_tx.c +++ b/drivers/net/ethernet/sfc/ef100_tx.c @@ -254,7 +254,8 @@ static void ef100_make_tso_desc(struct efx_nic *efx, static void ef100_tx_make_descriptors(struct efx_tx_queue *tx_queue, const struct sk_buff *skb, - unsigned int segment_count) + unsigned int segment_count, + struct efx_rep *efv) { unsigned int old_write_count = tx_queue->write_count; unsigned int new_write_count = old_write_count; @@ -272,6 +273,20 @@ static void ef100_tx_make_descriptors(struct efx_tx_queue *tx_queue, else next_desc_type = ESE_GZ_TX_DESC_TYPE_SEND; + if (unlikely(efv)) { + /* Create TX override descriptor */ + write_ptr = new_write_count & tx_queue->ptr_mask; + txd = ef100_tx_desc(tx_queue, write_ptr); + ++new_write_count; + + tx_queue->packet_write_count = new_write_count; + EFX_POPULATE_OWORD_3(*txd, + ESF_GZ_TX_DESC_TYPE, ESE_GZ_TX_DESC_TYPE_PREFIX, + ESF_GZ_TX_PREFIX_EGRESS_MPORT, efv->mport, + ESF_GZ_TX_PREFIX_EGRESS_MPORT_EN, 1); + nr_descs--; + } + /* if it's a raw write (such as XDP) then always SEND single frames */ if (!skb) nr_descs = 1; @@ -306,6 +321,9 @@ static void ef100_tx_make_descriptors(struct efx_tx_queue *tx_queue, /* if it's a raw write (such as XDP) then always SEND */ next_desc_type = skb ? ESE_GZ_TX_DESC_TYPE_SEG : ESE_GZ_TX_DESC_TYPE_SEND; + /* mark as an EFV buffer if applicable */ + if (unlikely(efv)) + buffer->flags |= EFX_TX_BUF_EFV; } while (new_write_count != tx_queue->insert_count); @@ -324,7 +342,7 @@ static void ef100_tx_make_descriptors(struct efx_tx_queue *tx_queue, void ef100_tx_write(struct efx_tx_queue *tx_queue) { - ef100_tx_make_descriptors(tx_queue, NULL, 0); + ef100_tx_make_descriptors(tx_queue, NULL, 0, NULL); ef100_tx_push_buffers(tx_queue); } @@ -350,6 +368,12 @@ void ef100_ev_tx(struct efx_channel *channel, const efx_qword_t *p_event) * function will free the SKB. */ int ef100_enqueue_skb(struct efx_tx_queue *tx_queue, struct sk_buff *skb) +{ + return __ef100_enqueue_skb(tx_queue, skb, NULL); +} + +int __ef100_enqueue_skb(struct efx_tx_queue *tx_queue, struct sk_buff *skb, + struct efx_rep *efv) { unsigned int old_insert_count = tx_queue->insert_count; struct efx_nic *efx = tx_queue->efx; @@ -376,16 +400,64 @@ int ef100_enqueue_skb(struct efx_tx_queue *tx_queue, struct sk_buff *skb) return 0; } + if (unlikely(efv)) { + struct efx_tx_buffer *buffer = __efx_tx_queue_get_insert_buffer(tx_queue); + + /* Drop representor packets if the queue is stopped. + * We currently don't assert backoff to representors so this is + * to make sure representor traffic can't starve the main + * net device. + * And, of course, if there are no TX descriptors left. + */ + if (netif_tx_queue_stopped(tx_queue->core_txq) || + unlikely(efx_tx_buffer_in_use(buffer))) { + atomic64_inc(&efv->stats.tx_errors); + rc = -ENOSPC; + goto err; + } + + /* Also drop representor traffic if it could cause us to + * stop the queue. If we assert backoff and we haven't + * received traffic on the main net device recently then the + * TX watchdog can go off erroneously. + */ + fill_level = efx_channel_tx_old_fill_level(tx_queue->channel); + fill_level += efx_tx_max_skb_descs(efx); + if (fill_level > efx->txq_stop_thresh) { + struct efx_tx_queue *txq2; + + /* Refresh cached fill level and re-check */ + efx_for_each_channel_tx_queue(txq2, tx_queue->channel) + txq2->old_read_count = READ_ONCE(txq2->read_count); + + fill_level = efx_channel_tx_old_fill_level(tx_queue->channel); + fill_level += efx_tx_max_skb_descs(efx); + if (fill_level > efx->txq_stop_thresh) { + atomic64_inc(&efv->stats.tx_errors); + rc = -ENOSPC; + goto err; + } + } + + buffer->flags = EFX_TX_BUF_OPTION | EFX_TX_BUF_EFV; + tx_queue->insert_count++; + } + /* Map for DMA and create descriptors */ rc = efx_tx_map_data(tx_queue, skb, segments); if (rc) goto err; - ef100_tx_make_descriptors(tx_queue, skb, segments); + ef100_tx_make_descriptors(tx_queue, skb, segments, efv); fill_level = efx_channel_tx_old_fill_level(tx_queue->channel); if (fill_level > efx->txq_stop_thresh) { struct efx_tx_queue *txq2; + /* Because of checks above, representor traffic should + * not be able to stop the queue. + */ + WARN_ON(efv); + netif_tx_stop_queue(tx_queue->core_txq); /* Re-read after a memory barrier in case we've raced with * the completion path. Otherwise there's a danger we'll never @@ -404,8 +476,12 @@ int ef100_enqueue_skb(struct efx_tx_queue *tx_queue, struct sk_buff *skb) /* If xmit_more then we don't need to push the doorbell, unless there * are 256 descriptors already queued in which case we have to push to * ensure we never push more than 256 at once. + * + * Always push for representor traffic, and don't account it to parent + * PF netdevice's BQL. */ - if (__netdev_tx_sent_queue(tx_queue->core_txq, skb->len, xmit_more) || + if (unlikely(efv) || + __netdev_tx_sent_queue(tx_queue->core_txq, skb->len, xmit_more) || tx_queue->write_count - tx_queue->notify_count > 255) ef100_tx_push_buffers(tx_queue); diff --git a/drivers/net/ethernet/sfc/ef100_tx.h b/drivers/net/ethernet/sfc/ef100_tx.h index ddc4b98fa6db..e9e11540fcde 100644 --- a/drivers/net/ethernet/sfc/ef100_tx.h +++ b/drivers/net/ethernet/sfc/ef100_tx.h @@ -13,6 +13,7 @@ #define EFX_EF100_TX_H #include "net_driver.h" +#include "ef100_rep.h" int ef100_tx_probe(struct efx_tx_queue *tx_queue); void ef100_tx_init(struct efx_tx_queue *tx_queue); @@ -22,4 +23,6 @@ unsigned int ef100_tx_max_skb_descs(struct efx_nic *efx); void ef100_ev_tx(struct efx_channel *channel, const efx_qword_t *p_event); netdev_tx_t ef100_enqueue_skb(struct efx_tx_queue *tx_queue, struct sk_buff *skb); +int __ef100_enqueue_skb(struct efx_tx_queue *tx_queue, struct sk_buff *skb, + struct efx_rep *efv); #endif diff --git a/drivers/net/ethernet/sfc/net_driver.h b/drivers/net/ethernet/sfc/net_driver.h index 037cfa184764..4cde54cf77b9 100644 --- a/drivers/net/ethernet/sfc/net_driver.h +++ b/drivers/net/ethernet/sfc/net_driver.h @@ -178,6 +178,7 @@ struct efx_tx_buffer { #define EFX_TX_BUF_OPTION 0x10 /* empty buffer for option descriptor */ #define EFX_TX_BUF_XDP 0x20 /* buffer was sent with XDP */ #define EFX_TX_BUF_TSO_V3 0x40 /* empty buffer for a TSO_V3 descriptor */ +#define EFX_TX_BUF_EFV 0x100 /* buffer was sent from representor */ /** * struct efx_tx_queue - An Efx TX queue diff --git a/drivers/net/ethernet/sfc/tx.c b/drivers/net/ethernet/sfc/tx.c index 79cc0bb76321..d12474042c84 100644 --- a/drivers/net/ethernet/sfc/tx.c +++ b/drivers/net/ethernet/sfc/tx.c @@ -559,6 +559,7 @@ netdev_tx_t efx_hard_start_xmit(struct sk_buff *skb, void efx_xmit_done_single(struct efx_tx_queue *tx_queue) { unsigned int pkts_compl = 0, bytes_compl = 0; + unsigned int efv_pkts_compl = 0; unsigned int read_ptr; bool finished = false; @@ -580,7 +581,8 @@ void efx_xmit_done_single(struct efx_tx_queue *tx_queue) /* Need to check the flag before dequeueing. */ if (buffer->flags & EFX_TX_BUF_SKB) finished = true; - efx_dequeue_buffer(tx_queue, buffer, &pkts_compl, &bytes_compl); + efx_dequeue_buffer(tx_queue, buffer, &pkts_compl, &bytes_compl, + &efv_pkts_compl); ++tx_queue->read_count; read_ptr = tx_queue->read_count & tx_queue->ptr_mask; @@ -589,7 +591,7 @@ void efx_xmit_done_single(struct efx_tx_queue *tx_queue) tx_queue->pkts_compl += pkts_compl; tx_queue->bytes_compl += bytes_compl; - EFX_WARN_ON_PARANOID(pkts_compl != 1); + EFX_WARN_ON_PARANOID(pkts_compl + efv_pkts_compl != 1); efx_xmit_done_check_empty(tx_queue); } diff --git a/drivers/net/ethernet/sfc/tx_common.c b/drivers/net/ethernet/sfc/tx_common.c index 658ea2d34070..67e789b96c43 100644 --- a/drivers/net/ethernet/sfc/tx_common.c +++ b/drivers/net/ethernet/sfc/tx_common.c @@ -109,9 +109,11 @@ void efx_fini_tx_queue(struct efx_tx_queue *tx_queue) /* Free any buffers left in the ring */ while (tx_queue->read_count != tx_queue->write_count) { unsigned int pkts_compl = 0, bytes_compl = 0; + unsigned int efv_pkts_compl = 0; buffer = &tx_queue->buffer[tx_queue->read_count & tx_queue->ptr_mask]; - efx_dequeue_buffer(tx_queue, buffer, &pkts_compl, &bytes_compl); + efx_dequeue_buffer(tx_queue, buffer, &pkts_compl, &bytes_compl, + &efv_pkts_compl); ++tx_queue->read_count; } @@ -146,7 +148,8 @@ void efx_remove_tx_queue(struct efx_tx_queue *tx_queue) void efx_dequeue_buffer(struct efx_tx_queue *tx_queue, struct efx_tx_buffer *buffer, unsigned int *pkts_compl, - unsigned int *bytes_compl) + unsigned int *bytes_compl, + unsigned int *efv_pkts_compl) { if (buffer->unmap_len) { struct device *dma_dev = &tx_queue->efx->pci_dev->dev; @@ -164,9 +167,15 @@ void efx_dequeue_buffer(struct efx_tx_queue *tx_queue, if (buffer->flags & EFX_TX_BUF_SKB) { struct sk_buff *skb = (struct sk_buff *)buffer->skb; - EFX_WARN_ON_PARANOID(!pkts_compl || !bytes_compl); - (*pkts_compl)++; - (*bytes_compl) += skb->len; + if (unlikely(buffer->flags & EFX_TX_BUF_EFV)) { + EFX_WARN_ON_PARANOID(!efv_pkts_compl); + (*efv_pkts_compl)++; + } else { + EFX_WARN_ON_PARANOID(!pkts_compl || !bytes_compl); + (*pkts_compl)++; + (*bytes_compl) += skb->len; + } + if (tx_queue->timestamping && (tx_queue->completed_timestamp_major || tx_queue->completed_timestamp_minor)) { @@ -199,7 +208,8 @@ void efx_dequeue_buffer(struct efx_tx_queue *tx_queue, static void efx_dequeue_buffers(struct efx_tx_queue *tx_queue, unsigned int index, unsigned int *pkts_compl, - unsigned int *bytes_compl) + unsigned int *bytes_compl, + unsigned int *efv_pkts_compl) { struct efx_nic *efx = tx_queue->efx; unsigned int stop_index, read_ptr; @@ -218,7 +228,8 @@ static void efx_dequeue_buffers(struct efx_tx_queue *tx_queue, return; } - efx_dequeue_buffer(tx_queue, buffer, pkts_compl, bytes_compl); + efx_dequeue_buffer(tx_queue, buffer, pkts_compl, bytes_compl, + efv_pkts_compl); ++tx_queue->read_count; read_ptr = tx_queue->read_count & tx_queue->ptr_mask; @@ -241,15 +252,17 @@ void efx_xmit_done_check_empty(struct efx_tx_queue *tx_queue) void efx_xmit_done(struct efx_tx_queue *tx_queue, unsigned int index) { unsigned int fill_level, pkts_compl = 0, bytes_compl = 0; + unsigned int efv_pkts_compl = 0; struct efx_nic *efx = tx_queue->efx; EFX_WARN_ON_ONCE_PARANOID(index > tx_queue->ptr_mask); - efx_dequeue_buffers(tx_queue, index, &pkts_compl, &bytes_compl); + efx_dequeue_buffers(tx_queue, index, &pkts_compl, &bytes_compl, + &efv_pkts_compl); tx_queue->pkts_compl += pkts_compl; tx_queue->bytes_compl += bytes_compl; - if (pkts_compl > 1) + if (pkts_compl + efv_pkts_compl > 1) ++tx_queue->merge_events; /* See if we need to restart the netif queue. This memory @@ -274,6 +287,7 @@ void efx_xmit_done(struct efx_tx_queue *tx_queue, unsigned int index) void efx_enqueue_unwind(struct efx_tx_queue *tx_queue, unsigned int insert_count) { + unsigned int efv_pkts_compl = 0; struct efx_tx_buffer *buffer; unsigned int bytes_compl = 0; unsigned int pkts_compl = 0; @@ -282,7 +296,8 @@ void efx_enqueue_unwind(struct efx_tx_queue *tx_queue, while (tx_queue->insert_count != insert_count) { --tx_queue->insert_count; buffer = __efx_tx_queue_get_insert_buffer(tx_queue); - efx_dequeue_buffer(tx_queue, buffer, &pkts_compl, &bytes_compl); + efx_dequeue_buffer(tx_queue, buffer, &pkts_compl, &bytes_compl, + &efv_pkts_compl); } } diff --git a/drivers/net/ethernet/sfc/tx_common.h b/drivers/net/ethernet/sfc/tx_common.h index bbab7f248250..d87aecbc7bf1 100644 --- a/drivers/net/ethernet/sfc/tx_common.h +++ b/drivers/net/ethernet/sfc/tx_common.h @@ -19,7 +19,8 @@ void efx_remove_tx_queue(struct efx_tx_queue *tx_queue); void efx_dequeue_buffer(struct efx_tx_queue *tx_queue, struct efx_tx_buffer *buffer, unsigned int *pkts_compl, - unsigned int *bytes_compl); + unsigned int *bytes_compl, + unsigned int *efv_pkts_compl); static inline bool efx_tx_buffer_in_use(struct efx_tx_buffer *buffer) { From f72c38fad234759fe943cb2e40bf3d0f7de1d4d9 Mon Sep 17 00:00:00 2001 From: Edward Cree Date: Wed, 20 Jul 2022 19:33:48 +0100 Subject: [PATCH 8/9] sfc: hook up ef100 representor TX Implement .ndo_start_xmit() by calling into the parent PF's TX path. Signed-off-by: Edward Cree Signed-off-by: David S. Miller --- drivers/net/ethernet/sfc/ef100_netdev.c | 11 ++++++++++- drivers/net/ethernet/sfc/ef100_netdev.h | 5 +++++ drivers/net/ethernet/sfc/ef100_rep.c | 23 +++++++++++++++++++++++ 3 files changed, 38 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/sfc/ef100_netdev.c b/drivers/net/ethernet/sfc/ef100_netdev.c index f4a124b8ffbe..3443477c26da 100644 --- a/drivers/net/ethernet/sfc/ef100_netdev.c +++ b/drivers/net/ethernet/sfc/ef100_netdev.c @@ -195,6 +195,15 @@ static netdev_tx_t ef100_hard_start_xmit(struct sk_buff *skb, struct net_device *net_dev) { struct efx_nic *efx = efx_netdev_priv(net_dev); + + return __ef100_hard_start_xmit(skb, efx, net_dev, NULL); +} + +netdev_tx_t __ef100_hard_start_xmit(struct sk_buff *skb, + struct efx_nic *efx, + struct net_device *net_dev, + struct efx_rep *efv) +{ struct efx_tx_queue *tx_queue; struct efx_channel *channel; int rc; @@ -209,7 +218,7 @@ static netdev_tx_t ef100_hard_start_xmit(struct sk_buff *skb, } tx_queue = &channel->tx_queue[0]; - rc = ef100_enqueue_skb(tx_queue, skb); + rc = __ef100_enqueue_skb(tx_queue, skb, efv); if (rc == 0) return NETDEV_TX_OK; diff --git a/drivers/net/ethernet/sfc/ef100_netdev.h b/drivers/net/ethernet/sfc/ef100_netdev.h index 38b032ba0953..86bf985e0951 100644 --- a/drivers/net/ethernet/sfc/ef100_netdev.h +++ b/drivers/net/ethernet/sfc/ef100_netdev.h @@ -10,7 +10,12 @@ */ #include +#include "ef100_rep.h" +netdev_tx_t __ef100_hard_start_xmit(struct sk_buff *skb, + struct efx_nic *efx, + struct net_device *net_dev, + struct efx_rep *efv); int ef100_netdev_event(struct notifier_block *this, unsigned long event, void *ptr); int ef100_probe_netdev(struct efx_probe_data *probe_data); diff --git a/drivers/net/ethernet/sfc/ef100_rep.c b/drivers/net/ethernet/sfc/ef100_rep.c index cf0eac920592..6d4c3f0eee0a 100644 --- a/drivers/net/ethernet/sfc/ef100_rep.c +++ b/drivers/net/ethernet/sfc/ef100_rep.c @@ -10,6 +10,7 @@ */ #include "ef100_rep.h" +#include "ef100_netdev.h" #include "ef100_nic.h" #include "mae.h" @@ -28,6 +29,25 @@ static int efx_ef100_rep_init_struct(struct efx_nic *efx, struct efx_rep *efv, return 0; } +static netdev_tx_t efx_ef100_rep_xmit(struct sk_buff *skb, + struct net_device *dev) +{ + struct efx_rep *efv = netdev_priv(dev); + struct efx_nic *efx = efv->parent; + netdev_tx_t rc; + + /* __ef100_hard_start_xmit() will always return success even in the + * case of TX drops, where it will increment efx's tx_dropped. The + * efv stats really only count attempted TX, not success/failure. + */ + atomic64_inc(&efv->stats.tx_packets); + atomic64_add(skb->len, &efv->stats.tx_bytes); + netif_tx_lock(efx->net_dev); + rc = __ef100_hard_start_xmit(skb, efx, dev, efv); + netif_tx_unlock(efx->net_dev); + return rc; +} + static int efx_ef100_rep_get_port_parent_id(struct net_device *dev, struct netdev_phys_item_id *ppid) { @@ -60,6 +80,7 @@ static int efx_ef100_rep_get_phys_port_name(struct net_device *dev, } static const struct net_device_ops efx_ef100_rep_netdev_ops = { + .ndo_start_xmit = efx_ef100_rep_xmit, .ndo_get_port_parent_id = efx_ef100_rep_get_port_parent_id, .ndo_get_phys_port_name = efx_ef100_rep_get_phys_port_name, }; @@ -119,6 +140,8 @@ static struct efx_rep *efx_ef100_rep_create_netdev(struct efx_nic *efx, net_dev->ethtool_ops = &efx_ef100_rep_ethtool_ops; net_dev->min_mtu = EFX_MIN_MTU; net_dev->max_mtu = EFX_MAX_MTU; + net_dev->features |= NETIF_F_LLTX; + net_dev->hw_features |= NETIF_F_LLTX; return efv; fail1: free_netdev(net_dev); From 84e7fc2591f72987b43da91b3fdb01a196204379 Mon Sep 17 00:00:00 2001 From: Edward Cree Date: Wed, 20 Jul 2022 19:33:49 +0100 Subject: [PATCH 9/9] sfc: attach/detach EF100 representors along with their owning PF Since representors piggy-back on the PF's queues for TX, they can only accept new TXes while the PF is up. Thus, any operation which detaches the PF must first detach all its VFreps. Signed-off-by: Edward Cree Signed-off-by: David S. Miller --- drivers/net/ethernet/sfc/ef100_netdev.c | 3 +++ drivers/net/ethernet/sfc/ef100_rep.c | 11 ++++++-- drivers/net/ethernet/sfc/efx.h | 9 ++++++- drivers/net/ethernet/sfc/efx_common.c | 36 +++++++++++++++++++++++++ drivers/net/ethernet/sfc/efx_common.h | 3 +++ 5 files changed, 59 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/sfc/ef100_netdev.c b/drivers/net/ethernet/sfc/ef100_netdev.c index 3443477c26da..9e65de1ab889 100644 --- a/drivers/net/ethernet/sfc/ef100_netdev.c +++ b/drivers/net/ethernet/sfc/ef100_netdev.c @@ -85,6 +85,7 @@ static int ef100_net_stop(struct net_device *net_dev) netif_dbg(efx, ifdown, efx->net_dev, "closing on CPU %d\n", raw_smp_processor_id()); + efx_detach_reps(efx); netif_stop_queue(net_dev); efx_stop_all(efx); efx_mcdi_mac_fini_stats(efx); @@ -176,6 +177,8 @@ static int ef100_net_open(struct net_device *net_dev) mutex_unlock(&efx->mac_lock); efx->state = STATE_NET_UP; + if (netif_running(efx->net_dev)) + efx_attach_reps(efx); return 0; diff --git a/drivers/net/ethernet/sfc/ef100_rep.c b/drivers/net/ethernet/sfc/ef100_rep.c index 6d4c3f0eee0a..d07539f091b8 100644 --- a/drivers/net/ethernet/sfc/ef100_rep.c +++ b/drivers/net/ethernet/sfc/ef100_rep.c @@ -132,8 +132,13 @@ static struct efx_rep *efx_ef100_rep_create_netdev(struct efx_nic *efx, spin_lock_bh(&efx->vf_reps_lock); list_add_tail(&efv->list, &efx->vf_reps); spin_unlock_bh(&efx->vf_reps_lock); - netif_carrier_off(net_dev); - netif_tx_stop_all_queues(net_dev); + if (netif_running(efx->net_dev) && efx->state == STATE_NET_UP) { + netif_device_attach(net_dev); + netif_carrier_on(net_dev); + } else { + netif_carrier_off(net_dev); + netif_tx_stop_all_queues(net_dev); + } rtnl_unlock(); net_dev->netdev_ops = &efx_ef100_rep_netdev_ops; @@ -171,9 +176,11 @@ static void efx_ef100_rep_destroy_netdev(struct efx_rep *efv) { struct efx_nic *efx = efv->parent; + rtnl_lock(); spin_lock_bh(&efx->vf_reps_lock); list_del(&efv->list); spin_unlock_bh(&efx->vf_reps_lock); + rtnl_unlock(); free_netdev(efv->net_dev); } diff --git a/drivers/net/ethernet/sfc/efx.h b/drivers/net/ethernet/sfc/efx.h index c05a83da9e44..4239c7ece123 100644 --- a/drivers/net/ethernet/sfc/efx.h +++ b/drivers/net/ethernet/sfc/efx.h @@ -12,6 +12,7 @@ #include "net_driver.h" #include "ef100_rx.h" #include "ef100_tx.h" +#include "efx_common.h" #include "filter.h" int efx_net_open(struct net_device *net_dev); @@ -206,6 +207,9 @@ static inline void efx_device_detach_sync(struct efx_nic *efx) { struct net_device *dev = efx->net_dev; + /* We must stop reps (which use our TX) before we stop ourselves. */ + efx_detach_reps(efx); + /* Lock/freeze all TX queues so that we can be sure the * TX scheduler is stopped when we're done and before * netif_device_present() becomes false. @@ -217,8 +221,11 @@ static inline void efx_device_detach_sync(struct efx_nic *efx) static inline void efx_device_attach_if_not_resetting(struct efx_nic *efx) { - if ((efx->state != STATE_DISABLED) && !efx->reset_pending) + if ((efx->state != STATE_DISABLED) && !efx->reset_pending) { netif_device_attach(efx->net_dev); + if (efx->state == STATE_NET_UP) + efx_attach_reps(efx); + } } static inline bool efx_rwsem_assert_write_locked(struct rw_semaphore *sem) diff --git a/drivers/net/ethernet/sfc/efx_common.c b/drivers/net/ethernet/sfc/efx_common.c index fb6b66b8707b..a929a1aaba92 100644 --- a/drivers/net/ethernet/sfc/efx_common.c +++ b/drivers/net/ethernet/sfc/efx_common.c @@ -24,6 +24,7 @@ #include "mcdi_port_common.h" #include "io.h" #include "mcdi_pcol.h" +#include "ef100_rep.h" static unsigned int debug = (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK | NETIF_MSG_IFDOWN | @@ -1391,3 +1392,38 @@ int efx_get_phys_port_name(struct net_device *net_dev, char *name, size_t len) return -EINVAL; return 0; } + +void efx_detach_reps(struct efx_nic *efx) +{ + struct net_device *rep_dev; + struct efx_rep *efv; + + ASSERT_RTNL(); + netif_dbg(efx, drv, efx->net_dev, "Detaching VF representors\n"); + list_for_each_entry(efv, &efx->vf_reps, list) { + rep_dev = efv->net_dev; + if (!rep_dev) + continue; + netif_carrier_off(rep_dev); + /* See efx_device_detach_sync() */ + netif_tx_lock_bh(rep_dev); + netif_tx_stop_all_queues(rep_dev); + netif_tx_unlock_bh(rep_dev); + } +} + +void efx_attach_reps(struct efx_nic *efx) +{ + struct net_device *rep_dev; + struct efx_rep *efv; + + ASSERT_RTNL(); + netif_dbg(efx, drv, efx->net_dev, "Attaching VF representors\n"); + list_for_each_entry(efv, &efx->vf_reps, list) { + rep_dev = efv->net_dev; + if (!rep_dev) + continue; + netif_tx_wake_all_queues(rep_dev); + netif_carrier_on(rep_dev); + } +} diff --git a/drivers/net/ethernet/sfc/efx_common.h b/drivers/net/ethernet/sfc/efx_common.h index 93babc1a2678..2c54dac3e662 100644 --- a/drivers/net/ethernet/sfc/efx_common.h +++ b/drivers/net/ethernet/sfc/efx_common.h @@ -111,4 +111,7 @@ int efx_get_phys_port_id(struct net_device *net_dev, int efx_get_phys_port_name(struct net_device *net_dev, char *name, size_t len); + +void efx_detach_reps(struct efx_nic *efx); +void efx_attach_reps(struct efx_nic *efx); #endif