diff options
author | Rafael J. Wysocki <rjw@sisk.pl> | 2009-03-16 17:34:06 -0400 |
---|---|---|
committer | Rafael J. Wysocki <rjw@sisk.pl> | 2009-03-30 15:46:54 -0400 |
commit | 2ed8d2b3a81bdbb0418301628ccdb008ac9f40b7 (patch) | |
tree | 54ab0cd7aa7db73151533b463bd490b62a29c462 /drivers/base | |
parent | 0a0c5168df270a50e3518e4f12bddb31f8f5f38f (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 'drivers/base')
-rw-r--r-- | drivers/base/power/main.c | 20 | ||||
-rw-r--r-- | drivers/base/sys.c | 8 |
2 files changed, 19 insertions, 9 deletions
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 | */ |
354 | static void dpm_power_up(pm_message_t state) | 356 | static 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 | */ |
378 | void device_power_up(pm_message_t state) | 378 | void 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 | } |
382 | EXPORT_SYMBOL_GPL(device_power_up); | 383 | EXPORT_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 | */ |
610 | int device_power_down(pm_message_t state) | 611 | int 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 | } |
627 | EXPORT_SYMBOL_GPL(device_power_down); | 629 | EXPORT_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) { |