Page MenuHomeFreeBSD

D59329.diff
No OneTemporary

D59329.diff

diff --git a/share/man/man9/Makefile b/share/man/man9/Makefile
--- a/share/man/man9/Makefile
+++ b/share/man/man9/Makefile
@@ -1327,12 +1327,14 @@
iflibdd.9 ifdi_media_change.9 \
iflibdd.9 ifdi_mtu_set.9 \
iflibdd.9 ifdi_multi_set.9 \
+ iflibdd.9 ifdi_power_prepare.9 \
iflibdd.9 ifdi_promisc_set.9 \
iflibdd.9 ifdi_queues_alloc.9 \
iflibdd.9 ifdi_queues_free.9 \
iflibdd.9 ifdi_queue_intr_enable.9 \
iflibdd.9 ifdi_resume.9 \
iflibdd.9 ifdi_rxq_setup.9 \
+ iflibdd.9 ifdi_shutdown.9 \
iflibdd.9 ifdi_stop.9 \
iflibdd.9 ifdi_suspend.9 \
iflibdd.9 ifdi_sysctl_int_delay.9 \
diff --git a/share/man/man9/iflibdd.9 b/share/man/man9/iflibdd.9
--- a/share/man/man9/iflibdd.9
+++ b/share/man/man9/iflibdd.9
@@ -1,4 +1,4 @@
-.Dd August 8, 2026
+.Dd September 11, 2026
.Dt IFLIBDD 9
.Os
.Sh NAME
@@ -65,10 +65,19 @@
.Fa "uint16_t vtag"
.Fc
.Ft int
+.Fo ifdi_power_prepare
+.Fa "if_ctx_t ctx"
+.Fa "enum iflib_power_event event"
+.Fc
+.Ft int
.Fo ifdi_suspend
.Fa "if_ctx_t ctx"
.Fc
.Ft int
+.Fo ifdi_shutdown
+.Fa "if_ctx_t ctx"
+.Fc
+.Ft int
.Fo ifdi_resume
.Fa "if_ctx_t ctx"
.Fc
@@ -235,10 +244,37 @@
is the new VLAN tag.
.It Fn ifdi_vlan_unregister
Optional function called by the VLAN unconfig eventhandler.
+.It Fn ifdi_power_prepare
+Optional function called before iflib performs the terminal stop used for
+detach, suspend, or shutdown.
+The
+.Fa event
+argument is one of
+.Dv IFLIB_POWER_DETACH ,
+.Dv IFLIB_POWER_SUSPEND ,
+or
+.Dv IFLIB_POWER_SHUTDOWN .
+Drivers should use this hook only to establish policy which must precede the
+stop and must not start, stop, or alter queue DMA; the default implementation
+does nothing.
+If it returns an error, it must undo any partial policy change before
+returning.
.It Fn ifdi_suspend
-Optional function that suspends the driver.
+Optional function called after iflib has stopped queue DMA and drained its
+private configuration taskqueue.
+It performs suspend-specific firmware, wake, PHY, and low-power work.
+An error is returned to the bus, and iflib attempts to resume the stopped
+interface.
+.It Fn ifdi_shutdown
+Optional function called after the same terminal stop and taskqueue drain as
+.Fn ifdi_suspend .
+Shutdown errors are returned without attempting rollback.
.It Fn ifdi_resume
-Optional function that resumes a driver.
+Optional function that restores suspend-specific device state while the
+datapath remains stopped.
+After it succeeds, iflib initializes the datapath exactly once when the
+interface is administratively up and leaves an administratively-down interface
+stopped.
.El
.Ss Device Configuration Functions
.Bl -ohang -offset indent
diff --git a/sys/dev/aq/aq_main.c b/sys/dev/aq/aq_main.c
--- a/sys/dev/aq/aq_main.c
+++ b/sys/dev/aq/aq_main.c
@@ -565,12 +565,9 @@
AQ_DBG_ENTER();
- aq_if_stop(ctx);
if (aq_hw_deinit(&softc->hw) != 0)
device_printf(softc->dev,
"could not shut the hardware down for suspend\n");
- /* iflib_device_suspend() does not stop the interface for us. */
- if_setdrvflagbits(iflib_get_ifp(ctx), IFF_DRV_OACTIVE, IFF_DRV_RUNNING);
AQ_DBG_EXIT(0);
return (0);
diff --git a/sys/dev/ice/if_ice_iflib.c b/sys/dev/ice/if_ice_iflib.c
--- a/sys/dev/ice/if_ice_iflib.c
+++ b/sys/dev/ice/if_ice_iflib.c
@@ -3378,7 +3378,7 @@
* Deinitializes the driver and clears HW resources in preparation for
* suspend or an FLR.
*
- * @returns 0; this return value is ignored
+ * @returns 0 on success, or an error code on failure
*/
static int
ice_if_suspend(if_ctx_t ctx)
@@ -3402,7 +3402,7 @@
* Reinitializes the driver and the HW after PCI resume or after
* an FLR. An init is performed by iflib after this function is finished.
*
- * @returns 0; this return value is ignored
+ * @returns 0 on success, or an error code on failure
*/
static int
ice_if_resume(if_ctx_t ctx)
diff --git a/sys/dev/ixgbe/if_ix.c b/sys/dev/ixgbe/if_ix.c
--- a/sys/dev/ixgbe/if_ix.c
+++ b/sys/dev/ixgbe/if_ix.c
@@ -181,6 +181,7 @@
static int ixgbe_if_attach_pre(if_ctx_t);
static int ixgbe_if_attach_post(if_ctx_t);
static int ixgbe_if_detach(if_ctx_t);
+static int ixgbe_if_power_prepare(if_ctx_t, enum iflib_power_event);
static int ixgbe_if_shutdown(if_ctx_t);
static int ixgbe_if_suspend(if_ctx_t);
static int ixgbe_if_resume(if_ctx_t);
@@ -227,7 +228,7 @@
static void ixgbe_disable_queue(struct ixgbe_softc *, u32);
static void ixgbe_add_device_sysctls(if_ctx_t);
static int ixgbe_allocate_pci_resources(if_ctx_t);
-static int ixgbe_setup_low_power_mode(if_ctx_t, bool);
+static int ixgbe_setup_low_power_mode(if_ctx_t);
static void ixgbe_config_dmac(struct ixgbe_softc *);
static void ixgbe_configure_ivars(struct ixgbe_softc *);
@@ -362,6 +363,7 @@
DEVMETHOD(ifdi_attach_pre, ixgbe_if_attach_pre),
DEVMETHOD(ifdi_attach_post, ixgbe_if_attach_post),
DEVMETHOD(ifdi_detach, ixgbe_if_detach),
+ DEVMETHOD(ifdi_power_prepare, ixgbe_if_power_prepare),
DEVMETHOD(ifdi_shutdown, ixgbe_if_shutdown),
DEVMETHOD(ifdi_suspend, ixgbe_if_suspend),
DEVMETHOD(ifdi_resume, ixgbe_if_resume),
@@ -3915,7 +3917,7 @@
sc->iov_recovery_stop = true;
- ixgbe_setup_low_power_mode(ctx, false);
+ ixgbe_setup_low_power_mode(ctx);
/* let hardware know driver is unloading */
ctrl_ext = IXGBE_READ_REG(&sc->hw, IXGBE_CTRL_EXT);
@@ -3993,13 +3995,27 @@
hw->mac.mc_filter_type);
}
+/************************************************************************
+ * ixgbe_if_power_prepare - Establish policy required before a terminal stop
+ *
+ * Snapshot the requested wake filters before iflib stops the interface.
+ * X550EM 10GBASE-T must also suppress its PHY reset during that stop.
+ ************************************************************************/
+static int
+ixgbe_if_power_prepare(if_ctx_t ctx, enum iflib_power_event event)
+{
+
+ ixgbe_prepare_wakeup(ctx, event != IFLIB_POWER_DETACH);
+ return (0);
+}
+
/************************************************************************
* ixgbe_setup_low_power_mode - LPLU/WoL preparation
*
- * Prepare the adapter/port for LPLU and/or WoL
+ * Prepare the adapter/port for LPLU and/or WoL after a terminal stop
************************************************************************/
static int
-ixgbe_setup_low_power_mode(if_ctx_t ctx, bool arm_wake)
+ixgbe_setup_low_power_mode(if_ctx_t ctx)
{
struct ixgbe_softc *sc = iflib_get_softc(ctx);
struct ixgbe_hw *hw = &sc->hw;
@@ -4007,10 +4023,7 @@
u32 fctrl, grc, wufc;
s32 error = 0;
- /* Snapshot wake policy before the terminal stop clears hardware state. */
- ixgbe_prepare_wakeup(ctx, arm_wake);
wufc = sc->wol_filters;
- ixgbe_if_stop(ctx);
/* Limit power management flow to X550EM baseT */
if (hw->device_id == IXGBE_DEV_ID_X550EM_X_10G_T &&
@@ -4086,7 +4099,7 @@
INIT_DEBUGOUT("ixgbe_shutdown: begin");
- error = ixgbe_setup_low_power_mode(ctx, true);
+ error = ixgbe_setup_low_power_mode(ctx);
if (error != 0)
device_printf(iflib_get_dev(ctx),
"Wake configuration failed during shutdown: %d\n", error);
@@ -4105,7 +4118,7 @@
INIT_DEBUGOUT("ixgbe_suspend: begin");
- error = ixgbe_setup_low_power_mode(ctx, true);
+ error = ixgbe_setup_low_power_mode(ctx);
return (error);
} /* ixgbe_if_suspend */
@@ -4120,7 +4133,6 @@
{
struct ixgbe_softc *sc = iflib_get_softc(ctx);
device_t dev = iflib_get_dev(ctx);
- if_t ifp = iflib_get_ifp(ctx);
struct ixgbe_hw *hw = &sc->hw;
u32 wus;
@@ -4143,13 +4155,6 @@
hw->wol_enabled = false;
sc->wol_filters = 0;
- /*
- * Required after D3->D0 transition;
- * will re-advertise all previous advertised speeds
- */
- if (if_getflags(ifp) & IFF_UP)
- ixgbe_if_init(ctx);
-
return (0);
} /* ixgbe_if_resume */
diff --git a/sys/net/ifdi_if.m b/sys/net/ifdi_if.m
--- a/sys/net/ifdi_if.m
+++ b/sys/net/ifdi_if.m
@@ -61,6 +61,13 @@
return (0);
}
+ static int
+ null_power_prepare(if_ctx_t _ctx __unused,
+ enum iflib_power_event _event __unused)
+ {
+ return (0);
+ }
+
static int
null_queue_intr_enable(if_ctx_t _ctx __unused, uint16_t _qid __unused)
{
@@ -163,6 +170,16 @@
if_ctx_t _ctx;
};
+#
+# Prepare driver policy which must be established before a terminal stop used
+# for detach, suspend, or shutdown. This method must not start, stop, or alter
+# queue DMA. The ordinary lifecycle callback runs after the stop.
+#
+METHOD int power_prepare {
+ if_ctx_t _ctx;
+ enum iflib_power_event _event;
+} DEFAULT null_power_prepare;
+
METHOD int suspend {
if_ctx_t _ctx;
} DEFAULT null_int_op;
diff --git a/sys/net/iflib.h b/sys/net/iflib.h
--- a/sys/net/iflib.h
+++ b/sys/net/iflib.h
@@ -411,6 +411,13 @@
*/
#define IFLIB_FEATURE_SUB_INTERFACES 1500014
+/* Power transitions for ifdi_power_prepare(). */
+enum iflib_power_event {
+ IFLIB_POWER_DETACH,
+ IFLIB_POWER_SUSPEND,
+ IFLIB_POWER_SHUTDOWN,
+};
+
/*
* These enum values are used in iflib_needs_restart to indicate to iflib
* functions whether or not the interface needs restarting when certain events
diff --git a/sys/net/iflib.c b/sys/net/iflib.c
--- a/sys/net/iflib.c
+++ b/sys/net/iflib.c
@@ -163,6 +163,17 @@
IFLIB_DP_STOPPING,
};
+/*
+ * Power-transition state is separate from datapath ownership. It gates
+ * configuration callbacks while the device is entering or remains in low
+ * power without making any claim about driver-owned firmware or admin DMA.
+ */
+enum iflib_pm_state {
+ IFLIB_PM_ACTIVE = 0,
+ IFLIB_PM_SUSPENDING,
+ IFLIB_PM_SUSPENDED,
+};
+
static void iru_init(if_rxd_update_t iru, iflib_rxq_t rxq, uint8_t flid);
static void iflib_timer(void *arg);
static void iflib_tqg_detach(if_ctx_t ctx);
@@ -198,6 +209,7 @@
uint32_t ifc_if_flags;
uint32_t ifc_flags;
enum iflib_datapath_state ifc_datapath_state;
+ enum iflib_pm_state ifc_pm_state;
uint32_t ifc_max_fl_buf_size;
uint32_t ifc_rx_mbuf_sz;
@@ -2610,6 +2622,9 @@
bool init_failed;
sx_assert(&ctx->ifc_ctx_sx, SA_XLOCKED);
+ /* Configuration changes made during suspend take effect on resume. */
+ if (ctx->ifc_pm_state != IFLIB_PM_ACTIVE)
+ return;
KASSERT(ctx->ifc_datapath_state == IFLIB_DP_STOPPED,
("iflib init from datapath state %d", ctx->ifc_datapath_state));
ctx->ifc_datapath_state = IFLIB_DP_STARTING;
@@ -2733,6 +2748,12 @@
STATE_UNLOCK(ctx);
CTX_LOCK(ctx);
+ if (ctx->ifc_pm_state != IFLIB_PM_ACTIVE) {
+ ifmr->ifm_status = IFM_AVALID;
+ ifmr->ifm_active = IFM_ETHER | IFM_NONE;
+ CTX_UNLOCK(ctx);
+ return;
+ }
/*
* There is no need to update the admin status when it is done regularly by
* _task_fn_admin(), so only do it if that's not running. That can be quite
@@ -4325,6 +4346,10 @@
iflib_admin_task_fail_device) == 0, FAIL_POINT_NONSLEEPABLE, {});
CTX_LOCK(ctx);
+ if (ctx->ifc_pm_state != IFLIB_PM_ACTIVE) {
+ CTX_UNLOCK(ctx);
+ return;
+ }
if (!do_reset && do_reset_if_up &&
(if_getflags(ctx->ifc_ifp) & IFF_UP) != 0)
do_reset = true;
@@ -4367,6 +4392,10 @@
return;
CTX_LOCK(ctx);
+ if (ctx->ifc_pm_state != IFLIB_PM_ACTIVE) {
+ CTX_UNLOCK(ctx);
+ return;
+ }
IFDI_VFLR_HANDLE(ctx);
CTX_UNLOCK(ctx);
}
@@ -4397,6 +4426,8 @@
static void
iflib_if_init_locked(if_ctx_t ctx)
{
+ if (ctx->ifc_pm_state != IFLIB_PM_ACTIVE)
+ return;
if (ctx->ifc_datapath_state != IFLIB_DP_STOPPED)
iflib_stop(ctx);
iflib_init_locked(ctx);
@@ -4890,7 +4921,8 @@
return;
CTX_LOCK(ctx);
- IFDI_LED_FUNC(ctx, onoff);
+ if (ctx->ifc_pm_state == IFLIB_PM_ACTIVE)
+ IFDI_LED_FUNC(ctx, onoff);
CTX_UNLOCK(ctx);
}
@@ -5362,6 +5394,7 @@
queues_allocated = false;
ctx = malloc(sizeof(*ctx), M_IFLIB, M_WAITOK | M_ZERO);
ctx->ifc_datapath_state = IFLIB_DP_UNKNOWN;
+ ctx->ifc_pm_state = IFLIB_PM_ACTIVE;
if (sc == NULL) {
sc = malloc(sctx->isc_driver->size, M_IFLIB, M_WAITOK | M_ZERO);
@@ -5733,9 +5766,7 @@
{
if_t ifp = ctx->ifc_ifp;
device_t dev = ctx->ifc_dev;
-
- sysctl_ctx_free(&ctx->ifc_sysctl_ctx);
- ctx->ifc_sysctl_node = NULL;
+ int error;
/* Make sure VLANS are not using driver */
if (if_vlantrunkinuse(ifp)) {
@@ -5749,9 +5780,26 @@
}
#endif
+ /*
+ * Establish any ordering required by the terminal stop while the
+ * interface is still intact. Once this succeeds, mark the context
+ * inactive before releasing the lock so configuration tasks cannot
+ * consume partially applied policy.
+ */
+ CTX_LOCK(ctx);
+ error = IFDI_POWER_PREPARE(ctx, IFLIB_POWER_DETACH);
+ if (error != 0) {
+ CTX_UNLOCK(ctx);
+ return (error);
+ }
STATE_LOCK(ctx);
ctx->ifc_flags |= IFC_IN_DETACH;
STATE_UNLOCK(ctx);
+ ctx->ifc_pm_state = IFLIB_PM_SUSPENDING;
+ CTX_UNLOCK(ctx);
+
+ sysctl_ctx_free(&ctx->ifc_sysctl_ctx);
+ ctx->ifc_sysctl_node = NULL;
/* Unregister VLAN handlers before calling iflib_stop() */
iflib_unregister_vlan_handlers(ctx);
@@ -5852,25 +5900,108 @@
return (iflib_device_deregister(ctx));
}
+static int
+iflib_device_resume_locked(if_ctx_t ctx)
+{
+ if_t ifp;
+ int error;
+
+ sx_assert(&ctx->ifc_ctx_sx, SA_XLOCKED);
+ KASSERT(ctx->ifc_datapath_state == IFLIB_DP_STOPPED,
+ ("iflib resume with active datapath state %d",
+ ctx->ifc_datapath_state));
+ KASSERT(ctx->ifc_pm_state == IFLIB_PM_SUSPENDING ||
+ ctx->ifc_pm_state == IFLIB_PM_SUSPENDED,
+ ("iflib resume from power state %d", ctx->ifc_pm_state));
+
+ ifp = ctx->ifc_ifp;
+ error = IFDI_RESUME(ctx);
+ if (error != 0)
+ return (error);
+ ctx->ifc_pm_state = IFLIB_PM_ACTIVE;
+
+ if ((if_getflags(ifp) & IFF_UP) == 0) {
+ if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, IFF_DRV_RUNNING);
+ return (0);
+ }
+
+ iflib_init_locked(ctx);
+ return (0);
+}
+
int
iflib_device_suspend(device_t dev)
{
if_ctx_t ctx = device_get_softc(dev);
+ int error, resume_error;
CTX_LOCK(ctx);
- IFDI_SUSPEND(ctx);
+ error = IFDI_POWER_PREPARE(ctx, IFLIB_POWER_SUSPEND);
+ if (error == 0) {
+ iflib_stop(ctx);
+ ctx->ifc_pm_state = IFLIB_PM_SUSPENDING;
+ }
CTX_UNLOCK(ctx);
+ if (error != 0)
+ return (error);
- return (bus_generic_suspend(dev));
+ /* Driver configuration tasks must finish before entering low power. */
+ taskqueue_drain_all(ctx->ifc_tq);
+
+ CTX_LOCK(ctx);
+ error = IFDI_SUSPEND(ctx);
+ if (error == 0)
+ ctx->ifc_pm_state = IFLIB_PM_SUSPENDED;
+ else {
+ resume_error = iflib_device_resume_locked(ctx);
+ if (resume_error != 0)
+ device_printf(dev,
+ "failed to resume after suspend error: %d\n",
+ resume_error);
+ }
+ CTX_UNLOCK(ctx);
+ if (error != 0)
+ return (error);
+
+ error = bus_generic_suspend(dev);
+ if (error != 0) {
+ CTX_LOCK(ctx);
+ resume_error = iflib_device_resume_locked(ctx);
+ CTX_UNLOCK(ctx);
+ if (resume_error != 0)
+ device_printf(dev,
+ "failed to resume after child suspend error: %d\n",
+ resume_error);
+ }
+
+ return (error);
}
+
int
iflib_device_shutdown(device_t dev)
{
if_ctx_t ctx = device_get_softc(dev);
+ int error;
CTX_LOCK(ctx);
- IFDI_SHUTDOWN(ctx);
+ error = IFDI_POWER_PREPARE(ctx, IFLIB_POWER_SHUTDOWN);
+ if (error == 0) {
+ iflib_stop(ctx);
+ ctx->ifc_pm_state = IFLIB_PM_SUSPENDING;
+ }
CTX_UNLOCK(ctx);
+ if (error != 0)
+ return (error);
+
+ taskqueue_drain_all(ctx->ifc_tq);
+
+ CTX_LOCK(ctx);
+ error = IFDI_SHUTDOWN(ctx);
+ if (error == 0)
+ ctx->ifc_pm_state = IFLIB_PM_SUSPENDED;
+ CTX_UNLOCK(ctx);
+ if (error != 0)
+ return (error);
return (bus_generic_suspend(dev));
}
@@ -5880,15 +6011,20 @@
{
if_ctx_t ctx = device_get_softc(dev);
iflib_txq_t txq = ctx->ifc_txqs;
+ bool running;
+ int error, child_error;
CTX_LOCK(ctx);
- IFDI_RESUME(ctx);
- iflib_if_init_locked(ctx);
+ error = iflib_device_resume_locked(ctx);
+ running = (if_getdrvflags(ctx->ifc_ifp) & IFF_DRV_RUNNING) != 0;
CTX_UNLOCK(ctx);
- for (int i = 0; i < NTXQSETS(ctx); i++, txq++)
- iflib_txq_check_drain(txq, IFLIB_RESTART_BUDGET);
+ if (running) {
+ for (int i = 0; i < NTXQSETS(ctx); i++, txq++)
+ iflib_txq_check_drain(txq, IFLIB_RESTART_BUDGET);
+ }
- return (bus_generic_resume(dev));
+ child_error = bus_generic_resume(dev);
+ return (error != 0 ? error : child_error);
}
int

File Metadata

Mime Type
text/plain
Expires
Tue, Sep 15, 11:07 PM (7 h, 13 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
38986765
Default Alt Text
D59329.diff (15 KB)

Event Timeline