Commit | Line | Data |
---|---|---|
6fa3eb70 S |
1 | /* |
2 | * kernel/power/tuxonice_highlevel.c | |
3 | */ | |
4 | /** \mainpage TuxOnIce. | |
5 | * | |
6 | * TuxOnIce provides support for saving and restoring an image of | |
7 | * system memory to an arbitrary storage device, either on the local computer, | |
8 | * or across some network. The support is entirely OS based, so TuxOnIce | |
9 | * works without requiring BIOS, APM or ACPI support. The vast majority of the | |
10 | * code is also architecture independant, so it should be very easy to port | |
11 | * the code to new architectures. TuxOnIce includes support for SMP, 4G HighMem | |
12 | * and preemption. Initramfses and initrds are also supported. | |
13 | * | |
14 | * TuxOnIce uses a modular design, in which the method of storing the image is | |
15 | * completely abstracted from the core code, as are transformations on the data | |
16 | * such as compression and/or encryption (multiple 'modules' can be used to | |
17 | * provide arbitrary combinations of functionality). The user interface is also | |
18 | * modular, so that arbitrarily simple or complex interfaces can be used to | |
19 | * provide anything from debugging information through to eye candy. | |
20 | * | |
21 | * \section Copyright | |
22 | * | |
23 | * TuxOnIce is released under the GPLv2. | |
24 | * | |
25 | * Copyright (C) 1998-2001 Gabor Kuti <seasons@fornax.hu><BR> | |
26 | * Copyright (C) 1998,2001,2002 Pavel Machek <pavel@suse.cz><BR> | |
27 | * Copyright (C) 2002-2003 Florent Chabaud <fchabaud@free.fr><BR> | |
28 | * Copyright (C) 2002-2010 Nigel Cunningham (nigel at tuxonice net)<BR> | |
29 | * | |
30 | * \section Credits | |
31 | * | |
32 | * Nigel would like to thank the following people for their work: | |
33 | * | |
34 | * Bernard Blackham <bernard@blackham.com.au><BR> | |
35 | * Web page & Wiki administration, some coding. A person without whom | |
36 | * TuxOnIce would not be where it is. | |
37 | * | |
38 | * Michael Frank <mhf@linuxmail.org><BR> | |
39 | * Extensive testing and help with improving stability. I was constantly | |
40 | * amazed by the quality and quantity of Michael's help. | |
41 | * | |
42 | * Pavel Machek <pavel@ucw.cz><BR> | |
43 | * Modifications, defectiveness pointing, being with Gabor at the very | |
44 | * beginning, suspend to swap space, stop all tasks. Port to 2.4.18-ac and | |
45 | * 2.5.17. Even though Pavel and I disagree on the direction suspend to | |
46 | * disk should take, I appreciate the valuable work he did in helping Gabor | |
47 | * get the concept working. | |
48 | * | |
49 | * ..and of course the myriads of TuxOnIce users who have helped diagnose | |
50 | * and fix bugs, made suggestions on how to improve the code, proofread | |
51 | * documentation, and donated time and money. | |
52 | * | |
53 | * Thanks also to corporate sponsors: | |
54 | * | |
55 | * <B>Redhat.</B>Sometime employer from May 2006 (my fault, not Redhat's!). | |
56 | * | |
57 | * <B>Cyclades.com.</B> Nigel's employers from Dec 2004 until May 2006, who | |
58 | * allowed him to work on TuxOnIce and PM related issues on company time. | |
59 | * | |
60 | * <B>LinuxFund.org.</B> Sponsored Nigel's work on TuxOnIce for four months Oct | |
61 | * 2003 to Jan 2004. | |
62 | * | |
63 | * <B>LAC Linux.</B> Donated P4 hardware that enabled development and ongoing | |
64 | * maintenance of SMP and Highmem support. | |
65 | * | |
66 | * <B>OSDL.</B> Provided access to various hardware configurations, make | |
67 | * occasional small donations to the project. | |
68 | */ | |
69 | ||
70 | #include <linux/suspend.h> | |
71 | #include <linux/freezer.h> | |
72 | #include <generated/utsrelease.h> | |
73 | #include <linux/cpu.h> | |
74 | #include <linux/console.h> | |
75 | #include <linux/writeback.h> | |
76 | #include <linux/uaccess.h> /* for get/set_fs & KERNEL_DS on i386 */ | |
77 | #include <linux/bio.h> | |
78 | #include <linux/kgdb.h> | |
79 | ||
80 | #include "tuxonice.h" | |
81 | #include "tuxonice_modules.h" | |
82 | #include "tuxonice_sysfs.h" | |
83 | #include "tuxonice_prepare_image.h" | |
84 | #include "tuxonice_io.h" | |
85 | #include "tuxonice_ui.h" | |
86 | #include "tuxonice_power_off.h" | |
87 | #include "tuxonice_storage.h" | |
88 | #include "tuxonice_checksum.h" | |
89 | #include "tuxonice_builtin.h" | |
90 | #include "tuxonice_atomic_copy.h" | |
91 | #include "tuxonice_alloc.h" | |
92 | #include "tuxonice_cluster.h" | |
93 | ||
94 | /*! Pageset metadata. */ | |
95 | struct pagedir pagedir2 = { 2 }; | |
96 | ||
97 | EXPORT_SYMBOL_GPL(pagedir2); | |
98 | ||
99 | static mm_segment_t oldfs; | |
100 | static DEFINE_MUTEX(tuxonice_in_use); | |
101 | static int block_dump_save; | |
102 | ||
103 | /* Binary signature if an image is present */ | |
104 | char tuxonice_signature[9] = "\xed\xc3\x02\xe9\x98\x56\xe5\x0c"; | |
105 | EXPORT_SYMBOL_GPL(tuxonice_signature); | |
106 | ||
107 | unsigned long boot_kernel_data_buffer; | |
108 | ||
109 | #if 0 /* JJ: removed it */ | |
110 | static char *result_strings[] = { | |
111 | "Hibernation was aborted", | |
112 | "The user requested that we cancel the hibernation", | |
113 | "No storage was available", | |
114 | "Insufficient storage was available", | |
115 | "Freezing filesystems and/or tasks failed", | |
116 | "A pre-existing image was used", | |
117 | "We would free memory, but image size limit doesn't allow this", | |
118 | "Unable to free enough memory to hibernate", | |
119 | "Unable to obtain the Power Management Semaphore", | |
120 | "A device suspend/resume returned an error", | |
121 | "A system device suspend/resume returned an error", | |
122 | "The extra pages allowance is too small", | |
123 | "We were unable to successfully prepare an image", | |
124 | "TuxOnIce module initialisation failed", | |
125 | "TuxOnIce module cleanup failed", | |
126 | "I/O errors were encountered", | |
127 | "Ran out of memory", | |
128 | "An error was encountered while reading the image", | |
129 | "Platform preparation failed", | |
130 | "CPU Hotplugging failed", | |
131 | "Architecture specific preparation failed", | |
132 | "Pages needed resaving, but we were told to abort if this happens", | |
133 | "We can't hibernate at the moment (invalid resume= or filewriter " "target?)", | |
134 | "A hibernation preparation notifier chain member cancelled the " "hibernation", | |
135 | "Pre-snapshot preparation failed", | |
136 | "Pre-restore preparation failed", | |
137 | "Failed to disable usermode helpers", | |
138 | "Can't resume from alternate image", | |
139 | "Header reservation too small", | |
140 | "Device Power Management Preparation failed", | |
141 | }; | |
142 | #endif | |
143 | ||
144 | /** | |
145 | * toi_finish_anything - cleanup after doing anything | |
146 | * @hibernate_or_resume: Whether finishing a cycle or attempt at | |
147 | * resuming. | |
148 | * | |
149 | * This is our basic clean-up routine, matching start_anything below. We | |
150 | * call cleanup routines, drop module references and restore process fs and | |
151 | * cpus allowed masks, together with the global block_dump variable's value. | |
152 | **/ | |
153 | void toi_finish_anything(int hibernate_or_resume) | |
154 | { | |
155 | hib_log("hibernate_or_resume(%d)\n", hibernate_or_resume); | |
156 | toi_cleanup_modules(hibernate_or_resume); | |
157 | toi_put_modules(); | |
158 | if (hibernate_or_resume) { | |
159 | block_dump = block_dump_save; | |
160 | set_cpus_allowed_ptr(current, cpu_all_mask); | |
161 | toi_alloc_print_debug_stats(); | |
162 | atomic_inc(&snapshot_device_available); | |
163 | unlock_system_sleep(); | |
164 | } | |
165 | ||
166 | set_fs(oldfs); | |
167 | mutex_unlock(&tuxonice_in_use); | |
168 | } | |
169 | ||
170 | /** | |
171 | * toi_start_anything - basic initialisation for TuxOnIce | |
172 | * @toi_or_resume: Whether starting a cycle or attempt at resuming. | |
173 | * | |
174 | * Our basic initialisation routine. Take references on modules, use the | |
175 | * kernel segment, recheck resume= if no active allocator is set, initialise | |
176 | * modules, save and reset block_dump and ensure we're running on CPU0. | |
177 | **/ | |
178 | int toi_start_anything(int hibernate_or_resume) | |
179 | { | |
180 | mutex_lock(&tuxonice_in_use); | |
181 | ||
182 | oldfs = get_fs(); | |
183 | set_fs(KERNEL_DS); | |
184 | ||
185 | if (hibernate_or_resume) { | |
186 | lock_system_sleep(); | |
187 | ||
188 | if (!atomic_add_unless(&snapshot_device_available, -1, 0)) | |
189 | goto snapshotdevice_unavailable; | |
190 | } | |
191 | ||
192 | if (hibernate_or_resume == SYSFS_HIBERNATE) | |
193 | toi_print_modules(); | |
194 | ||
195 | if (toi_get_modules()) { | |
196 | printk(KERN_INFO "TuxOnIce: Get modules failed!\n"); | |
197 | goto prehibernate_err; | |
198 | } | |
199 | ||
200 | if (hibernate_or_resume) { | |
201 | block_dump_save = block_dump; | |
202 | block_dump = 0; | |
203 | set_cpus_allowed_ptr(current, cpumask_of(cpumask_first(cpu_online_mask))); | |
204 | } | |
205 | ||
206 | if (toi_initialise_modules_early(hibernate_or_resume)) | |
207 | goto early_init_err; | |
208 | ||
209 | if (!toiActiveAllocator) { | |
210 | hib_log("hibernate_or_resume(0x%08x), resume_file=\"%s\"\n", hibernate_or_resume, | |
211 | resume_file); | |
212 | toi_attempt_to_parse_resume_device(!hibernate_or_resume); | |
213 | } | |
214 | ||
215 | if (!toi_initialise_modules_late(hibernate_or_resume)) | |
216 | return 0; | |
217 | ||
218 | toi_cleanup_modules(hibernate_or_resume); | |
219 | early_init_err: | |
220 | if (hibernate_or_resume) { | |
221 | block_dump_save = block_dump; | |
222 | set_cpus_allowed_ptr(current, cpu_all_mask); | |
223 | } | |
224 | toi_put_modules(); | |
225 | prehibernate_err: | |
226 | if (hibernate_or_resume) | |
227 | atomic_inc(&snapshot_device_available); | |
228 | snapshotdevice_unavailable: | |
229 | if (hibernate_or_resume) | |
230 | mutex_unlock(&pm_mutex); | |
231 | set_fs(oldfs); | |
232 | mutex_unlock(&tuxonice_in_use); | |
233 | return -EBUSY; | |
234 | } | |
235 | ||
236 | /* | |
237 | * Nosave page tracking. | |
238 | * | |
239 | * Here rather than in prepare_image because we want to do it once only at the | |
240 | * start of a cycle. | |
241 | */ | |
242 | ||
243 | /** | |
244 | * mark_nosave_pages - set up our Nosave bitmap | |
245 | * | |
246 | * Build a bitmap of Nosave pages from the list. The bitmap allows faster | |
247 | * use when preparing the image. | |
248 | **/ | |
249 | static void mark_nosave_pages(void) | |
250 | { | |
251 | struct nosave_region *region; | |
252 | ||
253 | list_for_each_entry(region, &nosave_regions, list) { | |
254 | unsigned long pfn; | |
255 | ||
256 | for (pfn = region->start_pfn; pfn < region->end_pfn; pfn++) | |
257 | if (pfn_valid(pfn)) | |
258 | SetPageNosave(pfn_to_page(pfn)); | |
259 | } | |
260 | } | |
261 | ||
262 | static int toi_alloc_bitmap(struct memory_bitmap **bm) | |
263 | { | |
264 | int result = 0; | |
265 | ||
266 | *bm = kzalloc(sizeof(struct memory_bitmap), GFP_KERNEL); | |
267 | if (!*bm) { | |
268 | printk(KERN_ERR "Failed to kzalloc memory for a bitmap.\n"); | |
269 | return -ENOMEM; | |
270 | } | |
271 | ||
272 | result = memory_bm_create(*bm, GFP_KERNEL, 0); | |
273 | ||
274 | if (result) { | |
275 | printk(KERN_ERR "Failed to create a bitmap.\n"); | |
276 | kfree(*bm); | |
277 | *bm = NULL; | |
278 | } | |
279 | ||
280 | return result; | |
281 | } | |
282 | ||
283 | /** | |
284 | * allocate_bitmaps - allocate bitmaps used to record page states | |
285 | * | |
286 | * Allocate the bitmaps we use to record the various TuxOnIce related | |
287 | * page states. | |
288 | **/ | |
289 | static int allocate_bitmaps(void) | |
290 | { | |
291 | if (toi_alloc_bitmap(&pageset1_map) || | |
292 | toi_alloc_bitmap(&pageset1_copy_map) || | |
293 | toi_alloc_bitmap(&pageset2_map) || | |
294 | toi_alloc_bitmap(&io_map) || | |
295 | toi_alloc_bitmap(&nosave_map) || | |
296 | toi_alloc_bitmap(&free_map) || toi_alloc_bitmap(&page_resave_map)) | |
297 | return 1; | |
298 | ||
299 | return 0; | |
300 | } | |
301 | ||
302 | static void toi_free_bitmap(struct memory_bitmap **bm) | |
303 | { | |
304 | if (!*bm) | |
305 | return; | |
306 | ||
307 | memory_bm_free(*bm, 0); | |
308 | kfree(*bm); | |
309 | *bm = NULL; | |
310 | } | |
311 | ||
312 | /** | |
313 | * free_bitmaps - free the bitmaps used to record page states | |
314 | * | |
315 | * Free the bitmaps allocated above. It is not an error to call | |
316 | * memory_bm_free on a bitmap that isn't currently allocated. | |
317 | **/ | |
318 | static void free_bitmaps(void) | |
319 | { | |
320 | toi_free_bitmap(&pageset1_map); | |
321 | toi_free_bitmap(&pageset1_copy_map); | |
322 | toi_free_bitmap(&pageset2_map); | |
323 | toi_free_bitmap(&io_map); | |
324 | toi_free_bitmap(&nosave_map); | |
325 | toi_free_bitmap(&free_map); | |
326 | toi_free_bitmap(&page_resave_map); | |
327 | } | |
328 | ||
329 | /** | |
330 | * io_MB_per_second - return the number of MB/s read or written | |
331 | * @write: Whether to return the speed at which we wrote. | |
332 | * | |
333 | * Calculate the number of megabytes per second that were read or written. | |
334 | **/ | |
335 | static int io_MB_per_second(int write) | |
336 | { | |
337 | return (toi_bkd.toi_io_time[write][1]) ? | |
338 | MB((unsigned long)toi_bkd.toi_io_time[write][0]) * HZ / | |
339 | toi_bkd.toi_io_time[write][1] : 0; | |
340 | } | |
341 | ||
342 | #define SNPRINTF(a...) do { len += scnprintf(((char *) buffer) + len, \ | |
343 | count - len - 1, ## a); } while (0) | |
344 | ||
345 | /** | |
346 | * get_debug_info - fill a buffer with debugging information | |
347 | * @buffer: The buffer to be filled. | |
348 | * @count: The size of the buffer, in bytes. | |
349 | * | |
350 | * Fill a (usually PAGE_SIZEd) buffer with the debugging info that we will | |
351 | * either printk or return via sysfs. | |
352 | **/ | |
353 | static int get_toi_debug_info(const char *buffer, int count) | |
354 | { | |
355 | int len = 0, i, first_result = 1; | |
356 | ||
357 | SNPRINTF("TuxOnIce debugging info:\n"); | |
358 | SNPRINTF("- TuxOnIce core : " TOI_CORE_VERSION "\n"); | |
359 | SNPRINTF("- Kernel Version : " UTS_RELEASE "\n"); | |
360 | SNPRINTF("- Compiler vers. : %d.%d\n", __GNUC__, __GNUC_MINOR__); | |
361 | SNPRINTF("- Attempt number : %d\n", nr_hibernates); | |
362 | SNPRINTF("- Parameters : %ld %ld %ld %d %ld %ld\n", | |
363 | toi_result, | |
364 | toi_bkd.toi_action, | |
365 | toi_bkd.toi_debug_state, | |
366 | toi_bkd.toi_default_console_level, image_size_limit, toi_poweroff_method); | |
367 | SNPRINTF("- Overall expected compression percentage: %d.\n", | |
368 | 100 - toi_expected_compression_ratio()); | |
369 | len += toi_print_module_debug_info(((char *)buffer) + len, count - len - 1); | |
370 | if (toi_bkd.toi_io_time[0][1]) { | |
371 | if ((io_MB_per_second(0) < 5) || (io_MB_per_second(1) < 5)) { | |
372 | SNPRINTF("- I/O speed: Write %ld KB/s", | |
373 | (KB((unsigned long)toi_bkd.toi_io_time[0][0]) * HZ / | |
374 | toi_bkd.toi_io_time[0][1])); | |
375 | if (toi_bkd.toi_io_time[1][1]) | |
376 | SNPRINTF(", Read %ld KB/s", (KB((unsigned long) | |
377 | toi_bkd.toi_io_time[1][0]) * HZ / | |
378 | toi_bkd.toi_io_time[1][1])); | |
379 | } else { | |
380 | SNPRINTF("- I/O speed: Write %ld MB/s", | |
381 | (MB((unsigned long)toi_bkd.toi_io_time[0][0]) * HZ / | |
382 | toi_bkd.toi_io_time[0][1])); | |
383 | if (toi_bkd.toi_io_time[1][1]) | |
384 | SNPRINTF(", Read %ld MB/s", (MB((unsigned long) | |
385 | toi_bkd.toi_io_time[1][0]) * HZ / | |
386 | toi_bkd.toi_io_time[1][1])); | |
387 | } | |
388 | SNPRINTF(".\n"); | |
389 | } else | |
390 | SNPRINTF("- No I/O speed stats available.\n"); | |
391 | SNPRINTF("- Extra pages : %lu used/%lu.\n", | |
392 | extra_pd1_pages_used, extra_pd1_pages_allowance); | |
393 | ||
394 | for (i = 0; i < TOI_NUM_RESULT_STATES; i++) | |
395 | if (test_result_state(i)) { | |
396 | #if 0 /* JJ: removed it */ | |
397 | SNPRINTF("%s: %s.\n", first_result ? | |
398 | "- Result " : " ", result_strings[i]); | |
399 | #endif | |
400 | first_result = 0; | |
401 | } | |
402 | if (first_result) { | |
403 | #if 0 /* JJ: removed it */ | |
404 | SNPRINTF("- Result : %s.\n", nr_hibernates ? | |
405 | "Succeeded" : "No hibernation attempts so far"); | |
406 | #endif | |
407 | } | |
408 | return len; | |
409 | } | |
410 | ||
411 | /** | |
412 | * do_cleanup - cleanup after attempting to hibernate or resume | |
413 | * @get_debug_info: Whether to allocate and return debugging info. | |
414 | * | |
415 | * Cleanup after attempting to hibernate or resume, possibly getting | |
416 | * debugging info as we do so. | |
417 | **/ | |
418 | static void do_cleanup(int get_debug_info, int restarting) | |
419 | { | |
420 | int i = 0; | |
421 | char *buffer = NULL; | |
422 | ||
423 | trap_non_toi_io = 0; | |
424 | ||
425 | if (get_debug_info) | |
426 | toi_prepare_status(DONT_CLEAR_BAR, "Cleaning up..."); | |
427 | #if !defined(HIB_TOI_DEBUG) /* turn off the verbose when debug off */ | |
428 | hib_warn("Turn off debug info\n"); | |
429 | get_debug_info = 0; | |
430 | #endif | |
431 | ||
432 | #ifdef CONFIG_TOI_ENHANCE | |
433 | toi_actual_compression_ratio(); /* keep the actual compressed ratio for reference */ | |
434 | #endif | |
435 | ||
436 | free_checksum_pages(); | |
437 | ||
438 | if (get_debug_info) | |
439 | buffer = (char *)toi_get_zeroed_page(20, TOI_ATOMIC_GFP); | |
440 | ||
441 | if (buffer) | |
442 | i = get_toi_debug_info(buffer, PAGE_SIZE); | |
443 | ||
444 | toi_free_extra_pagedir_memory(); | |
445 | ||
446 | pagedir1.size = 0; | |
447 | pagedir2.size = 0; | |
448 | set_highmem_size(pagedir1, 0); | |
449 | set_highmem_size(pagedir2, 0); | |
450 | ||
451 | if (boot_kernel_data_buffer) { | |
452 | if (!test_toi_state(TOI_BOOT_KERNEL)) | |
453 | toi_free_page(37, boot_kernel_data_buffer); | |
454 | boot_kernel_data_buffer = 0; | |
455 | } | |
456 | ||
457 | clear_toi_state(TOI_BOOT_KERNEL); | |
458 | thaw_processes(); | |
459 | ||
460 | if (!restarting) | |
461 | toi_stop_other_threads(); | |
462 | ||
463 | if (test_action_state(TOI_KEEP_IMAGE) && !test_result_state(TOI_ABORTED)) { | |
464 | toi_message(TOI_ANY_SECTION, TOI_LOW, 1, | |
465 | "TuxOnIce: Not invalidating the image due " | |
466 | "to Keep Image being enabled."); | |
467 | set_result_state(TOI_KEPT_IMAGE); | |
468 | } else if (toiActiveAllocator) | |
469 | toiActiveAllocator->remove_image(); | |
470 | ||
471 | free_bitmaps(); | |
472 | usermodehelper_enable(); | |
473 | ||
474 | if (test_toi_state(TOI_NOTIFIERS_PREPARE)) { | |
475 | pm_notifier_call_chain(PM_POST_HIBERNATION); | |
476 | clear_toi_state(TOI_NOTIFIERS_PREPARE); | |
477 | } | |
478 | ||
479 | if (buffer && i) { | |
480 | /* Printk can only handle 1023 bytes, including | |
481 | * its level mangling. */ | |
482 | for (i = 0; i < 3; i++) | |
483 | printk(KERN_ERR "%s", buffer + (1023 * i)); | |
484 | toi_free_page(20, (unsigned long)buffer); | |
485 | } | |
486 | ||
487 | if (!test_action_state(TOI_LATE_CPU_HOTPLUG)) | |
488 | enable_nonboot_cpus(); | |
489 | ||
490 | if (!restarting) | |
491 | toi_cleanup_console(); | |
492 | ||
493 | free_attention_list(); | |
494 | ||
495 | if (!restarting) | |
496 | toi_deactivate_storage(0); | |
497 | ||
498 | clear_toi_state(TOI_IGNORE_LOGLEVEL); | |
499 | clear_toi_state(TOI_TRYING_TO_RESUME); | |
500 | clear_toi_state(TOI_NOW_RESUMING); | |
501 | } | |
502 | ||
503 | /** | |
504 | * check_still_keeping_image - we kept an image; check whether to reuse it. | |
505 | * | |
506 | * We enter this routine when we have kept an image. If the user has said they | |
507 | * want to still keep it, all we need to do is powerdown. If powering down | |
508 | * means hibernating to ram and the power doesn't run out, we'll return 1. | |
509 | * If we do power off properly or the battery runs out, we'll resume via the | |
510 | * normal paths. | |
511 | * | |
512 | * If the user has said they want to remove the previously kept image, we | |
513 | * remove it, and return 0. We'll then store a new image. | |
514 | **/ | |
515 | static int check_still_keeping_image(void) | |
516 | { | |
517 | if (test_action_state(TOI_KEEP_IMAGE)) { | |
518 | printk(KERN_INFO "Image already stored: powering down " "immediately."); | |
519 | do_toi_step(STEP_HIBERNATE_POWERDOWN); | |
520 | return 1; /* Just in case we're using S3 */ | |
521 | } | |
522 | ||
523 | printk(KERN_INFO "Invalidating previous image.\n"); | |
524 | toiActiveAllocator->remove_image(); | |
525 | ||
526 | return 0; | |
527 | } | |
528 | ||
529 | /** | |
530 | * toi_init - prepare to hibernate to disk | |
531 | * | |
532 | * Initialise variables & data structures, in preparation for | |
533 | * hibernating to disk. | |
534 | **/ | |
535 | static int toi_init(int restarting) | |
536 | { | |
537 | int result, i, j; | |
538 | ||
539 | #ifdef CONFIG_TOI_FIXUP | |
540 | if (test_result_state(TOI_ABORTED)) | |
541 | return 1; | |
542 | #endif | |
543 | ||
544 | toi_result = 0; | |
545 | ||
546 | printk(KERN_INFO "Initiating a hibernation cycle.\n"); | |
547 | ||
548 | nr_hibernates++; | |
549 | ||
550 | for (i = 0; i < 2; i++) | |
551 | for (j = 0; j < 2; j++) | |
552 | toi_bkd.toi_io_time[i][j] = 0; | |
553 | ||
554 | if (!test_toi_state(TOI_CAN_HIBERNATE) || allocate_bitmaps()) | |
555 | return 1; | |
556 | ||
557 | mark_nosave_pages(); | |
558 | ||
559 | if (!restarting) | |
560 | toi_prepare_console(); | |
561 | ||
562 | result = pm_notifier_call_chain(PM_HIBERNATION_PREPARE); | |
563 | if (result) { | |
564 | set_result_state(TOI_NOTIFIERS_PREPARE_FAILED); | |
565 | return 1; | |
566 | } | |
567 | set_toi_state(TOI_NOTIFIERS_PREPARE); | |
568 | ||
569 | if (!restarting) { | |
570 | int num_threaded; | |
571 | num_threaded = toi_start_other_threads(); | |
572 | printk(KERN_ERR "Starting other threads (%d).", num_threaded); | |
573 | } | |
574 | ||
575 | result = usermodehelper_disable(); | |
576 | if (result) { | |
577 | printk(KERN_ERR "TuxOnIce: Failed to disable usermode " "helpers\n"); | |
578 | set_result_state(TOI_USERMODE_HELPERS_ERR); | |
579 | return 1; | |
580 | } | |
581 | ||
582 | boot_kernel_data_buffer = toi_get_zeroed_page(37, TOI_ATOMIC_GFP); | |
583 | if (!boot_kernel_data_buffer) { | |
584 | printk(KERN_ERR "TuxOnIce: Failed to allocate " "boot_kernel_data_buffer.\n"); | |
585 | set_result_state(TOI_OUT_OF_MEMORY); | |
586 | return 1; | |
587 | } | |
588 | ||
589 | if (!test_action_state(TOI_LATE_CPU_HOTPLUG) && disable_nonboot_cpus()) { | |
590 | set_abort_result(TOI_CPU_HOTPLUG_FAILED); | |
591 | return 1; | |
592 | } | |
593 | ||
594 | return 0; | |
595 | } | |
596 | ||
597 | /** | |
598 | * can_hibernate - perform basic 'Can we hibernate?' tests | |
599 | * | |
600 | * Perform basic tests that must pass if we're going to be able to hibernate: | |
601 | * Can we get the pm_mutex? Is resume= valid (we need to know where to write | |
602 | * the image header). | |
603 | **/ | |
604 | static int can_hibernate(void) | |
605 | { | |
606 | if (!test_toi_state(TOI_CAN_HIBERNATE)) | |
607 | toi_attempt_to_parse_resume_device(0); | |
608 | ||
609 | if (!test_toi_state(TOI_CAN_HIBERNATE)) { | |
610 | printk(KERN_INFO "TuxOnIce: Hibernation is disabled.\n" | |
611 | "This may be because you haven't put something along " | |
612 | "the lines of\n\nresume=swap:/dev/hda1\n\n" | |
613 | "in lilo.conf or equivalent. (Where /dev/hda1 is your " | |
614 | "swap partition).\n"); | |
615 | set_abort_result(TOI_CANT_SUSPEND); | |
616 | return 0; | |
617 | } | |
618 | ||
619 | if (strlen(alt_resume_param)) { | |
620 | attempt_to_parse_alt_resume_param(); | |
621 | ||
622 | if (!strlen(alt_resume_param)) { | |
623 | printk(KERN_INFO "Alternate resume parameter now " "invalid. Aborting.\n"); | |
624 | set_abort_result(TOI_CANT_USE_ALT_RESUME); | |
625 | return 0; | |
626 | } | |
627 | } | |
628 | ||
629 | hib_log("passed!\n"); | |
630 | ||
631 | return 1; | |
632 | } | |
633 | ||
634 | /** | |
635 | * do_post_image_write - having written an image, figure out what to do next | |
636 | * | |
637 | * After writing an image, we might load an alternate image or power down. | |
638 | * Powering down might involve hibernating to ram, in which case we also | |
639 | * need to handle reloading pageset2. | |
640 | **/ | |
641 | static int do_post_image_write(void) | |
642 | { | |
643 | /* If switching images fails, do normal powerdown */ | |
644 | if (alt_resume_param[0]) | |
645 | do_toi_step(STEP_RESUME_ALT_IMAGE); | |
646 | ||
647 | toi_power_down(); | |
648 | ||
649 | barrier(); | |
650 | mb(); | |
651 | return 0; | |
652 | } | |
653 | ||
654 | /** | |
655 | * __save_image - do the hard work of saving the image | |
656 | * | |
657 | * High level routine for getting the image saved. The key assumptions made | |
658 | * are that processes have been frozen and sufficient memory is available. | |
659 | * | |
660 | * We also exit through here at resume time, coming back from toi_hibernate | |
661 | * after the atomic restore. This is the reason for the toi_in_hibernate | |
662 | * test. | |
663 | **/ | |
664 | static int __save_image(void) | |
665 | { | |
666 | int temp_result, did_copy = 0; | |
667 | ||
668 | toi_prepare_status(DONT_CLEAR_BAR, "Starting to save the image.."); | |
669 | ||
670 | toi_message(TOI_ANY_SECTION, TOI_LOW, 1, | |
671 | " - Final values: %d and %d.", pagedir1.size, pagedir2.size); | |
672 | ||
673 | toi_cond_pause(1, "About to write pagedir2."); | |
674 | ||
675 | temp_result = write_pageset(&pagedir2); | |
676 | ||
677 | if (temp_result == -1 || test_result_state(TOI_ABORTED)) | |
678 | return 1; | |
679 | ||
680 | toi_cond_pause(1, "About to copy pageset 1."); | |
681 | ||
682 | if (test_result_state(TOI_ABORTED)) | |
683 | return 1; | |
684 | ||
685 | toi_deactivate_storage(1); | |
686 | ||
687 | toi_prepare_status(DONT_CLEAR_BAR, "Doing atomic copy/restore."); | |
688 | ||
689 | toi_in_hibernate = 1; | |
690 | ||
691 | if (toi_go_atomic(PMSG_FREEZE, 1)) | |
692 | goto Failed; | |
693 | ||
694 | hib_log("calling toi_hibernate()\n"); | |
695 | temp_result = toi_hibernate(); | |
696 | ||
697 | #ifdef CONFIG_KGDB | |
698 | if (test_action_state(TOI_POST_RESUME_BREAKPOINT)) | |
699 | kgdb_breakpoint(); | |
700 | #endif | |
701 | ||
702 | if (!temp_result) | |
703 | did_copy = 1; | |
704 | ||
705 | hib_log("calling toi_end_atomic() toi_in_hibernate(%d) temp_result(%d)\n", toi_in_hibernate, | |
706 | temp_result); | |
707 | /* We return here at resume time too! */ | |
708 | toi_end_atomic(ATOMIC_ALL_STEPS, toi_in_hibernate, temp_result); | |
709 | ||
710 | Failed: | |
711 | if (toi_activate_storage(1)) | |
712 | panic("Failed to reactivate our storage."); | |
713 | ||
714 | /* Resume time? */ | |
715 | if (!toi_in_hibernate) { | |
716 | hib_log("last resume here ...\n"); | |
717 | copyback_post(); | |
718 | return 0; | |
719 | } | |
720 | ||
721 | /* Nope. Hibernating. So, see if we can save the image... */ | |
722 | ||
723 | if (temp_result || test_result_state(TOI_ABORTED)) { | |
724 | if (did_copy) | |
725 | goto abort_reloading_pagedir_two; | |
726 | else | |
727 | return 1; | |
728 | } | |
729 | ||
730 | hib_log("@line:%d\n", __LINE__); | |
731 | toi_update_status(pagedir2.size, pagedir1.size + pagedir2.size, NULL); | |
732 | ||
733 | if (test_result_state(TOI_ABORTED)) | |
734 | goto abort_reloading_pagedir_two; | |
735 | ||
736 | toi_cond_pause(1, "About to write pageset1."); | |
737 | ||
738 | toi_message(TOI_ANY_SECTION, TOI_LOW, 1, "-- Writing pageset1"); | |
739 | ||
740 | temp_result = write_pageset(&pagedir1); | |
741 | ||
742 | /* We didn't overwrite any memory, so no reread needs to be done. */ | |
743 | if (test_action_state(TOI_TEST_FILTER_SPEED) || test_action_state(TOI_TEST_BIO)) | |
744 | return 1; | |
745 | ||
746 | if (temp_result == 1 || test_result_state(TOI_ABORTED)) | |
747 | goto abort_reloading_pagedir_two; | |
748 | ||
749 | toi_cond_pause(1, "About to write header."); | |
750 | ||
751 | if (test_result_state(TOI_ABORTED)) | |
752 | goto abort_reloading_pagedir_two; | |
753 | ||
754 | temp_result = write_image_header(); | |
755 | ||
756 | if (!temp_result && !test_result_state(TOI_ABORTED)) | |
757 | return 0; | |
758 | ||
759 | abort_reloading_pagedir_two: | |
760 | temp_result = read_pageset2(1); | |
761 | ||
762 | /* If that failed, we're sunk. Panic! */ | |
763 | if (temp_result) | |
764 | panic("Attempt to reload pagedir 2 while aborting " "a hibernate failed."); | |
765 | ||
766 | return 1; | |
767 | } | |
768 | ||
769 | static void map_ps2_pages(int enable) | |
770 | { | |
771 | unsigned long pfn = 0; | |
772 | ||
773 | pfn = memory_bm_next_pfn(pageset2_map); | |
774 | ||
775 | while (pfn != BM_END_OF_MAP) { | |
776 | struct page *page = pfn_to_page(pfn); | |
777 | kernel_map_pages(page, 1, enable); | |
778 | pfn = memory_bm_next_pfn(pageset2_map); | |
779 | } | |
780 | } | |
781 | ||
782 | /** | |
783 | * do_save_image - save the image and handle the result | |
784 | * | |
785 | * Save the prepared image. If we fail or we're in the path returning | |
786 | * from the atomic restore, cleanup. | |
787 | **/ | |
788 | static int do_save_image(void) | |
789 | { | |
790 | int result; | |
791 | map_ps2_pages(0); | |
792 | result = __save_image(); | |
793 | map_ps2_pages(1); | |
794 | return result; | |
795 | } | |
796 | ||
797 | /** | |
798 | * do_prepare_image - try to prepare an image | |
799 | * | |
800 | * Seek to initialise and prepare an image to be saved. On failure, | |
801 | * cleanup. | |
802 | **/ | |
803 | static int do_prepare_image(void) | |
804 | { | |
805 | int restarting = test_result_state(TOI_EXTRA_PAGES_ALLOW_TOO_SMALL); | |
806 | ||
807 | if (!restarting && toi_activate_storage(0)) | |
808 | return 1; | |
809 | ||
810 | hib_log("step 1 @line:%d\n", __LINE__); | |
811 | /* | |
812 | * If kept image and still keeping image and hibernating to RAM, we will | |
813 | * return 1 after hibernating and resuming (provided the power doesn't | |
814 | * run out. In that case, we skip directly to cleaning up and exiting. | |
815 | */ | |
816 | ||
817 | if (!can_hibernate() || (test_result_state(TOI_KEPT_IMAGE) && check_still_keeping_image())) | |
818 | return 1; | |
819 | ||
820 | hib_log("step 2 @line:%d\n", __LINE__); | |
821 | ||
822 | if (toi_init(restarting) || toi_prepare_image() || test_result_state(TOI_ABORTED)) | |
823 | return 1; | |
824 | ||
825 | hib_log("step 3 @line:%d\n", __LINE__); | |
826 | ||
827 | trap_non_toi_io = 1; | |
828 | ||
829 | return 0; | |
830 | } | |
831 | ||
832 | /** | |
833 | * do_check_can_resume - find out whether an image has been stored | |
834 | * | |
835 | * Read whether an image exists. We use the same routine as the | |
836 | * image_exists sysfs entry, and just look to see whether the | |
837 | * first character in the resulting buffer is a '1'. | |
838 | **/ | |
839 | int do_check_can_resume(void) | |
840 | { | |
841 | int result = -1; | |
842 | ||
843 | if (toi_activate_storage(0)) | |
844 | return -1; | |
845 | ||
846 | if (!test_toi_state(TOI_RESUME_DEVICE_OK)) | |
847 | toi_attempt_to_parse_resume_device(1); | |
848 | ||
849 | if (toiActiveAllocator) | |
850 | result = toiActiveAllocator->image_exists(1); | |
851 | ||
852 | toi_deactivate_storage(0); | |
853 | return result; | |
854 | } | |
855 | EXPORT_SYMBOL_GPL(do_check_can_resume); | |
856 | ||
857 | /** | |
858 | * do_load_atomic_copy - load the first part of an image, if it exists | |
859 | * | |
860 | * Check whether we have an image. If one exists, do sanity checking | |
861 | * (possibly invalidating the image or even rebooting if the user | |
862 | * requests that) before loading it into memory in preparation for the | |
863 | * atomic restore. | |
864 | * | |
865 | * If and only if we have an image loaded and ready to restore, we return 1. | |
866 | **/ | |
867 | static int do_load_atomic_copy(void) | |
868 | { | |
869 | int read_image_result = 0; | |
870 | ||
871 | if (sizeof(swp_entry_t) != sizeof(long)) { | |
872 | printk(KERN_WARNING "TuxOnIce: The size of swp_entry_t != size" | |
873 | " of long. Please report this!\n"); | |
874 | return 1; | |
875 | } | |
876 | ||
877 | if (!resume_file[0]) | |
878 | printk(KERN_WARNING "TuxOnIce: " | |
879 | "You need to use a resume= command line parameter to " | |
880 | "tell TuxOnIce where to look for an image.\n"); | |
881 | ||
882 | toi_activate_storage(0); | |
883 | ||
884 | if (!(test_toi_state(TOI_RESUME_DEVICE_OK)) && !toi_attempt_to_parse_resume_device(0)) { | |
885 | /* | |
886 | * Without a usable storage device we can do nothing - | |
887 | * even if noresume is given | |
888 | */ | |
889 | ||
890 | if (!toiNumAllocators) | |
891 | printk(KERN_ALERT "TuxOnIce: " | |
892 | "No storage allocators have been registered.\n"); | |
893 | else | |
894 | printk(KERN_ALERT "TuxOnIce: " | |
895 | "Missing or invalid storage location " | |
896 | "(resume= parameter). Please correct and " | |
897 | "rerun lilo (or equivalent) before " "hibernating.\n"); | |
898 | toi_deactivate_storage(0); | |
899 | return 1; | |
900 | } | |
901 | ||
902 | if (allocate_bitmaps()) | |
903 | return 1; | |
904 | ||
905 | read_image_result = read_pageset1(); /* non fatal error ignored */ | |
906 | ||
907 | if (test_toi_state(TOI_NORESUME_SPECIFIED)) | |
908 | clear_toi_state(TOI_NORESUME_SPECIFIED); | |
909 | ||
910 | toi_deactivate_storage(0); | |
911 | ||
912 | if (read_image_result) | |
913 | return 1; | |
914 | ||
915 | return 0; | |
916 | } | |
917 | ||
918 | /** | |
919 | * prepare_restore_load_alt_image - save & restore alt image variables | |
920 | * | |
921 | * Save and restore the pageset1 maps, when loading an alternate image. | |
922 | **/ | |
923 | static void prepare_restore_load_alt_image(int prepare) | |
924 | { | |
925 | static struct memory_bitmap *pageset1_map_save, *pageset1_copy_map_save; | |
926 | ||
927 | if (prepare) { | |
928 | pageset1_map_save = pageset1_map; | |
929 | pageset1_map = NULL; | |
930 | pageset1_copy_map_save = pageset1_copy_map; | |
931 | pageset1_copy_map = NULL; | |
932 | set_toi_state(TOI_LOADING_ALT_IMAGE); | |
933 | toi_reset_alt_image_pageset2_pfn(); | |
934 | } else { | |
935 | memory_bm_free(pageset1_map, 0); | |
936 | pageset1_map = pageset1_map_save; | |
937 | memory_bm_free(pageset1_copy_map, 0); | |
938 | pageset1_copy_map = pageset1_copy_map_save; | |
939 | clear_toi_state(TOI_NOW_RESUMING); | |
940 | clear_toi_state(TOI_LOADING_ALT_IMAGE); | |
941 | } | |
942 | } | |
943 | ||
944 | /** | |
945 | * do_toi_step - perform a step in hibernating or resuming | |
946 | * | |
947 | * Perform a step in hibernating or resuming an image. This abstraction | |
948 | * is in preparation for implementing cluster support, and perhaps replacing | |
949 | * uswsusp too (haven't looked whether that's possible yet). | |
950 | **/ | |
951 | int do_toi_step(int step) | |
952 | { | |
953 | switch (step) { | |
954 | case STEP_HIBERNATE_PREPARE_IMAGE: | |
955 | return do_prepare_image(); | |
956 | case STEP_HIBERNATE_SAVE_IMAGE: | |
957 | return do_save_image(); | |
958 | case STEP_HIBERNATE_POWERDOWN: | |
959 | return do_post_image_write(); | |
960 | case STEP_RESUME_CAN_RESUME: | |
961 | return do_check_can_resume(); | |
962 | case STEP_RESUME_LOAD_PS1: | |
963 | return do_load_atomic_copy(); | |
964 | case STEP_RESUME_DO_RESTORE: | |
965 | /* | |
966 | * If we succeed, this doesn't return. | |
967 | * Instead, we return from do_save_image() in the | |
968 | * hibernated kernel. | |
969 | */ | |
970 | return toi_atomic_restore(); | |
971 | case STEP_RESUME_ALT_IMAGE: | |
972 | printk(KERN_INFO "Trying to resume alternate image.\n"); | |
973 | toi_in_hibernate = 0; | |
974 | save_restore_alt_param(SAVE, NOQUIET); | |
975 | prepare_restore_load_alt_image(1); | |
976 | if (!do_check_can_resume()) { | |
977 | printk(KERN_INFO "Nothing to resume from.\n"); | |
978 | goto out; | |
979 | } | |
980 | if (!do_load_atomic_copy()) | |
981 | toi_atomic_restore(); | |
982 | ||
983 | printk(KERN_INFO "Failed to load image.\n"); | |
984 | out: | |
985 | prepare_restore_load_alt_image(0); | |
986 | save_restore_alt_param(RESTORE, NOQUIET); | |
987 | break; | |
988 | case STEP_CLEANUP: | |
989 | do_cleanup(1, 0); | |
990 | break; | |
991 | case STEP_QUIET_CLEANUP: | |
992 | do_cleanup(0, 0); | |
993 | break; | |
994 | } | |
995 | ||
996 | return 0; | |
997 | } | |
998 | EXPORT_SYMBOL_GPL(do_toi_step); | |
999 | ||
1000 | /* -- Functions for kickstarting a hibernate or resume --- */ | |
1001 | ||
1002 | /** | |
1003 | * toi_try_resume - try to do the steps in resuming | |
1004 | * | |
1005 | * Check if we have an image and if so try to resume. Clear the status | |
1006 | * flags too. | |
1007 | **/ | |
1008 | void toi_try_resume(void) | |
1009 | { | |
1010 | int num_threaded; | |
1011 | ||
1012 | hib_log("entering...\n"); | |
1013 | set_toi_state(TOI_TRYING_TO_RESUME); | |
1014 | resume_attempted = 1; | |
1015 | ||
1016 | current->flags |= PF_MEMALLOC; | |
1017 | ||
1018 | get_online_cpus(); /* to protect against hotplug interference */ | |
1019 | num_threaded = toi_start_other_threads(); | |
1020 | printk(KERN_ERR "[resume] Starting other threads (%d).", num_threaded); | |
1021 | ||
1022 | if (do_toi_step(STEP_RESUME_CAN_RESUME) && !do_toi_step(STEP_RESUME_LOAD_PS1)) { | |
1023 | put_online_cpus(); /* to protect against hotplug interference */ | |
1024 | do_toi_step(STEP_RESUME_DO_RESTORE); | |
1025 | } else { | |
1026 | put_online_cpus(); /* to protect against hotplug interference */ | |
1027 | } | |
1028 | ||
1029 | toi_stop_other_threads(); | |
1030 | do_cleanup(0, 0); | |
1031 | ||
1032 | current->flags &= ~PF_MEMALLOC; | |
1033 | ||
1034 | clear_toi_state(TOI_IGNORE_LOGLEVEL); | |
1035 | clear_toi_state(TOI_TRYING_TO_RESUME); | |
1036 | clear_toi_state(TOI_NOW_RESUMING); | |
1037 | } | |
1038 | ||
1039 | /** | |
1040 | * toi_sys_power_disk_try_resume - wrapper calling toi_try_resume | |
1041 | * | |
1042 | * Wrapper for when __toi_try_resume is called from swsusp resume path, | |
1043 | * rather than from echo > /sys/power/tuxonice/do_resume. | |
1044 | **/ | |
1045 | static void toi_sys_power_disk_try_resume(void) | |
1046 | { | |
1047 | resume_attempted = 1; | |
1048 | ||
1049 | /* | |
1050 | * There's a comment in kernel/power/disk.c that indicates | |
1051 | * we should be able to use mutex_lock_nested below. That | |
1052 | * doesn't seem to cut it, though, so let's just turn lockdep | |
1053 | * off for now. | |
1054 | */ | |
1055 | lockdep_off(); | |
1056 | ||
1057 | if (toi_start_anything(SYSFS_RESUMING)) | |
1058 | goto out; | |
1059 | ||
1060 | toi_try_resume(); | |
1061 | ||
1062 | /* | |
1063 | * For initramfs, we have to clear the boot time | |
1064 | * flag after trying to resume | |
1065 | */ | |
1066 | clear_toi_state(TOI_BOOT_TIME); | |
1067 | ||
1068 | toi_finish_anything(SYSFS_RESUMING); | |
1069 | out: | |
1070 | lockdep_on(); | |
1071 | } | |
1072 | ||
1073 | /** | |
1074 | * toi_try_hibernate - try to start a hibernation cycle | |
1075 | * | |
1076 | * Start a hibernation cycle, coming in from either | |
1077 | * echo > /sys/power/tuxonice/do_suspend | |
1078 | * | |
1079 | * or | |
1080 | * | |
1081 | * echo disk > /sys/power/state | |
1082 | * | |
1083 | * In the later case, we come in without pm_sem taken; in the | |
1084 | * former, it has been taken. | |
1085 | **/ | |
1086 | int toi_try_hibernate(void) | |
1087 | { | |
1088 | int result = 0, sys_power_disk = 0, retries = 0; | |
1089 | ||
1090 | if (!mutex_is_locked(&tuxonice_in_use)) { | |
1091 | /* Came in via /sys/power/disk */ | |
1092 | if (toi_start_anything(SYSFS_HIBERNATING)) | |
1093 | return -EBUSY; | |
1094 | sys_power_disk = 1; | |
1095 | } | |
1096 | ||
1097 | current->flags |= PF_MEMALLOC; | |
1098 | ||
1099 | if (test_toi_state(TOI_CLUSTER_MODE)) { | |
1100 | toi_initiate_cluster_hibernate(); | |
1101 | goto out; | |
1102 | } | |
1103 | ||
1104 | prepare: | |
1105 | result = do_toi_step(STEP_HIBERNATE_PREPARE_IMAGE); | |
1106 | hib_log("after calling do_toi_step(STEP_HIBERNATE_PREPARE_IMAGE), result(%d)\n", result); | |
1107 | ||
1108 | if (result) | |
1109 | goto out; | |
1110 | ||
1111 | if (test_action_state(TOI_FREEZER_TEST)) | |
1112 | goto out_restore_gfp_mask; | |
1113 | ||
1114 | result = do_toi_step(STEP_HIBERNATE_SAVE_IMAGE); | |
1115 | ||
1116 | if (test_result_state(TOI_EXTRA_PAGES_ALLOW_TOO_SMALL)) { | |
1117 | if (retries < 2) { | |
1118 | hib_log("failed and calling do_cleanup(0, 1)\n"); | |
1119 | do_cleanup(0, 1); | |
1120 | retries++; | |
1121 | clear_result_state(TOI_ABORTED); | |
1122 | extra_pd1_pages_allowance = extra_pd1_pages_used + 500; | |
1123 | printk(KERN_INFO "Automatically adjusting the extra" | |
1124 | " pages allowance to %ld and restarting.\n", | |
1125 | extra_pd1_pages_allowance); | |
1126 | pm_restore_gfp_mask(); | |
1127 | goto prepare; | |
1128 | } | |
1129 | ||
1130 | printk(KERN_INFO "Adjusted extra pages allowance twice and " | |
1131 | "still couldn't hibernate successfully. Giving up."); | |
1132 | } | |
1133 | ||
1134 | /* This code runs at resume time too! */ | |
1135 | if (!result && toi_in_hibernate) | |
1136 | result = do_toi_step(STEP_HIBERNATE_POWERDOWN); | |
1137 | ||
1138 | out_restore_gfp_mask: | |
1139 | pm_restore_gfp_mask(); | |
1140 | out: | |
1141 | do_cleanup(1, 0); | |
1142 | current->flags &= ~PF_MEMALLOC; | |
1143 | ||
1144 | if (sys_power_disk) | |
1145 | toi_finish_anything(SYSFS_HIBERNATING); | |
1146 | ||
1147 | return result; | |
1148 | } | |
1149 | ||
1150 | /* | |
1151 | * channel_no: If !0, -c <channel_no> is added to args (userui). | |
1152 | */ | |
1153 | int toi_launch_userspace_program(char *command, int channel_no, int wait, int debug) | |
1154 | { | |
1155 | int retval; | |
1156 | static char *envp[] = { | |
1157 | "HOME=/", | |
1158 | "TERM=linux", | |
1159 | "PATH=/sbin:/usr/sbin:/bin:/usr/bin", | |
1160 | NULL | |
1161 | }; | |
1162 | static char *argv[] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL | |
1163 | }; | |
1164 | char *channel = NULL; | |
1165 | int arg = 0, size; | |
1166 | char test_read[255]; | |
1167 | char *orig_posn = command; | |
1168 | ||
1169 | if (!strlen(orig_posn)) | |
1170 | return 1; | |
1171 | ||
1172 | if (channel_no) { | |
1173 | channel = toi_kzalloc(4, 6, GFP_KERNEL); | |
1174 | if (!channel) { | |
1175 | printk(KERN_INFO "Failed to allocate memory in " | |
1176 | "preparing to launch userspace program.\n"); | |
1177 | return 1; | |
1178 | } | |
1179 | } | |
1180 | ||
1181 | /* Up to 6 args supported */ | |
1182 | while (arg < 6) { | |
1183 | sscanf(orig_posn, "%254s", test_read); | |
1184 | size = strlen(test_read); | |
1185 | if (!(size)) | |
1186 | break; | |
1187 | argv[arg] = toi_kzalloc(5, size + 1, TOI_ATOMIC_GFP); | |
1188 | strcpy(argv[arg], test_read); | |
1189 | orig_posn += size + 1; | |
1190 | *test_read = 0; | |
1191 | arg++; | |
1192 | } | |
1193 | ||
1194 | if (channel_no) { | |
1195 | sprintf(channel, "-c%d", channel_no); | |
1196 | argv[arg] = channel; | |
1197 | } else | |
1198 | arg--; | |
1199 | ||
1200 | if (debug) { | |
1201 | argv[++arg] = toi_kzalloc(5, 8, TOI_ATOMIC_GFP); | |
1202 | strcpy(argv[arg], "--debug"); | |
1203 | } | |
1204 | ||
1205 | retval = call_usermodehelper(argv[0], argv, envp, wait); | |
1206 | ||
1207 | /* | |
1208 | * If the program reports an error, retval = 256. Don't complain | |
1209 | * about that here. | |
1210 | */ | |
1211 | if (retval && retval != 256) | |
1212 | printk(KERN_ERR "Failed to launch userspace program '%s': " | |
1213 | "Error %d\n", command, retval); | |
1214 | ||
1215 | { | |
1216 | int i; | |
1217 | for (i = 0; i < arg; i++) | |
1218 | if (argv[i] && argv[i] != channel) | |
1219 | toi_kfree(5, argv[i], sizeof(*argv[i])); | |
1220 | } | |
1221 | ||
1222 | toi_kfree(4, channel, sizeof(*channel)); | |
1223 | ||
1224 | return retval; | |
1225 | } | |
1226 | ||
1227 | #ifdef CONFIG_TOI_ENHANCE | |
1228 | int toi_abort_hibernate(void) | |
1229 | { | |
1230 | if (test_result_state(TOI_ABORTED)) | |
1231 | return 0; | |
1232 | ||
1233 | set_result_state(TOI_ABORTED); | |
1234 | ||
1235 | return 0; | |
1236 | } | |
1237 | EXPORT_SYMBOL_GPL(toi_abort_hibernate); | |
1238 | ||
1239 | int toi_hibernate_fatalerror(void) | |
1240 | { | |
1241 | if (test_result_state(TOI_ARCH_PREPARE_FAILED)) | |
1242 | return 1; | |
1243 | else | |
1244 | return 0; | |
1245 | } | |
1246 | EXPORT_SYMBOL_GPL(toi_hibernate_fatalerror); | |
1247 | #endif | |
1248 | ||
1249 | /* | |
1250 | * This array contains entries that are automatically registered at | |
1251 | * boot. Modules and the console code register their own entries separately. | |
1252 | */ | |
1253 | static struct toi_sysfs_data sysfs_params[] = { | |
1254 | SYSFS_LONG("extra_pages_allowance", SYSFS_RW, | |
1255 | &extra_pd1_pages_allowance, 0, LONG_MAX, 0), | |
1256 | SYSFS_CUSTOM("image_exists", SYSFS_RW, image_exists_read, | |
1257 | image_exists_write, SYSFS_NEEDS_SM_FOR_BOTH, NULL), | |
1258 | SYSFS_STRING("resume", SYSFS_RW, resume_file, 255, | |
1259 | SYSFS_NEEDS_SM_FOR_WRITE, | |
1260 | attempt_to_parse_resume_device2), | |
1261 | SYSFS_STRING("alt_resume_param", SYSFS_RW, alt_resume_param, 255, | |
1262 | SYSFS_NEEDS_SM_FOR_WRITE, | |
1263 | attempt_to_parse_alt_resume_param), | |
1264 | SYSFS_CUSTOM("debug_info", SYSFS_READONLY, get_toi_debug_info, NULL, 0, | |
1265 | NULL), | |
1266 | SYSFS_BIT("ignore_rootfs", SYSFS_RW, &toi_bkd.toi_action, | |
1267 | TOI_IGNORE_ROOTFS, 0), | |
1268 | SYSFS_LONG("image_size_limit", SYSFS_RW, &image_size_limit, -2, | |
1269 | INT_MAX, 0), | |
1270 | SYSFS_UL("last_result", SYSFS_RW, &toi_result, 0, 0, 0), | |
1271 | SYSFS_BIT("no_multithreaded_io", SYSFS_RW, &toi_bkd.toi_action, | |
1272 | TOI_NO_MULTITHREADED_IO, 0), | |
1273 | SYSFS_BIT("no_flusher_thread", SYSFS_RW, &toi_bkd.toi_action, | |
1274 | TOI_NO_FLUSHER_THREAD, 0), | |
1275 | SYSFS_BIT("full_pageset2", SYSFS_RW, &toi_bkd.toi_action, | |
1276 | TOI_PAGESET2_FULL, 0), | |
1277 | SYSFS_BIT("reboot", SYSFS_RW, &toi_bkd.toi_action, TOI_REBOOT, 0), | |
1278 | SYSFS_BIT("replace_swsusp", SYSFS_RW, &toi_bkd.toi_action, | |
1279 | TOI_REPLACE_SWSUSP, 0), | |
1280 | SYSFS_STRING("resume_commandline", SYSFS_RW, | |
1281 | toi_bkd.toi_nosave_commandline, COMMAND_LINE_SIZE, 0, | |
1282 | NULL), | |
1283 | SYSFS_STRING("version", SYSFS_READONLY, TOI_CORE_VERSION, 0, 0, NULL), | |
1284 | SYSFS_BIT("freezer_test", SYSFS_RW, &toi_bkd.toi_action, | |
1285 | TOI_FREEZER_TEST, 0), | |
1286 | SYSFS_BIT("test_bio", SYSFS_RW, &toi_bkd.toi_action, TOI_TEST_BIO, 0), | |
1287 | SYSFS_BIT("test_filter_speed", SYSFS_RW, &toi_bkd.toi_action, | |
1288 | TOI_TEST_FILTER_SPEED, 0), | |
1289 | SYSFS_BIT("no_pageset2", SYSFS_RW, &toi_bkd.toi_action, | |
1290 | TOI_NO_PAGESET2, 0), | |
1291 | SYSFS_BIT("no_pageset2_if_unneeded", SYSFS_RW, &toi_bkd.toi_action, | |
1292 | TOI_NO_PS2_IF_UNNEEDED, 0), | |
1293 | SYSFS_BIT("late_cpu_hotplug", SYSFS_RW, &toi_bkd.toi_action, | |
1294 | TOI_LATE_CPU_HOTPLUG, 0), | |
1295 | SYSFS_STRING("binary_signature", SYSFS_READONLY, | |
1296 | tuxonice_signature, 9, 0, NULL), | |
1297 | SYSFS_INT("max_workers", SYSFS_RW, &toi_max_workers, 0, NR_CPUS, 0, | |
1298 | NULL), | |
1299 | #ifdef CONFIG_KGDB | |
1300 | SYSFS_BIT("post_resume_breakpoint", SYSFS_RW, &toi_bkd.toi_action, | |
1301 | TOI_POST_RESUME_BREAKPOINT, 0), | |
1302 | #endif | |
1303 | SYSFS_BIT("no_readahead", SYSFS_RW, &toi_bkd.toi_action, | |
1304 | TOI_NO_READAHEAD, 0), | |
1305 | #ifdef CONFIG_TOI_KEEP_IMAGE | |
1306 | SYSFS_BIT("keep_image", SYSFS_RW, &toi_bkd.toi_action, TOI_KEEP_IMAGE, | |
1307 | 0), | |
1308 | #endif | |
1309 | }; | |
1310 | ||
1311 | static struct toi_core_fns my_fns = { | |
1312 | .get_nonconflicting_page = __toi_get_nonconflicting_page, | |
1313 | .post_context_save = __toi_post_context_save, | |
1314 | .try_hibernate = toi_try_hibernate, | |
1315 | .try_resume = toi_sys_power_disk_try_resume, | |
1316 | }; | |
1317 | ||
1318 | /** | |
1319 | * core_load - initialisation of TuxOnIce core | |
1320 | * | |
1321 | * Initialise the core, beginning with sysfs. Checksum and so on are part of | |
1322 | * the core, but have their own initialisation routines because they either | |
1323 | * aren't compiled in all the time or have their own subdirectories. | |
1324 | **/ | |
1325 | static __init int core_load(void) | |
1326 | { | |
1327 | int i, numfiles = sizeof(sysfs_params) / sizeof(struct toi_sysfs_data); | |
1328 | ||
1329 | printk(KERN_INFO "TuxOnIce " TOI_CORE_VERSION " (http://tuxonice.net)\n"); | |
1330 | ||
1331 | if (toi_sysfs_init()) | |
1332 | return 1; | |
1333 | ||
1334 | for (i = 0; i < numfiles; i++) | |
1335 | toi_register_sysfs_file(tuxonice_kobj, &sysfs_params[i]); | |
1336 | ||
1337 | toi_core_fns = &my_fns; | |
1338 | ||
1339 | if (toi_alloc_init()) | |
1340 | return 1; | |
1341 | if (toi_checksum_init()) | |
1342 | return 1; | |
1343 | if (toi_usm_init()) | |
1344 | return 1; | |
1345 | if (toi_ui_init()) | |
1346 | return 1; | |
1347 | if (toi_poweroff_init()) | |
1348 | return 1; | |
1349 | if (toi_cluster_init()) | |
1350 | return 1; | |
1351 | ||
1352 | return 0; | |
1353 | } | |
1354 | ||
1355 | #ifdef MODULE | |
1356 | /** | |
1357 | * core_unload: Prepare to unload the core code. | |
1358 | **/ | |
1359 | static __exit void core_unload(void) | |
1360 | { | |
1361 | int i, numfiles = sizeof(sysfs_params) / sizeof(struct toi_sysfs_data); | |
1362 | ||
1363 | toi_alloc_exit(); | |
1364 | toi_checksum_exit(); | |
1365 | toi_poweroff_exit(); | |
1366 | toi_ui_exit(); | |
1367 | toi_usm_exit(); | |
1368 | toi_cluster_exit(); | |
1369 | ||
1370 | for (i = 0; i < numfiles; i++) | |
1371 | toi_unregister_sysfs_file(tuxonice_kobj, &sysfs_params[i]); | |
1372 | ||
1373 | toi_core_fns = NULL; | |
1374 | ||
1375 | toi_sysfs_exit(); | |
1376 | } | |
1377 | ||
1378 | MODULE_LICENSE("GPL"); | |
1379 | module_init(core_load); | |
1380 | module_exit(core_unload); | |
1381 | #else | |
1382 | late_initcall(core_load); | |
1383 | #endif |