diff options
author | Dan Williams <dan.j.williams@intel.com> | 2012-03-01 20:06:24 -0500 |
---|---|---|
committer | Dan Williams <dan.j.williams@intel.com> | 2012-05-17 15:27:12 -0400 |
commit | 2396a2650a5a39634e3ad6b29e1104944e5ab88f (patch) | |
tree | a5f1e724f1a3eec60da92aef018c6c7840f95bcc /drivers/scsi/isci | |
parent | 50a92d93148ec073efd2456b007e04ecae452086 (diff) |
isci: fix interrupt disable
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>
Diffstat (limited to 'drivers/scsi/isci')
-rw-r--r-- | drivers/scsi/isci/host.c | 39 | ||||
-rw-r--r-- | drivers/scsi/isci/host.h | 1 |
2 files changed, 27 insertions, 13 deletions
diff --git a/drivers/scsi/isci/host.c b/drivers/scsi/isci/host.c index 577a8369274c..5832b13e7b07 100644 --- a/drivers/scsi/isci/host.c +++ b/drivers/scsi/isci/host.c | |||
@@ -192,22 +192,27 @@ static bool sci_controller_completion_queue_has_entries(struct isci_host *ihost) | |||
192 | 192 | ||
193 | static bool sci_controller_isr(struct isci_host *ihost) | 193 | static bool sci_controller_isr(struct isci_host *ihost) |
194 | { | 194 | { |
195 | if (sci_controller_completion_queue_has_entries(ihost)) { | 195 | if (sci_controller_completion_queue_has_entries(ihost)) |
196 | return true; | 196 | return true; |
197 | } else { | ||
198 | /* | ||
199 | * we have a spurious interrupt it could be that we have already | ||
200 | * emptied the completion queue from a previous interrupt */ | ||
201 | writel(SMU_ISR_COMPLETION, &ihost->smu_registers->interrupt_status); | ||
202 | 197 | ||
203 | /* | 198 | /* we have a spurious interrupt it could be that we have already |
204 | * There is a race in the hardware that could cause us not to be notified | 199 | * emptied the completion queue from a previous interrupt |
205 | * of an interrupt completion if we do not take this step. We will mask | 200 | * FIXME: really!? |
206 | * then unmask the interrupts so if there is another interrupt pending | 201 | */ |
207 | * the clearing of the interrupt source we get the next interrupt message. */ | 202 | writel(SMU_ISR_COMPLETION, &ihost->smu_registers->interrupt_status); |
203 | |||
204 | /* There is a race in the hardware that could cause us not to be | ||
205 | * notified of an interrupt completion if we do not take this | ||
206 | * step. We will mask then unmask the interrupts so if there is | ||
207 | * another interrupt pending the clearing of the interrupt | ||
208 | * source we get the next interrupt message. | ||
209 | */ | ||
210 | spin_lock(&ihost->scic_lock); | ||
211 | if (test_bit(IHOST_IRQ_ENABLED, &ihost->flags)) { | ||
208 | writel(0xFF000000, &ihost->smu_registers->interrupt_mask); | 212 | writel(0xFF000000, &ihost->smu_registers->interrupt_mask); |
209 | writel(0, &ihost->smu_registers->interrupt_mask); | 213 | writel(0, &ihost->smu_registers->interrupt_mask); |
210 | } | 214 | } |
215 | spin_unlock(&ihost->scic_lock); | ||
211 | 216 | ||
212 | return false; | 217 | return false; |
213 | } | 218 | } |
@@ -698,14 +703,15 @@ static u32 sci_controller_get_suggested_start_timeout(struct isci_host *ihost) | |||
698 | 703 | ||
699 | static void sci_controller_enable_interrupts(struct isci_host *ihost) | 704 | static void sci_controller_enable_interrupts(struct isci_host *ihost) |
700 | { | 705 | { |
701 | BUG_ON(ihost->smu_registers == NULL); | 706 | set_bit(IHOST_IRQ_ENABLED, &ihost->flags); |
702 | writel(0, &ihost->smu_registers->interrupt_mask); | 707 | writel(0, &ihost->smu_registers->interrupt_mask); |
703 | } | 708 | } |
704 | 709 | ||
705 | void sci_controller_disable_interrupts(struct isci_host *ihost) | 710 | void sci_controller_disable_interrupts(struct isci_host *ihost) |
706 | { | 711 | { |
707 | BUG_ON(ihost->smu_registers == NULL); | 712 | clear_bit(IHOST_IRQ_ENABLED, &ihost->flags); |
708 | writel(0xffffffff, &ihost->smu_registers->interrupt_mask); | 713 | writel(0xffffffff, &ihost->smu_registers->interrupt_mask); |
714 | readl(&ihost->smu_registers->interrupt_mask); /* flush */ | ||
709 | } | 715 | } |
710 | 716 | ||
711 | static void sci_controller_enable_port_task_scheduler(struct isci_host *ihost) | 717 | static void sci_controller_enable_port_task_scheduler(struct isci_host *ihost) |
@@ -1318,7 +1324,9 @@ void isci_host_deinit(struct isci_host *ihost) | |||
1318 | */ | 1324 | */ |
1319 | writel(0, &ihost->scu_registers->peg0.sgpio.interface_control); | 1325 | writel(0, &ihost->scu_registers->peg0.sgpio.interface_control); |
1320 | 1326 | ||
1327 | spin_lock_irq(&ihost->scic_lock); | ||
1321 | sci_controller_reset(ihost); | 1328 | sci_controller_reset(ihost); |
1329 | spin_unlock_irq(&ihost->scic_lock); | ||
1322 | 1330 | ||
1323 | /* Cancel any/all outstanding port timers */ | 1331 | /* Cancel any/all outstanding port timers */ |
1324 | for (i = 0; i < ihost->logical_port_entries; i++) { | 1332 | for (i = 0; i < ihost->logical_port_entries; i++) { |
@@ -1605,6 +1613,9 @@ static void sci_controller_reset_hardware(struct isci_host *ihost) | |||
1605 | 1613 | ||
1606 | /* The write to the UFQGP clears the UFQPR */ | 1614 | /* The write to the UFQGP clears the UFQPR */ |
1607 | writel(0, &ihost->scu_registers->sdma.unsolicited_frame_get_pointer); | 1615 | writel(0, &ihost->scu_registers->sdma.unsolicited_frame_get_pointer); |
1616 | |||
1617 | /* clear all interrupts */ | ||
1618 | writel(~SMU_INTERRUPT_STATUS_RESERVED_MASK, &ihost->smu_registers->interrupt_status); | ||
1608 | } | 1619 | } |
1609 | 1620 | ||
1610 | static void sci_controller_resetting_state_enter(struct sci_base_state_machine *sm) | 1621 | 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) | |||
2391 | int i, err; | 2402 | int i, err; |
2392 | enum sci_status status; | 2403 | enum sci_status status; |
2393 | 2404 | ||
2405 | spin_lock_irq(&ihost->scic_lock); | ||
2394 | status = sci_controller_construct(ihost, scu_base(ihost), smu_base(ihost)); | 2406 | status = sci_controller_construct(ihost, scu_base(ihost), smu_base(ihost)); |
2407 | spin_unlock_irq(&ihost->scic_lock); | ||
2395 | if (status != SCI_SUCCESS) { | 2408 | if (status != SCI_SUCCESS) { |
2396 | dev_err(&ihost->pdev->dev, | 2409 | dev_err(&ihost->pdev->dev, |
2397 | "%s: sci_controller_construct failed - status = %x\n", | 2410 | "%s: sci_controller_construct failed - status = %x\n", |
diff --git a/drivers/scsi/isci/host.h b/drivers/scsi/isci/host.h index 9dc910b9d921..9701c1d673ba 100644 --- a/drivers/scsi/isci/host.h +++ b/drivers/scsi/isci/host.h | |||
@@ -200,6 +200,7 @@ struct isci_host { | |||
200 | struct pci_dev *pdev; | 200 | struct pci_dev *pdev; |
201 | #define IHOST_START_PENDING 0 | 201 | #define IHOST_START_PENDING 0 |
202 | #define IHOST_STOP_PENDING 1 | 202 | #define IHOST_STOP_PENDING 1 |
203 | #define IHOST_IRQ_ENABLED 2 | ||
203 | unsigned long flags; | 204 | unsigned long flags; |
204 | wait_queue_head_t eventq; | 205 | wait_queue_head_t eventq; |
205 | struct Scsi_Host *shost; | 206 | struct Scsi_Host *shost; |