aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/scsi/ibmvscsi
diff options
context:
space:
mode:
authorBrian King <brking@linux.vnet.ibm.com>2009-03-20 16:44:35 -0400
committerJames Bottomley <James.Bottomley@HansenPartnership.com>2009-04-03 10:22:42 -0400
commit039a08981a49e05f09db969cdd3f38f05a5df46f (patch)
treeb7ba49c8740a97ea4be7c7d99445ec3a224f45a7 /drivers/scsi/ibmvscsi
parent8fe74cf053de7ad2124a894996f84fa890a81093 (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.c25
-rw-r--r--drivers/scsi/ibmvscsi/ibmvfc.h1
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)
2699static irqreturn_t ibmvfc_interrupt(int irq, void *dev_instance) 2700static 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 **/
2719static 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
3876req_irq_failed: 3896req_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};