diff options
Diffstat (limited to 'kernel/power/disk.c')
-rw-r--r-- | kernel/power/disk.c | 101 |
1 files changed, 78 insertions, 23 deletions
diff --git a/kernel/power/disk.c b/kernel/power/disk.c index ae6bbc903b7d..88fc5d7ac737 100644 --- a/kernel/power/disk.c +++ b/kernel/power/disk.c | |||
@@ -20,6 +20,7 @@ | |||
20 | #include <linux/pm.h> | 20 | #include <linux/pm.h> |
21 | #include <linux/console.h> | 21 | #include <linux/console.h> |
22 | #include <linux/cpu.h> | 22 | #include <linux/cpu.h> |
23 | #include <linux/freezer.h> | ||
23 | 24 | ||
24 | #include "power.h" | 25 | #include "power.h" |
25 | 26 | ||
@@ -27,6 +28,23 @@ | |||
27 | static int noresume = 0; | 28 | static int noresume = 0; |
28 | char resume_file[256] = CONFIG_PM_STD_PARTITION; | 29 | char resume_file[256] = CONFIG_PM_STD_PARTITION; |
29 | dev_t swsusp_resume_device; | 30 | dev_t swsusp_resume_device; |
31 | sector_t swsusp_resume_block; | ||
32 | |||
33 | /** | ||
34 | * platform_prepare - prepare the machine for hibernation using the | ||
35 | * platform driver if so configured and return an error code if it fails | ||
36 | */ | ||
37 | |||
38 | static inline int platform_prepare(void) | ||
39 | { | ||
40 | int error = 0; | ||
41 | |||
42 | if (pm_disk_mode == PM_DISK_PLATFORM) { | ||
43 | if (pm_ops && pm_ops->prepare) | ||
44 | error = pm_ops->prepare(PM_SUSPEND_DISK); | ||
45 | } | ||
46 | return error; | ||
47 | } | ||
30 | 48 | ||
31 | /** | 49 | /** |
32 | * power_down - Shut machine down for hibernate. | 50 | * power_down - Shut machine down for hibernate. |
@@ -40,13 +58,11 @@ dev_t swsusp_resume_device; | |||
40 | 58 | ||
41 | static void power_down(suspend_disk_method_t mode) | 59 | static void power_down(suspend_disk_method_t mode) |
42 | { | 60 | { |
43 | int error = 0; | ||
44 | |||
45 | switch(mode) { | 61 | switch(mode) { |
46 | case PM_DISK_PLATFORM: | 62 | case PM_DISK_PLATFORM: |
47 | if (pm_ops && pm_ops->enter) { | 63 | if (pm_ops && pm_ops->enter) { |
48 | kernel_shutdown_prepare(SYSTEM_SUSPEND_DISK); | 64 | kernel_shutdown_prepare(SYSTEM_SUSPEND_DISK); |
49 | error = pm_ops->enter(PM_SUSPEND_DISK); | 65 | pm_ops->enter(PM_SUSPEND_DISK); |
50 | break; | 66 | break; |
51 | } | 67 | } |
52 | case PM_DISK_SHUTDOWN: | 68 | case PM_DISK_SHUTDOWN: |
@@ -73,7 +89,7 @@ static inline void platform_finish(void) | |||
73 | 89 | ||
74 | static int prepare_processes(void) | 90 | static int prepare_processes(void) |
75 | { | 91 | { |
76 | int error; | 92 | int error = 0; |
77 | 93 | ||
78 | pm_prepare_console(); | 94 | pm_prepare_console(); |
79 | 95 | ||
@@ -86,12 +102,24 @@ static int prepare_processes(void) | |||
86 | goto thaw; | 102 | goto thaw; |
87 | } | 103 | } |
88 | 104 | ||
105 | if (pm_disk_mode == PM_DISK_TESTPROC) { | ||
106 | printk("swsusp debug: Waiting for 5 seconds.\n"); | ||
107 | mdelay(5000); | ||
108 | goto thaw; | ||
109 | } | ||
110 | |||
111 | error = platform_prepare(); | ||
112 | if (error) | ||
113 | goto thaw; | ||
114 | |||
89 | /* Free memory before shutting down devices. */ | 115 | /* Free memory before shutting down devices. */ |
90 | if (!(error = swsusp_shrink_memory())) | 116 | if (!(error = swsusp_shrink_memory())) |
91 | return 0; | 117 | return 0; |
92 | thaw: | 118 | |
119 | platform_finish(); | ||
120 | thaw: | ||
93 | thaw_processes(); | 121 | thaw_processes(); |
94 | enable_cpus: | 122 | enable_cpus: |
95 | enable_nonboot_cpus(); | 123 | enable_nonboot_cpus(); |
96 | pm_restore_console(); | 124 | pm_restore_console(); |
97 | return error; | 125 | return error; |
@@ -122,13 +150,21 @@ int pm_suspend_disk(void) | |||
122 | if (error) | 150 | if (error) |
123 | return error; | 151 | return error; |
124 | 152 | ||
153 | if (pm_disk_mode == PM_DISK_TESTPROC) | ||
154 | return 0; | ||
155 | |||
125 | suspend_console(); | 156 | suspend_console(); |
126 | error = device_suspend(PMSG_FREEZE); | 157 | error = device_suspend(PMSG_FREEZE); |
127 | if (error) { | 158 | if (error) { |
128 | resume_console(); | 159 | resume_console(); |
129 | printk("Some devices failed to suspend\n"); | 160 | printk("Some devices failed to suspend\n"); |
130 | unprepare_processes(); | 161 | goto Thaw; |
131 | return error; | 162 | } |
163 | |||
164 | if (pm_disk_mode == PM_DISK_TEST) { | ||
165 | printk("swsusp debug: Waiting for 5 seconds.\n"); | ||
166 | mdelay(5000); | ||
167 | goto Done; | ||
132 | } | 168 | } |
133 | 169 | ||
134 | pr_debug("PM: snapshotting memory.\n"); | 170 | pr_debug("PM: snapshotting memory.\n"); |
@@ -145,16 +181,17 @@ int pm_suspend_disk(void) | |||
145 | power_down(pm_disk_mode); | 181 | power_down(pm_disk_mode); |
146 | else { | 182 | else { |
147 | swsusp_free(); | 183 | swsusp_free(); |
148 | unprepare_processes(); | 184 | goto Thaw; |
149 | return error; | ||
150 | } | 185 | } |
151 | } else | 186 | } else { |
152 | pr_debug("PM: Image restored successfully.\n"); | 187 | pr_debug("PM: Image restored successfully.\n"); |
188 | } | ||
153 | 189 | ||
154 | swsusp_free(); | 190 | swsusp_free(); |
155 | Done: | 191 | Done: |
156 | device_resume(); | 192 | device_resume(); |
157 | resume_console(); | 193 | resume_console(); |
194 | Thaw: | ||
158 | unprepare_processes(); | 195 | unprepare_processes(); |
159 | return error; | 196 | return error; |
160 | } | 197 | } |
@@ -176,10 +213,10 @@ static int software_resume(void) | |||
176 | { | 213 | { |
177 | int error; | 214 | int error; |
178 | 215 | ||
179 | down(&pm_sem); | 216 | mutex_lock(&pm_mutex); |
180 | if (!swsusp_resume_device) { | 217 | if (!swsusp_resume_device) { |
181 | if (!strlen(resume_file)) { | 218 | if (!strlen(resume_file)) { |
182 | up(&pm_sem); | 219 | mutex_unlock(&pm_mutex); |
183 | return -ENOENT; | 220 | return -ENOENT; |
184 | } | 221 | } |
185 | swsusp_resume_device = name_to_dev_t(resume_file); | 222 | swsusp_resume_device = name_to_dev_t(resume_file); |
@@ -194,7 +231,7 @@ static int software_resume(void) | |||
194 | * FIXME: If noresume is specified, we need to find the partition | 231 | * FIXME: If noresume is specified, we need to find the partition |
195 | * and reset it back to normal swap space. | 232 | * and reset it back to normal swap space. |
196 | */ | 233 | */ |
197 | up(&pm_sem); | 234 | mutex_unlock(&pm_mutex); |
198 | return 0; | 235 | return 0; |
199 | } | 236 | } |
200 | 237 | ||
@@ -238,7 +275,7 @@ static int software_resume(void) | |||
238 | unprepare_processes(); | 275 | unprepare_processes(); |
239 | Done: | 276 | Done: |
240 | /* For success case, the suspend path will release the lock */ | 277 | /* For success case, the suspend path will release the lock */ |
241 | up(&pm_sem); | 278 | mutex_unlock(&pm_mutex); |
242 | pr_debug("PM: Resume from disk failed.\n"); | 279 | pr_debug("PM: Resume from disk failed.\n"); |
243 | return 0; | 280 | return 0; |
244 | } | 281 | } |
@@ -251,6 +288,8 @@ static const char * const pm_disk_modes[] = { | |||
251 | [PM_DISK_PLATFORM] = "platform", | 288 | [PM_DISK_PLATFORM] = "platform", |
252 | [PM_DISK_SHUTDOWN] = "shutdown", | 289 | [PM_DISK_SHUTDOWN] = "shutdown", |
253 | [PM_DISK_REBOOT] = "reboot", | 290 | [PM_DISK_REBOOT] = "reboot", |
291 | [PM_DISK_TEST] = "test", | ||
292 | [PM_DISK_TESTPROC] = "testproc", | ||
254 | }; | 293 | }; |
255 | 294 | ||
256 | /** | 295 | /** |
@@ -297,7 +336,7 @@ static ssize_t disk_store(struct subsystem * s, const char * buf, size_t n) | |||
297 | p = memchr(buf, '\n', n); | 336 | p = memchr(buf, '\n', n); |
298 | len = p ? p - buf : n; | 337 | len = p ? p - buf : n; |
299 | 338 | ||
300 | down(&pm_sem); | 339 | mutex_lock(&pm_mutex); |
301 | for (i = PM_DISK_FIRMWARE; i < PM_DISK_MAX; i++) { | 340 | for (i = PM_DISK_FIRMWARE; i < PM_DISK_MAX; i++) { |
302 | if (!strncmp(buf, pm_disk_modes[i], len)) { | 341 | if (!strncmp(buf, pm_disk_modes[i], len)) { |
303 | mode = i; | 342 | mode = i; |
@@ -305,21 +344,23 @@ static ssize_t disk_store(struct subsystem * s, const char * buf, size_t n) | |||
305 | } | 344 | } |
306 | } | 345 | } |
307 | if (mode) { | 346 | if (mode) { |
308 | if (mode == PM_DISK_SHUTDOWN || mode == PM_DISK_REBOOT) | 347 | if (mode == PM_DISK_SHUTDOWN || mode == PM_DISK_REBOOT || |
348 | mode == PM_DISK_TEST || mode == PM_DISK_TESTPROC) { | ||
309 | pm_disk_mode = mode; | 349 | pm_disk_mode = mode; |
310 | else { | 350 | } else { |
311 | if (pm_ops && pm_ops->enter && | 351 | if (pm_ops && pm_ops->enter && |
312 | (mode == pm_ops->pm_disk_mode)) | 352 | (mode == pm_ops->pm_disk_mode)) |
313 | pm_disk_mode = mode; | 353 | pm_disk_mode = mode; |
314 | else | 354 | else |
315 | error = -EINVAL; | 355 | error = -EINVAL; |
316 | } | 356 | } |
317 | } else | 357 | } else { |
318 | error = -EINVAL; | 358 | error = -EINVAL; |
359 | } | ||
319 | 360 | ||
320 | pr_debug("PM: suspend-to-disk mode set to '%s'\n", | 361 | pr_debug("PM: suspend-to-disk mode set to '%s'\n", |
321 | pm_disk_modes[mode]); | 362 | pm_disk_modes[mode]); |
322 | up(&pm_sem); | 363 | mutex_unlock(&pm_mutex); |
323 | return error ? error : n; | 364 | return error ? error : n; |
324 | } | 365 | } |
325 | 366 | ||
@@ -344,14 +385,14 @@ static ssize_t resume_store(struct subsystem *subsys, const char *buf, size_t n) | |||
344 | if (maj != MAJOR(res) || min != MINOR(res)) | 385 | if (maj != MAJOR(res) || min != MINOR(res)) |
345 | goto out; | 386 | goto out; |
346 | 387 | ||
347 | down(&pm_sem); | 388 | mutex_lock(&pm_mutex); |
348 | swsusp_resume_device = res; | 389 | swsusp_resume_device = res; |
349 | up(&pm_sem); | 390 | mutex_unlock(&pm_mutex); |
350 | printk("Attempting manual resume\n"); | 391 | printk("Attempting manual resume\n"); |
351 | noresume = 0; | 392 | noresume = 0; |
352 | software_resume(); | 393 | software_resume(); |
353 | ret = n; | 394 | ret = n; |
354 | out: | 395 | out: |
355 | return ret; | 396 | return ret; |
356 | } | 397 | } |
357 | 398 | ||
@@ -406,6 +447,19 @@ static int __init resume_setup(char *str) | |||
406 | return 1; | 447 | return 1; |
407 | } | 448 | } |
408 | 449 | ||
450 | static int __init resume_offset_setup(char *str) | ||
451 | { | ||
452 | unsigned long long offset; | ||
453 | |||
454 | if (noresume) | ||
455 | return 1; | ||
456 | |||
457 | if (sscanf(str, "%llu", &offset) == 1) | ||
458 | swsusp_resume_block = offset; | ||
459 | |||
460 | return 1; | ||
461 | } | ||
462 | |||
409 | static int __init noresume_setup(char *str) | 463 | static int __init noresume_setup(char *str) |
410 | { | 464 | { |
411 | noresume = 1; | 465 | noresume = 1; |
@@ -413,4 +467,5 @@ static int __init noresume_setup(char *str) | |||
413 | } | 467 | } |
414 | 468 | ||
415 | __setup("noresume", noresume_setup); | 469 | __setup("noresume", noresume_setup); |
470 | __setup("resume_offset=", resume_offset_setup); | ||
416 | __setup("resume=", resume_setup); | 471 | __setup("resume=", resume_setup); |