nvme-pci: fix freeing of the HMB descriptor table

[ Upstream commit 3c2fb1ca8086eb139b2a551358137525ae8e0d7a ]

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: 87ad72a59a ("nvme-pci: implement host memory buffer support")
Signed-off-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Keith Busch <kbusch@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
Christoph Hellwig
2024-11-01 05:40:04 +01:00
committed by Greg Kroah-Hartman
parent f892ddcf9f
commit fb96d5cfa9

View File

@@ -153,6 +153,7 @@ struct nvme_dev {
/* host memory buffer support: */ /* host memory buffer support: */
u64 host_mem_size; u64 host_mem_size;
u32 nr_host_mem_descs; u32 nr_host_mem_descs;
u32 host_mem_descs_size;
dma_addr_t host_mem_descs_dma; dma_addr_t host_mem_descs_dma;
struct nvme_host_mem_buf_desc *host_mem_descs; struct nvme_host_mem_buf_desc *host_mem_descs;
void **host_mem_desc_bufs; void **host_mem_desc_bufs;
@@ -2007,10 +2008,10 @@ static void nvme_free_host_mem(struct nvme_dev *dev)
kfree(dev->host_mem_desc_bufs); kfree(dev->host_mem_desc_bufs);
dev->host_mem_desc_bufs = NULL; dev->host_mem_desc_bufs = NULL;
dma_free_coherent(dev->dev, dma_free_coherent(dev->dev, dev->host_mem_descs_size,
dev->nr_host_mem_descs * sizeof(*dev->host_mem_descs),
dev->host_mem_descs, dev->host_mem_descs_dma); dev->host_mem_descs, dev->host_mem_descs_dma);
dev->host_mem_descs = NULL; dev->host_mem_descs = NULL;
dev->host_mem_descs_size = 0;
dev->nr_host_mem_descs = 0; dev->nr_host_mem_descs = 0;
} }
@@ -2018,7 +2019,7 @@ static int __nvme_alloc_host_mem(struct nvme_dev *dev, u64 preferred,
u32 chunk_size) u32 chunk_size)
{ {
struct nvme_host_mem_buf_desc *descs; struct nvme_host_mem_buf_desc *descs;
u32 max_entries, len; u32 max_entries, len, descs_size;
dma_addr_t descs_dma; dma_addr_t descs_dma;
int i = 0; int i = 0;
void **bufs; void **bufs;
@@ -2031,8 +2032,9 @@ static int __nvme_alloc_host_mem(struct nvme_dev *dev, u64 preferred,
if (dev->ctrl.hmmaxd && dev->ctrl.hmmaxd < max_entries) if (dev->ctrl.hmmaxd && dev->ctrl.hmmaxd < max_entries)
max_entries = dev->ctrl.hmmaxd; max_entries = dev->ctrl.hmmaxd;
descs = dma_alloc_coherent(dev->dev, max_entries * sizeof(*descs), descs_size = max_entries * sizeof(*descs);
&descs_dma, GFP_KERNEL); descs = dma_alloc_coherent(dev->dev, descs_size, &descs_dma,
GFP_KERNEL);
if (!descs) if (!descs)
goto out; goto out;
@@ -2061,6 +2063,7 @@ static int __nvme_alloc_host_mem(struct nvme_dev *dev, u64 preferred,
dev->host_mem_size = size; dev->host_mem_size = size;
dev->host_mem_descs = descs; dev->host_mem_descs = descs;
dev->host_mem_descs_dma = descs_dma; dev->host_mem_descs_dma = descs_dma;
dev->host_mem_descs_size = descs_size;
dev->host_mem_desc_bufs = bufs; dev->host_mem_desc_bufs = bufs;
return 0; return 0;
@@ -2075,8 +2078,7 @@ out_free_bufs:
kfree(bufs); kfree(bufs);
out_free_descs: out_free_descs:
dma_free_coherent(dev->dev, max_entries * sizeof(*descs), descs, dma_free_coherent(dev->dev, descs_size, descs, descs_dma);
descs_dma);
out: out:
dev->host_mem_descs = NULL; dev->host_mem_descs = NULL;
return -ENOMEM; return -ENOMEM;