Skip to content

Commit cae0056

Browse files
damien-lemoalaxboe
authored andcommitted
block: Use a zone write plug BIO work for REQ_NOWAIT BIOs
For zoned block devices, a write BIO issued to a zone that has no on-going writes will be prepared for execution and allowed to execute immediately by blk_zone_wplug_handle_write() (called from blk_zone_plug_bio()). However, if this BIO specifies REQ_NOWAIT, the allocation of a request for its execution in blk_mq_submit_bio() may fail after blk_zone_plug_bio() completed, marking the target zone of the BIO as plugged. When this BIO is retried later on, it will be blocked as the zone write plug of the target zone is in a plugged state without any on-going write operation (completion of write operations trigger unplugging of the next write BIOs for a zone). This leads to a BIO that is stuck in a zone write plug and never completes, which results in various issues such as hung tasks. Avoid this problem by always executing REQ_NOWAIT write BIOs using the BIO work of a zone write plug. This ensure that we never block the BIO issuer and can thus safely ignore the REQ_NOWAIT flag when executing the BIO from the zone write plug BIO work. Since such BIO may be the first write BIO issued to a zone with no on-going write, modify disk_zone_wplug_add_bio() to schedule the zone write plug BIO work if the write plug is not already marked with the BLK_ZONE_WPLUG_PLUGGED flag. This scheduling is otherwise not necessary as the completion of the on-going write for the zone will schedule the execution of the next plugged BIOs. blk_zone_wplug_handle_write() is also fixed to better handle zone write plug allocation failures for REQ_NOWAIT BIOs by failing a write BIO using bio_wouldblock_error() instead of bio_io_error(). Reported-by: Bart Van Assche <[email protected]> Fixes: dd291d7 ("block: Introduce zone write plugging") Cc: [email protected] Signed-off-by: Damien Le Moal <[email protected]> Reviewed-by: Christoph Hellwig <[email protected]> Reviewed-by: Martin K. Petersen <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Jens Axboe <[email protected]>
1 parent 22465bb commit cae0056

File tree

1 file changed

+42
-20
lines changed

1 file changed

+42
-20
lines changed

block/blk-zoned.c

Lines changed: 42 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -746,9 +746,25 @@ static bool blk_zone_wplug_handle_reset_all(struct bio *bio)
746746
return false;
747747
}
748748

749-
static inline void blk_zone_wplug_add_bio(struct blk_zone_wplug *zwplug,
750-
struct bio *bio, unsigned int nr_segs)
749+
static void disk_zone_wplug_schedule_bio_work(struct gendisk *disk,
750+
struct blk_zone_wplug *zwplug)
751+
{
752+
/*
753+
* Take a reference on the zone write plug and schedule the submission
754+
* of the next plugged BIO. blk_zone_wplug_bio_work() will release the
755+
* reference we take here.
756+
*/
757+
WARN_ON_ONCE(!(zwplug->flags & BLK_ZONE_WPLUG_PLUGGED));
758+
refcount_inc(&zwplug->ref);
759+
queue_work(disk->zone_wplugs_wq, &zwplug->bio_work);
760+
}
761+
762+
static inline void disk_zone_wplug_add_bio(struct gendisk *disk,
763+
struct blk_zone_wplug *zwplug,
764+
struct bio *bio, unsigned int nr_segs)
751765
{
766+
bool schedule_bio_work = false;
767+
752768
/*
753769
* Grab an extra reference on the BIO request queue usage counter.
754770
* This reference will be reused to submit a request for the BIO for
@@ -764,6 +780,16 @@ static inline void blk_zone_wplug_add_bio(struct blk_zone_wplug *zwplug,
764780
*/
765781
bio_clear_polled(bio);
766782

783+
/*
784+
* REQ_NOWAIT BIOs are always handled using the zone write plug BIO
785+
* work, which can block. So clear the REQ_NOWAIT flag and schedule the
786+
* work if this is the first BIO we are plugging.
787+
*/
788+
if (bio->bi_opf & REQ_NOWAIT) {
789+
schedule_bio_work = !(zwplug->flags & BLK_ZONE_WPLUG_PLUGGED);
790+
bio->bi_opf &= ~REQ_NOWAIT;
791+
}
792+
767793
/*
768794
* Reuse the poll cookie field to store the number of segments when
769795
* split to the hardware limits.
@@ -777,6 +803,11 @@ static inline void blk_zone_wplug_add_bio(struct blk_zone_wplug *zwplug,
777803
* at the tail of the list to preserve the sequential write order.
778804
*/
779805
bio_list_add(&zwplug->bio_list, bio);
806+
807+
zwplug->flags |= BLK_ZONE_WPLUG_PLUGGED;
808+
809+
if (schedule_bio_work)
810+
disk_zone_wplug_schedule_bio_work(disk, zwplug);
780811
}
781812

782813
/*
@@ -970,7 +1001,10 @@ static bool blk_zone_wplug_handle_write(struct bio *bio, unsigned int nr_segs)
9701001

9711002
zwplug = disk_get_and_lock_zone_wplug(disk, sector, gfp_mask, &flags);
9721003
if (!zwplug) {
973-
bio_io_error(bio);
1004+
if (bio->bi_opf & REQ_NOWAIT)
1005+
bio_wouldblock_error(bio);
1006+
else
1007+
bio_io_error(bio);
9741008
return true;
9751009
}
9761010

@@ -979,9 +1013,11 @@ static bool blk_zone_wplug_handle_write(struct bio *bio, unsigned int nr_segs)
9791013

9801014
/*
9811015
* If the zone is already plugged or has a pending error, add the BIO
982-
* to the plug BIO list. Otherwise, plug and let the BIO execute.
1016+
* to the plug BIO list. Do the same for REQ_NOWAIT BIOs to ensure that
1017+
* we will not see a BLK_STS_AGAIN failure if we let the BIO execute.
1018+
* Otherwise, plug and let the BIO execute.
9831019
*/
984-
if (zwplug->flags & BLK_ZONE_WPLUG_BUSY)
1020+
if (zwplug->flags & BLK_ZONE_WPLUG_BUSY || (bio->bi_opf & REQ_NOWAIT))
9851021
goto plug;
9861022

9871023
/*
@@ -998,8 +1034,7 @@ static bool blk_zone_wplug_handle_write(struct bio *bio, unsigned int nr_segs)
9981034
return false;
9991035

10001036
plug:
1001-
zwplug->flags |= BLK_ZONE_WPLUG_PLUGGED;
1002-
blk_zone_wplug_add_bio(zwplug, bio, nr_segs);
1037+
disk_zone_wplug_add_bio(disk, zwplug, bio, nr_segs);
10031038

10041039
spin_unlock_irqrestore(&zwplug->lock, flags);
10051040

@@ -1083,19 +1118,6 @@ bool blk_zone_plug_bio(struct bio *bio, unsigned int nr_segs)
10831118
}
10841119
EXPORT_SYMBOL_GPL(blk_zone_plug_bio);
10851120

1086-
static void disk_zone_wplug_schedule_bio_work(struct gendisk *disk,
1087-
struct blk_zone_wplug *zwplug)
1088-
{
1089-
/*
1090-
* Take a reference on the zone write plug and schedule the submission
1091-
* of the next plugged BIO. blk_zone_wplug_bio_work() will release the
1092-
* reference we take here.
1093-
*/
1094-
WARN_ON_ONCE(!(zwplug->flags & BLK_ZONE_WPLUG_PLUGGED));
1095-
refcount_inc(&zwplug->ref);
1096-
queue_work(disk->zone_wplugs_wq, &zwplug->bio_work);
1097-
}
1098-
10991121
static void disk_zone_wplug_unplug_bio(struct gendisk *disk,
11001122
struct blk_zone_wplug *zwplug)
11011123
{

0 commit comments

Comments
 (0)