diff options
author | Rafael J. Wysocki <rjw@sisk.pl> | 2007-05-09 05:33:18 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-05-09 15:30:48 -0400 |
commit | a3d25c275d383975504dc53c25b691df59bd3c48 (patch) | |
tree | 161a2ae12a20a630c2f639e144872db2b92eb098 /kernel/power/disk.c | |
parent | d60846c4d16f9518b098b905af2b87cb6bf6dc42 (diff) |
PM: Separate hibernation code from suspend code
[ With Johannes Berg <johannes@sipsolutions.net> ]
Separate the hibernation (aka suspend to disk code) from the other suspend
code. In particular:
* Remove the definitions related to hibernation from include/linux/pm.h
* Introduce struct hibernation_ops and a new hibernate() function to hibernate
the system, defined in include/linux/suspend.h
* Separate suspend code in kernel/power/main.c from hibernation-related code
in kernel/power/disk.c and kernel/power/user.c (with the help of
hibernation_ops)
* Switch ACPI (the only user of pm_ops.pm_disk_mode) to hibernation_ops
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Cc: Greg KH <greg@kroah.com>
Cc: Pavel Machek <pavel@ucw.cz>
Cc: Nigel Cunningham <nigel@nigel.suspend2.net>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'kernel/power/disk.c')
-rw-r--r-- | kernel/power/disk.c | 195 |
1 files changed, 108 insertions, 87 deletions
diff --git a/kernel/power/disk.c b/kernel/power/disk.c index 06331374d862..b5f0543ed84d 100644 --- a/kernel/power/disk.c +++ b/kernel/power/disk.c | |||
@@ -30,30 +30,69 @@ char resume_file[256] = CONFIG_PM_STD_PARTITION; | |||
30 | dev_t swsusp_resume_device; | 30 | dev_t swsusp_resume_device; |
31 | sector_t swsusp_resume_block; | 31 | sector_t swsusp_resume_block; |
32 | 32 | ||
33 | enum { | ||
34 | HIBERNATION_INVALID, | ||
35 | HIBERNATION_PLATFORM, | ||
36 | HIBERNATION_TEST, | ||
37 | HIBERNATION_TESTPROC, | ||
38 | HIBERNATION_SHUTDOWN, | ||
39 | HIBERNATION_REBOOT, | ||
40 | /* keep last */ | ||
41 | __HIBERNATION_AFTER_LAST | ||
42 | }; | ||
43 | #define HIBERNATION_MAX (__HIBERNATION_AFTER_LAST-1) | ||
44 | #define HIBERNATION_FIRST (HIBERNATION_INVALID + 1) | ||
45 | |||
46 | static int hibernation_mode = HIBERNATION_SHUTDOWN; | ||
47 | |||
48 | struct hibernation_ops *hibernation_ops; | ||
49 | |||
50 | /** | ||
51 | * hibernation_set_ops - set the global hibernate operations | ||
52 | * @ops: the hibernation operations to use in subsequent hibernation transitions | ||
53 | */ | ||
54 | |||
55 | void hibernation_set_ops(struct hibernation_ops *ops) | ||
56 | { | ||
57 | if (ops && !(ops->prepare && ops->enter && ops->finish)) { | ||
58 | WARN_ON(1); | ||
59 | return; | ||
60 | } | ||
61 | mutex_lock(&pm_mutex); | ||
62 | hibernation_ops = ops; | ||
63 | if (ops) | ||
64 | hibernation_mode = HIBERNATION_PLATFORM; | ||
65 | else if (hibernation_mode == HIBERNATION_PLATFORM) | ||
66 | hibernation_mode = HIBERNATION_SHUTDOWN; | ||
67 | |||
68 | mutex_unlock(&pm_mutex); | ||
69 | } | ||
70 | |||
71 | |||
33 | /** | 72 | /** |
34 | * platform_prepare - prepare the machine for hibernation using the | 73 | * platform_prepare - prepare the machine for hibernation using the |
35 | * platform driver if so configured and return an error code if it fails | 74 | * platform driver if so configured and return an error code if it fails |
36 | */ | 75 | */ |
37 | 76 | ||
38 | static inline int platform_prepare(void) | 77 | static int platform_prepare(void) |
39 | { | 78 | { |
40 | int error = 0; | 79 | return (hibernation_mode == HIBERNATION_PLATFORM && hibernation_ops) ? |
80 | hibernation_ops->prepare() : 0; | ||
81 | } | ||
41 | 82 | ||
42 | switch (pm_disk_mode) { | 83 | /** |
43 | case PM_DISK_TEST: | 84 | * platform_finish - switch the machine to the normal mode of operation |
44 | case PM_DISK_TESTPROC: | 85 | * using the platform driver (must be called after platform_prepare()) |
45 | case PM_DISK_SHUTDOWN: | 86 | */ |
46 | case PM_DISK_REBOOT: | 87 | |
47 | break; | 88 | static void platform_finish(void) |
48 | default: | 89 | { |
49 | if (pm_ops && pm_ops->prepare) | 90 | if (hibernation_mode == HIBERNATION_PLATFORM && hibernation_ops) |
50 | error = pm_ops->prepare(PM_SUSPEND_DISK); | 91 | hibernation_ops->finish(); |
51 | } | ||
52 | return error; | ||
53 | } | 92 | } |
54 | 93 | ||
55 | /** | 94 | /** |
56 | * power_down - Shut machine down for hibernate. | 95 | * power_down - Shut the machine down for hibernation. |
57 | * | 96 | * |
58 | * Use the platform driver, if configured so; otherwise try | 97 | * Use the platform driver, if configured so; otherwise try |
59 | * to power off or reboot. | 98 | * to power off or reboot. |
@@ -61,20 +100,20 @@ static inline int platform_prepare(void) | |||
61 | 100 | ||
62 | static void power_down(void) | 101 | static void power_down(void) |
63 | { | 102 | { |
64 | switch (pm_disk_mode) { | 103 | switch (hibernation_mode) { |
65 | case PM_DISK_TEST: | 104 | case HIBERNATION_TEST: |
66 | case PM_DISK_TESTPROC: | 105 | case HIBERNATION_TESTPROC: |
67 | break; | 106 | break; |
68 | case PM_DISK_SHUTDOWN: | 107 | case HIBERNATION_SHUTDOWN: |
69 | kernel_power_off(); | 108 | kernel_power_off(); |
70 | break; | 109 | break; |
71 | case PM_DISK_REBOOT: | 110 | case HIBERNATION_REBOOT: |
72 | kernel_restart(NULL); | 111 | kernel_restart(NULL); |
73 | break; | 112 | break; |
74 | default: | 113 | case HIBERNATION_PLATFORM: |
75 | if (pm_ops && pm_ops->enter) { | 114 | if (hibernation_ops) { |
76 | kernel_shutdown_prepare(SYSTEM_SUSPEND_DISK); | 115 | kernel_shutdown_prepare(SYSTEM_SUSPEND_DISK); |
77 | pm_ops->enter(PM_SUSPEND_DISK); | 116 | hibernation_ops->enter(); |
78 | break; | 117 | break; |
79 | } | 118 | } |
80 | } | 119 | } |
@@ -87,20 +126,6 @@ static void power_down(void) | |||
87 | while(1); | 126 | while(1); |
88 | } | 127 | } |
89 | 128 | ||
90 | static inline void platform_finish(void) | ||
91 | { | ||
92 | switch (pm_disk_mode) { | ||
93 | case PM_DISK_TEST: | ||
94 | case PM_DISK_TESTPROC: | ||
95 | case PM_DISK_SHUTDOWN: | ||
96 | case PM_DISK_REBOOT: | ||
97 | break; | ||
98 | default: | ||
99 | if (pm_ops && pm_ops->finish) | ||
100 | pm_ops->finish(PM_SUSPEND_DISK); | ||
101 | } | ||
102 | } | ||
103 | |||
104 | static void unprepare_processes(void) | 129 | static void unprepare_processes(void) |
105 | { | 130 | { |
106 | thaw_processes(); | 131 | thaw_processes(); |
@@ -120,13 +145,10 @@ static int prepare_processes(void) | |||
120 | } | 145 | } |
121 | 146 | ||
122 | /** | 147 | /** |
123 | * pm_suspend_disk - The granpappy of hibernation power management. | 148 | * hibernate - The granpappy of the built-in hibernation management |
124 | * | ||
125 | * If not, then call swsusp to do its thing, then figure out how | ||
126 | * to power down the system. | ||
127 | */ | 149 | */ |
128 | 150 | ||
129 | int pm_suspend_disk(void) | 151 | int hibernate(void) |
130 | { | 152 | { |
131 | int error; | 153 | int error; |
132 | 154 | ||
@@ -143,7 +165,8 @@ int pm_suspend_disk(void) | |||
143 | if (error) | 165 | if (error) |
144 | goto Finish; | 166 | goto Finish; |
145 | 167 | ||
146 | if (pm_disk_mode == PM_DISK_TESTPROC) { | 168 | mutex_lock(&pm_mutex); |
169 | if (hibernation_mode == HIBERNATION_TESTPROC) { | ||
147 | printk("swsusp debug: Waiting for 5 seconds.\n"); | 170 | printk("swsusp debug: Waiting for 5 seconds.\n"); |
148 | mdelay(5000); | 171 | mdelay(5000); |
149 | goto Thaw; | 172 | goto Thaw; |
@@ -168,7 +191,7 @@ int pm_suspend_disk(void) | |||
168 | if (error) | 191 | if (error) |
169 | goto Enable_cpus; | 192 | goto Enable_cpus; |
170 | 193 | ||
171 | if (pm_disk_mode == PM_DISK_TEST) { | 194 | if (hibernation_mode == HIBERNATION_TEST) { |
172 | printk("swsusp debug: Waiting for 5 seconds.\n"); | 195 | printk("swsusp debug: Waiting for 5 seconds.\n"); |
173 | mdelay(5000); | 196 | mdelay(5000); |
174 | goto Enable_cpus; | 197 | goto Enable_cpus; |
@@ -205,6 +228,7 @@ int pm_suspend_disk(void) | |||
205 | device_resume(); | 228 | device_resume(); |
206 | resume_console(); | 229 | resume_console(); |
207 | Thaw: | 230 | Thaw: |
231 | mutex_unlock(&pm_mutex); | ||
208 | unprepare_processes(); | 232 | unprepare_processes(); |
209 | Finish: | 233 | Finish: |
210 | free_basic_memory_bitmaps(); | 234 | free_basic_memory_bitmaps(); |
@@ -220,7 +244,7 @@ int pm_suspend_disk(void) | |||
220 | * Called as a late_initcall (so all devices are discovered and | 244 | * Called as a late_initcall (so all devices are discovered and |
221 | * initialized), we call swsusp to see if we have a saved image or not. | 245 | * initialized), we call swsusp to see if we have a saved image or not. |
222 | * If so, we quiesce devices, the restore the saved image. We will | 246 | * If so, we quiesce devices, the restore the saved image. We will |
223 | * return above (in pm_suspend_disk() ) if everything goes well. | 247 | * return above (in hibernate() ) if everything goes well. |
224 | * Otherwise, we fail gracefully and return to the normally | 248 | * Otherwise, we fail gracefully and return to the normally |
225 | * scheduled program. | 249 | * scheduled program. |
226 | * | 250 | * |
@@ -315,25 +339,26 @@ static int software_resume(void) | |||
315 | late_initcall(software_resume); | 339 | late_initcall(software_resume); |
316 | 340 | ||
317 | 341 | ||
318 | static const char * const pm_disk_modes[] = { | 342 | static const char * const hibernation_modes[] = { |
319 | [PM_DISK_PLATFORM] = "platform", | 343 | [HIBERNATION_PLATFORM] = "platform", |
320 | [PM_DISK_SHUTDOWN] = "shutdown", | 344 | [HIBERNATION_SHUTDOWN] = "shutdown", |
321 | [PM_DISK_REBOOT] = "reboot", | 345 | [HIBERNATION_REBOOT] = "reboot", |
322 | [PM_DISK_TEST] = "test", | 346 | [HIBERNATION_TEST] = "test", |
323 | [PM_DISK_TESTPROC] = "testproc", | 347 | [HIBERNATION_TESTPROC] = "testproc", |
324 | }; | 348 | }; |
325 | 349 | ||
326 | /** | 350 | /** |
327 | * disk - Control suspend-to-disk mode | 351 | * disk - Control hibernation mode |
328 | * | 352 | * |
329 | * Suspend-to-disk can be handled in several ways. We have a few options | 353 | * Suspend-to-disk can be handled in several ways. We have a few options |
330 | * for putting the system to sleep - using the platform driver (e.g. ACPI | 354 | * for putting the system to sleep - using the platform driver (e.g. ACPI |
331 | * or other pm_ops), powering off the system or rebooting the system | 355 | * or other hibernation_ops), powering off the system or rebooting the |
332 | * (for testing) as well as the two test modes. | 356 | * system (for testing) as well as the two test modes. |
333 | * | 357 | * |
334 | * The system can support 'platform', and that is known a priori (and | 358 | * The system can support 'platform', and that is known a priori (and |
335 | * encoded in pm_ops). However, the user may choose 'shutdown' or 'reboot' | 359 | * encoded by the presence of hibernation_ops). However, the user may |
336 | * as alternatives, as well as the test modes 'test' and 'testproc'. | 360 | * choose 'shutdown' or 'reboot' as alternatives, as well as one fo the |
361 | * test modes, 'test' or 'testproc'. | ||
337 | * | 362 | * |
338 | * show() will display what the mode is currently set to. | 363 | * show() will display what the mode is currently set to. |
339 | * store() will accept one of | 364 | * store() will accept one of |
@@ -345,7 +370,7 @@ static const char * const pm_disk_modes[] = { | |||
345 | * 'testproc' | 370 | * 'testproc' |
346 | * | 371 | * |
347 | * It will only change to 'platform' if the system | 372 | * It will only change to 'platform' if the system |
348 | * supports it (as determined from pm_ops->pm_disk_mode). | 373 | * supports it (as determined by having hibernation_ops). |
349 | */ | 374 | */ |
350 | 375 | ||
351 | static ssize_t disk_show(struct kset *kset, char *buf) | 376 | static ssize_t disk_show(struct kset *kset, char *buf) |
@@ -353,28 +378,25 @@ static ssize_t disk_show(struct kset *kset, char *buf) | |||
353 | int i; | 378 | int i; |
354 | char *start = buf; | 379 | char *start = buf; |
355 | 380 | ||
356 | for (i = PM_DISK_PLATFORM; i < PM_DISK_MAX; i++) { | 381 | for (i = HIBERNATION_FIRST; i <= HIBERNATION_MAX; i++) { |
357 | if (!pm_disk_modes[i]) | 382 | if (!hibernation_modes[i]) |
358 | continue; | 383 | continue; |
359 | switch (i) { | 384 | switch (i) { |
360 | case PM_DISK_SHUTDOWN: | 385 | case HIBERNATION_SHUTDOWN: |
361 | case PM_DISK_REBOOT: | 386 | case HIBERNATION_REBOOT: |
362 | case PM_DISK_TEST: | 387 | case HIBERNATION_TEST: |
363 | case PM_DISK_TESTPROC: | 388 | case HIBERNATION_TESTPROC: |
364 | break; | 389 | break; |
365 | default: | 390 | case HIBERNATION_PLATFORM: |
366 | if (pm_ops && pm_ops->enter && | 391 | if (hibernation_ops) |
367 | (i == pm_ops->pm_disk_mode)) | ||
368 | break; | 392 | break; |
369 | /* not a valid mode, continue with loop */ | 393 | /* not a valid mode, continue with loop */ |
370 | continue; | 394 | continue; |
371 | } | 395 | } |
372 | if (i == pm_disk_mode) | 396 | if (i == hibernation_mode) |
373 | buf += sprintf(buf, "[%s]", pm_disk_modes[i]); | 397 | buf += sprintf(buf, "[%s] ", hibernation_modes[i]); |
374 | else | 398 | else |
375 | buf += sprintf(buf, "%s", pm_disk_modes[i]); | 399 | buf += sprintf(buf, "%s ", hibernation_modes[i]); |
376 | if (i+1 != PM_DISK_MAX) | ||
377 | buf += sprintf(buf, " "); | ||
378 | } | 400 | } |
379 | buf += sprintf(buf, "\n"); | 401 | buf += sprintf(buf, "\n"); |
380 | return buf-start; | 402 | return buf-start; |
@@ -387,39 +409,38 @@ static ssize_t disk_store(struct kset *kset, const char *buf, size_t n) | |||
387 | int i; | 409 | int i; |
388 | int len; | 410 | int len; |
389 | char *p; | 411 | char *p; |
390 | suspend_disk_method_t mode = 0; | 412 | int mode = HIBERNATION_INVALID; |
391 | 413 | ||
392 | p = memchr(buf, '\n', n); | 414 | p = memchr(buf, '\n', n); |
393 | len = p ? p - buf : n; | 415 | len = p ? p - buf : n; |
394 | 416 | ||
395 | mutex_lock(&pm_mutex); | 417 | mutex_lock(&pm_mutex); |
396 | for (i = PM_DISK_PLATFORM; i < PM_DISK_MAX; i++) { | 418 | for (i = HIBERNATION_FIRST; i <= HIBERNATION_MAX; i++) { |
397 | if (!strncmp(buf, pm_disk_modes[i], len)) { | 419 | if (!strncmp(buf, hibernation_modes[i], len)) { |
398 | mode = i; | 420 | mode = i; |
399 | break; | 421 | break; |
400 | } | 422 | } |
401 | } | 423 | } |
402 | if (mode) { | 424 | if (mode != HIBERNATION_INVALID) { |
403 | switch (mode) { | 425 | switch (mode) { |
404 | case PM_DISK_SHUTDOWN: | 426 | case HIBERNATION_SHUTDOWN: |
405 | case PM_DISK_REBOOT: | 427 | case HIBERNATION_REBOOT: |
406 | case PM_DISK_TEST: | 428 | case HIBERNATION_TEST: |
407 | case PM_DISK_TESTPROC: | 429 | case HIBERNATION_TESTPROC: |
408 | pm_disk_mode = mode; | 430 | hibernation_mode = mode; |
409 | break; | 431 | break; |
410 | default: | 432 | case HIBERNATION_PLATFORM: |
411 | if (pm_ops && pm_ops->enter && | 433 | if (hibernation_ops) |
412 | (mode == pm_ops->pm_disk_mode)) | 434 | hibernation_mode = mode; |
413 | pm_disk_mode = mode; | ||
414 | else | 435 | else |
415 | error = -EINVAL; | 436 | error = -EINVAL; |
416 | } | 437 | } |
417 | } else { | 438 | } else |
418 | error = -EINVAL; | 439 | error = -EINVAL; |
419 | } | ||
420 | 440 | ||
421 | pr_debug("PM: suspend-to-disk mode set to '%s'\n", | 441 | if (!error) |
422 | pm_disk_modes[mode]); | 442 | pr_debug("PM: suspend-to-disk mode set to '%s'\n", |
443 | hibernation_modes[mode]); | ||
423 | mutex_unlock(&pm_mutex); | 444 | mutex_unlock(&pm_mutex); |
424 | return error ? error : n; | 445 | return error ? error : n; |
425 | } | 446 | } |