]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
block: fix partial IOVA mapping cleanup in blk_rq_dma_map_iova
authorChaitanya Kulkarni <kch@nvidia.com>
Wed, 11 Feb 2026 20:49:44 +0000 (12:49 -0800)
committerSasha Levin <sashal@kernel.org>
Wed, 4 Mar 2026 12:20:43 +0000 (07:20 -0500)
[ Upstream commit 81e7223b1a2d63b655ee72577c8579f968d037e3 ]

When dma_iova_link() fails partway through mapping a request's bvec
list, the function breaks out of the loop without cleaning up
already mapped segments. Similarly, if dma_iova_sync() fails after
linking all segments, no cleanup is performed.

This leaves partial IOVA mappings in place. The completion path
attempts to unmap the full expected size via dma_iova_destroy() or
nvme_unmap_data(), but only a partial size was actually mapped,
leading to incorrect unmap operations.

Add an out_unlink error path that calls dma_iova_destroy() to clean
up partial mappings before returning failure. The dma_iova_destroy()
function handles both partial unlink and IOVA space freeing. It
correctly handles the mapped_len == 0 case (first dma_iova_link()
failure) by only freeing the IOVA allocation without attempting to
unmap.

Signed-off-by: Chaitanya Kulkarni <kch@nvidia.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Signed-off-by: Sasha Levin <sashal@kernel.org>
block/blk-mq-dma.c

index fb018fffffdcc5f411f1f287d718f0b114d179a7..feead1934301a827bee77f468754d4e5d30f3e60 100644 (file)
@@ -126,17 +126,20 @@ static bool blk_rq_dma_map_iova(struct request *req, struct device *dma_dev,
                error = dma_iova_link(dma_dev, state, vec->paddr, mapped,
                                vec->len, dir, attrs);
                if (error)
-                       break;
+                       goto out_unlink;
                mapped += vec->len;
        } while (blk_map_iter_next(req, &iter->iter, vec));
 
        error = dma_iova_sync(dma_dev, state, 0, mapped);
-       if (error) {
-               iter->status = errno_to_blk_status(error);
-               return false;
-       }
+       if (error)
+               goto out_unlink;
 
        return true;
+
+out_unlink:
+       dma_iova_destroy(dma_dev, state, mapped, dir, attrs);
+       iter->status = errno_to_blk_status(error);
+       return false;
 }
 
 static inline void blk_rq_map_iter_init(struct request *rq,