[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: Babu Moger <babu.moger@lsi.com> Signed-off-by: Chandra Seetharaman <sekharan@us.ibm.com> Signed-off-by: James Bottomley <James.Bottomley@suse.de>
This commit is contained in:
committed by
James Bottomley
parent
3ae31f6a7b
commit
970f3f47e7
@@ -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 {
|
|||||||
} 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[] =
|
|||||||
"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,
|
|||||||
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,
|
|||||||
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)
|
|||||||
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,
|
|||||||
|
|
||||||
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 @@ done:
|
|||||||
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 @@ retry:
|
|||||||
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 @@ retry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
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,
|
|||||||
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)
|
|||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user