@@ -1343,22 +1343,17 @@ static void disk_destroy_zone_wplugs_hash_table(struct gendisk *disk)
disk->zone_wplugs_hash_bits = 0;
}
-static unsigned int disk_set_conv_zones_bitmap(struct gendisk *disk,
- unsigned long *bitmap)
+static void 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)
@@ -1386,6 +1381,7 @@ void disk_free_zone_resources(struct gendisk *disk)
disk->last_zone_capacity = 0;
disk->nr_zones = 0;
}
+EXPORT_SYMBOL_GPL(disk_free_zone_resources);
static inline bool disk_need_zone_resources(struct gendisk *disk)
{
@@ -1434,24 +1430,23 @@ struct blk_revalidate_zone_args {
/*
* Update the disk zone resources information and device queue limits.
- * The disk queue is frozen when this is executed.
+ * The disk queue is frozen when this is executed on blk-mq drivers.
*/
static int disk_update_zone_resources(struct gendisk *disk,
struct blk_revalidate_zone_args *args)
{
struct request_queue *q = disk->queue;
- unsigned int nr_seq_zones, nr_conv_zones;
+ unsigned int nr_seq_zones, nr_conv_zones = 0;
unsigned int pool_size;
struct queue_limits lim;
+ int ret;
- disk->nr_zones = args->nr_zones;
- disk->zone_capacity = args->zone_capacity;
- disk->last_zone_capacity = args->last_zone_capacity;
- nr_conv_zones =
- disk_set_conv_zones_bitmap(disk, args->conv_zones_bitmap);
- if (nr_conv_zones >= disk->nr_zones) {
+ if (args->conv_zones_bitmap)
+ nr_conv_zones = bitmap_weight(args->conv_zones_bitmap,
+ args->nr_zones);
+ if (nr_conv_zones >= args->nr_zones) {
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, args->nr_zones);
return -ENODEV;
}
@@ -1463,7 +1458,7 @@ static int disk_update_zone_resources(struct gendisk *disk,
* small ZNS namespace. For such case, assume that the zoned device has
* no zone resource limits.
*/
- nr_seq_zones = disk->nr_zones - nr_conv_zones;
+ nr_seq_zones = args->nr_zones - nr_conv_zones;
if (lim.max_open_zones >= nr_seq_zones)
lim.max_open_zones = 0;
if (lim.max_active_zones >= nr_seq_zones)
@@ -1493,7 +1488,19 @@ static int disk_update_zone_resources(struct gendisk *disk,
}
commit:
- return queue_limits_commit_update_frozen(q, &lim);
+ if (queue_is_mq(disk->queue))
+ ret = queue_limits_commit_update_frozen(q, &lim);
+ else
+ ret = queue_limits_commit_update(q, &lim);
+
+ if (!ret) {
+ disk->nr_zones = args->nr_zones;
+ disk->zone_capacity = args->zone_capacity;
+ disk->last_zone_capacity = args->last_zone_capacity;
+ disk_set_conv_zones_bitmap(disk, args->conv_zones_bitmap);
+ }
+
+ return ret;
}
static int blk_revalidate_conv_zone(struct blk_zone *zone, unsigned int idx,
@@ -1648,8 +1655,6 @@ static int blk_revalidate_zone_cb(struct blk_zone *zone, unsigned int idx,
* and when the zone configuration of the gendisk changes (e.g. after a format).
* Before calling this function, the device driver must already have set the
* device zone size (chunk_sector limit) and the max zone append limit.
- * BIO based drivers can also use this function as long as the device queue
- * can be safely frozen.
*/
int blk_revalidate_disk_zones(struct gendisk *disk)
{
@@ -1709,13 +1714,13 @@ int blk_revalidate_disk_zones(struct gendisk *disk)
/*
* Set the new disk zone parameters only once the queue is frozen and
- * all I/Os are completed.
+ * all I/Os are completed on blk-mq drivers.
*/
if (ret > 0)
ret = disk_update_zone_resources(disk, &args);
else
pr_warn("%s: failed to revalidate zones\n", disk->disk_name);
- if (ret) {
+ if (ret && queue_is_mq(disk->queue)) {
unsigned int memflags = blk_mq_freeze_queue(q);
disk_free_zone_resources(disk);
@@ -454,7 +454,6 @@ static inline struct bio *blk_queue_bounce(struct bio *bio,
#ifdef CONFIG_BLK_DEV_ZONED
void disk_init_zone_resources(struct gendisk *disk);
-void disk_free_zone_resources(struct gendisk *disk);
static inline bool bio_zone_write_plugging(struct bio *bio)
{
return bio_flagged(bio, BIO_ZONE_WRITE_PLUGGING);
@@ -500,9 +499,6 @@ int blkdev_zone_mgmt_ioctl(struct block_device *bdev, blk_mode_t mode,
static inline void disk_init_zone_resources(struct gendisk *disk)
{
}
-static inline void disk_free_zone_resources(struct gendisk *disk)
-{
-}
static inline bool bio_zone_write_plugging(struct bio *bio)
{
return false;
@@ -159,6 +159,7 @@ int dm_revalidate_zones(struct dm_table *t, struct request_queue *q)
struct mapped_device *md = t->md;
struct gendisk *disk = md->disk;
int ret;
+ bool was_zoned = disk->nr_zones != 0;
if (!get_capacity(disk))
return 0;
@@ -187,6 +188,8 @@ int dm_revalidate_zones(struct dm_table *t, struct request_queue *q)
if (ret) {
DMERR("Revalidate zones failed %d", ret);
+ if (!was_zoned)
+ disk_free_zone_resources(disk);
return ret;
}
@@ -690,12 +690,16 @@ static inline bool blk_queue_is_zoned(struct request_queue *q)
}
#ifdef CONFIG_BLK_DEV_ZONED
+void disk_free_zone_resources(struct gendisk *disk);
static inline unsigned int disk_nr_zones(struct gendisk *disk)
{
return disk->nr_zones;
}
bool blk_zone_plug_bio(struct bio *bio, unsigned int nr_segs);
#else /* CONFIG_BLK_DEV_ZONED */
+static inline void disk_free_zone_resources(struct gendisk *disk)
+{
+}
static inline unsigned int disk_nr_zones(struct gendisk *disk)
{
return 0;
If device-mapper is swapping zoned tables, and blk_revalidate_disk_zones() fails. It must retain its current zoned resources since device-mapper will be failing back to using the previous table and the zoned settings need to match the table. Allocating unnecessary zwplugs is acceptable, but the zoned configuration must not change. Otherwise it can run into errors like bdev_zone_is_seq() reading invalid memory because disk->conv_zones_bitmap is the wrong size. However if device-mapper did not previously have a zoned table, it should free up the zoned resources, instead of leaving them allocated and unused. To solve this, do not free the zone resources when blk_revalidate_disk_zones() fails for bio based drivers. Additionally, delay copying the zoned settings to the gendisk until disk_update_zone_resources() can no longer fail, and do not freeze the queue for bio-based drivers, since this will hang if there are any plugged zone write bios. Also, export disk_free_zone_resources() so that device-mapper can choose when to free the zoned resources. Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com> --- block/blk-zoned.c | 49 +++++++++++++++++++++++------------------- block/blk.h | 4 ---- drivers/md/dm-zone.c | 3 +++ include/linux/blkdev.h | 4 ++++ 4 files changed, 34 insertions(+), 26 deletions(-)