diff options
author | David S. Miller <davem@davemloft.net> | 2020-01-27 13:05:43 +0100 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2020-01-27 13:05:43 +0100 |
commit | 7d9996870ae49b4febe8b6ac458a2f8f70cea132 (patch) | |
tree | 6cbb1fb7d87622304adf5157cd0b413f1f802818 | |
parent | 4d94e4350e119050be5122a01849d000024d5c88 (diff) | |
parent | 6c77065bbdff817cea4c391720b120d4dd81e1c8 (diff) |
Merge branch 'sfc-refactor-mcdi-filtering-code'
Alex Maftei says:
====================
sfc: refactor mcdi filtering code
Splitting final bits of the driver code into different files, which
will later be used in another driver for a new product.
This is a continuation to my previous patch series. (three of them)
Refactoring will be concluded with this series, for now.
As instructed, split the renaming and moving into different patches.
Removed stray spaces before tabs... twice.
Minor refactoring was done with the renaming, as explained in the
first patch.
====================
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | drivers/net/ethernet/sfc/Makefile | 2 | ||||
-rw-r--r-- | drivers/net/ethernet/sfc/ef10.c | 2418 | ||||
-rw-r--r-- | drivers/net/ethernet/sfc/mcdi_filters.c | 2270 | ||||
-rw-r--r-- | drivers/net/ethernet/sfc/mcdi_filters.h | 159 |
4 files changed, 2476 insertions, 2373 deletions
diff --git a/drivers/net/ethernet/sfc/Makefile b/drivers/net/ethernet/sfc/Makefile index 890fd65caa2d..87d093da22ca 100644 --- a/drivers/net/ethernet/sfc/Makefile +++ b/drivers/net/ethernet/sfc/Makefile @@ -4,7 +4,7 @@ sfc-y += efx.o efx_common.o efx_channels.o nic.o \ tx.o tx_common.o tx_tso.o rx.o rx_common.o \ selftest.o ethtool.o ethtool_common.o ptp.o \ mcdi.o mcdi_port.o mcdi_port_common.o \ - mcdi_functions.o mcdi_mon.o + mcdi_functions.o mcdi_filters.o mcdi_mon.o sfc-$(CONFIG_SFC_MTD) += mtd.o sfc-$(CONFIG_SFC_SRIOV) += sriov.o siena_sriov.o ef10_sriov.o diff --git a/drivers/net/ethernet/sfc/ef10.c b/drivers/net/ethernet/sfc/ef10.c index 4997f61de3d6..397f3bc7da3b 100644 --- a/drivers/net/ethernet/sfc/ef10.c +++ b/drivers/net/ethernet/sfc/ef10.c @@ -13,6 +13,7 @@ #include "mcdi_port_common.h" #include "mcdi_functions.h" #include "nic.h" +#include "mcdi_filters.h" #include "workarounds.h" #include "selftest.h" #include "ef10_sriov.h" @@ -28,28 +29,6 @@ enum { EFX_EF10_TEST = 1, EFX_EF10_REFILL, }; -/* The maximum size of a shared RSS context */ -/* TODO: this should really be from the mcdi protocol export */ -#define EFX_EF10_MAX_SHARED_RSS_CONTEXT_SIZE 64UL - -/* The filter table(s) are managed by firmware and we have write-only - * access. When removing filters we must identify them to the - * firmware by a 64-bit handle, but this is too wide for Linux kernel - * interfaces (32-bit for RX NFC, 16-bit for RFS). Also, we need to - * be able to tell in advance whether a requested insertion will - * replace an existing filter. Therefore we maintain a software hash - * table, which should be at least as large as the hardware hash - * table. - * - * Huntington has a single 8K filter table shared between all filter - * types and both ports. - */ -#define HUNT_FILTER_TBL_ROWS 8192 - -#define EFX_EF10_FILTER_ID_INVALID 0xffff - -#define EFX_EF10_FILTER_DEV_UC_MAX 32 -#define EFX_EF10_FILTER_DEV_MC_MAX 256 /* VLAN list entry */ struct efx_ef10_vlan { @@ -57,95 +36,8 @@ struct efx_ef10_vlan { u16 vid; }; -enum efx_ef10_default_filters { - EFX_EF10_BCAST, - EFX_EF10_UCDEF, - EFX_EF10_MCDEF, - EFX_EF10_VXLAN4_UCDEF, - EFX_EF10_VXLAN4_MCDEF, - EFX_EF10_VXLAN6_UCDEF, - EFX_EF10_VXLAN6_MCDEF, - EFX_EF10_NVGRE4_UCDEF, - EFX_EF10_NVGRE4_MCDEF, - EFX_EF10_NVGRE6_UCDEF, - EFX_EF10_NVGRE6_MCDEF, - EFX_EF10_GENEVE4_UCDEF, - EFX_EF10_GENEVE4_MCDEF, - EFX_EF10_GENEVE6_UCDEF, - EFX_EF10_GENEVE6_MCDEF, - - EFX_EF10_NUM_DEFAULT_FILTERS -}; - -/* Per-VLAN filters information */ -struct efx_ef10_filter_vlan { - struct list_head list; - u16 vid; - u16 uc[EFX_EF10_FILTER_DEV_UC_MAX]; - u16 mc[EFX_EF10_FILTER_DEV_MC_MAX]; - u16 default_filters[EFX_EF10_NUM_DEFAULT_FILTERS]; -}; - -struct efx_ef10_dev_addr { - u8 addr[ETH_ALEN]; -}; - -struct efx_ef10_filter_table { -/* The MCDI match masks supported by this fw & hw, in order of priority */ - u32 rx_match_mcdi_flags[ - MC_CMD_GET_PARSER_DISP_INFO_OUT_SUPPORTED_MATCHES_MAXNUM * 2]; - unsigned int rx_match_count; - - struct rw_semaphore lock; /* Protects entries */ - struct { - unsigned long spec; /* pointer to spec plus flag bits */ -/* AUTO_OLD is used to mark and sweep MAC filters for the device address lists. */ -/* unused flag 1UL */ -#define EFX_EF10_FILTER_FLAG_AUTO_OLD 2UL -#define EFX_EF10_FILTER_FLAGS 3UL - u64 handle; /* firmware handle */ - } *entry; -/* Shadow of net_device address lists, guarded by mac_lock */ - struct efx_ef10_dev_addr dev_uc_list[EFX_EF10_FILTER_DEV_UC_MAX]; - struct efx_ef10_dev_addr dev_mc_list[EFX_EF10_FILTER_DEV_MC_MAX]; - int dev_uc_count; - int dev_mc_count; - bool uc_promisc; - bool mc_promisc; -/* Whether in multicast promiscuous mode when last changed */ - bool mc_promisc_last; - bool mc_overflow; /* Too many MC addrs; should always imply mc_promisc */ - bool vlan_filter; - struct list_head vlan_list; -}; - -/* An arbitrary search limit for the software hash table */ -#define EFX_EF10_FILTER_SEARCH_LIMIT 200 - -static void efx_ef10_rx_free_indir_table(struct efx_nic *efx); -static void efx_ef10_filter_table_remove(struct efx_nic *efx); -static int efx_ef10_filter_add_vlan(struct efx_nic *efx, u16 vid); -static void efx_ef10_filter_del_vlan_internal(struct efx_nic *efx, - struct efx_ef10_filter_vlan *vlan); -static void efx_ef10_filter_del_vlan(struct efx_nic *efx, u16 vid); static int efx_ef10_set_udp_tnl_ports(struct efx_nic *efx, bool unloading); -static u32 efx_ef10_filter_get_unsafe_id(u32 filter_id) -{ - WARN_ON_ONCE(filter_id == EFX_EF10_FILTER_ID_INVALID); - return filter_id & (HUNT_FILTER_TBL_ROWS - 1); -} - -static unsigned int efx_ef10_filter_get_unsafe_pri(u32 filter_id) -{ - return filter_id / (HUNT_FILTER_TBL_ROWS * 2); -} - -static u32 efx_ef10_make_filter_id(unsigned int pri, u16 idx) -{ - return pri * HUNT_FILTER_TBL_ROWS * 2 + idx; -} - static int efx_ef10_get_warm_boot_count(struct efx_nic *efx) { efx_dword_t reg; @@ -546,7 +438,7 @@ static int efx_ef10_add_vlan(struct efx_nic *efx, u16 vid) if (efx->filter_state) { mutex_lock(&efx->mac_lock); down_write(&efx->filter_sem); - rc = efx_ef10_filter_add_vlan(efx, vlan->vid); + rc = efx_mcdi_filter_add_vlan(efx, vlan->vid); up_write(&efx->filter_sem); mutex_unlock(&efx->mac_lock); if (rc) @@ -575,7 +467,7 @@ static void efx_ef10_del_vlan_internal(struct efx_nic *efx, if (efx->filter_state) { down_write(&efx->filter_sem); - efx_ef10_filter_del_vlan(efx, vlan->vid); + efx_mcdi_filter_del_vlan(efx, vlan->vid); up_write(&efx->filter_sem); } @@ -1038,7 +930,7 @@ static void efx_ef10_remove(struct efx_nic *efx) efx_mcdi_mon_remove(efx); - efx_ef10_rx_free_indir_table(efx); + efx_mcdi_rx_free_indir_table(efx); if (nic_data->wc_membase) iounmap(nic_data->wc_membase); @@ -1426,7 +1318,7 @@ static int efx_ef10_init_nic(struct efx_nic *efx) return 0; } -static void efx_ef10_reset_mc_allocations(struct efx_nic *efx) +static void efx_ef10_table_reset_mc_allocations(struct efx_nic *efx) { struct efx_ef10_nic_data *nic_data = efx->nic_data; #ifdef CONFIG_SFC_SRIOV @@ -1507,7 +1399,7 @@ static int efx_ef10_reset(struct efx_nic *efx, enum reset_type reset_type) */ if ((reset_type == RESET_TYPE_ALL || reset_type == RESET_TYPE_MCDI_TIMEOUT) && !rc) - efx_ef10_reset_mc_allocations(efx); + efx_ef10_table_reset_mc_allocations(efx); return rc; } @@ -2123,7 +2015,7 @@ static void efx_ef10_mcdi_reboot_detected(struct efx_nic *efx) struct efx_ef10_nic_data *nic_data = efx->nic_data; /* All our allocations have been reset */ - efx_ef10_reset_mc_allocations(efx); + efx_ef10_table_reset_mc_allocations(efx); /* The datapath firmware might have been changed */ nic_data->must_check_datapath_caps = true; @@ -2497,442 +2389,6 @@ static void efx_ef10_tx_write(struct efx_tx_queue *tx_queue) } } -#define RSS_MODE_HASH_ADDRS (1 << RSS_MODE_HASH_SRC_ADDR_LBN |\ - 1 << RSS_MODE_HASH_DST_ADDR_LBN) -#define RSS_MODE_HASH_PORTS (1 << RSS_MODE_HASH_SRC_PORT_LBN |\ - 1 << RSS_MODE_HASH_DST_PORT_LBN) -#define RSS_CONTEXT_FLAGS_DEFAULT (1 << MC_CMD_RSS_CONTEXT_GET_FLAGS_OUT_TOEPLITZ_IPV4_EN_LBN |\ - 1 << MC_CMD_RSS_CONTEXT_GET_FLAGS_OUT_TOEPLITZ_TCPV4_EN_LBN |\ - 1 << MC_CMD_RSS_CONTEXT_GET_FLAGS_OUT_TOEPLITZ_IPV6_EN_LBN |\ - 1 << MC_CMD_RSS_CONTEXT_GET_FLAGS_OUT_TOEPLITZ_TCPV6_EN_LBN |\ - (RSS_MODE_HASH_ADDRS | RSS_MODE_HASH_PORTS) << MC_CMD_RSS_CONTEXT_GET_FLAGS_OUT_TCP_IPV4_RSS_MODE_LBN |\ - RSS_MODE_HASH_ADDRS << MC_CMD_RSS_CONTEXT_GET_FLAGS_OUT_UDP_IPV4_RSS_MODE_LBN |\ - RSS_MODE_HASH_ADDRS << MC_CMD_RSS_CONTEXT_GET_FLAGS_OUT_OTHER_IPV4_RSS_MODE_LBN |\ - (RSS_MODE_HASH_ADDRS | RSS_MODE_HASH_PORTS) << MC_CMD_RSS_CONTEXT_GET_FLAGS_OUT_TCP_IPV6_RSS_MODE_LBN |\ - RSS_MODE_HASH_ADDRS << MC_CMD_RSS_CONTEXT_GET_FLAGS_OUT_UDP_IPV6_RSS_MODE_LBN |\ - RSS_MODE_HASH_ADDRS << MC_CMD_RSS_CONTEXT_GET_FLAGS_OUT_OTHER_IPV6_RSS_MODE_LBN) - -static int efx_ef10_get_rss_flags(struct efx_nic *efx, u32 context, u32 *flags) -{ - /* Firmware had a bug (sfc bug 61952) where it would not actually - * fill in the flags field in the response to MC_CMD_RSS_CONTEXT_GET_FLAGS. - * This meant that it would always contain whatever was previously - * in the MCDI buffer. Fortunately, all firmware versions with - * this bug have the same default flags value for a newly-allocated - * RSS context, and the only time we want to get the flags is just - * after allocating. Moreover, the response has a 32-bit hole - * where the context ID would be in the request, so we can use an - * overlength buffer in the request and pre-fill the flags field - * with what we believe the default to be. Thus if the firmware - * has the bug, it will leave our pre-filled value in the flags - * field of the response, and we will get the right answer. - * - * However, this does mean that this function should NOT be used if - * the RSS context flags might not be their defaults - it is ONLY - * reliably correct for a newly-allocated RSS context. - */ - MCDI_DECLARE_BUF(inbuf, MC_CMD_RSS_CONTEXT_GET_FLAGS_OUT_LEN); - MCDI_DECLARE_BUF(outbuf, MC_CMD_RSS_CONTEXT_GET_FLAGS_OUT_LEN); - size_t outlen; - int rc; - - /* Check we have a hole for the context ID */ - BUILD_BUG_ON(MC_CMD_RSS_CONTEXT_GET_FLAGS_IN_LEN != MC_CMD_RSS_CONTEXT_GET_FLAGS_OUT_FLAGS_OFST); - MCDI_SET_DWORD(inbuf, RSS_CONTEXT_GET_FLAGS_IN_RSS_CONTEXT_ID, context); - MCDI_SET_DWORD(inbuf, RSS_CONTEXT_GET_FLAGS_OUT_FLAGS, - RSS_CONTEXT_FLAGS_DEFAULT); - rc = efx_mcdi_rpc(efx, MC_CMD_RSS_CONTEXT_GET_FLAGS, inbuf, - sizeof(inbuf), outbuf, sizeof(outbuf), &outlen); - if (rc == 0) { - if (outlen < MC_CMD_RSS_CONTEXT_GET_FLAGS_OUT_LEN) - rc = -EIO; - else - *flags = MCDI_DWORD(outbuf, RSS_CONTEXT_GET_FLAGS_OUT_FLAGS); - } - return rc; -} - -/* Attempt to enable 4-tuple UDP hashing on the specified RSS context. - * If we fail, we just leave the RSS context at its default hash settings, - * which is safe but may slightly reduce performance. - * Defaults are 4-tuple for TCP and 2-tuple for UDP and other-IP, so we - * just need to set the UDP ports flags (for both IP versions). - */ -static void efx_ef10_set_rss_flags(struct efx_nic *efx, - struct efx_rss_context *ctx) -{ - MCDI_DECLARE_BUF(inbuf, MC_CMD_RSS_CONTEXT_SET_FLAGS_IN_LEN); - u32 flags; - - BUILD_BUG_ON(MC_CMD_RSS_CONTEXT_SET_FLAGS_OUT_LEN != 0); - - if (efx_ef10_get_rss_flags(efx, ctx->context_id, &flags) != 0) - return; - MCDI_SET_DWORD(inbuf, RSS_CONTEXT_SET_FLAGS_IN_RSS_CONTEXT_ID, - ctx->context_id); - flags |= RSS_MODE_HASH_PORTS << MC_CMD_RSS_CONTEXT_GET_FLAGS_OUT_UDP_IPV4_RSS_MODE_LBN; - flags |= RSS_MODE_HASH_PORTS << MC_CMD_RSS_CONTEXT_GET_FLAGS_OUT_UDP_IPV6_RSS_MODE_LBN; - MCDI_SET_DWORD(inbuf, RSS_CONTEXT_SET_FLAGS_IN_FLAGS, flags); - if (!efx_mcdi_rpc(efx, MC_CMD_RSS_CONTEXT_SET_FLAGS, inbuf, sizeof(inbuf), - NULL, 0, NULL)) - /* Succeeded, so UDP 4-tuple is now enabled */ - ctx->rx_hash_udp_4tuple = true; -} - -static int efx_ef10_alloc_rss_context(struct efx_nic *efx, bool exclusive, - struct efx_rss_context *ctx, - unsigned *context_size) -{ - MCDI_DECLARE_BUF(inbuf, MC_CMD_RSS_CONTEXT_ALLOC_IN_LEN); - MCDI_DECLARE_BUF(outbuf, MC_CMD_RSS_CONTEXT_ALLOC_OUT_LEN); - struct efx_ef10_nic_data *nic_data = efx->nic_data; - size_t outlen; - int rc; - u32 alloc_type = exclusive ? - MC_CMD_RSS_CONTEXT_ALLOC_IN_TYPE_EXCLUSIVE : - MC_CMD_RSS_CONTEXT_ALLOC_IN_TYPE_SHARED; - unsigned rss_spread = exclusive ? - efx->rss_spread : - min(rounddown_pow_of_two(efx->rss_spread), - EFX_EF10_MAX_SHARED_RSS_CONTEXT_SIZE); - - if (!exclusive && rss_spread == 1) { - ctx->context_id = EFX_MCDI_RSS_CONTEXT_INVALID; - if (context_size) - *context_size = 1; - return 0; - } - - if (nic_data->datapath_caps & - 1 << MC_CMD_GET_CAPABILITIES_OUT_RX_RSS_LIMITED_LBN) - return -EOPNOTSUPP; - - MCDI_SET_DWORD(inbuf, RSS_CONTEXT_ALLOC_IN_UPSTREAM_PORT_ID, - nic_data->vport_id); - MCDI_SET_DWORD(inbuf, RSS_CONTEXT_ALLOC_IN_TYPE, alloc_type); - MCDI_SET_DWORD(inbuf, RSS_CONTEXT_ALLOC_IN_NUM_QUEUES, rss_spread); - - rc = efx_mcdi_rpc(efx, MC_CMD_RSS_CONTEXT_ALLOC, inbuf, sizeof(inbuf), - outbuf, sizeof(outbuf), &outlen); - if (rc != 0) - return rc; - - if (outlen < MC_CMD_RSS_CONTEXT_ALLOC_OUT_LEN) - return -EIO; - - ctx->context_id = MCDI_DWORD(outbuf, RSS_CONTEXT_ALLOC_OUT_RSS_CONTEXT_ID); - - if (context_size) - *context_size = rss_spread; - - if (nic_data->datapath_caps & - 1 << MC_CMD_GET_CAPABILITIES_OUT_ADDITIONAL_RSS_MODES_LBN) - efx_ef10_set_rss_flags(efx, ctx); - - return 0; -} - -static int efx_ef10_free_rss_context(struct efx_nic *efx, u32 context) -{ - MCDI_DECLARE_BUF(inbuf, MC_CMD_RSS_CONTEXT_FREE_IN_LEN); - - MCDI_SET_DWORD(inbuf, RSS_CONTEXT_FREE_IN_RSS_CONTEXT_ID, - context); - return efx_mcdi_rpc(efx, MC_CMD_RSS_CONTEXT_FREE, inbuf, sizeof(inbuf), - NULL, 0, NULL); -} - -static int efx_ef10_populate_rss_table(struct efx_nic *efx, u32 context, - const u32 *rx_indir_table, const u8 *key) -{ - MCDI_DECLARE_BUF(tablebuf, MC_CMD_RSS_CONTEXT_SET_TABLE_IN_LEN); - MCDI_DECLARE_BUF(keybuf, MC_CMD_RSS_CONTEXT_SET_KEY_IN_LEN); - int i, rc; - - MCDI_SET_DWORD(tablebuf, RSS_CONTEXT_SET_TABLE_IN_RSS_CONTEXT_ID, - context); - BUILD_BUG_ON(ARRAY_SIZE(efx->rss_context.rx_indir_table) != - MC_CMD_RSS_CONTEXT_SET_TABLE_IN_INDIRECTION_TABLE_LEN); - - /* This iterates over the length of efx->rss_context.rx_indir_table, but - * copies bytes from rx_indir_table. That's because the latter is a - * pointer rather than an array, but should have the same length. - * The efx->rss_context.rx_hash_key loop below is similar. - */ - for (i = 0; i < ARRAY_SIZE(efx->rss_context.rx_indir_table); ++i) - MCDI_PTR(tablebuf, - RSS_CONTEXT_SET_TABLE_IN_INDIRECTION_TABLE)[i] = - (u8) rx_indir_table[i]; - - rc = efx_mcdi_rpc(efx, MC_CMD_RSS_CONTEXT_SET_TABLE, tablebuf, - sizeof(tablebuf), NULL, 0, NULL); - if (rc != 0) - return rc; - - MCDI_SET_DWORD(keybuf, RSS_CONTEXT_SET_KEY_IN_RSS_CONTEXT_ID, - context); - BUILD_BUG_ON(ARRAY_SIZE(efx->rss_context.rx_hash_key) != - MC_CMD_RSS_CONTEXT_SET_KEY_IN_TOEPLITZ_KEY_LEN); - for (i = 0; i < ARRAY_SIZE(efx->rss_context.rx_hash_key); ++i) - MCDI_PTR(keybuf, RSS_CONTEXT_SET_KEY_IN_TOEPLITZ_KEY)[i] = key[i]; - - return efx_mcdi_rpc(efx, MC_CMD_RSS_CONTEXT_SET_KEY, keybuf, - sizeof(keybuf), NULL, 0, NULL); -} - -static void efx_ef10_rx_free_indir_table(struct efx_nic *efx) -{ - int rc; - - if (efx->rss_context.context_id != EFX_MCDI_RSS_CONTEXT_INVALID) { - rc = efx_ef10_free_rss_context(efx, efx->rss_context.context_id); - WARN_ON(rc != 0); - } - efx->rss_context.context_id = EFX_MCDI_RSS_CONTEXT_INVALID; -} - -static int efx_ef10_rx_push_shared_rss_config(struct efx_nic *efx, - unsigned *context_size) -{ - struct efx_ef10_nic_data *nic_data = efx->nic_data; - int rc = efx_ef10_alloc_rss_context(efx, false, &efx->rss_context, - context_size); - - if (rc != 0) - return rc; - - nic_data->rx_rss_context_exclusive = false; - efx_set_default_rx_indir_table(efx, &efx->rss_context); - return 0; -} - -static int efx_ef10_rx_push_exclusive_rss_config(struct efx_nic *efx, - const u32 *rx_indir_table, - const u8 *key) -{ - u32 old_rx_rss_context = efx->rss_context.context_id; - struct efx_ef10_nic_data *nic_data = efx->nic_data; - int rc; - - if (efx->rss_context.context_id == EFX_MCDI_RSS_CONTEXT_INVALID || - !nic_data->rx_rss_context_exclusive) { - rc = efx_ef10_alloc_rss_context(efx, true, &efx->rss_context, - NULL); - if (rc == -EOPNOTSUPP) - return rc; - else if (rc != 0) - goto fail1; - } - - rc = efx_ef10_populate_rss_table(efx, efx->rss_context.context_id, - rx_indir_table, key); - if (rc != 0) - goto fail2; - - if (efx->rss_context.context_id != old_rx_rss_context && - old_rx_rss_context != EFX_MCDI_RSS_CONTEXT_INVALID) - WARN_ON(efx_ef10_free_rss_context(efx, old_rx_rss_context) != 0); - nic_data->rx_rss_context_exclusive = true; - if (rx_indir_table != efx->rss_context.rx_indir_table) - memcpy(efx->rss_context.rx_indir_table, rx_indir_table, - sizeof(efx->rss_context.rx_indir_table)); - if (key != efx->rss_context.rx_hash_key) - memcpy(efx->rss_context.rx_hash_key, key, - efx->type->rx_hash_key_size); - - return 0; - -fail2: - if (old_rx_rss_context != efx->rss_context.context_id) { - WARN_ON(efx_ef10_free_rss_context(efx, efx->rss_context.context_id) != 0); - efx->rss_context.context_id = old_rx_rss_context; - } -fail1: - netif_err(efx, hw, efx->net_dev, "%s: failed rc=%d\n", __func__, rc); - return rc; -} - -static int efx_ef10_rx_push_rss_context_config(struct efx_nic *efx, - struct efx_rss_context *ctx, - const u32 *rx_indir_table, - const u8 *key) -{ - int rc; - - WARN_ON(!mutex_is_locked(&efx->rss_lock)); - - if (ctx->context_id == EFX_MCDI_RSS_CONTEXT_INVALID) { - rc = efx_ef10_alloc_rss_context(efx, true, ctx, NULL); - if (rc) - return rc; - } - - if (!rx_indir_table) /* Delete this context */ - return efx_ef10_free_rss_context(efx, ctx->context_id); - - rc = efx_ef10_populate_rss_table(efx, ctx->context_id, - rx_indir_table, key); - if (rc) - return rc; - - memcpy(ctx->rx_indir_table, rx_indir_table, - sizeof(efx->rss_context.rx_indir_table)); - memcpy(ctx->rx_hash_key, key, efx->type->rx_hash_key_size); - - return 0; -} - -static int efx_ef10_rx_pull_rss_context_config(struct efx_nic *efx, - struct efx_rss_context *ctx) -{ - MCDI_DECLARE_BUF(inbuf, MC_CMD_RSS_CONTEXT_GET_TABLE_IN_LEN); - MCDI_DECLARE_BUF(tablebuf, MC_CMD_RSS_CONTEXT_GET_TABLE_OUT_LEN); - MCDI_DECLARE_BUF(keybuf, MC_CMD_RSS_CONTEXT_GET_KEY_OUT_LEN); - size_t outlen; - int rc, i; - - WARN_ON(!mutex_is_locked(&efx->rss_lock)); - - BUILD_BUG_ON(MC_CMD_RSS_CONTEXT_GET_TABLE_IN_LEN != - MC_CMD_RSS_CONTEXT_GET_KEY_IN_LEN); - - if (ctx->context_id == EFX_MCDI_RSS_CONTEXT_INVALID) - return -ENOENT; - - MCDI_SET_DWORD(inbuf, RSS_CONTEXT_GET_TABLE_IN_RSS_CONTEXT_ID, - ctx->context_id); - BUILD_BUG_ON(ARRAY_SIZE(ctx->rx_indir_table) != - MC_CMD_RSS_CONTEXT_GET_TABLE_OUT_INDIRECTION_TABLE_LEN); - rc = efx_mcdi_rpc(efx, MC_CMD_RSS_CONTEXT_GET_TABLE, inbuf, sizeof(inbuf), - tablebuf, sizeof(tablebuf), &outlen); - if (rc != 0) - return rc; - - if (WARN_ON(outlen != MC_CMD_RSS_CONTEXT_GET_TABLE_OUT_LEN)) - return -EIO; - - for (i = 0; i < ARRAY_SIZE(ctx->rx_indir_table); i++) - ctx->rx_indir_table[i] = MCDI_PTR(tablebuf, - RSS_CONTEXT_GET_TABLE_OUT_INDIRECTION_TABLE)[i]; - - MCDI_SET_DWORD(inbuf, RSS_CONTEXT_GET_KEY_IN_RSS_CONTEXT_ID, - ctx->context_id); - BUILD_BUG_ON(ARRAY_SIZE(ctx->rx_hash_key) != - MC_CMD_RSS_CONTEXT_SET_KEY_IN_TOEPLITZ_KEY_LEN); - rc = efx_mcdi_rpc(efx, MC_CMD_RSS_CONTEXT_GET_KEY, inbuf, sizeof(inbuf), - keybuf, sizeof(keybuf), &outlen); - if (rc != 0) - return rc; - - if (WARN_ON(outlen != MC_CMD_RSS_CONTEXT_GET_KEY_OUT_LEN)) - return -EIO; - - for (i = 0; i < ARRAY_SIZE(ctx->rx_hash_key); ++i) - ctx->rx_hash_key[i] = MCDI_PTR( - keybuf, RSS_CONTEXT_GET_KEY_OUT_TOEPLITZ_KEY)[i]; - - return 0; -} - -static int efx_ef10_rx_pull_rss_config(struct efx_nic *efx) -{ - int rc; - - mutex_lock(&efx->rss_lock); - rc = efx_ef10_rx_pull_rss_context_config(efx, &efx->rss_context); - mutex_unlock(&efx->rss_lock); - return rc; -} - -static void efx_ef10_rx_restore_rss_contexts(struct efx_nic *efx) -{ - struct efx_ef10_nic_data *nic_data = efx->nic_data; - struct efx_rss_context *ctx; - int rc; - - WARN_ON(!mutex_is_locked(&efx->rss_lock)); - - if (!nic_data->must_restore_rss_contexts) - return; - - list_for_each_entry(ctx, &efx->rss_context.list, list) { - /* previous NIC RSS context is gone */ - ctx->context_id = EFX_MCDI_RSS_CONTEXT_INVALID; - /* so try to allocate a new one */ - rc = efx_ef10_rx_push_rss_context_config(efx, ctx, - ctx->rx_indir_table, - ctx->rx_hash_key); - if (rc) - netif_warn(efx, probe, efx->net_dev, - "failed to restore RSS context %u, rc=%d" - "; RSS filters may fail to be applied\n", - ctx->user_id, rc); - } - nic_data->must_restore_rss_contexts = false; -} - -static int efx_ef10_pf_rx_push_rss_config(struct efx_nic *efx, bool user, - const u32 *rx_indir_table, - const u8 *key) -{ - int rc; - - if (efx->rss_spread == 1) - return 0; - - if (!key) - key = efx->rss_context.rx_hash_key; - - rc = efx_ef10_rx_push_exclusive_rss_config(efx, rx_indir_table, key); - - if (rc == -ENOBUFS && !user) { - unsigned context_size; - bool mismatch = false; - size_t i; - - for (i = 0; - i < ARRAY_SIZE(efx->rss_context.rx_indir_table) && !mismatch; - i++) - mismatch = rx_indir_table[i] != - ethtool_rxfh_indir_default(i, efx->rss_spread); - - rc = efx_ef10_rx_push_shared_rss_config(efx, &context_size); - if (rc == 0) { - if (context_size != efx->rss_spread) - netif_warn(efx, probe, efx->net_dev, - "Could not allocate an exclusive RSS" - " context; allocated a shared one of" - " different size." - " Wanted %u, got %u.\n", - efx->rss_spread, context_size); - else if (mismatch) - netif_warn(efx, probe, efx->net_dev, - "Could not allocate an exclusive RSS" - " context; allocated a shared one but" - " could not apply custom" - " indirection.\n"); - else - netif_info(efx, probe, efx->net_dev, - "Could not allocate an exclusive RSS" - " context; allocated a shared one.\n"); - } - } - return rc; -} - -static int efx_ef10_vf_rx_push_rss_config(struct efx_nic *efx, bool user, - const u32 *rx_indir_table - __attribute__ ((unused)), - const u8 *key - __attribute__ ((unused))) -{ - if (user) - return -EOPNOTSUPP; - if (efx->rss_context.context_id != EFX_MCDI_RSS_CONTEXT_INVALID) - return 0; - return efx_ef10_rx_push_shared_rss_config(efx, NULL); -} - /* This creates an entry in the RX descriptor queue */ static inline void efx_ef10_build_rx_desc(struct efx_rx_queue *rx_queue, unsigned int index) @@ -3684,1538 +3140,6 @@ static void efx_ef10_prepare_flr(struct efx_nic *efx) atomic_set(&efx->active_queues, 0); } -/* Decide whether a filter should be exclusive or else should allow - * delivery to additional recipients. Currently we decide that - * filters for specific local unicast MAC and IP addresses are - * exclusive. - */ -static bool efx_ef10_filter_is_exclusive(const struct efx_filter_spec *spec) -{ - if (spec->match_flags & EFX_FILTER_MATCH_LOC_MAC && - !is_multicast_ether_addr(spec->loc_mac)) - return true; - - if ((spec->match_flags & - (EFX_FILTER_MATCH_ETHER_TYPE | EFX_FILTER_MATCH_LOC_HOST)) == - (EFX_FILTER_MATCH_ETHER_TYPE | EFX_FILTER_MATCH_LOC_HOST)) { - if (spec->ether_type == htons(ETH_P_IP) && - !ipv4_is_multicast(spec->loc_host[0])) - return true; - if (spec->ether_type == htons(ETH_P_IPV6) && - ((const u8 *)spec->loc_host)[0] != 0xff) - return true; - } - - return false; -} - -static struct efx_filter_spec * -efx_ef10_filter_entry_spec(const struct efx_ef10_filter_table *table, - unsigned int filter_idx) -{ - return (struct efx_filter_spec *)(table->entry[filter_idx].spec & - ~EFX_EF10_FILTER_FLAGS); -} - -static unsigned int -efx_ef10_filter_entry_flags(const struct efx_ef10_filter_table *table, - unsigned int filter_idx) -{ - return table->entry[filter_idx].spec & EFX_EF10_FILTER_FLAGS; -} - -static void -efx_ef10_filter_set_entry(struct efx_ef10_filter_table *table, - unsigned int filter_idx, - const struct efx_filter_spec *spec, - unsigned int flags) -{ - table->entry[filter_idx].spec = (unsigned long)spec | flags; -} - -static void -efx_ef10_filter_push_prep_set_match_fields(struct efx_nic *efx, - const struct efx_filter_spec *spec, - efx_dword_t *inbuf) -{ - enum efx_encap_type encap_type = efx_filter_get_encap_type(spec); - u32 match_fields = 0, uc_match, mc_match; - - MCDI_SET_DWORD(inbuf, FILTER_OP_IN_OP, - efx_ef10_filter_is_exclusive(spec) ? - MC_CMD_FILTER_OP_IN_OP_INSERT : - MC_CMD_FILTER_OP_IN_OP_SUBSCRIBE); - - /* Convert match flags and values. Unlike almost - * everything else in MCDI, these fields are in - * network byte order. - */ -#define COPY_VALUE(value, mcdi_field) \ - do { \ - match_fields |= \ - 1 << MC_CMD_FILTER_OP_IN_MATCH_ ## \ - mcdi_field ## _LBN; \ - BUILD_BUG_ON( \ - MC_CMD_FILTER_OP_IN_ ## mcdi_field ## _LEN < \ - sizeof(value)); \ - memcpy(MCDI_PTR(inbuf, FILTER_OP_IN_ ## mcdi_field), \ - &value, sizeof(value)); \ - } while (0) -#define COPY_FIELD(gen_flag, gen_field, mcdi_field) \ - if (spec->match_flags & EFX_FILTER_MATCH_ ## gen_flag) { \ - COPY_VALUE(spec->gen_field, mcdi_field); \ - } - /* Handle encap filters first. They will always be mismatch - * (unknown UC or MC) filters - */ - if (encap_type) { - /* ether_type and outer_ip_proto need to be variables - * because COPY_VALUE wants to memcpy them - */ - __be16 ether_type = - htons(encap_type & EFX_ENCAP_FLAG_IPV6 ? - ETH_P_IPV6 : ETH_P_IP); - u8 vni_type = MC_CMD_FILTER_OP_EXT_IN_VNI_TYPE_GENEVE; - u8 outer_ip_proto; - - switch (encap_type & EFX_ENCAP_TYPES_MASK) { - case EFX_ENCAP_TYPE_VXLAN: - vni_type = MC_CMD_FILTER_OP_EXT_IN_VNI_TYPE_VXLAN; - /* fallthrough */ - case EFX_ENCAP_TYPE_GENEVE: - COPY_VALUE(ether_type, ETHER_TYPE); - outer_ip_proto = IPPROTO_UDP; - COPY_VALUE(outer_ip_proto, IP_PROTO); - /* We always need to set the type field, even - * though we're not matching on the TNI. - */ - MCDI_POPULATE_DWORD_1(inbuf, - FILTER_OP_EXT_IN_VNI_OR_VSID, - FILTER_OP_EXT_IN_VNI_TYPE, - vni_type); - break; - case EFX_ENCAP_TYPE_NVGRE: - COPY_VALUE(ether_type, ETHER_TYPE); - outer_ip_proto = IPPROTO_GRE; - COPY_VALUE(outer_ip_proto, IP_PROTO); - break; - default: - WARN_ON(1); - } - - uc_match = MC_CMD_FILTER_OP_EXT_IN_MATCH_IFRM_UNKNOWN_UCAST_DST_LBN; - mc_match = MC_CMD_FILTER_OP_EXT_IN_MATCH_IFRM_UNKNOWN_MCAST_DST_LBN; - } else { - uc_match = MC_CMD_FILTER_OP_EXT_IN_MATCH_UNKNOWN_UCAST_DST_LBN; - mc_match = MC_CMD_FILTER_OP_EXT_IN_MATCH_UNKNOWN_MCAST_DST_LBN; - } - - if (spec->match_flags & EFX_FILTER_MATCH_LOC_MAC_IG) - match_fields |= - is_multicast_ether_addr(spec->loc_mac) ? - 1 << mc_match : - 1 << uc_match; - COPY_FIELD(REM_HOST, rem_host, SRC_IP); - COPY_FIELD(LOC_HOST, loc_host, DST_IP); - COPY_FIELD(REM_MAC, rem_mac, SRC_MAC); - COPY_FIELD(REM_PORT, rem_port, SRC_PORT); - COPY_FIELD(LOC_MAC, loc_mac, DST_MAC); - COPY_FIELD(LOC_PORT, loc_port, DST_PORT); - COPY_FIELD(ETHER_TYPE, ether_type, ETHER_TYPE); - COPY_FIELD(INNER_VID, inner_vid, INNER_VLAN); - COPY_FIELD(OUTER_VID, outer_vid, OUTER_VLAN); - COPY_FIELD(IP_PROTO, ip_proto, IP_PROTO); -#undef COPY_FIELD -#undef COPY_VALUE - MCDI_SET_DWORD(inbuf, FILTER_OP_IN_MATCH_FIELDS, - match_fields); -} - -static void efx_ef10_filter_push_prep(struct efx_nic *efx, - const struct efx_filter_spec *spec, - efx_dword_t *inbuf, u64 handle, - struct efx_rss_context *ctx, - bool replacing) -{ - struct efx_ef10_nic_data *nic_data = efx->nic_data; - u32 flags = spec->flags; - - memset(inbuf, 0, MC_CMD_FILTER_OP_EXT_IN_LEN); - - /* If RSS filter, caller better have given us an RSS context */ - if (flags & EFX_FILTER_FLAG_RX_RSS) { - /* We don't have the ability to return an error, so we'll just - * log a warning and disable RSS for the filter. - */ - if (WARN_ON_ONCE(!ctx)) - flags &= ~EFX_FILTER_FLAG_RX_RSS; - else if (WARN_ON_ONCE(ctx->context_id == EFX_MCDI_RSS_CONTEXT_INVALID)) - flags &= ~EFX_FILTER_FLAG_RX_RSS; - } - - if (replacing) { - MCDI_SET_DWORD(inbuf, FILTER_OP_IN_OP, - MC_CMD_FILTER_OP_IN_OP_REPLACE); - MCDI_SET_QWORD(inbuf, FILTER_OP_IN_HANDLE, handle); - } else { - efx_ef10_filter_push_prep_set_match_fields(efx, spec, inbuf); - } - - MCDI_SET_DWORD(inbuf, FILTER_OP_IN_PORT_ID, nic_data->vport_id); - MCDI_SET_DWORD(inbuf, FILTER_OP_IN_RX_DEST, - spec->dmaq_id == EFX_FILTER_RX_DMAQ_ID_DROP ? - MC_CMD_FILTER_OP_IN_RX_DEST_DROP : - MC_CMD_FILTER_OP_IN_RX_DEST_HOST); - MCDI_SET_DWORD(inbuf, FILTER_OP_IN_TX_DOMAIN, 0); - MCDI_SET_DWORD(inbuf, FILTER_OP_IN_TX_DEST, - MC_CMD_FILTER_OP_IN_TX_DEST_DEFAULT); - MCDI_SET_DWORD(inbuf, FILTER_OP_IN_RX_QUEUE, - spec->dmaq_id == EFX_FILTER_RX_DMAQ_ID_DROP ? - 0 : spec->dmaq_id); - MCDI_SET_DWORD(inbuf, FILTER_OP_IN_RX_MODE, - (flags & EFX_FILTER_FLAG_RX_RSS) ? - MC_CMD_FILTER_OP_IN_RX_MODE_RSS : - MC_CMD_FILTER_OP_IN_RX_MODE_SIMPLE); - if (flags & EFX_FILTER_FLAG_RX_RSS) - MCDI_SET_DWORD(inbuf, FILTER_OP_IN_RX_CONTEXT, ctx->context_id); -} - -static int efx_ef10_filter_push(struct efx_nic *efx, - const struct efx_filter_spec *spec, u64 *handle, - struct efx_rss_context *ctx, bool replacing) -{ - MCDI_DECLARE_BUF(inbuf, MC_CMD_FILTER_OP_EXT_IN_LEN); - MCDI_DECLARE_BUF(outbuf, MC_CMD_FILTER_OP_EXT_OUT_LEN); - size_t outlen; - int rc; - - efx_ef10_filter_push_prep(efx, spec, inbuf, *handle, ctx, replacing); - rc = efx_mcdi_rpc_quiet(efx, MC_CMD_FILTER_OP, inbuf, sizeof(inbuf), - outbuf, sizeof(outbuf), &outlen); - if (rc && spec->priority != EFX_FILTER_PRI_HINT) - efx_mcdi_display_error(efx, MC_CMD_FILTER_OP, sizeof(inbuf), - outbuf, outlen, rc); - if (rc == 0) - *handle = MCDI_QWORD(outbuf, FILTER_OP_OUT_HANDLE); - if (rc == -ENOSPC) - rc = -EBUSY; /* to match efx_farch_filter_insert() */ - return rc; -} - -static u32 efx_ef10_filter_mcdi_flags_from_spec(const struct efx_filter_spec *spec) -{ - enum efx_encap_type encap_type = efx_filter_get_encap_type(spec); - unsigned int match_flags = spec->match_flags; - unsigned int uc_match, mc_match; - u32 mcdi_flags = 0; - -#define MAP_FILTER_TO_MCDI_FLAG(gen_flag, mcdi_field, encap) { \ - unsigned int old_match_flags = match_flags; \ - match_flags &= ~EFX_FILTER_MATCH_ ## gen_flag; \ - if (match_flags != old_match_flags) \ - mcdi_flags |= \ - (1 << ((encap) ? \ - MC_CMD_FILTER_OP_EXT_IN_MATCH_IFRM_ ## \ - mcdi_field ## _LBN : \ - MC_CMD_FILTER_OP_EXT_IN_MATCH_ ##\ - mcdi_field ## _LBN)); \ - } - /* inner or outer based on encap type */ - MAP_FILTER_TO_MCDI_FLAG(REM_HOST, SRC_IP, encap_type); - MAP_FILTER_TO_MCDI_FLAG(LOC_HOST, DST_IP, encap_type); - MAP_FILTER_TO_MCDI_FLAG(REM_MAC, SRC_MAC, encap_type); - MAP_FILTER_TO_MCDI_FLAG(REM_PORT, SRC_PORT, encap_type); - MAP_FILTER_TO_MCDI_FLAG(LOC_MAC, DST_MAC, encap_type); - MAP_FILTER_TO_MCDI_FLAG(LOC_PORT, DST_PORT, encap_type); - MAP_FILTER_TO_MCDI_FLAG(ETHER_TYPE, ETHER_TYPE, encap_type); - MAP_FILTER_TO_MCDI_FLAG(IP_PROTO, IP_PROTO, encap_type); - /* always outer */ - MAP_FILTER_TO_MCDI_FLAG(INNER_VID, INNER_VLAN, false); - MAP_FILTER_TO_MCDI_FLAG(OUTER_VID, OUTER_VLAN, false); -#undef MAP_FILTER_TO_MCDI_FLAG - - /* special handling for encap type, and mismatch */ - if (encap_type) { - match_flags &= ~EFX_FILTER_MATCH_ENCAP_TYPE; - mcdi_flags |= - (1 << MC_CMD_FILTER_OP_EXT_IN_MATCH_ETHER_TYPE_LBN); - mcdi_flags |= (1 << MC_CMD_FILTER_OP_EXT_IN_MATCH_IP_PROTO_LBN); - - uc_match = MC_CMD_FILTER_OP_EXT_IN_MATCH_IFRM_UNKNOWN_UCAST_DST_LBN; - mc_match = MC_CMD_FILTER_OP_EXT_IN_MATCH_IFRM_UNKNOWN_MCAST_DST_LBN; - } else { - uc_match = MC_CMD_FILTER_OP_EXT_IN_MATCH_UNKNOWN_UCAST_DST_LBN; - mc_match = MC_CMD_FILTER_OP_EXT_IN_MATCH_UNKNOWN_MCAST_DST_LBN; - } - - if (match_flags & EFX_FILTER_MATCH_LOC_MAC_IG) { - match_flags &= ~EFX_FILTER_MATCH_LOC_MAC_IG; - mcdi_flags |= - is_multicast_ether_addr(spec->loc_mac) ? - 1 << mc_match : - 1 << uc_match; - } - - /* Did we map them all? */ - WARN_ON_ONCE(match_flags); - - return mcdi_flags; -} - -static int efx_ef10_filter_pri(struct efx_ef10_filter_table *table, - const struct efx_filter_spec *spec) -{ - u32 mcdi_flags = efx_ef10_filter_mcdi_flags_from_spec(spec); - unsigned int match_pri; - - for (match_pri = 0; - match_pri < table->rx_match_count; - match_pri++) - if (table->rx_match_mcdi_flags[match_pri] == mcdi_flags) - return match_pri; - - return -EPROTONOSUPPORT; -} - -static s32 efx_ef10_filter_insert_locked(struct efx_nic *efx, - struct efx_filter_spec *spec, - bool replace_equal) -{ - DECLARE_BITMAP(mc_rem_map, EFX_EF10_FILTER_SEARCH_LIMIT); - struct efx_ef10_nic_data *nic_data = efx->nic_data; - struct efx_ef10_filter_table *table; - struct efx_filter_spec *saved_spec; - struct efx_rss_context *ctx = NULL; - unsigned int match_pri, hash; - unsigned int priv_flags; - bool rss_locked = false; - bool replacing = false; - unsigned int depth, i; - int ins_index = -1; - DEFINE_WAIT(wait); - bool is_mc_recip; - s32 rc; - - WARN_ON(!rwsem_is_locked(&efx->filter_sem)); - table = efx->filter_state; - down_write(&table->lock); - - /* For now, only support RX filters */ - if ((spec->flags & (EFX_FILTER_FLAG_RX | EFX_FILTER_FLAG_TX)) != - EFX_FILTER_FLAG_RX) { - rc = -EINVAL; - goto out_unlock; - } - - rc = efx_ef10_filter_pri(table, spec); - if (rc < 0) - goto out_unlock; - match_pri = rc; - - hash = efx_filter_spec_hash(spec); - is_mc_recip = efx_filter_is_mc_recipient(spec); - if (is_mc_recip) - bitmap_zero(mc_rem_map, EFX_EF10_FILTER_SEARCH_LIMIT); - - if (spec->flags & EFX_FILTER_FLAG_RX_RSS) { - mutex_lock(&efx->rss_lock); - rss_locked = true; - if (spec->rss_context) - ctx = efx_find_rss_context_entry(efx, spec->rss_context); - else - ctx = &efx->rss_context; - if (!ctx) { - rc = -ENOENT; - goto out_unlock; - } - if (ctx->context_id == EFX_MCDI_RSS_CONTEXT_INVALID) { - rc = -EOPNOTSUPP; - goto out_unlock; - } - } - - /* Find any existing filters with the same match tuple or - * else a free slot to insert at. - */ - for (depth = 1; depth < EFX_EF10_FILTER_SEARCH_LIMIT; depth++) { - i = (hash + depth) & (HUNT_FILTER_TBL_ROWS - 1); - saved_spec |