1 Subject: Fix dropped interrupts
2 From: Brian King <brking@linux.vnet.ibm.com>
3 References: 473115 - LTC51485
5 This patch fixes a problem of possible dropped interrupts. Currently, the ibmvfc
6 driver has a race condition where after ibmvfc_interrupt gets run, the platform
7 code clears the interrupt. This can result in lost interrupts and, in worst case
8 scenarios, result in command timeouts. Fix this by implementing a tasklet
9 similar to what the ibmvscsi driver does so that interrupt processing is no longer
10 done in the actual interrupt handler, which eliminates the race.
12 Signed-off-by: Brian King <brking@linux.vnet.ibm.com>
13 Signed-off-by: Olaf Hering <olh@suse.de>
16 drivers/scsi/ibmvscsi/ibmvfc.c | 25 +++++++++++++++++++++++--
17 drivers/scsi/ibmvscsi/ibmvfc.h | 1 +
18 2 files changed, 24 insertions(+), 2 deletions(-)
20 --- a/drivers/scsi/ibmvscsi/ibmvfc.c
21 +++ b/drivers/scsi/ibmvscsi/ibmvfc.c
22 @@ -640,6 +640,7 @@ static void ibmvfc_release_crq_queue(str
24 ibmvfc_dbg(vhost, "Releasing CRQ\n");
25 free_irq(vdev->irq, vhost);
26 + tasklet_kill(&vhost->tasklet);
28 rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address);
29 } while (rc == H_BUSY || H_IS_LONG_BUSY(rc));
30 @@ -2697,6 +2698,25 @@ static struct ibmvfc_crq *ibmvfc_next_cr
31 static irqreturn_t ibmvfc_interrupt(int irq, void *dev_instance)
33 struct ibmvfc_host *vhost = (struct ibmvfc_host *)dev_instance;
34 + unsigned long flags;
36 + spin_lock_irqsave(vhost->host->host_lock, flags);
37 + vio_disable_interrupts(to_vio_dev(vhost->dev));
38 + tasklet_schedule(&vhost->tasklet);
39 + spin_unlock_irqrestore(vhost->host->host_lock, flags);
44 + * ibmvfc_tasklet - Interrupt handler tasklet
45 + * @data: ibmvfc host struct
50 +static void ibmvfc_tasklet(void *data)
52 + struct ibmvfc_host *vhost = data;
53 struct vio_dev *vdev = to_vio_dev(vhost->dev);
54 struct ibmvfc_crq *crq;
55 struct ibmvfc_async_crq *async;
56 @@ -2704,7 +2724,6 @@ static irqreturn_t ibmvfc_interrupt(int
59 spin_lock_irqsave(vhost->host->host_lock, flags);
60 - vio_disable_interrupts(to_vio_dev(vhost->dev));
62 /* Pull all the valid messages off the CRQ */
63 while ((crq = ibmvfc_next_crq(vhost)) != NULL) {
64 @@ -2732,7 +2751,6 @@ static irqreturn_t ibmvfc_interrupt(int
67 spin_unlock_irqrestore(vhost->host->host_lock, flags);
72 @@ -3857,6 +3875,8 @@ static int ibmvfc_init_crq(struct ibmvfc
76 + tasklet_init(&vhost->tasklet, (void *)ibmvfc_tasklet, (unsigned long)vhost);
78 if ((rc = request_irq(vdev->irq, ibmvfc_interrupt, 0, IBMVFC_NAME, vhost))) {
79 dev_err(dev, "Couldn't register irq 0x%x. rc=%d\n", vdev->irq, rc);
81 @@ -3872,6 +3892,7 @@ static int ibmvfc_init_crq(struct ibmvfc
85 + tasklet_kill(&vhost->tasklet);
87 rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address);
88 } while (rc == H_BUSY || H_IS_LONG_BUSY(rc));
89 --- a/drivers/scsi/ibmvscsi/ibmvfc.h
90 +++ b/drivers/scsi/ibmvscsi/ibmvfc.h
91 @@ -684,6 +684,7 @@ struct ibmvfc_host {
92 char partition_name[97];
93 void (*job_step) (struct ibmvfc_host *);
94 struct task_struct *work_thread;
95 + struct tasklet_struct tasklet;
96 wait_queue_head_t init_wait_q;
97 wait_queue_head_t work_wait_q;