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