Linux RAID subsystem development
 help / color / mirror / Atom feed
* [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

* [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 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 0/3] lib/raid6: Optimize raid6_select_algo while ensuring
From: sunliming @ 2026-02-06  5:43 UTC (permalink / raw)
  To: song, yukuai; +Cc: linux-raid, linux-kernel, linan666, sunliming

From: sunliming <sunliming@kylinos.cn>

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

---
Changes in v2:
  Select the highest-priority algorithm instead of the first one. 
  Add the cancel_work_sync function in the exit function to handle the 
work queue cleanup.
instead of the first one. 
--- 
sunliming (3):
  lib/raid6: Divide the raid6 algorithm selection process into two parts
  lib/raid6: Optimizing the raid6_select_algo time through asynchronous
    processing
  lib/raid6: Delete the RAID6_PQ_BENCHMARK config

 include/linux/raid/pq.h |  3 --
 lib/Kconfig             |  8 ----
 lib/raid6/algos.c       | 97 +++++++++++++++++++++++++++++------------
 3 files changed, 68 insertions(+), 40 deletions(-)

-- 
2.25.1


^ permalink raw reply

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

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.

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);
 }
 
 /*
-- 
2.50.1 (Apple Git-155)


^ permalink raw reply related

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

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(-)

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);
 
-- 
2.50.1 (Apple Git-155)


^ permalink raw reply related

* [PATCH 0/2] md/raid1: serialize overlapped ios for writemostly device
From: Xiao Ni @ 2026-02-06  5:38 UTC (permalink / raw)
  To: yukuai; +Cc: song, linux-raid, linan666

Xiao Ni (2):
  md/raid1: fix the comparing region of interval tree
  md/raid1: serialize overlap io for writemostly disk

 drivers/md/md.c    |  1 -
 drivers/md/md.h    |  4 +++-
 drivers/md/raid1.c | 43 +++++++++++++++++++++++++++++++------------
 3 files changed, 34 insertions(+), 14 deletions(-)

-- 
2.50.1 (Apple Git-155)


^ permalink raw reply

* Re: [PATCH v2 00/14] folio support for sync I/O in RAID
From: Li Nan @ 2026-02-05 12:57 UTC (permalink / raw)
  To: yukuai, linan666, song; +Cc: xni, linux-raid, linux-kernel, yangerkun, yi.zhang
In-Reply-To: <def3b536-7840-426a-9805-f62363174812@fnnas.com>



在 2026/2/5 1:02, Yu Kuai 写道:
> Hi,
> 
> 在 2026/1/28 15:56, linan666@huaweicloud.com 写道:
>> From: Li Nan <linan122@huawei.com>
>>
>> This patchset adds folio support to sync operations in raid1/10.
>> Previously, we used 16 * 4K pages for 64K sync I/O. With this change,
>> we'll use a single 64K folio instead. Using folios reduces
>> resync/recovery time by 20% on HDD.
>>
>> This is the first step towards full folio support in RAID. Going forward,
>> I will replace the remaining page-based usage with folio.
>>
>> The patchset was tested with mdadm. Additional fault injection stress tests
>> were run under file systems.
> 
> For conclusion, I think you can send patch 1,8,9 first, they're not quite

Hi, kuai

Thanks for your review.

In handle_reshape_read_error(), if fix error in lbs before folio, we need
to consider 'idx' of page and offset in page, which make logical become 
complex. I prefer making the change after folio , how do you think?

> related to folio and can be applied. Then, please move patch 12,13 before patch 5

Patches 12,13 only make sense after folio support. Cleanup before folio
offers little benefit. I will merge them directly into patch 5.

> in the separate patch set, and looks like the number of patches can be small.
> 
> BTW, there are still pages in other context, do you plan to convert them?
> 

Yes, there are still many page usages in bitmap and RAID5. I will continue
folio support work with this series as a reference after it is applied.

-- 
Thanks,
Nan


^ permalink raw reply

* Re: [PATCH v2 03/14] md/raid1: use folio for tmppage
From: Yu Kuai @ 2026-02-05  7:33 UTC (permalink / raw)
  To: Li Nan, song; +Cc: xni, linux-raid, linux-kernel, yangerkun, yi.zhang, yukuai
In-Reply-To: <4b57e9b4-e660-a7df-4655-93c2815ec758@huaweicloud.com>

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.

-- 
Thansk,
Kuai

^ permalink raw reply

* Re: [PATCH v2 14/14] md/raid1,raid10: fall back to smaller order if sync folio alloc fails
From: Li Nan @ 2026-02-05  7:29 UTC (permalink / raw)
  To: yukuai, linan666, song; +Cc: xni, linux-raid, linux-kernel, yangerkun, yi.zhang
In-Reply-To: <887aa7a7-0b74-4b92-a053-0f019bac7d85@fnnas.com>



在 2026/2/5 0:48, Yu Kuai 写道:
> Hi,
> 
> 在 2026/1/28 15:57, linan666@huaweicloud.com 写道:
>> From: Li Nan <linan122@huawei.com>
>>
>> RESYNC_BLOCK_SIZE (64K) has higher allocation failure chance than 4k,
>> so retry with lower orders to improve allocation reliability.
>>
>> A r1/10_bio may have different rf->folio orders. Use minimum order as
>> r1/10_bio sectors to prevent exceeding size when adding folio to IO later.
>>
>> Signed-off-by: Li Nan <linan122@huawei.com>
>> ---
>>    drivers/md/raid1-10.c | 14 +++++++++++---
>>    drivers/md/raid1.c    | 13 +++++++++----
>>    drivers/md/raid10.c   | 28 ++++++++++++++++++++++++++--
>>    3 files changed, 46 insertions(+), 9 deletions(-)
> 
> Looks like this patch should be merged into patch 5, there is no need to introduce
> that higher allocation failure and then fix it here.
> 

OK, I will merge abouve patches.

-- 
Thanks,
Nan


^ permalink raw reply

* Re: [PATCH v2 06/14] md: Clean up folio sync support related code
From: Li Nan @ 2026-02-05  7:27 UTC (permalink / raw)
  To: yukuai, linan666, song; +Cc: xni, linux-raid, linux-kernel, yangerkun, yi.zhang
In-Reply-To: <0b8ba3a3-ce7e-4fd0-aacb-3ea62876acc8@fnnas.com>



在 2026/2/5 0:52, Yu Kuai 写道:
> Hi,
> 
> 在 2026/1/28 15:57, linan666@huaweicloud.com 写道:
>> From: Li Nan <linan122@huawei.com>
>>
>> 1. Remove resync_get_all_folio() and invoke folio_get() directly instead.
>> 2. Clean up redundant while(0) loop in md_bio_reset_resync_folio().
>> 3. Clean up bio variable by directly referencing r10_bio->devs[j].bio
>>      instead in r1buf_pool_alloc() and r10buf_pool_alloc().
>> 4. Clean up RESYNC_PAGES.
>>
>> Signed-off-by: Li Nan <linan122@huawei.com>
>> Reviewed-by: Xiao Ni <xni@redhat.com>
>> ---
>>    drivers/md/raid1-10.c | 22 ++++++----------------
>>    drivers/md/raid1.c    |  6 ++----
>>    drivers/md/raid10.c   |  6 ++----
>>    3 files changed, 10 insertions(+), 24 deletions(-)
> 
> I think this patch can be merged into patch 5.
> 

Thanks for your review.

Previously I modified them together, which made patch 5 difficult to review
as it is already large enough. I will merge them in the next version.

-- 
Thanks,
Nan


^ permalink raw reply

* Re: [PATCH v2 03/14] md/raid1: use folio for tmppage
From: Li Nan @ 2026-02-05  7:23 UTC (permalink / raw)
  To: yukuai, linan666, song; +Cc: xni, linux-raid, linux-kernel, yangerkun, yi.zhang
In-Reply-To: <b19b6eb8-b5c1-4ca2-a80e-f177370085bf@fnnas.com>



在 2026/2/5 0:45, Yu Kuai 写道:
> Hi,
> 
> 在 2026/1/28 15:56, linan666@huaweicloud.com 写道:
>> From: Li Nan <linan122@huawei.com>
>>
>> Convert tmppage to tmpfolio and use it throughout in raid1.
>>
>> Signed-off-by: Li Nan <linan122@huawei.com>
>> Reviewed-by: Xiao Ni <xni@redhat.com>
>> ---
>>    drivers/md/raid1.h |  2 +-
>>    drivers/md/raid1.c | 18 ++++++++++--------
>>    2 files changed, 11 insertions(+), 9 deletions(-)
>>
>> diff --git a/drivers/md/raid1.h b/drivers/md/raid1.h
>> index c98d43a7ae99..d480b3a8c2c4 100644
>> --- a/drivers/md/raid1.h
>> +++ b/drivers/md/raid1.h
>> @@ -101,7 +101,7 @@ struct r1conf {
>>    	/* temporary buffer to synchronous IO when attempting to repair
>>    	 * a read error.
>>    	 */
>> -	struct page		*tmppage;
>> +	struct folio		*tmpfolio;
>>    
>>    	/* When taking over an array from a different personality, we store
>>    	 * the new thread here until we fully activate the array.
>> diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
>> index 407925951299..43453f1a04f4 100644
>> --- a/drivers/md/raid1.c
>> +++ b/drivers/md/raid1.c
>> @@ -2417,8 +2417,8 @@ static void fix_read_error(struct r1conf *conf, struct r1bio *r1_bio)
>>    			      rdev->recovery_offset >= sect + s)) &&
>>    			    rdev_has_badblock(rdev, sect, s) == 0) {
>>    				atomic_inc(&rdev->nr_pending);
>> -				if (sync_page_io(rdev, sect, s<<9,
>> -					 conf->tmppage, REQ_OP_READ, false))
>> +				if (sync_folio_io(rdev, sect, s<<9, 0,
>> +					 conf->tmpfolio, REQ_OP_READ, false))
>>    					success = 1;
>>    				rdev_dec_pending(rdev, mddev);
>>    				if (success)
>> @@ -2447,7 +2447,8 @@ static void fix_read_error(struct r1conf *conf, struct r1bio *r1_bio)
>>    			    !test_bit(Faulty, &rdev->flags)) {
>>    				atomic_inc(&rdev->nr_pending);
>>    				r1_sync_page_io(rdev, sect, s,
>> -						conf->tmppage, REQ_OP_WRITE);
>> +						folio_page(conf->tmpfolio, 0),
>> +						REQ_OP_WRITE);
>>    				rdev_dec_pending(rdev, mddev);
>>    			}
>>    		}
>> @@ -2461,7 +2462,8 @@ static void fix_read_error(struct r1conf *conf, struct r1bio *r1_bio)
>>    			    !test_bit(Faulty, &rdev->flags)) {
>>    				atomic_inc(&rdev->nr_pending);
>>    				if (r1_sync_page_io(rdev, sect, s,
>> -						conf->tmppage, REQ_OP_READ)) {
>> +						folio_page(conf->tmpfolio, 0),
>> +						REQ_OP_READ)) {
>>    					atomic_add(s, &rdev->corrected_errors);
>>    					pr_info("md/raid1:%s: read error corrected (%d sectors at %llu on %pg)\n",
>>    						mdname(mddev), s,
>> @@ -3120,8 +3122,8 @@ static struct r1conf *setup_conf(struct mddev *mddev)
>>    	if (!conf->mirrors)
>>    		goto abort;
>>    
>> -	conf->tmppage = alloc_page(GFP_KERNEL);
>> -	if (!conf->tmppage)
>> +	conf->tmpfolio = folio_alloc(GFP_KERNEL, 0);
>> +	if (!conf->tmpfolio)
>>    		goto abort;
>>    
>>    	r1bio_size = offsetof(struct r1bio, bios[mddev->raid_disks * 2]);
>> @@ -3196,7 +3198,7 @@ static struct r1conf *setup_conf(struct mddev *mddev)
>>    	if (conf) {
>>    		mempool_destroy(conf->r1bio_pool);
>>    		kfree(conf->mirrors);
>> -		safe_put_page(conf->tmppage);
>> +		folio_put(conf->tmpfolio);
> 
> Is this safe? folio_put() can't be called with NULL.
> 

Yeah, should we introduce safe_put_folio()? Or just check NULL here.

>>    		kfree(conf->nr_pending);
>>    		kfree(conf->nr_waiting);
>>    		kfree(conf->nr_queued);
>> @@ -3310,7 +3312,7 @@ static void raid1_free(struct mddev *mddev, void *priv)
>>    
>>    	mempool_destroy(conf->r1bio_pool);
>>    	kfree(conf->mirrors);
>> -	safe_put_page(conf->tmppage);
>> +	folio_put(conf->tmpfolio);
> 
> Same here.
> 

In raid1_free(), setup_conf() is successful, and conf->tmpfolio must not be
NULL. It is safe.

>>    	kfree(conf->nr_pending);
>>    	kfree(conf->nr_waiting);
>>    	kfree(conf->nr_queued);
> 

-- 
Thanks,
Nan


^ permalink raw reply

* Re: [PATCH RFC 0/3] md/raid1: data corruption with serialization
From: Xiao Ni @ 2026-02-05  2:03 UTC (permalink / raw)
  To: yukuai; +Cc: linux-raid, Guoqing Jiang
In-Reply-To: <9e56a1ae-05b3-48de-bead-386352e136ee@fnnas.com>

On Thu, Feb 5, 2026 at 9:48 AM Yu Kuai <yukuai@fnnas.com> wrote:
>
> Hi,
>
> 在 2026/2/5 9:06, Xiao Ni 写道:
> > And let me change to this:
> > Setup: RAID1 with one fast disk and one slow disk. Two requests R1 and
> > R2, each split into LO and HI parts
> >
> > Step 1-10: Request R1 Processing
> >    - R1-LO: writes to fast disk → completes
> >    - R1-LO: writes to slow disk → starts (slow)
> >    - R1-HI: incorrectly waits for R1-LO (wrong sector range check)
> >    - R1-HI: R1-LO finishes and wakes up serial_io_wait. R1-HI finally
> > writes to fast disk → completes
>
> R1-hi should not wait for r1-lo, as they're not overlap. And as I said
> in the first reply, if here are still problem with WriteMostly case, please
> rebase and keep serialization for rdev.

Yes, it's a bug and patch03 fixes. I'll re-send patches based on
rdev-serialization.

>
> --
> Thansk,
> Kuai
>


^ permalink raw reply

* Re: [PATCH RFC 0/3] md/raid1: data corruption with serialization
From: Xiao Ni @ 2026-02-05  1:56 UTC (permalink / raw)
  To: yukuai; +Cc: linux-raid, Guoqing Jiang
In-Reply-To: <744a8650-ac7d-485e-bc65-cde38e2507a0@fnnas.com>

On Thu, Feb 5, 2026 at 9:44 AM Yu Kuai <yukuai@fnnas.com> wrote:
>
> 在 2026/2/5 8:34, Xiao Ni 写道:
> > I know the reason why it needs serialization for writemostly case. At
> > first, I asked the same question if we need to handle overlap if a
> > filesystem is on raid. I got the answer from AI, filesystem can submit
> > overlap bios simultaneously, maybe it's the reason why
> > serialize_policy was introduced? And database is another case which
> > can write to raid1 directly without a filesystem. So I started this
> > job. If we don't need to consider overlap, why do we need
> > serialize_policy? cc guoqing who is the author of serialize_policy
> > also.
>
> Please don't just trust answer from AI, filesystem can't submit overlap

Thanks for pointing out this.

> bios concurrently. And I already said serialize_policy is for writemostly
> case.

de31ee949739aba9ce7dbb8b10e72c6fce0e76c7 (md: reorgnize
mddev_create/destroy_serial_pool). serialize_policy is not for
writemostly.

>
> And I also said we don't need to consider user manage data on raw disk
> and write to same area concurrently, database will not do this.
>
> The reason is not complicated, if you write to the same LBA concurrent,
> it's pointless because you don't know what you'll read after that.

Thanks for the explanation.

>
> --
> Thansk,
> Kuai
>


^ permalink raw reply

* Re: [PATCH RFC 0/3] md/raid1: data corruption with serialization
From: Yu Kuai @ 2026-02-05  1:48 UTC (permalink / raw)
  To: Xiao Ni; +Cc: linux-raid, Guoqing Jiang, yukuai
In-Reply-To: <CALTww2_e_49Cy=WLhtZVWWtMK+=v8dErCSvxK9qpB0vmybsGyw@mail.gmail.com>

Hi,

在 2026/2/5 9:06, Xiao Ni 写道:
> And let me change to this:
> Setup: RAID1 with one fast disk and one slow disk. Two requests R1 and
> R2, each split into LO and HI parts
>
> Step 1-10: Request R1 Processing
>    - R1-LO: writes to fast disk → completes
>    - R1-LO: writes to slow disk → starts (slow)
>    - R1-HI: incorrectly waits for R1-LO (wrong sector range check)
>    - R1-HI: R1-LO finishes and wakes up serial_io_wait. R1-HI finally
> writes to fast disk → completes

R1-hi should not wait for r1-lo, as they're not overlap. And as I said
in the first reply, if here are still problem with WriteMostly case, please
rebase and keep serialization for rdev.

-- 
Thansk,
Kuai

^ permalink raw reply

* Re: [PATCH RFC 0/3] md/raid1: data corruption with serialization
From: Yu Kuai @ 2026-02-05  1:44 UTC (permalink / raw)
  To: Xiao Ni; +Cc: linux-raid, Guoqing Jiang, yukuai
In-Reply-To: <CALTww28vHeWNf7kejN_R_-K_nu4JJRig6ieQRBv63N1Xy7FXhw@mail.gmail.com>

在 2026/2/5 8:34, Xiao Ni 写道:
> I know the reason why it needs serialization for writemostly case. At
> first, I asked the same question if we need to handle overlap if a
> filesystem is on raid. I got the answer from AI, filesystem can submit
> overlap bios simultaneously, maybe it's the reason why
> serialize_policy was introduced? And database is another case which
> can write to raid1 directly without a filesystem. So I started this
> job. If we don't need to consider overlap, why do we need
> serialize_policy? cc guoqing who is the author of serialize_policy
> also.

Please don't just trust answer from AI, filesystem can't submit overlap
bios concurrently. And I already said serialize_policy is for writemostly
case.

And I also said we don't need to consider user manage data on raw disk
and write to same area concurrently, database will not do this.

The reason is not complicated, if you write to the same LBA concurrent,
it's pointless because you don't know what you'll read after that.

-- 
Thansk,
Kuai

^ permalink raw reply

* Re: [PATCH RFC 0/3] md/raid1: data corruption with serialization
From: Xiao Ni @ 2026-02-05  1:06 UTC (permalink / raw)
  To: yukuai; +Cc: linux-raid, Guoqing Jiang
In-Reply-To: <CALTww28vHeWNf7kejN_R_-K_nu4JJRig6ieQRBv63N1Xy7FXhw@mail.gmail.com>

On Thu, Feb 5, 2026 at 8:34 AM Xiao Ni <xni@redhat.com> wrote:
>
> On Wed, Feb 4, 2026 at 11:58 PM Yu Kuai <yukuai@fnnas.com> wrote:
> >
> > Hi,
> >
> > 在 2026/2/4 22:58, Xiao Ni 写道:
> > > A data corruption can happen when using serialization for raid1.
> > > Serialization is not enabled by default. But it looks like there
> > > is a data corruption risk if serialization is closed. Because the
> > > lower driver can't guarantee the sequence which io is written first.
> > > So it's possible that different member disks will have different
> > > data for nvme devices. This patch set doesn't open serialization
> > > by default.
> >
> > An important idea is that if there are filesystem on top of raid1/10/5, there
> > is no need to consider overlap bio. The only case for serialization is WriteMostly,
> > that bio can be returned to user while it's not done for slow disks. And that is
> > the reason why serialization is for rdev.
>
> I know the reason why it needs serialization for writemostly case. At
> first, I asked the same question if we need to handle overlap if a
> filesystem is on raid. I got the answer from AI, filesystem can submit
> overlap bios simultaneously, maybe it's the reason why
> serialize_policy was introduced? And database is another case which
> can write to raid1 directly without a filesystem. So I started this
> job. If we don't need to consider overlap, why do we need
> serialize_policy? cc guoqing who is the author of serialize_policy
> also.
>
> >
> > We definitely will not enable serialization by default, because there will
> > be huge performance degradation. And I don't think we'll handle the case user
>
> Yes, this is the reason why I don't enable it.
>
> > manage data with raw disk, and write to the same area concurrently. At last, if
> > there are still problem with WriteMostly case, please rebase and keep serialization
> > for rdev.
>
> The data corruption is:
>
> R1 issued (split into LO and HI)
> (LO)                                                              (HI)
> R1 write to high-speed disk begins              R1 write to high-speed
> disk waits (waits while LO is being written due to an incorrect sector
> range)
> R1 write to low-speed disk begins
> R1 write to high-speed disk completes
> High-speed disk wakes up                           R1 write to
> high-speed disk begins
>
> R1 write to low-speed disk waits (waits while LO is being written due
> to an incorrect sector range)
>
> R1 write to high-speed disk completes
> R1 completion notification
>
> R2 issued (split into LO and HI)
> R2 write to high-speed disk begins              R2 write to high-speed
> disk waits (waits while LO is being written due to an incorrect sector
> range)
> R2 write to low-speed disk waits (due to R1 being written)
> R2 write to high-speed disk completes
> High-speed disk wakes up                           R2 write to
> high-speed disk begins
>
> R2 write to low-speed disk Wait (R1 is still waiting)
>
> R2 write completed on high-speed disk
> R2 completion notification
>
> R1 write completed on slow disk
> Slow disk wake-up
> R2 write started on slow disk
> R2 write completed on slow disk
> Slow disk wake-up                                       R2 write
> started on slow disk *R2 will be written before R1 here.
>
> R2 write completed on slow disk
>
> Slow disk wake-up
>
> R1 write started on slow disk

Sorry the format looks a mess.

And let me change to this:
Setup: RAID1 with one fast disk and one slow disk. Two requests R1 and
R2, each split into LO and HI parts

Step 1-10: Request R1 Processing
  - R1-LO: writes to fast disk → completes
  - R1-LO: writes to slow disk → starts (slow)
  - R1-HI: incorrectly waits for R1-LO (wrong sector range check)
  - R1-HI: R1-LO finishes and wakes up serial_io_wait. R1-HI finally
writes to fast disk → completes
  - R1 completion notified (but R1-LO to slow disk is still handling,
R1-HI still pending on slow disk)

Step 11-19: Request R2 Processing
  - R2-LO: writes to fast disk → completes
  - R2-LO: waits for R1-LO on slow disk (correct behavior)
  - R2-HI: incorrectly waits for R2-LO (wrong sector range check)
  - R2-HI: writes to fast disk → completes
  - R2 completion notified

Now, R1-LO is writing to slow disk, R2-LO is pending on slow disk.
R1-HI and R2-HI are pending on slow disk

Step 20-28: Slow Disk Completes (OUT OF ORDER!)
  - R1-LO completes on slow disk
  - R2-LO starts and completes on slow disk
  - R2-HI starts and completes on slow disk ← R2 BEFORE R1!
  - R1-HI finally starts and completes on slow disk

RESULT:
=======
Fast disk write order:  R1-LO → R1-HI → R2-LO → R2-HI  (CORRECT)
Slow disk write order:  R1-LO → R2-LO → R2-HI → R1-HI  (WRONG!)

The slow disk has R2-HI written before R1-HI, causing data corruption.

Best Regards
Xiao


>
> Patch03 can fix this problem. But the root cause should be the
> sequence getting lock after being woken up. So besides patch03, it is
> necessary to ensure that the order of processing I/O matches the order
> of adding I/O. If we don't need to consider upper layer can submit
> overlap bios simultaneously, I'll fix this based on rdev rathar than
> mddev.
>
> Best Regards
> Xiao
>
> >
> > >
> > > Xiao Ni (3):
> > >    md: add return value of mddev_create_serial_pool
> > >    md/raid1: fix data corruption by moving serialization to mddev level
> > >    md/raid1: fix incorrect sector range in serialization
> > >
> > >   drivers/md/md-bitmap.c |  28 +++++--
> > >   drivers/md/md.c        | 171 ++++++++++++++---------------------------
> > >   drivers/md/md.h        |  30 ++++----
> > >   drivers/md/raid1.c     |  47 ++++++-----
> > >   4 files changed, 115 insertions(+), 161 deletions(-)
> > >
> > --
> > Thansk,
> > Kuai
> >


^ permalink raw reply

* Re: [PATCH RFC 0/3] md/raid1: data corruption with serialization
From: Xiao Ni @ 2026-02-05  0:34 UTC (permalink / raw)
  To: yukuai; +Cc: linux-raid, Guoqing Jiang
In-Reply-To: <a058e5a3-e96c-4feb-8f9d-e1135951973c@fnnas.com>

On Wed, Feb 4, 2026 at 11:58 PM Yu Kuai <yukuai@fnnas.com> wrote:
>
> Hi,
>
> 在 2026/2/4 22:58, Xiao Ni 写道:
> > A data corruption can happen when using serialization for raid1.
> > Serialization is not enabled by default. But it looks like there
> > is a data corruption risk if serialization is closed. Because the
> > lower driver can't guarantee the sequence which io is written first.
> > So it's possible that different member disks will have different
> > data for nvme devices. This patch set doesn't open serialization
> > by default.
>
> An important idea is that if there are filesystem on top of raid1/10/5, there
> is no need to consider overlap bio. The only case for serialization is WriteMostly,
> that bio can be returned to user while it's not done for slow disks. And that is
> the reason why serialization is for rdev.

I know the reason why it needs serialization for writemostly case. At
first, I asked the same question if we need to handle overlap if a
filesystem is on raid. I got the answer from AI, filesystem can submit
overlap bios simultaneously, maybe it's the reason why
serialize_policy was introduced? And database is another case which
can write to raid1 directly without a filesystem. So I started this
job. If we don't need to consider overlap, why do we need
serialize_policy? cc guoqing who is the author of serialize_policy
also.

>
> We definitely will not enable serialization by default, because there will
> be huge performance degradation. And I don't think we'll handle the case user

Yes, this is the reason why I don't enable it.

> manage data with raw disk, and write to the same area concurrently. At last, if
> there are still problem with WriteMostly case, please rebase and keep serialization
> for rdev.

The data corruption is:

R1 issued (split into LO and HI)
(LO)                                                              (HI)
R1 write to high-speed disk begins              R1 write to high-speed
disk waits (waits while LO is being written due to an incorrect sector
range)
R1 write to low-speed disk begins
R1 write to high-speed disk completes
High-speed disk wakes up                           R1 write to
high-speed disk begins

R1 write to low-speed disk waits (waits while LO is being written due
to an incorrect sector range)

R1 write to high-speed disk completes
R1 completion notification

R2 issued (split into LO and HI)
R2 write to high-speed disk begins              R2 write to high-speed
disk waits (waits while LO is being written due to an incorrect sector
range)
R2 write to low-speed disk waits (due to R1 being written)
R2 write to high-speed disk completes
High-speed disk wakes up                           R2 write to
high-speed disk begins

R2 write to low-speed disk Wait (R1 is still waiting)

R2 write completed on high-speed disk
R2 completion notification

R1 write completed on slow disk
Slow disk wake-up
R2 write started on slow disk
R2 write completed on slow disk
Slow disk wake-up                                       R2 write
started on slow disk *R2 will be written before R1 here.

R2 write completed on slow disk

Slow disk wake-up

R1 write started on slow disk

Patch03 can fix this problem. But the root cause should be the
sequence getting lock after being woken up. So besides patch03, it is
necessary to ensure that the order of processing I/O matches the order
of adding I/O. If we don't need to consider upper layer can submit
overlap bios simultaneously, I'll fix this based on rdev rathar than
mddev.

Best Regards
Xiao

>
> >
> > Xiao Ni (3):
> >    md: add return value of mddev_create_serial_pool
> >    md/raid1: fix data corruption by moving serialization to mddev level
> >    md/raid1: fix incorrect sector range in serialization
> >
> >   drivers/md/md-bitmap.c |  28 +++++--
> >   drivers/md/md.c        | 171 ++++++++++++++---------------------------
> >   drivers/md/md.h        |  30 ++++----
> >   drivers/md/raid1.c     |  47 ++++++-----
> >   4 files changed, 115 insertions(+), 161 deletions(-)
> >
> --
> Thansk,
> Kuai
>


^ permalink raw reply

* Re: [PATCH v2 00/14] folio support for sync I/O in RAID
From: Yu Kuai @ 2026-02-04 17:02 UTC (permalink / raw)
  To: linan666, song; +Cc: xni, linux-raid, linux-kernel, yangerkun, yi.zhang, yukuai
In-Reply-To: <20260128075708.2259525-1-linan666@huaweicloud.com>

Hi,

在 2026/1/28 15:56, linan666@huaweicloud.com 写道:
> From: Li Nan <linan122@huawei.com>
>
> This patchset adds folio support to sync operations in raid1/10.
> Previously, we used 16 * 4K pages for 64K sync I/O. With this change,
> we'll use a single 64K folio instead. Using folios reduces
> resync/recovery time by 20% on HDD.
>
> This is the first step towards full folio support in RAID. Going forward,
> I will replace the remaining page-based usage with folio.
>
> The patchset was tested with mdadm. Additional fault injection stress tests
> were run under file systems.

For conclusion, I think you can send patch 1,8,9 first, they're not quite
related to folio and can be applied. Then, please move patch 12,13 before patch 5
in the separate patch set, and looks like the number of patches can be small.

BTW, there are still pages in other context, do you plan to convert them?

>
> v2:
>   - Remove patch "md: use folio for bb_folio". It will be included in
>     a later patch set
>   - In patch 5:
>      1) fix typo
>      2) rewrite the logic of copying data in process_checks()
>      3) rename resync_get_all_folio() to resync_get_folio()
>      4) s/resync_pages *rps/resync_folio *rfs/g in
>         raid1_alloc_init_r1buf() and raid10_alloc_init_r10buf()
>   - Subsequent patches: Adapting conflicts caused by patch 5
>
> Li Nan (14):
>    md/raid1,raid10: clean up of RESYNC_SECTORS
>    md: introduce sync_folio_io for folio support in RAID
>    md/raid1: use folio for tmppage
>    md/raid10: use folio for tmppage
>    md/raid1,raid10: use folio for sync path IO
>    md: Clean up folio sync support related code
>    md/raid1: clean up useless sync_blocks handling in raid1_sync_request
>    md/raid1: fix IO error at logical block size granularity
>    md/raid10: fix IO error at logical block size granularity
>    md/raid1,raid10: clean up resync_fetch_folio
>    md: clean up resync_free_folio
>    md/raid1: clean up sync IO size calculation in raid1_sync_request
>    md/raid10: clean up sync IO size calculation in raid10_sync_request
>    md/raid1,raid10: fall back to smaller order if sync folio alloc fails
>
>   drivers/md/md.h       |   2 +
>   drivers/md/raid1.h    |   2 +-
>   drivers/md/raid10.h   |   2 +-
>   drivers/md/md.c       |  29 +++-
>   drivers/md/raid1-10.c |  81 ++++-------
>   drivers/md/raid1.c    | 233 ++++++++++++++-----------------
>   drivers/md/raid10.c   | 315 ++++++++++++++++++++----------------------
>   7 files changed, 308 insertions(+), 356 deletions(-)
>
-- 
Thansk,
Kuai

^ permalink raw reply

* Re: [PATCH v2 11/14] md: clean up resync_free_folio
From: Yu Kuai @ 2026-02-04 16:59 UTC (permalink / raw)
  To: linan666, song; +Cc: xni, linux-raid, linux-kernel, yangerkun, yi.zhang, yukuai
In-Reply-To: <20260128075708.2259525-12-linan666@huaweicloud.com>

Hi,

在 2026/1/28 15:57, linan666@huaweicloud.com 写道:
> From: Li Nan <linan122@huawei.com>
>
> The resync_free_folio() helper only wraps a single folio_put() call,
> so remove it and call folio_put() directly.
>
> Signed-off-by: Li Nan <linan122@huawei.com>
> ---
>   drivers/md/raid1-10.c | 5 -----
>   drivers/md/raid1.c    | 4 ++--
>   drivers/md/raid10.c   | 4 ++--
>   3 files changed, 4 insertions(+), 9 deletions(-)

Same as patch 10, I think this patch should be merged into patch 5.

>
> diff --git a/drivers/md/raid1-10.c b/drivers/md/raid1-10.c
> index 2ff1f8855900..ffbd7bd0f6e8 100644
> --- a/drivers/md/raid1-10.c
> +++ b/drivers/md/raid1-10.c
> @@ -50,11 +50,6 @@ static inline int resync_alloc_folio(struct resync_folio *rf,
>   	return 0;
>   }
>   
> -static inline void resync_free_folio(struct resync_folio *rf)
> -{
> -	folio_put(rf->folio);
> -}
> -
>   /*
>    * 'strct resync_folio' stores actual pages used for doing the resync
>    *  IO, and it is per-bio, so make .bi_private points to it.
> diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
> index a303349eeff4..b5239b5cb4e9 100644
> --- a/drivers/md/raid1.c
> +++ b/drivers/md/raid1.c
> @@ -199,7 +199,7 @@ static void * r1buf_pool_alloc(gfp_t gfp_flags, void *data)
>   
>   out_free_folio:
>   	while (--j >= 0)
> -		resync_free_folio(&rfs[j]);
> +		folio_put(rfs[j].folio);
>   
>   out_free_bio:
>   	while (++j < conf->raid_disks * 2) {
> @@ -222,7 +222,7 @@ static void r1buf_pool_free(void *__r1_bio, void *data)
>   
>   	for (i = conf->raid_disks * 2; i--; ) {
>   		rf = get_resync_folio(r1bio->bios[i]);
> -		resync_free_folio(rf);
> +		folio_put(rf->folio);
>   		bio_uninit(r1bio->bios[i]);
>   		kfree(r1bio->bios[i]);
>   	}
> diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
> index d8a5fadfc933..b728131bdad4 100644
> --- a/drivers/md/raid10.c
> +++ b/drivers/md/raid10.c
> @@ -204,7 +204,7 @@ static void * r10buf_pool_alloc(gfp_t gfp_flags, void *data)
>   
>   out_free_folio:
>   	while (--j >= 0)
> -		resync_free_folio(&rfs[j]);
> +		folio_put(rfs[j].folio);
>   
>   	j = 0;
>   out_free_bio:
> @@ -234,7 +234,7 @@ static void r10buf_pool_free(void *__r10_bio, void *data)
>   
>   		if (bio) {
>   			rf = get_resync_folio(bio);
> -			resync_free_folio(rf);
> +			folio_put(rf->folio);
>   			bio_uninit(bio);
>   			kfree(bio);
>   		}

-- 
Thansk,
Kuai

^ permalink raw reply

* Re: [RFC v2 0/5] md/raid1: introduce a new sync action to repair badblocks
From: Christoph Hellwig @ 2026-02-04 16:59 UTC (permalink / raw)
  To: Pascal Hambourg
  Cc: Christoph Hellwig, Zheng Qixing, song, yukuai, linan122, xni,
	linux-raid, linux-kernel, yi.zhang, yangerkun, houtao1,
	zhengqixing
In-Reply-To: <9eba8de0-c3b5-47a8-8729-205e0000fc23@plouf.fr.eu.org>

On Tue, Feb 03, 2026 at 09:36:38PM +0100, Pascal Hambourg wrote:
> I did not keep track of the models and do not remember them, it was a long
> time ago. They were mostly hard disk drives from Dell and HP professional
> desktop and laptop series, so consumer grade I guess, manufactured around
> 2010.

Ok, for 15ish year old consumer devices I would not be very surprised.

^ permalink raw reply

* Re: [PATCH v2 10/14] md/raid1,raid10: clean up resync_fetch_folio
From: Yu Kuai @ 2026-02-04 16:57 UTC (permalink / raw)
  To: linan666, song; +Cc: xni, linux-raid, linux-kernel, yangerkun, yi.zhang, yukuai
In-Reply-To: <20260128075708.2259525-11-linan666@huaweicloud.com>

Hi,

在 2026/1/28 15:57, linan666@huaweicloud.com 写道:
> From: Li Nan <linan122@huawei.com>
>
> The helper resync_fetch_folio() only returns the folio member without
> any additional logic. Clean it up by accessing rf->folio directly.
>
> Signed-off-by: Li Nan <linan122@huawei.com>
> ---
>   drivers/md/raid1-10.c |  7 +------
>   drivers/md/raid1.c    | 10 ++++------
>   drivers/md/raid10.c   |  3 +--
>   3 files changed, 6 insertions(+), 14 deletions(-)

Again, I think this patch should be merged into patch 5.

>
> diff --git a/drivers/md/raid1-10.c b/drivers/md/raid1-10.c
> index 568ab002691f..2ff1f8855900 100644
> --- a/drivers/md/raid1-10.c
> +++ b/drivers/md/raid1-10.c
> @@ -55,11 +55,6 @@ static inline void resync_free_folio(struct resync_folio *rf)
>   	folio_put(rf->folio);
>   }
>   
> -static inline struct folio *resync_fetch_folio(struct resync_folio *rf)
> -{
> -	return rf->folio;
> -}
> -
>   /*
>    * 'strct resync_folio' stores actual pages used for doing the resync
>    *  IO, and it is per-bio, so make .bi_private points to it.
> @@ -74,7 +69,7 @@ static void md_bio_reset_resync_folio(struct bio *bio, struct resync_folio *rf,
>   			       int size)
>   {
>   	/* initialize bvec table again */
> -	if (WARN_ON(!bio_add_folio(bio, resync_fetch_folio(rf),
> +	if (WARN_ON(!bio_add_folio(bio, rf->folio,
>   				   min_t(int, size, RESYNC_BLOCK_SIZE),
>   				   0))) {
>   		bio->bi_status = BLK_STS_RESOURCE;
> diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
> index 27e3b2375b16..a303349eeff4 100644
> --- a/drivers/md/raid1.c
> +++ b/drivers/md/raid1.c
> @@ -2981,8 +2981,8 @@ static sector_t raid1_sync_request(struct mddev *mddev, sector_t sector_nr,
>   		max_sector = sector_nr + good_sectors;
>   	nr_sectors = 0;
>   	do {
> -		struct folio *folio;
>   		int len = RESYNC_BLOCK_SIZE;
> +
>   		if (sector_nr + (len>>9) > max_sector)
>   			len = (max_sector - sector_nr) << 9;
>   		if (len == 0)
> @@ -2996,13 +2996,11 @@ static sector_t raid1_sync_request(struct mddev *mddev, sector_t sector_nr,
>   			len = sync_blocks<<9;
>   
>   		for (i = 0 ; i < conf->raid_disks * 2; i++) {
> -			struct resync_folio *rf;
> -
>   			bio = r1_bio->bios[i];
> -			rf = get_resync_folio(bio);
>   			if (bio->bi_end_io) {
> -				folio = resync_fetch_folio(rf);
> -				bio_add_folio_nofail(bio, folio, len, 0);
> +				struct resync_folio *rf = get_resync_folio(bio);
> +
> +				bio_add_folio_nofail(bio, rf->folio, len, 0);
>   			}
>   		}
>   		nr_sectors += len>>9;
> diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
> index 06257eea97ed..d8a5fadfc933 100644
> --- a/drivers/md/raid10.c
> +++ b/drivers/md/raid10.c
> @@ -3630,9 +3630,8 @@ static sector_t raid10_sync_request(struct mddev *mddev, sector_t sector_nr,
>   			break;
>   		for (bio= biolist ; bio ; bio=bio->bi_next) {
>   			struct resync_folio *rf = get_resync_folio(bio);
> -			struct folio *folio = resync_fetch_folio(rf);
>   
> -			if (WARN_ON(!bio_add_folio(bio, folio, len, 0))) {
> +			if (WARN_ON(!bio_add_folio(bio, rf->folio, len, 0))) {
>   				bio->bi_status = BLK_STS_RESOURCE;
>   				bio_endio(bio);
>   				*skipped = 1;

-- 
Thansk,
Kuai

^ permalink raw reply

* Re: [PATCH v2 09/14] md/raid10: fix IO error at logical block size granularity
From: Yu Kuai @ 2026-02-04 16:55 UTC (permalink / raw)
  To: linan666, song; +Cc: xni, linux-raid, linux-kernel, yangerkun, yi.zhang, yukuai
In-Reply-To: <20260128075708.2259525-10-linan666@huaweicloud.com>

在 2026/1/28 15:57, linan666@huaweicloud.com 写道:

> From: Li Nan<linan122@huawei.com>
>
> RAID10 currently fixes IO error at PAGE_SIZE granularity. Fix at smaller
> granularity can handle more errors, and RAID will support logical block
> sizes larger than PAGE_SIZE in the future, where PAGE_SIZE IO will fail.
>
> Switch IO error fix granularity to logical block size.
>
> Signed-off-by: Li Nan<linan122@huawei.com>
> ---
>   drivers/md/raid10.c | 18 +++++-------------
>   1 file changed, 5 insertions(+), 13 deletions(-)
Reviewed-by: Yu Kuai <yukuai@fnnas.com>

-- 
Thansk,
Kuai

^ permalink raw reply

* Re: [PATCH v2 06/14] md: Clean up folio sync support related code
From: Yu Kuai @ 2026-02-04 16:52 UTC (permalink / raw)
  To: linan666, song; +Cc: xni, linux-raid, linux-kernel, yangerkun, yi.zhang, yukuai
In-Reply-To: <20260128075708.2259525-7-linan666@huaweicloud.com>

Hi,

在 2026/1/28 15:57, linan666@huaweicloud.com 写道:
> From: Li Nan <linan122@huawei.com>
>
> 1. Remove resync_get_all_folio() and invoke folio_get() directly instead.
> 2. Clean up redundant while(0) loop in md_bio_reset_resync_folio().
> 3. Clean up bio variable by directly referencing r10_bio->devs[j].bio
>     instead in r1buf_pool_alloc() and r10buf_pool_alloc().
> 4. Clean up RESYNC_PAGES.
>
> Signed-off-by: Li Nan <linan122@huawei.com>
> Reviewed-by: Xiao Ni <xni@redhat.com>
> ---
>   drivers/md/raid1-10.c | 22 ++++++----------------
>   drivers/md/raid1.c    |  6 ++----
>   drivers/md/raid10.c   |  6 ++----
>   3 files changed, 10 insertions(+), 24 deletions(-)

I think this patch can be merged into patch 5.

> diff --git a/drivers/md/raid1-10.c b/drivers/md/raid1-10.c
> index 300fbe9dc02e..568ab002691f 100644
> --- a/drivers/md/raid1-10.c
> +++ b/drivers/md/raid1-10.c
> @@ -1,7 +1,6 @@
>   // SPDX-License-Identifier: GPL-2.0
>   /* Maximum size of each resync request */
>   #define RESYNC_BLOCK_SIZE (64*1024)
> -#define RESYNC_PAGES ((RESYNC_BLOCK_SIZE + PAGE_SIZE-1) / PAGE_SIZE)
>   #define RESYNC_SECTORS (RESYNC_BLOCK_SIZE >> 9)
>   
>   /*
> @@ -56,11 +55,6 @@ static inline void resync_free_folio(struct resync_folio *rf)
>   	folio_put(rf->folio);
>   }
>   
> -static inline void resync_get_folio(struct resync_folio *rf)
> -{
> -	folio_get(rf->folio);
> -}
> -
>   static inline struct folio *resync_fetch_folio(struct resync_folio *rf)
>   {
>   	return rf->folio;
> @@ -80,16 +74,12 @@ static void md_bio_reset_resync_folio(struct bio *bio, struct resync_folio *rf,
>   			       int size)
>   {
>   	/* initialize bvec table again */
> -	do {
> -		struct folio *folio = resync_fetch_folio(rf);
> -		int len = min_t(int, size, RESYNC_BLOCK_SIZE);
> -
> -		if (WARN_ON(!bio_add_folio(bio, folio, len, 0))) {
> -			bio->bi_status = BLK_STS_RESOURCE;
> -			bio_endio(bio);
> -			return;
> -		}
> -	} while (0);
> +	if (WARN_ON(!bio_add_folio(bio, resync_fetch_folio(rf),
> +				   min_t(int, size, RESYNC_BLOCK_SIZE),
> +				   0))) {
> +		bio->bi_status = BLK_STS_RESOURCE;
> +		bio_endio(bio);
> +	}
>   }
>   
>   
> diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
> index d9c106529289..5954ead7dfd4 100644
> --- a/drivers/md/raid1.c
> +++ b/drivers/md/raid1.c
> @@ -181,18 +181,16 @@ static void * r1buf_pool_alloc(gfp_t gfp_flags, void *data)
>   	for (j = 0; j < conf->raid_disks * 2; j++) {
>   		struct resync_folio *rf = &rfs[j];
>   
> -		bio = r1_bio->bios[j];
> -
>   		if (j < need_folio) {
>   			if (resync_alloc_folio(rf, gfp_flags))
>   				goto out_free_folio;
>   		} else {
>   			memcpy(rf, &rfs[0], sizeof(*rf));
> -			resync_get_folio(rf);
> +			folio_get(rf->folio);
>   		}
>   
>   		rf->raid_bio = r1_bio;
> -		bio->bi_private = rf;
> +		r1_bio->bios[j]->bi_private = rf;
>   	}
>   
>   	r1_bio->master_bio = NULL;
> diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
> index 7533aeb23819..5c0975ec8809 100644
> --- a/drivers/md/raid10.c
> +++ b/drivers/md/raid10.c
> @@ -183,19 +183,17 @@ static void * r10buf_pool_alloc(gfp_t gfp_flags, void *data)
>   		if (rbio)
>   			rf_repl = &rfs[nalloc + j];
>   
> -		bio = r10_bio->devs[j].bio;
> -
>   		if (!j || test_bit(MD_RECOVERY_SYNC,
>   				   &conf->mddev->recovery)) {
>   			if (resync_alloc_folio(rf, gfp_flags))
>   				goto out_free_folio;
>   		} else {
>   			memcpy(rf, &rfs[0], sizeof(*rf));
> -			resync_get_folio(rf);
> +			folio_get(rf->folio);
>   		}
>   
>   		rf->raid_bio = r10_bio;
> -		bio->bi_private = rf;
> +		r10_bio->devs[j].bio->bi_private = rf;
>   		if (rbio) {
>   			memcpy(rf_repl, rf, sizeof(*rf));
>   			rbio->bi_private = rf_repl;

-- 
Thansk,
Kuai

^ permalink raw reply

* Re: [PATCH v2 08/14] md/raid1: fix IO error at logical block size granularity
From: Yu Kuai @ 2026-02-04 16:54 UTC (permalink / raw)
  To: linan666, song; +Cc: xni, linux-raid, linux-kernel, yangerkun, yi.zhang, yukuai
In-Reply-To: <20260128075708.2259525-9-linan666@huaweicloud.com>

在 2026/1/28 15:57, linan666@huaweicloud.com 写道:

> From: Li Nan<linan122@huawei.com>
>
> RAID1 currently fixes IO error at PAGE_SIZE granularity. Fix at smaller
> granularity can handle more errors, and RAID will support logical block
> sizes larger than PAGE_SIZE in the future, where PAGE_SIZE IO will fail.
>
> Switch IO error fix granularity to logical block size.
>
> Signed-off-by: Li Nan<linan122@huawei.com>
> ---
>   drivers/md/raid1.c | 13 ++++---------
>   1 file changed, 4 insertions(+), 9 deletions(-)
Reviewed-by: Yu Kuai <yukuai@fnnas.com>

-- 
Thansk,
Kuai

^ permalink raw reply


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