[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)
|
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 {
|
||||||
|
|
Загрузка…
Ссылка в новой задаче