dm snapshot: support barriers in snapshot merge target
[safe/jmp/linux-2.6] / drivers / md / dm-ioctl.c
index d06dd39..1d66932 100644 (file)
@@ -523,8 +523,6 @@ static int list_versions(struct dm_ioctl *param, size_t param_size)
        return 0;
 }
 
-
-
 static int check_name(const char *name)
 {
        if (strchr(name, '/')) {
@@ -536,6 +534,40 @@ static int check_name(const char *name)
 }
 
 /*
+ * On successful return, the caller must not attempt to acquire
+ * _hash_lock without first calling dm_table_put, because dm_table_destroy
+ * waits for this dm_table_put and could be called under this lock.
+ */
+static struct dm_table *dm_get_inactive_table(struct mapped_device *md)
+{
+       struct hash_cell *hc;
+       struct dm_table *table = NULL;
+
+       down_read(&_hash_lock);
+       hc = dm_get_mdptr(md);
+       if (!hc || hc->md != md) {
+               DMWARN("device has been removed from the dev hash table.");
+               goto out;
+       }
+
+       table = hc->new_map;
+       if (table)
+               dm_table_get(table);
+
+out:
+       up_read(&_hash_lock);
+
+       return table;
+}
+
+static struct dm_table *dm_get_live_or_inactive_table(struct mapped_device *md,
+                                                     struct dm_ioctl *param)
+{
+       return (param->flags & DM_QUERY_INACTIVE_TABLE_FLAG) ?
+               dm_get_inactive_table(md) : dm_get_live_table(md);
+}
+
+/*
  * Fills in a dm_ioctl structure, ready for sending back to
  * userland.
  */
@@ -547,7 +579,7 @@ static int __dev_status(struct mapped_device *md, struct dm_ioctl *param)
        param->flags &= ~(DM_SUSPEND_FLAG | DM_READONLY_FLAG |
                          DM_ACTIVE_PRESENT_FLAG);
 
-       if (dm_suspended(md))
+       if (dm_suspended_md(md))
                param->flags |= DM_SUSPEND_FLAG;
 
        param->dev = huge_encode_dev(disk_devt(disk));
@@ -559,18 +591,30 @@ static int __dev_status(struct mapped_device *md, struct dm_ioctl *param)
         */
        param->open_count = dm_open_count(md);
 
-       if (get_disk_ro(disk))
-               param->flags |= DM_READONLY_FLAG;
-
        param->event_nr = dm_get_event_nr(md);
+       param->target_count = 0;
 
        table = dm_get_live_table(md);
        if (table) {
-               param->flags |= DM_ACTIVE_PRESENT_FLAG;
-               param->target_count = dm_table_get_num_targets(table);
+               if (!(param->flags & DM_QUERY_INACTIVE_TABLE_FLAG)) {
+                       if (get_disk_ro(disk))
+                               param->flags |= DM_READONLY_FLAG;
+                       param->target_count = dm_table_get_num_targets(table);
+               }
                dm_table_put(table);
-       } else
-               param->target_count = 0;
+
+               param->flags |= DM_ACTIVE_PRESENT_FLAG;
+       }
+
+       if (param->flags & DM_QUERY_INACTIVE_TABLE_FLAG) {
+               table = dm_get_inactive_table(md);
+               if (table) {
+                       if (!(dm_table_get_mode(table) & FMODE_WRITE))
+                               param->flags |= DM_READONLY_FLAG;
+                       param->target_count = dm_table_get_num_targets(table);
+                       dm_table_put(table);
+               }
+       }
 
        return 0;
 }
@@ -795,7 +839,7 @@ static int do_suspend(struct dm_ioctl *param)
        if (param->flags & DM_NOFLUSH_FLAG)
                suspend_flags |= DM_SUSPEND_NOFLUSH_FLAG;
 
-       if (!dm_suspended(md))
+       if (!dm_suspended_md(md))
                r = dm_suspend(md, suspend_flags);
 
        if (!r)
@@ -811,7 +855,7 @@ static int do_resume(struct dm_ioctl *param)
        unsigned suspend_flags = DM_SUSPEND_LOCKFS_FLAG;
        struct hash_cell *hc;
        struct mapped_device *md;
-       struct dm_table *new_map;
+       struct dm_table *new_map, *old_map = NULL;
 
        down_write(&_hash_lock);
 
@@ -837,14 +881,14 @@ static int do_resume(struct dm_ioctl *param)
                        suspend_flags &= ~DM_SUSPEND_LOCKFS_FLAG;
                if (param->flags & DM_NOFLUSH_FLAG)
                        suspend_flags |= DM_SUSPEND_NOFLUSH_FLAG;
-               if (!dm_suspended(md))
+               if (!dm_suspended_md(md))
                        dm_suspend(md, suspend_flags);
 
-               r = dm_swap_table(md, new_map);
-               if (r) {
+               old_map = dm_swap_table(md, new_map);
+               if (IS_ERR(old_map)) {
                        dm_table_destroy(new_map);
                        dm_put(md);
-                       return r;
+                       return PTR_ERR(old_map);
                }
 
                if (dm_table_get_mode(new_map) & FMODE_WRITE)
@@ -853,9 +897,11 @@ static int do_resume(struct dm_ioctl *param)
                        set_disk_ro(dm_disk(md), 1);
        }
 
-       if (dm_suspended(md))
+       if (dm_suspended_md(md))
                r = dm_resume(md);
 
+       if (old_map)
+               dm_table_destroy(old_map);
 
        if (!r) {
                dm_kobject_uevent(md, KOBJ_CHANGE, param->event_nr);
@@ -993,7 +1039,7 @@ static int dev_wait(struct dm_ioctl *param, size_t param_size)
        if (r)
                goto out;
 
-       table = dm_get_live_table(md);
+       table = dm_get_live_or_inactive_table(md, param);
        if (table) {
                retrieve_status(table, param, param_size);
                dm_table_put(table);
@@ -1226,7 +1272,7 @@ static int table_deps(struct dm_ioctl *param, size_t param_size)
        if (r)
                goto out;
 
-       table = dm_get_live_table(md);
+       table = dm_get_live_or_inactive_table(md, param);
        if (table) {
                retrieve_deps(table, param, param_size);
                dm_table_put(table);
@@ -1255,13 +1301,13 @@ static int table_status(struct dm_ioctl *param, size_t param_size)
        if (r)
                goto out;
 
-       table = dm_get_live_table(md);
+       table = dm_get_live_or_inactive_table(md, param);
        if (table) {
                retrieve_status(table, param, param_size);
                dm_table_put(table);
        }
 
- out:
+out:
        dm_put(md);
        return r;
 }