Skip to content

Commit d7cb6d7

Browse files
damien-lemoalaxboe
authored andcommitted
block: RCU protect disk->conv_zones_bitmap
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 <[email protected]> Reviewed-by: Christoph Hellwig <[email protected]> Reviewed-by: Johannes Thumshirn <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Jens Axboe <[email protected]>
1 parent 8e71afb commit d7cb6d7

File tree

2 files changed

+32
-13
lines changed

2 files changed

+32
-13
lines changed

block/blk-zoned.c

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -350,9 +350,15 @@ int blkdev_zone_mgmt_ioctl(struct block_device *bdev, blk_mode_t mode,
350350

351351
static inline bool disk_zone_is_conv(struct gendisk *disk, sector_t sector)
352352
{
353-
if (!disk->conv_zones_bitmap)
354-
return false;
355-
return test_bit(disk_zone_no(disk, sector), disk->conv_zones_bitmap);
353+
unsigned long *bitmap;
354+
bool is_conv;
355+
356+
rcu_read_lock();
357+
bitmap = rcu_dereference(disk->conv_zones_bitmap);
358+
is_conv = bitmap && test_bit(disk_zone_no(disk, sector), bitmap);
359+
rcu_read_unlock();
360+
361+
return is_conv;
356362
}
357363

358364
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)
14551461
disk->zone_wplugs_hash_bits = 0;
14561462
}
14571463

1464+
static unsigned int disk_set_conv_zones_bitmap(struct gendisk *disk,
1465+
unsigned long *bitmap)
1466+
{
1467+
unsigned int nr_conv_zones = 0;
1468+
unsigned long flags;
1469+
1470+
spin_lock_irqsave(&disk->zone_wplugs_lock, flags);
1471+
if (bitmap)
1472+
nr_conv_zones = bitmap_weight(bitmap, disk->nr_zones);
1473+
bitmap = rcu_replace_pointer(disk->conv_zones_bitmap, bitmap,
1474+
lockdep_is_held(&disk->zone_wplugs_lock));
1475+
spin_unlock_irqrestore(&disk->zone_wplugs_lock, flags);
1476+
1477+
kfree_rcu_mightsleep(bitmap);
1478+
1479+
return nr_conv_zones;
1480+
}
1481+
14581482
void disk_free_zone_resources(struct gendisk *disk)
14591483
{
14601484
if (!disk->zone_wplugs_pool)
@@ -1478,8 +1502,7 @@ void disk_free_zone_resources(struct gendisk *disk)
14781502
mempool_destroy(disk->zone_wplugs_pool);
14791503
disk->zone_wplugs_pool = NULL;
14801504

1481-
bitmap_free(disk->conv_zones_bitmap);
1482-
disk->conv_zones_bitmap = NULL;
1505+
disk_set_conv_zones_bitmap(disk, NULL);
14831506
disk->zone_capacity = 0;
14841507
disk->last_zone_capacity = 0;
14851508
disk->nr_zones = 0;
@@ -1538,17 +1561,15 @@ static int disk_update_zone_resources(struct gendisk *disk,
15381561
struct blk_revalidate_zone_args *args)
15391562
{
15401563
struct request_queue *q = disk->queue;
1541-
unsigned int nr_seq_zones, nr_conv_zones = 0;
1564+
unsigned int nr_seq_zones, nr_conv_zones;
15421565
unsigned int pool_size;
15431566
struct queue_limits lim;
15441567

15451568
disk->nr_zones = args->nr_zones;
15461569
disk->zone_capacity = args->zone_capacity;
15471570
disk->last_zone_capacity = args->last_zone_capacity;
1548-
swap(disk->conv_zones_bitmap, args->conv_zones_bitmap);
1549-
if (disk->conv_zones_bitmap)
1550-
nr_conv_zones = bitmap_weight(disk->conv_zones_bitmap,
1551-
disk->nr_zones);
1571+
nr_conv_zones =
1572+
disk_set_conv_zones_bitmap(disk, args->conv_zones_bitmap);
15521573
if (nr_conv_zones >= disk->nr_zones) {
15531574
pr_warn("%s: Invalid number of conventional zones %u / %u\n",
15541575
disk->disk_name, nr_conv_zones, disk->nr_zones);
@@ -1817,8 +1838,6 @@ int blk_revalidate_disk_zones(struct gendisk *disk)
18171838
disk_free_zone_resources(disk);
18181839
blk_mq_unfreeze_queue(q);
18191840

1820-
kfree(args.conv_zones_bitmap);
1821-
18221841
return ret;
18231842
}
18241843
EXPORT_SYMBOL_GPL(blk_revalidate_disk_zones);

include/linux/blkdev.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ struct gendisk {
195195
unsigned int nr_zones;
196196
unsigned int zone_capacity;
197197
unsigned int last_zone_capacity;
198-
unsigned long *conv_zones_bitmap;
198+
unsigned long __rcu *conv_zones_bitmap;
199199
unsigned int zone_wplugs_hash_bits;
200200
spinlock_t zone_wplugs_lock;
201201
struct mempool_s *zone_wplugs_pool;

0 commit comments

Comments
 (0)