xref: /linux/lib/raid/raid6/algos.c (revision dd83de0341da9979d6e584cd10e44361a183c938)
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>
1710f4b8e2SChristoph 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 */
2710f4b8e2SChristoph Hellwig DEFINE_STATIC_CALL_NULL(raid6_gen_syndrome_impl, *raid6_intx1.gen_syndrome);
2810f4b8e2SChristoph Hellwig DEFINE_STATIC_CALL_NULL(raid6_xor_syndrome_impl, *raid6_intx1.xor_syndrome);
29*dd83de03SChristoph Hellwig DEFINE_STATIC_CALL_NULL(raid6_recov_2data_impl, *raid6_recov_intx1.data2);
30*dd83de03SChristoph Hellwig DEFINE_STATIC_CALL_NULL(raid6_recov_datap_impl, *raid6_recov_intx1.datap);
3135472bc6SChristoph Hellwig 
3235472bc6SChristoph Hellwig /**
3335472bc6SChristoph Hellwig  * raid6_gen_syndrome - generate RAID6 P/Q parity
3435472bc6SChristoph Hellwig  * @disks:	number of "disks" to operate on including parity
3535472bc6SChristoph Hellwig  * @bytes:	length in bytes of each vector
3635472bc6SChristoph Hellwig  * @ptrs:	@disks size array of memory pointers
3735472bc6SChristoph Hellwig  *
3835472bc6SChristoph Hellwig  * Generate @bytes worth of RAID6 P and Q parity in @ptrs[@disks - 2] and
3935472bc6SChristoph Hellwig  * @ptrs[@disks - 1] respectively from the memory pointed to by @ptrs[0] to
4035472bc6SChristoph Hellwig  * @ptrs[@disks - 3].
4135472bc6SChristoph Hellwig  *
4235472bc6SChristoph Hellwig  * @disks must be at least 4, and the memory pointed to by each member of @ptrs
4335472bc6SChristoph Hellwig  * must be at least 64-byte aligned.  @bytes must be non-zero and a multiple of
4435472bc6SChristoph Hellwig  * 512.
4535472bc6SChristoph Hellwig  *
4635472bc6SChristoph Hellwig  * See https://kernel.org/pub/linux/kernel/people/hpa/raid6.pdf for underlying
4735472bc6SChristoph Hellwig  * algorithm.
4835472bc6SChristoph Hellwig  */
4935472bc6SChristoph Hellwig void raid6_gen_syndrome(int disks, size_t bytes, void **ptrs)
5035472bc6SChristoph Hellwig {
5135472bc6SChristoph Hellwig 	WARN_ON_ONCE(!in_task() || irqs_disabled() || softirq_count());
5235472bc6SChristoph Hellwig 	WARN_ON_ONCE(bytes & 511);
532790045aSChristoph Hellwig 	WARN_ON_ONCE(disks < RAID6_MIN_DISKS);
5435472bc6SChristoph Hellwig 
5510f4b8e2SChristoph Hellwig 	static_call(raid6_gen_syndrome_impl)(disks, bytes, ptrs);
5635472bc6SChristoph Hellwig }
5735472bc6SChristoph Hellwig EXPORT_SYMBOL_GPL(raid6_gen_syndrome);
5835472bc6SChristoph Hellwig 
5935472bc6SChristoph Hellwig /**
6035472bc6SChristoph Hellwig  * raid6_xor_syndrome - update RAID6 P/Q parity
6135472bc6SChristoph Hellwig  * @disks:	number of "disks" to operate on including parity
6235472bc6SChristoph Hellwig  * @start:	first index into @disk to update
6335472bc6SChristoph Hellwig  * @stop:	last index into @disk to update
6435472bc6SChristoph Hellwig  * @bytes:	length in bytes of each vector
6535472bc6SChristoph Hellwig  * @ptrs:	@disks size array of memory pointers
6635472bc6SChristoph Hellwig  *
6735472bc6SChristoph Hellwig  * Update @bytes worth of RAID6 P and Q parity in @ptrs[@disks - 2] and
6835472bc6SChristoph Hellwig  * @ptrs[@disks - 1] respectively for the memory pointed to by
6935472bc6SChristoph Hellwig  * @ptrs[@start..@stop].
7035472bc6SChristoph Hellwig  *
7135472bc6SChristoph Hellwig  * This is used to update parity in place using the following sequence:
7235472bc6SChristoph Hellwig  *
7335472bc6SChristoph Hellwig  * 1) call raid6_xor_syndrome(disk, start, stop, ...) for the existing data.
7435472bc6SChristoph Hellwig  * 2) update the the data in @ptrs[@start..@stop].
7535472bc6SChristoph Hellwig  * 3) call raid6_xor_syndrome(disk, start, stop, ...) for the new data.
7635472bc6SChristoph Hellwig  *
7735472bc6SChristoph Hellwig  * Data between @start and @stop that is not changed should be filled
7835472bc6SChristoph Hellwig  * with a pointer to the kernel zero page.
7935472bc6SChristoph Hellwig  *
8035472bc6SChristoph Hellwig  * @disks must be at least 4, and the memory pointed to by each member of @ptrs
8135472bc6SChristoph Hellwig  * must be at least 64-byte aligned.  @bytes must be non-zero and a multiple of
8235472bc6SChristoph Hellwig  * 512.  @stop must be larger or equal to @start.
8335472bc6SChristoph Hellwig  */
8435472bc6SChristoph Hellwig void raid6_xor_syndrome(int disks, int start, int stop, size_t bytes,
8535472bc6SChristoph Hellwig 		void **ptrs)
8635472bc6SChristoph Hellwig {
8735472bc6SChristoph Hellwig 	WARN_ON_ONCE(!in_task() || irqs_disabled() || softirq_count());
8835472bc6SChristoph Hellwig 	WARN_ON_ONCE(bytes & 511);
892790045aSChristoph Hellwig 	WARN_ON_ONCE(disks < RAID6_MIN_DISKS);
9035472bc6SChristoph Hellwig 	WARN_ON_ONCE(stop < start);
9135472bc6SChristoph Hellwig 
9210f4b8e2SChristoph Hellwig 	static_call(raid6_xor_syndrome_impl)(disks, start, stop, bytes, ptrs);
9335472bc6SChristoph Hellwig }
9435472bc6SChristoph Hellwig EXPORT_SYMBOL_GPL(raid6_xor_syndrome);
9535472bc6SChristoph Hellwig 
9635472bc6SChristoph Hellwig /*
9735472bc6SChristoph Hellwig  * raid6_can_xor_syndrome - check if raid6_xor_syndrome() can be used
9835472bc6SChristoph Hellwig  *
9935472bc6SChristoph Hellwig  * Returns %true if raid6_can_xor_syndrome() can be used, else %false.
10035472bc6SChristoph Hellwig  */
10135472bc6SChristoph Hellwig bool raid6_can_xor_syndrome(void)
10235472bc6SChristoph Hellwig {
10310f4b8e2SChristoph Hellwig 	return !!static_call_query(raid6_xor_syndrome_impl);
10435472bc6SChristoph Hellwig }
10535472bc6SChristoph Hellwig EXPORT_SYMBOL_GPL(raid6_can_xor_syndrome);
1063626738bSChristoph Hellwig 
10735472bc6SChristoph Hellwig /**
10835472bc6SChristoph Hellwig  * raid6_recov_2data - recover two missing data disks
10935472bc6SChristoph Hellwig  * @disks:	number of "disks" to operate on including parity
11035472bc6SChristoph Hellwig  * @bytes:	length in bytes of each vector
11135472bc6SChristoph Hellwig  * @faila:	first failed data disk index
11235472bc6SChristoph Hellwig  * @failb:	second failed data disk index
11335472bc6SChristoph Hellwig  * @ptrs:	@disks size array of memory pointers
11435472bc6SChristoph Hellwig  *
11535472bc6SChristoph Hellwig  * Rebuild @bytes of missing data in @ptrs[@faila] and @ptrs[@failb] from the
11635472bc6SChristoph Hellwig  * data in the remaining disks and the two parities pointed to by the other
11735472bc6SChristoph Hellwig  * indices between 0 and @disks - 1 in @ptrs.  @disks includes the data disks
11835472bc6SChristoph Hellwig  * and the two parities.  @faila must be smaller than @failb.
11935472bc6SChristoph Hellwig  *
12035472bc6SChristoph Hellwig  * Memory pointed to by each pointer in @ptrs must be page aligned and is
12135472bc6SChristoph Hellwig  * limited to %PAGE_SIZE.
12235472bc6SChristoph Hellwig  */
12335472bc6SChristoph Hellwig void raid6_recov_2data(int disks, size_t bytes, int faila, int failb,
12435472bc6SChristoph Hellwig 		void **ptrs)
12535472bc6SChristoph Hellwig {
12635472bc6SChristoph Hellwig 	WARN_ON_ONCE(!in_task() || irqs_disabled() || softirq_count());
12735472bc6SChristoph Hellwig 	WARN_ON_ONCE(bytes & 511);
12835472bc6SChristoph Hellwig 	WARN_ON_ONCE(bytes > PAGE_SIZE);
12935472bc6SChristoph Hellwig 	WARN_ON_ONCE(failb <= faila);
1303626738bSChristoph Hellwig 
131*dd83de03SChristoph Hellwig 	static_call(raid6_recov_2data_impl)(disks, bytes, faila, failb, ptrs);
13235472bc6SChristoph Hellwig }
13335472bc6SChristoph Hellwig EXPORT_SYMBOL_GPL(raid6_recov_2data);
13435472bc6SChristoph Hellwig 
13535472bc6SChristoph Hellwig /**
13635472bc6SChristoph Hellwig  * raid6_recov_datap - recover a missing data disk and missing P-parity
13735472bc6SChristoph Hellwig  * @disks:	number of "disks" to operate on including parity
13835472bc6SChristoph Hellwig  * @bytes:	length in bytes of each vector
13935472bc6SChristoph Hellwig  * @faila:	failed data disk index
14035472bc6SChristoph Hellwig  * @ptrs:	@disks size array of memory pointers
14135472bc6SChristoph Hellwig  *
14235472bc6SChristoph Hellwig  * Rebuild @bytes of missing data in @ptrs[@faila] and the missing P-parity in
14335472bc6SChristoph Hellwig  * @ptrs[@disks - 2] from the data in the remaining disks and the Q-parity
14435472bc6SChristoph Hellwig  * pointed to by the other indices between 0 and @disks - 1 in @ptrs.  @disks
14535472bc6SChristoph Hellwig  * includes the data disks and the two parities.
14635472bc6SChristoph Hellwig  *
14735472bc6SChristoph Hellwig  * Memory pointed to by each pointer in @ptrs must be page aligned and is
14835472bc6SChristoph Hellwig  * limited to %PAGE_SIZE.
14935472bc6SChristoph Hellwig  */
15035472bc6SChristoph Hellwig void raid6_recov_datap(int disks, size_t bytes, int faila, void **ptrs)
15135472bc6SChristoph Hellwig {
15235472bc6SChristoph Hellwig 	WARN_ON_ONCE(!in_task() || irqs_disabled() || softirq_count());
15335472bc6SChristoph Hellwig 	WARN_ON_ONCE(bytes & 511);
15435472bc6SChristoph Hellwig 	WARN_ON_ONCE(bytes > PAGE_SIZE);
15535472bc6SChristoph Hellwig 
156*dd83de03SChristoph Hellwig 	static_call(raid6_recov_datap_impl)(disks, bytes, faila, ptrs);
15735472bc6SChristoph Hellwig }
15835472bc6SChristoph Hellwig EXPORT_SYMBOL_GPL(raid6_recov_datap);
1593626738bSChristoph Hellwig 
1603626738bSChristoph Hellwig #define RAID6_TIME_JIFFIES_LG2	4
1613626738bSChristoph Hellwig #define RAID6_TEST_DISKS	8
1623626738bSChristoph Hellwig #define RAID6_TEST_DISKS_ORDER	3
1633626738bSChristoph Hellwig 
164adfcf6e8SChristoph Hellwig static int raid6_choose_gen(void *(*const dptrs)[RAID6_TEST_DISKS],
165adfcf6e8SChristoph Hellwig 		const int disks)
1663626738bSChristoph Hellwig {
167adfcf6e8SChristoph Hellwig 	/* work on the second half of the disks */
168adfcf6e8SChristoph Hellwig 	int start = (disks >> 1) - 1, stop = disks - 3;
169adfcf6e8SChristoph Hellwig 	const struct raid6_calls *best = NULL;
170adfcf6e8SChristoph Hellwig 	unsigned long bestgenperf = 0;
171adfcf6e8SChristoph Hellwig 	unsigned int i;
1723626738bSChristoph Hellwig 
173adfcf6e8SChristoph Hellwig 	for (i = 0; i < raid6_nr_algos; i++) {
174adfcf6e8SChristoph Hellwig 		const struct raid6_calls *algo = raid6_algos[i];
175adfcf6e8SChristoph Hellwig 		unsigned long perf = 0, j0, j1;
1763626738bSChristoph Hellwig 
1773626738bSChristoph Hellwig 		preempt_disable();
1783626738bSChristoph Hellwig 		j0 = jiffies;
1793626738bSChristoph Hellwig 		while ((j1 = jiffies) == j0)
1803626738bSChristoph Hellwig 			cpu_relax();
1813626738bSChristoph Hellwig 		while (time_before(jiffies,
1823626738bSChristoph Hellwig 				    j1 + (1<<RAID6_TIME_JIFFIES_LG2))) {
183adfcf6e8SChristoph Hellwig 			algo->gen_syndrome(disks, PAGE_SIZE, *dptrs);
1843626738bSChristoph Hellwig 			perf++;
1853626738bSChristoph Hellwig 		}
1863626738bSChristoph Hellwig 		preempt_enable();
1873626738bSChristoph Hellwig 
1883626738bSChristoph Hellwig 		if (perf > bestgenperf) {
1893626738bSChristoph Hellwig 			bestgenperf = perf;
190adfcf6e8SChristoph Hellwig 			best = algo;
1913626738bSChristoph Hellwig 		}
192adfcf6e8SChristoph Hellwig 		pr_info("raid6: %-8s gen() %5ld MB/s\n", algo->name,
1933626738bSChristoph Hellwig 			(perf * HZ * (disks-2)) >>
1943626738bSChristoph Hellwig 			(20 - PAGE_SHIFT + RAID6_TIME_JIFFIES_LG2));
1953626738bSChristoph Hellwig 	}
1963626738bSChristoph Hellwig 
1973626738bSChristoph Hellwig 	if (!best) {
1983626738bSChristoph Hellwig 		pr_err("raid6: Yikes! No algorithm found!\n");
199adfcf6e8SChristoph Hellwig 		return -EINVAL;
2003626738bSChristoph Hellwig 	}
2013626738bSChristoph Hellwig 
20210f4b8e2SChristoph Hellwig 	static_call_update(raid6_gen_syndrome_impl, best->gen_syndrome);
20310f4b8e2SChristoph Hellwig 	static_call_update(raid6_xor_syndrome_impl, best->xor_syndrome);
2043626738bSChristoph Hellwig 
2053626738bSChristoph Hellwig 	pr_info("raid6: using algorithm %s gen() %ld MB/s\n",
2063626738bSChristoph Hellwig 		best->name,
2073626738bSChristoph Hellwig 		(bestgenperf * HZ * (disks - 2)) >>
2083626738bSChristoph Hellwig 		(20 - PAGE_SHIFT + RAID6_TIME_JIFFIES_LG2));
2093626738bSChristoph Hellwig 
2103626738bSChristoph Hellwig 	if (best->xor_syndrome) {
211adfcf6e8SChristoph Hellwig 		unsigned long perf = 0, j0, j1;
2123626738bSChristoph Hellwig 
2133626738bSChristoph Hellwig 		preempt_disable();
2143626738bSChristoph Hellwig 		j0 = jiffies;
2153626738bSChristoph Hellwig 		while ((j1 = jiffies) == j0)
2163626738bSChristoph Hellwig 			cpu_relax();
2173626738bSChristoph Hellwig 		while (time_before(jiffies,
2183626738bSChristoph Hellwig 				   j1 + (1 << RAID6_TIME_JIFFIES_LG2))) {
2193626738bSChristoph Hellwig 			best->xor_syndrome(disks, start, stop,
2203626738bSChristoph Hellwig 					   PAGE_SIZE, *dptrs);
2213626738bSChristoph Hellwig 			perf++;
2223626738bSChristoph Hellwig 		}
2233626738bSChristoph Hellwig 		preempt_enable();
2243626738bSChristoph Hellwig 
2253626738bSChristoph Hellwig 		pr_info("raid6: .... xor() %ld MB/s, rmw enabled\n",
2263626738bSChristoph Hellwig 			(perf * HZ * (disks - 2)) >>
2273626738bSChristoph Hellwig 			(20 - PAGE_SHIFT + RAID6_TIME_JIFFIES_LG2 + 1));
2283626738bSChristoph Hellwig 	}
2293626738bSChristoph Hellwig 
230adfcf6e8SChristoph Hellwig 	return 0;
2313626738bSChristoph Hellwig }
2323626738bSChristoph Hellwig 
2333626738bSChristoph Hellwig 
2343626738bSChristoph Hellwig /* Try to pick the best algorithm */
2353626738bSChristoph Hellwig /* This code uses the gfmul table as convenient data set to abuse */
2363626738bSChristoph Hellwig 
2373626738bSChristoph Hellwig static int __init raid6_select_algo(void)
2383626738bSChristoph Hellwig {
2393626738bSChristoph Hellwig 	const int disks = RAID6_TEST_DISKS;
2403626738bSChristoph Hellwig 	char *disk_ptr, *p;
2413626738bSChristoph Hellwig 	void *dptrs[RAID6_TEST_DISKS];
2423626738bSChristoph Hellwig 	int i, cycle;
243adfcf6e8SChristoph Hellwig 	int error;
244adfcf6e8SChristoph Hellwig 
245adfcf6e8SChristoph Hellwig 	if (!IS_ENABLED(CONFIG_RAID6_PQ_BENCHMARK) || raid6_nr_algos == 1) {
246adfcf6e8SChristoph Hellwig 		pr_info("raid6: skipped pq benchmark and selected %s\n",
247adfcf6e8SChristoph Hellwig 			raid6_algos[raid6_nr_algos - 1]->name);
24810f4b8e2SChristoph Hellwig 		static_call_update(raid6_gen_syndrome_impl,
24910f4b8e2SChristoph Hellwig 				raid6_algos[raid6_nr_algos - 1]->gen_syndrome);
25010f4b8e2SChristoph Hellwig 		static_call_update(raid6_xor_syndrome_impl,
25110f4b8e2SChristoph Hellwig 				raid6_algos[raid6_nr_algos - 1]->xor_syndrome);
252adfcf6e8SChristoph Hellwig 		return 0;
253adfcf6e8SChristoph Hellwig 	}
2543626738bSChristoph Hellwig 
2553626738bSChristoph Hellwig 	/* prepare the buffer and fill it circularly with gfmul table */
2563626738bSChristoph Hellwig 	disk_ptr = (char *)__get_free_pages(GFP_KERNEL, RAID6_TEST_DISKS_ORDER);
2573626738bSChristoph Hellwig 	if (!disk_ptr) {
2583626738bSChristoph Hellwig 		pr_err("raid6: Yikes!  No memory available.\n");
2593626738bSChristoph Hellwig 		return -ENOMEM;
2603626738bSChristoph Hellwig 	}
2613626738bSChristoph Hellwig 
2623626738bSChristoph Hellwig 	p = disk_ptr;
2633626738bSChristoph Hellwig 	for (i = 0; i < disks; i++)
2643626738bSChristoph Hellwig 		dptrs[i] = p + PAGE_SIZE * i;
2653626738bSChristoph Hellwig 
2663626738bSChristoph Hellwig 	cycle = ((disks - 2) * PAGE_SIZE) / 65536;
2673626738bSChristoph Hellwig 	for (i = 0; i < cycle; i++) {
2683626738bSChristoph Hellwig 		memcpy(p, raid6_gfmul, 65536);
2693626738bSChristoph Hellwig 		p += 65536;
2703626738bSChristoph Hellwig 	}
2713626738bSChristoph Hellwig 
2723626738bSChristoph Hellwig 	if ((disks - 2) * PAGE_SIZE % 65536)
2733626738bSChristoph Hellwig 		memcpy(p, raid6_gfmul, (disks - 2) * PAGE_SIZE % 65536);
2743626738bSChristoph Hellwig 
2753626738bSChristoph Hellwig 	/* select raid gen_syndrome function */
276adfcf6e8SChristoph Hellwig 	error = raid6_choose_gen(&dptrs, disks);
2773626738bSChristoph Hellwig 
2783626738bSChristoph Hellwig 	free_pages((unsigned long)disk_ptr, RAID6_TEST_DISKS_ORDER);
2793626738bSChristoph Hellwig 
280adfcf6e8SChristoph Hellwig 	return error;
2813626738bSChristoph Hellwig }
2823626738bSChristoph Hellwig 
283adfcf6e8SChristoph Hellwig /*
284adfcf6e8SChristoph Hellwig  * Register a RAID6 P/Q generation algorithm.  The most optimized/unrolled
285adfcf6e8SChristoph Hellwig  * implementation should be registered last so it will be selected when the
286adfcf6e8SChristoph Hellwig  * boot-time benchmark is disabled.
287adfcf6e8SChristoph Hellwig  */
288adfcf6e8SChristoph Hellwig void __init raid6_algo_add(const struct raid6_calls *algo)
2893626738bSChristoph Hellwig {
290adfcf6e8SChristoph Hellwig 	if (WARN_ON_ONCE(raid6_nr_algos == RAID6_MAX_ALGOS))
291adfcf6e8SChristoph Hellwig 		return;
292adfcf6e8SChristoph Hellwig 	raid6_algos[raid6_nr_algos++] = algo;
2933626738bSChristoph Hellwig }
2943626738bSChristoph Hellwig 
295adfcf6e8SChristoph Hellwig void __init raid6_algo_add_default(void)
296adfcf6e8SChristoph Hellwig {
297adfcf6e8SChristoph Hellwig 	raid6_algo_add(&raid6_intx1);
298adfcf6e8SChristoph Hellwig 	raid6_algo_add(&raid6_intx2);
299adfcf6e8SChristoph Hellwig 	raid6_algo_add(&raid6_intx4);
300adfcf6e8SChristoph Hellwig 	raid6_algo_add(&raid6_intx8);
301adfcf6e8SChristoph Hellwig }
302adfcf6e8SChristoph Hellwig 
303adfcf6e8SChristoph Hellwig void __init raid6_recov_algo_add(const struct raid6_recov_calls *algo)
304adfcf6e8SChristoph Hellwig {
305adfcf6e8SChristoph Hellwig 	if (WARN_ON_ONCE(raid6_recov_algo))
306adfcf6e8SChristoph Hellwig 		return;
307adfcf6e8SChristoph Hellwig 	raid6_recov_algo = algo;
308adfcf6e8SChristoph Hellwig }
309adfcf6e8SChristoph Hellwig 
310adfcf6e8SChristoph Hellwig #ifdef CONFIG_RAID6_PQ_ARCH
311adfcf6e8SChristoph Hellwig #include "pq_arch.h"
312adfcf6e8SChristoph Hellwig #else
313adfcf6e8SChristoph Hellwig static inline void arch_raid6_init(void)
314adfcf6e8SChristoph Hellwig {
315adfcf6e8SChristoph Hellwig 	raid6_algo_add_default();
316adfcf6e8SChristoph Hellwig }
317adfcf6e8SChristoph Hellwig #endif /* CONFIG_RAID6_PQ_ARCH */
318adfcf6e8SChristoph Hellwig 
319adfcf6e8SChristoph Hellwig static int __init raid6_init(void)
320adfcf6e8SChristoph Hellwig {
321adfcf6e8SChristoph Hellwig 	/*
322adfcf6e8SChristoph Hellwig 	 * Architectures providing arch_raid6_init must add all PQ generation
323adfcf6e8SChristoph Hellwig 	 * algorithms they want to consider in arch_raid6_init(), including
324adfcf6e8SChristoph Hellwig 	 * the generic ones using raid6_algo_add_default() if wanted.
325adfcf6e8SChristoph Hellwig 	 */
326adfcf6e8SChristoph Hellwig 	arch_raid6_init();
327adfcf6e8SChristoph Hellwig 
328adfcf6e8SChristoph Hellwig 	/*
329adfcf6e8SChristoph Hellwig 	 * Architectures don't have to set a recovery algorithm, we'll just pick
330adfcf6e8SChristoph Hellwig 	 * the generic integer one if none was set.
331adfcf6e8SChristoph Hellwig 	 */
332adfcf6e8SChristoph Hellwig 	if (!raid6_recov_algo)
333adfcf6e8SChristoph Hellwig 		raid6_recov_algo = &raid6_recov_intx1;
334*dd83de03SChristoph Hellwig 	static_call_update(raid6_recov_2data_impl, raid6_recov_algo->data2);
335*dd83de03SChristoph Hellwig 	static_call_update(raid6_recov_datap_impl, raid6_recov_algo->datap);
336adfcf6e8SChristoph Hellwig 	pr_info("raid6: using %s recovery algorithm\n", raid6_recov_algo->name);
337adfcf6e8SChristoph Hellwig 
338adfcf6e8SChristoph Hellwig 	return raid6_select_algo();
339adfcf6e8SChristoph Hellwig }
340adfcf6e8SChristoph Hellwig 
341adfcf6e8SChristoph Hellwig static void __exit raid6_exit(void)
342adfcf6e8SChristoph Hellwig {
343adfcf6e8SChristoph Hellwig }
344adfcf6e8SChristoph Hellwig 
345adfcf6e8SChristoph Hellwig subsys_initcall(raid6_init);
3463626738bSChristoph Hellwig module_exit(raid6_exit);
3473626738bSChristoph Hellwig MODULE_LICENSE("GPL");
3483626738bSChristoph Hellwig MODULE_DESCRIPTION("RAID6 Q-syndrome calculations");
349adfcf6e8SChristoph Hellwig 
350adfcf6e8SChristoph Hellwig #if IS_ENABLED(CONFIG_RAID6_PQ_KUNIT_TEST)
351adfcf6e8SChristoph Hellwig const struct raid6_calls *raid6_algo_find(unsigned int idx)
352adfcf6e8SChristoph Hellwig {
353adfcf6e8SChristoph Hellwig 	if (idx >= raid6_nr_algos) {
354adfcf6e8SChristoph Hellwig 		/*
355adfcf6e8SChristoph Hellwig 		 * Always include the simplest generic integer implementation in
356adfcf6e8SChristoph Hellwig 		 * the unit tests as a baseline.
357adfcf6e8SChristoph Hellwig 		 */
358adfcf6e8SChristoph Hellwig 		if (idx == raid6_nr_algos &&
359adfcf6e8SChristoph Hellwig 		    raid6_algos[0] != &raid6_intx1)
360adfcf6e8SChristoph Hellwig 			return &raid6_intx1;
361adfcf6e8SChristoph Hellwig 		return NULL;
362adfcf6e8SChristoph Hellwig 	}
363adfcf6e8SChristoph Hellwig 	return raid6_algos[idx];
364adfcf6e8SChristoph Hellwig }
365adfcf6e8SChristoph Hellwig EXPORT_SYMBOL_IF_KUNIT(raid6_algo_find);
366adfcf6e8SChristoph Hellwig 
367adfcf6e8SChristoph Hellwig const struct raid6_recov_calls *raid6_recov_algo_find(unsigned int idx)
368adfcf6e8SChristoph Hellwig {
369adfcf6e8SChristoph Hellwig 	switch (idx) {
370adfcf6e8SChristoph Hellwig 	case 0:
371adfcf6e8SChristoph Hellwig 		/* always test the generic integer implementation */
372adfcf6e8SChristoph Hellwig 		return &raid6_recov_intx1;
373adfcf6e8SChristoph Hellwig 	case 1:
374adfcf6e8SChristoph Hellwig 		/* test the optimized implementation if there is one */
375adfcf6e8SChristoph Hellwig 		if (raid6_recov_algo != &raid6_recov_intx1)
376adfcf6e8SChristoph Hellwig 			return raid6_recov_algo;
377adfcf6e8SChristoph Hellwig 		return NULL;
378adfcf6e8SChristoph Hellwig 	default:
379adfcf6e8SChristoph Hellwig 		return NULL;
380adfcf6e8SChristoph Hellwig 	}
381adfcf6e8SChristoph Hellwig }
382adfcf6e8SChristoph Hellwig EXPORT_SYMBOL_IF_KUNIT(raid6_recov_algo_find);
383adfcf6e8SChristoph Hellwig #endif /* CONFIG_RAID6_PQ_KUNIT_TEST */
384