public inbox for linux-raid@vger.kernel.org
 help / color / mirror / Atom feed
From: Su Yue <l@damenly.org>
To: Xiao Ni <xni@redhat.com>
Cc: Su Yue <glass.su@suse.com>,
	 linux-raid@vger.kernel.org, song@kernel.org,
	 linan122@huawei.com,  yukuai@fnnas.com, heming.zhao@suse.com
Subject: Re: [PATCH v2 3/5] md/md-bitmap: add dummy bitmap ops for none to fix wrong bitmap offset
Date: Tue, 21 Apr 2026 10:29:06 +0800	[thread overview]
Message-ID: <1pg982rx.fsf@damenly.org> (raw)
In-Reply-To: <CALTww28XwfDKbcnrka_YMtgTtArjAXEGhbUafVVGEWuAjV2o3w@mail.gmail.com> (Xiao Ni's message of "Mon, 20 Apr 2026 15:05:46 +0800")

On Mon 20 Apr 2026 at 15:05, Xiao Ni <xni@redhat.com> wrote:

> On Tue, Apr 7, 2026 at 6:27 PM Su Yue <glass.su@suse.com> 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 <yukuai@fnnas.com>
>> Signed-off-by: Su Yue <glass.su@suse.com>
>> ---
>>  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().


>> +
>>  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.

--
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
>>

  reply	other threads:[~2026-04-21  2:34 UTC|newest]

Thread overview: 22+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-04-07 10:26 [PATCH v2 0/5] md: bitmap grow fixes Su Yue
2026-04-07 10:26 ` [PATCH v2 1/5] md/md-bitmap: call md_bitmap_create,destroy in location_store Su Yue
2026-04-13  7:47   ` Li Nan
2026-04-13 10:18     ` Su Yue
2026-04-15 10:34   ` Xiao Ni
2026-04-16 14:08     ` Su Yue
2026-04-20  5:21       ` Xiao Ni
2026-04-21  1:26         ` Su Yue
2026-04-07 10:26 ` [PATCH v2 2/5] md/md-bitmap: add an extra sysfs argument to md_bitmap_create and destroy Su Yue
2026-04-20  5:24   ` Xiao Ni
2026-04-07 10:26 ` [PATCH v2 3/5] md/md-bitmap: add dummy bitmap ops for none to fix wrong bitmap offset Su Yue
2026-04-20  7:05   ` Xiao Ni
2026-04-21  2:29     ` Su Yue [this message]
2026-04-21  7:36       ` Xiao Ni
2026-04-21  9:21         ` Su Yue
2026-04-07 10:26 ` [PATCH v2 4/5] md: skip ID_BITMAP_NONE when show available bitmap types Su Yue
2026-04-13  8:15   ` Li Nan
2026-04-13 10:23     ` Su Yue
2026-04-07 10:26 ` [PATCH v2 5/5] md/md-bitmap: remove member group from bitmap_operations Su Yue
2026-04-16 14:10 ` [PATCH v2 0/5] md: bitmap grow fixes Su Yue
2026-04-21  5:15 ` Yu Kuai
2026-04-21  5:39   ` Su Yue

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1pg982rx.fsf@damenly.org \
    --to=l@damenly.org \
    --cc=glass.su@suse.com \
    --cc=heming.zhao@suse.com \
    --cc=linan122@huawei.com \
    --cc=linux-raid@vger.kernel.org \
    --cc=song@kernel.org \
    --cc=xni@redhat.com \
    --cc=yukuai@fnnas.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox