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 | |
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>
-rw-r--r-- | kernel/power/Makefile | 2 | ||||
-rw-r--r-- | kernel/power/power.h | 31 | ||||
-rw-r--r-- | kernel/power/swap.c | 544 | ||||
-rw-r--r-- | kernel/power/swsusp.c | 568 |
4 files changed, 581 insertions, 564 deletions
diff --git a/kernel/power/Makefile b/kernel/power/Makefile index 04be7d0d96a7..bb91a0615303 100644 --- a/kernel/power/Makefile +++ b/kernel/power/Makefile | |||
@@ -5,7 +5,7 @@ endif | |||
5 | 5 | ||
6 | obj-y := main.o process.o console.o | 6 | obj-y := main.o process.o console.o |
7 | obj-$(CONFIG_PM_LEGACY) += pm.o | 7 | obj-$(CONFIG_PM_LEGACY) += pm.o |
8 | obj-$(CONFIG_SOFTWARE_SUSPEND) += swsusp.o disk.o snapshot.o | 8 | obj-$(CONFIG_SOFTWARE_SUSPEND) += swsusp.o disk.o snapshot.o swap.o |
9 | 9 | ||
10 | obj-$(CONFIG_SUSPEND_SMP) += smp.o | 10 | obj-$(CONFIG_SUSPEND_SMP) += smp.o |
11 | 11 | ||
diff --git a/kernel/power/power.h b/kernel/power/power.h index ea7132ed029b..089c84bed895 100644 --- a/kernel/power/power.h +++ b/kernel/power/power.h | |||
@@ -41,8 +41,8 @@ extern struct pbe *pagedir_nosave; | |||
41 | 41 | ||
42 | /* Preferred image size in bytes (default 500 MB) */ | 42 | /* Preferred image size in bytes (default 500 MB) */ |
43 | extern unsigned long image_size; | 43 | extern unsigned long image_size; |
44 | |||
45 | extern int in_suspend; | 44 | extern int in_suspend; |
45 | extern dev_t swsusp_resume_device; | ||
46 | 46 | ||
47 | extern asmlinkage int swsusp_arch_suspend(void); | 47 | extern asmlinkage int swsusp_arch_suspend(void); |
48 | extern asmlinkage int swsusp_arch_resume(void); | 48 | extern asmlinkage int swsusp_arch_resume(void); |
@@ -65,3 +65,32 @@ struct snapshot_handle { | |||
65 | extern int snapshot_read_next(struct snapshot_handle *handle, size_t count); | 65 | extern int snapshot_read_next(struct snapshot_handle *handle, size_t count); |
66 | extern int snapshot_write_next(struct snapshot_handle *handle, size_t count); | 66 | extern int snapshot_write_next(struct snapshot_handle *handle, size_t count); |
67 | int snapshot_image_loaded(struct snapshot_handle *handle); | 67 | int snapshot_image_loaded(struct snapshot_handle *handle); |
68 | |||
69 | /** | ||
70 | * The bitmap is used for tracing allocated swap pages | ||
71 | * | ||
72 | * The entire bitmap consists of a number of bitmap_page | ||
73 | * structures linked with the help of the .next member. | ||
74 | * Thus each page can be allocated individually, so we only | ||
75 | * need to make 0-order memory allocations to create | ||
76 | * the bitmap. | ||
77 | */ | ||
78 | |||
79 | #define BITMAP_PAGE_SIZE (PAGE_SIZE - sizeof(void *)) | ||
80 | #define BITMAP_PAGE_CHUNKS (BITMAP_PAGE_SIZE / sizeof(long)) | ||
81 | #define BITS_PER_CHUNK (sizeof(long) * 8) | ||
82 | #define BITMAP_PAGE_BITS (BITMAP_PAGE_CHUNKS * BITS_PER_CHUNK) | ||
83 | |||
84 | struct bitmap_page { | ||
85 | unsigned long chunks[BITMAP_PAGE_CHUNKS]; | ||
86 | struct bitmap_page *next; | ||
87 | }; | ||
88 | |||
89 | extern void free_bitmap(struct bitmap_page *bitmap); | ||
90 | extern struct bitmap_page *alloc_bitmap(unsigned int nr_bits); | ||
91 | extern unsigned long alloc_swap_page(int swap, struct bitmap_page *bitmap); | ||
92 | extern void free_all_swap_pages(int swap, struct bitmap_page *bitmap); | ||
93 | |||
94 | extern int swsusp_shrink_memory(void); | ||
95 | extern int swsusp_suspend(void); | ||
96 | extern int swsusp_resume(void); | ||
diff --git a/kernel/power/swap.c b/kernel/power/swap.c new file mode 100644 index 000000000000..9177f3f73a6c --- /dev/null +++ b/kernel/power/swap.c | |||
@@ -0,0 +1,544 @@ | |||
1 | /* | ||
2 | * linux/kernel/power/swap.c | ||
3 | * | ||
4 | * This file provides functions for reading the suspend image from | ||
5 | * and writing it to a swap partition. | ||
6 | * | ||
7 | * Copyright (C) 1998,2001-2005 Pavel Machek <pavel@suse.cz> | ||
8 | * Copyright (C) 2006 Rafael J. Wysocki <rjw@sisk.pl> | ||
9 | * | ||
10 | * This file is released under the GPLv2. | ||
11 | * | ||
12 | */ | ||
13 | |||
14 | #include <linux/module.h> | ||
15 | #include <linux/smp_lock.h> | ||
16 | #include <linux/file.h> | ||
17 | #include <linux/utsname.h> | ||
18 | #include <linux/version.h> | ||
19 | #include <linux/delay.h> | ||
20 | #include <linux/bitops.h> | ||
21 | #include <linux/genhd.h> | ||
22 | #include <linux/device.h> | ||
23 | #include <linux/buffer_head.h> | ||
24 | #include <linux/bio.h> | ||
25 | #include <linux/swap.h> | ||
26 | #include <linux/swapops.h> | ||
27 | #include <linux/pm.h> | ||
28 | |||
29 | #include "power.h" | ||
30 | |||
31 | extern char resume_file[]; | ||
32 | |||
33 | #define SWSUSP_SIG "S1SUSPEND" | ||
34 | |||
35 | static struct swsusp_header { | ||
36 | char reserved[PAGE_SIZE - 20 - sizeof(swp_entry_t)]; | ||
37 | swp_entry_t image; | ||
38 | char orig_sig[10]; | ||
39 | char sig[10]; | ||
40 | } __attribute__((packed, aligned(PAGE_SIZE))) swsusp_header; | ||
41 | |||
42 | /* | ||
43 | * Saving part... | ||
44 | */ | ||
45 | |||
46 | static unsigned short root_swap = 0xffff; | ||
47 | |||
48 | static int mark_swapfiles(swp_entry_t start) | ||
49 | { | ||
50 | int error; | ||
51 | |||
52 | rw_swap_page_sync(READ, | ||
53 | swp_entry(root_swap, 0), | ||
54 | virt_to_page((unsigned long)&swsusp_header)); | ||
55 | if (!memcmp("SWAP-SPACE",swsusp_header.sig, 10) || | ||
56 | !memcmp("SWAPSPACE2",swsusp_header.sig, 10)) { | ||
57 | memcpy(swsusp_header.orig_sig,swsusp_header.sig, 10); | ||
58 | memcpy(swsusp_header.sig,SWSUSP_SIG, 10); | ||
59 | swsusp_header.image = start; | ||
60 | error = rw_swap_page_sync(WRITE, | ||
61 | swp_entry(root_swap, 0), | ||
62 | virt_to_page((unsigned long) | ||
63 | &swsusp_header)); | ||
64 | } else { | ||
65 | pr_debug("swsusp: Partition is not swap space.\n"); | ||
66 | error = -ENODEV; | ||
67 | } | ||
68 | return error; | ||
69 | } | ||
70 | |||
71 | /** | ||
72 | * swsusp_swap_check - check if the resume device is a swap device | ||
73 | * and get its index (if so) | ||
74 | */ | ||
75 | |||
76 | static int swsusp_swap_check(void) /* This is called before saving image */ | ||
77 | { | ||
78 | int res = swap_type_of(swsusp_resume_device); | ||
79 | |||
80 | if (res >= 0) { | ||
81 | root_swap = res; | ||
82 | return 0; | ||
83 | } | ||
84 | return res; | ||
85 | } | ||
86 | |||
87 | /** | ||
88 | * write_page - Write one page to given swap location. | ||
89 | * @buf: Address we're writing. | ||
90 | * @offset: Offset of the swap page we're writing to. | ||
91 | */ | ||
92 | |||
93 | static int write_page(void *buf, unsigned long offset) | ||
94 | { | ||
95 | swp_entry_t entry; | ||
96 | int error = -ENOSPC; | ||
97 | |||
98 | if (offset) { | ||
99 | entry = swp_entry(root_swap, offset); | ||
100 | error = rw_swap_page_sync(WRITE, entry, virt_to_page(buf)); | ||
101 | } | ||
102 | return error; | ||
103 | } | ||
104 | |||
105 | /* | ||
106 | * The swap map is a data structure used for keeping track of each page | ||
107 | * written to a swap partition. It consists of many swap_map_page | ||
108 | * structures that contain each an array of MAP_PAGE_SIZE swap entries. | ||
109 | * These structures are stored on the swap and linked together with the | ||
110 | * help of the .next_swap member. | ||
111 | * | ||
112 | * The swap map is created during suspend. The swap map pages are | ||
113 | * allocated and populated one at a time, so we only need one memory | ||
114 | * page to set up the entire structure. | ||
115 | * | ||
116 | * During resume we also only need to use one swap_map_page structure | ||
117 | * at a time. | ||
118 | */ | ||
119 | |||
120 | #define MAP_PAGE_ENTRIES (PAGE_SIZE / sizeof(long) - 1) | ||
121 | |||
122 | struct swap_map_page { | ||
123 | unsigned long entries[MAP_PAGE_ENTRIES]; | ||
124 | unsigned long next_swap; | ||
125 | }; | ||
126 | |||
127 | /** | ||
128 | * The swap_map_handle structure is used for handling swap in | ||
129 | * a file-alike way | ||
130 | */ | ||
131 | |||
132 | struct swap_map_handle { | ||
133 | struct swap_map_page *cur; | ||
134 | unsigned long cur_swap; | ||
135 | struct bitmap_page *bitmap; | ||
136 | unsigned int k; | ||
137 | }; | ||
138 | |||
139 | static void release_swap_writer(struct swap_map_handle *handle) | ||
140 | { | ||
141 | if (handle->cur) | ||
142 | free_page((unsigned long)handle->cur); | ||
143 | handle->cur = NULL; | ||
144 | if (handle->bitmap) | ||
145 | free_bitmap(handle->bitmap); | ||
146 | handle->bitmap = NULL; | ||
147 | } | ||
148 | |||
149 | static int get_swap_writer(struct swap_map_handle *handle) | ||
150 | { | ||
151 | handle->cur = (struct swap_map_page *)get_zeroed_page(GFP_KERNEL); | ||
152 | if (!handle->cur) | ||
153 | return -ENOMEM; | ||
154 | handle->bitmap = alloc_bitmap(count_swap_pages(root_swap, 0)); | ||
155 | if (!handle->bitmap) { | ||
156 | release_swap_writer(handle); | ||
157 | return -ENOMEM; | ||
158 | } | ||
159 | handle->cur_swap = alloc_swap_page(root_swap, handle->bitmap); | ||
160 | if (!handle->cur_swap) { | ||
161 | release_swap_writer(handle); | ||
162 | return -ENOSPC; | ||
163 | } | ||
164 | handle->k = 0; | ||
165 | return 0; | ||
166 | } | ||
167 | |||
168 | static int swap_write_page(struct swap_map_handle *handle, void *buf) | ||
169 | { | ||
170 | int error; | ||
171 | unsigned long offset; | ||
172 | |||
173 | if (!handle->cur) | ||
174 | return -EINVAL; | ||
175 | offset = alloc_swap_page(root_swap, handle->bitmap); | ||
176 | error = write_page(buf, offset); | ||
177 | if (error) | ||
178 | return error; | ||
179 | handle->cur->entries[handle->k++] = offset; | ||
180 | if (handle->k >= MAP_PAGE_ENTRIES) { | ||
181 | offset = alloc_swap_page(root_swap, handle->bitmap); | ||
182 | if (!offset) | ||
183 | return -ENOSPC; | ||
184 | handle->cur->next_swap = offset; | ||
185 | error = write_page(handle->cur, handle->cur_swap); | ||
186 | if (error) | ||
187 | return error; | ||
188 | memset(handle->cur, 0, PAGE_SIZE); | ||
189 | handle->cur_swap = offset; | ||
190 | handle->k = 0; | ||
191 | } | ||
192 | return 0; | ||
193 | } | ||
194 | |||
195 | static int flush_swap_writer(struct swap_map_handle *handle) | ||
196 | { | ||
197 | if (handle->cur && handle->cur_swap) | ||
198 | return write_page(handle->cur, handle->cur_swap); | ||
199 | else | ||
200 | return -EINVAL; | ||
201 | } | ||
202 | |||
203 | /** | ||
204 | * save_image - save the suspend image data | ||
205 | */ | ||
206 | |||
207 | static int save_image(struct swap_map_handle *handle, | ||
208 | struct snapshot_handle *snapshot, | ||
209 | unsigned int nr_pages) | ||
210 | { | ||
211 | unsigned int m; | ||
212 | int ret; | ||
213 | int error = 0; | ||
214 | |||
215 | printk("Saving image data pages (%u pages) ... ", nr_pages); | ||
216 | m = nr_pages / 100; | ||
217 | if (!m) | ||
218 | m = 1; | ||
219 | nr_pages = 0; | ||
220 | do { | ||
221 | ret = snapshot_read_next(snapshot, PAGE_SIZE); | ||
222 | if (ret > 0) { | ||
223 | error = swap_write_page(handle, data_of(*snapshot)); | ||
224 | if (error) | ||
225 | break; | ||
226 | if (!(nr_pages % m)) | ||
227 | printk("\b\b\b\b%3d%%", nr_pages / m); | ||
228 | nr_pages++; | ||
229 | } | ||
230 | } while (ret > 0); | ||
231 | if (!error) | ||
232 | printk("\b\b\b\bdone\n"); | ||
233 | return error; | ||
234 | } | ||
235 | |||
236 | /** | ||
237 | * enough_swap - Make sure we have enough swap to save the image. | ||
238 | * | ||
239 | * Returns TRUE or FALSE after checking the total amount of swap | ||
240 | * space avaiable from the resume partition. | ||
241 | */ | ||
242 | |||
243 | static int enough_swap(unsigned int nr_pages) | ||
244 | { | ||
245 | unsigned int free_swap = count_swap_pages(root_swap, 1); | ||
246 | |||
247 | pr_debug("swsusp: free swap pages: %u\n", free_swap); | ||
248 | return free_swap > (nr_pages + PAGES_FOR_IO + | ||
249 | (nr_pages + PBES_PER_PAGE - 1) / PBES_PER_PAGE); | ||
250 | } | ||
251 | |||
252 | /** | ||
253 | * swsusp_write - Write entire image and metadata. | ||
254 | * | ||
255 | * It is important _NOT_ to umount filesystems at this point. We want | ||
256 | * them synced (in case something goes wrong) but we DO not want to mark | ||
257 | * filesystem clean: it is not. (And it does not matter, if we resume | ||
258 | * correctly, we'll mark system clean, anyway.) | ||
259 | */ | ||
260 | |||
261 | int swsusp_write(void) | ||
262 | { | ||
263 | struct swap_map_handle handle; | ||
264 | struct snapshot_handle snapshot; | ||
265 | struct swsusp_info *header; | ||
266 | unsigned long start; | ||
267 | int error; | ||
268 | |||
269 | if ((error = swsusp_swap_check())) { | ||
270 | printk(KERN_ERR "swsusp: Cannot find swap device, try swapon -a.\n"); | ||
271 | return error; | ||
272 | } | ||
273 | memset(&snapshot, 0, sizeof(struct snapshot_handle)); | ||
274 | error = snapshot_read_next(&snapshot, PAGE_SIZE); | ||
275 | if (error < PAGE_SIZE) | ||
276 | return error < 0 ? error : -EFAULT; | ||
277 | header = (struct swsusp_info *)data_of(snapshot); | ||
278 | if (!enough_swap(header->pages)) { | ||
279 | printk(KERN_ERR "swsusp: Not enough free swap\n"); | ||
280 | return -ENOSPC; | ||
281 | } | ||
282 | error = get_swap_writer(&handle); | ||
283 | if (!error) { | ||
284 | start = handle.cur_swap; | ||
285 | error = swap_write_page(&handle, header); | ||
286 | } | ||
287 | if (!error) | ||
288 | error = save_image(&handle, &snapshot, header->pages - 1); | ||
289 | if (!error) { | ||
290 | flush_swap_writer(&handle); | ||
291 | printk("S"); | ||
292 | error = mark_swapfiles(swp_entry(root_swap, start)); | ||
293 | printk("|\n"); | ||
294 | } | ||
295 | if (error) | ||
296 | free_all_swap_pages(root_swap, handle.bitmap); | ||
297 | release_swap_writer(&handle); | ||
298 | return error; | ||
299 | } | ||
300 | |||
301 | /* | ||
302 | * Using bio to read from swap. | ||
303 | * This code requires a bit more work than just using buffer heads | ||
304 | * but, it is the recommended way for 2.5/2.6. | ||
305 | * The following are to signal the beginning and end of I/O. Bios | ||
306 | * finish asynchronously, while we want them to happen synchronously. | ||
307 | * A simple atomic_t, and a wait loop take care of this problem. | ||
308 | */ | ||
309 | |||
310 | static atomic_t io_done = ATOMIC_INIT(0); | ||
311 | |||
312 | static int end_io(struct bio *bio, unsigned int num, int err) | ||
313 | { | ||
314 | if (!test_bit(BIO_UPTODATE, &bio->bi_flags)) | ||
315 | panic("I/O error reading memory image"); | ||
316 | atomic_set(&io_done, 0); | ||
317 | return 0; | ||
318 | } | ||
319 | |||
320 | static struct block_device *resume_bdev; | ||
321 | |||
322 | /** | ||
323 | * submit - submit BIO request. | ||
324 | * @rw: READ or WRITE. | ||
325 | * @off physical offset of page. | ||
326 | * @page: page we're reading or writing. | ||
327 | * | ||
328 | * Straight from the textbook - allocate and initialize the bio. | ||
329 | * If we're writing, make sure the page is marked as dirty. | ||
330 | * Then submit it and wait. | ||
331 | */ | ||
332 | |||
333 | static int submit(int rw, pgoff_t page_off, void *page) | ||
334 | { | ||
335 | int error = 0; | ||
336 | struct bio *bio; | ||
337 | |||
338 | bio = bio_alloc(GFP_ATOMIC, 1); | ||
339 | if (!bio) | ||
340 | return -ENOMEM; | ||
341 | bio->bi_sector = page_off * (PAGE_SIZE >> 9); | ||
342 | bio->bi_bdev = resume_bdev; | ||
343 | bio->bi_end_io = end_io; | ||
344 | |||
345 | if (bio_add_page(bio, virt_to_page(page), PAGE_SIZE, 0) < PAGE_SIZE) { | ||
346 | printk("swsusp: ERROR: adding page to bio at %ld\n",page_off); | ||
347 | error = -EFAULT; | ||
348 | goto Done; | ||
349 | } | ||
350 | |||
351 | atomic_set(&io_done, 1); | ||
352 | submit_bio(rw | (1 << BIO_RW_SYNC), bio); | ||
353 | while (atomic_read(&io_done)) | ||
354 | yield(); | ||
355 | if (rw == READ) | ||
356 | bio_set_pages_dirty(bio); | ||
357 | Done: | ||
358 | bio_put(bio); | ||
359 | return error; | ||
360 | } | ||
361 | |||
362 | static int bio_read_page(pgoff_t page_off, void *page) | ||
363 | { | ||
364 | return submit(READ, page_off, page); | ||
365 | } | ||
366 | |||
367 | static int bio_write_page(pgoff_t page_off, void *page) | ||
368 | { | ||
369 | return submit(WRITE, page_off, page); | ||
370 | } | ||
371 | |||
372 | /** | ||
373 | * The following functions allow us to read data using a swap map | ||
374 | * in a file-alike way | ||
375 | */ | ||
376 | |||
377 | static void release_swap_reader(struct swap_map_handle *handle) | ||
378 | { | ||
379 | if (handle->cur) | ||
380 | free_page((unsigned long)handle->cur); | ||
381 | handle->cur = NULL; | ||
382 | } | ||
383 | |||
384 | static int get_swap_reader(struct swap_map_handle *handle, | ||
385 | swp_entry_t start) | ||
386 | { | ||
387 | int error; | ||
388 | |||
389 | if (!swp_offset(start)) | ||
390 | return -EINVAL; | ||
391 | handle->cur = (struct swap_map_page *)get_zeroed_page(GFP_ATOMIC); | ||
392 | if (!handle->cur) | ||
393 | return -ENOMEM; | ||
394 | error = bio_read_page(swp_offset(start), handle->cur); | ||
395 | if (error) { | ||
396 | release_swap_reader(handle); | ||
397 | return error; | ||
398 | } | ||
399 | handle->k = 0; | ||
400 | return 0; | ||
401 | } | ||
402 | |||
403 | static int swap_read_page(struct swap_map_handle *handle, void *buf) | ||
404 | { | ||
405 | unsigned long offset; | ||
406 | int error; | ||
407 | |||
408 | if (!handle->cur) | ||
409 | return -EINVAL; | ||
410 | offset = handle->cur->entries[handle->k]; | ||
411 | if (!offset) | ||
412 | return -EFAULT; | ||
413 | error = bio_read_page(offset, buf); | ||
414 | if (error) | ||
415 | return error; | ||
416 | if (++handle->k >= MAP_PAGE_ENTRIES) { | ||
417 | handle->k = 0; | ||
418 | offset = handle->cur->next_swap; | ||
419 | if (!offset) | ||
420 | release_swap_reader(handle); | ||
421 | else | ||
422 | error = bio_read_page(offset, handle->cur); | ||
423 | } | ||
424 | return error; | ||
425 | } | ||
426 | |||
427 | /** | ||
428 | * load_image - load the image using the swap map handle | ||
429 | * @handle and the snapshot handle @snapshot | ||
430 | * (assume there are @nr_pages pages to load) | ||
431 | */ | ||
432 | |||
433 | static int load_image(struct swap_map_handle *handle, | ||
434 | struct snapshot_handle *snapshot, | ||
435 | unsigned int nr_pages) | ||
436 | { | ||
437 | unsigned int m; | ||
438 | int ret; | ||
439 | int error = 0; | ||
440 | |||
441 | printk("Loading image data pages (%u pages) ... ", nr_pages); | ||
442 | m = nr_pages / 100; | ||
443 | if (!m) | ||
444 | m = 1; | ||
445 | nr_pages = 0; | ||
446 | do { | ||
447 | ret = snapshot_write_next(snapshot, PAGE_SIZE); | ||
448 | if (ret > 0) { | ||
449 | error = swap_read_page(handle, data_of(*snapshot)); | ||
450 | if (error) | ||
451 | break; | ||
452 | if (!(nr_pages % m)) | ||
453 | printk("\b\b\b\b%3d%%", nr_pages / m); | ||
454 | nr_pages++; | ||
455 | } | ||
456 | } while (ret > 0); | ||
457 | if (!error) | ||
458 | printk("\b\b\b\bdone\n"); | ||
459 | if (!snapshot_image_loaded(snapshot)) | ||
460 | error = -ENODATA; | ||
461 | return error; | ||
462 | } | ||
463 | |||
464 | int swsusp_read(void) | ||
465 | { | ||
466 | int error; | ||
467 | struct swap_map_handle handle; | ||
468 | struct snapshot_handle snapshot; | ||
469 | struct swsusp_info *header; | ||
470 | |||
471 | if (IS_ERR(resume_bdev)) { | ||
472 | pr_debug("swsusp: block device not initialised\n"); | ||
473 | return PTR_ERR(resume_bdev); | ||
474 | } | ||
475 | |||
476 | memset(&snapshot, 0, sizeof(struct snapshot_handle)); | ||
477 | error = snapshot_write_next(&snapshot, PAGE_SIZE); | ||
478 | if (error < PAGE_SIZE) | ||
479 | return error < 0 ? error : -EFAULT; | ||
480 | header = (struct swsusp_info *)data_of(snapshot); | ||
481 | error = get_swap_reader(&handle, swsusp_header.image); | ||
482 | if (!error) | ||
483 | error = swap_read_page(&handle, header); | ||
484 | if (!error) | ||
485 | error = load_image(&handle, &snapshot, header->pages - 1); | ||
486 | release_swap_reader(&handle); | ||
487 | |||
488 | blkdev_put(resume_bdev); | ||
489 | |||
490 | if (!error) | ||
491 | pr_debug("swsusp: Reading resume file was successful\n"); | ||
492 | else | ||
493 | pr_debug("swsusp: Error %d resuming\n", error); | ||
494 | return error; | ||
495 | } | ||
496 | |||
497 | /** | ||
498 | * swsusp_check - Check for swsusp signature in the resume device | ||
499 | */ | ||
500 | |||
501 | int swsusp_check(void) | ||
502 | { | ||
503 | int error; | ||
504 | |||
505 | resume_bdev = open_by_devnum(swsusp_resume_device, FMODE_READ); | ||
506 | if (!IS_ERR(resume_bdev)) { | ||
507 | set_blocksize(resume_bdev, PAGE_SIZE); | ||
508 | memset(&swsusp_header, 0, sizeof(swsusp_header)); | ||
509 | if ((error = bio_read_page(0, &swsusp_header))) | ||
510 | return error; | ||
511 | if (!memcmp(SWSUSP_SIG, swsusp_header.sig, 10)) { | ||
512 | memcpy(swsusp_header.sig, swsusp_header.orig_sig, 10); | ||
513 | /* Reset swap signature now */ | ||
514 | error = bio_write_page(0, &swsusp_header); | ||
515 | } else { | ||
516 | return -EINVAL; | ||
517 | } | ||
518 | if (error) | ||
519 | blkdev_put(resume_bdev); | ||
520 | else | ||
521 | pr_debug("swsusp: Signature found, resuming\n"); | ||
522 | } else { | ||
523 | error = PTR_ERR(resume_bdev); | ||
524 | } | ||
525 | |||
526 | if (error) | ||
527 | pr_debug("swsusp: Error %d check for resume file\n", error); | ||
528 | |||
529 | return error; | ||
530 | } | ||
531 | |||
532 | /** | ||
533 | * swsusp_close - close swap device. | ||
534 | */ | ||
535 | |||
536 | void swsusp_close(void) | ||
537 | { | ||
538 | if (IS_ERR(resume_bdev)) { | ||
539 | pr_debug("swsusp: block device not initialised\n"); | ||
540 | return; | ||
541 | } | ||
542 | |||
543 | blkdev_put(resume_bdev); | ||
544 | } | ||
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 | } | ||