07da1223ec
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>
103 lines
2.9 KiB
C
103 lines
2.9 KiB
C
#include <stdio.h>
|
|
#include <assert.h>
|
|
|
|
#include <linux/scatterlist.h>
|
|
|
|
#define MAX_PAGES (64)
|
|
|
|
struct test {
|
|
int alloc_ret;
|
|
unsigned num_pages;
|
|
unsigned *pfn;
|
|
unsigned size;
|
|
unsigned int max_seg;
|
|
unsigned int expected_segments;
|
|
};
|
|
|
|
static void set_pages(struct page **pages, const unsigned *array, unsigned num)
|
|
{
|
|
unsigned int i;
|
|
|
|
assert(num < MAX_PAGES);
|
|
for (i = 0; i < num; i++)
|
|
pages[i] = (struct page *)(unsigned long)
|
|
((1 + array[i]) * PAGE_SIZE);
|
|
}
|
|
|
|
#define pfn(...) (unsigned []){ __VA_ARGS__ }
|
|
|
|
static void fail(struct test *test, struct sg_table *st, const char *cond)
|
|
{
|
|
unsigned int i;
|
|
|
|
fprintf(stderr, "Failed on '%s'!\n\n", cond);
|
|
|
|
printf("size = %u, max segment = %u, expected nents = %u\nst->nents = %u, st->orig_nents= %u\n",
|
|
test->size, test->max_seg, test->expected_segments, st->nents,
|
|
st->orig_nents);
|
|
|
|
printf("%u input PFNs:", test->num_pages);
|
|
for (i = 0; i < test->num_pages; i++)
|
|
printf(" %x", test->pfn[i]);
|
|
printf("\n");
|
|
|
|
exit(1);
|
|
}
|
|
|
|
#define VALIDATE(cond, st, test) \
|
|
if (!(cond)) \
|
|
fail((test), (st), #cond);
|
|
|
|
int main(void)
|
|
{
|
|
const unsigned int sgmax = SCATTERLIST_MAX_SEGMENT;
|
|
struct test *test, tests[] = {
|
|
{ -EINVAL, 1, pfn(0), PAGE_SIZE, PAGE_SIZE + 1, 1 },
|
|
{ -EINVAL, 1, pfn(0), PAGE_SIZE, 0, 1 },
|
|
{ -EINVAL, 1, pfn(0), PAGE_SIZE, sgmax + 1, 1 },
|
|
{ 0, 1, pfn(0), PAGE_SIZE, sgmax, 1 },
|
|
{ 0, 1, pfn(0), 1, sgmax, 1 },
|
|
{ 0, 2, pfn(0, 1), 2 * PAGE_SIZE, sgmax, 1 },
|
|
{ 0, 2, pfn(1, 0), 2 * PAGE_SIZE, sgmax, 2 },
|
|
{ 0, 3, pfn(0, 1, 2), 3 * PAGE_SIZE, sgmax, 1 },
|
|
{ 0, 3, pfn(0, 2, 1), 3 * PAGE_SIZE, sgmax, 3 },
|
|
{ 0, 3, pfn(0, 1, 3), 3 * PAGE_SIZE, sgmax, 2 },
|
|
{ 0, 3, pfn(1, 2, 4), 3 * PAGE_SIZE, sgmax, 2 },
|
|
{ 0, 3, pfn(1, 3, 4), 3 * PAGE_SIZE, sgmax, 2 },
|
|
{ 0, 4, pfn(0, 1, 3, 4), 4 * PAGE_SIZE, sgmax, 2 },
|
|
{ 0, 5, pfn(0, 1, 3, 4, 5), 5 * PAGE_SIZE, sgmax, 2 },
|
|
{ 0, 5, pfn(0, 1, 3, 4, 6), 5 * PAGE_SIZE, sgmax, 3 },
|
|
{ 0, 5, pfn(0, 1, 2, 3, 4), 5 * PAGE_SIZE, sgmax, 1 },
|
|
{ 0, 5, pfn(0, 1, 2, 3, 4), 5 * PAGE_SIZE, 2 * PAGE_SIZE, 3 },
|
|
{ 0, 6, pfn(0, 1, 2, 3, 4, 5), 6 * PAGE_SIZE, 2 * PAGE_SIZE, 3 },
|
|
{ 0, 6, pfn(0, 2, 3, 4, 5, 6), 6 * PAGE_SIZE, 2 * PAGE_SIZE, 4 },
|
|
{ 0, 6, pfn(0, 1, 3, 4, 5, 6), 6 * PAGE_SIZE, 2 * PAGE_SIZE, 3 },
|
|
{ 0, 0, NULL, 0, 0, 0 },
|
|
};
|
|
unsigned int i;
|
|
|
|
for (i = 0, test = tests; test->expected_segments; test++, i++) {
|
|
struct page *pages[MAX_PAGES];
|
|
struct sg_table st;
|
|
struct scatterlist *sg;
|
|
|
|
set_pages(pages, test->pfn, test->num_pages);
|
|
|
|
sg = __sg_alloc_table_from_pages(&st, pages, test->num_pages, 0,
|
|
test->size, test->max_seg, NULL, 0, GFP_KERNEL);
|
|
assert(PTR_ERR_OR_ZERO(sg) == test->alloc_ret);
|
|
|
|
if (test->alloc_ret)
|
|
continue;
|
|
|
|
VALIDATE(st.nents == test->expected_segments, &st, test);
|
|
VALIDATE(st.orig_nents == test->expected_segments, &st, test);
|
|
|
|
sg_free_table(&st);
|
|
}
|
|
|
|
assert(i == (sizeof(tests) / sizeof(tests[0])) - 1);
|
|
|
|
return 0;
|
|
}
|