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