Skip to content

Commit 33e4f80

Browse files
committed
ACPI / PM: 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. However, to preserve the existing behavior with respect to suspend-to-RAM, this only is done in the suspend-to-idle case and only if an SCI has occurred while suspended. Signed-off-by: Rafael J. Wysocki <[email protected]>
1 parent 63dada8 commit 33e4f80

File tree

11 files changed

+103
-25
lines changed

11 files changed

+103
-25
lines changed

drivers/acpi/battery.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -782,7 +782,7 @@ static int acpi_battery_update(struct acpi_battery *battery, bool resume)
782782
if ((battery->state & ACPI_BATTERY_STATE_CRITICAL) ||
783783
(test_bit(ACPI_BATTERY_ALARM_PRESENT, &battery->flags) &&
784784
(battery->capacity_now <= battery->alarm)))
785-
pm_wakeup_event(&battery->device->dev, 0);
785+
acpi_pm_wakeup_event(&battery->device->dev);
786786

787787
return result;
788788
}

drivers/acpi/button.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ static int acpi_lid_notify_state(struct acpi_device *device, int state)
217217
}
218218

219219
if (state)
220-
pm_wakeup_event(&device->dev, 0);
220+
acpi_pm_wakeup_event(&device->dev);
221221

222222
ret = blocking_notifier_call_chain(&acpi_lid_notifier, state, device);
223223
if (ret == NOTIFY_DONE)
@@ -402,7 +402,7 @@ static void acpi_button_notify(struct acpi_device *device, u32 event)
402402
} else {
403403
int keycode;
404404

405-
pm_wakeup_event(&device->dev, 0);
405+
acpi_pm_wakeup_event(&device->dev);
406406
if (button->suspended)
407407
break;
408408

@@ -534,6 +534,7 @@ static int acpi_button_add(struct acpi_device *device)
534534
lid_device = device;
535535
}
536536

537+
device_init_wakeup(&device->dev, true);
537538
printk(KERN_INFO PREFIX "%s [%s]\n", name, acpi_device_bid(device));
538539
return 0;
539540

drivers/acpi/device_pm.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include <linux/pm_qos.h>
2525
#include <linux/pm_domain.h>
2626
#include <linux/pm_runtime.h>
27+
#include <linux/suspend.h>
2728

2829
#include "internal.h"
2930

@@ -385,6 +386,12 @@ EXPORT_SYMBOL(acpi_bus_power_manageable);
385386
#ifdef CONFIG_PM
386387
static DEFINE_MUTEX(acpi_pm_notifier_lock);
387388

389+
void acpi_pm_wakeup_event(struct device *dev)
390+
{
391+
pm_wakeup_dev_event(dev, 0, acpi_s2idle_wakeup());
392+
}
393+
EXPORT_SYMBOL_GPL(acpi_pm_wakeup_event);
394+
388395
static void acpi_pm_notify_handler(acpi_handle handle, u32 val, void *not_used)
389396
{
390397
struct acpi_device *adev;
@@ -399,7 +406,7 @@ static void acpi_pm_notify_handler(acpi_handle handle, u32 val, void *not_used)
399406
mutex_lock(&acpi_pm_notifier_lock);
400407

401408
if (adev->wakeup.flags.notifier_present) {
402-
__pm_wakeup_event(adev->wakeup.ws, 0);
409+
pm_wakeup_ws_event(adev->wakeup.ws, 0, acpi_s2idle_wakeup());
403410
if (adev->wakeup.context.func)
404411
adev->wakeup.context.func(&adev->wakeup.context);
405412
}

drivers/acpi/internal.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,8 +198,10 @@ void acpi_ec_remove_query_handler(struct acpi_ec *ec, u8 query_bit);
198198
Suspend/Resume
199199
-------------------------------------------------------------------------- */
200200
#ifdef CONFIG_ACPI_SYSTEM_POWER_STATES_SUPPORT
201+
extern bool acpi_s2idle_wakeup(void);
201202
extern int acpi_sleep_init(void);
202203
#else
204+
static inline bool acpi_s2idle_wakeup(void) { return false; }
203205
static inline int acpi_sleep_init(void) { return -ENXIO; }
204206
#endif
205207

drivers/acpi/sleep.c

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -650,6 +650,8 @@ static const struct platform_suspend_ops acpi_suspend_ops_old = {
650650
.recover = acpi_pm_finish,
651651
};
652652

653+
static bool s2idle_wakeup;
654+
653655
static int acpi_freeze_begin(void)
654656
{
655657
acpi_scan_lock_acquire();
@@ -666,6 +668,33 @@ static int acpi_freeze_prepare(void)
666668
return 0;
667669
}
668670

671+
static void acpi_freeze_wake(void)
672+
{
673+
/*
674+
* If IRQD_WAKEUP_ARMED is not set for the SCI at this point, it means
675+
* that the SCI has triggered while suspended, so cancel the wakeup in
676+
* case it has not been a wakeup event (the GPEs will be checked later).
677+
*/
678+
if (acpi_sci_irq_valid() &&
679+
!irqd_is_wakeup_armed(irq_get_irq_data(acpi_sci_irq))) {
680+
pm_system_cancel_wakeup();
681+
s2idle_wakeup = true;
682+
}
683+
}
684+
685+
static void acpi_freeze_sync(void)
686+
{
687+
/*
688+
* Process all pending events in case there are any wakeup ones.
689+
*
690+
* The EC driver uses the system workqueue, so that one needs to be
691+
* flushed too.
692+
*/
693+
acpi_os_wait_events_complete();
694+
flush_scheduled_work();
695+
s2idle_wakeup = false;
696+
}
697+
669698
static void acpi_freeze_restore(void)
670699
{
671700
if (acpi_sci_irq_valid())
@@ -682,6 +711,8 @@ static void acpi_freeze_end(void)
682711
static const struct platform_freeze_ops acpi_freeze_ops = {
683712
.begin = acpi_freeze_begin,
684713
.prepare = acpi_freeze_prepare,
714+
.wake = acpi_freeze_wake,
715+
.sync = acpi_freeze_sync,
685716
.restore = acpi_freeze_restore,
686717
.end = acpi_freeze_end,
687718
};
@@ -700,9 +731,15 @@ static void acpi_sleep_suspend_setup(void)
700731
}
701732

702733
#else /* !CONFIG_SUSPEND */
734+
#define s2idle_wakeup (false)
703735
static inline void acpi_sleep_suspend_setup(void) {}
704736
#endif /* !CONFIG_SUSPEND */
705737

738+
bool acpi_s2idle_wakeup(void)
739+
{
740+
return s2idle_wakeup;
741+
}
742+
706743
#ifdef CONFIG_PM_SLEEP
707744
static u32 saved_bm_rld;
708745

drivers/base/power/main.c

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1095,11 +1095,6 @@ static int __device_suspend_noirq(struct device *dev, pm_message_t state, bool a
10951095
if (async_error)
10961096
goto Complete;
10971097

1098-
if (pm_wakeup_pending()) {
1099-
async_error = -EBUSY;
1100-
goto Complete;
1101-
}
1102-
11031098
if (dev->power.syscore || dev->power.direct_complete)
11041099
goto Complete;
11051100

drivers/base/power/wakeup.c

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ bool events_check_enabled __read_mostly;
2828
/* First wakeup IRQ seen by the kernel in the last cycle. */
2929
unsigned int pm_wakeup_irq __read_mostly;
3030

31-
/* If set and the system is suspending, terminate the suspend. */
32-
static bool pm_abort_suspend __read_mostly;
31+
/* If greater than 0 and the system is suspending, terminate the suspend. */
32+
static atomic_t pm_abort_suspend __read_mostly;
3333

3434
/*
3535
* Combined counters of registered wakeup events and wakeup events in progress.
@@ -855,20 +855,26 @@ bool pm_wakeup_pending(void)
855855
pm_print_active_wakeup_sources();
856856
}
857857

858-
return ret || pm_abort_suspend;
858+
return ret || atomic_read(&pm_abort_suspend) > 0;
859859
}
860860

861861
void pm_system_wakeup(void)
862862
{
863-
pm_abort_suspend = true;
863+
atomic_inc(&pm_abort_suspend);
864864
freeze_wake();
865865
}
866866
EXPORT_SYMBOL_GPL(pm_system_wakeup);
867867

868-
void pm_wakeup_clear(void)
868+
void pm_system_cancel_wakeup(void)
869+
{
870+
atomic_dec(&pm_abort_suspend);
871+
}
872+
873+
void pm_wakeup_clear(bool reset)
869874
{
870-
pm_abort_suspend = false;
871875
pm_wakeup_irq = 0;
876+
if (reset)
877+
atomic_set(&pm_abort_suspend, 0);
872878
}
873879

874880
void pm_system_irq_wakeup(unsigned int irq_number)

include/acpi/acpi_bus.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -598,15 +598,19 @@ static inline bool acpi_device_always_present(struct acpi_device *adev)
598598
#endif
599599

600600
#ifdef CONFIG_PM
601+
void acpi_pm_wakeup_event(struct device *dev);
601602
acpi_status acpi_add_pm_notifier(struct acpi_device *adev, struct device *dev,
602603
void (*func)(struct acpi_device_wakeup_context *context));
603604
acpi_status acpi_remove_pm_notifier(struct acpi_device *adev);
604605
int acpi_pm_device_sleep_state(struct device *, int *, int);
605606
int acpi_pm_device_run_wake(struct device *, bool);
606607
#else
608+
static inline void acpi_pm_wakeup_event(struct device *dev)
609+
{
610+
}
607611
static inline acpi_status acpi_add_pm_notifier(struct acpi_device *adev,
608612
struct device *dev,
609-
void (*work_func)(struct work_struct *work))
613+
void (*func)(struct acpi_device_wakeup_context *context))
610614
{
611615
return AE_SUPPORT;
612616
}

include/linux/suspend.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,8 @@ struct platform_suspend_ops {
189189
struct platform_freeze_ops {
190190
int (*begin)(void);
191191
int (*prepare)(void);
192+
void (*wake)(void);
193+
void (*sync)(void);
192194
void (*restore)(void);
193195
void (*end)(void);
194196
};
@@ -428,7 +430,8 @@ extern unsigned int pm_wakeup_irq;
428430

429431
extern bool pm_wakeup_pending(void);
430432
extern void pm_system_wakeup(void);
431-
extern void pm_wakeup_clear(void);
433+
extern void pm_system_cancel_wakeup(void);
434+
extern void pm_wakeup_clear(bool reset);
432435
extern void pm_system_irq_wakeup(unsigned int irq_number);
433436
extern bool pm_get_wakeup_count(unsigned int *count, bool block);
434437
extern bool pm_save_wakeup_count(unsigned int count);
@@ -478,7 +481,7 @@ static inline int unregister_pm_notifier(struct notifier_block *nb)
478481

479482
static inline bool pm_wakeup_pending(void) { return false; }
480483
static inline void pm_system_wakeup(void) {}
481-
static inline void pm_wakeup_clear(void) {}
484+
static inline void pm_wakeup_clear(bool reset) {}
482485
static inline void pm_system_irq_wakeup(unsigned int irq_number) {}
483486

484487
static inline void lock_system_sleep(void) {}

kernel/power/process.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ int freeze_processes(void)
132132
if (!pm_freezing)
133133
atomic_inc(&system_freezing_cnt);
134134

135-
pm_wakeup_clear();
135+
pm_wakeup_clear(true);
136136
pr_info("Freezing user space processes ... ");
137137
pm_freezing = true;
138138
error = try_to_freeze_tasks(true);

kernel/power/suspend.c

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ static void freeze_begin(void)
7272

7373
static void freeze_enter(void)
7474
{
75+
trace_suspend_resume(TPS("machine_suspend"), PM_SUSPEND_FREEZE, true);
76+
7577
spin_lock_irq(&suspend_freeze_lock);
7678
if (pm_wakeup_pending())
7779
goto out;
@@ -84,11 +86,9 @@ static void freeze_enter(void)
8486

8587
/* Push all the CPUs into the idle loop. */
8688
wake_up_all_idle_cpus();
87-
pr_debug("PM: suspend-to-idle\n");
8889
/* Make the current CPU wait so it can enter the idle loop too. */
8990
wait_event(suspend_freeze_wait_head,
9091
suspend_freeze_state == FREEZE_STATE_WAKE);
91-
pr_debug("PM: resume from suspend-to-idle\n");
9292

9393
cpuidle_pause();
9494
put_online_cpus();
@@ -98,6 +98,31 @@ static void freeze_enter(void)
9898
out:
9999
suspend_freeze_state = FREEZE_STATE_NONE;
100100
spin_unlock_irq(&suspend_freeze_lock);
101+
102+
trace_suspend_resume(TPS("machine_suspend"), PM_SUSPEND_FREEZE, false);
103+
}
104+
105+
static void s2idle_loop(void)
106+
{
107+
pr_debug("PM: suspend-to-idle\n");
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));
124+
125+
pr_debug("PM: resume from suspend-to-idle\n");
101126
}
102127

103128
void freeze_wake(void)
@@ -371,10 +396,8 @@ static int suspend_enter(suspend_state_t state, bool *wakeup)
371396
* all the devices are suspended.
372397
*/
373398
if (state == PM_SUSPEND_FREEZE) {
374-
trace_suspend_resume(TPS("machine_suspend"), state, true);
375-
freeze_enter();
376-
trace_suspend_resume(TPS("machine_suspend"), state, false);
377-
goto Platform_wake;
399+
s2idle_loop();
400+
goto Platform_early_resume;
378401
}
379402

380403
error = disable_nonboot_cpus();

0 commit comments

Comments
 (0)