]> err.no Git - linux-2.6/commitdiff
[SCSI] scsi_dh: Add a single threaded workqueue for initializing paths
authorChandra Seetharaman <sekharan@us.ibm.com>
Thu, 1 May 2008 21:50:22 +0000 (14:50 -0700)
committerJames Bottomley <James.Bottomley@HansenPartnership.com>
Thu, 5 Jun 2008 14:23:41 +0000 (09:23 -0500)
Before this patch set (SCSI hardware handlers), initialization of a
path was done asynchronously. Doing that requires a workqueue in each
device/hardware handler module and leads to unneccessary complication
in the device handler code, making it difficult to read the code and
follow the state diagram.

Moving that workqueue to this level makes the device handler code simpler.
Hence, the workqueue is moved to dm level.

A new workqueue is added instead of adding it to the existing workqueue
(kmpathd) for the following reasons:
1. Device activation has to happen faster, stacking them along
   with the other workqueue might lead to unnecessary delay
   in the activation of the path.
2. The effect could be felt the other way too. i.e the current
   events that are handled by the existing workqueue might get
   a delayed response.

Signed-off-by: Chandra Seetharaman <sekharan@us.ibm.com>
Acked-by: Alasdair G Kergon <agk@redhat.com>
Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
drivers/md/dm-mpath.c

index e54ff372d711b53e652220e8616ddf3d07995f5c..9b16788118d2e3c662483c9013f907a87030b125 100644 (file)
@@ -63,6 +63,7 @@ struct multipath {
        spinlock_t lock;
 
        const char *hw_handler_name;
+       struct work_struct activate_path;
        unsigned nr_priority_groups;
        struct list_head priority_groups;
        unsigned pg_init_required;      /* pg_init needs calling? */
@@ -107,10 +108,10 @@ typedef int (*action_fn) (struct pgpath *pgpath);
 
 static struct kmem_cache *_mpio_cache;
 
-static struct workqueue_struct *kmultipathd;
+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 pg_init_done(struct dm_path *, int);
+static void activate_path(struct work_struct *work);
 
 
 /*-----------------------------------------------
@@ -180,6 +181,7 @@ static struct multipath *alloc_multipath(struct dm_target *ti)
                m->queue_io = 1;
                INIT_WORK(&m->process_queued_ios, process_queued_ios);
                INIT_WORK(&m->trigger_event, trigger_event);
+               INIT_WORK(&m->activate_path, activate_path);
                m->mpio_pool = mempool_create_slab_pool(MIN_IOS, _mpio_cache);
                if (!m->mpio_pool) {
                        kfree(m);
@@ -432,11 +434,8 @@ static void process_queued_ios(struct work_struct *work)
 out:
        spin_unlock_irqrestore(&m->lock, flags);
 
-       if (init_required) {
-               struct dm_path *path = &pgpath->path;
-               int ret = scsi_dh_activate(bdev_get_queue(path->dev->bdev));
-               pg_init_done(path, ret);
-       }
+       if (init_required)
+               queue_work(kmpath_handlerd, &m->activate_path);
 
        if (!must_queue)
                dispatch_queued_ios(m);
@@ -791,6 +790,7 @@ static void multipath_dtr(struct dm_target *ti)
 {
        struct multipath *m = (struct multipath *) ti->private;
 
+       flush_workqueue(kmpath_handlerd);
        flush_workqueue(kmultipathd);
        free_multipath(m);
 }
@@ -1108,6 +1108,17 @@ static void pg_init_done(struct dm_path *path, int errors)
        spin_unlock_irqrestore(&m->lock, flags);
 }
 
+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;
+
+       ret = scsi_dh_activate(bdev_get_queue(path->dev->bdev));
+       pg_init_done(path, ret);
+}
+
 /*
  * end_io handling
  */
@@ -1451,6 +1462,21 @@ static int __init dm_multipath_init(void)
                return -ENOMEM;
        }
 
+       /*
+        * A separate workqueue is used to handle the device handlers
+        * to avoid overloading existing workqueue. Overloading the
+        * old workqueue would also create a bottleneck in the
+        * path of the storage hardware device activation.
+        */
+       kmpath_handlerd = create_singlethread_workqueue("kmpath_handlerd");
+       if (!kmpath_handlerd) {
+               DMERR("failed to create workqueue kmpath_handlerd");
+               destroy_workqueue(kmultipathd);
+               dm_unregister_target(&multipath_target);
+               kmem_cache_destroy(_mpio_cache);
+               return -ENOMEM;
+       }
+
        DMINFO("version %u.%u.%u loaded",
               multipath_target.version[0], multipath_target.version[1],
               multipath_target.version[2]);
@@ -1462,6 +1488,7 @@ static void __exit dm_multipath_exit(void)
 {
        int r;
 
+       destroy_workqueue(kmpath_handlerd);
        destroy_workqueue(kmultipathd);
 
        r = dm_unregister_target(&multipath_target);