]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/md/dm-mpath.c
dm mpath: add missing path switching locking
[karo-tx-linux.git] / drivers / md / dm-mpath.c
index 71dd65aa31b696aa37a894b0d7bd9cbdc2034123..c2fcf28b4c7022b2ec66e0c9d661e8a87b5279c8 100644 (file)
@@ -63,6 +63,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? */
@@ -146,6 +147,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 +156,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 +427,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))
@@ -1093,8 +1100,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);
 }