From: Josh Hilke <jrhilke@google.com>
To: David Matlack <dmatlack@google.com>, Alex Williamson <alex@shazbot.org>
Cc: Shuah Khan <shuah@kernel.org>,
linux-kernel@vger.kernel.org, kvm@vger.kernel.org,
linux-kselftest@vger.kernel.org,
Vipin Sharma <vipinsh@google.com>,
Josh Hilke <jrhilke@google.com>,
Alex Williamson <alex.williamson@nvidia.com>
Subject: [PATCH v4 2/9] vfio: selftests: igb: Use PHY internal loopback on 82576
Date: Fri, 10 Jul 2026 22:06:46 +0000 [thread overview]
Message-ID: <20260710-igb_v3_b4-v4-2-56e7e2576cc1@google.com> (raw)
In-Reply-To: <20260710-igb_v3_b4-v4-0-56e7e2576cc1@google.com>
From: Alex Williamson <alex.williamson@nvidia.com>
The submitted driver waits for PHY autonegotiation and then enables MAC
loopback via RCTL.LBM_MAC. QEMU's emulated igb tolerates this, but the
82576 datasheet rejects the MAC loopback path on real hardware:
Section 3.5.6.1 (Loopback Support / General): "Use PHY Loopback
instead of MAC Loopback on the 82576."
Section 3.5.6.2 (MAC Loopback): "MAC Loopback is not used on this
device."
Section 3.5.6.3.1 (Setting the 82576 to PHY loopback Mode): set PHY
control register bits 8 (duplex), 14 (loopback), clear bit 12
(autoneg enable), set the speed via bits 6 and 13. For 1Gb/s the
register value is 0x4140.
Section 8.10.1 (RCTL register): "When using the internal PHY, LBM
should remain set to 00b and the PHY instead configured for
loopback through the MDIO interface."
Replace igb_phy_setup_autoneg() with igb_setup_loopback() which:
- writes PHY register 0 with LOOPBACK | SPEED_1000 | FULL_DUPLEX
- forces the MAC into 1Gb/s full duplex via CTRL.FRCSPD, CTRL.FRCDPX,
CTRL.SPD_1000, CTRL.FD, CTRL.SLU; without forcing the MAC link
state, the descriptor engine does not run on real hardware
PHY internal loopback (section 3.5.6.3) wraps data at the end of the
PHY datapath before the MDI, so the physical link state and cable
speed are irrelevant. This matches the kernel ethtool selftest path
in igb_integrated_phy_loopback()
(drivers/net/ethernet/intel/igb/igb_ethtool.c).
QEMU's igb emulation drives STATUS.LU exclusively from its autoneg-done
timer or a network-backend link-state change; the guest cannot set
STATUS.LU through CTRL.SLU. Its receive path checks STATUS.LU
(e1000x_hw_rx_enabled in hw/net/e1000x_common.c) and drops every
loopback frame until LU is set. Issue a one-shot autoneg-restart PHY
write at the top of igb_setup_loopback() to kick the timer; the
subsequent PHY write clears autoneg-enable, so on real hardware
autoneg never starts and the write is a no-op.
QEMU's igb also does not honor PHY register 0 bit 14 (PHY internal
loopback) and relies on RCTL.LBM_MAC to wrap TX descriptors back to
the RX queue. Datasheet 8.10.1 advises that LBM remain 00b when
using the internal PHY, but empirically setting LBM_MAC has no
observable effect on real 82576 (MAC loopback is not implemented per
3.5.6.2), so set it alongside PHY loopback as the actual loopback
mechanism under QEMU. With these two QEMU-only accommodations the
selftest works in both environments without environment-specific code
paths.
Remove igb_read_phy() which becomes unused.
Assisted-by: Claude:claude-opus-4-7
Signed-off-by: Alex Williamson <alex.williamson@nvidia.com>
---
tools/testing/selftests/vfio/lib/drivers/igb/igb.c | 122 +++++++++++++--------
1 file changed, 74 insertions(+), 48 deletions(-)
diff --git a/tools/testing/selftests/vfio/lib/drivers/igb/igb.c b/tools/testing/selftests/vfio/lib/drivers/igb/igb.c
index 923b2341abad..cccaedf86e32 100644
--- a/tools/testing/selftests/vfio/lib/drivers/igb/igb.c
+++ b/tools/testing/selftests/vfio/lib/drivers/igb/igb.c
@@ -114,57 +114,70 @@ static int igb_write_phy(struct igb *igb, u32 offset, u16 data)
return 0;
}
-static int igb_read_phy(struct igb *igb, u32 offset, u16 *data)
+/*
+ * Configure the device for PHY internal loopback per 82576 datasheet
+ * section 3.5.6.3.1. Force the PHY to 1Gb/s full duplex with loopback
+ * enabled, then force the MAC link state to match. Internal loopback
+ * wraps data at the end of the PHY datapath (section 3.5.6.3), so the
+ * physical link state is irrelevant.
+ *
+ * Section 3.5.6.1 directs to "Use PHY Loopback instead of MAC Loopback
+ * on the 82576", and section 3.5.6.2 states "MAC Loopback is not used
+ * on this device." RCTL.LBM_MAC is still set elsewhere as a QEMU-only
+ * accommodation; see the RCTL programming in the caller for the
+ * rationale.
+ */
+static void igb_setup_loopback(struct igb *igb)
{
- u32 mdic;
- int i;
-
- mdic = ((offset << E1000_MDIC_REG_SHIFT) |
- (1 << E1000_MDIC_PHY_SHIFT) |
- E1000_MDIC_OP_READ);
-
- igb_write32(igb, E1000_MDIC, mdic);
-
- for (i = 0; i < 1000; i++) {
- usleep(50);
- mdic = igb_read32(igb, E1000_MDIC);
- if (mdic & E1000_MDIC_READY)
- break;
- }
-
- if (!(mdic & E1000_MDIC_READY))
- return -1;
-
- if (mdic & E1000_MDIC_ERROR)
- return -1;
-
- *data = (u16)mdic;
- return 0;
-}
-
-static void igb_phy_setup_autoneg(struct igb *igb)
-{
- int timeout_ms = 1000;
- bool success = false;
- u16 phy_status;
+ u32 ctrl;
int ret;
- int i;
- /* Trigger auto-negotiation */
- ret = igb_write_phy(igb, MII_BMCR,
+ /*
+ * Kick the autoneg machinery solely to bring STATUS.LU up under
+ * QEMU's igb emulation: QEMU only updates STATUS.LU via its
+ * autoneg-done timer, and without LU set its receive path
+ * (e1000x_hw_rx_enabled) drops every loopback frame. On real
+ * hardware autoneg cannot complete before the next PHY write
+ * below clears the autoneg-enable bit, so this is effectively a
+ * no-op there.
+ */
+ (void)igb_write_phy(igb, MII_BMCR,
BMCR_ANENABLE | BMCR_ANRESTART);
+
+ /* PHY control: loopback + 1Gb/s full duplex, autoneg disabled. */
+ ret = igb_write_phy(igb, MII_BMCR,
+ BMCR_LOOPBACK |
+ BMCR_SPEED1000 |
+ BMCR_FULLDPLX);
VFIO_ASSERT_EQ(ret, 0, "Failed to write PHY control register");
- for (i = 0; i < timeout_ms; i++) {
- if (igb_read_phy(igb, MII_BMSR, &phy_status) == 0) {
- success = !!(phy_status & BMSR_ANEGCOMPLETE);
- if (success)
- break;
- }
- usleep(1000);
- }
+ /*
+ * Brief delay before forcing the MAC, mirroring the kernel ethtool
+ * selftest in igb_integrated_phy_loopback(). Not specified by the
+ * datasheet, but empirically required by the kernel driver.
+ */
+ usleep(50000);
+
+ /*
+ * Force the MAC to 1Gb/s full duplex with link up. Without forcing
+ * the link state the descriptor engine does not run, since the chip
+ * normally waits for a real negotiated link.
+ */
+ ctrl = igb_read32(igb, E1000_CTRL);
+ ctrl &= ~E1000_CTRL_SPD_SEL;
+ ctrl |= E1000_CTRL_FRCSPD |
+ E1000_CTRL_FRCDPX |
+ E1000_CTRL_SPD_1000 |
+ E1000_CTRL_FD |
+ E1000_CTRL_SLU;
+ igb_write32(igb, E1000_CTRL, ctrl);
- VFIO_ASSERT_TRUE(success, "Auto-negotiation did not complete in time");
+ /*
+ * Settling delay matching the kernel ethtool selftest's msleep(500)
+ * at the tail of igb_integrated_phy_loopback(). Not specified by
+ * the datasheet; empirical, and inherited from the kernel driver.
+ */
+ usleep(500000);
}
static int igb_probe(struct vfio_pci_device *device)
@@ -191,6 +204,7 @@ static void igb_init(struct vfio_pci_device *device)
iova_tx = to_iova(device, igb->tx_ring);
iova_rx = to_iova(device, igb->rx_ring);
+ /* Reset device and disable all interrupts */
igb_write32(igb, E1000_CTRL, igb_read32(igb, E1000_CTRL) | E1000_CTRL_RST);
/*
* Must wait at least 1 millisecond after setting the reset bit before
@@ -216,8 +230,8 @@ static void igb_init(struct vfio_pci_device *device)
vfio_pci_config_writew(device, PCI_COMMAND, cmd_reg);
}
- /* Trigger autonegotiation. This enables IGB to transmit data. */
- igb_phy_setup_autoneg(igb);
+ /* Configure PHY internal loopback for testing. */
+ igb_setup_loopback(igb);
/* Configure TX and RX descriptor rings */
igb_write32(igb, E1000_TDBAL(0), (u32)iova_tx);
@@ -243,10 +257,22 @@ static void igb_init(struct vfio_pci_device *device)
usleep(10);
}
- /* Enable Receiver and Transmitter */
+ /*
+ * Enable Receiver and Transmitter. RCTL.LBM_MAC is set in addition
+ * to PHY loopback as a QEMU-only accommodation: QEMU's emulated igb
+ * does not honor PHY register 0 bit 14 (PHY internal loopback) and
+ * relies on RCTL.LBM_MAC to wrap TX descriptors back to the RX
+ * queue. Datasheet 8.10.1 (RCTL register) advises "When using the
+ * internal PHY, LBM should remain set to 00b", so setting LBM_MAC
+ * here deviates from datasheet guidance; empirically the bit has
+ * no observable effect on real 82576 hardware because MAC loopback
+ * is not implemented (datasheet 3.5.6.2). Setting both lets the
+ * selftest work on both real hardware and QEMU without conditional
+ * code paths.
+ */
rctl = E1000_RCTL_EN | /* Receiver Enable */
E1000_RCTL_UPE | /* Unicast Promiscuous (for dummy MAC) */
- E1000_RCTL_LBM_MAC | /* MAC Loopback Mode */
+ E1000_RCTL_LBM_MAC | /* MAC Loopback - for QEMU emulation only */
E1000_RCTL_SECRC; /* Strip CRC (needed for memcmp) */
igb_write32(igb, E1000_RCTL, rctl);
igb_write32(igb, E1000_TCTL, E1000_TCTL_EN);
--
2.55.0.795.g602f6c329a-goog
next prev parent reply other threads:[~2026-07-10 22:06 UTC|newest]
Thread overview: 14+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-10 22:06 [PATCH v4 0/9] vfio: selftests: Add driver for Intel Ethernet Gigabit Controller (IGB) Josh Hilke
2026-07-10 22:06 ` [PATCH v4 1/9] vfio: selftests: igb: Add driver for IGB QEMU device Josh Hilke
2026-07-10 22:19 ` sashiko-bot
2026-07-10 22:06 ` Josh Hilke [this message]
2026-07-10 22:06 ` [PATCH v4 3/9] vfio: selftests: igb: Use advanced TX and RX descriptors Josh Hilke
2026-07-10 22:06 ` [PATCH v4 4/9] vfio: selftests: igb: Program MSI-X interrupt routing Josh Hilke
2026-07-10 22:23 ` sashiko-bot
2026-07-10 22:06 ` [PATCH v4 5/9] vfio: selftests: igb: Extend memcpy completion timeout for line-rate hardware Josh Hilke
2026-07-10 22:24 ` sashiko-bot
2026-07-10 22:06 ` [PATCH v4 6/9] vfio: selftests: igb: Disable PCIe completion timeout retries Josh Hilke
2026-07-10 22:06 ` [PATCH v4 7/9] vfio: selftests: Add helpers to re-enable interrupts Josh Hilke
2026-07-10 22:06 ` [PATCH v4 8/9] vfio: selftests: igb: Factor hardware programming into igb_hw_init() Josh Hilke
2026-07-10 22:06 ` [PATCH v4 9/9] vfio: selftests: igb: Recover after DMA-read faults Josh Hilke
2026-07-10 22:33 ` sashiko-bot
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260710-igb_v3_b4-v4-2-56e7e2576cc1@google.com \
--to=jrhilke@google.com \
--cc=alex.williamson@nvidia.com \
--cc=alex@shazbot.org \
--cc=dmatlack@google.com \
--cc=kvm@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-kselftest@vger.kernel.org \
--cc=shuah@kernel.org \
--cc=vipinsh@google.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox