1#include <stdio.h>
2#include <assert.h>
3
4#include <linux/scatterlist.h>
5
6#define MAX_PAGES (64)
7
8struct test {
9	int alloc_ret;
10	unsigned num_pages;
11	unsigned *pfn;
12	unsigned size;
13	unsigned int max_seg;
14	unsigned int expected_segments;
15};
16
17static void set_pages(struct page **pages, const unsigned *array, unsigned num)
18{
19	unsigned int i;
20
21	assert(num < MAX_PAGES);
22	for (i = 0; i < num; i++)
23		pages[i] = (struct page *)(unsigned long)
24			   ((1 + array[i]) * PAGE_SIZE);
25}
26
27#define pfn(...) (unsigned []){ __VA_ARGS__ }
28
29static void fail(struct test *test, struct sg_table *st, const char *cond)
30{
31	unsigned int i;
32
33	fprintf(stderr, "Failed on '%s'!\n\n", cond);
34
35	printf("size = %u, max segment = %u, expected nents = %u\nst->nents = %u, st->orig_nents= %u\n",
36	       test->size, test->max_seg, test->expected_segments, st->nents,
37	       st->orig_nents);
38
39	printf("%u input PFNs:", test->num_pages);
40	for (i = 0; i < test->num_pages; i++)
41		printf(" %x", test->pfn[i]);
42	printf("\n");
43
44	exit(1);
45}
46
47#define VALIDATE(cond, st, test) \
48	if (!(cond)) \
49		fail((test), (st), #cond);
50
51int main(void)
52{
53	const unsigned int sgmax = SCATTERLIST_MAX_SEGMENT;
54	struct test *test, tests[] = {
55		{ -EINVAL, 1, pfn(0), PAGE_SIZE, 0, 1 },
56		{ 0, 1, pfn(0), PAGE_SIZE, PAGE_SIZE + 1, 1 },
57		{ 0, 1, pfn(0), PAGE_SIZE, sgmax + 1, 1 },
58		{ 0, 1, pfn(0), PAGE_SIZE, sgmax, 1 },
59		{ 0, 1, pfn(0), 1, sgmax, 1 },
60		{ 0, 2, pfn(0, 1), 2 * PAGE_SIZE, sgmax, 1 },
61		{ 0, 2, pfn(1, 0), 2 * PAGE_SIZE, sgmax, 2 },
62		{ 0, 3, pfn(0, 1, 2), 3 * PAGE_SIZE, sgmax, 1 },
63		{ 0, 3, pfn(0, 2, 1), 3 * PAGE_SIZE, sgmax, 3 },
64		{ 0, 3, pfn(0, 1, 3), 3 * PAGE_SIZE, sgmax, 2 },
65		{ 0, 3, pfn(1, 2, 4), 3 * PAGE_SIZE, sgmax, 2 },
66		{ 0, 3, pfn(1, 3, 4), 3 * PAGE_SIZE, sgmax, 2 },
67		{ 0, 4, pfn(0, 1, 3, 4), 4 * PAGE_SIZE, sgmax, 2 },
68		{ 0, 5, pfn(0, 1, 3, 4, 5), 5 * PAGE_SIZE, sgmax, 2 },
69		{ 0, 5, pfn(0, 1, 3, 4, 6), 5 * PAGE_SIZE, sgmax, 3 },
70		{ 0, 5, pfn(0, 1, 2, 3, 4), 5 * PAGE_SIZE, sgmax, 1 },
71		{ 0, 5, pfn(0, 1, 2, 3, 4), 5 * PAGE_SIZE, 2 * PAGE_SIZE, 3 },
72		{ 0, 6, pfn(0, 1, 2, 3, 4, 5), 6 * PAGE_SIZE, 2 * PAGE_SIZE, 3 },
73		{ 0, 6, pfn(0, 2, 3, 4, 5, 6), 6 * PAGE_SIZE, 2 * PAGE_SIZE, 4 },
74		{ 0, 6, pfn(0, 1, 3, 4, 5, 6), 6 * PAGE_SIZE, 2 * PAGE_SIZE, 3 },
75		{ 0, 0, NULL, 0, 0, 0 },
76	};
77	unsigned int i;
78
79	for (i = 0, test = tests; test->expected_segments; test++, i++) {
80		struct page *pages[MAX_PAGES];
81		struct sg_table st;
82		struct scatterlist *sg;
83
84		set_pages(pages, test->pfn, test->num_pages);
85
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);
89
90		if (test->alloc_ret)
91			continue;
92
93		VALIDATE(st.nents == test->expected_segments, &st, test);
94		VALIDATE(st.orig_nents == test->expected_segments, &st, test);
95
96		sg_free_table(&st);
97	}
98
99	assert(i == (sizeof(tests) / sizeof(tests[0])) - 1);
100
101	return 0;
102}
103