Skip to content

Commit 07da122

Browse files
maorgottliebjgunthorpe
authored andcommitted
lib/scatterlist: Add support in dynamic allocation of SG table from pages
Extend __sg_alloc_table_from_pages to support dynamic allocation of SG table from pages. It should be used by drivers that can't supply all the pages at one time. This function returns the last populated SGE in the table. Users should pass it as an argument to the function from the second call and forward. As before, nents will be equal to the number of populated SGEs (chunks). With this new extension, drivers can benefit the optimization of merging contiguous pages without a need to allocate all pages in advance and hold them in a large buffer. E.g. with the Infiniband driver that allocates a single page for hold the pages. For 1TB memory registration, the temporary buffer would consume only 4KB, instead of 2GB. Link: https://lore.kernel.org/r/20201004154340.1080481-2-leon@kernel.org Signed-off-by: Maor Gottlieb <maorg@nvidia.com> Reviewed-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Leon Romanovsky <leonro@nvidia.com> Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
1 parent 29d8868 commit 07da122

5 files changed

Lines changed: 142 additions & 57 deletions

File tree

drivers/gpu/drm/i915/gem/i915_gem_userptr.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -403,20 +403,20 @@ __i915_gem_userptr_alloc_pages(struct drm_i915_gem_object *obj,
403403
unsigned int max_segment = i915_sg_segment_size();
404404
struct sg_table *st;
405405
unsigned int sg_page_sizes;
406+
struct scatterlist *sg;
406407
int ret;
407408

408409
st = kmalloc(sizeof(*st), GFP_KERNEL);
409410
if (!st)
410411
return ERR_PTR(-ENOMEM);
411412

412413
alloc_table:
413-
ret = __sg_alloc_table_from_pages(st, pvec, num_pages,
414-
0, num_pages << PAGE_SHIFT,
415-
max_segment,
416-
GFP_KERNEL);
417-
if (ret) {
414+
sg = __sg_alloc_table_from_pages(st, pvec, num_pages, 0,
415+
num_pages << PAGE_SHIFT, max_segment,
416+
NULL, 0, GFP_KERNEL);
417+
if (IS_ERR(sg)) {
418418
kfree(st);
419-
return ERR_PTR(ret);
419+
return ERR_CAST(sg);
420420
}
421421

422422
ret = i915_gem_gtt_prepare_pages(obj, st);

drivers/gpu/drm/vmwgfx/vmwgfx_ttm_buffer.c

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,7 @@ static int vmw_ttm_map_dma(struct vmw_ttm_tt *vmw_tt)
419419
int ret = 0;
420420
static size_t sgl_size;
421421
static size_t sgt_size;
422+
struct scatterlist *sg;
422423

423424
if (vmw_tt->mapped)
424425
return 0;
@@ -441,13 +442,15 @@ static int vmw_ttm_map_dma(struct vmw_ttm_tt *vmw_tt)
441442
if (unlikely(ret != 0))
442443
return ret;
443444

444-
ret = __sg_alloc_table_from_pages
445-
(&vmw_tt->sgt, vsgt->pages, vsgt->num_pages, 0,
446-
(unsigned long) vsgt->num_pages << PAGE_SHIFT,
447-
dma_get_max_seg_size(dev_priv->dev->dev),
448-
GFP_KERNEL);
449-
if (unlikely(ret != 0))
445+
sg = __sg_alloc_table_from_pages(&vmw_tt->sgt, vsgt->pages,
446+
vsgt->num_pages, 0,
447+
(unsigned long) vsgt->num_pages << PAGE_SHIFT,
448+
dma_get_max_seg_size(dev_priv->dev->dev),
449+
NULL, 0, GFP_KERNEL);
450+
if (IS_ERR(sg)) {
451+
ret = PTR_ERR(sg);
450452
goto out_sg_alloc_fail;
453+
}
451454

452455
if (vsgt->num_pages > vmw_tt->sgt.nents) {
453456
uint64_t over_alloc =

include/linux/scatterlist.h

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,22 @@ static inline void sg_set_buf(struct scatterlist *sg, const void *buf,
165165
#define for_each_sgtable_dma_sg(sgt, sg, i) \
166166
for_each_sg((sgt)->sgl, sg, (sgt)->nents, i)
167167

168+
static inline void __sg_chain(struct scatterlist *chain_sg,
169+
struct scatterlist *sgl)
170+
{
171+
/*
172+
* offset and length are unused for chain entry. Clear them.
173+
*/
174+
chain_sg->offset = 0;
175+
chain_sg->length = 0;
176+
177+
/*
178+
* Set lowest bit to indicate a link pointer, and make sure to clear
179+
* the termination bit if it happens to be set.
180+
*/
181+
chain_sg->page_link = ((unsigned long) sgl | SG_CHAIN) & ~SG_END;
182+
}
183+
168184
/**
169185
* sg_chain - Chain two sglists together
170186
* @prv: First scatterlist
@@ -178,18 +194,7 @@ static inline void sg_set_buf(struct scatterlist *sg, const void *buf,
178194
static inline void sg_chain(struct scatterlist *prv, unsigned int prv_nents,
179195
struct scatterlist *sgl)
180196
{
181-
/*
182-
* offset and length are unused for chain entry. Clear them.
183-
*/
184-
prv[prv_nents - 1].offset = 0;
185-
prv[prv_nents - 1].length = 0;
186-
187-
/*
188-
* Set lowest bit to indicate a link pointer, and make sure to clear
189-
* the termination bit if it happens to be set.
190-
*/
191-
prv[prv_nents - 1].page_link = ((unsigned long) sgl | SG_CHAIN)
192-
& ~SG_END;
197+
__sg_chain(&prv[prv_nents - 1], sgl);
193198
}
194199

195200
/**
@@ -286,10 +291,11 @@ void sg_free_table(struct sg_table *);
286291
int __sg_alloc_table(struct sg_table *, unsigned int, unsigned int,
287292
struct scatterlist *, unsigned int, gfp_t, sg_alloc_fn *);
288293
int sg_alloc_table(struct sg_table *, unsigned int, gfp_t);
289-
int __sg_alloc_table_from_pages(struct sg_table *sgt, struct page **pages,
290-
unsigned int n_pages, unsigned int offset,
291-
unsigned long size, unsigned int max_segment,
292-
gfp_t gfp_mask);
294+
struct scatterlist *__sg_alloc_table_from_pages(struct sg_table *sgt,
295+
struct page **pages, unsigned int n_pages, unsigned int offset,
296+
unsigned long size, unsigned int max_segment,
297+
struct scatterlist *prv, unsigned int left_pages,
298+
gfp_t gfp_mask);
293299
int sg_alloc_table_from_pages(struct sg_table *sgt, struct page **pages,
294300
unsigned int n_pages, unsigned int offset,
295301
unsigned long size, gfp_t gfp_mask);

lib/scatterlist.c

Lines changed: 101 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,37 @@ int sg_alloc_table(struct sg_table *table, unsigned int nents, gfp_t gfp_mask)
365365
}
366366
EXPORT_SYMBOL(sg_alloc_table);
367367

368+
static struct scatterlist *get_next_sg(struct sg_table *table,
369+
struct scatterlist *cur,
370+
unsigned long needed_sges,
371+
gfp_t gfp_mask)
372+
{
373+
struct scatterlist *new_sg, *next_sg;
374+
unsigned int alloc_size;
375+
376+
if (cur) {
377+
next_sg = sg_next(cur);
378+
/* Check if last entry should be keeped for chainning */
379+
if (!sg_is_last(next_sg) || needed_sges == 1)
380+
return next_sg;
381+
}
382+
383+
alloc_size = min_t(unsigned long, needed_sges, SG_MAX_SINGLE_ALLOC);
384+
new_sg = sg_kmalloc(alloc_size, gfp_mask);
385+
if (!new_sg)
386+
return ERR_PTR(-ENOMEM);
387+
sg_init_table(new_sg, alloc_size);
388+
if (cur) {
389+
__sg_chain(next_sg, new_sg);
390+
table->orig_nents += alloc_size - 1;
391+
} else {
392+
table->sgl = new_sg;
393+
table->orig_nents = alloc_size;
394+
table->nents = 0;
395+
}
396+
return new_sg;
397+
}
398+
368399
/**
369400
* __sg_alloc_table_from_pages - Allocate and initialize an sg table from
370401
* an array of pages
@@ -374,29 +405,63 @@ EXPORT_SYMBOL(sg_alloc_table);
374405
* @offset: Offset from start of the first page to the start of a buffer
375406
* @size: Number of valid bytes in the buffer (after offset)
376407
* @max_segment: Maximum size of a scatterlist node in bytes (page aligned)
408+
* @prv: Last populated sge in sgt
409+
* @left_pages: Left pages caller have to set after this call
377410
* @gfp_mask: GFP allocation mask
378411
*
379-
* Description:
380-
* Allocate and initialize an sg table from a list of pages. Contiguous
381-
* ranges of the pages are squashed into a single scatterlist node up to the
382-
* maximum size specified in @max_segment. An user may provide an offset at a
383-
* start and a size of valid data in a buffer specified by the page array.
384-
* The returned sg table is released by sg_free_table.
412+
* Description:
413+
* If @prv is NULL, allocate and initialize an sg table from a list of pages,
414+
* else reuse the scatterlist passed in at @prv.
415+
* Contiguous ranges of the pages are squashed into a single scatterlist
416+
* entry up to the maximum size specified in @max_segment. A user may
417+
* provide an offset at a start and a size of valid data in a buffer
418+
* specified by the page array.
385419
*
386420
* Returns:
387-
* 0 on success, negative error on failure
421+
* Last SGE in sgt on success, PTR_ERR on otherwise.
422+
* The allocation in @sgt must be released by sg_free_table.
423+
*
424+
* Notes:
425+
* If this function returns non-0 (eg failure), the caller must call
426+
* sg_free_table() to cleanup any leftover allocations.
388427
*/
389-
int __sg_alloc_table_from_pages(struct sg_table *sgt, struct page **pages,
390-
unsigned int n_pages, unsigned int offset,
391-
unsigned long size, unsigned int max_segment,
392-
gfp_t gfp_mask)
428+
struct scatterlist *__sg_alloc_table_from_pages(struct sg_table *sgt,
429+
struct page **pages, unsigned int n_pages, unsigned int offset,
430+
unsigned long size, unsigned int max_segment,
431+
struct scatterlist *prv, unsigned int left_pages,
432+
gfp_t gfp_mask)
393433
{
394-
unsigned int chunks, cur_page, seg_len, i;
395-
int ret;
396-
struct scatterlist *s;
434+
unsigned int chunks, cur_page, seg_len, i, prv_len = 0;
435+
unsigned int added_nents = 0;
436+
struct scatterlist *s = prv;
397437

398438
if (WARN_ON(!max_segment || offset_in_page(max_segment)))
399-
return -EINVAL;
439+
return ERR_PTR(-EINVAL);
440+
441+
if (IS_ENABLED(CONFIG_ARCH_NO_SG_CHAIN) && prv)
442+
return ERR_PTR(-EOPNOTSUPP);
443+
444+
if (prv) {
445+
unsigned long paddr = (page_to_pfn(sg_page(prv)) * PAGE_SIZE +
446+
prv->offset + prv->length) /
447+
PAGE_SIZE;
448+
449+
if (WARN_ON(offset))
450+
return ERR_PTR(-EINVAL);
451+
452+
/* Merge contiguous pages into the last SG */
453+
prv_len = prv->length;
454+
while (n_pages && page_to_pfn(pages[0]) == paddr) {
455+
if (prv->length + PAGE_SIZE > max_segment)
456+
break;
457+
prv->length += PAGE_SIZE;
458+
paddr++;
459+
pages++;
460+
n_pages--;
461+
}
462+
if (!n_pages)
463+
goto out;
464+
}
400465

401466
/* compute number of contiguous chunks */
402467
chunks = 1;
@@ -410,13 +475,9 @@ int __sg_alloc_table_from_pages(struct sg_table *sgt, struct page **pages,
410475
}
411476
}
412477

413-
ret = sg_alloc_table(sgt, chunks, gfp_mask);
414-
if (unlikely(ret))
415-
return ret;
416-
417478
/* merging chunks and putting them into the scatterlist */
418479
cur_page = 0;
419-
for_each_sg(sgt->sgl, s, sgt->orig_nents, i) {
480+
for (i = 0; i < chunks; i++) {
420481
unsigned int j, chunk_size;
421482

422483
/* look for the end of the current chunk */
@@ -429,15 +490,30 @@ int __sg_alloc_table_from_pages(struct sg_table *sgt, struct page **pages,
429490
break;
430491
}
431492

493+
/* Pass how many chunks might be left */
494+
s = get_next_sg(sgt, s, chunks - i + left_pages, gfp_mask);
495+
if (IS_ERR(s)) {
496+
/*
497+
* Adjust entry length to be as before function was
498+
* called.
499+
*/
500+
if (prv)
501+
prv->length = prv_len;
502+
return s;
503+
}
432504
chunk_size = ((j - cur_page) << PAGE_SHIFT) - offset;
433505
sg_set_page(s, pages[cur_page],
434506
min_t(unsigned long, size, chunk_size), offset);
507+
added_nents++;
435508
size -= chunk_size;
436509
offset = 0;
437510
cur_page = j;
438511
}
439-
440-
return 0;
512+
sgt->nents += added_nents;
513+
out:
514+
if (!left_pages)
515+
sg_mark_end(s);
516+
return s;
441517
}
442518
EXPORT_SYMBOL(__sg_alloc_table_from_pages);
443519

@@ -465,8 +541,9 @@ int sg_alloc_table_from_pages(struct sg_table *sgt, struct page **pages,
465541
unsigned int n_pages, unsigned int offset,
466542
unsigned long size, gfp_t gfp_mask)
467543
{
468-
return __sg_alloc_table_from_pages(sgt, pages, n_pages, offset, size,
469-
SCATTERLIST_MAX_SEGMENT, gfp_mask);
544+
return PTR_ERR_OR_ZERO(__sg_alloc_table_from_pages(sgt, pages, n_pages,
545+
offset, size, SCATTERLIST_MAX_SEGMENT,
546+
NULL, 0, gfp_mask));
470547
}
471548
EXPORT_SYMBOL(sg_alloc_table_from_pages);
472549

tools/testing/scatterlist/main.c

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -79,14 +79,13 @@ int main(void)
7979
for (i = 0, test = tests; test->expected_segments; test++, i++) {
8080
struct page *pages[MAX_PAGES];
8181
struct sg_table st;
82-
int ret;
82+
struct scatterlist *sg;
8383

8484
set_pages(pages, test->pfn, test->num_pages);
8585

86-
ret = __sg_alloc_table_from_pages(&st, pages, test->num_pages,
87-
0, test->size, test->max_seg,
88-
GFP_KERNEL);
89-
assert(ret == test->alloc_ret);
86+
sg = __sg_alloc_table_from_pages(&st, pages, test->num_pages, 0,
87+
test->size, test->max_seg, NULL, 0, GFP_KERNEL);
88+
assert(PTR_ERR_OR_ZERO(sg) == test->alloc_ret);
9089

9190
if (test->alloc_ret)
9291
continue;

0 commit comments

Comments
 (0)