Merge branch 'e1000-fixes' of git://198.78.49.142/~jbrandeb/linux-2.6
[powerpc.git] / drivers / md / dm-mpath.c
index 98da8ee..f72a82f 100644 (file)
@@ -63,6 +63,7 @@ struct multipath {
        unsigned nr_priority_groups;
        struct list_head priority_groups;
        unsigned pg_init_required;      /* pg_init needs calling? */
+       unsigned pg_init_in_progress;   /* Only one pg_init allowed at once */
 
        unsigned nr_valid_paths;        /* Total number of usable paths */
        struct pgpath *current_pgpath;
@@ -308,7 +309,8 @@ static int map_io(struct multipath *m, struct bio *bio, struct mpath_io *mpio,
                /* Queue for the daemon to resubmit */
                bio_list_add(&m->queued_ios, bio);
                m->queue_size++;
-               if (m->pg_init_required || !m->queue_io)
+               if ((m->pg_init_required && !m->pg_init_in_progress) ||
+                   !m->queue_io)
                        queue_work(kmultipathd, &m->process_queued_ios);
                pgpath = NULL;
                r = 0;
@@ -327,15 +329,19 @@ static int map_io(struct multipath *m, struct bio *bio, struct mpath_io *mpio,
 /*
  * If we run out of usable paths, should we queue I/O or error it?
  */
-static int queue_if_no_path(struct multipath *m, unsigned queue_if_no_path)
+static int queue_if_no_path(struct multipath *m, unsigned queue_if_no_path,
+                           unsigned save_old_value)
 {
        unsigned long flags;
 
        spin_lock_irqsave(&m->lock, flags);
 
-       m->saved_queue_if_no_path = m->queue_if_no_path;
+       if (save_old_value)
+               m->saved_queue_if_no_path = m->queue_if_no_path;
+       else
+               m->saved_queue_if_no_path = queue_if_no_path;
        m->queue_if_no_path = queue_if_no_path;
-       if (!m->queue_if_no_path)
+       if (!m->queue_if_no_path && m->queue_size)
                queue_work(kmultipathd, &m->process_queued_ios);
 
        spin_unlock_irqrestore(&m->lock, flags);
@@ -380,25 +386,31 @@ static void process_queued_ios(void *data)
 {
        struct multipath *m = (struct multipath *) data;
        struct hw_handler *hwh = &m->hw_handler;
-       struct pgpath *pgpath;
-       unsigned init_required, must_queue = 0;
+       struct pgpath *pgpath = NULL;
+       unsigned init_required = 0, must_queue = 1;
        unsigned long flags;
 
        spin_lock_irqsave(&m->lock, flags);
 
+       if (!m->queue_size)
+               goto out;
+
        if (!m->current_pgpath)
                __choose_pgpath(m);
 
        pgpath = m->current_pgpath;
 
-       if ((pgpath && m->queue_io) ||
-           (!pgpath && m->queue_if_no_path))
-               must_queue = 1;
+       if ((pgpath && !m->queue_io) ||
+           (!pgpath && !m->queue_if_no_path))
+               must_queue = 0;
 
-       init_required = m->pg_init_required;
-       if (init_required)
+       if (m->pg_init_required && !m->pg_init_in_progress) {
                m->pg_init_required = 0;
+               m->pg_init_in_progress = 1;
+               init_required = 1;
+       }
 
+out:
        spin_unlock_irqrestore(&m->lock, flags);
 
        if (init_required)
@@ -669,7 +681,7 @@ static int parse_features(struct arg_set *as, struct multipath *m,
                return 0;
 
        if (!strnicmp(shift(as), MESG_STR("queue_if_no_path")))
-               return queue_if_no_path(m, 1);
+               return queue_if_no_path(m, 1, 0);
        else {
                ti->error = "Unrecognised multipath feature request";
                return -EINVAL;
@@ -843,7 +855,7 @@ static int reinstate_path(struct pgpath *pgpath)
        pgpath->path.is_active = 1;
 
        m->current_pgpath = NULL;
-       if (!m->nr_valid_paths++)
+       if (!m->nr_valid_paths++ && m->queue_size)
                queue_work(kmultipathd, &m->process_queued_ios);
 
        queue_work(kmultipathd, &m->trigger_event);
@@ -969,12 +981,13 @@ void dm_pg_init_complete(struct path *path, unsigned err_flags)
                bypass_pg(m, pg, 1);
 
        spin_lock_irqsave(&m->lock, flags);
-       if (!err_flags)
-               m->queue_io = 0;
-       else {
+       if (err_flags) {
                m->current_pgpath = NULL;
                m->current_pg = NULL;
-       }
+       } else if (!m->pg_init_required)
+               m->queue_io = 0;
+
+       m->pg_init_in_progress = 0;
        queue_work(kmultipathd, &m->process_queued_ios);
        spin_unlock_irqrestore(&m->lock, flags);
 }
@@ -987,6 +1000,7 @@ static int do_end_io(struct multipath *m, struct bio *bio,
 {
        struct hw_handler *hwh = &m->hw_handler;
        unsigned err_flags = MP_FAIL_PATH;      /* Default behavior */
+       unsigned long flags;
 
        if (!error)
                return 0;       /* I/O complete */
@@ -997,17 +1011,17 @@ static int do_end_io(struct multipath *m, struct bio *bio,
        if (error == -EOPNOTSUPP)
                return error;
 
-       spin_lock(&m->lock);
+       spin_lock_irqsave(&m->lock, flags);
        if (!m->nr_valid_paths) {
                if (!m->queue_if_no_path) {
-                       spin_unlock(&m->lock);
+                       spin_unlock_irqrestore(&m->lock, flags);
                        return -EIO;
                } else {
-                       spin_unlock(&m->lock);
+                       spin_unlock_irqrestore(&m->lock, flags);
                        goto requeue;
                }
        }
-       spin_unlock(&m->lock);
+       spin_unlock_irqrestore(&m->lock, flags);
 
        if (hwh->type && hwh->type->error)
                err_flags = hwh->type->error(hwh, bio);
@@ -1027,12 +1041,12 @@ static int do_end_io(struct multipath *m, struct bio *bio,
        dm_bio_restore(&mpio->details, bio);
 
        /* queue for the daemon to resubmit or fail */
-       spin_lock(&m->lock);
+       spin_lock_irqsave(&m->lock, flags);
        bio_list_add(&m->queued_ios, bio);
        m->queue_size++;
        if (!m->queue_io)
                queue_work(kmultipathd, &m->process_queued_ios);
-       spin_unlock(&m->lock);
+       spin_unlock_irqrestore(&m->lock, flags);
 
        return 1;       /* io not complete */
 }
@@ -1068,7 +1082,7 @@ static void multipath_presuspend(struct dm_target *ti)
 {
        struct multipath *m = (struct multipath *) ti->private;
 
-       queue_if_no_path(m, 0);
+       queue_if_no_path(m, 0, 1);
 }
 
 /*
@@ -1213,9 +1227,9 @@ static int multipath_message(struct dm_target *ti, unsigned argc, char **argv)
 
        if (argc == 1) {
                if (!strnicmp(argv[0], MESG_STR("queue_if_no_path")))
-                       return queue_if_no_path(m, 1);
+                       return queue_if_no_path(m, 1, 0);
                else if (!strnicmp(argv[0], MESG_STR("fail_if_no_path")))
-                       return queue_if_no_path(m, 0);
+                       return queue_if_no_path(m, 0, 0);
        }
 
        if (argc != 2)