Commit 970f3f47 authored by Chandra Seetharaman's avatar Chandra Seetharaman Committed by James Bottomley

[SCSI] scsi_dh: Make rdac hardware handler's activate() async

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: default avatarBabu Moger <babu.moger@lsi.com>
Signed-off-by: default avatarChandra Seetharaman <sekharan@us.ibm.com>
Signed-off-by: default avatarJames Bottomley <James.Bottomley@suse.de>
parent 3ae31f6a
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include <scsi/scsi.h> #include <scsi/scsi.h>
#include <scsi/scsi_eh.h> #include <scsi/scsi_eh.h>
#include <scsi/scsi_dh.h> #include <scsi/scsi_dh.h>
#include <linux/workqueue.h>
#define RDAC_NAME "rdac" #define RDAC_NAME "rdac"
#define RDAC_RETRY_COUNT 5 #define RDAC_RETRY_COUNT 5
...@@ -138,7 +139,13 @@ struct rdac_controller { ...@@ -138,7 +139,13 @@ struct rdac_controller {
} mode_select; } mode_select;
u8 index; u8 index;
u8 array_name[ARRAY_LABEL_LEN]; 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 { struct c8_inquiry {
u8 peripheral_info; u8 peripheral_info;
u8 page_code; /* 0xC8 */ u8 page_code; /* 0xC8 */
...@@ -198,8 +205,17 @@ static const char *lun_state[] = ...@@ -198,8 +205,17 @@ static const char *lun_state[] =
"owned (AVT mode)", "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 LIST_HEAD(ctlr_list);
static DEFINE_SPINLOCK(list_lock); 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. * module parameter to enable rdac debug logging.
...@@ -281,7 +297,6 @@ static struct request *rdac_failover_get(struct scsi_device *sdev, ...@@ -281,7 +297,6 @@ static struct request *rdac_failover_get(struct scsi_device *sdev,
rdac_pg->subpage_code = 0x1; rdac_pg->subpage_code = 0x1;
rdac_pg->page_len[0] = 0x01; rdac_pg->page_len[0] = 0x01;
rdac_pg->page_len[1] = 0x28; rdac_pg->page_len[1] = 0x28;
rdac_pg->lun_table[h->lun] = 0x81;
} else { } else {
struct rdac_pg_legacy *rdac_pg; struct rdac_pg_legacy *rdac_pg;
...@@ -291,7 +306,6 @@ static struct request *rdac_failover_get(struct scsi_device *sdev, ...@@ -291,7 +306,6 @@ static struct request *rdac_failover_get(struct scsi_device *sdev,
common = &rdac_pg->common; common = &rdac_pg->common;
rdac_pg->page_code = RDAC_PAGE_CODE_REDUNDANT_CONTROLLER; rdac_pg->page_code = RDAC_PAGE_CODE_REDUNDANT_CONTROLLER;
rdac_pg->page_len = 0x68; rdac_pg->page_len = 0x68;
rdac_pg->lun_table[h->lun] = 0x81;
} }
common->rdac_mode[1] = RDAC_MODE_TRANSFER_SPECIFIED_LUNS; common->rdac_mode[1] = RDAC_MODE_TRANSFER_SPECIFIED_LUNS;
common->quiescence_timeout = RDAC_QUIESCENCE_TIME; common->quiescence_timeout = RDAC_QUIESCENCE_TIME;
...@@ -325,6 +339,7 @@ static void release_controller(struct kref *kref) ...@@ -325,6 +339,7 @@ static void release_controller(struct kref *kref)
struct rdac_controller *ctlr; struct rdac_controller *ctlr;
ctlr = container_of(kref, struct rdac_controller, kref); ctlr = container_of(kref, struct rdac_controller, kref);
flush_workqueue(kmpath_rdacd);
spin_lock(&list_lock); spin_lock(&list_lock);
list_del(&ctlr->node); list_del(&ctlr->node);
spin_unlock(&list_lock); spin_unlock(&list_lock);
...@@ -363,6 +378,11 @@ static struct rdac_controller *get_controller(u8 *subsys_id, u8 *slot_id, ...@@ -363,6 +378,11 @@ static struct rdac_controller *get_controller(u8 *subsys_id, u8 *slot_id,
kref_init(&ctlr->kref); kref_init(&ctlr->kref);
ctlr->use_ms10 = -1; 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); list_add(&ctlr->node, &ctlr_list);
done: done:
spin_unlock(&list_lock); spin_unlock(&list_lock);
...@@ -533,11 +553,29 @@ static int mode_select_handle_sense(struct scsi_device *sdev, ...@@ -533,11 +553,29 @@ static int mode_select_handle_sense(struct scsi_device *sdev,
return err; 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 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; struct request_queue *q = sdev->request_queue;
int err, retry_cnt = RDAC_RETRY_COUNT; 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: retry:
err = SCSI_DH_RES_TEMP_UNAVAIL; err = SCSI_DH_RES_TEMP_UNAVAIL;
...@@ -545,6 +583,10 @@ static int send_mode_select(struct scsi_device *sdev, struct rdac_dh_data *h) ...@@ -545,6 +583,10 @@ static int send_mode_select(struct scsi_device *sdev, struct rdac_dh_data *h)
if (!rq) if (!rq)
goto done; 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, " RDAC_LOG(RDAC_LOG_FAILOVER, sdev, "array %s, ctlr %d, "
"%s MODE_SELECT command", "%s MODE_SELECT command",
(char *) h->ctlr->array_name, h->ctlr->index, (char *) h->ctlr->array_name, h->ctlr->index,
...@@ -565,7 +607,41 @@ static int send_mode_select(struct scsi_device *sdev, struct rdac_dh_data *h) ...@@ -565,7 +607,41 @@ static int send_mode_select(struct scsi_device *sdev, struct rdac_dh_data *h)
} }
done: 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, static int rdac_activate(struct scsi_device *sdev,
...@@ -578,8 +654,11 @@ 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) if (err != SCSI_DH_OK)
goto done; goto done;
if (h->lun_state == RDAC_LUN_UNOWNED) if (h->lun_state == RDAC_LUN_UNOWNED) {
err = send_mode_select(sdev, h); err = queue_mode_select(sdev, fn, data);
if (err == SCSI_DH_OK)
return 0;
}
done: done:
if (fn) if (fn)
fn(data, err); fn(data, err);
...@@ -793,13 +872,26 @@ static int __init rdac_init(void) ...@@ -793,13 +872,26 @@ static int __init rdac_init(void)
int r; int r;
r = scsi_register_device_handler(&rdac_dh); r = scsi_register_device_handler(&rdac_dh);
if (r != 0) if (r != 0) {
printk(KERN_ERR "Failed to register scsi device handler."); 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; return r;
} }
static void __exit rdac_exit(void) static void __exit rdac_exit(void)
{ {
destroy_workqueue(kmpath_rdacd);
scsi_unregister_device_handler(&rdac_dh); scsi_unregister_device_handler(&rdac_dh);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment