Re: [PATCH] zloop: zero out only the unread tail of short reads

Damien Le Moal <[email protected]> Thu, 30 Jul 2026 15:07:13 +0900
Newsgroups org.kernel.vger.linux-block
Organization Western Digital Research
Message-ID <[email protected]>
On 2026/07/30 14:58, Shin'ichiro Kawasaki wrote:
> zloop_complete_rq() handles a short read from a zone's backing file by
> zero-filling every bio in the request. This is correct when the entire
> request is beyond the written part of the zone. When a read request
> straddles the boundary between written and unwritten data, read_iter()
> returns a partial byte count and zloop then zeroes the whole request
> even for the partially read area with valid data.
> 
> Such an unexpected zero data read was observed with fio test script
> t/zbd/test-zbd-support and its test case 69, failing with "bad magic
> header 0".
> 
> Avoid the unexpected zero data read by zeroing out only the unread tail
> part. When read data size cmd->ret is smaller than the request size,
> call the new helper function zloop_fill_zero_rq() that walks through
> each bio_vec and fills zeros from the specified start offset.
> 
> Fixes: eb0570c7df23 ("block: new zoned loop block device driver")
> Signed-off-by: Shin'ichiro Kawasaki <[email protected]>
> ---
> The fio test case 69 of t/zbd/test-zbd-support started failing since the
> recent kernel commit 9cbbac29d752 ("block: Remove redundant plug in
> __submit_bio()"). My understanding is that this commit in block layer
> changed request merge behavior, and unveiled the bug in zloop.
> 
>  drivers/block/zloop.c | 30 +++++++++++++++++++++++-------
>  1 file changed, 23 insertions(+), 7 deletions(-)
> 
> diff --git a/drivers/block/zloop.c b/drivers/block/zloop.c
> index 55eeb6aac0ea..42967e7aea58 100644
> --- a/drivers/block/zloop.c
> +++ b/drivers/block/zloop.c
> @@ -798,6 +798,26 @@ static void zloop_cmd_workfn(struct work_struct *work)
>  	current->flags = orig_flags;
>  }
>  
> +static void zloop_fill_zero_rq(struct request *rq, unsigned int zero_from)
> +{
> +	struct req_iterator iter;
> +	struct bio_vec bv;
> +	unsigned int pos = 0;
> +	unsigned int end, off;
> +
> +	rq_for_each_segment(bv, rq, iter) {
> +		end = pos + bv.bv_len;
> +		if (end > zero_from) {
> +			off = 0;
> +			if (pos < zero_from)
> +				off = zero_from - pos;

Maybe write this as:

			if (pos < zero_from)
				off = zero_from - pos;
			else
				off = 0;

> +			memzero_page(bv.bv_page, bv.bv_offset + off,
> +				     bv.bv_len - off);
> +		}
> +		pos = end;
> +	}
> +}
> +
>  static void zloop_complete_rq(struct request *rq)
>  {
>  	struct zloop_cmd *cmd = blk_mq_rq_to_pdu(rq);
> @@ -812,13 +832,9 @@ static void zloop_complete_rq(struct request *rq)
>  			pr_err("Zone %u: failed read sector %llu, %llu sectors\n",
>  			       zone_no, cmd->sector, cmd->nr_sectors);
>  
> -		if (cmd->ret >= 0 && cmd->ret != blk_rq_bytes(rq)) {
> -			/* short read */
> -			struct bio *bio;
> -
> -			__rq_for_each_bio(bio, rq)
> -				zero_fill_bio(bio);
> -		}
> +		/* Short read. Zero out the unread tail beyond cmd->ret. */
> +		if (cmd->ret >= 0 && cmd->ret != blk_rq_bytes(rq))
> +			zloop_fill_zero_rq(rq, cmd->ret);
>  		break;
>  	case REQ_OP_WRITE:
>  	case REQ_OP_ZONE_APPEND:


-- 
Damien Le Moal
Western Digital Research