]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
block: fix cached zone reporting after zone append was used
authorChristoph Hellwig <hch@lst.de>
Wed, 5 Nov 2025 19:52:15 +0000 (14:52 -0500)
committerJens Axboe <axboe@kernel.dk>
Thu, 6 Nov 2025 23:15:27 +0000 (16:15 -0700)
No zone plugs are allocated when a zone is opened by calling Zone Append
on it.  This makes the cached zone reporting report incorrectly empty
zones if the file system is unmounted and report zones is called after
that, e.g. by xfstests test cases using the scratch device.

Fix this by recording if zone append was used on a device, and disable
cached reporting for the device until a ZONE_RESET_ALL happens that
guarantees all zones are empty.

We could probably do even better using a per-zone flag, but the practical
use cache for zone reporting after the initial mount are rather limited,
so let's keep things simple for now.

Fixes: 31f0656a4ab7 ("block: introduce blkdev_report_zones_cached()")
Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Damien Le Moal <dlemoal@kernel.org>
Reviewed-by: Bart Van Assche <bvanassche@acm.org>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
block/blk-zoned.c
include/linux/blkdev.h

index a0ce17e2143f4a40c569b0ca3dc60f09d2d633a2..c5226bcaaa94007bd0441d3ad173367d674b43e2 100644 (file)
@@ -899,6 +899,19 @@ static int blkdev_report_zone_fallback(struct block_device *bdev,
        return blkdev_do_report_zones(bdev, sector, 1, &args);
 }
 
+/*
+ * 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
@@ -932,6 +945,9 @@ int blkdev_get_zone_info(struct block_device *bdev, sector_t sector,
        memset(zone, 0, sizeof(*zone));
        sector = ALIGN_DOWN(sector, zone_sectors);
 
+       if (!blkdev_has_cached_report_zones(bdev))
+               return blkdev_report_zone_fallback(bdev, sector, zone);
+
        rcu_read_lock();
        zones_cond = rcu_dereference(disk->zones_cond);
        if (!disk->zone_wplugs_hash || !zones_cond) {
@@ -1035,11 +1051,7 @@ int blkdev_report_zones_cached(struct block_device *bdev, sector_t sector,
        if (!nr_zones || sector >= capacity)
                return 0;
 
-       /*
-        * If we do not have any zone write plug resources, fallback to using
-        * the regular zone report.
-        */
-       if (!disk_need_zone_resources(disk)) {
+       if (!blkdev_has_cached_report_zones(bdev)) {
                struct blk_report_zones_args args = {
                        .cb = cb,
                        .data = data,
@@ -1115,6 +1127,7 @@ static void blk_zone_reset_all_bio_endio(struct bio *bio)
        for (sector = 0; sector < capacity;
             sector += bdev_zone_sectors(bio->bi_bdev))
                disk_zone_set_cond(disk, sector, BLK_ZONE_COND_EMPTY);
+       clear_bit(GD_ZONE_APPEND_USED, &disk->state);
 }
 
 static void blk_zone_finish_bio_endio(struct bio *bio)
@@ -1474,6 +1487,9 @@ static void blk_zone_wplug_handle_native_zone_append(struct bio *bio)
        struct blk_zone_wplug *zwplug;
        unsigned long flags;
 
+       if (!test_bit(GD_ZONE_APPEND_USED, &disk->state))
+               set_bit(GD_ZONE_APPEND_USED, &disk->state);
+
        /*
         * We have native support for zone append operations, so we are not
         * going to handle @bio through plugging. However, we may already have a
index f0ab02e0a673c82a8fb7e324ac26ad9c2d7d9a63..6a498aa7f7e767e03c5c309b56643577e654bcdc 100644 (file)
@@ -173,6 +173,7 @@ struct gendisk {
 #define GD_ADDED                       4
 #define GD_SUPPRESS_PART_SCAN          5
 #define GD_OWNS_QUEUE                  6
+#define GD_ZONE_APPEND_USED            7
 
        struct mutex open_mutex;        /* open/close mutex */
        unsigned open_partitions;       /* number of open partitions */