aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMike Snitzer <snitzer@redhat.com>2012-09-26 18:45:45 -0400
committerAlasdair G Kergon <agk@redhat.com>2012-09-26 18:45:45 -0400
commit3ae706561637331aa578e52bb89ecbba5edcb7a9 (patch)
tree681ff02fc4687617e9293ac3c28919ec26f225b0
parentc3c4555edd10dbc0b388a0125b9c50de5e79af05 (diff)
dm: retain table limits when swapping to new table with no devices
Add a safety net that will re-use the DM device's existing limits in the event that DM device has a temporary table that doesn't have any component devices. This is to reduce the chance that requests not respecting the hardware limits will reach the device. DM recalculates queue limits based only on devices which currently exist in the table. This creates a problem in the event all devices are temporarily removed such as all paths being lost in multipath. DM will reset the limits to the maximum permissible, which can then assemble requests which exceed the limits of the paths when the paths are restored. The request will fail the blk_rq_check_limits() test when sent to a path with lower limits, and will be retried without end by multipath. This became a much bigger issue after v3.6 commit fe86cdcef ("block: do not artificially constrain max_sectors for stacking drivers"). Reported-by: David Jeffery <djeffery@redhat.com> Signed-off-by: Mike Snitzer <snitzer@redhat.com> Signed-off-by: Alasdair G Kergon <agk@redhat.com>
-rw-r--r--drivers/md/dm-table.c35
-rw-r--r--drivers/md/dm.c15
-rw-r--r--drivers/md/dm.h1
3 files changed, 50 insertions, 1 deletions
diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c
index 77b90ae66991..100368eb7991 100644
--- a/drivers/md/dm-table.c
+++ b/drivers/md/dm-table.c
@@ -1212,6 +1212,41 @@ struct dm_target *dm_table_find_target(struct dm_table *t, sector_t sector)
1212 return &t->targets[(KEYS_PER_NODE * n) + k]; 1212 return &t->targets[(KEYS_PER_NODE * n) + k];
1213} 1213}
1214 1214
1215static int count_device(struct dm_target *ti, struct dm_dev *dev,
1216 sector_t start, sector_t len, void *data)
1217{
1218 unsigned *num_devices = data;
1219
1220 (*num_devices)++;
1221
1222 return 0;
1223}
1224
1225/*
1226 * Check whether a table has no data devices attached using each
1227 * target's iterate_devices method.
1228 * Returns false if the result is unknown because a target doesn't
1229 * support iterate_devices.
1230 */
1231bool dm_table_has_no_data_devices(struct dm_table *table)
1232{
1233 struct dm_target *uninitialized_var(ti);
1234 unsigned i = 0, num_devices = 0;
1235
1236 while (i < dm_table_get_num_targets(table)) {
1237 ti = dm_table_get_target(table, i++);
1238
1239 if (!ti->type->iterate_devices)
1240 return false;
1241
1242 ti->type->iterate_devices(ti, count_device, &num_devices);
1243 if (num_devices)
1244 return false;
1245 }
1246
1247 return true;
1248}
1249
1215/* 1250/*
1216 * Establish the new table's queue_limits and validate them. 1251 * Establish the new table's queue_limits and validate them.
1217 */ 1252 */
diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index 6748e0c4df1f..67ffa391edcf 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -2429,7 +2429,7 @@ static void dm_queue_flush(struct mapped_device *md)
2429 */ 2429 */
2430struct dm_table *dm_swap_table(struct mapped_device *md, struct dm_table *table) 2430struct dm_table *dm_swap_table(struct mapped_device *md, struct dm_table *table)
2431{ 2431{
2432 struct dm_table *map = ERR_PTR(-EINVAL); 2432 struct dm_table *live_map, *map = ERR_PTR(-EINVAL);
2433 struct queue_limits limits; 2433 struct queue_limits limits;
2434 int r; 2434 int r;
2435 2435
@@ -2439,6 +2439,19 @@ struct dm_table *dm_swap_table(struct mapped_device *md, struct dm_table *table)
2439 if (!dm_suspended_md(md)) 2439 if (!dm_suspended_md(md))
2440 goto out; 2440 goto out;
2441 2441
2442 /*
2443 * If the new table has no data devices, retain the existing limits.
2444 * This helps multipath with queue_if_no_path if all paths disappear,
2445 * then new I/O is queued based on these limits, and then some paths
2446 * reappear.
2447 */
2448 if (dm_table_has_no_data_devices(table)) {
2449 live_map = dm_get_live_table(md);
2450 if (live_map)
2451 limits = md->queue->limits;
2452 dm_table_put(live_map);
2453 }
2454
2442 r = dm_calculate_queue_limits(table, &limits); 2455 r = dm_calculate_queue_limits(table, &limits);
2443 if (r) { 2456 if (r) {
2444 map = ERR_PTR(r); 2457 map = ERR_PTR(r);
diff --git a/drivers/md/dm.h b/drivers/md/dm.h
index 52eef493d266..6a99fefaa743 100644
--- a/drivers/md/dm.h
+++ b/drivers/md/dm.h
@@ -54,6 +54,7 @@ void dm_table_event_callback(struct dm_table *t,
54 void (*fn)(void *), void *context); 54 void (*fn)(void *), void *context);
55struct dm_target *dm_table_get_target(struct dm_table *t, unsigned int index); 55struct dm_target *dm_table_get_target(struct dm_table *t, unsigned int index);
56struct dm_target *dm_table_find_target(struct dm_table *t, sector_t sector); 56struct dm_target *dm_table_find_target(struct dm_table *t, sector_t sector);
57bool dm_table_has_no_data_devices(struct dm_table *table);
57int dm_calculate_queue_limits(struct dm_table *table, 58int dm_calculate_queue_limits(struct dm_table *table,
58 struct queue_limits *limits); 59 struct queue_limits *limits);
59void dm_table_set_restrictions(struct dm_table *t, struct request_queue *q, 60void dm_table_set_restrictions(struct dm_table *t, struct request_queue *q,