diff options
author | Rafael J. Wysocki <rjw@sisk.pl> | 2009-03-16 17:34:26 -0400 |
---|---|---|
committer | Rafael J. Wysocki <rjw@sisk.pl> | 2009-03-30 15:46:54 -0400 |
commit | 4aecd6718939eb3c4145b248369b65f7483a8a02 (patch) | |
tree | 20030736ca8e762b5de7ad2e66c39321c9ef94f5 /kernel | |
parent | 900af0d973856d6feb6fc088c2d0d3fde57707d3 (diff) |
PM: Change hibernation code ordering
Change the ordering of the hibernation core code so that the platform
"prepare" callbacks are executed and the nonboot CPUs are disabled
after calling device drivers' "late suspend" methods.
This change (along with the previous analogous change of the suspend
core code) will allow us to rework the PCI PM core so that the power
state of devices is changed in the "late" phase of suspend (and
analogously in the "early" phase of resume), which in turn will allow
us to avoid the race condition where a device using shared interrupts
is put into a low power state with interrupts enabled and then an
interrupt (for another device) comes in and confuses its driver.
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Acked-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/power/disk.c | 109 |
1 files changed, 61 insertions, 48 deletions
diff --git a/kernel/power/disk.c b/kernel/power/disk.c index 320bb0949bdf..e886d1332a10 100644 --- a/kernel/power/disk.c +++ b/kernel/power/disk.c | |||
@@ -228,13 +228,22 @@ static int create_image(int platform_mode) | |||
228 | goto Unlock; | 228 | goto Unlock; |
229 | } | 229 | } |
230 | 230 | ||
231 | error = platform_pre_snapshot(platform_mode); | ||
232 | if (error || hibernation_test(TEST_PLATFORM)) | ||
233 | goto Platform_finish; | ||
234 | |||
235 | error = disable_nonboot_cpus(); | ||
236 | if (error || hibernation_test(TEST_CPUS) | ||
237 | || hibernation_testmode(HIBERNATION_TEST)) | ||
238 | goto Enable_cpus; | ||
239 | |||
231 | local_irq_disable(); | 240 | local_irq_disable(); |
232 | 241 | ||
233 | sysdev_suspend(PMSG_FREEZE); | 242 | sysdev_suspend(PMSG_FREEZE); |
234 | if (error) { | 243 | if (error) { |
235 | printk(KERN_ERR "PM: Some devices failed to power down, " | 244 | printk(KERN_ERR "PM: Some devices failed to power down, " |
236 | "aborting hibernation\n"); | 245 | "aborting hibernation\n"); |
237 | goto Power_up_devices; | 246 | goto Enable_irqs; |
238 | } | 247 | } |
239 | 248 | ||
240 | if (hibernation_test(TEST_CORE)) | 249 | if (hibernation_test(TEST_CORE)) |
@@ -250,15 +259,22 @@ static int create_image(int platform_mode) | |||
250 | restore_processor_state(); | 259 | restore_processor_state(); |
251 | if (!in_suspend) | 260 | if (!in_suspend) |
252 | platform_leave(platform_mode); | 261 | platform_leave(platform_mode); |
262 | |||
253 | Power_up: | 263 | Power_up: |
254 | sysdev_resume(); | 264 | sysdev_resume(); |
255 | /* NOTE: device_power_up() is just a resume() for devices | 265 | /* NOTE: device_power_up() is just a resume() for devices |
256 | * that suspended with irqs off ... no overall powerup. | 266 | * that suspended with irqs off ... no overall powerup. |
257 | */ | 267 | */ |
258 | 268 | ||
259 | Power_up_devices: | 269 | Enable_irqs: |
260 | local_irq_enable(); | 270 | local_irq_enable(); |
261 | 271 | ||
272 | Enable_cpus: | ||
273 | enable_nonboot_cpus(); | ||
274 | |||
275 | Platform_finish: | ||
276 | platform_finish(platform_mode); | ||
277 | |||
262 | device_power_up(in_suspend ? | 278 | device_power_up(in_suspend ? |
263 | (error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE); | 279 | (error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE); |
264 | 280 | ||
@@ -298,25 +314,9 @@ int hibernation_snapshot(int platform_mode) | |||
298 | if (hibernation_test(TEST_DEVICES)) | 314 | if (hibernation_test(TEST_DEVICES)) |
299 | goto Recover_platform; | 315 | goto Recover_platform; |
300 | 316 | ||
301 | error = platform_pre_snapshot(platform_mode); | 317 | error = create_image(platform_mode); |
302 | if (error || hibernation_test(TEST_PLATFORM)) | 318 | /* Control returns here after successful restore */ |
303 | goto Finish; | ||
304 | |||
305 | error = disable_nonboot_cpus(); | ||
306 | if (!error) { | ||
307 | if (hibernation_test(TEST_CPUS)) | ||
308 | goto Enable_cpus; | ||
309 | |||
310 | if (hibernation_testmode(HIBERNATION_TEST)) | ||
311 | goto Enable_cpus; | ||
312 | 319 | ||
313 | error = create_image(platform_mode); | ||
314 | /* Control returns here after successful restore */ | ||
315 | } | ||
316 | Enable_cpus: | ||
317 | enable_nonboot_cpus(); | ||
318 | Finish: | ||
319 | platform_finish(platform_mode); | ||
320 | Resume_devices: | 320 | Resume_devices: |
321 | device_resume(in_suspend ? | 321 | device_resume(in_suspend ? |
322 | (error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE); | 322 | (error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE); |
@@ -338,7 +338,7 @@ int hibernation_snapshot(int platform_mode) | |||
338 | * kernel. | 338 | * kernel. |
339 | */ | 339 | */ |
340 | 340 | ||
341 | static int resume_target_kernel(void) | 341 | static int resume_target_kernel(bool platform_mode) |
342 | { | 342 | { |
343 | int error; | 343 | int error; |
344 | 344 | ||
@@ -351,9 +351,20 @@ static int resume_target_kernel(void) | |||
351 | goto Unlock; | 351 | goto Unlock; |
352 | } | 352 | } |
353 | 353 | ||
354 | error = platform_pre_restore(platform_mode); | ||
355 | if (error) | ||
356 | goto Cleanup; | ||
357 | |||
358 | error = disable_nonboot_cpus(); | ||
359 | if (error) | ||
360 | goto Enable_cpus; | ||
361 | |||
354 | local_irq_disable(); | 362 | local_irq_disable(); |
355 | 363 | ||
356 | sysdev_suspend(PMSG_QUIESCE); | 364 | error = sysdev_suspend(PMSG_QUIESCE); |
365 | if (error) | ||
366 | goto Enable_irqs; | ||
367 | |||
357 | /* We'll ignore saved state, but this gets preempt count (etc) right */ | 368 | /* We'll ignore saved state, but this gets preempt count (etc) right */ |
358 | save_processor_state(); | 369 | save_processor_state(); |
359 | error = restore_highmem(); | 370 | error = restore_highmem(); |
@@ -379,8 +390,15 @@ static int resume_target_kernel(void) | |||
379 | 390 | ||
380 | sysdev_resume(); | 391 | sysdev_resume(); |
381 | 392 | ||
393 | Enable_irqs: | ||
382 | local_irq_enable(); | 394 | local_irq_enable(); |
383 | 395 | ||
396 | Enable_cpus: | ||
397 | enable_nonboot_cpus(); | ||
398 | |||
399 | Cleanup: | ||
400 | platform_restore_cleanup(platform_mode); | ||
401 | |||
384 | device_power_up(PMSG_RECOVER); | 402 | device_power_up(PMSG_RECOVER); |
385 | 403 | ||
386 | Unlock: | 404 | Unlock: |
@@ -405,19 +423,10 @@ int hibernation_restore(int platform_mode) | |||
405 | pm_prepare_console(); | 423 | pm_prepare_console(); |
406 | suspend_console(); | 424 | suspend_console(); |
407 | error = device_suspend(PMSG_QUIESCE); | 425 | error = device_suspend(PMSG_QUIESCE); |
408 | if (error) | ||
409 | goto Finish; | ||
410 | |||
411 | error = platform_pre_restore(platform_mode); | ||
412 | if (!error) { | 426 | if (!error) { |
413 | error = disable_nonboot_cpus(); | 427 | error = resume_target_kernel(platform_mode); |
414 | if (!error) | 428 | device_resume(PMSG_RECOVER); |
415 | error = resume_target_kernel(); | ||
416 | enable_nonboot_cpus(); | ||
417 | } | 429 | } |
418 | platform_restore_cleanup(platform_mode); | ||
419 | device_resume(PMSG_RECOVER); | ||
420 | Finish: | ||
421 | resume_console(); | 430 | resume_console(); |
422 | pm_restore_console(); | 431 | pm_restore_console(); |
423 | return error; | 432 | return error; |
@@ -453,34 +462,38 @@ int hibernation_platform_enter(void) | |||
453 | goto Resume_devices; | 462 | goto Resume_devices; |
454 | } | 463 | } |
455 | 464 | ||
465 | device_pm_lock(); | ||
466 | |||
467 | error = device_power_down(PMSG_HIBERNATE); | ||
468 | if (error) | ||
469 | goto Unlock; | ||
470 | |||
456 | error = hibernation_ops->prepare(); | 471 | error = hibernation_ops->prepare(); |
457 | if (error) | 472 | if (error) |
458 | goto Resume_devices; | 473 | goto Platofrm_finish; |
459 | 474 | ||
460 | error = disable_nonboot_cpus(); | 475 | error = disable_nonboot_cpus(); |
461 | if (error) | 476 | if (error) |
462 | goto Finish; | 477 | goto Platofrm_finish; |
463 | |||
464 | device_pm_lock(); | ||
465 | |||
466 | error = device_power_down(PMSG_HIBERNATE); | ||
467 | if (!error) { | ||
468 | local_irq_disable(); | ||
469 | sysdev_suspend(PMSG_HIBERNATE); | ||
470 | hibernation_ops->enter(); | ||
471 | /* We should never get here */ | ||
472 | while (1); | ||
473 | } | ||
474 | 478 | ||
475 | device_pm_unlock(); | 479 | local_irq_disable(); |
480 | sysdev_suspend(PMSG_HIBERNATE); | ||
481 | hibernation_ops->enter(); | ||
482 | /* We should never get here */ | ||
483 | while (1); | ||
476 | 484 | ||
477 | /* | 485 | /* |
478 | * We don't need to reenable the nonboot CPUs or resume consoles, since | 486 | * We don't need to reenable the nonboot CPUs or resume consoles, since |
479 | * the system is going to be halted anyway. | 487 | * the system is going to be halted anyway. |
480 | */ | 488 | */ |
481 | Finish: | 489 | Platofrm_finish: |
482 | hibernation_ops->finish(); | 490 | hibernation_ops->finish(); |
483 | 491 | ||
492 | device_power_up(PMSG_RESTORE); | ||
493 | |||
494 | Unlock: | ||
495 | device_pm_unlock(); | ||
496 | |||
484 | Resume_devices: | 497 | Resume_devices: |
485 | entering_platform_hibernation = false; | 498 | entering_platform_hibernation = false; |
486 | device_resume(PMSG_RESTORE); | 499 | device_resume(PMSG_RESTORE); |