Linux RAID subsystem development
 help / color / mirror / Atom feed
* [PATCH v2 2/3] lib/raid6: Optimizing the raid6_select_algo time through asynchronous processing
From: sunliming @ 2026-02-06  5:43 UTC (permalink / raw)
  To: song, yukuai; +Cc: linux-raid, linux-kernel, linan666, sunliming
In-Reply-To: <20260206054341.106878-1-sunliming@linux.dev>

From: sunliming <sunliming@kylinos.cn>

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.

Signed-off-by: sunliming <sunliming@kylinos.cn>
---
 lib/raid6/algos.c | 30 ++++++++++++++++++++----------
 1 file changed, 20 insertions(+), 10 deletions(-)

diff --git a/lib/raid6/algos.c b/lib/raid6/algos.c
index a14469388b0c..a3c94641a8e5 100644
--- a/lib/raid6/algos.c
+++ b/lib/raid6/algos.c
@@ -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: 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;
 	}
 
@@ -286,24 +287,33 @@ static int raid6_choose_gen_benmark(void)
 	return ret;
 }
 
+static struct work_struct raid6_benchmark_work __initdata;
+
+static __init 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;
@@ -311,7 +321,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);
-- 
2.25.1


^ permalink raw reply related

* [PATCH v2 1/3] lib/raid6: Divide the raid6 algorithm selection process into two parts
From: sunliming @ 2026-02-06  5:43 UTC (permalink / raw)
  To: song, yukuai; +Cc: linux-raid, linux-kernel, linan666, sunliming
In-Reply-To: <20260206054341.106878-1-sunliming@linux.dev>

From: sunliming <sunliming@kylinos.cn>

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

Signed-off-by: sunliming <sunliming@kylinos.cn>
---
 lib/raid6/algos.c | 85 +++++++++++++++++++++++++++++++----------------
 1 file changed, 57 insertions(+), 28 deletions(-)

diff --git a/lib/raid6/algos.c b/lib/raid6/algos.c
index 799e0e5eac26..a14469388b0c 100644
--- a/lib/raid6/algos.c
+++ b/lib/raid6/algos.c
@@ -134,7 +134,7 @@ const struct raid6_recov_calls *const raid6_recov_algos[] = {
 static inline const struct raid6_recov_calls *raid6_choose_recov(void)
 {
 	const struct raid6_recov_calls *const *algo;
-	const struct raid6_recov_calls *best;
+	const struct raid6_recov_calls *best = NULL;
 
 	for (best = NULL, algo = raid6_recov_algos; *algo; algo++)
 		if (!best || (*algo)->priority > best->priority)
@@ -152,24 +152,43 @@ 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: selects the first 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 */
 	const struct raid6_calls *const *algo;
-	const struct raid6_calls *best;
+	const struct raid6_calls *best = NULL;
 
 	for (bestgenperf = 0, best = NULL, algo = raid6_algos; *algo; algo++) {
 		if (!best || (*algo)->priority >= best->priority) {
 			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)) >>
@@ -230,24 +243,19 @@ static inline const struct raid6_calls *raid6_choose_gen(
 			(perf * HZ * (disks - 2)) >>
 			(20 - PAGE_SHIFT + RAID6_TIME_JIFFIES_LG2 + 1));
 	}
-
 out:
 	return best;
 }
 
-
 /* 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;
+	const struct raid6_calls *best = NULL;
+	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 +277,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);
+	best = raid6_gen_benchmark(&dptrs, disks);
+	if (!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)
-- 
2.25.1


^ permalink raw reply related

* [PATCH v2 3/3] lib/raid6: Delete the RAID6_PQ_BENCHMARK config
From: sunliming @ 2026-02-06  5:43 UTC (permalink / raw)
  To: song, yukuai; +Cc: linux-raid, linux-kernel, linan666, sunliming
In-Reply-To: <20260206054341.106878-1-sunliming@linux.dev>

From: sunliming <sunliming@kylinos.cn>

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

Signed-off-by: sunliming <sunliming@kylinos.cn>
---
 include/linux/raid/pq.h | 3 ---
 lib/Kconfig             | 8 --------
 2 files changed, 11 deletions(-)

diff --git a/include/linux/raid/pq.h b/include/linux/raid/pq.h
index 2467b3be15c9..6378ec4ae4ba 100644
--- a/include/linux/raid/pq.h
+++ b/include/linux/raid/pq.h
@@ -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 */
diff --git a/lib/Kconfig b/lib/Kconfig
index 2923924bea78..841a0245a2c4 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -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
 
-- 
2.25.1


^ permalink raw reply related

* Re: [PATCH v2 03/14] md/raid1: use folio for tmppage
From: Li Nan @ 2026-02-06  9:07 UTC (permalink / raw)
  To: yukuai, Li Nan, song; +Cc: xni, linux-raid, linux-kernel, yangerkun, yi.zhang
In-Reply-To: <44521568-3ac0-4374-9c3b-756b8e18192f@fnnas.com>



在 2026/2/5 15:33, Yu Kuai 写道:
> Hi,
> 
> 在 2026/2/5 15:23, Li Nan 写道:
>> Yeah, should we introduce safe_put_folio()? Or just check NULL here.
> 
> Yes, and don't copy implementation. Just convert safe_put_page(page) to
> safe_put_folio(page_folio(page)) first.
> 

page cannot be NULL in page_folio(), and the code would be:

if (page)
   safe_put_folio(page_folio(page))

This also looks odd. Keeping both functions and removing the page one
after the last reference to it in RAID5 is removed seems better. What do
you think?

-- 
Thanks,
Nan


^ permalink raw reply

* Re: [PATCH v2 2/3] lib/raid6: Optimizing the raid6_select_algo time through asynchronous processing
From: kernel test robot @ 2026-02-06 19:22 UTC (permalink / raw)
  To: sunliming, song, yukuai
  Cc: oe-kbuild-all, linux-raid, linux-kernel, linan666, sunliming
In-Reply-To: <20260206054341.106878-3-sunliming@linux.dev>

Hi,

kernel test robot noticed the following build warnings:

[auto build test WARNING on akpm-mm/mm-nonmm-unstable]
[also build test WARNING on linus/master v6.16-rc1 next-20260205]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/sunliming-linux-dev/lib-raid6-Divide-the-raid6-algorithm-selection-process-into-two-parts/20260206-134850
base:   https://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm.git mm-nonmm-unstable
patch link:    https://lore.kernel.org/r/20260206054341.106878-3-sunliming%40linux.dev
patch subject: [PATCH v2 2/3] lib/raid6: Optimizing the raid6_select_algo time through asynchronous processing
config: arm64-allnoconfig-bpf (https://download.01.org/0day-ci/archive/20260206/202602062027.j08UeCWD-lkp@intel.com/config)
compiler: aarch64-linux-gcc (GCC) 15.1.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260206/202602062027.j08UeCWD-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202602062027.j08UeCWD-lkp@intel.com/

All warnings (new ones prefixed by >>, old ones prefixed by <<):

>> WARNING: modpost: lib/raid6/raid6_pq: section mismatch in reference: raid6_exit+0xc (section: .text) -> raid6_benchmark_work (section: .init.data)
WARNING: modpost: lib/raid6/raid6_pq: section mismatch in reference: raid6_exit+0x10 (section: .text) -> raid6_benchmark_work (section: .init.data)

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply

* Re: [PATCH v2 2/3] lib/raid6: Optimizing the raid6_select_algo time through asynchronous processing
From: kernel test robot @ 2026-02-06 19:34 UTC (permalink / raw)
  To: sunliming, song, yukuai
  Cc: llvm, oe-kbuild-all, linux-raid, linux-kernel, linan666,
	sunliming
In-Reply-To: <20260206054341.106878-3-sunliming@linux.dev>

Hi,

kernel test robot noticed the following build warnings:

[auto build test WARNING on akpm-mm/mm-nonmm-unstable]
[also build test WARNING on linus/master v6.16-rc1 next-20260205]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/sunliming-linux-dev/lib-raid6-Divide-the-raid6-algorithm-selection-process-into-two-parts/20260206-134850
base:   https://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm.git mm-nonmm-unstable
patch link:    https://lore.kernel.org/r/20260206054341.106878-3-sunliming%40linux.dev
patch subject: [PATCH v2 2/3] lib/raid6: Optimizing the raid6_select_algo time through asynchronous processing
config: x86_64-kexec (https://download.01.org/0day-ci/archive/20260206/202602062049.sdwQ1NiZ-lkp@intel.com/config)
compiler: clang version 20.1.8 (https://github.com/llvm/llvm-project 87f0227cb60147a26a1eeb4fb06e3b505e9c7261)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260206/202602062049.sdwQ1NiZ-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202602062049.sdwQ1NiZ-lkp@intel.com/

All warnings (new ones prefixed by >>, old ones prefixed by <<):

>> WARNING: modpost: lib/raid6/raid6_pq: section mismatch in reference: cleanup_module+0xc (section: .text) -> raid6_benchmark_work (section: .init.data)

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply

* Re: [PATCH v2 2/3] lib/raid6: Optimizing the raid6_select_algo time through asynchronous processing
From: kernel test robot @ 2026-02-07  6:51 UTC (permalink / raw)
  To: sunliming, song, yukuai
  Cc: oe-kbuild-all, linux-raid, linux-kernel, linan666, sunliming
In-Reply-To: <20260206054341.106878-3-sunliming@linux.dev>

Hi,

kernel test robot noticed the following build warnings:

[auto build test WARNING on akpm-mm/mm-nonmm-unstable]
[also build test WARNING on linus/master v6.19-rc8]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/sunliming-linux-dev/lib-raid6-Divide-the-raid6-algorithm-selection-process-into-two-parts/20260206-134850
base:   https://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm.git mm-nonmm-unstable
patch link:    https://lore.kernel.org/r/20260206054341.106878-3-sunliming%40linux.dev
patch subject: [PATCH v2 2/3] lib/raid6: Optimizing the raid6_select_algo time through asynchronous processing
config: arc-randconfig-001-20260207 (https://download.01.org/0day-ci/archive/20260207/202602071412.HND36w5Q-lkp@intel.com/config)
compiler: arc-linux-gcc (GCC) 9.5.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260207/202602071412.HND36w5Q-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202602071412.HND36w5Q-lkp@intel.com/

All warnings (new ones prefixed by >>, old ones prefixed by <<):

>> WARNING: modpost: lib/raid6/raid6_pq: section mismatch in reference: raid6_exit+0x2 (section: .text) -> raid6_benchmark_work (section: .init.data)

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply

* Re: [PATCH v2 2/3] lib/raid6: Optimizing the raid6_select_algo time through asynchronous processing
From: kernel test robot @ 2026-02-07  7:22 UTC (permalink / raw)
  To: sunliming, song, yukuai
  Cc: oe-kbuild-all, linux-raid, linux-kernel, linan666, sunliming
In-Reply-To: <20260206054341.106878-3-sunliming@linux.dev>

Hi,

kernel test robot noticed the following build warnings:

[auto build test WARNING on akpm-mm/mm-nonmm-unstable]
[also build test WARNING on linus/master v6.19-rc8]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/sunliming-linux-dev/lib-raid6-Divide-the-raid6-algorithm-selection-process-into-two-parts/20260206-134850
base:   https://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm.git mm-nonmm-unstable
patch link:    https://lore.kernel.org/r/20260206054341.106878-3-sunliming%40linux.dev
patch subject: [PATCH v2 2/3] lib/raid6: Optimizing the raid6_select_algo time through asynchronous processing
config: i386-buildonly-randconfig-001-20260207 (https://download.01.org/0day-ci/archive/20260207/202602071500.ctQs6ZeY-lkp@intel.com/config)
compiler: gcc-14 (Debian 14.2.0-19) 14.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260207/202602071500.ctQs6ZeY-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202602071500.ctQs6ZeY-lkp@intel.com/

All warnings (new ones prefixed by >>, old ones prefixed by <<):

>> WARNING: modpost: vmlinux: section mismatch in reference: raid6_exit+0x9 (section: .text) -> raid6_benchmark_work (section: .init.data)

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply

* [PATCH] md/raid10: fix deadlock with check operation and nowait requests
From: Josh Hunt @ 2026-02-10  5:09 UTC (permalink / raw)
  To: song, yukuai, linan122, linux-raid; +Cc: ncroxon, Josh Hunt, stable

When an array check is running it will raise the barrier at which point
normal requests will become blocked and increment the nr_pending value to
signal there is work pending inside of wait_barrier(). NOWAIT requests
do not block and so will return immediately with an error, and additionally
do not increment nr_pending in wait_barrier(). Upstream change
43806c3d5b9b ("raid10: cleanup memleak at raid10_make_request") added a
call to raid_end_bio_io() to fix a memory leak when NOWAIT requests hit
this condition. raid_end_bio_io() eventually calls allow_barrier() and
it will unconditionally do an atomic_dec_and_test(&conf->nr_pending) even
though the corresponding increment on nr_pending didn't happen in the
NOWAIT case.

This can be easily seen by starting a check operation while an application
is doing nowait IO on the same array. This results in a deadlocked state
due to nr_pending value underflowing and so the md resync thread gets
stuck waiting for nr_pending to == 0.

Output of r10conf state of the array when we hit this condition:

  crash> struct r10conf.barrier,nr_pending,nr_waiting,nr_queued <addr of r10conf>
    barrier = 1,
    nr_pending = {
      counter = -41
    },
    nr_waiting = 15,
    nr_queued = 0,

Example of md_sync thread stuck waiting on raise_barrier() and other
requests stuck in wait_barrier():

md1_resync
[<0>] raise_barrier+0xce/0x1c0
[<0>] raid10_sync_request+0x1ca/0x1ed0
[<0>] md_do_sync+0x779/0x1110
[<0>] md_thread+0x90/0x160
[<0>] kthread+0xbe/0xf0
[<0>] ret_from_fork+0x34/0x50
[<0>] ret_from_fork_asm+0x1a/0x30

kworker/u1040:2+flush-253:4
[<0>] wait_barrier+0x1de/0x220
[<0>] regular_request_wait+0x30/0x180
[<0>] raid10_make_request+0x261/0x1000
[<0>] md_handle_request+0x13b/0x230
[<0>] __submit_bio+0x107/0x1f0
[<0>] submit_bio_noacct_nocheck+0x16f/0x390
[<0>] ext4_io_submit+0x24/0x40
[<0>] ext4_do_writepages+0x254/0xc80
[<0>] ext4_writepages+0x84/0x120
[<0>] do_writepages+0x7a/0x260
[<0>] __writeback_single_inode+0x3d/0x300
[<0>] writeback_sb_inodes+0x1dd/0x470
[<0>] __writeback_inodes_wb+0x4c/0xe0
[<0>] wb_writeback+0x18b/0x2d0
[<0>] wb_workfn+0x2a1/0x400
[<0>] process_one_work+0x149/0x330
[<0>] worker_thread+0x2d2/0x410
[<0>] kthread+0xbe/0xf0
[<0>] ret_from_fork+0x34/0x50
[<0>] ret_from_fork_asm+0x1a/0x30

Fixes: 43806c3d5b9b ("raid10: cleanup memleak at raid10_make_request")
Cc: stable@vger.kernel.org
Signed-off-by: Josh Hunt <johunt@akamai.com>
---
 drivers/md/raid10.c | 36 +++++++++++++++++++++++++-----------
 1 file changed, 25 insertions(+), 11 deletions(-)

diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index 9debb20cf129..184b5b3906d1 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -68,6 +68,7 @@
  */
 
 static void allow_barrier(struct r10conf *conf);
+static void allow_barrier_nowait(struct r10conf *conf);
 static void lower_barrier(struct r10conf *conf);
 static int _enough(struct r10conf *conf, int previous, int ignore);
 static int enough(struct r10conf *conf, int ignore);
@@ -317,7 +318,7 @@ static void reschedule_retry(struct r10bio *r10_bio)
  * operation and are ready to return a success/failure code to the buffer
  * cache layer.
  */
-static void raid_end_bio_io(struct r10bio *r10_bio)
+static void raid_end_bio_io(struct r10bio *r10_bio, bool adjust_pending)
 {
 	struct bio *bio = r10_bio->master_bio;
 	struct r10conf *conf = r10_bio->mddev->private;
@@ -332,7 +333,10 @@ static void raid_end_bio_io(struct r10bio *r10_bio)
 	 * Wake up any possible resync thread that waits for the device
 	 * to go idle.
 	 */
-	allow_barrier(conf);
+	if (adjust_pending)
+		allow_barrier(conf);
+	else
+		allow_barrier_nowait(conf);
 
 	free_r10bio(r10_bio);
 }
@@ -414,7 +418,7 @@ static void raid10_end_read_request(struct bio *bio)
 			uptodate = 1;
 	}
 	if (uptodate) {
-		raid_end_bio_io(r10_bio);
+		raid_end_bio_io(r10_bio, true);
 		rdev_dec_pending(rdev, conf->mddev);
 	} else {
 		/*
@@ -446,7 +450,7 @@ static void one_write_done(struct r10bio *r10_bio)
 			if (test_bit(R10BIO_MadeGood, &r10_bio->state))
 				reschedule_retry(r10_bio);
 			else
-				raid_end_bio_io(r10_bio);
+				raid_end_bio_io(r10_bio, true);
 		}
 	}
 }
@@ -1030,13 +1034,23 @@ static bool wait_barrier(struct r10conf *conf, bool nowait)
 	return ret;
 }
 
-static void allow_barrier(struct r10conf *conf)
+static void __allow_barrier(struct r10conf *conf, bool adjust_pending)
 {
-	if ((atomic_dec_and_test(&conf->nr_pending)) ||
+	if ((adjust_pending && atomic_dec_and_test(&conf->nr_pending)) ||
 			(conf->array_freeze_pending))
 		wake_up_barrier(conf);
 }
 
+static void allow_barrier(struct r10conf *conf)
+{
+	__allow_barrier(conf, true);
+}
+
+static void allow_barrier_nowait(struct r10conf *conf)
+{
+	__allow_barrier(conf, false);
+}
+
 static void freeze_array(struct r10conf *conf, int extra)
 {
 	/* stop syncio and normal IO and wait for everything to
@@ -1184,7 +1198,7 @@ static void raid10_read_request(struct mddev *mddev, struct bio *bio,
 	}
 
 	if (!regular_request_wait(mddev, conf, bio, r10_bio->sectors)) {
-		raid_end_bio_io(r10_bio);
+		raid_end_bio_io(r10_bio, false);
 		return;
 	}
 
@@ -1195,7 +1209,7 @@ static void raid10_read_request(struct mddev *mddev, struct bio *bio,
 					    mdname(mddev), b,
 					    (unsigned long long)r10_bio->sector);
 		}
-		raid_end_bio_io(r10_bio);
+		raid_end_bio_io(r10_bio, true);
 		return;
 	}
 	if (err_rdev)
@@ -1372,7 +1386,7 @@ static void raid10_write_request(struct mddev *mddev, struct bio *bio,
 
 	sectors = r10_bio->sectors;
 	if (!regular_request_wait(mddev, conf, bio, sectors)) {
-		raid_end_bio_io(r10_bio);
+		raid_end_bio_io(r10_bio, false);
 		return;
 	}
 
@@ -2952,7 +2966,7 @@ static void handle_write_completed(struct r10conf *conf, struct r10bio *r10_bio)
 			if (test_bit(R10BIO_WriteError,
 				     &r10_bio->state))
 				close_write(r10_bio);
-			raid_end_bio_io(r10_bio);
+			raid_end_bio_io(r10_bio, true);
 		}
 	}
 }
@@ -2987,7 +3001,7 @@ static void raid10d(struct md_thread *thread)
 			if (test_bit(R10BIO_WriteError,
 				     &r10_bio->state))
 				close_write(r10_bio);
-			raid_end_bio_io(r10_bio);
+			raid_end_bio_io(r10_bio, true);
 		}
 	}
 
-- 
2.34.1


^ permalink raw reply related

* [PATCH] md: suppress spurious superblock update warning for dm-raid
From: chencheng @ 2026-02-10 12:41 UTC (permalink / raw)
  To: song, yukuai, linux-raid, linux-kernel; +Cc: chencheng

dm-raid have external metadata management (mddev->external = 1) and
no persistent superblock (mddev->persistent = 0). For these arrays,
there's no superblock to update, so the warning is spurious.

Signed-off-by: chencheng <chencheng@fnnas.com>
---
 drivers/md/md.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/drivers/md/md.c b/drivers/md/md.c
index 6d73f6e196a..e30b658641e 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -2790,7 +2790,9 @@ void md_update_sb(struct mddev *mddev, int force_change)
 	if (!md_is_rdwr(mddev)) {
 		if (force_change)
 			set_bit(MD_SB_CHANGE_DEVS, &mddev->sb_flags);
-		pr_err("%s: can't update sb for read-only array %s\n", __func__, mdname(mddev));
+		if (!mddev_is_dm(mddev))
+			pr_err_ratelimited("%s: can't update sb for read-only array %s\n",
+					   __func__, mdname(mddev));
 		return;
 	}
 
-- 
2.51.0

^ permalink raw reply related

* Re: [PATCH] md: suppress spurious superblock update warning for dm-raid
From: Paul Menzel @ 2026-02-10  7:49 UTC (permalink / raw)
  To: chencheng; +Cc: song, yukuai, linux-raid, linux-kernel
In-Reply-To: <20260210124114.169207-1-chencheng@fnnas.com>

Dear Chen,


Thank you for your patch.

Am 10.02.26 um 13:41 schrieb chencheng:
> dm-raid have external metadata management (mddev->external = 1) and
> no persistent superblock (mddev->persistent = 0). For these arrays,
> there's no superblock to update, so the warning is spurious.

Please paste the warning.

Please also add a Fixes: tag.

> Signed-off-by: chencheng <chencheng@fnnas.com>

Would Chen Cheng be the official (transcribed) spelling of your name. If 
you yes, it’d be great, if you updated it:

     git config --global user.name "Chen Cheng"
     git commit --amend -s --author="Chen Cheng <chencheng@fnnas.com>"

> ---
>   drivers/md/md.c | 4 +++-
>   1 file changed, 3 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/md/md.c b/drivers/md/md.c
> index 6d73f6e196a..e30b658641e 100644
> --- a/drivers/md/md.c
> +++ b/drivers/md/md.c
> @@ -2790,7 +2790,9 @@ void md_update_sb(struct mddev *mddev, int force_change)
>   	if (!md_is_rdwr(mddev)) {
>   		if (force_change)
>   			set_bit(MD_SB_CHANGE_DEVS, &mddev->sb_flags);
> -		pr_err("%s: can't update sb for read-only array %s\n", __func__, mdname(mddev));
> +		if (!mddev_is_dm(mddev))
> +			pr_err_ratelimited("%s: can't update sb for read-only array %s\n",
> +					   __func__, mdname(mddev));

(It looks like it’s an error level message and not a warning level message.)

>   		return;
>   	}

With the commit message suggestions addressed, feel free to add:

Reviewed-by: Paul Menzel <pmenzel@molgen.mpg.de>


Kind regards,

Paul

^ permalink raw reply

* [PATCH] md: suppress spurious superblock update error message for dm-raid
From: Chen Cheng @ 2026-02-10 13:38 UTC (permalink / raw)
  To: linux-raid; +Cc: linux-kernel, pmenzel, tj.iam.tj, Chen Cheng

dm-raid has external metadata management (mddev->external = 1) and
no persistent superblock (mddev->persistent = 0). For these arrays,
there's no superblock to update, so the error message is spurious.

The error appears as:
[  123.456789] md_update_sb: can't update sb for read-only array md0

Link: https://lore.kernel.org/all/20260128082430.96788-1-tj.iam.tj@proton.me/
Fixes: 6a5cb53aaa1d ("md: don't ignore read-only array in md_update_sb()")
Signed-off-by: Chen Cheng <chencheng@fnnas.com>
Reviewed-by: Paul Menzel <pmenzel@molgen.mpg.de>
---
 drivers/md/md.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/drivers/md/md.c b/drivers/md/md.c
index 6d73f6e196a..e30b658641e 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -2790,7 +2790,9 @@ void md_update_sb(struct mddev *mddev, int force_change)
 	if (!md_is_rdwr(mddev)) {
 		if (force_change)
 			set_bit(MD_SB_CHANGE_DEVS, &mddev->sb_flags);
-		pr_err("%s: can't update sb for read-only array %s\n", __func__, mdname(mddev));
+		if (!mddev_is_dm(mddev))
+			pr_err_ratelimited("%s: can't update sb for read-only array %s\n",
+					   __func__, mdname(mddev));
 		return;
 	}
 
-- 
2.51.0

^ permalink raw reply related

* Re: [PATCH] md/raid10: fix deadlock with check operation and nowait requests
From: kernel test robot @ 2026-02-10 10:17 UTC (permalink / raw)
  To: Josh Hunt, song, yukuai, linan122, linux-raid
  Cc: oe-kbuild-all, ncroxon, Josh Hunt, stable
In-Reply-To: <20260210050942.3731656-1-johunt@akamai.com>

Hi Josh,

kernel test robot noticed the following build errors:

[auto build test ERROR on linus/master]
[also build test ERROR on v6.19 next-20260209]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Josh-Hunt/md-raid10-fix-deadlock-with-check-operation-and-nowait-requests/20260210-135305
base:   linus/master
patch link:    https://lore.kernel.org/r/20260210050942.3731656-1-johunt%40akamai.com
patch subject: [PATCH] md/raid10: fix deadlock with check operation and nowait requests
config: nios2-allmodconfig (https://download.01.org/0day-ci/archive/20260210/202602101844.0pRyZv4D-lkp@intel.com/config)
compiler: nios2-linux-gcc (GCC) 11.5.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260210/202602101844.0pRyZv4D-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202602101844.0pRyZv4D-lkp@intel.com/

All errors (new ones prefixed by >>):

   drivers/md/raid10.c: In function 'raid10_read_request':
>> drivers/md/raid10.c:1257:9: error: too few arguments to function 'raid_end_bio_io'
    1257 |         raid_end_bio_io(r10_bio);
         |         ^~~~~~~~~~~~~~~
   drivers/md/raid10.c:321:13: note: declared here
     321 | static void raid_end_bio_io(struct r10bio *r10_bio, bool adjust_pending)
         |             ^~~~~~~~~~~~~~~
   drivers/md/raid10.c: In function 'raid10_write_request':
   drivers/md/raid10.c:1540:9: error: too few arguments to function 'raid_end_bio_io'
    1540 |         raid_end_bio_io(r10_bio);
         |         ^~~~~~~~~~~~~~~
   drivers/md/raid10.c:321:13: note: declared here
     321 | static void raid_end_bio_io(struct r10bio *r10_bio, bool adjust_pending)
         |             ^~~~~~~~~~~~~~~


vim +/raid_end_bio_io +1257 drivers/md/raid10.c

caea3c47ad5152 Guoqing Jiang     2018-12-07  1161  
bb5f1ed70bc3bb Robert LeBlanc    2016-12-05  1162  static void raid10_read_request(struct mddev *mddev, struct bio *bio,
820455238366a7 Yu Kuai           2023-06-22  1163  				struct r10bio *r10_bio, bool io_accounting)
^1da177e4c3f41 Linus Torvalds    2005-04-16  1164  {
e879a8793f915a NeilBrown         2011-10-11  1165  	struct r10conf *conf = mddev->private;
^1da177e4c3f41 Linus Torvalds    2005-04-16  1166  	struct bio *read_bio;
d4432c23be957f NeilBrown         2011-07-28  1167  	int max_sectors;
bb5f1ed70bc3bb Robert LeBlanc    2016-12-05  1168  	struct md_rdev *rdev;
545250f2480911 NeilBrown         2017-04-05  1169  	char b[BDEVNAME_SIZE];
545250f2480911 NeilBrown         2017-04-05  1170  	int slot = r10_bio->read_slot;
545250f2480911 NeilBrown         2017-04-05  1171  	struct md_rdev *err_rdev = NULL;
545250f2480911 NeilBrown         2017-04-05  1172  	gfp_t gfp = GFP_NOIO;
9b622e2bbcf049 Tomasz Majchrzak  2016-07-28  1173  
93decc563637c4 Kevin Vigor       2020-11-06  1174  	if (slot >= 0 && r10_bio->devs[slot].rdev) {
545250f2480911 NeilBrown         2017-04-05  1175  		/*
545250f2480911 NeilBrown         2017-04-05  1176  		 * This is an error retry, but we cannot
545250f2480911 NeilBrown         2017-04-05  1177  		 * safely dereference the rdev in the r10_bio,
545250f2480911 NeilBrown         2017-04-05  1178  		 * we must use the one in conf.
545250f2480911 NeilBrown         2017-04-05  1179  		 * If it has already been disconnected (unlikely)
545250f2480911 NeilBrown         2017-04-05  1180  		 * we lose the device name in error messages.
545250f2480911 NeilBrown         2017-04-05  1181  		 */
545250f2480911 NeilBrown         2017-04-05  1182  		int disk;
545250f2480911 NeilBrown         2017-04-05  1183  		/*
545250f2480911 NeilBrown         2017-04-05  1184  		 * As we are blocking raid10, it is a little safer to
545250f2480911 NeilBrown         2017-04-05  1185  		 * use __GFP_HIGH.
545250f2480911 NeilBrown         2017-04-05  1186  		 */
545250f2480911 NeilBrown         2017-04-05  1187  		gfp = GFP_NOIO | __GFP_HIGH;
545250f2480911 NeilBrown         2017-04-05  1188  
545250f2480911 NeilBrown         2017-04-05  1189  		disk = r10_bio->devs[slot].devnum;
a448af25becf4b Yu Kuai           2023-11-25  1190  		err_rdev = conf->mirrors[disk].rdev;
545250f2480911 NeilBrown         2017-04-05  1191  		if (err_rdev)
900d156bac2bc4 Christoph Hellwig 2022-07-13  1192  			snprintf(b, sizeof(b), "%pg", err_rdev->bdev);
545250f2480911 NeilBrown         2017-04-05  1193  		else {
545250f2480911 NeilBrown         2017-04-05  1194  			strcpy(b, "???");
545250f2480911 NeilBrown         2017-04-05  1195  			/* This never gets dereferenced */
545250f2480911 NeilBrown         2017-04-05  1196  			err_rdev = r10_bio->devs[slot].rdev;
545250f2480911 NeilBrown         2017-04-05  1197  		}
545250f2480911 NeilBrown         2017-04-05  1198  	}
856e08e23762df NeilBrown         2011-07-28  1199  
43806c3d5b9bb7 Nigel Croxon      2025-07-03  1200  	if (!regular_request_wait(mddev, conf, bio, r10_bio->sectors)) {
4e9814d1943b0e Josh Hunt         2026-02-10  1201  		raid_end_bio_io(r10_bio, false);
c9aa889b035fca Vishal Verma      2021-12-21  1202  		return;
43806c3d5b9bb7 Nigel Croxon      2025-07-03  1203  	}
43806c3d5b9bb7 Nigel Croxon      2025-07-03  1204  
96c3fd1f380237 NeilBrown         2011-12-23  1205  	rdev = read_balance(conf, r10_bio, &max_sectors);
96c3fd1f380237 NeilBrown         2011-12-23  1206  	if (!rdev) {
545250f2480911 NeilBrown         2017-04-05  1207  		if (err_rdev) {
545250f2480911 NeilBrown         2017-04-05  1208  			pr_crit_ratelimited("md/raid10:%s: %s: unrecoverable I/O read error for block %llu\n",
545250f2480911 NeilBrown         2017-04-05  1209  					    mdname(mddev), b,
545250f2480911 NeilBrown         2017-04-05  1210  					    (unsigned long long)r10_bio->sector);
545250f2480911 NeilBrown         2017-04-05  1211  		}
4e9814d1943b0e Josh Hunt         2026-02-10  1212  		raid_end_bio_io(r10_bio, true);
5a7bbad27a4103 Christoph Hellwig 2011-09-12  1213  		return;
^1da177e4c3f41 Linus Torvalds    2005-04-16  1214  	}
545250f2480911 NeilBrown         2017-04-05  1215  	if (err_rdev)
913cce5a1e588e Christoph Hellwig 2022-05-12  1216  		pr_err_ratelimited("md/raid10:%s: %pg: redirecting sector %llu to another mirror\n",
545250f2480911 NeilBrown         2017-04-05  1217  				   mdname(mddev),
913cce5a1e588e Christoph Hellwig 2022-05-12  1218  				   rdev->bdev,
545250f2480911 NeilBrown         2017-04-05  1219  				   (unsigned long long)r10_bio->sector);
fc9977dd069e4f NeilBrown         2017-04-05  1220  	if (max_sectors < bio_sectors(bio)) {
e820d55cb99dd9 Guoqing Jiang     2018-12-19  1221  		allow_barrier(conf);
6fc07785d9b892 Yu Kuai           2025-09-10  1222  		bio = bio_submit_split_bioset(bio, max_sectors,
6fc07785d9b892 Yu Kuai           2025-09-10  1223  					      &conf->bio_split);
c9aa889b035fca Vishal Verma      2021-12-21  1224  		wait_barrier(conf, false);
6fc07785d9b892 Yu Kuai           2025-09-10  1225  		if (!bio) {
6fc07785d9b892 Yu Kuai           2025-09-10  1226  			set_bit(R10BIO_Returned, &r10_bio->state);
4cf58d95290973 John Garry        2024-11-11  1227  			goto err_handle;
4cf58d95290973 John Garry        2024-11-11  1228  		}
22f166218f7313 Yu Kuai           2025-09-10  1229  
fc9977dd069e4f NeilBrown         2017-04-05  1230  		r10_bio->master_bio = bio;
fc9977dd069e4f NeilBrown         2017-04-05  1231  		r10_bio->sectors = max_sectors;
fc9977dd069e4f NeilBrown         2017-04-05  1232  	}
96c3fd1f380237 NeilBrown         2011-12-23  1233  	slot = r10_bio->read_slot;
^1da177e4c3f41 Linus Torvalds    2005-04-16  1234  
820455238366a7 Yu Kuai           2023-06-22  1235  	if (io_accounting) {
820455238366a7 Yu Kuai           2023-06-22  1236  		md_account_bio(mddev, &bio);
820455238366a7 Yu Kuai           2023-06-22  1237  		r10_bio->master_bio = bio;
820455238366a7 Yu Kuai           2023-06-22  1238  	}
abfc426d1b2fb2 Christoph Hellwig 2022-02-02  1239  	read_bio = bio_alloc_clone(rdev->bdev, bio, gfp, &mddev->bio_set);
5fa31c49928139 Zheng Qixing      2025-07-02  1240  	read_bio->bi_opf &= ~REQ_NOWAIT;
^1da177e4c3f41 Linus Torvalds    2005-04-16  1241  
^1da177e4c3f41 Linus Torvalds    2005-04-16  1242  	r10_bio->devs[slot].bio = read_bio;
abbf098e6e1e23 NeilBrown         2011-12-23  1243  	r10_bio->devs[slot].rdev = rdev;
^1da177e4c3f41 Linus Torvalds    2005-04-16  1244  
4f024f3797c43c Kent Overstreet   2013-10-11  1245  	read_bio->bi_iter.bi_sector = r10_bio->devs[slot].addr +
f8c9e74ff0832f NeilBrown         2012-05-21  1246  		choose_data_offset(r10_bio, rdev);
^1da177e4c3f41 Linus Torvalds    2005-04-16  1247  	read_bio->bi_end_io = raid10_end_read_request;
8d3ca83dcf9ca3 NeilBrown         2016-11-18  1248  	if (test_bit(FailFast, &rdev->flags) &&
8d3ca83dcf9ca3 NeilBrown         2016-11-18  1249  	    test_bit(R10BIO_FailFast, &r10_bio->state))
8d3ca83dcf9ca3 NeilBrown         2016-11-18  1250  	        read_bio->bi_opf |= MD_FAILFAST;
^1da177e4c3f41 Linus Torvalds    2005-04-16  1251  	read_bio->bi_private = r10_bio;
c396b90e502691 Christoph Hellwig 2024-03-03  1252  	mddev_trace_remap(mddev, read_bio, r10_bio->sector);
ed00aabd5eb9fb Christoph Hellwig 2020-07-01  1253  	submit_bio_noacct(read_bio);
5a7bbad27a4103 Christoph Hellwig 2011-09-12  1254  	return;
4cf58d95290973 John Garry        2024-11-11  1255  err_handle:
4cf58d95290973 John Garry        2024-11-11  1256  	atomic_dec(&rdev->nr_pending);
4cf58d95290973 John Garry        2024-11-11 @1257  	raid_end_bio_io(r10_bio);
^1da177e4c3f41 Linus Torvalds    2005-04-16  1258  }
^1da177e4c3f41 Linus Torvalds    2005-04-16  1259  

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply

* Re: [PATCH] md/raid10: fix deadlock with check operation and nowait requests
From: kernel test robot @ 2026-02-10 10:18 UTC (permalink / raw)
  To: Josh Hunt, song, yukuai, linan122, linux-raid
  Cc: oe-kbuild-all, ncroxon, Josh Hunt, stable
In-Reply-To: <20260210050942.3731656-1-johunt@akamai.com>

Hi Josh,

kernel test robot noticed the following build errors:

[auto build test ERROR on linus/master]
[also build test ERROR on v6.19 next-20260209]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Josh-Hunt/md-raid10-fix-deadlock-with-check-operation-and-nowait-requests/20260210-135305
base:   linus/master
patch link:    https://lore.kernel.org/r/20260210050942.3731656-1-johunt%40akamai.com
patch subject: [PATCH] md/raid10: fix deadlock with check operation and nowait requests
config: m68k-defconfig (https://download.01.org/0day-ci/archive/20260210/202602101850.DC3BeMD5-lkp@intel.com/config)
compiler: m68k-linux-gcc (GCC) 15.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260210/202602101850.DC3BeMD5-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202602101850.DC3BeMD5-lkp@intel.com/

All errors (new ones prefixed by >>):

   drivers/md/raid10.c: In function 'raid10_read_request':
>> drivers/md/raid10.c:1257:9: error: too few arguments to function 'raid_end_bio_io'; expected 2, have 1
    1257 |         raid_end_bio_io(r10_bio);
         |         ^~~~~~~~~~~~~~~
   drivers/md/raid10.c:321:13: note: declared here
     321 | static void raid_end_bio_io(struct r10bio *r10_bio, bool adjust_pending)
         |             ^~~~~~~~~~~~~~~
   drivers/md/raid10.c: In function 'raid10_write_request':
   drivers/md/raid10.c:1540:9: error: too few arguments to function 'raid_end_bio_io'; expected 2, have 1
    1540 |         raid_end_bio_io(r10_bio);
         |         ^~~~~~~~~~~~~~~
   drivers/md/raid10.c:321:13: note: declared here
     321 | static void raid_end_bio_io(struct r10bio *r10_bio, bool adjust_pending)
         |             ^~~~~~~~~~~~~~~


vim +/raid_end_bio_io +1257 drivers/md/raid10.c

caea3c47ad5152 Guoqing Jiang     2018-12-07  1161  
bb5f1ed70bc3bb Robert LeBlanc    2016-12-05  1162  static void raid10_read_request(struct mddev *mddev, struct bio *bio,
820455238366a7 Yu Kuai           2023-06-22  1163  				struct r10bio *r10_bio, bool io_accounting)
^1da177e4c3f41 Linus Torvalds    2005-04-16  1164  {
e879a8793f915a NeilBrown         2011-10-11  1165  	struct r10conf *conf = mddev->private;
^1da177e4c3f41 Linus Torvalds    2005-04-16  1166  	struct bio *read_bio;
d4432c23be957f NeilBrown         2011-07-28  1167  	int max_sectors;
bb5f1ed70bc3bb Robert LeBlanc    2016-12-05  1168  	struct md_rdev *rdev;
545250f2480911 NeilBrown         2017-04-05  1169  	char b[BDEVNAME_SIZE];
545250f2480911 NeilBrown         2017-04-05  1170  	int slot = r10_bio->read_slot;
545250f2480911 NeilBrown         2017-04-05  1171  	struct md_rdev *err_rdev = NULL;
545250f2480911 NeilBrown         2017-04-05  1172  	gfp_t gfp = GFP_NOIO;
9b622e2bbcf049 Tomasz Majchrzak  2016-07-28  1173  
93decc563637c4 Kevin Vigor       2020-11-06  1174  	if (slot >= 0 && r10_bio->devs[slot].rdev) {
545250f2480911 NeilBrown         2017-04-05  1175  		/*
545250f2480911 NeilBrown         2017-04-05  1176  		 * This is an error retry, but we cannot
545250f2480911 NeilBrown         2017-04-05  1177  		 * safely dereference the rdev in the r10_bio,
545250f2480911 NeilBrown         2017-04-05  1178  		 * we must use the one in conf.
545250f2480911 NeilBrown         2017-04-05  1179  		 * If it has already been disconnected (unlikely)
545250f2480911 NeilBrown         2017-04-05  1180  		 * we lose the device name in error messages.
545250f2480911 NeilBrown         2017-04-05  1181  		 */
545250f2480911 NeilBrown         2017-04-05  1182  		int disk;
545250f2480911 NeilBrown         2017-04-05  1183  		/*
545250f2480911 NeilBrown         2017-04-05  1184  		 * As we are blocking raid10, it is a little safer to
545250f2480911 NeilBrown         2017-04-05  1185  		 * use __GFP_HIGH.
545250f2480911 NeilBrown         2017-04-05  1186  		 */
545250f2480911 NeilBrown         2017-04-05  1187  		gfp = GFP_NOIO | __GFP_HIGH;
545250f2480911 NeilBrown         2017-04-05  1188  
545250f2480911 NeilBrown         2017-04-05  1189  		disk = r10_bio->devs[slot].devnum;
a448af25becf4b Yu Kuai           2023-11-25  1190  		err_rdev = conf->mirrors[disk].rdev;
545250f2480911 NeilBrown         2017-04-05  1191  		if (err_rdev)
900d156bac2bc4 Christoph Hellwig 2022-07-13  1192  			snprintf(b, sizeof(b), "%pg", err_rdev->bdev);
545250f2480911 NeilBrown         2017-04-05  1193  		else {
545250f2480911 NeilBrown         2017-04-05  1194  			strcpy(b, "???");
545250f2480911 NeilBrown         2017-04-05  1195  			/* This never gets dereferenced */
545250f2480911 NeilBrown         2017-04-05  1196  			err_rdev = r10_bio->devs[slot].rdev;
545250f2480911 NeilBrown         2017-04-05  1197  		}
545250f2480911 NeilBrown         2017-04-05  1198  	}
856e08e23762df NeilBrown         2011-07-28  1199  
43806c3d5b9bb7 Nigel Croxon      2025-07-03  1200  	if (!regular_request_wait(mddev, conf, bio, r10_bio->sectors)) {
4e9814d1943b0e Josh Hunt         2026-02-10  1201  		raid_end_bio_io(r10_bio, false);
c9aa889b035fca Vishal Verma      2021-12-21  1202  		return;
43806c3d5b9bb7 Nigel Croxon      2025-07-03  1203  	}
43806c3d5b9bb7 Nigel Croxon      2025-07-03  1204  
96c3fd1f380237 NeilBrown         2011-12-23  1205  	rdev = read_balance(conf, r10_bio, &max_sectors);
96c3fd1f380237 NeilBrown         2011-12-23  1206  	if (!rdev) {
545250f2480911 NeilBrown         2017-04-05  1207  		if (err_rdev) {
545250f2480911 NeilBrown         2017-04-05  1208  			pr_crit_ratelimited("md/raid10:%s: %s: unrecoverable I/O read error for block %llu\n",
545250f2480911 NeilBrown         2017-04-05  1209  					    mdname(mddev), b,
545250f2480911 NeilBrown         2017-04-05  1210  					    (unsigned long long)r10_bio->sector);
545250f2480911 NeilBrown         2017-04-05  1211  		}
4e9814d1943b0e Josh Hunt         2026-02-10  1212  		raid_end_bio_io(r10_bio, true);
5a7bbad27a4103 Christoph Hellwig 2011-09-12  1213  		return;
^1da177e4c3f41 Linus Torvalds    2005-04-16  1214  	}
545250f2480911 NeilBrown         2017-04-05  1215  	if (err_rdev)
913cce5a1e588e Christoph Hellwig 2022-05-12  1216  		pr_err_ratelimited("md/raid10:%s: %pg: redirecting sector %llu to another mirror\n",
545250f2480911 NeilBrown         2017-04-05  1217  				   mdname(mddev),
913cce5a1e588e Christoph Hellwig 2022-05-12  1218  				   rdev->bdev,
545250f2480911 NeilBrown         2017-04-05  1219  				   (unsigned long long)r10_bio->sector);
fc9977dd069e4f NeilBrown         2017-04-05  1220  	if (max_sectors < bio_sectors(bio)) {
e820d55cb99dd9 Guoqing Jiang     2018-12-19  1221  		allow_barrier(conf);
6fc07785d9b892 Yu Kuai           2025-09-10  1222  		bio = bio_submit_split_bioset(bio, max_sectors,
6fc07785d9b892 Yu Kuai           2025-09-10  1223  					      &conf->bio_split);
c9aa889b035fca Vishal Verma      2021-12-21  1224  		wait_barrier(conf, false);
6fc07785d9b892 Yu Kuai           2025-09-10  1225  		if (!bio) {
6fc07785d9b892 Yu Kuai           2025-09-10  1226  			set_bit(R10BIO_Returned, &r10_bio->state);
4cf58d95290973 John Garry        2024-11-11  1227  			goto err_handle;
4cf58d95290973 John Garry        2024-11-11  1228  		}
22f166218f7313 Yu Kuai           2025-09-10  1229  
fc9977dd069e4f NeilBrown         2017-04-05  1230  		r10_bio->master_bio = bio;
fc9977dd069e4f NeilBrown         2017-04-05  1231  		r10_bio->sectors = max_sectors;
fc9977dd069e4f NeilBrown         2017-04-05  1232  	}
96c3fd1f380237 NeilBrown         2011-12-23  1233  	slot = r10_bio->read_slot;
^1da177e4c3f41 Linus Torvalds    2005-04-16  1234  
820455238366a7 Yu Kuai           2023-06-22  1235  	if (io_accounting) {
820455238366a7 Yu Kuai           2023-06-22  1236  		md_account_bio(mddev, &bio);
820455238366a7 Yu Kuai           2023-06-22  1237  		r10_bio->master_bio = bio;
820455238366a7 Yu Kuai           2023-06-22  1238  	}
abfc426d1b2fb2 Christoph Hellwig 2022-02-02  1239  	read_bio = bio_alloc_clone(rdev->bdev, bio, gfp, &mddev->bio_set);
5fa31c49928139 Zheng Qixing      2025-07-02  1240  	read_bio->bi_opf &= ~REQ_NOWAIT;
^1da177e4c3f41 Linus Torvalds    2005-04-16  1241  
^1da177e4c3f41 Linus Torvalds    2005-04-16  1242  	r10_bio->devs[slot].bio = read_bio;
abbf098e6e1e23 NeilBrown         2011-12-23  1243  	r10_bio->devs[slot].rdev = rdev;
^1da177e4c3f41 Linus Torvalds    2005-04-16  1244  
4f024f3797c43c Kent Overstreet   2013-10-11  1245  	read_bio->bi_iter.bi_sector = r10_bio->devs[slot].addr +
f8c9e74ff0832f NeilBrown         2012-05-21  1246  		choose_data_offset(r10_bio, rdev);
^1da177e4c3f41 Linus Torvalds    2005-04-16  1247  	read_bio->bi_end_io = raid10_end_read_request;
8d3ca83dcf9ca3 NeilBrown         2016-11-18  1248  	if (test_bit(FailFast, &rdev->flags) &&
8d3ca83dcf9ca3 NeilBrown         2016-11-18  1249  	    test_bit(R10BIO_FailFast, &r10_bio->state))
8d3ca83dcf9ca3 NeilBrown         2016-11-18  1250  	        read_bio->bi_opf |= MD_FAILFAST;
^1da177e4c3f41 Linus Torvalds    2005-04-16  1251  	read_bio->bi_private = r10_bio;
c396b90e502691 Christoph Hellwig 2024-03-03  1252  	mddev_trace_remap(mddev, read_bio, r10_bio->sector);
ed00aabd5eb9fb Christoph Hellwig 2020-07-01  1253  	submit_bio_noacct(read_bio);
5a7bbad27a4103 Christoph Hellwig 2011-09-12  1254  	return;
4cf58d95290973 John Garry        2024-11-11  1255  err_handle:
4cf58d95290973 John Garry        2024-11-11  1256  	atomic_dec(&rdev->nr_pending);
4cf58d95290973 John Garry        2024-11-11 @1257  	raid_end_bio_io(r10_bio);
^1da177e4c3f41 Linus Torvalds    2005-04-16  1258  }
^1da177e4c3f41 Linus Torvalds    2005-04-16  1259  

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply

* Re: [PATCH] md/raid10: fix deadlock with check operation and nowait requests
From: kernel test robot @ 2026-02-10 11:14 UTC (permalink / raw)
  To: Josh Hunt, song, yukuai, linan122, linux-raid
  Cc: oe-kbuild-all, ncroxon, Josh Hunt, stable
In-Reply-To: <20260210050942.3731656-1-johunt@akamai.com>

Hi Josh,

kernel test robot noticed the following build errors:

[auto build test ERROR on linus/master]
[also build test ERROR on v6.19 next-20260209]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Josh-Hunt/md-raid10-fix-deadlock-with-check-operation-and-nowait-requests/20260210-135305
base:   linus/master
patch link:    https://lore.kernel.org/r/20260210050942.3731656-1-johunt%40akamai.com
patch subject: [PATCH] md/raid10: fix deadlock with check operation and nowait requests
config: x86_64-rhel-9.4 (https://download.01.org/0day-ci/archive/20260210/202602101220.J4BofeDD-lkp@intel.com/config)
compiler: gcc-14 (Debian 14.2.0-19) 14.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260210/202602101220.J4BofeDD-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202602101220.J4BofeDD-lkp@intel.com/

All errors (new ones prefixed by >>):

   drivers/md/raid10.c: In function 'raid10_read_request':
>> drivers/md/raid10.c:1257:9: error: too few arguments to function 'raid_end_bio_io'
    1257 |         raid_end_bio_io(r10_bio);
         |         ^~~~~~~~~~~~~~~
   drivers/md/raid10.c:321:13: note: declared here
     321 | static void raid_end_bio_io(struct r10bio *r10_bio, bool adjust_pending)
         |             ^~~~~~~~~~~~~~~
   drivers/md/raid10.c: In function 'raid10_write_request':
   drivers/md/raid10.c:1540:9: error: too few arguments to function 'raid_end_bio_io'
    1540 |         raid_end_bio_io(r10_bio);
         |         ^~~~~~~~~~~~~~~
   drivers/md/raid10.c:321:13: note: declared here
     321 | static void raid_end_bio_io(struct r10bio *r10_bio, bool adjust_pending)
         |             ^~~~~~~~~~~~~~~


vim +/raid_end_bio_io +1257 drivers/md/raid10.c

caea3c47ad5152 Guoqing Jiang     2018-12-07  1161  
bb5f1ed70bc3bb Robert LeBlanc    2016-12-05  1162  static void raid10_read_request(struct mddev *mddev, struct bio *bio,
820455238366a7 Yu Kuai           2023-06-22  1163  				struct r10bio *r10_bio, bool io_accounting)
^1da177e4c3f41 Linus Torvalds    2005-04-16  1164  {
e879a8793f915a NeilBrown         2011-10-11  1165  	struct r10conf *conf = mddev->private;
^1da177e4c3f41 Linus Torvalds    2005-04-16  1166  	struct bio *read_bio;
d4432c23be957f NeilBrown         2011-07-28  1167  	int max_sectors;
bb5f1ed70bc3bb Robert LeBlanc    2016-12-05  1168  	struct md_rdev *rdev;
545250f2480911 NeilBrown         2017-04-05  1169  	char b[BDEVNAME_SIZE];
545250f2480911 NeilBrown         2017-04-05  1170  	int slot = r10_bio->read_slot;
545250f2480911 NeilBrown         2017-04-05  1171  	struct md_rdev *err_rdev = NULL;
545250f2480911 NeilBrown         2017-04-05  1172  	gfp_t gfp = GFP_NOIO;
9b622e2bbcf049 Tomasz Majchrzak  2016-07-28  1173  
93decc563637c4 Kevin Vigor       2020-11-06  1174  	if (slot >= 0 && r10_bio->devs[slot].rdev) {
545250f2480911 NeilBrown         2017-04-05  1175  		/*
545250f2480911 NeilBrown         2017-04-05  1176  		 * This is an error retry, but we cannot
545250f2480911 NeilBrown         2017-04-05  1177  		 * safely dereference the rdev in the r10_bio,
545250f2480911 NeilBrown         2017-04-05  1178  		 * we must use the one in conf.
545250f2480911 NeilBrown         2017-04-05  1179  		 * If it has already been disconnected (unlikely)
545250f2480911 NeilBrown         2017-04-05  1180  		 * we lose the device name in error messages.
545250f2480911 NeilBrown         2017-04-05  1181  		 */
545250f2480911 NeilBrown         2017-04-05  1182  		int disk;
545250f2480911 NeilBrown         2017-04-05  1183  		/*
545250f2480911 NeilBrown         2017-04-05  1184  		 * As we are blocking raid10, it is a little safer to
545250f2480911 NeilBrown         2017-04-05  1185  		 * use __GFP_HIGH.
545250f2480911 NeilBrown         2017-04-05  1186  		 */
545250f2480911 NeilBrown         2017-04-05  1187  		gfp = GFP_NOIO | __GFP_HIGH;
545250f2480911 NeilBrown         2017-04-05  1188  
545250f2480911 NeilBrown         2017-04-05  1189  		disk = r10_bio->devs[slot].devnum;
a448af25becf4b Yu Kuai           2023-11-25  1190  		err_rdev = conf->mirrors[disk].rdev;
545250f2480911 NeilBrown         2017-04-05  1191  		if (err_rdev)
900d156bac2bc4 Christoph Hellwig 2022-07-13  1192  			snprintf(b, sizeof(b), "%pg", err_rdev->bdev);
545250f2480911 NeilBrown         2017-04-05  1193  		else {
545250f2480911 NeilBrown         2017-04-05  1194  			strcpy(b, "???");
545250f2480911 NeilBrown         2017-04-05  1195  			/* This never gets dereferenced */
545250f2480911 NeilBrown         2017-04-05  1196  			err_rdev = r10_bio->devs[slot].rdev;
545250f2480911 NeilBrown         2017-04-05  1197  		}
545250f2480911 NeilBrown         2017-04-05  1198  	}
856e08e23762df NeilBrown         2011-07-28  1199  
43806c3d5b9bb7 Nigel Croxon      2025-07-03  1200  	if (!regular_request_wait(mddev, conf, bio, r10_bio->sectors)) {
4e9814d1943b0e Josh Hunt         2026-02-10  1201  		raid_end_bio_io(r10_bio, false);
c9aa889b035fca Vishal Verma      2021-12-21  1202  		return;
43806c3d5b9bb7 Nigel Croxon      2025-07-03  1203  	}
43806c3d5b9bb7 Nigel Croxon      2025-07-03  1204  
96c3fd1f380237 NeilBrown         2011-12-23  1205  	rdev = read_balance(conf, r10_bio, &max_sectors);
96c3fd1f380237 NeilBrown         2011-12-23  1206  	if (!rdev) {
545250f2480911 NeilBrown         2017-04-05  1207  		if (err_rdev) {
545250f2480911 NeilBrown         2017-04-05  1208  			pr_crit_ratelimited("md/raid10:%s: %s: unrecoverable I/O read error for block %llu\n",
545250f2480911 NeilBrown         2017-04-05  1209  					    mdname(mddev), b,
545250f2480911 NeilBrown         2017-04-05  1210  					    (unsigned long long)r10_bio->sector);
545250f2480911 NeilBrown         2017-04-05  1211  		}
4e9814d1943b0e Josh Hunt         2026-02-10  1212  		raid_end_bio_io(r10_bio, true);
5a7bbad27a4103 Christoph Hellwig 2011-09-12  1213  		return;
^1da177e4c3f41 Linus Torvalds    2005-04-16  1214  	}
545250f2480911 NeilBrown         2017-04-05  1215  	if (err_rdev)
913cce5a1e588e Christoph Hellwig 2022-05-12  1216  		pr_err_ratelimited("md/raid10:%s: %pg: redirecting sector %llu to another mirror\n",
545250f2480911 NeilBrown         2017-04-05  1217  				   mdname(mddev),
913cce5a1e588e Christoph Hellwig 2022-05-12  1218  				   rdev->bdev,
545250f2480911 NeilBrown         2017-04-05  1219  				   (unsigned long long)r10_bio->sector);
fc9977dd069e4f NeilBrown         2017-04-05  1220  	if (max_sectors < bio_sectors(bio)) {
e820d55cb99dd9 Guoqing Jiang     2018-12-19  1221  		allow_barrier(conf);
6fc07785d9b892 Yu Kuai           2025-09-10  1222  		bio = bio_submit_split_bioset(bio, max_sectors,
6fc07785d9b892 Yu Kuai           2025-09-10  1223  					      &conf->bio_split);
c9aa889b035fca Vishal Verma      2021-12-21  1224  		wait_barrier(conf, false);
6fc07785d9b892 Yu Kuai           2025-09-10  1225  		if (!bio) {
6fc07785d9b892 Yu Kuai           2025-09-10  1226  			set_bit(R10BIO_Returned, &r10_bio->state);
4cf58d95290973 John Garry        2024-11-11  1227  			goto err_handle;
4cf58d95290973 John Garry        2024-11-11  1228  		}
22f166218f7313 Yu Kuai           2025-09-10  1229  
fc9977dd069e4f NeilBrown         2017-04-05  1230  		r10_bio->master_bio = bio;
fc9977dd069e4f NeilBrown         2017-04-05  1231  		r10_bio->sectors = max_sectors;
fc9977dd069e4f NeilBrown         2017-04-05  1232  	}
96c3fd1f380237 NeilBrown         2011-12-23  1233  	slot = r10_bio->read_slot;
^1da177e4c3f41 Linus Torvalds    2005-04-16  1234  
820455238366a7 Yu Kuai           2023-06-22  1235  	if (io_accounting) {
820455238366a7 Yu Kuai           2023-06-22  1236  		md_account_bio(mddev, &bio);
820455238366a7 Yu Kuai           2023-06-22  1237  		r10_bio->master_bio = bio;
820455238366a7 Yu Kuai           2023-06-22  1238  	}
abfc426d1b2fb2 Christoph Hellwig 2022-02-02  1239  	read_bio = bio_alloc_clone(rdev->bdev, bio, gfp, &mddev->bio_set);
5fa31c49928139 Zheng Qixing      2025-07-02  1240  	read_bio->bi_opf &= ~REQ_NOWAIT;
^1da177e4c3f41 Linus Torvalds    2005-04-16  1241  
^1da177e4c3f41 Linus Torvalds    2005-04-16  1242  	r10_bio->devs[slot].bio = read_bio;
abbf098e6e1e23 NeilBrown         2011-12-23  1243  	r10_bio->devs[slot].rdev = rdev;
^1da177e4c3f41 Linus Torvalds    2005-04-16  1244  
4f024f3797c43c Kent Overstreet   2013-10-11  1245  	read_bio->bi_iter.bi_sector = r10_bio->devs[slot].addr +
f8c9e74ff0832f NeilBrown         2012-05-21  1246  		choose_data_offset(r10_bio, rdev);
^1da177e4c3f41 Linus Torvalds    2005-04-16  1247  	read_bio->bi_end_io = raid10_end_read_request;
8d3ca83dcf9ca3 NeilBrown         2016-11-18  1248  	if (test_bit(FailFast, &rdev->flags) &&
8d3ca83dcf9ca3 NeilBrown         2016-11-18  1249  	    test_bit(R10BIO_FailFast, &r10_bio->state))
8d3ca83dcf9ca3 NeilBrown         2016-11-18  1250  	        read_bio->bi_opf |= MD_FAILFAST;
^1da177e4c3f41 Linus Torvalds    2005-04-16  1251  	read_bio->bi_private = r10_bio;
c396b90e502691 Christoph Hellwig 2024-03-03  1252  	mddev_trace_remap(mddev, read_bio, r10_bio->sector);
ed00aabd5eb9fb Christoph Hellwig 2020-07-01  1253  	submit_bio_noacct(read_bio);
5a7bbad27a4103 Christoph Hellwig 2011-09-12  1254  	return;
4cf58d95290973 John Garry        2024-11-11  1255  err_handle:
4cf58d95290973 John Garry        2024-11-11  1256  	atomic_dec(&rdev->nr_pending);
4cf58d95290973 John Garry        2024-11-11 @1257  	raid_end_bio_io(r10_bio);
^1da177e4c3f41 Linus Torvalds    2005-04-16  1258  }
^1da177e4c3f41 Linus Torvalds    2005-04-16  1259  

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply

* Re: [PATCH] md/raid10: fix deadlock with check operation and nowait requests
From: Josh Hunt @ 2026-02-10 11:56 UTC (permalink / raw)
  To: kernel test robot, song, yukuai, linan122, linux-raid
  Cc: oe-kbuild-all, ncroxon, stable
In-Reply-To: <202602101220.J4BofeDD-lkp@intel.com>

On 2/10/26 3:14 AM, kernel test robot wrote:
> !-------------------------------------------------------------------|
>    This Message Is From an External Sender
>    This message came from outside your organization.
> |-------------------------------------------------------------------!
> 
> Hi Josh,
> 
> kernel test robot noticed the following build errors:
> 
> [auto build test ERROR on linus/master]
> [also build test ERROR on v6.19 next-20260209]
> [If your patch is applied to the wrong git tree, kindly drop us a note.
> And when submitting patch, we suggest to use '--base' as documented in
> https://urldefense.com/v3/__https://git-scm.com/docs/git-format-patch*_base_tree_information__;Iw!!GjvTz_vk!QT9JklLw7YgSInuzDS_0EDSnJkZTTG057ef2CiWO2UwIN0aUk9RGNuVtQ6XCy8sTbfadURM$ ]
> 
> url:    https://urldefense.com/v3/__https://github.com/intel-lab-lkp/linux/commits/Josh-Hunt/md-raid10-fix-deadlock-with-check-operation-and-nowait-requests/20260210-135305__;!!GjvTz_vk!QT9JklLw7YgSInuzDS_0EDSnJkZTTG057ef2CiWO2UwIN0aUk9RGNuVtQ6XCy8sTilUfWlw$
> base:   linus/master
> patch link:    https://urldefense.com/v3/__https://lore.kernel.org/r/20260210050942.3731656-1-johunt*40akamai.com__;JQ!!GjvTz_vk!QT9JklLw7YgSInuzDS_0EDSnJkZTTG057ef2CiWO2UwIN0aUk9RGNuVtQ6XCy8sT0QqCm3Y$
> patch subject: [PATCH] md/raid10: fix deadlock with check operation and nowait requests
> config: x86_64-rhel-9.4 (https://urldefense.com/v3/__https://download.01.org/0day-ci/archive/20260210/202602101220.J4BofeDD-lkp@intel.com/config__;!!GjvTz_vk!QT9JklLw7YgSInuzDS_0EDSnJkZTTG057ef2CiWO2UwIN0aUk9RGNuVtQ6XCy8sTZp6wg4c$ )
> compiler: gcc-14 (Debian 14.2.0-19) 14.2.0
> reproduce (this is a W=1 build): (https://urldefense.com/v3/__https://download.01.org/0day-ci/archive/20260210/202602101220.J4BofeDD-lkp@intel.com/reproduce__;!!GjvTz_vk!QT9JklLw7YgSInuzDS_0EDSnJkZTTG057ef2CiWO2UwIN0aUk9RGNuVtQ6XCy8sTFU3cM60$ )
> 
> If you fix the issue in a separate patch/commit (i.e. not just a new version of
> the same patch/commit), kindly add following tags
> | Reported-by: kernel test robot <lkp@intel.com>
> | Closes: https://urldefense.com/v3/__https://lore.kernel.org/oe-kbuild-all/202602101220.J4BofeDD-lkp@intel.com/__;!!GjvTz_vk!QT9JklLw7YgSInuzDS_0EDSnJkZTTG057ef2CiWO2UwIN0aUk9RGNuVtQ6XCy8sTUPVdYDI$
> 
> All errors (new ones prefixed by >>):
> 
>     drivers/md/raid10.c: In function 'raid10_read_request':
>>> drivers/md/raid10.c:1257:9: error: too few arguments to function 'raid_end_bio_io'
>      1257 |         raid_end_bio_io(r10_bio);
>           |         ^~~~~~~~~~~~~~~
>     drivers/md/raid10.c:321:13: note: declared here
>       321 | static void raid_end_bio_io(struct r10bio *r10_bio, bool adjust_pending)
>           |             ^~~~~~~~~~~~~~~
>     drivers/md/raid10.c: In function 'raid10_write_request':
>     drivers/md/raid10.c:1540:9: error: too few arguments to function 'raid_end_bio_io'
>      1540 |         raid_end_bio_io(r10_bio);
>           |         ^~~~~~~~~~~~~~~
>     drivers/md/raid10.c:321:13: note: declared here
>       321 | static void raid_end_bio_io(struct r10bio *r10_bio, bool adjust_pending)
>           |             ^~~~~~~~~~~~~~~
> 
> 
> vim +/raid_end_bio_io +1257 drivers/md/raid10.c
> 
> caea3c47ad5152 Guoqing Jiang     2018-12-07  1161
> bb5f1ed70bc3bb Robert LeBlanc    2016-12-05  1162  static void raid10_read_request(struct mddev *mddev, struct bio *bio,
> 820455238366a7 Yu Kuai           2023-06-22  1163  				struct r10bio *r10_bio, bool io_accounting)
> ^1da177e4c3f41 Linus Torvalds    2005-04-16  1164  {
> e879a8793f915a NeilBrown         2011-10-11  1165  	struct r10conf *conf = mddev->private;
> ^1da177e4c3f41 Linus Torvalds    2005-04-16  1166  	struct bio *read_bio;
> d4432c23be957f NeilBrown         2011-07-28  1167  	int max_sectors;
> bb5f1ed70bc3bb Robert LeBlanc    2016-12-05  1168  	struct md_rdev *rdev;
> 545250f2480911 NeilBrown         2017-04-05  1169  	char b[BDEVNAME_SIZE];
> 545250f2480911 NeilBrown         2017-04-05  1170  	int slot = r10_bio->read_slot;
> 545250f2480911 NeilBrown         2017-04-05  1171  	struct md_rdev *err_rdev = NULL;
> 545250f2480911 NeilBrown         2017-04-05  1172  	gfp_t gfp = GFP_NOIO;
> 9b622e2bbcf049 Tomasz Majchrzak  2016-07-28  1173
> 93decc563637c4 Kevin Vigor       2020-11-06  1174  	if (slot >= 0 && r10_bio->devs[slot].rdev) {
> 545250f2480911 NeilBrown         2017-04-05  1175  		/*
> 545250f2480911 NeilBrown         2017-04-05  1176  		 * This is an error retry, but we cannot
> 545250f2480911 NeilBrown         2017-04-05  1177  		 * safely dereference the rdev in the r10_bio,
> 545250f2480911 NeilBrown         2017-04-05  1178  		 * we must use the one in conf.
> 545250f2480911 NeilBrown         2017-04-05  1179  		 * If it has already been disconnected (unlikely)
> 545250f2480911 NeilBrown         2017-04-05  1180  		 * we lose the device name in error messages.
> 545250f2480911 NeilBrown         2017-04-05  1181  		 */
> 545250f2480911 NeilBrown         2017-04-05  1182  		int disk;
> 545250f2480911 NeilBrown         2017-04-05  1183  		/*
> 545250f2480911 NeilBrown         2017-04-05  1184  		 * As we are blocking raid10, it is a little safer to
> 545250f2480911 NeilBrown         2017-04-05  1185  		 * use __GFP_HIGH.
> 545250f2480911 NeilBrown         2017-04-05  1186  		 */
> 545250f2480911 NeilBrown         2017-04-05  1187  		gfp = GFP_NOIO | __GFP_HIGH;
> 545250f2480911 NeilBrown         2017-04-05  1188
> 545250f2480911 NeilBrown         2017-04-05  1189  		disk = r10_bio->devs[slot].devnum;
> a448af25becf4b Yu Kuai           2023-11-25  1190  		err_rdev = conf->mirrors[disk].rdev;
> 545250f2480911 NeilBrown         2017-04-05  1191  		if (err_rdev)
> 900d156bac2bc4 Christoph Hellwig 2022-07-13  1192  			snprintf(b, sizeof(b), "%pg", err_rdev->bdev);
> 545250f2480911 NeilBrown         2017-04-05  1193  		else {
> 545250f2480911 NeilBrown         2017-04-05  1194  			strcpy(b, "???");
> 545250f2480911 NeilBrown         2017-04-05  1195  			/* This never gets dereferenced */
> 545250f2480911 NeilBrown         2017-04-05  1196  			err_rdev = r10_bio->devs[slot].rdev;
> 545250f2480911 NeilBrown         2017-04-05  1197  		}
> 545250f2480911 NeilBrown         2017-04-05  1198  	}
> 856e08e23762df NeilBrown         2011-07-28  1199
> 43806c3d5b9bb7 Nigel Croxon      2025-07-03  1200  	if (!regular_request_wait(mddev, conf, bio, r10_bio->sectors)) {
> 4e9814d1943b0e Josh Hunt         2026-02-10  1201  		raid_end_bio_io(r10_bio, false);
> c9aa889b035fca Vishal Verma      2021-12-21  1202  		return;
> 43806c3d5b9bb7 Nigel Croxon      2025-07-03  1203  	}
> 43806c3d5b9bb7 Nigel Croxon      2025-07-03  1204
> 96c3fd1f380237 NeilBrown         2011-12-23  1205  	rdev = read_balance(conf, r10_bio, &max_sectors);
> 96c3fd1f380237 NeilBrown         2011-12-23  1206  	if (!rdev) {
> 545250f2480911 NeilBrown         2017-04-05  1207  		if (err_rdev) {
> 545250f2480911 NeilBrown         2017-04-05  1208  			pr_crit_ratelimited("md/raid10:%s: %s: unrecoverable I/O read error for block %llu\n",
> 545250f2480911 NeilBrown         2017-04-05  1209  					    mdname(mddev), b,
> 545250f2480911 NeilBrown         2017-04-05  1210  					    (unsigned long long)r10_bio->sector);
> 545250f2480911 NeilBrown         2017-04-05  1211  		}
> 4e9814d1943b0e Josh Hunt         2026-02-10  1212  		raid_end_bio_io(r10_bio, true);
> 5a7bbad27a4103 Christoph Hellwig 2011-09-12  1213  		return;
> ^1da177e4c3f41 Linus Torvalds    2005-04-16  1214  	}
> 545250f2480911 NeilBrown         2017-04-05  1215  	if (err_rdev)
> 913cce5a1e588e Christoph Hellwig 2022-05-12  1216  		pr_err_ratelimited("md/raid10:%s: %pg: redirecting sector %llu to another mirror\n",
> 545250f2480911 NeilBrown         2017-04-05  1217  				   mdname(mddev),
> 913cce5a1e588e Christoph Hellwig 2022-05-12  1218  				   rdev->bdev,
> 545250f2480911 NeilBrown         2017-04-05  1219  				   (unsigned long long)r10_bio->sector);
> fc9977dd069e4f NeilBrown         2017-04-05  1220  	if (max_sectors < bio_sectors(bio)) {
> e820d55cb99dd9 Guoqing Jiang     2018-12-19  1221  		allow_barrier(conf);
> 6fc07785d9b892 Yu Kuai           2025-09-10  1222  		bio = bio_submit_split_bioset(bio, max_sectors,
> 6fc07785d9b892 Yu Kuai           2025-09-10  1223  					      &conf->bio_split);
> c9aa889b035fca Vishal Verma      2021-12-21  1224  		wait_barrier(conf, false);
> 6fc07785d9b892 Yu Kuai           2025-09-10  1225  		if (!bio) {
> 6fc07785d9b892 Yu Kuai           2025-09-10  1226  			set_bit(R10BIO_Returned, &r10_bio->state);
> 4cf58d95290973 John Garry        2024-11-11  1227  			goto err_handle;
> 4cf58d95290973 John Garry        2024-11-11  1228  		}
> 22f166218f7313 Yu Kuai           2025-09-10  1229
> fc9977dd069e4f NeilBrown         2017-04-05  1230  		r10_bio->master_bio = bio;
> fc9977dd069e4f NeilBrown         2017-04-05  1231  		r10_bio->sectors = max_sectors;
> fc9977dd069e4f NeilBrown         2017-04-05  1232  	}
> 96c3fd1f380237 NeilBrown         2011-12-23  1233  	slot = r10_bio->read_slot;
> ^1da177e4c3f41 Linus Torvalds    2005-04-16  1234
> 820455238366a7 Yu Kuai           2023-06-22  1235  	if (io_accounting) {
> 820455238366a7 Yu Kuai           2023-06-22  1236  		md_account_bio(mddev, &bio);
> 820455238366a7 Yu Kuai           2023-06-22  1237  		r10_bio->master_bio = bio;
> 820455238366a7 Yu Kuai           2023-06-22  1238  	}
> abfc426d1b2fb2 Christoph Hellwig 2022-02-02  1239  	read_bio = bio_alloc_clone(rdev->bdev, bio, gfp, &mddev->bio_set);
> 5fa31c49928139 Zheng Qixing      2025-07-02  1240  	read_bio->bi_opf &= ~REQ_NOWAIT;
> ^1da177e4c3f41 Linus Torvalds    2005-04-16  1241
> ^1da177e4c3f41 Linus Torvalds    2005-04-16  1242  	r10_bio->devs[slot].bio = read_bio;
> abbf098e6e1e23 NeilBrown         2011-12-23  1243  	r10_bio->devs[slot].rdev = rdev;
> ^1da177e4c3f41 Linus Torvalds    2005-04-16  1244
> 4f024f3797c43c Kent Overstreet   2013-10-11  1245  	read_bio->bi_iter.bi_sector = r10_bio->devs[slot].addr +
> f8c9e74ff0832f NeilBrown         2012-05-21  1246  		choose_data_offset(r10_bio, rdev);
> ^1da177e4c3f41 Linus Torvalds    2005-04-16  1247  	read_bio->bi_end_io = raid10_end_read_request;
> 8d3ca83dcf9ca3 NeilBrown         2016-11-18  1248  	if (test_bit(FailFast, &rdev->flags) &&
> 8d3ca83dcf9ca3 NeilBrown         2016-11-18  1249  	    test_bit(R10BIO_FailFast, &r10_bio->state))
> 8d3ca83dcf9ca3 NeilBrown         2016-11-18  1250  	        read_bio->bi_opf |= MD_FAILFAST;
> ^1da177e4c3f41 Linus Torvalds    2005-04-16  1251  	read_bio->bi_private = r10_bio;
> c396b90e502691 Christoph Hellwig 2024-03-03  1252  	mddev_trace_remap(mddev, read_bio, r10_bio->sector);
> ed00aabd5eb9fb Christoph Hellwig 2020-07-01  1253  	submit_bio_noacct(read_bio);
> 5a7bbad27a4103 Christoph Hellwig 2011-09-12  1254  	return;
> 4cf58d95290973 John Garry        2024-11-11  1255  err_handle:
> 4cf58d95290973 John Garry        2024-11-11  1256  	atomic_dec(&rdev->nr_pending);
> 4cf58d95290973 John Garry        2024-11-11 @1257  	raid_end_bio_io(r10_bio);
> ^1da177e4c3f41 Linus Torvalds    2005-04-16  1258  }
> ^1da177e4c3f41 Linus Torvalds    2005-04-16  1259
> 

Apologies. I cherry-picked this from my 6.12.y branch which is where we 
hit the issue, but looks like I forgot to build test it. Will send a v2.

Josh

^ permalink raw reply

* [PATCH v2] md/raid10: fix deadlock with check operation and nowait requests
From: Josh Hunt @ 2026-02-10 16:51 UTC (permalink / raw)
  To: song, yukuai, linan122, linux-raid; +Cc: ncroxon, Josh Hunt, stable

When an array check is running it will raise the barrier at which point
normal requests will become blocked and increment the nr_pending value to
signal there is work pending inside of wait_barrier(). NOWAIT requests
do not block and so will return immediately with an error, and additionally
do not increment nr_pending in wait_barrier(). Upstream change
43806c3d5b9b ("raid10: cleanup memleak at raid10_make_request") added a
call to raid_end_bio_io() to fix a memory leak when NOWAIT requests hit
this condition. raid_end_bio_io() eventually calls allow_barrier() and
it will unconditionally do an atomic_dec_and_test(&conf->nr_pending) even
though the corresponding increment on nr_pending didn't happen in the
NOWAIT case.

This can be easily seen by starting a check operation while an application is
doing nowait IO on the same array. This results in a deadlocked state due to
nr_pending value underflowing and so the md resync thread gets stuck waiting
for nr_pending to == 0.

Output of r10conf state of the array when we hit this condition:

  crash> struct r10conf.barrier,nr_pending,nr_waiting,nr_queued <addr of r10conf>
    barrier = 1,
    nr_pending = {
      counter = -41
    },
    nr_waiting = 15,
    nr_queued = 0,

Example of md_sync thread stuck waiting on raise_barrier() and other requests
stuck in wait_barrier():

md1_resync
[<0>] raise_barrier+0xce/0x1c0
[<0>] raid10_sync_request+0x1ca/0x1ed0
[<0>] md_do_sync+0x779/0x1110
[<0>] md_thread+0x90/0x160
[<0>] kthread+0xbe/0xf0
[<0>] ret_from_fork+0x34/0x50
[<0>] ret_from_fork_asm+0x1a/0x30

kworker/u1040:2+flush-253:4
[<0>] wait_barrier+0x1de/0x220
[<0>] regular_request_wait+0x30/0x180
[<0>] raid10_make_request+0x261/0x1000
[<0>] md_handle_request+0x13b/0x230
[<0>] __submit_bio+0x107/0x1f0
[<0>] submit_bio_noacct_nocheck+0x16f/0x390
[<0>] ext4_io_submit+0x24/0x40
[<0>] ext4_do_writepages+0x254/0xc80
[<0>] ext4_writepages+0x84/0x120
[<0>] do_writepages+0x7a/0x260
[<0>] __writeback_single_inode+0x3d/0x300
[<0>] writeback_sb_inodes+0x1dd/0x470
[<0>] __writeback_inodes_wb+0x4c/0xe0
[<0>] wb_writeback+0x18b/0x2d0
[<0>] wb_workfn+0x2a1/0x400
[<0>] process_one_work+0x149/0x330
[<0>] worker_thread+0x2d2/0x410
[<0>] kthread+0xbe/0xf0
[<0>] ret_from_fork+0x34/0x50
[<0>] ret_from_fork_asm+0x1a/0x30

Fixes: 43806c3d5b9b ("raid10: cleanup memleak at raid10_make_request")
Cc: stable@vger.kernel.org
Signed-off-by: Josh Hunt <johunt@akamai.com>
---
 drivers/md/raid10.c | 40 +++++++++++++++++++++++++++-------------
 1 file changed, 27 insertions(+), 13 deletions(-)

diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index 9debb20cf129..b05066dde693 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -68,6 +68,7 @@
  */
 
 static void allow_barrier(struct r10conf *conf);
+static void allow_barrier_nowait(struct r10conf *conf);
 static void lower_barrier(struct r10conf *conf);
 static int _enough(struct r10conf *conf, int previous, int ignore);
 static int enough(struct r10conf *conf, int ignore);
@@ -317,7 +318,7 @@ static void reschedule_retry(struct r10bio *r10_bio)
  * operation and are ready to return a success/failure code to the buffer
  * cache layer.
  */
-static void raid_end_bio_io(struct r10bio *r10_bio)
+static void raid_end_bio_io(struct r10bio *r10_bio, bool adjust_pending)
 {
 	struct bio *bio = r10_bio->master_bio;
 	struct r10conf *conf = r10_bio->mddev->private;
@@ -332,7 +333,10 @@ static void raid_end_bio_io(struct r10bio *r10_bio)
 	 * Wake up any possible resync thread that waits for the device
 	 * to go idle.
 	 */
-	allow_barrier(conf);
+	if (adjust_pending)
+		allow_barrier(conf);
+	else
+		allow_barrier_nowait(conf);
 
 	free_r10bio(r10_bio);
 }
@@ -414,7 +418,7 @@ static void raid10_end_read_request(struct bio *bio)
 			uptodate = 1;
 	}
 	if (uptodate) {
-		raid_end_bio_io(r10_bio);
+		raid_end_bio_io(r10_bio, true);
 		rdev_dec_pending(rdev, conf->mddev);
 	} else {
 		/*
@@ -446,7 +450,7 @@ static void one_write_done(struct r10bio *r10_bio)
 			if (test_bit(R10BIO_MadeGood, &r10_bio->state))
 				reschedule_retry(r10_bio);
 			else
-				raid_end_bio_io(r10_bio);
+				raid_end_bio_io(r10_bio, true);
 		}
 	}
 }
@@ -1030,13 +1034,23 @@ static bool wait_barrier(struct r10conf *conf, bool nowait)
 	return ret;
 }
 
-static void allow_barrier(struct r10conf *conf)
+static void __allow_barrier(struct r10conf *conf, bool adjust_pending)
 {
-	if ((atomic_dec_and_test(&conf->nr_pending)) ||
+	if ((adjust_pending && atomic_dec_and_test(&conf->nr_pending)) ||
 			(conf->array_freeze_pending))
 		wake_up_barrier(conf);
 }
 
+static void allow_barrier(struct r10conf *conf)
+{
+	__allow_barrier(conf, true);
+}
+
+static void allow_barrier_nowait(struct r10conf *conf)
+{
+	__allow_barrier(conf, false);
+}
+
 static void freeze_array(struct r10conf *conf, int extra)
 {
 	/* stop syncio and normal IO and wait for everything to
@@ -1184,7 +1198,7 @@ static void raid10_read_request(struct mddev *mddev, struct bio *bio,
 	}
 
 	if (!regular_request_wait(mddev, conf, bio, r10_bio->sectors)) {
-		raid_end_bio_io(r10_bio);
+		raid_end_bio_io(r10_bio, false);
 		return;
 	}
 
@@ -1195,7 +1209,7 @@ static void raid10_read_request(struct mddev *mddev, struct bio *bio,
 					    mdname(mddev), b,
 					    (unsigned long long)r10_bio->sector);
 		}
-		raid_end_bio_io(r10_bio);
+		raid_end_bio_io(r10_bio, true);
 		return;
 	}
 	if (err_rdev)
@@ -1240,7 +1254,7 @@ static void raid10_read_request(struct mddev *mddev, struct bio *bio,
 	return;
 err_handle:
 	atomic_dec(&rdev->nr_pending);
-	raid_end_bio_io(r10_bio);
+	raid_end_bio_io(r10_bio, true);
 }
 
 static void raid10_write_one_disk(struct mddev *mddev, struct r10bio *r10_bio,
@@ -1372,7 +1386,7 @@ static void raid10_write_request(struct mddev *mddev, struct bio *bio,
 
 	sectors = r10_bio->sectors;
 	if (!regular_request_wait(mddev, conf, bio, sectors)) {
-		raid_end_bio_io(r10_bio);
+		raid_end_bio_io(r10_bio, false);
 		return;
 	}
 
@@ -1523,7 +1537,7 @@ static void raid10_write_request(struct mddev *mddev, struct bio *bio,
 		}
 	}
 
-	raid_end_bio_io(r10_bio);
+	raid_end_bio_io(r10_bio, true);
 }
 
 static void __make_request(struct mddev *mddev, struct bio *bio, int sectors)
@@ -2952,7 +2966,7 @@ static void handle_write_completed(struct r10conf *conf, struct r10bio *r10_bio)
 			if (test_bit(R10BIO_WriteError,
 				     &r10_bio->state))
 				close_write(r10_bio);
-			raid_end_bio_io(r10_bio);
+			raid_end_bio_io(r10_bio, true);
 		}
 	}
 }
@@ -2987,7 +3001,7 @@ static void raid10d(struct md_thread *thread)
 			if (test_bit(R10BIO_WriteError,
 				     &r10_bio->state))
 				close_write(r10_bio);
-			raid_end_bio_io(r10_bio);
+			raid_end_bio_io(r10_bio, true);
 		}
 	}
 
-- 
2.34.1


^ permalink raw reply related

* Re: [PATCH 1/2] md/raid1: fix the comparing region of interval tree
From: Yu Kuai @ 2026-02-11  2:18 UTC (permalink / raw)
  To: Xiao Ni; +Cc: song, linux-raid, linan666, yukuai
In-Reply-To: <20260206053826.37416-2-xni@redhat.com>

Hi,

在 2026/2/6 13:38, Xiao Ni 写道:
> Interval tree uses [start, end] as a region which stores in the tree.
> In raid1, it uses the wrong end value. For example:
> bio(A,B) is too big and needs to be split to bio1(A,C-1), bio2(C,B).
> The region of bio1 is [A,C] and the region of bio2 is [C,B]. So bio1 and
> bio2 overlap which is not right.
>
> Fix this problem by using right end value of the region.
>
> Signed-off-by: Xiao Ni <xni@redhat.com>
> ---
>   drivers/md/raid1.c | 4 ++--
>   1 file changed, 2 insertions(+), 2 deletions(-)

Perhaps also add a fix tag, with this:

Reviewed-by: Yu Kuai <yukuai@fnnas.com>

>
> diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
> index 867db18bc3ba..a41b1ec3d695 100644
> --- a/drivers/md/raid1.c
> +++ b/drivers/md/raid1.c
> @@ -62,7 +62,7 @@ static int check_and_add_serial(struct md_rdev *rdev, struct r1bio *r1_bio,
>   	unsigned long flags;
>   	int ret = 0;
>   	sector_t lo = r1_bio->sector;
> -	sector_t hi = lo + r1_bio->sectors;
> +	sector_t hi = lo + r1_bio->sectors - 1;
>   	struct serial_in_rdev *serial = &rdev->serial[idx];
>   
>   	spin_lock_irqsave(&serial->serial_lock, flags);
> @@ -453,7 +453,7 @@ static void raid1_end_write_request(struct bio *bio)
>   	int mirror = find_bio_disk(r1_bio, bio);
>   	struct md_rdev *rdev = conf->mirrors[mirror].rdev;
>   	sector_t lo = r1_bio->sector;
> -	sector_t hi = r1_bio->sector + r1_bio->sectors;
> +	sector_t hi = r1_bio->sector + r1_bio->sectors - 1;
>   	bool ignore_error = !raid1_should_handle_error(bio) ||
>   		(bio->bi_status && bio_op(bio) == REQ_OP_DISCARD);
>   

-- 
Thansk,
Kuai

^ permalink raw reply

* Re: [PATCH 2/2] md/raid1: serialize overlapped io for writemostly disk
From: Yu Kuai @ 2026-02-11  2:31 UTC (permalink / raw)
  To: Xiao Ni; +Cc: song, linux-raid, linan666, yukuai
In-Reply-To: <20260206053826.37416-3-xni@redhat.com>

Hi,

在 2026/2/6 13:38, Xiao Ni 写道:
> In behind mode, overlap bios for writemostly device are queued. They
> In behind mode, bios which overlap the bio from the interval tree
> need to wait in waitqueue. Those bios will be handled once the in-tree
> bio finishes. They are waken up and try to get the tree lock. The bio
> which gets the lock will be handled. So the sequence can't be guaranteed.
>
> For example
> bio1(100,200)
> bio2(150,200)
> bio3(150,300)
> The write sequence of fast device is bio1,bio2,bio3. But the write
> sequence of slow device is bio1,bio3,bio2. This causes data corruption.
> Replace waitqueue with a fifo list to guarantee the write sequence.

To be honest, I think this is fine with the respect user won't send overlap
bios. In this case, bio1 is done in fast disk and pending in slow disk, then
bio2 will stuck while issuing and waiting for bio1 to return from slow disk.
So send bio3 in this case is not expected, and if you do you can't guarantee
the complete order from fast disk as bio2, bio3 as well.

>
> Signed-off-by: Xiao Ni <xni@redhat.com>
> ---
>   drivers/md/md.c    |  1 -
>   drivers/md/md.h    |  4 +++-
>   drivers/md/raid1.c | 39 +++++++++++++++++++++++++++++----------
>   3 files changed, 32 insertions(+), 12 deletions(-)
>
> diff --git a/drivers/md/md.c b/drivers/md/md.c
> index 59cd303548de..ab91d17c2d68 100644
> --- a/drivers/md/md.c
> +++ b/drivers/md/md.c
> @@ -188,7 +188,6 @@ static int rdev_init_serial(struct md_rdev *rdev)
>   
>   		spin_lock_init(&serial_tmp->serial_lock);
>   		serial_tmp->serial_rb = RB_ROOT_CACHED;
> -		init_waitqueue_head(&serial_tmp->serial_io_wait);
>   	}
>   
>   	rdev->serial = serial;
> diff --git a/drivers/md/md.h b/drivers/md/md.h
> index ac84289664cd..64ffbb2efb87 100644
> --- a/drivers/md/md.h
> +++ b/drivers/md/md.h
> @@ -126,7 +126,6 @@ enum sync_action {
>   struct serial_in_rdev {
>   	struct rb_root_cached serial_rb;
>   	spinlock_t serial_lock;
> -	wait_queue_head_t serial_io_wait;
>   };
>   
>   /*
> @@ -382,6 +381,9 @@ struct serial_info {
>   	sector_t start;		/* start sector of rb node */
>   	sector_t last;		/* end sector of rb node */
>   	sector_t _subtree_last; /* highest sector in subtree of rb node */
> +	struct list_head	list_node;
> +	struct list_head	waiters;
> +	struct completion	ready;
>   };
>   
>   /*
> diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
> index a41b1ec3d695..38c73538d038 100644
> --- a/drivers/md/raid1.c
> +++ b/drivers/md/raid1.c
> @@ -57,19 +57,25 @@ INTERVAL_TREE_DEFINE(struct serial_info, node, sector_t, _subtree_last,
>   		     START, LAST, static inline, raid1_rb);
>   
>   static int check_and_add_serial(struct md_rdev *rdev, struct r1bio *r1_bio,
> -				struct serial_info *si, int idx)
> +				struct serial_info *si)
>   {
>   	unsigned long flags;
>   	int ret = 0;
>   	sector_t lo = r1_bio->sector;
>   	sector_t hi = lo + r1_bio->sectors - 1;
> +	int idx = sector_to_idx(r1_bio->sector);
>   	struct serial_in_rdev *serial = &rdev->serial[idx];
> +	struct serial_info *head_si;
>   
>   	spin_lock_irqsave(&serial->serial_lock, flags);
>   	/* collision happened */
> -	if (raid1_rb_iter_first(&serial->serial_rb, lo, hi))
> +	head_si = raid1_rb_iter_first(&serial->serial_rb, lo, hi);
> +	if (head_si && head_si != si) {
> +		si->start = lo;
> +		si->last = hi;
> +		list_add_tail(&si->list_node, &head_si->waiters);
>   		ret = -EBUSY;
> -	else {
> +	} else if (!head_si) {
>   		si->start = lo;
>   		si->last = hi;
>   		raid1_rb_insert(si, &serial->serial_rb);
> @@ -83,19 +89,23 @@ static void wait_for_serialization(struct md_rdev *rdev, struct r1bio *r1_bio)
>   {
>   	struct mddev *mddev = rdev->mddev;
>   	struct serial_info *si;
> -	int idx = sector_to_idx(r1_bio->sector);
> -	struct serial_in_rdev *serial = &rdev->serial[idx];
>   
>   	if (WARN_ON(!mddev->serial_info_pool))
>   		return;
>   	si = mempool_alloc(mddev->serial_info_pool, GFP_NOIO);
> -	wait_event(serial->serial_io_wait,
> -		   check_and_add_serial(rdev, r1_bio, si, idx) == 0);
> +	INIT_LIST_HEAD(&si->waiters);
> +	INIT_LIST_HEAD(&si->list_node);
> +	init_completion(&si->ready);
> +	while (check_and_add_serial(rdev, r1_bio, si)) {
> +		wait_for_completion(&si->ready);
> +		reinit_completion(&si->ready);
> +	}
>   }
>   
>   static void remove_serial(struct md_rdev *rdev, sector_t lo, sector_t hi)
>   {
>   	struct serial_info *si;
> +	struct serial_info *first_si;
>   	unsigned long flags;
>   	int found = 0;
>   	struct mddev *mddev = rdev->mddev;
> @@ -106,16 +116,25 @@ static void remove_serial(struct md_rdev *rdev, sector_t lo, sector_t hi)
>   	for (si = raid1_rb_iter_first(&serial->serial_rb, lo, hi);
>   	     si; si = raid1_rb_iter_next(si, lo, hi)) {
>   		if (si->start == lo && si->last == hi) {
> -			raid1_rb_remove(si, &serial->serial_rb);
> -			mempool_free(si, mddev->serial_info_pool);
>   			found = 1;
>   			break;
>   		}
>   	}
>   	if (!found)
>   		WARN(1, "The write IO is not recorded for serialization\n");
> +	else {
> +		raid1_rb_remove(si, &serial->serial_rb);
> +		if (!list_empty(&si->waiters)) {
> +			first_si = list_first_entry(&si->waiters,
> +						struct serial_info, list_node);
> +			list_del_init(&first_si->list_node);
> +			list_splice_init(&si->waiters, &first_si->waiters);
> +			raid1_rb_insert(first_si, &serial->serial_rb);
> +			complete(&first_si->ready);
> +		}
> +		mempool_free(si, mddev->serial_info_pool);
> +	}
>   	spin_unlock_irqrestore(&serial->serial_lock, flags);
> -	wake_up(&serial->serial_io_wait);
>   }
>   
>   /*

-- 
Thansk,
Kuai

^ permalink raw reply

* Re: [PATCH 1/2] md/raid1: fix the comparing region of interval tree
From: Xiao Ni @ 2026-02-11  2:35 UTC (permalink / raw)
  To: yukuai; +Cc: song, linux-raid, linan666
In-Reply-To: <02c05b44-ee43-44c0-ae39-d1c77d31158f@fnnas.com>

On Wed, Feb 11, 2026 at 10:19 AM Yu Kuai <yukuai@fnnas.com> wrote:
>
> Hi,
>
> 在 2026/2/6 13:38, Xiao Ni 写道:
> > Interval tree uses [start, end] as a region which stores in the tree.
> > In raid1, it uses the wrong end value. For example:
> > bio(A,B) is too big and needs to be split to bio1(A,C-1), bio2(C,B).
> > The region of bio1 is [A,C] and the region of bio2 is [C,B]. So bio1 and
> > bio2 overlap which is not right.
> >
> > Fix this problem by using right end value of the region.
> >
> > Signed-off-by: Xiao Ni <xni@redhat.com>
> > ---
> >   drivers/md/raid1.c | 4 ++--
> >   1 file changed, 2 insertions(+), 2 deletions(-)
>
> Perhaps also add a fix tag, with this:

Thanks for the suggestion. I'll add it and send V2
>
> Reviewed-by: Yu Kuai <yukuai@fnnas.com>
>
> >
> > diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
> > index 867db18bc3ba..a41b1ec3d695 100644
> > --- a/drivers/md/raid1.c
> > +++ b/drivers/md/raid1.c
> > @@ -62,7 +62,7 @@ static int check_and_add_serial(struct md_rdev *rdev, struct r1bio *r1_bio,
> >       unsigned long flags;
> >       int ret = 0;
> >       sector_t lo = r1_bio->sector;
> > -     sector_t hi = lo + r1_bio->sectors;
> > +     sector_t hi = lo + r1_bio->sectors - 1;
> >       struct serial_in_rdev *serial = &rdev->serial[idx];
> >
> >       spin_lock_irqsave(&serial->serial_lock, flags);
> > @@ -453,7 +453,7 @@ static void raid1_end_write_request(struct bio *bio)
> >       int mirror = find_bio_disk(r1_bio, bio);
> >       struct md_rdev *rdev = conf->mirrors[mirror].rdev;
> >       sector_t lo = r1_bio->sector;
> > -     sector_t hi = r1_bio->sector + r1_bio->sectors;
> > +     sector_t hi = r1_bio->sector + r1_bio->sectors - 1;
> >       bool ignore_error = !raid1_should_handle_error(bio) ||
> >               (bio->bi_status && bio_op(bio) == REQ_OP_DISCARD);
> >
>
> --
> Thansk,
> Kuai
>


^ permalink raw reply

* Re: [PATCH 2/2] md/raid1: serialize overlapped io for writemostly disk
From: Xiao Ni @ 2026-02-11  2:44 UTC (permalink / raw)
  To: yukuai; +Cc: song, linux-raid, linan666
In-Reply-To: <9bd6581f-289a-4490-af6d-f15589446e1e@fnnas.com>

On Wed, Feb 11, 2026 at 10:32 AM Yu Kuai <yukuai@fnnas.com> wrote:
>
> Hi,
>
> 在 2026/2/6 13:38, Xiao Ni 写道:
> > In behind mode, overlap bios for writemostly device are queued. They
> > In behind mode, bios which overlap the bio from the interval tree
> > need to wait in waitqueue. Those bios will be handled once the in-tree
> > bio finishes. They are waken up and try to get the tree lock. The bio
> > which gets the lock will be handled. So the sequence can't be guaranteed.
> >
> > For example
> > bio1(100,200)
> > bio2(150,200)
> > bio3(150,300)
> > The write sequence of fast device is bio1,bio2,bio3. But the write
> > sequence of slow device is bio1,bio3,bio2. This causes data corruption.
> > Replace waitqueue with a fifo list to guarantee the write sequence.
>
> To be honest, I think this is fine with the respect user won't send overlap
> bios. In this case, bio1 is done in fast disk and pending in slow disk, then
> bio2 will stuck while issuing and waiting for bio1 to return from slow disk.
> So send bio3 in this case is not expected, and if you do you can't guarantee
> the complete order from fast disk as bio2, bio3 as well.

Hi Kuai

In write behind mode, raid1 returns bio1 to the upper layer once the
write on the fast disk finishes. The upper layer (app/filesystem)
thinks that bio1 has finished, while the write of bio1 to the slow
disk is still in progress. So bio2 will not be stuck while issuing, am
I right? If so, bio2 can be submitted to raid1 once the write of bio1
to the fast disk finishes. And bio3 can be submitted to raid1 once the
write of bio2 to fast disk finishes.

Best Regards
Xiao
>
> >
> > Signed-off-by: Xiao Ni <xni@redhat.com>
> > ---
> >   drivers/md/md.c    |  1 -
> >   drivers/md/md.h    |  4 +++-
> >   drivers/md/raid1.c | 39 +++++++++++++++++++++++++++++----------
> >   3 files changed, 32 insertions(+), 12 deletions(-)
> >
> > diff --git a/drivers/md/md.c b/drivers/md/md.c
> > index 59cd303548de..ab91d17c2d68 100644
> > --- a/drivers/md/md.c
> > +++ b/drivers/md/md.c
> > @@ -188,7 +188,6 @@ static int rdev_init_serial(struct md_rdev *rdev)
> >
> >               spin_lock_init(&serial_tmp->serial_lock);
> >               serial_tmp->serial_rb = RB_ROOT_CACHED;
> > -             init_waitqueue_head(&serial_tmp->serial_io_wait);
> >       }
> >
> >       rdev->serial = serial;
> > diff --git a/drivers/md/md.h b/drivers/md/md.h
> > index ac84289664cd..64ffbb2efb87 100644
> > --- a/drivers/md/md.h
> > +++ b/drivers/md/md.h
> > @@ -126,7 +126,6 @@ enum sync_action {
> >   struct serial_in_rdev {
> >       struct rb_root_cached serial_rb;
> >       spinlock_t serial_lock;
> > -     wait_queue_head_t serial_io_wait;
> >   };
> >
> >   /*
> > @@ -382,6 +381,9 @@ struct serial_info {
> >       sector_t start;         /* start sector of rb node */
> >       sector_t last;          /* end sector of rb node */
> >       sector_t _subtree_last; /* highest sector in subtree of rb node */
> > +     struct list_head        list_node;
> > +     struct list_head        waiters;
> > +     struct completion       ready;
> >   };
> >
> >   /*
> > diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
> > index a41b1ec3d695..38c73538d038 100644
> > --- a/drivers/md/raid1.c
> > +++ b/drivers/md/raid1.c
> > @@ -57,19 +57,25 @@ INTERVAL_TREE_DEFINE(struct serial_info, node, sector_t, _subtree_last,
> >                    START, LAST, static inline, raid1_rb);
> >
> >   static int check_and_add_serial(struct md_rdev *rdev, struct r1bio *r1_bio,
> > -                             struct serial_info *si, int idx)
> > +                             struct serial_info *si)
> >   {
> >       unsigned long flags;
> >       int ret = 0;
> >       sector_t lo = r1_bio->sector;
> >       sector_t hi = lo + r1_bio->sectors - 1;
> > +     int idx = sector_to_idx(r1_bio->sector);
> >       struct serial_in_rdev *serial = &rdev->serial[idx];
> > +     struct serial_info *head_si;
> >
> >       spin_lock_irqsave(&serial->serial_lock, flags);
> >       /* collision happened */
> > -     if (raid1_rb_iter_first(&serial->serial_rb, lo, hi))
> > +     head_si = raid1_rb_iter_first(&serial->serial_rb, lo, hi);
> > +     if (head_si && head_si != si) {
> > +             si->start = lo;
> > +             si->last = hi;
> > +             list_add_tail(&si->list_node, &head_si->waiters);
> >               ret = -EBUSY;
> > -     else {
> > +     } else if (!head_si) {
> >               si->start = lo;
> >               si->last = hi;
> >               raid1_rb_insert(si, &serial->serial_rb);
> > @@ -83,19 +89,23 @@ static void wait_for_serialization(struct md_rdev *rdev, struct r1bio *r1_bio)
> >   {
> >       struct mddev *mddev = rdev->mddev;
> >       struct serial_info *si;
> > -     int idx = sector_to_idx(r1_bio->sector);
> > -     struct serial_in_rdev *serial = &rdev->serial[idx];
> >
> >       if (WARN_ON(!mddev->serial_info_pool))
> >               return;
> >       si = mempool_alloc(mddev->serial_info_pool, GFP_NOIO);
> > -     wait_event(serial->serial_io_wait,
> > -                check_and_add_serial(rdev, r1_bio, si, idx) == 0);
> > +     INIT_LIST_HEAD(&si->waiters);
> > +     INIT_LIST_HEAD(&si->list_node);
> > +     init_completion(&si->ready);
> > +     while (check_and_add_serial(rdev, r1_bio, si)) {
> > +             wait_for_completion(&si->ready);
> > +             reinit_completion(&si->ready);
> > +     }
> >   }
> >
> >   static void remove_serial(struct md_rdev *rdev, sector_t lo, sector_t hi)
> >   {
> >       struct serial_info *si;
> > +     struct serial_info *first_si;
> >       unsigned long flags;
> >       int found = 0;
> >       struct mddev *mddev = rdev->mddev;
> > @@ -106,16 +116,25 @@ static void remove_serial(struct md_rdev *rdev, sector_t lo, sector_t hi)
> >       for (si = raid1_rb_iter_first(&serial->serial_rb, lo, hi);
> >            si; si = raid1_rb_iter_next(si, lo, hi)) {
> >               if (si->start == lo && si->last == hi) {
> > -                     raid1_rb_remove(si, &serial->serial_rb);
> > -                     mempool_free(si, mddev->serial_info_pool);
> >                       found = 1;
> >                       break;
> >               }
> >       }
> >       if (!found)
> >               WARN(1, "The write IO is not recorded for serialization\n");
> > +     else {
> > +             raid1_rb_remove(si, &serial->serial_rb);
> > +             if (!list_empty(&si->waiters)) {
> > +                     first_si = list_first_entry(&si->waiters,
> > +                                             struct serial_info, list_node);
> > +                     list_del_init(&first_si->list_node);
> > +                     list_splice_init(&si->waiters, &first_si->waiters);
> > +                     raid1_rb_insert(first_si, &serial->serial_rb);
> > +                     complete(&first_si->ready);
> > +             }
> > +             mempool_free(si, mddev->serial_info_pool);
> > +     }
> >       spin_unlock_irqrestore(&serial->serial_lock, flags);
> > -     wake_up(&serial->serial_io_wait);
> >   }
> >
> >   /*
>
> --
> Thansk,
> Kuai
>


^ permalink raw reply

* Re: [PATCH 2/2] md/raid1: serialize overlapped io for writemostly disk
From: Xiao Ni @ 2026-02-11  2:50 UTC (permalink / raw)
  To: yukuai; +Cc: song, linux-raid, linan666
In-Reply-To: <CALTww29NpmNbEb79KbVCX-rAF4o39rEd3-P1TjiDTc1OWpQiAw@mail.gmail.com>

On Wed, Feb 11, 2026 at 10:44 AM Xiao Ni <xni@redhat.com> wrote:
>
> On Wed, Feb 11, 2026 at 10:32 AM Yu Kuai <yukuai@fnnas.com> wrote:
> >
> > Hi,
> >
> > 在 2026/2/6 13:38, Xiao Ni 写道:
> > > In behind mode, overlap bios for writemostly device are queued. They
> > > In behind mode, bios which overlap the bio from the interval tree
> > > need to wait in waitqueue. Those bios will be handled once the in-tree
> > > bio finishes. They are waken up and try to get the tree lock. The bio
> > > which gets the lock will be handled. So the sequence can't be guaranteed.
> > >
> > > For example
> > > bio1(100,200)
> > > bio2(150,200)
> > > bio3(150,300)
> > > The write sequence of fast device is bio1,bio2,bio3. But the write
> > > sequence of slow device is bio1,bio3,bio2. This causes data corruption.
> > > Replace waitqueue with a fifo list to guarantee the write sequence.
> >
> > To be honest, I think this is fine with the respect user won't send overlap
> > bios. In this case, bio1 is done in fast disk and pending in slow disk, then
> > bio2 will stuck while issuing and waiting for bio1 to return from slow disk.
> > So send bio3 in this case is not expected, and if you do you can't guarantee
> > the complete order from fast disk as bio2, bio3 as well.
>
> Hi Kuai
>
> In write behind mode, raid1 returns bio1 to the upper layer once the
> write on the fast disk finishes. The upper layer (app/filesystem)
> thinks that bio1 has finished, while the write of bio1 to the slow
> disk is still in progress. So bio2 will not be stuck while issuing, am
> I right? If so, bio2 can be submitted to raid1 once the write of bio1
> to the fast disk finishes. And bio3 can be submitted to raid1 once the
> write of bio2 to fast disk finishes.

By the way, it's reported by our customer, there is a data corruption
by the following test script:
#!/bin/sh

disk1=/dev/loop0
disk2=/dev/loop1
mddev=/dev/md0
mount_md=/data-md
mount_disk1=/data-1
mount_disk2=/data-2
bitmap=/root/bitmap
test_prg=/root/test-serializewrite
test_num=1000
test_str=abcdefghijklmnopqrstuvwxyz

loop=0
touch MD-TEST
while [ -f MD-TEST ]; do
    set -x
    loop=$((loop + 1))
    echo "START LOOP=$loop at $(date)"

    echo "# lsblk"
    lsblk

    echo "# dmsetup"
    dmsetup create disk-500ms --table "0 $(blockdev --getsz $disk2)
delay $disk2 0 500"
    sleep 2

    echo "# mdadm"
    #mdadm --create $mddev --run --assume-clean --force --level=1
--write-behind=1024 --bitmap=$bitmap --raid-devices=2 $disk1
--write-mostly /dev/mapper/disk-500ms
    mdadm --create $mddev --run --assume-clean --force --level=1
--write-behind=1024 --bitmap=internal --raid-devices=2 $disk1
--write-mostly /dev/mapper/disk-500ms

    echo "# cat /proc/mdstat"
    cat /proc/mdstat

    #echo "# mdadm -X $bitmap"
    #mdadm -X $bitmap

    echo "# mkfs.xfs -f $mddev"
    mkfs.xfs -f $mddev

    echo "# mount $mddev $mount_md"
    mount $mddev $mount_md

    set +x
    echo "# $test_prg"
    for n in $(seq 1 $test_num); do
$test_prg $mount_md/test.$n $test_str &
    done

    while true; do
sleep 2
pgrep -f $(basename $test_prg) > /dev/null
if [ $? -ne 0 ]; then
    break
fi
    done

    set -x
    echo "# cat /proc/mdstat"
    cat /proc/mdstat

    #echo "# mdadm -X $bitmap"
    #mdadm -X $bitmap

    echo "# umount $mount_md"
    sleep 10
    while true; do
sleep 2
umount $mount_md
if [ $? -eq 0 ]; then
    break
fi
    done

    echo "# mdadm --stop $mddev"
    mdadm --stop $mddev

    echo "# dmsetup remove disk-500ms"
    while true; do
        dmsetup remove disk-500ms
        if [ $? -ne 0 ]; then
            sleep 5
        else
            break
        fi
    done

    offset=$(mdadm --examine $disk1 | awk -F': ' '/Data Offset/ {print
$2}' | awk '{print $1}')
    offset=$((offset * 512))
    echo "# mount -o ro,offset=$offset,nouuid $disk1 $mount_disk1"
    mount -o ro,offset=$offset,nouuid $disk1 $mount_disk1

    offset=$(mdadm --examine $disk2 | awk -F': ' '/Data Offset/ {print
$2}' | awk '{print $1}')
    #offset=$(mdadm --examine /dev/mapper/disk-500ms | awk -F': '
'/Data Offset/ {print $2}' | awk '{print $1}')

    offset=$((offset * 512))
    echo "# mount -o ro,offset=$offset,nouuid $disk2 $mount_disk2"
    #echo "# mount -o ro,offset=$offset,nouuid /dev/mapper/disk-500ms
$mount_disk2"

    mount -o ro,offset=$offset,nouuid $disk2 $mount_disk2
    #mount -o ro,offset=$offset,nouuid /dev/mapper/disk-500ms $mount_disk2

    echo "# diff -rq $mount_disk1 $mount_disk2"
    diff -rq $mount_disk1 $mount_disk2
    if [ $? -eq 0 ]; then
echo "@@@ PASS (loop=$loop)"
    else
echo "@@@ FAIL (loop=$loop)"
    fi

    umount $mount_disk1
    umount $mount_disk2

    mdadm --zero-superblock  $disk1
    mdadm --zero-superblock  $disk2

    sleep 10
    set +x
done
>
> Best Regards
> Xiao
> >
> > >
> > > Signed-off-by: Xiao Ni <xni@redhat.com>
> > > ---
> > >   drivers/md/md.c    |  1 -
> > >   drivers/md/md.h    |  4 +++-
> > >   drivers/md/raid1.c | 39 +++++++++++++++++++++++++++++----------
> > >   3 files changed, 32 insertions(+), 12 deletions(-)
> > >
> > > diff --git a/drivers/md/md.c b/drivers/md/md.c
> > > index 59cd303548de..ab91d17c2d68 100644
> > > --- a/drivers/md/md.c
> > > +++ b/drivers/md/md.c
> > > @@ -188,7 +188,6 @@ static int rdev_init_serial(struct md_rdev *rdev)
> > >
> > >               spin_lock_init(&serial_tmp->serial_lock);
> > >               serial_tmp->serial_rb = RB_ROOT_CACHED;
> > > -             init_waitqueue_head(&serial_tmp->serial_io_wait);
> > >       }
> > >
> > >       rdev->serial = serial;
> > > diff --git a/drivers/md/md.h b/drivers/md/md.h
> > > index ac84289664cd..64ffbb2efb87 100644
> > > --- a/drivers/md/md.h
> > > +++ b/drivers/md/md.h
> > > @@ -126,7 +126,6 @@ enum sync_action {
> > >   struct serial_in_rdev {
> > >       struct rb_root_cached serial_rb;
> > >       spinlock_t serial_lock;
> > > -     wait_queue_head_t serial_io_wait;
> > >   };
> > >
> > >   /*
> > > @@ -382,6 +381,9 @@ struct serial_info {
> > >       sector_t start;         /* start sector of rb node */
> > >       sector_t last;          /* end sector of rb node */
> > >       sector_t _subtree_last; /* highest sector in subtree of rb node */
> > > +     struct list_head        list_node;
> > > +     struct list_head        waiters;
> > > +     struct completion       ready;
> > >   };
> > >
> > >   /*
> > > diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
> > > index a41b1ec3d695..38c73538d038 100644
> > > --- a/drivers/md/raid1.c
> > > +++ b/drivers/md/raid1.c
> > > @@ -57,19 +57,25 @@ INTERVAL_TREE_DEFINE(struct serial_info, node, sector_t, _subtree_last,
> > >                    START, LAST, static inline, raid1_rb);
> > >
> > >   static int check_and_add_serial(struct md_rdev *rdev, struct r1bio *r1_bio,
> > > -                             struct serial_info *si, int idx)
> > > +                             struct serial_info *si)
> > >   {
> > >       unsigned long flags;
> > >       int ret = 0;
> > >       sector_t lo = r1_bio->sector;
> > >       sector_t hi = lo + r1_bio->sectors - 1;
> > > +     int idx = sector_to_idx(r1_bio->sector);
> > >       struct serial_in_rdev *serial = &rdev->serial[idx];
> > > +     struct serial_info *head_si;
> > >
> > >       spin_lock_irqsave(&serial->serial_lock, flags);
> > >       /* collision happened */
> > > -     if (raid1_rb_iter_first(&serial->serial_rb, lo, hi))
> > > +     head_si = raid1_rb_iter_first(&serial->serial_rb, lo, hi);
> > > +     if (head_si && head_si != si) {
> > > +             si->start = lo;
> > > +             si->last = hi;
> > > +             list_add_tail(&si->list_node, &head_si->waiters);
> > >               ret = -EBUSY;
> > > -     else {
> > > +     } else if (!head_si) {
> > >               si->start = lo;
> > >               si->last = hi;
> > >               raid1_rb_insert(si, &serial->serial_rb);
> > > @@ -83,19 +89,23 @@ static void wait_for_serialization(struct md_rdev *rdev, struct r1bio *r1_bio)
> > >   {
> > >       struct mddev *mddev = rdev->mddev;
> > >       struct serial_info *si;
> > > -     int idx = sector_to_idx(r1_bio->sector);
> > > -     struct serial_in_rdev *serial = &rdev->serial[idx];
> > >
> > >       if (WARN_ON(!mddev->serial_info_pool))
> > >               return;
> > >       si = mempool_alloc(mddev->serial_info_pool, GFP_NOIO);
> > > -     wait_event(serial->serial_io_wait,
> > > -                check_and_add_serial(rdev, r1_bio, si, idx) == 0);
> > > +     INIT_LIST_HEAD(&si->waiters);
> > > +     INIT_LIST_HEAD(&si->list_node);
> > > +     init_completion(&si->ready);
> > > +     while (check_and_add_serial(rdev, r1_bio, si)) {
> > > +             wait_for_completion(&si->ready);
> > > +             reinit_completion(&si->ready);
> > > +     }
> > >   }
> > >
> > >   static void remove_serial(struct md_rdev *rdev, sector_t lo, sector_t hi)
> > >   {
> > >       struct serial_info *si;
> > > +     struct serial_info *first_si;
> > >       unsigned long flags;
> > >       int found = 0;
> > >       struct mddev *mddev = rdev->mddev;
> > > @@ -106,16 +116,25 @@ static void remove_serial(struct md_rdev *rdev, sector_t lo, sector_t hi)
> > >       for (si = raid1_rb_iter_first(&serial->serial_rb, lo, hi);
> > >            si; si = raid1_rb_iter_next(si, lo, hi)) {
> > >               if (si->start == lo && si->last == hi) {
> > > -                     raid1_rb_remove(si, &serial->serial_rb);
> > > -                     mempool_free(si, mddev->serial_info_pool);
> > >                       found = 1;
> > >                       break;
> > >               }
> > >       }
> > >       if (!found)
> > >               WARN(1, "The write IO is not recorded for serialization\n");
> > > +     else {
> > > +             raid1_rb_remove(si, &serial->serial_rb);
> > > +             if (!list_empty(&si->waiters)) {
> > > +                     first_si = list_first_entry(&si->waiters,
> > > +                                             struct serial_info, list_node);
> > > +                     list_del_init(&first_si->list_node);
> > > +                     list_splice_init(&si->waiters, &first_si->waiters);
> > > +                     raid1_rb_insert(first_si, &serial->serial_rb);
> > > +                     complete(&first_si->ready);
> > > +             }
> > > +             mempool_free(si, mddev->serial_info_pool);
> > > +     }
> > >       spin_unlock_irqrestore(&serial->serial_lock, flags);
> > > -     wake_up(&serial->serial_io_wait);
> > >   }
> > >
> > >   /*
> >
> > --
> > Thansk,
> > Kuai
> >


^ permalink raw reply

* Re: [PATCH 2/2] md/raid1: serialize overlapped io for writemostly disk
From: Yu Kuai @ 2026-02-11  3:15 UTC (permalink / raw)
  To: Xiao Ni; +Cc: song, linux-raid, linan666, yukuai
In-Reply-To: <CALTww29NpmNbEb79KbVCX-rAF4o39rEd3-P1TjiDTc1OWpQiAw@mail.gmail.com>

Hi,

在 2026/2/11 10:44, Xiao Ni 写道:
> On Wed, Feb 11, 2026 at 10:32 AM Yu Kuai <yukuai@fnnas.com> wrote:
>> Hi,
>>
>> 在 2026/2/6 13:38, Xiao Ni 写道:
>>> In behind mode, overlap bios for writemostly device are queued. They
>>> In behind mode, bios which overlap the bio from the interval tree
>>> need to wait in waitqueue. Those bios will be handled once the in-tree
>>> bio finishes. They are waken up and try to get the tree lock. The bio
>>> which gets the lock will be handled. So the sequence can't be guaranteed.
>>>
>>> For example
>>> bio1(100,200)
>>> bio2(150,200)
>>> bio3(150,300)
>>> The write sequence of fast device is bio1,bio2,bio3. But the write
>>> sequence of slow device is bio1,bio3,bio2. This causes data corruption.
>>> Replace waitqueue with a fifo list to guarantee the write sequence.
>> To be honest, I think this is fine with the respect user won't send overlap
>> bios. In this case, bio1 is done in fast disk and pending in slow disk, then
>> bio2 will stuck while issuing and waiting for bio1 to return from slow disk.
>> So send bio3 in this case is not expected, and if you do you can't guarantee
>> the complete order from fast disk as bio2, bio3 as well.
> Hi Kuai
>
> In write behind mode, raid1 returns bio1 to the upper layer once the
> write on the fast disk finishes. The upper layer (app/filesystem)
> thinks that bio1 has finished, while the write of bio1 to the slow
> disk is still in progress. So bio2 will not be stuck while issuing, am
> I right? If so, bio2 can be submitted to raid1 once the write of bio1
> to the fast disk finishes. And bio3 can be submitted to raid1 once the
> write of bio2 to fast disk finishes.

I just take a look at raid1 code, turns out raid1_write_request() just allocate
bio and issue it directly in each rdev iteration, I misunderstood.

This solution looks fine to me, however, I'll suggest to keep the wait_queue and
use prepare_to_wait_exclusive() to guarantee the fifo order.

>
> Best Regards
> Xiao
>>> Signed-off-by: Xiao Ni <xni@redhat.com>
>>> ---
>>>    drivers/md/md.c    |  1 -
>>>    drivers/md/md.h    |  4 +++-
>>>    drivers/md/raid1.c | 39 +++++++++++++++++++++++++++++----------
>>>    3 files changed, 32 insertions(+), 12 deletions(-)
>>>
>>> diff --git a/drivers/md/md.c b/drivers/md/md.c
>>> index 59cd303548de..ab91d17c2d68 100644
>>> --- a/drivers/md/md.c
>>> +++ b/drivers/md/md.c
>>> @@ -188,7 +188,6 @@ static int rdev_init_serial(struct md_rdev *rdev)
>>>
>>>                spin_lock_init(&serial_tmp->serial_lock);
>>>                serial_tmp->serial_rb = RB_ROOT_CACHED;
>>> -             init_waitqueue_head(&serial_tmp->serial_io_wait);
>>>        }
>>>
>>>        rdev->serial = serial;
>>> diff --git a/drivers/md/md.h b/drivers/md/md.h
>>> index ac84289664cd..64ffbb2efb87 100644
>>> --- a/drivers/md/md.h
>>> +++ b/drivers/md/md.h
>>> @@ -126,7 +126,6 @@ enum sync_action {
>>>    struct serial_in_rdev {
>>>        struct rb_root_cached serial_rb;
>>>        spinlock_t serial_lock;
>>> -     wait_queue_head_t serial_io_wait;
>>>    };
>>>
>>>    /*
>>> @@ -382,6 +381,9 @@ struct serial_info {
>>>        sector_t start;         /* start sector of rb node */
>>>        sector_t last;          /* end sector of rb node */
>>>        sector_t _subtree_last; /* highest sector in subtree of rb node */
>>> +     struct list_head        list_node;
>>> +     struct list_head        waiters;
>>> +     struct completion       ready;
>>>    };
>>>
>>>    /*
>>> diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
>>> index a41b1ec3d695..38c73538d038 100644
>>> --- a/drivers/md/raid1.c
>>> +++ b/drivers/md/raid1.c
>>> @@ -57,19 +57,25 @@ INTERVAL_TREE_DEFINE(struct serial_info, node, sector_t, _subtree_last,
>>>                     START, LAST, static inline, raid1_rb);
>>>
>>>    static int check_and_add_serial(struct md_rdev *rdev, struct r1bio *r1_bio,
>>> -                             struct serial_info *si, int idx)
>>> +                             struct serial_info *si)
>>>    {
>>>        unsigned long flags;
>>>        int ret = 0;
>>>        sector_t lo = r1_bio->sector;
>>>        sector_t hi = lo + r1_bio->sectors - 1;
>>> +     int idx = sector_to_idx(r1_bio->sector);
>>>        struct serial_in_rdev *serial = &rdev->serial[idx];
>>> +     struct serial_info *head_si;
>>>
>>>        spin_lock_irqsave(&serial->serial_lock, flags);
>>>        /* collision happened */
>>> -     if (raid1_rb_iter_first(&serial->serial_rb, lo, hi))
>>> +     head_si = raid1_rb_iter_first(&serial->serial_rb, lo, hi);
>>> +     if (head_si && head_si != si) {
>>> +             si->start = lo;
>>> +             si->last = hi;
>>> +             list_add_tail(&si->list_node, &head_si->waiters);
>>>                ret = -EBUSY;
>>> -     else {
>>> +     } else if (!head_si) {
>>>                si->start = lo;
>>>                si->last = hi;
>>>                raid1_rb_insert(si, &serial->serial_rb);
>>> @@ -83,19 +89,23 @@ static void wait_for_serialization(struct md_rdev *rdev, struct r1bio *r1_bio)
>>>    {
>>>        struct mddev *mddev = rdev->mddev;
>>>        struct serial_info *si;
>>> -     int idx = sector_to_idx(r1_bio->sector);
>>> -     struct serial_in_rdev *serial = &rdev->serial[idx];
>>>
>>>        if (WARN_ON(!mddev->serial_info_pool))
>>>                return;
>>>        si = mempool_alloc(mddev->serial_info_pool, GFP_NOIO);
>>> -     wait_event(serial->serial_io_wait,
>>> -                check_and_add_serial(rdev, r1_bio, si, idx) == 0);
>>> +     INIT_LIST_HEAD(&si->waiters);
>>> +     INIT_LIST_HEAD(&si->list_node);
>>> +     init_completion(&si->ready);
>>> +     while (check_and_add_serial(rdev, r1_bio, si)) {
>>> +             wait_for_completion(&si->ready);
>>> +             reinit_completion(&si->ready);
>>> +     }
>>>    }
>>>
>>>    static void remove_serial(struct md_rdev *rdev, sector_t lo, sector_t hi)
>>>    {
>>>        struct serial_info *si;
>>> +     struct serial_info *first_si;
>>>        unsigned long flags;
>>>        int found = 0;
>>>        struct mddev *mddev = rdev->mddev;
>>> @@ -106,16 +116,25 @@ static void remove_serial(struct md_rdev *rdev, sector_t lo, sector_t hi)
>>>        for (si = raid1_rb_iter_first(&serial->serial_rb, lo, hi);
>>>             si; si = raid1_rb_iter_next(si, lo, hi)) {
>>>                if (si->start == lo && si->last == hi) {
>>> -                     raid1_rb_remove(si, &serial->serial_rb);
>>> -                     mempool_free(si, mddev->serial_info_pool);
>>>                        found = 1;
>>>                        break;
>>>                }
>>>        }
>>>        if (!found)
>>>                WARN(1, "The write IO is not recorded for serialization\n");
>>> +     else {
>>> +             raid1_rb_remove(si, &serial->serial_rb);
>>> +             if (!list_empty(&si->waiters)) {
>>> +                     first_si = list_first_entry(&si->waiters,
>>> +                                             struct serial_info, list_node);
>>> +                     list_del_init(&first_si->list_node);
>>> +                     list_splice_init(&si->waiters, &first_si->waiters);
>>> +                     raid1_rb_insert(first_si, &serial->serial_rb);
>>> +                     complete(&first_si->ready);
>>> +             }
>>> +             mempool_free(si, mddev->serial_info_pool);
>>> +     }
>>>        spin_unlock_irqrestore(&serial->serial_lock, flags);
>>> -     wake_up(&serial->serial_io_wait);
>>>    }
>>>
>>>    /*
>> --
>> Thansk,
>> Kuai
>>
>
-- 
Thansk,
Kuai

^ permalink raw reply

* Re: [PATCH 2/2] md/raid1: serialize overlapped io for writemostly disk
From: Xiao Ni @ 2026-02-11  3:26 UTC (permalink / raw)
  To: yukuai; +Cc: song, linux-raid, linan666
In-Reply-To: <a753611f-91ee-4cee-97c8-706154d1cade@fnnas.com>

On Wed, Feb 11, 2026 at 11:15 AM Yu Kuai <yukuai@fnnas.com> wrote:
>
> Hi,
>
> 在 2026/2/11 10:44, Xiao Ni 写道:
> > On Wed, Feb 11, 2026 at 10:32 AM Yu Kuai <yukuai@fnnas.com> wrote:
> >> Hi,
> >>
> >> 在 2026/2/6 13:38, Xiao Ni 写道:
> >>> In behind mode, overlap bios for writemostly device are queued. They
> >>> In behind mode, bios which overlap the bio from the interval tree
> >>> need to wait in waitqueue. Those bios will be handled once the in-tree
> >>> bio finishes. They are waken up and try to get the tree lock. The bio
> >>> which gets the lock will be handled. So the sequence can't be guaranteed.
> >>>
> >>> For example
> >>> bio1(100,200)
> >>> bio2(150,200)
> >>> bio3(150,300)
> >>> The write sequence of fast device is bio1,bio2,bio3. But the write
> >>> sequence of slow device is bio1,bio3,bio2. This causes data corruption.
> >>> Replace waitqueue with a fifo list to guarantee the write sequence.
> >> To be honest, I think this is fine with the respect user won't send overlap
> >> bios. In this case, bio1 is done in fast disk and pending in slow disk, then
> >> bio2 will stuck while issuing and waiting for bio1 to return from slow disk.
> >> So send bio3 in this case is not expected, and if you do you can't guarantee
> >> the complete order from fast disk as bio2, bio3 as well.
> > Hi Kuai
> >
> > In write behind mode, raid1 returns bio1 to the upper layer once the
> > write on the fast disk finishes. The upper layer (app/filesystem)
> > thinks that bio1 has finished, while the write of bio1 to the slow
> > disk is still in progress. So bio2 will not be stuck while issuing, am
> > I right? If so, bio2 can be submitted to raid1 once the write of bio1
> > to the fast disk finishes. And bio3 can be submitted to raid1 once the
> > write of bio2 to fast disk finishes.
>
> I just take a look at raid1 code, turns out raid1_write_request() just allocate
> bio and issue it directly in each rdev iteration, I misunderstood.
>
> This solution looks fine to me, however, I'll suggest to keep the wait_queue and
> use prepare_to_wait_exclusive() to guarantee the fifo order.

Thanks for the suggestion. I'll use prepare_to_wait_exclusive and send v2.

Best Regards
Xiao
>
> >
> > Best Regards
> > Xiao
> >>> Signed-off-by: Xiao Ni <xni@redhat.com>
> >>> ---
> >>>    drivers/md/md.c    |  1 -
> >>>    drivers/md/md.h    |  4 +++-
> >>>    drivers/md/raid1.c | 39 +++++++++++++++++++++++++++++----------
> >>>    3 files changed, 32 insertions(+), 12 deletions(-)
> >>>
> >>> diff --git a/drivers/md/md.c b/drivers/md/md.c
> >>> index 59cd303548de..ab91d17c2d68 100644
> >>> --- a/drivers/md/md.c
> >>> +++ b/drivers/md/md.c
> >>> @@ -188,7 +188,6 @@ static int rdev_init_serial(struct md_rdev *rdev)
> >>>
> >>>                spin_lock_init(&serial_tmp->serial_lock);
> >>>                serial_tmp->serial_rb = RB_ROOT_CACHED;
> >>> -             init_waitqueue_head(&serial_tmp->serial_io_wait);
> >>>        }
> >>>
> >>>        rdev->serial = serial;
> >>> diff --git a/drivers/md/md.h b/drivers/md/md.h
> >>> index ac84289664cd..64ffbb2efb87 100644
> >>> --- a/drivers/md/md.h
> >>> +++ b/drivers/md/md.h
> >>> @@ -126,7 +126,6 @@ enum sync_action {
> >>>    struct serial_in_rdev {
> >>>        struct rb_root_cached serial_rb;
> >>>        spinlock_t serial_lock;
> >>> -     wait_queue_head_t serial_io_wait;
> >>>    };
> >>>
> >>>    /*
> >>> @@ -382,6 +381,9 @@ struct serial_info {
> >>>        sector_t start;         /* start sector of rb node */
> >>>        sector_t last;          /* end sector of rb node */
> >>>        sector_t _subtree_last; /* highest sector in subtree of rb node */
> >>> +     struct list_head        list_node;
> >>> +     struct list_head        waiters;
> >>> +     struct completion       ready;
> >>>    };
> >>>
> >>>    /*
> >>> diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
> >>> index a41b1ec3d695..38c73538d038 100644
> >>> --- a/drivers/md/raid1.c
> >>> +++ b/drivers/md/raid1.c
> >>> @@ -57,19 +57,25 @@ INTERVAL_TREE_DEFINE(struct serial_info, node, sector_t, _subtree_last,
> >>>                     START, LAST, static inline, raid1_rb);
> >>>
> >>>    static int check_and_add_serial(struct md_rdev *rdev, struct r1bio *r1_bio,
> >>> -                             struct serial_info *si, int idx)
> >>> +                             struct serial_info *si)
> >>>    {
> >>>        unsigned long flags;
> >>>        int ret = 0;
> >>>        sector_t lo = r1_bio->sector;
> >>>        sector_t hi = lo + r1_bio->sectors - 1;
> >>> +     int idx = sector_to_idx(r1_bio->sector);
> >>>        struct serial_in_rdev *serial = &rdev->serial[idx];
> >>> +     struct serial_info *head_si;
> >>>
> >>>        spin_lock_irqsave(&serial->serial_lock, flags);
> >>>        /* collision happened */
> >>> -     if (raid1_rb_iter_first(&serial->serial_rb, lo, hi))
> >>> +     head_si = raid1_rb_iter_first(&serial->serial_rb, lo, hi);
> >>> +     if (head_si && head_si != si) {
> >>> +             si->start = lo;
> >>> +             si->last = hi;
> >>> +             list_add_tail(&si->list_node, &head_si->waiters);
> >>>                ret = -EBUSY;
> >>> -     else {
> >>> +     } else if (!head_si) {
> >>>                si->start = lo;
> >>>                si->last = hi;
> >>>                raid1_rb_insert(si, &serial->serial_rb);
> >>> @@ -83,19 +89,23 @@ static void wait_for_serialization(struct md_rdev *rdev, struct r1bio *r1_bio)
> >>>    {
> >>>        struct mddev *mddev = rdev->mddev;
> >>>        struct serial_info *si;
> >>> -     int idx = sector_to_idx(r1_bio->sector);
> >>> -     struct serial_in_rdev *serial = &rdev->serial[idx];
> >>>
> >>>        if (WARN_ON(!mddev->serial_info_pool))
> >>>                return;
> >>>        si = mempool_alloc(mddev->serial_info_pool, GFP_NOIO);
> >>> -     wait_event(serial->serial_io_wait,
> >>> -                check_and_add_serial(rdev, r1_bio, si, idx) == 0);
> >>> +     INIT_LIST_HEAD(&si->waiters);
> >>> +     INIT_LIST_HEAD(&si->list_node);
> >>> +     init_completion(&si->ready);
> >>> +     while (check_and_add_serial(rdev, r1_bio, si)) {
> >>> +             wait_for_completion(&si->ready);
> >>> +             reinit_completion(&si->ready);
> >>> +     }
> >>>    }
> >>>
> >>>    static void remove_serial(struct md_rdev *rdev, sector_t lo, sector_t hi)
> >>>    {
> >>>        struct serial_info *si;
> >>> +     struct serial_info *first_si;
> >>>        unsigned long flags;
> >>>        int found = 0;
> >>>        struct mddev *mddev = rdev->mddev;
> >>> @@ -106,16 +116,25 @@ static void remove_serial(struct md_rdev *rdev, sector_t lo, sector_t hi)
> >>>        for (si = raid1_rb_iter_first(&serial->serial_rb, lo, hi);
> >>>             si; si = raid1_rb_iter_next(si, lo, hi)) {
> >>>                if (si->start == lo && si->last == hi) {
> >>> -                     raid1_rb_remove(si, &serial->serial_rb);
> >>> -                     mempool_free(si, mddev->serial_info_pool);
> >>>                        found = 1;
> >>>                        break;
> >>>                }
> >>>        }
> >>>        if (!found)
> >>>                WARN(1, "The write IO is not recorded for serialization\n");
> >>> +     else {
> >>> +             raid1_rb_remove(si, &serial->serial_rb);
> >>> +             if (!list_empty(&si->waiters)) {
> >>> +                     first_si = list_first_entry(&si->waiters,
> >>> +                                             struct serial_info, list_node);
> >>> +                     list_del_init(&first_si->list_node);
> >>> +                     list_splice_init(&si->waiters, &first_si->waiters);
> >>> +                     raid1_rb_insert(first_si, &serial->serial_rb);
> >>> +                     complete(&first_si->ready);
> >>> +             }
> >>> +             mempool_free(si, mddev->serial_info_pool);
> >>> +     }
> >>>        spin_unlock_irqrestore(&serial->serial_lock, flags);
> >>> -     wake_up(&serial->serial_io_wait);
> >>>    }
> >>>
> >>>    /*
> >> --
> >> Thansk,
> >> Kuai
> >>
> >
> --
> Thansk,
> Kuai
>


^ permalink raw reply

* [PATCH AUTOSEL 6.19-6.18] md raid: fix hang when stopping arrays with metadata through dm-raid
From: Sasha Levin @ 2026-02-11 12:30 UTC (permalink / raw)
  To: patches, stable; +Cc: Heinz Mauelshagen, Yu Kuai, Sasha Levin, song, linux-raid
In-Reply-To: <20260211123112.1330287-1-sashal@kernel.org>

From: Heinz Mauelshagen <heinzm@redhat.com>

[ Upstream commit cefcb9297fbdb6d94b61787b4f8d84f55b741470 ]

When using device-mapper's dm-raid target, stopping a RAID array can cause
the system to hang under specific conditions.

This occurs when:

- A dm-raid managed device tree is suspended from top to bottom
   (the top-level RAID device is suspended first, followed by its
    underlying metadata and data devices)

- The top-level RAID device is then removed

Removing the top-level device triggers a hang in the following sequence:
the dm-raid destructor calls md_stop(), which tries to flush the
write-intent bitmap by writing to the metadata sub-devices. However, these
devices are already suspended, making them unable to complete the write-intent
operations and causing an indefinite block.

Fix:

- Prevent bitmap flushing when md_stop() is called from dm-raid
destructor context
  and avoid a quiescing/unquescing cycle which could also cause I/O

- Still allow write-intent bitmap flushing when called from dm-raid
suspend context

This ensures that RAID array teardown can complete successfully even when the
underlying devices are in a suspended state.

This second patch uses md_is_rdwr() to distinguish between suspend and
destructor paths as elaborated on above.

Link: https://lore.kernel.org/linux-raid/CAM23VxqYrwkhKEBeQrZeZwQudbiNey2_8B_SEOLqug=pXxaFrA@mail.gmail.com
Signed-off-by: Heinz Mauelshagen <heinzm@redhat.com>
Signed-off-by: Yu Kuai <yukuai@fnnas.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---

LLM Generated explanations, may be completely bogus:

## Complete Analysis

### 1. What the Bug Is

This commit fixes a **system hang** (indefinite block/deadlock) when
stopping dm-raid managed RAID arrays. The hang scenario is:

1. A dm-raid device tree is suspended top-to-bottom (top-level RAID
   device first, then underlying metadata/data devices)
2. The top-level RAID device is removed (`raid_dtr` destructor)
3. `raid_dtr()` → `md_stop()` → `__md_stop_writes()` tries to:
   - Quiesce the RAID personality (`pers->quiesce()`)
   - Flush the write-intent bitmap to metadata devices
     (`bitmap_ops->flush()`)
4. But the metadata devices are already suspended and **cannot complete
   I/O**
5. The flush waits indefinitely → **system hang**

This is a real, user-reported bug (Link in commit message points to a
lore report).

### 2. The Fix

The fix adds a conditional guard around the quiesce and bitmap flush in
`__md_stop_writes()`:

```c
if (md_is_rdwr(mddev) || !mddev_is_dm(mddev)) {
    // quiesce + bitmap flush
}
```

This condition skips the quiesce and bitmap flush **only** when:
- The device is a dm-raid device (`mddev_is_dm()` returns true), AND
- The device is NOT in read-write mode (`md_is_rdwr()` returns false)

The clever trick: `raid_postsuspend()` (suspend path) already calls
`md_stop_writes()` while the device is still `MD_RDWR`, so the bitmap
flush proceeds normally during suspend. Then it sets `rs->md.ro =
MD_RDONLY`. Later when `raid_dtr()` calls `md_stop()` →
`__md_stop_writes()`, the device is `MD_RDONLY`, so the condition is
false and the dangerous I/O is skipped.

For non-dm md arrays (`!mddev_is_dm()` is true), the condition is always
true and behavior is unchanged.

### 3. Code Change Scope

- **1 file changed**: `drivers/md/md.c`
- **8 insertions, 6 deletions** (net +2 lines)
- Only touches the `__md_stop_writes()` function
- Small and surgical

### 4. Critical Dependency Issue

The commit message explicitly says **"This second patch"**, indicating
it's part of a 2-patch series. The first patch is `55dcfdf8af9c3` ("dm
raid: use proper md_ro_state enumerators"), which:
- Added `rs->md.ro = MD_RDONLY;` to `raid_postsuspend()` in `dm-raid.c`
- Without this line, when `raid_dtr` runs, `mddev->ro` is still
  `MD_RDWR` (from `raid_resume`), so `md_is_rdwr()` returns true, and
  the quiesce/flush is NOT skipped → the hang still occurs!

**This means the fix (`cefcb9297fbdb`) CANNOT work without the
prerequisite (`55dcfdf8af9c3`).**

The prerequisite `55dcfdf8af9c3` was merged for v6.18. It is:
- Present in 6.18.y
- **NOT** present in 6.12.y, 6.6.y, 6.1.y, or 5.15.y

### 5. Additional Backport Complications

For older stable trees (6.12.y and earlier), the code context has
changed significantly:
- v6.12+: `bitmap_ops->flush()` (ops-based interface)
- v6.11 and earlier: `md_bitmap_flush()` (direct function call)
- The `md_bitmap_enabled()` guard was added between v6.12 and current
- The `serialize_policy` field changed to a flag bit
- `md_stop_writes()` in v6.6 includes `set_bit(MD_RECOVERY_FROZEN)` and
  sync thread reaping, which was later moved to the callers

### 6. Is the Bug Present in Older Stable Trees?

**Yes**, the underlying bug (bitmap flush blocking on suspended metadata
devices) exists in all kernel versions with dm-raid. In v6.6/v6.1,
`__md_stop_writes()` also calls `md_bitmap_flush()` and
`pers->quiesce()` unconditionally, and the same hang can occur when
`raid_dtr` runs after `raid_postsuspend`.

However, to fix it in older trees, you would need BOTH:
1. The prerequisite that sets `ro = MD_RDONLY` in `raid_postsuspend`
   (equivalent of `55dcfdf8af9c3`)
2. This fix that checks `md_is_rdwr()` in `__md_stop_writes` (this
   commit)

And both would need adaptation to the different code context.

### 7. Risk Assessment

- **Severity of bug**: HIGH — system hang is a serious issue, especially
  for enterprise users of dm-raid (LVM RAID, etc.)
- **Risk of regression**: LOW-MEDIUM — the fix only changes behavior for
  dm-raid devices in the destructor path (when `!md_is_rdwr &&
  mddev_is_dm`); all other paths are unchanged
- **Size**: Very small (8 insertions, 6 deletions)
- **Dependency complexity**: HIGH — requires prerequisite commit and
  significant code context differences across versions

### 8. Verdict

This commit fixes a **real, user-reported system hang** in dm-raid
teardown, which is clearly important for stable. The fix itself is
small, surgical, and well-understood. However, it has a **hard
dependency** on `55dcfdf8af9c3` ("dm raid: use proper md_ro_state
enumerators") which adds the critical `rs->md.ro = MD_RDONLY` line in
`raid_postsuspend()`. Without that prerequisite, the `md_is_rdwr()`
check in this fix would never distinguish between suspend and destructor
paths, and the fix would be completely ineffective.

For 6.18.y, the prerequisite is present and this patch could apply
(possibly with minor context adjustments). For older LTS trees (6.12.y,
6.6.y, 6.1.y), both this commit and the prerequisite (with appropriate
context adaptations) would need to be backported together.

Given that:
1. It fixes a definite system hang (high severity)
2. It's small and well-contained
3. It has a clear dependency that must also be backported
4. dm-raid (LVM RAID) is widely used in enterprise environments
5. The author is from Red Hat (enterprise focus) and it was reported by
   a real user

This is a **YES** for backporting, with the strong caveat that the
prerequisite commit `55dcfdf8af9c3` must be included in any backport.

**YES**

 drivers/md/md.c | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/drivers/md/md.c b/drivers/md/md.c
index 6d73f6e196a9f..ac71640ff3a81 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -6848,13 +6848,15 @@ static void __md_stop_writes(struct mddev *mddev)
 {
 	timer_delete_sync(&mddev->safemode_timer);
 
-	if (mddev->pers && mddev->pers->quiesce) {
-		mddev->pers->quiesce(mddev, 1);
-		mddev->pers->quiesce(mddev, 0);
-	}
+	if (md_is_rdwr(mddev) || !mddev_is_dm(mddev)) {
+		if (mddev->pers && mddev->pers->quiesce) {
+			mddev->pers->quiesce(mddev, 1);
+			mddev->pers->quiesce(mddev, 0);
+		}
 
-	if (md_bitmap_enabled(mddev, true))
-		mddev->bitmap_ops->flush(mddev);
+		if (md_bitmap_enabled(mddev, true))
+			mddev->bitmap_ops->flush(mddev);
+	}
 
 	if (md_is_rdwr(mddev) &&
 	    ((!mddev->in_sync && !mddev_is_clustered(mddev)) ||
-- 
2.51.0


^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox