diff options
author | Brian King <brking@linux.vnet.ibm.com> | 2009-05-28 17:17:29 -0400 |
---|---|---|
committer | James Bottomley <James.Bottomley@HansenPartnership.com> | 2009-06-08 14:07:44 -0400 |
commit | 79111d0899a122fa3cf0a2921292c3e6a25452d0 (patch) | |
tree | 73300ac26b25f515e2c04aa04f50cc9664886441 /drivers/scsi/ibmvscsi/ibmvfc.c | |
parent | 43c8da907ccc656935d1085701f4db83385d8a59 (diff) |
[SCSI] ibmvfc: Add support for NPIV Logout
This patch adds support for a new command supported by the Virtual I/O
Server, NPIV Logout. The command will abort all outstanding commands
and log out of the fabric. Currently, the only way to do this is
by breaking the CRQ, which can take a fairly long time when lots of
commands are outstanding. The NPIV Logout commands provides a mechanism
to accomplish virtually the same function, but is much faster.
Signed-off-by: Brian King <brking@linux.vnet.ibm.com>
Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
Diffstat (limited to 'drivers/scsi/ibmvscsi/ibmvfc.c')
-rw-r--r-- | drivers/scsi/ibmvscsi/ibmvfc.c | 97 |
1 files changed, 94 insertions, 3 deletions
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c index 76ae266b07c4..6eb3b75eec93 100644 --- a/drivers/scsi/ibmvscsi/ibmvfc.c +++ b/drivers/scsi/ibmvscsi/ibmvfc.c | |||
@@ -143,6 +143,7 @@ static void ibmvfc_npiv_login(struct ibmvfc_host *); | |||
143 | static void ibmvfc_tgt_send_prli(struct ibmvfc_target *); | 143 | static void ibmvfc_tgt_send_prli(struct ibmvfc_target *); |
144 | static void ibmvfc_tgt_send_plogi(struct ibmvfc_target *); | 144 | static void ibmvfc_tgt_send_plogi(struct ibmvfc_target *); |
145 | static void ibmvfc_tgt_query_target(struct ibmvfc_target *); | 145 | static void ibmvfc_tgt_query_target(struct ibmvfc_target *); |
146 | static void ibmvfc_npiv_logout(struct ibmvfc_host *); | ||
146 | 147 | ||
147 | static const char *unknown_error = "unknown error"; | 148 | static const char *unknown_error = "unknown error"; |
148 | 149 | ||
@@ -477,6 +478,10 @@ static void ibmvfc_set_host_action(struct ibmvfc_host *vhost, | |||
477 | if (vhost->action == IBMVFC_HOST_ACTION_INIT_WAIT) | 478 | if (vhost->action == IBMVFC_HOST_ACTION_INIT_WAIT) |
478 | vhost->action = action; | 479 | vhost->action = action; |
479 | break; | 480 | break; |
481 | case IBMVFC_HOST_ACTION_LOGO_WAIT: | ||
482 | if (vhost->action == IBMVFC_HOST_ACTION_LOGO) | ||
483 | vhost->action = action; | ||
484 | break; | ||
480 | case IBMVFC_HOST_ACTION_INIT_WAIT: | 485 | case IBMVFC_HOST_ACTION_INIT_WAIT: |
481 | if (vhost->action == IBMVFC_HOST_ACTION_INIT) | 486 | if (vhost->action == IBMVFC_HOST_ACTION_INIT) |
482 | vhost->action = action; | 487 | vhost->action = action; |
@@ -496,6 +501,7 @@ static void ibmvfc_set_host_action(struct ibmvfc_host *vhost, | |||
496 | if (vhost->action == IBMVFC_HOST_ACTION_ALLOC_TGTS) | 501 | if (vhost->action == IBMVFC_HOST_ACTION_ALLOC_TGTS) |
497 | vhost->action = action; | 502 | vhost->action = action; |
498 | break; | 503 | break; |
504 | case IBMVFC_HOST_ACTION_LOGO: | ||
499 | case IBMVFC_HOST_ACTION_INIT: | 505 | case IBMVFC_HOST_ACTION_INIT: |
500 | case IBMVFC_HOST_ACTION_TGT_DEL: | 506 | case IBMVFC_HOST_ACTION_TGT_DEL: |
501 | case IBMVFC_HOST_ACTION_QUERY_TGTS: | 507 | case IBMVFC_HOST_ACTION_QUERY_TGTS: |
@@ -647,6 +653,7 @@ static void ibmvfc_release_crq_queue(struct ibmvfc_host *vhost) | |||
647 | } while (rc == H_BUSY || H_IS_LONG_BUSY(rc)); | 653 | } while (rc == H_BUSY || H_IS_LONG_BUSY(rc)); |
648 | 654 | ||
649 | vhost->state = IBMVFC_NO_CRQ; | 655 | vhost->state = IBMVFC_NO_CRQ; |
656 | vhost->logged_in = 0; | ||
650 | dma_unmap_single(vhost->dev, crq->msg_token, PAGE_SIZE, DMA_BIDIRECTIONAL); | 657 | dma_unmap_single(vhost->dev, crq->msg_token, PAGE_SIZE, DMA_BIDIRECTIONAL); |
651 | free_page((unsigned long)crq->msgs); | 658 | free_page((unsigned long)crq->msgs); |
652 | } | 659 | } |
@@ -693,6 +700,7 @@ static int ibmvfc_reset_crq(struct ibmvfc_host *vhost) | |||
693 | } while (rc == H_BUSY || H_IS_LONG_BUSY(rc)); | 700 | } while (rc == H_BUSY || H_IS_LONG_BUSY(rc)); |
694 | 701 | ||
695 | vhost->state = IBMVFC_NO_CRQ; | 702 | vhost->state = IBMVFC_NO_CRQ; |
703 | vhost->logged_in = 0; | ||
696 | ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_NONE); | 704 | ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_NONE); |
697 | 705 | ||
698 | /* Clean out the queue */ | 706 | /* Clean out the queue */ |
@@ -808,10 +816,10 @@ static void ibmvfc_purge_requests(struct ibmvfc_host *vhost, int error_code) | |||
808 | } | 816 | } |
809 | 817 | ||
810 | /** | 818 | /** |
811 | * __ibmvfc_reset_host - Reset the connection to the server (no locking) | 819 | * ibmvfc_hard_reset_host - Reset the connection to the server by breaking the CRQ |
812 | * @vhost: struct ibmvfc host to reset | 820 | * @vhost: struct ibmvfc host to reset |
813 | **/ | 821 | **/ |
814 | static void __ibmvfc_reset_host(struct ibmvfc_host *vhost) | 822 | static void ibmvfc_hard_reset_host(struct ibmvfc_host *vhost) |
815 | { | 823 | { |
816 | int rc; | 824 | int rc; |
817 | 825 | ||
@@ -827,9 +835,25 @@ static void __ibmvfc_reset_host(struct ibmvfc_host *vhost) | |||
827 | } | 835 | } |
828 | 836 | ||
829 | /** | 837 | /** |
830 | * ibmvfc_reset_host - Reset the connection to the server | 838 | * __ibmvfc_reset_host - Reset the connection to the server (no locking) |
831 | * @vhost: struct ibmvfc host to reset | 839 | * @vhost: struct ibmvfc host to reset |
832 | **/ | 840 | **/ |
841 | static void __ibmvfc_reset_host(struct ibmvfc_host *vhost) | ||
842 | { | ||
843 | if (vhost->logged_in && vhost->action != IBMVFC_HOST_ACTION_LOGO_WAIT && | ||
844 | !ibmvfc_set_host_state(vhost, IBMVFC_INITIALIZING)) { | ||
845 | scsi_block_requests(vhost->host); | ||
846 | ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_LOGO); | ||
847 | vhost->job_step = ibmvfc_npiv_logout; | ||
848 | wake_up(&vhost->work_wait_q); | ||
849 | } else | ||
850 | ibmvfc_hard_reset_host(vhost); | ||
851 | } | ||
852 | |||
853 | /** | ||
854 | * ibmvfc_reset_host - Reset the connection to the server | ||
855 | * @vhost: ibmvfc host struct | ||
856 | **/ | ||
833 | static void ibmvfc_reset_host(struct ibmvfc_host *vhost) | 857 | static void ibmvfc_reset_host(struct ibmvfc_host *vhost) |
834 | { | 858 | { |
835 | unsigned long flags; | 859 | unsigned long flags; |
@@ -2230,6 +2254,7 @@ static void ibmvfc_handle_crq(struct ibmvfc_crq *crq, struct ibmvfc_host *vhost) | |||
2230 | return; | 2254 | return; |
2231 | case IBMVFC_CRQ_XPORT_EVENT: | 2255 | case IBMVFC_CRQ_XPORT_EVENT: |
2232 | vhost->state = IBMVFC_NO_CRQ; | 2256 | vhost->state = IBMVFC_NO_CRQ; |
2257 | vhost->logged_in = 0; | ||
2233 | ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_NONE); | 2258 | ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_NONE); |
2234 | if (crq->format == IBMVFC_PARTITION_MIGRATED) { | 2259 | if (crq->format == IBMVFC_PARTITION_MIGRATED) { |
2235 | /* We need to re-setup the interpartition connection */ | 2260 | /* We need to re-setup the interpartition connection */ |
@@ -3554,6 +3579,7 @@ static void ibmvfc_npiv_login_done(struct ibmvfc_event *evt) | |||
3554 | return; | 3579 | return; |
3555 | } | 3580 | } |
3556 | 3581 | ||
3582 | vhost->logged_in = 1; | ||
3557 | npiv_max_sectors = min((uint)(rsp->max_dma_len >> 9), IBMVFC_MAX_SECTORS); | 3583 | npiv_max_sectors = min((uint)(rsp->max_dma_len >> 9), IBMVFC_MAX_SECTORS); |
3558 | dev_info(vhost->dev, "Host partition: %s, device: %s %s %s max sectors %u\n", | 3584 | dev_info(vhost->dev, "Host partition: %s, device: %s %s %s max sectors %u\n", |
3559 | rsp->partition_name, rsp->device_name, rsp->port_loc_code, | 3585 | rsp->partition_name, rsp->device_name, rsp->port_loc_code, |
@@ -3612,6 +3638,65 @@ static void ibmvfc_npiv_login(struct ibmvfc_host *vhost) | |||
3612 | }; | 3638 | }; |
3613 | 3639 | ||
3614 | /** | 3640 | /** |
3641 | * ibmvfc_npiv_logout_done - Completion handler for NPIV Logout | ||
3642 | * @vhost: ibmvfc host struct | ||
3643 | * | ||
3644 | **/ | ||
3645 | static void ibmvfc_npiv_logout_done(struct ibmvfc_event *evt) | ||
3646 | { | ||
3647 | struct ibmvfc_host *vhost = evt->vhost; | ||
3648 | u32 mad_status = evt->xfer_iu->npiv_logout.common.status; | ||
3649 | |||
3650 | ibmvfc_free_event(evt); | ||
3651 | |||
3652 | switch (mad_status) { | ||
3653 | case IBMVFC_MAD_SUCCESS: | ||
3654 | if (list_empty(&vhost->sent) && | ||
3655 | vhost->action == IBMVFC_HOST_ACTION_LOGO_WAIT) { | ||
3656 | ibmvfc_init_host(vhost, 0); | ||
3657 | return; | ||
3658 | } | ||
3659 | break; | ||
3660 | case IBMVFC_MAD_FAILED: | ||
3661 | case IBMVFC_MAD_NOT_SUPPORTED: | ||
3662 | case IBMVFC_MAD_CRQ_ERROR: | ||
3663 | case IBMVFC_MAD_DRIVER_FAILED: | ||
3664 | default: | ||
3665 | ibmvfc_dbg(vhost, "NPIV Logout failed. 0x%X\n", mad_status); | ||
3666 | break; | ||
3667 | } | ||
3668 | |||
3669 | ibmvfc_hard_reset_host(vhost); | ||
3670 | } | ||
3671 | |||
3672 | /** | ||
3673 | * ibmvfc_npiv_logout - Issue an NPIV Logout | ||
3674 | * @vhost: ibmvfc host struct | ||
3675 | * | ||
3676 | **/ | ||
3677 | static void ibmvfc_npiv_logout(struct ibmvfc_host *vhost) | ||
3678 | { | ||
3679 | struct ibmvfc_npiv_logout_mad *mad; | ||
3680 | struct ibmvfc_event *evt; | ||
3681 | |||
3682 | evt = ibmvfc_get_event(vhost); | ||
3683 | ibmvfc_init_event(evt, ibmvfc_npiv_logout_done, IBMVFC_MAD_FORMAT); | ||
3684 | |||
3685 | mad = &evt->iu.npiv_logout; | ||
3686 | memset(mad, 0, sizeof(*mad)); | ||
3687 | mad->common.version = 1; | ||
3688 | mad->common.opcode = IBMVFC_NPIV_LOGOUT; | ||
3689 | mad->common.length = sizeof(struct ibmvfc_npiv_logout_mad); | ||
3690 | |||
3691 | ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_LOGO_WAIT); | ||
3692 | |||
3693 | if (!ibmvfc_send_event(evt, vhost, default_timeout)) | ||
3694 | ibmvfc_dbg(vhost, "Sent NPIV logout\n"); | ||
3695 | else | ||
3696 | ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD); | ||
3697 | } | ||
3698 | |||
3699 | /** | ||
3615 | * ibmvfc_dev_init_to_do - Is there target initialization work to do? | 3700 | * ibmvfc_dev_init_to_do - Is there target initialization work to do? |
3616 | * @vhost: ibmvfc host struct | 3701 | * @vhost: ibmvfc host struct |
3617 | * | 3702 | * |
@@ -3647,6 +3732,7 @@ static int __ibmvfc_work_to_do(struct ibmvfc_host *vhost) | |||
3647 | switch (vhost->action) { | 3732 | switch (vhost->action) { |
3648 | case IBMVFC_HOST_ACTION_NONE: | 3733 | case IBMVFC_HOST_ACTION_NONE: |
3649 | case IBMVFC_HOST_ACTION_INIT_WAIT: | 3734 | case IBMVFC_HOST_ACTION_INIT_WAIT: |
3735 | case IBMVFC_HOST_ACTION_LOGO_WAIT: | ||
3650 | return 0; | 3736 | return 0; |
3651 | case IBMVFC_HOST_ACTION_TGT_INIT: | 3737 | case IBMVFC_HOST_ACTION_TGT_INIT: |
3652 | case IBMVFC_HOST_ACTION_QUERY_TGTS: | 3738 | case IBMVFC_HOST_ACTION_QUERY_TGTS: |
@@ -3659,6 +3745,7 @@ static int __ibmvfc_work_to_do(struct ibmvfc_host *vhost) | |||
3659 | if (tgt->action == IBMVFC_TGT_ACTION_INIT_WAIT) | 3745 | if (tgt->action == IBMVFC_TGT_ACTION_INIT_WAIT) |
3660 | return 0; | 3746 | return 0; |
3661 | return 1; | 3747 | return 1; |
3748 | case IBMVFC_HOST_ACTION_LOGO: | ||
3662 | case IBMVFC_HOST_ACTION_INIT: | 3749 | case IBMVFC_HOST_ACTION_INIT: |
3663 | case IBMVFC_HOST_ACTION_ALLOC_TGTS: | 3750 | case IBMVFC_HOST_ACTION_ALLOC_TGTS: |
3664 | case IBMVFC_HOST_ACTION_TGT_DEL: | 3751 | case IBMVFC_HOST_ACTION_TGT_DEL: |
@@ -3765,8 +3852,12 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost) | |||
3765 | vhost->events_to_log = 0; | 3852 | vhost->events_to_log = 0; |
3766 | switch (vhost->action) { | 3853 | switch (vhost->action) { |
3767 | case IBMVFC_HOST_ACTION_NONE: | 3854 | case IBMVFC_HOST_ACTION_NONE: |
3855 | case IBMVFC_HOST_ACTION_LOGO_WAIT: | ||
3768 | case IBMVFC_HOST_ACTION_INIT_WAIT: | 3856 | case IBMVFC_HOST_ACTION_INIT_WAIT: |
3769 | break; | 3857 | break; |
3858 | case IBMVFC_HOST_ACTION_LOGO: | ||
3859 | vhost->job_step(vhost); | ||
3860 | break; | ||
3770 | case IBMVFC_HOST_ACTION_INIT: | 3861 | case IBMVFC_HOST_ACTION_INIT: |
3771 | BUG_ON(vhost->state != IBMVFC_INITIALIZING); | 3862 | BUG_ON(vhost->state != IBMVFC_INITIALIZING); |
3772 | if (vhost->delay_init) { | 3863 | if (vhost->delay_init) { |