diff options
Diffstat (limited to 'kernel/power/swsusp.c')
-rw-r--r-- | kernel/power/swsusp.c | 98 |
1 files changed, 70 insertions, 28 deletions
diff --git a/kernel/power/swsusp.c b/kernel/power/swsusp.c index 0b66659dc516..31aa0390c777 100644 --- a/kernel/power/swsusp.c +++ b/kernel/power/swsusp.c | |||
@@ -49,6 +49,7 @@ | |||
49 | #include <linux/bootmem.h> | 49 | #include <linux/bootmem.h> |
50 | #include <linux/syscalls.h> | 50 | #include <linux/syscalls.h> |
51 | #include <linux/highmem.h> | 51 | #include <linux/highmem.h> |
52 | #include <linux/time.h> | ||
52 | 53 | ||
53 | #include "power.h" | 54 | #include "power.h" |
54 | 55 | ||
@@ -64,10 +65,8 @@ int in_suspend __nosavedata = 0; | |||
64 | 65 | ||
65 | #ifdef CONFIG_HIGHMEM | 66 | #ifdef CONFIG_HIGHMEM |
66 | unsigned int count_highmem_pages(void); | 67 | unsigned int count_highmem_pages(void); |
67 | int save_highmem(void); | ||
68 | int restore_highmem(void); | 68 | int restore_highmem(void); |
69 | #else | 69 | #else |
70 | static inline int save_highmem(void) { return 0; } | ||
71 | static inline int restore_highmem(void) { return 0; } | 70 | static inline int restore_highmem(void) { return 0; } |
72 | static inline unsigned int count_highmem_pages(void) { return 0; } | 71 | static inline unsigned int count_highmem_pages(void) { return 0; } |
73 | #endif | 72 | #endif |
@@ -134,18 +133,18 @@ static int bitmap_set(struct bitmap_page *bitmap, unsigned long bit) | |||
134 | return 0; | 133 | return 0; |
135 | } | 134 | } |
136 | 135 | ||
137 | unsigned long alloc_swap_page(int swap, struct bitmap_page *bitmap) | 136 | sector_t alloc_swapdev_block(int swap, struct bitmap_page *bitmap) |
138 | { | 137 | { |
139 | unsigned long offset; | 138 | unsigned long offset; |
140 | 139 | ||
141 | offset = swp_offset(get_swap_page_of_type(swap)); | 140 | offset = swp_offset(get_swap_page_of_type(swap)); |
142 | if (offset) { | 141 | if (offset) { |
143 | if (bitmap_set(bitmap, offset)) { | 142 | if (bitmap_set(bitmap, offset)) |
144 | swap_free(swp_entry(swap, offset)); | 143 | swap_free(swp_entry(swap, offset)); |
145 | offset = 0; | 144 | else |
146 | } | 145 | return swapdev_block(swap, offset); |
147 | } | 146 | } |
148 | return offset; | 147 | return 0; |
149 | } | 148 | } |
150 | 149 | ||
151 | void free_all_swap_pages(int swap, struct bitmap_page *bitmap) | 150 | void free_all_swap_pages(int swap, struct bitmap_page *bitmap) |
@@ -166,6 +165,34 @@ void free_all_swap_pages(int swap, struct bitmap_page *bitmap) | |||
166 | } | 165 | } |
167 | 166 | ||
168 | /** | 167 | /** |
168 | * swsusp_show_speed - print the time elapsed between two events represented by | ||
169 | * @start and @stop | ||
170 | * | ||
171 | * @nr_pages - number of pages processed between @start and @stop | ||
172 | * @msg - introductory message to print | ||
173 | */ | ||
174 | |||
175 | void swsusp_show_speed(struct timeval *start, struct timeval *stop, | ||
176 | unsigned nr_pages, char *msg) | ||
177 | { | ||
178 | s64 elapsed_centisecs64; | ||
179 | int centisecs; | ||
180 | int k; | ||
181 | int kps; | ||
182 | |||
183 | elapsed_centisecs64 = timeval_to_ns(stop) - timeval_to_ns(start); | ||
184 | do_div(elapsed_centisecs64, NSEC_PER_SEC / 100); | ||
185 | centisecs = elapsed_centisecs64; | ||
186 | if (centisecs == 0) | ||
187 | centisecs = 1; /* avoid div-by-zero */ | ||
188 | k = nr_pages * (PAGE_SIZE / 1024); | ||
189 | kps = (k * 100) / centisecs; | ||
190 | printk("%s %d kbytes in %d.%02d seconds (%d.%02d MB/s)\n", msg, k, | ||
191 | centisecs / 100, centisecs % 100, | ||
192 | kps / 1000, (kps % 1000) / 10); | ||
193 | } | ||
194 | |||
195 | /** | ||
169 | * swsusp_shrink_memory - Try to free as much memory as needed | 196 | * swsusp_shrink_memory - Try to free as much memory as needed |
170 | * | 197 | * |
171 | * ... but do not OOM-kill anyone | 198 | * ... but do not OOM-kill anyone |
@@ -184,23 +211,37 @@ static inline unsigned long __shrink_memory(long tmp) | |||
184 | 211 | ||
185 | int swsusp_shrink_memory(void) | 212 | int swsusp_shrink_memory(void) |
186 | { | 213 | { |
187 | long size, tmp; | 214 | long tmp; |
188 | struct zone *zone; | 215 | struct zone *zone; |
189 | unsigned long pages = 0; | 216 | unsigned long pages = 0; |
190 | unsigned int i = 0; | 217 | unsigned int i = 0; |
191 | char *p = "-\\|/"; | 218 | char *p = "-\\|/"; |
219 | struct timeval start, stop; | ||
192 | 220 | ||
193 | printk("Shrinking memory... "); | 221 | printk("Shrinking memory... "); |
222 | do_gettimeofday(&start); | ||
194 | do { | 223 | do { |
195 | size = 2 * count_highmem_pages(); | 224 | long size, highmem_size; |
196 | size += size / 50 + count_data_pages() + PAGES_FOR_IO; | 225 | |
226 | highmem_size = count_highmem_pages(); | ||
227 | size = count_data_pages() + PAGES_FOR_IO; | ||
197 | tmp = size; | 228 | tmp = size; |
229 | size += highmem_size; | ||
198 | for_each_zone (zone) | 230 | for_each_zone (zone) |
199 | if (!is_highmem(zone) && populated_zone(zone)) { | 231 | if (populated_zone(zone)) { |
200 | tmp -= zone->free_pages; | 232 | if (is_highmem(zone)) { |
201 | tmp += zone->lowmem_reserve[ZONE_NORMAL]; | 233 | highmem_size -= zone->free_pages; |
202 | tmp += snapshot_additional_pages(zone); | 234 | } else { |
235 | tmp -= zone->free_pages; | ||
236 | tmp += zone->lowmem_reserve[ZONE_NORMAL]; | ||
237 | tmp += snapshot_additional_pages(zone); | ||
238 | } | ||
203 | } | 239 | } |
240 | |||
241 | if (highmem_size < 0) | ||
242 | highmem_size = 0; | ||
243 | |||
244 | tmp += highmem_size; | ||
204 | if (tmp > 0) { | 245 | if (tmp > 0) { |
205 | tmp = __shrink_memory(tmp); | 246 | tmp = __shrink_memory(tmp); |
206 | if (!tmp) | 247 | if (!tmp) |
@@ -212,7 +253,9 @@ int swsusp_shrink_memory(void) | |||
212 | } | 253 | } |
213 | printk("\b%c", p[i++%4]); | 254 | printk("\b%c", p[i++%4]); |
214 | } while (tmp > 0); | 255 | } while (tmp > 0); |
256 | do_gettimeofday(&stop); | ||
215 | printk("\bdone (%lu pages freed)\n", pages); | 257 | printk("\bdone (%lu pages freed)\n", pages); |
258 | swsusp_show_speed(&start, &stop, pages, "Freed"); | ||
216 | 259 | ||
217 | return 0; | 260 | return 0; |
218 | } | 261 | } |
@@ -223,6 +266,7 @@ int swsusp_suspend(void) | |||
223 | 266 | ||
224 | if ((error = arch_prepare_suspend())) | 267 | if ((error = arch_prepare_suspend())) |
225 | return error; | 268 | return error; |
269 | |||
226 | local_irq_disable(); | 270 | local_irq_disable(); |
227 | /* At this point, device_suspend() has been called, but *not* | 271 | /* At this point, device_suspend() has been called, but *not* |
228 | * device_power_down(). We *must* device_power_down() now. | 272 | * device_power_down(). We *must* device_power_down() now. |
@@ -235,23 +279,16 @@ int swsusp_suspend(void) | |||
235 | goto Enable_irqs; | 279 | goto Enable_irqs; |
236 | } | 280 | } |
237 | 281 | ||
238 | if ((error = save_highmem())) { | ||
239 | printk(KERN_ERR "swsusp: Not enough free pages for highmem\n"); | ||
240 | goto Restore_highmem; | ||
241 | } | ||
242 | |||
243 | save_processor_state(); | 282 | save_processor_state(); |
244 | if ((error = swsusp_arch_suspend())) | 283 | if ((error = swsusp_arch_suspend())) |
245 | printk(KERN_ERR "Error %d suspending\n", error); | 284 | printk(KERN_ERR "Error %d suspending\n", error); |
246 | /* Restore control flow magically appears here */ | 285 | /* Restore control flow magically appears here */ |
247 | restore_processor_state(); | 286 | restore_processor_state(); |
248 | Restore_highmem: | ||
249 | restore_highmem(); | ||
250 | /* NOTE: device_power_up() is just a resume() for devices | 287 | /* NOTE: device_power_up() is just a resume() for devices |
251 | * that suspended with irqs off ... no overall powerup. | 288 | * that suspended with irqs off ... no overall powerup. |
252 | */ | 289 | */ |
253 | device_power_up(); | 290 | device_power_up(); |
254 | Enable_irqs: | 291 | Enable_irqs: |
255 | local_irq_enable(); | 292 | local_irq_enable(); |
256 | return error; | 293 | return error; |
257 | } | 294 | } |
@@ -268,18 +305,23 @@ int swsusp_resume(void) | |||
268 | printk(KERN_ERR "Some devices failed to power down, very bad\n"); | 305 | printk(KERN_ERR "Some devices failed to power down, very bad\n"); |
269 | /* We'll ignore saved state, but this gets preempt count (etc) right */ | 306 | /* We'll ignore saved state, but this gets preempt count (etc) right */ |
270 | save_processor_state(); | 307 | save_processor_state(); |
271 | error = swsusp_arch_resume(); | 308 | error = restore_highmem(); |
272 | /* Code below is only ever reached in case of failure. Otherwise | 309 | if (!error) { |
273 | * execution continues at place where swsusp_arch_suspend was called | 310 | error = swsusp_arch_resume(); |
274 | */ | 311 | /* The code below is only ever reached in case of a failure. |
275 | BUG_ON(!error); | 312 | * Otherwise execution continues at place where |
313 | * swsusp_arch_suspend() was called | ||
314 | */ | ||
315 | BUG_ON(!error); | ||
316 | /* This call to restore_highmem() undos the previous one */ | ||
317 | restore_highmem(); | ||
318 | } | ||
276 | /* The only reason why swsusp_arch_resume() can fail is memory being | 319 | /* The only reason why swsusp_arch_resume() can fail is memory being |
277 | * very tight, so we have to free it as soon as we can to avoid | 320 | * very tight, so we have to free it as soon as we can to avoid |
278 | * subsequent failures | 321 | * subsequent failures |
279 | */ | 322 | */ |
280 | swsusp_free(); | 323 | swsusp_free(); |
281 | restore_processor_state(); | 324 | restore_processor_state(); |
282 | restore_highmem(); | ||
283 | touch_softlockup_watchdog(); | 325 | touch_softlockup_watchdog(); |
284 | device_power_up(); | 326 | device_power_up(); |
285 | local_irq_enable(); | 327 | local_irq_enable(); |