From: Alasdair G Kergon When replacing a mapped device's table during a 'resume', delay the destruction of the old table until the new one is successfully in place. This will make it easier for a later patch to transfer internal state information from the old table to the new one (something we do not currently support) while giving us more options for reversion if a later part of the operation fails. Devices are always in the suspended state during dm_swap_table(). This patch reinforces the requirement that all I/O must have been flushed from the table targets while in this state (including any in workqueues). In the case of 'noflush' suspending, unprocessed I/O should have been 'pushed back' to the dm core prior to this point, for resubmission after the new table is in place. Signed-off-by: Alasdair G Kergon --- drivers/md/dm-table.c | 3 +++ drivers/md/dm.c | 16 +++++++++++----- 2 files changed, 14 insertions(+), 5 deletions(-) Index: linux-2.6.32/drivers/md/dm-table.c =================================================================== --- linux-2.6.32.orig/drivers/md/dm-table.c +++ linux-2.6.32/drivers/md/dm-table.c @@ -237,6 +237,9 @@ void dm_table_destroy(struct dm_table *t { unsigned int i; + if (!t) + return; + while (atomic_read(&t->holders)) msleep(1); smp_mb(); Index: linux-2.6.32/drivers/md/dm.c =================================================================== --- linux-2.6.32.orig/drivers/md/dm.c +++ linux-2.6.32/drivers/md/dm.c @@ -2077,19 +2077,23 @@ static int __bind(struct mapped_device * return 0; } -static void __unbind(struct mapped_device *md) +/* + * Returns unbound table for the caller to free. + */ +static struct dm_table *__unbind(struct mapped_device *md) { struct dm_table *map = md->map; unsigned long flags; if (!map) - return; + return NULL; dm_table_event_callback(map, NULL, NULL); write_lock_irqsave(&md->map_lock, flags); md->map = NULL; write_unlock_irqrestore(&md->map_lock, flags); - dm_table_destroy(map); + + return map; } /* @@ -2182,7 +2186,7 @@ void dm_put(struct mapped_device *md) } dm_sysfs_exit(md); dm_table_put(map); - __unbind(md); + dm_table_destroy(__unbind(md)); free_dev(md); } } @@ -2368,6 +2372,7 @@ static void dm_rq_barrier_work(struct wo */ int dm_swap_table(struct mapped_device *md, struct dm_table *table) { + struct dm_table *map; struct queue_limits limits; int r = -EINVAL; @@ -2388,8 +2393,9 @@ int dm_swap_table(struct mapped_device * goto out; } - __unbind(md); + map = __unbind(md); r = __bind(md, table, &limits); + dm_table_destroy(map); out: mutex_unlock(&md->suspend_lock);