]> bbs.cooldavid.org Git - net-next-2.6.git/blobdiff - drivers/md/dm-ioctl.c
dm ioctl: make bio or request based device type immutable
[net-next-2.6.git] / drivers / md / dm-ioctl.c
index d7500e1c26f24dc878bff7bfde6c16d1ac872245..4702f380cb456f10bad1389576b75774dab93845 100644 (file)
@@ -249,55 +249,66 @@ static void __hash_remove(struct hash_cell *hc)
 
 static void dm_hash_remove_all(int keep_open_devices)
 {
-       int i, dev_skipped, dev_removed;
+       int i, dev_skipped;
        struct hash_cell *hc;
-       struct list_head *tmp, *n;
+       struct mapped_device *md;
+
+retry:
+       dev_skipped = 0;
 
        down_write(&_hash_lock);
 
-retry:
-       dev_skipped = dev_removed = 0;
        for (i = 0; i < NUM_BUCKETS; i++) {
-               list_for_each_safe (tmp, n, _name_buckets + i) {
-                       hc = list_entry(tmp, struct hash_cell, name_list);
+               list_for_each_entry(hc, _name_buckets + i, name_list) {
+                       md = hc->md;
+                       dm_get(md);
 
-                       if (keep_open_devices &&
-                           dm_lock_for_deletion(hc->md)) {
+                       if (keep_open_devices && dm_lock_for_deletion(md)) {
+                               dm_put(md);
                                dev_skipped++;
                                continue;
                        }
+
                        __hash_remove(hc);
-                       dev_removed = 1;
-               }
-       }
 
-       /*
-        * Some mapped devices may be using other mapped devices, so if any
-        * still exist, repeat until we make no further progress.
-        */
-       if (dev_skipped) {
-               if (dev_removed)
-                       goto retry;
+                       up_write(&_hash_lock);
 
-               DMWARN("remove_all left %d open device(s)", dev_skipped);
+                       dm_put(md);
+                       if (likely(keep_open_devices))
+                               dm_destroy(md);
+                       else
+                               dm_destroy_immediate(md);
+
+                       /*
+                        * Some mapped devices may be using other mapped
+                        * devices, so repeat until we make no further
+                        * progress.  If a new mapped device is created
+                        * here it will also get removed.
+                        */
+                       goto retry;
+               }
        }
 
        up_write(&_hash_lock);
+
+       if (dev_skipped)
+               DMWARN("remove_all left %d open device(s)", dev_skipped);
 }
 
-static int dm_hash_rename(uint32_t cookie, uint32_t *flags, const char *old,
-                         const char *new)
+static struct mapped_device *dm_hash_rename(struct dm_ioctl *param,
+                                           const char *new)
 {
        char *new_name, *old_name;
        struct hash_cell *hc;
        struct dm_table *table;
+       struct mapped_device *md;
 
        /*
         * duplicate new.
         */
        new_name = kstrdup(new, GFP_KERNEL);
        if (!new_name)
-               return -ENOMEM;
+               return ERR_PTR(-ENOMEM);
 
        down_write(&_hash_lock);
 
@@ -306,24 +317,24 @@ static int dm_hash_rename(uint32_t cookie, uint32_t *flags, const char *old,
         */
        hc = __get_name_cell(new);
        if (hc) {
-               DMWARN("asked to rename to an already existing name %s -> %s",
-                      old, new);
+               DMWARN("asked to rename to an already-existing name %s -> %s",
+                      param->name, new);
                dm_put(hc->md);
                up_write(&_hash_lock);
                kfree(new_name);
-               return -EBUSY;
+               return ERR_PTR(-EBUSY);
        }
 
        /*
         * Is there such a device as 'old' ?
         */
-       hc = __get_name_cell(old);
+       hc = __get_name_cell(param->name);
        if (!hc) {
-               DMWARN("asked to rename a non existent device %s -> %s",
-                      old, new);
+               DMWARN("asked to rename a non-existent device %s -> %s",
+                      param->name, new);
                up_write(&_hash_lock);
                kfree(new_name);
-               return -ENXIO;
+               return ERR_PTR(-ENXIO);
        }
 
        /*
@@ -345,13 +356,14 @@ static int dm_hash_rename(uint32_t cookie, uint32_t *flags, const char *old,
                dm_table_put(table);
        }
 
-       if (!dm_kobject_uevent(hc->md, KOBJ_CHANGE, cookie))
-               *flags |= DM_UEVENT_GENERATED_FLAG;
+       if (!dm_kobject_uevent(hc->md, KOBJ_CHANGE, param->event_nr))
+               param->flags |= DM_UEVENT_GENERATED_FLAG;
 
-       dm_put(hc->md);
+       md = hc->md;
        up_write(&_hash_lock);
        kfree(old_name);
-       return 0;
+
+       return md;
 }
 
 /*-----------------------------------------------------------------
@@ -573,7 +585,7 @@ static struct dm_table *dm_get_live_or_inactive_table(struct mapped_device *md,
  * Fills in a dm_ioctl structure, ready for sending back to
  * userland.
  */
-static int __dev_status(struct mapped_device *md, struct dm_ioctl *param)
+static void __dev_status(struct mapped_device *md, struct dm_ioctl *param)
 {
        struct gendisk *disk = dm_disk(md);
        struct dm_table *table;
@@ -617,8 +629,6 @@ static int __dev_status(struct mapped_device *md, struct dm_ioctl *param)
                        dm_table_put(table);
                }
        }
-
-       return 0;
 }
 
 static int dev_create(struct dm_ioctl *param, size_t param_size)
@@ -640,15 +650,17 @@ static int dev_create(struct dm_ioctl *param, size_t param_size)
        r = dm_hash_insert(param->name, *param->uuid ? param->uuid : NULL, md);
        if (r) {
                dm_put(md);
+               dm_destroy(md);
                return r;
        }
 
        param->flags &= ~DM_INACTIVE_PRESENT_FLAG;
 
-       r = __dev_status(md, param);
+       __dev_status(md, param);
+
        dm_put(md);
 
-       return r;
+       return 0;
 }
 
 /*
@@ -742,6 +754,7 @@ static int dev_remove(struct dm_ioctl *param, size_t param_size)
                param->flags |= DM_UEVENT_GENERATED_FLAG;
 
        dm_put(md);
+       dm_destroy(md);
        return 0;
 }
 
@@ -762,6 +775,7 @@ static int dev_rename(struct dm_ioctl *param, size_t param_size)
 {
        int r;
        char *new_name = (char *) param + param->data_start;
+       struct mapped_device *md;
 
        if (new_name < param->data ||
            invalid_str(new_name, (void *) param + param_size) ||
@@ -774,10 +788,14 @@ static int dev_rename(struct dm_ioctl *param, size_t param_size)
        if (r)
                return r;
 
-       param->data_size = 0;
+       md = dm_hash_rename(param, new_name);
+       if (IS_ERR(md))
+               return PTR_ERR(md);
 
-       return dm_hash_rename(param->event_nr, &param->flags, param->name,
-                             new_name);
+       __dev_status(md, param);
+       dm_put(md);
+
+       return 0;
 }
 
 static int dev_set_geometry(struct dm_ioctl *param, size_t param_size)
@@ -818,8 +836,6 @@ static int dev_set_geometry(struct dm_ioctl *param, size_t param_size)
        geometry.start = indata[3];
 
        r = dm_set_geometry(md, &geometry);
-       if (!r)
-               r = __dev_status(md, param);
 
        param->data_size = 0;
 
@@ -843,13 +859,17 @@ static int do_suspend(struct dm_ioctl *param)
        if (param->flags & DM_NOFLUSH_FLAG)
                suspend_flags |= DM_SUSPEND_NOFLUSH_FLAG;
 
-       if (!dm_suspended_md(md))
+       if (!dm_suspended_md(md)) {
                r = dm_suspend(md, suspend_flags);
+               if (r)
+                       goto out;
+       }
 
-       if (!r)
-               r = __dev_status(md, param);
+       __dev_status(md, param);
 
+out:
        dm_put(md);
+
        return r;
 }
 
@@ -911,7 +931,7 @@ static int do_resume(struct dm_ioctl *param)
                dm_table_destroy(old_map);
 
        if (!r)
-               r = __dev_status(md, param);
+               __dev_status(md, param);
 
        dm_put(md);
        return r;
@@ -935,16 +955,16 @@ static int dev_suspend(struct dm_ioctl *param, size_t param_size)
  */
 static int dev_status(struct dm_ioctl *param, size_t param_size)
 {
-       int r;
        struct mapped_device *md;
 
        md = find_device(param);
        if (!md)
                return -ENXIO;
 
-       r = __dev_status(md, param);
+       __dev_status(md, param);
        dm_put(md);
-       return r;
+
+       return 0;
 }
 
 /*
@@ -1019,7 +1039,7 @@ static void retrieve_status(struct dm_table *table,
  */
 static int dev_wait(struct dm_ioctl *param, size_t param_size)
 {
-       int r;
+       int r = 0;
        struct mapped_device *md;
        struct dm_table *table;
 
@@ -1040,9 +1060,7 @@ static int dev_wait(struct dm_ioctl *param, size_t param_size)
         * changed to trigger the event, so we may as well tell
         * him and save an ioctl.
         */
-       r = __dev_status(md, param);
-       if (r)
-               goto out;
+       __dev_status(md, param);
 
        table = dm_get_live_or_inactive_table(md, param);
        if (table) {
@@ -1050,8 +1068,9 @@ static int dev_wait(struct dm_ioctl *param, size_t param_size)
                dm_table_put(table);
        }
 
- out:
+out:
        dm_put(md);
+
        return r;
 }
 
@@ -1170,6 +1189,21 @@ static int table_load(struct dm_ioctl *param, size_t param_size)
                goto out;
        }
 
+       /* Protect md->type against concurrent table loads. */
+       dm_lock_md_type(md);
+       if (dm_get_md_type(md) == DM_TYPE_NONE)
+               /* Initial table load: acquire type of table. */
+               dm_set_md_type(md, dm_table_get_type(t));
+       else if (dm_get_md_type(md) != dm_table_get_type(t)) {
+               DMWARN("can't change device type after initial table load.");
+               dm_table_destroy(t);
+               dm_unlock_md_type(md);
+               r = -EINVAL;
+               goto out;
+       }
+       dm_unlock_md_type(md);
+
+       /* stage inactive table */
        down_write(&_hash_lock);
        hc = dm_get_mdptr(md);
        if (!hc || hc->md != md) {
@@ -1186,7 +1220,7 @@ static int table_load(struct dm_ioctl *param, size_t param_size)
        up_write(&_hash_lock);
 
        param->flags |= DM_INACTIVE_PRESENT_FLAG;
-       r = __dev_status(md, param);
+       __dev_status(md, param);
 
 out:
        dm_put(md);
@@ -1196,7 +1230,6 @@ out:
 
 static int table_clear(struct dm_ioctl *param, size_t param_size)
 {
-       int r;
        struct hash_cell *hc;
        struct mapped_device *md;
 
@@ -1216,11 +1249,12 @@ static int table_clear(struct dm_ioctl *param, size_t param_size)
 
        param->flags &= ~DM_INACTIVE_PRESENT_FLAG;
 
-       r = __dev_status(hc->md, param);
+       __dev_status(hc->md, param);
        md = hc->md;
        up_write(&_hash_lock);
        dm_put(md);
-       return r;
+
+       return 0;
 }
 
 /*
@@ -1265,7 +1299,6 @@ static void retrieve_deps(struct dm_table *table,
 
 static int table_deps(struct dm_ioctl *param, size_t param_size)
 {
-       int r = 0;
        struct mapped_device *md;
        struct dm_table *table;
 
@@ -1273,9 +1306,7 @@ static int table_deps(struct dm_ioctl *param, size_t param_size)
        if (!md)
                return -ENXIO;
 
-       r = __dev_status(md, param);
-       if (r)
-               goto out;
+       __dev_status(md, param);
 
        table = dm_get_live_or_inactive_table(md, param);
        if (table) {
@@ -1283,9 +1314,9 @@ static int table_deps(struct dm_ioctl *param, size_t param_size)
                dm_table_put(table);
        }
 
- out:
        dm_put(md);
-       return r;
+
+       return 0;
 }
 
 /*
@@ -1294,7 +1325,6 @@ static int table_deps(struct dm_ioctl *param, size_t param_size)
  */
 static int table_status(struct dm_ioctl *param, size_t param_size)
 {
-       int r;
        struct mapped_device *md;
        struct dm_table *table;
 
@@ -1302,9 +1332,7 @@ static int table_status(struct dm_ioctl *param, size_t param_size)
        if (!md)
                return -ENXIO;
 
-       r = __dev_status(md, param);
-       if (r)
-               goto out;
+       __dev_status(md, param);
 
        table = dm_get_live_or_inactive_table(md, param);
        if (table) {
@@ -1312,9 +1340,9 @@ static int table_status(struct dm_ioctl *param, size_t param_size)
                dm_table_put(table);
        }
 
-out:
        dm_put(md);
-       return r;
+
+       return 0;
 }
 
 /*
@@ -1333,10 +1361,6 @@ static int target_message(struct dm_ioctl *param, size_t param_size)
        if (!md)
                return -ENXIO;
 
-       r = __dev_status(md, param);
-       if (r)
-               goto out;
-
        if (tmsg < (struct dm_target_msg *) param->data ||
            invalid_str(tmsg->message, (void *) param + param_size)) {
                DMWARN("Invalid target message parameters.");
@@ -1593,6 +1617,7 @@ static long dm_compat_ctl_ioctl(struct file *file, uint command, ulong u)
 #endif
 
 static const struct file_operations _ctl_fops = {
+       .open = nonseekable_open,
        .unlocked_ioctl  = dm_ctl_ioctl,
        .compat_ioctl = dm_compat_ctl_ioctl,
        .owner   = THIS_MODULE,