xref: /OK3568_Linux_fs/kernel/lib/raid6/algos.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /* -*- linux-c -*- ------------------------------------------------------- *
3*4882a593Smuzhiyun  *
4*4882a593Smuzhiyun  *   Copyright 2002 H. Peter Anvin - All Rights Reserved
5*4882a593Smuzhiyun  *
6*4882a593Smuzhiyun  * ----------------------------------------------------------------------- */
7*4882a593Smuzhiyun 
8*4882a593Smuzhiyun /*
9*4882a593Smuzhiyun  * raid6/algos.c
10*4882a593Smuzhiyun  *
11*4882a593Smuzhiyun  * Algorithm list and algorithm selection for RAID-6
12*4882a593Smuzhiyun  */
13*4882a593Smuzhiyun 
14*4882a593Smuzhiyun #include <linux/raid/pq.h>
15*4882a593Smuzhiyun #ifndef __KERNEL__
16*4882a593Smuzhiyun #include <sys/mman.h>
17*4882a593Smuzhiyun #include <stdio.h>
18*4882a593Smuzhiyun #else
19*4882a593Smuzhiyun #include <linux/module.h>
20*4882a593Smuzhiyun #include <linux/gfp.h>
21*4882a593Smuzhiyun #if !RAID6_USE_EMPTY_ZERO_PAGE
22*4882a593Smuzhiyun /* In .bss so it's zeroed */
23*4882a593Smuzhiyun const char raid6_empty_zero_page[PAGE_SIZE] __attribute__((aligned(256)));
24*4882a593Smuzhiyun EXPORT_SYMBOL(raid6_empty_zero_page);
25*4882a593Smuzhiyun #endif
26*4882a593Smuzhiyun #endif
27*4882a593Smuzhiyun 
28*4882a593Smuzhiyun struct raid6_calls raid6_call;
29*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(raid6_call);
30*4882a593Smuzhiyun 
31*4882a593Smuzhiyun const struct raid6_calls * const raid6_algos[] = {
32*4882a593Smuzhiyun #if defined(__i386__) && !defined(__arch_um__)
33*4882a593Smuzhiyun #ifdef CONFIG_AS_AVX512
34*4882a593Smuzhiyun 	&raid6_avx512x2,
35*4882a593Smuzhiyun 	&raid6_avx512x1,
36*4882a593Smuzhiyun #endif
37*4882a593Smuzhiyun 	&raid6_avx2x2,
38*4882a593Smuzhiyun 	&raid6_avx2x1,
39*4882a593Smuzhiyun 	&raid6_sse2x2,
40*4882a593Smuzhiyun 	&raid6_sse2x1,
41*4882a593Smuzhiyun 	&raid6_sse1x2,
42*4882a593Smuzhiyun 	&raid6_sse1x1,
43*4882a593Smuzhiyun 	&raid6_mmxx2,
44*4882a593Smuzhiyun 	&raid6_mmxx1,
45*4882a593Smuzhiyun #endif
46*4882a593Smuzhiyun #if defined(__x86_64__) && !defined(__arch_um__)
47*4882a593Smuzhiyun #ifdef CONFIG_AS_AVX512
48*4882a593Smuzhiyun 	&raid6_avx512x4,
49*4882a593Smuzhiyun 	&raid6_avx512x2,
50*4882a593Smuzhiyun 	&raid6_avx512x1,
51*4882a593Smuzhiyun #endif
52*4882a593Smuzhiyun 	&raid6_avx2x4,
53*4882a593Smuzhiyun 	&raid6_avx2x2,
54*4882a593Smuzhiyun 	&raid6_avx2x1,
55*4882a593Smuzhiyun 	&raid6_sse2x4,
56*4882a593Smuzhiyun 	&raid6_sse2x2,
57*4882a593Smuzhiyun 	&raid6_sse2x1,
58*4882a593Smuzhiyun #endif
59*4882a593Smuzhiyun #ifdef CONFIG_ALTIVEC
60*4882a593Smuzhiyun 	&raid6_vpermxor8,
61*4882a593Smuzhiyun 	&raid6_vpermxor4,
62*4882a593Smuzhiyun 	&raid6_vpermxor2,
63*4882a593Smuzhiyun 	&raid6_vpermxor1,
64*4882a593Smuzhiyun 	&raid6_altivec8,
65*4882a593Smuzhiyun 	&raid6_altivec4,
66*4882a593Smuzhiyun 	&raid6_altivec2,
67*4882a593Smuzhiyun 	&raid6_altivec1,
68*4882a593Smuzhiyun #endif
69*4882a593Smuzhiyun #if defined(CONFIG_S390)
70*4882a593Smuzhiyun 	&raid6_s390vx8,
71*4882a593Smuzhiyun #endif
72*4882a593Smuzhiyun #ifdef CONFIG_KERNEL_MODE_NEON
73*4882a593Smuzhiyun 	&raid6_neonx8,
74*4882a593Smuzhiyun 	&raid6_neonx4,
75*4882a593Smuzhiyun 	&raid6_neonx2,
76*4882a593Smuzhiyun 	&raid6_neonx1,
77*4882a593Smuzhiyun #endif
78*4882a593Smuzhiyun #if defined(__ia64__)
79*4882a593Smuzhiyun 	&raid6_intx32,
80*4882a593Smuzhiyun 	&raid6_intx16,
81*4882a593Smuzhiyun #endif
82*4882a593Smuzhiyun 	&raid6_intx8,
83*4882a593Smuzhiyun 	&raid6_intx4,
84*4882a593Smuzhiyun 	&raid6_intx2,
85*4882a593Smuzhiyun 	&raid6_intx1,
86*4882a593Smuzhiyun 	NULL
87*4882a593Smuzhiyun };
88*4882a593Smuzhiyun 
89*4882a593Smuzhiyun void (*raid6_2data_recov)(int, size_t, int, int, void **);
90*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(raid6_2data_recov);
91*4882a593Smuzhiyun 
92*4882a593Smuzhiyun void (*raid6_datap_recov)(int, size_t, int, void **);
93*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(raid6_datap_recov);
94*4882a593Smuzhiyun 
95*4882a593Smuzhiyun const struct raid6_recov_calls *const raid6_recov_algos[] = {
96*4882a593Smuzhiyun #ifdef CONFIG_X86
97*4882a593Smuzhiyun #ifdef CONFIG_AS_AVX512
98*4882a593Smuzhiyun 	&raid6_recov_avx512,
99*4882a593Smuzhiyun #endif
100*4882a593Smuzhiyun 	&raid6_recov_avx2,
101*4882a593Smuzhiyun 	&raid6_recov_ssse3,
102*4882a593Smuzhiyun #endif
103*4882a593Smuzhiyun #ifdef CONFIG_S390
104*4882a593Smuzhiyun 	&raid6_recov_s390xc,
105*4882a593Smuzhiyun #endif
106*4882a593Smuzhiyun #if defined(CONFIG_KERNEL_MODE_NEON)
107*4882a593Smuzhiyun 	&raid6_recov_neon,
108*4882a593Smuzhiyun #endif
109*4882a593Smuzhiyun 	&raid6_recov_intx1,
110*4882a593Smuzhiyun 	NULL
111*4882a593Smuzhiyun };
112*4882a593Smuzhiyun 
113*4882a593Smuzhiyun #ifdef __KERNEL__
114*4882a593Smuzhiyun #define RAID6_TIME_JIFFIES_LG2	4
115*4882a593Smuzhiyun #else
116*4882a593Smuzhiyun /* Need more time to be stable in userspace */
117*4882a593Smuzhiyun #define RAID6_TIME_JIFFIES_LG2	9
118*4882a593Smuzhiyun #define time_before(x, y) ((x) < (y))
119*4882a593Smuzhiyun #endif
120*4882a593Smuzhiyun 
121*4882a593Smuzhiyun #define RAID6_TEST_DISKS	8
122*4882a593Smuzhiyun #define RAID6_TEST_DISKS_ORDER	3
123*4882a593Smuzhiyun 
raid6_choose_recov(void)124*4882a593Smuzhiyun static inline const struct raid6_recov_calls *raid6_choose_recov(void)
125*4882a593Smuzhiyun {
126*4882a593Smuzhiyun 	const struct raid6_recov_calls *const *algo;
127*4882a593Smuzhiyun 	const struct raid6_recov_calls *best;
128*4882a593Smuzhiyun 
129*4882a593Smuzhiyun 	for (best = NULL, algo = raid6_recov_algos; *algo; algo++)
130*4882a593Smuzhiyun 		if (!best || (*algo)->priority > best->priority)
131*4882a593Smuzhiyun 			if (!(*algo)->valid || (*algo)->valid())
132*4882a593Smuzhiyun 				best = *algo;
133*4882a593Smuzhiyun 
134*4882a593Smuzhiyun 	if (best) {
135*4882a593Smuzhiyun 		raid6_2data_recov = best->data2;
136*4882a593Smuzhiyun 		raid6_datap_recov = best->datap;
137*4882a593Smuzhiyun 
138*4882a593Smuzhiyun 		pr_info("raid6: using %s recovery algorithm\n", best->name);
139*4882a593Smuzhiyun 	} else
140*4882a593Smuzhiyun 		pr_err("raid6: Yikes! No recovery algorithm found!\n");
141*4882a593Smuzhiyun 
142*4882a593Smuzhiyun 	return best;
143*4882a593Smuzhiyun }
144*4882a593Smuzhiyun 
raid6_choose_gen(void * (* const dptrs)[RAID6_TEST_DISKS],const int disks)145*4882a593Smuzhiyun static inline const struct raid6_calls *raid6_choose_gen(
146*4882a593Smuzhiyun 	void *(*const dptrs)[RAID6_TEST_DISKS], const int disks)
147*4882a593Smuzhiyun {
148*4882a593Smuzhiyun 	unsigned long perf, bestgenperf, bestxorperf, j0, j1;
149*4882a593Smuzhiyun 	int start = (disks>>1)-1, stop = disks-3;	/* work on the second half of the disks */
150*4882a593Smuzhiyun 	const struct raid6_calls *const *algo;
151*4882a593Smuzhiyun 	const struct raid6_calls *best;
152*4882a593Smuzhiyun 
153*4882a593Smuzhiyun 	for (bestgenperf = 0, bestxorperf = 0, best = NULL, algo = raid6_algos; *algo; algo++) {
154*4882a593Smuzhiyun 		if (!best || (*algo)->prefer >= best->prefer) {
155*4882a593Smuzhiyun 			if ((*algo)->valid && !(*algo)->valid())
156*4882a593Smuzhiyun 				continue;
157*4882a593Smuzhiyun 
158*4882a593Smuzhiyun 			if (!IS_ENABLED(CONFIG_RAID6_PQ_BENCHMARK)) {
159*4882a593Smuzhiyun 				best = *algo;
160*4882a593Smuzhiyun 				break;
161*4882a593Smuzhiyun 			}
162*4882a593Smuzhiyun 
163*4882a593Smuzhiyun 			perf = 0;
164*4882a593Smuzhiyun 
165*4882a593Smuzhiyun 			preempt_disable();
166*4882a593Smuzhiyun 			j0 = jiffies;
167*4882a593Smuzhiyun 			while ((j1 = jiffies) == j0)
168*4882a593Smuzhiyun 				cpu_relax();
169*4882a593Smuzhiyun 			while (time_before(jiffies,
170*4882a593Smuzhiyun 					    j1 + (1<<RAID6_TIME_JIFFIES_LG2))) {
171*4882a593Smuzhiyun 				(*algo)->gen_syndrome(disks, PAGE_SIZE, *dptrs);
172*4882a593Smuzhiyun 				perf++;
173*4882a593Smuzhiyun 			}
174*4882a593Smuzhiyun 			preempt_enable();
175*4882a593Smuzhiyun 
176*4882a593Smuzhiyun 			if (perf > bestgenperf) {
177*4882a593Smuzhiyun 				bestgenperf = perf;
178*4882a593Smuzhiyun 				best = *algo;
179*4882a593Smuzhiyun 			}
180*4882a593Smuzhiyun 			pr_info("raid6: %-8s gen() %5ld MB/s\n", (*algo)->name,
181*4882a593Smuzhiyun 				(perf * HZ * (disks-2)) >>
182*4882a593Smuzhiyun 				(20 - PAGE_SHIFT + RAID6_TIME_JIFFIES_LG2));
183*4882a593Smuzhiyun 
184*4882a593Smuzhiyun 			if (!(*algo)->xor_syndrome)
185*4882a593Smuzhiyun 				continue;
186*4882a593Smuzhiyun 
187*4882a593Smuzhiyun 			perf = 0;
188*4882a593Smuzhiyun 
189*4882a593Smuzhiyun 			preempt_disable();
190*4882a593Smuzhiyun 			j0 = jiffies;
191*4882a593Smuzhiyun 			while ((j1 = jiffies) == j0)
192*4882a593Smuzhiyun 				cpu_relax();
193*4882a593Smuzhiyun 			while (time_before(jiffies,
194*4882a593Smuzhiyun 					    j1 + (1<<RAID6_TIME_JIFFIES_LG2))) {
195*4882a593Smuzhiyun 				(*algo)->xor_syndrome(disks, start, stop,
196*4882a593Smuzhiyun 						      PAGE_SIZE, *dptrs);
197*4882a593Smuzhiyun 				perf++;
198*4882a593Smuzhiyun 			}
199*4882a593Smuzhiyun 			preempt_enable();
200*4882a593Smuzhiyun 
201*4882a593Smuzhiyun 			if (best == *algo)
202*4882a593Smuzhiyun 				bestxorperf = perf;
203*4882a593Smuzhiyun 
204*4882a593Smuzhiyun 			pr_info("raid6: %-8s xor() %5ld MB/s\n", (*algo)->name,
205*4882a593Smuzhiyun 				(perf * HZ * (disks-2)) >>
206*4882a593Smuzhiyun 				(20 - PAGE_SHIFT + RAID6_TIME_JIFFIES_LG2 + 1));
207*4882a593Smuzhiyun 		}
208*4882a593Smuzhiyun 	}
209*4882a593Smuzhiyun 
210*4882a593Smuzhiyun 	if (best) {
211*4882a593Smuzhiyun 		if (IS_ENABLED(CONFIG_RAID6_PQ_BENCHMARK)) {
212*4882a593Smuzhiyun 			pr_info("raid6: using algorithm %s gen() %ld MB/s\n",
213*4882a593Smuzhiyun 				best->name,
214*4882a593Smuzhiyun 				(bestgenperf * HZ * (disks-2)) >>
215*4882a593Smuzhiyun 				(20 - PAGE_SHIFT+RAID6_TIME_JIFFIES_LG2));
216*4882a593Smuzhiyun 			if (best->xor_syndrome)
217*4882a593Smuzhiyun 				pr_info("raid6: .... xor() %ld MB/s, rmw enabled\n",
218*4882a593Smuzhiyun 					(bestxorperf * HZ * (disks-2)) >>
219*4882a593Smuzhiyun 					(20 - PAGE_SHIFT + RAID6_TIME_JIFFIES_LG2 + 1));
220*4882a593Smuzhiyun 		} else
221*4882a593Smuzhiyun 			pr_info("raid6: skip pq benchmark and using algorithm %s\n",
222*4882a593Smuzhiyun 				best->name);
223*4882a593Smuzhiyun 		raid6_call = *best;
224*4882a593Smuzhiyun 	} else
225*4882a593Smuzhiyun 		pr_err("raid6: Yikes!  No algorithm found!\n");
226*4882a593Smuzhiyun 
227*4882a593Smuzhiyun 	return best;
228*4882a593Smuzhiyun }
229*4882a593Smuzhiyun 
230*4882a593Smuzhiyun 
231*4882a593Smuzhiyun /* Try to pick the best algorithm */
232*4882a593Smuzhiyun /* This code uses the gfmul table as convenient data set to abuse */
233*4882a593Smuzhiyun 
raid6_select_algo(void)234*4882a593Smuzhiyun int __init raid6_select_algo(void)
235*4882a593Smuzhiyun {
236*4882a593Smuzhiyun 	const int disks = RAID6_TEST_DISKS;
237*4882a593Smuzhiyun 
238*4882a593Smuzhiyun 	const struct raid6_calls *gen_best;
239*4882a593Smuzhiyun 	const struct raid6_recov_calls *rec_best;
240*4882a593Smuzhiyun 	char *disk_ptr, *p;
241*4882a593Smuzhiyun 	void *dptrs[RAID6_TEST_DISKS];
242*4882a593Smuzhiyun 	int i, cycle;
243*4882a593Smuzhiyun 
244*4882a593Smuzhiyun 	/* prepare the buffer and fill it circularly with gfmul table */
245*4882a593Smuzhiyun 	disk_ptr = (char *)__get_free_pages(GFP_KERNEL, RAID6_TEST_DISKS_ORDER);
246*4882a593Smuzhiyun 	if (!disk_ptr) {
247*4882a593Smuzhiyun 		pr_err("raid6: Yikes!  No memory available.\n");
248*4882a593Smuzhiyun 		return -ENOMEM;
249*4882a593Smuzhiyun 	}
250*4882a593Smuzhiyun 
251*4882a593Smuzhiyun 	p = disk_ptr;
252*4882a593Smuzhiyun 	for (i = 0; i < disks; i++)
253*4882a593Smuzhiyun 		dptrs[i] = p + PAGE_SIZE * i;
254*4882a593Smuzhiyun 
255*4882a593Smuzhiyun 	cycle = ((disks - 2) * PAGE_SIZE) / 65536;
256*4882a593Smuzhiyun 	for (i = 0; i < cycle; i++) {
257*4882a593Smuzhiyun 		memcpy(p, raid6_gfmul, 65536);
258*4882a593Smuzhiyun 		p += 65536;
259*4882a593Smuzhiyun 	}
260*4882a593Smuzhiyun 
261*4882a593Smuzhiyun 	if ((disks - 2) * PAGE_SIZE % 65536)
262*4882a593Smuzhiyun 		memcpy(p, raid6_gfmul, (disks - 2) * PAGE_SIZE % 65536);
263*4882a593Smuzhiyun 
264*4882a593Smuzhiyun 	/* select raid gen_syndrome function */
265*4882a593Smuzhiyun 	gen_best = raid6_choose_gen(&dptrs, disks);
266*4882a593Smuzhiyun 
267*4882a593Smuzhiyun 	/* select raid recover functions */
268*4882a593Smuzhiyun 	rec_best = raid6_choose_recov();
269*4882a593Smuzhiyun 
270*4882a593Smuzhiyun 	free_pages((unsigned long)disk_ptr, RAID6_TEST_DISKS_ORDER);
271*4882a593Smuzhiyun 
272*4882a593Smuzhiyun 	return gen_best && rec_best ? 0 : -EINVAL;
273*4882a593Smuzhiyun }
274*4882a593Smuzhiyun 
raid6_exit(void)275*4882a593Smuzhiyun static void raid6_exit(void)
276*4882a593Smuzhiyun {
277*4882a593Smuzhiyun 	do { } while (0);
278*4882a593Smuzhiyun }
279*4882a593Smuzhiyun 
280*4882a593Smuzhiyun subsys_initcall(raid6_select_algo);
281*4882a593Smuzhiyun module_exit(raid6_exit);
282*4882a593Smuzhiyun MODULE_LICENSE("GPL");
283*4882a593Smuzhiyun MODULE_DESCRIPTION("RAID6 Q-syndrome calculations");
284