xref: /linux/lib/raid/raid6/algos.c (revision 30bf04bd13a58cd9b877589569aa0abd06f04e52)
13626738bSChristoph Hellwig // SPDX-License-Identifier: GPL-2.0-or-later
23626738bSChristoph Hellwig /*
3*30bf04bdSChristoph 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>
1110f4b8e2SChristoph Hellwig #include <linux/static_call.h>
123626738bSChristoph Hellwig #include <kunit/visibility.h>
13769d603fSChristoph Hellwig #include "algos.h"
143626738bSChristoph Hellwig 
15adfcf6e8SChristoph Hellwig #define RAID6_MAX_ALGOS		16
16adfcf6e8SChristoph Hellwig static const struct raid6_calls *raid6_algos[RAID6_MAX_ALGOS];
17adfcf6e8SChristoph Hellwig static unsigned int raid6_nr_algos;
1835472bc6SChristoph Hellwig static const struct raid6_recov_calls *raid6_recov_algo;
1935472bc6SChristoph Hellwig 
2035472bc6SChristoph Hellwig /* Selected algorithm */
2110f4b8e2SChristoph Hellwig DEFINE_STATIC_CALL_NULL(raid6_gen_syndrome_impl, *raid6_intx1.gen_syndrome);
2210f4b8e2SChristoph Hellwig DEFINE_STATIC_CALL_NULL(raid6_xor_syndrome_impl, *raid6_intx1.xor_syndrome);
23dd83de03SChristoph Hellwig DEFINE_STATIC_CALL_NULL(raid6_recov_2data_impl, *raid6_recov_intx1.data2);
24dd83de03SChristoph Hellwig DEFINE_STATIC_CALL_NULL(raid6_recov_datap_impl, *raid6_recov_intx1.datap);
2535472bc6SChristoph Hellwig 
2635472bc6SChristoph Hellwig /**
2735472bc6SChristoph Hellwig  * raid6_gen_syndrome - generate RAID6 P/Q parity
2835472bc6SChristoph Hellwig  * @disks:	number of "disks" to operate on including parity
2935472bc6SChristoph Hellwig  * @bytes:	length in bytes of each vector
3035472bc6SChristoph Hellwig  * @ptrs:	@disks size array of memory pointers
3135472bc6SChristoph Hellwig  *
3235472bc6SChristoph Hellwig  * Generate @bytes worth of RAID6 P and Q parity in @ptrs[@disks - 2] and
3335472bc6SChristoph Hellwig  * @ptrs[@disks - 1] respectively from the memory pointed to by @ptrs[0] to
3435472bc6SChristoph Hellwig  * @ptrs[@disks - 3].
3535472bc6SChristoph Hellwig  *
3635472bc6SChristoph Hellwig  * @disks must be at least 4, and the memory pointed to by each member of @ptrs
3735472bc6SChristoph Hellwig  * must be at least 64-byte aligned.  @bytes must be non-zero and a multiple of
3835472bc6SChristoph Hellwig  * 512.
3935472bc6SChristoph Hellwig  *
4035472bc6SChristoph Hellwig  * See https://kernel.org/pub/linux/kernel/people/hpa/raid6.pdf for underlying
4135472bc6SChristoph Hellwig  * algorithm.
4235472bc6SChristoph Hellwig  */
4335472bc6SChristoph Hellwig void raid6_gen_syndrome(int disks, size_t bytes, void **ptrs)
4435472bc6SChristoph Hellwig {
4535472bc6SChristoph Hellwig 	WARN_ON_ONCE(!in_task() || irqs_disabled() || softirq_count());
4635472bc6SChristoph Hellwig 	WARN_ON_ONCE(bytes & 511);
472790045aSChristoph Hellwig 	WARN_ON_ONCE(disks < RAID6_MIN_DISKS);
4835472bc6SChristoph Hellwig 
4910f4b8e2SChristoph Hellwig 	static_call(raid6_gen_syndrome_impl)(disks, bytes, ptrs);
5035472bc6SChristoph Hellwig }
5135472bc6SChristoph Hellwig EXPORT_SYMBOL_GPL(raid6_gen_syndrome);
5235472bc6SChristoph Hellwig 
5335472bc6SChristoph Hellwig /**
5435472bc6SChristoph Hellwig  * raid6_xor_syndrome - update RAID6 P/Q parity
5535472bc6SChristoph Hellwig  * @disks:	number of "disks" to operate on including parity
5635472bc6SChristoph Hellwig  * @start:	first index into @disk to update
5735472bc6SChristoph Hellwig  * @stop:	last index into @disk to update
5835472bc6SChristoph Hellwig  * @bytes:	length in bytes of each vector
5935472bc6SChristoph Hellwig  * @ptrs:	@disks size array of memory pointers
6035472bc6SChristoph Hellwig  *
6135472bc6SChristoph Hellwig  * Update @bytes worth of RAID6 P and Q parity in @ptrs[@disks - 2] and
6235472bc6SChristoph Hellwig  * @ptrs[@disks - 1] respectively for the memory pointed to by
6335472bc6SChristoph Hellwig  * @ptrs[@start..@stop].
6435472bc6SChristoph Hellwig  *
6535472bc6SChristoph Hellwig  * This is used to update parity in place using the following sequence:
6635472bc6SChristoph Hellwig  *
6735472bc6SChristoph Hellwig  * 1) call raid6_xor_syndrome(disk, start, stop, ...) for the existing data.
6835472bc6SChristoph Hellwig  * 2) update the the data in @ptrs[@start..@stop].
6935472bc6SChristoph Hellwig  * 3) call raid6_xor_syndrome(disk, start, stop, ...) for the new data.
7035472bc6SChristoph Hellwig  *
7135472bc6SChristoph Hellwig  * Data between @start and @stop that is not changed should be filled
7235472bc6SChristoph Hellwig  * with a pointer to the kernel zero page.
7335472bc6SChristoph Hellwig  *
7435472bc6SChristoph Hellwig  * @disks must be at least 4, and the memory pointed to by each member of @ptrs
7535472bc6SChristoph Hellwig  * must be at least 64-byte aligned.  @bytes must be non-zero and a multiple of
7635472bc6SChristoph Hellwig  * 512.  @stop must be larger or equal to @start.
7735472bc6SChristoph Hellwig  */
7835472bc6SChristoph Hellwig void raid6_xor_syndrome(int disks, int start, int stop, size_t bytes,
7935472bc6SChristoph Hellwig 		void **ptrs)
8035472bc6SChristoph Hellwig {
8135472bc6SChristoph Hellwig 	WARN_ON_ONCE(!in_task() || irqs_disabled() || softirq_count());
8235472bc6SChristoph Hellwig 	WARN_ON_ONCE(bytes & 511);
832790045aSChristoph Hellwig 	WARN_ON_ONCE(disks < RAID6_MIN_DISKS);
8435472bc6SChristoph Hellwig 	WARN_ON_ONCE(stop < start);
8535472bc6SChristoph Hellwig 
8610f4b8e2SChristoph Hellwig 	static_call(raid6_xor_syndrome_impl)(disks, start, stop, bytes, ptrs);
8735472bc6SChristoph Hellwig }
8835472bc6SChristoph Hellwig EXPORT_SYMBOL_GPL(raid6_xor_syndrome);
8935472bc6SChristoph Hellwig 
9035472bc6SChristoph Hellwig /*
9135472bc6SChristoph Hellwig  * raid6_can_xor_syndrome - check if raid6_xor_syndrome() can be used
9235472bc6SChristoph Hellwig  *
9335472bc6SChristoph Hellwig  * Returns %true if raid6_can_xor_syndrome() can be used, else %false.
9435472bc6SChristoph Hellwig  */
9535472bc6SChristoph Hellwig bool raid6_can_xor_syndrome(void)
9635472bc6SChristoph Hellwig {
9710f4b8e2SChristoph Hellwig 	return !!static_call_query(raid6_xor_syndrome_impl);
9835472bc6SChristoph Hellwig }
9935472bc6SChristoph Hellwig EXPORT_SYMBOL_GPL(raid6_can_xor_syndrome);
1003626738bSChristoph Hellwig 
10135472bc6SChristoph Hellwig /**
10235472bc6SChristoph Hellwig  * raid6_recov_2data - recover two missing data disks
10335472bc6SChristoph Hellwig  * @disks:	number of "disks" to operate on including parity
10435472bc6SChristoph Hellwig  * @bytes:	length in bytes of each vector
10535472bc6SChristoph Hellwig  * @faila:	first failed data disk index
10635472bc6SChristoph Hellwig  * @failb:	second failed data disk index
10735472bc6SChristoph Hellwig  * @ptrs:	@disks size array of memory pointers
10835472bc6SChristoph Hellwig  *
10935472bc6SChristoph Hellwig  * Rebuild @bytes of missing data in @ptrs[@faila] and @ptrs[@failb] from the
11035472bc6SChristoph Hellwig  * data in the remaining disks and the two parities pointed to by the other
11135472bc6SChristoph Hellwig  * indices between 0 and @disks - 1 in @ptrs.  @disks includes the data disks
11235472bc6SChristoph Hellwig  * and the two parities.  @faila must be smaller than @failb.
11335472bc6SChristoph Hellwig  *
11435472bc6SChristoph Hellwig  * Memory pointed to by each pointer in @ptrs must be page aligned and is
11535472bc6SChristoph Hellwig  * limited to %PAGE_SIZE.
11635472bc6SChristoph Hellwig  */
11735472bc6SChristoph Hellwig void raid6_recov_2data(int disks, size_t bytes, int faila, int failb,
11835472bc6SChristoph Hellwig 		void **ptrs)
11935472bc6SChristoph Hellwig {
12035472bc6SChristoph Hellwig 	WARN_ON_ONCE(!in_task() || irqs_disabled() || softirq_count());
12135472bc6SChristoph Hellwig 	WARN_ON_ONCE(bytes & 511);
12235472bc6SChristoph Hellwig 	WARN_ON_ONCE(bytes > PAGE_SIZE);
12335472bc6SChristoph Hellwig 	WARN_ON_ONCE(failb <= faila);
1243626738bSChristoph Hellwig 
125dd83de03SChristoph Hellwig 	static_call(raid6_recov_2data_impl)(disks, bytes, faila, failb, ptrs);
12635472bc6SChristoph Hellwig }
12735472bc6SChristoph Hellwig EXPORT_SYMBOL_GPL(raid6_recov_2data);
12835472bc6SChristoph Hellwig 
12935472bc6SChristoph Hellwig /**
13035472bc6SChristoph Hellwig  * raid6_recov_datap - recover a missing data disk and missing P-parity
13135472bc6SChristoph Hellwig  * @disks:	number of "disks" to operate on including parity
13235472bc6SChristoph Hellwig  * @bytes:	length in bytes of each vector
13335472bc6SChristoph Hellwig  * @faila:	failed data disk index
13435472bc6SChristoph Hellwig  * @ptrs:	@disks size array of memory pointers
13535472bc6SChristoph Hellwig  *
13635472bc6SChristoph Hellwig  * Rebuild @bytes of missing data in @ptrs[@faila] and the missing P-parity in
13735472bc6SChristoph Hellwig  * @ptrs[@disks - 2] from the data in the remaining disks and the Q-parity
13835472bc6SChristoph Hellwig  * pointed to by the other indices between 0 and @disks - 1 in @ptrs.  @disks
13935472bc6SChristoph Hellwig  * includes the data disks and the two parities.
14035472bc6SChristoph Hellwig  *
14135472bc6SChristoph Hellwig  * Memory pointed to by each pointer in @ptrs must be page aligned and is
14235472bc6SChristoph Hellwig  * limited to %PAGE_SIZE.
14335472bc6SChristoph Hellwig  */
14435472bc6SChristoph Hellwig void raid6_recov_datap(int disks, size_t bytes, int faila, void **ptrs)
14535472bc6SChristoph Hellwig {
14635472bc6SChristoph Hellwig 	WARN_ON_ONCE(!in_task() || irqs_disabled() || softirq_count());
14735472bc6SChristoph Hellwig 	WARN_ON_ONCE(bytes & 511);
14835472bc6SChristoph Hellwig 	WARN_ON_ONCE(bytes > PAGE_SIZE);
14935472bc6SChristoph Hellwig 
150dd83de03SChristoph Hellwig 	static_call(raid6_recov_datap_impl)(disks, bytes, faila, ptrs);
15135472bc6SChristoph Hellwig }
15235472bc6SChristoph Hellwig EXPORT_SYMBOL_GPL(raid6_recov_datap);
1533626738bSChristoph Hellwig 
1543626738bSChristoph Hellwig #define RAID6_TIME_JIFFIES_LG2	4
1553626738bSChristoph Hellwig #define RAID6_TEST_DISKS	8
1563626738bSChristoph Hellwig #define RAID6_TEST_DISKS_ORDER	3
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 */
2503626738bSChristoph Hellwig 	disk_ptr = (char *)__get_free_pages(GFP_KERNEL, RAID6_TEST_DISKS_ORDER);
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 
2723626738bSChristoph Hellwig 	free_pages((unsigned long)disk_ptr, RAID6_TEST_DISKS_ORDER);
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