Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 31 Jul 2026 09:43:51 +0000
From:      Kevin Bowling <kbowling@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org
Cc:        Michael Adler <madler@tapil.com>
Subject:   git: 9d30fd353dd3 - main - igc: Disable ASPM L1.2 on I226 to prevent RX stalls
Message-ID:  <6a6c6e57.32455.6b6ebed8@gitrepo.freebsd.org>

index | next in thread | raw e-mail

The branch main has been updated by kbowling:

URL: https://cgit.FreeBSD.org/src/commit/?id=9d30fd353dd3eacb095ce98cb9c91ec015b9af64

commit 9d30fd353dd3eacb095ce98cb9c91ec015b9af64
Author:     Michael Adler <madler@tapil.com>
AuthorDate: 2026-07-09 17:02:37 +0000
Commit:     Kevin Bowling <kbowling@FreeBSD.org>
CommitDate: 2026-07-31 09:33:54 +0000

    igc: Disable ASPM L1.2 on I226 to prevent RX stalls
    
    I226 parts advertise support for the PCIe L1.2 link substate, but a
    hardware erratum makes the exit latency from that low-power state
    longer than the packet buffer can absorb under load. This stalls the
    inbound packet stream. Disabling ASPM system-wide (BIOS or OS ASPM
    policy) does not fix it. The L1.2 enable bit must be cleared directly
    in the device's own PCIe L1 PM extended capability.
    
    Add igc_is_device_id_i226() to identify affected parts and
    igc_disable_broken_aspm_l1_2() to clear the ASPM L1.2 enable bit
    on attach and after resume, since PCIe config space can be
    reset across a suspend/resume cycle.
    
    Adapted from the Linux igc driver:
    
      0325143b59c6 igc: disable L1.2 PCI-E link substate to avoid
                   performance issue
      1468c1f97cf3 igc: fix disabling L1.2 PCI-E link substate on I226
                   on init
    
    Signed-off-by: Michael Adler <madler@tapil.com>
    
    PR:             279245
    Reviewed by:    Jim Thompson
    MFC after:      1 week
    Pull-Request:   https://github.com/freebsd/freebsd-src/pull/2318
---
 sys/dev/igc/if_igc.c   | 44 ++++++++++++++++++++++++++++++++++++++++++++
 sys/dev/igc/igc_base.c | 21 +++++++++++++++++++++
 sys/dev/igc/igc_base.h |  1 +
 3 files changed, 66 insertions(+)

diff --git a/sys/dev/igc/if_igc.c b/sys/dev/igc/if_igc.c
index 6dcb55ed6475..f8de961afeca 100644
--- a/sys/dev/igc/if_igc.c
+++ b/sys/dev/igc/if_igc.c
@@ -114,6 +114,7 @@ static bool	igc_if_needs_restart(if_ctx_t, enum iflib_restart_event);
 static void	igc_identify_hardware(if_ctx_t);
 static int	igc_allocate_pci_resources(if_ctx_t);
 static void	igc_free_pci_resources(if_ctx_t);
+static void	igc_disable_broken_aspm_l1_2(if_ctx_t);
 static void	igc_reset(if_ctx_t);
 static int	igc_setup_interface(if_ctx_t);
 static int	igc_setup_msix(if_ctx_t);
@@ -554,6 +555,14 @@ igc_if_attach_pre(if_ctx_t ctx)
 	/* Determine hardware and mac info */
 	igc_identify_hardware(ctx);
 
+	/*
+	 * I226 parts have an erratum where the PCIe ASPM L1.2 exit
+	 * latency can exceed what the packet buffer can absorb under
+	 * load, stalling the inbound packet stream.  Disable ASPM L1.2
+	 * on the device to work around it.
+	 */
+	igc_disable_broken_aspm_l1_2(ctx);
+
 	scctx->isc_tx_nsegments = IGC_MAX_SCATTER;
 	scctx->isc_nrxqsets_max =
 	    scctx->isc_ntxqsets_max = igc_set_num_queues(ctx);
@@ -796,6 +805,12 @@ igc_if_suspend(if_ctx_t ctx)
 static int
 igc_if_resume(if_ctx_t ctx)
 {
+	/*
+	 * PCIe config space, and with it ASPM L1.2, may have been reset
+	 * across the suspend/resume cycle.
+	 */
+	igc_disable_broken_aspm_l1_2(ctx);
+
 	igc_if_init(ctx);
 
 	return(0);
@@ -1497,6 +1512,35 @@ igc_identify_hardware(if_ctx_t ctx)
 	}
 }
 
+/*********************************************************************
+ *
+ *  I226 devices advertise support for the PCIe L1.2 link substate, but
+ *  due to a hardware erratum the exit latency from that low-power state
+ *  can exceed what the packet buffer can tolerate under load, which
+ *  stalls the inbound packet stream.  Disabling ASPM L1.2 on the device
+ *  itself (as opposed to disabling ASPM/power management in the BIOS or
+ *  at the OS level) works around the issue.
+ *
+ **********************************************************************/
+static void
+igc_disable_broken_aspm_l1_2(if_ctx_t ctx)
+{
+	device_t dev = iflib_get_dev(ctx);
+	struct igc_softc *sc = iflib_get_softc(ctx);
+	int cap;
+	uint32_t ctl1;
+
+	if (!igc_is_device_id_i226(&sc->hw))
+		return;
+
+	if (pci_find_extcap(dev, PCIZ_L1PM, &cap) != 0)
+		return;
+
+	ctl1 = pci_read_config(dev, cap + PCIR_L1PM_CTL1, 4);
+	ctl1 &= ~PCIM_L1PM_CTL1_ASPM_L1_2;
+	pci_write_config(dev, cap + PCIR_L1PM_CTL1, ctl1, 4);
+}
+
 static int
 igc_allocate_pci_resources(if_ctx_t ctx)
 {
diff --git a/sys/dev/igc/igc_base.c b/sys/dev/igc/igc_base.c
index de48303a9c7c..181fd7025ba3 100644
--- a/sys/dev/igc/igc_base.c
+++ b/sys/dev/igc/igc_base.c
@@ -184,3 +184,24 @@ void igc_rx_fifo_flush_base(struct igc_hw *hw)
 	IGC_READ_REG(hw, IGC_RNBC);
 	IGC_READ_REG(hw, IGC_MPC);
 }
+
+/**
+ *  igc_is_device_id_i226 - Check whether the device is I226 silicon
+ *  @hw: pointer to the HW structure
+ *
+ *  I225 and I226 share the same mac.type, so this checks the PCI
+ *  device ID directly to distinguish I226 parts, e.g. for erratum
+ *  workarounds that apply only to that silicon.
+ **/
+bool igc_is_device_id_i226(struct igc_hw *hw)
+{
+	switch (hw->device_id) {
+	case IGC_DEV_ID_I226_LM:
+	case IGC_DEV_ID_I226_V:
+	case IGC_DEV_ID_I226_K:
+	case IGC_DEV_ID_I226_IT:
+		return true;
+	default:
+		return false;
+	}
+}
diff --git a/sys/dev/igc/igc_base.h b/sys/dev/igc/igc_base.h
index c986ecc840e3..91cb602f809d 100644
--- a/sys/dev/igc/igc_base.h
+++ b/sys/dev/igc/igc_base.h
@@ -13,6 +13,7 @@ void igc_power_down_phy_copper_base(struct igc_hw *hw);
 extern void igc_rx_fifo_flush_base(struct igc_hw *hw);
 s32 igc_acquire_phy_base(struct igc_hw *hw);
 void igc_release_phy_base(struct igc_hw *hw);
+bool igc_is_device_id_i226(struct igc_hw *hw);
 
 /* Transmit Descriptor - Advanced */
 union igc_adv_tx_desc {


home | help

Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?6a6c6e57.32455.6b6ebed8>