Skip to content

Commit 2cd2003

Browse files
damien-lemoalgregkh
authored andcommitted
block: handle zone management operations completions
[ Upstream commit efae226 ] The functions blk_zone_wplug_handle_reset_or_finish() and blk_zone_wplug_handle_reset_all() both modify the zone write pointer offset of zone write plugs that are the target of a reset, reset all or finish zone management operation. However, these functions do this modification before the BIO is executed. So if the zone operation fails, the modified zone write pointer offsets become invalid. Avoid this by modifying the zone write pointer offset of a zone write plug that is the target of a zone management operation when the operation completes. To do so, modify blk_zone_bio_endio() to call the new function blk_zone_mgmt_bio_endio() which in turn calls the functions blk_zone_reset_all_bio_endio(), blk_zone_reset_bio_endio() or blk_zone_finish_bio_endio() depending on the operation of the completed BIO, to modify a zone write plug write pointer offset accordingly. These functions are called only if the BIO execution was successful. Fixes: dd291d7 ("block: Introduce zone write plugging") Cc: stable@vger.kernel.org Signed-off-by: Damien Le Moal <dlemoal@kernel.org> Reviewed-by: Christoph Hellwig <hch@lst.de> Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com> Reviewed-by: Chaitanya Kulkarni <kch@nvidia.com> Reviewed-by: Hannes Reinecke <hare@suse.de> Reviewed-by: Martin K. Petersen <martin.petersen@oracle.com> Signed-off-by: Jens Axboe <axboe@kernel.dk> [ adapted bdev_zone_is_seq() check to disk_zone_is_conv() ] Signed-off-by: Sasha Levin <sashal@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 1fe39f5 commit 2cd2003

2 files changed

Lines changed: 106 additions & 49 deletions

File tree

block/blk-zoned.c

Lines changed: 92 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,11 @@ struct blk_zone_wplug {
7373
struct gendisk *disk;
7474
};
7575

76+
static inline unsigned int disk_zone_wplugs_hash_size(struct gendisk *disk)
77+
{
78+
return 1U << disk->zone_wplugs_hash_bits;
79+
}
80+
7681
/*
7782
* Zone write plug flags bits:
7883
* - BLK_ZONE_WPLUG_PLUGGED: Indicates that the zone write plug is plugged,
@@ -712,71 +717,91 @@ static int disk_zone_sync_wp_offset(struct gendisk *disk, sector_t sector)
712717
disk_report_zones_cb, &args);
713718
}
714719

715-
static bool blk_zone_wplug_handle_reset_or_finish(struct bio *bio,
716-
unsigned int wp_offset)
720+
static void blk_zone_reset_bio_endio(struct bio *bio)
717721
{
718722
struct gendisk *disk = bio->bi_bdev->bd_disk;
719-
sector_t sector = bio->bi_iter.bi_sector;
720723
struct blk_zone_wplug *zwplug;
721-
unsigned long flags;
722-
723-
/* Conventional zones cannot be reset nor finished. */
724-
if (disk_zone_is_conv(disk, sector)) {
725-
bio_io_error(bio);
726-
return true;
727-
}
728724

729725
/*
730-
* No-wait reset or finish BIOs do not make much sense as the callers
731-
* issue these as blocking operations in most cases. To avoid issues
732-
* the BIO execution potentially failing with BLK_STS_AGAIN, warn about
733-
* REQ_NOWAIT being set and ignore that flag.
734-
*/
735-
if (WARN_ON_ONCE(bio->bi_opf & REQ_NOWAIT))
736-
bio->bi_opf &= ~REQ_NOWAIT;
737-
738-
/*
739-
* If we have a zone write plug, set its write pointer offset to 0
740-
* (reset case) or to the zone size (finish case). This will abort all
741-
* BIOs plugged for the target zone. It is fine as resetting or
742-
* finishing zones while writes are still in-flight will result in the
726+
* If we have a zone write plug, set its write pointer offset to 0.
727+
* This will abort all BIOs plugged for the target zone. It is fine as
728+
* resetting zones while writes are still in-flight will result in the
743729
* writes failing anyway.
744730
*/
745-
zwplug = disk_get_zone_wplug(disk, sector);
731+
zwplug = disk_get_zone_wplug(disk, bio->bi_iter.bi_sector);
746732
if (zwplug) {
733+
unsigned long flags;
734+
747735
spin_lock_irqsave(&zwplug->lock, flags);
748-
disk_zone_wplug_set_wp_offset(disk, zwplug, wp_offset);
736+
disk_zone_wplug_set_wp_offset(disk, zwplug, 0);
749737
spin_unlock_irqrestore(&zwplug->lock, flags);
750738
disk_put_zone_wplug(zwplug);
751739
}
752-
753-
return false;
754740
}
755741

756-
static bool blk_zone_wplug_handle_reset_all(struct bio *bio)
742+
static void blk_zone_reset_all_bio_endio(struct bio *bio)
757743
{
758744
struct gendisk *disk = bio->bi_bdev->bd_disk;
759745
struct blk_zone_wplug *zwplug;
760746
unsigned long flags;
761-
sector_t sector;
747+
unsigned int i;
762748

763-
/*
764-
* Set the write pointer offset of all zone write plugs to 0. This will
765-
* abort all plugged BIOs. It is fine as resetting zones while writes
766-
* are still in-flight will result in the writes failing anyway.
767-
*/
768-
for (sector = 0; sector < get_capacity(disk);
769-
sector += disk->queue->limits.chunk_sectors) {
770-
zwplug = disk_get_zone_wplug(disk, sector);
771-
if (zwplug) {
749+
/* Update the condition of all zone write plugs. */
750+
rcu_read_lock();
751+
for (i = 0; i < disk_zone_wplugs_hash_size(disk); i++) {
752+
hlist_for_each_entry_rcu(zwplug, &disk->zone_wplugs_hash[i],
753+
node) {
772754
spin_lock_irqsave(&zwplug->lock, flags);
773755
disk_zone_wplug_set_wp_offset(disk, zwplug, 0);
774756
spin_unlock_irqrestore(&zwplug->lock, flags);
775-
disk_put_zone_wplug(zwplug);
776757
}
777758
}
759+
rcu_read_unlock();
760+
}
778761

779-
return false;
762+
static void blk_zone_finish_bio_endio(struct bio *bio)
763+
{
764+
struct block_device *bdev = bio->bi_bdev;
765+
struct gendisk *disk = bdev->bd_disk;
766+
struct blk_zone_wplug *zwplug;
767+
768+
/*
769+
* If we have a zone write plug, set its write pointer offset to the
770+
* zone size. This will abort all BIOs plugged for the target zone. It
771+
* is fine as resetting zones while writes are still in-flight will
772+
* result in the writes failing anyway.
773+
*/
774+
zwplug = disk_get_zone_wplug(disk, bio->bi_iter.bi_sector);
775+
if (zwplug) {
776+
unsigned long flags;
777+
778+
spin_lock_irqsave(&zwplug->lock, flags);
779+
disk_zone_wplug_set_wp_offset(disk, zwplug,
780+
bdev_zone_sectors(bdev));
781+
spin_unlock_irqrestore(&zwplug->lock, flags);
782+
disk_put_zone_wplug(zwplug);
783+
}
784+
}
785+
786+
void blk_zone_mgmt_bio_endio(struct bio *bio)
787+
{
788+
/* If the BIO failed, we have nothing to do. */
789+
if (bio->bi_status != BLK_STS_OK)
790+
return;
791+
792+
switch (bio_op(bio)) {
793+
case REQ_OP_ZONE_RESET:
794+
blk_zone_reset_bio_endio(bio);
795+
return;
796+
case REQ_OP_ZONE_RESET_ALL:
797+
blk_zone_reset_all_bio_endio(bio);
798+
return;
799+
case REQ_OP_ZONE_FINISH:
800+
blk_zone_finish_bio_endio(bio);
801+
return;
802+
default:
803+
return;
804+
}
780805
}
781806

782807
static void disk_zone_wplug_schedule_bio_work(struct gendisk *disk,
@@ -1119,6 +1144,32 @@ static void blk_zone_wplug_handle_native_zone_append(struct bio *bio)
11191144
disk_put_zone_wplug(zwplug);
11201145
}
11211146

1147+
static bool blk_zone_wplug_handle_zone_mgmt(struct bio *bio)
1148+
{
1149+
struct gendisk *disk = bio->bi_bdev->bd_disk;
1150+
1151+
if (bio_op(bio) != REQ_OP_ZONE_RESET_ALL &&
1152+
disk_zone_is_conv(disk, bio->bi_iter.bi_sector)) {
1153+
/*
1154+
* Zone reset and zone finish operations do not apply to
1155+
* conventional zones.
1156+
*/
1157+
bio_io_error(bio);
1158+
return true;
1159+
}
1160+
1161+
/*
1162+
* No-wait zone management BIOs do not make much sense as the callers
1163+
* issue these as blocking operations in most cases. To avoid issues
1164+
* with the BIO execution potentially failing with BLK_STS_AGAIN, warn
1165+
* about REQ_NOWAIT being set and ignore that flag.
1166+
*/
1167+
if (WARN_ON_ONCE(bio->bi_opf & REQ_NOWAIT))
1168+
bio->bi_opf &= ~REQ_NOWAIT;
1169+
1170+
return false;
1171+
}
1172+
11221173
/**
11231174
* blk_zone_plug_bio - Handle a zone write BIO with zone write plugging
11241175
* @bio: The BIO being submitted
@@ -1166,12 +1217,9 @@ bool blk_zone_plug_bio(struct bio *bio, unsigned int nr_segs)
11661217
case REQ_OP_WRITE_ZEROES:
11671218
return blk_zone_wplug_handle_write(bio, nr_segs);
11681219
case REQ_OP_ZONE_RESET:
1169-
return blk_zone_wplug_handle_reset_or_finish(bio, 0);
11701220
case REQ_OP_ZONE_FINISH:
1171-
return blk_zone_wplug_handle_reset_or_finish(bio,
1172-
bdev_zone_sectors(bdev));
11731221
case REQ_OP_ZONE_RESET_ALL:
1174-
return blk_zone_wplug_handle_reset_all(bio);
1222+
return blk_zone_wplug_handle_zone_mgmt(bio);
11751223
default:
11761224
return false;
11771225
}
@@ -1328,11 +1376,6 @@ static void blk_zone_wplug_bio_work(struct work_struct *work)
13281376
disk_put_zone_wplug(zwplug);
13291377
}
13301378

1331-
static inline unsigned int disk_zone_wplugs_hash_size(struct gendisk *disk)
1332-
{
1333-
return 1U << disk->zone_wplugs_hash_bits;
1334-
}
1335-
13361379
void disk_init_zone_resources(struct gendisk *disk)
13371380
{
13381381
spin_lock_init(&disk->zone_wplugs_lock);

block/blk.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -486,9 +486,23 @@ static inline void blk_zone_update_request_bio(struct request *rq,
486486
bio_flagged(bio, BIO_EMULATES_ZONE_APPEND))
487487
bio->bi_iter.bi_sector = rq->__sector;
488488
}
489+
void blk_zone_mgmt_bio_endio(struct bio *bio);
489490
void blk_zone_write_plug_bio_endio(struct bio *bio);
490491
static inline void blk_zone_bio_endio(struct bio *bio)
491492
{
493+
/*
494+
* Zone management BIOs may impact zone write plugs (e.g. a zone reset
495+
* changes a zone write plug zone write pointer offset), but these
496+
* operation do not go through zone write plugging as they may operate
497+
* on zones that do not have a zone write
498+
* plug. blk_zone_mgmt_bio_endio() handles the potential changes to zone
499+
* write plugs that are present.
500+
*/
501+
if (op_is_zone_mgmt(bio_op(bio))) {
502+
blk_zone_mgmt_bio_endio(bio);
503+
return;
504+
}
505+
492506
/*
493507
* For write BIOs to zoned devices, signal the completion of the BIO so
494508
* that the next write BIO can be submitted by zone write plugging.

0 commit comments

Comments
 (0)