aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/power/disk.c
diff options
context:
space:
mode:
authorRafael J. Wysocki <rjw@sisk.pl>2009-03-16 17:34:26 -0400
committerRafael J. Wysocki <rjw@sisk.pl>2009-03-30 15:46:54 -0400
commit4aecd6718939eb3c4145b248369b65f7483a8a02 (patch)
tree20030736ca8e762b5de7ad2e66c39321c9ef94f5 /kernel/power/disk.c
parent900af0d973856d6feb6fc088c2d0d3fde57707d3 (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/power/disk.c')
-rw-r--r--kernel/power/disk.c109
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
341static int resume_target_kernel(void) 341static 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);