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 | } | ||
