From 28a236c54c9ae648fda9cc961e2343c70b8b3b49 Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Wed, 1 Jul 2026 16:57:18 +0300 Subject: bnx2x: use kzalloc() to allocate mac filtering list bnx2x_mcast_enqueue_cmd() allocates memory for mac filtering list using __get_free_pages(). This memory can be allocated with kzalloc() as there's nothing special about it to go directly to the page allocator. kmalloc() provides a better API that does not require ugly casts and kfree() does not need to know the size of the freed object. Performance difference between kmalloc() and __get_free_pages() is not measurable as both allocators take an object/page from a per-CPU list for fast path allocations. For the slow path the performance is anyway determined by the amount of reclaim involved rather than by what allocator is used. Replace use of __get_free_page() with kzalloc() and free_page() with kfree(). Link: https://lore.kernel.org/all/635405e4-9423-4a25-a6e7-e03c8ea0bcbe@redhat.com Signed-off-by: Mike Rapoport (Microsoft) Link: https://patch.msgid.link/20260701-b4-drivers-ethernet-v1-1-58776615db6e@kernel.org Signed-off-by: Paolo Abeni --- drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/net/ethernet/broadcom') diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c index 07a908a2c72f..d560524d317d 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "bnx2x.h" #include "bnx2x_cmn.h" #include "bnx2x_sp.h" @@ -2664,7 +2665,7 @@ static void bnx2x_free_groups(struct list_head *mcast_group_list) struct bnx2x_mcast_elem_group, mcast_group_link); list_del(¤t_mcast_group->mcast_group_link); - free_page((unsigned long)current_mcast_group); + kfree(current_mcast_group); } } @@ -2713,8 +2714,7 @@ static int bnx2x_mcast_enqueue_cmd(struct bnx2x *bp, total_elems = BNX2X_MCAST_BINS_NUM; } while (total_elems > 0) { - elem_group = (struct bnx2x_mcast_elem_group *) - __get_free_page(GFP_ATOMIC | __GFP_ZERO); + elem_group = kzalloc(PAGE_SIZE, GFP_ATOMIC); if (!elem_group) { bnx2x_free_groups(&new_cmd->group_head); kfree(new_cmd); -- cgit v1.2.3 From e354f7d60f14a3eacd5ec7b607346a5612f655ec Mon Sep 17 00:00:00 2001 From: Abdun Nihaal Date: Tue, 7 Jul 2026 11:16:16 +0530 Subject: bnx2x: fix null pointer dereference in bnx2x_free_mem_bp() In one of the error path in bnx2x_alloc_mem_bp(), bnx2x_free_mem_bp() may be called with bp->fp uninitialized. And so, there could be a null pointer dereference in bnx2x_free_mem_bp(). Fix that by initializing the fp_array_size after the bp->fp pointer is correctly initialized. Cc: stable+noautosel@kernel.org # untested fix to unlikely error path Reviewed-by: Maciej Fijalkowski Signed-off-by: Abdun Nihaal Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260707054618.932108-1-nihaal@cse.iitm.ac.in Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/net/ethernet/broadcom') diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c index 5b2640bd31c3..5a9742fd3ddf 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c @@ -4742,13 +4742,13 @@ int bnx2x_alloc_mem_bp(struct bnx2x *bp) /* fp array: RSS plus CNIC related L2 queues */ fp_array_size = BNX2X_MAX_RSS_COUNT(bp) + CNIC_SUPPORT(bp); - bp->fp_array_size = fp_array_size; - BNX2X_DEV_INFO("fp_array_size %d\n", bp->fp_array_size); - - fp = kzalloc_objs(*fp, bp->fp_array_size); + BNX2X_DEV_INFO("fp_array_size %d\n", fp_array_size); + fp = kzalloc_objs(*fp, fp_array_size); if (!fp) goto alloc_err; bp->fp = fp; + bp->fp_array_size = fp_array_size; + for (i = 0; i < bp->fp_array_size; i++) { fp[i].tpa_info = kzalloc_objs(struct bnx2x_agg_info, -- cgit v1.2.3 From d5d7b17b3f9b0fb5eb5a755a9b34d89a39813d50 Mon Sep 17 00:00:00 2001 From: Vikas Gupta Date: Fri, 31 Jul 2026 22:07:10 +0530 Subject: bnge: refactor rx mode helpers to accept explicit address lists Rename bnge_cfg_def_vnic() to bnge_cfg_rx_mode() and update bnge_mc_list_updated() and bnge_uc_list_updated() to accept explicit netdev_hw_addr_list pointers rather than deriving them from the netdev. Add a snapshot parameter to bnge_cfg_rx_mode() to skip netif_addr_lock_bh() when the caller provides a pre-snapshotted list. On the open path (snapshot=false), the live netdev UC list is passed and the addr lock is taken as before. Signed-off-by: Vikas Gupta Reviewed-by: Dharmender Garg Reviewed-by: Rahul Gupta Link: https://patch.msgid.link/20260731163712.3463362-2-vikas.gupta@broadcom.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bnge/bnge_netdev.c | 39 ++++++++++++++---------- 1 file changed, 23 insertions(+), 16 deletions(-) (limited to 'drivers/net/ethernet/broadcom') diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_netdev.c b/drivers/net/ethernet/broadcom/bnge/bnge_netdev.c index 6f7ef506d4e1..db0e616fc46c 100644 --- a/drivers/net/ethernet/broadcom/bnge/bnge_netdev.c +++ b/drivers/net/ethernet/broadcom/bnge/bnge_netdev.c @@ -2144,16 +2144,16 @@ err_del_l2_filter: return rc; } -static bool bnge_mc_list_updated(struct bnge_net *bn, u32 *rx_mask) +static bool bnge_mc_list_updated(struct bnge_net *bn, u32 *rx_mask, + const struct netdev_hw_addr_list *mc) { struct bnge_vnic_info *vnic = &bn->vnic_info[BNGE_VNIC_DEFAULT]; - struct net_device *dev = bn->netdev; struct netdev_hw_addr *ha; int mc_count = 0, off = 0; bool update = false; u8 *haddr; - netdev_for_each_mc_addr(ha, dev) { + netdev_hw_addr_list_for_each(ha, mc) { if (mc_count >= BNGE_MAX_MC_ADDRS) { *rx_mask |= CFA_L2_SET_RX_MASK_REQ_MASK_ALL_MCAST; vnic->mc_list_count = 0; @@ -2177,17 +2177,17 @@ static bool bnge_mc_list_updated(struct bnge_net *bn, u32 *rx_mask) return update; } -static bool bnge_uc_list_updated(struct bnge_net *bn) +static bool bnge_uc_list_updated(struct bnge_net *bn, + const struct netdev_hw_addr_list *uc) { struct bnge_vnic_info *vnic = &bn->vnic_info[BNGE_VNIC_DEFAULT]; - struct net_device *dev = bn->netdev; struct netdev_hw_addr *ha; int off = 0; - if (netdev_uc_count(dev) != (vnic->uc_filter_count - 1)) + if (netdev_hw_addr_list_count(uc) != (vnic->uc_filter_count - 1)) return true; - netdev_for_each_uc_addr(ha, dev) { + netdev_hw_addr_list_for_each(ha, uc) { if (!ether_addr_equal(ha->addr, vnic->uc_list + off)) return true; @@ -2201,7 +2201,8 @@ static bool bnge_promisc_ok(struct bnge_net *bn) return true; } -static int bnge_cfg_def_vnic(struct bnge_net *bn) +static int bnge_cfg_rx_mode(struct bnge_net *bn, struct netdev_hw_addr_list *uc, + bool snapshot) { struct bnge_vnic_info *vnic = &bn->vnic_info[BNGE_VNIC_DEFAULT]; struct net_device *dev = bn->netdev; @@ -2211,7 +2212,7 @@ static int bnge_cfg_def_vnic(struct bnge_net *bn) bool uc_update; netif_addr_lock_bh(dev); - uc_update = bnge_uc_list_updated(bn); + uc_update = bnge_uc_list_updated(bn, uc); netif_addr_unlock_bh(dev); if (!uc_update) @@ -2226,22 +2227,28 @@ static int bnge_cfg_def_vnic(struct bnge_net *bn) vnic->uc_filter_count = 1; - netif_addr_lock_bh(dev); - if (netdev_uc_count(dev) > (BNGE_MAX_UC_ADDRS - 1)) { + if (!snapshot) + netif_addr_lock_bh(dev); + if (netdev_hw_addr_list_count(uc) > (BNGE_MAX_UC_ADDRS - 1)) { vnic->rx_mask |= CFA_L2_SET_RX_MASK_REQ_MASK_PROMISCUOUS; } else { - netdev_for_each_uc_addr(ha, dev) { + netdev_hw_addr_list_for_each(ha, uc) { memcpy(vnic->uc_list + off, ha->addr, ETH_ALEN); off += ETH_ALEN; vnic->uc_filter_count++; } } - netif_addr_unlock_bh(dev); + if (!snapshot) + netif_addr_unlock_bh(dev); for (i = 1, off = 0; i < vnic->uc_filter_count; i++, off += ETH_ALEN) { rc = bnge_hwrm_set_vnic_filter(bn, 0, i, vnic->uc_list + off); if (rc) { - netdev_err(dev, "HWRM vnic filter failure rc: %d\n", rc); + if (rc == -EAGAIN) + netdev_warn(dev, "FW busy while setting vnic filter, will retry\n"); + else + netdev_err(dev, "HWRM vnic filter failure rc: %d\n", + rc); vnic->uc_filter_count = i; return rc; } @@ -2695,11 +2702,11 @@ static int bnge_init_chip(struct bnge_net *bn) } else if (bn->netdev->flags & IFF_MULTICAST) { u32 mask = 0; - bnge_mc_list_updated(bn, &mask); + bnge_mc_list_updated(bn, &mask, &bn->netdev->mc); vnic->rx_mask |= mask; } - rc = bnge_cfg_def_vnic(bn); + rc = bnge_cfg_rx_mode(bn, &bn->netdev->uc, false); if (rc) goto err_out; return 0; -- cgit v1.2.3 From fbe3647fd464e8b4051543c5b16368e877c2d37c Mon Sep 17 00:00:00 2001 From: Vikas Gupta Date: Fri, 31 Jul 2026 22:07:11 +0530 Subject: bnge: add ndo_set_rx_mode_async support Register bnge_set_rx_mode() as ndo_set_rx_mode_async to handle unicast, multicast, broadcast, and promiscuous filter updates via CFA_L2_SET_RX_MASK. The async variant receives pre-snapshotted address lists from the kernel, allowing the driver to issue sleepable HWRM firmware commands without holding the addr lock. Move uc_update detection to the caller so the async path can compute it directly from the snapshotted UC list before calling bnge_cfg_rx_mode(). Handle -EAGAIN from bnge_hwrm_set_vnic_filter() and bnge_hwrm_cfa_l2_set_rx_mask() on the open path by scheduling a retry via netif_rx_mode_schedule_retry() rather than failing the open. Signed-off-by: Vikas Gupta Reviewed-by: Dharmender Garg Reviewed-by: Rahul Gupta Link: https://patch.msgid.link/20260731163712.3463362-3-vikas.gupta@broadcom.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bnge/bnge_netdev.c | 67 ++++++++++++++++++++---- drivers/net/ethernet/broadcom/bnge/bnge_netdev.h | 6 +++ 2 files changed, 62 insertions(+), 11 deletions(-) (limited to 'drivers/net/ethernet/broadcom') diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_netdev.c b/drivers/net/ethernet/broadcom/bnge/bnge_netdev.c index db0e616fc46c..d47eb9bc5b8d 100644 --- a/drivers/net/ethernet/broadcom/bnge/bnge_netdev.c +++ b/drivers/net/ethernet/broadcom/bnge/bnge_netdev.c @@ -2202,18 +2202,13 @@ static bool bnge_promisc_ok(struct bnge_net *bn) } static int bnge_cfg_rx_mode(struct bnge_net *bn, struct netdev_hw_addr_list *uc, - bool snapshot) + bool uc_update, bool snapshot) { struct bnge_vnic_info *vnic = &bn->vnic_info[BNGE_VNIC_DEFAULT]; struct net_device *dev = bn->netdev; struct bnge_dev *bd = bn->bd; struct netdev_hw_addr *ha; int i, off = 0, rc; - bool uc_update; - - netif_addr_lock_bh(dev); - uc_update = bnge_uc_list_updated(bn, uc); - netif_addr_unlock_bh(dev); if (!uc_update) goto skip_uc; @@ -2267,13 +2262,58 @@ skip_uc: vnic->mc_list_count = 0; rc = bnge_hwrm_cfa_l2_set_rx_mask(bd, vnic); } - if (rc) - netdev_err(dev, "HWRM cfa l2 rx mask failure rc: %d\n", - rc); + if (rc) { + if (rc == -EAGAIN) { + netdev_warn(dev, "FW busy while setting l2 rx mask in CFA, will retry\n"); + vnic->rx_mask &= ~BNGE_RX_MASK_CFG_FLAGS; + } else { + netdev_err(dev, "HWRM CFA L2 rx mask failure rc: %d\n", + rc); + } + } return rc; } +static int bnge_set_rx_mode(struct net_device *dev, + struct netdev_hw_addr_list *uc, + struct netdev_hw_addr_list *mc) +{ + struct bnge_net *bn = netdev_priv(dev); + struct bnge_vnic_info *vnic; + bool mc_update = false; + bool uc_update; + u32 mask; + + if (!test_bit(BNGE_STATE_OPEN, &bn->bd->state)) + return 0; + + vnic = &bn->vnic_info[BNGE_VNIC_DEFAULT]; + mask = vnic->rx_mask; + mask &= ~BNGE_RX_MASK_CFG_FLAGS; + + if (dev->flags & IFF_PROMISC) + mask |= CFA_L2_SET_RX_MASK_REQ_MASK_PROMISCUOUS; + + uc_update = bnge_uc_list_updated(bn, uc); + + if (dev->flags & IFF_BROADCAST) + mask |= CFA_L2_SET_RX_MASK_REQ_MASK_BCAST; + if (dev->flags & IFF_ALLMULTI) { + mask |= CFA_L2_SET_RX_MASK_REQ_MASK_ALL_MCAST; + vnic->mc_list_count = 0; + } else if (dev->flags & IFF_MULTICAST) { + mc_update = bnge_mc_list_updated(bn, &mask, mc); + } + + if (mask != vnic->rx_mask || uc_update || mc_update) { + vnic->rx_mask = mask; + return bnge_cfg_rx_mode(bn, uc, uc_update, true); + } + + return 0; +} + static void bnge_disable_int(struct bnge_net *bn) { struct bnge_dev *bd = bn->bd; @@ -2706,9 +2746,13 @@ static int bnge_init_chip(struct bnge_net *bn) vnic->rx_mask |= mask; } - rc = bnge_cfg_rx_mode(bn, &bn->netdev->uc, false); - if (rc) + rc = bnge_cfg_rx_mode(bn, &bn->netdev->uc, true, false); + if (rc == -EAGAIN) { + netif_rx_mode_schedule_retry(bn->netdev); + rc = 0; + } else if (rc) { goto err_out; + } return 0; err_out: @@ -3202,6 +3246,7 @@ static const struct net_device_ops bnge_netdev_ops = { .ndo_stop = bnge_close, .ndo_start_xmit = bnge_start_xmit, .ndo_get_stats64 = bnge_get_stats64, + .ndo_set_rx_mode_async = bnge_set_rx_mode, .ndo_features_check = bnge_features_check, }; diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_netdev.h b/drivers/net/ethernet/broadcom/bnge/bnge_netdev.h index d177919c2e11..476b5bab96fe 100644 --- a/drivers/net/ethernet/broadcom/bnge/bnge_netdev.h +++ b/drivers/net/ethernet/broadcom/bnge/bnge_netdev.h @@ -561,6 +561,12 @@ struct bnge_napi { #define BNGE_VNIC_DEFAULT 0 #define BNGE_MAX_UC_ADDRS 4 +#define BNGE_RX_MASK_CFG_FLAGS \ + (CFA_L2_SET_RX_MASK_REQ_MASK_PROMISCUOUS | \ + CFA_L2_SET_RX_MASK_REQ_MASK_MCAST | \ + CFA_L2_SET_RX_MASK_REQ_MASK_ALL_MCAST | \ + CFA_L2_SET_RX_MASK_REQ_MASK_BCAST) + struct bnge_vnic_info { u16 fw_vnic_id; #define BNGE_MAX_CTX_PER_VNIC 8 -- cgit v1.2.3 From 1b1e855e4306e227b610c5c2c7d475281a912f29 Mon Sep 17 00:00:00 2001 From: Vikas Gupta Date: Fri, 31 Jul 2026 22:07:12 +0530 Subject: bnge: send hwrm for interface down/up transitions Firmware expects HWRM_FUNC_DRV_IF_CHANGE on interface down/up transitions to coordinate resource management. Add bnge_hwrm_if_change() to send this notification. Signed-off-by: Vikas Gupta Reviewed-by: Dharmender Garg Reviewed-by: Rahul Gupta Link: https://patch.msgid.link/20260731163712.3463362-4-vikas.gupta@broadcom.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bnge/bnge_netdev.c | 32 ++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) (limited to 'drivers/net/ethernet/broadcom') diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_netdev.c b/drivers/net/ethernet/broadcom/bnge/bnge_netdev.c index d47eb9bc5b8d..c2e865f8d9c4 100644 --- a/drivers/net/ethernet/broadcom/bnge/bnge_netdev.c +++ b/drivers/net/ethernet/broadcom/bnge/bnge_netdev.c @@ -20,6 +20,7 @@ #include #include "bnge.h" +#include "bnge_hwrm.h" #include "bnge_hwrm_lib.h" #include "bnge_ethtool.h" #include "bnge_rmem.h" @@ -2864,6 +2865,24 @@ static void bnge_tx_enable(struct bnge_net *bn) netif_carrier_on(bn->netdev); } +static int bnge_hwrm_if_change(struct bnge_dev *bd, bool up) +{ + struct hwrm_func_drv_if_change_input *req; + int rc; + + if (!(bd->fw_cap & BNGE_FW_CAP_IF_CHANGE)) + return 0; + + rc = bnge_hwrm_req_init(bd, req, HWRM_FUNC_DRV_IF_CHANGE); + if (rc) + return rc; + + if (up) + req->flags = cpu_to_le32(FUNC_DRV_IF_CHANGE_REQ_FLAGS_UP); + + return bnge_hwrm_req_send(bd, req); +} + static int bnge_open_core(struct bnge_net *bn) { struct bnge_dev *bd = bn->bd; @@ -2871,16 +2890,22 @@ static int bnge_open_core(struct bnge_net *bn) netif_carrier_off(bn->netdev); + rc = bnge_hwrm_if_change(bd, true); + if (rc) { + netdev_err(bn->netdev, "bnge_hwrm_if_change err: %d\n", rc); + return rc; + } + rc = bnge_reserve_rings(bd); if (rc) { netdev_err(bn->netdev, "bnge_reserve_rings err: %d\n", rc); - return rc; + goto err_if_change; } rc = bnge_alloc_core(bn); if (rc) { netdev_err(bn->netdev, "bnge_alloc_core err: %d\n", rc); - return rc; + goto err_if_change; } bnge_init_napi(bn); @@ -2927,6 +2952,8 @@ err_free_irq: err_del_napi: bnge_del_napi(bn); bnge_free_core(bn); +err_if_change: + bnge_hwrm_if_change(bd, false); return rc; } @@ -3157,6 +3184,7 @@ static int bnge_close(struct net_device *dev) bnge_close_core(bn); bnge_hwrm_shutdown_link(bn->bd); + bnge_hwrm_if_change(bn->bd, false); bn->sp_event = 0; return 0; -- cgit v1.2.3 From 5838193edccab7810d5dc51c165a316089272dc6 Mon Sep 17 00:00:00 2001 From: Vadim Fedorenko Date: Thu, 6 Aug 2026 20:18:49 +0000 Subject: bnxt_en: enable PTM function The patch mentioned in Fixes missed one main point of implementing proper PTM support. To make it fully operational it has to be explicitly enabled. Add missing call in probe callback and disable it in teardown callback. Signed-off-by: Vadim Fedorenko Reviewed-by: Pavan Chebbi Link: https://patch.msgid.link/20260806201849.3161402-1-vadim.fedorenko@linux.dev Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/net/ethernet/broadcom') diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 25099077fe4f..8a9c7646b53f 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -14978,6 +14978,7 @@ static void bnxt_unmap_bars(struct bnxt *bp, struct pci_dev *pdev) static void bnxt_cleanup_pci(struct bnxt *bp) { + pci_disable_ptm(bp->pdev); bnxt_unmap_bars(bp, bp->pdev); pci_release_regions(bp->pdev); if (pci_is_enabled(bp->pdev)) @@ -15546,6 +15547,8 @@ static int bnxt_init_board(struct pci_dev *pdev, struct net_device *dev) goto init_err_release; } + pci_enable_ptm(pdev); + INIT_WORK(&bp->sp_task, bnxt_sp_task); INIT_DELAYED_WORK(&bp->fw_reset_task, bnxt_fw_reset_task); -- cgit v1.2.3 From 2b49709667de8d6a5fbf4c4b7585aa08e9db5582 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Thu, 13 Aug 2026 12:32:47 -0700 Subject: eth: bnxt: decrease indent in bnxt_init_int_mode() Handle the IRQ table allocation failure right away instead of wrapping the rest of the function in an if. Purely to make upcoming changes more readable. While refactoring, drop the init of rc which is not necessary. No functional changes. Reviewed-by: Pavan Chebbi Link: https://patch.msgid.link/20260813193248.2578626-2-kuba@kernel.org Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 36 +++++++++++++++---------------- 1 file changed, 18 insertions(+), 18 deletions(-) (limited to 'drivers/net/ethernet/broadcom') diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 1e4944f3e606..d3531cd283a5 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -11592,7 +11592,7 @@ static int bnxt_get_num_msix(struct bnxt *bp) static int bnxt_init_int_mode(struct bnxt *bp) { - int i, total_vecs, max, rc = 0, min = 1, ulp_msix, tx_cp, tbl_size; + int i, total_vecs, max, rc, min = 1, ulp_msix, tx_cp, tbl_size; total_vecs = bnxt_get_num_msix(bp); max = bnxt_get_max_func_irqs(bp); @@ -11617,26 +11617,26 @@ static int bnxt_init_int_mode(struct bnxt *bp) if (pci_msix_can_alloc_dyn(bp->pdev)) tbl_size = max; bp->irq_tbl = kzalloc_objs(*bp->irq_tbl, tbl_size); - if (bp->irq_tbl) { - for (i = 0; i < total_vecs; i++) - bp->irq_tbl[i].vector = pci_irq_vector(bp->pdev, i); - - bp->total_irqs = total_vecs; - /* Trim rings based upon num of vectors allocated */ - rc = bnxt_trim_rings(bp, &bp->rx_nr_rings, &bp->tx_nr_rings, - total_vecs - ulp_msix, min == 1); - if (rc) - goto msix_setup_exit; - - tx_cp = bnxt_num_tx_to_cp(bp, bp->tx_nr_rings); - bp->cp_nr_rings = (min == 1) ? - max_t(int, tx_cp, bp->rx_nr_rings) : - tx_cp + bp->rx_nr_rings; - - } else { + if (!bp->irq_tbl) { rc = -ENOMEM; goto msix_setup_exit; } + + for (i = 0; i < total_vecs; i++) + bp->irq_tbl[i].vector = pci_irq_vector(bp->pdev, i); + + bp->total_irqs = total_vecs; + /* Trim rings based upon num of vectors allocated */ + rc = bnxt_trim_rings(bp, &bp->rx_nr_rings, &bp->tx_nr_rings, + total_vecs - ulp_msix, min == 1); + if (rc) + goto msix_setup_exit; + + tx_cp = bnxt_num_tx_to_cp(bp, bp->tx_nr_rings); + bp->cp_nr_rings = (min == 1) ? + max_t(int, tx_cp, bp->rx_nr_rings) : + tx_cp + bp->rx_nr_rings; + return 0; msix_setup_exit: -- cgit v1.2.3 From fb05026490430d6370506f50b8dbab0e1da79c4f Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Thu, 13 Aug 2026 12:32:48 -0700 Subject: eth: bnxt: preserve IRQ affinity across IRQ reallocation Reconfiguring the rings frees the MSI-X vectors and allocates them again. The IRQ descriptors go away with them, so the affinity user space set is silently replaced by the driver's default NUMA spread. This is painful to deal with for user space as seemingly arbitrary NIC configuration changes lead to loss of configuration. In NIPA (netdev CI) this results in the toeplitz test reporting: Exception| net.lib.py.ksft.KsftFailEx: IRQ170 is not mapped to a single core: 0-31 if the test run after another test which reconfigured the device. We configure the IRQ mapping at boot, but if the driver is not preserving the config - it gets lost. Record the affinity in the notifier and apply it when the IRQs are requested again. The notifier has to be registered unconditionally now, so far it was only installed when TPH was enabled. Drivers which let the core manage the affinity (idpf, ice, iavf via netif_set_affinity_auto()) work exactly like this, napi_restore_config() reapplies napi_config.affinity_mask on every napi_enable(). Note that the affinity is supposed to follow the NAPI / queue, same as the napi_config behavior in drivers mentioned above. If the user changes the affinity when the device is down - we will override it on up. That's expected, the IRQs are not associated with queues when device is down (no name, no entry in /proc/interrupts, no entry in netdev netlink). map_idx is ulp_msix + i, so the slot shifts whenever RoCE takes or releases vectors and the mask would end up on a different ring. Key using the completion ring id, which maps to the NAPI instance. Note2: this restores the side effect fcf42409c6e1 ("bnxt_en: use irq_update_affinity_hint()") removed, but not the problem it was fixing. The complaint there was that reopening the device resets the affinity and can move an IRQ onto a CPU irqbalance was told to stay away from. We now replay what user space or irqbalance last asked for, the driver's own placement is only used for a ring nobody has configured. Note3: the combined irq_set_affinity_and_hint() looks like it may hide the failure from __irq_set_affinity(), but let's assume the IRQ maintainers know what their doing - either this can't happen or is intentional. Reviewed-by: Pavan Chebbi Link: https://patch.msgid.link/20260813193248.2578626-3-kuba@kernel.org Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 95 +++++++++++++++++++++++-------- drivers/net/ethernet/broadcom/bnxt/bnxt.h | 11 +++- 2 files changed, 80 insertions(+), 26 deletions(-) (limited to 'drivers/net/ethernet/broadcom') diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index d3531cd283a5..35795374a8dd 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -11792,6 +11792,9 @@ static void bnxt_irq_affinity_notify(struct irq_affinity_notify *notify, irq = container_of(notify, struct bnxt_irq, affinity_notify); + cpumask_copy(irq->bp->ring_cpu_mask[irq->ring_nr], mask); + set_bit(irq->ring_nr, irq->bp->ring_affinity_set); + #ifdef CONFIG_RFS_ACCEL if (irq->bp->dev->rx_cpu_rmap && irq->ring_nr < irq->bp->rx_nr_rings) { int err; @@ -11807,8 +11810,6 @@ static void bnxt_irq_affinity_notify(struct irq_affinity_notify *notify, if (!irq->bp->tph_mode) return; - cpumask_copy(irq->cpu_mask, mask); - if (irq->ring_nr >= irq->bp->rx_nr_rings) return; @@ -11850,10 +11851,6 @@ static void bnxt_register_irq_notifier(struct bnxt *bp, struct bnxt_irq *irq) irq->bp = bp; - /* Nothing to do if TPH is not enabled */ - if (!bp->tph_mode) - return; - /* Register IRQ affinity notifier */ notify = &irq->affinity_notify; notify->irq = irq->vector; @@ -11863,6 +11860,41 @@ static void bnxt_register_irq_notifier(struct bnxt *bp, struct bnxt_irq *irq) irq_set_affinity_notifier(irq->vector, notify); } +static int bnxt_alloc_ring_cpu_masks(struct bnxt *bp) +{ + int i; + + bp->ring_cpu_mask = kzalloc_objs(*bp->ring_cpu_mask, bp->max_irqs); + if (!bp->ring_cpu_mask) + return -ENOMEM; + + bp->ring_affinity_set = bitmap_zalloc(bp->max_irqs, GFP_KERNEL); + if (!bp->ring_affinity_set) + return -ENOMEM; + + for (i = 0; i < bp->max_irqs; i++) + if (!zalloc_cpumask_var(&bp->ring_cpu_mask[i], GFP_KERNEL)) + return -ENOMEM; + + return 0; +} + +static void bnxt_free_ring_cpu_masks(struct bnxt *bp) +{ + int i; + + if (!bp->ring_cpu_mask) + return; + + for (i = 0; i < bp->max_irqs; i++) + free_cpumask_var(bp->ring_cpu_mask[i]); + + bitmap_free(bp->ring_affinity_set); + bp->ring_affinity_set = NULL; + kfree(bp->ring_cpu_mask); + bp->ring_cpu_mask = NULL; +} + static void bnxt_free_irq(struct bnxt *bp) { struct bnxt_irq *irq; @@ -11877,13 +11909,7 @@ static void bnxt_free_irq(struct bnxt *bp) irq = &bp->irq_tbl[map_idx]; if (irq->requested) { bnxt_release_irq_notifier(irq); - - if (irq->have_cpumask) { - irq_update_affinity_hint(irq->vector, NULL); - free_cpumask_var(irq->cpu_mask); - irq->have_cpumask = 0; - } - + irq_update_affinity_hint(irq->vector, NULL); free_irq(irq->vector, bp->bnapi[i]); } @@ -11925,9 +11951,9 @@ static int bnxt_request_irq(struct bnxt *bp) bp->tph_mode = PCI_TPH_ST_IV_MODE; for (i = 0, j = 0; i < bp->cp_nr_rings; i++) { + struct cpumask *cpu_mask = bp->ring_cpu_mask[i]; int map_idx = bnxt_cp_num_to_irq_num(bp, i); struct bnxt_irq *irq = &bp->irq_tbl[map_idx]; - unsigned int cpu_num; u16 tag; if (IS_ENABLED(CONFIG_RFS_ACCEL) && @@ -11946,19 +11972,24 @@ static int bnxt_request_irq(struct bnxt *bp) netif_napi_set_irq_locked(&bp->bnapi[i]->napi, irq->vector); irq->requested = 1; - - if (!zalloc_cpumask_var(&irq->cpu_mask, GFP_KERNEL)) - continue; - - irq->have_cpumask = 1; irq->msix_nr = map_idx; irq->ring_nr = i; - cpu_num = cpumask_local_spread(i, numa_node); - cpumask_set_cpu(cpu_num, irq->cpu_mask); + + /* Reuse the mask recorded before the IRQs were freed. Nothing + * was recorded yet on the very first request, and the mask + * may have gone stale if the CPUs went offline in between. + */ + if (!test_bit(i, bp->ring_affinity_set) || + !cpumask_intersects(cpu_mask, cpu_online_mask)) { + clear_bit(i, bp->ring_affinity_set); + cpumask_clear(cpu_mask); + cpumask_set_cpu(cpumask_local_spread(i, numa_node), + cpu_mask); + } /* Init ST table entry if we can get the mapping */ if (!pcie_tph_get_cpu_st(bp->pdev, TPH_MEM_TYPE_VM, - cpu_num, &tag)) { + cpumask_first(cpu_mask), &tag)) { pcie_tph_set_st_entry(bp->pdev, irq->msix_nr, tag); irq->tag = tag; irq->new_tag = tag; @@ -11966,10 +11997,19 @@ static int bnxt_request_irq(struct bnxt *bp) bnxt_register_irq_notifier(bp, irq); - rc = irq_update_affinity_hint(irq->vector, irq->cpu_mask); + /* Only put the IRQ back where it was configured to be, our own + * placement is just a hint, the core spreads within + * irq_default_affinity which we know nothing about. + * Set after installing the notifier, if we race with the user + * it's better to overwrite than miss the notification. + */ + if (test_bit(i, bp->ring_affinity_set)) + rc = irq_set_affinity_and_hint(irq->vector, cpu_mask); + else + rc = irq_update_affinity_hint(irq->vector, cpu_mask); if (rc) { netdev_warn(bp->dev, - "Update affinity hint failed, IRQ = %d\n", + "Setting IRQ affinity failed, IRQ = %d\n", irq->vector); break; } @@ -16610,6 +16650,7 @@ static void bnxt_remove_one(struct pci_dev *pdev) bnxt_shutdown_tc(bp); bnxt_clear_int_mode(bp); + bnxt_free_ring_cpu_masks(bp); bnxt_hwrm_func_drv_unrgtr(bp); bnxt_free_hwrm_resources(bp); bnxt_hwmon_uninit(bp); @@ -17048,6 +17089,11 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) bp->msg_enable = BNXT_DEF_MSG_ENABLE; bnxt_set_max_func_irqs(bp, max_irqs); + bp->max_irqs = max_irqs; + rc = bnxt_alloc_ring_cpu_masks(bp); + if (rc) + goto init_err_free; + if (bnxt_vf_pciid(bp->board_idx)) bp->flags |= BNXT_FLAG_VF; @@ -17297,6 +17343,7 @@ init_err_pci_clean: bp->rss_indir_tbl = NULL; init_err_free: + bnxt_free_ring_cpu_masks(bp); free_netdev(dev); return rc; } diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h index dc8ec5e5733e..ab894f8addef 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h @@ -1261,9 +1261,7 @@ struct bnxt_irq { irq_handler_t handler; unsigned int vector; u8 requested:1; - u8 have_cpumask:1; char name[IFNAMSIZ + BNXT_IRQ_NAME_EXTRA]; - cpumask_var_t cpu_mask; struct bnxt *bp; int msix_nr; @@ -2482,6 +2480,15 @@ struct bnxt { pci_channel_offline((bp)->pdev)) struct bnxt_irq *irq_tbl; + /* IRQ affinity, indexed by completion ring. Kept across IRQ + * reallocation, the MSI-X vector index is not stable. + */ + cpumask_var_t *ring_cpu_mask; + /* Rings for which the mask above was configured from the outside, + * rather than being our own default placement. + */ + unsigned long *ring_affinity_set; + int max_irqs; int total_irqs; int ulp_num_msix_want; u8 mac_addr[ETH_ALEN]; -- cgit v1.2.3 From 35afa239e5f312c6202a080bffeceec505dea391 Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Fri, 14 Aug 2026 14:56:55 -0700 Subject: bnxt_en: Add missing NETIF_F_TSO_ECN feature flag All bnxt devices support TSO packets with RFC 3168 ECN flags set. The CWR flag is replicated only on the first segment. Reviewed-by: Andy Gospodarek Signed-off-by: Michael Chan Link: https://patch.msgid.link/20260814215655.2331655-1-michael.chan@broadcom.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net/ethernet/broadcom') diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 35795374a8dd..9377bf675981 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -17148,7 +17148,7 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) } dev->hw_features = NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | NETIF_F_SG | - NETIF_F_TSO | NETIF_F_TSO6 | + NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_TSO_ECN | NETIF_F_GSO_UDP_TUNNEL | NETIF_F_GSO_GRE | NETIF_F_GSO_IPXIP4 | NETIF_F_GSO_UDP_TUNNEL_CSUM | NETIF_F_GSO_GRE_CSUM | @@ -17161,7 +17161,7 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) dev->hw_enc_features = NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | NETIF_F_SG | - NETIF_F_TSO | NETIF_F_TSO6 | + NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_TSO_ECN | NETIF_F_GSO_UDP_TUNNEL | NETIF_F_GSO_GRE | NETIF_F_GSO_UDP_TUNNEL_CSUM | NETIF_F_GSO_GRE_CSUM | NETIF_F_GSO_IPXIP4 | NETIF_F_GSO_PARTIAL; -- cgit v1.2.3