Skip to content

Commit

Permalink
sfc: support RSS spreading of ethtool ntuple filters
Browse files Browse the repository at this point in the history
Use a linked list to associate user-facing context IDs with FW-facing
 context IDs (since the latter can change after an MC reset).

Signed-off-by: Edward Cree <ecree@solarflare.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Edward Cree authored and David S. Miller committed Mar 9, 2018
1 parent 84a1d9c commit 42356d9
Show file tree
Hide file tree
Showing 9 changed files with 443 additions and 150 deletions.
273 changes: 181 additions & 92 deletions drivers/net/ethernet/sfc/ef10.c

Large diffs are not rendered by default.

65 changes: 59 additions & 6 deletions drivers/net/ethernet/sfc/efx.c
Original file line number Diff line number Diff line change
Expand Up @@ -1353,12 +1353,13 @@ static void efx_fini_io(struct efx_nic *efx)
pci_disable_device(efx->pci_dev);
}

void efx_set_default_rx_indir_table(struct efx_nic *efx)
void efx_set_default_rx_indir_table(struct efx_nic *efx,
struct efx_rss_context *ctx)
{
size_t i;

for (i = 0; i < ARRAY_SIZE(efx->rx_indir_table); i++)
efx->rx_indir_table[i] =
for (i = 0; i < ARRAY_SIZE(ctx->rx_indir_table); i++)
ctx->rx_indir_table[i] =
ethtool_rxfh_indir_default(i, efx->rss_spread);
}

Expand Down Expand Up @@ -1739,9 +1740,9 @@ static int efx_probe_nic(struct efx_nic *efx)
} while (rc == -EAGAIN);

if (efx->n_channels > 1)
netdev_rss_key_fill(&efx->rx_hash_key,
sizeof(efx->rx_hash_key));
efx_set_default_rx_indir_table(efx);
netdev_rss_key_fill(efx->rss_context.rx_hash_key,
sizeof(efx->rss_context.rx_hash_key));
efx_set_default_rx_indir_table(efx, &efx->rss_context);

netif_set_real_num_tx_queues(efx->net_dev, efx->n_tx_channels);
netif_set_real_num_rx_queues(efx->net_dev, efx->n_rx_channels);
Expand Down Expand Up @@ -2700,6 +2701,8 @@ int efx_reset_up(struct efx_nic *efx, enum reset_type method, bool ok)
" VFs may not function\n", rc);
#endif

if (efx->type->rx_restore_rss_contexts)
efx->type->rx_restore_rss_contexts(efx);
down_read(&efx->filter_sem);
efx_restore_filters(efx);
up_read(&efx->filter_sem);
Expand Down Expand Up @@ -3003,6 +3006,7 @@ static int efx_init_struct(struct efx_nic *efx,
efx->type->rx_hash_offset - efx->type->rx_prefix_size;
efx->rx_packet_ts_offset =
efx->type->rx_ts_offset - efx->type->rx_prefix_size;
INIT_LIST_HEAD(&efx->rss_context.list);
spin_lock_init(&efx->stats_lock);
efx->vi_stride = EFX_DEFAULT_VI_STRIDE;
efx->num_mac_stats = MC_CMD_MAC_NSTATS;
Expand Down Expand Up @@ -3072,6 +3076,55 @@ void efx_update_sw_stats(struct efx_nic *efx, u64 *stats)
stats[GENERIC_STAT_rx_noskb_drops] = atomic_read(&efx->n_rx_noskb_drops);
}

/* RSS contexts. We're using linked lists and crappy O(n) algorithms, because
* (a) this is an infrequent control-plane operation and (b) n is small (max 64)
*/
struct efx_rss_context *efx_alloc_rss_context_entry(struct list_head *head)
{
struct efx_rss_context *ctx, *new;
u32 id = 1; /* Don't use zero, that refers to the master RSS context */

/* Search for first gap in the numbering */
list_for_each_entry(ctx, head, list) {
if (ctx->user_id != id)
break;
id++;
/* Check for wrap. If this happens, we have nearly 2^32
* allocated RSS contexts, which seems unlikely.
*/
if (WARN_ON_ONCE(!id))
return NULL;
}

/* Create the new entry */
new = kmalloc(sizeof(struct efx_rss_context), GFP_KERNEL);
if (!new)
return NULL;
new->context_id = EFX_EF10_RSS_CONTEXT_INVALID;
new->rx_hash_udp_4tuple = false;

/* Insert the new entry into the gap */
new->user_id = id;
list_add_tail(&new->list, &ctx->list);
return new;
}

struct efx_rss_context *efx_find_rss_context_entry(u32 id, struct list_head *head)
{
struct efx_rss_context *ctx;

list_for_each_entry(ctx, head, list)
if (ctx->user_id == id)
return ctx;
return NULL;
}

void efx_free_rss_context_entry(struct efx_rss_context *ctx)
{
list_del(&ctx->list);
kfree(ctx);
}

/**************************************************************************
*
* PCI interface
Expand Down
12 changes: 11 additions & 1 deletion drivers/net/ethernet/sfc/efx.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ extern unsigned int efx_piobuf_size;
extern bool efx_separate_tx_channels;

/* RX */
void efx_set_default_rx_indir_table(struct efx_nic *efx);
void efx_set_default_rx_indir_table(struct efx_nic *efx,
struct efx_rss_context *ctx);
void efx_rx_config_page_split(struct efx_nic *efx);
int efx_probe_rx_queue(struct efx_rx_queue *rx_queue);
void efx_remove_rx_queue(struct efx_rx_queue *rx_queue);
Expand Down Expand Up @@ -182,6 +183,15 @@ static inline void efx_filter_rfs_expire(struct efx_channel *channel) {}
#endif
bool efx_filter_is_mc_recipient(const struct efx_filter_spec *spec);

/* RSS contexts */
struct efx_rss_context *efx_alloc_rss_context_entry(struct list_head *list);
struct efx_rss_context *efx_find_rss_context_entry(u32 id, struct list_head *list);
void efx_free_rss_context_entry(struct efx_rss_context *ctx);
static inline bool efx_rss_active(struct efx_rss_context *ctx)
{
return ctx->context_id != EFX_EF10_RSS_CONTEXT_INVALID;
}

/* Channels */
int efx_channel_dummy_op_int(struct efx_channel *channel);
void efx_channel_dummy_op_void(struct efx_channel *channel);
Expand Down
153 changes: 134 additions & 19 deletions drivers/net/ethernet/sfc/ethtool.c
Original file line number Diff line number Diff line change
Expand Up @@ -808,7 +808,8 @@ static inline void ip6_fill_mask(__be32 *mask)
}

static int efx_ethtool_get_class_rule(struct efx_nic *efx,
struct ethtool_rx_flow_spec *rule)
struct ethtool_rx_flow_spec *rule,
u32 *rss_context)
{
struct ethtool_tcpip4_spec *ip_entry = &rule->h_u.tcp_ip4_spec;
struct ethtool_tcpip4_spec *ip_mask = &rule->m_u.tcp_ip4_spec;
Expand Down Expand Up @@ -964,6 +965,11 @@ static int efx_ethtool_get_class_rule(struct efx_nic *efx,
rule->m_ext.vlan_tci = htons(0xfff);
}

if (spec.flags & EFX_FILTER_FLAG_RX_RSS) {
rule->flow_type |= FLOW_RSS;
*rss_context = spec.rss_context;
}

return rc;
}

Expand All @@ -972,19 +978,29 @@ efx_ethtool_get_rxnfc(struct net_device *net_dev,
struct ethtool_rxnfc *info, u32 *rule_locs)
{
struct efx_nic *efx = netdev_priv(net_dev);
u32 rss_context = 0;
s32 rc;

switch (info->cmd) {
case ETHTOOL_GRXRINGS:
info->data = efx->n_rx_channels;
return 0;

case ETHTOOL_GRXFH: {
struct efx_rss_context *ctx = &efx->rss_context;

if (info->flow_type & FLOW_RSS && info->rss_context) {
ctx = efx_find_rss_context_entry(info->rss_context,
&efx->rss_context.list);
if (!ctx)
return -ENOENT;
}
info->data = 0;
if (!efx->rss_active) /* No RSS */
if (!efx_rss_active(ctx)) /* No RSS */
return 0;
switch (info->flow_type) {
switch (info->flow_type & ~FLOW_RSS) {
case UDP_V4_FLOW:
if (efx->rx_hash_udp_4tuple)
if (ctx->rx_hash_udp_4tuple)
/* fall through */
case TCP_V4_FLOW:
info->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
Expand All @@ -995,7 +1011,7 @@ efx_ethtool_get_rxnfc(struct net_device *net_dev,
info->data |= RXH_IP_SRC | RXH_IP_DST;
break;
case UDP_V6_FLOW:
if (efx->rx_hash_udp_4tuple)
if (ctx->rx_hash_udp_4tuple)
/* fall through */
case TCP_V6_FLOW:
info->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
Expand Down Expand Up @@ -1023,10 +1039,14 @@ efx_ethtool_get_rxnfc(struct net_device *net_dev,
case ETHTOOL_GRXCLSRULE:
if (efx_filter_get_rx_id_limit(efx) == 0)
return -EOPNOTSUPP;
return efx_ethtool_get_class_rule(efx, &info->fs);
rc = efx_ethtool_get_class_rule(efx, &info->fs, &rss_context);
if (rc < 0)
return rc;
if (info->fs.flow_type & FLOW_RSS)
info->rss_context = rss_context;
return 0;

case ETHTOOL_GRXCLSRLALL: {
s32 rc;
case ETHTOOL_GRXCLSRLALL:
info->data = efx_filter_get_rx_id_limit(efx);
if (info->data == 0)
return -EOPNOTSUPP;
Expand All @@ -1036,7 +1056,6 @@ efx_ethtool_get_rxnfc(struct net_device *net_dev,
return rc;
info->rule_cnt = rc;
return 0;
}

default:
return -EOPNOTSUPP;
Expand All @@ -1054,7 +1073,8 @@ static inline bool ip6_mask_is_empty(__be32 mask[4])
}

static int efx_ethtool_set_class_rule(struct efx_nic *efx,
struct ethtool_rx_flow_spec *rule)
struct ethtool_rx_flow_spec *rule,
u32 rss_context)
{
struct ethtool_tcpip4_spec *ip_entry = &rule->h_u.tcp_ip4_spec;
struct ethtool_tcpip4_spec *ip_mask = &rule->m_u.tcp_ip4_spec;
Expand All @@ -1066,6 +1086,7 @@ static int efx_ethtool_set_class_rule(struct efx_nic *efx,
struct ethtool_usrip6_spec *uip6_mask = &rule->m_u.usr_ip6_spec;
struct ethhdr *mac_entry = &rule->h_u.ether_spec;
struct ethhdr *mac_mask = &rule->m_u.ether_spec;
enum efx_filter_flags flags = 0;
struct efx_filter_spec spec;
int rc;

Expand All @@ -1084,12 +1105,19 @@ static int efx_ethtool_set_class_rule(struct efx_nic *efx,
rule->m_ext.data[1]))
return -EINVAL;

efx_filter_init_rx(&spec, EFX_FILTER_PRI_MANUAL,
efx->rx_scatter ? EFX_FILTER_FLAG_RX_SCATTER : 0,
if (efx->rx_scatter)
flags |= EFX_FILTER_FLAG_RX_SCATTER;
if (rule->flow_type & FLOW_RSS)
flags |= EFX_FILTER_FLAG_RX_RSS;

efx_filter_init_rx(&spec, EFX_FILTER_PRI_MANUAL, flags,
(rule->ring_cookie == RX_CLS_FLOW_DISC) ?
EFX_FILTER_RX_DMAQ_ID_DROP : rule->ring_cookie);

switch (rule->flow_type & ~FLOW_EXT) {
if (rule->flow_type & FLOW_RSS)
spec.rss_context = rss_context;

switch (rule->flow_type & ~(FLOW_EXT | FLOW_RSS)) {
case TCP_V4_FLOW:
case UDP_V4_FLOW:
spec.match_flags = (EFX_FILTER_MATCH_ETHER_TYPE |
Expand Down Expand Up @@ -1265,7 +1293,8 @@ static int efx_ethtool_set_rxnfc(struct net_device *net_dev,

switch (info->cmd) {
case ETHTOOL_SRXCLSRLINS:
return efx_ethtool_set_class_rule(efx, &info->fs);
return efx_ethtool_set_class_rule(efx, &info->fs,
info->rss_context);

case ETHTOOL_SRXCLSRLDEL:
return efx_filter_remove_id_safe(efx, EFX_FILTER_PRI_MANUAL,
Expand All @@ -1280,7 +1309,9 @@ static u32 efx_ethtool_get_rxfh_indir_size(struct net_device *net_dev)
{
struct efx_nic *efx = netdev_priv(net_dev);

return (efx->n_rx_channels == 1) ? 0 : ARRAY_SIZE(efx->rx_indir_table);
if (efx->n_rx_channels == 1)
return 0;
return ARRAY_SIZE(efx->rss_context.rx_indir_table);
}

static u32 efx_ethtool_get_rxfh_key_size(struct net_device *net_dev)
Expand All @@ -1303,9 +1334,11 @@ static int efx_ethtool_get_rxfh(struct net_device *net_dev, u32 *indir, u8 *key,
if (hfunc)
*hfunc = ETH_RSS_HASH_TOP;
if (indir)
memcpy(indir, efx->rx_indir_table, sizeof(efx->rx_indir_table));
memcpy(indir, efx->rss_context.rx_indir_table,
sizeof(efx->rss_context.rx_indir_table));
if (key)
memcpy(key, efx->rx_hash_key, efx->type->rx_hash_key_size);
memcpy(key, efx->rss_context.rx_hash_key,
efx->type->rx_hash_key_size);
return 0;
}

Expand All @@ -1321,13 +1354,93 @@ static int efx_ethtool_set_rxfh(struct net_device *net_dev, const u32 *indir,
return 0;

if (!key)
key = efx->rx_hash_key;
key = efx->rss_context.rx_hash_key;
if (!indir)
indir = efx->rx_indir_table;
indir = efx->rss_context.rx_indir_table;

return efx->type->rx_push_rss_config(efx, true, indir, key);
}

static int efx_ethtool_get_rxfh_context(struct net_device *net_dev, u32 *indir,
u8 *key, u8 *hfunc, u32 rss_context)
{
struct efx_nic *efx = netdev_priv(net_dev);
struct efx_rss_context *ctx;
int rc;

if (!efx->type->rx_pull_rss_context_config)
return -EOPNOTSUPP;
ctx = efx_find_rss_context_entry(rss_context, &efx->rss_context.list);
if (!ctx)
return -ENOENT;
rc = efx->type->rx_pull_rss_context_config(efx, ctx);
if (rc)
return rc;

if (hfunc)
*hfunc = ETH_RSS_HASH_TOP;
if (indir)
memcpy(indir, ctx->rx_indir_table, sizeof(ctx->rx_indir_table));
if (key)
memcpy(key, ctx->rx_hash_key, efx->type->rx_hash_key_size);
return 0;
}

static int efx_ethtool_set_rxfh_context(struct net_device *net_dev,
const u32 *indir, const u8 *key,
const u8 hfunc, u32 *rss_context,
bool delete)
{
struct efx_nic *efx = netdev_priv(net_dev);
struct efx_rss_context *ctx;
bool allocated = false;
int rc;

if (!efx->type->rx_push_rss_context_config)
return -EOPNOTSUPP;
/* Hash function is Toeplitz, cannot be changed */
if (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP)
return -EOPNOTSUPP;
if (*rss_context == ETH_RXFH_CONTEXT_ALLOC) {
if (delete)
/* alloc + delete == Nothing to do */
return -EINVAL;
ctx = efx_alloc_rss_context_entry(&efx->rss_context.list);
if (!ctx)
return -ENOMEM;
ctx->context_id = EFX_EF10_RSS_CONTEXT_INVALID;
/* Initialise indir table and key to defaults */
efx_set_default_rx_indir_table(efx, ctx);
netdev_rss_key_fill(ctx->rx_hash_key, sizeof(ctx->rx_hash_key));
allocated = true;
} else {
ctx = efx_find_rss_context_entry(*rss_context,
&efx->rss_context.list);
if (!ctx)
return -ENOENT;
}

if (delete) {
/* delete this context */
rc = efx->type->rx_push_rss_context_config(efx, ctx, NULL, NULL);
if (!rc)
efx_free_rss_context_entry(ctx);
return rc;
}

if (!key)
key = ctx->rx_hash_key;
if (!indir)
indir = ctx->rx_indir_table;

rc = efx->type->rx_push_rss_context_config(efx, ctx, indir, key);
if (rc && allocated)
efx_free_rss_context_entry(ctx);
else
*rss_context = ctx->user_id;
return rc;
}

static int efx_ethtool_get_ts_info(struct net_device *net_dev,
struct ethtool_ts_info *ts_info)
{
Expand Down Expand Up @@ -1403,6 +1516,8 @@ const struct ethtool_ops efx_ethtool_ops = {
.get_rxfh_key_size = efx_ethtool_get_rxfh_key_size,
.get_rxfh = efx_ethtool_get_rxfh,
.set_rxfh = efx_ethtool_set_rxfh,
.get_rxfh_context = efx_ethtool_get_rxfh_context,
.set_rxfh_context = efx_ethtool_set_rxfh_context,
.get_ts_info = efx_ethtool_get_ts_info,
.get_module_info = efx_ethtool_get_module_info,
.get_module_eeprom = efx_ethtool_get_module_eeprom,
Expand Down
Loading

0 comments on commit 42356d9

Please sign in to comment.