aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/ata/libata-sff.c
diff options
context:
space:
mode:
authorAlan Cox <alan@redhat.com>2009-03-24 06:23:46 -0400
committerJeff Garzik <jgarzik@redhat.com>2009-03-24 22:52:39 -0400
commitc96f1732e25362d10ee7bcac1df8412a2e6b7d23 (patch)
tree66e24eddb174d6751579ec5952f72cbbac0fb038 /drivers/ata/libata-sff.c
parent3d47aa8e7e7b2aa09256590388aa8dddc79280f9 (diff)
[libata] Improve timeout handling
On a timeout call a device specific handler early in the recovery so that we can complete and process successful commands which timed out due to IRQ loss or the like rather more elegantly. [Revised to exclude the timeout handling on a few devices that inherit from SFF but are not SFF enough to use the default timeout handler] Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
Diffstat (limited to 'drivers/ata/libata-sff.c')
-rw-r--r--drivers/ata/libata-sff.c46
1 files changed, 45 insertions, 1 deletions
diff --git a/drivers/ata/libata-sff.c b/drivers/ata/libata-sff.c
index 9a10cb055ac2..8332e97a9de3 100644
--- a/drivers/ata/libata-sff.c
+++ b/drivers/ata/libata-sff.c
@@ -65,6 +65,8 @@ const struct ata_port_operations ata_sff_port_ops = {
65 .sff_irq_on = ata_sff_irq_on, 65 .sff_irq_on = ata_sff_irq_on,
66 .sff_irq_clear = ata_sff_irq_clear, 66 .sff_irq_clear = ata_sff_irq_clear,
67 67
68 .lost_interrupt = ata_sff_lost_interrupt,
69
68 .port_start = ata_sff_port_start, 70 .port_start = ata_sff_port_start,
69}; 71};
70EXPORT_SYMBOL_GPL(ata_sff_port_ops); 72EXPORT_SYMBOL_GPL(ata_sff_port_ops);
@@ -1647,7 +1649,7 @@ EXPORT_SYMBOL_GPL(ata_sff_qc_fill_rtf);
1647 * RETURNS: 1649 * RETURNS:
1648 * One if interrupt was handled, zero if not (shared irq). 1650 * One if interrupt was handled, zero if not (shared irq).
1649 */ 1651 */
1650inline unsigned int ata_sff_host_intr(struct ata_port *ap, 1652unsigned int ata_sff_host_intr(struct ata_port *ap,
1651 struct ata_queued_cmd *qc) 1653 struct ata_queued_cmd *qc)
1652{ 1654{
1653 struct ata_eh_info *ehi = &ap->link.eh_info; 1655 struct ata_eh_info *ehi = &ap->link.eh_info;
@@ -1776,6 +1778,48 @@ irqreturn_t ata_sff_interrupt(int irq, void *dev_instance)
1776EXPORT_SYMBOL_GPL(ata_sff_interrupt); 1778EXPORT_SYMBOL_GPL(ata_sff_interrupt);
1777 1779
1778/** 1780/**
1781 * ata_sff_lost_interrupt - Check for an apparent lost interrupt
1782 * @ap: port that appears to have timed out
1783 *
1784 * Called from the libata error handlers when the core code suspects
1785 * an interrupt has been lost. If it has complete anything we can and
1786 * then return. Interface must support altstatus for this faster
1787 * recovery to occur.
1788 *
1789 * Locking:
1790 * Caller holds host lock
1791 */
1792
1793void ata_sff_lost_interrupt(struct ata_port *ap)
1794{
1795 u8 status;
1796 struct ata_queued_cmd *qc;
1797
1798 /* Only one outstanding command per SFF channel */
1799 qc = ata_qc_from_tag(ap, ap->link.active_tag);
1800 /* Check we have a live one.. */
1801 if (qc == NULL || !(qc->flags & ATA_QCFLAG_ACTIVE))
1802 return;
1803 /* We cannot lose an interrupt on a polled command */
1804 if (qc->tf.flags & ATA_TFLAG_POLLING)
1805 return;
1806 /* See if the controller thinks it is still busy - if so the command
1807 isn't a lost IRQ but is still in progress */
1808 status = ata_sff_altstatus(ap);
1809 if (status & ATA_BUSY)
1810 return;
1811
1812 /* There was a command running, we are no longer busy and we have
1813 no interrupt. */
1814 ata_port_printk(ap, KERN_WARNING, "lost interrupt (Status 0x%x)\n",
1815 status);
1816 /* Run the host interrupt logic as if the interrupt had not been
1817 lost */
1818 ata_sff_host_intr(ap, qc);
1819}
1820EXPORT_SYMBOL_GPL(ata_sff_lost_interrupt);
1821
1822/**
1779 * ata_sff_freeze - Freeze SFF controller port 1823 * ata_sff_freeze - Freeze SFF controller port
1780 * @ap: port to freeze 1824 * @ap: port to freeze
1781 * 1825 *