Merge branch 'ethtool-track-custom-rss-contexts-in-the-core'
Edward Cree says: ==================== ethtool: track custom RSS contexts in the core Make the core responsible for tracking the set of custom RSS contexts, their IDs, indirection tables, hash keys, and hash functions; this lets us get rid of duplicative code in drivers, and will allow us to support netlink dumps later. This series only moves the sfc EF10 & EF100 driver over to the new API; other drivers (mvpp2, octeontx2, mlx5, sfc/siena, bnxt_en) can be converted afterwards and the legacy API removed. ==================== Link: https://patch.msgid.link/cover.1719502239.git.ecree.xilinx@gmail.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
commit
30972a4ea0
@ -1608,7 +1608,7 @@ static void __rtl8169_set_wol(struct rtl8169_private *tp, u32 wolopts)
|
||||
|
||||
if (!tp->dash_enabled) {
|
||||
rtl_set_d3_pll_down(tp, !wolopts);
|
||||
tp->dev->wol_enabled = wolopts ? 1 : 0;
|
||||
tp->dev->ethtool->wol_enabled = wolopts ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -5478,7 +5478,7 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
|
||||
rtl_set_d3_pll_down(tp, true);
|
||||
} else {
|
||||
rtl_set_d3_pll_down(tp, false);
|
||||
dev->wol_enabled = 1;
|
||||
dev->ethtool->wol_enabled = 1;
|
||||
}
|
||||
|
||||
jumbo_max = rtl_jumbo_max(tp);
|
||||
|
@ -1396,7 +1396,7 @@ static void efx_ef10_table_reset_mc_allocations(struct efx_nic *efx)
|
||||
efx_mcdi_filter_table_reset_mc_allocations(efx);
|
||||
nic_data->must_restore_piobufs = true;
|
||||
efx_ef10_forget_old_piobufs(efx);
|
||||
efx->rss_context.context_id = EFX_MCDI_RSS_CONTEXT_INVALID;
|
||||
efx->rss_context.priv.context_id = EFX_MCDI_RSS_CONTEXT_INVALID;
|
||||
|
||||
/* Driver-created vswitches and vports must be re-created */
|
||||
nic_data->must_probe_vswitching = true;
|
||||
|
@ -59,8 +59,12 @@ const struct ethtool_ops ef100_ethtool_ops = {
|
||||
|
||||
.get_rxfh_indir_size = efx_ethtool_get_rxfh_indir_size,
|
||||
.get_rxfh_key_size = efx_ethtool_get_rxfh_key_size,
|
||||
.rxfh_priv_size = sizeof(struct efx_rss_context_priv),
|
||||
.get_rxfh = efx_ethtool_get_rxfh,
|
||||
.set_rxfh = efx_ethtool_set_rxfh,
|
||||
.create_rxfh_context = efx_ethtool_create_rxfh_context,
|
||||
.modify_rxfh_context = efx_ethtool_modify_rxfh_context,
|
||||
.remove_rxfh_context = efx_ethtool_remove_rxfh_context,
|
||||
|
||||
.get_module_info = efx_ethtool_get_module_info,
|
||||
.get_module_eeprom = efx_ethtool_get_module_eeprom,
|
||||
|
@ -299,7 +299,7 @@ static int efx_probe_nic(struct efx_nic *efx)
|
||||
if (efx->n_channels > 1)
|
||||
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);
|
||||
efx_set_default_rx_indir_table(efx, efx->rss_context.rx_indir_table);
|
||||
|
||||
/* Initialise the interrupt moderation settings */
|
||||
efx->irq_mod_step_us = DIV_ROUND_UP(efx->timer_quantum_ns, 1000);
|
||||
|
@ -158,7 +158,7 @@ static inline s32 efx_filter_get_rx_ids(struct efx_nic *efx,
|
||||
}
|
||||
|
||||
/* RSS contexts */
|
||||
static inline bool efx_rss_active(struct efx_rss_context *ctx)
|
||||
static inline bool efx_rss_active(struct efx_rss_context_priv *ctx)
|
||||
{
|
||||
return ctx->context_id != EFX_MCDI_RSS_CONTEXT_INVALID;
|
||||
}
|
||||
|
@ -714,7 +714,7 @@ void efx_reset_down(struct efx_nic *efx, enum reset_type method)
|
||||
|
||||
mutex_lock(&efx->mac_lock);
|
||||
down_write(&efx->filter_sem);
|
||||
mutex_lock(&efx->rss_lock);
|
||||
mutex_lock(&efx->net_dev->ethtool->rss_lock);
|
||||
efx->type->fini(efx);
|
||||
}
|
||||
|
||||
@ -777,7 +777,7 @@ int efx_reset_up(struct efx_nic *efx, enum reset_type method, bool ok)
|
||||
|
||||
if (efx->type->rx_restore_rss_contexts)
|
||||
efx->type->rx_restore_rss_contexts(efx);
|
||||
mutex_unlock(&efx->rss_lock);
|
||||
mutex_unlock(&efx->net_dev->ethtool->rss_lock);
|
||||
efx->type->filter_table_restore(efx);
|
||||
up_write(&efx->filter_sem);
|
||||
|
||||
@ -793,7 +793,7 @@ int efx_reset_up(struct efx_nic *efx, enum reset_type method, bool ok)
|
||||
fail:
|
||||
efx->port_initialized = false;
|
||||
|
||||
mutex_unlock(&efx->rss_lock);
|
||||
mutex_unlock(&efx->net_dev->ethtool->rss_lock);
|
||||
up_write(&efx->filter_sem);
|
||||
mutex_unlock(&efx->mac_lock);
|
||||
|
||||
@ -1000,9 +1000,7 @@ int efx_init_struct(struct efx_nic *efx, struct pci_dev *pci_dev)
|
||||
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);
|
||||
efx->rss_context.context_id = EFX_MCDI_RSS_CONTEXT_INVALID;
|
||||
mutex_init(&efx->rss_lock);
|
||||
efx->rss_context.priv.context_id = EFX_MCDI_RSS_CONTEXT_INVALID;
|
||||
efx->vport_id = EVB_PORT_ID_ASSIGNED;
|
||||
spin_lock_init(&efx->stats_lock);
|
||||
efx->vi_stride = EFX_DEFAULT_VI_STRIDE;
|
||||
|
@ -268,8 +268,12 @@ const struct ethtool_ops efx_ethtool_ops = {
|
||||
.set_rxnfc = efx_ethtool_set_rxnfc,
|
||||
.get_rxfh_indir_size = efx_ethtool_get_rxfh_indir_size,
|
||||
.get_rxfh_key_size = efx_ethtool_get_rxfh_key_size,
|
||||
.rxfh_priv_size = sizeof(struct efx_rss_context_priv),
|
||||
.get_rxfh = efx_ethtool_get_rxfh,
|
||||
.set_rxfh = efx_ethtool_set_rxfh,
|
||||
.create_rxfh_context = efx_ethtool_create_rxfh_context,
|
||||
.modify_rxfh_context = efx_ethtool_modify_rxfh_context,
|
||||
.remove_rxfh_context = efx_ethtool_remove_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,
|
||||
|
@ -820,10 +820,10 @@ int efx_ethtool_get_rxnfc(struct net_device *net_dev,
|
||||
return 0;
|
||||
|
||||
case ETHTOOL_GRXFH: {
|
||||
struct efx_rss_context *ctx = &efx->rss_context;
|
||||
struct efx_rss_context_priv *ctx = &efx->rss_context.priv;
|
||||
__u64 data;
|
||||
|
||||
mutex_lock(&efx->rss_lock);
|
||||
mutex_lock(&net_dev->ethtool->rss_lock);
|
||||
if (info->flow_type & FLOW_RSS && info->rss_context) {
|
||||
ctx = efx_find_rss_context_entry(efx, info->rss_context);
|
||||
if (!ctx) {
|
||||
@ -864,7 +864,7 @@ int efx_ethtool_get_rxnfc(struct net_device *net_dev,
|
||||
out_setdata_unlock:
|
||||
info->data = data;
|
||||
out_unlock:
|
||||
mutex_unlock(&efx->rss_lock);
|
||||
mutex_unlock(&net_dev->ethtool->rss_lock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -1163,46 +1163,14 @@ u32 efx_ethtool_get_rxfh_key_size(struct net_device *net_dev)
|
||||
return efx->type->rx_hash_key_size;
|
||||
}
|
||||
|
||||
static int efx_ethtool_get_rxfh_context(struct net_device *net_dev,
|
||||
struct ethtool_rxfh_param *rxfh)
|
||||
{
|
||||
struct efx_nic *efx = efx_netdev_priv(net_dev);
|
||||
struct efx_rss_context *ctx;
|
||||
int rc = 0;
|
||||
|
||||
if (!efx->type->rx_pull_rss_context_config)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
mutex_lock(&efx->rss_lock);
|
||||
ctx = efx_find_rss_context_entry(efx, rxfh->rss_context);
|
||||
if (!ctx) {
|
||||
rc = -ENOENT;
|
||||
goto out_unlock;
|
||||
}
|
||||
rc = efx->type->rx_pull_rss_context_config(efx, ctx);
|
||||
if (rc)
|
||||
goto out_unlock;
|
||||
|
||||
rxfh->hfunc = ETH_RSS_HASH_TOP;
|
||||
if (rxfh->indir)
|
||||
memcpy(rxfh->indir, ctx->rx_indir_table,
|
||||
sizeof(ctx->rx_indir_table));
|
||||
if (rxfh->key)
|
||||
memcpy(rxfh->key, ctx->rx_hash_key,
|
||||
efx->type->rx_hash_key_size);
|
||||
out_unlock:
|
||||
mutex_unlock(&efx->rss_lock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int efx_ethtool_get_rxfh(struct net_device *net_dev,
|
||||
struct ethtool_rxfh_param *rxfh)
|
||||
{
|
||||
struct efx_nic *efx = efx_netdev_priv(net_dev);
|
||||
int rc;
|
||||
|
||||
if (rxfh->rss_context)
|
||||
return efx_ethtool_get_rxfh_context(net_dev, rxfh);
|
||||
if (rxfh->rss_context) /* core should never call us for these */
|
||||
return -EINVAL;
|
||||
|
||||
rc = efx->type->rx_pull_rss_config(efx);
|
||||
if (rc)
|
||||
@ -1218,68 +1186,85 @@ int efx_ethtool_get_rxfh(struct net_device *net_dev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int efx_ethtool_set_rxfh_context(struct net_device *net_dev,
|
||||
struct ethtool_rxfh_param *rxfh,
|
||||
struct netlink_ext_ack *extack)
|
||||
int efx_ethtool_modify_rxfh_context(struct net_device *net_dev,
|
||||
struct ethtool_rxfh_context *ctx,
|
||||
const struct ethtool_rxfh_param *rxfh,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct efx_nic *efx = efx_netdev_priv(net_dev);
|
||||
u32 *rss_context = &rxfh->rss_context;
|
||||
struct efx_rss_context *ctx;
|
||||
u32 *indir = rxfh->indir;
|
||||
bool allocated = false;
|
||||
u8 *key = rxfh->key;
|
||||
int rc;
|
||||
struct efx_rss_context_priv *priv;
|
||||
const u32 *indir = rxfh->indir;
|
||||
const u8 *key = rxfh->key;
|
||||
|
||||
if (!efx->type->rx_push_rss_context_config)
|
||||
if (!efx->type->rx_push_rss_context_config) {
|
||||
NL_SET_ERR_MSG_MOD(extack,
|
||||
"NIC type does not support custom contexts");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
/* Hash function is Toeplitz, cannot be changed */
|
||||
if (rxfh->hfunc != ETH_RSS_HASH_NO_CHANGE &&
|
||||
rxfh->hfunc != ETH_RSS_HASH_TOP) {
|
||||
NL_SET_ERR_MSG_MOD(extack, "Only Toeplitz hash is supported");
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
mutex_lock(&efx->rss_lock);
|
||||
|
||||
if (*rss_context == ETH_RXFH_CONTEXT_ALLOC) {
|
||||
if (rxfh->rss_delete) {
|
||||
/* alloc + delete == Nothing to do */
|
||||
rc = -EINVAL;
|
||||
goto out_unlock;
|
||||
}
|
||||
ctx = efx_alloc_rss_context_entry(efx);
|
||||
if (!ctx) {
|
||||
rc = -ENOMEM;
|
||||
goto out_unlock;
|
||||
}
|
||||
ctx->context_id = EFX_MCDI_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(efx, *rss_context);
|
||||
if (!ctx) {
|
||||
rc = -ENOENT;
|
||||
goto out_unlock;
|
||||
}
|
||||
}
|
||||
|
||||
if (rxfh->rss_delete) {
|
||||
/* delete this context */
|
||||
rc = efx->type->rx_push_rss_context_config(efx, ctx, NULL, NULL);
|
||||
if (!rc)
|
||||
efx_free_rss_context_entry(ctx);
|
||||
goto out_unlock;
|
||||
}
|
||||
priv = ethtool_rxfh_context_priv(ctx);
|
||||
|
||||
if (!key)
|
||||
key = ctx->rx_hash_key;
|
||||
key = ethtool_rxfh_context_key(ctx);
|
||||
if (!indir)
|
||||
indir = ctx->rx_indir_table;
|
||||
indir = ethtool_rxfh_context_indir(ctx);
|
||||
|
||||
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;
|
||||
out_unlock:
|
||||
mutex_unlock(&efx->rss_lock);
|
||||
return rc;
|
||||
return efx->type->rx_push_rss_context_config(efx, priv, indir, key,
|
||||
false);
|
||||
}
|
||||
|
||||
int efx_ethtool_create_rxfh_context(struct net_device *net_dev,
|
||||
struct ethtool_rxfh_context *ctx,
|
||||
const struct ethtool_rxfh_param *rxfh,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct efx_nic *efx = efx_netdev_priv(net_dev);
|
||||
struct efx_rss_context_priv *priv;
|
||||
|
||||
priv = ethtool_rxfh_context_priv(ctx);
|
||||
|
||||
priv->context_id = EFX_MCDI_RSS_CONTEXT_INVALID;
|
||||
priv->rx_hash_udp_4tuple = false;
|
||||
/* Generate default indir table and/or key if not specified.
|
||||
* We use ctx as a place to store these; this is fine because
|
||||
* we're doing a create, so if we fail then the ctx will just
|
||||
* be deleted.
|
||||
*/
|
||||
if (!rxfh->indir)
|
||||
efx_set_default_rx_indir_table(efx, ethtool_rxfh_context_indir(ctx));
|
||||
if (!rxfh->key)
|
||||
netdev_rss_key_fill(ethtool_rxfh_context_key(ctx),
|
||||
ctx->key_size);
|
||||
if (rxfh->hfunc == ETH_RSS_HASH_NO_CHANGE)
|
||||
ctx->hfunc = ETH_RSS_HASH_TOP;
|
||||
if (rxfh->input_xfrm == RXH_XFRM_NO_CHANGE)
|
||||
ctx->input_xfrm = 0;
|
||||
return efx_ethtool_modify_rxfh_context(net_dev, ctx, rxfh, extack);
|
||||
}
|
||||
|
||||
int efx_ethtool_remove_rxfh_context(struct net_device *net_dev,
|
||||
struct ethtool_rxfh_context *ctx,
|
||||
u32 rss_context,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct efx_nic *efx = efx_netdev_priv(net_dev);
|
||||
struct efx_rss_context_priv *priv;
|
||||
|
||||
if (!efx->type->rx_push_rss_context_config) {
|
||||
NL_SET_ERR_MSG_MOD(extack,
|
||||
"NIC type does not support custom contexts");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
priv = ethtool_rxfh_context_priv(ctx);
|
||||
return efx->type->rx_push_rss_context_config(efx, priv, NULL, NULL,
|
||||
true);
|
||||
}
|
||||
|
||||
int efx_ethtool_set_rxfh(struct net_device *net_dev,
|
||||
@ -1295,8 +1280,9 @@ int efx_ethtool_set_rxfh(struct net_device *net_dev,
|
||||
rxfh->hfunc != ETH_RSS_HASH_TOP)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (rxfh->rss_context)
|
||||
return efx_ethtool_set_rxfh_context(net_dev, rxfh, extack);
|
||||
/* Custom contexts should use new API */
|
||||
if (WARN_ON_ONCE(rxfh->rss_context))
|
||||
return -EIO;
|
||||
|
||||
if (!indir && !key)
|
||||
return 0;
|
||||
|
@ -49,6 +49,18 @@ int efx_ethtool_get_rxfh(struct net_device *net_dev,
|
||||
int efx_ethtool_set_rxfh(struct net_device *net_dev,
|
||||
struct ethtool_rxfh_param *rxfh,
|
||||
struct netlink_ext_ack *extack);
|
||||
int efx_ethtool_create_rxfh_context(struct net_device *net_dev,
|
||||
struct ethtool_rxfh_context *ctx,
|
||||
const struct ethtool_rxfh_param *rxfh,
|
||||
struct netlink_ext_ack *extack);
|
||||
int efx_ethtool_modify_rxfh_context(struct net_device *net_dev,
|
||||
struct ethtool_rxfh_context *ctx,
|
||||
const struct ethtool_rxfh_param *rxfh,
|
||||
struct netlink_ext_ack *extack);
|
||||
int efx_ethtool_remove_rxfh_context(struct net_device *net_dev,
|
||||
struct ethtool_rxfh_context *ctx,
|
||||
u32 rss_context,
|
||||
struct netlink_ext_ack *extack);
|
||||
int efx_ethtool_reset(struct net_device *net_dev, u32 *flags);
|
||||
int efx_ethtool_get_module_eeprom(struct net_device *net_dev,
|
||||
struct ethtool_eeprom *ee,
|
||||
|
@ -194,7 +194,7 @@ efx_mcdi_filter_push_prep_set_match_fields(struct efx_nic *efx,
|
||||
static void efx_mcdi_filter_push_prep(struct efx_nic *efx,
|
||||
const struct efx_filter_spec *spec,
|
||||
efx_dword_t *inbuf, u64 handle,
|
||||
struct efx_rss_context *ctx,
|
||||
struct efx_rss_context_priv *ctx,
|
||||
bool replacing)
|
||||
{
|
||||
u32 flags = spec->flags;
|
||||
@ -245,7 +245,7 @@ static void efx_mcdi_filter_push_prep(struct efx_nic *efx,
|
||||
|
||||
static int efx_mcdi_filter_push(struct efx_nic *efx,
|
||||
const struct efx_filter_spec *spec, u64 *handle,
|
||||
struct efx_rss_context *ctx, bool replacing)
|
||||
struct efx_rss_context_priv *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);
|
||||
@ -345,9 +345,9 @@ static s32 efx_mcdi_filter_insert_locked(struct efx_nic *efx,
|
||||
bool replace_equal)
|
||||
{
|
||||
DECLARE_BITMAP(mc_rem_map, EFX_EF10_FILTER_SEARCH_LIMIT);
|
||||
struct efx_rss_context_priv *ctx = NULL;
|
||||
struct efx_mcdi_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;
|
||||
@ -380,12 +380,12 @@ static s32 efx_mcdi_filter_insert_locked(struct efx_nic *efx,
|
||||
bitmap_zero(mc_rem_map, EFX_EF10_FILTER_SEARCH_LIMIT);
|
||||
|
||||
if (spec->flags & EFX_FILTER_FLAG_RX_RSS) {
|
||||
mutex_lock(&efx->rss_lock);
|
||||
mutex_lock(&efx->net_dev->ethtool->rss_lock);
|
||||
rss_locked = true;
|
||||
if (spec->rss_context)
|
||||
ctx = efx_find_rss_context_entry(efx, spec->rss_context);
|
||||
else
|
||||
ctx = &efx->rss_context;
|
||||
ctx = &efx->rss_context.priv;
|
||||
if (!ctx) {
|
||||
rc = -ENOENT;
|
||||
goto out_unlock;
|
||||
@ -548,7 +548,7 @@ static s32 efx_mcdi_filter_insert_locked(struct efx_nic *efx,
|
||||
|
||||
out_unlock:
|
||||
if (rss_locked)
|
||||
mutex_unlock(&efx->rss_lock);
|
||||
mutex_unlock(&efx->net_dev->ethtool->rss_lock);
|
||||
up_write(&table->lock);
|
||||
return rc;
|
||||
}
|
||||
@ -611,13 +611,13 @@ static int efx_mcdi_filter_remove_internal(struct efx_nic *efx,
|
||||
|
||||
new_spec.priority = EFX_FILTER_PRI_AUTO;
|
||||
new_spec.flags = (EFX_FILTER_FLAG_RX |
|
||||
(efx_rss_active(&efx->rss_context) ?
|
||||
(efx_rss_active(&efx->rss_context.priv) ?
|
||||
EFX_FILTER_FLAG_RX_RSS : 0));
|
||||
new_spec.dmaq_id = 0;
|
||||
new_spec.rss_context = 0;
|
||||
rc = efx_mcdi_filter_push(efx, &new_spec,
|
||||
&table->entry[filter_idx].handle,
|
||||
&efx->rss_context,
|
||||
&efx->rss_context.priv,
|
||||
true);
|
||||
|
||||
if (rc == 0)
|
||||
@ -764,7 +764,7 @@ static int efx_mcdi_filter_insert_addr_list(struct efx_nic *efx,
|
||||
ids = vlan->uc;
|
||||
}
|
||||
|
||||
filter_flags = efx_rss_active(&efx->rss_context) ? EFX_FILTER_FLAG_RX_RSS : 0;
|
||||
filter_flags = efx_rss_active(&efx->rss_context.priv) ? EFX_FILTER_FLAG_RX_RSS : 0;
|
||||
|
||||
/* Insert/renew filters */
|
||||
for (i = 0; i < addr_count; i++) {
|
||||
@ -833,7 +833,7 @@ static int efx_mcdi_filter_insert_def(struct efx_nic *efx,
|
||||
int rc;
|
||||
u16 *id;
|
||||
|
||||
filter_flags = efx_rss_active(&efx->rss_context) ? EFX_FILTER_FLAG_RX_RSS : 0;
|
||||
filter_flags = efx_rss_active(&efx->rss_context.priv) ? EFX_FILTER_FLAG_RX_RSS : 0;
|
||||
|
||||
efx_filter_init_rx(&spec, EFX_FILTER_PRI_AUTO, filter_flags, 0);
|
||||
|
||||
@ -1375,8 +1375,8 @@ void efx_mcdi_filter_table_restore(struct efx_nic *efx)
|
||||
struct efx_mcdi_filter_table *table = efx->filter_state;
|
||||
unsigned int invalid_filters = 0, failed = 0;
|
||||
struct efx_mcdi_filter_vlan *vlan;
|
||||
struct efx_rss_context_priv *ctx;
|
||||
struct efx_filter_spec *spec;
|
||||
struct efx_rss_context *ctx;
|
||||
unsigned int filter_idx;
|
||||
u32 mcdi_flags;
|
||||
int match_pri;
|
||||
@ -1388,7 +1388,7 @@ void efx_mcdi_filter_table_restore(struct efx_nic *efx)
|
||||
return;
|
||||
|
||||
down_write(&table->lock);
|
||||
mutex_lock(&efx->rss_lock);
|
||||
mutex_lock(&efx->net_dev->ethtool->rss_lock);
|
||||
|
||||
for (filter_idx = 0; filter_idx < EFX_MCDI_FILTER_TBL_ROWS; filter_idx++) {
|
||||
spec = efx_mcdi_filter_entry_spec(table, filter_idx);
|
||||
@ -1407,7 +1407,7 @@ void efx_mcdi_filter_table_restore(struct efx_nic *efx)
|
||||
if (spec->rss_context)
|
||||
ctx = efx_find_rss_context_entry(efx, spec->rss_context);
|
||||
else
|
||||
ctx = &efx->rss_context;
|
||||
ctx = &efx->rss_context.priv;
|
||||
if (spec->flags & EFX_FILTER_FLAG_RX_RSS) {
|
||||
if (!ctx) {
|
||||
netif_warn(efx, drv, efx->net_dev,
|
||||
@ -1444,7 +1444,7 @@ not_restored:
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&efx->rss_lock);
|
||||
mutex_unlock(&efx->net_dev->ethtool->rss_lock);
|
||||
up_write(&table->lock);
|
||||
|
||||
/*
|
||||
@ -1861,7 +1861,8 @@ out_unlock:
|
||||
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)
|
||||
|
||||
int efx_mcdi_get_rss_context_flags(struct efx_nic *efx, u32 context, u32 *flags)
|
||||
static int efx_mcdi_get_rss_context_flags(struct efx_nic *efx, u32 context,
|
||||
u32 *flags)
|
||||
{
|
||||
/*
|
||||
* Firmware had a bug (sfc bug 61952) where it would not actually
|
||||
@ -1909,8 +1910,8 @@ int efx_mcdi_get_rss_context_flags(struct efx_nic *efx, u32 context, u32 *flags)
|
||||
* 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).
|
||||
*/
|
||||
void efx_mcdi_set_rss_context_flags(struct efx_nic *efx,
|
||||
struct efx_rss_context *ctx)
|
||||
static void efx_mcdi_set_rss_context_flags(struct efx_nic *efx,
|
||||
struct efx_rss_context_priv *ctx)
|
||||
{
|
||||
MCDI_DECLARE_BUF(inbuf, MC_CMD_RSS_CONTEXT_SET_FLAGS_IN_LEN);
|
||||
u32 flags;
|
||||
@ -1931,7 +1932,7 @@ void efx_mcdi_set_rss_context_flags(struct efx_nic *efx,
|
||||
}
|
||||
|
||||
static int efx_mcdi_filter_alloc_rss_context(struct efx_nic *efx, bool exclusive,
|
||||
struct efx_rss_context *ctx,
|
||||
struct efx_rss_context_priv *ctx,
|
||||
unsigned *context_size)
|
||||
{
|
||||
MCDI_DECLARE_BUF(inbuf, MC_CMD_RSS_CONTEXT_ALLOC_IN_LEN);
|
||||
@ -2032,25 +2033,26 @@ void efx_mcdi_rx_free_indir_table(struct efx_nic *efx)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (efx->rss_context.context_id != EFX_MCDI_RSS_CONTEXT_INVALID) {
|
||||
rc = efx_mcdi_filter_free_rss_context(efx, efx->rss_context.context_id);
|
||||
if (efx->rss_context.priv.context_id != EFX_MCDI_RSS_CONTEXT_INVALID) {
|
||||
rc = efx_mcdi_filter_free_rss_context(efx, efx->rss_context.priv.context_id);
|
||||
WARN_ON(rc != 0);
|
||||
}
|
||||
efx->rss_context.context_id = EFX_MCDI_RSS_CONTEXT_INVALID;
|
||||
efx->rss_context.priv.context_id = EFX_MCDI_RSS_CONTEXT_INVALID;
|
||||
}
|
||||
|
||||
static int efx_mcdi_filter_rx_push_shared_rss_config(struct efx_nic *efx,
|
||||
unsigned *context_size)
|
||||
{
|
||||
struct efx_mcdi_filter_table *table = efx->filter_state;
|
||||
int rc = efx_mcdi_filter_alloc_rss_context(efx, false, &efx->rss_context,
|
||||
context_size);
|
||||
int rc = efx_mcdi_filter_alloc_rss_context(efx, false,
|
||||
&efx->rss_context.priv,
|
||||
context_size);
|
||||
|
||||
if (rc != 0)
|
||||
return rc;
|
||||
|
||||
table->rx_rss_context_exclusive = false;
|
||||
efx_set_default_rx_indir_table(efx, &efx->rss_context);
|
||||
efx_set_default_rx_indir_table(efx, efx->rss_context.rx_indir_table);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -2058,26 +2060,27 @@ static int efx_mcdi_filter_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.priv.context_id;
|
||||
struct efx_mcdi_filter_table *table = efx->filter_state;
|
||||
u32 old_rx_rss_context = efx->rss_context.context_id;
|
||||
int rc;
|
||||
|
||||
if (efx->rss_context.context_id == EFX_MCDI_RSS_CONTEXT_INVALID ||
|
||||
if (efx->rss_context.priv.context_id == EFX_MCDI_RSS_CONTEXT_INVALID ||
|
||||
!table->rx_rss_context_exclusive) {
|
||||
rc = efx_mcdi_filter_alloc_rss_context(efx, true, &efx->rss_context,
|
||||
NULL);
|
||||
rc = efx_mcdi_filter_alloc_rss_context(efx, true,
|
||||
&efx->rss_context.priv,
|
||||
NULL);
|
||||
if (rc == -EOPNOTSUPP)
|
||||
return rc;
|
||||
else if (rc != 0)
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
rc = efx_mcdi_filter_populate_rss_table(efx, efx->rss_context.context_id,
|
||||
rx_indir_table, key);
|
||||
rc = efx_mcdi_filter_populate_rss_table(efx, efx->rss_context.priv.context_id,
|
||||
rx_indir_table, key);
|
||||
if (rc != 0)
|
||||
goto fail2;
|
||||
|
||||
if (efx->rss_context.context_id != old_rx_rss_context &&
|
||||
if (efx->rss_context.priv.context_id != old_rx_rss_context &&
|
||||
old_rx_rss_context != EFX_MCDI_RSS_CONTEXT_INVALID)
|
||||
WARN_ON(efx_mcdi_filter_free_rss_context(efx, old_rx_rss_context) != 0);
|
||||
table->rx_rss_context_exclusive = true;
|
||||
@ -2091,9 +2094,9 @@ static int efx_mcdi_filter_rx_push_exclusive_rss_config(struct efx_nic *efx,
|
||||
return 0;
|
||||
|
||||
fail2:
|
||||
if (old_rx_rss_context != efx->rss_context.context_id) {
|
||||
WARN_ON(efx_mcdi_filter_free_rss_context(efx, efx->rss_context.context_id) != 0);
|
||||
efx->rss_context.context_id = old_rx_rss_context;
|
||||
if (old_rx_rss_context != efx->rss_context.priv.context_id) {
|
||||
WARN_ON(efx_mcdi_filter_free_rss_context(efx, efx->rss_context.priv.context_id) != 0);
|
||||
efx->rss_context.priv.context_id = old_rx_rss_context;
|
||||
}
|
||||
fail1:
|
||||
netif_err(efx, hw, efx->net_dev, "%s: failed rc=%d\n", __func__, rc);
|
||||
@ -2101,33 +2104,28 @@ fail1:
|
||||
}
|
||||
|
||||
int efx_mcdi_rx_push_rss_context_config(struct efx_nic *efx,
|
||||
struct efx_rss_context *ctx,
|
||||
struct efx_rss_context_priv *ctx,
|
||||
const u32 *rx_indir_table,
|
||||
const u8 *key)
|
||||
const u8 *key, bool delete)
|
||||
{
|
||||
int rc;
|
||||
|
||||
WARN_ON(!mutex_is_locked(&efx->rss_lock));
|
||||
WARN_ON(!mutex_is_locked(&efx->net_dev->ethtool->rss_lock));
|
||||
|
||||
if (ctx->context_id == EFX_MCDI_RSS_CONTEXT_INVALID) {
|
||||
if (delete)
|
||||
/* already wasn't in HW, nothing to do */
|
||||
return 0;
|
||||
rc = efx_mcdi_filter_alloc_rss_context(efx, true, ctx, NULL);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (!rx_indir_table) /* Delete this context */
|
||||
if (delete) /* Delete this context */
|
||||
return efx_mcdi_filter_free_rss_context(efx, ctx->context_id);
|
||||
|
||||
rc = efx_mcdi_filter_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;
|
||||
return efx_mcdi_filter_populate_rss_table(efx, ctx->context_id,
|
||||
rx_indir_table, key);
|
||||
}
|
||||
|
||||
int efx_mcdi_rx_pull_rss_context_config(struct efx_nic *efx,
|
||||
@ -2139,16 +2137,16 @@ int efx_mcdi_rx_pull_rss_context_config(struct efx_nic *efx,
|
||||
size_t outlen;
|
||||
int rc, i;
|
||||
|
||||
WARN_ON(!mutex_is_locked(&efx->rss_lock));
|
||||
WARN_ON(!mutex_is_locked(&efx->net_dev->ethtool->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)
|
||||
if (ctx->priv.context_id == EFX_MCDI_RSS_CONTEXT_INVALID)
|
||||
return -ENOENT;
|
||||
|
||||
MCDI_SET_DWORD(inbuf, RSS_CONTEXT_GET_TABLE_IN_RSS_CONTEXT_ID,
|
||||
ctx->context_id);
|
||||
ctx->priv.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),
|
||||
@ -2164,7 +2162,7 @@ int efx_mcdi_rx_pull_rss_context_config(struct efx_nic *efx,
|
||||
RSS_CONTEXT_GET_TABLE_OUT_INDIRECTION_TABLE)[i];
|
||||
|
||||
MCDI_SET_DWORD(inbuf, RSS_CONTEXT_GET_KEY_IN_RSS_CONTEXT_ID,
|
||||
ctx->context_id);
|
||||
ctx->priv.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),
|
||||
@ -2186,35 +2184,42 @@ int efx_mcdi_rx_pull_rss_config(struct efx_nic *efx)
|
||||
{
|
||||
int rc;
|
||||
|
||||
mutex_lock(&efx->rss_lock);
|
||||
mutex_lock(&efx->net_dev->ethtool->rss_lock);
|
||||
rc = efx_mcdi_rx_pull_rss_context_config(efx, &efx->rss_context);
|
||||
mutex_unlock(&efx->rss_lock);
|
||||
mutex_unlock(&efx->net_dev->ethtool->rss_lock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
void efx_mcdi_rx_restore_rss_contexts(struct efx_nic *efx)
|
||||
{
|
||||
struct efx_mcdi_filter_table *table = efx->filter_state;
|
||||
struct efx_rss_context *ctx;
|
||||
struct ethtool_rxfh_context *ctx;
|
||||
unsigned long context;
|
||||
int rc;
|
||||
|
||||
WARN_ON(!mutex_is_locked(&efx->rss_lock));
|
||||
WARN_ON(!mutex_is_locked(&efx->net_dev->ethtool->rss_lock));
|
||||
|
||||
if (!table->must_restore_rss_contexts)
|
||||
return;
|
||||
|
||||
list_for_each_entry(ctx, &efx->rss_context.list, list) {
|
||||
xa_for_each(&efx->net_dev->ethtool->rss_ctx, context, ctx) {
|
||||
struct efx_rss_context_priv *priv;
|
||||
u32 *indir;
|
||||
u8 *key;
|
||||
|
||||
priv = ethtool_rxfh_context_priv(ctx);
|
||||
/* previous NIC RSS context is gone */
|
||||
ctx->context_id = EFX_MCDI_RSS_CONTEXT_INVALID;
|
||||
priv->context_id = EFX_MCDI_RSS_CONTEXT_INVALID;
|
||||
/* so try to allocate a new one */
|
||||
rc = efx_mcdi_rx_push_rss_context_config(efx, ctx,
|
||||
ctx->rx_indir_table,
|
||||
ctx->rx_hash_key);
|
||||
indir = ethtool_rxfh_context_indir(ctx);
|
||||
key = ethtool_rxfh_context_key(ctx);
|
||||
rc = efx_mcdi_rx_push_rss_context_config(efx, priv, indir, key,
|
||||
false);
|
||||
if (rc)
|
||||
netif_warn(efx, probe, efx->net_dev,
|
||||
"failed to restore RSS context %u, rc=%d"
|
||||
"failed to restore RSS context %lu, rc=%d"
|
||||
"; RSS filters may fail to be applied\n",
|
||||
ctx->user_id, rc);
|
||||
context, rc);
|
||||
}
|
||||
table->must_restore_rss_contexts = false;
|
||||
}
|
||||
@ -2276,7 +2281,7 @@ int efx_mcdi_vf_rx_push_rss_config(struct efx_nic *efx, bool user,
|
||||
{
|
||||
if (user)
|
||||
return -EOPNOTSUPP;
|
||||
if (efx->rss_context.context_id != EFX_MCDI_RSS_CONTEXT_INVALID)
|
||||
if (efx->rss_context.priv.context_id != EFX_MCDI_RSS_CONTEXT_INVALID)
|
||||
return 0;
|
||||
return efx_mcdi_filter_rx_push_shared_rss_config(efx, NULL);
|
||||
}
|
||||
@ -2295,7 +2300,7 @@ int efx_mcdi_push_default_indir_table(struct efx_nic *efx,
|
||||
|
||||
efx_mcdi_rx_free_indir_table(efx);
|
||||
if (rss_spread > 1) {
|
||||
efx_set_default_rx_indir_table(efx, &efx->rss_context);
|
||||
efx_set_default_rx_indir_table(efx, efx->rss_context.rx_indir_table);
|
||||
rc = efx->type->rx_push_rss_config(efx, false,
|
||||
efx->rss_context.rx_indir_table, NULL);
|
||||
}
|
||||
|
@ -145,9 +145,9 @@ void efx_mcdi_filter_del_vlan(struct efx_nic *efx, u16 vid);
|
||||
|
||||
void efx_mcdi_rx_free_indir_table(struct efx_nic *efx);
|
||||
int efx_mcdi_rx_push_rss_context_config(struct efx_nic *efx,
|
||||
struct efx_rss_context *ctx,
|
||||
struct efx_rss_context_priv *ctx,
|
||||
const u32 *rx_indir_table,
|
||||
const u8 *key);
|
||||
const u8 *key, bool delete);
|
||||
int efx_mcdi_pf_rx_push_rss_config(struct efx_nic *efx, bool user,
|
||||
const u32 *rx_indir_table,
|
||||
const u8 *key);
|
||||
@ -161,10 +161,6 @@ int efx_mcdi_push_default_indir_table(struct efx_nic *efx,
|
||||
int efx_mcdi_rx_pull_rss_config(struct efx_nic *efx);
|
||||
int efx_mcdi_rx_pull_rss_context_config(struct efx_nic *efx,
|
||||
struct efx_rss_context *ctx);
|
||||
int efx_mcdi_get_rss_context_flags(struct efx_nic *efx, u32 context,
|
||||
u32 *flags);
|
||||
void efx_mcdi_set_rss_context_flags(struct efx_nic *efx,
|
||||
struct efx_rss_context *ctx);
|
||||
void efx_mcdi_rx_restore_rss_contexts(struct efx_nic *efx);
|
||||
|
||||
static inline void efx_mcdi_update_rx_scatter(struct efx_nic *efx)
|
||||
|
@ -737,21 +737,24 @@ struct vfdi_status;
|
||||
/* The reserved RSS context value */
|
||||
#define EFX_MCDI_RSS_CONTEXT_INVALID 0xffffffff
|
||||
/**
|
||||
* struct efx_rss_context - A user-defined RSS context for filtering
|
||||
* @list: node of linked list on which this struct is stored
|
||||
* struct efx_rss_context_priv - driver private data for an RSS context
|
||||
* @context_id: the RSS_CONTEXT_ID returned by MC firmware, or
|
||||
* %EFX_MCDI_RSS_CONTEXT_INVALID if this context is not present on the NIC.
|
||||
* For Siena, 0 if RSS is active, else %EFX_MCDI_RSS_CONTEXT_INVALID.
|
||||
* @user_id: the rss_context ID exposed to userspace over ethtool.
|
||||
* @rx_hash_udp_4tuple: UDP 4-tuple hashing enabled
|
||||
*/
|
||||
struct efx_rss_context_priv {
|
||||
u32 context_id;
|
||||
bool rx_hash_udp_4tuple;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct efx_rss_context - an RSS context
|
||||
* @priv: hardware-specific state
|
||||
* @rx_hash_key: Toeplitz hash key for this RSS context
|
||||
* @indir_table: Indirection table for this RSS context
|
||||
*/
|
||||
struct efx_rss_context {
|
||||
struct list_head list;
|
||||
u32 context_id;
|
||||
u32 user_id;
|
||||
bool rx_hash_udp_4tuple;
|
||||
struct efx_rss_context_priv priv;
|
||||
u8 rx_hash_key[40];
|
||||
u32 rx_indir_table[128];
|
||||
};
|
||||
@ -883,9 +886,7 @@ struct efx_mae;
|
||||
* @rx_packet_ts_offset: Offset of timestamp from start of packet data
|
||||
* (valid only if channel->sync_timestamps_enabled; always negative)
|
||||
* @rx_scatter: Scatter mode enabled for receives
|
||||
* @rss_context: Main RSS context. Its @list member is the head of the list of
|
||||
* RSS contexts created by user requests
|
||||
* @rss_lock: Protects custom RSS context software state in @rss_context.list
|
||||
* @rss_context: Main RSS context.
|
||||
* @vport_id: The function's vport ID, only relevant for PFs
|
||||
* @int_error_count: Number of internal errors seen recently
|
||||
* @int_error_expire: Time at which error count will be expired
|
||||
@ -1052,7 +1053,6 @@ struct efx_nic {
|
||||
int rx_packet_ts_offset;
|
||||
bool rx_scatter;
|
||||
struct efx_rss_context rss_context;
|
||||
struct mutex rss_lock;
|
||||
u32 vport_id;
|
||||
|
||||
unsigned int_error_count;
|
||||
@ -1416,9 +1416,9 @@ struct efx_nic_type {
|
||||
const u32 *rx_indir_table, const u8 *key);
|
||||
int (*rx_pull_rss_config)(struct efx_nic *efx);
|
||||
int (*rx_push_rss_context_config)(struct efx_nic *efx,
|
||||
struct efx_rss_context *ctx,
|
||||
struct efx_rss_context_priv *ctx,
|
||||
const u32 *rx_indir_table,
|
||||
const u8 *key);
|
||||
const u8 *key, bool delete);
|
||||
int (*rx_pull_rss_context_config)(struct efx_nic *efx,
|
||||
struct efx_rss_context *ctx);
|
||||
void (*rx_restore_rss_contexts)(struct efx_nic *efx);
|
||||
|
@ -557,69 +557,25 @@ efx_rx_packet_gro(struct efx_channel *channel, struct efx_rx_buffer *rx_buf,
|
||||
napi_gro_frags(napi);
|
||||
}
|
||||
|
||||
/* 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 efx_nic *efx)
|
||||
struct efx_rss_context_priv *efx_find_rss_context_entry(struct efx_nic *efx,
|
||||
u32 id)
|
||||
{
|
||||
struct list_head *head = &efx->rss_context.list;
|
||||
struct efx_rss_context *ctx, *new;
|
||||
u32 id = 1; /* Don't use zero, that refers to the master RSS context */
|
||||
struct ethtool_rxfh_context *ctx;
|
||||
|
||||
WARN_ON(!mutex_is_locked(&efx->rss_lock));
|
||||
WARN_ON(!mutex_is_locked(&efx->net_dev->ethtool->rss_lock));
|
||||
|
||||
/* 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(*new), GFP_KERNEL);
|
||||
if (!new)
|
||||
ctx = xa_load(&efx->net_dev->ethtool->rss_ctx, id);
|
||||
if (!ctx)
|
||||
return NULL;
|
||||
new->context_id = EFX_MCDI_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;
|
||||
return ethtool_rxfh_context_priv(ctx);
|
||||
}
|
||||
|
||||
struct efx_rss_context *efx_find_rss_context_entry(struct efx_nic *efx, u32 id)
|
||||
{
|
||||
struct list_head *head = &efx->rss_context.list;
|
||||
struct efx_rss_context *ctx;
|
||||
|
||||
WARN_ON(!mutex_is_locked(&efx->rss_lock));
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void efx_set_default_rx_indir_table(struct efx_nic *efx,
|
||||
struct efx_rss_context *ctx)
|
||||
void efx_set_default_rx_indir_table(struct efx_nic *efx, u32 *indir)
|
||||
{
|
||||
size_t 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);
|
||||
for (i = 0; i < ARRAY_SIZE(efx->rss_context.rx_indir_table); i++)
|
||||
indir[i] = ethtool_rxfh_indir_default(i, efx->rss_spread);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -84,11 +84,9 @@ void
|
||||
efx_rx_packet_gro(struct efx_channel *channel, struct efx_rx_buffer *rx_buf,
|
||||
unsigned int n_frags, u8 *eh, __wsum csum);
|
||||
|
||||
struct efx_rss_context *efx_alloc_rss_context_entry(struct efx_nic *efx);
|
||||
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_set_default_rx_indir_table(struct efx_nic *efx,
|
||||
struct efx_rss_context *ctx);
|
||||
struct efx_rss_context_priv *efx_find_rss_context_entry(struct efx_nic *efx,
|
||||
u32 id);
|
||||
void efx_set_default_rx_indir_table(struct efx_nic *efx, u32 *indir);
|
||||
|
||||
bool efx_filter_is_mc_recipient(const struct efx_filter_spec *spec);
|
||||
bool efx_filter_spec_equal(const struct efx_filter_spec *left,
|
||||
|
@ -37,9 +37,9 @@ static int ngbe_set_wol(struct net_device *netdev,
|
||||
wx->wol = 0;
|
||||
if (wol->wolopts & WAKE_MAGIC)
|
||||
wx->wol = WX_PSR_WKUP_CTL_MAG;
|
||||
netdev->wol_enabled = !!(wx->wol);
|
||||
netdev->ethtool->wol_enabled = !!(wx->wol);
|
||||
wr32(wx, WX_PSR_WKUP_CTL, wx->wol);
|
||||
device_set_wakeup_enable(&pdev->dev, netdev->wol_enabled);
|
||||
device_set_wakeup_enable(&pdev->dev, netdev->ethtool->wol_enabled);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -650,7 +650,7 @@ static int ngbe_probe(struct pci_dev *pdev,
|
||||
if (wx->wol_hw_supported)
|
||||
wx->wol = NGBE_PSR_WKUP_CTL_MAG;
|
||||
|
||||
netdev->wol_enabled = !!(wx->wol);
|
||||
netdev->ethtool->wol_enabled = !!(wx->wol);
|
||||
wr32(wx, NGBE_PSR_WKUP_CTL, wx->wol);
|
||||
device_set_wakeup_enable(&pdev->dev, wx->wol);
|
||||
|
||||
|
@ -1309,7 +1309,7 @@ static irqreturn_t phy_interrupt(int irq, void *phy_dat)
|
||||
if (netdev) {
|
||||
struct device *parent = netdev->dev.parent;
|
||||
|
||||
if (netdev->wol_enabled)
|
||||
if (netdev->ethtool->wol_enabled)
|
||||
pm_system_wakeup();
|
||||
else if (device_may_wakeup(&netdev->dev))
|
||||
pm_wakeup_dev_event(&netdev->dev, 0, true);
|
||||
|
@ -296,7 +296,7 @@ static bool mdio_bus_phy_may_suspend(struct phy_device *phydev)
|
||||
if (!netdev)
|
||||
goto out;
|
||||
|
||||
if (netdev->wol_enabled)
|
||||
if (netdev->ethtool->wol_enabled)
|
||||
return false;
|
||||
|
||||
/* As long as not all affected network drivers support the
|
||||
@ -1984,7 +1984,8 @@ int phy_suspend(struct phy_device *phydev)
|
||||
return 0;
|
||||
|
||||
phy_ethtool_get_wol(phydev, &wol);
|
||||
phydev->wol_enabled = wol.wolopts || (netdev && netdev->wol_enabled);
|
||||
phydev->wol_enabled = wol.wolopts ||
|
||||
(netdev && netdev->ethtool->wol_enabled);
|
||||
/* If the device has WOL enabled, we cannot suspend the PHY */
|
||||
if (phydev->wol_enabled && !(phydrv->flags & PHY_ALWAYS_CALL_SUSPEND))
|
||||
return -EBUSY;
|
||||
|
@ -2282,7 +2282,7 @@ void phylink_suspend(struct phylink *pl, bool mac_wol)
|
||||
{
|
||||
ASSERT_RTNL();
|
||||
|
||||
if (mac_wol && (!pl->netdev || pl->netdev->wol_enabled)) {
|
||||
if (mac_wol && (!pl->netdev || pl->netdev->ethtool->wol_enabled)) {
|
||||
/* Wake-on-Lan enabled, MAC handling */
|
||||
mutex_lock(&pl->state_mutex);
|
||||
|
||||
|
@ -159,6 +159,57 @@ static inline u32 ethtool_rxfh_indir_default(u32 index, u32 n_rx_rings)
|
||||
return index % n_rx_rings;
|
||||
}
|
||||
|
||||
/**
|
||||
* struct ethtool_rxfh_context - a custom RSS context configuration
|
||||
* @indir_size: Number of u32 entries in indirection table
|
||||
* @key_size: Size of hash key, in bytes
|
||||
* @priv_size: Size of driver private data, in bytes
|
||||
* @hfunc: RSS hash function identifier. One of the %ETH_RSS_HASH_*
|
||||
* @input_xfrm: Defines how the input data is transformed. Valid values are one
|
||||
* of %RXH_XFRM_*.
|
||||
* @indir_configured: indir has been specified (at create time or subsequently)
|
||||
* @key_configured: hkey has been specified (at create time or subsequently)
|
||||
*/
|
||||
struct ethtool_rxfh_context {
|
||||
u32 indir_size;
|
||||
u32 key_size;
|
||||
u16 priv_size;
|
||||
u8 hfunc;
|
||||
u8 input_xfrm;
|
||||
u8 indir_configured:1;
|
||||
u8 key_configured:1;
|
||||
/* private: driver private data, indirection table, and hash key are
|
||||
* stored sequentially in @data area. Use below helpers to access.
|
||||
*/
|
||||
u8 data[] __aligned(sizeof(void *));
|
||||
};
|
||||
|
||||
static inline void *ethtool_rxfh_context_priv(struct ethtool_rxfh_context *ctx)
|
||||
{
|
||||
return ctx->data;
|
||||
}
|
||||
|
||||
static inline u32 *ethtool_rxfh_context_indir(struct ethtool_rxfh_context *ctx)
|
||||
{
|
||||
return (u32 *)(ctx->data + ALIGN(ctx->priv_size, sizeof(u32)));
|
||||
}
|
||||
|
||||
static inline u8 *ethtool_rxfh_context_key(struct ethtool_rxfh_context *ctx)
|
||||
{
|
||||
return (u8 *)(ethtool_rxfh_context_indir(ctx) + ctx->indir_size);
|
||||
}
|
||||
|
||||
static inline size_t ethtool_rxfh_context_size(u32 indir_size, u32 key_size,
|
||||
u16 priv_size)
|
||||
{
|
||||
size_t indir_bytes = array_size(indir_size, sizeof(u32));
|
||||
size_t flex_len;
|
||||
|
||||
flex_len = size_add(size_add(indir_bytes, key_size),
|
||||
ALIGN(priv_size, sizeof(u32)));
|
||||
return struct_size_t(struct ethtool_rxfh_context, data, flex_len);
|
||||
}
|
||||
|
||||
/* declare a link mode bitmap */
|
||||
#define __ETHTOOL_DECLARE_LINK_MODE_MASK(name) \
|
||||
DECLARE_BITMAP(name, __ETHTOOL_LINK_MODE_MASK_NBITS)
|
||||
@ -670,6 +721,12 @@ struct ethtool_rxfh_param {
|
||||
* contexts.
|
||||
* @cap_rss_sym_xor_supported: indicates if the driver supports symmetric-xor
|
||||
* RSS.
|
||||
* @rxfh_priv_size: size of the driver private data area the core should
|
||||
* allocate for an RSS context (in &struct ethtool_rxfh_context).
|
||||
* @rxfh_max_context_id: maximum (exclusive) supported RSS context ID. If this
|
||||
* is zero then the core may choose any (nonzero) ID, otherwise the core
|
||||
* will only use IDs strictly less than this value, as the @rss_context
|
||||
* argument to @create_rxfh_context and friends.
|
||||
* @supported_coalesce_params: supported types of interrupt coalescing.
|
||||
* @supported_ring_params: supported ring params.
|
||||
* @get_drvinfo: Report driver/device information. Modern drivers no
|
||||
@ -766,6 +823,32 @@ struct ethtool_rxfh_param {
|
||||
* will remain unchanged.
|
||||
* Returns a negative error code or zero. An error code must be returned
|
||||
* if at least one unsupported change was requested.
|
||||
* @create_rxfh_context: Create a new RSS context with the specified RX flow
|
||||
* hash indirection table, hash key, and hash function.
|
||||
* The &struct ethtool_rxfh_context for this context is passed in @ctx;
|
||||
* note that the indir table, hkey and hfunc are not yet populated as
|
||||
* of this call. The driver does not need to update these; the core
|
||||
* will do so if this op succeeds.
|
||||
* However, if @rxfh.indir is set to %NULL, the driver must update the
|
||||
* indir table in @ctx with the (default or inherited) table actually in
|
||||
* use; similarly, if @rxfh.key is %NULL, @rxfh.hfunc is
|
||||
* %ETH_RSS_HASH_NO_CHANGE, or @rxfh.input_xfrm is %RXH_XFRM_NO_CHANGE,
|
||||
* the driver should update the corresponding information in @ctx.
|
||||
* If the driver provides this method, it must also provide
|
||||
* @modify_rxfh_context and @remove_rxfh_context.
|
||||
* Returns a negative error code or zero.
|
||||
* @modify_rxfh_context: Reconfigure the specified RSS context. Allows setting
|
||||
* the contents of the RX flow hash indirection table, hash key, and/or
|
||||
* hash function associated with the given context.
|
||||
* Parameters which are set to %NULL or zero will remain unchanged.
|
||||
* The &struct ethtool_rxfh_context for this context is passed in @ctx;
|
||||
* note that it will still contain the *old* settings. The driver does
|
||||
* not need to update these; the core will do so if this op succeeds.
|
||||
* Returns a negative error code or zero. An error code must be returned
|
||||
* if at least one unsupported change was requested.
|
||||
* @remove_rxfh_context: Remove the specified RSS context.
|
||||
* The &struct ethtool_rxfh_context for this context is passed in @ctx.
|
||||
* Returns a negative error code or zero.
|
||||
* @get_channels: Get number of channels.
|
||||
* @set_channels: Set number of channels. Returns a negative error code or
|
||||
* zero.
|
||||
@ -855,6 +938,8 @@ struct ethtool_ops {
|
||||
u32 cap_link_lanes_supported:1;
|
||||
u32 cap_rss_ctx_supported:1;
|
||||
u32 cap_rss_sym_xor_supported:1;
|
||||
u16 rxfh_priv_size;
|
||||
u32 rxfh_max_context_id;
|
||||
u32 supported_coalesce_params;
|
||||
u32 supported_ring_params;
|
||||
void (*get_drvinfo)(struct net_device *, struct ethtool_drvinfo *);
|
||||
@ -917,6 +1002,18 @@ struct ethtool_ops {
|
||||
int (*get_rxfh)(struct net_device *, struct ethtool_rxfh_param *);
|
||||
int (*set_rxfh)(struct net_device *, struct ethtool_rxfh_param *,
|
||||
struct netlink_ext_ack *extack);
|
||||
int (*create_rxfh_context)(struct net_device *,
|
||||
struct ethtool_rxfh_context *ctx,
|
||||
const struct ethtool_rxfh_param *rxfh,
|
||||
struct netlink_ext_ack *extack);
|
||||
int (*modify_rxfh_context)(struct net_device *,
|
||||
struct ethtool_rxfh_context *ctx,
|
||||
const struct ethtool_rxfh_param *rxfh,
|
||||
struct netlink_ext_ack *extack);
|
||||
int (*remove_rxfh_context)(struct net_device *,
|
||||
struct ethtool_rxfh_context *ctx,
|
||||
u32 rss_context,
|
||||
struct netlink_ext_ack *extack);
|
||||
void (*get_channels)(struct net_device *, struct ethtool_channels *);
|
||||
int (*set_channels)(struct net_device *, struct ethtool_channels *);
|
||||
int (*get_dump_flag)(struct net_device *, struct ethtool_dump *);
|
||||
@ -1004,6 +1101,19 @@ int ethtool_virtdev_set_link_ksettings(struct net_device *dev,
|
||||
const struct ethtool_link_ksettings *cmd,
|
||||
u32 *dev_speed, u8 *dev_duplex);
|
||||
|
||||
/**
|
||||
* struct ethtool_netdev_state - per-netdevice state for ethtool features
|
||||
* @rss_ctx: XArray of custom RSS contexts
|
||||
* @rss_lock: Protects entries in @rss_ctx. May be taken from
|
||||
* within RTNL.
|
||||
* @wol_enabled: Wake-on-LAN is enabled
|
||||
*/
|
||||
struct ethtool_netdev_state {
|
||||
struct xarray rss_ctx;
|
||||
struct mutex rss_lock;
|
||||
unsigned wol_enabled:1;
|
||||
};
|
||||
|
||||
struct phy_device;
|
||||
struct phy_tdr_config;
|
||||
struct phy_plca_cfg;
|
||||
|
@ -80,6 +80,7 @@ struct xdp_buff;
|
||||
struct xdp_frame;
|
||||
struct xdp_metadata_ops;
|
||||
struct xdp_md;
|
||||
struct ethtool_netdev_state;
|
||||
|
||||
typedef u32 xdp_features_t;
|
||||
|
||||
@ -1986,8 +1987,6 @@ enum netdev_reg_state {
|
||||
* switch driver and used to set the phys state of the
|
||||
* switch port.
|
||||
*
|
||||
* @wol_enabled: Wake-on-LAN is enabled
|
||||
*
|
||||
* @threaded: napi threaded mode is enabled
|
||||
*
|
||||
* @module_fw_flash_in_progress: Module firmware flashing is in progress.
|
||||
@ -2001,6 +2000,7 @@ enum netdev_reg_state {
|
||||
* @udp_tunnel_nic_info: static structure describing the UDP tunnel
|
||||
* offload capabilities of the device
|
||||
* @udp_tunnel_nic: UDP tunnel offload state
|
||||
* @ethtool: ethtool related state
|
||||
* @xdp_state: stores info on attached XDP BPF programs
|
||||
*
|
||||
* @nested_level: Used as a parameter of spin_lock_nested() of
|
||||
@ -2375,7 +2375,7 @@ struct net_device {
|
||||
struct lock_class_key *qdisc_tx_busylock;
|
||||
bool proto_down;
|
||||
bool threaded;
|
||||
unsigned wol_enabled:1;
|
||||
|
||||
unsigned module_fw_flash_in_progress:1;
|
||||
struct list_head net_notifier_list;
|
||||
|
||||
@ -2386,6 +2386,8 @@ struct net_device {
|
||||
const struct udp_tunnel_nic_info *udp_tunnel_nic_info;
|
||||
struct udp_tunnel_nic *udp_tunnel_nic;
|
||||
|
||||
struct ethtool_netdev_state *ethtool;
|
||||
|
||||
/* protected by rtnl_lock */
|
||||
struct bpf_xdp_entity xdp_state[__MAX_XDP_MODE];
|
||||
|
||||
|
@ -10336,6 +10336,10 @@ int register_netdevice(struct net_device *dev)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* rss ctx ID 0 is reserved for the default context, start from 1 */
|
||||
xa_init_flags(&dev->ethtool->rss_ctx, XA_FLAGS_ALLOC1);
|
||||
mutex_init(&dev->ethtool->rss_lock);
|
||||
|
||||
spin_lock_init(&dev->addr_list_lock);
|
||||
netdev_set_addr_lockdep_class(dev);
|
||||
|
||||
@ -11116,6 +11120,9 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name,
|
||||
dev->real_num_rx_queues = rxqs;
|
||||
if (netif_alloc_rx_queues(dev))
|
||||
goto free_all;
|
||||
dev->ethtool = kzalloc(sizeof(*dev->ethtool), GFP_KERNEL_ACCOUNT);
|
||||
if (!dev->ethtool)
|
||||
goto free_all;
|
||||
|
||||
strcpy(dev->name, name);
|
||||
dev->name_assign_type = name_assign_type;
|
||||
@ -11166,6 +11173,7 @@ void free_netdev(struct net_device *dev)
|
||||
return;
|
||||
}
|
||||
|
||||
kfree(dev->ethtool);
|
||||
netif_free_tx_queues(dev);
|
||||
netif_free_rx_queues(dev);
|
||||
|
||||
@ -11231,6 +11239,34 @@ void synchronize_net(void)
|
||||
}
|
||||
EXPORT_SYMBOL(synchronize_net);
|
||||
|
||||
static void netdev_rss_contexts_free(struct net_device *dev)
|
||||
{
|
||||
struct ethtool_rxfh_context *ctx;
|
||||
unsigned long context;
|
||||
|
||||
mutex_lock(&dev->ethtool->rss_lock);
|
||||
xa_for_each(&dev->ethtool->rss_ctx, context, ctx) {
|
||||
struct ethtool_rxfh_param rxfh;
|
||||
|
||||
rxfh.indir = ethtool_rxfh_context_indir(ctx);
|
||||
rxfh.key = ethtool_rxfh_context_key(ctx);
|
||||
rxfh.hfunc = ctx->hfunc;
|
||||
rxfh.input_xfrm = ctx->input_xfrm;
|
||||
rxfh.rss_context = context;
|
||||
rxfh.rss_delete = true;
|
||||
|
||||
xa_erase(&dev->ethtool->rss_ctx, context);
|
||||
if (dev->ethtool_ops->create_rxfh_context)
|
||||
dev->ethtool_ops->remove_rxfh_context(dev, ctx,
|
||||
context, NULL);
|
||||
else
|
||||
dev->ethtool_ops->set_rxfh(dev, &rxfh, NULL);
|
||||
kfree(ctx);
|
||||
}
|
||||
xa_destroy(&dev->ethtool->rss_ctx);
|
||||
mutex_unlock(&dev->ethtool->rss_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* unregister_netdevice_queue - remove device from the kernel
|
||||
* @dev: device
|
||||
@ -11334,11 +11370,15 @@ void unregister_netdevice_many_notify(struct list_head *head,
|
||||
netdev_name_node_alt_flush(dev);
|
||||
netdev_name_node_free(dev->name_node);
|
||||
|
||||
netdev_rss_contexts_free(dev);
|
||||
|
||||
call_netdevice_notifiers(NETDEV_PRE_UNINIT, dev);
|
||||
|
||||
if (dev->netdev_ops->ndo_uninit)
|
||||
dev->netdev_ops->ndo_uninit(dev);
|
||||
|
||||
mutex_destroy(&dev->ethtool->rss_lock);
|
||||
|
||||
if (skb)
|
||||
rtmsg_ifinfo_send(skb, dev, GFP_KERNEL, portid, nlh);
|
||||
|
||||
|
@ -1202,6 +1202,7 @@ static noinline_for_stack int ethtool_get_rxfh(struct net_device *dev,
|
||||
const struct ethtool_ops *ops = dev->ethtool_ops;
|
||||
struct ethtool_rxfh_param rxfh_dev = {};
|
||||
u32 user_indir_size, user_key_size;
|
||||
struct ethtool_rxfh_context *ctx;
|
||||
struct ethtool_rxfh rxfh;
|
||||
u32 indir_bytes;
|
||||
u8 *rss_config;
|
||||
@ -1249,11 +1250,26 @@ static noinline_for_stack int ethtool_get_rxfh(struct net_device *dev,
|
||||
if (user_key_size)
|
||||
rxfh_dev.key = rss_config + indir_bytes;
|
||||
|
||||
rxfh_dev.rss_context = rxfh.rss_context;
|
||||
|
||||
ret = dev->ethtool_ops->get_rxfh(dev, &rxfh_dev);
|
||||
if (ret)
|
||||
goto out;
|
||||
if (rxfh.rss_context) {
|
||||
ctx = xa_load(&dev->ethtool->rss_ctx, rxfh.rss_context);
|
||||
if (!ctx) {
|
||||
ret = -ENOENT;
|
||||
goto out;
|
||||
}
|
||||
if (rxfh_dev.indir)
|
||||
memcpy(rxfh_dev.indir, ethtool_rxfh_context_indir(ctx),
|
||||
indir_bytes);
|
||||
if (rxfh_dev.key)
|
||||
memcpy(rxfh_dev.key, ethtool_rxfh_context_key(ctx),
|
||||
user_key_size);
|
||||
rxfh_dev.hfunc = ctx->hfunc;
|
||||
rxfh_dev.input_xfrm = ctx->input_xfrm;
|
||||
ret = 0;
|
||||
} else {
|
||||
ret = dev->ethtool_ops->get_rxfh(dev, &rxfh_dev);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (copy_to_user(useraddr + offsetof(struct ethtool_rxfh, hfunc),
|
||||
&rxfh_dev.hfunc, sizeof(rxfh.hfunc))) {
|
||||
@ -1281,10 +1297,13 @@ static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev,
|
||||
const struct ethtool_ops *ops = dev->ethtool_ops;
|
||||
u32 dev_indir_size = 0, dev_key_size = 0, i;
|
||||
struct ethtool_rxfh_param rxfh_dev = {};
|
||||
struct ethtool_rxfh_context *ctx = NULL;
|
||||
struct netlink_ext_ack *extack = NULL;
|
||||
struct ethtool_rxnfc rx_rings;
|
||||
struct ethtool_rxfh rxfh;
|
||||
bool locked = false; /* dev->ethtool->rss_lock taken */
|
||||
u32 indir_bytes = 0;
|
||||
bool create = false;
|
||||
u8 *rss_config;
|
||||
int ret;
|
||||
|
||||
@ -1312,6 +1331,7 @@ static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev,
|
||||
if ((rxfh.input_xfrm & RXH_XFRM_SYM_XOR) &&
|
||||
!ops->cap_rss_sym_xor_supported)
|
||||
return -EOPNOTSUPP;
|
||||
create = rxfh.rss_context == ETH_RXFH_CONTEXT_ALLOC;
|
||||
|
||||
/* If either indir, hash key or function is valid, proceed further.
|
||||
* Must request at least one change: indir size, hash key, function
|
||||
@ -1377,13 +1397,77 @@ static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev,
|
||||
}
|
||||
}
|
||||
|
||||
if (rxfh.rss_context) {
|
||||
mutex_lock(&dev->ethtool->rss_lock);
|
||||
locked = true;
|
||||
}
|
||||
if (create) {
|
||||
if (rxfh_dev.rss_delete) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
ctx = kzalloc(ethtool_rxfh_context_size(dev_indir_size,
|
||||
dev_key_size,
|
||||
ops->rxfh_priv_size),
|
||||
GFP_KERNEL_ACCOUNT);
|
||||
if (!ctx) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
ctx->indir_size = dev_indir_size;
|
||||
ctx->key_size = dev_key_size;
|
||||
ctx->priv_size = ops->rxfh_priv_size;
|
||||
/* Initialise to an empty context */
|
||||
ctx->hfunc = ETH_RSS_HASH_NO_CHANGE;
|
||||
ctx->input_xfrm = RXH_XFRM_NO_CHANGE;
|
||||
if (ops->create_rxfh_context) {
|
||||
u32 limit = ops->rxfh_max_context_id ?: U32_MAX;
|
||||
u32 ctx_id;
|
||||
|
||||
/* driver uses new API, core allocates ID */
|
||||
ret = xa_alloc(&dev->ethtool->rss_ctx, &ctx_id, ctx,
|
||||
XA_LIMIT(1, limit), GFP_KERNEL_ACCOUNT);
|
||||
if (ret < 0) {
|
||||
kfree(ctx);
|
||||
goto out;
|
||||
}
|
||||
WARN_ON(!ctx_id); /* can't happen */
|
||||
rxfh.rss_context = ctx_id;
|
||||
}
|
||||
} else if (rxfh.rss_context) {
|
||||
ctx = xa_load(&dev->ethtool->rss_ctx, rxfh.rss_context);
|
||||
if (!ctx) {
|
||||
ret = -ENOENT;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
rxfh_dev.hfunc = rxfh.hfunc;
|
||||
rxfh_dev.rss_context = rxfh.rss_context;
|
||||
rxfh_dev.input_xfrm = rxfh.input_xfrm;
|
||||
|
||||
ret = ops->set_rxfh(dev, &rxfh_dev, extack);
|
||||
if (ret)
|
||||
if (rxfh.rss_context && ops->create_rxfh_context) {
|
||||
if (create)
|
||||
ret = ops->create_rxfh_context(dev, ctx, &rxfh_dev,
|
||||
extack);
|
||||
else if (rxfh_dev.rss_delete)
|
||||
ret = ops->remove_rxfh_context(dev, ctx,
|
||||
rxfh.rss_context,
|
||||
extack);
|
||||
else
|
||||
ret = ops->modify_rxfh_context(dev, ctx, &rxfh_dev,
|
||||
extack);
|
||||
} else {
|
||||
ret = ops->set_rxfh(dev, &rxfh_dev, extack);
|
||||
}
|
||||
if (ret) {
|
||||
if (create) {
|
||||
/* failed to create, free our new tracking entry */
|
||||
if (ops->create_rxfh_context)
|
||||
xa_erase(&dev->ethtool->rss_ctx, rxfh.rss_context);
|
||||
kfree(ctx);
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (copy_to_user(useraddr + offsetof(struct ethtool_rxfh, rss_context),
|
||||
&rxfh_dev.rss_context, sizeof(rxfh_dev.rss_context)))
|
||||
@ -1396,8 +1480,44 @@ static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev,
|
||||
else if (rxfh.indir_size != ETH_RXFH_INDIR_NO_CHANGE)
|
||||
dev->priv_flags |= IFF_RXFH_CONFIGURED;
|
||||
}
|
||||
/* Update rss_ctx tracking */
|
||||
if (create && !ops->create_rxfh_context) {
|
||||
/* driver uses old API, it chose context ID */
|
||||
if (WARN_ON(xa_load(&dev->ethtool->rss_ctx, rxfh.rss_context))) {
|
||||
/* context ID reused, our tracking is screwed */
|
||||
kfree(ctx);
|
||||
goto out;
|
||||
}
|
||||
/* Allocate the exact ID the driver gave us */
|
||||
if (xa_is_err(xa_store(&dev->ethtool->rss_ctx, rxfh.rss_context,
|
||||
ctx, GFP_KERNEL))) {
|
||||
kfree(ctx);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
if (rxfh_dev.rss_delete) {
|
||||
WARN_ON(xa_erase(&dev->ethtool->rss_ctx, rxfh.rss_context) != ctx);
|
||||
kfree(ctx);
|
||||
} else if (ctx) {
|
||||
if (rxfh_dev.indir) {
|
||||
for (i = 0; i < dev_indir_size; i++)
|
||||
ethtool_rxfh_context_indir(ctx)[i] = rxfh_dev.indir[i];
|
||||
ctx->indir_configured = 1;
|
||||
}
|
||||
if (rxfh_dev.key) {
|
||||
memcpy(ethtool_rxfh_context_key(ctx), rxfh_dev.key,
|
||||
dev_key_size);
|
||||
ctx->key_configured = 1;
|
||||
}
|
||||
if (rxfh_dev.hfunc != ETH_RSS_HASH_NO_CHANGE)
|
||||
ctx->hfunc = rxfh_dev.hfunc;
|
||||
if (rxfh_dev.input_xfrm != RXH_XFRM_NO_CHANGE)
|
||||
ctx->input_xfrm = rxfh_dev.input_xfrm;
|
||||
}
|
||||
|
||||
out:
|
||||
if (locked)
|
||||
mutex_unlock(&dev->ethtool->rss_lock);
|
||||
kfree(rss_config);
|
||||
return ret;
|
||||
}
|
||||
@ -1509,7 +1629,7 @@ static int ethtool_set_wol(struct net_device *dev, char __user *useraddr)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev->wol_enabled = !!wol.wolopts;
|
||||
dev->ethtool->wol_enabled = !!wol.wolopts;
|
||||
ethtool_notify(dev, ETHTOOL_MSG_WOL_NTF, NULL);
|
||||
|
||||
return 0;
|
||||
|
@ -137,7 +137,7 @@ ethnl_set_wol(struct ethnl_req_info *req_info, struct genl_info *info)
|
||||
ret = dev->ethtool_ops->set_wol(dev, &wol);
|
||||
if (ret)
|
||||
return ret;
|
||||
dev->wol_enabled = !!wol.wolopts;
|
||||
dev->ethtool->wol_enabled = !!wol.wolopts;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user