Commit 06a79b82 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/suspend-2.6

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/suspend-2.6:
  PM / Hibernate: Fix preallocating of memory
  PM / Hibernate: Remove swsusp.c finally
  PM / Hibernate: Remove trailing space in message
  PM: Allow SCSI devices to suspend/resume asynchronously
  PM: Allow USB devices to suspend/resume asynchronously
  USB: implement non-tree resume ordering constraints for PCI host controllers
  PM: Allow PCI devices to suspend/resume asynchronously
  PM / Hibernate: Swap, remove useless check from swsusp_read()
  PM / Hibernate: Really deprecate deprecated user ioctls
  PM: Allow device drivers to use dpm_wait()
  PM: Start asynchronous resume threads upfront
  PM: Add facility for advanced testing of async suspend/resume
  PM: Add a switch for disabling/enabling asynchronous suspend/resume
  PM: Asynchronous suspend and resume of devices
  PM: Add parent information to timing messages
  PM: Document device power attributes in sysfs
  PM / Runtime: Add sysfs switch for disabling device run-time PM
parents 4912002f a9c9b442
What: /sys/devices/.../power/
Date: January 2009
Contact: Rafael J. Wysocki <rjw@sisk.pl>
Description:
The /sys/devices/.../power directory contains attributes
allowing the user space to check and modify some power
management related properties of given device.
What: /sys/devices/.../power/wakeup
Date: January 2009
Contact: Rafael J. Wysocki <rjw@sisk.pl>
Description:
The /sys/devices/.../power/wakeup attribute allows the user
space to check if the device is enabled to wake up the system
from sleep states, such as the memory sleep state (suspend to
RAM) and hibernation (suspend to disk), and to enable or disable
it to do that as desired.
Some devices support "wakeup" events, which are hardware signals
used to activate the system from a sleep state. Such devices
have one of the following two values for the sysfs power/wakeup
file:
+ "enabled\n" to issue the events;
+ "disabled\n" not to do so;
In that cases the user space can change the setting represented
by the contents of this file by writing either "enabled", or
"disabled" to it.
For the devices that are not capable of generating system wakeup
events this file contains "\n". In that cases the user space
cannot modify the contents of this file and the device cannot be
enabled to wake up the system.
What: /sys/devices/.../power/control
Date: January 2009
Contact: Rafael J. Wysocki <rjw@sisk.pl>
Description:
The /sys/devices/.../power/control attribute allows the user
space to control the run-time power management of the device.
All devices have one of the following two values for the
power/control file:
+ "auto\n" to allow the device to be power managed at run time;
+ "on\n" to prevent the device from being power managed;
The default for all devices is "auto", which means that they may
be subject to automatic power management, depending on their
drivers. Changing this attribute to "on" prevents the driver
from power managing the device at run time. Doing that while
the device is suspended causes it to be woken up.
What: /sys/devices/.../power/async
Date: January 2009
Contact: Rafael J. Wysocki <rjw@sisk.pl>
Description:
The /sys/devices/.../async attribute allows the user space to
enable or diasble the device's suspend and resume callbacks to
be executed asynchronously (ie. in separate threads, in parallel
with the main suspend/resume thread) during system-wide power
transitions (eg. suspend to RAM, hibernation).
All devices have one of the following two values for the
power/async file:
+ "enabled\n" to permit the asynchronous suspend/resume;
+ "disabled\n" to forbid it;
The value of this attribute may be changed by writing either
"enabled", or "disabled" to it.
It generally is unsafe to permit the asynchronous suspend/resume
of a device unless it is certain that all of the PM dependencies
of the device are known to the PM core. However, for some
devices this attribute is set to "enabled" by bus type code or
device drivers and in that cases it should be safe to leave the
default value.
...@@ -101,3 +101,16 @@ Description: ...@@ -101,3 +101,16 @@ Description:
CAUTION: Using it will cause your machine's real-time (CMOS) CAUTION: Using it will cause your machine's real-time (CMOS)
clock to be set to a random invalid time after a resume. clock to be set to a random invalid time after a resume.
What: /sys/power/pm_async
Date: January 2009
Contact: Rafael J. Wysocki <rjw@sisk.pl>
Description:
The /sys/power/pm_async file controls the switch allowing the
user space to enable or disable asynchronous suspend and resume
of devices. If enabled, this feature will cause some device
drivers' suspend and resume callbacks to be executed in parallel
with each other and with the main suspend thread. It is enabled
if this file contains "1", which is the default. It may be
disabled by writing "0" to this file, in which case all devices
will be suspended and resumed synchronously.
...@@ -64,6 +64,17 @@ Who: Robin Getz <rgetz@blackfin.uclinux.org> & Matt Mackall <mpm@selenic.com> ...@@ -64,6 +64,17 @@ Who: Robin Getz <rgetz@blackfin.uclinux.org> & Matt Mackall <mpm@selenic.com>
--------------------------- ---------------------------
What: Deprecated snapshot ioctls
When: 2.6.36
Why: The ioctls in kernel/power/user.c were marked as deprecated long time
ago. Now they notify users about that so that they need to replace
their userspace. After some more time, remove them completely.
Who: Jiri Slaby <jirislaby@gmail.com>
---------------------------
What: The ieee80211_regdom module parameter What: The ieee80211_regdom module parameter
When: March 2010 / desktop catchup When: March 2010 / desktop catchup
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#include <linux/resume-trace.h> #include <linux/resume-trace.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/async.h>
#include "../base.h" #include "../base.h"
#include "power.h" #include "power.h"
...@@ -42,6 +43,7 @@ ...@@ -42,6 +43,7 @@
LIST_HEAD(dpm_list); LIST_HEAD(dpm_list);
static DEFINE_MUTEX(dpm_list_mtx); static DEFINE_MUTEX(dpm_list_mtx);
static pm_message_t pm_transition;
/* /*
* Set once the preparation of devices for a PM transition has started, reset * Set once the preparation of devices for a PM transition has started, reset
...@@ -56,6 +58,7 @@ static bool transition_started; ...@@ -56,6 +58,7 @@ static bool transition_started;
void device_pm_init(struct device *dev) void device_pm_init(struct device *dev)
{ {
dev->power.status = DPM_ON; dev->power.status = DPM_ON;
init_completion(&dev->power.completion);
pm_runtime_init(dev); pm_runtime_init(dev);
} }
...@@ -111,6 +114,7 @@ void device_pm_remove(struct device *dev) ...@@ -111,6 +114,7 @@ void device_pm_remove(struct device *dev)
pr_debug("PM: Removing info for %s:%s\n", pr_debug("PM: Removing info for %s:%s\n",
dev->bus ? dev->bus->name : "No Bus", dev->bus ? dev->bus->name : "No Bus",
kobject_name(&dev->kobj)); kobject_name(&dev->kobj));
complete_all(&dev->power.completion);
mutex_lock(&dpm_list_mtx); mutex_lock(&dpm_list_mtx);
list_del_init(&dev->power.entry); list_del_init(&dev->power.entry);
mutex_unlock(&dpm_list_mtx); mutex_unlock(&dpm_list_mtx);
...@@ -187,6 +191,31 @@ static void initcall_debug_report(struct device *dev, ktime_t calltime, ...@@ -187,6 +191,31 @@ static void initcall_debug_report(struct device *dev, ktime_t calltime,
} }
} }
/**
* dpm_wait - Wait for a PM operation to complete.
* @dev: Device to wait for.
* @async: If unset, wait only if the device's power.async_suspend flag is set.
*/
static void dpm_wait(struct device *dev, bool async)
{
if (!dev)
return;
if (async || (pm_async_enabled && dev->power.async_suspend))
wait_for_completion(&dev->power.completion);
}
static int dpm_wait_fn(struct device *dev, void *async_ptr)
{
dpm_wait(dev, *((bool *)async_ptr));
return 0;
}
static void dpm_wait_for_children(struct device *dev, bool async)
{
device_for_each_child(dev, &async, dpm_wait_fn);
}
/** /**
* pm_op - Execute the PM operation appropriate for given PM event. * pm_op - Execute the PM operation appropriate for given PM event.
* @dev: Device to handle. * @dev: Device to handle.
...@@ -271,8 +300,9 @@ static int pm_noirq_op(struct device *dev, ...@@ -271,8 +300,9 @@ static int pm_noirq_op(struct device *dev,
ktime_t calltime, delta, rettime; ktime_t calltime, delta, rettime;
if (initcall_debug) { if (initcall_debug) {
pr_info("calling %s_i+ @ %i\n", pr_info("calling %s+ @ %i, parent: %s\n",
dev_name(dev), task_pid_nr(current)); dev_name(dev), task_pid_nr(current),
dev->parent ? dev_name(dev->parent) : "none");
calltime = ktime_get(); calltime = ktime_get();
} }
...@@ -468,16 +498,20 @@ static int legacy_resume(struct device *dev, int (*cb)(struct device *dev)) ...@@ -468,16 +498,20 @@ static int legacy_resume(struct device *dev, int (*cb)(struct device *dev))
* device_resume - Execute "resume" callbacks for given device. * device_resume - Execute "resume" callbacks for given device.
* @dev: Device to handle. * @dev: Device to handle.
* @state: PM transition of the system being carried out. * @state: PM transition of the system being carried out.
* @async: If true, the device is being resumed asynchronously.
*/ */
static int device_resume(struct device *dev, pm_message_t state) static int device_resume(struct device *dev, pm_message_t state, bool async)
{ {
int error = 0; int error = 0;
TRACE_DEVICE(dev); TRACE_DEVICE(dev);
TRACE_RESUME(0); TRACE_RESUME(0);
dpm_wait(dev->parent, async);
down(&dev->sem); down(&dev->sem);
dev->power.status = DPM_RESUMING;
if (dev->bus) { if (dev->bus) {
if (dev->bus->pm) { if (dev->bus->pm) {
pm_dev_dbg(dev, state, ""); pm_dev_dbg(dev, state, "");
...@@ -510,11 +544,29 @@ static int device_resume(struct device *dev, pm_message_t state) ...@@ -510,11 +544,29 @@ static int device_resume(struct device *dev, pm_message_t state)
} }
End: End:
up(&dev->sem); up(&dev->sem);
complete_all(&dev->power.completion);
TRACE_RESUME(error); TRACE_RESUME(error);
return error; return error;
} }
static void async_resume(void *data, async_cookie_t cookie)
{
struct device *dev = (struct device *)data;
int error;
error = device_resume(dev, pm_transition, true);
if (error)
pm_dev_err(dev, pm_transition, " async", error);
put_device(dev);
}
static bool is_async(struct device *dev)
{
return dev->power.async_suspend && pm_async_enabled
&& !pm_trace_is_enabled();
}
/** /**
* dpm_resume - Execute "resume" callbacks for non-sysdev devices. * dpm_resume - Execute "resume" callbacks for non-sysdev devices.
* @state: PM transition of the system being carried out. * @state: PM transition of the system being carried out.
...@@ -525,21 +577,33 @@ static int device_resume(struct device *dev, pm_message_t state) ...@@ -525,21 +577,33 @@ static int device_resume(struct device *dev, pm_message_t state)
static void dpm_resume(pm_message_t state) static void dpm_resume(pm_message_t state)
{ {
struct list_head list; struct list_head list;
struct device *dev;
ktime_t starttime = ktime_get(); ktime_t starttime = ktime_get();
INIT_LIST_HEAD(&list); INIT_LIST_HEAD(&list);
mutex_lock(&dpm_list_mtx); mutex_lock(&dpm_list_mtx);
while (!list_empty(&dpm_list)) { pm_transition = state;
struct device *dev = to_device(dpm_list.next);
list_for_each_entry(dev, &dpm_list, power.entry) {
if (dev->power.status < DPM_OFF)
continue;
INIT_COMPLETION(dev->power.completion);
if (is_async(dev)) {
get_device(dev); get_device(dev);
if (dev->power.status >= DPM_OFF) { async_schedule(async_resume, dev);
}
}
while (!list_empty(&dpm_list)) {
dev = to_device(dpm_list.next);
get_device(dev);
if (dev->power.status >= DPM_OFF && !is_async(dev)) {
int error; int error;
dev->power.status = DPM_RESUMING;
mutex_unlock(&dpm_list_mtx); mutex_unlock(&dpm_list_mtx);
error = device_resume(dev, state); error = device_resume(dev, state, false);
mutex_lock(&dpm_list_mtx); mutex_lock(&dpm_list_mtx);
if (error) if (error)
...@@ -554,6 +618,7 @@ static void dpm_resume(pm_message_t state) ...@@ -554,6 +618,7 @@ static void dpm_resume(pm_message_t state)
} }
list_splice(&list, &dpm_list); list_splice(&list, &dpm_list);
mutex_unlock(&dpm_list_mtx); mutex_unlock(&dpm_list_mtx);
async_synchronize_full();
dpm_show_time(starttime, state, NULL); dpm_show_time(starttime, state, NULL);
} }
...@@ -731,17 +796,24 @@ static int legacy_suspend(struct device *dev, pm_message_t state, ...@@ -731,17 +796,24 @@ static int legacy_suspend(struct device *dev, pm_message_t state,
return error; return error;
} }
static int async_error;
/** /**
* device_suspend - Execute "suspend" callbacks for given device. * device_suspend - Execute "suspend" callbacks for given device.
* @dev: Device to handle. * @dev: Device to handle.
* @state: PM transition of the system being carried out. * @state: PM transition of the system being carried out.
* @async: If true, the device is being suspended asynchronously.
*/ */
static int device_suspend(struct device *dev, pm_message_t state) static int __device_suspend(struct device *dev, pm_message_t state, bool async)
{ {
int error = 0; int error = 0;
dpm_wait_for_children(dev, async);
down(&dev->sem); down(&dev->sem);
if (async_error)
goto End;
if (dev->class) { if (dev->class) {
if (dev->class->pm) { if (dev->class->pm) {
pm_dev_dbg(dev, state, "class "); pm_dev_dbg(dev, state, "class ");
...@@ -772,12 +844,44 @@ static int device_suspend(struct device *dev, pm_message_t state) ...@@ -772,12 +844,44 @@ static int device_suspend(struct device *dev, pm_message_t state)
error = legacy_suspend(dev, state, dev->bus->suspend); error = legacy_suspend(dev, state, dev->bus->suspend);
} }
} }
if (!error)
dev->power.status = DPM_OFF;
End: End:
up(&dev->sem); up(&dev->sem);
complete_all(&dev->power.completion);
return error; return error;
} }
static void async_suspend(void *data, async_cookie_t cookie)
{
struct device *dev = (struct device *)data;
int error;
error = __device_suspend(dev, pm_transition, true);
if (error) {
pm_dev_err(dev, pm_transition, " async", error);
async_error = error;
}
put_device(dev);
}
static int device_suspend(struct device *dev)
{
INIT_COMPLETION(dev->power.completion);
if (pm_async_enabled && dev->power.async_suspend) {
get_device(dev);
async_schedule(async_suspend, dev);
return 0;
}
return __device_suspend(dev, pm_transition, false);
}
/** /**
* dpm_suspend - Execute "suspend" callbacks for all non-sysdev devices. * dpm_suspend - Execute "suspend" callbacks for all non-sysdev devices.
* @state: PM transition of the system being carried out. * @state: PM transition of the system being carried out.
...@@ -790,13 +894,15 @@ static int dpm_suspend(pm_message_t state) ...@@ -790,13 +894,15 @@ static int dpm_suspend(pm_message_t state)
INIT_LIST_HEAD(&list); INIT_LIST_HEAD(&list);
mutex_lock(&dpm_list_mtx); mutex_lock(&dpm_list_mtx);
pm_transition = state;
async_error = 0;
while (!list_empty(&dpm_list)) { while (!list_empty(&dpm_list)) {
struct device *dev = to_device(dpm_list.prev); struct device *dev = to_device(dpm_list.prev);
get_device(dev); get_device(dev);
mutex_unlock(&dpm_list_mtx); mutex_unlock(&dpm_list_mtx);
error = device_suspend(dev, state); error = device_suspend(dev);
mutex_lock(&dpm_list_mtx); mutex_lock(&dpm_list_mtx);
if (error) { if (error) {
...@@ -804,13 +910,17 @@ static int dpm_suspend(pm_message_t state) ...@@ -804,13 +910,17 @@ static int dpm_suspend(pm_message_t state)
put_device(dev); put_device(dev);
break; break;
} }
dev->power.status = DPM_OFF;
if (!list_empty(&dev->power.entry)) if (!list_empty(&dev->power.entry))
list_move(&dev->power.entry, &list); list_move(&dev->power.entry, &list);
put_device(dev); put_device(dev);
if (async_error)
break;
} }
list_splice(&list, dpm_list.prev); list_splice(&list, dpm_list.prev);
mutex_unlock(&dpm_list_mtx); mutex_unlock(&dpm_list_mtx);
async_synchronize_full();
if (!error)
error = async_error;
if (!error) if (!error)
dpm_show_time(starttime, state, NULL); dpm_show_time(starttime, state, NULL);
return error; return error;
...@@ -936,3 +1046,14 @@ void __suspend_report_result(const char *function, void *fn, int ret) ...@@ -936,3 +1046,14 @@ void __suspend_report_result(const char *function, void *fn, int ret)
printk(KERN_ERR "%s(): %pF returns %d\n", function, fn, ret); printk(KERN_ERR "%s(): %pF returns %d\n", function, fn, ret);
} }
EXPORT_SYMBOL_GPL(__suspend_report_result); EXPORT_SYMBOL_GPL(__suspend_report_result);
/**
* device_pm_wait_for_dev - Wait for suspend/resume of a device to complete.
* @dev: Device to wait for.
* @subordinate: Device that needs to wait for @dev.
*/
void device_pm_wait_for_dev(struct device *subordinate, struct device *dev)
{
dpm_wait(dev, subordinate->power.async_suspend);
}
EXPORT_SYMBOL_GPL(device_pm_wait_for_dev);
...@@ -12,10 +12,10 @@ static inline void pm_runtime_remove(struct device *dev) {} ...@@ -12,10 +12,10 @@ static inline void pm_runtime_remove(struct device *dev) {}
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM_SLEEP
/* /* kernel/power/main.c */
* main.c extern int pm_async_enabled;
*/
/* drivers/base/power/main.c */
extern struct list_head dpm_list; /* The active device list */ extern struct list_head dpm_list; /* The active device list */
static inline struct device *to_device(struct list_head *entry) static inline struct device *to_device(struct list_head *entry)
......
...@@ -1010,6 +1010,50 @@ void pm_runtime_enable(struct device *dev) ...@@ -1010,6 +1010,50 @@ void pm_runtime_enable(struct device *dev)
} }
EXPORT_SYMBOL_GPL(pm_runtime_enable); EXPORT_SYMBOL_GPL(pm_runtime_enable);
/**
* pm_runtime_forbid - Block run-time PM of a device.
* @dev: Device to handle.
*
* Increase the device's usage count and clear its power.runtime_auto flag,
* so that it cannot be suspended at run time until pm_runtime_allow() is called
* for it.
*/
void pm_runtime_forbid(struct device *dev)
{
spin_lock_irq(&dev->power.lock);
if (!dev->power.runtime_auto)
goto out;
dev->power.runtime_auto = false;
atomic_inc(&dev->power.usage_count);
__pm_runtime_resume(dev, false);
out:
spin_unlock_irq(&dev->power.lock);
}
EXPORT_SYMBOL_GPL(pm_runtime_forbid);
/**
* pm_runtime_allow - Unblock run-time PM of a device.
* @dev: Device to handle.
*
* Decrease the device's usage count and set its power.runtime_auto flag.
*/
void pm_runtime_allow(struct device *dev)
{
spin_lock_irq(&dev->power.lock);
if (dev->power.runtime_auto)
goto out;
dev->power.runtime_auto = true;
if (atomic_dec_and_test(&dev->power.usage_count))
__pm_runtime_idle(dev);
out:
spin_unlock_irq(&dev->power.lock);
}
EXPORT_SYMBOL_GPL(pm_runtime_allow);
/** /**
* pm_runtime_init - Initialize run-time PM fields in given device object. * pm_runtime_init - Initialize run-time PM fields in given device object.
* @dev: Device object to initialize. * @dev: Device object to initialize.
...@@ -1028,6 +1072,7 @@ void pm_runtime_init(struct device *dev) ...@@ -1028,6 +1072,7 @@ void pm_runtime_init(struct device *dev)
atomic_set(&dev->power.child_count, 0); atomic_set(&dev->power.child_count, 0);
pm_suspend_ignore_children(dev, false); pm_suspend_ignore_children(dev, false);
dev->power.runtime_auto = true;
dev->power.request_pending = false; dev->power.request_pending = false;
dev->power.request = RPM_REQ_NONE; dev->power.request = RPM_REQ_NONE;
......
...@@ -4,9 +4,25 @@ ...@@ -4,9 +4,25 @@
#include <linux/device.h> #include <linux/device.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/pm_runtime.h>
#include "power.h" #include "power.h"
/* /*
* control - Report/change current runtime PM setting of the device
*
* Runtime power management of a device can be blocked with the help of
* this attribute. All devices have one of the following two values for
* the power/control file:
*
* + "auto\n" to allow the device to be power managed at run time;
* + "on\n" to prevent the device from being power managed at run time;
*
* The default for all devices is "auto", which means that devices may be
* subject to automatic power management, depending on their drivers.
* Changing this attribute to "on" prevents the driver from power managing
* the device at run time. Doing that while the device is suspended causes
* it to be woken up.
*
* wakeup - Report/change current wakeup option for device * wakeup - Report/change current wakeup option for device
* *
* Some devices support "wakeup" events, which are hardware signals * Some devices support "wakeup" events, which are hardware signals
...@@ -38,11 +54,61 @@ ...@@ -38,11 +54,61 @@
* wakeup events internally (unless they are disabled), keeping * wakeup events internally (unless they are disabled), keeping
* their hardware in low power modes whenever they're unused. This * their hardware in low power modes whenever they're unused. This
* saves runtime power, without requiring system-wide sleep states. * saves runtime power, without requiring system-wide sleep states.
*
* async - Report/change current async suspend setting for the device
*
* Asynchronous suspend and resume of the device during system-wide power
* state transitions can be enabled by writing "enabled" to this file.
* Analogously, if "disabled" is written to this file, the device will be
* suspended and resumed synchronously.
*
* All devices have one of the following two values for power/async:
*
* + "enabled\n" to permit the asynchronous suspend/resume of the device;
* + "disabled\n" to forbid it;
*
* NOTE: It generally is unsafe to permit the asynchronous suspend/resume
* of a device unless it is certain that all of the PM dependencies of the
* device are known to the PM core. However, for some devices this
* attribute is set to "enabled" by bus type code or device drivers and in
* that cases it should be safe to leave the default value.
*/ */
static const char enabled[] = "enabled"; static const char enabled[] = "enabled";
static const char disabled[] = "disabled"; static const char disabled[] = "disabled";
#ifdef CONFIG_PM_RUNTIME
static const char ctrl_auto[] = "auto";
static const char ctrl_on[] = "on";
static ssize_t control_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
return sprintf(buf, "%s\n",
dev->power.runtime_auto ? ctrl_auto : ctrl_on);
}
static ssize_t control_store(struct device * dev, struct device_attribute *attr,
const char * buf, size_t n)
{
char *cp;
int len = n;
cp = memchr(buf, '\n', n);
if (cp)
len = cp - buf;
if (len == sizeof ctrl_auto - 1 && strncmp(buf, ctrl_auto, len) == 0)
pm_runtime_allow(dev);
else if (len == sizeof ctrl_on - 1 && strncmp(buf, ctrl_on, len) == 0)
pm_runtime_forbid(dev);
else
return -EINVAL;
return n;
}
static DEVICE_ATTR(control, 0644, control_show, control_store);
#endif
static ssize_t static ssize_t
wake_show(struct device * dev, struct device_attribute *attr, char * buf) wake_show(struct device * dev, struct device_attribute *attr, char * buf)
{ {
...@@ -77,9 +143,43 @@ wake_store(struct device * dev, struct device_attribute *attr, ...@@ -77,9 +143,43 @@ wake_store(struct device * dev, struct device_attribute *attr,
static DEVICE_ATTR(wakeup, 0644, wake_show, wake_store); static DEVICE_ATTR(wakeup, 0644, wake_show, wake_store);
#ifdef CONFIG_PM_SLEEP_ADVANCED_DEBUG
static ssize_t async_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
return sprintf(buf, "%s\n",
device_async_suspend_enabled(dev) ? enabled : disabled);
}
static ssize_t async_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t n)
{
char *cp;
int len = n;
cp = memchr(buf, '\n', n);
if (cp)
len = cp - buf;
if (len == sizeof enabled - 1 && strncmp(buf, enabled, len) == 0)
device_enable_async_suspend(dev);
else if (len == sizeof disabled - 1 && strncmp(buf, disabled, len) == 0)
device_disable_async_suspend(dev);
else
return -EINVAL;
return n;
}
static DEVICE_ATTR(async, 0644, async_show, async_store);
#endif /* CONFIG_PM_SLEEP_ADVANCED_DEBUG */
static struct attribute * power_attrs[] = { static struct attribute * power_attrs[] = {
#ifdef CONFIG_PM_RUNTIME
&dev_attr_control.attr,
#endif
&dev_attr_wakeup.attr, &dev_attr_wakeup.attr,
#ifdef CONFIG_PM_SLEEP_ADVANCED_DEBUG
&dev_attr_async.attr,
#endif
NULL, NULL,
}; };
static struct attribute_group pm_attr_group = { static struct attribute_group pm_attr_group = {
......
...@@ -1540,6 +1540,7 @@ void pci_pm_init(struct pci_dev *dev) ...@@ -1540,6 +1540,7 @@ void pci_pm_init(struct pci_dev *dev)
int pm; int pm;
u16 pmc; u16 pmc;
device_enable_async_suspend(&dev->dev);
dev->wakeup_prepared = false; dev->wakeup_prepared = false;
dev->pm_cap = 0; dev->pm_cap = 0;
......
...@@ -285,6 +285,7 @@ static int pcie_device_init(struct pci_dev *pdev, int service, int irq) ...@@ -285,6 +285,7 @@ static int pcie_device_init(struct pci_dev *pdev, int service, int irq)
pci_name(pdev), pci_name(pdev),
get_descriptor_id(pdev->pcie_type, service)); get_descriptor_id(pdev->pcie_type, service));
device->parent = &pdev->dev; device->parent = &pdev->dev;
device_enable_async_suspend(device);
retval = device_register(device); retval = device_register(device);
if (retval) if (retval)
......
...@@ -1436,6 +1436,7 @@ struct pci_bus * pci_create_bus(struct device *parent, ...@@ -1436,6 +1436,7 @@ struct pci_bus * pci_create_bus(struct device *parent,
if (error) if (error)
goto dev_reg_err; goto dev_reg_err;
b->bridge = get_device(dev); b->bridge = get_device(dev);
device_enable_async_suspend(b->bridge);
if (!parent) if (!parent)
set_dev_node(b->bridge, pcibus_to_node(b)); set_dev_node(b->bridge, pcibus_to_node(b));
......
...@@ -215,6 +215,8 @@ int scsi_add_host_with_dma(struct Scsi_Host *shost, struct device *dev, ...@@ -215,6 +215,8 @@ int scsi_add_host_with_dma(struct Scsi_Host *shost, struct device *dev,
shost->shost_gendev.parent = dev ? dev : &platform_bus; shost->shost_gendev.parent = dev ? dev : &platform_bus;
shost->dma_dev = dma_dev; shost->dma_dev = dma_dev;
device_enable_async_suspend(&shost->shost_gendev);
error = device_add(&shost->shost_gendev); error = device_add(&shost->shost_gendev);
if (error) if (error)
goto out; goto out;
...@@ -222,6 +224,8 @@ int scsi_add_host_with_dma(struct Scsi_Host *shost, struct device *dev, ...@@ -222,6 +224,8 @@ int scsi_add_host_with_dma(struct Scsi_Host *shost, struct device *dev,
scsi_host_set_state(shost, SHOST_RUNNING); scsi_host_set_state(shost, SHOST_RUNNING);
get_device(shost->shost_gendev.parent); get_device(shost->shost_gendev.parent);
device_enable_async_suspend(&shost->shost_dev);
error = device_add(&shost->shost_dev); error = device_add(&shost->shost_dev);
if (error) if (error)
goto out_del_gendev; goto out_del_gendev;
......
...@@ -847,6 +847,8 @@ static int scsi_target_add(struct scsi_target *starget) ...@@ -847,6 +847,8 @@ static int scsi_target_add(struct scsi_target *starget)
if (starget->state != STARGET_CREATED) if (starget->state != STARGET_CREATED)
return 0; return 0;
device_enable_async_suspend(&starget->dev);
error = device_add(&starget->dev); error = device_add(&starget->dev);
if (error) { if (error) {
dev_err(&starget->dev, "target device_add failed, error %d\n", error); dev_err(&starget->dev, "target device_add failed, error %d\n", error);
...@@ -887,11 +889,13 @@ int scsi_sysfs_add_sdev(struct scsi_device *sdev) ...@@ -887,11 +889,13 @@ int scsi_sysfs_add_sdev(struct scsi_device *sdev)
return error; return error;
transport_configure_device(&starget->dev); transport_configure_device(&starget->dev);
device_enable_async_suspend(&sdev->sdev_gendev);
error = device_add(&sdev->sdev_gendev); error = device_add(&sdev->sdev_gendev);
if (error) { if (error) {
printk(KERN_INFO "error 1\n"); printk(KERN_INFO "error 1\n");
return error; return error;
} }
device_enable_async_suspend(&sdev->sdev_dev);
error = device_add(&sdev->sdev_dev); error = device_add(&sdev->sdev_dev);
if (error) { if (error) {
printk(KERN_INFO "error 2\n"); printk(KERN_INFO "error 2\n");
......
...@@ -1022,6 +1022,14 @@ static int usb_resume_device(struct usb_device *udev, pm_message_t msg) ...@@ -1022,6 +1022,14 @@ static int usb_resume_device(struct usb_device *udev, pm_message_t msg)
goto done; goto done;
} }
/* Non-root devices on a full/low-speed bus must wait for their
* companion high-speed root hub, in case a handoff is needed.
*/
if (!(msg.event & PM_EVENT_AUTO) && udev->parent &&
udev->bus->hs_companion)
device_pm_wait_for_dev(&udev->dev,
&udev->bus->hs_companion->root_hub->dev);
if (udev->quirks & USB_QUIRK_RESET_RESUME) if (udev->quirks & USB_QUIRK_RESET_RESUME)
udev->reset_resume = 1; udev->reset_resume = 1;
......
...@@ -186,6 +186,7 @@ int usb_create_ep_devs(struct device *parent, ...@@ -186,6 +186,7 @@ int usb_create_ep_devs(struct device *parent,
ep_dev->dev.parent = parent; ep_dev->dev.parent = parent;
ep_dev->dev.release = ep_device_release; ep_dev->dev.release = ep_device_release;
dev_set_name(&ep_dev->dev, "ep_%02x", endpoint->desc.bEndpointAddress); dev_set_name(&ep_dev->dev, "ep_%02x", endpoint->desc.bEndpointAddress);
device_enable_async_suspend(&ep_dev->dev);
retval = device_register(&ep_dev->dev); retval = device_register(&ep_dev->dev);
if (retval) if (retval)
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/pm_runtime.h>
#include <linux/usb.h> #include <linux/usb.h>
#include <asm/io.h> #include <asm/io.h>
...@@ -37,6 +38,122 @@ ...@@ -37,6 +38,122 @@
/* PCI-based HCs are common, but plenty of non-PCI HCs are used too */ /* PCI-based HCs are common, but plenty of non-PCI HCs are used too */
#ifdef CONFIG_PM_SLEEP
/* Coordinate handoffs between EHCI and companion controllers
* during system resume
*/
static DEFINE_MUTEX(companions_mutex);
#define CL_UHCI PCI_CLASS_SERIAL_USB_UHCI
#define CL_OHCI PCI_CLASS_SERIAL_USB_OHCI
#define CL_EHCI PCI_CLASS_SERIAL_USB_EHCI
enum companion_action {
SET_HS_COMPANION, CLEAR_HS_COMPANION, WAIT_FOR_COMPANIONS
};
static void companion_common(struct pci_dev *pdev, struct usb_hcd *hcd,
enum companion_action action)
{
struct pci_dev *companion;
struct usb_hcd *companion_hcd;
unsigned int slot = PCI_SLOT(pdev->devfn);
/* Iterate through other PCI functions in the same slot.
* If pdev is OHCI or UHCI then we are looking for EHCI, and
* vice versa.
*/
companion = NULL;
for (;;) {
companion = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, companion);
if (!companion)
break;
if (companion->bus != pdev->bus ||
PCI_SLOT(companion->devfn) != slot)
continue;
companion_hcd = pci_get_drvdata(companion);
if (!companion_hcd)
continue;
/* For SET_HS_COMPANION, store a pointer to the EHCI bus in
* the OHCI/UHCI companion bus structure.
* For CLEAR_HS_COMPANION, clear the pointer to the EHCI bus
* in the OHCI/UHCI companion bus structure.
* For WAIT_FOR_COMPANIONS, wait until the OHCI/UHCI
* companion controllers have fully resumed.
*/
if ((pdev->class == CL_OHCI || pdev->class == CL_UHCI) &&
companion->class == CL_EHCI) {
/* action must be SET_HS_COMPANION */
dev_dbg(&companion->dev, "HS companion for %s\n",
dev_name(&pdev->dev));
hcd->self.hs_companion = &companion_hcd->self;
} else if (pdev->class == CL_EHCI &&
(companion->class == CL_OHCI ||
companion->class == CL_UHCI)) {
switch (action) {
case SET_HS_COMPANION:
dev_dbg(&pdev->dev, "HS companion for %s\n",
dev_name(&companion->dev));
companion_hcd->self.hs_companion = &hcd->self;
break;
case CLEAR_HS_COMPANION:
companion_hcd->self.hs_companion = NULL;
break;
case WAIT_FOR_COMPANIONS:
device_pm_wait_for_dev(&pdev->dev,
&companion->dev);
break;
}
}
}
}
static void set_hs_companion(struct pci_dev *pdev, struct usb_hcd *hcd)
{
mutex_lock(&companions_mutex);
dev_set_drvdata(&pdev->dev, hcd);
companion_common(pdev, hcd, SET_HS_COMPANION);
mutex_unlock(&companions_mutex);
}
static void clear_hs_companion(struct pci_dev *pdev, struct usb_hcd *hcd)
{
mutex_lock(&companions_mutex);
dev_set_drvdata(&pdev->dev, NULL);
/* If pdev is OHCI or UHCI, just clear its hs_companion pointer */
if (pdev->class == CL_OHCI || pdev->class == CL_UHCI)
hcd->self.hs_companion = NULL;
/* Otherwise search for companion buses and clear their pointers */
else
companion_common(pdev, hcd, CLEAR_HS_COMPANION);
mutex_unlock(&companions_mutex);
}
static void wait_for_companions(struct pci_dev *pdev, struct usb_hcd *hcd)
{
/* Only EHCI controllers need to wait.
* No locking is needed because a controller cannot be resumed
* while one of its companions is getting unbound.
*/
if (pdev->class == CL_EHCI)
companion_common(pdev, hcd, WAIT_FOR_COMPANIONS);
}
#else /* !CONFIG_PM_SLEEP */
static inline void set_hs_companion(struct pci_dev *d, struct usb_hcd *h) {}
static inline void clear_hs_companion(struct pci_dev *d, struct usb_hcd *h) {}
static inline void wait_for_companions(struct pci_dev *d, struct usb_hcd *h) {}
#endif /* !CONFIG_PM_SLEEP */
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
...@@ -123,7 +240,7 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) ...@@ -123,7 +240,7 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
if (region == PCI_ROM_RESOURCE) { if (region == PCI_ROM_RESOURCE) {
dev_dbg(&dev->dev, "no i/o regions available\n"); dev_dbg(&dev->dev, "no i/o regions available\n");
retval = -EBUSY; retval = -EBUSY;
goto err1; goto err2;
} }
} }
...@@ -132,6 +249,7 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) ...@@ -132,6 +249,7 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
retval = usb_add_hcd(hcd, dev->irq, IRQF_DISABLED | IRQF_SHARED); retval = usb_add_hcd(hcd, dev->irq, IRQF_DISABLED | IRQF_SHARED);
if (retval != 0) if (retval != 0)
goto err4; goto err4;
set_hs_companion(dev, hcd);
return retval; return retval;
err4: err4:
...@@ -142,6 +260,7 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) ...@@ -142,6 +260,7 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
} else } else
release_region(hcd->rsrc_start, hcd->rsrc_len); release_region(hcd->rsrc_start, hcd->rsrc_len);
err2: err2:
clear_hs_companion(dev, hcd);
usb_put_hcd(hcd); usb_put_hcd(hcd);
err1: err1:
pci_disable_device(dev); pci_disable_device(dev);
...@@ -180,6 +299,7 @@ void usb_hcd_pci_remove(struct pci_dev *dev) ...@@ -180,6 +299,7 @@ void usb_hcd_pci_remove(struct pci_dev *dev)
} else { } else {
release_region(hcd->rsrc_start, hcd->rsrc_len); release_region(hcd->rsrc_start, hcd->rsrc_len);
} }
clear_hs_companion(dev, hcd);
usb_put_hcd(hcd); usb_put_hcd(hcd);
pci_disable_device(dev); pci_disable_device(dev);
} }
...@@ -344,6 +464,11 @@ static int resume_common(struct device *dev, bool hibernated) ...@@ -344,6 +464,11 @@ static int resume_common(struct device *dev, bool hibernated)
clear_bit(HCD_FLAG_SAW_IRQ, &hcd->flags); clear_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);
if (hcd->driver->pci_resume) { if (hcd->driver->pci_resume) {
/* This call should be made only during system resume,
* not during runtime resume.
*/
wait_for_companions(pci_dev, hcd);
retval = hcd->driver->pci_resume(hcd, hibernated); retval = hcd->driver->pci_resume(hcd, hibernated);
if (retval) { if (retval) {
dev_err(dev, "PCI post-resume error %d!\n", retval); dev_err(dev, "PCI post-resume error %d!\n", retval);
......
...@@ -1817,6 +1817,7 @@ int usb_new_device(struct usb_device *udev) ...@@ -1817,6 +1817,7 @@ int usb_new_device(struct usb_device *udev)
/* Tell the world! */ /* Tell the world! */
announce_device(udev); announce_device(udev);
device_enable_async_suspend(&udev->dev);
/* Register the device. The device driver is responsible /* Register the device. The device driver is responsible
* for configuring the device and invoking the add-device * for configuring the device and invoking the add-device
* notifier chain (used by usbfs and possibly others). * notifier chain (used by usbfs and possibly others).
......
...@@ -1867,6 +1867,7 @@ int usb_set_configuration(struct usb_device *dev, int configuration) ...@@ -1867,6 +1867,7 @@ int usb_set_configuration(struct usb_device *dev, int configuration)
"adding %s (config #%d, interface %d)\n", "adding %s (config #%d, interface %d)\n",
dev_name(&intf->dev), configuration, dev_name(&intf->dev), configuration,
intf->cur_altsetting->desc.bInterfaceNumber); intf->cur_altsetting->desc.bInterfaceNumber);
device_enable_async_suspend(&intf->dev);
ret = device_add(&intf->dev); ret = device_add(&intf->dev);
if (ret != 0) { if (ret != 0) {
dev_err(&dev->dev, "device_add(%s) --> %d\n", dev_err(&dev->dev, "device_add(%s) --> %d\n",
......
...@@ -472,6 +472,23 @@ static inline int device_is_registered(struct device *dev) ...@@ -472,6 +472,23 @@ static inline int device_is_registered(struct device *dev)
return dev->kobj.state_in_sysfs; return dev->kobj.state_in_sysfs;
} }
static inline void device_enable_async_suspend(struct device *dev)
{
if (dev->power.status == DPM_ON)
dev->power.async_suspend = true;
}
static inline void device_disable_async_suspend(struct device *dev)
{
if (dev->power.status == DPM_ON)
dev->power.async_suspend = false;
}
static inline bool device_async_suspend_enabled(struct device *dev)
{
return !!dev->power.async_suspend;
}
void driver_init(void); void driver_init(void);
/* /*
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/wait.h> #include <linux/wait.h>
#include <linux/timer.h> #include <linux/timer.h>
#include <linux/completion.h>
/* /*
* Callbacks for platform drivers to implement. * Callbacks for platform drivers to implement.
...@@ -412,9 +413,11 @@ struct dev_pm_info { ...@@ -412,9 +413,11 @@ struct dev_pm_info {
pm_message_t power_state; pm_message_t power_state;
unsigned int can_wakeup:1; unsigned int can_wakeup:1;
unsigned int should_wakeup:1; unsigned int should_wakeup:1;
unsigned async_suspend:1;
enum dpm_state status; /* Owned by the PM core */ enum dpm_state status; /* Owned by the PM core */
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM_SLEEP
struct list_head entry; struct list_head entry;
struct completion completion;
#endif #endif
#ifdef CONFIG_PM_RUNTIME #ifdef CONFIG_PM_RUNTIME
struct timer_list suspend_timer; struct timer_list suspend_timer;
...@@ -430,6 +433,7 @@ struct dev_pm_info { ...@@ -430,6 +433,7 @@ struct dev_pm_info {
unsigned int request_pending:1; unsigned int request_pending:1;
unsigned int deferred_resume:1; unsigned int deferred_resume:1;
unsigned int run_wake:1; unsigned int run_wake:1;
unsigned int runtime_auto:1;
enum rpm_request request; enum rpm_request request;
enum rpm_status runtime_status; enum rpm_status runtime_status;
int runtime_error; int runtime_error;
...@@ -508,6 +512,7 @@ extern void __suspend_report_result(const char *function, void *fn, int ret); ...@@ -508,6 +512,7 @@ extern void __suspend_report_result(const char *function, void *fn, int ret);
__suspend_report_result(__func__, fn, ret); \ __suspend_report_result(__func__, fn, ret); \
} while (0) } while (0)
extern void device_pm_wait_for_dev(struct device *sub, struct device *dev);
#else /* !CONFIG_PM_SLEEP */ #else /* !CONFIG_PM_SLEEP */
#define device_pm_lock() do {} while (0) #define device_pm_lock() do {} while (0)
...@@ -520,6 +525,7 @@ static inline int dpm_suspend_start(pm_message_t state) ...@@ -520,6 +525,7 @@ static inline int dpm_suspend_start(pm_message_t state)
#define suspend_report_result(fn, ret) do {} while (0) #define suspend_report_result(fn, ret) do {} while (0)
static inline void device_pm_wait_for_dev(struct device *a, struct device *b) {}
#endif /* !CONFIG_PM_SLEEP */ #endif /* !CONFIG_PM_SLEEP */
/* How to reorder dpm_list after device_move() */ /* How to reorder dpm_list after device_move() */
......
...@@ -28,6 +28,8 @@ extern int __pm_runtime_set_status(struct device *dev, unsigned int status); ...@@ -28,6 +28,8 @@ extern int __pm_runtime_set_status(struct device *dev, unsigned int status);
extern int pm_runtime_barrier(struct device *dev); extern int pm_runtime_barrier(struct device *dev);
extern void pm_runtime_enable(struct device *dev); extern void pm_runtime_enable(struct device *dev);
extern void __pm_runtime_disable(struct device *dev, bool check_resume); extern void __pm_runtime_disable(struct device *dev, bool check_resume);
extern void pm_runtime_allow(struct device *dev);
extern void pm_runtime_forbid(struct device *dev);
static inline bool pm_children_suspended(struct device *dev) static inline bool pm_children_suspended(struct device *dev)
{ {
...@@ -78,6 +80,8 @@ static inline int __pm_runtime_set_status(struct device *dev, ...@@ -78,6 +80,8 @@ static inline int __pm_runtime_set_status(struct device *dev,
static inline int pm_runtime_barrier(struct device *dev) { return 0; } static inline int pm_runtime_barrier(struct device *dev) { return 0; }
static inline void pm_runtime_enable(struct device *dev) {} static inline void pm_runtime_enable(struct device *dev) {}
static inline void __pm_runtime_disable(struct device *dev, bool c) {} static inline void __pm_runtime_disable(struct device *dev, bool c) {}
static inline void pm_runtime_allow(struct device *dev) {}
static inline void pm_runtime_forbid(struct device *dev) {}
static inline bool pm_children_suspended(struct device *dev) { return false; } static inline bool pm_children_suspended(struct device *dev) { return false; }
static inline void pm_suspend_ignore_children(struct device *dev, bool en) {} static inline void pm_suspend_ignore_children(struct device *dev, bool en) {}
......
...@@ -6,6 +6,11 @@ ...@@ -6,6 +6,11 @@
extern int pm_trace_enabled; extern int pm_trace_enabled;
static inline int pm_trace_is_enabled(void)
{
return pm_trace_enabled;
}
struct device; struct device;
extern void set_trace_device(struct device *); extern void set_trace_device(struct device *);
extern void generate_resume_trace(const void *tracedata, unsigned int user); extern void generate_resume_trace(const void *tracedata, unsigned int user);
...@@ -17,6 +22,8 @@ extern void generate_resume_trace(const void *tracedata, unsigned int user); ...@@ -17,6 +22,8 @@ extern void generate_resume_trace(const void *tracedata, unsigned int user);
#else #else
static inline int pm_trace_is_enabled(void) { return 0; }
#define TRACE_DEVICE(dev) do { } while (0) #define TRACE_DEVICE(dev) do { } while (0)
#define TRACE_RESUME(dev) do { } while (0) #define TRACE_RESUME(dev) do { } while (0)
......
...@@ -339,6 +339,7 @@ struct usb_bus { ...@@ -339,6 +339,7 @@ struct usb_bus {
struct usb_devmap devmap; /* device address allocation map */ struct usb_devmap devmap; /* device address allocation map */
struct usb_device *root_hub; /* Root hub */ struct usb_device *root_hub; /* Root hub */
struct usb_bus *hs_companion; /* Companion EHCI bus, if any */
struct list_head bus_list; /* list of busses */ struct list_head bus_list; /* list of busses */
int bandwidth_allocated; /* on this bus: how much of the time int bandwidth_allocated; /* on this bus: how much of the time
......
...@@ -27,6 +27,15 @@ config PM_DEBUG ...@@ -27,6 +27,15 @@ config PM_DEBUG
code. This is helpful when debugging and reporting PM bugs, like code. This is helpful when debugging and reporting PM bugs, like
suspend support. suspend support.
config PM_ADVANCED_DEBUG
bool "Extra PM attributes in sysfs for low-level debugging/testing"
depends on PM_DEBUG
default n
---help---
Add extra sysfs attributes allowing one to access some Power Management
fields of device objects from user space. If you are not a kernel
developer interested in debugging/testing Power Management, say "no".
config PM_VERBOSE config PM_VERBOSE
bool "Verbose Power Management debugging" bool "Verbose Power Management debugging"
depends on PM_DEBUG depends on PM_DEBUG
...@@ -85,6 +94,11 @@ config PM_SLEEP ...@@ -85,6 +94,11 @@ config PM_SLEEP
depends on SUSPEND || HIBERNATION || XEN_SAVE_RESTORE depends on SUSPEND || HIBERNATION || XEN_SAVE_RESTORE
default y default y
config PM_SLEEP_ADVANCED_DEBUG
bool
depends on PM_ADVANCED_DEBUG
default n
config SUSPEND config SUSPEND
bool "Suspend to RAM and standby" bool "Suspend to RAM and standby"
depends on PM && ARCH_SUSPEND_POSSIBLE depends on PM && ARCH_SUSPEND_POSSIBLE
......
...@@ -44,6 +44,32 @@ int pm_notifier_call_chain(unsigned long val) ...@@ -44,6 +44,32 @@ int pm_notifier_call_chain(unsigned long val)
== NOTIFY_BAD) ? -EINVAL : 0; == NOTIFY_BAD) ? -EINVAL : 0;
} }
/* If set, devices may be suspended and resumed asynchronously. */
int pm_async_enabled = 1;
static ssize_t pm_async_show(struct kobject *kobj, struct kobj_attribute *attr,
char *buf)
{
return sprintf(buf, "%d\n", pm_async_enabled);
}
static ssize_t pm_async_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t n)
{
unsigned long val;
if (strict_strtoul(buf, 10, &val))
return -EINVAL;
if (val > 1)
return -EINVAL;
pm_async_enabled = val;
return n;
}
power_attr(pm_async);
#ifdef CONFIG_PM_DEBUG #ifdef CONFIG_PM_DEBUG
int pm_test_level = TEST_NONE; int pm_test_level = TEST_NONE;
...@@ -208,8 +234,11 @@ static struct attribute * g[] = { ...@@ -208,8 +234,11 @@ static struct attribute * g[] = {
#ifdef CONFIG_PM_TRACE #ifdef CONFIG_PM_TRACE
&pm_trace_attr.attr, &pm_trace_attr.attr,
#endif #endif
#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_PM_DEBUG) #ifdef CONFIG_PM_SLEEP
&pm_async_attr.attr,
#ifdef CONFIG_PM_DEBUG
&pm_test_attr.attr, &pm_test_attr.attr,
#endif
#endif #endif
NULL, NULL,
}; };
......
...@@ -1181,7 +1181,7 @@ static void free_unnecessary_pages(void) ...@@ -1181,7 +1181,7 @@ static void free_unnecessary_pages(void)
memory_bm_position_reset(&copy_bm); memory_bm_position_reset(&copy_bm);
while (to_free_normal > 0 && to_free_highmem > 0) { while (to_free_normal > 0 || to_free_highmem > 0) {
unsigned long pfn = memory_bm_next_pfn(&copy_bm); unsigned long pfn = memory_bm_next_pfn(&copy_bm);
struct page *page = pfn_to_page(pfn); struct page *page = pfn_to_page(pfn);
...@@ -1500,7 +1500,7 @@ asmlinkage int swsusp_save(void) ...@@ -1500,7 +1500,7 @@ asmlinkage int swsusp_save(void)
{ {
unsigned int nr_pages, nr_highmem; unsigned int nr_pages, nr_highmem;
printk(KERN_INFO "PM: Creating hibernation image: \n"); printk(KERN_INFO "PM: Creating hibernation image:\n");
drain_local_pages(NULL); drain_local_pages(NULL);
nr_pages = count_data_pages(); nr_pages = count_data_pages();
......
...@@ -657,10 +657,6 @@ int swsusp_read(unsigned int *flags_p) ...@@ -657,10 +657,6 @@ int swsusp_read(unsigned int *flags_p)
struct swsusp_info *header; struct swsusp_info *header;
*flags_p = swsusp_header->flags; *flags_p = swsusp_header->flags;
if (IS_ERR(resume_bdev)) {
pr_debug("PM: Image device not initialised\n");
return PTR_ERR(resume_bdev);
}
memset(&snapshot, 0, sizeof(struct snapshot_handle)); memset(&snapshot, 0, sizeof(struct snapshot_handle));
error = snapshot_write_next(&snapshot, PAGE_SIZE); error = snapshot_write_next(&snapshot, PAGE_SIZE);
......
/*
* linux/kernel/power/swsusp.c
*
* This file provides code to write suspend image to swap and read it back.
*
* Copyright (C) 1998-2001 Gabor Kuti <seasons@fornax.hu>
* Copyright (C) 1998,2001-2005 Pavel Machek <pavel@suse.cz>
*
* This file is released under the GPLv2.
*
* I'd like to thank the following people for their work:
*
* Pavel Machek <pavel@ucw.cz>:
* Modifications, defectiveness pointing, being with me at the very beginning,
* suspend to swap space, stop all tasks. Port to 2.4.18-ac and 2.5.17.
*
* Steve Doddi <dirk@loth.demon.co.uk>:
* Support the possibility of hardware state restoring.
*
* Raph <grey.havens@earthling.net>:
* Support for preserving states of network devices and virtual console
* (including X and svgatextmode)
*
* Kurt Garloff <garloff@suse.de>:
* Straightened the critical function in order to prevent compilers from
* playing tricks with local variables.
*
* Andreas Mohr <a.mohr@mailto.de>
*
* Alex Badea <vampire@go.ro>:
* Fixed runaway init
*
* Rafael J. Wysocki <rjw@sisk.pl>
* Reworked the freeing of memory and the handling of swap
*
* More state savers are welcome. Especially for the scsi layer...
*
* For TODOs,FIXMEs also look in Documentation/power/swsusp.txt
*/
#include <linux/mm.h>
#include <linux/suspend.h>
#include <linux/spinlock.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/swap.h>
#include <linux/pm.h>
#include <linux/swapops.h>
#include <linux/bootmem.h>
#include <linux/syscalls.h>
#include <linux/highmem.h>
#include <linux/time.h>
#include <linux/rbtree.h>
#include <linux/io.h>
#include "power.h"
int in_suspend __nosavedata = 0;
...@@ -195,6 +195,15 @@ static ssize_t snapshot_write(struct file *filp, const char __user *buf, ...@@ -195,6 +195,15 @@ static ssize_t snapshot_write(struct file *filp, const char __user *buf,
return res; return res;
} }
static void snapshot_deprecated_ioctl(unsigned int cmd)
{
if (printk_ratelimit())
printk(KERN_NOTICE "%pf: ioctl '%.8x' is deprecated and will "
"be removed soon, update your suspend-to-disk "
"utilities\n",
__builtin_return_address(0), cmd);
}
static long snapshot_ioctl(struct file *filp, unsigned int cmd, static long snapshot_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg) unsigned long arg)
{ {
...@@ -246,8 +255,9 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd, ...@@ -246,8 +255,9 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd,
data->frozen = 0; data->frozen = 0;
break; break;
case SNAPSHOT_CREATE_IMAGE:
case SNAPSHOT_ATOMIC_SNAPSHOT: case SNAPSHOT_ATOMIC_SNAPSHOT:
snapshot_deprecated_ioctl(cmd);
case SNAPSHOT_CREATE_IMAGE:
if (data->mode != O_RDONLY || !data->frozen || data->ready) { if (data->mode != O_RDONLY || !data->frozen || data->ready) {
error = -EPERM; error = -EPERM;
break; break;
...@@ -275,8 +285,9 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd, ...@@ -275,8 +285,9 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd,
data->ready = 0; data->ready = 0;
break; break;
case SNAPSHOT_PREF_IMAGE_SIZE:
case SNAPSHOT_SET_IMAGE_SIZE: case SNAPSHOT_SET_IMAGE_SIZE:
snapshot_deprecated_ioctl(cmd);
case SNAPSHOT_PREF_IMAGE_SIZE:
image_size = arg; image_size = arg;
break; break;
...@@ -290,15 +301,17 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd, ...@@ -290,15 +301,17 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd,
error = put_user(size, (loff_t __user *)arg); error = put_user(size, (loff_t __user *)arg);
break; break;
case SNAPSHOT_AVAIL_SWAP_SIZE:
case SNAPSHOT_AVAIL_SWAP: case SNAPSHOT_AVAIL_SWAP:
snapshot_deprecated_ioctl(cmd);
case SNAPSHOT_AVAIL_SWAP_SIZE:
size = count_swap_pages(data->swap, 1); size = count_swap_pages(data->swap, 1);
size <<= PAGE_SHIFT; size <<= PAGE_SHIFT;
error = put_user(size, (loff_t __user *)arg); error = put_user(size, (loff_t __user *)arg);
break; break;
case SNAPSHOT_ALLOC_SWAP_PAGE:
case SNAPSHOT_GET_SWAP_PAGE: case SNAPSHOT_GET_SWAP_PAGE:
snapshot_deprecated_ioctl(cmd);
case SNAPSHOT_ALLOC_SWAP_PAGE:
if (data->swap < 0 || data->swap >= MAX_SWAPFILES) { if (data->swap < 0 || data->swap >= MAX_SWAPFILES) {
error = -ENODEV; error = -ENODEV;
break; break;
...@@ -321,6 +334,7 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd, ...@@ -321,6 +334,7 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd,
break; break;
case SNAPSHOT_SET_SWAP_FILE: /* This ioctl is deprecated */ case SNAPSHOT_SET_SWAP_FILE: /* This ioctl is deprecated */
snapshot_deprecated_ioctl(cmd);
if (!swsusp_swap_in_use()) { if (!swsusp_swap_in_use()) {
/* /*
* User space encodes device types as two-byte values, * User space encodes device types as two-byte values,
...@@ -362,6 +376,7 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd, ...@@ -362,6 +376,7 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd,
break; break;
case SNAPSHOT_PMOPS: /* This ioctl is deprecated */ case SNAPSHOT_PMOPS: /* This ioctl is deprecated */
snapshot_deprecated_ioctl(cmd);
error = -EINVAL; error = -EINVAL;
switch (arg) { switch (arg) {
......
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