nvme-pci: fix dma mapping leak on data setup error

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: b8b7570a7e ("nvme-pci: fix dma unmapping when using PRPs and not using the IOVA mapping")
Fixes: 7ce3c1dd78 ("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>
This commit is contained in:
Keith Busch 2026-05-19 13:01:57 -07:00
parent 85686c7296
commit 1bf86336e4

View 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 @@ static blk_status_t nvme_pci_setup_data_prp(struct request *req,
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;