Skip to content

Commit 63a5c7a

Browse files
Christoph Hellwigkeithbusch
authored andcommitted
nvme-pci: use dma_alloc_noncontigous if possible
Use dma_alloc_noncontigous to allocate a single IOVA-contigous segment when backed by an IOMMU. This allow to easily use bigger segments and avoids running into segment limits if we can avoid it. Signed-off-by: Christoph Hellwig <[email protected]> Signed-off-by: Keith Busch <[email protected]>
1 parent 3c2fb1c commit 63a5c7a

File tree

1 file changed

+53
-5
lines changed

1 file changed

+53
-5
lines changed

drivers/nvme/host/pci.c

Lines changed: 53 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ struct nvme_dev {
141141
struct nvme_ctrl ctrl;
142142
u32 last_ps;
143143
bool hmb;
144+
struct sg_table *hmb_sgt;
144145

145146
mempool_t *iod_mempool;
146147

@@ -1952,7 +1953,7 @@ static int nvme_set_host_mem(struct nvme_dev *dev, u32 bits)
19521953
return ret;
19531954
}
19541955

1955-
static void nvme_free_host_mem(struct nvme_dev *dev)
1956+
static void nvme_free_host_mem_multi(struct nvme_dev *dev)
19561957
{
19571958
int i;
19581959

@@ -1967,14 +1968,50 @@ static void nvme_free_host_mem(struct nvme_dev *dev)
19671968

19681969
kfree(dev->host_mem_desc_bufs);
19691970
dev->host_mem_desc_bufs = NULL;
1971+
}
1972+
1973+
static void nvme_free_host_mem(struct nvme_dev *dev)
1974+
{
1975+
if (dev->hmb_sgt)
1976+
dma_free_noncontiguous(dev->dev, dev->host_mem_size,
1977+
dev->hmb_sgt, DMA_BIDIRECTIONAL);
1978+
else
1979+
nvme_free_host_mem_multi(dev);
1980+
19701981
dma_free_coherent(dev->dev, dev->host_mem_descs_size,
19711982
dev->host_mem_descs, dev->host_mem_descs_dma);
19721983
dev->host_mem_descs = NULL;
19731984
dev->host_mem_descs_size = 0;
19741985
dev->nr_host_mem_descs = 0;
19751986
}
19761987

1977-
static int __nvme_alloc_host_mem(struct nvme_dev *dev, u64 preferred,
1988+
static int nvme_alloc_host_mem_single(struct nvme_dev *dev, u64 size)
1989+
{
1990+
dev->hmb_sgt = dma_alloc_noncontiguous(dev->dev, size,
1991+
DMA_BIDIRECTIONAL, GFP_KERNEL, 0);
1992+
if (!dev->hmb_sgt)
1993+
return -ENOMEM;
1994+
1995+
dev->host_mem_descs = dma_alloc_coherent(dev->dev,
1996+
sizeof(*dev->host_mem_descs), &dev->host_mem_descs_dma,
1997+
GFP_KERNEL);
1998+
if (!dev->host_mem_descs) {
1999+
dma_free_noncontiguous(dev->dev, dev->host_mem_size,
2000+
dev->hmb_sgt, DMA_BIDIRECTIONAL);
2001+
dev->hmb_sgt = NULL;
2002+
return -ENOMEM;
2003+
}
2004+
dev->host_mem_size = size;
2005+
dev->host_mem_descs_size = sizeof(*dev->host_mem_descs);
2006+
dev->nr_host_mem_descs = 1;
2007+
2008+
dev->host_mem_descs[0].addr =
2009+
cpu_to_le64(dev->hmb_sgt->sgl->dma_address);
2010+
dev->host_mem_descs[0].size = cpu_to_le32(size / NVME_CTRL_PAGE_SIZE);
2011+
return 0;
2012+
}
2013+
2014+
static int nvme_alloc_host_mem_multi(struct nvme_dev *dev, u64 preferred,
19782015
u32 chunk_size)
19792016
{
19802017
struct nvme_host_mem_buf_desc *descs;
@@ -2049,9 +2086,18 @@ static int nvme_alloc_host_mem(struct nvme_dev *dev, u64 min, u64 preferred)
20492086
u64 hmminds = max_t(u32, dev->ctrl.hmminds * 4096, PAGE_SIZE * 2);
20502087
u64 chunk_size;
20512088

2089+
/*
2090+
* If there is an IOMMU that can merge pages, try a virtually
2091+
* non-contiguous allocation for a single segment first.
2092+
*/
2093+
if (!(PAGE_SIZE & dma_get_merge_boundary(dev->dev))) {
2094+
if (!nvme_alloc_host_mem_single(dev, preferred))
2095+
return 0;
2096+
}
2097+
20522098
/* start big and work our way down */
20532099
for (chunk_size = min_chunk; chunk_size >= hmminds; chunk_size /= 2) {
2054-
if (!__nvme_alloc_host_mem(dev, preferred, chunk_size)) {
2100+
if (!nvme_alloc_host_mem_multi(dev, preferred, chunk_size)) {
20552101
if (!min || dev->host_mem_size >= min)
20562102
return 0;
20572103
nvme_free_host_mem(dev);
@@ -2099,8 +2145,10 @@ static int nvme_setup_host_mem(struct nvme_dev *dev)
20992145
}
21002146

21012147
dev_info(dev->ctrl.device,
2102-
"allocated %lld MiB host memory buffer.\n",
2103-
dev->host_mem_size >> ilog2(SZ_1M));
2148+
"allocated %lld MiB host memory buffer (%u segment%s).\n",
2149+
dev->host_mem_size >> ilog2(SZ_1M),
2150+
dev->nr_host_mem_descs,
2151+
str_plural(dev->nr_host_mem_descs));
21042152
}
21052153

21062154
ret = nvme_set_host_mem(dev, enable_bits);

0 commit comments

Comments
 (0)