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.
Signed-off-by: Yu Kuai <yukuai@fnnas.com>
---
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
On Mon, Feb 23, 2026 at 10:43 AM Yu Kuai <yukuai@fnnas.com> 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@fnnas.com>
> ---
> 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
>
>
Hi,
在 2026/3/10 9:06, Xiao Ni 写道:
> On Mon, Feb 23, 2026 at 10:43 AM Yu Kuai <yukuai@fnnas.com> 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
At first, my colleague met this problem during power off and reboot test, however
I can't reproduce this, after adding some debug info, I'm quite sure this is still
an mdadm issue, that somewhere write llbitmap to sysfs bitmap_type file is missing.
Also, this way can be check as well if this patch works.
>
> Is it the case you want to fix?
>
> Regards
> Xiao
>> Signed-off-by: Yu Kuai <yukuai@fnnas.com>
>> ---
>> 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
>>
>>
--
Thansk,
Kuai
© 2016 - 2026 Red Hat, Inc.