dm mpath: simplify failure path of dm_multipath_init()
[pandora-kernel.git] / drivers / md / dm-mpath.c
index 5e0090e..8483407 100644 (file)
@@ -84,6 +84,7 @@ struct multipath {
        unsigned queue_io;              /* Must we queue all I/O? */
        unsigned queue_if_no_path;      /* Queue I/O if last path fails? */
        unsigned saved_queue_if_no_path;/* Saved state during suspension */
+       unsigned pg_init_disabled:1;    /* pg_init is not currently allowed */
        unsigned pg_init_retries;       /* Number of times to retry pg_init */
        unsigned pg_init_count;         /* Number of times pg_init called */
        unsigned pg_init_delay_msecs;   /* Number of msecs before pg_init retry */
@@ -473,7 +474,8 @@ static void process_queued_ios(struct work_struct *work)
            (!pgpath && !m->queue_if_no_path))
                must_queue = 0;
 
-       if (m->pg_init_required && !m->pg_init_in_progress && pgpath)
+       if (m->pg_init_required && !m->pg_init_in_progress && pgpath &&
+           !m->pg_init_disabled)
                __pg_init_all_paths(m);
 
 out:
@@ -698,8 +700,8 @@ static int parse_hw_handler(struct dm_arg_set *as, struct multipath *m)
                return 0;
 
        m->hw_handler_name = kstrdup(dm_shift_arg(as), GFP_KERNEL);
-       request_module("scsi_dh_%s", m->hw_handler_name);
-       if (scsi_dh_handler_exist(m->hw_handler_name) == 0) {
+       if (!try_then_request_module(scsi_dh_handler_exist(m->hw_handler_name),
+                                    "scsi_dh_%s", m->hw_handler_name)) {
                ti->error = "unknown hardware handler type";
                ret = -EINVAL;
                goto fail;
@@ -887,10 +889,20 @@ static void multipath_wait_for_pg_init_completion(struct multipath *m)
 
 static void flush_multipath_work(struct multipath *m)
 {
+       unsigned long flags;
+
+       spin_lock_irqsave(&m->lock, flags);
+       m->pg_init_disabled = 1;
+       spin_unlock_irqrestore(&m->lock, flags);
+
        flush_workqueue(kmpath_handlerd);
        multipath_wait_for_pg_init_completion(m);
        flush_workqueue(kmultipathd);
        flush_work_sync(&m->trigger_event);
+
+       spin_lock_irqsave(&m->lock, flags);
+       m->pg_init_disabled = 0;
+       spin_unlock_irqrestore(&m->lock, flags);
 }
 
 static void multipath_dtr(struct dm_target *ti)
@@ -1111,7 +1123,7 @@ static int pg_init_limit_reached(struct multipath *m, struct pgpath *pgpath)
 
        spin_lock_irqsave(&m->lock, flags);
 
-       if (m->pg_init_count <= m->pg_init_retries)
+       if (m->pg_init_count <= m->pg_init_retries && !m->pg_init_disabled)
                m->pg_init_required = 1;
        else
                limit_reached = 1;
@@ -1323,8 +1335,8 @@ static void multipath_resume(struct dm_target *ti)
  *     [priority selector-name num_ps_args [ps_args]*
  *      num_paths num_selector_args [path_dev [selector_args]* ]+ ]+
  */
-static int multipath_status(struct dm_target *ti, status_type_t type,
-                           char *result, unsigned int maxlen)
+static void multipath_status(struct dm_target *ti, status_type_t type,
+                            char *result, unsigned maxlen)
 {
        int sz = 0;
        unsigned long flags;
@@ -1427,8 +1439,6 @@ static int multipath_status(struct dm_target *ti, status_type_t type,
        }
 
        spin_unlock_irqrestore(&m->lock, flags);
-
-       return 0;
 }
 
 static int multipath_message(struct dm_target *ti, unsigned argc, char **argv)
@@ -1520,6 +1530,12 @@ static int multipath_ioctl(struct dm_target *ti, unsigned int cmd,
 
        spin_unlock_irqrestore(&m->lock, flags);
 
+       /*
+        * Only pass ioctls through if the device sizes match exactly.
+        */
+       if (!r && ti->len != i_size_read(bdev->bd_inode) >> SECTOR_SHIFT)
+               r = scsi_verify_blk_ioctl(NULL, cmd);
+
        return r ? : __blkdev_driver_ioctl(bdev, mode, cmd, arg);
 }
 
@@ -1617,7 +1633,7 @@ out:
  *---------------------------------------------------------------*/
 static struct target_type multipath_target = {
        .name = "multipath",
-       .version = {1, 3, 0},
+       .version = {1, 3, 2},
        .module = THIS_MODULE,
        .ctr = multipath_ctr,
        .dtr = multipath_dtr,
@@ -1645,16 +1661,15 @@ static int __init dm_multipath_init(void)
        r = dm_register_target(&multipath_target);
        if (r < 0) {
                DMERR("register failed %d", r);
-               kmem_cache_destroy(_mpio_cache);
-               return -EINVAL;
+               r = -EINVAL;
+               goto bad_register_target;
        }
 
        kmultipathd = alloc_workqueue("kmpathd", WQ_MEM_RECLAIM, 0);
        if (!kmultipathd) {
                DMERR("failed to create workqueue kmpathd");
-               dm_unregister_target(&multipath_target);
-               kmem_cache_destroy(_mpio_cache);
-               return -ENOMEM;
+               r = -ENOMEM;
+               goto bad_alloc_kmultipathd;
        }
 
        /*
@@ -1667,16 +1682,23 @@ static int __init dm_multipath_init(void)
                                                  WQ_MEM_RECLAIM);
        if (!kmpath_handlerd) {
                DMERR("failed to create workqueue kmpath_handlerd");
-               destroy_workqueue(kmultipathd);
-               dm_unregister_target(&multipath_target);
-               kmem_cache_destroy(_mpio_cache);
-               return -ENOMEM;
+               r = -ENOMEM;
+               goto bad_alloc_kmpath_handlerd;
        }
 
        DMINFO("version %u.%u.%u loaded",
               multipath_target.version[0], multipath_target.version[1],
               multipath_target.version[2]);
 
+       return 0;
+
+bad_alloc_kmpath_handlerd:
+       destroy_workqueue(kmultipathd);
+bad_alloc_kmultipathd:
+       dm_unregister_target(&multipath_target);
+bad_register_target:
+       kmem_cache_destroy(_mpio_cache);
+
        return r;
 }