Re: [PATCH v6 3/8] nbd: clear queue limits on disconnect

yangerkun <[email protected]> Wed, 5 Aug 2026 14:27:02 +0800
Newsgroups org.kernel.vger.linux-block
Message-ID <[email protected]>

在 2026/8/4 16:39, Yang Erkun 写道:
> An inactive nbd device may refuse any I/O operations. The nbd_config_put
> function calls invalidate_disk, which sets the device capacity to zero
> to reject all read and write I/O. For zero-sector flush I/O requests
> from blkdev_issue_flush, if the write cache is disabled, the zero-sector
> flush I/O immediately returns 0 in submit_bio_noacct. However, since
> nbd_config_put does not clear the write cache state, an inactive nbd
> device might still have the write cache enabled. In this situation,
> zero-sector flush I/O will return -EIO because there is no active socket.
> Additionally, BLK_FEAT_FUA and BLK_FEAT_ROTATIONAL flags may also remain
> stale, resetting all of them ensures consistent behavior.
> 
> The limits update uses queue_limits_commit_update() (the non-freezing
> variant) because config_refs == 0 here means every fd is closed and recv
> threads have drained, so no in-flight I/O can read q->limits concurrently.
> 
> Signed-off-by: Yang Erkun <[email protected]>
> ---
>   drivers/block/nbd.c | 43 ++++++++++++++++++++++++++-----------------
>   1 file changed, 26 insertions(+), 17 deletions(-)
> 
> diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c
> index 7ec85f94f742..45e58191c5d7 100644
> --- a/drivers/block/nbd.c
> +++ b/drivers/block/nbd.c
> @@ -331,6 +331,26 @@ static void nbd_mark_nsock_dead(struct nbd_device *nbd, struct nbd_sock *nsock,
>   	nsock->sent = 0;
>   }
>   
> +static void nbd_apply_limits(struct queue_limits *lim, u32 flags)
> +{
> +	lim->features &= ~(BLK_FEAT_WRITE_CACHE | BLK_FEAT_FUA | BLK_FEAT_ROTATIONAL);
> +	lim->max_hw_discard_sectors = 0;
> +	lim->max_write_zeroes_sectors = 0;
> +
> +	if (flags & NBD_FLAG_SEND_TRIM)
> +		lim->max_hw_discard_sectors = UINT_MAX << SECTOR_SHIFT;

I am so sorry there is a mistake here... Should be UINT_MAX >> SECTOR_SHIFT

> +	if (flags & NBD_FLAG_SEND_FLUSH) {
> +		lim->features |= BLK_FEAT_WRITE_CACHE;
> +		if (flags & NBD_FLAG_SEND_FUA)
> +			lim->features |= BLK_FEAT_FUA;
> +	}
> +
> +	if (flags & NBD_FLAG_ROTATIONAL)
> +		lim->features |= BLK_FEAT_ROTATIONAL;
> +	if (flags & NBD_FLAG_SEND_WRITE_ZEROES)
> +		lim->max_write_zeroes_sectors = UINT_MAX >> SECTOR_SHIFT;
> +}
> +
>   static int nbd_set_size(struct nbd_device *nbd, loff_t bytesize, loff_t blksize)
>   {
>   	struct queue_limits lim;
> @@ -352,23 +372,7 @@ static int nbd_set_size(struct nbd_device *nbd, loff_t bytesize, loff_t blksize)
>   		return 0;
>   
>   	lim = queue_limits_start_update(nbd->disk->queue);
> -	if (nbd->config->flags & NBD_FLAG_SEND_TRIM)
> -		lim.max_hw_discard_sectors = UINT_MAX >> SECTOR_SHIFT;
> -	else
> -		lim.max_hw_discard_sectors = 0;
> -	if (!(nbd->config->flags & NBD_FLAG_SEND_FLUSH)) {
> -		lim.features &= ~(BLK_FEAT_WRITE_CACHE | BLK_FEAT_FUA);
> -	} else if (nbd->config->flags & NBD_FLAG_SEND_FUA) {
> -		lim.features |= BLK_FEAT_WRITE_CACHE | BLK_FEAT_FUA;
> -	} else {
> -		lim.features |= BLK_FEAT_WRITE_CACHE;
> -		lim.features &= ~BLK_FEAT_FUA;
> -	}
> -	if (nbd->config->flags & NBD_FLAG_ROTATIONAL)
> -		lim.features |= BLK_FEAT_ROTATIONAL;
> -	if (nbd->config->flags & NBD_FLAG_SEND_WRITE_ZEROES)
> -		lim.max_write_zeroes_sectors = UINT_MAX >> SECTOR_SHIFT;
> -
> +	nbd_apply_limits(&lim, nbd->config->flags);
>   	lim.logical_block_size = blksize;
>   	lim.physical_block_size = blksize;
>   	error = queue_limits_commit_update_frozen(nbd->disk->queue, &lim);
> @@ -1469,8 +1473,13 @@ static void nbd_config_put(struct nbd_device *nbd)
>   	if (refcount_dec_and_mutex_lock(&nbd->config_refs,
>   					&nbd->config_lock)) {
>   		struct nbd_config *config = nbd->config;
> +		struct queue_limits lim;
>   		nbd_dev_dbg_close(nbd);
>   		invalidate_disk(nbd->disk);
> +		/* reset queue limits to default */
> +		lim = queue_limits_start_update(nbd->disk->queue);
> +		nbd_apply_limits(&lim, 0);
> +		queue_limits_commit_update(nbd->disk->queue, &lim);
>   		if (nbd->config->bytesize)
>   			kobject_uevent(&nbd_to_dev(nbd)->kobj, KOBJ_CHANGE);
>   		if (test_and_clear_bit(NBD_RT_HAS_PID_FILE,