Linux RAID subsystem development
 help / color / mirror / Atom feed
* [PATCH v2 06/14] md: Clean up folio sync support related code
From: linan666 @ 2026-01-28  7:57 UTC (permalink / raw)
  To: song, yukuai; +Cc: xni, linux-raid, linux-kernel, linan666, yangerkun, yi.zhang
In-Reply-To: <20260128075708.2259525-1-linan666@huaweicloud.com>

From: Li Nan <linan122@huawei.com>

1. Remove resync_get_all_folio() and invoke folio_get() directly instead.
2. Clean up redundant while(0) loop in md_bio_reset_resync_folio().
3. Clean up bio variable by directly referencing r10_bio->devs[j].bio
   instead in r1buf_pool_alloc() and r10buf_pool_alloc().
4. Clean up RESYNC_PAGES.

Signed-off-by: Li Nan <linan122@huawei.com>
Reviewed-by: Xiao Ni <xni@redhat.com>
---
 drivers/md/raid1-10.c | 22 ++++++----------------
 drivers/md/raid1.c    |  6 ++----
 drivers/md/raid10.c   |  6 ++----
 3 files changed, 10 insertions(+), 24 deletions(-)

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


^ permalink raw reply related

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

From: Li Nan <linan122@huawei.com>

The helper resync_fetch_folio() only returns the folio member without
any additional logic. Clean it up by accessing rf->folio directly.

Signed-off-by: Li Nan <linan122@huawei.com>
---
 drivers/md/raid1-10.c |  7 +------
 drivers/md/raid1.c    | 10 ++++------
 drivers/md/raid10.c   |  3 +--
 3 files changed, 6 insertions(+), 14 deletions(-)

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


^ permalink raw reply related

* [PATCH v2 00/14] folio support for sync I/O in RAID
From: linan666 @ 2026-01-28  7:56 UTC (permalink / raw)
  To: song, yukuai; +Cc: xni, linux-raid, linux-kernel, linan666, yangerkun, yi.zhang

From: Li Nan <linan122@huawei.com>

This patchset adds folio support to sync operations in raid1/10.
Previously, we used 16 * 4K pages for 64K sync I/O. With this change,
we'll use a single 64K folio instead. Using folios reduces
resync/recovery time by 20% on HDD.

This is the first step towards full folio support in RAID. Going forward,
I will replace the remaining page-based usage with folio.

The patchset was tested with mdadm. Additional fault injection stress tests
were run under file systems.

v2:
 - Remove patch "md: use folio for bb_folio". It will be included in
   a later patch set
 - In patch 5:
    1) fix typo
    2) rewrite the logic of copying data in process_checks()
    3) rename resync_get_all_folio() to resync_get_folio()
    4) s/resync_pages *rps/resync_folio *rfs/g in
       raid1_alloc_init_r1buf() and raid10_alloc_init_r10buf()
 - Subsequent patches: Adapting conflicts caused by patch 5

Li Nan (14):
  md/raid1,raid10: clean up of RESYNC_SECTORS
  md: introduce sync_folio_io for folio support in RAID
  md/raid1: use folio for tmppage
  md/raid10: use folio for tmppage
  md/raid1,raid10: use folio for sync path IO
  md: Clean up folio sync support related code
  md/raid1: clean up useless sync_blocks handling in raid1_sync_request
  md/raid1: fix IO error at logical block size granularity
  md/raid10: fix IO error at logical block size granularity
  md/raid1,raid10: clean up resync_fetch_folio
  md: clean up resync_free_folio
  md/raid1: clean up sync IO size calculation in raid1_sync_request
  md/raid10: clean up sync IO size calculation in raid10_sync_request
  md/raid1,raid10: fall back to smaller order if sync folio alloc fails

 drivers/md/md.h       |   2 +
 drivers/md/raid1.h    |   2 +-
 drivers/md/raid10.h   |   2 +-
 drivers/md/md.c       |  29 +++-
 drivers/md/raid1-10.c |  81 ++++-------
 drivers/md/raid1.c    | 233 ++++++++++++++-----------------
 drivers/md/raid10.c   | 315 ++++++++++++++++++++----------------------
 7 files changed, 308 insertions(+), 356 deletions(-)

-- 
2.39.2


^ permalink raw reply

* [PATCH v2 02/14] md: introduce sync_folio_io for folio support in RAID
From: linan666 @ 2026-01-28  7:56 UTC (permalink / raw)
  To: song, yukuai; +Cc: xni, linux-raid, linux-kernel, linan666, yangerkun, yi.zhang
In-Reply-To: <20260128075708.2259525-1-linan666@huaweicloud.com>

From: Li Nan <linan122@huawei.com>

Prepare for folio support in RAID by introducing sync_folio_io(),
matching sync_page_io()'s functionality. Differences are:

- Replace input parameter 'page' with 'folio'
- Replace __bio_add_page() calls with bio_add_folio_nofail()
- Add new parameter 'off' to prepare for adding a folio to bio in segments,
  e.g. in fix_recovery_read_error()

sync_page_io() will be removed once full folio support is complete.

Signed-off-by: Li Nan <linan122@huawei.com>
---
 drivers/md/md.h |  2 ++
 drivers/md/md.c | 27 +++++++++++++++++++++++++++
 2 files changed, 29 insertions(+)

diff --git a/drivers/md/md.h b/drivers/md/md.h
index a083f37374d0..410f8a6b75e7 100644
--- a/drivers/md/md.h
+++ b/drivers/md/md.h
@@ -920,6 +920,8 @@ void md_write_metadata(struct mddev *mddev, struct md_rdev *rdev,
 extern int md_super_wait(struct mddev *mddev);
 extern int sync_page_io(struct md_rdev *rdev, sector_t sector, int size,
 		struct page *page, blk_opf_t opf, bool metadata_op);
+extern int sync_folio_io(struct md_rdev *rdev, sector_t sector, int size,
+		int off, struct folio *folio, blk_opf_t opf, bool metadata_op);
 extern void md_do_sync(struct md_thread *thread);
 extern void md_new_event(void);
 extern void md_allow_write(struct mddev *mddev);
diff --git a/drivers/md/md.c b/drivers/md/md.c
index 5df2220b1bd1..b8c8a16cf037 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -1192,6 +1192,33 @@ int sync_page_io(struct md_rdev *rdev, sector_t sector, int size,
 }
 EXPORT_SYMBOL_GPL(sync_page_io);
 
+int sync_folio_io(struct md_rdev *rdev, sector_t sector, int size, int off,
+		  struct folio *folio, blk_opf_t opf, bool metadata_op)
+{
+	struct bio bio;
+	struct bio_vec bvec;
+
+	if (metadata_op && rdev->meta_bdev)
+		bio_init(&bio, rdev->meta_bdev, &bvec, 1, opf);
+	else
+		bio_init(&bio, rdev->bdev, &bvec, 1, opf);
+
+	if (metadata_op)
+		bio.bi_iter.bi_sector = sector + rdev->sb_start;
+	else if (rdev->mddev->reshape_position != MaxSector &&
+		 (rdev->mddev->reshape_backwards ==
+		  (sector >= rdev->mddev->reshape_position)))
+		bio.bi_iter.bi_sector = sector + rdev->new_data_offset;
+	else
+		bio.bi_iter.bi_sector = sector + rdev->data_offset;
+	bio_add_folio_nofail(&bio, folio, size, off);
+
+	submit_bio_wait(&bio);
+
+	return !bio.bi_status;
+}
+EXPORT_SYMBOL_GPL(sync_folio_io);
+
 static int read_disk_sb(struct md_rdev *rdev, int size)
 {
 	if (rdev->sb_loaded)
-- 
2.39.2


^ permalink raw reply related

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

From: Li Nan <linan122@huawei.com>

Since the loop is changed to while(0), some handling of sync_blocks
in raid1_sync_request() is no longer needed and can be removed.

No functional changes.

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

diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
index 5954ead7dfd4..2308e16b1280 100644
--- a/drivers/md/raid1.c
+++ b/drivers/md/raid1.c
@@ -2985,7 +2985,6 @@ static sector_t raid1_sync_request(struct mddev *mddev, sector_t sector_nr,
 	if (max_sector > sector_nr + good_sectors)
 		max_sector = sector_nr + good_sectors;
 	nr_sectors = 0;
-	sync_blocks = 0;
 	do {
 		struct folio *folio;
 		int len = RESYNC_BLOCK_SIZE;
@@ -2993,15 +2992,13 @@ static sector_t raid1_sync_request(struct mddev *mddev, sector_t sector_nr,
 			len = (max_sector - sector_nr) << 9;
 		if (len == 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 ((len >> 9) > sync_blocks)
+			len = sync_blocks<<9;
 
 		for (i = 0 ; i < conf->raid_disks * 2; i++) {
 			struct resync_folio *rf;
@@ -3015,7 +3012,6 @@ static sector_t raid1_sync_request(struct mddev *mddev, sector_t sector_nr,
 		}
 		nr_sectors += len>>9;
 		sector_nr += len>>9;
-		sync_blocks -= (len>>9);
 	} while (0);
 
 	r1_bio->sectors = nr_sectors;
-- 
2.39.2


^ permalink raw reply related

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

From: Li Nan <linan122@huawei.com>

RAID10 currently fixes IO error at PAGE_SIZE granularity. Fix at smaller
granularity can handle more errors, and RAID will support logical block
sizes larger than PAGE_SIZE in the future, where PAGE_SIZE IO will fail.

Switch IO error fix granularity to logical block size.

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

diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index 5c0975ec8809..06257eea97ed 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -2452,7 +2452,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
@@ -2468,14 +2468,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,
@@ -2619,14 +2616,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;
@@ -4925,16 +4919,14 @@ 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;
+
 			if (rdev == NULL ||
 			    test_bit(Faulty, &rdev->flags) ||
 			    !test_bit(In_sync, &rdev->flags))
-- 
2.39.2


^ permalink raw reply related

* [PATCH v2 05/14] md/raid1,raid10: use folio for sync path IO
From: linan666 @ 2026-01-28  7:56 UTC (permalink / raw)
  To: song, yukuai; +Cc: xni, linux-raid, linux-kernel, linan666, yangerkun, yi.zhang
In-Reply-To: <20260128075708.2259525-1-linan666@huaweicloud.com>

From: Li Nan <linan122@huawei.com>

Convert all IO on the sync path to use folios. Rename page-related
identifiers to match folio.

Retain some now-unnecessary while and for loops to minimize code
changes, clean them up in a subsequent patch.

Signed-off-by: Li Nan <linan122@huawei.com>
---
 drivers/md/md.c       |   2 +-
 drivers/md/raid1-10.c |  60 ++++--------
 drivers/md/raid1.c    | 173 +++++++++++++++------------------
 drivers/md/raid10.c   | 219 +++++++++++++++++++-----------------------
 4 files changed, 196 insertions(+), 258 deletions(-)

diff --git a/drivers/md/md.c b/drivers/md/md.c
index b8c8a16cf037..5b42b157263d 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -9450,7 +9450,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 260d7fd7ccbe..300fbe9dc02e 100644
--- a/drivers/md/raid1-10.c
+++ b/drivers/md/raid1-10.c
@@ -25,9 +25,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 {
@@ -41,77 +41,55 @@ static void rbio_pool_free(void *rbio, void *data)
 	kfree(rbio);
 }
 
-static inline int resync_alloc_pages(struct resync_pages *rp,
+static inline int resync_alloc_folio(struct resync_folio *rf,
 				     gfp_t gfp_flags)
 {
-	int i;
-
-	for (i = 0; i < RESYNC_PAGES; i++) {
-		rp->pages[i] = alloc_page(gfp_flags);
-		if (!rp->pages[i])
-			goto out_free;
-	}
+	rf->folio = folio_alloc(gfp_flags, get_order(RESYNC_BLOCK_SIZE));
+	if (!rf->folio)
+		return -ENOMEM;
 
 	return 0;
-
-out_free:
-	while (--i >= 0)
-		put_page(rp->pages[i]);
-	return -ENOMEM;
 }
 
-static inline void resync_free_pages(struct resync_pages *rp)
+static inline void resync_free_folio(struct resync_folio *rf)
 {
-	int i;
-
-	for (i = 0; i < RESYNC_PAGES; i++)
-		put_page(rp->pages[i]);
+	folio_put(rf->folio);
 }
 
-static inline void resync_get_all_pages(struct resync_pages *rp)
+static inline void resync_get_folio(struct resync_folio *rf)
 {
-	int i;
-
-	for (i = 0; i < RESYNC_PAGES; i++)
-		get_page(rp->pages[i]);
+	folio_get(rf->folio);
 }
 
-static inline struct page *resync_fetch_page(struct resync_pages *rp,
-					     unsigned idx)
+static inline struct folio *resync_fetch_folio(struct resync_folio *rf)
 {
-	if (WARN_ON_ONCE(idx >= RESYNC_PAGES))
-		return NULL;
-	return rp->pages[idx];
+	return rf->folio;
 }
 
 /*
- * '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);
+		struct folio *folio = resync_fetch_folio(rf);
+		int len = min_t(int, size, RESYNC_BLOCK_SIZE);
 
-		if (WARN_ON(!bio_add_page(bio, page, len, 0))) {
+		if (WARN_ON(!bio_add_folio(bio, folio, len, 0))) {
 			bio->bi_status = BLK_STS_RESOURCE;
 			bio_endio(bio);
 			return;
 		}
-
-		size -= len;
-	} while (idx++ < RESYNC_PAGES && size > 0);
+	} while (0);
 }
 
 
diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
index 43453f1a04f4..d9c106529289 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;
 	int j;
-	struct resync_pages *rps;
+	struct resync_folio *rfs;
 
 	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))
+				goto out_free_folio;
 		} else {
-			memcpy(rp, &rps[0], sizeof(*rp));
-			resync_get_all_pages(rp);
+			memcpy(rf, &rfs[0], sizeof(*rf));
+			resync_get_folio(rf);
 		}
 
-		rp->raid_bio = r1_bio;
-		bio->bi_private = rp;
+		rf->raid_bio = r1_bio;
+		bio->bi_private = rf;
 	}
 
 	r1_bio->master_bio = NULL;
 
 	return r1_bio;
 
-out_free_pages:
+out_free_folio:
 	while (--j >= 0)
-		resync_free_pages(&rps[j]);
+		resync_free_folio(&rfs[j]);
 
 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]);
+		resync_free_folio(rf);
 		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,
@@ -2759,15 +2746,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;
@@ -2799,7 +2786,6 @@ static sector_t raid1_sync_request(struct mddev *mddev, sector_t sector_nr,
 	int good_sectors = RESYNC_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))
@@ -3003,8 +2989,8 @@ static sector_t raid1_sync_request(struct mddev *mddev, sector_t sector_nr,
 	nr_sectors = 0;
 	sync_blocks = 0;
 	do {
-		struct page *page;
-		int len = PAGE_SIZE;
+		struct folio *folio;
+		int len = RESYNC_BLOCK_SIZE;
 		if (sector_nr + (len>>9) > max_sector)
 			len = (max_sector - sector_nr) << 9;
 		if (len == 0)
@@ -3020,24 +3006,19 @@ static sector_t raid1_sync_request(struct mddev *mddev, sector_t sector_nr,
 		}
 
 		for (i = 0 ; i < conf->raid_disks * 2; i++) {
-			struct resync_pages *rp;
+			struct resync_folio *rf;
 
 			bio = r1_bio->bios[i];
-			rp = get_resync_pages(bio);
+			rf = get_resync_folio(bio);
 			if (bio->bi_end_io) {
-				page = resync_fetch_page(rp, page_idx);
-
-				/*
-				 * won't fail because the vec table is big
-				 * enough to hold all these pages
-				 */
-				__bio_add_page(bio, page, len, 0);
+				folio = resync_fetch_folio(rf);
+				bio_add_folio_nofail(bio, folio, len, 0);
 			}
 		}
 		nr_sectors += len>>9;
 		sector_nr += len>>9;
 		sync_blocks -= (len>>9);
-	} while (++page_idx < RESYNC_PAGES);
+	} while (0);
 
 	r1_bio->sectors = nr_sectors;
 
diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index 09238dc9cde6..7533aeb23819 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,8 @@ 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;
 
 	r10_bio = r10bio_pool_alloc(gfp_flags, conf);
 	if (!r10_bio)
@@ -148,66 +148,65 @@ 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];
+			rf_repl = &rfs[nalloc + j];
 
 		bio = r10_bio->devs[j].bio;
 
 		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))
+				goto out_free_folio;
 		} else {
-			memcpy(rp, &rps[0], sizeof(*rp));
-			resync_get_all_pages(rp);
+			memcpy(rf, &rfs[0], sizeof(*rf));
+			resync_get_folio(rf);
 		}
 
-		rp->raid_bio = r10_bio;
-		bio->bi_private = rp;
+		rf->raid_bio = r10_bio;
+		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;
 		}
 	}
 
 	return r10_bio;
 
-out_free_pages:
+out_free_folio:
 	while (--j >= 0)
-		resync_free_pages(&rps[j]);
+		resync_free_folio(&rfs[j]);
 
 	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);
+			resync_free_folio(rf);
 			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++;
 	}
 }
 
@@ -3068,7 +3057,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;
@@ -3081,14 +3070,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;
@@ -3174,7 +3163,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
@@ -3642,25 +3630,26 @@ static sector_t raid10_sync_request(struct mddev *mddev, sector_t sector_nr,
 	if (sector_nr + max_sync < max_sector)
 		max_sector = sector_nr + max_sync;
 	do {
-		struct page *page;
-		int len = PAGE_SIZE;
+		int len = RESYNC_BLOCK_SIZE;
+
 		if (sector_nr + (len>>9) > max_sector)
 			len = (max_sector - sector_nr) << 9;
 		if (len == 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);
+			struct folio *folio = resync_fetch_folio(rf);
+
+			if (WARN_ON(!bio_add_folio(bio, folio, len, 0))) {
 				bio->bi_status = BLK_STS_RESOURCE;
 				bio_endio(bio);
 				*skipped = 1;
-				return max_sync;
+				return len;
 			}
 		}
 		nr_sectors += len>>9;
 		sector_nr += len>>9;
-	} while (++page_idx < RESYNC_PAGES);
+	} while (0);
 	r10_bio->sectors = nr_sectors;
 
 	if (mddev_is_clustered(mddev) &&
@@ -4578,7 +4567,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
@@ -4618,14 +4607,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 */
@@ -4741,7 +4729,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);
@@ -4805,32 +4793,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;
 		}
-		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;
 
@@ -4932,8 +4911,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) {
@@ -4941,8 +4920,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);
@@ -4958,19 +4937,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;
@@ -4989,7 +4968,7 @@ static int handle_reshape_read_error(struct mddev *mddev,
 			return -EIO;
 		}
 		sectors -= s;
-		idx++;
+		sect += s;
 	}
 	kfree(r10b);
 	return 0;
-- 
2.39.2


^ permalink raw reply related

* [PATCH v2 03/14] md/raid1: use folio for tmppage
From: linan666 @ 2026-01-28  7:56 UTC (permalink / raw)
  To: song, yukuai; +Cc: xni, linux-raid, linux-kernel, linan666, yangerkun, yi.zhang
In-Reply-To: <20260128075708.2259525-1-linan666@huaweicloud.com>

From: Li Nan <linan122@huawei.com>

Convert tmppage to tmpfolio and use it throughout in raid1.

Signed-off-by: Li Nan <linan122@huawei.com>
Reviewed-by: Xiao Ni <xni@redhat.com>
---
 drivers/md/raid1.h |  2 +-
 drivers/md/raid1.c | 18 ++++++++++--------
 2 files changed, 11 insertions(+), 9 deletions(-)

diff --git a/drivers/md/raid1.h b/drivers/md/raid1.h
index c98d43a7ae99..d480b3a8c2c4 100644
--- a/drivers/md/raid1.h
+++ b/drivers/md/raid1.h
@@ -101,7 +101,7 @@ struct r1conf {
 	/* temporary buffer to synchronous IO when attempting to repair
 	 * a read error.
 	 */
-	struct page		*tmppage;
+	struct folio		*tmpfolio;
 
 	/* When taking over an array from a different personality, we store
 	 * the new thread here until we fully activate the array.
diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
index 407925951299..43453f1a04f4 100644
--- a/drivers/md/raid1.c
+++ b/drivers/md/raid1.c
@@ -2417,8 +2417,8 @@ static void fix_read_error(struct r1conf *conf, struct r1bio *r1_bio)
 			      rdev->recovery_offset >= sect + s)) &&
 			    rdev_has_badblock(rdev, sect, s) == 0) {
 				atomic_inc(&rdev->nr_pending);
-				if (sync_page_io(rdev, sect, s<<9,
-					 conf->tmppage, REQ_OP_READ, false))
+				if (sync_folio_io(rdev, sect, s<<9, 0,
+					 conf->tmpfolio, REQ_OP_READ, false))
 					success = 1;
 				rdev_dec_pending(rdev, mddev);
 				if (success)
@@ -2447,7 +2447,8 @@ static void fix_read_error(struct r1conf *conf, struct r1bio *r1_bio)
 			    !test_bit(Faulty, &rdev->flags)) {
 				atomic_inc(&rdev->nr_pending);
 				r1_sync_page_io(rdev, sect, s,
-						conf->tmppage, REQ_OP_WRITE);
+						folio_page(conf->tmpfolio, 0),
+						REQ_OP_WRITE);
 				rdev_dec_pending(rdev, mddev);
 			}
 		}
@@ -2461,7 +2462,8 @@ static void fix_read_error(struct r1conf *conf, struct r1bio *r1_bio)
 			    !test_bit(Faulty, &rdev->flags)) {
 				atomic_inc(&rdev->nr_pending);
 				if (r1_sync_page_io(rdev, sect, s,
-						conf->tmppage, REQ_OP_READ)) {
+						folio_page(conf->tmpfolio, 0),
+						REQ_OP_READ)) {
 					atomic_add(s, &rdev->corrected_errors);
 					pr_info("md/raid1:%s: read error corrected (%d sectors at %llu on %pg)\n",
 						mdname(mddev), s,
@@ -3120,8 +3122,8 @@ static struct r1conf *setup_conf(struct mddev *mddev)
 	if (!conf->mirrors)
 		goto abort;
 
-	conf->tmppage = alloc_page(GFP_KERNEL);
-	if (!conf->tmppage)
+	conf->tmpfolio = folio_alloc(GFP_KERNEL, 0);
+	if (!conf->tmpfolio)
 		goto abort;
 
 	r1bio_size = offsetof(struct r1bio, bios[mddev->raid_disks * 2]);
@@ -3196,7 +3198,7 @@ static struct r1conf *setup_conf(struct mddev *mddev)
 	if (conf) {
 		mempool_destroy(conf->r1bio_pool);
 		kfree(conf->mirrors);
-		safe_put_page(conf->tmppage);
+		folio_put(conf->tmpfolio);
 		kfree(conf->nr_pending);
 		kfree(conf->nr_waiting);
 		kfree(conf->nr_queued);
@@ -3310,7 +3312,7 @@ static void raid1_free(struct mddev *mddev, void *priv)
 
 	mempool_destroy(conf->r1bio_pool);
 	kfree(conf->mirrors);
-	safe_put_page(conf->tmppage);
+	folio_put(conf->tmpfolio);
 	kfree(conf->nr_pending);
 	kfree(conf->nr_waiting);
 	kfree(conf->nr_queued);
-- 
2.39.2


^ permalink raw reply related

* [PATCH v2 04/14] md/raid10: use folio for tmppage
From: linan666 @ 2026-01-28  7:56 UTC (permalink / raw)
  To: song, yukuai; +Cc: xni, linux-raid, linux-kernel, linan666, yangerkun, yi.zhang
In-Reply-To: <20260128075708.2259525-1-linan666@huaweicloud.com>

From: Li Nan <linan122@huawei.com>

Convert tmppage to tmpfolio and use it throughout in raid10.

Signed-off-by: Li Nan <linan122@huawei.com>
Reviewed-by: Xiao Ni <xni@redhat.com>
---
 drivers/md/raid10.h |  2 +-
 drivers/md/raid10.c | 37 +++++++++++++++++++------------------
 2 files changed, 20 insertions(+), 19 deletions(-)

diff --git a/drivers/md/raid10.h b/drivers/md/raid10.h
index ec79d87fb92f..19f37439a4e2 100644
--- a/drivers/md/raid10.h
+++ b/drivers/md/raid10.h
@@ -89,7 +89,7 @@ struct r10conf {
 
 	mempool_t		r10bio_pool;
 	mempool_t		r10buf_pool;
-	struct page		*tmppage;
+	struct folio		*tmpfolio;
 	struct bio_set		bio_split;
 
 	/* When taking over an array from a different personality, we store
diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index 1e57d9ce98e7..09238dc9cde6 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -2581,13 +2581,13 @@ static void recovery_request_write(struct mddev *mddev, struct r10bio *r10_bio)
 	}
 }
 
-static int r10_sync_page_io(struct md_rdev *rdev, sector_t sector,
-			    int sectors, struct page *page, enum req_op op)
+static int r10_sync_folio_io(struct md_rdev *rdev, sector_t sector,
+			    int sectors, struct folio *folio, enum req_op op)
 {
 	if (rdev_has_badblock(rdev, sector, sectors) &&
 	    (op == REQ_OP_READ || test_bit(WriteErrorSeen, &rdev->flags)))
 		return -1;
-	if (sync_page_io(rdev, sector, sectors << 9, page, op, false))
+	if (sync_folio_io(rdev, sector, sectors << 9, 0, folio, op, false))
 		/* success */
 		return 1;
 	if (op == REQ_OP_WRITE) {
@@ -2650,12 +2650,13 @@ static void fix_read_error(struct r10conf *conf, struct mddev *mddev, struct r10
 					      r10_bio->devs[sl].addr + sect,
 					      s) == 0) {
 				atomic_inc(&rdev->nr_pending);
-				success = sync_page_io(rdev,
-						       r10_bio->devs[sl].addr +
-						       sect,
-						       s<<9,
-						       conf->tmppage,
-						       REQ_OP_READ, false);
+				success = sync_folio_io(rdev,
+							r10_bio->devs[sl].addr +
+							sect,
+							s<<9,
+							0,
+							conf->tmpfolio,
+							REQ_OP_READ, false);
 				rdev_dec_pending(rdev, mddev);
 				if (success)
 					break;
@@ -2698,10 +2699,10 @@ static void fix_read_error(struct r10conf *conf, struct mddev *mddev, struct r10
 				continue;
 
 			atomic_inc(&rdev->nr_pending);
-			if (r10_sync_page_io(rdev,
-					     r10_bio->devs[sl].addr +
-					     sect,
-					     s, conf->tmppage, REQ_OP_WRITE)
+			if (r10_sync_folio_io(rdev,
+					      r10_bio->devs[sl].addr +
+					      sect,
+					      s, conf->tmpfolio, REQ_OP_WRITE)
 			    == 0) {
 				/* Well, this device is dead */
 				pr_notice("md/raid10:%s: read correction write failed (%d sectors at %llu on %pg)\n",
@@ -2730,10 +2731,10 @@ static void fix_read_error(struct r10conf *conf, struct mddev *mddev, struct r10
 				continue;
 
 			atomic_inc(&rdev->nr_pending);
-			switch (r10_sync_page_io(rdev,
+			switch (r10_sync_folio_io(rdev,
 					     r10_bio->devs[sl].addr +
 					     sect,
-					     s, conf->tmppage, REQ_OP_READ)) {
+					     s, conf->tmpfolio, REQ_OP_READ)) {
 			case 0:
 				/* Well, this device is dead */
 				pr_notice("md/raid10:%s: unable to read back corrected sectors (%d sectors at %llu on %pg)\n",
@@ -3841,7 +3842,7 @@ static void raid10_free_conf(struct r10conf *conf)
 	kfree(conf->mirrors);
 	kfree(conf->mirrors_old);
 	kfree(conf->mirrors_new);
-	safe_put_page(conf->tmppage);
+	folio_put(conf->tmpfolio);
 	bioset_exit(&conf->bio_split);
 	kfree(conf);
 }
@@ -3879,8 +3880,8 @@ static struct r10conf *setup_conf(struct mddev *mddev)
 	if (!conf->mirrors)
 		goto out;
 
-	conf->tmppage = alloc_page(GFP_KERNEL);
-	if (!conf->tmppage)
+	conf->tmpfolio = folio_alloc(GFP_KERNEL, 0);
+	if (!conf->tmpfolio)
 		goto out;
 
 	conf->geo = geo;
-- 
2.39.2


^ permalink raw reply related

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

From: Li Nan <linan122@huawei.com>

Move redundant RESYNC_SECTORS definition from raid1 and raid10
implementations to raid1-10.c.

Simplify max_sync assignment in raid10_sync_request().

No functional changes.

Signed-off-by: Li Nan <linan122@huawei.com>
---
 drivers/md/raid1-10.c | 1 +
 drivers/md/raid1.c    | 1 -
 drivers/md/raid10.c   | 4 +---
 3 files changed, 2 insertions(+), 4 deletions(-)

diff --git a/drivers/md/raid1-10.c b/drivers/md/raid1-10.c
index 521625756128..260d7fd7ccbe 100644
--- a/drivers/md/raid1-10.c
+++ b/drivers/md/raid1-10.c
@@ -2,6 +2,7 @@
 /* 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)
 
 /*
  * Number of guaranteed raid bios in case of extreme VM load:
diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
index 00120c86c443..407925951299 100644
--- a/drivers/md/raid1.c
+++ b/drivers/md/raid1.c
@@ -136,7 +136,6 @@ static void *r1bio_pool_alloc(gfp_t gfp_flags, struct r1conf *conf)
 }
 
 #define RESYNC_DEPTH 32
-#define RESYNC_SECTORS (RESYNC_BLOCK_SIZE >> 9)
 #define RESYNC_WINDOW (RESYNC_BLOCK_SIZE * RESYNC_DEPTH)
 #define RESYNC_WINDOW_SECTORS (RESYNC_WINDOW >> 9)
 #define CLUSTER_RESYNC_WINDOW (16 * RESYNC_WINDOW)
diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index 1adad768e277..1e57d9ce98e7 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -113,7 +113,6 @@ static void * r10bio_pool_alloc(gfp_t gfp_flags, void *data)
 	return kzalloc(size, gfp_flags);
 }
 
-#define RESYNC_SECTORS (RESYNC_BLOCK_SIZE >> 9)
 /* amount of memory to reserve for resync requests */
 #define RESYNC_WINDOW (1024*1024)
 /* maximum number of concurrent requests, memory permitting */
@@ -3171,7 +3170,7 @@ static sector_t raid10_sync_request(struct mddev *mddev, sector_t sector_nr,
 	struct bio *biolist = NULL, *bio;
 	sector_t nr_sectors;
 	int i;
-	int max_sync;
+	int max_sync = RESYNC_SECTORS;
 	sector_t sync_blocks;
 	sector_t chunk_mask = conf->geo.chunk_mask;
 	int page_idx = 0;
@@ -3284,7 +3283,6 @@ static sector_t raid10_sync_request(struct mddev *mddev, sector_t sector_nr,
 	 * end_sync_write if we will want to write.
 	 */
 
-	max_sync = RESYNC_PAGES << (PAGE_SHIFT-9);
 	if (!test_bit(MD_RECOVERY_SYNC, &mddev->recovery)) {
 		/* recovery... the complicated one */
 		int j;
-- 
2.39.2


^ permalink raw reply related

* Re: [PATCH V3 4/6] nvmet: ignore discard return value
From: Chaitanya Kulkarni @ 2026-01-27 22:40 UTC (permalink / raw)
  To: hch@lst.de
  Cc: Chaitanya Kulkarni, axboe@kernel.dk, agk@redhat.com,
	snitzer@kernel.org, mpatocka@redhat.com, song@kernel.org,
	yukuai@fnnas.com, sagi@grimberg.me, jaegeuk@kernel.org,
	chao@kernel.org, cem@kernel.org, linux-block@vger.kernel.org,
	linux-kernel@vger.kernel.org, dm-devel@lists.linux.dev,
	linux-raid@vger.kernel.org, linux-nvme@lists.infradead.org,
	linux-f2fs-devel@lists.sourceforge.net, linux-xfs@vger.kernel.org,
	bpf@vger.kernel.org, Martin K . Petersen, Johannes Thumshirn
In-Reply-To: <20260126045716.GA31683@lst.de>

On 1/25/26 20:57, hch@lst.de wrote:
> On Sat, Jan 24, 2026 at 09:35:16PM +0000, Chaitanya Kulkarni wrote:
>> On 11/24/25 15:48, Chaitanya Kulkarni wrote:
>>> __blkdev_issue_discard() always returns 0, making the error checking
>>> in nvmet_bdev_discard_range() dead code.
>>>
>>> Kill the function nvmet_bdev_discard_range() and call
>>> __blkdev_issue_discard() directly from nvmet_bdev_execute_discard(),
>>> since no error handling is needed anymore for __blkdev_issue_discard()
>>> call.
>>>
>>> Reviewed-by: Martin K. Petersen <martin.petersen@oracle.com>
>>> Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
>>> Reviewed-by: Christoph Hellwig <hch@lst.de>
>>> Signed-off-by: Chaitanya Kulkarni <ckulkarnilinux@gmail.com>
>>> ---
>> Gentle ping on this, can we apply this patch ?
> Are we down to three patches now?  Maybe resend the whole series and
> get ACKs to merge everything through the block layer?
>
sounds good, will re-spin the remaining patches on linux-block/for-next.

-ck



^ permalink raw reply

* Re: [PATCH 1/1] md: fix return value of mddev_trylock
From: Bart Van Assche @ 2026-01-27 17:09 UTC (permalink / raw)
  To: Xiao Ni, yukuai; +Cc: linux-raid
In-Reply-To: <20260127073951.17248-1-xni@redhat.com>

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

Maybe this is a good time to add some documentation above
mddev_trylock() that explains the meaning of the return value? Anyway:

Reviewed-by: Bart Van Assche <bvanassche@acm.org>

^ permalink raw reply

* [PATCH 1/1] md: fix return value of mddev_trylock
From: Xiao Ni @ 2026-01-27  7:39 UTC (permalink / raw)
  To: yukuai; +Cc: linux-raid, bvanassche

A return value of 0 is treaded as successful lock acquisition. In fact, a
return value of 1 means getting the lock successfully.

Fixes: 9e59d609763f ("md: call del_gendisk in control path")
Reported-by: Bart Van Assche <bvanassche@acm.org>
Closes: https://lore.kernel.org/linux-raid/20250611073108.25463-1-xni@redhat.com/T/#mfa369ef5faa4aa58e13e6d9fdb88aecd862b8f2f
Signed-off-by: Xiao Ni <xni@redhat.com>
---
 drivers/md/md.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/md/md.h b/drivers/md/md.h
index 6985f2829bbd..3bfbee595156 100644
--- a/drivers/md/md.h
+++ b/drivers/md/md.h
@@ -737,8 +737,8 @@ static inline int mddev_trylock(struct mddev *mddev)
 	int ret;
 
 	ret = mutex_trylock(&mddev->reconfig_mutex);
-	if (!ret && test_bit(MD_DELETED, &mddev->flags)) {
-		ret = -ENODEV;
+	if (ret && test_bit(MD_DELETED, &mddev->flags)) {
+		ret = 0;
 		mutex_unlock(&mddev->reconfig_mutex);
 	}
 	return ret;
-- 
2.50.1 (Apple Git-155)


^ permalink raw reply related

* Re: [GIT PULL] md-7.0-20260127
From: Jens Axboe @ 2026-01-27  4:09 UTC (permalink / raw)
  To: Yu Kuai, linux-block, linux-raid
  Cc: linux-kernel, heinzm, jiashengjiangcool, jinpu.wang, linan122
In-Reply-To: <20260126172008.343807-1-yukuai@fnnas.com>

On 1/26/26 10:20 AM, Yu Kuai wrote:
> Hi Jens,
> 
> Please consider pulling the following changes into your for-7.0/block
> branch.
> 
> This pull request contains:
> 
> Bug Fixes:
> - Fix raid5_run() to return error when log_init() fails (Yu Kuai)
> - Fix IO hang with degraded array with llbitmap (Yu Kuai)
> - Fix percpu_ref not resurrected on suspend timeout in llbitmap (Yu Kuai)
> - Fix GPF in write_page caused by resize race (Jack Wang)
> - Fix NULL pointer dereference in process_metadata_update (Jiasheng Jiang)
> - Fix hang when stopping arrays with metadata through dm-raid
>   (Heinz Mauelshagen)
> - Fix any_working flag handling in raid10_sync_request (Li Nan)
> 
> Cleanups & Refactoring:
> - Refactor sync/recovery code path, improve error handling for badblocks,
>   and remove unused recovery_disabled field (Li Nan)
> - Consolidate mddev boolean fields into mddev_flags (Yu Kuai)
> 
> Improvements:
> - Use mempool to allocate stripe_request_ctx and make sure max_sectors is
>   not less than io_opt in raid5 (Yu Kuai)

Pulled, thanks.

-- 
Jens Axboe


^ permalink raw reply

* Re: [PATCH 1/3] md: call del_gendisk in control path
From: Yu Kuai @ 2026-01-27  2:23 UTC (permalink / raw)
  To: Bart Van Assche, Xiao Ni, linux-raid; +Cc: ncroxon, song, yukuai
In-Reply-To: <c0510f92-900d-4d63-aeae-85b2b8f5d59a@acm.org>

Hi,

在 2026/1/27 10:13, Bart Van Assche 写道:
> On 6/11/25 12:31 AM, Xiao Ni wrote:
>>   static inline int mddev_trylock(struct mddev *mddev)
>>   {
>> -    return mutex_trylock(&mddev->reconfig_mutex);
>> +    int ret;
>> +
>> +    ret = mutex_trylock(&mddev->reconfig_mutex);
>> +    if (!ret && test_bit(MD_DELETED, &mddev->flags)) {
>> +        ret = -ENODEV;
>> +        mutex_unlock(&mddev->reconfig_mutex);
>> +    }
>> +    return ret;
>>   }
>
> This change seems wrong to me. This change will cause mutex_unlock() to
> be called if mutex_trylock() *failed*. Additionally, returning -ENODEV
> from the failure path will cause the caller to call mutex_unlock() a
> second time if mutex_trylock() failed. Please fix this!

oops, this looks correct.

Just wonder why we didn't hit any strange bug with this, a try lock failure
should be quite possible.

>
> As a reminder, from include/linux/mutex.h:
>
> /*
>  * NOTE: mutex_trylock() follows the spin_trylock() convention,
>  *       not the down_trylock() convention!
>  *
>  * Returns 1 if the mutex has been acquired successfully, and 0 on 
> contention.
>  */
>
> Bart.
>
-- 
Thansk,
Kuai

^ permalink raw reply

* Re: [PATCH 1/3] md: call del_gendisk in control path
From: Bart Van Assche @ 2026-01-27  2:13 UTC (permalink / raw)
  To: Xiao Ni, linux-raid; +Cc: yukuai3, ncroxon, song, yukuai1
In-Reply-To: <20250611073108.25463-2-xni@redhat.com>

On 6/11/25 12:31 AM, Xiao Ni wrote:
>   static inline int mddev_trylock(struct mddev *mddev)
>   {
> -	return mutex_trylock(&mddev->reconfig_mutex);
> +	int ret;
> +
> +	ret = mutex_trylock(&mddev->reconfig_mutex);
> +	if (!ret && test_bit(MD_DELETED, &mddev->flags)) {
> +		ret = -ENODEV;
> +		mutex_unlock(&mddev->reconfig_mutex);
> +	}
> +	return ret;
>   }

This change seems wrong to me. This change will cause mutex_unlock() to
be called if mutex_trylock() *failed*. Additionally, returning -ENODEV
from the failure path will cause the caller to call mutex_unlock() a
second time if mutex_trylock() failed. Please fix this!

As a reminder, from include/linux/mutex.h:

/*
  * NOTE: mutex_trylock() follows the spin_trylock() convention,
  *       not the down_trylock() convention!
  *
  * Returns 1 if the mutex has been acquired successfully, and 0 on 
contention.
  */

Bart.

^ permalink raw reply

* [GIT PULL] md-7.0-20260127
From: Yu Kuai @ 2026-01-26 17:20 UTC (permalink / raw)
  To: axboe, linux-block, linux-raid
  Cc: linux-kernel, yukuai, heinzm, jiashengjiangcool, jinpu.wang,
	linan122

Hi Jens,

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

This pull request contains:

Bug Fixes:
- Fix raid5_run() to return error when log_init() fails (Yu Kuai)
- Fix IO hang with degraded array with llbitmap (Yu Kuai)
- Fix percpu_ref not resurrected on suspend timeout in llbitmap (Yu Kuai)
- Fix GPF in write_page caused by resize race (Jack Wang)
- Fix NULL pointer dereference in process_metadata_update (Jiasheng Jiang)
- Fix hang when stopping arrays with metadata through dm-raid
  (Heinz Mauelshagen)
- Fix any_working flag handling in raid10_sync_request (Li Nan)

Cleanups & Refactoring:
- Refactor sync/recovery code path, improve error handling for badblocks,
  and remove unused recovery_disabled field (Li Nan)
- Consolidate mddev boolean fields into mddev_flags (Yu Kuai)

Improvements:
- Use mempool to allocate stripe_request_ctx and make sure max_sectors is
  not less than io_opt in raid5 (Yu Kuai)

Thanks,
Kuai

The following changes since commit 8e5bcc3a955a2cc4460b391f55d3b49905eb248e:

  selftests: ublk: add missing gitignore for metadata_size binary (2026-01-25 10:11:32 -0700)

are available in the Git repository at:

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

for you to fetch changes up to cefcb9297fbdb6d94b61787b4f8d84f55b741470:

  md raid: fix hang when stopping arrays with metadata through dm-raid (2026-01-26 13:46:40 +0800)

----------------------------------------------------------------
Heinz Mauelshagen (1):
      md raid: fix hang when stopping arrays with metadata through dm-raid

Jack Wang (1):
      md/bitmap: fix GPF in write_page caused by resize race

Jiasheng Jiang (1):
      md-cluster: fix NULL pointer dereference in process_metadata_update

Li Nan (12):
      md/raid1: simplify uptodate handling in end_sync_write
      md: factor error handling out of md_done_sync into helper
      md/raid1,raid10: support narrow_write_error when badblocks is disabled
      md: break remaining operations on badblocks set failure in narrow_write_error
      md: mark rdev Faulty when badblocks setting fails
      md: update curr_resync_completed even when MD_RECOVERY_INTR is set
      md: remove MD_RECOVERY_ERROR handling and simplify resync_offset update
      md: factor out sync completion update into helper
      md: move finish_reshape to md_finish_sync()
      md/raid10: fix any_working flag handling in raid10_sync_request
      md/raid10: cleanup skip handling in raid10_sync_request
      md: remove recovery_disabled

Yu Kuai (8):
      md/raid5: fix raid5_run() to return error when log_init() fails
      md: merge mddev has_superblock into mddev_flags
      md: merge mddev faillast_dev into mddev_flags
      md: merge mddev serialize_policy into mddev_flags
      md/raid5: use mempool to allocate stripe_request_ctx
      md/raid5: make sure max_sectors is not less than io_opt
      md/raid5: fix IO hang with degraded array with llbitmap
      md/md-llbitmap: fix percpu_ref not resurrected on suspend timeout

 drivers/md/md-bitmap.c   |   7 +-
 drivers/md/md-cluster.c  |   7 +-
 drivers/md/md-llbitmap.c |   4 +-
 drivers/md/md.c          | 188 +++++++++++++++++++++++++----------------------
 drivers/md/md.h          |  25 +++----
 drivers/md/raid0.c       |   4 +-
 drivers/md/raid1-10.c    |   5 --
 drivers/md/raid1.c       |  88 +++++++++-------------
 drivers/md/raid1.h       |   5 --
 drivers/md/raid10.c      | 178 ++++++++++++++------------------------------
 drivers/md/raid10.h      |   5 --
 drivers/md/raid5.c       | 143 +++++++++++++++++++++--------------
 drivers/md/raid5.h       |   4 +-
 13 files changed, 310 insertions(+), 353 deletions(-)


^ permalink raw reply

* Re: [PATCH] md/bitmap: fix GPF in write_page caused by resize race
From: Jinpu Wang @ 2026-01-26  8:21 UTC (permalink / raw)
  To: yukuai; +Cc: song, linux-raid, linux-kernel, stable
In-Reply-To: <ad63a8bf-410f-4b91-aa89-3963dadf87af@fnnas.com>

On Mon, Jan 26, 2026 at 6:33 AM Yu Kuai <yukuai@fnnas.com> wrote:
>
> 在 2026/1/20 18:24, Jack Wang 写道:
>
> > A General Protection Fault occurs in write_page() during array resize:
> > RIP: 0010:write_page+0x22b/0x3c0 [md_mod]
> >
> > This is a use-after-free race between bitmap_daemon_work() and
> > __bitmap_resize(). The daemon iterates over `bitmap->storage.filemap`
> > without locking, while the resize path frees that storage via
> > md_bitmap_file_unmap(). `quiesce()` does not stop the md thread,
> > allowing concurrent access to freed pages.
> >
> > Fix by holding `mddev->bitmap_info.mutex` during the bitmap update.
> >
> > Closes:https://lore.kernel.org/linux-raid/CAMGffE=Mbfp=7xD_hYxXk1PAaCZNSEAVeQGKGy7YF9f2S4=NEA@mail.gmail.com/T/#u
> > Cc:stable@vger.kernel.org
> > Signed-off-by: Jack Wang<jinpu.wang@ionos.com>
> > ---
> >   drivers/md/md-bitmap.c | 3 ++-
> >   1 file changed, 2 insertions(+), 1 deletion(-)
>
> Applied with a fixtag:
>
> Fixes: d60b479d177a ("md/bitmap: add bitmap_resize function to allow
> bitmap resizing.")
>
> --
> Thansk,
> Kuai
Hi Kuai,

Thanks for applying the fix with the fixes tag.

Best,
Jinpu

^ permalink raw reply

* [PATCH] md/raid1: fix memory leak in raid1_run()
From: Zilin Guan @ 2026-01-26  7:15 UTC (permalink / raw)
  To: song; +Cc: yukuai, linux-raid, linux-kernel, jianhao.xu, Zilin Guan

raid1_run() calls setup_conf() which registers a thread via
md_register_thread(). If raid1_set_limits() fails, the previously
registered thread is not unregistered, resulting in a memory leak
of the md_thread structure and the thread resource itself.

Add md_unregister_thread() to the error path to properly cleanup
the thread, which aligns with the error handling logic of other paths
in this function.

Compile tested only. Issue found using a prototype static analysis tool
and code review.

Fixes: 97894f7d3c29 ("md/raid1: use the atomic queue limit update APIs")
Signed-off-by: Zilin Guan <zilin@seu.edu.cn>
---
 drivers/md/raid1.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
index 57d50465eed1..cc9914bd15c1 100644
--- a/drivers/md/raid1.c
+++ b/drivers/md/raid1.c
@@ -3254,6 +3254,7 @@ static int raid1_run(struct mddev *mddev)
 	if (!mddev_is_dm(mddev)) {
 		ret = raid1_set_limits(mddev);
 		if (ret) {
+			md_unregister_thread(mddev, &conf->thread);
 			if (!mddev->private)
 				raid1_free(mddev, conf);
 			return ret;
-- 
2.34.1


^ permalink raw reply related

* Re: Fwd: [PATCH V2] md raid: fix hang when stopping arrays with metadata through dm-raid
From: Yu Kuai @ 2026-01-26  5:50 UTC (permalink / raw)
  To: Heinz Mauelshagen, linux-raid, Linux-Kernel, Song Liu, yukuai
In-Reply-To: <CAM23VxqYrwkhKEBeQrZeZwQudbiNey2_8B_SEOLqug=pXxaFrA@mail.gmail.com>

在 2026/1/15 1:52, Heinz Mauelshagen 写道:

> hen using device-mapper's dm-raid target, stopping a RAID array can cause the
> system to hang under specific conditions.
>
> This occurs when:
>
> - A dm-raid managed device tree is suspended from top to bottom
>     (the top-level RAID device is suspended first, followed by its
>      underlying metadata and data devices)
>
> - The top-level RAID device is then removed
>
> Removing the top-level device triggers a hang in the following
> sequence: the dm-raid
> destructor calls md_stop(), which tries to flush the write-intent
> bitmap by writing
> to the metadata sub-devices. However, these devices are already
> suspended, making
> them unable to complete the write operations and causing an indefinite block.
>
> Fix:
>
> - Prevent bitmap flushing when md_stop() is called from dm-raid
> destructor context
>    and avoid a quiescing/unquescing cycle which could also cause I/O
>
> - Still allow write-intent bitmap flushing when called from dm-raid
> suspend context
>
> This ensures that RAID array teardown can complete successfully even when the
> underlying devices are in a suspended state.
>
> This second patch uses md_is_rdwr() to distinguish between suspend and
> destructor paths as elaborated on above.
>
> Signed-off-by: Heinz Mauelshagen<heinzm@redhat.com>
> ---
>   drivers/md/md.c | 12 +++++++-----
>   1 file changed, 7 insertions(+), 5 deletions(-)

There are conflicts now, applied with some rebasing:

diff --git a/drivers/md/md.c b/drivers/md/md.c
index 606f616190d7..59cd303548de 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -6851,13 +6851,15 @@ static void __md_stop_writes(struct mddev *mddev)
  {
         timer_delete_sync(&mddev->safemode_timer);

-       if (mddev->pers && mddev->pers->quiesce) {
-               mddev->pers->quiesce(mddev, 1);
-               mddev->pers->quiesce(mddev, 0);
-       }
+       if (md_is_rdwr(mddev) || !mddev_is_dm(mddev)) {
+               if (mddev->pers && mddev->pers->quiesce) {
+                       mddev->pers->quiesce(mddev, 1);
+                       mddev->pers->quiesce(mddev, 0);
+               }

-       if (md_bitmap_enabled(mddev, true))
-               mddev->bitmap_ops->flush(mddev);
+               if (md_bitmap_enabled(mddev, true))
+                       mddev->bitmap_ops->flush(mddev);
+       }

         if (md_is_rdwr(mddev) &&
             ((!mddev->in_sync && !mddev_is_clustered(mddev)) ||

-- 
Thansk,
Kuai

^ permalink raw reply related

* Re: [PATCH] md-cluster: fix NULL pointer dereference in process_metadata_update
From: Yu Kuai @ 2026-01-26  5:38 UTC (permalink / raw)
  To: Jiasheng Jiang, Song Liu, linux-raid, linux-kernel, yukuai
In-Reply-To: <20260117145903.28921-1-jiashengjiangcool@gmail.com>

在 2026/1/17 22:59, Jiasheng Jiang 写道:

> The function process_metadata_update() blindly dereferences the 'thread'
> pointer (acquired via rcu_dereference_protected) within the wait_event()
> macro.
>
> While the code comment states "daemon thread must exist", there is a valid
> race condition window during the MD array startup sequence (md_run):
>
> 1. bitmap_load() is called, which invokes md_cluster_ops->join().
> 2. join() starts the "cluster_recv" thread (recv_daemon).
> 3. At this point, recv_daemon is active and processing messages.
> 4. However, mddev->thread (the main MD thread) is not initialized until
>     later in md_run().
>
> If a METADATA_UPDATED message is received from a remote node during this
> specific window, process_metadata_update() will be called while
> mddev->thread is still NULL, leading to a kernel panic.
>
> To fix this, we must validate the 'thread' pointer. If it is NULL, we
> release the held lock (no_new_dev_lockres) and return early, safely
> ignoring the update request as the array is not yet fully ready to
> process it.
>
> Signed-off-by: Jiasheng Jiang<jiashengjiangcool@gmail.com>
> ---
>   drivers/md/md-cluster.c | 7 ++++++-
>   1 file changed, 6 insertions(+), 1 deletion(-)
Applied to md-7.0

-- 
Thansk,
Kuai

^ permalink raw reply

* Re: [PATCH] md/bitmap: fix GPF in write_page caused by resize race
From: Yu Kuai @ 2026-01-26  5:33 UTC (permalink / raw)
  To: Jack Wang, song, linux-raid, linux-kernel; +Cc: stable, yukuai
In-Reply-To: <20260120102456.25169-1-jinpu.wang@ionos.com>

在 2026/1/20 18:24, Jack Wang 写道:

> A General Protection Fault occurs in write_page() during array resize:
> RIP: 0010:write_page+0x22b/0x3c0 [md_mod]
>
> This is a use-after-free race between bitmap_daemon_work() and
> __bitmap_resize(). The daemon iterates over `bitmap->storage.filemap`
> without locking, while the resize path frees that storage via
> md_bitmap_file_unmap(). `quiesce()` does not stop the md thread,
> allowing concurrent access to freed pages.
>
> Fix by holding `mddev->bitmap_info.mutex` during the bitmap update.
>
> Closes:https://lore.kernel.org/linux-raid/CAMGffE=Mbfp=7xD_hYxXk1PAaCZNSEAVeQGKGy7YF9f2S4=NEA@mail.gmail.com/T/#u
> Cc:stable@vger.kernel.org
> Signed-off-by: Jack Wang<jinpu.wang@ionos.com>
> ---
>   drivers/md/md-bitmap.c | 3 ++-
>   1 file changed, 2 insertions(+), 1 deletion(-)

Applied with a fixtag:

Fixes: d60b479d177a ("md/bitmap: add bitmap_resize function to allow 
bitmap resizing.")

-- 
Thansk,
Kuai

^ permalink raw reply

* Re: [PATCH V3 4/6] nvmet: ignore discard return value
From: hch @ 2026-01-26  4:57 UTC (permalink / raw)
  To: Chaitanya Kulkarni
  Cc: Chaitanya Kulkarni, axboe@kernel.dk, agk@redhat.com,
	snitzer@kernel.org, mpatocka@redhat.com, song@kernel.org,
	yukuai@fnnas.com, hch@lst.de, sagi@grimberg.me,
	jaegeuk@kernel.org, chao@kernel.org, cem@kernel.org,
	linux-block@vger.kernel.org, linux-kernel@vger.kernel.org,
	dm-devel@lists.linux.dev, linux-raid@vger.kernel.org,
	linux-nvme@lists.infradead.org,
	linux-f2fs-devel@lists.sourceforge.net, linux-xfs@vger.kernel.org,
	bpf@vger.kernel.org, Martin K . Petersen, Johannes Thumshirn
In-Reply-To: <942ad29c-cff3-458f-b175-0111de821970@nvidia.com>

On Sat, Jan 24, 2026 at 09:35:16PM +0000, Chaitanya Kulkarni wrote:
> On 11/24/25 15:48, Chaitanya Kulkarni wrote:
> > __blkdev_issue_discard() always returns 0, making the error checking
> > in nvmet_bdev_discard_range() dead code.
> >
> > Kill the function nvmet_bdev_discard_range() and call
> > __blkdev_issue_discard() directly from nvmet_bdev_execute_discard(),
> > since no error handling is needed anymore for __blkdev_issue_discard()
> > call.
> >
> > Reviewed-by: Martin K. Petersen <martin.petersen@oracle.com>
> > Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
> > Reviewed-by: Christoph Hellwig <hch@lst.de>
> > Signed-off-by: Chaitanya Kulkarni <ckulkarnilinux@gmail.com>
> > ---
> 
> Gentle ping on this, can we apply this patch ?

Are we down to three patches now?  Maybe resend the whole series and
get ACKs to merge everything through the block layer?


^ permalink raw reply

* Re: [PATCH V3 4/6] nvmet: ignore discard return value
From: Chaitanya Kulkarni @ 2026-01-24 21:35 UTC (permalink / raw)
  To: Chaitanya Kulkarni, axboe@kernel.dk, agk@redhat.com,
	snitzer@kernel.org, mpatocka@redhat.com, song@kernel.org,
	yukuai@fnnas.com, hch@lst.de, sagi@grimberg.me,
	Chaitanya Kulkarni, jaegeuk@kernel.org, chao@kernel.org,
	cem@kernel.org
  Cc: linux-block@vger.kernel.org, linux-kernel@vger.kernel.org,
	dm-devel@lists.linux.dev, linux-raid@vger.kernel.org,
	linux-nvme@lists.infradead.org,
	linux-f2fs-devel@lists.sourceforge.net, linux-xfs@vger.kernel.org,
	bpf@vger.kernel.org, Martin K . Petersen, Johannes Thumshirn
In-Reply-To: <20251124234806.75216-5-ckulkarnilinux@gmail.com>

On 11/24/25 15:48, Chaitanya Kulkarni wrote:
> __blkdev_issue_discard() always returns 0, making the error checking
> in nvmet_bdev_discard_range() dead code.
>
> Kill the function nvmet_bdev_discard_range() and call
> __blkdev_issue_discard() directly from nvmet_bdev_execute_discard(),
> since no error handling is needed anymore for __blkdev_issue_discard()
> call.
>
> Reviewed-by: Martin K. Petersen <martin.petersen@oracle.com>
> Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
> Reviewed-by: Christoph Hellwig <hch@lst.de>
> Signed-off-by: Chaitanya Kulkarni <ckulkarnilinux@gmail.com>
> ---

Gentle ping on this, can we apply this patch ?

-ck



^ permalink raw reply

* Re: [PATCH V3 2/6] md: ignore discard return value
From: Chaitanya Kulkarni @ 2026-01-24 21:29 UTC (permalink / raw)
  To: song@kernel.org, yukuai@fnnas.com
  Cc: linux-raid@vger.kernel.org, hch@lst.de, Chaitanya Kulkarni,
	Chaitanya Kulkarni, Johannes Thumshirn, Martin K. Petersen
In-Reply-To: <938be45c-dcd9-45ba-b6ba-0fd29b0815db@nvidia.com>

On 11/30/25 22:22, Chaitanya Kulkarni wrote:
> Hi Song Liu and Yu Kuai,
>
> On 11/24/25 15:48, Chaitanya Kulkarni wrote:
>> __blkdev_issue_discard() always returns 0, making all error checking at
>> call sites dead code.
>>
>> Simplify md to only check !discard_bio by ignoring the
>> __blkdev_issue_discard() value.
>>
>> Reviewed-by: Martin K. Petersen <martin.petersen@oracle.com>
>> Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
>> Reviewed-by: Christoph Hellwig <hch@lst.de>
>> Signed-off-by: Chaitanya Kulkarni <ckulkarnilinux@gmail.com>
>> ---
>>   drivers/md/md.c | 4 ++--
>>   1 file changed, 2 insertions(+), 2 deletions(-)
>>
>> diff --git a/drivers/md/md.c b/drivers/md/md.c
>> index 7b5c5967568f..aeb62df39828 100644
>> --- a/drivers/md/md.c
>> +++ b/drivers/md/md.c
>> @@ -9132,8 +9132,8 @@ void md_submit_discard_bio(struct mddev *mddev, 
>> struct md_rdev *rdev,
>>   {
>>       struct bio *discard_bio = NULL;
>>   -    if (__blkdev_issue_discard(rdev->bdev, start, size, GFP_NOIO,
>> -            &discard_bio) || !discard_bio)
>> +    __blkdev_issue_discard(rdev->bdev, start, size, GFP_NOIO, 
>> &discard_bio);
>> +    if (!discard_bio)
>>           return;
>>         bio_chain(discard_bio, bio);
>
>
> Gentle ping on this.
>
> -ck
>
>
Hi Song Liu and Yu Kuai,

Just pulled latest changes I don't see this patch been applied [1].
Unless my git repo is messed up.

Can you please provide review comments and let me know the blocker ?

-ck

  9169 /* This is used by raid0 and raid10 */
  9170 void md_submit_discard_bio(struct mddev *mddev, struct md_rdev *rdev,
  9171                         struct bio *bio, sector_t start, sector_t size)
  9172 {
  9173         struct bio *discard_bio = NULL;
  9174
  9175         if (__blkdev_issue_discard(rdev->bdev, start, size, GFP_NOIO,
  9176                         &discard_bio) || !discard_bio)
  9177                 return;
  9178
  9179         bio_chain(discard_bio, bio);
  9180         bio_clone_blkg_association(discard_bio, bio);
  9181         mddev_trace_remap(mddev, discard_bio, bio->bi_iter.bi_sector);
  9182         submit_bio_noacct(discard_bio);
  9183 }
  9184 EXPORT_SYMBOL_GPL(md_submit_discard_bio);



^ 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