Linux RAID subsystem development
 help / color / mirror / Atom feed
* 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


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox