eth: bnxt: fix IRQ notifier bugs
Jakub Kicinski
maintainercover eth: bnxt: fix IRQ notifier bugs
I was trying to make bnxt preserve IRQ mappings across reconfiguration. While hacking on that I noticed 2 bugs in the notifiers that should probably be fixed before development work.
First one is simple - TPH recofig makes aARFs not work. There can only be one notifier per IRQ and TPH "steals" the callback from the rmap updates. Fix by patches 1 and 2.
Second one is a deadlock between the affinity notifier and reconfig. This one is a bit more involved (patch 3 and 4).
Unfortunately, I can't really verify the problem or test the fix. I managed to get my hands on a system with an AMD Venice CPU which is supposed to support TPH, but the ACPI is missing some bits to actually advertise it. pcie_tph_get_cpu_st() returns -EINVAL.
This series would conflict with the affinity preservation which will need to go to net-next, so I don't want to wait until I can test.
Jakub Kicinski (4):
eth: bnxt: cancel IRQ notifier before freeing affinity mask
eth: bnxt: keep the aRFS rmap updated when TPH is enabled
eth: bnxt: decrease indent in bnxt_request_irq()
eth: bnxt: avoid deadlock when canceling IRQ affinity notifier
drivers/net/ethernet/broadcom/bnxt/bnxt.h | 3 + drivers/net/ethernet/broadcom/bnxt/bnxt.c | 129 +++++++++++++++------- 2 files changed, 91 insertions(+), 41 deletions(-)
Signature · 1 line
2.55.0
Jakub Kicinski
maintainerPATCH 1/4 eth: bnxt: cancel IRQ notifier before freeing affinity mask
bnxt_irq_affinity_notify() copies into irq->cpu_mask. Cancel the notifier before freeing irq->cpu_mask.
--- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/net/ethernet/broadcom/bnxt/bnxt.c +2 -2
@@ -11859,14 +11859,14 @@ 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 ; } -bnxt_release_irq_notifier (irq); -free_irq (irq->vector, bp->bnapi[i]); }
Signature · 1 line
2.55.0
Michael Chan
maintainerRe: [PATCH net 1/4] eth: bnxt: cancel IRQ notifier before freeing affinity mask
Quoted 15 lines · Jakub Kicinski
bnxt_irq_affinity_notify()copies intoirq->cpu_mask. Cancel the notifier before freeingirq->cpu_mask.Fixes c214410c47d6 ("bnxt_en: Add TPH support in BNXT driver")Signed-off-by Jakub Kicinski <[email protected]>--- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)drivers/net/ethernet/broadcom/bnxt/bnxt.c +0
-0
drivers/net/ethernet/broadcom/bnxt/bnxt.c static void bnxt_free_irq(struct bnxt *bp) · line 11863
Quoted 5 lines
drivers/net/ethernet/broadcom/bnxt/bnxt.c +2
-0@@ -11859,14 +11859,14 @@ static void bnxt_free_irq(struct bnxt *bp) irq = &bp->irq_tbl[map_idx];if (irq->requested) { +bnxt_release_irq_notifier (irq); +
bnxt_release_irq_notifier() calls irq_set_affinity_notifier() to set the notifier to NULL. But the call is not synchronous, right? A pending notifier is still possible after irq_set_affinity_notifier() returns, right?
Quoted 10 lines
drivers/net/ethernet/broadcom/bnxt/bnxt.c +0
-2if (irq->have_cpumask) {irq_update_affinity_hint (irq->vector,NULL );free_cpumask_var (irq->cpu_mask); irq->have_cpumask =0 ; } -bnxt_release_irq_notifier (irq); -free_irq (irq->vector, bp->bnapi[i]); }
Re: [PATCH net 1/4] eth: bnxt: cancel IRQ notifier before freeing affinity mask
Quoted 18 lines · Michael Chan
Quoted 16 lines · Jakub Kicinski
bnxt_irq_affinity_notify()copies intoirq->cpu_mask. Cancel the notifier before freeingirq->cpu_mask.Fixes c214410c47d6 ("bnxt_en: Add TPH support in BNXT driver")Signed-off-by Jakub Kicinski <[email protected]>--- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)drivers/net/ethernet/broadcom/bnxt/bnxt.c +0
-0@@ -11859,14 +11859,14 @@ static void bnxt_free_irq(struct bnxt *bp)
Quoted 9 lines
drivers/net/ethernet/broadcom/bnxt/bnxt.c static void bnxt_free_irq(struct bnxt *bp) · line 11863
Quoted 4 lines
drivers/net/ethernet/broadcom/bnxt/bnxt.c +2
-0irq = &bp->irq_tbl[map_idx];if (irq->requested) { +bnxt_release_irq_notifier (irq); +
bnxt_release_irq_notifier()callsirq_set_affinity_notifier()to set the notifier to NULL. But the call is not synchronous, right? A pending notifier is still possible afterirq_set_affinity_notifier()returns, right?
Nevermind. It is synchronous so there is no issue.
Jakub Kicinski
maintainerPATCH 2/4 eth: bnxt: keep the aRFS rmap updated when TPH is enabled
In reply to Jakub KicinskiThe TPH support must have broken aRFS in bnxt. IRQ can only have one notifier, so installing the TPH notifier is overriding the one implicitly installed by irq_cpu_rmap_add().
Make sure we call cpu_rmap_update() from the TPH notifier.
We need to be careful with the ordering and not free the rmap until we unregistered the notifier. Note that moving the rmap freeing after the early return in bnxt_free_irq() is fine - there's no path that could leave rmap with irq_tbl being NULL.
--- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-)
drivers/net/ethernet/broadcom/bnxt/bnxt.c +15 -4
@@ -11774,6 +11774,16 @@ static void bnxt_irq_affinity_notify(struct irq_affinity_notify *notify, irq = container_of(notify, struct bnxt_irq, affinity_notify); +#ifdef CONFIG_RFS_ACCEL +if (irq->bp->dev->rx_cpu_rmap && irq->ring_nr < irq->bp->rx_nr_rings) { + err =cpu_rmap_update (irq->bp->dev->rx_cpu_rmap, irq->ring_nr, + mask); +if (err) +netdev_warn (irq->bp->dev, +"aRFS rmap update failed: %d\n" , err); + } +#endif +if (!irq->bp->tph_mode)return ;
@@ -11847,10 +11857,6 @@ static void bnxt_free_irq(struct bnxt *bp)struct bnxt_irq *irq;int i; -#ifdef CONFIG_RFS_ACCEL -free_irq_cpu_rmap (bp->dev->rx_cpu_rmap); - bp->dev->rx_cpu_rmap =NULL ; -#endif if (!bp->irq_tbl || !bp->bnapi)return ;
@@ -11876,6 +11882,11 @@ static void bnxt_free_irq(struct bnxt *bp)/* Disable TPH support */ pcie_disable_tph (bp->pdev); bp->tph_mode =0 ; + +#ifdef CONFIG_RFS_ACCEL +free_irq_cpu_rmap (bp->dev->rx_cpu_rmap); + bp->dev->rx_cpu_rmap =NULL ; +#endif }static int bnxt_request_irq (struct bnxt *bp)
Signature · 1 line
2.55.0
Re: [PATCH net 2/4] eth: bnxt: keep the aRFS rmap updated when TPH is enabled
Quoted 7 lines · Jakub Kicinski
The TPH support must have broken aRFS in bnxt. IRQ can only have one notifier, so installing the TPH notifier is overriding the one implicitly installed by
irq_cpu_rmap_add().Make sure we call
cpu_rmap_update()from the TPH notifier.
Quoted 7 lines
We need to be careful with the ordering and not free the rmap until we unregistered the notifier. Note that moving the rmap freeing after the early return in
bnxt_free_irq()is fine - there's no path that could leave rmap withirq_tblbeing NULL.Fixes c214410c47d6 ("bnxt_en: Add TPH support in BNXT driver")Signed-off-by Jakub Kicinski <[email protected]>
Jakub Kicinski
maintainerPATCH 3/4 eth: bnxt: decrease indent in bnxt_request_irq()
In reply to Jakub Kicinskibnxt_request_irq() has unnecessary level of indentation. Use continue instead. No need to re-fetch NUMA node for each IRQ, move to the function level.
No functional changes.
--- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 50 +++++++++++------------ 1 file changed, 25 insertions(+), 25 deletions(-)
drivers/net/ethernet/broadcom/bnxt/bnxt.c +25 -25
@@ -11891,6 +11891,7 @@ static void bnxt_free_irq(struct bnxt *bp)static int bnxt_request_irq (struct bnxt *bp) { +const int numa_node =dev_to_node (&bp->pdev->dev);struct cpu_rmap *rmap =NULL ;int i, j, rc =0 ;unsigned long flags =0 ;
@@ -11913,6 +11914,7 @@ static int bnxt_request_irq(struct bnxt *bp)for (i =0 , j =0 ; i < bp->cp_nr_rings; i++) {int map_idx =bnxt_cp_num_to_irq_num (bp, i);struct bnxt_irq *irq = &bp->irq_tbl[map_idx]; +u16 tag;if (IS_ENABLED (CONFIG_RFS_ACCEL ) && rmap && bp->bnapi[i]->rx_ring) {
@@ -11931,33 +11933,31 @@ 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 )) { -int numa_node =dev_to_node (&bp->pdev->dev); -u16 tag; +if (!zalloc_cpumask_var (&irq->cpu_mask,GFP_KERNEL )) +continue ; - irq->have_cpumask =1 ; - irq->msix_nr = map_idx; - irq->ring_nr = i; -cpumask_set_cpu (cpumask_local_spread (i, numa_node), - irq->cpu_mask); - rc =irq_update_affinity_hint (irq->vector, irq->cpu_mask); -if (rc) { -netdev_warn (bp->dev, -"Update affinity hint failed, IRQ = %d\n" , - irq->vector); -break ; - } - -bnxt_register_irq_notifier (bp, irq); - -/* Init ST table entry */ -if (pcie_tph_get_cpu_st (irq->bp->pdev,TPH_MEM_TYPE_VM , -cpumask_first (irq->cpu_mask), - &tag)) -continue ; - -pcie_tph_set_st_entry (irq->bp->pdev, irq->msix_nr, tag); + irq->have_cpumask =1 ; + irq->msix_nr = map_idx; + irq->ring_nr = i; +cpumask_set_cpu (cpumask_local_spread (i, numa_node), + irq->cpu_mask); + rc =irq_update_affinity_hint (irq->vector, irq->cpu_mask); +if (rc) { +netdev_warn (bp->dev, +"Update affinity hint failed, IRQ = %d\n" , + irq->vector); +break ; } + +bnxt_register_irq_notifier (bp, irq); + +/* Init ST table entry */ +if (pcie_tph_get_cpu_st (irq->bp->pdev,TPH_MEM_TYPE_VM , +cpumask_first (irq->cpu_mask), + &tag)) +continue ; + +pcie_tph_set_st_entry (irq->bp->pdev, irq->msix_nr, tag); }return rc; }
Signature · 1 line
2.55.0
Re: [PATCH net 3/4] eth: bnxt: decrease indent in bnxt_request_irq()
Quoted 5 lines · Jakub Kicinski
bnxt_request_irq()has unnecessary level of indentation. Use continue instead. No need to re-fetch NUMA node for each IRQ, move to the function level.No functional changes.
The transformation looks equivalent to me.
Quoted 1 line
Signed-off-by Jakub Kicinski <[email protected]>
Jakub Kicinski
maintainerPATCH 4/4 eth: bnxt: avoid deadlock when canceling IRQ affinity notifier
In reply to Jakub KicinskiUnregistering IRQ affinity notifiers waits for the callback synchronously. bnxt takes the netdev instance lock in the notifier (to restart the queue) and cancels the work under the same lock. This may obviously deadlock.
Move the restart to the async service task. The queue restart isn't super time sensitive. Store the new TPH tag, schedule the task. Safely canceling the service task is already ironed out.
In bnxt_request_irq() the order of registering notifier, affinity and initial TPH programming has to be inverted. I think it was racy previously since user may trigger an update as soon as notifier is installed.
There's a small known gap - if pcie_tph_get_cpu_st() fails at init and the target tag is 0 we may miss programming the entry. This does not seem worth fixing, the code has skip-on-failure all over the place, anyway.
--- drivers/net/ethernet/broadcom/bnxt/bnxt.h | 3 + drivers/net/ethernet/broadcom/bnxt/bnxt.c | 86 ++++++++++++++++------- 2 files changed, 64 insertions(+), 25 deletions(-)
drivers/net/ethernet/broadcom/bnxt/bnxt.h +3 -0
@@ -1268,6 +1268,8 @@ struct bnxt_irq {struct bnxt *bp;int msix_nr;int ring_nr; +u16 tag; +u16 new_tag;struct irq_affinity_notify affinity_notify; };
@@ -2641,6 +2643,7 @@ struct bnxt {#define BNXT_RING_COAL_NOW_SP_EVENT 17#define BNXT_FW_RESET_NOTIFY_SP_EVENT 18#define BNXT_FW_EXCEPTION_SP_EVENT 19 +#define BNXT_TPH_UPDATE_SP_EVENT 20#define BNXT_LINK_CFG_CHANGE_SP_EVENT 21#define BNXT_THERMAL_THRESHOLD_SP_EVENT 22#define BNXT_FW_ECHO_REQUEST_SP_EVENT 23
drivers/net/ethernet/broadcom/bnxt/bnxt.c +61 -25
141 lines not on this page · raw message
Signature · 1 line
2.55.0
Michael Chan
maintainerRe: [PATCH net 4/4] eth: bnxt: avoid deadlock when canceling IRQ affinity notifier
Quoted 14 lines · Jakub Kicinski
Unregistering IRQ affinity notifiers waits for the callback synchronously. bnxt takes the netdev instance lock in the notifier (to restart the queue) and cancels the work under the same lock. This may obviously deadlock.
Move the restart to the async service task. The queue restart isn't super time sensitive. Store the new TPH tag, schedule the task. Safely canceling the service task is already ironed out.
In
bnxt_request_irq()the order of registering notifier, affinity and initial TPH programming has to be inverted. I think it was racy previously since user may trigger an update as soon as notifier is installed.
Quoted 7 lines
There's a small known gap - if
pcie_tph_get_cpu_st()fails at init and the target tag is 0 we may miss programming the entry. This does not seem worth fixing, the code has skip-on-failure all over the place, anyway.Fixes c214410c47d6 ("bnxt_en: Add TPH support in BNXT driver")Signed-off-by Jakub Kicinski <[email protected]>
Thanks for the patches. We'll get the patches tested in our lab.
Vishvambar Panth S
Tested-byRe: [PATCH net 4/4] eth: bnxt: avoid deadlock when canceling IRQ affinity notifier
Test summary: on an AMD EPYC 9335 CPU.
- Instrumented driver to see relevant debug logs - init/notify/update/teardown
- smp_affinity_list of irqs modified under active iperf3 traffic (irqbalance also active)
- Affinity modify with concurrent 'ip link down' (~20 iterations): no hang/deadlock
- aRFS/TPH coexistence: verified packets are correctly steered to the relevant queues.
Quoted 25 lines · Michael Chan
Quoted 14 lines · Jakub Kicinski
Unregistering IRQ affinity notifiers waits for the callback synchronously. bnxt takes the netdev instance lock in the notifier (to restart the queue) and cancels the work under the same lock. This may obviously deadlock.
Move the restart to the async service task. The queue restart isn't super time sensitive. Store the new TPH tag, schedule the task. Safely canceling the service task is already ironed out.
In
bnxt_request_irq()the order of registering notifier, affinity and initial TPH programming has to be inverted. I think it was racy previously since user may trigger an update as soon as notifier is installed.Quoted 7 lines
There's a small known gap - if
pcie_tph_get_cpu_st()fails at init and the target tag is 0 we may miss programming the entry. This does not seem worth fixing, the code has skip-on-failure all over the place, anyway.Fixes c214410c47d6 ("bnxt_en: Add TPH support in BNXT driver")Signed-off-by Jakub Kicinski <[email protected]>Thanks for the patches. We'll get the patches tested in our lab.
This series was applied to netdev/net.git (main) by Jakub Kicinski <[email protected]>:
Hello:
This series was applied to netdev/net.git (main)
by Jakub Kicinski <[email protected]>:
Quoted 9 lines · On Mon, 3 Aug 2026 12:31:31 -0700 you wrote:
I was trying to make bnxt preserve IRQ mappings across reconfiguration. While hacking on that I noticed 2 bugs in the notifiers that should probably be fixed before development work.
First one is simple - TPH recofig makes aARFs not work. There can only be one notifier per IRQ and TPH "steals" the callback from the rmap updates. Fix by patches 1 and 2.
[...]
Here is the summary with links:
- [net,1/4] eth: bnxt: cancel IRQ notifier before freeing affinity mask
https://git.kernel.org/netdev/net/c/c703f62c30f2 - [net,2/4] eth: bnxt: keep the aRFS rmap updated when TPH is enabled https://git.kernel.org/netdev/net/c/4b5cb58a4443 - [net,3/4] eth: bnxt: decrease indent in bnxt_request_irq() https://git.kernel.org/netdev/net/c/0fd562c7b9e4 - [net,4/4] eth: bnxt: avoid deadlock when canceling IRQ affinity notifier https://git.kernel.org/netdev/net/c/51e96fa31f7e
You are awesome, thank you!
Signature · 2 lines
Deet-doot-dot, I am a bot. https://korg.docs.kernel.org/patchwork/pwbot.html