diff options
Diffstat (limited to 'kernel/power')
-rw-r--r-- | kernel/power/Kconfig | 2 | ||||
-rw-r--r-- | kernel/power/disk.c | 101 | ||||
-rw-r--r-- | kernel/power/main.c | 4 | ||||
-rw-r--r-- | kernel/power/power.h | 24 | ||||
-rw-r--r-- | kernel/power/snapshot.c | 89 | ||||
-rw-r--r-- | kernel/power/swsusp.c | 1020 |
6 files changed, 657 insertions, 583 deletions
diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig index 5ec248cb7f4a..9fd8d4f03595 100644 --- a/kernel/power/Kconfig +++ b/kernel/power/Kconfig | |||
@@ -38,7 +38,7 @@ config PM_DEBUG | |||
38 | 38 | ||
39 | config SOFTWARE_SUSPEND | 39 | config SOFTWARE_SUSPEND |
40 | bool "Software Suspend" | 40 | bool "Software Suspend" |
41 | depends on PM && SWAP && (X86 && (!SMP || SUSPEND_SMP)) || ((FVR || PPC32) && !SMP) | 41 | depends on PM && SWAP && (X86 && (!SMP || SUSPEND_SMP)) || ((FRV || PPC32) && !SMP) |
42 | ---help--- | 42 | ---help--- |
43 | Enable the possibility of suspending the machine. | 43 | Enable the possibility of suspending the machine. |
44 | It doesn't need APM. | 44 | It doesn't need APM. |
diff --git a/kernel/power/disk.c b/kernel/power/disk.c index 027322a564f4..e03d85e55291 100644 --- a/kernel/power/disk.c +++ b/kernel/power/disk.c | |||
@@ -24,10 +24,11 @@ | |||
24 | 24 | ||
25 | extern suspend_disk_method_t pm_disk_mode; | 25 | extern suspend_disk_method_t pm_disk_mode; |
26 | 26 | ||
27 | extern int swsusp_shrink_memory(void); | ||
27 | extern int swsusp_suspend(void); | 28 | extern int swsusp_suspend(void); |
28 | extern int swsusp_write(void); | 29 | extern int swsusp_write(struct pbe *pblist, unsigned int nr_pages); |
29 | extern int swsusp_check(void); | 30 | extern int swsusp_check(void); |
30 | extern int swsusp_read(void); | 31 | extern int swsusp_read(struct pbe **pblist_ptr); |
31 | extern void swsusp_close(void); | 32 | extern void swsusp_close(void); |
32 | extern int swsusp_resume(void); | 33 | extern int swsusp_resume(void); |
33 | 34 | ||
@@ -52,7 +53,7 @@ static void power_down(suspend_disk_method_t mode) | |||
52 | 53 | ||
53 | switch(mode) { | 54 | switch(mode) { |
54 | case PM_DISK_PLATFORM: | 55 | case PM_DISK_PLATFORM: |
55 | kernel_power_off_prepare(); | 56 | kernel_shutdown_prepare(SYSTEM_SUSPEND_DISK); |
56 | error = pm_ops->enter(PM_SUSPEND_DISK); | 57 | error = pm_ops->enter(PM_SUSPEND_DISK); |
57 | break; | 58 | break; |
58 | case PM_DISK_SHUTDOWN: | 59 | case PM_DISK_SHUTDOWN: |
@@ -73,31 +74,6 @@ static void power_down(suspend_disk_method_t mode) | |||
73 | static int in_suspend __nosavedata = 0; | 74 | static int in_suspend __nosavedata = 0; |
74 | 75 | ||
75 | 76 | ||
76 | /** | ||
77 | * free_some_memory - Try to free as much memory as possible | ||
78 | * | ||
79 | * ... but do not OOM-kill anyone | ||
80 | * | ||
81 | * Notice: all userland should be stopped at this point, or | ||
82 | * livelock is possible. | ||
83 | */ | ||
84 | |||
85 | static void free_some_memory(void) | ||
86 | { | ||
87 | unsigned int i = 0; | ||
88 | unsigned int tmp; | ||
89 | unsigned long pages = 0; | ||
90 | char *p = "-\\|/"; | ||
91 | |||
92 | printk("Freeing memory... "); | ||
93 | while ((tmp = shrink_all_memory(10000))) { | ||
94 | pages += tmp; | ||
95 | printk("\b%c", p[i++ % 4]); | ||
96 | } | ||
97 | printk("\bdone (%li pages freed)\n", pages); | ||
98 | } | ||
99 | |||
100 | |||
101 | static inline void platform_finish(void) | 77 | static inline void platform_finish(void) |
102 | { | 78 | { |
103 | if (pm_disk_mode == PM_DISK_PLATFORM) { | 79 | if (pm_disk_mode == PM_DISK_PLATFORM) { |
@@ -119,16 +95,9 @@ static int prepare_processes(void) | |||
119 | goto thaw; | 95 | goto thaw; |
120 | } | 96 | } |
121 | 97 | ||
122 | if (pm_disk_mode == PM_DISK_PLATFORM) { | ||
123 | if (pm_ops && pm_ops->prepare) { | ||
124 | if ((error = pm_ops->prepare(PM_SUSPEND_DISK))) | ||
125 | goto thaw; | ||
126 | } | ||
127 | } | ||
128 | |||
129 | /* Free memory before shutting down devices. */ | 98 | /* Free memory before shutting down devices. */ |
130 | free_some_memory(); | 99 | if (!(error = swsusp_shrink_memory())) |
131 | return 0; | 100 | return 0; |
132 | thaw: | 101 | thaw: |
133 | thaw_processes(); | 102 | thaw_processes(); |
134 | enable_nonboot_cpus(); | 103 | enable_nonboot_cpus(); |
@@ -176,7 +145,7 @@ int pm_suspend_disk(void) | |||
176 | if (in_suspend) { | 145 | if (in_suspend) { |
177 | device_resume(); | 146 | device_resume(); |
178 | pr_debug("PM: writing image.\n"); | 147 | pr_debug("PM: writing image.\n"); |
179 | error = swsusp_write(); | 148 | error = swsusp_write(pagedir_nosave, nr_copy_pages); |
180 | if (!error) | 149 | if (!error) |
181 | power_down(pm_disk_mode); | 150 | power_down(pm_disk_mode); |
182 | else { | 151 | else { |
@@ -247,7 +216,7 @@ static int software_resume(void) | |||
247 | 216 | ||
248 | pr_debug("PM: Reading swsusp image.\n"); | 217 | pr_debug("PM: Reading swsusp image.\n"); |
249 | 218 | ||
250 | if ((error = swsusp_read())) { | 219 | if ((error = swsusp_read(&pagedir_nosave))) { |
251 | swsusp_free(); | 220 | swsusp_free(); |
252 | goto Thaw; | 221 | goto Thaw; |
253 | } | 222 | } |
@@ -363,37 +332,55 @@ static ssize_t resume_show(struct subsystem * subsys, char *buf) | |||
363 | MINOR(swsusp_resume_device)); | 332 | MINOR(swsusp_resume_device)); |
364 | } | 333 | } |
365 | 334 | ||
366 | static ssize_t resume_store(struct subsystem * subsys, const char * buf, size_t n) | 335 | static ssize_t resume_store(struct subsystem *subsys, const char *buf, size_t n) |
367 | { | 336 | { |
368 | int len; | ||
369 | char *p; | ||
370 | unsigned int maj, min; | 337 | unsigned int maj, min; |
371 | int error = -EINVAL; | ||
372 | dev_t res; | 338 | dev_t res; |
339 | int ret = -EINVAL; | ||
373 | 340 | ||
374 | p = memchr(buf, '\n', n); | 341 | if (sscanf(buf, "%u:%u", &maj, &min) != 2) |
375 | len = p ? p - buf : n; | 342 | goto out; |
376 | 343 | ||
377 | if (sscanf(buf, "%u:%u", &maj, &min) == 2) { | 344 | res = MKDEV(maj,min); |
378 | res = MKDEV(maj,min); | 345 | if (maj != MAJOR(res) || min != MINOR(res)) |
379 | if (maj == MAJOR(res) && min == MINOR(res)) { | 346 | goto out; |
380 | down(&pm_sem); | ||
381 | swsusp_resume_device = res; | ||
382 | up(&pm_sem); | ||
383 | printk("Attempting manual resume\n"); | ||
384 | noresume = 0; | ||
385 | software_resume(); | ||
386 | } | ||
387 | } | ||
388 | 347 | ||
389 | return error >= 0 ? n : error; | 348 | down(&pm_sem); |
349 | swsusp_resume_device = res; | ||
350 | up(&pm_sem); | ||
351 | printk("Attempting manual resume\n"); | ||
352 | noresume = 0; | ||
353 | software_resume(); | ||
354 | ret = n; | ||
355 | out: | ||
356 | return ret; | ||
390 | } | 357 | } |
391 | 358 | ||
392 | power_attr(resume); | 359 | power_attr(resume); |
393 | 360 | ||
361 | static ssize_t image_size_show(struct subsystem * subsys, char *buf) | ||
362 | { | ||
363 | return sprintf(buf, "%u\n", image_size); | ||
364 | } | ||
365 | |||
366 | static ssize_t image_size_store(struct subsystem * subsys, const char * buf, size_t n) | ||
367 | { | ||
368 | unsigned int size; | ||
369 | |||
370 | if (sscanf(buf, "%u", &size) == 1) { | ||
371 | image_size = size; | ||
372 | return n; | ||
373 | } | ||
374 | |||
375 | return -EINVAL; | ||
376 | } | ||
377 | |||
378 | power_attr(image_size); | ||
379 | |||
394 | static struct attribute * g[] = { | 380 | static struct attribute * g[] = { |
395 | &disk_attr.attr, | 381 | &disk_attr.attr, |
396 | &resume_attr.attr, | 382 | &resume_attr.attr, |
383 | &image_size_attr.attr, | ||
397 | NULL, | 384 | NULL, |
398 | }; | 385 | }; |
399 | 386 | ||
diff --git a/kernel/power/main.c b/kernel/power/main.c index d253f3ae2fa5..9cb235cba4a9 100644 --- a/kernel/power/main.c +++ b/kernel/power/main.c | |||
@@ -133,10 +133,10 @@ static int suspend_enter(suspend_state_t state) | |||
133 | static void suspend_finish(suspend_state_t state) | 133 | static void suspend_finish(suspend_state_t state) |
134 | { | 134 | { |
135 | device_resume(); | 135 | device_resume(); |
136 | if (pm_ops && pm_ops->finish) | ||
137 | pm_ops->finish(state); | ||
138 | thaw_processes(); | 136 | thaw_processes(); |
139 | enable_nonboot_cpus(); | 137 | enable_nonboot_cpus(); |
138 | if (pm_ops && pm_ops->finish) | ||
139 | pm_ops->finish(state); | ||
140 | pm_restore_console(); | 140 | pm_restore_console(); |
141 | } | 141 | } |
142 | 142 | ||
diff --git a/kernel/power/power.h b/kernel/power/power.h index 6c042b5ee14b..7e8492fd1423 100644 --- a/kernel/power/power.h +++ b/kernel/power/power.h | |||
@@ -9,19 +9,13 @@ | |||
9 | #define SUSPEND_CONSOLE (MAX_NR_CONSOLES-1) | 9 | #define SUSPEND_CONSOLE (MAX_NR_CONSOLES-1) |
10 | #endif | 10 | #endif |
11 | 11 | ||
12 | #define MAX_PBES ((PAGE_SIZE - sizeof(struct new_utsname) \ | ||
13 | - 4 - 3*sizeof(unsigned long) - sizeof(int) \ | ||
14 | - sizeof(void *)) / sizeof(swp_entry_t)) | ||
15 | |||
16 | struct swsusp_info { | 12 | struct swsusp_info { |
17 | struct new_utsname uts; | 13 | struct new_utsname uts; |
18 | u32 version_code; | 14 | u32 version_code; |
19 | unsigned long num_physpages; | 15 | unsigned long num_physpages; |
20 | int cpus; | 16 | int cpus; |
21 | unsigned long image_pages; | 17 | unsigned long image_pages; |
22 | unsigned long pagedir_pages; | 18 | unsigned long pages; |
23 | suspend_pagedir_t * suspend_pagedir; | ||
24 | swp_entry_t pagedir[MAX_PBES]; | ||
25 | } __attribute__((aligned(PAGE_SIZE))); | 19 | } __attribute__((aligned(PAGE_SIZE))); |
26 | 20 | ||
27 | 21 | ||
@@ -48,25 +42,27 @@ static struct subsys_attribute _name##_attr = { \ | |||
48 | 42 | ||
49 | extern struct subsystem power_subsys; | 43 | extern struct subsystem power_subsys; |
50 | 44 | ||
51 | extern int freeze_processes(void); | ||
52 | extern void thaw_processes(void); | ||
53 | |||
54 | extern int pm_prepare_console(void); | 45 | extern int pm_prepare_console(void); |
55 | extern void pm_restore_console(void); | 46 | extern void pm_restore_console(void); |
56 | 47 | ||
57 | |||
58 | /* References to section boundaries */ | 48 | /* References to section boundaries */ |
59 | extern const void __nosave_begin, __nosave_end; | 49 | extern const void __nosave_begin, __nosave_end; |
60 | 50 | ||
61 | extern unsigned int nr_copy_pages; | 51 | extern unsigned int nr_copy_pages; |
62 | extern suspend_pagedir_t *pagedir_nosave; | 52 | extern struct pbe *pagedir_nosave; |
63 | extern suspend_pagedir_t *pagedir_save; | 53 | |
54 | /* Preferred image size in MB (default 500) */ | ||
55 | extern unsigned int image_size; | ||
64 | 56 | ||
65 | extern asmlinkage int swsusp_arch_suspend(void); | 57 | extern asmlinkage int swsusp_arch_suspend(void); |
66 | extern asmlinkage int swsusp_arch_resume(void); | 58 | extern asmlinkage int swsusp_arch_resume(void); |
67 | 59 | ||
60 | extern unsigned int count_data_pages(void); | ||
68 | extern void free_pagedir(struct pbe *pblist); | 61 | extern void free_pagedir(struct pbe *pblist); |
62 | extern void release_eaten_pages(void); | ||
69 | extern struct pbe *alloc_pagedir(unsigned nr_pages, gfp_t gfp_mask, int safe_needed); | 63 | extern struct pbe *alloc_pagedir(unsigned nr_pages, gfp_t gfp_mask, int safe_needed); |
70 | extern void create_pbe_list(struct pbe *pblist, unsigned nr_pages); | ||
71 | extern void swsusp_free(void); | 64 | extern void swsusp_free(void); |
72 | extern int alloc_data_pages(struct pbe *pblist, gfp_t gfp_mask, int safe_needed); | 65 | extern int alloc_data_pages(struct pbe *pblist, gfp_t gfp_mask, int safe_needed); |
66 | extern unsigned int snapshot_nr_pages(void); | ||
67 | extern struct pbe *snapshot_pblist(void); | ||
68 | extern void snapshot_pblist_set(struct pbe *pblist); | ||
diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c index 4a6dbcefd378..41f66365f0d8 100644 --- a/kernel/power/snapshot.c +++ b/kernel/power/snapshot.c | |||
@@ -33,7 +33,35 @@ | |||
33 | 33 | ||
34 | #include "power.h" | 34 | #include "power.h" |
35 | 35 | ||
36 | struct pbe *pagedir_nosave; | ||
37 | unsigned int nr_copy_pages; | ||
38 | |||
36 | #ifdef CONFIG_HIGHMEM | 39 | #ifdef CONFIG_HIGHMEM |
40 | unsigned int count_highmem_pages(void) | ||
41 | { | ||
42 | struct zone *zone; | ||
43 | unsigned long zone_pfn; | ||
44 | unsigned int n = 0; | ||
45 | |||
46 | for_each_zone (zone) | ||
47 | if (is_highmem(zone)) { | ||
48 | mark_free_pages(zone); | ||
49 | for (zone_pfn = 0; zone_pfn < zone->spanned_pages; zone_pfn++) { | ||
50 | struct page *page; | ||
51 | unsigned long pfn = zone_pfn + zone->zone_start_pfn; | ||
52 | if (!pfn_valid(pfn)) | ||
53 | continue; | ||
54 | page = pfn_to_page(pfn); | ||
55 | if (PageReserved(page)) | ||
56 | continue; | ||
57 | if (PageNosaveFree(page)) | ||
58 | continue; | ||
59 | n++; | ||
60 | } | ||
61 | } | ||
62 | return n; | ||
63 | } | ||
64 | |||
37 | struct highmem_page { | 65 | struct highmem_page { |
38 | char *data; | 66 | char *data; |
39 | struct page *page; | 67 | struct page *page; |
@@ -149,17 +177,15 @@ static int saveable(struct zone *zone, unsigned long *zone_pfn) | |||
149 | BUG_ON(PageReserved(page) && PageNosave(page)); | 177 | BUG_ON(PageReserved(page) && PageNosave(page)); |
150 | if (PageNosave(page)) | 178 | if (PageNosave(page)) |
151 | return 0; | 179 | return 0; |
152 | if (PageReserved(page) && pfn_is_nosave(pfn)) { | 180 | if (PageReserved(page) && pfn_is_nosave(pfn)) |
153 | pr_debug("[nosave pfn 0x%lx]", pfn); | ||
154 | return 0; | 181 | return 0; |
155 | } | ||
156 | if (PageNosaveFree(page)) | 182 | if (PageNosaveFree(page)) |
157 | return 0; | 183 | return 0; |
158 | 184 | ||
159 | return 1; | 185 | return 1; |
160 | } | 186 | } |
161 | 187 | ||
162 | static unsigned count_data_pages(void) | 188 | unsigned int count_data_pages(void) |
163 | { | 189 | { |
164 | struct zone *zone; | 190 | struct zone *zone; |
165 | unsigned long zone_pfn; | 191 | unsigned long zone_pfn; |
@@ -244,7 +270,7 @@ static inline void fill_pb_page(struct pbe *pbpage) | |||
244 | * of memory pages allocated with alloc_pagedir() | 270 | * of memory pages allocated with alloc_pagedir() |
245 | */ | 271 | */ |
246 | 272 | ||
247 | void create_pbe_list(struct pbe *pblist, unsigned int nr_pages) | 273 | static inline void create_pbe_list(struct pbe *pblist, unsigned int nr_pages) |
248 | { | 274 | { |
249 | struct pbe *pbpage, *p; | 275 | struct pbe *pbpage, *p; |
250 | unsigned int num = PBES_PER_PAGE; | 276 | unsigned int num = PBES_PER_PAGE; |
@@ -261,7 +287,35 @@ void create_pbe_list(struct pbe *pblist, unsigned int nr_pages) | |||
261 | p->next = p + 1; | 287 | p->next = p + 1; |
262 | p->next = NULL; | 288 | p->next = NULL; |
263 | } | 289 | } |
264 | pr_debug("create_pbe_list(): initialized %d PBEs\n", num); | 290 | } |
291 | |||
292 | /** | ||
293 | * On resume it is necessary to trace and eventually free the unsafe | ||
294 | * pages that have been allocated, because they are needed for I/O | ||
295 | * (on x86-64 we likely will "eat" these pages once again while | ||
296 | * creating the temporary page translation tables) | ||
297 | */ | ||
298 | |||
299 | struct eaten_page { | ||
300 | struct eaten_page *next; | ||
301 | char padding[PAGE_SIZE - sizeof(void *)]; | ||
302 | }; | ||
303 | |||
304 | static struct eaten_page *eaten_pages = NULL; | ||
305 | |||
306 | void release_eaten_pages(void) | ||
307 | { | ||
308 | struct eaten_page *p, *q; | ||
309 | |||
310 | p = eaten_pages; | ||
311 | while (p) { | ||
312 | q = p->next; | ||
313 | /* We don't want swsusp_free() to free this page again */ | ||
314 | ClearPageNosave(virt_to_page(p)); | ||
315 | free_page((unsigned long)p); | ||
316 | p = q; | ||
317 | } | ||
318 | eaten_pages = NULL; | ||
265 | } | 319 | } |
266 | 320 | ||
267 | /** | 321 | /** |
@@ -282,9 +336,12 @@ static inline void *alloc_image_page(gfp_t gfp_mask, int safe_needed) | |||
282 | if (safe_needed) | 336 | if (safe_needed) |
283 | do { | 337 | do { |
284 | res = (void *)get_zeroed_page(gfp_mask); | 338 | res = (void *)get_zeroed_page(gfp_mask); |
285 | if (res && PageNosaveFree(virt_to_page(res))) | 339 | if (res && PageNosaveFree(virt_to_page(res))) { |
286 | /* This is for swsusp_free() */ | 340 | /* This is for swsusp_free() */ |
287 | SetPageNosave(virt_to_page(res)); | 341 | SetPageNosave(virt_to_page(res)); |
342 | ((struct eaten_page *)res)->next = eaten_pages; | ||
343 | eaten_pages = res; | ||
344 | } | ||
288 | } while (res && PageNosaveFree(virt_to_page(res))); | 345 | } while (res && PageNosaveFree(virt_to_page(res))); |
289 | else | 346 | else |
290 | res = (void *)get_zeroed_page(gfp_mask); | 347 | res = (void *)get_zeroed_page(gfp_mask); |
@@ -332,7 +389,8 @@ struct pbe *alloc_pagedir(unsigned int nr_pages, gfp_t gfp_mask, int safe_needed | |||
332 | if (!pbe) { /* get_zeroed_page() failed */ | 389 | if (!pbe) { /* get_zeroed_page() failed */ |
333 | free_pagedir(pblist); | 390 | free_pagedir(pblist); |
334 | pblist = NULL; | 391 | pblist = NULL; |
335 | } | 392 | } else |
393 | create_pbe_list(pblist, nr_pages); | ||
336 | return pblist; | 394 | return pblist; |
337 | } | 395 | } |
338 | 396 | ||
@@ -370,8 +428,14 @@ void swsusp_free(void) | |||
370 | 428 | ||
371 | static int enough_free_mem(unsigned int nr_pages) | 429 | static int enough_free_mem(unsigned int nr_pages) |
372 | { | 430 | { |
373 | pr_debug("swsusp: available memory: %u pages\n", nr_free_pages()); | 431 | struct zone *zone; |
374 | return nr_free_pages() > (nr_pages + PAGES_FOR_IO + | 432 | unsigned int n = 0; |
433 | |||
434 | for_each_zone (zone) | ||
435 | if (!is_highmem(zone)) | ||
436 | n += zone->free_pages; | ||
437 | pr_debug("swsusp: available memory: %u pages\n", n); | ||
438 | return n > (nr_pages + PAGES_FOR_IO + | ||
375 | (nr_pages + PBES_PER_PAGE - 1) / PBES_PER_PAGE); | 439 | (nr_pages + PBES_PER_PAGE - 1) / PBES_PER_PAGE); |
376 | } | 440 | } |
377 | 441 | ||
@@ -395,7 +459,6 @@ static struct pbe *swsusp_alloc(unsigned int nr_pages) | |||
395 | printk(KERN_ERR "suspend: Allocating pagedir failed.\n"); | 459 | printk(KERN_ERR "suspend: Allocating pagedir failed.\n"); |
396 | return NULL; | 460 | return NULL; |
397 | } | 461 | } |
398 | create_pbe_list(pblist, nr_pages); | ||
399 | 462 | ||
400 | if (alloc_data_pages(pblist, GFP_ATOMIC | __GFP_COLD, 0)) { | 463 | if (alloc_data_pages(pblist, GFP_ATOMIC | __GFP_COLD, 0)) { |
401 | printk(KERN_ERR "suspend: Allocating image pages failed.\n"); | 464 | printk(KERN_ERR "suspend: Allocating image pages failed.\n"); |
@@ -421,10 +484,6 @@ asmlinkage int swsusp_save(void) | |||
421 | (nr_pages + PBES_PER_PAGE - 1) / PBES_PER_PAGE, | 484 | (nr_pages + PBES_PER_PAGE - 1) / PBES_PER_PAGE, |
422 | PAGES_FOR_IO, nr_free_pages()); | 485 | PAGES_FOR_IO, nr_free_pages()); |
423 | 486 | ||
424 | /* This is needed because of the fixed size of swsusp_info */ | ||
425 | if (MAX_PBES < (nr_pages + PBES_PER_PAGE - 1) / PBES_PER_PAGE) | ||
426 | return -ENOSPC; | ||
427 | |||
428 | if (!enough_free_mem(nr_pages)) { | 487 | if (!enough_free_mem(nr_pages)) { |
429 | printk(KERN_ERR "swsusp: Not enough free memory\n"); | 488 | printk(KERN_ERR "swsusp: Not enough free memory\n"); |
430 | return -ENOMEM; | 489 | return -ENOMEM; |
diff --git a/kernel/power/swsusp.c b/kernel/power/swsusp.c index c05f46e7348f..55a18d26abed 100644 --- a/kernel/power/swsusp.c +++ b/kernel/power/swsusp.c | |||
@@ -30,8 +30,8 @@ | |||
30 | * Alex Badea <vampire@go.ro>: | 30 | * Alex Badea <vampire@go.ro>: |
31 | * Fixed runaway init | 31 | * Fixed runaway init |
32 | * | 32 | * |
33 | * Andreas Steinmetz <ast@domdv.de>: | 33 | * Rafael J. Wysocki <rjw@sisk.pl> |
34 | * Added encrypted suspend option | 34 | * Added the swap map data structure and reworked the handling of swap |
35 | * | 35 | * |
36 | * More state savers are welcome. Especially for the scsi layer... | 36 | * More state savers are welcome. Especially for the scsi layer... |
37 | * | 37 | * |
@@ -67,44 +67,33 @@ | |||
67 | #include <asm/tlbflush.h> | 67 | #include <asm/tlbflush.h> |
68 | #include <asm/io.h> | 68 | #include <asm/io.h> |
69 | 69 | ||
70 | #include <linux/random.h> | ||
71 | #include <linux/crypto.h> | ||
72 | #include <asm/scatterlist.h> | ||
73 | |||
74 | #include "power.h" | 70 | #include "power.h" |
75 | 71 | ||
72 | /* | ||
73 | * Preferred image size in MB (tunable via /sys/power/image_size). | ||
74 | * When it is set to N, swsusp will do its best to ensure the image | ||
75 | * size will not exceed N MB, but if that is impossible, it will | ||
76 | * try to create the smallest image possible. | ||
77 | */ | ||
78 | unsigned int image_size = 500; | ||
79 | |||
76 | #ifdef CONFIG_HIGHMEM | 80 | #ifdef CONFIG_HIGHMEM |
81 | unsigned int count_highmem_pages(void); | ||
77 | int save_highmem(void); | 82 | int save_highmem(void); |
78 | int restore_highmem(void); | 83 | int restore_highmem(void); |
79 | #else | 84 | #else |
80 | static int save_highmem(void) { return 0; } | 85 | static int save_highmem(void) { return 0; } |
81 | static int restore_highmem(void) { return 0; } | 86 | static int restore_highmem(void) { return 0; } |
87 | static unsigned int count_highmem_pages(void) { return 0; } | ||
82 | #endif | 88 | #endif |
83 | 89 | ||
84 | #define CIPHER "aes" | ||
85 | #define MAXKEY 32 | ||
86 | #define MAXIV 32 | ||
87 | |||
88 | extern char resume_file[]; | 90 | extern char resume_file[]; |
89 | 91 | ||
90 | /* Local variables that should not be affected by save */ | ||
91 | unsigned int nr_copy_pages __nosavedata = 0; | ||
92 | |||
93 | /* Suspend pagedir is allocated before final copy, therefore it | ||
94 | must be freed after resume | ||
95 | |||
96 | Warning: this is even more evil than it seems. Pagedirs this file | ||
97 | talks about are completely different from page directories used by | ||
98 | MMU hardware. | ||
99 | */ | ||
100 | suspend_pagedir_t *pagedir_nosave __nosavedata = NULL; | ||
101 | |||
102 | #define SWSUSP_SIG "S1SUSPEND" | 92 | #define SWSUSP_SIG "S1SUSPEND" |
103 | 93 | ||
104 | static struct swsusp_header { | 94 | static struct swsusp_header { |
105 | char reserved[PAGE_SIZE - 20 - MAXKEY - MAXIV - sizeof(swp_entry_t)]; | 95 | char reserved[PAGE_SIZE - 20 - sizeof(swp_entry_t)]; |
106 | u8 key_iv[MAXKEY+MAXIV]; | 96 | swp_entry_t image; |
107 | swp_entry_t swsusp_info; | ||
108 | char orig_sig[10]; | 97 | char orig_sig[10]; |
109 | char sig[10]; | 98 | char sig[10]; |
110 | } __attribute__((packed, aligned(PAGE_SIZE))) swsusp_header; | 99 | } __attribute__((packed, aligned(PAGE_SIZE))) swsusp_header; |
@@ -115,140 +104,9 @@ static struct swsusp_info swsusp_info; | |||
115 | * Saving part... | 104 | * Saving part... |
116 | */ | 105 | */ |
117 | 106 | ||
118 | /* We memorize in swapfile_used what swap devices are used for suspension */ | 107 | static unsigned short root_swap = 0xffff; |
119 | #define SWAPFILE_UNUSED 0 | ||
120 | #define SWAPFILE_SUSPEND 1 /* This is the suspending device */ | ||
121 | #define SWAPFILE_IGNORED 2 /* Those are other swap devices ignored for suspension */ | ||
122 | |||
123 | static unsigned short swapfile_used[MAX_SWAPFILES]; | ||
124 | static unsigned short root_swap; | ||
125 | |||
126 | static int write_page(unsigned long addr, swp_entry_t *loc); | ||
127 | static int bio_read_page(pgoff_t page_off, void *page); | ||
128 | |||
129 | static u8 key_iv[MAXKEY+MAXIV]; | ||
130 | |||
131 | #ifdef CONFIG_SWSUSP_ENCRYPT | ||
132 | |||
133 | static int crypto_init(int mode, void **mem) | ||
134 | { | ||
135 | int error = 0; | ||
136 | int len; | ||
137 | char *modemsg; | ||
138 | struct crypto_tfm *tfm; | ||
139 | |||
140 | modemsg = mode ? "suspend not possible" : "resume not possible"; | ||
141 | |||
142 | tfm = crypto_alloc_tfm(CIPHER, CRYPTO_TFM_MODE_CBC); | ||
143 | if(!tfm) { | ||
144 | printk(KERN_ERR "swsusp: no tfm, %s\n", modemsg); | ||
145 | error = -EINVAL; | ||
146 | goto out; | ||
147 | } | ||
148 | |||
149 | if(MAXKEY < crypto_tfm_alg_min_keysize(tfm)) { | ||
150 | printk(KERN_ERR "swsusp: key buffer too small, %s\n", modemsg); | ||
151 | error = -ENOKEY; | ||
152 | goto fail; | ||
153 | } | ||
154 | |||
155 | if (mode) | ||
156 | get_random_bytes(key_iv, MAXKEY+MAXIV); | ||
157 | |||
158 | len = crypto_tfm_alg_max_keysize(tfm); | ||
159 | if (len > MAXKEY) | ||
160 | len = MAXKEY; | ||
161 | |||
162 | if (crypto_cipher_setkey(tfm, key_iv, len)) { | ||
163 | printk(KERN_ERR "swsusp: key setup failure, %s\n", modemsg); | ||
164 | error = -EKEYREJECTED; | ||
165 | goto fail; | ||
166 | } | ||
167 | |||
168 | len = crypto_tfm_alg_ivsize(tfm); | ||
169 | |||
170 | if (MAXIV < len) { | ||
171 | printk(KERN_ERR "swsusp: iv buffer too small, %s\n", modemsg); | ||
172 | error = -EOVERFLOW; | ||
173 | goto fail; | ||
174 | } | ||
175 | |||
176 | crypto_cipher_set_iv(tfm, key_iv+MAXKEY, len); | ||
177 | |||
178 | *mem=(void *)tfm; | ||
179 | |||
180 | goto out; | ||
181 | |||
182 | fail: crypto_free_tfm(tfm); | ||
183 | out: return error; | ||
184 | } | ||
185 | |||
186 | static __inline__ void crypto_exit(void *mem) | ||
187 | { | ||
188 | crypto_free_tfm((struct crypto_tfm *)mem); | ||
189 | } | ||
190 | |||
191 | static __inline__ int crypto_write(struct pbe *p, void *mem) | ||
192 | { | ||
193 | int error = 0; | ||
194 | struct scatterlist src, dst; | ||
195 | |||
196 | src.page = virt_to_page(p->address); | ||
197 | src.offset = 0; | ||
198 | src.length = PAGE_SIZE; | ||
199 | dst.page = virt_to_page((void *)&swsusp_header); | ||
200 | dst.offset = 0; | ||
201 | dst.length = PAGE_SIZE; | ||
202 | |||
203 | error = crypto_cipher_encrypt((struct crypto_tfm *)mem, &dst, &src, | ||
204 | PAGE_SIZE); | ||
205 | |||
206 | if (!error) | ||
207 | error = write_page((unsigned long)&swsusp_header, | ||
208 | &(p->swap_address)); | ||
209 | return error; | ||
210 | } | ||
211 | |||
212 | static __inline__ int crypto_read(struct pbe *p, void *mem) | ||
213 | { | ||
214 | int error = 0; | ||
215 | struct scatterlist src, dst; | ||
216 | |||
217 | error = bio_read_page(swp_offset(p->swap_address), (void *)p->address); | ||
218 | if (!error) { | ||
219 | src.offset = 0; | ||
220 | src.length = PAGE_SIZE; | ||
221 | dst.offset = 0; | ||
222 | dst.length = PAGE_SIZE; | ||
223 | src.page = dst.page = virt_to_page((void *)p->address); | ||
224 | |||
225 | error = crypto_cipher_decrypt((struct crypto_tfm *)mem, &dst, | ||
226 | &src, PAGE_SIZE); | ||
227 | } | ||
228 | return error; | ||
229 | } | ||
230 | #else | ||
231 | static __inline__ int crypto_init(int mode, void *mem) | ||
232 | { | ||
233 | return 0; | ||
234 | } | ||
235 | |||
236 | static __inline__ void crypto_exit(void *mem) | ||
237 | { | ||
238 | } | ||
239 | |||
240 | static __inline__ int crypto_write(struct pbe *p, void *mem) | ||
241 | { | ||
242 | return write_page(p->address, &(p->swap_address)); | ||
243 | } | ||
244 | 108 | ||
245 | static __inline__ int crypto_read(struct pbe *p, void *mem) | 109 | static int mark_swapfiles(swp_entry_t start) |
246 | { | ||
247 | return bio_read_page(swp_offset(p->swap_address), (void *)p->address); | ||
248 | } | ||
249 | #endif | ||
250 | |||
251 | static int mark_swapfiles(swp_entry_t prev) | ||
252 | { | 110 | { |
253 | int error; | 111 | int error; |
254 | 112 | ||
@@ -259,8 +117,7 @@ static int mark_swapfiles(swp_entry_t prev) | |||
259 | !memcmp("SWAPSPACE2",swsusp_header.sig, 10)) { | 117 | !memcmp("SWAPSPACE2",swsusp_header.sig, 10)) { |
260 | memcpy(swsusp_header.orig_sig,swsusp_header.sig, 10); | 118 | memcpy(swsusp_header.orig_sig,swsusp_header.sig, 10); |
261 | memcpy(swsusp_header.sig,SWSUSP_SIG, 10); | 119 | memcpy(swsusp_header.sig,SWSUSP_SIG, 10); |
262 | memcpy(swsusp_header.key_iv, key_iv, MAXKEY+MAXIV); | 120 | swsusp_header.image = start; |
263 | swsusp_header.swsusp_info = prev; | ||
264 | error = rw_swap_page_sync(WRITE, | 121 | error = rw_swap_page_sync(WRITE, |
265 | swp_entry(root_swap, 0), | 122 | swp_entry(root_swap, 0), |
266 | virt_to_page((unsigned long) | 123 | virt_to_page((unsigned long) |
@@ -283,7 +140,7 @@ static int mark_swapfiles(swp_entry_t prev) | |||
283 | * devfs, since the resume code can only recognize the form /dev/hda4, | 140 | * devfs, since the resume code can only recognize the form /dev/hda4, |
284 | * but the suspend code would see the long name.) | 141 | * but the suspend code would see the long name.) |
285 | */ | 142 | */ |
286 | static int is_resume_device(const struct swap_info_struct *swap_info) | 143 | static inline int is_resume_device(const struct swap_info_struct *swap_info) |
287 | { | 144 | { |
288 | struct file *file = swap_info->swap_file; | 145 | struct file *file = swap_info->swap_file; |
289 | struct inode *inode = file->f_dentry->d_inode; | 146 | struct inode *inode = file->f_dentry->d_inode; |
@@ -294,54 +151,22 @@ static int is_resume_device(const struct swap_info_struct *swap_info) | |||
294 | 151 | ||
295 | static int swsusp_swap_check(void) /* This is called before saving image */ | 152 | static int swsusp_swap_check(void) /* This is called before saving image */ |
296 | { | 153 | { |
297 | int i, len; | ||
298 | |||
299 | len=strlen(resume_file); | ||
300 | root_swap = 0xFFFF; | ||
301 | |||
302 | spin_lock(&swap_lock); | ||
303 | for (i=0; i<MAX_SWAPFILES; i++) { | ||
304 | if (!(swap_info[i].flags & SWP_WRITEOK)) { | ||
305 | swapfile_used[i]=SWAPFILE_UNUSED; | ||
306 | } else { | ||
307 | if (!len) { | ||
308 | printk(KERN_WARNING "resume= option should be used to set suspend device" ); | ||
309 | if (root_swap == 0xFFFF) { | ||
310 | swapfile_used[i] = SWAPFILE_SUSPEND; | ||
311 | root_swap = i; | ||
312 | } else | ||
313 | swapfile_used[i] = SWAPFILE_IGNORED; | ||
314 | } else { | ||
315 | /* we ignore all swap devices that are not the resume_file */ | ||
316 | if (is_resume_device(&swap_info[i])) { | ||
317 | swapfile_used[i] = SWAPFILE_SUSPEND; | ||
318 | root_swap = i; | ||
319 | } else { | ||
320 | swapfile_used[i] = SWAPFILE_IGNORED; | ||
321 | } | ||
322 | } | ||
323 | } | ||
324 | } | ||
325 | spin_unlock(&swap_lock); | ||
326 | return (root_swap != 0xffff) ? 0 : -ENODEV; | ||
327 | } | ||
328 | |||
329 | /** | ||
330 | * This is called after saving image so modification | ||
331 | * will be lost after resume... and that's what we want. | ||
332 | * we make the device unusable. A new call to | ||
333 | * lock_swapdevices can unlock the devices. | ||
334 | */ | ||
335 | static void lock_swapdevices(void) | ||
336 | { | ||
337 | int i; | 154 | int i; |
338 | 155 | ||
156 | if (!swsusp_resume_device) | ||
157 | return -ENODEV; | ||
339 | spin_lock(&swap_lock); | 158 | spin_lock(&swap_lock); |
340 | for (i = 0; i< MAX_SWAPFILES; i++) | 159 | for (i = 0; i < MAX_SWAPFILES; i++) { |
341 | if (swapfile_used[i] == SWAPFILE_IGNORED) { | 160 | if (!(swap_info[i].flags & SWP_WRITEOK)) |
342 | swap_info[i].flags ^= SWP_WRITEOK; | 161 | continue; |
162 | if (is_resume_device(swap_info + i)) { | ||
163 | spin_unlock(&swap_lock); | ||
164 | root_swap = i; | ||
165 | return 0; | ||
343 | } | 166 | } |
167 | } | ||
344 | spin_unlock(&swap_lock); | 168 | spin_unlock(&swap_lock); |
169 | return -ENODEV; | ||
345 | } | 170 | } |
346 | 171 | ||
347 | /** | 172 | /** |
@@ -359,72 +184,217 @@ static void lock_swapdevices(void) | |||
359 | static int write_page(unsigned long addr, swp_entry_t *loc) | 184 | static int write_page(unsigned long addr, swp_entry_t *loc) |
360 | { | 185 | { |
361 | swp_entry_t entry; | 186 | swp_entry_t entry; |
362 | int error = 0; | 187 | int error = -ENOSPC; |
363 | 188 | ||
364 | entry = get_swap_page(); | 189 | entry = get_swap_page_of_type(root_swap); |
365 | if (swp_offset(entry) && | 190 | if (swp_offset(entry)) { |
366 | swapfile_used[swp_type(entry)] == SWAPFILE_SUSPEND) { | 191 | error = rw_swap_page_sync(WRITE, entry, virt_to_page(addr)); |
367 | error = rw_swap_page_sync(WRITE, entry, | 192 | if (!error || error == -EIO) |
368 | virt_to_page(addr)); | ||
369 | if (error == -EIO) | ||
370 | error = 0; | ||
371 | if (!error) | ||
372 | *loc = entry; | 193 | *loc = entry; |
373 | } else | 194 | } |
374 | error = -ENOSPC; | ||
375 | return error; | 195 | return error; |
376 | } | 196 | } |
377 | 197 | ||
378 | /** | 198 | /** |
379 | * data_free - Free the swap entries used by the saved image. | 199 | * Swap map-handling functions |
200 | * | ||
201 | * The swap map is a data structure used for keeping track of each page | ||
202 | * written to the swap. It consists of many swap_map_page structures | ||
203 | * that contain each an array of MAP_PAGE_SIZE swap entries. | ||
204 | * These structures are linked together with the help of either the | ||
205 | * .next (in memory) or the .next_swap (in swap) member. | ||
380 | * | 206 | * |
381 | * Walk the list of used swap entries and free each one. | 207 | * The swap map is created during suspend. At that time we need to keep |
382 | * This is only used for cleanup when suspend fails. | 208 | * it in memory, because we have to free all of the allocated swap |
209 | * entries if an error occurs. The memory needed is preallocated | ||
210 | * so that we know in advance if there's enough of it. | ||
211 | * | ||
212 | * The first swap_map_page structure is filled with the swap entries that | ||
213 | * correspond to the first MAP_PAGE_SIZE data pages written to swap and | ||
214 | * so on. After the all of the data pages have been written, the order | ||
215 | * of the swap_map_page structures in the map is reversed so that they | ||
216 | * can be read from swap in the original order. This causes the data | ||
217 | * pages to be loaded in exactly the same order in which they have been | ||
218 | * saved. | ||
219 | * | ||
220 | * During resume we only need to use one swap_map_page structure | ||
221 | * at a time, which means that we only need to use two memory pages for | ||
222 | * reading the image - one for reading the swap_map_page structures | ||
223 | * and the second for reading the data pages from swap. | ||
383 | */ | 224 | */ |
384 | static void data_free(void) | 225 | |
226 | #define MAP_PAGE_SIZE ((PAGE_SIZE - sizeof(swp_entry_t) - sizeof(void *)) \ | ||
227 | / sizeof(swp_entry_t)) | ||
228 | |||
229 | struct swap_map_page { | ||
230 | swp_entry_t entries[MAP_PAGE_SIZE]; | ||
231 | swp_entry_t next_swap; | ||
232 | struct swap_map_page *next; | ||
233 | }; | ||
234 | |||
235 | static inline void free_swap_map(struct swap_map_page *swap_map) | ||
385 | { | 236 | { |
386 | swp_entry_t entry; | 237 | struct swap_map_page *swp; |
387 | struct pbe *p; | ||
388 | 238 | ||
389 | for_each_pbe (p, pagedir_nosave) { | 239 | while (swap_map) { |
390 | entry = p->swap_address; | 240 | swp = swap_map->next; |
391 | if (entry.val) | 241 | free_page((unsigned long)swap_map); |
392 | swap_free(entry); | 242 | swap_map = swp; |
393 | else | ||
394 | break; | ||
395 | } | 243 | } |
396 | } | 244 | } |
397 | 245 | ||
246 | static struct swap_map_page *alloc_swap_map(unsigned int nr_pages) | ||
247 | { | ||
248 | struct swap_map_page *swap_map, *swp; | ||
249 | unsigned n = 0; | ||
250 | |||
251 | if (!nr_pages) | ||
252 | return NULL; | ||
253 | |||
254 | pr_debug("alloc_swap_map(): nr_pages = %d\n", nr_pages); | ||
255 | swap_map = (struct swap_map_page *)get_zeroed_page(GFP_ATOMIC); | ||
256 | swp = swap_map; | ||
257 | for (n = MAP_PAGE_SIZE; n < nr_pages; n += MAP_PAGE_SIZE) { | ||
258 | swp->next = (struct swap_map_page *)get_zeroed_page(GFP_ATOMIC); | ||
259 | swp = swp->next; | ||
260 | if (!swp) { | ||
261 | free_swap_map(swap_map); | ||
262 | return NULL; | ||
263 | } | ||
264 | } | ||
265 | return swap_map; | ||
266 | } | ||
267 | |||
398 | /** | 268 | /** |
399 | * data_write - Write saved image to swap. | 269 | * reverse_swap_map - reverse the order of pages in the swap map |
400 | * | 270 | * @swap_map |
401 | * Walk the list of pages in the image and sync each one to swap. | ||
402 | */ | 271 | */ |
403 | static int data_write(void) | 272 | |
273 | static inline struct swap_map_page *reverse_swap_map(struct swap_map_page *swap_map) | ||
404 | { | 274 | { |
405 | int error = 0, i = 0; | 275 | struct swap_map_page *prev, *next; |
406 | unsigned int mod = nr_copy_pages / 100; | 276 | |
407 | struct pbe *p; | 277 | prev = NULL; |
408 | void *tfm; | 278 | while (swap_map) { |
279 | next = swap_map->next; | ||
280 | swap_map->next = prev; | ||
281 | prev = swap_map; | ||
282 | swap_map = next; | ||
283 | } | ||
284 | return prev; | ||
285 | } | ||
409 | 286 | ||
410 | if ((error = crypto_init(1, &tfm))) | 287 | /** |
411 | return error; | 288 | * free_swap_map_entries - free the swap entries allocated to store |
289 | * the swap map @swap_map (this is only called in case of an error) | ||
290 | */ | ||
291 | static inline void free_swap_map_entries(struct swap_map_page *swap_map) | ||
292 | { | ||
293 | while (swap_map) { | ||
294 | if (swap_map->next_swap.val) | ||
295 | swap_free(swap_map->next_swap); | ||
296 | swap_map = swap_map->next; | ||
297 | } | ||
298 | } | ||
412 | 299 | ||
413 | if (!mod) | 300 | /** |
414 | mod = 1; | 301 | * save_swap_map - save the swap map used for tracing the data pages |
302 | * stored in the swap | ||
303 | */ | ||
415 | 304 | ||
416 | printk( "Writing data to swap (%d pages)... ", nr_copy_pages ); | 305 | static int save_swap_map(struct swap_map_page *swap_map, swp_entry_t *start) |
417 | for_each_pbe (p, pagedir_nosave) { | 306 | { |
418 | if (!(i%mod)) | 307 | swp_entry_t entry = (swp_entry_t){0}; |
419 | printk( "\b\b\b\b%3d%%", i / mod ); | 308 | int error; |
420 | if ((error = crypto_write(p, tfm))) { | 309 | |
421 | crypto_exit(tfm); | 310 | while (swap_map) { |
311 | swap_map->next_swap = entry; | ||
312 | if ((error = write_page((unsigned long)swap_map, &entry))) | ||
422 | return error; | 313 | return error; |
423 | } | 314 | swap_map = swap_map->next; |
424 | i++; | ||
425 | } | 315 | } |
426 | printk("\b\b\b\bdone\n"); | 316 | *start = entry; |
427 | crypto_exit(tfm); | 317 | return 0; |
318 | } | ||
319 | |||
320 | /** | ||
321 | * free_image_entries - free the swap entries allocated to store | ||
322 | * the image data pages (this is only called in case of an error) | ||
323 | */ | ||
324 | |||
325 | static inline void free_image_entries(struct swap_map_page *swp) | ||
326 | { | ||
327 | unsigned k; | ||
328 | |||
329 | while (swp) { | ||
330 | for (k = 0; k < MAP_PAGE_SIZE; k++) | ||
331 | if (swp->entries[k].val) | ||
332 | swap_free(swp->entries[k]); | ||
333 | swp = swp->next; | ||
334 | } | ||
335 | } | ||
336 | |||
337 | /** | ||
338 | * The swap_map_handle structure is used for handling the swap map in | ||
339 | * a file-alike way | ||
340 | */ | ||
341 | |||
342 | struct swap_map_handle { | ||
343 | struct swap_map_page *cur; | ||
344 | unsigned int k; | ||
345 | }; | ||
346 | |||
347 | static inline void init_swap_map_handle(struct swap_map_handle *handle, | ||
348 | struct swap_map_page *map) | ||
349 | { | ||
350 | handle->cur = map; | ||
351 | handle->k = 0; | ||
352 | } | ||
353 | |||
354 | static inline int swap_map_write_page(struct swap_map_handle *handle, | ||
355 | unsigned long addr) | ||
356 | { | ||
357 | int error; | ||
358 | |||
359 | error = write_page(addr, handle->cur->entries + handle->k); | ||
360 | if (error) | ||
361 | return error; | ||
362 | if (++handle->k >= MAP_PAGE_SIZE) { | ||
363 | handle->cur = handle->cur->next; | ||
364 | handle->k = 0; | ||
365 | } | ||
366 | return 0; | ||
367 | } | ||
368 | |||
369 | /** | ||
370 | * save_image_data - save the data pages pointed to by the PBEs | ||
371 | * from the list @pblist using the swap map handle @handle | ||
372 | * (assume there are @nr_pages data pages to save) | ||
373 | */ | ||
374 | |||
375 | static int save_image_data(struct pbe *pblist, | ||
376 | struct swap_map_handle *handle, | ||
377 | unsigned int nr_pages) | ||
378 | { | ||
379 | unsigned int m; | ||
380 | struct pbe *p; | ||
381 | int error = 0; | ||
382 | |||
383 | printk("Saving image data pages (%u pages) ... ", nr_pages); | ||
384 | m = nr_pages / 100; | ||
385 | if (!m) | ||
386 | m = 1; | ||
387 | nr_pages = 0; | ||
388 | for_each_pbe (p, pblist) { | ||
389 | error = swap_map_write_page(handle, p->address); | ||
390 | if (error) | ||
391 | break; | ||
392 | if (!(nr_pages % m)) | ||
393 | printk("\b\b\b\b%3d%%", nr_pages / m); | ||
394 | nr_pages++; | ||
395 | } | ||
396 | if (!error) | ||
397 | printk("\b\b\b\bdone\n"); | ||
428 | return error; | 398 | return error; |
429 | } | 399 | } |
430 | 400 | ||
@@ -440,70 +410,70 @@ static void dump_info(void) | |||
440 | pr_debug(" swsusp: UTS Domain: %s\n",swsusp_info.uts.domainname); | 410 | pr_debug(" swsusp: UTS Domain: %s\n",swsusp_info.uts.domainname); |
441 | pr_debug(" swsusp: CPUs: %d\n",swsusp_info.cpus); | 411 | pr_debug(" swsusp: CPUs: %d\n",swsusp_info.cpus); |
442 | pr_debug(" swsusp: Image: %ld Pages\n",swsusp_info.image_pages); | 412 | pr_debug(" swsusp: Image: %ld Pages\n",swsusp_info.image_pages); |
443 | pr_debug(" swsusp: Pagedir: %ld Pages\n",swsusp_info.pagedir_pages); | 413 | pr_debug(" swsusp: Total: %ld Pages\n", swsusp_info.pages); |
444 | } | 414 | } |
445 | 415 | ||
446 | static void init_header(void) | 416 | static void init_header(unsigned int nr_pages) |
447 | { | 417 | { |
448 | memset(&swsusp_info, 0, sizeof(swsusp_info)); | 418 | memset(&swsusp_info, 0, sizeof(swsusp_info)); |
449 | swsusp_info.version_code = LINUX_VERSION_CODE; | 419 | swsusp_info.version_code = LINUX_VERSION_CODE; |
450 | swsusp_info.num_physpages = num_physpages; | 420 | swsusp_info.num_physpages = num_physpages; |
451 | memcpy(&swsusp_info.uts, &system_utsname, sizeof(system_utsname)); | 421 | memcpy(&swsusp_info.uts, &system_utsname, sizeof(system_utsname)); |
452 | 422 | ||
453 | swsusp_info.suspend_pagedir = pagedir_nosave; | ||
454 | swsusp_info.cpus = num_online_cpus(); | 423 | swsusp_info.cpus = num_online_cpus(); |
455 | swsusp_info.image_pages = nr_copy_pages; | 424 | swsusp_info.image_pages = nr_pages; |
456 | } | 425 | swsusp_info.pages = nr_pages + |
457 | 426 | ((nr_pages * sizeof(long) + PAGE_SIZE - 1) >> PAGE_SHIFT) + 1; | |
458 | static int close_swap(void) | ||
459 | { | ||
460 | swp_entry_t entry; | ||
461 | int error; | ||
462 | |||
463 | dump_info(); | ||
464 | error = write_page((unsigned long)&swsusp_info, &entry); | ||
465 | if (!error) { | ||
466 | printk( "S" ); | ||
467 | error = mark_swapfiles(entry); | ||
468 | printk( "|\n" ); | ||
469 | } | ||
470 | return error; | ||
471 | } | 427 | } |
472 | 428 | ||
473 | /** | 429 | /** |
474 | * free_pagedir_entries - Free pages used by the page directory. | 430 | * pack_orig_addresses - the .orig_address fields of the PBEs from the |
475 | * | 431 | * list starting at @pbe are stored in the array @buf[] (1 page) |
476 | * This is used during suspend for error recovery. | ||
477 | */ | 432 | */ |
478 | 433 | ||
479 | static void free_pagedir_entries(void) | 434 | static inline struct pbe *pack_orig_addresses(unsigned long *buf, |
435 | struct pbe *pbe) | ||
480 | { | 436 | { |
481 | int i; | 437 | int j; |
482 | 438 | ||
483 | for (i = 0; i < swsusp_info.pagedir_pages; i++) | 439 | for (j = 0; j < PAGE_SIZE / sizeof(long) && pbe; j++) { |
484 | swap_free(swsusp_info.pagedir[i]); | 440 | buf[j] = pbe->orig_address; |
441 | pbe = pbe->next; | ||
442 | } | ||
443 | if (!pbe) | ||
444 | for (; j < PAGE_SIZE / sizeof(long); j++) | ||
445 | buf[j] = 0; | ||
446 | return pbe; | ||
485 | } | 447 | } |
486 | 448 | ||
487 | |||
488 | /** | 449 | /** |
489 | * write_pagedir - Write the array of pages holding the page directory. | 450 | * save_image_metadata - save the .orig_address fields of the PBEs |
490 | * @last: Last swap entry we write (needed for header). | 451 | * from the list @pblist using the swap map handle @handle |
491 | */ | 452 | */ |
492 | 453 | ||
493 | static int write_pagedir(void) | 454 | static int save_image_metadata(struct pbe *pblist, |
455 | struct swap_map_handle *handle) | ||
494 | { | 456 | { |
495 | int error = 0; | 457 | unsigned long *buf; |
496 | unsigned int n = 0; | 458 | unsigned int n = 0; |
497 | struct pbe *pbe; | 459 | struct pbe *p; |
460 | int error = 0; | ||
498 | 461 | ||
499 | printk( "Writing pagedir..."); | 462 | printk("Saving image metadata ... "); |
500 | for_each_pb_page (pbe, pagedir_nosave) { | 463 | buf = (unsigned long *)get_zeroed_page(GFP_ATOMIC); |
501 | if ((error = write_page((unsigned long)pbe, &swsusp_info.pagedir[n++]))) | 464 | if (!buf) |
502 | return error; | 465 | return -ENOMEM; |
466 | p = pblist; | ||
467 | while (p) { | ||
468 | p = pack_orig_addresses(buf, p); | ||
469 | error = swap_map_write_page(handle, (unsigned long)buf); | ||
470 | if (error) | ||
471 | break; | ||
472 | n++; | ||
503 | } | 473 | } |
504 | 474 | free_page((unsigned long)buf); | |
505 | swsusp_info.pagedir_pages = n; | 475 | if (!error) |
506 | printk("done (%u pages)\n", n); | 476 | printk("done (%u pages saved)\n", n); |
507 | return error; | 477 | return error; |
508 | } | 478 | } |
509 | 479 | ||
@@ -511,75 +481,125 @@ static int write_pagedir(void) | |||
511 | * enough_swap - Make sure we have enough swap to save the image. | 481 | * enough_swap - Make sure we have enough swap to save the image. |
512 | * | 482 | * |
513 | * Returns TRUE or FALSE after checking the total amount of swap | 483 | * Returns TRUE or FALSE after checking the total amount of swap |
514 | * space avaiable. | 484 | * space avaiable from the resume partition. |
515 | * | ||
516 | * FIXME: si_swapinfo(&i) returns all swap devices information. | ||
517 | * We should only consider resume_device. | ||
518 | */ | 485 | */ |
519 | 486 | ||
520 | static int enough_swap(unsigned int nr_pages) | 487 | static int enough_swap(unsigned int nr_pages) |
521 | { | 488 | { |
522 | struct sysinfo i; | 489 | unsigned int free_swap = swap_info[root_swap].pages - |
490 | swap_info[root_swap].inuse_pages; | ||
523 | 491 | ||
524 | si_swapinfo(&i); | 492 | pr_debug("swsusp: free swap pages: %u\n", free_swap); |
525 | pr_debug("swsusp: available swap: %lu pages\n", i.freeswap); | 493 | return free_swap > (nr_pages + PAGES_FOR_IO + |
526 | return i.freeswap > (nr_pages + PAGES_FOR_IO + | ||
527 | (nr_pages + PBES_PER_PAGE - 1) / PBES_PER_PAGE); | 494 | (nr_pages + PBES_PER_PAGE - 1) / PBES_PER_PAGE); |
528 | } | 495 | } |
529 | 496 | ||
530 | /** | 497 | /** |
531 | * write_suspend_image - Write entire image and metadata. | 498 | * swsusp_write - Write entire image and metadata. |
532 | * | 499 | * |
500 | * It is important _NOT_ to umount filesystems at this point. We want | ||
501 | * them synced (in case something goes wrong) but we DO not want to mark | ||
502 | * filesystem clean: it is not. (And it does not matter, if we resume | ||
503 | * correctly, we'll mark system clean, anyway.) | ||
533 | */ | 504 | */ |
534 | static int write_suspend_image(void) | 505 | |
506 | int swsusp_write(struct pbe *pblist, unsigned int nr_pages) | ||
535 | { | 507 | { |
508 | struct swap_map_page *swap_map; | ||
509 | struct swap_map_handle handle; | ||
510 | swp_entry_t start; | ||
536 | int error; | 511 | int error; |
537 | 512 | ||
538 | if (!enough_swap(nr_copy_pages)) { | 513 | if ((error = swsusp_swap_check())) { |
514 | printk(KERN_ERR "swsusp: Cannot find swap device, try swapon -a.\n"); | ||
515 | return error; | ||
516 | } | ||
517 | if (!enough_swap(nr_pages)) { | ||
539 | printk(KERN_ERR "swsusp: Not enough free swap\n"); | 518 | printk(KERN_ERR "swsusp: Not enough free swap\n"); |
540 | return -ENOSPC; | 519 | return -ENOSPC; |
541 | } | 520 | } |
542 | 521 | ||
543 | init_header(); | 522 | init_header(nr_pages); |
544 | if ((error = data_write())) | 523 | swap_map = alloc_swap_map(swsusp_info.pages); |
545 | goto FreeData; | 524 | if (!swap_map) |
525 | return -ENOMEM; | ||
526 | init_swap_map_handle(&handle, swap_map); | ||
527 | |||
528 | error = swap_map_write_page(&handle, (unsigned long)&swsusp_info); | ||
529 | if (!error) | ||
530 | error = save_image_metadata(pblist, &handle); | ||
531 | if (!error) | ||
532 | error = save_image_data(pblist, &handle, nr_pages); | ||
533 | if (error) | ||
534 | goto Free_image_entries; | ||
546 | 535 | ||
547 | if ((error = write_pagedir())) | 536 | swap_map = reverse_swap_map(swap_map); |
548 | goto FreePagedir; | 537 | error = save_swap_map(swap_map, &start); |
538 | if (error) | ||
539 | goto Free_map_entries; | ||
549 | 540 | ||
550 | if ((error = close_swap())) | 541 | dump_info(); |
551 | goto FreePagedir; | 542 | printk( "S" ); |
552 | Done: | 543 | error = mark_swapfiles(start); |
553 | memset(key_iv, 0, MAXKEY+MAXIV); | 544 | printk( "|\n" ); |
545 | if (error) | ||
546 | goto Free_map_entries; | ||
547 | |||
548 | Free_swap_map: | ||
549 | free_swap_map(swap_map); | ||
554 | return error; | 550 | return error; |
555 | FreePagedir: | 551 | |
556 | free_pagedir_entries(); | 552 | Free_map_entries: |
557 | FreeData: | 553 | free_swap_map_entries(swap_map); |
558 | data_free(); | 554 | Free_image_entries: |
559 | goto Done; | 555 | free_image_entries(swap_map); |
556 | goto Free_swap_map; | ||
560 | } | 557 | } |
561 | 558 | ||
562 | /* It is important _NOT_ to umount filesystems at this point. We want | 559 | /** |
563 | * them synced (in case something goes wrong) but we DO not want to mark | 560 | * swsusp_shrink_memory - Try to free as much memory as needed |
564 | * filesystem clean: it is not. (And it does not matter, if we resume | 561 | * |
565 | * correctly, we'll mark system clean, anyway.) | 562 | * ... but do not OOM-kill anyone |
563 | * | ||
564 | * Notice: all userland should be stopped before it is called, or | ||
565 | * livelock is possible. | ||
566 | */ | 566 | */ |
567 | int swsusp_write(void) | ||
568 | { | ||
569 | int error; | ||
570 | 567 | ||
571 | if ((error = swsusp_swap_check())) { | 568 | #define SHRINK_BITE 10000 |
572 | printk(KERN_ERR "swsusp: cannot find swap device, try swapon -a.\n"); | ||
573 | return error; | ||
574 | } | ||
575 | lock_swapdevices(); | ||
576 | error = write_suspend_image(); | ||
577 | /* This will unlock ignored swap devices since writing is finished */ | ||
578 | lock_swapdevices(); | ||
579 | return error; | ||
580 | } | ||
581 | 569 | ||
570 | int swsusp_shrink_memory(void) | ||
571 | { | ||
572 | long size, tmp; | ||
573 | struct zone *zone; | ||
574 | unsigned long pages = 0; | ||
575 | unsigned int i = 0; | ||
576 | char *p = "-\\|/"; | ||
577 | |||
578 | printk("Shrinking memory... "); | ||
579 | do { | ||
580 | size = 2 * count_highmem_pages(); | ||
581 | size += size / 50 + count_data_pages(); | ||
582 | size += (size + PBES_PER_PAGE - 1) / PBES_PER_PAGE + | ||
583 | PAGES_FOR_IO; | ||
584 | tmp = size; | ||
585 | for_each_zone (zone) | ||
586 | if (!is_highmem(zone)) | ||
587 | tmp -= zone->free_pages; | ||
588 | if (tmp > 0) { | ||
589 | tmp = shrink_all_memory(SHRINK_BITE); | ||
590 | if (!tmp) | ||
591 | return -ENOMEM; | ||
592 | pages += tmp; | ||
593 | } else if (size > (image_size * 1024 * 1024) / PAGE_SIZE) { | ||
594 | tmp = shrink_all_memory(SHRINK_BITE); | ||
595 | pages += tmp; | ||
596 | } | ||
597 | printk("\b%c", p[i++%4]); | ||
598 | } while (tmp > 0); | ||
599 | printk("\bdone (%lu pages freed)\n", pages); | ||
582 | 600 | ||
601 | return 0; | ||
602 | } | ||
583 | 603 | ||
584 | int swsusp_suspend(void) | 604 | int swsusp_suspend(void) |
585 | { | 605 | { |
@@ -677,7 +697,6 @@ static void copy_page_backup_list(struct pbe *dst, struct pbe *src) | |||
677 | /* We assume both lists contain the same number of elements */ | 697 | /* We assume both lists contain the same number of elements */ |
678 | while (src) { | 698 | while (src) { |
679 | dst->orig_address = src->orig_address; | 699 | dst->orig_address = src->orig_address; |
680 | dst->swap_address = src->swap_address; | ||
681 | dst = dst->next; | 700 | dst = dst->next; |
682 | src = src->next; | 701 | src = src->next; |
683 | } | 702 | } |
@@ -757,198 +776,224 @@ static int bio_write_page(pgoff_t page_off, void *page) | |||
757 | return submit(WRITE, page_off, page); | 776 | return submit(WRITE, page_off, page); |
758 | } | 777 | } |
759 | 778 | ||
760 | /* | 779 | /** |
761 | * Sanity check if this image makes sense with this kernel/swap context | 780 | * The following functions allow us to read data using a swap map |
762 | * I really don't think that it's foolproof but more than nothing.. | 781 | * in a file-alike way |
763 | */ | 782 | */ |
764 | 783 | ||
765 | static const char *sanity_check(void) | 784 | static inline void release_swap_map_reader(struct swap_map_handle *handle) |
766 | { | 785 | { |
767 | dump_info(); | 786 | if (handle->cur) |
768 | if (swsusp_info.version_code != LINUX_VERSION_CODE) | 787 | free_page((unsigned long)handle->cur); |
769 | return "kernel version"; | 788 | handle->cur = NULL; |
770 | if (swsusp_info.num_physpages != num_physpages) | ||
771 | return "memory size"; | ||
772 | if (strcmp(swsusp_info.uts.sysname,system_utsname.sysname)) | ||
773 | return "system type"; | ||
774 | if (strcmp(swsusp_info.uts.release,system_utsname.release)) | ||
775 | return "kernel release"; | ||
776 | if (strcmp(swsusp_info.uts.version,system_utsname.version)) | ||
777 | return "version"; | ||
778 | if (strcmp(swsusp_info.uts.machine,system_utsname.machine)) | ||
779 | return "machine"; | ||
780 | #if 0 | ||
781 | /* We can't use number of online CPUs when we use hotplug to remove them ;-))) */ | ||
782 | if (swsusp_info.cpus != num_possible_cpus()) | ||
783 | return "number of cpus"; | ||
784 | #endif | ||
785 | return NULL; | ||
786 | } | 789 | } |
787 | 790 | ||
788 | 791 | static inline int get_swap_map_reader(struct swap_map_handle *handle, | |
789 | static int check_header(void) | 792 | swp_entry_t start) |
790 | { | 793 | { |
791 | const char *reason = NULL; | ||
792 | int error; | 794 | int error; |
793 | 795 | ||
794 | if ((error = bio_read_page(swp_offset(swsusp_header.swsusp_info), &swsusp_info))) | 796 | if (!swp_offset(start)) |
797 | return -EINVAL; | ||
798 | handle->cur = (struct swap_map_page *)get_zeroed_page(GFP_ATOMIC); | ||
799 | if (!handle->cur) | ||
800 | return -ENOMEM; | ||
801 | error = bio_read_page(swp_offset(start), handle->cur); | ||
802 | if (error) { | ||
803 | release_swap_map_reader(handle); | ||
795 | return error; | 804 | return error; |
796 | |||
797 | /* Is this same machine? */ | ||
798 | if ((reason = sanity_check())) { | ||
799 | printk(KERN_ERR "swsusp: Resume mismatch: %s\n",reason); | ||
800 | return -EPERM; | ||
801 | } | 805 | } |
802 | nr_copy_pages = swsusp_info.image_pages; | 806 | handle->k = 0; |
803 | return error; | 807 | return 0; |
804 | } | 808 | } |
805 | 809 | ||
806 | static int check_sig(void) | 810 | static inline int swap_map_read_page(struct swap_map_handle *handle, void *buf) |
807 | { | 811 | { |
812 | unsigned long offset; | ||
808 | int error; | 813 | int error; |
809 | 814 | ||
810 | memset(&swsusp_header, 0, sizeof(swsusp_header)); | 815 | if (!handle->cur) |
811 | if ((error = bio_read_page(0, &swsusp_header))) | 816 | return -EINVAL; |
812 | return error; | 817 | offset = swp_offset(handle->cur->entries[handle->k]); |
813 | if (!memcmp(SWSUSP_SIG, swsusp_header.sig, 10)) { | 818 | if (!offset) |
814 | memcpy(swsusp_header.sig, swsusp_header.orig_sig, 10); | ||
815 | memcpy(key_iv, swsusp_header.key_iv, MAXKEY+MAXIV); | ||
816 | memset(swsusp_header.key_iv, 0, MAXKEY+MAXIV); | ||
817 | |||
818 | /* | ||
819 | * Reset swap signature now. | ||
820 | */ | ||
821 | error = bio_write_page(0, &swsusp_header); | ||
822 | } else { | ||
823 | return -EINVAL; | 819 | return -EINVAL; |
820 | error = bio_read_page(offset, buf); | ||
821 | if (error) | ||
822 | return error; | ||
823 | if (++handle->k >= MAP_PAGE_SIZE) { | ||
824 | handle->k = 0; | ||
825 | offset = swp_offset(handle->cur->next_swap); | ||
826 | if (!offset) | ||
827 | release_swap_map_reader(handle); | ||
828 | else | ||
829 | error = bio_read_page(offset, handle->cur); | ||
824 | } | 830 | } |
825 | if (!error) | ||
826 | pr_debug("swsusp: Signature found, resuming\n"); | ||
827 | return error; | 831 | return error; |
828 | } | 832 | } |
829 | 833 | ||
830 | /** | 834 | static int check_header(void) |
831 | * data_read - Read image pages from swap. | ||
832 | * | ||
833 | * You do not need to check for overlaps, check_pagedir() | ||
834 | * already did that. | ||
835 | */ | ||
836 | |||
837 | static int data_read(struct pbe *pblist) | ||
838 | { | 835 | { |
839 | struct pbe *p; | 836 | char *reason = NULL; |
840 | int error = 0; | ||
841 | int i = 0; | ||
842 | int mod = swsusp_info.image_pages / 100; | ||
843 | void *tfm; | ||
844 | |||
845 | if ((error = crypto_init(0, &tfm))) | ||
846 | return error; | ||
847 | |||
848 | if (!mod) | ||
849 | mod = 1; | ||
850 | |||
851 | printk("swsusp: Reading image data (%lu pages): ", | ||
852 | swsusp_info.image_pages); | ||
853 | |||
854 | for_each_pbe (p, pblist) { | ||
855 | if (!(i % mod)) | ||
856 | printk("\b\b\b\b%3d%%", i / mod); | ||
857 | 837 | ||
858 | if ((error = crypto_read(p, tfm))) { | 838 | dump_info(); |
859 | crypto_exit(tfm); | 839 | if (swsusp_info.version_code != LINUX_VERSION_CODE) |
860 | return error; | 840 | reason = "kernel version"; |
861 | } | 841 | if (swsusp_info.num_physpages != num_physpages) |
862 | 842 | reason = "memory size"; | |
863 | i++; | 843 | if (strcmp(swsusp_info.uts.sysname,system_utsname.sysname)) |
844 | reason = "system type"; | ||
845 | if (strcmp(swsusp_info.uts.release,system_utsname.release)) | ||
846 | reason = "kernel release"; | ||
847 | if (strcmp(swsusp_info.uts.version,system_utsname.version)) | ||
848 | reason = "version"; | ||
849 | if (strcmp(swsusp_info.uts.machine,system_utsname.machine)) | ||
850 | reason = "machine"; | ||
851 | if (reason) { | ||
852 | printk(KERN_ERR "swsusp: Resume mismatch: %s\n", reason); | ||
853 | return -EPERM; | ||
864 | } | 854 | } |
865 | printk("\b\b\b\bdone\n"); | 855 | return 0; |
866 | crypto_exit(tfm); | ||
867 | return error; | ||
868 | } | 856 | } |
869 | 857 | ||
870 | /** | 858 | /** |
871 | * read_pagedir - Read page backup list pages from swap | 859 | * load_image_data - load the image data using the swap map handle |
860 | * @handle and store them using the page backup list @pblist | ||
861 | * (assume there are @nr_pages pages to load) | ||
872 | */ | 862 | */ |
873 | 863 | ||
874 | static int read_pagedir(struct pbe *pblist) | 864 | static int load_image_data(struct pbe *pblist, |
865 | struct swap_map_handle *handle, | ||
866 | unsigned int nr_pages) | ||
875 | { | 867 | { |
876 | struct pbe *pbpage, *p; | ||
877 | unsigned int i = 0; | ||
878 | int error; | 868 | int error; |
869 | unsigned int m; | ||
870 | struct pbe *p; | ||
879 | 871 | ||
880 | if (!pblist) | 872 | if (!pblist) |
881 | return -EFAULT; | 873 | return -EINVAL; |
882 | 874 | printk("Loading image data pages (%u pages) ... ", nr_pages); | |
883 | printk("swsusp: Reading pagedir (%lu pages)\n", | 875 | m = nr_pages / 100; |
884 | swsusp_info.pagedir_pages); | 876 | if (!m) |
885 | 877 | m = 1; | |
886 | for_each_pb_page (pbpage, pblist) { | 878 | nr_pages = 0; |
887 | unsigned long offset = swp_offset(swsusp_info.pagedir[i++]); | 879 | p = pblist; |
888 | 880 | while (p) { | |
889 | error = -EFAULT; | 881 | error = swap_map_read_page(handle, (void *)p->address); |
890 | if (offset) { | ||
891 | p = (pbpage + PB_PAGE_SKIP)->next; | ||
892 | error = bio_read_page(offset, (void *)pbpage); | ||
893 | (pbpage + PB_PAGE_SKIP)->next = p; | ||
894 | } | ||
895 | if (error) | 882 | if (error) |
896 | break; | 883 | break; |
884 | p = p->next; | ||
885 | if (!(nr_pages % m)) | ||
886 | printk("\b\b\b\b%3d%%", nr_pages / m); | ||
887 | nr_pages++; | ||
897 | } | 888 | } |
898 | |||
899 | if (!error) | 889 | if (!error) |
900 | BUG_ON(i != swsusp_info.pagedir_pages); | 890 | printk("\b\b\b\bdone\n"); |
901 | |||
902 | return error; | 891 | return error; |
903 | } | 892 | } |
904 | 893 | ||
894 | /** | ||
895 | * unpack_orig_addresses - copy the elements of @buf[] (1 page) to | ||
896 | * the PBEs in the list starting at @pbe | ||
897 | */ | ||
905 | 898 | ||
906 | static int check_suspend_image(void) | 899 | static inline struct pbe *unpack_orig_addresses(unsigned long *buf, |
900 | struct pbe *pbe) | ||
907 | { | 901 | { |
908 | int error = 0; | 902 | int j; |
909 | 903 | ||
910 | if ((error = check_sig())) | 904 | for (j = 0; j < PAGE_SIZE / sizeof(long) && pbe; j++) { |
911 | return error; | 905 | pbe->orig_address = buf[j]; |
912 | 906 | pbe = pbe->next; | |
913 | if ((error = check_header())) | 907 | } |
914 | return error; | 908 | return pbe; |
915 | |||
916 | return 0; | ||
917 | } | 909 | } |
918 | 910 | ||
919 | static int read_suspend_image(void) | 911 | /** |
912 | * load_image_metadata - load the image metadata using the swap map | ||
913 | * handle @handle and put them into the PBEs in the list @pblist | ||
914 | */ | ||
915 | |||
916 | static int load_image_metadata(struct pbe *pblist, struct swap_map_handle *handle) | ||
920 | { | 917 | { |
921 | int error = 0; | ||
922 | struct pbe *p; | 918 | struct pbe *p; |
919 | unsigned long *buf; | ||
920 | unsigned int n = 0; | ||
921 | int error = 0; | ||
923 | 922 | ||
924 | if (!(p = alloc_pagedir(nr_copy_pages, GFP_ATOMIC, 0))) | 923 | printk("Loading image metadata ... "); |
924 | buf = (unsigned long *)get_zeroed_page(GFP_ATOMIC); | ||
925 | if (!buf) | ||
925 | return -ENOMEM; | 926 | return -ENOMEM; |
926 | 927 | p = pblist; | |
927 | if ((error = read_pagedir(p))) | 928 | while (p) { |
928 | return error; | 929 | error = swap_map_read_page(handle, buf); |
929 | create_pbe_list(p, nr_copy_pages); | 930 | if (error) |
930 | mark_unsafe_pages(p); | 931 | break; |
931 | pagedir_nosave = alloc_pagedir(nr_copy_pages, GFP_ATOMIC, 1); | 932 | p = unpack_orig_addresses(buf, p); |
932 | if (pagedir_nosave) { | 933 | n++; |
933 | create_pbe_list(pagedir_nosave, nr_copy_pages); | ||
934 | copy_page_backup_list(pagedir_nosave, p); | ||
935 | } | 934 | } |
936 | free_pagedir(p); | 935 | free_page((unsigned long)buf); |
937 | if (!pagedir_nosave) | 936 | if (!error) |
938 | return -ENOMEM; | 937 | printk("done (%u pages loaded)\n", n); |
938 | return error; | ||
939 | } | ||
939 | 940 | ||
940 | /* Allocate memory for the image and read the data from swap */ | 941 | int swsusp_read(struct pbe **pblist_ptr) |
942 | { | ||
943 | int error; | ||
944 | struct pbe *p, *pblist; | ||
945 | struct swap_map_handle handle; | ||
946 | unsigned int nr_pages; | ||
941 | 947 | ||
942 | error = alloc_data_pages(pagedir_nosave, GFP_ATOMIC, 1); | 948 | if (IS_ERR(resume_bdev)) { |
949 | pr_debug("swsusp: block device not initialised\n"); | ||
950 | return PTR_ERR(resume_bdev); | ||
951 | } | ||
943 | 952 | ||
953 | error = get_swap_map_reader(&handle, swsusp_header.image); | ||
944 | if (!error) | 954 | if (!error) |
945 | error = data_read(pagedir_nosave); | 955 | error = swap_map_read_page(&handle, &swsusp_info); |
956 | if (!error) | ||
957 | error = check_header(); | ||
958 | if (error) | ||
959 | return error; | ||
960 | nr_pages = swsusp_info.image_pages; | ||
961 | p = alloc_pagedir(nr_pages, GFP_ATOMIC, 0); | ||
962 | if (!p) | ||
963 | return -ENOMEM; | ||
964 | error = load_image_metadata(p, &handle); | ||
965 | if (!error) { | ||
966 | mark_unsafe_pages(p); | ||
967 | pblist = alloc_pagedir(nr_pages, GFP_ATOMIC, 1); | ||
968 | if (pblist) | ||
969 | copy_page_backup_list(pblist, p); | ||
970 | free_pagedir(p); | ||
971 | if (!pblist) | ||
972 | error = -ENOMEM; | ||
973 | |||
974 | /* Allocate memory for the image and read the data from swap */ | ||
975 | if (!error) | ||
976 | error = alloc_data_pages(pblist, GFP_ATOMIC, 1); | ||
977 | if (!error) { | ||
978 | release_eaten_pages(); | ||
979 | error = load_image_data(pblist, &handle, nr_pages); | ||
980 | } | ||
981 | if (!error) | ||
982 | *pblist_ptr = pblist; | ||
983 | } | ||
984 | release_swap_map_reader(&handle); | ||
946 | 985 | ||
986 | blkdev_put(resume_bdev); | ||
987 | |||
988 | if (!error) | ||
989 | pr_debug("swsusp: Reading resume file was successful\n"); | ||
990 | else | ||
991 | pr_debug("swsusp: Error %d resuming\n", error); | ||
947 | return error; | 992 | return error; |
948 | } | 993 | } |
949 | 994 | ||
950 | /** | 995 | /** |
951 | * swsusp_check - Check for saved image in swap | 996 | * swsusp_check - Check for swsusp signature in the resume device |
952 | */ | 997 | */ |
953 | 998 | ||
954 | int swsusp_check(void) | 999 | int swsusp_check(void) |
@@ -958,40 +1003,27 @@ int swsusp_check(void) | |||
958 | resume_bdev = open_by_devnum(swsusp_resume_device, FMODE_READ); | 1003 | resume_bdev = open_by_devnum(swsusp_resume_device, FMODE_READ); |
959 | if (!IS_ERR(resume_bdev)) { | 1004 | if (!IS_ERR(resume_bdev)) { |
960 | set_blocksize(resume_bdev, PAGE_SIZE); | 1005 | set_blocksize(resume_bdev, PAGE_SIZE); |
961 | error = check_suspend_image(); | 1006 | memset(&swsusp_header, 0, sizeof(swsusp_header)); |
1007 | if ((error = bio_read_page(0, &swsusp_header))) | ||
1008 | return error; | ||
1009 | if (!memcmp(SWSUSP_SIG, swsusp_header.sig, 10)) { | ||
1010 | memcpy(swsusp_header.sig, swsusp_header.orig_sig, 10); | ||
1011 | /* Reset swap signature now */ | ||
1012 | error = bio_write_page(0, &swsusp_header); | ||
1013 | } else { | ||
1014 | return -EINVAL; | ||
1015 | } | ||
962 | if (error) | 1016 | if (error) |
963 | blkdev_put(resume_bdev); | 1017 | blkdev_put(resume_bdev); |
964 | } else | 1018 | else |
1019 | pr_debug("swsusp: Signature found, resuming\n"); | ||
1020 | } else { | ||
965 | error = PTR_ERR(resume_bdev); | 1021 | error = PTR_ERR(resume_bdev); |
966 | |||
967 | if (!error) | ||
968 | pr_debug("swsusp: resume file found\n"); | ||
969 | else | ||
970 | pr_debug("swsusp: Error %d check for resume file\n", error); | ||
971 | return error; | ||
972 | } | ||
973 | |||
974 | /** | ||
975 | * swsusp_read - Read saved image from swap. | ||
976 | */ | ||
977 | |||
978 | int swsusp_read(void) | ||
979 | { | ||
980 | int error; | ||
981 | |||
982 | if (IS_ERR(resume_bdev)) { | ||
983 | pr_debug("swsusp: block device not initialised\n"); | ||
984 | return PTR_ERR(resume_bdev); | ||
985 | } | 1022 | } |
986 | 1023 | ||
987 | error = read_suspend_image(); | 1024 | if (error) |
988 | blkdev_put(resume_bdev); | 1025 | pr_debug("swsusp: Error %d check for resume file\n", error); |
989 | memset(key_iv, 0, MAXKEY+MAXIV); | ||
990 | 1026 | ||
991 | if (!error) | ||
992 | pr_debug("swsusp: Reading resume file was successful\n"); | ||
993 | else | ||
994 | pr_debug("swsusp: Error %d resuming\n", error); | ||
995 | return error; | 1027 | return error; |
996 | } | 1028 | } |
997 | 1029 | ||