* Re: [PATCH v2 08/14] md/raid1: fix IO error at logical block size granularity
From: Yu Kuai @ 2026-02-04 16:54 UTC (permalink / raw)
To: linan666, song; +Cc: xni, linux-raid, linux-kernel, yangerkun, yi.zhang, yukuai
In-Reply-To: <20260128075708.2259525-9-linan666@huaweicloud.com>
在 2026/1/28 15:57, linan666@huaweicloud.com 写道:
> From: Li Nan<linan122@huawei.com>
>
> RAID1 currently fixes IO error at PAGE_SIZE granularity. Fix at smaller
> granularity can handle more errors, and RAID will support logical block
> sizes larger than PAGE_SIZE in the future, where PAGE_SIZE IO will fail.
>
> Switch IO error fix granularity to logical block size.
>
> Signed-off-by: Li Nan<linan122@huawei.com>
> ---
> drivers/md/raid1.c | 13 ++++---------
> 1 file changed, 4 insertions(+), 9 deletions(-)
Reviewed-by: Yu Kuai <yukuai@fnnas.com>
--
Thansk,
Kuai
^ permalink raw reply
* Re: [PATCH v2 14/14] md/raid1,raid10: fall back to smaller order if sync folio alloc fails
From: Yu Kuai @ 2026-02-04 16:48 UTC (permalink / raw)
To: linan666, song; +Cc: xni, linux-raid, linux-kernel, yangerkun, yi.zhang, yukuai
In-Reply-To: <20260128075708.2259525-15-linan666@huaweicloud.com>
Hi,
在 2026/1/28 15:57, linan666@huaweicloud.com 写道:
> From: Li Nan <linan122@huawei.com>
>
> RESYNC_BLOCK_SIZE (64K) has higher allocation failure chance than 4k,
> so retry with lower orders to improve allocation reliability.
>
> A r1/10_bio may have different rf->folio orders. Use minimum order as
> r1/10_bio sectors to prevent exceeding size when adding folio to IO later.
>
> Signed-off-by: Li Nan <linan122@huawei.com>
> ---
> drivers/md/raid1-10.c | 14 +++++++++++---
> drivers/md/raid1.c | 13 +++++++++----
> drivers/md/raid10.c | 28 ++++++++++++++++++++++++++--
> 3 files changed, 46 insertions(+), 9 deletions(-)
Looks like this patch should be merged into patch 5, there is no need to introduce
that higher allocation failure and then fix it here.
>
> diff --git a/drivers/md/raid1-10.c b/drivers/md/raid1-10.c
> index ffbd7bd0f6e8..e966d11a81e7 100644
> --- a/drivers/md/raid1-10.c
> +++ b/drivers/md/raid1-10.c
> @@ -41,12 +41,20 @@ static void rbio_pool_free(void *rbio, void *data)
> }
>
> static inline int resync_alloc_folio(struct resync_folio *rf,
> - gfp_t gfp_flags)
> + gfp_t gfp_flags, int *order)
> {
> - rf->folio = folio_alloc(gfp_flags, get_order(RESYNC_BLOCK_SIZE));
> - if (!rf->folio)
> + struct folio *folio;
> +
> + do {
> + folio = folio_alloc(gfp_flags, *order);
> + if (folio)
> + break;
> + } while (--(*order) > 0);
> +
> + if (!folio)
> return -ENOMEM;
>
> + rf->folio = folio;
> return 0;
> }
>
> diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
> index 2253e65c5f03..5bee846f1534 100644
> --- a/drivers/md/raid1.c
> +++ b/drivers/md/raid1.c
> @@ -149,6 +149,7 @@ static void * r1buf_pool_alloc(gfp_t gfp_flags, void *data)
> int need_folio;
> int j;
> struct resync_folio *rfs;
> + int order = get_order(RESYNC_BLOCK_SIZE);
>
> r1_bio = r1bio_pool_alloc(gfp_flags, conf);
> if (!r1_bio)
> @@ -182,7 +183,7 @@ static void * r1buf_pool_alloc(gfp_t gfp_flags, void *data)
> struct resync_folio *rf = &rfs[j];
>
> if (j < need_folio) {
> - if (resync_alloc_folio(rf, gfp_flags))
> + if (resync_alloc_folio(rf, gfp_flags, &order))
> goto out_free_folio;
> } else {
> memcpy(rf, &rfs[0], sizeof(*rf));
> @@ -193,6 +194,7 @@ static void * r1buf_pool_alloc(gfp_t gfp_flags, void *data)
> r1_bio->bios[j]->bi_private = rf;
> }
>
> + r1_bio->sectors = 1 << (order + PAGE_SECTORS_SHIFT);
> r1_bio->master_bio = NULL;
>
> return r1_bio;
> @@ -2776,7 +2778,7 @@ static sector_t raid1_sync_request(struct mddev *mddev, sector_t sector_nr,
> int write_targets = 0, read_targets = 0;
> sector_t sync_blocks;
> bool still_degraded = false;
> - int good_sectors = RESYNC_SECTORS;
> + int good_sectors;
> int min_bad = 0; /* number of sectors that are bad in all devices */
> int idx = sector_to_idx(sector_nr);
>
> @@ -2858,8 +2860,11 @@ static sector_t raid1_sync_request(struct mddev *mddev, sector_t sector_nr,
> r1_bio->sector = sector_nr;
> r1_bio->state = 0;
> set_bit(R1BIO_IsSync, &r1_bio->state);
> - /* make sure good_sectors won't go across barrier unit boundary */
> - good_sectors = align_to_barrier_unit_end(sector_nr, good_sectors);
> + /*
> + * make sure good_sectors won't go across barrier unit boundary.
> + * r1_bio->sectors <= RESYNC_SECTORS.
> + */
> + good_sectors = align_to_barrier_unit_end(sector_nr, r1_bio->sectors);
>
> for (i = 0; i < conf->raid_disks * 2; i++) {
> struct md_rdev *rdev;
> diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
> index 030812f908ac..72c77db9957c 100644
> --- a/drivers/md/raid10.c
> +++ b/drivers/md/raid10.c
> @@ -135,6 +135,7 @@ static void * r10buf_pool_alloc(gfp_t gfp_flags, void *data)
> int j;
> int nalloc, nalloc_rf;
> struct resync_folio *rfs;
> + int order = get_order(RESYNC_BLOCK_SIZE);
>
> r10_bio = r10bio_pool_alloc(gfp_flags, conf);
> if (!r10_bio)
> @@ -185,7 +186,7 @@ static void * r10buf_pool_alloc(gfp_t gfp_flags, void *data)
>
> if (!j || test_bit(MD_RECOVERY_SYNC,
> &conf->mddev->recovery)) {
> - if (resync_alloc_folio(rf, gfp_flags))
> + if (resync_alloc_folio(rf, gfp_flags, &order))
> goto out_free_folio;
> } else {
> memcpy(rf, &rfs[0], sizeof(*rf));
> @@ -200,6 +201,7 @@ static void * r10buf_pool_alloc(gfp_t gfp_flags, void *data)
> }
> }
>
> + r10_bio->sectors = 1 << (order + PAGE_SECTORS_SHIFT);
> return r10_bio;
>
> out_free_folio:
> @@ -3374,6 +3376,15 @@ static sector_t raid10_sync_request(struct mddev *mddev, sector_t sector_nr,
> continue;
> }
> }
> +
> + /*
> + * RESYNC_BLOCK_SIZE folio might alloc failed in
> + * resync_alloc_folio(). Fall back to smaller sync
> + * size if needed.
> + */
> + if (max_sync > r10_bio->sectors)
> + max_sync = r10_bio->sectors;
> +
> any_working = 1;
> bio = r10_bio->devs[0].bio;
> bio->bi_next = biolist;
> @@ -3525,7 +3536,15 @@ static sector_t raid10_sync_request(struct mddev *mddev, sector_t sector_nr,
> }
> if (sync_blocks < max_sync)
> max_sync = sync_blocks;
> +
> r10_bio = raid10_alloc_init_r10buf(conf);
> + /*
> + * RESYNC_BLOCK_SIZE folio might alloc failed in resync_alloc_folio().
> + * Fall back to smaller sync size if needed.
> + */
> + if (max_sync > r10_bio->sectors)
> + max_sync = r10_bio->sectors;
> +
> r10_bio->state = 0;
>
> r10_bio->mddev = mddev;
> @@ -4702,7 +4721,12 @@ static sector_t reshape_request(struct mddev *mddev, sector_t sector_nr,
> r10_bio->mddev = mddev;
> r10_bio->sector = sector_nr;
> set_bit(R10BIO_IsReshape, &r10_bio->state);
> - r10_bio->sectors = last - sector_nr + 1;
> + /*
> + * RESYNC_BLOCK_SIZE folio might alloc failed in
> + * resync_alloc_folio(). Fall back to smaller sync
> + * size if needed.
> + */
> + r10_bio->sectors = min_t(int, r10_bio->sectors, last - sector_nr + 1);
> rdev = read_balance(conf, r10_bio, &max_sectors);
> BUG_ON(!test_bit(R10BIO_Previous, &r10_bio->state));
>
--
Thansk,
Kuai
^ permalink raw reply
* Re: [PATCH v2 03/14] md/raid1: use folio for tmppage
From: Yu Kuai @ 2026-02-04 16:45 UTC (permalink / raw)
To: linan666, song; +Cc: xni, linux-raid, linux-kernel, yangerkun, yi.zhang, yukuai
In-Reply-To: <20260128075708.2259525-4-linan666@huaweicloud.com>
Hi,
在 2026/1/28 15:56, linan666@huaweicloud.com 写道:
> From: Li Nan <linan122@huawei.com>
>
> Convert tmppage to tmpfolio and use it throughout in raid1.
>
> Signed-off-by: Li Nan <linan122@huawei.com>
> Reviewed-by: Xiao Ni <xni@redhat.com>
> ---
> drivers/md/raid1.h | 2 +-
> drivers/md/raid1.c | 18 ++++++++++--------
> 2 files changed, 11 insertions(+), 9 deletions(-)
>
> diff --git a/drivers/md/raid1.h b/drivers/md/raid1.h
> index c98d43a7ae99..d480b3a8c2c4 100644
> --- a/drivers/md/raid1.h
> +++ b/drivers/md/raid1.h
> @@ -101,7 +101,7 @@ struct r1conf {
> /* temporary buffer to synchronous IO when attempting to repair
> * a read error.
> */
> - struct page *tmppage;
> + struct folio *tmpfolio;
>
> /* When taking over an array from a different personality, we store
> * the new thread here until we fully activate the array.
> diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
> index 407925951299..43453f1a04f4 100644
> --- a/drivers/md/raid1.c
> +++ b/drivers/md/raid1.c
> @@ -2417,8 +2417,8 @@ static void fix_read_error(struct r1conf *conf, struct r1bio *r1_bio)
> rdev->recovery_offset >= sect + s)) &&
> rdev_has_badblock(rdev, sect, s) == 0) {
> atomic_inc(&rdev->nr_pending);
> - if (sync_page_io(rdev, sect, s<<9,
> - conf->tmppage, REQ_OP_READ, false))
> + if (sync_folio_io(rdev, sect, s<<9, 0,
> + conf->tmpfolio, REQ_OP_READ, false))
> success = 1;
> rdev_dec_pending(rdev, mddev);
> if (success)
> @@ -2447,7 +2447,8 @@ static void fix_read_error(struct r1conf *conf, struct r1bio *r1_bio)
> !test_bit(Faulty, &rdev->flags)) {
> atomic_inc(&rdev->nr_pending);
> r1_sync_page_io(rdev, sect, s,
> - conf->tmppage, REQ_OP_WRITE);
> + folio_page(conf->tmpfolio, 0),
> + REQ_OP_WRITE);
> rdev_dec_pending(rdev, mddev);
> }
> }
> @@ -2461,7 +2462,8 @@ static void fix_read_error(struct r1conf *conf, struct r1bio *r1_bio)
> !test_bit(Faulty, &rdev->flags)) {
> atomic_inc(&rdev->nr_pending);
> if (r1_sync_page_io(rdev, sect, s,
> - conf->tmppage, REQ_OP_READ)) {
> + folio_page(conf->tmpfolio, 0),
> + REQ_OP_READ)) {
> atomic_add(s, &rdev->corrected_errors);
> pr_info("md/raid1:%s: read error corrected (%d sectors at %llu on %pg)\n",
> mdname(mddev), s,
> @@ -3120,8 +3122,8 @@ static struct r1conf *setup_conf(struct mddev *mddev)
> if (!conf->mirrors)
> goto abort;
>
> - conf->tmppage = alloc_page(GFP_KERNEL);
> - if (!conf->tmppage)
> + conf->tmpfolio = folio_alloc(GFP_KERNEL, 0);
> + if (!conf->tmpfolio)
> goto abort;
>
> r1bio_size = offsetof(struct r1bio, bios[mddev->raid_disks * 2]);
> @@ -3196,7 +3198,7 @@ static struct r1conf *setup_conf(struct mddev *mddev)
> if (conf) {
> mempool_destroy(conf->r1bio_pool);
> kfree(conf->mirrors);
> - safe_put_page(conf->tmppage);
> + folio_put(conf->tmpfolio);
Is this safe? folio_put() can't be called with NULL.
> kfree(conf->nr_pending);
> kfree(conf->nr_waiting);
> kfree(conf->nr_queued);
> @@ -3310,7 +3312,7 @@ static void raid1_free(struct mddev *mddev, void *priv)
>
> mempool_destroy(conf->r1bio_pool);
> kfree(conf->mirrors);
> - safe_put_page(conf->tmppage);
> + folio_put(conf->tmpfolio);
Same here.
> kfree(conf->nr_pending);
> kfree(conf->nr_waiting);
> kfree(conf->nr_queued);
--
Thansk,
Kuai
^ permalink raw reply
* Re: [PATCH v2 02/14] md: introduce sync_folio_io for folio support in RAID
From: Yu Kuai @ 2026-02-04 16:38 UTC (permalink / raw)
To: linan666, song; +Cc: xni, linux-raid, linux-kernel, yangerkun, yi.zhang, yukuai
In-Reply-To: <20260128075708.2259525-3-linan666@huaweicloud.com>
Hi,
在 2026/1/28 15:56, 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;
Above code are the same as sync_page_io(), I think you can just remove sync_page_io()
directly in this patch, and convert to sync_folio_io() by passing in page_folio(page).
> + bio_add_folio_nofail(&bio, folio, size, off);
> +
> + submit_bio_wait(&bio);
> +
> + return !bio.bi_status;
Please also change return value to bool, and replace the checking to
bio.bi_status == BLK_STS_OK.
> +}
> +EXPORT_SYMBOL_GPL(sync_folio_io);
> +
> static int read_disk_sb(struct md_rdev *rdev, int size)
> {
> if (rdev->sb_loaded)
--
Thansk,
Kuai
^ permalink raw reply
* Re: [PATCH v2 01/14] md/raid1,raid10: clean up of RESYNC_SECTORS
From: Yu Kuai @ 2026-02-04 16:29 UTC (permalink / raw)
To: linan666, song; +Cc: xni, linux-raid, linux-kernel, yangerkun, yi.zhang
In-Reply-To: <20260128075708.2259525-2-linan666@huaweicloud.com>
在 2026/1/28 15:56, 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(-)
Reviewed-by: Yu Kuai <yukuai@fnnas.com>
--
Thansk,
Kuai
^ permalink raw reply
* Re: [PATCH RFC 0/3] md/raid1: data corruption with serialization
From: Yu Kuai @ 2026-02-04 15:58 UTC (permalink / raw)
To: Xiao Ni; +Cc: linux-raid, yukuai
In-Reply-To: <20260204145912.9463-1-xni@redhat.com>
Hi,
在 2026/2/4 22:58, Xiao Ni 写道:
> A data corruption can happen when using serialization for raid1.
> Serialization is not enabled by default. But it looks like there
> is a data corruption risk if serialization is closed. Because the
> lower driver can't guarantee the sequence which io is written first.
> So it's possible that different member disks will have different
> data for nvme devices. This patch set doesn't open serialization
> by default.
An important idea is that if there are filesystem on top of raid1/10/5, there
is no need to consider overlap bio. The only case for serialization is WriteMostly,
that bio can be returned to user while it's not done for slow disks. And that is
the reason why serialization is for rdev.
We definitely will not enable serialization by default, because there will
be huge performance degradation. And I don't think we'll handle the case user
manage data with raw disk, and write to the same area concurrently. At last, if
there are still problem with WriteMostly case, please rebase and keep serialization
for rdev.
>
> Xiao Ni (3):
> md: add return value of mddev_create_serial_pool
> md/raid1: fix data corruption by moving serialization to mddev level
> md/raid1: fix incorrect sector range in serialization
>
> drivers/md/md-bitmap.c | 28 +++++--
> drivers/md/md.c | 171 ++++++++++++++---------------------------
> drivers/md/md.h | 30 ++++----
> drivers/md/raid1.c | 47 ++++++-----
> 4 files changed, 115 insertions(+), 161 deletions(-)
>
--
Thansk,
Kuai
^ permalink raw reply
* [PATCH RFC 3/3] md/raid1: fix incorrect sector range in serialization
From: Xiao Ni @ 2026-02-04 14:58 UTC (permalink / raw)
To: yukuai; +Cc: linux-raid
In-Reply-To: <20260204145912.9463-1-xni@redhat.com>
md/raid1: fix incorrect sector range in serialization
Fix off-by-one error in sector range calculation for serialization.
The range should be [lo, hi] inclusive, where hi = lo + sectors - 1,
not lo + sectors.
The current implementation causes false collision detection for
consecutive writes. For example, when writing:
- R1 to sectors 0-1023 (hi = 1024)
- R2 to sectors 1024-2047 (lo = 1024)
R1's hi equals R2's lo, causing raid1_rb_iter_first() to return
true and triggering unnecessary serialization, even though these
requests don't actually overlap.
Signed-off-by: Xiao Ni <xni@redhat.com>
---
drivers/md/raid1.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
index 029aa458ffb2..e3d9ba71796f 100644
--- a/drivers/md/raid1.c
+++ b/drivers/md/raid1.c
@@ -62,7 +62,7 @@ static int check_and_add_serial(struct mddev *mddev, struct r1bio *r1_bio,
unsigned long flags;
int ret = 0;
sector_t lo = r1_bio->sector;
- sector_t hi = lo + r1_bio->sectors;
+ sector_t hi = lo + r1_bio->sectors - 1;
struct serial *serial = &mddev->serial[idx];
spin_lock_irqsave(&serial->serial_lock, flags);
@@ -100,7 +100,7 @@ static void remove_serial(struct r1bio *r1_bio)
int found = 0;
struct mddev *mddev = r1_bio->mddev;
sector_t lo = r1_bio->sector;
- sector_t hi = r1_bio->sector + r1_bio->sectors;
+ sector_t hi = r1_bio->sector + r1_bio->sectors - 1;
int idx = sector_to_idx(lo);
struct serial *serial = &mddev->serial[idx];
--
2.50.1 (Apple Git-155)
^ permalink raw reply related
* [PATCH RFC 2/3] md/raid1: fix data corruption by moving serialization to mddev level
From: Xiao Ni @ 2026-02-04 14:58 UTC (permalink / raw)
To: yukuai; +Cc: linux-raid
In-Reply-To: <20260204145912.9463-1-xni@redhat.com>
Overlap io request is handled in per-rdev serialization now. Overlap io
requests need to wait and are woken up when one overlap io returns. But
the sequence of getting the lock after waking up maybe different in
member disks.
For example:
bio1 (100,200)
bio2 (150,200)
bio3 (160,200)
bio1 is writing and bio2/bio3 are waiting. It wakes up bio2/bio3 when bio1
finishes. bio2 gets the lock and is submitted to disk0. But in disk1, bio3
gets the lock and is submitted. Finally, the data on member disks are
different. Even if they get locks in the same order, the lower drivers
don't guarantee the sequence that bios are landed in disks. It still has
a risk that member disks have different data.
So per-rdev serialization cannot guarantee ordering across different
member disks when multiple requests are queued. Each disk's wake_up()
independently wakes waiters, but the order they reacquire locks is
undefined.
Fix by moving serialization from per-rdev to per-mddev level:
- Change serial data structure from rdev->serial to mddev->serial
- Call wait_for_serialization() once per r1_bio at mddev level
before submitting to any disk
- Call remove_serial() once in r1_bio_write_done() after all disks
complete, instead of per-rdev in raid1_end_write_request()
This ensures:
- All member disks see identical write ordering
- Only one wait queue exists for each sector range
- Wake-up ordering issues cannot cause cross-disk inconsistency
Signed-off-by: Xiao Ni <xni@redhat.com>
---
drivers/md/md.c | 143 ++++++++++++---------------------------------
drivers/md/md.h | 28 ++++-----
drivers/md/raid1.c | 45 +++++++-------
3 files changed, 71 insertions(+), 145 deletions(-)
diff --git a/drivers/md/md.c b/drivers/md/md.c
index 115df70354ed..c113af3865ac 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -152,69 +152,37 @@ static int sync_io_depth(struct mddev *mddev)
mddev->sync_io_depth : sysctl_sync_io_depth;
}
-static void rdev_uninit_serial(struct md_rdev *rdev)
+static void mddev_uninit_serial(struct mddev *mddev)
{
- if (!test_and_clear_bit(CollisionCheck, &rdev->flags))
- return;
-
- kvfree(rdev->serial);
- rdev->serial = NULL;
+ kvfree(mddev->serial);
+ mddev->serial = NULL;
}
-static void rdevs_uninit_serial(struct mddev *mddev)
-{
- struct md_rdev *rdev;
-
- rdev_for_each(rdev, mddev)
- rdev_uninit_serial(rdev);
-}
-
-static int rdev_init_serial(struct md_rdev *rdev)
+static int mddev_init_serial(struct mddev *mddev)
{
/* serial_nums equals with BARRIER_BUCKETS_NR */
int i, serial_nums = 1 << ((PAGE_SHIFT - ilog2(sizeof(atomic_t))));
- struct serial_in_rdev *serial = NULL;
-
- if (test_bit(CollisionCheck, &rdev->flags))
- return 0;
+ struct serial *serial = NULL;
- serial = kvmalloc(sizeof(struct serial_in_rdev) * serial_nums,
+ serial = kvmalloc_array(serial_nums, sizeof(struct serial),
GFP_KERNEL);
- if (!serial)
+ if (!serial) {
+ pr_err("failed to alloc serial\n");
return -ENOMEM;
+ }
for (i = 0; i < serial_nums; i++) {
- struct serial_in_rdev *serial_tmp = &serial[i];
+ struct serial *serial_tmp = &serial[i];
spin_lock_init(&serial_tmp->serial_lock);
serial_tmp->serial_rb = RB_ROOT_CACHED;
init_waitqueue_head(&serial_tmp->serial_io_wait);
}
- rdev->serial = serial;
- set_bit(CollisionCheck, &rdev->flags);
-
+ mddev->serial = serial;
return 0;
}
-static int rdevs_init_serial(struct mddev *mddev)
-{
- struct md_rdev *rdev;
- int ret = 0;
-
- rdev_for_each(rdev, mddev) {
- ret = rdev_init_serial(rdev);
- if (ret)
- break;
- }
-
- /* Free all resources if pool is not existed */
- if (ret && !mddev->serial_info_pool)
- rdevs_uninit_serial(mddev);
-
- return ret;
-}
-
/*
* rdev needs to enable serial stuffs if it meets the conditions:
* 1. it is multi-queue device flaged with writemostly.
@@ -236,72 +204,40 @@ int mddev_create_serial_pool(struct mddev *mddev, struct md_rdev *rdev)
{
int ret = 0;
- if (rdev && !rdev_need_serial(rdev) &&
- !test_bit(CollisionCheck, &rdev->flags))
+ if (mddev->serial_info_pool)
return ret;
- if (!rdev)
- ret = rdevs_init_serial(mddev);
- else
- ret = rdev_init_serial(rdev);
- if (ret)
+ /* return success if rdev doesn't support serial */
+ if (rdev && !rdev_need_serial(rdev))
return ret;
- if (mddev->serial_info_pool == NULL) {
- /*
- * already in memalloc noio context by
- * mddev_suspend()
- */
- mddev->serial_info_pool =
- mempool_create_kmalloc_pool(NR_SERIAL_INFOS,
- sizeof(struct serial_info));
- if (!mddev->serial_info_pool) {
- rdevs_uninit_serial(mddev);
- pr_err("can't alloc memory pool for serialization\n");
- ret = -ENOMEM;
- }
+ ret = mddev_init_serial(mddev);
+ if (ret)
+ return ret;
+ /*
+ * already in memalloc noio context by
+ * mddev_suspend()
+ */
+ mddev->serial_info_pool = mempool_create_kmalloc_pool(
+ NR_SERIAL_INFOS, sizeof(struct serial_info));
+ if (!mddev->serial_info_pool) {
+ mddev_uninit_serial(mddev);
+ pr_err("can't alloc memory pool for serialization\n");
+ ret = -ENOMEM;
}
+ set_bit(MD_SERIAL, &mddev->flags);
return ret;
}
-/*
- * Free resource from rdev(s), and destroy serial_info_pool under conditions:
- * 1. rdev is the last device flaged with CollisionCheck.
- * 2. when bitmap is destroyed while policy is not enabled.
- * 3. for disable policy, the pool is destroyed only when no rdev needs it.
- */
void mddev_destroy_serial_pool(struct mddev *mddev, struct md_rdev *rdev)
{
- if (rdev && !test_bit(CollisionCheck, &rdev->flags))
+ if (!mddev->serial_info_pool)
return;
-
- if (mddev->serial_info_pool) {
- struct md_rdev *temp;
- int num = 0; /* used to track if other rdevs need the pool */
-
- rdev_for_each(temp, mddev) {
- if (!rdev) {
- if (!mddev->serialize_policy ||
- !rdev_need_serial(temp))
- rdev_uninit_serial(temp);
- else
- num++;
- } else if (temp != rdev &&
- test_bit(CollisionCheck, &temp->flags))
- num++;
- }
-
- if (rdev)
- rdev_uninit_serial(rdev);
-
- if (num)
- pr_info("The mempool could be used by other devices\n");
- else {
- mempool_destroy(mddev->serial_info_pool);
- mddev->serial_info_pool = NULL;
- }
- }
+ mddev_uninit_serial(mddev);
+ mempool_destroy(mddev->serial_info_pool);
+ mddev->serial_info_pool = NULL;
+ clear_bit(MD_SERIAL, &mddev->flags);
}
static struct ctl_table_header *raid_table_header;
@@ -6633,18 +6569,13 @@ int md_run(struct mddev *mddev)
bool create_pool = false;
rdev_for_each(rdev, mddev) {
- if (test_bit(WriteMostly, &rdev->flags) &&
- rdev_init_serial(rdev))
+ if (test_bit(WriteMostly, &rdev->flags))
create_pool = true;
}
- if (create_pool && mddev->serial_info_pool == NULL) {
- mddev->serial_info_pool =
- mempool_create_kmalloc_pool(NR_SERIAL_INFOS,
- sizeof(struct serial_info));
- if (!mddev->serial_info_pool) {
- err = -ENOMEM;
+ if (create_pool) {
+ err = mddev_create_serial_pool(mddev, NULL);
+ if (err)
goto bitmap_abort;
- }
}
}
diff --git a/drivers/md/md.h b/drivers/md/md.h
index 3c4edc6ed50e..7fc6d03b0c33 100644
--- a/drivers/md/md.h
+++ b/drivers/md/md.h
@@ -116,15 +116,6 @@ enum sync_action {
NR_SYNC_ACTIONS,
};
-/*
- * The struct embedded in rdev is used to serialize IO.
- */
-struct serial_in_rdev {
- struct rb_root_cached serial_rb;
- spinlock_t serial_lock;
- wait_queue_head_t serial_io_wait;
-};
-
/*
* MD's 'extended' device
*/
@@ -204,8 +195,6 @@ struct md_rdev {
* in superblock.
*/
- struct serial_in_rdev *serial; /* used for raid1 io serialization */
-
struct kernfs_node *sysfs_state; /* handle for 'state'
* sysfs entry */
/* handle for 'unacknowledged_bad_blocks' sysfs dentry */
@@ -286,10 +275,6 @@ enum flag_bits {
* it didn't fail, so don't use FailFast
* any more for metadata
*/
- CollisionCheck, /*
- * check if there is collision between raid1
- * serial bios.
- */
Nonrot, /* non-rotational device (SSD) */
};
@@ -356,6 +341,7 @@ enum mddev_flags {
MD_BROKEN,
MD_DO_DELETE,
MD_DELETED,
+ MD_SERIAL,
};
enum mddev_sb_flags {
@@ -389,6 +375,15 @@ enum {
MD_RESYNC_ACTIVE = 3,
};
+/*
+ * The struct embedded is used to serialize IO.
+ */
+struct serial {
+ struct rb_root_cached serial_rb;
+ spinlock_t serial_lock;
+ wait_queue_head_t serial_io_wait;
+};
+
struct mddev {
void *private;
struct md_personality *pers;
@@ -607,7 +602,8 @@ struct mddev {
struct bio_set io_clone_set;
struct work_struct event_work; /* used by dm to report failure event */
- mempool_t *serial_info_pool;
+ mempool_t *serial_info_pool;
+ struct serial *serial; /* used for raid1 io serialization */
void (*sync_super)(struct mddev *mddev, struct md_rdev *rdev);
struct md_cluster_info *cluster_info;
struct md_cluster_operations *cluster_ops;
diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
index 57d50465eed1..029aa458ffb2 100644
--- a/drivers/md/raid1.c
+++ b/drivers/md/raid1.c
@@ -56,14 +56,14 @@ static void raid1_free(struct mddev *mddev, void *priv);
INTERVAL_TREE_DEFINE(struct serial_info, node, sector_t, _subtree_last,
START, LAST, static inline, raid1_rb);
-static int check_and_add_serial(struct md_rdev *rdev, struct r1bio *r1_bio,
+static int check_and_add_serial(struct mddev *mddev, struct r1bio *r1_bio,
struct serial_info *si, int idx)
{
unsigned long flags;
int ret = 0;
sector_t lo = r1_bio->sector;
sector_t hi = lo + r1_bio->sectors;
- struct serial_in_rdev *serial = &rdev->serial[idx];
+ struct serial *serial = &mddev->serial[idx];
spin_lock_irqsave(&serial->serial_lock, flags);
/* collision happened */
@@ -79,28 +79,33 @@ static int check_and_add_serial(struct md_rdev *rdev, struct r1bio *r1_bio,
return ret;
}
-static void wait_for_serialization(struct md_rdev *rdev, struct r1bio *r1_bio)
+static void wait_for_serialization(struct mddev *mddev, struct r1bio *r1_bio)
{
- struct mddev *mddev = rdev->mddev;
struct serial_info *si;
int idx = sector_to_idx(r1_bio->sector);
- struct serial_in_rdev *serial = &rdev->serial[idx];
+ struct serial *serial = &mddev->serial[idx];
- if (WARN_ON(!mddev->serial_info_pool))
+ if (!test_bit(MD_SERIAL, &mddev->flags))
return;
+
si = mempool_alloc(mddev->serial_info_pool, GFP_NOIO);
wait_event(serial->serial_io_wait,
- check_and_add_serial(rdev, r1_bio, si, idx) == 0);
+ check_and_add_serial(mddev, r1_bio, si, idx) == 0);
}
-static void remove_serial(struct md_rdev *rdev, sector_t lo, sector_t hi)
+static void remove_serial(struct r1bio *r1_bio)
{
struct serial_info *si;
unsigned long flags;
int found = 0;
- struct mddev *mddev = rdev->mddev;
+ struct mddev *mddev = r1_bio->mddev;
+ sector_t lo = r1_bio->sector;
+ sector_t hi = r1_bio->sector + r1_bio->sectors;
int idx = sector_to_idx(lo);
- struct serial_in_rdev *serial = &rdev->serial[idx];
+ struct serial *serial = &mddev->serial[idx];
+
+ if (!test_bit(MD_SERIAL, &mddev->flags))
+ return;
spin_lock_irqsave(&serial->serial_lock, flags);
for (si = raid1_rb_iter_first(&serial->serial_rb, lo, hi);
@@ -433,6 +438,8 @@ static void r1_bio_write_done(struct r1bio *r1_bio)
if (!atomic_dec_and_test(&r1_bio->remaining))
return;
+ remove_serial(r1_bio);
+
if (test_bit(R1BIO_WriteError, &r1_bio->state))
reschedule_retry(r1_bio);
else {
@@ -452,8 +459,6 @@ static void raid1_end_write_request(struct bio *bio)
struct bio *to_put = NULL;
int mirror = find_bio_disk(r1_bio, bio);
struct md_rdev *rdev = conf->mirrors[mirror].rdev;
- sector_t lo = r1_bio->sector;
- sector_t hi = r1_bio->sector + r1_bio->sectors;
bool ignore_error = !raid1_should_handle_error(bio) ||
(bio->bi_status && bio_op(bio) == REQ_OP_DISCARD);
@@ -518,8 +523,6 @@ static void raid1_end_write_request(struct bio *bio)
}
if (behind) {
- if (test_bit(CollisionCheck, &rdev->flags))
- remove_serial(rdev, lo, hi);
if (test_bit(WriteMostly, &rdev->flags))
atomic_dec(&r1_bio->behind_remaining);
@@ -542,8 +545,8 @@ static void raid1_end_write_request(struct bio *bio)
call_bio_endio(r1_bio);
}
}
- } else if (rdev->mddev->serialize_policy)
- remove_serial(rdev, lo, hi);
+ }
+
if (r1_bio->bios[mirror] == NULL)
rdev_dec_pending(rdev, conf->mddev);
@@ -1620,6 +1623,8 @@ static void raid1_write_request(struct mddev *mddev, struct bio *bio,
first_clone = 1;
+ wait_for_serialization(mddev, r1_bio);
+
for (i = 0; i < disks; i++) {
struct bio *mbio = NULL;
struct md_rdev *rdev = conf->mirrors[i].rdev;
@@ -1636,18 +1641,12 @@ static void raid1_write_request(struct mddev *mddev, struct bio *bio,
mbio = bio_alloc_clone(rdev->bdev,
r1_bio->behind_master_bio,
GFP_NOIO, &mddev->bio_set);
- if (test_bit(CollisionCheck, &rdev->flags))
- wait_for_serialization(rdev, r1_bio);
if (test_bit(WriteMostly, &rdev->flags))
atomic_inc(&r1_bio->behind_remaining);
- } else {
+ } else
mbio = bio_alloc_clone(rdev->bdev, bio, GFP_NOIO,
&mddev->bio_set);
- if (mddev->serialize_policy)
- wait_for_serialization(rdev, r1_bio);
- }
-
mbio->bi_opf &= ~REQ_NOWAIT;
r1_bio->bios[i] = mbio;
--
2.50.1 (Apple Git-155)
^ permalink raw reply related
* [PATCH RFC 1/3] md: add return value of mddev_create_serial_pool
From: Xiao Ni @ 2026-02-04 14:58 UTC (permalink / raw)
To: yukuai; +Cc: linux-raid
In-Reply-To: <20260204145912.9463-1-xni@redhat.com>
Prepare for the next patch.
Signed-off-by: Xiao Ni <xni@redhat.com>
---
drivers/md/md-bitmap.c | 28 ++++++++++++++++++++++------
drivers/md/md.c | 30 +++++++++++++++++++++---------
drivers/md/md.h | 2 +-
3 files changed, 44 insertions(+), 16 deletions(-)
diff --git a/drivers/md/md-bitmap.c b/drivers/md/md-bitmap.c
index 84b7e2af6dba..1c82961833ba 100644
--- a/drivers/md/md-bitmap.c
+++ b/drivers/md/md-bitmap.c
@@ -2213,8 +2213,11 @@ static int bitmap_load(struct mddev *mddev)
if (!bitmap)
goto out;
- rdev_for_each(rdev, mddev)
- mddev_create_serial_pool(mddev, rdev);
+ rdev_for_each(rdev, mddev) {
+ err = mddev_create_serial_pool(mddev, rdev);
+ if (err)
+ goto out;
+ }
if (mddev_is_clustered(mddev))
mddev->cluster_ops->load_bitmaps(mddev, mddev->bitmap_info.nodes);
@@ -2253,9 +2256,15 @@ static int bitmap_load(struct mddev *mddev)
bitmap_update_sb(bitmap);
- if (test_bit(BITMAP_WRITE_ERROR, &bitmap->flags))
+ if (test_bit(BITMAP_WRITE_ERROR, &bitmap->flags)) {
err = -EIO;
+ goto out;
+ }
+
+ return err;
+
out:
+ mddev_destroy_serial_pool(mddev, NULL);
return err;
}
@@ -2806,19 +2815,26 @@ backlog_store(struct mddev *mddev, const char *buf, size_t len)
return -EINVAL;
}
- mddev->bitmap_info.max_write_behind = backlog;
if (!backlog && mddev->serial_info_pool) {
/* serial_info_pool is not needed if backlog is zero */
if (!mddev->serialize_policy)
mddev_destroy_serial_pool(mddev, NULL);
} else if (backlog && !mddev->serial_info_pool) {
/* serial_info_pool is needed since backlog is not zero */
- rdev_for_each(rdev, mddev)
- mddev_create_serial_pool(mddev, rdev);
+ rdev_for_each(rdev, mddev) {
+ rv = mddev_create_serial_pool(mddev, rdev);
+ if (rv) {
+ mddev_destroy_serial_pool(mddev, NULL);
+ goto unlock;
+ }
+ }
}
+
+ mddev->bitmap_info.max_write_behind = backlog;
if (old_mwb != backlog)
bitmap_update_sb(mddev->bitmap);
+unlock:
mddev_unlock_and_resume(mddev);
return len;
}
diff --git a/drivers/md/md.c b/drivers/md/md.c
index 6d73f6e196a9..115df70354ed 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -232,20 +232,20 @@ static int rdev_need_serial(struct md_rdev *rdev)
* 1. rdev is the first device which return true from rdev_enable_serial.
* 2. rdev is NULL, means we want to enable serialization for all rdevs.
*/
-void mddev_create_serial_pool(struct mddev *mddev, struct md_rdev *rdev)
+int mddev_create_serial_pool(struct mddev *mddev, struct md_rdev *rdev)
{
int ret = 0;
if (rdev && !rdev_need_serial(rdev) &&
!test_bit(CollisionCheck, &rdev->flags))
- return;
+ return ret;
if (!rdev)
ret = rdevs_init_serial(mddev);
else
ret = rdev_init_serial(rdev);
if (ret)
- return;
+ return ret;
if (mddev->serial_info_pool == NULL) {
/*
@@ -258,8 +258,11 @@ void mddev_create_serial_pool(struct mddev *mddev, struct md_rdev *rdev)
if (!mddev->serial_info_pool) {
rdevs_uninit_serial(mddev);
pr_err("can't alloc memory pool for serialization\n");
+ ret = -ENOMEM;
}
}
+
+ return ret;
}
/*
@@ -2600,8 +2603,13 @@ static int bind_rdev_to_array(struct md_rdev *rdev, struct mddev *mddev)
rdev->mddev = mddev;
pr_debug("md: bind<%s>\n", b);
- if (mddev->raid_disks)
- mddev_create_serial_pool(mddev, rdev);
+ if (mddev->raid_disks) {
+ err = mddev_create_serial_pool(mddev, rdev);
+ if (err) {
+ pr_err("failed to create serial pool\n");
+ return err;
+ }
+ }
if ((err = kobject_add(&rdev->kobj, &mddev->kobj, "dev-%s", b)))
goto fail;
@@ -3114,7 +3122,9 @@ state_store(struct md_rdev *rdev, const char *buf, size_t len)
}
} else if (cmd_match(buf, "writemostly")) {
set_bit(WriteMostly, &rdev->flags);
- mddev_create_serial_pool(rdev->mddev, rdev);
+ err = mddev_create_serial_pool(rdev->mddev, rdev);
+ if (err)
+ return err;
need_update_sb = true;
err = 0;
} else if (cmd_match(buf, "-writemostly")) {
@@ -5924,9 +5934,11 @@ serialize_policy_store(struct mddev *mddev, const char *buf, size_t len)
goto unlock;
}
- if (value)
- mddev_create_serial_pool(mddev, NULL);
- else
+ if (value) {
+ err = mddev_create_serial_pool(mddev, NULL);
+ if (err)
+ goto unlock;
+ } else
mddev_destroy_serial_pool(mddev, NULL);
mddev->serialize_policy = value;
unlock:
diff --git a/drivers/md/md.h b/drivers/md/md.h
index 6985f2829bbd..3c4edc6ed50e 100644
--- a/drivers/md/md.h
+++ b/drivers/md/md.h
@@ -956,7 +956,7 @@ extern void md_frozen_sync_thread(struct mddev *mddev);
extern void md_unfrozen_sync_thread(struct mddev *mddev);
extern void md_update_sb(struct mddev *mddev, int force);
-extern void mddev_create_serial_pool(struct mddev *mddev, struct md_rdev *rdev);
+extern int mddev_create_serial_pool(struct mddev *mddev, struct md_rdev *rdev);
extern void mddev_destroy_serial_pool(struct mddev *mddev,
struct md_rdev *rdev);
struct md_rdev *md_find_rdev_nr_rcu(struct mddev *mddev, int nr);
--
2.50.1 (Apple Git-155)
^ permalink raw reply related
* [PATCH RFC 0/3] md/raid1: data corruption with serialization
From: Xiao Ni @ 2026-02-04 14:58 UTC (permalink / raw)
To: yukuai; +Cc: linux-raid
A data corruption can happen when using serialization for raid1.
Serialization is not enabled by default. But it looks like there
is a data corruption risk if serialization is closed. Because the
lower driver can't guarantee the sequence which io is written first.
So it's possible that different member disks will have different
data for nvme devices. This patch set doesn't open serialization
by default.
Xiao Ni (3):
md: add return value of mddev_create_serial_pool
md/raid1: fix data corruption by moving serialization to mddev level
md/raid1: fix incorrect sector range in serialization
drivers/md/md-bitmap.c | 28 +++++--
drivers/md/md.c | 171 ++++++++++++++---------------------------
drivers/md/md.h | 30 ++++----
drivers/md/raid1.c | 47 ++++++-----
4 files changed, 115 insertions(+), 161 deletions(-)
--
2.50.1 (Apple Git-155)
^ permalink raw reply
* [PATCH 1/1] mdadm/imsm: use creation_time for ctime in container info
From: Xiao Ni @ 2026-02-04 14:00 UTC (permalink / raw)
To: mtkaczyk; +Cc: linux-raid
When a disk has both DDF and IMSM metadata (e.g., migrated from DDF
to IMSM or has remnant DDF metadata), guess_super_type() selects the
metadata with the later creation time. Previously, IMSM always
returned ctime=0 in getinfo_super_imsm(), causing it to lose to DDF
which extracts a real timestamp from its GUID.
This resulted in the wrong metadata being selected during assembly,
leading to boot failures when LVM activated raw PVs instead of MD
devices.
Fix this by extracting the actual creation time from the IMSM
metadata structure (mpb->creation_time) instead of hardcoding 0.
This ensures that when both metadata types are present, the more
recent one is correctly selected.
Signed-off-by: Xiao Ni <xni@redhat.com>
---
super-intel.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/super-intel.c b/super-intel.c
index e9fce12c35c7..2ff9d4862f7f 100644
--- a/super-intel.c
+++ b/super-intel.c
@@ -3826,11 +3826,13 @@ static void getinfo_super_imsm(struct supertype *st, struct mdinfo *info, char *
/* Set raid_disks to zero so that Assemble will always pull in valid
* spares
*/
+ mpb = super->anchor;
+
info->array.raid_disks = 0;
info->array.level = LEVEL_CONTAINER;
info->array.layout = 0;
info->array.md_minor = -1;
- info->array.ctime = 0; /* N/A for imsm */
+ info->array.ctime = __le64_to_cpu(mpb->creation_time);
info->array.utime = 0;
info->array.chunk_size = 0;
@@ -3850,7 +3852,6 @@ static void getinfo_super_imsm(struct supertype *st, struct mdinfo *info, char *
info->bb.supported = 1;
/* do we have the all the insync disks that we expect? */
- mpb = super->anchor;
info->events = __le32_to_cpu(mpb->generation_num);
for (i = 0; i < mpb->num_raid_devs; i++) {
--
2.50.1 (Apple Git-155)
^ permalink raw reply related
* Re: [RFC v2 0/5] md/raid1: introduce a new sync action to repair badblocks
From: Zheng Qixing @ 2026-02-04 9:32 UTC (permalink / raw)
To: Christoph Hellwig
Cc: song, yukuai, linan122, xni, linux-raid, linux-kernel, yi.zhang,
yangerkun, houtao1, Zheng Qixing
In-Reply-To: <aYIi1JoZnDMq8rNe@infradead.org>
resend..
在 2026/2/4 0:31, Christoph Hellwig 写道:
> On Tue, Feb 03, 2026 at 04:08:23PM +0800, Zheng Qixing wrote:
>> Hi,
>>
>> 在 2026/2/3 15:31, Christoph Hellwig 写道:
>>> Just curious, but what kind of devices do you see that have
>>> permanent bad blocks at a fixed location that are not fixed by
>>> rewriting the sector?
>> The bad_blocks entries record sectors where I/O failed, which
>> indicates that the device-internal remapping did not succeed
>> at that time.
>>
>> `rectify` does not assume a permanently bad or fixed LBA. Its
>> purpose is to trigger an additional rewrite, giving the underlying
>> device (e.g. FTL or firmware) another opportunity to perform its
>> own remapping.
> Well, what devices do you see where writes fail, but rewrites
> fix them?
I understand your concerns, but I do not have a concrete example tied
to a specific device model...
The intent here is to provide an additional rewrite opportunity, which
allows the write path to be exercised again and gives the underlying
device or stack a chance to recover or remap the affected range.
For remote storage devices, I/O may fail due to network or transport
issues. If the final attempt fails, MD can record the affected range in
bad_blocks. This behavior does not appear to be tied to a specific
device model.
For local storage, some controllers may have limitations or corner cases
in their remapping mechanisms. In such cases, a sector that could
potentially be recovered may be marked as bad, leaving no opportunity
for a subsequent successful rewrite.
^ permalink raw reply
* Re: [RFC v2 0/5] md/raid1: introduce a new sync action to repair badblocks
From: Zheng Qixing @ 2026-02-04 9:29 UTC (permalink / raw)
To: Christoph Hellwig
Cc: song, yukuai, linan122, xni, linux-raid, linux-kernel, yi.zhang,
yangerkun, houtao1, Zheng Qixing
In-Reply-To: <aYIi1JoZnDMq8rNe@infradead.org>
在 2026/2/4 0:31, Christoph Hellwig 写道:
> On Tue, Feb 03, 2026 at 04:08:23PM +0800, Zheng Qixing wrote:
>> Hi,
>>
>> 在 2026/2/3 15:31, Christoph Hellwig 写道:
>>> Just curious, but what kind of devices do you see that have
>>> permanent bad blocks at a fixed location that are not fixed by
>>> rewriting the sector?
>> The bad_blocks entries record sectors where I/O failed, which
>> indicates that the device-internal remapping did not succeed
>> at that time.
>>
>> `rectify` does not assume a permanently bad or fixed LBA. Its
>> purpose is to trigger an additional rewrite, giving the underlying
>> device (e.g. FTL or firmware) another opportunity to perform its
>> own remapping.
> Well, what devices do you see where writes fail, but rewrites
> fix them?
I understand your concerns, but I do not have a concrete example tied to a specific device model... The intent here is to provide an additional rewrite opportunity, which allows the write path to be exercised again and gives the underlying device or stack a chance to recover or remap the affected range. For remote storage devices, I/O may fail due to network or transport issues. If the final attempt fails, MD can record the affected range in bad_blocks. This behavior does not appear to be tied to a specific device model. For local storage, some controllers may have limitations or corner cases in their remapping mechanisms. In such cases, a sector that could potentially be recovered may be marked as bad, leaving no opportunity for a subsequent successful rewrite.
^ permalink raw reply
* Re: [RFC v2 0/5] md/raid1: introduce a new sync action to repair badblocks
From: Pascal Hambourg @ 2026-02-03 20:36 UTC (permalink / raw)
To: Christoph Hellwig
Cc: Zheng Qixing, song, yukuai, linan122, xni, linux-raid,
linux-kernel, yi.zhang, yangerkun, houtao1, zhengqixing
In-Reply-To: <aYIirWALBGrMxmbw@infradead.org>
On 03/02/2026 at 17:30, Christoph Hellwig wrote:
> On Tue, Feb 03, 2026 at 09:08:18AM +0100, Pascal Hambourg wrote:
>> On 03/02/2026 à 08:31, Christoph Hellwig wrote:
>>> Just curious, but what kind of devices do you see that have
>>> permanent bad blocks at a fixed location that are not fixed by
>>> rewriting the sector?
>>
>> I have seen this with several hard disk drives of various brands, even
>> though SMART attribute #5 (reallocated sector count) had not reached the
>> limit.
>
> Weird. Can you share the models? I'm especially curious if these
> are consumer of enterprise drives and of what vintage.
I did not keep track of the models and do not remember them, it was a
long time ago. They were mostly hard disk drives from Dell and HP
professional desktop and laptop series, so consumer grade I guess,
manufactured around 2010.
^ permalink raw reply
* Re: [RFC v2 0/5] md/raid1: introduce a new sync action to repair badblocks
From: Christoph Hellwig @ 2026-02-03 16:31 UTC (permalink / raw)
To: Zheng Qixing
Cc: Christoph Hellwig, song, yukuai, linan122, xni, linux-raid,
linux-kernel, yi.zhang, yangerkun, houtao1, Zheng Qixing
In-Reply-To: <a1b70bef-27df-45d0-abbb-2d6b75ef5f4c@huaweicloud.com>
On Tue, Feb 03, 2026 at 04:08:23PM +0800, Zheng Qixing wrote:
> Hi,
>
> 在 2026/2/3 15:31, Christoph Hellwig 写道:
> > Just curious, but what kind of devices do you see that have
> > permanent bad blocks at a fixed location that are not fixed by
> > rewriting the sector?
>
> The bad_blocks entries record sectors where I/O failed, which
> indicates that the device-internal remapping did not succeed
> at that time.
>
> `rectify` does not assume a permanently bad or fixed LBA. Its
> purpose is to trigger an additional rewrite, giving the underlying
> device (e.g. FTL or firmware) another opportunity to perform its
> own remapping.
Well, what devices do you see where writes fail, but rewrites
fix them?
^ permalink raw reply
* Re: [RFC v2 0/5] md/raid1: introduce a new sync action to repair badblocks
From: Christoph Hellwig @ 2026-02-03 16:30 UTC (permalink / raw)
To: Pascal Hambourg
Cc: Christoph Hellwig, Zheng Qixing, song, yukuai, linan122, xni,
linux-raid, linux-kernel, yi.zhang, yangerkun, houtao1,
zhengqixing
In-Reply-To: <40f37494-cf9c-4bc3-aa38-75c76b5d988e@plouf.fr.eu.org>
On Tue, Feb 03, 2026 at 09:08:18AM +0100, Pascal Hambourg wrote:
> On 03/02/2026 à 08:31, Christoph Hellwig wrote:
> > Just curious, but what kind of devices do you see that have
> > permanent bad blocks at a fixed location that are not fixed by
> > rewriting the sector?
>
> I have seen this with several hard disk drives of various brands, even
> though SMART attribute #5 (reallocated sector count) had not reached the
> limit.
Weird. Can you share the models? I'm especially curious if these
are consumer of enterprise drives and of what vintage.
^ permalink raw reply
* Re: [PATCH 2/3] lib/raid6: Optimizing the raid6_select_algo time through asynchronous processing
From: kernel test robot @ 2026-02-03 13:54 UTC (permalink / raw)
To: sunliming
Cc: oe-lkp, lkp, linux-raid, song, yukuai, linux-kernel, sunliming,
oliver.sang
In-Reply-To: <20260128104923.338443-3-sunliming@linux.dev>
Hello,
kernel test robot noticed "Oops:int3:#[##]SMP_KASAN_NOPTI" on:
commit: e806b74f91ad2c995a3313d4cc369c85fab1da5f ("[PATCH 2/3] lib/raid6: Optimizing the raid6_select_algo time through asynchronous processing")
url: https://github.com/intel-lab-lkp/linux/commits/sunliming-linux-dev/lib-raid6-Divide-the-raid6-algorithm-selection-process-into-two-parts/20260128-185709
base: https://git.kernel.org/cgit/linux/kernel/git/akpm/mm.git mm-nonmm-unstable
patch link: https://lore.kernel.org/all/20260128104923.338443-3-sunliming@linux.dev/
patch subject: [PATCH 2/3] lib/raid6: Optimizing the raid6_select_algo time through asynchronous processing
in testcase: perf-sanity-tests
version:
with following parameters:
perf_compiler: gcc
group: group-01
config: x86_64-rhel-9.4-bpf
compiler: gcc-14
test machine: 16 threads 1 sockets Intel(R) Xeon(R) E-2278G CPU @ 3.40GHz (Coffee Lake-E) with 32G memory
(please refer to attached dmesg/kmsg for entire log/backtrace)
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 <oliver.sang@intel.com>
| Closes: https://lore.kernel.org/oe-lkp/202602032100.d8e49b6f-lkp@intel.com
[ 81.117339][ T479] xor: automatically using best checksumming function avx
[ 81.122258][ T109] raid6: avx2x2 gen() 17654 MB/s
[ 81.157936][ T109] raid6: avx2x1 gen() 10371 MB/s
[ 81.172813][ T109] raid6: using algorithm avx2x2 gen() 17654 MB/s
[ 81.205659][ T109] raid6: .... xor() 12927 MB/s, rmw enabled
[ 81.221136][ C15] Oops: int3: 0000 [#1] SMP KASAN NOPTI
[ 81.221144][ C15] CPU: 15 UID: 0 PID: 109 Comm: kworker/15:0 Tainted: G S 6.19.0-rc6-00161-ge806b74f91ad #1 PREEMPT(full)
[ 81.221149][ C15] Tainted: [S]=CPU_OUT_OF_SPEC
[ 81.221150][ C15] Hardware name: Intel Corporation Mehlow UP Server Platform/Moss Beach Server, BIOS CNLSE2R1.R00.X188.B13.1903250419 03/25/2019
[ 81.221153][ C15] Workqueue: events benchmark_work_func [raid6_pq]
[ 81.221160][ C15] RIP: 0010:benchmark_work_func (recov_avx512.c:?) raid6_pq
[ 81.221163][ C15] Code: cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc <cc> cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc
All code
========
0: cc int3
1: cc int3
2: cc int3
3: cc int3
4: cc int3
5: cc int3
6: cc int3
7: cc int3
8: cc int3
9: cc int3
a: cc int3
b: cc int3
c: cc int3
d: cc int3
e: cc int3
f: cc int3
10: cc int3
11: cc int3
12: cc int3
13: cc int3
14: cc int3
15: cc int3
16: cc int3
17: cc int3
18: cc int3
19: cc int3
1a: cc int3
1b: cc int3
1c: cc int3
1d: cc int3
1e: cc int3
1f: cc int3
20: cc int3
21: cc int3
22: cc int3
23: cc int3
24: cc int3
25: cc int3
26: cc int3
27: cc int3
28: cc int3
29: cc int3
2a:* cc int3 <-- trapping instruction
2b: cc int3
2c: cc int3
2d: cc int3
2e: cc int3
2f: cc int3
30: cc int3
31: cc int3
32: cc int3
33: cc int3
34: cc int3
35: cc int3
36: cc int3
37: cc int3
38: cc int3
39: cc int3
3a: cc int3
3b: cc int3
3c: cc int3
3d: cc int3
3e: cc int3
3f: cc int3
Code starting with the faulting instruction
===========================================
0: cc int3
1: cc int3
2: cc int3
3: cc int3
4: cc int3
5: cc int3
6: cc int3
7: cc int3
8: cc int3
9: cc int3
a: cc int3
b: cc int3
c: cc int3
d: cc int3
e: cc int3
f: cc int3
10: cc int3
11: cc int3
12: cc int3
13: cc int3
14: cc int3
15: cc int3
[ 81.221166][ C15] RSP: 0018:ffff888101557b58 EFLAGS: 00000282
[ 81.221169][ C15] RAX: 0000000000000029 RBX: 1ffff110202aaf6b RCX: 0000000000000000
[ 81.221171][ C15] RDX: 0000000000000029 RSI: ffffffff81513d9c RDI: ffffed10202aaf5e
[ 81.221173][ C15] RBP: ffff88836aad0000 R08: 0000000000000000 R09: fffffbfff0b21df4
[ 81.221175][ C15] R10: ffffffff8590efa7 R11: 0000000000000001 R12: ffff888101557bb8
[ 81.221178][ C15] R13: ffff88836aad8000 R14: ffff888101557bb8 R15: ffff8887887c33c0
[ 81.221180][ C15] FS: 0000000000000000(0000) GS:ffff888801e6a000(0000) knlGS:0000000000000000
[ 81.221182][ C15] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 81.221185][ C15] CR2: 00007fbfb077d000 CR3: 000000084d2f6001 CR4: 00000000003726f0
[ 81.221187][ C15] Call Trace:
[ 81.221189][ C15] <TASK>
[ 81.221194][ C15] ? rcu_is_watching (kbuild/src/consumer/arch/x86/include/asm/atomic.h:23 kbuild/src/consumer/include/linux/atomic/atomic-arch-fallback.h:457 kbuild/src/consumer/include/linux/context_tracking.h:128 kbuild/src/consumer/kernel/rcu/tree.c:751)
[ 81.221199][ C15] ? rcu_is_watching (kbuild/src/consumer/arch/x86/include/asm/atomic.h:23 kbuild/src/consumer/include/linux/atomic/atomic-arch-fallback.h:457 kbuild/src/consumer/include/linux/context_tracking.h:128 kbuild/src/consumer/kernel/rcu/tree.c:751)
[ 81.221203][ C15] ? lock_acquire (kbuild/src/consumer/include/trace/events/lock.h:24 (discriminator 2) kbuild/src/consumer/kernel/locking/lockdep.c:5831 (discriminator 2))
[ 81.221208][ C15] ? process_one_work (kbuild/src/consumer/arch/x86/include/asm/jump_label.h:37 kbuild/src/consumer/include/trace/events/workqueue.h:110 kbuild/src/consumer/kernel/workqueue.c:3262)
The kernel config and materials to reproduce are available at:
https://download.01.org/0day-ci/archive/20260203/202602032100.d8e49b6f-lkp@intel.com
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply
* [PATCH 1/1] md/raid5: don't set bi_status for requeued IO in STRIPE_WAIT_RESHAPE
From: Yang Xiuwei @ 2026-02-03 9:51 UTC (permalink / raw)
To: song, yukuai, snitzer, xni; +Cc: linux-raid, Yang Xiuwei
When reshape is interrupted, IO that needs to be requeued incorrectly
sets bi->bi_status to BLK_STS_RESOURCE. This causes md_free_cloned_bio()
to copy the error status to the original bio, which is wrong because
requeued IO should be retried, not failed.
This was found during LVM testing where reshape operations under I/O
load would cause I/O errors and filesystem corruption.
Remove the bi_status assignment to prevent incorrect error marking.
Fixes: 41425f96d7aa ("dm-raid456, md/raid456: fix a deadlock for dm-raid456 while io concurrent with reshape")
Signed-off-by: Yang Xiuwei <yangxiuwei@kylinos.cn>
---
drivers/md/raid5.c | 1 -
1 file changed, 1 deletion(-)
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index 8dc98f545969..128c0b7e9f54 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -6038,7 +6038,6 @@ 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;
pr_err_ratelimited("dm-raid456: io across reshape position while reshape can't make progress");
}
--
2.25.1
^ permalink raw reply related
* Re: [RFC v2 0/5] md/raid1: introduce a new sync action to repair badblocks
From: Pascal Hambourg @ 2026-02-03 8:08 UTC (permalink / raw)
To: Christoph Hellwig, Zheng Qixing
Cc: song, yukuai, linan122, xni, linux-raid, linux-kernel, yi.zhang,
yangerkun, houtao1, zhengqixing
In-Reply-To: <aYGkSwDZTLLbjm6p@infradead.org>
On 03/02/2026 à 08:31, Christoph Hellwig wrote:
> Just curious, but what kind of devices do you see that have
> permanent bad blocks at a fixed location that are not fixed by
> rewriting the sector?
I have seen this with several hard disk drives of various brands, even
though SMART attribute #5 (reallocated sector count) had not reached the
limit.
^ permalink raw reply
* Re: [RFC v2 0/5] md/raid1: introduce a new sync action to repair badblocks
From: Zheng Qixing @ 2026-02-03 8:08 UTC (permalink / raw)
To: Christoph Hellwig
Cc: song, yukuai, linan122, xni, linux-raid, linux-kernel, yi.zhang,
yangerkun, houtao1, Zheng Qixing
In-Reply-To: <aYGkSwDZTLLbjm6p@infradead.org>
Hi,
在 2026/2/3 15:31, Christoph Hellwig 写道:
> Just curious, but what kind of devices do you see that have
> permanent bad blocks at a fixed location that are not fixed by
> rewriting the sector?
The bad_blocks entries record sectors where I/O failed, which
indicates that the device-internal remapping did not succeed
at that time.
`rectify` does not assume a permanently bad or fixed LBA. Its
purpose is to trigger an additional rewrite, giving the underlying
device (e.g. FTL or firmware) another opportunity to perform its
own remapping.
^ permalink raw reply
* Re: [RFC v2 0/5] md/raid1: introduce a new sync action to repair badblocks
From: Christoph Hellwig @ 2026-02-03 7:31 UTC (permalink / raw)
To: Zheng Qixing
Cc: song, yukuai, linan122, xni, linux-raid, linux-kernel, yi.zhang,
yangerkun, houtao1, zhengqixing
In-Reply-To: <20260203061259.609206-1-zhengqixing@huaweicloud.com>
Just curious, but what kind of devices do you see that have
permanent bad blocks at a fixed location that are not fixed by
rewriting the sector?
^ permalink raw reply
* [RFC v2 3/5] md: rename mdstat action "recovery" to "recover"
From: Zheng Qixing @ 2026-02-03 6:12 UTC (permalink / raw)
To: song, yukuai, linan122
Cc: xni, linux-raid, linux-kernel, yi.zhang, yangerkun, houtao1,
zhengqixing
In-Reply-To: <20260203061259.609206-1-zhengqixing@huaweicloud.com>
From: Zheng Qixing <zhengqixing@huawei.com>
Simplify the code in status_resync() that prints the progress of sync
actions.
Also rename the /proc/mdstat action from "recovery" to "recover" to
match the naming used by action_store() and action_show().
Note:
The md-raid-utilities/mdadm test suite will need to be updated to expect
"recover" instead of "recovery" in /proc/mdstat.
Signed-off-by: Zheng Qixing <zhengqixing@huawei.com>
---
drivers/md/md.c | 10 ++++------
1 file changed, 4 insertions(+), 6 deletions(-)
diff --git a/drivers/md/md.c b/drivers/md/md.c
index 7fe02ee21d3e..f319621c6832 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -8669,6 +8669,7 @@ static int status_resync(struct seq_file *seq, struct mddev *mddev)
sector_t rt, curr_mark_cnt, resync_mark_cnt;
int scale, recovery_active;
unsigned int per_milli;
+ enum sync_action action;
if (test_bit(MD_RECOVERY_SYNC, &mddev->recovery) ||
test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery))
@@ -8750,13 +8751,10 @@ static int status_resync(struct seq_file *seq, struct mddev *mddev)
seq_printf(seq, ".");
seq_printf(seq, "] ");
}
+
+ action = md_sync_action(mddev);
seq_printf(seq, " %s =%3u.%u%% (%llu/%llu)",
- (test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery)?
- "reshape" :
- (test_bit(MD_RECOVERY_CHECK, &mddev->recovery)?
- "check" :
- (test_bit(MD_RECOVERY_SYNC, &mddev->recovery) ?
- "resync" : "recovery"))),
+ md_sync_action_name(action),
per_milli/10, per_milli % 10,
(unsigned long long) resync/2,
(unsigned long long) max_sectors/2);
--
2.39.2
^ permalink raw reply related
* [RFC v2 2/5] md: serialize requested sync actions and clear stale request state
From: Zheng Qixing @ 2026-02-03 6:12 UTC (permalink / raw)
To: song, yukuai, linan122
Cc: xni, linux-raid, linux-kernel, yi.zhang, yangerkun, houtao1,
zhengqixing
In-Reply-To: <20260203061259.609206-1-zhengqixing@huaweicloud.com>
From: Zheng Qixing <zhengqixing@huawei.com>
In handle_requested_sync_action(), return -EBUSY when
MD_RECOVERY_REQUESTED is already set. This serializes requested sync
actions (such as check or repair) and avoids a race window where a
second sync request can be issued before MD_RECOVERY_RUNNING is set,
resulting in the later request being neither rejected nor executed.
Additionally, in md_check_recovery(), clear requested-sync related
state bits when no recovery operation is running. This prevents stale
request state from persisting in cases where a sync action is queued
and 'frozen' is written before MD_RECOVERY_RUNNING is set, which would
cause subsequent sync requests to spuriously fail with -EBUSY.
Signed-off-by: Zheng Qixing <zhengqixing@huawei.com>
---
drivers/md/md.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/drivers/md/md.c b/drivers/md/md.c
index 84af578876e2..7fe02ee21d3e 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -668,6 +668,9 @@ void mddev_put(struct mddev *mddev)
static int handle_requested_sync_action(struct mddev *mddev,
enum sync_action action)
{
+ if (test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery))
+ return -EBUSY;
+
switch (action) {
case ACTION_CHECK:
set_bit(MD_RECOVERY_CHECK, &mddev->recovery);
@@ -10318,6 +10321,9 @@ void md_check_recovery(struct mddev *mddev)
queue_work(md_misc_wq, &mddev->sync_work);
} else {
clear_bit(MD_RECOVERY_RUNNING, &mddev->recovery);
+ clear_bit(MD_RECOVERY_SYNC, &mddev->recovery);
+ clear_bit(MD_RECOVERY_REQUESTED, &mddev->recovery);
+ clear_bit(MD_RECOVERY_CHECK, &mddev->recovery);
wake_up(&resync_wait);
}
--
2.39.2
^ permalink raw reply related
* [RFC v2 0/5] md/raid1: introduce a new sync action to repair badblocks
From: Zheng Qixing @ 2026-02-03 6:12 UTC (permalink / raw)
To: song, yukuai, linan122
Cc: xni, linux-raid, linux-kernel, yi.zhang, yangerkun, houtao1,
zhengqixing
From: Zheng Qixing <zhengqixing@huawei.com>
Hi,
This is v2 of the series.
# Mechanism
When rectifying badblocks, we issue a single repair write for
the bad range (copy data from a good mirror to the corresponding
LBA on bad mirror). Once the write completes successfully
(bi_status == 0), the LBA range is cleared from the badblocks
table. If the media is still bad for that LBA, a subsequent
read/write will fail again and the range will be marked bad
again.
Doing a read-back for every repair would only prove that the data
is readable at that moment, and it does not provide a stronger
guarantee against future internal remapping.
# Why use LBS granularity for bad-block repair?
In our RAID1 bad-block repair (rectify) testing on a device
reporting 512B logical blocks and 4KiB physical blocks, we
issue 512B I/O directly to the md device and inject an I/O
fault.
Since the md badblocks table can only track failures in terms
of host-visible LBA ranges, it is updated at 512B sector
granularity (i.e., it records the failing sector) and does not
attempt to infer or expand the entry to a 4KiB physical-block
boundary.
Given that the OS has no visibility into the device's internal
mapping from LBAs to physical media (or the FTL), using
logical block size for recording and repairing bad blocks is
the most appropriate choice from a correctness standpoint.
If the underlying media failure is actually larger than 512B,
this is typically reflected by subsequent failures on adjacent
LBAs, at which point the recorded bad range will naturally
grow to cover the affected area.
# Tests
This feature has been tested on a RAID1 built from two 480GB
system disks. It has also been tested under QEMU with a 4-disk
RAID1 setup, with both memory fault injection and I/O fault
injection enabled.
In addition, we will add a new test (26raid1-rectify-badblocks)
in mdadm/tests to verify whether `rectify` can effectively repair
sectors recorded in bad_blocks.
# TODO
rectify currently only supports bad-block repair for the RAID1
level. We will consider extending it to RAID5/10 in follow-up
work.
Changes in v2:
- Patch 1: Remove non-essential helpers to reduce indirection.
- Patch 2: Split out a bugfix that was previously included in patch 1.
- Patch 3: Rename the /proc/mdstat action from "recovery" to "recover"
to match the naming used by action_store() and action_show().
- Patch 4: Add a brief comment for MAX_RAID_DISKS.
- Patch 5: For rectify, reuse handle_sync_write_finished() to handle
write request completion, removing duplicate completion handling.
Link of v1:
https://lore.kernel.org/all/20251231070952.1233903-1-zhengqixing@huaweicloud.com/
Zheng Qixing (5):
md: add helpers for requested sync action
md: serialize requested sync actions and clear stale request state
md: rename mdstat action "recovery" to "recover"
md: introduce MAX_RAID_DISKS macro to replace magic number
md/raid1: introduce rectify action to repair badblocks
drivers/md/md.c | 152 +++++++++++++++++++------
drivers/md/md.h | 21 ++++
drivers/md/raid1.c | 270 ++++++++++++++++++++++++++++++++++++++++++++-
drivers/md/raid1.h | 1 +
4 files changed, 409 insertions(+), 35 deletions(-)
--
2.39.2
^ permalink raw reply
* [RFC v2 5/5] md/raid1: introduce rectify action to repair badblocks
From: Zheng Qixing @ 2026-02-03 6:12 UTC (permalink / raw)
To: song, yukuai, linan122
Cc: xni, linux-raid, linux-kernel, yi.zhang, yangerkun, houtao1,
zhengqixing
In-Reply-To: <20260203061259.609206-1-zhengqixing@huaweicloud.com>
From: Zheng Qixing <zhengqixing@huawei.com>
Add support for repairing known badblocks in RAID1. When disks
have known badblocks (shown in sysfs bad_blocks), data can be
read from other healthy disks in the array and written to repair
the badblock areas and clear it in bad_blocks.
echo rectify > sync_action can trigger this action.
Signed-off-by: Zheng Qixing <zhengqixing@huawei.com>
---
drivers/md/md.c | 71 +++++++++++-
drivers/md/md.h | 16 +++
drivers/md/raid1.c | 270 ++++++++++++++++++++++++++++++++++++++++++++-
drivers/md/raid1.h | 1 +
4 files changed, 348 insertions(+), 10 deletions(-)
diff --git a/drivers/md/md.c b/drivers/md/md.c
index aebbdbaa4e0a..9b818fcef666 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -74,6 +74,7 @@ static const char *action_name[NR_SYNC_ACTIONS] = {
[ACTION_RECOVER] = "recover",
[ACTION_CHECK] = "check",
[ACTION_REPAIR] = "repair",
+ [ACTION_RECTIFY] = "rectify",
[ACTION_RESHAPE] = "reshape",
[ACTION_FROZEN] = "frozen",
[ACTION_IDLE] = "idle",
@@ -665,13 +666,47 @@ void mddev_put(struct mddev *mddev)
spin_unlock(&all_mddevs_lock);
}
+static int md_badblocks_precheck(struct mddev *mddev)
+{
+ struct md_rdev *rdev;
+ int valid_disks = 0;
+ int ret = -EINVAL;
+
+ /* rectify is currently supported only for RAID1 */
+ if (mddev->level != 1) {
+ pr_err("md/raid1:%s requires raid1 array\n", mdname(mddev));
+ return -EINVAL;
+ }
+
+ rdev_for_each(rdev, mddev) {
+ if (rdev->raid_disk < 0 ||
+ test_bit(Faulty, &rdev->flags))
+ continue;
+ valid_disks++;
+ }
+ if (valid_disks >= 2)
+ ret = 0;
+
+ return ret;
+}
+
static int handle_requested_sync_action(struct mddev *mddev,
enum sync_action action)
{
+ int ret;
+
if (test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery))
return -EBUSY;
switch (action) {
+ case ACTION_RECTIFY:
+ ret = md_badblocks_precheck(mddev);
+ if (ret)
+ return ret;
+ set_bit(MD_RECOVERY_BADBLOCKS_RECTIFY, &mddev->recovery);
+ set_bit(MD_RECOVERY_REQUESTED, &mddev->recovery);
+ set_bit(MD_RECOVERY_SYNC, &mddev->recovery);
+ return 0;
case ACTION_CHECK:
set_bit(MD_RECOVERY_CHECK, &mddev->recovery);
fallthrough;
@@ -686,6 +721,8 @@ static int handle_requested_sync_action(struct mddev *mddev,
static enum sync_action get_recovery_sync_action(struct mddev *mddev)
{
+ if (test_bit(MD_RECOVERY_BADBLOCKS_RECTIFY, &mddev->recovery))
+ return ACTION_RECTIFY;
if (test_bit(MD_RECOVERY_CHECK, &mddev->recovery))
return ACTION_CHECK;
if (test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery))
@@ -695,11 +732,16 @@ static enum sync_action get_recovery_sync_action(struct mddev *mddev)
static void set_requested_position(struct mddev *mddev, sector_t value)
{
- mddev->resync_min = value;
+ if (test_bit(MD_RECOVERY_BADBLOCKS_RECTIFY, &mddev->recovery))
+ mddev->rectify_min = value;
+ else
+ mddev->resync_min = value;
}
static sector_t get_requested_position(struct mddev *mddev)
{
+ if (test_bit(MD_RECOVERY_BADBLOCKS_RECTIFY, &mddev->recovery))
+ return mddev->rectify_min;
return mddev->resync_min;
}
@@ -820,6 +862,7 @@ int mddev_init(struct mddev *mddev)
mddev->reshape_backwards = 0;
mddev->last_sync_action = ACTION_IDLE;
mddev->resync_min = 0;
+ mddev->rectify_min = 0;
mddev->resync_max = MaxSector;
mddev->level = LEVEL_NONE;
@@ -5139,7 +5182,10 @@ enum sync_action md_sync_action(struct mddev *mddev)
if (test_bit(MD_RECOVERY_RECOVER, &recovery))
return ACTION_RECOVER;
- /* MD_RECOVERY_CHECK must be paired with MD_RECOVERY_REQUESTED. */
+ /*
+ * MD_RECOVERY_CHECK / MD_RECOVERY_BADBLOCKS_RECTIFY must be
+ * paired with MD_RECOVERY_REQUESTED.
+ */
if (test_bit(MD_RECOVERY_SYNC, &recovery))
return get_recovery_sync_action(mddev);
@@ -5304,6 +5350,7 @@ action_store(struct mddev *mddev, const char *page, size_t len)
break;
case ACTION_RESHAPE:
case ACTION_RECOVER:
+ case ACTION_RECTIFY:
case ACTION_CHECK:
case ACTION_REPAIR:
case ACTION_RESYNC:
@@ -5329,6 +5376,7 @@ action_store(struct mddev *mddev, const char *page, size_t len)
clear_bit(MD_RECOVERY_FROZEN, &mddev->recovery);
set_bit(MD_RECOVERY_RECOVER, &mddev->recovery);
break;
+ case ACTION_RECTIFY:
case ACTION_CHECK:
case ACTION_REPAIR:
ret = handle_requested_sync_action(mddev, action);
@@ -6813,6 +6861,7 @@ static void md_clean(struct mddev *mddev)
mddev->raid_disks = 0;
mddev->resync_offset = 0;
mddev->resync_min = 0;
+ mddev->rectify_min = 0;
mddev->resync_max = MaxSector;
mddev->reshape_position = MaxSector;
/* we still need mddev->external in export_rdev, do not clear it yet */
@@ -9343,6 +9392,7 @@ static sector_t md_sync_max_sectors(struct mddev *mddev,
{
switch (action) {
case ACTION_RESYNC:
+ case ACTION_RECTIFY:
case ACTION_CHECK:
case ACTION_REPAIR:
atomic64_set(&mddev->resync_mismatches, 0);
@@ -9395,6 +9445,7 @@ static sector_t md_sync_position(struct mddev *mddev, enum sync_action action)
struct md_rdev *rdev;
switch (action) {
+ case ACTION_RECTIFY:
case ACTION_CHECK:
case ACTION_REPAIR:
return get_requested_position(mddev);
@@ -10020,6 +10071,7 @@ static bool md_choose_sync_action(struct mddev *mddev, int *spares)
clear_bit(MD_RECOVERY_SYNC, &mddev->recovery);
clear_bit(MD_RECOVERY_CHECK, &mddev->recovery);
clear_bit(MD_RECOVERY_REQUESTED, &mddev->recovery);
+ clear_bit(MD_RECOVERY_BADBLOCKS_RECTIFY, &mddev->recovery);
/* Start new recovery. */
set_bit(MD_RECOVERY_RECOVER, &mddev->recovery);
@@ -10077,10 +10129,14 @@ static void md_start_sync(struct work_struct *ws)
if (spares && md_bitmap_enabled(mddev, true))
mddev->bitmap_ops->write_all(mddev);
- name = test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery) ?
- "reshape" : "resync";
- rcu_assign_pointer(mddev->sync_thread,
- md_register_thread(md_do_sync, mddev, name));
+ if (!is_badblocks_recovery_requested(mddev) ||
+ !md_badblocks_precheck(mddev)) {
+ name = test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery) ?
+ "reshape" : "resync";
+ rcu_assign_pointer(mddev->sync_thread,
+ md_register_thread(md_do_sync, mddev, name));
+ }
+
if (!mddev->sync_thread) {
pr_warn("%s: could not start resync thread...\n",
mdname(mddev));
@@ -10108,6 +10164,7 @@ static void md_start_sync(struct work_struct *ws)
clear_bit(MD_RECOVERY_REQUESTED, &mddev->recovery);
clear_bit(MD_RECOVERY_CHECK, &mddev->recovery);
clear_bit(MD_RECOVERY_RUNNING, &mddev->recovery);
+ clear_bit(MD_RECOVERY_BADBLOCKS_RECTIFY, &mddev->recovery);
mddev_unlock(mddev);
/*
* md_start_sync was triggered by MD_RECOVERY_NEEDED, so we should
@@ -10322,6 +10379,7 @@ void md_check_recovery(struct mddev *mddev)
clear_bit(MD_RECOVERY_SYNC, &mddev->recovery);
clear_bit(MD_RECOVERY_REQUESTED, &mddev->recovery);
clear_bit(MD_RECOVERY_CHECK, &mddev->recovery);
+ clear_bit(MD_RECOVERY_BADBLOCKS_RECTIFY, &mddev->recovery);
wake_up(&resync_wait);
}
@@ -10372,6 +10430,7 @@ void md_reap_sync_thread(struct mddev *mddev)
clear_bit(MD_RECOVERY_RESHAPE, &mddev->recovery);
clear_bit(MD_RECOVERY_REQUESTED, &mddev->recovery);
clear_bit(MD_RECOVERY_CHECK, &mddev->recovery);
+ clear_bit(MD_RECOVERY_BADBLOCKS_RECTIFY, &mddev->recovery);
clear_bit(MD_RECOVERY_LAZY_RECOVER, &mddev->recovery);
/*
* We call mddev->cluster_ops->update_size here because sync_size could
diff --git a/drivers/md/md.h b/drivers/md/md.h
index 14f9db38b7c5..0b9e3487bfed 100644
--- a/drivers/md/md.h
+++ b/drivers/md/md.h
@@ -102,6 +102,13 @@ enum sync_action {
* are inconsistent data,
*/
ACTION_REPAIR,
+ /*
+ * Represent by MD_RECOVERY_SYNC | MD_RECOVERY_REQUESTED |
+ * MD_RECOVERY_BADBLOCKS_RECTIFY, start when user echo "rectify"
+ * to sysfs api sync_action, used to repair the badblocks acked
+ * in bad table;
+ */
+ ACTION_RECTIFY,
/*
* Represent by MD_RECOVERY_RESHAPE, start when new member disk is added
* to the conf, notice that this is different from spares or
@@ -528,6 +535,7 @@ struct mddev {
sector_t resync_offset;
sector_t resync_min; /* user requested sync
* starts here */
+ sector_t rectify_min;
sector_t resync_max; /* resync should pause
* when it gets here */
@@ -668,6 +676,8 @@ enum recovery_flags {
MD_RESYNCING_REMOTE,
/* raid456 lazy initial recover */
MD_RECOVERY_LAZY_RECOVER,
+ /* try to repair acked badblocks*/
+ MD_RECOVERY_BADBLOCKS_RECTIFY,
};
enum md_ro_state {
@@ -1020,6 +1030,12 @@ static inline void mddev_unlock_and_resume(struct mddev *mddev)
mddev_resume(mddev);
}
+static inline bool is_badblocks_recovery_requested(struct mddev *mddev)
+{
+ return test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery) &&
+ test_bit(MD_RECOVERY_BADBLOCKS_RECTIFY, &mddev->recovery);
+}
+
struct mdu_array_info_s;
struct mdu_disk_info_s;
diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
index 00120c86c443..90686a0ff9ca 100644
--- a/drivers/md/raid1.c
+++ b/drivers/md/raid1.c
@@ -176,7 +176,8 @@ static void * r1buf_pool_alloc(gfp_t gfp_flags, void *data)
* If this is a user-requested check/repair, allocate
* RESYNC_PAGES for each bio.
*/
- if (test_bit(MD_RECOVERY_REQUESTED, &conf->mddev->recovery))
+ if (test_bit(MD_RECOVERY_REQUESTED, &conf->mddev->recovery) &&
+ !is_badblocks_recovery_requested(conf->mddev))
need_pages = conf->raid_disks * 2;
else
need_pages = 1;
@@ -2380,6 +2381,260 @@ static void sync_request_write(struct mddev *mddev, struct r1bio *r1_bio)
put_sync_write_buf(r1_bio);
}
+static void end_rectify_read(struct bio *bio)
+{
+ struct r1bio *r1_bio = get_resync_r1bio(bio);
+ struct r1conf *conf = r1_bio->mddev->private;
+ struct md_rdev *rdev;
+ struct bio *next_bio;
+ bool all_fail = true;
+ int i;
+
+ update_head_pos(r1_bio->read_disk, r1_bio);
+
+ if (!bio->bi_status) {
+ set_bit(R1BIO_Uptodate, &r1_bio->state);
+ goto out;
+ }
+
+ for (i = r1_bio->read_disk + 1; i < conf->raid_disks; i++) {
+ rdev = conf->mirrors[i].rdev;
+ if (!rdev || test_bit(Faulty, &rdev->flags))
+ continue;
+
+ next_bio = r1_bio->bios[i];
+ if (next_bio->bi_end_io == end_rectify_read) {
+ r1_bio->read_disk = i;
+ all_fail = false;
+ break;
+ }
+ }
+
+ if (unlikely(all_fail)) {
+ md_done_sync(r1_bio->mddev, r1_bio->sectors);
+ md_sync_error(r1_bio->mddev);
+ put_buf(r1_bio);
+ return;
+ }
+out:
+ reschedule_retry(r1_bio);
+}
+
+static void end_rectify_write(struct bio *bio)
+{
+ struct r1bio *r1_bio = get_resync_r1bio(bio);
+
+ if (atomic_dec_and_test(&r1_bio->remaining)) {
+ /*
+ * Rectify only attempts to clear acked bad
+ * blocks, and it does not set bad blocks in
+ * cases of R1BIO_WriteError.
+ * Here we reuse R1BIO_MadeGood flag, which
+ * does not guarantee that all write I/Os
+ * actually succeeded.
+ */
+ set_bit(R1BIO_MadeGood, &r1_bio->state);
+ reschedule_retry(r1_bio);
+ }
+}
+
+static void submit_rectify_read(struct r1bio *r1_bio)
+{
+ struct bio *bio;
+
+ bio = r1_bio->bios[r1_bio->read_disk];
+ bio->bi_status = 0;
+ submit_bio_noacct(bio);
+}
+
+static void rectify_request_write(struct mddev *mddev, struct r1bio *r1_bio)
+{
+ struct r1conf *conf = mddev->private;
+ struct bio *wbio = NULL;
+ struct md_rdev *rdev;
+ int wcnt = 0;
+ int i;
+
+ if (!test_bit(R1BIO_Uptodate, &r1_bio->state)) {
+ submit_rectify_read(r1_bio);
+ return;
+ }
+
+ atomic_set(&r1_bio->remaining, 0);
+ for (i = 0; i < conf->raid_disks; i++) {
+ rdev = conf->mirrors[i].rdev;
+ if (!rdev || test_bit(Faulty, &rdev->flags))
+ continue;
+ wbio = r1_bio->bios[i];
+ if (wbio->bi_end_io == end_rectify_write) {
+ atomic_inc(&r1_bio->remaining);
+ wcnt++;
+ submit_bio_noacct(wbio);
+ }
+ }
+
+ if (unlikely(!wcnt)) {
+ md_done_sync(r1_bio->mddev, r1_bio->sectors);
+ put_buf(r1_bio);
+ }
+}
+
+static void handle_sync_write(struct mddev *mddev, struct r1bio *r1_bio)
+{
+ if (test_bit(R1BIO_BadBlocksRectify, &r1_bio->state))
+ rectify_request_write(mddev, r1_bio);
+ else
+ sync_request_write(mddev, r1_bio);
+}
+
+static sector_t get_badblocks_sync_sectors(struct mddev *mddev, sector_t sector_nr,
+ int *skipped, unsigned long *bad_disks)
+{
+ struct r1conf *conf = mddev->private;
+ sector_t nr_sectors = mddev->dev_sectors - sector_nr;
+ bool all_faulty = true;
+ struct md_rdev *rdev;
+ bool good = false;
+ int i;
+
+ *skipped = 0;
+ for (i = 0; i < conf->raid_disks; i++) {
+ sector_t first_bad;
+ sector_t bad_sectors;
+
+ rdev = conf->mirrors[i].rdev;
+ if (!rdev || test_bit(Faulty, &rdev->flags))
+ continue;
+
+ all_faulty = false;
+ if (is_badblock(rdev, sector_nr, nr_sectors, &first_bad, &bad_sectors)) {
+ if (first_bad <= sector_nr) {
+ set_bit(i, bad_disks);
+ nr_sectors = min(nr_sectors, first_bad + bad_sectors - sector_nr);
+ } else {
+ good = true;
+ nr_sectors = min(nr_sectors, first_bad - sector_nr);
+ }
+ } else {
+ good = true;
+ }
+ }
+
+ if (all_faulty) {
+ *skipped = 1;
+ return 0;
+ }
+
+ if (!good || !bitmap_weight(bad_disks, conf->raid_disks))
+ *skipped = 1;
+
+ /* make sure nr_sectors won't go across barrier unit boundary */
+ return align_to_barrier_unit_end(sector_nr, nr_sectors);
+}
+
+static sector_t get_next_sync_sector(struct mddev *mddev, sector_t sector_nr,
+ int *skipped, unsigned long *bad_disks)
+{
+ sector_t nr_sectors;
+
+ nr_sectors = get_badblocks_sync_sectors(mddev, sector_nr,
+ skipped, bad_disks);
+ if (!(*skipped) && nr_sectors > RESYNC_PAGES * (PAGE_SIZE >> 9))
+ nr_sectors = RESYNC_PAGES * (PAGE_SIZE >> 9);
+ return nr_sectors;
+}
+
+static struct r1bio *raid1_alloc_init_r1buf(struct r1conf *conf);
+static struct r1bio *init_sync_badblocks_r1bio(struct mddev *mddev,
+ sector_t sector_nr,
+ sector_t nr_sectors,
+ unsigned long *bad_disks)
+{
+ struct r1conf *conf = mddev->private;
+ struct r1bio *r1_bio;
+ struct md_rdev *rdev;
+ int page_idx = 0;
+ struct bio *bio;
+ int i;
+
+ r1_bio = raid1_alloc_init_r1buf(conf);
+ r1_bio->mddev = mddev;
+ r1_bio->sector = sector_nr;
+ r1_bio->sectors = nr_sectors;
+ r1_bio->state = 0;
+ r1_bio->read_disk = -1;
+ set_bit(R1BIO_IsSync, &r1_bio->state);
+ set_bit(R1BIO_BadBlocksRectify, &r1_bio->state);
+
+ for (i = 0; i < conf->raid_disks; i++) {
+ rdev = conf->mirrors[i].rdev;
+ if (!rdev || test_bit(Faulty, &rdev->flags))
+ continue;
+
+ if (r1_bio->read_disk < 0 && !test_bit(i, bad_disks))
+ r1_bio->read_disk = i;
+
+ bio = r1_bio->bios[i];
+ if (test_bit(i, bad_disks)) {
+ bio->bi_opf = REQ_OP_WRITE;
+ bio->bi_end_io = end_rectify_write;
+ } else {
+ bio->bi_opf = REQ_OP_READ;
+ bio->bi_end_io = end_rectify_read;
+ }
+
+ atomic_inc(&rdev->nr_pending);
+ bio->bi_iter.bi_sector = sector_nr + rdev->data_offset;
+ bio_set_dev(bio, rdev->bdev);
+ }
+
+ if (unlikely(r1_bio->read_disk < 0)) {
+ put_buf(r1_bio);
+ return NULL;
+ }
+
+ while (nr_sectors > 0 && page_idx < RESYNC_PAGES) {
+ int len = nr_sectors << 9 < PAGE_SIZE ?
+ nr_sectors << 9 : PAGE_SIZE;
+ struct resync_pages *rp;
+
+ for (i = 0; i < conf->raid_disks; i++) {
+ bio = r1_bio->bios[i];
+ rp = get_resync_pages(bio);
+ __bio_add_page(bio, resync_fetch_page(rp, page_idx), len, 0);
+ }
+
+ nr_sectors -= len >> 9;
+ page_idx++;
+ }
+
+ return r1_bio;
+}
+
+static sector_t do_sync_badblocks_rectify(struct mddev *mddev,
+ sector_t sector_nr, int *skipped)
+{
+ DECLARE_BITMAP(bad_disks, MAX_RAID_DISKS);
+ struct r1conf *conf = mddev->private;
+ struct r1bio *r1_bio;
+ sector_t nr_sectors;
+
+ bitmap_zero(bad_disks, MAX_RAID_DISKS);
+ nr_sectors = get_next_sync_sector(mddev, sector_nr, skipped, bad_disks);
+ if (*skipped) {
+ lower_barrier(conf, sector_nr);
+ return nr_sectors;
+ }
+
+ r1_bio = init_sync_badblocks_r1bio(mddev, sector_nr,
+ nr_sectors, bad_disks);
+ if (!r1_bio)
+ return 0;
+
+ submit_rectify_read(r1_bio);
+ return nr_sectors;
+}
+
/*
* This is a kernel thread which:
*
@@ -2558,13 +2813,16 @@ static void handle_sync_write_finished(struct r1conf *conf, struct r1bio *r1_bio
{
int m;
int s = r1_bio->sectors;
+ bool is_rectify = test_bit(R1BIO_BadBlocksRectify, &r1_bio->state);
+
for (m = 0; m < conf->raid_disks * 2 ; m++) {
struct md_rdev *rdev = conf->mirrors[m].rdev;
struct bio *bio = r1_bio->bios[m];
if (bio->bi_end_io == NULL)
continue;
if (!bio->bi_status &&
- test_bit(R1BIO_MadeGood, &r1_bio->state))
+ test_bit(R1BIO_MadeGood, &r1_bio->state) &&
+ (!is_rectify || bio->bi_end_io == end_rectify_write))
rdev_clear_badblocks(rdev, r1_bio->sector, s, 0);
if (bio->bi_status &&
test_bit(R1BIO_WriteError, &r1_bio->state))
@@ -2728,7 +2986,7 @@ static void raid1d(struct md_thread *thread)
test_bit(R1BIO_WriteError, &r1_bio->state))
handle_sync_write_finished(conf, r1_bio);
else
- sync_request_write(mddev, r1_bio);
+ handle_sync_write(mddev, r1_bio);
} else if (test_bit(R1BIO_MadeGood, &r1_bio->state) ||
test_bit(R1BIO_WriteError, &r1_bio->state))
handle_write_finished(conf, r1_bio);
@@ -2837,7 +3095,8 @@ static sector_t raid1_sync_request(struct mddev *mddev, sector_t sector_nr,
/* before building a request, check if we can skip these blocks..
* This call the bitmap_start_sync doesn't actually record anything
*/
- if (!md_bitmap_start_sync(mddev, sector_nr, &sync_blocks, true) &&
+ if (!is_badblocks_recovery_requested(mddev) &&
+ !md_bitmap_start_sync(mddev, sector_nr, &sync_blocks, true) &&
!conf->fullsync && !test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery)) {
/* We can skip this block, and probably several more */
*skipped = 1;
@@ -2863,6 +3122,9 @@ static sector_t raid1_sync_request(struct mddev *mddev, sector_t sector_nr,
if (raise_barrier(conf, sector_nr))
return 0;
+ if (is_badblocks_recovery_requested(mddev))
+ return do_sync_badblocks_rectify(mddev, sector_nr, skipped);
+
r1_bio = raid1_alloc_init_r1buf(conf);
/*
diff --git a/drivers/md/raid1.h b/drivers/md/raid1.h
index c98d43a7ae99..6ca8bf808d69 100644
--- a/drivers/md/raid1.h
+++ b/drivers/md/raid1.h
@@ -184,6 +184,7 @@ enum r1bio_state {
R1BIO_MadeGood,
R1BIO_WriteError,
R1BIO_FailFast,
+ R1BIO_BadBlocksRectify,
};
static inline int sector_to_idx(sector_t sector)
--
2.39.2
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox