xref: /linux/lib/raid/raid6/algos.c (revision d999af2192efe4896222b777363c960083617085)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright 2002 H. Peter Anvin - All Rights Reserved
4  *
5  * Algorithm list and algorithm selection for RAID-6
6  */
7 
8 #include <linux/module.h>
9 #include <linux/gfp.h>
10 #include <linux/raid/pq.h>
11 #include <linux/slab.h>
12 #include <linux/static_call.h>
13 #include <kunit/visibility.h>
14 #include "algos.h"
15 
16 #define RAID6_MAX_ALGOS		16
17 static const struct raid6_calls *raid6_algos[RAID6_MAX_ALGOS];
18 static unsigned int raid6_nr_algos;
19 static const struct raid6_recov_calls *raid6_recov_algo;
20 
21 /* Selected algorithm */
22 DEFINE_STATIC_CALL_NULL(raid6_gen_syndrome_impl, *raid6_intx1.gen_syndrome);
23 DEFINE_STATIC_CALL_NULL(raid6_xor_syndrome_impl, *raid6_intx1.xor_syndrome);
24 DEFINE_STATIC_CALL_NULL(raid6_recov_2data_impl, *raid6_recov_intx1.data2);
25 DEFINE_STATIC_CALL_NULL(raid6_recov_datap_impl, *raid6_recov_intx1.datap);
26 
27 /**
28  * raid6_gen_syndrome - generate RAID6 P/Q parity
29  * @disks:	number of "disks" to operate on including parity
30  * @bytes:	length in bytes of each vector
31  * @ptrs:	@disks size array of memory pointers
32  *
33  * Generate @bytes worth of RAID6 P and Q parity in @ptrs[@disks - 2] and
34  * @ptrs[@disks - 1] respectively from the memory pointed to by @ptrs[0] to
35  * @ptrs[@disks - 3].
36  *
37  * @disks must be at least 4, and the memory pointed to by each member of @ptrs
38  * must be at least 64-byte aligned.  @bytes must be non-zero and a multiple of
39  * 512.
40  *
41  * See https://kernel.org/pub/linux/kernel/people/hpa/raid6.pdf for underlying
42  * algorithm.
43  */
44 void raid6_gen_syndrome(int disks, size_t bytes, void **ptrs)
45 {
46 	WARN_ON_ONCE(!in_task() || irqs_disabled() || softirq_count());
47 	WARN_ON_ONCE(bytes & 511);
48 	WARN_ON_ONCE(disks < RAID6_MIN_DISKS);
49 
50 	static_call(raid6_gen_syndrome_impl)(disks, bytes, ptrs);
51 }
52 EXPORT_SYMBOL_GPL(raid6_gen_syndrome);
53 
54 /**
55  * raid6_xor_syndrome - update RAID6 P/Q parity
56  * @disks:	number of "disks" to operate on including parity
57  * @start:	first index into @disk to update
58  * @stop:	last index into @disk to update
59  * @bytes:	length in bytes of each vector
60  * @ptrs:	@disks size array of memory pointers
61  *
62  * Update @bytes worth of RAID6 P and Q parity in @ptrs[@disks - 2] and
63  * @ptrs[@disks - 1] respectively for the memory pointed to by
64  * @ptrs[@start..@stop].
65  *
66  * This is used to update parity in place using the following sequence:
67  *
68  * 1) call raid6_xor_syndrome(disk, start, stop, ...) for the existing data.
69  * 2) update the the data in @ptrs[@start..@stop].
70  * 3) call raid6_xor_syndrome(disk, start, stop, ...) for the new data.
71  *
72  * Data between @start and @stop that is not changed should be filled
73  * with a pointer to the kernel zero page.
74  *
75  * @disks must be at least 4, and the memory pointed to by each member of @ptrs
76  * must be at least 64-byte aligned.  @bytes must be non-zero and a multiple of
77  * 512.  @stop must be larger or equal to @start.
78  */
79 void raid6_xor_syndrome(int disks, int start, int stop, size_t bytes,
80 		void **ptrs)
81 {
82 	WARN_ON_ONCE(!in_task() || irqs_disabled() || softirq_count());
83 	WARN_ON_ONCE(bytes & 511);
84 	WARN_ON_ONCE(disks < RAID6_MIN_DISKS);
85 	WARN_ON_ONCE(stop < start);
86 
87 	static_call(raid6_xor_syndrome_impl)(disks, start, stop, bytes, ptrs);
88 }
89 EXPORT_SYMBOL_GPL(raid6_xor_syndrome);
90 
91 /*
92  * raid6_can_xor_syndrome - check if raid6_xor_syndrome() can be used
93  *
94  * Returns %true if raid6_can_xor_syndrome() can be used, else %false.
95  */
96 bool raid6_can_xor_syndrome(void)
97 {
98 	return !!static_call_query(raid6_xor_syndrome_impl);
99 }
100 EXPORT_SYMBOL_GPL(raid6_can_xor_syndrome);
101 
102 /**
103  * raid6_recov_2data - recover two missing data disks
104  * @disks:	number of "disks" to operate on including parity
105  * @bytes:	length in bytes of each vector
106  * @faila:	first failed data disk index
107  * @failb:	second failed data disk index
108  * @ptrs:	@disks size array of memory pointers
109  *
110  * Rebuild @bytes of missing data in @ptrs[@faila] and @ptrs[@failb] from the
111  * data in the remaining disks and the two parities pointed to by the other
112  * indices between 0 and @disks - 1 in @ptrs.  @disks includes the data disks
113  * and the two parities.  @faila must be smaller than @failb.
114  *
115  * Memory pointed to by each pointer in @ptrs must be page aligned and is
116  * limited to %PAGE_SIZE.
117  */
118 void raid6_recov_2data(int disks, size_t bytes, int faila, int failb,
119 		void **ptrs)
120 {
121 	WARN_ON_ONCE(!in_task() || irqs_disabled() || softirq_count());
122 	WARN_ON_ONCE(bytes & 511);
123 	WARN_ON_ONCE(bytes > PAGE_SIZE);
124 	WARN_ON_ONCE(failb <= faila);
125 
126 	static_call(raid6_recov_2data_impl)(disks, bytes, faila, failb, ptrs);
127 }
128 EXPORT_SYMBOL_GPL(raid6_recov_2data);
129 
130 /**
131  * raid6_recov_datap - recover a missing data disk and missing P-parity
132  * @disks:	number of "disks" to operate on including parity
133  * @bytes:	length in bytes of each vector
134  * @faila:	failed data disk index
135  * @ptrs:	@disks size array of memory pointers
136  *
137  * Rebuild @bytes of missing data in @ptrs[@faila] and the missing P-parity in
138  * @ptrs[@disks - 2] from the data in the remaining disks and the Q-parity
139  * pointed to by the other indices between 0 and @disks - 1 in @ptrs.  @disks
140  * includes the data disks and the two parities.
141  *
142  * Memory pointed to by each pointer in @ptrs must be page aligned and is
143  * limited to %PAGE_SIZE.
144  */
145 void raid6_recov_datap(int disks, size_t bytes, int faila, void **ptrs)
146 {
147 	WARN_ON_ONCE(!in_task() || irqs_disabled() || softirq_count());
148 	WARN_ON_ONCE(bytes & 511);
149 	WARN_ON_ONCE(bytes > PAGE_SIZE);
150 
151 	static_call(raid6_recov_datap_impl)(disks, bytes, faila, ptrs);
152 }
153 EXPORT_SYMBOL_GPL(raid6_recov_datap);
154 
155 #define RAID6_TIME_JIFFIES_LG2	4
156 #define RAID6_TEST_DISKS	8
157 
158 static int raid6_choose_gen(void *(*const dptrs)[RAID6_TEST_DISKS],
159 		const int disks)
160 {
161 	/* work on the second half of the disks */
162 	int start = (disks >> 1) - 1, stop = disks - 3;
163 	const struct raid6_calls *best = NULL;
164 	unsigned long bestgenperf = 0;
165 	unsigned int i;
166 
167 	for (i = 0; i < raid6_nr_algos; i++) {
168 		const struct raid6_calls *algo = raid6_algos[i];
169 		unsigned long perf = 0, j0, j1;
170 
171 		preempt_disable();
172 		j0 = jiffies;
173 		while ((j1 = jiffies) == j0)
174 			cpu_relax();
175 		while (time_before(jiffies,
176 				    j1 + (1<<RAID6_TIME_JIFFIES_LG2))) {
177 			algo->gen_syndrome(disks, PAGE_SIZE, *dptrs);
178 			perf++;
179 		}
180 		preempt_enable();
181 
182 		if (perf > bestgenperf) {
183 			bestgenperf = perf;
184 			best = algo;
185 		}
186 		pr_info("raid6: %-8s gen() %5ld MB/s\n", algo->name,
187 			(perf * HZ * (disks-2)) >>
188 			(20 - PAGE_SHIFT + RAID6_TIME_JIFFIES_LG2));
189 	}
190 
191 	if (!best) {
192 		pr_err("raid6: Yikes! No algorithm found!\n");
193 		return -EINVAL;
194 	}
195 
196 	static_call_update(raid6_gen_syndrome_impl, best->gen_syndrome);
197 	static_call_update(raid6_xor_syndrome_impl, best->xor_syndrome);
198 
199 	pr_info("raid6: using algorithm %s gen() %ld MB/s\n",
200 		best->name,
201 		(bestgenperf * HZ * (disks - 2)) >>
202 		(20 - PAGE_SHIFT + RAID6_TIME_JIFFIES_LG2));
203 
204 	if (best->xor_syndrome) {
205 		unsigned long perf = 0, j0, j1;
206 
207 		preempt_disable();
208 		j0 = jiffies;
209 		while ((j1 = jiffies) == j0)
210 			cpu_relax();
211 		while (time_before(jiffies,
212 				   j1 + (1 << RAID6_TIME_JIFFIES_LG2))) {
213 			best->xor_syndrome(disks, start, stop,
214 					   PAGE_SIZE, *dptrs);
215 			perf++;
216 		}
217 		preempt_enable();
218 
219 		pr_info("raid6: .... xor() %ld MB/s, rmw enabled\n",
220 			(perf * HZ * (disks - 2)) >>
221 			(20 - PAGE_SHIFT + RAID6_TIME_JIFFIES_LG2 + 1));
222 	}
223 
224 	return 0;
225 }
226 
227 
228 /* Try to pick the best algorithm */
229 /* This code uses the gfmul table as convenient data set to abuse */
230 
231 static int __init raid6_select_algo(void)
232 {
233 	const int disks = RAID6_TEST_DISKS;
234 	char *disk_ptr, *p;
235 	void *dptrs[RAID6_TEST_DISKS];
236 	int i, cycle;
237 	int error;
238 
239 	if (!IS_ENABLED(CONFIG_RAID6_PQ_BENCHMARK) || raid6_nr_algos == 1) {
240 		pr_info("raid6: skipped pq benchmark and selected %s\n",
241 			raid6_algos[raid6_nr_algos - 1]->name);
242 		return 0;
243 	}
244 
245 	/* prepare the buffer and fill it circularly with gfmul table */
246 	disk_ptr = kmalloc(PAGE_SIZE * RAID6_TEST_DISKS, GFP_KERNEL);
247 	if (!disk_ptr) {
248 		pr_err("raid6: Yikes!  No memory available.\n");
249 		return -ENOMEM;
250 	}
251 
252 	p = disk_ptr;
253 	for (i = 0; i < disks; i++)
254 		dptrs[i] = p + PAGE_SIZE * i;
255 
256 	cycle = ((disks - 2) * PAGE_SIZE) / 65536;
257 	for (i = 0; i < cycle; i++) {
258 		memcpy(p, raid6_gfmul, 65536);
259 		p += 65536;
260 	}
261 
262 	if ((disks - 2) * PAGE_SIZE % 65536)
263 		memcpy(p, raid6_gfmul, (disks - 2) * PAGE_SIZE % 65536);
264 
265 	/* select raid gen_syndrome function */
266 	error = raid6_choose_gen(&dptrs, disks);
267 
268 	kfree(disk_ptr);
269 
270 	return error;
271 }
272 
273 /*
274  * Register a RAID6 P/Q generation algorithm.  The most optimized/unrolled
275  * implementation should be registered last so it will be selected when the
276  * boot-time benchmark is disabled.
277  */
278 void __init raid6_algo_add(const struct raid6_calls *algo)
279 {
280 	if (WARN_ON_ONCE(raid6_nr_algos == RAID6_MAX_ALGOS))
281 		return;
282 	raid6_algos[raid6_nr_algos++] = algo;
283 }
284 
285 void __init raid6_algo_add_default(void)
286 {
287 	raid6_algo_add(&raid6_intx1);
288 	raid6_algo_add(&raid6_intx2);
289 	raid6_algo_add(&raid6_intx4);
290 	raid6_algo_add(&raid6_intx8);
291 }
292 
293 void __init raid6_recov_algo_add(const struct raid6_recov_calls *algo)
294 {
295 	if (WARN_ON_ONCE(raid6_recov_algo))
296 		return;
297 	raid6_recov_algo = algo;
298 }
299 
300 #ifdef CONFIG_RAID6_PQ_ARCH
301 #include "pq_arch.h"
302 #else
303 static inline void arch_raid6_init(void)
304 {
305 	raid6_algo_add_default();
306 }
307 #endif /* CONFIG_RAID6_PQ_ARCH */
308 
309 static int __init raid6_init(void)
310 {
311 	/*
312 	 * Architectures providing arch_raid6_init must add all PQ generation
313 	 * algorithms they want to consider in arch_raid6_init(), including
314 	 * the generic ones using raid6_algo_add_default() if wanted.
315 	 */
316 	arch_raid6_init();
317 
318 	/*
319 	 * Architectures don't have to set a recovery algorithm, we'll just pick
320 	 * the generic integer one if none was set.
321 	 */
322 	if (!raid6_recov_algo)
323 		raid6_recov_algo = &raid6_recov_intx1;
324 	static_call_update(raid6_recov_2data_impl, raid6_recov_algo->data2);
325 	static_call_update(raid6_recov_datap_impl, raid6_recov_algo->datap);
326 	pr_info("raid6: using %s recovery algorithm\n", raid6_recov_algo->name);
327 
328 	/*
329 	 * Pick the last registered implementation as the temporary default until
330 	 * calibration happens.
331 	 */
332 	static_call_update(raid6_gen_syndrome_impl,
333 			raid6_algos[raid6_nr_algos - 1]->gen_syndrome);
334 	static_call_update(raid6_xor_syndrome_impl,
335 			raid6_algos[raid6_nr_algos - 1]->xor_syndrome);
336 
337 #ifdef MODULE
338 	return raid6_select_algo();
339 #else
340 	return 0;
341 #endif
342 }
343 
344 static void __exit raid6_exit(void)
345 {
346 }
347 
348 /*
349  * When built-in we must register the default implementation before md
350  * initializes, but we don't want calibration to run that early as that
351  * would delay the boot process.
352  */
353 #ifndef MODULE
354 device_initcall(raid6_select_algo);
355 #endif
356 subsys_initcall(raid6_init);
357 module_exit(raid6_exit);
358 MODULE_LICENSE("GPL");
359 MODULE_DESCRIPTION("RAID6 Q-syndrome calculations");
360 
361 #if IS_ENABLED(CONFIG_RAID6_PQ_KUNIT_TEST)
362 const struct raid6_calls *raid6_algo_find(unsigned int idx)
363 {
364 	if (idx >= raid6_nr_algos) {
365 		/*
366 		 * Always include the simplest generic integer implementation in
367 		 * the unit tests as a baseline.
368 		 */
369 		if (idx == raid6_nr_algos &&
370 		    raid6_algos[0] != &raid6_intx1)
371 			return &raid6_intx1;
372 		return NULL;
373 	}
374 	return raid6_algos[idx];
375 }
376 EXPORT_SYMBOL_IF_KUNIT(raid6_algo_find);
377 
378 const struct raid6_recov_calls *raid6_recov_algo_find(unsigned int idx)
379 {
380 	switch (idx) {
381 	case 0:
382 		/* always test the generic integer implementation */
383 		return &raid6_recov_intx1;
384 	case 1:
385 		/* test the optimized implementation if there is one */
386 		if (raid6_recov_algo != &raid6_recov_intx1)
387 			return raid6_recov_algo;
388 		return NULL;
389 	default:
390 		return NULL;
391 	}
392 }
393 EXPORT_SYMBOL_IF_KUNIT(raid6_recov_algo_find);
394 #endif /* CONFIG_RAID6_PQ_KUNIT_TEST */
395