]> www.infradead.org Git - users/hch/misc.git/commitdiff
nvme-pci: fix freeing of the HMB descriptor table
authorChristoph Hellwig <hch@lst.de>
Tue, 29 Oct 2024 04:03:09 +0000 (05:03 +0100)
committerChristoph Hellwig <hch@lst.de>
Tue, 29 Oct 2024 04:44:23 +0000 (05:44 +0100)
The HMB descriptor table is sized to the maximum number of descriptors
that could be used for a given device, but __nvme_alloc_host_mem could
break out of the loop earlier on memory allocation failure and end up
using less descriptors than planned for, which leads to an incorrect
size passed to dma_free_coherent.

In practice this was not showing up because the number of descriptors
tends to be low and the dma coherent allocator always allocates and
frees at least a page.

Fixes: 87ad72a59a38 ("nvme-pci: implement host memory buffer support")
Signed-off-by: Christoph Hellwig <hch@lst.de>
drivers/nvme/host/pci.c

index 4b9fda0b1d9a33af4d7030b72532835b205e9cbb..34daf6d8db07b2a6519bf58231a9b820932fab15 100644 (file)
@@ -153,6 +153,7 @@ struct nvme_dev {
        /* host memory buffer support: */
        u64 host_mem_size;
        u32 nr_host_mem_descs;
+       u32 host_mem_descs_size;
        dma_addr_t host_mem_descs_dma;
        struct nvme_host_mem_buf_desc *host_mem_descs;
        void **host_mem_desc_bufs;
@@ -1966,10 +1967,10 @@ static void nvme_free_host_mem(struct nvme_dev *dev)
 
        kfree(dev->host_mem_desc_bufs);
        dev->host_mem_desc_bufs = NULL;
-       dma_free_coherent(dev->dev,
-                       dev->nr_host_mem_descs * sizeof(*dev->host_mem_descs),
+       dma_free_coherent(dev->dev, dev->host_mem_descs_size,
                        dev->host_mem_descs, dev->host_mem_descs_dma);
        dev->host_mem_descs = NULL;
+       dev->host_mem_descs_size = 0;
        dev->nr_host_mem_descs = 0;
 }
 
@@ -1977,7 +1978,7 @@ static int __nvme_alloc_host_mem(struct nvme_dev *dev, u64 preferred,
                u32 chunk_size)
 {
        struct nvme_host_mem_buf_desc *descs;
-       u32 max_entries, len;
+       u32 max_entries, len, descs_size;
        dma_addr_t descs_dma;
        int i = 0;
        void **bufs;
@@ -1990,8 +1991,9 @@ static int __nvme_alloc_host_mem(struct nvme_dev *dev, u64 preferred,
        if (dev->ctrl.hmmaxd && dev->ctrl.hmmaxd < max_entries)
                max_entries = dev->ctrl.hmmaxd;
 
-       descs = dma_alloc_coherent(dev->dev, max_entries * sizeof(*descs),
-                                  &descs_dma, GFP_KERNEL);
+       descs_size = max_entries * sizeof(*descs);
+       descs = dma_alloc_coherent(dev->dev, descs_size, &descs_dma,
+                       GFP_KERNEL);
        if (!descs)
                goto out;
 
@@ -2020,6 +2022,7 @@ static int __nvme_alloc_host_mem(struct nvme_dev *dev, u64 preferred,
        dev->host_mem_size = size;
        dev->host_mem_descs = descs;
        dev->host_mem_descs_dma = descs_dma;
+       dev->host_mem_descs_size = descs_size;
        dev->host_mem_desc_bufs = bufs;
        return 0;
 
@@ -2034,8 +2037,7 @@ out_free_bufs:
 
        kfree(bufs);
 out_free_descs:
-       dma_free_coherent(dev->dev, max_entries * sizeof(*descs), descs,
-                       descs_dma);
+       dma_free_coherent(dev->dev, descs_size, descs, descs_dma);
 out:
        dev->host_mem_descs = NULL;
        return -ENOMEM;