summaryrefslogtreecommitdiff
path: root/drivers/ata/libata-scsi.c
diff options
context:
space:
mode:
authorTejun Heo <htejun@gmail.com>2007-07-16 14:29:40 +0900
committerJeff Garzik <jeff@garzik.org>2007-07-20 08:26:25 -0400
commit1ae463171cc1b1ea6dad7bcb298e96c073e7373e (patch)
treec3d62562c9be8c855f7238785ea9c9348f1e56f1 /drivers/ata/libata-scsi.c
parentf1545154a5c96590b1992aac8ee1e2c445e301ed (diff)
libata: improve SCSI scan failure handling
SCSI scan may fail due to memory allocation failure even if EH is not in progress. Due to use of GFP_ATOMIC in SCSI scan path, allocation failure isn't too rare especially while probing multiple devices at once which is the case when a bunch of devices are connected to PMP. This patch moves SCSI scan failure detetion logic from ata_scsi_hotplug() to ata_scsi_scan_host() and implement synchronous scan behavior. The synchronous path sleeps briefly and repeats SCSI scan if some devices aren't attached properly. It contains robust retry loop to minimize the chance of device misdetection during boot and falls back to async retry if everything fails. Signed-off-by: Tejun Heo <htejun@gmail.com> Signed-off-by: Jeff Garzik <jeff@garzik.org>
Diffstat (limited to 'drivers/ata/libata-scsi.c')
-rw-r--r--drivers/ata/libata-scsi.c63
1 files changed, 47 insertions, 16 deletions
diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
index cfde22da07ac..12ac0b511f79 100644
--- a/drivers/ata/libata-scsi.c
+++ b/drivers/ata/libata-scsi.c
@@ -2947,17 +2947,22 @@ int ata_scsi_add_hosts(struct ata_host *host, struct scsi_host_template *sht)
return rc;
}
-void ata_scsi_scan_host(struct ata_port *ap)
+void ata_scsi_scan_host(struct ata_port *ap, int sync)
{
+ int tries = 5;
+ struct ata_device *last_failed_dev = NULL;
+ struct ata_device *dev;
unsigned int i;
if (ap->flags & ATA_FLAG_DISABLED)
return;
+ repeat:
for (i = 0; i < ATA_MAX_DEVICES; i++) {
- struct ata_device *dev = &ap->device[i];
struct scsi_device *sdev;
+ dev = &ap->device[i];
+
if (!ata_dev_enabled(dev) || dev->sdev)
continue;
@@ -2967,6 +2972,45 @@ void ata_scsi_scan_host(struct ata_port *ap)
scsi_device_put(sdev);
}
}
+
+ /* If we scanned while EH was in progress or allocation
+ * failure occurred, scan would have failed silently. Check
+ * whether all devices are attached.
+ */
+ for (i = 0; i < ATA_MAX_DEVICES; i++) {
+ dev = &ap->device[i];
+ if (ata_dev_enabled(dev) && !dev->sdev)
+ break;
+ }
+ if (i == ATA_MAX_DEVICES)
+ return;
+
+ /* we're missing some SCSI devices */
+ if (sync) {
+ /* If caller requested synchrnous scan && we've made
+ * any progress, sleep briefly and repeat.
+ */
+ if (dev != last_failed_dev) {
+ msleep(100);
+ last_failed_dev = dev;
+ goto repeat;
+ }
+
+ /* We might be failing to detect boot device, give it
+ * a few more chances.
+ */
+ if (--tries) {
+ msleep(100);
+ goto repeat;
+ }
+
+ ata_port_printk(ap, KERN_ERR, "WARNING: synchronous SCSI scan "
+ "failed without making any progress,\n"
+ " switching to async\n");
+ }
+
+ queue_delayed_work(ata_aux_wq, &ap->hotplug_task,
+ round_jiffies_relative(HZ));
}
/**
@@ -3093,20 +3137,7 @@ void ata_scsi_hotplug(struct work_struct *work)
}
/* scan for new ones */
- ata_scsi_scan_host(ap);
-
- /* If we scanned while EH was in progress, scan would have
- * failed silently. Requeue if there are enabled but
- * unattached devices.
- */
- for (i = 0; i < ATA_MAX_DEVICES; i++) {
- struct ata_device *dev = &ap->device[i];
- if (ata_dev_enabled(dev) && !dev->sdev) {
- queue_delayed_work(ata_aux_wq, &ap->hotplug_task,
- round_jiffies_relative(HZ));
- break;
- }
- }
+ ata_scsi_scan_host(ap, 0);
DPRINTK("EXIT\n");
}