Blame view
kernel/power/hibernate.c
30.1 KB
55716d264 treewide: Replace... |
1 |
// SPDX-License-Identifier: GPL-2.0-only |
1da177e4c Linux-2.6.12-rc2 |
2 |
/* |
8b759b84c PM/Hibernate: Ren... |
3 |
* kernel/power/hibernate.c - Hibernation (a.k.a suspend-to-disk) support. |
1da177e4c Linux-2.6.12-rc2 |
4 5 6 |
* * Copyright (c) 2003 Patrick Mochel * Copyright (c) 2003 Open Source Development Lab |
a2531293d update email address |
7 |
* Copyright (c) 2004 Pavel Machek <pavel@ucw.cz> |
8b759b84c PM/Hibernate: Ren... |
8 |
* Copyright (c) 2009 Rafael J. Wysocki, Novell Inc. |
62c552ccc PM / Hibernate: E... |
9 |
* Copyright (C) 2012 Bojan Smojver <bojan@rexursive.com> |
1da177e4c Linux-2.6.12-rc2 |
10 |
*/ |
7a7b99bf8 PM: hibernate: Ad... |
11 |
#define pr_fmt(fmt) "PM: hibernation: " fmt |
2872de138 PM / hibernate: D... |
12 |
|
6e5fdeedc kernel: Fix files... |
13 |
#include <linux/export.h> |
1da177e4c Linux-2.6.12-rc2 |
14 |
#include <linux/suspend.h> |
1da177e4c Linux-2.6.12-rc2 |
15 16 17 |
#include <linux/reboot.h> #include <linux/string.h> #include <linux/device.h> |
6f8d7022a PM / Hibernate: A... |
18 |
#include <linux/async.h> |
1da177e4c Linux-2.6.12-rc2 |
19 20 |
#include <linux/delay.h> #include <linux/fs.h> |
d53d9f16e [PATCH] name_to_d... |
21 |
#include <linux/mount.h> |
88d10bbaa [PATCH] suspend: ... |
22 |
#include <linux/pm.h> |
38b8d208a sched/headers: Pr... |
23 |
#include <linux/nmi.h> |
97c7801cd [PATCH] swsusp: U... |
24 |
#include <linux/console.h> |
e3920fb42 [PATCH] Disable C... |
25 |
#include <linux/cpu.h> |
7dfb71030 [PATCH] Add inclu... |
26 |
#include <linux/freezer.h> |
5a0e3ad6a include cleanup: ... |
27 |
#include <linux/gfp.h> |
40dc166cb PM / Core: Introd... |
28 |
#include <linux/syscore_ops.h> |
2df83fa4b PM / Hibernate: U... |
29 30 |
#include <linux/ctype.h> #include <linux/genhd.h> |
db5976058 PM / Hibernate: M... |
31 |
#include <linux/ktime.h> |
38bd94b8a hibernate: Disabl... |
32 |
#include <linux/security.h> |
bb3632c61 PM / sleep: trace... |
33 |
#include <trace/events/power.h> |
d53d9f16e [PATCH] name_to_d... |
34 |
|
1da177e4c Linux-2.6.12-rc2 |
35 |
#include "power.h" |
d231ff1af PM / Hibernate: D... |
36 37 |
static int nocompress; static int noresume; |
a6e15a390 PM / hibernate: i... |
38 |
static int nohibernate; |
d231ff1af PM / Hibernate: D... |
39 |
static int resume_wait; |
f6514be5f PM / hibernate: F... |
40 |
static unsigned int resume_delay; |
47a460d5a kernel/power/disk... |
41 |
static char resume_file[256] = CONFIG_PM_STD_PARTITION; |
1da177e4c Linux-2.6.12-rc2 |
42 |
dev_t swsusp_resume_device; |
9a154d9d9 [PATCH] swsusp: a... |
43 |
sector_t swsusp_resume_block; |
d6efc2f72 x86, asmlinkage, ... |
44 |
__visible int in_suspend __nosavedata; |
1da177e4c Linux-2.6.12-rc2 |
45 |
|
a3d25c275 PM: Separate hibe... |
46 47 48 |
enum { HIBERNATION_INVALID, HIBERNATION_PLATFORM, |
a3d25c275 PM: Separate hibe... |
49 50 |
HIBERNATION_SHUTDOWN, HIBERNATION_REBOOT, |
62c552ccc PM / Hibernate: E... |
51 52 53 |
#ifdef CONFIG_SUSPEND HIBERNATION_SUSPEND, #endif |
fe12c00d2 PM / hibernate: I... |
54 |
HIBERNATION_TEST_RESUME, |
a3d25c275 PM: Separate hibe... |
55 56 57 58 59 60 61 |
/* keep last */ __HIBERNATION_AFTER_LAST }; #define HIBERNATION_MAX (__HIBERNATION_AFTER_LAST-1) #define HIBERNATION_FIRST (HIBERNATION_INVALID + 1) static int hibernation_mode = HIBERNATION_SHUTDOWN; |
97819a262 PM / Hibernate: T... |
62 |
bool freezer_test_done; |
aa9a7b118 PM / Hibernate: F... |
63 |
|
073ef1f6e hibernation: cons... |
64 |
static const struct platform_hibernation_ops *hibernation_ops; |
a3d25c275 PM: Separate hibe... |
65 |
|
ab7e9b067 PM: hibernate: In... |
66 67 68 69 70 71 72 73 74 75 76 |
static atomic_t hibernate_atomic = ATOMIC_INIT(1); bool hibernate_acquire(void) { return atomic_add_unless(&hibernate_atomic, -1, 0); } void hibernate_release(void) { atomic_inc(&hibernate_atomic); } |
a6e15a390 PM / hibernate: i... |
77 78 |
bool hibernation_available(void) { |
38bd94b8a hibernate: Disabl... |
79 |
return nohibernate == 0 && !security_locked_down(LOCKDOWN_HIBERNATION); |
a6e15a390 PM / hibernate: i... |
80 |
} |
a3d25c275 PM: Separate hibe... |
81 |
/** |
f42a9813f PM / Hibernate: U... |
82 83 |
* hibernation_set_ops - Set the global hibernate operations. * @ops: Hibernation operations to use in subsequent hibernation transitions. |
a3d25c275 PM: Separate hibe... |
84 |
*/ |
073ef1f6e hibernation: cons... |
85 |
void hibernation_set_ops(const struct platform_hibernation_ops *ops) |
a3d25c275 PM: Separate hibe... |
86 |
{ |
caea99ef3 Hibernation: Intr... |
87 88 |
if (ops && !(ops->begin && ops->end && ops->pre_snapshot && ops->prepare && ops->finish && ops->enter && ops->pre_restore |
5729c63a5 PM / Hibernate: h... |
89 |
&& ops->restore_cleanup && ops->leave)) { |
a3d25c275 PM: Separate hibe... |
90 91 92 |
WARN_ON(1); return; } |
bcda53faf PM / Sleep: Repla... |
93 |
lock_system_sleep(); |
a3d25c275 PM: Separate hibe... |
94 95 96 97 98 |
hibernation_ops = ops; if (ops) hibernation_mode = HIBERNATION_PLATFORM; else if (hibernation_mode == HIBERNATION_PLATFORM) hibernation_mode = HIBERNATION_SHUTDOWN; |
bcda53faf PM / Sleep: Repla... |
99 |
unlock_system_sleep(); |
a3d25c275 PM: Separate hibe... |
100 |
} |
e0c7855e3 PM / hibernate: e... |
101 |
EXPORT_SYMBOL_GPL(hibernation_set_ops); |
a3d25c275 PM: Separate hibe... |
102 |
|
abfe2d7b9 Hibernation: Intr... |
103 104 105 106 107 108 109 |
static bool entering_platform_hibernation; bool system_entering_hibernation(void) { return entering_platform_hibernation; } EXPORT_SYMBOL(system_entering_hibernation); |
4cc79776c Hibernation: New ... |
110 111 112 |
#ifdef CONFIG_PM_DEBUG static void hibernation_debug_sleep(void) { |
7a7b99bf8 PM: hibernate: Ad... |
113 114 |
pr_info("debug: Waiting for 5 seconds. "); |
4cc79776c Hibernation: New ... |
115 116 |
mdelay(5000); } |
4cc79776c Hibernation: New ... |
117 118 119 120 121 122 123 124 125 |
static int hibernation_test(int level) { if (pm_test_level == level) { hibernation_debug_sleep(); return 1; } return 0; } #else /* !CONFIG_PM_DEBUG */ |
4cc79776c Hibernation: New ... |
126 127 |
static int hibernation_test(int level) { return 0; } #endif /* !CONFIG_PM_DEBUG */ |
74f270af0 PM: Rework struct... |
128 |
/** |
f42a9813f PM / Hibernate: U... |
129 130 |
* platform_begin - Call platform to start hibernation. * @platform_mode: Whether or not to use the platform driver. |
74f270af0 PM: Rework struct... |
131 |
*/ |
caea99ef3 Hibernation: Intr... |
132 |
static int platform_begin(int platform_mode) |
74f270af0 PM: Rework struct... |
133 134 |
{ return (platform_mode && hibernation_ops) ? |
bb1869012 ACPI: PM: Call pm... |
135 |
hibernation_ops->begin(PMSG_FREEZE) : 0; |
caea99ef3 Hibernation: Intr... |
136 137 138 |
} /** |
f42a9813f PM / Hibernate: U... |
139 140 |
* platform_end - Call platform to finish transition to the working state. * @platform_mode: Whether or not to use the platform driver. |
caea99ef3 Hibernation: Intr... |
141 |
*/ |
caea99ef3 Hibernation: Intr... |
142 143 144 145 |
static void platform_end(int platform_mode) { if (platform_mode && hibernation_ops) hibernation_ops->end(); |
74f270af0 PM: Rework struct... |
146 |
} |
a3d25c275 PM: Separate hibe... |
147 |
|
1da177e4c Linux-2.6.12-rc2 |
148 |
/** |
f42a9813f PM / Hibernate: U... |
149 150 151 152 153 |
* platform_pre_snapshot - Call platform to prepare the machine for hibernation. * @platform_mode: Whether or not to use the platform driver. * * Use the platform driver to prepare the system for creating a hibernate image, * if so configured, and return an error code if that fails. |
8a05aac26 [PATCH] swsusp: f... |
154 |
*/ |
74f270af0 PM: Rework struct... |
155 |
static int platform_pre_snapshot(int platform_mode) |
8a05aac26 [PATCH] swsusp: f... |
156 |
{ |
7777fab98 swsusp: remove co... |
157 |
return (platform_mode && hibernation_ops) ? |
74f270af0 PM: Rework struct... |
158 |
hibernation_ops->pre_snapshot() : 0; |
a3d25c275 PM: Separate hibe... |
159 |
} |
8a05aac26 [PATCH] swsusp: f... |
160 |
|
a3d25c275 PM: Separate hibe... |
161 |
/** |
f42a9813f PM / Hibernate: U... |
162 163 164 165 166 167 168 |
* platform_leave - Call platform to prepare a transition to the working state. * @platform_mode: Whether or not to use the platform driver. * * Use the platform driver prepare to prepare the machine for switching to the * normal mode of operation. * * This routine is called on one CPU with interrupts disabled. |
c7e0831d3 Hibernation: Chec... |
169 |
*/ |
c7e0831d3 Hibernation: Chec... |
170 171 172 173 174 175 176 |
static void platform_leave(int platform_mode) { if (platform_mode && hibernation_ops) hibernation_ops->leave(); } /** |
f42a9813f PM / Hibernate: U... |
177 178 179 180 181 182 183 |
* platform_finish - Call platform to switch the system to the working state. * @platform_mode: Whether or not to use the platform driver. * * Use the platform driver to switch the machine to the normal mode of * operation. * * This routine must be called after platform_prepare(). |
a3d25c275 PM: Separate hibe... |
184 |
*/ |
7777fab98 swsusp: remove co... |
185 |
static void platform_finish(int platform_mode) |
a3d25c275 PM: Separate hibe... |
186 |
{ |
7777fab98 swsusp: remove co... |
187 |
if (platform_mode && hibernation_ops) |
a3d25c275 PM: Separate hibe... |
188 |
hibernation_ops->finish(); |
8a05aac26 [PATCH] swsusp: f... |
189 190 191 |
} /** |
f42a9813f PM / Hibernate: U... |
192 193 194 195 196 197 198 199 |
* platform_pre_restore - Prepare for hibernate image restoration. * @platform_mode: Whether or not to use the platform driver. * * Use the platform driver to prepare the system for resume from a hibernation * image. * * If the restore fails after this function has been called, * platform_restore_cleanup() must be called. |
a634cc101 swsusp: introduce... |
200 |
*/ |
a634cc101 swsusp: introduce... |
201 202 203 204 205 206 207 |
static int platform_pre_restore(int platform_mode) { return (platform_mode && hibernation_ops) ? hibernation_ops->pre_restore() : 0; } /** |
f42a9813f PM / Hibernate: U... |
208 209 210 211 212 213 214 215 216 |
* platform_restore_cleanup - Switch to the working state after failing restore. * @platform_mode: Whether or not to use the platform driver. * * Use the platform driver to switch the system to the normal mode of operation * after a failing restore. * * If platform_pre_restore() has been called before the failing restore, this * function must be called too, regardless of the result of * platform_pre_restore(). |
a634cc101 swsusp: introduce... |
217 |
*/ |
a634cc101 swsusp: introduce... |
218 219 220 221 222 223 224 |
static void platform_restore_cleanup(int platform_mode) { if (platform_mode && hibernation_ops) hibernation_ops->restore_cleanup(); } /** |
f42a9813f PM / Hibernate: U... |
225 226 |
* platform_recover - Recover from a failure to suspend devices. * @platform_mode: Whether or not to use the platform driver. |
d8f3de0d2 Suspend-related p... |
227 |
*/ |
d8f3de0d2 Suspend-related p... |
228 229 230 231 232 233 234 |
static void platform_recover(int platform_mode) { if (platform_mode && hibernation_ops && hibernation_ops->recover) hibernation_ops->recover(); } /** |
f42a9813f PM / Hibernate: U... |
235 236 237 238 239 |
* swsusp_show_speed - Print time elapsed between two events during hibernation. * @start: Starting event. * @stop: Final event. * @nr_pages: Number of memory pages processed between @start and @stop. * @msg: Additional diagnostic message to print. |
8e60c6a13 PM / Hibernate: S... |
240 |
*/ |
db5976058 PM / Hibernate: M... |
241 242 |
void swsusp_show_speed(ktime_t start, ktime_t stop, unsigned nr_pages, char *msg) |
8e60c6a13 PM / Hibernate: S... |
243 |
{ |
db5976058 PM / Hibernate: M... |
244 |
ktime_t diff; |
4881f603d PM / hibernate: u... |
245 246 247 248 |
u64 elapsed_centisecs64; unsigned int centisecs; unsigned int k; unsigned int kps; |
8e60c6a13 PM / Hibernate: S... |
249 |
|
db5976058 PM / Hibernate: M... |
250 251 |
diff = ktime_sub(stop, start); elapsed_centisecs64 = ktime_divns(diff, 10*NSEC_PER_MSEC); |
8e60c6a13 PM / Hibernate: S... |
252 253 254 255 256 |
centisecs = elapsed_centisecs64; if (centisecs == 0) centisecs = 1; /* avoid div-by-zero */ k = nr_pages * (PAGE_SIZE / 1024); kps = (k * 100) / centisecs; |
2872de138 PM / hibernate: D... |
257 258 259 260 |
pr_info("%s %u kbytes in %u.%02u seconds (%u.%02u MB/s) ", msg, k, centisecs / 100, centisecs % 100, kps / 1000, (kps % 1000) / 10); |
8e60c6a13 PM / Hibernate: S... |
261 |
} |
ec527c318 x86/power: Fix 'n... |
262 263 264 265 |
__weak int arch_resume_nosmt(void) { return 0; } |
8e60c6a13 PM / Hibernate: S... |
266 |
/** |
f42a9813f PM / Hibernate: U... |
267 268 269 |
* create_image - Create a hibernation image. * @platform_mode: Whether or not to use the platform driver. * |
cf579dfb8 PM / Sleep: Intro... |
270 271 |
* Execute device drivers' "late" and "noirq" freeze callbacks, create a * hibernation image and run the drivers' "noirq" and "early" thaw callbacks. |
f42a9813f PM / Hibernate: U... |
272 273 |
* * Control reappears in this routine after the subsequent restore. |
c7e0831d3 Hibernation: Chec... |
274 |
*/ |
47a460d5a kernel/power/disk... |
275 |
static int create_image(int platform_mode) |
c7e0831d3 Hibernation: Chec... |
276 277 |
{ int error; |
cf579dfb8 PM / Sleep: Intro... |
278 |
error = dpm_suspend_end(PMSG_FREEZE); |
c7e0831d3 Hibernation: Chec... |
279 |
if (error) { |
7a7b99bf8 PM: hibernate: Ad... |
280 281 |
pr_err("Some devices failed to power down, aborting "); |
32bdfac54 PM: Do not hold d... |
282 |
return error; |
c7e0831d3 Hibernation: Chec... |
283 |
} |
2ed8d2b3a PM: Rework handli... |
284 |
|
4aecd6718 PM: Change hibern... |
285 286 287 |
error = platform_pre_snapshot(platform_mode); if (error || hibernation_test(TEST_PLATFORM)) goto Platform_finish; |
2f1a6fbbe power/suspend: Ad... |
288 |
error = suspend_disable_secondary_cpus(); |
48580ab87 PM / Hibernate: R... |
289 |
if (error || hibernation_test(TEST_CPUS)) |
4aecd6718 PM: Change hibern... |
290 |
goto Enable_cpus; |
2ed8d2b3a PM: Rework handli... |
291 |
local_irq_disable(); |
c1a957d17 PM / suspend: Pre... |
292 |
system_state = SYSTEM_SUSPEND; |
2e711c04d PM: Remove sysdev... |
293 |
error = syscore_suspend(); |
770824bdc PM: Split up sysd... |
294 |
if (error) { |
7a7b99bf8 PM: hibernate: Ad... |
295 296 |
pr_err("Some system devices failed to power down, aborting "); |
4aecd6718 PM: Change hibern... |
297 |
goto Enable_irqs; |
770824bdc PM: Split up sysd... |
298 |
} |
c7e0831d3 Hibernation: Chec... |
299 |
|
a2867e08c PM / Wakeup: Repl... |
300 |
if (hibernation_test(TEST_CORE) || pm_wakeup_pending()) |
4cc79776c Hibernation: New ... |
301 302 303 |
goto Power_up; in_suspend = 1; |
c7e0831d3 Hibernation: Chec... |
304 |
save_processor_state(); |
bb3632c61 PM / sleep: trace... |
305 |
trace_suspend_resume(TPS("machine_suspend"), PM_EVENT_HIBERNATE, true); |
c7e0831d3 Hibernation: Chec... |
306 |
error = swsusp_arch_suspend(); |
62822e2ec PM / hibernate: R... |
307 308 |
/* Restore control flow magically appears here */ restore_processor_state(); |
bb3632c61 PM / sleep: trace... |
309 |
trace_suspend_resume(TPS("machine_suspend"), PM_EVENT_HIBERNATE, false); |
c7e0831d3 Hibernation: Chec... |
310 |
if (error) |
7a7b99bf8 PM: hibernate: Ad... |
311 312 |
pr_err("Error %d creating image ", error); |
2872de138 PM / hibernate: D... |
313 |
|
1ad1410f6 PM / Hibernate: a... |
314 |
if (!in_suspend) { |
c125e96f0 PM: Make it possi... |
315 |
events_check_enabled = false; |
1ad1410f6 PM / Hibernate: a... |
316 317 |
clear_free_pages(); } |
362e77d1c PM / hibernate: C... |
318 319 |
platform_leave(platform_mode); |
4aecd6718 PM: Change hibern... |
320 |
|
4cc79776c Hibernation: New ... |
321 |
Power_up: |
40dc166cb PM / Core: Introd... |
322 |
syscore_resume(); |
2ed8d2b3a PM: Rework handli... |
323 |
|
4aecd6718 PM: Change hibern... |
324 |
Enable_irqs: |
c1a957d17 PM / suspend: Pre... |
325 |
system_state = SYSTEM_RUNNING; |
2ed8d2b3a PM: Rework handli... |
326 |
local_irq_enable(); |
4aecd6718 PM: Change hibern... |
327 |
Enable_cpus: |
2f1a6fbbe power/suspend: Ad... |
328 |
suspend_enable_secondary_cpus(); |
4aecd6718 PM: Change hibern... |
329 |
|
ec527c318 x86/power: Fix 'n... |
330 331 332 |
/* Allow architectures to do nosmt-specific post-resume dances */ if (!in_suspend) error = arch_resume_nosmt(); |
4aecd6718 PM: Change hibern... |
333 334 |
Platform_finish: platform_finish(platform_mode); |
cf579dfb8 PM / Sleep: Intro... |
335 |
dpm_resume_start(in_suspend ? |
1eede070a Introduce new top... |
336 |
(error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE); |
2ed8d2b3a PM: Rework handli... |
337 |
|
c7e0831d3 Hibernation: Chec... |
338 339 340 341 |
return error; } /** |
f42a9813f PM / Hibernate: U... |
342 343 |
* hibernation_snapshot - Quiesce devices and create a hibernation image. * @platform_mode: If set, use platform driver to prepare for the transition. |
7777fab98 swsusp: remove co... |
344 |
* |
55f2503c3 PM / reboot: Elim... |
345 |
* This routine must be called with system_transition_mutex held. |
7777fab98 swsusp: remove co... |
346 |
*/ |
7777fab98 swsusp: remove co... |
347 348 |
int hibernation_snapshot(int platform_mode) { |
953a20639 PM / Hibernate: R... |
349 |
pm_message_t msg; |
cbe2f5a6e tracing: allow tr... |
350 |
int error; |
7777fab98 swsusp: remove co... |
351 |
|
276142730 PM / sleep: Clear... |
352 |
pm_suspend_clear_flags(); |
3fe0313e6 Hibernate: Call p... |
353 |
error = platform_begin(platform_mode); |
7777fab98 swsusp: remove co... |
354 |
if (error) |
d074ee023 PM / Hibernate: F... |
355 |
goto Close; |
7777fab98 swsusp: remove co... |
356 |
|
64a473cb7 PM/Hibernate: Do ... |
357 358 |
/* Preallocate image memory before shutting down devices. */ error = hibernate_preallocate_memory(); |
74f270af0 PM: Rework struct... |
359 |
if (error) |
2aede851d PM / Hibernate: F... |
360 361 362 363 |
goto Close; error = freeze_kernel_threads(); if (error) |
bb58dd5d1 PM / Hibernate: D... |
364 |
goto Cleanup; |
2aede851d PM / Hibernate: F... |
365 |
|
48580ab87 PM / Hibernate: R... |
366 |
if (hibernation_test(TEST_FREEZER)) { |
aa9a7b118 PM / Hibernate: F... |
367 368 369 370 371 372 |
/* * Indicate to the caller that we are returning due to a * successful freezer test. */ freezer_test_done = true; |
51d6ff7ac PM / Hibernate: T... |
373 |
goto Thaw; |
aa9a7b118 PM / Hibernate: F... |
374 |
} |
2aede851d PM / Hibernate: F... |
375 |
error = dpm_prepare(PMSG_FREEZE); |
bb58dd5d1 PM / Hibernate: D... |
376 |
if (error) { |
953a20639 PM / Hibernate: R... |
377 |
dpm_complete(PMSG_RECOVER); |
51d6ff7ac PM / Hibernate: T... |
378 |
goto Thaw; |
bb58dd5d1 PM / Hibernate: D... |
379 |
} |
74f270af0 PM: Rework struct... |
380 |
|
7777fab98 swsusp: remove co... |
381 |
suspend_console(); |
c9e664f1f PM / Hibernate: F... |
382 |
pm_restrict_gfp_mask(); |
953a20639 PM / Hibernate: R... |
383 |
|
91e7c75ba PM: Allow drivers... |
384 |
error = dpm_suspend(PMSG_FREEZE); |
10a1803d6 swsusp: fix hiber... |
385 |
|
953a20639 PM / Hibernate: R... |
386 387 388 389 |
if (error || hibernation_test(TEST_DEVICES)) platform_recover(platform_mode); else error = create_image(platform_mode); |
7777fab98 swsusp: remove co... |
390 |
|
c9e664f1f PM / Hibernate: F... |
391 |
/* |
953a20639 PM / Hibernate: R... |
392 393 |
* In the case that we call create_image() above, the control * returns here (1) after the image has been created or the |
c9e664f1f PM / Hibernate: F... |
394 395 |
* image creation has failed and (2) after a successful restore. */ |
4cc79776c Hibernation: New ... |
396 |
|
64a473cb7 PM/Hibernate: Do ... |
397 398 399 |
/* We may need to release the preallocated image pages here. */ if (error || !in_suspend) swsusp_free(); |
91e7c75ba PM: Allow drivers... |
400 401 |
msg = in_suspend ? (error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE; dpm_resume(msg); |
c9e664f1f PM / Hibernate: F... |
402 403 404 |
if (error || !in_suspend) pm_restore_gfp_mask(); |
7777fab98 swsusp: remove co... |
405 |
resume_console(); |
91e7c75ba PM: Allow drivers... |
406 |
dpm_complete(msg); |
caea99ef3 Hibernation: Intr... |
407 408 |
Close: platform_end(platform_mode); |
7777fab98 swsusp: remove co... |
409 |
return error; |
d8f3de0d2 Suspend-related p... |
410 |
|
51d6ff7ac PM / Hibernate: T... |
411 412 |
Thaw: thaw_kernel_threads(); |
bb58dd5d1 PM / Hibernate: D... |
413 414 415 |
Cleanup: swsusp_free(); goto Close; |
7777fab98 swsusp: remove co... |
416 |
} |
406f992e4 x86 / hibernate: ... |
417 418 |
int __weak hibernate_resume_nonboot_cpu_disable(void) { |
2f1a6fbbe power/suspend: Ad... |
419 |
return suspend_disable_secondary_cpus(); |
406f992e4 x86 / hibernate: ... |
420 |
} |
7777fab98 swsusp: remove co... |
421 |
/** |
f42a9813f PM / Hibernate: U... |
422 423 424 |
* resume_target_kernel - Restore system state from a hibernation image. * @platform_mode: Whether or not to use the platform driver. * |
cf579dfb8 PM / Sleep: Intro... |
425 426 427 428 |
* Execute device drivers' "noirq" and "late" freeze callbacks, restore the * contents of highmem that have not been restored yet from the image and run * the low-level code that will restore the remaining contents of memory and * switch to the just restored target kernel. |
72df68ca8 Hibernation: Move... |
429 |
*/ |
4aecd6718 PM: Change hibern... |
430 |
static int resume_target_kernel(bool platform_mode) |
72df68ca8 Hibernation: Move... |
431 432 |
{ int error; |
cf579dfb8 PM / Sleep: Intro... |
433 |
error = dpm_suspend_end(PMSG_QUIESCE); |
72df68ca8 Hibernation: Move... |
434 |
if (error) { |
2872de138 PM / hibernate: D... |
435 436 |
pr_err("Some devices failed to power down, aborting resume "); |
32bdfac54 PM: Do not hold d... |
437 |
return error; |
72df68ca8 Hibernation: Move... |
438 |
} |
2ed8d2b3a PM: Rework handli... |
439 |
|
4aecd6718 PM: Change hibern... |
440 441 442 |
error = platform_pre_restore(platform_mode); if (error) goto Cleanup; |
406f992e4 x86 / hibernate: ... |
443 |
error = hibernate_resume_nonboot_cpu_disable(); |
4aecd6718 PM: Change hibern... |
444 445 |
if (error) goto Enable_cpus; |
2ed8d2b3a PM: Rework handli... |
446 |
local_irq_disable(); |
c1a957d17 PM / suspend: Pre... |
447 |
system_state = SYSTEM_SUSPEND; |
2ed8d2b3a PM: Rework handli... |
448 |
|
2e711c04d PM: Remove sysdev... |
449 |
error = syscore_suspend(); |
4aecd6718 PM: Change hibern... |
450 451 |
if (error) goto Enable_irqs; |
72df68ca8 Hibernation: Move... |
452 453 454 455 456 457 |
save_processor_state(); error = restore_highmem(); if (!error) { error = swsusp_arch_resume(); /* * The code below is only ever reached in case of a failure. |
4e2d9491a PM / Hibernate: U... |
458 459 |
* Otherwise, execution continues at the place where * swsusp_arch_suspend() was called. |
72df68ca8 Hibernation: Move... |
460 461 |
*/ BUG_ON(!error); |
4e2d9491a PM / Hibernate: U... |
462 463 464 465 |
/* * This call to restore_highmem() reverts the changes made by * the previous one. */ |
72df68ca8 Hibernation: Move... |
466 467 468 469 470 |
restore_highmem(); } /* * The only reason why swsusp_arch_resume() can fail is memory being * very tight, so we have to free it as soon as we can to avoid |
4e2d9491a PM / Hibernate: U... |
471 |
* subsequent failures. |
72df68ca8 Hibernation: Move... |
472 473 474 475 |
*/ swsusp_free(); restore_processor_state(); touch_softlockup_watchdog(); |
2ed8d2b3a PM: Rework handli... |
476 |
|
40dc166cb PM / Core: Introd... |
477 |
syscore_resume(); |
2ed8d2b3a PM: Rework handli... |
478 |
|
4aecd6718 PM: Change hibern... |
479 |
Enable_irqs: |
c1a957d17 PM / suspend: Pre... |
480 |
system_state = SYSTEM_RUNNING; |
72df68ca8 Hibernation: Move... |
481 |
local_irq_enable(); |
2ed8d2b3a PM: Rework handli... |
482 |
|
4aecd6718 PM: Change hibern... |
483 |
Enable_cpus: |
2f1a6fbbe power/suspend: Ad... |
484 |
suspend_enable_secondary_cpus(); |
4aecd6718 PM: Change hibern... |
485 486 487 |
Cleanup: platform_restore_cleanup(platform_mode); |
cf579dfb8 PM / Sleep: Intro... |
488 |
dpm_resume_start(PMSG_RECOVER); |
2ed8d2b3a PM: Rework handli... |
489 |
|
72df68ca8 Hibernation: Move... |
490 491 492 493 |
return error; } /** |
f42a9813f PM / Hibernate: U... |
494 495 |
* hibernation_restore - Quiesce devices and restore from a hibernation image. * @platform_mode: If set, use platform driver to prepare for the transition. |
7777fab98 swsusp: remove co... |
496 |
* |
55f2503c3 PM / reboot: Elim... |
497 498 499 |
* This routine must be called with system_transition_mutex held. If it is * successful, control reappears in the restored target kernel in * hibernation_snapshot(). |
7777fab98 swsusp: remove co... |
500 |
*/ |
a634cc101 swsusp: introduce... |
501 |
int hibernation_restore(int platform_mode) |
7777fab98 swsusp: remove co... |
502 |
{ |
cbe2f5a6e tracing: allow tr... |
503 |
int error; |
7777fab98 swsusp: remove co... |
504 505 506 |
pm_prepare_console(); suspend_console(); |
c9e664f1f PM / Hibernate: F... |
507 |
pm_restrict_gfp_mask(); |
d16163029 PM core: rename s... |
508 |
error = dpm_suspend_start(PMSG_QUIESCE); |
a634cc101 swsusp: introduce... |
509 |
if (!error) { |
4aecd6718 PM: Change hibern... |
510 |
error = resume_target_kernel(platform_mode); |
94fb823fc PM / Sleep: fix r... |
511 512 513 514 515 516 |
/* * The above should either succeed and jump to the new kernel, * or return with an error. Otherwise things are just * undefined, so let's be paranoid. */ BUG_ON(!error); |
a634cc101 swsusp: introduce... |
517 |
} |
94fb823fc PM / Sleep: fix r... |
518 |
dpm_resume_end(PMSG_RECOVER); |
c9e664f1f PM / Hibernate: F... |
519 |
pm_restore_gfp_mask(); |
7777fab98 swsusp: remove co... |
520 521 522 523 524 525 |
resume_console(); pm_restore_console(); return error; } /** |
f42a9813f PM / Hibernate: U... |
526 |
* hibernation_platform_enter - Power off the system using the platform driver. |
7777fab98 swsusp: remove co... |
527 |
*/ |
7777fab98 swsusp: remove co... |
528 529 |
int hibernation_platform_enter(void) { |
cbe2f5a6e tracing: allow tr... |
530 |
int error; |
b1457bcc3 Hibernation: prep... |
531 |
|
9cd9a0058 Hibernation: Ente... |
532 533 534 535 536 537 538 539 |
if (!hibernation_ops) return -ENOSYS; /* * We have cancelled the power transition by running * hibernation_ops->finish() before saving the image, so we should let * the firmware know that we're going to enter the sleep state after all */ |
bb1869012 ACPI: PM: Call pm... |
540 |
error = hibernation_ops->begin(PMSG_HIBERNATE); |
9cd9a0058 Hibernation: Ente... |
541 |
if (error) |
caea99ef3 Hibernation: Intr... |
542 |
goto Close; |
9cd9a0058 Hibernation: Ente... |
543 |
|
abfe2d7b9 Hibernation: Intr... |
544 |
entering_platform_hibernation = true; |
9cd9a0058 Hibernation: Ente... |
545 |
suspend_console(); |
d16163029 PM core: rename s... |
546 |
error = dpm_suspend_start(PMSG_HIBERNATE); |
d8f3de0d2 Suspend-related p... |
547 548 549 550 551 |
if (error) { if (hibernation_ops->recover) hibernation_ops->recover(); goto Resume_devices; } |
9cd9a0058 Hibernation: Ente... |
552 |
|
cf579dfb8 PM / Sleep: Intro... |
553 |
error = dpm_suspend_end(PMSG_HIBERNATE); |
4aecd6718 PM: Change hibern... |
554 |
if (error) |
32bdfac54 PM: Do not hold d... |
555 |
goto Resume_devices; |
4aecd6718 PM: Change hibern... |
556 |
|
9cd9a0058 Hibernation: Ente... |
557 558 |
error = hibernation_ops->prepare(); if (error) |
e681c9dd6 PM: Fix typo in l... |
559 |
goto Platform_finish; |
9cd9a0058 Hibernation: Ente... |
560 |
|
2f1a6fbbe power/suspend: Ad... |
561 |
error = suspend_disable_secondary_cpus(); |
9cd9a0058 Hibernation: Ente... |
562 |
if (error) |
8c506608c PM / hibernate: r... |
563 |
goto Enable_cpus; |
2ed8d2b3a PM: Rework handli... |
564 |
|
4aecd6718 PM: Change hibern... |
565 |
local_irq_disable(); |
c1a957d17 PM / suspend: Pre... |
566 |
system_state = SYSTEM_SUSPEND; |
40dc166cb PM / Core: Introd... |
567 |
syscore_suspend(); |
a2867e08c PM / Wakeup: Repl... |
568 |
if (pm_wakeup_pending()) { |
c125e96f0 PM: Make it possi... |
569 570 571 |
error = -EAGAIN; goto Power_up; } |
4aecd6718 PM: Change hibern... |
572 573 574 |
hibernation_ops->enter(); /* We should never get here */ while (1); |
9cd9a0058 Hibernation: Ente... |
575 |
|
c125e96f0 PM: Make it possi... |
576 |
Power_up: |
40dc166cb PM / Core: Introd... |
577 |
syscore_resume(); |
c1a957d17 PM / suspend: Pre... |
578 |
system_state = SYSTEM_RUNNING; |
c125e96f0 PM: Make it possi... |
579 |
local_irq_enable(); |
8c506608c PM / hibernate: r... |
580 581 |
Enable_cpus: |
2f1a6fbbe power/suspend: Ad... |
582 |
suspend_enable_secondary_cpus(); |
c125e96f0 PM: Make it possi... |
583 |
|
e681c9dd6 PM: Fix typo in l... |
584 |
Platform_finish: |
9cd9a0058 Hibernation: Ente... |
585 |
hibernation_ops->finish(); |
2ed8d2b3a PM: Rework handli... |
586 |
|
cf579dfb8 PM / Sleep: Intro... |
587 |
dpm_resume_start(PMSG_RESTORE); |
4aecd6718 PM: Change hibern... |
588 |
|
9cd9a0058 Hibernation: Ente... |
589 |
Resume_devices: |
abfe2d7b9 Hibernation: Intr... |
590 |
entering_platform_hibernation = false; |
d16163029 PM core: rename s... |
591 |
dpm_resume_end(PMSG_RESTORE); |
9cd9a0058 Hibernation: Ente... |
592 |
resume_console(); |
2ed8d2b3a PM: Rework handli... |
593 |
|
caea99ef3 Hibernation: Intr... |
594 595 |
Close: hibernation_ops->end(); |
2ed8d2b3a PM: Rework handli... |
596 |
|
b1457bcc3 Hibernation: prep... |
597 |
return error; |
7777fab98 swsusp: remove co... |
598 599 600 |
} /** |
f42a9813f PM / Hibernate: U... |
601 |
* power_down - Shut the machine down for hibernation. |
1da177e4c Linux-2.6.12-rc2 |
602 |
* |
f42a9813f PM / Hibernate: U... |
603 604 605 |
* Use the platform driver, if configured, to put the system into the sleep * state corresponding to hibernation, or try to power it off or reboot, * depending on the value of hibernation_mode. |
1da177e4c Linux-2.6.12-rc2 |
606 |
*/ |
fe0c935a6 rework pm_ops pm_... |
607 |
static void power_down(void) |
1da177e4c Linux-2.6.12-rc2 |
608 |
{ |
62c552ccc PM / Hibernate: E... |
609 610 |
#ifdef CONFIG_SUSPEND int error; |
81d45bdf8 PM / hibernate: U... |
611 612 613 614 615 616 617 618 619 620 621 |
if (hibernation_mode == HIBERNATION_SUSPEND) { error = suspend_devices_and_enter(PM_SUSPEND_MEM); if (error) { hibernation_mode = hibernation_ops ? HIBERNATION_PLATFORM : HIBERNATION_SHUTDOWN; } else { /* Restore swap signature. */ error = swsusp_unmark(); if (error) |
2872de138 PM / hibernate: D... |
622 623 |
pr_err("Swap will be unusable! Try swapon -a. "); |
81d45bdf8 PM / hibernate: U... |
624 625 626 627 |
return; } } |
62c552ccc PM / Hibernate: E... |
628 |
#endif |
a3d25c275 PM: Separate hibe... |
629 |
switch (hibernation_mode) { |
a3d25c275 PM: Separate hibe... |
630 |
case HIBERNATION_REBOOT: |
fdde86ac5 [PATCH] swpsuspen... |
631 |
kernel_restart(NULL); |
1da177e4c Linux-2.6.12-rc2 |
632 |
break; |
a3d25c275 PM: Separate hibe... |
633 |
case HIBERNATION_PLATFORM: |
7777fab98 swsusp: remove co... |
634 |
hibernation_platform_enter(); |
df561f668 treewide: Use fal... |
635 |
fallthrough; |
9cd9a0058 Hibernation: Ente... |
636 |
case HIBERNATION_SHUTDOWN: |
2c730785d PM / hibernate: n... |
637 638 |
if (pm_power_off) kernel_power_off(); |
9cd9a0058 Hibernation: Ente... |
639 |
break; |
1da177e4c Linux-2.6.12-rc2 |
640 |
} |
fdde86ac5 [PATCH] swpsuspen... |
641 |
kernel_halt(); |
fe0c935a6 rework pm_ops pm_... |
642 643 644 645 |
/* * Valid image is on the disk, if we continue we risk serious data * corruption after resume. */ |
2872de138 PM / hibernate: D... |
646 647 |
pr_crit("Power down manually "); |
2c730785d PM / hibernate: n... |
648 649 |
while (1) cpu_relax(); |
1da177e4c Linux-2.6.12-rc2 |
650 |
} |
fe12c00d2 PM / hibernate: I... |
651 652 653 654 |
static int load_image_and_restore(void) { int error; unsigned int flags; |
8d8b2441d PM / sleep: Do no... |
655 656 |
pm_pr_dbg("Loading hibernation image. "); |
fe12c00d2 PM / hibernate: I... |
657 658 659 660 661 662 663 664 665 |
lock_device_hotplug(); error = create_basic_memory_bitmaps(); if (error) goto Unlock; error = swsusp_read(&flags); swsusp_close(FMODE_READ); if (!error) |
3704a6a44 PM: hibernate: Pr... |
666 |
error = hibernation_restore(flags & SF_PLATFORM_MODE); |
fe12c00d2 PM / hibernate: I... |
667 |
|
7a7b99bf8 PM: hibernate: Ad... |
668 669 |
pr_err("Failed to load image, recovering. "); |
fe12c00d2 PM / hibernate: I... |
670 671 672 673 674 675 676 |
swsusp_free(); free_basic_memory_bitmaps(); Unlock: unlock_device_hotplug(); return error; } |
1da177e4c Linux-2.6.12-rc2 |
677 |
/** |
f42a9813f PM / Hibernate: U... |
678 |
* hibernate - Carry out system hibernation, including saving the image. |
1da177e4c Linux-2.6.12-rc2 |
679 |
*/ |
a3d25c275 PM: Separate hibe... |
680 |
int hibernate(void) |
1da177e4c Linux-2.6.12-rc2 |
681 |
{ |
fe12c00d2 PM / hibernate: I... |
682 |
bool snapshot_test = false; |
70d932985 notifier: Fix bro... |
683 |
int error; |
1da177e4c Linux-2.6.12-rc2 |
684 |
|
a6e15a390 PM / hibernate: i... |
685 |
if (!hibernation_available()) { |
8d8b2441d PM / sleep: Do no... |
686 687 |
pm_pr_dbg("Hibernation not available. "); |
a6e15a390 PM / hibernate: i... |
688 689 |
return -EPERM; } |
bcda53faf PM / Sleep: Repla... |
690 |
lock_system_sleep(); |
0709db607 swsusp: use GFP_K... |
691 |
/* The snapshot device should not be opened while we're running */ |
ab7e9b067 PM: hibernate: In... |
692 |
if (!hibernate_acquire()) { |
b10d91174 PM: introduce hib... |
693 694 695 |
error = -EBUSY; goto Unlock; } |
8915aa204 PM / sleep: Mark ... |
696 697 |
pr_info("hibernation entry "); |
5a0a2f304 Hibernation: Invo... |
698 |
pm_prepare_console(); |
70d932985 notifier: Fix bro... |
699 700 701 |
error = pm_notifier_call_chain_robust(PM_HIBERNATION_PREPARE, PM_POST_HIBERNATION); if (error) goto Restore; |
0709db607 swsusp: use GFP_K... |
702 |
|
b5dee3130 PM / sleep: Refac... |
703 |
ksys_sync_helper(); |
232b14328 freezer: do not s... |
704 |
|
03afed8bc freezer: clean up... |
705 |
error = freeze_processes(); |
5a72e04df [PATCH] suspend/r... |
706 |
if (error) |
8fd37a4c9 PM / hibernate: C... |
707 |
goto Exit; |
942f40155 PM / hibernate / ... |
708 |
lock_device_hotplug(); |
8fd37a4c9 PM / hibernate: C... |
709 710 711 712 |
/* Allocate memory management structures */ error = create_basic_memory_bitmaps(); if (error) goto Thaw; |
1da177e4c Linux-2.6.12-rc2 |
713 |
|
7777fab98 swsusp: remove co... |
714 |
error = hibernation_snapshot(hibernation_mode == HIBERNATION_PLATFORM); |
a556d5b58 PM / Hibernate: R... |
715 |
if (error || freezer_test_done) |
8fd37a4c9 PM / hibernate: C... |
716 |
goto Free_bitmaps; |
64a473cb7 PM/Hibernate: Do ... |
717 718 |
if (in_suspend) { |
a634cc101 swsusp: introduce... |
719 720 721 722 |
unsigned int flags = 0; if (hibernation_mode == HIBERNATION_PLATFORM) flags |= SF_PLATFORM_MODE; |
f996fc967 PM / Hibernate: C... |
723 724 |
if (nocompress) flags |= SF_NOCOMPRESS_MODE; |
081a9d043 PM / Hibernate: I... |
725 726 |
else flags |= SF_CRC32_MODE; |
7a7b99bf8 PM: hibernate: Ad... |
727 728 |
pm_pr_dbg("Writing hibernation image. "); |
a634cc101 swsusp: introduce... |
729 |
error = swsusp_write(flags); |
7777fab98 swsusp: remove co... |
730 |
swsusp_free(); |
fe12c00d2 PM / hibernate: I... |
731 732 733 734 735 736 |
if (!error) { if (hibernation_mode == HIBERNATION_TEST_RESUME) snapshot_test = true; else power_down(); } |
5262a4750 PM / Hibernate: W... |
737 |
in_suspend = 0; |
c9e664f1f PM / Hibernate: F... |
738 |
pm_restore_gfp_mask(); |
b918f6e62 [PATCH] swsusp: d... |
739 |
} else { |
7a7b99bf8 PM: hibernate: Ad... |
740 741 |
pm_pr_dbg("Hibernation image restored successfully. "); |
b918f6e62 [PATCH] swsusp: d... |
742 |
} |
64a473cb7 PM/Hibernate: Do ... |
743 |
|
8fd37a4c9 PM / hibernate: C... |
744 745 |
Free_bitmaps: free_basic_memory_bitmaps(); |
b918f6e62 [PATCH] swsusp: d... |
746 |
Thaw: |
942f40155 PM / hibernate / ... |
747 |
unlock_device_hotplug(); |
fe12c00d2 PM / hibernate: I... |
748 |
if (snapshot_test) { |
8d8b2441d PM / sleep: Do no... |
749 750 |
pm_pr_dbg("Checking hibernation image "); |
fe12c00d2 PM / hibernate: I... |
751 752 753 754 |
error = swsusp_check(); if (!error) error = load_image_and_restore(); } |
5a0a2f304 Hibernation: Invo... |
755 |
thaw_processes(); |
a556d5b58 PM / Hibernate: R... |
756 757 758 |
/* Don't bother checking whether freezer_test_done is true */ freezer_test_done = false; |
0709db607 swsusp: use GFP_K... |
759 |
Exit: |
70d932985 notifier: Fix bro... |
760 761 |
pm_notifier_call_chain(PM_POST_HIBERNATION); Restore: |
5a0a2f304 Hibernation: Invo... |
762 |
pm_restore_console(); |
ab7e9b067 PM: hibernate: In... |
763 |
hibernate_release(); |
b10d91174 PM: introduce hib... |
764 |
Unlock: |
bcda53faf PM / Sleep: Repla... |
765 |
unlock_system_sleep(); |
8915aa204 PM / sleep: Mark ... |
766 767 |
pr_info("hibernation exit "); |
1da177e4c Linux-2.6.12-rc2 |
768 769 |
return error; } |
48001ea50 PM, libnvdimm: Ad... |
770 771 772 773 774 775 776 777 778 |
/** * hibernate_quiet_exec - Execute a function with all devices frozen. * @func: Function to execute. * @data: Data pointer to pass to @func. * * Return the @func return value or an error code if it cannot be executed. */ int hibernate_quiet_exec(int (*func)(void *data), void *data) { |
70d932985 notifier: Fix bro... |
779 |
int error; |
48001ea50 PM, libnvdimm: Ad... |
780 781 782 783 784 785 786 787 788 |
lock_system_sleep(); if (!hibernate_acquire()) { error = -EBUSY; goto unlock; } pm_prepare_console(); |
70d932985 notifier: Fix bro... |
789 790 791 |
error = pm_notifier_call_chain_robust(PM_HIBERNATION_PREPARE, PM_POST_HIBERNATION); if (error) goto restore; |
48001ea50 PM, libnvdimm: Ad... |
792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 |
error = freeze_processes(); if (error) goto exit; lock_device_hotplug(); pm_suspend_clear_flags(); error = platform_begin(true); if (error) goto thaw; error = freeze_kernel_threads(); if (error) goto thaw; error = dpm_prepare(PMSG_FREEZE); if (error) goto dpm_complete; suspend_console(); error = dpm_suspend(PMSG_FREEZE); if (error) goto dpm_resume; error = dpm_suspend_end(PMSG_FREEZE); if (error) goto dpm_resume; error = platform_pre_snapshot(true); if (error) goto skip; error = func(data); skip: platform_finish(true); dpm_resume_start(PMSG_THAW); dpm_resume: dpm_resume(PMSG_THAW); resume_console(); dpm_complete: dpm_complete(PMSG_THAW); thaw_kernel_threads(); thaw: platform_end(true); unlock_device_hotplug(); thaw_processes(); exit: |
70d932985 notifier: Fix bro... |
852 |
pm_notifier_call_chain(PM_POST_HIBERNATION); |
48001ea50 PM, libnvdimm: Ad... |
853 |
|
70d932985 notifier: Fix bro... |
854 |
restore: |
48001ea50 PM, libnvdimm: Ad... |
855 856 857 858 859 860 861 862 863 864 |
pm_restore_console(); hibernate_release(); unlock: unlock_system_sleep(); return error; } EXPORT_SYMBOL_GPL(hibernate_quiet_exec); |
1da177e4c Linux-2.6.12-rc2 |
865 866 |
/** |
f42a9813f PM / Hibernate: U... |
867 868 869 870 |
* software_resume - Resume from a saved hibernation image. * * This routine is called as a late initcall, when all devices have been * discovered and initialized already. |
1da177e4c Linux-2.6.12-rc2 |
871 |
* |
f42a9813f PM / Hibernate: U... |
872 873 874 |
* The image reading code is called to see if there is a hibernation image * available for reading. If that is the case, devices are quiesced and the * contents of memory is restored from the saved image. |
1da177e4c Linux-2.6.12-rc2 |
875 |
* |
f42a9813f PM / Hibernate: U... |
876 |
* If this is successful, control reappears in the restored target kernel in |
d439e64f2 PM / hibernate: f... |
877 |
* hibernation_snapshot() which returns to hibernate(). Otherwise, the routine |
f42a9813f PM / Hibernate: U... |
878 879 |
* attempts to recover gracefully and make the kernel return to the normal mode * of operation. |
1da177e4c Linux-2.6.12-rc2 |
880 |
*/ |
1da177e4c Linux-2.6.12-rc2 |
881 882 |
static int software_resume(void) { |
70d932985 notifier: Fix bro... |
883 |
int error; |
1da177e4c Linux-2.6.12-rc2 |
884 |
|
60a0d2338 hibernate: fix lo... |
885 |
/* |
eed3ee082 PM/resume: wait f... |
886 887 |
* If the user said "noresume".. bail out early. */ |
a6e15a390 PM / hibernate: i... |
888 |
if (noresume || !hibernation_available()) |
eed3ee082 PM/resume: wait f... |
889 890 891 |
return 0; /* |
60a0d2338 hibernate: fix lo... |
892 893 894 |
* name_to_dev_t() below takes a sysfs buffer mutex when sysfs * is configured into the kernel. Since the regular hibernate * trigger path is via sysfs which takes a buffer mutex before |
55f2503c3 PM / reboot: Elim... |
895 896 |
* calling hibernate functions (which take system_transition_mutex) * this can cause lockdep to complain about a possible ABBA deadlock |
60a0d2338 hibernate: fix lo... |
897 898 899 900 |
* which cannot happen since we're in the boot code here and * sysfs can't be invoked yet. Therefore, we use a subclass * here to avoid lockdep complaining. */ |
55f2503c3 PM / reboot: Elim... |
901 |
mutex_lock_nested(&system_transition_mutex, SINGLE_DEPTH_NESTING); |
0c8454f56 PM/Hibernate: Fix... |
902 903 904 905 906 907 908 909 |
if (swsusp_resume_device) goto Check_image; if (!strlen(resume_file)) { error = -ENOENT; goto Unlock; } |
8d8b2441d PM / sleep: Do no... |
910 911 |
pm_pr_dbg("Checking hibernation image partition %s ", resume_file); |
0c8454f56 PM/Hibernate: Fix... |
912 |
|
f126f7334 PM / Hibernate: A... |
913 |
if (resume_delay) { |
2872de138 PM / hibernate: D... |
914 915 |
pr_info("Waiting %dsec before reading resume device ... ", |
f126f7334 PM / Hibernate: A... |
916 917 918 |
resume_delay); ssleep(resume_delay); } |
0c8454f56 PM/Hibernate: Fix... |
919 920 |
/* Check if the device is there */ swsusp_resume_device = name_to_dev_t(resume_file); |
3efa147ad [PATCH] pm: Fix r... |
921 |
if (!swsusp_resume_device) { |
eed3ee082 PM/resume: wait f... |
922 923 924 925 926 |
/* * Some device discovery might still be in progress; we need * to wait for this to finish. */ wait_for_device_probe(); |
6f8d7022a PM / Hibernate: A... |
927 928 929 930 931 932 |
if (resume_wait) { while ((swsusp_resume_device = name_to_dev_t(resume_file)) == 0) msleep(10); async_synchronize_full(); } |
3efa147ad [PATCH] pm: Fix r... |
933 |
swsusp_resume_device = name_to_dev_t(resume_file); |
0c8454f56 PM/Hibernate: Fix... |
934 935 936 937 |
if (!swsusp_resume_device) { error = -ENODEV; goto Unlock; } |
3efa147ad [PATCH] pm: Fix r... |
938 |
} |
0c8454f56 PM/Hibernate: Fix... |
939 |
Check_image: |
8d8b2441d PM / sleep: Do no... |
940 941 |
pm_pr_dbg("Hibernation image partition %d:%d present ", |
0c8454f56 PM/Hibernate: Fix... |
942 |
MAJOR(swsusp_resume_device), MINOR(swsusp_resume_device)); |
1da177e4c Linux-2.6.12-rc2 |
943 |
|
8d8b2441d PM / sleep: Do no... |
944 945 |
pm_pr_dbg("Looking for hibernation image. "); |
ed746e3b1 [PATCH] swsusp: C... |
946 947 |
error = swsusp_check(); if (error) |
74dfd666d swsusp: do not us... |
948 |
goto Unlock; |
1da177e4c Linux-2.6.12-rc2 |
949 |
|
0709db607 swsusp: use GFP_K... |
950 |
/* The snapshot device should not be opened while we're running */ |
ab7e9b067 PM: hibernate: In... |
951 |
if (!hibernate_acquire()) { |
0709db607 swsusp: use GFP_K... |
952 |
error = -EBUSY; |
76b57e613 PM / Hibernate: F... |
953 |
swsusp_close(FMODE_READ); |
0709db607 swsusp: use GFP_K... |
954 955 |
goto Unlock; } |
8915aa204 PM / sleep: Mark ... |
956 957 |
pr_info("resume from hibernation "); |
5a0a2f304 Hibernation: Invo... |
958 |
pm_prepare_console(); |
70d932985 notifier: Fix bro... |
959 960 961 |
error = pm_notifier_call_chain_robust(PM_RESTORE_PREPARE, PM_POST_RESTORE); if (error) goto Restore; |
1bfcf1304 pm: rework disabl... |
962 |
|
7a7b99bf8 PM: hibernate: Ad... |
963 964 |
pm_pr_dbg("Preparing processes for hibernation restore. "); |
03afed8bc freezer: clean up... |
965 |
error = freeze_processes(); |
8fd37a4c9 PM / hibernate: C... |
966 967 |
if (error) goto Close_Finish; |
2351f8d29 PM: hibernate: Fr... |
968 969 970 971 972 973 |
error = freeze_kernel_threads(); if (error) { thaw_processes(); goto Close_Finish; } |
fe12c00d2 PM / hibernate: I... |
974 |
error = load_image_and_restore(); |
8fd37a4c9 PM / hibernate: C... |
975 |
thaw_processes(); |
0709db607 swsusp: use GFP_K... |
976 |
Finish: |
70d932985 notifier: Fix bro... |
977 978 |
pm_notifier_call_chain(PM_POST_RESTORE); Restore: |
5a0a2f304 Hibernation: Invo... |
979 |
pm_restore_console(); |
7a7b99bf8 PM: hibernate: Ad... |
980 981 |
pr_info("resume failed (%d) ", error); |
ab7e9b067 PM: hibernate: In... |
982 |
hibernate_release(); |
dd5d666b7 [PATCH] swsusp: a... |
983 |
/* For success case, the suspend path will release the lock */ |
74dfd666d swsusp: do not us... |
984 |
Unlock: |
55f2503c3 PM / reboot: Elim... |
985 |
mutex_unlock(&system_transition_mutex); |
8d8b2441d PM / sleep: Do no... |
986 987 |
pm_pr_dbg("Hibernation image not present or could not be loaded. "); |
7777fab98 swsusp: remove co... |
988 |
return error; |
8fd37a4c9 PM / hibernate: C... |
989 |
Close_Finish: |
76b57e613 PM / Hibernate: F... |
990 991 |
swsusp_close(FMODE_READ); goto Finish; |
1da177e4c Linux-2.6.12-rc2 |
992 |
} |
d3c345dbc PM / hibernate: M... |
993 |
late_initcall_sync(software_resume); |
1da177e4c Linux-2.6.12-rc2 |
994 |
|
a3d25c275 PM: Separate hibe... |
995 996 997 998 |
static const char * const hibernation_modes[] = { [HIBERNATION_PLATFORM] = "platform", [HIBERNATION_SHUTDOWN] = "shutdown", [HIBERNATION_REBOOT] = "reboot", |
62c552ccc PM / Hibernate: E... |
999 1000 1001 |
#ifdef CONFIG_SUSPEND [HIBERNATION_SUSPEND] = "suspend", #endif |
fe12c00d2 PM / hibernate: I... |
1002 |
[HIBERNATION_TEST_RESUME] = "test_resume", |
1da177e4c Linux-2.6.12-rc2 |
1003 |
}; |
f42a9813f PM / Hibernate: U... |
1004 1005 |
/* * /sys/power/disk - Control hibernation mode. |
1da177e4c Linux-2.6.12-rc2 |
1006 |
* |
f42a9813f PM / Hibernate: U... |
1007 1008 1009 |
* Hibernation can be handled in several ways. There are a few different ways * to put the system into the sleep state: using the platform driver (e.g. ACPI * or other hibernation_ops), powering it off or rebooting it (for testing |
48580ab87 PM / Hibernate: R... |
1010 |
* mostly). |
1da177e4c Linux-2.6.12-rc2 |
1011 |
* |
f42a9813f PM / Hibernate: U... |
1012 1013 |
* The sysfs file /sys/power/disk provides an interface for selecting the * hibernation mode to use. Reading from this file causes the available modes |
48580ab87 PM / Hibernate: R... |
1014 |
* to be printed. There are 3 modes that can be supported: |
1da177e4c Linux-2.6.12-rc2 |
1015 |
* |
1da177e4c Linux-2.6.12-rc2 |
1016 1017 1018 1019 |
* 'platform' * 'shutdown' * 'reboot' * |
f42a9813f PM / Hibernate: U... |
1020 1021 1022 1023 1024 1025 1026 1027 |
* If a platform hibernation driver is in use, 'platform' will be supported * and will be used by default. Otherwise, 'shutdown' will be used by default. * The selected option (i.e. the one corresponding to the current value of * hibernation_mode) is enclosed by a square bracket. * * To select a given hibernation mode it is necessary to write the mode's * string representation (as returned by reading from /sys/power/disk) back * into /sys/power/disk. |
1da177e4c Linux-2.6.12-rc2 |
1028 |
*/ |
386f275f5 Driver Core: swit... |
1029 1030 |
static ssize_t disk_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) |
1da177e4c Linux-2.6.12-rc2 |
1031 |
{ |
f0ced9b22 power management:... |
1032 1033 |
int i; char *start = buf; |
a6e15a390 PM / hibernate: i... |
1034 1035 1036 |
if (!hibernation_available()) return sprintf(buf, "[disabled] "); |
a3d25c275 PM: Separate hibe... |
1037 1038 |
for (i = HIBERNATION_FIRST; i <= HIBERNATION_MAX; i++) { if (!hibernation_modes[i]) |
f0ced9b22 power management:... |
1039 1040 |
continue; switch (i) { |
a3d25c275 PM: Separate hibe... |
1041 1042 |
case HIBERNATION_SHUTDOWN: case HIBERNATION_REBOOT: |
62c552ccc PM / Hibernate: E... |
1043 1044 1045 |
#ifdef CONFIG_SUSPEND case HIBERNATION_SUSPEND: #endif |
fe12c00d2 PM / hibernate: I... |
1046 |
case HIBERNATION_TEST_RESUME: |
f0ced9b22 power management:... |
1047 |
break; |
a3d25c275 PM: Separate hibe... |
1048 1049 |
case HIBERNATION_PLATFORM: if (hibernation_ops) |
f0ced9b22 power management:... |
1050 1051 1052 1053 |
break; /* not a valid mode, continue with loop */ continue; } |
a3d25c275 PM: Separate hibe... |
1054 1055 |
if (i == hibernation_mode) buf += sprintf(buf, "[%s] ", hibernation_modes[i]); |
f0ced9b22 power management:... |
1056 |
else |
a3d25c275 PM: Separate hibe... |
1057 |
buf += sprintf(buf, "%s ", hibernation_modes[i]); |
f0ced9b22 power management:... |
1058 1059 1060 1061 |
} buf += sprintf(buf, " "); return buf-start; |
1da177e4c Linux-2.6.12-rc2 |
1062 |
} |
386f275f5 Driver Core: swit... |
1063 1064 |
static ssize_t disk_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t n) |
1da177e4c Linux-2.6.12-rc2 |
1065 1066 1067 1068 1069 |
{ int error = 0; int i; int len; char *p; |
a3d25c275 PM: Separate hibe... |
1070 |
int mode = HIBERNATION_INVALID; |
1da177e4c Linux-2.6.12-rc2 |
1071 |
|
a6e15a390 PM / hibernate: i... |
1072 1073 |
if (!hibernation_available()) return -EPERM; |
1da177e4c Linux-2.6.12-rc2 |
1074 1075 1076 |
p = memchr(buf, ' ', n); len = p ? p - buf : n; |
bcda53faf PM / Sleep: Repla... |
1077 |
lock_system_sleep(); |
a3d25c275 PM: Separate hibe... |
1078 |
for (i = HIBERNATION_FIRST; i <= HIBERNATION_MAX; i++) { |
8d98a690f swsusp: fix sysfs... |
1079 1080 |
if (len == strlen(hibernation_modes[i]) && !strncmp(buf, hibernation_modes[i], len)) { |
1da177e4c Linux-2.6.12-rc2 |
1081 1082 1083 1084 |
mode = i; break; } } |
a3d25c275 PM: Separate hibe... |
1085 |
if (mode != HIBERNATION_INVALID) { |
fe0c935a6 rework pm_ops pm_... |
1086 |
switch (mode) { |
a3d25c275 PM: Separate hibe... |
1087 1088 |
case HIBERNATION_SHUTDOWN: case HIBERNATION_REBOOT: |
62c552ccc PM / Hibernate: E... |
1089 1090 1091 |
#ifdef CONFIG_SUSPEND case HIBERNATION_SUSPEND: #endif |
fe12c00d2 PM / hibernate: I... |
1092 |
case HIBERNATION_TEST_RESUME: |
a3d25c275 PM: Separate hibe... |
1093 |
hibernation_mode = mode; |
fe0c935a6 rework pm_ops pm_... |
1094 |
break; |
a3d25c275 PM: Separate hibe... |
1095 1096 1097 |
case HIBERNATION_PLATFORM: if (hibernation_ops) hibernation_mode = mode; |
1da177e4c Linux-2.6.12-rc2 |
1098 1099 1100 |
else error = -EINVAL; } |
a3d25c275 PM: Separate hibe... |
1101 |
} else |
1da177e4c Linux-2.6.12-rc2 |
1102 |
error = -EINVAL; |
a3d25c275 PM: Separate hibe... |
1103 |
if (!error) |
8d8b2441d PM / sleep: Do no... |
1104 1105 1106 |
pm_pr_dbg("Hibernation mode set to '%s' ", hibernation_modes[mode]); |
bcda53faf PM / Sleep: Repla... |
1107 |
unlock_system_sleep(); |
1da177e4c Linux-2.6.12-rc2 |
1108 1109 1110 1111 |
return error ? error : n; } power_attr(disk); |
386f275f5 Driver Core: swit... |
1112 1113 |
static ssize_t resume_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) |
1da177e4c Linux-2.6.12-rc2 |
1114 |
{ |
6ada7ba2f PM: hibernate: fi... |
1115 1116 |
return sprintf(buf, "%d:%d ", MAJOR(swsusp_resume_device), |
1da177e4c Linux-2.6.12-rc2 |
1117 1118 |
MINOR(swsusp_resume_device)); } |
386f275f5 Driver Core: swit... |
1119 1120 |
static ssize_t resume_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t n) |
1da177e4c Linux-2.6.12-rc2 |
1121 |
{ |
1da177e4c Linux-2.6.12-rc2 |
1122 |
dev_t res; |
421a5fa1a PM / hibernate: u... |
1123 1124 |
int len = n; char *name; |
1da177e4c Linux-2.6.12-rc2 |
1125 |
|
421a5fa1a PM / hibernate: u... |
1126 1127 1128 1129 1130 1131 |
if (len && buf[len-1] == ' ') len--; name = kstrndup(buf, len, GFP_KERNEL); if (!name) return -ENOMEM; |
1da177e4c Linux-2.6.12-rc2 |
1132 |
|
421a5fa1a PM / hibernate: u... |
1133 1134 1135 1136 |
res = name_to_dev_t(name); kfree(name); if (!res) return -EINVAL; |
1da177e4c Linux-2.6.12-rc2 |
1137 |
|
bcda53faf PM / Sleep: Repla... |
1138 |
lock_system_sleep(); |
a576219ac [PATCH] swsusp: r... |
1139 |
swsusp_resume_device = res; |
bcda53faf PM / Sleep: Repla... |
1140 |
unlock_system_sleep(); |
7a7b99bf8 PM: hibernate: Ad... |
1141 1142 1143 |
pm_pr_dbg("Configured hibernation resume from disk to %u ", swsusp_resume_device); |
a576219ac [PATCH] swsusp: r... |
1144 1145 |
noresume = 0; software_resume(); |
421a5fa1a PM / hibernate: u... |
1146 |
return n; |
1da177e4c Linux-2.6.12-rc2 |
1147 1148 1149 |
} power_attr(resume); |
355064675 PM / hibernate: M... |
1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 |
static ssize_t resume_offset_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { return sprintf(buf, "%llu ", (unsigned long long)swsusp_resume_block); } static ssize_t resume_offset_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t n) { unsigned long long offset; int rc; rc = kstrtoull(buf, 0, &offset); if (rc) return rc; swsusp_resume_block = offset; return n; } power_attr(resume_offset); |
386f275f5 Driver Core: swit... |
1173 1174 |
static ssize_t image_size_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) |
ca0aec0f7 [PATCH] swsusp: m... |
1175 |
{ |
853609b61 [PATCH] swsusp: u... |
1176 1177 |
return sprintf(buf, "%lu ", image_size); |
ca0aec0f7 [PATCH] swsusp: m... |
1178 |
} |
386f275f5 Driver Core: swit... |
1179 1180 |
static ssize_t image_size_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t n) |
ca0aec0f7 [PATCH] swsusp: m... |
1181 |
{ |
853609b61 [PATCH] swsusp: u... |
1182 |
unsigned long size; |
ca0aec0f7 [PATCH] swsusp: m... |
1183 |
|
853609b61 [PATCH] swsusp: u... |
1184 |
if (sscanf(buf, "%lu", &size) == 1) { |
ca0aec0f7 [PATCH] swsusp: m... |
1185 1186 1187 1188 1189 1190 1191 1192 |
image_size = size; return n; } return -EINVAL; } power_attr(image_size); |
ddeb64870 PM / Hibernate: A... |
1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 |
static ssize_t reserved_size_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { return sprintf(buf, "%lu ", reserved_size); } static ssize_t reserved_size_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t n) { unsigned long size; if (sscanf(buf, "%lu", &size) == 1) { reserved_size = size; return n; } return -EINVAL; } power_attr(reserved_size); |
6ada7ba2f PM: hibernate: fi... |
1215 |
static struct attribute *g[] = { |
1da177e4c Linux-2.6.12-rc2 |
1216 |
&disk_attr.attr, |
355064675 PM / hibernate: M... |
1217 |
&resume_offset_attr.attr, |
1da177e4c Linux-2.6.12-rc2 |
1218 |
&resume_attr.attr, |
ca0aec0f7 [PATCH] swsusp: m... |
1219 |
&image_size_attr.attr, |
ddeb64870 PM / Hibernate: A... |
1220 |
&reserved_size_attr.attr, |
1da177e4c Linux-2.6.12-rc2 |
1221 1222 |
NULL, }; |
59494fe2c PM: hibernate: co... |
1223 |
static const struct attribute_group attr_group = { |
1da177e4c Linux-2.6.12-rc2 |
1224 1225 1226 1227 1228 1229 |
.attrs = g, }; static int __init pm_disk_init(void) { |
d76e15fb2 driver core: make... |
1230 |
return sysfs_create_group(power_kobj, &attr_group); |
1da177e4c Linux-2.6.12-rc2 |
1231 1232 1233 1234 1235 1236 1237 1238 1239 |
} core_initcall(pm_disk_init); static int __init resume_setup(char *str) { if (noresume) return 1; |
6ada7ba2f PM: hibernate: fi... |
1240 |
strncpy(resume_file, str, 255); |
1da177e4c Linux-2.6.12-rc2 |
1241 1242 |
return 1; } |
9a154d9d9 [PATCH] swsusp: a... |
1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 |
static int __init resume_offset_setup(char *str) { unsigned long long offset; if (noresume) return 1; if (sscanf(str, "%llu", &offset) == 1) swsusp_resume_block = offset; return 1; } |
f996fc967 PM / Hibernate: C... |
1255 1256 |
static int __init hibernate_setup(char *str) { |
2f88e41a2 PM / hibernate: A... |
1257 |
if (!strncmp(str, "noresume", 8)) { |
f996fc967 PM / Hibernate: C... |
1258 |
noresume = 1; |
2f88e41a2 PM / hibernate: A... |
1259 |
} else if (!strncmp(str, "nocompress", 10)) { |
f996fc967 PM / Hibernate: C... |
1260 |
nocompress = 1; |
2f88e41a2 PM / hibernate: A... |
1261 |
} else if (!strncmp(str, "no", 2)) { |
a6e15a390 PM / hibernate: i... |
1262 1263 |
noresume = 1; nohibernate = 1; |
0f5bf6d0a arch: Rename CONF... |
1264 |
} else if (IS_ENABLED(CONFIG_STRICT_KERNEL_RWX) |
4c0b6c10f PM / hibernate: I... |
1265 1266 |
&& !strncmp(str, "protect_image", 13)) { enable_restore_image_protection(); |
a6e15a390 PM / hibernate: i... |
1267 |
} |
f996fc967 PM / Hibernate: C... |
1268 1269 |
return 1; } |
1da177e4c Linux-2.6.12-rc2 |
1270 1271 1272 1273 1274 |
static int __init noresume_setup(char *str) { noresume = 1; return 1; } |
6f8d7022a PM / Hibernate: A... |
1275 1276 1277 1278 1279 |
static int __init resumewait_setup(char *str) { resume_wait = 1; return 1; } |
f126f7334 PM / Hibernate: A... |
1280 1281 |
static int __init resumedelay_setup(char *str) { |
f6514be5f PM / hibernate: F... |
1282 |
int rc = kstrtouint(str, 0, &resume_delay); |
317cf7e5e PM / hibernate: c... |
1283 1284 1285 |
if (rc) return rc; |
f126f7334 PM / Hibernate: A... |
1286 1287 |
return 1; } |
a6e15a390 PM / hibernate: i... |
1288 1289 1290 1291 1292 1293 |
static int __init nohibernate_setup(char *str) { noresume = 1; nohibernate = 1; return 1; } |
1da177e4c Linux-2.6.12-rc2 |
1294 |
__setup("noresume", noresume_setup); |
9a154d9d9 [PATCH] swsusp: a... |
1295 |
__setup("resume_offset=", resume_offset_setup); |
1da177e4c Linux-2.6.12-rc2 |
1296 |
__setup("resume=", resume_setup); |
f996fc967 PM / Hibernate: C... |
1297 |
__setup("hibernate=", hibernate_setup); |
6f8d7022a PM / Hibernate: A... |
1298 |
__setup("resumewait", resumewait_setup); |
f126f7334 PM / Hibernate: A... |
1299 |
__setup("resumedelay=", resumedelay_setup); |
a6e15a390 PM / hibernate: i... |
1300 |
__setup("nohibernate", nohibernate_setup); |