Re: [PATCH v2 3/5] md/md-bitmap: add dummy bitmap ops for none to fix wrong bitmap offset

Xiao Ni <[email protected]>
Newsgroups gmane.linux.raid
Message-ID <CALTww29y_EJq3Qkimn67cSTnL6_+Yi6Lf1LoT=JJM6YqmFTjaw@mail.gmail.com>
On Tue, Apr 21, 2026 at 10:29 AM Su Yue <[email protected]> wrote:
>
> On Mon 20 Apr 2026 at 15:05, Xiao Ni <[email protected]> wrote:
>
> > On Tue, Apr 7, 2026 at 6:27 PM Su Yue <[email protected]> wrote:
> >>
> >> Before commit fb8cc3b0d9db ("md/md-bitmap: delay registration
> >> of bitmap_ops until creating bitmap")
> >> if CONFIG_MD_BITMAP is enabled, both bitmap none, internal and
> >> clustered have
> >> the sysfs file bitmap/location.
> >>
> >> After the commit, if bitmap is none, bitmap/location doesn't
> >> exist anymore.
> >> It breaks 'grow' behavior of a md array of madam with
> >> MD_FEATURE_BITMAP_OFFSET.
> >> Take level=mirror and metadata=1.2 as an example:
> >>
> >> $ mdadm  --create  /dev/md0 -f --bitmap=none --raid-devices=2
> >> --level=mirror \
> >>   --metadata=1.2 /dev/vdd /dev/vde
> >> $ mdadm --grow /dev/md0 --bitmap=internal
> >> $ cat /sys/block/md0/md/bitmap/location
> >> Before:+8
> >> After: +2
> >>
> >> While growing bitmap from none to internal, clustered and
> >> llbitmap,
> >> mdadm/Grow.c:Grow_addbitmap() tries to detect bitmap/location
> >> first.
> >> 1)If bitmap/location exists, it sets bitmap/location after
> >> getinfo_super().
> >> 2)If bitmap/location doesn't exist, mdadm just calls
> >> md_set_array_info() then
> >> mddev->bitmap_info.default_offset will be used.
> >> Situation can be worse if growing none to clustered, bitmap
> >> offset of the node
> >> calling `madm --grow` will be changed but the other node are
> >> reading bitmap sb from
> >> the old location.
> >>
> >> Here introducing a dummy bitmap_operations for ID_BITMAP_NONE
> >> to restore sysfs
> >> file bitmap/location for ID_BITMAP_NONE and ID_BITMAP.
> >> location_store() now calls md_bitmap_(create|destroy) with
> >> false sysfs parameter then
> >> (add,remove)_internal_bitmap() will be called to manage sysfs
> >> entries.
> >>
> >> Fixes: fb8cc3b0d9db ("md/md-bitmap: delay registration of
> >> bitmap_ops until creating bitmap")
> >> Suggested-by: Yu Kuai <[email protected]>
> >> Signed-off-by: Su Yue <[email protected]>
> >> ---
> >>  drivers/md/md-bitmap.c   | 116
> >>  ++++++++++++++++++++++++++++++++++++---
> >>  drivers/md/md-bitmap.h   |   3 +
> >>  drivers/md/md-llbitmap.c |  12 ++++
> >>  drivers/md/md.c          |  16 +++---
> >>  4 files changed, 130 insertions(+), 17 deletions(-)
> >>
> >> diff --git a/drivers/md/md-bitmap.c b/drivers/md/md-bitmap.c
> >> index ac06c9647bf0..a8a176428c61 100644
> >> --- a/drivers/md/md-bitmap.c
> >> +++ b/drivers/md/md-bitmap.c
> >> @@ -240,6 +240,11 @@ static bool bitmap_enabled(void *data,
> >> bool flush)
> >>                bitmap->storage.filemap != NULL;
> >>  }
> >>
> >> +static bool dummy_bitmap_enabled(void *data, bool flush)
> >> +{
> >> +       return false;
> >> +}
> >> +
> >>  /*
> >>   * check a page and, if necessary, allocate it (or hijack it
> >>   if the alloc fails)
> >>   *
> >> @@ -2201,6 +2206,11 @@ static int bitmap_create(struct mddev
> >> *mddev)
> >>         return 0;
> >>  }
> >>
> >> +static int dummy_bitmap_create(struct mddev *mddev)
> >> +{
> >> +       return 0;
> >> +}
> >> +
> >>  static int bitmap_load(struct mddev *mddev)
> >>  {
> >>         int err = 0;
> >> @@ -2594,6 +2604,9 @@ location_show(struct mddev *mddev, char
> >> *page)
> >>         return len;
> >>  }
> >>
> >> +static int create_internal_group(struct mddev *mddev);
> >> +static void remove_internal_group(struct mddev *mddev);
> >> +
> >>  static ssize_t
> >>  location_store(struct mddev *mddev, const char *buf, size_t
> >>  len)
> >>  {
> >> @@ -2618,7 +2631,8 @@ location_store(struct mddev *mddev, const
> >> char *buf, size_t len)
> >>                         goto out;
> >>                 }
> >>
> >> -               md_bitmap_destroy(mddev, true);
> >> +               md_bitmap_destroy(mddev, false);
> >> +               remove_internal_group(mddev);
> >>                 mddev->bitmap_info.offset = 0;
> >>                 if (mddev->bitmap_info.file) {
> >>                         struct file *f =
> >>                         mddev->bitmap_info.file;
> >> @@ -2659,14 +2673,22 @@ location_store(struct mddev *mddev,
> >> const char *buf, size_t len)
> >>                          */
> >>                         mddev->bitmap_id = ID_BITMAP;
> >>                         mddev->bitmap_info.offset = offset;
> >> -                       rv = md_bitmap_create(mddev, true);
> >> +                       rv = md_bitmap_create(mddev, false);
> >>                         if (rv)
> >>                                 goto out;
> >>
> >> +                       rv = create_internal_group(mddev);
> >> +                       if (rv) {
> >> +                               mddev->bitmap_info.offset = 0;
> >> +                               bitmap_destroy(mddev);
> >> +                               goto out;
> >> +                       }
> >> +
> >>                         rv = bitmap_load(mddev);
> >>                         if (rv) {
> >> +                               remove_internal_group(mddev);
> >>                                 mddev->bitmap_info.offset = 0;
> >> -                               md_bitmap_destroy(mddev, true);
> >> +                               md_bitmap_destroy(mddev,
> >> false);
> >>                                 goto out;
> >>                         }
> >>                 }
> >> @@ -2960,8 +2982,7 @@ static struct md_sysfs_entry
> >> max_backlog_used =
> >>  __ATTR(max_backlog_used, S_IRUGO | S_IWUSR,
> >>         behind_writes_used_show, behind_writes_used_reset);
> >>
> >> -static struct attribute *md_bitmap_attrs[] = {
> >> -       &bitmap_location.attr,
> >> +static struct attribute *internal_bitmap_attrs[] = {
> >>         &bitmap_space.attr,
> >>         &bitmap_timeout.attr,
> >>         &bitmap_backlog.attr,
> >> @@ -2972,11 +2993,57 @@ static struct attribute
> >> *md_bitmap_attrs[] = {
> >>         NULL
> >>  };
> >>
> >> -static struct attribute_group md_bitmap_group = {
> >> +static struct attribute_group internal_bitmap_group = {
> >>         .name = "bitmap",
> >> -       .attrs = md_bitmap_attrs,
> >> +       .attrs = internal_bitmap_attrs,
> >>  };
> >>
> >> +/* Only necessary attrs for compatibility */
> >> +static struct attribute *common_bitmap_attrs[] = {
> >> +       &bitmap_location.attr,
> >> +       NULL
> >> +};
> >> +
> >> +static const struct attribute_group common_bitmap_group = {
> >> +       .name = "bitmap",
> >> +       .attrs = common_bitmap_attrs,
> >> +};
> >> +
> >> +static int create_internal_group(struct mddev *mddev)
> >> +{
> >> +       /*
> >> +        * md_bitmap_group and md_bitmap_common_group are using
> >> same name
> >> +        * 'bitmap'.
> >> +        */
> >> +       return sysfs_merge_group(&mddev->kobj,
> >> &internal_bitmap_group);
> >> +}
> >> +
> >> +static void remove_internal_group(struct mddev *mddev)
> >> +{
> >> +       sysfs_unmerge_group(&mddev->kobj,
> >> &internal_bitmap_group);
> >> +}
> >> +
> >> +static int bitmap_register_groups(struct mddev *mddev)
> >> +{
> >> +       int ret;
> >> +
> >> +       ret = sysfs_create_group(&mddev->kobj,
> >> &common_bitmap_group);
> >> +
> >> +       if (ret)
> >> +               return ret;
> >> +
> >> +       ret = sysfs_merge_group(&mddev->kobj,
> >> &internal_bitmap_group);
> >> +       if (ret)
> >> +               sysfs_remove_group(&mddev->kobj,
> >> &common_bitmap_group);
> >> +
> >> +       return ret;
> >> +}
> >> +
> >> +static void bitmap_unregister_groups(struct mddev *mddev)
> >> +{
> >> +       sysfs_unmerge_group(&mddev->kobj,
> >> &internal_bitmap_group);
> >> +}
> >
> > Hi Su
> >
> > bitmap_unregister_groups should also remove common_bitmap_group,
> > right?
> >
>
> As you pasted before, mdadm:Grow.c:
>
> int Grow_addbitmap(char *devname, int fd, struct context *c,
> struct shape *s)
> {
> ...
>         if (array.state & (1 << MD_SB_BITMAP_PRESENT)) {
>                 if (s->btype == BitmapNone) {
>                         array.state &= ~(1 << MD_SB_BITMAP_PRESENT);
>                         if (md_set_array_info(fd, &array) != 0) {
>                                 if (array.state & (1 << MD_SB_CLUSTERED))
>                                         pr_err("failed to remove clustered
>                                         bitmap.\n");
>                                 else
>                                         pr_err("failed to remove internal bitmap.\n");
>                                 return 1;
>                         }
>                         return 0;
>                 }
>                 pr_err("bitmap already present on %s\n", devname);
>                 return 1;
>         }
> ...
> }
>
> In case of growing from internal to none,
> bitmap_unregister_groups() will
> be called, if common_bitmap_group is removed, bitmap/location
> won't exist.
> update_array_info() is a common function used by many call paths.
> Also
> llbitmap is involved here. I don't want to make situation and code
> more
> complicated like adding more codes in update_array_info().

The above mdadm codes use bitmap/location. In your patch, you already
pass false to md_bitmap_destroy in location_store. So removing
common_bitmap_group in bitmap_unregister_groups can't affect the above
case.

But after reading your below comments. It's good to me that
bitmap_unregister_groups doesn't remove common_bitmap_group.

>
>
> >> +
> >>  static struct bitmap_operations bitmap_ops = {
> >
> > You already changed md_bitmap_group to internal_bitmap_group.
> > It's
> > better to change bitmap_ops to internal_bitmap_ops?
> >
> >>         .head = {
> >>                 .type   = MD_BITMAP,
> >> @@ -3018,21 +3085,52 @@ static struct bitmap_operations
> >> bitmap_ops = {
> >>         .set_pages              = bitmap_set_pages,
> >>         .free                   = md_bitmap_free,
> >>
> >> -       .group                  = &md_bitmap_group,
> >> +       .register_groups        = bitmap_register_groups,
> >> +       .unregister_groups      = bitmap_unregister_groups,
> >> +};
> >> +
> >> +static int none_bitmap_register_groups(struct mddev *mddev)
> >> +{
> >> +       return sysfs_create_group(&mddev->kobj,
> >> &common_bitmap_group);
> >> +}
> >> +
> >> +static struct bitmap_operations none_bitmap_ops = {
> >> +       .head = {
> >> +               .type   = MD_BITMAP,
> >> +               .id     = ID_BITMAP_NONE,
> >> +               .name   = "none",
> >> +       },
> >> +
> >> +       .enabled                = dummy_bitmap_enabled,
> >> +       .create                 = dummy_bitmap_create,
> >> +       .destroy                = bitmap_destroy,
> >> +       .load                   = bitmap_load,
> >> +       .get_stats              = bitmap_get_stats,
> >> +       .free                   = md_bitmap_free,
> >> +
> >> +       .register_groups        = none_bitmap_register_groups,
> >> +       .unregister_groups      = NULL,
> >
> > How does bitmap/location can be deleted if array is created with
> > no
> > bitmap? Can the `mdadm --stop` command get stuck?
> >
> No. It wont get stuck.
>
> I guess here your concern is the timing of removing
> common_bitmap_group.
> The life cycle is same as before fb8cc3b0d9db. At the time
> llbitmap is
> not introduced, because there is no need to switch bitmap ops, so
> all
> attrs of bitmap are removed in kobject_put() in
> mddev_delayed_delete()
> queued by mddev_put(). This is now where common_bitmap_group is
> being removed.

Thanks for the explanation.

Best Regards
Xiao
>
> --
> Su
> > Best Regards
> > Xiao
> >
> >>  };
> >>
> >>  int md_bitmap_init(void)
> >>  {
> >> +       int ret;
> >> +
> >>         md_bitmap_wq = alloc_workqueue("md_bitmap",
> >>         WQ_MEM_RECLAIM | WQ_UNBOUND,
> >>                                        0);
> >>         if (!md_bitmap_wq)
> >>                 return -ENOMEM;
> >>
> >> -       return register_md_submodule(&bitmap_ops.head);
> >> +       ret = register_md_submodule(&bitmap_ops.head);
> >> +       if (ret)
> >> +               return ret;
> >> +
> >> +       return register_md_submodule(&none_bitmap_ops.head);
> >>  }
> >>
> >>  void md_bitmap_exit(void)
> >>  {
> >>         destroy_workqueue(md_bitmap_wq);
> >>         unregister_md_submodule(&bitmap_ops.head);
> >> +       unregister_md_submodule(&none_bitmap_ops.head);
> >>  }
> >> diff --git a/drivers/md/md-bitmap.h b/drivers/md/md-bitmap.h
> >> index b42a28fa83a0..10bc6854798c 100644
> >> --- a/drivers/md/md-bitmap.h
> >> +++ b/drivers/md/md-bitmap.h
> >> @@ -125,6 +125,9 @@ struct bitmap_operations {
> >>         void (*set_pages)(void *data, unsigned long pages);
> >>         void (*free)(void *data);
> >>
> >> +       int (*register_groups)(struct mddev *mddev);
> >> +       void (*unregister_groups)(struct mddev *mddev);
> >> +
> >>         struct attribute_group *group;
> >>  };
> >>
> >> diff --git a/drivers/md/md-llbitmap.c
> >> b/drivers/md/md-llbitmap.c
> >> index bf398d7476b3..9b3ea4f1d268 100644
> >> --- a/drivers/md/md-llbitmap.c
> >> +++ b/drivers/md/md-llbitmap.c
> >> @@ -1561,6 +1561,16 @@ static struct attribute_group
> >> md_llbitmap_group = {
> >>         .attrs = md_llbitmap_attrs,
> >>  };
> >>
> >> +static int llbitmap_register_groups(struct mddev *mddev)
> >> +{
> >> +       return sysfs_create_group(&mddev->kobj,
> >> &md_llbitmap_group);
> >> +}
> >> +
> >> +static void llbitmap_unregister_groups(struct mddev *mddev)
> >> +{
> >> +       sysfs_remove_group(&mddev->kobj, &md_llbitmap_group);
> >> +}
> >> +
> >>  static struct bitmap_operations llbitmap_ops = {
> >>         .head = {
> >>                 .type   = MD_BITMAP,
> >> @@ -1597,6 +1607,8 @@ static struct bitmap_operations
> >> llbitmap_ops = {
> >>         .dirty_bits             = llbitmap_dirty_bits,
> >>         .write_all              = llbitmap_write_all,
> >>
> >> +       .register_groups        = llbitmap_register_groups,
> >> +       .unregister_groups      = llbitmap_unregister_groups,
> >>         .group                  = &md_llbitmap_group,
> >>  };
> >>
> >> diff --git a/drivers/md/md.c b/drivers/md/md.c
> >> index d3c8f77b4fe3..55a95b227b83 100644
> >> --- a/drivers/md/md.c
> >> +++ b/drivers/md/md.c
> >> @@ -683,8 +683,7 @@ static bool mddev_set_bitmap_ops(struct
> >> mddev *mddev, bool create_sysfs)
> >>         struct bitmap_operations *old = mddev->bitmap_ops;
> >>         struct md_submodule_head *head;
> >>
> >> -       if (mddev->bitmap_id == ID_BITMAP_NONE ||
> >> -           (old && old->head.id == mddev->bitmap_id))
> >> +       if (old && old->head.id == mddev->bitmap_id)
> >>                 return true;
> >>
> >>         xa_lock(&md_submodule);
> >> @@ -703,8 +702,8 @@ static bool mddev_set_bitmap_ops(struct
> >> mddev *mddev, bool create_sysfs)
> >>         mddev->bitmap_ops = (void *)head;
> >>         xa_unlock(&md_submodule);
> >>
> >> -       if (create_sysfs && !mddev_is_dm(mddev) &&
> >> mddev->bitmap_ops->group) {
> >> -               if (sysfs_create_group(&mddev->kobj,
> >> mddev->bitmap_ops->group))
> >> +       if (create_sysfs && !mddev_is_dm(mddev) &&
> >> mddev->bitmap_ops->register_groups) {
> >> +               if (mddev->bitmap_ops->register_groups(mddev))
> >>                         pr_warn("md: cannot register extra
> >>                         bitmap attributes for %s\n",
> >>                                 mdname(mddev));
> >>                 else
> >> @@ -724,8 +723,8 @@ static bool mddev_set_bitmap_ops(struct
> >> mddev *mddev, bool create_sysfs)
> >>  static void mddev_clear_bitmap_ops(struct mddev *mddev, bool
> >>  remove_sysfs)
> >>  {
> >>         if (remove_sysfs && !mddev_is_dm(mddev) &&
> >>         mddev->bitmap_ops &&
> >> -           mddev->bitmap_ops->group)
> >> -               sysfs_remove_group(&mddev->kobj,
> >> mddev->bitmap_ops->group);
> >> +           mddev->bitmap_ops->unregister_groups)
> >> +               mddev->bitmap_ops->unregister_groups(mddev);
> >>
> >>         mddev->bitmap_ops = NULL;
> >>  }
> >> @@ -6610,8 +6609,9 @@ int md_run(struct mddev *mddev)
> >>                         (unsigned long long)pers->size(mddev,
> >>                         0, 0) / 2);
> >>                 err = -EINVAL;
> >>         }
> >> -       if (err == 0 && pers->sync_request &&
> >> -           (mddev->bitmap_info.file ||
> >> mddev->bitmap_info.offset)) {
> >> +       if (err == 0 && pers->sync_request) {
> >> +               if (mddev->bitmap_info.offset == 0)
> >> +                       mddev->bitmap_id = ID_BITMAP_NONE;
> >>                 err = md_bitmap_create(mddev, true);
> >>                 if (err)
> >>                         pr_warn("%s: failed to create bitmap
> >>                         (%d)\n",
> >> --
> >> 2.53.0
> >>
>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.