aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorRafael J. Wysocki <rjw@sisk.pl>2007-11-19 17:41:19 -0500
committerLen Brown <len.brown@intel.com>2008-02-01 18:30:54 -0500
commit0e7d56e3d9d7e37c79d0e05ffb3994e34beb3bbc (patch)
treecac3e72f882085bc869df9302248a079c657bb10 /kernel
parentc3e94d899c864e558f938f9845ddb8c2e5d5ccd0 (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.c108
-rw-r--r--kernel/power/power.h18
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);
31unsigned int pm_flags; 31unsigned int pm_flags;
32EXPORT_SYMBOL(pm_flags); 32EXPORT_SYMBOL(pm_flags);
33 33
34#ifdef CONFIG_PM_DEBUG
35int pm_test_level = TEST_NONE;
36
37static 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
47static 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
56static 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
76static 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
102power_attr(pm_test);
103#else /* !CONFIG_PM_DEBUG */
104static 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
371power_attr(pm_trace); 461power_attr(pm_trace);
462#endif /* CONFIG_PM_TRACE */
372 463
373static struct attribute * g[] = { 464static 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
379static struct attribute * g[] = {
380 &state_attr.attr,
381 NULL,
382};
383#endif /* CONFIG_PM_TRACE */
384 474
385static struct attribute_group attr_group = { 475static 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);
188static inline unsigned int count_highmem_pages(void) { return 0; } 188static inline unsigned int count_highmem_pages(void) { return 0; }
189static inline int restore_highmem(void) { return 0; } 189static inline int restore_highmem(void) { return 0; }
190#endif 190#endif
191
192/*
193 * Suspend test levels
194 */
195enum {
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)