diff options
| author | Damien Le Moal <dlemoal@kernel.org> | 2026-09-08 17:57:31 +0900 |
|---|---|---|
| committer | Jens Axboe <axboe@kernel.dk> | 2026-09-15 06:53:06 -0600 |
| commit | 496b9748884cf8b1c656a69eadc9f9f7eb84eb4e (patch) | |
| tree | d22e84f971f675f95352c683c10fd3fa9475431f | |
| parent | 73ef8afbe8c9a53c2f42811f9f2a7f862647d180 (diff) | |
| download | linux-next-496b9748884cf8b1c656a69eadc9f9f7eb84eb4e.tar.gz linux-next-496b9748884cf8b1c656a69eadc9f9f7eb84eb4e.zip | |
block: improve capacity handling during zone revalidation
While executing blk_revalidate_disk_zones(), a disk capacity is looked at
using get_capacity() several times: on entry to
blk_revalidate_disk_zones(), when allocating zone revalidation arguments
in disk_revalidate_zone_resources(), while validating zones in
blk_revalidate_zone_cb() and one last time at the end of
blk_revalidate_disk_zones() to check that all zones have been inspected.
Since this is all done while passthrough commands can be issued, it is
possible that a capacity change operation (e.g. the removal of a storage
element on a SCSI or SATA disk) is concurrently executed, potentially
resulting in an inconsistent or conflicting revalidation with potentially
out-of-bound accesses to the zone condition array.
Prevent issues by using get_capacity() once on entry to
blk_revalidate_disk_zones(), remembering this capacity as a field of
struct blk_revalidate_zone_args and using that field while revalidating
zones. A final second call to get_capacity() is done at the end of
blk_revalidate_disk_zones() to ensure that the disk capacity has not
changed, thus revalidating the capacity (and number of zones) itself.
Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Link: https://patch.msgid.link/20260908085745.1082697-3-dlemoal@kernel.org
Signed-off-by: Jens Axboe <axboe@kernel.dk>
| -rw-r--r-- | block/blk-zoned.c | 19 |
1 files changed, 13 insertions, 6 deletions
diff --git a/block/blk-zoned.c b/block/blk-zoned.c index c7d8c767bb31..8b3b5c7bfd03 100644 --- a/block/blk-zoned.c +++ b/block/blk-zoned.c @@ -1993,6 +1993,7 @@ void disk_release_zone_resources(struct gendisk *disk) struct blk_revalidate_zone_args { struct gendisk *disk; + sector_t capacity; u8 *zones_cond; unsigned int nr_zones; unsigned int nr_conv_zones; @@ -2010,7 +2011,7 @@ static int disk_revalidate_zone_resources(struct gendisk *disk, args->disk = disk; args->nr_zones = - DIV_ROUND_UP_ULL(get_capacity(disk), lim->chunk_sectors); + DIV_ROUND_UP_ULL(args->capacity, lim->chunk_sectors); /* Cached zone conditions: 1 byte per zone */ args->zones_cond = kzalloc(args->nr_zones, GFP_NOIO); @@ -2231,7 +2232,7 @@ static int blk_revalidate_zone_cb(struct blk_zone *zone, unsigned int idx, return -ENODEV; } - if (zone->start >= get_capacity(disk) || !zone->len) { + if (zone->start >= args->capacity || !zone->len) { pr_warn("%s: Invalid zone start %llu, length %llu\n", disk->disk_name, zone->start, zone->len); return -ENODEV; @@ -2302,8 +2303,9 @@ int blk_revalidate_disk_zones(struct gendisk *disk) { struct request_queue *q = disk->queue; sector_t zone_sectors = q->limits.chunk_sectors; - sector_t capacity = get_capacity(disk); - struct blk_revalidate_zone_args args = { }; + struct blk_revalidate_zone_args args = { + .capacity = get_capacity(disk), + }; struct blk_report_zones_args rep_args = { .cb = blk_revalidate_zone_cb, .data = &args, @@ -2314,7 +2316,7 @@ int blk_revalidate_disk_zones(struct gendisk *disk) if (WARN_ON_ONCE(!blk_queue_is_zoned(q))) return -EIO; - if (!capacity) + if (!args.capacity) return -ENODEV; /* @@ -2352,7 +2354,12 @@ int blk_revalidate_disk_zones(struct gendisk *disk) * If zones where reported, make sure that the entire disk capacity * has been checked. */ - if (args.sector != capacity) { + if (args.capacity != get_capacity(disk)) { + pr_warn("%s: Capacity has changed\n", disk->disk_name); + ret = -ENODEV; + goto free_args; + } + if (args.sector != args.capacity) { pr_warn("%s: Missing zones from sector %llu\n", disk->disk_name, args.sector); ret = -ENODEV; |
