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