[PATCH] libata: improve ata_bus_probe()

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>
This commit is contained in:
Tejun Heo 2006-04-02 17:54:46 +09:00 коммит произвёл Jeff Garzik
Родитель cf176e1aa9
Коммит 14d2bac187
2 изменённых файлов: 52 добавлений и 16 удалений

Просмотреть файл

@ -1370,11 +1370,18 @@ err_out_nosup:
static int ata_bus_probe(struct ata_port *ap) static int ata_bus_probe(struct ata_port *ap)
{ {
unsigned int classes[ATA_MAX_DEVICES]; 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; struct ata_device *dev;
ata_port_probe(ap); 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 */ /* reset and determine device classes */
for (i = 0; i < ATA_MAX_DEVICES; i++) for (i = 0; i < ATA_MAX_DEVICES; i++)
classes[i] = ATA_DEV_UNKNOWN; classes[i] = ATA_DEV_UNKNOWN;
@ -1404,21 +1411,23 @@ static int ata_bus_probe(struct ata_port *ap)
dev = &ap->device[i]; dev = &ap->device[i];
dev->class = classes[i]; dev->class = classes[i];
if (!tries[i]) {
ata_down_xfermask_limit(ap, dev, 1);
ata_dev_disable(ap, dev);
}
if (!ata_dev_enabled(dev)) if (!ata_dev_enabled(dev))
continue; continue;
WARN_ON(dev->id != NULL); kfree(dev->id);
if (ata_dev_read_id(ap, dev, &dev->class, 1, &dev->id)) { dev->id = NULL;
dev->class = ATA_DEV_NONE; rc = ata_dev_read_id(ap, dev, &dev->class, 1, &dev->id);
continue; if (rc)
} goto fail;
if (ata_dev_configure(ap, dev, 1)) { rc = ata_dev_configure(ap, dev, 1);
ata_dev_disable(ap, dev); if (rc)
continue; goto fail;
}
found = 1;
} }
/* configure transfer mode */ /* 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 * return error code and failing device on failure as
* ata_set_mode() does. * ata_set_mode() does.
*/ */
if (found) for (i = 0; i < ATA_MAX_DEVICES; i++)
ap->ops->set_mode(ap); if (ata_dev_enabled(&ap->device[i])) {
ap->ops->set_mode(ap);
break;
}
rc = 0; rc = 0;
} else { } else {
while (ata_set_mode(ap, &dev)) rc = ata_set_mode(ap, &dev);
ata_dev_disable(ap, dev); if (rc) {
down_xfermask = 1;
goto fail;
}
} }
for (i = 0; i < ATA_MAX_DEVICES; i++) 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); ata_port_disable(ap);
ap->ops->port_disable(ap); ap->ops->port_disable(ap);
return -ENODEV; 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;
} }
/** /**

Просмотреть файл

@ -211,6 +211,9 @@ enum {
/* Masks for port functions */ /* Masks for port functions */
ATA_PORT_PRIMARY = (1 << 0), ATA_PORT_PRIMARY = (1 << 0),
ATA_PORT_SECONDARY = (1 << 1), ATA_PORT_SECONDARY = (1 << 1),
/* how hard are we gonna try to probe/recover devices */
ATA_PROBE_MAX_TRIES = 3,
}; };
enum hsm_task_states { enum hsm_task_states {