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