forked from luck/tmp_suning_uos_patched
[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>
This commit is contained in:
parent
267a6ad4ae
commit
fea6d607e1
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
#include <linux/export.h>
|
#include <linux/export.h>
|
||||||
|
#include <linux/async.h>
|
||||||
|
|
||||||
#include <scsi/scsi.h>
|
#include <scsi/scsi.h>
|
||||||
#include <scsi/scsi_device.h>
|
#include <scsi/scsi_device.h>
|
||||||
|
@ -92,6 +93,19 @@ static int scsi_bus_resume_common(struct device *dev)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int scsi_bus_prepare(struct device *dev)
|
||||||
|
{
|
||||||
|
if (scsi_is_sdev_device(dev)) {
|
||||||
|
/* sd probing uses async_schedule. Wait until it finishes. */
|
||||||
|
async_synchronize_full();
|
||||||
|
|
||||||
|
} else if (scsi_is_host_device(dev)) {
|
||||||
|
/* Wait until async scanning is finished */
|
||||||
|
scsi_complete_async_scans();
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int scsi_bus_suspend(struct device *dev)
|
static int scsi_bus_suspend(struct device *dev)
|
||||||
{
|
{
|
||||||
return scsi_bus_suspend_common(dev, PMSG_SUSPEND);
|
return scsi_bus_suspend_common(dev, PMSG_SUSPEND);
|
||||||
|
@ -110,6 +124,7 @@ static int scsi_bus_poweroff(struct device *dev)
|
||||||
#else /* CONFIG_PM_SLEEP */
|
#else /* CONFIG_PM_SLEEP */
|
||||||
|
|
||||||
#define scsi_bus_resume_common NULL
|
#define scsi_bus_resume_common NULL
|
||||||
|
#define scsi_bus_prepare NULL
|
||||||
#define scsi_bus_suspend NULL
|
#define scsi_bus_suspend NULL
|
||||||
#define scsi_bus_freeze NULL
|
#define scsi_bus_freeze NULL
|
||||||
#define scsi_bus_poweroff NULL
|
#define scsi_bus_poweroff NULL
|
||||||
|
@ -218,6 +233,7 @@ void scsi_autopm_put_host(struct Scsi_Host *shost)
|
||||||
#endif /* CONFIG_PM_RUNTIME */
|
#endif /* CONFIG_PM_RUNTIME */
|
||||||
|
|
||||||
const struct dev_pm_ops scsi_bus_pm_ops = {
|
const struct dev_pm_ops scsi_bus_pm_ops = {
|
||||||
|
.prepare = scsi_bus_prepare,
|
||||||
.suspend = scsi_bus_suspend,
|
.suspend = scsi_bus_suspend,
|
||||||
.resume = scsi_bus_resume_common,
|
.resume = scsi_bus_resume_common,
|
||||||
.freeze = scsi_bus_freeze,
|
.freeze = scsi_bus_freeze,
|
||||||
|
|
|
@ -109,6 +109,7 @@ extern void scsi_exit_procfs(void);
|
||||||
#endif /* CONFIG_PROC_FS */
|
#endif /* CONFIG_PROC_FS */
|
||||||
|
|
||||||
/* scsi_scan.c */
|
/* scsi_scan.c */
|
||||||
|
extern int scsi_complete_async_scans(void);
|
||||||
extern int scsi_scan_host_selected(struct Scsi_Host *, unsigned int,
|
extern int scsi_scan_host_selected(struct Scsi_Host *, unsigned int,
|
||||||
unsigned int, unsigned int, int);
|
unsigned int, unsigned int, int);
|
||||||
extern void scsi_forget_host(struct Scsi_Host *);
|
extern void scsi_forget_host(struct Scsi_Host *);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user