summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>2017-04-26 17:23:03 -0400
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2017-05-05 16:54:28 -0400
commiteed4d47efe9508b855b09754cf6de4325d8a2f0d (patch)
treea5116bd74fa697a1601dff6db711022e82a55fde
parent8a537ece3d946227e4afa81eae0e43fa47439c7d (diff)
ACPI / sleep: Ignore spurious SCI wakeups from suspend-to-idle
The ACPI SCI (System Control Interrupt) is set up as a wakeup IRQ during suspend-to-idle transitions and, consequently, any events signaled through it wake up the system from that state. However, on some systems some of the events signaled via the ACPI SCI while suspended to idle should not cause the system to wake up. In fact, quite often they should just be discarded. Arguably, systems should not resume entirely on such events, but in order to decide which events really should cause the system to resume and which are spurious, it is necessary to resume up to the point when ACPI SCIs are actually handled and processed, which is after executing dpm_resume_noirq() in the system resume path. For this reasons, add a loop around freeze_enter() in which the platforms can process events signaled via multiplexed IRQ lines like the ACPI SCI and add suspend-to-idle hooks that can be used for this purpose to struct platform_freeze_ops. In the ACPI case, the ->wake hook is used for checking if the SCI has triggered while suspended and deferring the interrupt-induced system wakeup until the events signaled through it are actually processed sufficiently to decide whether or not the system should resume. In turn, the ->sync hook allows all of the relevant event queues to be flushed so as to prevent events from being missed due to race conditions. In addition to that, some ACPI code processing wakeup events needs to be modified to use the "hard" version of wakeup triggers, so that it will cause a system resume to happen on device-induced wakeup events even if the "soft" mechanism to prevent the system from suspending is not enabled (that also helps to catch device-induced wakeup events occurring during suspend transitions in progress). Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
-rw-r--r--drivers/acpi/battery.c2
-rw-r--r--drivers/acpi/button.c5
-rw-r--r--drivers/acpi/device_pm.c3
-rw-r--r--drivers/acpi/sleep.c28
-rw-r--r--drivers/base/power/main.c5
-rw-r--r--drivers/base/power/wakeup.c18
-rw-r--r--include/linux/suspend.h7
-rw-r--r--kernel/power/process.c2
-rw-r--r--kernel/power/suspend.c29
9 files changed, 77 insertions, 22 deletions
diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c
index 4ef1e4624b2b..83ab17e4a795 100644
--- a/drivers/acpi/battery.c
+++ b/drivers/acpi/battery.c
@@ -776,7 +776,7 @@ static int acpi_battery_update(struct acpi_battery *battery, bool resume)
776 if ((battery->state & ACPI_BATTERY_STATE_CRITICAL) || 776 if ((battery->state & ACPI_BATTERY_STATE_CRITICAL) ||
777 (test_bit(ACPI_BATTERY_ALARM_PRESENT, &battery->flags) && 777 (test_bit(ACPI_BATTERY_ALARM_PRESENT, &battery->flags) &&
778 (battery->capacity_now <= battery->alarm))) 778 (battery->capacity_now <= battery->alarm)))
779 pm_wakeup_event(&battery->device->dev, 0); 779 pm_wakeup_hard_event(&battery->device->dev);
780 780
781 return result; 781 return result;
782} 782}
diff --git a/drivers/acpi/button.c b/drivers/acpi/button.c
index 668137e4a069..b7c2a06963d6 100644
--- a/drivers/acpi/button.c
+++ b/drivers/acpi/button.c
@@ -216,7 +216,7 @@ static int acpi_lid_notify_state(struct acpi_device *device, int state)
216 } 216 }
217 217
218 if (state) 218 if (state)
219 pm_wakeup_event(&device->dev, 0); 219 pm_wakeup_hard_event(&device->dev);
220 220
221 ret = blocking_notifier_call_chain(&acpi_lid_notifier, state, device); 221 ret = blocking_notifier_call_chain(&acpi_lid_notifier, state, device);
222 if (ret == NOTIFY_DONE) 222 if (ret == NOTIFY_DONE)
@@ -398,7 +398,7 @@ static void acpi_button_notify(struct acpi_device *device, u32 event)
398 } else { 398 } else {
399 int keycode; 399 int keycode;
400 400
401 pm_wakeup_event(&device->dev, 0); 401 pm_wakeup_hard_event(&device->dev);
402 if (button->suspended) 402 if (button->suspended)
403 break; 403 break;
404 404
@@ -530,6 +530,7 @@ static int acpi_button_add(struct acpi_device *device)
530 lid_device = device; 530 lid_device = device;
531 } 531 }
532 532
533 device_init_wakeup(&device->dev, true);
533 printk(KERN_INFO PREFIX "%s [%s]\n", name, acpi_device_bid(device)); 534 printk(KERN_INFO PREFIX "%s [%s]\n", name, acpi_device_bid(device));
534 return 0; 535 return 0;
535 536
diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c
index 993fd31394c8..798d5003a039 100644
--- a/drivers/acpi/device_pm.c
+++ b/drivers/acpi/device_pm.c
@@ -24,6 +24,7 @@
24#include <linux/pm_qos.h> 24#include <linux/pm_qos.h>
25#include <linux/pm_domain.h> 25#include <linux/pm_domain.h>
26#include <linux/pm_runtime.h> 26#include <linux/pm_runtime.h>
27#include <linux/suspend.h>
27 28
28#include "internal.h" 29#include "internal.h"
29 30
@@ -399,7 +400,7 @@ static void acpi_pm_notify_handler(acpi_handle handle, u32 val, void *not_used)
399 mutex_lock(&acpi_pm_notifier_lock); 400 mutex_lock(&acpi_pm_notifier_lock);
400 401
401 if (adev->wakeup.flags.notifier_present) { 402 if (adev->wakeup.flags.notifier_present) {
402 __pm_wakeup_event(adev->wakeup.ws, 0); 403 pm_wakeup_ws_event(adev->wakeup.ws, 0, true);
403 if (adev->wakeup.context.work.func) 404 if (adev->wakeup.context.work.func)
404 queue_pm_work(&adev->wakeup.context.work); 405 queue_pm_work(&adev->wakeup.context.work);
405 } 406 }
diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c
index a4327af676fe..e84005d642e6 100644
--- a/drivers/acpi/sleep.c
+++ b/drivers/acpi/sleep.c
@@ -662,14 +662,40 @@ static int acpi_freeze_prepare(void)
662 acpi_os_wait_events_complete(); 662 acpi_os_wait_events_complete();
663 if (acpi_sci_irq_valid()) 663 if (acpi_sci_irq_valid())
664 enable_irq_wake(acpi_sci_irq); 664 enable_irq_wake(acpi_sci_irq);
665
665 return 0; 666 return 0;
666} 667}
667 668
669static void acpi_freeze_wake(void)
670{
671 /*
672 * If IRQD_WAKEUP_ARMED is not set for the SCI at this point, it means
673 * that the SCI has triggered while suspended, so cancel the wakeup in
674 * case it has not been a wakeup event (the GPEs will be checked later).
675 */
676 if (acpi_sci_irq_valid() &&
677 !irqd_is_wakeup_armed(irq_get_irq_data(acpi_sci_irq)))
678 pm_system_cancel_wakeup();
679}
680
681static void acpi_freeze_sync(void)
682{
683 /*
684 * Process all pending events in case there are any wakeup ones.
685 *
686 * The EC driver uses the system workqueue, so that one needs to be
687 * flushed too.
688 */
689 acpi_os_wait_events_complete();
690 flush_scheduled_work();
691}
692
668static void acpi_freeze_restore(void) 693static void acpi_freeze_restore(void)
669{ 694{
670 acpi_disable_wakeup_devices(ACPI_STATE_S0); 695 acpi_disable_wakeup_devices(ACPI_STATE_S0);
671 if (acpi_sci_irq_valid()) 696 if (acpi_sci_irq_valid())
672 disable_irq_wake(acpi_sci_irq); 697 disable_irq_wake(acpi_sci_irq);
698
673 acpi_enable_all_runtime_gpes(); 699 acpi_enable_all_runtime_gpes();
674} 700}
675 701
@@ -681,6 +707,8 @@ static void acpi_freeze_end(void)
681static const struct platform_freeze_ops acpi_freeze_ops = { 707static const struct platform_freeze_ops acpi_freeze_ops = {
682 .begin = acpi_freeze_begin, 708 .begin = acpi_freeze_begin,
683 .prepare = acpi_freeze_prepare, 709 .prepare = acpi_freeze_prepare,
710 .wake = acpi_freeze_wake,
711 .sync = acpi_freeze_sync,
684 .restore = acpi_freeze_restore, 712 .restore = acpi_freeze_restore,
685 .end = acpi_freeze_end, 713 .end = acpi_freeze_end,
686}; 714};
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
index 9faee1c893e5..e987a6f55d36 100644
--- a/drivers/base/power/main.c
+++ b/drivers/base/power/main.c
@@ -1091,11 +1091,6 @@ static int __device_suspend_noirq(struct device *dev, pm_message_t state, bool a
1091 if (async_error) 1091 if (async_error)
1092 goto Complete; 1092 goto Complete;
1093 1093
1094 if (pm_wakeup_pending()) {
1095 async_error = -EBUSY;
1096 goto Complete;
1097 }
1098
1099 if (dev->power.syscore || dev->power.direct_complete) 1094 if (dev->power.syscore || dev->power.direct_complete)
1100 goto Complete; 1095 goto Complete;
1101 1096
diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c
index 84c41c98d6fc..f62082fdd670 100644
--- a/drivers/base/power/wakeup.c
+++ b/drivers/base/power/wakeup.c
@@ -28,8 +28,8 @@ bool events_check_enabled __read_mostly;
28/* First wakeup IRQ seen by the kernel in the last cycle. */ 28/* First wakeup IRQ seen by the kernel in the last cycle. */
29unsigned int pm_wakeup_irq __read_mostly; 29unsigned int pm_wakeup_irq __read_mostly;
30 30
31/* If set and the system is suspending, terminate the suspend. */ 31/* If greater than 0 and the system is suspending, terminate the suspend. */
32static bool pm_abort_suspend __read_mostly; 32static atomic_t pm_abort_suspend __read_mostly;
33 33
34/* 34/*
35 * Combined counters of registered wakeup events and wakeup events in progress. 35 * Combined counters of registered wakeup events and wakeup events in progress.
@@ -856,20 +856,26 @@ bool pm_wakeup_pending(void)
856 pm_print_active_wakeup_sources(); 856 pm_print_active_wakeup_sources();
857 } 857 }
858 858
859 return ret || pm_abort_suspend; 859 return ret || atomic_read(&pm_abort_suspend) > 0;
860} 860}
861 861
862void pm_system_wakeup(void) 862void pm_system_wakeup(void)
863{ 863{
864 pm_abort_suspend = true; 864 atomic_inc(&pm_abort_suspend);
865 freeze_wake(); 865 freeze_wake();
866} 866}
867EXPORT_SYMBOL_GPL(pm_system_wakeup); 867EXPORT_SYMBOL_GPL(pm_system_wakeup);
868 868
869void pm_wakeup_clear(void) 869void pm_system_cancel_wakeup(void)
870{
871 atomic_dec(&pm_abort_suspend);
872}
873
874void pm_wakeup_clear(bool reset)
870{ 875{
871 pm_abort_suspend = false;
872 pm_wakeup_irq = 0; 876 pm_wakeup_irq = 0;
877 if (reset)
878 atomic_set(&pm_abort_suspend, 0);
873} 879}
874 880
875void pm_system_irq_wakeup(unsigned int irq_number) 881void pm_system_irq_wakeup(unsigned int irq_number)
diff --git a/include/linux/suspend.h b/include/linux/suspend.h
index d9718378a8be..0b1cf32edfd7 100644
--- a/include/linux/suspend.h
+++ b/include/linux/suspend.h
@@ -189,6 +189,8 @@ struct platform_suspend_ops {
189struct platform_freeze_ops { 189struct platform_freeze_ops {
190 int (*begin)(void); 190 int (*begin)(void);
191 int (*prepare)(void); 191 int (*prepare)(void);
192 void (*wake)(void);
193 void (*sync)(void);
192 void (*restore)(void); 194 void (*restore)(void);
193 void (*end)(void); 195 void (*end)(void);
194}; 196};
@@ -428,7 +430,8 @@ extern unsigned int pm_wakeup_irq;
428 430
429extern bool pm_wakeup_pending(void); 431extern bool pm_wakeup_pending(void);
430extern void pm_system_wakeup(void); 432extern void pm_system_wakeup(void);
431extern void pm_wakeup_clear(void); 433extern void pm_system_cancel_wakeup(void);
434extern void pm_wakeup_clear(bool reset);
432extern void pm_system_irq_wakeup(unsigned int irq_number); 435extern void pm_system_irq_wakeup(unsigned int irq_number);
433extern bool pm_get_wakeup_count(unsigned int *count, bool block); 436extern bool pm_get_wakeup_count(unsigned int *count, bool block);
434extern bool pm_save_wakeup_count(unsigned int count); 437extern bool pm_save_wakeup_count(unsigned int count);
@@ -478,7 +481,7 @@ static inline int unregister_pm_notifier(struct notifier_block *nb)
478 481
479static inline bool pm_wakeup_pending(void) { return false; } 482static inline bool pm_wakeup_pending(void) { return false; }
480static inline void pm_system_wakeup(void) {} 483static inline void pm_system_wakeup(void) {}
481static inline void pm_wakeup_clear(void) {} 484static inline void pm_wakeup_clear(bool reset) {}
482static inline void pm_system_irq_wakeup(unsigned int irq_number) {} 485static inline void pm_system_irq_wakeup(unsigned int irq_number) {}
483 486
484static inline void lock_system_sleep(void) {} 487static inline void lock_system_sleep(void) {}
diff --git a/kernel/power/process.c b/kernel/power/process.c
index c7209f060eeb..78672d324a6e 100644
--- a/kernel/power/process.c
+++ b/kernel/power/process.c
@@ -132,7 +132,7 @@ int freeze_processes(void)
132 if (!pm_freezing) 132 if (!pm_freezing)
133 atomic_inc(&system_freezing_cnt); 133 atomic_inc(&system_freezing_cnt);
134 134
135 pm_wakeup_clear(); 135 pm_wakeup_clear(true);
136 pr_info("Freezing user space processes ... "); 136 pr_info("Freezing user space processes ... ");
137 pm_freezing = true; 137 pm_freezing = true;
138 error = try_to_freeze_tasks(true); 138 error = try_to_freeze_tasks(true);
diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c
index 15e6baef5c73..c0248c74d6d4 100644
--- a/kernel/power/suspend.c
+++ b/kernel/power/suspend.c
@@ -72,6 +72,8 @@ static void freeze_begin(void)
72 72
73static void freeze_enter(void) 73static void freeze_enter(void)
74{ 74{
75 trace_suspend_resume(TPS("machine_suspend"), PM_SUSPEND_FREEZE, true);
76
75 spin_lock_irq(&suspend_freeze_lock); 77 spin_lock_irq(&suspend_freeze_lock);
76 if (pm_wakeup_pending()) 78 if (pm_wakeup_pending())
77 goto out; 79 goto out;
@@ -98,6 +100,27 @@ static void freeze_enter(void)
98 out: 100 out:
99 suspend_freeze_state = FREEZE_STATE_NONE; 101 suspend_freeze_state = FREEZE_STATE_NONE;
100 spin_unlock_irq(&suspend_freeze_lock); 102 spin_unlock_irq(&suspend_freeze_lock);
103
104 trace_suspend_resume(TPS("machine_suspend"), PM_SUSPEND_FREEZE, false);
105}
106
107static void s2idle_loop(void)
108{
109 do {
110 freeze_enter();
111
112 if (freeze_ops && freeze_ops->wake)
113 freeze_ops->wake();
114
115 dpm_resume_noirq(PMSG_RESUME);
116 if (freeze_ops && freeze_ops->sync)
117 freeze_ops->sync();
118
119 if (pm_wakeup_pending())
120 break;
121
122 pm_wakeup_clear(false);
123 } while (!dpm_suspend_noirq(PMSG_SUSPEND));
101} 124}
102 125
103void freeze_wake(void) 126void freeze_wake(void)
@@ -371,10 +394,8 @@ static int suspend_enter(suspend_state_t state, bool *wakeup)
371 * all the devices are suspended. 394 * all the devices are suspended.
372 */ 395 */
373 if (state == PM_SUSPEND_FREEZE) { 396 if (state == PM_SUSPEND_FREEZE) {
374 trace_suspend_resume(TPS("machine_suspend"), state, true); 397 s2idle_loop();
375 freeze_enter(); 398 goto Platform_early_resume;
376 trace_suspend_resume(TPS("machine_suspend"), state, false);
377 goto Platform_wake;
378 } 399 }
379 400
380 error = disable_nonboot_cpus(); 401 error = disable_nonboot_cpus();