diff options
| -rw-r--r-- | drivers/md/dm-table.c | 35 | ||||
| -rw-r--r-- | drivers/md/dm.c | 15 | ||||
| -rw-r--r-- | drivers/md/dm.h | 1 | 
3 files changed, 50 insertions, 1 deletions
diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c index 77b90ae6699..100368eb799 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)  	return &t->targets[(KEYS_PER_NODE * n) + k];  } +static int count_device(struct dm_target *ti, struct dm_dev *dev, +			sector_t start, sector_t len, void *data) +{ +	unsigned *num_devices = data; + +	(*num_devices)++; + +	return 0; +} + +/* + * Check whether a table has no data devices attached using each + * target's iterate_devices method. + * Returns false if the result is unknown because a target doesn't + * support iterate_devices. + */ +bool dm_table_has_no_data_devices(struct dm_table *table) +{ +	struct dm_target *uninitialized_var(ti); +	unsigned i = 0, num_devices = 0; + +	while (i < dm_table_get_num_targets(table)) { +		ti = dm_table_get_target(table, i++); + +		if (!ti->type->iterate_devices) +			return false; + +		ti->type->iterate_devices(ti, count_device, &num_devices); +		if (num_devices) +			return false; +	} + +	return true; +} +  /*   * Establish the new table's queue_limits and validate them.   */ diff --git a/drivers/md/dm.c b/drivers/md/dm.c index 6748e0c4df1..67ffa391edc 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -2429,7 +2429,7 @@ static void dm_queue_flush(struct mapped_device *md)   */  struct dm_table *dm_swap_table(struct mapped_device *md, struct dm_table *table)  { -	struct dm_table *map = ERR_PTR(-EINVAL); +	struct dm_table *live_map, *map = ERR_PTR(-EINVAL);  	struct queue_limits limits;  	int r; @@ -2439,6 +2439,19 @@ struct dm_table *dm_swap_table(struct mapped_device *md, struct dm_table *table)  	if (!dm_suspended_md(md))  		goto out; +	/* +	 * If the new table has no data devices, retain the existing limits. +	 * This helps multipath with queue_if_no_path if all paths disappear, +	 * then new I/O is queued based on these limits, and then some paths +	 * reappear. +	 */ +	if (dm_table_has_no_data_devices(table)) { +		live_map = dm_get_live_table(md); +		if (live_map) +			limits = md->queue->limits; +		dm_table_put(live_map); +	} +  	r = dm_calculate_queue_limits(table, &limits);  	if (r) {  		map = ERR_PTR(r); diff --git a/drivers/md/dm.h b/drivers/md/dm.h index 52eef493d26..6a99fefaa74 100644 --- a/drivers/md/dm.h +++ b/drivers/md/dm.h @@ -54,6 +54,7 @@ void dm_table_event_callback(struct dm_table *t,  			     void (*fn)(void *), void *context);  struct dm_target *dm_table_get_target(struct dm_table *t, unsigned int index);  struct dm_target *dm_table_find_target(struct dm_table *t, sector_t sector); +bool dm_table_has_no_data_devices(struct dm_table *table);  int dm_calculate_queue_limits(struct dm_table *table,  			      struct queue_limits *limits);  void dm_table_set_restrictions(struct dm_table *t, struct request_queue *q,  |