162306a36Sopenharmony_ci#include <stdio.h>
262306a36Sopenharmony_ci#include <assert.h>
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include <linux/scatterlist.h>
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#define MAX_PAGES (64)
762306a36Sopenharmony_ci
862306a36Sopenharmony_cistruct test {
962306a36Sopenharmony_ci	int alloc_ret;
1062306a36Sopenharmony_ci	unsigned num_pages;
1162306a36Sopenharmony_ci	unsigned *pfn;
1262306a36Sopenharmony_ci	unsigned *pfn_app;
1362306a36Sopenharmony_ci	unsigned size;
1462306a36Sopenharmony_ci	unsigned int max_seg;
1562306a36Sopenharmony_ci	unsigned int expected_segments;
1662306a36Sopenharmony_ci};
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_cistatic void set_pages(struct page **pages, const unsigned *array, unsigned num)
1962306a36Sopenharmony_ci{
2062306a36Sopenharmony_ci	unsigned int i;
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci	assert(num < MAX_PAGES);
2362306a36Sopenharmony_ci	for (i = 0; i < num; i++)
2462306a36Sopenharmony_ci		pages[i] = (struct page *)(unsigned long)
2562306a36Sopenharmony_ci			   ((1 + array[i]) * PAGE_SIZE);
2662306a36Sopenharmony_ci}
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#define pfn(...) (unsigned []){ __VA_ARGS__ }
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistatic void fail(struct test *test, struct sg_table *st, const char *cond)
3162306a36Sopenharmony_ci{
3262306a36Sopenharmony_ci	unsigned int i;
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	fprintf(stderr, "Failed on '%s'!\n\n", cond);
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	printf("size = %u, max segment = %u, expected nents = %u\nst->nents = %u, st->orig_nents= %u\n",
3762306a36Sopenharmony_ci	       test->size, test->max_seg, test->expected_segments, st->nents,
3862306a36Sopenharmony_ci	       st->orig_nents);
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	printf("%u input PFNs:", test->num_pages);
4162306a36Sopenharmony_ci	for (i = 0; i < test->num_pages; i++)
4262306a36Sopenharmony_ci		printf(" %x", test->pfn[i]);
4362306a36Sopenharmony_ci	printf("\n");
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	exit(1);
4662306a36Sopenharmony_ci}
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci#define VALIDATE(cond, st, test) \
4962306a36Sopenharmony_ci	if (!(cond)) \
5062306a36Sopenharmony_ci		fail((test), (st), #cond);
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ciint main(void)
5362306a36Sopenharmony_ci{
5462306a36Sopenharmony_ci	const unsigned int sgmax = UINT_MAX;
5562306a36Sopenharmony_ci	struct test *test, tests[] = {
5662306a36Sopenharmony_ci		{ -EINVAL, 1, pfn(0), NULL, PAGE_SIZE, 0, 1 },
5762306a36Sopenharmony_ci		{ 0, 1, pfn(0), NULL, PAGE_SIZE, PAGE_SIZE + 1, 1 },
5862306a36Sopenharmony_ci		{ 0, 1, pfn(0), NULL, PAGE_SIZE, sgmax, 1 },
5962306a36Sopenharmony_ci		{ 0, 1, pfn(0), NULL, 1, sgmax, 1 },
6062306a36Sopenharmony_ci		{ 0, 2, pfn(0, 1), NULL, 2 * PAGE_SIZE, sgmax, 1 },
6162306a36Sopenharmony_ci		{ 0, 2, pfn(1, 0), NULL, 2 * PAGE_SIZE, sgmax, 2 },
6262306a36Sopenharmony_ci		{ 0, 3, pfn(0, 1, 2), NULL, 3 * PAGE_SIZE, sgmax, 1 },
6362306a36Sopenharmony_ci		{ 0, 3, pfn(0, 1, 2), NULL, 3 * PAGE_SIZE, sgmax, 1 },
6462306a36Sopenharmony_ci		{ 0, 3, pfn(0, 1, 2), pfn(3, 4, 5), 3 * PAGE_SIZE, sgmax, 1 },
6562306a36Sopenharmony_ci		{ 0, 3, pfn(0, 1, 2), pfn(4, 5, 6), 3 * PAGE_SIZE, sgmax, 2 },
6662306a36Sopenharmony_ci		{ 0, 3, pfn(0, 2, 1), NULL, 3 * PAGE_SIZE, sgmax, 3 },
6762306a36Sopenharmony_ci		{ 0, 3, pfn(0, 1, 3), NULL, 3 * PAGE_SIZE, sgmax, 2 },
6862306a36Sopenharmony_ci		{ 0, 3, pfn(1, 2, 4), NULL, 3 * PAGE_SIZE, sgmax, 2 },
6962306a36Sopenharmony_ci		{ 0, 3, pfn(1, 3, 4), NULL, 3 * PAGE_SIZE, sgmax, 2 },
7062306a36Sopenharmony_ci		{ 0, 4, pfn(0, 1, 3, 4), NULL, 4 * PAGE_SIZE, sgmax, 2 },
7162306a36Sopenharmony_ci		{ 0, 5, pfn(0, 1, 3, 4, 5), NULL, 5 * PAGE_SIZE, sgmax, 2 },
7262306a36Sopenharmony_ci		{ 0, 5, pfn(0, 1, 3, 4, 6), NULL, 5 * PAGE_SIZE, sgmax, 3 },
7362306a36Sopenharmony_ci		{ 0, 5, pfn(0, 1, 2, 3, 4), NULL, 5 * PAGE_SIZE, sgmax, 1 },
7462306a36Sopenharmony_ci		{ 0, 5, pfn(0, 1, 2, 3, 4), NULL, 5 * PAGE_SIZE, 2 * PAGE_SIZE,
7562306a36Sopenharmony_ci		  3 },
7662306a36Sopenharmony_ci		{ 0, 6, pfn(0, 1, 2, 3, 4, 5), NULL, 6 * PAGE_SIZE,
7762306a36Sopenharmony_ci		  2 * PAGE_SIZE, 3 },
7862306a36Sopenharmony_ci		{ 0, 6, pfn(0, 2, 3, 4, 5, 6), NULL, 6 * PAGE_SIZE,
7962306a36Sopenharmony_ci		  2 * PAGE_SIZE, 4 },
8062306a36Sopenharmony_ci		{ 0, 6, pfn(0, 1, 3, 4, 5, 6), pfn(7, 8, 9, 10, 11, 12),
8162306a36Sopenharmony_ci		  6 * PAGE_SIZE, 12 * PAGE_SIZE, 2 },
8262306a36Sopenharmony_ci		{ 0, 0, NULL, NULL, 0, 0, 0 },
8362306a36Sopenharmony_ci	};
8462306a36Sopenharmony_ci	unsigned int i;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	for (i = 0, test = tests; test->expected_segments; test++, i++) {
8762306a36Sopenharmony_ci		int left_pages = test->pfn_app ? test->num_pages : 0;
8862306a36Sopenharmony_ci		struct sg_append_table append = {};
8962306a36Sopenharmony_ci		struct page *pages[MAX_PAGES];
9062306a36Sopenharmony_ci		int ret;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci		set_pages(pages, test->pfn, test->num_pages);
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci		if (test->pfn_app)
9562306a36Sopenharmony_ci			ret = sg_alloc_append_table_from_pages(
9662306a36Sopenharmony_ci				&append, pages, test->num_pages, 0, test->size,
9762306a36Sopenharmony_ci				test->max_seg, left_pages, GFP_KERNEL);
9862306a36Sopenharmony_ci		else
9962306a36Sopenharmony_ci			ret = sg_alloc_table_from_pages_segment(
10062306a36Sopenharmony_ci				&append.sgt, pages, test->num_pages, 0,
10162306a36Sopenharmony_ci				test->size, test->max_seg, GFP_KERNEL);
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci		assert(ret == test->alloc_ret);
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci		if (test->alloc_ret)
10662306a36Sopenharmony_ci			continue;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci		if (test->pfn_app) {
10962306a36Sopenharmony_ci			set_pages(pages, test->pfn_app, test->num_pages);
11062306a36Sopenharmony_ci			ret = sg_alloc_append_table_from_pages(
11162306a36Sopenharmony_ci				&append, pages, test->num_pages, 0, test->size,
11262306a36Sopenharmony_ci				test->max_seg, 0, GFP_KERNEL);
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci			assert(ret == test->alloc_ret);
11562306a36Sopenharmony_ci		}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci		VALIDATE(append.sgt.nents == test->expected_segments,
11862306a36Sopenharmony_ci			 &append.sgt, test);
11962306a36Sopenharmony_ci		if (!test->pfn_app)
12062306a36Sopenharmony_ci			VALIDATE(append.sgt.orig_nents ==
12162306a36Sopenharmony_ci					 test->expected_segments,
12262306a36Sopenharmony_ci				 &append.sgt, test);
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci		if (test->pfn_app)
12562306a36Sopenharmony_ci			sg_free_append_table(&append);
12662306a36Sopenharmony_ci		else
12762306a36Sopenharmony_ci			sg_free_table(&append.sgt);
12862306a36Sopenharmony_ci	}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	assert(i == (sizeof(tests) / sizeof(tests[0])) - 1);
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	return 0;
13362306a36Sopenharmony_ci}
134