diff options
author | Brian King <brking@linux.vnet.ibm.com> | 2009-03-20 16:44:35 -0400 |
---|---|---|
committer | James Bottomley <James.Bottomley@HansenPartnership.com> | 2009-04-03 10:22:42 -0400 |
commit | 039a08981a49e05f09db969cdd3f38f05a5df46f (patch) | |
tree | b7ba49c8740a97ea4be7c7d99445ec3a224f45a7 /drivers/scsi/ibmvscsi | |
parent | 8fe74cf053de7ad2124a894996f84fa890a81093 (diff) |
[SCSI] ibmvfc: Fix dropped interrupts
This patch fixes a problem of possible dropped interrupts. Currently,
the ibmvfc driver has a race condition where after ibmvfc_interrupt
gets run, the platform code clears the interrupt. This can result in
lost interrupts and, in worst case scenarios, result in command
timeouts. Fix this by implementing a tasklet similar to what the
ibmvscsi driver does so that interrupt processing is no longer done in
the actual interrupt handler, which eliminates the race.
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')
-rw-r--r-- | drivers/scsi/ibmvscsi/ibmvfc.c | 25 | ||||
-rw-r--r-- | drivers/scsi/ibmvscsi/ibmvfc.h | 1 |
2 files changed, 24 insertions, 2 deletions
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c index 93d1fbe4ee5d..229e360f0220 100644 --- a/drivers/scsi/ibmvscsi/ibmvfc.c +++ b/drivers/scsi/ibmvscsi/ibmvfc.c | |||
@@ -640,6 +640,7 @@ static void ibmvfc_release_crq_queue(struct ibmvfc_host *vhost) | |||
640 | 640 | ||
641 | ibmvfc_dbg(vhost, "Releasing CRQ\n"); | 641 | ibmvfc_dbg(vhost, "Releasing CRQ\n"); |
642 | free_irq(vdev->irq, vhost); | 642 | free_irq(vdev->irq, vhost); |
643 | tasklet_kill(&vhost->tasklet); | ||
643 | do { | 644 | do { |
644 | rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address); | 645 | rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address); |
645 | } while (rc == H_BUSY || H_IS_LONG_BUSY(rc)); | 646 | } while (rc == H_BUSY || H_IS_LONG_BUSY(rc)); |
@@ -2699,6 +2700,25 @@ static struct ibmvfc_crq *ibmvfc_next_crq(struct ibmvfc_host *vhost) | |||
2699 | static irqreturn_t ibmvfc_interrupt(int irq, void *dev_instance) | 2700 | static irqreturn_t ibmvfc_interrupt(int irq, void *dev_instance) |
2700 | { | 2701 | { |
2701 | struct ibmvfc_host *vhost = (struct ibmvfc_host *)dev_instance; | 2702 | struct ibmvfc_host *vhost = (struct ibmvfc_host *)dev_instance; |
2703 | unsigned long flags; | ||
2704 | |||
2705 | spin_lock_irqsave(vhost->host->host_lock, flags); | ||
2706 | vio_disable_interrupts(to_vio_dev(vhost->dev)); | ||
2707 | tasklet_schedule(&vhost->tasklet); | ||
2708 | spin_unlock_irqrestore(vhost->host->host_lock, flags); | ||
2709 | return IRQ_HANDLED; | ||
2710 | } | ||
2711 | |||
2712 | /** | ||
2713 | * ibmvfc_tasklet - Interrupt handler tasklet | ||
2714 | * @data: ibmvfc host struct | ||
2715 | * | ||
2716 | * Returns: | ||
2717 | * Nothing | ||
2718 | **/ | ||
2719 | static void ibmvfc_tasklet(void *data) | ||
2720 | { | ||
2721 | struct ibmvfc_host *vhost = data; | ||
2702 | struct vio_dev *vdev = to_vio_dev(vhost->dev); | 2722 | struct vio_dev *vdev = to_vio_dev(vhost->dev); |
2703 | struct ibmvfc_crq *crq; | 2723 | struct ibmvfc_crq *crq; |
2704 | struct ibmvfc_async_crq *async; | 2724 | struct ibmvfc_async_crq *async; |
@@ -2706,7 +2726,6 @@ static irqreturn_t ibmvfc_interrupt(int irq, void *dev_instance) | |||
2706 | int done = 0; | 2726 | int done = 0; |
2707 | 2727 | ||
2708 | spin_lock_irqsave(vhost->host->host_lock, flags); | 2728 | spin_lock_irqsave(vhost->host->host_lock, flags); |
2709 | vio_disable_interrupts(to_vio_dev(vhost->dev)); | ||
2710 | while (!done) { | 2729 | while (!done) { |
2711 | /* Pull all the valid messages off the CRQ */ | 2730 | /* Pull all the valid messages off the CRQ */ |
2712 | while ((crq = ibmvfc_next_crq(vhost)) != NULL) { | 2731 | while ((crq = ibmvfc_next_crq(vhost)) != NULL) { |
@@ -2734,7 +2753,6 @@ static irqreturn_t ibmvfc_interrupt(int irq, void *dev_instance) | |||
2734 | } | 2753 | } |
2735 | 2754 | ||
2736 | spin_unlock_irqrestore(vhost->host->host_lock, flags); | 2755 | spin_unlock_irqrestore(vhost->host->host_lock, flags); |
2737 | return IRQ_HANDLED; | ||
2738 | } | 2756 | } |
2739 | 2757 | ||
2740 | /** | 2758 | /** |
@@ -3859,6 +3877,8 @@ static int ibmvfc_init_crq(struct ibmvfc_host *vhost) | |||
3859 | 3877 | ||
3860 | retrc = 0; | 3878 | retrc = 0; |
3861 | 3879 | ||
3880 | tasklet_init(&vhost->tasklet, (void *)ibmvfc_tasklet, (unsigned long)vhost); | ||
3881 | |||
3862 | if ((rc = request_irq(vdev->irq, ibmvfc_interrupt, 0, IBMVFC_NAME, vhost))) { | 3882 | if ((rc = request_irq(vdev->irq, ibmvfc_interrupt, 0, IBMVFC_NAME, vhost))) { |
3863 | dev_err(dev, "Couldn't register irq 0x%x. rc=%d\n", vdev->irq, rc); | 3883 | dev_err(dev, "Couldn't register irq 0x%x. rc=%d\n", vdev->irq, rc); |
3864 | goto req_irq_failed; | 3884 | goto req_irq_failed; |
@@ -3874,6 +3894,7 @@ static int ibmvfc_init_crq(struct ibmvfc_host *vhost) | |||
3874 | return retrc; | 3894 | return retrc; |
3875 | 3895 | ||
3876 | req_irq_failed: | 3896 | req_irq_failed: |
3897 | tasklet_kill(&vhost->tasklet); | ||
3877 | do { | 3898 | do { |
3878 | rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address); | 3899 | rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address); |
3879 | } while (rc == H_BUSY || H_IS_LONG_BUSY(rc)); | 3900 | } while (rc == H_BUSY || H_IS_LONG_BUSY(rc)); |
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.h b/drivers/scsi/ibmvscsi/ibmvfc.h index b21e071b9862..70107522e3a9 100644 --- a/drivers/scsi/ibmvscsi/ibmvfc.h +++ b/drivers/scsi/ibmvscsi/ibmvfc.h | |||
@@ -684,6 +684,7 @@ struct ibmvfc_host { | |||
684 | char partition_name[97]; | 684 | char partition_name[97]; |
685 | void (*job_step) (struct ibmvfc_host *); | 685 | void (*job_step) (struct ibmvfc_host *); |
686 | struct task_struct *work_thread; | 686 | struct task_struct *work_thread; |
687 | struct tasklet_struct tasklet; | ||
687 | wait_queue_head_t init_wait_q; | 688 | wait_queue_head_t init_wait_q; |
688 | wait_queue_head_t work_wait_q; | 689 | wait_queue_head_t work_wait_q; |
689 | }; | 690 | }; |