[PATCH net 6/7] net: bcm4908_enet: publish the buffer address before OWN

From: Kordian Chmielewski (mlody1212)

Date: Mon Nov 09 2026 - 18:58:40 EST


bcm4908_enet_dma_alloc_rx_buf() hands a descriptor to the DMA by
writing its control word, with DMA_CTL_STATUS_OWN, first and the new
buffer address second. The refill runs on the descriptor that has just
been completed, which is where the DMA waits once the ring is full: if
it picks the descriptor up between the two stores, it writes the next
frame into the old buffer, which poll_rx() is turning into an skb.
start_xmit() writes the address first, but nothing orders the two
stores, and the descriptors live in coherent memory, where arm64 may
make them visible in either order.

Write the address first on both paths and order it before the control
word with dma_wmb(). In poll_rx(), read what the DMA wrote only after
seeing OWN clear (dma_rmb()).

Fixes: 4feffeadbcb2 ("net: broadcom: bcm4908enet: add BCM4908 controller driver")
Signed-off-by: Kordian Chmielewski (mlody1212) <kudzo51@xxxxxxxxx>
---
drivers/net/ethernet/broadcom/bcm4908_enet.c | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/drivers/net/ethernet/broadcom/bcm4908_enet.c b/drivers/net/ethernet/broadcom/bcm4908_enet.c
index c0aca6801..ea4a9eb98 100644
--- a/drivers/net/ethernet/broadcom/bcm4908_enet.c
+++ b/drivers/net/ethernet/broadcom/bcm4908_enet.c
@@ -289,8 +289,13 @@ static int bcm4908_enet_dma_alloc_rx_buf(struct bcm4908_enet *enet, unsigned int
tmp |= DMA_CTL_STATUS_OWN;
if (idx == enet->rx_ring.length - 1)
tmp |= DMA_CTL_STATUS_WRAP;
- buf_desc->ctl = cpu_to_le32(tmp);
+ /* The DMA must not see OWN before the new buffer address: with a full
+ * ring it waits on this very descriptor and would fill the old buffer,
+ * which is on its way up the stack as an skb.
+ */
buf_desc->addr = cpu_to_le32(slot->dma_addr);
+ dma_wmb();
+ buf_desc->ctl = cpu_to_le32(tmp);

return 0;
}
@@ -600,6 +605,8 @@ static netdev_tx_t bcm4908_enet_start_xmit(struct sk_buff *skb, struct net_devic
netdev_sent_queue(enet->netdev, skb->len);

buf_desc->addr = cpu_to_le32((uint32_t)slot->dma_addr);
+ /* The DMA must not see OWN before the buffer address */
+ dma_wmb();
buf_desc->ctl = cpu_to_le32(tmp);

bcm4908_enet_dma_tx_ring_enable(enet, &enet->tx_ring);
@@ -638,6 +645,8 @@ static int bcm4908_enet_poll_rx(struct napi_struct *napi, int weight)
ctl = le32_to_cpu(buf_desc->ctl);
if (ctl & DMA_CTL_STATUS_OWN)
break;
+ /* Nothing the DMA wrote may be read before it gave the descriptor back */
+ dma_rmb();

slot = enet->rx_ring.slots[enet->rx_ring.read_idx];

--
2.56.0