block: don't leak disk->zones_cond for !disk_need_zone_resources

disk->zones_cond is allocated for all zoned devices, but
disk_free_zone_resources skips it when the zone write plug hash is not
allocated, leaking the allocation for non-mq devices that don't emulate
zone append.  This is reported by kmemleak-enabled xfstests for various
tests that use simple device mapper targets.

Fix this by moving all code that requires writes plugs from
disk_free_zone_resources into disk_destroy_zone_wplugs_hash_table
and executing the rest of the code, including the disk->zones_cond
freeing unconditionally.

Fixes: 6e945ffb65 ("block: use zone condition to determine conventional zones")
Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Damien Le Moal <dlemoal@kernel.org>
Reviewed-by: Bart Van Assche <bvanassche@acm.org>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
This commit is contained in:
Christoph Hellwig
2025-11-05 14:52:14 -05:00
committed by Jens Axboe
parent ba13710ddd
commit c6886cf610

View File

@@ -1834,6 +1834,14 @@ static void disk_destroy_zone_wplugs_hash_table(struct gendisk *disk)
kfree(disk->zone_wplugs_hash);
disk->zone_wplugs_hash = NULL;
disk->zone_wplugs_hash_bits = 0;
/*
* Wait for the zone write plugs to be RCU-freed before destroying the
* mempool.
*/
rcu_barrier();
mempool_destroy(disk->zone_wplugs_pool);
disk->zone_wplugs_pool = NULL;
}
static void disk_set_zones_cond_array(struct gendisk *disk, u8 *zones_cond)
@@ -1850,9 +1858,6 @@ static void disk_set_zones_cond_array(struct gendisk *disk, u8 *zones_cond)
void disk_free_zone_resources(struct gendisk *disk)
{
if (!disk->zone_wplugs_pool)
return;
if (disk->zone_wplugs_wq) {
destroy_workqueue(disk->zone_wplugs_wq);
disk->zone_wplugs_wq = NULL;
@@ -1860,15 +1865,6 @@ void disk_free_zone_resources(struct gendisk *disk)
disk_destroy_zone_wplugs_hash_table(disk);
/*
* Wait for the zone write plugs to be RCU-freed before
* destorying the mempool.
*/
rcu_barrier();
mempool_destroy(disk->zone_wplugs_pool);
disk->zone_wplugs_pool = NULL;
disk_set_zones_cond_array(disk, NULL);
disk->zone_capacity = 0;
disk->last_zone_capacity = 0;