Commit 228f47b9 authored by Tejun Heo's avatar Tejun Heo Committed by Jeff Garzik

sata_sil24: fix IRQ clearing race when PCIX_IRQ_WOC is used

When PCIX_IRQ_WOC is used, sil24 has an inherent race condition
between clearing IRQ pending and reading IRQ status.  If IRQ pending
is cleared after reading IRQ status, there's possibility of lost IRQ.
If IRQ pending is cleared before reading IRQ status, spurious IRQs
will occur.

sata_sil24 till now cleared IRQ pending after reading IRQ status thus
losing IRQs on machines where PCIX_IRQ_WOC was used.  Reverse the
order and ignore spurious IRQs if PCIX_IRQ_WOC.
Signed-off-by: default avatarTejun Heo <htejun@gmail.com>
Signed-off-by: default avatarJeff Garzik <jeff@garzik.org>
parent 4942de4a
...@@ -888,6 +888,16 @@ static inline void sil24_host_intr(struct ata_port *ap) ...@@ -888,6 +888,16 @@ static inline void sil24_host_intr(struct ata_port *ap)
u32 slot_stat, qc_active; u32 slot_stat, qc_active;
int rc; int rc;
/* If PCIX_IRQ_WOC, there's an inherent race window between
* clearing IRQ pending status and reading PORT_SLOT_STAT
* which may cause spurious interrupts afterwards. This is
* unavoidable and much better than losing interrupts which
* happens if IRQ pending is cleared after reading
* PORT_SLOT_STAT.
*/
if (ap->flags & SIL24_FLAG_PCIX_IRQ_WOC)
writel(PORT_IRQ_COMPLETE, port + PORT_IRQ_STAT);
slot_stat = readl(port + PORT_SLOT_STAT); slot_stat = readl(port + PORT_SLOT_STAT);
if (unlikely(slot_stat & HOST_SSTAT_ATTN)) { if (unlikely(slot_stat & HOST_SSTAT_ATTN)) {
...@@ -895,9 +905,6 @@ static inline void sil24_host_intr(struct ata_port *ap) ...@@ -895,9 +905,6 @@ static inline void sil24_host_intr(struct ata_port *ap)
return; return;
} }
if (ap->flags & SIL24_FLAG_PCIX_IRQ_WOC)
writel(PORT_IRQ_COMPLETE, port + PORT_IRQ_STAT);
qc_active = slot_stat & ~HOST_SSTAT_ATTN; qc_active = slot_stat & ~HOST_SSTAT_ATTN;
rc = ata_qc_complete_multiple(ap, qc_active, sil24_finish_qc); rc = ata_qc_complete_multiple(ap, qc_active, sil24_finish_qc);
if (rc > 0) if (rc > 0)
...@@ -910,7 +917,8 @@ static inline void sil24_host_intr(struct ata_port *ap) ...@@ -910,7 +917,8 @@ static inline void sil24_host_intr(struct ata_port *ap)
return; return;
} }
if (ata_ratelimit()) /* spurious interrupts are expected if PCIX_IRQ_WOC */
if (!(ap->flags & SIL24_FLAG_PCIX_IRQ_WOC) && ata_ratelimit())
ata_port_printk(ap, KERN_INFO, "spurious interrupt " ata_port_printk(ap, KERN_INFO, "spurious interrupt "
"(slot_stat 0x%x active_tag %d sactive 0x%x)\n", "(slot_stat 0x%x active_tag %d sactive 0x%x)\n",
slot_stat, ap->active_tag, ap->sactive); slot_stat, ap->active_tag, ap->sactive);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment