[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>
This commit is contained in:
Alan Cox
2009-03-24 10:23:46 +00:00
committed by Jeff Garzik
parent 3d47aa8e7e
commit c96f1732e2
10 changed files with 84 additions and 4 deletions

View File

@@ -65,6 +65,8 @@ const struct ata_port_operations ata_sff_port_ops = {
.sff_irq_on = ata_sff_irq_on,
.sff_irq_clear = ata_sff_irq_clear,
.lost_interrupt = ata_sff_lost_interrupt,
.port_start = ata_sff_port_start,
};
EXPORT_SYMBOL_GPL(ata_sff_port_ops);
@@ -1647,7 +1649,7 @@ EXPORT_SYMBOL_GPL(ata_sff_qc_fill_rtf);
* RETURNS:
* One if interrupt was handled, zero if not (shared irq).
*/
inline unsigned int ata_sff_host_intr(struct ata_port *ap,
unsigned int ata_sff_host_intr(struct ata_port *ap,
struct ata_queued_cmd *qc)
{
struct ata_eh_info *ehi = &ap->link.eh_info;
@@ -1775,6 +1777,48 @@ irqreturn_t ata_sff_interrupt(int irq, void *dev_instance)
}
EXPORT_SYMBOL_GPL(ata_sff_interrupt);
/**
* ata_sff_lost_interrupt - Check for an apparent lost interrupt
* @ap: port that appears to have timed out
*
* Called from the libata error handlers when the core code suspects
* an interrupt has been lost. If it has complete anything we can and
* then return. Interface must support altstatus for this faster
* recovery to occur.
*
* Locking:
* Caller holds host lock
*/
void ata_sff_lost_interrupt(struct ata_port *ap)
{
u8 status;
struct ata_queued_cmd *qc;
/* Only one outstanding command per SFF channel */
qc = ata_qc_from_tag(ap, ap->link.active_tag);
/* Check we have a live one.. */
if (qc == NULL || !(qc->flags & ATA_QCFLAG_ACTIVE))
return;
/* We cannot lose an interrupt on a polled command */
if (qc->tf.flags & ATA_TFLAG_POLLING)
return;
/* See if the controller thinks it is still busy - if so the command
isn't a lost IRQ but is still in progress */
status = ata_sff_altstatus(ap);
if (status & ATA_BUSY)
return;
/* There was a command running, we are no longer busy and we have
no interrupt. */
ata_port_printk(ap, KERN_WARNING, "lost interrupt (Status 0x%x)\n",
status);
/* Run the host interrupt logic as if the interrupt had not been
lost */
ata_sff_host_intr(ap, qc);
}
EXPORT_SYMBOL_GPL(ata_sff_lost_interrupt);
/**
* ata_sff_freeze - Freeze SFF controller port
* @ap: port to freeze