diff options
author | Thomas Gleixner <tglx@linutronix.de> | 2018-05-25 11:54:41 -0400 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2018-05-27 05:55:02 -0400 |
commit | c1a957d17086d20d52d7f9c8dffaeac2ee09d6f9 (patch) | |
tree | 55fd308201993a3caf2b2df2bf23ab9ef2ed5718 | |
parent | 771c577c23bac90597c685971d7297ea00f99d11 (diff) |
PM / suspend: Prevent might sleep splats
timekeeping suspend/resume calls read_persistent_clock() which takes
rtc_lock. That results in might sleep warnings because at that point
we run with interrupts disabled.
We cannot convert rtc_lock to a raw spinlock as that would trigger
other might sleep warnings.
As a workaround we disable the might sleep warnings by setting
system_state to SYSTEM_SUSPEND before calling sysdev_suspend() and
restoring it to SYSTEM_RUNNING afer sysdev_resume(). There is no lock
contention because hibernate / suspend to RAM is single-CPU at this
point.
In s2idle's case the system_state is set to SYSTEM_SUSPEND before
timekeeping_suspend() which is invoked by the last CPU. In the resume
case it set back to SYSTEM_RUNNING after timekeeping_resume() which is
invoked by the first CPU in the resume case. The other CPUs will block
on tick_freeze_lock.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
[bigeasy: cover s2idle in tick_freeze() / tick_unfreeze()]
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
-rw-r--r-- | include/linux/kernel.h | 1 | ||||
-rw-r--r-- | kernel/power/hibernate.c | 7 | ||||
-rw-r--r-- | kernel/power/suspend.c | 4 | ||||
-rw-r--r-- | kernel/time/tick-common.c | 2 |
4 files changed, 14 insertions, 0 deletions
diff --git a/include/linux/kernel.h b/include/linux/kernel.h index 6a1eb0b0aad9..7aed92624531 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h | |||
@@ -542,6 +542,7 @@ extern enum system_states { | |||
542 | SYSTEM_HALT, | 542 | SYSTEM_HALT, |
543 | SYSTEM_POWER_OFF, | 543 | SYSTEM_POWER_OFF, |
544 | SYSTEM_RESTART, | 544 | SYSTEM_RESTART, |
545 | SYSTEM_SUSPEND, | ||
545 | } system_state; | 546 | } system_state; |
546 | 547 | ||
547 | /* This cannot be an enum because some may be used in assembly source. */ | 548 | /* This cannot be an enum because some may be used in assembly source. */ |
diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c index 5454cc639a8d..9c85c7822383 100644 --- a/kernel/power/hibernate.c +++ b/kernel/power/hibernate.c | |||
@@ -287,6 +287,8 @@ static int create_image(int platform_mode) | |||
287 | 287 | ||
288 | local_irq_disable(); | 288 | local_irq_disable(); |
289 | 289 | ||
290 | system_state = SYSTEM_SUSPEND; | ||
291 | |||
290 | error = syscore_suspend(); | 292 | error = syscore_suspend(); |
291 | if (error) { | 293 | if (error) { |
292 | pr_err("Some system devices failed to power down, aborting hibernation\n"); | 294 | pr_err("Some system devices failed to power down, aborting hibernation\n"); |
@@ -317,6 +319,7 @@ static int create_image(int platform_mode) | |||
317 | syscore_resume(); | 319 | syscore_resume(); |
318 | 320 | ||
319 | Enable_irqs: | 321 | Enable_irqs: |
322 | system_state = SYSTEM_RUNNING; | ||
320 | local_irq_enable(); | 323 | local_irq_enable(); |
321 | 324 | ||
322 | Enable_cpus: | 325 | Enable_cpus: |
@@ -445,6 +448,7 @@ static int resume_target_kernel(bool platform_mode) | |||
445 | goto Enable_cpus; | 448 | goto Enable_cpus; |
446 | 449 | ||
447 | local_irq_disable(); | 450 | local_irq_disable(); |
451 | system_state = SYSTEM_SUSPEND; | ||
448 | 452 | ||
449 | error = syscore_suspend(); | 453 | error = syscore_suspend(); |
450 | if (error) | 454 | if (error) |
@@ -478,6 +482,7 @@ static int resume_target_kernel(bool platform_mode) | |||
478 | syscore_resume(); | 482 | syscore_resume(); |
479 | 483 | ||
480 | Enable_irqs: | 484 | Enable_irqs: |
485 | system_state = SYSTEM_RUNNING; | ||
481 | local_irq_enable(); | 486 | local_irq_enable(); |
482 | 487 | ||
483 | Enable_cpus: | 488 | Enable_cpus: |
@@ -563,6 +568,7 @@ int hibernation_platform_enter(void) | |||
563 | goto Enable_cpus; | 568 | goto Enable_cpus; |
564 | 569 | ||
565 | local_irq_disable(); | 570 | local_irq_disable(); |
571 | system_state = SYSTEM_SUSPEND; | ||
566 | syscore_suspend(); | 572 | syscore_suspend(); |
567 | if (pm_wakeup_pending()) { | 573 | if (pm_wakeup_pending()) { |
568 | error = -EAGAIN; | 574 | error = -EAGAIN; |
@@ -575,6 +581,7 @@ int hibernation_platform_enter(void) | |||
575 | 581 | ||
576 | Power_up: | 582 | Power_up: |
577 | syscore_resume(); | 583 | syscore_resume(); |
584 | system_state = SYSTEM_RUNNING; | ||
578 | local_irq_enable(); | 585 | local_irq_enable(); |
579 | 586 | ||
580 | Enable_cpus: | 587 | Enable_cpus: |
diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c index 4c10be0f4843..5149c77506b3 100644 --- a/kernel/power/suspend.c +++ b/kernel/power/suspend.c | |||
@@ -428,6 +428,8 @@ static int suspend_enter(suspend_state_t state, bool *wakeup) | |||
428 | arch_suspend_disable_irqs(); | 428 | arch_suspend_disable_irqs(); |
429 | BUG_ON(!irqs_disabled()); | 429 | BUG_ON(!irqs_disabled()); |
430 | 430 | ||
431 | system_state = SYSTEM_SUSPEND; | ||
432 | |||
431 | error = syscore_suspend(); | 433 | error = syscore_suspend(); |
432 | if (!error) { | 434 | if (!error) { |
433 | *wakeup = pm_wakeup_pending(); | 435 | *wakeup = pm_wakeup_pending(); |
@@ -443,6 +445,8 @@ static int suspend_enter(suspend_state_t state, bool *wakeup) | |||
443 | syscore_resume(); | 445 | syscore_resume(); |
444 | } | 446 | } |
445 | 447 | ||
448 | system_state = SYSTEM_RUNNING; | ||
449 | |||
446 | arch_suspend_enable_irqs(); | 450 | arch_suspend_enable_irqs(); |
447 | BUG_ON(irqs_disabled()); | 451 | BUG_ON(irqs_disabled()); |
448 | 452 | ||
diff --git a/kernel/time/tick-common.c b/kernel/time/tick-common.c index 49edc1c4f3e6..14de3727b18e 100644 --- a/kernel/time/tick-common.c +++ b/kernel/time/tick-common.c | |||
@@ -490,6 +490,7 @@ void tick_freeze(void) | |||
490 | if (tick_freeze_depth == num_online_cpus()) { | 490 | if (tick_freeze_depth == num_online_cpus()) { |
491 | trace_suspend_resume(TPS("timekeeping_freeze"), | 491 | trace_suspend_resume(TPS("timekeeping_freeze"), |
492 | smp_processor_id(), true); | 492 | smp_processor_id(), true); |
493 | system_state = SYSTEM_SUSPEND; | ||
493 | timekeeping_suspend(); | 494 | timekeeping_suspend(); |
494 | } else { | 495 | } else { |
495 | tick_suspend_local(); | 496 | tick_suspend_local(); |
@@ -513,6 +514,7 @@ void tick_unfreeze(void) | |||
513 | 514 | ||
514 | if (tick_freeze_depth == num_online_cpus()) { | 515 | if (tick_freeze_depth == num_online_cpus()) { |
515 | timekeeping_resume(); | 516 | timekeeping_resume(); |
517 | system_state = SYSTEM_RUNNING; | ||
516 | trace_suspend_resume(TPS("timekeeping_freeze"), | 518 | trace_suspend_resume(TPS("timekeeping_freeze"), |
517 | smp_processor_id(), false); | 519 | smp_processor_id(), false); |
518 | } else { | 520 | } else { |