]> err.no Git - linux-2.6/blobdiff - drivers/ata/libata-eh.c
Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/linville/wireles...
[linux-2.6] / drivers / ata / libata-eh.c
index a34adc2c85df65711eb7cf80de3ffd9ead3e78a6..7894d83ea1eb8d10f57db21f4c71013cd22209f8 100644 (file)
@@ -1308,12 +1308,7 @@ static void ata_eh_analyze_serror(struct ata_link *link)
        unsigned int err_mask = 0, action = 0;
        u32 hotplug_mask;
 
-       if (serror & SERR_PERSISTENT) {
-               err_mask |= AC_ERR_ATA_BUS;
-               action |= ATA_EH_RESET;
-       }
-       if (serror &
-           (SERR_DATA_RECOVERED | SERR_COMM_RECOVERED | SERR_DATA)) {
+       if (serror & (SERR_PERSISTENT | SERR_DATA)) {
                err_mask |= AC_ERR_ATA_BUS;
                action |= ATA_EH_RESET;
        }
@@ -2047,19 +2042,11 @@ static int ata_do_reset(struct ata_link *link, ata_reset_fn_t reset,
                        unsigned int *classes, unsigned long deadline)
 {
        struct ata_device *dev;
-       int rc;
 
        ata_link_for_each_dev(dev, link)
                classes[dev->devno] = ATA_DEV_UNKNOWN;
 
-       rc = reset(link, classes, deadline);
-
-       /* convert all ATA_DEV_UNKNOWN to ATA_DEV_NONE */
-       ata_link_for_each_dev(dev, link)
-               if (classes[dev->devno] == ATA_DEV_UNKNOWN)
-                       classes[dev->devno] = ATA_DEV_NONE;
-
-       return rc;
+       return reset(link, classes, deadline);
 }
 
 static int ata_eh_followup_srst_needed(struct ata_link *link,
@@ -2096,7 +2083,7 @@ int ata_eh_reset(struct ata_link *link, int classify,
        ata_reset_fn_t reset;
        unsigned long flags;
        u32 sstatus;
-       int rc;
+       int nr_known, rc;
 
        /*
         * Prepare to reset
@@ -2170,6 +2157,9 @@ int ata_eh_reset(struct ata_link *link, int classify,
        /*
         * Perform reset
         */
+       if (ata_is_host_link(link))
+               ata_eh_freeze_port(ap);
+
        deadline = jiffies + ata_eh_reset_timeouts[try++];
 
        if (reset) {
@@ -2238,9 +2228,53 @@ int ata_eh_reset(struct ata_link *link, int classify,
        if (sata_scr_read(link, SCR_STATUS, &sstatus) == 0)
                link->sata_spd = (sstatus >> 4) & 0xf;
 
+       /* thaw the port */
+       if (ata_is_host_link(link))
+               ata_eh_thaw_port(ap);
+
+       /* postreset() should clear hardware SError.  Although SError
+        * is cleared during link resume, clearing SError here is
+        * necessary as some PHYs raise hotplug events after SRST.
+        * This introduces race condition where hotplug occurs between
+        * reset and here.  This race is mediated by cross checking
+        * link onlineness and classification result later.
+        */
        if (postreset)
                postreset(link, classes);
 
+       /* clear cached SError */
+       spin_lock_irqsave(link->ap->lock, flags);
+       link->eh_info.serror = 0;
+       spin_unlock_irqrestore(link->ap->lock, flags);
+
+       /* Make sure onlineness and classification result correspond.
+        * Hotplug could have happened during reset and some
+        * controllers fail to wait while a drive is spinning up after
+        * being hotplugged causing misdetection.  By cross checking
+        * link onlineness and classification result, those conditions
+        * can be reliably detected and retried.
+        */
+       nr_known = 0;
+       ata_link_for_each_dev(dev, link) {
+               /* convert all ATA_DEV_UNKNOWN to ATA_DEV_NONE */
+               if (classes[dev->devno] == ATA_DEV_UNKNOWN)
+                       classes[dev->devno] = ATA_DEV_NONE;
+               else
+                       nr_known++;
+       }
+
+       if (classify && !nr_known && ata_link_online(link)) {
+               if (try < max_tries) {
+                       ata_link_printk(link, KERN_WARNING, "link online but "
+                                      "device misclassified, retrying\n");
+                       rc = -EAGAIN;
+                       goto fail;
+               }
+               ata_link_printk(link, KERN_WARNING,
+                              "link online but device misclassified, "
+                              "device detection might fail\n");
+       }
+
        /* reset successful, schedule revalidation */
        ata_eh_done(link, NULL, ATA_EH_RESET);
        ehc->i.action |= ATA_EH_REVALIDATE;
@@ -2589,7 +2623,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 reset, rc;
+       int rc;
        unsigned long flags;
 
        DPRINTK("ENTER\n");
@@ -2632,7 +2666,6 @@ int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
        rc = 0;
        nr_failed_devs = 0;
        nr_disabled_devs = 0;
-       reset = 0;
 
        /* if UNLOADING, finish immediately */
        if (ap->pflags & ATA_PFLAG_UNLOADING)
@@ -2646,40 +2679,24 @@ int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
                if (ata_eh_skip_recovery(link))
                        ehc->i.action = 0;
 
-               /* do we need to reset? */
-               if (ehc->i.action & ATA_EH_RESET)
-                       reset = 1;
-
                ata_link_for_each_dev(dev, link)
                        ehc->classes[dev->devno] = ATA_DEV_UNKNOWN;
        }
 
        /* reset */
-       if (reset) {
-               /* if PMP is attached, this function only deals with
-                * downstream links, port should stay thawed.
-                */
-               if (!sata_pmp_attached(ap))
-                       ata_eh_freeze_port(ap);
-
-               ata_port_for_each_link(link, ap) {
-                       struct ata_eh_context *ehc = &link->eh_context;
+       ata_port_for_each_link(link, ap) {
+               struct ata_eh_context *ehc = &link->eh_context;
 
-                       if (!(ehc->i.action & ATA_EH_RESET))
-                               continue;
+               if (!(ehc->i.action & ATA_EH_RESET))
+                       continue;
 
-                       rc = ata_eh_reset(link, ata_link_nr_vacant(link),
-                                         prereset, softreset, hardreset,
-                                         postreset);
-                       if (rc) {
-                               ata_link_printk(link, KERN_ERR,
-                                               "reset failed, giving up\n");
-                               goto out;
-                       }
+               rc = ata_eh_reset(link, ata_link_nr_vacant(link),
+                                 prereset, softreset, hardreset, postreset);
+               if (rc) {
+                       ata_link_printk(link, KERN_ERR,
+                                       "reset failed, giving up\n");
+                       goto out;
                }
-
-               if (!sata_pmp_attached(ap))
-                       ata_eh_thaw_port(ap);
        }
 
        /* the rest */