]> Pileus Git - ~andy/linux/commitdiff
isci: fix interrupt disable
authorDan Williams <dan.j.williams@intel.com>
Fri, 2 Mar 2012 01:06:24 +0000 (17:06 -0800)
committerDan Williams <dan.j.williams@intel.com>
Thu, 17 May 2012 19:27:12 +0000 (12:27 -0700)
There is a (dubious?) lost irq workaround in sci_controller_isr() that
effectively nullifies attempts to disable interrupts.  Until the
workaround can be re-evaluated add some infrastructure to prevent the
interrupt handler from inadvertantly re-enabling interrupts.

The failure mode was interrupts continuing to run after the driver had
been removed and its iomappings torn down.

Reported-by: Jacek Danecki <jacek.danecki@intel.com>
Tested-by: Jacek Danecki <jacek.danecki@intel.com>
[richard: clear remaining interrupts at the end of reset]
Acked-by: Richard Boyd <richard.g.boyd@intel.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
drivers/scsi/isci/host.c
drivers/scsi/isci/host.h

index 577a8369274cf8af37d73e443c3f46f225f04aa3..5832b13e7b07ebc7e20994d66e5bf55b643ec7e9 100644 (file)
@@ -192,22 +192,27 @@ static bool sci_controller_completion_queue_has_entries(struct isci_host *ihost)
 
 static bool sci_controller_isr(struct isci_host *ihost)
 {
-       if (sci_controller_completion_queue_has_entries(ihost)) {
+       if (sci_controller_completion_queue_has_entries(ihost))
                return true;
-       } else {
-               /*
-                * we have a spurious interrupt it could be that we have already
-                * emptied the completion queue from a previous interrupt */
-               writel(SMU_ISR_COMPLETION, &ihost->smu_registers->interrupt_status);
 
-               /*
-                * There is a race in the hardware that could cause us not to be notified
-                * of an interrupt completion if we do not take this step.  We will mask
-                * then unmask the interrupts so if there is another interrupt pending
-                * the clearing of the interrupt source we get the next interrupt message. */
+       /* we have a spurious interrupt it could be that we have already
+        * emptied the completion queue from a previous interrupt
+        * FIXME: really!?
+        */
+       writel(SMU_ISR_COMPLETION, &ihost->smu_registers->interrupt_status);
+
+       /* There is a race in the hardware that could cause us not to be
+        * notified of an interrupt completion if we do not take this
+        * step.  We will mask then unmask the interrupts so if there is
+        * another interrupt pending the clearing of the interrupt
+        * source we get the next interrupt message.
+        */
+       spin_lock(&ihost->scic_lock);
+       if (test_bit(IHOST_IRQ_ENABLED, &ihost->flags)) {
                writel(0xFF000000, &ihost->smu_registers->interrupt_mask);
                writel(0, &ihost->smu_registers->interrupt_mask);
        }
+       spin_unlock(&ihost->scic_lock);
 
        return false;
 }
@@ -698,14 +703,15 @@ static u32 sci_controller_get_suggested_start_timeout(struct isci_host *ihost)
 
 static void sci_controller_enable_interrupts(struct isci_host *ihost)
 {
-       BUG_ON(ihost->smu_registers == NULL);
+       set_bit(IHOST_IRQ_ENABLED, &ihost->flags);
        writel(0, &ihost->smu_registers->interrupt_mask);
 }
 
 void sci_controller_disable_interrupts(struct isci_host *ihost)
 {
-       BUG_ON(ihost->smu_registers == NULL);
+       clear_bit(IHOST_IRQ_ENABLED, &ihost->flags);
        writel(0xffffffff, &ihost->smu_registers->interrupt_mask);
+       readl(&ihost->smu_registers->interrupt_mask); /* flush */
 }
 
 static void sci_controller_enable_port_task_scheduler(struct isci_host *ihost)
@@ -1318,7 +1324,9 @@ void isci_host_deinit(struct isci_host *ihost)
         */
        writel(0, &ihost->scu_registers->peg0.sgpio.interface_control);
 
+       spin_lock_irq(&ihost->scic_lock);
        sci_controller_reset(ihost);
+       spin_unlock_irq(&ihost->scic_lock);
 
        /* Cancel any/all outstanding port timers */
        for (i = 0; i < ihost->logical_port_entries; i++) {
@@ -1605,6 +1613,9 @@ static void sci_controller_reset_hardware(struct isci_host *ihost)
 
        /* The write to the UFQGP clears the UFQPR */
        writel(0, &ihost->scu_registers->sdma.unsolicited_frame_get_pointer);
+
+       /* clear all interrupts */
+       writel(~SMU_INTERRUPT_STATUS_RESERVED_MASK, &ihost->smu_registers->interrupt_status);
 }
 
 static void sci_controller_resetting_state_enter(struct sci_base_state_machine *sm)
@@ -2391,7 +2402,9 @@ int isci_host_init(struct isci_host *ihost)
        int i, err;
        enum sci_status status;
 
+       spin_lock_irq(&ihost->scic_lock);
        status = sci_controller_construct(ihost, scu_base(ihost), smu_base(ihost));
+       spin_unlock_irq(&ihost->scic_lock);
        if (status != SCI_SUCCESS) {
                dev_err(&ihost->pdev->dev,
                        "%s: sci_controller_construct failed - status = %x\n",
index 9dc910b9d9218134334d412f7e26cd4c766e95c5..9701c1d673ba74a73b3bc08b83a1aca687456647 100644 (file)
@@ -200,6 +200,7 @@ struct isci_host {
        struct pci_dev *pdev;
        #define IHOST_START_PENDING 0
        #define IHOST_STOP_PENDING 1
+       #define IHOST_IRQ_ENABLED 2
        unsigned long flags;
        wait_queue_head_t eventq;
        struct Scsi_Host *shost;