diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c index 68fa64d24721a908e980e2ac23787b0519a1ceb3..33b5bff58cc3a422262bebbb0908d3b8c05d96c3 100644 --- a/drivers/scsi/libata-core.c +++ b/drivers/scsi/libata-core.c @@ -1370,11 +1370,18 @@ static int ata_dev_configure(struct ata_port *ap, struct ata_device *dev, 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; } /** diff --git a/include/linux/libata.h b/include/linux/libata.h index a5207e66ca5257f3e37622c25207dc559a940e1f..a4a1e6304e78473cd8a6b4d84cf0c3348fe431bc 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -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 {