git: 45dc4a13654c - main - ixgbe: Pause firmware recovery polling in low power
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Fri, 11 Sep 2026 20:16:18 UTC
The branch main has been updated by kbowling:
URL: https://cgit.FreeBSD.org/src/commit/?id=45dc4a13654c5e90bcf5f757e46f1353b30848ee
commit 45dc4a13654c5e90bcf5f757e46f1353b30848ee
Author: Kevin Bowling <kbowling@FreeBSD.org>
AuthorDate: 2026-09-04 06:37:25 +0000
Commit: Kevin Bowling <kbowling@FreeBSD.org>
CommitDate: 2026-09-11 20:15:55 +0000
ixgbe: Pause firmware recovery polling in low power
The firmware recovery callout continues polling controller registers
after a power transition. An inaccessible E610 GL_MNG_FWSM register
reads as all ones in D3, which looks like firmware recovery mode and
queues an iflib reset while the device is suspended. A later D0 poll
then reports recovery complete and queues another reset.
Pause and drain the callout before terminal stop policy is applied,
prevent an in-flight callback from rearming it, and restart polling
only after resume has cleared the wake state. Track callout
initialization so partial attach cleanup does not drain an uninitialized
callout.
The false transition was reproduced on a dual-port E610 with direct D3
and system S3. Validate the guard with wake-disabled and wake-armed D3,
three repeated D3 cycles per port, and an S3 magic-packet wake. Both
ports returned to D0 without a false recovery transition.
MFC after: 2 weeks
Sponsored by: BBOX.io
---
sys/dev/ixgbe/if_ix.c | 38 +++++++++++++++++++++++++++++++++-----
sys/dev/ixgbe/ixgbe.h | 2 ++
2 files changed, 35 insertions(+), 5 deletions(-)
diff --git a/sys/dev/ixgbe/if_ix.c b/sys/dev/ixgbe/if_ix.c
index f734db0f79f0..b2a133c80b55 100644
--- a/sys/dev/ixgbe/if_ix.c
+++ b/sys/dev/ixgbe/if_ix.c
@@ -254,6 +254,8 @@ static void ixgbe_update_stats_counters(struct ixgbe_softc *);
static void ixgbe_config_link(if_ctx_t);
static void ixgbe_get_slot_info(struct ixgbe_softc *);
static void ixgbe_fw_mode_timer(void *);
+static void ixgbe_fw_mode_timer_pause(struct ixgbe_softc *);
+static void ixgbe_fw_mode_timer_resume(struct ixgbe_softc *);
static void ixgbe_configure_wakeup(if_ctx_t);
static void ixgbe_configure_wakeup_mta(if_ctx_t);
static void ixgbe_prepare_wakeup(if_ctx_t, bool);
@@ -1408,6 +1410,7 @@ ixgbe_if_attach_post(if_ctx_t ctx)
/* Set up the timer callout */
callout_init(&sc->fw_mode_timer, true);
+ sc->fw_mode_timer_initialized = true;
/* Start the task */
callout_reset(&sc->fw_mode_timer, hz, ixgbe_fw_mode_timer, sc);
@@ -3916,6 +3919,7 @@ ixgbe_if_detach(if_ctx_t ctx)
INIT_DEBUGOUT("ixgbe_detach: begin");
sc->iov_recovery_stop = true;
+ ixgbe_fw_mode_timer_pause(sc);
ixgbe_setup_low_power_mode(ctx);
@@ -3924,8 +3928,6 @@ ixgbe_if_detach(if_ctx_t ctx)
ctrl_ext &= ~IXGBE_CTRL_EXT_DRV_LOAD;
IXGBE_WRITE_REG(&sc->hw, IXGBE_CTRL_EXT, ctrl_ext);
- callout_drain(&sc->fw_mode_timer);
-
if (sc->hw.mac.type == ixgbe_mac_E610) {
ixgbe_disable_lse(sc);
ixgbe_shutdown_aci(&sc->hw);
@@ -4004,7 +4006,9 @@ ixgbe_configure_wakeup_mta(if_ctx_t ctx)
static int
ixgbe_if_power_prepare(if_ctx_t ctx, enum iflib_power_event event)
{
+ struct ixgbe_softc *sc = iflib_get_softc(ctx);
+ ixgbe_fw_mode_timer_pause(sc);
ixgbe_prepare_wakeup(ctx, event != IFLIB_POWER_DETACH);
return (0);
}
@@ -4154,6 +4158,7 @@ ixgbe_if_resume(if_ctx_t ctx)
pci_clear_pme(dev);
hw->wol_enabled = false;
sc->wol_filters = 0;
+ ixgbe_fw_mode_timer_resume(sc);
return (0);
} /* ixgbe_if_resume */
@@ -4796,6 +4801,9 @@ ixgbe_fw_mode_timer(void *arg)
struct ixgbe_softc *sc = arg;
struct ixgbe_hw *hw = &sc->hw;
+ if (atomic_load_acq_int(&sc->fw_mode_timer_paused) != 0)
+ return;
+
if (ixgbe_fw_recovery_mode(hw)) {
if (atomic_cmpset_acq_int(&sc->recovery_mode, 0, 1)) {
/* Firmware error detected, entering recovery mode */
@@ -4815,11 +4823,31 @@ ixgbe_fw_mode_timer(void *arg)
iflib_admin_intr_deferred(sc->ctx);
}
-
- callout_reset(&sc->fw_mode_timer, hz,
- ixgbe_fw_mode_timer, sc);
+ if (atomic_load_acq_int(&sc->fw_mode_timer_paused) == 0)
+ callout_reset(&sc->fw_mode_timer, hz,
+ ixgbe_fw_mode_timer, sc);
} /* ixgbe_fw_mode_timer */
+static void
+ixgbe_fw_mode_timer_pause(struct ixgbe_softc *sc)
+{
+
+ if (!sc->fw_mode_timer_initialized ||
+ atomic_swap_int(&sc->fw_mode_timer_paused, 1) != 0)
+ return;
+ callout_drain(&sc->fw_mode_timer);
+}
+
+static void
+ixgbe_fw_mode_timer_resume(struct ixgbe_softc *sc)
+{
+
+ if (!sc->fw_mode_timer_initialized ||
+ atomic_swap_int(&sc->fw_mode_timer_paused, 0) == 0)
+ return;
+ callout_reset(&sc->fw_mode_timer, hz, ixgbe_fw_mode_timer, sc);
+}
+
/************************************************************************
* ixgbe_sfp_probe
*
diff --git a/sys/dev/ixgbe/ixgbe.h b/sys/dev/ixgbe/ixgbe.h
index ba8cfb94915a..fb0ac15f5008 100644
--- a/sys/dev/ixgbe/ixgbe.h
+++ b/sys/dev/ixgbe/ixgbe.h
@@ -505,6 +505,8 @@ struct ixgbe_softc {
/* Firmware error check */
int recovery_mode;
+ u_int fw_mode_timer_paused;
+ bool fw_mode_timer_initialized;
bool overtemp_shutdown_pending;
struct callout fw_mode_timer;