Skip to content

Commit 2ed8d2b

Browse files
committed
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 <[email protected]> Acked-by: Ingo Molnar <[email protected]>
1 parent 0a0c516 commit 2ed8d2b

File tree

7 files changed

+83
-40
lines changed

7 files changed

+83
-40
lines changed

arch/x86/kernel/apm_32.c

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1190,8 +1190,10 @@ static int suspend(int vetoable)
11901190
struct apm_user *as;
11911191

11921192
device_suspend(PMSG_SUSPEND);
1193-
local_irq_disable();
1193+
11941194
device_power_down(PMSG_SUSPEND);
1195+
1196+
local_irq_disable();
11951197
sysdev_suspend(PMSG_SUSPEND);
11961198

11971199
local_irq_enable();
@@ -1209,9 +1211,12 @@ static int suspend(int vetoable)
12091211
if (err != APM_SUCCESS)
12101212
apm_error("suspend", err);
12111213
err = (err == APM_SUCCESS) ? 0 : -EIO;
1214+
12121215
sysdev_resume();
1213-
device_power_up(PMSG_RESUME);
12141216
local_irq_enable();
1217+
1218+
device_power_up(PMSG_RESUME);
1219+
12151220
device_resume(PMSG_RESUME);
12161221
queue_event(APM_NORMAL_RESUME, NULL);
12171222
spin_lock(&user_list_lock);
@@ -1228,8 +1233,9 @@ static void standby(void)
12281233
{
12291234
int err;
12301235

1231-
local_irq_disable();
12321236
device_power_down(PMSG_SUSPEND);
1237+
1238+
local_irq_disable();
12331239
sysdev_suspend(PMSG_SUSPEND);
12341240
local_irq_enable();
12351241

@@ -1239,8 +1245,9 @@ static void standby(void)
12391245

12401246
local_irq_disable();
12411247
sysdev_resume();
1242-
device_power_up(PMSG_RESUME);
12431248
local_irq_enable();
1249+
1250+
device_power_up(PMSG_RESUME);
12441251
}
12451252

12461253
static apm_event_t get_event(void)

drivers/base/power/main.c

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include <linux/pm.h>
2424
#include <linux/resume-trace.h>
2525
#include <linux/rwsem.h>
26+
#include <linux/interrupt.h>
2627

2728
#include "../base.h"
2829
#include "power.h"
@@ -349,7 +350,8 @@ static int resume_device_noirq(struct device *dev, pm_message_t state)
349350
* Execute the appropriate "noirq resume" callback for all devices marked
350351
* as DPM_OFF_IRQ.
351352
*
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.
353355
*/
354356
static void dpm_power_up(pm_message_t state)
355357
{
@@ -370,14 +372,13 @@ static void dpm_power_up(pm_message_t state)
370372
* device_power_up - Turn on all devices that need special attention.
371373
* @state: PM transition of the system being carried out.
372374
*
373-
* Power on system devices, then devices that required we shut them down
374-
* with interrupts disabled.
375-
*
376-
* Must be called with interrupts disabled.
375+
* Call the "early" resume handlers and enable device drivers to receive
376+
* interrupts.
377377
*/
378378
void device_power_up(pm_message_t state)
379379
{
380380
dpm_power_up(state);
381+
resume_device_irqs();
381382
}
382383
EXPORT_SYMBOL_GPL(device_power_up);
383384

@@ -602,16 +603,17 @@ static int suspend_device_noirq(struct device *dev, pm_message_t state)
602603
* device_power_down - Shut down special devices.
603604
* @state: PM transition of the system being carried out.
604605
*
605-
* Power down devices that require interrupts to be disabled.
606-
* Then power down system devices.
606+
* Prevent device drivers from receiving interrupts and call the "late"
607+
* suspend handlers.
607608
*
608-
* Must be called with interrupts disabled and only one CPU running.
609+
* Must be called under dpm_list_mtx.
609610
*/
610611
int device_power_down(pm_message_t state)
611612
{
612613
struct device *dev;
613614
int error = 0;
614615

616+
suspend_device_irqs();
615617
list_for_each_entry_reverse(dev, &dpm_list, power.entry) {
616618
error = suspend_device_noirq(dev, state);
617619
if (error) {
@@ -621,7 +623,7 @@ int device_power_down(pm_message_t state)
621623
dev->power.status = DPM_OFF_IRQ;
622624
}
623625
if (error)
624-
dpm_power_up(resume_event(state));
626+
device_power_up(resume_event(state));
625627
return error;
626628
}
627629
EXPORT_SYMBOL_GPL(device_power_down);

drivers/base/sys.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include <linux/pm.h>
2323
#include <linux/device.h>
2424
#include <linux/mutex.h>
25+
#include <linux/interrupt.h>
2526

2627
#include "base.h"
2728

@@ -369,6 +370,13 @@ int sysdev_suspend(pm_message_t state)
369370
struct sysdev_driver *drv, *err_drv;
370371
int ret;
371372

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+
372380
pr_debug("Suspending System Devices\n");
373381

374382
list_for_each_entry_reverse(cls, &system_kset->list, kset.kobj.entry) {

drivers/xen/manage.c

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,6 @@ static int xen_suspend(void *data)
3939

4040
BUG_ON(!irqs_disabled());
4141

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-
}
4842
err = sysdev_suspend(PMSG_SUSPEND);
4943
if (err) {
5044
printk(KERN_ERR "xen_suspend: sysdev_suspend failed: %d\n",
@@ -69,7 +63,6 @@ static int xen_suspend(void *data)
6963
xen_mm_unpin_all();
7064

7165
sysdev_resume();
72-
device_power_up(PMSG_RESUME);
7366

7467
if (!*cancelled) {
7568
xen_irq_resume();
@@ -108,6 +101,12 @@ static void do_suspend(void)
108101
/* XXX use normal device tree? */
109102
xenbus_suspend();
110103

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+
111110
err = stop_machine(xen_suspend, &cancelled, cpumask_of(0));
112111
if (err) {
113112
printk(KERN_ERR "failed to start xen_suspend: %d\n", err);
@@ -120,6 +119,9 @@ static void do_suspend(void)
120119
} else
121120
xenbus_suspend_cancel();
122121

122+
device_power_up(PMSG_RESUME);
123+
124+
resume_devices:
123125
device_resume(PMSG_RESUME);
124126

125127
/* Make sure timer events get retriggered on all CPUs */

kernel/kexec.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1454,7 +1454,6 @@ int kernel_kexec(void)
14541454
if (error)
14551455
goto Resume_devices;
14561456
device_pm_lock();
1457-
local_irq_disable();
14581457
/* At this point, device_suspend() has been called,
14591458
* but *not* device_power_down(). We *must*
14601459
* device_power_down() now. Otherwise, drivers for
@@ -1464,8 +1463,9 @@ int kernel_kexec(void)
14641463
*/
14651464
error = device_power_down(PMSG_FREEZE);
14661465
if (error)
1467-
goto Enable_irqs;
1466+
goto Unlock_pm;
14681467

1468+
local_irq_disable();
14691469
/* Suspend system devices */
14701470
error = sysdev_suspend(PMSG_FREEZE);
14711471
if (error)
@@ -1484,9 +1484,9 @@ int kernel_kexec(void)
14841484
if (kexec_image->preserve_context) {
14851485
sysdev_resume();
14861486
Power_up_devices:
1487-
device_power_up(PMSG_RESTORE);
1488-
Enable_irqs:
14891487
local_irq_enable();
1488+
device_power_up(PMSG_RESTORE);
1489+
Unlock_pm:
14901490
device_pm_unlock();
14911491
enable_nonboot_cpus();
14921492
Resume_devices:

kernel/power/disk.c

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ static int create_image(int platform_mode)
214214
return error;
215215

216216
device_pm_lock();
217-
local_irq_disable();
217+
218218
/* At this point, device_suspend() has been called, but *not*
219219
* device_power_down(). We *must* call device_power_down() now.
220220
* Otherwise, drivers for some devices (e.g. interrupt controllers)
@@ -225,8 +225,11 @@ static int create_image(int platform_mode)
225225
if (error) {
226226
printk(KERN_ERR "PM: Some devices failed to power down, "
227227
"aborting hibernation\n");
228-
goto Enable_irqs;
228+
goto Unlock;
229229
}
230+
231+
local_irq_disable();
232+
230233
sysdev_suspend(PMSG_FREEZE);
231234
if (error) {
232235
printk(KERN_ERR "PM: Some devices failed to power down, "
@@ -252,12 +255,16 @@ static int create_image(int platform_mode)
252255
/* NOTE: device_power_up() is just a resume() for devices
253256
* that suspended with irqs off ... no overall powerup.
254257
*/
258+
255259
Power_up_devices:
260+
local_irq_enable();
261+
256262
device_power_up(in_suspend ?
257263
(error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE);
258-
Enable_irqs:
259-
local_irq_enable();
264+
265+
Unlock:
260266
device_pm_unlock();
267+
261268
return error;
262269
}
263270

@@ -336,13 +343,16 @@ static int resume_target_kernel(void)
336343
int error;
337344

338345
device_pm_lock();
339-
local_irq_disable();
346+
340347
error = device_power_down(PMSG_QUIESCE);
341348
if (error) {
342349
printk(KERN_ERR "PM: Some devices failed to power down, "
343350
"aborting resume\n");
344-
goto Enable_irqs;
351+
goto Unlock;
345352
}
353+
354+
local_irq_disable();
355+
346356
sysdev_suspend(PMSG_QUIESCE);
347357
/* We'll ignore saved state, but this gets preempt count (etc) right */
348358
save_processor_state();
@@ -366,11 +376,16 @@ static int resume_target_kernel(void)
366376
swsusp_free();
367377
restore_processor_state();
368378
touch_softlockup_watchdog();
379+
369380
sysdev_resume();
370-
device_power_up(PMSG_RECOVER);
371-
Enable_irqs:
381+
372382
local_irq_enable();
383+
384+
device_power_up(PMSG_RECOVER);
385+
386+
Unlock:
373387
device_pm_unlock();
388+
374389
return error;
375390
}
376391

@@ -447,15 +462,16 @@ int hibernation_platform_enter(void)
447462
goto Finish;
448463

449464
device_pm_lock();
450-
local_irq_disable();
465+
451466
error = device_power_down(PMSG_HIBERNATE);
452467
if (!error) {
468+
local_irq_disable();
453469
sysdev_suspend(PMSG_HIBERNATE);
454470
hibernation_ops->enter();
455471
/* We should never get here */
456472
while (1);
457473
}
458-
local_irq_enable();
474+
459475
device_pm_unlock();
460476

461477
/*
@@ -464,12 +480,15 @@ int hibernation_platform_enter(void)
464480
*/
465481
Finish:
466482
hibernation_ops->finish();
483+
467484
Resume_devices:
468485
entering_platform_hibernation = false;
469486
device_resume(PMSG_RESTORE);
470487
resume_console();
488+
471489
Close:
472490
hibernation_ops->end();
491+
473492
return error;
474493
}
475494

kernel/power/main.c

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -287,29 +287,34 @@ void __attribute__ ((weak)) arch_suspend_enable_irqs(void)
287287
*/
288288
static int suspend_enter(suspend_state_t state)
289289
{
290-
int error = 0;
290+
int error;
291291

292292
device_pm_lock();
293-
arch_suspend_disable_irqs();
294-
BUG_ON(!irqs_disabled());
295293

296-
if ((error = device_power_down(PMSG_SUSPEND))) {
294+
error = device_power_down(PMSG_SUSPEND);
295+
if (error) {
297296
printk(KERN_ERR "PM: Some devices failed to power down\n");
298297
goto Done;
299298
}
300299

300+
arch_suspend_disable_irqs();
301+
BUG_ON(!irqs_disabled());
302+
301303
error = sysdev_suspend(PMSG_SUSPEND);
302304
if (!error) {
303305
if (!suspend_test(TEST_CORE))
304306
error = suspend_ops->enter(state);
305307
sysdev_resume();
306308
}
307309

308-
device_power_up(PMSG_RESUME);
309-
Done:
310310
arch_suspend_enable_irqs();
311311
BUG_ON(irqs_disabled());
312+
313+
device_power_up(PMSG_RESUME);
314+
315+
Done:
312316
device_pm_unlock();
317+
313318
return error;
314319
}
315320

0 commit comments

Comments
 (0)