From nobody Sun Oct 11 00:53:49 2026 X-Original-To: dev-commits-src-all@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 4j2MYH0VQ9z6w7mD for ; Sun, 11 Oct 2026 00:53:55 +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 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "YR2" (not verified)) by mx1.freebsd.org (Postfix) with ESMTPS id 4j2MYH0BWvz4vpf for ; Sun, 11 Oct 2026 00:53:55 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1791680035; 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=IAha4aeys2iF5oTcRCJK772enmI/WPZwQv9rDljH6rI=; b=nu8s6HrFJikRv5khzZom2sDYdAaw04MrT0ZRemsNABPznWSsLlVAfFBplHs3Vq66CxR5uY mcPgBTBCazCraGqxlTDSMlCUh4muWvAbO6zoPPGmS8DH8aQ9REvo/BFPNn6UtZIleiy9Er 7X+rJjESzFd7wynMB3P/Hq1aKOv/d5gsdRqIOLwjl4mIOlJeYr+h0h8AgFfmSq5WgRptCZ S7AOrGz9/tFffB+U3udDc1buQoyXBpppgIF+Pum9p2htkZLRBp932hwHR5Qe+bwrjhVF2U WoWWYwYGvH/VBVNNG8kLS94ra1TSiHQmcHSruORFWC44pQTuNuQJAMkNrhjzYA== ARC-Seal: i=1; a=rsa-sha256; d=freebsd.org; s=dkim; cv=none; t=1791680035; b=byTrpfhmk2Y8UT2N5H9XuLGGSX/CvGLAnWw2kd5t4A698KgNk1aGVq1QsJ9M1ZawlYwtqN 6bMz891wzIL6KVFq+dsC4NGjL8/rKxubuHbX/LCbS7RTjvkFdbUa/C6sVC+afYcd/3M4Z0 WdNgsTC7ILMcAqS7uSIOt3UVUJomGw9Ao5uVjXhpod1AcVvZclHo3CZEcVdb+I3+MG5a2q IWjqUgRPzxD7551TDM9KaITZubHZB1s5xanxxTQhJfcQfi2+22b3n4f978ZnleQ5xa5qCP pNnfvNkI0NZnNumASh09DAAW2l0to80k+uzT6vSFagoOlZtw2o+GPiequlsqPg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1791680035; 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=IAha4aeys2iF5oTcRCJK772enmI/WPZwQv9rDljH6rI=; b=ManyNytUDRwEX7qVwCh7w+NJ6csHiN/XhTlr4+FcXUh7UoZia2c4ZkROvqjj1N8jhICszh OTkfVIzWsHBkS4jRpJIQCiluRHBFQjTF0EsY9isR9n9zj1GNHvo5boDGXblN/js6Pv3vOs twpDbI+XPN0ofvJWbH/ltKxIAoyzHM88yfbrlq8evZ9gK1r2x9hHlSZOzfKjHvgiFK1xJy I5+equjdP4znNjttmn2l5aekg/ssEvYQIbZV5wAMhvULTEs1FnhFTc2UrFss5myIj9GVzb siQzYQ5Gkd6ZY8ucTErFt/oqY8uyTVh5VuUs4XvXOQgYL5CVKtTsZApl5OU5Hw== 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 4j2MYG6LvzzHyd for ; Sun, 11 Oct 2026 00:53:54 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 32564 by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Sun, 11 Oct 2026 00:53:49 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org From: Kevin Bowling Subject: git: 2f49578f3d6a - stable/15 - e1000: Recover from igb(4) controller DEV_RST List-Archive: https://lists.freebsd.org/archives/dev-commits-src-all X-BeenThere: dev-commits-src-all@freebsd.org Sender: owner-dev-commits-src-all@FreeBSD.org List-Id: Commit messages for all branches of the 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: 2f49578f3d6a1e440f6392136db6e920309511e3 Auto-Submitted: auto-generated Date: Sun, 11 Oct 2026 00:53:49 +0000 Message-Id: <6acade1d.32564.61c1cdf5@gitrepo.freebsd.org> The branch stable/15 has been updated by kbowling: URL: https://cgit.FreeBSD.org/src/commit/?id=2f49578f3d6a1e440f6392136db6e920309511e3 commit 2f49578f3d6a1e440f6392136db6e920309511e3 Author: Kevin Bowling AuthorDate: 2026-08-29 02:44:59 +0000 Commit: Kevin Bowling CommitDate: 2026-10-11 00:53:18 +0000 e1000: Recover from igb(4) controller DEV_RST CTRL.DEV_RST resets every port on an 82580 and newer igb device. Hardware reports the event to each affected function through ICR.DRSTA and requires software to reinitialize the port registers and descriptor rings. The driver neither enabled nor handled this cause, so a reset initiated by another function could leave a running interface using stale state. In FreeBSD, we do not currently send this, but other OSes including Linux do, so a PF passed through to such a guest or FreeBSD as a guest running with a passthrough PF on the same controller can be wedged. Enable DRSTA for 82580 and newer PFs in both MSI-X and shared MSI/legacy modes. Bit 30 is reserved on 82575 and is the TCP timer on 82576, so leave it masked on those parts. Latch the event without programming port registers from the interrupt filter. Defer the iflib reset request to admin-task context because the request takes STATE_LOCK. Use IAM to auto-mask shared interrupts on the first ICR read. Keep admin, queue-vector, and interrupt-rearm work quiesced until initialization succeeds. Follow the device reset handshake before the first CTX-owned register programming: wait for GCR to report that the device reset and pending PCIe transactions have completed, then acknowledge STATUS.DEV_RST_SET. Also verify EEPROM autoload and PF reset completion on I350 and newer parts. This follows the Intel 82580 Datasheet, section 4.3.2, and the Intel I350 Datasheet, section 4.3.4. Detect a completed reset from STATUS even when the interface was down. A bounded timeout falls back to the requested port reset. Retain staged reset state through the complete initialization. Check ICR.DRSTA, GCR, and STATUS.DEV_RST_SET after all registers and rings have been rebuilt. If another reset arrived while interrupts were masked, reject the incomplete initialization and repeat the handshake. If both MMIO and PCI configuration space have disappeared, leave the interface stopped rather than queueing an endless recovery loop. Validated on an I210 with INVARIANTS and WITNESS. Injecting CTRL.DEV_RST while igb0 was running recovered through a full iflib initialization in both MSI-X and MSI modes without a panic. STATUS.DEV_RST_SET and GCR.DEV_RST_IN_PROGRESS cleared, enabled DMA coalescing was restored. Injecting the reset while igb0 was down left DEV_RST_SET latched; the first ifconfig up consumed it. On a dual-port 82580, synthetic DRSTA injection produced one complete reinitialization in four-queue MSI-X and shared-MSI modes. Five repeated events produced five clean reinitializations without a panic or watchdog. A raw CTRL.DEV_RST test is not counted because Intel's shared code deliberately avoids that unreliable operation on 82580. On an I350, a real device-wide reset initiated by a sibling function produced one reinitialization while igb0 was running and restored its carrier and enabled state. With igb0 down, STATUS.DEV_RST_SET remained latched until the first up, which consumed it and restored the correct state. The host remained healthy in both cases. Sponsored by: BBOX.io (cherry picked from commit 4674cc87a8d013b0f2337329c4314cb9fc29e02b) --- sys/dev/e1000/e1000_82575.h | 1 + sys/dev/e1000/if_em.c | 301 +++++++++++++++++++++++++++++++++++++++++--- sys/dev/e1000/if_em.h | 1 + 3 files changed, 288 insertions(+), 15 deletions(-) diff --git a/sys/dev/e1000/e1000_82575.h b/sys/dev/e1000/e1000_82575.h index 91ac3fe9fcbf..69c195cc199d 100644 --- a/sys/dev/e1000/e1000_82575.h +++ b/sys/dev/e1000/e1000_82575.h @@ -55,6 +55,7 @@ #define E1000_RAR_ENTRIES_I350 32 #define E1000_SW_SYNCH_MB 0x00000100 #define E1000_STAT_DEV_RST_SET 0x00100000 +#define E1000_GCR_DEV_RST_IN_PROGRESS 0x80000000 struct e1000_adv_data_desc { __le64 buffer_addr; /* Address of the descriptor's data buffer */ diff --git a/sys/dev/e1000/if_em.c b/sys/dev/e1000/if_em.c index c482bdb0ccd4..94e952f0cf46 100644 --- a/sys/dev/e1000/if_em.c +++ b/sys/dev/e1000/if_em.c @@ -444,6 +444,11 @@ static int igb_if_rx_queue_intr_enable(if_ctx_t, uint16_t); static int igb_if_tx_queue_intr_enable(if_ctx_t, uint16_t); static void em_handle_fatal_error_intr(struct e1000_softc *, u32); static bool em_handle_fatal_error_admin(struct e1000_softc *); +static u32 igb_device_reset_intr_mask(struct e1000_softc *); +static bool igb_device_reset_pending(struct e1000_softc *); +static bool igb_handle_device_reset(struct e1000_softc *, u32); +static void igb_prepare_device_reset(struct e1000_softc *); +static bool igb_finish_device_reset(struct e1000_softc *, u32); static void em_prepare_fatal_error_reset(struct e1000_softc *); static void em_finish_fatal_error_reset(struct e1000_softc *); static void em_configure_peind_memory_errors(struct e1000_softc *); @@ -502,6 +507,15 @@ enum em_fatal_error_state { EM_FATAL_ERROR_RESET_PREPARED, }; +enum igb_device_reset_state { + IGB_DEVICE_RESET_NONE, + IGB_DEVICE_RESET_DETECTED, + IGB_DEVICE_RESET_REQUESTED, + IGB_DEVICE_RESET_PREPARED, +}; + +#define IGB_DEVICE_RESET_TIMEOUT_MS 100 + /* MSI-X handlers */ static int em_if_msix_intr_assign(if_ctx_t, int); static int em_msix_link(void *); @@ -1615,6 +1629,7 @@ em_if_init(if_ctx_t ctx) if_softc_ctx_t scctx = sc->shared; if_t ifp = iflib_get_ifp(ctx); struct em_tx_queue *tx_que; + u32 icr; int i; INIT_DEBUGOUT("em_if_init: begin"); @@ -1707,10 +1722,6 @@ em_if_init(if_ctx_t ctx) if (sc->hw.mac.type >= igb_mac_min) igb_initialize_interrupt_rate(sc); - /* this clears any pending interrupts */ - E1000_READ_REG(&sc->hw, E1000_ICR); - E1000_WRITE_REG(&sc->hw, E1000_ICS, E1000_ICS_LSC); - /* AMT based hardware can now take control from firmware */ if (sc->has_manage && sc->has_amt) em_get_hw_control(sc); @@ -1726,6 +1737,18 @@ em_if_init(if_ctx_t ctx) em_configure_peind_memory_errors(sc); em_configure_82575_memory_errors(sc); em_configure_82580_memory_errors(sc); + + /* + * Drain stale causes only after register reconstruction is complete. + * DRSTA and DEV_RST_SET together close the window in which another + * device reset can arrive while interrupts are masked. + */ + icr = E1000_READ_REG(&sc->hw, E1000_ICR); + if (igb_finish_device_reset(sc, icr)) { + iflib_init_failed(ctx); + return; + } + E1000_WRITE_REG(&sc->hw, E1000_ICS, E1000_ICS_LSC); } /* @@ -2595,6 +2618,203 @@ em_handle_fatal_error_admin(struct e1000_softc *sc) return (true); } +/* + * ICR bit 30 is reserved on 82575 and is the TCP timer on 82576. It becomes + * the Device Reset Asserted interrupt starting with 82580. + */ +static u32 +igb_device_reset_intr_mask(struct e1000_softc *sc) +{ + + return (!sc->vf_ifp && sc->hw.mac.type >= e1000_82580 ? + E1000_IMS_DRSTA : 0); +} + +/* Keep interrupt-side work quiesced until device-reset recovery completes. */ +static bool +igb_device_reset_pending(struct e1000_softc *sc) +{ + + return (!sc->vf_ifp && igb_device_reset_intr_mask(sc) != 0 && + atomic_load_acq_32(&sc->device_reset_state) != + IGB_DEVICE_RESET_NONE); +} + +/* + * CTRL.DEV_RST resets every port in the device. ICR.DRSTA tells the other + * ports that their registers and descriptor rings must be reinitialized. + */ +static bool +igb_handle_device_reset(struct e1000_softc *sc, u32 icr) +{ + u32 state; + + if (sc->vf_ifp || igb_device_reset_intr_mask(sc) == 0 || + (icr & E1000_ICR_DRSTA) == 0) + return (false); + state = atomic_swap_32(&sc->device_reset_state, + IGB_DEVICE_RESET_DETECTED); + if (state == IGB_DEVICE_RESET_DETECTED) + return (true); + + iflib_admin_intr_deferred(sc->ctx); + return (true); +} + +/* + * A device reset can leave a sibling port accessible before its internal + * reset and PCIe transactions have completed. For 82580 and newer parts, + * wait for that device-wide reset to finish and acknowledge it before any + * ordinary port register programming. I350 and newer parts also publish + * explicit EEPROM autoload and PF-reset completion indications. + * + * The wait is bounded because the only useful fallback for a controller + * that never completes the device reset is the port reset already requested + * by the interrupt handler. + */ +static void +igb_prepare_device_reset(struct e1000_softc *sc) +{ + struct e1000_hw *hw; + u32 state; + u32 eecd, gcr, status; + int i; + + hw = &sc->hw; + state = atomic_load_acq_32(&sc->device_reset_state); + if (state != IGB_DEVICE_RESET_DETECTED && + state != IGB_DEVICE_RESET_REQUESTED && + hw->mac.type >= e1000_82580) { + /* + * A reset can start while this interface has interrupts disabled. + * GCR is the documented gate before ordinary port accesses. STATUS + * also detects a reset that completed while this interface was down + * or after an earlier preparation pass. + */ + gcr = E1000_READ_REG(hw, E1000_GCR); + if (gcr != 0xffffffff && + (gcr & E1000_GCR_DEV_RST_IN_PROGRESS) != 0) { + atomic_store_rel_32(&sc->device_reset_state, + IGB_DEVICE_RESET_DETECTED); + state = IGB_DEVICE_RESET_DETECTED; + } else if (gcr != 0xffffffff) { + status = E1000_READ_REG(hw, E1000_STATUS); + if (status != 0xffffffff && + (status & E1000_STAT_DEV_RST_SET) != 0) { + atomic_store_rel_32(&sc->device_reset_state, + IGB_DEVICE_RESET_DETECTED); + state = IGB_DEVICE_RESET_DETECTED; + } + } + } + if (state != IGB_DEVICE_RESET_DETECTED && + state != IGB_DEVICE_RESET_REQUESTED) + return; + + if (hw->mac.type >= e1000_82580) { + for (i = 0; i < IGB_DEVICE_RESET_TIMEOUT_MS; i++) { + gcr = E1000_READ_REG(hw, E1000_GCR); + if (gcr != 0xffffffff && + (gcr & E1000_GCR_DEV_RST_IN_PROGRESS) == 0) + break; + msec_delay(1); + } + if (i == IGB_DEVICE_RESET_TIMEOUT_MS) { + device_printf(sc->dev, + "device-wide reset did not complete; " + "attempting port reset\n"); + goto prepared; + } + + /* STATUS.DEV_RST_SET is write-one-to-clear. */ + E1000_WRITE_REG(hw, E1000_STATUS, E1000_STAT_DEV_RST_SET); + + if (hw->mac.type >= e1000_i350) { + for (i = 0; i < IGB_DEVICE_RESET_TIMEOUT_MS; i++) { + eecd = E1000_READ_REG(hw, E1000_EECD); + status = E1000_READ_REG(hw, E1000_STATUS); + if (eecd != 0xffffffff && status != 0xffffffff && + (eecd & E1000_EECD_AUTO_RD) != 0 && + (status & E1000_STATUS_RST_DONE) != 0) + break; + msec_delay(1); + } + if (i == IGB_DEVICE_RESET_TIMEOUT_MS) + device_printf(sc->dev, + "device-wide reset did not finish EEPROM " + "autoload or port reset; attempting port " + "reset\n"); + } + } + +prepared: + atomic_store_rel_32(&sc->device_reset_state, + IGB_DEVICE_RESET_PREPARED); +} + +/* + * A second device reset can arrive while the port is being initialized. + * Leave its status latched for the next preparation pass and do not let + * iflib publish this incomplete initialization as a running datapath. + */ +static bool +igb_finish_device_reset(struct e1000_softc *sc, u32 icr) +{ + bool reset_again; + u32 gcr, state, status; + + if (igb_device_reset_intr_mask(sc) == 0) + return (false); + + state = atomic_load_acq_32(&sc->device_reset_state); + reset_again = icr != 0xffffffff && + (icr & E1000_ICR_DRSTA) != 0; + if (sc->hw.mac.type >= e1000_82580) { + gcr = E1000_READ_REG(&sc->hw, E1000_GCR); + if (gcr != 0xffffffff && + (gcr & E1000_GCR_DEV_RST_IN_PROGRESS) != 0) + reset_again = true; + status = E1000_READ_REG(&sc->hw, E1000_STATUS); + if (status == 0xffffffff && + state != IGB_DEVICE_RESET_NONE) { + /* + * MMIO can disappear briefly while SR-IOV is changing, but + * config space remains readable. If both are gone, retain the + * stopped state without queueing an endless reset loop. + */ + if (pci_read_config(sc->dev, PCIR_VENDOR, 2) == 0xffff) { + atomic_store_rel_32(&sc->device_reset_state, + IGB_DEVICE_RESET_DETECTED); + device_printf(sc->dev, + "device unavailable after device-wide reset; " + "leaving interface stopped\n"); + return (true); + } + reset_again = true; + } else if (status != 0xffffffff && + (status & E1000_STAT_DEV_RST_SET) != 0) + reset_again = true; + } + if (state == IGB_DEVICE_RESET_DETECTED || + state == IGB_DEVICE_RESET_REQUESTED) + reset_again = true; + if (!reset_again) { + if (state == IGB_DEVICE_RESET_PREPARED && + !atomic_cmpset_rel_32(&sc->device_reset_state, + IGB_DEVICE_RESET_PREPARED, IGB_DEVICE_RESET_NONE)) + return (true); + return (false); + } + + state = atomic_swap_32(&sc->device_reset_state, + IGB_DEVICE_RESET_DETECTED); + if (state != IGB_DEVICE_RESET_DETECTED) { + iflib_request_reset_if_up(sc->ctx); + iflib_admin_intr_deferred(sc->ctx); + } + return (true); +} + /* * A PCIe-region parity failure stops PCIe and DMA traffic. I350, I354, * I210, and I211 require a port reset before master disable in this case. @@ -2798,14 +3018,18 @@ em_intr(void *arg) if (hw->mac.type >= e1000_82571 && (reg_icr & E1000_ICR_INT_ASSERTED) == 0) return FILTER_STRAY; + if (igb_handle_device_reset(sc, reg_icr)) + return (FILTER_HANDLED); + if (igb_device_reset_pending(sc)) + return (FILTER_HANDLED); /* - * Only MSI-X interrupts have one-shot behavior by taking advantage - * of the EIAC register. Thus, explicitly disable interrupts. This - * also works around the MSI message reordering errata on certain - * systems. + * IAM auto-masks igb shared interrupts when ICR is read. Older em + * hardware still needs an explicit disable, which also works around + * MSI message reordering errata on certain systems. */ - IFDI_INTR_DISABLE(ctx); + if (sc->vf_ifp || hw->mac.type < igb_mac_min) + IFDI_INTR_DISABLE(ctx); /* Link status change */ if (reg_icr & (E1000_ICR_RXSEQ | E1000_ICR_LSC)) @@ -2848,6 +3072,8 @@ igb_if_rx_queue_intr_enable(if_ctx_t ctx, uint16_t rxqid) struct e1000_softc *sc = iflib_get_softc(ctx); struct em_rx_queue *rxq = &sc->rx_queues[rxqid]; + if (igb_device_reset_pending(sc)) + return (0); E1000_WRITE_REG(&sc->hw, E1000_EIMS, rxq->eims); return (0); } @@ -2858,6 +3084,8 @@ igb_if_tx_queue_intr_enable(if_ctx_t ctx, uint16_t txqid) struct e1000_softc *sc = iflib_get_softc(ctx); struct em_tx_queue *txq = &sc->tx_queues[txqid]; + if (igb_device_reset_pending(sc)) + return (0); E1000_WRITE_REG(&sc->hw, E1000_EIMS, txq->eims); return (0); } @@ -2876,6 +3104,8 @@ em_msix_que(void *arg) ++que->irqs; + if (igb_device_reset_pending(sc)) + return (FILTER_HANDLED); em_newitr(sc, que, rxr); return (FILTER_SCHEDULE_THREAD); @@ -2895,6 +3125,14 @@ em_msix_link(void *arg) ++sc->link_irq; MPASS(sc->hw.back != NULL); reg_icr = E1000_READ_REG(&sc->hw, E1000_ICR); + if (igb_device_reset_pending(sc)) + return (FILTER_HANDLED); + + /* Ignore transient all-ones MMIO reads as interrupt causes. */ + if (__predict_false(reg_icr == 0xffffffff)) + goto rearm; + if (igb_handle_device_reset(sc, reg_icr)) + return (FILTER_HANDLED); if (reg_icr & E1000_ICR_RXO) sc->rx_overruns++; @@ -2903,10 +3141,12 @@ em_msix_link(void *arg) em_handle_link(sc->ctx); em_handle_fatal_error_intr(sc, reg_icr); +rearm: /* Re-arm unconditionally */ if (sc->hw.mac.type >= igb_mac_min) { E1000_WRITE_REG(&sc->hw, E1000_IMS, - E1000_IMS_LSC | em_fatal_error_intr_mask(sc)); + E1000_IMS_LSC | igb_device_reset_intr_mask(sc) | + em_fatal_error_intr_mask(sc)); E1000_WRITE_REG(&sc->hw, E1000_EIMS, sc->link_mask); } else if (sc->hw.mac.type == e1000_82574) { E1000_WRITE_REG(&sc->hw, E1000_IMS, @@ -3183,6 +3423,24 @@ em_if_update_admin_status(if_ctx_t ctx) if (em_handle_fatal_error_admin(sc)) return; + /* A sibling-port reset invalidated the port registers. */ + if (atomic_cmpset_acq_32(&sc->device_reset_state, + IGB_DEVICE_RESET_DETECTED, IGB_DEVICE_RESET_REQUESTED)) { + if (sc->link_state == EM_LINK_STATE_UP) + iflib_link_state_change(ctx, LINK_STATE_DOWN, 0); + sc->link_speed = 0; + sc->link_duplex = 0; + sc->link_state = EM_LINK_STATE_DOWN_RESET_PENDING; + /* Request the reset here; interrupt filters cannot take STATE_LOCK. */ + iflib_request_reset_if_up(ctx); + /* Re-enter the admin task so it observes the reset request. */ + iflib_admin_intr_deferred(ctx); + return; + } + if (atomic_load_acq_32(&sc->device_reset_state) != + IGB_DEVICE_RESET_NONE) + return; + link_check = thstat = ctrl = 0; /* Get the cached link value or read phy for real */ switch (hw->phy.media_type) { @@ -5472,16 +5730,24 @@ igb_if_intr_enable(if_ctx_t ctx) struct e1000_hw *hw = &sc->hw; u32 mask; + if (igb_device_reset_pending(sc)) + return; if (__predict_true(sc->intr_type == IFLIB_INTR_MSIX)) { mask = (sc->que_mask | sc->link_mask); E1000_WRITE_REG(hw, E1000_EIAC, mask); E1000_WRITE_REG(hw, E1000_EIAM, mask); E1000_WRITE_REG(hw, E1000_EIMS, mask); E1000_WRITE_REG(hw, E1000_IMS, - E1000_IMS_LSC | em_fatal_error_intr_mask(sc)); - } else - E1000_WRITE_REG(hw, E1000_IMS, - IMS_ENABLE_MASK | em_fatal_error_intr_mask(sc)); + E1000_IMS_LSC | igb_device_reset_intr_mask(sc) | + em_fatal_error_intr_mask(sc)); + } else { + mask = IMS_ENABLE_MASK | igb_device_reset_intr_mask(sc) | + em_fatal_error_intr_mask(sc); + /* Reading ICR masks every shared interrupt before the filter runs. */ + if (!sc->vf_ifp) + E1000_WRITE_REG(hw, E1000_IAM, mask); + E1000_WRITE_REG(hw, E1000_IMS, mask); + } E1000_WRITE_FLUSH(hw); } @@ -5491,10 +5757,15 @@ igb_if_intr_disable(if_ctx_t ctx) struct e1000_softc *sc = iflib_get_softc(ctx); struct e1000_hw *hw = &sc->hw; + /* This is the first CTX-owned register access after ICR.DRSTA. */ + if (!sc->vf_ifp) + igb_prepare_device_reset(sc); + if (__predict_true(sc->intr_type == IFLIB_INTR_MSIX)) { E1000_WRITE_REG(hw, E1000_EIMC, 0xffffffff); E1000_WRITE_REG(hw, E1000_EIAC, 0); - } + } else if (!sc->vf_ifp) + E1000_WRITE_REG(hw, E1000_IAM, 0); E1000_WRITE_REG(hw, E1000_IMC, 0xffffffff); E1000_WRITE_FLUSH(hw); } diff --git a/sys/dev/e1000/if_em.h b/sys/dev/e1000/if_em.h index ee1cd0dea211..6be12edd6e56 100644 --- a/sys/dev/e1000/if_em.h +++ b/sys/dev/e1000/if_em.h @@ -620,6 +620,7 @@ struct e1000_softc { int tso_automasked; u32 phy_hang_count; u32 stats_pending; + u32 device_reset_state; u32 fatal_error_state; u32 fatal_error_icr; u32 fatal_error_pbeccsts;