xref: /linux/lib/raid/raid6/tests/raid6_kunit.c (revision fa0c812c0aa58d4375317d804dd54e44b8bcf5e3)
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>
11*fa0c812cSChristoph 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
17*fa0c812cSChristoph Hellwig #define RAID6_KUNIT_NUM_TEST_ITERS	10
18*fa0c812cSChristoph Hellwig #define RAID6_KUNIT_MAX_BUFFERS		64 /* Including P and Q */
19d67c2571SChristoph Hellwig #define RAID6_KUNIT_MAX_FAILURES	2
20*fa0c812cSChristoph Hellwig #define RAID6_KUNIT_MAX_BYTES		PAGE_SIZE
213626738bSChristoph Hellwig 
223626738bSChristoph Hellwig static struct rnd_state rng;
23*fa0c812cSChristoph Hellwig static void *test_buffers[RAID6_KUNIT_MAX_BUFFERS];
24d67c2571SChristoph Hellwig static void *test_recov_buffers[RAID6_KUNIT_MAX_FAILURES];
25*fa0c812cSChristoph Hellwig static size_t test_buflen;
263626738bSChristoph Hellwig 
272175395fSChristoph Hellwig struct test_args {
282175395fSChristoph Hellwig 	unsigned int recov_idx;
292175395fSChristoph Hellwig 	const struct raid6_recov_calls *recov;
302175395fSChristoph Hellwig 	unsigned int gen_idx;
312175395fSChristoph Hellwig 	const struct raid6_calls *gen;
322175395fSChristoph Hellwig };
332175395fSChristoph Hellwig 
342175395fSChristoph Hellwig static struct test_args args;
352175395fSChristoph Hellwig 
36*fa0c812cSChristoph Hellwig static u32 rand32(void)
37*fa0c812cSChristoph Hellwig {
38*fa0c812cSChristoph Hellwig 	return prandom_u32_state(&rng);
39*fa0c812cSChristoph Hellwig }
40*fa0c812cSChristoph Hellwig 
41*fa0c812cSChristoph Hellwig /* Generate a random length that is a multiple of 512. */
42*fa0c812cSChristoph Hellwig static unsigned int random_length(unsigned int max_length)
43*fa0c812cSChristoph Hellwig {
44*fa0c812cSChristoph Hellwig 	return round_up((rand32() % max_length) + 1, 512);
45*fa0c812cSChristoph Hellwig }
46*fa0c812cSChristoph Hellwig 
47*fa0c812cSChristoph Hellwig static unsigned int random_nr_buffers(void)
48*fa0c812cSChristoph Hellwig {
49*fa0c812cSChristoph Hellwig 	return (rand32() % (RAID6_KUNIT_MAX_BUFFERS - (RAID6_MIN_DISKS - 1))) +
50*fa0c812cSChristoph Hellwig 			RAID6_MIN_DISKS;
51*fa0c812cSChristoph Hellwig }
52*fa0c812cSChristoph Hellwig 
533626738bSChristoph Hellwig static void makedata(int start, int stop)
543626738bSChristoph Hellwig {
553626738bSChristoph Hellwig 	int i;
563626738bSChristoph Hellwig 
57562bcbfcSChristoph Hellwig 	for (i = start; i <= stop; i++)
58*fa0c812cSChristoph Hellwig 		prandom_bytes_state(&rng, test_buffers[i], test_buflen);
593626738bSChristoph Hellwig }
603626738bSChristoph Hellwig 
61*fa0c812cSChristoph Hellwig static char member_type(unsigned int nr_buffers, int d)
623626738bSChristoph Hellwig {
63*fa0c812cSChristoph Hellwig 	if (d == nr_buffers - 2)
643626738bSChristoph Hellwig 		return 'P';
65*fa0c812cSChristoph Hellwig 	if (d == nr_buffers - 1)
663626738bSChristoph Hellwig 		return 'Q';
673626738bSChristoph Hellwig 	return 'D';
683626738bSChristoph Hellwig }
693626738bSChristoph Hellwig 
70*fa0c812cSChristoph Hellwig static void test_recover_one(struct kunit *test, unsigned int nr_buffers,
71*fa0c812cSChristoph Hellwig 		unsigned int len, int faila, int failb)
723626738bSChristoph Hellwig {
732175395fSChristoph Hellwig 	const struct test_args *ta = test->param_value;
74*fa0c812cSChristoph Hellwig 	void *dataptrs[RAID6_KUNIT_MAX_BUFFERS];
75d67c2571SChristoph Hellwig 	int i;
762175395fSChristoph Hellwig 
77*fa0c812cSChristoph Hellwig 	if (faila > failb)
78*fa0c812cSChristoph Hellwig 		swap(faila, failb);
79*fa0c812cSChristoph Hellwig 
80d67c2571SChristoph Hellwig 	for (i = 0; i < RAID6_KUNIT_MAX_FAILURES; i++)
81*fa0c812cSChristoph Hellwig 		memset(test_recov_buffers[i], 0xf0, test_buflen);
823626738bSChristoph Hellwig 
83562bcbfcSChristoph Hellwig 	memcpy(dataptrs, test_buffers, sizeof(dataptrs));
84d67c2571SChristoph Hellwig 	dataptrs[faila] = test_recov_buffers[0];
85d67c2571SChristoph Hellwig 	dataptrs[failb] = test_recov_buffers[1];
863626738bSChristoph Hellwig 
87*fa0c812cSChristoph Hellwig 	if (failb == nr_buffers - 1) {
883626738bSChristoph Hellwig 		/*
893626738bSChristoph Hellwig 		 * We don't implement the data+Q failure scenario, since it
903626738bSChristoph Hellwig 		 * is equivalent to a RAID-5 failure (XOR, then recompute Q).
913626738bSChristoph Hellwig 		 */
92*fa0c812cSChristoph Hellwig 		if (WARN_ON_ONCE(faila != nr_buffers - 2))
93562bcbfcSChristoph Hellwig 			return;
943626738bSChristoph Hellwig 
953626738bSChristoph Hellwig 		/* P+Q failure.  Just rebuild the syndrome. */
96*fa0c812cSChristoph Hellwig 		ta->gen->gen_syndrome(nr_buffers, len, dataptrs);
97*fa0c812cSChristoph Hellwig 	} else if (failb == nr_buffers - 2) {
983626738bSChristoph Hellwig 		/* data+P failure. */
99*fa0c812cSChristoph Hellwig 		ta->recov->datap(nr_buffers, len, faila, dataptrs);
1003626738bSChristoph Hellwig 	} else {
1013626738bSChristoph Hellwig 		/* data+data failure. */
102*fa0c812cSChristoph Hellwig 		ta->recov->data2(nr_buffers, len, faila, failb, dataptrs);
1033626738bSChristoph Hellwig 	}
1043626738bSChristoph Hellwig 
105d67c2571SChristoph Hellwig 	KUNIT_EXPECT_MEMEQ_MSG(test, test_buffers[faila], test_recov_buffers[0],
106*fa0c812cSChristoph Hellwig 			len,
107*fa0c812cSChristoph Hellwig 			"faila miscompared: %3d[%c] buffers %u len %u (failb=%3d[%c])\n",
108*fa0c812cSChristoph Hellwig 			faila, member_type(nr_buffers, faila),
109*fa0c812cSChristoph Hellwig 			nr_buffers, len,
110*fa0c812cSChristoph Hellwig 			failb, member_type(nr_buffers, failb));
111d67c2571SChristoph Hellwig 	KUNIT_EXPECT_MEMEQ_MSG(test, test_buffers[failb], test_recov_buffers[1],
112*fa0c812cSChristoph Hellwig 			len,
113*fa0c812cSChristoph Hellwig 			"failb miscompared: %3d[%c] buffers %u len %u (faila=%3d[%c])\n",
114*fa0c812cSChristoph Hellwig 			failb, member_type(nr_buffers, failb),
115*fa0c812cSChristoph Hellwig 			nr_buffers, len,
116*fa0c812cSChristoph Hellwig 			faila, member_type(nr_buffers, faila));
117*fa0c812cSChristoph Hellwig }
118*fa0c812cSChristoph Hellwig 
119*fa0c812cSChristoph Hellwig static void test_recover(struct kunit *test, unsigned int nr_buffers,
120*fa0c812cSChristoph Hellwig 		unsigned int len)
121*fa0c812cSChristoph Hellwig {
122*fa0c812cSChristoph Hellwig 	unsigned int nr_data = nr_buffers - 2;
123*fa0c812cSChristoph Hellwig 	int iterations, i;
124*fa0c812cSChristoph Hellwig 
125*fa0c812cSChristoph Hellwig 	/* Test P+Q recovery */
126*fa0c812cSChristoph Hellwig 	test_recover_one(test, nr_buffers, len, nr_data, nr_buffers - 1);
127*fa0c812cSChristoph Hellwig 
128*fa0c812cSChristoph Hellwig 	/* Test data+P recovery */
129*fa0c812cSChristoph Hellwig 	for (i = 0; i < nr_buffers - 2; i++)
130*fa0c812cSChristoph Hellwig 		test_recover_one(test, nr_buffers, len, i, nr_data);
131*fa0c812cSChristoph Hellwig 
132*fa0c812cSChristoph Hellwig 	/* Double data failure is impossible with a single data disk */
133*fa0c812cSChristoph Hellwig 	if (nr_data == 1)
134*fa0c812cSChristoph Hellwig 		return;
135*fa0c812cSChristoph Hellwig 
136*fa0c812cSChristoph Hellwig 	/* Test data+data recovery using random sampling */
137*fa0c812cSChristoph Hellwig 	iterations = nr_buffers * 2; /* should provide good enough coverage */
138*fa0c812cSChristoph Hellwig 	for (i = 0; i < iterations; i++) {
139*fa0c812cSChristoph Hellwig 		int faila = rand32() % nr_data, failb;
140*fa0c812cSChristoph Hellwig 
141*fa0c812cSChristoph Hellwig 		do {
142*fa0c812cSChristoph Hellwig 			failb = rand32() % nr_data;
143*fa0c812cSChristoph Hellwig 		} while (failb == faila);
144*fa0c812cSChristoph Hellwig 
145*fa0c812cSChristoph Hellwig 		test_recover_one(test, nr_buffers, len, faila, failb);
146*fa0c812cSChristoph Hellwig 	}
147*fa0c812cSChristoph Hellwig }
148*fa0c812cSChristoph Hellwig 
149*fa0c812cSChristoph Hellwig /* Simulate rmw run */
150*fa0c812cSChristoph Hellwig static void test_rmw_one(struct kunit *test, unsigned int nr_buffers,
151*fa0c812cSChristoph Hellwig 		unsigned int len, int p1, int p2)
152*fa0c812cSChristoph Hellwig {
153*fa0c812cSChristoph Hellwig 	const struct test_args *ta = test->param_value;
154*fa0c812cSChristoph Hellwig 
155*fa0c812cSChristoph Hellwig 	ta->gen->xor_syndrome(nr_buffers, p1, p2, len, test_buffers);
156*fa0c812cSChristoph Hellwig 	makedata(p1, p2);
157*fa0c812cSChristoph Hellwig 	ta->gen->xor_syndrome(nr_buffers, p1, p2, len, test_buffers);
158*fa0c812cSChristoph Hellwig 	test_recover(test, nr_buffers, len);
159*fa0c812cSChristoph Hellwig }
160*fa0c812cSChristoph Hellwig 
161*fa0c812cSChristoph Hellwig static void test_rmw(struct kunit *test, unsigned int nr_buffers,
162*fa0c812cSChristoph Hellwig 		unsigned int len)
163*fa0c812cSChristoph Hellwig {
164*fa0c812cSChristoph Hellwig 	int iterations = nr_buffers / 2, i;
165*fa0c812cSChristoph Hellwig 
166*fa0c812cSChristoph Hellwig 	for (i = 0; i < iterations; i++) {
167*fa0c812cSChristoph Hellwig 		int p1 = rand32() % (nr_buffers - 2);
168*fa0c812cSChristoph Hellwig 		int p2 = rand32() % (nr_buffers - 2);
169*fa0c812cSChristoph Hellwig 
170*fa0c812cSChristoph Hellwig 		if (p2 < p1)
171*fa0c812cSChristoph Hellwig 			swap(p1, p2);
172*fa0c812cSChristoph Hellwig 		test_rmw_one(test, nr_buffers, len, p1, p2);
173*fa0c812cSChristoph Hellwig 	}
174*fa0c812cSChristoph Hellwig }
175*fa0c812cSChristoph Hellwig 
176*fa0c812cSChristoph Hellwig static void raid6_test_one(struct kunit *test)
177*fa0c812cSChristoph Hellwig {
178*fa0c812cSChristoph Hellwig 	const struct test_args *ta = test->param_value;
179*fa0c812cSChristoph Hellwig 	unsigned int nr_buffers = random_nr_buffers();
180*fa0c812cSChristoph Hellwig 	unsigned int len = random_length(RAID6_KUNIT_MAX_BYTES);
181*fa0c812cSChristoph Hellwig 
182*fa0c812cSChristoph Hellwig 	/* Nuke syndromes */
183*fa0c812cSChristoph Hellwig 	memset(test_buffers[nr_buffers - 2], 0xee, test_buflen);
184*fa0c812cSChristoph Hellwig 	memset(test_buffers[nr_buffers - 1], 0xee, test_buflen);
185*fa0c812cSChristoph Hellwig 
186*fa0c812cSChristoph Hellwig 	/* Generate assumed good syndrome */
187*fa0c812cSChristoph Hellwig 	ta->gen->gen_syndrome(nr_buffers, len, test_buffers);
188*fa0c812cSChristoph Hellwig 
189*fa0c812cSChristoph Hellwig 	test_recover(test, nr_buffers, len);
190*fa0c812cSChristoph Hellwig 
191*fa0c812cSChristoph Hellwig 	if (ta->gen->xor_syndrome)
192*fa0c812cSChristoph Hellwig 		test_rmw(test, nr_buffers, len);
1933626738bSChristoph Hellwig }
1943626738bSChristoph Hellwig 
1953626738bSChristoph Hellwig static void raid6_test(struct kunit *test)
1963626738bSChristoph Hellwig {
197*fa0c812cSChristoph Hellwig 	int i;
1983626738bSChristoph Hellwig 
199*fa0c812cSChristoph Hellwig 	for (i = 0; i < RAID6_KUNIT_NUM_TEST_ITERS; i++)
200*fa0c812cSChristoph Hellwig 		raid6_test_one(test);
2013626738bSChristoph Hellwig }
2023626738bSChristoph Hellwig 
2032175395fSChristoph Hellwig static const void *raid6_gen_params(struct kunit *test, const void *prev,
2042175395fSChristoph Hellwig 		char *desc)
2052175395fSChristoph Hellwig {
2062175395fSChristoph Hellwig 	if (!prev) {
2072175395fSChristoph Hellwig 		memset(&args, 0, sizeof(args));
2082175395fSChristoph Hellwig next_algo:
2092175395fSChristoph Hellwig 		args.recov_idx = 0;
2102175395fSChristoph Hellwig 		args.gen = raid6_algo_find(args.gen_idx);
2112175395fSChristoph Hellwig 		if (!args.gen)
2122175395fSChristoph Hellwig 			return NULL;
2133626738bSChristoph Hellwig 	}
2142175395fSChristoph Hellwig 
2152175395fSChristoph Hellwig 	if (args.recov)
2162175395fSChristoph Hellwig 		args.recov_idx++;
2172175395fSChristoph Hellwig 	args.recov = raid6_recov_algo_find(args.recov_idx);
2182175395fSChristoph Hellwig 	if (!args.recov) {
2192175395fSChristoph Hellwig 		args.gen_idx++;
2202175395fSChristoph Hellwig 		goto next_algo;
2213626738bSChristoph Hellwig 	}
2222175395fSChristoph Hellwig 
2232175395fSChristoph Hellwig 	snprintf(desc, KUNIT_PARAM_DESC_SIZE, "gen=%s recov=%s",
2242175395fSChristoph Hellwig 			args.gen->name, args.recov->name);
2252175395fSChristoph Hellwig 	return &args;
2263626738bSChristoph Hellwig }
2273626738bSChristoph Hellwig 
2283626738bSChristoph Hellwig static struct kunit_case raid6_test_cases[] = {
2292175395fSChristoph Hellwig 	KUNIT_CASE_PARAM(raid6_test, raid6_gen_params),
2303626738bSChristoph Hellwig 	{},
2313626738bSChristoph Hellwig };
2323626738bSChristoph Hellwig 
2333626738bSChristoph Hellwig static int raid6_suite_init(struct kunit_suite *suite)
2343626738bSChristoph Hellwig {
235d67c2571SChristoph Hellwig 	int i;
236d67c2571SChristoph Hellwig 
2373626738bSChristoph Hellwig 	prandom_seed_state(&rng, RAID6_KUNIT_SEED);
238d67c2571SChristoph Hellwig 
239d67c2571SChristoph Hellwig 	/*
240d67c2571SChristoph Hellwig 	 * Allocate the test buffer using vmalloc() with a page-aligned length
241d67c2571SChristoph Hellwig 	 * so that it is immediately followed by a guard page.  This allows
242d67c2571SChristoph Hellwig 	 * buffer overreads to be detected, even in assembly code.
243d67c2571SChristoph Hellwig 	 */
244*fa0c812cSChristoph Hellwig 	test_buflen = round_up(RAID6_KUNIT_MAX_BYTES, PAGE_SIZE);
245d67c2571SChristoph Hellwig 	for (i = 0; i < RAID6_KUNIT_MAX_FAILURES; i++) {
246*fa0c812cSChristoph Hellwig 		test_recov_buffers[i] = vmalloc(test_buflen);
247d67c2571SChristoph Hellwig 		if (!test_recov_buffers[i])
248d67c2571SChristoph Hellwig 			goto out_free_recov_buffers;
249d67c2571SChristoph Hellwig 	}
250*fa0c812cSChristoph Hellwig 	for (i = 0; i < RAID6_KUNIT_MAX_BUFFERS; i++) {
251*fa0c812cSChristoph Hellwig 		test_buffers[i] = vmalloc(test_buflen);
252d67c2571SChristoph Hellwig 		if (!test_buffers[i])
253d67c2571SChristoph Hellwig 			goto out_free_buffers;
254d67c2571SChristoph Hellwig 	}
255d67c2571SChristoph Hellwig 
256*fa0c812cSChristoph Hellwig 	makedata(0, RAID6_KUNIT_MAX_BUFFERS - 1);
257d67c2571SChristoph Hellwig 
2583626738bSChristoph Hellwig 	return 0;
259d67c2571SChristoph Hellwig 
260d67c2571SChristoph Hellwig out_free_buffers:
261*fa0c812cSChristoph Hellwig 	for (i = 0; i < RAID6_KUNIT_MAX_BUFFERS; i++)
262d67c2571SChristoph Hellwig 		vfree(test_buffers[i]);
263d67c2571SChristoph Hellwig 	memset(test_buffers, 0, sizeof(test_buffers));
264d67c2571SChristoph Hellwig out_free_recov_buffers:
265d67c2571SChristoph Hellwig 	for (i = 0; i < RAID6_KUNIT_MAX_FAILURES; i++)
266d67c2571SChristoph Hellwig 		vfree(test_recov_buffers[i]);
267d67c2571SChristoph Hellwig 	memset(test_recov_buffers, 0, sizeof(test_recov_buffers));
268d67c2571SChristoph Hellwig 	return -ENOMEM;
269d67c2571SChristoph Hellwig }
270d67c2571SChristoph Hellwig 
271d67c2571SChristoph Hellwig static void raid6_suite_exit(struct kunit_suite *suite)
272d67c2571SChristoph Hellwig {
273d67c2571SChristoph Hellwig 	int i;
274d67c2571SChristoph Hellwig 
275*fa0c812cSChristoph Hellwig 	for (i = 0; i < RAID6_KUNIT_MAX_BUFFERS; i++)
276d67c2571SChristoph Hellwig 		vfree(test_buffers[i]);
277d67c2571SChristoph Hellwig 	memset(test_buffers, 0, sizeof(test_buffers));
278d67c2571SChristoph Hellwig 	for (i = 0; i < RAID6_KUNIT_MAX_FAILURES; i++)
279d67c2571SChristoph Hellwig 		vfree(test_recov_buffers[i]);
280d67c2571SChristoph Hellwig 	memset(test_recov_buffers, 0, sizeof(test_recov_buffers));
2813626738bSChristoph Hellwig }
2823626738bSChristoph Hellwig 
2833626738bSChristoph Hellwig static struct kunit_suite raid6_test_suite = {
2843626738bSChristoph Hellwig 	.name		= "raid6",
2853626738bSChristoph Hellwig 	.test_cases	= raid6_test_cases,
2863626738bSChristoph Hellwig 	.suite_init	= raid6_suite_init,
287d67c2571SChristoph Hellwig 	.suite_exit	= raid6_suite_exit,
2883626738bSChristoph Hellwig };
2893626738bSChristoph Hellwig kunit_test_suite(raid6_test_suite);
2903626738bSChristoph Hellwig 
2913626738bSChristoph Hellwig MODULE_DESCRIPTION("Unit test for the RAID P/Q library functions");
2923626738bSChristoph Hellwig MODULE_LICENSE("GPL");
293