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 --- 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