From nobody Thu Oct 29 04:58:21 2026 X-Original-To: dev-commits-src-branches@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4jFX714SCQz6xmKp for ; Thu, 29 Oct 2026 04:58:21 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by mx1.freebsd.org (Postfix) with ESMTPS id 4jFX713FJ1z4bj8 for ; Thu, 29 Oct 2026 04:58:21 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1793249901; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=Yq8K+N1Lb4PucVSAnRBEWrrpAzB8p1NK3Nlusmc8hcU=; b=W1SBo0TjAV6zvjLdhHvNdAiAR9nwsP4t1+y6TquD32RBJHDs5qHNVSj65g8TZsLIuMcZ4y ZarhhMbxRcZR166Ao9bL3jZOxnyR2G6aXRufth1HWU9zFqjtSt9J7yYxxp1ghB45FRymiF 99QjnAHYezzFoT0doZ0PV8YUgsIH3H0YEkhbB0xt4fSvM/uQZWY9TVQfXwnScIsL996qRO q9mQd6B66dbRg3WkdJkFKSrrbB7N2ofa5uvKvFj/4K6M6IFGSt+6tzLvypO7xu4CTAgx58 Q97fzoZWgCtQFgDej/KUs6a3fTwGenrJvRTBN5QViAO/kU4wdWAvY06ykVc5IA== ARC-Seal: i=1; a=rsa-sha256; d=freebsd.org; s=dkim; cv=none; t=1793249901; b=X4LvgjE6kG+6NKsNufYrlgAo5llMULkox0WC2wHlJCJ0hP7SlB60yAoNOLPmC+FtDCkiSh 0wxtiglHx1jYDo0+VxLADIh2xLnlhIMwHPOG8YuFetbqkopAklWME57ZAidPOR6EZMlbTB sHkXVI2o0X8xsCbVPZEmOWP3zkRya8C7UvHSjyDZvaCgW9gugyBuT+fPwRv6RU5YmV+GGj q8CnUqFKjaGnLyyRTUhRabD7W0sIDcjqUbMN5o5pYBihM1EZDTNXHsSEB9uY0UOKWVnmRG QUC7l4YajsLIf84BzF1/Sq/y1DPaRxMj96KeTHYGYkDJ7f8g0Ru3VBXhO7JUCQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1793249901; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=Yq8K+N1Lb4PucVSAnRBEWrrpAzB8p1NK3Nlusmc8hcU=; b=D74J2kifZ5y+nAWcULZY1MFWg+8BnXDb9vQmIs1N8FhuRU87pUiZ+/eKySXZLJA2iAND+M Jtomdfe+FRBS66eMQ/exd4DGTzx/v2puZPXBw+UjXiIjCXLiURW8HthSdwpxcQes4ILs5r SRvrv9w9oM9KFVtM0r9jCbOa6Mtz6UNC4yZ++rr5RYYcySgX9SSnMu6cbefSbqDPUEDmWB qvXCeX7f3Fi4mnDYcluCMPjpRiitLBvylxhtutgR3rWrtyZRTY9HzaqraZLXogxG+wFb0c H85ixCI4EOxq5j+eJbW1ohjpsvwmRLNfxFmbICj1kpUf6ZthIJDF8uFuYK9GTw== ARC-Authentication-Results: i=1; mx1.freebsd.org; none Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4jFX712th2zP9G for ; Thu, 29 Oct 2026 04:58:21 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 30cfa by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Thu, 29 Oct 2026 04:58:21 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org From: Kevin Bowling Subject: git: e00102818cdd - stable/15 - iflib: Own queue quiescence during power transitions List-Archive: https://lists.freebsd.org/archives/dev-commits-src-branches X-BeenThere: dev-commits-src-branches@freebsd.org Sender: owner-dev-commits-src-branches@FreeBSD.org List-Id: Commits to the stable branches of the FreeBSD src repository List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: kbowling X-Git-Repository: src X-Git-Refname: refs/heads/stable/15 X-Git-Reftype: branch X-Git-Commit: e00102818cddebb7146181a11903a5c7f441ae46 Auto-Submitted: auto-generated Date: Thu, 29 Oct 2026 04:58:21 +0000 Message-Id: <6ae2d26d.30cfa.3c0762ea@gitrepo.freebsd.org> The branch stable/15 has been updated by kbowling: URL: https://cgit.FreeBSD.org/src/commit/?id=e00102818cddebb7146181a11903a5c7f441ae46 commit e00102818cddebb7146181a11903a5c7f441ae46 Author: Kevin Bowling AuthorDate: 2026-08-26 09:21:55 +0000 Commit: Kevin Bowling CommitDate: 2026-10-29 04:57:44 +0000 iflib: Own queue quiescence during power transitions Perform a terminal datapath stop before suspend and shutdown callbacks, then drain the private configuration taskqueue before entering low power. Track power state independently of queue ownership and prevent built-in admin, IOV, LED, and media-status callbacks from accessing a suspended device. Restore driver-specific state while the datapath remains stopped. Initialize it exactly once on resume when the interface is administratively up, and keep an administratively-down interface stopped. Roll back the driver when suspend or child suspension fails. Add ifdi_power_prepare() for policy which must be established before the terminal stop. Use it to snapshot ixgbe(4) wake policy and preserve X550EM PHY ordering, and remove the duplicate stop from aq(4). Reviewed by: iflib (gallatin) Sponsored by: BBOX.io Differential Revision: https://reviews.freebsd.org/D59329 (cherry picked from commit 2b2cc52236f462f1ca2b86581c8115252fb1a834) --- share/man/man9/Makefile | 2 + share/man/man9/iflibdd.9 | 42 +++++++++++- sys/dev/aq/aq_main.c | 3 - sys/dev/ice/if_ice_iflib.c | 4 +- sys/dev/ixgbe/if_ix.c | 39 ++++++----- sys/net/ifdi_if.m | 17 +++++ sys/net/iflib.c | 160 +++++++++++++++++++++++++++++++++++++++++---- sys/net/iflib.h | 7 ++ 8 files changed, 237 insertions(+), 37 deletions(-) diff --git a/share/man/man9/Makefile b/share/man/man9/Makefile index eafe6568fe16..7cdfffc47e97 100644 --- a/share/man/man9/Makefile +++ b/share/man/man9/Makefile @@ -1314,12 +1314,14 @@ MLINKS+=iflibdd.9 ifdi_attach_pre.9 \ 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 index d42ad6d2872e..3706271352ae 100644 --- 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 @@ Optional function called by the VLAN config eventhandler. 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 index 04ce3a055aa0..0ab05d949120 100644 --- a/sys/dev/aq/aq_main.c +++ b/sys/dev/aq/aq_main.c @@ -566,12 +566,9 @@ aq_if_suspend(if_ctx_t ctx) 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 index d72bce8b5468..a507eea5ea4b 100644 --- a/sys/dev/ice/if_ice_iflib.c +++ b/sys/dev/ice/if_ice_iflib.c @@ -3378,7 +3378,7 @@ ice_if_i2c_req(if_ctx_t ctx, struct ifi2creq *req) * 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 @@ ice_if_suspend(if_ctx_t ctx) * 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 index 2524c5caeb60..77152b90a987 100644 --- a/sys/dev/ixgbe/if_ix.c +++ b/sys/dev/ixgbe/if_ix.c @@ -177,6 +177,7 @@ static void *ixgbe_register(device_t); 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); @@ -223,7 +224,7 @@ static void ixgbe_enable_queue(struct ixgbe_softc *, u32); 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 *); @@ -352,6 +353,7 @@ static device_method_t ixgbe_if_methods[] = { 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), @@ -3905,7 +3907,7 @@ ixgbe_if_detach(if_ctx_t ctx) 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); @@ -3983,13 +3985,27 @@ ixgbe_configure_wakeup_mta(if_ctx_t ctx) 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; @@ -3997,10 +4013,7 @@ ixgbe_setup_low_power_mode(if_ctx_t ctx, bool arm_wake) 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 && @@ -4076,7 +4089,7 @@ ixgbe_if_shutdown(if_ctx_t ctx) 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); @@ -4095,7 +4108,7 @@ ixgbe_if_suspend(if_ctx_t ctx) 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 */ @@ -4110,7 +4123,6 @@ ixgbe_if_resume(if_ctx_t ctx) { 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; @@ -4133,13 +4145,6 @@ ixgbe_if_resume(if_ctx_t ctx) 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 index 7f49de5457cd..c9806a3c9b1d 100644 --- a/sys/net/ifdi_if.m +++ b/sys/net/ifdi_if.m @@ -60,6 +60,13 @@ CODE { 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) { @@ -149,6 +156,16 @@ METHOD int detach { 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.c b/sys/net/iflib.c index c62bf10370f2..b2a6a68b69f1 100644 --- a/sys/net/iflib.c +++ b/sys/net/iflib.c @@ -162,6 +162,17 @@ enum iflib_datapath_state { 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); @@ -195,6 +206,7 @@ struct iflib_ctx { 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; @@ -2487,6 +2499,9 @@ iflib_init_locked(if_ctx_t ctx) 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; @@ -2610,6 +2625,12 @@ iflib_media_status(if_t ifp, struct ifmediareq *ifmr) 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 @@ -4190,6 +4211,10 @@ _task_fn_admin(void *context, int pending) 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; @@ -4232,6 +4257,10 @@ _task_fn_iov(void *context, int pending) return; CTX_LOCK(ctx); + if (ctx->ifc_pm_state != IFLIB_PM_ACTIVE) { + CTX_UNLOCK(ctx); + return; + } IFDI_VFLR_HANDLE(ctx); CTX_UNLOCK(ctx); } @@ -4262,6 +4291,8 @@ iflib_sysctl_int_delay(SYSCTL_HANDLER_ARGS) 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); @@ -4724,7 +4755,8 @@ _task_fn_led(void *context, int pending __unused) 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); } @@ -5196,6 +5228,7 @@ iflib_device_register(device_t dev, void *sc, if_shared_ctx_t sctx, if_ctx_t *ct 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); @@ -5566,9 +5599,7 @@ iflib_device_deregister(if_ctx_t ctx) { 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)) { @@ -5582,9 +5613,26 @@ iflib_device_deregister(if_ctx_t ctx) } #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); @@ -5685,25 +5733,108 @@ iflib_device_detach(device_t dev) 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)); } @@ -5713,15 +5844,20 @@ iflib_device_resume(device_t dev) { 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 diff --git a/sys/net/iflib.h b/sys/net/iflib.h index a2bc9c1f8835..844ee1bb9594 100644 --- a/sys/net/iflib.h +++ b/sys/net/iflib.h @@ -400,6 +400,13 @@ typedef enum { */ #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