diff options
Diffstat (limited to 'kernel/power')
| -rw-r--r-- | kernel/power/Kconfig | 17 | ||||
| -rw-r--r-- | kernel/power/hibernate.c | 25 | ||||
| -rw-r--r-- | kernel/power/main.c | 29 | ||||
| -rw-r--r-- | kernel/power/power.h | 10 | ||||
| -rw-r--r-- | kernel/power/process.c | 11 | ||||
| -rw-r--r-- | kernel/power/snapshot.c | 13 | ||||
| -rw-r--r-- | kernel/power/swap.c | 300 |
7 files changed, 376 insertions, 29 deletions
diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig index ca6066a6952e..29bff6117abc 100644 --- a/kernel/power/Kconfig +++ b/kernel/power/Kconfig | |||
| @@ -86,6 +86,7 @@ config PM_SLEEP_SMP | |||
| 86 | depends on SMP | 86 | depends on SMP |
| 87 | depends on ARCH_SUSPEND_POSSIBLE || ARCH_HIBERNATION_POSSIBLE | 87 | depends on ARCH_SUSPEND_POSSIBLE || ARCH_HIBERNATION_POSSIBLE |
| 88 | depends on PM_SLEEP | 88 | depends on PM_SLEEP |
| 89 | select HOTPLUG | ||
| 89 | select HOTPLUG_CPU | 90 | select HOTPLUG_CPU |
| 90 | default y | 91 | default y |
| 91 | 92 | ||
| @@ -137,6 +138,8 @@ config SUSPEND_FREEZER | |||
| 137 | config HIBERNATION | 138 | config HIBERNATION |
| 138 | bool "Hibernation (aka 'suspend to disk')" | 139 | bool "Hibernation (aka 'suspend to disk')" |
| 139 | depends on PM && SWAP && ARCH_HIBERNATION_POSSIBLE | 140 | depends on PM && SWAP && ARCH_HIBERNATION_POSSIBLE |
| 141 | select LZO_COMPRESS | ||
| 142 | select LZO_DECOMPRESS | ||
| 140 | select SUSPEND_NVS if HAS_IOMEM | 143 | select SUSPEND_NVS if HAS_IOMEM |
| 141 | ---help--- | 144 | ---help--- |
| 142 | Enable the suspend to disk (STD) functionality, which is usually | 145 | Enable the suspend to disk (STD) functionality, which is usually |
| @@ -242,3 +245,17 @@ config PM_OPS | |||
| 242 | bool | 245 | bool |
| 243 | depends on PM_SLEEP || PM_RUNTIME | 246 | depends on PM_SLEEP || PM_RUNTIME |
| 244 | default y | 247 | default y |
| 248 | |||
| 249 | config PM_OPP | ||
| 250 | bool "Operating Performance Point (OPP) Layer library" | ||
| 251 | depends on PM | ||
| 252 | ---help--- | ||
| 253 | SOCs have a standard set of tuples consisting of frequency and | ||
| 254 | voltage pairs that the device will support per voltage domain. This | ||
| 255 | is called Operating Performance Point or OPP. The actual definitions | ||
| 256 | of OPP varies over silicon within the same family of devices. | ||
| 257 | |||
| 258 | OPP layer organizes the data internally using device pointers | ||
| 259 | representing individual voltage domains and provides SOC | ||
| 260 | implementations a ready to use framework to manage OPPs. | ||
| 261 | For more information, read <file:Documentation/power/opp.txt> | ||
diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c index 8dc31e02ae12..657272e91d0a 100644 --- a/kernel/power/hibernate.c +++ b/kernel/power/hibernate.c | |||
| @@ -29,6 +29,7 @@ | |||
| 29 | #include "power.h" | 29 | #include "power.h" |
| 30 | 30 | ||
| 31 | 31 | ||
| 32 | static int nocompress = 0; | ||
| 32 | static int noresume = 0; | 33 | static int noresume = 0; |
| 33 | static char resume_file[256] = CONFIG_PM_STD_PARTITION; | 34 | static char resume_file[256] = CONFIG_PM_STD_PARTITION; |
| 34 | dev_t swsusp_resume_device; | 35 | dev_t swsusp_resume_device; |
| @@ -638,6 +639,8 @@ int hibernate(void) | |||
| 638 | 639 | ||
| 639 | if (hibernation_mode == HIBERNATION_PLATFORM) | 640 | if (hibernation_mode == HIBERNATION_PLATFORM) |
| 640 | flags |= SF_PLATFORM_MODE; | 641 | flags |= SF_PLATFORM_MODE; |
| 642 | if (nocompress) | ||
| 643 | flags |= SF_NOCOMPRESS_MODE; | ||
| 641 | pr_debug("PM: writing image.\n"); | 644 | pr_debug("PM: writing image.\n"); |
| 642 | error = swsusp_write(flags); | 645 | error = swsusp_write(flags); |
| 643 | swsusp_free(); | 646 | swsusp_free(); |
| @@ -705,7 +708,7 @@ static int software_resume(void) | |||
| 705 | goto Unlock; | 708 | goto Unlock; |
| 706 | } | 709 | } |
| 707 | 710 | ||
| 708 | pr_debug("PM: Checking image partition %s\n", resume_file); | 711 | pr_debug("PM: Checking hibernation image partition %s\n", resume_file); |
| 709 | 712 | ||
| 710 | /* Check if the device is there */ | 713 | /* Check if the device is there */ |
| 711 | swsusp_resume_device = name_to_dev_t(resume_file); | 714 | swsusp_resume_device = name_to_dev_t(resume_file); |
| @@ -730,10 +733,10 @@ static int software_resume(void) | |||
| 730 | } | 733 | } |
| 731 | 734 | ||
| 732 | Check_image: | 735 | Check_image: |
| 733 | pr_debug("PM: Resume from partition %d:%d\n", | 736 | pr_debug("PM: Hibernation image partition %d:%d present\n", |
| 734 | MAJOR(swsusp_resume_device), MINOR(swsusp_resume_device)); | 737 | MAJOR(swsusp_resume_device), MINOR(swsusp_resume_device)); |
| 735 | 738 | ||
| 736 | pr_debug("PM: Checking hibernation image.\n"); | 739 | pr_debug("PM: Looking for hibernation image.\n"); |
| 737 | error = swsusp_check(); | 740 | error = swsusp_check(); |
| 738 | if (error) | 741 | if (error) |
| 739 | goto Unlock; | 742 | goto Unlock; |
| @@ -765,14 +768,14 @@ static int software_resume(void) | |||
| 765 | goto Done; | 768 | goto Done; |
| 766 | } | 769 | } |
| 767 | 770 | ||
| 768 | pr_debug("PM: Reading hibernation image.\n"); | 771 | pr_debug("PM: Loading hibernation image.\n"); |
| 769 | 772 | ||
| 770 | error = swsusp_read(&flags); | 773 | error = swsusp_read(&flags); |
| 771 | swsusp_close(FMODE_READ); | 774 | swsusp_close(FMODE_READ); |
| 772 | if (!error) | 775 | if (!error) |
| 773 | hibernation_restore(flags & SF_PLATFORM_MODE); | 776 | hibernation_restore(flags & SF_PLATFORM_MODE); |
| 774 | 777 | ||
| 775 | printk(KERN_ERR "PM: Restore failed, recovering.\n"); | 778 | printk(KERN_ERR "PM: Failed to load hibernation image, recovering.\n"); |
| 776 | swsusp_free(); | 779 | swsusp_free(); |
| 777 | thaw_processes(); | 780 | thaw_processes(); |
| 778 | Done: | 781 | Done: |
| @@ -785,7 +788,7 @@ static int software_resume(void) | |||
| 785 | /* For success case, the suspend path will release the lock */ | 788 | /* For success case, the suspend path will release the lock */ |
| 786 | Unlock: | 789 | Unlock: |
| 787 | mutex_unlock(&pm_mutex); | 790 | mutex_unlock(&pm_mutex); |
| 788 | pr_debug("PM: Resume from disk failed.\n"); | 791 | pr_debug("PM: Hibernation image not present or could not be loaded.\n"); |
| 789 | return error; | 792 | return error; |
| 790 | close_finish: | 793 | close_finish: |
| 791 | swsusp_close(FMODE_READ); | 794 | swsusp_close(FMODE_READ); |
| @@ -1004,6 +1007,15 @@ static int __init resume_offset_setup(char *str) | |||
| 1004 | return 1; | 1007 | return 1; |
| 1005 | } | 1008 | } |
| 1006 | 1009 | ||
| 1010 | static int __init hibernate_setup(char *str) | ||
| 1011 | { | ||
| 1012 | if (!strncmp(str, "noresume", 8)) | ||
| 1013 | noresume = 1; | ||
| 1014 | else if (!strncmp(str, "nocompress", 10)) | ||
| 1015 | nocompress = 1; | ||
| 1016 | return 1; | ||
| 1017 | } | ||
| 1018 | |||
| 1007 | static int __init noresume_setup(char *str) | 1019 | static int __init noresume_setup(char *str) |
| 1008 | { | 1020 | { |
| 1009 | noresume = 1; | 1021 | noresume = 1; |
| @@ -1013,3 +1025,4 @@ static int __init noresume_setup(char *str) | |||
| 1013 | __setup("noresume", noresume_setup); | 1025 | __setup("noresume", noresume_setup); |
| 1014 | __setup("resume_offset=", resume_offset_setup); | 1026 | __setup("resume_offset=", resume_offset_setup); |
| 1015 | __setup("resume=", resume_setup); | 1027 | __setup("resume=", resume_setup); |
| 1028 | __setup("hibernate=", hibernate_setup); | ||
diff --git a/kernel/power/main.c b/kernel/power/main.c index 62b0bc6e4983..7b5db6a8561e 100644 --- a/kernel/power/main.c +++ b/kernel/power/main.c | |||
| @@ -237,18 +237,18 @@ static ssize_t wakeup_count_show(struct kobject *kobj, | |||
| 237 | struct kobj_attribute *attr, | 237 | struct kobj_attribute *attr, |
| 238 | char *buf) | 238 | char *buf) |
| 239 | { | 239 | { |
| 240 | unsigned long val; | 240 | unsigned int val; |
| 241 | 241 | ||
| 242 | return pm_get_wakeup_count(&val) ? sprintf(buf, "%lu\n", val) : -EINTR; | 242 | return pm_get_wakeup_count(&val) ? sprintf(buf, "%u\n", val) : -EINTR; |
| 243 | } | 243 | } |
| 244 | 244 | ||
| 245 | static ssize_t wakeup_count_store(struct kobject *kobj, | 245 | static ssize_t wakeup_count_store(struct kobject *kobj, |
| 246 | struct kobj_attribute *attr, | 246 | struct kobj_attribute *attr, |
| 247 | const char *buf, size_t n) | 247 | const char *buf, size_t n) |
| 248 | { | 248 | { |
| 249 | unsigned long val; | 249 | unsigned int val; |
| 250 | 250 | ||
| 251 | if (sscanf(buf, "%lu", &val) == 1) { | 251 | if (sscanf(buf, "%u", &val) == 1) { |
| 252 | if (pm_save_wakeup_count(val)) | 252 | if (pm_save_wakeup_count(val)) |
| 253 | return n; | 253 | return n; |
| 254 | } | 254 | } |
| @@ -281,12 +281,30 @@ pm_trace_store(struct kobject *kobj, struct kobj_attribute *attr, | |||
| 281 | } | 281 | } |
| 282 | 282 | ||
| 283 | power_attr(pm_trace); | 283 | power_attr(pm_trace); |
| 284 | |||
| 285 | static ssize_t pm_trace_dev_match_show(struct kobject *kobj, | ||
| 286 | struct kobj_attribute *attr, | ||
| 287 | char *buf) | ||
| 288 | { | ||
| 289 | return show_trace_dev_match(buf, PAGE_SIZE); | ||
| 290 | } | ||
| 291 | |||
| 292 | static ssize_t | ||
| 293 | pm_trace_dev_match_store(struct kobject *kobj, struct kobj_attribute *attr, | ||
| 294 | const char *buf, size_t n) | ||
| 295 | { | ||
| 296 | return -EINVAL; | ||
| 297 | } | ||
| 298 | |||
| 299 | power_attr(pm_trace_dev_match); | ||
| 300 | |||
| 284 | #endif /* CONFIG_PM_TRACE */ | 301 | #endif /* CONFIG_PM_TRACE */ |
| 285 | 302 | ||
| 286 | static struct attribute * g[] = { | 303 | static struct attribute * g[] = { |
| 287 | &state_attr.attr, | 304 | &state_attr.attr, |
| 288 | #ifdef CONFIG_PM_TRACE | 305 | #ifdef CONFIG_PM_TRACE |
| 289 | &pm_trace_attr.attr, | 306 | &pm_trace_attr.attr, |
| 307 | &pm_trace_dev_match_attr.attr, | ||
| 290 | #endif | 308 | #endif |
| 291 | #ifdef CONFIG_PM_SLEEP | 309 | #ifdef CONFIG_PM_SLEEP |
| 292 | &pm_async_attr.attr, | 310 | &pm_async_attr.attr, |
| @@ -308,7 +326,7 @@ EXPORT_SYMBOL_GPL(pm_wq); | |||
| 308 | 326 | ||
| 309 | static int __init pm_start_workqueue(void) | 327 | static int __init pm_start_workqueue(void) |
| 310 | { | 328 | { |
| 311 | pm_wq = create_freezeable_workqueue("pm"); | 329 | pm_wq = alloc_workqueue("pm", WQ_FREEZEABLE, 0); |
| 312 | 330 | ||
| 313 | return pm_wq ? 0 : -ENOMEM; | 331 | return pm_wq ? 0 : -ENOMEM; |
| 314 | } | 332 | } |
| @@ -321,6 +339,7 @@ static int __init pm_init(void) | |||
| 321 | int error = pm_start_workqueue(); | 339 | int error = pm_start_workqueue(); |
| 322 | if (error) | 340 | if (error) |
| 323 | return error; | 341 | return error; |
| 342 | hibernate_image_size_init(); | ||
| 324 | power_kobj = kobject_create_and_add("power", NULL); | 343 | power_kobj = kobject_create_and_add("power", NULL); |
| 325 | if (!power_kobj) | 344 | if (!power_kobj) |
| 326 | return -ENOMEM; | 345 | return -ENOMEM; |
diff --git a/kernel/power/power.h b/kernel/power/power.h index 006270fe382d..03634be55f62 100644 --- a/kernel/power/power.h +++ b/kernel/power/power.h | |||
| @@ -14,6 +14,9 @@ struct swsusp_info { | |||
| 14 | } __attribute__((aligned(PAGE_SIZE))); | 14 | } __attribute__((aligned(PAGE_SIZE))); |
| 15 | 15 | ||
| 16 | #ifdef CONFIG_HIBERNATION | 16 | #ifdef CONFIG_HIBERNATION |
| 17 | /* kernel/power/snapshot.c */ | ||
| 18 | extern void __init hibernate_image_size_init(void); | ||
| 19 | |||
| 17 | #ifdef CONFIG_ARCH_HIBERNATION_HEADER | 20 | #ifdef CONFIG_ARCH_HIBERNATION_HEADER |
| 18 | /* Maximum size of architecture specific data in a hibernation header */ | 21 | /* Maximum size of architecture specific data in a hibernation header */ |
| 19 | #define MAX_ARCH_HEADER_SIZE (sizeof(struct new_utsname) + 4) | 22 | #define MAX_ARCH_HEADER_SIZE (sizeof(struct new_utsname) + 4) |
| @@ -49,7 +52,11 @@ static inline char *check_image_kernel(struct swsusp_info *info) | |||
| 49 | extern int hibernation_snapshot(int platform_mode); | 52 | extern int hibernation_snapshot(int platform_mode); |
| 50 | extern int hibernation_restore(int platform_mode); | 53 | extern int hibernation_restore(int platform_mode); |
| 51 | extern int hibernation_platform_enter(void); | 54 | extern int hibernation_platform_enter(void); |
| 52 | #endif | 55 | |
| 56 | #else /* !CONFIG_HIBERNATION */ | ||
| 57 | |||
| 58 | static inline void hibernate_image_size_init(void) {} | ||
| 59 | #endif /* !CONFIG_HIBERNATION */ | ||
| 53 | 60 | ||
| 54 | extern int pfn_is_nosave(unsigned long); | 61 | extern int pfn_is_nosave(unsigned long); |
| 55 | 62 | ||
| @@ -134,6 +141,7 @@ extern int swsusp_swap_in_use(void); | |||
| 134 | * the image header. | 141 | * the image header. |
| 135 | */ | 142 | */ |
| 136 | #define SF_PLATFORM_MODE 1 | 143 | #define SF_PLATFORM_MODE 1 |
| 144 | #define SF_NOCOMPRESS_MODE 2 | ||
| 137 | 145 | ||
| 138 | /* kernel/power/hibernate.c */ | 146 | /* kernel/power/hibernate.c */ |
| 139 | extern int swsusp_check(void); | 147 | extern int swsusp_check(void); |
diff --git a/kernel/power/process.c b/kernel/power/process.c index 028a99598f49..e50b4c1b2a0f 100644 --- a/kernel/power/process.c +++ b/kernel/power/process.c | |||
| @@ -40,6 +40,7 @@ static int try_to_freeze_tasks(bool sig_only) | |||
| 40 | struct timeval start, end; | 40 | struct timeval start, end; |
| 41 | u64 elapsed_csecs64; | 41 | u64 elapsed_csecs64; |
| 42 | unsigned int elapsed_csecs; | 42 | unsigned int elapsed_csecs; |
| 43 | bool wakeup = false; | ||
| 43 | 44 | ||
| 44 | do_gettimeofday(&start); | 45 | do_gettimeofday(&start); |
| 45 | 46 | ||
| @@ -78,6 +79,11 @@ static int try_to_freeze_tasks(bool sig_only) | |||
| 78 | if (!todo || time_after(jiffies, end_time)) | 79 | if (!todo || time_after(jiffies, end_time)) |
| 79 | break; | 80 | break; |
| 80 | 81 | ||
| 82 | if (!pm_check_wakeup_events()) { | ||
| 83 | wakeup = true; | ||
| 84 | break; | ||
| 85 | } | ||
| 86 | |||
| 81 | /* | 87 | /* |
| 82 | * We need to retry, but first give the freezing tasks some | 88 | * We need to retry, but first give the freezing tasks some |
| 83 | * time to enter the regrigerator. | 89 | * time to enter the regrigerator. |
| @@ -97,8 +103,9 @@ static int try_to_freeze_tasks(bool sig_only) | |||
| 97 | * but it cleans up leftover PF_FREEZE requests. | 103 | * but it cleans up leftover PF_FREEZE requests. |
| 98 | */ | 104 | */ |
| 99 | printk("\n"); | 105 | printk("\n"); |
| 100 | printk(KERN_ERR "Freezing of tasks failed after %d.%02d seconds " | 106 | printk(KERN_ERR "Freezing of tasks %s after %d.%02d seconds " |
| 101 | "(%d tasks refusing to freeze, wq_busy=%d):\n", | 107 | "(%d tasks refusing to freeze, wq_busy=%d):\n", |
| 108 | wakeup ? "aborted" : "failed", | ||
| 102 | elapsed_csecs / 100, elapsed_csecs % 100, | 109 | elapsed_csecs / 100, elapsed_csecs % 100, |
| 103 | todo - wq_busy, wq_busy); | 110 | todo - wq_busy, wq_busy); |
| 104 | 111 | ||
| @@ -107,7 +114,7 @@ static int try_to_freeze_tasks(bool sig_only) | |||
| 107 | read_lock(&tasklist_lock); | 114 | read_lock(&tasklist_lock); |
| 108 | do_each_thread(g, p) { | 115 | do_each_thread(g, p) { |
| 109 | task_lock(p); | 116 | task_lock(p); |
| 110 | if (freezing(p) && !freezer_should_skip(p)) | 117 | if (!wakeup && freezing(p) && !freezer_should_skip(p)) |
| 111 | sched_show_task(p); | 118 | sched_show_task(p); |
| 112 | cancel_freezing(p); | 119 | cancel_freezing(p); |
| 113 | task_unlock(p); | 120 | task_unlock(p); |
diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c index d3f795f01bbc..ac7eb109f196 100644 --- a/kernel/power/snapshot.c +++ b/kernel/power/snapshot.c | |||
| @@ -46,7 +46,12 @@ static void swsusp_unset_page_forbidden(struct page *); | |||
| 46 | * size will not exceed N bytes, but if that is impossible, it will | 46 | * size will not exceed N bytes, but if that is impossible, it will |
| 47 | * try to create the smallest image possible. | 47 | * try to create the smallest image possible. |
| 48 | */ | 48 | */ |
| 49 | unsigned long image_size = 500 * 1024 * 1024; | 49 | unsigned long image_size; |
| 50 | |||
| 51 | void __init hibernate_image_size_init(void) | ||
| 52 | { | ||
| 53 | image_size = ((totalram_pages * 2) / 5) * PAGE_SIZE; | ||
| 54 | } | ||
| 50 | 55 | ||
| 51 | /* List of PBEs needed for restoring the pages that were allocated before | 56 | /* List of PBEs needed for restoring the pages that were allocated before |
| 52 | * the suspend and included in the suspend image, but have also been | 57 | * the suspend and included in the suspend image, but have also been |
| @@ -1318,12 +1323,14 @@ int hibernate_preallocate_memory(void) | |||
| 1318 | 1323 | ||
| 1319 | /* Compute the maximum number of saveable pages to leave in memory. */ | 1324 | /* Compute the maximum number of saveable pages to leave in memory. */ |
| 1320 | max_size = (count - (size + PAGES_FOR_IO)) / 2 - 2 * SPARE_PAGES; | 1325 | max_size = (count - (size + PAGES_FOR_IO)) / 2 - 2 * SPARE_PAGES; |
| 1326 | /* Compute the desired number of image pages specified by image_size. */ | ||
| 1321 | size = DIV_ROUND_UP(image_size, PAGE_SIZE); | 1327 | size = DIV_ROUND_UP(image_size, PAGE_SIZE); |
| 1322 | if (size > max_size) | 1328 | if (size > max_size) |
| 1323 | size = max_size; | 1329 | size = max_size; |
| 1324 | /* | 1330 | /* |
| 1325 | * If the maximum is not less than the current number of saveable pages | 1331 | * If the desired number of image pages is at least as large as the |
| 1326 | * in memory, allocate page frames for the image and we're done. | 1332 | * current number of saveable pages in memory, allocate page frames for |
| 1333 | * the image and we're done. | ||
| 1327 | */ | 1334 | */ |
| 1328 | if (size >= saveable) { | 1335 | if (size >= saveable) { |
| 1329 | pages = preallocate_image_highmem(save_highmem); | 1336 | pages = preallocate_image_highmem(save_highmem); |
diff --git a/kernel/power/swap.c b/kernel/power/swap.c index e6a5bdf61a37..916eaa790399 100644 --- a/kernel/power/swap.c +++ b/kernel/power/swap.c | |||
| @@ -24,10 +24,12 @@ | |||
| 24 | #include <linux/swapops.h> | 24 | #include <linux/swapops.h> |
| 25 | #include <linux/pm.h> | 25 | #include <linux/pm.h> |
| 26 | #include <linux/slab.h> | 26 | #include <linux/slab.h> |
| 27 | #include <linux/lzo.h> | ||
| 28 | #include <linux/vmalloc.h> | ||
| 27 | 29 | ||
| 28 | #include "power.h" | 30 | #include "power.h" |
| 29 | 31 | ||
| 30 | #define SWSUSP_SIG "S1SUSPEND" | 32 | #define HIBERNATE_SIG "LINHIB0001" |
| 31 | 33 | ||
| 32 | /* | 34 | /* |
| 33 | * The swap map is a data structure used for keeping track of each page | 35 | * The swap map is a data structure used for keeping track of each page |
| @@ -193,7 +195,7 @@ static int mark_swapfiles(struct swap_map_handle *handle, unsigned int flags) | |||
| 193 | if (!memcmp("SWAP-SPACE",swsusp_header->sig, 10) || | 195 | if (!memcmp("SWAP-SPACE",swsusp_header->sig, 10) || |
| 194 | !memcmp("SWAPSPACE2",swsusp_header->sig, 10)) { | 196 | !memcmp("SWAPSPACE2",swsusp_header->sig, 10)) { |
| 195 | memcpy(swsusp_header->orig_sig,swsusp_header->sig, 10); | 197 | memcpy(swsusp_header->orig_sig,swsusp_header->sig, 10); |
| 196 | memcpy(swsusp_header->sig,SWSUSP_SIG, 10); | 198 | memcpy(swsusp_header->sig, HIBERNATE_SIG, 10); |
| 197 | swsusp_header->image = handle->first_sector; | 199 | swsusp_header->image = handle->first_sector; |
| 198 | swsusp_header->flags = flags; | 200 | swsusp_header->flags = flags; |
| 199 | error = hib_bio_write_page(swsusp_resume_block, | 201 | error = hib_bio_write_page(swsusp_resume_block, |
| @@ -357,6 +359,18 @@ static int swap_writer_finish(struct swap_map_handle *handle, | |||
| 357 | return error; | 359 | return error; |
| 358 | } | 360 | } |
| 359 | 361 | ||
| 362 | /* We need to remember how much compressed data we need to read. */ | ||
| 363 | #define LZO_HEADER sizeof(size_t) | ||
| 364 | |||
| 365 | /* Number of pages/bytes we'll compress at one time. */ | ||
| 366 | #define LZO_UNC_PAGES 32 | ||
| 367 | #define LZO_UNC_SIZE (LZO_UNC_PAGES * PAGE_SIZE) | ||
| 368 | |||
| 369 | /* Number of pages/bytes we need for compressed data (worst case). */ | ||
| 370 | #define LZO_CMP_PAGES DIV_ROUND_UP(lzo1x_worst_compress(LZO_UNC_SIZE) + \ | ||
| 371 | LZO_HEADER, PAGE_SIZE) | ||
| 372 | #define LZO_CMP_SIZE (LZO_CMP_PAGES * PAGE_SIZE) | ||
| 373 | |||
| 360 | /** | 374 | /** |
| 361 | * save_image - save the suspend image data | 375 | * save_image - save the suspend image data |
| 362 | */ | 376 | */ |
| @@ -404,6 +418,137 @@ static int save_image(struct swap_map_handle *handle, | |||
| 404 | return ret; | 418 | return ret; |
| 405 | } | 419 | } |
| 406 | 420 | ||
| 421 | |||
| 422 | /** | ||
| 423 | * save_image_lzo - Save the suspend image data compressed with LZO. | ||
| 424 | * @handle: Swap mam handle to use for saving the image. | ||
| 425 | * @snapshot: Image to read data from. | ||
| 426 | * @nr_to_write: Number of pages to save. | ||
| 427 | */ | ||
| 428 | static int save_image_lzo(struct swap_map_handle *handle, | ||
| 429 | struct snapshot_handle *snapshot, | ||
| 430 | unsigned int nr_to_write) | ||
| 431 | { | ||
| 432 | unsigned int m; | ||
| 433 | int ret = 0; | ||
| 434 | int nr_pages; | ||
| 435 | int err2; | ||
| 436 | struct bio *bio; | ||
| 437 | struct timeval start; | ||
| 438 | struct timeval stop; | ||
| 439 | size_t off, unc_len, cmp_len; | ||
| 440 | unsigned char *unc, *cmp, *wrk, *page; | ||
| 441 | |||
| 442 | page = (void *)__get_free_page(__GFP_WAIT | __GFP_HIGH); | ||
| 443 | if (!page) { | ||
| 444 | printk(KERN_ERR "PM: Failed to allocate LZO page\n"); | ||
| 445 | return -ENOMEM; | ||
| 446 | } | ||
| 447 | |||
| 448 | wrk = vmalloc(LZO1X_1_MEM_COMPRESS); | ||
| 449 | if (!wrk) { | ||
| 450 | printk(KERN_ERR "PM: Failed to allocate LZO workspace\n"); | ||
| 451 | free_page((unsigned long)page); | ||
| 452 | return -ENOMEM; | ||
| 453 | } | ||
| 454 | |||
| 455 | unc = vmalloc(LZO_UNC_SIZE); | ||
| 456 | if (!unc) { | ||
| 457 | printk(KERN_ERR "PM: Failed to allocate LZO uncompressed\n"); | ||
| 458 | vfree(wrk); | ||
| 459 | free_page((unsigned long)page); | ||
| 460 | return -ENOMEM; | ||
| 461 | } | ||
| 462 | |||
| 463 | cmp = vmalloc(LZO_CMP_SIZE); | ||
| 464 | if (!cmp) { | ||
| 465 | printk(KERN_ERR "PM: Failed to allocate LZO compressed\n"); | ||
| 466 | vfree(unc); | ||
| 467 | vfree(wrk); | ||
| 468 | free_page((unsigned long)page); | ||
| 469 | return -ENOMEM; | ||
| 470 | } | ||
| 471 | |||
| 472 | printk(KERN_INFO | ||
| 473 | "PM: Compressing and saving image data (%u pages) ... ", | ||
| 474 | nr_to_write); | ||
| 475 | m = nr_to_write / 100; | ||
| 476 | if (!m) | ||
| 477 | m = 1; | ||
| 478 | nr_pages = 0; | ||
| 479 | bio = NULL; | ||
| 480 | do_gettimeofday(&start); | ||
| 481 | for (;;) { | ||
| 482 | for (off = 0; off < LZO_UNC_SIZE; off += PAGE_SIZE) { | ||
| 483 | ret = snapshot_read_next(snapshot); | ||
| 484 | if (ret < 0) | ||
| 485 | goto out_finish; | ||
| 486 | |||
| 487 | if (!ret) | ||
| 488 | break; | ||
| 489 | |||
| 490 | memcpy(unc + off, data_of(*snapshot), PAGE_SIZE); | ||
| 491 | |||
| 492 | if (!(nr_pages % m)) | ||
| 493 | printk(KERN_CONT "\b\b\b\b%3d%%", nr_pages / m); | ||
| 494 | nr_pages++; | ||
| 495 | } | ||
| 496 | |||
| 497 | if (!off) | ||
| 498 | break; | ||
| 499 | |||
| 500 | unc_len = off; | ||
| 501 | ret = lzo1x_1_compress(unc, unc_len, | ||
| 502 | cmp + LZO_HEADER, &cmp_len, wrk); | ||
| 503 | if (ret < 0) { | ||
| 504 | printk(KERN_ERR "PM: LZO compression failed\n"); | ||
| 505 | break; | ||
| 506 | } | ||
| 507 | |||
| 508 | if (unlikely(!cmp_len || | ||
| 509 | cmp_len > lzo1x_worst_compress(unc_len))) { | ||
| 510 | printk(KERN_ERR "PM: Invalid LZO compressed length\n"); | ||
| 511 | ret = -1; | ||
| 512 | break; | ||
| 513 | } | ||
| 514 | |||
| 515 | *(size_t *)cmp = cmp_len; | ||
| 516 | |||
| 517 | /* | ||
| 518 | * Given we are writing one page at a time to disk, we copy | ||
| 519 | * that much from the buffer, although the last bit will likely | ||
| 520 | * be smaller than full page. This is OK - we saved the length | ||
| 521 | * of the compressed data, so any garbage at the end will be | ||
| 522 | * discarded when we read it. | ||
| 523 | */ | ||
| 524 | for (off = 0; off < LZO_HEADER + cmp_len; off += PAGE_SIZE) { | ||
| 525 | memcpy(page, cmp + off, PAGE_SIZE); | ||
| 526 | |||
| 527 | ret = swap_write_page(handle, page, &bio); | ||
| 528 | if (ret) | ||
| 529 | goto out_finish; | ||
| 530 | } | ||
| 531 | } | ||
| 532 | |||
| 533 | out_finish: | ||
| 534 | err2 = hib_wait_on_bio_chain(&bio); | ||
| 535 | do_gettimeofday(&stop); | ||
| 536 | if (!ret) | ||
| 537 | ret = err2; | ||
| 538 | if (!ret) | ||
| 539 | printk(KERN_CONT "\b\b\b\bdone\n"); | ||
| 540 | else | ||
| 541 | printk(KERN_CONT "\n"); | ||
| 542 | swsusp_show_speed(&start, &stop, nr_to_write, "Wrote"); | ||
| 543 | |||
| 544 | vfree(cmp); | ||
| 545 | vfree(unc); | ||
| 546 | vfree(wrk); | ||
| 547 | free_page((unsigned long)page); | ||
| 548 | |||
| 549 | return ret; | ||
| 550 | } | ||
| 551 | |||
| 407 | /** | 552 | /** |
| 408 | * enough_swap - Make sure we have enough swap to save the image. | 553 | * enough_swap - Make sure we have enough swap to save the image. |
| 409 | * | 554 | * |
| @@ -411,12 +556,16 @@ static int save_image(struct swap_map_handle *handle, | |||
| 411 | * space avaiable from the resume partition. | 556 | * space avaiable from the resume partition. |
| 412 | */ | 557 | */ |
| 413 | 558 | ||
| 414 | static int enough_swap(unsigned int nr_pages) | 559 | static int enough_swap(unsigned int nr_pages, unsigned int flags) |
| 415 | { | 560 | { |
| 416 | unsigned int free_swap = count_swap_pages(root_swap, 1); | 561 | unsigned int free_swap = count_swap_pages(root_swap, 1); |
| 562 | unsigned int required; | ||
| 417 | 563 | ||
| 418 | pr_debug("PM: Free swap pages: %u\n", free_swap); | 564 | pr_debug("PM: Free swap pages: %u\n", free_swap); |
| 419 | return free_swap > nr_pages + PAGES_FOR_IO; | 565 | |
| 566 | required = PAGES_FOR_IO + ((flags & SF_NOCOMPRESS_MODE) ? | ||
| 567 | nr_pages : (nr_pages * LZO_CMP_PAGES) / LZO_UNC_PAGES + 1); | ||
| 568 | return free_swap > required; | ||
| 420 | } | 569 | } |
| 421 | 570 | ||
| 422 | /** | 571 | /** |
| @@ -443,7 +592,7 @@ int swsusp_write(unsigned int flags) | |||
| 443 | printk(KERN_ERR "PM: Cannot get swap writer\n"); | 592 | printk(KERN_ERR "PM: Cannot get swap writer\n"); |
| 444 | return error; | 593 | return error; |
| 445 | } | 594 | } |
| 446 | if (!enough_swap(pages)) { | 595 | if (!enough_swap(pages, flags)) { |
| 447 | printk(KERN_ERR "PM: Not enough free swap\n"); | 596 | printk(KERN_ERR "PM: Not enough free swap\n"); |
| 448 | error = -ENOSPC; | 597 | error = -ENOSPC; |
| 449 | goto out_finish; | 598 | goto out_finish; |
| @@ -458,8 +607,11 @@ int swsusp_write(unsigned int flags) | |||
| 458 | } | 607 | } |
| 459 | header = (struct swsusp_info *)data_of(snapshot); | 608 | header = (struct swsusp_info *)data_of(snapshot); |
| 460 | error = swap_write_page(&handle, header, NULL); | 609 | error = swap_write_page(&handle, header, NULL); |
| 461 | if (!error) | 610 | if (!error) { |
| 462 | error = save_image(&handle, &snapshot, pages - 1); | 611 | error = (flags & SF_NOCOMPRESS_MODE) ? |
| 612 | save_image(&handle, &snapshot, pages - 1) : | ||
| 613 | save_image_lzo(&handle, &snapshot, pages - 1); | ||
| 614 | } | ||
| 463 | out_finish: | 615 | out_finish: |
| 464 | error = swap_writer_finish(&handle, flags, error); | 616 | error = swap_writer_finish(&handle, flags, error); |
| 465 | return error; | 617 | return error; |
| @@ -590,6 +742,127 @@ static int load_image(struct swap_map_handle *handle, | |||
| 590 | } | 742 | } |
| 591 | 743 | ||
| 592 | /** | 744 | /** |
| 745 | * load_image_lzo - Load compressed image data and decompress them with LZO. | ||
| 746 | * @handle: Swap map handle to use for loading data. | ||
| 747 | * @snapshot: Image to copy uncompressed data into. | ||
| 748 | * @nr_to_read: Number of pages to load. | ||
| 749 | */ | ||
| 750 | static int load_image_lzo(struct swap_map_handle *handle, | ||
| 751 | struct snapshot_handle *snapshot, | ||
| 752 | unsigned int nr_to_read) | ||
| 753 | { | ||
| 754 | unsigned int m; | ||
| 755 | int error = 0; | ||
| 756 | struct timeval start; | ||
| 757 | struct timeval stop; | ||
| 758 | unsigned nr_pages; | ||
| 759 | size_t off, unc_len, cmp_len; | ||
| 760 | unsigned char *unc, *cmp, *page; | ||
| 761 | |||
| 762 | page = (void *)__get_free_page(__GFP_WAIT | __GFP_HIGH); | ||
| 763 | if (!page) { | ||
| 764 | printk(KERN_ERR "PM: Failed to allocate LZO page\n"); | ||
| 765 | return -ENOMEM; | ||
| 766 | } | ||
| 767 | |||
| 768 | unc = vmalloc(LZO_UNC_SIZE); | ||
| 769 | if (!unc) { | ||
| 770 | printk(KERN_ERR "PM: Failed to allocate LZO uncompressed\n"); | ||
| 771 | free_page((unsigned long)page); | ||
| 772 | return -ENOMEM; | ||
| 773 | } | ||
| 774 | |||
| 775 | cmp = vmalloc(LZO_CMP_SIZE); | ||
| 776 | if (!cmp) { | ||
| 777 | printk(KERN_ERR "PM: Failed to allocate LZO compressed\n"); | ||
| 778 | vfree(unc); | ||
| 779 | free_page((unsigned long)page); | ||
| 780 | return -ENOMEM; | ||
| 781 | } | ||
| 782 | |||
| 783 | printk(KERN_INFO | ||
| 784 | "PM: Loading and decompressing image data (%u pages) ... ", | ||
| 785 | nr_to_read); | ||
| 786 | m = nr_to_read / 100; | ||
| 787 | if (!m) | ||
| 788 | m = 1; | ||
| 789 | nr_pages = 0; | ||
| 790 | do_gettimeofday(&start); | ||
| 791 | |||
| 792 | error = snapshot_write_next(snapshot); | ||
| 793 | if (error <= 0) | ||
| 794 | goto out_finish; | ||
| 795 | |||
| 796 | for (;;) { | ||
| 797 | error = swap_read_page(handle, page, NULL); /* sync */ | ||
| 798 | if (error) | ||
| 799 | break; | ||
| 800 | |||
| 801 | cmp_len = *(size_t *)page; | ||
| 802 | if (unlikely(!cmp_len || | ||
| 803 | cmp_len > lzo1x_worst_compress(LZO_UNC_SIZE))) { | ||
| 804 | printk(KERN_ERR "PM: Invalid LZO compressed length\n"); | ||
| 805 | error = -1; | ||
| 806 | break; | ||
| 807 | } | ||
| 808 | |||
| 809 | memcpy(cmp, page, PAGE_SIZE); | ||
| 810 | for (off = PAGE_SIZE; off < LZO_HEADER + cmp_len; off += PAGE_SIZE) { | ||
| 811 | error = swap_read_page(handle, page, NULL); /* sync */ | ||
| 812 | if (error) | ||
| 813 | goto out_finish; | ||
| 814 | |||
| 815 | memcpy(cmp + off, page, PAGE_SIZE); | ||
| 816 | } | ||
| 817 | |||
| 818 | unc_len = LZO_UNC_SIZE; | ||
| 819 | error = lzo1x_decompress_safe(cmp + LZO_HEADER, cmp_len, | ||
| 820 | unc, &unc_len); | ||
| 821 | if (error < 0) { | ||
| 822 | printk(KERN_ERR "PM: LZO decompression failed\n"); | ||
| 823 | break; | ||
| 824 | } | ||
| 825 | |||
| 826 | if (unlikely(!unc_len || | ||
| 827 | unc_len > LZO_UNC_SIZE || | ||
| 828 | unc_len & (PAGE_SIZE - 1))) { | ||
| 829 | printk(KERN_ERR "PM: Invalid LZO uncompressed length\n"); | ||
| 830 | error = -1; | ||
| 831 | break; | ||
| 832 | } | ||
| 833 | |||
| 834 | for (off = 0; off < unc_len; off += PAGE_SIZE) { | ||
| 835 | memcpy(data_of(*snapshot), unc + off, PAGE_SIZE); | ||
| 836 | |||
| 837 | if (!(nr_pages % m)) | ||
| 838 | printk("\b\b\b\b%3d%%", nr_pages / m); | ||
| 839 | nr_pages++; | ||
| 840 | |||
| 841 | error = snapshot_write_next(snapshot); | ||
| 842 | if (error <= 0) | ||
| 843 | goto out_finish; | ||
| 844 | } | ||
| 845 | } | ||
| 846 | |||
| 847 | out_finish: | ||
| 848 | do_gettimeofday(&stop); | ||
| 849 | if (!error) { | ||
| 850 | printk("\b\b\b\bdone\n"); | ||
| 851 | snapshot_write_finalize(snapshot); | ||
| 852 | if (!snapshot_image_loaded(snapshot)) | ||
| 853 | error = -ENODATA; | ||
| 854 | } else | ||
| 855 | printk("\n"); | ||
| 856 | swsusp_show_speed(&start, &stop, nr_to_read, "Read"); | ||
| 857 | |||
| 858 | vfree(cmp); | ||
| 859 | vfree(unc); | ||
| 860 | free_page((unsigned long)page); | ||
| 861 | |||
| 862 | return error; | ||
| 863 | } | ||
| 864 | |||
| 865 | /** | ||
| 593 | * swsusp_read - read the hibernation image. | 866 | * swsusp_read - read the hibernation image. |
| 594 | * @flags_p: flags passed by the "frozen" kernel in the image header should | 867 | * @flags_p: flags passed by the "frozen" kernel in the image header should |
| 595 | * be written into this memeory location | 868 | * be written into this memeory location |
| @@ -612,8 +885,11 @@ int swsusp_read(unsigned int *flags_p) | |||
| 612 | goto end; | 885 | goto end; |
| 613 | if (!error) | 886 | if (!error) |
| 614 | error = swap_read_page(&handle, header, NULL); | 887 | error = swap_read_page(&handle, header, NULL); |
| 615 | if (!error) | 888 | if (!error) { |
| 616 | error = load_image(&handle, &snapshot, header->pages - 1); | 889 | error = (*flags_p & SF_NOCOMPRESS_MODE) ? |
| 890 | load_image(&handle, &snapshot, header->pages - 1) : | ||
| 891 | load_image_lzo(&handle, &snapshot, header->pages - 1); | ||
| 892 | } | ||
| 617 | swap_reader_finish(&handle); | 893 | swap_reader_finish(&handle); |
| 618 | end: | 894 | end: |
| 619 | if (!error) | 895 | if (!error) |
| @@ -640,7 +916,7 @@ int swsusp_check(void) | |||
| 640 | if (error) | 916 | if (error) |
| 641 | goto put; | 917 | goto put; |
| 642 | 918 | ||
| 643 | if (!memcmp(SWSUSP_SIG, swsusp_header->sig, 10)) { | 919 | if (!memcmp(HIBERNATE_SIG, swsusp_header->sig, 10)) { |
| 644 | memcpy(swsusp_header->sig, swsusp_header->orig_sig, 10); | 920 | memcpy(swsusp_header->sig, swsusp_header->orig_sig, 10); |
| 645 | /* Reset swap signature now */ | 921 | /* Reset swap signature now */ |
| 646 | error = hib_bio_write_page(swsusp_resume_block, | 922 | error = hib_bio_write_page(swsusp_resume_block, |
| @@ -653,13 +929,13 @@ put: | |||
| 653 | if (error) | 929 | if (error) |
| 654 | blkdev_put(hib_resume_bdev, FMODE_READ); | 930 | blkdev_put(hib_resume_bdev, FMODE_READ); |
| 655 | else | 931 | else |
| 656 | pr_debug("PM: Signature found, resuming\n"); | 932 | pr_debug("PM: Image signature found, resuming\n"); |
| 657 | } else { | 933 | } else { |
| 658 | error = PTR_ERR(hib_resume_bdev); | 934 | error = PTR_ERR(hib_resume_bdev); |
| 659 | } | 935 | } |
| 660 | 936 | ||
| 661 | if (error) | 937 | if (error) |
| 662 | pr_debug("PM: Error %d checking image file\n", error); | 938 | pr_debug("PM: Image not found (code %d)\n", error); |
| 663 | 939 | ||
| 664 | return error; | 940 | return error; |
| 665 | } | 941 | } |
