Linux RAID subsystem development
 help / color / mirror / Atom feed
* Re: [PATCH] md/raid5: add exact old and new llbitmap mapping helpers
From: kernel test robot @ 2026-05-01 18:51 UTC (permalink / raw)
  To: Yu Kuai, linux-raid
  Cc: llvm, oe-kbuild-all, linux-kernel, Li Nan, Yu Kuai, Cheng Cheng
In-Reply-To: <20260419030942.824195-17-yukuai@fnnas.com>

Hi Yu,

kernel test robot noticed the following build errors:

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

url:    https://github.com/intel-lab-lkp/linux/commits/Yu-Kuai/md-raid5-add-exact-old-and-new-llbitmap-mapping-helpers/20260421-233709
base:   linus/master
patch link:    https://lore.kernel.org/r/20260419030942.824195-17-yukuai%40fnnas.com
patch subject: [PATCH] md/raid5: add exact old and new llbitmap mapping helpers
config: hexagon-allmodconfig (https://download.01.org/0day-ci/archive/20260502/202605020242.1lRKHrkP-lkp@intel.com/config)
compiler: clang version 17.0.6 (https://github.com/llvm/llvm-project 6009708b4367171ccdbf4b5905cb6a803753fe18)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260502/202605020242.1lRKHrkP-lkp@intel.com/reproduce)

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

All errors (new ones prefixed by >>):

>> drivers/md/raid5.c:9065:3: error: field designator 'bitmap_sector_map' does not refer to any field in type 'struct md_personality'; did you mean 'bitmap_sector'?
    9065 |         .bitmap_sector_map = raid5_bitmap_sector_map,
         |          ^~~~~~~~~~~~~~~~~
         |          bitmap_sector
   drivers/md/md.h:797:9: note: 'bitmap_sector' declared here
     797 |         void (*bitmap_sector)(struct mddev *mddev, sector_t *offset,
         |                ^
>> drivers/md/raid5.c:9065:23: error: incompatible function pointer types initializing 'void (*)(struct mddev *, sector_t *, unsigned long *)' (aka 'void (*)(struct mddev *, unsigned long long *, unsigned long *)') with an expression of type 'void (struct mddev *, sector_t *, unsigned long *, bool)' (aka 'void (struct mddev *, unsigned long long *, unsigned long *, _Bool)') [-Wincompatible-function-pointer-types]
    9065 |         .bitmap_sector_map = raid5_bitmap_sector_map,
         |                              ^~~~~~~~~~~~~~~~~~~~~~~
   drivers/md/raid5.c:9065:23: warning: initializer overrides prior initialization of this subobject [-Winitializer-overrides]
    9065 |         .bitmap_sector_map = raid5_bitmap_sector_map,
         |                              ^~~~~~~~~~~~~~~~~~~~~~~
   drivers/md/raid5.c:9064:19: note: previous initialization is here
    9064 |         .bitmap_sector  = raid5_bitmap_sector,
         |                           ^~~~~~~~~~~~~~~~~~~
>> drivers/md/raid5.c:9066:3: error: field designator 'bitmap_sync_size' does not refer to any field in type 'struct md_personality'
    9066 |         .bitmap_sync_size = raid5_bitmap_sync_size,
         |         ~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>> drivers/md/raid5.c:9067:3: error: field designator 'bitmap_array_sectors' does not refer to any field in type 'struct md_personality'
    9067 |         .bitmap_array_sectors = raid5_bitmap_array_sectors,
         |         ~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/md/raid5.c:9098:3: error: field designator 'bitmap_sector_map' does not refer to any field in type 'struct md_personality'; did you mean 'bitmap_sector'?
    9098 |         .bitmap_sector_map = raid5_bitmap_sector_map,
         |          ^~~~~~~~~~~~~~~~~
         |          bitmap_sector
   drivers/md/md.h:797:9: note: 'bitmap_sector' declared here
     797 |         void (*bitmap_sector)(struct mddev *mddev, sector_t *offset,
         |                ^
   drivers/md/raid5.c:9098:23: error: incompatible function pointer types initializing 'void (*)(struct mddev *, sector_t *, unsigned long *)' (aka 'void (*)(struct mddev *, unsigned long long *, unsigned long *)') with an expression of type 'void (struct mddev *, sector_t *, unsigned long *, bool)' (aka 'void (struct mddev *, unsigned long long *, unsigned long *, _Bool)') [-Wincompatible-function-pointer-types]
    9098 |         .bitmap_sector_map = raid5_bitmap_sector_map,
         |                              ^~~~~~~~~~~~~~~~~~~~~~~
   drivers/md/raid5.c:9098:23: warning: initializer overrides prior initialization of this subobject [-Winitializer-overrides]
    9098 |         .bitmap_sector_map = raid5_bitmap_sector_map,
         |                              ^~~~~~~~~~~~~~~~~~~~~~~
   drivers/md/raid5.c:9097:19: note: previous initialization is here
    9097 |         .bitmap_sector  = raid5_bitmap_sector,
         |                           ^~~~~~~~~~~~~~~~~~~
   drivers/md/raid5.c:9099:3: error: field designator 'bitmap_sync_size' does not refer to any field in type 'struct md_personality'
    9099 |         .bitmap_sync_size = raid5_bitmap_sync_size,
         |         ~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/md/raid5.c:9100:3: error: field designator 'bitmap_array_sectors' does not refer to any field in type 'struct md_personality'
    9100 |         .bitmap_array_sectors = raid5_bitmap_array_sectors,
         |         ~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/md/raid5.c:9132:3: error: field designator 'bitmap_sector_map' does not refer to any field in type 'struct md_personality'; did you mean 'bitmap_sector'?
    9132 |         .bitmap_sector_map = raid5_bitmap_sector_map,
         |          ^~~~~~~~~~~~~~~~~
         |          bitmap_sector
   drivers/md/md.h:797:9: note: 'bitmap_sector' declared here
     797 |         void (*bitmap_sector)(struct mddev *mddev, sector_t *offset,
         |                ^
   drivers/md/raid5.c:9132:23: error: incompatible function pointer types initializing 'void (*)(struct mddev *, sector_t *, unsigned long *)' (aka 'void (*)(struct mddev *, unsigned long long *, unsigned long *)') with an expression of type 'void (struct mddev *, sector_t *, unsigned long *, bool)' (aka 'void (struct mddev *, unsigned long long *, unsigned long *, _Bool)') [-Wincompatible-function-pointer-types]
    9132 |         .bitmap_sector_map = raid5_bitmap_sector_map,
         |                              ^~~~~~~~~~~~~~~~~~~~~~~
   drivers/md/raid5.c:9132:23: warning: initializer overrides prior initialization of this subobject [-Winitializer-overrides]
    9132 |         .bitmap_sector_map = raid5_bitmap_sector_map,
         |                              ^~~~~~~~~~~~~~~~~~~~~~~
   drivers/md/raid5.c:9131:19: note: previous initialization is here
    9131 |         .bitmap_sector  = raid5_bitmap_sector,
         |                           ^~~~~~~~~~~~~~~~~~~
   drivers/md/raid5.c:9133:3: error: field designator 'bitmap_sync_size' does not refer to any field in type 'struct md_personality'
    9133 |         .bitmap_sync_size = raid5_bitmap_sync_size,
         |         ~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/md/raid5.c:9134:3: error: field designator 'bitmap_array_sectors' does not refer to any field in type 'struct md_personality'
    9134 |         .bitmap_array_sectors = raid5_bitmap_array_sectors,
         |         ~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   3 warnings and 12 errors generated.


vim +9065 drivers/md/raid5.c

  9035	
  9036	static struct md_personality raid6_personality =
  9037	{
  9038		.head = {
  9039			.type	= MD_PERSONALITY,
  9040			.id	= ID_RAID6,
  9041			.name	= "raid6",
  9042			.owner	= THIS_MODULE,
  9043		},
  9044	
  9045		.make_request	= raid5_make_request,
  9046		.run		= raid5_run,
  9047		.start		= raid5_start,
  9048		.free		= raid5_free,
  9049		.status		= raid5_status,
  9050		.error_handler	= raid5_error,
  9051		.hot_add_disk	= raid5_add_disk,
  9052		.hot_remove_disk= raid5_remove_disk,
  9053		.spare_active	= raid5_spare_active,
  9054		.sync_request	= raid5_sync_request,
  9055		.resize		= raid5_resize,
  9056		.size		= raid5_size,
  9057		.check_reshape	= raid6_check_reshape,
  9058		.start_reshape  = raid5_start_reshape,
  9059		.finish_reshape = raid5_finish_reshape,
  9060		.quiesce	= raid5_quiesce,
  9061		.takeover	= raid6_takeover,
  9062		.change_consistency_policy = raid5_change_consistency_policy,
  9063		.prepare_suspend = raid5_prepare_suspend,
  9064		.bitmap_sector	= raid5_bitmap_sector,
> 9065		.bitmap_sector_map = raid5_bitmap_sector_map,
> 9066		.bitmap_sync_size = raid5_bitmap_sync_size,
> 9067		.bitmap_array_sectors = raid5_bitmap_array_sectors,
  9068	};
  9069	static struct md_personality raid5_personality =
  9070	{
  9071		.head = {
  9072			.type	= MD_PERSONALITY,
  9073			.id	= ID_RAID5,
  9074			.name	= "raid5",
  9075			.owner	= THIS_MODULE,
  9076		},
  9077	
  9078		.make_request	= raid5_make_request,
  9079		.run		= raid5_run,
  9080		.start		= raid5_start,
  9081		.free		= raid5_free,
  9082		.status		= raid5_status,
  9083		.error_handler	= raid5_error,
  9084		.hot_add_disk	= raid5_add_disk,
  9085		.hot_remove_disk= raid5_remove_disk,
  9086		.spare_active	= raid5_spare_active,
  9087		.sync_request	= raid5_sync_request,
  9088		.resize		= raid5_resize,
  9089		.size		= raid5_size,
  9090		.check_reshape	= raid5_check_reshape,
  9091		.start_reshape  = raid5_start_reshape,
  9092		.finish_reshape = raid5_finish_reshape,
  9093		.quiesce	= raid5_quiesce,
  9094		.takeover	= raid5_takeover,
  9095		.change_consistency_policy = raid5_change_consistency_policy,
  9096		.prepare_suspend = raid5_prepare_suspend,
  9097		.bitmap_sector	= raid5_bitmap_sector,
  9098		.bitmap_sector_map = raid5_bitmap_sector_map,
  9099		.bitmap_sync_size = raid5_bitmap_sync_size,
  9100		.bitmap_array_sectors = raid5_bitmap_array_sectors,
  9101	};
  9102	

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

^ permalink raw reply

* [PATCH v2 3/3] md/raid1,raid10: fix bio accounting for split md cloned bios
From: Abd-Alrhman Masalkhi @ 2026-05-01 11:46 UTC (permalink / raw)
  To: song, yukuai, xni, neilb, shli
  Cc: linux-raid, linux-kernel, Abd-Alrhman Masalkhi
In-Reply-To: <20260501114652.590037-1-abd.masalkhi@gmail.com>

Use md_cloned_bio() to control bio accounting instead of relying
on r1bio_existed in raid1 or the io_accounting flag in raid10.

The previous logic does not reliably reflect whether a bio is an
md cloned bio. When a failed bio is split and resubmitted via
bio_submit_split_bioset() on the error path, this can lead to either
double accounting for md cloned bios, or missing accounting for bios
returned from bio_submit_split_bioset()

Fix this by using md_cloned_bio() to detect md cloned bios and
skip accounting accordingly.

Fixes: bb2a9acefaf9 ("md/raid1: switch to use md_account_bio() for io accounting")
Fixes: 820455238366 ("md/raid10: switch to use md_account_bio() for io accounting")
Signed-off-by: Abd-Alrhman Masalkhi <abd.masalkhi@gmail.com>
---
This patch depends on patch 1.

Changes in v2:
 - New patch.
---
 drivers/md/raid1.c  | 2 +-
 drivers/md/raid10.c | 8 ++++----
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
index c52ecd38c163..dfaf34141325 100644
--- a/drivers/md/raid1.c
+++ b/drivers/md/raid1.c
@@ -1396,7 +1396,7 @@ static void raid1_read_request(struct mddev *mddev, struct bio *bio,
 	}
 
 	r1_bio->read_disk = rdisk;
-	if (!r1bio_existed) {
+	if (likely(!md_cloned_bio(mddev, bio))) {
 		md_account_bio(mddev, &bio);
 		r1_bio->master_bio = bio;
 	}
diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index 8c6fc398260e..93af7bbc9005 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -1146,7 +1146,7 @@ static bool regular_request_wait(struct mddev *mddev, struct r10conf *conf,
 }
 
 static void raid10_read_request(struct mddev *mddev, struct bio *bio,
-				struct r10bio *r10_bio, bool io_accounting)
+				struct r10bio *r10_bio)
 {
 	struct r10conf *conf = mddev->private;
 	struct bio *read_bio;
@@ -1226,7 +1226,7 @@ static void raid10_read_request(struct mddev *mddev, struct bio *bio,
 	}
 	slot = r10_bio->read_slot;
 
-	if (io_accounting) {
+	if (likely(!md_cloned_bio(mddev, bio))) {
 		md_account_bio(mddev, &bio);
 		r10_bio->master_bio = bio;
 	}
@@ -1552,7 +1552,7 @@ static void __make_request(struct mddev *mddev, struct bio *bio, int sectors)
 			conf->geo.raid_disks);
 
 	if (bio_data_dir(bio) == READ)
-		raid10_read_request(mddev, bio, r10_bio, true);
+		raid10_read_request(mddev, bio, r10_bio);
 	else
 		raid10_write_request(mddev, bio, r10_bio);
 }
@@ -2872,7 +2872,7 @@ static void handle_read_error(struct mddev *mddev, struct r10bio *r10_bio)
 
 	rdev_dec_pending(rdev, mddev);
 	r10_bio->state = 0;
-	raid10_read_request(mddev, r10_bio->master_bio, r10_bio, false);
+	raid10_read_request(mddev, r10_bio->master_bio, r10_bio);
 	/*
 	 * allow_barrier after re-submit to ensure no sync io
 	 * can be issued while regular io pending.
-- 
2.43.0


^ permalink raw reply related

* [PATCH v2 2/3] md/raid1,raid10: fix error-path detection with md_cloned_bio()
From: Abd-Alrhman Masalkhi @ 2026-05-01 11:46 UTC (permalink / raw)
  To: song, yukuai, xni, neilb, shli
  Cc: linux-raid, linux-kernel, Abd-Alrhman Masalkhi
In-Reply-To: <20260501114652.590037-1-abd.masalkhi@gmail.com>

Detect the error path using md_cloned_bio() instead of relying
on r1_bio in raid1 or r10_bio->read_slot in raid10, which may be
NULL or -1 after splitting and resubmitting a failed bio.

As a result, the error path may not be recognized and memory
allocations can incorrectly use GFP_NOIO instead of
(GFP_NOIO | __GFP_HIGH), which can lead to a deadlock under
memory pressure.

Fixes: 689389a06ce7 ("md/raid1: simplify handle_read_error().")
Fixes: 545250f24809 ("md/raid10: simplify handle_read_error()")
Signed-off-by: Abd-Alrhman Masalkhi <abd.masalkhi@gmail.com>
---
This patch depends on patch 1.

Changes in v2:
 - New patch.
---
 drivers/md/raid1.c  | 13 ++++++++++---
 drivers/md/raid10.c | 20 ++++++++++++++------
 2 files changed, 24 insertions(+), 9 deletions(-)

diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
index cc9914bd15c1..c52ecd38c163 100644
--- a/drivers/md/raid1.c
+++ b/drivers/md/raid1.c
@@ -1321,11 +1321,18 @@ static void raid1_read_request(struct mddev *mddev, struct bio *bio,
 	bool r1bio_existed = !!r1_bio;
 
 	/*
-	 * If r1_bio is set, we are blocking the raid1d thread
-	 * so there is a tiny risk of deadlock.  So ask for
+	 * An md cloned bio indicates we are in the error path.
+	 * This is more reliable than checking r1_bio, which might
+	 * be NULL even in the error path if a failed bio was split.
+	 */
+	bool err_path = md_cloned_bio(mddev, bio);
+
+	/*
+	 * If we are in the error path, we are blocking the raid1d
+	 * thread so there is a tiny risk of deadlock.  So ask for
 	 * emergency memory if needed.
 	 */
-	gfp_t gfp = r1_bio ? (GFP_NOIO | __GFP_HIGH) : GFP_NOIO;
+	gfp_t gfp = err_path ? (GFP_NOIO | __GFP_HIGH) : GFP_NOIO;
 
 	/*
 	 * Still need barrier for READ in case that whole
diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index 3a591e60a144..8c6fc398260e 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -1155,7 +1155,20 @@ static void raid10_read_request(struct mddev *mddev, struct bio *bio,
 	char b[BDEVNAME_SIZE];
 	int slot = r10_bio->read_slot;
 	struct md_rdev *err_rdev = NULL;
-	gfp_t gfp = GFP_NOIO;
+
+	/*
+	 * An md cloned bio indicates we are in the error path.
+	 * This is more reliable than checking slot, which might
+	 * be -1 even in the error path if a failed bio was split.
+	 */
+	bool err_path = md_cloned_bio(mddev, bio);
+
+	/*
+	 * If we are in the error path, we are blocking the raid10d
+	 * thread so there is a tiny risk of deadlock.  So ask for
+	 * emergency memory if needed.
+	 */
+	gfp_t gfp = err_path ? (GFP_NOIO | __GFP_HIGH) : GFP_NOIO;
 
 	if (slot >= 0 && r10_bio->devs[slot].rdev) {
 		/*
@@ -1166,11 +1179,6 @@ static void raid10_read_request(struct mddev *mddev, struct bio *bio,
 		 * we lose the device name in error messages.
 		 */
 		int disk;
-		/*
-		 * As we are blocking raid10, it is a little safer to
-		 * use __GFP_HIGH.
-		 */
-		gfp = GFP_NOIO | __GFP_HIGH;
 
 		disk = r10_bio->devs[slot].devnum;
 		err_rdev = conf->mirrors[disk].rdev;
-- 
2.43.0


^ permalink raw reply related

* [PATCH v2 1/3] md/raid1,raid10: fix deadlock in read error recovery path
From: Abd-Alrhman Masalkhi @ 2026-05-01 11:46 UTC (permalink / raw)
  To: song, yukuai, xni, neilb, shli
  Cc: linux-raid, linux-kernel, Abd-Alrhman Masalkhi
In-Reply-To: <20260501114652.590037-1-abd.masalkhi@gmail.com>

raid1d and raid10d may resubmit a split md cloned bio while handling
a read error. In this case, resubmitting the bio can lead to a deadlock
if the array is suspended before md_handle_request() acquires an
active_io reference via percpu_ref_tryget_live().

Since the cloned bio already holds an active_io reference,
trying to acquire another reference via percpu_ref_tryget_live()
can lead to a deadlock while the array is suspended.

Fix this by using percpu_ref_get() for md cloned bios.

Fixes: bb2a9acefaf9 ("md/raid1: switch to use md_account_bio() for io accounting")
Fixes: 820455238366 ("md/raid10: switch to use md_account_bio() for io accounting")
Signed-off-by: Abd-Alrhman Masalkhi <abd.masalkhi@gmail.com>
---
Changes in v2:
 - Use md_cloned_bio() consistently to detect cloned bios.
 - Recognize that raid10 has the same issue and fix it in this series	
 - Allow splitting bios.
 - Handle md cloned bios explicitly in md_handle_request()
 - Link v1: https://lore.kernel.org/linux-raid/20260427103446.300378-1-abd.masalkhi@gmail.com/

Please let me know if I should add a Suggested-by tag for Yu Kuai,
as the solution approach was suggested during review.

Link to Yu Kuai' email: https://lore.kernel.org/linux-raid/m2lde74dtw.fsf@gmail.com/T/#m714020a38b60fc5f84b9a24f0c46acbe5d7342d6

Thanks
Abd-alrhman
---
 drivers/md/md.c | 25 ++++++++++++++++---------
 drivers/md/md.h |  5 +++++
 2 files changed, 21 insertions(+), 9 deletions(-)

diff --git a/drivers/md/md.c b/drivers/md/md.c
index e926aef9ec43..96db1e7850e9 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -396,17 +396,24 @@ static bool is_suspended(struct mddev *mddev, struct bio *bio)
 bool md_handle_request(struct mddev *mddev, struct bio *bio)
 {
 check_suspended:
-	if (is_suspended(mddev, bio)) {
-		/* Bail out if REQ_NOWAIT is set for the bio */
-		if (bio->bi_opf & REQ_NOWAIT) {
-			bio_wouldblock_error(bio);
-			return true;
+	if (unlikely(md_cloned_bio(mddev, bio))) {
+		/*
+		 * This bio is an MD cloned bio and already holds an
+		 * active_io reference, so percpu_ref_get() is safe here.
+		 */
+		percpu_ref_get(&mddev->active_io);
+	} else {
+		if (is_suspended(mddev, bio)) {
+			/* Bail out if REQ_NOWAIT is set for the bio */
+			if (bio->bi_opf & REQ_NOWAIT) {
+				bio_wouldblock_error(bio);
+				return true;
+			}
+			wait_event(mddev->sb_wait, !is_suspended(mddev, bio));
 		}
-		wait_event(mddev->sb_wait, !is_suspended(mddev, bio));
+		if (!percpu_ref_tryget_live(&mddev->active_io))
+			goto check_suspended;
 	}
-	if (!percpu_ref_tryget_live(&mddev->active_io))
-		goto check_suspended;
-
 	if (!mddev->pers->make_request(mddev, bio)) {
 		percpu_ref_put(&mddev->active_io);
 		if (mddev_is_dm(mddev) && mddev->pers->prepare_suspend)
diff --git a/drivers/md/md.h b/drivers/md/md.h
index 3bfbee595156..e44074d30cf9 100644
--- a/drivers/md/md.h
+++ b/drivers/md/md.h
@@ -1038,6 +1038,11 @@ void mddev_update_io_opt(struct mddev *mddev, unsigned int nr_stripes);
 
 extern const struct block_device_operations md_fops;
 
+static inline bool md_cloned_bio(struct mddev *mddev, struct bio *bio)
+{
+	return bio->bi_pool == &mddev->io_clone_set;
+}
+
 /*
  * MD devices can be used undeneath by DM, in which case ->gendisk is NULL.
  */
-- 
2.43.0


^ permalink raw reply related

* [PATCH v2 0/3] md/raid1,raid10: fix deadlock and bio accounting in read error path
From: Abd-Alrhman Masalkhi @ 2026-05-01 11:46 UTC (permalink / raw)
  To: song, yukuai, xni, neilb, shli
  Cc: linux-raid, linux-kernel, Abd-Alrhman Masalkhi

Hi,

This series revisits an issue in the read error recovery path for
raid1 and raid10 when bios are split and resubmitted.

In v1, the approach was to avoid splitting bios in the raid1 thread
to prevent recursion and potential deadlocks. However, this was not
ideal and too restrictive.

As suggested by Yu Kuai, this series instead handles the problem in
md_handle_request() and allows bio splitting.

Link to Yu Kuai' email: https://lore.kernel.org/linux-raid/m2lde74dtw.fsf@gmail.com/T/#m714020a38b60fc5f84b9a24f0c46acbe5d7342d6

This series fixes the above by:
 - handling md cloned bios explicitly in md_handle_request()
 - using md_cloned_bio() to reliably detect the error path
 - fixing bio accounting to avoid double/missing accounting

Changes in v2:
 - Split fixes into separate patches for clarity.
 - Use md_cloned_bio() consistently to detect cloned bios.
 - Recognize that raid10 has the same issue and fix it in this series	
 - Allow splitting bios.
 - Handle md cloned bios explicitly in md_handle_request()
 - Link v1: https://lore.kernel.org/linux-raid/20260427103446.300378-1-abd.masalkhi@gmail.com/

Abd-Alrhman Masalkhi (3):
  md/raid1,raid10: fix deadlock in read error recovery path
  md/raid1,raid10: fix error-path detection with md_cloned_bio()
  md/raid1,raid10: fix bio accounting for split md cloned bios

 drivers/md/md.c     | 25 ++++++++++++++++---------
 drivers/md/md.h     |  5 +++++
 drivers/md/raid1.c  | 15 +++++++++++----
 drivers/md/raid10.c | 28 ++++++++++++++++++----------
 4 files changed, 50 insertions(+), 23 deletions(-)

-- 
2.43.0


^ permalink raw reply

* Re: [PATCH] md/raid5: split reshape bios before bitmap accounting
From: kernel test robot @ 2026-04-30 19:48 UTC (permalink / raw)
  To: Yu Kuai, linux-raid
  Cc: oe-kbuild-all, linux-kernel, Li Nan, Yu Kuai, Cheng Cheng
In-Reply-To: <20260419030942.824195-20-yukuai@fnnas.com>

Hi Yu,

kernel test robot noticed the following build warnings:

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

url:    https://github.com/intel-lab-lkp/linux/commits/Yu-Kuai/md-raid5-split-reshape-bios-before-bitmap-accounting/20260425-083941
base:   linus/master
patch link:    https://lore.kernel.org/r/20260419030942.824195-20-yukuai%40fnnas.com
patch subject: [PATCH] md/raid5: split reshape bios before bitmap accounting
config: arc-randconfig-001 (https://download.01.org/0day-ci/archive/20260501/202605010352.oO1uCwCR-lkp@intel.com/config)
compiler: arc-linux-gcc (GCC) 8.5.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260501/202605010352.oO1uCwCR-lkp@intel.com/reproduce)

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

All warnings (new ones prefixed by >>):

   drivers/md/raid5.c: In function 'raid5_make_request':
   drivers/md/raid5.c:6126:7: error: implicit declaration of function 'mddev_bio_split_at_reshape_offset' [-Werror=implicit-function-declaration]
     bi = mddev_bio_split_at_reshape_offset(mddev, bi, NULL,
          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>> drivers/md/raid5.c:6126:5: warning: assignment to 'struct bio *' from 'int' makes pointer from integer without a cast [-Wint-conversion]
     bi = mddev_bio_split_at_reshape_offset(mddev, bi, NULL,
        ^
   cc1: some warnings being treated as errors


vim +6126 drivers/md/raid5.c

  6083	
  6084	static bool raid5_make_request(struct mddev *mddev, struct bio * bi)
  6085	{
  6086		DEFINE_WAIT_FUNC(wait, woken_wake_function);
  6087		struct r5conf *conf = mddev->private;
  6088		const int rw = bio_data_dir(bi);
  6089		struct stripe_request_ctx *ctx;
  6090		sector_t logical_sector;
  6091		enum stripe_result res;
  6092		int s, stripe_cnt;
  6093		bool on_wq;
  6094	
  6095		if (unlikely(bi->bi_opf & REQ_PREFLUSH)) {
  6096			int ret = log_handle_flush_request(conf, bi);
  6097	
  6098			if (ret == 0)
  6099				return true;
  6100			if (ret == -ENODEV) {
  6101				if (md_flush_request(mddev, bi))
  6102					return true;
  6103			}
  6104			/* ret == -EAGAIN, fallback */
  6105		}
  6106	
  6107		md_write_start(mddev, bi);
  6108		/*
  6109		 * If array is degraded, better not do chunk aligned read because
  6110		 * later we might have to read it again in order to reconstruct
  6111		 * data on failed drives.
  6112		 */
  6113		if (rw == READ && mddev->degraded == 0 &&
  6114		    mddev->reshape_position == MaxSector) {
  6115			bi = chunk_aligned_read(mddev, bi);
  6116			if (!bi)
  6117				return true;
  6118		}
  6119	
  6120		if (unlikely(bio_op(bi) == REQ_OP_DISCARD)) {
  6121			make_discard_request(mddev, bi);
  6122			md_write_end(mddev);
  6123			return true;
  6124		}
  6125	
> 6126		bi = mddev_bio_split_at_reshape_offset(mddev, bi, NULL,
  6127						       &conf->bio_split);
  6128		if (!bi) {
  6129			if (rw == WRITE)
  6130				md_write_end(mddev);
  6131			return true;
  6132		}
  6133	
  6134		logical_sector = bi->bi_iter.bi_sector & ~((sector_t)RAID5_STRIPE_SECTORS(conf)-1);
  6135		bi->bi_next = NULL;
  6136	
  6137		ctx = mempool_alloc(conf->ctx_pool, GFP_NOIO);
  6138		memset(ctx, 0, conf->ctx_size);
  6139		ctx->first_sector = logical_sector;
  6140		ctx->last_sector = bio_end_sector(bi);
  6141		/*
  6142		 * if r5l_handle_flush_request() didn't clear REQ_PREFLUSH,
  6143		 * we need to flush journal device
  6144		 */
  6145		if (unlikely(bi->bi_opf & REQ_PREFLUSH))
  6146			ctx->do_flush = true;
  6147	
  6148		stripe_cnt = DIV_ROUND_UP_SECTOR_T(ctx->last_sector - logical_sector,
  6149						   RAID5_STRIPE_SECTORS(conf));
  6150		bitmap_set(ctx->sectors_to_do, 0, stripe_cnt);
  6151	
  6152		pr_debug("raid456: %s, logical %llu to %llu\n", __func__,
  6153			 bi->bi_iter.bi_sector, ctx->last_sector);
  6154	
  6155		/* Bail out if conflicts with reshape and REQ_NOWAIT is set */
  6156		if ((bi->bi_opf & REQ_NOWAIT) &&
  6157		    get_reshape_loc(mddev, conf, logical_sector) == LOC_INSIDE_RESHAPE) {
  6158			bio_wouldblock_error(bi);
  6159			if (rw == WRITE)
  6160				md_write_end(mddev);
  6161			mempool_free(ctx, conf->ctx_pool);
  6162			return true;
  6163		}
  6164		md_account_bio(mddev, &bi);
  6165	
  6166		/*
  6167		 * Lets start with the stripe with the lowest chunk offset in the first
  6168		 * chunk. That has the best chances of creating IOs adjacent to
  6169		 * previous IOs in case of sequential IO and thus creates the most
  6170		 * sequential IO pattern. We don't bother with the optimization when
  6171		 * reshaping as the performance benefit is not worth the complexity.
  6172		 */
  6173		if (likely(conf->reshape_progress == MaxSector)) {
  6174			logical_sector = raid5_bio_lowest_chunk_sector(conf, bi);
  6175			on_wq = false;
  6176		} else {
  6177			add_wait_queue(&conf->wait_for_reshape, &wait);
  6178			on_wq = true;
  6179		}
  6180		s = (logical_sector - ctx->first_sector) >> RAID5_STRIPE_SHIFT(conf);
  6181	
  6182		while (1) {
  6183			res = make_stripe_request(mddev, conf, ctx, logical_sector,
  6184						  bi);
  6185			if (res == STRIPE_FAIL || res == STRIPE_WAIT_RESHAPE)
  6186				break;
  6187	
  6188			if (res == STRIPE_RETRY)
  6189				continue;
  6190	
  6191			if (res == STRIPE_SCHEDULE_AND_RETRY) {
  6192				WARN_ON_ONCE(!on_wq);
  6193				/*
  6194				 * Must release the reference to batch_last before
  6195				 * scheduling and waiting for work to be done,
  6196				 * otherwise the batch_last stripe head could prevent
  6197				 * raid5_activate_delayed() from making progress
  6198				 * and thus deadlocking.
  6199				 */
  6200				if (ctx->batch_last) {
  6201					raid5_release_stripe(ctx->batch_last);
  6202					ctx->batch_last = NULL;
  6203				}
  6204	
  6205				wait_woken(&wait, TASK_UNINTERRUPTIBLE,
  6206					   MAX_SCHEDULE_TIMEOUT);
  6207				continue;
  6208			}
  6209	
  6210			s = find_next_bit_wrap(ctx->sectors_to_do, stripe_cnt, s);
  6211			if (s == stripe_cnt)
  6212				break;
  6213	
  6214			logical_sector = ctx->first_sector +
  6215				(s << RAID5_STRIPE_SHIFT(conf));
  6216		}
  6217		if (unlikely(on_wq))
  6218			remove_wait_queue(&conf->wait_for_reshape, &wait);
  6219	
  6220		if (ctx->batch_last)
  6221			raid5_release_stripe(ctx->batch_last);
  6222	
  6223		if (rw == WRITE)
  6224			md_write_end(mddev);
  6225	
  6226		mempool_free(ctx, conf->ctx_pool);
  6227		if (res == STRIPE_WAIT_RESHAPE) {
  6228			md_free_cloned_bio(bi);
  6229			return false;
  6230		}
  6231	
  6232		bio_endio(bi);
  6233		return true;
  6234	}
  6235	

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

^ permalink raw reply

* Why is mdraid not listed sashiko.dev (review by gemini/gemini-3.1-pro-preview)?
From: Paul Menzel @ 2026-04-30 15:12 UTC (permalink / raw)
  To: Yu Kuai, Song Liu; +Cc: linux-raid

Dear Kuai, dear Song, dear Linux folks,


Do you know, why this subsystem is not listed on the sashiko.dev [1], 
where Google’s gemini/gemini-3.1-pro-preview reviews Linux kernel patches?


Kind regards,

Paul


PS: LKML is listed, so patches sent to linux-kernel@vger.kernel.org are 
reviewed, but cannot be filtered according to the subsystem.


[1]: https://sashiko.dev/

^ permalink raw reply

* Re: [PATCH] md/raid5: Fix bio retry on interrupted reshape
From: Nigel Croxon @ 2026-04-30 11:53 UTC (permalink / raw)
  To: Paul Menzel; +Cc: song, yukuai, linux-raid
In-Reply-To: <a7e94a16-439e-4b28-bc0b-580e00953473@molgen.mpg.de>

Looks like this was just fixed by Benjamin Marzinski's

commit 418b3e64e4459 md/raid5: Fix UAF on IO across the reshape position

So my patch is not needed anymore.

-Nigel


On 4/29/26 9:07 AM, Paul Menzel wrote:
> Dear Nigel,
>
>
> Thank you for your patch.
>
> Am 29.04.26 um 13:10 schrieb Nigel Croxon:
>> When a bio encounters LOC_INSIDE_RESHAPE during a reshape that is
>> interrupted (stopped or unable to progress), the code sets
>> bi->bi_status = BLK_STS_RESOURCE to signal the block layer for retry.
>> However, bio_endio() is never called, so the block layer never
>> receives the completion notification and the retry never happens.
>>
>> This causes I/O to hang when a filesystem is layered over RAID5 and
>> reshape gets stuck.
>>
>> Fix this by calling bio_endio(bi) before md_free_cloned_bio(bi) so
>> the block layer is properly notified of the BLK_STS_RESOURCE status
>> and can retry the request.
>>
>> Tested stripes and stripe size conversions under load comparing
>> files multiple times during each conversion (i.e. MD reshape) on
>> ext4 after dropping caches degrading the RaidLV each time and
>
> I thought RaidLV misspelled Raid V (Raid 5), so should you resend, 
> maybe write it as RAID LV.
>
>> no data corruption.
>>
>> Fixes: https://lwn.net/Articles/757123/
>
> Which paragraph/comment exactly?
>
>> Signed-off-by: Nigel Croxon <ncroxon@redhat.com>
>> ---
>>   drivers/md/raid5.c | 1 +
>>   1 file changed, 1 insertion(+)
>>
>> diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
>> index 6e79829c5acb..9a3475429ef4 100644
>> --- a/drivers/md/raid5.c
>> +++ b/drivers/md/raid5.c
>> @@ -6217,6 +6217,7 @@ static bool raid5_make_request(struct mddev 
>> *mddev, struct bio * bi)
>>
>>       mempool_free(ctx, conf->ctx_pool);
>>       if (res == STRIPE_WAIT_RESHAPE) {
>> +         bio_endio(bi);
>>           md_free_cloned_bio(bi);
>>           return false;
>>       }
>
> Reviewed-by: Paul Menzel <pmenzel@molgen.mpg.de>
>
>
> Kind regards,
>
> Paul
>


^ permalink raw reply

* Re: Suggest me a cost effective SATA SSD?
From: o1bigtenor @ 2026-04-30 10:48 UTC (permalink / raw)
  To: linux-raid
In-Reply-To: <afKLmiyhEe4Om0Nt@mail.bitfolk.com>

On Wed, Apr 29, 2026 at 6:09 PM Andy Smith <andy@strugglers.net> wrote:
>
> Hi,
>
> I've inherited a system with a pair of these:
>
> Model Family:     Crucial/Micron Client SSDs
> Device Model:     CT4000BX500SSD1
> User Capacity:    4,000,787,030,016 bytes [4.00 TB]
>
> Their write performance is terrible. Struggling to get 20MB/s sequential
> write. Their TBW count is low so it's not an issue of excessive write
> cycles. I've searched around and established they are just really bad
> SSDs. They also don't support SMART self-tests, which seems like a
> really cheap thing to do.
>
> I'm further confident that the problem lies with these SSDs because
> there is a pair of much better SSDs in there and they perform as I would
> expect. However, the use case for this storage is for lower cost so it's
> not an option to just buy more of those.
>
> So, could anyone suggest a decent low end (consumer/prosumer market) SSD
> model that is known to work well without terrible firmware bugs under
> Linux, preferably with power loss protection? Low write endurance is
> fine. Capacity of 4TB ideally.
>
I would suggest that power loss protection be sought using a UPS - - - imo a
better idea.

HTH

^ permalink raw reply

* Re: [PATCH] md/raid5: split reshape bios before bitmap accounting
From: kernel test robot @ 2026-04-30  4:07 UTC (permalink / raw)
  To: Yu Kuai, linux-raid
  Cc: oe-kbuild-all, linux-kernel, Li Nan, Yu Kuai, Cheng Cheng
In-Reply-To: <20260419030942.824195-20-yukuai@fnnas.com>

Hi Yu,

kernel test robot noticed the following build errors:

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

url:    https://github.com/intel-lab-lkp/linux/commits/Yu-Kuai/md-raid5-split-reshape-bios-before-bitmap-accounting/20260425-083941
base:   linus/master
patch link:    https://lore.kernel.org/r/20260419030942.824195-20-yukuai%40fnnas.com
patch subject: [PATCH] md/raid5: split reshape bios before bitmap accounting
config: parisc64-defconfig (https://download.01.org/0day-ci/archive/20260430/202604301127.hkMOIzHM-lkp@intel.com/config)
compiler: hppa64-linux-gcc (GCC) 15.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260430/202604301127.hkMOIzHM-lkp@intel.com/reproduce)

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

All errors (new ones prefixed by >>):

   drivers/md/raid5.c: In function 'raid5_make_request':
>> drivers/md/raid5.c:6126:14: error: implicit declaration of function 'mddev_bio_split_at_reshape_offset' [-Wimplicit-function-declaration]
    6126 |         bi = mddev_bio_split_at_reshape_offset(mddev, bi, NULL,
         |              ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>> drivers/md/raid5.c:6126:12: error: assignment to 'struct bio *' from 'int' makes pointer from integer without a cast [-Wint-conversion]
    6126 |         bi = mddev_bio_split_at_reshape_offset(mddev, bi, NULL,
         |            ^


vim +/mddev_bio_split_at_reshape_offset +6126 drivers/md/raid5.c

  6083	
  6084	static bool raid5_make_request(struct mddev *mddev, struct bio * bi)
  6085	{
  6086		DEFINE_WAIT_FUNC(wait, woken_wake_function);
  6087		struct r5conf *conf = mddev->private;
  6088		const int rw = bio_data_dir(bi);
  6089		struct stripe_request_ctx *ctx;
  6090		sector_t logical_sector;
  6091		enum stripe_result res;
  6092		int s, stripe_cnt;
  6093		bool on_wq;
  6094	
  6095		if (unlikely(bi->bi_opf & REQ_PREFLUSH)) {
  6096			int ret = log_handle_flush_request(conf, bi);
  6097	
  6098			if (ret == 0)
  6099				return true;
  6100			if (ret == -ENODEV) {
  6101				if (md_flush_request(mddev, bi))
  6102					return true;
  6103			}
  6104			/* ret == -EAGAIN, fallback */
  6105		}
  6106	
  6107		md_write_start(mddev, bi);
  6108		/*
  6109		 * If array is degraded, better not do chunk aligned read because
  6110		 * later we might have to read it again in order to reconstruct
  6111		 * data on failed drives.
  6112		 */
  6113		if (rw == READ && mddev->degraded == 0 &&
  6114		    mddev->reshape_position == MaxSector) {
  6115			bi = chunk_aligned_read(mddev, bi);
  6116			if (!bi)
  6117				return true;
  6118		}
  6119	
  6120		if (unlikely(bio_op(bi) == REQ_OP_DISCARD)) {
  6121			make_discard_request(mddev, bi);
  6122			md_write_end(mddev);
  6123			return true;
  6124		}
  6125	
> 6126		bi = mddev_bio_split_at_reshape_offset(mddev, bi, NULL,
  6127						       &conf->bio_split);
  6128		if (!bi) {
  6129			if (rw == WRITE)
  6130				md_write_end(mddev);
  6131			return true;
  6132		}
  6133	
  6134		logical_sector = bi->bi_iter.bi_sector & ~((sector_t)RAID5_STRIPE_SECTORS(conf)-1);
  6135		bi->bi_next = NULL;
  6136	
  6137		ctx = mempool_alloc(conf->ctx_pool, GFP_NOIO);
  6138		memset(ctx, 0, conf->ctx_size);
  6139		ctx->first_sector = logical_sector;
  6140		ctx->last_sector = bio_end_sector(bi);
  6141		/*
  6142		 * if r5l_handle_flush_request() didn't clear REQ_PREFLUSH,
  6143		 * we need to flush journal device
  6144		 */
  6145		if (unlikely(bi->bi_opf & REQ_PREFLUSH))
  6146			ctx->do_flush = true;
  6147	
  6148		stripe_cnt = DIV_ROUND_UP_SECTOR_T(ctx->last_sector - logical_sector,
  6149						   RAID5_STRIPE_SECTORS(conf));
  6150		bitmap_set(ctx->sectors_to_do, 0, stripe_cnt);
  6151	
  6152		pr_debug("raid456: %s, logical %llu to %llu\n", __func__,
  6153			 bi->bi_iter.bi_sector, ctx->last_sector);
  6154	
  6155		/* Bail out if conflicts with reshape and REQ_NOWAIT is set */
  6156		if ((bi->bi_opf & REQ_NOWAIT) &&
  6157		    get_reshape_loc(mddev, conf, logical_sector) == LOC_INSIDE_RESHAPE) {
  6158			bio_wouldblock_error(bi);
  6159			if (rw == WRITE)
  6160				md_write_end(mddev);
  6161			mempool_free(ctx, conf->ctx_pool);
  6162			return true;
  6163		}
  6164		md_account_bio(mddev, &bi);
  6165	
  6166		/*
  6167		 * Lets start with the stripe with the lowest chunk offset in the first
  6168		 * chunk. That has the best chances of creating IOs adjacent to
  6169		 * previous IOs in case of sequential IO and thus creates the most
  6170		 * sequential IO pattern. We don't bother with the optimization when
  6171		 * reshaping as the performance benefit is not worth the complexity.
  6172		 */
  6173		if (likely(conf->reshape_progress == MaxSector)) {
  6174			logical_sector = raid5_bio_lowest_chunk_sector(conf, bi);
  6175			on_wq = false;
  6176		} else {
  6177			add_wait_queue(&conf->wait_for_reshape, &wait);
  6178			on_wq = true;
  6179		}
  6180		s = (logical_sector - ctx->first_sector) >> RAID5_STRIPE_SHIFT(conf);
  6181	
  6182		while (1) {
  6183			res = make_stripe_request(mddev, conf, ctx, logical_sector,
  6184						  bi);
  6185			if (res == STRIPE_FAIL || res == STRIPE_WAIT_RESHAPE)
  6186				break;
  6187	
  6188			if (res == STRIPE_RETRY)
  6189				continue;
  6190	
  6191			if (res == STRIPE_SCHEDULE_AND_RETRY) {
  6192				WARN_ON_ONCE(!on_wq);
  6193				/*
  6194				 * Must release the reference to batch_last before
  6195				 * scheduling and waiting for work to be done,
  6196				 * otherwise the batch_last stripe head could prevent
  6197				 * raid5_activate_delayed() from making progress
  6198				 * and thus deadlocking.
  6199				 */
  6200				if (ctx->batch_last) {
  6201					raid5_release_stripe(ctx->batch_last);
  6202					ctx->batch_last = NULL;
  6203				}
  6204	
  6205				wait_woken(&wait, TASK_UNINTERRUPTIBLE,
  6206					   MAX_SCHEDULE_TIMEOUT);
  6207				continue;
  6208			}
  6209	
  6210			s = find_next_bit_wrap(ctx->sectors_to_do, stripe_cnt, s);
  6211			if (s == stripe_cnt)
  6212				break;
  6213	
  6214			logical_sector = ctx->first_sector +
  6215				(s << RAID5_STRIPE_SHIFT(conf));
  6216		}
  6217		if (unlikely(on_wq))
  6218			remove_wait_queue(&conf->wait_for_reshape, &wait);
  6219	
  6220		if (ctx->batch_last)
  6221			raid5_release_stripe(ctx->batch_last);
  6222	
  6223		if (rw == WRITE)
  6224			md_write_end(mddev);
  6225	
  6226		mempool_free(ctx, conf->ctx_pool);
  6227		if (res == STRIPE_WAIT_RESHAPE) {
  6228			md_free_cloned_bio(bi);
  6229			return false;
  6230		}
  6231	
  6232		bio_endio(bi);
  6233		return true;
  6234	}
  6235	

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

^ permalink raw reply

* Re: [PATCH 01/28] xor: assert that xor_blocks is not call from interrupt context
From: patchwork-bot+linux-riscv @ 2026-04-30  3:24 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: linux-riscv, akpm, richard.henderson, mattst88, linmag7, linux,
	catalin.marinas, will, ardb, chenhuacai, kernel, maddy, mpe,
	npiggin, chleroy, pjw, palmer, aou, alex, hca, gor, agordeev,
	borntraeger, svens, davem, andreas, richard, anton.ivanov,
	johannes, tglx, mingo, bp, dave.hansen, x86, hpa, herbert,
	dan.j.williams, clm, dsterba, arnd, song, yukuai, linan122, tytso,
	Jason, linux-alpha, linux-kernel, linux-arm-kernel, loongarch,
	linuxppc-dev, linux-s390, sparclinux, linux-um, linux-crypto,
	linux-btrfs, linux-arch, linux-raid
In-Reply-To: <20260327061704.3707577-2-hch@lst.de>

Hello:

This series was applied to riscv/linux.git (fixes)
by Andrew Morton <akpm@linux-foundation.org>:

On Fri, 27 Mar 2026 07:16:33 +0100 you wrote:
> Most of the optimized xor_blocks versions require FPU/vector registers,
> which generally are not supported in interrupt context.
> 
> Both callers already are in user context, so enforce this at the highest
> level.
> 
> Signed-off-by: Christoph Hellwig <hch@lst.de>
> 
> [...]

Here is the summary with links:
  - [01/28] xor: assert that xor_blocks is not call from interrupt context
    https://git.kernel.org/riscv/c/d8593b8f9354
  - [02/28] arm/xor: remove in_interrupt() handling
    https://git.kernel.org/riscv/c/b7ca705758b9
  - [03/28] arm64/xor: fix conflicting attributes for xor_block_template
    https://git.kernel.org/riscv/c/675a0dd596e7
  - [04/28] um/xor: cleanup xor.h
    https://git.kernel.org/riscv/c/3ea16a98518a
  - [05/28] xor: move to lib/raid/
    https://git.kernel.org/riscv/c/9e229025e247
  - [06/28] xor: small cleanups
    https://git.kernel.org/riscv/c/7c6e6b2b48e8
  - [07/28] xor: cleanup registration and probing
    https://git.kernel.org/riscv/c/0471415f3fd6
  - [08/28] xor: split xor.h
    https://git.kernel.org/riscv/c/54e20be48fd4
  - [09/28] xor: remove macro abuse for XOR implementation registrations
    https://git.kernel.org/riscv/c/35ebc4de1059
  - [10/28] xor: move generic implementations out of asm-generic/xor.h
    https://git.kernel.org/riscv/c/c46928fdcfa0
  - [11/28] alpha: move the XOR code to lib/raid/
    https://git.kernel.org/riscv/c/503793b1340e
  - [12/28] arm: move the XOR code to lib/raid/
    https://git.kernel.org/riscv/c/0d64a24ec0c0
  - [13/28] arm64: move the XOR code to lib/raid/
    https://git.kernel.org/riscv/c/3786f2ad0095
  - [14/28] loongarch: move the XOR code to lib/raid/
    https://git.kernel.org/riscv/c/033bee3e4963
  - [15/28] powerpc: move the XOR code to lib/raid/
    https://git.kernel.org/riscv/c/3f276cece4dd
  - [16/28] riscv: move the XOR code to lib/raid/
    https://git.kernel.org/riscv/c/5265d55b2146
  - [17/28] sparc: move the XOR code to lib/raid/
    https://git.kernel.org/riscv/c/7f96362396ee
  - [18/28] s390: move the XOR code to lib/raid/
    https://git.kernel.org/riscv/c/95c104cc5571
  - [19/28] x86: move the XOR code to lib/raid/
    https://git.kernel.org/riscv/c/77fd47e57a09
  - [20/28] xor: avoid indirect calls for arm64-optimized ops
    https://git.kernel.org/riscv/c/352ebd066b62
  - [21/28] xor: make xor.ko self-contained in lib/raid/
    https://git.kernel.org/riscv/c/e20043b4765c
  - [22/28] xor: add a better public API
    (no matching commit)
  - [23/28] xor: add a better public API
    (no matching commit)
  - [24/28] async_xor: use xor_gen
    https://git.kernel.org/riscv/c/7c12c32b9f73
  - [25/28] btrfs: use xor_gen
    https://git.kernel.org/riscv/c/0f629e7283ad
  - [26/28] xor: pass the entire operation to the low-level ops
    https://git.kernel.org/riscv/c/80dcf0a7832a
  - [27/28] xor: use static_call for xor_gen
    https://git.kernel.org/riscv/c/a21921dd02d3
  - [28/28] xor: add a kunit test case
    https://git.kernel.org/riscv/c/af53e85ef797

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



^ permalink raw reply

* Re: [PATCH] md/raid10: wire llbitmap reshape lifecycle
From: kernel test robot @ 2026-04-30  2:37 UTC (permalink / raw)
  To: Yu Kuai, linux-raid
  Cc: llvm, oe-kbuild-all, linux-kernel, Li Nan, Yu Kuai, Cheng Cheng
In-Reply-To: <20260419030942.824195-15-yukuai@fnnas.com>

Hi Yu,

kernel test robot noticed the following build errors:

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

url:    https://github.com/intel-lab-lkp/linux/commits/Yu-Kuai/md-raid10-wire-llbitmap-reshape-lifecycle/20260423-170302
base:   linus/master
patch link:    https://lore.kernel.org/r/20260419030942.824195-15-yukuai%40fnnas.com
patch subject: [PATCH] md/raid10: wire llbitmap reshape lifecycle
config: um-randconfig-001-20260430 (https://download.01.org/0day-ci/archive/20260430/202604301028.uutGNSgD-lkp@intel.com/config)
compiler: clang version 23.0.0git (https://github.com/llvm/llvm-project 5bac06718f502014fade905512f1d26d578a18f3)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260430/202604301028.uutGNSgD-lkp@intel.com/reproduce)

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

All errors (new ones prefixed by >>):

>> drivers/md/raid10.c:4370:25: error: no member named 'reshape_can_start' in 'struct bitmap_operations'
    4370 |             mddev->bitmap_ops->reshape_can_start) {
         |             ~~~~~~~~~~~~~~~~~  ^
   drivers/md/raid10.c:4371:28: error: no member named 'reshape_can_start' in 'struct bitmap_operations'
    4371 |                 ret = mddev->bitmap_ops->reshape_can_start(mddev);
         |                       ~~~~~~~~~~~~~~~~~  ^
>> drivers/md/raid10.c:4689:26: error: no member named 'reshape_mark' in 'struct bitmap_operations'
    4689 |                     mddev->bitmap_ops->reshape_mark &&
         |                     ~~~~~~~~~~~~~~~~~  ^
   drivers/md/raid10.c:4691:23: error: no member named 'reshape_mark' in 'struct bitmap_operations'
    4691 |                         mddev->bitmap_ops->reshape_mark(mddev, conf->reshape_safe,
         |                         ~~~~~~~~~~~~~~~~~  ^
   drivers/md/raid10.c:4899:25: error: no member named 'reshape_mark' in 'struct bitmap_operations'
    4899 |             mddev->bitmap_ops->reshape_mark &&
         |             ~~~~~~~~~~~~~~~~~  ^
   drivers/md/raid10.c:4901:22: error: no member named 'reshape_mark' in 'struct bitmap_operations'
    4901 |                 mddev->bitmap_ops->reshape_mark(mddev, conf->reshape_safe,
         |                 ~~~~~~~~~~~~~~~~~  ^
>> drivers/md/raid10.c:5043:37: error: no member named 'reshape_finish' in 'struct bitmap_operations'
    5043 |         if (llbitmap && mddev->bitmap_ops->reshape_finish)
         |                         ~~~~~~~~~~~~~~~~~  ^
   drivers/md/raid10.c:5044:22: error: no member named 'reshape_finish' in 'struct bitmap_operations'
    5044 |                 mddev->bitmap_ops->reshape_finish(mddev);
         |                 ~~~~~~~~~~~~~~~~~  ^
>> drivers/md/raid10.c:5107:3: error: field designator 'bitmap_sync_size' does not refer to any field in type 'struct md_personality'
    5107 |         .bitmap_sync_size = raid10_bitmap_sync_size,
         |         ~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>> drivers/md/raid10.c:5108:3: error: field designator 'bitmap_array_sectors' does not refer to any field in type 'struct md_personality'
    5108 |         .bitmap_array_sectors = raid10_bitmap_sync_size,
         |         ~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   10 errors generated.


vim +4370 drivers/md/raid10.c

  4345	
  4346	static int raid10_start_reshape(struct mddev *mddev)
  4347	{
  4348		/* A 'reshape' has been requested. This commits
  4349		 * the various 'new' fields and sets MD_RECOVER_RESHAPE
  4350		 * This also checks if there are enough spares and adds them
  4351		 * to the array.
  4352		 * We currently require enough spares to make the final
  4353		 * array non-degraded.  We also require that the difference
  4354		 * between old and new data_offset - on each device - is
  4355		 * enough that we never risk over-writing.
  4356		 */
  4357	
  4358		unsigned long before_length, after_length;
  4359		sector_t min_offset_diff = 0;
  4360		int first = 1;
  4361		struct geom new;
  4362		struct r10conf *conf = mddev->private;
  4363		struct md_rdev *rdev;
  4364		int spares = 0;
  4365		int ret;
  4366	
  4367		if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery))
  4368			return -EBUSY;
  4369		if (md_bitmap_enabled(mddev, false) &&
> 4370		    mddev->bitmap_ops->reshape_can_start) {
  4371			ret = mddev->bitmap_ops->reshape_can_start(mddev);
  4372			if (ret)
  4373				return ret;
  4374		}
  4375	
  4376		if (setup_geo(&new, mddev, geo_start) != conf->copies)
  4377			return -EINVAL;
  4378	
  4379		before_length = ((1 << conf->prev.chunk_shift) *
  4380				 conf->prev.far_copies);
  4381		after_length = ((1 << conf->geo.chunk_shift) *
  4382				conf->geo.far_copies);
  4383	
  4384		rdev_for_each(rdev, mddev) {
  4385			if (!test_bit(In_sync, &rdev->flags)
  4386			    && !test_bit(Faulty, &rdev->flags))
  4387				spares++;
  4388			if (rdev->raid_disk >= 0) {
  4389				long long diff = (rdev->new_data_offset
  4390						  - rdev->data_offset);
  4391				if (!mddev->reshape_backwards)
  4392					diff = -diff;
  4393				if (diff < 0)
  4394					diff = 0;
  4395				if (first || diff < min_offset_diff)
  4396					min_offset_diff = diff;
  4397				first = 0;
  4398			}
  4399		}
  4400	
  4401		if (max(before_length, after_length) > min_offset_diff)
  4402			return -EINVAL;
  4403	
  4404		if (spares < mddev->delta_disks)
  4405			return -EINVAL;
  4406	
  4407		conf->offset_diff = min_offset_diff;
  4408		spin_lock_irq(&conf->device_lock);
  4409		if (conf->mirrors_new) {
  4410			memcpy(conf->mirrors_new, conf->mirrors,
  4411			       sizeof(struct raid10_info)*conf->prev.raid_disks);
  4412			smp_mb();
  4413			kfree(conf->mirrors_old);
  4414			conf->mirrors_old = conf->mirrors;
  4415			conf->mirrors = conf->mirrors_new;
  4416			conf->mirrors_new = NULL;
  4417		}
  4418		setup_geo(&conf->geo, mddev, geo_start);
  4419		smp_mb();
  4420		if (mddev->reshape_backwards) {
  4421			sector_t size = raid10_size(mddev, 0, 0);
  4422			if (size < mddev->array_sectors) {
  4423				spin_unlock_irq(&conf->device_lock);
  4424				pr_warn("md/raid10:%s: array size must be reduce before number of disks\n",
  4425					mdname(mddev));
  4426				return -EINVAL;
  4427			}
  4428			mddev->resync_max_sectors = size;
  4429			conf->reshape_progress = size;
  4430		} else
  4431			conf->reshape_progress = 0;
  4432		conf->reshape_safe = conf->reshape_progress;
  4433		spin_unlock_irq(&conf->device_lock);
  4434	
  4435		if (mddev->delta_disks && mddev->bitmap) {
  4436			struct mdp_superblock_1 *sb = NULL;
  4437			sector_t oldsize, newsize;
  4438	
  4439			oldsize = raid10_size(mddev, 0, 0);
  4440			newsize = raid10_size(mddev, 0, conf->geo.raid_disks);
  4441	
  4442			if (!mddev_is_clustered(mddev) &&
  4443			    md_bitmap_enabled(mddev, false)) {
  4444				ret = mddev->bitmap_ops->resize(mddev, newsize, 0);
  4445				if (ret)
  4446					goto abort;
  4447				else
  4448					goto out;
  4449			}
  4450	
  4451			rdev_for_each(rdev, mddev) {
  4452				if (rdev->raid_disk > -1 &&
  4453				    !test_bit(Faulty, &rdev->flags))
  4454					sb = page_address(rdev->sb_page);
  4455			}
  4456	
  4457			/*
  4458			 * some node is already performing reshape, and no need to
  4459			 * call bitmap_ops->resize again since it should be called when
  4460			 * receiving BITMAP_RESIZE msg
  4461			 */
  4462			if ((sb && (le32_to_cpu(sb->feature_map) &
  4463				    MD_FEATURE_RESHAPE_ACTIVE)) || (oldsize == newsize))
  4464				goto out;
  4465	
  4466			/* cluster can't be setup without bitmap */
  4467			ret = mddev->bitmap_ops->resize(mddev, newsize, 0);
  4468			if (ret)
  4469				goto abort;
  4470	
  4471			ret = mddev->cluster_ops->resize_bitmaps(mddev, newsize, oldsize);
  4472			if (ret) {
  4473				mddev->bitmap_ops->resize(mddev, oldsize, 0);
  4474				goto abort;
  4475			}
  4476		}
  4477	out:
  4478		if (mddev->delta_disks > 0) {
  4479			rdev_for_each(rdev, mddev)
  4480				if (rdev->raid_disk < 0 &&
  4481				    !test_bit(Faulty, &rdev->flags)) {
  4482					if (raid10_add_disk(mddev, rdev) == 0) {
  4483						if (rdev->raid_disk >=
  4484						    conf->prev.raid_disks)
  4485							set_bit(In_sync, &rdev->flags);
  4486						else
  4487							rdev->recovery_offset = 0;
  4488	
  4489						/* Failure here is OK */
  4490						sysfs_link_rdev(mddev, rdev);
  4491					}
  4492				} else if (rdev->raid_disk >= conf->prev.raid_disks
  4493					   && !test_bit(Faulty, &rdev->flags)) {
  4494					/* This is a spare that was manually added */
  4495					set_bit(In_sync, &rdev->flags);
  4496				}
  4497		}
  4498		/* When a reshape changes the number of devices,
  4499		 * ->degraded is measured against the larger of the
  4500		 * pre and  post numbers.
  4501		 */
  4502		spin_lock_irq(&conf->device_lock);
  4503		mddev->degraded = calc_degraded(conf);
  4504		spin_unlock_irq(&conf->device_lock);
  4505		mddev->raid_disks = conf->geo.raid_disks;
  4506		mddev->reshape_position = conf->reshape_progress;
  4507		set_bit(MD_SB_CHANGE_DEVS, &mddev->sb_flags);
  4508	
  4509		clear_bit(MD_RECOVERY_SYNC, &mddev->recovery);
  4510		clear_bit(MD_RECOVERY_CHECK, &mddev->recovery);
  4511		clear_bit(MD_RECOVERY_DONE, &mddev->recovery);
  4512		set_bit(MD_RECOVERY_RESHAPE, &mddev->recovery);
  4513		set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
  4514		conf->reshape_checkpoint = jiffies;
  4515		md_new_event();
  4516		return 0;
  4517	
  4518	abort:
  4519		mddev->recovery = 0;
  4520		spin_lock_irq(&conf->device_lock);
  4521		conf->geo = conf->prev;
  4522		mddev->raid_disks = conf->geo.raid_disks;
  4523		rdev_for_each(rdev, mddev)
  4524			rdev->new_data_offset = rdev->data_offset;
  4525		smp_wmb();
  4526		conf->reshape_progress = MaxSector;
  4527		conf->reshape_safe = MaxSector;
  4528		mddev->reshape_position = MaxSector;
  4529		spin_unlock_irq(&conf->device_lock);
  4530		return ret;
  4531	}
  4532	

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

^ permalink raw reply

* Re: [PATCH v3 8/8] md/raid10: fix IO error at logical block size granularity
From: Xiao Ni @ 2026-04-30  2:23 UTC (permalink / raw)
  To: linan666; +Cc: song, yukuai, linux-raid, linux-kernel, yangerkun, yi.zhang
In-Reply-To: <20260416033801.786415-9-linan666@huaweicloud.com>

On Thu, Apr 16, 2026 at 11:51 AM <linan666@huaweicloud.com> wrote:
>
> 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>
> Reviewed-by: Yu Kuai <yukuai@fnnas.com>
> ---
>  drivers/md/raid10.c | 17 ++++-------------
>  1 file changed, 4 insertions(+), 13 deletions(-)
>
> diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
> index 3638e00fe420..5b4ffd23211a 100644
> --- a/drivers/md/raid10.c
> +++ b/drivers/md/raid10.c
> @@ -2454,7 +2454,7 @@ static void sync_request_write(struct mddev *mddev, struct r10bio *r10_bio)
>  static void fix_recovery_read_error(struct r10bio *r10_bio)
>  {
>         /* We got a read error during recovery.
> -        * We repeat the read in smaller page-sized sections.
> +        * We repeat the read in smaller logical_block_sized sections.
>          * If a read succeeds, write it to the new device or record
>          * a bad block if we cannot.
>          * If a read fails, record a bad block on both old and
> @@ -2470,14 +2470,11 @@ static void fix_recovery_read_error(struct r10bio *r10_bio)
>         struct folio *folio = get_resync_folio(bio)->folio;
>
>         while (sectors) {
> -               int s = sectors;
> +               int s = min_t(int, sectors, mddev->logical_block_size >> 9);
>                 struct md_rdev *rdev;
>                 sector_t addr;
>                 int ok;
>
> -               if (s > (PAGE_SIZE>>9))
> -                       s = PAGE_SIZE >> 9;
> -
>                 rdev = conf->mirrors[dr].rdev;
>                 addr = r10_bio->devs[0].addr + sect;
>                 ok = sync_folio_io(rdev,
> @@ -2621,14 +2618,11 @@ static void fix_read_error(struct r10conf *conf, struct mddev *mddev, struct r10
>         }
>
>         while(sectors) {
> -               int s = sectors;
> +               int s = min_t(int, sectors, mddev->logical_block_size >> 9);
>                 int sl = slot;
>                 int success = 0;
>                 int start;
>
> -               if (s > (PAGE_SIZE>>9))
> -                       s = PAGE_SIZE >> 9;
> -
>                 do {
>                         d = r10_bio->devs[sl].devnum;
>                         rdev = conf->mirrors[d].rdev;
> @@ -4926,13 +4920,10 @@ static int handle_reshape_read_error(struct mddev *mddev,
>         __raid10_find_phys(&conf->prev, r10b);
>
>         while (sectors) {
> -               int s = sectors;
> +               int s = min_t(int, sectors, mddev->logical_block_size >> 9);
>                 int success = 0;
>                 int first_slot = slot;
>
> -               if (s > (PAGE_SIZE >> 9))
> -                       s = PAGE_SIZE >> 9;
> -
>                 while (!success) {
>                         int d = r10b->devs[slot].devnum;
>                         struct md_rdev *rdev = conf->mirrors[d].rdev;
> --
> 2.39.2
>
>

This patch looks good to me.
Reviewed-by: Xiao Ni <xni@redhat.com>


^ permalink raw reply

* Re: [PATCH v3 7/8] md/raid1: fix IO error at logical block size granularity
From: Xiao Ni @ 2026-04-30  2:22 UTC (permalink / raw)
  To: linan666; +Cc: song, yukuai, linux-raid, linux-kernel, yangerkun, yi.zhang
In-Reply-To: <20260416033801.786415-8-linan666@huaweicloud.com>

On Thu, Apr 16, 2026 at 11:55 AM <linan666@huaweicloud.com> wrote:
>
> 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>
> Reviewed-by: Yu Kuai <yukuai@fnnas.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 724fd4f2cc3a..de8c964ca11d 100644
> --- a/drivers/md/raid1.c
> +++ b/drivers/md/raid1.c
> @@ -2116,7 +2116,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
> @@ -2147,13 +2147,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
> @@ -2192,7 +2190,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;
> @@ -2390,14 +2388,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
>
>

This patch looks good to me.
Reviewed-by: Xiao Ni <xni@redhat.com>


^ permalink raw reply

* Re: [PATCH v3 6/8] md/raid1,raid10: use folio for sync path IO
From: Xiao Ni @ 2026-04-30  1:54 UTC (permalink / raw)
  To: linan666; +Cc: song, yukuai, linux-raid, linux-kernel, yangerkun, yi.zhang
In-Reply-To: <20260416033801.786415-7-linan666@huaweicloud.com>

Hi Nan

On Thu, Apr 16, 2026 at 11:55 AM <linan666@huaweicloud.com> wrote:
>
> From: Li Nan <linan122@huawei.com>
>
> Convert all IO on the sync path to use folios, and rename page-related
> identifiers to match folio.
>
> Since RESYNC_BLOCK_SIZE (64K) has higher allocation failure chance than 4k,
> retry with lower orders to improve allocation reliability. A r1/10_bio may
> have different rf->folio orders, so use minimum order as r1/10_bio sectors
> to prevent exceeding size when adding folio to IO later.
>
> Clean up:
> 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.
> 5. Remove resync_fetch_folio(), access 'rf->folio' directly.
> 6. Remove resync_free_folio(), call folio_put() directly.
> 7. clean up sync IO size calculation in raid1/10_sync_request.
>
> Signed-off-by: Li Nan <linan122@huawei.com>
> ---
>  drivers/md/md.c       |   2 +-
>  drivers/md/raid1-10.c |  80 ++++---------
>  drivers/md/raid1.c    | 209 +++++++++++++++-------------------
>  drivers/md/raid10.c   | 254 +++++++++++++++++++++---------------------
>  4 files changed, 240 insertions(+), 305 deletions(-)
>
> diff --git a/drivers/md/md.c b/drivers/md/md.c
> index 5e83914d5c14..6554b849ac74 100644
> --- a/drivers/md/md.c
> +++ b/drivers/md/md.c
> @@ -9440,7 +9440,7 @@ static bool sync_io_within_limit(struct mddev *mddev)
>  {
>         /*
>          * For raid456, sync IO is stripe(4k) per IO, for other levels, it's
> -        * RESYNC_PAGES(64k) per IO.
> +        * RESYNC_BLOCK_SIZE(64k) per IO.
>          */
>         return atomic_read(&mddev->recovery_active) <
>                (raid_is_456(mddev) ? 8 : 128) * sync_io_depth(mddev);
> diff --git a/drivers/md/raid1-10.c b/drivers/md/raid1-10.c
> index cda531d0720b..10200b0a3fd2 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)
>
>  /* when we get a read error on a read-only array, we redirect to another
> @@ -20,9 +19,9 @@
>  #define MAX_PLUG_BIO 32
>
>  /* for managing resync I/O pages */
> -struct resync_pages {
> +struct resync_folio {
>         void            *raid_bio;
> -       struct page     *pages[RESYNC_PAGES];
> +       struct folio    *folio;
>  };
>
>  struct raid1_plug_cb {
> @@ -36,77 +35,44 @@ static void rbio_pool_free(void *rbio, void *data)
>         kfree(rbio);
>  }
>
> -static inline int resync_alloc_pages(struct resync_pages *rp,
> -                                    gfp_t gfp_flags)
> +static inline int resync_alloc_folio(struct resync_folio *rf,
> +                                    gfp_t gfp_flags, int *order)
>  {
> -       int i;
> +       struct folio *folio;
>
> -       for (i = 0; i < RESYNC_PAGES; i++) {
> -               rp->pages[i] = alloc_page(gfp_flags);
> -               if (!rp->pages[i])
> -                       goto out_free;
> -       }
> +       do {
> +               folio = folio_alloc(gfp_flags, *order);
> +               if (folio)
> +                       break;
> +       } while (--(*order) > 0);

It has a problem here. If it can't allocate a big page, the sync
request unit will be smaller and sync performance may decrease. This
can happen when the system lacks sufficient continuous memory. This
change looks good to me. I just want to throw this problem out for an
open discussion.

>
> +       if (!folio)
> +               return -ENOMEM;
> +
> +       rf->folio = folio;
>         return 0;
> -
> -out_free:
> -       while (--i >= 0)
> -               put_page(rp->pages[i]);
> -       return -ENOMEM;
> -}
> -
> -static inline void resync_free_pages(struct resync_pages *rp)
> -{
> -       int i;
> -
> -       for (i = 0; i < RESYNC_PAGES; i++)
> -               put_page(rp->pages[i]);
> -}
> -
> -static inline void resync_get_all_pages(struct resync_pages *rp)
> -{
> -       int i;
> -
> -       for (i = 0; i < RESYNC_PAGES; i++)
> -               get_page(rp->pages[i]);
> -}
> -
> -static inline struct page *resync_fetch_page(struct resync_pages *rp,
> -                                            unsigned idx)
> -{
> -       if (WARN_ON_ONCE(idx >= RESYNC_PAGES))
> -               return NULL;
> -       return rp->pages[idx];
>  }
>
>  /*
> - * 'strct resync_pages' stores actual pages used for doing the resync
> + * 'strct resync_folio' stores actual pages used for doing the resync
>   *  IO, and it is per-bio, so make .bi_private points to it.
>   */
> -static inline struct resync_pages *get_resync_pages(struct bio *bio)
> +static inline struct resync_folio *get_resync_folio(struct bio *bio)
>  {
>         return bio->bi_private;
>  }
>
>  /* generally called after bio_reset() for reseting bvec */
> -static void md_bio_reset_resync_pages(struct bio *bio, struct resync_pages *rp,
> +static void md_bio_reset_resync_folio(struct bio *bio, struct resync_folio *rf,
>                                int size)
>  {
> -       int idx = 0;
> -
>         /* initialize bvec table again */
> -       do {
> -               struct page *page = resync_fetch_page(rp, idx);
> -               int len = min_t(int, size, PAGE_SIZE);
> -
> -               if (WARN_ON(!bio_add_page(bio, page, len, 0))) {
> -                       bio->bi_status = BLK_STS_RESOURCE;
> -                       bio_endio(bio);
> -                       return;
> -               }
> -
> -               size -= len;
> -       } while (idx++ < RESYNC_PAGES && size > 0);
> +       if (WARN_ON(!bio_add_folio(bio, rf->folio,
> +                                  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 a72abdc37a2d..724fd4f2cc3a 100644
> --- a/drivers/md/raid1.c
> +++ b/drivers/md/raid1.c
> @@ -120,11 +120,11 @@ static void remove_serial(struct md_rdev *rdev, sector_t lo, sector_t hi)
>
>  /*
>   * for resync bio, r1bio pointer can be retrieved from the per-bio
> - * 'struct resync_pages'.
> + * 'struct resync_folio'.
>   */
>  static inline struct r1bio *get_resync_r1bio(struct bio *bio)
>  {
> -       return get_resync_pages(bio)->raid_bio;
> +       return get_resync_folio(bio)->raid_bio;
>  }
>
>  static void *r1bio_pool_alloc(gfp_t gfp_flags, struct r1conf *conf)
> @@ -146,70 +146,69 @@ static void * r1buf_pool_alloc(gfp_t gfp_flags, void *data)
>         struct r1conf *conf = data;
>         struct r1bio *r1_bio;
>         struct bio *bio;
> -       int need_pages;
> +       int need_folio;

The name need_folio is confusing. Can we keep the same style as the
old version? How about need_folios?

>         int j;
> -       struct resync_pages *rps;
> +       struct resync_folio *rfs;
> +       int order = get_order(RESYNC_BLOCK_SIZE);
>
>         r1_bio = r1bio_pool_alloc(gfp_flags, conf);
>         if (!r1_bio)
>                 return NULL;
>
> -       rps = kmalloc_array(conf->raid_disks * 2, sizeof(struct resync_pages),
> +       rfs = kmalloc_array(conf->raid_disks * 2, sizeof(struct resync_folio),
>                             gfp_flags);
> -       if (!rps)
> +       if (!rfs)
>                 goto out_free_r1bio;
>
>         /*
>          * Allocate bios : 1 for reading, n-1 for writing
>          */
>         for (j = conf->raid_disks * 2; j-- ; ) {
> -               bio = bio_kmalloc(RESYNC_PAGES, gfp_flags);
> +               bio = bio_kmalloc(1, gfp_flags);
>                 if (!bio)
>                         goto out_free_bio;
> -               bio_init_inline(bio, NULL, RESYNC_PAGES, 0);
> +               bio_init_inline(bio, NULL, 1, 0);
>                 r1_bio->bios[j] = bio;
>         }
>         /*
> -        * Allocate RESYNC_PAGES data pages and attach them to
> -        * the first bio.
> +        * Allocate data folio and attach it to the first bio.
>          * If this is a user-requested check/repair, allocate
> -        * RESYNC_PAGES for each bio.
> +        * folio for each bio.
>          */
>         if (test_bit(MD_RECOVERY_REQUESTED, &conf->mddev->recovery))
> -               need_pages = conf->raid_disks * 2;
> +               need_folio = conf->raid_disks * 2;
>         else
> -               need_pages = 1;
> +               need_folio = 1;
>         for (j = 0; j < conf->raid_disks * 2; j++) {
> -               struct resync_pages *rp = &rps[j];
> +               struct resync_folio *rf = &rfs[j];
>
> -               bio = r1_bio->bios[j];
> -
> -               if (j < need_pages) {
> -                       if (resync_alloc_pages(rp, gfp_flags))
> -                               goto out_free_pages;
> +               if (j < need_folio) {
> +                       if (resync_alloc_folio(rf, gfp_flags, &order))
> +                               goto out_free_folio;
>                 } else {
> -                       memcpy(rp, &rps[0], sizeof(*rp));
> -                       resync_get_all_pages(rp);
> +                       memcpy(rf, &rfs[0], sizeof(*rf));
> +                       folio_get(rf->folio);
>                 }
>
> -               rp->raid_bio = r1_bio;
> -               bio->bi_private = rp;
> +               rf->raid_bio = r1_bio;
> +               r1_bio->bios[j]->bi_private = rf;
>         }
>
> +       r1_bio->sectors = 1 << (order + PAGE_SECTORS_SHIFT);
>         r1_bio->master_bio = NULL;
>
>         return r1_bio;
>
> -out_free_pages:
> +out_free_folio:
>         while (--j >= 0)
> -               resync_free_pages(&rps[j]);
> +               folio_put(rfs[j].folio);
>
>  out_free_bio:
>         while (++j < conf->raid_disks * 2) {
>                 bio_uninit(r1_bio->bios[j]);
>                 kfree(r1_bio->bios[j]);
>         }
> -       kfree(rps);
> +       kfree(rfs);
>
>  out_free_r1bio:
>         rbio_pool_free(r1_bio, data);
> @@ -221,17 +220,17 @@ static void r1buf_pool_free(void *__r1_bio, void *data)
>         struct r1conf *conf = data;
>         int i;
>         struct r1bio *r1bio = __r1_bio;
> -       struct resync_pages *rp = NULL;
> +       struct resync_folio *rf = NULL;
>
>         for (i = conf->raid_disks * 2; i--; ) {
> -               rp = get_resync_pages(r1bio->bios[i]);
> -               resync_free_pages(rp);
> +               rf = get_resync_folio(r1bio->bios[i]);
> +               folio_put(rf->folio);
>                 bio_uninit(r1bio->bios[i]);
>                 kfree(r1bio->bios[i]);
>         }
>
> -       /* resync pages array stored in the 1st bio's .bi_private */
> -       kfree(rp);
> +       /* resync folio stored in the 1st bio's .bi_private */
> +       kfree(rf);
>
>         rbio_pool_free(r1bio, data);
>  }
> @@ -2095,10 +2094,10 @@ static void end_sync_write(struct bio *bio)
>         put_sync_write_buf(r1_bio);
>  }
>
> -static int r1_sync_page_io(struct md_rdev *rdev, sector_t sector,
> -                          int sectors, struct page *page, blk_opf_t rw)
> +static int r1_sync_folio_io(struct md_rdev *rdev, sector_t sector, int sectors,
> +                           int off, struct folio *folio, blk_opf_t rw)
>  {
> -       if (sync_page_io(rdev, sector, sectors << 9, page, rw, false))
> +       if (sync_folio_io(rdev, sector, sectors << 9, off, folio, rw, false))
>                 /* success */
>                 return 1;
>         if (rw == REQ_OP_WRITE) {
> @@ -2129,10 +2128,10 @@ static int fix_sync_read_error(struct r1bio *r1_bio)
>         struct mddev *mddev = r1_bio->mddev;
>         struct r1conf *conf = mddev->private;
>         struct bio *bio = r1_bio->bios[r1_bio->read_disk];
> -       struct page **pages = get_resync_pages(bio)->pages;
> +       struct folio *folio = get_resync_folio(bio)->folio;
>         sector_t sect = r1_bio->sector;
>         int sectors = r1_bio->sectors;
> -       int idx = 0;
> +       int off = 0;
>         struct md_rdev *rdev;
>
>         rdev = conf->mirrors[r1_bio->read_disk].rdev;
> @@ -2162,9 +2161,8 @@ static int fix_sync_read_error(struct r1bio *r1_bio)
>                                  * active, and resync is currently active
>                                  */
>                                 rdev = conf->mirrors[d].rdev;
> -                               if (sync_page_io(rdev, sect, s<<9,
> -                                                pages[idx],
> -                                                REQ_OP_READ, false)) {
> +                               if (sync_folio_io(rdev, sect, s<<9, off, folio,
> +                                                 REQ_OP_READ, false)) {
>                                         success = 1;
>                                         break;
>                                 }
> @@ -2197,7 +2195,7 @@ static int fix_sync_read_error(struct r1bio *r1_bio)
>                         /* Try next page */
>                         sectors -= s;
>                         sect += s;
> -                       idx++;
> +                       off += s << 9;
>                         continue;
>                 }
>
> @@ -2210,8 +2208,7 @@ static int fix_sync_read_error(struct r1bio *r1_bio)
>                         if (r1_bio->bios[d]->bi_end_io != end_sync_read)
>                                 continue;
>                         rdev = conf->mirrors[d].rdev;
> -                       if (r1_sync_page_io(rdev, sect, s,
> -                                           pages[idx],
> +                       if (r1_sync_folio_io(rdev, sect, s, off, folio,
>                                             REQ_OP_WRITE) == 0) {
>                                 r1_bio->bios[d]->bi_end_io = NULL;
>                                 rdev_dec_pending(rdev, mddev);
> @@ -2225,14 +2222,13 @@ static int fix_sync_read_error(struct r1bio *r1_bio)
>                         if (r1_bio->bios[d]->bi_end_io != end_sync_read)
>                                 continue;
>                         rdev = conf->mirrors[d].rdev;
> -                       if (r1_sync_page_io(rdev, sect, s,
> -                                           pages[idx],
> +                       if (r1_sync_folio_io(rdev, sect, s, off, folio,
>                                             REQ_OP_READ) != 0)
>                                 atomic_add(s, &rdev->corrected_errors);
>                 }
>                 sectors -= s;
>                 sect += s;
> -               idx ++;
> +               off += s << 9;
>         }
>         set_bit(R1BIO_Uptodate, &r1_bio->state);
>         bio->bi_status = 0;
> @@ -2252,14 +2248,12 @@ static void process_checks(struct r1bio *r1_bio)
>         struct r1conf *conf = mddev->private;
>         int primary;
>         int i;
> -       int vcnt;
>
>         /* Fix variable parts of all bios */
> -       vcnt = (r1_bio->sectors + PAGE_SIZE / 512 - 1) >> (PAGE_SHIFT - 9);
>         for (i = 0; i < conf->raid_disks * 2; i++) {
>                 blk_status_t status;
>                 struct bio *b = r1_bio->bios[i];
> -               struct resync_pages *rp = get_resync_pages(b);
> +               struct resync_folio *rf = get_resync_folio(b);
>                 if (b->bi_end_io != end_sync_read)
>                         continue;
>                 /* fixup the bio for reuse, but preserve errno */
> @@ -2269,11 +2263,11 @@ static void process_checks(struct r1bio *r1_bio)
>                 b->bi_iter.bi_sector = r1_bio->sector +
>                         conf->mirrors[i].rdev->data_offset;
>                 b->bi_end_io = end_sync_read;
> -               rp->raid_bio = r1_bio;
> -               b->bi_private = rp;
> +               rf->raid_bio = r1_bio;
> +               b->bi_private = rf;
>
>                 /* initialize bvec table again */
> -               md_bio_reset_resync_pages(b, rp, r1_bio->sectors << 9);
> +               md_bio_reset_resync_folio(b, rf, r1_bio->sectors << 9);
>         }
>         for (primary = 0; primary < conf->raid_disks * 2; primary++)
>                 if (r1_bio->bios[primary]->bi_end_io == end_sync_read &&
> @@ -2284,44 +2278,39 @@ static void process_checks(struct r1bio *r1_bio)
>                 }
>         r1_bio->read_disk = primary;
>         for (i = 0; i < conf->raid_disks * 2; i++) {
> -               int j = 0;
>                 struct bio *pbio = r1_bio->bios[primary];
>                 struct bio *sbio = r1_bio->bios[i];
>                 blk_status_t status = sbio->bi_status;
> -               struct page **ppages = get_resync_pages(pbio)->pages;
> -               struct page **spages = get_resync_pages(sbio)->pages;
> -               struct bio_vec *bi;
> -               int page_len[RESYNC_PAGES] = { 0 };
> -               struct bvec_iter_all iter_all;
> +               struct folio *pfolio = get_resync_folio(pbio)->folio;
> +               struct folio *sfolio = get_resync_folio(sbio)->folio;
>
>                 if (sbio->bi_end_io != end_sync_read)
>                         continue;
>                 /* Now we can 'fixup' the error value */
>                 sbio->bi_status = 0;
>
> -               bio_for_each_segment_all(bi, sbio, iter_all)
> -                       page_len[j++] = bi->bv_len;
> -
> -               if (!status) {
> -                       for (j = vcnt; j-- ; ) {
> -                               if (memcmp(page_address(ppages[j]),
> -                                          page_address(spages[j]),
> -                                          page_len[j]))
> -                                       break;
> -                       }
> -               } else
> -                       j = 0;
> -               if (j >= 0)
> +               /*
> +                * Copy data and submit write in two cases:
> +                * - IO error (non-zero status)
> +                * - Data inconsistency and not a CHECK operation.
> +                */
> +               if (status) {
>                         atomic64_add(r1_bio->sectors, &mddev->resync_mismatches);
> -               if (j < 0 || (test_bit(MD_RECOVERY_CHECK, &mddev->recovery)
> -                             && !status)) {
> -                       /* No need to write to this device. */
> -                       sbio->bi_end_io = NULL;
> -                       rdev_dec_pending(conf->mirrors[i].rdev, mddev);
> +                       bio_copy_data(sbio, pbio);
>                         continue;
> +               } else if (memcmp(folio_address(pfolio),
> +                       folio_address(sfolio),
> +                       r1_bio->sectors << 9)) {
> +                       atomic64_add(r1_bio->sectors, &mddev->resync_mismatches);
> +                       if (!test_bit(MD_RECOVERY_CHECK, &mddev->recovery)) {
> +                               bio_copy_data(sbio, pbio);
> +                               continue;
> +                       }
>                 }
>
> -               bio_copy_data(sbio, pbio);
> +               /* No need to write to this device. */
> +               sbio->bi_end_io = NULL;
> +               rdev_dec_pending(conf->mirrors[i].rdev, mddev);
>         }
>  }
>
> @@ -2446,9 +2435,8 @@ static void fix_read_error(struct r1conf *conf, struct r1bio *r1_bio)
>                         if (rdev &&
>                             !test_bit(Faulty, &rdev->flags)) {
>                                 atomic_inc(&rdev->nr_pending);
> -                               r1_sync_page_io(rdev, sect, s,
> -                                               folio_page(conf->tmpfolio, 0),
> -                                               REQ_OP_WRITE);
> +                               r1_sync_folio_io(rdev, sect, s, 0,
> +                                               conf->tmpfolio, REQ_OP_WRITE);
>                                 rdev_dec_pending(rdev, mddev);
>                         }
>                 }
> @@ -2461,9 +2449,8 @@ static void fix_read_error(struct r1conf *conf, struct r1bio *r1_bio)
>                         if (rdev &&
>                             !test_bit(Faulty, &rdev->flags)) {
>                                 atomic_inc(&rdev->nr_pending);
> -                               if (r1_sync_page_io(rdev, sect, s,
> -                                               folio_page(conf->tmpfolio, 0),
> -                                               REQ_OP_READ)) {
> +                               if (r1_sync_folio_io(rdev, sect, s, 0,
> +                                               conf->tmpfolio, 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,
> @@ -2738,15 +2725,15 @@ static int init_resync(struct r1conf *conf)
>  static struct r1bio *raid1_alloc_init_r1buf(struct r1conf *conf)
>  {
>         struct r1bio *r1bio = mempool_alloc(&conf->r1buf_pool, GFP_NOIO);
> -       struct resync_pages *rps;
> +       struct resync_folio *rfs;
>         struct bio *bio;
>         int i;
>
>         for (i = conf->raid_disks * 2; i--; ) {
>                 bio = r1bio->bios[i];
> -               rps = bio->bi_private;
> +               rfs = bio->bi_private;
>                 bio_reset(bio, NULL, 0);
> -               bio->bi_private = rps;
> +               bio->bi_private = rfs;
>         }
>         r1bio->master_bio = NULL;
>         return r1bio;
> @@ -2775,10 +2762,9 @@ 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);
> -       int page_idx = 0;
>
>         if (!mempool_initialized(&conf->r1buf_pool))
>                 if (init_resync(conf))
> @@ -2858,8 +2844,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;
> @@ -2979,44 +2968,28 @@ 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;
> -       sync_blocks = 0;
>         do {
> -               struct page *page;
> -               int len = PAGE_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 (sync_blocks == 0) {
> -                       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 (!md_bitmap_start_sync(mddev, sector_nr,
> +                                         &sync_blocks, still_degraded) &&
> +                   !conf->fullsync &&
> +                   !test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery))
> +                       break;
> +               if (nr_sectors > sync_blocks)
> +                       nr_sectors = sync_blocks;
>
>                 for (i = 0 ; i < conf->raid_disks * 2; i++) {
> -                       struct resync_pages *rp;
> -
>                         bio = r1_bio->bios[i];
> -                       rp = get_resync_pages(bio);
>                         if (bio->bi_end_io) {
> -                               page = resync_fetch_page(rp, page_idx);
> +                               struct resync_folio *rf = get_resync_folio(bio);
>
> -                               /*
> -                                * won't fail because the vec table is big
> -                                * enough to hold all these pages
> -                                */
> -                               __bio_add_page(bio, page, len, 0);
> +                               bio_add_folio_nofail(bio, rf->folio, nr_sectors << 9, 0);
>                         }
>                 }
> -               nr_sectors += len>>9;
> -               sector_nr += len>>9;
> -               sync_blocks -= (len>>9);
> -       } while (++page_idx < RESYNC_PAGES);
> +               sector_nr += nr_sectors;
> +       } while (0);

Now it can handle all pages in one go via a folio. It's strange to
keep while(0) here.


>
>         r1_bio->sectors = nr_sectors;


This patch is a little big. Is it better to split this patch here?

>
> diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
> index 26f93040cd13..3638e00fe420 100644
> --- a/drivers/md/raid10.c
> +++ b/drivers/md/raid10.c
> @@ -96,11 +96,11 @@ static void end_reshape(struct r10conf *conf);
>
>  /*
>   * for resync bio, r10bio pointer can be retrieved from the per-bio
> - * 'struct resync_pages'.
> + * 'struct resync_folio'.
>   */
>  static inline struct r10bio *get_resync_r10bio(struct bio *bio)
>  {
> -       return get_resync_pages(bio)->raid_bio;
> +       return get_resync_folio(bio)->raid_bio;
>  }
>
>  static void * r10bio_pool_alloc(gfp_t gfp_flags, void *data)
> @@ -133,8 +133,9 @@ static void * r10buf_pool_alloc(gfp_t gfp_flags, void *data)
>         struct r10bio *r10_bio;
>         struct bio *bio;
>         int j;
> -       int nalloc, nalloc_rp;
> -       struct resync_pages *rps;
> +       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)
> @@ -148,66 +149,64 @@ static void * r10buf_pool_alloc(gfp_t gfp_flags, void *data)
>
>         /* allocate once for all bios */
>         if (!conf->have_replacement)
> -               nalloc_rp = nalloc;
> +               nalloc_rf = nalloc;
>         else
> -               nalloc_rp = nalloc * 2;
> -       rps = kmalloc_array(nalloc_rp, sizeof(struct resync_pages), gfp_flags);
> -       if (!rps)
> +               nalloc_rf = nalloc * 2;
> +       rfs = kmalloc_array(nalloc_rf, sizeof(struct resync_folio), gfp_flags);
> +       if (!rfs)
>                 goto out_free_r10bio;
>
>         /*
>          * Allocate bios.
>          */
>         for (j = nalloc ; j-- ; ) {
> -               bio = bio_kmalloc(RESYNC_PAGES, gfp_flags);
> +               bio = bio_kmalloc(1, gfp_flags);
>                 if (!bio)
>                         goto out_free_bio;
> -               bio_init_inline(bio, NULL, RESYNC_PAGES, 0);
> +               bio_init_inline(bio, NULL, 1, 0);
>                 r10_bio->devs[j].bio = bio;
>                 if (!conf->have_replacement)
>                         continue;
> -               bio = bio_kmalloc(RESYNC_PAGES, gfp_flags);
> +               bio = bio_kmalloc(1, gfp_flags);
>                 if (!bio)
>                         goto out_free_bio;
> -               bio_init_inline(bio, NULL, RESYNC_PAGES, 0);
> +               bio_init_inline(bio, NULL, 1, 0);
>                 r10_bio->devs[j].repl_bio = bio;
>         }
>         /*
> -        * Allocate RESYNC_PAGES data pages and attach them
> -        * where needed.
> +        * Allocate data folio and attach it where needed.
>          */
>         for (j = 0; j < nalloc; j++) {
>                 struct bio *rbio = r10_bio->devs[j].repl_bio;
> -               struct resync_pages *rp, *rp_repl;
> +               struct resync_folio *rf, *rf_repl;
>
> -               rp = &rps[j];
> +               rf = &rfs[j];
>                 if (rbio)
> -                       rp_repl = &rps[nalloc + j];
> -
> -               bio = r10_bio->devs[j].bio;
> +                       rf_repl = &rfs[nalloc + j];
>
>                 if (!j || test_bit(MD_RECOVERY_SYNC,
>                                    &conf->mddev->recovery)) {
> -                       if (resync_alloc_pages(rp, gfp_flags))
> -                               goto out_free_pages;
> +                       if (resync_alloc_folio(rf, gfp_flags, &order))
> +                               goto out_free_folio;
>                 } else {
> -                       memcpy(rp, &rps[0], sizeof(*rp));
> -                       resync_get_all_pages(rp);
> +                       memcpy(rf, &rfs[0], sizeof(*rf));
> +                       folio_get(rf->folio);
>                 }
>
> -               rp->raid_bio = r10_bio;
> -               bio->bi_private = rp;
> +               rf->raid_bio = r10_bio;
> +               r10_bio->devs[j].bio->bi_private = rf;
>                 if (rbio) {
> -                       memcpy(rp_repl, rp, sizeof(*rp));
> -                       rbio->bi_private = rp_repl;
> +                       memcpy(rf_repl, rf, sizeof(*rf));
> +                       rbio->bi_private = rf_repl;
>                 }
>         }
>
> +       r10_bio->sectors = 1 << (order + PAGE_SECTORS_SHIFT);
>         return r10_bio;
>
> -out_free_pages:
> +out_free_folio:
>         while (--j >= 0)
> -               resync_free_pages(&rps[j]);
> +               folio_put(rfs[j].folio);
>
>         j = 0;
>  out_free_bio:
> @@ -219,7 +218,7 @@ static void * r10buf_pool_alloc(gfp_t gfp_flags, void *data)
>                         bio_uninit(r10_bio->devs[j].repl_bio);
>                 kfree(r10_bio->devs[j].repl_bio);
>         }
> -       kfree(rps);
> +       kfree(rfs);
>  out_free_r10bio:
>         rbio_pool_free(r10_bio, conf);
>         return NULL;
> @@ -230,14 +229,14 @@ static void r10buf_pool_free(void *__r10_bio, void *data)
>         struct r10conf *conf = data;
>         struct r10bio *r10bio = __r10_bio;
>         int j;
> -       struct resync_pages *rp = NULL;
> +       struct resync_folio *rf = NULL;
>
>         for (j = conf->copies; j--; ) {
>                 struct bio *bio = r10bio->devs[j].bio;
>
>                 if (bio) {
> -                       rp = get_resync_pages(bio);
> -                       resync_free_pages(rp);
> +                       rf = get_resync_folio(bio);
> +                       folio_put(rf->folio);
>                         bio_uninit(bio);
>                         kfree(bio);
>                 }
> @@ -250,7 +249,7 @@ static void r10buf_pool_free(void *__r10_bio, void *data)
>         }
>
>         /* resync pages array stored in the 1st bio's .bi_private */
> -       kfree(rp);
> +       kfree(rf);
>
>         rbio_pool_free(r10bio, conf);
>  }
> @@ -2342,8 +2341,7 @@ static void sync_request_write(struct mddev *mddev, struct r10bio *r10_bio)
>         struct r10conf *conf = mddev->private;
>         int i, first;
>         struct bio *tbio, *fbio;
> -       int vcnt;
> -       struct page **tpages, **fpages;
> +       struct folio *tfolio, *ffolio;
>
>         atomic_set(&r10_bio->remaining, 1);
>
> @@ -2359,14 +2357,13 @@ static void sync_request_write(struct mddev *mddev, struct r10bio *r10_bio)
>         fbio = r10_bio->devs[i].bio;
>         fbio->bi_iter.bi_size = r10_bio->sectors << 9;
>         fbio->bi_iter.bi_idx = 0;
> -       fpages = get_resync_pages(fbio)->pages;
> +       ffolio = get_resync_folio(fbio)->folio;
>
> -       vcnt = (r10_bio->sectors + (PAGE_SIZE >> 9) - 1) >> (PAGE_SHIFT - 9);
>         /* now find blocks with errors */
>         for (i=0 ; i < conf->copies ; i++) {
> -               int  j, d;
> +               int  d;
>                 struct md_rdev *rdev;
> -               struct resync_pages *rp;
> +               struct resync_folio *rf;
>
>                 tbio = r10_bio->devs[i].bio;
>
> @@ -2375,31 +2372,23 @@ static void sync_request_write(struct mddev *mddev, struct r10bio *r10_bio)
>                 if (i == first)
>                         continue;
>
> -               tpages = get_resync_pages(tbio)->pages;
> +               tfolio = get_resync_folio(tbio)->folio;
>                 d = r10_bio->devs[i].devnum;
>                 rdev = conf->mirrors[d].rdev;
>                 if (!r10_bio->devs[i].bio->bi_status) {
>                         /* We know that the bi_io_vec layout is the same for
>                          * both 'first' and 'i', so we just compare them.
> -                        * All vec entries are PAGE_SIZE;
>                          */
> -                       int sectors = r10_bio->sectors;
> -                       for (j = 0; j < vcnt; j++) {
> -                               int len = PAGE_SIZE;
> -                               if (sectors < (len / 512))
> -                                       len = sectors * 512;
> -                               if (memcmp(page_address(fpages[j]),
> -                                          page_address(tpages[j]),
> -                                          len))
> -                                       break;
> -                               sectors -= len/512;
> +                       if (memcmp(folio_address(ffolio),
> +                                  folio_address(tfolio),
> +                                  r10_bio->sectors << 9)) {
> +                               atomic64_add(r10_bio->sectors,
> +                                            &mddev->resync_mismatches);
> +                               if (test_bit(MD_RECOVERY_CHECK,
> +                                            &mddev->recovery))
> +                                       /* Don't fix anything. */
> +                                       continue;
>                         }
> -                       if (j == vcnt)
> -                               continue;
> -                       atomic64_add(r10_bio->sectors, &mddev->resync_mismatches);
> -                       if (test_bit(MD_RECOVERY_CHECK, &mddev->recovery))
> -                               /* Don't fix anything. */
> -                               continue;
>                 } else if (test_bit(FailFast, &rdev->flags)) {
>                         /* Just give up on this device */
>                         md_error(rdev->mddev, rdev);
> @@ -2410,13 +2399,13 @@ static void sync_request_write(struct mddev *mddev, struct r10bio *r10_bio)
>                  * First we need to fixup bv_offset, bv_len and
>                  * bi_vecs, as the read request might have corrupted these
>                  */
> -               rp = get_resync_pages(tbio);
> +               rf = get_resync_folio(tbio);
>                 bio_reset(tbio, conf->mirrors[d].rdev->bdev, REQ_OP_WRITE);
>
> -               md_bio_reset_resync_pages(tbio, rp, fbio->bi_iter.bi_size);
> +               md_bio_reset_resync_folio(tbio, rf, fbio->bi_iter.bi_size);
>
> -               rp->raid_bio = r10_bio;
> -               tbio->bi_private = rp;
> +               rf->raid_bio = r10_bio;
> +               tbio->bi_private = rf;
>                 tbio->bi_iter.bi_sector = r10_bio->devs[i].addr;
>                 tbio->bi_end_io = end_sync_write;
>
> @@ -2476,10 +2465,9 @@ static void fix_recovery_read_error(struct r10bio *r10_bio)
>         struct bio *bio = r10_bio->devs[0].bio;
>         sector_t sect = 0;
>         int sectors = r10_bio->sectors;
> -       int idx = 0;
>         int dr = r10_bio->devs[0].devnum;
>         int dw = r10_bio->devs[1].devnum;
> -       struct page **pages = get_resync_pages(bio)->pages;
> +       struct folio *folio = get_resync_folio(bio)->folio;
>
>         while (sectors) {
>                 int s = sectors;
> @@ -2492,19 +2480,21 @@ static void fix_recovery_read_error(struct r10bio *r10_bio)
>
>                 rdev = conf->mirrors[dr].rdev;
>                 addr = r10_bio->devs[0].addr + sect;
> -               ok = sync_page_io(rdev,
> -                                 addr,
> -                                 s << 9,
> -                                 pages[idx],
> -                                 REQ_OP_READ, false);
> +               ok = sync_folio_io(rdev,
> +                                  addr,
> +                                  s << 9,
> +                                  sect << 9,
> +                                  folio,
> +                                  REQ_OP_READ, false);
>                 if (ok) {
>                         rdev = conf->mirrors[dw].rdev;
>                         addr = r10_bio->devs[1].addr + sect;
> -                       ok = sync_page_io(rdev,
> -                                         addr,
> -                                         s << 9,
> -                                         pages[idx],
> -                                         REQ_OP_WRITE, false);
> +                       ok = sync_folio_io(rdev,
> +                                          addr,
> +                                          s << 9,
> +                                          sect << 9,
> +                                          folio,
> +                                          REQ_OP_WRITE, false);
>                         if (!ok) {
>                                 set_bit(WriteErrorSeen, &rdev->flags);
>                                 if (!test_and_set_bit(WantReplacement,
> @@ -2539,7 +2529,6 @@ static void fix_recovery_read_error(struct r10bio *r10_bio)
>
>                 sectors -= s;
>                 sect += s;
> -               idx++;
>         }
>  }
>
> @@ -3050,7 +3039,7 @@ static int init_resync(struct r10conf *conf)
>  static struct r10bio *raid10_alloc_init_r10buf(struct r10conf *conf)
>  {
>         struct r10bio *r10bio = mempool_alloc(&conf->r10buf_pool, GFP_NOIO);
> -       struct rsync_pages *rp;
> +       struct resync_folio *rf;
>         struct bio *bio;
>         int nalloc;
>         int i;
> @@ -3063,14 +3052,14 @@ static struct r10bio *raid10_alloc_init_r10buf(struct r10conf *conf)
>
>         for (i = 0; i < nalloc; i++) {
>                 bio = r10bio->devs[i].bio;
> -               rp = bio->bi_private;
> +               rf = bio->bi_private;
>                 bio_reset(bio, NULL, 0);
> -               bio->bi_private = rp;
> +               bio->bi_private = rf;
>                 bio = r10bio->devs[i].repl_bio;
>                 if (bio) {
> -                       rp = bio->bi_private;
> +                       rf = bio->bi_private;
>                         bio_reset(bio, NULL, 0);
> -                       bio->bi_private = rp;
> +                       bio->bi_private = rf;
>                 }
>         }
>         return r10bio;
> @@ -3156,7 +3145,6 @@ static sector_t raid10_sync_request(struct mddev *mddev, sector_t sector_nr,
>         int max_sync = RESYNC_SECTORS;
>         sector_t sync_blocks;
>         sector_t chunk_mask = conf->geo.chunk_mask;
> -       int page_idx = 0;
>
>         /*
>          * Allow skipping a full rebuild for incremental assembly
> @@ -3376,6 +3364,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;
> @@ -3527,7 +3524,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;
> @@ -3620,29 +3625,25 @@ 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 {
> -               struct page *page;
> -               int len = PAGE_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;
>                 for (bio= biolist ; bio ; bio=bio->bi_next) {
> -                       struct resync_pages *rp = get_resync_pages(bio);
> -                       page = resync_fetch_page(rp, page_idx);
> -                       if (WARN_ON(!bio_add_page(bio, page, len, 0))) {
> +                       struct resync_folio *rf = get_resync_folio(bio);
> +
> +                       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 max_sync;
> +                               return nr_sectors << 9;
>                         }
>                 }
> -               nr_sectors += len>>9;
> -               sector_nr += len>>9;
> -       } while (++page_idx < RESYNC_PAGES);
> +               sector_nr += nr_sectors;
> +       } while (0);
>         r10_bio->sectors = nr_sectors;
>
>         if (mddev_is_clustered(mddev) &&
> @@ -4560,7 +4561,7 @@ static sector_t reshape_request(struct mddev *mddev, sector_t sector_nr,
>                                 int *skipped)
>  {
>         /* We simply copy at most one chunk (smallest of old and new)
> -        * at a time, possibly less if that exceeds RESYNC_PAGES,
> +        * at a time, possibly less if that exceeds RESYNC_BLOCK_SIZE,
>          * or we hit a bad block or something.
>          * This might mean we pause for normal IO in the middle of
>          * a chunk, but that is not a problem as mddev->reshape_position
> @@ -4600,14 +4601,13 @@ static sector_t reshape_request(struct mddev *mddev, sector_t sector_nr,
>         struct r10bio *r10_bio;
>         sector_t next, safe, last;
>         int max_sectors;
> -       int nr_sectors;
>         int s;
>         struct md_rdev *rdev;
>         int need_flush = 0;
>         struct bio *blist;
>         struct bio *bio, *read_bio;
>         int sectors_done = 0;
> -       struct page **pages;
> +       struct folio *folio;
>
>         if (sector_nr == 0) {
>                 /* If restarting in the middle, skip the initial sectors */
> @@ -4709,7 +4709,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));
>
> @@ -4723,7 +4728,7 @@ static sector_t reshape_request(struct mddev *mddev, sector_t sector_nr,
>                 return sectors_done;
>         }
>
> -       read_bio = bio_alloc_bioset(rdev->bdev, RESYNC_PAGES, REQ_OP_READ,
> +       read_bio = bio_alloc_bioset(rdev->bdev, 1, REQ_OP_READ,
>                                     GFP_KERNEL, &mddev->bio_set);
>         read_bio->bi_iter.bi_sector = (r10_bio->devs[r10_bio->read_slot].addr
>                                + rdev->data_offset);
> @@ -4787,32 +4792,23 @@ static sector_t reshape_request(struct mddev *mddev, sector_t sector_nr,
>                 blist = b;
>         }
>
> -       /* Now add as many pages as possible to all of these bios. */
> +       /* Now add folio to all of these bios. */
>
> -       nr_sectors = 0;
> -       pages = get_resync_pages(r10_bio->devs[0].bio)->pages;
> -       for (s = 0 ; s < max_sectors; s += PAGE_SIZE >> 9) {
> -               struct page *page = pages[s / (PAGE_SIZE >> 9)];
> -               int len = (max_sectors - s) << 9;
> -               if (len > PAGE_SIZE)
> -                       len = PAGE_SIZE;
> -               for (bio = blist; bio ; bio = bio->bi_next) {
> -                       if (WARN_ON(!bio_add_page(bio, page, len, 0))) {
> -                               bio->bi_status = BLK_STS_RESOURCE;
> -                               bio_endio(bio);
> -                               return sectors_done;
> -                       }
> +       folio = get_resync_folio(r10_bio->devs[0].bio)->folio;
> +       for (bio = blist; bio ; bio = bio->bi_next) {
> +               if (WARN_ON(!bio_add_folio(bio, folio, max_sectors, 0))) {
> +                       bio->bi_status = BLK_STS_RESOURCE;
> +                       bio_endio(bio);
> +                       return sectors_done;

In fact, the original codes don't clean up before returning.
bio_add_folio_nofail is used in raid1 and can we use
bio_add_folio_nofail here as well?

>                 }
> -               sector_nr += len >> 9;
> -               nr_sectors += len >> 9;
>         }
> -       r10_bio->sectors = nr_sectors;
> +       r10_bio->sectors = max_sectors >> 9;
>
>         /* Now submit the read */
>         atomic_inc(&r10_bio->remaining);
>         read_bio->bi_next = NULL;
>         submit_bio_noacct(read_bio);
> -       sectors_done += nr_sectors;
> +       sectors_done += max_sectors;
>         if (sector_nr <= last)
>                 goto read_more;
>
> @@ -4914,8 +4910,8 @@ static int handle_reshape_read_error(struct mddev *mddev,
>         struct r10conf *conf = mddev->private;
>         struct r10bio *r10b;
>         int slot = 0;
> -       int idx = 0;
> -       struct page **pages;
> +       int sect = 0;
> +       struct folio *folio;
>
>         r10b = kmalloc(struct_size(r10b, devs, conf->copies), GFP_NOIO);
>         if (!r10b) {
> @@ -4923,8 +4919,8 @@ static int handle_reshape_read_error(struct mddev *mddev,
>                 return -ENOMEM;
>         }
>
> -       /* reshape IOs share pages from .devs[0].bio */
> -       pages = get_resync_pages(r10_bio->devs[0].bio)->pages;
> +       /* reshape IOs share folio from .devs[0].bio */
> +       folio = get_resync_folio(r10_bio->devs[0].bio)->folio;
>
>         r10b->sector = r10_bio->sector;
>         __raid10_find_phys(&conf->prev, r10b);
> @@ -4940,19 +4936,19 @@ static int handle_reshape_read_error(struct mddev *mddev,
>                 while (!success) {
>                         int d = r10b->devs[slot].devnum;
>                         struct md_rdev *rdev = conf->mirrors[d].rdev;
> -                       sector_t addr;
>                         if (rdev == NULL ||
>                             test_bit(Faulty, &rdev->flags) ||
>                             !test_bit(In_sync, &rdev->flags))
>                                 goto failed;
>
> -                       addr = r10b->devs[slot].addr + idx * PAGE_SIZE;
>                         atomic_inc(&rdev->nr_pending);
> -                       success = sync_page_io(rdev,
> -                                              addr,
> -                                              s << 9,
> -                                              pages[idx],
> -                                              REQ_OP_READ, false);
> +                       success = sync_folio_io(rdev,
> +                                               r10b->devs[slot].addr +
> +                                               sect,
> +                                               s << 9,
> +                                               sect << 9,
> +                                               folio,
> +                                               REQ_OP_READ, false);
>                         rdev_dec_pending(rdev, mddev);
>                         if (success)
>                                 break;
> @@ -4971,7 +4967,7 @@ static int handle_reshape_read_error(struct mddev *mddev,
>                         return -EIO;
>                 }
>                 sectors -= s;
> -               idx++;
> +               sect += s;
>         }
>         kfree(r10b);
>         return 0;
> --
> 2.39.2
>
>

Regards
Xiao


^ permalink raw reply

* Re: [PATCH] md/raid5: split reshape bios before bitmap accounting
From: kernel test robot @ 2026-04-30  0:59 UTC (permalink / raw)
  To: Yu Kuai, linux-raid
  Cc: llvm, oe-kbuild-all, linux-kernel, Li Nan, Yu Kuai, Cheng Cheng
In-Reply-To: <20260419030942.824195-20-yukuai@fnnas.com>

Hi Yu,

kernel test robot noticed the following build errors:

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

url:    https://github.com/intel-lab-lkp/linux/commits/Yu-Kuai/md-raid5-split-reshape-bios-before-bitmap-accounting/20260425-083941
base:   linus/master
patch link:    https://lore.kernel.org/r/20260419030942.824195-20-yukuai%40fnnas.com
patch subject: [PATCH] md/raid5: split reshape bios before bitmap accounting
config: um-randconfig-001-20260430 (https://download.01.org/0day-ci/archive/20260430/202604300803.nq5tYBQB-lkp@intel.com/config)
compiler: clang version 23.0.0git (https://github.com/llvm/llvm-project 5bac06718f502014fade905512f1d26d578a18f3)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260430/202604300803.nq5tYBQB-lkp@intel.com/reproduce)

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

All errors (new ones prefixed by >>):

   drivers/md/raid5.c:4221:7: warning: variable 'qread' set but not used [-Wunused-but-set-variable]
    4221 |                 int qread =0;
         |                     ^
>> drivers/md/raid5.c:6126:7: error: call to undeclared function 'mddev_bio_split_at_reshape_offset'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
    6126 |         bi = mddev_bio_split_at_reshape_offset(mddev, bi, NULL,
         |              ^
>> drivers/md/raid5.c:6126:5: error: incompatible integer to pointer conversion assigning to 'struct bio *' from 'int' [-Wint-conversion]
    6126 |         bi = mddev_bio_split_at_reshape_offset(mddev, bi, NULL,
         |            ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    6127 |                                                &conf->bio_split);
         |                                                ~~~~~~~~~~~~~~~~~
   1 warning and 2 errors generated.


vim +/mddev_bio_split_at_reshape_offset +6126 drivers/md/raid5.c

  6083	
  6084	static bool raid5_make_request(struct mddev *mddev, struct bio * bi)
  6085	{
  6086		DEFINE_WAIT_FUNC(wait, woken_wake_function);
  6087		struct r5conf *conf = mddev->private;
  6088		const int rw = bio_data_dir(bi);
  6089		struct stripe_request_ctx *ctx;
  6090		sector_t logical_sector;
  6091		enum stripe_result res;
  6092		int s, stripe_cnt;
  6093		bool on_wq;
  6094	
  6095		if (unlikely(bi->bi_opf & REQ_PREFLUSH)) {
  6096			int ret = log_handle_flush_request(conf, bi);
  6097	
  6098			if (ret == 0)
  6099				return true;
  6100			if (ret == -ENODEV) {
  6101				if (md_flush_request(mddev, bi))
  6102					return true;
  6103			}
  6104			/* ret == -EAGAIN, fallback */
  6105		}
  6106	
  6107		md_write_start(mddev, bi);
  6108		/*
  6109		 * If array is degraded, better not do chunk aligned read because
  6110		 * later we might have to read it again in order to reconstruct
  6111		 * data on failed drives.
  6112		 */
  6113		if (rw == READ && mddev->degraded == 0 &&
  6114		    mddev->reshape_position == MaxSector) {
  6115			bi = chunk_aligned_read(mddev, bi);
  6116			if (!bi)
  6117				return true;
  6118		}
  6119	
  6120		if (unlikely(bio_op(bi) == REQ_OP_DISCARD)) {
  6121			make_discard_request(mddev, bi);
  6122			md_write_end(mddev);
  6123			return true;
  6124		}
  6125	
> 6126		bi = mddev_bio_split_at_reshape_offset(mddev, bi, NULL,
  6127						       &conf->bio_split);
  6128		if (!bi) {
  6129			if (rw == WRITE)
  6130				md_write_end(mddev);
  6131			return true;
  6132		}
  6133	
  6134		logical_sector = bi->bi_iter.bi_sector & ~((sector_t)RAID5_STRIPE_SECTORS(conf)-1);
  6135		bi->bi_next = NULL;
  6136	
  6137		ctx = mempool_alloc(conf->ctx_pool, GFP_NOIO);
  6138		memset(ctx, 0, conf->ctx_size);
  6139		ctx->first_sector = logical_sector;
  6140		ctx->last_sector = bio_end_sector(bi);
  6141		/*
  6142		 * if r5l_handle_flush_request() didn't clear REQ_PREFLUSH,
  6143		 * we need to flush journal device
  6144		 */
  6145		if (unlikely(bi->bi_opf & REQ_PREFLUSH))
  6146			ctx->do_flush = true;
  6147	
  6148		stripe_cnt = DIV_ROUND_UP_SECTOR_T(ctx->last_sector - logical_sector,
  6149						   RAID5_STRIPE_SECTORS(conf));
  6150		bitmap_set(ctx->sectors_to_do, 0, stripe_cnt);
  6151	
  6152		pr_debug("raid456: %s, logical %llu to %llu\n", __func__,
  6153			 bi->bi_iter.bi_sector, ctx->last_sector);
  6154	
  6155		/* Bail out if conflicts with reshape and REQ_NOWAIT is set */
  6156		if ((bi->bi_opf & REQ_NOWAIT) &&
  6157		    get_reshape_loc(mddev, conf, logical_sector) == LOC_INSIDE_RESHAPE) {
  6158			bio_wouldblock_error(bi);
  6159			if (rw == WRITE)
  6160				md_write_end(mddev);
  6161			mempool_free(ctx, conf->ctx_pool);
  6162			return true;
  6163		}
  6164		md_account_bio(mddev, &bi);
  6165	
  6166		/*
  6167		 * Lets start with the stripe with the lowest chunk offset in the first
  6168		 * chunk. That has the best chances of creating IOs adjacent to
  6169		 * previous IOs in case of sequential IO and thus creates the most
  6170		 * sequential IO pattern. We don't bother with the optimization when
  6171		 * reshaping as the performance benefit is not worth the complexity.
  6172		 */
  6173		if (likely(conf->reshape_progress == MaxSector)) {
  6174			logical_sector = raid5_bio_lowest_chunk_sector(conf, bi);
  6175			on_wq = false;
  6176		} else {
  6177			add_wait_queue(&conf->wait_for_reshape, &wait);
  6178			on_wq = true;
  6179		}
  6180		s = (logical_sector - ctx->first_sector) >> RAID5_STRIPE_SHIFT(conf);
  6181	
  6182		while (1) {
  6183			res = make_stripe_request(mddev, conf, ctx, logical_sector,
  6184						  bi);
  6185			if (res == STRIPE_FAIL || res == STRIPE_WAIT_RESHAPE)
  6186				break;
  6187	
  6188			if (res == STRIPE_RETRY)
  6189				continue;
  6190	
  6191			if (res == STRIPE_SCHEDULE_AND_RETRY) {
  6192				WARN_ON_ONCE(!on_wq);
  6193				/*
  6194				 * Must release the reference to batch_last before
  6195				 * scheduling and waiting for work to be done,
  6196				 * otherwise the batch_last stripe head could prevent
  6197				 * raid5_activate_delayed() from making progress
  6198				 * and thus deadlocking.
  6199				 */
  6200				if (ctx->batch_last) {
  6201					raid5_release_stripe(ctx->batch_last);
  6202					ctx->batch_last = NULL;
  6203				}
  6204	
  6205				wait_woken(&wait, TASK_UNINTERRUPTIBLE,
  6206					   MAX_SCHEDULE_TIMEOUT);
  6207				continue;
  6208			}
  6209	
  6210			s = find_next_bit_wrap(ctx->sectors_to_do, stripe_cnt, s);
  6211			if (s == stripe_cnt)
  6212				break;
  6213	
  6214			logical_sector = ctx->first_sector +
  6215				(s << RAID5_STRIPE_SHIFT(conf));
  6216		}
  6217		if (unlikely(on_wq))
  6218			remove_wait_queue(&conf->wait_for_reshape, &wait);
  6219	
  6220		if (ctx->batch_last)
  6221			raid5_release_stripe(ctx->batch_last);
  6222	
  6223		if (rw == WRITE)
  6224			md_write_end(mddev);
  6225	
  6226		mempool_free(ctx, conf->ctx_pool);
  6227		if (res == STRIPE_WAIT_RESHAPE) {
  6228			md_free_cloned_bio(bi);
  6229			return false;
  6230		}
  6231	
  6232		bio_endio(bi);
  6233		return true;
  6234	}
  6235	

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

^ permalink raw reply

* Re: Suggest me a cost effective SATA SSD?
From: Roman Mamedov @ 2026-04-29 23:20 UTC (permalink / raw)
  To: Andy Smith; +Cc: linux-raid
In-Reply-To: <afKLmiyhEe4Om0Nt@mail.bitfolk.com>

On Wed, 29 Apr 2026 22:52:10 +0000
Andy Smith <andy@strugglers.net> wrote:

> Model Family:     Crucial/Micron Client SSDs
> Device Model:     CT4000BX500SSD1
> User Capacity:    4,000,787,030,016 bytes [4.00 TB]
> 
> Their write performance is terrible. Struggling to get 20MB/s sequential
> write. Their TBW count is low so it's not an issue of excessive write
> cycles. I've searched around and established they are just really bad
> SSDs. They also don't support SMART self-tests, which seems like a
> really cheap thing to do.
> 
> I'm further confident that the problem lies with these SSDs because
> there is a pair of much better SSDs in there and they perform as I would
> expect. However, the use case for this storage is for lower cost so it's
> not an option to just buy more of those.
> 
> So, could anyone suggest a decent low end (consumer/prosumer market) SSD
> model that is known to work well without terrible firmware bugs under
> Linux, preferably with power loss protection? Low write endurance is
> fine. Capacity of 4TB ideally.

Basically the main thing to avoid is QLC flash, and look for TLC instead.

Low write speeds and low endurance are all due to the QLC, which prioritizes
capacity and low cost in favor of performance and reliability.

Consult the SSD database at https://www.techpowerup.com/ssd-specs/ about your
potential choices that you see in shops, for what flash type they have.

Or conversely, filter by TLC: https://www.techpowerup.com/ssd-specs/?type=TLC
and then add search term of your favorite vendor to look for candidates to buy.

Keep in mind this info may not be 100% reliable, as it is crowd-sourced and
way too often hidden by the manufacturers, or flash type is switched back and
forth even within the same model.

Side note, I would not count on SMART self tests as being relevant on an SSD.
Sure, on HDD they can scan the entire surface looking for unreadable areas. On
SSDs, what they could do? Read out the entire flash? From the duration of even
the extended offline tests on SSD, they are not doing that. It is opaque and
proprietary as to what they actually do, if anything.

-- 
With respect,
Roman

^ permalink raw reply

* Suggest me a cost effective SATA SSD?
From: Andy Smith @ 2026-04-29 22:52 UTC (permalink / raw)
  To: linux-raid

Hi,

I've inherited a system with a pair of these:

Model Family:     Crucial/Micron Client SSDs
Device Model:     CT4000BX500SSD1
User Capacity:    4,000,787,030,016 bytes [4.00 TB]

Their write performance is terrible. Struggling to get 20MB/s sequential
write. Their TBW count is low so it's not an issue of excessive write
cycles. I've searched around and established they are just really bad
SSDs. They also don't support SMART self-tests, which seems like a
really cheap thing to do.

I'm further confident that the problem lies with these SSDs because
there is a pair of much better SSDs in there and they perform as I would
expect. However, the use case for this storage is for lower cost so it's
not an option to just buy more of those.

So, could anyone suggest a decent low end (consumer/prosumer market) SSD
model that is known to work well without terrible firmware bugs under
Linux, preferably with power loss protection? Low write endurance is
fine. Capacity of 4TB ideally.

Thanks,
Andy

^ permalink raw reply

* Re: [PATCH] md/raid5: Fix bio retry on interrupted reshape
From: Paul Menzel @ 2026-04-29 13:07 UTC (permalink / raw)
  To: Nigel Croxon; +Cc: song, yukuai, linux-raid
In-Reply-To: <f861791b-d539-45ce-8575-5ec74d58d15d@redhat.com>

Dear Nigel,


Thank you for your patch.

Am 29.04.26 um 13:10 schrieb Nigel Croxon:
> When a bio encounters LOC_INSIDE_RESHAPE during a reshape that is
> interrupted (stopped or unable to progress), the code sets
> bi->bi_status = BLK_STS_RESOURCE to signal the block layer for retry.
> However, bio_endio() is never called, so the block layer never
> receives the completion notification and the retry never happens.
> 
> This causes I/O to hang when a filesystem is layered over RAID5 and
> reshape gets stuck.
> 
> Fix this by calling bio_endio(bi) before md_free_cloned_bio(bi) so
> the block layer is properly notified of the BLK_STS_RESOURCE status
> and can retry the request.
> 
> Tested stripes and stripe size conversions under load comparing
> files multiple times during each conversion (i.e. MD reshape) on
> ext4 after dropping caches degrading the RaidLV each time and

I thought RaidLV misspelled Raid V (Raid 5), so should you resend, maybe 
write it as RAID LV.

> no data corruption.
> 
> Fixes: https://lwn.net/Articles/757123/

Which paragraph/comment exactly?

> Signed-off-by: Nigel Croxon <ncroxon@redhat.com>
> ---
>   drivers/md/raid5.c | 1 +
>   1 file changed, 1 insertion(+)
> 
> diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
> index 6e79829c5acb..9a3475429ef4 100644
> --- a/drivers/md/raid5.c
> +++ b/drivers/md/raid5.c
> @@ -6217,6 +6217,7 @@ static bool raid5_make_request(struct mddev 
> *mddev, struct bio * bi)
> 
>       mempool_free(ctx, conf->ctx_pool);
>       if (res == STRIPE_WAIT_RESHAPE) {
> +         bio_endio(bi);
>           md_free_cloned_bio(bi);
>           return false;
>       }

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


Kind regards,

Paul

^ permalink raw reply

* [PATCH] md/raid5: Fix bio retry on interrupted reshape
From: Nigel Croxon @ 2026-04-29 11:10 UTC (permalink / raw)
  To: song, yukuai, linux-raid

When a bio encounters LOC_INSIDE_RESHAPE during a reshape that is
interrupted (stopped or unable to progress), the code sets
bi->bi_status = BLK_STS_RESOURCE to signal the block layer for retry.
However, bio_endio() is never called, so the block layer never
receives the completion notification and the retry never happens.

This causes I/O to hang when a filesystem is layered over RAID5 and
reshape gets stuck.

Fix this by calling bio_endio(bi) before md_free_cloned_bio(bi) so
the block layer is properly notified of the BLK_STS_RESOURCE status
and can retry the request.

Tested stripes and stripe size conversions under load comparing
files multiple times during each conversion (i.e. MD reshape) on
ext4 after dropping caches degrading the RaidLV each time and
no data corruption.

Fixes: https://lwn.net/Articles/757123/

Signed-off-by: Nigel Croxon <ncroxon@redhat.com>
---
  drivers/md/raid5.c | 1 +
  1 file changed, 1 insertion(+)

diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index 6e79829c5acb..9a3475429ef4 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -6217,6 +6217,7 @@ static bool raid5_make_request(struct mddev 
*mddev, struct bio * bi)

      mempool_free(ctx, conf->ctx_pool);
      if (res == STRIPE_WAIT_RESHAPE) {
+        bio_endio(bi);
          md_free_cloned_bio(bi);
          return false;
      }
-- 
2.47.3


^ permalink raw reply related

* [PATCH v2] dm-raid: only requeue bios when dm is suspending.
From: Benjamin Marzinski @ 2026-04-28 23:20 UTC (permalink / raw)
  To: Yu Kuai, Song Liu
  Cc: linux-raid, dm-devel, Yang Xiuwei, Xiao Ni, Li Nan, Nigel Croxon

returning DM_MAPIO_REQUEUE from the target map() function only requeues
the bio during noflush suspends. During regular operations or during
flushing suspends, it fails the bio. Failing the bio during flushing
suspends is the correct behavior here. We cannot handle the bio, and we
cannot suspends while it is outstanding. But during normal operations,
we should not push the bio back to dm. Instead, wait for the reshape
to be resumed.

Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---

Changes from v1:
- Track the dm device's suspending state in mddev->flags instead of
  adding a new integer to mddev.

 drivers/md/dm-raid.c | 6 ++++++
 drivers/md/md.h      | 2 ++
 drivers/md/raid5.c   | 7 +++++--
 3 files changed, 13 insertions(+), 2 deletions(-)

diff --git a/drivers/md/dm-raid.c b/drivers/md/dm-raid.c
index c5dc083c7244..8f5a5e1342a9 100644
--- a/drivers/md/dm-raid.c
+++ b/drivers/md/dm-raid.c
@@ -3831,6 +3831,7 @@ static void raid_presuspend(struct dm_target *ti)
 	 * resume, raid_postsuspend() is too late.
 	 */
 	set_bit(RT_FLAG_RS_FROZEN, &rs->runtime_flags);
+	set_bit(MD_DM_SUSPENDING, &mddev->flags);
 
 	if (!reshape_interrupted(mddev))
 		return;
@@ -3847,13 +3848,16 @@ static void raid_presuspend(struct dm_target *ti)
 static void raid_presuspend_undo(struct dm_target *ti)
 {
 	struct raid_set *rs = ti->private;
+	struct mddev *mddev = &rs->md;
 
+	clear_bit(MD_DM_SUSPENDING, &mddev->flags);
 	clear_bit(RT_FLAG_RS_FROZEN, &rs->runtime_flags);
 }
 
 static void raid_postsuspend(struct dm_target *ti)
 {
 	struct raid_set *rs = ti->private;
+	struct mddev *mddev = &rs->md;
 
 	if (!test_and_set_bit(RT_FLAG_RS_SUSPENDED, &rs->runtime_flags)) {
 		/*
@@ -3864,6 +3868,8 @@ static void raid_postsuspend(struct dm_target *ti)
 		mddev_suspend(&rs->md, false);
 		rs->md.ro = MD_RDONLY;
 	}
+	clear_bit(MD_DM_SUSPENDING, &mddev->flags);
+
 }
 
 static void attempt_restore_of_faulty_devices(struct raid_set *rs)
diff --git a/drivers/md/md.h b/drivers/md/md.h
index 52c378086046..9e5100609d12 100644
--- a/drivers/md/md.h
+++ b/drivers/md/md.h
@@ -346,6 +346,7 @@ struct md_cluster_operations;
  * @MD_HAS_SUPERBLOCK: There is persistence sb in member disks.
  * @MD_FAILLAST_DEV: Allow last rdev to be removed.
  * @MD_SERIALIZE_POLICY: Enforce write IO is not reordered, just used by raid1.
+ * @MD_DM_SUSPENDING: This DM raid device is suspending.
  *
  * change UNSUPPORTED_MDDEV_FLAGS for each array type if new flag is added
  */
@@ -365,6 +366,7 @@ enum mddev_flags {
 	MD_HAS_SUPERBLOCK,
 	MD_FAILLAST_DEV,
 	MD_SERIALIZE_POLICY,
+	MD_DM_SUSPENDING,
 };
 
 enum mddev_sb_flags {
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index 0d76e82f4506..65ae7d8930fc 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -6042,8 +6042,11 @@ static enum stripe_result make_stripe_request(struct mddev *mddev,
 	raid5_release_stripe(sh);
 out:
 	if (ret == STRIPE_SCHEDULE_AND_RETRY && reshape_interrupted(mddev)) {
-		bi->bi_status = BLK_STS_RESOURCE;
-		ret = STRIPE_WAIT_RESHAPE;
+		if (!mddev_is_dm(mddev) ||
+		    test_bit(MD_DM_SUSPENDING, &mddev->flags)) {
+			bi->bi_status = BLK_STS_RESOURCE;
+			ret = STRIPE_WAIT_RESHAPE;
+		}
 		pr_err_ratelimited("dm-raid456: io across reshape position while reshape can't make progress");
 	}
 	return ret;
-- 
2.53.0


^ permalink raw reply related

* Re: [GIT PULL] md-7.1-20260428
From: Jens Axboe @ 2026-04-28 14:41 UTC (permalink / raw)
  To: Yu Kuai
  Cc: linux-block, linux-raid, Song Liu, Li Nan, Xiao Ni,
	Abd-Alrhman Masalkhi, Benjamin Marzinski, Junrui Luo, Keith Busch
In-Reply-To: <20260428143340.1088943-1-yukuai@fnnas.com>

On 4/28/26 8:33 AM, Yu Kuai wrote:
> Hi Jens,
> 
> Please consider pulling the following changes into your block-7.1
> branch.
> 
> This pull request contains:
> 
> Bug Fixes:
> - Fix a raid5 UAF on IO across the reshape position.
> - Avoid failing RAID1/RAID10 devices for invalid IO errors.
> - Fix RAID10 divide-by-zero when far_copies is zero.
> - Restore bitmap grow through sysfs.
> 
> Cleanups:
> - Use mddev_is_dm() instead of open-coding gendisk checks.
> - Use ATTRIBUTE_GROUPS() for md default sysfs attributes.
> - Replace open-coded wait loops with wait_event helpers.
> 
> Others:
> - Add Xiao Ni as md/raid reviewer.

Two notes:

1) Why are you rebasing the tree right before sending it? There
   should be zero need to do that, please don't.

2) Please switch to https://patch.msgid.link/ for you Link tags,
   if they are just links to the patch submission. That makes it
   clear this is the case, just the patch. You can use lore for
   actual bug reports etc.

Pulled, but please change the above two things going forward.

-- 
Jens Axboe


^ permalink raw reply

* [GIT PULL] md-7.1-20260428
From: Yu Kuai @ 2026-04-28 14:33 UTC (permalink / raw)
  To: Jens Axboe
  Cc: linux-block, linux-raid, Song Liu, Li Nan, Xiao Ni,
	Abd-Alrhman Masalkhi, Benjamin Marzinski, Junrui Luo, Keith Busch

Hi Jens,

Please consider pulling the following changes into your block-7.1
branch.

This pull request contains:

Bug Fixes:
- Fix a raid5 UAF on IO across the reshape position.
- Avoid failing RAID1/RAID10 devices for invalid IO errors.
- Fix RAID10 divide-by-zero when far_copies is zero.
- Restore bitmap grow through sysfs.

Cleanups:
- Use mddev_is_dm() instead of open-coding gendisk checks.
- Use ATTRIBUTE_GROUPS() for md default sysfs attributes.
- Replace open-coded wait loops with wait_event helpers.

Others:
- Add Xiao Ni as md/raid reviewer.

Thanks,
Kuai

---

The following changes since commit 0898a817621a2f0cddca8122d9b974003fe5036d:

  cdrom, scsi: sr: propagate read-only status to block layer via set_disk_ro() (2026-04-27 15:52:51 -0600)

are available in the Git repository at:

  https://git.kernel.org/pub/scm/linux/kernel/git/mdraid/linux.git tags/md-7.1-20260428

for you to fetch changes up to 3b2f70eab5a2cd15e27b1447e66e45302b28ff2c:

  md: use ATTRIBUTE_GROUPS() for md default sysfs attributes (2026-04-28 20:44:38 +0800)

----------------------------------------------------------------
Abd-Alrhman Masalkhi (5):
      md: replace wait loop with wait_event() in md_handle_request()
      md: use mddev_lock_nointr() in mddev_suspend_and_lock_nointr()
      md/raid1: replace wait loop with wait_event_idle() in raid1_write_request()
      md: use mddev_is_dm() instead of open-coding gendisk checks
      md: use ATTRIBUTE_GROUPS() for md default sysfs attributes

Benjamin Marzinski (1):
      md/raid5: Fix UAF on IO across the reshape position

Junrui Luo (1):
      md/raid10: fix divide-by-zero in setup_geo() with zero far_copies

Keith Busch (1):
      md/raid1,raid10: don't fail devices for invalid IO errors

Xiao Ni (1):
      MAINTAINERS: Add Xiao Ni as md/raid reviewer

Yu Kuai (3):
      md: factor bitmap creation away from sysfs handling
      md/md-bitmap: split bitmap sysfs groups
      md/md-bitmap: add a none backend for bitmap grow

 MAINTAINERS              |   1 +
 drivers/md/md-bitmap.c   | 131 ++++++++++++++++++++++++++++++----
 drivers/md/md-bitmap.h   |   2 +-
 drivers/md/md-llbitmap.c |   7 +-
 drivers/md/md.c          | 182 ++++++++++++++++++++++++++---------------------
 drivers/md/md.h          |   6 +-
 drivers/md/raid1-10.c    |   7 +-
 drivers/md/raid1.c       |  15 ++--
 drivers/md/raid10.c      |   2 +
 drivers/md/raid5.c       |   7 +-
 10 files changed, 251 insertions(+), 109 deletions(-)

^ permalink raw reply

* [PATCH v2] md: skip redundant raid_disks update when value is unchanged
From: Abd-Alrhman Masalkhi @ 2026-04-28 13:05 UTC (permalink / raw)
  To: song, yukuai; +Cc: linux-raid, linux-kernel, Abd-Alrhman Masalkhi

Calling update_raid_disks() with the same value as the current one
can trigger unnecessary work. For example, RAID1 will reallocate
resources such as the mempool for r1bio.

Signed-off-by: Abd-Alrhman Masalkhi <abd.masalkhi@gmail.com>
---
 drivers/md/md.c | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/drivers/md/md.c b/drivers/md/md.c
index ac71640ff3a8..21b8ad17685f 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -4409,9 +4409,10 @@ raid_disks_store(struct mddev *mddev, const char *buf, size_t len)
 	err = mddev_suspend_and_lock(mddev);
 	if (err)
 		return err;
-	if (mddev->pers)
-		err = update_raid_disks(mddev, n);
-	else if (mddev->reshape_position != MaxSector) {
+	if (mddev->pers) {
+		if (n != mddev->raid_disks)
+			err = update_raid_disks(mddev, n);
+	} else if (mddev->reshape_position != MaxSector) {
 		struct md_rdev *rdev;
 		int olddisks = mddev->raid_disks - mddev->delta_disks;
 
-- 
2.43.0


^ permalink raw reply related

* Re: [PATCH] md/raid1: fix len reuse across rdevs in choose_first_rdev()
From: Abd-Alrhman Masalkhi @ 2026-04-28 11:53 UTC (permalink / raw)
  To: Yu Kuai, song, paul.e.luse, xni, yukuai; +Cc: linux-raid, linux-kernel
In-Reply-To: <5c71cb6b-d860-4bcb-a900-a27544be7a17@fnnas.com>


Hi Kaui,

On Tue, Apr 28, 2026 at 16:23 +0800, Yu Kuai wrote:
> Hi,
>
> 在 2026/4/26 17:35, Abd-Alrhman Masalkhi 写道:
>> choose_first_rdev() initializes the variable len before iterating over
>> all rdevs, but passes it by reference to raid1_check_read_range(), which
>> it might update *len and return 0 depending on the layout of the bad
>> block region. As a result, 'len' can be modified during the first
>> iteration and reused for subsequent rdevs, causing later devices to be
>> evaluated with an incorrect length value.
>>
>> Fixes: 31a73331752d3 ("md/raid1: factor out read_first_rdev() from read_balance()")
>> Signed-off-by: Abd-Alrhman Masalkhi <abd.masalkhi@gmail.com>
>> ---
>>   drivers/md/raid1.c | 2 +-
>>   1 file changed, 1 insertion(+), 1 deletion(-)
>>
>> diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
>> index b549be9174bb..5f5dbf79c903 100644
>> --- a/drivers/md/raid1.c
>> +++ b/drivers/md/raid1.c
>> @@ -591,12 +591,12 @@ static int choose_first_rdev(struct r1conf *conf, struct r1bio *r1_bio,
>>   			     int *max_sectors)
>>   {
>>   	sector_t this_sector = r1_bio->sector;
>> -	int len = r1_bio->sectors;
>>   	int disk;
>>   
>>   	for (disk = 0 ; disk < conf->raid_disks * 2 ; disk++) {
>>   		struct md_rdev *rdev;
>>   		int read_len;
>> +		int len = r1_bio->sectors;
>>   
>>   		if (r1_bio->bios[disk] == IO_BLOCKED)
>>   			continue;
>
> This patch is wrong, choose_first_rdev() is used when raid1_should_read_first() is true,
> meaning the read overlaps an unsynced/resyncing area. Reset len can cause the problem that
> reading the same area can return different data.
>

Thank you for the detailed explanation. After carefully re-reading the
code and your feedback, I understand why the patch is wrong.

> -- 
> Thansk,
> Kuai

-- 
Best Regards,
Abd-Alrhman

^ 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