* [PATCH v7 00/16] Improve handling of offline and read-only zones
@ 2026-09-08 8:57 Damien Le Moal
2026-09-08 8:57 ` [PATCH v7 01/16] block: remove disk_free_zone_resources() Damien Le Moal
` (15 more replies)
0 siblings, 16 replies; 28+ messages in thread
From: Damien Le Moal @ 2026-09-08 8:57 UTC (permalink / raw)
To: Jens Axboe, linux-block; +Cc: Christoph Hellwig
Jens,
This patch series refactor some zone related code, improves the caching of
zone information and the management of zone write plugs in preparation for
fully supporting storage element depopulation.
Most of the functional changes here are around the revalidation of the
device capacity and the management of zone write plugs to better handle
read-only and offline zones as these conditions can become more common
with storage element being depopulated.
Changes from v6:
- Rebased on block/for-next
- Added patch 2, 9 and 11 to make revalidation more solid against disk
capacity changes. Due to these changes, review tags for patch 4 were
dropped.
- Added patch 7 to improve cached report zones.
Changes from v5:
- Rebased on 7.3-rc1
- Removed patch 13 as it was sent separately as a fix patch
Changes from v4:
- Renamed bdev_check_zone_mgmt() to bdev_zone_mgmt_allowed() in patch 10
- Added Fixes tag to patch 13
- Added review tags
Changes from v3:
- Rebased on latest block/for-next
- Small modification to patch 4 to address Sashiko review comments
complaining about the potential for out of bound accesses to the zone
state array.
- Added review tags
Changes from v2:
- Minor changes to patches 1, 5, 7 and 8 to address some of the Sashiko
review comments (a lot of these comments point to cases that cannot
happen in practice or that are non-fatal and would generate errors due
to a user invalid use of the device).
- Applied review tags
Changes from v1:
- Fixup error path change in patch 1
- Use WARN_ON_ONCE in patch 4
- Dropped patch 14
- Applied review tags
Damien Le Moal (16):
block: remove disk_free_zone_resources()
block: improve capacity handling during zone revalidation
block: refactor disk_revalidate_zone_resources()
block: refactor disk_update_zone_resources()
block: remember a zone type regardless of its condition
block: refactor bdev_zone_is_seq()
block: improve blkdev_get_zone_info()
block: introduce disk_for_all_zone_wplugs()
block: serialize zone revalidation
block: drop all zone write plugs on capacity changes
block: retry zone revalidation on capacity change
block: propagate readonly and offline conditions to zone write plugs
block: always treat offline and read-only zones as dead
block: fail zone management operations to read-only and offline zones
block: allow read-only and offline conventional zones
block: simplify disk_zone_set_cond()
block/blk-core.c | 7 +-
block/blk-zoned.c | 848 ++++++++++++++++++++++++++---------------
block/blk.h | 6 +
include/linux/blkdev.h | 11 +-
4 files changed, 550 insertions(+), 322 deletions(-)
base-commit: ba285edf3fd1a27f8ec739b5b396d45c98eabee0
--
2.55.0
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH v7 01/16] block: remove disk_free_zone_resources()
2026-09-08 8:57 [PATCH v7 00/16] Improve handling of offline and read-only zones Damien Le Moal
@ 2026-09-08 8:57 ` Damien Le Moal
2026-09-08 8:57 ` [PATCH v7 02/16] block: improve capacity handling during zone revalidation Damien Le Moal
` (14 subsequent siblings)
15 siblings, 0 replies; 28+ messages in thread
From: Damien Le Moal @ 2026-09-08 8:57 UTC (permalink / raw)
To: Jens Axboe, linux-block; +Cc: Christoph Hellwig
In the rare event when revalidating the zones of a zoned block device
fails, the device capacity will be dropped to 0. In such case, the zoned
block device will either be rescanned and restored or will be dropped
entirely and its gendisk will be destroyed. So calling
disk_free_zone_resources() from blk_revalidate_disk_zones() in case of
an error does not make much sense. We can keep the zone resources in case
the device is rescanned and restored and simply free all resources in
disk_release_zone_resources() when the gendisk is destroyed.
Remove the call to disk_free_zone_resources() from
blk_revalidate_disk_zones() and squash disk_free_zone_resources() inside
disk_release_zone_resources(). With this change, the conditional creation
of the zone write plugs work queue is not necessary anymore as the
workqueue will keep existing together with all other resources until the
disk is released. This simplifies disk_alloc_zone_resources().
Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
Reviewed-by: Hannes Reinecke <hare@suse.de>
Reviewed-by: Bart Van Assche <bvanassche@acm.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
---
block/blk-zoned.c | 58 +++++++++++++++++------------------------------
1 file changed, 21 insertions(+), 37 deletions(-)
diff --git a/block/blk-zoned.c b/block/blk-zoned.c
index a5afb842bf35..c7d8c767bb31 100644
--- a/block/blk-zoned.c
+++ b/block/blk-zoned.c
@@ -1893,21 +1893,6 @@ static int disk_alloc_zone_resources(struct gendisk *disk,
if (!disk->zone_wplugs_pool)
goto free_hash;
- /*
- * We may already have a zone write plug workqueue as this function may
- * be called after disk_free_zone_resources(), which does not destroy
- * the workqueue (the zone write plugs workqueue is destroyed at
- * disk_release() time).
- */
- if (!disk->zone_wplugs_wq) {
- disk->zone_wplugs_wq =
- alloc_workqueue("%s_zwplugs",
- WQ_MEM_RECLAIM | WQ_HIGHPRI | WQ_PERCPU,
- pool_size, disk->disk_name);
- if (!disk->zone_wplugs_wq)
- goto destroy_pool;
- }
-
disk->zone_wplugs_worker =
kthread_create(disk_zone_wplugs_worker, disk,
"%s_zwplugs_worker", disk->disk_name);
@@ -1918,8 +1903,18 @@ static int disk_alloc_zone_resources(struct gendisk *disk,
}
wake_up_process(disk->zone_wplugs_worker);
+ disk->zone_wplugs_wq =
+ alloc_workqueue("%s_zwplugs",
+ WQ_MEM_RECLAIM | WQ_HIGHPRI | WQ_PERCPU,
+ pool_size, disk->disk_name);
+ if (!disk->zone_wplugs_wq)
+ goto stop_worker;
+
return 0;
+stop_worker:
+ kthread_stop(disk->zone_wplugs_worker);
+ disk->zone_wplugs_worker = NULL;
destroy_pool:
mempool_destroy(disk->zone_wplugs_pool);
disk->zone_wplugs_pool = NULL;
@@ -1975,7 +1970,7 @@ static void disk_set_zones_cond_array(struct gendisk *disk, u8 *zones_cond)
kfree_rcu_mightsleep(zones_cond);
}
-static void disk_free_zone_resources(struct gendisk *disk)
+void disk_release_zone_resources(struct gendisk *disk)
{
if (disk->zone_wplugs_worker) {
kthread_stop(disk->zone_wplugs_worker);
@@ -1983,8 +1978,10 @@ static void disk_free_zone_resources(struct gendisk *disk)
}
WARN_ON_ONCE(!list_empty(&disk->zone_wplugs_list));
- if (disk->zone_wplugs_wq)
- drain_workqueue(disk->zone_wplugs_wq);
+ if (disk->zone_wplugs_wq) {
+ destroy_workqueue(disk->zone_wplugs_wq);
+ disk->zone_wplugs_wq = NULL;
+ }
disk_destroy_zone_wplugs_hash_table(disk);
@@ -1994,16 +1991,6 @@ static void disk_free_zone_resources(struct gendisk *disk)
disk->nr_zones = 0;
}
-void disk_release_zone_resources(struct gendisk *disk)
-{
- if (disk->zone_wplugs_wq) {
- destroy_workqueue(disk->zone_wplugs_wq);
- disk->zone_wplugs_wq = NULL;
- }
-
- disk_free_zone_resources(disk);
-}
-
struct blk_revalidate_zone_args {
struct gendisk *disk;
u8 *zones_cond;
@@ -2317,11 +2304,11 @@ int blk_revalidate_disk_zones(struct gendisk *disk)
sector_t zone_sectors = q->limits.chunk_sectors;
sector_t capacity = get_capacity(disk);
struct blk_revalidate_zone_args args = { };
- unsigned int memflags, noio_flag;
struct blk_report_zones_args rep_args = {
.cb = blk_revalidate_zone_cb,
.data = &args,
};
+ unsigned int noio_flag;
int ret = -ENOMEM;
if (WARN_ON_ONCE(!blk_queue_is_zoned(q)))
@@ -2358,8 +2345,8 @@ int blk_revalidate_disk_zones(struct gendisk *disk)
}
memalloc_noio_restore(noio_flag);
- if (ret <= 0)
- goto free_resources;
+ if (ret < 0)
+ goto free_args;
/*
* If zones where reported, make sure that the entire disk capacity
@@ -2369,22 +2356,19 @@ int blk_revalidate_disk_zones(struct gendisk *disk)
pr_warn("%s: Missing zones from sector %llu\n",
disk->disk_name, args.sector);
ret = -ENODEV;
- goto free_resources;
+ goto free_args;
}
ret = disk_update_zone_resources(disk, &args);
if (ret)
- goto free_resources;
+ goto free_args;
return 0;
-free_resources:
+free_args:
pr_warn("%s: failed to revalidate zones\n", disk->disk_name);
kfree(args.zones_cond);
- memflags = blk_mq_freeze_queue(q);
- disk_free_zone_resources(disk);
- blk_mq_unfreeze_queue(q, memflags);
return ret;
}
--
2.55.0
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH v7 02/16] block: improve capacity handling during zone revalidation
2026-09-08 8:57 [PATCH v7 00/16] Improve handling of offline and read-only zones Damien Le Moal
2026-09-08 8:57 ` [PATCH v7 01/16] block: remove disk_free_zone_resources() Damien Le Moal
@ 2026-09-08 8:57 ` Damien Le Moal
2026-09-09 8:51 ` Hannes Reinecke
2026-09-10 5:20 ` Christoph Hellwig
2026-09-08 8:57 ` [PATCH v7 03/16] block: refactor disk_revalidate_zone_resources() Damien Le Moal
` (13 subsequent siblings)
15 siblings, 2 replies; 28+ messages in thread
From: Damien Le Moal @ 2026-09-08 8:57 UTC (permalink / raw)
To: Jens Axboe, linux-block; +Cc: Christoph Hellwig
While executing blk_revalidate_disk_zones(), a disk capacity is looked at
using get_capacity() several times: on entry to
blk_revalidate_disk_zones(), when allocating zone revalidation arguments
in disk_revalidate_zone_resources(), while validating zones in
blk_revalidate_zone_cb() and one last time at the end of
blk_revalidate_disk_zones() to check that all zones have been inspected.
Since this is all done while passthrough commands can be issued, it is
possible that a capacity change operation (e.g. the removal of a storage
element on a SCSI or SATA disk) is concurrently executed, potentially
resulting in an inconsistent or conflicting revalidation with potentially
out-of-bound accesses to the zone condition array.
Prevent issues by using get_capacity() once on entry to
blk_revalidate_disk_zones(), remembering this capacity as a field of
struct blk_revalidate_zone_args and using that field while revalidating
zones. A final second call to get_capacity() is done at the end of
blk_revalidate_disk_zones() to ensure that the disk capacity has not
changed, thus revalidating the capacity (and number of zones) itself.
Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
---
block/blk-zoned.c | 19 +++++++++++++------
1 file changed, 13 insertions(+), 6 deletions(-)
diff --git a/block/blk-zoned.c b/block/blk-zoned.c
index c7d8c767bb31..8b3b5c7bfd03 100644
--- a/block/blk-zoned.c
+++ b/block/blk-zoned.c
@@ -1993,6 +1993,7 @@ void disk_release_zone_resources(struct gendisk *disk)
struct blk_revalidate_zone_args {
struct gendisk *disk;
+ sector_t capacity;
u8 *zones_cond;
unsigned int nr_zones;
unsigned int nr_conv_zones;
@@ -2010,7 +2011,7 @@ static int disk_revalidate_zone_resources(struct gendisk *disk,
args->disk = disk;
args->nr_zones =
- DIV_ROUND_UP_ULL(get_capacity(disk), lim->chunk_sectors);
+ DIV_ROUND_UP_ULL(args->capacity, lim->chunk_sectors);
/* Cached zone conditions: 1 byte per zone */
args->zones_cond = kzalloc(args->nr_zones, GFP_NOIO);
@@ -2231,7 +2232,7 @@ static int blk_revalidate_zone_cb(struct blk_zone *zone, unsigned int idx,
return -ENODEV;
}
- if (zone->start >= get_capacity(disk) || !zone->len) {
+ if (zone->start >= args->capacity || !zone->len) {
pr_warn("%s: Invalid zone start %llu, length %llu\n",
disk->disk_name, zone->start, zone->len);
return -ENODEV;
@@ -2302,8 +2303,9 @@ int blk_revalidate_disk_zones(struct gendisk *disk)
{
struct request_queue *q = disk->queue;
sector_t zone_sectors = q->limits.chunk_sectors;
- sector_t capacity = get_capacity(disk);
- struct blk_revalidate_zone_args args = { };
+ struct blk_revalidate_zone_args args = {
+ .capacity = get_capacity(disk),
+ };
struct blk_report_zones_args rep_args = {
.cb = blk_revalidate_zone_cb,
.data = &args,
@@ -2314,7 +2316,7 @@ int blk_revalidate_disk_zones(struct gendisk *disk)
if (WARN_ON_ONCE(!blk_queue_is_zoned(q)))
return -EIO;
- if (!capacity)
+ if (!args.capacity)
return -ENODEV;
/*
@@ -2352,7 +2354,12 @@ int blk_revalidate_disk_zones(struct gendisk *disk)
* If zones where reported, make sure that the entire disk capacity
* has been checked.
*/
- if (args.sector != capacity) {
+ if (args.capacity != get_capacity(disk)) {
+ pr_warn("%s: Capacity has changed\n", disk->disk_name);
+ ret = -ENODEV;
+ goto free_args;
+ }
+ if (args.sector != args.capacity) {
pr_warn("%s: Missing zones from sector %llu\n",
disk->disk_name, args.sector);
ret = -ENODEV;
--
2.55.0
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH v7 03/16] block: refactor disk_revalidate_zone_resources()
2026-09-08 8:57 [PATCH v7 00/16] Improve handling of offline and read-only zones Damien Le Moal
2026-09-08 8:57 ` [PATCH v7 01/16] block: remove disk_free_zone_resources() Damien Le Moal
2026-09-08 8:57 ` [PATCH v7 02/16] block: improve capacity handling during zone revalidation Damien Le Moal
@ 2026-09-08 8:57 ` Damien Le Moal
2026-09-08 8:57 ` [PATCH v7 04/16] block: refactor disk_update_zone_resources() Damien Le Moal
` (12 subsequent siblings)
15 siblings, 0 replies; 28+ messages in thread
From: Damien Le Moal @ 2026-09-08 8:57 UTC (permalink / raw)
To: Jens Axboe, linux-block; +Cc: Christoph Hellwig
The function disk_revalidate_zone_resources() is misnamed as it does not
revalidate anything but rather allocates the revalidation arguments and
then calls disk_alloc_zone_resources() to initialize the disk zone
resources if they are needed and not already allocated.
Make this function less confusing by renaming it
disk_init_revalidate_args() and moving the call to
disk_alloc_zone_resources() into blk_revalidate_disk_zones(). As before,
this function is only called if the zone resources are needed and not yet
allocated. The initialization of the atomic nr_zone_wplugs is also moved
to the function disk_init_zone_resources() so that this is done once and
not on all revalidation.
Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
Reviewed-by: Hannes Reinecke <hare@suse.de>
Reviewed-by: Bart Van Assche <bvanassche@acm.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
---
block/blk-zoned.c | 77 ++++++++++++++++++++++++++---------------------
1 file changed, 43 insertions(+), 34 deletions(-)
diff --git a/block/blk-zoned.c b/block/blk-zoned.c
index 8b3b5c7bfd03..993ddaf7acf1 100644
--- a/block/blk-zoned.c
+++ b/block/blk-zoned.c
@@ -1854,12 +1854,23 @@ static int disk_zone_wplugs_worker(void *data)
void disk_init_zone_resources(struct gendisk *disk)
{
+ atomic_set(&disk->nr_zone_wplugs, 0);
spin_lock_init(&disk->zone_wplugs_hash_lock);
spin_lock_init(&disk->zone_wplugs_list_lock);
INIT_LIST_HEAD(&disk->zone_wplugs_list);
init_completion(&disk->zone_wplugs_worker_bio_done);
}
+static unsigned int disk_get_nr_zones(struct gendisk *disk, sector_t capacity)
+{
+ struct queue_limits *lim = &disk->queue->limits;
+
+ if (!capacity || !lim->chunk_sectors)
+ return 0;
+
+ return DIV_ROUND_UP_ULL(capacity, lim->chunk_sectors);
+}
+
/*
* For the size of a disk zone write plug hash table, use the size of the
* zone write plug mempool, which is the maximum of the disk open zones and
@@ -1869,13 +1880,24 @@ void disk_init_zone_resources(struct gendisk *disk)
#define BLK_ZONE_WPLUG_MAX_HASH_BITS 9
#define BLK_ZONE_WPLUG_DEFAULT_POOL_SIZE 128
-static int disk_alloc_zone_resources(struct gendisk *disk,
- unsigned int pool_size)
+static int disk_alloc_zone_resources(struct gendisk *disk, sector_t capacity)
{
- unsigned int i;
+ struct queue_limits *lim = &disk->queue->limits;
+ unsigned int nr_zones, pool_size, i;
int ret = -ENOMEM;
- atomic_set(&disk->nr_zone_wplugs, 0);
+ nr_zones = disk_get_nr_zones(disk, capacity);
+ if (!nr_zones)
+ return -ENODEV;
+
+ /*
+ * If the device has no limit on the maximum number of open and active
+ * zones, use BLK_ZONE_WPLUG_DEFAULT_POOL_SIZE.
+ */
+ pool_size = max(lim->max_open_zones, lim->max_active_zones);
+ if (!pool_size)
+ pool_size = min(BLK_ZONE_WPLUG_DEFAULT_POOL_SIZE, nr_zones);
+
disk->zone_wplugs_hash_bits =
min(ilog2(pool_size) + 1, BLK_ZONE_WPLUG_MAX_HASH_BITS);
@@ -2002,41 +2024,18 @@ struct blk_revalidate_zone_args {
sector_t sector;
};
-static int disk_revalidate_zone_resources(struct gendisk *disk,
- struct blk_revalidate_zone_args *args)
+static int disk_init_revalidate_args(struct gendisk *disk,
+ struct blk_revalidate_zone_args *args)
{
- struct queue_limits *lim = &disk->queue->limits;
- unsigned int pool_size;
- int ret = 0;
-
args->disk = disk;
- args->nr_zones =
- DIV_ROUND_UP_ULL(args->capacity, lim->chunk_sectors);
+ args->nr_zones = disk_get_nr_zones(disk, args->capacity);
/* Cached zone conditions: 1 byte per zone */
args->zones_cond = kzalloc(args->nr_zones, GFP_NOIO);
if (!args->zones_cond)
return -ENOMEM;
- if (!disk_need_zone_resources(disk))
- return 0;
-
- /*
- * If the device has no limit on the maximum number of open and active
- * zones, use BLK_ZONE_WPLUG_DEFAULT_POOL_SIZE.
- */
- pool_size = max(lim->max_open_zones, lim->max_active_zones);
- if (!pool_size)
- pool_size =
- min(BLK_ZONE_WPLUG_DEFAULT_POOL_SIZE, args->nr_zones);
-
- if (!disk->zone_wplugs_hash) {
- ret = disk_alloc_zone_resources(disk, pool_size);
- if (ret)
- kfree(args->zones_cond);
- }
-
- return ret;
+ return 0;
}
/*
@@ -2330,11 +2329,21 @@ int blk_revalidate_disk_zones(struct gendisk *disk)
}
/*
- * Ensure that all memory allocations in this context are done as if
- * GFP_NOIO was specified.
+ * Allocate zone resources if they are needed and we have not done
+ * so yet, and initialize the revalidation arguments passed to report
+ * zones. Ensure that all memory allocations in this context are done as
+ * if GFP_NOIO was specified.
*/
noio_flag = memalloc_noio_save();
- ret = disk_revalidate_zone_resources(disk, &args);
+ if (disk_need_zone_resources(disk) && !disk->zone_wplugs_hash) {
+ ret = disk_alloc_zone_resources(disk, args.capacity);
+ if (ret) {
+ memalloc_noio_restore(noio_flag);
+ return ret;
+ }
+ }
+
+ ret = disk_init_revalidate_args(disk, &args);
if (ret) {
memalloc_noio_restore(noio_flag);
return ret;
--
2.55.0
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH v7 04/16] block: refactor disk_update_zone_resources()
2026-09-08 8:57 [PATCH v7 00/16] Improve handling of offline and read-only zones Damien Le Moal
` (2 preceding siblings ...)
2026-09-08 8:57 ` [PATCH v7 03/16] block: refactor disk_revalidate_zone_resources() Damien Le Moal
@ 2026-09-08 8:57 ` Damien Le Moal
2026-09-09 9:09 ` Hannes Reinecke
2026-09-10 5:20 ` Christoph Hellwig
2026-09-08 8:57 ` [PATCH v7 05/16] block: remember a zone type regardless of its condition Damien Le Moal
` (11 subsequent siblings)
15 siblings, 2 replies; 28+ messages in thread
From: Damien Le Moal @ 2026-09-08 8:57 UTC (permalink / raw)
To: Jens Axboe, linux-block; +Cc: Christoph Hellwig
Since the function disk_update_zone_resources() does a lot of checks
beside updating a zoned disk limits and resources, rename this function
to disk_revalidate_zone_resources().
To keep all the checks together, move the capacity checks (including the
check on the end sector of the last zone of the disk) in
blk_revalidate_disk_zones() to this function.
Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
---
block/blk-zoned.c | 64 ++++++++++++++++++++++++++---------------------
1 file changed, 35 insertions(+), 29 deletions(-)
diff --git a/block/blk-zoned.c b/block/blk-zoned.c
index 993ddaf7acf1..f07f5ab31b06 100644
--- a/block/blk-zoned.c
+++ b/block/blk-zoned.c
@@ -2039,31 +2039,51 @@ static int disk_init_revalidate_args(struct gendisk *disk,
}
/*
- * Update the disk zone resources information and device queue limits.
- * The disk queue is frozen when this is executed.
+ * Revalidate and update the disk zone resources information and device queue
+ * limits.
*/
-static int disk_update_zone_resources(struct gendisk *disk,
- struct blk_revalidate_zone_args *args)
+static int disk_revalidate_zone_resources(struct gendisk *disk,
+ struct blk_revalidate_zone_args *args)
{
struct request_queue *q = disk->queue;
unsigned int nr_seq_zones;
unsigned int pool_size, memflags;
struct queue_limits lim;
+ sector_t capacity;
int ret = 0;
lim = queue_limits_start_update(q);
memflags = blk_mq_freeze_queue(q);
- disk->nr_zones = args->nr_zones;
- if (args->nr_conv_zones >= disk->nr_zones) {
- queue_limits_cancel_update(q);
+ /*
+ * Using the re-evaluated disk capacity, make sure that the entire disk
+ * has been checked.
+ */
+ capacity = get_capacity(disk);
+ if (args->capacity != capacity) {
+ pr_warn("%s: Capacity has changed (%llu -> %llu)\n",
+ disk->disk_name, args->capacity, capacity);
+ ret = -ENODEV;
+ goto unfreeze;
+ }
+
+ /* Make sure that all zones have been checked. */
+ if (args->sector != capacity) {
+ pr_warn("%s: last zone and capacity mismatch (%llu != %llu)\n",
+ disk->disk_name, args->sector, capacity);
+ ret = -ENODEV;
+ goto unfreeze;
+ }
+
+ if (args->nr_conv_zones >= args->nr_zones) {
pr_warn("%s: Invalid number of conventional zones %u / %u\n",
- disk->disk_name, args->nr_conv_zones, disk->nr_zones);
+ disk->disk_name, args->nr_conv_zones, args->nr_zones);
ret = -ENODEV;
goto unfreeze;
}
+ disk->nr_zones = args->nr_zones;
disk->zone_capacity = args->zone_capacity;
disk->last_zone_capacity = args->last_zone_capacity;
disk_set_zones_cond_array(disk, args->zones_cond);
@@ -2082,7 +2102,7 @@ static int disk_update_zone_resources(struct gendisk *disk,
lim.max_active_zones = 0;
if (!disk->zone_wplugs_pool)
- goto commit;
+ goto unfreeze;
/*
* If the device has no limit on the maximum number of open and active
@@ -2104,10 +2124,12 @@ static int disk_update_zone_resources(struct gendisk *disk,
lim.max_open_zones = 0;
}
-commit:
- ret = queue_limits_commit_update(q, &lim);
-
unfreeze:
+ if (ret)
+ queue_limits_cancel_update(q);
+ else
+ ret = queue_limits_commit_update(q, &lim);
+
blk_mq_unfreeze_queue(q, memflags);
return ret;
@@ -2359,23 +2381,7 @@ int blk_revalidate_disk_zones(struct gendisk *disk)
if (ret < 0)
goto free_args;
- /*
- * If zones where reported, make sure that the entire disk capacity
- * has been checked.
- */
- if (args.capacity != get_capacity(disk)) {
- pr_warn("%s: Capacity has changed\n", disk->disk_name);
- ret = -ENODEV;
- goto free_args;
- }
- if (args.sector != args.capacity) {
- pr_warn("%s: Missing zones from sector %llu\n",
- disk->disk_name, args.sector);
- ret = -ENODEV;
- goto free_args;
- }
-
- ret = disk_update_zone_resources(disk, &args);
+ ret = disk_revalidate_zone_resources(disk, &args);
if (ret)
goto free_args;
--
2.55.0
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH v7 05/16] block: remember a zone type regardless of its condition
2026-09-08 8:57 [PATCH v7 00/16] Improve handling of offline and read-only zones Damien Le Moal
` (3 preceding siblings ...)
2026-09-08 8:57 ` [PATCH v7 04/16] block: refactor disk_update_zone_resources() Damien Le Moal
@ 2026-09-08 8:57 ` Damien Le Moal
2026-09-08 8:57 ` [PATCH v7 06/16] block: refactor bdev_zone_is_seq() Damien Le Moal
` (10 subsequent siblings)
15 siblings, 0 replies; 28+ messages in thread
From: Damien Le Moal @ 2026-09-08 8:57 UTC (permalink / raw)
To: Jens Axboe, linux-block; +Cc: Christoph Hellwig
Currently, bdev_zone_is_seq() identifies a sequential zone by looking at
the zone condition, assuming that any zone that does not have the
condition BLK_ZONE_COND_NOT_WP is a sequential write required zone. That
is correct only as long as the target zoned device does not support
storage element depopulation (aka HDD head depopulation), which is a
feature that can transition conventional zones to the read-only or offline
condition. For such device, we cannot distinguish anymore between
conventional and sequential zones using the zone condition as both zone
types can have the same conditions.
Prepare for fully supporting storage element depopulation and restoration
by caching the type of a zone in addition to its condition. This is
implemented by reformating the zones_cond array using a more compact zone
condition representation with the new enum blk_zstate. This zone condition
representation only uses the lower 4 bits of a byte for the condition
values, thus leaving the high order 4 bits of each byte entry of the
array for flags. The flag BLK_ZFLAG_CONV is defined to indicate
conventional zones.
The helper functions blk_zstate_to_zone_cond() and
blk_zone_cond_to_zstate() are implemented using lookup tables to convert
between enum blk_zone_condition values and enum blk_zstate zone condition
values. The helper blk_zstate_set() can be used to set a zones_state
entry with a zone condition and zone type flags, safely in accordance to
the size of the zone state array being accessed. This helper is used in
disk_zone_set_cond() to update a zone condition.
bdev_zone_is_seq() is modified to use the flags of the zones_state array
entries to identify sequential zones. The zones_state array initialization
and revalidation is unchanged from the former zones_cond array.
Overall, any zone condition that is being considered outside of the
zones_state array always uses the enum blk_zone_condition values as
before, thus minimizing the number of changes.
Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
Reviewed-by: Bart Van Assche <bvanassche@acm.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
---
block/blk-zoned.c | 205 ++++++++++++++++++++++++++++-------------
include/linux/blkdev.h | 2 +-
2 files changed, 144 insertions(+), 63 deletions(-)
diff --git a/block/blk-zoned.c b/block/blk-zoned.c
index f07f5ab31b06..1284cc8cc367 100644
--- a/block/blk-zoned.c
+++ b/block/blk-zoned.c
@@ -39,6 +39,103 @@ static const char *const zone_cond_name[] = {
};
#undef ZONE_COND_NAME
+/*
+ * Internal and compact representation of enum blk_zone_cond values for zone
+ * conditions. All these values fit into 4-bits, allowing using the high order
+ * bits as the zone type.
+ */
+enum blk_zstate {
+ BLK_ZSTATE_NOT_WP = 0x00,
+ BLK_ZSTATE_EMPTY = 0x01,
+ BLK_ZSTATE_IMP_OPEN = 0x02,
+ BLK_ZSTATE_EXP_OPEN = 0x03,
+ BLK_ZSTATE_CLOSED = 0x04,
+ BLK_ZSTATE_READONLY = 0x05,
+ BLK_ZSTATE_FULL = 0x06,
+ BLK_ZSTATE_OFFLINE = 0x07,
+ BLK_ZSTATE_ACTIVE = 0x08,
+
+ BLK_ZSTATE_COND_MASK = 0x0F,
+
+ /* Conventional zone. */
+ BLK_ZFLAG_CONV = 0x80,
+ BLK_ZSTATE_FLAGS_MASK = ~BLK_ZSTATE_COND_MASK,
+};
+
+/*
+ * Lookup table and helper to convert enum blk_zstate conditions into enum
+ * blk_zone_condition values.
+ */
+static const u8 blk_zstate2zcond[] = {
+ [BLK_ZSTATE_NOT_WP] = BLK_ZONE_COND_NOT_WP,
+ [BLK_ZSTATE_EMPTY] = BLK_ZONE_COND_EMPTY,
+ [BLK_ZSTATE_IMP_OPEN] = BLK_ZONE_COND_IMP_OPEN,
+ [BLK_ZSTATE_EXP_OPEN] = BLK_ZONE_COND_EXP_OPEN,
+ [BLK_ZSTATE_CLOSED] = BLK_ZONE_COND_CLOSED,
+ [BLK_ZSTATE_READONLY] = BLK_ZONE_COND_READONLY,
+ [BLK_ZSTATE_FULL] = BLK_ZONE_COND_FULL,
+ [BLK_ZSTATE_OFFLINE] = BLK_ZONE_COND_OFFLINE,
+ [BLK_ZSTATE_ACTIVE] = BLK_ZONE_COND_ACTIVE,
+};
+
+static inline enum blk_zone_cond blk_zstate_to_zone_cond(enum blk_zstate zs)
+{
+ u8 idx = zs & BLK_ZSTATE_COND_MASK;
+
+ if (WARN_ON_ONCE(idx >= ARRAY_SIZE(blk_zstate2zcond)))
+ return 0;
+
+ return blk_zstate2zcond[idx];
+}
+
+/*
+ * Lookup table and helper to convert an enum blk_zone_condition into an enum
+ * blk_zstate condition value. To keep the lookup table small, the
+ * BLK_ZONE_COND_ACTIVE condition is not added and handled separately.
+ */
+static const u8 blk_zcond2zstate[] = {
+ [BLK_ZONE_COND_NOT_WP] = BLK_ZSTATE_NOT_WP,
+ [BLK_ZONE_COND_EMPTY] = BLK_ZSTATE_EMPTY,
+ [BLK_ZONE_COND_IMP_OPEN] = BLK_ZSTATE_ACTIVE,
+ [BLK_ZONE_COND_EXP_OPEN] = BLK_ZSTATE_ACTIVE,
+ [BLK_ZONE_COND_CLOSED] = BLK_ZSTATE_ACTIVE,
+ [BLK_ZONE_COND_READONLY] = BLK_ZSTATE_READONLY,
+ [BLK_ZONE_COND_FULL] = BLK_ZSTATE_FULL,
+ [BLK_ZONE_COND_OFFLINE] = BLK_ZSTATE_OFFLINE,
+};
+
+static inline enum blk_zstate blk_zone_cond_to_zstate(enum blk_zone_cond cond)
+{
+ if (cond == BLK_ZONE_COND_ACTIVE)
+ return BLK_ZSTATE_ACTIVE;
+
+ if (WARN_ON_ONCE(cond >= ARRAY_SIZE(blk_zcond2zstate)))
+ return 0;
+
+ return blk_zcond2zstate[cond];
+}
+
+/*
+ * Combine an enum blk_zone_condition and zone flags into a zones_state array
+ * entry.
+ */
+static inline void blk_zstate_set(u8 *zones_state, unsigned int nr_zones,
+ unsigned int zno, enum blk_zone_cond cond, u8 flags)
+{
+ if (zones_state && zno < nr_zones)
+ zones_state[zno] = flags | blk_zone_cond_to_zstate(cond);
+}
+
+static inline u8 blk_zstate_flags(enum blk_zstate zs)
+{
+ return zs & BLK_ZSTATE_FLAGS_MASK;
+}
+
+static inline bool blk_zstate_is_conv(enum blk_zstate zs)
+{
+ return blk_zstate_flags(zs) & BLK_ZFLAG_CONV;
+}
+
/*
* Per-zone write plug.
* @node: hlist_node structure for managing the plug using a hash table.
@@ -135,51 +232,28 @@ const char *blk_zone_cond_str(enum blk_zone_cond zone_cond)
}
EXPORT_SYMBOL_GPL(blk_zone_cond_str);
-static void blk_zone_set_cond(u8 *zones_cond, unsigned int zno,
- enum blk_zone_cond cond)
-{
- if (!zones_cond)
- return;
-
- switch (cond) {
- case BLK_ZONE_COND_IMP_OPEN:
- case BLK_ZONE_COND_EXP_OPEN:
- case BLK_ZONE_COND_CLOSED:
- zones_cond[zno] = BLK_ZONE_COND_ACTIVE;
- return;
- case BLK_ZONE_COND_NOT_WP:
- case BLK_ZONE_COND_EMPTY:
- case BLK_ZONE_COND_FULL:
- case BLK_ZONE_COND_OFFLINE:
- case BLK_ZONE_COND_READONLY:
- default:
- zones_cond[zno] = cond;
- return;
- }
-}
-
static void disk_zone_set_cond(struct gendisk *disk, sector_t sector,
enum blk_zone_cond cond)
{
- u8 *zones_cond;
+ unsigned int zno = disk_zone_no(disk, sector);
+ u8 *zones_state;
rcu_read_lock();
- zones_cond = rcu_dereference(disk->zones_cond);
- if (zones_cond) {
- unsigned int zno = disk_zone_no(disk, sector);
-
+ zones_state = rcu_dereference(disk->zones_state);
+ if (zones_state && zno < disk->nr_zones) {
/*
* The condition of a conventional, readonly and offline zones
* never changes, so do nothing if the target zone is in one of
* these conditions.
*/
- switch (zones_cond[zno]) {
- case BLK_ZONE_COND_NOT_WP:
- case BLK_ZONE_COND_READONLY:
- case BLK_ZONE_COND_OFFLINE:
+ switch (zones_state[zno] & BLK_ZSTATE_COND_MASK) {
+ case BLK_ZSTATE_NOT_WP:
+ case BLK_ZSTATE_READONLY:
+ case BLK_ZSTATE_OFFLINE:
break;
default:
- blk_zone_set_cond(zones_cond, zno, cond);
+ blk_zstate_set(zones_state, disk->nr_zones, zno, cond,
+ blk_zstate_flags(zones_state[zno]));
break;
}
}
@@ -198,15 +272,15 @@ bool bdev_zone_is_seq(struct block_device *bdev, sector_t sector)
struct gendisk *disk = bdev->bd_disk;
unsigned int zno = disk_zone_no(disk, sector);
bool is_seq = false;
- u8 *zones_cond;
+ u8 *zones_state;
if (!bdev_is_zoned(bdev))
return false;
rcu_read_lock();
- zones_cond = rcu_dereference(disk->zones_cond);
- if (zones_cond && zno < disk->nr_zones)
- is_seq = zones_cond[zno] != BLK_ZONE_COND_NOT_WP;
+ zones_state = rcu_dereference(disk->zones_state);
+ if (zones_state && zno < disk->nr_zones)
+ is_seq = !blk_zstate_is_conv(zones_state[zno]);
rcu_read_unlock();
return is_seq;
@@ -505,7 +579,7 @@ static bool disk_insert_zone_wplug(struct gendisk *disk,
{
struct blk_zone_wplug *zwplg;
unsigned long flags;
- u8 *zones_cond;
+ u8 *zones_state;
unsigned int idx =
hash_32(zwplug->zone_no, disk->zone_wplugs_hash_bits);
@@ -524,15 +598,16 @@ static bool disk_insert_zone_wplug(struct gendisk *disk,
}
/*
- * Set the zone condition: if we do not yet have a zones_cond array
+ * Set the zone condition: if we do not yet have a zones_state array
* attached to the disk, then this is a zone write plug insert from the
* first call to blk_revalidate_disk_zones(), in which case the zone is
* necessarilly in the active condition.
*/
- zones_cond = rcu_dereference_check(disk->zones_cond,
+ zones_state = rcu_dereference_check(disk->zones_state,
lockdep_is_held(&disk->zone_wplugs_hash_lock));
- if (zones_cond)
- zwplug->cond = zones_cond[zwplug->zone_no];
+ if (zones_state)
+ zwplug->cond =
+ blk_zstate_to_zone_cond(zones_state[zwplug->zone_no]);
else
zwplug->cond = BLK_ZONE_COND_ACTIVE;
@@ -592,9 +667,9 @@ static void disk_free_zone_wplug(struct blk_zone_wplug *zwplug)
WARN_ON_ONCE(!bio_list_empty(&zwplug->bio_list));
spin_lock_irqsave(&disk->zone_wplugs_hash_lock, flags);
- blk_zone_set_cond(rcu_dereference_check(disk->zones_cond,
+ blk_zstate_set(rcu_dereference_check(disk->zones_state,
lockdep_is_held(&disk->zone_wplugs_hash_lock)),
- zwplug->zone_no, zwplug->cond);
+ disk->nr_zones, zwplug->zone_no, zwplug->cond, 0);
hlist_del_init_rcu(&zwplug->node);
atomic_dec(&disk->nr_zone_wplugs);
spin_unlock_irqrestore(&disk->zone_wplugs_hash_lock, flags);
@@ -940,7 +1015,7 @@ int blkdev_get_zone_info(struct block_device *bdev, sector_t sector,
sector_t zone_sectors = bdev_zone_sectors(bdev);
struct blk_zone_wplug *zwplug;
unsigned long flags;
- u8 *zones_cond;
+ u8 *zones_state, zs;
if (!bdev_is_zoned(bdev))
return -EOPNOTSUPP;
@@ -955,12 +1030,18 @@ int blkdev_get_zone_info(struct block_device *bdev, sector_t sector,
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) {
+ zones_state = rcu_dereference(disk->zones_state);
+ if (!disk->zone_wplugs_hash || !zones_state) {
rcu_read_unlock();
return blkdev_report_zone_fallback(bdev, sector, zone);
}
- zone->cond = zones_cond[disk_zone_no(disk, sector)];
+
+ zs = zones_state[disk_zone_no(disk, sector)];
+ zone->cond = blk_zstate_to_zone_cond(zs);
+ if (blk_zstate_is_conv(zs))
+ zone->type = BLK_ZONE_TYPE_CONVENTIONAL;
+ else
+ zone->type = BLK_ZONE_TYPE_SEQWRITE_REQ;
rcu_read_unlock();
zone->start = sector;
@@ -970,8 +1051,7 @@ int blkdev_get_zone_info(struct block_device *bdev, sector_t sector,
* If this is a conventional zone, we do not have a zone write plug and
* can report the zone immediately.
*/
- if (zone->cond == BLK_ZONE_COND_NOT_WP) {
- zone->type = BLK_ZONE_TYPE_CONVENTIONAL;
+ if (zone->type == BLK_ZONE_TYPE_CONVENTIONAL) {
zone->capacity = zone_sectors;
zone->wp = ULLONG_MAX;
return 0;
@@ -982,7 +1062,6 @@ int blkdev_get_zone_info(struct block_device *bdev, sector_t sector,
* offline, only set the zone write pointer to an invalid value and
* report the zone.
*/
- zone->type = BLK_ZONE_TYPE_SEQWRITE_REQ;
if (disk_zone_is_last(disk, zone))
zone->capacity = disk->last_zone_capacity;
else
@@ -1980,16 +2059,16 @@ static void disk_destroy_zone_wplugs_hash_table(struct gendisk *disk)
disk->zone_wplugs_pool = NULL;
}
-static void disk_set_zones_cond_array(struct gendisk *disk, u8 *zones_cond)
+static void disk_set_zones_state_array(struct gendisk *disk, u8 *zones_state)
{
unsigned long flags;
spin_lock_irqsave(&disk->zone_wplugs_hash_lock, flags);
- zones_cond = rcu_replace_pointer(disk->zones_cond, zones_cond,
+ zones_state = rcu_replace_pointer(disk->zones_state, zones_state,
lockdep_is_held(&disk->zone_wplugs_hash_lock));
spin_unlock_irqrestore(&disk->zone_wplugs_hash_lock, flags);
- kfree_rcu_mightsleep(zones_cond);
+ kfree_rcu_mightsleep(zones_state);
}
void disk_release_zone_resources(struct gendisk *disk)
@@ -2007,7 +2086,7 @@ void disk_release_zone_resources(struct gendisk *disk)
disk_destroy_zone_wplugs_hash_table(disk);
- disk_set_zones_cond_array(disk, NULL);
+ disk_set_zones_state_array(disk, NULL);
disk->zone_capacity = 0;
disk->last_zone_capacity = 0;
disk->nr_zones = 0;
@@ -2016,7 +2095,7 @@ void disk_release_zone_resources(struct gendisk *disk)
struct blk_revalidate_zone_args {
struct gendisk *disk;
sector_t capacity;
- u8 *zones_cond;
+ u8 *zones_state;
unsigned int nr_zones;
unsigned int nr_conv_zones;
unsigned int zone_capacity;
@@ -2031,8 +2110,8 @@ static int disk_init_revalidate_args(struct gendisk *disk,
args->nr_zones = disk_get_nr_zones(disk, args->capacity);
/* Cached zone conditions: 1 byte per zone */
- args->zones_cond = kzalloc(args->nr_zones, GFP_NOIO);
- if (!args->zones_cond)
+ args->zones_state = kzalloc(args->nr_zones, GFP_NOIO);
+ if (!args->zones_state)
return -ENOMEM;
return 0;
@@ -2086,8 +2165,8 @@ static int disk_revalidate_zone_resources(struct gendisk *disk,
disk->nr_zones = args->nr_zones;
disk->zone_capacity = args->zone_capacity;
disk->last_zone_capacity = args->last_zone_capacity;
- disk_set_zones_cond_array(disk, args->zones_cond);
- args->zones_cond = NULL;
+ disk_set_zones_state_array(disk, args->zones_state);
+ args->zones_state = NULL;
/*
* Some devices can advertise zone resource limits that are larger than
@@ -2139,12 +2218,14 @@ static int blk_revalidate_zone_cond(struct blk_zone *zone, unsigned int idx,
struct blk_revalidate_zone_args *args)
{
enum blk_zone_cond cond = zone->cond;
+ u8 flags = 0;
/* Check that the zone condition is consistent with the zone type. */
switch (cond) {
case BLK_ZONE_COND_NOT_WP:
if (zone->type != BLK_ZONE_TYPE_CONVENTIONAL)
goto invalid_condition;
+ flags = BLK_ZFLAG_CONV;
break;
case BLK_ZONE_COND_IMP_OPEN:
case BLK_ZONE_COND_EXP_OPEN:
@@ -2162,7 +2243,7 @@ static int blk_revalidate_zone_cond(struct blk_zone *zone, unsigned int idx,
return -ENODEV;
}
- blk_zone_set_cond(args->zones_cond, idx, cond);
+ blk_zstate_set(args->zones_state, args->nr_zones, idx, cond, flags);
return 0;
@@ -2390,7 +2471,7 @@ int blk_revalidate_disk_zones(struct gendisk *disk)
free_args:
pr_warn("%s: failed to revalidate zones\n", disk->disk_name);
- kfree(args.zones_cond);
+ kfree(args.zones_state);
return ret;
}
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
index 4f7905c3412b..6a765146a2d3 100644
--- a/include/linux/blkdev.h
+++ b/include/linux/blkdev.h
@@ -198,7 +198,7 @@ struct gendisk {
unsigned int nr_zones;
unsigned int zone_capacity;
unsigned int last_zone_capacity;
- u8 __rcu *zones_cond;
+ u8 __rcu *zones_state;
unsigned int zone_wplugs_hash_bits;
atomic_t nr_zone_wplugs;
spinlock_t zone_wplugs_hash_lock;
--
2.55.0
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH v7 06/16] block: refactor bdev_zone_is_seq()
2026-09-08 8:57 [PATCH v7 00/16] Improve handling of offline and read-only zones Damien Le Moal
` (4 preceding siblings ...)
2026-09-08 8:57 ` [PATCH v7 05/16] block: remember a zone type regardless of its condition Damien Le Moal
@ 2026-09-08 8:57 ` Damien Le Moal
2026-09-08 8:57 ` [PATCH v7 07/16] block: improve blkdev_get_zone_info() Damien Le Moal
` (9 subsequent siblings)
15 siblings, 0 replies; 28+ messages in thread
From: Damien Le Moal @ 2026-09-08 8:57 UTC (permalink / raw)
To: Jens Axboe, linux-block; +Cc: Christoph Hellwig
Define the helper function disk_zone_is_seq() and use it to refactor
bdev_zone_is_seq(). disk_zone_is_seq() is also used in
blk_zone_wplug_handle_write().
Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
Reviewed-by: Bart Van Assche <bvanassche@acm.org>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
block/blk-zoned.c | 38 +++++++++++++++++++++++++-------------
1 file changed, 25 insertions(+), 13 deletions(-)
diff --git a/block/blk-zoned.c b/block/blk-zoned.c
index 1284cc8cc367..14c88d11f243 100644
--- a/block/blk-zoned.c
+++ b/block/blk-zoned.c
@@ -260,6 +260,29 @@ static void disk_zone_set_cond(struct gendisk *disk, sector_t sector,
rcu_read_unlock();
}
+static inline u8 disk_zone_get_state(struct gendisk *disk, sector_t sector)
+{
+ unsigned int zno = disk_zone_no(disk, sector);
+ u8 *zones_state, zs;
+
+ rcu_read_lock();
+ zones_state = rcu_dereference(disk->zones_state);
+ if (likely(zones_state && zno < disk->nr_zones))
+ zs = zones_state[zno];
+ else
+ zs = BLK_ZFLAG_CONV;
+ rcu_read_unlock();
+
+ return zs;
+}
+
+static bool disk_zone_is_seq(struct gendisk *disk, sector_t sector)
+{
+ u8 zs = disk_zone_get_state(disk, sector);
+
+ return !blk_zstate_is_conv(zs);
+}
+
/**
* bdev_zone_is_seq - check if a sector belongs to a sequential write zone
* @bdev: block device to check
@@ -269,21 +292,10 @@ static void disk_zone_set_cond(struct gendisk *disk, sector_t sector,
*/
bool bdev_zone_is_seq(struct block_device *bdev, sector_t sector)
{
- struct gendisk *disk = bdev->bd_disk;
- unsigned int zno = disk_zone_no(disk, sector);
- bool is_seq = false;
- u8 *zones_state;
-
if (!bdev_is_zoned(bdev))
return false;
- rcu_read_lock();
- zones_state = rcu_dereference(disk->zones_state);
- if (zones_state && zno < disk->nr_zones)
- is_seq = !blk_zstate_is_conv(zones_state[zno]);
- rcu_read_unlock();
-
- return is_seq;
+ return disk_zone_is_seq(bdev->bd_disk, sector);
}
EXPORT_SYMBOL_GPL(bdev_zone_is_seq);
@@ -1521,7 +1533,7 @@ static bool blk_zone_wplug_handle_write(struct bio *bio, unsigned int nr_segs)
}
/* Conventional zones do not need write plugging. */
- if (!bdev_zone_is_seq(bio->bi_bdev, sector)) {
+ if (!disk_zone_is_seq(disk, sector)) {
/* Zone append to conventional zones is not allowed. */
if (bio_op(bio) == REQ_OP_ZONE_APPEND) {
bio_io_error(bio);
--
2.55.0
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH v7 07/16] block: improve blkdev_get_zone_info()
2026-09-08 8:57 [PATCH v7 00/16] Improve handling of offline and read-only zones Damien Le Moal
` (5 preceding siblings ...)
2026-09-08 8:57 ` [PATCH v7 06/16] block: refactor bdev_zone_is_seq() Damien Le Moal
@ 2026-09-08 8:57 ` Damien Le Moal
2026-09-09 9:11 ` Hannes Reinecke
2026-09-10 5:21 ` Christoph Hellwig
2026-09-08 8:57 ` [PATCH v7 08/16] block: introduce disk_for_all_zone_wplugs() Damien Le Moal
` (8 subsequent siblings)
15 siblings, 2 replies; 28+ messages in thread
From: Damien Le Moal @ 2026-09-08 8:57 UTC (permalink / raw)
To: Jens Axboe, linux-block; +Cc: Christoph Hellwig
Add a zone state array bound check to ensure that like
disk_zone_get_state(), we never attempt to access the zone state array
beyond its size.
Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
---
block/blk-zoned.c | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/block/blk-zoned.c b/block/blk-zoned.c
index 14c88d11f243..93d862d44e7c 100644
--- a/block/blk-zoned.c
+++ b/block/blk-zoned.c
@@ -1025,6 +1025,7 @@ int blkdev_get_zone_info(struct block_device *bdev, sector_t sector,
{
struct gendisk *disk = bdev->bd_disk;
sector_t zone_sectors = bdev_zone_sectors(bdev);
+ unsigned int zno = disk_zone_no(disk, sector);
struct blk_zone_wplug *zwplug;
unsigned long flags;
u8 *zones_state, zs;
@@ -1032,23 +1033,23 @@ int blkdev_get_zone_info(struct block_device *bdev, sector_t sector,
if (!bdev_is_zoned(bdev))
return -EOPNOTSUPP;
- if (sector >= get_capacity(disk))
+ if (sector >= get_capacity(disk) || zno >= disk->nr_zones)
return -EINVAL;
memset(zone, 0, sizeof(*zone));
sector = bdev_zone_start(bdev, sector);
if (!blkdev_has_cached_report_zones(bdev))
- return blkdev_report_zone_fallback(bdev, sector, zone);
+ goto fallback;
rcu_read_lock();
zones_state = rcu_dereference(disk->zones_state);
if (!disk->zone_wplugs_hash || !zones_state) {
rcu_read_unlock();
- return blkdev_report_zone_fallback(bdev, sector, zone);
+ goto fallback;
}
- zs = zones_state[disk_zone_no(disk, sector)];
+ zs = zones_state[zno];
zone->cond = blk_zstate_to_zone_cond(zs);
if (blk_zstate_is_conv(zs))
zone->type = BLK_ZONE_TYPE_CONVENTIONAL;
@@ -1113,6 +1114,9 @@ int blkdev_get_zone_info(struct block_device *bdev, sector_t sector,
disk_put_zone_wplug(zwplug);
return 0;
+
+fallback:
+ return blkdev_report_zone_fallback(bdev, sector, zone);
}
EXPORT_SYMBOL_GPL(blkdev_get_zone_info);
--
2.55.0
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH v7 08/16] block: introduce disk_for_all_zone_wplugs()
2026-09-08 8:57 [PATCH v7 00/16] Improve handling of offline and read-only zones Damien Le Moal
` (6 preceding siblings ...)
2026-09-08 8:57 ` [PATCH v7 07/16] block: improve blkdev_get_zone_info() Damien Le Moal
@ 2026-09-08 8:57 ` Damien Le Moal
2026-09-08 8:57 ` [PATCH v7 09/16] block: serialize zone revalidation Damien Le Moal
` (7 subsequent siblings)
15 siblings, 0 replies; 28+ messages in thread
From: Damien Le Moal @ 2026-09-08 8:57 UTC (permalink / raw)
To: Jens Axboe, linux-block; +Cc: Christoph Hellwig
Introduce the helper function disk_for_all_zone_wplugs() to allow
executing an actor function on all hashed zone write plugs. This helper
is used to simplify the implementation of blk_zone_reset_all_bio_endio()
and queue_zone_wplugs_show().
Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
Reviewed-by: Bart Van Assche <bvanassche@acm.org>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
block/blk-zoned.c | 68 +++++++++++++++++++++++++----------------------
1 file changed, 36 insertions(+), 32 deletions(-)
diff --git a/block/blk-zoned.c b/block/blk-zoned.c
index 93d862d44e7c..70a687bb371f 100644
--- a/block/blk-zoned.c
+++ b/block/blk-zoned.c
@@ -661,6 +661,26 @@ static inline struct blk_zone_wplug *disk_get_zone_wplug(struct gendisk *disk,
return disk_get_hashed_zone_wplug(disk, sector);
}
+static void disk_for_all_zone_wplugs(struct gendisk *disk,
+ void (*actor)(struct blk_zone_wplug *,
+ void *),
+ void *data)
+{
+ struct blk_zone_wplug *zwplug;
+ unsigned int i;
+
+ if (!disk->zone_wplugs_hash)
+ return;
+
+ rcu_read_lock();
+ for (i = 0; i < disk_zone_wplugs_hash_size(disk); i++) {
+ hlist_for_each_entry_rcu(zwplug, &disk->zone_wplugs_hash[i],
+ node)
+ actor(zwplug, data);
+ }
+ rcu_read_unlock();
+}
+
static void disk_free_zone_wplug_rcu(struct rcu_head *rcu_head)
{
struct blk_zone_wplug *zwplug =
@@ -1203,32 +1223,26 @@ static void blk_zone_reset_bio_endio(struct bio *bio)
}
}
+static void disk_zone_wplug_reset_wp(struct blk_zone_wplug *zwplug, void *data)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&zwplug->lock, flags);
+ disk_zone_wplug_set_wp_offset(zwplug->disk, zwplug, 0);
+ spin_unlock_irqrestore(&zwplug->lock, flags);
+}
+
static void blk_zone_reset_all_bio_endio(struct bio *bio)
{
struct gendisk *disk = bio->bi_bdev->bd_disk;
- sector_t capacity = get_capacity(disk);
- struct blk_zone_wplug *zwplug;
- unsigned long flags;
sector_t sector;
- unsigned int i;
- if (atomic_read(&disk->nr_zone_wplugs)) {
- /* Update the condition of all zone write plugs. */
- rcu_read_lock();
- for (i = 0; i < disk_zone_wplugs_hash_size(disk); i++) {
- hlist_for_each_entry_rcu(zwplug,
- &disk->zone_wplugs_hash[i],
- node) {
- spin_lock_irqsave(&zwplug->lock, flags);
- disk_zone_wplug_set_wp_offset(disk, zwplug, 0);
- spin_unlock_irqrestore(&zwplug->lock, flags);
- }
- }
- rcu_read_unlock();
- }
+ /* Update the condition of all zone write plugs. */
+ if (atomic_read(&disk->nr_zone_wplugs))
+ disk_for_all_zone_wplugs(disk, disk_zone_wplug_reset_wp, NULL);
/* Update the cached zone conditions. */
- for (sector = 0; sector < capacity;
+ for (sector = 0; sector < get_capacity(disk);
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);
@@ -2538,8 +2552,9 @@ EXPORT_SYMBOL_GPL(blk_zone_issue_zeroout);
#ifdef CONFIG_BLK_DEBUG_FS
static void queue_zone_wplug_show(struct blk_zone_wplug *zwplug,
- struct seq_file *m)
+ void *data)
{
+ struct seq_file *m = data;
unsigned int zwp_wp_offset, zwp_flags;
unsigned int zwp_zone_no, zwp_ref;
unsigned int zwp_bio_list_size;
@@ -2564,19 +2579,8 @@ static void queue_zone_wplug_show(struct blk_zone_wplug *zwplug,
int queue_zone_wplugs_show(void *data, struct seq_file *m)
{
struct request_queue *q = data;
- struct gendisk *disk = q->disk;
- struct blk_zone_wplug *zwplug;
- unsigned int i;
-
- if (!disk->zone_wplugs_hash)
- return 0;
- rcu_read_lock();
- for (i = 0; i < disk_zone_wplugs_hash_size(disk); i++)
- hlist_for_each_entry_rcu(zwplug, &disk->zone_wplugs_hash[i],
- node)
- queue_zone_wplug_show(zwplug, m);
- rcu_read_unlock();
+ disk_for_all_zone_wplugs(q->disk, queue_zone_wplug_show, m);
return 0;
}
--
2.55.0
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH v7 09/16] block: serialize zone revalidation
2026-09-08 8:57 [PATCH v7 00/16] Improve handling of offline and read-only zones Damien Le Moal
` (7 preceding siblings ...)
2026-09-08 8:57 ` [PATCH v7 08/16] block: introduce disk_for_all_zone_wplugs() Damien Le Moal
@ 2026-09-08 8:57 ` Damien Le Moal
2026-09-09 9:20 ` Hannes Reinecke
2026-09-10 5:21 ` Christoph Hellwig
2026-09-08 8:57 ` [PATCH v7 10/16] block: drop all zone write plugs on capacity changes Damien Le Moal
` (6 subsequent siblings)
15 siblings, 2 replies; 28+ messages in thread
From: Damien Le Moal @ 2026-09-08 8:57 UTC (permalink / raw)
To: Jens Axboe, linux-block; +Cc: Christoph Hellwig
The zone related fields of struct gendisk can be modified by
blk_revalidate_disk_zones() either on the first scan of the disk, or
during user triggered scans or device revalidation, if some
characteristics of the disk has changed (e.g. the disk capacity). Changes
to these fields are always done with the disk request queue frozen so that
BIO processing does not see any inconsistent state of the zones. This
implies a contract that reading these zone related fields must always be
done while holding a usage count on the request queue of the disk.
However, increasing the usage count of the disk request queue cannot be
done from the context of blk_revalidate_disk_zones() itself, as that would
prevent freezing the disk queue and result in a deadlock. This prevents
blk_revalidate_disk_zones() from consulting the zone related fields of
struct gendisk to detect, for instance, a change in the number of zones of
the disk. For such case, we want to detect the change, take appropriate
measures and revalidate exclusively revalidate the zones to avoid
concurrent revalidation calls to see the same change while corrections are
already on-going.
A simple solution to avoid this issue is to introduce a mutex to serialize
calls to blk_revalidate_disk_zones() and ensure only a single context at a
time can modify the zone related fields of a gendisk. In preparation for
handling disk capacity revalidation in blk_revalidate_disk_zones(), do so
with the mutex zone_revalidate_mutex. This mutex is initialized in
disk_init_zone_resources(), destroyed in disk_release_zone_resources() and
taken and released only in blk_revalidate_disk_zones() to serialize the
execution of this function.
Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
---
block/blk-zoned.c | 17 +++++++++++++++--
include/linux/blkdev.h | 9 ++++++---
2 files changed, 21 insertions(+), 5 deletions(-)
diff --git a/block/blk-zoned.c b/block/blk-zoned.c
index 70a687bb371f..676620ed93de 100644
--- a/block/blk-zoned.c
+++ b/block/blk-zoned.c
@@ -1963,6 +1963,7 @@ static int disk_zone_wplugs_worker(void *data)
void disk_init_zone_resources(struct gendisk *disk)
{
+ mutex_init(&disk->zone_revalidate_mutex);
atomic_set(&disk->nr_zone_wplugs, 0);
spin_lock_init(&disk->zone_wplugs_hash_lock);
spin_lock_init(&disk->zone_wplugs_list_lock);
@@ -2120,6 +2121,7 @@ void disk_release_zone_resources(struct gendisk *disk)
disk->zone_capacity = 0;
disk->last_zone_capacity = 0;
disk->nr_zones = 0;
+ mutex_destroy(&disk->zone_revalidate_mutex);
}
struct blk_revalidate_zone_args {
@@ -2461,6 +2463,12 @@ int blk_revalidate_disk_zones(struct gendisk *disk)
return -ENODEV;
}
+ /*
+ * Serialize calls to this function so that we can safely look at and
+ * eventually change the disk zone information.
+ */
+ mutex_lock(&disk->zone_revalidate_mutex);
+
/*
* Allocate zone resources if they are needed and we have not done
* so yet, and initialize the revalidation arguments passed to report
@@ -2472,14 +2480,14 @@ int blk_revalidate_disk_zones(struct gendisk *disk)
ret = disk_alloc_zone_resources(disk, args.capacity);
if (ret) {
memalloc_noio_restore(noio_flag);
- return ret;
+ goto unlock;
}
}
ret = disk_init_revalidate_args(disk, &args);
if (ret) {
memalloc_noio_restore(noio_flag);
- return ret;
+ goto unlock;
}
ret = disk->fops->report_zones(disk, 0, UINT_MAX, &rep_args);
@@ -2496,6 +2504,8 @@ int blk_revalidate_disk_zones(struct gendisk *disk)
if (ret)
goto free_args;
+ mutex_unlock(&disk->zone_revalidate_mutex);
+
return 0;
free_args:
@@ -2503,6 +2513,9 @@ int blk_revalidate_disk_zones(struct gendisk *disk)
kfree(args.zones_state);
+unlock:
+ mutex_unlock(&disk->zone_revalidate_mutex);
+
return ret;
}
EXPORT_SYMBOL_GPL(blk_revalidate_disk_zones);
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
index 6a765146a2d3..8252c896e3ea 100644
--- a/include/linux/blkdev.h
+++ b/include/linux/blkdev.h
@@ -191,10 +191,13 @@ struct gendisk {
#ifdef CONFIG_BLK_DEV_ZONED
/*
* Zoned block device information. Reads of this information must be
- * protected with blk_queue_enter() / blk_queue_exit(). Modifying this
- * information is only allowed while no requests are being processed.
- * See also blk_mq_freeze_queue() and blk_mq_unfreeze_queue().
+ * protected with blk_queue_enter() / blk_queue_exit() or by holding a
+ * lock on zone_revalidate_mutex. blk_revalidate_disk_zones() may modify
+ * this information while no requests are being processed (disk queue
+ * frozen with blk_mq_freeze_queue()) and while holding a lock on
+ * zone_revalidate_mutex.
*/
+ struct mutex zone_revalidate_mutex;
unsigned int nr_zones;
unsigned int zone_capacity;
unsigned int last_zone_capacity;
--
2.55.0
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH v7 10/16] block: drop all zone write plugs on capacity changes
2026-09-08 8:57 [PATCH v7 00/16] Improve handling of offline and read-only zones Damien Le Moal
` (8 preceding siblings ...)
2026-09-08 8:57 ` [PATCH v7 09/16] block: serialize zone revalidation Damien Le Moal
@ 2026-09-08 8:57 ` Damien Le Moal
2026-09-08 8:57 ` [PATCH v7 11/16] block: retry zone revalidation on capacity change Damien Le Moal
` (5 subsequent siblings)
15 siblings, 0 replies; 28+ messages in thread
From: Damien Le Moal @ 2026-09-08 8:57 UTC (permalink / raw)
To: Jens Axboe, linux-block; +Cc: Christoph Hellwig
If during revalidation, we detect a capacity change for a zoned block
device, e.g. due to a storage element removal on an HDD, we can assume
that the device was reformatted, which implies that all sequential zones
are empty. For such case, we can remove and free all zone write plugs in
the gendisk hash table by marking them as dead, thus avoiding also to
leave zone write plugs for zones that are beyond the new device capacity
in the disk hash table.
Introduce the function disk_revalidate_capacity() to do this and call this
new function at the beginning of blk_revalidate_disk_zones(), so that the
zone revalidation process can re-create, if needed, any zone write plug
for sequential zones that are not empty.
The checks on the capacity and zone size that were in
blk_revalidate_disk_zones() are moved to disk_revalidate_capacity() and
if true, also trigger dropping all zone write plugs.
Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
Reviewed-by: Bart Van Assche <bvanassche@acm.org>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
block/blk-zoned.c | 77 +++++++++++++++++++++++++++++++++++------------
1 file changed, 57 insertions(+), 20 deletions(-)
diff --git a/block/blk-zoned.c b/block/blk-zoned.c
index 676620ed93de..e7f20b5262c7 100644
--- a/block/blk-zoned.c
+++ b/block/blk-zoned.c
@@ -2246,6 +2246,56 @@ static int disk_revalidate_zone_resources(struct gendisk *disk,
return ret;
}
+static void disk_drop_zone_wplug(struct blk_zone_wplug *zwplug, void *data)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&zwplug->lock, flags);
+ disk_zone_wplug_abort(zwplug);
+ disk_mark_zone_wplug_dead(zwplug);
+ spin_unlock_irqrestore(&zwplug->lock, flags);
+}
+
+static int disk_revalidate_capacity(struct gendisk *disk,
+ struct blk_revalidate_zone_args *args)
+{
+ struct queue_limits *lim = &disk->queue->limits;
+ sector_t zone_sectors = lim->chunk_sectors;
+ unsigned int nr_zones;
+ int ret = -ENODEV;
+
+ /* Checks that the device driver indicated a valid zone size. */
+ if (!zone_sectors || !is_power_of_2(zone_sectors)) {
+ pr_warn("%s: Invalid non power of two zone size (%llu)\n",
+ disk->disk_name, zone_sectors);
+ goto drop_all_zwplugs;
+ }
+
+ args->capacity = get_capacity(disk);
+ nr_zones = disk_get_nr_zones(disk, args->capacity);
+ if (!args->capacity || !nr_zones)
+ goto drop_all_zwplugs;
+
+ /*
+ * Check if the capacity has changed. If it did, assume that the device
+ * was reformatted and that all sequential zones are now empty. So drop
+ * all zone write plug.
+ */
+ if (disk->nr_zones && disk->nr_zones != nr_zones) {
+ pr_warn("%s: Number of zones changed (%u -> %u)\n",
+ disk->disk_name, disk->nr_zones, nr_zones);
+ ret = 0;
+ goto drop_all_zwplugs;
+ }
+
+ return 0;
+
+drop_all_zwplugs:
+ disk_for_all_zone_wplugs(disk, disk_drop_zone_wplug, NULL);
+
+ return ret;
+}
+
static int blk_revalidate_zone_cond(struct blk_zone *zone, unsigned int idx,
struct blk_revalidate_zone_args *args)
{
@@ -2435,40 +2485,27 @@ static int blk_revalidate_zone_cb(struct blk_zone *zone, unsigned int idx,
*/
int blk_revalidate_disk_zones(struct gendisk *disk)
{
- struct request_queue *q = disk->queue;
- sector_t zone_sectors = q->limits.chunk_sectors;
- struct blk_revalidate_zone_args args = {
- .capacity = get_capacity(disk),
- };
+ struct blk_revalidate_zone_args args = { };
struct blk_report_zones_args rep_args = {
.cb = blk_revalidate_zone_cb,
.data = &args,
};
unsigned int noio_flag;
- int ret = -ENOMEM;
+ int ret;
- if (WARN_ON_ONCE(!blk_queue_is_zoned(q)))
+ if (WARN_ON_ONCE(!blk_queue_is_zoned(disk->queue)))
return -EIO;
- if (!args.capacity)
- return -ENODEV;
-
- /*
- * Checks that the device driver indicated a valid zone size and that
- * the max zone append limit is set.
- */
- if (!zone_sectors || !is_power_of_2(zone_sectors)) {
- pr_warn("%s: Invalid non power of two zone size (%llu)\n",
- disk->disk_name, zone_sectors);
- return -ENODEV;
- }
-
/*
* Serialize calls to this function so that we can safely look at and
* eventually change the disk zone information.
*/
mutex_lock(&disk->zone_revalidate_mutex);
+ ret = disk_revalidate_capacity(disk, &args);
+ if (ret)
+ goto unlock;
+
/*
* Allocate zone resources if they are needed and we have not done
* so yet, and initialize the revalidation arguments passed to report
--
2.55.0
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH v7 11/16] block: retry zone revalidation on capacity change
2026-09-08 8:57 [PATCH v7 00/16] Improve handling of offline and read-only zones Damien Le Moal
` (9 preceding siblings ...)
2026-09-08 8:57 ` [PATCH v7 10/16] block: drop all zone write plugs on capacity changes Damien Le Moal
@ 2026-09-08 8:57 ` Damien Le Moal
2026-09-09 9:26 ` Hannes Reinecke
2026-09-10 5:24 ` Christoph Hellwig
2026-09-08 8:57 ` [PATCH v7 12/16] block: propagate readonly and offline conditions to zone write plugs Damien Le Moal
` (4 subsequent siblings)
15 siblings, 2 replies; 28+ messages in thread
From: Damien Le Moal @ 2026-09-08 8:57 UTC (permalink / raw)
To: Jens Axboe, linux-block; +Cc: Christoph Hellwig
When disk_revalidate_zone_resources() detects a capacity change, -ENODEV
is returned, failing the disk revalidation. However, since a capacity
change may happen due to a storage element removal being executed
concurrently to blk_revalidate_disk_zones(), we can simply retry the
revalidation to capture the new zone state with the new capacity without
failing the revalidation.
Retrying the revalidation is driven by disk_revalidate_zone_resources()
returning -EAGAIN when a new valid capacity is detected. And to avoid
getting stuck in an infinite loop revalidating zones, retries are limited
to 2.
Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
---
block/blk-zoned.c | 20 +++++++++++++++++---
1 file changed, 17 insertions(+), 3 deletions(-)
diff --git a/block/blk-zoned.c b/block/blk-zoned.c
index e7f20b5262c7..96e922b7a126 100644
--- a/block/blk-zoned.c
+++ b/block/blk-zoned.c
@@ -2175,7 +2175,11 @@ static int disk_revalidate_zone_resources(struct gendisk *disk,
if (args->capacity != capacity) {
pr_warn("%s: Capacity has changed (%llu -> %llu)\n",
disk->disk_name, args->capacity, capacity);
- ret = -ENODEV;
+ /* Force a retry if we have a valid (non-zero) capacity. */
+ if (capacity)
+ ret = -EAGAIN;
+ else
+ ret = -ENODEV;
goto unfreeze;
}
@@ -2491,6 +2495,7 @@ int blk_revalidate_disk_zones(struct gendisk *disk)
.data = &args,
};
unsigned int noio_flag;
+ int retries = 2;
int ret;
if (WARN_ON_ONCE(!blk_queue_is_zoned(disk->queue)))
@@ -2502,6 +2507,7 @@ int blk_revalidate_disk_zones(struct gendisk *disk)
*/
mutex_lock(&disk->zone_revalidate_mutex);
+again:
ret = disk_revalidate_capacity(disk, &args);
if (ret)
goto unlock;
@@ -2546,10 +2552,18 @@ int blk_revalidate_disk_zones(struct gendisk *disk)
return 0;
free_args:
- pr_warn("%s: failed to revalidate zones\n", disk->disk_name);
-
kfree(args.zones_state);
+ if (ret == -EAGAIN) {
+ if (retries) {
+ memset(&args, 0, sizeof(args));
+ retries--;
+ goto again;
+ }
+ ret = -ENODEV;
+ }
+
+ pr_warn("%s: failed to revalidate zones\n", disk->disk_name);
unlock:
mutex_unlock(&disk->zone_revalidate_mutex);
--
2.55.0
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH v7 12/16] block: propagate readonly and offline conditions to zone write plugs
2026-09-08 8:57 [PATCH v7 00/16] Improve handling of offline and read-only zones Damien Le Moal
` (10 preceding siblings ...)
2026-09-08 8:57 ` [PATCH v7 11/16] block: retry zone revalidation on capacity change Damien Le Moal
@ 2026-09-08 8:57 ` Damien Le Moal
2026-09-08 8:57 ` [PATCH v7 13/16] block: always treat offline and read-only zones as dead Damien Le Moal
` (3 subsequent siblings)
15 siblings, 0 replies; 28+ messages in thread
From: Damien Le Moal @ 2026-09-08 8:57 UTC (permalink / raw)
To: Jens Axboe, linux-block; +Cc: Christoph Hellwig
When revalidating the zones of a zoned block device, or when handling a
zone which has a zone write plug flagged with
BLK_ZONE_WPLUG_NEED_WP_UPDATE, the function
disk_zone_wplug_sync_wp_offset() is used to update the write pointer
offset of a zone write plug. However, this does not take into account the
condition of the zone, which may have changed to readonly or offline,
which in itself will always cause errors.
In order to catch such errors, rename disk_zone_wplug_sync_wp_offset() to
disk_zone_wplug_sync_state() and in addition to the zone write pointer,
also update the zone write plug condition if the zone is readonly or
offline.
This change also requires changes to how a reset all zones operation
(REQ_OP_ZONE_RESET_ALL) is handled so that the offline or read-only
condition of zone write plugs is not overwritten with an erroneous empty
condition. To do so, introduce the helper function
disk_zone_is_offline_or_readonly() to skip updating the condition of zones
that are read-only or offline and that do not have a zone write plug.
For zones that have a zone write plug, the helper function
disk_zone_wplug_is_offline_or_readonly() is used in
disk_zone_wplug_set_wp_offset() to not update a zone write plug write
pointer and condition for read-only and offline zones.
Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
Reviewed-by: Bart Van Assche <bvanassche@acm.org>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
---
block/blk-zoned.c | 55 +++++++++++++++++++++++++++++++++++++++--------
1 file changed, 46 insertions(+), 9 deletions(-)
diff --git a/block/blk-zoned.c b/block/blk-zoned.c
index 96e922b7a126..ac94ab32564b 100644
--- a/block/blk-zoned.c
+++ b/block/blk-zoned.c
@@ -276,6 +276,29 @@ static inline u8 disk_zone_get_state(struct gendisk *disk, sector_t sector)
return zs;
}
+static enum blk_zone_cond disk_zone_get_cond(struct gendisk *disk,
+ sector_t sector)
+{
+ u8 zs = disk_zone_get_state(disk, sector);
+
+ return blk_zstate_to_zone_cond(zs);
+}
+
+static inline bool
+disk_zone_cond_is_offline_or_readonly(enum blk_zone_cond cond)
+{
+ return cond == BLK_ZONE_COND_READONLY ||
+ cond == BLK_ZONE_COND_OFFLINE;
+}
+
+static inline bool disk_zone_is_offline_or_readonly(struct gendisk *disk,
+ sector_t sector)
+{
+ enum blk_zone_cond cond = disk_zone_get_cond(disk, sector);
+
+ return disk_zone_cond_is_offline_or_readonly(cond);
+}
+
static bool disk_zone_is_seq(struct gendisk *disk, sector_t sector)
{
u8 zs = disk_zone_get_state(disk, sector);
@@ -586,6 +609,11 @@ static bool disk_zone_wplug_is_full(struct gendisk *disk,
return zwplug->wp_offset >= disk->last_zone_capacity;
}
+static bool disk_zone_wplug_is_offline_or_readonly(struct blk_zone_wplug *zwplug)
+{
+ return disk_zone_cond_is_offline_or_readonly(zwplug->cond);
+}
+
static bool disk_insert_zone_wplug(struct gendisk *disk,
struct blk_zone_wplug *zwplug)
{
@@ -892,8 +920,10 @@ static void disk_zone_wplug_set_wp_offset(struct gendisk *disk,
/* Update the zone write pointer and abort all plugged BIOs. */
zwplug->flags &= ~BLK_ZONE_WPLUG_NEED_WP_UPDATE;
- zwplug->wp_offset = wp_offset;
- disk_zone_wplug_update_cond(disk, zwplug);
+ if (!disk_zone_wplug_is_offline_or_readonly(zwplug)) {
+ zwplug->wp_offset = wp_offset;
+ disk_zone_wplug_update_cond(disk, zwplug);
+ }
disk_zone_wplug_abort(zwplug);
if (!zwplug->wp_offset || disk_zone_wplug_is_full(disk, zwplug))
@@ -923,8 +953,8 @@ static unsigned int blk_zone_wp_offset(struct blk_zone *zone)
}
}
-static unsigned int disk_zone_wplug_sync_wp_offset(struct gendisk *disk,
- struct blk_zone *zone)
+static unsigned int disk_zone_wplug_sync_state(struct gendisk *disk,
+ struct blk_zone *zone)
{
struct blk_zone_wplug *zwplug;
unsigned int wp_offset = blk_zone_wp_offset(zone);
@@ -934,6 +964,11 @@ static unsigned int disk_zone_wplug_sync_wp_offset(struct gendisk *disk,
unsigned long flags;
spin_lock_irqsave(&zwplug->lock, flags);
+ if (disk_zone_cond_is_offline_or_readonly(zone->cond)) {
+ zwplug->flags &= ~BLK_ZONE_WPLUG_NEED_WP_UPDATE;
+ zwplug->cond = zone->cond;
+ zwplug->wp_offset = UINT_MAX;
+ }
if (zwplug->flags & BLK_ZONE_WPLUG_NEED_WP_UPDATE)
disk_zone_wplug_set_wp_offset(disk, zwplug, wp_offset);
spin_unlock_irqrestore(&zwplug->lock, flags);
@@ -978,7 +1013,7 @@ int disk_report_zone(struct gendisk *disk, struct blk_zone *zone,
}
if (disk->zone_wplugs_hash)
- disk_zone_wplug_sync_wp_offset(disk, zone);
+ disk_zone_wplug_sync_state(disk, zone);
if (args && args->cb)
return args->cb(zone, idx, args->data);
@@ -1100,8 +1135,7 @@ int blkdev_get_zone_info(struct block_device *bdev, sector_t sector,
else
zone->capacity = disk->zone_capacity;
- if (zone->cond == BLK_ZONE_COND_READONLY ||
- zone->cond == BLK_ZONE_COND_OFFLINE) {
+ if (disk_zone_cond_is_offline_or_readonly(zone->cond)) {
zone->wp = ULLONG_MAX;
return 0;
}
@@ -1243,8 +1277,11 @@ static void blk_zone_reset_all_bio_endio(struct bio *bio)
/* Update the cached zone conditions. */
for (sector = 0; sector < get_capacity(disk);
- sector += bdev_zone_sectors(bio->bi_bdev))
+ sector += bdev_zone_sectors(bio->bi_bdev)) {
+ if (disk_zone_is_offline_or_readonly(disk, sector))
+ continue;
disk_zone_set_cond(disk, sector, BLK_ZONE_COND_EMPTY);
+ }
clear_bit(GD_ZONE_APPEND_USED, &disk->state);
}
@@ -2390,7 +2427,7 @@ static int blk_revalidate_seq_zone(struct blk_zone *zone, unsigned int idx,
if (!disk->zone_wplugs_hash)
return 0;
- wp_offset = disk_zone_wplug_sync_wp_offset(disk, zone);
+ wp_offset = disk_zone_wplug_sync_state(disk, zone);
if (!wp_offset || wp_offset >= zone->capacity)
return 0;
--
2.55.0
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH v7 13/16] block: always treat offline and read-only zones as dead
2026-09-08 8:57 [PATCH v7 00/16] Improve handling of offline and read-only zones Damien Le Moal
` (11 preceding siblings ...)
2026-09-08 8:57 ` [PATCH v7 12/16] block: propagate readonly and offline conditions to zone write plugs Damien Le Moal
@ 2026-09-08 8:57 ` Damien Le Moal
2026-09-08 8:57 ` [PATCH v7 14/16] block: fail zone management operations to read-only and offline zones Damien Le Moal
` (2 subsequent siblings)
15 siblings, 0 replies; 28+ messages in thread
From: Damien Le Moal @ 2026-09-08 8:57 UTC (permalink / raw)
To: Jens Axboe, linux-block; +Cc: Christoph Hellwig
Since any write BIO targeting an offline or a read-only zone will fail,
there is no point in keeping zone write plugs for these zones.
So for any offline or read-only zone, the zone write plug should always
be treated as dead.
Do this by modifying disk_check_zone_wplug_dead() to always mark read-only
and offline zones as dead to force a removal of the zone write plug from
the disk hash table on BIO submission. blk_zone_wplug_prepare_bio() is
also modified to have the same checks to immediately fail a write BIO
targeting a read-only or offline zone. With these two changes, any newly
issued or unplugged write BIO targeting a read-only or offline zone is
immediately failed.
Finally, disk_zone_wplug_sync_state() is modified to add a call to
disk_mark_zone_wplug_dead() for the zone write plug of any read-only or
offline zone found during zone revalidation or a report zones.
Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
Reviewed-by: Bart Van Assche <bvanassche@acm.org>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
---
block/blk-zoned.c | 110 +++++++++++++++++++++++++---------------------
1 file changed, 59 insertions(+), 51 deletions(-)
diff --git a/block/blk-zoned.c b/block/blk-zoned.c
index ac94ab32564b..ecaeacf3b287 100644
--- a/block/blk-zoned.c
+++ b/block/blk-zoned.c
@@ -743,6 +743,53 @@ static inline void disk_put_zone_wplug(struct blk_zone_wplug *zwplug)
disk_free_zone_wplug(zwplug);
}
+static inline void blk_zone_wplug_bio_io_error(struct blk_zone_wplug *zwplug,
+ struct bio *bio)
+{
+ struct request_queue *q = zwplug->disk->queue;
+
+ bio_clear_flag(bio, BIO_ZONE_WRITE_PLUGGING);
+ bio_io_error(bio);
+ disk_put_zone_wplug(zwplug);
+ /* Drop the reference taken by disk_zone_wplug_add_bio(). */
+ blk_queue_exit(q);
+}
+
+/*
+ * Abort (fail) all plugged BIOs of a zone write plug.
+ */
+static void disk_zone_wplug_abort(struct blk_zone_wplug *zwplug)
+{
+ struct gendisk *disk = zwplug->disk;
+ struct bio *bio;
+
+ lockdep_assert_held(&zwplug->lock);
+
+ if (bio_list_empty(&zwplug->bio_list))
+ return;
+
+ pr_warn_ratelimited("%s: zone %u: Aborting plugged BIOs\n",
+ zwplug->disk->disk_name, zwplug->zone_no);
+ while ((bio = bio_list_pop(&zwplug->bio_list)))
+ blk_zone_wplug_bio_io_error(zwplug, bio);
+
+ zwplug->flags &= ~BLK_ZONE_WPLUG_PLUGGED;
+
+ /*
+ * If we are using the per disk zone write plugs worker thread, remove
+ * the zone write plug from the work list and drop the reference we
+ * took when the zone write plug was added to that list.
+ */
+ if (blk_queue_zoned_qd1_writes(disk->queue)) {
+ spin_lock(&disk->zone_wplugs_list_lock);
+ if (!list_empty(&zwplug->entry)) {
+ list_del_init(&zwplug->entry);
+ disk_put_zone_wplug(zwplug);
+ }
+ spin_unlock(&disk->zone_wplugs_list_lock);
+ }
+}
+
/*
* Flag the zone write plug as dead and drop the initial reference we got when
* the zone write plug was added to the hash table. The zone write plug will be
@@ -760,6 +807,12 @@ static void disk_mark_zone_wplug_dead(struct blk_zone_wplug *zwplug)
static inline bool disk_check_zone_wplug_dead(struct blk_zone_wplug *zwplug)
{
+ if (disk_zone_wplug_is_offline_or_readonly(zwplug)) {
+ disk_zone_wplug_abort(zwplug);
+ disk_mark_zone_wplug_dead(zwplug);
+ return true;
+ }
+
if (!(zwplug->flags & BLK_ZONE_WPLUG_DEAD))
return false;
@@ -843,53 +896,6 @@ static struct blk_zone_wplug *disk_get_or_alloc_zone_wplug(struct gendisk *disk,
return zwplug;
}
-static inline void blk_zone_wplug_bio_io_error(struct blk_zone_wplug *zwplug,
- struct bio *bio)
-{
- struct request_queue *q = zwplug->disk->queue;
-
- bio_clear_flag(bio, BIO_ZONE_WRITE_PLUGGING);
- bio_io_error(bio);
- disk_put_zone_wplug(zwplug);
- /* Drop the reference taken by disk_zone_wplug_add_bio(). */
- blk_queue_exit(q);
-}
-
-/*
- * Abort (fail) all plugged BIOs of a zone write plug.
- */
-static void disk_zone_wplug_abort(struct blk_zone_wplug *zwplug)
-{
- struct gendisk *disk = zwplug->disk;
- struct bio *bio;
-
- lockdep_assert_held(&zwplug->lock);
-
- if (bio_list_empty(&zwplug->bio_list))
- return;
-
- pr_warn_ratelimited("%s: zone %u: Aborting plugged BIOs\n",
- zwplug->disk->disk_name, zwplug->zone_no);
- while ((bio = bio_list_pop(&zwplug->bio_list)))
- blk_zone_wplug_bio_io_error(zwplug, bio);
-
- zwplug->flags &= ~BLK_ZONE_WPLUG_PLUGGED;
-
- /*
- * If we are using the per disk zone write plugs worker thread, remove
- * the zone write plug from the work list and drop the reference we
- * took when the zone write plug was added to that list.
- */
- if (blk_queue_zoned_qd1_writes(disk->queue)) {
- spin_lock(&disk->zone_wplugs_list_lock);
- if (!list_empty(&zwplug->entry)) {
- list_del_init(&zwplug->entry);
- disk_put_zone_wplug(zwplug);
- }
- spin_unlock(&disk->zone_wplugs_list_lock);
- }
-}
-
/*
* Update a zone write plug condition based on the write pointer offset.
*/
@@ -968,6 +974,7 @@ static unsigned int disk_zone_wplug_sync_state(struct gendisk *disk,
zwplug->flags &= ~BLK_ZONE_WPLUG_NEED_WP_UPDATE;
zwplug->cond = zone->cond;
zwplug->wp_offset = UINT_MAX;
+ disk_mark_zone_wplug_dead(zwplug);
}
if (zwplug->flags & BLK_ZONE_WPLUG_NEED_WP_UPDATE)
disk_zone_wplug_set_wp_offset(disk, zwplug, wp_offset);
@@ -1527,11 +1534,12 @@ static bool blk_zone_wplug_prepare_bio(struct blk_zone_wplug *zwplug,
return false;
/*
- * Check that the user is not attempting to write to a full zone.
- * We know such BIO will fail, and that would potentially overflow our
- * write pointer offset beyond the end of the zone.
+ * Check that the user is not attempting to write to a full, read-only
+ * or offline zone. We know such BIOs will fail, so there is no point
+ * in issuing them.
*/
- if (disk_zone_wplug_is_full(disk, zwplug))
+ if (disk_zone_wplug_is_full(disk, zwplug) ||
+ disk_zone_wplug_is_offline_or_readonly(zwplug))
return false;
if (bio_op(bio) == REQ_OP_ZONE_APPEND) {
--
2.55.0
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH v7 14/16] block: fail zone management operations to read-only and offline zones
2026-09-08 8:57 [PATCH v7 00/16] Improve handling of offline and read-only zones Damien Le Moal
` (12 preceding siblings ...)
2026-09-08 8:57 ` [PATCH v7 13/16] block: always treat offline and read-only zones as dead Damien Le Moal
@ 2026-09-08 8:57 ` Damien Le Moal
2026-09-08 8:57 ` [PATCH v7 15/16] block: allow read-only and offline conventional zones Damien Le Moal
2026-09-08 8:57 ` [PATCH v7 16/16] block: simplify disk_zone_set_cond() Damien Le Moal
15 siblings, 0 replies; 28+ messages in thread
From: Damien Le Moal @ 2026-09-08 8:57 UTC (permalink / raw)
To: Jens Axboe, linux-block; +Cc: Christoph Hellwig
Any zone management operation targeting a zone that is in the read-only
or offline condition will fail. So there is no point in issuing such
BIO. Modify the check in submit_bio_noacct() to use the new helper
function bdev_zone_mgmt_allowed() to check that a zone is sequential (as
was checked before) and also that the zone is not offline nor read-only.
Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
Reviewed-by: Bart Van Assche <bvanassche@acm.org>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
---
block/blk-core.c | 7 +++++--
block/blk-zoned.c | 25 +++++++++++++++++++++++++
block/blk.h | 6 ++++++
3 files changed, 36 insertions(+), 2 deletions(-)
diff --git a/block/blk-core.c b/block/blk-core.c
index 196bccf27f58..13dc70e8f55d 100644
--- a/block/blk-core.c
+++ b/block/blk-core.c
@@ -901,8 +901,11 @@ void submit_bio_noacct(struct bio *bio)
case REQ_OP_ZONE_CLOSE:
case REQ_OP_ZONE_RESET:
case REQ_OP_ZONE_FINISH:
- /* Zone management operations require sequential zones. */
- if (!bdev_zone_is_seq(bio->bi_bdev, bio->bi_iter.bi_sector))
+ /*
+ * Zone management operations require sequential zones that are
+ * not offline nor read-only.
+ */
+ if (!bdev_zone_mgmt_allowed(bdev, bio->bi_iter.bi_sector))
goto end_io;
break;
case REQ_OP_ZONE_RESET_ALL:
diff --git a/block/blk-zoned.c b/block/blk-zoned.c
index ecaeacf3b287..c653cfc9823c 100644
--- a/block/blk-zoned.c
+++ b/block/blk-zoned.c
@@ -322,6 +322,31 @@ bool bdev_zone_is_seq(struct block_device *bdev, sector_t sector)
}
EXPORT_SYMBOL_GPL(bdev_zone_is_seq);
+/**
+ * bdev_zone_mgmt_allowed - check if management operations are allowed on a zone
+ * @bdev: block device to check
+ * @sector: sector number
+ *
+ * Check if the zone containing @sector on @bdev can be a target for a zone
+ * management operation, that is, if the zone is a sequential write required
+ * zone that is not offline nor read-only.
+ */
+bool bdev_zone_mgmt_allowed(struct block_device *bdev, sector_t sector)
+{
+ enum blk_zone_cond cond;
+ u8 zs;
+
+ if (!bdev_is_zoned(bdev))
+ return false;
+
+ zs = disk_zone_get_state(bdev->bd_disk, sector);
+ if (blk_zstate_is_conv(zs))
+ return false;
+
+ cond = blk_zstate_to_zone_cond(zs);
+ return !disk_zone_cond_is_offline_or_readonly(cond);
+}
+
/*
* Zone report arguments for block device drivers report_zones operation.
* @cb: report_zones_cb callback for each reported zone.
diff --git a/block/blk.h b/block/blk.h
index 50abfd932886..2cc03aa54c53 100644
--- a/block/blk.h
+++ b/block/blk.h
@@ -577,6 +577,7 @@ int blkdev_report_zones_ioctl(struct block_device *bdev, unsigned int cmd,
unsigned long arg);
int blkdev_zone_mgmt_ioctl(struct block_device *bdev, blk_mode_t mode,
unsigned int cmd, unsigned long arg);
+bool bdev_zone_mgmt_allowed(struct block_device *bdev, sector_t sector);
#else /* CONFIG_BLK_DEV_ZONED */
static inline void disk_init_zone_resources(struct gendisk *disk)
{
@@ -619,6 +620,11 @@ static inline int blkdev_zone_mgmt_ioctl(struct block_device *bdev,
{
return -ENOTTY;
}
+static inline bool bdev_zone_mgmt_allowed(struct block_device *bdev,
+ sector_t sector)
+{
+ return false;
+}
#endif /* CONFIG_BLK_DEV_ZONED */
struct block_device *bdev_alloc(struct gendisk *disk, u8 partno);
--
2.55.0
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH v7 15/16] block: allow read-only and offline conventional zones
2026-09-08 8:57 [PATCH v7 00/16] Improve handling of offline and read-only zones Damien Le Moal
` (13 preceding siblings ...)
2026-09-08 8:57 ` [PATCH v7 14/16] block: fail zone management operations to read-only and offline zones Damien Le Moal
@ 2026-09-08 8:57 ` Damien Le Moal
2026-09-08 8:57 ` [PATCH v7 16/16] block: simplify disk_zone_set_cond() Damien Le Moal
15 siblings, 0 replies; 28+ messages in thread
From: Damien Le Moal @ 2026-09-08 8:57 UTC (permalink / raw)
To: Jens Axboe, linux-block; +Cc: Christoph Hellwig
With SCSI and ATA SMR HDDs, the storage element depopulation feature can
change the condition of conventional zones to read-only (if a write head
is depopulated) or to offline (if a read head is depopulated).
However, the function blk_revalidate_zone_cond() currently does not allow
these conditions for conventional zones, causing a zone revalidation
failure.
Remove blk_revalidate_zone_cond() and move the zone condition checks for
conventional zones to blk_revalidate_conv_zone(), allowing the regular
BLK_ZONE_COND_NOT_WP condition as well as the BLK_ZONE_COND_OFFLINE and
BLK_ZONE_COND_READONLY conditions to match the conditions that can be
seen from a zoned device with depopulated storage elements.
The zone condition checks for sequential write required zones are moved
to blk_revalidate_seq_zone() without any change to the conditions allowed.
Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
Reviewed-by: Bart Van Assche <bvanassche@acm.org>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
---
block/blk-zoned.c | 66 +++++++++++++++++++----------------------------
1 file changed, 27 insertions(+), 39 deletions(-)
diff --git a/block/blk-zoned.c b/block/blk-zoned.c
index c653cfc9823c..2a8e573034b3 100644
--- a/block/blk-zoned.c
+++ b/block/blk-zoned.c
@@ -2370,57 +2370,32 @@ static int disk_revalidate_capacity(struct gendisk *disk,
return ret;
}
-static int blk_revalidate_zone_cond(struct blk_zone *zone, unsigned int idx,
+static int blk_revalidate_conv_zone(struct blk_zone *zone, unsigned int idx,
struct blk_revalidate_zone_args *args)
{
- enum blk_zone_cond cond = zone->cond;
- u8 flags = 0;
+ struct gendisk *disk = args->disk;
- /* Check that the zone condition is consistent with the zone type. */
- switch (cond) {
+ /* Check the zone condition. */
+ switch (zone->cond) {
case BLK_ZONE_COND_NOT_WP:
- if (zone->type != BLK_ZONE_TYPE_CONVENTIONAL)
- goto invalid_condition;
- flags = BLK_ZFLAG_CONV;
- break;
- case BLK_ZONE_COND_IMP_OPEN:
- case BLK_ZONE_COND_EXP_OPEN:
- case BLK_ZONE_COND_CLOSED:
- case BLK_ZONE_COND_EMPTY:
- case BLK_ZONE_COND_FULL:
case BLK_ZONE_COND_OFFLINE:
case BLK_ZONE_COND_READONLY:
- if (zone->type != BLK_ZONE_TYPE_SEQWRITE_REQ)
- goto invalid_condition;
break;
default:
- pr_warn("%s: Invalid zone condition 0x%X\n",
- args->disk->disk_name, cond);
+ pr_warn("%s: Invalid conv. zone condition 0x%X at sector %llu\n",
+ disk->disk_name, zone->cond, zone->start);
return -ENODEV;
}
- blk_zstate_set(args->zones_state, args->nr_zones, idx, cond, flags);
-
- return 0;
-
-invalid_condition:
- pr_warn("%s: Invalid zone condition 0x%x for type 0x%x\n",
- args->disk->disk_name, cond, zone->type);
-
- return -ENODEV;
-}
-
-static int blk_revalidate_conv_zone(struct blk_zone *zone, unsigned int idx,
- struct blk_revalidate_zone_args *args)
-{
- struct gendisk *disk = args->disk;
-
if (zone->capacity != zone->len) {
pr_warn("%s: Invalid conventional zone capacity\n",
disk->disk_name);
return -ENODEV;
}
+ blk_zstate_set(args->zones_state, args->nr_zones, idx,
+ zone->cond, BLK_ZFLAG_CONV);
+
if (disk_zone_is_last(disk, zone))
args->last_zone_capacity = zone->capacity;
@@ -2436,6 +2411,24 @@ static int blk_revalidate_seq_zone(struct blk_zone *zone, unsigned int idx,
struct blk_zone_wplug *zwplug;
unsigned int wp_offset;
+ /* Check the zone condition. */
+ switch (zone->cond) {
+ case BLK_ZONE_COND_IMP_OPEN:
+ case BLK_ZONE_COND_EXP_OPEN:
+ case BLK_ZONE_COND_CLOSED:
+ case BLK_ZONE_COND_EMPTY:
+ case BLK_ZONE_COND_FULL:
+ case BLK_ZONE_COND_OFFLINE:
+ case BLK_ZONE_COND_READONLY:
+ break;
+ default:
+ pr_warn("%s: Invalid seq. zone condition 0x%X at sector %llu\n",
+ disk->disk_name, zone->cond, zone->start);
+ return -ENODEV;
+ }
+
+ blk_zstate_set(args->zones_state, args->nr_zones, idx, zone->cond, 0);
+
/*
* Remember the capacity of the first sequential zone and check
* if it is constant for all zones, ignoring the last zone as it can be
@@ -2518,11 +2511,6 @@ static int blk_revalidate_zone_cb(struct blk_zone *zone, unsigned int idx,
return -ENODEV;
}
- /* Check zone condition */
- ret = blk_revalidate_zone_cond(zone, idx, args);
- if (ret)
- return ret;
-
/* Check zone type */
switch (zone->type) {
case BLK_ZONE_TYPE_CONVENTIONAL:
--
2.55.0
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH v7 16/16] block: simplify disk_zone_set_cond()
2026-09-08 8:57 [PATCH v7 00/16] Improve handling of offline and read-only zones Damien Le Moal
` (14 preceding siblings ...)
2026-09-08 8:57 ` [PATCH v7 15/16] block: allow read-only and offline conventional zones Damien Le Moal
@ 2026-09-08 8:57 ` Damien Le Moal
15 siblings, 0 replies; 28+ messages in thread
From: Damien Le Moal @ 2026-09-08 8:57 UTC (permalink / raw)
To: Jens Axboe, linux-block; +Cc: Christoph Hellwig
disk_zone_set_cond() is used to set a zone condition afer a reset, a
finish or a reset all operation. For a single zone reset or finish, we are
guaranteed that the target zone is a sequential one that is not offline
nor read-only (otherwise, the operation would have failed). For a reset
all operation, there is no point in calling this function for offline and
read-only zones since the condition checks in disk_zone_set_cond() will
result in nothing being done.
Simplify all this using disk_zone_is_offline_or_readonly() in
blk_zone_reset_all_bio_endio() to skip zones that are offline or
read-only. This change allows simplifying disk_zone_set_cond() by removing
the zone condition checks. This change is also consistent with the fact
that conventional zones can now have the offline or read-only condition.
Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
Reviewed-by: Bart Van Assche <bvanassche@acm.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
block/blk-zoned.c | 23 +++++------------------
1 file changed, 5 insertions(+), 18 deletions(-)
diff --git a/block/blk-zoned.c b/block/blk-zoned.c
index 2a8e573034b3..ec510d66dfe2 100644
--- a/block/blk-zoned.c
+++ b/block/blk-zoned.c
@@ -240,23 +240,9 @@ static void disk_zone_set_cond(struct gendisk *disk, sector_t sector,
rcu_read_lock();
zones_state = rcu_dereference(disk->zones_state);
- if (zones_state && zno < disk->nr_zones) {
- /*
- * The condition of a conventional, readonly and offline zones
- * never changes, so do nothing if the target zone is in one of
- * these conditions.
- */
- switch (zones_state[zno] & BLK_ZSTATE_COND_MASK) {
- case BLK_ZSTATE_NOT_WP:
- case BLK_ZSTATE_READONLY:
- case BLK_ZSTATE_OFFLINE:
- break;
- default:
- blk_zstate_set(zones_state, disk->nr_zones, zno, cond,
- blk_zstate_flags(zones_state[zno]));
- break;
- }
- }
+ if (likely(zones_state && zno < disk->nr_zones))
+ blk_zstate_set(zones_state, disk->nr_zones, zno, cond,
+ blk_zstate_flags(zones_state[zno]));
rcu_read_unlock();
}
@@ -1310,7 +1296,8 @@ static void blk_zone_reset_all_bio_endio(struct bio *bio)
/* Update the cached zone conditions. */
for (sector = 0; sector < get_capacity(disk);
sector += bdev_zone_sectors(bio->bi_bdev)) {
- if (disk_zone_is_offline_or_readonly(disk, sector))
+ if (!disk_zone_is_seq(disk, sector) ||
+ disk_zone_is_offline_or_readonly(disk, sector))
continue;
disk_zone_set_cond(disk, sector, BLK_ZONE_COND_EMPTY);
}
--
2.55.0
^ permalink raw reply related [flat|nested] 28+ messages in thread
* Re: [PATCH v7 02/16] block: improve capacity handling during zone revalidation
2026-09-08 8:57 ` [PATCH v7 02/16] block: improve capacity handling during zone revalidation Damien Le Moal
@ 2026-09-09 8:51 ` Hannes Reinecke
2026-09-10 5:20 ` Christoph Hellwig
1 sibling, 0 replies; 28+ messages in thread
From: Hannes Reinecke @ 2026-09-09 8:51 UTC (permalink / raw)
To: Damien Le Moal, Jens Axboe, linux-block; +Cc: Christoph Hellwig
On 9/8/26 10:57 AM, Damien Le Moal wrote:
> While executing blk_revalidate_disk_zones(), a disk capacity is looked at
> using get_capacity() several times: on entry to
> blk_revalidate_disk_zones(), when allocating zone revalidation arguments
> in disk_revalidate_zone_resources(), while validating zones in
> blk_revalidate_zone_cb() and one last time at the end of
> blk_revalidate_disk_zones() to check that all zones have been inspected.
>
> Since this is all done while passthrough commands can be issued, it is
> possible that a capacity change operation (e.g. the removal of a storage
> element on a SCSI or SATA disk) is concurrently executed, potentially
> resulting in an inconsistent or conflicting revalidation with potentially
> out-of-bound accesses to the zone condition array.
>
> Prevent issues by using get_capacity() once on entry to
> blk_revalidate_disk_zones(), remembering this capacity as a field of
> struct blk_revalidate_zone_args and using that field while revalidating
> zones. A final second call to get_capacity() is done at the end of
> blk_revalidate_disk_zones() to ensure that the disk capacity has not
> changed, thus revalidating the capacity (and number of zones) itself.
>
> Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
> ---
> block/blk-zoned.c | 19 +++++++++++++------
> 1 file changed, 13 insertions(+), 6 deletions(-)
>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
Cheers,
Hannes
--
Dr. Hannes Reinecke Kernel Storage Architect
hare@suse.de +49 911 74053 688
SUSE Software Solutions GmbH, Frankenstr. 146, 90461 Nürnberg
HRB 36809 (AG Nürnberg), GF: I. Totev, A. McDonald, W. Knoblich
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH v7 04/16] block: refactor disk_update_zone_resources()
2026-09-08 8:57 ` [PATCH v7 04/16] block: refactor disk_update_zone_resources() Damien Le Moal
@ 2026-09-09 9:09 ` Hannes Reinecke
2026-09-10 5:20 ` Christoph Hellwig
1 sibling, 0 replies; 28+ messages in thread
From: Hannes Reinecke @ 2026-09-09 9:09 UTC (permalink / raw)
To: Damien Le Moal, Jens Axboe, linux-block; +Cc: Christoph Hellwig
On 9/8/26 10:57 AM, Damien Le Moal wrote:
> Since the function disk_update_zone_resources() does a lot of checks
> beside updating a zoned disk limits and resources, rename this function
> to disk_revalidate_zone_resources().
>
> To keep all the checks together, move the capacity checks (including the
> check on the end sector of the last zone of the disk) in
> blk_revalidate_disk_zones() to this function.
>
> Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
> ---
> block/blk-zoned.c | 64 ++++++++++++++++++++++++++---------------------
> 1 file changed, 35 insertions(+), 29 deletions(-)
>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
Cheers,
Hannes
--
Dr. Hannes Reinecke Kernel Storage Architect
hare@suse.de +49 911 74053 688
SUSE Software Solutions GmbH, Frankenstr. 146, 90461 Nürnberg
HRB 36809 (AG Nürnberg), GF: I. Totev, A. McDonald, W. Knoblich
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH v7 07/16] block: improve blkdev_get_zone_info()
2026-09-08 8:57 ` [PATCH v7 07/16] block: improve blkdev_get_zone_info() Damien Le Moal
@ 2026-09-09 9:11 ` Hannes Reinecke
2026-09-10 5:21 ` Christoph Hellwig
1 sibling, 0 replies; 28+ messages in thread
From: Hannes Reinecke @ 2026-09-09 9:11 UTC (permalink / raw)
To: Damien Le Moal, Jens Axboe, linux-block; +Cc: Christoph Hellwig
On 9/8/26 10:57 AM, Damien Le Moal wrote:
> Add a zone state array bound check to ensure that like
> disk_zone_get_state(), we never attempt to access the zone state array
> beyond its size.
>
> Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
> ---
> block/blk-zoned.c | 12 ++++++++----
> 1 file changed, 8 insertions(+), 4 deletions(-)
>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
Cheers,
Hannes
--
Dr. Hannes Reinecke Kernel Storage Architect
hare@suse.de +49 911 74053 688
SUSE Software Solutions GmbH, Frankenstr. 146, 90461 Nürnberg
HRB 36809 (AG Nürnberg), GF: I. Totev, A. McDonald, W. Knoblich
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH v7 09/16] block: serialize zone revalidation
2026-09-08 8:57 ` [PATCH v7 09/16] block: serialize zone revalidation Damien Le Moal
@ 2026-09-09 9:20 ` Hannes Reinecke
2026-09-10 5:21 ` Christoph Hellwig
1 sibling, 0 replies; 28+ messages in thread
From: Hannes Reinecke @ 2026-09-09 9:20 UTC (permalink / raw)
To: Damien Le Moal, Jens Axboe, linux-block; +Cc: Christoph Hellwig
On 9/8/26 10:57 AM, Damien Le Moal wrote:
> The zone related fields of struct gendisk can be modified by
> blk_revalidate_disk_zones() either on the first scan of the disk, or
> during user triggered scans or device revalidation, if some
> characteristics of the disk has changed (e.g. the disk capacity). Changes
> to these fields are always done with the disk request queue frozen so that
> BIO processing does not see any inconsistent state of the zones. This
> implies a contract that reading these zone related fields must always be
> done while holding a usage count on the request queue of the disk.
>
> However, increasing the usage count of the disk request queue cannot be
> done from the context of blk_revalidate_disk_zones() itself, as that would
> prevent freezing the disk queue and result in a deadlock. This prevents
> blk_revalidate_disk_zones() from consulting the zone related fields of
> struct gendisk to detect, for instance, a change in the number of zones of
> the disk. For such case, we want to detect the change, take appropriate
> measures and revalidate exclusively revalidate the zones to avoid
> concurrent revalidation calls to see the same change while corrections are
> already on-going.
>
> A simple solution to avoid this issue is to introduce a mutex to serialize
> calls to blk_revalidate_disk_zones() and ensure only a single context at a
> time can modify the zone related fields of a gendisk. In preparation for
> handling disk capacity revalidation in blk_revalidate_disk_zones(), do so
> with the mutex zone_revalidate_mutex. This mutex is initialized in
> disk_init_zone_resources(), destroyed in disk_release_zone_resources() and
> taken and released only in blk_revalidate_disk_zones() to serialize the
> execution of this function.
>
> Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
> ---
> block/blk-zoned.c | 17 +++++++++++++++--
> include/linux/blkdev.h | 9 ++++++---
> 2 files changed, 21 insertions(+), 5 deletions(-)
>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
Cheers,
Hannes
--
Dr. Hannes Reinecke Kernel Storage Architect
hare@suse.de +49 911 74053 688
SUSE Software Solutions GmbH, Frankenstr. 146, 90461 Nürnberg
HRB 36809 (AG Nürnberg), GF: I. Totev, A. McDonald, W. Knoblich
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH v7 11/16] block: retry zone revalidation on capacity change
2026-09-08 8:57 ` [PATCH v7 11/16] block: retry zone revalidation on capacity change Damien Le Moal
@ 2026-09-09 9:26 ` Hannes Reinecke
2026-09-10 5:24 ` Christoph Hellwig
1 sibling, 0 replies; 28+ messages in thread
From: Hannes Reinecke @ 2026-09-09 9:26 UTC (permalink / raw)
To: Damien Le Moal, Jens Axboe, linux-block; +Cc: Christoph Hellwig
On 9/8/26 10:57 AM, Damien Le Moal wrote:
> When disk_revalidate_zone_resources() detects a capacity change, -ENODEV
> is returned, failing the disk revalidation. However, since a capacity
> change may happen due to a storage element removal being executed
> concurrently to blk_revalidate_disk_zones(), we can simply retry the
> revalidation to capture the new zone state with the new capacity without
> failing the revalidation.
>
> Retrying the revalidation is driven by disk_revalidate_zone_resources()
> returning -EAGAIN when a new valid capacity is detected. And to avoid
> getting stuck in an infinite loop revalidating zones, retries are limited
> to 2.
>
> Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
> ---
> block/blk-zoned.c | 20 +++++++++++++++++---
> 1 file changed, 17 insertions(+), 3 deletions(-)
>
... and, or course, messing up anything accessing the disk.
But that's probably intended at this point.
Reviewed-by: Hannes Reinecke <hare@kernel.org>
Cheers,
Hannes
--
Dr. Hannes Reinecke Kernel Storage Architect
hare@suse.de +49 911 74053 688
SUSE Software Solutions GmbH, Frankenstr. 146, 90461 Nürnberg
HRB 36809 (AG Nürnberg), GF: I. Totev, A. McDonald, W. Knoblich
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH v7 02/16] block: improve capacity handling during zone revalidation
2026-09-08 8:57 ` [PATCH v7 02/16] block: improve capacity handling during zone revalidation Damien Le Moal
2026-09-09 8:51 ` Hannes Reinecke
@ 2026-09-10 5:20 ` Christoph Hellwig
1 sibling, 0 replies; 28+ messages in thread
From: Christoph Hellwig @ 2026-09-10 5:20 UTC (permalink / raw)
To: Damien Le Moal; +Cc: Jens Axboe, linux-block
Looks good:
Reviewed-by: Christoph Hellwig <hch@lst.de>
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH v7 04/16] block: refactor disk_update_zone_resources()
2026-09-08 8:57 ` [PATCH v7 04/16] block: refactor disk_update_zone_resources() Damien Le Moal
2026-09-09 9:09 ` Hannes Reinecke
@ 2026-09-10 5:20 ` Christoph Hellwig
1 sibling, 0 replies; 28+ messages in thread
From: Christoph Hellwig @ 2026-09-10 5:20 UTC (permalink / raw)
To: Damien Le Moal; +Cc: Jens Axboe, linux-block, Christoph Hellwig
Looks good:
Reviewed-by: Christoph Hellwig <hch@lst.de>
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH v7 07/16] block: improve blkdev_get_zone_info()
2026-09-08 8:57 ` [PATCH v7 07/16] block: improve blkdev_get_zone_info() Damien Le Moal
2026-09-09 9:11 ` Hannes Reinecke
@ 2026-09-10 5:21 ` Christoph Hellwig
1 sibling, 0 replies; 28+ messages in thread
From: Christoph Hellwig @ 2026-09-10 5:21 UTC (permalink / raw)
To: Damien Le Moal; +Cc: Jens Axboe, linux-block, Christoph Hellwig
Looks good:
Reviewed-by: Christoph Hellwig <hch@lst.de>
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH v7 09/16] block: serialize zone revalidation
2026-09-08 8:57 ` [PATCH v7 09/16] block: serialize zone revalidation Damien Le Moal
2026-09-09 9:20 ` Hannes Reinecke
@ 2026-09-10 5:21 ` Christoph Hellwig
1 sibling, 0 replies; 28+ messages in thread
From: Christoph Hellwig @ 2026-09-10 5:21 UTC (permalink / raw)
To: Damien Le Moal; +Cc: Jens Axboe, linux-block, Christoph Hellwig
Looks good:
Reviewed-by: Christoph Hellwig <hch@lst.de>
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH v7 11/16] block: retry zone revalidation on capacity change
2026-09-08 8:57 ` [PATCH v7 11/16] block: retry zone revalidation on capacity change Damien Le Moal
2026-09-09 9:26 ` Hannes Reinecke
@ 2026-09-10 5:24 ` Christoph Hellwig
2026-09-10 5:26 ` Damien Le Moal
1 sibling, 1 reply; 28+ messages in thread
From: Christoph Hellwig @ 2026-09-10 5:24 UTC (permalink / raw)
To: Damien Le Moal; +Cc: Jens Axboe, linux-block, Christoph Hellwig
On Tue, Sep 08, 2026 at 05:57:40PM +0900, Damien Le Moal wrote:
> When disk_revalidate_zone_resources() detects a capacity change, -ENODEV
> is returned, failing the disk revalidation. However, since a capacity
> change may happen due to a storage element removal being executed
> concurrently to blk_revalidate_disk_zones(), we can simply retry the
> revalidation to capture the new zone state with the new capacity without
> failing the revalidation.
>
> Retrying the revalidation is driven by disk_revalidate_zone_resources()
> returning -EAGAIN when a new valid capacity is detected. And to avoid
> getting stuck in an infinite loop revalidating zones, retries are limited
> to 2.
I thought our random pulled out the air retyr numbers was 3 :)
Either way, this looks fine:
Reviewed-by: Christoph Hellwig <hch@lst.de>
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH v7 11/16] block: retry zone revalidation on capacity change
2026-09-10 5:24 ` Christoph Hellwig
@ 2026-09-10 5:26 ` Damien Le Moal
0 siblings, 0 replies; 28+ messages in thread
From: Damien Le Moal @ 2026-09-10 5:26 UTC (permalink / raw)
To: Christoph Hellwig; +Cc: Jens Axboe, linux-block
On 9/10/26 14:24, Christoph Hellwig wrote:
> On Tue, Sep 08, 2026 at 05:57:40PM +0900, Damien Le Moal wrote:
>> When disk_revalidate_zone_resources() detects a capacity change, -ENODEV
>> is returned, failing the disk revalidation. However, since a capacity
>> change may happen due to a storage element removal being executed
>> concurrently to blk_revalidate_disk_zones(), we can simply retry the
>> revalidation to capture the new zone state with the new capacity without
>> failing the revalidation.
>>
>> Retrying the revalidation is driven by disk_revalidate_zone_resources()
>> returning -EAGAIN when a new valid capacity is detected. And to avoid
>> getting stuck in an infinite loop revalidating zones, retries are limited
>> to 2.
>
> I thought our random pulled out the air retyr numbers was 3 :)
Retry=2 leads to 3 tries since the first one is a try and not a RE-try :)
>
> Either way, this looks fine:
>
> Reviewed-by: Christoph Hellwig <hch@lst.de>
>
--
Damien Le Moal
Western Digital Research
^ permalink raw reply [flat|nested] 28+ messages in thread
end of thread, other threads:[~2026-09-10 5:26 UTC | newest]
Thread overview: 28+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-08 8:57 [PATCH v7 00/16] Improve handling of offline and read-only zones Damien Le Moal
2026-09-08 8:57 ` [PATCH v7 01/16] block: remove disk_free_zone_resources() Damien Le Moal
2026-09-08 8:57 ` [PATCH v7 02/16] block: improve capacity handling during zone revalidation Damien Le Moal
2026-09-09 8:51 ` Hannes Reinecke
2026-09-10 5:20 ` Christoph Hellwig
2026-09-08 8:57 ` [PATCH v7 03/16] block: refactor disk_revalidate_zone_resources() Damien Le Moal
2026-09-08 8:57 ` [PATCH v7 04/16] block: refactor disk_update_zone_resources() Damien Le Moal
2026-09-09 9:09 ` Hannes Reinecke
2026-09-10 5:20 ` Christoph Hellwig
2026-09-08 8:57 ` [PATCH v7 05/16] block: remember a zone type regardless of its condition Damien Le Moal
2026-09-08 8:57 ` [PATCH v7 06/16] block: refactor bdev_zone_is_seq() Damien Le Moal
2026-09-08 8:57 ` [PATCH v7 07/16] block: improve blkdev_get_zone_info() Damien Le Moal
2026-09-09 9:11 ` Hannes Reinecke
2026-09-10 5:21 ` Christoph Hellwig
2026-09-08 8:57 ` [PATCH v7 08/16] block: introduce disk_for_all_zone_wplugs() Damien Le Moal
2026-09-08 8:57 ` [PATCH v7 09/16] block: serialize zone revalidation Damien Le Moal
2026-09-09 9:20 ` Hannes Reinecke
2026-09-10 5:21 ` Christoph Hellwig
2026-09-08 8:57 ` [PATCH v7 10/16] block: drop all zone write plugs on capacity changes Damien Le Moal
2026-09-08 8:57 ` [PATCH v7 11/16] block: retry zone revalidation on capacity change Damien Le Moal
2026-09-09 9:26 ` Hannes Reinecke
2026-09-10 5:24 ` Christoph Hellwig
2026-09-10 5:26 ` Damien Le Moal
2026-09-08 8:57 ` [PATCH v7 12/16] block: propagate readonly and offline conditions to zone write plugs Damien Le Moal
2026-09-08 8:57 ` [PATCH v7 13/16] block: always treat offline and read-only zones as dead Damien Le Moal
2026-09-08 8:57 ` [PATCH v7 14/16] block: fail zone management operations to read-only and offline zones Damien Le Moal
2026-09-08 8:57 ` [PATCH v7 15/16] block: allow read-only and offline conventional zones Damien Le Moal
2026-09-08 8:57 ` [PATCH v7 16/16] block: simplify disk_zone_set_cond() Damien Le Moal
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).