]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/ata/libata-eh.c
libata: improve EH retry delay handling
[linux-2.6-omap-h63xx.git] / drivers / ata / libata-eh.c
index 7894d83ea1eb8d10f57db21f4c71013cd22209f8..5b5ae631ed037ed28f0bc1699285d2a5df3dfd84 100644 (file)
@@ -66,15 +66,17 @@ enum {
        ATA_ECAT_DUBIOUS_TOUT_HSM       = 6,
        ATA_ECAT_DUBIOUS_UNK_DEV        = 7,
        ATA_ECAT_NR                     = 8,
-};
 
-/* 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,
-       ATA_EH_FASTDRAIN_INTERVAL       = 3 * HZ,
+       /* always put at least this amount of time between resets */
+       ATA_EH_RESET_COOL_DOWN          =  5000,
+
+       /* 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.
+        */
+       ATA_EH_PRERESET_TIMEOUT         = 10000,
+       ATA_EH_FASTDRAIN_INTERVAL       =  3000,
 };
 
 /* The following table determines how we sequence resets.  Each entry
@@ -84,10 +86,10 @@ enum {
  * 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 */
+       10000,  /* most drives spin up by 10sec */
+       10000,  /* > 99% working drives spin up before 20sec */
+       35000,  /* give > 30 secs of idleness for retarded devices */
+        5000,  /* and sweet one last chance */
        /* > 1 min has elapsed, give up */
 };
 
@@ -486,6 +488,9 @@ void ata_scsi_error(struct Scsi_Host *host)
                                if (ata_ncq_enabled(dev))
                                        ehc->saved_ncq_enabled |= 1 << devno;
                        }
+
+                       /* set last reset timestamp to some time in the past */
+                       ehc->last_reset = jiffies - 60 * HZ;
                }
 
                ap->pflags |= ATA_PFLAG_EH_IN_PROGRESS;
@@ -641,7 +646,7 @@ void ata_eh_fastdrain_timerfn(unsigned long arg)
                /* some qcs have finished, give it another chance */
                ap->fastdrain_cnt = cnt;
                ap->fastdrain_timer.expires =
-                       jiffies + ATA_EH_FASTDRAIN_INTERVAL;
+                       ata_deadline(jiffies, ATA_EH_FASTDRAIN_INTERVAL);
                add_timer(&ap->fastdrain_timer);
        }
 
@@ -681,7 +686,8 @@ static void ata_eh_set_pending(struct ata_port *ap, int fastdrain)
 
        /* activate fast drain */
        ap->fastdrain_cnt = cnt;
-       ap->fastdrain_timer.expires = jiffies + ATA_EH_FASTDRAIN_INTERVAL;
+       ap->fastdrain_timer.expires =
+               ata_deadline(jiffies, ATA_EH_FASTDRAIN_INTERVAL);
        add_timer(&ap->fastdrain_timer);
 }
 
@@ -2088,11 +2094,17 @@ int ata_eh_reset(struct ata_link *link, int classify,
        /*
         * Prepare to reset
         */
+       now = jiffies;
+       deadline = ata_deadline(ehc->last_reset, ATA_EH_RESET_COOL_DOWN);
+       if (time_before(now, deadline))
+               schedule_timeout_uninterruptible(deadline - now);
+
        spin_lock_irqsave(ap->lock, flags);
        ap->pflags |= ATA_PFLAG_RESETTING;
        spin_unlock_irqrestore(ap->lock, flags);
 
        ata_eh_about_to_do(link, NULL, ATA_EH_RESET);
+       ehc->last_reset = jiffies;
 
        ata_link_for_each_dev(dev, link) {
                /* If we issue an SRST then an ATA drive (not ATAPI)
@@ -2125,7 +2137,8 @@ int ata_eh_reset(struct ata_link *link, int classify,
        }
 
        if (prereset) {
-               rc = prereset(link, jiffies + ATA_EH_PRERESET_TIMEOUT);
+               rc = prereset(link,
+                             ata_deadline(jiffies, ATA_EH_PRERESET_TIMEOUT));
                if (rc) {
                        if (rc == -ENOENT) {
                                ata_link_printk(link, KERN_DEBUG,
@@ -2157,10 +2170,11 @@ int ata_eh_reset(struct ata_link *link, int classify,
        /*
         * Perform reset
         */
+       ehc->last_reset = jiffies;
        if (ata_is_host_link(link))
                ata_eh_freeze_port(ap);
 
-       deadline = jiffies + ata_eh_reset_timeouts[try++];
+       deadline = ata_deadline(jiffies, ata_eh_reset_timeouts[try++]);
 
        if (reset) {
                if (verbose)
@@ -2277,6 +2291,7 @@ int ata_eh_reset(struct ata_link *link, int classify,
 
        /* reset successful, schedule revalidation */
        ata_eh_done(link, NULL, ATA_EH_RESET);
+       ehc->last_reset = jiffies;
        ehc->i.action |= ATA_EH_REVALIDATE;
 
        rc = 0;
@@ -2303,9 +2318,9 @@ int ata_eh_reset(struct ata_link *link, int classify,
        if (time_before(now, deadline)) {
                unsigned long delta = deadline - now;
 
-               ata_link_printk(link, KERN_WARNING, "reset failed "
-                               "(errno=%d), retrying in %u secs\n",
-                               rc, (jiffies_to_msecs(delta) + 999) / 1000);
+               ata_link_printk(link, KERN_WARNING,
+                       "reset failed (errno=%d), retrying in %u secs\n",
+                       rc, DIV_ROUND_UP(jiffies_to_msecs(delta), 1000));
 
                while (delta)
                        delta = schedule_timeout_uninterruptible(delta);
@@ -2622,7 +2637,7 @@ int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
 {
        struct ata_link *link;
        struct ata_device *dev;
-       int nr_failed_devs, nr_disabled_devs;
+       int nr_failed_devs;
        int rc;
        unsigned long flags;
 
@@ -2665,7 +2680,6 @@ int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
  retry:
        rc = 0;
        nr_failed_devs = 0;
-       nr_disabled_devs = 0;
 
        /* if UNLOADING, finish immediately */
        if (ap->pflags & ATA_PFLAG_UNLOADING)
@@ -2732,8 +2746,7 @@ int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
 
 dev_fail:
                nr_failed_devs++;
-               if (ata_eh_handle_dev_fail(dev, rc))
-                       nr_disabled_devs++;
+               ata_eh_handle_dev_fail(dev, rc);
 
                if (ap->pflags & ATA_PFLAG_FROZEN) {
                        /* PMP reset requires working host port.
@@ -2745,18 +2758,8 @@ dev_fail:
                }
        }
 
-       if (nr_failed_devs) {
-               if (nr_failed_devs != nr_disabled_devs) {
-                       ata_port_printk(ap, KERN_WARNING, "failed to recover "
-                                       "some devices, retrying in 5 secs\n");
-                       ssleep(5);
-               } else {
-                       /* no device left to recover, repeat fast */
-                       msleep(500);
-               }
-
+       if (nr_failed_devs)
                goto retry;
-       }
 
  out:
        if (rc && r_failed_link)