sfc: move RPS code
Includes a couple of filtering functions and also renames a constant. Style fixes included. Signed-off-by: Alexandru-Mihai Maftei <amaftei@solarflare.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
314823108c
commit
f7226e0f9b
@ -660,7 +660,7 @@ static int efx_ef10_probe(struct efx_nic *efx)
|
|||||||
}
|
}
|
||||||
nic_data->warm_boot_count = rc;
|
nic_data->warm_boot_count = rc;
|
||||||
|
|
||||||
efx->rss_context.context_id = EFX_EF10_RSS_CONTEXT_INVALID;
|
efx->rss_context.context_id = EFX_MCDI_RSS_CONTEXT_INVALID;
|
||||||
|
|
||||||
nic_data->vport_id = EVB_PORT_ID_ASSIGNED;
|
nic_data->vport_id = EVB_PORT_ID_ASSIGNED;
|
||||||
|
|
||||||
@ -1440,7 +1440,7 @@ static void efx_ef10_reset_mc_allocations(struct efx_nic *efx)
|
|||||||
nic_data->must_restore_filters = true;
|
nic_data->must_restore_filters = true;
|
||||||
nic_data->must_restore_piobufs = true;
|
nic_data->must_restore_piobufs = true;
|
||||||
efx_ef10_forget_old_piobufs(efx);
|
efx_ef10_forget_old_piobufs(efx);
|
||||||
efx->rss_context.context_id = EFX_EF10_RSS_CONTEXT_INVALID;
|
efx->rss_context.context_id = EFX_MCDI_RSS_CONTEXT_INVALID;
|
||||||
|
|
||||||
/* Driver-created vswitches and vports must be re-created */
|
/* Driver-created vswitches and vports must be re-created */
|
||||||
nic_data->must_probe_vswitching = true;
|
nic_data->must_probe_vswitching = true;
|
||||||
@ -2598,7 +2598,7 @@ static int efx_ef10_alloc_rss_context(struct efx_nic *efx, bool exclusive,
|
|||||||
EFX_EF10_MAX_SHARED_RSS_CONTEXT_SIZE);
|
EFX_EF10_MAX_SHARED_RSS_CONTEXT_SIZE);
|
||||||
|
|
||||||
if (!exclusive && rss_spread == 1) {
|
if (!exclusive && rss_spread == 1) {
|
||||||
ctx->context_id = EFX_EF10_RSS_CONTEXT_INVALID;
|
ctx->context_id = EFX_MCDI_RSS_CONTEXT_INVALID;
|
||||||
if (context_size)
|
if (context_size)
|
||||||
*context_size = 1;
|
*context_size = 1;
|
||||||
return 0;
|
return 0;
|
||||||
@ -2685,11 +2685,11 @@ static void efx_ef10_rx_free_indir_table(struct efx_nic *efx)
|
|||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
if (efx->rss_context.context_id != EFX_EF10_RSS_CONTEXT_INVALID) {
|
if (efx->rss_context.context_id != EFX_MCDI_RSS_CONTEXT_INVALID) {
|
||||||
rc = efx_ef10_free_rss_context(efx, efx->rss_context.context_id);
|
rc = efx_ef10_free_rss_context(efx, efx->rss_context.context_id);
|
||||||
WARN_ON(rc != 0);
|
WARN_ON(rc != 0);
|
||||||
}
|
}
|
||||||
efx->rss_context.context_id = EFX_EF10_RSS_CONTEXT_INVALID;
|
efx->rss_context.context_id = EFX_MCDI_RSS_CONTEXT_INVALID;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int efx_ef10_rx_push_shared_rss_config(struct efx_nic *efx,
|
static int efx_ef10_rx_push_shared_rss_config(struct efx_nic *efx,
|
||||||
@ -2715,7 +2715,7 @@ static int efx_ef10_rx_push_exclusive_rss_config(struct efx_nic *efx,
|
|||||||
struct efx_ef10_nic_data *nic_data = efx->nic_data;
|
struct efx_ef10_nic_data *nic_data = efx->nic_data;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
if (efx->rss_context.context_id == EFX_EF10_RSS_CONTEXT_INVALID ||
|
if (efx->rss_context.context_id == EFX_MCDI_RSS_CONTEXT_INVALID ||
|
||||||
!nic_data->rx_rss_context_exclusive) {
|
!nic_data->rx_rss_context_exclusive) {
|
||||||
rc = efx_ef10_alloc_rss_context(efx, true, &efx->rss_context,
|
rc = efx_ef10_alloc_rss_context(efx, true, &efx->rss_context,
|
||||||
NULL);
|
NULL);
|
||||||
@ -2731,7 +2731,7 @@ static int efx_ef10_rx_push_exclusive_rss_config(struct efx_nic *efx,
|
|||||||
goto fail2;
|
goto fail2;
|
||||||
|
|
||||||
if (efx->rss_context.context_id != old_rx_rss_context &&
|
if (efx->rss_context.context_id != old_rx_rss_context &&
|
||||||
old_rx_rss_context != EFX_EF10_RSS_CONTEXT_INVALID)
|
old_rx_rss_context != EFX_MCDI_RSS_CONTEXT_INVALID)
|
||||||
WARN_ON(efx_ef10_free_rss_context(efx, old_rx_rss_context) != 0);
|
WARN_ON(efx_ef10_free_rss_context(efx, old_rx_rss_context) != 0);
|
||||||
nic_data->rx_rss_context_exclusive = true;
|
nic_data->rx_rss_context_exclusive = true;
|
||||||
if (rx_indir_table != efx->rss_context.rx_indir_table)
|
if (rx_indir_table != efx->rss_context.rx_indir_table)
|
||||||
@ -2762,7 +2762,7 @@ static int efx_ef10_rx_push_rss_context_config(struct efx_nic *efx,
|
|||||||
|
|
||||||
WARN_ON(!mutex_is_locked(&efx->rss_lock));
|
WARN_ON(!mutex_is_locked(&efx->rss_lock));
|
||||||
|
|
||||||
if (ctx->context_id == EFX_EF10_RSS_CONTEXT_INVALID) {
|
if (ctx->context_id == EFX_MCDI_RSS_CONTEXT_INVALID) {
|
||||||
rc = efx_ef10_alloc_rss_context(efx, true, ctx, NULL);
|
rc = efx_ef10_alloc_rss_context(efx, true, ctx, NULL);
|
||||||
if (rc)
|
if (rc)
|
||||||
return rc;
|
return rc;
|
||||||
@ -2797,7 +2797,7 @@ static int efx_ef10_rx_pull_rss_context_config(struct efx_nic *efx,
|
|||||||
BUILD_BUG_ON(MC_CMD_RSS_CONTEXT_GET_TABLE_IN_LEN !=
|
BUILD_BUG_ON(MC_CMD_RSS_CONTEXT_GET_TABLE_IN_LEN !=
|
||||||
MC_CMD_RSS_CONTEXT_GET_KEY_IN_LEN);
|
MC_CMD_RSS_CONTEXT_GET_KEY_IN_LEN);
|
||||||
|
|
||||||
if (ctx->context_id == EFX_EF10_RSS_CONTEXT_INVALID)
|
if (ctx->context_id == EFX_MCDI_RSS_CONTEXT_INVALID)
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
|
|
||||||
MCDI_SET_DWORD(inbuf, RSS_CONTEXT_GET_TABLE_IN_RSS_CONTEXT_ID,
|
MCDI_SET_DWORD(inbuf, RSS_CONTEXT_GET_TABLE_IN_RSS_CONTEXT_ID,
|
||||||
@ -2858,7 +2858,7 @@ static void efx_ef10_rx_restore_rss_contexts(struct efx_nic *efx)
|
|||||||
|
|
||||||
list_for_each_entry(ctx, &efx->rss_context.list, list) {
|
list_for_each_entry(ctx, &efx->rss_context.list, list) {
|
||||||
/* previous NIC RSS context is gone */
|
/* previous NIC RSS context is gone */
|
||||||
ctx->context_id = EFX_EF10_RSS_CONTEXT_INVALID;
|
ctx->context_id = EFX_MCDI_RSS_CONTEXT_INVALID;
|
||||||
/* so try to allocate a new one */
|
/* so try to allocate a new one */
|
||||||
rc = efx_ef10_rx_push_rss_context_config(efx, ctx,
|
rc = efx_ef10_rx_push_rss_context_config(efx, ctx,
|
||||||
ctx->rx_indir_table,
|
ctx->rx_indir_table,
|
||||||
@ -2929,7 +2929,7 @@ static int efx_ef10_vf_rx_push_rss_config(struct efx_nic *efx, bool user,
|
|||||||
{
|
{
|
||||||
if (user)
|
if (user)
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
if (efx->rss_context.context_id != EFX_EF10_RSS_CONTEXT_INVALID)
|
if (efx->rss_context.context_id != EFX_MCDI_RSS_CONTEXT_INVALID)
|
||||||
return 0;
|
return 0;
|
||||||
return efx_ef10_rx_push_shared_rss_config(efx, NULL);
|
return efx_ef10_rx_push_shared_rss_config(efx, NULL);
|
||||||
}
|
}
|
||||||
@ -3850,7 +3850,7 @@ static void efx_ef10_filter_push_prep(struct efx_nic *efx,
|
|||||||
*/
|
*/
|
||||||
if (WARN_ON_ONCE(!ctx))
|
if (WARN_ON_ONCE(!ctx))
|
||||||
flags &= ~EFX_FILTER_FLAG_RX_RSS;
|
flags &= ~EFX_FILTER_FLAG_RX_RSS;
|
||||||
else if (WARN_ON_ONCE(ctx->context_id == EFX_EF10_RSS_CONTEXT_INVALID))
|
else if (WARN_ON_ONCE(ctx->context_id == EFX_MCDI_RSS_CONTEXT_INVALID))
|
||||||
flags &= ~EFX_FILTER_FLAG_RX_RSS;
|
flags &= ~EFX_FILTER_FLAG_RX_RSS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4029,7 +4029,7 @@ static s32 efx_ef10_filter_insert_locked(struct efx_nic *efx,
|
|||||||
rc = -ENOENT;
|
rc = -ENOENT;
|
||||||
goto out_unlock;
|
goto out_unlock;
|
||||||
}
|
}
|
||||||
if (ctx->context_id == EFX_EF10_RSS_CONTEXT_INVALID) {
|
if (ctx->context_id == EFX_MCDI_RSS_CONTEXT_INVALID) {
|
||||||
rc = -EOPNOTSUPP;
|
rc = -EOPNOTSUPP;
|
||||||
goto out_unlock;
|
goto out_unlock;
|
||||||
}
|
}
|
||||||
@ -4770,7 +4770,7 @@ static void efx_ef10_filter_table_restore(struct efx_nic *efx)
|
|||||||
invalid_filters++;
|
invalid_filters++;
|
||||||
goto not_restored;
|
goto not_restored;
|
||||||
}
|
}
|
||||||
if (ctx->context_id == EFX_EF10_RSS_CONTEXT_INVALID) {
|
if (ctx->context_id == EFX_MCDI_RSS_CONTEXT_INVALID) {
|
||||||
netif_warn(efx, drv, efx->net_dev,
|
netif_warn(efx, drv, efx->net_dev,
|
||||||
"Warning: unable to restore a filter with RSS context %u as it was not created.\n",
|
"Warning: unable to restore a filter with RSS context %u as it was not created.\n",
|
||||||
spec->rss_context);
|
spec->rss_context);
|
||||||
|
@ -384,70 +384,6 @@ static void efx_remove_nic(struct efx_nic *efx)
|
|||||||
efx->type->remove(efx);
|
efx->type->remove(efx);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int efx_probe_filters(struct efx_nic *efx)
|
|
||||||
{
|
|
||||||
int rc;
|
|
||||||
|
|
||||||
init_rwsem(&efx->filter_sem);
|
|
||||||
mutex_lock(&efx->mac_lock);
|
|
||||||
down_write(&efx->filter_sem);
|
|
||||||
rc = efx->type->filter_table_probe(efx);
|
|
||||||
if (rc)
|
|
||||||
goto out_unlock;
|
|
||||||
|
|
||||||
#ifdef CONFIG_RFS_ACCEL
|
|
||||||
if (efx->type->offload_features & NETIF_F_NTUPLE) {
|
|
||||||
struct efx_channel *channel;
|
|
||||||
int i, success = 1;
|
|
||||||
|
|
||||||
efx_for_each_channel(channel, efx) {
|
|
||||||
channel->rps_flow_id =
|
|
||||||
kcalloc(efx->type->max_rx_ip_filters,
|
|
||||||
sizeof(*channel->rps_flow_id),
|
|
||||||
GFP_KERNEL);
|
|
||||||
if (!channel->rps_flow_id)
|
|
||||||
success = 0;
|
|
||||||
else
|
|
||||||
for (i = 0;
|
|
||||||
i < efx->type->max_rx_ip_filters;
|
|
||||||
++i)
|
|
||||||
channel->rps_flow_id[i] =
|
|
||||||
RPS_FLOW_ID_INVALID;
|
|
||||||
channel->rfs_expire_index = 0;
|
|
||||||
channel->rfs_filter_count = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!success) {
|
|
||||||
efx_for_each_channel(channel, efx)
|
|
||||||
kfree(channel->rps_flow_id);
|
|
||||||
efx->type->filter_table_remove(efx);
|
|
||||||
rc = -ENOMEM;
|
|
||||||
goto out_unlock;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
out_unlock:
|
|
||||||
up_write(&efx->filter_sem);
|
|
||||||
mutex_unlock(&efx->mac_lock);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void efx_remove_filters(struct efx_nic *efx)
|
|
||||||
{
|
|
||||||
#ifdef CONFIG_RFS_ACCEL
|
|
||||||
struct efx_channel *channel;
|
|
||||||
|
|
||||||
efx_for_each_channel(channel, efx) {
|
|
||||||
cancel_delayed_work_sync(&channel->filter_work);
|
|
||||||
kfree(channel->rps_flow_id);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
down_write(&efx->filter_sem);
|
|
||||||
efx->type->filter_table_remove(efx);
|
|
||||||
up_write(&efx->filter_sem);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
/**************************************************************************
|
||||||
*
|
*
|
||||||
* NIC startup/shutdown
|
* NIC startup/shutdown
|
||||||
@ -1109,142 +1045,6 @@ void efx_update_sw_stats(struct efx_nic *efx, u64 *stats)
|
|||||||
stats[GENERIC_STAT_rx_noskb_drops] = atomic_read(&efx->n_rx_noskb_drops);
|
stats[GENERIC_STAT_rx_noskb_drops] = atomic_read(&efx->n_rx_noskb_drops);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool efx_filter_spec_equal(const struct efx_filter_spec *left,
|
|
||||||
const struct efx_filter_spec *right)
|
|
||||||
{
|
|
||||||
if ((left->match_flags ^ right->match_flags) |
|
|
||||||
((left->flags ^ right->flags) &
|
|
||||||
(EFX_FILTER_FLAG_RX | EFX_FILTER_FLAG_TX)))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return memcmp(&left->outer_vid, &right->outer_vid,
|
|
||||||
sizeof(struct efx_filter_spec) -
|
|
||||||
offsetof(struct efx_filter_spec, outer_vid)) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 efx_filter_spec_hash(const struct efx_filter_spec *spec)
|
|
||||||
{
|
|
||||||
BUILD_BUG_ON(offsetof(struct efx_filter_spec, outer_vid) & 3);
|
|
||||||
return jhash2((const u32 *)&spec->outer_vid,
|
|
||||||
(sizeof(struct efx_filter_spec) -
|
|
||||||
offsetof(struct efx_filter_spec, outer_vid)) / 4,
|
|
||||||
0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef CONFIG_RFS_ACCEL
|
|
||||||
bool efx_rps_check_rule(struct efx_arfs_rule *rule, unsigned int filter_idx,
|
|
||||||
bool *force)
|
|
||||||
{
|
|
||||||
if (rule->filter_id == EFX_ARFS_FILTER_ID_PENDING) {
|
|
||||||
/* ARFS is currently updating this entry, leave it */
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (rule->filter_id == EFX_ARFS_FILTER_ID_ERROR) {
|
|
||||||
/* ARFS tried and failed to update this, so it's probably out
|
|
||||||
* of date. Remove the filter and the ARFS rule entry.
|
|
||||||
*/
|
|
||||||
rule->filter_id = EFX_ARFS_FILTER_ID_REMOVING;
|
|
||||||
*force = true;
|
|
||||||
return true;
|
|
||||||
} else if (WARN_ON(rule->filter_id != filter_idx)) { /* can't happen */
|
|
||||||
/* ARFS has moved on, so old filter is not needed. Since we did
|
|
||||||
* not mark the rule with EFX_ARFS_FILTER_ID_REMOVING, it will
|
|
||||||
* not be removed by efx_rps_hash_del() subsequently.
|
|
||||||
*/
|
|
||||||
*force = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
/* Remove it iff ARFS wants to. */
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static
|
|
||||||
struct hlist_head *efx_rps_hash_bucket(struct efx_nic *efx,
|
|
||||||
const struct efx_filter_spec *spec)
|
|
||||||
{
|
|
||||||
u32 hash = efx_filter_spec_hash(spec);
|
|
||||||
|
|
||||||
lockdep_assert_held(&efx->rps_hash_lock);
|
|
||||||
if (!efx->rps_hash_table)
|
|
||||||
return NULL;
|
|
||||||
return &efx->rps_hash_table[hash % EFX_ARFS_HASH_TABLE_SIZE];
|
|
||||||
}
|
|
||||||
|
|
||||||
struct efx_arfs_rule *efx_rps_hash_find(struct efx_nic *efx,
|
|
||||||
const struct efx_filter_spec *spec)
|
|
||||||
{
|
|
||||||
struct efx_arfs_rule *rule;
|
|
||||||
struct hlist_head *head;
|
|
||||||
struct hlist_node *node;
|
|
||||||
|
|
||||||
head = efx_rps_hash_bucket(efx, spec);
|
|
||||||
if (!head)
|
|
||||||
return NULL;
|
|
||||||
hlist_for_each(node, head) {
|
|
||||||
rule = container_of(node, struct efx_arfs_rule, node);
|
|
||||||
if (efx_filter_spec_equal(spec, &rule->spec))
|
|
||||||
return rule;
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct efx_arfs_rule *efx_rps_hash_add(struct efx_nic *efx,
|
|
||||||
const struct efx_filter_spec *spec,
|
|
||||||
bool *new)
|
|
||||||
{
|
|
||||||
struct efx_arfs_rule *rule;
|
|
||||||
struct hlist_head *head;
|
|
||||||
struct hlist_node *node;
|
|
||||||
|
|
||||||
head = efx_rps_hash_bucket(efx, spec);
|
|
||||||
if (!head)
|
|
||||||
return NULL;
|
|
||||||
hlist_for_each(node, head) {
|
|
||||||
rule = container_of(node, struct efx_arfs_rule, node);
|
|
||||||
if (efx_filter_spec_equal(spec, &rule->spec)) {
|
|
||||||
*new = false;
|
|
||||||
return rule;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
rule = kmalloc(sizeof(*rule), GFP_ATOMIC);
|
|
||||||
*new = true;
|
|
||||||
if (rule) {
|
|
||||||
memcpy(&rule->spec, spec, sizeof(rule->spec));
|
|
||||||
hlist_add_head(&rule->node, head);
|
|
||||||
}
|
|
||||||
return rule;
|
|
||||||
}
|
|
||||||
|
|
||||||
void efx_rps_hash_del(struct efx_nic *efx, const struct efx_filter_spec *spec)
|
|
||||||
{
|
|
||||||
struct efx_arfs_rule *rule;
|
|
||||||
struct hlist_head *head;
|
|
||||||
struct hlist_node *node;
|
|
||||||
|
|
||||||
head = efx_rps_hash_bucket(efx, spec);
|
|
||||||
if (WARN_ON(!head))
|
|
||||||
return;
|
|
||||||
hlist_for_each(node, head) {
|
|
||||||
rule = container_of(node, struct efx_arfs_rule, node);
|
|
||||||
if (efx_filter_spec_equal(spec, &rule->spec)) {
|
|
||||||
/* Someone already reused the entry. We know that if
|
|
||||||
* this check doesn't fire (i.e. filter_id == REMOVING)
|
|
||||||
* then the REMOVING mark was put there by our caller,
|
|
||||||
* because caller is holding a lock on filter table and
|
|
||||||
* only holders of that lock set REMOVING.
|
|
||||||
*/
|
|
||||||
if (rule->filter_id != EFX_ARFS_FILTER_ID_REMOVING)
|
|
||||||
return;
|
|
||||||
hlist_del(node);
|
|
||||||
kfree(rule);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* We didn't find it. */
|
|
||||||
WARN_ON(1);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**************************************************************************
|
/**************************************************************************
|
||||||
*
|
*
|
||||||
* PCI interface
|
* PCI interface
|
||||||
|
@ -169,33 +169,11 @@ static inline void efx_filter_rfs_expire(struct work_struct *data)
|
|||||||
static inline void efx_filter_rfs_expire(struct work_struct *data) {}
|
static inline void efx_filter_rfs_expire(struct work_struct *data) {}
|
||||||
#define efx_filter_rfs_enabled() 0
|
#define efx_filter_rfs_enabled() 0
|
||||||
#endif
|
#endif
|
||||||
bool efx_filter_is_mc_recipient(const struct efx_filter_spec *spec);
|
|
||||||
|
|
||||||
bool efx_filter_spec_equal(const struct efx_filter_spec *left,
|
|
||||||
const struct efx_filter_spec *right);
|
|
||||||
u32 efx_filter_spec_hash(const struct efx_filter_spec *spec);
|
|
||||||
|
|
||||||
#ifdef CONFIG_RFS_ACCEL
|
|
||||||
bool efx_rps_check_rule(struct efx_arfs_rule *rule, unsigned int filter_idx,
|
|
||||||
bool *force);
|
|
||||||
|
|
||||||
struct efx_arfs_rule *efx_rps_hash_find(struct efx_nic *efx,
|
|
||||||
const struct efx_filter_spec *spec);
|
|
||||||
|
|
||||||
/* @new is written to indicate if entry was newly added (true) or if an old
|
|
||||||
* entry was found and returned (false).
|
|
||||||
*/
|
|
||||||
struct efx_arfs_rule *efx_rps_hash_add(struct efx_nic *efx,
|
|
||||||
const struct efx_filter_spec *spec,
|
|
||||||
bool *new);
|
|
||||||
|
|
||||||
void efx_rps_hash_del(struct efx_nic *efx, const struct efx_filter_spec *spec);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* RSS contexts */
|
/* RSS contexts */
|
||||||
static inline bool efx_rss_active(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;
|
return ctx->context_id != EFX_MCDI_RSS_CONTEXT_INVALID;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Ethtool support */
|
/* Ethtool support */
|
||||||
|
@ -1020,7 +1020,7 @@ static int efx_ethtool_set_rxfh_context(struct net_device *net_dev,
|
|||||||
rc = -ENOMEM;
|
rc = -ENOMEM;
|
||||||
goto out_unlock;
|
goto out_unlock;
|
||||||
}
|
}
|
||||||
ctx->context_id = EFX_EF10_RSS_CONTEXT_INVALID;
|
ctx->context_id = EFX_MCDI_RSS_CONTEXT_INVALID;
|
||||||
/* Initialise indir table and key to defaults */
|
/* Initialise indir table and key to defaults */
|
||||||
efx_set_default_rx_indir_table(efx, ctx);
|
efx_set_default_rx_indir_table(efx, ctx);
|
||||||
netdev_rss_key_fill(ctx->rx_hash_key, sizeof(ctx->rx_hash_key));
|
netdev_rss_key_fill(ctx->rx_hash_key, sizeof(ctx->rx_hash_key));
|
||||||
|
@ -744,13 +744,13 @@ union efx_multicast_hash {
|
|||||||
struct vfdi_status;
|
struct vfdi_status;
|
||||||
|
|
||||||
/* The reserved RSS context value */
|
/* The reserved RSS context value */
|
||||||
#define EFX_EF10_RSS_CONTEXT_INVALID 0xffffffff
|
#define EFX_MCDI_RSS_CONTEXT_INVALID 0xffffffff
|
||||||
/**
|
/**
|
||||||
* struct efx_rss_context - A user-defined RSS context for filtering
|
* struct efx_rss_context - A user-defined RSS context for filtering
|
||||||
* @list: node of linked list on which this struct is stored
|
* @list: node of linked list on which this struct is stored
|
||||||
* @context_id: the RSS_CONTEXT_ID returned by MC firmware, or
|
* @context_id: the RSS_CONTEXT_ID returned by MC firmware, or
|
||||||
* %EFX_EF10_RSS_CONTEXT_INVALID if this context is not present on the NIC.
|
* %EFX_MCDI_RSS_CONTEXT_INVALID if this context is not present on the NIC.
|
||||||
* For Siena, 0 if RSS is active, else %EFX_EF10_RSS_CONTEXT_INVALID.
|
* For Siena, 0 if RSS is active, else %EFX_MCDI_RSS_CONTEXT_INVALID.
|
||||||
* @user_id: the rss_context ID exposed to userspace over ethtool.
|
* @user_id: the rss_context ID exposed to userspace over ethtool.
|
||||||
* @rx_hash_udp_4tuple: UDP 4-tuple hashing enabled
|
* @rx_hash_udp_4tuple: UDP 4-tuple hashing enabled
|
||||||
* @rx_hash_key: Toeplitz hash key for this RSS context
|
* @rx_hash_key: Toeplitz hash key for this RSS context
|
||||||
|
@ -650,37 +650,3 @@ bool __efx_filter_rfs_expire(struct efx_channel *channel, unsigned int quota)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#endif /* CONFIG_RFS_ACCEL */
|
#endif /* CONFIG_RFS_ACCEL */
|
||||||
|
|
||||||
/**
|
|
||||||
* efx_filter_is_mc_recipient - test whether spec is a multicast recipient
|
|
||||||
* @spec: Specification to test
|
|
||||||
*
|
|
||||||
* Return: %true if the specification is a non-drop RX filter that
|
|
||||||
* matches a local MAC address I/G bit value of 1 or matches a local
|
|
||||||
* IPv4 or IPv6 address value in the respective multicast address
|
|
||||||
* range. Otherwise %false.
|
|
||||||
*/
|
|
||||||
bool efx_filter_is_mc_recipient(const struct efx_filter_spec *spec)
|
|
||||||
{
|
|
||||||
if (!(spec->flags & EFX_FILTER_FLAG_RX) ||
|
|
||||||
spec->dmaq_id == EFX_FILTER_RX_DMAQ_ID_DROP)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (spec->match_flags &
|
|
||||||
(EFX_FILTER_MATCH_LOC_MAC | EFX_FILTER_MATCH_LOC_MAC_IG) &&
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
@ -579,7 +579,7 @@ struct efx_rss_context *efx_alloc_rss_context_entry(struct efx_nic *efx)
|
|||||||
new = kmalloc(sizeof(*new), GFP_KERNEL);
|
new = kmalloc(sizeof(*new), GFP_KERNEL);
|
||||||
if (!new)
|
if (!new)
|
||||||
return NULL;
|
return NULL;
|
||||||
new->context_id = EFX_EF10_RSS_CONTEXT_INVALID;
|
new->context_id = EFX_MCDI_RSS_CONTEXT_INVALID;
|
||||||
new->rx_hash_udp_4tuple = false;
|
new->rx_hash_udp_4tuple = false;
|
||||||
|
|
||||||
/* Insert the new entry into the gap */
|
/* Insert the new entry into the gap */
|
||||||
@ -616,3 +616,236 @@ void efx_set_default_rx_indir_table(struct efx_nic *efx,
|
|||||||
ctx->rx_indir_table[i] =
|
ctx->rx_indir_table[i] =
|
||||||
ethtool_rxfh_indir_default(i, efx->rss_spread);
|
ethtool_rxfh_indir_default(i, efx->rss_spread);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* efx_filter_is_mc_recipient - test whether spec is a multicast recipient
|
||||||
|
* @spec: Specification to test
|
||||||
|
*
|
||||||
|
* Return: %true if the specification is a non-drop RX filter that
|
||||||
|
* matches a local MAC address I/G bit value of 1 or matches a local
|
||||||
|
* IPv4 or IPv6 address value in the respective multicast address
|
||||||
|
* range. Otherwise %false.
|
||||||
|
*/
|
||||||
|
bool efx_filter_is_mc_recipient(const struct efx_filter_spec *spec)
|
||||||
|
{
|
||||||
|
if (!(spec->flags & EFX_FILTER_FLAG_RX) ||
|
||||||
|
spec->dmaq_id == EFX_FILTER_RX_DMAQ_ID_DROP)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (spec->match_flags &
|
||||||
|
(EFX_FILTER_MATCH_LOC_MAC | EFX_FILTER_MATCH_LOC_MAC_IG) &&
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool efx_filter_spec_equal(const struct efx_filter_spec *left,
|
||||||
|
const struct efx_filter_spec *right)
|
||||||
|
{
|
||||||
|
if ((left->match_flags ^ right->match_flags) |
|
||||||
|
((left->flags ^ right->flags) &
|
||||||
|
(EFX_FILTER_FLAG_RX | EFX_FILTER_FLAG_TX)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return memcmp(&left->outer_vid, &right->outer_vid,
|
||||||
|
sizeof(struct efx_filter_spec) -
|
||||||
|
offsetof(struct efx_filter_spec, outer_vid)) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 efx_filter_spec_hash(const struct efx_filter_spec *spec)
|
||||||
|
{
|
||||||
|
BUILD_BUG_ON(offsetof(struct efx_filter_spec, outer_vid) & 3);
|
||||||
|
return jhash2((const u32 *)&spec->outer_vid,
|
||||||
|
(sizeof(struct efx_filter_spec) -
|
||||||
|
offsetof(struct efx_filter_spec, outer_vid)) / 4,
|
||||||
|
0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_RFS_ACCEL
|
||||||
|
bool efx_rps_check_rule(struct efx_arfs_rule *rule, unsigned int filter_idx,
|
||||||
|
bool *force)
|
||||||
|
{
|
||||||
|
if (rule->filter_id == EFX_ARFS_FILTER_ID_PENDING) {
|
||||||
|
/* ARFS is currently updating this entry, leave it */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (rule->filter_id == EFX_ARFS_FILTER_ID_ERROR) {
|
||||||
|
/* ARFS tried and failed to update this, so it's probably out
|
||||||
|
* of date. Remove the filter and the ARFS rule entry.
|
||||||
|
*/
|
||||||
|
rule->filter_id = EFX_ARFS_FILTER_ID_REMOVING;
|
||||||
|
*force = true;
|
||||||
|
return true;
|
||||||
|
} else if (WARN_ON(rule->filter_id != filter_idx)) { /* can't happen */
|
||||||
|
/* ARFS has moved on, so old filter is not needed. Since we did
|
||||||
|
* not mark the rule with EFX_ARFS_FILTER_ID_REMOVING, it will
|
||||||
|
* not be removed by efx_rps_hash_del() subsequently.
|
||||||
|
*/
|
||||||
|
*force = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
/* Remove it iff ARFS wants to. */
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
struct hlist_head *efx_rps_hash_bucket(struct efx_nic *efx,
|
||||||
|
const struct efx_filter_spec *spec)
|
||||||
|
{
|
||||||
|
u32 hash = efx_filter_spec_hash(spec);
|
||||||
|
|
||||||
|
lockdep_assert_held(&efx->rps_hash_lock);
|
||||||
|
if (!efx->rps_hash_table)
|
||||||
|
return NULL;
|
||||||
|
return &efx->rps_hash_table[hash % EFX_ARFS_HASH_TABLE_SIZE];
|
||||||
|
}
|
||||||
|
|
||||||
|
struct efx_arfs_rule *efx_rps_hash_find(struct efx_nic *efx,
|
||||||
|
const struct efx_filter_spec *spec)
|
||||||
|
{
|
||||||
|
struct efx_arfs_rule *rule;
|
||||||
|
struct hlist_head *head;
|
||||||
|
struct hlist_node *node;
|
||||||
|
|
||||||
|
head = efx_rps_hash_bucket(efx, spec);
|
||||||
|
if (!head)
|
||||||
|
return NULL;
|
||||||
|
hlist_for_each(node, head) {
|
||||||
|
rule = container_of(node, struct efx_arfs_rule, node);
|
||||||
|
if (efx_filter_spec_equal(spec, &rule->spec))
|
||||||
|
return rule;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct efx_arfs_rule *efx_rps_hash_add(struct efx_nic *efx,
|
||||||
|
const struct efx_filter_spec *spec,
|
||||||
|
bool *new)
|
||||||
|
{
|
||||||
|
struct efx_arfs_rule *rule;
|
||||||
|
struct hlist_head *head;
|
||||||
|
struct hlist_node *node;
|
||||||
|
|
||||||
|
head = efx_rps_hash_bucket(efx, spec);
|
||||||
|
if (!head)
|
||||||
|
return NULL;
|
||||||
|
hlist_for_each(node, head) {
|
||||||
|
rule = container_of(node, struct efx_arfs_rule, node);
|
||||||
|
if (efx_filter_spec_equal(spec, &rule->spec)) {
|
||||||
|
*new = false;
|
||||||
|
return rule;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rule = kmalloc(sizeof(*rule), GFP_ATOMIC);
|
||||||
|
*new = true;
|
||||||
|
if (rule) {
|
||||||
|
memcpy(&rule->spec, spec, sizeof(rule->spec));
|
||||||
|
hlist_add_head(&rule->node, head);
|
||||||
|
}
|
||||||
|
return rule;
|
||||||
|
}
|
||||||
|
|
||||||
|
void efx_rps_hash_del(struct efx_nic *efx, const struct efx_filter_spec *spec)
|
||||||
|
{
|
||||||
|
struct efx_arfs_rule *rule;
|
||||||
|
struct hlist_head *head;
|
||||||
|
struct hlist_node *node;
|
||||||
|
|
||||||
|
head = efx_rps_hash_bucket(efx, spec);
|
||||||
|
if (WARN_ON(!head))
|
||||||
|
return;
|
||||||
|
hlist_for_each(node, head) {
|
||||||
|
rule = container_of(node, struct efx_arfs_rule, node);
|
||||||
|
if (efx_filter_spec_equal(spec, &rule->spec)) {
|
||||||
|
/* Someone already reused the entry. We know that if
|
||||||
|
* this check doesn't fire (i.e. filter_id == REMOVING)
|
||||||
|
* then the REMOVING mark was put there by our caller,
|
||||||
|
* because caller is holding a lock on filter table and
|
||||||
|
* only holders of that lock set REMOVING.
|
||||||
|
*/
|
||||||
|
if (rule->filter_id != EFX_ARFS_FILTER_ID_REMOVING)
|
||||||
|
return;
|
||||||
|
hlist_del(node);
|
||||||
|
kfree(rule);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* We didn't find it. */
|
||||||
|
WARN_ON(1);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int efx_probe_filters(struct efx_nic *efx)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
init_rwsem(&efx->filter_sem);
|
||||||
|
mutex_lock(&efx->mac_lock);
|
||||||
|
down_write(&efx->filter_sem);
|
||||||
|
rc = efx->type->filter_table_probe(efx);
|
||||||
|
if (rc)
|
||||||
|
goto out_unlock;
|
||||||
|
|
||||||
|
#ifdef CONFIG_RFS_ACCEL
|
||||||
|
if (efx->type->offload_features & NETIF_F_NTUPLE) {
|
||||||
|
struct efx_channel *channel;
|
||||||
|
int i, success = 1;
|
||||||
|
|
||||||
|
efx_for_each_channel(channel, efx) {
|
||||||
|
channel->rps_flow_id =
|
||||||
|
kcalloc(efx->type->max_rx_ip_filters,
|
||||||
|
sizeof(*channel->rps_flow_id),
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!channel->rps_flow_id)
|
||||||
|
success = 0;
|
||||||
|
else
|
||||||
|
for (i = 0;
|
||||||
|
i < efx->type->max_rx_ip_filters;
|
||||||
|
++i)
|
||||||
|
channel->rps_flow_id[i] =
|
||||||
|
RPS_FLOW_ID_INVALID;
|
||||||
|
channel->rfs_expire_index = 0;
|
||||||
|
channel->rfs_filter_count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
efx_for_each_channel(channel, efx)
|
||||||
|
kfree(channel->rps_flow_id);
|
||||||
|
efx->type->filter_table_remove(efx);
|
||||||
|
rc = -ENOMEM;
|
||||||
|
goto out_unlock;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
out_unlock:
|
||||||
|
up_write(&efx->filter_sem);
|
||||||
|
mutex_unlock(&efx->mac_lock);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
void efx_remove_filters(struct efx_nic *efx)
|
||||||
|
{
|
||||||
|
#ifdef CONFIG_RFS_ACCEL
|
||||||
|
struct efx_channel *channel;
|
||||||
|
|
||||||
|
efx_for_each_channel(channel, efx) {
|
||||||
|
cancel_delayed_work_sync(&channel->filter_work);
|
||||||
|
kfree(channel->rps_flow_id);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
down_write(&efx->filter_sem);
|
||||||
|
efx->type->filter_table_remove(efx);
|
||||||
|
up_write(&efx->filter_sem);
|
||||||
|
}
|
||||||
|
@ -74,4 +74,24 @@ struct efx_rss_context *efx_find_rss_context_entry(struct efx_nic *efx, u32 id);
|
|||||||
void efx_free_rss_context_entry(struct efx_rss_context *ctx);
|
void efx_free_rss_context_entry(struct efx_rss_context *ctx);
|
||||||
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);
|
struct efx_rss_context *ctx);
|
||||||
|
|
||||||
|
bool efx_filter_is_mc_recipient(const struct efx_filter_spec *spec);
|
||||||
|
bool efx_filter_spec_equal(const struct efx_filter_spec *left,
|
||||||
|
const struct efx_filter_spec *right);
|
||||||
|
u32 efx_filter_spec_hash(const struct efx_filter_spec *spec);
|
||||||
|
|
||||||
|
#ifdef CONFIG_RFS_ACCEL
|
||||||
|
bool efx_rps_check_rule(struct efx_arfs_rule *rule, unsigned int filter_idx,
|
||||||
|
bool *force);
|
||||||
|
struct efx_arfs_rule *efx_rps_hash_find(struct efx_nic *efx,
|
||||||
|
const struct efx_filter_spec *spec);
|
||||||
|
struct efx_arfs_rule *efx_rps_hash_add(struct efx_nic *efx,
|
||||||
|
const struct efx_filter_spec *spec,
|
||||||
|
bool *new);
|
||||||
|
void efx_rps_hash_del(struct efx_nic *efx, const struct efx_filter_spec *spec);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int efx_probe_filters(struct efx_nic *efx);
|
||||||
|
void efx_remove_filters(struct efx_nic *efx);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
Loading…
Reference in New Issue
Block a user