dm: bind new table before destroying old
authorAlasdair G Kergon <agk@redhat.com>
Thu, 10 Dec 2009 23:52:23 +0000 (23:52 +0000)
committerAlasdair G Kergon <agk@redhat.com>
Thu, 10 Dec 2009 23:52:23 +0000 (23:52 +0000)
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 <agk@redhat.com>
drivers/md/dm-table.c
drivers/md/dm.c

index 1a6cb3c7822ed96c6c0b2eaa764522c5f0e7ae89..3162b9c3c3f29bca8ceb321f3bf488ac63e14166 100644 (file)
@@ -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 16f759fe04fe81b779fb69534b549118f79c0d90..255b75c61544718ddebf56ed1be5e0e970d960cc 100644 (file)
@@ -2077,19 +2077,23 @@ static int __bind(struct mapped_device *md, struct dm_table *t,
        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 work_struct *work)
  */
 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 *md, struct dm_table *table)
                goto out;
        }
 
-       __unbind(md);
+       map = __unbind(md);
        r = __bind(md, table, &limits);
+       dm_table_destroy(map);
 
 out:
        mutex_unlock(&md->suspend_lock);