qualcommbe: fix PPE EDMA RX DMA mappings

EDMA maps RX fill buffers with DMA_TO_DEVICE but unmaps them with DMA_FROM_DEVICE. Use DMA_FROM_DEVICE consistently, release mappings left on RX rings during teardown, and flush ring-disable writes before descriptor cleanup.

Signed-off-by: krava <igor.kravchenko05@gmail.com>
Link: https://github.com/openwrt/openwrt/pull/24218
Signed-off-by: Robert Marko <robimarko@gmail.com>
This commit is contained in:
krava authored and Robert Marko committed 2026-09-25 21:46:38 +02:00
1 parent da141cba5c
commit e8ca976eff
1 file changed
+135
@@ -0,0 +1,135 @@
From 67edad5e196f088098d25e1c843ffbf798627860 Mon Sep 17 00:00:00 2001
From: Igor Kravchenko <igor.kravchenko05@gmail.com>
Date: Mon, 13 Jul 2026 19:13:59 +0000
Subject: [PATCH] net: ethernet: qualcomm: ppe: fix RX DMA mappings
EDMA writes received frames into buffers supplied by the RxFill ring.
Map them with DMA_FROM_DEVICE and use the same direction when unmapping.
The driver currently maps those buffers with DMA_TO_DEVICE but unmaps
them with DMA_FROM_DEVICE. Besides violating the DMA API direction
pairing requirement, this can skip CPU cache invalidation on
non-coherent systems and expose stale RX data.
Also unmap buffers still attached to RxFill and RxDesc descriptors
during ring cleanup. Freeing an skb alone does not release its streaming
DMA mapping, leaving mappings behind on error handling and teardown.
Read back the ring enable registers after disabling them, ensuring the
disable writes reach the hardware before cleanup touches descriptors and
buffers.
Signed-off-by: Igor Kravchenko <igor.kravchenko05@gmail.com>
---
drivers/net/ethernet/qualcomm/ppe/edma_cfg_rx.c | 26 ++++++++++++++++++++++++++
drivers/net/ethernet/qualcomm/ppe/edma_rx.c | 4 ++--
drivers/net/ethernet/qualcomm/ppe/edma_rx.h | 2 ++
--- a/drivers/net/ethernet/qualcomm/ppe/edma_rx.c
+++ b/drivers/net/ethernet/qualcomm/ppe/edma_rx.c
@@ -58,7 +58,7 @@ static int edma_rx_alloc_buffer_list(str
skb_reserve(skb, EDMA_RX_SKB_HEADROOM + NET_IP_ALIGN);
if (likely(!page_mode)) {
- buff_addr = dma_map_single(dev, skb->data, dma_map_size, DMA_TO_DEVICE);
+ buff_addr = dma_map_single(dev, skb->data, dma_map_size, DMA_FROM_DEVICE);
if (dma_mapping_error(dev, buff_addr)) {
dev_dbg(dev, "edma_context:%pK Unable to dma for non page mode",
edma_ctx);
@@ -77,7 +77,7 @@ static int edma_rx_alloc_buffer_list(str
break;
}
- buff_addr = dma_map_page(dev, pg, 0, PAGE_SIZE, DMA_TO_DEVICE);
+ buff_addr = dma_map_page(dev, pg, 0, PAGE_SIZE, DMA_FROM_DEVICE);
if (dma_mapping_error(dev, buff_addr)) {
dev_dbg(dev, "edma_context:%pK Mapping error for page mode",
edma_ctx);
--- a/drivers/net/ethernet/qualcomm/ppe/edma_cfg_rx.c
+++ b/drivers/net/ethernet/qualcomm/ppe/edma_cfg_rx.c
@@ -43,6 +43,22 @@ static u32 edma_rx_ring_queue_map[][EDMA
{ 6, 14, 22, 30 },
{ 7, 15, 23, 31 }};
+static void edma_cfg_rx_unmap_buffer(struct device *dev,
+ struct edma_rxfill_ring *rxfill_ring,
+ dma_addr_t dma_addr)
+{
+ u32 dma_map_size;
+
+ if (rxfill_ring->page_mode) {
+ dma_unmap_page(dev, dma_addr, PAGE_SIZE, DMA_FROM_DEVICE);
+ return;
+ }
+
+ dma_map_size = rxfill_ring->alloc_size -
+ EDMA_RX_SKB_HEADROOM - NET_IP_ALIGN;
+ dma_unmap_single(dev, dma_addr, dma_map_size, DMA_FROM_DEVICE);
+}
+
u32 edma_cfg_rx_rps_bitmap_cores = EDMA_RX_DEFAULT_BITMAP;
static int edma_cfg_rx_desc_rings_reset_queue_mapping(void)
@@ -376,6 +392,8 @@ void edma_cfg_rx_rings_disable(void)
regmap_read(regmap, reg, &data);
data &= ~EDMA_RXDESC_RX_EN;
regmap_write(regmap, reg, data);
+ /* Flush the disable write before ring cleanup. */
+ regmap_read(regmap, reg, &data);
}
/* Disable RxFill Rings */
@@ -388,6 +406,8 @@ void edma_cfg_rx_rings_disable(void)
regmap_read(regmap, reg, &data);
data &= ~EDMA_RXFILL_RING_EN;
regmap_write(regmap, reg, data);
+ /* Flush the disable write before ring cleanup. */
+ regmap_read(regmap, reg, &data);
}
}
@@ -420,6 +440,7 @@ static void edma_cfg_rx_fill_ring_cleanu
while (curr_idx != cons_idx) {
struct edma_rxfill_desc *rxfill_desc;
+ dma_addr_t dma_addr;
struct sk_buff *skb;
/* Get RxFill descriptor */
@@ -435,6 +456,8 @@ static void edma_cfg_rx_fill_ring_cleanu
continue;
}
+ dma_addr = EDMA_RXFILL_BUFFER_ADDR_GET(rxfill_desc);
+ edma_cfg_rx_unmap_buffer(dev, rxfill_ring, dma_addr);
dev_kfree_skb_any(skb);
}
@@ -506,6 +529,7 @@ static void edma_cfg_rx_desc_ring_cleanu
while (cons_idx != prod_idx) {
struct edma_rxdesc_pri *rxdesc_pri =
EDMA_RXDESC_PRI_DESC(rxdesc_ring, cons_idx);
+ dma_addr_t dma_addr;
struct sk_buff *skb;
/* Update consumer index */
@@ -519,6 +543,8 @@ static void edma_cfg_rx_desc_ring_cleanu
continue;
}
+ dma_addr = EDMA_RXDESC_BUFFER_ADDR_GET(rxdesc_pri);
+ edma_cfg_rx_unmap_buffer(dev, rxdesc_ring->rxfill, dma_addr);
dev_kfree_skb_any(skb);
}
--- a/drivers/net/ethernet/qualcomm/ppe/edma_rx.h
+++ b/drivers/net/ethernet/qualcomm/ppe/edma_rx.h
@@ -124,6 +124,8 @@
}
#define EDMA_RXFILL_BUFFER_ADDR_SET(desc, addr) (((desc)->word0) = (u32)(addr))
+#define EDMA_RXFILL_BUFFER_ADDR_GET(desc) \
+ ((u32)(le32_to_cpu((__force __le32)((desc)->word0))))
/* Opaque values are set in word2 and word3, they are not accessed by the EDMA HW,
* so endianness conversion is not needed.