Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[SCSI] scsi_pm: Fix bug in the SCSI power management handler

This patch (as1520) fixes a bug in the SCSI layer's power management
implementation.

LUN scanning can be carried out asynchronously in do_scan_async(), and
sd uses an asynchronous thread for the time-consuming parts of disk
probing in sd_probe_async(). Currently nothing coordinates these
async threads with system sleep transitions; they can and do attempt
to continue scanning/probing SCSI devices even after the host adapter
has been suspended. As one might expect, the outcome is not ideal.

This is what the "prepare" stage of system suspend was created for.
After the prepare callback has been called for a host, target, or
device, drivers are not allowed to register any children underneath
them. Currently the SCSI prepare callback is not implemented; this
patch rectifies that omission.

For SCSI hosts, the prepare routine calls scsi_complete_async_scans()
to wait until async scanning is finished. It might be slightly more
efficient to wait only until the host in question has been scanned,
but there's currently no way to do that. Besides, during a sleep
transition we will ultimately have to wait until all the host scanning
has finished anyway.

For SCSI devices, the prepare routine calls async_synchronize_full()
to wait until sd probing is finished. The routine does nothing for
SCSI targets, because asynchronous target scanning is done only as
part of host scanning.

Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
CC: <stable@kernel.org>
Signed-off-by: James Bottomley <JBottomley@Parallels.com>

authored by

Alan Stern and committed by
James Bottomley
fea6d607 267a6ad4

+17
+16
drivers/scsi/scsi_pm.c
··· 7 7 8 8 #include <linux/pm_runtime.h> 9 9 #include <linux/export.h> 10 + #include <linux/async.h> 10 11 11 12 #include <scsi/scsi.h> 12 13 #include <scsi/scsi_device.h> ··· 93 92 return err; 94 93 } 95 94 95 + static int scsi_bus_prepare(struct device *dev) 96 + { 97 + if (scsi_is_sdev_device(dev)) { 98 + /* sd probing uses async_schedule. Wait until it finishes. */ 99 + async_synchronize_full(); 100 + 101 + } else if (scsi_is_host_device(dev)) { 102 + /* Wait until async scanning is finished */ 103 + scsi_complete_async_scans(); 104 + } 105 + return 0; 106 + } 107 + 96 108 static int scsi_bus_suspend(struct device *dev) 97 109 { 98 110 return scsi_bus_suspend_common(dev, PMSG_SUSPEND); ··· 124 110 #else /* CONFIG_PM_SLEEP */ 125 111 126 112 #define scsi_bus_resume_common NULL 113 + #define scsi_bus_prepare NULL 127 114 #define scsi_bus_suspend NULL 128 115 #define scsi_bus_freeze NULL 129 116 #define scsi_bus_poweroff NULL ··· 233 218 #endif /* CONFIG_PM_RUNTIME */ 234 219 235 220 const struct dev_pm_ops scsi_bus_pm_ops = { 221 + .prepare = scsi_bus_prepare, 236 222 .suspend = scsi_bus_suspend, 237 223 .resume = scsi_bus_resume_common, 238 224 .freeze = scsi_bus_freeze,
+1
drivers/scsi/scsi_priv.h
··· 109 109 #endif /* CONFIG_PROC_FS */ 110 110 111 111 /* scsi_scan.c */ 112 + extern int scsi_complete_async_scans(void); 112 113 extern int scsi_scan_host_selected(struct Scsi_Host *, unsigned int, 113 114 unsigned int, unsigned int, int); 114 115 extern void scsi_forget_host(struct Scsi_Host *);