]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/md/dm-mpath.c
Merge branch 'for-next' of git://git.samba.org/sfrench/cifs-2.6
[karo-tx-linux.git] / drivers / md / dm-mpath.c
index 422a9fdeb53e641d97acec4793ab00747dde9b1d..aa009e86587189a20a4ccf4734ad9d999cb5701f 100644 (file)
@@ -93,10 +93,6 @@ struct multipath {
        unsigned pg_init_count;         /* Number of times pg_init called */
        unsigned pg_init_delay_msecs;   /* Number of msecs before pg_init retry */
 
-       unsigned queue_size;
-       struct work_struct process_queued_ios;
-       struct list_head queued_ios;
-
        struct work_struct trigger_event;
 
        /*
@@ -121,9 +117,9 @@ typedef int (*action_fn) (struct pgpath *pgpath);
 static struct kmem_cache *_mpio_cache;
 
 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 int __pgpath_busy(struct pgpath *pgpath);
 
 
 /*-----------------------------------------------
@@ -195,11 +191,9 @@ static struct multipath *alloc_multipath(struct dm_target *ti)
        m = kzalloc(sizeof(*m), GFP_KERNEL);
        if (m) {
                INIT_LIST_HEAD(&m->priority_groups);
-               INIT_LIST_HEAD(&m->queued_ios);
                spin_lock_init(&m->lock);
                m->queue_io = 1;
                m->pg_init_delay_msecs = DM_PG_INIT_DELAY_DEFAULT;
-               INIT_WORK(&m->process_queued_ios, process_queued_ios);
                INIT_WORK(&m->trigger_event, trigger_event);
                init_waitqueue_head(&m->pg_init_wait);
                mutex_init(&m->work_mutex);
@@ -256,13 +250,21 @@ static void clear_mapinfo(struct multipath *m, union map_info *info)
  * Path selection
  *-----------------------------------------------*/
 
-static void __pg_init_all_paths(struct multipath *m)
+static int __pg_init_all_paths(struct multipath *m)
 {
        struct pgpath *pgpath;
        unsigned long pg_init_delay = 0;
 
+       if (m->pg_init_in_progress || m->pg_init_disabled)
+               return 0;
+
        m->pg_init_count++;
        m->pg_init_required = 0;
+
+       /* Check here to reset pg_init_required */
+       if (!m->current_pg)
+               return 0;
+
        if (m->pg_init_delay_retry)
                pg_init_delay = msecs_to_jiffies(m->pg_init_delay_msecs != DM_PG_INIT_DELAY_DEFAULT ?
                                                 m->pg_init_delay_msecs : DM_PG_INIT_DELAY_MSECS);
@@ -274,6 +276,7 @@ static void __pg_init_all_paths(struct multipath *m)
                                       pg_init_delay))
                        m->pg_init_in_progress++;
        }
+       return m->pg_init_in_progress;
 }
 
 static void __switch_pg(struct multipath *m, struct pgpath *pgpath)
@@ -365,19 +368,26 @@ failed:
  */
 static int __must_push_back(struct multipath *m)
 {
-       return (m->queue_if_no_path != m->saved_queue_if_no_path &&
-               dm_noflush_suspending(m->ti));
+       return (m->queue_if_no_path ||
+               (m->queue_if_no_path != m->saved_queue_if_no_path &&
+                dm_noflush_suspending(m->ti)));
 }
 
-static int map_io(struct multipath *m, struct request *clone,
-                 union map_info *map_context, unsigned was_queued)
+#define pg_ready(m) (!(m)->queue_io && !(m)->pg_init_required)
+
+/*
+ * Map cloned requests
+ */
+static int multipath_map(struct dm_target *ti, struct request *clone,
+                        union map_info *map_context)
 {
-       int r = DM_MAPIO_REMAPPED;
+       struct multipath *m = (struct multipath *) ti->private;
+       int r = DM_MAPIO_REQUEUE;
        size_t nr_bytes = blk_rq_bytes(clone);
        unsigned long flags;
        struct pgpath *pgpath;
        struct block_device *bdev;
-       struct dm_mpath_io *mpio = map_context->ptr;
+       struct dm_mpath_io *mpio;
 
        spin_lock_irqsave(&m->lock, flags);
 
@@ -388,38 +398,33 @@ static int map_io(struct multipath *m, struct request *clone,
 
        pgpath = m->current_pgpath;
 
-       if (was_queued)
-               m->queue_size--;
-
-       if (m->pg_init_required) {
-               if (!m->pg_init_in_progress)
-                       queue_work(kmultipathd, &m->process_queued_ios);
-               r = DM_MAPIO_REQUEUE;
-       } else if ((pgpath && m->queue_io) ||
-                  (!pgpath && m->queue_if_no_path)) {
-               /* Queue for the daemon to resubmit */
-               list_add_tail(&clone->queuelist, &m->queued_ios);
-               m->queue_size++;
-               if (!m->queue_io)
-                       queue_work(kmultipathd, &m->process_queued_ios);
-               pgpath = NULL;
-               r = DM_MAPIO_SUBMITTED;
-       } else if (pgpath) {
-               bdev = pgpath->path.dev->bdev;
-               clone->q = bdev_get_queue(bdev);
-               clone->rq_disk = bdev->bd_disk;
-       } else if (__must_push_back(m))
-               r = DM_MAPIO_REQUEUE;
-       else
-               r = -EIO;       /* Failed */
+       if (!pgpath) {
+               if (!__must_push_back(m))
+                       r = -EIO;       /* Failed */
+               goto out_unlock;
+       }
+       if (!pg_ready(m)) {
+               __pg_init_all_paths(m);
+               goto out_unlock;
+       }
+       if (set_mapinfo(m, map_context) < 0)
+               /* ENOMEM, requeue */
+               goto out_unlock;
 
+       bdev = pgpath->path.dev->bdev;
+       clone->q = bdev_get_queue(bdev);
+       clone->rq_disk = bdev->bd_disk;
+       clone->cmd_flags |= REQ_FAILFAST_TRANSPORT;
+       mpio = map_context->ptr;
        mpio->pgpath = pgpath;
        mpio->nr_bytes = nr_bytes;
-
-       if (r == DM_MAPIO_REMAPPED && pgpath->pg->ps.type->start_io)
-               pgpath->pg->ps.type->start_io(&pgpath->pg->ps, &pgpath->path,
+       if (pgpath->pg->ps.type->start_io)
+               pgpath->pg->ps.type->start_io(&pgpath->pg->ps,
+                                             &pgpath->path,
                                              nr_bytes);
+       r = DM_MAPIO_REMAPPED;
 
+out_unlock:
        spin_unlock_irqrestore(&m->lock, flags);
 
        return r;
@@ -440,76 +445,14 @@ static int queue_if_no_path(struct multipath *m, unsigned 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 && m->queue_size)
-               queue_work(kmultipathd, &m->process_queued_ios);
+       if (!m->queue_if_no_path)
+               dm_table_run_md_queue_async(m->ti->table);
 
        spin_unlock_irqrestore(&m->lock, flags);
 
        return 0;
 }
 
-/*-----------------------------------------------------------------
- * The multipath daemon is responsible for resubmitting queued ios.
- *---------------------------------------------------------------*/
-
-static void dispatch_queued_ios(struct multipath *m)
-{
-       int r;
-       unsigned long flags;
-       union map_info *info;
-       struct request *clone, *n;
-       LIST_HEAD(cl);
-
-       spin_lock_irqsave(&m->lock, flags);
-       list_splice_init(&m->queued_ios, &cl);
-       spin_unlock_irqrestore(&m->lock, flags);
-
-       list_for_each_entry_safe(clone, n, &cl, queuelist) {
-               list_del_init(&clone->queuelist);
-
-               info = dm_get_rq_mapinfo(clone);
-
-               r = map_io(m, clone, info, 1);
-               if (r < 0) {
-                       clear_mapinfo(m, info);
-                       dm_kill_unmapped_request(clone, r);
-               } else if (r == DM_MAPIO_REMAPPED)
-                       dm_dispatch_request(clone);
-               else if (r == DM_MAPIO_REQUEUE) {
-                       clear_mapinfo(m, info);
-                       dm_requeue_unmapped_request(clone);
-               }
-       }
-}
-
-static void process_queued_ios(struct work_struct *work)
-{
-       struct multipath *m =
-               container_of(work, struct multipath, process_queued_ios);
-       struct pgpath *pgpath = NULL;
-       unsigned must_queue = 1;
-       unsigned long flags;
-
-       spin_lock_irqsave(&m->lock, flags);
-
-       if (!m->current_pgpath)
-               __choose_pgpath(m, 0);
-
-       pgpath = m->current_pgpath;
-
-       if ((pgpath && !m->queue_io) ||
-           (!pgpath && !m->queue_if_no_path))
-               must_queue = 0;
-
-       if (m->pg_init_required && !m->pg_init_in_progress && pgpath &&
-           !m->pg_init_disabled)
-               __pg_init_all_paths(m);
-
-       spin_unlock_irqrestore(&m->lock, flags);
-       if (!must_queue)
-               dispatch_queued_ios(m);
-}
-
 /*
  * An event is triggered whenever a path is taken out of use.
  * Includes path failure and PG bypass.
@@ -971,27 +914,6 @@ static void multipath_dtr(struct dm_target *ti)
        free_multipath(m);
 }
 
-/*
- * Map cloned requests
- */
-static int multipath_map(struct dm_target *ti, struct request *clone,
-                        union map_info *map_context)
-{
-       int r;
-       struct multipath *m = (struct multipath *) ti->private;
-
-       if (set_mapinfo(m, map_context) < 0)
-               /* ENOMEM, requeue */
-               return DM_MAPIO_REQUEUE;
-
-       clone->cmd_flags |= REQ_FAILFAST_TRANSPORT;
-       r = map_io(m, clone, map_context, 0);
-       if (r < 0 || r == DM_MAPIO_REQUEUE)
-               clear_mapinfo(m, map_context);
-
-       return r;
-}
-
 /*
  * Take a path out of use.
  */
@@ -1054,9 +976,9 @@ static int reinstate_path(struct pgpath *pgpath)
 
        pgpath->is_active = 1;
 
-       if (!m->nr_valid_paths++ && m->queue_size) {
+       if (!m->nr_valid_paths++) {
                m->current_pgpath = NULL;
-               queue_work(kmultipathd, &m->process_queued_ios);
+               dm_table_run_md_queue_async(m->ti->table);
        } else if (m->hw_handler_name && (m->current_pg == pgpath->pg)) {
                if (queue_work(kmpath_handlerd, &pgpath->activate_path.work))
                        m->pg_init_in_progress++;
@@ -1252,11 +1174,12 @@ static void pg_init_done(void *data, int errors)
                /* Activations of other paths are still on going */
                goto out;
 
-       if (!m->pg_init_required)
-               m->queue_io = 0;
-
-       m->pg_init_delay_retry = delay_retry;
-       queue_work(kmultipathd, &m->process_queued_ios);
+       if (m->pg_init_required) {
+               m->pg_init_delay_retry = delay_retry;
+               if (__pg_init_all_paths(m))
+                       goto out;
+       }
+       m->queue_io = 0;
 
        /*
         * Wake up any thread waiting to suspend.
@@ -1272,8 +1195,11 @@ 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->is_active)
+               scsi_dh_activate(bdev_get_queue(pgpath->path.dev->bdev),
+                                pg_init_done, pgpath);
+       else
+               pg_init_done(pgpath, SCSI_DH_DEV_OFFLINED);
 }
 
 static int noretry_error(int error)
@@ -1433,7 +1359,7 @@ static void multipath_status(struct dm_target *ti, status_type_t type,
 
        /* Features */
        if (type == STATUSTYPE_INFO)
-               DMEMIT("2 %u %u ", m->queue_size, m->pg_init_count);
+               DMEMIT("2 %u %u ", m->queue_io, m->pg_init_count);
        else {
                DMEMIT("%u ", m->queue_if_no_path +
                              (m->pg_init_retries > 0) * 2 +
@@ -1552,7 +1478,7 @@ static int multipath_message(struct dm_target *ti, unsigned argc, char **argv)
        }
 
        if (argc != 2) {
-               DMWARN("Unrecognised multipath message received.");
+               DMWARN("Invalid multipath message arguments. Expected 2 arguments, got %d.", argc);
                goto out;
        }
 
@@ -1570,7 +1496,7 @@ static int multipath_message(struct dm_target *ti, unsigned argc, char **argv)
        else if (!strcasecmp(argv[0], "fail_path"))
                action = fail_path;
        else {
-               DMWARN("Unrecognised multipath message received.");
+               DMWARN("Unrecognised multipath message received: %s", argv[0]);
                goto out;
        }
 
@@ -1632,8 +1558,17 @@ static int multipath_ioctl(struct dm_target *ti, unsigned int cmd,
                        r = err;
        }
 
-       if (r == -ENOTCONN && !fatal_signal_pending(current))
-               queue_work(kmultipathd, &m->process_queued_ios);
+       if (r == -ENOTCONN && !fatal_signal_pending(current)) {
+               spin_lock_irqsave(&m->lock, flags);
+               if (!m->current_pg) {
+                       /* Path status changed, redo selection */
+                       __choose_pgpath(m, 0);
+               }
+               if (m->pg_init_required)
+                       __pg_init_all_paths(m);
+               spin_unlock_irqrestore(&m->lock, flags);
+               dm_table_run_md_queue_async(m->ti->table);
+       }
 
        return r ? : __blkdev_driver_ioctl(bdev, mode, cmd, arg);
 }
@@ -1684,7 +1619,7 @@ static int multipath_busy(struct dm_target *ti)
        spin_lock_irqsave(&m->lock, flags);
 
        /* pg_init in progress, requeue until done */
-       if (m->pg_init_in_progress) {
+       if (!pg_ready(m)) {
                busy = 1;
                goto out;
        }
@@ -1737,7 +1672,7 @@ out:
  *---------------------------------------------------------------*/
 static struct target_type multipath_target = {
        .name = "multipath",
-       .version = {1, 6, 0},
+       .version = {1, 7, 0},
        .module = THIS_MODULE,
        .ctr = multipath_ctr,
        .dtr = multipath_dtr,