[PATCH v2 08/11] dma: fix dma_sync when not all device DMA is equally coherent

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]


The LS1046A features a cache-coherent interconnect and the drivers
configure the hardware appropriately, e.g. setting the FMan PRAM_MODE_GLOBAL
bit, so the existing Ethernet Controllers snoop caches.

Yet, we use the standard arm64 cache maintenance routines when the MMU
is enabled and thus risk memory corruption if CPU prefetches receive buffers
in the time window between dma_map_single() cleaning them to
Point-of-Coherency and dma_unmap_single() invalidating them[1].

To properly solve this issue, we need to consult the newly added per-device
dma coherent attribute to decide whether to do manual cache maintenance.

[1]: https://lore.kernel.org/all/a5d6cc26-cd23-7c31-f56e-f6d535ea39b0@xxxxxxx/

Signed-off-by: Ahmad Fatoum <a.fatoum@xxxxxxxxxxxxxx>
v1 -> v2:
  - switch to boolean comparisons instead of comparison <= or >= to zero
 drivers/dma/map.c | 12 ++++++++----
 1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/drivers/dma/map.c b/drivers/dma/map.c
index e320f6aad4ac..ab86a8c7b139 100644
--- a/drivers/dma/map.c
+++ b/drivers/dma/map.c
@@ -9,7 +9,8 @@ void dma_sync_single_for_cpu(struct device *dev, dma_addr_t address,
 	debug_dma_sync_single_for_cpu(dev, address, size, dir);
-	arch_sync_dma_for_cpu(ptr, size, dir);
+	if (!dev_is_dma_coherent(dev))
+		arch_sync_dma_for_cpu(ptr, size, dir);
 void dma_sync_single_for_device(struct device *dev, dma_addr_t address,
@@ -19,7 +20,8 @@ void dma_sync_single_for_device(struct device *dev, dma_addr_t address,
 	debug_dma_sync_single_for_device(dev, address, size, dir);
-	arch_sync_dma_for_device(ptr, size, dir);
+	if (!dev_is_dma_coherent(dev))
+		arch_sync_dma_for_device(ptr, size, dir);
 dma_addr_t dma_map_single(struct device *dev, void *ptr,
@@ -29,7 +31,8 @@ dma_addr_t dma_map_single(struct device *dev, void *ptr,
 	debug_dma_map(dev, ptr, size, dir, dma_addr);
-	arch_sync_dma_for_device(ptr, size, dir);
+	if (!dev_is_dma_coherent(dev))
+		arch_sync_dma_for_device(ptr, size, dir);
 	return dma_addr;
@@ -37,7 +40,8 @@ dma_addr_t dma_map_single(struct device *dev, void *ptr,
 void dma_unmap_single(struct device *dev, dma_addr_t dma_addr,
 				    size_t size, enum dma_data_direction dir)
-	dma_sync_single_for_cpu(dev, dma_addr, size, dir);
+	if (!dev_is_dma_coherent(dev))
+		dma_sync_single_for_cpu(dev, dma_addr, size, dir);
 	debug_dma_unmap(dev, dma_addr, size, dir);

[Index of Archives]     [Linux Embedded]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [XFree86]

  Powered by Linux