Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F171961639
D59329.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
15 KB
Referenced Files
None
Subscribers
None
D59329.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D59329: iflib: Own queue quiescence during power transitions
Attached
Detach File
Event Timeline
Log In to Comment