Skip to content

Commit 29459c3

Browse files
damien-lemoalaxboe
authored andcommitted
block: Fix zone write plugging handling of devices with a runt zone
A zoned device may have a last sequential write required zone that is smaller than other zones. However, all tests to check if a zone write plug write offset exceeds the zone capacity use the same capacity value stored in the gendisk zone_capacity field. This is incorrect for a zoned device with a last runt (smaller) zone. Add the new field last_zone_capacity to struct gendisk to store the capacity of the last zone of the device. blk_revalidate_seq_zone() and blk_revalidate_conv_zone() are both modified to get this value when disk_zone_is_last() returns true. Similarly to zone_capacity, the value is first stored using the last_zone_capacity field of struct blk_revalidate_zone_args. Once zone revalidation of all zones is done, this is used to set the gendisk last_zone_capacity field. The checks to determine if a zone is full or if a sector offset in a zone exceeds the zone capacity in disk_should_remove_zone_wplug(), disk_zone_wplug_abort_unaligned(), blk_zone_write_plug_init_request(), and blk_zone_wplug_prepare_bio() are modified to use the new helper functions disk_zone_is_full() and disk_zone_wplug_is_full(). disk_zone_is_full() uses the zone index to determine if the zone being tested is the last one of the disk and uses the either the disk zone_capacity or last_zone_capacity accordingly. Fixes: dd291d7 ("block: Introduce zone write plugging") Signed-off-by: Damien Le Moal <dlemoal@kernel.org> Reviewed-by: Bart Van Assche <bvanassche@acm.org> Reviewed-by: Niklas Cassel <cassel@kernel.org> Link: https://lore.kernel.org/r/20240530054035.491497-4-dlemoal@kernel.org Signed-off-by: Jens Axboe <axboe@kernel.dk>
1 parent cd63999 commit 29459c3

File tree

2 files changed

+28
-8
lines changed

2 files changed

+28
-8
lines changed

block/blk-zoned.c

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,20 @@ static bool disk_zone_is_last(struct gendisk *disk, struct blk_zone *zone)
455455
return zone->start + zone->len >= get_capacity(disk);
456456
}
457457

458+
static bool disk_zone_is_full(struct gendisk *disk,
459+
unsigned int zno, unsigned int offset_in_zone)
460+
{
461+
if (zno < disk->nr_zones - 1)
462+
return offset_in_zone >= disk->zone_capacity;
463+
return offset_in_zone >= disk->last_zone_capacity;
464+
}
465+
466+
static bool disk_zone_wplug_is_full(struct gendisk *disk,
467+
struct blk_zone_wplug *zwplug)
468+
{
469+
return disk_zone_is_full(disk, zwplug->zone_no, zwplug->wp_offset);
470+
}
471+
458472
static bool disk_insert_zone_wplug(struct gendisk *disk,
459473
struct blk_zone_wplug *zwplug)
460474
{
@@ -548,7 +562,7 @@ static inline bool disk_should_remove_zone_wplug(struct gendisk *disk,
548562
return false;
549563

550564
/* We can remove zone write plugs for zones that are empty or full. */
551-
return !zwplug->wp_offset || zwplug->wp_offset >= disk->zone_capacity;
565+
return !zwplug->wp_offset || disk_zone_wplug_is_full(disk, zwplug);
552566
}
553567

554568
static void disk_remove_zone_wplug(struct gendisk *disk,
@@ -669,13 +683,12 @@ static void disk_zone_wplug_abort(struct blk_zone_wplug *zwplug)
669683
static void disk_zone_wplug_abort_unaligned(struct gendisk *disk,
670684
struct blk_zone_wplug *zwplug)
671685
{
672-
unsigned int zone_capacity = disk->zone_capacity;
673686
unsigned int wp_offset = zwplug->wp_offset;
674687
struct bio_list bl = BIO_EMPTY_LIST;
675688
struct bio *bio;
676689

677690
while ((bio = bio_list_pop(&zwplug->bio_list))) {
678-
if (wp_offset >= zone_capacity ||
691+
if (disk_zone_is_full(disk, zwplug->zone_no, wp_offset) ||
679692
(bio_op(bio) != REQ_OP_ZONE_APPEND &&
680693
bio_offset_from_zone_start(bio) != wp_offset)) {
681694
blk_zone_wplug_bio_io_error(zwplug, bio);
@@ -914,7 +927,6 @@ void blk_zone_write_plug_init_request(struct request *req)
914927
sector_t req_back_sector = blk_rq_pos(req) + blk_rq_sectors(req);
915928
struct request_queue *q = req->q;
916929
struct gendisk *disk = q->disk;
917-
unsigned int zone_capacity = disk->zone_capacity;
918930
struct blk_zone_wplug *zwplug =
919931
disk_get_zone_wplug(disk, blk_rq_pos(req));
920932
unsigned long flags;
@@ -938,7 +950,7 @@ void blk_zone_write_plug_init_request(struct request *req)
938950
* into the back of the request.
939951
*/
940952
spin_lock_irqsave(&zwplug->lock, flags);
941-
while (zwplug->wp_offset < zone_capacity) {
953+
while (!disk_zone_wplug_is_full(disk, zwplug)) {
942954
bio = bio_list_peek(&zwplug->bio_list);
943955
if (!bio)
944956
break;
@@ -984,7 +996,7 @@ static bool blk_zone_wplug_prepare_bio(struct blk_zone_wplug *zwplug,
984996
* We know such BIO will fail, and that would potentially overflow our
985997
* write pointer offset beyond the end of the zone.
986998
*/
987-
if (zwplug->wp_offset >= disk->zone_capacity)
999+
if (disk_zone_wplug_is_full(disk, zwplug))
9881000
goto err;
9891001

9901002
if (bio_op(bio) == REQ_OP_ZONE_APPEND) {
@@ -1561,6 +1573,7 @@ void disk_free_zone_resources(struct gendisk *disk)
15611573
kfree(disk->conv_zones_bitmap);
15621574
disk->conv_zones_bitmap = NULL;
15631575
disk->zone_capacity = 0;
1576+
disk->last_zone_capacity = 0;
15641577
disk->nr_zones = 0;
15651578
}
15661579

@@ -1605,6 +1618,7 @@ struct blk_revalidate_zone_args {
16051618
unsigned long *conv_zones_bitmap;
16061619
unsigned int nr_zones;
16071620
unsigned int zone_capacity;
1621+
unsigned int last_zone_capacity;
16081622
sector_t sector;
16091623
};
16101624

@@ -1622,6 +1636,7 @@ static int disk_update_zone_resources(struct gendisk *disk,
16221636

16231637
disk->nr_zones = args->nr_zones;
16241638
disk->zone_capacity = args->zone_capacity;
1639+
disk->last_zone_capacity = args->last_zone_capacity;
16251640
swap(disk->conv_zones_bitmap, args->conv_zones_bitmap);
16261641
if (disk->conv_zones_bitmap)
16271642
nr_conv_zones = bitmap_weight(disk->conv_zones_bitmap,
@@ -1673,6 +1688,9 @@ static int blk_revalidate_conv_zone(struct blk_zone *zone, unsigned int idx,
16731688
return -ENODEV;
16741689
}
16751690

1691+
if (disk_zone_is_last(disk, zone))
1692+
args->last_zone_capacity = zone->capacity;
1693+
16761694
if (!disk_need_zone_resources(disk))
16771695
return 0;
16781696

@@ -1703,8 +1721,9 @@ static int blk_revalidate_seq_zone(struct blk_zone *zone, unsigned int idx,
17031721
*/
17041722
if (!args->zone_capacity)
17051723
args->zone_capacity = zone->capacity;
1706-
if (!disk_zone_is_last(disk, zone) &&
1707-
zone->capacity != args->zone_capacity) {
1724+
if (disk_zone_is_last(disk, zone)) {
1725+
args->last_zone_capacity = zone->capacity;
1726+
} else if (zone->capacity != args->zone_capacity) {
17081727
pr_warn("%s: Invalid variable zone capacity\n",
17091728
disk->disk_name);
17101729
return -ENODEV;

include/linux/blkdev.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@ struct gendisk {
186186
*/
187187
unsigned int nr_zones;
188188
unsigned int zone_capacity;
189+
unsigned int last_zone_capacity;
189190
unsigned long *conv_zones_bitmap;
190191
unsigned int zone_wplugs_hash_bits;
191192
spinlock_t zone_wplugs_lock;

0 commit comments

Comments
 (0)