The branch main has been updated by adrian:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=38187938f52283143308dd219db35d889da55ea4

commit 38187938f52283143308dd219db35d889da55ea4
Author:     Jérémie Jourdin <[email protected]>
AuthorDate: 2026-07-19 18:39:49 +0000
Commit:     Adrian Chadd <[email protected]>
CommitDate: 2026-07-19 18:44:36 +0000

    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
---
 sys/dev/re/if_re.c    | 41 ++++++++++++++++++++++++++++++++++++++++-
 sys/dev/rl/if_rlreg.h |  8 ++++++++
 2 files changed, 48 insertions(+), 1 deletion(-)

diff --git a/sys/dev/re/if_re.c b/sys/dev/re/if_re.c
index 50e7e1cad16c..cf7ba90c9a5a 100644
--- a/sys/dev/re/if_re.c
+++ b/sys/dev/re/if_re.c
@@ -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;
diff --git a/sys/dev/rl/if_rlreg.h b/sys/dev/rl/if_rlreg.h
index 10e25fcb5373..0db4b2066ce6 100644
--- a/sys/dev/rl/if_rlreg.h
+++ b/sys/dev/rl/if_rlreg.h
@@ -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
  */

Reply via email to