diff options
author | Chandra Seetharaman <sekharan@us.ibm.com> | 2009-10-21 12:22:51 -0400 |
---|---|---|
committer | James Bottomley <James.Bottomley@suse.de> | 2009-12-04 13:00:47 -0500 |
commit | 970f3f47e7c97c0bfe9f91356943b55ac389cb1d (patch) | |
tree | dab9946544c452937d165239df74d667d16bddf9 /drivers/scsi/device_handler | |
parent | 3ae31f6a7b6e442fc6a92f29330fbad230dc3992 (diff) |
[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>
Diffstat (limited to 'drivers/scsi/device_handler')
-rw-r--r-- | drivers/scsi/device_handler/scsi_dh_rdac.c | 108 |
1 files changed, 100 insertions, 8 deletions
diff --git a/drivers/scsi/device_handler/scsi_dh_rdac.c b/drivers/scsi/device_handler/scsi_dh_rdac.c index be362adbd8e7..47cfe1c49c3e 100644 --- a/drivers/scsi/device_handler/scsi_dh_rdac.c +++ b/drivers/scsi/device_handler/scsi_dh_rdac.c | |||
@@ -22,6 +22,7 @@ | |||
22 | #include <scsi/scsi.h> | 22 | #include <scsi/scsi.h> |
23 | #include <scsi/scsi_eh.h> | 23 | #include <scsi/scsi_eh.h> |
24 | #include <scsi/scsi_dh.h> | 24 | #include <scsi/scsi_dh.h> |
25 | #include <linux/workqueue.h> | ||
25 | 26 | ||
26 | #define RDAC_NAME "rdac" | 27 | #define RDAC_NAME "rdac" |
27 | #define RDAC_RETRY_COUNT 5 | 28 | #define RDAC_RETRY_COUNT 5 |
@@ -138,7 +139,13 @@ struct rdac_controller { | |||
138 | } mode_select; | 139 | } mode_select; |
139 | u8 index; | 140 | u8 index; |
140 | u8 array_name[ARRAY_LABEL_LEN]; | 141 | u8 array_name[ARRAY_LABEL_LEN]; |
142 | spinlock_t ms_lock; | ||
143 | int ms_queued; | ||
144 | struct work_struct ms_work; | ||
145 | struct scsi_device *ms_sdev; | ||
146 | struct list_head ms_head; | ||
141 | }; | 147 | }; |
148 | |||
142 | struct c8_inquiry { | 149 | struct c8_inquiry { |
143 | u8 peripheral_info; | 150 | u8 peripheral_info; |
144 | u8 page_code; /* 0xC8 */ | 151 | u8 page_code; /* 0xC8 */ |
@@ -198,8 +205,17 @@ static const char *lun_state[] = | |||
198 | "owned (AVT mode)", | 205 | "owned (AVT mode)", |
199 | }; | 206 | }; |
200 | 207 | ||
208 | struct rdac_queue_data { | ||
209 | struct list_head entry; | ||
210 | struct rdac_dh_data *h; | ||
211 | activate_complete callback_fn; | ||
212 | void *callback_data; | ||
213 | }; | ||
214 | |||
201 | static LIST_HEAD(ctlr_list); | 215 | static LIST_HEAD(ctlr_list); |
202 | static DEFINE_SPINLOCK(list_lock); | 216 | static DEFINE_SPINLOCK(list_lock); |
217 | static struct workqueue_struct *kmpath_rdacd; | ||
218 | static void send_mode_select(struct work_struct *work); | ||
203 | 219 | ||
204 | /* | 220 | /* |
205 | * module parameter to enable rdac debug logging. | 221 | * module parameter to enable rdac debug logging. |
@@ -281,7 +297,6 @@ static struct request *rdac_failover_get(struct scsi_device *sdev, | |||
281 | rdac_pg->subpage_code = 0x1; | 297 | rdac_pg->subpage_code = 0x1; |
282 | rdac_pg->page_len[0] = 0x01; | 298 | rdac_pg->page_len[0] = 0x01; |
283 | rdac_pg->page_len[1] = 0x28; | 299 | rdac_pg->page_len[1] = 0x28; |
284 | rdac_pg->lun_table[h->lun] = 0x81; | ||
285 | } else { | 300 | } else { |
286 | struct rdac_pg_legacy *rdac_pg; | 301 | struct rdac_pg_legacy *rdac_pg; |
287 | 302 | ||
@@ -291,7 +306,6 @@ static struct request *rdac_failover_get(struct scsi_device *sdev, | |||
291 | common = &rdac_pg->common; | 306 | common = &rdac_pg->common; |
292 | rdac_pg->page_code = RDAC_PAGE_CODE_REDUNDANT_CONTROLLER; | 307 | rdac_pg->page_code = RDAC_PAGE_CODE_REDUNDANT_CONTROLLER; |
293 | rdac_pg->page_len = 0x68; | 308 | rdac_pg->page_len = 0x68; |
294 | rdac_pg->lun_table[h->lun] = 0x81; | ||
295 | } | 309 | } |
296 | common->rdac_mode[1] = RDAC_MODE_TRANSFER_SPECIFIED_LUNS; | 310 | common->rdac_mode[1] = RDAC_MODE_TRANSFER_SPECIFIED_LUNS; |
297 | common->quiescence_timeout = RDAC_QUIESCENCE_TIME; | 311 | common->quiescence_timeout = RDAC_QUIESCENCE_TIME; |
@@ -325,6 +339,7 @@ static void release_controller(struct kref *kref) | |||
325 | struct rdac_controller *ctlr; | 339 | struct rdac_controller *ctlr; |
326 | ctlr = container_of(kref, struct rdac_controller, kref); | 340 | ctlr = container_of(kref, struct rdac_controller, kref); |
327 | 341 | ||
342 | flush_workqueue(kmpath_rdacd); | ||
328 | spin_lock(&list_lock); | 343 | spin_lock(&list_lock); |
329 | list_del(&ctlr->node); | 344 | list_del(&ctlr->node); |
330 | spin_unlock(&list_lock); | 345 | spin_unlock(&list_lock); |
@@ -363,6 +378,11 @@ static struct rdac_controller *get_controller(u8 *subsys_id, u8 *slot_id, | |||
363 | 378 | ||
364 | kref_init(&ctlr->kref); | 379 | kref_init(&ctlr->kref); |
365 | ctlr->use_ms10 = -1; | 380 | ctlr->use_ms10 = -1; |
381 | ctlr->ms_queued = 0; | ||
382 | ctlr->ms_sdev = NULL; | ||
383 | spin_lock_init(&ctlr->ms_lock); | ||
384 | INIT_WORK(&ctlr->ms_work, send_mode_select); | ||
385 | INIT_LIST_HEAD(&ctlr->ms_head); | ||
366 | list_add(&ctlr->node, &ctlr_list); | 386 | list_add(&ctlr->node, &ctlr_list); |
367 | done: | 387 | done: |
368 | spin_unlock(&list_lock); | 388 | spin_unlock(&list_lock); |
@@ -490,7 +510,7 @@ static int set_mode_select(struct scsi_device *sdev, struct rdac_dh_data *h) | |||
490 | } | 510 | } |
491 | 511 | ||
492 | static int mode_select_handle_sense(struct scsi_device *sdev, | 512 | static int mode_select_handle_sense(struct scsi_device *sdev, |
493 | unsigned char *sensebuf) | 513 | unsigned char *sensebuf) |
494 | { | 514 | { |
495 | struct scsi_sense_hdr sense_hdr; | 515 | struct scsi_sense_hdr sense_hdr; |
496 | int err = SCSI_DH_IO, ret; | 516 | int err = SCSI_DH_IO, ret; |
@@ -533,11 +553,29 @@ done: | |||
533 | return err; | 553 | return err; |
534 | } | 554 | } |
535 | 555 | ||
536 | static int send_mode_select(struct scsi_device *sdev, struct rdac_dh_data *h) | 556 | static void send_mode_select(struct work_struct *work) |
537 | { | 557 | { |
558 | struct rdac_controller *ctlr = | ||
559 | container_of(work, struct rdac_controller, ms_work); | ||
538 | struct request *rq; | 560 | struct request *rq; |
561 | struct scsi_device *sdev = ctlr->ms_sdev; | ||
562 | struct rdac_dh_data *h = get_rdac_data(sdev); | ||
539 | struct request_queue *q = sdev->request_queue; | 563 | struct request_queue *q = sdev->request_queue; |
540 | int err, retry_cnt = RDAC_RETRY_COUNT; | 564 | int err, retry_cnt = RDAC_RETRY_COUNT; |
565 | struct rdac_queue_data *tmp, *qdata; | ||
566 | LIST_HEAD(list); | ||
567 | u8 *lun_table; | ||
568 | |||
569 | spin_lock(&ctlr->ms_lock); | ||
570 | list_splice_init(&ctlr->ms_head, &list); | ||
571 | ctlr->ms_queued = 0; | ||
572 | ctlr->ms_sdev = NULL; | ||
573 | spin_unlock(&ctlr->ms_lock); | ||
574 | |||
575 | if (ctlr->use_ms10) | ||
576 | lun_table = ctlr->mode_select.expanded.lun_table; | ||
577 | else | ||
578 | lun_table = ctlr->mode_select.legacy.lun_table; | ||
541 | 579 | ||
542 | retry: | 580 | retry: |
543 | err = SCSI_DH_RES_TEMP_UNAVAIL; | 581 | err = SCSI_DH_RES_TEMP_UNAVAIL; |
@@ -545,6 +583,10 @@ retry: | |||
545 | if (!rq) | 583 | if (!rq) |
546 | goto done; | 584 | goto done; |
547 | 585 | ||
586 | list_for_each_entry(qdata, &list, entry) { | ||
587 | lun_table[qdata->h->lun] = 0x81; | ||
588 | } | ||
589 | |||
548 | RDAC_LOG(RDAC_LOG_FAILOVER, sdev, "array %s, ctlr %d, " | 590 | RDAC_LOG(RDAC_LOG_FAILOVER, sdev, "array %s, ctlr %d, " |
549 | "%s MODE_SELECT command", | 591 | "%s MODE_SELECT command", |
550 | (char *) h->ctlr->array_name, h->ctlr->index, | 592 | (char *) h->ctlr->array_name, h->ctlr->index, |
@@ -565,7 +607,41 @@ retry: | |||
565 | } | 607 | } |
566 | 608 | ||
567 | done: | 609 | done: |
568 | return err; | 610 | list_for_each_entry_safe(qdata, tmp, &list, entry) { |
611 | list_del(&qdata->entry); | ||
612 | if (err == SCSI_DH_OK) | ||
613 | qdata->h->state = RDAC_STATE_ACTIVE; | ||
614 | if (qdata->callback_fn) | ||
615 | qdata->callback_fn(qdata->callback_data, err); | ||
616 | kfree(qdata); | ||
617 | } | ||
618 | return; | ||
619 | } | ||
620 | |||
621 | static int queue_mode_select(struct scsi_device *sdev, | ||
622 | activate_complete fn, void *data) | ||
623 | { | ||
624 | struct rdac_queue_data *qdata; | ||
625 | struct rdac_controller *ctlr; | ||
626 | |||
627 | qdata = kzalloc(sizeof(*qdata), GFP_KERNEL); | ||
628 | if (!qdata) | ||
629 | return SCSI_DH_RETRY; | ||
630 | |||
631 | qdata->h = get_rdac_data(sdev); | ||
632 | qdata->callback_fn = fn; | ||
633 | qdata->callback_data = data; | ||
634 | |||
635 | ctlr = qdata->h->ctlr; | ||
636 | spin_lock(&ctlr->ms_lock); | ||
637 | list_add_tail(&qdata->entry, &ctlr->ms_head); | ||
638 | if (!ctlr->ms_queued) { | ||
639 | ctlr->ms_queued = 1; | ||
640 | ctlr->ms_sdev = sdev; | ||
641 | queue_work(kmpath_rdacd, &ctlr->ms_work); | ||
642 | } | ||
643 | spin_unlock(&ctlr->ms_lock); | ||
644 | return SCSI_DH_OK; | ||
569 | } | 645 | } |
570 | 646 | ||
571 | static int rdac_activate(struct scsi_device *sdev, | 647 | static int rdac_activate(struct scsi_device *sdev, |
@@ -578,8 +654,11 @@ static int rdac_activate(struct scsi_device *sdev, | |||
578 | if (err != SCSI_DH_OK) | 654 | if (err != SCSI_DH_OK) |
579 | goto done; | 655 | goto done; |
580 | 656 | ||
581 | if (h->lun_state == RDAC_LUN_UNOWNED) | 657 | if (h->lun_state == RDAC_LUN_UNOWNED) { |
582 | err = send_mode_select(sdev, h); | 658 | err = queue_mode_select(sdev, fn, data); |
659 | if (err == SCSI_DH_OK) | ||
660 | return 0; | ||
661 | } | ||
583 | done: | 662 | done: |
584 | if (fn) | 663 | if (fn) |
585 | fn(data, err); | 664 | fn(data, err); |
@@ -793,13 +872,26 @@ static int __init rdac_init(void) | |||
793 | int r; | 872 | int r; |
794 | 873 | ||
795 | r = scsi_register_device_handler(&rdac_dh); | 874 | r = scsi_register_device_handler(&rdac_dh); |
796 | if (r != 0) | 875 | if (r != 0) { |
797 | printk(KERN_ERR "Failed to register scsi device handler."); | 876 | printk(KERN_ERR "Failed to register scsi device handler."); |
877 | goto done; | ||
878 | } | ||
879 | |||
880 | /* | ||
881 | * Create workqueue to handle mode selects for rdac | ||
882 | */ | ||
883 | kmpath_rdacd = create_singlethread_workqueue("kmpath_rdacd"); | ||
884 | if (!kmpath_rdacd) { | ||
885 | scsi_unregister_device_handler(&rdac_dh); | ||
886 | printk(KERN_ERR "kmpath_rdacd creation failed.\n"); | ||
887 | } | ||
888 | done: | ||
798 | return r; | 889 | return r; |
799 | } | 890 | } |
800 | 891 | ||
801 | static void __exit rdac_exit(void) | 892 | static void __exit rdac_exit(void) |
802 | { | 893 | { |
894 | destroy_workqueue(kmpath_rdacd); | ||
803 | scsi_unregister_device_handler(&rdac_dh); | 895 | scsi_unregister_device_handler(&rdac_dh); |
804 | } | 896 | } |
805 | 897 | ||