diff options
| author | Alan Cox <alan@redhat.com> | 2009-03-24 06:23:46 -0400 |
|---|---|---|
| committer | Jeff Garzik <jgarzik@redhat.com> | 2009-03-24 22:52:39 -0400 |
| commit | c96f1732e25362d10ee7bcac1df8412a2e6b7d23 (patch) | |
| tree | 66e24eddb174d6751579ec5952f72cbbac0fb038 | |
| parent | 3d47aa8e7e7b2aa09256590388aa8dddc79280f9 (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>
| -rw-r--r-- | drivers/ata/libata-eh.c | 19 | ||||
| -rw-r--r-- | drivers/ata/libata-sff.c | 46 | ||||
| -rw-r--r-- | drivers/ata/pata_isapnp.c | 12 | ||||
| -rw-r--r-- | drivers/ata/pdc_adma.c | 2 | ||||
| -rw-r--r-- | drivers/ata/sata_mv.c | 2 | ||||
| -rw-r--r-- | drivers/ata/sata_nv.c | 1 | ||||
| -rw-r--r-- | drivers/ata/sata_promise.c | 2 | ||||
| -rw-r--r-- | drivers/ata/sata_qstor.c | 1 | ||||
| -rw-r--r-- | drivers/ata/sata_vsc.c | 3 | ||||
| -rw-r--r-- | include/linux/libata.h | 2 |
10 files changed, 85 insertions, 5 deletions
diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index ea890911d4fa..01831312c360 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c | |||
| @@ -547,7 +547,7 @@ void ata_scsi_error(struct Scsi_Host *host) | |||
| 547 | 547 | ||
| 548 | /* For new EH, all qcs are finished in one of three ways - | 548 | /* For new EH, all qcs are finished in one of three ways - |
| 549 | * normal completion, error completion, and SCSI timeout. | 549 | * normal completion, error completion, and SCSI timeout. |
| 550 | * Both cmpletions can race against SCSI timeout. When normal | 550 | * Both completions can race against SCSI timeout. When normal |
| 551 | * completion wins, the qc never reaches EH. When error | 551 | * completion wins, the qc never reaches EH. When error |
| 552 | * completion wins, the qc has ATA_QCFLAG_FAILED set. | 552 | * completion wins, the qc has ATA_QCFLAG_FAILED set. |
| 553 | * | 553 | * |
| @@ -562,7 +562,19 @@ void ata_scsi_error(struct Scsi_Host *host) | |||
| 562 | int nr_timedout = 0; | 562 | int nr_timedout = 0; |
| 563 | 563 | ||
| 564 | spin_lock_irqsave(ap->lock, flags); | 564 | spin_lock_irqsave(ap->lock, flags); |
| 565 | 565 | ||
| 566 | /* This must occur under the ap->lock as we don't want | ||
| 567 | a polled recovery to race the real interrupt handler | ||
| 568 | |||
| 569 | The lost_interrupt handler checks for any completed but | ||
| 570 | non-notified command and completes much like an IRQ handler. | ||
| 571 | |||
| 572 | We then fall into the error recovery code which will treat | ||
| 573 | this as if normal completion won the race */ | ||
| 574 | |||
| 575 | if (ap->ops->lost_interrupt) | ||
| 576 | ap->ops->lost_interrupt(ap); | ||
| 577 | |||
| 566 | list_for_each_entry_safe(scmd, tmp, &host->eh_cmd_q, eh_entry) { | 578 | list_for_each_entry_safe(scmd, tmp, &host->eh_cmd_q, eh_entry) { |
| 567 | struct ata_queued_cmd *qc; | 579 | struct ata_queued_cmd *qc; |
| 568 | 580 | ||
| @@ -606,6 +618,9 @@ void ata_scsi_error(struct Scsi_Host *host) | |||
| 606 | ap->eh_tries = ATA_EH_MAX_TRIES; | 618 | ap->eh_tries = ATA_EH_MAX_TRIES; |
| 607 | } else | 619 | } else |
| 608 | spin_unlock_wait(ap->lock); | 620 | spin_unlock_wait(ap->lock); |
| 621 | |||
| 622 | /* If we timed raced normal completion and there is nothing to | ||
| 623 | recover nr_timedout == 0 why exactly are we doing error recovery ? */ | ||
| 609 | 624 | ||
| 610 | repeat: | 625 | repeat: |
| 611 | /* invoke error handler */ | 626 | /* invoke error handler */ |
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 | }; |
| 70 | EXPORT_SYMBOL_GPL(ata_sff_port_ops); | 72 | EXPORT_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 | */ |
| 1650 | inline unsigned int ata_sff_host_intr(struct ata_port *ap, | 1652 | unsigned 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) | |||
| 1776 | EXPORT_SYMBOL_GPL(ata_sff_interrupt); | 1778 | EXPORT_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 | |||
| 1793 | void 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 | } | ||
| 1820 | EXPORT_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 | * |
diff --git a/drivers/ata/pata_isapnp.c b/drivers/ata/pata_isapnp.c index afa8f704271e..4bceb8803a10 100644 --- a/drivers/ata/pata_isapnp.c +++ b/drivers/ata/pata_isapnp.c | |||
| @@ -17,7 +17,7 @@ | |||
| 17 | #include <linux/libata.h> | 17 | #include <linux/libata.h> |
| 18 | 18 | ||
| 19 | #define DRV_NAME "pata_isapnp" | 19 | #define DRV_NAME "pata_isapnp" |
| 20 | #define DRV_VERSION "0.2.2" | 20 | #define DRV_VERSION "0.2.5" |
| 21 | 21 | ||
| 22 | static struct scsi_host_template isapnp_sht = { | 22 | static struct scsi_host_template isapnp_sht = { |
| 23 | ATA_PIO_SHT(DRV_NAME), | 23 | ATA_PIO_SHT(DRV_NAME), |
| @@ -28,6 +28,13 @@ static struct ata_port_operations isapnp_port_ops = { | |||
| 28 | .cable_detect = ata_cable_40wire, | 28 | .cable_detect = ata_cable_40wire, |
| 29 | }; | 29 | }; |
| 30 | 30 | ||
| 31 | static struct ata_port_operations isapnp_noalt_port_ops = { | ||
| 32 | .inherits = &ata_sff_port_ops, | ||
| 33 | .cable_detect = ata_cable_40wire, | ||
| 34 | /* No altstatus so we don't want to use the lost interrupt poll */ | ||
| 35 | .lost_interrupt = ATA_OP_NULL, | ||
| 36 | }; | ||
| 37 | |||
| 31 | /** | 38 | /** |
| 32 | * isapnp_init_one - attach an isapnp interface | 39 | * isapnp_init_one - attach an isapnp interface |
| 33 | * @idev: PnP device | 40 | * @idev: PnP device |
| @@ -65,7 +72,7 @@ static int isapnp_init_one(struct pnp_dev *idev, const struct pnp_device_id *dev | |||
| 65 | 72 | ||
| 66 | ap = host->ports[0]; | 73 | ap = host->ports[0]; |
| 67 | 74 | ||
| 68 | ap->ops = &isapnp_port_ops; | 75 | ap->ops = &isapnp_noalt_port_ops; |
| 69 | ap->pio_mask = ATA_PIO0; | 76 | ap->pio_mask = ATA_PIO0; |
| 70 | ap->flags |= ATA_FLAG_SLAVE_POSS; | 77 | ap->flags |= ATA_FLAG_SLAVE_POSS; |
| 71 | 78 | ||
| @@ -76,6 +83,7 @@ static int isapnp_init_one(struct pnp_dev *idev, const struct pnp_device_id *dev | |||
| 76 | pnp_port_start(idev, 1), 1); | 83 | pnp_port_start(idev, 1), 1); |
| 77 | ap->ioaddr.altstatus_addr = ctl_addr; | 84 | ap->ioaddr.altstatus_addr = ctl_addr; |
| 78 | ap->ioaddr.ctl_addr = ctl_addr; | 85 | ap->ioaddr.ctl_addr = ctl_addr; |
| 86 | ap->ops = &isapnp_port_ops; | ||
| 79 | } | 87 | } |
| 80 | 88 | ||
| 81 | ata_sff_std_ports(&ap->ioaddr); | 89 | ata_sff_std_ports(&ap->ioaddr); |
diff --git a/drivers/ata/pdc_adma.c b/drivers/ata/pdc_adma.c index c509c206a459..39588178d028 100644 --- a/drivers/ata/pdc_adma.c +++ b/drivers/ata/pdc_adma.c | |||
| @@ -148,6 +148,8 @@ static struct scsi_host_template adma_ata_sht = { | |||
| 148 | static struct ata_port_operations adma_ata_ops = { | 148 | static struct ata_port_operations adma_ata_ops = { |
| 149 | .inherits = &ata_sff_port_ops, | 149 | .inherits = &ata_sff_port_ops, |
| 150 | 150 | ||
| 151 | .lost_interrupt = ATA_OP_NULL, | ||
| 152 | |||
| 151 | .check_atapi_dma = adma_check_atapi_dma, | 153 | .check_atapi_dma = adma_check_atapi_dma, |
| 152 | .qc_prep = adma_qc_prep, | 154 | .qc_prep = adma_qc_prep, |
| 153 | .qc_issue = adma_qc_issue, | 155 | .qc_issue = adma_qc_issue, |
diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c index 8a751054c8a1..a377226b81c8 100644 --- a/drivers/ata/sata_mv.c +++ b/drivers/ata/sata_mv.c | |||
| @@ -646,6 +646,8 @@ static struct scsi_host_template mv6_sht = { | |||
| 646 | static struct ata_port_operations mv5_ops = { | 646 | static struct ata_port_operations mv5_ops = { |
| 647 | .inherits = &ata_sff_port_ops, | 647 | .inherits = &ata_sff_port_ops, |
| 648 | 648 | ||
| 649 | .lost_interrupt = ATA_OP_NULL, | ||
| 650 | |||
| 649 | .qc_defer = mv_qc_defer, | 651 | .qc_defer = mv_qc_defer, |
| 650 | .qc_prep = mv_qc_prep, | 652 | .qc_prep = mv_qc_prep, |
| 651 | .qc_issue = mv_qc_issue, | 653 | .qc_issue = mv_qc_issue, |
diff --git a/drivers/ata/sata_nv.c b/drivers/ata/sata_nv.c index 2f523f8c27f6..6cda12ba8122 100644 --- a/drivers/ata/sata_nv.c +++ b/drivers/ata/sata_nv.c | |||
| @@ -408,6 +408,7 @@ static struct scsi_host_template nv_swncq_sht = { | |||
| 408 | 408 | ||
| 409 | static struct ata_port_operations nv_common_ops = { | 409 | static struct ata_port_operations nv_common_ops = { |
| 410 | .inherits = &ata_bmdma_port_ops, | 410 | .inherits = &ata_bmdma_port_ops, |
| 411 | .lost_interrupt = ATA_OP_NULL, | ||
| 411 | .scr_read = nv_scr_read, | 412 | .scr_read = nv_scr_read, |
| 412 | .scr_write = nv_scr_write, | 413 | .scr_write = nv_scr_write, |
| 413 | }; | 414 | }; |
diff --git a/drivers/ata/sata_promise.c b/drivers/ata/sata_promise.c index 3ad2b8863636..b1fd7d62071a 100644 --- a/drivers/ata/sata_promise.c +++ b/drivers/ata/sata_promise.c | |||
| @@ -176,7 +176,9 @@ static const struct ata_port_operations pdc_common_ops = { | |||
| 176 | .check_atapi_dma = pdc_check_atapi_dma, | 176 | .check_atapi_dma = pdc_check_atapi_dma, |
| 177 | .qc_prep = pdc_qc_prep, | 177 | .qc_prep = pdc_qc_prep, |
| 178 | .qc_issue = pdc_qc_issue, | 178 | .qc_issue = pdc_qc_issue, |
| 179 | |||
| 179 | .sff_irq_clear = pdc_irq_clear, | 180 | .sff_irq_clear = pdc_irq_clear, |
| 181 | .lost_interrupt = ATA_OP_NULL, | ||
| 180 | 182 | ||
| 181 | .post_internal_cmd = pdc_post_internal_cmd, | 183 | .post_internal_cmd = pdc_post_internal_cmd, |
| 182 | .error_handler = pdc_error_handler, | 184 | .error_handler = pdc_error_handler, |
diff --git a/drivers/ata/sata_qstor.c b/drivers/ata/sata_qstor.c index 7112d89fd9ff..c3936d35cdac 100644 --- a/drivers/ata/sata_qstor.c +++ b/drivers/ata/sata_qstor.c | |||
| @@ -147,6 +147,7 @@ static struct ata_port_operations qs_ata_ops = { | |||
| 147 | .softreset = ATA_OP_NULL, | 147 | .softreset = ATA_OP_NULL, |
| 148 | .error_handler = qs_error_handler, | 148 | .error_handler = qs_error_handler, |
| 149 | .post_internal_cmd = ATA_OP_NULL, | 149 | .post_internal_cmd = ATA_OP_NULL, |
| 150 | .lost_interrupt = ATA_OP_NULL, | ||
| 150 | 151 | ||
| 151 | .scr_read = qs_scr_read, | 152 | .scr_read = qs_scr_read, |
| 152 | .scr_write = qs_scr_write, | 153 | .scr_write = qs_scr_write, |
diff --git a/drivers/ata/sata_vsc.c b/drivers/ata/sata_vsc.c index ef211f333d7b..ed70bd28fa2c 100644 --- a/drivers/ata/sata_vsc.c +++ b/drivers/ata/sata_vsc.c | |||
| @@ -308,6 +308,9 @@ static struct scsi_host_template vsc_sata_sht = { | |||
| 308 | 308 | ||
| 309 | static struct ata_port_operations vsc_sata_ops = { | 309 | static struct ata_port_operations vsc_sata_ops = { |
| 310 | .inherits = &ata_bmdma_port_ops, | 310 | .inherits = &ata_bmdma_port_ops, |
| 311 | /* The IRQ handling is not quite standard SFF behaviour so we | ||
| 312 | cannot use the default lost interrupt handler */ | ||
| 313 | .lost_interrupt = ATA_OP_NULL, | ||
| 311 | .sff_tf_load = vsc_sata_tf_load, | 314 | .sff_tf_load = vsc_sata_tf_load, |
| 312 | .sff_tf_read = vsc_sata_tf_read, | 315 | .sff_tf_read = vsc_sata_tf_read, |
| 313 | .freeze = vsc_freeze, | 316 | .freeze = vsc_freeze, |
diff --git a/include/linux/libata.h b/include/linux/libata.h index 3a07a32dfc2e..76262d83656b 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h | |||
| @@ -795,6 +795,7 @@ struct ata_port_operations { | |||
| 795 | ata_reset_fn_t pmp_hardreset; | 795 | ata_reset_fn_t pmp_hardreset; |
| 796 | ata_postreset_fn_t pmp_postreset; | 796 | ata_postreset_fn_t pmp_postreset; |
| 797 | void (*error_handler)(struct ata_port *ap); | 797 | void (*error_handler)(struct ata_port *ap); |
| 798 | void (*lost_interrupt)(struct ata_port *ap); | ||
| 798 | void (*post_internal_cmd)(struct ata_queued_cmd *qc); | 799 | void (*post_internal_cmd)(struct ata_queued_cmd *qc); |
| 799 | 800 | ||
| 800 | /* | 801 | /* |
| @@ -1577,6 +1578,7 @@ extern bool ata_sff_qc_fill_rtf(struct ata_queued_cmd *qc); | |||
| 1577 | extern unsigned int ata_sff_host_intr(struct ata_port *ap, | 1578 | extern unsigned int ata_sff_host_intr(struct ata_port *ap, |
| 1578 | struct ata_queued_cmd *qc); | 1579 | struct ata_queued_cmd *qc); |
| 1579 | extern irqreturn_t ata_sff_interrupt(int irq, void *dev_instance); | 1580 | extern irqreturn_t ata_sff_interrupt(int irq, void *dev_instance); |
| 1581 | extern void ata_sff_lost_interrupt(struct ata_port *ap); | ||
| 1580 | extern void ata_sff_freeze(struct ata_port *ap); | 1582 | extern void ata_sff_freeze(struct ata_port *ap); |
| 1581 | extern void ata_sff_thaw(struct ata_port *ap); | 1583 | extern void ata_sff_thaw(struct ata_port *ap); |
| 1582 | extern int ata_sff_prereset(struct ata_link *link, unsigned long deadline); | 1584 | extern int ata_sff_prereset(struct ata_link *link, unsigned long deadline); |
