Skip to content

Commit 121218b

Browse files
bmarzinsMikulas Patocka
authored andcommitted
dm: limit swapping tables for devices with zone write plugs
dm_revalidate_zones() only allowed new or previously unzoned devices to call blk_revalidate_disk_zones(). If the device was already zoned, disk->nr_zones would always equal md->nr_zones, so dm_revalidate_zones() returned without doing any work. This would make the zoned settings for the device not match the new table. If the device had zone write plug resources, it could run into errors like bdev_zone_is_seq() reading invalid memory because disk->conv_zones_bitmap was the wrong size. If the device doesn't have any zone write plug resources, calling blk_revalidate_disk_zones() will always correctly update device. If blk_revalidate_disk_zones() fails, it can still overwrite or clear the current disk->nr_zones value. In this case, DM must restore the previous value of disk->nr_zones, so that the zoned settings will continue to match the previous value that it fell back to. If the device already has zone write plug resources, blk_revalidate_disk_zones() will not correctly update them, if it is called for arbitrary zoned device changes. Since there is not much need for this ability, the easiest solution is to disallow any table reloads that change the zoned settings, for devices that already have zone plug resources. Specifically, if a device already has zone plug resources allocated, it can only switch to another zoned table that also emulates zone append. Also, it cannot change the device size or the zone size. A device can switch to an error target. Fixes: bb37d77 ("dm: introduce zone append emulation") Reviewed-by: Damien Le Moal <[email protected]> Tested-by: Damien Le Moal <[email protected]> Signed-off-by: Benjamin Marzinski <[email protected]> Signed-off-by: Mikulas Patocka <[email protected]>
1 parent 37f53a2 commit 121218b

File tree

4 files changed

+73
-14
lines changed

4 files changed

+73
-14
lines changed

drivers/md/dm-table.c

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1491,6 +1491,18 @@ bool dm_table_has_no_data_devices(struct dm_table *t)
14911491
return true;
14921492
}
14931493

1494+
bool dm_table_is_wildcard(struct dm_table *t)
1495+
{
1496+
for (unsigned int i = 0; i < t->num_targets; i++) {
1497+
struct dm_target *ti = dm_table_get_target(t, i);
1498+
1499+
if (!dm_target_is_wildcard(ti->type))
1500+
return false;
1501+
}
1502+
1503+
return true;
1504+
}
1505+
14941506
static int device_not_zoned(struct dm_target *ti, struct dm_dev *dev,
14951507
sector_t start, sector_t len, void *data)
14961508
{
@@ -1831,6 +1843,19 @@ static bool dm_table_supports_atomic_writes(struct dm_table *t)
18311843
return true;
18321844
}
18331845

1846+
bool dm_table_supports_size_change(struct dm_table *t, sector_t old_size,
1847+
sector_t new_size)
1848+
{
1849+
if (IS_ENABLED(CONFIG_BLK_DEV_ZONED) && dm_has_zone_plugs(t->md) &&
1850+
old_size != new_size) {
1851+
DMWARN("%s: device has zone write plug resources. "
1852+
"Cannot change size",
1853+
dm_device_name(t->md));
1854+
return false;
1855+
}
1856+
return true;
1857+
}
1858+
18341859
int dm_table_set_restrictions(struct dm_table *t, struct request_queue *q,
18351860
struct queue_limits *limits)
18361861
{
@@ -1868,11 +1893,17 @@ int dm_table_set_restrictions(struct dm_table *t, struct request_queue *q,
18681893
limits->features &= ~BLK_FEAT_DAX;
18691894

18701895
/* For a zoned table, setup the zone related queue attributes. */
1871-
if (IS_ENABLED(CONFIG_BLK_DEV_ZONED) &&
1872-
(limits->features & BLK_FEAT_ZONED)) {
1873-
r = dm_set_zones_restrictions(t, q, limits);
1874-
if (r)
1875-
return r;
1896+
if (IS_ENABLED(CONFIG_BLK_DEV_ZONED)) {
1897+
if (limits->features & BLK_FEAT_ZONED) {
1898+
r = dm_set_zones_restrictions(t, q, limits);
1899+
if (r)
1900+
return r;
1901+
} else if (dm_has_zone_plugs(t->md)) {
1902+
DMWARN("%s: device has zone write plug resources. "
1903+
"Cannot switch to non-zoned table.",
1904+
dm_device_name(t->md));
1905+
return -EINVAL;
1906+
}
18761907
}
18771908

18781909
if (dm_table_supports_atomic_writes(t))

drivers/md/dm-zone.c

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -160,22 +160,22 @@ int dm_revalidate_zones(struct dm_table *t, struct request_queue *q)
160160
{
161161
struct mapped_device *md = t->md;
162162
struct gendisk *disk = md->disk;
163+
unsigned int nr_zones = disk->nr_zones;
163164
int ret;
164165

165166
if (!get_capacity(disk))
166167
return 0;
167168

168-
/* Revalidate only if something changed. */
169-
if (!disk->nr_zones || disk->nr_zones != md->nr_zones) {
170-
DMINFO("%s using %s zone append",
171-
disk->disk_name,
172-
queue_emulates_zone_append(q) ? "emulated" : "native");
173-
md->nr_zones = 0;
174-
}
175-
176-
if (md->nr_zones)
169+
/*
170+
* Do not revalidate if zone write plug resources have already
171+
* been allocated.
172+
*/
173+
if (dm_has_zone_plugs(md))
177174
return 0;
178175

176+
DMINFO("%s using %s zone append", disk->disk_name,
177+
queue_emulates_zone_append(q) ? "emulated" : "native");
178+
179179
/*
180180
* Our table is not live yet. So the call to dm_get_live_table()
181181
* in dm_blk_report_zones() will fail. Set a temporary pointer to
@@ -189,6 +189,7 @@ int dm_revalidate_zones(struct dm_table *t, struct request_queue *q)
189189

190190
if (ret) {
191191
DMERR("Revalidate zones failed %d", ret);
192+
disk->nr_zones = nr_zones;
192193
return ret;
193194
}
194195

@@ -385,12 +386,28 @@ int dm_set_zones_restrictions(struct dm_table *t, struct request_queue *q,
385386
lim->max_open_zones = 0;
386387
lim->max_active_zones = 0;
387388
lim->max_hw_zone_append_sectors = 0;
389+
lim->max_zone_append_sectors = 0;
388390
lim->zone_write_granularity = 0;
389391
lim->chunk_sectors = 0;
390392
lim->features &= ~BLK_FEAT_ZONED;
391393
return 0;
392394
}
393395

396+
if (get_capacity(disk) && dm_has_zone_plugs(t->md)) {
397+
if (q->limits.chunk_sectors != lim->chunk_sectors) {
398+
DMWARN("%s: device has zone write plug resources. "
399+
"Cannot change zone size",
400+
disk->disk_name);
401+
return -EINVAL;
402+
}
403+
if (lim->max_hw_zone_append_sectors != 0 &&
404+
!dm_table_is_wildcard(t)) {
405+
DMWARN("%s: device has zone write plug resources. "
406+
"New table must emulate zone append",
407+
disk->disk_name);
408+
return -EINVAL;
409+
}
410+
}
394411
/*
395412
* Warn once (when the capacity is not yet set) if the mapped device is
396413
* partially using zone resources of the target devices as that leads to

drivers/md/dm.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2429,6 +2429,12 @@ static struct dm_table *__bind(struct mapped_device *md, struct dm_table *t,
24292429
size = dm_table_get_size(t);
24302430

24312431
old_size = dm_get_size(md);
2432+
2433+
if (!dm_table_supports_size_change(t, old_size, size)) {
2434+
old_map = ERR_PTR(-EINVAL);
2435+
goto out;
2436+
}
2437+
24322438
set_capacity(md->disk, size);
24332439

24342440
ret = dm_table_set_restrictions(t, md->queue, limits);

drivers/md/dm.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ void dm_table_event_callback(struct dm_table *t,
5858
void (*fn)(void *), void *context);
5959
struct dm_target *dm_table_find_target(struct dm_table *t, sector_t sector);
6060
bool dm_table_has_no_data_devices(struct dm_table *table);
61+
bool dm_table_is_wildcard(struct dm_table *t);
6162
int dm_calculate_queue_limits(struct dm_table *table,
6263
struct queue_limits *limits);
6364
int dm_table_set_restrictions(struct dm_table *t, struct request_queue *q,
@@ -72,6 +73,8 @@ struct target_type *dm_table_get_immutable_target_type(struct dm_table *t);
7273
struct dm_target *dm_table_get_immutable_target(struct dm_table *t);
7374
struct dm_target *dm_table_get_wildcard_target(struct dm_table *t);
7475
bool dm_table_request_based(struct dm_table *t);
76+
bool dm_table_supports_size_change(struct dm_table *t, sector_t old_size,
77+
sector_t new_size);
7578

7679
void dm_lock_md_type(struct mapped_device *md);
7780
void dm_unlock_md_type(struct mapped_device *md);
@@ -111,12 +114,14 @@ bool dm_is_zone_write(struct mapped_device *md, struct bio *bio);
111114
int dm_zone_get_reset_bitmap(struct mapped_device *md, struct dm_table *t,
112115
sector_t sector, unsigned int nr_zones,
113116
unsigned long *need_reset);
117+
#define dm_has_zone_plugs(md) ((md)->disk->zone_wplugs_hash != NULL)
114118
#else
115119
#define dm_blk_report_zones NULL
116120
static inline bool dm_is_zone_write(struct mapped_device *md, struct bio *bio)
117121
{
118122
return false;
119123
}
124+
#define dm_has_zone_plugs(md) false
120125
#endif
121126

122127
/*

0 commit comments

Comments
 (0)