[SCSI] scsi_dh: Make rdac hardware handler's activate() async
authorChandra Seetharaman <sekharan@us.ibm.com>
Wed, 21 Oct 2009 16:22:51 +0000 (09:22 -0700)
committerJames Bottomley <James.Bottomley@suse.de>
Fri, 4 Dec 2009 18:00:47 +0000 (12:00 -0600)
Batch up MODE_SELECT in rdac device handler.

LSI RDAC storage has the capability of handling mode selects for
multiple luns in a same command. Make use of that ability to send
as few MODE SELECTs as possible to the storage controller as possible.

This patch creates a work queue and queues up activate requests
when a MODE SELECT is sent down the wire. When that MODE SELECT
completes, it compiles queued up activate requests for multiple
luns into a single MODE SELECT.

This reduces the time to do failover/failback of large number of LUNS.

Signed-off-by: Babu Moger <babu.moger@lsi.com>
Signed-off-by: Chandra Seetharaman <sekharan@us.ibm.com>
Signed-off-by: James Bottomley <James.Bottomley@suse.de>
drivers/scsi/device_handler/scsi_dh_rdac.c

index be362adbd8e747523b9b87d4adfff6ae4ff16ede..47cfe1c49c3ec8ec3162fdd6fdb8cb06d6090ad4 100644 (file)
@@ -22,6 +22,7 @@
 #include <scsi/scsi.h>
 #include <scsi/scsi_eh.h>
 #include <scsi/scsi_dh.h>
+#include <linux/workqueue.h>
 
 #define RDAC_NAME "rdac"
 #define RDAC_RETRY_COUNT 5
@@ -138,7 +139,13 @@ struct rdac_controller {
        } mode_select;
        u8      index;
        u8      array_name[ARRAY_LABEL_LEN];
+       spinlock_t              ms_lock;
+       int                     ms_queued;
+       struct work_struct      ms_work;
+       struct scsi_device      *ms_sdev;
+       struct list_head        ms_head;
 };
+
 struct c8_inquiry {
        u8      peripheral_info;
        u8      page_code; /* 0xC8 */
@@ -198,8 +205,17 @@ static const char *lun_state[] =
        "owned (AVT mode)",
 };
 
+struct rdac_queue_data {
+       struct list_head        entry;
+       struct rdac_dh_data     *h;
+       activate_complete       callback_fn;
+       void                    *callback_data;
+};
+
 static LIST_HEAD(ctlr_list);
 static DEFINE_SPINLOCK(list_lock);
+static struct workqueue_struct *kmpath_rdacd;
+static void send_mode_select(struct work_struct *work);
 
 /*
  * module parameter to enable rdac debug logging.
@@ -281,7 +297,6 @@ static struct request *rdac_failover_get(struct scsi_device *sdev,
                rdac_pg->subpage_code = 0x1;
                rdac_pg->page_len[0] = 0x01;
                rdac_pg->page_len[1] = 0x28;
-               rdac_pg->lun_table[h->lun] = 0x81;
        } else {
                struct rdac_pg_legacy *rdac_pg;
 
@@ -291,7 +306,6 @@ static struct request *rdac_failover_get(struct scsi_device *sdev,
                common = &rdac_pg->common;
                rdac_pg->page_code = RDAC_PAGE_CODE_REDUNDANT_CONTROLLER;
                rdac_pg->page_len = 0x68;
-               rdac_pg->lun_table[h->lun] = 0x81;
        }
        common->rdac_mode[1] = RDAC_MODE_TRANSFER_SPECIFIED_LUNS;
        common->quiescence_timeout = RDAC_QUIESCENCE_TIME;
@@ -325,6 +339,7 @@ static void release_controller(struct kref *kref)
        struct rdac_controller *ctlr;
        ctlr = container_of(kref, struct rdac_controller, kref);
 
+       flush_workqueue(kmpath_rdacd);
        spin_lock(&list_lock);
        list_del(&ctlr->node);
        spin_unlock(&list_lock);
@@ -363,6 +378,11 @@ static struct rdac_controller *get_controller(u8 *subsys_id, u8 *slot_id,
 
        kref_init(&ctlr->kref);
        ctlr->use_ms10 = -1;
+       ctlr->ms_queued = 0;
+       ctlr->ms_sdev = NULL;
+       spin_lock_init(&ctlr->ms_lock);
+       INIT_WORK(&ctlr->ms_work, send_mode_select);
+       INIT_LIST_HEAD(&ctlr->ms_head);
        list_add(&ctlr->node, &ctlr_list);
 done:
        spin_unlock(&list_lock);
@@ -490,7 +510,7 @@ static int set_mode_select(struct scsi_device *sdev, struct rdac_dh_data *h)
 }
 
 static int mode_select_handle_sense(struct scsi_device *sdev,
-                                   unsigned char *sensebuf)
+                                       unsigned char *sensebuf)
 {
        struct scsi_sense_hdr sense_hdr;
        int err = SCSI_DH_IO, ret;
@@ -533,11 +553,29 @@ done:
        return err;
 }
 
-static int send_mode_select(struct scsi_device *sdev, struct rdac_dh_data *h)
+static void send_mode_select(struct work_struct *work)
 {
+       struct rdac_controller *ctlr =
+               container_of(work, struct rdac_controller, ms_work);
        struct request *rq;
+       struct scsi_device *sdev = ctlr->ms_sdev;
+       struct rdac_dh_data *h = get_rdac_data(sdev);
        struct request_queue *q = sdev->request_queue;
        int err, retry_cnt = RDAC_RETRY_COUNT;
+       struct rdac_queue_data *tmp, *qdata;
+       LIST_HEAD(list);
+       u8 *lun_table;
+
+       spin_lock(&ctlr->ms_lock);
+       list_splice_init(&ctlr->ms_head, &list);
+       ctlr->ms_queued = 0;
+       ctlr->ms_sdev = NULL;
+       spin_unlock(&ctlr->ms_lock);
+
+       if (ctlr->use_ms10)
+               lun_table = ctlr->mode_select.expanded.lun_table;
+       else
+               lun_table = ctlr->mode_select.legacy.lun_table;
 
 retry:
        err = SCSI_DH_RES_TEMP_UNAVAIL;
@@ -545,6 +583,10 @@ retry:
        if (!rq)
                goto done;
 
+       list_for_each_entry(qdata, &list, entry) {
+               lun_table[qdata->h->lun] = 0x81;
+       }
+
        RDAC_LOG(RDAC_LOG_FAILOVER, sdev, "array %s, ctlr %d, "
                "%s MODE_SELECT command",
                (char *) h->ctlr->array_name, h->ctlr->index,
@@ -565,7 +607,41 @@ retry:
        }
 
 done:
-       return err;
+       list_for_each_entry_safe(qdata, tmp, &list, entry) {
+               list_del(&qdata->entry);
+               if (err == SCSI_DH_OK)
+                       qdata->h->state = RDAC_STATE_ACTIVE;
+               if (qdata->callback_fn)
+                       qdata->callback_fn(qdata->callback_data, err);
+               kfree(qdata);
+       }
+       return;
+}
+
+static int queue_mode_select(struct scsi_device *sdev,
+                               activate_complete fn, void *data)
+{
+       struct rdac_queue_data *qdata;
+       struct rdac_controller *ctlr;
+
+       qdata = kzalloc(sizeof(*qdata), GFP_KERNEL);
+       if (!qdata)
+               return SCSI_DH_RETRY;
+
+       qdata->h = get_rdac_data(sdev);
+       qdata->callback_fn = fn;
+       qdata->callback_data = data;
+
+       ctlr = qdata->h->ctlr;
+       spin_lock(&ctlr->ms_lock);
+       list_add_tail(&qdata->entry, &ctlr->ms_head);
+       if (!ctlr->ms_queued) {
+               ctlr->ms_queued = 1;
+               ctlr->ms_sdev = sdev;
+               queue_work(kmpath_rdacd, &ctlr->ms_work);
+       }
+       spin_unlock(&ctlr->ms_lock);
+       return SCSI_DH_OK;
 }
 
 static int rdac_activate(struct scsi_device *sdev,
@@ -578,8 +654,11 @@ static int rdac_activate(struct scsi_device *sdev,
        if (err != SCSI_DH_OK)
                goto done;
 
-       if (h->lun_state == RDAC_LUN_UNOWNED)
-               err = send_mode_select(sdev, h);
+       if (h->lun_state == RDAC_LUN_UNOWNED) {
+               err = queue_mode_select(sdev, fn, data);
+               if (err == SCSI_DH_OK)
+                       return 0;
+       }
 done:
        if (fn)
                fn(data, err);
@@ -793,13 +872,26 @@ static int __init rdac_init(void)
        int r;
 
        r = scsi_register_device_handler(&rdac_dh);
-       if (r != 0)
+       if (r != 0) {
                printk(KERN_ERR "Failed to register scsi device handler.");
+               goto done;
+       }
+
+       /*
+        * Create workqueue to handle mode selects for rdac
+        */
+       kmpath_rdacd = create_singlethread_workqueue("kmpath_rdacd");
+       if (!kmpath_rdacd) {
+               scsi_unregister_device_handler(&rdac_dh);
+               printk(KERN_ERR "kmpath_rdacd creation failed.\n");
+       }
+done:
        return r;
 }
 
 static void __exit rdac_exit(void)
 {
+       destroy_workqueue(kmpath_rdacd);
        scsi_unregister_device_handler(&rdac_dh);
 }