Skip to contentSkip to conversation

lib/raid6: Optimize raid6_select_algo to ensure

PATCH RESEND In review [email protected] 3 participants 8 messages Mar 20 – Mar 25 1 message not in the archive

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

v3 cover lib/raid6: Optimize raid6_select_algo to ensure

From: sunliming <[email protected]>

The selection of RAID6 PQ functions involves a dual-strategy approach:
prioritizing startup speed leads to quickly choosing a usable algorithm, while prioritizing performance leads to selecting an optimal algorithm via benchmarking. This choice is determined by the RAID6_PQ_BENCHMARK configuration. This patch series achieves both fast startup and optimal algorithm selection by initially choosing an algorithm quickly at startup, then asynchronously determining the optimal algorithm through benchmarking. Since all RAID6 PQ function algorithms are functionally equivalent despite performance differences, this approach should be effective.

---
Changes in v3:
Remove the __init annotation from raid6_benchmark_work and benchmark_work_func,
as they are still referenced during initialization.
Changes in v2:
Select the highest-priority algorithm instead of the first one. Add the cancel_work_sync function in the exit function to handle the
work queue cleanup.
---
sunliming (3):
ib/raid6: Divide the raid6 algorithm selection process into two parts lib/raid6: Optimizing the raid6_select_algo time through asynchronous
processing
lib/raid6: Delete the RAID6_PQ_BENCHMARK config

 include/linux/raid/pq.h |  3 --
 lib/Kconfig             |  8 ----
 lib/raid6/algos.c       | 88 ++++++++++++++++++++++++++++++-----------
 3 files changed, 65 insertions(+), 34 deletions(-)
Signature · 1 line
2.25.1
#1 lore raw

PATCH RESEND v3 1/3 lib/raid6: Divide the raid6 algorithm selection process into two parts

From: sunliming <[email protected]>

Divide the RAID6 algorithm selection process into two parts: fast selection and benchmark selection. To prepare for the asynchronous processing of the benchmark phase.

---
 lib/raid6/algos.c | 76 +++++++++++++++++++++++++++++++++--------------
 1 file changed, 54 insertions(+), 22 deletions(-)
lib/raid6/algos.c +54 -22
index 799e0e5eac26..c21e3ad99d97 100644
@@ -152,8 +152,32 @@ static inline const struct raid6_recov_calls *raid6_choose_recov(void)
 	return best;
 }
 
-static inline const struct raid6_calls *raid6_choose_gen(
-	void *(*const dptrs)[RAID6_TEST_DISKS], const int disks)
+/* Quick selection: select the highest priority valid algorithm. */
+static inline int raid6_choose_gen_fast(void)
+{
+	int ret = 0;
+	const struct raid6_calls *const *algo;
+	const struct raid6_calls *best = NULL;
+
+	for (best = NULL, algo = raid6_algos; *algo; algo++)
+		if (!best || (*algo)->priority > best->priority)
+			if (!(*algo)->valid || (*algo)->valid())
+				best = *algo;
+
+	if (best) {
+		raid6_call = *best;
+		pr_info("raid6: skipped pq benchmark and selected %s\n",
+				best->name);
+	} else {
+		pr_err("raid6: No valid algorithm found even for fast selection!\n");
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static inline const struct raid6_calls *raid6_gen_benchmark(
+		void *(*const dptrs)[RAID6_TEST_DISKS], const int disks)
 {
 	unsigned long perf, bestgenperf, j0, j1;
 	int start = (disks>>1)-1, stop = disks-3;	/* work on the second half of the disks */
@@ -165,11 +189,6 @@ static inline const struct raid6_calls *raid6_choose_gen(
 			if ((*algo)->valid && !(*algo)->valid())
 				continue;
 
-			if (!IS_ENABLED(CONFIG_RAID6_PQ_BENCHMARK)) {
-				best = *algo;
-				break;
-			}
-
 			perf = 0;
 
 			preempt_disable();
@@ -200,12 +219,6 @@ static inline const struct raid6_calls *raid6_choose_gen(
 
 	raid6_call = *best;
 
-	if (!IS_ENABLED(CONFIG_RAID6_PQ_BENCHMARK)) {
-		pr_info("raid6: skipped pq benchmark and selected %s\n",
-			best->name);
-		goto out;
-	}
-
 	pr_info("raid6: using algorithm %s gen() %ld MB/s\n",
 		best->name,
 		(bestgenperf * HZ * (disks - 2)) >>
@@ -239,15 +252,13 @@ static inline const struct raid6_calls *raid6_choose_gen(
 /* Try to pick the best algorithm */
 /* This code uses the gfmul table as convenient data set to abuse */
 
-int __init raid6_select_algo(void)
+static int raid6_choose_gen_benmark(void)
 {
 	const int disks = RAID6_TEST_DISKS;
-
 	const struct raid6_calls *gen_best;
-	const struct raid6_recov_calls *rec_best;
 	char *disk_ptr, *p;
 	void *dptrs[RAID6_TEST_DISKS];
-	int i, cycle;
+	int i, cycle, ret = 0;
 
 	/* prepare the buffer and fill it circularly with gfmul table */
 	disk_ptr = (char *)__get_free_pages(GFP_KERNEL, RAID6_TEST_DISKS_ORDER);
@@ -269,15 +280,36 @@ int __init raid6_select_algo(void)
 	if ((disks - 2) * PAGE_SIZE % 65536)
 		memcpy(p, raid6_gfmul, (disks - 2) * PAGE_SIZE % 65536);
 
-	/* select raid gen_syndrome function */
-	gen_best = raid6_choose_gen(&dptrs, disks);
+	gen_best = raid6_gen_benchmark(&dptrs, disks);
+	if (!gen_best)
+		ret = -EINVAL;
+
+	free_pages((unsigned long)disk_ptr, RAID6_TEST_DISKS_ORDER);
+
+	return ret;
+}
+
+int __init raid6_select_algo(void)
+{
+	int ret = 0;
+	const struct raid6_recov_calls *rec_best = NULL;
+
+	/* select raid gen_syndrome functions */
+	if (!IS_ENABLED(CONFIG_RAID6_PQ_BENCHMARK))
+		ret = raid6_choose_gen_fast();
+	else
+		ret = raid6_choose_gen_benmark();
+
+	if (ret < 0)
+		goto out;
 
 	/* select raid recover functions */
 	rec_best = raid6_choose_recov();
+	if (!rec_best)
+		ret = -EINVAL;
 
-	free_pages((unsigned long)disk_ptr, RAID6_TEST_DISKS_ORDER);
-
-	return gen_best && rec_best ? 0 : -EINVAL;
+out:
+	return ret;
 }
 
 static void raid6_exit(void)
Signature · 1 line
2.25.1
#2 lore raw

Re: [PATCH RESEND v3 1/3] lib/raid6: Divide the raid6 algorithm selection process into two parts

I've fixed this slightly different in my "cleanup the RAID6 P/Q library" series by simply not considering non-priority algorithms at all.

#3 lore raw

PATCH RESEND v3 2/3 lib/raid6: Optimizing the raid6_select_algo time through asynchronous processing

In reply to [email protected]

From: sunliming <[email protected]>

Optimizing the raid6_select_algo time. In raid6_select_algo(), an raid6 gen algorithm is first selected quickly through synchronous processing, while the time-consuming process of selecting the optimal algorithm via benchmarking is handled asynchronously. This approach speeds up the overall startup time and ultimately ensures the selection of an optimal algorithm.

---
 lib/raid6/algos.c | 30 ++++++++++++++++++++----------
 1 file changed, 20 insertions(+), 10 deletions(-)
lib/raid6/algos.c +20 -10
index c21e3ad99d97..b8b5515ac7a6 100644
@@ -12,6 +12,7 @@
  */
 
 #include <linux/raid/pq.h>
+#include <linux/workqueue.h>
 #ifndef __KERNEL__
 #include <sys/mman.h>
 #include <stdio.h>
@@ -166,7 +167,7 @@ static inline int raid6_choose_gen_fast(void)
 
 	if (best) {
 		raid6_call = *best;
-		pr_info("raid6: skipped pq benchmark and selected %s\n",
+		pr_info("raid6: raid6: fast selected %s, async benchmark pending\n",
 				best->name);
 	} else {
 		pr_err("raid6: No valid algorithm found even for fast selection!\n");
@@ -213,7 +214,7 @@ static inline const struct raid6_calls *raid6_gen_benchmark(
 	}
 
 	if (!best) {
-		pr_err("raid6: Yikes! No algorithm found!\n");
+		pr_warn("raid6: async benchmark failed to find any algorithm\n");
 		goto out;
 	}
 
@@ -289,24 +290,33 @@ static int raid6_choose_gen_benmark(void)
 	return ret;
 }
 
+static struct work_struct raid6_benchmark_work;
+
+static void benchmark_work_func(struct work_struct *work)
+{
+	raid6_choose_gen_benmark();
+}
+
 int __init raid6_select_algo(void)
 {
 	int ret = 0;
 	const struct raid6_recov_calls *rec_best = NULL;
 
-	/* select raid gen_syndrome functions */
-	if (!IS_ENABLED(CONFIG_RAID6_PQ_BENCHMARK))
-		ret = raid6_choose_gen_fast();
-	else
-		ret = raid6_choose_gen_benmark();
-
+	/* phase 1: synchronous fast selection generation algorithm */
+	ret = raid6_choose_gen_fast();
 	if (ret < 0)
 		goto out;
 
 	/* select raid recover functions */
 	rec_best = raid6_choose_recov();
-	if (!rec_best)
+	if (!rec_best) {
 		ret = -EINVAL;
+		goto out;
+	}
+
+	/* phase 2: asynchronous performance benchmarking */
+	INIT_WORK(&raid6_benchmark_work, benchmark_work_func);
+	schedule_work(&raid6_benchmark_work);
 
 out:
 	return ret;
@@ -314,7 +324,7 @@ int __init raid6_select_algo(void)
 
 static void raid6_exit(void)
 {
-	do { } while (0);
+	cancel_work_sync(&raid6_benchmark_work);
 }
 
 subsys_initcall(raid6_select_algo);
Signature · 1 line
2.25.1
#4 lore raw

Re: [PATCH RESEND v3 2/3] lib/raid6: Optimizing the raid6_select_algo time through asynchronous processing

Dear sunliming,

Thank you for your patch. Two formal things, and one “real” question.

Am 20.03.26 um 07:07 schrieb [email protected]:

Quoted 1 line

From: sunliming <[email protected]>

It’d be great if you could spell your name with spaces. Sun Li Ming or Sunli Ming? (git config --global user.name "Sun Li Ming" would configure it.)

Quoted 3 lines

Optimizing the raid6_select_algo time. In raid6_select_algo(), an raid6 gen algorithm is first selected quickly through synchronous processing, while the time-consuming process of selecting the optimal algorithm via benchmarking

Please adhere to the text width of 75 characters. (scripts/checkpatch.pl should have found this.)

Quoted 2 lines

is handled asynchronously. This approach speeds up the overall startup time and ultimately ensures the selection of an optimal algorithm.

It’d be great if you shared the test system and exact numbers, and new log messages.

Is there any problem with the benchmark, if it is run asynchronously, because other things are run on the CPU?

If you are working on this, I was wondering if the current approach of selecting the algorithm during each boot is the best choice, and if it shouldn’t be dynamically configurable like schedulers and other things over sysfs and the Linux kernel command line, and also if the chosen algorithm can’t be cached as long as – I guess – the processor/CPU stays the same.

Quoted 1 line

(Ditto regarding the name.)

Kind regards,

Paul

Quoted 84 lines
---
  lib/raid6/algos.c | 30 ++++++++++++++++++++----------
  1 file changed, 20 insertions(+), 10 deletions(-)
lib/raid6/algos.c +20 -10
index c21e3ad99d97..b8b5515ac7a6 100644
@@ -12,6 +12,7 @@
   */
 
  #include <linux/raid/pq.h>
+#include <linux/workqueue.h>
  #ifndef __KERNEL__
  #include <sys/mman.h>
  #include <stdio.h>
@@ -166,7 +167,7 @@ static inline int raid6_choose_gen_fast(void)
 
  	if (best) {
  		raid6_call = *best;
-		pr_info("raid6: skipped pq benchmark and selected %s\n",
+		pr_info("raid6: raid6: fast selected %s, async benchmark pending\n",
  				best->name);
  	} else {
  		pr_err("raid6: No valid algorithm found even for fast selection!\n");
@@ -213,7 +214,7 @@ static inline const struct raid6_calls *raid6_gen_benchmark(
  	}
 
  	if (!best) {
-		pr_err("raid6: Yikes! No algorithm found!\n");
+		pr_warn("raid6: async benchmark failed to find any algorithm\n");
  		goto out;
  	}
 
@@ -289,24 +290,33 @@ static int raid6_choose_gen_benmark(void)
  	return ret;
  }
 
+static struct work_struct raid6_benchmark_work;
+
+static void benchmark_work_func(struct work_struct *work)
+{
+	raid6_choose_gen_benmark();
+}
+
  int __init raid6_select_algo(void)
  {
  	int ret = 0;
  	const struct raid6_recov_calls *rec_best = NULL;
 
-	/* select raid gen_syndrome functions */
-	if (!IS_ENABLED(CONFIG_RAID6_PQ_BENCHMARK))
-		ret = raid6_choose_gen_fast();
-	else
-		ret = raid6_choose_gen_benmark();
-
+	/* phase 1: synchronous fast selection generation algorithm */
+	ret = raid6_choose_gen_fast();
  	if (ret < 0)
  		goto out;
 
  	/* select raid recover functions */
  	rec_best = raid6_choose_recov();
-	if (!rec_best)
+	if (!rec_best) {
  		ret = -EINVAL;
+		goto out;
+	}
+
+	/* phase 2: asynchronous performance benchmarking */
+	INIT_WORK(&raid6_benchmark_work, benchmark_work_func);
+	schedule_work(&raid6_benchmark_work);
 
  out:
  	return ret;
@@ -314,7 +324,7 @@ int __init raid6_select_algo(void)
 
  static void raid6_exit(void)
  {
-	do { } while (0);
+	cancel_work_sync(&raid6_benchmark_work);
  }
 
  subsys_initcall(raid6_select_algo);
#5 lore raw

Re: [PATCH RESEND v3 2/3] lib/raid6: Optimizing the raid6_select_algo time through asynchronous processing

In reply to [email protected]
Quoted 7 lines · On Fri, Mar 20, 2026 at 02:07:49PM +0800, [email protected] wrote:

From: sunliming <[email protected]>

Optimizing the raid6_select_algo time. In raid6_select_algo(), an raid6 gen algorithm is first selected quickly through synchronous processing, while the time-consuming process of selecting the optimal algorithm via benchmarking is handled asynchronously. This approach speeds up the overall startup time and ultimately ensures the selection of an optimal algorithm.

Offloading the selection means it run in parallem with other code, which could affect different test algorithms differently, so this is not a good idea.

A message that is not in the archive (sent off-list or not yet received): [email protected]

#7 lore raw

Re: [PATCH RESEND v3 2/3] lib/raid6: Optimizing the raid6_select_algo time through asynchronous processing

Quoted 4 lines · On Wed, Mar 25, 2026 at 02:25:16AM +0000, [email protected] wrote:
Quoted 3 lines

which could affect different test algorithms differently, so this is not a good idea.

Yes,but this code can be compiled as a module,Could the same problem exist when loading the module?

It could. But at least it is under user control, while offloading during normal boot almost guarantees a spiky load because that is how the boot process works.

#8 lore raw

PATCH RESEND v3 3/3 lib/raid6: Delete the RAID6_PQ_BENCHMARK config

In reply to [email protected]

From: sunliming <[email protected]>

Now RAID6 PQ functions is automatically choosed and the RAID6_PQ_BENCHMARK is not needed.

---
 include/linux/raid/pq.h | 3 ---
 lib/Kconfig             | 8 --------
 2 files changed, 11 deletions(-)
include/linux/raid/pq.h +0 -3
index 2467b3be15c9..6378ec4ae4ba 100644
@@ -67,9 +67,6 @@ extern const char raid6_empty_zero_page[PAGE_SIZE];
 #define MODULE_DESCRIPTION(desc)
 #define subsys_initcall(x)
 #define module_exit(x)
-
-#define IS_ENABLED(x) (x)
-#define CONFIG_RAID6_PQ_BENCHMARK 1
 #endif /* __KERNEL__ */
 
 /* Routine choices */
lib/Kconfig +0 -8
index 2923924bea78..841a0245a2c4 100644
@@ -11,14 +11,6 @@ menu "Library routines"
 config RAID6_PQ
 	tristate
 
-config RAID6_PQ_BENCHMARK
-	bool "Automatically choose fastest RAID6 PQ functions"
-	depends on RAID6_PQ
-	default y
-	help
-	  Benchmark all available RAID6 PQ functions on init and choose the
-	  fastest one.
-
 config LINEAR_RANGES
 	tristate
 
Signature · 1 line
2.25.1