git: 361c6a6b93fd - stable/15 - LinuxKPI: Fix DMA_BIDIRECTIONAL and other mappings

From: Bjoern A. Zeeb <bz_at_FreeBSD.org>
Date: Thu, 10 Sep 2026 09:48:42 UTC
The branch stable/15 has been updated by bz:

URL: https://cgit.FreeBSD.org/src/commit/?id=361c6a6b93fde0c112158b74611c9712ec8683b7

commit 361c6a6b93fde0c112158b74611c9712ec8683b7
Author:     Zishun Yi <zishun.yi.dev@gmail.com>
AuthorDate: 2026-09-04 19:11:35 +0000
Commit:     Bjoern A. Zeeb <bz@FreeBSD.org>
CommitDate: 2026-09-10 09:46:54 +0000

    LinuxKPI: Fix DMA_BIDIRECTIONAL and other mappings
    
    In dma_sync_single_for_cpu(), the DMA_BIDIRECTIONAL direction currently
    performs BUS_DMASYNC_POSTREAD followed by BUS_DMASYNC_PREREAD. This
    patch corrects the mapping to use BUS_DMASYNC_POSTREAD |
    BUS_DMASYNC_POSTWRITE.
    
    When ownership of the DMA area is transferred to the CPU, we must assume
    the previous device access was bidirectional. Both POST operations are
    necessary to ensure the CPU sees a consistent view of memory after
    potential device reads and writes. A PREREAD is unnecessary here because
    the device will no longer access the memory since ownership has been
    transferred to the CPU.
    
    Conversely, for dma_sync_single_for_device(), ownership is being
    transferred back to the hardware. The buffer must be prepared for
    potential bidirectional access by the device, requiring
    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE.
    
    PR:             293381, 297155
    Reported by:    Zishun Yi <zishun.yi.dev@gmail.com>
    Reported by:    Kim Shrier (fbsdbugs westryn.net)
    Fixes:          95edb10b47fc ("LinuxKPI: implement dma_sync_single_for_*, apply to (un)map single/sg")
    Signed-off-by:  Zishun Yi <zishun.yi.dev@gmail.com>
    Reviewed by:    aokblast, bz
    Differential Revision: https://reviews.freebsd.org/D55497
    
    (cherry picked from commit 6e1436d748273be52fb155914db3efae075a8619)
---
 sys/compat/linuxkpi/common/include/linux/dma-mapping.h | 10 ++++------
 sys/compat/linuxkpi/common/src/linux_pci.c             | 15 ++++++++-------
 2 files changed, 12 insertions(+), 13 deletions(-)

diff --git a/sys/compat/linuxkpi/common/include/linux/dma-mapping.h b/sys/compat/linuxkpi/common/include/linux/dma-mapping.h
index 5e5d40ef8339..d32518a4d6ae 100644
--- a/sys/compat/linuxkpi/common/include/linux/dma-mapping.h
+++ b/sys/compat/linuxkpi/common/include/linux/dma-mapping.h
@@ -245,9 +245,7 @@ dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma, size_t size,
 
 	switch (direction) {
 	case DMA_BIDIRECTIONAL:
-		op = BUS_DMASYNC_POSTREAD;
-		linuxkpi_dma_sync(dev, dma, size, op);
-		op = BUS_DMASYNC_PREREAD;
+		op = BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE;
 		break;
 	case DMA_TO_DEVICE:
 		op = BUS_DMASYNC_POSTWRITE;
@@ -277,13 +275,13 @@ dma_sync_single_for_device(struct device *dev, dma_addr_t dma,
 
 	switch (direction) {
 	case DMA_BIDIRECTIONAL:
-		op = BUS_DMASYNC_PREWRITE;
+		op = BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD;
 		break;
 	case DMA_TO_DEVICE:
-		op = BUS_DMASYNC_PREREAD;
+		op = BUS_DMASYNC_PREWRITE;
 		break;
 	case DMA_FROM_DEVICE:
-		op = BUS_DMASYNC_PREWRITE;
+		op = BUS_DMASYNC_PREREAD;
 		break;
 	default:
 		return;
diff --git a/sys/compat/linuxkpi/common/src/linux_pci.c b/sys/compat/linuxkpi/common/src/linux_pci.c
index 940926c6297c..9d4d7bba8265 100644
--- a/sys/compat/linuxkpi/common/src/linux_pci.c
+++ b/sys/compat/linuxkpi/common/src/linux_pci.c
@@ -1842,8 +1842,8 @@ lkpi_dma_unmap(struct device *dev, dma_addr_t dma_addr, size_t len,
 	/* dma_sync_single_for_cpu() unrolled to avoid lock recursicn. */
 	switch (direction) {
 	case DMA_BIDIRECTIONAL:
-		bus_dmamap_sync(obj->dmat, obj->dmamap, BUS_DMASYNC_POSTREAD);
-		bus_dmamap_sync(obj->dmat, obj->dmamap, BUS_DMASYNC_PREREAD);
+		bus_dmamap_sync(obj->dmat, obj->dmamap,
+		    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
 		break;
 	case DMA_TO_DEVICE:
 		bus_dmamap_sync(obj->dmat, obj->dmamap, BUS_DMASYNC_POSTWRITE);
@@ -2053,13 +2053,14 @@ linux_dma_map_sg_attrs(struct device *dev, struct scatterlist *sgl, int nents,
 
 	switch (direction) {
 	case DMA_BIDIRECTIONAL:
-		bus_dmamap_sync(priv->dmat, sgl->dma_map, BUS_DMASYNC_PREWRITE);
+		bus_dmamap_sync(priv->dmat, sgl->dma_map,
+		    BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
 		break;
 	case DMA_TO_DEVICE:
-		bus_dmamap_sync(priv->dmat, sgl->dma_map, BUS_DMASYNC_PREREAD);
+		bus_dmamap_sync(priv->dmat, sgl->dma_map, BUS_DMASYNC_PREWRITE);
 		break;
 	case DMA_FROM_DEVICE:
-		bus_dmamap_sync(priv->dmat, sgl->dma_map, BUS_DMASYNC_PREWRITE);
+		bus_dmamap_sync(priv->dmat, sgl->dma_map, BUS_DMASYNC_PREREAD);
 		break;
 	default:
 		break;
@@ -2087,8 +2088,8 @@ linux_dma_unmap_sg_attrs(struct device *dev, struct scatterlist *sgl,
 
 	switch (direction) {
 	case DMA_BIDIRECTIONAL:
-		bus_dmamap_sync(priv->dmat, sgl->dma_map, BUS_DMASYNC_POSTREAD);
-		bus_dmamap_sync(priv->dmat, sgl->dma_map, BUS_DMASYNC_PREREAD);
+		bus_dmamap_sync(priv->dmat, sgl->dma_map,
+		    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
 		break;
 	case DMA_TO_DEVICE:
 		bus_dmamap_sync(priv->dmat, sgl->dma_map, BUS_DMASYNC_POSTWRITE);