diff options
author | Brian King <brking@linux.vnet.ibm.com> | 2010-06-17 14:55:13 -0400 |
---|---|---|
committer | James Bottomley <James.Bottomley@suse.de> | 2010-07-27 13:02:39 -0400 |
commit | 73ee5d8672871bd69077ca71e7208a36bfa6343c (patch) | |
tree | 6494ae5426a54a98ad5f8f6aec4ba58e41003fc4 /drivers/scsi/ibmvscsi | |
parent | 15f7fc060a7bf49991c35b23e1e7d73a1535382a (diff) |
[SCSI] ibmvfc: Fix soft lockup on resume
This fixes a softlockup seen on resume. During resume, the CRQ
must be reenabled. However, the H_ENABLE_CRQ hcall used to do
this may return H_BUSY or H_LONG_BUSY. When this happens, the
caller is expected to retry later. Normally the H_ENABLE_CRQ
succeeds relatively soon. However, we have seen cases where
this can take long enough to see softlockup warnings.
This patch changes a simple loop, which was causing the
softlockup, to a loop at task level which sleeps between
retries rather than simply spinning.
Signed-off-by: Brian King <brking@linux.vnet.ibm.com>
Signed-off-by: James Bottomley <James.Bottomley@suse.de>
Diffstat (limited to 'drivers/scsi/ibmvscsi')
-rw-r--r-- | drivers/scsi/ibmvscsi/ibmvfc.c | 81 | ||||
-rw-r--r-- | drivers/scsi/ibmvscsi/ibmvfc.h | 2 |
2 files changed, 56 insertions, 27 deletions
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c index fef49521cbc3..d6fcb3f43969 100644 --- a/drivers/scsi/ibmvscsi/ibmvfc.c +++ b/drivers/scsi/ibmvscsi/ibmvfc.c | |||
@@ -504,12 +504,23 @@ static void ibmvfc_set_host_action(struct ibmvfc_host *vhost, | |||
504 | if (vhost->action == IBMVFC_HOST_ACTION_ALLOC_TGTS) | 504 | if (vhost->action == IBMVFC_HOST_ACTION_ALLOC_TGTS) |
505 | vhost->action = action; | 505 | vhost->action = action; |
506 | break; | 506 | break; |
507 | case IBMVFC_HOST_ACTION_LOGO: | ||
508 | case IBMVFC_HOST_ACTION_INIT: | 507 | case IBMVFC_HOST_ACTION_INIT: |
509 | case IBMVFC_HOST_ACTION_TGT_DEL: | 508 | case IBMVFC_HOST_ACTION_TGT_DEL: |
509 | switch (vhost->action) { | ||
510 | case IBMVFC_HOST_ACTION_RESET: | ||
511 | case IBMVFC_HOST_ACTION_REENABLE: | ||
512 | break; | ||
513 | default: | ||
514 | vhost->action = action; | ||
515 | break; | ||
516 | }; | ||
517 | break; | ||
518 | case IBMVFC_HOST_ACTION_LOGO: | ||
510 | case IBMVFC_HOST_ACTION_QUERY_TGTS: | 519 | case IBMVFC_HOST_ACTION_QUERY_TGTS: |
511 | case IBMVFC_HOST_ACTION_TGT_DEL_FAILED: | 520 | case IBMVFC_HOST_ACTION_TGT_DEL_FAILED: |
512 | case IBMVFC_HOST_ACTION_NONE: | 521 | case IBMVFC_HOST_ACTION_NONE: |
522 | case IBMVFC_HOST_ACTION_RESET: | ||
523 | case IBMVFC_HOST_ACTION_REENABLE: | ||
513 | default: | 524 | default: |
514 | vhost->action = action; | 525 | vhost->action = action; |
515 | break; | 526 | break; |
@@ -641,7 +652,7 @@ static int ibmvfc_send_crq_init_complete(struct ibmvfc_host *vhost) | |||
641 | **/ | 652 | **/ |
642 | static void ibmvfc_release_crq_queue(struct ibmvfc_host *vhost) | 653 | static void ibmvfc_release_crq_queue(struct ibmvfc_host *vhost) |
643 | { | 654 | { |
644 | long rc; | 655 | long rc = 0; |
645 | struct vio_dev *vdev = to_vio_dev(vhost->dev); | 656 | struct vio_dev *vdev = to_vio_dev(vhost->dev); |
646 | struct ibmvfc_crq_queue *crq = &vhost->crq; | 657 | struct ibmvfc_crq_queue *crq = &vhost->crq; |
647 | 658 | ||
@@ -649,6 +660,8 @@ static void ibmvfc_release_crq_queue(struct ibmvfc_host *vhost) | |||
649 | free_irq(vdev->irq, vhost); | 660 | free_irq(vdev->irq, vhost); |
650 | tasklet_kill(&vhost->tasklet); | 661 | tasklet_kill(&vhost->tasklet); |
651 | do { | 662 | do { |
663 | if (rc) | ||
664 | msleep(100); | ||
652 | rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address); | 665 | rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address); |
653 | } while (rc == H_BUSY || H_IS_LONG_BUSY(rc)); | 666 | } while (rc == H_BUSY || H_IS_LONG_BUSY(rc)); |
654 | 667 | ||
@@ -667,11 +680,13 @@ static void ibmvfc_release_crq_queue(struct ibmvfc_host *vhost) | |||
667 | **/ | 680 | **/ |
668 | static int ibmvfc_reenable_crq_queue(struct ibmvfc_host *vhost) | 681 | static int ibmvfc_reenable_crq_queue(struct ibmvfc_host *vhost) |
669 | { | 682 | { |
670 | int rc; | 683 | int rc = 0; |
671 | struct vio_dev *vdev = to_vio_dev(vhost->dev); | 684 | struct vio_dev *vdev = to_vio_dev(vhost->dev); |
672 | 685 | ||
673 | /* Re-enable the CRQ */ | 686 | /* Re-enable the CRQ */ |
674 | do { | 687 | do { |
688 | if (rc) | ||
689 | msleep(100); | ||
675 | rc = plpar_hcall_norets(H_ENABLE_CRQ, vdev->unit_address); | 690 | rc = plpar_hcall_norets(H_ENABLE_CRQ, vdev->unit_address); |
676 | } while (rc == H_IN_PROGRESS || rc == H_BUSY || H_IS_LONG_BUSY(rc)); | 691 | } while (rc == H_IN_PROGRESS || rc == H_BUSY || H_IS_LONG_BUSY(rc)); |
677 | 692 | ||
@@ -690,15 +705,19 @@ static int ibmvfc_reenable_crq_queue(struct ibmvfc_host *vhost) | |||
690 | **/ | 705 | **/ |
691 | static int ibmvfc_reset_crq(struct ibmvfc_host *vhost) | 706 | static int ibmvfc_reset_crq(struct ibmvfc_host *vhost) |
692 | { | 707 | { |
693 | int rc; | 708 | int rc = 0; |
709 | unsigned long flags; | ||
694 | struct vio_dev *vdev = to_vio_dev(vhost->dev); | 710 | struct vio_dev *vdev = to_vio_dev(vhost->dev); |
695 | struct ibmvfc_crq_queue *crq = &vhost->crq; | 711 | struct ibmvfc_crq_queue *crq = &vhost->crq; |
696 | 712 | ||
697 | /* Close the CRQ */ | 713 | /* Close the CRQ */ |
698 | do { | 714 | do { |
715 | if (rc) | ||
716 | msleep(100); | ||
699 | rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address); | 717 | rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address); |
700 | } while (rc == H_BUSY || H_IS_LONG_BUSY(rc)); | 718 | } while (rc == H_BUSY || H_IS_LONG_BUSY(rc)); |
701 | 719 | ||
720 | spin_lock_irqsave(vhost->host->host_lock, flags); | ||
702 | vhost->state = IBMVFC_NO_CRQ; | 721 | vhost->state = IBMVFC_NO_CRQ; |
703 | vhost->logged_in = 0; | 722 | vhost->logged_in = 0; |
704 | ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_NONE); | 723 | ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_NONE); |
@@ -716,6 +735,7 @@ static int ibmvfc_reset_crq(struct ibmvfc_host *vhost) | |||
716 | dev_warn(vhost->dev, "Partner adapter not ready\n"); | 735 | dev_warn(vhost->dev, "Partner adapter not ready\n"); |
717 | else if (rc != 0) | 736 | else if (rc != 0) |
718 | dev_warn(vhost->dev, "Couldn't register crq (rc=%d)\n", rc); | 737 | dev_warn(vhost->dev, "Couldn't register crq (rc=%d)\n", rc); |
738 | spin_unlock_irqrestore(vhost->host->host_lock, flags); | ||
719 | 739 | ||
720 | return rc; | 740 | return rc; |
721 | } | 741 | } |
@@ -821,17 +841,9 @@ static void ibmvfc_purge_requests(struct ibmvfc_host *vhost, int error_code) | |||
821 | **/ | 841 | **/ |
822 | static void ibmvfc_hard_reset_host(struct ibmvfc_host *vhost) | 842 | static void ibmvfc_hard_reset_host(struct ibmvfc_host *vhost) |
823 | { | 843 | { |
824 | int rc; | ||
825 | |||
826 | scsi_block_requests(vhost->host); | ||
827 | ibmvfc_purge_requests(vhost, DID_ERROR); | 844 | ibmvfc_purge_requests(vhost, DID_ERROR); |
828 | if ((rc = ibmvfc_reset_crq(vhost)) || | 845 | ibmvfc_link_down(vhost, IBMVFC_LINK_DOWN); |
829 | (rc = ibmvfc_send_crq_init(vhost)) || | 846 | ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_RESET); |
830 | (rc = vio_enable_interrupts(to_vio_dev(vhost->dev)))) { | ||
831 | dev_err(vhost->dev, "Error after reset rc=%d\n", rc); | ||
832 | ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD); | ||
833 | } else | ||
834 | ibmvfc_link_down(vhost, IBMVFC_LINK_DOWN); | ||
835 | } | 847 | } |
836 | 848 | ||
837 | /** | 849 | /** |
@@ -2606,22 +2618,13 @@ static void ibmvfc_handle_crq(struct ibmvfc_crq *crq, struct ibmvfc_host *vhost) | |||
2606 | dev_info(vhost->dev, "Re-enabling adapter\n"); | 2618 | dev_info(vhost->dev, "Re-enabling adapter\n"); |
2607 | vhost->client_migrated = 1; | 2619 | vhost->client_migrated = 1; |
2608 | ibmvfc_purge_requests(vhost, DID_REQUEUE); | 2620 | ibmvfc_purge_requests(vhost, DID_REQUEUE); |
2609 | if ((rc = ibmvfc_reenable_crq_queue(vhost)) || | 2621 | ibmvfc_link_down(vhost, IBMVFC_LINK_DOWN); |
2610 | (rc = ibmvfc_send_crq_init(vhost))) { | 2622 | ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_REENABLE); |
2611 | ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD); | ||
2612 | dev_err(vhost->dev, "Error after enable (rc=%ld)\n", rc); | ||
2613 | } else | ||
2614 | ibmvfc_link_down(vhost, IBMVFC_LINK_DOWN); | ||
2615 | } else { | 2623 | } else { |
2616 | dev_err(vhost->dev, "Virtual adapter failed (rc=%d)\n", crq->format); | 2624 | dev_err(vhost->dev, "Virtual adapter failed (rc=%d)\n", crq->format); |
2617 | |||
2618 | ibmvfc_purge_requests(vhost, DID_ERROR); | 2625 | ibmvfc_purge_requests(vhost, DID_ERROR); |
2619 | if ((rc = ibmvfc_reset_crq(vhost)) || | 2626 | ibmvfc_link_down(vhost, IBMVFC_LINK_DOWN); |
2620 | (rc = ibmvfc_send_crq_init(vhost))) { | 2627 | ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_RESET); |
2621 | ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD); | ||
2622 | dev_err(vhost->dev, "Error after reset (rc=%ld)\n", rc); | ||
2623 | } else | ||
2624 | ibmvfc_link_down(vhost, IBMVFC_LINK_DOWN); | ||
2625 | } | 2628 | } |
2626 | return; | 2629 | return; |
2627 | case IBMVFC_CRQ_CMD_RSP: | 2630 | case IBMVFC_CRQ_CMD_RSP: |
@@ -4123,6 +4126,8 @@ static int __ibmvfc_work_to_do(struct ibmvfc_host *vhost) | |||
4123 | case IBMVFC_HOST_ACTION_TGT_DEL: | 4126 | case IBMVFC_HOST_ACTION_TGT_DEL: |
4124 | case IBMVFC_HOST_ACTION_TGT_DEL_FAILED: | 4127 | case IBMVFC_HOST_ACTION_TGT_DEL_FAILED: |
4125 | case IBMVFC_HOST_ACTION_QUERY: | 4128 | case IBMVFC_HOST_ACTION_QUERY: |
4129 | case IBMVFC_HOST_ACTION_RESET: | ||
4130 | case IBMVFC_HOST_ACTION_REENABLE: | ||
4126 | default: | 4131 | default: |
4127 | break; | 4132 | break; |
4128 | }; | 4133 | }; |
@@ -4220,6 +4225,7 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost) | |||
4220 | struct ibmvfc_target *tgt; | 4225 | struct ibmvfc_target *tgt; |
4221 | unsigned long flags; | 4226 | unsigned long flags; |
4222 | struct fc_rport *rport; | 4227 | struct fc_rport *rport; |
4228 | int rc; | ||
4223 | 4229 | ||
4224 | ibmvfc_log_ae(vhost, vhost->events_to_log); | 4230 | ibmvfc_log_ae(vhost, vhost->events_to_log); |
4225 | spin_lock_irqsave(vhost->host->host_lock, flags); | 4231 | spin_lock_irqsave(vhost->host->host_lock, flags); |
@@ -4229,6 +4235,27 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost) | |||
4229 | case IBMVFC_HOST_ACTION_LOGO_WAIT: | 4235 | case IBMVFC_HOST_ACTION_LOGO_WAIT: |
4230 | case IBMVFC_HOST_ACTION_INIT_WAIT: | 4236 | case IBMVFC_HOST_ACTION_INIT_WAIT: |
4231 | break; | 4237 | break; |
4238 | case IBMVFC_HOST_ACTION_RESET: | ||
4239 | vhost->action = IBMVFC_HOST_ACTION_TGT_DEL; | ||
4240 | spin_unlock_irqrestore(vhost->host->host_lock, flags); | ||
4241 | rc = ibmvfc_reset_crq(vhost); | ||
4242 | spin_lock_irqsave(vhost->host->host_lock, flags); | ||
4243 | if (rc || (rc = ibmvfc_send_crq_init(vhost)) || | ||
4244 | (rc = vio_enable_interrupts(to_vio_dev(vhost->dev)))) { | ||
4245 | ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD); | ||
4246 | dev_err(vhost->dev, "Error after reset (rc=%d)\n", rc); | ||
4247 | } | ||
4248 | break; | ||
4249 | case IBMVFC_HOST_ACTION_REENABLE: | ||
4250 | vhost->action = IBMVFC_HOST_ACTION_TGT_DEL; | ||
4251 | spin_unlock_irqrestore(vhost->host->host_lock, flags); | ||
4252 | rc = ibmvfc_reenable_crq_queue(vhost); | ||
4253 | spin_lock_irqsave(vhost->host->host_lock, flags); | ||
4254 | if (rc || (rc = ibmvfc_send_crq_init(vhost))) { | ||
4255 | ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD); | ||
4256 | dev_err(vhost->dev, "Error after enable (rc=%d)\n", rc); | ||
4257 | } | ||
4258 | break; | ||
4232 | case IBMVFC_HOST_ACTION_LOGO: | 4259 | case IBMVFC_HOST_ACTION_LOGO: |
4233 | vhost->job_step(vhost); | 4260 | vhost->job_step(vhost); |
4234 | break; | 4261 | break; |
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.h b/drivers/scsi/ibmvscsi/ibmvfc.h index 7e9742764e4b..2010d73aca81 100644 --- a/drivers/scsi/ibmvscsi/ibmvfc.h +++ b/drivers/scsi/ibmvscsi/ibmvfc.h | |||
@@ -649,6 +649,8 @@ struct ibmvfc_event_pool { | |||
649 | 649 | ||
650 | enum ibmvfc_host_action { | 650 | enum ibmvfc_host_action { |
651 | IBMVFC_HOST_ACTION_NONE = 0, | 651 | IBMVFC_HOST_ACTION_NONE = 0, |
652 | IBMVFC_HOST_ACTION_RESET, | ||
653 | IBMVFC_HOST_ACTION_REENABLE, | ||
652 | IBMVFC_HOST_ACTION_LOGO, | 654 | IBMVFC_HOST_ACTION_LOGO, |
653 | IBMVFC_HOST_ACTION_LOGO_WAIT, | 655 | IBMVFC_HOST_ACTION_LOGO_WAIT, |
654 | IBMVFC_HOST_ACTION_INIT, | 656 | IBMVFC_HOST_ACTION_INIT, |