When dma_iova_link() fails partway through mapping a request's bvec list, the function breaks out of the loop without cleaning up the already-mapped portions. Similarly, if dma_iova_sync() fails after all segments are linked, no cleanup is performed.
This leaves the IOVA state partially mapped. The completion path (via dma_iova_destroy() or nvme_unmap_data()) then attempts to unmap the full expected size, but only a partial size was actually mapped.
Fix by adding an out_unlink error path that calls dma_iova_destroy() to clean up any partial mapping before returning failure. The dma_iova_destroy() function handles both partial unlink and IOVA space freeing, and correctly handles the case where mapped_len is zero (first dma_iova_link() failed) by just freeing the IOVA allocation.
This ensures that when an error occurs: 1. All partially-mapped IOVA ranges are properly unmapped 2. The IOVA address space is freed 3. The completion path won't attempt to unmap non-existent mappings
Fixes: 858299dc6160 ("block: add scatterlist-less DMA mapping helpers") Cc: stable@vger.kernel.org Signed-off-by: Chaitanya Kulkarni ckulkarnilinux@gmail.com ---
Hi Leon,
Your last email is not accessible to me.
Updated the patch description to explain dma_iova_destroy().
Please let me know for any issues you want me to fix before I send.
-ck
--- block/blk-mq-dma.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-)
diff --git a/block/blk-mq-dma.c b/block/blk-mq-dma.c index fb018fffffdc..feead1934301 100644 --- a/block/blk-mq-dma.c +++ b/block/blk-mq-dma.c @@ -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,
linux-stable-mirror@lists.linaro.org