[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:
Родитель
cf176e1aa9
Коммит
14d2bac187
|
@ -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 (!tries[i]) {
|
||||
ata_down_xfermask_limit(ap, dev, 1);
|
||||
ata_dev_disable(ap, dev);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
kfree(dev->id);
|
||||
dev->id = NULL;
|
||||
rc = ata_dev_read_id(ap, dev, &dev->class, 1, &dev->id);
|
||||
if (rc)
|
||||
goto fail;
|
||||
|
||||
if (ata_dev_configure(ap, dev, 1)) {
|
||||
ata_dev_disable(ap, dev);
|
||||
continue;
|
||||
}
|
||||
|
||||
found = 1;
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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 {
|
||||
|
|
Загрузка…
Ссылка в новой задаче