xref: /linux/lib/raid/raid6/algos.c (revision 35472bc6f31b64e58d1fe560340aa4f1684b8d6d)
13626738bSChristoph Hellwig // SPDX-License-Identifier: GPL-2.0-or-later
23626738bSChristoph Hellwig /* -*- linux-c -*- ------------------------------------------------------- *
33626738bSChristoph Hellwig  *
43626738bSChristoph Hellwig  *   Copyright 2002 H. Peter Anvin - All Rights Reserved
53626738bSChristoph Hellwig  *
63626738bSChristoph Hellwig  * ----------------------------------------------------------------------- */
73626738bSChristoph Hellwig 
83626738bSChristoph Hellwig /*
93626738bSChristoph Hellwig  * raid6/algos.c
103626738bSChristoph Hellwig  *
113626738bSChristoph Hellwig  * Algorithm list and algorithm selection for RAID-6
123626738bSChristoph Hellwig  */
133626738bSChristoph Hellwig 
143626738bSChristoph Hellwig #include <linux/raid/pq.h>
153626738bSChristoph Hellwig #include <linux/module.h>
163626738bSChristoph Hellwig #include <linux/gfp.h>
173626738bSChristoph Hellwig #include <kunit/visibility.h>
183626738bSChristoph Hellwig 
19*35472bc6SChristoph Hellwig static const struct raid6_recov_calls *raid6_recov_algo;
20*35472bc6SChristoph Hellwig 
21*35472bc6SChristoph Hellwig /* Selected algorithm */
22*35472bc6SChristoph Hellwig static struct raid6_calls raid6_call;
23*35472bc6SChristoph Hellwig 
24*35472bc6SChristoph Hellwig /**
25*35472bc6SChristoph Hellwig  * raid6_gen_syndrome - generate RAID6 P/Q parity
26*35472bc6SChristoph Hellwig  * @disks:	number of "disks" to operate on including parity
27*35472bc6SChristoph Hellwig  * @bytes:	length in bytes of each vector
28*35472bc6SChristoph Hellwig  * @ptrs:	@disks size array of memory pointers
29*35472bc6SChristoph Hellwig  *
30*35472bc6SChristoph Hellwig  * Generate @bytes worth of RAID6 P and Q parity in @ptrs[@disks - 2] and
31*35472bc6SChristoph Hellwig  * @ptrs[@disks - 1] respectively from the memory pointed to by @ptrs[0] to
32*35472bc6SChristoph Hellwig  * @ptrs[@disks - 3].
33*35472bc6SChristoph Hellwig  *
34*35472bc6SChristoph Hellwig  * @disks must be at least 4, and the memory pointed to by each member of @ptrs
35*35472bc6SChristoph Hellwig  * must be at least 64-byte aligned.  @bytes must be non-zero and a multiple of
36*35472bc6SChristoph Hellwig  * 512.
37*35472bc6SChristoph Hellwig  *
38*35472bc6SChristoph Hellwig  * See https://kernel.org/pub/linux/kernel/people/hpa/raid6.pdf for underlying
39*35472bc6SChristoph Hellwig  * algorithm.
40*35472bc6SChristoph Hellwig  */
41*35472bc6SChristoph Hellwig void raid6_gen_syndrome(int disks, size_t bytes, void **ptrs)
42*35472bc6SChristoph Hellwig {
43*35472bc6SChristoph Hellwig 	WARN_ON_ONCE(!in_task() || irqs_disabled() || softirq_count());
44*35472bc6SChristoph Hellwig 	WARN_ON_ONCE(bytes & 511);
45*35472bc6SChristoph Hellwig 
46*35472bc6SChristoph Hellwig 	raid6_call.gen_syndrome(disks, bytes, ptrs);
47*35472bc6SChristoph Hellwig }
48*35472bc6SChristoph Hellwig EXPORT_SYMBOL_GPL(raid6_gen_syndrome);
49*35472bc6SChristoph Hellwig 
50*35472bc6SChristoph Hellwig /**
51*35472bc6SChristoph Hellwig  * raid6_xor_syndrome - update RAID6 P/Q parity
52*35472bc6SChristoph Hellwig  * @disks:	number of "disks" to operate on including parity
53*35472bc6SChristoph Hellwig  * @start:	first index into @disk to update
54*35472bc6SChristoph Hellwig  * @stop:	last index into @disk to update
55*35472bc6SChristoph Hellwig  * @bytes:	length in bytes of each vector
56*35472bc6SChristoph Hellwig  * @ptrs:	@disks size array of memory pointers
57*35472bc6SChristoph Hellwig  *
58*35472bc6SChristoph Hellwig  * Update @bytes worth of RAID6 P and Q parity in @ptrs[@disks - 2] and
59*35472bc6SChristoph Hellwig  * @ptrs[@disks - 1] respectively for the memory pointed to by
60*35472bc6SChristoph Hellwig  * @ptrs[@start..@stop].
61*35472bc6SChristoph Hellwig  *
62*35472bc6SChristoph Hellwig  * This is used to update parity in place using the following sequence:
63*35472bc6SChristoph Hellwig  *
64*35472bc6SChristoph Hellwig  * 1) call raid6_xor_syndrome(disk, start, stop, ...) for the existing data.
65*35472bc6SChristoph Hellwig  * 2) update the the data in @ptrs[@start..@stop].
66*35472bc6SChristoph Hellwig  * 3) call raid6_xor_syndrome(disk, start, stop, ...) for the new data.
67*35472bc6SChristoph Hellwig  *
68*35472bc6SChristoph Hellwig  * Data between @start and @stop that is not changed should be filled
69*35472bc6SChristoph Hellwig  * with a pointer to the kernel zero page.
70*35472bc6SChristoph Hellwig  *
71*35472bc6SChristoph Hellwig  * @disks must be at least 4, and the memory pointed to by each member of @ptrs
72*35472bc6SChristoph Hellwig  * must be at least 64-byte aligned.  @bytes must be non-zero and a multiple of
73*35472bc6SChristoph Hellwig  * 512.  @stop must be larger or equal to @start.
74*35472bc6SChristoph Hellwig  */
75*35472bc6SChristoph Hellwig void raid6_xor_syndrome(int disks, int start, int stop, size_t bytes,
76*35472bc6SChristoph Hellwig 		void **ptrs)
77*35472bc6SChristoph Hellwig {
78*35472bc6SChristoph Hellwig 	WARN_ON_ONCE(!in_task() || irqs_disabled() || softirq_count());
79*35472bc6SChristoph Hellwig 	WARN_ON_ONCE(bytes & 511);
80*35472bc6SChristoph Hellwig 	WARN_ON_ONCE(stop < start);
81*35472bc6SChristoph Hellwig 
82*35472bc6SChristoph Hellwig 	raid6_call.xor_syndrome(disks, start, stop, bytes, ptrs);
83*35472bc6SChristoph Hellwig }
84*35472bc6SChristoph Hellwig EXPORT_SYMBOL_GPL(raid6_xor_syndrome);
85*35472bc6SChristoph Hellwig 
86*35472bc6SChristoph Hellwig /*
87*35472bc6SChristoph Hellwig  * raid6_can_xor_syndrome - check if raid6_xor_syndrome() can be used
88*35472bc6SChristoph Hellwig  *
89*35472bc6SChristoph Hellwig  * Returns %true if raid6_can_xor_syndrome() can be used, else %false.
90*35472bc6SChristoph Hellwig  */
91*35472bc6SChristoph Hellwig bool raid6_can_xor_syndrome(void)
92*35472bc6SChristoph Hellwig {
93*35472bc6SChristoph Hellwig 	return !!raid6_call.xor_syndrome;
94*35472bc6SChristoph Hellwig }
95*35472bc6SChristoph Hellwig EXPORT_SYMBOL_GPL(raid6_can_xor_syndrome);
963626738bSChristoph Hellwig 
973626738bSChristoph Hellwig const struct raid6_calls * const raid6_algos[] = {
983626738bSChristoph Hellwig #if defined(__i386__) && !defined(__arch_um__)
993626738bSChristoph Hellwig 	&raid6_avx512x2,
1003626738bSChristoph Hellwig 	&raid6_avx512x1,
1013626738bSChristoph Hellwig 	&raid6_avx2x2,
1023626738bSChristoph Hellwig 	&raid6_avx2x1,
1033626738bSChristoph Hellwig 	&raid6_sse2x2,
1043626738bSChristoph Hellwig 	&raid6_sse2x1,
1053626738bSChristoph Hellwig 	&raid6_sse1x2,
1063626738bSChristoph Hellwig 	&raid6_sse1x1,
1073626738bSChristoph Hellwig 	&raid6_mmxx2,
1083626738bSChristoph Hellwig 	&raid6_mmxx1,
1093626738bSChristoph Hellwig #endif
1103626738bSChristoph Hellwig #if defined(__x86_64__) && !defined(__arch_um__)
1113626738bSChristoph Hellwig 	&raid6_avx512x4,
1123626738bSChristoph Hellwig 	&raid6_avx512x2,
1133626738bSChristoph Hellwig 	&raid6_avx512x1,
1143626738bSChristoph Hellwig 	&raid6_avx2x4,
1153626738bSChristoph Hellwig 	&raid6_avx2x2,
1163626738bSChristoph Hellwig 	&raid6_avx2x1,
1173626738bSChristoph Hellwig 	&raid6_sse2x4,
1183626738bSChristoph Hellwig 	&raid6_sse2x2,
1193626738bSChristoph Hellwig 	&raid6_sse2x1,
1203626738bSChristoph Hellwig #endif
1213626738bSChristoph Hellwig #ifdef CONFIG_ALTIVEC
1223626738bSChristoph Hellwig 	&raid6_vpermxor8,
1233626738bSChristoph Hellwig 	&raid6_vpermxor4,
1243626738bSChristoph Hellwig 	&raid6_vpermxor2,
1253626738bSChristoph Hellwig 	&raid6_vpermxor1,
1263626738bSChristoph Hellwig 	&raid6_altivec8,
1273626738bSChristoph Hellwig 	&raid6_altivec4,
1283626738bSChristoph Hellwig 	&raid6_altivec2,
1293626738bSChristoph Hellwig 	&raid6_altivec1,
1303626738bSChristoph Hellwig #endif
1313626738bSChristoph Hellwig #if defined(CONFIG_S390)
1323626738bSChristoph Hellwig 	&raid6_s390vx8,
1333626738bSChristoph Hellwig #endif
1343626738bSChristoph Hellwig #ifdef CONFIG_KERNEL_MODE_NEON
1353626738bSChristoph Hellwig 	&raid6_neonx8,
1363626738bSChristoph Hellwig 	&raid6_neonx4,
1373626738bSChristoph Hellwig 	&raid6_neonx2,
1383626738bSChristoph Hellwig 	&raid6_neonx1,
1393626738bSChristoph Hellwig #endif
1403626738bSChristoph Hellwig #ifdef CONFIG_LOONGARCH
1413626738bSChristoph Hellwig #ifdef CONFIG_CPU_HAS_LASX
1423626738bSChristoph Hellwig 	&raid6_lasx,
1433626738bSChristoph Hellwig #endif
1443626738bSChristoph Hellwig #ifdef CONFIG_CPU_HAS_LSX
1453626738bSChristoph Hellwig 	&raid6_lsx,
1463626738bSChristoph Hellwig #endif
1473626738bSChristoph Hellwig #endif
1483626738bSChristoph Hellwig #ifdef CONFIG_RISCV_ISA_V
1493626738bSChristoph Hellwig 	&raid6_rvvx1,
1503626738bSChristoph Hellwig 	&raid6_rvvx2,
1513626738bSChristoph Hellwig 	&raid6_rvvx4,
1523626738bSChristoph Hellwig 	&raid6_rvvx8,
1533626738bSChristoph Hellwig #endif
1543626738bSChristoph Hellwig 	&raid6_intx8,
1553626738bSChristoph Hellwig 	&raid6_intx4,
1563626738bSChristoph Hellwig 	&raid6_intx2,
1573626738bSChristoph Hellwig 	&raid6_intx1,
1583626738bSChristoph Hellwig 	NULL
1593626738bSChristoph Hellwig };
1603626738bSChristoph Hellwig EXPORT_SYMBOL_IF_KUNIT(raid6_algos);
1613626738bSChristoph Hellwig 
162*35472bc6SChristoph Hellwig /**
163*35472bc6SChristoph Hellwig  * raid6_recov_2data - recover two missing data disks
164*35472bc6SChristoph Hellwig  * @disks:	number of "disks" to operate on including parity
165*35472bc6SChristoph Hellwig  * @bytes:	length in bytes of each vector
166*35472bc6SChristoph Hellwig  * @faila:	first failed data disk index
167*35472bc6SChristoph Hellwig  * @failb:	second failed data disk index
168*35472bc6SChristoph Hellwig  * @ptrs:	@disks size array of memory pointers
169*35472bc6SChristoph Hellwig  *
170*35472bc6SChristoph Hellwig  * Rebuild @bytes of missing data in @ptrs[@faila] and @ptrs[@failb] from the
171*35472bc6SChristoph Hellwig  * data in the remaining disks and the two parities pointed to by the other
172*35472bc6SChristoph Hellwig  * indices between 0 and @disks - 1 in @ptrs.  @disks includes the data disks
173*35472bc6SChristoph Hellwig  * and the two parities.  @faila must be smaller than @failb.
174*35472bc6SChristoph Hellwig  *
175*35472bc6SChristoph Hellwig  * Memory pointed to by each pointer in @ptrs must be page aligned and is
176*35472bc6SChristoph Hellwig  * limited to %PAGE_SIZE.
177*35472bc6SChristoph Hellwig  */
178*35472bc6SChristoph Hellwig void raid6_recov_2data(int disks, size_t bytes, int faila, int failb,
179*35472bc6SChristoph Hellwig 		void **ptrs)
180*35472bc6SChristoph Hellwig {
181*35472bc6SChristoph Hellwig 	WARN_ON_ONCE(!in_task() || irqs_disabled() || softirq_count());
182*35472bc6SChristoph Hellwig 	WARN_ON_ONCE(bytes & 511);
183*35472bc6SChristoph Hellwig 	WARN_ON_ONCE(bytes > PAGE_SIZE);
184*35472bc6SChristoph Hellwig 	WARN_ON_ONCE(failb <= faila);
1853626738bSChristoph Hellwig 
186*35472bc6SChristoph Hellwig 	raid6_recov_algo->data2(disks, bytes, faila, failb, ptrs);
187*35472bc6SChristoph Hellwig }
188*35472bc6SChristoph Hellwig EXPORT_SYMBOL_GPL(raid6_recov_2data);
189*35472bc6SChristoph Hellwig 
190*35472bc6SChristoph Hellwig /**
191*35472bc6SChristoph Hellwig  * raid6_recov_datap - recover a missing data disk and missing P-parity
192*35472bc6SChristoph Hellwig  * @disks:	number of "disks" to operate on including parity
193*35472bc6SChristoph Hellwig  * @bytes:	length in bytes of each vector
194*35472bc6SChristoph Hellwig  * @faila:	failed data disk index
195*35472bc6SChristoph Hellwig  * @ptrs:	@disks size array of memory pointers
196*35472bc6SChristoph Hellwig  *
197*35472bc6SChristoph Hellwig  * Rebuild @bytes of missing data in @ptrs[@faila] and the missing P-parity in
198*35472bc6SChristoph Hellwig  * @ptrs[@disks - 2] from the data in the remaining disks and the Q-parity
199*35472bc6SChristoph Hellwig  * pointed to by the other indices between 0 and @disks - 1 in @ptrs.  @disks
200*35472bc6SChristoph Hellwig  * includes the data disks and the two parities.
201*35472bc6SChristoph Hellwig  *
202*35472bc6SChristoph Hellwig  * Memory pointed to by each pointer in @ptrs must be page aligned and is
203*35472bc6SChristoph Hellwig  * limited to %PAGE_SIZE.
204*35472bc6SChristoph Hellwig  */
205*35472bc6SChristoph Hellwig void raid6_recov_datap(int disks, size_t bytes, int faila, void **ptrs)
206*35472bc6SChristoph Hellwig {
207*35472bc6SChristoph Hellwig 	WARN_ON_ONCE(!in_task() || irqs_disabled() || softirq_count());
208*35472bc6SChristoph Hellwig 	WARN_ON_ONCE(bytes & 511);
209*35472bc6SChristoph Hellwig 	WARN_ON_ONCE(bytes > PAGE_SIZE);
210*35472bc6SChristoph Hellwig 
211*35472bc6SChristoph Hellwig 	raid6_recov_algo->datap(disks, bytes, faila, ptrs);
212*35472bc6SChristoph Hellwig }
213*35472bc6SChristoph Hellwig EXPORT_SYMBOL_GPL(raid6_recov_datap);
2143626738bSChristoph Hellwig 
2153626738bSChristoph Hellwig const struct raid6_recov_calls *const raid6_recov_algos[] = {
2163626738bSChristoph Hellwig #ifdef CONFIG_X86
2173626738bSChristoph Hellwig 	&raid6_recov_avx512,
2183626738bSChristoph Hellwig 	&raid6_recov_avx2,
2193626738bSChristoph Hellwig 	&raid6_recov_ssse3,
2203626738bSChristoph Hellwig #endif
2213626738bSChristoph Hellwig #ifdef CONFIG_S390
2223626738bSChristoph Hellwig 	&raid6_recov_s390xc,
2233626738bSChristoph Hellwig #endif
2243626738bSChristoph Hellwig #if defined(CONFIG_KERNEL_MODE_NEON)
2253626738bSChristoph Hellwig 	&raid6_recov_neon,
2263626738bSChristoph Hellwig #endif
2273626738bSChristoph Hellwig #ifdef CONFIG_LOONGARCH
2283626738bSChristoph Hellwig #ifdef CONFIG_CPU_HAS_LASX
2293626738bSChristoph Hellwig 	&raid6_recov_lasx,
2303626738bSChristoph Hellwig #endif
2313626738bSChristoph Hellwig #ifdef CONFIG_CPU_HAS_LSX
2323626738bSChristoph Hellwig 	&raid6_recov_lsx,
2333626738bSChristoph Hellwig #endif
2343626738bSChristoph Hellwig #endif
2353626738bSChristoph Hellwig #ifdef CONFIG_RISCV_ISA_V
2363626738bSChristoph Hellwig 	&raid6_recov_rvv,
2373626738bSChristoph Hellwig #endif
2383626738bSChristoph Hellwig 	&raid6_recov_intx1,
2393626738bSChristoph Hellwig 	NULL
2403626738bSChristoph Hellwig };
2413626738bSChristoph Hellwig EXPORT_SYMBOL_IF_KUNIT(raid6_recov_algos);
2423626738bSChristoph Hellwig 
2433626738bSChristoph Hellwig #define RAID6_TIME_JIFFIES_LG2	4
2443626738bSChristoph Hellwig #define RAID6_TEST_DISKS	8
2453626738bSChristoph Hellwig #define RAID6_TEST_DISKS_ORDER	3
2463626738bSChristoph Hellwig 
2473626738bSChristoph Hellwig static inline const struct raid6_recov_calls *raid6_choose_recov(void)
2483626738bSChristoph Hellwig {
2493626738bSChristoph Hellwig 	const struct raid6_recov_calls *const *algo;
2503626738bSChristoph Hellwig 	const struct raid6_recov_calls *best;
2513626738bSChristoph Hellwig 
2523626738bSChristoph Hellwig 	for (best = NULL, algo = raid6_recov_algos; *algo; algo++)
2533626738bSChristoph Hellwig 		if (!best || (*algo)->priority > best->priority)
2543626738bSChristoph Hellwig 			if (!(*algo)->valid || (*algo)->valid())
2553626738bSChristoph Hellwig 				best = *algo;
2563626738bSChristoph Hellwig 
2573626738bSChristoph Hellwig 	if (best) {
258*35472bc6SChristoph Hellwig 		raid6_recov_algo = best;
2593626738bSChristoph Hellwig 
2603626738bSChristoph Hellwig 		pr_info("raid6: using %s recovery algorithm\n", best->name);
2613626738bSChristoph Hellwig 	} else
2623626738bSChristoph Hellwig 		pr_err("raid6: Yikes! No recovery algorithm found!\n");
2633626738bSChristoph Hellwig 
2643626738bSChristoph Hellwig 	return best;
2653626738bSChristoph Hellwig }
2663626738bSChristoph Hellwig 
2673626738bSChristoph Hellwig static inline const struct raid6_calls *raid6_choose_gen(
2683626738bSChristoph Hellwig 	void *(*const dptrs)[RAID6_TEST_DISKS], const int disks)
2693626738bSChristoph Hellwig {
2703626738bSChristoph Hellwig 	unsigned long perf, bestgenperf, j0, j1;
2713626738bSChristoph Hellwig 	int start = (disks>>1)-1, stop = disks-3;	/* work on the second half of the disks */
2723626738bSChristoph Hellwig 	const struct raid6_calls *const *algo;
2733626738bSChristoph Hellwig 	const struct raid6_calls *best;
2743626738bSChristoph Hellwig 
2753626738bSChristoph Hellwig 	for (bestgenperf = 0, best = NULL, algo = raid6_algos; *algo; algo++) {
2763626738bSChristoph Hellwig 		if (!best || (*algo)->priority >= best->priority) {
2773626738bSChristoph Hellwig 			if ((*algo)->valid && !(*algo)->valid())
2783626738bSChristoph Hellwig 				continue;
2793626738bSChristoph Hellwig 
2803626738bSChristoph Hellwig 			if (!IS_ENABLED(CONFIG_RAID6_PQ_BENCHMARK)) {
2813626738bSChristoph Hellwig 				best = *algo;
2823626738bSChristoph Hellwig 				break;
2833626738bSChristoph Hellwig 			}
2843626738bSChristoph Hellwig 
2853626738bSChristoph Hellwig 			perf = 0;
2863626738bSChristoph Hellwig 
2873626738bSChristoph Hellwig 			preempt_disable();
2883626738bSChristoph Hellwig 			j0 = jiffies;
2893626738bSChristoph Hellwig 			while ((j1 = jiffies) == j0)
2903626738bSChristoph Hellwig 				cpu_relax();
2913626738bSChristoph Hellwig 			while (time_before(jiffies,
2923626738bSChristoph Hellwig 					    j1 + (1<<RAID6_TIME_JIFFIES_LG2))) {
2933626738bSChristoph Hellwig 				(*algo)->gen_syndrome(disks, PAGE_SIZE, *dptrs);
2943626738bSChristoph Hellwig 				perf++;
2953626738bSChristoph Hellwig 			}
2963626738bSChristoph Hellwig 			preempt_enable();
2973626738bSChristoph Hellwig 
2983626738bSChristoph Hellwig 			if (perf > bestgenperf) {
2993626738bSChristoph Hellwig 				bestgenperf = perf;
3003626738bSChristoph Hellwig 				best = *algo;
3013626738bSChristoph Hellwig 			}
3023626738bSChristoph Hellwig 			pr_info("raid6: %-8s gen() %5ld MB/s\n", (*algo)->name,
3033626738bSChristoph Hellwig 				(perf * HZ * (disks-2)) >>
3043626738bSChristoph Hellwig 				(20 - PAGE_SHIFT + RAID6_TIME_JIFFIES_LG2));
3053626738bSChristoph Hellwig 		}
3063626738bSChristoph Hellwig 	}
3073626738bSChristoph Hellwig 
3083626738bSChristoph Hellwig 	if (!best) {
3093626738bSChristoph Hellwig 		pr_err("raid6: Yikes! No algorithm found!\n");
3103626738bSChristoph Hellwig 		goto out;
3113626738bSChristoph Hellwig 	}
3123626738bSChristoph Hellwig 
3133626738bSChristoph Hellwig 	raid6_call = *best;
3143626738bSChristoph Hellwig 
3153626738bSChristoph Hellwig 	if (!IS_ENABLED(CONFIG_RAID6_PQ_BENCHMARK)) {
3163626738bSChristoph Hellwig 		pr_info("raid6: skipped pq benchmark and selected %s\n",
3173626738bSChristoph Hellwig 			best->name);
3183626738bSChristoph Hellwig 		goto out;
3193626738bSChristoph Hellwig 	}
3203626738bSChristoph Hellwig 
3213626738bSChristoph Hellwig 	pr_info("raid6: using algorithm %s gen() %ld MB/s\n",
3223626738bSChristoph Hellwig 		best->name,
3233626738bSChristoph Hellwig 		(bestgenperf * HZ * (disks - 2)) >>
3243626738bSChristoph Hellwig 		(20 - PAGE_SHIFT + RAID6_TIME_JIFFIES_LG2));
3253626738bSChristoph Hellwig 
3263626738bSChristoph Hellwig 	if (best->xor_syndrome) {
3273626738bSChristoph Hellwig 		perf = 0;
3283626738bSChristoph Hellwig 
3293626738bSChristoph Hellwig 		preempt_disable();
3303626738bSChristoph Hellwig 		j0 = jiffies;
3313626738bSChristoph Hellwig 		while ((j1 = jiffies) == j0)
3323626738bSChristoph Hellwig 			cpu_relax();
3333626738bSChristoph Hellwig 		while (time_before(jiffies,
3343626738bSChristoph Hellwig 				   j1 + (1 << RAID6_TIME_JIFFIES_LG2))) {
3353626738bSChristoph Hellwig 			best->xor_syndrome(disks, start, stop,
3363626738bSChristoph Hellwig 					   PAGE_SIZE, *dptrs);
3373626738bSChristoph Hellwig 			perf++;
3383626738bSChristoph Hellwig 		}
3393626738bSChristoph Hellwig 		preempt_enable();
3403626738bSChristoph Hellwig 
3413626738bSChristoph Hellwig 		pr_info("raid6: .... xor() %ld MB/s, rmw enabled\n",
3423626738bSChristoph Hellwig 			(perf * HZ * (disks - 2)) >>
3433626738bSChristoph Hellwig 			(20 - PAGE_SHIFT + RAID6_TIME_JIFFIES_LG2 + 1));
3443626738bSChristoph Hellwig 	}
3453626738bSChristoph Hellwig 
3463626738bSChristoph Hellwig out:
3473626738bSChristoph Hellwig 	return best;
3483626738bSChristoph Hellwig }
3493626738bSChristoph Hellwig 
3503626738bSChristoph Hellwig 
3513626738bSChristoph Hellwig /* Try to pick the best algorithm */
3523626738bSChristoph Hellwig /* This code uses the gfmul table as convenient data set to abuse */
3533626738bSChristoph Hellwig 
3543626738bSChristoph Hellwig static int __init raid6_select_algo(void)
3553626738bSChristoph Hellwig {
3563626738bSChristoph Hellwig 	const int disks = RAID6_TEST_DISKS;
3573626738bSChristoph Hellwig 
3583626738bSChristoph Hellwig 	const struct raid6_calls *gen_best;
3593626738bSChristoph Hellwig 	const struct raid6_recov_calls *rec_best;
3603626738bSChristoph Hellwig 	char *disk_ptr, *p;
3613626738bSChristoph Hellwig 	void *dptrs[RAID6_TEST_DISKS];
3623626738bSChristoph Hellwig 	int i, cycle;
3633626738bSChristoph Hellwig 
3643626738bSChristoph Hellwig 	/* prepare the buffer and fill it circularly with gfmul table */
3653626738bSChristoph Hellwig 	disk_ptr = (char *)__get_free_pages(GFP_KERNEL, RAID6_TEST_DISKS_ORDER);
3663626738bSChristoph Hellwig 	if (!disk_ptr) {
3673626738bSChristoph Hellwig 		pr_err("raid6: Yikes!  No memory available.\n");
3683626738bSChristoph Hellwig 		return -ENOMEM;
3693626738bSChristoph Hellwig 	}
3703626738bSChristoph Hellwig 
3713626738bSChristoph Hellwig 	p = disk_ptr;
3723626738bSChristoph Hellwig 	for (i = 0; i < disks; i++)
3733626738bSChristoph Hellwig 		dptrs[i] = p + PAGE_SIZE * i;
3743626738bSChristoph Hellwig 
3753626738bSChristoph Hellwig 	cycle = ((disks - 2) * PAGE_SIZE) / 65536;
3763626738bSChristoph Hellwig 	for (i = 0; i < cycle; i++) {
3773626738bSChristoph Hellwig 		memcpy(p, raid6_gfmul, 65536);
3783626738bSChristoph Hellwig 		p += 65536;
3793626738bSChristoph Hellwig 	}
3803626738bSChristoph Hellwig 
3813626738bSChristoph Hellwig 	if ((disks - 2) * PAGE_SIZE % 65536)
3823626738bSChristoph Hellwig 		memcpy(p, raid6_gfmul, (disks - 2) * PAGE_SIZE % 65536);
3833626738bSChristoph Hellwig 
3843626738bSChristoph Hellwig 	/* select raid gen_syndrome function */
3853626738bSChristoph Hellwig 	gen_best = raid6_choose_gen(&dptrs, disks);
3863626738bSChristoph Hellwig 
3873626738bSChristoph Hellwig 	/* select raid recover functions */
3883626738bSChristoph Hellwig 	rec_best = raid6_choose_recov();
3893626738bSChristoph Hellwig 
3903626738bSChristoph Hellwig 	free_pages((unsigned long)disk_ptr, RAID6_TEST_DISKS_ORDER);
3913626738bSChristoph Hellwig 
3923626738bSChristoph Hellwig 	return gen_best && rec_best ? 0 : -EINVAL;
3933626738bSChristoph Hellwig }
3943626738bSChristoph Hellwig 
3953626738bSChristoph Hellwig static void raid6_exit(void)
3963626738bSChristoph Hellwig {
3973626738bSChristoph Hellwig 	do { } while (0);
3983626738bSChristoph Hellwig }
3993626738bSChristoph Hellwig 
4003626738bSChristoph Hellwig subsys_initcall(raid6_select_algo);
4013626738bSChristoph Hellwig module_exit(raid6_exit);
4023626738bSChristoph Hellwig MODULE_LICENSE("GPL");
4033626738bSChristoph Hellwig MODULE_DESCRIPTION("RAID6 Q-syndrome calculations");
404