Commit 94c510a5dc for qemu.org

commit 94c510a5dc4836550af1ecddcfa1ff945bae562a
Author: Bin Meng <bin.meng@processmission.com>
Date:   Mon Jul 20 20:07:31 2026 +0800

    hw/net/cadence: Return current Cadence GEM queue pointers

    Cadence GEM queue pointer registers are programmed with the descriptor
    ring base, but reads return the descriptor currently being accessed.
    The model tracked the current positions separately while continuing to
    return the configured base.

    The Linux macb driver uses the transmit queue pointer when recovering
    from a used-buffer interrupt. A stale priority-queue pointer can make
    the driver restart DMA before that queue handles its completion
    interrupt, causing queue 0 to repeatedly raise TX_USED.

    The primary queue has had this mismatch since the initial model.
    Priority queue support later copied the same register-read behavior.

    A single-queue machine usually handles TX_COMPLETE before TX_USED and
    empties the software queue before the restart check, which kept the
    issue hidden there.

    Return the current RX and TX descriptor positions on queue-pointer reads
    and clear those positions on reset.

    Fixes: e9f186e514a7 ("cadence_gem: initial version of device model")
    Fixes: 6710172501be ("cadence_gem: Add queue support")
    Cc: qemu-stable@nongnu.org
    Signed-off-by: Bin Meng <bin.meng@processmission.com>
    Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
    Message-ID: <20260720120731.2022475-1-bin.meng@processmission.com>
    Signed-off-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>

diff --git a/hw/net/cadence_gem.c b/hw/net/cadence_gem.c
index b568fa3392..39e3620ef5 100644
--- a/hw/net/cadence_gem.c
+++ b/hw/net/cadence_gem.c
@@ -1469,6 +1469,8 @@ static void gem_reset(DeviceState *d)

     /* Set post reset register values */
     memset(&s->regs[0], 0, sizeof(s->regs));
+    memset(&s->rx_desc_addr[0], 0, sizeof(s->rx_desc_addr));
+    memset(&s->tx_desc_addr[0], 0, sizeof(s->tx_desc_addr));
     s->regs[R_NWCFG] = 0x00080000;
     s->regs[R_NWSTATUS] = 0x00000006;
     s->regs[R_DMACFG] = 0x00020784;
@@ -1593,9 +1595,22 @@ static uint64_t gem_read(void *opaque, hwaddr offset, unsigned size)
     offset >>= 2;
     retval = s->regs[offset];

-    DB_PRINT("offset: 0x%04x read: 0x%08x\n", (unsigned)offset*4, retval);
+    DB_PRINT("offset: 0x%04x read: 0x%08x\n", (unsigned)offset * 4,
+             retval);

     switch (offset) {
+    case R_RXQBASE:
+        retval = s->rx_desc_addr[0];
+        break;
+    case R_TXQBASE:
+        retval = s->tx_desc_addr[0];
+        break;
+    case R_TRANSMIT_Q1_PTR ... R_TRANSMIT_Q7_PTR:
+        retval = s->tx_desc_addr[offset - R_TRANSMIT_Q1_PTR + 1];
+        break;
+    case R_RECEIVE_Q1_PTR ... R_RECEIVE_Q7_PTR:
+        retval = s->rx_desc_addr[offset - R_RECEIVE_Q1_PTR + 1];
+        break;
     case R_ISR:
         DB_PRINT("lowering irqs on ISR read\n");
         /* The interrupts get updated at the end of the function. */