diff options
Diffstat (limited to 'kernel/power/swsusp.c')
-rw-r--r-- | kernel/power/swsusp.c | 53 |
1 files changed, 31 insertions, 22 deletions
diff --git a/kernel/power/swsusp.c b/kernel/power/swsusp.c index 4147a756a8c7..68de5c1dbd78 100644 --- a/kernel/power/swsusp.c +++ b/kernel/power/swsusp.c | |||
@@ -64,10 +64,8 @@ int in_suspend __nosavedata = 0; | |||
64 | 64 | ||
65 | #ifdef CONFIG_HIGHMEM | 65 | #ifdef CONFIG_HIGHMEM |
66 | unsigned int count_highmem_pages(void); | 66 | unsigned int count_highmem_pages(void); |
67 | int save_highmem(void); | ||
68 | int restore_highmem(void); | 67 | int restore_highmem(void); |
69 | #else | 68 | #else |
70 | static inline int save_highmem(void) { return 0; } | ||
71 | static inline int restore_highmem(void) { return 0; } | 69 | static inline int restore_highmem(void) { return 0; } |
72 | static inline unsigned int count_highmem_pages(void) { return 0; } | 70 | static inline unsigned int count_highmem_pages(void) { return 0; } |
73 | #endif | 71 | #endif |
@@ -184,7 +182,7 @@ static inline unsigned long __shrink_memory(long tmp) | |||
184 | 182 | ||
185 | int swsusp_shrink_memory(void) | 183 | int swsusp_shrink_memory(void) |
186 | { | 184 | { |
187 | long size, tmp; | 185 | long tmp; |
188 | struct zone *zone; | 186 | struct zone *zone; |
189 | unsigned long pages = 0; | 187 | unsigned long pages = 0; |
190 | unsigned int i = 0; | 188 | unsigned int i = 0; |
@@ -192,15 +190,27 @@ int swsusp_shrink_memory(void) | |||
192 | 190 | ||
193 | printk("Shrinking memory... "); | 191 | printk("Shrinking memory... "); |
194 | do { | 192 | do { |
195 | size = 2 * count_highmem_pages(); | 193 | long size, highmem_size; |
196 | size += size / 50 + count_data_pages() + PAGES_FOR_IO; | 194 | |
195 | highmem_size = count_highmem_pages(); | ||
196 | size = count_data_pages() + PAGES_FOR_IO; | ||
197 | tmp = size; | 197 | tmp = size; |
198 | size += highmem_size; | ||
198 | for_each_zone (zone) | 199 | for_each_zone (zone) |
199 | if (!is_highmem(zone) && populated_zone(zone)) { | 200 | if (populated_zone(zone)) { |
200 | tmp -= zone->free_pages; | 201 | if (is_highmem(zone)) { |
201 | tmp += zone->lowmem_reserve[ZONE_NORMAL]; | 202 | highmem_size -= zone->free_pages; |
202 | tmp += snapshot_additional_pages(zone); | 203 | } else { |
204 | tmp -= zone->free_pages; | ||
205 | tmp += zone->lowmem_reserve[ZONE_NORMAL]; | ||
206 | tmp += snapshot_additional_pages(zone); | ||
207 | } | ||
203 | } | 208 | } |
209 | |||
210 | if (highmem_size < 0) | ||
211 | highmem_size = 0; | ||
212 | |||
213 | tmp += highmem_size; | ||
204 | if (tmp > 0) { | 214 | if (tmp > 0) { |
205 | tmp = __shrink_memory(tmp); | 215 | tmp = __shrink_memory(tmp); |
206 | if (!tmp) | 216 | if (!tmp) |
@@ -223,6 +233,7 @@ int swsusp_suspend(void) | |||
223 | 233 | ||
224 | if ((error = arch_prepare_suspend())) | 234 | if ((error = arch_prepare_suspend())) |
225 | return error; | 235 | return error; |
236 | |||
226 | local_irq_disable(); | 237 | local_irq_disable(); |
227 | /* At this point, device_suspend() has been called, but *not* | 238 | /* At this point, device_suspend() has been called, but *not* |
228 | * device_power_down(). We *must* device_power_down() now. | 239 | * device_power_down(). We *must* device_power_down() now. |
@@ -235,18 +246,11 @@ int swsusp_suspend(void) | |||
235 | goto Enable_irqs; | 246 | goto Enable_irqs; |
236 | } | 247 | } |
237 | 248 | ||
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(); | 249 | save_processor_state(); |
244 | if ((error = swsusp_arch_suspend())) | 250 | if ((error = swsusp_arch_suspend())) |
245 | printk(KERN_ERR "Error %d suspending\n", error); | 251 | printk(KERN_ERR "Error %d suspending\n", error); |
246 | /* Restore control flow magically appears here */ | 252 | /* Restore control flow magically appears here */ |
247 | restore_processor_state(); | 253 | restore_processor_state(); |
248 | Restore_highmem: | ||
249 | restore_highmem(); | ||
250 | /* NOTE: device_power_up() is just a resume() for devices | 254 | /* NOTE: device_power_up() is just a resume() for devices |
251 | * that suspended with irqs off ... no overall powerup. | 255 | * that suspended with irqs off ... no overall powerup. |
252 | */ | 256 | */ |
@@ -268,18 +272,23 @@ int swsusp_resume(void) | |||
268 | printk(KERN_ERR "Some devices failed to power down, very bad\n"); | 272 | 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 */ | 273 | /* We'll ignore saved state, but this gets preempt count (etc) right */ |
270 | save_processor_state(); | 274 | save_processor_state(); |
271 | error = swsusp_arch_resume(); | 275 | error = restore_highmem(); |
272 | /* Code below is only ever reached in case of failure. Otherwise | 276 | if (!error) { |
273 | * execution continues at place where swsusp_arch_suspend was called | 277 | error = swsusp_arch_resume(); |
274 | */ | 278 | /* The code below is only ever reached in case of a failure. |
275 | BUG_ON(!error); | 279 | * Otherwise execution continues at place where |
280 | * swsusp_arch_suspend() was called | ||
281 | */ | ||
282 | BUG_ON(!error); | ||
283 | /* This call to restore_highmem() undos the previous one */ | ||
284 | restore_highmem(); | ||
285 | } | ||
276 | /* The only reason why swsusp_arch_resume() can fail is memory being | 286 | /* 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 | 287 | * very tight, so we have to free it as soon as we can to avoid |
278 | * subsequent failures | 288 | * subsequent failures |
279 | */ | 289 | */ |
280 | swsusp_free(); | 290 | swsusp_free(); |
281 | restore_processor_state(); | 291 | restore_processor_state(); |
282 | restore_highmem(); | ||
283 | touch_softlockup_watchdog(); | 292 | touch_softlockup_watchdog(); |
284 | device_power_up(); | 293 | device_power_up(); |
285 | local_irq_enable(); | 294 | local_irq_enable(); |