From: Bryan Cochrane <bcochrane@google.com>
To: Michael Chan <michael.chan@broadcom.com>,
Pavan Chebbi <pavan.chebbi@broadcom.com>,
netdev@vger.kernel.org
Cc: Andrew Lunn <andrew+netdev@lunn.ch>,
"David S . Miller" <davem@davemloft.net>,
Eric Dumazet <edumazet@kernel.org>,
Jakub Kicinski <kuba@kernel.org>, Paolo Abeni <pabeni@redhat.com>,
Stanislav Fomichev <sdf@fomichev.me>, Joe Damato <joe@dama.to>,
linux-kernel@vger.kernel.org,
Bryan Cochrane <bcochrane@google.com>,
stable@vger.kernel.org
Subject: [PATCH net v5] bnxt_en: Take RTNL before netif_close() when aborting FW reset
Date: Mon, 9 Nov 2026 20:42:35 -0800 [thread overview]
Message-ID: <20261110044235.204386-1-bcochrane@google.com> (raw)
netif_close() must be called with RTNL held in addition to the netdev
instance lock: __dev_close_many() asserts it, and the NETDEV_GOING_DOWN
and NETDEV_DOWN notifier chains it runs rely on it for serialization.
Commit 004b5008016a ("eth: bnxt: remove most dependencies on RTNL")
replaced rtnl_lock() with netdev_lock() in bnxt_fw_reset_task() and
bnxt_fw_reset(). That is sufficient for the paths that only tear the
NIC down internally with __bnxt_close_nic(), but the abort paths close
the netdev via bnxt_fw_reset_abort() -> netif_close(), and the error
path in bnxt_fw_reset() calls netif_close() directly. Those now run
without RTNL and trigger a WARN_ONCE() cascade when a firmware reset is
aborted:
RTNL: assertion failed at net/core/dev.c (1740)
WARNING: net/core/dev.c:1740 at __dev_close_many+0x1be/0x1f0
Workqueue: bnxt_pf_wq bnxt_fw_reset_task [bnxt_en]
Call Trace:
netif_close_many+0xb5/0x190
netif_close+0xa2/0xd0
bnxt_fw_reset_task.cold+0x8d/0x213 [bnxt_en]
process_one_work+0x19d/0x3a0
worker_thread+0x1af/0x320
kthread+0xe3/0x120
followed by the same assertion from call_netdevice_notifiers_info(),
fib_rules_event(), inetdev_event(), rtnl_fill_ifinfo(),
devlink_compat_phys_port_name_get() and ip_mc_down().
This was observed on a machine in real use with a BCM57504 (4 PFs,
FW 236.1.153.0) running v7.1.6: the firmware crashed, announced a
"Fatal firmware reset event" and then failed to come back within the
advertised max wait, so bnxt_fw_reset_task() aborted the reset on all
ports:
bnxt_en 0000:c1:00.1 eno16905np1: Firmware reset aborted
bnxt_en 0000:c1:00.1 eno16905np1: fw_health_status 0x448100
Take RTNL (before the netdev instance lock, matching the OPENING state
which already uses rtnl_trylock()) in the fw_reset_abort path of
bnxt_fw_reset_task() and route the POLL_VF abort through that same
path.
In bnxt_fw_reset(), only a PF can reach the bnxt_get_registered_vfs()
error path that calls netif_close(), so take RTNL up front via
bnxt_rtnl_lock_sp() when BNXT_PF(bp) is true and keep bnxt_lock_sp() on
VFs. That avoids dropping the instance lock mid-reset (which would
expose a transient BNXT_STATE_IN_FW_RESET and let a concurrent devlink
health recover set BNXT_STATE_IN_SP_TASK), avoids racing with
bnxt_sriov_configure() updating active_vfs outside RTNL, and keeps VFs
from contending on RTNL during recovery while PFs already run on the
single-threaded bnxt_pf_wq.
The bug was found by inspecting the kernel log of the affected machine
and the driver source; the fix was prepared with the help of an AI
coding assistant (Gemini based) and reviewed by the submitter. It is
build tested (allmodconfig with W=1, plus the same change on top of
v7.1.6 built as a module for the affected BCM57504 host). The aborted
reset path itself only runs when the firmware fails to return from a
reset, which could not be reproduced on demand.
Fixes: 004b5008016a ("eth: bnxt: remove most dependencies on RTNL")
Cc: stable@vger.kernel.org
Signed-off-by: Bryan Cochrane <bcochrane@google.com>
---
v5:
- In bnxt_fw_reset(), take bnxt_rtnl_lock_sp() up front when BNXT_PF(bp)
is true (keeping bnxt_lock_sp() on VFs) instead of dropping and
reacquiring locks on the bnxt_get_registered_vfs() < 0 path, and drop
the bnxt_in_sp_task() change from v4. Dropping netdev_lock mid-reset
exposed a transient BNXT_STATE_IN_FW_RESET, while guarding
clear_bit(BNXT_STATE_IN_SP_TASK) in bnxt_[rtnl_]lock_sp() risked a
deadlock with devl_lock during devlink health recover (reported by the
netdev Sashiko review). Using BNXT_PF(bp) rather than active_vfs avoids
racing with bnxt_sriov_configure() while still keeping VFs off RTNL.
v4: https://lore.kernel.org/netdev/20261108222942.1233295-1-bcochrane@google.com/
- Return to the synchronous close in bnxt_fw_reset() from v2, using
netdev_unlock() + bnxt_rtnl_lock_sp() as suggested by Michael Chan,
and guard BNXT_STATE_IN_SP_TASK in bnxt_[rtnl_]lock_sp() and
bnxt_[rtnl_]unlock_sp() with current_work() == &bp->sp_task. Deferring
the close to bnxt_fw_reset_task() in v3 still hung when entered via
bnxt_fw_recover(), raced with bnxt_hwrm_if_change() clearing
fw_reset_state, and marked the reporter SEVERITY_FATAL (reported by
the netdev Sashiko review).
- Omitted Michael's Reviewed-by and Stanislav's Acked-by from v2 due to
the new bnxt_in_sp_task() check.
v3: https://lore.kernel.org/netdev/20261104045534.79783-1-bcochrane@google.com/
- Do not drop and retake the locks in bnxt_fw_reset(). Route the
bnxt_get_registered_vfs() failure through the bnxt_fw_reset_task()
abort path instead; this also restarts the ULP after that abort. The
v2 unlock/relock window let a concurrent devlink health recover set
BNXT_STATE_IN_SP_TASK via bnxt_unlock_sp() and hang the close
(reported by the netdev Sashiko review).
v2: https://lore.kernel.org/netdev/20261103042210.2984373-1-bcochrane@google.com/
- Keep bnxt_lock_sp() in bnxt_fw_reset() and take RTNL only on the
bnxt_get_registered_vfs() error path, to avoid RTNL contention when
many VFs recover at the same time (Michael Chan).
- Dropped Stanislav's Acked-by because of the change above.
v1: https://lore.kernel.org/netdev/20261029032832.1511180-1-bcochrane@google.com/
drivers/net/ethernet/broadcom/bnxt/bnxt.c | 15 +++++++++++----
1 file changed, 11 insertions(+), 4 deletions(-)
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
index 33c4cacc8ffe..8b7ebf2eefaa 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
@@ -14838,7 +14838,10 @@ static int bnxt_get_registered_vfs(struct bnxt *bp)
void bnxt_fw_reset(struct bnxt *bp)
{
bnxt_ulp_stop(bp);
- bnxt_lock_sp(bp);
+ if (BNXT_PF(bp))
+ bnxt_rtnl_lock_sp(bp);
+ else
+ bnxt_lock_sp(bp);
if (test_bit(BNXT_STATE_OPEN, &bp->state) &&
!test_bit(BNXT_STATE_IN_FW_RESET, &bp->state)) {
struct bnxt_ptp_cfg *ptp = bp->ptp_cfg;
@@ -14884,7 +14887,10 @@ void bnxt_fw_reset(struct bnxt *bp)
bnxt_queue_fw_reset_work(bp, tmo);
}
fw_reset_exit:
- bnxt_unlock_sp(bp);
+ if (BNXT_PF(bp))
+ bnxt_rtnl_unlock_sp(bp);
+ else
+ bnxt_unlock_sp(bp);
}
static void bnxt_chk_missed_irq(struct bnxt *bp)
@@ -15547,9 +15553,8 @@ static void bnxt_fw_reset_task(struct work_struct *work)
bp->fw_reset_timestamp = jiffies;
netdev_lock(bp->dev);
if (test_bit(BNXT_STATE_ABORT_ERR, &bp->state)) {
- bnxt_fw_reset_abort(bp, rc);
netdev_unlock(bp->dev);
- goto ulp_start;
+ goto fw_reset_abort;
}
bnxt_fw_reset_close(bp);
if (bp->fw_cap & BNXT_FW_CAP_ERR_RECOVER_RELOAD) {
@@ -15682,9 +15687,11 @@ static void bnxt_fw_reset_task(struct work_struct *work)
netdev_err(bp->dev, "fw_health_status 0x%x\n", sts);
}
fw_reset_abort:
+ rtnl_lock();
netdev_lock(bp->dev);
bnxt_fw_reset_abort(bp, rc);
netdev_unlock(bp->dev);
+ rtnl_unlock();
ulp_start:
if (!rc)
bnxt_ulp_start(bp);
base-commit: a45dbf182137275f44ad7c8bc8acd23f7af6b2cf
--
2.56.0.506.g506a7eabc5-goog
next reply other threads:[~2026-11-10 4:42 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-10 4:42 Bryan Cochrane [this message]
2026-11-12 18:40 ` [PATCH net v5] bnxt_en: Take RTNL before netif_close() when aborting FW reset patchwork-bot+netdevbpf
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261110044235.204386-1-bcochrane@google.com \
--to=bcochrane@google.com \
--cc=andrew+netdev@lunn.ch \
--cc=davem@davemloft.net \
--cc=edumazet@kernel.org \
--cc=joe@dama.to \
--cc=kuba@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=michael.chan@broadcom.com \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=pavan.chebbi@broadcom.com \
--cc=sdf@fomichev.me \
--cc=stable@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.