Re: [PATCH v2 3/5] md: add fallback to correct bitmap_ops on version mismatch

From: Xiao Ni

Date: Mon Mar 09 2026 - 21:11:33 EST


On Mon, Feb 23, 2026 at 10:43 AM Yu Kuai <yukuai@xxxxxxxxx> wrote:
>
> If default bitmap version and on-disk version doesn't match, and mdadm
> is not the latest version to set bitmap_type, set bitmap_ops based on
> the disk version.

Hi Kuai

How can I do test to check if this patch works?

1. Create array with llbitmap
2. Stop the array
3. uninstall mdadm with llbitmap support and install mdadm without llbitmap
4. assemble the array

Is it the case you want to fix?

Regards
Xiao
>
> Signed-off-by: Yu Kuai <yukuai@xxxxxxxxx>
> ---
> drivers/md/md.c | 111 +++++++++++++++++++++++++++++++++++++++++++++++-
> 1 file changed, 110 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/md/md.c b/drivers/md/md.c
> index 72a1c7267851..245785ad0ffd 100644
> --- a/drivers/md/md.c
> +++ b/drivers/md/md.c
> @@ -6447,15 +6447,124 @@ static void md_safemode_timeout(struct timer_list *t)
>
> static int start_dirty_degraded;
>
> +/*
> + * Read bitmap superblock and return the bitmap_id based on disk version.
> + * This is used as fallback when default bitmap version and on-disk version
> + * doesn't match, and mdadm is not the latest version to set bitmap_type.
> + */
> +static enum md_submodule_id md_bitmap_get_id_from_sb(struct mddev *mddev)
> +{
> + struct md_rdev *rdev;
> + struct page *sb_page;
> + bitmap_super_t *sb;
> + enum md_submodule_id id = ID_BITMAP_NONE;
> + sector_t sector;
> + u32 version;
> +
> + if (!mddev->bitmap_info.offset)
> + return ID_BITMAP_NONE;
> +
> + sb_page = alloc_page(GFP_KERNEL);
> + if (!sb_page) {
> + pr_warn("md: %s: failed to allocate memory for bitmap\n",
> + mdname(mddev));
> + return ID_BITMAP_NONE;
> + }
> +
> + sector = mddev->bitmap_info.offset;
> +
> + rdev_for_each(rdev, mddev) {
> + u32 iosize;
> +
> + if (!test_bit(In_sync, &rdev->flags) ||
> + test_bit(Faulty, &rdev->flags) ||
> + test_bit(Bitmap_sync, &rdev->flags))
> + continue;
> +
> + iosize = roundup(sizeof(bitmap_super_t),
> + bdev_logical_block_size(rdev->bdev));
> + if (sync_page_io(rdev, sector, iosize, sb_page, REQ_OP_READ,
> + true))
> + goto read_ok;
> + }
> + pr_warn("md: %s: failed to read bitmap from any device\n",
> + mdname(mddev));
> + goto out;
> +
> +read_ok:
> + sb = kmap_local_page(sb_page);
> + if (sb->magic != cpu_to_le32(BITMAP_MAGIC)) {
> + pr_warn("md: %s: invalid bitmap magic 0x%x\n",
> + mdname(mddev), le32_to_cpu(sb->magic));
> + goto out_unmap;
> + }
> +
> + version = le32_to_cpu(sb->version);
> + switch (version) {
> + case BITMAP_MAJOR_LO:
> + case BITMAP_MAJOR_HI:
> + case BITMAP_MAJOR_CLUSTERED:
> + id = ID_BITMAP;
> + break;
> + case BITMAP_MAJOR_LOCKLESS:
> + id = ID_LLBITMAP;
> + break;
> + default:
> + pr_warn("md: %s: unknown bitmap version %u\n",
> + mdname(mddev), version);
> + break;
> + }
> +
> +out_unmap:
> + kunmap_local(sb);
> +out:
> + __free_page(sb_page);
> + return id;
> +}
> +
> static int md_bitmap_create(struct mddev *mddev)
> {
> + enum md_submodule_id orig_id = mddev->bitmap_id;
> + enum md_submodule_id sb_id;
> + int err;
> +
> if (mddev->bitmap_id == ID_BITMAP_NONE)
> return -EINVAL;
>
> if (!mddev_set_bitmap_ops(mddev))
> return -ENOENT;
>
> - return mddev->bitmap_ops->create(mddev);
> + err = mddev->bitmap_ops->create(mddev);
> + if (!err)
> + return 0;
> +
> + /*
> + * Create failed, if default bitmap version and on-disk version
> + * doesn't match, and mdadm is not the latest version to set
> + * bitmap_type, set bitmap_ops based on the disk version.
> + */
> + mddev_clear_bitmap_ops(mddev);
> +
> + sb_id = md_bitmap_get_id_from_sb(mddev);
> + if (sb_id == ID_BITMAP_NONE || sb_id == orig_id)
> + return err;
> +
> + pr_info("md: %s: bitmap version mismatch, switching from %d to %d\n",
> + mdname(mddev), orig_id, sb_id);
> +
> + mddev->bitmap_id = sb_id;
> + if (!mddev_set_bitmap_ops(mddev)) {
> + mddev->bitmap_id = orig_id;
> + return -ENOENT;
> + }
> +
> + err = mddev->bitmap_ops->create(mddev);
> + if (err) {
> + mddev_clear_bitmap_ops(mddev);
> + mddev->bitmap_id = orig_id;
> + }
> +
> + return err;
> }
>
> static void md_bitmap_destroy(struct mddev *mddev)
> --
> 2.51.0
>
>