aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRafael J. Wysocki <rjw@sisk.pl>2012-04-29 16:53:22 -0400
committerRafael J. Wysocki <rjw@sisk.pl>2012-05-01 15:25:38 -0400
commit7483b4a4d9abf9dcf1ffe6e805ead2847ec3264e (patch)
treed03af746dc3be6480580ec569e0c2d708031f0bd
parent6791e36c4a40e8930e08669e60077eea6770c429 (diff)
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 <rjw@sisk.pl> Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Reviewed-by: NeilBrown <neilb@suse.de>
-rw-r--r--Documentation/ABI/testing/sysfs-power17
-rw-r--r--drivers/base/power/wakeup.c34
-rw-r--r--include/linux/suspend.h13
-rw-r--r--kernel/power/Kconfig8
-rw-r--r--kernel/power/Makefile1
-rw-r--r--kernel/power/autosleep.c123
-rw-r--r--kernel/power/main.c119
-rw-r--r--kernel/power/power.h18
8 files changed, 298 insertions, 35 deletions
diff --git a/Documentation/ABI/testing/sysfs-power b/Documentation/ABI/testing/sysfs-power
index b464d12761ba..237c735db6c9 100644
--- a/Documentation/ABI/testing/sysfs-power
+++ b/Documentation/ABI/testing/sysfs-power
@@ -172,3 +172,20 @@ Description:
172 172
173 Reading from this file will display the current value, which is 173 Reading from this file will display the current value, which is
174 set to 1 MB by default. 174 set to 1 MB by default.
175
176What: /sys/power/autosleep
177Date: April 2012
178Contact: Rafael J. Wysocki <rjw@sisk.pl>
179Description:
180 The /sys/power/autosleep file can be written one of the strings
181 returned by reads from /sys/power/state. If that happens, a
182 work item attempting to trigger a transition of the system to
183 the sleep state represented by that string is queued up. This
184 attempt will only succeed if there are no active wakeup sources
185 in the system at that time. After every execution, regardless
186 of whether or not the attempt to put the system to sleep has
187 succeeded, the work item requeues itself until user space
188 writes "off" to /sys/power/autosleep.
189
190 Reading from this file causes the last string successfully
191 written to it to be returned.
diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c
index 1132799421cd..cf1706df7610 100644
--- a/drivers/base/power/wakeup.c
+++ b/drivers/base/power/wakeup.c
@@ -660,29 +660,33 @@ bool pm_wakeup_pending(void)
660/** 660/**
661 * pm_get_wakeup_count - Read the number of registered wakeup events. 661 * pm_get_wakeup_count - Read the number of registered wakeup events.
662 * @count: Address to store the value at. 662 * @count: Address to store the value at.
663 * @block: Whether or not to block.
663 * 664 *
664 * Store the number of registered wakeup events at the address in @count. Block 665 * Store the number of registered wakeup events at the address in @count. If
665 * if the current number of wakeup events being processed is nonzero. 666 * @block is set, block until the current number of wakeup events being
667 * processed is zero.
666 * 668 *
667 * Return 'false' if the wait for the number of wakeup events being processed to 669 * Return 'false' if the current number of wakeup events being processed is
668 * drop down to zero has been interrupted by a signal (and the current number 670 * nonzero. Otherwise return 'true'.
669 * of wakeup events being processed is still nonzero). Otherwise return 'true'.
670 */ 671 */
671bool pm_get_wakeup_count(unsigned int *count) 672bool pm_get_wakeup_count(unsigned int *count, bool block)
672{ 673{
673 unsigned int cnt, inpr; 674 unsigned int cnt, inpr;
674 DEFINE_WAIT(wait);
675 675
676 for (;;) { 676 if (block) {
677 prepare_to_wait(&wakeup_count_wait_queue, &wait, 677 DEFINE_WAIT(wait);
678 TASK_INTERRUPTIBLE); 678
679 split_counters(&cnt, &inpr); 679 for (;;) {
680 if (inpr == 0 || signal_pending(current)) 680 prepare_to_wait(&wakeup_count_wait_queue, &wait,
681 break; 681 TASK_INTERRUPTIBLE);
682 split_counters(&cnt, &inpr);
683 if (inpr == 0 || signal_pending(current))
684 break;
682 685
683 schedule(); 686 schedule();
687 }
688 finish_wait(&wakeup_count_wait_queue, &wait);
684 } 689 }
685 finish_wait(&wakeup_count_wait_queue, &wait);
686 690
687 split_counters(&cnt, &inpr); 691 split_counters(&cnt, &inpr);
688 *count = cnt; 692 *count = cnt;
diff --git a/include/linux/suspend.h b/include/linux/suspend.h
index ac1c114c499d..76b7ec7d3a81 100644
--- a/include/linux/suspend.h
+++ b/include/linux/suspend.h
@@ -356,7 +356,7 @@ extern int unregister_pm_notifier(struct notifier_block *nb);
356extern bool events_check_enabled; 356extern bool events_check_enabled;
357 357
358extern bool pm_wakeup_pending(void); 358extern bool pm_wakeup_pending(void);
359extern bool pm_get_wakeup_count(unsigned int *count); 359extern bool pm_get_wakeup_count(unsigned int *count, bool block);
360extern bool pm_save_wakeup_count(unsigned int count); 360extern bool pm_save_wakeup_count(unsigned int count);
361 361
362static inline void lock_system_sleep(void) 362static inline void lock_system_sleep(void)
@@ -407,6 +407,17 @@ static inline void unlock_system_sleep(void) {}
407 407
408#endif /* !CONFIG_PM_SLEEP */ 408#endif /* !CONFIG_PM_SLEEP */
409 409
410#ifdef CONFIG_PM_AUTOSLEEP
411
412/* kernel/power/autosleep.c */
413void queue_up_suspend_work(void);
414
415#else /* !CONFIG_PM_AUTOSLEEP */
416
417static inline void queue_up_suspend_work(void) {}
418
419#endif /* !CONFIG_PM_AUTOSLEEP */
420
410#ifdef CONFIG_ARCH_SAVE_PAGE_KEYS 421#ifdef CONFIG_ARCH_SAVE_PAGE_KEYS
411/* 422/*
412 * The ARCH_SAVE_PAGE_KEYS functions can be used by an architecture 423 * The ARCH_SAVE_PAGE_KEYS functions can be used by an architecture
diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig
index deb5461e3216..67947083f842 100644
--- a/kernel/power/Kconfig
+++ b/kernel/power/Kconfig
@@ -103,6 +103,14 @@ config PM_SLEEP_SMP
103 select HOTPLUG 103 select HOTPLUG
104 select HOTPLUG_CPU 104 select HOTPLUG_CPU
105 105
106config PM_AUTOSLEEP
107 bool "Opportunistic sleep"
108 depends on PM_SLEEP
109 default n
110 ---help---
111 Allow the kernel to trigger a system transition into a global sleep
112 state automatically whenever there are no active wakeup sources.
113
106config PM_RUNTIME 114config PM_RUNTIME
107 bool "Run-time PM core functionality" 115 bool "Run-time PM core functionality"
108 depends on !IA64_HP_SIM 116 depends on !IA64_HP_SIM
diff --git a/kernel/power/Makefile b/kernel/power/Makefile
index 66d808ec5252..010b2f7e148c 100644
--- a/kernel/power/Makefile
+++ b/kernel/power/Makefile
@@ -9,5 +9,6 @@ obj-$(CONFIG_SUSPEND) += suspend.o
9obj-$(CONFIG_PM_TEST_SUSPEND) += suspend_test.o 9obj-$(CONFIG_PM_TEST_SUSPEND) += suspend_test.o
10obj-$(CONFIG_HIBERNATION) += hibernate.o snapshot.o swap.o user.o \ 10obj-$(CONFIG_HIBERNATION) += hibernate.o snapshot.o swap.o user.o \
11 block_io.o 11 block_io.o
12obj-$(CONFIG_PM_AUTOSLEEP) += autosleep.o
12 13
13obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o 14obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o
diff --git a/kernel/power/autosleep.c b/kernel/power/autosleep.c
new file mode 100644
index 000000000000..42348e3589d3
--- /dev/null
+++ b/kernel/power/autosleep.c
@@ -0,0 +1,123 @@
1/*
2 * kernel/power/autosleep.c
3 *
4 * Opportunistic sleep support.
5 *
6 * Copyright (C) 2012 Rafael J. Wysocki <rjw@sisk.pl>
7 */
8
9#include <linux/device.h>
10#include <linux/mutex.h>
11#include <linux/pm_wakeup.h>
12
13#include "power.h"
14
15static suspend_state_t autosleep_state;
16static struct workqueue_struct *autosleep_wq;
17/*
18 * Note: it is only safe to mutex_lock(&autosleep_lock) if a wakeup_source
19 * is active, otherwise a deadlock with try_to_suspend() is possible.
20 * Alternatively mutex_lock_interruptible() can be used. This will then fail
21 * if an auto_sleep cycle tries to freeze processes.
22 */
23static DEFINE_MUTEX(autosleep_lock);
24static struct wakeup_source *autosleep_ws;
25
26static void try_to_suspend(struct work_struct *work)
27{
28 unsigned int initial_count, final_count;
29
30 if (!pm_get_wakeup_count(&initial_count, true))
31 goto out;
32
33 mutex_lock(&autosleep_lock);
34
35 if (!pm_save_wakeup_count(initial_count)) {
36 mutex_unlock(&autosleep_lock);
37 goto out;
38 }
39