aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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
40 if (autosleep_state == PM_SUSPEND_ON) {
41 mutex_unlock(&autosleep_lock);
42 return;
43 }
44 if (autosleep_state >= PM_SUSPEND_MAX)
45 hibernate();
46 else
47 pm_suspend(autosleep_state);
48
49 mutex_unlock(&autosleep_lock);
50
51 if (!pm_get_wakeup_count(&final_count, false))
52 goto out;
53
54 /*
55 * If the wakeup occured for an unknown reason, wait to prevent the
56 * system from trying to suspend and waking up in a tight loop.
57 */
58 if (final_count == initial_count)
59 schedule_timeout_uninterruptible(HZ / 2);
60
61 out:
62 queue_up_suspend_work();
63}
64
65static DECLARE_WORK(suspend_work, try_to_suspend);
66
67void queue_up_suspend_work(void)
68{
69 if (!work_pending(&suspend_work) && autosleep_state > PM_SUSPEND_ON)
70 queue_work(autosleep_wq, &suspend_work);
71}
72
73suspend_state_t pm_autosleep_state(void)
74{
75 return autosleep_state;
76}
77
78int pm_autosleep_lock(void)
79{
80 return mutex_lock_interruptible(&autosleep_lock);
81}
82
83void pm_autosleep_unlock(void)
84{
85 mutex_unlock(&autosleep_lock);
86}
87
88int pm_autosleep_set_state(suspend_state_t state)
89{
90
91#ifndef CONFIG_HIBERNATION
92 if (state >= PM_SUSPEND_MAX)
93 return -EINVAL;
94#endif
95
96 __pm_stay_awake(autosleep_ws);
97
98 mutex_lock(&autosleep_lock);
99
100 autosleep_state = state;
101
102 __pm_relax(autosleep_ws);
103
104 if (state > PM_SUSPEND_ON)
105 queue_up_suspend_work();
106
107 mutex_unlock(&autosleep_lock);
108 return 0;
109}
110
111int __init pm_autosleep_init(void)
112{
113 autosleep_ws = wakeup_source_register("autosleep");
114 if (!autosleep_ws)
115 return -ENOMEM;
116
117 autosleep_wq = alloc_ordered_workqueue("autosleep", 0);
118 if (autosleep_wq)
119 return 0;
120
121 wakeup_source_unregister(autosleep_ws);
122 return -ENOMEM;
123}
diff --git a/kernel/power/main.c b/kernel/power/main.c
index 1c12581f1c62..ba6a5645952d 100644
--- a/kernel/power/main.c
+++ b/kernel/power/main.c
@@ -269,8 +269,7 @@ static ssize_t state_show(struct kobject *kobj, struct kobj_attribute *attr,
269 return (s - buf); 269 return (s - buf);
270} 270}
271 271
272static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr, 272static suspend_state_t decode_state(const char *buf, size_t n)
273 const char *buf, size_t n)
274{ 273{
275#ifdef CONFIG_SUSPEND 274#ifdef CONFIG_SUSPEND
276 suspend_state_t state = PM_SUSPEND_STANDBY; 275 suspend_state_t state = PM_SUSPEND_STANDBY;
@@ -278,27 +277,48 @@ static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
278#endif 277#endif
279 char *p; 278 char *p;
280 int len; 279 int len;
281 int error = -EINVAL;
282 280
283 p = memchr(buf, '\n', n); 281 p = memchr(buf, '\n', n);
284 len = p ? p - buf : n; 282 len = p ? p - buf : n;
285 283
286 /* First, check if we are requested to hibernate */ 284 /* Check hibernation first. */
287 if (len == 4 && !strncmp(buf, "disk", len)) { 285 if (len == 4 && !strncmp(buf, "disk", len))
288 error = hibernate(); 286 return PM_SUSPEND_MAX;
289 goto Exit;
290 }
291 287
292#ifdef CONFIG_SUSPEND 288#ifdef CONFIG_SUSPEND
293 for (s = &pm_states[state]; state < PM_SUSPEND_MAX; s++, state++) { 289 for (s = &pm_states[state]; state < PM_SUSPEND_MAX; s++, state++)
294 if (*s && len == strlen(*s) && !strncmp(buf, *s, len)) { 290 if (*s && len == strlen(*s) && !strncmp(buf, *s, len))
295 error = pm_suspend(state); 291 return state;
296 break;
297 }
298 }
299#endif 292#endif
300 293
301 Exit: 294 return PM_SUSPEND_ON;
295}
296
297static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
298 const char *buf, size_t n)
299{
300 suspend_state_t state;
301 int error;
302
303 error = pm_autosleep_lock();
304 if (error)
305 return error;
306
307 if (pm_autosleep_state() > PM_SUSPEND_ON) {
308 error = -EBUSY;
309 goto out;
310 }
311
312 state = decode_state(buf, n);
313 if (state < PM_SUSPEND_MAX)
314 error = pm_suspend(state);
315 else if (state == PM_SUSPEND_MAX)
316 error = hibernate();
317 else
318 error = -EINVAL;
319
320 out:
321 pm_autosleep_unlock();
302 return error ? error : n; 322 return error ? error : n;
303} 323}
304 324
@@ -339,7 +359,8 @@ static ssize_t wakeup_count_show(struct kobject *kobj,
339{ 359{
340 unsigned int val; 360 unsigned int val;
341 361
342 return pm_get_wakeup_count(&val) ? sprintf(buf, "%u\n", val) : -EINTR; 362 return pm_get_wakeup_count(&val, true) ?
363 sprintf(buf, "%u\n", val) : -EINTR;
343} 364}
344 365
345static ssize_t wakeup_count_store(struct kobject *kobj, 366static ssize_t wakeup_count_store(struct kobject *kobj,
@@ -347,15 +368,69 @@ static ssize_t wakeup_count_store(struct kobject *kobj,
347 const char *buf, size_t n) 368 const char *buf, size_t n)
348{ 369{
349 unsigned int val; 370 unsigned int val;
371 int error;
372
373 error = pm_autosleep_lock();
374 if (error)
375 return error;
350 376
377 if (pm_autosleep_state() > PM_SUSPEND_ON) {
378 error = -EBUSY;
379 goto out;
380 }
381
382 error = -EINVAL;
351 if (sscanf(buf, "%u", &val) == 1) { 383 if (sscanf(buf, "%u", &val) == 1) {
352 if (pm_save_wakeup_count(val)) 384 if (pm_save_wakeup_count(val))
353 return n; 385 error = n;
354 } 386 }
355 return -EINVAL; 387
388 out:
389 pm_autosleep_unlock();
390 return error;
356} 391}
357 392
358power_attr(wakeup_count); 393power_attr(wakeup_count);
394
395#ifdef CONFIG_PM_AUTOSLEEP
396static ssize_t autosleep_show(struct kobject *kobj,
397 struct kobj_attribute *attr,
398 char *buf)
399{
400 suspend_state_t state = pm_autosleep_state();
401
402 if (state == PM_SUSPEND_ON)
403 return sprintf(buf, "off\n");
404
405#ifdef CONFIG_SUSPEND
406 if (state < PM_SUSPEND_MAX)
407 return sprintf(buf, "%s\n", valid_state(state) ?
408 pm_states[state] : "error");
409#endif
410#ifdef CONFIG_HIBERNATION
411 return sprintf(buf, "disk\n");
412#else
413 return sprintf(buf, "error");
414#endif
415}
416
417static ssize_t autosleep_store(struct kobject *kobj,
418 struct kobj_attribute *attr,
419 const char *buf, size_t n)
420{
421 suspend_state_t state = decode_state(buf, n);
422 int error;
423
424 if (state == PM_SUSPEND_ON
425 && !(strncmp(buf, "off", 3) && strncmp(buf, "off\n", 4)))
426 return -EINVAL;
427
428 error = pm_autosleep_set_state(state);
429 return error ? error : n;
430}
431
432power_attr(autosleep);
433#endif /* CONFIG_PM_AUTOSLEEP */
359#endif /* CONFIG_PM_SLEEP */ 434#endif /* CONFIG_PM_SLEEP */
360 435
361#ifdef CONFIG_PM_TRACE 436#ifdef CONFIG_PM_TRACE
@@ -409,6 +484,9 @@ static struct attribute * g[] = {
409#ifdef CONFIG_PM_SLEEP 484#ifdef CONFIG_PM_SLEEP
410 &pm_async_attr.attr, 485 &pm_async_attr.attr,
411 &wakeup_count_attr.attr, 486 &wakeup_count_attr.attr,
487#ifdef CONFIG_PM_AUTOSLEEP
488 &autosleep_attr.attr,
489#endif
412#ifdef CONFIG_PM_DEBUG 490#ifdef CONFIG_PM_DEBUG
413 &pm_test_attr.attr, 491 &pm_test_attr.attr,
414#endif 492#endif
@@ -444,7 +522,10 @@ static int __init pm_init(void)
444 power_kobj = kobject_create_and_add("power", NULL); 522 power_kobj = kobject_create_and_add("power", NULL);
445 if (!power_kobj) 523 if (!power_kobj)
446 return -ENOMEM; 524 return -ENOMEM;
447 return sysfs_create_group(power_kobj, &attr_group); 525 error = sysfs_create_group(power_kobj, &attr_group);
526 if (error)
527 return error;
528 return pm_autosleep_init();
448} 529}
449 530
450core_initcall(pm_init); 531core_initcall(pm_init);
diff --git a/kernel/power/power.h b/kernel/power/power.h
index 98f3622d7407..4cf80fa115d9 100644
--- a/kernel/power/power.h
+++ b/kernel/power/power.h
@@ -264,3 +264,21 @@ static inline void suspend_thaw_processes(void)
264{ 264{
265} 265}
266#endif 266#endif
267
268#ifdef CONFIG_PM_AUTOSLEEP
269
270/* kernel/power/autosleep.c */
271extern int pm_autosleep_init(void);
272extern int pm_autosleep_lock(void);
273extern void pm_autosleep_unlock(void);
274extern suspend_state_t pm_autosleep_state(void);
275extern int pm_autosleep_set_state(suspend_state_t state);
276
277#else /* !CONFIG_PM_AUTOSLEEP */
278
279static inline int pm_autosleep_init(void) { return 0; }
280static inline int pm_autosleep_lock(void) { return 0; }
281static inline void pm_autosleep_unlock(void) {}
282static inline suspend_state_t pm_autosleep_state(void) { return PM_SUSPEND_ON; }
283
284#endif /* !CONFIG_PM_AUTOSLEEP */