xref: /linux/lib/raid/raid6/tests/raid6_kunit.c (revision 769d603fc44f896e7f61de7f0cdb8b78d46bc8c8)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright 2002-2007 H. Peter Anvin - All Rights Reserved
4  *
5  * Test RAID-6 recovery algorithms.
6  */
7 
8 #include <kunit/test.h>
9 #include <linux/prandom.h>
10 #include "../algos.h"
11 
12 MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
13 
14 #define RAID6_KUNIT_SEED		42
15 
16 #define NDISKS		16	/* Including P and Q */
17 
18 static struct rnd_state rng;
19 static void *dataptrs[NDISKS];
20 static char data[NDISKS][PAGE_SIZE] __attribute__((aligned(PAGE_SIZE)));
21 static char recovi[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE)));
22 static char recovj[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE)));
23 
24 static void makedata(int start, int stop)
25 {
26 	int i;
27 
28 	for (i = start; i <= stop; i++) {
29 		prandom_bytes_state(&rng, data[i], PAGE_SIZE);
30 		dataptrs[i] = data[i];
31 	}
32 }
33 
34 static char member_type(int d)
35 {
36 	switch (d) {
37 	case NDISKS-2:
38 		return 'P';
39 	case NDISKS-1:
40 		return 'Q';
41 	default:
42 		return 'D';
43 	}
44 }
45 
46 static void test_disks(struct kunit *test, const struct raid6_calls *calls,
47 		const struct raid6_recov_calls *ra, int faila, int failb)
48 {
49 	memset(recovi, 0xf0, PAGE_SIZE);
50 	memset(recovj, 0xba, PAGE_SIZE);
51 
52 	dataptrs[faila] = recovi;
53 	dataptrs[failb] = recovj;
54 
55 	if (failb == NDISKS - 1) {
56 		/*
57 		 * We don't implement the data+Q failure scenario, since it
58 		 * is equivalent to a RAID-5 failure (XOR, then recompute Q).
59 		 */
60 		if (faila != NDISKS - 2)
61 			goto skip;
62 
63 		/* P+Q failure.  Just rebuild the syndrome. */
64 		calls->gen_syndrome(NDISKS, PAGE_SIZE, dataptrs);
65 	} else if (failb == NDISKS - 2) {
66 		/* data+P failure. */
67 		ra->datap(NDISKS, PAGE_SIZE, faila, dataptrs);
68 	} else {
69 		/* data+data failure. */
70 		ra->data2(NDISKS, PAGE_SIZE, faila, failb, dataptrs);
71 	}
72 
73 	KUNIT_EXPECT_MEMEQ_MSG(test, data[faila], recovi, PAGE_SIZE,
74 		"algo=%-8s/%-8s faila miscompared: %3d[%c] (failb=%3d[%c])\n",
75 	       calls->name, ra->name,
76 	       faila, member_type(faila),
77 	       failb, member_type(failb));
78 	KUNIT_EXPECT_MEMEQ_MSG(test, data[failb], recovj, PAGE_SIZE,
79 		"algo=%-8s/%-8s failb miscompared: %3d[%c] (faila=%3d[%c])\n",
80 	       calls->name, ra->name,
81 	       failb, member_type(failb),
82 	       faila, member_type(faila));
83 
84 skip:
85 	dataptrs[faila] = data[faila];
86 	dataptrs[failb] = data[failb];
87 }
88 
89 static void raid6_test(struct kunit *test)
90 {
91 	const struct raid6_calls *const *algo;
92 	const struct raid6_recov_calls *const *ra;
93 	int i, j, p1, p2;
94 
95 	for (ra = raid6_recov_algos; *ra; ra++) {
96 		if ((*ra)->valid  && !(*ra)->valid())
97 			continue;
98 
99 		for (algo = raid6_algos; *algo; algo++) {
100 			const struct raid6_calls *calls = *algo;
101 
102 			if (calls->valid && !calls->valid())
103 				continue;
104 
105 			/* Nuke syndromes */
106 			memset(data[NDISKS - 2], 0xee, PAGE_SIZE);
107 			memset(data[NDISKS - 1], 0xee, PAGE_SIZE);
108 
109 			/* Generate assumed good syndrome */
110 			calls->gen_syndrome(NDISKS, PAGE_SIZE,
111 						(void **)&dataptrs);
112 
113 			for (i = 0; i < NDISKS-1; i++)
114 				for (j = i+1; j < NDISKS; j++)
115 					test_disks(test, calls, *ra, i, j);
116 
117 			if (!calls->xor_syndrome)
118 				continue;
119 
120 			for (p1 = 0; p1 < NDISKS-2; p1++)
121 				for (p2 = p1; p2 < NDISKS-2; p2++) {
122 
123 					/* Simulate rmw run */
124 					calls->xor_syndrome(NDISKS, p1, p2, PAGE_SIZE,
125 								(void **)&dataptrs);
126 					makedata(p1, p2);
127 					calls->xor_syndrome(NDISKS, p1, p2, PAGE_SIZE,
128                                                                 (void **)&dataptrs);
129 
130 					for (i = 0; i < NDISKS-1; i++)
131 						for (j = i+1; j < NDISKS; j++)
132 							test_disks(test, calls,
133 									*ra, i, j);
134 				}
135 
136 		}
137 	}
138 }
139 
140 static struct kunit_case raid6_test_cases[] = {
141 	KUNIT_CASE(raid6_test),
142 	{},
143 };
144 
145 static int raid6_suite_init(struct kunit_suite *suite)
146 {
147 	prandom_seed_state(&rng, RAID6_KUNIT_SEED);
148 	makedata(0, NDISKS - 1);
149 	return 0;
150 }
151 
152 static struct kunit_suite raid6_test_suite = {
153 	.name		= "raid6",
154 	.test_cases	= raid6_test_cases,
155 	.suite_init	= raid6_suite_init,
156 };
157 kunit_test_suite(raid6_test_suite);
158 
159 MODULE_DESCRIPTION("Unit test for the RAID P/Q library functions");
160 MODULE_LICENSE("GPL");
161