aboutsummaryrefslogtreecommitdiffstats
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
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>
-rw-r--r--arch/x86/kernel/apm_32.c15
-rw-r--r--drivers/base/power/main.c20
-rw-r--r--drivers/base/sys.c8
-rw-r--r--drivers/xen/manage.c16
-rw-r--r--kernel/kexec.c8
-rw-r--r--kernel/power/disk.c39
-rw-r--r--kernel/power/main.c17
7 files changed, 83 insertions, 40 deletions
diff --git a/arch/x86/kernel/apm_32.c b/arch/x86/kernel/apm_32.c
index 10033fe718e0..ac7783a67432 100644
--- a/arch/x86/kernel/apm_32.c
+++ b/arch/x86/kernel/apm_32.c
@@ -1190,8 +1190,10 @@ static int suspend(int vetoable)
1190 struct apm_user *as; 1190 struct apm_user *as;
1191 1191
1192 device_suspend(PMSG_SUSPEND); 1192 device_suspend(PMSG_SUSPEND);
1193 local_irq_disable(); 1193
1194 device_power_down(PMSG_SUSPEND); 1194 device_power_down(PMSG_SUSPEND);
1195
1196 local_irq_disable();
1195 sysdev_suspend(PMSG_SUSPEND); 1197 sysdev_suspend(PMSG_SUSPEND);
1196 1198
1197 local_irq_enable(); 1199 local_irq_enable();
@@ -1209,9 +1211,12 @@ static int suspend(int vetoable)
1209 if (err != APM_SUCCESS) 1211 if (err != APM_SUCCESS)
1210 apm_error("suspend", err); 1212 apm_error("suspend", err);
1211 err = (err == APM_SUCCESS) ? 0 : -EIO; 1213 err = (err == APM_SUCCESS) ? 0 : -EIO;
1214
1212 sysdev_resume(); 1215 sysdev_resume();
1213 device_power_up(PMSG_RESUME);
1214 local_irq_enable(); 1216 local_irq_enable();
1217
1218 device_power_up(PMSG_RESUME);
1219
1215 device_resume(PMSG_RESUME); 1220 device_resume(PMSG_RESUME);
1216 queue_event(APM_NORMAL_RESUME, NULL); 1221 queue_event(APM_NORMAL_RESUME, NULL);
1217 spin_lock(&user_list_lock); 1222 spin_lock(&user_list_lock);
@@ -1228,8 +1233,9 @@ static void standby(void)
1228{ 1233{
1229 int err; 1234 int err;
1230 1235
1231 local_irq_disable();
1232 device_power_down(PMSG_SUSPEND); 1236 device_power_down(PMSG_SUSPEND);
1237
1238 local_irq_disable();
1233 sysdev_suspend(PMSG_SUSPEND); 1239 sysdev_suspend(PMSG_SUSPEND);
1234 local_irq_enable(); 1240 local_irq_enable();
1235 1241
@@ -1239,8 +1245,9 @@ static void standby(void)
1239 1245
1240 local_irq_disable(); 1246 local_irq_disable();
1241 sysdev_resume(); 1247 sysdev_resume();
1242 device_power_up(PMSG_RESUME);
1243 local_irq_enable(); 1248 local_irq_enable();
1249
1250 device_power_up(PMSG_RESUME);
1244} 1251}
1245 1252
1246static apm_event_t get_event(void) 1253static apm_event_t get_event(void)
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
index e255341682c8..69b4ddb7de3b 100644
--- a/drivers/base/power/main.c
+++ b/drivers/base/power/main.c
@@ -23,6 +23,7 @@
23#include <linux/pm.h> 23#include <linux/pm.h>
24#include <linux/resume-trace.h> 24#include <linux/resume-trace.h>
25#include <linux/rwsem.h> 25#include <linux/rwsem.h>
26#include <linux/interrupt.h>
26 27
27#include "../base.h" 28#include "../base.h"
28#include "power.h" 29#include "power.h"
@@ -349,7 +350,8 @@ static int resume_device_noirq(struct device *dev, pm_message_t state)
349 * Execute the appropriate "noirq resume" callback for all devices marked 350 * Execute the appropriate "noirq resume" callback for all devices marked
350 * as DPM_OFF_IRQ. 351 * as DPM_OFF_IRQ.
351 * 352 *
352 * Must be called with interrupts disabled and only one CPU running. 353 * Must be called under dpm_list_mtx. Device drivers should not receive
354 * interrupts while it's being executed.
353 */ 355 */
354static void dpm_power_up(pm_message_t state) 356static void dpm_power_up(pm_message_t state)
355{ 357{
@@ -370,14 +372,13 @@ static void dpm_power_up(pm_message_t state)
370 * device_power_up - Turn on all devices that need special attention. 372 * device_power_up - Turn on all devices that need special attention.
371 * @state: PM transition of the system being carried out. 373 * @state: PM transition of the system being carried out.
372 * 374 *
373 * Power on system devices, then devices that required we shut them down 375 * Call the "early" resume handlers and enable device drivers to receive
374 * with interrupts disabled. 376 * interrupts.
375 *
376 * Must be called with interrupts disabled.
377 */ 377 */
378void device_power_up(pm_message_t state) 378void device_power_up(pm_message_t state)
379{ 379{
380 dpm_power_up(state); 380 dpm_power_up(state);
381 resume_device_irqs();
381} 382}
382EXPORT_SYMBOL_GPL(device_power_up); 383EXPORT_SYMBOL_GPL(device_power_up);
383 384
@@ -602,16 +603,17 @@ static int suspend_device_noirq(struct device *dev, pm_message_t state)
602 * device_power_down - Shut down special devices. 603 * device_power_down - Shut down special devices.
603 * @state: PM transition of the system being carried out. 604 * @state: PM transition of the system being carried out.
604 * 605 *
605 * Power down devices that require interrupts to be disabled. 606 * Prevent device drivers from receiving interrupts and call the "late"
606 * Then power down system devices. 607 * suspend handlers.
607 * 608 *
608 * Must be called with interrupts disabled and only one CPU running. 609 * Must be called under dpm_list_mtx.
609 */ 610 */
610int device_power_down(pm_message_t state) 611int device_power_down(pm_message_t state)
611{ 612{
612 struct device *dev; 613 struct device *dev;
613 int error = 0; 614 int error = 0;
614 615
616 suspend_device_irqs();
615 list_for_each_entry_reverse(dev, &dpm_list, power.entry) { 617 list_for_each_entry_reverse(dev, &dpm_list, power.entry) {
616 error = suspend_device_noirq(dev, state); 618 error = suspend_device_noirq(dev, state);
617 if (error) { 619 if (error) {
@@ -621,7 +623,7 @@ int device_power_down(pm_message_t state)
621 dev->power.status = DPM_OFF_IRQ; 623 dev->power.status = DPM_OFF_IRQ;
622 } 624 }
623 if (error) 625 if (error)
624 dpm_power_up(resume_event(state)); 626 device_power_up(resume_event(state));
625 return error; 627 return error;
626} 628}
627EXPORT_SYMBOL_GPL(device_power_down); 629EXPORT_SYMBOL_GPL(device_power_down);
diff --git a/drivers/base/sys.c b/drivers/base/sys.c
index cbd36cf59a0f..76ce75bad91e 100644
--- a/drivers/base/sys.c
+++ b/drivers/base/sys.c
@@ -22,6 +22,7 @@
22#include <linux/pm.h> 22#include <linux/pm.h>
23#include <linux/device.h> 23#include <linux/device.h>
24#include <linux/mutex.h> 24#include <linux/mutex.h>
25#include <linux/interrupt.h>
25 26
26#include "base.h" 27#include "base.h"
27 28
@@ -369,6 +370,13 @@ int sysdev_suspend(pm_message_t state)
369 struct sysdev_driver *drv, *err_drv; 370 struct sysdev_driver *drv, *err_drv;
370 int ret; 371 int ret;
371 372
373 pr_debug("Checking wake-up interrupts\n");
374
375 /* Return error code if there are any wake-up interrupts pending */
376 ret = check_wakeup_irqs();
377 if (ret)
378 return ret;
379
372 pr_debug("Suspending System Devices\n"); 380 pr_debug("Suspending System Devices\n");
373 381
374 list_for_each_entry_reverse(cls, &system_kset->list, kset.kobj.entry) { 382 list_for_each_entry_reverse(cls, &system_kset->list, kset.kobj.entry) {
diff --git a/drivers/xen/manage.c b/drivers/xen/manage.c
index 3ccd348d112d..0d61db1e7b49 100644
--- a/drivers/xen/manage.c
+++ b/drivers/xen/manage.c
@@ -39,12 +39,6 @@ static int xen_suspend(void *data)
39 39
40 BUG_ON(!irqs_disabled()); 40 BUG_ON(!irqs_disabled());
41 41
42 err = device_power_down(PMSG_SUSPEND);
43 if (err) {
44 printk(KERN_ERR "xen_suspend: device_power_down failed: %d\n",
45 err);
46 return err;
47 }
48 err = sysdev_suspend(PMSG_SUSPEND); 42 err = sysdev_suspend(PMSG_SUSPEND);
49 if (err) { 43 if (err) {
50 printk(KERN_ERR "xen_suspend: sysdev_suspend failed: %d\n", 44 printk(KERN_ERR "xen_suspend: sysdev_suspend failed: %d\n",
@@ -69,7 +63,6 @@ static int xen_suspend(void *data)
69 xen_mm_unpin_all(); 63 xen_mm_unpin_all();
70 64
71 sysdev_resume(); 65 sysdev_resume();
72 device_power_up(PMSG_RESUME);
73 66
74 if (!*cancelled) { 67 if (!*cancelled) {
75 xen_irq_resume(); 68 xen_irq_resume();
@@ -108,6 +101,12 @@ static void do_suspend(void)
108 /* XXX use normal device tree? */ 101 /* XXX use normal device tree? */
109 xenbus_suspend(); 102 xenbus_suspend();
110 103
104 err = device_power_down(PMSG_SUSPEND);
105 if (err) {
106 printk(KERN_ERR "device_power_down failed: %d\n", err);
107 goto resume_devices;
108 }
109
111 err = stop_machine(xen_suspend, &cancelled, cpumask_of(0)); 110 err = stop_machine(xen_suspend, &cancelled, cpumask_of(0));
112 if (err) { 111 if (err) {
113 printk(KERN_ERR "failed to start xen_suspend: %d\n", err); 112 printk(KERN_ERR "failed to start xen_suspend: %d\n", err);
@@ -120,6 +119,9 @@ static void do_suspend(void)
120 } else 119 } else
121 xenbus_suspend_cancel(); 120 xenbus_suspend_cancel();
122 121
122 device_power_up(PMSG_RESUME);
123
124resume_devices:
123 device_resume(PMSG_RESUME); 125 device_resume(PMSG_RESUME);
124 126
125 /* Make sure timer events get retriggered on all CPUs */ 127 /* Make sure timer events get retriggered on all CPUs */
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