[net-next, v4 05/10] bnge: add ethtool support to manage RSS contexts
From: Vikas Gupta <vikas.gupta@broadcom.com>
Date: 2026-09-28 06:13:51
Also in:
lkml
Subsystem:
broadcom bng_en 800 gigabit ethernet driver, networking drivers, the rest · Maintainers:
Vikas Gupta, Andrew Lunn, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Linus Torvalds
Introduce ethtool callbacks to manage RSS contexts. Each context allocates a dedicated VNIC with its own RSS indirection table and hash key, configured with TPA settings matching those of the default VNIC. Signed-off-by: Vikas Gupta <vikas.gupta@broadcom.com> Reviewed-by: Bhargava Chenna Marreddy <redacted> Reviewed-by: Dharmender Garg <redacted> --- .../net/ethernet/broadcom/bnge/bnge_ethtool.c | 163 ++++++++++++++++++ .../net/ethernet/broadcom/bnge/bnge_netdev.c | 9 + .../net/ethernet/broadcom/bnge/bnge_resc.c | 26 ++- .../net/ethernet/broadcom/bnge/bnge_vnic.c | 39 ++++- .../net/ethernet/broadcom/bnge/bnge_vnic.h | 14 +- 5 files changed, 241 insertions(+), 10 deletions(-)
diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_ethtool.c b/drivers/net/ethernet/broadcom/bnge/bnge_ethtool.c
index 6fbda4fc1a0c..85dbe64d4c12 100644
--- a/drivers/net/ethernet/broadcom/bnge/bnge_ethtool.c
+++ b/drivers/net/ethernet/broadcom/bnge/bnge_ethtool.c@@ -1009,6 +1009,166 @@ static u32 bnge_get_rx_ring_count(struct net_device *dev) return bd->rx_nr_rings; } +static int bnge_rxfh_context_check(struct bnge_net *bn, + const struct ethtool_rxfh_param *rxfh, + struct netlink_ext_ack *extack) +{ + if (rxfh->hfunc && rxfh->hfunc != ETH_RSS_HASH_TOP) { + NL_SET_ERR_MSG_MOD(extack, "RSS hash function not supported"); + return -EOPNOTSUPP; + } + + if (!(bn->priv_flags & BNGE_NET_EN_NTUPLE)) { + NL_SET_ERR_MSG_MOD(extack, + "Enable ntuple filtering before adding RSS contexts"); + return -EOPNOTSUPP; + } + + if (!netif_running(bn->netdev)) { + NL_SET_ERR_MSG_MOD(extack, "Unable to set RSS contexts when interface is down"); + return -EAGAIN; + } + + return 0; +} + +static int bnge_create_rxfh_context(struct net_device *dev, + struct ethtool_rxfh_context *ctx, + const struct ethtool_rxfh_param *rxfh, + struct netlink_ext_ack *extack) +{ + struct bnge_net *bn = netdev_priv(dev); + struct bnge_rss_ctx *rss_ctx; + struct bnge_vnic_info *vnic; + int rc; + + rc = bnge_rxfh_context_check(bn, rxfh, extack); + if (rc) + return rc; + + if (bn->num_rss_ctx >= BNGE_MAX_ETH_RSS_CTX) { + NL_SET_ERR_MSG_FMT_MOD(extack, "Out of RSS contexts, maximum %u", + BNGE_MAX_ETH_RSS_CTX); + return -EINVAL; + } + + if (!bnge_arfs_capable(bn->bd, true)) { + NL_SET_ERR_MSG_MOD(extack, "Out of hardware resources"); + return -ENOMEM; + } + + rss_ctx = ethtool_rxfh_context_priv(ctx); + + bn->num_rss_ctx++; + + vnic = &rss_ctx->vnic; + + bnge_init_vnic_mem(vnic); + + vnic->rss_ctx = ctx; + vnic->flags |= BNGE_VNIC_RSSCTX_FLAG; + rc = bnge_alloc_vnic_rss_table(bn, vnic); + if (rc) + goto err_del_rss_ctx; + + /* Populate defaults in the context */ + bnge_set_dflt_rss_indir_tbl(bn->bd, ctx); + ctx->hfunc = ETH_RSS_HASH_TOP; + memcpy(vnic->rss_hash_key, bn->rss_hash_key, HW_HASH_KEY_SIZE); + memcpy(ethtool_rxfh_context_key(ctx), + bn->rss_hash_key, HW_HASH_KEY_SIZE); + + rc = bnge_hwrm_vnic_alloc(bn->bd, vnic, bn->bd->rx_nr_rings); + if (rc) { + NL_SET_ERR_MSG_MOD(extack, "Unable to allocate VNIC"); + goto err_del_rss_ctx; + } + + rc = bnge_hwrm_vnic_set_tpa(bn->bd, vnic, + bn->priv_flags & BNGE_NET_EN_TPA); + if (rc) { + NL_SET_ERR_MSG_MOD(extack, + "Unable to set TPA settings to vnic"); + goto err_del_rss_ctx; + } + bnge_modify_rss(bn, ctx, rss_ctx, rxfh); + + rc = bnge_setup_vnic(bn, vnic); + if (rc) { + NL_SET_ERR_MSG_MOD(extack, "Unable to setup vnic"); + goto err_del_rss_ctx; + } + + rss_ctx->index = rxfh->rss_context; + return 0; + +err_del_rss_ctx: + bnge_del_one_rss_ctx(bn, rss_ctx, true); + return rc; +} + +static int bnge_modify_rxfh_context(struct net_device *dev, + struct ethtool_rxfh_context *ctx, + const struct ethtool_rxfh_param *rxfh, + struct netlink_ext_ack *extack) +{ + struct bnge_net *bn = netdev_priv(dev); + u8 old_key[HW_HASH_KEY_SIZE]; + struct bnge_rss_ctx *rss_ctx; + u32 *old_indir = NULL; + u32 tbl_size; + int rc; + + rc = bnge_rxfh_context_check(bn, rxfh, extack); + if (rc) + return rc; + + rss_ctx = ethtool_rxfh_context_priv(ctx); + tbl_size = bnge_get_rxfh_indir_size(bn->bd); + + /* Snapshot the software state so it can be restored if the hardware + * update fails, keeping the reported config consistent with the + * hardware. + */ + if (rxfh->key) + memcpy(old_key, rss_ctx->vnic.rss_hash_key, HW_HASH_KEY_SIZE); + if (rxfh->indir) { + old_indir = kmemdup(ethtool_rxfh_context_indir(ctx), + tbl_size * sizeof(*old_indir), GFP_KERNEL); + if (!old_indir) + return -ENOMEM; + } + + bnge_modify_rss(bn, ctx, rss_ctx, rxfh); + + rc = bnge_hwrm_vnic_rss_cfg(bn, &rss_ctx->vnic); + if (rc) { + if (rxfh->key) + memcpy(rss_ctx->vnic.rss_hash_key, old_key, + HW_HASH_KEY_SIZE); + if (rxfh->indir) + memcpy(ethtool_rxfh_context_indir(ctx), old_indir, + tbl_size * sizeof(*old_indir)); + } + + kfree(old_indir); + return rc; +} + +static int bnge_remove_rxfh_context(struct net_device *dev, + struct ethtool_rxfh_context *ctx, + u32 rss_context, + struct netlink_ext_ack *extack) +{ + struct bnge_net *bn = netdev_priv(dev); + struct bnge_rss_ctx *rss_ctx; + + rss_ctx = ethtool_rxfh_context_priv(ctx); + + bnge_del_one_rss_ctx(bn, rss_ctx, true); + return 0; +} + static const struct ethtool_ops bnge_ethtool_ops = { .cap_link_lanes_supported = 1, .get_link_ksettings = bnge_get_link_ksettings,
@@ -1038,6 +1198,9 @@ static const struct ethtool_ops bnge_ethtool_ops = { .set_rxfh = bnge_set_rxfh, .get_rxfh_fields = bnge_get_rxfh_fields, .set_rxfh_fields = bnge_set_rxfh_fields, + .create_rxfh_context = bnge_create_rxfh_context, + .modify_rxfh_context = bnge_modify_rxfh_context, + .remove_rxfh_context = bnge_remove_rxfh_context, }; void bnge_set_ethtool_ops(struct net_device *dev)
diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_netdev.c b/drivers/net/ethernet/broadcom/bnge/bnge_netdev.c
index ef5d13337e60..ac77ba813f01 100644
--- a/drivers/net/ethernet/broadcom/bnge/bnge_netdev.c
+++ b/drivers/net/ethernet/broadcom/bnge/bnge_netdev.c@@ -2784,6 +2784,8 @@ static int bnge_open_core(struct bnge_net *bn) /* Poll link status and check for SFP+ module status */ bnge_get_port_module_status(bn); + bnge_hwrm_realloc_rss_ctx_vnic(bn); + return 0; err_free_irq:
@@ -3001,7 +3003,10 @@ static void bnge_close_core(struct bnge_net *bn) clear_bit(BNGE_STATE_OPEN, &bd->state); timer_delete_sync(&bn->timer); + + bnge_clear_rss_ctxs(bn); bnge_shutdown_nic(bn); + bnge_disable_napi(bn); /* Save ring stats before shutdown */
@@ -3139,6 +3144,10 @@ static int bnge_set_features(struct net_device *dev, netdev_features_t features) if (flags == bn->priv_flags) return 0; + if ((bn->priv_flags & BNGE_NET_EN_NTUPLE) && + !(flags & BNGE_NET_EN_NTUPLE) && bn->num_rss_ctx) + return -EBUSY; + bn->priv_flags = flags; return 0;
diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_resc.c b/drivers/net/ethernet/broadcom/bnge/bnge_resc.c
index e0744e59c6ae..8f1b0f42773a 100644
--- a/drivers/net/ethernet/broadcom/bnge/bnge_resc.c
+++ b/drivers/net/ethernet/broadcom/bnge/bnge_resc.c@@ -229,6 +229,19 @@ u32 bnge_get_rxfh_indir_size(struct bnge_dev *bd) BNGE_RSS_TABLE_ENTRIES; } +static u16 bnge_get_max_rss_ring(struct bnge_dev *bd) +{ + u32 i, tbl_size, max_ring = 0; + + if (!bd->rss_indir_tbl) + return 0; + + tbl_size = bnge_get_rxfh_indir_size(bd); + for (i = 0; i < tbl_size; i++) + max_ring = max_t(u32, max_ring, bd->rss_indir_tbl[i]); + return max_ring; +} + static void bnge_copy_reserved_rings(struct bnge_dev *bd, struct bnge_hw_rings *hwr) {
@@ -344,9 +357,15 @@ int bnge_reserve_rings(struct bnge_dev *bd) hwr.nq = sh ? max_t(u16, tx_cp, rx_rings) : tx_cp + rx_rings; bd->tx_nr_rings = hwr.tx; - if (rx_rings != bd->rx_nr_rings) + if (rx_rings != bd->rx_nr_rings) { dev_warn(bd->dev, "RX rings resv reduced to %d than earlier %d requested\n", rx_rings, bd->rx_nr_rings); + if (bd->netdev && netif_is_rxfh_configured(bd->netdev) && + (bnge_cal_nr_rss_ctxs(bd->rx_nr_rings) != + bnge_cal_nr_rss_ctxs(rx_rings) || + bnge_get_max_rss_ring(bd) >= rx_rings)) + ethtool_rxfh_indir_lost(bd->netdev); + } bd->rx_nr_rings = rx_rings; bd->nq_nr_rings = hwr.nq;
@@ -354,8 +373,9 @@ int bnge_reserve_rings(struct bnge_dev *bd) if (!bnge_rings_ok(&hwr)) return -ENOMEM; - if (old_rx_rings != bd->hw_resc.resv_rx_rings) - bnge_set_dflt_rss_indir_tbl(bd); + if (old_rx_rings != bd->hw_resc.resv_rx_rings && + (!bd->netdev || !netif_is_rxfh_configured(bd->netdev))) + bnge_set_dflt_rss_indir_tbl(bd, NULL); if (!bnge_aux_registered(bd)) { u16 resv_msix, resv_ctx, aux_ctxs;
diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_vnic.c b/drivers/net/ethernet/broadcom/bnge/bnge_vnic.c
index a44971a7c74d..4ad676e983f2 100644
--- a/drivers/net/ethernet/broadcom/bnge/bnge_vnic.c
+++ b/drivers/net/ethernet/broadcom/bnge/bnge_vnic.c@@ -12,21 +12,26 @@ #include "bnge_filter.h" #include "bnge_resc.h" -void bnge_set_dflt_rss_indir_tbl(struct bnge_dev *bd) +void bnge_set_dflt_rss_indir_tbl(struct bnge_dev *bd, + struct ethtool_rxfh_context *rss_ctx) { u16 max_entries, pad; u32 *rss_indir_tbl; u16 i; max_entries = bnge_get_rxfh_indir_size(bd); - rss_indir_tbl = &bd->rss_indir_tbl[0]; + + if (rss_ctx) + rss_indir_tbl = ethtool_rxfh_context_indir(rss_ctx); + else + rss_indir_tbl = &bd->rss_indir_tbl[0]; for (i = 0; i < max_entries; i++) rss_indir_tbl[i] = ethtool_rxfh_indir_default(i, bd->rx_nr_rings); pad = bd->rss_indir_tbl_entries - max_entries; - if (pad) + if (pad && !rss_ctx) memset(&rss_indir_tbl[i], 0, pad * sizeof(*rss_indir_tbl)); }
@@ -44,9 +49,14 @@ void bnge_fill_hw_rss_tbl(struct bnge_net *bn, struct bnge_vnic_info *vnic) if (vnic->flags & BNGE_VNIC_NTUPLE_FLAG) j = ethtool_rxfh_indir_default(i, bd->rx_nr_rings); + else if (vnic->flags & BNGE_VNIC_RSSCTX_FLAG) + j = ethtool_rxfh_context_indir(vnic->rss_ctx)[i]; else j = bd->rss_indir_tbl[i]; + if (j >= bd->rx_nr_rings) + j = ethtool_rxfh_indir_default(i, bd->rx_nr_rings); + rxr = &bn->rx_ring[j]; ring_id = rxr->rx_ring_struct.fw_ring_id;
@@ -155,7 +165,7 @@ void bnge_modify_rss(struct bnge_net *bn, struct ethtool_rxfh_context *ctx, for (i = 0; i < tbl_size; i++) indir_tbl[i] = rxfh->indir[i]; pad = bd->rss_indir_tbl_entries - tbl_size; - if (pad) + if (pad && !rss_ctx) memset(&indir_tbl[i], 0, pad * sizeof(*indir_tbl)); } }
@@ -168,8 +178,11 @@ void bnge_del_one_rss_ctx(struct bnge_net *bn, struct bnge_rss_ctx *rss_ctx, bnge_hwrm_vnic_free_one(bn->bd, &rss_ctx->vnic); for (i = 0; i < BNGE_MAX_CTX_PER_VNIC; i++) { - if (vnic->fw_rss_cos_lb_ctx[i] != INVALID_HW_RING_ID) + if (vnic->fw_rss_cos_lb_ctx[i] != INVALID_HW_RING_ID) { bnge_hwrm_vnic_ctx_free_one(bn->bd, vnic, i); + vnic->fw_rss_cos_lb_ctx[i] = INVALID_HW_RING_ID; + bn->rsscos_nr_ctxs--; + } } if (!all)
@@ -188,6 +201,10 @@ void bnge_hwrm_realloc_rss_ctx_vnic(struct bnge_net *bn) struct ethtool_rxfh_context *ctx; unsigned long context; + /* Erasing lost contexts touches netdev->ethtool->rss_ctx and calls + * ethtool_rxfh_context_lost(), which requires the ethtool rss_lock. + */ + mutex_lock(&bn->netdev->ethtool->rss_lock); xa_for_each(&bn->netdev->ethtool->rss_ctx, context, ctx) { struct bnge_rss_ctx *rss_ctx = ethtool_rxfh_context_priv(ctx); struct bnge_vnic_info *vnic = &rss_ctx->vnic;
@@ -201,6 +218,7 @@ void bnge_hwrm_realloc_rss_ctx_vnic(struct bnge_net *bn) ethtool_rxfh_context_lost(bn->netdev, rss_ctx->index); } } + mutex_unlock(&bn->netdev->ethtool->rss_lock); } void bnge_clear_rss_ctxs(struct bnge_net *bn)
@@ -242,3 +260,14 @@ int bnge_alloc_vnic_rss_table(struct bnge_net *bn, vnic->rss_hash_key_dma_addr = vnic->rss_table_dma_addr + size; return 0; } + +void bnge_init_vnic_mem(struct bnge_vnic_info *vnic) +{ + int i; + + vnic->fw_vnic_id = INVALID_HW_RING_ID; + vnic->vnic_id = BNGE_VNIC_ID_INVALID; + + for (i = 0; i < BNGE_MAX_CTX_PER_VNIC; i++) + vnic->fw_rss_cos_lb_ctx[i] = INVALID_HW_RING_ID; +}
diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_vnic.h b/drivers/net/ethernet/broadcom/bnge/bnge_vnic.h
index bc2ed911ced3..1cc2481ba217 100644
--- a/drivers/net/ethernet/broadcom/bnge/bnge_vnic.h
+++ b/drivers/net/ethernet/broadcom/bnge/bnge_vnic.h@@ -22,6 +22,11 @@ struct bnge_l2_filter; #define BNGE_MAX_MC_ADDRS 16 #define BNGE_MAX_UC_ADDRS 4 +#define BNGE_VNIC_ID_INVALID 0xffffffff + +struct ethtool_rxfh_context; +struct ethtool_rxfh_param; + enum { BNGE_VNIC_DEFAULT = 0, BNGE_VNIC_NTUPLE = 1
@@ -31,7 +36,8 @@ enum { BNGE_VNIC_RSS_FLAG = BIT(0), BNGE_VNIC_MCAST_FLAG = BIT(1), BNGE_VNIC_UCAST_FLAG = BIT(2), - BNGE_VNIC_NTUPLE_FLAG = BIT(3) + BNGE_VNIC_NTUPLE_FLAG = BIT(3), + BNGE_VNIC_RSSCTX_FLAG = BIT(4) }; struct bnge_vnic_info {
@@ -56,6 +62,8 @@ struct bnge_vnic_info { u32 flags; u32 vnic_id; + + struct ethtool_rxfh_context *rss_ctx; }; struct bnge_rss_ctx {
@@ -67,7 +75,8 @@ void bnge_fill_hw_rss_tbl(struct bnge_net *bn, struct bnge_vnic_info *vnic); int bnge_hwrm_vnic_rss_cfg(struct bnge_net *bn, struct bnge_vnic_info *vnic); int bnge_setup_vnic(struct bnge_net *bn, struct bnge_vnic_info *vnic); -void bnge_set_dflt_rss_indir_tbl(struct bnge_dev *bd); +void bnge_set_dflt_rss_indir_tbl(struct bnge_dev *bd, + struct ethtool_rxfh_context *ctx); int bnge_alloc_rfs_vnic(struct bnge_net *bn); int bnge_alloc_vnic_rss_table(struct bnge_net *bn, struct bnge_vnic_info *vnic);
@@ -79,4 +88,5 @@ void bnge_del_one_rss_ctx(struct bnge_net *bn, struct bnge_rss_ctx *rss_ctx, bool all); void bnge_hwrm_realloc_rss_ctx_vnic(struct bnge_net *bn); void bnge_clear_rss_ctxs(struct bnge_net *bn); +void bnge_init_vnic_mem(struct bnge_vnic_info *vnic); #endif /* _BNGE_VNIC_H_ */
--
2.52.0