libata: improve SCSI scan failure handling
[powerpc.git] / drivers / ata / libata-scsi.c
index cfde22d..12ac0b5 100644 (file)
@@ -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");
 }