diff options
Diffstat (limited to 'drivers/scsi')
-rw-r--r-- | drivers/scsi/ahci.c | 3 | ||||
-rw-r--r-- | drivers/scsi/libata-bmdma.c | 11 | ||||
-rw-r--r-- | drivers/scsi/libata-core.c | 85 | ||||
-rw-r--r-- | drivers/scsi/libata-eh.c | 60 | ||||
-rw-r--r-- | drivers/scsi/sata_sil24.c | 3 |
5 files changed, 147 insertions, 15 deletions
diff --git a/drivers/scsi/ahci.c b/drivers/scsi/ahci.c index 45fd71d8012..8493b021cc0 100644 --- a/drivers/scsi/ahci.c +++ b/drivers/scsi/ahci.c @@ -1026,7 +1026,8 @@ static void ahci_error_handler(struct ata_port *ap) } /* perform recovery */ - ata_do_eh(ap, ahci_softreset, ahci_hardreset, ahci_postreset); + ata_do_eh(ap, ata_std_prereset, ahci_softreset, ahci_hardreset, + ahci_postreset); } static void ahci_post_internal_cmd(struct ata_queued_cmd *qc) diff --git a/drivers/scsi/libata-bmdma.c b/drivers/scsi/libata-bmdma.c index 6d30d2c5296..4bc05371737 100644 --- a/drivers/scsi/libata-bmdma.c +++ b/drivers/scsi/libata-bmdma.c @@ -695,6 +695,7 @@ void ata_bmdma_thaw(struct ata_port *ap) /** * ata_bmdma_drive_eh - Perform EH with given methods for BMDMA controller * @ap: port to handle error for + * @prereset: prereset method (can be NULL) * @softreset: softreset method (can be NULL) * @hardreset: hardreset method (can be NULL) * @postreset: postreset method (can be NULL) @@ -710,8 +711,9 @@ void ata_bmdma_thaw(struct ata_port *ap) * LOCKING: * Kernel thread context (may sleep) */ -void ata_bmdma_drive_eh(struct ata_port *ap, ata_reset_fn_t softreset, - ata_reset_fn_t hardreset, ata_postreset_fn_t postreset) +void ata_bmdma_drive_eh(struct ata_port *ap, ata_prereset_fn_t prereset, + ata_reset_fn_t softreset, ata_reset_fn_t hardreset, + ata_postreset_fn_t postreset) { struct ata_host_set *host_set = ap->host_set; struct ata_eh_context *ehc = &ap->eh_context; @@ -759,7 +761,7 @@ void ata_bmdma_drive_eh(struct ata_port *ap, ata_reset_fn_t softreset, ata_eh_thaw_port(ap); /* PIO and DMA engines have been stopped, perform recovery */ - ata_do_eh(ap, softreset, hardreset, postreset); + ata_do_eh(ap, prereset, softreset, hardreset, postreset); } /** @@ -779,7 +781,8 @@ void ata_bmdma_error_handler(struct ata_port *ap) if (sata_scr_valid(ap)) hardreset = sata_std_hardreset; - ata_bmdma_drive_eh(ap, ata_std_softreset, hardreset, ata_std_postreset); + ata_bmdma_drive_eh(ap, ata_std_prereset, ata_std_softreset, hardreset, + ata_std_postreset); } /** diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c index 4823ecefb8a..2531a701d6e 100644 --- a/drivers/scsi/libata-core.c +++ b/drivers/scsi/libata-core.c @@ -2525,6 +2525,90 @@ int sata_phy_resume(struct ata_port *ap, const unsigned long *params) return sata_phy_debounce(ap, params); } +static void ata_wait_spinup(struct ata_port *ap) +{ + struct ata_eh_context *ehc = &ap->eh_context; + unsigned long end, secs; + int rc; + + /* first, debounce phy if SATA */ + if (ap->cbl == ATA_CBL_SATA) { + rc = sata_phy_debounce(ap, sata_deb_timing_eh); + + /* if debounced successfully and offline, no need to wait */ + if ((rc == 0 || rc == -EOPNOTSUPP) && ata_port_offline(ap)) + return; + } + + /* okay, let's give the drive time to spin up */ + end = ehc->i.hotplug_timestamp + ATA_SPINUP_WAIT * HZ / 1000; + secs = ((end - jiffies) + HZ - 1) / HZ; + + if (time_after(jiffies, end)) + return; + + if (secs > 5) + ata_port_printk(ap, KERN_INFO, "waiting for device to spin up " + "(%lu secs)\n", secs); + + schedule_timeout_uninterruptible(end - jiffies); +} + +/** + * ata_std_prereset - prepare for reset + * @ap: ATA port to be reset + * + * @ap is about to be reset. Initialize it. + * + * LOCKING: + * Kernel thread context (may sleep) + * + * RETURNS: + * 0 on success, -errno otherwise. + */ +int ata_std_prereset(struct ata_port *ap) +{ + struct ata_eh_context *ehc = &ap->eh_context; + const unsigned long *timing; + int rc; + + /* hotplug? */ + if (ehc->i.flags & ATA_EHI_HOTPLUGGED) { + if (ap->flags & ATA_FLAG_HRST_TO_RESUME) + ehc->i.action |= ATA_EH_HARDRESET; + if (ap->flags & ATA_FLAG_SKIP_D2H_BSY) + ata_wait_spinup(ap); + } + + /* if we're about to do hardreset, nothing more to do */ + if (ehc->i.action & ATA_EH_HARDRESET) + return 0; + + /* if SATA, resume phy */ + if (ap->cbl == ATA_CBL_SATA) { + if (ap->flags & ATA_FLAG_LOADING) + timing = sata_deb_timing_boot; + else + timing = sata_deb_timing_eh; + + rc = sata_phy_resume(ap, timing); + if (rc && rc != -EOPNOTSUPP) { + /* phy resume failed */ + ata_port_printk(ap, KERN_WARNING, "failed to resume " + "link for reset (errno=%d)\n", rc); + return rc; + } + } + + /* Wait for !BSY if the controller can wait for the first D2H + * Reg FIS and we don't know that no device is attached. + */ + if (!(ap->flags & ATA_FLAG_SKIP_D2H_BSY) && !ata_port_offline(ap)) + ata_busy_sleep(ap, ATA_TMOUT_BOOT_QUICK, ATA_TMOUT_BOOT); + + return 0; +} + /** * ata_std_probeinit - initialize probing * @ap: port to be probed @@ -5840,6 +5924,7 @@ EXPORT_SYMBOL_GPL(sata_phy_reset); EXPORT_SYMBOL_GPL(__sata_phy_reset); EXPORT_SYMBOL_GPL(ata_bus_reset); EXPORT_SYMBOL_GPL(ata_std_probeinit); +EXPORT_SYMBOL_GPL(ata_std_prereset); EXPORT_SYMBOL_GPL(ata_std_softreset); EXPORT_SYMBOL_GPL(sata_std_hardreset); EXPORT_SYMBOL_GPL(ata_std_postreset); diff --git a/drivers/scsi/libata-eh.c b/drivers/scsi/libata-eh.c index 9173d8f2ce5..0e66f140e53 100644 --- a/drivers/scsi/libata-eh.c +++ b/drivers/scsi/libata-eh.c @@ -1318,20 +1318,58 @@ static void ata_eh_report(struct ata_port *ap) } } -static int ata_eh_reset(struct ata_port *ap, ata_reset_fn_t softreset, +static int ata_eh_reset(struct ata_port *ap, + ata_prereset_fn_t prereset, ata_reset_fn_t softreset, ata_reset_fn_t hardreset, ata_postreset_fn_t postreset) { struct ata_eh_context *ehc = &ap->eh_context; unsigned int classes[ATA_MAX_DEVICES]; int tries = ATA_EH_RESET_TRIES; + unsigned int action; ata_reset_fn_t reset; int i, rc; + /* Determine which reset to use and record in ehc->i.action. + * prereset() may examine and modify it. + */ + action = ehc->i.action; + ehc->i.action &= ~ATA_EH_RESET_MASK; if (softreset && (!hardreset || (!sata_set_spd_needed(ap) && - !(ehc->i.action & ATA_EH_HARDRESET)))) - reset = softreset; + !(action & ATA_EH_HARDRESET)))) + ehc->i.action |= ATA_EH_SOFTRESET; else + ehc->i.action |= ATA_EH_HARDRESET; + + if (prereset) { + rc = prereset(ap); + if (rc) { + ata_port_printk(ap, KERN_ERR, + "prereset failed (errno=%d)\n", rc); + return rc; + } + } + + /* prereset() might have modified ehc->i.action */ + if (ehc->i.action & ATA_EH_HARDRESET) reset = hardreset; + else if (ehc->i.action & ATA_EH_SOFTRESET) + reset = softreset; + else { + /* prereset told us not to reset, bang classes and return */ + for (i = 0; i < ATA_MAX_DEVICES; i++) + classes[i] = ATA_DEV_NONE; + return 0; + } + + /* did prereset() screw up? if so, fix up to avoid oopsing */ + if (!reset) { + ata_port_printk(ap, KERN_ERR, "BUG: prereset() requested " + "invalid reset type\n"); + if (softreset) + reset = softreset; + else + reset = hardreset; + } retry: ata_port_printk(ap, KERN_INFO, "%s resetting port\n", @@ -1424,6 +1462,7 @@ static int ata_port_nr_enabled(struct ata_port *ap) /** * ata_eh_recover - recover host port after error * @ap: host port to recover + * @prereset: prereset method (can be NULL) * @softreset: softreset method (can be NULL) * @hardreset: hardreset method (can be NULL) * @postreset: postreset method (can be NULL) @@ -1440,8 +1479,8 @@ static int ata_port_nr_enabled(struct ata_port *ap) * RETURNS: * 0 on success, -errno on failure. */ -static int ata_eh_recover(struct ata_port *ap, ata_reset_fn_t softreset, - ata_reset_fn_t hardreset, +static int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset, + ata_reset_fn_t softreset, ata_reset_fn_t hardreset, ata_postreset_fn_t postreset) { struct ata_eh_context *ehc = &ap->eh_context; @@ -1469,7 +1508,8 @@ static int ata_eh_recover(struct ata_port *ap, ata_reset_fn_t softreset, if (ehc->i.action & ATA_EH_RESET_MASK) { ata_eh_freeze_port(ap); - rc = ata_eh_reset(ap, softreset, hardreset, postreset); + rc = ata_eh_reset(ap, prereset, softreset, hardreset, + postreset); if (rc) { ata_port_printk(ap, KERN_ERR, "reset failed, giving up\n"); @@ -1586,6 +1626,7 @@ static void ata_eh_finish(struct ata_port *ap) /** * ata_do_eh - do standard error handling * @ap: host port to handle error for + * @prereset: prereset method (can be NULL) * @softreset: softreset method (can be NULL) * @hardreset: hardreset method (can be NULL) * @postreset: postreset method (can be NULL) @@ -1595,11 +1636,12 @@ static void ata_eh_finish(struct ata_port *ap) * LOCKING: * Kernel thread context (may sleep). */ -void ata_do_eh(struct ata_port *ap, ata_reset_fn_t softreset, - ata_reset_fn_t hardreset, ata_postreset_fn_t postreset) +void ata_do_eh(struct ata_port *ap, ata_prereset_fn_t prereset, + ata_reset_fn_t softreset, ata_reset_fn_t hardreset, + ata_postreset_fn_t postreset) { ata_eh_autopsy(ap); ata_eh_report(ap); - ata_eh_recover(ap, softreset, hardreset, postreset); + ata_eh_recover(ap, prereset, softreset, hardreset, postreset); ata_eh_finish(ap); } diff --git a/drivers/scsi/sata_sil24.c b/drivers/scsi/sata_sil24.c index 4c76f05d9b6..26d7c54c175 100644 --- a/drivers/scsi/sata_sil24.c +++ b/drivers/scsi/sata_sil24.c @@ -912,7 +912,8 @@ static void sil24_error_handler(struct ata_port *ap) } /* perform recovery */ - ata_do_eh(ap, sil24_softreset, sil24_hardreset, ata_std_postreset); + ata_do_eh(ap, ata_std_prereset, sil24_softreset, sil24_hardreset, + ata_std_postreset); } static void sil24_post_internal_cmd(struct ata_queued_cmd *qc) |