Commit 1ae46317 authored by Tejun Heo's avatar Tejun Heo Committed by Jeff Garzik

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: default avatarTejun Heo <htejun@gmail.com>
Signed-off-by: default avatarJeff Garzik <jeff@garzik.org>
parent f1545154
...@@ -6453,7 +6453,7 @@ int ata_host_register(struct ata_host *host, struct scsi_host_template *sht) ...@@ -6453,7 +6453,7 @@ int ata_host_register(struct ata_host *host, struct scsi_host_template *sht)
for (i = 0; i < host->n_ports; i++) { for (i = 0; i < host->n_ports; i++) {
struct ata_port *ap = host->ports[i]; struct ata_port *ap = host->ports[i];
ata_scsi_scan_host(ap); ata_scsi_scan_host(ap, 1);
} }
return 0; return 0;
......
...@@ -2947,17 +2947,22 @@ int ata_scsi_add_hosts(struct ata_host *host, struct scsi_host_template *sht) ...@@ -2947,17 +2947,22 @@ int ata_scsi_add_hosts(struct ata_host *host, struct scsi_host_template *sht)
return rc; 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; unsigned int i;
if (ap->flags & ATA_FLAG_DISABLED) if (ap->flags & ATA_FLAG_DISABLED)
return; return;
repeat:
for (i = 0; i < ATA_MAX_DEVICES; i++) { for (i = 0; i < ATA_MAX_DEVICES; i++) {
struct ata_device *dev = &ap->device[i];
struct scsi_device *sdev; struct scsi_device *sdev;
dev = &ap->device[i];
if (!ata_dev_enabled(dev) || dev->sdev) if (!ata_dev_enabled(dev) || dev->sdev)
continue; continue;
...@@ -2967,6 +2972,45 @@ void ata_scsi_scan_host(struct ata_port *ap) ...@@ -2967,6 +2972,45 @@ void ata_scsi_scan_host(struct ata_port *ap)
scsi_device_put(sdev); 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) ...@@ -3093,20 +3137,7 @@ void ata_scsi_hotplug(struct work_struct *work)
} }
/* scan for new ones */ /* scan for new ones */
ata_scsi_scan_host(ap); ata_scsi_scan_host(ap, 0);
/* 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;
}
}
DPRINTK("EXIT\n"); DPRINTK("EXIT\n");
} }
......
...@@ -112,7 +112,7 @@ static inline int ata_acpi_on_devcfg(struct ata_device *adev) { return 0; } ...@@ -112,7 +112,7 @@ static inline int ata_acpi_on_devcfg(struct ata_device *adev) { return 0; }
/* libata-scsi.c */ /* libata-scsi.c */
extern int ata_scsi_add_hosts(struct ata_host *host, extern int ata_scsi_add_hosts(struct ata_host *host,
struct scsi_host_template *sht); struct scsi_host_template *sht);
extern void ata_scsi_scan_host(struct ata_port *ap); extern void ata_scsi_scan_host(struct ata_port *ap, int sync);
extern int ata_scsi_offline_dev(struct ata_device *dev); extern int ata_scsi_offline_dev(struct ata_device *dev);
extern void ata_scsi_hotplug(struct work_struct *work); extern void ata_scsi_hotplug(struct work_struct *work);
extern unsigned int ata_scsiop_inq_std(struct ata_scsi_args *args, u8 *rbuf, extern unsigned int ata_scsiop_inq_std(struct ata_scsi_args *args, u8 *rbuf,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment