diff options
author | Rafael J. Wysocki <rjw@sisk.pl> | 2006-03-23 06:00:00 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-03-23 10:38:07 -0500 |
commit | 61159a314bca6408320c3173c1282c64f5cdaa76 (patch) | |
tree | 8e1b7627443da0fd52b2fac66366dde9f7871f1e /kernel/power/swsusp.c | |
parent | f577eb30afdc68233f25d4d82b04102129262365 (diff) |
[PATCH] swsusp: separate swap-writing/reading code
Move the swap-writing/reading code of swsusp to a separate file.
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Acked-by: Pavel Machek <pavel@ucw.cz>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'kernel/power/swsusp.c')
-rw-r--r-- | kernel/power/swsusp.c | 568 |
1 files changed, 6 insertions, 562 deletions
diff --git a/kernel/power/swsusp.c b/kernel/power/swsusp.c index 457084f50010..c4016cbbd3e0 100644 --- a/kernel/power/swsusp.c +++ b/kernel/power/swsusp.c | |||
@@ -31,41 +31,24 @@ | |||
31 | * Fixed runaway init | 31 | * Fixed runaway init |
32 | * | 32 | * |
33 | * Rafael J. Wysocki <rjw@sisk.pl> | 33 | * Rafael J. Wysocki <rjw@sisk.pl> |
34 | * Added the swap map data structure and reworked the handling of swap | 34 | * Reworked the freeing of memory and 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 | * |
38 | * For TODOs,FIXMEs also look in Documentation/power/swsusp.txt | 38 | * For TODOs,FIXMEs also look in Documentation/power/swsusp.txt |
39 | */ | 39 | */ |
40 | 40 | ||
41 | #include <linux/module.h> | ||
42 | #include <linux/mm.h> | 41 | #include <linux/mm.h> |
43 | #include <linux/suspend.h> | 42 | #include <linux/suspend.h> |
44 | #include <linux/smp_lock.h> | ||
45 | #include <linux/file.h> | ||
46 | #include <linux/utsname.h> | ||
47 | #include <linux/version.h> | ||
48 | #include <linux/delay.h> | ||
49 | #include <linux/bitops.h> | ||
50 | #include <linux/spinlock.h> | 43 | #include <linux/spinlock.h> |
51 | #include <linux/genhd.h> | ||
52 | #include <linux/kernel.h> | 44 | #include <linux/kernel.h> |
53 | #include <linux/major.h> | 45 | #include <linux/major.h> |
54 | #include <linux/swap.h> | 46 | #include <linux/swap.h> |
55 | #include <linux/pm.h> | 47 | #include <linux/pm.h> |
56 | #include <linux/device.h> | ||
57 | #include <linux/buffer_head.h> | ||
58 | #include <linux/swapops.h> | 48 | #include <linux/swapops.h> |
59 | #include <linux/bootmem.h> | 49 | #include <linux/bootmem.h> |
60 | #include <linux/syscalls.h> | 50 | #include <linux/syscalls.h> |
61 | #include <linux/highmem.h> | 51 | #include <linux/highmem.h> |
62 | #include <linux/bio.h> | ||
63 | |||
64 | #include <asm/uaccess.h> | ||
65 | #include <asm/mmu_context.h> | ||
66 | #include <asm/pgtable.h> | ||
67 | #include <asm/tlbflush.h> | ||
68 | #include <asm/io.h> | ||
69 | 52 | ||
70 | #include "power.h" | 53 | #include "power.h" |
71 | 54 | ||
@@ -89,91 +72,15 @@ static int restore_highmem(void) { return 0; } | |||
89 | static unsigned int count_highmem_pages(void) { return 0; } | 72 | static unsigned int count_highmem_pages(void) { return 0; } |
90 | #endif | 73 | #endif |
91 | 74 | ||
92 | extern char resume_file[]; | ||
93 | |||
94 | #define SWSUSP_SIG "S1SUSPEND" | ||
95 | |||
96 | static struct swsusp_header { | ||
97 | char reserved[PAGE_SIZE - 20 - sizeof(swp_entry_t)]; | ||
98 | swp_entry_t image; | ||
99 | char orig_sig[10]; | ||
100 | char sig[10]; | ||
101 | } __attribute__((packed, aligned(PAGE_SIZE))) swsusp_header; | ||
102 | |||
103 | /* | ||
104 | * Saving part... | ||
105 | */ | ||
106 | |||
107 | static unsigned short root_swap = 0xffff; | ||
108 | |||
109 | static int mark_swapfiles(swp_entry_t start) | ||
110 | { | ||
111 | int error; | ||
112 | |||
113 | rw_swap_page_sync(READ, | ||
114 | swp_entry(root_swap, 0), | ||
115 | virt_to_page((unsigned long)&swsusp_header)); | ||
116 | if (!memcmp("SWAP-SPACE",swsusp_header.sig, 10) || | ||
117 | !memcmp("SWAPSPACE2",swsusp_header.sig, 10)) { | ||
118 | memcpy(swsusp_header.orig_sig,swsusp_header.sig, 10); | ||
119 | memcpy(swsusp_header.sig,SWSUSP_SIG, 10); | ||
120 | swsusp_header.image = start; | ||
121 | error = rw_swap_page_sync(WRITE, | ||
122 | swp_entry(root_swap, 0), | ||
123 | virt_to_page((unsigned long) | ||
124 | &swsusp_header)); | ||
125 | } else { | ||
126 | pr_debug("swsusp: Partition is not swap space.\n"); | ||
127 | error = -ENODEV; | ||
128 | } | ||
129 | return error; | ||
130 | } | ||
131 | |||
132 | /** | ||
133 | * swsusp_swap_check - check if the resume device is a swap device | ||
134 | * and get its index (if so) | ||
135 | */ | ||
136 | |||
137 | static int swsusp_swap_check(void) /* This is called before saving image */ | ||
138 | { | ||
139 | int res = swap_type_of(swsusp_resume_device); | ||
140 | |||
141 | if (res >= 0) { | ||
142 | root_swap = res; | ||
143 | return 0; | ||
144 | } | ||
145 | return res; | ||
146 | } | ||
147 | |||
148 | /** | ||
149 | * The bitmap is used for tracing allocated swap pages | ||
150 | * | ||
151 | * The entire bitmap consists of a number of bitmap_page | ||
152 | * structures linked with the help of the .next member. | ||
153 | * Thus each page can be allocated individually, so we only | ||
154 | * need to make 0-order memory allocations to create | ||
155 | * the bitmap. | ||
156 | */ | ||
157 | |||
158 | #define BITMAP_PAGE_SIZE (PAGE_SIZE - sizeof(void *)) | ||
159 | #define BITMAP_PAGE_CHUNKS (BITMAP_PAGE_SIZE / sizeof(long)) | ||
160 | #define BITS_PER_CHUNK (sizeof(long) * 8) | ||
161 | #define BITMAP_PAGE_BITS (BITMAP_PAGE_CHUNKS * BITS_PER_CHUNK) | ||
162 | |||
163 | struct bitmap_page { | ||
164 | unsigned long chunks[BITMAP_PAGE_CHUNKS]; | ||
165 | struct bitmap_page *next; | ||
166 | }; | ||
167 | |||
168 | /** | 75 | /** |
169 | * The following functions are used for tracing the allocated | 76 | * The following functions are used for tracing the allocated |
170 | * swap pages, so that they can be freed in case of an error. | 77 | * swap pages, so that they can be freed in case of an error. |
171 | * | 78 | * |
172 | * The functions operate on a linked bitmap structure defined | 79 | * The functions operate on a linked bitmap structure defined |
173 | * above | 80 | * in power.h |
174 | */ | 81 | */ |
175 | 82 | ||
176 | static void free_bitmap(struct bitmap_page *bitmap) | 83 | void free_bitmap(struct bitmap_page *bitmap) |
177 | { | 84 | { |
178 | struct bitmap_page *bp; | 85 | struct bitmap_page *bp; |
179 | 86 | ||
@@ -184,7 +91,7 @@ static void free_bitmap(struct bitmap_page *bitmap) | |||
184 | } | 91 | } |
185 | } | 92 | } |
186 | 93 | ||
187 | static struct bitmap_page *alloc_bitmap(unsigned int nr_bits) | 94 | struct bitmap_page *alloc_bitmap(unsigned int nr_bits) |
188 | { | 95 | { |
189 | struct bitmap_page *bitmap, *bp; | 96 | struct bitmap_page *bitmap, *bp; |
190 | unsigned int n; | 97 | unsigned int n; |
@@ -227,7 +134,7 @@ static int bitmap_set(struct bitmap_page *bitmap, unsigned long bit) | |||
227 | return 0; | 134 | return 0; |
228 | } | 135 | } |
229 | 136 | ||
230 | static unsigned long alloc_swap_page(int swap, struct bitmap_page *bitmap) | 137 | unsigned long alloc_swap_page(int swap, struct bitmap_page *bitmap) |
231 | { | 138 | { |
232 | unsigned long offset; | 139 | unsigned long offset; |
233 | 140 | ||
@@ -241,7 +148,7 @@ static unsigned long alloc_swap_page(int swap, struct bitmap_page *bitmap) | |||
241 | return offset; | 148 | return offset; |
242 | } | 149 | } |
243 | 150 | ||
244 | static void free_all_swap_pages(int swap, struct bitmap_page *bitmap) | 151 | void free_all_swap_pages(int swap, struct bitmap_page *bitmap) |
245 | { | 152 | { |
246 | unsigned int bit, n; | 153 | unsigned int bit, n; |
247 | unsigned long test; | 154 | unsigned long test; |
@@ -259,220 +166,6 @@ static void free_all_swap_pages(int swap, struct bitmap_page *bitmap) | |||
259 | } | 166 | } |
260 | 167 | ||
261 | /** | 168 | /** |
262 | * write_page - Write one page to given swap location. | ||
263 | * @buf: Address we're writing. | ||
264 | * @offset: Offset of the swap page we're writing to. | ||
265 | */ | ||
266 | |||
267 | static int write_page(void *buf, unsigned long offset) | ||
268 | { | ||
269 | swp_entry_t entry; | ||
270 | int error = -ENOSPC; | ||
271 | |||
272 | if (offset) { | ||
273 | entry = swp_entry(root_swap, offset); | ||
274 | error = rw_swap_page_sync(WRITE, entry, virt_to_page(buf)); | ||
275 | } | ||
276 | return error; | ||
277 | } | ||
278 | |||
279 | /* | ||
280 | * The swap map is a data structure used for keeping track of each page | ||
281 | * written to a swap partition. It consists of many swap_map_page | ||
282 | * structures that contain each an array of MAP_PAGE_SIZE swap entries. | ||
283 | * These structures are stored on the swap and linked together with the | ||
284 | * help of the .next_swap member. | ||
285 | * | ||
286 | * The swap map is created during suspend. The swap map pages are | ||
287 | * allocated and populated one at a time, so we only need one memory | ||
288 | * page to set up the entire structure. | ||
289 | * | ||
290 | * During resume we also only need to use one swap_map_page structure | ||
291 | * at a time. | ||
292 | */ | ||
293 | |||
294 | #define MAP_PAGE_ENTRIES (PAGE_SIZE / sizeof(long) - 1) | ||
295 | |||
296 | struct swap_map_page { | ||
297 | unsigned long entries[MAP_PAGE_ENTRIES]; | ||
298 | unsigned long next_swap; | ||
299 | }; | ||
300 | |||
301 | /** | ||
302 | * The swap_map_handle structure is used for handling swap in | ||
303 | * a file-alike way | ||
304 | */ | ||
305 | |||
306 | struct swap_map_handle { | ||
307 | struct swap_map_page *cur; | ||
308 | unsigned long cur_swap; | ||
309 | struct bitmap_page *bitmap; | ||
310 | unsigned int k; | ||
311 | }; | ||
312 | |||
313 | static void release_swap_writer(struct swap_map_handle *handle) | ||
314 | { | ||
315 | if (handle->cur) | ||
316 | free_page((unsigned long)handle->cur); | ||
317 | handle->cur = NULL; | ||
318 | if (handle->bitmap) | ||
319 | free_bitmap(handle->bitmap); | ||
320 | handle->bitmap = NULL; | ||
321 | } | ||
322 | |||
323 | static int get_swap_writer(struct swap_map_handle *handle) | ||
324 | { | ||
325 | handle->cur = (struct swap_map_page *)get_zeroed_page(GFP_KERNEL); | ||
326 | if (!handle->cur) | ||
327 | return -ENOMEM; | ||
328 | handle->bitmap = alloc_bitmap(count_swap_pages(root_swap, 0)); | ||
329 | if (!handle->bitmap) { | ||
330 | release_swap_writer(handle); | ||
331 | return -ENOMEM; | ||
332 | } | ||
333 | handle->cur_swap = alloc_swap_page(root_swap, handle->bitmap); | ||
334 | if (!handle->cur_swap) { | ||
335 | release_swap_writer(handle); | ||
336 | return -ENOSPC; | ||
337 | } | ||
338 | handle->k = 0; | ||
339 | return 0; | ||
340 | } | ||
341 | |||
342 | static int swap_write_page(struct swap_map_handle *handle, void *buf) | ||
343 | { | ||
344 | int error; | ||
345 | unsigned long offset; | ||
346 | |||
347 | if (!handle->cur) | ||
348 | return -EINVAL; | ||
349 | offset = alloc_swap_page(root_swap, handle->bitmap); | ||
350 | error = write_page(buf, offset); | ||
351 | if (error) | ||
352 | return error; | ||
353 | handle->cur->entries[handle->k++] = offset; | ||
354 | if (handle->k >= MAP_PAGE_ENTRIES) { | ||
355 | offset = alloc_swap_page(root_swap, handle->bitmap); | ||
356 | if (!offset) | ||
357 | return -ENOSPC; | ||
358 | handle->cur->next_swap = offset; | ||
359 | error = write_page(handle->cur, handle->cur_swap); | ||
360 | if (error) | ||
361 | return error; | ||
362 | memset(handle->cur, 0, PAGE_SIZE); | ||
363 | handle->cur_swap = offset; | ||
364 | handle->k = 0; | ||
365 | } | ||
366 | return 0; | ||
367 | } | ||
368 | |||
369 | static int flush_swap_writer(struct swap_map_handle *handle) | ||
370 | { | ||
371 | if (handle->cur && handle->cur_swap) | ||
372 | return write_page(handle->cur, handle->cur_swap); | ||
373 | else | ||
374 | return -EINVAL; | ||
375 | } | ||
376 | |||
377 | /** | ||
378 | * save_image - save the suspend image data | ||
379 | */ | ||
380 | |||
381 | static int save_image(struct swap_map_handle *handle, | ||
382 | struct snapshot_handle *snapshot, | ||
383 | unsigned int nr_pages) | ||
384 | { | ||
385 | unsigned int m; | ||
386 | int ret; | ||
387 | int error = 0; | ||
388 | |||
389 | printk("Saving image data pages (%u pages) ... ", nr_pages); | ||
390 | m = nr_pages / 100; | ||
391 | if (!m) | ||
392 | m = 1; | ||
393 | nr_pages = 0; | ||
394 | do { | ||
395 | ret = snapshot_read_next(snapshot, PAGE_SIZE); | ||
396 | if (ret > 0) { | ||
397 | error = swap_write_page(handle, data_of(*snapshot)); | ||
398 | if (error) | ||
399 | break; | ||
400 | if (!(nr_pages % m)) | ||
401 | printk("\b\b\b\b%3d%%", nr_pages / m); | ||
402 | nr_pages++; | ||
403 | } | ||
404 | } while (ret > 0); | ||
405 | if (!error) | ||
406 | printk("\b\b\b\bdone\n"); | ||
407 | return error; | ||
408 | } | ||
409 | |||
410 | /** | ||
411 | * enough_swap - Make sure we have enough swap to save the image. | ||
412 | * | ||
413 | * Returns TRUE or FALSE after checking the total amount of swap | ||
414 | * space avaiable from the resume partition. | ||
415 | */ | ||
416 | |||
417 | static int enough_swap(unsigned int nr_pages) | ||
418 | { | ||
419 | unsigned int free_swap = count_swap_pages(root_swap, 1); | ||
420 | |||
421 | pr_debug("swsusp: free swap pages: %u\n", free_swap); | ||
422 | return free_swap > (nr_pages + PAGES_FOR_IO + | ||
423 | (nr_pages + PBES_PER_PAGE - 1) / PBES_PER_PAGE); | ||
424 | } | ||
425 | |||
426 | /** | ||
427 | * swsusp_write - Write entire image and metadata. | ||
428 | * | ||
429 | * It is important _NOT_ to umount filesystems at this point. We want | ||
430 | * them synced (in case something goes wrong) but we DO not want to mark | ||
431 | * filesystem clean: it is not. (And it does not matter, if we resume | ||
432 | * correctly, we'll mark system clean, anyway.) | ||
433 | */ | ||
434 | |||
435 | int swsusp_write(void) | ||
436 | { | ||
437 | struct swap_map_handle handle; | ||
438 | struct snapshot_handle snapshot; | ||
439 | struct swsusp_info *header; | ||
440 | unsigned long start; | ||
441 | int error; | ||
442 | |||
443 | if ((error = swsusp_swap_check())) { | ||
444 | printk(KERN_ERR "swsusp: Cannot find swap device, try swapon -a.\n"); | ||
445 | return error; | ||
446 | } | ||
447 | memset(&snapshot, 0, sizeof(struct snapshot_handle)); | ||
448 | error = snapshot_read_next(&snapshot, PAGE_SIZE); | ||
449 | if (error < PAGE_SIZE) | ||
450 | return error < 0 ? error : -EFAULT; | ||
451 | header = (struct swsusp_info *)data_of(snapshot); | ||
452 | if (!enough_swap(header->pages)) { | ||
453 | printk(KERN_ERR "swsusp: Not enough free swap\n"); | ||
454 | return -ENOSPC; | ||
455 | } | ||
456 | error = get_swap_writer(&handle); | ||
457 | if (!error) { | ||
458 | start = handle.cur_swap; | ||
459 | error = swap_write_page(&handle, header); | ||
460 | } | ||
461 | if (!error) | ||
462 | error = save_image(&handle, &snapshot, header->pages - 1); | ||
463 | if (!error) { | ||
464 | flush_swap_writer(&handle); | ||
465 | printk("S"); | ||
466 | error = mark_swapfiles(swp_entry(root_swap, start)); | ||
467 | printk("|\n"); | ||
468 | } | ||
469 | if (error) | ||
470 | free_all_swap_pages(root_swap, handle.bitmap); | ||
471 | release_swap_writer(&handle); | ||
472 | return error; | ||
473 | } | ||
474 | |||
475 | /** | ||
476 | * swsusp_shrink_memory - Try to free as much memory as needed | 169 | * swsusp_shrink_memory - Try to free as much memory as needed |
477 | * | 170 | * |
478 | * ... but do not OOM-kill anyone | 171 | * ... but do not OOM-kill anyone |
@@ -578,252 +271,3 @@ int swsusp_resume(void) | |||
578 | local_irq_enable(); | 271 | local_irq_enable(); |
579 | return error; | 272 | return error; |
580 | } | 273 | } |
581 | |||
582 | /* | ||
583 | * Using bio to read from swap. | ||
584 | * This code requires a bit more work than just using buffer heads | ||
585 | * but, it is the recommended way for 2.5/2.6. | ||
586 | * The following are to signal the beginning and end of I/O. Bios | ||
587 | * finish asynchronously, while we want them to happen synchronously. | ||
588 | * A simple atomic_t, and a wait loop take care of this problem. | ||
589 | */ | ||
590 | |||
591 | static atomic_t io_done = ATOMIC_INIT(0); | ||
592 | |||
593 | static int end_io(struct bio *bio, unsigned int num, int err) | ||
594 | { | ||
595 | if (!test_bit(BIO_UPTODATE, &bio->bi_flags)) | ||
596 | panic("I/O error reading memory image"); | ||
597 | atomic_set(&io_done, 0); | ||
598 | return 0; | ||
599 | } | ||
600 | |||
601 | static struct block_device *resume_bdev; | ||
602 | |||
603 | /** | ||
604 | * submit - submit BIO request. | ||
605 | * @rw: READ or WRITE. | ||
606 | * @off physical offset of page. | ||
607 | * @page: page we're reading or writing. | ||
608 | * | ||
609 | * Straight from the textbook - allocate and initialize the bio. | ||
610 | * If we're writing, make sure the page is marked as dirty. | ||
611 | * Then submit it and wait. | ||
612 | */ | ||
613 | |||
614 | static int submit(int rw, pgoff_t page_off, void *page) | ||
615 | { | ||
616 | int error = 0; | ||
617 | struct bio *bio; | ||
618 | |||
619 | bio = bio_alloc(GFP_ATOMIC, 1); | ||
620 | if (!bio) | ||
621 | return -ENOMEM; | ||
622 | bio->bi_sector = page_off * (PAGE_SIZE >> 9); | ||
623 | bio->bi_bdev = resume_bdev; | ||
624 | bio->bi_end_io = end_io; | ||
625 | |||
626 | if (bio_add_page(bio, virt_to_page(page), PAGE_SIZE, 0) < PAGE_SIZE) { | ||
627 | printk("swsusp: ERROR: adding page to bio at %ld\n",page_off); | ||
628 | error = -EFAULT; | ||
629 | goto Done; | ||
630 | } | ||
631 | |||
632 | |||
633 | atomic_set(&io_done, 1); | ||
634 | submit_bio(rw | (1 << BIO_RW_SYNC), bio); | ||
635 | while (atomic_read(&io_done)) | ||
636 | yield(); | ||
637 | if (rw == READ) | ||
638 | bio_set_pages_dirty(bio); | ||
639 | Done: | ||
640 | bio_put(bio); | ||
641 | return error; | ||
642 | } | ||
643 | |||
644 | static int bio_read_page(pgoff_t page_off, void *page) | ||
645 | { | ||
646 | return submit(READ, page_off, page); | ||
647 | } | ||
648 | |||
649 | static int bio_write_page(pgoff_t page_off, void *page) | ||
650 | { | ||
651 | return submit(WRITE, page_off, page); | ||
652 | } | ||
653 | |||
654 | /** | ||
655 | * The following functions allow us to read data using a swap map | ||
656 | * in a file-alike way | ||
657 | */ | ||
658 | |||
659 | static void release_swap_reader(struct swap_map_handle *handle) | ||
660 | { | ||
661 | if (handle->cur) | ||
662 | free_page((unsigned long)handle->cur); | ||
663 | handle->cur = NULL; | ||
664 | } | ||
665 | |||
666 | static int get_swap_reader(struct swap_map_handle *handle, | ||
667 | swp_entry_t start) | ||
668 | { | ||
669 | int error; | ||
670 | |||
671 | if (!swp_offset(start)) | ||
672 | return -EINVAL; | ||
673 | handle->cur = (struct swap_map_page *)get_zeroed_page(GFP_ATOMIC); | ||
674 | if (!handle->cur) | ||
675 | return -ENOMEM; | ||
676 | error = bio_read_page(swp_offset(start), handle->cur); | ||
677 | if (error) { | ||
678 | release_swap_reader(handle); | ||
679 | return error; | ||
680 | } | ||
681 | handle->k = 0; | ||
682 | return 0; | ||
683 | } | ||
684 | |||
685 | static int swap_read_page(struct swap_map_handle *handle, void *buf) | ||
686 | { | ||
687 | unsigned long offset; | ||
688 | int error; | ||
689 | |||
690 | if (!handle->cur) | ||
691 | return -EINVAL; | ||
692 | offset = handle->cur->entries[handle->k]; | ||
693 | if (!offset) | ||
694 | return -EFAULT; | ||
695 | error = bio_read_page(offset, buf); | ||
696 | if (error) | ||
697 | return error; | ||
698 | if (++handle->k >= MAP_PAGE_ENTRIES) { | ||
699 | handle->k = 0; | ||
700 | offset = handle->cur->next_swap; | ||
701 | if (!offset) | ||
702 | release_swap_reader(handle); | ||
703 | else | ||
704 | error = bio_read_page(offset, handle->cur); | ||
705 | } | ||
706 | return error; | ||
707 | } | ||
708 | |||
709 | /** | ||
710 | * load_image - load the image using the swap map handle | ||
711 | * @handle and the snapshot handle @snapshot | ||
712 | * (assume there are @nr_pages pages to load) | ||
713 | */ | ||
714 | |||
715 | static int load_image(struct swap_map_handle *handle, | ||
716 | struct snapshot_handle *snapshot, | ||
717 | unsigned int nr_pages) | ||
718 | { | ||
719 | unsigned int m; | ||
720 | int ret; | ||
721 | int error = 0; | ||
722 | |||
723 | printk("Loading image data pages (%u pages) ... ", nr_pages); | ||
724 | m = nr_pages / 100; | ||
725 | if (!m) | ||
726 | m = 1; | ||
727 | nr_pages = 0; | ||
728 | do { | ||
729 | ret = snapshot_write_next(snapshot, PAGE_SIZE); | ||
730 | if (ret > 0) { | ||
731 | error = swap_read_page(handle, data_of(*snapshot)); | ||
732 | if (error) | ||
733 | break; | ||
734 | if (!(nr_pages % m)) | ||
735 | printk("\b\b\b\b%3d%%", nr_pages / m); | ||
736 | nr_pages++; | ||
737 | } | ||
738 | } while (ret > 0); | ||
739 | if (!error) | ||
740 | printk("\b\b\b\bdone\n"); | ||
741 | if (!snapshot_image_loaded(snapshot)) | ||
742 | error = -ENODATA; | ||
743 | return error; | ||
744 | } | ||
745 | |||
746 | int swsusp_read(void) | ||
747 | { | ||
748 | int error; | ||
749 | struct swap_map_handle handle; | ||
750 | struct snapshot_handle snapshot; | ||
751 | struct swsusp_info *header; | ||
752 | unsigned int nr_pages; | ||
753 | |||
754 | if (IS_ERR(resume_bdev)) { | ||
755 | pr_debug("swsusp: block device not initialised\n"); | ||
756 | return PTR_ERR(resume_bdev); | ||
757 | } | ||
758 | |||
759 | memset(&snapshot, 0, sizeof(struct snapshot_handle)); | ||
760 | error = snapshot_write_next(&snapshot, PAGE_SIZE); | ||
761 | if (error < PAGE_SIZE) | ||
762 | return error < 0 ? error : -EFAULT; | ||
763 | header = (struct swsusp_info *)data_of(snapshot); | ||
764 | error = get_swap_reader(&handle, swsusp_header.image); | ||
765 | if (!error) | ||
766 | error = swap_read_page(&handle, header); | ||
767 | if (!error) { | ||
768 | nr_pages = header->image_pages; | ||
769 | error = load_image(&handle, &snapshot, nr_pages); | ||
770 | } | ||
771 | release_swap_reader(&handle); | ||
772 | |||
773 | blkdev_put(resume_bdev); | ||
774 | |||
775 | if (!error) | ||
776 | pr_debug("swsusp: Reading resume file was successful\n"); | ||
777 | else | ||
778 | pr_debug("swsusp: Error %d resuming\n", error); | ||
779 | return error; | ||
780 | } | ||
781 | |||
782 | /** | ||
783 | * swsusp_check - Check for swsusp signature in the resume device | ||
784 | */ | ||
785 | |||
786 | int swsusp_check(void) | ||
787 | { | ||
788 | int error; | ||
789 | |||
790 | resume_bdev = open_by_devnum(swsusp_resume_device, FMODE_READ); | ||
791 | if (!IS_ERR(resume_bdev)) { | ||
792 | set_blocksize(resume_bdev, PAGE_SIZE); | ||
793 | memset(&swsusp_header, 0, sizeof(swsusp_header)); | ||
794 | if ((error = bio_read_page(0, &swsusp_header))) | ||
795 | return error; | ||
796 | if (!memcmp(SWSUSP_SIG, swsusp_header.sig, 10)) { | ||
797 | memcpy(swsusp_header.sig, swsusp_header.orig_sig, 10); | ||
798 | /* Reset swap signature now */ | ||
799 | error = bio_write_page(0, &swsusp_header); | ||
800 | } else { | ||
801 | return -EINVAL; | ||
802 | } | ||
803 | if (error) | ||
804 | blkdev_put(resume_bdev); | ||
805 | else | ||
806 | pr_debug("swsusp: Signature found, resuming\n"); | ||
807 | } else { | ||
808 | error = PTR_ERR(resume_bdev); | ||
809 | } | ||
810 | |||
811 | if (error) | ||
812 | pr_debug("swsusp: Error %d check for resume file\n", error); | ||
813 | |||
814 | return error; | ||
815 | } | ||
816 | |||
817 | /** | ||
818 | * swsusp_close - close swap device. | ||
819 | */ | ||
820 | |||
821 | void swsusp_close(void) | ||
822 | { | ||
823 | if (IS_ERR(resume_bdev)) { | ||
824 | pr_debug("swsusp: block device not initialised\n"); | ||
825 | return; | ||
826 | } | ||
827 | |||
828 | blkdev_put(resume_bdev); | ||
829 | } | ||