diff options
author | Rafael J. Wysocki <rjw@sisk.pl> | 2007-11-19 17:41:19 -0500 |
---|---|---|
committer | Len Brown <len.brown@intel.com> | 2008-02-01 18:30:54 -0500 |
commit | 0e7d56e3d9d7e37c79d0e05ffb3994e34beb3bbc (patch) | |
tree | cac3e72f882085bc869df9302248a079c657bb10 /kernel | |
parent | c3e94d899c864e558f938f9845ddb8c2e5d5ccd0 (diff) |
Suspend: Testing facility (rev. 2)
Introduce sysfs attribute /sys/power/pm_test allowing one to test the suspend
core code. Namely, writing one of the strings:
freezer
devices
platform
processors
core
to this file causes the suspend code to work in one of the test modes defined as
follows:
freezer
- test the freezing of processes
devices
- test the freezing of processes and suspending of devices
platform
- test the freezing of processes, suspending of devices and platform global
control methods(*)
processors
- test the freezing of processes, suspending of devices, platform global
control methods and the disabling of nonboot CPUs
core
- test the freezing of processes, suspending of devices, platform global
control methods, the disabling of nonboot CPUs and suspending of
platform/system devices
(*) These are ACPI global control methods on ACPI systems
Then, if a suspend is started by normal means, the suspend core will perform
its normal operations up to the point indicated by given test level. Next, it
will wait for 5 seconds and carry out the resume operations needed to transition
the system back to the fully functional state.
Writing "none" to /sys/power/pm_test turns the testing off.
When open for reading, /sys/power/pm_test contains a space-separated list of all
available tests (including "none" that represents the normal functionality) in
which the current test level is indicated by square brackets.
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Acked-by: Pavel Machek <pavel@ucw.cz>
Signed-off-by: Len Brown <len.brown@intel.com>
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/power/main.c | 108 | ||||
-rw-r--r-- | kernel/power/power.h | 18 |
2 files changed, 117 insertions, 9 deletions
diff --git a/kernel/power/main.c b/kernel/power/main.c index efc08360e627..84e1ae63bb8c 100644 --- a/kernel/power/main.c +++ b/kernel/power/main.c | |||
@@ -31,6 +31,79 @@ DEFINE_MUTEX(pm_mutex); | |||
31 | unsigned int pm_flags; | 31 | unsigned int pm_flags; |
32 | EXPORT_SYMBOL(pm_flags); | 32 | EXPORT_SYMBOL(pm_flags); |
33 | 33 | ||
34 | #ifdef CONFIG_PM_DEBUG | ||
35 | int pm_test_level = TEST_NONE; | ||
36 | |||
37 | static int suspend_test(int level) | ||
38 | { | ||
39 | if (pm_test_level == level) { | ||
40 | printk(KERN_INFO "suspend debug: Waiting for 5 seconds.\n"); | ||
41 | mdelay(5000); | ||
42 | return 1; | ||
43 | } | ||
44 | return 0; | ||
45 | } | ||
46 | |||
47 | static const char * const pm_tests[__TEST_AFTER_LAST] = { | ||
48 | [TEST_NONE] = "none", | ||
49 | [TEST_CORE] = "core", | ||
50 | [TEST_CPUS] = "processors", | ||
51 | [TEST_PLATFORM] = "platform", | ||
52 | [TEST_DEVICES] = "devices", | ||
53 | [TEST_FREEZER] = "freezer", | ||
54 | }; | ||
55 | |||
56 | static ssize_t pm_test_show(struct kset *kset, char *buf) | ||
57 | { | ||
58 | char *s = buf; | ||
59 | int level; | ||
60 | |||
61 | for (level = TEST_FIRST; level <= TEST_MAX; level++) | ||
62 | if (pm_tests[level]) { | ||
63 | if (level == pm_test_level) | ||
64 | s += sprintf(s, "[%s] ", pm_tests[level]); | ||
65 | else | ||
66 | s += sprintf(s, "%s ", pm_tests[level]); | ||
67 | } | ||
68 | |||
69 | if (s != buf) | ||
70 | /* convert the last space to a newline */ | ||
71 | *(s-1) = '\n'; | ||
72 | |||
73 | return (s - buf); | ||
74 | } | ||
75 | |||
76 | static ssize_t pm_test_store(struct kset *kset, const char *buf, size_t n) | ||
77 | { | ||
78 | const char * const *s; | ||
79 | int level; | ||
80 | char *p; | ||
81 | int len; | ||
82 | int error = -EINVAL; | ||
83 | |||
84 | p = memchr(buf, '\n', n); | ||
85 | len = p ? p - buf : n; | ||
86 | |||
87 | mutex_lock(&pm_mutex); | ||
88 | |||
89 | level = TEST_FIRST; | ||
90 | for (s = &pm_tests[level]; level <= TEST_MAX; s++, level++) | ||
91 | if (*s && len == strlen(*s) && !strncmp(buf, *s, len)) { | ||
92 | pm_test_level = level; | ||
93 | error = 0; | ||
94 | break; | ||
95 | } | ||
96 | |||
97 | mutex_unlock(&pm_mutex); | ||
98 | |||
99 | return error ? error : n; | ||
100 | } | ||
101 | |||
102 | power_attr(pm_test); | ||
103 | #else /* !CONFIG_PM_DEBUG */ | ||
104 | static inline int suspend_test(int level) { return 0; } | ||
105 | #endif /* !CONFIG_PM_DEBUG */ | ||
106 | |||
34 | #ifdef CONFIG_SUSPEND | 107 | #ifdef CONFIG_SUSPEND |
35 | 108 | ||
36 | /* This is just an arbitrary number */ | 109 | /* This is just an arbitrary number */ |
@@ -136,7 +209,10 @@ static int suspend_enter(suspend_state_t state) | |||
136 | printk(KERN_ERR "Some devices failed to power down\n"); | 209 | printk(KERN_ERR "Some devices failed to power down\n"); |
137 | goto Done; | 210 | goto Done; |
138 | } | 211 | } |
139 | error = suspend_ops->enter(state); | 212 | |
213 | if (!suspend_test(TEST_CORE)) | ||
214 | error = suspend_ops->enter(state); | ||
215 | |||
140 | device_power_up(); | 216 | device_power_up(); |
141 | Done: | 217 | Done: |
142 | arch_suspend_enable_irqs(); | 218 | arch_suspend_enable_irqs(); |
@@ -167,16 +243,25 @@ int suspend_devices_and_enter(suspend_state_t state) | |||
167 | printk(KERN_ERR "Some devices failed to suspend\n"); | 243 | printk(KERN_ERR "Some devices failed to suspend\n"); |
168 | goto Resume_console; | 244 | goto Resume_console; |
169 | } | 245 | } |
246 | |||
247 | if (suspend_test(TEST_DEVICES)) | ||
248 | goto Resume_devices; | ||
249 | |||
170 | if (suspend_ops->prepare) { | 250 | if (suspend_ops->prepare) { |
171 | error = suspend_ops->prepare(); | 251 | error = suspend_ops->prepare(); |
172 | if (error) | 252 | if (error) |
173 | goto Resume_devices; | 253 | goto Resume_devices; |
174 | } | 254 | } |
255 | |||
256 | if (suspend_test(TEST_PLATFORM)) | ||
257 | goto Finish; | ||
258 | |||
175 | error = disable_nonboot_cpus(); | 259 | error = disable_nonboot_cpus(); |
176 | if (!error) | 260 | if (!error && !suspend_test(TEST_CPUS)) |
177 | suspend_enter(state); | 261 | suspend_enter(state); |
178 | 262 | ||
179 | enable_nonboot_cpus(); | 263 | enable_nonboot_cpus(); |
264 | Finish: | ||
180 | if (suspend_ops->finish) | 265 | if (suspend_ops->finish) |
181 | suspend_ops->finish(); | 266 | suspend_ops->finish(); |
182 | Resume_devices: | 267 | Resume_devices: |
@@ -243,12 +328,17 @@ static int enter_state(suspend_state_t state) | |||
243 | printk("done.\n"); | 328 | printk("done.\n"); |
244 | 329 | ||
245 | pr_debug("PM: Preparing system for %s sleep\n", pm_states[state]); | 330 | pr_debug("PM: Preparing system for %s sleep\n", pm_states[state]); |
246 | if ((error = suspend_prepare())) | 331 | error = suspend_prepare(); |
332 | if (error) | ||
247 | goto Unlock; | 333 | goto Unlock; |
248 | 334 | ||
335 | if (suspend_test(TEST_FREEZER)) | ||
336 | goto Finish; | ||
337 | |||
249 | pr_debug("PM: Entering %s sleep\n", pm_states[state]); | 338 | pr_debug("PM: Entering %s sleep\n", pm_states[state]); |
250 | error = suspend_devices_and_enter(state); | 339 | error = suspend_devices_and_enter(state); |
251 | 340 | ||
341 | Finish: | ||
252 | pr_debug("PM: Finishing wakeup.\n"); | 342 | pr_debug("PM: Finishing wakeup.\n"); |
253 | suspend_finish(); | 343 | suspend_finish(); |
254 | Unlock: | 344 | Unlock: |
@@ -369,18 +459,18 @@ pm_trace_store(struct kobject *kobj, struct kobj_attribute *attr, | |||
369 | } | 459 | } |
370 | 460 | ||
371 | power_attr(pm_trace); | 461 | power_attr(pm_trace); |
462 | #endif /* CONFIG_PM_TRACE */ | ||
372 | 463 | ||
373 | static struct attribute * g[] = { | 464 | static struct attribute * g[] = { |
374 | &state_attr.attr, | 465 | &state_attr.attr, |
466 | #ifdef CONFIG_PM_TRACE | ||
375 | &pm_trace_attr.attr, | 467 | &pm_trace_attr.attr, |
468 | #endif | ||
469 | #ifdef CONFIG_PM_DEBUG | ||
470 | &pm_test_attr.attr, | ||
471 | #endif | ||
376 | NULL, | 472 | NULL, |
377 | }; | 473 | }; |
378 | #else | ||
379 | static struct attribute * g[] = { | ||
380 | &state_attr.attr, | ||
381 | NULL, | ||
382 | }; | ||
383 | #endif /* CONFIG_PM_TRACE */ | ||
384 | 474 | ||
385 | static struct attribute_group attr_group = { | 475 | static struct attribute_group attr_group = { |
386 | .attrs = g, | 476 | .attrs = g, |
diff --git a/kernel/power/power.h b/kernel/power/power.h index c5321eb1f7c8..9f9e16e33962 100644 --- a/kernel/power/power.h +++ b/kernel/power/power.h | |||
@@ -188,3 +188,21 @@ int restore_highmem(void); | |||
188 | static inline unsigned int count_highmem_pages(void) { return 0; } | 188 | static inline unsigned int count_highmem_pages(void) { return 0; } |
189 | static inline int restore_highmem(void) { return 0; } | 189 | static inline int restore_highmem(void) { return 0; } |
190 | #endif | 190 | #endif |
191 | |||
192 | /* | ||
193 | * Suspend test levels | ||
194 | */ | ||
195 | enum { | ||
196 | /* keep first */ | ||
197 | TEST_NONE, | ||
198 | TEST_CORE, | ||
199 | TEST_CPUS, | ||
200 | TEST_PLATFORM, | ||
201 | TEST_DEVICES, | ||
202 | TEST_FREEZER, | ||
203 | /* keep last */ | ||
204 | __TEST_AFTER_LAST | ||
205 | }; | ||
206 | |||
207 | #define TEST_FIRST TEST_NONE | ||
208 | #define TEST_MAX (__TEST_AFTER_LAST - 1) | ||