diff options
-rw-r--r-- | include/linux/suspend.h | 6 | ||||
-rw-r--r-- | kernel/power/disk.c | 8 | ||||
-rw-r--r-- | kernel/power/power.h | 13 | ||||
-rw-r--r-- | kernel/power/snapshot.c | 14 | ||||
-rw-r--r-- | kernel/power/swsusp.c | 558 |
5 files changed, 418 insertions, 181 deletions
diff --git a/include/linux/suspend.h b/include/linux/suspend.h index a61c04f804b2..33bbaea23aaf 100644 --- a/include/linux/suspend.h +++ b/include/linux/suspend.h | |||
@@ -14,11 +14,7 @@ | |||
14 | typedef struct pbe { | 14 | typedef struct pbe { |
15 | unsigned long address; /* address of the copy */ | 15 | unsigned long address; /* address of the copy */ |
16 | unsigned long orig_address; /* original address of page */ | 16 | unsigned long orig_address; /* original address of page */ |
17 | swp_entry_t swap_address; | 17 | struct pbe *next; |
18 | |||
19 | struct pbe *next; /* also used as scratch space at | ||
20 | * end of page (see link, diskpage) | ||
21 | */ | ||
22 | } suspend_pagedir_t; | 18 | } suspend_pagedir_t; |
23 | 19 | ||
24 | #define for_each_pbe(pbe, pblist) \ | 20 | #define for_each_pbe(pbe, pblist) \ |
diff --git a/kernel/power/disk.c b/kernel/power/disk.c index 4d944b281b28..76a5131b0e80 100644 --- a/kernel/power/disk.c +++ b/kernel/power/disk.c | |||
@@ -25,9 +25,9 @@ | |||
25 | extern suspend_disk_method_t pm_disk_mode; | 25 | extern suspend_disk_method_t pm_disk_mode; |
26 | 26 | ||
27 | extern int swsusp_suspend(void); | 27 | extern int swsusp_suspend(void); |
28 | extern int swsusp_write(void); | 28 | extern int swsusp_write(struct pbe *pblist, unsigned int nr_pages); |
29 | extern int swsusp_check(void); | 29 | extern int swsusp_check(void); |
30 | extern int swsusp_read(void); | 30 | extern int swsusp_read(struct pbe **pblist_ptr); |
31 | extern void swsusp_close(void); | 31 | extern void swsusp_close(void); |
32 | extern int swsusp_resume(void); | 32 | extern int swsusp_resume(void); |
33 | 33 | ||
@@ -176,7 +176,7 @@ int pm_suspend_disk(void) | |||
176 | if (in_suspend) { | 176 | if (in_suspend) { |
177 | device_resume(); | 177 | device_resume(); |
178 | pr_debug("PM: writing image.\n"); | 178 | pr_debug("PM: writing image.\n"); |
179 | error = swsusp_write(); | 179 | error = swsusp_write(pagedir_nosave, nr_copy_pages); |
180 | if (!error) | 180 | if (!error) |
181 | power_down(pm_disk_mode); | 181 | power_down(pm_disk_mode); |
182 | else { | 182 | else { |
@@ -247,7 +247,7 @@ static int software_resume(void) | |||
247 | 247 | ||
248 | pr_debug("PM: Reading swsusp image.\n"); | 248 | pr_debug("PM: Reading swsusp image.\n"); |
249 | 249 | ||
250 | if ((error = swsusp_read())) { | 250 | if ((error = swsusp_read(&pagedir_nosave))) { |
251 | swsusp_free(); | 251 | swsusp_free(); |
252 | goto Thaw; | 252 | goto Thaw; |
253 | } | 253 | } |
diff --git a/kernel/power/power.h b/kernel/power/power.h index 6c042b5ee14b..977877c6dcfc 100644 --- a/kernel/power/power.h +++ b/kernel/power/power.h | |||
@@ -9,19 +9,14 @@ | |||
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; | 19 | swp_entry_t start; |
24 | swp_entry_t pagedir[MAX_PBES]; | ||
25 | } __attribute__((aligned(PAGE_SIZE))); | 20 | } __attribute__((aligned(PAGE_SIZE))); |
26 | 21 | ||
27 | 22 | ||
@@ -67,6 +62,8 @@ extern asmlinkage int swsusp_arch_resume(void); | |||
67 | 62 | ||
68 | extern void free_pagedir(struct pbe *pblist); | 63 | extern void free_pagedir(struct pbe *pblist); |
69 | extern struct pbe *alloc_pagedir(unsigned nr_pages, gfp_t gfp_mask, int safe_needed); | 64 | 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); | 65 | extern void swsusp_free(void); |
72 | extern int alloc_data_pages(struct pbe *pblist, gfp_t gfp_mask, int safe_needed); | 66 | extern int alloc_data_pages(struct pbe *pblist, gfp_t gfp_mask, int safe_needed); |
67 | extern unsigned int snapshot_nr_pages(void); | ||
68 | extern struct pbe *snapshot_pblist(void); | ||
69 | extern void snapshot_pblist_set(struct pbe *pblist); | ||
diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c index 4a6dbcefd378..152d56cdf017 100644 --- a/kernel/power/snapshot.c +++ b/kernel/power/snapshot.c | |||
@@ -33,6 +33,9 @@ | |||
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 |
37 | struct highmem_page { | 40 | struct highmem_page { |
38 | char *data; | 41 | char *data; |
@@ -244,7 +247,7 @@ static inline void fill_pb_page(struct pbe *pbpage) | |||
244 | * of memory pages allocated with alloc_pagedir() | 247 | * of memory pages allocated with alloc_pagedir() |
245 | */ | 248 | */ |
246 | 249 | ||
247 | void create_pbe_list(struct pbe *pblist, unsigned int nr_pages) | 250 | static inline void create_pbe_list(struct pbe *pblist, unsigned int nr_pages) |
248 | { | 251 | { |
249 | struct pbe *pbpage, *p; | 252 | struct pbe *pbpage, *p; |
250 | unsigned int num = PBES_PER_PAGE; | 253 | unsigned int num = PBES_PER_PAGE; |
@@ -261,7 +264,6 @@ void create_pbe_list(struct pbe *pblist, unsigned int nr_pages) | |||
261 | p->next = p + 1; | 264 | p->next = p + 1; |
262 | p->next = NULL; | 265 | p->next = NULL; |
263 | } | 266 | } |
264 | pr_debug("create_pbe_list(): initialized %d PBEs\n", num); | ||
265 | } | 267 | } |
266 | 268 | ||
267 | /** | 269 | /** |
@@ -332,7 +334,8 @@ struct pbe *alloc_pagedir(unsigned int nr_pages, gfp_t gfp_mask, int safe_needed | |||
332 | if (!pbe) { /* get_zeroed_page() failed */ | 334 | if (!pbe) { /* get_zeroed_page() failed */ |
333 | free_pagedir(pblist); | 335 | free_pagedir(pblist); |
334 | pblist = NULL; | 336 | pblist = NULL; |
335 | } | 337 | } else |
338 | create_pbe_list(pblist, nr_pages); | ||
336 | return pblist; | 339 | return pblist; |
337 | } | 340 | } |
338 | 341 | ||
@@ -395,7 +398,6 @@ static struct pbe *swsusp_alloc(unsigned int nr_pages) | |||
395 | printk(KERN_ERR "suspend: Allocating pagedir failed.\n"); | 398 | printk(KERN_ERR "suspend: Allocating pagedir failed.\n"); |
396 | return NULL; | 399 | return NULL; |
397 | } | 400 | } |
398 | create_pbe_list(pblist, nr_pages); | ||
399 | 401 | ||
400 | if (alloc_data_pages(pblist, GFP_ATOMIC | __GFP_COLD, 0)) { | 402 | if (alloc_data_pages(pblist, GFP_ATOMIC | __GFP_COLD, 0)) { |
401 | printk(KERN_ERR "suspend: Allocating image pages failed.\n"); | 403 | printk(KERN_ERR "suspend: Allocating image pages failed.\n"); |
@@ -421,10 +423,6 @@ asmlinkage int swsusp_save(void) | |||
421 | (nr_pages + PBES_PER_PAGE - 1) / PBES_PER_PAGE, | 423 | (nr_pages + PBES_PER_PAGE - 1) / PBES_PER_PAGE, |
422 | PAGES_FOR_IO, nr_free_pages()); | 424 | PAGES_FOR_IO, nr_free_pages()); |
423 | 425 | ||
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)) { | 426 | if (!enough_free_mem(nr_pages)) { |
429 | printk(KERN_ERR "swsusp: Not enough free memory\n"); | 427 | printk(KERN_ERR "swsusp: Not enough free memory\n"); |
430 | return -ENOMEM; | 428 | return -ENOMEM; |
diff --git a/kernel/power/swsusp.c b/kernel/power/swsusp.c index bd3097c583bf..b09bd7c0998d 100644 --- a/kernel/power/swsusp.c +++ b/kernel/power/swsusp.c | |||
@@ -30,6 +30,9 @@ | |||
30 | * Alex Badea <vampire@go.ro>: | 30 | * Alex Badea <vampire@go.ro>: |
31 | * Fixed runaway init | 31 | * Fixed runaway init |
32 | * | 32 | * |
33 | * Rafael J. Wysocki <rjw@sisk.pl> | ||
34 | * Added the swap map data structure and reworked the handling of swap | ||
35 | * | ||
33 | * More state savers are welcome. Especially for the scsi layer... | 36 | * More state savers are welcome. Especially for the scsi layer... |
34 | * | 37 | * |
35 | * For TODOs,FIXMEs also look in Documentation/power/swsusp.txt | 38 | * For TODOs,FIXMEs also look in Documentation/power/swsusp.txt |
@@ -76,18 +79,6 @@ static int restore_highmem(void) { return 0; } | |||
76 | 79 | ||
77 | extern char resume_file[]; | 80 | extern char resume_file[]; |
78 | 81 | ||
79 | /* Local variables that should not be affected by save */ | ||
80 | unsigned int nr_copy_pages __nosavedata = 0; | ||
81 | |||
82 | /* Suspend pagedir is allocated before final copy, therefore it | ||
83 | must be freed after resume | ||
84 | |||
85 | Warning: this is even more evil than it seems. Pagedirs this file | ||
86 | talks about are completely different from page directories used by | ||
87 | MMU hardware. | ||
88 | */ | ||
89 | suspend_pagedir_t *pagedir_nosave __nosavedata = NULL; | ||
90 | |||
91 | #define SWSUSP_SIG "S1SUSPEND" | 82 | #define SWSUSP_SIG "S1SUSPEND" |
92 | 83 | ||
93 | static struct swsusp_header { | 84 | static struct swsusp_header { |
@@ -238,48 +229,205 @@ static int write_page(unsigned long addr, swp_entry_t *loc) | |||
238 | } | 229 | } |
239 | 230 | ||
240 | /** | 231 | /** |
241 | * data_free - Free the swap entries used by the saved image. | 232 | * Swap map-handling functions |
233 | * | ||
234 | * The swap map is a data structure used for keeping track of each page | ||
235 | * written to the swap. It consists of many swap_map_page structures | ||
236 | * that contain each an array of MAP_PAGE_SIZE swap entries. | ||
237 | * These structures are linked together with the help of either the | ||
238 | * .next (in memory) or the .next_swap (in swap) member. | ||
242 | * | 239 | * |
243 | * Walk the list of used swap entries and free each one. | 240 | * The swap map is created during suspend. At that time we need to keep |
244 | * This is only used for cleanup when suspend fails. | 241 | * it in memory, because we have to free all of the allocated swap |
242 | * entries if an error occurs. The memory needed is preallocated | ||
243 | * so that we know in advance if there's enough of it. | ||
244 | * | ||
245 | * The first swap_map_page structure is filled with the swap entries that | ||
246 | * correspond to the first MAP_PAGE_SIZE data pages written to swap and | ||
247 | * so on. After the all of the data pages have been written, the order | ||
248 | * of the swap_map_page structures in the map is reversed so that they | ||
249 | * can be read from swap in the original order. This causes the data | ||
250 | * pages to be loaded in exactly the same order in which they have been | ||
251 | * saved. | ||
252 | * | ||
253 | * During resume we only need to use one swap_map_page structure | ||
254 | * at a time, which means that we only need to use two memory pages for | ||
255 | * reading the image - one for reading the swap_map_page structures | ||
256 | * and the second for reading the data pages from swap. | ||
245 | */ | 257 | */ |
246 | static void data_free(void) | 258 | |
259 | #define MAP_PAGE_SIZE ((PAGE_SIZE - sizeof(swp_entry_t) - sizeof(void *)) \ | ||
260 | / sizeof(swp_entry_t)) | ||
261 | |||
262 | struct swap_map_page { | ||
263 | swp_entry_t entries[MAP_PAGE_SIZE]; | ||
264 | swp_entry_t next_swap; | ||
265 | struct swap_map_page *next; | ||
266 | }; | ||
267 | |||
268 | static inline void free_swap_map(struct swap_map_page *swap_map) | ||
247 | { | 269 | { |
248 | swp_entry_t entry; | 270 | struct swap_map_page *swp; |
249 | struct pbe *p; | ||
250 | 271 | ||
251 | for_each_pbe (p, pagedir_nosave) { | 272 | while (swap_map) { |
252 | entry = p->swap_address; | 273 | swp = swap_map->next; |
253 | if (entry.val) | 274 | free_page((unsigned long)swap_map); |
254 | swap_free(entry); | 275 | swap_map = swp; |
255 | else | 276 | } |
256 | break; | 277 | } |
278 | |||
279 | static struct swap_map_page *alloc_swap_map(unsigned int nr_pages) | ||
280 | { | ||
281 | struct swap_map_page *swap_map, *swp; | ||
282 | unsigned n = 0; | ||
283 | |||
284 | if (!nr_pages) | ||
285 | return NULL; | ||
286 | |||
287 | pr_debug("alloc_swap_map(): nr_pages = %d\n", nr_pages); | ||
288 | swap_map = (struct swap_map_page *)get_zeroed_page(GFP_ATOMIC); | ||
289 | swp = swap_map; | ||
290 | for (n = MAP_PAGE_SIZE; n < nr_pages; n += MAP_PAGE_SIZE) { | ||
291 | swp->next = (struct swap_map_page *)get_zeroed_page(GFP_ATOMIC); | ||
292 | swp = swp->next; | ||
293 | if (!swp) { | ||
294 | free_swap_map(swap_map); | ||
295 | return NULL; | ||
296 | } | ||
257 | } | 297 | } |
298 | return swap_map; | ||
258 | } | 299 | } |
259 | 300 | ||
260 | /** | 301 | /** |
261 | * data_write - Write saved image to swap. | 302 | * reverse_swap_map - reverse the order of pages in the swap map |
262 | * | 303 | * @swap_map |
263 | * Walk the list of pages in the image and sync each one to swap. | ||
264 | */ | 304 | */ |
265 | static int data_write(void) | 305 | |
306 | static inline struct swap_map_page *reverse_swap_map(struct swap_map_page *swap_map) | ||
266 | { | 307 | { |
267 | int error = 0, i = 0; | 308 | struct swap_map_page *prev, *next; |
268 | unsigned int mod = nr_copy_pages / 100; | 309 | |
269 | struct pbe *p; | 310 | prev = NULL; |
311 | while (swap_map) { | ||
312 | next = swap_map->next; | ||
313 | swap_map->next = prev; | ||
314 | prev = swap_map; | ||
315 | swap_map = next; | ||
316 | } | ||
317 | return prev; | ||
318 | } | ||
270 | 319 | ||
271 | if (!mod) | 320 | /** |
272 | mod = 1; | 321 | * free_swap_map_entries - free the swap entries allocated to store |
322 | * the swap map @swap_map (this is only called in case of an error) | ||
323 | */ | ||
324 | static inline void free_swap_map_entries(struct swap_map_page *swap_map) | ||
325 | { | ||
326 | while (swap_map) { | ||
327 | if (swap_map->next_swap.val) | ||
328 | swap_free(swap_map->next_swap); | ||
329 | swap_map = swap_map->next; | ||
330 | } | ||
331 | } | ||
273 | 332 | ||
274 | printk( "Writing data to swap (%d pages)... ", nr_copy_pages ); | 333 | /** |
275 | for_each_pbe (p, pagedir_nosave) { | 334 | * save_swap_map - save the swap map used for tracing the data pages |
276 | if (!(i%mod)) | 335 | * stored in the swap |
277 | printk( "\b\b\b\b%3d%%", i / mod ); | 336 | */ |
278 | if ((error = write_page(p->address, &p->swap_address))) | 337 | |
338 | static int save_swap_map(struct swap_map_page *swap_map, swp_entry_t *start) | ||
339 | { | ||
340 | swp_entry_t entry = (swp_entry_t){0}; | ||
341 | int error; | ||
342 | |||
343 | while (swap_map) { | ||
344 | swap_map->next_swap = entry; | ||
345 | if ((error = write_page((unsigned long)swap_map, &entry))) | ||
279 | return error; | 346 | return error; |
280 | i++; | 347 | swap_map = swap_map->next; |
281 | } | 348 | } |
282 | printk("\b\b\b\bdone\n"); | 349 | *start = entry; |
350 | return 0; | ||
351 | } | ||
352 | |||
353 | /** | ||
354 | * free_image_entries - free the swap entries allocated to store | ||
355 | * the image data pages (this is only called in case of an error) | ||
356 | */ | ||
357 | |||
358 | static inline void free_image_entries(struct swap_map_page *swp) | ||
359 | { | ||
360 | unsigned k; | ||
361 | |||
362 | while (swp) { | ||
363 | for (k = 0; k < MAP_PAGE_SIZE; k++) | ||
364 | if (swp->entries[k].val) | ||
365 | swap_free(swp->entries[k]); | ||
366 | swp = swp->next; | ||
367 | } | ||
368 | } | ||
369 | |||
370 | /** | ||
371 | * The swap_map_handle structure is used for handling the swap map in | ||
372 | * a file-alike way | ||
373 | */ | ||
374 | |||
375 | struct swap_map_handle { | ||
376 | struct swap_map_page *cur; | ||
377 | unsigned int k; | ||
378 | }; | ||
379 | |||
380 | static inline void init_swap_map_handle(struct swap_map_handle *handle, | ||
381 | struct swap_map_page *map) | ||
382 | { | ||
383 | handle->cur = map; | ||
384 | handle->k = 0; | ||
385 | } | ||
386 | |||
387 | static inline int swap_map_write_page(struct swap_map_handle *handle, | ||
388 | unsigned long addr) | ||
389 | { | ||
390 | int error; | ||
391 | |||
392 | error = write_page(addr, handle->cur->entries + handle->k); | ||
393 | if (error) | ||
394 | return error; | ||
395 | if (++handle->k >= MAP_PAGE_SIZE) { | ||
396 | handle->cur = handle->cur->next; | ||
397 | handle->k = 0; | ||
398 | } | ||
399 | return 0; | ||
400 | } | ||
401 | |||
402 | /** | ||
403 | * save_image_data - save the data pages pointed to by the PBEs | ||
404 | * from the list @pblist using the swap map handle @handle | ||
405 | * (assume there are @nr_pages data pages to save) | ||
406 | */ | ||
407 | |||
408 | static int save_image_data(struct pbe *pblist, | ||
409 | struct swap_map_handle *handle, | ||
410 | unsigned int nr_pages) | ||
411 | { | ||
412 | unsigned int m; | ||
413 | struct pbe *p; | ||
414 | int error = 0; | ||
415 | |||
416 | printk("Saving image data pages (%u pages) ... ", nr_pages); | ||
417 | m = nr_pages / 100; | ||
418 | if (!m) | ||
419 | m = 1; | ||
420 | nr_pages = 0; | ||
421 | for_each_pbe (p, pblist) { | ||
422 | error = swap_map_write_page(handle, p->address); | ||
423 | if (error) | ||
424 | break; | ||
425 | if (!(nr_pages % m)) | ||
426 | printk("\b\b\b\b%3d%%", nr_pages / m); | ||
427 | nr_pages++; | ||
428 | } | ||
429 | if (!error) | ||
430 | printk("\b\b\b\bdone\n"); | ||
283 | return error; | 431 | return error; |
284 | } | 432 | } |
285 | 433 | ||
@@ -295,19 +443,20 @@ static void dump_info(void) | |||
295 | pr_debug(" swsusp: UTS Domain: %s\n",swsusp_info.uts.domainname); | 443 | pr_debug(" swsusp: UTS Domain: %s\n",swsusp_info.uts.domainname); |
296 | pr_debug(" swsusp: CPUs: %d\n",swsusp_info.cpus); | 444 | pr_debug(" swsusp: CPUs: %d\n",swsusp_info.cpus); |
297 | pr_debug(" swsusp: Image: %ld Pages\n",swsusp_info.image_pages); | 445 | pr_debug(" swsusp: Image: %ld Pages\n",swsusp_info.image_pages); |
298 | pr_debug(" swsusp: Pagedir: %ld Pages\n",swsusp_info.pagedir_pages); | 446 | pr_debug(" swsusp: Total: %ld Pages\n", swsusp_info.pages); |
299 | } | 447 | } |
300 | 448 | ||
301 | static void init_header(void) | 449 | static void init_header(unsigned int nr_pages) |
302 | { | 450 | { |
303 | memset(&swsusp_info, 0, sizeof(swsusp_info)); | 451 | memset(&swsusp_info, 0, sizeof(swsusp_info)); |
304 | swsusp_info.version_code = LINUX_VERSION_CODE; | 452 | swsusp_info.version_code = LINUX_VERSION_CODE; |
305 | swsusp_info.num_physpages = num_physpages; | 453 | swsusp_info.num_physpages = num_physpages; |
306 | memcpy(&swsusp_info.uts, &system_utsname, sizeof(system_utsname)); | 454 | memcpy(&swsusp_info.uts, &system_utsname, sizeof(system_utsname)); |
307 | 455 | ||
308 | swsusp_info.suspend_pagedir = pagedir_nosave; | ||
309 | swsusp_info.cpus = num_online_cpus(); | 456 | swsusp_info.cpus = num_online_cpus(); |
310 | swsusp_info.image_pages = nr_copy_pages; | 457 | swsusp_info.image_pages = nr_pages; |
458 | swsusp_info.pages = nr_pages + | ||
459 | ((nr_pages * sizeof(long) + PAGE_SIZE - 1) >> PAGE_SHIFT); | ||
311 | } | 460 | } |
312 | 461 | ||
313 | static int close_swap(void) | 462 | static int close_swap(void) |
@@ -326,39 +475,53 @@ static int close_swap(void) | |||
326 | } | 475 | } |
327 | 476 | ||
328 | /** | 477 | /** |
329 | * free_pagedir_entries - Free pages used by the page directory. | 478 | * pack_orig_addresses - the .orig_address fields of the PBEs from the |
330 | * | 479 | * list starting at @pbe are stored in the array @buf[] (1 page) |
331 | * This is used during suspend for error recovery. | ||
332 | */ | 480 | */ |
333 | 481 | ||
334 | static void free_pagedir_entries(void) | 482 | static inline struct pbe *pack_orig_addresses(unsigned long *buf, |
483 | struct pbe *pbe) | ||
335 | { | 484 | { |
336 | int i; | 485 | int j; |
337 | 486 | ||
338 | for (i = 0; i < swsusp_info.pagedir_pages; i++) | 487 | for (j = 0; j < PAGE_SIZE / sizeof(long) && pbe; j++) { |
339 | swap_free(swsusp_info.pagedir[i]); | 488 | buf[j] = pbe->orig_address; |
489 | pbe = pbe->next; | ||
490 | } | ||
491 | if (!pbe) | ||
492 | for (; j < PAGE_SIZE / sizeof(long); j++) | ||
493 | buf[j] = 0; | ||
494 | return pbe; | ||
340 | } | 495 | } |
341 | 496 | ||
342 | |||
343 | /** | 497 | /** |
344 | * write_pagedir - Write the array of pages holding the page directory. | 498 | * save_image_metadata - save the .orig_address fields of the PBEs |
345 | * @last: Last swap entry we write (needed for header). | 499 | * from the list @pblist using the swap map handle @handle |
346 | */ | 500 | */ |
347 | 501 | ||
348 | static int write_pagedir(void) | 502 | static int save_image_metadata(struct pbe *pblist, |
503 | struct swap_map_handle *handle) | ||
349 | { | 504 | { |
350 | int error = 0; | 505 | unsigned long *buf; |
351 | unsigned int n = 0; | 506 | unsigned int n = 0; |
352 | struct pbe *pbe; | 507 | struct pbe *p; |
508 | int error = 0; | ||
353 | 509 | ||
354 | printk( "Writing pagedir..."); | 510 | printk("Saving image metadata ... "); |
355 | for_each_pb_page (pbe, pagedir_nosave) { | 511 | buf = (unsigned long *)get_zeroed_page(GFP_ATOMIC); |
356 | if ((error = write_page((unsigned long)pbe, &swsusp_info.pagedir[n++]))) | 512 | if (!buf) |
357 | return error; | 513 | return -ENOMEM; |
514 | p = pblist; | ||
515 | while (p) { | ||
516 | p = pack_orig_addresses(buf, p); | ||
517 | error = swap_map_write_page(handle, (unsigned long)buf); | ||
518 | if (error) | ||
519 | break; | ||
520 | n++; | ||
358 | } | 521 | } |
359 | 522 | free_page((unsigned long)buf); | |
360 | swsusp_info.pagedir_pages = n; | 523 | if (!error) |
361 | printk("done (%u pages)\n", n); | 524 | printk("done (%u pages saved)\n", n); |
362 | return error; | 525 | return error; |
363 | } | 526 | } |
364 | 527 | ||
@@ -384,33 +547,48 @@ static int enough_swap(unsigned int nr_pages) | |||
384 | 547 | ||
385 | /** | 548 | /** |
386 | * write_suspend_image - Write entire image and metadata. | 549 | * write_suspend_image - Write entire image and metadata. |
387 | * | ||
388 | */ | 550 | */ |
389 | static int write_suspend_image(void) | 551 | static int write_suspend_image(struct pbe *pblist, unsigned int nr_pages) |
390 | { | 552 | { |
553 | struct swap_map_page *swap_map; | ||
554 | struct swap_map_handle handle; | ||
391 | int error; | 555 | int error; |
392 | 556 | ||
393 | if (!enough_swap(nr_copy_pages)) { | 557 | if (!enough_swap(nr_pages)) { |
394 | printk(KERN_ERR "swsusp: Not enough free swap\n"); | 558 | printk(KERN_ERR "swsusp: Not enough free swap\n"); |
395 | return -ENOSPC; | 559 | return -ENOSPC; |
396 | } | 560 | } |
397 | 561 | ||
398 | init_header(); | 562 | init_header(nr_pages); |
399 | if ((error = data_write())) | 563 | swap_map = alloc_swap_map(swsusp_info.pages); |
400 | goto FreeData; | 564 | if (!swap_map) |
565 | return -ENOMEM; | ||
566 | init_swap_map_handle(&handle, swap_map); | ||
401 | 567 | ||
402 | if ((error = write_pagedir())) | 568 | error = save_image_metadata(pblist, &handle); |
403 | goto FreePagedir; | 569 | if (!error) |
570 | error = save_image_data(pblist, &handle, nr_pages); | ||
571 | if (error) | ||
572 | goto Free_image_entries; | ||
404 | 573 | ||
405 | if ((error = close_swap())) | 574 | swap_map = reverse_swap_map(swap_map); |
406 | goto FreePagedir; | 575 | error = save_swap_map(swap_map, &swsusp_info.start); |
407 | Done: | 576 | if (error) |
577 | goto Free_map_entries; | ||
578 | |||
579 | error = close_swap(); | ||
580 | if (error) | ||
581 | goto Free_map_entries; | ||
582 | |||
583 | Free_swap_map: | ||
584 | free_swap_map(swap_map); | ||
408 | return error; | 585 | return error; |
409 | FreePagedir: | 586 | |
410 | free_pagedir_entries(); | 587 | Free_map_entries: |
411 | FreeData: | 588 | free_swap_map_entries(swap_map); |
412 | data_free(); | 589 | Free_image_entries: |
413 | goto Done; | 590 | free_image_entries(swap_map); |
591 | goto Free_swap_map; | ||
414 | } | 592 | } |
415 | 593 | ||
416 | /* It is important _NOT_ to umount filesystems at this point. We want | 594 | /* It is important _NOT_ to umount filesystems at this point. We want |
@@ -418,7 +596,7 @@ static int write_suspend_image(void) | |||
418 | * filesystem clean: it is not. (And it does not matter, if we resume | 596 | * filesystem clean: it is not. (And it does not matter, if we resume |
419 | * correctly, we'll mark system clean, anyway.) | 597 | * correctly, we'll mark system clean, anyway.) |
420 | */ | 598 | */ |
421 | int swsusp_write(void) | 599 | int swsusp_write(struct pbe *pblist, unsigned int nr_pages) |
422 | { | 600 | { |
423 | int error; | 601 | int error; |
424 | 602 | ||
@@ -427,14 +605,12 @@ int swsusp_write(void) | |||
427 | return error; | 605 | return error; |
428 | } | 606 | } |
429 | lock_swapdevices(); | 607 | lock_swapdevices(); |
430 | error = write_suspend_image(); | 608 | error = write_suspend_image(pblist, nr_pages); |
431 | /* This will unlock ignored swap devices since writing is finished */ | 609 | /* This will unlock ignored swap devices since writing is finished */ |
432 | lock_swapdevices(); | 610 | lock_swapdevices(); |
433 | return error; | 611 | return error; |
434 | } | 612 | } |
435 | 613 | ||
436 | |||
437 | |||
438 | int swsusp_suspend(void) | 614 | int swsusp_suspend(void) |
439 | { | 615 | { |
440 | int error; | 616 | int error; |
@@ -531,7 +707,6 @@ static void copy_page_backup_list(struct pbe *dst, struct pbe *src) | |||
531 | /* We assume both lists contain the same number of elements */ | 707 | /* We assume both lists contain the same number of elements */ |
532 | while (src) { | 708 | while (src) { |
533 | dst->orig_address = src->orig_address; | 709 | dst->orig_address = src->orig_address; |
534 | dst->swap_address = src->swap_address; | ||
535 | dst = dst->next; | 710 | dst = dst->next; |
536 | src = src->next; | 711 | src = src->next; |
537 | } | 712 | } |
@@ -611,6 +786,61 @@ static int bio_write_page(pgoff_t page_off, void *page) | |||
611 | return submit(WRITE, page_off, page); | 786 | return submit(WRITE, page_off, page); |
612 | } | 787 | } |
613 | 788 | ||
789 | /** | ||
790 | * The following functions allow us to read data using a swap map | ||
791 | * in a file-alike way | ||
792 | */ | ||
793 | |||
794 | static inline void release_swap_map_reader(struct swap_map_handle *handle) | ||
795 | { | ||
796 | if (handle->cur) | ||
797 | free_page((unsigned long)handle->cur); | ||
798 | handle->cur = NULL; | ||
799 | } | ||
800 | |||
801 | static inline int get_swap_map_reader(struct swap_map_handle *handle, | ||
802 | swp_entry_t start) | ||
803 | { | ||
804 | int error; | ||
805 | |||
806 | if (!swp_offset(start)) | ||
807 | return -EINVAL; | ||
808 | handle->cur = (struct swap_map_page *)get_zeroed_page(GFP_ATOMIC); | ||
809 | if (!handle->cur) | ||
810 | return -ENOMEM; | ||
811 | error = bio_read_page(swp_offset(start), handle->cur); | ||
812 | if (error) { | ||
813 | release_swap_map_reader(handle); | ||
814 | return error; | ||
815 | } | ||
816 | handle->k = 0; | ||
817 | return 0; | ||
818 | } | ||
819 | |||
820 | static inline int swap_map_read_page(struct swap_map_handle *handle, void *buf) | ||
821 | { | ||
822 | unsigned long offset; | ||
823 | int error; | ||
824 | |||
825 | if (!handle->cur) | ||
826 | return -EINVAL; | ||
827 | offset = swp_offset(handle->cur->entries[handle->k]); | ||
828 | if (!offset) | ||
829 | return -EINVAL; | ||
830 | error = bio_read_page(offset, buf); | ||
831 | if (error) | ||
832 | return error; | ||
833 | if (++handle->k >= MAP_PAGE_SIZE) { | ||
834 | handle->k = 0; | ||
835 | offset = swp_offset(handle->cur->next_swap); | ||
836 | if (!offset) | ||
837 | release_swap_map_reader(handle); | ||
838 | else | ||
839 | error = bio_read_page(offset, handle->cur); | ||
840 | } | ||
841 | return error; | ||
842 | } | ||
843 | |||
614 | /* | 844 | /* |
615 | * Sanity check if this image makes sense with this kernel/swap context | 845 | * Sanity check if this image makes sense with this kernel/swap context |
616 | * I really don't think that it's foolproof but more than nothing.. | 846 | * I really don't think that it's foolproof but more than nothing.. |
@@ -639,7 +869,6 @@ static const char *sanity_check(void) | |||
639 | return NULL; | 869 | return NULL; |
640 | } | 870 | } |
641 | 871 | ||
642 | |||
643 | static int check_header(void) | 872 | static int check_header(void) |
644 | { | 873 | { |
645 | const char *reason = NULL; | 874 | const char *reason = NULL; |
@@ -653,7 +882,6 @@ static int check_header(void) | |||
653 | printk(KERN_ERR "swsusp: Resume mismatch: %s\n",reason); | 882 | printk(KERN_ERR "swsusp: Resume mismatch: %s\n",reason); |
654 | return -EPERM; | 883 | return -EPERM; |
655 | } | 884 | } |
656 | nr_copy_pages = swsusp_info.image_pages; | ||
657 | return error; | 885 | return error; |
658 | } | 886 | } |
659 | 887 | ||
@@ -680,75 +908,88 @@ static int check_sig(void) | |||
680 | } | 908 | } |
681 | 909 | ||
682 | /** | 910 | /** |
683 | * data_read - Read image pages from swap. | 911 | * load_image_data - load the image data using the swap map handle |
684 | * | 912 | * @handle and store them using the page backup list @pblist |
685 | * You do not need to check for overlaps, check_pagedir() | 913 | * (assume there are @nr_pages pages to load) |
686 | * already did that. | ||
687 | */ | 914 | */ |
688 | 915 | ||
689 | static int data_read(struct pbe *pblist) | 916 | static int load_image_data(struct pbe *pblist, |
917 | struct swap_map_handle *handle, | ||
918 | unsigned int nr_pages) | ||
690 | { | 919 | { |
920 | int error; | ||
921 | unsigned int m; | ||
691 | struct pbe *p; | 922 | struct pbe *p; |
692 | int error = 0; | ||
693 | int i = 0; | ||
694 | int mod = swsusp_info.image_pages / 100; | ||
695 | |||
696 | if (!mod) | ||
697 | mod = 1; | ||
698 | |||
699 | printk("swsusp: Reading image data (%lu pages): ", | ||
700 | swsusp_info.image_pages); | ||
701 | |||
702 | for_each_pbe (p, pblist) { | ||
703 | if (!(i % mod)) | ||
704 | printk("\b\b\b\b%3d%%", i / mod); | ||
705 | 923 | ||
706 | if ((error = bio_read_page(swp_offset(p->swap_address), | 924 | if (!pblist) |
707 | (void *)p->address))) | 925 | return -EINVAL; |
708 | return error; | 926 | printk("Loading image data pages (%u pages) ... ", nr_pages); |
709 | 927 | m = nr_pages / 100; | |
710 | i++; | 928 | if (!m) |
929 | m = 1; | ||
930 | nr_pages = 0; | ||
931 | p = pblist; | ||
932 | while (p) { | ||
933 | error = swap_map_read_page(handle, (void *)p->address); | ||
934 | if (error) | ||
935 | break; | ||
936 | p = p->next; | ||
937 | if (!(nr_pages % m)) | ||
938 | printk("\b\b\b\b%3d%%", nr_pages / m); | ||
939 | nr_pages++; | ||
711 | } | 940 | } |
712 | printk("\b\b\b\bdone\n"); | 941 | if (!error) |
942 | printk("\b\b\b\bdone\n"); | ||
713 | return error; | 943 | return error; |
714 | } | 944 | } |
715 | 945 | ||
716 | /** | 946 | /** |
717 | * read_pagedir - Read page backup list pages from swap | 947 | * unpack_orig_addresses - copy the elements of @buf[] (1 page) to |
948 | * the PBEs in the list starting at @pbe | ||
718 | */ | 949 | */ |
719 | 950 | ||
720 | static int read_pagedir(struct pbe *pblist) | 951 | static inline struct pbe *unpack_orig_addresses(unsigned long *buf, |
952 | struct pbe *pbe) | ||
721 | { | 953 | { |
722 | struct pbe *pbpage, *p; | 954 | int j; |
723 | unsigned int i = 0; | ||
724 | int error; | ||
725 | 955 | ||
726 | if (!pblist) | 956 | for (j = 0; j < PAGE_SIZE / sizeof(long) && pbe; j++) { |
727 | return -EFAULT; | 957 | pbe->orig_address = buf[j]; |
958 | pbe = pbe->next; | ||
959 | } | ||
960 | return pbe; | ||
961 | } | ||
728 | 962 | ||
729 | printk("swsusp: Reading pagedir (%lu pages)\n", | 963 | /** |
730 | swsusp_info.pagedir_pages); | 964 | * load_image_metadata - load the image metadata using the swap map |
965 | * handle @handle and put them into the PBEs in the list @pblist | ||
966 | */ | ||
731 | 967 | ||
732 | for_each_pb_page (pbpage, pblist) { | 968 | static int load_image_metadata(struct pbe *pblist, struct swap_map_handle *handle) |
733 | unsigned long offset = swp_offset(swsusp_info.pagedir[i++]); | 969 | { |
970 | struct pbe *p; | ||
971 | unsigned long *buf; | ||
972 | unsigned int n = 0; | ||
973 | int error = 0; | ||
734 | 974 | ||
735 | error = -EFAULT; | 975 | printk("Loading image metadata ... "); |
736 | if (offset) { | 976 | buf = (unsigned long *)get_zeroed_page(GFP_ATOMIC); |
737 | p = (pbpage + PB_PAGE_SKIP)->next; | 977 | if (!buf) |
738 | error = bio_read_page(offset, (void *)pbpage); | 978 | return -ENOMEM; |
739 | (pbpage + PB_PAGE_SKIP)->next = p; | 979 | p = pblist; |
740 | } | 980 | while (p) { |
981 | error = swap_map_read_page(handle, buf); | ||
741 | if (error) | 982 | if (error) |
742 | break; | 983 | break; |
984 | p = unpack_orig_addresses(buf, p); | ||
985 | n++; | ||
743 | } | 986 | } |
744 | 987 | free_page((unsigned long)buf); | |
745 | if (!error) | 988 | if (!error) |
746 | BUG_ON(i != swsusp_info.pagedir_pages); | 989 | printk("done (%u pages loaded)\n", n); |
747 | |||
748 | return error; | 990 | return error; |
749 | } | 991 | } |
750 | 992 | ||
751 | |||
752 | static int check_suspend_image(void) | 993 | static int check_suspend_image(void) |
753 | { | 994 | { |
754 | int error = 0; | 995 | int error = 0; |
@@ -762,34 +1003,39 @@ static int check_suspend_image(void) | |||
762 | return 0; | 1003 | return 0; |
763 | } | 1004 | } |
764 | 1005 | ||
765 | static int read_suspend_image(void) | 1006 | static int read_suspend_image(struct pbe **pblist_ptr) |
766 | { | 1007 | { |
767 | int error = 0; | 1008 | int error = 0; |
768 | struct pbe *p; | 1009 | struct pbe *p, *pblist; |
1010 | struct swap_map_handle handle; | ||
1011 | unsigned int nr_pages = swsusp_info.image_pages; | ||
769 | 1012 | ||
770 | if (!(p = alloc_pagedir(nr_copy_pages, GFP_ATOMIC, 0))) | 1013 | p = alloc_pagedir(nr_pages, GFP_ATOMIC, 0); |
1014 | if (!p) | ||
771 | return -ENOMEM; | 1015 | return -ENOMEM; |
772 | 1016 | error = get_swap_map_reader(&handle, swsusp_info.start); | |
773 | if ((error = read_pagedir(p))) | 1017 | if (error) |
1018 | /* The PBE list at p will be released by swsusp_free() */ | ||
774 | return error; | 1019 | return error; |
775 | create_pbe_list(p, nr_copy_pages); | 1020 | error = load_image_metadata(p, &handle); |
776 | mark_unsafe_pages(p); | 1021 | if (!error) { |
777 | pagedir_nosave = alloc_pagedir(nr_copy_pages, GFP_ATOMIC, 1); | 1022 | mark_unsafe_pages(p); |
778 | if (pagedir_nosave) { | 1023 | pblist = alloc_pagedir(nr_pages, GFP_ATOMIC, 1); |
779 | create_pbe_list(pagedir_nosave, nr_copy_pages); | 1024 | if (pblist) |
780 | copy_page_backup_list(pagedir_nosave, p); | 1025 | copy_page_backup_list(pblist, p); |
1026 | free_pagedir(p); | ||
1027 | if (!pblist) | ||
1028 | error = -ENOMEM; | ||
1029 | |||
1030 | /* Allocate memory for the image and read the data from swap */ | ||
1031 | if (!error) | ||
1032 | error = alloc_data_pages(pblist, GFP_ATOMIC, 1); | ||
1033 | if (!error) | ||
1034 | error = load_image_data(pblist, &handle, nr_pages); | ||
1035 | if (!error) | ||
1036 | *pblist_ptr = pblist; | ||
781 | } | 1037 | } |
782 | free_pagedir(p); | 1038 | release_swap_map_reader(&handle); |
783 | if (!pagedir_nosave) | ||
784 | return -ENOMEM; | ||
785 | |||
786 | /* Allocate memory for the image and read the data from swap */ | ||
787 | |||
788 | error = alloc_data_pages(pagedir_nosave, GFP_ATOMIC, 1); | ||
789 | |||
790 | if (!error) | ||
791 | error = data_read(pagedir_nosave); | ||
792 | |||
793 | return error; | 1039 | return error; |
794 | } | 1040 | } |
795 | 1041 | ||
@@ -821,7 +1067,7 @@ int swsusp_check(void) | |||
821 | * swsusp_read - Read saved image from swap. | 1067 | * swsusp_read - Read saved image from swap. |
822 | */ | 1068 | */ |
823 | 1069 | ||
824 | int swsusp_read(void) | 1070 | int swsusp_read(struct pbe **pblist_ptr) |
825 | { | 1071 | { |
826 | int error; | 1072 | int error; |
827 | 1073 | ||
@@ -830,7 +1076,7 @@ int swsusp_read(void) | |||
830 | return PTR_ERR(resume_bdev); | 1076 | return PTR_ERR(resume_bdev); |
831 | } | 1077 | } |
832 | 1078 | ||
833 | error = read_suspend_image(); | 1079 | error = read_suspend_image(pblist_ptr); |
834 | blkdev_put(resume_bdev); | 1080 | blkdev_put(resume_bdev); |
835 | 1081 | ||
836 | if (!error) | 1082 | if (!error) |