diff options
Diffstat (limited to 'kernel/power/swsusp.c')
-rw-r--r-- | kernel/power/swsusp.c | 198 |
1 files changed, 0 insertions, 198 deletions
diff --git a/kernel/power/swsusp.c b/kernel/power/swsusp.c index 78c35047586d..6a07f4dbf2f8 100644 --- a/kernel/power/swsusp.c +++ b/kernel/power/swsusp.c | |||
@@ -55,14 +55,6 @@ | |||
55 | 55 | ||
56 | #include "power.h" | 56 | #include "power.h" |
57 | 57 | ||
58 | /* | ||
59 | * Preferred image size in bytes (tunable via /sys/power/image_size). | ||
60 | * When it is set to N, swsusp will do its best to ensure the image | ||
61 | * size will not exceed N bytes, but if that is impossible, it will | ||
62 | * try to create the smallest image possible. | ||
63 | */ | ||
64 | unsigned long image_size = 500 * 1024 * 1024; | ||
65 | |||
66 | int in_suspend __nosavedata = 0; | 58 | int in_suspend __nosavedata = 0; |
67 | 59 | ||
68 | /** | 60 | /** |
@@ -194,193 +186,3 @@ void swsusp_show_speed(struct timeval *start, struct timeval *stop, | |||
194 | centisecs / 100, centisecs % 100, | 186 | centisecs / 100, centisecs % 100, |
195 | kps / 1000, (kps % 1000) / 10); | 187 | kps / 1000, (kps % 1000) / 10); |
196 | } | 188 | } |
197 | |||
198 | /** | ||
199 | * swsusp_shrink_memory - Try to free as much memory as needed | ||
200 | * | ||
201 | * ... but do not OOM-kill anyone | ||
202 | * | ||
203 | * Notice: all userland should be stopped before it is called, or | ||
204 | * livelock is possible. | ||
205 | */ | ||
206 | |||
207 | #define SHRINK_BITE 10000 | ||
208 | static inline unsigned long __shrink_memory(long tmp) | ||
209 | { | ||
210 | if (tmp > SHRINK_BITE) | ||
211 | tmp = SHRINK_BITE; | ||
212 | return shrink_all_memory(tmp); | ||
213 | } | ||
214 | |||
215 | int swsusp_shrink_memory(void) | ||
216 | { | ||
217 | long tmp; | ||
218 | struct zone *zone; | ||
219 | unsigned long pages = 0; | ||
220 | unsigned int i = 0; | ||
221 | char *p = "-\\|/"; | ||
222 | struct timeval start, stop; | ||
223 | |||
224 | printk(KERN_INFO "PM: Shrinking memory... "); | ||
225 | do_gettimeofday(&start); | ||
226 | do { | ||
227 | long size, highmem_size; | ||
228 | |||
229 | highmem_size = count_highmem_pages(); | ||
230 | size = count_data_pages() + PAGES_FOR_IO + SPARE_PAGES; | ||
231 | tmp = size; | ||
232 | size += highmem_size; | ||
233 | for_each_populated_zone(zone) { | ||
234 | tmp += snapshot_additional_pages(zone); | ||
235 | if (is_highmem(zone)) { | ||
236 | highmem_size -= | ||
237 | zone_page_state(zone, NR_FREE_PAGES); | ||
238 | } else { | ||
239 | tmp -= zone_page_state(zone, NR_FREE_PAGES); | ||
240 | tmp += zone->lowmem_reserve[ZONE_NORMAL]; | ||
241 | } | ||
242 | } | ||
243 | |||
244 | if (highmem_size < 0) | ||
245 | highmem_size = 0; | ||
246 | |||
247 | tmp += highmem_size; | ||
248 | if (tmp > 0) { | ||
249 | tmp = __shrink_memory(tmp); | ||
250 | if (!tmp) | ||
251 | return -ENOMEM; | ||
252 | pages += tmp; | ||
253 | } else if (size > image_size / PAGE_SIZE) { | ||
254 | tmp = __shrink_memory(size - (image_size / PAGE_SIZE)); | ||
255 | pages += tmp; | ||
256 | } | ||
257 | printk("\b%c", p[i++%4]); | ||
258 | } while (tmp > 0); | ||
259 | do_gettimeofday(&stop); | ||
260 | printk("\bdone (%lu pages freed)\n", pages); | ||
261 | swsusp_show_speed(&start, &stop, pages, "Freed"); | ||
262 | |||
263 | return 0; | ||
264 | } | ||
265 | |||
266 | /* | ||
267 | * Platforms, like ACPI, may want us to save some memory used by them during | ||
268 | * hibernation and to restore the contents of this memory during the subsequent | ||
269 | * resume. The code below implements a mechanism allowing us to do that. | ||
270 | */ | ||
271 | |||
272 | struct nvs_page { | ||
273 | unsigned long phys_start; | ||
274 | unsigned int size; | ||
275 | void *kaddr; | ||
276 | void *data; | ||
277 | struct list_head node; | ||
278 | }; | ||
279 | |||
280 | static LIST_HEAD(nvs_list); | ||
281 | |||
282 | /** | ||
283 | * hibernate_nvs_register - register platform NVS memory region to save | ||
284 | * @start - physical address of the region | ||
285 | * @size - size of the region | ||
286 | * | ||
287 | * The NVS region need not be page-aligned (both ends) and we arrange | ||
288 | * things so that the data from page-aligned addresses in this region will | ||
289 | * be copied into separate RAM pages. | ||
290 | */ | ||
291 | int hibernate_nvs_register(unsigned long start, unsigned long size) | ||
292 | { | ||
293 | struct nvs_page *entry, *next; | ||
294 | |||
295 | while (size > 0) { | ||
296 | unsigned int nr_bytes; | ||
297 | |||
298 | entry = kzalloc(sizeof(struct nvs_page), GFP_KERNEL); | ||
299 | if (!entry) | ||
300 | goto Error; | ||
301 | |||
302 | list_add_tail(&entry->node, &nvs_list); | ||
303 | entry->phys_start = start; | ||
304 | nr_bytes = PAGE_SIZE - (start & ~PAGE_MASK); | ||
305 | entry->size = (size < nr_bytes) ? size : nr_bytes; | ||
306 | |||
307 | start += entry->size; | ||
308 | size -= entry->size; | ||
309 | } | ||
310 | return 0; | ||
311 | |||
312 | Error: | ||
313 | list_for_each_entry_safe(entry, next, &nvs_list, node) { | ||
314 | list_del(&entry->node); | ||
315 | kfree(entry); | ||
316 | } | ||
317 | return -ENOMEM; | ||
318 | } | ||
319 | |||
320 | /** | ||
321 | * hibernate_nvs_free - free data pages allocated for saving NVS regions | ||
322 | */ | ||
323 | void hibernate_nvs_free(void) | ||
324 | { | ||
325 | struct nvs_page *entry; | ||
326 | |||
327 | list_for_each_entry(entry, &nvs_list, node) | ||
328 | if (entry->data) { | ||
329 | free_page((unsigned long)entry->data); | ||
330 | entry->data = NULL; | ||
331 | if (entry->kaddr) { | ||
332 | iounmap(entry->kaddr); | ||
333 | entry->kaddr = NULL; | ||
334 | } | ||
335 | } | ||
336 | } | ||
337 | |||
338 | /** | ||
339 | * hibernate_nvs_alloc - allocate memory necessary for saving NVS regions | ||
340 | */ | ||
341 | int hibernate_nvs_alloc(void) | ||
342 | { | ||
343 | struct nvs_page *entry; | ||
344 | |||
345 | list_for_each_entry(entry, &nvs_list, node) { | ||
346 | entry->data = (void *)__get_free_page(GFP_KERNEL); | ||
347 | if (!entry->data) { | ||
348 | hibernate_nvs_free(); | ||
349 | return -ENOMEM; | ||
350 | } | ||
351 | } | ||
352 | return 0; | ||
353 | } | ||
354 | |||
355 | /** | ||
356 | * hibernate_nvs_save - save NVS memory regions | ||
357 | */ | ||
358 | void hibernate_nvs_save(void) | ||
359 | { | ||
360 | struct nvs_page *entry; | ||
361 | |||
362 | printk(KERN_INFO "PM: Saving platform NVS memory\n"); | ||
363 | |||
364 | list_for_each_entry(entry, &nvs_list, node) | ||
365 | if (entry->data) { | ||
366 | entry->kaddr = ioremap(entry->phys_start, entry->size); | ||
367 | memcpy(entry->data, entry->kaddr, entry->size); | ||
368 | } | ||
369 | } | ||
370 | |||
371 | /** | ||
372 | * hibernate_nvs_restore - restore NVS memory regions | ||
373 | * | ||
374 | * This function is going to be called with interrupts disabled, so it | ||
375 | * cannot iounmap the virtual addresses used to access the NVS region. | ||
376 | */ | ||
377 | void hibernate_nvs_restore(void) | ||
378 | { | ||
379 | struct nvs_page *entry; | ||
380 | |||
381 | printk(KERN_INFO "PM: Restoring platform NVS memory\n"); | ||
382 | |||
383 | list_for_each_entry(entry, &nvs_list, node) | ||
384 | if (entry->data) | ||
385 | memcpy(entry->kaddr, entry->data, entry->size); | ||
386 | } | ||