drm: wbinvd is cache coherent.
[safe/jmp/linux-2.6] / drivers / md / dm-mpath.c
index 71dd65a..9bf3460 100644 (file)
@@ -30,9 +30,11 @@ struct pgpath {
        struct list_head list;
 
        struct priority_group *pg;      /* Owning PG */
+       unsigned is_active;             /* Path status */
        unsigned fail_count;            /* Cumulative failure count */
 
        struct dm_path path;
+       struct work_struct deactivate_path;
 };
 
 #define path_to_pgpath(__pgp) container_of((__pgp), struct pgpath, path)
@@ -63,6 +65,7 @@ struct multipath {
 
        const char *hw_handler_name;
        struct work_struct activate_path;
+       struct pgpath *pgpath_to_activate;
        unsigned nr_priority_groups;
        struct list_head priority_groups;
        unsigned pg_init_required;      /* pg_init needs calling? */
@@ -111,6 +114,7 @@ static struct workqueue_struct *kmultipathd, *kmpath_handlerd;
 static void process_queued_ios(struct work_struct *work);
 static void trigger_event(struct work_struct *work);
 static void activate_path(struct work_struct *work);
+static void deactivate_path(struct work_struct *work);
 
 
 /*-----------------------------------------------
@@ -121,8 +125,10 @@ static struct pgpath *alloc_pgpath(void)
 {
        struct pgpath *pgpath = kzalloc(sizeof(*pgpath), GFP_KERNEL);
 
-       if (pgpath)
-               pgpath->path.is_active = 1;
+       if (pgpath) {
+               pgpath->is_active = 1;
+               INIT_WORK(&pgpath->deactivate_path, deactivate_path);
+       }
 
        return pgpath;
 }
@@ -132,6 +138,14 @@ static void free_pgpath(struct pgpath *pgpath)
        kfree(pgpath);
 }
 
+static void deactivate_path(struct work_struct *work)
+{
+       struct pgpath *pgpath =
+               container_of(work, struct pgpath, deactivate_path);
+
+       blk_abort_queue(pgpath->path.dev->bdev->bd_disk->queue);
+}
+
 static struct priority_group *alloc_priority_group(void)
 {
        struct priority_group *pg;
@@ -146,6 +160,7 @@ static struct priority_group *alloc_priority_group(void)
 
 static void free_pgpaths(struct list_head *pgpaths, struct dm_target *ti)
 {
+       unsigned long flags;
        struct pgpath *pgpath, *tmp;
        struct multipath *m = ti->private;
 
@@ -154,6 +169,10 @@ static void free_pgpaths(struct list_head *pgpaths, struct dm_target *ti)
                if (m->hw_handler_name)
                        scsi_dh_detach(bdev_get_queue(pgpath->path.dev->bdev));
                dm_put_device(ti, pgpath->path.dev);
+               spin_lock_irqsave(&m->lock, flags);
+               if (m->pgpath_to_activate == pgpath)
+                       m->pgpath_to_activate = NULL;
+               spin_unlock_irqrestore(&m->lock, flags);
                free_pgpath(pgpath);
        }
 }
@@ -421,6 +440,7 @@ static void process_queued_ios(struct work_struct *work)
                __choose_pgpath(m);
 
        pgpath = m->current_pgpath;
+       m->pgpath_to_activate = m->current_pgpath;
 
        if ((pgpath && !m->queue_io) ||
            (!pgpath && !m->queue_if_no_path))
@@ -556,12 +576,12 @@ static struct pgpath *parse_path(struct arg_set *as, struct path_selector *ps,
        /* we need at least a path arg */
        if (as->argc < 1) {
                ti->error = "no device given";
-               return NULL;
+               return ERR_PTR(-EINVAL);
        }
 
        p = alloc_pgpath();
        if (!p)
-               return NULL;
+               return ERR_PTR(-ENOMEM);
 
        r = dm_get_device(ti, shift(as), ti->begin, ti->len,
                          dm_table_get_mode(ti->table), &p->path.dev);
@@ -589,7 +609,7 @@ static struct pgpath *parse_path(struct arg_set *as, struct path_selector *ps,
 
  bad:
        free_pgpath(p);
-       return NULL;
+       return ERR_PTR(r);
 }
 
 static struct priority_group *parse_priority_group(struct arg_set *as,
@@ -607,14 +627,14 @@ static struct priority_group *parse_priority_group(struct arg_set *as,
 
        if (as->argc < 2) {
                as->argc = 0;
-               ti->error = "not enough priority group aruments";
-               return NULL;
+               ti->error = "not enough priority group arguments";
+               return ERR_PTR(-EINVAL);
        }
 
        pg = alloc_priority_group();
        if (!pg) {
                ti->error = "couldn't allocate priority group";
-               return NULL;
+               return ERR_PTR(-ENOMEM);
        }
        pg->m = m;
 
@@ -647,8 +667,10 @@ static struct priority_group *parse_priority_group(struct arg_set *as,
                path_args.argv = as->argv;
 
                pgpath = parse_path(&path_args, &pg->ps, ti);
-               if (!pgpath)
+               if (IS_ERR(pgpath)) {
+                       r = PTR_ERR(pgpath);
                        goto bad;
+               }
 
                pgpath->pg = pg;
                list_add_tail(&pgpath->list, &pg->pgpaths);
@@ -659,7 +681,7 @@ static struct priority_group *parse_priority_group(struct arg_set *as,
 
  bad:
        free_priority_group(pg, ti);
-       return NULL;
+       return ERR_PTR(r);
 }
 
 static int parse_hw_handler(struct arg_set *as, struct multipath *m)
@@ -778,8 +800,8 @@ static int multipath_ctr(struct dm_target *ti, unsigned int argc,
                struct priority_group *pg;
 
                pg = parse_priority_group(&as, m);
-               if (!pg) {
-                       r = -EINVAL;
+               if (IS_ERR(pg)) {
+                       r = PTR_ERR(pg);
                        goto bad;
                }
 
@@ -827,7 +849,7 @@ static int multipath_map(struct dm_target *ti, struct bio *bio,
        dm_bio_record(&mpio->details, bio);
 
        map_context->ptr = mpio;
-       bio->bi_rw |= (1 << BIO_RW_FAILFAST);
+       bio->bi_rw |= (1 << BIO_RW_FAILFAST_TRANSPORT);
        r = map_io(m, bio, mpio, 0);
        if (r < 0 || r == DM_MAPIO_REQUEUE)
                mempool_free(mpio, m->mpio_pool);
@@ -845,13 +867,13 @@ static int fail_path(struct pgpath *pgpath)
 
        spin_lock_irqsave(&m->lock, flags);
 
-       if (!pgpath->path.is_active)
+       if (!pgpath->is_active)
                goto out;
 
        DMWARN("Failing path %s.", pgpath->path.dev->name);
 
        pgpath->pg->ps.type->fail_path(&pgpath->pg->ps, &pgpath->path);
-       pgpath->path.is_active = 0;
+       pgpath->is_active = 0;
        pgpath->fail_count++;
 
        m->nr_valid_paths--;
@@ -863,6 +885,7 @@ static int fail_path(struct pgpath *pgpath)
                      pgpath->path.dev->name, m->nr_valid_paths);
 
        queue_work(kmultipathd, &m->trigger_event);
+       queue_work(kmultipathd, &pgpath->deactivate_path);
 
 out:
        spin_unlock_irqrestore(&m->lock, flags);
@@ -881,7 +904,7 @@ static int reinstate_path(struct pgpath *pgpath)
 
        spin_lock_irqsave(&m->lock, flags);
 
-       if (pgpath->path.is_active)
+       if (pgpath->is_active)
                goto out;
 
        if (!pgpath->pg->ps.type->reinstate_path) {
@@ -895,7 +918,7 @@ static int reinstate_path(struct pgpath *pgpath)
        if (r)
                goto out;
 
-       pgpath->path.is_active = 1;
+       pgpath->is_active = 1;
 
        m->current_pgpath = NULL;
        if (!m->nr_valid_paths++ && m->queue_size)
@@ -1093,8 +1116,15 @@ static void activate_path(struct work_struct *work)
        int ret;
        struct multipath *m =
                container_of(work, struct multipath, activate_path);
-       struct dm_path *path = &m->current_pgpath->path;
+       struct dm_path *path;
+       unsigned long flags;
 
+       spin_lock_irqsave(&m->lock, flags);
+       path = &m->pgpath_to_activate->path;
+       m->pgpath_to_activate = NULL;
+       spin_unlock_irqrestore(&m->lock, flags);
+       if (!path)
+               return;
        ret = scsi_dh_activate(bdev_get_queue(path->dev->bdev));
        pg_init_done(path, ret);
 }
@@ -1276,7 +1306,7 @@ static int multipath_status(struct dm_target *ti, status_type_t type,
 
                        list_for_each_entry(p, &pg->pgpaths, list) {
                                DMEMIT("%s %s %u ", p->path.dev->name,
-                                      p->path.is_active ? "A" : "F",
+                                      p->is_active ? "A" : "F",
                                       p->fail_count);
                                if (pg->ps.type->status)
                                        sz += pg->ps.type->status(&pg->ps,