xref: /linux/lib/raid/raid6/algos.c (revision 10f4b8e2a16452a7ead747c91a27ba3fabfe948d)
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