forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
PM / Sleep: Implement opportunistic sleep, v2
Introduce a mechanism by which the kernel can trigger global transitions to a sleep state chosen by user space if there are no active wakeup sources. It consists of a new sysfs attribute, /sys/power/autosleep, that can be written one of the strings returned by reads from /sys/power/state, an ordered workqueue and a work item carrying out the "suspend" operations. If a string representing the system's sleep state is written to /sys/power/autosleep, the work item triggering transitions to that state is queued up and it requeues itself after every execution until user space writes "off" to /sys/power/autosleep. That work item enables the detection of wakeup events using the functions already defined in drivers/base/power/wakeup.c (with one small modification) and calls either pm_suspend(), or hibernate() to put the system into a sleep state. If a wakeup event is reported while the transition is in progress, it will abort the transition and the "system suspend" work item will be queued up again. Signed-off-by: Rafael J. Wysocki <[email protected]> Acked-by: Greg Kroah-Hartman <[email protected]> Reviewed-by: NeilBrown <[email protected]>
- Loading branch information
Showing
8 changed files
with
298 additions
and
35 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -172,3 +172,20 @@ Description: | |
|
||
Reading from this file will display the current value, which is | ||
set to 1 MB by default. | ||
|
||
What: /sys/power/autosleep | ||
Date: April 2012 | ||
Contact: Rafael J. Wysocki <[email protected]> | ||
Description: | ||
The /sys/power/autosleep file can be written one of the strings | ||
returned by reads from /sys/power/state. If that happens, a | ||
work item attempting to trigger a transition of the system to | ||
the sleep state represented by that string is queued up. This | ||
attempt will only succeed if there are no active wakeup sources | ||
in the system at that time. After every execution, regardless | ||
of whether or not the attempt to put the system to sleep has | ||
succeeded, the work item requeues itself until user space | ||
writes "off" to /sys/power/autosleep. | ||
|
||
Reading from this file causes the last string successfully | ||
written to it to be returned. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
/* | ||
* kernel/power/autosleep.c | ||
* | ||
* Opportunistic sleep support. | ||
* | ||
* Copyright (C) 2012 Rafael J. Wysocki <[email protected]> | ||
*/ | ||
|
||
#include <linux/device.h> | ||
#include <linux/mutex.h> | ||
#include <linux/pm_wakeup.h> | ||
|
||
#include "power.h" | ||
|
||
static suspend_state_t autosleep_state; | ||
static struct workqueue_struct *autosleep_wq; | ||
/* | ||
* Note: it is only safe to mutex_lock(&autosleep_lock) if a wakeup_source | ||
* is active, otherwise a deadlock with try_to_suspend() is possible. | ||
* Alternatively mutex_lock_interruptible() can be used. This will then fail | ||
* if an auto_sleep cycle tries to freeze processes. | ||
*/ | ||
static DEFINE_MUTEX(autosleep_lock); | ||
static struct wakeup_source *autosleep_ws; | ||
|
||
static void try_to_suspend(struct work_struct *work) | ||
{ | ||
unsigned int initial_count, final_count; | ||
|
||
if (!pm_get_wakeup_count(&initial_count, true)) | ||
goto out; | ||
|
||
mutex_lock(&autosleep_lock); | ||
|
||
if (!pm_save_wakeup_count(initial_count)) { | ||
mutex_unlock(&autosleep_lock); | ||
goto out; | ||
} | ||
|
||
if (autosleep_state == PM_SUSPEND_ON) { | ||
mutex_unlock(&autosleep_lock); | ||
return; | ||
} | ||
if (autosleep_state >= PM_SUSPEND_MAX) | ||
hibernate(); | ||
else | ||
pm_suspend(autosleep_state); | ||
|
||
mutex_unlock(&autosleep_lock); | ||
|
||
if (!pm_get_wakeup_count(&final_count, false)) | ||
goto out; | ||
|
||
/* | ||
* If the wakeup occured for an unknown reason, wait to prevent the | ||
* system from trying to suspend and waking up in a tight loop. | ||
*/ | ||
if (final_count == initial_count) | ||
schedule_timeout_uninterruptible(HZ / 2); | ||
|
||
out: | ||
queue_up_suspend_work(); | ||
} | ||
|
||
static DECLARE_WORK(suspend_work, try_to_suspend); | ||
|
||
void queue_up_suspend_work(void) | ||
{ | ||
if (!work_pending(&suspend_work) && autosleep_state > PM_SUSPEND_ON) | ||
queue_work(autosleep_wq, &suspend_work); | ||
} | ||
|
||
suspend_state_t pm_autosleep_state(void) | ||
{ | ||
return autosleep_state; | ||
} | ||
|
||
int pm_autosleep_lock(void) | ||
{ | ||
return mutex_lock_interruptible(&autosleep_lock); | ||
} | ||
|
||
void pm_autosleep_unlock(void) | ||
{ | ||
mutex_unlock(&autosleep_lock); | ||
} | ||
|
||
int pm_autosleep_set_state(suspend_state_t state) | ||
{ | ||
|
||
#ifndef CONFIG_HIBERNATION | ||
if (state >= PM_SUSPEND_MAX) | ||
return -EINVAL; | ||
#endif | ||
|
||
__pm_stay_awake(autosleep_ws); | ||
|
||
mutex_lock(&autosleep_lock); | ||
|
||
autosleep_state = state; | ||
|
||
__pm_relax(autosleep_ws); | ||
|
||
if (state > PM_SUSPEND_ON) | ||
queue_up_suspend_work(); | ||
|
||
mutex_unlock(&autosleep_lock); | ||
return 0; | ||
} | ||
|
||
int __init pm_autosleep_init(void) | ||
{ | ||
autosleep_ws = wakeup_source_register("autosleep"); | ||
if (!autosleep_ws) | ||
return -ENOMEM; | ||
|
||
autosleep_wq = alloc_ordered_workqueue("autosleep", 0); | ||
if (autosleep_wq) | ||
return 0; | ||
|
||
wakeup_source_unregister(autosleep_ws); | ||
return -ENOMEM; | ||
} |
Oops, something went wrong.