re(4): quiesce RTL8168G+ and reset before freeing buffers in re_stop()

The STOPREQ command written by re_stop() is not defined for
RTL8168G and later; issuing it can wedge the MAC.

Replace it on those parts with the vendor-documented sequence:

* settle delay
* bounded poll for Tx queue empty
* clear TE/RE
* then bounded poll of the MCU command register (0xD3) FIFO-empty bits.

Also reset the controller before the Rx/Tx buffer free: a controller that
has not quiesced keeps DMAing stale, still-owned descriptors pointing at
freed mbufs (use-after-free under INVARIANTS, cross-NIC mbuf corruption
reported in the PR).

Adds the RL_MCU_* register definitions.

All waits are bounded; error paths only.

* iperf3 --bidir at line rate against RTL8168H (XID 0x541);
  previously wedged the controller until power cycle, with the
  quiesce the reset path recovers.

* Deployed in production on an RTL8168H fleet since 2026-07-01.

Reviewed by:	adrian
Differential Revision:	https://reviews.freebsd.org/D58276
PR: kern/166724
This commit is contained in:
Jérémie Jourdin authored and Adrian Chadd committed 2026-07-19 11:44:36 -07:00
1 parent c325b4024d
commit 38187938f5
2 files changed
+48 -1

No files matched your search

+40 -1
View File
@@ -3630,7 +3630,37 @@ re_stop(struct rl_softc *sc)
0x00080000);
}
if ((sc->rl_flags & RL_FLAG_WAIT_TXPOLL) != 0) {
if ((sc->rl_flags & RL_FLAG_8168G_PLUS) != 0) {
/*
* RTL8168G and later. The STOPREQ command is defined only for
* earlier controllers; issuing it on these parts can leave the
* MAC wedged. With the RXDV gate enabled above, drain the TX
* descriptor queue and the on-chip TX/RX FIFOs and clear the
* TX/RX enable bits so the DMA engine is idle before the reset
* and buffer free below. All waits are bounded.
*/
DELAY(2000);
for (i = RL_TIMEOUT; i > 0; i--) {
if ((CSR_READ_4(sc, RL_TXCFG) &
RL_TXCFG_QUEUE_EMPTY) != 0)
break;
DELAY(100);
}
if (i == 0)
device_printf(sc->rl_dev, "stopping TXQ timed out!\n");
CSR_WRITE_1(sc, RL_COMMAND, CSR_READ_1(sc, RL_COMMAND) &
~(RL_CMD_TX_ENB | RL_CMD_RX_ENB));
for (i = RL_TIMEOUT * 3; i > 0; i--) {
if ((CSR_READ_1(sc, RL_MCU_CMD) &
(RL_MCU_TXFIFO_EMPTY | RL_MCU_RXFIFO_EMPTY)) ==
(RL_MCU_TXFIFO_EMPTY | RL_MCU_RXFIFO_EMPTY))
break;
DELAY(20);
}
if (i == 0)
device_printf(sc->rl_dev,
"TX/RX FIFO drain timed out!\n");
} else if ((sc->rl_flags & RL_FLAG_WAIT_TXPOLL) != 0) {
for (i = RL_TIMEOUT; i > 0; i--) {
if ((CSR_READ_1(sc, sc->rl_txstart) &
RL_TXSTART_START) == 0)
@@ -3661,6 +3691,15 @@ re_stop(struct rl_softc *sc)
CSR_WRITE_2(sc, RL_IMR, 0x0000);
CSR_WRITE_2(sc, RL_ISR, 0xFFFF);
/*
* Reset the controller before freeing the DMA buffers below. A
* controller that has not fully quiesced can keep fetching stale,
* still-owned descriptors that point at about-to-be-freed mbufs.
* re_init_locked() resets again on the reinit path; the extra reset
* is idempotent and cheap.
*/
re_reset(sc);
if (sc->rl_head != NULL) {
m_freem(sc->rl_head);
sc->rl_head = sc->rl_tail = NULL;
+8
View File
@@ -145,6 +145,14 @@
#define RL_INTRMOD 0x00E2 /* 16 bits */
#define RL_MISC 0x00F0
/*
* MCU command / FIFO status register (RTL8168G and later). Polled to
* confirm the on-chip TX/RX FIFOs have drained before a controller reset.
*/
#define RL_MCU_CMD 0x00D3
#define RL_MCU_TXFIFO_EMPTY 0x20 /* bit 5 */
#define RL_MCU_RXFIFO_EMPTY 0x10 /* bit 4 */
/*
* TX config register bits
*/