13626738bSChristoph Hellwig // SPDX-License-Identifier: GPL-2.0-or-later 23626738bSChristoph Hellwig /* 330bf04bdSChristoph Hellwig * Copyright 2002 H. Peter Anvin - All Rights Reserved 43626738bSChristoph Hellwig * 53626738bSChristoph Hellwig * Algorithm list and algorithm selection for RAID-6 63626738bSChristoph Hellwig */ 73626738bSChristoph Hellwig 83626738bSChristoph Hellwig #include <linux/module.h> 93626738bSChristoph Hellwig #include <linux/gfp.h> 10769d603fSChristoph Hellwig #include <linux/raid/pq.h> 11*19c000baSMike Rapoport (Microsoft) #include <linux/slab.h> 1210f4b8e2SChristoph Hellwig #include <linux/static_call.h> 133626738bSChristoph Hellwig #include <kunit/visibility.h> 14769d603fSChristoph Hellwig #include "algos.h" 153626738bSChristoph Hellwig 16adfcf6e8SChristoph Hellwig #define RAID6_MAX_ALGOS 16 17adfcf6e8SChristoph Hellwig static const struct raid6_calls *raid6_algos[RAID6_MAX_ALGOS]; 18adfcf6e8SChristoph Hellwig static unsigned int raid6_nr_algos; 1935472bc6SChristoph Hellwig static const struct raid6_recov_calls *raid6_recov_algo; 2035472bc6SChristoph Hellwig 2135472bc6SChristoph Hellwig /* Selected algorithm */ 2210f4b8e2SChristoph Hellwig DEFINE_STATIC_CALL_NULL(raid6_gen_syndrome_impl, *raid6_intx1.gen_syndrome); 2310f4b8e2SChristoph Hellwig DEFINE_STATIC_CALL_NULL(raid6_xor_syndrome_impl, *raid6_intx1.xor_syndrome); 24dd83de03SChristoph Hellwig DEFINE_STATIC_CALL_NULL(raid6_recov_2data_impl, *raid6_recov_intx1.data2); 25dd83de03SChristoph Hellwig DEFINE_STATIC_CALL_NULL(raid6_recov_datap_impl, *raid6_recov_intx1.datap); 2635472bc6SChristoph Hellwig 2735472bc6SChristoph Hellwig /** 2835472bc6SChristoph Hellwig * raid6_gen_syndrome - generate RAID6 P/Q parity 2935472bc6SChristoph Hellwig * @disks: number of "disks" to operate on including parity 3035472bc6SChristoph Hellwig * @bytes: length in bytes of each vector 3135472bc6SChristoph Hellwig * @ptrs: @disks size array of memory pointers 3235472bc6SChristoph Hellwig * 3335472bc6SChristoph Hellwig * Generate @bytes worth of RAID6 P and Q parity in @ptrs[@disks - 2] and 3435472bc6SChristoph Hellwig * @ptrs[@disks - 1] respectively from the memory pointed to by @ptrs[0] to 3535472bc6SChristoph Hellwig * @ptrs[@disks - 3]. 3635472bc6SChristoph Hellwig * 3735472bc6SChristoph Hellwig * @disks must be at least 4, and the memory pointed to by each member of @ptrs 3835472bc6SChristoph Hellwig * must be at least 64-byte aligned. @bytes must be non-zero and a multiple of 3935472bc6SChristoph Hellwig * 512. 4035472bc6SChristoph Hellwig * 4135472bc6SChristoph Hellwig * See https://kernel.org/pub/linux/kernel/people/hpa/raid6.pdf for underlying 4235472bc6SChristoph Hellwig * algorithm. 4335472bc6SChristoph Hellwig */ 4435472bc6SChristoph Hellwig void raid6_gen_syndrome(int disks, size_t bytes, void **ptrs) 4535472bc6SChristoph Hellwig { 4635472bc6SChristoph Hellwig WARN_ON_ONCE(!in_task() || irqs_disabled() || softirq_count()); 4735472bc6SChristoph Hellwig WARN_ON_ONCE(bytes & 511); 482790045aSChristoph Hellwig WARN_ON_ONCE(disks < RAID6_MIN_DISKS); 4935472bc6SChristoph Hellwig 5010f4b8e2SChristoph Hellwig static_call(raid6_gen_syndrome_impl)(disks, bytes, ptrs); 5135472bc6SChristoph Hellwig } 5235472bc6SChristoph Hellwig EXPORT_SYMBOL_GPL(raid6_gen_syndrome); 5335472bc6SChristoph Hellwig 5435472bc6SChristoph Hellwig /** 5535472bc6SChristoph Hellwig * raid6_xor_syndrome - update RAID6 P/Q parity 5635472bc6SChristoph Hellwig * @disks: number of "disks" to operate on including parity 5735472bc6SChristoph Hellwig * @start: first index into @disk to update 5835472bc6SChristoph Hellwig * @stop: last index into @disk to update 5935472bc6SChristoph Hellwig * @bytes: length in bytes of each vector 6035472bc6SChristoph Hellwig * @ptrs: @disks size array of memory pointers 6135472bc6SChristoph Hellwig * 6235472bc6SChristoph Hellwig * Update @bytes worth of RAID6 P and Q parity in @ptrs[@disks - 2] and 6335472bc6SChristoph Hellwig * @ptrs[@disks - 1] respectively for the memory pointed to by 6435472bc6SChristoph Hellwig * @ptrs[@start..@stop]. 6535472bc6SChristoph Hellwig * 6635472bc6SChristoph Hellwig * This is used to update parity in place using the following sequence: 6735472bc6SChristoph Hellwig * 6835472bc6SChristoph Hellwig * 1) call raid6_xor_syndrome(disk, start, stop, ...) for the existing data. 6935472bc6SChristoph Hellwig * 2) update the the data in @ptrs[@start..@stop]. 7035472bc6SChristoph Hellwig * 3) call raid6_xor_syndrome(disk, start, stop, ...) for the new data. 7135472bc6SChristoph Hellwig * 7235472bc6SChristoph Hellwig * Data between @start and @stop that is not changed should be filled 7335472bc6SChristoph Hellwig * with a pointer to the kernel zero page. 7435472bc6SChristoph Hellwig * 7535472bc6SChristoph Hellwig * @disks must be at least 4, and the memory pointed to by each member of @ptrs 7635472bc6SChristoph Hellwig * must be at least 64-byte aligned. @bytes must be non-zero and a multiple of 7735472bc6SChristoph Hellwig * 512. @stop must be larger or equal to @start. 7835472bc6SChristoph Hellwig */ 7935472bc6SChristoph Hellwig void raid6_xor_syndrome(int disks, int start, int stop, size_t bytes, 8035472bc6SChristoph Hellwig void **ptrs) 8135472bc6SChristoph Hellwig { 8235472bc6SChristoph Hellwig WARN_ON_ONCE(!in_task() || irqs_disabled() || softirq_count()); 8335472bc6SChristoph Hellwig WARN_ON_ONCE(bytes & 511); 842790045aSChristoph Hellwig WARN_ON_ONCE(disks < RAID6_MIN_DISKS); 8535472bc6SChristoph Hellwig WARN_ON_ONCE(stop < start); 8635472bc6SChristoph Hellwig 8710f4b8e2SChristoph Hellwig static_call(raid6_xor_syndrome_impl)(disks, start, stop, bytes, ptrs); 8835472bc6SChristoph Hellwig } 8935472bc6SChristoph Hellwig EXPORT_SYMBOL_GPL(raid6_xor_syndrome); 9035472bc6SChristoph Hellwig 9135472bc6SChristoph Hellwig /* 9235472bc6SChristoph Hellwig * raid6_can_xor_syndrome - check if raid6_xor_syndrome() can be used 9335472bc6SChristoph Hellwig * 9435472bc6SChristoph Hellwig * Returns %true if raid6_can_xor_syndrome() can be used, else %false. 9535472bc6SChristoph Hellwig */ 9635472bc6SChristoph Hellwig bool raid6_can_xor_syndrome(void) 9735472bc6SChristoph Hellwig { 9810f4b8e2SChristoph Hellwig return !!static_call_query(raid6_xor_syndrome_impl); 9935472bc6SChristoph Hellwig } 10035472bc6SChristoph Hellwig EXPORT_SYMBOL_GPL(raid6_can_xor_syndrome); 1013626738bSChristoph Hellwig 10235472bc6SChristoph Hellwig /** 10335472bc6SChristoph Hellwig * raid6_recov_2data - recover two missing data disks 10435472bc6SChristoph Hellwig * @disks: number of "disks" to operate on including parity 10535472bc6SChristoph Hellwig * @bytes: length in bytes of each vector 10635472bc6SChristoph Hellwig * @faila: first failed data disk index 10735472bc6SChristoph Hellwig * @failb: second failed data disk index 10835472bc6SChristoph Hellwig * @ptrs: @disks size array of memory pointers 10935472bc6SChristoph Hellwig * 11035472bc6SChristoph Hellwig * Rebuild @bytes of missing data in @ptrs[@faila] and @ptrs[@failb] from the 11135472bc6SChristoph Hellwig * data in the remaining disks and the two parities pointed to by the other 11235472bc6SChristoph Hellwig * indices between 0 and @disks - 1 in @ptrs. @disks includes the data disks 11335472bc6SChristoph Hellwig * and the two parities. @faila must be smaller than @failb. 11435472bc6SChristoph Hellwig * 11535472bc6SChristoph Hellwig * Memory pointed to by each pointer in @ptrs must be page aligned and is 11635472bc6SChristoph Hellwig * limited to %PAGE_SIZE. 11735472bc6SChristoph Hellwig */ 11835472bc6SChristoph Hellwig void raid6_recov_2data(int disks, size_t bytes, int faila, int failb, 11935472bc6SChristoph Hellwig void **ptrs) 12035472bc6SChristoph Hellwig { 12135472bc6SChristoph Hellwig WARN_ON_ONCE(!in_task() || irqs_disabled() || softirq_count()); 12235472bc6SChristoph Hellwig WARN_ON_ONCE(bytes & 511); 12335472bc6SChristoph Hellwig WARN_ON_ONCE(bytes > PAGE_SIZE); 12435472bc6SChristoph Hellwig WARN_ON_ONCE(failb <= faila); 1253626738bSChristoph Hellwig 126dd83de03SChristoph Hellwig static_call(raid6_recov_2data_impl)(disks, bytes, faila, failb, ptrs); 12735472bc6SChristoph Hellwig } 12835472bc6SChristoph Hellwig EXPORT_SYMBOL_GPL(raid6_recov_2data); 12935472bc6SChristoph Hellwig 13035472bc6SChristoph Hellwig /** 13135472bc6SChristoph Hellwig * raid6_recov_datap - recover a missing data disk and missing P-parity 13235472bc6SChristoph Hellwig * @disks: number of "disks" to operate on including parity 13335472bc6SChristoph Hellwig * @bytes: length in bytes of each vector 13435472bc6SChristoph Hellwig * @faila: failed data disk index 13535472bc6SChristoph Hellwig * @ptrs: @disks size array of memory pointers 13635472bc6SChristoph Hellwig * 13735472bc6SChristoph Hellwig * Rebuild @bytes of missing data in @ptrs[@faila] and the missing P-parity in 13835472bc6SChristoph Hellwig * @ptrs[@disks - 2] from the data in the remaining disks and the Q-parity 13935472bc6SChristoph Hellwig * pointed to by the other indices between 0 and @disks - 1 in @ptrs. @disks 14035472bc6SChristoph Hellwig * includes the data disks and the two parities. 14135472bc6SChristoph Hellwig * 14235472bc6SChristoph Hellwig * Memory pointed to by each pointer in @ptrs must be page aligned and is 14335472bc6SChristoph Hellwig * limited to %PAGE_SIZE. 14435472bc6SChristoph Hellwig */ 14535472bc6SChristoph Hellwig void raid6_recov_datap(int disks, size_t bytes, int faila, void **ptrs) 14635472bc6SChristoph Hellwig { 14735472bc6SChristoph Hellwig WARN_ON_ONCE(!in_task() || irqs_disabled() || softirq_count()); 14835472bc6SChristoph Hellwig WARN_ON_ONCE(bytes & 511); 14935472bc6SChristoph Hellwig WARN_ON_ONCE(bytes > PAGE_SIZE); 15035472bc6SChristoph Hellwig 151dd83de03SChristoph Hellwig static_call(raid6_recov_datap_impl)(disks, bytes, faila, ptrs); 15235472bc6SChristoph Hellwig } 15335472bc6SChristoph Hellwig EXPORT_SYMBOL_GPL(raid6_recov_datap); 1543626738bSChristoph Hellwig 1553626738bSChristoph Hellwig #define RAID6_TIME_JIFFIES_LG2 4 1563626738bSChristoph Hellwig #define RAID6_TEST_DISKS 8 1573626738bSChristoph Hellwig 158adfcf6e8SChristoph Hellwig static int raid6_choose_gen(void *(*const dptrs)[RAID6_TEST_DISKS], 159adfcf6e8SChristoph Hellwig const int disks) 1603626738bSChristoph Hellwig { 161adfcf6e8SChristoph Hellwig /* work on the second half of the disks */ 162adfcf6e8SChristoph Hellwig int start = (disks >> 1) - 1, stop = disks - 3; 163adfcf6e8SChristoph Hellwig const struct raid6_calls *best = NULL; 164adfcf6e8SChristoph Hellwig unsigned long bestgenperf = 0; 165adfcf6e8SChristoph Hellwig unsigned int i; 1663626738bSChristoph Hellwig 167adfcf6e8SChristoph Hellwig for (i = 0; i < raid6_nr_algos; i++) { 168adfcf6e8SChristoph Hellwig const struct raid6_calls *algo = raid6_algos[i]; 169adfcf6e8SChristoph Hellwig unsigned long perf = 0, j0, j1; 1703626738bSChristoph Hellwig 1713626738bSChristoph Hellwig preempt_disable(); 1723626738bSChristoph Hellwig j0 = jiffies; 1733626738bSChristoph Hellwig while ((j1 = jiffies) == j0) 1743626738bSChristoph Hellwig cpu_relax(); 1753626738bSChristoph Hellwig while (time_before(jiffies, 1763626738bSChristoph Hellwig j1 + (1<<RAID6_TIME_JIFFIES_LG2))) { 177adfcf6e8SChristoph Hellwig algo->gen_syndrome(disks, PAGE_SIZE, *dptrs); 1783626738bSChristoph Hellwig perf++; 1793626738bSChristoph Hellwig } 1803626738bSChristoph Hellwig preempt_enable(); 1813626738bSChristoph Hellwig 1823626738bSChristoph Hellwig if (perf > bestgenperf) { 1833626738bSChristoph Hellwig bestgenperf = perf; 184adfcf6e8SChristoph Hellwig best = algo; 1853626738bSChristoph Hellwig } 186adfcf6e8SChristoph Hellwig pr_info("raid6: %-8s gen() %5ld MB/s\n", algo->name, 1873626738bSChristoph Hellwig (perf * HZ * (disks-2)) >> 1883626738bSChristoph Hellwig (20 - PAGE_SHIFT + RAID6_TIME_JIFFIES_LG2)); 1893626738bSChristoph Hellwig } 1903626738bSChristoph Hellwig 1913626738bSChristoph Hellwig if (!best) { 1923626738bSChristoph Hellwig pr_err("raid6: Yikes! No algorithm found!\n"); 193adfcf6e8SChristoph Hellwig return -EINVAL; 1943626738bSChristoph Hellwig } 1953626738bSChristoph Hellwig 19610f4b8e2SChristoph Hellwig static_call_update(raid6_gen_syndrome_impl, best->gen_syndrome); 19710f4b8e2SChristoph Hellwig static_call_update(raid6_xor_syndrome_impl, best->xor_syndrome); 1983626738bSChristoph Hellwig 1993626738bSChristoph Hellwig pr_info("raid6: using algorithm %s gen() %ld MB/s\n", 2003626738bSChristoph Hellwig best->name, 2013626738bSChristoph Hellwig (bestgenperf * HZ * (disks - 2)) >> 2023626738bSChristoph Hellwig (20 - PAGE_SHIFT + RAID6_TIME_JIFFIES_LG2)); 2033626738bSChristoph Hellwig 2043626738bSChristoph Hellwig if (best->xor_syndrome) { 205adfcf6e8SChristoph Hellwig unsigned long perf = 0, j0, j1; 2063626738bSChristoph Hellwig 2073626738bSChristoph Hellwig preempt_disable(); 2083626738bSChristoph Hellwig j0 = jiffies; 2093626738bSChristoph Hellwig while ((j1 = jiffies) == j0) 2103626738bSChristoph Hellwig cpu_relax(); 2113626738bSChristoph Hellwig while (time_before(jiffies, 2123626738bSChristoph Hellwig j1 + (1 << RAID6_TIME_JIFFIES_LG2))) { 2133626738bSChristoph Hellwig best->xor_syndrome(disks, start, stop, 2143626738bSChristoph Hellwig PAGE_SIZE, *dptrs); 2153626738bSChristoph Hellwig perf++; 2163626738bSChristoph Hellwig } 2173626738bSChristoph Hellwig preempt_enable(); 2183626738bSChristoph Hellwig 2193626738bSChristoph Hellwig pr_info("raid6: .... xor() %ld MB/s, rmw enabled\n", 2203626738bSChristoph Hellwig (perf * HZ * (disks - 2)) >> 2213626738bSChristoph Hellwig (20 - PAGE_SHIFT + RAID6_TIME_JIFFIES_LG2 + 1)); 2223626738bSChristoph Hellwig } 2233626738bSChristoph Hellwig 224adfcf6e8SChristoph Hellwig return 0; 2253626738bSChristoph Hellwig } 2263626738bSChristoph Hellwig 2273626738bSChristoph Hellwig 2283626738bSChristoph Hellwig /* Try to pick the best algorithm */ 2293626738bSChristoph Hellwig /* This code uses the gfmul table as convenient data set to abuse */ 2303626738bSChristoph Hellwig 2313626738bSChristoph Hellwig static int __init raid6_select_algo(void) 2323626738bSChristoph Hellwig { 2333626738bSChristoph Hellwig const int disks = RAID6_TEST_DISKS; 2343626738bSChristoph Hellwig char *disk_ptr, *p; 2353626738bSChristoph Hellwig void *dptrs[RAID6_TEST_DISKS]; 2363626738bSChristoph Hellwig int i, cycle; 237adfcf6e8SChristoph Hellwig int error; 238adfcf6e8SChristoph Hellwig 239adfcf6e8SChristoph Hellwig if (!IS_ENABLED(CONFIG_RAID6_PQ_BENCHMARK) || raid6_nr_algos == 1) { 240adfcf6e8SChristoph Hellwig pr_info("raid6: skipped pq benchmark and selected %s\n", 241adfcf6e8SChristoph Hellwig raid6_algos[raid6_nr_algos - 1]->name); 24210f4b8e2SChristoph Hellwig static_call_update(raid6_gen_syndrome_impl, 24310f4b8e2SChristoph Hellwig raid6_algos[raid6_nr_algos - 1]->gen_syndrome); 24410f4b8e2SChristoph Hellwig static_call_update(raid6_xor_syndrome_impl, 24510f4b8e2SChristoph Hellwig raid6_algos[raid6_nr_algos - 1]->xor_syndrome); 246adfcf6e8SChristoph Hellwig return 0; 247adfcf6e8SChristoph Hellwig } 2483626738bSChristoph Hellwig 2493626738bSChristoph Hellwig /* prepare the buffer and fill it circularly with gfmul table */ 250*19c000baSMike Rapoport (Microsoft) disk_ptr = kmalloc(PAGE_SIZE * RAID6_TEST_DISKS, GFP_KERNEL); 2513626738bSChristoph Hellwig if (!disk_ptr) { 2523626738bSChristoph Hellwig pr_err("raid6: Yikes! No memory available.\n"); 2533626738bSChristoph Hellwig return -ENOMEM; 2543626738bSChristoph Hellwig } 2553626738bSChristoph Hellwig 2563626738bSChristoph Hellwig p = disk_ptr; 2573626738bSChristoph Hellwig for (i = 0; i < disks; i++) 2583626738bSChristoph Hellwig dptrs[i] = p + PAGE_SIZE * i; 2593626738bSChristoph Hellwig 2603626738bSChristoph Hellwig cycle = ((disks - 2) * PAGE_SIZE) / 65536; 2613626738bSChristoph Hellwig for (i = 0; i < cycle; i++) { 2623626738bSChristoph Hellwig memcpy(p, raid6_gfmul, 65536); 2633626738bSChristoph Hellwig p += 65536; 2643626738bSChristoph Hellwig } 2653626738bSChristoph Hellwig 2663626738bSChristoph Hellwig if ((disks - 2) * PAGE_SIZE % 65536) 2673626738bSChristoph Hellwig memcpy(p, raid6_gfmul, (disks - 2) * PAGE_SIZE % 65536); 2683626738bSChristoph Hellwig 2693626738bSChristoph Hellwig /* select raid gen_syndrome function */ 270adfcf6e8SChristoph Hellwig error = raid6_choose_gen(&dptrs, disks); 2713626738bSChristoph Hellwig 272*19c000baSMike Rapoport (Microsoft) kfree(disk_ptr); 2733626738bSChristoph Hellwig 274adfcf6e8SChristoph Hellwig return error; 2753626738bSChristoph Hellwig } 2763626738bSChristoph Hellwig 277adfcf6e8SChristoph Hellwig /* 278adfcf6e8SChristoph Hellwig * Register a RAID6 P/Q generation algorithm. The most optimized/unrolled 279adfcf6e8SChristoph Hellwig * implementation should be registered last so it will be selected when the 280adfcf6e8SChristoph Hellwig * boot-time benchmark is disabled. 281adfcf6e8SChristoph Hellwig */ 282adfcf6e8SChristoph Hellwig void __init raid6_algo_add(const struct raid6_calls *algo) 2833626738bSChristoph Hellwig { 284adfcf6e8SChristoph Hellwig if (WARN_ON_ONCE(raid6_nr_algos == RAID6_MAX_ALGOS)) 285adfcf6e8SChristoph Hellwig return; 286adfcf6e8SChristoph Hellwig raid6_algos[raid6_nr_algos++] = algo; 2873626738bSChristoph Hellwig } 2883626738bSChristoph Hellwig 289adfcf6e8SChristoph Hellwig void __init raid6_algo_add_default(void) 290adfcf6e8SChristoph Hellwig { 291adfcf6e8SChristoph Hellwig raid6_algo_add(&raid6_intx1); 292adfcf6e8SChristoph Hellwig raid6_algo_add(&raid6_intx2); 293adfcf6e8SChristoph Hellwig raid6_algo_add(&raid6_intx4); 294adfcf6e8SChristoph Hellwig raid6_algo_add(&raid6_intx8); 295adfcf6e8SChristoph Hellwig } 296adfcf6e8SChristoph Hellwig 297adfcf6e8SChristoph Hellwig void __init raid6_recov_algo_add(const struct raid6_recov_calls *algo) 298adfcf6e8SChristoph Hellwig { 299adfcf6e8SChristoph Hellwig if (WARN_ON_ONCE(raid6_recov_algo)) 300adfcf6e8SChristoph Hellwig return; 301adfcf6e8SChristoph Hellwig raid6_recov_algo = algo; 302adfcf6e8SChristoph Hellwig } 303adfcf6e8SChristoph Hellwig 304adfcf6e8SChristoph Hellwig #ifdef CONFIG_RAID6_PQ_ARCH 305adfcf6e8SChristoph Hellwig #include "pq_arch.h" 306adfcf6e8SChristoph Hellwig #else 307adfcf6e8SChristoph Hellwig static inline void arch_raid6_init(void) 308adfcf6e8SChristoph Hellwig { 309adfcf6e8SChristoph Hellwig raid6_algo_add_default(); 310adfcf6e8SChristoph Hellwig } 311adfcf6e8SChristoph Hellwig #endif /* CONFIG_RAID6_PQ_ARCH */ 312adfcf6e8SChristoph Hellwig 313adfcf6e8SChristoph Hellwig static int __init raid6_init(void) 314adfcf6e8SChristoph Hellwig { 315adfcf6e8SChristoph Hellwig /* 316adfcf6e8SChristoph Hellwig * Architectures providing arch_raid6_init must add all PQ generation 317adfcf6e8SChristoph Hellwig * algorithms they want to consider in arch_raid6_init(), including 318adfcf6e8SChristoph Hellwig * the generic ones using raid6_algo_add_default() if wanted. 319adfcf6e8SChristoph Hellwig */ 320adfcf6e8SChristoph Hellwig arch_raid6_init(); 321adfcf6e8SChristoph Hellwig 322adfcf6e8SChristoph Hellwig /* 323adfcf6e8SChristoph Hellwig * Architectures don't have to set a recovery algorithm, we'll just pick 324adfcf6e8SChristoph Hellwig * the generic integer one if none was set. 325adfcf6e8SChristoph Hellwig */ 326adfcf6e8SChristoph Hellwig if (!raid6_recov_algo) 327adfcf6e8SChristoph Hellwig raid6_recov_algo = &raid6_recov_intx1; 328dd83de03SChristoph Hellwig static_call_update(raid6_recov_2data_impl, raid6_recov_algo->data2); 329dd83de03SChristoph Hellwig static_call_update(raid6_recov_datap_impl, raid6_recov_algo->datap); 330adfcf6e8SChristoph Hellwig pr_info("raid6: using %s recovery algorithm\n", raid6_recov_algo->name); 331adfcf6e8SChristoph Hellwig 332adfcf6e8SChristoph Hellwig return raid6_select_algo(); 333adfcf6e8SChristoph Hellwig } 334adfcf6e8SChristoph Hellwig 335adfcf6e8SChristoph Hellwig static void __exit raid6_exit(void) 336adfcf6e8SChristoph Hellwig { 337adfcf6e8SChristoph Hellwig } 338adfcf6e8SChristoph Hellwig 339adfcf6e8SChristoph Hellwig subsys_initcall(raid6_init); 3403626738bSChristoph Hellwig module_exit(raid6_exit); 3413626738bSChristoph Hellwig MODULE_LICENSE("GPL"); 3423626738bSChristoph Hellwig MODULE_DESCRIPTION("RAID6 Q-syndrome calculations"); 343adfcf6e8SChristoph Hellwig 344adfcf6e8SChristoph Hellwig #if IS_ENABLED(CONFIG_RAID6_PQ_KUNIT_TEST) 345adfcf6e8SChristoph Hellwig const struct raid6_calls *raid6_algo_find(unsigned int idx) 346adfcf6e8SChristoph Hellwig { 347adfcf6e8SChristoph Hellwig if (idx >= raid6_nr_algos) { 348adfcf6e8SChristoph Hellwig /* 349adfcf6e8SChristoph Hellwig * Always include the simplest generic integer implementation in 350adfcf6e8SChristoph Hellwig * the unit tests as a baseline. 351adfcf6e8SChristoph Hellwig */ 352adfcf6e8SChristoph Hellwig if (idx == raid6_nr_algos && 353adfcf6e8SChristoph Hellwig raid6_algos[0] != &raid6_intx1) 354adfcf6e8SChristoph Hellwig return &raid6_intx1; 355adfcf6e8SChristoph Hellwig return NULL; 356adfcf6e8SChristoph Hellwig } 357adfcf6e8SChristoph Hellwig return raid6_algos[idx]; 358adfcf6e8SChristoph Hellwig } 359adfcf6e8SChristoph Hellwig EXPORT_SYMBOL_IF_KUNIT(raid6_algo_find); 360adfcf6e8SChristoph Hellwig 361adfcf6e8SChristoph Hellwig const struct raid6_recov_calls *raid6_recov_algo_find(unsigned int idx) 362adfcf6e8SChristoph Hellwig { 363adfcf6e8SChristoph Hellwig switch (idx) { 364adfcf6e8SChristoph Hellwig case 0: 365adfcf6e8SChristoph Hellwig /* always test the generic integer implementation */ 366adfcf6e8SChristoph Hellwig return &raid6_recov_intx1; 367adfcf6e8SChristoph Hellwig case 1: 368adfcf6e8SChristoph Hellwig /* test the optimized implementation if there is one */ 369adfcf6e8SChristoph Hellwig if (raid6_recov_algo != &raid6_recov_intx1) 370adfcf6e8SChristoph Hellwig return raid6_recov_algo; 371adfcf6e8SChristoph Hellwig return NULL; 372adfcf6e8SChristoph Hellwig default: 373adfcf6e8SChristoph Hellwig return NULL; 374adfcf6e8SChristoph Hellwig } 375adfcf6e8SChristoph Hellwig } 376adfcf6e8SChristoph Hellwig EXPORT_SYMBOL_IF_KUNIT(raid6_recov_algo_find); 377adfcf6e8SChristoph Hellwig #endif /* CONFIG_RAID6_PQ_KUNIT_TEST */ 378