aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorRafael J. Wysocki <rjw@sisk.pl>2009-03-16 17:34:06 -0400
committerRafael J. Wysocki <rjw@sisk.pl>2009-03-30 15:46:54 -0400
commit2ed8d2b3a81bdbb0418301628ccdb008ac9f40b7 (patch)
tree54ab0cd7aa7db73151533b463bd490b62a29c462 /kernel
parent0a0c5168df270a50e3518e4f12bddb31f8f5f38f (diff)
PM: Rework handling of interrupts during suspend-resume
Use the functions introduced in by the previous patch, suspend_device_irqs(), resume_device_irqs() and check_wakeup_irqs(), to rework the handling of interrupts during suspend (hibernation) and resume. Namely, interrupts will only be disabled on the CPU right before suspending sysdevs, while device drivers will be prevented from receiving interrupts, with the help of the new helper function, before their "late" suspend callbacks run (and analogously during resume). In addition, since the device interrups are now disabled before the CPU has turned all interrupts off and the CPU will ACK the interrupts setting the IRQ_PENDING bit for them, check in sysdev_suspend() if any wake-up interrupts are pending and abort suspend if that's the case. Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl> Acked-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'kernel')
-rw-r--r--kernel/kexec.c8
-rw-r--r--kernel/power/disk.c39
-rw-r--r--kernel/power/main.c17
3 files changed, 44 insertions, 20 deletions
diff --git a/kernel/kexec.c b/kernel/kexec.c
index c7fd6692939d..dade9af6bf21 100644
--- a/kernel/kexec.c
+++ b/kernel/kexec.c
@@ -1454,7 +1454,6 @@ int kernel_kexec(void)
1454 if (error) 1454 if (error)
1455 goto Resume_devices; 1455 goto Resume_devices;
1456 device_pm_lock(); 1456 device_pm_lock();
1457 local_irq_disable();
1458 /* At this point, device_suspend() has been called, 1457 /* At this point, device_suspend() has been called,
1459 * but *not* device_power_down(). We *must* 1458 * but *not* device_power_down(). We *must*
1460 * device_power_down() now. Otherwise, drivers for 1459 * device_power_down() now. Otherwise, drivers for
@@ -1464,8 +1463,9 @@ int kernel_kexec(void)
1464 */ 1463 */
1465 error = device_power_down(PMSG_FREEZE); 1464 error = device_power_down(PMSG_FREEZE);
1466 if (error) 1465 if (error)
1467 goto Enable_irqs; 1466 goto Unlock_pm;
1468 1467
1468 local_irq_disable();
1469 /* Suspend system devices */ 1469 /* Suspend system devices */
1470 error = sysdev_suspend(PMSG_FREEZE); 1470 error = sysdev_suspend(PMSG_FREEZE);
1471 if (error) 1471 if (error)
@@ -1484,9 +1484,9 @@ int kernel_kexec(void)
1484 if (kexec_image->preserve_context) { 1484 if (kexec_image->preserve_context) {
1485 sysdev_resume(); 1485 sysdev_resume();
1486 Power_up_devices: 1486 Power_up_devices:
1487 device_power_up(PMSG_RESTORE);
1488 Enable_irqs:
1489 local_irq_enable(); 1487 local_irq_enable();
1488 device_power_up(PMSG_RESTORE);
1489 Unlock_pm:
1490 device_pm_unlock(); 1490 device_pm_unlock();
1491 enable_nonboot_cpus(); 1491 enable_nonboot_cpus();
1492 Resume_devices: 1492 Resume_devices:
diff --git a/kernel/power/disk.c b/kernel/power/disk.c
index 4a4a206b1979..320bb0949bdf 100644
--- a/kernel/power/disk.c
+++ b/kernel/power/disk.c
@@ -214,7 +214,7 @@ static int create_image(int platform_mode)
214 return error; 214 return error;
215 215
216 device_pm_lock(); 216 device_pm_lock();
217 local_irq_disable(); 217
218 /* At this point, device_suspend() has been called, but *not* 218 /* At this point, device_suspend() has been called, but *not*
219 * device_power_down(). We *must* call device_power_down() now. 219 * device_power_down(). We *must* call device_power_down() now.
220 * Otherwise, drivers for some devices (e.g. interrupt controllers) 220 * Otherwise, drivers for some devices (e.g. interrupt controllers)
@@ -225,8 +225,11 @@ static int create_image(int platform_mode)
225 if (error) { 225 if (error) {
226 printk(KERN_ERR "PM: Some devices failed to power down, " 226 printk(KERN_ERR "PM: Some devices failed to power down, "
227 "aborting hibernation\n"); 227 "aborting hibernation\n");
228 goto Enable_irqs; 228 goto Unlock;
229 } 229 }
230
231 local_irq_disable();
232
230 sysdev_suspend(PMSG_FREEZE); 233 sysdev_suspend(PMSG_FREEZE);
231 if (error) { 234 if (error) {
232 printk(KERN_ERR "PM: Some devices failed to power down, " 235 printk(KERN_ERR "PM: Some devices failed to power down, "
@@ -252,12 +255,16 @@ static int create_image(int platform_mode)
252 /* NOTE: device_power_up() is just a resume() for devices 255 /* NOTE: device_power_up() is just a resume() for devices
253 * that suspended with irqs off ... no overall powerup. 256 * that suspended with irqs off ... no overall powerup.
254 */ 257 */
258
255 Power_up_devices: 259 Power_up_devices:
260 local_irq_enable();
261
256 device_power_up(in_suspend ? 262 device_power_up(in_suspend ?
257 (error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE); 263 (error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE);
258 Enable_irqs: 264
259 local_irq_enable(); 265 Unlock:
260 device_pm_unlock(); 266 device_pm_unlock();
267
261 return error; 268 return error;
262} 269}
263 270
@@ -336,13 +343,16 @@ static int resume_target_kernel(void)
336 int error; 343 int error;
337 344
338 device_pm_lock(); 345 device_pm_lock();
339 local_irq_disable(); 346
340 error = device_power_down(PMSG_QUIESCE); 347 error = device_power_down(PMSG_QUIESCE);
341 if (error) { 348 if (error) {
342 printk(KERN_ERR "PM: Some devices failed to power down, " 349 printk(KERN_ERR "PM: Some devices failed to power down, "
343 "aborting resume\n"); 350 "aborting resume\n");
344 goto Enable_irqs; 351 goto Unlock;
345 } 352 }
353
354 local_irq_disable();
355
346 sysdev_suspend(PMSG_QUIESCE); 356 sysdev_suspend(PMSG_QUIESCE);
347 /* We'll ignore saved state, but this gets preempt count (etc) right */ 357 /* We'll ignore saved state, but this gets preempt count (etc) right */
348 save_processor_state(); 358 save_processor_state();
@@ -366,11 +376,16 @@ static int resume_target_kernel(void)
366 swsusp_free(); 376 swsusp_free();
367 restore_processor_state(); 377 restore_processor_state();
368 touch_softlockup_watchdog(); 378 touch_softlockup_watchdog();
379
369 sysdev_resume(); 380 sysdev_resume();
370 device_power_up(PMSG_RECOVER); 381
371 Enable_irqs:
372 local_irq_enable(); 382 local_irq_enable();
383
384 device_power_up(PMSG_RECOVER);
385
386 Unlock:
373 device_pm_unlock(); 387 device_pm_unlock();
388
374 return error; 389 return error;
375} 390}
376 391
@@ -447,15 +462,16 @@ int hibernation_platform_enter(void)
447 goto Finish; 462 goto Finish;
448 463
449 device_pm_lock(); 464 device_pm_lock();
450 local_irq_disable(); 465
451 error = device_power_down(PMSG_HIBERNATE); 466 error = device_power_down(PMSG_HIBERNATE);
452 if (!error) { 467 if (!error) {
468 local_irq_disable();
453 sysdev_suspend(PMSG_HIBERNATE); 469 sysdev_suspend(PMSG_HIBERNATE);
454 hibernation_ops->enter(); 470 hibernation_ops->enter();
455 /* We should never get here */ 471 /* We should never get here */
456 while (1); 472 while (1);
457 } 473 }
458 local_irq_enable(); 474
459 device_pm_unlock(); 475 device_pm_unlock();
460 476
461 /* 477 /*
@@ -464,12 +480,15 @@ int hibernation_platform_enter(void)
464 */ 480 */
465 Finish: 481 Finish:
466 hibernation_ops->finish(); 482 hibernation_ops->finish();
483
467 Resume_devices: 484 Resume_devices:
468 entering_platform_hibernation = false; 485 entering_platform_hibernation = false;
469 device_resume(PMSG_RESTORE); 486 device_resume(PMSG_RESTORE);
470 resume_console(); 487 resume_console();
488
471 Close: 489 Close:
472 hibernation_ops->end(); 490 hibernation_ops->end();
491
473 return error; 492 return error;
474} 493}
475 494
diff --git a/kernel/power/main.c b/kernel/power/main.c
index c9632f841f64..f0a466736c01 100644
--- a/kernel/power/main.c
+++ b/kernel/power/main.c
@@ -287,17 +287,19 @@ void __attribute__ ((weak)) arch_suspend_enable_irqs(void)
287 */ 287 */
288static int suspend_enter(suspend_state_t state) 288static int suspend_enter(suspend_state_t state)
289{ 289{
290 int error = 0; 290 int error;
291 291
292 device_pm_lock(); 292 device_pm_lock();
293 arch_suspend_disable_irqs();
294 BUG_ON(!irqs_disabled());
295 293
296 if ((error = device_power_down(PMSG_SUSPEND))) { 294 error = device_power_down(PMSG_SUSPEND);
295 if (error) {
297 printk(KERN_ERR "PM: Some devices failed to power down\n"); 296 printk(KERN_ERR "PM: Some devices failed to power down\n");
298 goto Done; 297 goto Done;
299 } 298 }
300 299
300 arch_suspend_disable_irqs();
301 BUG_ON(!irqs_disabled());
302
301 error = sysdev_suspend(PMSG_SUSPEND); 303 error = sysdev_suspend(PMSG_SUSPEND);
302 if (!error) { 304 if (!error) {
303 if (!suspend_test(TEST_CORE)) 305 if (!suspend_test(TEST_CORE))
@@ -305,11 +307,14 @@ static int suspend_enter(suspend_state_t state)
305 sysdev_resume(); 307 sysdev_resume();
306 } 308 }
307 309
308 device_power_up(PMSG_RESUME);
309 Done:
310 arch_suspend_enable_irqs(); 310 arch_suspend_enable_irqs();
311 BUG_ON(irqs_disabled()); 311 BUG_ON(irqs_disabled());
312
313 device_power_up(PMSG_RESUME);
314
315 Done:
312 device_pm_unlock(); 316 device_pm_unlock();
317
313 return error; 318 return error;
314} 319}
315 320