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