* Re: [PATCH v2] dm-raid: only requeue bios when dm is suspending.
From: Xiao Ni @ 2026-05-09 9:18 UTC (permalink / raw)
To: Benjamin Marzinski
Cc: Yu Kuai, Song Liu, linux-raid, dm-devel, Yang Xiuwei, Li Nan,
Nigel Croxon
In-Reply-To: <20260428232010.2785514-1-bmarzins@redhat.com>
On Wed, Apr 29, 2026 at 7:20 AM Benjamin Marzinski <bmarzins@redhat.com> wrote:
>
> returning DM_MAPIO_REQUEUE from the target map() function only requeues
> the bio during noflush suspends. During regular operations or during
> flushing suspends, it fails the bio. Failing the bio during flushing
> suspends is the correct behavior here. We cannot handle the bio, and we
> cannot suspends while it is outstanding. But during normal operations,
> we should not push the bio back to dm. Instead, wait for the reshape
> to be resumed.
>
> Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
> ---
>
> Changes from v1:
> - Track the dm device's suspending state in mddev->flags instead of
> adding a new integer to mddev.
>
> drivers/md/dm-raid.c | 6 ++++++
> drivers/md/md.h | 2 ++
> drivers/md/raid5.c | 7 +++++--
> 3 files changed, 13 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/md/dm-raid.c b/drivers/md/dm-raid.c
> index c5dc083c7244..8f5a5e1342a9 100644
> --- a/drivers/md/dm-raid.c
> +++ b/drivers/md/dm-raid.c
> @@ -3831,6 +3831,7 @@ static void raid_presuspend(struct dm_target *ti)
> * resume, raid_postsuspend() is too late.
> */
> set_bit(RT_FLAG_RS_FROZEN, &rs->runtime_flags);
> + set_bit(MD_DM_SUSPENDING, &mddev->flags);
>
> if (!reshape_interrupted(mddev))
> return;
> @@ -3847,13 +3848,16 @@ static void raid_presuspend(struct dm_target *ti)
> static void raid_presuspend_undo(struct dm_target *ti)
> {
> struct raid_set *rs = ti->private;
> + struct mddev *mddev = &rs->md;
>
> + clear_bit(MD_DM_SUSPENDING, &mddev->flags);
> clear_bit(RT_FLAG_RS_FROZEN, &rs->runtime_flags);
> }
>
> static void raid_postsuspend(struct dm_target *ti)
> {
> struct raid_set *rs = ti->private;
> + struct mddev *mddev = &rs->md;
>
> if (!test_and_set_bit(RT_FLAG_RS_SUSPENDED, &rs->runtime_flags)) {
> /*
> @@ -3864,6 +3868,8 @@ static void raid_postsuspend(struct dm_target *ti)
> mddev_suspend(&rs->md, false);
> rs->md.ro = MD_RDONLY;
> }
> + clear_bit(MD_DM_SUSPENDING, &mddev->flags);
> +
> }
>
> static void attempt_restore_of_faulty_devices(struct raid_set *rs)
> diff --git a/drivers/md/md.h b/drivers/md/md.h
> index 52c378086046..9e5100609d12 100644
> --- a/drivers/md/md.h
> +++ b/drivers/md/md.h
> @@ -346,6 +346,7 @@ struct md_cluster_operations;
> * @MD_HAS_SUPERBLOCK: There is persistence sb in member disks.
> * @MD_FAILLAST_DEV: Allow last rdev to be removed.
> * @MD_SERIALIZE_POLICY: Enforce write IO is not reordered, just used by raid1.
> + * @MD_DM_SUSPENDING: This DM raid device is suspending.
> *
> * change UNSUPPORTED_MDDEV_FLAGS for each array type if new flag is added
> */
> @@ -365,6 +366,7 @@ enum mddev_flags {
> MD_HAS_SUPERBLOCK,
> MD_FAILLAST_DEV,
> MD_SERIALIZE_POLICY,
> + MD_DM_SUSPENDING,
> };
>
> enum mddev_sb_flags {
> diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
> index 0d76e82f4506..65ae7d8930fc 100644
> --- a/drivers/md/raid5.c
> +++ b/drivers/md/raid5.c
> @@ -6042,8 +6042,11 @@ static enum stripe_result make_stripe_request(struct mddev *mddev,
> raid5_release_stripe(sh);
> out:
> if (ret == STRIPE_SCHEDULE_AND_RETRY && reshape_interrupted(mddev)) {
> - bi->bi_status = BLK_STS_RESOURCE;
> - ret = STRIPE_WAIT_RESHAPE;
> + if (!mddev_is_dm(mddev) ||
> + test_bit(MD_DM_SUSPENDING, &mddev->flags)) {
> + bi->bi_status = BLK_STS_RESOURCE;
> + ret = STRIPE_WAIT_RESHAPE;
> + }
> pr_err_ratelimited("dm-raid456: io across reshape position while reshape can't make progress");
> }
> return ret;
> --
> 2.53.0
>
This patch looks good to me.
Reviewed-by: Xiao Ni <xiao@kernel.org>
^ permalink raw reply
* [PATCH RESEND] MAINTAINERS: Update Li Nan's E-mail address
From: Li Nan @ 2026-05-08 9:55 UTC (permalink / raw)
To: song, yukuai; +Cc: zhangtonghao, linux-kernel, linux-raid, xiao, magiclinan
From: Li Nan <magiclinan@didiglobal.com>
Change to my new email address on didiglobal.com.
Signed-off-by: Li Nan <magiclinan@didiglobal.com>
---
add cc linux-raid
MAINTAINERS | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/MAINTAINERS b/MAINTAINERS
index 06c00e40999f..96702de58cc1 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -24763,7 +24763,7 @@ F: include/linux/property.h
SOFTWARE RAID (Multiple Disks) SUPPORT
M: Song Liu <song@kernel.org>
M: Yu Kuai <yukuai@fnnas.com>
-R: Li Nan <linan122@huawei.com>
+R: Li Nan <magiclinan@didiglobal.com>
R: Xiao Ni <xiao@kernel.org>
L: linux-raid@vger.kernel.org
S: Supported
--
2.50.1 (Apple Git-155)
^ permalink raw reply related
* Re: [PATCH v3 6/8] md/raid1,raid10: use folio for sync path IO
From: 李楠 Magic Li @ 2026-05-07 7:13 UTC (permalink / raw)
To: Xiao Ni, linan666@huaweicloud.com
Cc: song@kernel.org, yukuai@fnnas.com, linux-raid@vger.kernel.org,
linux-kernel@vger.kernel.org, yangerkun@huawei.com,
yi.zhang@huawei.com, 张同浩 Tonghao Zhang
In-Reply-To: <CALTww29duRpWZkYQjRJM45DAG=gLiPzbtn9x+CHAFcF3XJXRTg@mail.gmail.com>
On Thu Apr 30, 2026 at 9:54 AM CST, Xiao Ni wrote:
> Hi Nan
>
> On Thu, Apr 16, 2026 at 11:55 AM <linan666@huaweicloud.com> wrote:
>>
>> From: Li Nan <linan122@huawei.com>
>>
>> Convert all IO on the sync path to use folios, and rename page-related
>> identifiers to match folio.
>>
>> Since RESYNC_BLOCK_SIZE (64K) has higher allocation failure chance than 4k,
>> retry with lower orders to improve allocation reliability. A r1/10_bio may
>> have different rf->folio orders, so use minimum order as r1/10_bio sectors
>> to prevent exceeding size when adding folio to IO later.
>>
>> Clean up:
>> 1. Remove resync_get_all_folio() and invoke folio_get() directly instead.
>> 2. Clean up redundant while(0) loop in md_bio_reset_resync_folio().
>> 3. Clean up bio variable by directly referencing r10_bio->devs[j].bio
>> instead in r1buf_pool_alloc() and r10buf_pool_alloc().
>> 4. Clean up RESYNC_PAGES.
>> 5. Remove resync_fetch_folio(), access 'rf->folio' directly.
>> 6. Remove resync_free_folio(), call folio_put() directly.
>> 7. clean up sync IO size calculation in raid1/10_sync_request.
>>
>> Signed-off-by: Li Nan <linan122@huawei.com>
>> ---
>> drivers/md/md.c | 2 +-
>> drivers/md/raid1-10.c | 80 ++++---------
>> drivers/md/raid1.c | 209 +++++++++++++++-------------------
>> drivers/md/raid10.c | 254 +++++++++++++++++++++---------------------
>> 4 files changed, 240 insertions(+), 305 deletions(-)
>>
>> diff --git a/drivers/md/md.c b/drivers/md/md.c
>> index 5e83914d5c14..6554b849ac74 100644
>> --- a/drivers/md/md.c
>> +++ b/drivers/md/md.c
>> @@ -9440,7 +9440,7 @@ static bool sync_io_within_limit(struct mddev *mddev)
>> {
>> /*
>> * For raid456, sync IO is stripe(4k) per IO, for other levels, it's
>> - * RESYNC_PAGES(64k) per IO.
>> + * RESYNC_BLOCK_SIZE(64k) per IO.
>> */
>> return atomic_read(&mddev->recovery_active) <
>> (raid_is_456(mddev) ? 8 : 128) * sync_io_depth(mddev);
>> diff --git a/drivers/md/raid1-10.c b/drivers/md/raid1-10.c
>> index cda531d0720b..10200b0a3fd2 100644
>> --- a/drivers/md/raid1-10.c
>> +++ b/drivers/md/raid1-10.c
>> @@ -1,7 +1,6 @@
>> // SPDX-License-Identifier: GPL-2.0
>> /* Maximum size of each resync request */
>> #define RESYNC_BLOCK_SIZE (64*1024)
>> -#define RESYNC_PAGES ((RESYNC_BLOCK_SIZE + PAGE_SIZE-1) / PAGE_SIZE)
>> #define RESYNC_SECTORS (RESYNC_BLOCK_SIZE >> 9)
>>
>> /* when we get a read error on a read-only array, we redirect to another
>> @@ -20,9 +19,9 @@
>> #define MAX_PLUG_BIO 32
>>
>> /* for managing resync I/O pages */
>> -struct resync_pages {
>> +struct resync_folio {
>> void *raid_bio;
>> - struct page *pages[RESYNC_PAGES];
>> + struct folio *folio;
>> };
>>
>> struct raid1_plug_cb {
>> @@ -36,77 +35,44 @@ static void rbio_pool_free(void *rbio, void *data)
>> kfree(rbio);
>> }
>>
>> -static inline int resync_alloc_pages(struct resync_pages *rp,
>> - gfp_t gfp_flags)
>> +static inline int resync_alloc_folio(struct resync_folio *rf,
>> + gfp_t gfp_flags, int *order)
>> {
>> - int i;
>> + struct folio *folio;
>>
>> - for (i = 0; i < RESYNC_PAGES; i++) {
>> - rp->pages[i] = alloc_page(gfp_flags);
>> - if (!rp->pages[i])
>> - goto out_free;
>> - }
>> + do {
>> + folio = folio_alloc(gfp_flags, *order);
>> + if (folio)
>> + break;
>> + } while (--(*order) > 0);
>
> It has a problem here. If it can't allocate a big page, the sync
> request unit will be smaller and sync performance may decrease. This
> can happen when the system lacks sufficient continuous memory. This
> change looks good to me. I just want to throw this problem out for an
> open discussion.
>
Yeah, it can be easily reproduced in qemu. We have a few options:
1. Alloc smaller folio
2. Return -ENOMEM directly
3. Alloc multiple small folios to assemble a larger one. It is not and
good idea, as it will make the code much more complex.
IMO, 1 seems like the best choice.
>>
>> + if (!folio)
>> + return -ENOMEM;
>> +
>> + rf->folio = folio;
>> return 0;
>> -
>> -out_free:
>> - while (--i >= 0)
>> - put_page(rp->pages[i]);
>> - return -ENOMEM;
>> -}
>> -
>> -static inline void resync_free_pages(struct resync_pages *rp)
>> -{
>> - int i;
>> -
>> - for (i = 0; i < RESYNC_PAGES; i++)
>> - put_page(rp->pages[i]);
>> -}
>> -
>> -static inline void resync_get_all_pages(struct resync_pages *rp)
>> -{
>> - int i;
>> -
>> - for (i = 0; i < RESYNC_PAGES; i++)
>> - get_page(rp->pages[i]);
>> -}
>> -
>> -static inline struct page *resync_fetch_page(struct resync_pages *rp,
>> - unsigned idx)
>> -{
>> - if (WARN_ON_ONCE(idx >= RESYNC_PAGES))
>> - return NULL;
>> - return rp->pages[idx];
>> }
>>
>> /*
>> - * 'strct resync_pages' stores actual pages used for doing the resync
>> + * 'strct resync_folio' stores actual pages used for doing the resync
>> * IO, and it is per-bio, so make .bi_private points to it.
>> */
>> -static inline struct resync_pages *get_resync_pages(struct bio *bio)
>> +static inline struct resync_folio *get_resync_folio(struct bio *bio)
>> {
>> return bio->bi_private;
>> }
>>
>> /* generally called after bio_reset() for reseting bvec */
>> -static void md_bio_reset_resync_pages(struct bio *bio, struct resync_pages *rp,
>> +static void md_bio_reset_resync_folio(struct bio *bio, struct resync_folio *rf,
>> int size)
>> {
>> - int idx = 0;
>> -
>> /* initialize bvec table again */
>> - do {
>> - struct page *page = resync_fetch_page(rp, idx);
>> - int len = min_t(int, size, PAGE_SIZE);
>> -
>> - if (WARN_ON(!bio_add_page(bio, page, len, 0))) {
>> - bio->bi_status = BLK_STS_RESOURCE;
>> - bio_endio(bio);
>> - return;
>> - }
>> -
>> - size -= len;
>> - } while (idx++ < RESYNC_PAGES && size > 0);
>> + if (WARN_ON(!bio_add_folio(bio, rf->folio,
>> + min_t(int, size, RESYNC_BLOCK_SIZE),
>> + 0))) {
>> + bio->bi_status = BLK_STS_RESOURCE;
>> + bio_endio(bio);
>> + }
>> }
>>
>>
>> diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
>> index a72abdc37a2d..724fd4f2cc3a 100644
>> --- a/drivers/md/raid1.c
>> +++ b/drivers/md/raid1.c
>> @@ -120,11 +120,11 @@ static void remove_serial(struct md_rdev *rdev, sector_t lo, sector_t hi)
>>
>> /*
>> * for resync bio, r1bio pointer can be retrieved from the per-bio
>> - * 'struct resync_pages'.
>> + * 'struct resync_folio'.
>> */
>> static inline struct r1bio *get_resync_r1bio(struct bio *bio)
>> {
>> - return get_resync_pages(bio)->raid_bio;
>> + return get_resync_folio(bio)->raid_bio;
>> }
>>
>> static void *r1bio_pool_alloc(gfp_t gfp_flags, struct r1conf *conf)
>> @@ -146,70 +146,69 @@ static void * r1buf_pool_alloc(gfp_t gfp_flags, void *data)
>> struct r1conf *conf = data;
>> struct r1bio *r1_bio;
>> struct bio *bio;
>> - int need_pages;
>> + int need_folio;
>
> The name need_folio is confusing. Can we keep the same style as the
> old version? How about need_folios?
>
Agree, I will rename it in v2.
>> int j;
>> - struct resync_pages *rps;
>> + struct resync_folio *rfs;
>> + int order = get_order(RESYNC_BLOCK_SIZE);
>>
>> r1_bio = r1bio_pool_alloc(gfp_flags, conf);
>> if (!r1_bio)
>> return NULL;
>>
>> - rps = kmalloc_array(conf->raid_disks * 2, sizeof(struct resync_pages),
>> + rfs = kmalloc_array(conf->raid_disks * 2, sizeof(struct resync_folio),
>> gfp_flags);
>> - if (!rps)
>> + if (!rfs)
>> goto out_free_r1bio;
>>
>> /*
>> * Allocate bios : 1 for reading, n-1 for writing
>> */
>> for (j = conf->raid_disks * 2; j-- ; ) {
>> - bio = bio_kmalloc(RESYNC_PAGES, gfp_flags);
>> + bio = bio_kmalloc(1, gfp_flags);
>> if (!bio)
>> goto out_free_bio;
>> - bio_init_inline(bio, NULL, RESYNC_PAGES, 0);
>> + bio_init_inline(bio, NULL, 1, 0);
>> r1_bio->bios[j] = bio;
>> }
>> /*
>> - * Allocate RESYNC_PAGES data pages and attach them to
>> - * the first bio.
>> + * Allocate data folio and attach it to the first bio.
>> * If this is a user-requested check/repair, allocate
>> - * RESYNC_PAGES for each bio.
>> + * folio for each bio.
>> */
>> if (test_bit(MD_RECOVERY_REQUESTED, &conf->mddev->recovery))
>> - need_pages = conf->raid_disks * 2;
>> + need_folio = conf->raid_disks * 2;
>> else
>> - need_pages = 1;
>> + need_folio = 1;
>> for (j = 0; j < conf->raid_disks * 2; j++) {
>> - struct resync_pages *rp = &rps[j];
>> + struct resync_folio *rf = &rfs[j];
>>
>> - bio = r1_bio->bios[j];
>> -
>> - if (j < need_pages) {
>> - if (resync_alloc_pages(rp, gfp_flags))
>> - goto out_free_pages;
>> + if (j < need_folio) {
>> + if (resync_alloc_folio(rf, gfp_flags, &order))
>> + goto out_free_folio;
>> } else {
>> - memcpy(rp, &rps[0], sizeof(*rp));
>> - resync_get_all_pages(rp);
>> + memcpy(rf, &rfs[0], sizeof(*rf));
>> + folio_get(rf->folio);
>> }
>>
>> - rp->raid_bio = r1_bio;
>> - bio->bi_private = rp;
>> + rf->raid_bio = r1_bio;
>> + r1_bio->bios[j]->bi_private = rf;
>> }
>>
>> + r1_bio->sectors = 1 << (order + PAGE_SECTORS_SHIFT);
>> r1_bio->master_bio = NULL;
>>
>> return r1_bio;
>>
>> -out_free_pages:
>> +out_free_folio:
>> while (--j >= 0)
>> - resync_free_pages(&rps[j]);
>> + folio_put(rfs[j].folio);
>>
>> out_free_bio:
>> while (++j < conf->raid_disks * 2) {
>> bio_uninit(r1_bio->bios[j]);
>> kfree(r1_bio->bios[j]);
>> }
>> - kfree(rps);
>> + kfree(rfs);
>>
>> out_free_r1bio:
>> rbio_pool_free(r1_bio, data);
>> @@ -221,17 +220,17 @@ static void r1buf_pool_free(void *__r1_bio, void *data)
>> struct r1conf *conf = data;
>> int i;
>> struct r1bio *r1bio = __r1_bio;
>> - struct resync_pages *rp = NULL;
>> + struct resync_folio *rf = NULL;
>>
>> for (i = conf->raid_disks * 2; i--; ) {
>> - rp = get_resync_pages(r1bio->bios[i]);
>> - resync_free_pages(rp);
>> + rf = get_resync_folio(r1bio->bios[i]);
>> + folio_put(rf->folio);
>> bio_uninit(r1bio->bios[i]);
>> kfree(r1bio->bios[i]);
>> }
>>
>> - /* resync pages array stored in the 1st bio's .bi_private */
>> - kfree(rp);
>> + /* resync folio stored in the 1st bio's .bi_private */
>> + kfree(rf);
>>
>> rbio_pool_free(r1bio, data);
>> }
>> @@ -2095,10 +2094,10 @@ static void end_sync_write(struct bio *bio)
>> put_sync_write_buf(r1_bio);
>> }
>>
>> -static int r1_sync_page_io(struct md_rdev *rdev, sector_t sector,
>> - int sectors, struct page *page, blk_opf_t rw)
>> +static int r1_sync_folio_io(struct md_rdev *rdev, sector_t sector, int sectors,
>> + int off, struct folio *folio, blk_opf_t rw)
>> {
>> - if (sync_page_io(rdev, sector, sectors << 9, page, rw, false))
>> + if (sync_folio_io(rdev, sector, sectors << 9, off, folio, rw, false))
>> /* success */
>> return 1;
>> if (rw == REQ_OP_WRITE) {
>> @@ -2129,10 +2128,10 @@ static int fix_sync_read_error(struct r1bio *r1_bio)
>> struct mddev *mddev = r1_bio->mddev;
>> struct r1conf *conf = mddev->private;
>> struct bio *bio = r1_bio->bios[r1_bio->read_disk];
>> - struct page **pages = get_resync_pages(bio)->pages;
>> + struct folio *folio = get_resync_folio(bio)->folio;
>> sector_t sect = r1_bio->sector;
>> int sectors = r1_bio->sectors;
>> - int idx = 0;
>> + int off = 0;
>> struct md_rdev *rdev;
>>
>> rdev = conf->mirrors[r1_bio->read_disk].rdev;
>> @@ -2162,9 +2161,8 @@ static int fix_sync_read_error(struct r1bio *r1_bio)
>> * active, and resync is currently active
>> */
>> rdev = conf->mirrors[d].rdev;
>> - if (sync_page_io(rdev, sect, s<<9,
>> - pages[idx],
>> - REQ_OP_READ, false)) {
>> + if (sync_folio_io(rdev, sect, s<<9, off, folio,
>> + REQ_OP_READ, false)) {
>> success = 1;
>> break;
>> }
>> @@ -2197,7 +2195,7 @@ static int fix_sync_read_error(struct r1bio *r1_bio)
>> /* Try next page */
>> sectors -= s;
>> sect += s;
>> - idx++;
>> + off += s << 9;
>> continue;
>> }
>>
>> @@ -2210,8 +2208,7 @@ static int fix_sync_read_error(struct r1bio *r1_bio)
>> if (r1_bio->bios[d]->bi_end_io != end_sync_read)
>> continue;
>> rdev = conf->mirrors[d].rdev;
>> - if (r1_sync_page_io(rdev, sect, s,
>> - pages[idx],
>> + if (r1_sync_folio_io(rdev, sect, s, off, folio,
>> REQ_OP_WRITE) == 0) {
>> r1_bio->bios[d]->bi_end_io = NULL;
>> rdev_dec_pending(rdev, mddev);
>> @@ -2225,14 +2222,13 @@ static int fix_sync_read_error(struct r1bio *r1_bio)
>> if (r1_bio->bios[d]->bi_end_io != end_sync_read)
>> continue;
>> rdev = conf->mirrors[d].rdev;
>> - if (r1_sync_page_io(rdev, sect, s,
>> - pages[idx],
>> + if (r1_sync_folio_io(rdev, sect, s, off, folio,
>> REQ_OP_READ) != 0)
>> atomic_add(s, &rdev->corrected_errors);
>> }
>> sectors -= s;
>> sect += s;
>> - idx ++;
>> + off += s << 9;
>> }
>> set_bit(R1BIO_Uptodate, &r1_bio->state);
>> bio->bi_status = 0;
>> @@ -2252,14 +2248,12 @@ static void process_checks(struct r1bio *r1_bio)
>> struct r1conf *conf = mddev->private;
>> int primary;
>> int i;
>> - int vcnt;
>>
>> /* Fix variable parts of all bios */
>> - vcnt = (r1_bio->sectors + PAGE_SIZE / 512 - 1) >> (PAGE_SHIFT - 9);
>> for (i = 0; i < conf->raid_disks * 2; i++) {
>> blk_status_t status;
>> struct bio *b = r1_bio->bios[i];
>> - struct resync_pages *rp = get_resync_pages(b);
>> + struct resync_folio *rf = get_resync_folio(b);
>> if (b->bi_end_io != end_sync_read)
>> continue;
>> /* fixup the bio for reuse, but preserve errno */
>> @@ -2269,11 +2263,11 @@ static void process_checks(struct r1bio *r1_bio)
>> b->bi_iter.bi_sector = r1_bio->sector +
>> conf->mirrors[i].rdev->data_offset;
>> b->bi_end_io = end_sync_read;
>> - rp->raid_bio = r1_bio;
>> - b->bi_private = rp;
>> + rf->raid_bio = r1_bio;
>> + b->bi_private = rf;
>>
>> /* initialize bvec table again */
>> - md_bio_reset_resync_pages(b, rp, r1_bio->sectors << 9);
>> + md_bio_reset_resync_folio(b, rf, r1_bio->sectors << 9);
>> }
>> for (primary = 0; primary < conf->raid_disks * 2; primary++)
>> if (r1_bio->bios[primary]->bi_end_io == end_sync_read &&
>> @@ -2284,44 +2278,39 @@ static void process_checks(struct r1bio *r1_bio)
>> }
>> r1_bio->read_disk = primary;
>> for (i = 0; i < conf->raid_disks * 2; i++) {
>> - int j = 0;
>> struct bio *pbio = r1_bio->bios[primary];
>> struct bio *sbio = r1_bio->bios[i];
>> blk_status_t status = sbio->bi_status;
>> - struct page **ppages = get_resync_pages(pbio)->pages;
>> - struct page **spages = get_resync_pages(sbio)->pages;
>> - struct bio_vec *bi;
>> - int page_len[RESYNC_PAGES] = { 0 };
>> - struct bvec_iter_all iter_all;
>> + struct folio *pfolio = get_resync_folio(pbio)->folio;
>> + struct folio *sfolio = get_resync_folio(sbio)->folio;
>>
>> if (sbio->bi_end_io != end_sync_read)
>> continue;
>> /* Now we can 'fixup' the error value */
>> sbio->bi_status = 0;
>>
>> - bio_for_each_segment_all(bi, sbio, iter_all)
>> - page_len[j++] = bi->bv_len;
>> -
>> - if (!status) {
>> - for (j = vcnt; j-- ; ) {
>> - if (memcmp(page_address(ppages[j]),
>> - page_address(spages[j]),
>> - page_len[j]))
>> - break;
>> - }
>> - } else
>> - j = 0;
>> - if (j >= 0)
>> + /*
>> + * Copy data and submit write in two cases:
>> + * - IO error (non-zero status)
>> + * - Data inconsistency and not a CHECK operation.
>> + */
>> + if (status) {
>> atomic64_add(r1_bio->sectors, &mddev->resync_mismatches);
>> - if (j < 0 || (test_bit(MD_RECOVERY_CHECK, &mddev->recovery)
>> - && !status)) {
>> - /* No need to write to this device. */
>> - sbio->bi_end_io = NULL;
>> - rdev_dec_pending(conf->mirrors[i].rdev, mddev);
>> + bio_copy_data(sbio, pbio);
>> continue;
>> + } else if (memcmp(folio_address(pfolio),
>> + folio_address(sfolio),
>> + r1_bio->sectors << 9)) {
>> + atomic64_add(r1_bio->sectors, &mddev->resync_mismatches);
>> + if (!test_bit(MD_RECOVERY_CHECK, &mddev->recovery)) {
>> + bio_copy_data(sbio, pbio);
>> + continue;
>> + }
>> }
>>
>> - bio_copy_data(sbio, pbio);
>> + /* No need to write to this device. */
>> + sbio->bi_end_io = NULL;
>> + rdev_dec_pending(conf->mirrors[i].rdev, mddev);
>> }
>> }
>>
>> @@ -2446,9 +2435,8 @@ static void fix_read_error(struct r1conf *conf, struct r1bio *r1_bio)
>> if (rdev &&
>> !test_bit(Faulty, &rdev->flags)) {
>> atomic_inc(&rdev->nr_pending);
>> - r1_sync_page_io(rdev, sect, s,
>> - folio_page(conf->tmpfolio, 0),
>> - REQ_OP_WRITE);
>> + r1_sync_folio_io(rdev, sect, s, 0,
>> + conf->tmpfolio, REQ_OP_WRITE);
>> rdev_dec_pending(rdev, mddev);
>> }
>> }
>> @@ -2461,9 +2449,8 @@ static void fix_read_error(struct r1conf *conf, struct r1bio *r1_bio)
>> if (rdev &&
>> !test_bit(Faulty, &rdev->flags)) {
>> atomic_inc(&rdev->nr_pending);
>> - if (r1_sync_page_io(rdev, sect, s,
>> - folio_page(conf->tmpfolio, 0),
>> - REQ_OP_READ)) {
>> + if (r1_sync_folio_io(rdev, sect, s, 0,
>> + conf->tmpfolio, REQ_OP_READ)) {
>> atomic_add(s, &rdev->corrected_errors);
>> pr_info("md/raid1:%s: read error corrected (%d sectors at %llu on %pg)\n",
>> mdname(mddev), s,
>> @@ -2738,15 +2725,15 @@ static int init_resync(struct r1conf *conf)
>> static struct r1bio *raid1_alloc_init_r1buf(struct r1conf *conf)
>> {
>> struct r1bio *r1bio = mempool_alloc(&conf->r1buf_pool, GFP_NOIO);
>> - struct resync_pages *rps;
>> + struct resync_folio *rfs;
>> struct bio *bio;
>> int i;
>>
>> for (i = conf->raid_disks * 2; i--; ) {
>> bio = r1bio->bios[i];
>> - rps = bio->bi_private;
>> + rfs = bio->bi_private;
>> bio_reset(bio, NULL, 0);
>> - bio->bi_private = rps;
>> + bio->bi_private = rfs;
>> }
>> r1bio->master_bio = NULL;
>> return r1bio;
>> @@ -2775,10 +2762,9 @@ static sector_t raid1_sync_request(struct mddev *mddev, sector_t sector_nr,
>> int write_targets = 0, read_targets = 0;
>> sector_t sync_blocks;
>> bool still_degraded = false;
>> - int good_sectors = RESYNC_SECTORS;
>> + int good_sectors;
>> int min_bad = 0; /* number of sectors that are bad in all devices */
>> int idx = sector_to_idx(sector_nr);
>> - int page_idx = 0;
>>
>> if (!mempool_initialized(&conf->r1buf_pool))
>> if (init_resync(conf))
>> @@ -2858,8 +2844,11 @@ static sector_t raid1_sync_request(struct mddev *mddev, sector_t sector_nr,
>> r1_bio->sector = sector_nr;
>> r1_bio->state = 0;
>> set_bit(R1BIO_IsSync, &r1_bio->state);
>> - /* make sure good_sectors won't go across barrier unit boundary */
>> - good_sectors = align_to_barrier_unit_end(sector_nr, good_sectors);
>> + /*
>> + * make sure good_sectors won't go across barrier unit boundary.
>> + * r1_bio->sectors <= RESYNC_SECTORS.
>> + */
>> + good_sectors = align_to_barrier_unit_end(sector_nr, r1_bio->sectors);
>>
>> for (i = 0; i < conf->raid_disks * 2; i++) {
>> struct md_rdev *rdev;
>> @@ -2979,44 +2968,28 @@ static sector_t raid1_sync_request(struct mddev *mddev, sector_t sector_nr,
>> max_sector = mddev->resync_max; /* Don't do IO beyond here */
>> if (max_sector > sector_nr + good_sectors)
>> max_sector = sector_nr + good_sectors;
>> - nr_sectors = 0;
>> - sync_blocks = 0;
>> do {
>> - struct page *page;
>> - int len = PAGE_SIZE;
>> - if (sector_nr + (len>>9) > max_sector)
>> - len = (max_sector - sector_nr) << 9;
>> - if (len == 0)
>> + nr_sectors = max_sector - sector_nr;
>> + if (nr_sectors == 0)
>> break;
>> - if (sync_blocks == 0) {
>> - if (!md_bitmap_start_sync(mddev, sector_nr,
>> - &sync_blocks, still_degraded) &&
>> - !conf->fullsync &&
>> - !test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery))
>> - break;
>> - if ((len >> 9) > sync_blocks)
>> - len = sync_blocks<<9;
>> - }
>> + if (!md_bitmap_start_sync(mddev, sector_nr,
>> + &sync_blocks, still_degraded) &&
>> + !conf->fullsync &&
>> + !test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery))
>> + break;
>> + if (nr_sectors > sync_blocks)
>> + nr_sectors = sync_blocks;
>>
>> for (i = 0 ; i < conf->raid_disks * 2; i++) {
>> - struct resync_pages *rp;
>> -
>> bio = r1_bio->bios[i];
>> - rp = get_resync_pages(bio);
>> if (bio->bi_end_io) {
>> - page = resync_fetch_page(rp, page_idx);
>> + struct resync_folio *rf = get_resync_folio(bio);
>>
>> - /*
>> - * won't fail because the vec table is big
>> - * enough to hold all these pages
>> - */
>> - __bio_add_page(bio, page, len, 0);
>> + bio_add_folio_nofail(bio, rf->folio, nr_sectors << 9, 0);
>> }
>> }
>> - nr_sectors += len>>9;
>> - sector_nr += len>>9;
>> - sync_blocks -= (len>>9);
>> - } while (++page_idx < RESYNC_PAGES);
>> + sector_nr += nr_sectors;
>> + } while (0);
>
> Now it can handle all pages in one go via a folio. It's strange to
> keep while(0) here.
>
I tried cleanning up while(0), it made 'if' and 'break' statements
unreadable. So I kept while(0) here.
>
>>
>> r1_bio->sectors = nr_sectors;
>
>
> This patch is a little big. Is it better to split this patch here?
>
It can't be splitted. The changes in raid1.c and raid10.c are entirely about
resync_pages -> resync_folio. We have to change declaration and its usage in
one patch.
>>
>> diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
>> index 26f93040cd13..3638e00fe420 100644
>> --- a/drivers/md/raid10.c
>> +++ b/drivers/md/raid10.c
>> @@ -96,11 +96,11 @@ static void end_reshape(struct r10conf *conf);
>>
>> /*
>> * for resync bio, r10bio pointer can be retrieved from the per-bio
>> - * 'struct resync_pages'.
>> + * 'struct resync_folio'.
>> */
>> static inline struct r10bio *get_resync_r10bio(struct bio *bio)
>> {
>> - return get_resync_pages(bio)->raid_bio;
>> + return get_resync_folio(bio)->raid_bio;
>> }
>>
>> static void * r10bio_pool_alloc(gfp_t gfp_flags, void *data)
>> @@ -133,8 +133,9 @@ static void * r10buf_pool_alloc(gfp_t gfp_flags, void *data)
>> struct r10bio *r10_bio;
>> struct bio *bio;
>> int j;
>> - int nalloc, nalloc_rp;
>> - struct resync_pages *rps;
>> + int nalloc, nalloc_rf;
>> + struct resync_folio *rfs;
>> + int order = get_order(RESYNC_BLOCK_SIZE);
>>
>> r10_bio = r10bio_pool_alloc(gfp_flags, conf);
>> if (!r10_bio)
>> @@ -148,66 +149,64 @@ static void * r10buf_pool_alloc(gfp_t gfp_flags, void *data)
>>
>> /* allocate once for all bios */
>> if (!conf->have_replacement)
>> - nalloc_rp = nalloc;
>> + nalloc_rf = nalloc;
>> else
>> - nalloc_rp = nalloc * 2;
>> - rps = kmalloc_array(nalloc_rp, sizeof(struct resync_pages), gfp_flags);
>> - if (!rps)
>> + nalloc_rf = nalloc * 2;
>> + rfs = kmalloc_array(nalloc_rf, sizeof(struct resync_folio), gfp_flags);
>> + if (!rfs)
>> goto out_free_r10bio;
>>
>> /*
>> * Allocate bios.
>> */
>> for (j = nalloc ; j-- ; ) {
>> - bio = bio_kmalloc(RESYNC_PAGES, gfp_flags);
>> + bio = bio_kmalloc(1, gfp_flags);
>> if (!bio)
>> goto out_free_bio;
>> - bio_init_inline(bio, NULL, RESYNC_PAGES, 0);
>> + bio_init_inline(bio, NULL, 1, 0);
>> r10_bio->devs[j].bio = bio;
>> if (!conf->have_replacement)
>> continue;
>> - bio = bio_kmalloc(RESYNC_PAGES, gfp_flags);
>> + bio = bio_kmalloc(1, gfp_flags);
>> if (!bio)
>> goto out_free_bio;
>> - bio_init_inline(bio, NULL, RESYNC_PAGES, 0);
>> + bio_init_inline(bio, NULL, 1, 0);
>> r10_bio->devs[j].repl_bio = bio;
>> }
>> /*
>> - * Allocate RESYNC_PAGES data pages and attach them
>> - * where needed.
>> + * Allocate data folio and attach it where needed.
>> */
>> for (j = 0; j < nalloc; j++) {
>> struct bio *rbio = r10_bio->devs[j].repl_bio;
>> - struct resync_pages *rp, *rp_repl;
>> + struct resync_folio *rf, *rf_repl;
>>
>> - rp = &rps[j];
>> + rf = &rfs[j];
>> if (rbio)
>> - rp_repl = &rps[nalloc + j];
>> -
>> - bio = r10_bio->devs[j].bio;
>> + rf_repl = &rfs[nalloc + j];
>>
>> if (!j || test_bit(MD_RECOVERY_SYNC,
>> &conf->mddev->recovery)) {
>> - if (resync_alloc_pages(rp, gfp_flags))
>> - goto out_free_pages;
>> + if (resync_alloc_folio(rf, gfp_flags, &order))
>> + goto out_free_folio;
>> } else {
>> - memcpy(rp, &rps[0], sizeof(*rp));
>> - resync_get_all_pages(rp);
>> + memcpy(rf, &rfs[0], sizeof(*rf));
>> + folio_get(rf->folio);
>> }
>>
>> - rp->raid_bio = r10_bio;
>> - bio->bi_private = rp;
>> + rf->raid_bio = r10_bio;
>> + r10_bio->devs[j].bio->bi_private = rf;
>> if (rbio) {
>> - memcpy(rp_repl, rp, sizeof(*rp));
>> - rbio->bi_private = rp_repl;
>> + memcpy(rf_repl, rf, sizeof(*rf));
>> + rbio->bi_private = rf_repl;
>> }
>> }
>>
>> + r10_bio->sectors = 1 << (order + PAGE_SECTORS_SHIFT);
>> return r10_bio;
>>
>> -out_free_pages:
>> +out_free_folio:
>> while (--j >= 0)
>> - resync_free_pages(&rps[j]);
>> + folio_put(rfs[j].folio);
>>
>> j = 0;
>> out_free_bio:
>> @@ -219,7 +218,7 @@ static void * r10buf_pool_alloc(gfp_t gfp_flags, void *data)
>> bio_uninit(r10_bio->devs[j].repl_bio);
>> kfree(r10_bio->devs[j].repl_bio);
>> }
>> - kfree(rps);
>> + kfree(rfs);
>> out_free_r10bio:
>> rbio_pool_free(r10_bio, conf);
>> return NULL;
>> @@ -230,14 +229,14 @@ static void r10buf_pool_free(void *__r10_bio, void *data)
>> struct r10conf *conf = data;
>> struct r10bio *r10bio = __r10_bio;
>> int j;
>> - struct resync_pages *rp = NULL;
>> + struct resync_folio *rf = NULL;
>>
>> for (j = conf->copies; j--; ) {
>> struct bio *bio = r10bio->devs[j].bio;
>>
>> if (bio) {
>> - rp = get_resync_pages(bio);
>> - resync_free_pages(rp);
>> + rf = get_resync_folio(bio);
>> + folio_put(rf->folio);
>> bio_uninit(bio);
>> kfree(bio);
>> }
>> @@ -250,7 +249,7 @@ static void r10buf_pool_free(void *__r10_bio, void *data)
>> }
>>
>> /* resync pages array stored in the 1st bio's .bi_private */
>> - kfree(rp);
>> + kfree(rf);
>>
>> rbio_pool_free(r10bio, conf);
>> }
>> @@ -2342,8 +2341,7 @@ static void sync_request_write(struct mddev *mddev, struct r10bio *r10_bio)
>> struct r10conf *conf = mddev->private;
>> int i, first;
>> struct bio *tbio, *fbio;
>> - int vcnt;
>> - struct page **tpages, **fpages;
>> + struct folio *tfolio, *ffolio;
>>
>> atomic_set(&r10_bio->remaining, 1);
>>
>> @@ -2359,14 +2357,13 @@ static void sync_request_write(struct mddev *mddev, struct r10bio *r10_bio)
>> fbio = r10_bio->devs[i].bio;
>> fbio->bi_iter.bi_size = r10_bio->sectors << 9;
>> fbio->bi_iter.bi_idx = 0;
>> - fpages = get_resync_pages(fbio)->pages;
>> + ffolio = get_resync_folio(fbio)->folio;
>>
>> - vcnt = (r10_bio->sectors + (PAGE_SIZE >> 9) - 1) >> (PAGE_SHIFT - 9);
>> /* now find blocks with errors */
>> for (i=0 ; i < conf->copies ; i++) {
>> - int j, d;
>> + int d;
>> struct md_rdev *rdev;
>> - struct resync_pages *rp;
>> + struct resync_folio *rf;
>>
>> tbio = r10_bio->devs[i].bio;
>>
>> @@ -2375,31 +2372,23 @@ static void sync_request_write(struct mddev *mddev, struct r10bio *r10_bio)
>> if (i == first)
>> continue;
>>
>> - tpages = get_resync_pages(tbio)->pages;
>> + tfolio = get_resync_folio(tbio)->folio;
>> d = r10_bio->devs[i].devnum;
>> rdev = conf->mirrors[d].rdev;
>> if (!r10_bio->devs[i].bio->bi_status) {
>> /* We know that the bi_io_vec layout is the same for
>> * both 'first' and 'i', so we just compare them.
>> - * All vec entries are PAGE_SIZE;
>> */
>> - int sectors = r10_bio->sectors;
>> - for (j = 0; j < vcnt; j++) {
>> - int len = PAGE_SIZE;
>> - if (sectors < (len / 512))
>> - len = sectors * 512;
>> - if (memcmp(page_address(fpages[j]),
>> - page_address(tpages[j]),
>> - len))
>> - break;
>> - sectors -= len/512;
>> + if (memcmp(folio_address(ffolio),
>> + folio_address(tfolio),
>> + r10_bio->sectors << 9)) {
>> + atomic64_add(r10_bio->sectors,
>> + &mddev->resync_mismatches);
>> + if (test_bit(MD_RECOVERY_CHECK,
>> + &mddev->recovery))
>> + /* Don't fix anything. */
>> + continue;
>> }
>> - if (j == vcnt)
>> - continue;
>> - atomic64_add(r10_bio->sectors, &mddev->resync_mismatches);
>> - if (test_bit(MD_RECOVERY_CHECK, &mddev->recovery))
>> - /* Don't fix anything. */
>> - continue;
>> } else if (test_bit(FailFast, &rdev->flags)) {
>> /* Just give up on this device */
>> md_error(rdev->mddev, rdev);
>> @@ -2410,13 +2399,13 @@ static void sync_request_write(struct mddev *mddev, struct r10bio *r10_bio)
>> * First we need to fixup bv_offset, bv_len and
>> * bi_vecs, as the read request might have corrupted these
>> */
>> - rp = get_resync_pages(tbio);
>> + rf = get_resync_folio(tbio);
>> bio_reset(tbio, conf->mirrors[d].rdev->bdev, REQ_OP_WRITE);
>>
>> - md_bio_reset_resync_pages(tbio, rp, fbio->bi_iter.bi_size);
>> + md_bio_reset_resync_folio(tbio, rf, fbio->bi_iter.bi_size);
>>
>> - rp->raid_bio = r10_bio;
>> - tbio->bi_private = rp;
>> + rf->raid_bio = r10_bio;
>> + tbio->bi_private = rf;
>> tbio->bi_iter.bi_sector = r10_bio->devs[i].addr;
>> tbio->bi_end_io = end_sync_write;
>>
>> @@ -2476,10 +2465,9 @@ static void fix_recovery_read_error(struct r10bio *r10_bio)
>> struct bio *bio = r10_bio->devs[0].bio;
>> sector_t sect = 0;
>> int sectors = r10_bio->sectors;
>> - int idx = 0;
>> int dr = r10_bio->devs[0].devnum;
>> int dw = r10_bio->devs[1].devnum;
>> - struct page **pages = get_resync_pages(bio)->pages;
>> + struct folio *folio = get_resync_folio(bio)->folio;
>>
>> while (sectors) {
>> int s = sectors;
>> @@ -2492,19 +2480,21 @@ static void fix_recovery_read_error(struct r10bio *r10_bio)
>>
>> rdev = conf->mirrors[dr].rdev;
>> addr = r10_bio->devs[0].addr + sect;
>> - ok = sync_page_io(rdev,
>> - addr,
>> - s << 9,
>> - pages[idx],
>> - REQ_OP_READ, false);
>> + ok = sync_folio_io(rdev,
>> + addr,
>> + s << 9,
>> + sect << 9,
>> + folio,
>> + REQ_OP_READ, false);
>> if (ok) {
>> rdev = conf->mirrors[dw].rdev;
>> addr = r10_bio->devs[1].addr + sect;
>> - ok = sync_page_io(rdev,
>> - addr,
>> - s << 9,
>> - pages[idx],
>> - REQ_OP_WRITE, false);
>> + ok = sync_folio_io(rdev,
>> + addr,
>> + s << 9,
>> + sect << 9,
>> + folio,
>> + REQ_OP_WRITE, false);
>> if (!ok) {
>> set_bit(WriteErrorSeen, &rdev->flags);
>> if (!test_and_set_bit(WantReplacement,
>> @@ -2539,7 +2529,6 @@ static void fix_recovery_read_error(struct r10bio *r10_bio)
>>
>> sectors -= s;
>> sect += s;
>> - idx++;
>> }
>> }
>>
>> @@ -3050,7 +3039,7 @@ static int init_resync(struct r10conf *conf)
>> static struct r10bio *raid10_alloc_init_r10buf(struct r10conf *conf)
>> {
>> struct r10bio *r10bio = mempool_alloc(&conf->r10buf_pool, GFP_NOIO);
>> - struct rsync_pages *rp;
>> + struct resync_folio *rf;
>> struct bio *bio;
>> int nalloc;
>> int i;
>> @@ -3063,14 +3052,14 @@ static struct r10bio *raid10_alloc_init_r10buf(struct r10conf *conf)
>>
>> for (i = 0; i < nalloc; i++) {
>> bio = r10bio->devs[i].bio;
>> - rp = bio->bi_private;
>> + rf = bio->bi_private;
>> bio_reset(bio, NULL, 0);
>> - bio->bi_private = rp;
>> + bio->bi_private = rf;
>> bio = r10bio->devs[i].repl_bio;
>> if (bio) {
>> - rp = bio->bi_private;
>> + rf = bio->bi_private;
>> bio_reset(bio, NULL, 0);
>> - bio->bi_private = rp;
>> + bio->bi_private = rf;
>> }
>> }
>> return r10bio;
>> @@ -3156,7 +3145,6 @@ static sector_t raid10_sync_request(struct mddev *mddev, sector_t sector_nr,
>> int max_sync = RESYNC_SECTORS;
>> sector_t sync_blocks;
>> sector_t chunk_mask = conf->geo.chunk_mask;
>> - int page_idx = 0;
>>
>> /*
>> * Allow skipping a full rebuild for incremental assembly
>> @@ -3376,6 +3364,15 @@ static sector_t raid10_sync_request(struct mddev *mddev, sector_t sector_nr,
>> continue;
>> }
>> }
>> +
>> + /*
>> + * RESYNC_BLOCK_SIZE folio might alloc failed in
>> + * resync_alloc_folio(). Fall back to smaller sync
>> + * size if needed.
>> + */
>> + if (max_sync > r10_bio->sectors)
>> + max_sync = r10_bio->sectors;
>> +
>> any_working = 1;
>> bio = r10_bio->devs[0].bio;
>> bio->bi_next = biolist;
>> @@ -3527,7 +3524,15 @@ static sector_t raid10_sync_request(struct mddev *mddev, sector_t sector_nr,
>> }
>> if (sync_blocks < max_sync)
>> max_sync = sync_blocks;
>> +
>> r10_bio = raid10_alloc_init_r10buf(conf);
>> + /*
>> + * RESYNC_BLOCK_SIZE folio might alloc failed in resync_alloc_folio().
>> + * Fall back to smaller sync size if needed.
>> + */
>> + if (max_sync > r10_bio->sectors)
>> + max_sync = r10_bio->sectors;
>> +
>> r10_bio->state = 0;
>>
>> r10_bio->mddev = mddev;
>> @@ -3620,29 +3625,25 @@ static sector_t raid10_sync_request(struct mddev *mddev, sector_t sector_nr,
>> }
>> }
>>
>> - nr_sectors = 0;
>> if (sector_nr + max_sync < max_sector)
>> max_sector = sector_nr + max_sync;
>> do {
>> - struct page *page;
>> - int len = PAGE_SIZE;
>> - if (sector_nr + (len>>9) > max_sector)
>> - len = (max_sector - sector_nr) << 9;
>> - if (len == 0)
>> + nr_sectors = max_sector - sector_nr;
>> +
>> + if (nr_sectors == 0)
>> break;
>> for (bio= biolist ; bio ; bio=bio->bi_next) {
>> - struct resync_pages *rp = get_resync_pages(bio);
>> - page = resync_fetch_page(rp, page_idx);
>> - if (WARN_ON(!bio_add_page(bio, page, len, 0))) {
>> + struct resync_folio *rf = get_resync_folio(bio);
>> +
>> + if (WARN_ON(!bio_add_folio(bio, rf->folio, nr_sectors << 9, 0))) {
>> bio->bi_status = BLK_STS_RESOURCE;
>> bio_endio(bio);
>> *skipped = 1;
>> - return max_sync;
>> + return nr_sectors << 9;
>> }
>> }
>> - nr_sectors += len>>9;
>> - sector_nr += len>>9;
>> - } while (++page_idx < RESYNC_PAGES);
>> + sector_nr += nr_sectors;
>> + } while (0);
>> r10_bio->sectors = nr_sectors;
>>
>> if (mddev_is_clustered(mddev) &&
>> @@ -4560,7 +4561,7 @@ static sector_t reshape_request(struct mddev *mddev, sector_t sector_nr,
>> int *skipped)
>> {
>> /* We simply copy at most one chunk (smallest of old and new)
>> - * at a time, possibly less if that exceeds RESYNC_PAGES,
>> + * at a time, possibly less if that exceeds RESYNC_BLOCK_SIZE,
>> * or we hit a bad block or something.
>> * This might mean we pause for normal IO in the middle of
>> * a chunk, but that is not a problem as mddev->reshape_position
>> @@ -4600,14 +4601,13 @@ static sector_t reshape_request(struct mddev *mddev, sector_t sector_nr,
>> struct r10bio *r10_bio;
>> sector_t next, safe, last;
>> int max_sectors;
>> - int nr_sectors;
>> int s;
>> struct md_rdev *rdev;
>> int need_flush = 0;
>> struct bio *blist;
>> struct bio *bio, *read_bio;
>> int sectors_done = 0;
>> - struct page **pages;
>> + struct folio *folio;
>>
>> if (sector_nr == 0) {
>> /* If restarting in the middle, skip the initial sectors */
>> @@ -4709,7 +4709,12 @@ static sector_t reshape_request(struct mddev *mddev, sector_t sector_nr,
>> r10_bio->mddev = mddev;
>> r10_bio->sector = sector_nr;
>> set_bit(R10BIO_IsReshape, &r10_bio->state);
>> - r10_bio->sectors = last - sector_nr + 1;
>> + /*
>> + * RESYNC_BLOCK_SIZE folio might alloc failed in
>> + * resync_alloc_folio(). Fall back to smaller sync
>> + * size if needed.
>> + */
>> + r10_bio->sectors = min_t(int, r10_bio->sectors, last - sector_nr + 1);
>> rdev = read_balance(conf, r10_bio, &max_sectors);
>> BUG_ON(!test_bit(R10BIO_Previous, &r10_bio->state));
>>
>> @@ -4723,7 +4728,7 @@ static sector_t reshape_request(struct mddev *mddev, sector_t sector_nr,
>> return sectors_done;
>> }
>>
>> - read_bio = bio_alloc_bioset(rdev->bdev, RESYNC_PAGES, REQ_OP_READ,
>> + read_bio = bio_alloc_bioset(rdev->bdev, 1, REQ_OP_READ,
>> GFP_KERNEL, &mddev->bio_set);
>> read_bio->bi_iter.bi_sector = (r10_bio->devs[r10_bio->read_slot].addr
>> + rdev->data_offset);
>> @@ -4787,32 +4792,23 @@ static sector_t reshape_request(struct mddev *mddev, sector_t sector_nr,
>> blist = b;
>> }
>>
>> - /* Now add as many pages as possible to all of these bios. */
>> + /* Now add folio to all of these bios. */
>>
>> - nr_sectors = 0;
>> - pages = get_resync_pages(r10_bio->devs[0].bio)->pages;
>> - for (s = 0 ; s < max_sectors; s += PAGE_SIZE >> 9) {
>> - struct page *page = pages[s / (PAGE_SIZE >> 9)];
>> - int len = (max_sectors - s) << 9;
>> - if (len > PAGE_SIZE)
>> - len = PAGE_SIZE;
>> - for (bio = blist; bio ; bio = bio->bi_next) {
>> - if (WARN_ON(!bio_add_page(bio, page, len, 0))) {
>> - bio->bi_status = BLK_STS_RESOURCE;
>> - bio_endio(bio);
>> - return sectors_done;
>> - }
>> + folio = get_resync_folio(r10_bio->devs[0].bio)->folio;
>> + for (bio = blist; bio ; bio = bio->bi_next) {
>> + if (WARN_ON(!bio_add_folio(bio, folio, max_sectors, 0))) {
>> + bio->bi_status = BLK_STS_RESOURCE;
>> + bio_endio(bio);
>> + return sectors_done;
>
> In fact, the original codes don't clean up before returning.
> bio_add_folio_nofail is used in raid1 and can we use
> bio_add_folio_nofail here as well?
>
Agree, I will clean it up before this patch.
>> }
>> - sector_nr += len >> 9;
>> - nr_sectors += len >> 9;
>> }
>> - r10_bio->sectors = nr_sectors;
>> + r10_bio->sectors = max_sectors >> 9;
>>
>> /* Now submit the read */
>> atomic_inc(&r10_bio->remaining);
>> read_bio->bi_next = NULL;
>> submit_bio_noacct(read_bio);
>> - sectors_done += nr_sectors;
>> + sectors_done += max_sectors;
>> if (sector_nr <= last)
>> goto read_more;
>>
>> @@ -4914,8 +4910,8 @@ static int handle_reshape_read_error(struct mddev *mddev,
>> struct r10conf *conf = mddev->private;
>> struct r10bio *r10b;
>> int slot = 0;
>> - int idx = 0;
>> - struct page **pages;
>> + int sect = 0;
>> + struct folio *folio;
>>
>> r10b = kmalloc(struct_size(r10b, devs, conf->copies), GFP_NOIO);
>> if (!r10b) {
>> @@ -4923,8 +4919,8 @@ static int handle_reshape_read_error(struct mddev *mddev,
>> return -ENOMEM;
>> }
>>
>> - /* reshape IOs share pages from .devs[0].bio */
>> - pages = get_resync_pages(r10_bio->devs[0].bio)->pages;
>> + /* reshape IOs share folio from .devs[0].bio */
>> + folio = get_resync_folio(r10_bio->devs[0].bio)->folio;
>>
>> r10b->sector = r10_bio->sector;
>> __raid10_find_phys(&conf->prev, r10b);
>> @@ -4940,19 +4936,19 @@ static int handle_reshape_read_error(struct mddev *mddev,
>> while (!success) {
>> int d = r10b->devs[slot].devnum;
>> struct md_rdev *rdev = conf->mirrors[d].rdev;
>> - sector_t addr;
>> if (rdev == NULL ||
>> test_bit(Faulty, &rdev->flags) ||
>> !test_bit(In_sync, &rdev->flags))
>> goto failed;
>>
>> - addr = r10b->devs[slot].addr + idx * PAGE_SIZE;
>> atomic_inc(&rdev->nr_pending);
>> - success = sync_page_io(rdev,
>> - addr,
>> - s << 9,
>> - pages[idx],
>> - REQ_OP_READ, false);
>> + success = sync_folio_io(rdev,
>> + r10b->devs[slot].addr +
>> + sect,
>> + s << 9,
>> + sect << 9,
>> + folio,
>> + REQ_OP_READ, false);
>> rdev_dec_pending(rdev, mddev);
>> if (success)
>> break;
>> @@ -4971,7 +4967,7 @@ static int handle_reshape_read_error(struct mddev *mddev,
>> return -EIO;
>> }
>> sectors -= s;
>> - idx++;
>> + sect += s;
>> }
>> kfree(r10b);
>> return 0;
>> --
>> 2.39.2
>>
>>
>
> Regards
> Xiao
--
Thansk
Nan
^ permalink raw reply
* [PATCH AUTOSEL 7.0-6.18] md/raid5: Fix UAF on IO across the reshape position
From: Sasha Levin @ 2026-05-05 9:51 UTC (permalink / raw)
To: patches, stable
Cc: Benjamin Marzinski, Xiao Ni, Yu Kuai, Sasha Levin, song,
linux-raid, linux-kernel
In-Reply-To: <20260505095149.512052-1-sashal@kernel.org>
From: Benjamin Marzinski <bmarzins@redhat.com>
[ Upstream commit 418b3e64e4459feb3f75979de9ec89e085745343 ]
If make_stripe_request() returns STRIPE_WAIT_RESHAPE,
raid5_make_request() will free the cloned bio. But raid5_make_request()
can call make_stripe_request() multiple times, writing to the various
stripes. If that bio got added to the toread or towrite lists of a
stripe disk in an earlier call to make_stripe_request(), then it's not
safe to just free the bio if a later part of it is found to cross the
reshape position. Doing so can lead to a UAF error, when bio_endio()
is called on the bio for the earlier stripes.
Instead, raid5_make_request() needs to wait until all parts of the bio
have called bio_endio(). To do this, bios that cross the reshape
position while the reshape can't make progress are flagged as needing to
wait for all parts to complete. When raid5_make_request() has a bio that
failed make_stripe_request() with STRIPE_WAIT_RESHAPE, it sets
bi->bi_private to a completion struct and waits for completion after
ending the bio. When the bio_endio() is called for the last time on a
clone bio with bi->bi_private set, it wakes up the waiter. This
guarantees that raid5_make_request() doesn't return until the cloned bio
needing a retry for io across the reshape boundary is safely cleaned up.
There is a simple reproducer available at [1]. Compile the kernel with
KASAN for more useful reporting when the error is triggered (this is not
necessary to see the bug).
[1] https://gist.github.com/bmarzins/e48598824305cf2171289e47d7241fa5
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
Reviewed-by: Xiao Ni <xni@redhat.com>
Link: https://lore.kernel.org/r/20260408043548.1695157-1-bmarzins@redhat.com
Signed-off-by: Yu Kuai <yukuai@fnnas.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
LLM Generated explanations, may be completely bogus:
## Phase Walkthrough
1. Commit message forensics
Record: subsystem `md/raid5`; action verb `Fix`; intent is to prevent a
use-after-free when RAID5 IO crosses a reshape position and
`make_stripe_request()` returns `STRIPE_WAIT_RESHAPE`. Tags found:
`Signed-off-by: Benjamin Marzinski`, `Reviewed-by: Xiao Ni`, lore
`Link`, `Signed-off-by: Yu Kuai`; no `Fixes:`, `Reported-by:`, `Tested-
by`, or `Cc: stable`. Body describes a real UAF, a KASAN-aided
reproducer, and the root cause: a cloned bio can already be linked into
earlier stripe `toread`/`towrite` lists when a later stripe path frees
it.
2. Diff analysis
Record: 3 files, `14 insertions/25 deletions`; functions changed are
`md_end_clone_io()`, `md_clone_bio()`, removed `md_free_cloned_bio()`,
and `raid5_make_request()`. Before: `STRIPE_WAIT_RESHAPE` directly
called `md_free_cloned_bio(bi)`. After: it sets `bi_private` to a stack
completion, calls `bio_endio(bi)`, and waits until the cloned bio’s
final endio completes. Bug category: memory safety/UAF, caused by
freeing a clone still referenced by stripe bio chains. Fix is small and
contained.
3. Git history investigation
Record: upstream commit is `418b3e64e4459`. Blame shows the problematic
`STRIPE_WAIT_RESHAPE`/`md_free_cloned_bio()` path came from
`41425f96d7aa` (`dm-raid456, md/raid456: fix a deadlock...`), first
contained in `v6.9-rc1`/`v6.9`. That introducing commit was itself
stable-marked for `v6.7+` and is present in checked stable branches
`6.12.y`, `6.18.y`, `6.19.y`, and `7.0.y`; the specific buggy
helper/path was not found in `6.6.y` or `6.1.y`. No `Fixes:` tag exists,
so blame was used instead.
4. Mailing list and external research
Record: `b4 dig -c 418b3e64e4459` found `[PATCH v2]` at
`https://patch.msgid.link/20260408043548.1695157-1-bmarzins@redhat.com`.
`b4 dig -a` found v1 RFC and v2; committed patch matches v2. `b4 dig -w`
shows `linux-raid`, `dm-devel`, Song Liu, Yu Kuai, Li Nan, Xiao Ni, and
Red Hat participants were included. Thread review: Xiao Ni gave
`Reviewed-by`; Yu Kuai replied “Applied.” Reviewer asked about
`WRITE_ONCE`; author explained it was unnecessary but harmless on the
slow path, and Xiao accepted keeping it. No NAKs found. WebFetch for
lore was blocked by Anubis, but b4 retrieved the mbox. The gist
reproducer uses dmsetup/LVM RAID5 reshape loops.
5. Code semantic analysis
Record: `md_submit_bio()` reaches `md_handle_request()`, which calls the
RAID personality `.make_request = raid5_make_request` for RAID4/5/6.
`raid5_make_request()` calls `make_stripe_request()` repeatedly over
stripe bits. `add_all_stripe_bios()` calls `__add_stripe_bio()`, which
links the cloned bio into `toread`/`towrite` and calls
`bio_inc_remaining()`. `bio_endio()` only invokes `bi_end_io` after
`__bi_remaining` reaches zero, so the completion wait correctly waits
for all earlier stripe references to drain.
6. Stable tree analysis
Record: buggy code exists in `stable/linux-7.0.y`, `6.19.y`, `6.18.y`,
and `6.12.y`; not in checked `6.6.y`/`6.1.y`. Patch applies cleanly to
current `7.0.y` with `git apply --check`. Direct upstream patch does not
apply cleanly to `6.19.y` and `6.12.y` because nearby context differs
(`ctx` allocation/field access and older bitmap helpers), but the same
core code is present, so a small backport adjustment is needed.
7. Subsystem context
Record: subsystem is software RAID / MD, `drivers/md`; MAINTAINERS lists
Song Liu and Yu Kuai as maintainers and `linux-raid@vger.kernel.org` as
the list. Criticality: important storage subsystem, with data
availability and kernel memory-safety impact during RAID reshape.
Activity level is high; recent logs show multiple md/raid fixes and
refactors.
8. Impact and risk assessment
Record: affected users are md/dm RAID4/5/6 users doing IO while reshape
is blocked/interrupted. Trigger is specialized but real; reproducer
exists. Admin privileges are generally needed to create/reshape the
array, but ordinary IO can hit the path once the system is in that
state. Failure mode is UAF after freeing a cloned bio still referenced
by stripe lists; severity HIGH/CRITICAL because it can crash or corrupt
memory. Benefit is high for affected stable users; risk is low to
medium: small fix, no API change, but it touches bio completion
semantics in a storage path.
9. Final synthesis
Record: Evidence for backporting: explicit UAF, reproducer, KASAN
usefulness, small contained patch, reviewed by linux-raid reviewer,
applied by md maintainer, buggy code present in active stable trees from
`6.12.y` upward. Evidence against: not applicable to older
`6.6.y`/`6.1.y`; older applicable trees need a manual backport rather
than direct cherry-pick. Stable rules: obviously correct by code
inspection and review; fixes a real serious bug; small and contained; no
new features/APIs; applies cleanly to `7.0.y`, needs minor backport for
older affected branches. Exception categories: none needed.
## Verification
- Phase 1: `git show --format=fuller --stat 418b3e64e4459` verified
subject, body, tags, reviewer, lore link, and line counts.
- Phase 2: `git show --no-ext-diff 418b3e64e4459` verified exact changes
to `drivers/md/md.c`, `drivers/md/md.h`, and `drivers/md/raid5.c`.
- Phase 3: `git blame` verified `41425f96d7aa` introduced
`STRIPE_WAIT_RESHAPE` freeing via `md_free_cloned_bio()`.
- Phase 3: `git describe --contains 41425f96d7aa` and tag checks
verified first mainline containment around `v6.9`.
- Phase 4: `b4 dig -c/-a/-w/-m 418b3e64e4459` verified v1/v2 series,
recipients, review, and applied response.
- Phase 4: WebFetch verified the raw gist reproducer content; lore
WebFetch was blocked by Anubis.
- Phase 5: `rg`/file reads verified call chain, stripe list insertion,
`bio_inc_remaining()`, and `bio_endio()` final-callback semantics.
- Phase 6: `git grep` verified buggy code in `6.12.y`, `6.18.y`,
`6.19.y`, and `7.0.y`, absent from checked `6.6.y` and `6.1.y`.
- Phase 6: `git apply --check` verified clean apply to `7.0.y`; worktree
checks showed direct patch conflicts on `6.19.y` and `6.12.y`.
- Unverified: I did not run the reproducer locally or test a built
kernel.
This is stable material for affected trees, especially `6.12.y+`, with
backport adjustment where context differs.
**YES**
drivers/md/md.c | 31 ++++++++-----------------------
drivers/md/md.h | 1 -
drivers/md/raid5.c | 7 ++++++-
3 files changed, 14 insertions(+), 25 deletions(-)
diff --git a/drivers/md/md.c b/drivers/md/md.c
index 3ce6f9e9d38e6..4318d875a5f63 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -9215,9 +9215,11 @@ static void md_bitmap_end(struct mddev *mddev, struct md_io_clone *md_io_clone)
static void md_end_clone_io(struct bio *bio)
{
- struct md_io_clone *md_io_clone = bio->bi_private;
+ struct md_io_clone *md_io_clone = container_of(bio, struct md_io_clone,
+ bio_clone);
struct bio *orig_bio = md_io_clone->orig_bio;
struct mddev *mddev = md_io_clone->mddev;
+ struct completion *reshape_completion = bio->bi_private;
if (bio_data_dir(orig_bio) == WRITE && md_bitmap_enabled(mddev, false))
md_bitmap_end(mddev, md_io_clone);
@@ -9229,7 +9231,10 @@ static void md_end_clone_io(struct bio *bio)
bio_end_io_acct(orig_bio, md_io_clone->start_time);
bio_put(bio);
- bio_endio(orig_bio);
+ if (unlikely(reshape_completion))
+ complete(reshape_completion);
+ else
+ bio_endio(orig_bio);
percpu_ref_put(&mddev->active_io);
}
@@ -9254,7 +9259,7 @@ static void md_clone_bio(struct mddev *mddev, struct bio **bio)
}
clone->bi_end_io = md_end_clone_io;
- clone->bi_private = md_io_clone;
+ clone->bi_private = NULL;
*bio = clone;
}
@@ -9265,26 +9270,6 @@ void md_account_bio(struct mddev *mddev, struct bio **bio)
}
EXPORT_SYMBOL_GPL(md_account_bio);
-void md_free_cloned_bio(struct bio *bio)
-{
- struct md_io_clone *md_io_clone = bio->bi_private;
- struct bio *orig_bio = md_io_clone->orig_bio;
- struct mddev *mddev = md_io_clone->mddev;
-
- if (bio_data_dir(orig_bio) == WRITE && md_bitmap_enabled(mddev, false))
- md_bitmap_end(mddev, md_io_clone);
-
- if (bio->bi_status && !orig_bio->bi_status)
- orig_bio->bi_status = bio->bi_status;
-
- if (md_io_clone->start_time)
- bio_end_io_acct(orig_bio, md_io_clone->start_time);
-
- bio_put(bio);
- percpu_ref_put(&mddev->active_io);
-}
-EXPORT_SYMBOL_GPL(md_free_cloned_bio);
-
/* md_allow_write(mddev)
* Calling this ensures that the array is marked 'active' so that writes
* may proceed without blocking. It is important to call this before
diff --git a/drivers/md/md.h b/drivers/md/md.h
index ac84289664cd7..5d57fee22901f 100644
--- a/drivers/md/md.h
+++ b/drivers/md/md.h
@@ -917,7 +917,6 @@ extern void md_finish_reshape(struct mddev *mddev);
void md_submit_discard_bio(struct mddev *mddev, struct md_rdev *rdev,
struct bio *bio, sector_t start, sector_t size);
void md_account_bio(struct mddev *mddev, struct bio **bio);
-void md_free_cloned_bio(struct bio *bio);
extern bool __must_check md_flush_request(struct mddev *mddev, struct bio *bio);
void md_write_metadata(struct mddev *mddev, struct md_rdev *rdev,
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index a8e8d431071ba..dc0c680ca199b 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -6217,7 +6217,12 @@ static bool raid5_make_request(struct mddev *mddev, struct bio * bi)
mempool_free(ctx, conf->ctx_pool);
if (res == STRIPE_WAIT_RESHAPE) {
- md_free_cloned_bio(bi);
+ DECLARE_COMPLETION_ONSTACK(done);
+ WRITE_ONCE(bi->bi_private, &done);
+
+ bio_endio(bi);
+
+ wait_for_completion(&done);
return false;
}
--
2.53.0
^ permalink raw reply related
* Re: [PATCH V3 0/2] md/nvme: Enable PCI P2PDMA support for RAID0 and NVMe Multipath
From: Chaitanya Kulkarni @ 2026-05-05 0:32 UTC (permalink / raw)
To: Pranjal Shrivastava, Chaitanya Kulkarni
Cc: song@kernel.org, yukuai@fnnas.com, linan122@huawei.com,
kbusch@kernel.org, axboe@kernel.dk, hch@lst.de, sagi@grimberg.me,
linux-raid@vger.kernel.org, linux-nvme@lists.infradead.org,
Kiran Modukuri
In-Reply-To: <afkO1WExGV9-jv8b@google.com>
Pranjal,
On 5/4/26 2:25 PM, Pranjal Shrivastava wrote:
>> Chaitanya Kulkarni (1):
>> block: clear BLK_FEAT_PCI_P2PDMA in blk_stack_limits() for
>> non-supporting devices
>>
>> Kiran Kumar Modukuri (2):
>> md: propagate BLK_FEAT_PCI_P2PDMA from member devices to RAID device
>> nvme-multipath: enable PCI P2PDMA for multipath devices
>>
>> block/blk-settings.c | 2 ++
>> drivers/md/raid0.c | 1 +
>> drivers/md/raid1.c | 1 +
>> drivers/md/raid10.c | 1 +
>> drivers/nvme/host/multipath.c | 2 +-
>> 5 files changed, 6 insertions(+), 1 deletion(-)
>>
> Tested with NVMe-oF (RDMA).
>
> Tested=by: Pranjal Shrivastava<praan@google.com>
>
> Thanks,
> Praan
Thanks a lot for testing.
-ck
^ permalink raw reply
* Re: [PATCH V3 0/2] md/nvme: Enable PCI P2PDMA support for RAID0 and NVMe Multipath
From: Pranjal Shrivastava @ 2026-05-04 21:25 UTC (permalink / raw)
To: Chaitanya Kulkarni
Cc: song, yukuai, linan122, kbusch, axboe, hch, sagi, linux-raid,
linux-nvme, kmodukuri
In-Reply-To: <20260416212633.72650-1-kch@nvidia.com>
On Thu, Apr 16, 2026 at 02:26:30PM -0700, Chaitanya Kulkarni wrote:
> Hi,
>
> This patch series extends PCI peer-to-peer DMA (P2PDMA) support to enable
> direct data transfers between PCIe devices through RAID and NVMe multipath
> block layers.
>
> Current Linux kernel P2PDMA infrastructure supports direct peer-to-peer
> transfers, but this support is not propagated through certain storage
> stacks like MD RAID and NVMe multipath. This adds two patches for
> MD RAID 0/1/10 and NVMe to propogate P2PDMA support through the
> storage stack.
>
> All four test scenarios demonstrate that P2PDMA capabilities are correctly
> propagated through both the MD RAID layer (patch 1/2) and NVMe multipath
> layer (patch 2/2). Direct peer-to-peer transfers complete successfully with
> full data integrity verification, confirming that:
>
> 1. RAID devices properly inherit P2PDMA capability from member devices
> 2. NVMe multipath devices correctly expose P2PDMA support
> 3. P2P memory buffers can be used for transfers involving both types
> 4. Data integrity is maintained across all transfer combinations
>
> I've added the patch specific tests and blktest log as well at the end.
>
> Repo:-
>
> git://git.kernel.org/pub/scm/linux/kernel/git/axboe/linux.git
>
> Branch HEAD:-
>
> commit 88a57e15861997dd6fa98154ad087f7831bbead1 (origin/for-next)
> Merge: 81a0a2e4e535 36446de0c30c
> Author: Jens Axboe <axboe@kernel.dk>
> Date: Fri Apr 10 07:02:42 2026 -0600
>
> Merge branch 'for-7.1/block' into for-next
>
> * for-7.1/block:
> ublk: fix tautological comparison warning in ublk_ctrl_reg_buf
> -ck
>
> Changes from V2:-
>
> 1. Unconditionally set the BLK_FEAT_PCI_P2PDMA for md and nvme multipath.
> (Christoph)
> 2. Add a prep patch to diable BLK_FEAT_PCI_P2PDMA in the blk_stack_limit().
> (christoph)
>
> Changes from V1:-
> - Update patch 1 to explicitly support MD RAID 0/1/10.
> - Fix signoff chain order for patch 2.
> - Clear BLK_FEAT_PCI_P2PDMA in nvme_mpath_add_disk() when a newly
> added path does not support it, to handle multipath across different
> transports.
> - Add nvme multipath test log for mixed transport TCP and PCIe.
>
> Chaitanya Kulkarni (1):
> block: clear BLK_FEAT_PCI_P2PDMA in blk_stack_limits() for
> non-supporting devices
>
> Kiran Kumar Modukuri (2):
> md: propagate BLK_FEAT_PCI_P2PDMA from member devices to RAID device
> nvme-multipath: enable PCI P2PDMA for multipath devices
>
> block/blk-settings.c | 2 ++
> drivers/md/raid0.c | 1 +
> drivers/md/raid1.c | 1 +
> drivers/md/raid10.c | 1 +
> drivers/nvme/host/multipath.c | 2 +-
> 5 files changed, 6 insertions(+), 1 deletion(-)
>
Tested with NVMe-oF (RDMA).
Tested=by: Pranjal Shrivastava <praan@google.com>
Thanks,
Praan
^ permalink raw reply
* Attempt to replace 60TB Spinning Rust NAS with a Raspberry Pi 5 with Quad PCI Splitter and 18 2.5 Sata SSDs TLC and DRAM-less TeamGroup QLC drives: Unreliable single Lane PCI bus Under Load
From: Marc MERLIN @ 2026-05-01 23:44 UTC (permalink / raw)
To: linux-pci, linux-raid
This is not a bug report per se, but reporting this in case it can be helpful.
Gemini AI said taht I'm just being unrealistic in putting a 4 way PCI splitter with 9 port sata cards behind that and 18 drives total on a Pi5 PCI bus which is not super resilient with PCI and Sata timeouts.
In this case, it caused QLC drives to corrupt their flash in a way that
they had to be block wiped and reset. Now they work fine in an N355 PC
with one sata controller per PCI lane and no PCI splitters.
http://marc.merlins.org/perso/linux/post_2026-04-13_Attempt-to-replace-my-60TB-Spinning-Rust-NAS-with-a-Raspberry-Pi-5-with-Quad-PCI-Splitter-and-18-2_5-Sata-SSDs-TLC-and-DRAM-less-TeamGroup-QLC-drives_-Unreliable-single-Lane-PCI-bus-Under-Load.html
Details here:
logs below for google searches or whatnot. No help needed, but sending in case it helps
nvme nvme0: controller is down; will reset: CSTS=0x3, PCI_STATUS=0x10
[57247.067230] ata8.00: exception Emask 0x0 SAct 0x0 SErr 0x400001 action 0x6 frozen
[57247.076133] ata8: SError: { RecovData Handshk }
[57247.081246] ata8.00: failed command: READ DMA
[57247.086014] ata8.00: cmd c8/00:08:c8:03:5b/00:00:00:00:00/e1 tag 2 dma 4096 in
[57247.086014] res 40/00:ff:00:00:00/00:00:00:00:00/00 Emask 0x4 (timeout)
[57247.101469] ata8.00: status: { DRDY }
[57247.105822] ata8: hard resetting link
[57247.153797] nvme nvme0: 3/0/0 default/read/poll queues
[57247.587051] ata8: SATA link up 6.0 Gbps (SStatus 133 SControl 300)
[57247.630423] ata8.00: supports DRM functions and may not be fully accessible
[57247.750869] ata8.00: supports DRM functions and may not be fully accessible
[57247.807025] ata8.00: configured for UDMA/133
[57247.811957] sd 7:0:0:0: [sdh] tag#2 UNKNOWN(0x2003) Result: hostbyte=0x00 driverbyte=DRIVER_OK cmd_age=32s
[57247.822653] sd 7:0:0:0: [sdh] tag#2 Sense Key : 0xb [current]
[57247.829121] sd 7:0:0:0: [sdh] tag#2 ASC=0x0 ASCQ=0x0
[57247.835477] sd 7:0:0:0: [sdh] tag#2 CDB: opcode=0x88 88 00 00 00 00 00 01 5b 03 c8 00 00 00 08 00 00
[57247.845243] I/O error, dev sdh, sector 22741960 op 0x0:(READ) flags 0x80700 phys_seg 1 prio class 2
[57247.855511] ata8: EH complete
[57247.872535] ata8.00: Enabling discard_zeroes_data
[60367.285453] ata9.00: exception Emask 0x10 SAct 0x0 SErr 0x400100 action 0x6 frozen
[60367.293666] ata9.00: irq_stat 0x08000000, interface fatal error
[60367.300313] ata9: SError: { UnrecovData Handshk }
[60367.306530] ata9.00: failed command: WRITE DMA EXT
[60367.311966] ata9.00: cmd 35/00:00:78:8c:f7/00:05:1e:00:00/e0 tag 9 dma 655360 out
[60367.311966] res 50/00:00:ff:03:f7/00:00:1e:00:00/e0 Emask 0x10 (ATA bus error)
[60367.328871] ata9.00: status: { DRDY }
[60367.333036] ata9: hard resetting link
[60367.805496] ata9: SATA link up 6.0 Gbps (SStatus 133 SControl 300)
[60367.863205] ata9.00: configured for UDMA/133
[60367.868064] ata9: EH complete
[60397.357520] nvme nvme0: controller is down; will reset: CSTS=0x3, PCI_STATUS=0x10
[60397.453616] nvme nvme0: 3/0/0 default/read/poll queues
[60398.929509] ata1.00: exception Emask 0x0 SAct 0x0 SErr 0x400001 action 0x6 frozen
[60398.959616] ata1: SError: { RecovData Handshk }
[60398.966761] ata1.00: failed command: READ DMA
[60398.972859] ata1.00: cmd c8/00:08:78:b9:4a/00:00:00:00:00/e2 tag 22 dma 4096 in
[60398.972859] res 40/00:00:01:4f:c2/00:00:00:00:00/00 Emask 0x4 (timeout)
[60398.990825] ata1.00: status: { DRDY }
[60398.995717] ata1: hard resetting link
[60399.473455] ata1: SATA link up 6.0 Gbps (SStatus 133 SControl 300)
[60399.541525] ata1.00: configured for UDMA/133
[60399.546657] sd 0:0:0:0: [sda] tag#22 UNKNOWN(0x2003) Result: hostbyte=0x00 driverbyte=DRIVER_OK cmd_age=32s
[60399.557577] sd 0:0:0:0: [sda] tag#22 Sense Key : 0xb [current]
[60399.564532] sd 0:0:0:0: [sda] tag#22 ASC=0x0 ASCQ=0x0
[60399.570665] sd 0:0:0:0: [sda] tag#22 CDB: opcode=0x88 88 00 00 00 00 00 02 4a b9 78 00 00 00 08 00 00
[60399.580758] I/O error, dev sda, sector 38451576 op 0x0:(READ) flags 0x80700 phys_seg 1 prio class 2
[60399.590585] ata1: EH complete
[60399.640204] ata1.00: Enabling discard_zeroes_data
[72688.943036] ata1.00: exception Emask 0x0 SAct 0x0 SErr 0x400001 action 0x6 frozen
[72688.951084] ata1: SError: { RecovData Handshk }
[72688.956422] ata1.00: failed command: WRITE DMA
[72688.961594] ata1.00: cmd ca/00:20:00:ac:82/00:00:00:00:00/e5 tag 14 dma 16384 out
[72688.961594] res 40/00:00:01:4f:c2/00:00:00:00:00/00 Emask 0x4 (timeout)
[72688.977731] ata1.00: status: { DRDY }
[72688.981969] ata1: hard resetting link
[72688.986211] ata2.00: exception Emask 0x0 SAct 0x0 SErr 0x400001 action 0x6 frozen
[72688.994663] ata2: SError: { RecovData Handshk }
[72688.999881] ata2.00: failed command: WRITE DMA
[72689.005000] ata2.00: cmd ca/00:20:c0:b0:82/00:00:00:00:00/e5 tag 19 dma 16384 out
[72689.005000] res 40/00:00:00:4f:c2/00:00:00:00:00/00 Emask 0x4 (timeout)
[72689.022962] ata2.00: status: { DRDY }
[72689.027430] ata2: hard resetting link
[72689.499039] ata2: SATA link up 6.0 Gbps (SStatus 133 SControl 300)
[72689.506396] ata1: SATA link up 6.0 Gbps (SStatus 133 SControl 300)
[72689.611777] ata1.00: configured for UDMA/133
[72689.616890] ata1: EH complete
[72689.723181] ata2.00: configured for UDMA/133
[72689.728156] ata2: EH complete
[72689.865333] ata1.00: Enabling discard_zeroes_data
[72689.871277] ata2.00: Enabling discard_zeroes_data
[73227.538624] nvme nvme1: controller is down; will reset: CSTS=0x3, PCI_STATUS=0x10
[73227.640436] nvme nvme1: D3 entry latency set to 8 seconds
[73227.658550] nvme nvme1: 1/0/0 default/read/poll queues
[86766.334170] nvme nvme0: controller is down; will reset: CSTS=0x3, PCI_STATUS=0x10
[86766.442187] nvme nvme0: 3/0/0 default/read/poll queues
[86766.863105] ata6.00: exception Emask 0x0 SAct 0x0 SErr 0x400001 action 0x6 frozen
[86766.877356] ata6: SError: { RecovData Handshk }
[86766.884232] ata6.00: failed command: WRITE DMA
[86766.891103] ata6.00: cmd ca/00:80:18:95:b5/00:00:00:00:00/e6 tag 20 dma 65536 out
[86766.891103] res 40/00:ff:00:00:00/00:00:00:00:00/00 Emask 0x4 (timeout)
[86766.908556] ata6.00: status: { DRDY }
[86766.914377] ata6: hard resetting link
[86766.919016] ata2.00: exception Emask 0x0 SAct 0x0 SErr 0x400001 action 0x6 frozen
[86766.930307] ata2: SError: { RecovData Handshk }
[86766.937937] ata2.00: failed command: READ DMA
[86766.943738] ata2.00: cmd c8/00:38:a0:e6:3b/00:00:00:00:00/e5 tag 4 dma 28672 in
[86766.943738] res 40/00:00:00:4f:c2/00:00:00:00:00/00 Emask 0x4 (timeout)
[86766.965459] ata2.00: status: { DRDY }
[86766.970640] ata2: hard resetting link
[86766.976782] ata3.00: exception Emask 0x0 SAct 0x0 SErr 0x400001 action 0x6 frozen
[86766.989369] ata3: SError: { RecovData Handshk }
[86767.001777] ata3.00: failed command: WRITE DMA
[86767.010295] ata3.00: cmd ca/00:80:18:95:b5/00:00:00:00:00/e6 tag 21 dma 65536 out
[86767.010295] res 40/00:00:06:4f:c2/00:00:00:00:00/00 Emask 0x4 (timeout)
[86767.060215] ata3.00: status: { DRDY }
[86767.071409] ata3: hard resetting link
[86767.550253] ata3: SATA link up 6.0 Gbps (SStatus 133 SControl 300)
[86767.563271] ata2: SATA link up 6.0 Gbps (SStatus 133 SControl 300)
[86767.572715] ata6: SATA link up 6.0 Gbps (SStatus 133 SControl 300)
[86767.585598] ata6.00: supports DRM functions and may not be fully accessible
[86767.616959] ata6.00: supports DRM functions and may not be fully accessible
[86767.631980] ata3.00: configured for UDMA/133
[86767.639404] ata3: EH complete
[86767.643354] ata6.00: configured for UDMA/133
[86767.661059] ahci 0001:03:00.0: port does not support device sleep
[86767.663591] ata3.00: Enabling discard_zeroes_data
[86767.676336] ata6: EH complete
[86767.745871] ata2.00: configured for UDMA/133
[86767.754280] ata2: EH complete
[86767.772933] ata2.00: Enabling discard_zeroes_data
[95256.566913] nvme nvme0: controller is down; will reset: CSTS=0x3, PCI_STATUS=0x10
[95256.574928] nvme nvme1: controller is down; will reset: CSTS=0x3, PCI_STATUS=0x10
[95256.679475] nvme nvme1: D3 entry latency set to 8 seconds
[95256.689110] nvme nvme0: 2/0/0 default/read/poll queues
[95256.694718] nvme nvme1: 1/0/0 default/read/poll queues
[95256.697626] I/O error, dev nvme0n1, sector 264208 op 0x1:(WRITE) flags 0x29800 phys_seg 1 prio class 2
[95256.712397] I/O error, dev nvme0n1, sector 264208 op 0x1:(WRITE) flags 0x29800 phys_seg 1 prio class 2
[95256.722258] md: super_written gets error=-5
[95256.727133] md/raid1:md0: Disk failure on nvme0n1p2, disabling device.
[95256.727133] md/raid1:md0: Operation continuing on 1 devices.
[95256.742401] I/O error, dev nvme0n1, sector 77334752 op 0x1:(WRITE) flags 0x4000800 phys_seg 1 prio class 2
[95256.753375] BTRFS error (device nvme0n1p3): bdev /dev/nvme0n1p3 errs: wr 1, rd 1, flush 0, corrupt 0, gen 0
[95256.764177] I/O error, dev nvme0n1, sector 77335776 op 0x1:(WRITE) flags 0x4000800 phys_seg 1 prio class 2
[95256.774805] BTRFS error (device nvme0n1p3): bdev /dev/nvme0n1p3 errs: wr 2, rd 1, flush 0, corrupt 0, gen 0
[97602.825969] ata6.00: exception Emask 0x0 SAct 0x0 SErr 0x0 action 0x6 frozen
[97602.833948] ata6.00: failed command: WRITE DMA EXT
[97602.839911] ata6.00: cmd 35/00:00:78:5a:4c/00:04:09:00:00/e0 tag 22 dma 524288 out
[97602.839911] res 40/00:01:06:4f:c2/00:00:00:00:00/00 Emask 0x4 (timeout)
[97602.858583] ata6.00: status: { DRDY }
[97602.863617] ata6: hard resetting link
[97603.337938] ata6: SATA link up 6.0 Gbps (SStatus 133 SControl 300)
[97603.346750] ata6.00: supports DRM functions and may not be fully accessible
[97603.370306] ata6.00: supports DRM functions and may not be fully accessible
[97603.430476] ata6.00: configured for UDMA/133
[97603.445466] ahci 0001:03:00.0: port does not support device sleep
[97603.452251] ata6: EH complete
[97637.643844] BTRFS warning (device dm-1): csum failed root 263 ino 3692950 off 386400256 csum 0xd04e5f48 expected csum 0x6b9afaa1 mirror 1
[97637.657936] BTRFS error (device dm-1): bdev /dev/mapper/dshelf2 errs: wr 0, rd 0, flush 0, corrupt 1, gen 0
[97638.110104] BTRFS warning (device dm-1): csum failed root 263 ino 3692950 off 386400256 csum 0xd04e5f48 expected csum 0x6b9afaa1 mirror 1
[97638.123856] BTRFS error (device dm-1): bdev /dev/mapper/dshelf2 errs: wr 0, rd 0, flush 0, corrupt 2, gen 0
[97662.159091] BTRFS warning (device dm-1): csum failed root 263 ino 3692950 off 386400256 csum 0xd04e5f48 expected csum 0x6b9afaa1 mirror 1
[97662.173941] BTRFS error (device dm-1): bdev /dev/mapper/dshelf2 errs: wr 0, rd 0, flush 0, corrupt 3, gen 0
[97662.906008] BTRFS warning (device dm-1): csum failed root 263 ino 3692950 off 386400256 csum 0xd04e5f48 expected csum 0x6b9afaa1 mirror 1
[97662.920993] BTRFS error (device dm-1): bdev /dev/mapper/dshelf2 errs: wr 0, rd 0, flush 0, corrupt 4, gen 0
--
"A mouse is a device used to point at the xterm you want to type in" - A.S.R.
Home page: http://marc.merlins.org/ | PGP 7F55D5F27AAF9D08
^ permalink raw reply
* Re: [PATCH] md/raid5: add exact old and new llbitmap mapping helpers
From: kernel test robot @ 2026-05-01 18:51 UTC (permalink / raw)
To: Yu Kuai, linux-raid
Cc: llvm, oe-kbuild-all, linux-kernel, Li Nan, Yu Kuai, Cheng Cheng
In-Reply-To: <20260419030942.824195-17-yukuai@fnnas.com>
Hi Yu,
kernel test robot noticed the following build errors:
[auto build test ERROR on linus/master]
[also build test ERROR on v7.1-rc1 next-20260430]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Yu-Kuai/md-raid5-add-exact-old-and-new-llbitmap-mapping-helpers/20260421-233709
base: linus/master
patch link: https://lore.kernel.org/r/20260419030942.824195-17-yukuai%40fnnas.com
patch subject: [PATCH] md/raid5: add exact old and new llbitmap mapping helpers
config: hexagon-allmodconfig (https://download.01.org/0day-ci/archive/20260502/202605020242.1lRKHrkP-lkp@intel.com/config)
compiler: clang version 17.0.6 (https://github.com/llvm/llvm-project 6009708b4367171ccdbf4b5905cb6a803753fe18)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260502/202605020242.1lRKHrkP-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202605020242.1lRKHrkP-lkp@intel.com/
All errors (new ones prefixed by >>):
>> drivers/md/raid5.c:9065:3: error: field designator 'bitmap_sector_map' does not refer to any field in type 'struct md_personality'; did you mean 'bitmap_sector'?
9065 | .bitmap_sector_map = raid5_bitmap_sector_map,
| ^~~~~~~~~~~~~~~~~
| bitmap_sector
drivers/md/md.h:797:9: note: 'bitmap_sector' declared here
797 | void (*bitmap_sector)(struct mddev *mddev, sector_t *offset,
| ^
>> drivers/md/raid5.c:9065:23: error: incompatible function pointer types initializing 'void (*)(struct mddev *, sector_t *, unsigned long *)' (aka 'void (*)(struct mddev *, unsigned long long *, unsigned long *)') with an expression of type 'void (struct mddev *, sector_t *, unsigned long *, bool)' (aka 'void (struct mddev *, unsigned long long *, unsigned long *, _Bool)') [-Wincompatible-function-pointer-types]
9065 | .bitmap_sector_map = raid5_bitmap_sector_map,
| ^~~~~~~~~~~~~~~~~~~~~~~
drivers/md/raid5.c:9065:23: warning: initializer overrides prior initialization of this subobject [-Winitializer-overrides]
9065 | .bitmap_sector_map = raid5_bitmap_sector_map,
| ^~~~~~~~~~~~~~~~~~~~~~~
drivers/md/raid5.c:9064:19: note: previous initialization is here
9064 | .bitmap_sector = raid5_bitmap_sector,
| ^~~~~~~~~~~~~~~~~~~
>> drivers/md/raid5.c:9066:3: error: field designator 'bitmap_sync_size' does not refer to any field in type 'struct md_personality'
9066 | .bitmap_sync_size = raid5_bitmap_sync_size,
| ~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>> drivers/md/raid5.c:9067:3: error: field designator 'bitmap_array_sectors' does not refer to any field in type 'struct md_personality'
9067 | .bitmap_array_sectors = raid5_bitmap_array_sectors,
| ~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
drivers/md/raid5.c:9098:3: error: field designator 'bitmap_sector_map' does not refer to any field in type 'struct md_personality'; did you mean 'bitmap_sector'?
9098 | .bitmap_sector_map = raid5_bitmap_sector_map,
| ^~~~~~~~~~~~~~~~~
| bitmap_sector
drivers/md/md.h:797:9: note: 'bitmap_sector' declared here
797 | void (*bitmap_sector)(struct mddev *mddev, sector_t *offset,
| ^
drivers/md/raid5.c:9098:23: error: incompatible function pointer types initializing 'void (*)(struct mddev *, sector_t *, unsigned long *)' (aka 'void (*)(struct mddev *, unsigned long long *, unsigned long *)') with an expression of type 'void (struct mddev *, sector_t *, unsigned long *, bool)' (aka 'void (struct mddev *, unsigned long long *, unsigned long *, _Bool)') [-Wincompatible-function-pointer-types]
9098 | .bitmap_sector_map = raid5_bitmap_sector_map,
| ^~~~~~~~~~~~~~~~~~~~~~~
drivers/md/raid5.c:9098:23: warning: initializer overrides prior initialization of this subobject [-Winitializer-overrides]
9098 | .bitmap_sector_map = raid5_bitmap_sector_map,
| ^~~~~~~~~~~~~~~~~~~~~~~
drivers/md/raid5.c:9097:19: note: previous initialization is here
9097 | .bitmap_sector = raid5_bitmap_sector,
| ^~~~~~~~~~~~~~~~~~~
drivers/md/raid5.c:9099:3: error: field designator 'bitmap_sync_size' does not refer to any field in type 'struct md_personality'
9099 | .bitmap_sync_size = raid5_bitmap_sync_size,
| ~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
drivers/md/raid5.c:9100:3: error: field designator 'bitmap_array_sectors' does not refer to any field in type 'struct md_personality'
9100 | .bitmap_array_sectors = raid5_bitmap_array_sectors,
| ~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
drivers/md/raid5.c:9132:3: error: field designator 'bitmap_sector_map' does not refer to any field in type 'struct md_personality'; did you mean 'bitmap_sector'?
9132 | .bitmap_sector_map = raid5_bitmap_sector_map,
| ^~~~~~~~~~~~~~~~~
| bitmap_sector
drivers/md/md.h:797:9: note: 'bitmap_sector' declared here
797 | void (*bitmap_sector)(struct mddev *mddev, sector_t *offset,
| ^
drivers/md/raid5.c:9132:23: error: incompatible function pointer types initializing 'void (*)(struct mddev *, sector_t *, unsigned long *)' (aka 'void (*)(struct mddev *, unsigned long long *, unsigned long *)') with an expression of type 'void (struct mddev *, sector_t *, unsigned long *, bool)' (aka 'void (struct mddev *, unsigned long long *, unsigned long *, _Bool)') [-Wincompatible-function-pointer-types]
9132 | .bitmap_sector_map = raid5_bitmap_sector_map,
| ^~~~~~~~~~~~~~~~~~~~~~~
drivers/md/raid5.c:9132:23: warning: initializer overrides prior initialization of this subobject [-Winitializer-overrides]
9132 | .bitmap_sector_map = raid5_bitmap_sector_map,
| ^~~~~~~~~~~~~~~~~~~~~~~
drivers/md/raid5.c:9131:19: note: previous initialization is here
9131 | .bitmap_sector = raid5_bitmap_sector,
| ^~~~~~~~~~~~~~~~~~~
drivers/md/raid5.c:9133:3: error: field designator 'bitmap_sync_size' does not refer to any field in type 'struct md_personality'
9133 | .bitmap_sync_size = raid5_bitmap_sync_size,
| ~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
drivers/md/raid5.c:9134:3: error: field designator 'bitmap_array_sectors' does not refer to any field in type 'struct md_personality'
9134 | .bitmap_array_sectors = raid5_bitmap_array_sectors,
| ~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3 warnings and 12 errors generated.
vim +9065 drivers/md/raid5.c
9035
9036 static struct md_personality raid6_personality =
9037 {
9038 .head = {
9039 .type = MD_PERSONALITY,
9040 .id = ID_RAID6,
9041 .name = "raid6",
9042 .owner = THIS_MODULE,
9043 },
9044
9045 .make_request = raid5_make_request,
9046 .run = raid5_run,
9047 .start = raid5_start,
9048 .free = raid5_free,
9049 .status = raid5_status,
9050 .error_handler = raid5_error,
9051 .hot_add_disk = raid5_add_disk,
9052 .hot_remove_disk= raid5_remove_disk,
9053 .spare_active = raid5_spare_active,
9054 .sync_request = raid5_sync_request,
9055 .resize = raid5_resize,
9056 .size = raid5_size,
9057 .check_reshape = raid6_check_reshape,
9058 .start_reshape = raid5_start_reshape,
9059 .finish_reshape = raid5_finish_reshape,
9060 .quiesce = raid5_quiesce,
9061 .takeover = raid6_takeover,
9062 .change_consistency_policy = raid5_change_consistency_policy,
9063 .prepare_suspend = raid5_prepare_suspend,
9064 .bitmap_sector = raid5_bitmap_sector,
> 9065 .bitmap_sector_map = raid5_bitmap_sector_map,
> 9066 .bitmap_sync_size = raid5_bitmap_sync_size,
> 9067 .bitmap_array_sectors = raid5_bitmap_array_sectors,
9068 };
9069 static struct md_personality raid5_personality =
9070 {
9071 .head = {
9072 .type = MD_PERSONALITY,
9073 .id = ID_RAID5,
9074 .name = "raid5",
9075 .owner = THIS_MODULE,
9076 },
9077
9078 .make_request = raid5_make_request,
9079 .run = raid5_run,
9080 .start = raid5_start,
9081 .free = raid5_free,
9082 .status = raid5_status,
9083 .error_handler = raid5_error,
9084 .hot_add_disk = raid5_add_disk,
9085 .hot_remove_disk= raid5_remove_disk,
9086 .spare_active = raid5_spare_active,
9087 .sync_request = raid5_sync_request,
9088 .resize = raid5_resize,
9089 .size = raid5_size,
9090 .check_reshape = raid5_check_reshape,
9091 .start_reshape = raid5_start_reshape,
9092 .finish_reshape = raid5_finish_reshape,
9093 .quiesce = raid5_quiesce,
9094 .takeover = raid5_takeover,
9095 .change_consistency_policy = raid5_change_consistency_policy,
9096 .prepare_suspend = raid5_prepare_suspend,
9097 .bitmap_sector = raid5_bitmap_sector,
9098 .bitmap_sector_map = raid5_bitmap_sector_map,
9099 .bitmap_sync_size = raid5_bitmap_sync_size,
9100 .bitmap_array_sectors = raid5_bitmap_array_sectors,
9101 };
9102
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply
* [PATCH v2 3/3] md/raid1,raid10: fix bio accounting for split md cloned bios
From: Abd-Alrhman Masalkhi @ 2026-05-01 11:46 UTC (permalink / raw)
To: song, yukuai, xni, neilb, shli
Cc: linux-raid, linux-kernel, Abd-Alrhman Masalkhi
In-Reply-To: <20260501114652.590037-1-abd.masalkhi@gmail.com>
Use md_cloned_bio() to control bio accounting instead of relying
on r1bio_existed in raid1 or the io_accounting flag in raid10.
The previous logic does not reliably reflect whether a bio is an
md cloned bio. When a failed bio is split and resubmitted via
bio_submit_split_bioset() on the error path, this can lead to either
double accounting for md cloned bios, or missing accounting for bios
returned from bio_submit_split_bioset()
Fix this by using md_cloned_bio() to detect md cloned bios and
skip accounting accordingly.
Fixes: bb2a9acefaf9 ("md/raid1: switch to use md_account_bio() for io accounting")
Fixes: 820455238366 ("md/raid10: switch to use md_account_bio() for io accounting")
Signed-off-by: Abd-Alrhman Masalkhi <abd.masalkhi@gmail.com>
---
This patch depends on patch 1.
Changes in v2:
- New patch.
---
drivers/md/raid1.c | 2 +-
drivers/md/raid10.c | 8 ++++----
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
index c52ecd38c163..dfaf34141325 100644
--- a/drivers/md/raid1.c
+++ b/drivers/md/raid1.c
@@ -1396,7 +1396,7 @@ static void raid1_read_request(struct mddev *mddev, struct bio *bio,
}
r1_bio->read_disk = rdisk;
- if (!r1bio_existed) {
+ if (likely(!md_cloned_bio(mddev, bio))) {
md_account_bio(mddev, &bio);
r1_bio->master_bio = bio;
}
diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index 8c6fc398260e..93af7bbc9005 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -1146,7 +1146,7 @@ static bool regular_request_wait(struct mddev *mddev, struct r10conf *conf,
}
static void raid10_read_request(struct mddev *mddev, struct bio *bio,
- struct r10bio *r10_bio, bool io_accounting)
+ struct r10bio *r10_bio)
{
struct r10conf *conf = mddev->private;
struct bio *read_bio;
@@ -1226,7 +1226,7 @@ static void raid10_read_request(struct mddev *mddev, struct bio *bio,
}
slot = r10_bio->read_slot;
- if (io_accounting) {
+ if (likely(!md_cloned_bio(mddev, bio))) {
md_account_bio(mddev, &bio);
r10_bio->master_bio = bio;
}
@@ -1552,7 +1552,7 @@ static void __make_request(struct mddev *mddev, struct bio *bio, int sectors)
conf->geo.raid_disks);
if (bio_data_dir(bio) == READ)
- raid10_read_request(mddev, bio, r10_bio, true);
+ raid10_read_request(mddev, bio, r10_bio);
else
raid10_write_request(mddev, bio, r10_bio);
}
@@ -2872,7 +2872,7 @@ static void handle_read_error(struct mddev *mddev, struct r10bio *r10_bio)
rdev_dec_pending(rdev, mddev);
r10_bio->state = 0;
- raid10_read_request(mddev, r10_bio->master_bio, r10_bio, false);
+ raid10_read_request(mddev, r10_bio->master_bio, r10_bio);
/*
* allow_barrier after re-submit to ensure no sync io
* can be issued while regular io pending.
--
2.43.0
^ permalink raw reply related
* [PATCH v2 2/3] md/raid1,raid10: fix error-path detection with md_cloned_bio()
From: Abd-Alrhman Masalkhi @ 2026-05-01 11:46 UTC (permalink / raw)
To: song, yukuai, xni, neilb, shli
Cc: linux-raid, linux-kernel, Abd-Alrhman Masalkhi
In-Reply-To: <20260501114652.590037-1-abd.masalkhi@gmail.com>
Detect the error path using md_cloned_bio() instead of relying
on r1_bio in raid1 or r10_bio->read_slot in raid10, which may be
NULL or -1 after splitting and resubmitting a failed bio.
As a result, the error path may not be recognized and memory
allocations can incorrectly use GFP_NOIO instead of
(GFP_NOIO | __GFP_HIGH), which can lead to a deadlock under
memory pressure.
Fixes: 689389a06ce7 ("md/raid1: simplify handle_read_error().")
Fixes: 545250f24809 ("md/raid10: simplify handle_read_error()")
Signed-off-by: Abd-Alrhman Masalkhi <abd.masalkhi@gmail.com>
---
This patch depends on patch 1.
Changes in v2:
- New patch.
---
drivers/md/raid1.c | 13 ++++++++++---
drivers/md/raid10.c | 20 ++++++++++++++------
2 files changed, 24 insertions(+), 9 deletions(-)
diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
index cc9914bd15c1..c52ecd38c163 100644
--- a/drivers/md/raid1.c
+++ b/drivers/md/raid1.c
@@ -1321,11 +1321,18 @@ static void raid1_read_request(struct mddev *mddev, struct bio *bio,
bool r1bio_existed = !!r1_bio;
/*
- * If r1_bio is set, we are blocking the raid1d thread
- * so there is a tiny risk of deadlock. So ask for
+ * An md cloned bio indicates we are in the error path.
+ * This is more reliable than checking r1_bio, which might
+ * be NULL even in the error path if a failed bio was split.
+ */
+ bool err_path = md_cloned_bio(mddev, bio);
+
+ /*
+ * If we are in the error path, we are blocking the raid1d
+ * thread so there is a tiny risk of deadlock. So ask for
* emergency memory if needed.
*/
- gfp_t gfp = r1_bio ? (GFP_NOIO | __GFP_HIGH) : GFP_NOIO;
+ gfp_t gfp = err_path ? (GFP_NOIO | __GFP_HIGH) : GFP_NOIO;
/*
* Still need barrier for READ in case that whole
diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index 3a591e60a144..8c6fc398260e 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -1155,7 +1155,20 @@ static void raid10_read_request(struct mddev *mddev, struct bio *bio,
char b[BDEVNAME_SIZE];
int slot = r10_bio->read_slot;
struct md_rdev *err_rdev = NULL;
- gfp_t gfp = GFP_NOIO;
+
+ /*
+ * An md cloned bio indicates we are in the error path.
+ * This is more reliable than checking slot, which might
+ * be -1 even in the error path if a failed bio was split.
+ */
+ bool err_path = md_cloned_bio(mddev, bio);
+
+ /*
+ * If we are in the error path, we are blocking the raid10d
+ * thread so there is a tiny risk of deadlock. So ask for
+ * emergency memory if needed.
+ */
+ gfp_t gfp = err_path ? (GFP_NOIO | __GFP_HIGH) : GFP_NOIO;
if (slot >= 0 && r10_bio->devs[slot].rdev) {
/*
@@ -1166,11 +1179,6 @@ static void raid10_read_request(struct mddev *mddev, struct bio *bio,
* we lose the device name in error messages.
*/
int disk;
- /*
- * As we are blocking raid10, it is a little safer to
- * use __GFP_HIGH.
- */
- gfp = GFP_NOIO | __GFP_HIGH;
disk = r10_bio->devs[slot].devnum;
err_rdev = conf->mirrors[disk].rdev;
--
2.43.0
^ permalink raw reply related
* [PATCH v2 1/3] md/raid1,raid10: fix deadlock in read error recovery path
From: Abd-Alrhman Masalkhi @ 2026-05-01 11:46 UTC (permalink / raw)
To: song, yukuai, xni, neilb, shli
Cc: linux-raid, linux-kernel, Abd-Alrhman Masalkhi
In-Reply-To: <20260501114652.590037-1-abd.masalkhi@gmail.com>
raid1d and raid10d may resubmit a split md cloned bio while handling
a read error. In this case, resubmitting the bio can lead to a deadlock
if the array is suspended before md_handle_request() acquires an
active_io reference via percpu_ref_tryget_live().
Since the cloned bio already holds an active_io reference,
trying to acquire another reference via percpu_ref_tryget_live()
can lead to a deadlock while the array is suspended.
Fix this by using percpu_ref_get() for md cloned bios.
Fixes: bb2a9acefaf9 ("md/raid1: switch to use md_account_bio() for io accounting")
Fixes: 820455238366 ("md/raid10: switch to use md_account_bio() for io accounting")
Signed-off-by: Abd-Alrhman Masalkhi <abd.masalkhi@gmail.com>
---
Changes in v2:
- Use md_cloned_bio() consistently to detect cloned bios.
- Recognize that raid10 has the same issue and fix it in this series
- Allow splitting bios.
- Handle md cloned bios explicitly in md_handle_request()
- Link v1: https://lore.kernel.org/linux-raid/20260427103446.300378-1-abd.masalkhi@gmail.com/
Please let me know if I should add a Suggested-by tag for Yu Kuai,
as the solution approach was suggested during review.
Link to Yu Kuai' email: https://lore.kernel.org/linux-raid/m2lde74dtw.fsf@gmail.com/T/#m714020a38b60fc5f84b9a24f0c46acbe5d7342d6
Thanks
Abd-alrhman
---
drivers/md/md.c | 25 ++++++++++++++++---------
drivers/md/md.h | 5 +++++
2 files changed, 21 insertions(+), 9 deletions(-)
diff --git a/drivers/md/md.c b/drivers/md/md.c
index e926aef9ec43..96db1e7850e9 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -396,17 +396,24 @@ static bool is_suspended(struct mddev *mddev, struct bio *bio)
bool md_handle_request(struct mddev *mddev, struct bio *bio)
{
check_suspended:
- if (is_suspended(mddev, bio)) {
- /* Bail out if REQ_NOWAIT is set for the bio */
- if (bio->bi_opf & REQ_NOWAIT) {
- bio_wouldblock_error(bio);
- return true;
+ if (unlikely(md_cloned_bio(mddev, bio))) {
+ /*
+ * This bio is an MD cloned bio and already holds an
+ * active_io reference, so percpu_ref_get() is safe here.
+ */
+ percpu_ref_get(&mddev->active_io);
+ } else {
+ if (is_suspended(mddev, bio)) {
+ /* Bail out if REQ_NOWAIT is set for the bio */
+ if (bio->bi_opf & REQ_NOWAIT) {
+ bio_wouldblock_error(bio);
+ return true;
+ }
+ wait_event(mddev->sb_wait, !is_suspended(mddev, bio));
}
- wait_event(mddev->sb_wait, !is_suspended(mddev, bio));
+ if (!percpu_ref_tryget_live(&mddev->active_io))
+ goto check_suspended;
}
- if (!percpu_ref_tryget_live(&mddev->active_io))
- goto check_suspended;
-
if (!mddev->pers->make_request(mddev, bio)) {
percpu_ref_put(&mddev->active_io);
if (mddev_is_dm(mddev) && mddev->pers->prepare_suspend)
diff --git a/drivers/md/md.h b/drivers/md/md.h
index 3bfbee595156..e44074d30cf9 100644
--- a/drivers/md/md.h
+++ b/drivers/md/md.h
@@ -1038,6 +1038,11 @@ void mddev_update_io_opt(struct mddev *mddev, unsigned int nr_stripes);
extern const struct block_device_operations md_fops;
+static inline bool md_cloned_bio(struct mddev *mddev, struct bio *bio)
+{
+ return bio->bi_pool == &mddev->io_clone_set;
+}
+
/*
* MD devices can be used undeneath by DM, in which case ->gendisk is NULL.
*/
--
2.43.0
^ permalink raw reply related
* [PATCH v2 0/3] md/raid1,raid10: fix deadlock and bio accounting in read error path
From: Abd-Alrhman Masalkhi @ 2026-05-01 11:46 UTC (permalink / raw)
To: song, yukuai, xni, neilb, shli
Cc: linux-raid, linux-kernel, Abd-Alrhman Masalkhi
Hi,
This series revisits an issue in the read error recovery path for
raid1 and raid10 when bios are split and resubmitted.
In v1, the approach was to avoid splitting bios in the raid1 thread
to prevent recursion and potential deadlocks. However, this was not
ideal and too restrictive.
As suggested by Yu Kuai, this series instead handles the problem in
md_handle_request() and allows bio splitting.
Link to Yu Kuai' email: https://lore.kernel.org/linux-raid/m2lde74dtw.fsf@gmail.com/T/#m714020a38b60fc5f84b9a24f0c46acbe5d7342d6
This series fixes the above by:
- handling md cloned bios explicitly in md_handle_request()
- using md_cloned_bio() to reliably detect the error path
- fixing bio accounting to avoid double/missing accounting
Changes in v2:
- Split fixes into separate patches for clarity.
- Use md_cloned_bio() consistently to detect cloned bios.
- Recognize that raid10 has the same issue and fix it in this series
- Allow splitting bios.
- Handle md cloned bios explicitly in md_handle_request()
- Link v1: https://lore.kernel.org/linux-raid/20260427103446.300378-1-abd.masalkhi@gmail.com/
Abd-Alrhman Masalkhi (3):
md/raid1,raid10: fix deadlock in read error recovery path
md/raid1,raid10: fix error-path detection with md_cloned_bio()
md/raid1,raid10: fix bio accounting for split md cloned bios
drivers/md/md.c | 25 ++++++++++++++++---------
drivers/md/md.h | 5 +++++
drivers/md/raid1.c | 15 +++++++++++----
drivers/md/raid10.c | 28 ++++++++++++++++++----------
4 files changed, 50 insertions(+), 23 deletions(-)
--
2.43.0
^ permalink raw reply
* Re: [PATCH] md/raid5: split reshape bios before bitmap accounting
From: kernel test robot @ 2026-04-30 19:48 UTC (permalink / raw)
To: Yu Kuai, linux-raid
Cc: oe-kbuild-all, linux-kernel, Li Nan, Yu Kuai, Cheng Cheng
In-Reply-To: <20260419030942.824195-20-yukuai@fnnas.com>
Hi Yu,
kernel test robot noticed the following build warnings:
[auto build test WARNING on linus/master]
[also build test WARNING on song-md/md-next v7.1-rc1 next-20260429]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Yu-Kuai/md-raid5-split-reshape-bios-before-bitmap-accounting/20260425-083941
base: linus/master
patch link: https://lore.kernel.org/r/20260419030942.824195-20-yukuai%40fnnas.com
patch subject: [PATCH] md/raid5: split reshape bios before bitmap accounting
config: arc-randconfig-001 (https://download.01.org/0day-ci/archive/20260501/202605010352.oO1uCwCR-lkp@intel.com/config)
compiler: arc-linux-gcc (GCC) 8.5.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260501/202605010352.oO1uCwCR-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202605010352.oO1uCwCR-lkp@intel.com/
All warnings (new ones prefixed by >>):
drivers/md/raid5.c: In function 'raid5_make_request':
drivers/md/raid5.c:6126:7: error: implicit declaration of function 'mddev_bio_split_at_reshape_offset' [-Werror=implicit-function-declaration]
bi = mddev_bio_split_at_reshape_offset(mddev, bi, NULL,
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>> drivers/md/raid5.c:6126:5: warning: assignment to 'struct bio *' from 'int' makes pointer from integer without a cast [-Wint-conversion]
bi = mddev_bio_split_at_reshape_offset(mddev, bi, NULL,
^
cc1: some warnings being treated as errors
vim +6126 drivers/md/raid5.c
6083
6084 static bool raid5_make_request(struct mddev *mddev, struct bio * bi)
6085 {
6086 DEFINE_WAIT_FUNC(wait, woken_wake_function);
6087 struct r5conf *conf = mddev->private;
6088 const int rw = bio_data_dir(bi);
6089 struct stripe_request_ctx *ctx;
6090 sector_t logical_sector;
6091 enum stripe_result res;
6092 int s, stripe_cnt;
6093 bool on_wq;
6094
6095 if (unlikely(bi->bi_opf & REQ_PREFLUSH)) {
6096 int ret = log_handle_flush_request(conf, bi);
6097
6098 if (ret == 0)
6099 return true;
6100 if (ret == -ENODEV) {
6101 if (md_flush_request(mddev, bi))
6102 return true;
6103 }
6104 /* ret == -EAGAIN, fallback */
6105 }
6106
6107 md_write_start(mddev, bi);
6108 /*
6109 * If array is degraded, better not do chunk aligned read because
6110 * later we might have to read it again in order to reconstruct
6111 * data on failed drives.
6112 */
6113 if (rw == READ && mddev->degraded == 0 &&
6114 mddev->reshape_position == MaxSector) {
6115 bi = chunk_aligned_read(mddev, bi);
6116 if (!bi)
6117 return true;
6118 }
6119
6120 if (unlikely(bio_op(bi) == REQ_OP_DISCARD)) {
6121 make_discard_request(mddev, bi);
6122 md_write_end(mddev);
6123 return true;
6124 }
6125
> 6126 bi = mddev_bio_split_at_reshape_offset(mddev, bi, NULL,
6127 &conf->bio_split);
6128 if (!bi) {
6129 if (rw == WRITE)
6130 md_write_end(mddev);
6131 return true;
6132 }
6133
6134 logical_sector = bi->bi_iter.bi_sector & ~((sector_t)RAID5_STRIPE_SECTORS(conf)-1);
6135 bi->bi_next = NULL;
6136
6137 ctx = mempool_alloc(conf->ctx_pool, GFP_NOIO);
6138 memset(ctx, 0, conf->ctx_size);
6139 ctx->first_sector = logical_sector;
6140 ctx->last_sector = bio_end_sector(bi);
6141 /*
6142 * if r5l_handle_flush_request() didn't clear REQ_PREFLUSH,
6143 * we need to flush journal device
6144 */
6145 if (unlikely(bi->bi_opf & REQ_PREFLUSH))
6146 ctx->do_flush = true;
6147
6148 stripe_cnt = DIV_ROUND_UP_SECTOR_T(ctx->last_sector - logical_sector,
6149 RAID5_STRIPE_SECTORS(conf));
6150 bitmap_set(ctx->sectors_to_do, 0, stripe_cnt);
6151
6152 pr_debug("raid456: %s, logical %llu to %llu\n", __func__,
6153 bi->bi_iter.bi_sector, ctx->last_sector);
6154
6155 /* Bail out if conflicts with reshape and REQ_NOWAIT is set */
6156 if ((bi->bi_opf & REQ_NOWAIT) &&
6157 get_reshape_loc(mddev, conf, logical_sector) == LOC_INSIDE_RESHAPE) {
6158 bio_wouldblock_error(bi);
6159 if (rw == WRITE)
6160 md_write_end(mddev);
6161 mempool_free(ctx, conf->ctx_pool);
6162 return true;
6163 }
6164 md_account_bio(mddev, &bi);
6165
6166 /*
6167 * Lets start with the stripe with the lowest chunk offset in the first
6168 * chunk. That has the best chances of creating IOs adjacent to
6169 * previous IOs in case of sequential IO and thus creates the most
6170 * sequential IO pattern. We don't bother with the optimization when
6171 * reshaping as the performance benefit is not worth the complexity.
6172 */
6173 if (likely(conf->reshape_progress == MaxSector)) {
6174 logical_sector = raid5_bio_lowest_chunk_sector(conf, bi);
6175 on_wq = false;
6176 } else {
6177 add_wait_queue(&conf->wait_for_reshape, &wait);
6178 on_wq = true;
6179 }
6180 s = (logical_sector - ctx->first_sector) >> RAID5_STRIPE_SHIFT(conf);
6181
6182 while (1) {
6183 res = make_stripe_request(mddev, conf, ctx, logical_sector,
6184 bi);
6185 if (res == STRIPE_FAIL || res == STRIPE_WAIT_RESHAPE)
6186 break;
6187
6188 if (res == STRIPE_RETRY)
6189 continue;
6190
6191 if (res == STRIPE_SCHEDULE_AND_RETRY) {
6192 WARN_ON_ONCE(!on_wq);
6193 /*
6194 * Must release the reference to batch_last before
6195 * scheduling and waiting for work to be done,
6196 * otherwise the batch_last stripe head could prevent
6197 * raid5_activate_delayed() from making progress
6198 * and thus deadlocking.
6199 */
6200 if (ctx->batch_last) {
6201 raid5_release_stripe(ctx->batch_last);
6202 ctx->batch_last = NULL;
6203 }
6204
6205 wait_woken(&wait, TASK_UNINTERRUPTIBLE,
6206 MAX_SCHEDULE_TIMEOUT);
6207 continue;
6208 }
6209
6210 s = find_next_bit_wrap(ctx->sectors_to_do, stripe_cnt, s);
6211 if (s == stripe_cnt)
6212 break;
6213
6214 logical_sector = ctx->first_sector +
6215 (s << RAID5_STRIPE_SHIFT(conf));
6216 }
6217 if (unlikely(on_wq))
6218 remove_wait_queue(&conf->wait_for_reshape, &wait);
6219
6220 if (ctx->batch_last)
6221 raid5_release_stripe(ctx->batch_last);
6222
6223 if (rw == WRITE)
6224 md_write_end(mddev);
6225
6226 mempool_free(ctx, conf->ctx_pool);
6227 if (res == STRIPE_WAIT_RESHAPE) {
6228 md_free_cloned_bio(bi);
6229 return false;
6230 }
6231
6232 bio_endio(bi);
6233 return true;
6234 }
6235
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply
* Why is mdraid not listed sashiko.dev (review by gemini/gemini-3.1-pro-preview)?
From: Paul Menzel @ 2026-04-30 15:12 UTC (permalink / raw)
To: Yu Kuai, Song Liu; +Cc: linux-raid
Dear Kuai, dear Song, dear Linux folks,
Do you know, why this subsystem is not listed on the sashiko.dev [1],
where Google’s gemini/gemini-3.1-pro-preview reviews Linux kernel patches?
Kind regards,
Paul
PS: LKML is listed, so patches sent to linux-kernel@vger.kernel.org are
reviewed, but cannot be filtered according to the subsystem.
[1]: https://sashiko.dev/
^ permalink raw reply
* Re: [PATCH] md/raid5: Fix bio retry on interrupted reshape
From: Nigel Croxon @ 2026-04-30 11:53 UTC (permalink / raw)
To: Paul Menzel; +Cc: song, yukuai, linux-raid
In-Reply-To: <a7e94a16-439e-4b28-bc0b-580e00953473@molgen.mpg.de>
Looks like this was just fixed by Benjamin Marzinski's
commit 418b3e64e4459 md/raid5: Fix UAF on IO across the reshape position
So my patch is not needed anymore.
-Nigel
On 4/29/26 9:07 AM, Paul Menzel wrote:
> Dear Nigel,
>
>
> Thank you for your patch.
>
> Am 29.04.26 um 13:10 schrieb Nigel Croxon:
>> When a bio encounters LOC_INSIDE_RESHAPE during a reshape that is
>> interrupted (stopped or unable to progress), the code sets
>> bi->bi_status = BLK_STS_RESOURCE to signal the block layer for retry.
>> However, bio_endio() is never called, so the block layer never
>> receives the completion notification and the retry never happens.
>>
>> This causes I/O to hang when a filesystem is layered over RAID5 and
>> reshape gets stuck.
>>
>> Fix this by calling bio_endio(bi) before md_free_cloned_bio(bi) so
>> the block layer is properly notified of the BLK_STS_RESOURCE status
>> and can retry the request.
>>
>> Tested stripes and stripe size conversions under load comparing
>> files multiple times during each conversion (i.e. MD reshape) on
>> ext4 after dropping caches degrading the RaidLV each time and
>
> I thought RaidLV misspelled Raid V (Raid 5), so should you resend,
> maybe write it as RAID LV.
>
>> no data corruption.
>>
>> Fixes: https://lwn.net/Articles/757123/
>
> Which paragraph/comment exactly?
>
>> Signed-off-by: Nigel Croxon <ncroxon@redhat.com>
>> ---
>> drivers/md/raid5.c | 1 +
>> 1 file changed, 1 insertion(+)
>>
>> diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
>> index 6e79829c5acb..9a3475429ef4 100644
>> --- a/drivers/md/raid5.c
>> +++ b/drivers/md/raid5.c
>> @@ -6217,6 +6217,7 @@ static bool raid5_make_request(struct mddev
>> *mddev, struct bio * bi)
>>
>> mempool_free(ctx, conf->ctx_pool);
>> if (res == STRIPE_WAIT_RESHAPE) {
>> + bio_endio(bi);
>> md_free_cloned_bio(bi);
>> return false;
>> }
>
> Reviewed-by: Paul Menzel <pmenzel@molgen.mpg.de>
>
>
> Kind regards,
>
> Paul
>
^ permalink raw reply
* Re: Suggest me a cost effective SATA SSD?
From: o1bigtenor @ 2026-04-30 10:48 UTC (permalink / raw)
To: linux-raid
In-Reply-To: <afKLmiyhEe4Om0Nt@mail.bitfolk.com>
On Wed, Apr 29, 2026 at 6:09 PM Andy Smith <andy@strugglers.net> wrote:
>
> Hi,
>
> I've inherited a system with a pair of these:
>
> Model Family: Crucial/Micron Client SSDs
> Device Model: CT4000BX500SSD1
> User Capacity: 4,000,787,030,016 bytes [4.00 TB]
>
> Their write performance is terrible. Struggling to get 20MB/s sequential
> write. Their TBW count is low so it's not an issue of excessive write
> cycles. I've searched around and established they are just really bad
> SSDs. They also don't support SMART self-tests, which seems like a
> really cheap thing to do.
>
> I'm further confident that the problem lies with these SSDs because
> there is a pair of much better SSDs in there and they perform as I would
> expect. However, the use case for this storage is for lower cost so it's
> not an option to just buy more of those.
>
> So, could anyone suggest a decent low end (consumer/prosumer market) SSD
> model that is known to work well without terrible firmware bugs under
> Linux, preferably with power loss protection? Low write endurance is
> fine. Capacity of 4TB ideally.
>
I would suggest that power loss protection be sought using a UPS - - - imo a
better idea.
HTH
^ permalink raw reply
* Re: [PATCH] md/raid5: split reshape bios before bitmap accounting
From: kernel test robot @ 2026-04-30 4:07 UTC (permalink / raw)
To: Yu Kuai, linux-raid
Cc: oe-kbuild-all, linux-kernel, Li Nan, Yu Kuai, Cheng Cheng
In-Reply-To: <20260419030942.824195-20-yukuai@fnnas.com>
Hi Yu,
kernel test robot noticed the following build errors:
[auto build test ERROR on linus/master]
[also build test ERROR on v7.1-rc1 next-20260429]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Yu-Kuai/md-raid5-split-reshape-bios-before-bitmap-accounting/20260425-083941
base: linus/master
patch link: https://lore.kernel.org/r/20260419030942.824195-20-yukuai%40fnnas.com
patch subject: [PATCH] md/raid5: split reshape bios before bitmap accounting
config: parisc64-defconfig (https://download.01.org/0day-ci/archive/20260430/202604301127.hkMOIzHM-lkp@intel.com/config)
compiler: hppa64-linux-gcc (GCC) 15.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260430/202604301127.hkMOIzHM-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202604301127.hkMOIzHM-lkp@intel.com/
All errors (new ones prefixed by >>):
drivers/md/raid5.c: In function 'raid5_make_request':
>> drivers/md/raid5.c:6126:14: error: implicit declaration of function 'mddev_bio_split_at_reshape_offset' [-Wimplicit-function-declaration]
6126 | bi = mddev_bio_split_at_reshape_offset(mddev, bi, NULL,
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>> drivers/md/raid5.c:6126:12: error: assignment to 'struct bio *' from 'int' makes pointer from integer without a cast [-Wint-conversion]
6126 | bi = mddev_bio_split_at_reshape_offset(mddev, bi, NULL,
| ^
vim +/mddev_bio_split_at_reshape_offset +6126 drivers/md/raid5.c
6083
6084 static bool raid5_make_request(struct mddev *mddev, struct bio * bi)
6085 {
6086 DEFINE_WAIT_FUNC(wait, woken_wake_function);
6087 struct r5conf *conf = mddev->private;
6088 const int rw = bio_data_dir(bi);
6089 struct stripe_request_ctx *ctx;
6090 sector_t logical_sector;
6091 enum stripe_result res;
6092 int s, stripe_cnt;
6093 bool on_wq;
6094
6095 if (unlikely(bi->bi_opf & REQ_PREFLUSH)) {
6096 int ret = log_handle_flush_request(conf, bi);
6097
6098 if (ret == 0)
6099 return true;
6100 if (ret == -ENODEV) {
6101 if (md_flush_request(mddev, bi))
6102 return true;
6103 }
6104 /* ret == -EAGAIN, fallback */
6105 }
6106
6107 md_write_start(mddev, bi);
6108 /*
6109 * If array is degraded, better not do chunk aligned read because
6110 * later we might have to read it again in order to reconstruct
6111 * data on failed drives.
6112 */
6113 if (rw == READ && mddev->degraded == 0 &&
6114 mddev->reshape_position == MaxSector) {
6115 bi = chunk_aligned_read(mddev, bi);
6116 if (!bi)
6117 return true;
6118 }
6119
6120 if (unlikely(bio_op(bi) == REQ_OP_DISCARD)) {
6121 make_discard_request(mddev, bi);
6122 md_write_end(mddev);
6123 return true;
6124 }
6125
> 6126 bi = mddev_bio_split_at_reshape_offset(mddev, bi, NULL,
6127 &conf->bio_split);
6128 if (!bi) {
6129 if (rw == WRITE)
6130 md_write_end(mddev);
6131 return true;
6132 }
6133
6134 logical_sector = bi->bi_iter.bi_sector & ~((sector_t)RAID5_STRIPE_SECTORS(conf)-1);
6135 bi->bi_next = NULL;
6136
6137 ctx = mempool_alloc(conf->ctx_pool, GFP_NOIO);
6138 memset(ctx, 0, conf->ctx_size);
6139 ctx->first_sector = logical_sector;
6140 ctx->last_sector = bio_end_sector(bi);
6141 /*
6142 * if r5l_handle_flush_request() didn't clear REQ_PREFLUSH,
6143 * we need to flush journal device
6144 */
6145 if (unlikely(bi->bi_opf & REQ_PREFLUSH))
6146 ctx->do_flush = true;
6147
6148 stripe_cnt = DIV_ROUND_UP_SECTOR_T(ctx->last_sector - logical_sector,
6149 RAID5_STRIPE_SECTORS(conf));
6150 bitmap_set(ctx->sectors_to_do, 0, stripe_cnt);
6151
6152 pr_debug("raid456: %s, logical %llu to %llu\n", __func__,
6153 bi->bi_iter.bi_sector, ctx->last_sector);
6154
6155 /* Bail out if conflicts with reshape and REQ_NOWAIT is set */
6156 if ((bi->bi_opf & REQ_NOWAIT) &&
6157 get_reshape_loc(mddev, conf, logical_sector) == LOC_INSIDE_RESHAPE) {
6158 bio_wouldblock_error(bi);
6159 if (rw == WRITE)
6160 md_write_end(mddev);
6161 mempool_free(ctx, conf->ctx_pool);
6162 return true;
6163 }
6164 md_account_bio(mddev, &bi);
6165
6166 /*
6167 * Lets start with the stripe with the lowest chunk offset in the first
6168 * chunk. That has the best chances of creating IOs adjacent to
6169 * previous IOs in case of sequential IO and thus creates the most
6170 * sequential IO pattern. We don't bother with the optimization when
6171 * reshaping as the performance benefit is not worth the complexity.
6172 */
6173 if (likely(conf->reshape_progress == MaxSector)) {
6174 logical_sector = raid5_bio_lowest_chunk_sector(conf, bi);
6175 on_wq = false;
6176 } else {
6177 add_wait_queue(&conf->wait_for_reshape, &wait);
6178 on_wq = true;
6179 }
6180 s = (logical_sector - ctx->first_sector) >> RAID5_STRIPE_SHIFT(conf);
6181
6182 while (1) {
6183 res = make_stripe_request(mddev, conf, ctx, logical_sector,
6184 bi);
6185 if (res == STRIPE_FAIL || res == STRIPE_WAIT_RESHAPE)
6186 break;
6187
6188 if (res == STRIPE_RETRY)
6189 continue;
6190
6191 if (res == STRIPE_SCHEDULE_AND_RETRY) {
6192 WARN_ON_ONCE(!on_wq);
6193 /*
6194 * Must release the reference to batch_last before
6195 * scheduling and waiting for work to be done,
6196 * otherwise the batch_last stripe head could prevent
6197 * raid5_activate_delayed() from making progress
6198 * and thus deadlocking.
6199 */
6200 if (ctx->batch_last) {
6201 raid5_release_stripe(ctx->batch_last);
6202 ctx->batch_last = NULL;
6203 }
6204
6205 wait_woken(&wait, TASK_UNINTERRUPTIBLE,
6206 MAX_SCHEDULE_TIMEOUT);
6207 continue;
6208 }
6209
6210 s = find_next_bit_wrap(ctx->sectors_to_do, stripe_cnt, s);
6211 if (s == stripe_cnt)
6212 break;
6213
6214 logical_sector = ctx->first_sector +
6215 (s << RAID5_STRIPE_SHIFT(conf));
6216 }
6217 if (unlikely(on_wq))
6218 remove_wait_queue(&conf->wait_for_reshape, &wait);
6219
6220 if (ctx->batch_last)
6221 raid5_release_stripe(ctx->batch_last);
6222
6223 if (rw == WRITE)
6224 md_write_end(mddev);
6225
6226 mempool_free(ctx, conf->ctx_pool);
6227 if (res == STRIPE_WAIT_RESHAPE) {
6228 md_free_cloned_bio(bi);
6229 return false;
6230 }
6231
6232 bio_endio(bi);
6233 return true;
6234 }
6235
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply
* Re: [PATCH 01/28] xor: assert that xor_blocks is not call from interrupt context
From: patchwork-bot+linux-riscv @ 2026-04-30 3:24 UTC (permalink / raw)
To: Christoph Hellwig
Cc: linux-riscv, akpm, richard.henderson, mattst88, linmag7, linux,
catalin.marinas, will, ardb, chenhuacai, kernel, maddy, mpe,
npiggin, chleroy, pjw, palmer, aou, alex, hca, gor, agordeev,
borntraeger, svens, davem, andreas, richard, anton.ivanov,
johannes, tglx, mingo, bp, dave.hansen, x86, hpa, herbert,
dan.j.williams, clm, dsterba, arnd, song, yukuai, linan122, tytso,
Jason, linux-alpha, linux-kernel, linux-arm-kernel, loongarch,
linuxppc-dev, linux-s390, sparclinux, linux-um, linux-crypto,
linux-btrfs, linux-arch, linux-raid
In-Reply-To: <20260327061704.3707577-2-hch@lst.de>
Hello:
This series was applied to riscv/linux.git (fixes)
by Andrew Morton <akpm@linux-foundation.org>:
On Fri, 27 Mar 2026 07:16:33 +0100 you wrote:
> Most of the optimized xor_blocks versions require FPU/vector registers,
> which generally are not supported in interrupt context.
>
> Both callers already are in user context, so enforce this at the highest
> level.
>
> Signed-off-by: Christoph Hellwig <hch@lst.de>
>
> [...]
Here is the summary with links:
- [01/28] xor: assert that xor_blocks is not call from interrupt context
https://git.kernel.org/riscv/c/d8593b8f9354
- [02/28] arm/xor: remove in_interrupt() handling
https://git.kernel.org/riscv/c/b7ca705758b9
- [03/28] arm64/xor: fix conflicting attributes for xor_block_template
https://git.kernel.org/riscv/c/675a0dd596e7
- [04/28] um/xor: cleanup xor.h
https://git.kernel.org/riscv/c/3ea16a98518a
- [05/28] xor: move to lib/raid/
https://git.kernel.org/riscv/c/9e229025e247
- [06/28] xor: small cleanups
https://git.kernel.org/riscv/c/7c6e6b2b48e8
- [07/28] xor: cleanup registration and probing
https://git.kernel.org/riscv/c/0471415f3fd6
- [08/28] xor: split xor.h
https://git.kernel.org/riscv/c/54e20be48fd4
- [09/28] xor: remove macro abuse for XOR implementation registrations
https://git.kernel.org/riscv/c/35ebc4de1059
- [10/28] xor: move generic implementations out of asm-generic/xor.h
https://git.kernel.org/riscv/c/c46928fdcfa0
- [11/28] alpha: move the XOR code to lib/raid/
https://git.kernel.org/riscv/c/503793b1340e
- [12/28] arm: move the XOR code to lib/raid/
https://git.kernel.org/riscv/c/0d64a24ec0c0
- [13/28] arm64: move the XOR code to lib/raid/
https://git.kernel.org/riscv/c/3786f2ad0095
- [14/28] loongarch: move the XOR code to lib/raid/
https://git.kernel.org/riscv/c/033bee3e4963
- [15/28] powerpc: move the XOR code to lib/raid/
https://git.kernel.org/riscv/c/3f276cece4dd
- [16/28] riscv: move the XOR code to lib/raid/
https://git.kernel.org/riscv/c/5265d55b2146
- [17/28] sparc: move the XOR code to lib/raid/
https://git.kernel.org/riscv/c/7f96362396ee
- [18/28] s390: move the XOR code to lib/raid/
https://git.kernel.org/riscv/c/95c104cc5571
- [19/28] x86: move the XOR code to lib/raid/
https://git.kernel.org/riscv/c/77fd47e57a09
- [20/28] xor: avoid indirect calls for arm64-optimized ops
https://git.kernel.org/riscv/c/352ebd066b62
- [21/28] xor: make xor.ko self-contained in lib/raid/
https://git.kernel.org/riscv/c/e20043b4765c
- [22/28] xor: add a better public API
(no matching commit)
- [23/28] xor: add a better public API
(no matching commit)
- [24/28] async_xor: use xor_gen
https://git.kernel.org/riscv/c/7c12c32b9f73
- [25/28] btrfs: use xor_gen
https://git.kernel.org/riscv/c/0f629e7283ad
- [26/28] xor: pass the entire operation to the low-level ops
https://git.kernel.org/riscv/c/80dcf0a7832a
- [27/28] xor: use static_call for xor_gen
https://git.kernel.org/riscv/c/a21921dd02d3
- [28/28] xor: add a kunit test case
https://git.kernel.org/riscv/c/af53e85ef797
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply
* Re: [PATCH] md/raid10: wire llbitmap reshape lifecycle
From: kernel test robot @ 2026-04-30 2:37 UTC (permalink / raw)
To: Yu Kuai, linux-raid
Cc: llvm, oe-kbuild-all, linux-kernel, Li Nan, Yu Kuai, Cheng Cheng
In-Reply-To: <20260419030942.824195-15-yukuai@fnnas.com>
Hi Yu,
kernel test robot noticed the following build errors:
[auto build test ERROR on linus/master]
[also build test ERROR on song-md/md-next v7.1-rc1 next-20260429]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Yu-Kuai/md-raid10-wire-llbitmap-reshape-lifecycle/20260423-170302
base: linus/master
patch link: https://lore.kernel.org/r/20260419030942.824195-15-yukuai%40fnnas.com
patch subject: [PATCH] md/raid10: wire llbitmap reshape lifecycle
config: um-randconfig-001-20260430 (https://download.01.org/0day-ci/archive/20260430/202604301028.uutGNSgD-lkp@intel.com/config)
compiler: clang version 23.0.0git (https://github.com/llvm/llvm-project 5bac06718f502014fade905512f1d26d578a18f3)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260430/202604301028.uutGNSgD-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202604301028.uutGNSgD-lkp@intel.com/
All errors (new ones prefixed by >>):
>> drivers/md/raid10.c:4370:25: error: no member named 'reshape_can_start' in 'struct bitmap_operations'
4370 | mddev->bitmap_ops->reshape_can_start) {
| ~~~~~~~~~~~~~~~~~ ^
drivers/md/raid10.c:4371:28: error: no member named 'reshape_can_start' in 'struct bitmap_operations'
4371 | ret = mddev->bitmap_ops->reshape_can_start(mddev);
| ~~~~~~~~~~~~~~~~~ ^
>> drivers/md/raid10.c:4689:26: error: no member named 'reshape_mark' in 'struct bitmap_operations'
4689 | mddev->bitmap_ops->reshape_mark &&
| ~~~~~~~~~~~~~~~~~ ^
drivers/md/raid10.c:4691:23: error: no member named 'reshape_mark' in 'struct bitmap_operations'
4691 | mddev->bitmap_ops->reshape_mark(mddev, conf->reshape_safe,
| ~~~~~~~~~~~~~~~~~ ^
drivers/md/raid10.c:4899:25: error: no member named 'reshape_mark' in 'struct bitmap_operations'
4899 | mddev->bitmap_ops->reshape_mark &&
| ~~~~~~~~~~~~~~~~~ ^
drivers/md/raid10.c:4901:22: error: no member named 'reshape_mark' in 'struct bitmap_operations'
4901 | mddev->bitmap_ops->reshape_mark(mddev, conf->reshape_safe,
| ~~~~~~~~~~~~~~~~~ ^
>> drivers/md/raid10.c:5043:37: error: no member named 'reshape_finish' in 'struct bitmap_operations'
5043 | if (llbitmap && mddev->bitmap_ops->reshape_finish)
| ~~~~~~~~~~~~~~~~~ ^
drivers/md/raid10.c:5044:22: error: no member named 'reshape_finish' in 'struct bitmap_operations'
5044 | mddev->bitmap_ops->reshape_finish(mddev);
| ~~~~~~~~~~~~~~~~~ ^
>> drivers/md/raid10.c:5107:3: error: field designator 'bitmap_sync_size' does not refer to any field in type 'struct md_personality'
5107 | .bitmap_sync_size = raid10_bitmap_sync_size,
| ~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>> drivers/md/raid10.c:5108:3: error: field designator 'bitmap_array_sectors' does not refer to any field in type 'struct md_personality'
5108 | .bitmap_array_sectors = raid10_bitmap_sync_size,
| ~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
10 errors generated.
vim +4370 drivers/md/raid10.c
4345
4346 static int raid10_start_reshape(struct mddev *mddev)
4347 {
4348 /* A 'reshape' has been requested. This commits
4349 * the various 'new' fields and sets MD_RECOVER_RESHAPE
4350 * This also checks if there are enough spares and adds them
4351 * to the array.
4352 * We currently require enough spares to make the final
4353 * array non-degraded. We also require that the difference
4354 * between old and new data_offset - on each device - is
4355 * enough that we never risk over-writing.
4356 */
4357
4358 unsigned long before_length, after_length;
4359 sector_t min_offset_diff = 0;
4360 int first = 1;
4361 struct geom new;
4362 struct r10conf *conf = mddev->private;
4363 struct md_rdev *rdev;
4364 int spares = 0;
4365 int ret;
4366
4367 if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery))
4368 return -EBUSY;
4369 if (md_bitmap_enabled(mddev, false) &&
> 4370 mddev->bitmap_ops->reshape_can_start) {
4371 ret = mddev->bitmap_ops->reshape_can_start(mddev);
4372 if (ret)
4373 return ret;
4374 }
4375
4376 if (setup_geo(&new, mddev, geo_start) != conf->copies)
4377 return -EINVAL;
4378
4379 before_length = ((1 << conf->prev.chunk_shift) *
4380 conf->prev.far_copies);
4381 after_length = ((1 << conf->geo.chunk_shift) *
4382 conf->geo.far_copies);
4383
4384 rdev_for_each(rdev, mddev) {
4385 if (!test_bit(In_sync, &rdev->flags)
4386 && !test_bit(Faulty, &rdev->flags))
4387 spares++;
4388 if (rdev->raid_disk >= 0) {
4389 long long diff = (rdev->new_data_offset
4390 - rdev->data_offset);
4391 if (!mddev->reshape_backwards)
4392 diff = -diff;
4393 if (diff < 0)
4394 diff = 0;
4395 if (first || diff < min_offset_diff)
4396 min_offset_diff = diff;
4397 first = 0;
4398 }
4399 }
4400
4401 if (max(before_length, after_length) > min_offset_diff)
4402 return -EINVAL;
4403
4404 if (spares < mddev->delta_disks)
4405 return -EINVAL;
4406
4407 conf->offset_diff = min_offset_diff;
4408 spin_lock_irq(&conf->device_lock);
4409 if (conf->mirrors_new) {
4410 memcpy(conf->mirrors_new, conf->mirrors,
4411 sizeof(struct raid10_info)*conf->prev.raid_disks);
4412 smp_mb();
4413 kfree(conf->mirrors_old);
4414 conf->mirrors_old = conf->mirrors;
4415 conf->mirrors = conf->mirrors_new;
4416 conf->mirrors_new = NULL;
4417 }
4418 setup_geo(&conf->geo, mddev, geo_start);
4419 smp_mb();
4420 if (mddev->reshape_backwards) {
4421 sector_t size = raid10_size(mddev, 0, 0);
4422 if (size < mddev->array_sectors) {
4423 spin_unlock_irq(&conf->device_lock);
4424 pr_warn("md/raid10:%s: array size must be reduce before number of disks\n",
4425 mdname(mddev));
4426 return -EINVAL;
4427 }
4428 mddev->resync_max_sectors = size;
4429 conf->reshape_progress = size;
4430 } else
4431 conf->reshape_progress = 0;
4432 conf->reshape_safe = conf->reshape_progress;
4433 spin_unlock_irq(&conf->device_lock);
4434
4435 if (mddev->delta_disks && mddev->bitmap) {
4436 struct mdp_superblock_1 *sb = NULL;
4437 sector_t oldsize, newsize;
4438
4439 oldsize = raid10_size(mddev, 0, 0);
4440 newsize = raid10_size(mddev, 0, conf->geo.raid_disks);
4441
4442 if (!mddev_is_clustered(mddev) &&
4443 md_bitmap_enabled(mddev, false)) {
4444 ret = mddev->bitmap_ops->resize(mddev, newsize, 0);
4445 if (ret)
4446 goto abort;
4447 else
4448 goto out;
4449 }
4450
4451 rdev_for_each(rdev, mddev) {
4452 if (rdev->raid_disk > -1 &&
4453 !test_bit(Faulty, &rdev->flags))
4454 sb = page_address(rdev->sb_page);
4455 }
4456
4457 /*
4458 * some node is already performing reshape, and no need to
4459 * call bitmap_ops->resize again since it should be called when
4460 * receiving BITMAP_RESIZE msg
4461 */
4462 if ((sb && (le32_to_cpu(sb->feature_map) &
4463 MD_FEATURE_RESHAPE_ACTIVE)) || (oldsize == newsize))
4464 goto out;
4465
4466 /* cluster can't be setup without bitmap */
4467 ret = mddev->bitmap_ops->resize(mddev, newsize, 0);
4468 if (ret)
4469 goto abort;
4470
4471 ret = mddev->cluster_ops->resize_bitmaps(mddev, newsize, oldsize);
4472 if (ret) {
4473 mddev->bitmap_ops->resize(mddev, oldsize, 0);
4474 goto abort;
4475 }
4476 }
4477 out:
4478 if (mddev->delta_disks > 0) {
4479 rdev_for_each(rdev, mddev)
4480 if (rdev->raid_disk < 0 &&
4481 !test_bit(Faulty, &rdev->flags)) {
4482 if (raid10_add_disk(mddev, rdev) == 0) {
4483 if (rdev->raid_disk >=
4484 conf->prev.raid_disks)
4485 set_bit(In_sync, &rdev->flags);
4486 else
4487 rdev->recovery_offset = 0;
4488
4489 /* Failure here is OK */
4490 sysfs_link_rdev(mddev, rdev);
4491 }
4492 } else if (rdev->raid_disk >= conf->prev.raid_disks
4493 && !test_bit(Faulty, &rdev->flags)) {
4494 /* This is a spare that was manually added */
4495 set_bit(In_sync, &rdev->flags);
4496 }
4497 }
4498 /* When a reshape changes the number of devices,
4499 * ->degraded is measured against the larger of the
4500 * pre and post numbers.
4501 */
4502 spin_lock_irq(&conf->device_lock);
4503 mddev->degraded = calc_degraded(conf);
4504 spin_unlock_irq(&conf->device_lock);
4505 mddev->raid_disks = conf->geo.raid_disks;
4506 mddev->reshape_position = conf->reshape_progress;
4507 set_bit(MD_SB_CHANGE_DEVS, &mddev->sb_flags);
4508
4509 clear_bit(MD_RECOVERY_SYNC, &mddev->recovery);
4510 clear_bit(MD_RECOVERY_CHECK, &mddev->recovery);
4511 clear_bit(MD_RECOVERY_DONE, &mddev->recovery);
4512 set_bit(MD_RECOVERY_RESHAPE, &mddev->recovery);
4513 set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
4514 conf->reshape_checkpoint = jiffies;
4515 md_new_event();
4516 return 0;
4517
4518 abort:
4519 mddev->recovery = 0;
4520 spin_lock_irq(&conf->device_lock);
4521 conf->geo = conf->prev;
4522 mddev->raid_disks = conf->geo.raid_disks;
4523 rdev_for_each(rdev, mddev)
4524 rdev->new_data_offset = rdev->data_offset;
4525 smp_wmb();
4526 conf->reshape_progress = MaxSector;
4527 conf->reshape_safe = MaxSector;
4528 mddev->reshape_position = MaxSector;
4529 spin_unlock_irq(&conf->device_lock);
4530 return ret;
4531 }
4532
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply
* Re: [PATCH v3 8/8] md/raid10: fix IO error at logical block size granularity
From: Xiao Ni @ 2026-04-30 2:23 UTC (permalink / raw)
To: linan666; +Cc: song, yukuai, linux-raid, linux-kernel, yangerkun, yi.zhang
In-Reply-To: <20260416033801.786415-9-linan666@huaweicloud.com>
On Thu, Apr 16, 2026 at 11:51 AM <linan666@huaweicloud.com> wrote:
>
> From: Li Nan <linan122@huawei.com>
>
> RAID10 currently fixes IO error at PAGE_SIZE granularity. Fix at smaller
> granularity can handle more errors, and RAID will support logical block
> sizes larger than PAGE_SIZE in the future, where PAGE_SIZE IO will fail.
>
> Switch IO error fix granularity to logical block size.
>
> Signed-off-by: Li Nan <linan122@huawei.com>
> Reviewed-by: Yu Kuai <yukuai@fnnas.com>
> ---
> drivers/md/raid10.c | 17 ++++-------------
> 1 file changed, 4 insertions(+), 13 deletions(-)
>
> diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
> index 3638e00fe420..5b4ffd23211a 100644
> --- a/drivers/md/raid10.c
> +++ b/drivers/md/raid10.c
> @@ -2454,7 +2454,7 @@ static void sync_request_write(struct mddev *mddev, struct r10bio *r10_bio)
> static void fix_recovery_read_error(struct r10bio *r10_bio)
> {
> /* We got a read error during recovery.
> - * We repeat the read in smaller page-sized sections.
> + * We repeat the read in smaller logical_block_sized sections.
> * If a read succeeds, write it to the new device or record
> * a bad block if we cannot.
> * If a read fails, record a bad block on both old and
> @@ -2470,14 +2470,11 @@ static void fix_recovery_read_error(struct r10bio *r10_bio)
> struct folio *folio = get_resync_folio(bio)->folio;
>
> while (sectors) {
> - int s = sectors;
> + int s = min_t(int, sectors, mddev->logical_block_size >> 9);
> struct md_rdev *rdev;
> sector_t addr;
> int ok;
>
> - if (s > (PAGE_SIZE>>9))
> - s = PAGE_SIZE >> 9;
> -
> rdev = conf->mirrors[dr].rdev;
> addr = r10_bio->devs[0].addr + sect;
> ok = sync_folio_io(rdev,
> @@ -2621,14 +2618,11 @@ static void fix_read_error(struct r10conf *conf, struct mddev *mddev, struct r10
> }
>
> while(sectors) {
> - int s = sectors;
> + int s = min_t(int, sectors, mddev->logical_block_size >> 9);
> int sl = slot;
> int success = 0;
> int start;
>
> - if (s > (PAGE_SIZE>>9))
> - s = PAGE_SIZE >> 9;
> -
> do {
> d = r10_bio->devs[sl].devnum;
> rdev = conf->mirrors[d].rdev;
> @@ -4926,13 +4920,10 @@ static int handle_reshape_read_error(struct mddev *mddev,
> __raid10_find_phys(&conf->prev, r10b);
>
> while (sectors) {
> - int s = sectors;
> + int s = min_t(int, sectors, mddev->logical_block_size >> 9);
> int success = 0;
> int first_slot = slot;
>
> - if (s > (PAGE_SIZE >> 9))
> - s = PAGE_SIZE >> 9;
> -
> while (!success) {
> int d = r10b->devs[slot].devnum;
> struct md_rdev *rdev = conf->mirrors[d].rdev;
> --
> 2.39.2
>
>
This patch looks good to me.
Reviewed-by: Xiao Ni <xni@redhat.com>
^ permalink raw reply
* Re: [PATCH v3 7/8] md/raid1: fix IO error at logical block size granularity
From: Xiao Ni @ 2026-04-30 2:22 UTC (permalink / raw)
To: linan666; +Cc: song, yukuai, linux-raid, linux-kernel, yangerkun, yi.zhang
In-Reply-To: <20260416033801.786415-8-linan666@huaweicloud.com>
On Thu, Apr 16, 2026 at 11:55 AM <linan666@huaweicloud.com> wrote:
>
> From: Li Nan <linan122@huawei.com>
>
> RAID1 currently fixes IO error at PAGE_SIZE granularity. Fix at smaller
> granularity can handle more errors, and RAID will support logical block
> sizes larger than PAGE_SIZE in the future, where PAGE_SIZE IO will fail.
>
> Switch IO error fix granularity to logical block size.
>
> Signed-off-by: Li Nan <linan122@huawei.com>
> Reviewed-by: Yu Kuai <yukuai@fnnas.com>
> ---
> drivers/md/raid1.c | 13 ++++---------
> 1 file changed, 4 insertions(+), 9 deletions(-)
>
> diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
> index 724fd4f2cc3a..de8c964ca11d 100644
> --- a/drivers/md/raid1.c
> +++ b/drivers/md/raid1.c
> @@ -2116,7 +2116,7 @@ static int fix_sync_read_error(struct r1bio *r1_bio)
> {
> /* Try some synchronous reads of other devices to get
> * good data, much like with normal read errors. Only
> - * read into the pages we already have so we don't
> + * read into the block we already have so we don't
> * need to re-issue the read request.
> * We don't need to freeze the array, because being in an
> * active sync request, there is no normal IO, and
> @@ -2147,13 +2147,11 @@ static int fix_sync_read_error(struct r1bio *r1_bio)
> }
>
> while(sectors) {
> - int s = sectors;
> + int s = min_t(int, sectors, mddev->logical_block_size >> 9);
> int d = r1_bio->read_disk;
> int success = 0;
> int start;
>
> - if (s > (PAGE_SIZE>>9))
> - s = PAGE_SIZE >> 9;
> do {
> if (r1_bio->bios[d]->bi_end_io == end_sync_read) {
> /* No rcu protection needed here devices
> @@ -2192,7 +2190,7 @@ static int fix_sync_read_error(struct r1bio *r1_bio)
> if (abort)
> return 0;
>
> - /* Try next page */
> + /* Try next block */
> sectors -= s;
> sect += s;
> off += s << 9;
> @@ -2390,14 +2388,11 @@ static void fix_read_error(struct r1conf *conf, struct r1bio *r1_bio)
> }
>
> while(sectors) {
> - int s = sectors;
> + int s = min_t(int, sectors, mddev->logical_block_size >> 9);
> int d = read_disk;
> int success = 0;
> int start;
>
> - if (s > (PAGE_SIZE>>9))
> - s = PAGE_SIZE >> 9;
> -
> do {
> rdev = conf->mirrors[d].rdev;
> if (rdev &&
> --
> 2.39.2
>
>
This patch looks good to me.
Reviewed-by: Xiao Ni <xni@redhat.com>
^ permalink raw reply
* Re: [PATCH v3 6/8] md/raid1,raid10: use folio for sync path IO
From: Xiao Ni @ 2026-04-30 1:54 UTC (permalink / raw)
To: linan666; +Cc: song, yukuai, linux-raid, linux-kernel, yangerkun, yi.zhang
In-Reply-To: <20260416033801.786415-7-linan666@huaweicloud.com>
Hi Nan
On Thu, Apr 16, 2026 at 11:55 AM <linan666@huaweicloud.com> wrote:
>
> From: Li Nan <linan122@huawei.com>
>
> Convert all IO on the sync path to use folios, and rename page-related
> identifiers to match folio.
>
> Since RESYNC_BLOCK_SIZE (64K) has higher allocation failure chance than 4k,
> retry with lower orders to improve allocation reliability. A r1/10_bio may
> have different rf->folio orders, so use minimum order as r1/10_bio sectors
> to prevent exceeding size when adding folio to IO later.
>
> Clean up:
> 1. Remove resync_get_all_folio() and invoke folio_get() directly instead.
> 2. Clean up redundant while(0) loop in md_bio_reset_resync_folio().
> 3. Clean up bio variable by directly referencing r10_bio->devs[j].bio
> instead in r1buf_pool_alloc() and r10buf_pool_alloc().
> 4. Clean up RESYNC_PAGES.
> 5. Remove resync_fetch_folio(), access 'rf->folio' directly.
> 6. Remove resync_free_folio(), call folio_put() directly.
> 7. clean up sync IO size calculation in raid1/10_sync_request.
>
> Signed-off-by: Li Nan <linan122@huawei.com>
> ---
> drivers/md/md.c | 2 +-
> drivers/md/raid1-10.c | 80 ++++---------
> drivers/md/raid1.c | 209 +++++++++++++++-------------------
> drivers/md/raid10.c | 254 +++++++++++++++++++++---------------------
> 4 files changed, 240 insertions(+), 305 deletions(-)
>
> diff --git a/drivers/md/md.c b/drivers/md/md.c
> index 5e83914d5c14..6554b849ac74 100644
> --- a/drivers/md/md.c
> +++ b/drivers/md/md.c
> @@ -9440,7 +9440,7 @@ static bool sync_io_within_limit(struct mddev *mddev)
> {
> /*
> * For raid456, sync IO is stripe(4k) per IO, for other levels, it's
> - * RESYNC_PAGES(64k) per IO.
> + * RESYNC_BLOCK_SIZE(64k) per IO.
> */
> return atomic_read(&mddev->recovery_active) <
> (raid_is_456(mddev) ? 8 : 128) * sync_io_depth(mddev);
> diff --git a/drivers/md/raid1-10.c b/drivers/md/raid1-10.c
> index cda531d0720b..10200b0a3fd2 100644
> --- a/drivers/md/raid1-10.c
> +++ b/drivers/md/raid1-10.c
> @@ -1,7 +1,6 @@
> // SPDX-License-Identifier: GPL-2.0
> /* Maximum size of each resync request */
> #define RESYNC_BLOCK_SIZE (64*1024)
> -#define RESYNC_PAGES ((RESYNC_BLOCK_SIZE + PAGE_SIZE-1) / PAGE_SIZE)
> #define RESYNC_SECTORS (RESYNC_BLOCK_SIZE >> 9)
>
> /* when we get a read error on a read-only array, we redirect to another
> @@ -20,9 +19,9 @@
> #define MAX_PLUG_BIO 32
>
> /* for managing resync I/O pages */
> -struct resync_pages {
> +struct resync_folio {
> void *raid_bio;
> - struct page *pages[RESYNC_PAGES];
> + struct folio *folio;
> };
>
> struct raid1_plug_cb {
> @@ -36,77 +35,44 @@ static void rbio_pool_free(void *rbio, void *data)
> kfree(rbio);
> }
>
> -static inline int resync_alloc_pages(struct resync_pages *rp,
> - gfp_t gfp_flags)
> +static inline int resync_alloc_folio(struct resync_folio *rf,
> + gfp_t gfp_flags, int *order)
> {
> - int i;
> + struct folio *folio;
>
> - for (i = 0; i < RESYNC_PAGES; i++) {
> - rp->pages[i] = alloc_page(gfp_flags);
> - if (!rp->pages[i])
> - goto out_free;
> - }
> + do {
> + folio = folio_alloc(gfp_flags, *order);
> + if (folio)
> + break;
> + } while (--(*order) > 0);
It has a problem here. If it can't allocate a big page, the sync
request unit will be smaller and sync performance may decrease. This
can happen when the system lacks sufficient continuous memory. This
change looks good to me. I just want to throw this problem out for an
open discussion.
>
> + if (!folio)
> + return -ENOMEM;
> +
> + rf->folio = folio;
> return 0;
> -
> -out_free:
> - while (--i >= 0)
> - put_page(rp->pages[i]);
> - return -ENOMEM;
> -}
> -
> -static inline void resync_free_pages(struct resync_pages *rp)
> -{
> - int i;
> -
> - for (i = 0; i < RESYNC_PAGES; i++)
> - put_page(rp->pages[i]);
> -}
> -
> -static inline void resync_get_all_pages(struct resync_pages *rp)
> -{
> - int i;
> -
> - for (i = 0; i < RESYNC_PAGES; i++)
> - get_page(rp->pages[i]);
> -}
> -
> -static inline struct page *resync_fetch_page(struct resync_pages *rp,
> - unsigned idx)
> -{
> - if (WARN_ON_ONCE(idx >= RESYNC_PAGES))
> - return NULL;
> - return rp->pages[idx];
> }
>
> /*
> - * 'strct resync_pages' stores actual pages used for doing the resync
> + * 'strct resync_folio' stores actual pages used for doing the resync
> * IO, and it is per-bio, so make .bi_private points to it.
> */
> -static inline struct resync_pages *get_resync_pages(struct bio *bio)
> +static inline struct resync_folio *get_resync_folio(struct bio *bio)
> {
> return bio->bi_private;
> }
>
> /* generally called after bio_reset() for reseting bvec */
> -static void md_bio_reset_resync_pages(struct bio *bio, struct resync_pages *rp,
> +static void md_bio_reset_resync_folio(struct bio *bio, struct resync_folio *rf,
> int size)
> {
> - int idx = 0;
> -
> /* initialize bvec table again */
> - do {
> - struct page *page = resync_fetch_page(rp, idx);
> - int len = min_t(int, size, PAGE_SIZE);
> -
> - if (WARN_ON(!bio_add_page(bio, page, len, 0))) {
> - bio->bi_status = BLK_STS_RESOURCE;
> - bio_endio(bio);
> - return;
> - }
> -
> - size -= len;
> - } while (idx++ < RESYNC_PAGES && size > 0);
> + if (WARN_ON(!bio_add_folio(bio, rf->folio,
> + min_t(int, size, RESYNC_BLOCK_SIZE),
> + 0))) {
> + bio->bi_status = BLK_STS_RESOURCE;
> + bio_endio(bio);
> + }
> }
>
>
> diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
> index a72abdc37a2d..724fd4f2cc3a 100644
> --- a/drivers/md/raid1.c
> +++ b/drivers/md/raid1.c
> @@ -120,11 +120,11 @@ static void remove_serial(struct md_rdev *rdev, sector_t lo, sector_t hi)
>
> /*
> * for resync bio, r1bio pointer can be retrieved from the per-bio
> - * 'struct resync_pages'.
> + * 'struct resync_folio'.
> */
> static inline struct r1bio *get_resync_r1bio(struct bio *bio)
> {
> - return get_resync_pages(bio)->raid_bio;
> + return get_resync_folio(bio)->raid_bio;
> }
>
> static void *r1bio_pool_alloc(gfp_t gfp_flags, struct r1conf *conf)
> @@ -146,70 +146,69 @@ static void * r1buf_pool_alloc(gfp_t gfp_flags, void *data)
> struct r1conf *conf = data;
> struct r1bio *r1_bio;
> struct bio *bio;
> - int need_pages;
> + int need_folio;
The name need_folio is confusing. Can we keep the same style as the
old version? How about need_folios?
> int j;
> - struct resync_pages *rps;
> + struct resync_folio *rfs;
> + int order = get_order(RESYNC_BLOCK_SIZE);
>
> r1_bio = r1bio_pool_alloc(gfp_flags, conf);
> if (!r1_bio)
> return NULL;
>
> - rps = kmalloc_array(conf->raid_disks * 2, sizeof(struct resync_pages),
> + rfs = kmalloc_array(conf->raid_disks * 2, sizeof(struct resync_folio),
> gfp_flags);
> - if (!rps)
> + if (!rfs)
> goto out_free_r1bio;
>
> /*
> * Allocate bios : 1 for reading, n-1 for writing
> */
> for (j = conf->raid_disks * 2; j-- ; ) {
> - bio = bio_kmalloc(RESYNC_PAGES, gfp_flags);
> + bio = bio_kmalloc(1, gfp_flags);
> if (!bio)
> goto out_free_bio;
> - bio_init_inline(bio, NULL, RESYNC_PAGES, 0);
> + bio_init_inline(bio, NULL, 1, 0);
> r1_bio->bios[j] = bio;
> }
> /*
> - * Allocate RESYNC_PAGES data pages and attach them to
> - * the first bio.
> + * Allocate data folio and attach it to the first bio.
> * If this is a user-requested check/repair, allocate
> - * RESYNC_PAGES for each bio.
> + * folio for each bio.
> */
> if (test_bit(MD_RECOVERY_REQUESTED, &conf->mddev->recovery))
> - need_pages = conf->raid_disks * 2;
> + need_folio = conf->raid_disks * 2;
> else
> - need_pages = 1;
> + need_folio = 1;
> for (j = 0; j < conf->raid_disks * 2; j++) {
> - struct resync_pages *rp = &rps[j];
> + struct resync_folio *rf = &rfs[j];
>
> - bio = r1_bio->bios[j];
> -
> - if (j < need_pages) {
> - if (resync_alloc_pages(rp, gfp_flags))
> - goto out_free_pages;
> + if (j < need_folio) {
> + if (resync_alloc_folio(rf, gfp_flags, &order))
> + goto out_free_folio;
> } else {
> - memcpy(rp, &rps[0], sizeof(*rp));
> - resync_get_all_pages(rp);
> + memcpy(rf, &rfs[0], sizeof(*rf));
> + folio_get(rf->folio);
> }
>
> - rp->raid_bio = r1_bio;
> - bio->bi_private = rp;
> + rf->raid_bio = r1_bio;
> + r1_bio->bios[j]->bi_private = rf;
> }
>
> + r1_bio->sectors = 1 << (order + PAGE_SECTORS_SHIFT);
> r1_bio->master_bio = NULL;
>
> return r1_bio;
>
> -out_free_pages:
> +out_free_folio:
> while (--j >= 0)
> - resync_free_pages(&rps[j]);
> + folio_put(rfs[j].folio);
>
> out_free_bio:
> while (++j < conf->raid_disks * 2) {
> bio_uninit(r1_bio->bios[j]);
> kfree(r1_bio->bios[j]);
> }
> - kfree(rps);
> + kfree(rfs);
>
> out_free_r1bio:
> rbio_pool_free(r1_bio, data);
> @@ -221,17 +220,17 @@ static void r1buf_pool_free(void *__r1_bio, void *data)
> struct r1conf *conf = data;
> int i;
> struct r1bio *r1bio = __r1_bio;
> - struct resync_pages *rp = NULL;
> + struct resync_folio *rf = NULL;
>
> for (i = conf->raid_disks * 2; i--; ) {
> - rp = get_resync_pages(r1bio->bios[i]);
> - resync_free_pages(rp);
> + rf = get_resync_folio(r1bio->bios[i]);
> + folio_put(rf->folio);
> bio_uninit(r1bio->bios[i]);
> kfree(r1bio->bios[i]);
> }
>
> - /* resync pages array stored in the 1st bio's .bi_private */
> - kfree(rp);
> + /* resync folio stored in the 1st bio's .bi_private */
> + kfree(rf);
>
> rbio_pool_free(r1bio, data);
> }
> @@ -2095,10 +2094,10 @@ static void end_sync_write(struct bio *bio)
> put_sync_write_buf(r1_bio);
> }
>
> -static int r1_sync_page_io(struct md_rdev *rdev, sector_t sector,
> - int sectors, struct page *page, blk_opf_t rw)
> +static int r1_sync_folio_io(struct md_rdev *rdev, sector_t sector, int sectors,
> + int off, struct folio *folio, blk_opf_t rw)
> {
> - if (sync_page_io(rdev, sector, sectors << 9, page, rw, false))
> + if (sync_folio_io(rdev, sector, sectors << 9, off, folio, rw, false))
> /* success */
> return 1;
> if (rw == REQ_OP_WRITE) {
> @@ -2129,10 +2128,10 @@ static int fix_sync_read_error(struct r1bio *r1_bio)
> struct mddev *mddev = r1_bio->mddev;
> struct r1conf *conf = mddev->private;
> struct bio *bio = r1_bio->bios[r1_bio->read_disk];
> - struct page **pages = get_resync_pages(bio)->pages;
> + struct folio *folio = get_resync_folio(bio)->folio;
> sector_t sect = r1_bio->sector;
> int sectors = r1_bio->sectors;
> - int idx = 0;
> + int off = 0;
> struct md_rdev *rdev;
>
> rdev = conf->mirrors[r1_bio->read_disk].rdev;
> @@ -2162,9 +2161,8 @@ static int fix_sync_read_error(struct r1bio *r1_bio)
> * active, and resync is currently active
> */
> rdev = conf->mirrors[d].rdev;
> - if (sync_page_io(rdev, sect, s<<9,
> - pages[idx],
> - REQ_OP_READ, false)) {
> + if (sync_folio_io(rdev, sect, s<<9, off, folio,
> + REQ_OP_READ, false)) {
> success = 1;
> break;
> }
> @@ -2197,7 +2195,7 @@ static int fix_sync_read_error(struct r1bio *r1_bio)
> /* Try next page */
> sectors -= s;
> sect += s;
> - idx++;
> + off += s << 9;
> continue;
> }
>
> @@ -2210,8 +2208,7 @@ static int fix_sync_read_error(struct r1bio *r1_bio)
> if (r1_bio->bios[d]->bi_end_io != end_sync_read)
> continue;
> rdev = conf->mirrors[d].rdev;
> - if (r1_sync_page_io(rdev, sect, s,
> - pages[idx],
> + if (r1_sync_folio_io(rdev, sect, s, off, folio,
> REQ_OP_WRITE) == 0) {
> r1_bio->bios[d]->bi_end_io = NULL;
> rdev_dec_pending(rdev, mddev);
> @@ -2225,14 +2222,13 @@ static int fix_sync_read_error(struct r1bio *r1_bio)
> if (r1_bio->bios[d]->bi_end_io != end_sync_read)
> continue;
> rdev = conf->mirrors[d].rdev;
> - if (r1_sync_page_io(rdev, sect, s,
> - pages[idx],
> + if (r1_sync_folio_io(rdev, sect, s, off, folio,
> REQ_OP_READ) != 0)
> atomic_add(s, &rdev->corrected_errors);
> }
> sectors -= s;
> sect += s;
> - idx ++;
> + off += s << 9;
> }
> set_bit(R1BIO_Uptodate, &r1_bio->state);
> bio->bi_status = 0;
> @@ -2252,14 +2248,12 @@ static void process_checks(struct r1bio *r1_bio)
> struct r1conf *conf = mddev->private;
> int primary;
> int i;
> - int vcnt;
>
> /* Fix variable parts of all bios */
> - vcnt = (r1_bio->sectors + PAGE_SIZE / 512 - 1) >> (PAGE_SHIFT - 9);
> for (i = 0; i < conf->raid_disks * 2; i++) {
> blk_status_t status;
> struct bio *b = r1_bio->bios[i];
> - struct resync_pages *rp = get_resync_pages(b);
> + struct resync_folio *rf = get_resync_folio(b);
> if (b->bi_end_io != end_sync_read)
> continue;
> /* fixup the bio for reuse, but preserve errno */
> @@ -2269,11 +2263,11 @@ static void process_checks(struct r1bio *r1_bio)
> b->bi_iter.bi_sector = r1_bio->sector +
> conf->mirrors[i].rdev->data_offset;
> b->bi_end_io = end_sync_read;
> - rp->raid_bio = r1_bio;
> - b->bi_private = rp;
> + rf->raid_bio = r1_bio;
> + b->bi_private = rf;
>
> /* initialize bvec table again */
> - md_bio_reset_resync_pages(b, rp, r1_bio->sectors << 9);
> + md_bio_reset_resync_folio(b, rf, r1_bio->sectors << 9);
> }
> for (primary = 0; primary < conf->raid_disks * 2; primary++)
> if (r1_bio->bios[primary]->bi_end_io == end_sync_read &&
> @@ -2284,44 +2278,39 @@ static void process_checks(struct r1bio *r1_bio)
> }
> r1_bio->read_disk = primary;
> for (i = 0; i < conf->raid_disks * 2; i++) {
> - int j = 0;
> struct bio *pbio = r1_bio->bios[primary];
> struct bio *sbio = r1_bio->bios[i];
> blk_status_t status = sbio->bi_status;
> - struct page **ppages = get_resync_pages(pbio)->pages;
> - struct page **spages = get_resync_pages(sbio)->pages;
> - struct bio_vec *bi;
> - int page_len[RESYNC_PAGES] = { 0 };
> - struct bvec_iter_all iter_all;
> + struct folio *pfolio = get_resync_folio(pbio)->folio;
> + struct folio *sfolio = get_resync_folio(sbio)->folio;
>
> if (sbio->bi_end_io != end_sync_read)
> continue;
> /* Now we can 'fixup' the error value */
> sbio->bi_status = 0;
>
> - bio_for_each_segment_all(bi, sbio, iter_all)
> - page_len[j++] = bi->bv_len;
> -
> - if (!status) {
> - for (j = vcnt; j-- ; ) {
> - if (memcmp(page_address(ppages[j]),
> - page_address(spages[j]),
> - page_len[j]))
> - break;
> - }
> - } else
> - j = 0;
> - if (j >= 0)
> + /*
> + * Copy data and submit write in two cases:
> + * - IO error (non-zero status)
> + * - Data inconsistency and not a CHECK operation.
> + */
> + if (status) {
> atomic64_add(r1_bio->sectors, &mddev->resync_mismatches);
> - if (j < 0 || (test_bit(MD_RECOVERY_CHECK, &mddev->recovery)
> - && !status)) {
> - /* No need to write to this device. */
> - sbio->bi_end_io = NULL;
> - rdev_dec_pending(conf->mirrors[i].rdev, mddev);
> + bio_copy_data(sbio, pbio);
> continue;
> + } else if (memcmp(folio_address(pfolio),
> + folio_address(sfolio),
> + r1_bio->sectors << 9)) {
> + atomic64_add(r1_bio->sectors, &mddev->resync_mismatches);
> + if (!test_bit(MD_RECOVERY_CHECK, &mddev->recovery)) {
> + bio_copy_data(sbio, pbio);
> + continue;
> + }
> }
>
> - bio_copy_data(sbio, pbio);
> + /* No need to write to this device. */
> + sbio->bi_end_io = NULL;
> + rdev_dec_pending(conf->mirrors[i].rdev, mddev);
> }
> }
>
> @@ -2446,9 +2435,8 @@ static void fix_read_error(struct r1conf *conf, struct r1bio *r1_bio)
> if (rdev &&
> !test_bit(Faulty, &rdev->flags)) {
> atomic_inc(&rdev->nr_pending);
> - r1_sync_page_io(rdev, sect, s,
> - folio_page(conf->tmpfolio, 0),
> - REQ_OP_WRITE);
> + r1_sync_folio_io(rdev, sect, s, 0,
> + conf->tmpfolio, REQ_OP_WRITE);
> rdev_dec_pending(rdev, mddev);
> }
> }
> @@ -2461,9 +2449,8 @@ static void fix_read_error(struct r1conf *conf, struct r1bio *r1_bio)
> if (rdev &&
> !test_bit(Faulty, &rdev->flags)) {
> atomic_inc(&rdev->nr_pending);
> - if (r1_sync_page_io(rdev, sect, s,
> - folio_page(conf->tmpfolio, 0),
> - REQ_OP_READ)) {
> + if (r1_sync_folio_io(rdev, sect, s, 0,
> + conf->tmpfolio, REQ_OP_READ)) {
> atomic_add(s, &rdev->corrected_errors);
> pr_info("md/raid1:%s: read error corrected (%d sectors at %llu on %pg)\n",
> mdname(mddev), s,
> @@ -2738,15 +2725,15 @@ static int init_resync(struct r1conf *conf)
> static struct r1bio *raid1_alloc_init_r1buf(struct r1conf *conf)
> {
> struct r1bio *r1bio = mempool_alloc(&conf->r1buf_pool, GFP_NOIO);
> - struct resync_pages *rps;
> + struct resync_folio *rfs;
> struct bio *bio;
> int i;
>
> for (i = conf->raid_disks * 2; i--; ) {
> bio = r1bio->bios[i];
> - rps = bio->bi_private;
> + rfs = bio->bi_private;
> bio_reset(bio, NULL, 0);
> - bio->bi_private = rps;
> + bio->bi_private = rfs;
> }
> r1bio->master_bio = NULL;
> return r1bio;
> @@ -2775,10 +2762,9 @@ static sector_t raid1_sync_request(struct mddev *mddev, sector_t sector_nr,
> int write_targets = 0, read_targets = 0;
> sector_t sync_blocks;
> bool still_degraded = false;
> - int good_sectors = RESYNC_SECTORS;
> + int good_sectors;
> int min_bad = 0; /* number of sectors that are bad in all devices */
> int idx = sector_to_idx(sector_nr);
> - int page_idx = 0;
>
> if (!mempool_initialized(&conf->r1buf_pool))
> if (init_resync(conf))
> @@ -2858,8 +2844,11 @@ static sector_t raid1_sync_request(struct mddev *mddev, sector_t sector_nr,
> r1_bio->sector = sector_nr;
> r1_bio->state = 0;
> set_bit(R1BIO_IsSync, &r1_bio->state);
> - /* make sure good_sectors won't go across barrier unit boundary */
> - good_sectors = align_to_barrier_unit_end(sector_nr, good_sectors);
> + /*
> + * make sure good_sectors won't go across barrier unit boundary.
> + * r1_bio->sectors <= RESYNC_SECTORS.
> + */
> + good_sectors = align_to_barrier_unit_end(sector_nr, r1_bio->sectors);
>
> for (i = 0; i < conf->raid_disks * 2; i++) {
> struct md_rdev *rdev;
> @@ -2979,44 +2968,28 @@ static sector_t raid1_sync_request(struct mddev *mddev, sector_t sector_nr,
> max_sector = mddev->resync_max; /* Don't do IO beyond here */
> if (max_sector > sector_nr + good_sectors)
> max_sector = sector_nr + good_sectors;
> - nr_sectors = 0;
> - sync_blocks = 0;
> do {
> - struct page *page;
> - int len = PAGE_SIZE;
> - if (sector_nr + (len>>9) > max_sector)
> - len = (max_sector - sector_nr) << 9;
> - if (len == 0)
> + nr_sectors = max_sector - sector_nr;
> + if (nr_sectors == 0)
> break;
> - if (sync_blocks == 0) {
> - if (!md_bitmap_start_sync(mddev, sector_nr,
> - &sync_blocks, still_degraded) &&
> - !conf->fullsync &&
> - !test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery))
> - break;
> - if ((len >> 9) > sync_blocks)
> - len = sync_blocks<<9;
> - }
> + if (!md_bitmap_start_sync(mddev, sector_nr,
> + &sync_blocks, still_degraded) &&
> + !conf->fullsync &&
> + !test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery))
> + break;
> + if (nr_sectors > sync_blocks)
> + nr_sectors = sync_blocks;
>
> for (i = 0 ; i < conf->raid_disks * 2; i++) {
> - struct resync_pages *rp;
> -
> bio = r1_bio->bios[i];
> - rp = get_resync_pages(bio);
> if (bio->bi_end_io) {
> - page = resync_fetch_page(rp, page_idx);
> + struct resync_folio *rf = get_resync_folio(bio);
>
> - /*
> - * won't fail because the vec table is big
> - * enough to hold all these pages
> - */
> - __bio_add_page(bio, page, len, 0);
> + bio_add_folio_nofail(bio, rf->folio, nr_sectors << 9, 0);
> }
> }
> - nr_sectors += len>>9;
> - sector_nr += len>>9;
> - sync_blocks -= (len>>9);
> - } while (++page_idx < RESYNC_PAGES);
> + sector_nr += nr_sectors;
> + } while (0);
Now it can handle all pages in one go via a folio. It's strange to
keep while(0) here.
>
> r1_bio->sectors = nr_sectors;
This patch is a little big. Is it better to split this patch here?
>
> diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
> index 26f93040cd13..3638e00fe420 100644
> --- a/drivers/md/raid10.c
> +++ b/drivers/md/raid10.c
> @@ -96,11 +96,11 @@ static void end_reshape(struct r10conf *conf);
>
> /*
> * for resync bio, r10bio pointer can be retrieved from the per-bio
> - * 'struct resync_pages'.
> + * 'struct resync_folio'.
> */
> static inline struct r10bio *get_resync_r10bio(struct bio *bio)
> {
> - return get_resync_pages(bio)->raid_bio;
> + return get_resync_folio(bio)->raid_bio;
> }
>
> static void * r10bio_pool_alloc(gfp_t gfp_flags, void *data)
> @@ -133,8 +133,9 @@ static void * r10buf_pool_alloc(gfp_t gfp_flags, void *data)
> struct r10bio *r10_bio;
> struct bio *bio;
> int j;
> - int nalloc, nalloc_rp;
> - struct resync_pages *rps;
> + int nalloc, nalloc_rf;
> + struct resync_folio *rfs;
> + int order = get_order(RESYNC_BLOCK_SIZE);
>
> r10_bio = r10bio_pool_alloc(gfp_flags, conf);
> if (!r10_bio)
> @@ -148,66 +149,64 @@ static void * r10buf_pool_alloc(gfp_t gfp_flags, void *data)
>
> /* allocate once for all bios */
> if (!conf->have_replacement)
> - nalloc_rp = nalloc;
> + nalloc_rf = nalloc;
> else
> - nalloc_rp = nalloc * 2;
> - rps = kmalloc_array(nalloc_rp, sizeof(struct resync_pages), gfp_flags);
> - if (!rps)
> + nalloc_rf = nalloc * 2;
> + rfs = kmalloc_array(nalloc_rf, sizeof(struct resync_folio), gfp_flags);
> + if (!rfs)
> goto out_free_r10bio;
>
> /*
> * Allocate bios.
> */
> for (j = nalloc ; j-- ; ) {
> - bio = bio_kmalloc(RESYNC_PAGES, gfp_flags);
> + bio = bio_kmalloc(1, gfp_flags);
> if (!bio)
> goto out_free_bio;
> - bio_init_inline(bio, NULL, RESYNC_PAGES, 0);
> + bio_init_inline(bio, NULL, 1, 0);
> r10_bio->devs[j].bio = bio;
> if (!conf->have_replacement)
> continue;
> - bio = bio_kmalloc(RESYNC_PAGES, gfp_flags);
> + bio = bio_kmalloc(1, gfp_flags);
> if (!bio)
> goto out_free_bio;
> - bio_init_inline(bio, NULL, RESYNC_PAGES, 0);
> + bio_init_inline(bio, NULL, 1, 0);
> r10_bio->devs[j].repl_bio = bio;
> }
> /*
> - * Allocate RESYNC_PAGES data pages and attach them
> - * where needed.
> + * Allocate data folio and attach it where needed.
> */
> for (j = 0; j < nalloc; j++) {
> struct bio *rbio = r10_bio->devs[j].repl_bio;
> - struct resync_pages *rp, *rp_repl;
> + struct resync_folio *rf, *rf_repl;
>
> - rp = &rps[j];
> + rf = &rfs[j];
> if (rbio)
> - rp_repl = &rps[nalloc + j];
> -
> - bio = r10_bio->devs[j].bio;
> + rf_repl = &rfs[nalloc + j];
>
> if (!j || test_bit(MD_RECOVERY_SYNC,
> &conf->mddev->recovery)) {
> - if (resync_alloc_pages(rp, gfp_flags))
> - goto out_free_pages;
> + if (resync_alloc_folio(rf, gfp_flags, &order))
> + goto out_free_folio;
> } else {
> - memcpy(rp, &rps[0], sizeof(*rp));
> - resync_get_all_pages(rp);
> + memcpy(rf, &rfs[0], sizeof(*rf));
> + folio_get(rf->folio);
> }
>
> - rp->raid_bio = r10_bio;
> - bio->bi_private = rp;
> + rf->raid_bio = r10_bio;
> + r10_bio->devs[j].bio->bi_private = rf;
> if (rbio) {
> - memcpy(rp_repl, rp, sizeof(*rp));
> - rbio->bi_private = rp_repl;
> + memcpy(rf_repl, rf, sizeof(*rf));
> + rbio->bi_private = rf_repl;
> }
> }
>
> + r10_bio->sectors = 1 << (order + PAGE_SECTORS_SHIFT);
> return r10_bio;
>
> -out_free_pages:
> +out_free_folio:
> while (--j >= 0)
> - resync_free_pages(&rps[j]);
> + folio_put(rfs[j].folio);
>
> j = 0;
> out_free_bio:
> @@ -219,7 +218,7 @@ static void * r10buf_pool_alloc(gfp_t gfp_flags, void *data)
> bio_uninit(r10_bio->devs[j].repl_bio);
> kfree(r10_bio->devs[j].repl_bio);
> }
> - kfree(rps);
> + kfree(rfs);
> out_free_r10bio:
> rbio_pool_free(r10_bio, conf);
> return NULL;
> @@ -230,14 +229,14 @@ static void r10buf_pool_free(void *__r10_bio, void *data)
> struct r10conf *conf = data;
> struct r10bio *r10bio = __r10_bio;
> int j;
> - struct resync_pages *rp = NULL;
> + struct resync_folio *rf = NULL;
>
> for (j = conf->copies; j--; ) {
> struct bio *bio = r10bio->devs[j].bio;
>
> if (bio) {
> - rp = get_resync_pages(bio);
> - resync_free_pages(rp);
> + rf = get_resync_folio(bio);
> + folio_put(rf->folio);
> bio_uninit(bio);
> kfree(bio);
> }
> @@ -250,7 +249,7 @@ static void r10buf_pool_free(void *__r10_bio, void *data)
> }
>
> /* resync pages array stored in the 1st bio's .bi_private */
> - kfree(rp);
> + kfree(rf);
>
> rbio_pool_free(r10bio, conf);
> }
> @@ -2342,8 +2341,7 @@ static void sync_request_write(struct mddev *mddev, struct r10bio *r10_bio)
> struct r10conf *conf = mddev->private;
> int i, first;
> struct bio *tbio, *fbio;
> - int vcnt;
> - struct page **tpages, **fpages;
> + struct folio *tfolio, *ffolio;
>
> atomic_set(&r10_bio->remaining, 1);
>
> @@ -2359,14 +2357,13 @@ static void sync_request_write(struct mddev *mddev, struct r10bio *r10_bio)
> fbio = r10_bio->devs[i].bio;
> fbio->bi_iter.bi_size = r10_bio->sectors << 9;
> fbio->bi_iter.bi_idx = 0;
> - fpages = get_resync_pages(fbio)->pages;
> + ffolio = get_resync_folio(fbio)->folio;
>
> - vcnt = (r10_bio->sectors + (PAGE_SIZE >> 9) - 1) >> (PAGE_SHIFT - 9);
> /* now find blocks with errors */
> for (i=0 ; i < conf->copies ; i++) {
> - int j, d;
> + int d;
> struct md_rdev *rdev;
> - struct resync_pages *rp;
> + struct resync_folio *rf;
>
> tbio = r10_bio->devs[i].bio;
>
> @@ -2375,31 +2372,23 @@ static void sync_request_write(struct mddev *mddev, struct r10bio *r10_bio)
> if (i == first)
> continue;
>
> - tpages = get_resync_pages(tbio)->pages;
> + tfolio = get_resync_folio(tbio)->folio;
> d = r10_bio->devs[i].devnum;
> rdev = conf->mirrors[d].rdev;
> if (!r10_bio->devs[i].bio->bi_status) {
> /* We know that the bi_io_vec layout is the same for
> * both 'first' and 'i', so we just compare them.
> - * All vec entries are PAGE_SIZE;
> */
> - int sectors = r10_bio->sectors;
> - for (j = 0; j < vcnt; j++) {
> - int len = PAGE_SIZE;
> - if (sectors < (len / 512))
> - len = sectors * 512;
> - if (memcmp(page_address(fpages[j]),
> - page_address(tpages[j]),
> - len))
> - break;
> - sectors -= len/512;
> + if (memcmp(folio_address(ffolio),
> + folio_address(tfolio),
> + r10_bio->sectors << 9)) {
> + atomic64_add(r10_bio->sectors,
> + &mddev->resync_mismatches);
> + if (test_bit(MD_RECOVERY_CHECK,
> + &mddev->recovery))
> + /* Don't fix anything. */
> + continue;
> }
> - if (j == vcnt)
> - continue;
> - atomic64_add(r10_bio->sectors, &mddev->resync_mismatches);
> - if (test_bit(MD_RECOVERY_CHECK, &mddev->recovery))
> - /* Don't fix anything. */
> - continue;
> } else if (test_bit(FailFast, &rdev->flags)) {
> /* Just give up on this device */
> md_error(rdev->mddev, rdev);
> @@ -2410,13 +2399,13 @@ static void sync_request_write(struct mddev *mddev, struct r10bio *r10_bio)
> * First we need to fixup bv_offset, bv_len and
> * bi_vecs, as the read request might have corrupted these
> */
> - rp = get_resync_pages(tbio);
> + rf = get_resync_folio(tbio);
> bio_reset(tbio, conf->mirrors[d].rdev->bdev, REQ_OP_WRITE);
>
> - md_bio_reset_resync_pages(tbio, rp, fbio->bi_iter.bi_size);
> + md_bio_reset_resync_folio(tbio, rf, fbio->bi_iter.bi_size);
>
> - rp->raid_bio = r10_bio;
> - tbio->bi_private = rp;
> + rf->raid_bio = r10_bio;
> + tbio->bi_private = rf;
> tbio->bi_iter.bi_sector = r10_bio->devs[i].addr;
> tbio->bi_end_io = end_sync_write;
>
> @@ -2476,10 +2465,9 @@ static void fix_recovery_read_error(struct r10bio *r10_bio)
> struct bio *bio = r10_bio->devs[0].bio;
> sector_t sect = 0;
> int sectors = r10_bio->sectors;
> - int idx = 0;
> int dr = r10_bio->devs[0].devnum;
> int dw = r10_bio->devs[1].devnum;
> - struct page **pages = get_resync_pages(bio)->pages;
> + struct folio *folio = get_resync_folio(bio)->folio;
>
> while (sectors) {
> int s = sectors;
> @@ -2492,19 +2480,21 @@ static void fix_recovery_read_error(struct r10bio *r10_bio)
>
> rdev = conf->mirrors[dr].rdev;
> addr = r10_bio->devs[0].addr + sect;
> - ok = sync_page_io(rdev,
> - addr,
> - s << 9,
> - pages[idx],
> - REQ_OP_READ, false);
> + ok = sync_folio_io(rdev,
> + addr,
> + s << 9,
> + sect << 9,
> + folio,
> + REQ_OP_READ, false);
> if (ok) {
> rdev = conf->mirrors[dw].rdev;
> addr = r10_bio->devs[1].addr + sect;
> - ok = sync_page_io(rdev,
> - addr,
> - s << 9,
> - pages[idx],
> - REQ_OP_WRITE, false);
> + ok = sync_folio_io(rdev,
> + addr,
> + s << 9,
> + sect << 9,
> + folio,
> + REQ_OP_WRITE, false);
> if (!ok) {
> set_bit(WriteErrorSeen, &rdev->flags);
> if (!test_and_set_bit(WantReplacement,
> @@ -2539,7 +2529,6 @@ static void fix_recovery_read_error(struct r10bio *r10_bio)
>
> sectors -= s;
> sect += s;
> - idx++;
> }
> }
>
> @@ -3050,7 +3039,7 @@ static int init_resync(struct r10conf *conf)
> static struct r10bio *raid10_alloc_init_r10buf(struct r10conf *conf)
> {
> struct r10bio *r10bio = mempool_alloc(&conf->r10buf_pool, GFP_NOIO);
> - struct rsync_pages *rp;
> + struct resync_folio *rf;
> struct bio *bio;
> int nalloc;
> int i;
> @@ -3063,14 +3052,14 @@ static struct r10bio *raid10_alloc_init_r10buf(struct r10conf *conf)
>
> for (i = 0; i < nalloc; i++) {
> bio = r10bio->devs[i].bio;
> - rp = bio->bi_private;
> + rf = bio->bi_private;
> bio_reset(bio, NULL, 0);
> - bio->bi_private = rp;
> + bio->bi_private = rf;
> bio = r10bio->devs[i].repl_bio;
> if (bio) {
> - rp = bio->bi_private;
> + rf = bio->bi_private;
> bio_reset(bio, NULL, 0);
> - bio->bi_private = rp;
> + bio->bi_private = rf;
> }
> }
> return r10bio;
> @@ -3156,7 +3145,6 @@ static sector_t raid10_sync_request(struct mddev *mddev, sector_t sector_nr,
> int max_sync = RESYNC_SECTORS;
> sector_t sync_blocks;
> sector_t chunk_mask = conf->geo.chunk_mask;
> - int page_idx = 0;
>
> /*
> * Allow skipping a full rebuild for incremental assembly
> @@ -3376,6 +3364,15 @@ static sector_t raid10_sync_request(struct mddev *mddev, sector_t sector_nr,
> continue;
> }
> }
> +
> + /*
> + * RESYNC_BLOCK_SIZE folio might alloc failed in
> + * resync_alloc_folio(). Fall back to smaller sync
> + * size if needed.
> + */
> + if (max_sync > r10_bio->sectors)
> + max_sync = r10_bio->sectors;
> +
> any_working = 1;
> bio = r10_bio->devs[0].bio;
> bio->bi_next = biolist;
> @@ -3527,7 +3524,15 @@ static sector_t raid10_sync_request(struct mddev *mddev, sector_t sector_nr,
> }
> if (sync_blocks < max_sync)
> max_sync = sync_blocks;
> +
> r10_bio = raid10_alloc_init_r10buf(conf);
> + /*
> + * RESYNC_BLOCK_SIZE folio might alloc failed in resync_alloc_folio().
> + * Fall back to smaller sync size if needed.
> + */
> + if (max_sync > r10_bio->sectors)
> + max_sync = r10_bio->sectors;
> +
> r10_bio->state = 0;
>
> r10_bio->mddev = mddev;
> @@ -3620,29 +3625,25 @@ static sector_t raid10_sync_request(struct mddev *mddev, sector_t sector_nr,
> }
> }
>
> - nr_sectors = 0;
> if (sector_nr + max_sync < max_sector)
> max_sector = sector_nr + max_sync;
> do {
> - struct page *page;
> - int len = PAGE_SIZE;
> - if (sector_nr + (len>>9) > max_sector)
> - len = (max_sector - sector_nr) << 9;
> - if (len == 0)
> + nr_sectors = max_sector - sector_nr;
> +
> + if (nr_sectors == 0)
> break;
> for (bio= biolist ; bio ; bio=bio->bi_next) {
> - struct resync_pages *rp = get_resync_pages(bio);
> - page = resync_fetch_page(rp, page_idx);
> - if (WARN_ON(!bio_add_page(bio, page, len, 0))) {
> + struct resync_folio *rf = get_resync_folio(bio);
> +
> + if (WARN_ON(!bio_add_folio(bio, rf->folio, nr_sectors << 9, 0))) {
> bio->bi_status = BLK_STS_RESOURCE;
> bio_endio(bio);
> *skipped = 1;
> - return max_sync;
> + return nr_sectors << 9;
> }
> }
> - nr_sectors += len>>9;
> - sector_nr += len>>9;
> - } while (++page_idx < RESYNC_PAGES);
> + sector_nr += nr_sectors;
> + } while (0);
> r10_bio->sectors = nr_sectors;
>
> if (mddev_is_clustered(mddev) &&
> @@ -4560,7 +4561,7 @@ static sector_t reshape_request(struct mddev *mddev, sector_t sector_nr,
> int *skipped)
> {
> /* We simply copy at most one chunk (smallest of old and new)
> - * at a time, possibly less if that exceeds RESYNC_PAGES,
> + * at a time, possibly less if that exceeds RESYNC_BLOCK_SIZE,
> * or we hit a bad block or something.
> * This might mean we pause for normal IO in the middle of
> * a chunk, but that is not a problem as mddev->reshape_position
> @@ -4600,14 +4601,13 @@ static sector_t reshape_request(struct mddev *mddev, sector_t sector_nr,
> struct r10bio *r10_bio;
> sector_t next, safe, last;
> int max_sectors;
> - int nr_sectors;
> int s;
> struct md_rdev *rdev;
> int need_flush = 0;
> struct bio *blist;
> struct bio *bio, *read_bio;
> int sectors_done = 0;
> - struct page **pages;
> + struct folio *folio;
>
> if (sector_nr == 0) {
> /* If restarting in the middle, skip the initial sectors */
> @@ -4709,7 +4709,12 @@ static sector_t reshape_request(struct mddev *mddev, sector_t sector_nr,
> r10_bio->mddev = mddev;
> r10_bio->sector = sector_nr;
> set_bit(R10BIO_IsReshape, &r10_bio->state);
> - r10_bio->sectors = last - sector_nr + 1;
> + /*
> + * RESYNC_BLOCK_SIZE folio might alloc failed in
> + * resync_alloc_folio(). Fall back to smaller sync
> + * size if needed.
> + */
> + r10_bio->sectors = min_t(int, r10_bio->sectors, last - sector_nr + 1);
> rdev = read_balance(conf, r10_bio, &max_sectors);
> BUG_ON(!test_bit(R10BIO_Previous, &r10_bio->state));
>
> @@ -4723,7 +4728,7 @@ static sector_t reshape_request(struct mddev *mddev, sector_t sector_nr,
> return sectors_done;
> }
>
> - read_bio = bio_alloc_bioset(rdev->bdev, RESYNC_PAGES, REQ_OP_READ,
> + read_bio = bio_alloc_bioset(rdev->bdev, 1, REQ_OP_READ,
> GFP_KERNEL, &mddev->bio_set);
> read_bio->bi_iter.bi_sector = (r10_bio->devs[r10_bio->read_slot].addr
> + rdev->data_offset);
> @@ -4787,32 +4792,23 @@ static sector_t reshape_request(struct mddev *mddev, sector_t sector_nr,
> blist = b;
> }
>
> - /* Now add as many pages as possible to all of these bios. */
> + /* Now add folio to all of these bios. */
>
> - nr_sectors = 0;
> - pages = get_resync_pages(r10_bio->devs[0].bio)->pages;
> - for (s = 0 ; s < max_sectors; s += PAGE_SIZE >> 9) {
> - struct page *page = pages[s / (PAGE_SIZE >> 9)];
> - int len = (max_sectors - s) << 9;
> - if (len > PAGE_SIZE)
> - len = PAGE_SIZE;
> - for (bio = blist; bio ; bio = bio->bi_next) {
> - if (WARN_ON(!bio_add_page(bio, page, len, 0))) {
> - bio->bi_status = BLK_STS_RESOURCE;
> - bio_endio(bio);
> - return sectors_done;
> - }
> + folio = get_resync_folio(r10_bio->devs[0].bio)->folio;
> + for (bio = blist; bio ; bio = bio->bi_next) {
> + if (WARN_ON(!bio_add_folio(bio, folio, max_sectors, 0))) {
> + bio->bi_status = BLK_STS_RESOURCE;
> + bio_endio(bio);
> + return sectors_done;
In fact, the original codes don't clean up before returning.
bio_add_folio_nofail is used in raid1 and can we use
bio_add_folio_nofail here as well?
> }
> - sector_nr += len >> 9;
> - nr_sectors += len >> 9;
> }
> - r10_bio->sectors = nr_sectors;
> + r10_bio->sectors = max_sectors >> 9;
>
> /* Now submit the read */
> atomic_inc(&r10_bio->remaining);
> read_bio->bi_next = NULL;
> submit_bio_noacct(read_bio);
> - sectors_done += nr_sectors;
> + sectors_done += max_sectors;
> if (sector_nr <= last)
> goto read_more;
>
> @@ -4914,8 +4910,8 @@ static int handle_reshape_read_error(struct mddev *mddev,
> struct r10conf *conf = mddev->private;
> struct r10bio *r10b;
> int slot = 0;
> - int idx = 0;
> - struct page **pages;
> + int sect = 0;
> + struct folio *folio;
>
> r10b = kmalloc(struct_size(r10b, devs, conf->copies), GFP_NOIO);
> if (!r10b) {
> @@ -4923,8 +4919,8 @@ static int handle_reshape_read_error(struct mddev *mddev,
> return -ENOMEM;
> }
>
> - /* reshape IOs share pages from .devs[0].bio */
> - pages = get_resync_pages(r10_bio->devs[0].bio)->pages;
> + /* reshape IOs share folio from .devs[0].bio */
> + folio = get_resync_folio(r10_bio->devs[0].bio)->folio;
>
> r10b->sector = r10_bio->sector;
> __raid10_find_phys(&conf->prev, r10b);
> @@ -4940,19 +4936,19 @@ static int handle_reshape_read_error(struct mddev *mddev,
> while (!success) {
> int d = r10b->devs[slot].devnum;
> struct md_rdev *rdev = conf->mirrors[d].rdev;
> - sector_t addr;
> if (rdev == NULL ||
> test_bit(Faulty, &rdev->flags) ||
> !test_bit(In_sync, &rdev->flags))
> goto failed;
>
> - addr = r10b->devs[slot].addr + idx * PAGE_SIZE;
> atomic_inc(&rdev->nr_pending);
> - success = sync_page_io(rdev,
> - addr,
> - s << 9,
> - pages[idx],
> - REQ_OP_READ, false);
> + success = sync_folio_io(rdev,
> + r10b->devs[slot].addr +
> + sect,
> + s << 9,
> + sect << 9,
> + folio,
> + REQ_OP_READ, false);
> rdev_dec_pending(rdev, mddev);
> if (success)
> break;
> @@ -4971,7 +4967,7 @@ static int handle_reshape_read_error(struct mddev *mddev,
> return -EIO;
> }
> sectors -= s;
> - idx++;
> + sect += s;
> }
> kfree(r10b);
> return 0;
> --
> 2.39.2
>
>
Regards
Xiao
^ permalink raw reply
* Re: [PATCH] md/raid5: split reshape bios before bitmap accounting
From: kernel test robot @ 2026-04-30 0:59 UTC (permalink / raw)
To: Yu Kuai, linux-raid
Cc: llvm, oe-kbuild-all, linux-kernel, Li Nan, Yu Kuai, Cheng Cheng
In-Reply-To: <20260419030942.824195-20-yukuai@fnnas.com>
Hi Yu,
kernel test robot noticed the following build errors:
[auto build test ERROR on linus/master]
[also build test ERROR on song-md/md-next v7.1-rc1 next-20260429]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Yu-Kuai/md-raid5-split-reshape-bios-before-bitmap-accounting/20260425-083941
base: linus/master
patch link: https://lore.kernel.org/r/20260419030942.824195-20-yukuai%40fnnas.com
patch subject: [PATCH] md/raid5: split reshape bios before bitmap accounting
config: um-randconfig-001-20260430 (https://download.01.org/0day-ci/archive/20260430/202604300803.nq5tYBQB-lkp@intel.com/config)
compiler: clang version 23.0.0git (https://github.com/llvm/llvm-project 5bac06718f502014fade905512f1d26d578a18f3)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260430/202604300803.nq5tYBQB-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202604300803.nq5tYBQB-lkp@intel.com/
All errors (new ones prefixed by >>):
drivers/md/raid5.c:4221:7: warning: variable 'qread' set but not used [-Wunused-but-set-variable]
4221 | int qread =0;
| ^
>> drivers/md/raid5.c:6126:7: error: call to undeclared function 'mddev_bio_split_at_reshape_offset'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
6126 | bi = mddev_bio_split_at_reshape_offset(mddev, bi, NULL,
| ^
>> drivers/md/raid5.c:6126:5: error: incompatible integer to pointer conversion assigning to 'struct bio *' from 'int' [-Wint-conversion]
6126 | bi = mddev_bio_split_at_reshape_offset(mddev, bi, NULL,
| ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6127 | &conf->bio_split);
| ~~~~~~~~~~~~~~~~~
1 warning and 2 errors generated.
vim +/mddev_bio_split_at_reshape_offset +6126 drivers/md/raid5.c
6083
6084 static bool raid5_make_request(struct mddev *mddev, struct bio * bi)
6085 {
6086 DEFINE_WAIT_FUNC(wait, woken_wake_function);
6087 struct r5conf *conf = mddev->private;
6088 const int rw = bio_data_dir(bi);
6089 struct stripe_request_ctx *ctx;
6090 sector_t logical_sector;
6091 enum stripe_result res;
6092 int s, stripe_cnt;
6093 bool on_wq;
6094
6095 if (unlikely(bi->bi_opf & REQ_PREFLUSH)) {
6096 int ret = log_handle_flush_request(conf, bi);
6097
6098 if (ret == 0)
6099 return true;
6100 if (ret == -ENODEV) {
6101 if (md_flush_request(mddev, bi))
6102 return true;
6103 }
6104 /* ret == -EAGAIN, fallback */
6105 }
6106
6107 md_write_start(mddev, bi);
6108 /*
6109 * If array is degraded, better not do chunk aligned read because
6110 * later we might have to read it again in order to reconstruct
6111 * data on failed drives.
6112 */
6113 if (rw == READ && mddev->degraded == 0 &&
6114 mddev->reshape_position == MaxSector) {
6115 bi = chunk_aligned_read(mddev, bi);
6116 if (!bi)
6117 return true;
6118 }
6119
6120 if (unlikely(bio_op(bi) == REQ_OP_DISCARD)) {
6121 make_discard_request(mddev, bi);
6122 md_write_end(mddev);
6123 return true;
6124 }
6125
> 6126 bi = mddev_bio_split_at_reshape_offset(mddev, bi, NULL,
6127 &conf->bio_split);
6128 if (!bi) {
6129 if (rw == WRITE)
6130 md_write_end(mddev);
6131 return true;
6132 }
6133
6134 logical_sector = bi->bi_iter.bi_sector & ~((sector_t)RAID5_STRIPE_SECTORS(conf)-1);
6135 bi->bi_next = NULL;
6136
6137 ctx = mempool_alloc(conf->ctx_pool, GFP_NOIO);
6138 memset(ctx, 0, conf->ctx_size);
6139 ctx->first_sector = logical_sector;
6140 ctx->last_sector = bio_end_sector(bi);
6141 /*
6142 * if r5l_handle_flush_request() didn't clear REQ_PREFLUSH,
6143 * we need to flush journal device
6144 */
6145 if (unlikely(bi->bi_opf & REQ_PREFLUSH))
6146 ctx->do_flush = true;
6147
6148 stripe_cnt = DIV_ROUND_UP_SECTOR_T(ctx->last_sector - logical_sector,
6149 RAID5_STRIPE_SECTORS(conf));
6150 bitmap_set(ctx->sectors_to_do, 0, stripe_cnt);
6151
6152 pr_debug("raid456: %s, logical %llu to %llu\n", __func__,
6153 bi->bi_iter.bi_sector, ctx->last_sector);
6154
6155 /* Bail out if conflicts with reshape and REQ_NOWAIT is set */
6156 if ((bi->bi_opf & REQ_NOWAIT) &&
6157 get_reshape_loc(mddev, conf, logical_sector) == LOC_INSIDE_RESHAPE) {
6158 bio_wouldblock_error(bi);
6159 if (rw == WRITE)
6160 md_write_end(mddev);
6161 mempool_free(ctx, conf->ctx_pool);
6162 return true;
6163 }
6164 md_account_bio(mddev, &bi);
6165
6166 /*
6167 * Lets start with the stripe with the lowest chunk offset in the first
6168 * chunk. That has the best chances of creating IOs adjacent to
6169 * previous IOs in case of sequential IO and thus creates the most
6170 * sequential IO pattern. We don't bother with the optimization when
6171 * reshaping as the performance benefit is not worth the complexity.
6172 */
6173 if (likely(conf->reshape_progress == MaxSector)) {
6174 logical_sector = raid5_bio_lowest_chunk_sector(conf, bi);
6175 on_wq = false;
6176 } else {
6177 add_wait_queue(&conf->wait_for_reshape, &wait);
6178 on_wq = true;
6179 }
6180 s = (logical_sector - ctx->first_sector) >> RAID5_STRIPE_SHIFT(conf);
6181
6182 while (1) {
6183 res = make_stripe_request(mddev, conf, ctx, logical_sector,
6184 bi);
6185 if (res == STRIPE_FAIL || res == STRIPE_WAIT_RESHAPE)
6186 break;
6187
6188 if (res == STRIPE_RETRY)
6189 continue;
6190
6191 if (res == STRIPE_SCHEDULE_AND_RETRY) {
6192 WARN_ON_ONCE(!on_wq);
6193 /*
6194 * Must release the reference to batch_last before
6195 * scheduling and waiting for work to be done,
6196 * otherwise the batch_last stripe head could prevent
6197 * raid5_activate_delayed() from making progress
6198 * and thus deadlocking.
6199 */
6200 if (ctx->batch_last) {
6201 raid5_release_stripe(ctx->batch_last);
6202 ctx->batch_last = NULL;
6203 }
6204
6205 wait_woken(&wait, TASK_UNINTERRUPTIBLE,
6206 MAX_SCHEDULE_TIMEOUT);
6207 continue;
6208 }
6209
6210 s = find_next_bit_wrap(ctx->sectors_to_do, stripe_cnt, s);
6211 if (s == stripe_cnt)
6212 break;
6213
6214 logical_sector = ctx->first_sector +
6215 (s << RAID5_STRIPE_SHIFT(conf));
6216 }
6217 if (unlikely(on_wq))
6218 remove_wait_queue(&conf->wait_for_reshape, &wait);
6219
6220 if (ctx->batch_last)
6221 raid5_release_stripe(ctx->batch_last);
6222
6223 if (rw == WRITE)
6224 md_write_end(mddev);
6225
6226 mempool_free(ctx, conf->ctx_pool);
6227 if (res == STRIPE_WAIT_RESHAPE) {
6228 md_free_cloned_bio(bi);
6229 return false;
6230 }
6231
6232 bio_endio(bi);
6233 return true;
6234 }
6235
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply
* Re: Suggest me a cost effective SATA SSD?
From: Roman Mamedov @ 2026-04-29 23:20 UTC (permalink / raw)
To: Andy Smith; +Cc: linux-raid
In-Reply-To: <afKLmiyhEe4Om0Nt@mail.bitfolk.com>
On Wed, 29 Apr 2026 22:52:10 +0000
Andy Smith <andy@strugglers.net> wrote:
> Model Family: Crucial/Micron Client SSDs
> Device Model: CT4000BX500SSD1
> User Capacity: 4,000,787,030,016 bytes [4.00 TB]
>
> Their write performance is terrible. Struggling to get 20MB/s sequential
> write. Their TBW count is low so it's not an issue of excessive write
> cycles. I've searched around and established they are just really bad
> SSDs. They also don't support SMART self-tests, which seems like a
> really cheap thing to do.
>
> I'm further confident that the problem lies with these SSDs because
> there is a pair of much better SSDs in there and they perform as I would
> expect. However, the use case for this storage is for lower cost so it's
> not an option to just buy more of those.
>
> So, could anyone suggest a decent low end (consumer/prosumer market) SSD
> model that is known to work well without terrible firmware bugs under
> Linux, preferably with power loss protection? Low write endurance is
> fine. Capacity of 4TB ideally.
Basically the main thing to avoid is QLC flash, and look for TLC instead.
Low write speeds and low endurance are all due to the QLC, which prioritizes
capacity and low cost in favor of performance and reliability.
Consult the SSD database at https://www.techpowerup.com/ssd-specs/ about your
potential choices that you see in shops, for what flash type they have.
Or conversely, filter by TLC: https://www.techpowerup.com/ssd-specs/?type=TLC
and then add search term of your favorite vendor to look for candidates to buy.
Keep in mind this info may not be 100% reliable, as it is crowd-sourced and
way too often hidden by the manufacturers, or flash type is switched back and
forth even within the same model.
Side note, I would not count on SMART self tests as being relevant on an SSD.
Sure, on HDD they can scan the entire surface looking for unreadable areas. On
SSDs, what they could do? Read out the entire flash? From the duration of even
the extended offline tests on SSD, they are not doing that. It is opaque and
proprietary as to what they actually do, if anything.
--
With respect,
Roman
^ permalink raw reply
* Suggest me a cost effective SATA SSD?
From: Andy Smith @ 2026-04-29 22:52 UTC (permalink / raw)
To: linux-raid
Hi,
I've inherited a system with a pair of these:
Model Family: Crucial/Micron Client SSDs
Device Model: CT4000BX500SSD1
User Capacity: 4,000,787,030,016 bytes [4.00 TB]
Their write performance is terrible. Struggling to get 20MB/s sequential
write. Their TBW count is low so it's not an issue of excessive write
cycles. I've searched around and established they are just really bad
SSDs. They also don't support SMART self-tests, which seems like a
really cheap thing to do.
I'm further confident that the problem lies with these SSDs because
there is a pair of much better SSDs in there and they perform as I would
expect. However, the use case for this storage is for lower cost so it's
not an option to just buy more of those.
So, could anyone suggest a decent low end (consumer/prosumer market) SSD
model that is known to work well without terrible firmware bugs under
Linux, preferably with power loss protection? Low write endurance is
fine. Capacity of 4TB ideally.
Thanks,
Andy
^ permalink raw reply
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