Linux RAID subsystem development
 help / color / mirror / Atom feed
* [RFC v2 2/5] md: serialize requested sync actions and clear stale request state
From: Zheng Qixing @ 2026-02-03  6:12 UTC (permalink / raw)
  To: song, yukuai, linan122
  Cc: xni, linux-raid, linux-kernel, yi.zhang, yangerkun, houtao1,
	zhengqixing
In-Reply-To: <20260203061259.609206-1-zhengqixing@huaweicloud.com>

From: Zheng Qixing <zhengqixing@huawei.com>

In handle_requested_sync_action(), return -EBUSY when
MD_RECOVERY_REQUESTED is already set. This serializes requested sync
actions (such as check or repair) and avoids a race window where a
second sync request can be issued before MD_RECOVERY_RUNNING is set,
resulting in the later request being neither rejected nor executed.

Additionally, in md_check_recovery(), clear requested-sync related
state bits when no recovery operation is running. This prevents stale
request state from persisting in cases where a sync action is queued
and 'frozen' is written before MD_RECOVERY_RUNNING is set, which would
cause subsequent sync requests to spuriously fail with -EBUSY.

Signed-off-by: Zheng Qixing <zhengqixing@huawei.com>
---
 drivers/md/md.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/drivers/md/md.c b/drivers/md/md.c
index 84af578876e2..7fe02ee21d3e 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -668,6 +668,9 @@ void mddev_put(struct mddev *mddev)
 static int handle_requested_sync_action(struct mddev *mddev,
 					enum sync_action action)
 {
+	if (test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery))
+		return -EBUSY;
+
 	switch (action) {
 	case ACTION_CHECK:
 		set_bit(MD_RECOVERY_CHECK, &mddev->recovery);
@@ -10318,6 +10321,9 @@ void md_check_recovery(struct mddev *mddev)
 			queue_work(md_misc_wq, &mddev->sync_work);
 		} else {
 			clear_bit(MD_RECOVERY_RUNNING, &mddev->recovery);
+			clear_bit(MD_RECOVERY_SYNC, &mddev->recovery);
+			clear_bit(MD_RECOVERY_REQUESTED, &mddev->recovery);
+			clear_bit(MD_RECOVERY_CHECK, &mddev->recovery);
 			wake_up(&resync_wait);
 		}
 
-- 
2.39.2


^ permalink raw reply related

* [RFC v2 0/5] md/raid1: introduce a new sync action to repair badblocks
From: Zheng Qixing @ 2026-02-03  6:12 UTC (permalink / raw)
  To: song, yukuai, linan122
  Cc: xni, linux-raid, linux-kernel, yi.zhang, yangerkun, houtao1,
	zhengqixing

From: Zheng Qixing <zhengqixing@huawei.com>

Hi,

This is v2 of the series.

# Mechanism
When rectifying badblocks, we issue a single repair write for
the bad range (copy data from a good mirror to the corresponding
LBA on bad mirror). Once the write completes successfully
(bi_status == 0), the LBA range is cleared from the badblocks
table. If the media is still bad for that LBA, a subsequent
read/write will fail again and the range will be marked bad
again.

Doing a read-back for every repair would only prove that the data
is readable at that moment, and it does not provide a stronger
guarantee against future internal remapping.

# Why use LBS granularity for bad-block repair?
In our RAID1 bad-block repair (rectify) testing on a device
reporting 512B logical blocks and 4KiB physical blocks, we
issue 512B I/O directly to the md device and inject an I/O
fault.

Since the md badblocks table can only track failures in terms
of host-visible LBA ranges, it is updated at 512B sector
granularity (i.e., it records the failing sector) and does not
attempt to infer or expand the entry to a 4KiB physical-block
boundary.

Given that the OS has no visibility into the device's internal
mapping from LBAs to physical media (or the FTL), using
logical block size for recording and repairing bad blocks is
the most appropriate choice from a correctness standpoint.

If the underlying media failure is actually larger than 512B,
this is typically reflected by subsequent failures on adjacent
LBAs, at which point the recorded bad range will naturally
grow to cover the affected area.

# Tests
This feature has been tested on a RAID1 built from two 480GB
system disks. It has also been tested under QEMU with a 4-disk
RAID1 setup, with both memory fault injection and I/O fault
injection enabled.

In addition, we will add a new test (26raid1-rectify-badblocks)
in mdadm/tests to verify whether `rectify` can effectively repair
sectors recorded in bad_blocks.

# TODO
rectify currently only supports bad-block repair for the RAID1
level. We will consider extending it to RAID5/10 in follow-up
work.

Changes in v2:
 - Patch 1: Remove non-essential helpers to reduce indirection.
 - Patch 2: Split out a bugfix that was previously included in patch 1.
 - Patch 3: Rename the /proc/mdstat action from "recovery" to "recover"
   to match the naming used by action_store() and action_show().
 - Patch 4: Add a brief comment for MAX_RAID_DISKS.
 - Patch 5: For rectify, reuse handle_sync_write_finished() to handle
   write request completion, removing duplicate completion handling.

Link of v1:
  https://lore.kernel.org/all/20251231070952.1233903-1-zhengqixing@huaweicloud.com/

Zheng Qixing (5):
  md: add helpers for requested sync action
  md: serialize requested sync actions and clear stale request state
  md: rename mdstat action "recovery" to "recover"
  md: introduce MAX_RAID_DISKS macro to replace magic number
  md/raid1: introduce rectify action to repair badblocks

 drivers/md/md.c    | 152 +++++++++++++++++++------
 drivers/md/md.h    |  21 ++++
 drivers/md/raid1.c | 270 ++++++++++++++++++++++++++++++++++++++++++++-
 drivers/md/raid1.h |   1 +
 4 files changed, 409 insertions(+), 35 deletions(-)

-- 
2.39.2


^ permalink raw reply

* [RFC v2 5/5] md/raid1: introduce rectify action to repair badblocks
From: Zheng Qixing @ 2026-02-03  6:12 UTC (permalink / raw)
  To: song, yukuai, linan122
  Cc: xni, linux-raid, linux-kernel, yi.zhang, yangerkun, houtao1,
	zhengqixing
In-Reply-To: <20260203061259.609206-1-zhengqixing@huaweicloud.com>

From: Zheng Qixing <zhengqixing@huawei.com>

Add support for repairing known badblocks in RAID1. When disks
have known badblocks (shown in sysfs bad_blocks), data can be
read from other healthy disks in the array and written to repair
the badblock areas and clear it in bad_blocks.

echo rectify > sync_action can trigger this action.

Signed-off-by: Zheng Qixing <zhengqixing@huawei.com>
---
 drivers/md/md.c    |  71 +++++++++++-
 drivers/md/md.h    |  16 +++
 drivers/md/raid1.c | 270 ++++++++++++++++++++++++++++++++++++++++++++-
 drivers/md/raid1.h |   1 +
 4 files changed, 348 insertions(+), 10 deletions(-)

diff --git a/drivers/md/md.c b/drivers/md/md.c
index aebbdbaa4e0a..9b818fcef666 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -74,6 +74,7 @@ static const char *action_name[NR_SYNC_ACTIONS] = {
 	[ACTION_RECOVER]	= "recover",
 	[ACTION_CHECK]		= "check",
 	[ACTION_REPAIR]		= "repair",
+	[ACTION_RECTIFY]	= "rectify",
 	[ACTION_RESHAPE]	= "reshape",
 	[ACTION_FROZEN]		= "frozen",
 	[ACTION_IDLE]		= "idle",
@@ -665,13 +666,47 @@ void mddev_put(struct mddev *mddev)
 	spin_unlock(&all_mddevs_lock);
 }
 
+static int md_badblocks_precheck(struct mddev *mddev)
+{
+	struct md_rdev *rdev;
+	int valid_disks = 0;
+	int ret = -EINVAL;
+
+	/* rectify is currently supported only for RAID1 */
+	if (mddev->level != 1) {
+		pr_err("md/raid1:%s requires raid1 array\n", mdname(mddev));
+		return -EINVAL;
+	}
+
+	rdev_for_each(rdev, mddev) {
+		if (rdev->raid_disk < 0 ||
+		    test_bit(Faulty, &rdev->flags))
+			continue;
+		valid_disks++;
+	}
+	if (valid_disks >= 2)
+		ret = 0;
+
+	return ret;
+}
+
 static int handle_requested_sync_action(struct mddev *mddev,
 					enum sync_action action)
 {
+	int ret;
+
 	if (test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery))
 		return -EBUSY;
 
 	switch (action) {
+	case ACTION_RECTIFY:
+		ret = md_badblocks_precheck(mddev);
+		if (ret)
+			return ret;
+		set_bit(MD_RECOVERY_BADBLOCKS_RECTIFY, &mddev->recovery);
+		set_bit(MD_RECOVERY_REQUESTED, &mddev->recovery);
+		set_bit(MD_RECOVERY_SYNC, &mddev->recovery);
+		return 0;
 	case ACTION_CHECK:
 		set_bit(MD_RECOVERY_CHECK, &mddev->recovery);
 		fallthrough;
@@ -686,6 +721,8 @@ static int handle_requested_sync_action(struct mddev *mddev,
 
 static enum sync_action get_recovery_sync_action(struct mddev *mddev)
 {
+	if (test_bit(MD_RECOVERY_BADBLOCKS_RECTIFY, &mddev->recovery))
+		return ACTION_RECTIFY;
 	if (test_bit(MD_RECOVERY_CHECK, &mddev->recovery))
 		return ACTION_CHECK;
 	if (test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery))
@@ -695,11 +732,16 @@ static enum sync_action get_recovery_sync_action(struct mddev *mddev)
 
 static void set_requested_position(struct mddev *mddev, sector_t value)
 {
-	mddev->resync_min = value;
+	if (test_bit(MD_RECOVERY_BADBLOCKS_RECTIFY, &mddev->recovery))
+		mddev->rectify_min = value;
+	else
+		mddev->resync_min = value;
 }
 
 static sector_t get_requested_position(struct mddev *mddev)
 {
+	if (test_bit(MD_RECOVERY_BADBLOCKS_RECTIFY, &mddev->recovery))
+		return mddev->rectify_min;
 	return mddev->resync_min;
 }
 
@@ -820,6 +862,7 @@ int mddev_init(struct mddev *mddev)
 	mddev->reshape_backwards = 0;
 	mddev->last_sync_action = ACTION_IDLE;
 	mddev->resync_min = 0;
+	mddev->rectify_min = 0;
 	mddev->resync_max = MaxSector;
 	mddev->level = LEVEL_NONE;
 
@@ -5139,7 +5182,10 @@ enum sync_action md_sync_action(struct mddev *mddev)
 	if (test_bit(MD_RECOVERY_RECOVER, &recovery))
 		return ACTION_RECOVER;
 
-	/* MD_RECOVERY_CHECK must be paired with MD_RECOVERY_REQUESTED. */
+	/*
+	 * MD_RECOVERY_CHECK / MD_RECOVERY_BADBLOCKS_RECTIFY must be
+	 * paired with MD_RECOVERY_REQUESTED.
+	 */
 	if (test_bit(MD_RECOVERY_SYNC, &recovery))
 		return get_recovery_sync_action(mddev);
 
@@ -5304,6 +5350,7 @@ action_store(struct mddev *mddev, const char *page, size_t len)
 			break;
 		case ACTION_RESHAPE:
 		case ACTION_RECOVER:
+		case ACTION_RECTIFY:
 		case ACTION_CHECK:
 		case ACTION_REPAIR:
 		case ACTION_RESYNC:
@@ -5329,6 +5376,7 @@ action_store(struct mddev *mddev, const char *page, size_t len)
 			clear_bit(MD_RECOVERY_FROZEN, &mddev->recovery);
 			set_bit(MD_RECOVERY_RECOVER, &mddev->recovery);
 			break;
+		case ACTION_RECTIFY:
 		case ACTION_CHECK:
 		case ACTION_REPAIR:
 			ret = handle_requested_sync_action(mddev, action);
@@ -6813,6 +6861,7 @@ static void md_clean(struct mddev *mddev)
 	mddev->raid_disks = 0;
 	mddev->resync_offset = 0;
 	mddev->resync_min = 0;
+	mddev->rectify_min = 0;
 	mddev->resync_max = MaxSector;
 	mddev->reshape_position = MaxSector;
 	/* we still need mddev->external in export_rdev, do not clear it yet */
@@ -9343,6 +9392,7 @@ static sector_t md_sync_max_sectors(struct mddev *mddev,
 {
 	switch (action) {
 	case ACTION_RESYNC:
+	case ACTION_RECTIFY:
 	case ACTION_CHECK:
 	case ACTION_REPAIR:
 		atomic64_set(&mddev->resync_mismatches, 0);
@@ -9395,6 +9445,7 @@ static sector_t md_sync_position(struct mddev *mddev, enum sync_action action)
 	struct md_rdev *rdev;
 
 	switch (action) {
+	case ACTION_RECTIFY:
 	case ACTION_CHECK:
 	case ACTION_REPAIR:
 		return get_requested_position(mddev);
@@ -10020,6 +10071,7 @@ static bool md_choose_sync_action(struct mddev *mddev, int *spares)
 		clear_bit(MD_RECOVERY_SYNC, &mddev->recovery);
 		clear_bit(MD_RECOVERY_CHECK, &mddev->recovery);
 		clear_bit(MD_RECOVERY_REQUESTED, &mddev->recovery);
+		clear_bit(MD_RECOVERY_BADBLOCKS_RECTIFY, &mddev->recovery);
 
 		/* Start new recovery. */
 		set_bit(MD_RECOVERY_RECOVER, &mddev->recovery);
@@ -10077,10 +10129,14 @@ static void md_start_sync(struct work_struct *ws)
 	if (spares && md_bitmap_enabled(mddev, true))
 		mddev->bitmap_ops->write_all(mddev);
 
-	name = test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery) ?
-			"reshape" : "resync";
-	rcu_assign_pointer(mddev->sync_thread,
-			   md_register_thread(md_do_sync, mddev, name));
+	if (!is_badblocks_recovery_requested(mddev) ||
+	    !md_badblocks_precheck(mddev)) {
+		name = test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery) ?
+				"reshape" : "resync";
+		rcu_assign_pointer(mddev->sync_thread,
+				   md_register_thread(md_do_sync, mddev, name));
+	}
+
 	if (!mddev->sync_thread) {
 		pr_warn("%s: could not start resync thread...\n",
 			mdname(mddev));
@@ -10108,6 +10164,7 @@ static void md_start_sync(struct work_struct *ws)
 	clear_bit(MD_RECOVERY_REQUESTED, &mddev->recovery);
 	clear_bit(MD_RECOVERY_CHECK, &mddev->recovery);
 	clear_bit(MD_RECOVERY_RUNNING, &mddev->recovery);
+	clear_bit(MD_RECOVERY_BADBLOCKS_RECTIFY, &mddev->recovery);
 	mddev_unlock(mddev);
 	/*
 	 * md_start_sync was triggered by MD_RECOVERY_NEEDED, so we should
@@ -10322,6 +10379,7 @@ void md_check_recovery(struct mddev *mddev)
 			clear_bit(MD_RECOVERY_SYNC, &mddev->recovery);
 			clear_bit(MD_RECOVERY_REQUESTED, &mddev->recovery);
 			clear_bit(MD_RECOVERY_CHECK, &mddev->recovery);
+			clear_bit(MD_RECOVERY_BADBLOCKS_RECTIFY, &mddev->recovery);
 			wake_up(&resync_wait);
 		}
 
@@ -10372,6 +10430,7 @@ void md_reap_sync_thread(struct mddev *mddev)
 	clear_bit(MD_RECOVERY_RESHAPE, &mddev->recovery);
 	clear_bit(MD_RECOVERY_REQUESTED, &mddev->recovery);
 	clear_bit(MD_RECOVERY_CHECK, &mddev->recovery);
+	clear_bit(MD_RECOVERY_BADBLOCKS_RECTIFY, &mddev->recovery);
 	clear_bit(MD_RECOVERY_LAZY_RECOVER, &mddev->recovery);
 	/*
 	 * We call mddev->cluster_ops->update_size here because sync_size could
diff --git a/drivers/md/md.h b/drivers/md/md.h
index 14f9db38b7c5..0b9e3487bfed 100644
--- a/drivers/md/md.h
+++ b/drivers/md/md.h
@@ -102,6 +102,13 @@ enum sync_action {
 	 * are inconsistent data,
 	 */
 	ACTION_REPAIR,
+	/*
+	 * Represent by MD_RECOVERY_SYNC | MD_RECOVERY_REQUESTED |
+	 * MD_RECOVERY_BADBLOCKS_RECTIFY, start when user echo "rectify"
+	 * to sysfs api sync_action, used to repair the badblocks acked
+	 * in bad table;
+	 */
+	ACTION_RECTIFY,
 	/*
 	 * Represent by MD_RECOVERY_RESHAPE, start when new member disk is added
 	 * to the conf, notice that this is different from spares or
@@ -528,6 +535,7 @@ struct mddev {
 	sector_t			resync_offset;
 	sector_t			resync_min;	/* user requested sync
 							 * starts here */
+	sector_t			rectify_min;
 	sector_t			resync_max;	/* resync should pause
 							 * when it gets here */
 
@@ -668,6 +676,8 @@ enum recovery_flags {
 	MD_RESYNCING_REMOTE,
 	/* raid456 lazy initial recover */
 	MD_RECOVERY_LAZY_RECOVER,
+	/* try to repair acked badblocks*/
+	MD_RECOVERY_BADBLOCKS_RECTIFY,
 };
 
 enum md_ro_state {
@@ -1020,6 +1030,12 @@ static inline void mddev_unlock_and_resume(struct mddev *mddev)
 	mddev_resume(mddev);
 }
 
+static inline bool is_badblocks_recovery_requested(struct mddev *mddev)
+{
+	return test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery) &&
+	       test_bit(MD_RECOVERY_BADBLOCKS_RECTIFY, &mddev->recovery);
+}
+
 struct mdu_array_info_s;
 struct mdu_disk_info_s;
 
diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
index 00120c86c443..90686a0ff9ca 100644
--- a/drivers/md/raid1.c
+++ b/drivers/md/raid1.c
@@ -176,7 +176,8 @@ static void * r1buf_pool_alloc(gfp_t gfp_flags, void *data)
 	 * If this is a user-requested check/repair, allocate
 	 * RESYNC_PAGES for each bio.
 	 */
-	if (test_bit(MD_RECOVERY_REQUESTED, &conf->mddev->recovery))
+	if (test_bit(MD_RECOVERY_REQUESTED, &conf->mddev->recovery) &&
+	    !is_badblocks_recovery_requested(conf->mddev))
 		need_pages = conf->raid_disks * 2;
 	else
 		need_pages = 1;
@@ -2380,6 +2381,260 @@ static void sync_request_write(struct mddev *mddev, struct r1bio *r1_bio)
 	put_sync_write_buf(r1_bio);
 }
 
+static void end_rectify_read(struct bio *bio)
+{
+	struct r1bio *r1_bio = get_resync_r1bio(bio);
+	struct r1conf *conf = r1_bio->mddev->private;
+	struct md_rdev *rdev;
+	struct bio *next_bio;
+	bool all_fail = true;
+	int i;
+
+	update_head_pos(r1_bio->read_disk, r1_bio);
+
+	if (!bio->bi_status) {
+		set_bit(R1BIO_Uptodate, &r1_bio->state);
+		goto out;
+	}
+
+	for (i = r1_bio->read_disk + 1; i < conf->raid_disks; i++) {
+		rdev = conf->mirrors[i].rdev;
+		if (!rdev || test_bit(Faulty, &rdev->flags))
+			continue;
+
+		next_bio = r1_bio->bios[i];
+		if (next_bio->bi_end_io == end_rectify_read) {
+			r1_bio->read_disk = i;
+			all_fail = false;
+			break;
+		}
+	}
+
+	if (unlikely(all_fail)) {
+		md_done_sync(r1_bio->mddev, r1_bio->sectors);
+		md_sync_error(r1_bio->mddev);
+		put_buf(r1_bio);
+		return;
+	}
+out:
+	reschedule_retry(r1_bio);
+}
+
+static void end_rectify_write(struct bio *bio)
+{
+	struct r1bio *r1_bio = get_resync_r1bio(bio);
+
+	if (atomic_dec_and_test(&r1_bio->remaining)) {
+		/*
+		 * Rectify only attempts to clear acked bad
+		 * blocks, and it does not set bad blocks in
+		 * cases of R1BIO_WriteError.
+		 * Here we reuse R1BIO_MadeGood flag, which
+		 * does not guarantee that all write I/Os
+		 * actually succeeded.
+		 */
+		set_bit(R1BIO_MadeGood, &r1_bio->state);
+		reschedule_retry(r1_bio);
+	}
+}
+
+static void submit_rectify_read(struct r1bio *r1_bio)
+{
+	struct bio *bio;
+
+	bio = r1_bio->bios[r1_bio->read_disk];
+	bio->bi_status = 0;
+	submit_bio_noacct(bio);
+}
+
+static void rectify_request_write(struct mddev *mddev, struct r1bio *r1_bio)
+{
+	struct r1conf *conf = mddev->private;
+	struct bio *wbio = NULL;
+	struct md_rdev *rdev;
+	int wcnt = 0;
+	int i;
+
+	if (!test_bit(R1BIO_Uptodate, &r1_bio->state)) {
+		submit_rectify_read(r1_bio);
+		return;
+	}
+
+	atomic_set(&r1_bio->remaining, 0);
+	for (i = 0; i < conf->raid_disks; i++) {
+		rdev = conf->mirrors[i].rdev;
+		if (!rdev || test_bit(Faulty, &rdev->flags))
+			continue;
+		wbio = r1_bio->bios[i];
+		if (wbio->bi_end_io == end_rectify_write) {
+			atomic_inc(&r1_bio->remaining);
+			wcnt++;
+			submit_bio_noacct(wbio);
+		}
+	}
+
+	if (unlikely(!wcnt)) {
+		md_done_sync(r1_bio->mddev, r1_bio->sectors);
+		put_buf(r1_bio);
+	}
+}
+
+static void handle_sync_write(struct mddev *mddev, struct r1bio *r1_bio)
+{
+	if (test_bit(R1BIO_BadBlocksRectify, &r1_bio->state))
+		rectify_request_write(mddev, r1_bio);
+	else
+		sync_request_write(mddev, r1_bio);
+}
+
+static sector_t get_badblocks_sync_sectors(struct mddev *mddev, sector_t sector_nr,
+					   int *skipped, unsigned long *bad_disks)
+{
+	struct r1conf *conf = mddev->private;
+	sector_t nr_sectors = mddev->dev_sectors - sector_nr;
+	bool all_faulty = true;
+	struct md_rdev *rdev;
+	bool good = false;
+	int i;
+
+	*skipped = 0;
+	for (i = 0; i < conf->raid_disks; i++) {
+		sector_t first_bad;
+		sector_t bad_sectors;
+
+		rdev = conf->mirrors[i].rdev;
+		if (!rdev || test_bit(Faulty, &rdev->flags))
+			continue;
+
+		all_faulty = false;
+		if (is_badblock(rdev, sector_nr, nr_sectors, &first_bad, &bad_sectors)) {
+			if (first_bad <= sector_nr) {
+				set_bit(i, bad_disks);
+				nr_sectors = min(nr_sectors, first_bad + bad_sectors - sector_nr);
+			} else {
+				good  = true;
+				nr_sectors = min(nr_sectors, first_bad - sector_nr);
+			}
+		} else {
+			good  = true;
+		}
+	}
+
+	if (all_faulty) {
+		*skipped = 1;
+		return 0;
+	}
+
+	if (!good || !bitmap_weight(bad_disks, conf->raid_disks))
+		*skipped = 1;
+
+	/* make sure nr_sectors won't go across barrier unit boundary */
+	return align_to_barrier_unit_end(sector_nr, nr_sectors);
+}
+
+static sector_t get_next_sync_sector(struct mddev *mddev, sector_t sector_nr,
+				     int *skipped, unsigned long *bad_disks)
+{
+	sector_t nr_sectors;
+
+	nr_sectors = get_badblocks_sync_sectors(mddev, sector_nr,
+						skipped, bad_disks);
+	if (!(*skipped) && nr_sectors > RESYNC_PAGES * (PAGE_SIZE >> 9))
+		nr_sectors = RESYNC_PAGES * (PAGE_SIZE >> 9);
+	return nr_sectors;
+}
+
+static struct r1bio *raid1_alloc_init_r1buf(struct r1conf *conf);
+static struct r1bio *init_sync_badblocks_r1bio(struct mddev *mddev,
+					       sector_t sector_nr,
+					       sector_t nr_sectors,
+					       unsigned long *bad_disks)
+{
+	struct r1conf *conf = mddev->private;
+	struct r1bio *r1_bio;
+	struct md_rdev *rdev;
+	int page_idx = 0;
+	struct bio *bio;
+	int i;
+
+	r1_bio = raid1_alloc_init_r1buf(conf);
+	r1_bio->mddev = mddev;
+	r1_bio->sector = sector_nr;
+	r1_bio->sectors = nr_sectors;
+	r1_bio->state = 0;
+	r1_bio->read_disk = -1;
+	set_bit(R1BIO_IsSync, &r1_bio->state);
+	set_bit(R1BIO_BadBlocksRectify, &r1_bio->state);
+
+	for (i = 0; i < conf->raid_disks; i++) {
+		rdev = conf->mirrors[i].rdev;
+		if (!rdev || test_bit(Faulty, &rdev->flags))
+			continue;
+
+		if (r1_bio->read_disk < 0 && !test_bit(i, bad_disks))
+			r1_bio->read_disk = i;
+
+		bio = r1_bio->bios[i];
+		if (test_bit(i, bad_disks)) {
+			bio->bi_opf = REQ_OP_WRITE;
+			bio->bi_end_io = end_rectify_write;
+		} else {
+			bio->bi_opf = REQ_OP_READ;
+			bio->bi_end_io = end_rectify_read;
+		}
+
+		atomic_inc(&rdev->nr_pending);
+		bio->bi_iter.bi_sector = sector_nr + rdev->data_offset;
+		bio_set_dev(bio, rdev->bdev);
+	}
+
+	if (unlikely(r1_bio->read_disk < 0)) {
+		put_buf(r1_bio);
+		return NULL;
+	}
+
+	while (nr_sectors > 0 && page_idx < RESYNC_PAGES) {
+		int len = nr_sectors << 9 < PAGE_SIZE ?
+			  nr_sectors << 9 : PAGE_SIZE;
+		struct resync_pages *rp;
+
+		for (i = 0; i < conf->raid_disks; i++) {
+			bio = r1_bio->bios[i];
+			rp = get_resync_pages(bio);
+			__bio_add_page(bio, resync_fetch_page(rp, page_idx), len, 0);
+		}
+
+		nr_sectors -= len >> 9;
+		page_idx++;
+	}
+
+	return r1_bio;
+}
+
+static sector_t do_sync_badblocks_rectify(struct mddev *mddev,
+					  sector_t sector_nr, int *skipped)
+{
+	DECLARE_BITMAP(bad_disks, MAX_RAID_DISKS);
+	struct r1conf *conf = mddev->private;
+	struct r1bio *r1_bio;
+	sector_t nr_sectors;
+
+	bitmap_zero(bad_disks, MAX_RAID_DISKS);
+	nr_sectors = get_next_sync_sector(mddev, sector_nr, skipped, bad_disks);
+	if (*skipped) {
+		lower_barrier(conf, sector_nr);
+		return nr_sectors;
+	}
+
+	r1_bio = init_sync_badblocks_r1bio(mddev, sector_nr,
+					   nr_sectors, bad_disks);
+	if (!r1_bio)
+		return 0;
+
+	submit_rectify_read(r1_bio);
+	return nr_sectors;
+}
+
 /*
  * This is a kernel thread which:
  *
@@ -2558,13 +2813,16 @@ static void handle_sync_write_finished(struct r1conf *conf, struct r1bio *r1_bio
 {
 	int m;
 	int s = r1_bio->sectors;
+	bool is_rectify = test_bit(R1BIO_BadBlocksRectify, &r1_bio->state);
+
 	for (m = 0; m < conf->raid_disks * 2 ; m++) {
 		struct md_rdev *rdev = conf->mirrors[m].rdev;
 		struct bio *bio = r1_bio->bios[m];
 		if (bio->bi_end_io == NULL)
 			continue;
 		if (!bio->bi_status &&
-		    test_bit(R1BIO_MadeGood, &r1_bio->state))
+		    test_bit(R1BIO_MadeGood, &r1_bio->state) &&
+		    (!is_rectify || bio->bi_end_io == end_rectify_write))
 			rdev_clear_badblocks(rdev, r1_bio->sector, s, 0);
 		if (bio->bi_status &&
 		    test_bit(R1BIO_WriteError, &r1_bio->state))
@@ -2728,7 +2986,7 @@ static void raid1d(struct md_thread *thread)
 			    test_bit(R1BIO_WriteError, &r1_bio->state))
 				handle_sync_write_finished(conf, r1_bio);
 			else
-				sync_request_write(mddev, r1_bio);
+				handle_sync_write(mddev, r1_bio);
 		} else if (test_bit(R1BIO_MadeGood, &r1_bio->state) ||
 			   test_bit(R1BIO_WriteError, &r1_bio->state))
 			handle_write_finished(conf, r1_bio);
@@ -2837,7 +3095,8 @@ static sector_t raid1_sync_request(struct mddev *mddev, sector_t sector_nr,
 	/* before building a request, check if we can skip these blocks..
 	 * This call the bitmap_start_sync doesn't actually record anything
 	 */
-	if (!md_bitmap_start_sync(mddev, sector_nr, &sync_blocks, true) &&
+	if (!is_badblocks_recovery_requested(mddev) &&
+	    !md_bitmap_start_sync(mddev, sector_nr, &sync_blocks, true) &&
 	    !conf->fullsync && !test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery)) {
 		/* We can skip this block, and probably several more */
 		*skipped = 1;
@@ -2863,6 +3122,9 @@ static sector_t raid1_sync_request(struct mddev *mddev, sector_t sector_nr,
 	if (raise_barrier(conf, sector_nr))
 		return 0;
 
+	if (is_badblocks_recovery_requested(mddev))
+		return do_sync_badblocks_rectify(mddev, sector_nr, skipped);
+
 	r1_bio = raid1_alloc_init_r1buf(conf);
 
 	/*
diff --git a/drivers/md/raid1.h b/drivers/md/raid1.h
index c98d43a7ae99..6ca8bf808d69 100644
--- a/drivers/md/raid1.h
+++ b/drivers/md/raid1.h
@@ -184,6 +184,7 @@ enum r1bio_state {
 	R1BIO_MadeGood,
 	R1BIO_WriteError,
 	R1BIO_FailFast,
+	R1BIO_BadBlocksRectify,
 };
 
 static inline int sector_to_idx(sector_t sector)
-- 
2.39.2


^ permalink raw reply related

* [RFC v2 4/5] md: introduce MAX_RAID_DISKS macro to replace magic number
From: Zheng Qixing @ 2026-02-03  6:12 UTC (permalink / raw)
  To: song, yukuai, linan122
  Cc: xni, linux-raid, linux-kernel, yi.zhang, yangerkun, houtao1,
	zhengqixing
In-Reply-To: <20260203061259.609206-1-zhengqixing@huaweicloud.com>

From: Zheng Qixing <zhengqixing@huawei.com>

Per-device state is stored as a __le16 dev_roles[] array (2 bytes per
device) plus a fixed 256-byte header, still within a 4 KiB superblock.
Therefore the theoretical maximum is (4096 - 256) / 2 = 1920 entries.

Define MAX_RAID_DISKS macro for the maximum number of RAID disks. No
functional change.

Signed-off-by: Zheng Qixing <zhengqixing@huawei.com>
---
 drivers/md/md.c | 4 ++--
 drivers/md/md.h | 5 +++++
 2 files changed, 7 insertions(+), 2 deletions(-)

diff --git a/drivers/md/md.c b/drivers/md/md.c
index f319621c6832..aebbdbaa4e0a 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -1873,7 +1873,7 @@ static int super_1_load(struct md_rdev *rdev, struct md_rdev *refdev, int minor_
 
 	if (sb->magic != cpu_to_le32(MD_SB_MAGIC) ||
 	    sb->major_version != cpu_to_le32(1) ||
-	    le32_to_cpu(sb->max_dev) > (4096-256)/2 ||
+	    le32_to_cpu(sb->max_dev) > MAX_RAID_DISKS ||
 	    le64_to_cpu(sb->super_offset) != rdev->sb_start ||
 	    (le32_to_cpu(sb->feature_map) & ~MD_FEATURE_ALL) != 0)
 		return -EINVAL;
@@ -2050,7 +2050,7 @@ static int super_1_validate(struct mddev *mddev, struct md_rdev *freshest, struc
 		mddev->resync_offset = le64_to_cpu(sb->resync_offset);
 		memcpy(mddev->uuid, sb->set_uuid, 16);
 
-		mddev->max_disks =  (4096-256)/2;
+		mddev->max_disks = MAX_RAID_DISKS;
 
 		if (!mddev->logical_block_size)
 			mddev->logical_block_size = le32_to_cpu(sb->logical_block_size);
diff --git a/drivers/md/md.h b/drivers/md/md.h
index a083f37374d0..14f9db38b7c5 100644
--- a/drivers/md/md.h
+++ b/drivers/md/md.h
@@ -21,6 +21,11 @@
 #include <linux/raid/md_u.h>
 #include <trace/events/block.h>
 
+/*
+ * v1.x superblock occupies one 4 KiB block: 256B header + 2B per device
+ * in dev_roles[].
+ */
+#define MAX_RAID_DISKS ((4096-256)/2)
 #define MaxSector (~(sector_t)0)
 
 enum md_submodule_type {
-- 
2.39.2


^ permalink raw reply related

* [RFC v2 1/5] md: add helpers for requested sync action
From: Zheng Qixing @ 2026-02-03  6:12 UTC (permalink / raw)
  To: song, yukuai, linan122
  Cc: xni, linux-raid, linux-kernel, yi.zhang, yangerkun, houtao1,
	zhengqixing
In-Reply-To: <20260203061259.609206-1-zhengqixing@huaweicloud.com>

From: Zheng Qixing <zhengqixing@huawei.com>

Add helpers for handling requested sync action.

No functional change.

Signed-off-by: Zheng Qixing <zhengqixing@huawei.com>
---
 drivers/md/md.c | 65 ++++++++++++++++++++++++++++++++++---------------
 1 file changed, 46 insertions(+), 19 deletions(-)

diff --git a/drivers/md/md.c b/drivers/md/md.c
index 5df2220b1bd1..84af578876e2 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -665,6 +665,41 @@ void mddev_put(struct mddev *mddev)
 	spin_unlock(&all_mddevs_lock);
 }
 
+static int handle_requested_sync_action(struct mddev *mddev,
+					enum sync_action action)
+{
+	switch (action) {
+	case ACTION_CHECK:
+		set_bit(MD_RECOVERY_CHECK, &mddev->recovery);
+		fallthrough;
+	case ACTION_REPAIR:
+		set_bit(MD_RECOVERY_REQUESTED, &mddev->recovery);
+		set_bit(MD_RECOVERY_SYNC, &mddev->recovery);
+		return 0;
+	default:
+		return -EINVAL;
+	}
+}
+
+static enum sync_action get_recovery_sync_action(struct mddev *mddev)
+{
+	if (test_bit(MD_RECOVERY_CHECK, &mddev->recovery))
+		return ACTION_CHECK;
+	if (test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery))
+		return ACTION_REPAIR;
+	return ACTION_RESYNC;
+}
+
+static void set_requested_position(struct mddev *mddev, sector_t value)
+{
+	mddev->resync_min = value;
+}
+
+static sector_t get_requested_position(struct mddev *mddev)
+{
+	return mddev->resync_min;
+}
+
 static void md_safemode_timeout(struct timer_list *t);
 static void md_start_sync(struct work_struct *ws);
 
@@ -5101,17 +5136,9 @@ enum sync_action md_sync_action(struct mddev *mddev)
 	if (test_bit(MD_RECOVERY_RECOVER, &recovery))
 		return ACTION_RECOVER;
 
-	if (test_bit(MD_RECOVERY_SYNC, &recovery)) {
-		/*
-		 * MD_RECOVERY_CHECK must be paired with
-		 * MD_RECOVERY_REQUESTED.
-		 */
-		if (test_bit(MD_RECOVERY_CHECK, &recovery))
-			return ACTION_CHECK;
-		if (test_bit(MD_RECOVERY_REQUESTED, &recovery))
-			return ACTION_REPAIR;
-		return ACTION_RESYNC;
-	}
+	/* MD_RECOVERY_CHECK must be paired with MD_RECOVERY_REQUESTED. */
+	if (test_bit(MD_RECOVERY_SYNC, &recovery))
+		return get_recovery_sync_action(mddev);
 
 	/*
 	 * MD_RECOVERY_NEEDED or MD_RECOVERY_RUNNING is set, however, no
@@ -5300,11 +5327,10 @@ action_store(struct mddev *mddev, const char *page, size_t len)
 			set_bit(MD_RECOVERY_RECOVER, &mddev->recovery);
 			break;
 		case ACTION_CHECK:
-			set_bit(MD_RECOVERY_CHECK, &mddev->recovery);
-			fallthrough;
 		case ACTION_REPAIR:
-			set_bit(MD_RECOVERY_REQUESTED, &mddev->recovery);
-			set_bit(MD_RECOVERY_SYNC, &mddev->recovery);
+			ret = handle_requested_sync_action(mddev, action);
+			if (ret)
+				goto out;
 			fallthrough;
 		case ACTION_RESYNC:
 		case ACTION_IDLE:
@@ -9370,7 +9396,7 @@ static sector_t md_sync_position(struct mddev *mddev, enum sync_action action)
 	switch (action) {
 	case ACTION_CHECK:
 	case ACTION_REPAIR:
-		return mddev->resync_min;
+		return get_requested_position(mddev);
 	case ACTION_RESYNC:
 		if (!mddev->bitmap)
 			return mddev->resync_offset;
@@ -9795,10 +9821,11 @@ void md_do_sync(struct md_thread *thread)
 	if (!test_bit(MD_RECOVERY_INTR, &mddev->recovery)) {
 		/* We completed so min/max setting can be forgotten if used. */
 		if (test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery))
-			mddev->resync_min = 0;
+			set_requested_position(mddev, 0);
 		mddev->resync_max = MaxSector;
-	} else if (test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery))
-		mddev->resync_min = mddev->curr_resync_completed;
+	} else if (test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery)) {
+		set_requested_position(mddev, mddev->curr_resync_completed);
+	}
 	set_bit(MD_RECOVERY_DONE, &mddev->recovery);
 	mddev->curr_resync = MD_RESYNC_NONE;
 	spin_unlock(&mddev->lock);
-- 
2.39.2


^ permalink raw reply related

* Re: [GIT PULL] md-7.0-20260202
From: Jens Axboe @ 2026-02-02 14:04 UTC (permalink / raw)
  To: Yu Kuai; +Cc: Song Liu, linux-raid, linux-block, Li Nan, Xiao Ni, Zilin Guan
In-Reply-To: <20260202124236.4065531-1-yukuai@fnnas.com>

On 2/2/26 5:42 AM, Yu Kuai wrote:
> Hi Jens,
> 
> Please consider pulling the following changes into your for-7.0/block
> branch.
> 
> This pull request contains:
> 
> Bug Fixes:
> - Fix return value of mddev_trylock (Xiao Ni)
> - Fix memory leak in raid1_run() (Zilin Guan)
> 
> Maintainers:
> - Add Li Nan as mdraid reviewer (Li Nan)

Pulled, thanks.

-- 
Jens Axboe


^ permalink raw reply

* [GIT PULL] md-7.0-20260202
From: Yu Kuai @ 2026-02-02 12:42 UTC (permalink / raw)
  To: Jens Axboe; +Cc: Song Liu, linux-raid, linux-block, Li Nan, Xiao Ni, Zilin Guan

Hi Jens,

Please consider pulling the following changes into your for-7.0/block
branch.

This pull request contains:

Bug Fixes:
- Fix return value of mddev_trylock (Xiao Ni)
- Fix memory leak in raid1_run() (Zilin Guan)

Maintainers:
- Add Li Nan as mdraid reviewer (Li Nan)

Thanks,
Kuai

The following changes since commit 5314d25afbc44d0449fa2519d2c9d7f3c319f74c:

  selftests: ublk: improve I/O ordering test with bpftrace (2026-01-31 14:56:28 -0700)

are available in the Git repository at:

  git://git.kernel.org/pub/scm/linux/kernel/git/mdraid/linux.git tags/md-7.0-20260202

for you to fetch changes up to b36844f7d11e1f322d1e48a828c5bfb4a0ecabd1:

  MAINTAINERS: Add Li Nan as md/raid reviewer (2026-02-02 20:31:40 +0800)

----------------------------------------------------------------
Li Nan (1):
      MAINTAINERS: Add Li Nan as md/raid reviewer

Xiao Ni (1):
      md: fix return value of mddev_trylock

Zilin Guan (1):
      md/raid1: fix memory leak in raid1_run()

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


^ permalink raw reply

* [PATCH RESEND] MAINTAINERS: Add Li Nan as md/raid reviewer
From: linan666 @ 2026-02-02  8:32 UTC (permalink / raw)
  To: song, yukuai
  Cc: linux-raid, linux-kernel, linan666, yangerkun, yi.zhang,
	wanghai38

From: Li Nan <linan122@huawei.com>

I've long contributed to and reviewed the md/raid subsystem. I've fixed
many bugs and done code refactors, with dozens of patches merged.
I now volunteer to work as a reviewer for this subsystem.

Signed-off-by: Li Nan <linan122@huawei.com>
---
 MAINTAINERS | 1 +
 1 file changed, 1 insertion(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 67db88b04537..3262c74b93b4 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -24260,6 +24260,7 @@ F:	include/linux/property.h
 SOFTWARE RAID (Multiple Disks) SUPPORT
 M:	Song Liu <song@kernel.org>
 M:	Yu Kuai <yukuai@fnnas.com>
+R:	Li Nan <linan122@huawei.com>
 L:	linux-raid@vger.kernel.org
 S:	Supported
 Q:	https://patchwork.kernel.org/project/linux-raid/list/
-- 
2.39.2


^ permalink raw reply related

* Re: [PATCH] md/raid1: fix memory leak in raid1_run()
From: Li Nan @ 2026-01-31  1:45 UTC (permalink / raw)
  To: Zilin Guan, song; +Cc: yukuai, linux-raid, linux-kernel, jianhao.xu
In-Reply-To: <20260126071533.606263-1-zilin@seu.edu.cn>



在 2026/1/26 15:15, Zilin Guan 写道:
> raid1_run() calls setup_conf() which registers a thread via
> md_register_thread(). If raid1_set_limits() fails, the previously
> registered thread is not unregistered, resulting in a memory leak
> of the md_thread structure and the thread resource itself.
> 
> Add md_unregister_thread() to the error path to properly cleanup
> the thread, which aligns with the error handling logic of other paths
> in this function.
> 
> Compile tested only. Issue found using a prototype static analysis tool
> and code review.
> 
> Fixes: 97894f7d3c29 ("md/raid1: use the atomic queue limit update APIs")
> Signed-off-by: Zilin Guan <zilin@seu.edu.cn>
> ---
>   drivers/md/raid1.c | 1 +
>   1 file changed, 1 insertion(+)
> 
> diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
> index 57d50465eed1..cc9914bd15c1 100644
> --- a/drivers/md/raid1.c
> +++ b/drivers/md/raid1.c
> @@ -3254,6 +3254,7 @@ static int raid1_run(struct mddev *mddev)
>   	if (!mddev_is_dm(mddev)) {
>   		ret = raid1_set_limits(mddev);
>   		if (ret) {
> +			md_unregister_thread(mddev, &conf->thread);
>   			if (!mddev->private)
>   				raid1_free(mddev, conf);
>   			return ret;

LGTM

Reviewed-by: Li Nan <linan122@huawei.com>

-- 
Thanks,
Nan


^ permalink raw reply

* Re: [PATCH] MAINTAINERS: Add Li Nan as md/raid reviewer
From: Li Nan @ 2026-01-30  7:55 UTC (permalink / raw)
  To: linan666, song, yukuai
  Cc: linux-kernel, yangerkun, yi.zhang, Linux RAID Mailing List
In-Reply-To: <20260129130056.1014405-1-linan666@huaweicloud.com>

Sorry for missing cc to linux-raid.

On 2026/1/29 21:00, linan666@huaweicloud.com wrote:
> From: Li Nan <linan122@huawei.com>
> 
> I've long contributed to and reviewed the md/raid subsystem. I've fixed
> numerous bugs and done code refactors, with dozens of patches merged.
> I now volunteer to work as a reviewer for this subsystem.
> 
> Signed-off-by: Li Nan <linan122@huawei.com>
> ---
>   MAINTAINERS | 1 +
>   1 file changed, 1 insertion(+)
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 67db88b04537..3262c74b93b4 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -24260,6 +24260,7 @@ F:	include/linux/property.h
>   SOFTWARE RAID (Multiple Disks) SUPPORT
>   M:	Song Liu <song@kernel.org>
>   M:	Yu Kuai <yukuai@fnnas.com>
> +R:	Li Nan <linan122@huawei.com>
>   L:	linux-raid@vger.kernel.org
>   S:	Supported
>   Q:	https://patchwork.kernel.org/project/linux-raid/list/

-- 
Thanks,
Nan


^ permalink raw reply

* Re: [PATCH 1/3] lib/raid6: Divide the raid6 algorithm selection process into two parts
From: Li Nan @ 2026-01-29  3:26 UTC (permalink / raw)
  To: sunliming, song, yukuai; +Cc: linux-raid, linux-kernel, sunliming
In-Reply-To: <20260128104923.338443-2-sunliming@linux.dev>



在 2026/1/28 18:49, 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 | 76 +++++++++++++++++++++++++++++++----------------
>   1 file changed, 51 insertions(+), 25 deletions(-)
> 
> diff --git a/lib/raid6/algos.c b/lib/raid6/algos.c
> index 799e0e5eac26..ac6a77b0ae1d 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,44 @@ 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 const struct raid6_calls *raid6_choose_gen_fast(void)
> +{
> +	const struct raid6_calls *const *algo;
> +	const struct raid6_calls *best = NULL;
> +
> +	for (algo = raid6_algos; *algo; algo++) {
> +		if ((*algo)->valid && !(*algo)->valid())
> +			continue;
This logic is odd. We should select the highest-priority algorithm
instead of the first one. This was introduced by commit be85f93ae2df
("lib/raid6: add option to skip algorithm benchmarking").

After fix, this logic is identical to raid6_choose_recov(). Could we reuse
it after renaming the function?


> +
> +		best = *algo;
> +		break;
> +	}
> +
> +	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");
> +	}
> +
> +	return best;
> +}
> +
> +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 +220,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)) >>
> @@ -235,16 +249,11 @@ static inline const struct raid6_calls *raid6_choose_gen(
>   	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(const struct raid6_calls **gen_best)
>   {
>   	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;
> @@ -269,14 +278,31 @@ int __init raid6_select_algo(void)
>   	if ((disks - 2) * PAGE_SIZE % 65536)
>   		memcpy(p, raid6_gfmul, (disks - 2) * PAGE_SIZE % 65536);
>   
> -	/* select raid gen_syndrome function */
> -	gen_best = raid6_choose_gen(&dptrs, disks);
> +	*gen_best = raid6_gen_benchmark(&dptrs, disks);
> +
> +	free_pages((unsigned long)disk_ptr, RAID6_TEST_DISKS_ORDER);
> +
> +	return 0;
> +}
> +
> +int __init raid6_select_algo(void)
> +{
> +	int ret;
> +	const struct raid6_calls *gen_best = NULL;
> +	const struct raid6_recov_calls *rec_best = NULL;
> +
> +	/* select raid gen_syndrome functions */
> +	if (!IS_ENABLED(CONFIG_RAID6_PQ_BENCHMARK))
> +		gen_best = raid6_choose_gen_fast();
> +	else {
> +		ret = raid6_choose_gen_benmark(&gen_best);

'gen_best' is meaningless, use 'ret' directly. The input parameter of 
raid6_choose_gen_benchmark() can be modified to void.

> +		if (ret < 0)
> +			return ret;
> +	}
>   
>   	/* select raid recover functions */
>   	rec_best = raid6_choose_recov();
>   
> -	free_pages((unsigned long)disk_ptr, RAID6_TEST_DISKS_ORDER);
> -
>   	return gen_best && rec_best ? 0 : -EINVAL;
>   }
>   

-- 
Thanks,
Nan


^ permalink raw reply

* [PATCH 3/3] lib/raid6: Delete the RAID6_PQ_BENCHMARK config
From: sunliming @ 2026-01-28 10:49 UTC (permalink / raw)
  To: song, yukuai; +Cc: linux-raid, linux-kernel, sunliming
In-Reply-To: <20260128104923.338443-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>
---
 lib/Kconfig | 8 --------
 1 file changed, 8 deletions(-)

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 2/3] lib/raid6: Optimizing the raid6_select_algo time through asynchronous processing
From: sunliming @ 2026-01-28 10:49 UTC (permalink / raw)
  To: song, yukuai; +Cc: linux-raid, linux-kernel, sunliming
In-Reply-To: <20260128104923.338443-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 | 44 ++++++++++++++++++++++++--------------------
 1 file changed, 24 insertions(+), 20 deletions(-)

diff --git a/lib/raid6/algos.c b/lib/raid6/algos.c
index ac6a77b0ae1d..c92168d59df2 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>
@@ -168,7 +169,7 @@ static inline const struct raid6_calls *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");
@@ -177,7 +178,7 @@ static inline const struct raid6_calls *raid6_choose_gen_fast(void)
 	return best;
 }
 
-static inline const struct raid6_calls *raid6_gen_benchmark(
+static inline void raid6_gen_benchmark(
 		void *(*const dptrs)[RAID6_TEST_DISKS], const int disks)
 {
 	unsigned long perf, bestgenperf, j0, j1;
@@ -214,12 +215,11 @@ static inline const struct raid6_calls *raid6_gen_benchmark(
 	}
 
 	if (!best) {
-		pr_err("raid6: Yikes! No algorithm found!\n");
-		goto out;
+		pr_err("raid6: async benchmark failed to find any algorithm\n");
+		return;
 	}
 
 	raid6_call = *best;
-
 	pr_info("raid6: using algorithm %s gen() %ld MB/s\n",
 		best->name,
 		(bestgenperf * HZ * (disks - 2)) >>
@@ -244,14 +244,11 @@ static inline const struct raid6_calls *raid6_gen_benchmark(
 			(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 */
-static int raid6_choose_gen_benmark(const struct raid6_calls **gen_best)
+static int raid6_choose_gen_benmark(void)
 {
 	const int disks = RAID6_TEST_DISKS;
 	char *disk_ptr, *p;
@@ -278,32 +275,39 @@ static int raid6_choose_gen_benmark(const struct raid6_calls **gen_best)
 	if ((disks - 2) * PAGE_SIZE % 65536)
 		memcpy(p, raid6_gfmul, (disks - 2) * PAGE_SIZE % 65536);
 
-	*gen_best = raid6_gen_benchmark(&dptrs, disks);
+	raid6_gen_benchmark(&dptrs, disks);
 
 	free_pages((unsigned long)disk_ptr, RAID6_TEST_DISKS_ORDER);
 
 	return 0;
 }
 
+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;
 	const struct raid6_calls *gen_best = NULL;
 	const struct raid6_recov_calls *rec_best = NULL;
 
-	/* select raid gen_syndrome functions */
-	if (!IS_ENABLED(CONFIG_RAID6_PQ_BENCHMARK))
-		gen_best = raid6_choose_gen_fast();
-	else {
-		ret = raid6_choose_gen_benmark(&gen_best);
-		if (ret < 0)
-			return ret;
-	}
+	/* phase 1: synchronous fast selection generation algorithm */
+	gen_best = raid6_choose_gen_fast();
 
 	/* select raid recover functions */
 	rec_best = raid6_choose_recov();
 
-	return gen_best && rec_best ? 0 : -EINVAL;
+	if (!gen_best || !rec_best)
+		return -EINVAL;
+
+	/* phase 2: asynchronous performance benchmarking */
+	INIT_WORK(&raid6_benchmark_work, benchmark_work_func);
+	schedule_work(&raid6_benchmark_work);
+
+	return 0;
 }
 
 static void raid6_exit(void)
-- 
2.25.1


^ permalink raw reply related

* [PATCH 0/3] lib/raid6: Optimize raid6_select_algo while ensuring
From: sunliming @ 2026-01-28 10:49 UTC (permalink / raw)
  To: song, yukuai; +Cc: linux-raid, linux-kernel, 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.

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

 lib/Kconfig       |  8 -----
 lib/raid6/algos.c | 92 +++++++++++++++++++++++++++++++----------------
 2 files changed, 61 insertions(+), 39 deletions(-)

-- 
2.25.1


^ permalink raw reply

* [PATCH 1/3] lib/raid6: Divide the raid6 algorithm selection process into two parts
From: sunliming @ 2026-01-28 10:49 UTC (permalink / raw)
  To: song, yukuai; +Cc: linux-raid, linux-kernel, sunliming
In-Reply-To: <20260128104923.338443-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 | 76 +++++++++++++++++++++++++++++++----------------
 1 file changed, 51 insertions(+), 25 deletions(-)

diff --git a/lib/raid6/algos.c b/lib/raid6/algos.c
index 799e0e5eac26..ac6a77b0ae1d 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,44 @@ 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 const struct raid6_calls *raid6_choose_gen_fast(void)
+{
+	const struct raid6_calls *const *algo;
+	const struct raid6_calls *best = NULL;
+
+	for (algo = raid6_algos; *algo; algo++) {
+		if ((*algo)->valid && !(*algo)->valid())
+			continue;
+
+		best = *algo;
+		break;
+	}
+
+	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");
+	}
+
+	return best;
+}
+
+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 +220,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)) >>
@@ -235,16 +249,11 @@ static inline const struct raid6_calls *raid6_choose_gen(
 	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(const struct raid6_calls **gen_best)
 {
 	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;
@@ -269,14 +278,31 @@ int __init raid6_select_algo(void)
 	if ((disks - 2) * PAGE_SIZE % 65536)
 		memcpy(p, raid6_gfmul, (disks - 2) * PAGE_SIZE % 65536);
 
-	/* select raid gen_syndrome function */
-	gen_best = raid6_choose_gen(&dptrs, disks);
+	*gen_best = raid6_gen_benchmark(&dptrs, disks);
+
+	free_pages((unsigned long)disk_ptr, RAID6_TEST_DISKS_ORDER);
+
+	return 0;
+}
+
+int __init raid6_select_algo(void)
+{
+	int ret;
+	const struct raid6_calls *gen_best = NULL;
+	const struct raid6_recov_calls *rec_best = NULL;
+
+	/* select raid gen_syndrome functions */
+	if (!IS_ENABLED(CONFIG_RAID6_PQ_BENCHMARK))
+		gen_best = raid6_choose_gen_fast();
+	else {
+		ret = raid6_choose_gen_benmark(&gen_best);
+		if (ret < 0)
+			return ret;
+	}
 
 	/* select raid recover functions */
 	rec_best = raid6_choose_recov();
 
-	free_pages((unsigned long)disk_ptr, RAID6_TEST_DISKS_ORDER);
-
 	return gen_best && rec_best ? 0 : -EINVAL;
 }
 
-- 
2.25.1


^ permalink raw reply related

* Re: md: regression: incorrect warn about updating super block failure
From: Yu Kuai @ 2026-01-28  9:30 UTC (permalink / raw)
  To: Tj; +Cc: linux-kernel, linux-raid, yukuai
In-Reply-To: <20260128082430.96788-1-tj.iam.tj@proton.me>

Hi,

在 2026/1/28 16:25, Tj 写道:
> Commit 8c9e376b9d1a222 introduces an error message that is incorrectly reported for LVM raid volumes:
>
> $ journalctl --dmesg --priority err
>   md_update_sb: can't update sb for read-only array mdX
>   md_update_sb: can't update sb for read-only array mdX
>   md_update_sb: can't update sb for read-only array mdX
>   md_update_sb: can't update sb for read-only array mdX
>   md_update_sb: can't update sb for read-only array mdX
>   md_update_sb: can't update sb for read-only array mdX
>   md_update_sb: can't update sb for read-only array mdX
>   md_update_sb: can't update sb for read-only array mdX
>   md_update_sb: can't update sb for read-only array mdX
>   md_update_sb: can't update sb for read-only array mdX
>   md_update_sb: can't update sb for read-only array mdX
>   md_update_sb: can't update sb for read-only array mdX
>   md_update_sb: can't update sb for read-only array mdX
>   md_update_sb: can't update sb for read-only array mdX
>   md_update_sb: can't update sb for read-only array mdX
>   md_update_sb: can't update sb for read-only array mdX
Yeah, we should bypass dmraid, will fix it soon.
>
> $ sudo dmsetup table --target raid
> SUNNY-Documents: 0 20971520 raid raid1 3 0 region_size 4096 2 254:74 254:75 254:76 254:77
> SUNNY-Media: 0 171966464 raid raid1 3 0 region_size 4096 2 254:69 254:70 254:71 254:72
> SUNNY-NAS: 0 46874624 raid raid1 3 0 region_size 4096 2 254:15 254:16 254:17 254:18
> SUNNY-Projects: 0 23068672 raid raid1 3 0 region_size 4096 2 254:20 254:21 254:22 254:23
> SUNNY-SourceCode_wcorig: 0 478150656 raid raid1 3 0 region_size 4096 2 254:26 254:27 254:28 254:29
> SUNNY-WINE: 0 16777216 raid raid1 3 0 region_size 4096 2 254:80 254:81 254:82 254:83
> SUNNY-etc: 0 106496 raid raid1 3 0 region_size 4096 2 254:57 254:58 254:59 254:60
> SUNNY-home_wcorig: 0 301989888 raid raid1 3 0 region_size 4096 2 254:46 254:47 254:48 254:49
> SUNNY-l8r: 0 16777216 raid raid1 3 0 region_size 4096 2 254:37 254:38 254:39 254:40
> SUNNY-machines_old: 0 34185216 raid raid1 3 0 region_size 4096 2 254:32 254:33 254:34 254:35
> SUNNY-opt: 0 41943040 raid raid1 3 0 region_size 4096 2 254:64 254:65 254:66 254:67
> SUNNY-rootfs: 0 67108864 raid raid1 3 0 region_size 4096 2 254:3 254:4 254:5 254:6
> SUNNY-usr_local: 0 16777216 raid raid1 3 0 region_size 4096 2 254:52 254:53 254:54 254:55
> SUNNY-var_wcorig: 0 25165824 raid raid1 3 0 region_size 4096 2 254:9 254:10 254:11 254:12
>
>
-- 
Thansk,
Kuai

^ permalink raw reply

* md: regression: incorrect warn about updating super block failure
From: Tj @ 2026-01-28  8:25 UTC (permalink / raw)
  To: yukuai; +Cc: linux-kernel, linux-raid

Commit 8c9e376b9d1a222 introduces an error message that is incorrectly reported for LVM raid volumes:

$ journalctl --dmesg --priority err
 md_update_sb: can't update sb for read-only array mdX
 md_update_sb: can't update sb for read-only array mdX
 md_update_sb: can't update sb for read-only array mdX
 md_update_sb: can't update sb for read-only array mdX
 md_update_sb: can't update sb for read-only array mdX
 md_update_sb: can't update sb for read-only array mdX
 md_update_sb: can't update sb for read-only array mdX
 md_update_sb: can't update sb for read-only array mdX
 md_update_sb: can't update sb for read-only array mdX
 md_update_sb: can't update sb for read-only array mdX
 md_update_sb: can't update sb for read-only array mdX
 md_update_sb: can't update sb for read-only array mdX
 md_update_sb: can't update sb for read-only array mdX
 md_update_sb: can't update sb for read-only array mdX
 md_update_sb: can't update sb for read-only array mdX
 md_update_sb: can't update sb for read-only array mdX

$ sudo dmsetup table --target raid
SUNNY-Documents: 0 20971520 raid raid1 3 0 region_size 4096 2 254:74 254:75 254:76 254:77
SUNNY-Media: 0 171966464 raid raid1 3 0 region_size 4096 2 254:69 254:70 254:71 254:72
SUNNY-NAS: 0 46874624 raid raid1 3 0 region_size 4096 2 254:15 254:16 254:17 254:18
SUNNY-Projects: 0 23068672 raid raid1 3 0 region_size 4096 2 254:20 254:21 254:22 254:23
SUNNY-SourceCode_wcorig: 0 478150656 raid raid1 3 0 region_size 4096 2 254:26 254:27 254:28 254:29
SUNNY-WINE: 0 16777216 raid raid1 3 0 region_size 4096 2 254:80 254:81 254:82 254:83
SUNNY-etc: 0 106496 raid raid1 3 0 region_size 4096 2 254:57 254:58 254:59 254:60
SUNNY-home_wcorig: 0 301989888 raid raid1 3 0 region_size 4096 2 254:46 254:47 254:48 254:49
SUNNY-l8r: 0 16777216 raid raid1 3 0 region_size 4096 2 254:37 254:38 254:39 254:40
SUNNY-machines_old: 0 34185216 raid raid1 3 0 region_size 4096 2 254:32 254:33 254:34 254:35
SUNNY-opt: 0 41943040 raid raid1 3 0 region_size 4096 2 254:64 254:65 254:66 254:67
SUNNY-rootfs: 0 67108864 raid raid1 3 0 region_size 4096 2 254:3 254:4 254:5 254:6
SUNNY-usr_local: 0 16777216 raid raid1 3 0 region_size 4096 2 254:52 254:53 254:54 254:55
SUNNY-var_wcorig: 0 25165824 raid raid1 3 0 region_size 4096 2 254:9 254:10 254:11 254:12


^ permalink raw reply

* Re: [PATCH 1/1] md: fix return value of mddev_trylock
From: Li Nan @ 2026-01-28  8:24 UTC (permalink / raw)
  To: Xiao Ni, yukuai; +Cc: linux-raid, bvanassche
In-Reply-To: <20260127073951.17248-1-xni@redhat.com>



在 2026/1/27 15:39, Xiao Ni 写道:
> A return value of 0 is treaded as successful lock acquisition. In fact, a
> return value of 1 means getting the lock successfully.
> 
> Fixes: 9e59d609763f ("md: call del_gendisk in control path")
> Reported-by: Bart Van Assche <bvanassche@acm.org>
> Closes: https://lore.kernel.org/linux-raid/20250611073108.25463-1-xni@redhat.com/T/#mfa369ef5faa4aa58e13e6d9fdb88aecd862b8f2f
> Signed-off-by: Xiao Ni <xni@redhat.com>
> ---
>   drivers/md/md.h | 4 ++--
>   1 file changed, 2 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/md/md.h b/drivers/md/md.h
> index 6985f2829bbd..3bfbee595156 100644
> --- a/drivers/md/md.h
> +++ b/drivers/md/md.h
> @@ -737,8 +737,8 @@ static inline int mddev_trylock(struct mddev *mddev)
>   	int ret;
>   
>   	ret = mutex_trylock(&mddev->reconfig_mutex);
> -	if (!ret && test_bit(MD_DELETED, &mddev->flags)) {
> -		ret = -ENODEV;
> +	if (ret && test_bit(MD_DELETED, &mddev->flags)) {
> +		ret = 0;
>   		mutex_unlock(&mddev->reconfig_mutex);
>   	}
>   	return ret;

LGTM

Reviewed-by:  Li Nan <linan122@huawei.com>

-- 
Thanks,
Nan


^ permalink raw reply

* [PATCH v2 12/14] md/raid1: clean up sync IO size calculation in raid1_sync_request
From: linan666 @ 2026-01-28  7:57 UTC (permalink / raw)
  To: song, yukuai; +Cc: xni, linux-raid, linux-kernel, linan666, yangerkun, yi.zhang
In-Reply-To: <20260128075708.2259525-1-linan666@huaweicloud.com>

From: Li Nan <linan122@huawei.com>

Use 'nr_sectors' directly for sync IO size calculation. Prepare folio
allocation failure fallback.

No functional changes.

Signed-off-by: Li Nan <linan122@huawei.com>
---
 drivers/md/raid1.c | 17 ++++++-----------
 1 file changed, 6 insertions(+), 11 deletions(-)

diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
index b5239b5cb4e9..2253e65c5f03 100644
--- a/drivers/md/raid1.c
+++ b/drivers/md/raid1.c
@@ -2979,32 +2979,27 @@ static sector_t raid1_sync_request(struct mddev *mddev, sector_t sector_nr,
 		max_sector = mddev->resync_max; /* Don't do IO beyond here */
 	if (max_sector > sector_nr + good_sectors)
 		max_sector = sector_nr + good_sectors;
-	nr_sectors = 0;
 	do {
-		int len = RESYNC_BLOCK_SIZE;
-
-		if (sector_nr + (len>>9) > max_sector)
-			len = (max_sector - sector_nr) << 9;
-		if (len == 0)
+		nr_sectors = max_sector - sector_nr;
+		if (nr_sectors == 0)
 			break;
 		if (!md_bitmap_start_sync(mddev, sector_nr,
 					  &sync_blocks, still_degraded) &&
 		    !conf->fullsync &&
 		    !test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery))
 			break;
-		if ((len >> 9) > sync_blocks)
-			len = sync_blocks<<9;
+		if (nr_sectors > sync_blocks)
+			nr_sectors = sync_blocks;
 
 		for (i = 0 ; i < conf->raid_disks * 2; i++) {
 			bio = r1_bio->bios[i];
 			if (bio->bi_end_io) {
 				struct resync_folio *rf = get_resync_folio(bio);
 
-				bio_add_folio_nofail(bio, rf->folio, len, 0);
+				bio_add_folio_nofail(bio, rf->folio, nr_sectors << 9, 0);
 			}
 		}
-		nr_sectors += len>>9;
-		sector_nr += len>>9;
+		sector_nr += nr_sectors;
 	} while (0);
 
 	r1_bio->sectors = nr_sectors;
-- 
2.39.2


^ permalink raw reply related

* [PATCH v2 14/14] md/raid1,raid10: fall back to smaller order if sync folio alloc fails
From: linan666 @ 2026-01-28  7:57 UTC (permalink / raw)
  To: song, yukuai; +Cc: xni, linux-raid, linux-kernel, linan666, yangerkun, yi.zhang
In-Reply-To: <20260128075708.2259525-1-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(-)

diff --git a/drivers/md/raid1-10.c b/drivers/md/raid1-10.c
index ffbd7bd0f6e8..e966d11a81e7 100644
--- a/drivers/md/raid1-10.c
+++ b/drivers/md/raid1-10.c
@@ -41,12 +41,20 @@ static void rbio_pool_free(void *rbio, void *data)
 }
 
 static inline int resync_alloc_folio(struct resync_folio *rf,
-				     gfp_t gfp_flags)
+				     gfp_t gfp_flags, int *order)
 {
-	rf->folio = folio_alloc(gfp_flags, get_order(RESYNC_BLOCK_SIZE));
-	if (!rf->folio)
+	struct folio *folio;
+
+	do {
+		folio = folio_alloc(gfp_flags, *order);
+		if (folio)
+			break;
+	} while (--(*order) > 0);
+
+	if (!folio)
 		return -ENOMEM;
 
+	rf->folio = folio;
 	return 0;
 }
 
diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
index 2253e65c5f03..5bee846f1534 100644
--- a/drivers/md/raid1.c
+++ b/drivers/md/raid1.c
@@ -149,6 +149,7 @@ static void * r1buf_pool_alloc(gfp_t gfp_flags, void *data)
 	int need_folio;
 	int j;
 	struct resync_folio *rfs;
+	int order = get_order(RESYNC_BLOCK_SIZE);
 
 	r1_bio = r1bio_pool_alloc(gfp_flags, conf);
 	if (!r1_bio)
@@ -182,7 +183,7 @@ static void * r1buf_pool_alloc(gfp_t gfp_flags, void *data)
 		struct resync_folio *rf = &rfs[j];
 
 		if (j < need_folio) {
-			if (resync_alloc_folio(rf, gfp_flags))
+			if (resync_alloc_folio(rf, gfp_flags, &order))
 				goto out_free_folio;
 		} else {
 			memcpy(rf, &rfs[0], sizeof(*rf));
@@ -193,6 +194,7 @@ static void * r1buf_pool_alloc(gfp_t gfp_flags, void *data)
 		r1_bio->bios[j]->bi_private = rf;
 	}
 
+	r1_bio->sectors = 1 << (order + PAGE_SECTORS_SHIFT);
 	r1_bio->master_bio = NULL;
 
 	return r1_bio;
@@ -2776,7 +2778,7 @@ static sector_t raid1_sync_request(struct mddev *mddev, sector_t sector_nr,
 	int write_targets = 0, read_targets = 0;
 	sector_t sync_blocks;
 	bool still_degraded = false;
-	int good_sectors = RESYNC_SECTORS;
+	int good_sectors;
 	int min_bad = 0; /* number of sectors that are bad in all devices */
 	int idx = sector_to_idx(sector_nr);
 
@@ -2858,8 +2860,11 @@ static sector_t raid1_sync_request(struct mddev *mddev, sector_t sector_nr,
 	r1_bio->sector = sector_nr;
 	r1_bio->state = 0;
 	set_bit(R1BIO_IsSync, &r1_bio->state);
-	/* make sure good_sectors won't go across barrier unit boundary */
-	good_sectors = align_to_barrier_unit_end(sector_nr, good_sectors);
+	/*
+	 * make sure good_sectors won't go across barrier unit boundary.
+	 * r1_bio->sectors <= RESYNC_SECTORS.
+	 */
+	good_sectors = align_to_barrier_unit_end(sector_nr, r1_bio->sectors);
 
 	for (i = 0; i < conf->raid_disks * 2; i++) {
 		struct md_rdev *rdev;
diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index 030812f908ac..72c77db9957c 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -135,6 +135,7 @@ static void * r10buf_pool_alloc(gfp_t gfp_flags, void *data)
 	int j;
 	int nalloc, nalloc_rf;
 	struct resync_folio *rfs;
+	int order = get_order(RESYNC_BLOCK_SIZE);
 
 	r10_bio = r10bio_pool_alloc(gfp_flags, conf);
 	if (!r10_bio)
@@ -185,7 +186,7 @@ static void * r10buf_pool_alloc(gfp_t gfp_flags, void *data)
 
 		if (!j || test_bit(MD_RECOVERY_SYNC,
 				   &conf->mddev->recovery)) {
-			if (resync_alloc_folio(rf, gfp_flags))
+			if (resync_alloc_folio(rf, gfp_flags, &order))
 				goto out_free_folio;
 		} else {
 			memcpy(rf, &rfs[0], sizeof(*rf));
@@ -200,6 +201,7 @@ static void * r10buf_pool_alloc(gfp_t gfp_flags, void *data)
 		}
 	}
 
+	r10_bio->sectors = 1 << (order + PAGE_SECTORS_SHIFT);
 	return r10_bio;
 
 out_free_folio:
@@ -3374,6 +3376,15 @@ static sector_t raid10_sync_request(struct mddev *mddev, sector_t sector_nr,
 						continue;
 					}
 				}
+
+				/*
+				 * RESYNC_BLOCK_SIZE folio might alloc failed in
+				 * resync_alloc_folio(). Fall back to smaller sync
+				 * size if needed.
+				 */
+				if (max_sync > r10_bio->sectors)
+					max_sync = r10_bio->sectors;
+
 				any_working = 1;
 				bio = r10_bio->devs[0].bio;
 				bio->bi_next = biolist;
@@ -3525,7 +3536,15 @@ static sector_t raid10_sync_request(struct mddev *mddev, sector_t sector_nr,
 		}
 		if (sync_blocks < max_sync)
 			max_sync = sync_blocks;
+
 		r10_bio = raid10_alloc_init_r10buf(conf);
+		/*
+		 * RESYNC_BLOCK_SIZE folio might alloc failed in resync_alloc_folio().
+		 * Fall back to smaller sync size if needed.
+		 */
+		if (max_sync > r10_bio->sectors)
+			max_sync = r10_bio->sectors;
+
 		r10_bio->state = 0;
 
 		r10_bio->mddev = mddev;
@@ -4702,7 +4721,12 @@ static sector_t reshape_request(struct mddev *mddev, sector_t sector_nr,
 	r10_bio->mddev = mddev;
 	r10_bio->sector = sector_nr;
 	set_bit(R10BIO_IsReshape, &r10_bio->state);
-	r10_bio->sectors = last - sector_nr + 1;
+	/*
+	 * RESYNC_BLOCK_SIZE folio might alloc failed in
+	 * resync_alloc_folio(). Fall back to smaller sync
+	 * size if needed.
+	 */
+	r10_bio->sectors = min_t(int, r10_bio->sectors, last - sector_nr + 1);
 	rdev = read_balance(conf, r10_bio, &max_sectors);
 	BUG_ON(!test_bit(R10BIO_Previous, &r10_bio->state));
 
-- 
2.39.2


^ permalink raw reply related

* [PATCH v2 11/14] md: clean up resync_free_folio
From: linan666 @ 2026-01-28  7:57 UTC (permalink / raw)
  To: song, yukuai; +Cc: xni, linux-raid, linux-kernel, linan666, yangerkun, yi.zhang
In-Reply-To: <20260128075708.2259525-1-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(-)

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);
 		}
-- 
2.39.2


^ permalink raw reply related

* [PATCH v2 13/14] md/raid10: clean up sync IO size calculation in raid10_sync_request
From: linan666 @ 2026-01-28  7:57 UTC (permalink / raw)
  To: song, yukuai; +Cc: xni, linux-raid, linux-kernel, linan666, yangerkun, yi.zhang
In-Reply-To: <20260128075708.2259525-1-linan666@huaweicloud.com>

From: Li Nan <linan122@huawei.com>

Use 'nr_sectors' directly for sync IO size calculation. Prepare folio
allocation failure fallback.

No functional changes.

Signed-off-by: Li Nan <linan122@huawei.com>
---
 drivers/md/raid10.c | 14 +++++---------
 1 file changed, 5 insertions(+), 9 deletions(-)

diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index b728131bdad4..030812f908ac 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -3618,28 +3618,24 @@ static sector_t raid10_sync_request(struct mddev *mddev, sector_t sector_nr,
 		}
 	}
 
-	nr_sectors = 0;
 	if (sector_nr + max_sync < max_sector)
 		max_sector = sector_nr + max_sync;
 	do {
-		int len = RESYNC_BLOCK_SIZE;
+		nr_sectors = max_sector - sector_nr;
 
-		if (sector_nr + (len>>9) > max_sector)
-			len = (max_sector - sector_nr) << 9;
-		if (len == 0)
+		if (nr_sectors == 0)
 			break;
 		for (bio= biolist ; bio ; bio=bio->bi_next) {
 			struct resync_folio *rf = get_resync_folio(bio);
 
-			if (WARN_ON(!bio_add_folio(bio, rf->folio, len, 0))) {
+			if (WARN_ON(!bio_add_folio(bio, rf->folio, nr_sectors << 9, 0))) {
 				bio->bi_status = BLK_STS_RESOURCE;
 				bio_endio(bio);
 				*skipped = 1;
-				return len;
+				return nr_sectors << 9;
 			}
 		}
-		nr_sectors += len>>9;
-		sector_nr += len>>9;
+		sector_nr += nr_sectors;;
 	} while (0);
 	r10_bio->sectors = nr_sectors;
 
-- 
2.39.2


^ permalink raw reply related

* [PATCH v2 08/14] md/raid1: fix IO error at logical block size granularity
From: linan666 @ 2026-01-28  7:57 UTC (permalink / raw)
  To: song, yukuai; +Cc: xni, linux-raid, linux-kernel, linan666, yangerkun, yi.zhang
In-Reply-To: <20260128075708.2259525-1-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(-)

diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
index 2308e16b1280..27e3b2375b16 100644
--- a/drivers/md/raid1.c
+++ b/drivers/md/raid1.c
@@ -2114,7 +2114,7 @@ static int fix_sync_read_error(struct r1bio *r1_bio)
 {
 	/* Try some synchronous reads of other devices to get
 	 * good data, much like with normal read errors.  Only
-	 * read into the pages we already have so we don't
+	 * read into the block we already have so we don't
 	 * need to re-issue the read request.
 	 * We don't need to freeze the array, because being in an
 	 * active sync request, there is no normal IO, and
@@ -2145,13 +2145,11 @@ static int fix_sync_read_error(struct r1bio *r1_bio)
 	}
 
 	while(sectors) {
-		int s = sectors;
+		int s = min_t(int, sectors, mddev->logical_block_size >> 9);
 		int d = r1_bio->read_disk;
 		int success = 0;
 		int start;
 
-		if (s > (PAGE_SIZE>>9))
-			s = PAGE_SIZE >> 9;
 		do {
 			if (r1_bio->bios[d]->bi_end_io == end_sync_read) {
 				/* No rcu protection needed here devices
@@ -2190,7 +2188,7 @@ static int fix_sync_read_error(struct r1bio *r1_bio)
 			if (abort)
 				return 0;
 
-			/* Try next page */
+			/* Try next block */
 			sectors -= s;
 			sect += s;
 			off += s << 9;
@@ -2388,14 +2386,11 @@ static void fix_read_error(struct r1conf *conf, struct r1bio *r1_bio)
 	}
 
 	while(sectors) {
-		int s = sectors;
+		int s = min_t(int, sectors, mddev->logical_block_size >> 9);
 		int d = read_disk;
 		int success = 0;
 		int start;
 
-		if (s > (PAGE_SIZE>>9))
-			s = PAGE_SIZE >> 9;
-
 		do {
 			rdev = conf->mirrors[d].rdev;
 			if (rdev &&
-- 
2.39.2


^ permalink raw reply related

* [PATCH v2 06/14] md: Clean up folio sync support related code
From: linan666 @ 2026-01-28  7:57 UTC (permalink / raw)
  To: song, yukuai; +Cc: xni, linux-raid, linux-kernel, linan666, yangerkun, yi.zhang
In-Reply-To: <20260128075708.2259525-1-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(-)

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;
-- 
2.39.2


^ permalink raw reply related

* [PATCH v2 10/14] md/raid1,raid10: clean up resync_fetch_folio
From: linan666 @ 2026-01-28  7:57 UTC (permalink / raw)
  To: song, yukuai; +Cc: xni, linux-raid, linux-kernel, linan666, yangerkun, yi.zhang
In-Reply-To: <20260128075708.2259525-1-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(-)

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;
-- 
2.39.2


^ 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