[PATCH 09/14] block: always treat offline and read-only zones as dead
Damien Le Moal <[email protected]> Wed, 5 Aug 2026 11:27:14 +0900
| Newsgroups | org.kernel.vger.linux-block |
|---|---|
| Message-ID | <[email protected]> |
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 <[email protected]> --- block/blk-zoned.c | 113 +++++++++++++++++++++++++--------------------- 1 file changed, 61 insertions(+), 52 deletions(-) diff --git a/block/blk-zoned.c b/block/blk-zoned.c index 1560b000fb08..069251171154 100644 --- a/block/blk-zoned.c +++ b/block/blk-zoned.c @@ -744,6 +744,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 @@ -761,6 +808,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; @@ -844,53 +897,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. */ @@ -967,8 +973,10 @@ static unsigned int disk_zone_wplug_sync_state(struct gendisk *disk, spin_lock_irqsave(&zwplug->lock, flags); if (zwplug->flags & BLK_ZONE_WPLUG_NEED_WP_UPDATE) disk_zone_wplug_set_wp_offset(disk, zwplug, wp_offset); - if (disk_zone_cond_is_offline_or_readonly(zone->cond)) + if (disk_zone_cond_is_offline_or_readonly(zone->cond)) { zwplug->cond = zone->cond; + disk_mark_zone_wplug_dead(zwplug); + } spin_unlock_irqrestore(&zwplug->lock, flags); disk_put_zone_wplug(zwplug); } @@ -1521,11 +1529,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