[PATCH v2 1/2] block: only use the cached zone report if BLK_ZONE_REP_CACHED is set

From: Shashank Mohan Jain

Date: Sun Oct 11 2026 - 12:16:47 EST


BLKREPORTZONEV2 takes the flags field of struct blk_zone_report as an
input. With BLK_ZONE_REP_CACHED the report is built from the zone
information cached by the block layer. Without it, BLKREPORTZONEV2 is
documented to behave like BLKREPORTZONE and to get the report from the
device: this is what the changelog of the commit that added the ioctl
and the comments in include/uapi/linux/blkzoned.h say.

blkdev_report_zones_ioctl() only uses the flag to validate the input
and calls blkdev_report_zones_cached() for every BLKREPORTZONEV2
request. A caller that passes flags == 0 gets the cached report, in
which implicitly open, explicitly open and closed zones all have the
condition BLK_ZONE_COND_ACTIVE, and an explicitly opened empty zone is
reported as empty.

Example with a zoned null_blk device (10 MiB, 4 MiB zones) after
BLKOPENZONE on zone 0, and a write of 8 sectors to zone 1 followed by
BLKCLOSEZONE:

BLKREPORTZONE: zone 0 cond 0x3 (EXP_OPEN)
zone 1 cond 0x4 (CLOSED)
BLKREPORTZONEV2, flags 0: zone 0 cond 0x1 (EMPTY)
zone 1 cond 0xff (ACTIVE)

The uapi header marks BLKREPORTZONE as deprecated in favour of
BLKREPORTZONEV2. A program that follows this and does not ask for
cached information can no longer tell open zones from closed ones, and
is handed BLK_ZONE_COND_ACTIVE, which is only defined for the cached
report.

Use the cached report only when BLK_ZONE_REP_CACHED is set and the
device has a cached report; otherwise, and whenever the flag is not
set, get the report from the device with blkdev_report_zones(). Guard
the cached path with blkdev_has_cached_report_zones() so the redundant
call is avoided when there is no cached report: in that case
blkdev_report_zones_cached() already falls back to a device report, so
the observable behaviour is unchanged. blkdev_has_cached_report_zones()
is defined lower in the file, so move it, with its comment, above
blkdev_report_zones_ioctl().

Tested with zoned null_blk devices in qemu: a program that compares
the reports of BLKREPORTZONE and BLKREPORTZONEV2 with flags 0 after
the sequence above finds the differences before this change and none
with it. The report with BLK_ZONE_REP_CACHED is unchanged.

Fixes: b30ffcdc0c15 ("block: introduce BLKREPORTZONESV2 ioctl")
Cc: stable@xxxxxxxxxxxxxxx
Assisted-by: LLM
Signed-off-by: Shashank Mohan Jain <jain.sm@xxxxxxxxx>
---
Prepared with Claude Code (Anthropic), model Claude Opus 4.8
(claude-opus-4-8).

block/blk-zoned.c | 37 ++++++++++++++++++++++---------------
1 file changed, 22 insertions(+), 15 deletions(-)

diff --git a/block/blk-zoned.c b/block/blk-zoned.c
index 475aa16bc..c9b66b8a7 100644
--- a/block/blk-zoned.c
+++ b/block/blk-zoned.c
@@ -361,6 +361,19 @@ static int blkdev_copy_zone_to_user(struct blk_zone *zone, unsigned int idx,
return 0;
}

+/*
+ * For devices that natively support zone append operations, we do not use zone
+ * write plugging for zone append writes, which makes the zone condition
+ * tracking invalid once zone append was used. In that case fall back to a
+ * regular report zones to get correct information.
+ */
+static inline bool blkdev_has_cached_report_zones(struct block_device *bdev)
+{
+ return disk_need_zone_resources(bdev->bd_disk) &&
+ (bdev_emulates_zone_append(bdev) ||
+ !test_bit(GD_ZONE_APPEND_USED, &bdev->bd_disk->state));
+}
+
/*
* Mask of valid input flags for BLKREPORTZONEV2 ioctl.
*/
@@ -400,8 +413,15 @@ int blkdev_report_zones_ioctl(struct block_device *bdev, unsigned int cmd,
case BLKREPORTZONEV2:
if (rep.flags & ~BLK_ZONE_REPV2_INPUT_FLAGS)
return -EINVAL;
- ret = blkdev_report_zones_cached(bdev, rep.sector, rep.nr_zones,
- blkdev_copy_zone_to_user, &args);
+ if ((rep.flags & BLK_ZONE_REP_CACHED) &&
+ blkdev_has_cached_report_zones(bdev))
+ ret = blkdev_report_zones_cached(bdev, rep.sector,
+ rep.nr_zones,
+ blkdev_copy_zone_to_user, &args);
+ else
+ ret = blkdev_report_zones(bdev, rep.sector,
+ rep.nr_zones,
+ blkdev_copy_zone_to_user, &args);
break;
default:
return -EINVAL;
@@ -905,19 +925,6 @@ static int blkdev_report_zone_fallback(struct block_device *bdev,
return 0;
}

-/*
- * For devices that natively support zone append operations, we do not use zone
- * write plugging for zone append writes, which makes the zone condition
- * tracking invalid once zone append was used. In that case fall back to a
- * regular report zones to get correct information.
- */
-static inline bool blkdev_has_cached_report_zones(struct block_device *bdev)
-{
- return disk_need_zone_resources(bdev->bd_disk) &&
- (bdev_emulates_zone_append(bdev) ||
- !test_bit(GD_ZONE_APPEND_USED, &bdev->bd_disk->state));
-}
-
/**
* blkdev_get_zone_info - Get a single zone information from cached data
* @bdev: Target block device
--
2.43.0