xref: /linux/lib/raid/raid6/tests/raid6_kunit.c (revision 562bcbfcb99b2eb4fd17d6551d760450e128afe1)
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>
11769d603fSChristoph Hellwig #include "../algos.h"
123626738bSChristoph Hellwig 
133626738bSChristoph Hellwig MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
143626738bSChristoph Hellwig 
153626738bSChristoph Hellwig #define RAID6_KUNIT_SEED		42
16d67c2571SChristoph Hellwig #define RAID6_KUNIT_MAX_FAILURES	2
173626738bSChristoph Hellwig 
183626738bSChristoph Hellwig #define NDISKS		16	/* Including P and Q */
193626738bSChristoph Hellwig 
203626738bSChristoph Hellwig static struct rnd_state rng;
21d67c2571SChristoph Hellwig static void *test_buffers[NDISKS];
22d67c2571SChristoph Hellwig static void *test_recov_buffers[RAID6_KUNIT_MAX_FAILURES];
233626738bSChristoph Hellwig 
242175395fSChristoph Hellwig struct test_args {
252175395fSChristoph Hellwig 	unsigned int recov_idx;
262175395fSChristoph Hellwig 	const struct raid6_recov_calls *recov;
272175395fSChristoph Hellwig 	unsigned int gen_idx;
282175395fSChristoph Hellwig 	const struct raid6_calls *gen;
292175395fSChristoph Hellwig };
302175395fSChristoph Hellwig 
312175395fSChristoph Hellwig static struct test_args args;
322175395fSChristoph Hellwig 
333626738bSChristoph Hellwig static void makedata(int start, int stop)
343626738bSChristoph Hellwig {
353626738bSChristoph Hellwig 	int i;
363626738bSChristoph Hellwig 
37*562bcbfcSChristoph Hellwig 	for (i = start; i <= stop; i++)
38d67c2571SChristoph Hellwig 		prandom_bytes_state(&rng, test_buffers[i], PAGE_SIZE);
393626738bSChristoph Hellwig }
403626738bSChristoph Hellwig 
413626738bSChristoph Hellwig static char member_type(int d)
423626738bSChristoph Hellwig {
433626738bSChristoph Hellwig 	switch (d) {
443626738bSChristoph Hellwig 	case NDISKS-2:
453626738bSChristoph Hellwig 		return 'P';
463626738bSChristoph Hellwig 	case NDISKS-1:
473626738bSChristoph Hellwig 		return 'Q';
483626738bSChristoph Hellwig 	default:
493626738bSChristoph Hellwig 		return 'D';
503626738bSChristoph Hellwig 	}
513626738bSChristoph Hellwig }
523626738bSChristoph Hellwig 
532175395fSChristoph Hellwig static void test_recover(struct kunit *test, int faila, int failb)
543626738bSChristoph Hellwig {
552175395fSChristoph Hellwig 	const struct test_args *ta = test->param_value;
56*562bcbfcSChristoph Hellwig 	void *dataptrs[NDISKS];
57d67c2571SChristoph Hellwig 	int i;
582175395fSChristoph Hellwig 
59d67c2571SChristoph Hellwig 	for (i = 0; i < RAID6_KUNIT_MAX_FAILURES; i++)
60d67c2571SChristoph Hellwig 		memset(test_recov_buffers[i], 0xf0, PAGE_SIZE);
613626738bSChristoph Hellwig 
62*562bcbfcSChristoph Hellwig 	memcpy(dataptrs, test_buffers, sizeof(dataptrs));
63d67c2571SChristoph Hellwig 	dataptrs[faila] = test_recov_buffers[0];
64d67c2571SChristoph Hellwig 	dataptrs[failb] = test_recov_buffers[1];
653626738bSChristoph Hellwig 
663626738bSChristoph Hellwig 	if (failb == NDISKS - 1) {
673626738bSChristoph Hellwig 		/*
683626738bSChristoph Hellwig 		 * We don't implement the data+Q failure scenario, since it
693626738bSChristoph Hellwig 		 * is equivalent to a RAID-5 failure (XOR, then recompute Q).
703626738bSChristoph Hellwig 		 */
713626738bSChristoph Hellwig 		if (faila != NDISKS - 2)
72*562bcbfcSChristoph Hellwig 			return;
733626738bSChristoph Hellwig 
743626738bSChristoph Hellwig 		/* P+Q failure.  Just rebuild the syndrome. */
752175395fSChristoph Hellwig 		ta->gen->gen_syndrome(NDISKS, PAGE_SIZE, dataptrs);
763626738bSChristoph Hellwig 	} else if (failb == NDISKS - 2) {
773626738bSChristoph Hellwig 		/* data+P failure. */
782175395fSChristoph Hellwig 		ta->recov->datap(NDISKS, PAGE_SIZE, faila, dataptrs);
793626738bSChristoph Hellwig 	} else {
803626738bSChristoph Hellwig 		/* data+data failure. */
812175395fSChristoph Hellwig 		ta->recov->data2(NDISKS, PAGE_SIZE, faila, failb, dataptrs);
823626738bSChristoph Hellwig 	}
833626738bSChristoph Hellwig 
84d67c2571SChristoph Hellwig 	KUNIT_EXPECT_MEMEQ_MSG(test, test_buffers[faila], test_recov_buffers[0],
85d67c2571SChristoph Hellwig 			PAGE_SIZE,
862175395fSChristoph Hellwig 			"faila miscompared: %3d[%c] (failb=%3d[%c])\n",
873626738bSChristoph Hellwig 			faila, member_type(faila),
883626738bSChristoph Hellwig 			failb, member_type(failb));
89d67c2571SChristoph Hellwig 	KUNIT_EXPECT_MEMEQ_MSG(test, test_buffers[failb], test_recov_buffers[1],
90d67c2571SChristoph Hellwig 			PAGE_SIZE,
912175395fSChristoph Hellwig 			"failb miscompared: %3d[%c] (faila=%3d[%c])\n",
923626738bSChristoph Hellwig 			failb, member_type(failb),
933626738bSChristoph Hellwig 			faila, member_type(faila));
943626738bSChristoph Hellwig }
953626738bSChristoph Hellwig 
963626738bSChristoph Hellwig static void raid6_test(struct kunit *test)
973626738bSChristoph Hellwig {
982175395fSChristoph Hellwig 	const struct test_args *ta = test->param_value;
993626738bSChristoph Hellwig 	int i, j, p1, p2;
1003626738bSChristoph Hellwig 
1013626738bSChristoph Hellwig 	/* Nuke syndromes */
102d67c2571SChristoph Hellwig 	memset(test_buffers[NDISKS - 2], 0xee, PAGE_SIZE);
103d67c2571SChristoph Hellwig 	memset(test_buffers[NDISKS - 1], 0xee, PAGE_SIZE);
1043626738bSChristoph Hellwig 
1053626738bSChristoph Hellwig 	/* Generate assumed good syndrome */
106*562bcbfcSChristoph Hellwig 	ta->gen->gen_syndrome(NDISKS, PAGE_SIZE, test_buffers);
1073626738bSChristoph Hellwig 
1083626738bSChristoph Hellwig 	for (i = 0; i < NDISKS - 1; i++)
1093626738bSChristoph Hellwig 		for (j = i + 1; j < NDISKS; j++)
1102175395fSChristoph Hellwig 			test_recover(test, i, j);
1113626738bSChristoph Hellwig 
1122175395fSChristoph Hellwig 	if (!ta->gen->xor_syndrome)
1132175395fSChristoph Hellwig 		return;
1143626738bSChristoph Hellwig 
1152175395fSChristoph Hellwig 	for (p1 = 0; p1 < NDISKS - 2; p1++) {
1163626738bSChristoph Hellwig 		for (p2 = p1; p2 < NDISKS - 2; p2++) {
1173626738bSChristoph Hellwig 			/* Simulate rmw run */
1182175395fSChristoph Hellwig 			ta->gen->xor_syndrome(NDISKS, p1, p2, PAGE_SIZE,
119*562bcbfcSChristoph Hellwig 					test_buffers);
1203626738bSChristoph Hellwig 			makedata(p1, p2);
1212175395fSChristoph Hellwig 			ta->gen->xor_syndrome(NDISKS, p1, p2, PAGE_SIZE,
122*562bcbfcSChristoph Hellwig 					test_buffers);
1233626738bSChristoph Hellwig 
1243626738bSChristoph Hellwig 			for (i = 0; i < NDISKS - 1; i++)
1253626738bSChristoph Hellwig 				for (j = i + 1; j < NDISKS; j++)
1262175395fSChristoph Hellwig 					test_recover(test, i, j);
1272175395fSChristoph Hellwig 		}
1282175395fSChristoph Hellwig 	}
1293626738bSChristoph Hellwig }
1303626738bSChristoph Hellwig 
1312175395fSChristoph Hellwig static const void *raid6_gen_params(struct kunit *test, const void *prev,
1322175395fSChristoph Hellwig 		char *desc)
1332175395fSChristoph Hellwig {
1342175395fSChristoph Hellwig 	if (!prev) {
1352175395fSChristoph Hellwig 		memset(&args, 0, sizeof(args));
1362175395fSChristoph Hellwig next_algo:
1372175395fSChristoph Hellwig 		args.recov_idx = 0;
1382175395fSChristoph Hellwig 		args.gen = raid6_algo_find(args.gen_idx);
1392175395fSChristoph Hellwig 		if (!args.gen)
1402175395fSChristoph Hellwig 			return NULL;
1413626738bSChristoph Hellwig 	}
1422175395fSChristoph Hellwig 
1432175395fSChristoph Hellwig 	if (args.recov)
1442175395fSChristoph Hellwig 		args.recov_idx++;
1452175395fSChristoph Hellwig 	args.recov = raid6_recov_algo_find(args.recov_idx);
1462175395fSChristoph Hellwig 	if (!args.recov) {
1472175395fSChristoph Hellwig 		args.gen_idx++;
1482175395fSChristoph Hellwig 		goto next_algo;
1493626738bSChristoph Hellwig 	}
1502175395fSChristoph Hellwig 
1512175395fSChristoph Hellwig 	snprintf(desc, KUNIT_PARAM_DESC_SIZE, "gen=%s recov=%s",
1522175395fSChristoph Hellwig 			args.gen->name, args.recov->name);
1532175395fSChristoph Hellwig 	return &args;
1543626738bSChristoph Hellwig }
1553626738bSChristoph Hellwig 
1563626738bSChristoph Hellwig static struct kunit_case raid6_test_cases[] = {
1572175395fSChristoph Hellwig 	KUNIT_CASE_PARAM(raid6_test, raid6_gen_params),
1583626738bSChristoph Hellwig 	{},
1593626738bSChristoph Hellwig };
1603626738bSChristoph Hellwig 
1613626738bSChristoph Hellwig static int raid6_suite_init(struct kunit_suite *suite)
1623626738bSChristoph Hellwig {
163d67c2571SChristoph Hellwig 	int i;
164d67c2571SChristoph Hellwig 
1653626738bSChristoph Hellwig 	prandom_seed_state(&rng, RAID6_KUNIT_SEED);
166d67c2571SChristoph Hellwig 
167d67c2571SChristoph Hellwig 	/*
168d67c2571SChristoph Hellwig 	 * Allocate the test buffer using vmalloc() with a page-aligned length
169d67c2571SChristoph Hellwig 	 * so that it is immediately followed by a guard page.  This allows
170d67c2571SChristoph Hellwig 	 * buffer overreads to be detected, even in assembly code.
171d67c2571SChristoph Hellwig 	 */
172d67c2571SChristoph Hellwig 	for (i = 0; i < RAID6_KUNIT_MAX_FAILURES; i++) {
173d67c2571SChristoph Hellwig 		test_recov_buffers[i] = vmalloc(PAGE_SIZE);
174d67c2571SChristoph Hellwig 		if (!test_recov_buffers[i])
175d67c2571SChristoph Hellwig 			goto out_free_recov_buffers;
176d67c2571SChristoph Hellwig 	}
177d67c2571SChristoph Hellwig 	for (i = 0; i < NDISKS; i++) {
178d67c2571SChristoph Hellwig 		test_buffers[i] = vmalloc(PAGE_SIZE);
179d67c2571SChristoph Hellwig 		if (!test_buffers[i])
180d67c2571SChristoph Hellwig 			goto out_free_buffers;
181d67c2571SChristoph Hellwig 	}
182d67c2571SChristoph Hellwig 
1833626738bSChristoph Hellwig 	makedata(0, NDISKS - 1);
184d67c2571SChristoph Hellwig 
1853626738bSChristoph Hellwig 	return 0;
186d67c2571SChristoph Hellwig 
187d67c2571SChristoph Hellwig out_free_buffers:
188d67c2571SChristoph Hellwig 	for (i = 0; i < NDISKS; i++)
189d67c2571SChristoph Hellwig 		vfree(test_buffers[i]);
190d67c2571SChristoph Hellwig 	memset(test_buffers, 0, sizeof(test_buffers));
191d67c2571SChristoph Hellwig out_free_recov_buffers:
192d67c2571SChristoph Hellwig 	for (i = 0; i < RAID6_KUNIT_MAX_FAILURES; i++)
193d67c2571SChristoph Hellwig 		vfree(test_recov_buffers[i]);
194d67c2571SChristoph Hellwig 	memset(test_recov_buffers, 0, sizeof(test_recov_buffers));
195d67c2571SChristoph Hellwig 	return -ENOMEM;
196d67c2571SChristoph Hellwig }
197d67c2571SChristoph Hellwig 
198d67c2571SChristoph Hellwig static void raid6_suite_exit(struct kunit_suite *suite)
199d67c2571SChristoph Hellwig {
200d67c2571SChristoph Hellwig 	int i;
201d67c2571SChristoph Hellwig 
202d67c2571SChristoph Hellwig 	for (i = 0; i < NDISKS; i++)
203d67c2571SChristoph Hellwig 		vfree(test_buffers[i]);
204d67c2571SChristoph Hellwig 	memset(test_buffers, 0, sizeof(test_buffers));
205d67c2571SChristoph Hellwig 	for (i = 0; i < RAID6_KUNIT_MAX_FAILURES; i++)
206d67c2571SChristoph Hellwig 		vfree(test_recov_buffers[i]);
207d67c2571SChristoph Hellwig 	memset(test_recov_buffers, 0, sizeof(test_recov_buffers));
2083626738bSChristoph Hellwig }
2093626738bSChristoph Hellwig 
2103626738bSChristoph Hellwig static struct kunit_suite raid6_test_suite = {
2113626738bSChristoph Hellwig 	.name		= "raid6",
2123626738bSChristoph Hellwig 	.test_cases	= raid6_test_cases,
2133626738bSChristoph Hellwig 	.suite_init	= raid6_suite_init,
214d67c2571SChristoph Hellwig 	.suite_exit	= raid6_suite_exit,
2153626738bSChristoph Hellwig };
2163626738bSChristoph Hellwig kunit_test_suite(raid6_test_suite);
2173626738bSChristoph Hellwig 
2183626738bSChristoph Hellwig MODULE_DESCRIPTION("Unit test for the RAID P/Q library functions");
2193626738bSChristoph Hellwig MODULE_LICENSE("GPL");
220