xref: /linux/lib/raid/raid6/tests/raid6_kunit.c (revision d67c25712fe3c5d78fea03827771c49debb89c80)
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 <linux/vmalloc.h>
11 #include "../algos.h"
12 
13 MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
14 
15 #define RAID6_KUNIT_SEED		42
16 #define RAID6_KUNIT_MAX_FAILURES	2
17 
18 #define NDISKS		16	/* Including P and Q */
19 
20 static struct rnd_state rng;
21 static void *dataptrs[NDISKS];
22 static void *test_buffers[NDISKS];
23 static void *test_recov_buffers[RAID6_KUNIT_MAX_FAILURES];
24 
25 struct test_args {
26 	unsigned int recov_idx;
27 	const struct raid6_recov_calls *recov;
28 	unsigned int gen_idx;
29 	const struct raid6_calls *gen;
30 };
31 
32 static struct test_args args;
33 
34 static void makedata(int start, int stop)
35 {
36 	int i;
37 
38 	for (i = start; i <= stop; i++) {
39 		prandom_bytes_state(&rng, test_buffers[i], PAGE_SIZE);
40 		dataptrs[i] = test_buffers[i];
41 	}
42 }
43 
44 static char member_type(int d)
45 {
46 	switch (d) {
47 	case NDISKS-2:
48 		return 'P';
49 	case NDISKS-1:
50 		return 'Q';
51 	default:
52 		return 'D';
53 	}
54 }
55 
56 static void test_recover(struct kunit *test, int faila, int failb)
57 {
58 	const struct test_args *ta = test->param_value;
59 	int i;
60 
61 	for (i = 0; i < RAID6_KUNIT_MAX_FAILURES; i++)
62 		memset(test_recov_buffers[i], 0xf0, PAGE_SIZE);
63 
64 	dataptrs[faila] = test_recov_buffers[0];
65 	dataptrs[failb] = test_recov_buffers[1];
66 
67 	if (failb == NDISKS - 1) {
68 		/*
69 		 * We don't implement the data+Q failure scenario, since it
70 		 * is equivalent to a RAID-5 failure (XOR, then recompute Q).
71 		 */
72 		if (faila != NDISKS - 2)
73 			goto skip;
74 
75 		/* P+Q failure.  Just rebuild the syndrome. */
76 		ta->gen->gen_syndrome(NDISKS, PAGE_SIZE, dataptrs);
77 	} else if (failb == NDISKS - 2) {
78 		/* data+P failure. */
79 		ta->recov->datap(NDISKS, PAGE_SIZE, faila, dataptrs);
80 	} else {
81 		/* data+data failure. */
82 		ta->recov->data2(NDISKS, PAGE_SIZE, faila, failb, dataptrs);
83 	}
84 
85 	KUNIT_EXPECT_MEMEQ_MSG(test, test_buffers[faila], test_recov_buffers[0],
86 			PAGE_SIZE,
87 			"faila miscompared: %3d[%c] (failb=%3d[%c])\n",
88 			faila, member_type(faila),
89 			failb, member_type(failb));
90 	KUNIT_EXPECT_MEMEQ_MSG(test, test_buffers[failb], test_recov_buffers[1],
91 			PAGE_SIZE,
92 			"failb miscompared: %3d[%c] (faila=%3d[%c])\n",
93 			failb, member_type(failb),
94 			faila, member_type(faila));
95 
96 skip:
97 	dataptrs[faila] = test_buffers[faila];
98 	dataptrs[failb] = test_buffers[failb];
99 }
100 
101 static void raid6_test(struct kunit *test)
102 {
103 	const struct test_args *ta = test->param_value;
104 	int i, j, p1, p2;
105 
106 	/* Nuke syndromes */
107 	memset(test_buffers[NDISKS - 2], 0xee, PAGE_SIZE);
108 	memset(test_buffers[NDISKS - 1], 0xee, PAGE_SIZE);
109 
110 	/* Generate assumed good syndrome */
111 	ta->gen->gen_syndrome(NDISKS, PAGE_SIZE, (void **)&dataptrs);
112 
113 	for (i = 0; i < NDISKS - 1; i++)
114 		for (j = i + 1; j < NDISKS; j++)
115 			test_recover(test, i, j);
116 
117 	if (!ta->gen->xor_syndrome)
118 		return;
119 
120 	for (p1 = 0; p1 < NDISKS - 2; p1++) {
121 		for (p2 = p1; p2 < NDISKS - 2; p2++) {
122 			/* Simulate rmw run */
123 			ta->gen->xor_syndrome(NDISKS, p1, p2, PAGE_SIZE,
124 					(void **)&dataptrs);
125 			makedata(p1, p2);
126 			ta->gen->xor_syndrome(NDISKS, p1, p2, PAGE_SIZE,
127 					(void **)&dataptrs);
128 
129 			for (i = 0; i < NDISKS - 1; i++)
130 				for (j = i + 1; j < NDISKS; j++)
131 					test_recover(test, i, j);
132 		}
133 	}
134 }
135 
136 static const void *raid6_gen_params(struct kunit *test, const void *prev,
137 		char *desc)
138 {
139 	if (!prev) {
140 		memset(&args, 0, sizeof(args));
141 next_algo:
142 		args.recov_idx = 0;
143 		args.gen = raid6_algo_find(args.gen_idx);
144 		if (!args.gen)
145 			return NULL;
146 	}
147 
148 	if (args.recov)
149 		args.recov_idx++;
150 	args.recov = raid6_recov_algo_find(args.recov_idx);
151 	if (!args.recov) {
152 		args.gen_idx++;
153 		goto next_algo;
154 	}
155 
156 	snprintf(desc, KUNIT_PARAM_DESC_SIZE, "gen=%s recov=%s",
157 			args.gen->name, args.recov->name);
158 	return &args;
159 }
160 
161 static struct kunit_case raid6_test_cases[] = {
162 	KUNIT_CASE_PARAM(raid6_test, raid6_gen_params),
163 	{},
164 };
165 
166 static int raid6_suite_init(struct kunit_suite *suite)
167 {
168 	int i;
169 
170 	prandom_seed_state(&rng, RAID6_KUNIT_SEED);
171 
172 	/*
173 	 * Allocate the test buffer using vmalloc() with a page-aligned length
174 	 * so that it is immediately followed by a guard page.  This allows
175 	 * buffer overreads to be detected, even in assembly code.
176 	 */
177 	for (i = 0; i < RAID6_KUNIT_MAX_FAILURES; i++) {
178 		test_recov_buffers[i] = vmalloc(PAGE_SIZE);
179 		if (!test_recov_buffers[i])
180 			goto out_free_recov_buffers;
181 	}
182 	for (i = 0; i < NDISKS; i++) {
183 		test_buffers[i] = vmalloc(PAGE_SIZE);
184 		if (!test_buffers[i])
185 			goto out_free_buffers;
186 	}
187 
188 	makedata(0, NDISKS - 1);
189 
190 	return 0;
191 
192 out_free_buffers:
193 	for (i = 0; i < NDISKS; i++)
194 		vfree(test_buffers[i]);
195 	memset(test_buffers, 0, sizeof(test_buffers));
196 out_free_recov_buffers:
197 	for (i = 0; i < RAID6_KUNIT_MAX_FAILURES; i++)
198 		vfree(test_recov_buffers[i]);
199 	memset(test_recov_buffers, 0, sizeof(test_recov_buffers));
200 	return -ENOMEM;
201 }
202 
203 static void raid6_suite_exit(struct kunit_suite *suite)
204 {
205 	int i;
206 
207 	for (i = 0; i < NDISKS; i++)
208 		vfree(test_buffers[i]);
209 	memset(test_buffers, 0, sizeof(test_buffers));
210 	for (i = 0; i < RAID6_KUNIT_MAX_FAILURES; i++)
211 		vfree(test_recov_buffers[i]);
212 	memset(test_recov_buffers, 0, sizeof(test_recov_buffers));
213 }
214 
215 static struct kunit_suite raid6_test_suite = {
216 	.name		= "raid6",
217 	.test_cases	= raid6_test_cases,
218 	.suite_init	= raid6_suite_init,
219 	.suite_exit	= raid6_suite_exit,
220 };
221 kunit_test_suite(raid6_test_suite);
222 
223 MODULE_DESCRIPTION("Unit test for the RAID P/Q library functions");
224 MODULE_LICENSE("GPL");
225