Linux block layer
 help / color / mirror / Atom feed
* Re: [PATCH V2 5/6] null_blk: don't locklessly overwrite dev state after apply_fn
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
In-Reply-To: <20260707025542.1299859-6-wozizhi@huaweicloud.com>



在 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

* Re: [PATCH V2 1/6] null_blk: use DEFINE_MUTEX for the file-scope mutex
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
In-Reply-To: <20260707025542.1299859-2-wozizhi@huaweicloud.com>

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

* Re: [PATCH V2 2/6] null_blk: give the file-scope mutex a descriptive name
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
In-Reply-To: <20260707025542.1299859-3-wozizhi@huaweicloud.com>

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

* Re: [PATCH V2 3/6] null_blk: register configfs subsystem after creating default devices
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
In-Reply-To: <20260707025542.1299859-4-wozizhi@huaweicloud.com>

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

* Re: [PATCH V2 4/6] null_blk: move unregister_blkdev() after destroying dev in null_exit()
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
In-Reply-To: <20260707025542.1299859-5-wozizhi@huaweicloud.com>

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

* Re: [PATCH V2 5/6] null_blk: don't locklessly overwrite dev state after apply_fn
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
In-Reply-To: <20260707025542.1299859-6-wozizhi@huaweicloud.com>

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

* Re: [PATCH V2 6/6] null_blk: free global tag_set on init error path
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
In-Reply-To: <20260707025542.1299859-7-wozizhi@huaweicloud.com>

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

* Re: [PATCH V2 2/6] null_blk: give the file-scope mutex a descriptive name
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
In-Reply-To: <0ab391b1-20e2-4ae9-a606-7111014e489c@kernel.org>



在 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

* Re: [PATCH V2 2/6] null_blk: give the file-scope mutex a descriptive name
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
In-Reply-To: <5dd82788-aa48-4f11-907f-6f21b57a27fa@huaweicloud.com>

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

* blktests: call for agreement to relicense GPL-2.0 files
From: Shin'ichiro Kawasaki @ 2026-07-07  6:38 UTC (permalink / raw)
  To: linux-block, Bart Van Assche, Ming Lei, Ming Lei, Keith Busch,
	Yi Zhang, Christoph Hellwig, Li Zhijian, Akinobu Mita,
	John Pittman

Hello blktests contributors,

Recent discussion unveiled a license inconsistency [1]. Currently, each file of
blktests has one of the licenses: GPL-2.0, GPL-2.0+ or GPL-3.0+. Majority of the
blktests code has GPL-3.0+, so the effective main license is GPL-3.0+ (e.g.,
check, common/rc). However, GPL-2.0 and GPL-3.0 are not compatible with each
other [2]. Because the majority of blktests is GPL-3.0+, blktests cannot contain
GPL-2.0 files. All files shall be GPL-2.0+ or GPL-3.0+.

  [1] https://lore.kernel.org/linux-block/20260626045650.GA8752@lst.de/
  [2] https://www.gnu.org/licenses/gpl-faq.html#v2v3Compatibility

As of July 2026, seven files below have GPL-2.0. It is desired to change their
license to GPL-2.0+ (or GPL-3.0+). The list below shows Authors and Contributors
of each file. "Author" means the original file creator who set the license
GPL-2.0. "Contributor" means those who made changes to the files later on.

1) block/029
    Author: Bart Van Assche <bvanassche@acm.org>
    Contributors: Yi Zhang <yi.zhang@redhat.com>
		  Christoph Hellwig <hch@lst.de>
		  Li Zhijian <lizhijian@fujitsu.com>

2) block/030
    Author: Bart Van Assche <bvanassche@acm.org>
    Contributors: Christoph Hellwig <hch@lst.de>
		  Li Zhijian <lizhijian@fujitsu.com>
		  Akinobu Mita <akinobu.mita@gmail.com>

3) block/031
    Author: Bart Van Assche <bvanassche@acm.org>
    Contributors: Yi Zhang <yi.zhang@redhat.com>
		  Christoph Hellwig <hch@lst.de>
		  Shin'ichiro Kawasaki <shinichiro.kawasaki@wdc.com>

4) block/040
    Author: Ming Lei <ming.lei@redhat.com>

5) block/042
    Author: Keith Busch <kbusch@kernel.org>
    Contributors: Shin'ichiro Kawasaki <shinichiro.kawasaki@wdc.com>
		  John Pittman <jpittman@redhat.com>

6) block/043
    Author: Keith Busch <kbusch@kernel.org>
    Contributors: Shin'ichiro Kawasaki <shinichiro.kawasaki@wdc.com>

7) src/dio-offsets.c
    Author: Keith Busch <kbusch@kernel.org>
    Contributors: Shin'ichiro Kawasaki <shinichiro.kawasaki@wdc.com>
		  Bart Van Assche <bvanassche@acm.org>


To the authors and the contributors,

  I would like to call for your responses. Please let us know if you agree to
  modify the license of the files from GPL-2.0 to GPL-2.0+ (or GPL-3.0+).


I will wait for a few weeks to gather responses. Once agreement gets
estabilished, I will ask the authors to submit patches to make the license
change. Any other general comments on this topic will be appreciated also.

And as one of the contributors, here I express my agreement to change the
license of the files :)

^ permalink raw reply

* Re: [PATCH V2 2/6] null_blk: give the file-scope mutex a descriptive name
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
In-Reply-To: <017a3a24-2b9a-4567-b2d6-a496a86da0fe@kernel.org>



在 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

* Re: [PATCH 1/3] zram: fix zstd dict use-after-free on per-CPU error path
From: Sergey Senozhatsky @ 2026-07-07  7:19 UTC (permalink / raw)
  To: Haoqin Huang
  Cc: minchan, senozhatsky, axboe, terrelln, dsterba, akpm,
	linux-kernel, linux-block, rongwei.wrw, Haoqin Huang,
	Rongwei Wang
In-Reply-To: <20260627070216.13511-1-haoqinhuang7@gmail.com>

On (26/06/27 15:02), Haoqin Huang wrote:
> zstd_setup_params() creates global cdict and ddict stored in
> params->drv_data, shared across all per-CPU contexts. When a
> per-CPU zstd_create() failed, its error path called
> zstd_release_params() which freed those shared objects while
> other per-CPU contexts might already hold references to them.
> 
> Remove the premature zstd_release_params() from the per-CPU
> error path, the global cdict/ddict are properly released later
> by zstd_release_params(), called from zcomp_init()'s cleanup
> or from zcomp_destroy().
> 
> Fixes: 6a559ecd6e7e ("zram: add dictionary support to zstd backend")
> Signed-off-by: Haoqin Huang <haoqinhuang@tencent.com>
> Reviewed-by: Rongwei Wang <zigiwang@tencent.com>

Apologies for the delay, I'm catching up on emails and will
look into it in the coming days.

^ permalink raw reply

* Re: [PATCH V2 5/6] null_blk: don't locklessly overwrite dev state after apply_fn
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
In-Reply-To: <20260707025542.1299859-6-wozizhi@huaweicloud.com>

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

* Re: [PATCH] xen-blkfront: fix double completion of split requests on resume
From: Roger Pau Monné @ 2026-07-07  7:48 UTC (permalink / raw)
  To: Doruk Tan Ozturk
  Cc: Juergen Gross, Stefano Stabellini, Oleksandr Tyshchenko,
	Jens Axboe, xen-devel, linux-block, linux-kernel
In-Reply-To: <20260705115639.72805-1-doruk@0sec.ai>

On Sun, Jul 05, 2026 at 01:56:39PM +0200, Doruk Tan Ozturk wrote:
> When a block request is too large for a single ring entry and the
> backend does not support indirect descriptors, blkfront splits it
> across two ring requests.  blkif_ring_get_request() is called twice
> and both shadow slots (shadow[id] and shadow[extra_id]) are made to
> point at the *same* struct request, linked together through
> associated_id.

This is not exactly accurate.  Under normal operation the blk queue
parameters are already set to ensure the requests match the maximum
size the ring can accommodate.  However on ARM there's a corner case
when the guest is using 64K pages, as in that case even a single page
request cannot possibly fit into a single ring slot, and thus needs to
be split.

This needs mentioning explicitly in the commit message, that such
splitting only happens when the frontend is running on a 64K page
kernel.

> 
> On the normal completion path blkif_completion() collapses the pair:
> it recycles the second slot via add_id_to_freelist() and only completes
> the request once.  The suspend/resume path in blkfront_resume() does
> not.  It walks every physical shadow slot and, for each slot whose
> ->request is set, calls blk_mq_end_request() or re-queues
> ->request.  For an in-flight split request this visits the shared
> struct request twice, so on resume/migration the same request is
> ended (or re-queued) two times.  The second visit is a double
> blk_mq_end_request() (refcount underflow / double free) and a
> use-after-free read of req->bio, which was cleared on the first visit.
> 
> Skip the secondary slot of a split request in the resume walk, so each
> logical request is completed or re-queued exactly once, matching how
> blkif_completion() already treats the pair.  The secondary slot is the
> one that is linked (associated_id != NO_ASSOCIATED_ID) and carries no
> scatter-gather list (num_sg == 0); the first slot always keeps the
> scatter-gather list.

I find the above slightly too verbose, I don't think you need to go
into details about why freeing a requests twice is bad, this is
already well-known.

> This was found by 0sec automated security-research tooling
> (https://0sec.ai).

Isn't this information already conveyed by the `Assisted-by` tag?

> The bug is only reachable on suspend/resume or live
> migration of a guest whose backend lacks indirect-descriptor support, so
> it has no local reproducer; the fix is by source inspection against the
> existing blkif_completion() collapse logic.
> 
> Fixes: 6cc568339047 ("xen/blkfront: Handle non-indirect grant with 64KB pages")
> Assisted-by: 0sec:claude-opus-4-8
> Signed-off-by: Doruk Tan Ozturk <doruk@0sec.ai>
> ---
>  drivers/block/xen-blkfront.c | 9 +++++++++
>  1 file changed, 9 insertions(+)
> 
> diff --git a/drivers/block/xen-blkfront.c b/drivers/block/xen-blkfront.c
> index f765970578f9..b2e83fd0c77b 100644
> --- a/drivers/block/xen-blkfront.c
> +++ b/drivers/block/xen-blkfront.c
> @@ -2079,6 +2079,15 @@ static int blkfront_resume(struct xenbus_device *dev)
>  			if (!shadow[j].request)
>  				continue;
>  
> +			/*
> +			 * Split requests alias one request across two shadow
> +			 * slots; skip the sg-less secondary so it completes
> +			 * once, like blkif_completion() does.

I would possibly avoid mentioning blkif_completion(), as those
references tend to get stale as code changes.  What about using:

"For requests split across multiple slots only process the underlying
requests once."

Or something similar?

Thanks, Roger.

^ permalink raw reply

* Re: [PATCH V2 5/6] null_blk: don't locklessly overwrite dev state after apply_fn
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
In-Reply-To: <94fcb5aa-0b1a-4d32-bc07-fd939367c6ee@linux.ibm.com>



在 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

* [PATCH 1/2] nbd: replace wait_for_reconnect with non-blocking retry
From: Yun Zhou @ 2026-07-07 10:27 UTC (permalink / raw)
  To: josef, axboe; +Cc: linux-block, nbd, linux-kernel, yun.zhou

wait_for_reconnect() sleeps in the block dispatch path holding the
SRCU read lock.  This blocks blk_mq_quiesce_queue() (which needs
synchronize_srcu) for the entire dead_conn_timeout duration, triggering
hung task warnings.

Replace it with a non-blocking nbd_reconnect_possible() check that
returns BLK_STS_DEV_RESOURCE to keep the request on the dispatch list,
with a 1-second delayed queue run to re-evaluate.  Once the timeout
expires or the device is disconnected, fail the I/O immediately.

Fixes: 560bc4b39952 ("nbd: handle dead connections")
Reported-by: syzbot+30c16035531e3248dcbc@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=30c16035531e3248dcbc
Signed-off-by: Yun Zhou <yun.zhou@windriver.com>
---
 drivers/block/nbd.c | 61 +++++++++++++++++++++++++++------------------
 1 file changed, 37 insertions(+), 24 deletions(-)

diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c
index 70a04d541ea4..43aa4121f0c5 100644
--- a/drivers/block/nbd.c
+++ b/drivers/block/nbd.c
@@ -94,11 +94,11 @@ struct nbd_config {
 	u32 flags;
 	unsigned long runtime_flags;
 	u64 dead_conn_timeout;
+	unsigned long dead_conn_start; /* jiffies, 0 = not waiting */
 
 	struct nbd_sock **socks;
 	int num_connections;
 	atomic_t live_connections;
-	wait_queue_head_t conn_wait;
 
 	atomic_t recv_threads;
 	wait_queue_head_t recv_wq;
@@ -1124,20 +1124,25 @@ static int find_fallback(struct nbd_device *nbd, int index)
 	return new_index;
 }
 
-static int wait_for_reconnect(struct nbd_device *nbd)
+static bool nbd_reconnect_possible(struct nbd_device *nbd)
 {
 	struct nbd_config *config = nbd->config;
+
 	if (!config->dead_conn_timeout)
-		return 0;
+		return false;
+	if (test_bit(NBD_RT_DISCONNECTED, &config->runtime_flags))
+		return false;
 
-	if (!wait_event_timeout(config->conn_wait,
-				test_bit(NBD_RT_DISCONNECTED,
-					 &config->runtime_flags) ||
-				atomic_read(&config->live_connections) > 0,
-				config->dead_conn_timeout))
-		return 0;
+	/* Record when all connections first went dead */
+	if (!READ_ONCE(config->dead_conn_start))
+		WRITE_ONCE(config->dead_conn_start, jiffies ? : 1);
+
+	/* Check if we've exceeded the reconnect timeout */
+	if (time_after(jiffies, READ_ONCE(config->dead_conn_start) +
+		       (unsigned long)config->dead_conn_timeout))
+		return false;
 
-	return !test_bit(NBD_RT_DISCONNECTED, &config->runtime_flags);
+	return true;
 }
 
 static blk_status_t nbd_handle_cmd(struct nbd_cmd *cmd, int index)
@@ -1168,23 +1173,24 @@ static blk_status_t nbd_handle_cmd(struct nbd_cmd *cmd, int index)
 	nsock = config->socks[index];
 	mutex_lock(&nsock->tx_lock);
 	if (nsock->dead) {
-		int old_index = index;
 		index = find_fallback(nbd, index);
 		mutex_unlock(&nsock->tx_lock);
 		if (index < 0) {
-			if (wait_for_reconnect(nbd)) {
-				index = old_index;
-				goto again;
+			if (!nbd_reconnect_possible(nbd)) {
+				sock_shutdown(nbd);
+				nbd_config_put(nbd);
+				return BLK_STS_IOERR;
 			}
-			/* All the sockets should already be down at this point,
-			 * we just want to make sure that DISCONNECTED is set so
-			 * any requests that come in that were queue'ed waiting
-			 * for the reconnect timer don't trigger the timer again
-			 * and instead just error out.
+			/*
+			 * All connections are dead but reconnect timeout
+			 * has not expired.  Return BLK_STS_DEV_RESOURCE
+			 * so the request stays on the dispatch list, and
+			 * schedule a delayed queue run after 1 second to
+			 * re-evaluate.
 			 */
-			sock_shutdown(nbd);
+			blk_mq_delay_run_hw_queues(nbd->disk->queue, 1000);
 			nbd_config_put(nbd);
-			return BLK_STS_IOERR;
+			return BLK_STS_DEV_RESOURCE;
 		}
 		goto again;
 	}
@@ -1437,7 +1443,9 @@ static int nbd_reconnect_socket(struct nbd_device *nbd, unsigned long arg)
 		queue_work(nbd->recv_workq, &args->work);
 
 		atomic_inc(&config->live_connections);
-		wake_up(&config->conn_wait);
+		/* Reconnected -- stop waiting for dead_conn_timeout */
+		WRITE_ONCE(config->dead_conn_start, 0);
+		blk_mq_run_hw_queues(nbd->disk->queue, true);
 		return 0;
 	}
 	sk_clear_memalloc(sock->sk);
@@ -1499,6 +1507,13 @@ static int nbd_disconnect(struct nbd_device *nbd)
 	dev_info(disk_to_dev(nbd->disk), "NBD_DISCONNECT\n");
 	set_bit(NBD_RT_DISCONNECT_REQUESTED, &config->runtime_flags);
 	set_bit(NBD_DISCONNECT_REQUESTED, &nbd->flags);
+	/*
+	 * If all connections are already dead, no sock_shutdown callback
+	 * will fire to set NBD_RT_DISCONNECTED.  Set it here so
+	 * nbd_reconnect_possible() stops waiting immediately.
+	 */
+	if (atomic_read(&config->live_connections) == 0)
+		set_bit(NBD_RT_DISCONNECTED, &config->runtime_flags);
 	send_disconnects(nbd);
 	return 0;
 }
@@ -1766,7 +1781,6 @@ static int nbd_alloc_and_init_config(struct nbd_device *nbd)
 
 	atomic_set(&config->recv_threads, 0);
 	init_waitqueue_head(&config->recv_wq);
-	init_waitqueue_head(&config->conn_wait);
 	config->blksize_bits = NBD_DEF_BLKSIZE_BITS;
 	atomic_set(&config->live_connections, 0);
 
@@ -2337,7 +2351,6 @@ static void nbd_disconnect_and_put(struct nbd_device *nbd)
 	mutex_lock(&nbd->config_lock);
 	nbd_disconnect(nbd);
 	sock_shutdown(nbd);
-	wake_up(&nbd->config->conn_wait);
 	/*
 	 * Clear NBD_RT_BOUND before releasing config_lock so that
 	 * nbd_genl_reconfigure() won't queue new recv_work between
-- 
2.43.0


^ permalink raw reply related

* [PATCH 2/2] nbd: detect management process exit via netlink notifier
From: Yun Zhou @ 2026-07-07 10:27 UTC (permalink / raw)
  To: josef, axboe; +Cc: linux-block, nbd, linux-kernel, yun.zhou
In-Reply-To: <20260707102724.3838638-1-yun.zhou@windriver.com>

Register a netlink notifier to detect when the NBD management process
closes its netlink socket (on exit or crash).

Without this, in-flight I/O waits for socket timeout while udevd holds
disk->open_mutex for partition scanning, blocking poweroff's
sync_bdevs() and causing a hung task during shutdown.

Fix this by tracking the management process's netlink portid and, on
NETLINK_URELEASE, calling sock_shutdown() on all devices it owns.
This fails in-flight I/O immediately so shutdown can proceed.

Signed-off-by: Yun Zhou <yun.zhou@windriver.com>
---
 drivers/block/nbd.c | 59 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 59 insertions(+)

diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c
index 43aa4121f0c5..3bf88f7b3535 100644
--- a/drivers/block/nbd.c
+++ b/drivers/block/nbd.c
@@ -45,6 +45,7 @@
 #include <linux/nbd.h>
 #include <linux/nbd-netlink.h>
 #include <net/genetlink.h>
+#include <linux/netlink.h>
 
 #define CREATE_TRACE_POINTS
 #include <trace/events/nbd.h>
@@ -131,6 +132,7 @@ struct nbd_device {
 
 	unsigned long flags;
 	pid_t pid; /* pid of nbd-client, if attached */
+	u32 owner_portid; /* netlink portid of management process */
 
 	char *backend;
 };
@@ -2331,12 +2333,19 @@ static int nbd_genl_connect(struct sk_buff *skb, struct genl_info *info)
 	}
 	set_bit(NBD_RT_HAS_BACKEND_FILE, &config->runtime_flags);
 
+	/*
+	 * Set owner before starting device so that the netlink notifier
+	 * can clean up if the management process exits during setup.
+	 */
+	nbd->owner_portid = info->snd_portid;
 	ret = nbd_start_device(nbd);
 out:
 	if (!ret) {
 		set_bit(NBD_RT_HAS_CONFIG_REF, &config->runtime_flags);
 		refcount_inc(&nbd->config_refs);
 		nbd_connect_reply(info, nbd->index);
+	} else {
+		nbd->owner_portid = 0;
 	}
 	mutex_unlock(&nbd->config_lock);
 
@@ -2541,6 +2550,12 @@ static int nbd_genl_reconfigure(struct sk_buff *skb, struct genl_info *info)
 		}
 	}
 out:
+	/*
+	 * Update owner to the reconfiguring process so the notifier
+	 * tracks the correct management process.
+	 */
+	if (!ret)
+		nbd->owner_portid = info->snd_portid;
 	mutex_unlock(&nbd->config_lock);
 	nbd_config_put(nbd);
 	nbd_put(nbd);
@@ -2743,6 +2758,48 @@ static void nbd_dead_link_work(struct work_struct *work)
 	kfree(args);
 }
 
+/*
+ * Detect when the management process (the one that issued NBD_CMD_CONNECT)
+ * closes its netlink socket (e.g., exit or crash).  When this happens,
+ * no one can reconnect, so mark all its devices as disconnected to stop
+ * waiting for reconnect and fail pending I/O promptly.
+ */
+static int nbd_netlink_event(struct notifier_block *this,
+			     unsigned long event, void *ptr)
+{
+	struct netlink_notify *n = ptr;
+	struct nbd_device *nbd;
+	int id;
+
+	if (event != NETLINK_URELEASE || n->protocol != NETLINK_GENERIC)
+		return NOTIFY_DONE;
+
+	mutex_lock(&nbd_index_mutex);
+	idr_for_each_entry(&nbd_index_idr, nbd, id) {
+		if (nbd->owner_portid != n->portid)
+			continue;
+
+		nbd->owner_portid = 0;
+		if (refcount_inc_not_zero(&nbd->config_refs)) {
+			/*
+			 * Shut down all sockets.  sock_shutdown() holds
+			 * tx_lock for each socket, preventing races with
+			 * reconnect, and sets NBD_RT_DISCONNECTED so that
+			 * nbd_reconnect_possible() fails immediately.
+			 */
+			sock_shutdown(nbd);
+			nbd_config_put(nbd);
+		}
+	}
+	mutex_unlock(&nbd_index_mutex);
+
+	return NOTIFY_DONE;
+}
+
+static struct notifier_block nbd_netlink_notifier = {
+	.notifier_call = nbd_netlink_event,
+};
+
 static int __init nbd_init(void)
 {
 	int i;
@@ -2789,6 +2846,7 @@ static int __init nbd_init(void)
 		unregister_blkdev(NBD_MAJOR, "nbd");
 		return -EINVAL;
 	}
+	netlink_register_notifier(&nbd_netlink_notifier);
 	nbd_dbg_init();
 
 	for (i = 0; i < nbds_max; i++)
@@ -2818,6 +2876,7 @@ static void __exit nbd_cleanup(void)
 	 * for the completion of netlink commands.
 	 */
 	genl_unregister_family(&nbd_genl_family);
+	netlink_unregister_notifier(&nbd_netlink_notifier);
 
 	nbd_dbg_close();
 
-- 
2.43.0


^ permalink raw reply related

* [PATCH] blk-cgroup: fix blkg_rwstat_recursive_sum() locking kernel-doc
From: Guopeng Zhang @ 2026-07-07 12:19 UTC (permalink / raw)
  To: Jens Axboe; +Cc: linux-block, linux-kernel, Guopeng Zhang

From: Guopeng Zhang <zhangguopeng@kylinos.cn>

blkg_rwstat_recursive_sum() now runs under RCU read-side protection: it
warns if the RCU read lock is not held and no longer relies on the queue
lock for online tests. Update the kernel-doc to match the code.

Signed-off-by: Guopeng Zhang <zhangguopeng@kylinos.cn>
---
 block/blk-cgroup-rwstat.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/block/blk-cgroup-rwstat.c b/block/blk-cgroup-rwstat.c
index aae910713814..20cbcbe064ac 100644
--- a/block/blk-cgroup-rwstat.c
+++ b/block/blk-cgroup-rwstat.c
@@ -88,8 +88,8 @@ EXPORT_SYMBOL_GPL(blkg_prfill_rwstat);
  * @sum: blkg_rwstat_sample structure containing the results
  *
  * Collect the blkg_rwstat specified by @blkg, @pol and @off and all its
- * online descendants and their aux counts.  The caller must be holding the
- * queue lock for online tests.
+ * online descendants and their aux counts.  The caller must hold the
+ * RCU read lock.
  *
  * If @pol is NULL, blkg_rwstat is at @off bytes into @blkg; otherwise, it
  * is at @off bytes into @blkg's blkg_policy_data of the policy.
-- 
2.43.0


^ permalink raw reply related

* [PATCH] blk-ioprio: fix stale prio_policy kernel-doc
From: Guopeng Zhang @ 2026-07-07 12:21 UTC (permalink / raw)
  To: Jens Axboe; +Cc: linux-block, linux-kernel, Guopeng Zhang

From: Guopeng Zhang <zhangguopeng@kylinos.cn>

Fix two stale kernel-doc entries in blk-ioprio. The
POLICY_PROMOTE_TO_RT comment should describe promotion from
non-IOPRIO_CLASS_RT classes, and @prio_policy stores enum prio_policy
values rather than IOPRIO_CLASS_* values.

Signed-off-by: Guopeng Zhang <zhangguopeng@kylinos.cn>
---
 block/blk-ioprio.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/block/blk-ioprio.c b/block/blk-ioprio.c
index 8fa8bca35062..a94c50233d90 100644
--- a/block/blk-ioprio.c
+++ b/block/blk-ioprio.c
@@ -23,7 +23,8 @@
 /**
  * enum prio_policy - I/O priority class policy.
  * @POLICY_NO_CHANGE: (default) do not modify the I/O priority class.
- * @POLICY_PROMOTE_TO_RT: modify no-IOPRIO_CLASS_RT to IOPRIO_CLASS_RT.
+ * @POLICY_PROMOTE_TO_RT: promote non-IOPRIO_CLASS_RT classes to
+ *		IOPRIO_CLASS_RT.
  * @POLICY_RESTRICT_TO_BE: modify IOPRIO_CLASS_NONE and IOPRIO_CLASS_RT into
  *		IOPRIO_CLASS_BE.
  * @POLICY_ALL_TO_IDLE: change the I/O priority class into IOPRIO_CLASS_IDLE.
@@ -52,7 +53,7 @@ static struct blkcg_policy ioprio_policy;
 /**
  * struct ioprio_blkcg - Per cgroup data.
  * @cpd: blkcg_policy_data structure.
- * @prio_policy: One of the IOPRIO_CLASS_* values. See also <linux/ioprio.h>.
+ * @prio_policy: One of the POLICY_* values defined by enum prio_policy.
  */
 struct ioprio_blkcg {
 	struct blkcg_policy_data cpd;
-- 
2.43.0


^ permalink raw reply related

* [PATCH] blk-cgroup: clear blkg->pd[] with WRITE_ONCE() in blkcg_deactivate_policy()
From: Guopeng Zhang @ 2026-07-07 12:58 UTC (permalink / raw)
  To: Tejun Heo, Josef Bacik, Jens Axboe
  Cc: Yu Kuai, cgroups, linux-block, linux-kernel, Guopeng Zhang,
	Guopeng Zhang

From: Guopeng Zhang <zhangguopeng@kylinos.cn>

blkcg_activate_policy() installs blkg->pd[] entries with WRITE_ONCE()
and also uses WRITE_ONCE() when clearing them on its error path.
blkg_to_pd() is used by RCU readers and reads the same array with
READ_ONCE().

blkcg_deactivate_policy() clears the entry with a plain store. Use
WRITE_ONCE() there as well.

Fixes: 56cc24f59c14 ("blk-cgroup: don't nest queue_lock under rcu in blkcg_print_blkgs()")
Signed-off-by: Guopeng Zhang <zhangguopeng@kylinos.cn>
---
 block/blk-cgroup.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c
index d2a1f5903f24..a1dd69f99f5c 100644
--- a/block/blk-cgroup.c
+++ b/block/blk-cgroup.c
@@ -1691,7 +1691,7 @@ void blkcg_deactivate_policy(struct gendisk *disk,
 			if (blkg->pd[pol->plid]->online && pol->pd_offline_fn)
 				pol->pd_offline_fn(blkg->pd[pol->plid]);
 			pol->pd_free_fn(blkg->pd[pol->plid]);
-			blkg->pd[pol->plid] = NULL;
+			WRITE_ONCE(blkg->pd[pol->plid], NULL);
 		}
 		spin_unlock(&blkcg->lock);
 	}
-- 
2.43.0


^ permalink raw reply related

* Re: [PATCH v19 4/8] rust: page: convert to `Ownable`
From: Alice Ryhl @ 2026-07-07 13:04 UTC (permalink / raw)
  To: Andreas Hindborg
  Cc: Danilo Krummrich, Lorenzo Stoakes, Vlastimil Babka,
	Liam R. Howlett, Uladzislau Rezki, Miguel Ojeda, Boqun Feng,
	Gary Guo, Björn Roy Baron, Benno Lossin, Trevor Gross,
	Daniel Almeida, Tamir Duberstein, Alexandre Courbot,
	Onur Özkan, Lyude Paul, Greg Kroah-Hartman,
	Arve Hjønnevåg, Todd Kjos, Christian Brauner,
	Carlos Llamas, Rafael J. Wysocki, Dave Ertman, Ira Weiny,
	Leon Romanovsky, Paul Moore, Serge Hallyn, David Airlie,
	Simona Vetter, Alexander Viro, Jan Kara, Igor Korotin,
	Viresh Kumar, Nishanth Menon, Stephen Boyd, Bjorn Helgaas,
	Krzysztof Wilczyński, Pavel Tikhomirov, Michal Wilczynski,
	Philipp Stanner, rust-for-linux, linux-kernel, linux-mm,
	driver-core, linux-block, linux-security-module, dri-devel,
	linux-fsdevel, linux-pm, linux-pci, linux-pwm, Asahi Lina
In-Reply-To: <20260626-unique-ref-v19-4-2607ca88dfdf@kernel.org>

On Fri, Jun 26, 2026 at 1:56 PM Andreas Hindborg <a.hindborg@kernel.org> wrote:
>
> From: Asahi Lina <lina@asahilina.net>
>
> This allows Page references to be returned as borrowed references,
> without necessarily owning the struct page.
>
> Remove `BorrowedPage` and update users to use `Owned<Page>`.
>
> Signed-off-by: Asahi Lina <lina@asahilina.net>
> [ Andreas: Fix formatting and add a safety comment, update users. ]
> Signed-off-by: Andreas Hindborg <a.hindborg@kernel.org>
> Reviewed-by: Gary Guo <gary@garyguo.net>

Reviewed-by: Alice Ryhl <aliceryhl@google.com>

>      /// Returns a raw pointer to the page.
>      pub fn as_ptr(&self) -> *mut bindings::page {
> -        self.page.as_ptr()
> +        Opaque::cast_into(&self.page)

cast_into() is mainly used when you have a raw pointer to opaque. When
you have a reference, you can just do self.page.get()

Alice

^ permalink raw reply

* Re: [PATCH v19 3/8] rust: implement `ForeignOwnable` for `Owned`
From: Alice Ryhl @ 2026-07-07 13:04 UTC (permalink / raw)
  To: Andreas Hindborg
  Cc: Danilo Krummrich, Lorenzo Stoakes, Vlastimil Babka,
	Liam R. Howlett, Uladzislau Rezki, Miguel Ojeda, Boqun Feng,
	Gary Guo, Björn Roy Baron, Benno Lossin, Trevor Gross,
	Daniel Almeida, Tamir Duberstein, Alexandre Courbot,
	Onur Özkan, Lyude Paul, Greg Kroah-Hartman,
	Arve Hjønnevåg, Todd Kjos, Christian Brauner,
	Carlos Llamas, Rafael J. Wysocki, Dave Ertman, Ira Weiny,
	Leon Romanovsky, Paul Moore, Serge Hallyn, David Airlie,
	Simona Vetter, Alexander Viro, Jan Kara, Igor Korotin,
	Viresh Kumar, Nishanth Menon, Stephen Boyd, Bjorn Helgaas,
	Krzysztof Wilczyński, Pavel Tikhomirov, Michal Wilczynski,
	Philipp Stanner, rust-for-linux, linux-kernel, linux-mm,
	driver-core, linux-block, linux-security-module, dri-devel,
	linux-fsdevel, linux-pm, linux-pci, linux-pwm
In-Reply-To: <20260626-unique-ref-v19-3-2607ca88dfdf@kernel.org>

On Fri, Jun 26, 2026 at 1:55 PM Andreas Hindborg <a.hindborg@kernel.org> wrote:
>
> Implement `ForeignOwnable` for `Owned<T>`. This allows use of `Owned<T>` in
> places such as the `XArray`.
>
> Note that `T` does not need to implement `ForeignOwnable` for `Owned<T>` to
> implement `ForeignOwnable`.
>
> Signed-off-by: Andreas Hindborg <a.hindborg@kernel.org>
> Reviewed-by: Gary Guo <gary@garyguo.net>

Reviewed-by: Alice Ryhl <aliceryhl@google.com>

^ permalink raw reply

* Re: [PATCH v2 10/83] block: rust: allow `hrtimer::Timer` in `RequestData`
From: Andreas Hindborg @ 2026-07-07 13:45 UTC (permalink / raw)
  To: 하승종
  Cc: Liam R. Howlett, Alice Ryhl, Anna-Maria Behnsen, Benno Lossin,
	Björn Roy Baron, Boqun Feng, Danilo Krummrich,
	FUJITA Tomonori, Frederic Weisbecker, Gary Guo, Jens Axboe,
	John Stultz, Lorenzo Stoakes, Lyude Paul, Miguel Ojeda,
	Stephen Boyd, Thomas Gleixner, Trevor Gross, linux-block,
	linux-kernel, linux-mm, rust-for-linux
In-Reply-To: <CAGAB667mVuECQDwYDij5PcZ-fvqr-6womuRCHibrwiR_wP=YDg@mail.gmail.com>

하승종 <engineer.jjhama@gmail.com> writes:

> 2026년 6월 10일 (수) 오전 4:23, Andreas Hindborg <a.hindborg@kernel.org>님이 작성:
>
>> +    fn cancel(&mut self) -> bool {
>> +        let request_data_ptr = &self.inner.wrapper_ref().data as *const T::RequestData;
>
> I think this trips clippy::ref_as_ptr, which is enabled in the top-level
> Makefile (-Wclippy::ref_as_ptr), so `make CLIPPY=1` fails here under
> CONFIG_RUST_WERROR. IMO core::ptr::from_ref() would read better.

Thanks. I'm not sure why I did not see clippy complaining. I agree
`ptr::from_ref` is a better choice.

>
>> +        let pdu_ptr = self.data_ref() as *const T::RequestData;
>
> Same here, I'd go with:
>
>     let pdu_ptr = core::ptr::from_ref(self.data_ref());
>
> I applied both changes on top of rnull-v7.1-rc2 and `make CLIPPY=1` passes
> cleanly here (the two ref_as_ptr errors go away, no new warnings).

I'll add this in next version.

>
> Best regards,
> SeungJong Ha
>
> ---
> I'm not a regular reviewer for this subsystem — I just ran into this while
> building your rnull tree with clippy. Let me know if drive-by notes like this
> aren't the right fit here.

I appreciate the comments, please do continue sending them if you come
across more stuff.

Best regards,
Andreas


^ permalink raw reply

* Re: [PATCH v19 31/40] dept: assign unique dept_key to each distinct wait_for_completion() caller
From: Gary Guo @ 2026-07-07 14:18 UTC (permalink / raw)
  To: Byungchul Park, linux-kernel
  Cc: max.byungchul.park, kernel_team, torvalds, damien.lemoal,
	linux-ide, adilger.kernel, linux-ext4, mingo, peterz, will, tglx,
	rostedt, joel, sashal, daniel.vetter, duyuyang, johannes.berg, tj,
	tytso, willy, david, amir73il, gregkh, kernel-team, linux-mm,
	akpm, mhocko, minchan, hannes, vdavydov.dev, sj, jglisse, dennis,
	cl, penberg, rientjes, vbabka, ngupta, linux-block, josef,
	linux-fsdevel, jack, jlayton, dan.j.williams, hch, djwong,
	dri-devel, rodrigosiqueiramelo, melissa.srw, hamohammed.sa,
	harry.yoo, chris.p.wilson, gwan-gyeong.mun, boqun.feng, longman,
	yunseong.kim, ysk, yeoreum.yun, netdev, matthew.brost, her0gyugyu,
	corbet, catalin.marinas, bp, x86, hpa, luto, sumit.semwal,
	gustavo, christian.koenig, andi.shyti, arnd, lorenzo.stoakes,
	Liam.Howlett, rppt, surenb, mcgrof, petr.pavlu, da.gomez,
	samitolvanen, paulmck, frederic, neeraj.upadhyay, joelagnelf,
	josh, urezki, mathieu.desnoyers, jiangshanlai, qiang.zhang,
	juri.lelli, vincent.guittot, dietmar.eggemann, bsegall, mgorman,
	vschneid, chuck.lever, neil, okorniev, Dai.Ngo, tom, trondmy,
	anna, kees, bigeasy, clrkwllms, mark.rutland, ada.coupriediaz,
	kristina.martsenko, wangkefeng.wang, broonie, kevin.brodsky, dwmw,
	shakeel.butt, ast, ziy, yuzhao, baolin.wang, usamaarif642,
	joel.granados, richard.weiyang, geert+renesas, tim.c.chen, linux,
	alexander.shishkin, lillian, chenhuacai, francesco,
	guoweikang.kernel, link, jpoimboe, masahiroy, brauner,
	thomas.weissschuh, oleg, mjguzik, andrii, wangfushuai, linux-doc,
	linux-arm-kernel, linux-media, linaro-mm-sig, linux-i2c,
	linux-arch, linux-modules, rcu, linux-nfs, linux-rt-devel,
	2407018371, dakr, miguel.ojeda.sandonis, neilb, bagasdotme,
	wsa+renesas, dave.hansen, geert, ojeda, alex.gaynor, gary,
	bjorn3_gh, lossin, a.hindborg, aliceryhl, tmgross, rust-for-linux
In-Reply-To: <20260706061928.66713-32-byungchul@sk.com>

On Mon Jul 6, 2026 at 7:19 AM BST, Byungchul Park wrote:
> wait_for_completion() can be used at various points in the code and it's
> very hard to distinguish wait_for_completion()s between different usages.
> Using a single dept_key for all the wait_for_completion()s could trigger
> false positive reports.
>
> Assign unique dept_key to each distinct wait_for_completion() caller to
> avoid false positive reports.
>
> While at it, add a rust helper for wait_for_completion() to avoid build
> errors.

This will cause Rust code to share the same dept_key, so it will have all the
false positives that the change is trying to avoid.

In general it is easy to create Rust bindings for static inline C functions
because it'll be just some computation, while creating bindings for C
function-like macros that define additional statics can be challenging.

Is dept_key similar to lock_class_key, where only the address matters? If so,
the approach that I use in
https://lore.kernel.org/rust-for-linux/DJP0CDOR98N5.29BK8PUFRWRUK@garyguo.net
could be used for dept_key as well, then we can keep Rust `wait_for_completion`
still a function; otherwise we have to turn it into a macro too on the Rust side
to create such statics, which isn't ideal.

Best,
Gary

>
> Signed-off-by: Byungchul Park <byungchul@sk.com>
> ---
>  include/linux/completion.h | 100 +++++++++++++++++++++++++++++++------
>  kernel/sched/completion.c  |  60 +++++++++++-----------
>  rust/helpers/completion.c  |   5 ++
>  3 files changed, 120 insertions(+), 45 deletions(-)

^ permalink raw reply

* [PATCH 6.12.y 1/2] block: add a store_limit operations for sysfs entries
From: Konstantin Andreev @ 2026-07-07 15:37 UTC (permalink / raw)
  To: stable
  Cc: linux-block, linux-kernel, Christoph Hellwig, Ming Lei,
	Damien Le Moal, Martin K. Petersen, Nilay Shroff,
	Johannes Thumshirn, John Garry, Jens Axboe

From: Christoph Hellwig <hch@lst.de>

[ Upstream commit a16230649ce27f8ac7dd8a5b079d9657aa96de16 ]

De-duplicate the code for updating queue limits by adding a store_limit
method that allows having common code handle the actual queue limits
update.

Note that this is a pure refactoring patch and does not address the
existing freeze vs limits lock order problem in the refactored code,
which will be addressed next.

[6.12.y-backport note: dropped "iostats_passthrough"
queue limit parameter from the upstream commit,
as this parameter does not exist in the 6.12 kernel branch]

Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Ming Lei <ming.lei@redhat.com>
Reviewed-by: Damien Le Moal <dlemoal@kernel.org>
Reviewed-by: Martin K. Petersen <martin.petersen@oracle.com>
Reviewed-by: Nilay Shroff <nilay@linux.ibm.com>
Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
Reviewed-by: John Garry <john.g.garry@oracle.com>
Link: https://lore.kernel.org/r/20250110054726.1499538-6-hch@lst.de
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Stable-dep-of: c99f66e4084a ("block: fix queue freeze vs limits lock order in sysfs store methods")
Signed-off-by: Konstantin Andreev <andreev@swemel.ru>
---
The prerequisite patch to backport of the CVE-2025-21807 fix to 6.12.y LTS
(see Stable-dep-of)

The patch set applies on top of linux-6.12.y tag v6.12.95

 block/blk-sysfs.c | 108 +++++++++++++++++++++++-----------------------
 1 file changed, 54 insertions(+), 54 deletions(-)

diff --git a/block/blk-sysfs.c b/block/blk-sysfs.c
index 6a38f312e385..c9e572ae0b9c 100644
--- a/block/blk-sysfs.c
+++ b/block/blk-sysfs.c
@@ -25,6 +25,8 @@ struct queue_sysfs_entry {
 	ssize_t (*show)(struct gendisk *disk, char *page);
 	int (*load_module)(struct gendisk *disk, const char *page, size_t count);
 	ssize_t (*store)(struct gendisk *disk, const char *page, size_t count);
+	int (*store_limit)(struct gendisk *disk, const char *page,
+			size_t count, struct queue_limits *lim);
 };
 
 static ssize_t
@@ -152,13 +154,11 @@ QUEUE_SYSFS_SHOW_CONST(discard_zeroes_data, 0)
 QUEUE_SYSFS_SHOW_CONST(write_same_max, 0)
 QUEUE_SYSFS_SHOW_CONST(poll_delay, -1)
 
-static ssize_t queue_max_discard_sectors_store(struct gendisk *disk,
-		const char *page, size_t count)
+static int queue_max_discard_sectors_store(struct gendisk *disk,
+		const char *page, size_t count, struct queue_limits *lim)
 {
 	unsigned long max_discard_bytes;
-	struct queue_limits lim;
 	ssize_t ret;
-	int err;
 
 	ret = queue_var_store(&max_discard_bytes, page, count);
 	if (ret < 0)
@@ -170,12 +170,8 @@ static ssize_t queue_max_discard_sectors_store(struct gendisk *disk,
 	if ((max_discard_bytes >> SECTOR_SHIFT) > UINT_MAX)
 		return -EINVAL;
 
-	lim = queue_limits_start_update(disk->queue);
-	lim.max_user_discard_sectors = max_discard_bytes >> SECTOR_SHIFT;
-	err = queue_limits_commit_update(disk->queue, &lim);
-	if (err)
-		return err;
-	return ret;
+	lim->max_user_discard_sectors = max_discard_bytes >> SECTOR_SHIFT;
+	return 0;
 }
 
 /*
@@ -190,30 +186,24 @@ static ssize_t queue_zone_append_max_show(struct gendisk *disk, char *page)
 			SECTOR_SHIFT);
 }
 
-static ssize_t
-queue_max_sectors_store(struct gendisk *disk, const char *page, size_t count)
+static int
+queue_max_sectors_store(struct gendisk *disk, const char *page, size_t count,
+		struct queue_limits *lim)
 {
 	unsigned long max_sectors_kb;
-	struct queue_limits lim;
 	ssize_t ret;
-	int err;
 
 	ret = queue_var_store(&max_sectors_kb, page, count);
 	if (ret < 0)
 		return ret;
 
-	lim = queue_limits_start_update(disk->queue);
-	lim.max_user_sectors = max_sectors_kb << 1;
-	err = queue_limits_commit_update(disk->queue, &lim);
-	if (err)
-		return err;
-	return ret;
+	lim->max_user_sectors = max_sectors_kb << 1;
+	return 0;
 }
 
 static ssize_t queue_feature_store(struct gendisk *disk, const char *page,
-		size_t count, blk_features_t feature)
+		size_t count, struct queue_limits *lim, blk_features_t feature)
 {
-	struct queue_limits lim;
 	unsigned long val;
 	ssize_t ret;
 
@@ -221,15 +211,11 @@ static ssize_t queue_feature_store(struct gendisk *disk, const char *page,
 	if (ret < 0)
 		return ret;
 
-	lim = queue_limits_start_update(disk->queue);
 	if (val)
-		lim.features |= feature;
+		lim->features |= feature;
 	else
-		lim.features &= ~feature;
-	ret = queue_limits_commit_update(disk->queue, &lim);
-	if (ret)
-		return ret;
-	return count;
+		lim->features &= ~feature;
+	return 0;
 }
 
 #define QUEUE_SYSFS_FEATURE(_name, _feature)				\
@@ -238,10 +224,10 @@ static ssize_t queue_##_name##_show(struct gendisk *disk, char *page)	\
 	return sprintf(page, "%u\n",					\
 		!!(disk->queue->limits.features & _feature));		\
 }									\
-static ssize_t queue_##_name##_store(struct gendisk *disk,		\
-		const char *page, size_t count)				\
+static int queue_##_name##_store(struct gendisk *disk,			\
+		const char *page, size_t count, struct queue_limits *lim) \
 {									\
-	return queue_feature_store(disk, page, count, _feature);	\
+	return queue_feature_store(disk, page, count, lim, _feature);	\
 }
 
 QUEUE_SYSFS_FEATURE(rotational, BLK_FEAT_ROTATIONAL)
@@ -381,12 +367,10 @@ static ssize_t queue_wc_show(struct gendisk *disk, char *page)
 	return sprintf(page, "write through\n");
 }
 
-static ssize_t queue_wc_store(struct gendisk *disk, const char *page,
-			      size_t count)
+static int queue_wc_store(struct gendisk *disk, const char *page,
+		size_t count, struct queue_limits *lim)
 {
-	struct queue_limits lim;
 	bool disable;
-	int err;
 
 	if (!strncmp(page, "write back", 10)) {
 		disable = false;
@@ -397,15 +381,11 @@ static ssize_t queue_wc_store(struct gendisk *disk, const char *page,
 		return -EINVAL;
 	}
 
-	lim = queue_limits_start_update(disk->queue);
 	if (disable)
-		lim.flags |= BLK_FLAG_WRITE_CACHE_DISABLED;
+		lim->flags |= BLK_FLAG_WRITE_CACHE_DISABLED;
 	else
-		lim.flags &= ~BLK_FLAG_WRITE_CACHE_DISABLED;
-	err = queue_limits_commit_update(disk->queue, &lim);
-	if (err)
-		return err;
-	return count;
+		lim->flags &= ~BLK_FLAG_WRITE_CACHE_DISABLED;
+	return 0;
 }
 
 #define QUEUE_RO_ENTRY(_prefix, _name)			\
@@ -421,6 +401,13 @@ static struct queue_sysfs_entry _prefix##_entry = {	\
 	.store	= _prefix##_store,			\
 };
 
+#define QUEUE_LIM_RW_ENTRY(_prefix, _name)			\
+static struct queue_sysfs_entry _prefix##_entry = {	\
+	.attr		= { .name = _name, .mode = 0644 },	\
+	.show		= _prefix##_show,			\
+	.store_limit	= _prefix##_store,			\
+}
+
 #define QUEUE_RW_LOAD_MODULE_ENTRY(_prefix, _name)		\
 static struct queue_sysfs_entry _prefix##_entry = {		\
 	.attr		= { .name = _name, .mode = 0644 },	\
@@ -431,7 +418,7 @@ static struct queue_sysfs_entry _prefix##_entry = {		\
 
 QUEUE_RW_ENTRY(queue_requests, "nr_requests");
 QUEUE_RW_ENTRY(queue_ra, "read_ahead_kb");
-QUEUE_RW_ENTRY(queue_max_sectors, "max_sectors_kb");
+QUEUE_LIM_RW_ENTRY(queue_max_sectors, "max_sectors_kb");
 QUEUE_RO_ENTRY(queue_max_hw_sectors, "max_hw_sectors_kb");
 QUEUE_RO_ENTRY(queue_max_segments, "max_segments");
 QUEUE_RO_ENTRY(queue_max_integrity_segments, "max_integrity_segments");
@@ -447,7 +434,7 @@ QUEUE_RO_ENTRY(queue_io_opt, "optimal_io_size");
 QUEUE_RO_ENTRY(queue_max_discard_segments, "max_discard_segments");
 QUEUE_RO_ENTRY(queue_discard_granularity, "discard_granularity");
 QUEUE_RO_ENTRY(queue_max_hw_discard_sectors, "discard_max_hw_bytes");
-QUEUE_RW_ENTRY(queue_max_discard_sectors, "discard_max_bytes");
+QUEUE_LIM_RW_ENTRY(queue_max_discard_sectors, "discard_max_bytes");
 QUEUE_RO_ENTRY(queue_discard_zeroes_data, "discard_zeroes_data");
 
 QUEUE_RO_ENTRY(queue_atomic_write_max_sectors, "atomic_write_max_bytes");
@@ -470,7 +457,7 @@ QUEUE_RW_ENTRY(queue_nomerges, "nomerges");
 QUEUE_RW_ENTRY(queue_rq_affinity, "rq_affinity");
 QUEUE_RW_ENTRY(queue_poll, "io_poll");
 QUEUE_RW_ENTRY(queue_poll_delay, "io_poll_delay");
-QUEUE_RW_ENTRY(queue_wc, "write_cache");
+QUEUE_LIM_RW_ENTRY(queue_wc, "write_cache");
 QUEUE_RO_ENTRY(queue_fua, "fua");
 QUEUE_RO_ENTRY(queue_dax, "dax");
 QUEUE_RW_ENTRY(queue_io_timeout, "io_timeout");
@@ -483,10 +470,10 @@ static struct queue_sysfs_entry queue_hw_sector_size_entry = {
 	.show = queue_logical_block_size_show,
 };
 
-QUEUE_RW_ENTRY(queue_rotational, "rotational");
-QUEUE_RW_ENTRY(queue_iostats, "iostats");
-QUEUE_RW_ENTRY(queue_add_random, "add_random");
-QUEUE_RW_ENTRY(queue_stable_writes, "stable_writes");
+QUEUE_LIM_RW_ENTRY(queue_rotational, "rotational");
+QUEUE_LIM_RW_ENTRY(queue_iostats, "iostats");
+QUEUE_LIM_RW_ENTRY(queue_add_random, "add_random");
+QUEUE_LIM_RW_ENTRY(queue_stable_writes, "stable_writes");
 
 #ifdef CONFIG_BLK_WBT
 static ssize_t queue_var_store64(s64 *var, const char *page)
@@ -683,7 +670,7 @@ queue_attr_store(struct kobject *kobj, struct attribute *attr,
 	struct request_queue *q = disk->queue;
 	ssize_t res;
 
-	if (!entry->store)
+	if (!entry->store_limit && !entry->store)
 		return -EIO;
 
 	/*
@@ -697,11 +684,24 @@ queue_attr_store(struct kobject *kobj, struct attribute *attr,
 			return res;
 	}
 
-	blk_mq_freeze_queue(q);
 	mutex_lock(&q->sysfs_lock);
-	res = entry->store(disk, page, length);
-	mutex_unlock(&q->sysfs_lock);
+	blk_mq_freeze_queue(q);
+	if (entry->store_limit) {
+		struct queue_limits lim = queue_limits_start_update(q);
+
+		res = entry->store_limit(disk, page, length, &lim);
+		if (res < 0) {
+			queue_limits_cancel_update(q);
+		} else {
+			res = queue_limits_commit_update(q, &lim);
+			if (!res)
+				res = length;
+		}
+	} else {
+		res = entry->store(disk, page, length);
+	}
 	blk_mq_unfreeze_queue(q);
+	mutex_unlock(&q->sysfs_lock);
 	return res;
 }
 
-- 
2.47.3


^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox