diff options
Diffstat (limited to 'drivers/ata')
-rw-r--r-- | drivers/ata/libata-core.c | 35 | ||||
-rw-r--r-- | drivers/ata/libata-eh.c | 58 |
2 files changed, 42 insertions, 51 deletions
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 12717fa..a795088 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -3304,35 +3304,6 @@ int sata_phy_resume(struct ata_port *ap, const unsigned long *params, return sata_phy_debounce(ap, params, deadline); } -static void ata_wait_spinup(struct ata_port *ap, unsigned long deadline) -{ - 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_hotplug, deadline); - - /* 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 @@ -3356,15 +3327,11 @@ int ata_std_prereset(struct ata_port *ap, unsigned long deadline) const unsigned long *timing = sata_ehc_deb_timing(ehc); int rc; - /* handle link resume & hotplug spinup */ + /* handle link resume */ if ((ehc->i.flags & ATA_EHI_RESUME_LINK) && (ap->flags & ATA_FLAG_HRST_TO_RESUME)) ehc->i.action |= ATA_EH_HARDRESET; - if ((ehc->i.flags & ATA_EHI_HOTPLUGGED) && - (ap->flags & ATA_FLAG_SKIP_D2H_BSY)) - ata_wait_spinup(ap, deadline); - /* if we're about to do hardreset, nothing more to do */ if (ehc->i.action & ATA_EH_HARDRESET) return 0; diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index b3f7d3c..8256655 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -50,6 +50,28 @@ enum { ATA_EH_SPDN_FALLBACK_TO_PIO = (1 << 2), }; +/* Waiting in ->prereset can never be reliable. It's sometimes nice + * to wait there but it can't be depended upon; otherwise, we wouldn't + * be resetting. Just give it enough time for most drives to spin up. + */ +enum { + ATA_EH_PRERESET_TIMEOUT = 10 * HZ, +}; + +/* The following table determines how we sequence resets. Each entry + * represents timeout for that try. The first try can be soft or + * hardreset. All others are hardreset if available. In most cases + * the first reset w/ 10sec timeout should succeed. Following entries + * are mostly for error handling, hotplug and retarded devices. + */ +static const unsigned long ata_eh_reset_timeouts[] = { + 10 * HZ, /* most drives spin up by 10sec */ + 10 * HZ, /* > 99% working drives spin up before 20sec */ + 35 * HZ, /* give > 30 secs of idleness for retarded devices */ + 5 * HZ, /* and sweet one last chance */ + /* > 1 min has elapsed, give up */ +}; + static void __ata_port_freeze(struct ata_port *ap); static void ata_eh_finish(struct ata_port *ap); #ifdef CONFIG_PM @@ -1603,8 +1625,9 @@ static int ata_eh_reset(struct ata_port *ap, int classify, { struct ata_eh_context *ehc = &ap->eh_context; unsigned int *classes = ehc->classes; - int tries = ATA_EH_RESET_TRIES; int verbose = !(ehc->i.flags & ATA_EHI_QUIET); + int try = 0; + unsigned long deadline; unsigned int action; ata_reset_fn_t reset; int i, did_followup_srst, rc; @@ -1624,7 +1647,7 @@ static int ata_eh_reset(struct ata_port *ap, int classify, ehc->i.action |= ATA_EH_HARDRESET; if (prereset) { - rc = prereset(ap, jiffies + 40 * HZ); + rc = prereset(ap, jiffies + ATA_EH_PRERESET_TIMEOUT); if (rc) { if (rc == -ENOENT) { ata_port_printk(ap, KERN_DEBUG, @@ -1665,6 +1688,8 @@ static int ata_eh_reset(struct ata_port *ap, int classify, } retry: + deadline = jiffies + ata_eh_reset_timeouts[try++]; + /* shut up during boot probing */ if (verbose) ata_port_printk(ap, KERN_INFO, "%s resetting port\n", @@ -1676,7 +1701,7 @@ static int ata_eh_reset(struct ata_port *ap, int classify, else ehc->i.flags |= ATA_EHI_DID_SOFTRESET; - rc = ata_do_reset(ap, reset, classes, jiffies + 40 * HZ); + rc = ata_do_reset(ap, reset, classes, deadline); did_followup_srst = 0; if (reset == hardreset && @@ -1693,7 +1718,7 @@ static int ata_eh_reset(struct ata_port *ap, int classify, } ata_eh_about_to_do(ap, NULL, ATA_EH_RESET_MASK); - rc = ata_do_reset(ap, reset, classes, jiffies + 40 * HZ); + rc = ata_do_reset(ap, reset, classes, deadline); if (rc == 0 && classify && classes[0] == ATA_DEV_UNKNOWN) { @@ -1703,22 +1728,21 @@ static int ata_eh_reset(struct ata_port *ap, int classify, } } - if (rc && --tries) { - const char *type; + if (rc && try < ARRAY_SIZE(ata_eh_reset_timeouts)) { + unsigned long now = jiffies; - if (reset == softreset) { - if (did_followup_srst) - type = "follow-up soft"; - else - type = "soft"; - } else - type = "hard"; + if (time_before(now, deadline)) { + unsigned long delta = deadline - jiffies; - ata_port_printk(ap, KERN_WARNING, - "%sreset failed, retrying in 5 secs\n", type); - ssleep(5); + ata_port_printk(ap, KERN_WARNING, "reset failed " + "(errno=%d), retrying in %u secs\n", + rc, (jiffies_to_msecs(delta) + 999) / 1000); + + schedule_timeout_uninterruptible(delta); + } - if (reset == hardreset) + if (reset == hardreset && + try == ARRAY_SIZE(ata_eh_reset_timeouts) - 1) sata_down_spd_limit(ap); if (hardreset) reset = hardreset; |