block: RCU protect disk->conv_zones_bitmap
[ Upstream commit d7cb6d7414ea1b33536fa6d11805cb8dceec1f97 ] Ensure that a disk revalidation changing the conventional zones bitmap of a disk does not cause invalid memory references when using the disk_zone_is_conv() helper by RCU protecting the disk->conv_zones_bitmap pointer. disk_zone_is_conv() is modified to operate under the RCU read lock and the function disk_set_conv_zones_bitmap() is added to update a disk conv_zones_bitmap pointer using rcu_replace_pointer() with the disk zone_wplugs_lock spinlock held. disk_free_zone_resources() is modified to call disk_update_zone_resources() with a NULL bitmap pointer to free the disk conv_zones_bitmap. disk_set_conv_zones_bitmap() is also used in disk_update_zone_resources() to set the new (revalidated) bitmap and free the old one. Signed-off-by: Damien Le Moal <dlemoal@kernel.org> Reviewed-by: Christoph Hellwig <hch@lst.de> Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com> Link: https://lore.kernel.org/r/20241107064300.227731-2-dlemoal@kernel.org Signed-off-by: Jens Axboe <axboe@kernel.dk> Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
be54e6e0f9
commit
493326c4f1
+31
-12
@@ -350,9 +350,15 @@ fail:
|
|||||||
|
|
||||||
static inline bool disk_zone_is_conv(struct gendisk *disk, sector_t sector)
|
static inline bool disk_zone_is_conv(struct gendisk *disk, sector_t sector)
|
||||||
{
|
{
|
||||||
if (!disk->conv_zones_bitmap)
|
unsigned long *bitmap;
|
||||||
return false;
|
bool is_conv;
|
||||||
return test_bit(disk_zone_no(disk, sector), disk->conv_zones_bitmap);
|
|
||||||
|
rcu_read_lock();
|
||||||
|
bitmap = rcu_dereference(disk->conv_zones_bitmap);
|
||||||
|
is_conv = bitmap && test_bit(disk_zone_no(disk, sector), bitmap);
|
||||||
|
rcu_read_unlock();
|
||||||
|
|
||||||
|
return is_conv;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool disk_zone_is_last(struct gendisk *disk, struct blk_zone *zone)
|
static bool disk_zone_is_last(struct gendisk *disk, struct blk_zone *zone)
|
||||||
@@ -1455,6 +1461,24 @@ static void disk_destroy_zone_wplugs_hash_table(struct gendisk *disk)
|
|||||||
disk->zone_wplugs_hash_bits = 0;
|
disk->zone_wplugs_hash_bits = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static unsigned int disk_set_conv_zones_bitmap(struct gendisk *disk,
|
||||||
|
unsigned long *bitmap)
|
||||||
|
{
|
||||||
|
unsigned int nr_conv_zones = 0;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&disk->zone_wplugs_lock, flags);
|
||||||
|
if (bitmap)
|
||||||
|
nr_conv_zones = bitmap_weight(bitmap, disk->nr_zones);
|
||||||
|
bitmap = rcu_replace_pointer(disk->conv_zones_bitmap, bitmap,
|
||||||
|
lockdep_is_held(&disk->zone_wplugs_lock));
|
||||||
|
spin_unlock_irqrestore(&disk->zone_wplugs_lock, flags);
|
||||||
|
|
||||||
|
kfree_rcu_mightsleep(bitmap);
|
||||||
|
|
||||||
|
return nr_conv_zones;
|
||||||
|
}
|
||||||
|
|
||||||
void disk_free_zone_resources(struct gendisk *disk)
|
void disk_free_zone_resources(struct gendisk *disk)
|
||||||
{
|
{
|
||||||
if (!disk->zone_wplugs_pool)
|
if (!disk->zone_wplugs_pool)
|
||||||
@@ -1478,8 +1502,7 @@ void disk_free_zone_resources(struct gendisk *disk)
|
|||||||
mempool_destroy(disk->zone_wplugs_pool);
|
mempool_destroy(disk->zone_wplugs_pool);
|
||||||
disk->zone_wplugs_pool = NULL;
|
disk->zone_wplugs_pool = NULL;
|
||||||
|
|
||||||
bitmap_free(disk->conv_zones_bitmap);
|
disk_set_conv_zones_bitmap(disk, NULL);
|
||||||
disk->conv_zones_bitmap = NULL;
|
|
||||||
disk->zone_capacity = 0;
|
disk->zone_capacity = 0;
|
||||||
disk->last_zone_capacity = 0;
|
disk->last_zone_capacity = 0;
|
||||||
disk->nr_zones = 0;
|
disk->nr_zones = 0;
|
||||||
@@ -1538,7 +1561,7 @@ static int disk_update_zone_resources(struct gendisk *disk,
|
|||||||
struct blk_revalidate_zone_args *args)
|
struct blk_revalidate_zone_args *args)
|
||||||
{
|
{
|
||||||
struct request_queue *q = disk->queue;
|
struct request_queue *q = disk->queue;
|
||||||
unsigned int nr_seq_zones, nr_conv_zones = 0;
|
unsigned int nr_seq_zones, nr_conv_zones;
|
||||||
unsigned int pool_size;
|
unsigned int pool_size;
|
||||||
struct queue_limits lim;
|
struct queue_limits lim;
|
||||||
int ret;
|
int ret;
|
||||||
@@ -1546,10 +1569,8 @@ static int disk_update_zone_resources(struct gendisk *disk,
|
|||||||
disk->nr_zones = args->nr_zones;
|
disk->nr_zones = args->nr_zones;
|
||||||
disk->zone_capacity = args->zone_capacity;
|
disk->zone_capacity = args->zone_capacity;
|
||||||
disk->last_zone_capacity = args->last_zone_capacity;
|
disk->last_zone_capacity = args->last_zone_capacity;
|
||||||
swap(disk->conv_zones_bitmap, args->conv_zones_bitmap);
|
nr_conv_zones =
|
||||||
if (disk->conv_zones_bitmap)
|
disk_set_conv_zones_bitmap(disk, args->conv_zones_bitmap);
|
||||||
nr_conv_zones = bitmap_weight(disk->conv_zones_bitmap,
|
|
||||||
disk->nr_zones);
|
|
||||||
if (nr_conv_zones >= disk->nr_zones) {
|
if (nr_conv_zones >= disk->nr_zones) {
|
||||||
pr_warn("%s: Invalid number of conventional zones %u / %u\n",
|
pr_warn("%s: Invalid number of conventional zones %u / %u\n",
|
||||||
disk->disk_name, nr_conv_zones, disk->nr_zones);
|
disk->disk_name, nr_conv_zones, disk->nr_zones);
|
||||||
@@ -1829,8 +1850,6 @@ int blk_revalidate_disk_zones(struct gendisk *disk)
|
|||||||
blk_mq_unfreeze_queue(q);
|
blk_mq_unfreeze_queue(q);
|
||||||
}
|
}
|
||||||
|
|
||||||
kfree(args.conv_zones_bitmap);
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(blk_revalidate_disk_zones);
|
EXPORT_SYMBOL_GPL(blk_revalidate_disk_zones);
|
||||||
|
|||||||
@@ -195,7 +195,7 @@ struct gendisk {
|
|||||||
unsigned int nr_zones;
|
unsigned int nr_zones;
|
||||||
unsigned int zone_capacity;
|
unsigned int zone_capacity;
|
||||||
unsigned int last_zone_capacity;
|
unsigned int last_zone_capacity;
|
||||||
unsigned long *conv_zones_bitmap;
|
unsigned long __rcu *conv_zones_bitmap;
|
||||||
unsigned int zone_wplugs_hash_bits;
|
unsigned int zone_wplugs_hash_bits;
|
||||||
spinlock_t zone_wplugs_lock;
|
spinlock_t zone_wplugs_lock;
|
||||||
struct mempool_s *zone_wplugs_pool;
|
struct mempool_s *zone_wplugs_pool;
|
||||||
|
|||||||
Reference in New Issue
Block a user