Linux RAID subsystem development
 help / color / mirror / Atom feed
* Re: [RFC PATCH 1/2] kernel/notifier: replace single-linked list with double-linked list for reverse traversal
From: Song Chen @ 2026-04-26 14:14 UTC (permalink / raw)
  To: Masami Hiramatsu (Google)
  Cc: rafael, lenb, mturquette, sboyd, viresh.kumar, agk, snitzer,
	mpatocka, bmarzins, song, yukuai, linan122, jason.wessel, danielt,
	dianders, horms, davem, edumazet, kuba, pabeni, paulmck, frederic,
	mcgrof, petr.pavlu, da.gomez, samitolvanen, atomlin, jpoimboe,
	jikos, mbenes, pmladek, joe.lawrence, rostedt, mark.rutland,
	mathieu.desnoyers, linux-modules, linux-kernel,
	linux-trace-kernel, linux-acpi, linux-clk, linux-pm,
	live-patching, dm-devel, linux-raid, kgdb-bugreport, netdev
In-Reply-To: <20260420144429.57b45f2beece690bceea96ec@kernel.org>

Hi Hiramatsu san,


On 4/20/26 13:44, Masami Hiramatsu (Google) wrote:
> Hi Song,
> 
> On Wed, 15 Apr 2026 15:01:37 +0800
> chensong_2000@189.cn wrote:
> 
>> From: Song Chen <chensong_2000@189.cn>
>>
>> The current notifier chain implementation uses a single-linked list
>> (struct notifier_block *next), which only supports forward traversal
>> in priority order. This makes it difficult to handle cleanup/teardown
>> scenarios that require notifiers to be called in reverse priority order.
> 
> What about introducing a new notification callback API that allows you
> to describe dependencies between callback functions?
> 
> For example, when registering a callback, you could register a string
> as an ID and specify whether to call it before or after that ID,
> or you could register a comparison function that is called when adding
> to a list. (I prefer @name and @depends fields so that it can be easily
> maintained.)
> 
> This would allow for better dependency building when adding to the list.
> 

Is the new notification callback API going to replace 
blocking_notifier_chain in module loader? or an expansion inside 
blocking_notifier_chain but introducing less complexity?
>>
>> A concrete example is the ordering dependency between ftrace and
>> livepatch during module load/unload. see the detail here [1].
> 
> If this only concerns notification callback issues with the ftrace
> and livepatch modules, it's far more robust to simply call the
> necessary processing directly when the modules load and unload,
> rather than registering notification callbacks externally.
> 
> There are fprobe, kprobe and its trace-events, all of them are using
> ftrace as its fundation layer. In this case, I always needs to
> consider callback order when a module is unloaded.
> 
> If ftrace is working as a part of module callbacks, it will conflict
> with fprobe/kprobe module callback. Of course we can reorder it with
> modifying its priority. But this is ugly, because when we introduce
> a new other feature which depends on another layer, we need to
> reorder the callback's priority number on the list.
> 
> Based on the above, I don't think this can be resolved simply by
> changing the list of notification callbacks to a bidirectional list.
> 
> Thank you,
> 

understood, many thanks for your proposal, i will think  about it.

best regards,

Song


^ permalink raw reply

* Re: [RFC PATCH 1/2] kernel/notifier: replace single-linked list with double-linked list for reverse traversal
From: Song Chen @ 2026-04-26 13:56 UTC (permalink / raw)
  To: Petr Mladek, Masami Hiramatsu
  Cc: chensong_2000, rafael, lenb, mturquette, sboyd, viresh.kumar, agk,
	snitzer, mpatocka, bmarzins, song, yukuai, linan122, jason.wessel,
	danielt, dianders, horms, davem, edumazet, kuba, pabeni, paulmck,
	frederic, mcgrof, petr.pavlu, da.gomez, samitolvanen, atomlin,
	jpoimboe, jikos, mbenes, joe.lawrence, rostedt, mark.rutland,
	mathieu.desnoyers, linux-modules, linux-kernel,
	linux-trace-kernel, linux-acpi, linux-clk, linux-pm,
	live-patching, dm-devel, linux-raid, kgdb-bugreport, netdev
In-Reply-To: <aec90caYZDHDAHgw@pathway.suse.cz>

Hi,

On 4/21/26 17:05, Petr Mladek wrote:
> On Mon 2026-04-20 14:44:29, Masami Hiramatsu wrote:
>> Hi Song,
>>
>> On Wed, 15 Apr 2026 15:01:37 +0800
>> chensong_2000@189.cn wrote:
>>
>>> From: Song Chen <chensong_2000@189.cn>
>>>
>>> The current notifier chain implementation uses a single-linked list
>>> (struct notifier_block *next), which only supports forward traversal
>>> in priority order. This makes it difficult to handle cleanup/teardown
>>> scenarios that require notifiers to be called in reverse priority order.
>>
>> What about introducing a new notification callback API that allows you
>> to describe dependencies between callback functions?
>>
>> For example, when registering a callback, you could register a string
>> as an ID and specify whether to call it before or after that ID,
>> or you could register a comparison function that is called when adding
>> to a list. (I prefer @name and @depends fields so that it can be easily
>> maintained.)
> 
> This looks too complex. It would make sense only
> when this API has more users.
> 
> Also this won't be enough for the ftrace/livepatch callbacks.
> They need to be ordered against against each other. But they
> also need to be called before/after all other callbacks.
> For example, when the module is loaded:
> 
>     + 1st frace
>     + 2nd livepatch
>     + then other notifiers
> 
> See the commit c1bf08ac26e92122 ("ftrace: Be first to run code
> modification on modules").
> 
>> This would allow for better dependency building when adding to the list.
>   
>>>
>>> A concrete example is the ordering dependency between ftrace and
>>> livepatch during module load/unload. see the detail here [1].
>>
>> If this only concerns notification callback issues with the ftrace
>> and livepatch modules, it's far more robust to simply call the
>> necessary processing directly when the modules load and unload,
>> rather than registering notification callbacks externally.
>>
>> There are fprobe, kprobe and its trace-events, all of them are using
>> ftrace as its fundation layer. In this case, I always needs to
>> consider callback order when a module is unloaded.
>>
>> If ftrace is working as a part of module callbacks, it will conflict
>> with fprobe/kprobe module callback. Of course we can reorder it with
>> modifying its priority. But this is ugly, because when we introduce
>> a new other feature which depends on another layer, we need to
>> reorder the callback's priority number on the list.
>>
>> Based on the above, I don't think this can be resolved simply by
>> changing the list of notification callbacks to a bidirectional list.
> 
> I agree. I would keep it as is (hardcoded).
> 
> Best Regards,
> Petr
> 


Thanks for the feedback, the necessity doesn't convincing enough. I will 
try the proposal from Masami Hiramatsu.

Best regards,

Song


^ permalink raw reply

* [PATCH] md/raid1: fix len reuse across rdevs in choose_first_rdev()
From: Abd-Alrhman Masalkhi @ 2026-04-26  9:35 UTC (permalink / raw)
  To: song, yukuai, paul.e.luse, xni
  Cc: linux-raid, linux-kernel, Abd-Alrhman Masalkhi

choose_first_rdev() initializes the variable len before iterating over
all rdevs, but passes it by reference to raid1_check_read_range(), which
it might update *len and return 0 depending on the layout of the bad
block region. As a result, 'len' can be modified during the first
iteration and reused for subsequent rdevs, causing later devices to be
evaluated with an incorrect length value.

Fixes: 31a73331752d3 ("md/raid1: factor out read_first_rdev() from read_balance()")
Signed-off-by: Abd-Alrhman Masalkhi <abd.masalkhi@gmail.com>
---
 drivers/md/raid1.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
index b549be9174bb..5f5dbf79c903 100644
--- a/drivers/md/raid1.c
+++ b/drivers/md/raid1.c
@@ -591,12 +591,12 @@ static int choose_first_rdev(struct r1conf *conf, struct r1bio *r1_bio,
 			     int *max_sectors)
 {
 	sector_t this_sector = r1_bio->sector;
-	int len = r1_bio->sectors;
 	int disk;
 
 	for (disk = 0 ; disk < conf->raid_disks * 2 ; disk++) {
 		struct md_rdev *rdev;
 		int read_len;
+		int len = r1_bio->sectors;
 
 		if (r1_bio->bios[disk] == IO_BLOCKED)
 			continue;
-- 
2.43.0


^ permalink raw reply related

* [RFC] md/raid1: possible deadlock introduced in raid1_read_request()
From: Abd-Alrhman Masalkhi @ 2026-04-25 14:29 UTC (permalink / raw)
  To: song, yukuai; +Cc: linux-raid, linux-kernel

hi,

The raid1_read_request() function splits a bio regardless of whether it
is an original bio or an md-cloned bio (regardless of the r1bio_existed
value).

When an md_cloned_bio is resubmitted, raid1_read_request() treats it as a
new original bio instead of recognizing it as an md_cloned_bio.

If I understand this correctly, this results in allocating a new r1bio,
etc. More importantly, this may lead to a deadlock if we tried to 
suspended the array before the md driver calls percpu_ref_tryget_live()
(&mddev->active_io) on the path down to pers->make_request().

I am considering two possible approaches, ending the bio if max_sectors
is smaller than bio_sectors(bio), or modifing read_balance() to select
a disk that can handle the full bio if r1bio_existed was set.

Is this understanding correct? and What might be the preferred approach
in this case?

Thanks,
Abd-Alrhman

^ permalink raw reply

* Re: [PATCH v2] raid6: arm64: add SVE optimized implementation for syndrome generation
From: Demian Shulhan @ 2026-04-25 11:46 UTC (permalink / raw)
  To: Mark Brown
  Cc: Ard Biesheuvel, Robin Murphy, Christoph Hellwig, Mark Rutland,
	Song Liu, Yu Kuai, Will Deacon, Catalin Marinas, linux-arm-kernel,
	Li Nan, linux-raid, linux-kernel
In-Reply-To: <2c2eb793-c0a4-4b65-93f1-0ece5cf820d7@sirena.org.uk>

Hi all!

In the future I will try to look at optimisation of current algorithms
instead of SVE realisation, looks like there are few interesting
cases, I will share details in future changes.

Thanks all for the discussion!

пт, 17 квіт. 2026 р. о 18:36 Mark Brown <broonie@kernel.org> пише:
>
> On Fri, Apr 17, 2026 at 04:43:06PM +0200, Ard Biesheuvel wrote:
>
> > On arm64, kernel mode NEON is mostly used to gain access to AES and SHA
> > instructions, and only to a lesser degree to speed up ordinary
> > arithmetic, and so XOR is somewhat of an outlier here.
>
> > Given that Neoverse V1 apparently already carves up ordinary arithmetic
> > performed on 256-bit vectors and operates on 128 bits at a time, I am
> > rather skeptical that we're likely to see any SVE implementations of the
> > crypto extensions soon that are meaningfully faster, given that these
> > are presumably much costlier to implement in terms of gate count, and
> > therefore likely to be split up even on SVE implementations that can
> > perform ordinary arithmetic on 256+ bit vectors in a single cycle. Note
> > that even the arm64 SIMD accelerated CRC implementations rely heavily on
> > 64x64->128 polynomial multiplication.
>
> I'd not be surprised to see something that delivers useful benefits
> using SVE at some point.
>
> > IOW, before we consider kernel mode SVE, I'd like to see some benchmarks
> > for other algorithms too.
>
> Definitely, it needs a solid win to merge anything.  I do want to get
> back to the situation where we've got out of tree infrastructure patches
> so that people working on algorithms have something to base their work
> on (and see the overheads using SVE incurs) but unless theres's a
> practical user they should stay out of tree.

^ permalink raw reply

* [PATCH] md: skip redundant raid_disks update when value is unchanged
From: Abd-Alrhman Masalkhi @ 2026-04-25  8:58 UTC (permalink / raw)
  To: song, yukuai; +Cc: linux-raid, linux-kernel, Abd-Alrhman Masalkhi

Calling update_raid_disks() with the same value as the current one
can trigger unnecessary work. For example, RAID1 will reallocate
resources such as the mempool for r1bio.

Signed-off-by: Abd-Alrhman Masalkhi <abd.masalkhi@gmail.com>
---
It returns -EINVAL for the same value. If silent success is preferred
instead, please let me know so I adjust its behavior.
---
 drivers/md/md.c | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/drivers/md/md.c b/drivers/md/md.c
index 0e55639211f2..cb66c4ebbafa 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -4409,9 +4409,12 @@ raid_disks_store(struct mddev *mddev, const char *buf, size_t len)
 	err = mddev_suspend_and_lock(mddev);
 	if (err)
 		return err;
-	if (mddev->pers)
-		err = update_raid_disks(mddev, n);
-	else if (mddev->reshape_position != MaxSector) {
+	if (mddev->pers) {
+		if (n != mddev->raid_disks)
+			err = update_raid_disks(mddev, n);
+		else
+			err = -EINVAL;
+	} else if (mddev->reshape_position != MaxSector) {
 		struct md_rdev *rdev;
 		int olddisks = mddev->raid_disks - mddev->delta_disks;
 
-- 
2.43.0


^ permalink raw reply related

* Re: [PATCH v13 0/3] md/md-bitmap: restore bitmap grow through sysfs
From: Su Yue @ 2026-04-25  8:41 UTC (permalink / raw)
  To: Yu Kuai; +Cc: Song Liu, glass.su, Li Nan, Xiao Ni, linux-raid, linux-kernel
In-Reply-To: <20260425024615.1696892-1-yukuai@fnnas.com>

On Sat 25 Apr 2026 at 10:46, Yu Kuai <yukuai@fnnas.com> wrote:

> mdadm --grow adds an internal bitmap by writing bitmap/location 
> for an
> array that currently has no bitmap. That requires the bitmap 
> directory
> and location attribute to exist before the classic bitmap 
> backend is
> created.
>
> This series separates bitmap backend lifetime from bitmap sysfs 
> lifetime,
> splits the sysfs layout into common and backend-specific groups, 
> and adds
> a small "none" bitmap backend. The none backend keeps 
> bitmap/location
> available while no real bitmap is active, and the location store 
> path can
> then switch between the none backend and the classic bitmap 
> backend
> without tearing down the common bitmap sysfs directory.
>
> Patch 1 factors bitmap creation and destruction into helpers 
> that do not
> touch sysfs registration.
>
> Patch 2 splits the classic bitmap sysfs files into a common 
> group and an
> internal-bitmap group, and converts bitmap backend operations to 
> use a
> sysfs group array.
>
> Patch 3 adds the none backend and uses it to restore mdadm 
> --grow bitmap
> addition through bitmap/location.
>
> Changes since v12:
> - Keep the factoring patch focused on no-sysfs bitmap lifetime 
> helpers.
> - Make bitmap operation lookup depend only on the current bitmap 
> id
>   matching the installed backend.
> - Trim the none backend to only the operations required by the 
> active
>   call paths.
> - Rework bitmap/location error handling with explicit cleanup 
> labels.
> - Restore the none backend after bitmap removal and 
> creation/load
>   failures so bitmap/location stays available.
>
> Validation:
>   - create a RAID1 array with --bitmap=none
>   - verify /sys/block/md0/md/bitmap/location exists and reports 
>   "none"
>   - mdadm --grow /dev/md0 --bitmap=internal
>   - verify location switches to "+8", mdadm reports "Intent 
>   Bitmap:
>     Internal", and /proc/mdstat reports a bitmap
>   - mdadm --grow /dev/md0 --bitmap=none
>   - verify location switches back to "none" and only the common 
>   location
>     attribute remains under md/bitmap
>   - repeat the internal/none switch once more
> - Checked the QEMU serial log for panic, Oops, BUG, WARNING, 
> Call Trace,
>   RCU stall, and hung-task patterns; none were found.
>
> Yu Kuai (3):
>   md: factor bitmap creation away from sysfs handling
>   md/md-bitmap: split bitmap sysfs groups
>   md/md-bitmap: add a none backend for bitmap grow
>

Thanks for all.
Would you like to add tag Fixes: fb8cc3b0d9db for the whole series 
while merging?

--
Su

>  drivers/md/md-bitmap.c   | 131 
>  +++++++++++++++++++++++++++++++++++----
>  drivers/md/md-bitmap.h   |   2 +-
>  drivers/md/md-llbitmap.c |   7 ++-
>  drivers/md/md.c          | 125 
>  ++++++++++++++++++++++++++-----------
>  drivers/md/md.h          |   3 +
>  5 files changed, 218 insertions(+), 50 deletions(-)
>
> base-commit: c85d314b135ff569c1031f2ef8e40368bcfe72ac

^ permalink raw reply

* Re: [PATCH v13 3/3] md/md-bitmap: add a none backend for bitmap grow
From: Su Yue @ 2026-04-25  8:39 UTC (permalink / raw)
  To: Yu Kuai; +Cc: Song Liu, glass.su, Li Nan, Xiao Ni, linux-raid, linux-kernel
In-Reply-To: <20260425024615.1696892-4-yukuai@fnnas.com>

On Sat 25 Apr 2026 at 10:46, Yu Kuai <yukuai@fnnas.com> wrote:

> Add a real none bitmap backend that exposes the common bitmap 
> sysfs
> group and use it to keep bitmap/location available when an array 
> has no
> bitmap.
>
> Then switch the bitmap location sysfs path to move only between 
> none
> and the classic bitmap backend, using the no-sysfs bitmap 
> helpers while
> merging or unmerging the internal bitmap sysfs group.
>
> This restores mdadm --grow bitmap addition through 
> bitmap/location.
>
> Signed-off-by: Yu Kuai <yukuai@fnnas.com>
>

Reviewed-by: Su Yue <glass.su@suse.com>

> ---
>  drivers/md/md-bitmap.c | 108 
>  ++++++++++++++++++++++++++++++++++++++---
>  drivers/md/md.c        |  42 +++++++++++++---
>  drivers/md/md.h        |   3 ++
>  3 files changed, 137 insertions(+), 16 deletions(-)
>
> diff --git a/drivers/md/md-bitmap.c b/drivers/md/md-bitmap.c
> index eba649703a1c..028b9ca8ce52 100644
> --- a/drivers/md/md-bitmap.c
> +++ b/drivers/md/md-bitmap.c
> @@ -216,6 +216,7 @@ struct bitmap {
>  };
>
>  static struct workqueue_struct *md_bitmap_wq;
> +static struct attribute_group md_bitmap_internal_group;
>
>  static int __bitmap_resize(struct bitmap *bitmap, sector_t 
>  blocks,
>  			   int chunksize, bool init);
> @@ -2580,6 +2581,30 @@ static int bitmap_resize(struct mddev 
> *mddev, sector_t blocks, int chunksize)
>  	return __bitmap_resize(bitmap, blocks, chunksize, false);
>  }
>
> +static bool bitmap_none_enabled(void *data, bool flush)
> +{
> +	return false;
> +}
> +
> +static int bitmap_none_create(struct mddev *mddev)
> +{
> +	return 0;
> +}
> +
> +static int bitmap_none_load(struct mddev *mddev)
> +{
> +	return 0;
> +}
> +
> +static void bitmap_none_destroy(struct mddev *mddev)
> +{
> +}
> +
> +static int bitmap_none_get_stats(void *data, struct 
> md_bitmap_stats *stats)
> +{
> +	return -ENOENT;
> +}
> +
>  static ssize_t
>  location_show(struct mddev *mddev, char *page)
>  {
> @@ -2618,7 +2643,11 @@ location_store(struct mddev *mddev, const 
> char *buf, size_t len)
>  			goto out;
>  		}
>
> -		bitmap_destroy(mddev);
> +		sysfs_unmerge_group(&mddev->kobj, 
> &md_bitmap_internal_group);
> +		md_bitmap_destroy_nosysfs(mddev);
> +		mddev->bitmap_id = ID_BITMAP_NONE;
> +		if (!mddev_set_bitmap_ops_nosysfs(mddev))
> +			goto none_err;
>  		mddev->bitmap_info.offset = 0;
>  		if (mddev->bitmap_info.file) {
>  			struct file *f = mddev->bitmap_info.file;
> @@ -2654,16 +2683,25 @@ location_store(struct mddev *mddev, 
> const char *buf, size_t len)
>  			}
>
>  			mddev->bitmap_info.offset = offset;
> -			rv = bitmap_create(mddev);
> +			md_bitmap_destroy_nosysfs(mddev);
> +			mddev->bitmap_id = ID_BITMAP;
> +			if (!mddev_set_bitmap_ops_nosysfs(mddev))
> +				goto bitmap_err;
> +
> +			rv = md_bitmap_create_nosysfs(mddev);
>  			if (rv)
> -				goto out;
> +				goto create_err;
>
> -			rv = bitmap_load(mddev);
> +			rv = mddev->bitmap_ops->load(mddev);
>  			if (rv) {
>  				mddev->bitmap_info.offset = 0;
> -				bitmap_destroy(mddev);
> -				goto out;
> +				goto load_err;
>  			}
> +
> +			rv = sysfs_merge_group(&mddev->kobj,
> +					       &md_bitmap_internal_group);
> +			if (rv)
> +				goto merge_err;
>  		}
>  	}
>  	if (!mddev->external) {
> @@ -2679,6 +2717,22 @@ location_store(struct mddev *mddev, const 
> char *buf, size_t len)
>  	if (rv)
>  		return rv;
>  	return len;
> +
> +merge_err:
> +	mddev->bitmap_info.offset = 0;
> +load_err:
> +	md_bitmap_destroy_nosysfs(mddev);
> +create_err:
> +	mddev->bitmap_info.offset = 0;
> +	mddev->bitmap_id = ID_BITMAP_NONE;
> +	if (!mddev_set_bitmap_ops_nosysfs(mddev))
> +		rv = -ENOENT;
> +	goto out;
> +bitmap_err:
> +	rv = -ENOENT;
> +none_err:
> +	mddev->bitmap_info.offset = 0;
> +	goto out;
>  }
>
>  static struct md_sysfs_entry bitmap_location =
> @@ -2987,6 +3041,27 @@ static const struct attribute_group 
> *bitmap_groups[] = {
>  	NULL,
>  };
>
> +static const struct attribute_group *bitmap_none_groups[] = {
> +	&md_bitmap_common_group,
> +	NULL,
> +};
> +
> +static struct bitmap_operations bitmap_none_ops = {
> +	.head = {
> +		.type	= MD_BITMAP,
> +		.id	= ID_BITMAP_NONE,
> +		.name	= "none",
> +	},
> +
> +	.enabled		= bitmap_none_enabled,
> +	.create			= bitmap_none_create,
> +	.load			= bitmap_none_load,
> +	.destroy		= bitmap_none_destroy,
> +	.get_stats		= bitmap_none_get_stats,
> +
> +	.groups			= bitmap_none_groups,
> +};
> +
>  static struct bitmap_operations bitmap_ops = {
>  	.head = {
>  		.type	= MD_BITMAP,
> @@ -3033,16 +3108,33 @@ static struct bitmap_operations 
> bitmap_ops = {
>
>  int md_bitmap_init(void)
>  {
> +	int err;
> +
>  	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);
> +	err = register_md_submodule(&bitmap_none_ops.head);
> +	if (err)
> +		goto err_wq;
> +
> +	err = register_md_submodule(&bitmap_ops.head);
> +	if (err)
> +		goto err_none;
> +
> +	return 0;
> +
> +err_none:
> +	unregister_md_submodule(&bitmap_none_ops.head);
> +err_wq:
> +	destroy_workqueue(md_bitmap_wq);
> +	return err;
>  }
>
>  void md_bitmap_exit(void)
>  {
> -	destroy_workqueue(md_bitmap_wq);
>  	unregister_md_submodule(&bitmap_ops.head);
> +	unregister_md_submodule(&bitmap_none_ops.head);
> +	destroy_workqueue(md_bitmap_wq);
>  }
> diff --git a/drivers/md/md.c b/drivers/md/md.c
> index 0ef81d116191..7937b927d923 100644
> --- a/drivers/md/md.c
> +++ b/drivers/md/md.c
> @@ -705,7 +705,7 @@ static void md_bitmap_sysfs_del(struct mddev 
> *mddev)
>  	sysfs_remove_group(&mddev->kobj, 
>  mddev->bitmap_ops->groups[0]);
>  }
>
> -static bool mddev_set_bitmap_ops_nosysfs(struct mddev *mddev)
> +bool mddev_set_bitmap_ops_nosysfs(struct mddev *mddev)
>  {
>  	struct md_submodule_head *head;
>
> @@ -4275,7 +4275,7 @@ bitmap_type_show(struct mddev *mddev, char 
> *page)
>
>  	xa_lock(&md_submodule);
>  	xa_for_each(&md_submodule, i, head) {
> -		if (head->type != MD_BITMAP)
> +		if (head->type != MD_BITMAP || head->id == ID_BITMAP_NONE)
>  			continue;
>
>  		if (mddev->bitmap_id == head->id)
> @@ -6535,7 +6535,7 @@ static enum md_submodule_id 
> md_bitmap_get_id_from_sb(struct mddev *mddev)
>  	return id;
>  }
>
> -static int md_bitmap_create_nosysfs(struct mddev *mddev)
> +int md_bitmap_create_nosysfs(struct mddev *mddev)
>  {
>  	enum md_submodule_id orig_id = mddev->bitmap_id;
>  	enum md_submodule_id sb_id;
> @@ -6544,8 +6544,10 @@ static int 
> md_bitmap_create_nosysfs(struct mddev *mddev)
>  	if (mddev->bitmap_id == ID_BITMAP_NONE)
>  		return -EINVAL;
>
> -	if (!mddev_set_bitmap_ops_nosysfs(mddev))
> +	if (!mddev_set_bitmap_ops_nosysfs(mddev)) {
> +		mddev->bitmap_id = orig_id;
>  		return -ENOENT;
> +	}
>
>  	err = mddev->bitmap_ops->create(mddev);
>  	if (!err)
> @@ -6559,8 +6561,10 @@ static int 
> md_bitmap_create_nosysfs(struct mddev *mddev)
>  	mddev->bitmap_ops = NULL;
>
>  	sb_id = md_bitmap_get_id_from_sb(mddev);
> -	if (sb_id == ID_BITMAP_NONE || sb_id == orig_id)
> +	if (sb_id == ID_BITMAP_NONE || sb_id == orig_id) {
> +		mddev->bitmap_id = orig_id;
>  		return err;
> +	}
>
>  	pr_info("md: %s: bitmap version mismatch, switching from %d to 
>  %d\n",
>  		mdname(mddev), orig_id, sb_id);
> @@ -6594,7 +6598,7 @@ static int md_bitmap_create(struct mddev 
> *mddev)
>  	return 0;
>  }
>
> -static void md_bitmap_destroy_nosysfs(struct mddev *mddev)
> +void md_bitmap_destroy_nosysfs(struct mddev *mddev)
>  {
>  	if (!md_bitmap_registered(mddev))
>  		return;
> @@ -6612,6 +6616,16 @@ static void md_bitmap_destroy(struct 
> mddev *mddev)
>  	md_bitmap_destroy_nosysfs(mddev);
>  }
>
> +static void md_bitmap_set_none(struct mddev *mddev)
> +{
> +	mddev->bitmap_id = ID_BITMAP_NONE;
> +	if (!mddev_set_bitmap_ops_nosysfs(mddev))
> +		return;
> +
> +	if (!mddev_is_dm(mddev) && mddev->bitmap_ops->groups)
> +		md_bitmap_sysfs_add(mddev);
> +}
> +
>  int md_run(struct mddev *mddev)
>  {
>  	int err;
> @@ -6821,6 +6835,10 @@ int md_run(struct mddev *mddev)
>  	if (mddev->sb_flags)
>  		md_update_sb(mddev, 0);
>
> +	if (IS_ENABLED(CONFIG_MD_BITMAP) && !mddev->bitmap_info.file 
> &&
> +	    !mddev->bitmap_info.offset)
> +		md_bitmap_set_none(mddev);
> +
>  	md_new_event();
>  	return 0;
>
> @@ -7766,7 +7784,8 @@ static int set_bitmap_file(struct mddev 
> *mddev, int fd)
>  {
>  	int err = 0;
>
> -	if (!md_bitmap_registered(mddev))
> +	if (!md_bitmap_registered(mddev) ||
> +	    mddev->bitmap_id == ID_BITMAP_NONE)
>  		return -EINVAL;
>
>  	if (mddev->pers) {
> @@ -7831,10 +7850,12 @@ static int set_bitmap_file(struct mddev 
> *mddev, int fd)
>
>  			if (err) {
>  				md_bitmap_destroy(mddev);
> +				md_bitmap_set_none(mddev);
>  				fd = -1;
>  			}
>  		} else if (fd < 0) {
>  			md_bitmap_destroy(mddev);
> +			md_bitmap_set_none(mddev);
>  		}
>  	}
>
> @@ -8141,12 +8162,16 @@ static int update_array_info(struct 
> mddev *mddev, mdu_array_info_t *info)
>  				mddev->bitmap_info.default_offset;
>  			mddev->bitmap_info.space =
>  				mddev->bitmap_info.default_space;
> +			mddev->bitmap_id = ID_BITMAP;
>  			rv = md_bitmap_create(mddev);
>  			if (!rv)
>  				rv = mddev->bitmap_ops->load(mddev);
>
> -			if (rv)
> +			if (rv) {
>  				md_bitmap_destroy(mddev);
> +				mddev->bitmap_info.offset = 0;
> +				md_bitmap_set_none(mddev);
> +			}
>  		} else {
>  			struct md_bitmap_stats stats;
>
> @@ -8174,6 +8199,7 @@ static int update_array_info(struct mddev 
> *mddev, mdu_array_info_t *info)
>  			}
>  			md_bitmap_destroy(mddev);
>  			mddev->bitmap_info.offset = 0;
> +			md_bitmap_set_none(mddev);
>  		}
>  	}
>  	md_update_sb(mddev, 1);
> diff --git a/drivers/md/md.h b/drivers/md/md.h
> index d3d4e2150dc8..52c378086046 100644
> --- a/drivers/md/md.h
> +++ b/drivers/md/md.h
> @@ -934,6 +934,9 @@ extern void md_allow_write(struct mddev 
> *mddev);
>  extern void md_wait_for_blocked_rdev(struct md_rdev *rdev, 
>  struct mddev *mddev);
>  extern void md_set_array_sectors(struct mddev *mddev, sector_t 
>  array_sectors);
>  extern int md_check_no_bitmap(struct mddev *mddev);
> +bool mddev_set_bitmap_ops_nosysfs(struct mddev *mddev);
> +int md_bitmap_create_nosysfs(struct mddev *mddev);
> +void md_bitmap_destroy_nosysfs(struct mddev *mddev);
>  extern int md_integrity_register(struct mddev *mddev);
>  extern int strict_strtoul_scaled(const char *cp, unsigned long 
>  *res, int scale);

^ permalink raw reply

* Re: [PATCH v13 2/3] md/md-bitmap: split bitmap sysfs groups
From: Su Yue @ 2026-04-25  8:36 UTC (permalink / raw)
  To: Yu Kuai; +Cc: Song Liu, glass.su, Li Nan, Xiao Ni, linux-raid, linux-kernel
In-Reply-To: <20260425024615.1696892-3-yukuai@fnnas.com>

On Sat 25 Apr 2026 at 10:46, Yu Kuai <yukuai@fnnas.com> wrote:

> Split the classic bitmap sysfs files into a common bitmap group 
> with
> the location attribute and a separate internal bitmap group for 
> the
> remaining files.
>
> At the same time, convert bitmap operations from a single sysfs 
> group
> to a sysfs group array so backends can share part of their sysfs
> layout while adding backend-specific attributes separately.
>
> Switch the bitmap sysfs helpers to use sysfs_update_groups() for 
> the
> add and update path, and remove groups in reverse order so 
> shared named
> groups are unmerged before the last group removes the directory.
>
> Also make bitmap operation lookup depend only on the currently 
> selected
> bitmap id matching the installed backend. This prepares the 
> lookup path
> for a later registered none backend.
>
> Signed-off-by: Yu Kuai <yukuai@fnnas.com>
> ---
>  drivers/md/md-bitmap.c   | 23 +++++++++++++++++++----
>  drivers/md/md-bitmap.h   |  2 +-
>  drivers/md/md-llbitmap.c |  7 ++++++-
>  drivers/md/md.c          | 21 ++++++++++++++-------
>  4 files changed, 40 insertions(+), 13 deletions(-)
>
> diff --git a/drivers/md/md-bitmap.c b/drivers/md/md-bitmap.c
> index 83378c033c72..eba649703a1c 100644
> --- a/drivers/md/md-bitmap.c
> +++ b/drivers/md/md-bitmap.c
> @@ -2955,8 +2955,12 @@ 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[] = {
> +static struct attribute *md_bitmap_common_attrs[] = {
>  	&bitmap_location.attr,
> +	NULL
> +};
> +
> +static struct attribute *md_bitmap_internal_attrs[] = {
>  	&bitmap_space.attr,
>  	&bitmap_timeout.attr,
>  	&bitmap_backlog.attr,
> @@ -2967,9 +2971,20 @@ static struct attribute 
> *md_bitmap_attrs[] = {
>  	NULL
>  };
>
> -static struct attribute_group md_bitmap_group = {
> +static struct attribute_group md_bitmap_common_group = {
> +	.name = "bitmap",
> +	.attrs = md_bitmap_common_attrs,
> +};
> +
> +static struct attribute_group md_bitmap_internal_group = {
>  	.name = "bitmap",
> -	.attrs = md_bitmap_attrs,
> +	.attrs = md_bitmap_internal_attrs,
> +};
> +
> +static const struct attribute_group *bitmap_groups[] = {
> +	&md_bitmap_common_group,
> +	&md_bitmap_internal_group,
> +	NULL,
>  };
>
>  static struct bitmap_operations bitmap_ops = {
> @@ -3013,7 +3028,7 @@ static struct bitmap_operations bitmap_ops 
> = {
>  	.set_pages		= bitmap_set_pages,
>  	.free			= md_bitmap_free,
>
> -	.group			= &md_bitmap_group,
> +	.groups			= bitmap_groups,
>  };
>
>  int md_bitmap_init(void)
> diff --git a/drivers/md/md-bitmap.h b/drivers/md/md-bitmap.h
> index b42a28fa83a0..214f623c7e79 100644
> --- a/drivers/md/md-bitmap.h
> +++ b/drivers/md/md-bitmap.h
> @@ -125,7 +125,7 @@ struct bitmap_operations {
>  	void (*set_pages)(void *data, unsigned long pages);
>  	void (*free)(void *data);
>
> -	struct attribute_group *group;
> +	const struct attribute_group **groups;
>  };
>
>  /* the bitmap API */
> diff --git a/drivers/md/md-llbitmap.c b/drivers/md/md-llbitmap.c
> index 9e7e6b1a6f15..1adc5b117821 100644
> --- a/drivers/md/md-llbitmap.c
> +++ b/drivers/md/md-llbitmap.c
> @@ -1738,6 +1738,11 @@ static struct attribute_group 
> md_llbitmap_group = {
>  	.attrs = md_llbitmap_attrs,
>  };
>
> +static const struct attribute_group *md_llbitmap_groups[] = {
> +	&md_llbitmap_group,
> +	NULL,
> +};
> +
>  static struct bitmap_operations llbitmap_ops = {
>  	.head = {
>  		.type	= MD_BITMAP,
> @@ -1774,7 +1779,7 @@ static struct bitmap_operations 
> llbitmap_ops = {
>  	.dirty_bits		= llbitmap_dirty_bits,
>  	.write_all		= llbitmap_write_all,
>
> -	.group			= &md_llbitmap_group,
> +	.groups			= md_llbitmap_groups,
>  };
>
>  int md_llbitmap_init(void)
> diff --git a/drivers/md/md.c b/drivers/md/md.c
> index 99aa1367c991..0ef81d116191 100644
> --- a/drivers/md/md.c
> +++ b/drivers/md/md.c
> @@ -681,7 +681,7 @@ static void no_op(struct percpu_ref *r) {}
>
>  static void md_bitmap_sysfs_add(struct mddev *mddev)
>  {
> -	if (sysfs_create_group(&mddev->kobj, 
> mddev->bitmap_ops->group))
> +	if (sysfs_update_groups(&mddev->kobj, 
> mddev->bitmap_ops->groups))
>  		pr_warn("md: cannot register extra bitmap attributes for 
>  %s\n",
>  			mdname(mddev));
>  	else
> @@ -694,16 +694,23 @@ static void md_bitmap_sysfs_add(struct 
> mddev *mddev)
>
>  static void md_bitmap_sysfs_del(struct mddev *mddev)
>  {
> -	sysfs_remove_group(&mddev->kobj, mddev->bitmap_ops->group);
> +	int nr_groups = 0;
> +
> +	for (nr_groups = 0; mddev->bitmap_ops->groups[nr_groups]; 
> nr_groups++)
> +		;
> +
> +	while (--nr_groups >= 1)
> +		sysfs_unmerge_group(&mddev->kobj,
> +				    mddev->bitmap_ops->groups[nr_groups]);
>
Amazing magic here!

Reviewed-by: Su Yue <glass.su@suse.com>

> +	sysfs_remove_group(&mddev->kobj, 
> mddev->bitmap_ops->groups[0]);
>  }
>
>  static bool mddev_set_bitmap_ops_nosysfs(struct mddev *mddev)
>  {
> -	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 (mddev->bitmap_ops &&
> +	    mddev->bitmap_ops->head.id == mddev->bitmap_id)
>  		return true;
>
>  	xa_lock(&md_submodule);
> @@ -6581,7 +6588,7 @@ static int md_bitmap_create(struct mddev 
> *mddev)
>  	if (err)
>  		return err;
>
> -	if (!mddev_is_dm(mddev) && mddev->bitmap_ops->group)
> +	if (!mddev_is_dm(mddev) && mddev->bitmap_ops->groups)
>  		md_bitmap_sysfs_add(mddev);
>
>  	return 0;
> @@ -6599,7 +6606,7 @@ static void 
> md_bitmap_destroy_nosysfs(struct mddev *mddev)
>  static void md_bitmap_destroy(struct mddev *mddev)
>  {
>  	if (!mddev_is_dm(mddev) && mddev->bitmap_ops &&
> -	    mddev->bitmap_ops->group)
> +	    mddev->bitmap_ops->groups)
>  		md_bitmap_sysfs_del(mddev);
>
>  	md_bitmap_destroy_nosysfs(mddev);

^ permalink raw reply

* Re: [PATCH v13 1/3] md: factor bitmap creation away from sysfs handling
From: Su Yue @ 2026-04-25  8:30 UTC (permalink / raw)
  To: Yu Kuai; +Cc: Song Liu, glass.su, Li Nan, Xiao Ni, linux-raid, linux-kernel
In-Reply-To: <20260425024615.1696892-2-yukuai@fnnas.com>

On Sat 25 Apr 2026 at 10:46, Yu Kuai <yukuai@fnnas.com> wrote:

> Factor bitmap creation and destruction into helpers that do not 
> touch
> bitmap sysfs registration.
>
> This prepares the bitmap sysfs rework so callers such as the 
> sysfs
> bitmap location path can create or destroy a bitmap backend 
> without
> coupling that to sysfs group lifetime management.
>
> Signed-off-by: Yu Kuai <yukuai@fnnas.com>
>
Reviewed-by: Su Yue <glass.su@suse.com>

> ---
>  drivers/md/md.c | 78 
>  +++++++++++++++++++++++++++++++------------------
>  1 file changed, 49 insertions(+), 29 deletions(-)
>
> diff --git a/drivers/md/md.c b/drivers/md/md.c
> index ebaf47fb9de6..99aa1367c991 100644
> --- a/drivers/md/md.c
> +++ b/drivers/md/md.c
> @@ -679,7 +679,25 @@ static void active_io_release(struct 
> percpu_ref *ref)
>
>  static void no_op(struct percpu_ref *r) {}
>
> -static bool mddev_set_bitmap_ops(struct mddev *mddev)
> +static void md_bitmap_sysfs_add(struct mddev *mddev)
> +{
> +	if (sysfs_create_group(&mddev->kobj, 
> mddev->bitmap_ops->group))
> +		pr_warn("md: cannot register extra bitmap attributes for 
> %s\n",
> +			mdname(mddev));
> +	else
> +		/*
> +		 * Inform user with KOBJ_CHANGE about new bitmap
> +		 * attributes.
> +		 */
> +		kobject_uevent(&mddev->kobj, KOBJ_CHANGE);
> +}
> +
> +static void md_bitmap_sysfs_del(struct mddev *mddev)
> +{
> +	sysfs_remove_group(&mddev->kobj, mddev->bitmap_ops->group);
> +}
> +
> +static bool mddev_set_bitmap_ops_nosysfs(struct mddev *mddev)
>  {
>  	struct bitmap_operations *old = mddev->bitmap_ops;
>  	struct md_submodule_head *head;
> @@ -703,18 +721,6 @@ static bool mddev_set_bitmap_ops(struct 
> mddev *mddev)
>
>  	mddev->bitmap_ops = (void *)head;
>  	xa_unlock(&md_submodule);
> -
> -	if (!mddev_is_dm(mddev) && mddev->bitmap_ops->group) {
> -		if (sysfs_create_group(&mddev->kobj, 
> mddev->bitmap_ops->group))
> -			pr_warn("md: cannot register extra bitmap attributes 
> for %s\n",
> -				mdname(mddev));
> -		else
> -			/*
> -			 * Inform user with KOBJ_CHANGE about new bitmap
> -			 * attributes.
> -			 */
> -			kobject_uevent(&mddev->kobj, KOBJ_CHANGE);
> -	}
>  	return true;
>
>  err:
> @@ -722,15 +728,6 @@ static bool mddev_set_bitmap_ops(struct 
> mddev *mddev)
>  	return false;
>  }
>
> -static void mddev_clear_bitmap_ops(struct mddev *mddev)
> -{
> -	if (!mddev_is_dm(mddev) && mddev->bitmap_ops &&
> -	    mddev->bitmap_ops->group)
> -		sysfs_remove_group(&mddev->kobj, 
> mddev->bitmap_ops->group);
> -
> -	mddev->bitmap_ops = NULL;
> -}
> -
>  int mddev_init(struct mddev *mddev)
>  {
>  	int err = 0;
> @@ -6531,7 +6528,7 @@ static enum md_submodule_id 
> md_bitmap_get_id_from_sb(struct mddev *mddev)
>  	return id;
>  }
>
> -static int md_bitmap_create(struct mddev *mddev)
> +static int md_bitmap_create_nosysfs(struct mddev *mddev)
>  {
>  	enum md_submodule_id orig_id = mddev->bitmap_id;
>  	enum md_submodule_id sb_id;
> @@ -6540,7 +6537,7 @@ static int md_bitmap_create(struct mddev 
> *mddev)
>  	if (mddev->bitmap_id == ID_BITMAP_NONE)
>  		return -EINVAL;
>
> -	if (!mddev_set_bitmap_ops(mddev))
> +	if (!mddev_set_bitmap_ops_nosysfs(mddev))
>  		return -ENOENT;
>
>  	err = mddev->bitmap_ops->create(mddev);
> @@ -6552,7 +6549,7 @@ static int md_bitmap_create(struct mddev 
> *mddev)
>  	 * 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);
> +	mddev->bitmap_ops = NULL;
>
>  	sb_id = md_bitmap_get_id_from_sb(mddev);
>  	if (sb_id == ID_BITMAP_NONE || sb_id == orig_id)
> @@ -6562,27 +6559,50 @@ static int md_bitmap_create(struct mddev 
> *mddev)
>  		mdname(mddev), orig_id, sb_id);
>
>  	mddev->bitmap_id = sb_id;
> -	if (!mddev_set_bitmap_ops(mddev)) {
> +	if (!mddev_set_bitmap_ops_nosysfs(mddev)) {
>  		mddev->bitmap_id = orig_id;
>  		return -ENOENT;
>  	}
>
>  	err = mddev->bitmap_ops->create(mddev);
>  	if (err) {
> -		mddev_clear_bitmap_ops(mddev);
> +		mddev->bitmap_ops = NULL;
>  		mddev->bitmap_id = orig_id;
>  	}
>
>  	return err;
>  }
>
> -static void md_bitmap_destroy(struct mddev *mddev)
> +static int md_bitmap_create(struct mddev *mddev)
> +{
> +	int err;
> +
> +	err = md_bitmap_create_nosysfs(mddev);
> +	if (err)
> +		return err;
> +
> +	if (!mddev_is_dm(mddev) && mddev->bitmap_ops->group)
> +		md_bitmap_sysfs_add(mddev);
> +
> +	return 0;
> +}
> +
> +static void md_bitmap_destroy_nosysfs(struct mddev *mddev)
>  {
>  	if (!md_bitmap_registered(mddev))
>  		return;
>
>  	mddev->bitmap_ops->destroy(mddev);
> -	mddev_clear_bitmap_ops(mddev);
> +	mddev->bitmap_ops = NULL;
> +}
> +
> +static void md_bitmap_destroy(struct mddev *mddev)
> +{
> +	if (!mddev_is_dm(mddev) && mddev->bitmap_ops &&
> +	    mddev->bitmap_ops->group)
> +		md_bitmap_sysfs_del(mddev);
> +
> +	md_bitmap_destroy_nosysfs(mddev);
>  }
>
>  int md_run(struct mddev *mddev)

^ permalink raw reply

* Re: [PATCH v13 0/3] md/md-bitmap: restore bitmap grow through sysfs
From: Yu Kuai @ 2026-04-25  2:49 UTC (permalink / raw)
  To: Song Liu; +Cc: glass.su, Li Nan, Xiao Ni, linux-raid, linux-kernel, yukuai
In-Reply-To: <20260425024615.1696892-1-yukuai@fnnas.com>

Hi,

Please just ignore the v13, it's local version by ai that I forgot to remove.

在 2026/4/25 10:46, Yu Kuai 写道:
> mdadm --grow adds an internal bitmap by writing bitmap/location for an
> array that currently has no bitmap. That requires the bitmap directory
> and location attribute to exist before the classic bitmap backend is
> created.
>
> This series separates bitmap backend lifetime from bitmap sysfs lifetime,
> splits the sysfs layout into common and backend-specific groups, and adds
> a small "none" bitmap backend. The none backend keeps bitmap/location
> available while no real bitmap is active, and the location store path can
> then switch between the none backend and the classic bitmap backend
> without tearing down the common bitmap sysfs directory.
>
> Patch 1 factors bitmap creation and destruction into helpers that do not
> touch sysfs registration.
>
> Patch 2 splits the classic bitmap sysfs files into a common group and an
> internal-bitmap group, and converts bitmap backend operations to use a
> sysfs group array.
>
> Patch 3 adds the none backend and uses it to restore mdadm --grow bitmap
> addition through bitmap/location.
>
> Changes since v12:
> - Keep the factoring patch focused on no-sysfs bitmap lifetime helpers.
> - Make bitmap operation lookup depend only on the current bitmap id
>    matching the installed backend.
> - Trim the none backend to only the operations required by the active
>    call paths.
> - Rework bitmap/location error handling with explicit cleanup labels.
> - Restore the none backend after bitmap removal and creation/load
>    failures so bitmap/location stays available.
>
> Validation:
>    - create a RAID1 array with --bitmap=none
>    - verify /sys/block/md0/md/bitmap/location exists and reports "none"
>    - mdadm --grow /dev/md0 --bitmap=internal
>    - verify location switches to "+8", mdadm reports "Intent Bitmap:
>      Internal", and /proc/mdstat reports a bitmap
>    - mdadm --grow /dev/md0 --bitmap=none
>    - verify location switches back to "none" and only the common location
>      attribute remains under md/bitmap
>    - repeat the internal/none switch once more
> - Checked the QEMU serial log for panic, Oops, BUG, WARNING, Call Trace,
>    RCU stall, and hung-task patterns; none were found.
>
> Yu Kuai (3):
>    md: factor bitmap creation away from sysfs handling
>    md/md-bitmap: split bitmap sysfs groups
>    md/md-bitmap: add a none backend for bitmap grow
>
>   drivers/md/md-bitmap.c   | 131 +++++++++++++++++++++++++++++++++++----
>   drivers/md/md-bitmap.h   |   2 +-
>   drivers/md/md-llbitmap.c |   7 ++-
>   drivers/md/md.c          | 125 ++++++++++++++++++++++++++-----------
>   drivers/md/md.h          |   3 +
>   5 files changed, 218 insertions(+), 50 deletions(-)
>
> base-commit: c85d314b135ff569c1031f2ef8e40368bcfe72ac

-- 
Thansk,
Kuai

^ permalink raw reply

* [PATCH v13 3/3] md/md-bitmap: add a none backend for bitmap grow
From: Yu Kuai @ 2026-04-25  2:46 UTC (permalink / raw)
  To: Song Liu; +Cc: glass.su, Yu Kuai, Li Nan, Xiao Ni, linux-raid, linux-kernel
In-Reply-To: <20260425024615.1696892-1-yukuai@fnnas.com>

Add a real none bitmap backend that exposes the common bitmap sysfs
group and use it to keep bitmap/location available when an array has no
bitmap.

Then switch the bitmap location sysfs path to move only between none
and the classic bitmap backend, using the no-sysfs bitmap helpers while
merging or unmerging the internal bitmap sysfs group.

This restores mdadm --grow bitmap addition through bitmap/location.

Signed-off-by: Yu Kuai <yukuai@fnnas.com>
---
 drivers/md/md-bitmap.c | 108 ++++++++++++++++++++++++++++++++++++++---
 drivers/md/md.c        |  42 +++++++++++++---
 drivers/md/md.h        |   3 ++
 3 files changed, 137 insertions(+), 16 deletions(-)

diff --git a/drivers/md/md-bitmap.c b/drivers/md/md-bitmap.c
index eba649703a1c..028b9ca8ce52 100644
--- a/drivers/md/md-bitmap.c
+++ b/drivers/md/md-bitmap.c
@@ -216,6 +216,7 @@ struct bitmap {
 };
 
 static struct workqueue_struct *md_bitmap_wq;
+static struct attribute_group md_bitmap_internal_group;
 
 static int __bitmap_resize(struct bitmap *bitmap, sector_t blocks,
 			   int chunksize, bool init);
@@ -2580,6 +2581,30 @@ static int bitmap_resize(struct mddev *mddev, sector_t blocks, int chunksize)
 	return __bitmap_resize(bitmap, blocks, chunksize, false);
 }
 
+static bool bitmap_none_enabled(void *data, bool flush)
+{
+	return false;
+}
+
+static int bitmap_none_create(struct mddev *mddev)
+{
+	return 0;
+}
+
+static int bitmap_none_load(struct mddev *mddev)
+{
+	return 0;
+}
+
+static void bitmap_none_destroy(struct mddev *mddev)
+{
+}
+
+static int bitmap_none_get_stats(void *data, struct md_bitmap_stats *stats)
+{
+	return -ENOENT;
+}
+
 static ssize_t
 location_show(struct mddev *mddev, char *page)
 {
@@ -2618,7 +2643,11 @@ location_store(struct mddev *mddev, const char *buf, size_t len)
 			goto out;
 		}
 
-		bitmap_destroy(mddev);
+		sysfs_unmerge_group(&mddev->kobj, &md_bitmap_internal_group);
+		md_bitmap_destroy_nosysfs(mddev);
+		mddev->bitmap_id = ID_BITMAP_NONE;
+		if (!mddev_set_bitmap_ops_nosysfs(mddev))
+			goto none_err;
 		mddev->bitmap_info.offset = 0;
 		if (mddev->bitmap_info.file) {
 			struct file *f = mddev->bitmap_info.file;
@@ -2654,16 +2683,25 @@ location_store(struct mddev *mddev, const char *buf, size_t len)
 			}
 
 			mddev->bitmap_info.offset = offset;
-			rv = bitmap_create(mddev);
+			md_bitmap_destroy_nosysfs(mddev);
+			mddev->bitmap_id = ID_BITMAP;
+			if (!mddev_set_bitmap_ops_nosysfs(mddev))
+				goto bitmap_err;
+
+			rv = md_bitmap_create_nosysfs(mddev);
 			if (rv)
-				goto out;
+				goto create_err;
 
-			rv = bitmap_load(mddev);
+			rv = mddev->bitmap_ops->load(mddev);
 			if (rv) {
 				mddev->bitmap_info.offset = 0;
-				bitmap_destroy(mddev);
-				goto out;
+				goto load_err;
 			}
+
+			rv = sysfs_merge_group(&mddev->kobj,
+					       &md_bitmap_internal_group);
+			if (rv)
+				goto merge_err;
 		}
 	}
 	if (!mddev->external) {
@@ -2679,6 +2717,22 @@ location_store(struct mddev *mddev, const char *buf, size_t len)
 	if (rv)
 		return rv;
 	return len;
+
+merge_err:
+	mddev->bitmap_info.offset = 0;
+load_err:
+	md_bitmap_destroy_nosysfs(mddev);
+create_err:
+	mddev->bitmap_info.offset = 0;
+	mddev->bitmap_id = ID_BITMAP_NONE;
+	if (!mddev_set_bitmap_ops_nosysfs(mddev))
+		rv = -ENOENT;
+	goto out;
+bitmap_err:
+	rv = -ENOENT;
+none_err:
+	mddev->bitmap_info.offset = 0;
+	goto out;
 }
 
 static struct md_sysfs_entry bitmap_location =
@@ -2987,6 +3041,27 @@ static const struct attribute_group *bitmap_groups[] = {
 	NULL,
 };
 
+static const struct attribute_group *bitmap_none_groups[] = {
+	&md_bitmap_common_group,
+	NULL,
+};
+
+static struct bitmap_operations bitmap_none_ops = {
+	.head = {
+		.type	= MD_BITMAP,
+		.id	= ID_BITMAP_NONE,
+		.name	= "none",
+	},
+
+	.enabled		= bitmap_none_enabled,
+	.create			= bitmap_none_create,
+	.load			= bitmap_none_load,
+	.destroy		= bitmap_none_destroy,
+	.get_stats		= bitmap_none_get_stats,
+
+	.groups			= bitmap_none_groups,
+};
+
 static struct bitmap_operations bitmap_ops = {
 	.head = {
 		.type	= MD_BITMAP,
@@ -3033,16 +3108,33 @@ static struct bitmap_operations bitmap_ops = {
 
 int md_bitmap_init(void)
 {
+	int err;
+
 	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);
+	err = register_md_submodule(&bitmap_none_ops.head);
+	if (err)
+		goto err_wq;
+
+	err = register_md_submodule(&bitmap_ops.head);
+	if (err)
+		goto err_none;
+
+	return 0;
+
+err_none:
+	unregister_md_submodule(&bitmap_none_ops.head);
+err_wq:
+	destroy_workqueue(md_bitmap_wq);
+	return err;
 }
 
 void md_bitmap_exit(void)
 {
-	destroy_workqueue(md_bitmap_wq);
 	unregister_md_submodule(&bitmap_ops.head);
+	unregister_md_submodule(&bitmap_none_ops.head);
+	destroy_workqueue(md_bitmap_wq);
 }
diff --git a/drivers/md/md.c b/drivers/md/md.c
index 0ef81d116191..7937b927d923 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -705,7 +705,7 @@ static void md_bitmap_sysfs_del(struct mddev *mddev)
 	sysfs_remove_group(&mddev->kobj, mddev->bitmap_ops->groups[0]);
 }
 
-static bool mddev_set_bitmap_ops_nosysfs(struct mddev *mddev)
+bool mddev_set_bitmap_ops_nosysfs(struct mddev *mddev)
 {
 	struct md_submodule_head *head;
 
@@ -4275,7 +4275,7 @@ bitmap_type_show(struct mddev *mddev, char *page)
 
 	xa_lock(&md_submodule);
 	xa_for_each(&md_submodule, i, head) {
-		if (head->type != MD_BITMAP)
+		if (head->type != MD_BITMAP || head->id == ID_BITMAP_NONE)
 			continue;
 
 		if (mddev->bitmap_id == head->id)
@@ -6535,7 +6535,7 @@ static enum md_submodule_id md_bitmap_get_id_from_sb(struct mddev *mddev)
 	return id;
 }
 
-static int md_bitmap_create_nosysfs(struct mddev *mddev)
+int md_bitmap_create_nosysfs(struct mddev *mddev)
 {
 	enum md_submodule_id orig_id = mddev->bitmap_id;
 	enum md_submodule_id sb_id;
@@ -6544,8 +6544,10 @@ static int md_bitmap_create_nosysfs(struct mddev *mddev)
 	if (mddev->bitmap_id == ID_BITMAP_NONE)
 		return -EINVAL;
 
-	if (!mddev_set_bitmap_ops_nosysfs(mddev))
+	if (!mddev_set_bitmap_ops_nosysfs(mddev)) {
+		mddev->bitmap_id = orig_id;
 		return -ENOENT;
+	}
 
 	err = mddev->bitmap_ops->create(mddev);
 	if (!err)
@@ -6559,8 +6561,10 @@ static int md_bitmap_create_nosysfs(struct mddev *mddev)
 	mddev->bitmap_ops = NULL;
 
 	sb_id = md_bitmap_get_id_from_sb(mddev);
-	if (sb_id == ID_BITMAP_NONE || sb_id == orig_id)
+	if (sb_id == ID_BITMAP_NONE || sb_id == orig_id) {
+		mddev->bitmap_id = orig_id;
 		return err;
+	}
 
 	pr_info("md: %s: bitmap version mismatch, switching from %d to %d\n",
 		mdname(mddev), orig_id, sb_id);
@@ -6594,7 +6598,7 @@ static int md_bitmap_create(struct mddev *mddev)
 	return 0;
 }
 
-static void md_bitmap_destroy_nosysfs(struct mddev *mddev)
+void md_bitmap_destroy_nosysfs(struct mddev *mddev)
 {
 	if (!md_bitmap_registered(mddev))
 		return;
@@ -6612,6 +6616,16 @@ static void md_bitmap_destroy(struct mddev *mddev)
 	md_bitmap_destroy_nosysfs(mddev);
 }
 
+static void md_bitmap_set_none(struct mddev *mddev)
+{
+	mddev->bitmap_id = ID_BITMAP_NONE;
+	if (!mddev_set_bitmap_ops_nosysfs(mddev))
+		return;
+
+	if (!mddev_is_dm(mddev) && mddev->bitmap_ops->groups)
+		md_bitmap_sysfs_add(mddev);
+}
+
 int md_run(struct mddev *mddev)
 {
 	int err;
@@ -6821,6 +6835,10 @@ int md_run(struct mddev *mddev)
 	if (mddev->sb_flags)
 		md_update_sb(mddev, 0);
 
+	if (IS_ENABLED(CONFIG_MD_BITMAP) && !mddev->bitmap_info.file &&
+	    !mddev->bitmap_info.offset)
+		md_bitmap_set_none(mddev);
+
 	md_new_event();
 	return 0;
 
@@ -7766,7 +7784,8 @@ static int set_bitmap_file(struct mddev *mddev, int fd)
 {
 	int err = 0;
 
-	if (!md_bitmap_registered(mddev))
+	if (!md_bitmap_registered(mddev) ||
+	    mddev->bitmap_id == ID_BITMAP_NONE)
 		return -EINVAL;
 
 	if (mddev->pers) {
@@ -7831,10 +7850,12 @@ static int set_bitmap_file(struct mddev *mddev, int fd)
 
 			if (err) {
 				md_bitmap_destroy(mddev);
+				md_bitmap_set_none(mddev);
 				fd = -1;
 			}
 		} else if (fd < 0) {
 			md_bitmap_destroy(mddev);
+			md_bitmap_set_none(mddev);
 		}
 	}
 
@@ -8141,12 +8162,16 @@ static int update_array_info(struct mddev *mddev, mdu_array_info_t *info)
 				mddev->bitmap_info.default_offset;
 			mddev->bitmap_info.space =
 				mddev->bitmap_info.default_space;
+			mddev->bitmap_id = ID_BITMAP;
 			rv = md_bitmap_create(mddev);
 			if (!rv)
 				rv = mddev->bitmap_ops->load(mddev);
 
-			if (rv)
+			if (rv) {
 				md_bitmap_destroy(mddev);
+				mddev->bitmap_info.offset = 0;
+				md_bitmap_set_none(mddev);
+			}
 		} else {
 			struct md_bitmap_stats stats;
 
@@ -8174,6 +8199,7 @@ static int update_array_info(struct mddev *mddev, mdu_array_info_t *info)
 			}
 			md_bitmap_destroy(mddev);
 			mddev->bitmap_info.offset = 0;
+			md_bitmap_set_none(mddev);
 		}
 	}
 	md_update_sb(mddev, 1);
diff --git a/drivers/md/md.h b/drivers/md/md.h
index d3d4e2150dc8..52c378086046 100644
--- a/drivers/md/md.h
+++ b/drivers/md/md.h
@@ -934,6 +934,9 @@ extern void md_allow_write(struct mddev *mddev);
 extern void md_wait_for_blocked_rdev(struct md_rdev *rdev, struct mddev *mddev);
 extern void md_set_array_sectors(struct mddev *mddev, sector_t array_sectors);
 extern int md_check_no_bitmap(struct mddev *mddev);
+bool mddev_set_bitmap_ops_nosysfs(struct mddev *mddev);
+int md_bitmap_create_nosysfs(struct mddev *mddev);
+void md_bitmap_destroy_nosysfs(struct mddev *mddev);
 extern int md_integrity_register(struct mddev *mddev);
 extern int strict_strtoul_scaled(const char *cp, unsigned long *res, int scale);
 
-- 
2.51.0


^ permalink raw reply related

* [PATCH v13 2/3] md/md-bitmap: split bitmap sysfs groups
From: Yu Kuai @ 2026-04-25  2:46 UTC (permalink / raw)
  To: Song Liu; +Cc: glass.su, Yu Kuai, Li Nan, Xiao Ni, linux-raid, linux-kernel
In-Reply-To: <20260425024615.1696892-1-yukuai@fnnas.com>

Split the classic bitmap sysfs files into a common bitmap group with
the location attribute and a separate internal bitmap group for the
remaining files.

At the same time, convert bitmap operations from a single sysfs group
to a sysfs group array so backends can share part of their sysfs
layout while adding backend-specific attributes separately.

Switch the bitmap sysfs helpers to use sysfs_update_groups() for the
add and update path, and remove groups in reverse order so shared named
groups are unmerged before the last group removes the directory.

Also make bitmap operation lookup depend only on the currently selected
bitmap id matching the installed backend. This prepares the lookup path
for a later registered none backend.

Signed-off-by: Yu Kuai <yukuai@fnnas.com>
---
 drivers/md/md-bitmap.c   | 23 +++++++++++++++++++----
 drivers/md/md-bitmap.h   |  2 +-
 drivers/md/md-llbitmap.c |  7 ++++++-
 drivers/md/md.c          | 21 ++++++++++++++-------
 4 files changed, 40 insertions(+), 13 deletions(-)

diff --git a/drivers/md/md-bitmap.c b/drivers/md/md-bitmap.c
index 83378c033c72..eba649703a1c 100644
--- a/drivers/md/md-bitmap.c
+++ b/drivers/md/md-bitmap.c
@@ -2955,8 +2955,12 @@ 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[] = {
+static struct attribute *md_bitmap_common_attrs[] = {
 	&bitmap_location.attr,
+	NULL
+};
+
+static struct attribute *md_bitmap_internal_attrs[] = {
 	&bitmap_space.attr,
 	&bitmap_timeout.attr,
 	&bitmap_backlog.attr,
@@ -2967,9 +2971,20 @@ static struct attribute *md_bitmap_attrs[] = {
 	NULL
 };
 
-static struct attribute_group md_bitmap_group = {
+static struct attribute_group md_bitmap_common_group = {
+	.name = "bitmap",
+	.attrs = md_bitmap_common_attrs,
+};
+
+static struct attribute_group md_bitmap_internal_group = {
 	.name = "bitmap",
-	.attrs = md_bitmap_attrs,
+	.attrs = md_bitmap_internal_attrs,
+};
+
+static const struct attribute_group *bitmap_groups[] = {
+	&md_bitmap_common_group,
+	&md_bitmap_internal_group,
+	NULL,
 };
 
 static struct bitmap_operations bitmap_ops = {
@@ -3013,7 +3028,7 @@ static struct bitmap_operations bitmap_ops = {
 	.set_pages		= bitmap_set_pages,
 	.free			= md_bitmap_free,
 
-	.group			= &md_bitmap_group,
+	.groups			= bitmap_groups,
 };
 
 int md_bitmap_init(void)
diff --git a/drivers/md/md-bitmap.h b/drivers/md/md-bitmap.h
index b42a28fa83a0..214f623c7e79 100644
--- a/drivers/md/md-bitmap.h
+++ b/drivers/md/md-bitmap.h
@@ -125,7 +125,7 @@ struct bitmap_operations {
 	void (*set_pages)(void *data, unsigned long pages);
 	void (*free)(void *data);
 
-	struct attribute_group *group;
+	const struct attribute_group **groups;
 };
 
 /* the bitmap API */
diff --git a/drivers/md/md-llbitmap.c b/drivers/md/md-llbitmap.c
index 9e7e6b1a6f15..1adc5b117821 100644
--- a/drivers/md/md-llbitmap.c
+++ b/drivers/md/md-llbitmap.c
@@ -1738,6 +1738,11 @@ static struct attribute_group md_llbitmap_group = {
 	.attrs = md_llbitmap_attrs,
 };
 
+static const struct attribute_group *md_llbitmap_groups[] = {
+	&md_llbitmap_group,
+	NULL,
+};
+
 static struct bitmap_operations llbitmap_ops = {
 	.head = {
 		.type	= MD_BITMAP,
@@ -1774,7 +1779,7 @@ static struct bitmap_operations llbitmap_ops = {
 	.dirty_bits		= llbitmap_dirty_bits,
 	.write_all		= llbitmap_write_all,
 
-	.group			= &md_llbitmap_group,
+	.groups			= md_llbitmap_groups,
 };
 
 int md_llbitmap_init(void)
diff --git a/drivers/md/md.c b/drivers/md/md.c
index 99aa1367c991..0ef81d116191 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -681,7 +681,7 @@ static void no_op(struct percpu_ref *r) {}
 
 static void md_bitmap_sysfs_add(struct mddev *mddev)
 {
-	if (sysfs_create_group(&mddev->kobj, mddev->bitmap_ops->group))
+	if (sysfs_update_groups(&mddev->kobj, mddev->bitmap_ops->groups))
 		pr_warn("md: cannot register extra bitmap attributes for %s\n",
 			mdname(mddev));
 	else
@@ -694,16 +694,23 @@ static void md_bitmap_sysfs_add(struct mddev *mddev)
 
 static void md_bitmap_sysfs_del(struct mddev *mddev)
 {
-	sysfs_remove_group(&mddev->kobj, mddev->bitmap_ops->group);
+	int nr_groups = 0;
+
+	for (nr_groups = 0; mddev->bitmap_ops->groups[nr_groups]; nr_groups++)
+		;
+
+	while (--nr_groups >= 1)
+		sysfs_unmerge_group(&mddev->kobj,
+				    mddev->bitmap_ops->groups[nr_groups]);
+	sysfs_remove_group(&mddev->kobj, mddev->bitmap_ops->groups[0]);
 }
 
 static bool mddev_set_bitmap_ops_nosysfs(struct mddev *mddev)
 {
-	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 (mddev->bitmap_ops &&
+	    mddev->bitmap_ops->head.id == mddev->bitmap_id)
 		return true;
 
 	xa_lock(&md_submodule);
@@ -6581,7 +6588,7 @@ static int md_bitmap_create(struct mddev *mddev)
 	if (err)
 		return err;
 
-	if (!mddev_is_dm(mddev) && mddev->bitmap_ops->group)
+	if (!mddev_is_dm(mddev) && mddev->bitmap_ops->groups)
 		md_bitmap_sysfs_add(mddev);
 
 	return 0;
@@ -6599,7 +6606,7 @@ static void md_bitmap_destroy_nosysfs(struct mddev *mddev)
 static void md_bitmap_destroy(struct mddev *mddev)
 {
 	if (!mddev_is_dm(mddev) && mddev->bitmap_ops &&
-	    mddev->bitmap_ops->group)
+	    mddev->bitmap_ops->groups)
 		md_bitmap_sysfs_del(mddev);
 
 	md_bitmap_destroy_nosysfs(mddev);
-- 
2.51.0


^ permalink raw reply related

* [PATCH v13 1/3] md: factor bitmap creation away from sysfs handling
From: Yu Kuai @ 2026-04-25  2:46 UTC (permalink / raw)
  To: Song Liu; +Cc: glass.su, Yu Kuai, Li Nan, Xiao Ni, linux-raid, linux-kernel
In-Reply-To: <20260425024615.1696892-1-yukuai@fnnas.com>

Factor bitmap creation and destruction into helpers that do not touch
bitmap sysfs registration.

This prepares the bitmap sysfs rework so callers such as the sysfs
bitmap location path can create or destroy a bitmap backend without
coupling that to sysfs group lifetime management.

Signed-off-by: Yu Kuai <yukuai@fnnas.com>
---
 drivers/md/md.c | 78 +++++++++++++++++++++++++++++++------------------
 1 file changed, 49 insertions(+), 29 deletions(-)

diff --git a/drivers/md/md.c b/drivers/md/md.c
index ebaf47fb9de6..99aa1367c991 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -679,7 +679,25 @@ static void active_io_release(struct percpu_ref *ref)
 
 static void no_op(struct percpu_ref *r) {}
 
-static bool mddev_set_bitmap_ops(struct mddev *mddev)
+static void md_bitmap_sysfs_add(struct mddev *mddev)
+{
+	if (sysfs_create_group(&mddev->kobj, mddev->bitmap_ops->group))
+		pr_warn("md: cannot register extra bitmap attributes for %s\n",
+			mdname(mddev));
+	else
+		/*
+		 * Inform user with KOBJ_CHANGE about new bitmap
+		 * attributes.
+		 */
+		kobject_uevent(&mddev->kobj, KOBJ_CHANGE);
+}
+
+static void md_bitmap_sysfs_del(struct mddev *mddev)
+{
+	sysfs_remove_group(&mddev->kobj, mddev->bitmap_ops->group);
+}
+
+static bool mddev_set_bitmap_ops_nosysfs(struct mddev *mddev)
 {
 	struct bitmap_operations *old = mddev->bitmap_ops;
 	struct md_submodule_head *head;
@@ -703,18 +721,6 @@ static bool mddev_set_bitmap_ops(struct mddev *mddev)
 
 	mddev->bitmap_ops = (void *)head;
 	xa_unlock(&md_submodule);
-
-	if (!mddev_is_dm(mddev) && mddev->bitmap_ops->group) {
-		if (sysfs_create_group(&mddev->kobj, mddev->bitmap_ops->group))
-			pr_warn("md: cannot register extra bitmap attributes for %s\n",
-				mdname(mddev));
-		else
-			/*
-			 * Inform user with KOBJ_CHANGE about new bitmap
-			 * attributes.
-			 */
-			kobject_uevent(&mddev->kobj, KOBJ_CHANGE);
-	}
 	return true;
 
 err:
@@ -722,15 +728,6 @@ static bool mddev_set_bitmap_ops(struct mddev *mddev)
 	return false;
 }
 
-static void mddev_clear_bitmap_ops(struct mddev *mddev)
-{
-	if (!mddev_is_dm(mddev) && mddev->bitmap_ops &&
-	    mddev->bitmap_ops->group)
-		sysfs_remove_group(&mddev->kobj, mddev->bitmap_ops->group);
-
-	mddev->bitmap_ops = NULL;
-}
-
 int mddev_init(struct mddev *mddev)
 {
 	int err = 0;
@@ -6531,7 +6528,7 @@ static enum md_submodule_id md_bitmap_get_id_from_sb(struct mddev *mddev)
 	return id;
 }
 
-static int md_bitmap_create(struct mddev *mddev)
+static int md_bitmap_create_nosysfs(struct mddev *mddev)
 {
 	enum md_submodule_id orig_id = mddev->bitmap_id;
 	enum md_submodule_id sb_id;
@@ -6540,7 +6537,7 @@ static int md_bitmap_create(struct mddev *mddev)
 	if (mddev->bitmap_id == ID_BITMAP_NONE)
 		return -EINVAL;
 
-	if (!mddev_set_bitmap_ops(mddev))
+	if (!mddev_set_bitmap_ops_nosysfs(mddev))
 		return -ENOENT;
 
 	err = mddev->bitmap_ops->create(mddev);
@@ -6552,7 +6549,7 @@ static int md_bitmap_create(struct mddev *mddev)
 	 * 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);
+	mddev->bitmap_ops = NULL;
 
 	sb_id = md_bitmap_get_id_from_sb(mddev);
 	if (sb_id == ID_BITMAP_NONE || sb_id == orig_id)
@@ -6562,27 +6559,50 @@ static int md_bitmap_create(struct mddev *mddev)
 		mdname(mddev), orig_id, sb_id);
 
 	mddev->bitmap_id = sb_id;
-	if (!mddev_set_bitmap_ops(mddev)) {
+	if (!mddev_set_bitmap_ops_nosysfs(mddev)) {
 		mddev->bitmap_id = orig_id;
 		return -ENOENT;
 	}
 
 	err = mddev->bitmap_ops->create(mddev);
 	if (err) {
-		mddev_clear_bitmap_ops(mddev);
+		mddev->bitmap_ops = NULL;
 		mddev->bitmap_id = orig_id;
 	}
 
 	return err;
 }
 
-static void md_bitmap_destroy(struct mddev *mddev)
+static int md_bitmap_create(struct mddev *mddev)
+{
+	int err;
+
+	err = md_bitmap_create_nosysfs(mddev);
+	if (err)
+		return err;
+
+	if (!mddev_is_dm(mddev) && mddev->bitmap_ops->group)
+		md_bitmap_sysfs_add(mddev);
+
+	return 0;
+}
+
+static void md_bitmap_destroy_nosysfs(struct mddev *mddev)
 {
 	if (!md_bitmap_registered(mddev))
 		return;
 
 	mddev->bitmap_ops->destroy(mddev);
-	mddev_clear_bitmap_ops(mddev);
+	mddev->bitmap_ops = NULL;
+}
+
+static void md_bitmap_destroy(struct mddev *mddev)
+{
+	if (!mddev_is_dm(mddev) && mddev->bitmap_ops &&
+	    mddev->bitmap_ops->group)
+		md_bitmap_sysfs_del(mddev);
+
+	md_bitmap_destroy_nosysfs(mddev);
 }
 
 int md_run(struct mddev *mddev)
-- 
2.51.0


^ permalink raw reply related

* [PATCH v13 0/3] md/md-bitmap: restore bitmap grow through sysfs
From: Yu Kuai @ 2026-04-25  2:46 UTC (permalink / raw)
  To: Song Liu; +Cc: glass.su, Yu Kuai, Li Nan, Xiao Ni, linux-raid, linux-kernel

mdadm --grow adds an internal bitmap by writing bitmap/location for an
array that currently has no bitmap. That requires the bitmap directory
and location attribute to exist before the classic bitmap backend is
created.

This series separates bitmap backend lifetime from bitmap sysfs lifetime,
splits the sysfs layout into common and backend-specific groups, and adds
a small "none" bitmap backend. The none backend keeps bitmap/location
available while no real bitmap is active, and the location store path can
then switch between the none backend and the classic bitmap backend
without tearing down the common bitmap sysfs directory.

Patch 1 factors bitmap creation and destruction into helpers that do not
touch sysfs registration.

Patch 2 splits the classic bitmap sysfs files into a common group and an
internal-bitmap group, and converts bitmap backend operations to use a
sysfs group array.

Patch 3 adds the none backend and uses it to restore mdadm --grow bitmap
addition through bitmap/location.

Changes since v12:
- Keep the factoring patch focused on no-sysfs bitmap lifetime helpers.
- Make bitmap operation lookup depend only on the current bitmap id
  matching the installed backend.
- Trim the none backend to only the operations required by the active
  call paths.
- Rework bitmap/location error handling with explicit cleanup labels.
- Restore the none backend after bitmap removal and creation/load
  failures so bitmap/location stays available.

Validation:
  - create a RAID1 array with --bitmap=none
  - verify /sys/block/md0/md/bitmap/location exists and reports "none"
  - mdadm --grow /dev/md0 --bitmap=internal
  - verify location switches to "+8", mdadm reports "Intent Bitmap:
    Internal", and /proc/mdstat reports a bitmap
  - mdadm --grow /dev/md0 --bitmap=none
  - verify location switches back to "none" and only the common location
    attribute remains under md/bitmap
  - repeat the internal/none switch once more
- Checked the QEMU serial log for panic, Oops, BUG, WARNING, Call Trace,
  RCU stall, and hung-task patterns; none were found.

Yu Kuai (3):
  md: factor bitmap creation away from sysfs handling
  md/md-bitmap: split bitmap sysfs groups
  md/md-bitmap: add a none backend for bitmap grow

 drivers/md/md-bitmap.c   | 131 +++++++++++++++++++++++++++++++++++----
 drivers/md/md-bitmap.h   |   2 +-
 drivers/md/md-llbitmap.c |   7 ++-
 drivers/md/md.c          | 125 ++++++++++++++++++++++++++-----------
 drivers/md/md.h          |   3 +
 5 files changed, 218 insertions(+), 50 deletions(-)

base-commit: c85d314b135ff569c1031f2ef8e40368bcfe72ac
-- 
2.51.0

^ permalink raw reply

* Re: [PATCH 1/4] md/raid10: prepare per-r10bio dev slot tracking
From: Yu Kuai @ 2026-04-24  7:04 UTC (permalink / raw)
  To: Chen Cheng, linux-raid, yukuai; +Cc: chenchneg33
In-Reply-To: <20260422023317.796326-1-chencheng@fnnas.com>

Hi,

在 2026/4/22 10:33, Chen Cheng 写道:
> From: Chen Cheng <chencheng@fnnas.com>
>
> raid10 reuses r10bio objects from both r10bio_pool and r10buf_pool. Track
> the number of devs[] slots used by each request in the r10bio itself and
> initialize it whenever one of these objects is reused.
>
> No functional change yet. A later patch will use this width when reshape
> changes conf->geo.raid_disks.
> ---
>   drivers/md/raid10.c | 4 ++++
>   drivers/md/raid10.h | 1 +
>   2 files changed, 5 insertions(+)

For patchset please also add a patch 0.

This solution looks incorrect. The usage of r10bio_pool() is wrong in the first
place. Noted for mempool, it will preallocate elements and such elements can be
reused in following mempool allocation. Which means:

1) preallocate elements with old raid disks;
2) rehsape update raid disks;
3) allocate new r10bio, elements from 1) can be used.

The solution can refer to raid1.

1) convert mempool to fixed size;
2) during reshape, suspend/quiesce the array first to wait for all prallocated
r10bios to return first.

>
> diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
> index 0653b5d8545a..e93933632893 100644
> --- a/drivers/md/raid10.c
> +++ b/drivers/md/raid10.c
> @@ -1540,6 +1540,7 @@ static void __make_request(struct mddev *mddev, struct bio *bio, int sectors)
>   	r10_bio->sector = bio->bi_iter.bi_sector;
>   	r10_bio->state = 0;
>   	r10_bio->read_slot = -1;
> +	r10_bio->used_nr_devs = conf->geo.raid_disks;
>   	memset(r10_bio->devs, 0, sizeof(r10_bio->devs[0]) *
>   			conf->geo.raid_disks);
>   
> @@ -1727,6 +1728,7 @@ static int raid10_handle_discard(struct mddev *mddev, struct bio *bio)
>   	r10_bio->mddev = mddev;
>   	r10_bio->state = 0;
>   	r10_bio->sectors = 0;
> +	r10_bio->used_nr_devs = geo->raid_disks;
>   	memset(r10_bio->devs, 0, sizeof(r10_bio->devs[0]) * geo->raid_disks);
>   	wait_blocked_dev(mddev, r10_bio);
>   
> @@ -3061,6 +3063,8 @@ static struct r10bio *raid10_alloc_init_r10buf(struct r10conf *conf)
>   	else
>   		nalloc = 2; /* recovery */
>   
> +	r10bio->used_nr_devs = nalloc;
> +
>   	for (i = 0; i < nalloc; i++) {
>   		bio = r10bio->devs[i].bio;
>   		rp = bio->bi_private;
> diff --git a/drivers/md/raid10.h b/drivers/md/raid10.h
> index ec79d87fb92f..92e8743023e6 100644
> --- a/drivers/md/raid10.h
> +++ b/drivers/md/raid10.h
> @@ -127,6 +127,7 @@ struct r10bio {
>   	 * if the IO is in READ direction, then this is where we read
>   	 */
>   	int			read_slot;
> +	unsigned int		used_nr_devs;
>   
>   	struct list_head	retry_list;
>   	/*

-- 
Thansk,
Kuai

^ permalink raw reply

* Re: [PATCH 1/4] md/raid10: prepare per-r10bio dev slot tracking
From: Chen Cheng @ 2026-04-24  2:11 UTC (permalink / raw)
  To: Paul Menzel; +Cc: linux-raid, yukuai, chenchneg33
In-Reply-To: <6e6e4340-2181-4a79-9284-7ed167aab807@molgen.mpg.de>

On Wed, Apr 22, 2026 at 08:40:42AM +0200, Paul Menzel wrote:

Hi Paul,

> Dear Cheng,
>
>
> Am 22.04.26 um 04:33 schrieb Chen Cheng:
> > From: Chen Cheng <chencheng@fnnas.com>
> >
> > raid10 reuses r10bio objects from both r10bio_pool and r10buf_pool. Track
> > the number of devs[] slots used by each request in the r10bio itself and
> > initialize it whenever one of these objects is reused.
> >
> > No functional change yet. A later patch will use this width when reshape
> > changes conf->geo.raid_disks.
>
> Your Signed-off-by: line is missing.

Yes, i missed it, thanks for point-out;

>
> > ---
> >   drivers/md/raid10.c | 4 ++++
> >   drivers/md/raid10.h | 1 +
> >   2 files changed, 5 insertions(+)
> >
> > diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
> > index 0653b5d8545a..e93933632893 100644
> > --- a/drivers/md/raid10.c
> > +++ b/drivers/md/raid10.c
> > @@ -1540,6 +1540,7 @@ static void __make_request(struct mddev *mddev, struct bio *bio, int sectors)
> >     r10_bio->sector = bio->bi_iter.bi_sector;
> >     r10_bio->state = 0;
> >     r10_bio->read_slot = -1;
> > +   r10_bio->used_nr_devs = conf->geo.raid_disks;
> >     memset(r10_bio->devs, 0, sizeof(r10_bio->devs[0]) *
> >                     conf->geo.raid_disks);
> > @@ -1727,6 +1728,7 @@ static int raid10_handle_discard(struct mddev *mddev, struct bio *bio)
> >     r10_bio->mddev = mddev;
> >     r10_bio->state = 0;
> >     r10_bio->sectors = 0;
> > +   r10_bio->used_nr_devs = geo->raid_disks;
> >     memset(r10_bio->devs, 0, sizeof(r10_bio->devs[0]) * geo->raid_disks);
> >     wait_blocked_dev(mddev, r10_bio);
> > @@ -3061,6 +3063,8 @@ static struct r10bio *raid10_alloc_init_r10buf(struct r10conf *conf)
> >     else
> >             nalloc = 2; /* recovery */
> > +   r10bio->used_nr_devs = nalloc;
> > +
> >     for (i = 0; i < nalloc; i++) {
> >             bio = r10bio->devs[i].bio;
> >             rp = bio->bi_private;
> > diff --git a/drivers/md/raid10.h b/drivers/md/raid10.h
> > index ec79d87fb92f..92e8743023e6 100644
> > --- a/drivers/md/raid10.h
> > +++ b/drivers/md/raid10.h
> > @@ -127,6 +127,7 @@ struct r10bio {
> >      * if the IO is in READ direction, then this is where we read
> >      */
> >     int                     read_slot;
> > +   unsigned int            used_nr_devs;
>
> Most entries have a comment describing the use. Maybe add one too, or at
> least a blank line, so it’s clear that the existing comment is just for
> `read_slot`?

Agreed.

>
> >     struct list_head        retry_list;
> >     /*
>
> From a performance and resource usage point of view, will increasing the
> struct have a negative impact?

On 64-bit platform, doesn't have negative resource usage impact,
the new field fits into the existing padding after read_slot, so
offsetof(struct r10bio, devs) stays unchanged;

On 32-bit platform, may increase by 4 bytes per r10bio, but that's
negligible compared with the bios/pages allocated for each request;


No negative performance impact, cause bottleneck is IO, and
the IO path has no changed;

>
> The diff looks good.
>
> Reviewed-by: Paul Menzel <pmenzel@molgen.mpg.de>
>

Thanks for review;

>
> Kind regards,
>
> Paul


Thanks,
Cheng

^ permalink raw reply

* [PATCH 3/3] md: use ATTRIBUTE_GROUPS() for md default sysfs attributes
From: Abd-Alrhman Masalkhi @ 2026-04-23 10:13 UTC (permalink / raw)
  To: song, yukuai; +Cc: linux-raid, linux-kernel, Abd-Alrhman Masalkhi
In-Reply-To: <20260423101303.48196-1-abd.masalkhi@gmail.com>

Replace the md_default_group and md_attr_groups with
ATTRIBUTE_GROUPS().

Signed-off-by: Abd-Alrhman Masalkhi <abd.masalkhi@gmail.com>
---
 drivers/md/md.c | 12 ++----------
 1 file changed, 2 insertions(+), 10 deletions(-)

diff --git a/drivers/md/md.c b/drivers/md/md.c
index 346d071c1b8e..0e55639211f2 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -6045,10 +6045,7 @@ static struct attribute *md_default_attrs[] = {
 	&md_logical_block_size.attr,
 	NULL,
 };
-
-static const struct attribute_group md_default_group = {
-	.attrs = md_default_attrs,
-};
+ATTRIBUTE_GROUPS(md_default);
 
 static struct attribute *md_redundancy_attrs[] = {
 	&md_scan_mode.attr,
@@ -6073,11 +6070,6 @@ static const struct attribute_group md_redundancy_group = {
 	.attrs = md_redundancy_attrs,
 };
 
-static const struct attribute_group *md_attr_groups[] = {
-	&md_default_group,
-	NULL,
-};
-
 static ssize_t
 md_attr_show(struct kobject *kobj, struct attribute *attr, char *page)
 {
@@ -6154,7 +6146,7 @@ static const struct sysfs_ops md_sysfs_ops = {
 static const struct kobj_type md_ktype = {
 	.release	= md_kobj_release,
 	.sysfs_ops	= &md_sysfs_ops,
-	.default_groups	= md_attr_groups,
+	.default_groups	= md_default_groups,
 };
 
 int mdp_major = 0;
-- 
2.43.0


^ permalink raw reply related

* [PATCH 2/3] md: use mddev_is_dm() instead of open-coding gendisk checks
From: Abd-Alrhman Masalkhi @ 2026-04-23 10:13 UTC (permalink / raw)
  To: song, yukuai; +Cc: linux-raid, linux-kernel, Abd-Alrhman Masalkhi
In-Reply-To: <20260423101303.48196-1-abd.masalkhi@gmail.com>

Replace direct checks on mddev->gendisk with mddev_is_dm() in
md_handle_request() and md_run().

Signed-off-by: Abd-Alrhman Masalkhi <abd.masalkhi@gmail.com>
---
 drivers/md/md.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/md/md.c b/drivers/md/md.c
index ac71640ff3a8..346d071c1b8e 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -417,7 +417,7 @@ bool md_handle_request(struct mddev *mddev, struct bio *bio)
 
 	if (!mddev->pers->make_request(mddev, bio)) {
 		percpu_ref_put(&mddev->active_io);
-		if (!mddev->gendisk && mddev->pers->prepare_suspend)
+		if (mddev_is_dm(mddev) && mddev->pers->prepare_suspend)
 			return false;
 		goto check_suspended;
 	}
@@ -6584,7 +6584,7 @@ int md_run(struct mddev *mddev)
 	}
 
 	/* dm-raid expect sync_thread to be frozen until resume */
-	if (mddev->gendisk)
+	if (!mddev_is_dm(mddev))
 		mddev->recovery = 0;
 
 	/* may be over-ridden by personality */
-- 
2.43.0


^ permalink raw reply related

* [PATCH 1/3] md/raid1: replace wait loop with wait_event_idle() in raid1_write_request()
From: Abd-Alrhman Masalkhi @ 2026-04-23 10:13 UTC (permalink / raw)
  To: song, yukuai; +Cc: linux-raid, linux-kernel, Abd-Alrhman Masalkhi
In-Reply-To: <20260423101303.48196-1-abd.masalkhi@gmail.com>

The wait loop is equivalent to wait_event_idle() and can be simplified
by usaing it for improving readability.

Signed-off-by: Abd-Alrhman Masalkhi <abd.masalkhi@gmail.com>
---
 drivers/md/raid1.c | 15 ++++-----------
 1 file changed, 4 insertions(+), 11 deletions(-)

diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
index cc9914bd15c1..b549be9174bb 100644
--- a/drivers/md/raid1.c
+++ b/drivers/md/raid1.c
@@ -1488,21 +1488,14 @@ static void raid1_write_request(struct mddev *mddev, struct bio *bio,
 	    mddev->cluster_ops->area_resyncing(mddev, WRITE,
 		     bio->bi_iter.bi_sector, bio_end_sector(bio))) {
 
-		DEFINE_WAIT(w);
 		if (bio->bi_opf & REQ_NOWAIT) {
 			bio_wouldblock_error(bio);
 			return;
 		}
-		for (;;) {
-			prepare_to_wait(&conf->wait_barrier,
-					&w, TASK_IDLE);
-			if (!mddev->cluster_ops->area_resyncing(mddev, WRITE,
-							bio->bi_iter.bi_sector,
-							bio_end_sector(bio)))
-				break;
-			schedule();
-		}
-		finish_wait(&conf->wait_barrier, &w);
+		wait_event_idle(conf->wait_barrier,
+				!mddev->cluster_ops->area_resyncing(mddev, WRITE,
+								    bio->bi_iter.bi_sector,
+								    bio_end_sector(bio)));
 	}
 
 	/*
-- 
2.43.0


^ permalink raw reply related

* [PATCH 0/3] md/raid1: small cleanups in MD and raid1 drivers
From: Abd-Alrhman Masalkhi @ 2026-04-23 10:13 UTC (permalink / raw)
  To: song, yukuai; +Cc: linux-raid, linux-kernel, Abd-Alrhman Masalkhi

Hi,

This small series contains three cleanups in the MD and raid1 drivers.

Thanks,
Abd-Alrhman

Abd-Alrhman Masalkhi (3):
  md/raid1: replace wait loop with wait_event_idle() in
    raid1_write_request()
  md: use mddev_is_dm() instead of open-coding gendisk checks
  md: use ATTRIBUTE_GROUPS() for md default sysfs attributes

 drivers/md/md.c    | 16 ++++------------
 drivers/md/raid1.c | 15 ++++-----------
 2 files changed, 8 insertions(+), 23 deletions(-)

-- 
2.43.0


^ permalink raw reply

* Re: [PATCH 3/8] xor/arm64: Use shared NEON intrinsics implementation from 32-bit ARM
From: Ard Biesheuvel @ 2026-04-23  7:48 UTC (permalink / raw)
  To: Christoph Hellwig, Ard Biesheuvel
  Cc: linux-arm-kernel, linux-crypto, linux-raid, Russell King,
	Arnd Bergmann, Eric Biggers
In-Reply-To: <20260423074614.GB31018@lst.de>



On Thu, 23 Apr 2026, at 09:46, Christoph Hellwig wrote:
>> +extern void __xor_eor3_2(unsigned long bytes, unsigned long * __restrict p1,
>> +		const unsigned long * __restrict p2);
>
> Does the alias magic prevent this from being in a header?


Yes, it emits the ELF symbol for the alias, and this is only permitted
in the compilation unit that defines the original.

> If so a comment
> would be nice, otherwise moving it to a header would be even better.

Ack.

^ permalink raw reply

* Re: [PATCH 7/8] lib/raid6: Include asm/neon-intrinsics.h rather than arm_neon.h
From: Christoph Hellwig @ 2026-04-23  7:47 UTC (permalink / raw)
  To: Ard Biesheuvel
  Cc: linux-arm-kernel, linux-crypto, linux-raid, Ard Biesheuvel,
	Christoph Hellwig, Russell King, Arnd Bergmann, Eric Biggers
In-Reply-To: <20260422171655.3437334-17-ardb+git@google.com>

On Wed, Apr 22, 2026 at 07:17:03PM +0200, Ard Biesheuvel wrote:
> From: Ard Biesheuvel <ardb@kernel.org>
> 
> arm_neon.h is a compiler header which needs some scaffolding to work
> correctly in the linux context, and so it is better not to include it
> directly. Both ARM and arm64 now provide asm/neon-intrinsics.h which
> takes care of this.


This could potentially clash with the raid6 library rework I'm doing
for 7.2. Although git has become pretty good about renamed files, so
maybe it won't be so bad.


^ permalink raw reply

* Re: [PATCH 3/8] xor/arm64: Use shared NEON intrinsics implementation from 32-bit ARM
From: Christoph Hellwig @ 2026-04-23  7:46 UTC (permalink / raw)
  To: Ard Biesheuvel
  Cc: linux-arm-kernel, linux-crypto, linux-raid, Ard Biesheuvel,
	Christoph Hellwig, Russell King, Arnd Bergmann, Eric Biggers
In-Reply-To: <20260422171655.3437334-13-ardb+git@google.com>

> +extern void __xor_eor3_2(unsigned long bytes, unsigned long * __restrict p1,
> +		const unsigned long * __restrict p2);

Does the alias magic prevent this from being in a header?  If so a comment
would be nice, otherwise moving it to a header would be even better.


^ permalink raw reply

* Re: [PATCH 2/8] xor/arm: Replace vectorized implementation with arm64's intrinsics
From: Christoph Hellwig @ 2026-04-23  7:44 UTC (permalink / raw)
  To: Ard Biesheuvel
  Cc: linux-arm-kernel, linux-crypto, linux-raid, Ard Biesheuvel,
	Christoph Hellwig, Russell King, Arnd Bergmann, Eric Biggers
In-Reply-To: <20260422171655.3437334-12-ardb+git@google.com>

Nice!

Acked-by: Christoph Hellwig <hch@lst.de>


^ 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