* [PATCH V2 0/6] null_blk: fix init/exit races, leaks and cleanup
@ 2026-07-07 2:55 Zizhi Wo
2026-07-07 2:55 ` [PATCH V2 1/6] null_blk: use DEFINE_MUTEX for the file-scope mutex Zizhi Wo
` (5 more replies)
0 siblings, 6 replies; 19+ messages in thread
From: Zizhi Wo @ 2026-07-07 2:55 UTC (permalink / raw)
To: axboe, dlemoal, nilay, linux-block, kch, johannes.thumshirn,
kbusch, bvanassche
Cc: linux-kernel, yangerkun, chengzhihao1, wozizhi
This series fixes several issues in null_blk around lock initialization,
concurrent configfs access, and module init/exit cleanup.
Patches 1-2 fix the uninitialized mutex. Following Bart's suggestion, the
fix now uses DEFINE_MUTEX() and renames the lock.
Patch 3 fixes configfs registration concurrency.
Patch 4 reorders resource release in null_exit() to match null_init().
Patch 5 fixes a data race where the NULLB_DEVICE_ATTR macro locklessly
overwrote dev state already set under the lock by apply_fn().
Patch 6 fixes a global tag_set leak on the null_init() error path.
See the individual patch descriptions for details.
Changes since v1:
- Added patches 4-6, and modify the lock name in patch 2.
https://lore.kernel.org/all/20260706123507.3809871-1-wozizhi@huaweicloud.com/
Zizhi Wo (6):
null_blk: use DEFINE_MUTEX for the file-scope mutex
null_blk: give the file-scope mutex a descriptive name
null_blk: register configfs subsystem after creating default devices
null_blk: move unregister_blkdev() after destroying dev in null_exit()
null_blk: don't locklessly overwrite dev state after apply_fn
null_blk: free global tag_set on init error path
drivers/block/null_blk/main.c | 70 +++++++++++++++++------------------
1 file changed, 34 insertions(+), 36 deletions(-)
--
2.52.0
^ permalink raw reply [flat|nested] 19+ messages in thread
* [PATCH V2 1/6] null_blk: use DEFINE_MUTEX for the file-scope mutex
2026-07-07 2:55 [PATCH V2 0/6] null_blk: fix init/exit races, leaks and cleanup Zizhi Wo
@ 2026-07-07 2:55 ` Zizhi Wo
2026-07-07 4:07 ` Damien Le Moal
2026-07-07 2:55 ` [PATCH V2 2/6] null_blk: give the file-scope mutex a descriptive name Zizhi Wo
` (4 subsequent siblings)
5 siblings, 1 reply; 19+ messages in thread
From: Zizhi Wo @ 2026-07-07 2:55 UTC (permalink / raw)
To: axboe, dlemoal, nilay, linux-block, kch, johannes.thumshirn,
kbusch, bvanassche
Cc: linux-kernel, yangerkun, chengzhihao1, wozizhi
From: Zizhi Wo <wozizhi@huawei.com>
In null_init(), mutex_init(&lock) currently happens after
configfs_register_subsystem(), which exposes the nullb subsystem to
userspace. A racing mkdir() into /sys/kernel/config/nullb/ can reach
null_find_dev_by_name() -> mutex_lock(&lock) before the mutex is
initialized, trigger warning:
[ 123.137788] DEBUG_LOCKS_WARN_ON(lock->magic != lock)
[ 123.137796] WARNING: kernel/locking/mutex.c:159 at mutex_lock+0x171/0x1c0, CPU#13: mkdir/1301
[ 123.140090] Modules linked in: null_blk(+) nft_fib_inet nft_fib_ipv4
......
[ 123.154926] Call Trace:
[ 123.155172] <TASK>
[ 123.155419] ? __pfx_mutex_lock+0x10/0x10
[ 123.156181] ? __pfx__raw_spin_lock+0x10/0x10
[ 123.156571] nullb_group_make_group+0x20/0x100 [null_blk]
[ 123.157011] configfs_mkdir+0x47b/0xc70
[ 123.157337] ? __pfx_configfs_mkdir+0x10/0x10
[ 123.157719] ? may_create_dentry+0x242/0x2e0
[ 123.158061] vfs_mkdir+0x2a9/0x6c0
[ 123.158352] filename_mkdirat+0x3dc/0x500
[ 123.158710] ? __pfx_filename_mkdirat+0x10/0x10
[ 123.159070] ? strncpy_from_user+0x3a/0x1d0
[ 123.159413] __x64_sys_mkdir+0x6b/0x90
[ 123.159760] do_syscall_64+0xea/0x600
Replace the runtime mutex_init(&lock) with a static DEFINE_MUTEX(lock)
declaration to fix this issue.
Fixes: 49c3b9266a71 ("block: null_blk: Improve device creation with configfs")
Suggested-by: Bart Van Assche <bvanassche@acm.org>
Signed-off-by: Zizhi Wo <wozizhi@huawei.com>
Reviewed-by: Bart Van Assche <bvanassche@acm.org>
---
drivers/block/null_blk/main.c | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/drivers/block/null_blk/main.c b/drivers/block/null_blk/main.c
index f8c0fd57e041..eba204b27785 100644
--- a/drivers/block/null_blk/main.c
+++ b/drivers/block/null_blk/main.c
@@ -66,7 +66,7 @@ struct nullb_page {
#define NULLB_PAGE_FREE (MAP_SZ - 2)
static LIST_HEAD(nullb_list);
-static struct mutex lock;
+static DEFINE_MUTEX(lock);
static int null_major;
static DEFINE_IDA(nullb_indexes);
static struct blk_mq_tag_set tag_set;
@@ -2166,8 +2166,6 @@ static int __init null_init(void)
if (ret)
return ret;
- mutex_init(&lock);
-
null_major = register_blkdev(0, "nullb");
if (null_major < 0) {
ret = null_major;
--
2.52.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH V2 2/6] null_blk: give the file-scope mutex a descriptive name
2026-07-07 2:55 [PATCH V2 0/6] null_blk: fix init/exit races, leaks and cleanup Zizhi Wo
2026-07-07 2:55 ` [PATCH V2 1/6] null_blk: use DEFINE_MUTEX for the file-scope mutex Zizhi Wo
@ 2026-07-07 2:55 ` Zizhi Wo
2026-07-07 4:16 ` Damien Le Moal
2026-07-07 2:55 ` [PATCH V2 3/6] null_blk: register configfs subsystem after creating default devices Zizhi Wo
` (3 subsequent siblings)
5 siblings, 1 reply; 19+ messages in thread
From: Zizhi Wo @ 2026-07-07 2:55 UTC (permalink / raw)
To: axboe, dlemoal, nilay, linux-block, kch, johannes.thumshirn,
kbusch, bvanassche
Cc: linux-kernel, yangerkun, chengzhihao1, wozizhi
From: Zizhi Wo <wozizhi@huawei.com>
The file-scope lock mutex serializes access to global null_blk state,
including the nullb_list and device creation/removal. Rename it to
"nullb_global_lock" to make its purpose clear. No functional change.
Suggested-by: Bart Van Assche <bvanassche@acm.org>
Signed-off-by: Zizhi Wo <wozizhi@huawei.com>
---
drivers/block/null_blk/main.c | 32 ++++++++++++++++----------------
1 file changed, 16 insertions(+), 16 deletions(-)
diff --git a/drivers/block/null_blk/main.c b/drivers/block/null_blk/main.c
index eba204b27785..98f6935bb502 100644
--- a/drivers/block/null_blk/main.c
+++ b/drivers/block/null_blk/main.c
@@ -66,7 +66,7 @@ struct nullb_page {
#define NULLB_PAGE_FREE (MAP_SZ - 2)
static LIST_HEAD(nullb_list);
-static DEFINE_MUTEX(lock);
+static DEFINE_MUTEX(nullb_global_lock);
static int null_major;
static DEFINE_IDA(nullb_indexes);
static struct blk_mq_tag_set tag_set;
@@ -423,9 +423,9 @@ static int nullb_apply_submit_queues(struct nullb_device *dev,
{
int ret;
- mutex_lock(&lock);
+ mutex_lock(&nullb_global_lock);
ret = nullb_update_nr_hw_queues(dev, submit_queues, dev->poll_queues);
- mutex_unlock(&lock);
+ mutex_unlock(&nullb_global_lock);
return ret;
}
@@ -435,9 +435,9 @@ static int nullb_apply_poll_queues(struct nullb_device *dev,
{
int ret;
- mutex_lock(&lock);
+ mutex_lock(&nullb_global_lock);
ret = nullb_update_nr_hw_queues(dev, dev->submit_queues, poll_queues);
- mutex_unlock(&lock);
+ mutex_unlock(&nullb_global_lock);
return ret;
}
@@ -493,7 +493,7 @@ static ssize_t nullb_device_power_store(struct config_item *item,
return ret;
ret = count;
- mutex_lock(&lock);
+ mutex_lock(&nullb_global_lock);
if (!dev->power && newp) {
if (test_and_set_bit(NULLB_DEV_FL_UP, &dev->flags))
goto out;
@@ -516,7 +516,7 @@ static ssize_t nullb_device_power_store(struct config_item *item,
}
out:
- mutex_unlock(&lock);
+ mutex_unlock(&nullb_global_lock);
return ret;
}
@@ -707,10 +707,10 @@ nullb_group_drop_item(struct config_group *group, struct config_item *item)
struct nullb_device *dev = to_nullb_device(item);
if (test_and_clear_bit(NULLB_DEV_FL_UP, &dev->flags)) {
- mutex_lock(&lock);
+ mutex_lock(&nullb_global_lock);
dev->power = false;
null_del_dev(dev->nullb);
- mutex_unlock(&lock);
+ mutex_unlock(&nullb_global_lock);
}
nullb_del_fault_config(dev);
config_item_put(item);
@@ -2081,14 +2081,14 @@ static struct nullb *null_find_dev_by_name(const char *name)
{
struct nullb *nullb = NULL, *nb;
- mutex_lock(&lock);
+ mutex_lock(&nullb_global_lock);
list_for_each_entry(nb, &nullb_list, list) {
if (strcmp(nb->disk_name, name) == 0) {
nullb = nb;
break;
}
}
- mutex_unlock(&lock);
+ mutex_unlock(&nullb_global_lock);
return nullb;
}
@@ -2102,9 +2102,9 @@ static int null_create_dev(void)
if (!dev)
return -ENOMEM;
- mutex_lock(&lock);
+ mutex_lock(&nullb_global_lock);
ret = null_add_dev(dev);
- mutex_unlock(&lock);
+ mutex_unlock(&nullb_global_lock);
if (ret) {
null_free_dev(dev);
return ret;
@@ -2200,17 +2200,17 @@ static void __exit null_exit(void)
unregister_blkdev(null_major, "nullb");
- mutex_lock(&lock);
+ mutex_lock(&nullb_global_lock);
while (!list_empty(&nullb_list)) {
nullb = list_entry(nullb_list.next, struct nullb, list);
null_destroy_dev(nullb);
}
- mutex_unlock(&lock);
+ mutex_unlock(&nullb_global_lock);
if (tag_set.ops)
blk_mq_free_tag_set(&tag_set);
- mutex_destroy(&lock);
+ mutex_destroy(&nullb_global_lock);
}
module_init(null_init);
--
2.52.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH V2 3/6] null_blk: register configfs subsystem after creating default devices
2026-07-07 2:55 [PATCH V2 0/6] null_blk: fix init/exit races, leaks and cleanup Zizhi Wo
2026-07-07 2:55 ` [PATCH V2 1/6] null_blk: use DEFINE_MUTEX for the file-scope mutex Zizhi Wo
2026-07-07 2:55 ` [PATCH V2 2/6] null_blk: give the file-scope mutex a descriptive name Zizhi Wo
@ 2026-07-07 2:55 ` Zizhi Wo
2026-07-07 4:18 ` Damien Le Moal
2026-07-07 2:55 ` [PATCH V2 4/6] null_blk: move unregister_blkdev() after destroying dev in null_exit() Zizhi Wo
` (2 subsequent siblings)
5 siblings, 1 reply; 19+ messages in thread
From: Zizhi Wo @ 2026-07-07 2:55 UTC (permalink / raw)
To: axboe, dlemoal, nilay, linux-block, kch, johannes.thumshirn,
kbusch, bvanassche
Cc: linux-kernel, yangerkun, chengzhihao1, wozizhi
From: Zizhi Wo <wozizhi@huawei.com>
In null_init(), configfs_register_subsystem() currently runs before
register_blkdev(), so when null_blk is built as a module, a racing mkdir()
+ poweron from userspace can reach null_add_dev() while null_major is still
0. __add_disk() then hits WARN_ON(disk->minors) (major=0 with minors!=0)
and fails:
[root@fedora ~]# [ 2366.521436] WARNING: block/genhd.c:476 at __add_disk+0x8a7/0xde0,
[ 2366.523552] Modules linked in: null_blk(+) nft_fib_inet nft_fib_ipv4 nft_fib_ipv6 nft_fib
[ 2366.529081] CPU: 26 UID: 0 PID: 1600 Comm: sh Not tainted 7.2.0-rc1+ #66 PREEMPT(full)
......
[ 2366.547251] Call Trace:
[ 2366.547575] <TASK>
[ 2366.547831] ? _raw_spin_lock+0x84/0xe0
[ 2366.548260] add_disk_fwnode+0x114/0x560
[ 2366.548739] null_add_dev+0x102d/0x1b80 [null_blk]
[ 2366.549310] ? __pfx_null_add_dev+0x10/0x10 [null_blk]
[ 2366.549906] ? mutex_lock+0xde/0x1c0
[ 2366.550361] ? __pfx_mutex_lock+0x10/0x10
[ 2366.550827] nullb_device_power_store+0x1e7/0x280 [null_blk]
[ 2366.551499] ? __pfx_nullb_device_power_store+0x10/0x10 [null_blk]
[ 2366.552177] ? __kmalloc_cache_noprof+0x1f5/0x470
[ 2366.552748] ? configfs_write_iter+0x35c/0x4e0
[ 2366.553242] configfs_write_iter+0x286/0x4e0
[ 2366.553787] vfs_write+0x52d/0xd00
[ 2366.554169] ? __pfx_vfs_write+0x10/0x10
[ 2366.554679] ? __pfx___css_rstat_updated+0x10/0x10
[ 2366.555196] ? fdget_pos+0x1cf/0x4c0
[ 2366.555649] ksys_write+0xfc/0x1d0
......
Additionally, the err_dev path destroys all devices on nullb_list while
configfs is still registered. If a racing mkdir() + poweron puts a user
device on the list, null_destroy_dev()->null_free_dev() kfrees the user
device's nullb_device but /sys/kernel/config/nullb/<name> is still
reachable. Any userspace access to the item will trigger a UAF.
For simplicity, move configfs_register_subsystem() to the end to solve
the problems above. This also mirrors null_exit().
Fixes: 3bf2bd20734e ("nullb: add configfs interface")
Signed-off-by: Zizhi Wo <wozizhi@huawei.com>
---
drivers/block/null_blk/main.c | 16 ++++++----------
1 file changed, 6 insertions(+), 10 deletions(-)
diff --git a/drivers/block/null_blk/main.c b/drivers/block/null_blk/main.c
index 98f6935bb502..024c89b36ddc 100644
--- a/drivers/block/null_blk/main.c
+++ b/drivers/block/null_blk/main.c
@@ -2162,15 +2162,9 @@ static int __init null_init(void)
config_group_init(&nullb_subsys.su_group);
mutex_init(&nullb_subsys.su_mutex);
- ret = configfs_register_subsystem(&nullb_subsys);
- if (ret)
- return ret;
-
null_major = register_blkdev(0, "nullb");
- if (null_major < 0) {
- ret = null_major;
- goto err_conf;
- }
+ if (null_major < 0)
+ return null_major;
for (i = 0; i < nr_devices; i++) {
ret = null_create_dev();
@@ -2178,6 +2172,10 @@ static int __init null_init(void)
goto err_dev;
}
+ ret = configfs_register_subsystem(&nullb_subsys);
+ if (ret)
+ goto err_dev;
+
pr_info("module loaded\n");
return 0;
@@ -2187,8 +2185,6 @@ static int __init null_init(void)
null_destroy_dev(nullb);
}
unregister_blkdev(null_major, "nullb");
-err_conf:
- configfs_unregister_subsystem(&nullb_subsys);
return ret;
}
--
2.52.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH V2 4/6] null_blk: move unregister_blkdev() after destroying dev in null_exit()
2026-07-07 2:55 [PATCH V2 0/6] null_blk: fix init/exit races, leaks and cleanup Zizhi Wo
` (2 preceding siblings ...)
2026-07-07 2:55 ` [PATCH V2 3/6] null_blk: register configfs subsystem after creating default devices Zizhi Wo
@ 2026-07-07 2:55 ` Zizhi Wo
2026-07-07 4:19 ` Damien Le Moal
2026-07-07 2:55 ` [PATCH V2 5/6] null_blk: don't locklessly overwrite dev state after apply_fn Zizhi Wo
2026-07-07 2:55 ` [PATCH V2 6/6] null_blk: free global tag_set on init error path Zizhi Wo
5 siblings, 1 reply; 19+ messages in thread
From: Zizhi Wo @ 2026-07-07 2:55 UTC (permalink / raw)
To: axboe, dlemoal, nilay, linux-block, kch, johannes.thumshirn,
kbusch, bvanassche
Cc: linux-kernel, yangerkun, chengzhihao1, wozizhi
In null_exit(), unregister_blkdev() was called before the null_blk
instances were destroyed, which is inconsistent with the cleanup order in
null_init(). Move it after null_destroy_dev() so that teardown happens in
the reverse order of initialization.
No functional change intended.
Suggested-by: Bart Van Assche <bvanassche@acm.org>
Signed-off-by: Zizhi Wo <wozizhi@huaweicloud.com>
---
drivers/block/null_blk/main.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/block/null_blk/main.c b/drivers/block/null_blk/main.c
index 024c89b36ddc..cab51301560e 100644
--- a/drivers/block/null_blk/main.c
+++ b/drivers/block/null_blk/main.c
@@ -2194,8 +2194,6 @@ static void __exit null_exit(void)
configfs_unregister_subsystem(&nullb_subsys);
- unregister_blkdev(null_major, "nullb");
-
mutex_lock(&nullb_global_lock);
while (!list_empty(&nullb_list)) {
nullb = list_entry(nullb_list.next, struct nullb, list);
@@ -2203,6 +2201,8 @@ static void __exit null_exit(void)
}
mutex_unlock(&nullb_global_lock);
+ unregister_blkdev(null_major, "nullb");
+
if (tag_set.ops)
blk_mq_free_tag_set(&tag_set);
--
2.52.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH V2 5/6] null_blk: don't locklessly overwrite dev state after apply_fn
2026-07-07 2:55 [PATCH V2 0/6] null_blk: fix init/exit races, leaks and cleanup Zizhi Wo
` (3 preceding siblings ...)
2026-07-07 2:55 ` [PATCH V2 4/6] null_blk: move unregister_blkdev() after destroying dev in null_exit() Zizhi Wo
@ 2026-07-07 2:55 ` Zizhi Wo
2026-07-07 3:38 ` Zizhi Wo
` (2 more replies)
2026-07-07 2:55 ` [PATCH V2 6/6] null_blk: free global tag_set on init error path Zizhi Wo
5 siblings, 3 replies; 19+ messages in thread
From: Zizhi Wo @ 2026-07-07 2:55 UTC (permalink / raw)
To: axboe, dlemoal, nilay, linux-block, kch, johannes.thumshirn,
kbusch, bvanassche
Cc: linux-kernel, yangerkun, chengzhihao1, wozizhi
From: Zizhi Wo <wozizhi@huawei.com>
The NULLB_DEVICE_ATTR macro unconditionally writes dev->NAME = new_value
after apply_fn() returns. For attributes with an apply_fn (submit_queues,
poll_queues), apply_fn already sets dev->NAME under &nullb_list_lock.
configfs serializes writes via a per-open-file mutex (buffer->mutex), so
two threads writing to the same attribute through separate open file
descriptions run the store callback concurrently. The macro's write is
redundant and lockless, so a concurrent store's losing thread can overwrite
the winner's value after apply_fn set it, making dev->submit_queues
mismatch the hardware state. null_map_queues() then hits a WARN_ON_ONCE and
falls back to a single queue.
Restructure the macro so that apply_fn attributes return directly after
apply_fn, and only non-apply_fn attributes write dev->NAME -- those are
only changeable while not CONFIGURED and have no live hardware state to
mismatch.
Fixes: 45919fbfe1c4 ("null_blk: Enable modifying 'submit_queues' after an instance has been configured")
Signed-off-by: Zizhi Wo <wozizhi@huawei.com>
---
drivers/block/null_blk/main.c | 14 ++++++++------
1 file changed, 8 insertions(+), 6 deletions(-)
diff --git a/drivers/block/null_blk/main.c b/drivers/block/null_blk/main.c
index cab51301560e..e7555c47b671 100644
--- a/drivers/block/null_blk/main.c
+++ b/drivers/block/null_blk/main.c
@@ -360,13 +360,15 @@ nullb_device_##NAME##_store(struct config_item *item, const char *page, \
ret = nullb_device_##TYPE##_attr_store(&new_value, page, count);\
if (ret < 0) \
return ret; \
- if (apply_fn) \
+ if (apply_fn) { \
ret = apply_fn(dev, new_value); \
- else if (test_bit(NULLB_DEV_FL_CONFIGURED, &dev->flags)) \
- ret = -EBUSY; \
- if (ret < 0) \
- return ret; \
- dev->NAME = new_value; \
+ if (ret < 0) \
+ return ret; \
+ } else { \
+ if (test_bit(NULLB_DEV_FL_CONFIGURED, &dev->flags)) \
+ return -EBUSY; \
+ dev->NAME = new_value; \
+ } \
return count; \
} \
CONFIGFS_ATTR(nullb_device_, NAME);
--
2.52.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH V2 6/6] null_blk: free global tag_set on init error path
2026-07-07 2:55 [PATCH V2 0/6] null_blk: fix init/exit races, leaks and cleanup Zizhi Wo
` (4 preceding siblings ...)
2026-07-07 2:55 ` [PATCH V2 5/6] null_blk: don't locklessly overwrite dev state after apply_fn Zizhi Wo
@ 2026-07-07 2:55 ` Zizhi Wo
2026-07-07 4:24 ` Damien Le Moal
5 siblings, 1 reply; 19+ messages in thread
From: Zizhi Wo @ 2026-07-07 2:55 UTC (permalink / raw)
To: axboe, dlemoal, nilay, linux-block, kch, johannes.thumshirn,
kbusch, bvanassche
Cc: linux-kernel, yangerkun, chengzhihao1, wozizhi
From: Zizhi Wo <wozizhi@huawei.com>
If shared_tags is enabled, null_setup_tagset() allocates the global tag_set
via null_init_global_tag_set(). If device creation later fails, err_dev
destroys the default devices and calls unregister_blkdev(), but never frees
the global tag_set. Since module init failed, null_exit() is never invoked,
so the global tag_set's tags and maps are permanently leaked.
Free the global tag_set in err_dev, matching null_exit() which does
if (tag_set.ops) blk_mq_free_tag_set(&tag_set).
Fixes: 82f402fefa50 ("null_blk: add support for shared tags")
Signed-off-by: Zizhi Wo <wozizhi@huawei.com>
---
drivers/block/null_blk/main.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/block/null_blk/main.c b/drivers/block/null_blk/main.c
index e7555c47b671..1e24c7e23524 100644
--- a/drivers/block/null_blk/main.c
+++ b/drivers/block/null_blk/main.c
@@ -2187,6 +2187,8 @@ static int __init null_init(void)
null_destroy_dev(nullb);
}
unregister_blkdev(null_major, "nullb");
+ if (tag_set.ops)
+ blk_mq_free_tag_set(&tag_set);
return ret;
}
--
2.52.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* Re: [PATCH V2 5/6] null_blk: don't locklessly overwrite dev state after apply_fn
2026-07-07 2:55 ` [PATCH V2 5/6] null_blk: don't locklessly overwrite dev state after apply_fn Zizhi Wo
@ 2026-07-07 3:38 ` Zizhi Wo
2026-07-07 4:21 ` Damien Le Moal
2026-07-07 7:33 ` Nilay Shroff
2 siblings, 0 replies; 19+ messages in thread
From: Zizhi Wo @ 2026-07-07 3:38 UTC (permalink / raw)
To: Zizhi Wo, axboe, dlemoal, nilay, linux-block, kch,
johannes.thumshirn, kbusch, bvanassche
Cc: linux-kernel, yangerkun, chengzhihao1
在 2026/7/7 10:55, Zizhi Wo 写道:
> From: Zizhi Wo <wozizhi@huawei.com>
>
> The NULLB_DEVICE_ATTR macro unconditionally writes dev->NAME = new_value
> after apply_fn() returns. For attributes with an apply_fn (submit_queues,
> poll_queues), apply_fn already sets dev->NAME under &nullb_list_lock.
>
> configfs serializes writes via a per-open-file mutex (buffer->mutex), so
> two threads writing to the same attribute through separate open file
> descriptions run the store callback concurrently. The macro's write is
> redundant and lockless, so a concurrent store's losing thread can overwrite
> the winner's value after apply_fn set it, making dev->submit_queues
> mismatch the hardware state. null_map_queues() then hits a WARN_ON_ONCE and
> falls back to a single queue.
>
> Restructure the macro so that apply_fn attributes return directly after
> apply_fn, and only non-apply_fn attributes write dev->NAME -- those are
> only changeable while not CONFIGURED and have no live hardware state to
> mismatch.
>
> Fixes: 45919fbfe1c4 ("null_blk: Enable modifying 'submit_queues' after an instance has been configured")
> Signed-off-by: Zizhi Wo <wozizhi@huawei.com>
> ---
> drivers/block/null_blk/main.c | 14 ++++++++------
> 1 file changed, 8 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/block/null_blk/main.c b/drivers/block/null_blk/main.c
> index cab51301560e..e7555c47b671 100644
> --- a/drivers/block/null_blk/main.c
> +++ b/drivers/block/null_blk/main.c
> @@ -360,13 +360,15 @@ nullb_device_##NAME##_store(struct config_item *item, const char *page, \
> ret = nullb_device_##TYPE##_attr_store(&new_value, page, count);\
> if (ret < 0) \
> return ret; \
> - if (apply_fn) \
> + if (apply_fn) { \
> ret = apply_fn(dev, new_value); \
> - else if (test_bit(NULLB_DEV_FL_CONFIGURED, &dev->flags)) \
> - ret = -EBUSY; \
> - if (ret < 0) \
> - return ret; \
> - dev->NAME = new_value; \
> + if (ret < 0) \
> + return ret; \
> + } else { \
> + if (test_bit(NULLB_DEV_FL_CONFIGURED, &dev->flags)) \
> + return -EBUSY; \
> + dev->NAME = new_value; \
> + } \
> return count; \
> } \
> CONFIGFS_ATTR(nullb_device_, NAME);
Sorry for the noise. I missed that nullb_update_nr_hw_queues() returns
early without storing the value when !dev->nullb, so dropping the
macro's trailing write would silently discard pre-power-on
configuration. I'll add the update logic on the !dev->nullb path in v3.
Any further comments are welcome.
Thanks,
Zizhi Wo
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH V2 1/6] null_blk: use DEFINE_MUTEX for the file-scope mutex
2026-07-07 2:55 ` [PATCH V2 1/6] null_blk: use DEFINE_MUTEX for the file-scope mutex Zizhi Wo
@ 2026-07-07 4:07 ` Damien Le Moal
0 siblings, 0 replies; 19+ messages in thread
From: Damien Le Moal @ 2026-07-07 4:07 UTC (permalink / raw)
To: Zizhi Wo, axboe, nilay, linux-block, kch, johannes.thumshirn,
kbusch, bvanassche
Cc: linux-kernel, yangerkun, chengzhihao1, wozizhi
On 7/7/26 11:55, Zizhi Wo wrote:
> From: Zizhi Wo <wozizhi@huawei.com>
>
> In null_init(), mutex_init(&lock) currently happens after
> configfs_register_subsystem(), which exposes the nullb subsystem to
> userspace. A racing mkdir() into /sys/kernel/config/nullb/ can reach
> null_find_dev_by_name() -> mutex_lock(&lock) before the mutex is
> initialized, trigger warning:
>
> [ 123.137788] DEBUG_LOCKS_WARN_ON(lock->magic != lock)
> [ 123.137796] WARNING: kernel/locking/mutex.c:159 at mutex_lock+0x171/0x1c0, CPU#13: mkdir/1301
> [ 123.140090] Modules linked in: null_blk(+) nft_fib_inet nft_fib_ipv4
> ......
> [ 123.154926] Call Trace:
> [ 123.155172] <TASK>
> [ 123.155419] ? __pfx_mutex_lock+0x10/0x10
> [ 123.156181] ? __pfx__raw_spin_lock+0x10/0x10
> [ 123.156571] nullb_group_make_group+0x20/0x100 [null_blk]
> [ 123.157011] configfs_mkdir+0x47b/0xc70
> [ 123.157337] ? __pfx_configfs_mkdir+0x10/0x10
> [ 123.157719] ? may_create_dentry+0x242/0x2e0
> [ 123.158061] vfs_mkdir+0x2a9/0x6c0
> [ 123.158352] filename_mkdirat+0x3dc/0x500
> [ 123.158710] ? __pfx_filename_mkdirat+0x10/0x10
> [ 123.159070] ? strncpy_from_user+0x3a/0x1d0
> [ 123.159413] __x64_sys_mkdir+0x6b/0x90
> [ 123.159760] do_syscall_64+0xea/0x600
>
> Replace the runtime mutex_init(&lock) with a static DEFINE_MUTEX(lock)
> declaration to fix this issue.
>
> Fixes: 49c3b9266a71 ("block: null_blk: Improve device creation with configfs")
> Suggested-by: Bart Van Assche <bvanassche@acm.org>
> Signed-off-by: Zizhi Wo <wozizhi@huawei.com>
> Reviewed-by: Bart Van Assche <bvanassche@acm.org>
Reviewed-by: Damien Le Moal <dlemoal@kernel.org>
--
Damien Le Moal
Western Digital Research
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH V2 2/6] null_blk: give the file-scope mutex a descriptive name
2026-07-07 2:55 ` [PATCH V2 2/6] null_blk: give the file-scope mutex a descriptive name Zizhi Wo
@ 2026-07-07 4:16 ` Damien Le Moal
2026-07-07 6:24 ` Zizhi Wo
0 siblings, 1 reply; 19+ messages in thread
From: Damien Le Moal @ 2026-07-07 4:16 UTC (permalink / raw)
To: Zizhi Wo, axboe, nilay, linux-block, kch, johannes.thumshirn,
kbusch, bvanassche
Cc: linux-kernel, yangerkun, chengzhihao1, wozizhi
On 7/7/26 11:55, Zizhi Wo wrote:
> From: Zizhi Wo <wozizhi@huawei.com>
>
> The file-scope lock mutex serializes access to global null_blk state,
> including the nullb_list and device creation/removal. Rename it to
> "nullb_global_lock" to make its purpose clear. No functional change.
>
> Suggested-by: Bart Van Assche <bvanassche@acm.org>
> Signed-off-by: Zizhi Wo <wozizhi@huawei.com>
> ---
> drivers/block/null_blk/main.c | 32 ++++++++++++++++----------------
> 1 file changed, 16 insertions(+), 16 deletions(-)
>
> diff --git a/drivers/block/null_blk/main.c b/drivers/block/null_blk/main.c
> index eba204b27785..98f6935bb502 100644
> --- a/drivers/block/null_blk/main.c
> +++ b/drivers/block/null_blk/main.c
> @@ -66,7 +66,7 @@ struct nullb_page {
> #define NULLB_PAGE_FREE (MAP_SZ - 2)
>
> static LIST_HEAD(nullb_list);
> -static DEFINE_MUTEX(lock);
> +static DEFINE_MUTEX(nullb_global_lock);
Since this seem to protect only the device list, why not simply call this
nullb_list_lock ?
> static int null_major;
> static DEFINE_IDA(nullb_indexes);
> static struct blk_mq_tag_set tag_set;
> @@ -423,9 +423,9 @@ static int nullb_apply_submit_queues(struct nullb_device *dev,
> {
> int ret;
>
> - mutex_lock(&lock);
> + mutex_lock(&nullb_global_lock);
> ret = nullb_update_nr_hw_queues(dev, submit_queues, dev->poll_queues);
> - mutex_unlock(&lock);
> + mutex_unlock(&nullb_global_lock);
Nothing in nullb_update_nr_hw_queues() touches the device list. So it is very
odd that nullb_global_lock is used for serialization here instead of a
nullb_device mutex. If you change this, why not a prep patch to introduce such a
mutex ?
>
> return ret;
> }
> @@ -435,9 +435,9 @@ static int nullb_apply_poll_queues(struct nullb_device *dev,
> {
> int ret;
>
> - mutex_lock(&lock);
> + mutex_lock(&nullb_global_lock);
> ret = nullb_update_nr_hw_queues(dev, dev->submit_queues, poll_queues);
> - mutex_unlock(&lock);
> + mutex_unlock(&nullb_global_lock);
Same here.
>
> return ret;
> }
--
Damien Le Moal
Western Digital Research
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH V2 3/6] null_blk: register configfs subsystem after creating default devices
2026-07-07 2:55 ` [PATCH V2 3/6] null_blk: register configfs subsystem after creating default devices Zizhi Wo
@ 2026-07-07 4:18 ` Damien Le Moal
0 siblings, 0 replies; 19+ messages in thread
From: Damien Le Moal @ 2026-07-07 4:18 UTC (permalink / raw)
To: Zizhi Wo, axboe, nilay, linux-block, kch, johannes.thumshirn,
kbusch, bvanassche
Cc: linux-kernel, yangerkun, chengzhihao1, wozizhi
On 7/7/26 11:55, Zizhi Wo wrote:
> From: Zizhi Wo <wozizhi@huawei.com>
>
> In null_init(), configfs_register_subsystem() currently runs before
> register_blkdev(), so when null_blk is built as a module, a racing mkdir()
> + poweron from userspace can reach null_add_dev() while null_major is still
> 0. __add_disk() then hits WARN_ON(disk->minors) (major=0 with minors!=0)
> and fails:
>
> [root@fedora ~]# [ 2366.521436] WARNING: block/genhd.c:476 at __add_disk+0x8a7/0xde0,
> [ 2366.523552] Modules linked in: null_blk(+) nft_fib_inet nft_fib_ipv4 nft_fib_ipv6 nft_fib
> [ 2366.529081] CPU: 26 UID: 0 PID: 1600 Comm: sh Not tainted 7.2.0-rc1+ #66 PREEMPT(full)
> ......
> [ 2366.547251] Call Trace:
> [ 2366.547575] <TASK>
> [ 2366.547831] ? _raw_spin_lock+0x84/0xe0
> [ 2366.548260] add_disk_fwnode+0x114/0x560
> [ 2366.548739] null_add_dev+0x102d/0x1b80 [null_blk]
> [ 2366.549310] ? __pfx_null_add_dev+0x10/0x10 [null_blk]
> [ 2366.549906] ? mutex_lock+0xde/0x1c0
> [ 2366.550361] ? __pfx_mutex_lock+0x10/0x10
> [ 2366.550827] nullb_device_power_store+0x1e7/0x280 [null_blk]
> [ 2366.551499] ? __pfx_nullb_device_power_store+0x10/0x10 [null_blk]
> [ 2366.552177] ? __kmalloc_cache_noprof+0x1f5/0x470
> [ 2366.552748] ? configfs_write_iter+0x35c/0x4e0
> [ 2366.553242] configfs_write_iter+0x286/0x4e0
> [ 2366.553787] vfs_write+0x52d/0xd00
> [ 2366.554169] ? __pfx_vfs_write+0x10/0x10
> [ 2366.554679] ? __pfx___css_rstat_updated+0x10/0x10
> [ 2366.555196] ? fdget_pos+0x1cf/0x4c0
> [ 2366.555649] ksys_write+0xfc/0x1d0
> ......
>
> Additionally, the err_dev path destroys all devices on nullb_list while
> configfs is still registered. If a racing mkdir() + poweron puts a user
> device on the list, null_destroy_dev()->null_free_dev() kfrees the user
> device's nullb_device but /sys/kernel/config/nullb/<name> is still
> reachable. Any userspace access to the item will trigger a UAF.
>
> For simplicity, move configfs_register_subsystem() to the end to solve
> the problems above. This also mirrors null_exit().
>
> Fixes: 3bf2bd20734e ("nullb: add configfs interface")
> Signed-off-by: Zizhi Wo <wozizhi@huawei.com>
Looks good.
Reviewed-by: Damien Le Moal <dlemoal@kernel.org>
--
Damien Le Moal
Western Digital Research
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH V2 4/6] null_blk: move unregister_blkdev() after destroying dev in null_exit()
2026-07-07 2:55 ` [PATCH V2 4/6] null_blk: move unregister_blkdev() after destroying dev in null_exit() Zizhi Wo
@ 2026-07-07 4:19 ` Damien Le Moal
0 siblings, 0 replies; 19+ messages in thread
From: Damien Le Moal @ 2026-07-07 4:19 UTC (permalink / raw)
To: Zizhi Wo, axboe, nilay, linux-block, kch, johannes.thumshirn,
kbusch, bvanassche
Cc: linux-kernel, yangerkun, chengzhihao1, wozizhi
On 7/7/26 11:55, Zizhi Wo wrote:
> In null_exit(), unregister_blkdev() was called before the null_blk
s/was/is
> instances were destroyed, which is inconsistent with the cleanup order in
> null_init(). Move it after null_destroy_dev() so that teardown happens in
> the reverse order of initialization.
>
> No functional change intended.
>
> Suggested-by: Bart Van Assche <bvanassche@acm.org>
> Signed-off-by: Zizhi Wo <wozizhi@huaweicloud.com>
Looks good.
Reviewed-by: Damien Le Moal <dlemoal@kernel.org>
--
Damien Le Moal
Western Digital Research
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH V2 5/6] null_blk: don't locklessly overwrite dev state after apply_fn
2026-07-07 2:55 ` [PATCH V2 5/6] null_blk: don't locklessly overwrite dev state after apply_fn Zizhi Wo
2026-07-07 3:38 ` Zizhi Wo
@ 2026-07-07 4:21 ` Damien Le Moal
2026-07-07 7:33 ` Nilay Shroff
2 siblings, 0 replies; 19+ messages in thread
From: Damien Le Moal @ 2026-07-07 4:21 UTC (permalink / raw)
To: Zizhi Wo, axboe, nilay, linux-block, kch, johannes.thumshirn,
kbusch, bvanassche
Cc: linux-kernel, yangerkun, chengzhihao1, wozizhi
On 7/7/26 11:55, Zizhi Wo wrote:
> From: Zizhi Wo <wozizhi@huawei.com>
>
> The NULLB_DEVICE_ATTR macro unconditionally writes dev->NAME = new_value
> after apply_fn() returns. For attributes with an apply_fn (submit_queues,
> poll_queues), apply_fn already sets dev->NAME under &nullb_list_lock.
>
> configfs serializes writes via a per-open-file mutex (buffer->mutex), so
> two threads writing to the same attribute through separate open file
> descriptions run the store callback concurrently. The macro's write is
> redundant and lockless, so a concurrent store's losing thread can overwrite
> the winner's value after apply_fn set it, making dev->submit_queues
> mismatch the hardware state. null_map_queues() then hits a WARN_ON_ONCE and
> falls back to a single queue.
>
> Restructure the macro so that apply_fn attributes return directly after
> apply_fn, and only non-apply_fn attributes write dev->NAME -- those are
> only changeable while not CONFIGURED and have no live hardware state to
> mismatch.
>
> Fixes: 45919fbfe1c4 ("null_blk: Enable modifying 'submit_queues' after an instance has been configured")
> Signed-off-by: Zizhi Wo <wozizhi@huawei.com>
Looks OK.
Reviewed-by: Damien Le Moal <dlemoal@kernel.org>
--
Damien Le Moal
Western Digital Research
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH V2 6/6] null_blk: free global tag_set on init error path
2026-07-07 2:55 ` [PATCH V2 6/6] null_blk: free global tag_set on init error path Zizhi Wo
@ 2026-07-07 4:24 ` Damien Le Moal
0 siblings, 0 replies; 19+ messages in thread
From: Damien Le Moal @ 2026-07-07 4:24 UTC (permalink / raw)
To: Zizhi Wo, axboe, nilay, linux-block, kch, johannes.thumshirn,
kbusch, bvanassche
Cc: linux-kernel, yangerkun, chengzhihao1, wozizhi
On 7/7/26 11:55, Zizhi Wo wrote:
> From: Zizhi Wo <wozizhi@huawei.com>
>
> If shared_tags is enabled, null_setup_tagset() allocates the global tag_set
> via null_init_global_tag_set(). If device creation later fails, err_dev
> destroys the default devices and calls unregister_blkdev(), but never frees
> the global tag_set. Since module init failed, null_exit() is never invoked,
> so the global tag_set's tags and maps are permanently leaked.
>
> Free the global tag_set in err_dev, matching null_exit() which does
> if (tag_set.ops) blk_mq_free_tag_set(&tag_set).
>
> Fixes: 82f402fefa50 ("null_blk: add support for shared tags")
> Signed-off-by: Zizhi Wo <wozizhi@huawei.com>
Looks OK.
Reviewed-by: Damien Le Moal <dlemoal@kernel.org>
--
Damien Le Moal
Western Digital Research
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH V2 2/6] null_blk: give the file-scope mutex a descriptive name
2026-07-07 4:16 ` Damien Le Moal
@ 2026-07-07 6:24 ` Zizhi Wo
2026-07-07 6:28 ` Damien Le Moal
0 siblings, 1 reply; 19+ messages in thread
From: Zizhi Wo @ 2026-07-07 6:24 UTC (permalink / raw)
To: Damien Le Moal, Zizhi Wo, axboe, nilay, linux-block, kch,
johannes.thumshirn, kbusch, bvanassche
Cc: linux-kernel, yangerkun, chengzhihao1
在 2026/7/7 12:16, Damien Le Moal 写道:
> On 7/7/26 11:55, Zizhi Wo wrote:
>> From: Zizhi Wo <wozizhi@huawei.com>
>>
>> The file-scope lock mutex serializes access to global null_blk state,
>> including the nullb_list and device creation/removal. Rename it to
>> "nullb_global_lock" to make its purpose clear. No functional change.
>>
>> Suggested-by: Bart Van Assche <bvanassche@acm.org>
>> Signed-off-by: Zizhi Wo <wozizhi@huawei.com>
>> ---
>> drivers/block/null_blk/main.c | 32 ++++++++++++++++----------------
>> 1 file changed, 16 insertions(+), 16 deletions(-)
>>
>> diff --git a/drivers/block/null_blk/main.c b/drivers/block/null_blk/main.c
>> index eba204b27785..98f6935bb502 100644
>> --- a/drivers/block/null_blk/main.c
>> +++ b/drivers/block/null_blk/main.c
>> @@ -66,7 +66,7 @@ struct nullb_page {
>> #define NULLB_PAGE_FREE (MAP_SZ - 2)
>>
>> static LIST_HEAD(nullb_list);
>> -static DEFINE_MUTEX(lock);
>> +static DEFINE_MUTEX(nullb_global_lock);
>
> Since this seem to protect only the device list, why not simply call this
> nullb_list_lock ?
>
>> static int null_major;
>> static DEFINE_IDA(nullb_indexes);
>> static struct blk_mq_tag_set tag_set;
>> @@ -423,9 +423,9 @@ static int nullb_apply_submit_queues(struct nullb_device *dev,
>> {
>> int ret;
>>
>> - mutex_lock(&lock);
>> + mutex_lock(&nullb_global_lock);
>> ret = nullb_update_nr_hw_queues(dev, submit_queues, dev->poll_queues);
>> - mutex_unlock(&lock);
>> + mutex_unlock(&nullb_global_lock);
>
> Nothing in nullb_update_nr_hw_queues() touches the device list. So it is very
> odd that nullb_global_lock is used for serialization here instead of a
> nullb_device mutex. If you change this, why not a prep patch to introduce such a
> mutex ?
>
Thanks for pointing this out. The rename really belongs together with
the locking rework, so I'll drop this patch from v3 and fold the rename
into a separate series that splits the locking (introducing a per-
nullb_device mutex for the updates).
Thanks,
Zizhi Wo
>>
>> return ret;
>> }
>> @@ -435,9 +435,9 @@ static int nullb_apply_poll_queues(struct nullb_device *dev,
>> {
>> int ret;
>>
>> - mutex_lock(&lock);
>> + mutex_lock(&nullb_global_lock);
>> ret = nullb_update_nr_hw_queues(dev, dev->submit_queues, poll_queues);
>> - mutex_unlock(&lock);
>> + mutex_unlock(&nullb_global_lock);
>
> Same here.
>
>>
>> return ret;
>> }
>
>
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH V2 2/6] null_blk: give the file-scope mutex a descriptive name
2026-07-07 6:24 ` Zizhi Wo
@ 2026-07-07 6:28 ` Damien Le Moal
2026-07-07 6:45 ` Zizhi Wo
0 siblings, 1 reply; 19+ messages in thread
From: Damien Le Moal @ 2026-07-07 6:28 UTC (permalink / raw)
To: Zizhi Wo, axboe, nilay, linux-block, kch, johannes.thumshirn,
kbusch, bvanassche
Cc: linux-kernel, yangerkun, chengzhihao1
On 7/7/26 15:24, Zizhi Wo wrote:
>
>
> 在 2026/7/7 12:16, Damien Le Moal 写道:
>> On 7/7/26 11:55, Zizhi Wo wrote:
>>> From: Zizhi Wo <wozizhi@huawei.com>
>>>
>>> The file-scope lock mutex serializes access to global null_blk state,
>>> including the nullb_list and device creation/removal. Rename it to
>>> "nullb_global_lock" to make its purpose clear. No functional change.
>>>
>>> Suggested-by: Bart Van Assche <bvanassche@acm.org>
>>> Signed-off-by: Zizhi Wo <wozizhi@huawei.com>
>>> ---
>>> drivers/block/null_blk/main.c | 32 ++++++++++++++++----------------
>>> 1 file changed, 16 insertions(+), 16 deletions(-)
>>>
>>> diff --git a/drivers/block/null_blk/main.c b/drivers/block/null_blk/main.c
>>> index eba204b27785..98f6935bb502 100644
>>> --- a/drivers/block/null_blk/main.c
>>> +++ b/drivers/block/null_blk/main.c
>>> @@ -66,7 +66,7 @@ struct nullb_page {
>>> #define NULLB_PAGE_FREE (MAP_SZ - 2)
>>>
>>> static LIST_HEAD(nullb_list);
>>> -static DEFINE_MUTEX(lock);
>>> +static DEFINE_MUTEX(nullb_global_lock);
>>
>> Since this seem to protect only the device list, why not simply call this
>> nullb_list_lock ?
>>
>>> static int null_major;
>>> static DEFINE_IDA(nullb_indexes);
>>> static struct blk_mq_tag_set tag_set;
>>> @@ -423,9 +423,9 @@ static int nullb_apply_submit_queues(struct nullb_device *dev,
>>> {
>>> int ret;
>>>
>>> - mutex_lock(&lock);
>>> + mutex_lock(&nullb_global_lock);
>>> ret = nullb_update_nr_hw_queues(dev, submit_queues, dev->poll_queues);
>>> - mutex_unlock(&lock);
>>> + mutex_unlock(&nullb_global_lock);
>>
>> Nothing in nullb_update_nr_hw_queues() touches the device list. So it is very
>> odd that nullb_global_lock is used for serialization here instead of a
>> nullb_device mutex. If you change this, why not a prep patch to introduce such a
>> mutex ?
>>
>
> Thanks for pointing this out. The rename really belongs together with
> the locking rework, so I'll drop this patch from v3 and fold the rename
> into a separate series that splits the locking (introducing a per-
> nullb_device mutex for the updates).
Please carefully check if the use of the global lock in these function is not to
serialize with power on/off. The code is a little (uselessly) complicated in
that area and I may not be seeing something. But it seems that on/off control
could also use a device lock, with proper ordering: device lock first, then list
lock.
--
Damien Le Moal
Western Digital Research
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH V2 2/6] null_blk: give the file-scope mutex a descriptive name
2026-07-07 6:28 ` Damien Le Moal
@ 2026-07-07 6:45 ` Zizhi Wo
0 siblings, 0 replies; 19+ messages in thread
From: Zizhi Wo @ 2026-07-07 6:45 UTC (permalink / raw)
To: Damien Le Moal, Zizhi Wo, axboe, nilay, linux-block, kch,
johannes.thumshirn, kbusch, bvanassche
Cc: linux-kernel, yangerkun, chengzhihao1
在 2026/7/7 14:28, Damien Le Moal 写道:
> On 7/7/26 15:24, Zizhi Wo wrote:
>>
>>
>> 在 2026/7/7 12:16, Damien Le Moal 写道:
>>> On 7/7/26 11:55, Zizhi Wo wrote:
>>>> From: Zizhi Wo <wozizhi@huawei.com>
>>>>
>>>> The file-scope lock mutex serializes access to global null_blk state,
>>>> including the nullb_list and device creation/removal. Rename it to
>>>> "nullb_global_lock" to make its purpose clear. No functional change.
>>>>
>>>> Suggested-by: Bart Van Assche <bvanassche@acm.org>
>>>> Signed-off-by: Zizhi Wo <wozizhi@huawei.com>
>>>> ---
>>>> drivers/block/null_blk/main.c | 32 ++++++++++++++++----------------
>>>> 1 file changed, 16 insertions(+), 16 deletions(-)
>>>>
>>>> diff --git a/drivers/block/null_blk/main.c b/drivers/block/null_blk/main.c
>>>> index eba204b27785..98f6935bb502 100644
>>>> --- a/drivers/block/null_blk/main.c
>>>> +++ b/drivers/block/null_blk/main.c
>>>> @@ -66,7 +66,7 @@ struct nullb_page {
>>>> #define NULLB_PAGE_FREE (MAP_SZ - 2)
>>>>
>>>> static LIST_HEAD(nullb_list);
>>>> -static DEFINE_MUTEX(lock);
>>>> +static DEFINE_MUTEX(nullb_global_lock);
>>>
>>> Since this seem to protect only the device list, why not simply call this
>>> nullb_list_lock ?
>>>
>>>> static int null_major;
>>>> static DEFINE_IDA(nullb_indexes);
>>>> static struct blk_mq_tag_set tag_set;
>>>> @@ -423,9 +423,9 @@ static int nullb_apply_submit_queues(struct nullb_device *dev,
>>>> {
>>>> int ret;
>>>>
>>>> - mutex_lock(&lock);
>>>> + mutex_lock(&nullb_global_lock);
>>>> ret = nullb_update_nr_hw_queues(dev, submit_queues, dev->poll_queues);
>>>> - mutex_unlock(&lock);
>>>> + mutex_unlock(&nullb_global_lock);
>>>
>>> Nothing in nullb_update_nr_hw_queues() touches the device list. So it is very
>>> odd that nullb_global_lock is used for serialization here instead of a
>>> nullb_device mutex. If you change this, why not a prep patch to introduce such a
>>> mutex ?
>>>
>>
>> Thanks for pointing this out. The rename really belongs together with
>> the locking rework, so I'll drop this patch from v3 and fold the rename
>> into a separate series that splits the locking (introducing a per-
>> nullb_device mutex for the updates).
>
> Please carefully check if the use of the global lock in these function is not to
> serialize with power on/off. The code is a little (uselessly) complicated in
> that area and I may not be seeing something. But it seems that on/off control
> could also use a device lock, with proper ordering: device lock first, then list
> lock.
Thanks for the suggestion. At a first glance this does look doable, and
I will check it carefully. :)
Thanks,
Zizhi Wo
>
>
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH V2 5/6] null_blk: don't locklessly overwrite dev state after apply_fn
2026-07-07 2:55 ` [PATCH V2 5/6] null_blk: don't locklessly overwrite dev state after apply_fn Zizhi Wo
2026-07-07 3:38 ` Zizhi Wo
2026-07-07 4:21 ` Damien Le Moal
@ 2026-07-07 7:33 ` Nilay Shroff
2026-07-07 8:24 ` Zizhi Wo
2 siblings, 1 reply; 19+ messages in thread
From: Nilay Shroff @ 2026-07-07 7:33 UTC (permalink / raw)
To: Zizhi Wo, axboe, dlemoal, linux-block, kch, johannes.thumshirn,
kbusch, bvanassche
Cc: linux-kernel, yangerkun, chengzhihao1, wozizhi
On 7/7/26 8:25 AM, Zizhi Wo wrote:
> From: Zizhi Wo <wozizhi@huawei.com>
>
> The NULLB_DEVICE_ATTR macro unconditionally writes dev->NAME = new_value
> after apply_fn() returns. For attributes with an apply_fn (submit_queues,
> poll_queues), apply_fn already sets dev->NAME under &nullb_list_lock.
>
> configfs serializes writes via a per-open-file mutex (buffer->mutex), so
> two threads writing to the same attribute through separate open file
> descriptions run the store callback concurrently. The macro's write is
> redundant and lockless, so a concurrent store's losing thread can overwrite
> the winner's value after apply_fn set it, making dev->submit_queues
> mismatch the hardware state. null_map_queues() then hits a WARN_ON_ONCE and
> falls back to a single queue.
>
> Restructure the macro so that apply_fn attributes return directly after
> apply_fn, and only non-apply_fn attributes write dev->NAME -- those are
> only changeable while not CONFIGURED and have no live hardware state to
> mismatch.
>
> Fixes: 45919fbfe1c4 ("null_blk: Enable modifying 'submit_queues' after an instance has been configured")
> Signed-off-by: Zizhi Wo <wozizhi@huawei.com>
> ---
> drivers/block/null_blk/main.c | 14 ++++++++------
> 1 file changed, 8 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/block/null_blk/main.c b/drivers/block/null_blk/main.c
> index cab51301560e..e7555c47b671 100644
> --- a/drivers/block/null_blk/main.c
> +++ b/drivers/block/null_blk/main.c
> @@ -360,13 +360,15 @@ nullb_device_##NAME##_store(struct config_item *item, const char *page, \
> ret = nullb_device_##TYPE##_attr_store(&new_value, page, count);\
> if (ret < 0) \
> return ret; \
> - if (apply_fn) \
> + if (apply_fn) { \
> ret = apply_fn(dev, new_value); \
> - else if (test_bit(NULLB_DEV_FL_CONFIGURED, &dev->flags)) \
> - ret = -EBUSY; \
> - if (ret < 0) \
> - return ret; \
> - dev->NAME = new_value; \
> + if (ret < 0) \
> + return ret; \
> + } else { \
> + if (test_bit(NULLB_DEV_FL_CONFIGURED, &dev->flags)) \
> + return -EBUSY; \
> + dev->NAME = new_value; \
> + } \
> return count; \
> } \
> CONFIGFS_ATTR(nullb_device_, NAME);
Your patch correctly addresses the synchronization issue regarding the
apply_fn attributes under the nullb_list_lock. However, even with this
restructuring, the 'else' block remains vulnerable to concurrent
unmarked accesses to dev->NAME.
It seems when the device is not powered on, multiple threads can still
concurrently call the _show and _store callbacks on the same attribute
across separate open configfs file descriptions.
While this may be considered a benign data race, it can trigger KCSAN
splats and may allow the compiler to perform potentially unsafe optimization
heuristics (such as load/store tearing or merging). So I think we should
at-least mark those accesses using WRITE_ONCE() and READ_ONCE(). This also
help silence the KCSAN splat if it's configured.
Thanks,
--Nilay
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH V2 5/6] null_blk: don't locklessly overwrite dev state after apply_fn
2026-07-07 7:33 ` Nilay Shroff
@ 2026-07-07 8:24 ` Zizhi Wo
0 siblings, 0 replies; 19+ messages in thread
From: Zizhi Wo @ 2026-07-07 8:24 UTC (permalink / raw)
To: Nilay Shroff, Zizhi Wo, axboe, dlemoal, linux-block, kch,
johannes.thumshirn, kbusch, bvanassche
Cc: linux-kernel, yangerkun, chengzhihao1
在 2026/7/7 15:33, Nilay Shroff 写道:
> On 7/7/26 8:25 AM, Zizhi Wo wrote:
>> From: Zizhi Wo <wozizhi@huawei.com>
>>
>> The NULLB_DEVICE_ATTR macro unconditionally writes dev->NAME = new_value
>> after apply_fn() returns. For attributes with an apply_fn (submit_queues,
>> poll_queues), apply_fn already sets dev->NAME under &nullb_list_lock.
>>
>> configfs serializes writes via a per-open-file mutex (buffer->mutex), so
>> two threads writing to the same attribute through separate open file
>> descriptions run the store callback concurrently. The macro's write is
>> redundant and lockless, so a concurrent store's losing thread can
>> overwrite
>> the winner's value after apply_fn set it, making dev->submit_queues
>> mismatch the hardware state. null_map_queues() then hits a
>> WARN_ON_ONCE and
>> falls back to a single queue.
>>
>> Restructure the macro so that apply_fn attributes return directly after
>> apply_fn, and only non-apply_fn attributes write dev->NAME -- those are
>> only changeable while not CONFIGURED and have no live hardware state to
>> mismatch.
>>
>> Fixes: 45919fbfe1c4 ("null_blk: Enable modifying 'submit_queues' after
>> an instance has been configured")
>> Signed-off-by: Zizhi Wo <wozizhi@huawei.com>
>> ---
>> drivers/block/null_blk/main.c | 14 ++++++++------
>> 1 file changed, 8 insertions(+), 6 deletions(-)
>>
>> diff --git a/drivers/block/null_blk/main.c b/drivers/block/null_blk/
>> main.c
>> index cab51301560e..e7555c47b671 100644
>> --- a/drivers/block/null_blk/main.c
>> +++ b/drivers/block/null_blk/main.c
>> @@ -360,13 +360,15 @@ nullb_device_##NAME##_store(struct config_item
>> *item, const char *page, \
>> ret = nullb_device_##TYPE##_attr_store(&new_value, page, count);\
>> if (ret < 0) \
>> return ret; \
>> - if (apply_fn) \
>> + if (apply_fn) { \
>> ret = apply_fn(dev, new_value); \
>> - else if (test_bit(NULLB_DEV_FL_CONFIGURED, &dev->flags)) \
>> - ret = -EBUSY; \
>> - if (ret < 0) \
>> - return ret; \
>> - dev->NAME = new_value; \
>> + if (ret < 0) \
>> + return ret; \
>> + } else { \
>> + if (test_bit(NULLB_DEV_FL_CONFIGURED, &dev->flags)) \
>> + return -EBUSY; \
>> + dev->NAME = new_value; \
>> + } \
>> return count; \
>> } \
>> CONFIGFS_ATTR(nullb_device_, NAME);
>
> Your patch correctly addresses the synchronization issue regarding the
> apply_fn attributes under the nullb_list_lock. However, even with this
> restructuring, the 'else' block remains vulnerable to concurrent
> unmarked accesses to dev->NAME.
>
> It seems when the device is not powered on, multiple threads can still
> concurrently call the _show and _store callbacks on the same attribute
> across separate open configfs file descriptions.
>
> While this may be considered a benign data race, it can trigger KCSAN
> splats and may allow the compiler to perform potentially unsafe
> optimization
> heuristics (such as load/store tearing or merging). So I think we should
> at-least mark those accesses using WRITE_ONCE() and READ_ONCE(). This also
> help silence the KCSAN splat if it's configured.
>
> Thanks,
> --Nilay
>
Thanks for catching this! The 'else' path can indeed be accessed
concurrently; I'd left it alone earlier assuming there was no real
impact, but from the angle you described, adding READ_ONCE()/
WRITE_ONCE() does make sense. I'll fix this in the next version.
Thanks,
Zizhi Wo
^ permalink raw reply [flat|nested] 19+ messages in thread
end of thread, other threads:[~2026-07-07 8:24 UTC | newest]
Thread overview: 19+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-07 2:55 [PATCH V2 0/6] null_blk: fix init/exit races, leaks and cleanup Zizhi Wo
2026-07-07 2:55 ` [PATCH V2 1/6] null_blk: use DEFINE_MUTEX for the file-scope mutex Zizhi Wo
2026-07-07 4:07 ` Damien Le Moal
2026-07-07 2:55 ` [PATCH V2 2/6] null_blk: give the file-scope mutex a descriptive name Zizhi Wo
2026-07-07 4:16 ` Damien Le Moal
2026-07-07 6:24 ` Zizhi Wo
2026-07-07 6:28 ` Damien Le Moal
2026-07-07 6:45 ` Zizhi Wo
2026-07-07 2:55 ` [PATCH V2 3/6] null_blk: register configfs subsystem after creating default devices Zizhi Wo
2026-07-07 4:18 ` Damien Le Moal
2026-07-07 2:55 ` [PATCH V2 4/6] null_blk: move unregister_blkdev() after destroying dev in null_exit() Zizhi Wo
2026-07-07 4:19 ` Damien Le Moal
2026-07-07 2:55 ` [PATCH V2 5/6] null_blk: don't locklessly overwrite dev state after apply_fn Zizhi Wo
2026-07-07 3:38 ` Zizhi Wo
2026-07-07 4:21 ` Damien Le Moal
2026-07-07 7:33 ` Nilay Shroff
2026-07-07 8:24 ` Zizhi Wo
2026-07-07 2:55 ` [PATCH V2 6/6] null_blk: free global tag_set on init error path Zizhi Wo
2026-07-07 4:24 ` Damien Le Moal
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox