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/module.h> 153626738bSChristoph Hellwig #include <linux/gfp.h> 16769d603fSChristoph Hellwig #include <linux/raid/pq.h> 17*10f4b8e2SChristoph Hellwig #include <linux/static_call.h> 183626738bSChristoph Hellwig #include <kunit/visibility.h> 19769d603fSChristoph Hellwig #include "algos.h" 203626738bSChristoph Hellwig 21adfcf6e8SChristoph Hellwig #define RAID6_MAX_ALGOS 16 22adfcf6e8SChristoph Hellwig static const struct raid6_calls *raid6_algos[RAID6_MAX_ALGOS]; 23adfcf6e8SChristoph Hellwig static unsigned int raid6_nr_algos; 2435472bc6SChristoph Hellwig static const struct raid6_recov_calls *raid6_recov_algo; 2535472bc6SChristoph Hellwig 2635472bc6SChristoph Hellwig /* Selected algorithm */ 27*10f4b8e2SChristoph Hellwig DEFINE_STATIC_CALL_NULL(raid6_gen_syndrome_impl, *raid6_intx1.gen_syndrome); 28*10f4b8e2SChristoph Hellwig DEFINE_STATIC_CALL_NULL(raid6_xor_syndrome_impl, *raid6_intx1.xor_syndrome); 2935472bc6SChristoph Hellwig 3035472bc6SChristoph Hellwig /** 3135472bc6SChristoph Hellwig * raid6_gen_syndrome - generate RAID6 P/Q parity 3235472bc6SChristoph Hellwig * @disks: number of "disks" to operate on including parity 3335472bc6SChristoph Hellwig * @bytes: length in bytes of each vector 3435472bc6SChristoph Hellwig * @ptrs: @disks size array of memory pointers 3535472bc6SChristoph Hellwig * 3635472bc6SChristoph Hellwig * Generate @bytes worth of RAID6 P and Q parity in @ptrs[@disks - 2] and 3735472bc6SChristoph Hellwig * @ptrs[@disks - 1] respectively from the memory pointed to by @ptrs[0] to 3835472bc6SChristoph Hellwig * @ptrs[@disks - 3]. 3935472bc6SChristoph Hellwig * 4035472bc6SChristoph Hellwig * @disks must be at least 4, and the memory pointed to by each member of @ptrs 4135472bc6SChristoph Hellwig * must be at least 64-byte aligned. @bytes must be non-zero and a multiple of 4235472bc6SChristoph Hellwig * 512. 4335472bc6SChristoph Hellwig * 4435472bc6SChristoph Hellwig * See https://kernel.org/pub/linux/kernel/people/hpa/raid6.pdf for underlying 4535472bc6SChristoph Hellwig * algorithm. 4635472bc6SChristoph Hellwig */ 4735472bc6SChristoph Hellwig void raid6_gen_syndrome(int disks, size_t bytes, void **ptrs) 4835472bc6SChristoph Hellwig { 4935472bc6SChristoph Hellwig WARN_ON_ONCE(!in_task() || irqs_disabled() || softirq_count()); 5035472bc6SChristoph Hellwig WARN_ON_ONCE(bytes & 511); 512790045aSChristoph Hellwig WARN_ON_ONCE(disks < RAID6_MIN_DISKS); 5235472bc6SChristoph Hellwig 53*10f4b8e2SChristoph Hellwig static_call(raid6_gen_syndrome_impl)(disks, bytes, ptrs); 5435472bc6SChristoph Hellwig } 5535472bc6SChristoph Hellwig EXPORT_SYMBOL_GPL(raid6_gen_syndrome); 5635472bc6SChristoph Hellwig 5735472bc6SChristoph Hellwig /** 5835472bc6SChristoph Hellwig * raid6_xor_syndrome - update RAID6 P/Q parity 5935472bc6SChristoph Hellwig * @disks: number of "disks" to operate on including parity 6035472bc6SChristoph Hellwig * @start: first index into @disk to update 6135472bc6SChristoph Hellwig * @stop: last index into @disk to update 6235472bc6SChristoph Hellwig * @bytes: length in bytes of each vector 6335472bc6SChristoph Hellwig * @ptrs: @disks size array of memory pointers 6435472bc6SChristoph Hellwig * 6535472bc6SChristoph Hellwig * Update @bytes worth of RAID6 P and Q parity in @ptrs[@disks - 2] and 6635472bc6SChristoph Hellwig * @ptrs[@disks - 1] respectively for the memory pointed to by 6735472bc6SChristoph Hellwig * @ptrs[@start..@stop]. 6835472bc6SChristoph Hellwig * 6935472bc6SChristoph Hellwig * This is used to update parity in place using the following sequence: 7035472bc6SChristoph Hellwig * 7135472bc6SChristoph Hellwig * 1) call raid6_xor_syndrome(disk, start, stop, ...) for the existing data. 7235472bc6SChristoph Hellwig * 2) update the the data in @ptrs[@start..@stop]. 7335472bc6SChristoph Hellwig * 3) call raid6_xor_syndrome(disk, start, stop, ...) for the new data. 7435472bc6SChristoph Hellwig * 7535472bc6SChristoph Hellwig * Data between @start and @stop that is not changed should be filled 7635472bc6SChristoph Hellwig * with a pointer to the kernel zero page. 7735472bc6SChristoph Hellwig * 7835472bc6SChristoph Hellwig * @disks must be at least 4, and the memory pointed to by each member of @ptrs 7935472bc6SChristoph Hellwig * must be at least 64-byte aligned. @bytes must be non-zero and a multiple of 8035472bc6SChristoph Hellwig * 512. @stop must be larger or equal to @start. 8135472bc6SChristoph Hellwig */ 8235472bc6SChristoph Hellwig void raid6_xor_syndrome(int disks, int start, int stop, size_t bytes, 8335472bc6SChristoph Hellwig void **ptrs) 8435472bc6SChristoph Hellwig { 8535472bc6SChristoph Hellwig WARN_ON_ONCE(!in_task() || irqs_disabled() || softirq_count()); 8635472bc6SChristoph Hellwig WARN_ON_ONCE(bytes & 511); 872790045aSChristoph Hellwig WARN_ON_ONCE(disks < RAID6_MIN_DISKS); 8835472bc6SChristoph Hellwig WARN_ON_ONCE(stop < start); 8935472bc6SChristoph Hellwig 90*10f4b8e2SChristoph Hellwig static_call(raid6_xor_syndrome_impl)(disks, start, stop, bytes, ptrs); 9135472bc6SChristoph Hellwig } 9235472bc6SChristoph Hellwig EXPORT_SYMBOL_GPL(raid6_xor_syndrome); 9335472bc6SChristoph Hellwig 9435472bc6SChristoph Hellwig /* 9535472bc6SChristoph Hellwig * raid6_can_xor_syndrome - check if raid6_xor_syndrome() can be used 9635472bc6SChristoph Hellwig * 9735472bc6SChristoph Hellwig * Returns %true if raid6_can_xor_syndrome() can be used, else %false. 9835472bc6SChristoph Hellwig */ 9935472bc6SChristoph Hellwig bool raid6_can_xor_syndrome(void) 10035472bc6SChristoph Hellwig { 101*10f4b8e2SChristoph Hellwig return !!static_call_query(raid6_xor_syndrome_impl); 10235472bc6SChristoph Hellwig } 10335472bc6SChristoph Hellwig EXPORT_SYMBOL_GPL(raid6_can_xor_syndrome); 1043626738bSChristoph Hellwig 10535472bc6SChristoph Hellwig /** 10635472bc6SChristoph Hellwig * raid6_recov_2data - recover two missing data disks 10735472bc6SChristoph Hellwig * @disks: number of "disks" to operate on including parity 10835472bc6SChristoph Hellwig * @bytes: length in bytes of each vector 10935472bc6SChristoph Hellwig * @faila: first failed data disk index 11035472bc6SChristoph Hellwig * @failb: second failed data disk index 11135472bc6SChristoph Hellwig * @ptrs: @disks size array of memory pointers 11235472bc6SChristoph Hellwig * 11335472bc6SChristoph Hellwig * Rebuild @bytes of missing data in @ptrs[@faila] and @ptrs[@failb] from the 11435472bc6SChristoph Hellwig * data in the remaining disks and the two parities pointed to by the other 11535472bc6SChristoph Hellwig * indices between 0 and @disks - 1 in @ptrs. @disks includes the data disks 11635472bc6SChristoph Hellwig * and the two parities. @faila must be smaller than @failb. 11735472bc6SChristoph Hellwig * 11835472bc6SChristoph Hellwig * Memory pointed to by each pointer in @ptrs must be page aligned and is 11935472bc6SChristoph Hellwig * limited to %PAGE_SIZE. 12035472bc6SChristoph Hellwig */ 12135472bc6SChristoph Hellwig void raid6_recov_2data(int disks, size_t bytes, int faila, int failb, 12235472bc6SChristoph Hellwig void **ptrs) 12335472bc6SChristoph Hellwig { 12435472bc6SChristoph Hellwig WARN_ON_ONCE(!in_task() || irqs_disabled() || softirq_count()); 12535472bc6SChristoph Hellwig WARN_ON_ONCE(bytes & 511); 12635472bc6SChristoph Hellwig WARN_ON_ONCE(bytes > PAGE_SIZE); 12735472bc6SChristoph Hellwig WARN_ON_ONCE(failb <= faila); 1283626738bSChristoph Hellwig 12935472bc6SChristoph Hellwig raid6_recov_algo->data2(disks, bytes, faila, failb, ptrs); 13035472bc6SChristoph Hellwig } 13135472bc6SChristoph Hellwig EXPORT_SYMBOL_GPL(raid6_recov_2data); 13235472bc6SChristoph Hellwig 13335472bc6SChristoph Hellwig /** 13435472bc6SChristoph Hellwig * raid6_recov_datap - recover a missing data disk and missing P-parity 13535472bc6SChristoph Hellwig * @disks: number of "disks" to operate on including parity 13635472bc6SChristoph Hellwig * @bytes: length in bytes of each vector 13735472bc6SChristoph Hellwig * @faila: failed data disk index 13835472bc6SChristoph Hellwig * @ptrs: @disks size array of memory pointers 13935472bc6SChristoph Hellwig * 14035472bc6SChristoph Hellwig * Rebuild @bytes of missing data in @ptrs[@faila] and the missing P-parity in 14135472bc6SChristoph Hellwig * @ptrs[@disks - 2] from the data in the remaining disks and the Q-parity 14235472bc6SChristoph Hellwig * pointed to by the other indices between 0 and @disks - 1 in @ptrs. @disks 14335472bc6SChristoph Hellwig * includes the data disks and the two parities. 14435472bc6SChristoph Hellwig * 14535472bc6SChristoph Hellwig * Memory pointed to by each pointer in @ptrs must be page aligned and is 14635472bc6SChristoph Hellwig * limited to %PAGE_SIZE. 14735472bc6SChristoph Hellwig */ 14835472bc6SChristoph Hellwig void raid6_recov_datap(int disks, size_t bytes, int faila, void **ptrs) 14935472bc6SChristoph Hellwig { 15035472bc6SChristoph Hellwig WARN_ON_ONCE(!in_task() || irqs_disabled() || softirq_count()); 15135472bc6SChristoph Hellwig WARN_ON_ONCE(bytes & 511); 15235472bc6SChristoph Hellwig WARN_ON_ONCE(bytes > PAGE_SIZE); 15335472bc6SChristoph Hellwig 15435472bc6SChristoph Hellwig raid6_recov_algo->datap(disks, bytes, faila, ptrs); 15535472bc6SChristoph Hellwig } 15635472bc6SChristoph Hellwig EXPORT_SYMBOL_GPL(raid6_recov_datap); 1573626738bSChristoph Hellwig 1583626738bSChristoph Hellwig #define RAID6_TIME_JIFFIES_LG2 4 1593626738bSChristoph Hellwig #define RAID6_TEST_DISKS 8 1603626738bSChristoph Hellwig #define RAID6_TEST_DISKS_ORDER 3 1613626738bSChristoph Hellwig 162adfcf6e8SChristoph Hellwig static int raid6_choose_gen(void *(*const dptrs)[RAID6_TEST_DISKS], 163adfcf6e8SChristoph Hellwig const int disks) 1643626738bSChristoph Hellwig { 165adfcf6e8SChristoph Hellwig /* work on the second half of the disks */ 166adfcf6e8SChristoph Hellwig int start = (disks >> 1) - 1, stop = disks - 3; 167adfcf6e8SChristoph Hellwig const struct raid6_calls *best = NULL; 168adfcf6e8SChristoph Hellwig unsigned long bestgenperf = 0; 169adfcf6e8SChristoph Hellwig unsigned int i; 1703626738bSChristoph Hellwig 171adfcf6e8SChristoph Hellwig for (i = 0; i < raid6_nr_algos; i++) { 172adfcf6e8SChristoph Hellwig const struct raid6_calls *algo = raid6_algos[i]; 173adfcf6e8SChristoph Hellwig unsigned long perf = 0, j0, j1; 1743626738bSChristoph Hellwig 1753626738bSChristoph Hellwig preempt_disable(); 1763626738bSChristoph Hellwig j0 = jiffies; 1773626738bSChristoph Hellwig while ((j1 = jiffies) == j0) 1783626738bSChristoph Hellwig cpu_relax(); 1793626738bSChristoph Hellwig while (time_before(jiffies, 1803626738bSChristoph Hellwig j1 + (1<<RAID6_TIME_JIFFIES_LG2))) { 181adfcf6e8SChristoph Hellwig algo->gen_syndrome(disks, PAGE_SIZE, *dptrs); 1823626738bSChristoph Hellwig perf++; 1833626738bSChristoph Hellwig } 1843626738bSChristoph Hellwig preempt_enable(); 1853626738bSChristoph Hellwig 1863626738bSChristoph Hellwig if (perf > bestgenperf) { 1873626738bSChristoph Hellwig bestgenperf = perf; 188adfcf6e8SChristoph Hellwig best = algo; 1893626738bSChristoph Hellwig } 190adfcf6e8SChristoph Hellwig pr_info("raid6: %-8s gen() %5ld MB/s\n", algo->name, 1913626738bSChristoph Hellwig (perf * HZ * (disks-2)) >> 1923626738bSChristoph Hellwig (20 - PAGE_SHIFT + RAID6_TIME_JIFFIES_LG2)); 1933626738bSChristoph Hellwig } 1943626738bSChristoph Hellwig 1953626738bSChristoph Hellwig if (!best) { 1963626738bSChristoph Hellwig pr_err("raid6: Yikes! No algorithm found!\n"); 197adfcf6e8SChristoph Hellwig return -EINVAL; 1983626738bSChristoph Hellwig } 1993626738bSChristoph Hellwig 200*10f4b8e2SChristoph Hellwig static_call_update(raid6_gen_syndrome_impl, best->gen_syndrome); 201*10f4b8e2SChristoph Hellwig static_call_update(raid6_xor_syndrome_impl, best->xor_syndrome); 2023626738bSChristoph Hellwig 2033626738bSChristoph Hellwig pr_info("raid6: using algorithm %s gen() %ld MB/s\n", 2043626738bSChristoph Hellwig best->name, 2053626738bSChristoph Hellwig (bestgenperf * HZ * (disks - 2)) >> 2063626738bSChristoph Hellwig (20 - PAGE_SHIFT + RAID6_TIME_JIFFIES_LG2)); 2073626738bSChristoph Hellwig 2083626738bSChristoph Hellwig if (best->xor_syndrome) { 209adfcf6e8SChristoph Hellwig unsigned long perf = 0, j0, j1; 2103626738bSChristoph Hellwig 2113626738bSChristoph Hellwig preempt_disable(); 2123626738bSChristoph Hellwig j0 = jiffies; 2133626738bSChristoph Hellwig while ((j1 = jiffies) == j0) 2143626738bSChristoph Hellwig cpu_relax(); 2153626738bSChristoph Hellwig while (time_before(jiffies, 2163626738bSChristoph Hellwig j1 + (1 << RAID6_TIME_JIFFIES_LG2))) { 2173626738bSChristoph Hellwig best->xor_syndrome(disks, start, stop, 2183626738bSChristoph Hellwig PAGE_SIZE, *dptrs); 2193626738bSChristoph Hellwig perf++; 2203626738bSChristoph Hellwig } 2213626738bSChristoph Hellwig preempt_enable(); 2223626738bSChristoph Hellwig 2233626738bSChristoph Hellwig pr_info("raid6: .... xor() %ld MB/s, rmw enabled\n", 2243626738bSChristoph Hellwig (perf * HZ * (disks - 2)) >> 2253626738bSChristoph Hellwig (20 - PAGE_SHIFT + RAID6_TIME_JIFFIES_LG2 + 1)); 2263626738bSChristoph Hellwig } 2273626738bSChristoph Hellwig 228adfcf6e8SChristoph Hellwig return 0; 2293626738bSChristoph Hellwig } 2303626738bSChristoph Hellwig 2313626738bSChristoph Hellwig 2323626738bSChristoph Hellwig /* Try to pick the best algorithm */ 2333626738bSChristoph Hellwig /* This code uses the gfmul table as convenient data set to abuse */ 2343626738bSChristoph Hellwig 2353626738bSChristoph Hellwig static int __init raid6_select_algo(void) 2363626738bSChristoph Hellwig { 2373626738bSChristoph Hellwig const int disks = RAID6_TEST_DISKS; 2383626738bSChristoph Hellwig char *disk_ptr, *p; 2393626738bSChristoph Hellwig void *dptrs[RAID6_TEST_DISKS]; 2403626738bSChristoph Hellwig int i, cycle; 241adfcf6e8SChristoph Hellwig int error; 242adfcf6e8SChristoph Hellwig 243adfcf6e8SChristoph Hellwig if (!IS_ENABLED(CONFIG_RAID6_PQ_BENCHMARK) || raid6_nr_algos == 1) { 244adfcf6e8SChristoph Hellwig pr_info("raid6: skipped pq benchmark and selected %s\n", 245adfcf6e8SChristoph Hellwig raid6_algos[raid6_nr_algos - 1]->name); 246*10f4b8e2SChristoph Hellwig static_call_update(raid6_gen_syndrome_impl, 247*10f4b8e2SChristoph Hellwig raid6_algos[raid6_nr_algos - 1]->gen_syndrome); 248*10f4b8e2SChristoph Hellwig static_call_update(raid6_xor_syndrome_impl, 249*10f4b8e2SChristoph Hellwig raid6_algos[raid6_nr_algos - 1]->xor_syndrome); 250adfcf6e8SChristoph Hellwig return 0; 251adfcf6e8SChristoph Hellwig } 2523626738bSChristoph Hellwig 2533626738bSChristoph Hellwig /* prepare the buffer and fill it circularly with gfmul table */ 2543626738bSChristoph Hellwig disk_ptr = (char *)__get_free_pages(GFP_KERNEL, RAID6_TEST_DISKS_ORDER); 2553626738bSChristoph Hellwig if (!disk_ptr) { 2563626738bSChristoph Hellwig pr_err("raid6: Yikes! No memory available.\n"); 2573626738bSChristoph Hellwig return -ENOMEM; 2583626738bSChristoph Hellwig } 2593626738bSChristoph Hellwig 2603626738bSChristoph Hellwig p = disk_ptr; 2613626738bSChristoph Hellwig for (i = 0; i < disks; i++) 2623626738bSChristoph Hellwig dptrs[i] = p + PAGE_SIZE * i; 2633626738bSChristoph Hellwig 2643626738bSChristoph Hellwig cycle = ((disks - 2) * PAGE_SIZE) / 65536; 2653626738bSChristoph Hellwig for (i = 0; i < cycle; i++) { 2663626738bSChristoph Hellwig memcpy(p, raid6_gfmul, 65536); 2673626738bSChristoph Hellwig p += 65536; 2683626738bSChristoph Hellwig } 2693626738bSChristoph Hellwig 2703626738bSChristoph Hellwig if ((disks - 2) * PAGE_SIZE % 65536) 2713626738bSChristoph Hellwig memcpy(p, raid6_gfmul, (disks - 2) * PAGE_SIZE % 65536); 2723626738bSChristoph Hellwig 2733626738bSChristoph Hellwig /* select raid gen_syndrome function */ 274adfcf6e8SChristoph Hellwig error = raid6_choose_gen(&dptrs, disks); 2753626738bSChristoph Hellwig 2763626738bSChristoph Hellwig free_pages((unsigned long)disk_ptr, RAID6_TEST_DISKS_ORDER); 2773626738bSChristoph Hellwig 278adfcf6e8SChristoph Hellwig return error; 2793626738bSChristoph Hellwig } 2803626738bSChristoph Hellwig 281adfcf6e8SChristoph Hellwig /* 282adfcf6e8SChristoph Hellwig * Register a RAID6 P/Q generation algorithm. The most optimized/unrolled 283adfcf6e8SChristoph Hellwig * implementation should be registered last so it will be selected when the 284adfcf6e8SChristoph Hellwig * boot-time benchmark is disabled. 285adfcf6e8SChristoph Hellwig */ 286adfcf6e8SChristoph Hellwig void __init raid6_algo_add(const struct raid6_calls *algo) 2873626738bSChristoph Hellwig { 288adfcf6e8SChristoph Hellwig if (WARN_ON_ONCE(raid6_nr_algos == RAID6_MAX_ALGOS)) 289adfcf6e8SChristoph Hellwig return; 290adfcf6e8SChristoph Hellwig raid6_algos[raid6_nr_algos++] = algo; 2913626738bSChristoph Hellwig } 2923626738bSChristoph Hellwig 293adfcf6e8SChristoph Hellwig void __init raid6_algo_add_default(void) 294adfcf6e8SChristoph Hellwig { 295adfcf6e8SChristoph Hellwig raid6_algo_add(&raid6_intx1); 296adfcf6e8SChristoph Hellwig raid6_algo_add(&raid6_intx2); 297adfcf6e8SChristoph Hellwig raid6_algo_add(&raid6_intx4); 298adfcf6e8SChristoph Hellwig raid6_algo_add(&raid6_intx8); 299adfcf6e8SChristoph Hellwig } 300adfcf6e8SChristoph Hellwig 301adfcf6e8SChristoph Hellwig void __init raid6_recov_algo_add(const struct raid6_recov_calls *algo) 302adfcf6e8SChristoph Hellwig { 303adfcf6e8SChristoph Hellwig if (WARN_ON_ONCE(raid6_recov_algo)) 304adfcf6e8SChristoph Hellwig return; 305adfcf6e8SChristoph Hellwig raid6_recov_algo = algo; 306adfcf6e8SChristoph Hellwig } 307adfcf6e8SChristoph Hellwig 308adfcf6e8SChristoph Hellwig #ifdef CONFIG_RAID6_PQ_ARCH 309adfcf6e8SChristoph Hellwig #include "pq_arch.h" 310adfcf6e8SChristoph Hellwig #else 311adfcf6e8SChristoph Hellwig static inline void arch_raid6_init(void) 312adfcf6e8SChristoph Hellwig { 313adfcf6e8SChristoph Hellwig raid6_algo_add_default(); 314adfcf6e8SChristoph Hellwig } 315adfcf6e8SChristoph Hellwig #endif /* CONFIG_RAID6_PQ_ARCH */ 316adfcf6e8SChristoph Hellwig 317adfcf6e8SChristoph Hellwig static int __init raid6_init(void) 318adfcf6e8SChristoph Hellwig { 319adfcf6e8SChristoph Hellwig /* 320adfcf6e8SChristoph Hellwig * Architectures providing arch_raid6_init must add all PQ generation 321adfcf6e8SChristoph Hellwig * algorithms they want to consider in arch_raid6_init(), including 322adfcf6e8SChristoph Hellwig * the generic ones using raid6_algo_add_default() if wanted. 323adfcf6e8SChristoph Hellwig */ 324adfcf6e8SChristoph Hellwig arch_raid6_init(); 325adfcf6e8SChristoph Hellwig 326adfcf6e8SChristoph Hellwig /* 327adfcf6e8SChristoph Hellwig * Architectures don't have to set a recovery algorithm, we'll just pick 328adfcf6e8SChristoph Hellwig * the generic integer one if none was set. 329adfcf6e8SChristoph Hellwig */ 330adfcf6e8SChristoph Hellwig if (!raid6_recov_algo) 331adfcf6e8SChristoph Hellwig raid6_recov_algo = &raid6_recov_intx1; 332adfcf6e8SChristoph Hellwig pr_info("raid6: using %s recovery algorithm\n", raid6_recov_algo->name); 333adfcf6e8SChristoph Hellwig 334adfcf6e8SChristoph Hellwig return raid6_select_algo(); 335adfcf6e8SChristoph Hellwig } 336adfcf6e8SChristoph Hellwig 337adfcf6e8SChristoph Hellwig static void __exit raid6_exit(void) 338adfcf6e8SChristoph Hellwig { 339adfcf6e8SChristoph Hellwig } 340adfcf6e8SChristoph Hellwig 341adfcf6e8SChristoph Hellwig subsys_initcall(raid6_init); 3423626738bSChristoph Hellwig module_exit(raid6_exit); 3433626738bSChristoph Hellwig MODULE_LICENSE("GPL"); 3443626738bSChristoph Hellwig MODULE_DESCRIPTION("RAID6 Q-syndrome calculations"); 345adfcf6e8SChristoph Hellwig 346adfcf6e8SChristoph Hellwig #if IS_ENABLED(CONFIG_RAID6_PQ_KUNIT_TEST) 347adfcf6e8SChristoph Hellwig const struct raid6_calls *raid6_algo_find(unsigned int idx) 348adfcf6e8SChristoph Hellwig { 349adfcf6e8SChristoph Hellwig if (idx >= raid6_nr_algos) { 350adfcf6e8SChristoph Hellwig /* 351adfcf6e8SChristoph Hellwig * Always include the simplest generic integer implementation in 352adfcf6e8SChristoph Hellwig * the unit tests as a baseline. 353adfcf6e8SChristoph Hellwig */ 354adfcf6e8SChristoph Hellwig if (idx == raid6_nr_algos && 355adfcf6e8SChristoph Hellwig raid6_algos[0] != &raid6_intx1) 356adfcf6e8SChristoph Hellwig return &raid6_intx1; 357adfcf6e8SChristoph Hellwig return NULL; 358adfcf6e8SChristoph Hellwig } 359adfcf6e8SChristoph Hellwig return raid6_algos[idx]; 360adfcf6e8SChristoph Hellwig } 361adfcf6e8SChristoph Hellwig EXPORT_SYMBOL_IF_KUNIT(raid6_algo_find); 362adfcf6e8SChristoph Hellwig 363adfcf6e8SChristoph Hellwig const struct raid6_recov_calls *raid6_recov_algo_find(unsigned int idx) 364adfcf6e8SChristoph Hellwig { 365adfcf6e8SChristoph Hellwig switch (idx) { 366adfcf6e8SChristoph Hellwig case 0: 367adfcf6e8SChristoph Hellwig /* always test the generic integer implementation */ 368adfcf6e8SChristoph Hellwig return &raid6_recov_intx1; 369adfcf6e8SChristoph Hellwig case 1: 370adfcf6e8SChristoph Hellwig /* test the optimized implementation if there is one */ 371adfcf6e8SChristoph Hellwig if (raid6_recov_algo != &raid6_recov_intx1) 372adfcf6e8SChristoph Hellwig return raid6_recov_algo; 373adfcf6e8SChristoph Hellwig return NULL; 374adfcf6e8SChristoph Hellwig default: 375adfcf6e8SChristoph Hellwig return NULL; 376adfcf6e8SChristoph Hellwig } 377adfcf6e8SChristoph Hellwig } 378adfcf6e8SChristoph Hellwig EXPORT_SYMBOL_IF_KUNIT(raid6_recov_algo_find); 379adfcf6e8SChristoph Hellwig #endif /* CONFIG_RAID6_PQ_KUNIT_TEST */ 380