xref: /linux/lib/raid/raid6/algos.c (revision 769d603fc44f896e7f61de7f0cdb8b78d46bc8c8)
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/module.h>
153626738bSChristoph Hellwig #include <linux/gfp.h>
16*769d603fSChristoph Hellwig #include <linux/raid/pq.h>
173626738bSChristoph Hellwig #include <kunit/visibility.h>
18*769d603fSChristoph Hellwig #include "algos.h"
193626738bSChristoph Hellwig 
2035472bc6SChristoph Hellwig static const struct raid6_recov_calls *raid6_recov_algo;
2135472bc6SChristoph Hellwig 
2235472bc6SChristoph Hellwig /* Selected algorithm */
2335472bc6SChristoph Hellwig static struct raid6_calls raid6_call;
2435472bc6SChristoph Hellwig 
2535472bc6SChristoph Hellwig /**
2635472bc6SChristoph Hellwig  * raid6_gen_syndrome - generate RAID6 P/Q parity
2735472bc6SChristoph Hellwig  * @disks:	number of "disks" to operate on including parity
2835472bc6SChristoph Hellwig  * @bytes:	length in bytes of each vector
2935472bc6SChristoph Hellwig  * @ptrs:	@disks size array of memory pointers
3035472bc6SChristoph Hellwig  *
3135472bc6SChristoph Hellwig  * Generate @bytes worth of RAID6 P and Q parity in @ptrs[@disks - 2] and
3235472bc6SChristoph Hellwig  * @ptrs[@disks - 1] respectively from the memory pointed to by @ptrs[0] to
3335472bc6SChristoph Hellwig  * @ptrs[@disks - 3].
3435472bc6SChristoph Hellwig  *
3535472bc6SChristoph Hellwig  * @disks must be at least 4, and the memory pointed to by each member of @ptrs
3635472bc6SChristoph Hellwig  * must be at least 64-byte aligned.  @bytes must be non-zero and a multiple of
3735472bc6SChristoph Hellwig  * 512.
3835472bc6SChristoph Hellwig  *
3935472bc6SChristoph Hellwig  * See https://kernel.org/pub/linux/kernel/people/hpa/raid6.pdf for underlying
4035472bc6SChristoph Hellwig  * algorithm.
4135472bc6SChristoph Hellwig  */
4235472bc6SChristoph Hellwig void raid6_gen_syndrome(int disks, size_t bytes, void **ptrs)
4335472bc6SChristoph Hellwig {
4435472bc6SChristoph Hellwig 	WARN_ON_ONCE(!in_task() || irqs_disabled() || softirq_count());
4535472bc6SChristoph Hellwig 	WARN_ON_ONCE(bytes & 511);
462790045aSChristoph Hellwig 	WARN_ON_ONCE(disks < RAID6_MIN_DISKS);
4735472bc6SChristoph Hellwig 
4835472bc6SChristoph Hellwig 	raid6_call.gen_syndrome(disks, bytes, ptrs);
4935472bc6SChristoph Hellwig }
5035472bc6SChristoph Hellwig EXPORT_SYMBOL_GPL(raid6_gen_syndrome);
5135472bc6SChristoph Hellwig 
5235472bc6SChristoph Hellwig /**
5335472bc6SChristoph Hellwig  * raid6_xor_syndrome - update RAID6 P/Q parity
5435472bc6SChristoph Hellwig  * @disks:	number of "disks" to operate on including parity
5535472bc6SChristoph Hellwig  * @start:	first index into @disk to update
5635472bc6SChristoph Hellwig  * @stop:	last index into @disk to update
5735472bc6SChristoph Hellwig  * @bytes:	length in bytes of each vector
5835472bc6SChristoph Hellwig  * @ptrs:	@disks size array of memory pointers
5935472bc6SChristoph Hellwig  *
6035472bc6SChristoph Hellwig  * Update @bytes worth of RAID6 P and Q parity in @ptrs[@disks - 2] and
6135472bc6SChristoph Hellwig  * @ptrs[@disks - 1] respectively for the memory pointed to by
6235472bc6SChristoph Hellwig  * @ptrs[@start..@stop].
6335472bc6SChristoph Hellwig  *
6435472bc6SChristoph Hellwig  * This is used to update parity in place using the following sequence:
6535472bc6SChristoph Hellwig  *
6635472bc6SChristoph Hellwig  * 1) call raid6_xor_syndrome(disk, start, stop, ...) for the existing data.
6735472bc6SChristoph Hellwig  * 2) update the the data in @ptrs[@start..@stop].
6835472bc6SChristoph Hellwig  * 3) call raid6_xor_syndrome(disk, start, stop, ...) for the new data.
6935472bc6SChristoph Hellwig  *
7035472bc6SChristoph Hellwig  * Data between @start and @stop that is not changed should be filled
7135472bc6SChristoph Hellwig  * with a pointer to the kernel zero page.
7235472bc6SChristoph Hellwig  *
7335472bc6SChristoph Hellwig  * @disks must be at least 4, and the memory pointed to by each member of @ptrs
7435472bc6SChristoph Hellwig  * must be at least 64-byte aligned.  @bytes must be non-zero and a multiple of
7535472bc6SChristoph Hellwig  * 512.  @stop must be larger or equal to @start.
7635472bc6SChristoph Hellwig  */
7735472bc6SChristoph Hellwig void raid6_xor_syndrome(int disks, int start, int stop, size_t bytes,
7835472bc6SChristoph Hellwig 		void **ptrs)
7935472bc6SChristoph Hellwig {
8035472bc6SChristoph Hellwig 	WARN_ON_ONCE(!in_task() || irqs_disabled() || softirq_count());
8135472bc6SChristoph Hellwig 	WARN_ON_ONCE(bytes & 511);
822790045aSChristoph Hellwig 	WARN_ON_ONCE(disks < RAID6_MIN_DISKS);
8335472bc6SChristoph Hellwig 	WARN_ON_ONCE(stop < start);
8435472bc6SChristoph Hellwig 
8535472bc6SChristoph Hellwig 	raid6_call.xor_syndrome(disks, start, stop, bytes, ptrs);
8635472bc6SChristoph Hellwig }
8735472bc6SChristoph Hellwig EXPORT_SYMBOL_GPL(raid6_xor_syndrome);
8835472bc6SChristoph Hellwig 
8935472bc6SChristoph Hellwig /*
9035472bc6SChristoph Hellwig  * raid6_can_xor_syndrome - check if raid6_xor_syndrome() can be used
9135472bc6SChristoph Hellwig  *
9235472bc6SChristoph Hellwig  * Returns %true if raid6_can_xor_syndrome() can be used, else %false.
9335472bc6SChristoph Hellwig  */
9435472bc6SChristoph Hellwig bool raid6_can_xor_syndrome(void)
9535472bc6SChristoph Hellwig {
9635472bc6SChristoph Hellwig 	return !!raid6_call.xor_syndrome;
9735472bc6SChristoph Hellwig }
9835472bc6SChristoph Hellwig EXPORT_SYMBOL_GPL(raid6_can_xor_syndrome);
993626738bSChristoph Hellwig 
1003626738bSChristoph Hellwig const struct raid6_calls * const raid6_algos[] = {
1013626738bSChristoph Hellwig #if defined(__i386__) && !defined(__arch_um__)
1023626738bSChristoph Hellwig 	&raid6_avx512x2,
1033626738bSChristoph Hellwig 	&raid6_avx512x1,
1043626738bSChristoph Hellwig 	&raid6_avx2x2,
1053626738bSChristoph Hellwig 	&raid6_avx2x1,
1063626738bSChristoph Hellwig 	&raid6_sse2x2,
1073626738bSChristoph Hellwig 	&raid6_sse2x1,
1083626738bSChristoph Hellwig 	&raid6_sse1x2,
1093626738bSChristoph Hellwig 	&raid6_sse1x1,
1103626738bSChristoph Hellwig 	&raid6_mmxx2,
1113626738bSChristoph Hellwig 	&raid6_mmxx1,
1123626738bSChristoph Hellwig #endif
1133626738bSChristoph Hellwig #if defined(__x86_64__) && !defined(__arch_um__)
1143626738bSChristoph Hellwig 	&raid6_avx512x4,
1153626738bSChristoph Hellwig 	&raid6_avx512x2,
1163626738bSChristoph Hellwig 	&raid6_avx512x1,
1173626738bSChristoph Hellwig 	&raid6_avx2x4,
1183626738bSChristoph Hellwig 	&raid6_avx2x2,
1193626738bSChristoph Hellwig 	&raid6_avx2x1,
1203626738bSChristoph Hellwig 	&raid6_sse2x4,
1213626738bSChristoph Hellwig 	&raid6_sse2x2,
1223626738bSChristoph Hellwig 	&raid6_sse2x1,
1233626738bSChristoph Hellwig #endif
1243626738bSChristoph Hellwig #ifdef CONFIG_ALTIVEC
1253626738bSChristoph Hellwig 	&raid6_vpermxor8,
1263626738bSChristoph Hellwig 	&raid6_vpermxor4,
1273626738bSChristoph Hellwig 	&raid6_vpermxor2,
1283626738bSChristoph Hellwig 	&raid6_vpermxor1,
1293626738bSChristoph Hellwig 	&raid6_altivec8,
1303626738bSChristoph Hellwig 	&raid6_altivec4,
1313626738bSChristoph Hellwig 	&raid6_altivec2,
1323626738bSChristoph Hellwig 	&raid6_altivec1,
1333626738bSChristoph Hellwig #endif
1343626738bSChristoph Hellwig #if defined(CONFIG_S390)
1353626738bSChristoph Hellwig 	&raid6_s390vx8,
1363626738bSChristoph Hellwig #endif
1373626738bSChristoph Hellwig #ifdef CONFIG_KERNEL_MODE_NEON
1383626738bSChristoph Hellwig 	&raid6_neonx8,
1393626738bSChristoph Hellwig 	&raid6_neonx4,
1403626738bSChristoph Hellwig 	&raid6_neonx2,
1413626738bSChristoph Hellwig 	&raid6_neonx1,
1423626738bSChristoph Hellwig #endif
1433626738bSChristoph Hellwig #ifdef CONFIG_LOONGARCH
1443626738bSChristoph Hellwig #ifdef CONFIG_CPU_HAS_LASX
1453626738bSChristoph Hellwig 	&raid6_lasx,
1463626738bSChristoph Hellwig #endif
1473626738bSChristoph Hellwig #ifdef CONFIG_CPU_HAS_LSX
1483626738bSChristoph Hellwig 	&raid6_lsx,
1493626738bSChristoph Hellwig #endif
1503626738bSChristoph Hellwig #endif
1513626738bSChristoph Hellwig #ifdef CONFIG_RISCV_ISA_V
1523626738bSChristoph Hellwig 	&raid6_rvvx1,
1533626738bSChristoph Hellwig 	&raid6_rvvx2,
1543626738bSChristoph Hellwig 	&raid6_rvvx4,
1553626738bSChristoph Hellwig 	&raid6_rvvx8,
1563626738bSChristoph Hellwig #endif
1573626738bSChristoph Hellwig 	&raid6_intx8,
1583626738bSChristoph Hellwig 	&raid6_intx4,
1593626738bSChristoph Hellwig 	&raid6_intx2,
1603626738bSChristoph Hellwig 	&raid6_intx1,
1613626738bSChristoph Hellwig 	NULL
1623626738bSChristoph Hellwig };
1633626738bSChristoph Hellwig EXPORT_SYMBOL_IF_KUNIT(raid6_algos);
1643626738bSChristoph Hellwig 
16535472bc6SChristoph Hellwig /**
16635472bc6SChristoph Hellwig  * raid6_recov_2data - recover two missing data disks
16735472bc6SChristoph Hellwig  * @disks:	number of "disks" to operate on including parity
16835472bc6SChristoph Hellwig  * @bytes:	length in bytes of each vector
16935472bc6SChristoph Hellwig  * @faila:	first failed data disk index
17035472bc6SChristoph Hellwig  * @failb:	second failed data disk index
17135472bc6SChristoph Hellwig  * @ptrs:	@disks size array of memory pointers
17235472bc6SChristoph Hellwig  *
17335472bc6SChristoph Hellwig  * Rebuild @bytes of missing data in @ptrs[@faila] and @ptrs[@failb] from the
17435472bc6SChristoph Hellwig  * data in the remaining disks and the two parities pointed to by the other
17535472bc6SChristoph Hellwig  * indices between 0 and @disks - 1 in @ptrs.  @disks includes the data disks
17635472bc6SChristoph Hellwig  * and the two parities.  @faila must be smaller than @failb.
17735472bc6SChristoph Hellwig  *
17835472bc6SChristoph Hellwig  * Memory pointed to by each pointer in @ptrs must be page aligned and is
17935472bc6SChristoph Hellwig  * limited to %PAGE_SIZE.
18035472bc6SChristoph Hellwig  */
18135472bc6SChristoph Hellwig void raid6_recov_2data(int disks, size_t bytes, int faila, int failb,
18235472bc6SChristoph Hellwig 		void **ptrs)
18335472bc6SChristoph Hellwig {
18435472bc6SChristoph Hellwig 	WARN_ON_ONCE(!in_task() || irqs_disabled() || softirq_count());
18535472bc6SChristoph Hellwig 	WARN_ON_ONCE(bytes & 511);
18635472bc6SChristoph Hellwig 	WARN_ON_ONCE(bytes > PAGE_SIZE);
18735472bc6SChristoph Hellwig 	WARN_ON_ONCE(failb <= faila);
1883626738bSChristoph Hellwig 
18935472bc6SChristoph Hellwig 	raid6_recov_algo->data2(disks, bytes, faila, failb, ptrs);
19035472bc6SChristoph Hellwig }
19135472bc6SChristoph Hellwig EXPORT_SYMBOL_GPL(raid6_recov_2data);
19235472bc6SChristoph Hellwig 
19335472bc6SChristoph Hellwig /**
19435472bc6SChristoph Hellwig  * raid6_recov_datap - recover a missing data disk and missing P-parity
19535472bc6SChristoph Hellwig  * @disks:	number of "disks" to operate on including parity
19635472bc6SChristoph Hellwig  * @bytes:	length in bytes of each vector
19735472bc6SChristoph Hellwig  * @faila:	failed data disk index
19835472bc6SChristoph Hellwig  * @ptrs:	@disks size array of memory pointers
19935472bc6SChristoph Hellwig  *
20035472bc6SChristoph Hellwig  * Rebuild @bytes of missing data in @ptrs[@faila] and the missing P-parity in
20135472bc6SChristoph Hellwig  * @ptrs[@disks - 2] from the data in the remaining disks and the Q-parity
20235472bc6SChristoph Hellwig  * pointed to by the other indices between 0 and @disks - 1 in @ptrs.  @disks
20335472bc6SChristoph Hellwig  * includes the data disks and the two parities.
20435472bc6SChristoph Hellwig  *
20535472bc6SChristoph Hellwig  * Memory pointed to by each pointer in @ptrs must be page aligned and is
20635472bc6SChristoph Hellwig  * limited to %PAGE_SIZE.
20735472bc6SChristoph Hellwig  */
20835472bc6SChristoph Hellwig void raid6_recov_datap(int disks, size_t bytes, int faila, void **ptrs)
20935472bc6SChristoph Hellwig {
21035472bc6SChristoph Hellwig 	WARN_ON_ONCE(!in_task() || irqs_disabled() || softirq_count());
21135472bc6SChristoph Hellwig 	WARN_ON_ONCE(bytes & 511);
21235472bc6SChristoph Hellwig 	WARN_ON_ONCE(bytes > PAGE_SIZE);
21335472bc6SChristoph Hellwig 
21435472bc6SChristoph Hellwig 	raid6_recov_algo->datap(disks, bytes, faila, ptrs);
21535472bc6SChristoph Hellwig }
21635472bc6SChristoph Hellwig EXPORT_SYMBOL_GPL(raid6_recov_datap);
2173626738bSChristoph Hellwig 
2183626738bSChristoph Hellwig const struct raid6_recov_calls *const raid6_recov_algos[] = {
2193626738bSChristoph Hellwig #ifdef CONFIG_X86
2203626738bSChristoph Hellwig 	&raid6_recov_avx512,
2213626738bSChristoph Hellwig 	&raid6_recov_avx2,
2223626738bSChristoph Hellwig 	&raid6_recov_ssse3,
2233626738bSChristoph Hellwig #endif
2243626738bSChristoph Hellwig #ifdef CONFIG_S390
2253626738bSChristoph Hellwig 	&raid6_recov_s390xc,
2263626738bSChristoph Hellwig #endif
2273626738bSChristoph Hellwig #if defined(CONFIG_KERNEL_MODE_NEON)
2283626738bSChristoph Hellwig 	&raid6_recov_neon,
2293626738bSChristoph Hellwig #endif
2303626738bSChristoph Hellwig #ifdef CONFIG_LOONGARCH
2313626738bSChristoph Hellwig #ifdef CONFIG_CPU_HAS_LASX
2323626738bSChristoph Hellwig 	&raid6_recov_lasx,
2333626738bSChristoph Hellwig #endif
2343626738bSChristoph Hellwig #ifdef CONFIG_CPU_HAS_LSX
2353626738bSChristoph Hellwig 	&raid6_recov_lsx,
2363626738bSChristoph Hellwig #endif
2373626738bSChristoph Hellwig #endif
2383626738bSChristoph Hellwig #ifdef CONFIG_RISCV_ISA_V
2393626738bSChristoph Hellwig 	&raid6_recov_rvv,
2403626738bSChristoph Hellwig #endif
2413626738bSChristoph Hellwig 	&raid6_recov_intx1,
2423626738bSChristoph Hellwig 	NULL
2433626738bSChristoph Hellwig };
2443626738bSChristoph Hellwig EXPORT_SYMBOL_IF_KUNIT(raid6_recov_algos);
2453626738bSChristoph Hellwig 
2463626738bSChristoph Hellwig #define RAID6_TIME_JIFFIES_LG2	4
2473626738bSChristoph Hellwig #define RAID6_TEST_DISKS	8
2483626738bSChristoph Hellwig #define RAID6_TEST_DISKS_ORDER	3
2493626738bSChristoph Hellwig 
2503626738bSChristoph Hellwig static inline const struct raid6_recov_calls *raid6_choose_recov(void)
2513626738bSChristoph Hellwig {
2523626738bSChristoph Hellwig 	const struct raid6_recov_calls *const *algo;
2533626738bSChristoph Hellwig 	const struct raid6_recov_calls *best;
2543626738bSChristoph Hellwig 
2553626738bSChristoph Hellwig 	for (best = NULL, algo = raid6_recov_algos; *algo; algo++)
2563626738bSChristoph Hellwig 		if (!best || (*algo)->priority > best->priority)
2573626738bSChristoph Hellwig 			if (!(*algo)->valid || (*algo)->valid())
2583626738bSChristoph Hellwig 				best = *algo;
2593626738bSChristoph Hellwig 
2603626738bSChristoph Hellwig 	if (best) {
26135472bc6SChristoph Hellwig 		raid6_recov_algo = best;
2623626738bSChristoph Hellwig 
2633626738bSChristoph Hellwig 		pr_info("raid6: using %s recovery algorithm\n", best->name);
2643626738bSChristoph Hellwig 	} else
2653626738bSChristoph Hellwig 		pr_err("raid6: Yikes! No recovery algorithm found!\n");
2663626738bSChristoph Hellwig 
2673626738bSChristoph Hellwig 	return best;
2683626738bSChristoph Hellwig }
2693626738bSChristoph Hellwig 
2703626738bSChristoph Hellwig static inline const struct raid6_calls *raid6_choose_gen(
2713626738bSChristoph Hellwig 	void *(*const dptrs)[RAID6_TEST_DISKS], const int disks)
2723626738bSChristoph Hellwig {
2733626738bSChristoph Hellwig 	unsigned long perf, bestgenperf, j0, j1;
2743626738bSChristoph Hellwig 	int start = (disks>>1)-1, stop = disks-3;	/* work on the second half of the disks */
2753626738bSChristoph Hellwig 	const struct raid6_calls *const *algo;
2763626738bSChristoph Hellwig 	const struct raid6_calls *best;
2773626738bSChristoph Hellwig 
2783626738bSChristoph Hellwig 	for (bestgenperf = 0, best = NULL, algo = raid6_algos; *algo; algo++) {
2793626738bSChristoph Hellwig 		if (!best || (*algo)->priority >= best->priority) {
2803626738bSChristoph Hellwig 			if ((*algo)->valid && !(*algo)->valid())
2813626738bSChristoph Hellwig 				continue;
2823626738bSChristoph Hellwig 
2833626738bSChristoph Hellwig 			if (!IS_ENABLED(CONFIG_RAID6_PQ_BENCHMARK)) {
2843626738bSChristoph Hellwig 				best = *algo;
2853626738bSChristoph Hellwig 				break;
2863626738bSChristoph Hellwig 			}
2873626738bSChristoph Hellwig 
2883626738bSChristoph Hellwig 			perf = 0;
2893626738bSChristoph Hellwig 
2903626738bSChristoph Hellwig 			preempt_disable();
2913626738bSChristoph Hellwig 			j0 = jiffies;
2923626738bSChristoph Hellwig 			while ((j1 = jiffies) == j0)
2933626738bSChristoph Hellwig 				cpu_relax();
2943626738bSChristoph Hellwig 			while (time_before(jiffies,
2953626738bSChristoph Hellwig 					    j1 + (1<<RAID6_TIME_JIFFIES_LG2))) {
2963626738bSChristoph Hellwig 				(*algo)->gen_syndrome(disks, PAGE_SIZE, *dptrs);
2973626738bSChristoph Hellwig 				perf++;
2983626738bSChristoph Hellwig 			}
2993626738bSChristoph Hellwig 			preempt_enable();
3003626738bSChristoph Hellwig 
3013626738bSChristoph Hellwig 			if (perf > bestgenperf) {
3023626738bSChristoph Hellwig 				bestgenperf = perf;
3033626738bSChristoph Hellwig 				best = *algo;
3043626738bSChristoph Hellwig 			}
3053626738bSChristoph Hellwig 			pr_info("raid6: %-8s gen() %5ld MB/s\n", (*algo)->name,
3063626738bSChristoph Hellwig 				(perf * HZ * (disks-2)) >>
3073626738bSChristoph Hellwig 				(20 - PAGE_SHIFT + RAID6_TIME_JIFFIES_LG2));
3083626738bSChristoph Hellwig 		}
3093626738bSChristoph Hellwig 	}
3103626738bSChristoph Hellwig 
3113626738bSChristoph Hellwig 	if (!best) {
3123626738bSChristoph Hellwig 		pr_err("raid6: Yikes! No algorithm found!\n");
3133626738bSChristoph Hellwig 		goto out;
3143626738bSChristoph Hellwig 	}
3153626738bSChristoph Hellwig 
3163626738bSChristoph Hellwig 	raid6_call = *best;
3173626738bSChristoph Hellwig 
3183626738bSChristoph Hellwig 	if (!IS_ENABLED(CONFIG_RAID6_PQ_BENCHMARK)) {
3193626738bSChristoph Hellwig 		pr_info("raid6: skipped pq benchmark and selected %s\n",
3203626738bSChristoph Hellwig 			best->name);
3213626738bSChristoph Hellwig 		goto out;
3223626738bSChristoph Hellwig 	}
3233626738bSChristoph Hellwig 
3243626738bSChristoph Hellwig 	pr_info("raid6: using algorithm %s gen() %ld MB/s\n",
3253626738bSChristoph Hellwig 		best->name,
3263626738bSChristoph Hellwig 		(bestgenperf * HZ * (disks - 2)) >>
3273626738bSChristoph Hellwig 		(20 - PAGE_SHIFT + RAID6_TIME_JIFFIES_LG2));
3283626738bSChristoph Hellwig 
3293626738bSChristoph Hellwig 	if (best->xor_syndrome) {
3303626738bSChristoph Hellwig 		perf = 0;
3313626738bSChristoph Hellwig 
3323626738bSChristoph Hellwig 		preempt_disable();
3333626738bSChristoph Hellwig 		j0 = jiffies;
3343626738bSChristoph Hellwig 		while ((j1 = jiffies) == j0)
3353626738bSChristoph Hellwig 			cpu_relax();
3363626738bSChristoph Hellwig 		while (time_before(jiffies,
3373626738bSChristoph Hellwig 				   j1 + (1 << RAID6_TIME_JIFFIES_LG2))) {
3383626738bSChristoph Hellwig 			best->xor_syndrome(disks, start, stop,
3393626738bSChristoph Hellwig 					   PAGE_SIZE, *dptrs);
3403626738bSChristoph Hellwig 			perf++;
3413626738bSChristoph Hellwig 		}
3423626738bSChristoph Hellwig 		preempt_enable();
3433626738bSChristoph Hellwig 
3443626738bSChristoph Hellwig 		pr_info("raid6: .... xor() %ld MB/s, rmw enabled\n",
3453626738bSChristoph Hellwig 			(perf * HZ * (disks - 2)) >>
3463626738bSChristoph Hellwig 			(20 - PAGE_SHIFT + RAID6_TIME_JIFFIES_LG2 + 1));
3473626738bSChristoph Hellwig 	}
3483626738bSChristoph Hellwig 
3493626738bSChristoph Hellwig out:
3503626738bSChristoph Hellwig 	return best;
3513626738bSChristoph Hellwig }
3523626738bSChristoph Hellwig 
3533626738bSChristoph Hellwig 
3543626738bSChristoph Hellwig /* Try to pick the best algorithm */
3553626738bSChristoph Hellwig /* This code uses the gfmul table as convenient data set to abuse */
3563626738bSChristoph Hellwig 
3573626738bSChristoph Hellwig static int __init raid6_select_algo(void)
3583626738bSChristoph Hellwig {
3593626738bSChristoph Hellwig 	const int disks = RAID6_TEST_DISKS;
3603626738bSChristoph Hellwig 
3613626738bSChristoph Hellwig 	const struct raid6_calls *gen_best;
3623626738bSChristoph Hellwig 	const struct raid6_recov_calls *rec_best;
3633626738bSChristoph Hellwig 	char *disk_ptr, *p;
3643626738bSChristoph Hellwig 	void *dptrs[RAID6_TEST_DISKS];
3653626738bSChristoph Hellwig 	int i, cycle;
3663626738bSChristoph Hellwig 
3673626738bSChristoph Hellwig 	/* prepare the buffer and fill it circularly with gfmul table */
3683626738bSChristoph Hellwig 	disk_ptr = (char *)__get_free_pages(GFP_KERNEL, RAID6_TEST_DISKS_ORDER);
3693626738bSChristoph Hellwig 	if (!disk_ptr) {
3703626738bSChristoph Hellwig 		pr_err("raid6: Yikes!  No memory available.\n");
3713626738bSChristoph Hellwig 		return -ENOMEM;
3723626738bSChristoph Hellwig 	}
3733626738bSChristoph Hellwig 
3743626738bSChristoph Hellwig 	p = disk_ptr;
3753626738bSChristoph Hellwig 	for (i = 0; i < disks; i++)
3763626738bSChristoph Hellwig 		dptrs[i] = p + PAGE_SIZE * i;
3773626738bSChristoph Hellwig 
3783626738bSChristoph Hellwig 	cycle = ((disks - 2) * PAGE_SIZE) / 65536;
3793626738bSChristoph Hellwig 	for (i = 0; i < cycle; i++) {
3803626738bSChristoph Hellwig 		memcpy(p, raid6_gfmul, 65536);
3813626738bSChristoph Hellwig 		p += 65536;
3823626738bSChristoph Hellwig 	}
3833626738bSChristoph Hellwig 
3843626738bSChristoph Hellwig 	if ((disks - 2) * PAGE_SIZE % 65536)
3853626738bSChristoph Hellwig 		memcpy(p, raid6_gfmul, (disks - 2) * PAGE_SIZE % 65536);
3863626738bSChristoph Hellwig 
3873626738bSChristoph Hellwig 	/* select raid gen_syndrome function */
3883626738bSChristoph Hellwig 	gen_best = raid6_choose_gen(&dptrs, disks);
3893626738bSChristoph Hellwig 
3903626738bSChristoph Hellwig 	/* select raid recover functions */
3913626738bSChristoph Hellwig 	rec_best = raid6_choose_recov();
3923626738bSChristoph Hellwig 
3933626738bSChristoph Hellwig 	free_pages((unsigned long)disk_ptr, RAID6_TEST_DISKS_ORDER);
3943626738bSChristoph Hellwig 
3953626738bSChristoph Hellwig 	return gen_best && rec_best ? 0 : -EINVAL;
3963626738bSChristoph Hellwig }
3973626738bSChristoph Hellwig 
3983626738bSChristoph Hellwig static void raid6_exit(void)
3993626738bSChristoph Hellwig {
4003626738bSChristoph Hellwig 	do { } while (0);
4013626738bSChristoph Hellwig }
4023626738bSChristoph Hellwig 
4033626738bSChristoph Hellwig subsys_initcall(raid6_select_algo);
4043626738bSChristoph Hellwig module_exit(raid6_exit);
4053626738bSChristoph Hellwig MODULE_LICENSE("GPL");
4063626738bSChristoph Hellwig MODULE_DESCRIPTION("RAID6 Q-syndrome calculations");
407