mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH 0/2] ARM: preserve DMA_FROM_DEVICE buffer contents
@ 2026-09-10  6:36 Karl Mehltretter
  2026-09-10  6:36 ` [PATCH 1/2] ARM: dma-mapping: " Karl Mehltretter
                   ` (3 more replies)
  0 siblings, 4 replies; 7+ messages in thread
From: Karl Mehltretter @ 2026-09-10  6:36 UTC (permalink / raw)
  To: Russell King
  Cc: Karl Mehltretter, Hans Ulli Kroll, Robin Murphy,
	Marek Szyprowski, Will Deacon, Arnd Bergmann, Christoph Hellwig,
	Linus Walleij, Ard Biesheuvel, linux-arm-kernel, linux-kernel,
	stable

This series prevents a DMA_FROM_DEVICE map from discarding CPU-written
buffer contents on non-coherent 32-bit ARM. It is based on
v7.3-rc1-324-g986c24e0fe44.

ARM currently invalidates these buffers before the device writes them.
If the device writes only part of a buffer, discarded dirty cache lines
can expose older memory contents in the untouched bytes. A stock USB
webcam demonstrated this through usbfs. Short isochronous packets left
gaps, and usbfs returned non-zero data to userspace from bytes it had
cleared.

arm64 changed this handoff from invalidate to clean in 2022 with commit
c50f11c6196f ("arm64: mm: Don't invalidate FROM_DEVICE buffers at start
of DMA transfer"). Arnd Bergmann's 2023 ARM32 cache-maintenance series
left DMA_FROM_DEVICE preservation unresolved [1]. This series keeps the
existing ownership hooks.

Patch 1 covers v6, v7, v7-M and the common outer-cache path. These paths
already invalidate when ownership returns to the CPU. Patch 2 makes the
legacy write-back backends use their existing clean-and-invalidate
operation at device handoff. Their no-op ownership-return hooks remain
unchanged, avoiding the additional completion traversal raised during
review [2].

[1] https://lore.kernel.org/r/20230327121317.4081816-1-arnd@kernel.org
[2] https://lists.infradead.org/pipermail/linux-riscv/2023-March/029740.html

Testing:

  - An ARM11 MPCore (ARMv6) in a New Nintendo 2DS XL lost 402,080 of
    409,600 CPU-written bytes in a mapping-only test. With patch 1, none
    were lost. The UP kernel used out-of-tree platform support and the
    same map-time invalidate as mainline.

  - On an ARM926EJ-S (ARMv5TEJ) SAM9X75, eleven mapping-only runs lost
    339,936 to 351,744 of 409,600 bytes with the existing code. Five
    usbfs runs checked 245,708 unwritten gap bytes and exposed 29,403 of
    them as non-zero. With this series, five fresh 100-iteration
    mapping-only runs on the same board each lost 0 of 409,600 bytes.

  - A Cortex-A72 running AArch32/LPAE did not reproduce the loss in 22
    existing-kernel runs. This does not establish whether other v7 cores
    are affected.

The one-pass series passed focused W=1 object builds for ARM926,
Feroceon, XScale, ARM940 and v7. The remaining legacy backends have
source review and compile coverage from the previous two-pass revision.

Neither patch adds a cache traversal. Patch 2 changes the legacy
DMA_FROM_DEVICE operation from invalidate to clean-and-invalidate. This
can add per-line work and memory writes on dirty buffers. Throughput and
CPU cost have not been measured.

Karl Mehltretter (2):
  ARM: dma-mapping: preserve DMA_FROM_DEVICE buffer contents
  ARM: dma-mapping: flush FROM_DEVICE buffers on legacy backends

 arch/arm/mm/cache-fa.S          | 1 -
 arch/arm/mm/cache-v4wb.S        | 1 -
 arch/arm/mm/cache-v6.S          | 2 --
 arch/arm/mm/cache-v7.S          | 2 --
 arch/arm/mm/cache-v7m.S         | 2 --
 arch/arm/mm/dma-mapping-nommu.c | 5 +----
 arch/arm/mm/dma-mapping.c       | 8 +-------
 arch/arm/mm/proc-arm1020.S      | 1 -
 arch/arm/mm/proc-arm1020e.S     | 1 -
 arch/arm/mm/proc-arm1022.S      | 1 -
 arch/arm/mm/proc-arm1026.S      | 1 -
 arch/arm/mm/proc-arm920.S       | 1 -
 arch/arm/mm/proc-arm922.S       | 1 -
 arch/arm/mm/proc-arm925.S       | 1 -
 arch/arm/mm/proc-arm926.S       | 1 -
 arch/arm/mm/proc-arm940.S       | 1 -
 arch/arm/mm/proc-arm946.S       | 1 -
 arch/arm/mm/proc-feroceon.S     | 2 --
 arch/arm/mm/proc-mohawk.S       | 1 -
 arch/arm/mm/proc-xsc3.S         | 1 -
 arch/arm/mm/proc-xscale.S       | 1 -
 21 files changed, 2 insertions(+), 34 deletions(-)

-- 
2.53.0

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH 1/2] ARM: dma-mapping: preserve DMA_FROM_DEVICE buffer contents
  2026-09-10  6:36 [PATCH 0/2] ARM: preserve DMA_FROM_DEVICE buffer contents Karl Mehltretter
@ 2026-09-10  6:36 ` Karl Mehltretter
  2026-09-10  6:36 ` [PATCH 2/2] ARM: dma-mapping: flush FROM_DEVICE buffers on legacy backends Karl Mehltretter
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 7+ messages in thread
From: Karl Mehltretter @ 2026-09-10  6:36 UTC (permalink / raw)
  To: Russell King
  Cc: Karl Mehltretter, Hans Ulli Kroll, Robin Murphy,
	Marek Szyprowski, Will Deacon, Arnd Bergmann, Christoph Hellwig,
	Linus Walleij, Ard Biesheuvel, linux-arm-kernel, linux-kernel,
	stable

The v6, v7 and v7-M cache backends invalidate DMA_FROM_DEVICE buffers
when ownership passes to the device. The common outer-cache path does
the same. On a write-back cache, invalidating a dirty line can discard
CPU-written data. If the device writes only part of the buffer, the
untouched bytes can expose memory contents older than those present at
the handoff.

A mapping-only test on an ARM11 MPCore (ARMv6) system reproduced this
through the v6 path. The existing code lost 402,080 of 409,600
CPU-written bytes over 100 iterations. This change lost none.

Clean the inner and outer cache lines for the buffer at handoff. The
existing completion path still invalidates them before the CPU reads the
buffer. This matches arm64 commit c50f11c6196f ("arm64: mm: Don't
invalidate FROM_DEVICE buffers at start of DMA transfer") and adds no
cache traversal.

Cc: stable@vger.kernel.org
Link: https://lore.kernel.org/r/20220606152150.GA31568@willie-the-truck
Assisted-by: LLM
Signed-off-by: Karl Mehltretter <kmehltretter@gmail.com>
---
 arch/arm/mm/cache-v6.S          | 2 --
 arch/arm/mm/cache-v7.S          | 2 --
 arch/arm/mm/cache-v7m.S         | 2 --
 arch/arm/mm/dma-mapping-nommu.c | 5 +----
 arch/arm/mm/dma-mapping.c       | 8 +-------
 5 files changed, 2 insertions(+), 17 deletions(-)

diff --git a/arch/arm/mm/cache-v6.S b/arch/arm/mm/cache-v6.S
index 5ceea8965ea1..149042bac423 100644
--- a/arch/arm/mm/cache-v6.S
+++ b/arch/arm/mm/cache-v6.S
@@ -283,8 +283,6 @@ SYM_FUNC_END(v6_dma_flush_range)
  */
 SYM_TYPED_FUNC_START(v6_dma_map_area)
 	add	r1, r1, r0
-	teq	r2, #DMA_FROM_DEVICE
-	beq	v6_dma_inv_range
 	b	v6_dma_clean_range
 SYM_FUNC_END(v6_dma_map_area)
 
diff --git a/arch/arm/mm/cache-v7.S b/arch/arm/mm/cache-v7.S
index 726681fb7d4d..22897f98cc95 100644
--- a/arch/arm/mm/cache-v7.S
+++ b/arch/arm/mm/cache-v7.S
@@ -441,8 +441,6 @@ SYM_FUNC_END(v7_dma_flush_range)
  */
 SYM_TYPED_FUNC_START(v7_dma_map_area)
 	add	r1, r1, r0
-	teq	r2, #DMA_FROM_DEVICE
-	beq	v7_dma_inv_range
 	b	v7_dma_clean_range
 SYM_FUNC_END(v7_dma_map_area)
 
diff --git a/arch/arm/mm/cache-v7m.S b/arch/arm/mm/cache-v7m.S
index 7f9cfad2ea21..3ea5f047f43e 100644
--- a/arch/arm/mm/cache-v7m.S
+++ b/arch/arm/mm/cache-v7m.S
@@ -432,8 +432,6 @@ SYM_FUNC_END(v7m_dma_flush_range)
  */
 SYM_TYPED_FUNC_START(v7m_dma_map_area)
 	add	r1, r1, r0
-	teq	r2, #DMA_FROM_DEVICE
-	beq	v7m_dma_inv_range
 	b	v7m_dma_clean_range
 SYM_FUNC_END(v7m_dma_map_area)
 
diff --git a/arch/arm/mm/dma-mapping-nommu.c b/arch/arm/mm/dma-mapping-nommu.c
index c6a70686507b..a396bbceee73 100644
--- a/arch/arm/mm/dma-mapping-nommu.c
+++ b/arch/arm/mm/dma-mapping-nommu.c
@@ -18,10 +18,7 @@ void arch_sync_dma_for_device(phys_addr_t paddr, size_t size,
 {
 	dmac_map_area(__va(paddr), size, dir);
 
-	if (dir == DMA_FROM_DEVICE)
-		outer_inv_range(paddr, paddr + size);
-	else
-		outer_clean_range(paddr, paddr + size);
+	outer_clean_range(paddr, paddr + size);
 }
 
 void arch_sync_dma_for_cpu(phys_addr_t paddr, size_t size,
diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c
index 7761099dde9e..e2df8b680bd6 100644
--- a/arch/arm/mm/dma-mapping.c
+++ b/arch/arm/mm/dma-mapping.c
@@ -681,18 +681,12 @@ void arch_sync_dma_for_device(phys_addr_t paddr, size_t size,
 {
 	dma_cache_maint_page(paddr, size, dir, dmac_map_area);
 
-	if (dir == DMA_FROM_DEVICE) {
-		outer_inv_range(paddr, paddr + size);
-	} else {
-		outer_clean_range(paddr, paddr + size);
-	}
-	/* FIXME: non-speculating: flush on bidirectional mappings? */
+	outer_clean_range(paddr, paddr + size);
 }
 
 void arch_sync_dma_for_cpu(phys_addr_t paddr, size_t size,
 			   enum dma_data_direction dir)
 {
-	/* FIXME: non-speculating: not required */
 	/* in any case, don't bother invalidating if DMA to device */
 	if (dir != DMA_TO_DEVICE) {
 		outer_inv_range(paddr, paddr + size);
-- 
2.53.0

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH 2/2] ARM: dma-mapping: flush FROM_DEVICE buffers on legacy backends
  2026-09-10  6:36 [PATCH 0/2] ARM: preserve DMA_FROM_DEVICE buffer contents Karl Mehltretter
  2026-09-10  6:36 ` [PATCH 1/2] ARM: dma-mapping: " Karl Mehltretter
@ 2026-09-10  6:36 ` Karl Mehltretter
  2026-09-10  6:48 ` [PATCH 0/2] ARM: preserve DMA_FROM_DEVICE buffer contents Karl Mehltretter
  2026-09-10  9:14 ` Arnd Bergmann
  3 siblings, 0 replies; 7+ messages in thread
From: Karl Mehltretter @ 2026-09-10  6:36 UTC (permalink / raw)
  To: Russell King
  Cc: Karl Mehltretter, Hans Ulli Kroll, Robin Murphy,
	Marek Szyprowski, Will Deacon, Arnd Bergmann, Christoph Hellwig,
	Linus Walleij, Ard Biesheuvel, linux-arm-kernel, linux-kernel,
	stable

The legacy write-back cache backends invalidate DMA_FROM_DEVICE buffers
when ownership passes to the device. A plain invalidate can discard dirty
CPU cache lines. If the device writes only part of the buffer, untouched
bytes can expose memory contents older than those present at the handoff.

This was reproduced on the ARM926EJ-S (ARMv5TEJ) in a SAM9X75. A
mapping-only test lost 339,936 to 351,744 of 409,600 CPU-written bytes per
100-iteration run. In a separate usbfs test, a webcam returned short
isochronous packets into a buffer that usbfs had cleared. Across five
boots, the existing kernel exposed 29,403 non-zero bytes among 245,708
unwritten gap bytes to userspace.

With this change, five fresh 100-iteration runs on the same SAM9X75 board
each lost 0 of 409,600 bytes.

Use the existing clean-and-invalidate operation for DMA_FROM_DEVICE at
device handoff. The cache lines remain invalid after the handoff, but dirty
CPU data reaches memory first. Keep the existing no-op ownership-return
hooks. These backends already use the same operation for
DMA_BIDIRECTIONAL.

The XScale 80200 A0/A1 path already uses clean-and-invalidate for this case
and remains unchanged.

Cc: stable@vger.kernel.org
Assisted-by: LLM
Signed-off-by: Karl Mehltretter <kmehltretter@gmail.com>
---
 arch/arm/mm/cache-fa.S      | 1 -
 arch/arm/mm/cache-v4wb.S    | 1 -
 arch/arm/mm/proc-arm1020.S  | 1 -
 arch/arm/mm/proc-arm1020e.S | 1 -
 arch/arm/mm/proc-arm1022.S  | 1 -
 arch/arm/mm/proc-arm1026.S  | 1 -
 arch/arm/mm/proc-arm920.S   | 1 -
 arch/arm/mm/proc-arm922.S   | 1 -
 arch/arm/mm/proc-arm925.S   | 1 -
 arch/arm/mm/proc-arm926.S   | 1 -
 arch/arm/mm/proc-arm940.S   | 1 -
 arch/arm/mm/proc-arm946.S   | 1 -
 arch/arm/mm/proc-feroceon.S | 2 --
 arch/arm/mm/proc-mohawk.S   | 1 -
 arch/arm/mm/proc-xsc3.S     | 1 -
 arch/arm/mm/proc-xscale.S   | 1 -
 16 files changed, 17 deletions(-)

diff --git a/arch/arm/mm/cache-fa.S b/arch/arm/mm/cache-fa.S
index e1641799569b..1ff3d66fe944 100644
--- a/arch/arm/mm/cache-fa.S
+++ b/arch/arm/mm/cache-fa.S
@@ -232,7 +232,6 @@ SYM_TYPED_FUNC_START(fa_dma_map_area)
 	add	r1, r1, r0
 	cmp	r2, #DMA_TO_DEVICE
 	beq	fa_dma_clean_range
-	bcs	fa_dma_inv_range
 	b	fa_dma_flush_range
 SYM_FUNC_END(fa_dma_map_area)
 
diff --git a/arch/arm/mm/cache-v4wb.S b/arch/arm/mm/cache-v4wb.S
index 874fe5310f9a..87036c8bb318 100644
--- a/arch/arm/mm/cache-v4wb.S
+++ b/arch/arm/mm/cache-v4wb.S
@@ -244,7 +244,6 @@ SYM_TYPED_FUNC_START(v4wb_dma_map_area)
 	add	r1, r1, r0
 	cmp	r2, #DMA_TO_DEVICE
 	beq	v4wb_dma_clean_range
-	bcs	v4wb_dma_inv_range
 	b	v4wb_dma_flush_range
 SYM_FUNC_END(v4wb_dma_map_area)
 
diff --git a/arch/arm/mm/proc-arm1020.S b/arch/arm/mm/proc-arm1020.S
index 4612a4961e81..c79f3c3e47a0 100644
--- a/arch/arm/mm/proc-arm1020.S
+++ b/arch/arm/mm/proc-arm1020.S
@@ -350,7 +350,6 @@ SYM_TYPED_FUNC_START(arm1020_dma_map_area)
 	add	r1, r1, r0
 	cmp	r2, #DMA_TO_DEVICE
 	beq	arm1020_dma_clean_range
-	bcs	arm1020_dma_inv_range
 	b	arm1020_dma_flush_range
 SYM_FUNC_END(arm1020_dma_map_area)
 
diff --git a/arch/arm/mm/proc-arm1020e.S b/arch/arm/mm/proc-arm1020e.S
index b4a8a3a8eda3..a117e26e4763 100644
--- a/arch/arm/mm/proc-arm1020e.S
+++ b/arch/arm/mm/proc-arm1020e.S
@@ -337,7 +337,6 @@ SYM_TYPED_FUNC_START(arm1020e_dma_map_area)
 	add	r1, r1, r0
 	cmp	r2, #DMA_TO_DEVICE
 	beq	arm1020e_dma_clean_range
-	bcs	arm1020e_dma_inv_range
 	b	arm1020e_dma_flush_range
 SYM_FUNC_END(arm1020e_dma_map_area)
 
diff --git a/arch/arm/mm/proc-arm1022.S b/arch/arm/mm/proc-arm1022.S
index 709870e99e19..63b4992f8ad2 100644
--- a/arch/arm/mm/proc-arm1022.S
+++ b/arch/arm/mm/proc-arm1022.S
@@ -336,7 +336,6 @@ SYM_TYPED_FUNC_START(arm1022_dma_map_area)
 	add	r1, r1, r0
 	cmp	r2, #DMA_TO_DEVICE
 	beq	arm1022_dma_clean_range
-	bcs	arm1022_dma_inv_range
 	b	arm1022_dma_flush_range
 SYM_FUNC_END(arm1022_dma_map_area)
 
diff --git a/arch/arm/mm/proc-arm1026.S b/arch/arm/mm/proc-arm1026.S
index 02f7370a8c5c..c5e50395473b 100644
--- a/arch/arm/mm/proc-arm1026.S
+++ b/arch/arm/mm/proc-arm1026.S
@@ -331,7 +331,6 @@ SYM_TYPED_FUNC_START(arm1026_dma_map_area)
 	add	r1, r1, r0
 	cmp	r2, #DMA_TO_DEVICE
 	beq	arm1026_dma_clean_range
-	bcs	arm1026_dma_inv_range
 	b	arm1026_dma_flush_range
 SYM_FUNC_END(arm1026_dma_map_area)
 
diff --git a/arch/arm/mm/proc-arm920.S b/arch/arm/mm/proc-arm920.S
index 0326067c6c75..e30b013432f0 100644
--- a/arch/arm/mm/proc-arm920.S
+++ b/arch/arm/mm/proc-arm920.S
@@ -299,7 +299,6 @@ SYM_TYPED_FUNC_START(arm920_dma_map_area)
 	add	r1, r1, r0
 	cmp	r2, #DMA_TO_DEVICE
 	beq	arm920_dma_clean_range
-	bcs	arm920_dma_inv_range
 	b	arm920_dma_flush_range
 SYM_FUNC_END(arm920_dma_map_area)
 
diff --git a/arch/arm/mm/proc-arm922.S b/arch/arm/mm/proc-arm922.S
index 3fe6fdf0d325..b1ace8c664e2 100644
--- a/arch/arm/mm/proc-arm922.S
+++ b/arch/arm/mm/proc-arm922.S
@@ -301,7 +301,6 @@ SYM_TYPED_FUNC_START(arm922_dma_map_area)
 	add	r1, r1, r0
 	cmp	r2, #DMA_TO_DEVICE
 	beq	arm922_dma_clean_range
-	bcs	arm922_dma_inv_range
 	b	arm922_dma_flush_range
 SYM_FUNC_END(arm922_dma_map_area)
 
diff --git a/arch/arm/mm/proc-arm925.S b/arch/arm/mm/proc-arm925.S
index 2d15467e4a08..3d91d16e4af0 100644
--- a/arch/arm/mm/proc-arm925.S
+++ b/arch/arm/mm/proc-arm925.S
@@ -357,7 +357,6 @@ SYM_TYPED_FUNC_START(arm925_dma_map_area)
 	add	r1, r1, r0
 	cmp	r2, #DMA_TO_DEVICE
 	beq	arm925_dma_clean_range
-	bcs	arm925_dma_inv_range
 	b	arm925_dma_flush_range
 SYM_FUNC_END(arm925_dma_map_area)
 
diff --git a/arch/arm/mm/proc-arm926.S b/arch/arm/mm/proc-arm926.S
index d94aa8199452..bd335d703590 100644
--- a/arch/arm/mm/proc-arm926.S
+++ b/arch/arm/mm/proc-arm926.S
@@ -320,7 +320,6 @@ SYM_TYPED_FUNC_START(arm926_dma_map_area)
 	add	r1, r1, r0
 	cmp	r2, #DMA_TO_DEVICE
 	beq	arm926_dma_clean_range
-	bcs	arm926_dma_inv_range
 	b	arm926_dma_flush_range
 SYM_FUNC_END(arm926_dma_map_area)
 
diff --git a/arch/arm/mm/proc-arm940.S b/arch/arm/mm/proc-arm940.S
index 545c076c36d2..ba7158639ab7 100644
--- a/arch/arm/mm/proc-arm940.S
+++ b/arch/arm/mm/proc-arm940.S
@@ -264,7 +264,6 @@ SYM_TYPED_FUNC_START(arm940_dma_map_area)
 	add	r1, r1, r0
 	cmp	r2, #DMA_TO_DEVICE
 	beq	arm940_dma_clean_range
-	bcs	arm940_dma_inv_range
 	b	arm940_dma_flush_range
 SYM_FUNC_END(arm940_dma_map_area)
 
diff --git a/arch/arm/mm/proc-arm946.S b/arch/arm/mm/proc-arm946.S
index f3d4e18c3fba..a135c654b854 100644
--- a/arch/arm/mm/proc-arm946.S
+++ b/arch/arm/mm/proc-arm946.S
@@ -306,7 +306,6 @@ SYM_TYPED_FUNC_START(arm946_dma_map_area)
 	add	r1, r1, r0
 	cmp	r2, #DMA_TO_DEVICE
 	beq	arm946_dma_clean_range
-	bcs	arm946_dma_inv_range
 	b	arm946_dma_flush_range
 SYM_FUNC_END(arm946_dma_map_area)
 
diff --git a/arch/arm/mm/proc-feroceon.S b/arch/arm/mm/proc-feroceon.S
index 7f08d06c9625..dbaf01ba7cb5 100644
--- a/arch/arm/mm/proc-feroceon.S
+++ b/arch/arm/mm/proc-feroceon.S
@@ -391,7 +391,6 @@ SYM_TYPED_FUNC_START(feroceon_dma_map_area)
 	add	r1, r1, r0
 	cmp	r2, #DMA_TO_DEVICE
 	beq	feroceon_dma_clean_range
-	bcs	feroceon_dma_inv_range
 	b	feroceon_dma_flush_range
 SYM_FUNC_END(feroceon_dma_map_area)
 
@@ -405,7 +404,6 @@ SYM_TYPED_FUNC_START(feroceon_range_dma_map_area)
 	add	r1, r1, r0
 	cmp	r2, #DMA_TO_DEVICE
 	beq	feroceon_range_dma_clean_range
-	bcs	feroceon_range_dma_inv_range
 	b	feroceon_range_dma_flush_range
 SYM_FUNC_END(feroceon_range_dma_map_area)
 
diff --git a/arch/arm/mm/proc-mohawk.S b/arch/arm/mm/proc-mohawk.S
index 4669c63e3121..7df85a32f7b5 100644
--- a/arch/arm/mm/proc-mohawk.S
+++ b/arch/arm/mm/proc-mohawk.S
@@ -287,7 +287,6 @@ SYM_TYPED_FUNC_START(mohawk_dma_map_area)
 	add	r1, r1, r0
 	cmp	r2, #DMA_TO_DEVICE
 	beq	mohawk_dma_clean_range
-	bcs	mohawk_dma_inv_range
 	b	mohawk_dma_flush_range
 SYM_FUNC_END(mohawk_dma_map_area)
 
diff --git a/arch/arm/mm/proc-xsc3.S b/arch/arm/mm/proc-xsc3.S
index fd25634a2ed5..6c5910bcc211 100644
--- a/arch/arm/mm/proc-xsc3.S
+++ b/arch/arm/mm/proc-xsc3.S
@@ -332,7 +332,6 @@ SYM_TYPED_FUNC_START(xsc3_dma_map_area)
 	add	r1, r1, r0
 	cmp	r2, #DMA_TO_DEVICE
 	beq	xsc3_dma_clean_range
-	bcs	xsc3_dma_inv_range
 	b	xsc3_dma_flush_range
 SYM_FUNC_END(xsc3_dma_map_area)
 
diff --git a/arch/arm/mm/proc-xscale.S b/arch/arm/mm/proc-xscale.S
index d8462df8020b..86422559d91e 100644
--- a/arch/arm/mm/proc-xscale.S
+++ b/arch/arm/mm/proc-xscale.S
@@ -390,7 +390,6 @@ SYM_TYPED_FUNC_START(xscale_dma_map_area)
 	add	r1, r1, r0
 	cmp	r2, #DMA_TO_DEVICE
 	beq	xscale_dma_clean_range
-	bcs	xscale_dma_inv_range
 	b	xscale_dma_flush_range
 SYM_FUNC_END(xscale_dma_map_area)
 
-- 
2.53.0

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH 0/2] ARM: preserve DMA_FROM_DEVICE buffer contents
  2026-09-10  6:36 [PATCH 0/2] ARM: preserve DMA_FROM_DEVICE buffer contents Karl Mehltretter
  2026-09-10  6:36 ` [PATCH 1/2] ARM: dma-mapping: " Karl Mehltretter
  2026-09-10  6:36 ` [PATCH 2/2] ARM: dma-mapping: flush FROM_DEVICE buffers on legacy backends Karl Mehltretter
@ 2026-09-10  6:48 ` Karl Mehltretter
  2026-09-10  9:14 ` Arnd Bergmann
  3 siblings, 0 replies; 7+ messages in thread
From: Karl Mehltretter @ 2026-09-10  6:48 UTC (permalink / raw)
  To: Russell King
  Cc: Hans Ulli Kroll, Robin Murphy, Marek Szyprowski, Will Deacon,
	Arnd Bergmann, Christoph Hellwig, Linus Walleij, Ard Biesheuvel,
	linux-arm-kernel, linux-kernel, stable

On Thu, Sep 10, 2026 at 08:36:18AM +0100, Karl Mehltretter wrote:
> This series prevents a DMA_FROM_DEVICE map from discarding CPU-written
> buffer contents on non-coherent 32-bit ARM. It is based on
> v7.3-rc1-324-g986c24e0fe44.

> Karl Mehltretter (2):
>   ARM: dma-mapping: preserve DMA_FROM_DEVICE buffer contents
>   ARM: dma-mapping: flush FROM_DEVICE buffers on legacy backends
> 

Adding Linus at his current email address.

Sorry for the noise.

Karl

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH 0/2] ARM: preserve DMA_FROM_DEVICE buffer contents
  2026-09-10  6:36 [PATCH 0/2] ARM: preserve DMA_FROM_DEVICE buffer contents Karl Mehltretter
                   ` (2 preceding siblings ...)
  2026-09-10  6:48 ` [PATCH 0/2] ARM: preserve DMA_FROM_DEVICE buffer contents Karl Mehltretter
@ 2026-09-10  9:14 ` Arnd Bergmann
  2026-09-10 10:55   ` Will Deacon
  3 siblings, 1 reply; 7+ messages in thread
From: Arnd Bergmann @ 2026-09-10  9:14 UTC (permalink / raw)
  To: Karl Mehltretter, Russell King
  Cc: Hans Ulli Kroll, Robin Murphy, Marek Szyprowski, Will Deacon,
	Christoph Hellwig, Ard Biesheuvel, linux-arm-kernel,
	linux-kernel, stable, Linus Walleij

On Thu, Sep 10, 2026, at 08:36, Karl Mehltretter wrote:
> This series prevents a DMA_FROM_DEVICE map from discarding CPU-written
> buffer contents on non-coherent 32-bit ARM. It is based on
> v7.3-rc1-324-g986c24e0fe44.
>
> ARM currently invalidates these buffers before the device writes them.
> If the device writes only part of a buffer, discarded dirty cache lines
> can expose older memory contents in the untouched bytes. A stock USB
> webcam demonstrated this through usbfs. Short isochronous packets left
> gaps, and usbfs returned non-zero data to userspace from bytes it had
> cleared.
>
> arm64 changed this handoff from invalidate to clean in 2022 with commit
> c50f11c6196f ("arm64: mm: Don't invalidate FROM_DEVICE buffers at start
> of DMA transfer"). Arnd Bergmann's 2023 ARM32 cache-maintenance series
> left DMA_FROM_DEVICE preservation unresolved [1]. This series keeps the
> existing ownership hooks.

Hi Karl,

I think the main problem here is that we remain inconsistent about the
rules across CPU architectures, and changing Arm on its own does not
mean we have a solution if another architecture decides to change it
in the opposite direction at some point.

I see this as a tradeoff that can go either way:

- the current 32-bit Arm approach (also arc, hexagon, microblaze, mips,
  nios2, openrisc, powerpc32, sh) is obviously faster as it avoids
  the writeback, but it relies on device drivers to ensure no stale
  data can leak back into userspace.

- Will's patch changed arm64 (later copied into riscv) to avoid that
  risk by adding the overhead out of caution, and avoid having to
  audit and fix all drivers.

Clearly the current state is suboptimal, as most drivers are shared
across architectures and should expect a clear interface. Portable
drivers now get extra overhead on arm64/riscv for doing both the
zero-pad and writeback.

If we decide to align with arm64/riscv and take your series, I think
we need two more parts:

- actually measure the performance overhead: you already did the
  work to test this on three separate arm implementations but did
  not share performance numbers.
  Can you quantify how much this costs us on the hardware you used?

- change the remaining architectures the same way: right now, both
  variants are common enough across supported embedded systems
  on all architectures, but changing over arm32 means that all
  only a vanishingly small set of users gets the invalidate-only
  version and we're much more likely to miss future driver bugs
  when driver writes assume the arm/riscv behavior is universal.

       Arnd

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH 0/2] ARM: preserve DMA_FROM_DEVICE buffer contents
  2026-09-10  9:14 ` Arnd Bergmann
@ 2026-09-10 10:55   ` Will Deacon
  2026-09-10 13:15     ` Arnd Bergmann
  0 siblings, 1 reply; 7+ messages in thread
From: Will Deacon @ 2026-09-10 10:55 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: Karl Mehltretter, Russell King, Hans Ulli Kroll, Robin Murphy,
	Marek Szyprowski, Christoph Hellwig, Ard Biesheuvel,
	linux-arm-kernel, linux-kernel, stable, Linus Walleij

On Thu, Sep 10, 2026 at 11:14:28AM +0200, Arnd Bergmann wrote:
> On Thu, Sep 10, 2026, at 08:36, Karl Mehltretter wrote:
> > This series prevents a DMA_FROM_DEVICE map from discarding CPU-written
> > buffer contents on non-coherent 32-bit ARM. It is based on
> > v7.3-rc1-324-g986c24e0fe44.
> >
> > ARM currently invalidates these buffers before the device writes them.
> > If the device writes only part of a buffer, discarded dirty cache lines
> > can expose older memory contents in the untouched bytes. A stock USB
> > webcam demonstrated this through usbfs. Short isochronous packets left
> > gaps, and usbfs returned non-zero data to userspace from bytes it had
> > cleared.
> >
> > arm64 changed this handoff from invalidate to clean in 2022 with commit
> > c50f11c6196f ("arm64: mm: Don't invalidate FROM_DEVICE buffers at start
> > of DMA transfer"). Arnd Bergmann's 2023 ARM32 cache-maintenance series
> > left DMA_FROM_DEVICE preservation unresolved [1]. This series keeps the
> > existing ownership hooks.
> 
> I think the main problem here is that we remain inconsistent about the
> rules across CPU architectures, and changing Arm on its own does not
> mean we have a solution if another architecture decides to change it
> in the opposite direction at some point.
> 
> I see this as a tradeoff that can go either way:
> 
> - the current 32-bit Arm approach (also arc, hexagon, microblaze, mips,
>   nios2, openrisc, powerpc32, sh) is obviously faster as it avoids
>   the writeback, but it relies on device drivers to ensure no stale
>   data can leak back into userspace.
> 
> - Will's patch changed arm64 (later copied into riscv) to avoid that
>   risk by adding the overhead out of caution, and avoid having to
>   audit and fix all drivers.

How would you envisage fixing a driver for this? There were two issues
I tried to address by moving from invalidate to clean on arm64:

1. If the DMA transfer didn't write every cacheline in the buffer, then
   we could expose stale data in the gaps.

2. If the buffer has a pre-existing userspace mapping, then we expose
   stale data during the window between the DMA map() call and the DMA
   itself.

Fixing (1) in the driver would presumably require it to walk through the
buffer after the transfer and zero all the gaps, with an appreciation
for the cache writeback granule (!= cacheline size) and then (somehow)
clean those parts back to the PoC. Is that something any drivers attempt
today?

Fixing (2) in the driver would presumably require ruling out the
possibility of a user alias, which sounds hard and possibly ABI breaking
for some drivers (depending on how they manage their buffers).

> If we decide to align with arm64/riscv and take your series, I think
> we need two more parts:
> 
> - actually measure the performance overhead: you already did the
>   work to test this on three separate arm implementations but did
>   not share performance numbers.
>   Can you quantify how much this costs us on the hardware you used?

It's worth noting that many Arm CPUs upgrade invalidate to
clean+invalidate (either due to the micro-architecture, errata or because
of virtualisation).

Will

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH 0/2] ARM: preserve DMA_FROM_DEVICE buffer contents
  2026-09-10 10:55   ` Will Deacon
@ 2026-09-10 13:15     ` Arnd Bergmann
  0 siblings, 0 replies; 7+ messages in thread
From: Arnd Bergmann @ 2026-09-10 13:15 UTC (permalink / raw)
  To: Will Deacon
  Cc: Karl Mehltretter, Russell King, Hans Ulli Kroll, Robin Murphy,
	Marek Szyprowski, Christoph Hellwig, Ard Biesheuvel,
	linux-arm-kernel, linux-kernel, stable, Linus Walleij

On Thu, Sep 10, 2026, at 12:55, Will Deacon wrote:
> On Thu, Sep 10, 2026 at 11:14:28AM +0200, Arnd Bergmann wrote:
>> I see this as a tradeoff that can go either way:
>> 
>> - the current 32-bit Arm approach (also arc, hexagon, microblaze, mips,
>>   nios2, openrisc, powerpc32, sh) is obviously faster as it avoids
>>   the writeback, but it relies on device drivers to ensure no stale
>>   data can leak back into userspace.
>> 
>> - Will's patch changed arm64 (later copied into riscv) to avoid that
>>   risk by adding the overhead out of caution, and avoid having to
>>   audit and fix all drivers.
>
> How would you envisage fixing a driver for this? There were two issues
> I tried to address by moving from invalidate to clean on arm64:
>
> 1. If the DMA transfer didn't write every cacheline in the buffer, then
>    we could expose stale data in the gaps.
>
> 2. If the buffer has a pre-existing userspace mapping, then we expose
>    stale data during the window between the DMA map() call and the DMA
>    itself.
>
> Fixing (1) in the driver would presumably require it to walk through the
> buffer after the transfer and zero all the gaps, with an appreciation
> for the cache writeback granule (!= cacheline size) and then (somehow)
> clean those parts back to the PoC. Is that something any drivers attempt
> today?

I'm not aware of any driver doing this, but also haven't tried looking
for them. I think the usual assumption is that a driver asking for
a variable-length reply should ensure that it doesn't access of the
data that was not returned, and that the driver understands which
parts were received.

One thing that the arm32 implementation (but not any others as far
as IIRC) does is to do a writeback+invalidate for any partial
cache lines passed into dma_sync_*(), but this of course does
not handle short transfers.

We had at some point discussed using KASAN to debug these better:
mark any memory that is passed to a device as unaccessible through
the DMA mapping API (rounded up to full cache lines), and then mark
the data as accessible again during the sync to the CPU (not rounding
up). As long as the driver only passes the actually received size
into dma_sync_single_for_cpu(), any later access would trigger
a KASAN assertion.

> Fixing (2) in the driver would presumably require ruling out the
> possibility of a user alias, which sounds hard and possibly ABI breaking
> for some drivers (depending on how they manage their buffers).

There are not that many subsystems that do streaming DMA into
user-mapped buffers, so I also can't think of any good example
here where things would actually go wrong in practice. Have you
been able to find an example that runs into this scenario?

Block drivers always transfer entire pages, and I don't think you
can access a page until a transfer from userspace has completed.
GPU and media drivers might be affected, but it looks like those
usually use coherent mappings.

>> - actually measure the performance overhead: you already did the
>>   work to test this on three separate arm implementations but did
>>   not share performance numbers.
>>   Can you quantify how much this costs us on the hardware you used?
>
> It's worth noting that many Arm CPUs upgrade invalidate to
> clean+invalidate (either due to the micro-architecture, errata or because
> of virtualisation).

Right.

    Arnd

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2026-09-10 13:16 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-10  6:36 [PATCH 0/2] ARM: preserve DMA_FROM_DEVICE buffer contents Karl Mehltretter
2026-09-10  6:36 ` [PATCH 1/2] ARM: dma-mapping: " Karl Mehltretter
2026-09-10  6:36 ` [PATCH 2/2] ARM: dma-mapping: flush FROM_DEVICE buffers on legacy backends Karl Mehltretter
2026-09-10  6:48 ` [PATCH 0/2] ARM: preserve DMA_FROM_DEVICE buffer contents Karl Mehltretter
2026-09-10  9:14 ` Arnd Bergmann
2026-09-10 10:55   ` Will Deacon
2026-09-10 13:15     ` Arnd Bergmann

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®