diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2010-10-21 17:53:17 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-10-21 17:53:17 -0400 |
commit | a8cbf22559ceefdcdfac00701e8e6da7518b7e8e (patch) | |
tree | 63ebd5779a37f809f7daed77dbf27aa3f1e1110c /kernel | |
parent | e36f561a2c88394ef2708f1ab300fe8a79e9f651 (diff) | |
parent | 9c034392533f3e9f00656d5c58478cff2560ef81 (diff) |
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/suspend-2.6
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/suspend-2.6: (26 commits)
PM / Wakeup: Show wakeup sources statistics in debugfs
PM: Introduce library for device-specific OPPs (v7)
PM: Add sysfs attr for rechecking dev hash from PM trace
PM: Lock PM device list mutex in show_dev_hash()
PM / Runtime: Remove idle notification after failing suspend
PM / Hibernate: Modify signature used to mark swap
PM / Runtime: Reduce code duplication in core helper functions
PM: Allow wakeup events to abort freezing of tasks
PM: runtime: add missed pm_request_autosuspend
PM / Hibernate: Make some boot messages look less scary
PM / Runtime: Implement autosuspend support
PM / Runtime: Add no_callbacks flag
PM / Runtime: Combine runtime PM entry points
PM / Runtime: Merge synchronous and async runtime routines
PM / Runtime: Replace boolean arguments with bitflags
PM / Runtime: Move code in drivers/base/power/runtime.c
sysfs: Add sysfs_merge_group() and sysfs_unmerge_group()
PM: Fix potential issue with failing asynchronous suspend
PM / Wakeup: Introduce wakeup source objects and event statistics (v3)
PM: Fix signed/unsigned warning in dpm_show_time()
...
Diffstat (limited to 'kernel')
-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 | } |