aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--block/blk-zoned.c39
1 files changed, 31 insertions, 8 deletions
diff --git a/block/blk-zoned.c b/block/blk-zoned.c
index 2f61ba56dad2..1e5f362f0409 100644
--- a/block/blk-zoned.c
+++ b/block/blk-zoned.c
@@ -520,10 +520,28 @@ static inline void disk_put_zone_wplug(struct blk_zone_wplug *zwplug)
static inline bool disk_should_remove_zone_wplug(struct gendisk *disk,
struct blk_zone_wplug *zwplug)
{
- /* If the zone is still busy, the plug cannot be removed. */
+ /* If the zone write plug was already removed, we are done. */
+ if (zwplug->flags & BLK_ZONE_WPLUG_UNHASHED)
+ return false;
+
+ /* If the zone write plug is still busy, it cannot be removed. */
if (zwplug->flags & BLK_ZONE_WPLUG_BUSY)
return false;
+ /*
+ * Completions of BIOs with blk_zone_write_plug_bio_endio() may
+ * happen after handling a request completion with
+ * blk_zone_write_plug_complete_request() (e.g. with split BIOs
+ * that are chained). In such case, disk_zone_wplug_unplug_bio()
+ * should not attempt to remove the zone write plug until all BIO
+ * completions are seen. Check by looking at the zone write plug
+ * reference count, which is 2 when the plug is unused (one reference
+ * taken when the plug was allocated and another reference taken by the
+ * caller context).
+ */
+ if (atomic_read(&zwplug->ref) > 2)
+ return false;
+
/* We can remove zone write plugs for zones that are empty or full. */
return !zwplug->wp_offset || zwplug->wp_offset >= disk->zone_capacity;
}
@@ -893,8 +911,9 @@ void blk_zone_write_plug_attempt_merge(struct request *req)
struct bio *bio;
/*
- * Completion of this request needs to be handled with
- * blk_zone_write_plug_complete_request().
+ * Indicate that completion of this request needs to be handled with
+ * blk_zone_write_plug_complete_request(), which will drop the reference
+ * on the zone write plug we took above on entry to this function.
*/
req->rq_flags |= RQF_ZONE_WRITE_PLUGGING;
@@ -1223,6 +1242,9 @@ void blk_zone_write_plug_bio_endio(struct bio *bio)
spin_unlock_irqrestore(&zwplug->lock, flags);
}
+ /* Drop the reference we took when the BIO was issued. */
+ disk_put_zone_wplug(zwplug);
+
/*
* For BIO-based devices, blk_zone_write_plug_complete_request()
* is not called. So we need to schedule execution of the next
@@ -1231,8 +1253,7 @@ void blk_zone_write_plug_bio_endio(struct bio *bio)
if (bio->bi_bdev->bd_has_submit_bio)
disk_zone_wplug_unplug_bio(disk, zwplug);
- /* Drop the reference we took when the BIO was issued. */
- atomic_dec(&zwplug->ref);
+ /* Drop the reference we took when entering this function. */
disk_put_zone_wplug(zwplug);
}
@@ -1246,13 +1267,15 @@ void blk_zone_write_plug_complete_request(struct request *req)
req->rq_flags &= ~RQF_ZONE_WRITE_PLUGGING;
- disk_zone_wplug_unplug_bio(disk, zwplug);
-
/*
* Drop the reference we took when the request was initialized in
* blk_zone_write_plug_attempt_merge().
*/
- atomic_dec(&zwplug->ref);
+ disk_put_zone_wplug(zwplug);
+
+ disk_zone_wplug_unplug_bio(disk, zwplug);
+
+ /* Drop the reference we took when entering this function. */
disk_put_zone_wplug(zwplug);
}