- Update to 3.4-rc7.
[linux-flexiantxendom0-3.2.10.git] / drivers / md / dm-mpath.c
index 754f38f..6195bff 100644 (file)
@@ -56,6 +56,8 @@ struct priority_group {
        struct list_head pgpaths;
 };
 
+#define FEATURE_NO_PARTITIONS 1
+
 /* Multipath context */
 struct multipath {
        struct list_head list;
@@ -87,6 +89,7 @@ struct multipath {
        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 */
+       unsigned features;              /* Additional selected features */
 
        struct work_struct process_queued_ios;
        struct list_head queued_ios;
@@ -159,12 +162,9 @@ static struct priority_group *alloc_priority_group(void)
 static void free_pgpaths(struct list_head *pgpaths, struct dm_target *ti)
 {
        struct pgpath *pgpath, *tmp;
-       struct multipath *m = ti->private;
 
        list_for_each_entry_safe(pgpath, tmp, pgpaths, list) {
                list_del(&pgpath->list);
-               if (m->hw_handler_name)
-                       scsi_dh_detach(bdev_get_queue(pgpath->path.dev->bdev));
                dm_put_device(ti, pgpath->path.dev);
                free_pgpath(pgpath);
        }
@@ -299,6 +299,11 @@ static int __choose_path_in_pg(struct multipath *m, struct priority_group *pg,
 
        m->current_pgpath = path_to_pgpath(path);
 
+       if (!m->current_pgpath->path.dev) {
+               m->current_pgpath = NULL;
+               return -ENODEV;
+       }
+
        if (m->current_pg != pg)
                __switch_pg(m, m->current_pgpath);
 
@@ -565,6 +570,7 @@ static struct pgpath *parse_path(struct dm_arg_set *as, struct path_selector *ps
 {
        int r;
        struct pgpath *p;
+       char *path;
        struct multipath *m = ti->private;
 
        /* we need at least a path arg */
@@ -577,30 +583,57 @@ static struct pgpath *parse_path(struct dm_arg_set *as, struct path_selector *ps
        if (!p)
                return ERR_PTR(-ENOMEM);
 
-       r = dm_get_device(ti, dm_shift_arg(as), dm_table_get_mode(ti->table),
+       path = dm_shift_arg(as);
+       r = dm_get_device(ti, path, dm_table_get_mode(ti->table),
                          &p->path.dev);
        if (r) {
-               ti->error = "error getting device";
-               goto bad;
+               unsigned major, minor;
+
+               /* Try to add a failed device */
+               if (r == -ENXIO && sscanf(path, "%u:%u", &major, &minor) == 2) {
+                       dev_t dev;
+
+                       /* Extract the major/minor numbers */
+                       dev = MKDEV(major, minor);
+                       if (MAJOR(dev) != major || MINOR(dev) != minor) {
+                               /* Nice try, didn't work */
+                               DMWARN("Invalid device path %s", path);
+                               ti->error = "error converting devnum";
+                               goto bad;
+                       }
+                       DMWARN("adding disabled device %d:%d", major, minor);
+                       p->path.dev = NULL;
+                       format_dev_t(p->path.pdev, dev);
+                       p->is_active = 0;
+               } else {
+                       ti->error = "error getting device";
+                       goto bad;
+               }
+       } else {
+               memcpy(p->path.pdev, p->path.dev->name, 16);
        }
 
-       if (m->hw_handler_name) {
+       if (p->path.dev) {
                struct request_queue *q = bdev_get_queue(p->path.dev->bdev);
 
-               r = scsi_dh_attach(q, m->hw_handler_name);
-               if (r == -EBUSY) {
-                       /*
-                        * Already attached to different hw_handler,
-                        * try to reattach with correct one.
-                        */
-                       scsi_dh_detach(q);
+               if (m->hw_handler_name) {
                        r = scsi_dh_attach(q, m->hw_handler_name);
-               }
-
-               if (r < 0) {
-                       ti->error = "error attaching hardware handler";
-                       dm_put_device(ti, p->path.dev);
-                       goto bad;
+                       if (r == -EBUSY) {
+                               /*
+                                * Already attached to different hw_handler,
+                                * try to reattach with correct one.
+                                */
+                               scsi_dh_detach(q);
+                               r = scsi_dh_attach(q, m->hw_handler_name);
+                       }
+                       if (r < 0) {
+                               ti->error = "error attaching hardware handler";
+                               dm_put_device(ti, p->path.dev);
+                               goto bad;
+                       }
+               } else {
+                       /* Play safe and detach hardware handler */
+                       scsi_dh_detach(q);
                }
 
                if (m->hw_handler_params) {
@@ -621,6 +654,11 @@ static struct pgpath *parse_path(struct dm_arg_set *as, struct path_selector *ps
                goto bad;
        }
 
+       if (!p->is_active) {
+               ps->type->fail_path(ps, &p->path);
+               p->fail_count++;
+               m->nr_valid_paths--;
+       }
        return p;
 
  bad:
@@ -779,6 +817,10 @@ static int parse_features(struct dm_arg_set *as, struct multipath *m)
                        continue;
                }
 
+               if (!strcasecmp(arg_name, "no_partitions")) {
+                       m->features |= FEATURE_NO_PARTITIONS;
+                       continue;
+               }
                if (!strcasecmp(arg_name, "pg_init_retries") &&
                    (argc >= 1)) {
                        r = dm_read_arg(_args + 1, as, &m->pg_init_retries, &ti->error);
@@ -955,7 +997,7 @@ static int fail_path(struct pgpath *pgpath)
        if (!pgpath->is_active)
                goto out;
 
-       DMWARN("Failing path %s.", pgpath->path.dev->name);
+       DMWARN("Failing path %s.", pgpath->path.pdev);
 
        pgpath->pg->ps.type->fail_path(&pgpath->pg->ps, &pgpath->path);
        pgpath->is_active = 0;
@@ -967,7 +1009,7 @@ static int fail_path(struct pgpath *pgpath)
                m->current_pgpath = NULL;
 
        dm_path_uevent(DM_UEVENT_PATH_FAILED, m->ti,
-                     pgpath->path.dev->name, m->nr_valid_paths);
+                      pgpath->path.pdev, m->nr_valid_paths);
 
        schedule_work(&m->trigger_event);
 
@@ -991,6 +1033,12 @@ static int reinstate_path(struct pgpath *pgpath)
        if (pgpath->is_active)
                goto out;
 
+       if (!pgpath->path.dev) {
+               DMWARN("Cannot reinstate disabled path %s", pgpath->path.pdev);
+               r = -ENODEV;
+               goto out;
+       }
+
        if (!pgpath->pg->ps.type->reinstate_path) {
                DMWARN("Reinstate path not supported by path selector %s",
                       pgpath->pg->ps.type->name);
@@ -1013,7 +1061,7 @@ static int reinstate_path(struct pgpath *pgpath)
        }
 
        dm_path_uevent(DM_UEVENT_PATH_REINSTATED, m->ti,
-                     pgpath->path.dev->name, m->nr_valid_paths);
+                      pgpath->path.pdev, m->nr_valid_paths);
 
        schedule_work(&m->trigger_event);
 
@@ -1033,6 +1081,9 @@ static int action_dev(struct multipath *m, struct dm_dev *dev,
        struct pgpath *pgpath;
        struct priority_group *pg;
 
+       if (!dev)
+               return 0;
+
        list_for_each_entry(pg, &m->priority_groups, list) {
                list_for_each_entry(pgpath, &pg->pgpaths, list) {
                        if (pgpath->path.dev == dev)
@@ -1156,8 +1207,9 @@ static void pg_init_done(void *data, int errors)
                        errors = 0;
                        break;
                }
-               DMERR("Could not failover the device: Handler scsi_dh_%s "
-                     "Error %d.", m->hw_handler_name, errors);
+               DMERR("Count not failover device %s: Handler scsi_dh_%s "
+                     "was not loaded.", pgpath->path.dev->name,
+                     m->hw_handler_name);
                /*
                 * Fail path for now, so we do not ping pong
                 */
@@ -1170,6 +1222,10 @@ static void pg_init_done(void *data, int errors)
                 */
                bypass_pg(m, pg, 1);
                break;
+       case SCSI_DH_DEV_OFFLINED:
+               DMWARN("Device %s offlined.", pgpath->path.dev->name);
+               errors = 0;
+               break;
        case SCSI_DH_RETRY:
                /* Wait before retrying. */
                delay_retry = 1;
@@ -1191,7 +1247,8 @@ static void pg_init_done(void *data, int errors)
        spin_lock_irqsave(&m->lock, flags);
        if (errors) {
                if (pgpath == m->current_pgpath) {
-                       DMERR("Could not failover device. Error %d.", errors);
+                       DMERR("Could not failover device %s, error %d.",
+                             pgpath->path.dev->name, errors);
                        m->current_pgpath = NULL;
                        m->current_pg = NULL;
                }
@@ -1222,8 +1279,9 @@ static void activate_path(struct work_struct *work)
        struct pgpath *pgpath =
                container_of(work, struct pgpath, activate_path.work);
 
-       scsi_dh_activate(bdev_get_queue(pgpath->path.dev->bdev),
-                               pg_init_done, pgpath);
+       if (pgpath->path.dev)
+               scsi_dh_activate(bdev_get_queue(pgpath->path.dev->bdev),
+                                pg_init_done, pgpath);
 }
 
 /*
@@ -1362,11 +1420,14 @@ static int multipath_status(struct dm_target *ti, status_type_t type,
        else {
                DMEMIT("%u ", m->queue_if_no_path +
                              (m->pg_init_retries > 0) * 2 +
-                             (m->pg_init_delay_msecs != DM_PG_INIT_DELAY_DEFAULT) * 2);
+                             (m->pg_init_delay_msecs != DM_PG_INIT_DELAY_DEFAULT) * 2 +
+                             (m->features & FEATURE_NO_PARTITIONS));
                if (m->queue_if_no_path)
                        DMEMIT("queue_if_no_path ");
                if (m->pg_init_retries)
                        DMEMIT("pg_init_retries %u ", m->pg_init_retries);
+               if (m->features & FEATURE_NO_PARTITIONS)
+                       DMEMIT("no_partitions ");
                if (m->pg_init_delay_msecs != DM_PG_INIT_DELAY_DEFAULT)
                        DMEMIT("pg_init_delay_msecs %u ", m->pg_init_delay_msecs);
        }
@@ -1410,7 +1471,7 @@ static int multipath_status(struct dm_target *ti, status_type_t type,
                               pg->ps.type->info_args);
 
                        list_for_each_entry(p, &pg->pgpaths, list) {
-                               DMEMIT("%s %s %u ", p->path.dev->name,
+                               DMEMIT("%s %s %u ", p->path.pdev,
                                       p->is_active ? "A" : "F",
                                       p->fail_count);
                                if (pg->ps.type->status)
@@ -1436,7 +1497,7 @@ static int multipath_status(struct dm_target *ti, status_type_t type,
                               pg->ps.type->table_args);
 
                        list_for_each_entry(p, &pg->pgpaths, list) {
-                               DMEMIT("%s ", p->path.dev->name);
+                               DMEMIT("%s ", p->path.pdev);
                                if (pg->ps.type->status)
                                        sz += pg->ps.type->status(&pg->ps,
                                              &p->path, type, result + sz,
@@ -1528,7 +1589,7 @@ static int multipath_ioctl(struct dm_target *ti, unsigned int cmd,
        if (!m->current_pgpath)
                __choose_pgpath(m, 0);
 
-       if (m->current_pgpath) {
+       if (m->current_pgpath && m->current_pgpath->path.dev) {
                bdev = m->current_pgpath->path.dev->bdev;
                mode = m->current_pgpath->path.dev->mode;
        }