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
This commit is contained in:
commit
06a79b82b2
28 changed files with 631 additions and 84 deletions
79
Documentation/ABI/testing/sysfs-devices-power
Normal file
79
Documentation/ABI/testing/sysfs-devices-power
Normal file
|
@ -0,0 +1,79 @@
|
|||
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:
|
|||
|
||||
CAUTION: Using it will cause your machine's real-time (CMOS)
|
||||
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>
|
|||
|
||||
---------------------------
|
||||
|
||||
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
|
||||
When: March 2010 / desktop catchup
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include <linux/resume-trace.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/async.h>
|
||||
|
||||
#include "../base.h"
|
||||
#include "power.h"
|
||||
|
@ -42,6 +43,7 @@
|
|||
LIST_HEAD(dpm_list);
|
||||
|
||||
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
|
||||
|
@ -56,6 +58,7 @@ static bool transition_started;
|
|||
void device_pm_init(struct device *dev)
|
||||
{
|
||||
dev->power.status = DPM_ON;
|
||||
init_completion(&dev->power.completion);
|
||||
pm_runtime_init(dev);
|
||||
}
|
||||
|
||||
|
@ -111,6 +114,7 @@ void device_pm_remove(struct device *dev)
|
|||
pr_debug("PM: Removing info for %s:%s\n",
|
||||
dev->bus ? dev->bus->name : "No Bus",
|
||||
kobject_name(&dev->kobj));
|
||||
complete_all(&dev->power.completion);
|
||||
mutex_lock(&dpm_list_mtx);
|
||||
list_del_init(&dev->power.entry);
|
||||
mutex_unlock(&dpm_list_mtx);
|
||||
|
@ -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.
|
||||
* @dev: Device to handle.
|
||||
|
@ -271,8 +300,9 @@ static int pm_noirq_op(struct device *dev,
|
|||
ktime_t calltime, delta, rettime;
|
||||
|
||||
if (initcall_debug) {
|
||||
pr_info("calling %s_i+ @ %i\n",
|
||||
dev_name(dev), task_pid_nr(current));
|
||||
pr_info("calling %s+ @ %i, parent: %s\n",
|
||||
dev_name(dev), task_pid_nr(current),
|
||||
dev->parent ? dev_name(dev->parent) : "none");
|
||||
calltime = ktime_get();
|
||||
}
|
||||
|
||||
|
@ -468,16 +498,20 @@ static int legacy_resume(struct device *dev, int (*cb)(struct device *dev))
|
|||
* device_resume - Execute "resume" callbacks for given device.
|
||||
* @dev: Device to handle.
|
||||
* @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;
|
||||
|
||||
TRACE_DEVICE(dev);
|
||||
TRACE_RESUME(0);
|
||||
|
||||
dpm_wait(dev->parent, async);
|
||||
down(&dev->sem);
|
||||
|
||||
dev->power.status = DPM_RESUMING;
|
||||
|
||||
if (dev->bus) {
|
||||
if (dev->bus->pm) {
|
||||
pm_dev_dbg(dev, state, "");
|
||||
|
@ -510,11 +544,29 @@ static int device_resume(struct device *dev, pm_message_t state)
|
|||
}
|
||||
End:
|
||||
up(&dev->sem);
|
||||
complete_all(&dev->power.completion);
|
||||
|
||||
TRACE_RESUME(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.
|
||||
* @state: PM transition of the system being carried out.
|
||||
|
@ -525,21 +577,33 @@ static int device_resume(struct device *dev, pm_message_t state)
|
|||
static void dpm_resume(pm_message_t state)
|
||||
{
|
||||
struct list_head list;
|
||||
struct device *dev;
|
||||
ktime_t starttime = ktime_get();
|
||||
|
||||
INIT_LIST_HEAD(&list);
|
||||
mutex_lock(&dpm_list_mtx);
|
||||
while (!list_empty(&dpm_list)) {
|
||||
struct device *dev = to_device(dpm_list.next);
|
||||
pm_transition = state;
|
||||
|
||||
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);
|
||||
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) {
|
||||
if (dev->power.status >= DPM_OFF && !is_async(dev)) {
|
||||
int error;
|
||||
|
||||
dev->power.status = DPM_RESUMING;
|
||||
mutex_unlock(&dpm_list_mtx);
|
||||
|
||||
error = device_resume(dev, state);
|
||||
error = device_resume(dev, state, false);
|
||||
|
||||
mutex_lock(&dpm_list_mtx);
|
||||
if (error)
|
||||
|
@ -554,6 +618,7 @@ static void dpm_resume(pm_message_t state)
|
|||
}
|
||||
list_splice(&list, &dpm_list);
|
||||
mutex_unlock(&dpm_list_mtx);
|
||||
async_synchronize_full();
|
||||
dpm_show_time(starttime, state, NULL);
|
||||
}
|
||||
|
||||
|
@ -731,17 +796,24 @@ static int legacy_suspend(struct device *dev, pm_message_t state,
|
|||
return error;
|
||||
}
|
||||
|
||||
static int async_error;
|
||||
|
||||
/**
|
||||
* device_suspend - Execute "suspend" callbacks for given device.
|
||||
* @dev: Device to handle.
|
||||
* @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;
|
||||
|
||||
dpm_wait_for_children(dev, async);
|
||||
down(&dev->sem);
|
||||
|
||||
if (async_error)
|
||||
goto End;
|
||||
|
||||
if (dev->class) {
|
||||
if (dev->class->pm) {
|
||||
pm_dev_dbg(dev, state, "class ");
|
||||
|
@ -772,12 +844,44 @@ static int device_suspend(struct device *dev, pm_message_t state)
|
|||
error = legacy_suspend(dev, state, dev->bus->suspend);
|
||||
}
|
||||
}
|
||||
|
||||
if (!error)
|
||||
dev->power.status = DPM_OFF;
|
||||
|
||||
End:
|
||||
up(&dev->sem);
|
||||
complete_all(&dev->power.completion);
|
||||
|
||||
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.
|
||||
* @state: PM transition of the system being carried out.
|
||||
|
@ -790,13 +894,15 @@ static int dpm_suspend(pm_message_t state)
|
|||
|
||||
INIT_LIST_HEAD(&list);
|
||||
mutex_lock(&dpm_list_mtx);
|
||||
pm_transition = state;
|
||||
async_error = 0;
|
||||
while (!list_empty(&dpm_list)) {
|
||||
struct device *dev = to_device(dpm_list.prev);
|
||||
|
||||
get_device(dev);
|
||||
mutex_unlock(&dpm_list_mtx);
|
||||
|
||||
error = device_suspend(dev, state);
|
||||
error = device_suspend(dev);
|
||||
|
||||
mutex_lock(&dpm_list_mtx);
|
||||
if (error) {
|
||||
|
@ -804,13 +910,17 @@ static int dpm_suspend(pm_message_t state)
|
|||
put_device(dev);
|
||||
break;
|
||||
}
|
||||
dev->power.status = DPM_OFF;
|
||||
if (!list_empty(&dev->power.entry))
|
||||
list_move(&dev->power.entry, &list);
|
||||
put_device(dev);
|
||||
if (async_error)
|
||||
break;
|
||||
}
|
||||
list_splice(&list, dpm_list.prev);
|
||||
mutex_unlock(&dpm_list_mtx);
|
||||
async_synchronize_full();
|
||||
if (!error)
|
||||
error = async_error;
|
||||
if (!error)
|
||||
dpm_show_time(starttime, state, NULL);
|
||||
return error;
|
||||
|
@ -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);
|
||||
}
|
||||
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) {}
|
|||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
|
||||
/*
|
||||
* main.c
|
||||
*/
|
||||
/* kernel/power/main.c */
|
||||
extern int pm_async_enabled;
|
||||
|
||||
/* drivers/base/power/main.c */
|
||||
extern struct list_head dpm_list; /* The active device list */
|
||||
|
||||
static inline struct device *to_device(struct list_head *entry)
|
||||
|
|
|
@ -1010,6 +1010,50 @@ void pm_runtime_enable(struct device *dev)
|
|||
}
|
||||
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.
|
||||
* @dev: Device object to initialize.
|
||||
|
@ -1028,6 +1072,7 @@ void pm_runtime_init(struct device *dev)
|
|||
|
||||
atomic_set(&dev->power.child_count, 0);
|
||||
pm_suspend_ignore_children(dev, false);
|
||||
dev->power.runtime_auto = true;
|
||||
|
||||
dev->power.request_pending = false;
|
||||
dev->power.request = RPM_REQ_NONE;
|
||||
|
|
|
@ -4,9 +4,25 @@
|
|||
|
||||
#include <linux/device.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/pm_runtime.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
|
||||
*
|
||||
* Some devices support "wakeup" events, which are hardware signals
|
||||
|
@ -38,11 +54,61 @@
|
|||
* wakeup events internally (unless they are disabled), keeping
|
||||
* their hardware in low power modes whenever they're unused. This
|
||||
* 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 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
|
||||
wake_show(struct device * dev, struct device_attribute *attr, char * buf)
|
||||
{
|
||||
|
@ -77,9 +143,43 @@ wake_store(struct device * dev, struct device_attribute *attr,
|
|||
|
||||
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[] = {
|
||||
#ifdef CONFIG_PM_RUNTIME
|
||||
&dev_attr_control.attr,
|
||||
#endif
|
||||
&dev_attr_wakeup.attr,
|
||||
#ifdef CONFIG_PM_SLEEP_ADVANCED_DEBUG
|
||||
&dev_attr_async.attr,
|
||||
#endif
|
||||
NULL,
|
||||
};
|
||||
static struct attribute_group pm_attr_group = {
|
||||
|
|
|
@ -1540,6 +1540,7 @@ void pci_pm_init(struct pci_dev *dev)
|
|||
int pm;
|
||||
u16 pmc;
|
||||
|
||||
device_enable_async_suspend(&dev->dev);
|
||||
dev->wakeup_prepared = false;
|
||||
dev->pm_cap = 0;
|
||||
|
||||
|
|
|
@ -285,6 +285,7 @@ static int pcie_device_init(struct pci_dev *pdev, int service, int irq)
|
|||
pci_name(pdev),
|
||||
get_descriptor_id(pdev->pcie_type, service));
|
||||
device->parent = &pdev->dev;
|
||||
device_enable_async_suspend(device);
|
||||
|
||||
retval = device_register(device);
|
||||
if (retval)
|
||||
|
|
|
@ -1436,6 +1436,7 @@ struct pci_bus * pci_create_bus(struct device *parent,
|
|||
if (error)
|
||||
goto dev_reg_err;
|
||||
b->bridge = get_device(dev);
|
||||
device_enable_async_suspend(b->bridge);
|
||||
|
||||
if (!parent)
|
||||
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,
|
|||
shost->shost_gendev.parent = dev ? dev : &platform_bus;
|
||||
shost->dma_dev = dma_dev;
|
||||
|
||||
device_enable_async_suspend(&shost->shost_gendev);
|
||||
|
||||
error = device_add(&shost->shost_gendev);
|
||||
if (error)
|
||||
goto out;
|
||||
|
@ -222,6 +224,8 @@ int scsi_add_host_with_dma(struct Scsi_Host *shost, struct device *dev,
|
|||
scsi_host_set_state(shost, SHOST_RUNNING);
|
||||
get_device(shost->shost_gendev.parent);
|
||||
|
||||
device_enable_async_suspend(&shost->shost_dev);
|
||||
|
||||
error = device_add(&shost->shost_dev);
|
||||
if (error)
|
||||
goto out_del_gendev;
|
||||
|
|
|
@ -847,6 +847,8 @@ static int scsi_target_add(struct scsi_target *starget)
|
|||
if (starget->state != STARGET_CREATED)
|
||||
return 0;
|
||||
|
||||
device_enable_async_suspend(&starget->dev);
|
||||
|
||||
error = device_add(&starget->dev);
|
||||
if (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)
|
|||
return error;
|
||||
|
||||
transport_configure_device(&starget->dev);
|
||||
device_enable_async_suspend(&sdev->sdev_gendev);
|
||||
error = device_add(&sdev->sdev_gendev);
|
||||
if (error) {
|
||||
printk(KERN_INFO "error 1\n");
|
||||
return error;
|
||||
}
|
||||
device_enable_async_suspend(&sdev->sdev_dev);
|
||||
error = device_add(&sdev->sdev_dev);
|
||||
if (error) {
|
||||
printk(KERN_INFO "error 2\n");
|
||||
|
|
|
@ -1022,6 +1022,14 @@ static int usb_resume_device(struct usb_device *udev, pm_message_t msg)
|
|||
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)
|
||||
udev->reset_resume = 1;
|
||||
|
||||
|
|
|
@ -186,6 +186,7 @@ int usb_create_ep_devs(struct device *parent,
|
|||
ep_dev->dev.parent = parent;
|
||||
ep_dev->dev.release = ep_device_release;
|
||||
dev_set_name(&ep_dev->dev, "ep_%02x", endpoint->desc.bEndpointAddress);
|
||||
device_enable_async_suspend(&ep_dev->dev);
|
||||
|
||||
retval = device_register(&ep_dev->dev);
|
||||
if (retval)
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/usb.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
|
@ -37,6 +38,122 @@
|
|||
|
||||
/* 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)
|
|||
if (region == PCI_ROM_RESOURCE) {
|
||||
dev_dbg(&dev->dev, "no i/o regions available\n");
|
||||
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)
|
|||
retval = usb_add_hcd(hcd, dev->irq, IRQF_DISABLED | IRQF_SHARED);
|
||||
if (retval != 0)
|
||||
goto err4;
|
||||
set_hs_companion(dev, hcd);
|
||||
return retval;
|
||||
|
||||
err4:
|
||||
|
@ -142,6 +260,7 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
|||
} else
|
||||
release_region(hcd->rsrc_start, hcd->rsrc_len);
|
||||
err2:
|
||||
clear_hs_companion(dev, hcd);
|
||||
usb_put_hcd(hcd);
|
||||
err1:
|
||||
pci_disable_device(dev);
|
||||
|
@ -180,6 +299,7 @@ void usb_hcd_pci_remove(struct pci_dev *dev)
|
|||
} else {
|
||||
release_region(hcd->rsrc_start, hcd->rsrc_len);
|
||||
}
|
||||
clear_hs_companion(dev, hcd);
|
||||
usb_put_hcd(hcd);
|
||||
pci_disable_device(dev);
|
||||
}
|
||||
|
@ -344,6 +464,11 @@ static int resume_common(struct device *dev, bool hibernated)
|
|||
clear_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);
|
||||
|
||||
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);
|
||||
if (retval) {
|
||||
dev_err(dev, "PCI post-resume error %d!\n", retval);
|
||||
|
|
|
@ -1817,6 +1817,7 @@ int usb_new_device(struct usb_device *udev)
|
|||
/* Tell the world! */
|
||||
announce_device(udev);
|
||||
|
||||
device_enable_async_suspend(&udev->dev);
|
||||
/* Register the device. The device driver is responsible
|
||||
* for configuring the device and invoking the add-device
|
||||
* notifier chain (used by usbfs and possibly others).
|
||||
|
|
|
@ -1867,6 +1867,7 @@ int usb_set_configuration(struct usb_device *dev, int configuration)
|
|||
"adding %s (config #%d, interface %d)\n",
|
||||
dev_name(&intf->dev), configuration,
|
||||
intf->cur_altsetting->desc.bInterfaceNumber);
|
||||
device_enable_async_suspend(&intf->dev);
|
||||
ret = device_add(&intf->dev);
|
||||
if (ret != 0) {
|
||||
dev_err(&dev->dev, "device_add(%s) --> %d\n",
|
||||
|
|
|
@ -472,6 +472,23 @@ static inline int device_is_registered(struct device *dev)
|
|||
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);
|
||||
|
||||
/*
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include <linux/spinlock.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/completion.h>
|
||||
|
||||
/*
|
||||
* Callbacks for platform drivers to implement.
|
||||
|
@ -412,9 +413,11 @@ struct dev_pm_info {
|
|||
pm_message_t power_state;
|
||||
unsigned int can_wakeup:1;
|
||||
unsigned int should_wakeup:1;
|
||||
unsigned async_suspend:1;
|
||||
enum dpm_state status; /* Owned by the PM core */
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
struct list_head entry;
|
||||
struct completion completion;
|
||||
#endif
|
||||
#ifdef CONFIG_PM_RUNTIME
|
||||
struct timer_list suspend_timer;
|
||||
|
@ -430,6 +433,7 @@ struct dev_pm_info {
|
|||
unsigned int request_pending:1;
|
||||
unsigned int deferred_resume:1;
|
||||
unsigned int run_wake:1;
|
||||
unsigned int runtime_auto:1;
|
||||
enum rpm_request request;
|
||||
enum rpm_status runtime_status;
|
||||
int runtime_error;
|
||||
|
@ -508,6 +512,7 @@ extern void __suspend_report_result(const char *function, void *fn, int ret);
|
|||
__suspend_report_result(__func__, fn, ret); \
|
||||
} while (0)
|
||||
|
||||
extern void device_pm_wait_for_dev(struct device *sub, struct device *dev);
|
||||
#else /* !CONFIG_PM_SLEEP */
|
||||
|
||||
#define device_pm_lock() do {} while (0)
|
||||
|
@ -520,6 +525,7 @@ static inline int dpm_suspend_start(pm_message_t state)
|
|||
|
||||
#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 */
|
||||
|
||||
/* How to reorder dpm_list after device_move() */
|
||||
|
|
|
@ -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 void pm_runtime_enable(struct device *dev);
|
||||
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)
|
||||
{
|
||||
|
@ -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 void pm_runtime_enable(struct device *dev) {}
|
||||
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 void pm_suspend_ignore_children(struct device *dev, bool en) {}
|
||||
|
|
|
@ -6,6 +6,11 @@
|
|||
|
||||
extern int pm_trace_enabled;
|
||||
|
||||
static inline int pm_trace_is_enabled(void)
|
||||
{
|
||||
return pm_trace_enabled;
|
||||
}
|
||||
|
||||
struct device;
|
||||
extern void set_trace_device(struct device *);
|
||||
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
|
||||
|
||||
static inline int pm_trace_is_enabled(void) { return 0; }
|
||||
|
||||
#define TRACE_DEVICE(dev) do { } while (0)
|
||||
#define TRACE_RESUME(dev) do { } while (0)
|
||||
|
||||
|
|
|
@ -339,6 +339,7 @@ struct usb_bus {
|
|||
|
||||
struct usb_devmap devmap; /* device address allocation map */
|
||||
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 */
|
||||
|
||||
int bandwidth_allocated; /* on this bus: how much of the time
|
||||
|
|
|
@ -27,6 +27,15 @@ config PM_DEBUG
|
|||
code. This is helpful when debugging and reporting PM bugs, like
|
||||
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
|
||||
bool "Verbose Power Management debugging"
|
||||
depends on PM_DEBUG
|
||||
|
@ -85,6 +94,11 @@ config PM_SLEEP
|
|||
depends on SUSPEND || HIBERNATION || XEN_SAVE_RESTORE
|
||||
default y
|
||||
|
||||
config PM_SLEEP_ADVANCED_DEBUG
|
||||
bool
|
||||
depends on PM_ADVANCED_DEBUG
|
||||
default n
|
||||
|
||||
config SUSPEND
|
||||
bool "Suspend to RAM and standby"
|
||||
depends on PM && ARCH_SUSPEND_POSSIBLE
|
||||
|
|
|
@ -44,6 +44,32 @@ int pm_notifier_call_chain(unsigned long val)
|
|||
== 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
|
||||
int pm_test_level = TEST_NONE;
|
||||
|
||||
|
@ -208,8 +234,11 @@ static struct attribute * g[] = {
|
|||
#ifdef CONFIG_PM_TRACE
|
||||
&pm_trace_attr.attr,
|
||||
#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,
|
||||
#endif
|
||||
#endif
|
||||
NULL,
|
||||
};
|
||||
|
|
|
@ -1181,7 +1181,7 @@ static void free_unnecessary_pages(void)
|
|||
|
||||
memory_bm_position_reset(©_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(©_bm);
|
||||
struct page *page = pfn_to_page(pfn);
|
||||
|
||||
|
@ -1500,7 +1500,7 @@ asmlinkage int swsusp_save(void)
|
|||
{
|
||||
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);
|
||||
nr_pages = count_data_pages();
|
||||
|
|
|
@ -657,10 +657,6 @@ int swsusp_read(unsigned int *flags_p)
|
|||
struct swsusp_info *header;
|
||||
|
||||
*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));
|
||||
error = snapshot_write_next(&snapshot, PAGE_SIZE);
|
||||
|
|
|
@ -1,58 +0,0 @@
|
|||
/*
|
||||
* 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,
|
|||
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,
|
||||
unsigned long arg)
|
||||
{
|
||||
|
@ -246,8 +255,9 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd,
|
|||
data->frozen = 0;
|
||||
break;
|
||||
|
||||
case SNAPSHOT_CREATE_IMAGE:
|
||||
case SNAPSHOT_ATOMIC_SNAPSHOT:
|
||||
snapshot_deprecated_ioctl(cmd);
|
||||
case SNAPSHOT_CREATE_IMAGE:
|
||||
if (data->mode != O_RDONLY || !data->frozen || data->ready) {
|
||||
error = -EPERM;
|
||||
break;
|
||||
|
@ -275,8 +285,9 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd,
|
|||
data->ready = 0;
|
||||
break;
|
||||
|
||||
case SNAPSHOT_PREF_IMAGE_SIZE:
|
||||
case SNAPSHOT_SET_IMAGE_SIZE:
|
||||
snapshot_deprecated_ioctl(cmd);
|
||||
case SNAPSHOT_PREF_IMAGE_SIZE:
|
||||
image_size = arg;
|
||||
break;
|
||||
|
||||
|
@ -290,15 +301,17 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd,
|
|||
error = put_user(size, (loff_t __user *)arg);
|
||||
break;
|
||||
|
||||
case SNAPSHOT_AVAIL_SWAP_SIZE:
|
||||
case SNAPSHOT_AVAIL_SWAP:
|
||||
snapshot_deprecated_ioctl(cmd);
|
||||
case SNAPSHOT_AVAIL_SWAP_SIZE:
|
||||
size = count_swap_pages(data->swap, 1);
|
||||
size <<= PAGE_SHIFT;
|
||||
error = put_user(size, (loff_t __user *)arg);
|
||||
break;
|
||||
|
||||
case SNAPSHOT_ALLOC_SWAP_PAGE:
|
||||
case SNAPSHOT_GET_SWAP_PAGE:
|
||||
snapshot_deprecated_ioctl(cmd);
|
||||
case SNAPSHOT_ALLOC_SWAP_PAGE:
|
||||
if (data->swap < 0 || data->swap >= MAX_SWAPFILES) {
|
||||
error = -ENODEV;
|
||||
break;
|
||||
|
@ -321,6 +334,7 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd,
|
|||
break;
|
||||
|
||||
case SNAPSHOT_SET_SWAP_FILE: /* This ioctl is deprecated */
|
||||
snapshot_deprecated_ioctl(cmd);
|
||||
if (!swsusp_swap_in_use()) {
|
||||
/*
|
||||
* User space encodes device types as two-byte values,
|
||||
|
@ -362,6 +376,7 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd,
|
|||
break;
|
||||
|
||||
case SNAPSHOT_PMOPS: /* This ioctl is deprecated */
|
||||
snapshot_deprecated_ioctl(cmd);
|
||||
error = -EINVAL;
|
||||
|
||||
switch (arg) {
|
||||
|
|
Loading…
Reference in a new issue