xref: /linux/lib/raid/raid6/tests/raid6_kunit.c (revision 8cf0a6c4bb9e09a2d280376dba723b218c139e58)
13626738bSChristoph Hellwig // SPDX-License-Identifier: GPL-2.0-or-later
23626738bSChristoph Hellwig /*
33626738bSChristoph Hellwig  * Copyright 2002-2007 H. Peter Anvin - All Rights Reserved
43626738bSChristoph Hellwig  *
53626738bSChristoph Hellwig  * Test RAID-6 recovery algorithms.
63626738bSChristoph Hellwig  */
73626738bSChristoph Hellwig 
83626738bSChristoph Hellwig #include <kunit/test.h>
93626738bSChristoph Hellwig #include <linux/prandom.h>
10d67c2571SChristoph Hellwig #include <linux/vmalloc.h>
11fa0c812cSChristoph Hellwig #include <linux/raid/pq.h>
12769d603fSChristoph Hellwig #include "../algos.h"
133626738bSChristoph Hellwig 
143626738bSChristoph Hellwig MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
153626738bSChristoph Hellwig 
163626738bSChristoph Hellwig #define RAID6_KUNIT_SEED		42
17fa0c812cSChristoph Hellwig #define RAID6_KUNIT_NUM_TEST_ITERS	10
18fa0c812cSChristoph Hellwig #define RAID6_KUNIT_MAX_BUFFERS		64 /* Including P and Q */
19d67c2571SChristoph Hellwig #define RAID6_KUNIT_MAX_FAILURES	2
20fa0c812cSChristoph Hellwig #define RAID6_KUNIT_MAX_BYTES		PAGE_SIZE
213626738bSChristoph Hellwig 
223626738bSChristoph Hellwig static struct rnd_state rng;
23fa0c812cSChristoph Hellwig static void *test_buffers[RAID6_KUNIT_MAX_BUFFERS];
24*8cf0a6c4SChristoph Hellwig static void *aligned_buffers[RAID6_KUNIT_MAX_BUFFERS];
25d67c2571SChristoph Hellwig static void *test_recov_buffers[RAID6_KUNIT_MAX_FAILURES];
26fa0c812cSChristoph Hellwig static size_t test_buflen;
273626738bSChristoph Hellwig 
282175395fSChristoph Hellwig struct test_args {
292175395fSChristoph Hellwig 	unsigned int recov_idx;
302175395fSChristoph Hellwig 	const struct raid6_recov_calls *recov;
312175395fSChristoph Hellwig 	unsigned int gen_idx;
322175395fSChristoph Hellwig 	const struct raid6_calls *gen;
332175395fSChristoph Hellwig };
342175395fSChristoph Hellwig 
352175395fSChristoph Hellwig static struct test_args args;
362175395fSChristoph Hellwig 
37fa0c812cSChristoph Hellwig static u32 rand32(void)
38fa0c812cSChristoph Hellwig {
39fa0c812cSChristoph Hellwig 	return prandom_u32_state(&rng);
40fa0c812cSChristoph Hellwig }
41fa0c812cSChristoph Hellwig 
42fa0c812cSChristoph Hellwig /* Generate a random length that is a multiple of 512. */
43fa0c812cSChristoph Hellwig static unsigned int random_length(unsigned int max_length)
44fa0c812cSChristoph Hellwig {
45fa0c812cSChristoph Hellwig 	return round_up((rand32() % max_length) + 1, 512);
46fa0c812cSChristoph Hellwig }
47fa0c812cSChristoph Hellwig 
48fa0c812cSChristoph Hellwig static unsigned int random_nr_buffers(void)
49fa0c812cSChristoph Hellwig {
50fa0c812cSChristoph Hellwig 	return (rand32() % (RAID6_KUNIT_MAX_BUFFERS - (RAID6_MIN_DISKS - 1))) +
51fa0c812cSChristoph Hellwig 			RAID6_MIN_DISKS;
52fa0c812cSChristoph Hellwig }
53fa0c812cSChristoph Hellwig 
54*8cf0a6c4SChristoph Hellwig /* Generate a random alignment that is a multiple of 64. */
55*8cf0a6c4SChristoph Hellwig static unsigned int random_alignment(unsigned int max_alignment)
56*8cf0a6c4SChristoph Hellwig {
57*8cf0a6c4SChristoph Hellwig 	if (max_alignment == 0)
58*8cf0a6c4SChristoph Hellwig 		return 0;
59*8cf0a6c4SChristoph Hellwig 	return (rand32() % (max_alignment + 1)) & ~63;
60*8cf0a6c4SChristoph Hellwig }
61*8cf0a6c4SChristoph Hellwig 
623626738bSChristoph Hellwig static void makedata(int start, int stop)
633626738bSChristoph Hellwig {
643626738bSChristoph Hellwig 	int i;
653626738bSChristoph Hellwig 
66562bcbfcSChristoph Hellwig 	for (i = start; i <= stop; i++)
67fa0c812cSChristoph Hellwig 		prandom_bytes_state(&rng, test_buffers[i], test_buflen);
683626738bSChristoph Hellwig }
693626738bSChristoph Hellwig 
70fa0c812cSChristoph Hellwig static char member_type(unsigned int nr_buffers, int d)
713626738bSChristoph Hellwig {
72fa0c812cSChristoph Hellwig 	if (d == nr_buffers - 2)
733626738bSChristoph Hellwig 		return 'P';
74fa0c812cSChristoph Hellwig 	if (d == nr_buffers - 1)
753626738bSChristoph Hellwig 		return 'Q';
763626738bSChristoph Hellwig 	return 'D';
773626738bSChristoph Hellwig }
783626738bSChristoph Hellwig 
79fa0c812cSChristoph Hellwig static void test_recover_one(struct kunit *test, unsigned int nr_buffers,
80fa0c812cSChristoph Hellwig 		unsigned int len, int faila, int failb)
813626738bSChristoph Hellwig {
822175395fSChristoph Hellwig 	const struct test_args *ta = test->param_value;
83fa0c812cSChristoph Hellwig 	void *dataptrs[RAID6_KUNIT_MAX_BUFFERS];
84d67c2571SChristoph Hellwig 	int i;
852175395fSChristoph Hellwig 
86fa0c812cSChristoph Hellwig 	if (faila > failb)
87fa0c812cSChristoph Hellwig 		swap(faila, failb);
88fa0c812cSChristoph Hellwig 
89d67c2571SChristoph Hellwig 	for (i = 0; i < RAID6_KUNIT_MAX_FAILURES; i++)
90fa0c812cSChristoph Hellwig 		memset(test_recov_buffers[i], 0xf0, test_buflen);
913626738bSChristoph Hellwig 
92*8cf0a6c4SChristoph Hellwig 	memcpy(dataptrs, aligned_buffers, sizeof(dataptrs));
93d67c2571SChristoph Hellwig 	dataptrs[faila] = test_recov_buffers[0];
94d67c2571SChristoph Hellwig 	dataptrs[failb] = test_recov_buffers[1];
953626738bSChristoph Hellwig 
96fa0c812cSChristoph Hellwig 	if (failb == nr_buffers - 1) {
973626738bSChristoph Hellwig 		/*
983626738bSChristoph Hellwig 		 * We don't implement the data+Q failure scenario, since it
993626738bSChristoph Hellwig 		 * is equivalent to a RAID-5 failure (XOR, then recompute Q).
1003626738bSChristoph Hellwig 		 */
101fa0c812cSChristoph Hellwig 		if (WARN_ON_ONCE(faila != nr_buffers - 2))
102562bcbfcSChristoph Hellwig 			return;
1033626738bSChristoph Hellwig 
1043626738bSChristoph Hellwig 		/* P+Q failure.  Just rebuild the syndrome. */
105fa0c812cSChristoph Hellwig 		ta->gen->gen_syndrome(nr_buffers, len, dataptrs);
106fa0c812cSChristoph Hellwig 	} else if (failb == nr_buffers - 2) {
1073626738bSChristoph Hellwig 		/* data+P failure. */
108fa0c812cSChristoph Hellwig 		ta->recov->datap(nr_buffers, len, faila, dataptrs);
1093626738bSChristoph Hellwig 	} else {
1103626738bSChristoph Hellwig 		/* data+data failure. */
111fa0c812cSChristoph Hellwig 		ta->recov->data2(nr_buffers, len, faila, failb, dataptrs);
1123626738bSChristoph Hellwig 	}
1133626738bSChristoph Hellwig 
114*8cf0a6c4SChristoph Hellwig 	KUNIT_EXPECT_MEMEQ_MSG(test, aligned_buffers[faila], dataptrs[faila],
115fa0c812cSChristoph Hellwig 			len,
116fa0c812cSChristoph Hellwig 			"faila miscompared: %3d[%c] buffers %u len %u (failb=%3d[%c])\n",
117fa0c812cSChristoph Hellwig 			faila, member_type(nr_buffers, faila),
118fa0c812cSChristoph Hellwig 			nr_buffers, len,
119fa0c812cSChristoph Hellwig 			failb, member_type(nr_buffers, failb));
120*8cf0a6c4SChristoph Hellwig 	KUNIT_EXPECT_MEMEQ_MSG(test, aligned_buffers[failb], dataptrs[failb],
121fa0c812cSChristoph Hellwig 			len,
122fa0c812cSChristoph Hellwig 			"failb miscompared: %3d[%c] buffers %u len %u (faila=%3d[%c])\n",
123fa0c812cSChristoph Hellwig 			failb, member_type(nr_buffers, failb),
124fa0c812cSChristoph Hellwig 			nr_buffers, len,
125fa0c812cSChristoph Hellwig 			faila, member_type(nr_buffers, faila));
126fa0c812cSChristoph Hellwig }
127fa0c812cSChristoph Hellwig 
128fa0c812cSChristoph Hellwig static void test_recover(struct kunit *test, unsigned int nr_buffers,
129fa0c812cSChristoph Hellwig 		unsigned int len)
130fa0c812cSChristoph Hellwig {
131fa0c812cSChristoph Hellwig 	unsigned int nr_data = nr_buffers - 2;
132fa0c812cSChristoph Hellwig 	int iterations, i;
133fa0c812cSChristoph Hellwig 
134fa0c812cSChristoph Hellwig 	/* Test P+Q recovery */
135fa0c812cSChristoph Hellwig 	test_recover_one(test, nr_buffers, len, nr_data, nr_buffers - 1);
136fa0c812cSChristoph Hellwig 
137fa0c812cSChristoph Hellwig 	/* Test data+P recovery */
138fa0c812cSChristoph Hellwig 	for (i = 0; i < nr_buffers - 2; i++)
139fa0c812cSChristoph Hellwig 		test_recover_one(test, nr_buffers, len, i, nr_data);
140fa0c812cSChristoph Hellwig 
141fa0c812cSChristoph Hellwig 	/* Double data failure is impossible with a single data disk */
142fa0c812cSChristoph Hellwig 	if (nr_data == 1)
143fa0c812cSChristoph Hellwig 		return;
144fa0c812cSChristoph Hellwig 
145fa0c812cSChristoph Hellwig 	/* Test data+data recovery using random sampling */
146fa0c812cSChristoph Hellwig 	iterations = nr_buffers * 2; /* should provide good enough coverage */
147fa0c812cSChristoph Hellwig 	for (i = 0; i < iterations; i++) {
148fa0c812cSChristoph Hellwig 		int faila = rand32() % nr_data, failb;
149fa0c812cSChristoph Hellwig 
150fa0c812cSChristoph Hellwig 		do {
151fa0c812cSChristoph Hellwig 			failb = rand32() % nr_data;
152fa0c812cSChristoph Hellwig 		} while (failb == faila);
153fa0c812cSChristoph Hellwig 
154fa0c812cSChristoph Hellwig 		test_recover_one(test, nr_buffers, len, faila, failb);
155fa0c812cSChristoph Hellwig 	}
156fa0c812cSChristoph Hellwig }
157fa0c812cSChristoph Hellwig 
158fa0c812cSChristoph Hellwig /* Simulate rmw run */
159fa0c812cSChristoph Hellwig static void test_rmw_one(struct kunit *test, unsigned int nr_buffers,
160fa0c812cSChristoph Hellwig 		unsigned int len, int p1, int p2)
161fa0c812cSChristoph Hellwig {
162fa0c812cSChristoph Hellwig 	const struct test_args *ta = test->param_value;
163fa0c812cSChristoph Hellwig 
164*8cf0a6c4SChristoph Hellwig 	ta->gen->xor_syndrome(nr_buffers, p1, p2, len, aligned_buffers);
165fa0c812cSChristoph Hellwig 	makedata(p1, p2);
166*8cf0a6c4SChristoph Hellwig 	ta->gen->xor_syndrome(nr_buffers, p1, p2, len, aligned_buffers);
167fa0c812cSChristoph Hellwig 	test_recover(test, nr_buffers, len);
168fa0c812cSChristoph Hellwig }
169fa0c812cSChristoph Hellwig 
170fa0c812cSChristoph Hellwig static void test_rmw(struct kunit *test, unsigned int nr_buffers,
171fa0c812cSChristoph Hellwig 		unsigned int len)
172fa0c812cSChristoph Hellwig {
173fa0c812cSChristoph Hellwig 	int iterations = nr_buffers / 2, i;
174fa0c812cSChristoph Hellwig 
175fa0c812cSChristoph Hellwig 	for (i = 0; i < iterations; i++) {
176fa0c812cSChristoph Hellwig 		int p1 = rand32() % (nr_buffers - 2);
177fa0c812cSChristoph Hellwig 		int p2 = rand32() % (nr_buffers - 2);
178fa0c812cSChristoph Hellwig 
179fa0c812cSChristoph Hellwig 		if (p2 < p1)
180fa0c812cSChristoph Hellwig 			swap(p1, p2);
181fa0c812cSChristoph Hellwig 		test_rmw_one(test, nr_buffers, len, p1, p2);
182fa0c812cSChristoph Hellwig 	}
183fa0c812cSChristoph Hellwig }
184fa0c812cSChristoph Hellwig 
185fa0c812cSChristoph Hellwig static void raid6_test_one(struct kunit *test)
186fa0c812cSChristoph Hellwig {
187fa0c812cSChristoph Hellwig 	const struct test_args *ta = test->param_value;
188fa0c812cSChristoph Hellwig 	unsigned int nr_buffers = random_nr_buffers();
189fa0c812cSChristoph Hellwig 	unsigned int len = random_length(RAID6_KUNIT_MAX_BYTES);
190*8cf0a6c4SChristoph Hellwig 	unsigned int max_alignment;
191*8cf0a6c4SChristoph Hellwig 	int i;
192fa0c812cSChristoph Hellwig 
193fa0c812cSChristoph Hellwig 	/* Nuke syndromes */
194fa0c812cSChristoph Hellwig 	memset(test_buffers[nr_buffers - 2], 0xee, test_buflen);
195fa0c812cSChristoph Hellwig 	memset(test_buffers[nr_buffers - 1], 0xee, test_buflen);
196fa0c812cSChristoph Hellwig 
197*8cf0a6c4SChristoph Hellwig 	/*
198*8cf0a6c4SChristoph Hellwig 	 * If we're not using the entire buffer size, inject randomize alignment
199*8cf0a6c4SChristoph Hellwig 	 * into the buffer.
200*8cf0a6c4SChristoph Hellwig 	 */
201*8cf0a6c4SChristoph Hellwig 	max_alignment = RAID6_KUNIT_MAX_BYTES - len;
202*8cf0a6c4SChristoph Hellwig 	if (rand32() % 2 == 0) {
203*8cf0a6c4SChristoph Hellwig 		/* Use random alignments mod 64 */
204*8cf0a6c4SChristoph Hellwig 		for (i = 0; i < nr_buffers; i++)
205*8cf0a6c4SChristoph Hellwig 			aligned_buffers[i] = test_buffers[i] +
206*8cf0a6c4SChristoph Hellwig 				random_alignment(max_alignment);
207*8cf0a6c4SChristoph Hellwig 	} else {
208*8cf0a6c4SChristoph Hellwig 		/* Go up to the guard page, to catch buffer overreads */
209*8cf0a6c4SChristoph Hellwig 		unsigned int align = test_buflen - len;
210*8cf0a6c4SChristoph Hellwig 
211*8cf0a6c4SChristoph Hellwig 		for (i = 0; i < nr_buffers; i++)
212*8cf0a6c4SChristoph Hellwig 			aligned_buffers[i] = test_buffers[i] + align;
213*8cf0a6c4SChristoph Hellwig 	}
214*8cf0a6c4SChristoph Hellwig 
215fa0c812cSChristoph Hellwig 	/* Generate assumed good syndrome */
216*8cf0a6c4SChristoph Hellwig 	ta->gen->gen_syndrome(nr_buffers, len, aligned_buffers);
217fa0c812cSChristoph Hellwig 
218fa0c812cSChristoph Hellwig 	test_recover(test, nr_buffers, len);
219fa0c812cSChristoph Hellwig 
220fa0c812cSChristoph Hellwig 	if (ta->gen->xor_syndrome)
221fa0c812cSChristoph Hellwig 		test_rmw(test, nr_buffers, len);
2223626738bSChristoph Hellwig }
2233626738bSChristoph Hellwig 
2243626738bSChristoph Hellwig static void raid6_test(struct kunit *test)
2253626738bSChristoph Hellwig {
226fa0c812cSChristoph Hellwig 	int i;
2273626738bSChristoph Hellwig 
228fa0c812cSChristoph Hellwig 	for (i = 0; i < RAID6_KUNIT_NUM_TEST_ITERS; i++)
229fa0c812cSChristoph Hellwig 		raid6_test_one(test);
2303626738bSChristoph Hellwig }
2313626738bSChristoph Hellwig 
2322175395fSChristoph Hellwig static const void *raid6_gen_params(struct kunit *test, const void *prev,
2332175395fSChristoph Hellwig 		char *desc)
2342175395fSChristoph Hellwig {
2352175395fSChristoph Hellwig 	if (!prev) {
2362175395fSChristoph Hellwig 		memset(&args, 0, sizeof(args));
2372175395fSChristoph Hellwig next_algo:
2382175395fSChristoph Hellwig 		args.recov_idx = 0;
2392175395fSChristoph Hellwig 		args.gen = raid6_algo_find(args.gen_idx);
2402175395fSChristoph Hellwig 		if (!args.gen)
2412175395fSChristoph Hellwig 			return NULL;
2423626738bSChristoph Hellwig 	}
2432175395fSChristoph Hellwig 
2442175395fSChristoph Hellwig 	if (args.recov)
2452175395fSChristoph Hellwig 		args.recov_idx++;
2462175395fSChristoph Hellwig 	args.recov = raid6_recov_algo_find(args.recov_idx);
2472175395fSChristoph Hellwig 	if (!args.recov) {
2482175395fSChristoph Hellwig 		args.gen_idx++;
2492175395fSChristoph Hellwig 		goto next_algo;
2503626738bSChristoph Hellwig 	}
2512175395fSChristoph Hellwig 
2522175395fSChristoph Hellwig 	snprintf(desc, KUNIT_PARAM_DESC_SIZE, "gen=%s recov=%s",
2532175395fSChristoph Hellwig 			args.gen->name, args.recov->name);
2542175395fSChristoph Hellwig 	return &args;
2553626738bSChristoph Hellwig }
2563626738bSChristoph Hellwig 
2573626738bSChristoph Hellwig static struct kunit_case raid6_test_cases[] = {
2582175395fSChristoph Hellwig 	KUNIT_CASE_PARAM(raid6_test, raid6_gen_params),
2593626738bSChristoph Hellwig 	{},
2603626738bSChristoph Hellwig };
2613626738bSChristoph Hellwig 
2623626738bSChristoph Hellwig static int raid6_suite_init(struct kunit_suite *suite)
2633626738bSChristoph Hellwig {
264d67c2571SChristoph Hellwig 	int i;
265d67c2571SChristoph Hellwig 
2663626738bSChristoph Hellwig 	prandom_seed_state(&rng, RAID6_KUNIT_SEED);
267d67c2571SChristoph Hellwig 
268d67c2571SChristoph Hellwig 	/*
269d67c2571SChristoph Hellwig 	 * Allocate the test buffer using vmalloc() with a page-aligned length
270d67c2571SChristoph Hellwig 	 * so that it is immediately followed by a guard page.  This allows
271d67c2571SChristoph Hellwig 	 * buffer overreads to be detected, even in assembly code.
272d67c2571SChristoph Hellwig 	 */
273fa0c812cSChristoph Hellwig 	test_buflen = round_up(RAID6_KUNIT_MAX_BYTES, PAGE_SIZE);
274d67c2571SChristoph Hellwig 	for (i = 0; i < RAID6_KUNIT_MAX_FAILURES; i++) {
275fa0c812cSChristoph Hellwig 		test_recov_buffers[i] = vmalloc(test_buflen);
276d67c2571SChristoph Hellwig 		if (!test_recov_buffers[i])
277d67c2571SChristoph Hellwig 			goto out_free_recov_buffers;
278d67c2571SChristoph Hellwig 	}
279fa0c812cSChristoph Hellwig 	for (i = 0; i < RAID6_KUNIT_MAX_BUFFERS; i++) {
280fa0c812cSChristoph Hellwig 		test_buffers[i] = vmalloc(test_buflen);
281d67c2571SChristoph Hellwig 		if (!test_buffers[i])
282d67c2571SChristoph Hellwig 			goto out_free_buffers;
283d67c2571SChristoph Hellwig 	}
284d67c2571SChristoph Hellwig 
285fa0c812cSChristoph Hellwig 	makedata(0, RAID6_KUNIT_MAX_BUFFERS - 1);
286d67c2571SChristoph Hellwig 
2873626738bSChristoph Hellwig 	return 0;
288d67c2571SChristoph Hellwig 
289d67c2571SChristoph Hellwig out_free_buffers:
290fa0c812cSChristoph Hellwig 	for (i = 0; i < RAID6_KUNIT_MAX_BUFFERS; i++)
291d67c2571SChristoph Hellwig 		vfree(test_buffers[i]);
292d67c2571SChristoph Hellwig 	memset(test_buffers, 0, sizeof(test_buffers));
293d67c2571SChristoph Hellwig out_free_recov_buffers:
294d67c2571SChristoph Hellwig 	for (i = 0; i < RAID6_KUNIT_MAX_FAILURES; i++)
295d67c2571SChristoph Hellwig 		vfree(test_recov_buffers[i]);
296d67c2571SChristoph Hellwig 	memset(test_recov_buffers, 0, sizeof(test_recov_buffers));
297d67c2571SChristoph Hellwig 	return -ENOMEM;
298d67c2571SChristoph Hellwig }
299d67c2571SChristoph Hellwig 
300d67c2571SChristoph Hellwig static void raid6_suite_exit(struct kunit_suite *suite)
301d67c2571SChristoph Hellwig {
302d67c2571SChristoph Hellwig 	int i;
303d67c2571SChristoph Hellwig 
304fa0c812cSChristoph Hellwig 	for (i = 0; i < RAID6_KUNIT_MAX_BUFFERS; i++)
305d67c2571SChristoph Hellwig 		vfree(test_buffers[i]);
306d67c2571SChristoph Hellwig 	memset(test_buffers, 0, sizeof(test_buffers));
307d67c2571SChristoph Hellwig 	for (i = 0; i < RAID6_KUNIT_MAX_FAILURES; i++)
308d67c2571SChristoph Hellwig 		vfree(test_recov_buffers[i]);
309d67c2571SChristoph Hellwig 	memset(test_recov_buffers, 0, sizeof(test_recov_buffers));
3103626738bSChristoph Hellwig }
3113626738bSChristoph Hellwig 
3123626738bSChristoph Hellwig static struct kunit_suite raid6_test_suite = {
3133626738bSChristoph Hellwig 	.name		= "raid6",
3143626738bSChristoph Hellwig 	.test_cases	= raid6_test_cases,
3153626738bSChristoph Hellwig 	.suite_init	= raid6_suite_init,
316d67c2571SChristoph Hellwig 	.suite_exit	= raid6_suite_exit,
3173626738bSChristoph Hellwig };
3183626738bSChristoph Hellwig kunit_test_suite(raid6_test_suite);
3193626738bSChristoph Hellwig 
3203626738bSChristoph Hellwig MODULE_DESCRIPTION("Unit test for the RAID P/Q library functions");
3213626738bSChristoph Hellwig MODULE_LICENSE("GPL");
322