Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c96f1732 authored by Alan Cox's avatar Alan Cox Committed by Jeff Garzik
Browse files

[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: default avatarAlan Cox <alan@redhat.com>
Signed-off-by: default avatarJeff Garzik <jgarzik@redhat.com>
parent 3d47aa8e
Loading
Loading
Loading
Loading
+17 −2
Original line number Diff line number Diff line
@@ -547,7 +547,7 @@ void ata_scsi_error(struct Scsi_Host *host)

	/* For new EH, all qcs are finished in one of three ways -
	 * normal completion, error completion, and SCSI timeout.
	 * Both cmpletions can race against SCSI timeout.  When normal
	 * Both completions can race against SCSI timeout.  When normal
	 * completion wins, the qc never reaches EH.  When error
	 * completion wins, the qc has ATA_QCFLAG_FAILED set.
	 *
@@ -563,6 +563,18 @@ void ata_scsi_error(struct Scsi_Host *host)

		spin_lock_irqsave(ap->lock, flags);
		
		/* This must occur under the ap->lock as we don't want
		   a polled recovery to race the real interrupt handler
		   
		   The lost_interrupt handler checks for any completed but
		   non-notified command and completes much like an IRQ handler.
		   
		   We then fall into the error recovery code which will treat
		   this as if normal completion won the race */

		if (ap->ops->lost_interrupt)
			ap->ops->lost_interrupt(ap);
			
		list_for_each_entry_safe(scmd, tmp, &host->eh_cmd_q, eh_entry) {
			struct ata_queued_cmd *qc;

@@ -607,6 +619,9 @@ void ata_scsi_error(struct Scsi_Host *host)
	} else
		spin_unlock_wait(ap->lock);
		
	/* If we timed raced normal completion and there is nothing to
	   recover nr_timedout == 0 why exactly are we doing error recovery ? */

 repeat:
	/* invoke error handler */
	if (ap->ops->error_handler) {
+45 −1
Original line number Diff line number Diff line
@@ -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
+10 −2
Original line number Diff line number Diff line
@@ -17,7 +17,7 @@
#include <linux/libata.h>

#define DRV_NAME "pata_isapnp"
#define DRV_VERSION "0.2.2"
#define DRV_VERSION "0.2.5"

static struct scsi_host_template isapnp_sht = {
	ATA_PIO_SHT(DRV_NAME),
@@ -28,6 +28,13 @@ static struct ata_port_operations isapnp_port_ops = {
	.cable_detect	= ata_cable_40wire,
};

static struct ata_port_operations isapnp_noalt_port_ops = {
	.inherits	= &ata_sff_port_ops,
	.cable_detect	= ata_cable_40wire,
	/* No altstatus so we don't want to use the lost interrupt poll */
	.lost_interrupt = ATA_OP_NULL,
};

/**
 *	isapnp_init_one		-	attach an isapnp interface
 *	@idev: PnP device
@@ -65,7 +72,7 @@ static int isapnp_init_one(struct pnp_dev *idev, const struct pnp_device_id *dev

	ap = host->ports[0];

	ap->ops = &isapnp_port_ops;
	ap->ops = &isapnp_noalt_port_ops;
	ap->pio_mask = ATA_PIO0;
	ap->flags |= ATA_FLAG_SLAVE_POSS;

@@ -76,6 +83,7 @@ static int isapnp_init_one(struct pnp_dev *idev, const struct pnp_device_id *dev
					   pnp_port_start(idev, 1), 1);
		ap->ioaddr.altstatus_addr = ctl_addr;
		ap->ioaddr.ctl_addr = ctl_addr;
		ap->ops = &isapnp_port_ops;
	}

	ata_sff_std_ports(&ap->ioaddr);
+2 −0
Original line number Diff line number Diff line
@@ -148,6 +148,8 @@ static struct scsi_host_template adma_ata_sht = {
static struct ata_port_operations adma_ata_ops = {
	.inherits		= &ata_sff_port_ops,

	.lost_interrupt		= ATA_OP_NULL,

	.check_atapi_dma	= adma_check_atapi_dma,
	.qc_prep		= adma_qc_prep,
	.qc_issue		= adma_qc_issue,
+2 −0
Original line number Diff line number Diff line
@@ -646,6 +646,8 @@ static struct scsi_host_template mv6_sht = {
static struct ata_port_operations mv5_ops = {
	.inherits		= &ata_sff_port_ops,

	.lost_interrupt		= ATA_OP_NULL,

	.qc_defer		= mv_qc_defer,
	.qc_prep		= mv_qc_prep,
	.qc_issue		= mv_qc_issue,
Loading