[PATCH] libata: improve ata_bus_probe()
authorTejun Heo <htejun@gmail.com>
Sun, 2 Apr 2006 08:54:46 +0000 (17:54 +0900)
committerJeff Garzik <jeff@garzik.org>
Sun, 2 Apr 2006 14:02:57 +0000 (10:02 -0400)
Improve ata_bus_probe() such that configuration failures are handled
better.  Each device is given ATA_PROBE_MAX_TRIES chances, but any
non-transient error (revalidation failure with -ENODEV, configuration
failure with -EINVAL...) disables the device directly.  Any IO error
results in SATA PHY speed down and ata_set_mode() failure lowers
transfer mode.  The last try always puts a device into PIO-0.

After each failure, the whole port is reset to make sure that the
controller and all the devices are in a known and stable state.  The
reset also applies SATA SPD configuration if necessary.

Signed-off-by: Tejun Heo <htejun@gmail.com>
Signed-off-by: Jeff Garzik <jeff@garzik.org>
drivers/scsi/libata-core.c
include/linux/libata.h

index 68fa64d..33b5bff 100644 (file)
@@ -1370,11 +1370,18 @@ err_out_nosup:
 static int ata_bus_probe(struct ata_port *ap)
 {
        unsigned int classes[ATA_MAX_DEVICES];
-       int i, rc, found = 0;
+       int tries[ATA_MAX_DEVICES];
+       int i, rc, down_xfermask;
        struct ata_device *dev;
 
        ata_port_probe(ap);
 
+       for (i = 0; i < ATA_MAX_DEVICES; i++)
+               tries[i] = ATA_PROBE_MAX_TRIES;
+
+ retry:
+       down_xfermask = 0;
+
        /* reset and determine device classes */
        for (i = 0; i < ATA_MAX_DEVICES; i++)
                classes[i] = ATA_DEV_UNKNOWN;
@@ -1404,21 +1411,23 @@ static int ata_bus_probe(struct ata_port *ap)
                dev = &ap->device[i];
                dev->class = classes[i];
 
-               if (!ata_dev_enabled(dev))
-                       continue;
-
-               WARN_ON(dev->id != NULL);
-               if (ata_dev_read_id(ap, dev, &dev->class, 1, &dev->id)) {
-                       dev->class = ATA_DEV_NONE;
-                       continue;
+               if (!tries[i]) {
+                       ata_down_xfermask_limit(ap, dev, 1);
+                       ata_dev_disable(ap, dev);
                }
 
-               if (ata_dev_configure(ap, dev, 1)) {
-                       ata_dev_disable(ap, dev);
+               if (!ata_dev_enabled(dev))
                        continue;
-               }
 
-               found = 1;
+               kfree(dev->id);
+               dev->id = NULL;
+               rc = ata_dev_read_id(ap, dev, &dev->class, 1, &dev->id);
+               if (rc)
+                       goto fail;
+
+               rc = ata_dev_configure(ap, dev, 1);
+               if (rc)
+                       goto fail;
        }
 
        /* configure transfer mode */
@@ -1427,12 +1436,18 @@ static int ata_bus_probe(struct ata_port *ap)
                 * return error code and failing device on failure as
                 * ata_set_mode() does.
                 */
-               if (found)
-                       ap->ops->set_mode(ap);
+               for (i = 0; i < ATA_MAX_DEVICES; i++)
+                       if (ata_dev_enabled(&ap->device[i])) {
+                               ap->ops->set_mode(ap);
+                               break;
+                       }
                rc = 0;
        } else {
-               while (ata_set_mode(ap, &dev))
-                       ata_dev_disable(ap, dev);
+               rc = ata_set_mode(ap, &dev);
+               if (rc) {
+                       down_xfermask = 1;
+                       goto fail;
+               }
        }
 
        for (i = 0; i < ATA_MAX_DEVICES; i++)
@@ -1443,6 +1458,24 @@ static int ata_bus_probe(struct ata_port *ap)
        ata_port_disable(ap);
        ap->ops->port_disable(ap);
        return -ENODEV;
+
+ fail:
+       switch (rc) {
+       case -EINVAL:
+       case -ENODEV:
+               tries[dev->devno] = 0;
+               break;
+       case -EIO:
+               ata_down_sata_spd_limit(ap);
+               /* fall through */
+       default:
+               tries[dev->devno]--;
+               if (down_xfermask &&
+                   ata_down_xfermask_limit(ap, dev, tries[dev->devno] == 1))
+                       tries[dev->devno] = 0;
+       }
+
+       goto retry;
 }
 
 /**
index a5207e6..a4a1e63 100644 (file)
@@ -211,6 +211,9 @@ enum {
        /* Masks for port functions */
        ATA_PORT_PRIMARY        = (1 << 0),
        ATA_PORT_SECONDARY      = (1 << 1),
+
+       /* how hard are we gonna try to probe/recover devices */
+       ATA_PROBE_MAX_TRIES     = 3,
 };
 
 enum hsm_task_states {