]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
nvme-pci: fix dma mapping leak on data setup error
authorKeith Busch <kbusch@kernel.org>
Tue, 19 May 2026 20:01:57 +0000 (13:01 -0700)
committerKeith Busch <kbusch@kernel.org>
Thu, 21 May 2026 14:49:05 +0000 (07:49 -0700)
We're leaking the initial DMA mapping during iteration if we fail to
allocate the tracking descriptor for both PRP and SGL. Unmap the
iterator directly; we can't use the existing unmap helper because it
depends on the tracking descriptor being successfully allocated, so a
new one for an in-use iterator is provided.

The mappings were also leaking when the driver detects an invalid
bio_vec when mapping PRPs, so fix that too.

Fixes: b8b7570a7ec87 ("nvme-pci: fix dma unmapping when using PRPs and not using the IOVA mapping")
Fixes: 7ce3c1dd78fca ("nvme-pci: convert the data mapping to blk_rq_dma_map")
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Keith Busch <kbusch@kernel.org>
drivers/nvme/host/pci.c

index f423f17184398442fa2865cd2fda939a6d63be0c..b5f84620067899ea7acb037dae92d439c3a2beda 100644 (file)
@@ -997,6 +997,23 @@ static bool nvme_pci_prp_iter_next(struct request *req, struct device *dma_dev,
        return nvme_pci_prp_save_mapping(req, dma_dev, iter);
 }
 
+static void nvme_unmap_iter(struct request *req, struct blk_dma_iter *iter,
+                           struct dma_iova_state *state)
+{
+       struct nvme_queue *nvmeq = req->mq_hctx->driver_data;
+       struct device *dev = nvmeq->dev->dev;
+
+       if (!blk_rq_dma_unmap(req, dev, state, iter->len, iter->p2pdma.map)) {
+               unsigned int attrs = 0;
+
+               if (iter->p2pdma.map == PCI_P2PDMA_MAP_THRU_HOST_BRIDGE)
+                       attrs |= DMA_ATTR_MMIO;
+
+               dma_unmap_phys(dev, iter->addr, iter->len, rq_dma_dir(req),
+                              attrs);
+       }
+}
+
 static blk_status_t nvme_pci_setup_data_prp(struct request *req,
                struct blk_dma_iter *iter)
 {
@@ -1007,8 +1024,10 @@ static blk_status_t nvme_pci_setup_data_prp(struct request *req,
        unsigned int prp_len, i;
        __le64 *prp_list;
 
-       if (!nvme_pci_prp_save_mapping(req, nvmeq->dev->dev, iter))
+       if (!nvme_pci_prp_save_mapping(req, nvmeq->dev->dev, iter)) {
+               nvme_unmap_iter(req, iter, &iod->dma_state);
                return iter->status;
+       }
 
        /*
         * PRP1 always points to the start of the DMA transfers.
@@ -1113,6 +1132,7 @@ bad_sgl:
        dev_err_once(nvmeq->dev->dev,
                "Incorrectly formed request for payload:%d nents:%d\n",
                blk_rq_payload_bytes(req), blk_rq_nr_phys_segments(req));
+       nvme_unmap_data(req);
        return BLK_STS_IOERR;
 }
 
@@ -1156,8 +1176,11 @@ static blk_status_t nvme_pci_setup_data_sgl(struct request *req,
 
        sg_list = dma_pool_alloc(nvme_dma_pool(nvmeq, iod), GFP_ATOMIC,
                        &sgl_dma);
-       if (!sg_list)
+       if (!sg_list) {
+               nvme_unmap_iter(req, iter, &iod->dma_state);
                return BLK_STS_RESOURCE;
+       }
+
        iod->descriptors[iod->nr_descriptors++] = sg_list;
 
        do {
@@ -1314,8 +1337,10 @@ static blk_status_t nvme_pci_setup_meta_iter(struct request *req)
 
        sg_list = dma_pool_alloc(nvmeq->descriptor_pools.small, GFP_ATOMIC,
                        &sgl_dma);
-       if (!sg_list)
+       if (!sg_list) {
+               nvme_unmap_iter(req, &iter, &iod->meta_dma_state);
                return BLK_STS_RESOURCE;
+       }
 
        iod->meta_descriptor = sg_list;
        iod->meta_dma = sgl_dma;