[PATCH] nvme: fix dma direction macro in dma_unmap_page

zhichuang at google.com zhichuang at google.com
Fri Jul 14 12:14:51 PDT 2023


From: Zhichuang Sun <zhichuang at google.com>

This patch fix a DMA related bug in the nvme driver,
which caused a surge of DMA errors. The DMA debug
subsystem complains:

"nvme device driver frees DMA memory with different direction
[mapped with DMA_FROM_DEVICE] [unmapped with DMA_BIDIRECTIONAL]"

It's caused by the wrong macro used in "dma_unmap_page()" to
specify the dma direction. It should use "rq_dma_dir(req)"
instead of "rq_data_dir(req)".

For map:
drivers/nvme/host/pci.c
nvme_queue_rq
  --> nvme_map_data
    --> nvme_map_metadata
      --> dma_map_bvec(dev->dev, rq_integrity_vec(req), rq_dma_dir(req), 0);
         --> dma_map_page_attrs(dev, (bv)->bv_page, (bv)->bv_offset, \
				 (bv)->bv_len, (dir), (attrs))

For unmap:
drivers/nvme/host/pci.c
nvme_pci_complete_rq
  --> dma_unmap_page(dev->dev, iod->meta_dma, \
		rq_integrity_vec(req)->bv_len, rq_data_dir(req));
    --> dma_unmap_page_attrs(dev, addr, len, dir, attrs)

For the same req, during map, the mapping direction is passed with
rq_dma_dir(req); during unmap, the mapping direction is passed with
rq_data_dir(req). rq_data_dir and rq_dma_dir interprate direction
differently, see definition below:

include/linux/blkdev.h
 #define rq_data_dir(rq)         (op_is_write(req_op(rq)) ? WRITE : READ)

 #define rq_dma_dir(rq) \
         (op_is_write(req_op(rq)) ? DMA_TO_DEVICE : DMA_FROM_DEVICE)

include/linux/kernel.h
 /* generic data direction definitions */
 #define READ                    0
 #define WRITE                   1

include/linux/dma-direction.h
 enum dma_data_direction {
         DMA_BIDIRECTIONAL = 0,
         DMA_TO_DEVICE = 1,
         DMA_FROM_DEVICE = 2,
         DMA_NONE = 3,
 };

As a result, for a read DMA req, during map, it is recorded as
DMA_FROM_DEVICE(2) with rq_dma_dir(rq), and during unmap, it is
unmapped as READ(0) with rq_data_dir, which will be interprated
as DMA_DMA_BIDIRECTIONAL(0), thus causing a mismatch during DMA
unmap, triggering false alarms of DMA bugs.

Signed-off-by: Zhichuang Sun <zhichuang at google.com>
---
 drivers/nvme/host/pci.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c
index 72725729cb6c..7778f384293c 100644
--- a/drivers/nvme/host/pci.c
+++ b/drivers/nvme/host/pci.c
@@ -967,7 +967,7 @@ static __always_inline void nvme_pci_unmap_rq(struct request *req)
 	        struct nvme_iod *iod = blk_mq_rq_to_pdu(req);
 
 		dma_unmap_page(dev->dev, iod->meta_dma,
-			       rq_integrity_vec(req)->bv_len, rq_data_dir(req));
+			       rq_integrity_vec(req)->bv_len, rq_dma_dir(req));
 	}
 
 	if (blk_rq_nr_phys_segments(req))
-- 
2.41.0.255.g8b1d071c50-goog




More information about the Linux-nvme mailing list