ACPI: button: Report wakeup key only for power button wakeups
Subsystems (2)
PATCH 1/1 ACPI: button: Report wakeup key only for power button wakeups
Commit 16f70feaabe9 ("ACPI: button: trigger wakeup key events") makes the ACPI power button driver report KEY_WAKEUP from its resume callback. However, that callback is run whenever the ACPI button device is resumed, regardless of the actual system wakeup source.
As a result, userspace may receive a KEY_WAKEUP event after resumes caused by unrelated wakeup sources.
Avoid reporting the input event from acpi_button_resume(). Instead, report it only when the ACPI fixed power button status indicates that the power button was the wakeup source.
--- drivers/acpi/button.c | 23 +++++++++++++++-------- drivers/acpi/sleep.c | 21 +++++++++++++++++++++ drivers/acpi/sleep.h | 1 + drivers/acpi/x86/s2idle.c | 4 +++- include/acpi/button.h | 5 +++++ 5 files changed, 45 insertions(+), 9 deletions(-)
drivers/acpi/button.c +15 -8
@@ -480,6 +480,21 @@ static void acpi_button_notify(acpi_handle handle, u32 event, void *data) event, ++button->pushed); } +void acpi_power_button_wakeup (struct acpi_device *device) +{ +struct acpi_button *button =acpi_driver_data (device); +struct input_dev *input; + +if (button->type ==ACPI_BUTTON_TYPE_POWER ) { + input = button->input; +input_report_key (input,KEY_WAKEUP ,1 ); +input_sync (input); +input_report_key (input,KEY_WAKEUP ,0 ); +input_sync (input); + } +} +EXPORT_SYMBOL (acpi_power_button_wakeup); +static void acpi_button_notify_run (void *data) {acpi_button_notify (NULL ,ACPI_BUTTON_NOTIFY_STATUS , data);
@@ -503,7 +518,6 @@ static int acpi_button_suspend(struct device *dev)static int acpi_button_resume (struct device *dev) {struct acpi_button *button =dev_get_drvdata (dev); -struct input_dev *input; button->suspended = false;if (button->type ==ACPI_BUTTON_TYPE_LID ) {
@@ -512,13 +526,6 @@ static int acpi_button_resume(struct device *dev) acpi_lid_initialize_state(button); } -if (button->type ==ACPI_BUTTON_TYPE_POWER ) { - input = button->input; -input_report_key (input,KEY_WAKEUP ,1 ); -input_sync (input); -input_report_key (input,KEY_WAKEUP ,0 ); -input_sync (input); - }return 0 ; }#endif
drivers/acpi/sleep.c +21 -0
@@ -22,6 +22,7 @@#include <linux/syscore_ops.h> #include <asm/io.h> #include <trace/events/power.h> +#include <acpi/button.h> #include "internal.h" #include "sleep.h"
@@ -518,6 +519,7 @@ static void acpi_pm_finish(void) NULL, -1);if (pwr_btn_adev) {pm_wakeup_event (&pwr_btn_adev->dev,0 ); +acpi_power_button_wakeup (pwr_btn_adev);acpi_dev_put (pwr_btn_adev); } }
@@ -818,6 +820,24 @@ bool acpi_s2idle_wake(void)return false; } +void acpi_s2idle_restore_check_powerkey (void ) +{ +struct acpi_device *pwr_btn_adev; +acpi_event_status pwr_btn_status =ACPI_EVENT_FLAG_DISABLED ; + +acpi_get_event_status (ACPI_EVENT_POWER_BUTTON , &pwr_btn_status); + +if (pwr_btn_status &ACPI_EVENT_FLAG_STATUS_SET ) { + pwr_btn_adev =acpi_dev_get_first_match_dev (ACPI_BUTTON_HID_POWERF , +NULL ,-1 ); +if (pwr_btn_adev) { +pm_wakeup_event (&pwr_btn_adev->dev,0 ); +acpi_power_button_wakeup (pwr_btn_adev); +acpi_dev_put (pwr_btn_adev); + } + } +} +void acpi_s2idle_restore (void ) {/*
@@ -849,6 +869,7 @@ static const struct platform_s2idle_ops acpi_s2idle_ops = { .begin = acpi_s2idle_begin, .prepare = acpi_s2idle_prepare, .wake = acpi_s2idle_wake, + .restore_early = acpi_s2idle_restore_check_powerkey, .restore = acpi_s2idle_restore, .end = acpi_s2idle_end, };
drivers/acpi/sleep.h +1 -0
@@ -18,6 +18,7 @@ static inline acpi_status acpi_set_waking_vector(u32 wakeup_address)extern int acpi_s2idle_begin (void );extern int acpi_s2idle_prepare (void );extern bool acpi_s2idle_wake (void ); +extern void acpi_s2idle_restore_check_powerkey (void );extern void acpi_s2idle_restore (void );extern void acpi_s2idle_end (void );
drivers/acpi/x86/s2idle.c +3 -1
@@ -598,8 +598,10 @@ static void acpi_s2idle_restore_early_lps0(void) {struct acpi_s2idle_dev_ops *handler; -if (!lps0_device_handle || sleep_no_lps0) +if (!lps0_device_handle || sleep_no_lps0) { +acpi_s2idle_restore_check_powerkey ();return ; + }list_for_each_entry (handler, &lps0_s2idle_devops_head, list_node)if (handler->restore)
include/acpi/button.h +5 -0
@@ -8,11 +8,16 @@#if IS_ENABLED (CONFIG_ACPI_BUTTON )extern int acpi_lid_open (void ); +extern void acpi_power_button_wakeup (struct acpi_device *device);#else static inline int acpi_lid_open (void ) {return 1 ; } + +static inline void acpi_power_button_wakeup (struct acpi_device *device) +{ +}#endif /* IS_ENABLED(CONFIG_ACPI_BUTTON) */ #endif /* ACPI_BUTTON_H */
base-commit: 4d823c9d06aaa91476b58e56e4d44c4112da2811
Signature · 1 line
2.34.1
Rafael J. Wysocki (Intel)
maintainerQuoted 8 lines · Baorui Liu
Commit
16f70feaabe9("ACPI: button: trigger wakeup key events") makes the ACPI power button driver reportKEY_WAKEUPfrom its resume callback. However, that callback is run whenever the ACPI button device is resumed, regardless of the actual system wakeup source.As a result, userspace may receive a
KEY_WAKEUPevent after resumes caused by unrelated wakeup sources.
So why is this a problem in practice?
Quoted 3 lines
Avoid reporting the input event from
acpi_button_resume(). Instead, report it only when the ACPI fixed power button status indicates that the power button was the wakeup source.
But power buttons need not be fixed event devices and there are also sleep buttons that can generate KEY_WAKEUP, and what about lids?
Quoted 165 lines
Fixes 16f70feaabe9 ("ACPI: button: trigger wakeup key events")Signed-off-by Baorui Liu <[email protected]>--- drivers/acpi/button.c | 23 +++++++++++++++-------- drivers/acpi/sleep.c | 21 +++++++++++++++++++++ drivers/acpi/sleep.h | 1 + drivers/acpi/x86/s2idle.c | 4 +++- include/acpi/button.h | 5 +++++ 5 files changed, 45 insertions(+), 9 deletions(-)drivers/acpi/button.c +15
-8@@ -480,6 +480,21 @@ static void acpi_button_notify(acpi_handle handle, u32 event, void *data) event, ++button->pushed); } +void acpi_power_button_wakeup (struct acpi_device *device) +{ +struct acpi_button *button =acpi_driver_data (device); +struct input_dev *input; + +if (button->type ==ACPI_BUTTON_TYPE_POWER ) { + input = button->input; +input_report_key (input,KEY_WAKEUP ,1 ); +input_sync (input); +input_report_key (input,KEY_WAKEUP ,0 ); +input_sync (input); + } +} +EXPORT_SYMBOL (acpi_power_button_wakeup); +static void acpi_button_notify_run (void *data) {acpi_button_notify (NULL ,ACPI_BUTTON_NOTIFY_STATUS , data);@@ -503,7 +518,6 @@ static int acpi_button_suspend(struct device *dev)static int acpi_button_resume (struct device *dev) {struct acpi_button *button =dev_get_drvdata (dev); -struct input_dev *input; button->suspended = false;if (button->type ==ACPI_BUTTON_TYPE_LID ) {@@ -512,13 +526,6 @@ static int acpi_button_resume(struct device *dev) acpi_lid_initialize_state(button); } -if (button->type ==ACPI_BUTTON_TYPE_POWER ) { - input = button->input; -input_report_key (input,KEY_WAKEUP ,1 ); -input_sync (input); -input_report_key (input,KEY_WAKEUP ,0 ); -input_sync (input); - }return 0 ; }#endif drivers/acpi/sleep.c +21
-0@@ -22,6 +22,7 @@#include <linux/syscore_ops.h> #include <asm/io.h> #include <trace/events/power.h> +#include <acpi/button.h> #include "internal.h" #include "sleep.h" @@ -518,6 +519,7 @@ static void acpi_pm_finish(void) NULL, -1);if (pwr_btn_adev) {pm_wakeup_event (&pwr_btn_adev->dev,0 ); +acpi_power_button_wakeup (pwr_btn_adev);acpi_dev_put (pwr_btn_adev); } }@@ -818,6 +820,24 @@ bool acpi_s2idle_wake(void)return false; } +void acpi_s2idle_restore_check_powerkey (void ) +{ +struct acpi_device *pwr_btn_adev; +acpi_event_status pwr_btn_status =ACPI_EVENT_FLAG_DISABLED ; + +acpi_get_event_status (ACPI_EVENT_POWER_BUTTON , &pwr_btn_status); + +if (pwr_btn_status &ACPI_EVENT_FLAG_STATUS_SET ) { + pwr_btn_adev =acpi_dev_get_first_match_dev (ACPI_BUTTON_HID_POWERF , +NULL ,-1 ); +if (pwr_btn_adev) { +pm_wakeup_event (&pwr_btn_adev->dev,0 ); +acpi_power_button_wakeup (pwr_btn_adev); +acpi_dev_put (pwr_btn_adev); + } + } +} +void acpi_s2idle_restore (void ) {/* @@ -849,6 +869,7 @@ static const struct platform_s2idle_ops acpi_s2idle_ops = { .begin = acpi_s2idle_begin, .prepare = acpi_s2idle_prepare, .wake = acpi_s2idle_wake, + .restore_early = acpi_s2idle_restore_check_powerkey, .restore = acpi_s2idle_restore, .end = acpi_s2idle_end, };drivers/acpi/sleep.h +1
-0@@ -18,6 +18,7 @@ static inline acpi_status acpi_set_waking_vector(u32 wakeup_address)extern int acpi_s2idle_begin (void );extern int acpi_s2idle_prepare (void );extern bool acpi_s2idle_wake (void ); +extern void acpi_s2idle_restore_check_powerkey (void );extern void acpi_s2idle_restore (void );extern void acpi_s2idle_end (void );drivers/acpi/x86/s2idle.c +3
-1@@ -598,8 +598,10 @@ static void acpi_s2idle_restore_early_lps0(void) {struct acpi_s2idle_dev_ops *handler; -if (!lps0_device_handle || sleep_no_lps0) +if (!lps0_device_handle || sleep_no_lps0) { +acpi_s2idle_restore_check_powerkey ();return ; + }list_for_each_entry (handler, &lps0_s2idle_devops_head, list_node)if (handler->restore)include/acpi/button.h +5
-0@@ -8,11 +8,16 @@#if IS_ENABLED (CONFIG_ACPI_BUTTON )extern int acpi_lid_open (void ); +extern void acpi_power_button_wakeup (struct acpi_device *device);#else static inline int acpi_lid_open (void ) {return 1 ; } + +static inline void acpi_power_button_wakeup (struct acpi_device *device) +{ +}#endif /* IS_ENABLED(CONFIG_ACPI_BUTTON) */ #endif /* ACPI_BUTTON_H */ base-commit:
4d823c9d06aaa91476b58e56e4d44c4112da2811--
2.34.1
v2 cover ACPI: button: Report wakeup key only for power button wakeups
In reply to Baorui LiuThis v2 addresses the review feedback on v1 by documenting the practical impact and by avoiding KEY_WAKEUP reporting from generic ACPI sleep resume paths.
Problem reproduced on an AMD Android 15 Xen guest.
Input device mapping:
event0: LNXPWRBN Power Button, KEY_POWER + KEY_WAKEUP
event1: LNXSLPBN Sleep Button, KEY_SLEEP
Runtime trigger mapping:
xl trigger android power -> event0 KEY_POWER
xl trigger android sleep -> event1 KEY_SLEEP
without-fix kernel:
6.6.118-android15-14-maybe-dirty-gc3c82a22502d
echo mem > /sys/power/state
xl trigger android s3resume
/dev/input/event0 reports KEY_WAKEUP DOWN/UP.
with-fix v2 kernel:
6.6.118-android15-14-maybe-dirty-g5b231ece911d
The same non-power-button S3 resume path no longer reports KEY_WAKEUP from /dev/input/event0. This was verified twice; both event0 logs are empty.
The dmesg log confirms that the test path is S3 suspend/resume:
PM: suspend entry (deep)
ACPI: PM: Preparing to enter system sleep state S3
ACPI: PM: Waking up from system sleep state S3 Resume cause unknown
PM: suspend exit
A positive-control power-button S3 wake was attempted in the Xen guest:
echo mem > /sys/power/state
xl trigger android power
The guest remained suspended/offline and was recovered with:
xl trigger android s3resume
Therefore, xl trigger android power is not a reliable S3 wake source in this Xen setup. That run is not used as positive-control power-button wake evidence; it only confirms runtime input-device mapping.
Changes in v2:
- Add test evidence showing the practical impact.
- Track pending wakeup reporting in the ACPI button driver.
- Report KEY_WAKEUP only if a power-button event/notify is observed while the ACPI button device is suspended.
- Stop synthesizing Power Button KEY_WAKEUP events from generic ACPI sleep resume paths.
ACPI: button: Report wakeup key only for power button wakeups
drivers/acpi/button.c | 38 ++++++++++++++++------------- drivers/acpi/sleep.c | 56 +++---------------------------------------- include/acpi/button.h | 5 ---- 3 files changed, 24 insertions(+), 75 deletions(-)
Signature · 1 line
2.34.1
PATCH v2 1/1 ACPI: button: Report wakeup key only for power button wakeups
The ACPI button driver reports KEY_WAKEUP from the Power Button input device to let userspace know that the system was resumed by a power button wakeup.
However, reporting KEY_WAKEUP from generic system resume paths can make userspace observe a Power Button wakeup even when the system was resumed by a different wake source.
This is reproducible on an AMD Android 15 Xen guest. With a kernel without this fix, a non-power-button S3 resume:
echo mem > /sys/power/state
xl trigger android s3resume
makes the Power Button input device report KEY_WAKEUP. The same test on a kernel with this fix no longer reports KEY_WAKEUP from the Power Button input device.
Track whether a power button event/notify is observed while the ACPI button device is suspended, and report KEY_WAKEUP on resume only in that case. Do not synthesize a Power Button input event from generic ACPI sleep resume code.
--- drivers/acpi/button.c | 38 ++++++++++++++++------------- drivers/acpi/sleep.c | 56 +++---------------------------------------- include/acpi/button.h | 5 ---- 3 files changed, 24 insertions(+), 75 deletions(-)
drivers/acpi/button.c +21 -17
@@ -175,6 +175,7 @@ struct acpi_button {int last_state;ktime_t last_time;bool suspended; +bool wakeup_pending;bool lid_state_initialized; };
@@ -452,6 +453,9 @@ static void acpi_button_notify(acpi_handle handle, u32 event, void *data)acpi_pm_wakeup_event (&device->dev); button =acpi_driver_data (device); +if (button->type ==ACPI_BUTTON_TYPE_POWER && button->suspended) + button->wakeup_pending = true; +if (button->suspended)return ;
@@ -468,23 +472,6 @@ static void acpi_button_notify(acpi_handle handle, u32 event, void *data) event, ++button->pushed); } -void acpi_power_button_wakeup (struct acpi_device *device) -{ -struct acpi_button *button =acpi_driver_data (device); -struct input_dev *input; - -if (button->type ==ACPI_BUTTON_TYPE_POWER ) { - input = button->input; -input_report_key (input,KEY_WAKEUP ,1 ); -input_sync (input); -input_report_key (input,KEY_WAKEUP ,0 ); -input_sync (input); - } - -return ; -} -EXPORT_SYMBOL (acpi_power_button_wakeup); -static void acpi_button_notify_run (void *data) {acpi_button_notify (NULL ,ACPI_BUTTON_NOTIFY_STATUS , data);
@@ -492,6 +479,12 @@ static void acpi_button_notify_run(void *data)static u32 acpi_button_event (void *data) { +struct acpi_device *device = data; +struct acpi_button *button =acpi_driver_data (device); + +if (button->type ==ACPI_BUTTON_TYPE_POWER && button->suspended) + button->wakeup_pending = true; +acpi_os_execute (OSL_NOTIFY_HANDLER , acpi_button_notify_run, data);return ACPI_INTERRUPT_HANDLED ; }
@@ -503,11 +496,13 @@ static int acpi_button_suspend(struct device *dev)struct acpi_button *button =acpi_driver_data (device); button->suspended = true; + button->wakeup_pending = false;return 0 ; }static int acpi_button_resume (struct device *dev) { +struct input_dev *input;struct acpi_device *device =to_acpi_device (dev);struct acpi_button *button =acpi_driver_data (device);
@@ -518,6 +513,15 @@ static int acpi_button_resume(struct device *dev) acpi_lid_initialize_state(device); } +if (button->type ==ACPI_BUTTON_TYPE_POWER && button->wakeup_pending) { + input = button->input; +input_report_key (input,KEY_WAKEUP ,1 ); +input_sync (input); +input_report_key (input,KEY_WAKEUP ,0 ); +input_sync (input); + button->wakeup_pending = false; + } +return 0 ; }#endif
drivers/acpi/sleep.c +3 -53
95 lines not on this page · raw message
include/acpi/button.h +0 -5
@@ -8,16 +8,11 @@#if IS_ENABLED (CONFIG_ACPI_BUTTON )extern int acpi_lid_open (void ); -extern void acpi_power_button_wakeup (struct acpi_device *device);#else static inline int acpi_lid_open (void ) {return 1 ; } -static inline void acpi_power_button_wakeup (struct acpi_device *device) -{ -return ; -}#endif /* IS_ENABLED(CONFIG_ACPI_BUTTON) */ #endif /* ACPI_BUTTON_H */
Signature · 1 line
2.34.1
Rafael J. Wysocki (Intel)
maintainerQuoted 19 lines · Baorui.Liu
The ACPI button driver reports
KEY_WAKEUPfrom the Power Button input device to let userspace know that the system was resumed by a power button wakeup.However, reporting
KEY_WAKEUPfrom generic system resume paths can make userspace observe a Power Button wakeup even when the system was resumed by a different wake source.This is reproducible on an AMD Android 15 Xen guest. With a kernel without this fix, a non-power-button S3 resume:
echo mem >
/sys/power/state
xl trigger android s3resumemakes the Power Button input device report
KEY_WAKEUP. The same test on a kernel with this fix no longer reportsKEY_WAKEUPfrom the Power Button input device.
Quoted 6 lines
Track whether a power button event/notify is observed while the ACPI button device is suspended, and report
KEY_WAKEUPon resume only in that case. Do not synthesize a Power Button input event from generic ACPI sleep resume code.Signed-off-by Baorui.Liu <[email protected]>
This doesn't apply on top of 7.3-rc2 for me, so please rebase and resend it.
When you resend it, don't add a cover letter (cover letters are only needed for patch series, not for individual patches). If there is information in the cover letter that is not covered by the patch changelog, it can be copied there.
Thanks!
Quoted 224 lines
--- drivers/acpi/button.c | 38 ++++++++++++++++------------- drivers/acpi/sleep.c | 56 +++---------------------------------------- include/acpi/button.h | 5 ---- 3 files changed, 24 insertions(+), 75 deletions(-)drivers/acpi/button.c +21
-17@@ -175,6 +175,7 @@ struct acpi_button {int last_state;ktime_t last_time;bool suspended; +bool wakeup_pending;bool lid_state_initialized; };@@ -452,6 +453,9 @@ static void acpi_button_notify(acpi_handle handle, u32 event, void *data)acpi_pm_wakeup_event (&device->dev); button =acpi_driver_data (device); +if (button->type ==ACPI_BUTTON_TYPE_POWER && button->suspended) + button->wakeup_pending = true; +if (button->suspended)return ;@@ -468,23 +472,6 @@ static void acpi_button_notify(acpi_handle handle, u32 event, void *data) event, ++button->pushed); } -void acpi_power_button_wakeup (struct acpi_device *device) -{ -struct acpi_button *button =acpi_driver_data (device); -struct input_dev *input; - -if (button->type ==ACPI_BUTTON_TYPE_POWER ) { - input = button->input; -input_report_key (input,KEY_WAKEUP ,1 ); -input_sync (input); -input_report_key (input,KEY_WAKEUP ,0 ); -input_sync (input); - } - -return ; -} -EXPORT_SYMBOL (acpi_power_button_wakeup); -static void acpi_button_notify_run (void *data) {acpi_button_notify (NULL ,ACPI_BUTTON_NOTIFY_STATUS , data);@@ -492,6 +479,12 @@ static void acpi_button_notify_run(void *data)static u32 acpi_button_event (void *data) { +struct acpi_device *device = data; +struct acpi_button *button =acpi_driver_data (device); + +if (button->type ==ACPI_BUTTON_TYPE_POWER && button->suspended) + button->wakeup_pending = true; +acpi_os_execute (OSL_NOTIFY_HANDLER , acpi_button_notify_run, data);return ACPI_INTERRUPT_HANDLED ; }@@ -503,11 +496,13 @@ static int acpi_button_suspend(struct device *dev)struct acpi_button *button =acpi_driver_data (device); button->suspended = true; + button->wakeup_pending = false;return 0 ; }static int acpi_button_resume (struct device *dev) { +struct input_dev *input;struct acpi_device *device =to_acpi_device (dev);struct acpi_button *button =acpi_driver_data (device);@@ -518,6 +513,15 @@ static int acpi_button_resume(struct device *dev) acpi_lid_initialize_state(device); } +if (button->type ==ACPI_BUTTON_TYPE_POWER && button->wakeup_pending) { + input = button->input; +input_report_key (input,KEY_WAKEUP ,1 ); +input_sync (input); +input_report_key (input,KEY_WAKEUP ,0 ); +input_sync (input); + button->wakeup_pending = false; + } +return 0 ; }#endif drivers/acpi/sleep.c +3
-53@@ -22,7 +22,6 @@#include <linux/syscore_ops.h> #include <asm/io.h> #include <trace/events/power.h> -#include <acpi/button.h> #include "internal.h" #include "sleep.h" @@ -105,8 +104,6 @@ u32 acpi_target_system_state(void) }EXPORT_SYMBOL_GPL (acpi_target_system_state); -static bool pwr_btn_event_pending; -/* * The ACPI specification wants us to save NVS memory regions during hibernation * and to restore them during the subsequent resume. Windows does that also for @@ -463,7 +460,6 @@ static int acpi_pm_prepare(void)*/ static void acpi_pm_finish (void ) { -struct acpi_device *pwr_btn_adev;u32 acpi_state = acpi_target_sleep_state;acpi_ec_unblock_transactions ();@@ -482,24 +478,6 @@ static void acpi_pm_finish(void) acpi_target_sleep_state =ACPI_STATE_S0 ;acpi_resume_power_resources (); - -/* If we were woken with the fixed power button, provide a small -* hint to userspace in the form of a wakeup event on the fixed power -* button device (if it can be found). -* -* We delay the event generation til now, as the PM layer requires -* timekeeping to be running before we generate events. */ -if (!pwr_btn_event_pending) -return ; - - pwr_btn_event_pending = false; - pwr_btn_adev =acpi_dev_get_first_match_dev (ACPI_BUTTON_HID_POWERF , -NULL ,-1 ); -if (pwr_btn_adev) { -pm_wakeup_event (&pwr_btn_adev->dev,0 ); -acpi_power_button_wakeup (pwr_btn_adev); -acpi_dev_put (pwr_btn_adev); - } }/** @@ -604,27 +582,6 @@ static int acpi_suspend_enter(suspend_state_t pm_state)/* Reprogram control registers */ acpi_leave_sleep_state_prep (acpi_state); -/* ACPI 3.0 specs (P62) says that it's the responsibility -* of the OSPM to clear the status bit [ implying that the -* POWER_BUTTON event should not reach userspace ] -* -* However, we do generate a small hint for userspace in the form of -* a wakeup event. We flag this condition for now and generate the -* event later, as we're currently too early in resume to be able to -* generate wakeup events. -*/ -if (ACPI_SUCCESS (status) && (acpi_state ==ACPI_STATE_S3 )) { -acpi_event_status pwr_btn_status =ACPI_EVENT_FLAG_DISABLED ; - -acpi_get_event_status (ACPI_EVENT_POWER_BUTTON , &pwr_btn_status); - -if (pwr_btn_status &ACPI_EVENT_FLAG_STATUS_SET ) { -acpi_clear_event (ACPI_EVENT_POWER_BUTTON ); -/* Flag for later */ - pwr_btn_event_pending = true; - } - } -/* * Disable all GPE and clear their status bits before interrupts are * enabled. Some GPEs (like wakeup GPEs) have no handlers and this can @@ -797,19 +754,12 @@ bool acpi_s2idle_wake(void)void acpi_s2idle_restore_check_powerkey (void ) { -struct acpi_device *pwr_btn_adev;acpi_event_status pwr_btn_status =ACPI_EVENT_FLAG_DISABLED ; +acpi_get_event_status (ACPI_EVENT_POWER_BUTTON , &pwr_btn_status); -if (pwr_btn_status &ACPI_EVENT_FLAG_STATUS_SET ) { - pwr_btn_adev =acpi_dev_get_first_match_dev (ACPI_BUTTON_HID_POWERF , -NULL ,-1 ); -if (pwr_btn_adev) { -pm_wakeup_event (&pwr_btn_adev->dev,0 ); -acpi_power_button_wakeup (pwr_btn_adev); -acpi_dev_put (pwr_btn_adev); - } - } +if (pwr_btn_status &ACPI_EVENT_FLAG_STATUS_SET ) +acpi_clear_event (ACPI_EVENT_POWER_BUTTON ); }void acpi_s2idle_restore (void )include/acpi/button.h +0
-6@@ -8,16 +8,11 @@#if IS_ENABLED (CONFIG_ACPI_BUTTON )extern int acpi_lid_open (void ); -extern void acpi_power_button_wakeup (struct acpi_device *device);#else static inline int acpi_lid_open (void ) {return 1 ; } -static inline void acpi_power_button_wakeup (struct acpi_device *device) -{ -return ; -}#endif /* IS_ENABLED(CONFIG_ACPI_BUTTON) */ #endif /* ACPI_BUTTON_H */ --2.34.1
PATCH v3 1/1 ACPI: button: Report wakeup key only for power button wakeups
In reply to Baorui.LiuThe ACPI button driver reports KEY_WAKEUP from the Power Button input device to let userspace know that the system was resumed by a power button wakeup.
However, reporting KEY_WAKEUP from generic system resume paths can make userspace observe a Power Button wakeup even when the system was resumed by a different wake source.
This is reproducible on an AMD Android 15 Xen guest. With a kernel without this fix, a non-power-button S3 resume:
echo mem > /sys/power/state
xl trigger android s3resume
makes the Power Button input device report KEY_WAKEUP. The same test on a kernel with this fix no longer reports KEY_WAKEUP from the Power Button input device.
Track whether a power button event/notify is observed while the ACPI button device is suspended, and report KEY_WAKEUP on resume only in that case. Do not synthesize a Power Button input event from generic ACPI sleep resume code.
--- drivers/acpi/button.c | 13 ++++++++++++- drivers/acpi/sleep.c | 41 ----------------------------------------- 2 files changed, 12 insertions(+), 42 deletions(-)
drivers/acpi/button.c +12 -1
@@ -191,6 +191,7 @@ struct acpi_button {bool last_state;ktime_t last_time;bool suspended; +bool wakeup_pending;bool lid_state_initialized;bool gpe_enabled; };
@@ -476,6 +477,9 @@ static void acpi_button_notify(acpi_handle handle, u32 event, void *data)acpi_pm_wakeup_event (button->dev); +if (button->type ==ACPI_BUTTON_TYPE_POWER && button->suspended) + button->wakeup_pending = true; +if (button->suspended || event ==ACPI_BUTTON_NOTIFY_WAKE )return ;
@@ -498,6 +502,11 @@ static void acpi_button_notify_run(void *data)static u32 acpi_button_event (void *data) { +struct acpi_button *button = data; + +if (button->type ==ACPI_BUTTON_TYPE_POWER && button->suspended) + button->wakeup_pending = true; +acpi_os_execute (OSL_NOTIFY_HANDLER , acpi_button_notify_run, data);return ACPI_INTERRUPT_HANDLED ; }
@@ -508,6 +517,7 @@ static int acpi_button_suspend(struct device *dev)struct acpi_button *button =dev_get_drvdata (dev); button->suspended = true; + button->wakeup_pending = false;return 0 ; }
@@ -523,12 +533,13 @@ static int acpi_button_resume(struct device *dev)acpi_lid_initialize_state (button); } -if (button->type ==ACPI_BUTTON_TYPE_POWER ) { +if (button->type ==ACPI_BUTTON_TYPE_POWER && button->wakeup_pending) { input = button->input;input_report_key (input,KEY_WAKEUP ,1 );input_sync (input);input_report_key (input,KEY_WAKEUP ,0 );input_sync (input); + button->wakeup_pending = false; }return 0 ; }
drivers/acpi/sleep.c +0 -41
65 lines not on this page · raw message
Signature · 1 line
2.34.1
Rafael J. Wysocki (Intel)
maintainerQuoted 19 lines · Baorui.Liu
The ACPI button driver reports
KEY_WAKEUPfrom the Power Button input device to let userspace know that the system was resumed by a power button wakeup.However, reporting
KEY_WAKEUPfrom generic system resume paths can make userspace observe a Power Button wakeup even when the system was resumed by a different wake source.This is reproducible on an AMD Android 15 Xen guest. With a kernel without this fix, a non-power-button S3 resume:
echo mem >
/sys/power/state
xl trigger android s3resumemakes the Power Button input device report
KEY_WAKEUP. The same test on a kernel with this fix no longer reportsKEY_WAKEUPfrom the Power Button input device.
Quoted 6 lines
Track whether a power button event/notify is observed while the ACPI button device is suspended, and report
KEY_WAKEUPon resume only in that case. Do not synthesize a Power Button input event from generic ACPI sleep resume code.Signed-off-by Baorui.Liu <[email protected]>
Please see
https://sashiko.dev/#/patchset/20260917143116.445-1-baorliu%40amd.com
and tell me what you think.
Thanks!
Quoted 138 lines
--- drivers/acpi/button.c | 13 ++++++++++++- drivers/acpi/sleep.c | 41 ----------------------------------------- 2 files changed, 12 insertions(+), 42 deletions(-)drivers/acpi/button.c +12
-1@@ -191,6 +191,7 @@ struct acpi_button {bool last_state;ktime_t last_time;bool suspended; +bool wakeup_pending;bool lid_state_initialized;bool gpe_enabled; };@@ -476,6 +477,9 @@ static void acpi_button_notify(acpi_handle handle, u32 event, void *data)acpi_pm_wakeup_event (button->dev); +if (button->type ==ACPI_BUTTON_TYPE_POWER && button->suspended) + button->wakeup_pending = true; +if (button->suspended || event ==ACPI_BUTTON_NOTIFY_WAKE )return ;@@ -498,6 +502,11 @@ static void acpi_button_notify_run(void *data)static u32 acpi_button_event (void *data) { +struct acpi_button *button = data; + +if (button->type ==ACPI_BUTTON_TYPE_POWER && button->suspended) + button->wakeup_pending = true; +acpi_os_execute (OSL_NOTIFY_HANDLER , acpi_button_notify_run, data);return ACPI_INTERRUPT_HANDLED ; }@@ -508,6 +517,7 @@ static int acpi_button_suspend(struct device *dev)struct acpi_button *button =dev_get_drvdata (dev); button->suspended = true; + button->wakeup_pending = false;return 0 ; }@@ -523,12 +533,13 @@ static int acpi_button_resume(struct device *dev)acpi_lid_initialize_state (button); } -if (button->type ==ACPI_BUTTON_TYPE_POWER ) { +if (button->type ==ACPI_BUTTON_TYPE_POWER && button->wakeup_pending) { input = button->input;input_report_key (input,KEY_WAKEUP ,1 );input_sync (input);input_report_key (input,KEY_WAKEUP ,0 );input_sync (input); + button->wakeup_pending = false; }return 0 ; }drivers/acpi/sleep.c +0
-42@@ -104,8 +104,6 @@ u32 acpi_target_system_state(void) }EXPORT_SYMBOL_GPL (acpi_target_system_state); -static bool pwr_btn_event_pending; -/* * The ACPI specification wants us to save NVS memory regions during hibernation * and to restore them during the subsequent resume. Windows does that also for @@ -484,7 +482,6 @@ static int acpi_pm_prepare(void)*/ static void acpi_pm_finish (void ) { -struct acpi_device *pwr_btn_adev;u32 acpi_state = acpi_target_sleep_state;acpi_ec_unblock_transactions ();@@ -503,23 +500,6 @@ static void acpi_pm_finish(void) acpi_target_sleep_state =ACPI_STATE_S0 ;acpi_resume_power_resources (); - -/* If we were woken with the fixed power button, provide a small -* hint to userspace in the form of a wakeup event on the fixed power -* button device (if it can be found). -* -* We delay the event generation til now, as the PM layer requires -* timekeeping to be running before we generate events. */ -if (!pwr_btn_event_pending) -return ; - - pwr_btn_event_pending = false; - pwr_btn_adev =acpi_dev_get_first_match_dev (ACPI_BUTTON_HID_POWERF , -NULL ,-1 ); -if (pwr_btn_adev) { -pm_wakeup_event (&pwr_btn_adev->dev,0 ); -acpi_dev_put (pwr_btn_adev); - } }/** @@ -626,27 +606,6 @@ static int acpi_suspend_enter(suspend_state_t pm_state)/* Reprogram control registers */ acpi_leave_sleep_state_prep (acpi_state); -/* ACPI 3.0 specs (P62) says that it's the responsibility -* of the OSPM to clear the status bit [ implying that the -* POWER_BUTTON event should not reach userspace ] -* -* However, we do generate a small hint for userspace in the form of -* a wakeup event. We flag this condition for now and generate the -* event later, as we're currently too early in resume to be able to -* generate wakeup events. -*/ -if (ACPI_SUCCESS (status) && (acpi_state ==ACPI_STATE_S3 )) { -acpi_event_status pwr_btn_status =ACPI_EVENT_FLAG_DISABLED ; - -acpi_get_event_status (ACPI_EVENT_POWER_BUTTON , &pwr_btn_status); - -if (pwr_btn_status &ACPI_EVENT_FLAG_STATUS_SET ) { -acpi_clear_event (ACPI_EVENT_POWER_BUTTON ); -/* Flag for later */ - pwr_btn_event_pending = true; - } - } -/* * Disable all GPE and clear their status bits before interrupts are * enabled. Some GPEs (like wakeup GPEs) have no handlers and this can -- 2.34.1
Hi Rafael,
I reviewed the Sashiko findings and agree with both of them.
1) High: race in acpi_button_suspend()
Clearing wakeup_pending after setting suspended=true can lose a power-button SCI that arrives between the two stores. I will clear wakeup_pending before setting suspended.
2) Medium: removing acpi_clear_event(POWER_BUTTON)
Dropping the status-bit clear can allow a deferred STATUS notify to report KEY_POWER after resume has cleared suspended, which may confuse userspace. I will keep clearing the fixed power-button status bit, mark wakeup_pending so button resume can still report KEY_WAKEUP, and keep the PM wakeup event on the fixed power-button device. KEY_WAKEUP is still not synthesized from the generic ACPI sleep path for non-power wakes.
I will send a v4 with these updates.
Thanks,
Baorui
PATCH v4 1/1 ACPI: button: Report wakeup key only for power button wakeups
In reply to Baorui.LiuThe ACPI button driver reports KEY_WAKEUP from the Power Button input device to let userspace know that the system was resumed by a power button wakeup.
However, reporting KEY_WAKEUP from generic system resume paths can make userspace observe a Power Button wakeup even when the system was resumed by a different wake source.
This is reproducible on an AMD Android 15 Xen guest. With a kernel without this fix, a non-power-button S3 resume:
echo mem > /sys/power/state
xl trigger android s3resume
makes the Power Button input device report KEY_WAKEUP. The same test on a kernel with this fix no longer reports KEY_WAKEUP from the Power Button input device.
Track whether a power button event/notify is observed while the ACPI button device is suspended, and report KEY_WAKEUP on resume only in that case.
When the fixed power-button status bit is set during early S3 resume, clear it to avoid a deferred KEY_POWER delivery to userspace, and mark wakeup_pending so button resume can still report KEY_WAKEUP. Also clear wakeup_pending before setting suspended to avoid a race with a power button SCI during suspend entry.
--- drivers/acpi/button.c | 46 ++++++++++++++++++++++++++++++++++++++++++- drivers/acpi/sleep.c | 27 +++++++++++-------------- include/acpi/button.h | 5 +++++ 3 files changed, 62 insertions(+), 16 deletions(-)
drivers/acpi/button.c +45 -1
@@ -191,6 +191,7 @@ struct acpi_button {bool last_state;ktime_t last_time;bool suspended; +bool wakeup_pending;bool lid_state_initialized;bool gpe_enabled; };
@@ -397,6 +398,34 @@ static void acpi_lid_forget(struct acpi_device *adev) }/* Driver Interface */ +/** +* acpi_button_power_wakeup_pending - Mark fixed power button wakeup pending. +* +* Used when the fixed power-button status bit is observed and cleared during +* early S3 resume, so KEY_WAKEUP can still be reported from button resume +* without delivering a deferred KEY_POWER to userspace. +*/ +void acpi_button_power_wakeup_pending (void ) +{ +struct acpi_device *adev; +struct device *phys_dev; +struct acpi_button *button; + + adev =acpi_dev_get_first_match_dev (ACPI_BUTTON_HID_POWERF ,NULL ,-1 ); +if (!adev) +return ; + + phys_dev =acpi_get_first_physical_node (adev); +if (phys_dev) { + button =dev_get_drvdata (phys_dev); +if (button && button->type ==ACPI_BUTTON_TYPE_POWER ) + button->wakeup_pending = true; + } + +acpi_dev_put (adev); +} +EXPORT_SYMBOL_GPL (acpi_button_power_wakeup_pending); +int acpi_lid_open (void ) {guard (mutex)(&acpi_lid_lock);
@@ -476,6 +505,9 @@ static void acpi_button_notify(acpi_handle handle, u32 event, void *data)acpi_pm_wakeup_event (button->dev); +if (button->type ==ACPI_BUTTON_TYPE_POWER && button->suspended) + button->wakeup_pending = true; +if (button->suspended || event ==ACPI_BUTTON_NOTIFY_WAKE )return ;
@@ -498,6 +530,11 @@ static void acpi_button_notify_run(void *data)static u32 acpi_button_event (void *data) { +struct acpi_button *button = data; + +if (button->type ==ACPI_BUTTON_TYPE_POWER && button->suspended) + button->wakeup_pending = true; +acpi_os_execute (OSL_NOTIFY_HANDLER , acpi_button_notify_run, data);return ACPI_INTERRUPT_HANDLED ; }
@@ -507,6 +544,12 @@ static int acpi_button_suspend(struct device *dev) {struct acpi_button *button =dev_get_drvdata (dev); +/* +* Clear wakeup_pending before marking suspended. Otherwise a power +* button SCI between the two stores could set wakeup_pending and then +* be overwritten here, losing a real wakeup. +*/ + button->wakeup_pending = false; button->suspended = true;return 0 ; }
@@ -523,12 +566,13 @@ static int acpi_button_resume(struct device *dev)acpi_lid_initialize_state (button); } -if (button->type ==ACPI_BUTTON_TYPE_POWER ) { +if (button->type ==ACPI_BUTTON_TYPE_POWER && button->wakeup_pending) { input = button->input;input_report_key (input,KEY_WAKEUP ,1 );input_sync (input);input_report_key (input,KEY_WAKEUP ,0 );input_sync (input); + button->wakeup_pending = false; }return 0 ; }
drivers/acpi/sleep.c +12 -15
@@ -22,6 +22,7 @@#include <linux/syscore_ops.h> #include <asm/io.h> #include <trace/events/power.h> +#include <acpi/button.h> #include "internal.h" #include "sleep.h"
@@ -504,12 +505,11 @@ static void acpi_pm_finish(void)acpi_resume_power_resources (); -/* If we were woken with the fixed power button, provide a small -* hint to userspace in the form of a wakeup event on the fixed power -* button device (if it can be found). -* -* We delay the event generation til now, as the PM layer requires -* timekeeping to be running before we generate events. */ +/* +* If woken by the fixed power button, provide a wakeup event on that +* device. KEY_WAKEUP input reporting is handled by the button driver +* via wakeup_pending; do not synthesize KEY_POWER here. +*/ if (!pwr_btn_event_pending)return ;
@@ -626,14 +626,11 @@ static int acpi_suspend_enter(suspend_state_t pm_state)/* Reprogram control registers */ acpi_leave_sleep_state_prep (acpi_state); -/* ACPI 3.0 specs (P62) says that it's the responsibility -* of the OSPM to clear the status bit [ implying that the -* POWER_BUTTON event should not reach userspace ] -* -* However, we do generate a small hint for userspace in the form of -* a wakeup event. We flag this condition for now and generate the -* event later, as we're currently too early in resume to be able to -* generate wakeup events. +/* +* ACPI 3.0 (P62): OSPM should clear the fixed power-button status bit +* so the event does not reach userspace as KEY_POWER. Remember that a +* power-button wake occurred so button resume can report KEY_WAKEUP +* and acpi_pm_finish() can emit a PM wakeup event. */ if (ACPI_SUCCESS (status) && (acpi_state ==ACPI_STATE_S3 )) {acpi_event_status pwr_btn_status =ACPI_EVENT_FLAG_DISABLED ;
@@ -642,8 +639,8 @@ static int acpi_suspend_enter(suspend_state_t pm_state)if (pwr_btn_status &ACPI_EVENT_FLAG_STATUS_SET ) {acpi_clear_event (ACPI_EVENT_POWER_BUTTON ); -/* Flag for later */ pwr_btn_event_pending = true; +acpi_button_power_wakeup_pending (); } }
include/acpi/button.h +5 -0
@@ -8,11 +8,16 @@#if IS_ENABLED (CONFIG_ACPI_BUTTON )extern int acpi_lid_open (void ); +extern void acpi_button_power_wakeup_pending (void );#else static inline int acpi_lid_open (void ) {return 1 ; } + +static inline void acpi_button_power_wakeup_pending (void ) +{ +}#endif /* IS_ENABLED(CONFIG_ACPI_BUTTON) */ #endif /* ACPI_BUTTON_H */
Signature · 1 line
2.34.1
PATCH v5 1/1 ACPI: button: Report wakeup key only for power button wakeups
The ACPI button driver reports KEY_WAKEUP from the Power Button input device to let userspace know that the system was resumed by a power button wakeup.
However, reporting KEY_WAKEUP from generic system resume paths can make userspace observe a Power Button wakeup even when the system was resumed by a different wake source.
This is reproducible on an AMD Android 15 Xen guest. With a kernel without this fix, a non-power-button S3 resume:
echo mem > /sys/power/state
xl trigger android s3resume
makes the Power Button input device report KEY_WAKEUP. The same test on a kernel with this fix no longer reports KEY_WAKEUP from the Power Button input device.
Track whether a power button event/notify is observed while the ACPI button device is suspended, and report KEY_WAKEUP on resume only in that case.
When the fixed power-button status bit is set during early S3 resume, clear it to avoid a deferred KEY_POWER delivery to userspace, and mark wakeup_pending so button resume can still report KEY_WAKEUP. Also clear wakeup_pending before setting suspended to avoid a race with a power button SCI during suspend entry.
Declare acpi_button_power_wakeup_pending() with IS_REACHABLE so that CONFIG_ACPI_BUTTON=m does not leave an unresolved reference in built-in sleep.c.
---
Changes since v4:
- Fix LKP build ERROR when CONFIG_ACPI_BUTTON=m by declaring acpi_button_power_wakeup_pending() with IS_REACHABLE().
drivers/acpi/button.c | 46 ++++++++++++++++++++++++++++++++++++++++++- drivers/acpi/sleep.c | 27 +++++++++++-------------- include/acpi/button.h | 13 ++++++++++++ 3 files changed, 70 insertions(+), 16 deletions(-)
drivers/acpi/button.c +45 -1
@@ -191,6 +191,7 @@ struct acpi_button {bool last_state;ktime_t last_time;bool suspended; +bool wakeup_pending;bool lid_state_initialized;bool gpe_enabled; };
@@ -397,6 +398,34 @@ static void acpi_lid_forget(struct acpi_device *adev) }/* Driver Interface */ +/** +* acpi_button_power_wakeup_pending - Mark fixed power button wakeup pending. +* +* Used when the fixed power-button status bit is observed and cleared during +* early S3 resume, so KEY_WAKEUP can still be reported from button resume +* without delivering a deferred KEY_POWER to userspace. +*/ +void acpi_button_power_wakeup_pending (void ) +{ +struct acpi_device *adev; +struct device *phys_dev; +struct acpi_button *button; + + adev =acpi_dev_get_first_match_dev (ACPI_BUTTON_HID_POWERF ,NULL ,-1 ); +if (!adev) +return ; + + phys_dev =acpi_get_first_physical_node (adev); +if (phys_dev) { + button =dev_get_drvdata (phys_dev); +if (button && button->type ==ACPI_BUTTON_TYPE_POWER ) + button->wakeup_pending = true; + } + +acpi_dev_put (adev); +} +EXPORT_SYMBOL_GPL (acpi_button_power_wakeup_pending); +int acpi_lid_open (void ) {guard (mutex)(&acpi_lid_lock);
@@ -476,6 +505,9 @@ static void acpi_button_notify(acpi_handle handle, u32 event, void *data)acpi_pm_wakeup_event (button->dev); +if (button->type ==ACPI_BUTTON_TYPE_POWER && button->suspended) + button->wakeup_pending = true; +if (button->suspended || event ==ACPI_BUTTON_NOTIFY_WAKE )return ;
@@ -498,6 +530,11 @@ static void acpi_button_notify_run(void *data)static u32 acpi_button_event (void *data) { +struct acpi_button *button = data; + +if (button->type ==ACPI_BUTTON_TYPE_POWER && button->suspended) + button->wakeup_pending = true; +acpi_os_execute (OSL_NOTIFY_HANDLER , acpi_button_notify_run, data);return ACPI_INTERRUPT_HANDLED ; }
@@ -507,6 +544,12 @@ static int acpi_button_suspend(struct device *dev) {struct acpi_button *button =dev_get_drvdata (dev); +/* +* Clear wakeup_pending before marking suspended. Otherwise a power +* button SCI between the two stores could set wakeup_pending and then +* be overwritten here, losing a real wakeup. +*/ + button->wakeup_pending = false; button->suspended = true;return 0 ; }
@@ -523,12 +566,13 @@ static int acpi_button_resume(struct device *dev)acpi_lid_initialize_state (button); } -if (button->type ==ACPI_BUTTON_TYPE_POWER ) { +if (button->type ==ACPI_BUTTON_TYPE_POWER && button->wakeup_pending) { input = button->input;input_report_key (input,KEY_WAKEUP ,1 );input_sync (input);input_report_key (input,KEY_WAKEUP ,0 );input_sync (input); + button->wakeup_pending = false; }return 0 ; }
drivers/acpi/sleep.c +12 -15
@@ -22,6 +22,7 @@#include <linux/syscore_ops.h> #include <asm/io.h> #include <trace/events/power.h> +#include <acpi/button.h> #include "internal.h" #include "sleep.h"
@@ -504,12 +505,11 @@ static void acpi_pm_finish(void)acpi_resume_power_resources (); -/* If we were woken with the fixed power button, provide a small -* hint to userspace in the form of a wakeup event on the fixed power -* button device (if it can be found). -* -* We delay the event generation til now, as the PM layer requires -* timekeeping to be running before we generate events. */ +/* +* If woken by the fixed power button, provide a wakeup event on that +* device. KEY_WAKEUP input reporting is handled by the button driver +* via wakeup_pending; do not synthesize KEY_POWER here. +*/ if (!pwr_btn_event_pending)return ;
@@ -626,14 +626,11 @@ static int acpi_suspend_enter(suspend_state_t pm_state)/* Reprogram control registers */ acpi_leave_sleep_state_prep (acpi_state); -/* ACPI 3.0 specs (P62) says that it's the responsibility -* of the OSPM to clear the status bit [ implying that the -* POWER_BUTTON event should not reach userspace ] -* -* However, we do generate a small hint for userspace in the form of -* a wakeup event. We flag this condition for now and generate the -* event later, as we're currently too early in resume to be able to -* generate wakeup events. +/* +* ACPI 3.0 (P62): OSPM should clear the fixed power-button status bit +* so the event does not reach userspace as KEY_POWER. Remember that a +* power-button wake occurred so button resume can report KEY_WAKEUP +* and acpi_pm_finish() can emit a PM wakeup event. */ if (ACPI_SUCCESS (status) && (acpi_state ==ACPI_STATE_S3 )) {acpi_event_status pwr_btn_status =ACPI_EVENT_FLAG_DISABLED ;
@@ -642,8 +639,8 @@ static int acpi_suspend_enter(suspend_state_t pm_state)if (pwr_btn_status &ACPI_EVENT_FLAG_STATUS_SET ) {acpi_clear_event (ACPI_EVENT_POWER_BUTTON ); -/* Flag for later */ pwr_btn_event_pending = true; +acpi_button_power_wakeup_pending (); } }
include/acpi/button.h +13 -0
@@ -15,4 +15,17 @@ static inline int acpi_lid_open(void) }#endif /* IS_ENABLED(CONFIG_ACPI_BUTTON) */ +/* +* sleep.c is built-in and calls this helper. Use IS_REACHABLE so that when +* CONFIG_ACPI_BUTTON=m the call site gets an inline stub instead of an +* unresolved vmlinux link reference (0day/LKP ERROR). +*/ +#if IS_REACHABLE (CONFIG_ACPI_BUTTON ) +extern void acpi_button_power_wakeup_pending (void ); +#else +static inline void acpi_button_power_wakeup_pending (void ) +{ +} +#endif /* IS_REACHABLE(CONFIG_ACPI_BUTTON) */ +#endif /* ACPI_BUTTON_H */
base-commit: 4a819ee5f2834330656d6ac168c4c8cf27fdeec2
Signature · 1 line
2.34.1
Rafael J. Wysocki (Intel)
maintainerQuoted 33 lines · Baorui.Liu
The ACPI button driver reports
KEY_WAKEUPfrom the Power Button input device to let userspace know that the system was resumed by a power button wakeup.However, reporting
KEY_WAKEUPfrom generic system resume paths can make userspace observe a Power Button wakeup even when the system was resumed by a different wake source.This is reproducible on an AMD Android 15 Xen guest. With a kernel without this fix, a non-power-button S3 resume:
echo mem >
/sys/power/state
xl trigger android s3resumemakes the Power Button input device report
KEY_WAKEUP. The same test on a kernel with this fix no longer reportsKEY_WAKEUPfrom the Power Button input device.Track whether a power button event/notify is observed while the ACPI button device is suspended, and report
KEY_WAKEUPon resume only in that case.When the fixed power-button status bit is set during early S3 resume, clear it to avoid a deferred
KEY_POWERdelivery to userspace, and markwakeup_pendingso button resume can still reportKEY_WAKEUP. Also clearwakeup_pendingbefore setting suspended to avoid a race with a power button SCI during suspend entry.Declare
acpi_button_power_wakeup_pending()withIS_REACHABLEso thatCONFIG_ACPI_BUTTON=mdoes not leave an unresolved reference in built-insleep.c.
Quoted 7 lines
Reported-by kernel test robot <[email protected]>Signed-off-by Baorui.Liu <[email protected]>---
Changes since v4:
- Fix LKP build ERROR whenCONFIG_ACPI_BUTTON=mby declaringacpi_button_power_wakeup_pending()withIS_REACHABLE().
Please see the Sashiko feedback on this patch:
https://sashiko.dev/#/patchset/20261012060847.486-1-baorliu%40amd.com
and let me know what you think about it.
Thanks!
Quoted 189 lines
drivers/acpi/button.c | 46 ++++++++++++++++++++++++++++++++++++++++++- drivers/acpi/sleep.c | 27 +++++++++++-------------- include/acpi/button.h | 13 ++++++++++++ 3 files changed, 70 insertions(+), 16 deletions(-)drivers/acpi/button.c +45
-1@@ -191,6 +191,7 @@ struct acpi_button {bool last_state;ktime_t last_time;bool suspended; +bool wakeup_pending;bool lid_state_initialized;bool gpe_enabled; };@@ -397,6 +398,34 @@ static void acpi_lid_forget(struct acpi_device *adev) }/* Driver Interface */ +/** +* acpi_button_power_wakeup_pending - Mark fixed power button wakeup pending. +* +* Used when the fixed power-button status bit is observed and cleared during +* early S3 resume, so KEY_WAKEUP can still be reported from button resume +* without delivering a deferred KEY_POWER to userspace. +*/ +void acpi_button_power_wakeup_pending (void ) +{ +struct acpi_device *adev; +struct device *phys_dev; +struct acpi_button *button; + + adev =acpi_dev_get_first_match_dev (ACPI_BUTTON_HID_POWERF ,NULL ,-1 ); +if (!adev) +return ; + + phys_dev =acpi_get_first_physical_node (adev); +if (phys_dev) { + button =dev_get_drvdata (phys_dev); +if (button && button->type ==ACPI_BUTTON_TYPE_POWER ) + button->wakeup_pending = true; + } + +acpi_dev_put (adev); +} +EXPORT_SYMBOL_GPL (acpi_button_power_wakeup_pending); +int acpi_lid_open (void ) {guard (mutex)(&acpi_lid_lock);@@ -476,6 +505,9 @@ static void acpi_button_notify(acpi_handle handle, u32 event, void *data)acpi_pm_wakeup_event (button->dev); +if (button->type ==ACPI_BUTTON_TYPE_POWER && button->suspended) + button->wakeup_pending = true; +if (button->suspended || event ==ACPI_BUTTON_NOTIFY_WAKE )return ;@@ -498,6 +530,11 @@ static void acpi_button_notify_run(void *data)static u32 acpi_button_event (void *data) { +struct acpi_button *button = data; + +if (button->type ==ACPI_BUTTON_TYPE_POWER && button->suspended) + button->wakeup_pending = true; +acpi_os_execute (OSL_NOTIFY_HANDLER , acpi_button_notify_run, data);return ACPI_INTERRUPT_HANDLED ; }@@ -507,6 +544,12 @@ static int acpi_button_suspend(struct device *dev) {struct acpi_button *button =dev_get_drvdata (dev); +/* +* Clear wakeup_pending before marking suspended. Otherwise a power +* button SCI between the two stores could set wakeup_pending and then +* be overwritten here, losing a real wakeup. +*/ + button->wakeup_pending = false; button->suspended = true;return 0 ; }@@ -523,12 +566,13 @@ static int acpi_button_resume(struct device *dev)acpi_lid_initialize_state (button); } -if (button->type ==ACPI_BUTTON_TYPE_POWER ) { +if (button->type ==ACPI_BUTTON_TYPE_POWER && button->wakeup_pending) { input = button->input;input_report_key (input,KEY_WAKEUP ,1 );input_sync (input);input_report_key (input,KEY_WAKEUP ,0 );input_sync (input); + button->wakeup_pending = false; }return 0 ; }drivers/acpi/sleep.c +12
-15@@ -22,6 +22,7 @@#include <linux/syscore_ops.h> #include <asm/io.h> #include <trace/events/power.h> +#include <acpi/button.h> #include "internal.h" #include "sleep.h" @@ -504,12 +505,11 @@ static void acpi_pm_finish(void)acpi_resume_power_resources (); -/* If we were woken with the fixed power button, provide a small -* hint to userspace in the form of a wakeup event on the fixed power -* button device (if it can be found). -* -* We delay the event generation til now, as the PM layer requires -* timekeeping to be running before we generate events. */ +/* +* If woken by the fixed power button, provide a wakeup event on that +* device. KEY_WAKEUP input reporting is handled by the button driver +* via wakeup_pending; do not synthesize KEY_POWER here. +*/ if (!pwr_btn_event_pending)return ;@@ -626,14 +626,11 @@ static int acpi_suspend_enter(suspend_state_t pm_state)/* Reprogram control registers */ acpi_leave_sleep_state_prep (acpi_state); -/* ACPI 3.0 specs (P62) says that it's the responsibility -* of the OSPM to clear the status bit [ implying that the -* POWER_BUTTON event should not reach userspace ] -* -* However, we do generate a small hint for userspace in the form of -* a wakeup event. We flag this condition for now and generate the -* event later, as we're currently too early in resume to be able to -* generate wakeup events. +/* +* ACPI 3.0 (P62): OSPM should clear the fixed power-button status bit +* so the event does not reach userspace as KEY_POWER. Remember that a +* power-button wake occurred so button resume can report KEY_WAKEUP +* and acpi_pm_finish() can emit a PM wakeup event. */ if (ACPI_SUCCESS (status) && (acpi_state ==ACPI_STATE_S3 )) {acpi_event_status pwr_btn_status =ACPI_EVENT_FLAG_DISABLED ;@@ -642,8 +639,8 @@ static int acpi_suspend_enter(suspend_state_t pm_state)if (pwr_btn_status &ACPI_EVENT_FLAG_STATUS_SET ) {acpi_clear_event (ACPI_EVENT_POWER_BUTTON ); -/* Flag for later */ pwr_btn_event_pending = true; +acpi_button_power_wakeup_pending (); } }include/acpi/button.h +13
-0@@ -15,4 +15,17 @@ static inline int acpi_lid_open(void) }#endif /* IS_ENABLED(CONFIG_ACPI_BUTTON) */ +/* +* sleep.c is built-in and calls this helper. Use IS_REACHABLE so that when +* CONFIG_ACPI_BUTTON=m the call site gets an inline stub instead of an +* unresolved vmlinux link reference (0day/LKP ERROR). +*/ +#if IS_REACHABLE (CONFIG_ACPI_BUTTON ) +extern void acpi_button_power_wakeup_pending (void ); +#else +static inline void acpi_button_power_wakeup_pending (void ) +{ +} +#endif /* IS_REACHABLE(CONFIG_ACPI_BUTTON) */ +#endif /* ACPI_BUTTON_H */ base-commit:
4a819ee5f2834330656d6ac168c4c8cf27fdeec2--
2.34.1
Hi Rafael,
Thank you for pointing me at the Sashiko review. I went through the four findings and agree with all of them. I have prepared a v6 on current linux-pm/linux-next that addresses them as follows.
1) High: sleep-in-atomic in acpi_suspend_enter() I agree. acpi_button_power_wakeup_pending() reached acpi_get_first_physical_node(), which takes a mutex, while acpi_suspend_enter() runs with IRQs disabled.
In v6, that early path no longer calls into the button driver. It only sets plain flags in sleep.c: the existing pwr_btn_event_pending for acpi_pm_finish()'s pm_wakeup_event(), plus pwr_btn_wakeup_pending for KEY_WAKEUP. The button driver consumes the latter from resume via acpi_sleep_pwr_btn_wakeup_pending().
2) Medium: IS_REACHABLE stub drops KEY_WAKEUP when CONFIG_ACPI_BUTTON=m I agree this was a real functional hole. The v5 IS_REACHABLE() stub fixed the LKP link error, but with a modular button driver the built-in call site became a no-op after clearing the fixed power-button status bit.
Keeping the flag in built-in sleep.c and exporting a consumer for button.ko fixes both the link issue and the modular KEY_WAKEUP loss. The IS_REACHABLE stub is removed. The sleep.c flag uses WRITE_ONCE/READ_ONCE.
3) Medium: missing compiler barrier between the two stores in suspend I agree. v6 uses WRITE_ONCE()/barrier()/READ_ONCE() around wakeup_pending and suspended in acpi_button_suspend(). The same store ordering is used when clearing them in resume: suspended is cleared before wakeup_pending, with a barrier() between the two WRITE_ONCE()s.
4) Medium: race between clearing suspended and a late ACPI notify I agree. v6 calls acpi_os_wait_events_complete() first so queued notifies still observe suspended == true. It then samples the pending state and reports KEY_WAKEUP before clearing suspended, so a notify that arrives after sampling cannot set wakeup_pending only to have it dropped with neither KEY_WAKEUP nor KEY_POWER. After KEY_WAKEUP, suspended is cleared before wakeup_pending (with barrier()) so a new SCI takes the normal KEY_POWER path.
I will send v6 shortly unless you prefer a different approach on any of these points.
Thanks,
Baorui
PATCH v6 1/1 ACPI: button: Report wakeup key only for power button wakeups
In reply to Baorui.LiuThe ACPI button driver reports KEY_WAKEUP from the Power Button input device to let userspace know that the system was resumed by a power button wakeup.
However, reporting KEY_WAKEUP from generic system resume paths can make userspace observe a Power Button wakeup even when the system was resumed by a different wake source.
This is reproducible on an AMD Android 15 Xen guest. With a kernel without this fix, a non-power-button S3 resume:
echo mem > /sys/power/state
xl trigger android s3resume
makes the Power Button input device report KEY_WAKEUP. The same test on a kernel with this fix no longer reports KEY_WAKEUP from the Power Button input device.
Track whether a power button event/notify is observed while the ACPI button device is suspended, and report KEY_WAKEUP on resume only in that case.
When the fixed power-button status bit is set during early S3 resume, clear it to avoid a deferred KEY_POWER delivery to userspace, and set a sleep.c-owned flag so button resume can still report KEY_WAKEUP. Do not call into the button driver from that IRQ-disabled path. Also clear wakeup_pending before setting suspended, with a compiler barrier, and drain ACPI notify work before clearing suspended on resume. Report KEY_WAKEUP before clearing suspended so a late notify cannot drop both KEY_WAKEUP and KEY_POWER.
---
Changes since v5:
- Do not call into the button driver from acpi_suspend_enter(); keep only plain flags there and let button resume consume acpi_sleep_pwr_btn_wakeup_pending() (fixes sleep-in-atomic and the CONFIG_ACPI_BUTTON=m silent stub).
- Use WRITE_ONCE/barrier()/READ_ONCE around suspended and wakeup_pending, and around the sleep.c fixed-button wakeup flag.
- Call acpi_os_wait_events_complete() in button resume, report KEY_WAKEUP before clearing suspended, then clear suspended before wakeup_pending with a barrier so a late notify cannot lose KEY_WAKEUP or emit KEY_POWER.
drivers/acpi/button.c | 63 ++++++++++++++++++++++++++++++++++++------- drivers/acpi/sleep.c | 47 +++++++++++++++++++++----------- include/acpi/button.h | 16 +++++++++++ 3 files changed, 101 insertions(+), 25 deletions(-)
drivers/acpi/button.c +53 -10
@@ -203,6 +203,7 @@ struct acpi_button {bool last_state;ktime_t last_time;bool suspended; +bool wakeup_pending;bool lid_state_initialized;bool gpe_enabled; };
@@ -488,7 +489,11 @@ static void acpi_button_notify(acpi_handle handle, u32 event, void *data)acpi_pm_wakeup_event (button->dev); -if (button->suspended || event ==ACPI_BUTTON_NOTIFY_WAKE ) +if (button->type ==ACPI_BUTTON_TYPE_POWER && +READ_ONCE (button->suspended)) +WRITE_ONCE (button->wakeup_pending, true); + +if (READ_ONCE (button->suspended) || event ==ACPI_BUTTON_NOTIFY_WAKE )return ; input = button->input;
@@ -510,6 +515,12 @@ static void acpi_button_notify_run(void *data)static u32 acpi_button_event (void *data) { +struct acpi_button *button = data; + +if (button->type ==ACPI_BUTTON_TYPE_POWER && +READ_ONCE (button->suspended)) +WRITE_ONCE (button->wakeup_pending, true); +acpi_os_execute (OSL_NOTIFY_HANDLER , acpi_button_notify_run, data);return ACPI_INTERRUPT_HANDLED ; }
@@ -519,7 +530,15 @@ static int acpi_button_suspend(struct device *dev) {struct acpi_button *button =dev_get_drvdata (dev); - button->suspended = true; +/* +* Clear wakeup_pending before marking suspended. Otherwise a power +* button SCI between the two stores could set wakeup_pending and then +* be overwritten here, losing a real wakeup. barrier() prevents the +* compiler from reordering the stores relative to that IRQ handler. +*/ +WRITE_ONCE (button->wakeup_pending, false); +barrier (); +WRITE_ONCE (button->suspended, true);return 0 ; }
@@ -527,21 +546,45 @@ static int acpi_button_resume(struct device *dev) {struct acpi_button *button =dev_get_drvdata (dev);struct input_dev *input; +bool report_wakeup = false; + +/* +* Drain notify work queued while suspended so a late power-button +* notify still observes suspended == true, sets wakeup_pending, and +* does not synthesize KEY_POWER for userspace. +*/ +acpi_os_wait_events_complete (); + +/* +* Sample and report KEY_WAKEUP before clearing suspended. Otherwise a +* notify that arrives after sampling but before suspended is cleared +* can set wakeup_pending and then be dropped without KEY_WAKEUP or +* KEY_POWER. Clear suspended before wakeup_pending so a new SCI sees +* suspended == false and takes the normal KEY_POWER path; barrier() +* keeps that store order. +*/ +if (button->type ==ACPI_BUTTON_TYPE_POWER ) { + report_wakeup =READ_ONCE (button->wakeup_pending) || +acpi_sleep_pwr_btn_wakeup_pending (); +if (report_wakeup) { + input = button->input; +input_report_key (input,KEY_WAKEUP ,1 ); +input_sync (input); +input_report_key (input,KEY_WAKEUP ,0 ); +input_sync (input); + } + } + +WRITE_ONCE (button->suspended, false); +barrier (); +WRITE_ONCE (button->wakeup_pending, false); - button->suspended = false;if (button->type ==ACPI_BUTTON_TYPE_LID ) { button->last_state = !!acpi_lid_evaluate_state (ACPI_HANDLE (dev)); button->last_time =ktime_get ();acpi_lid_initialize_state (button); } -if (button->type ==ACPI_BUTTON_TYPE_POWER ) { - input = button->input; -input_report_key (input,KEY_WAKEUP ,1 ); -input_sync (input); -input_report_key (input,KEY_WAKEUP ,0 ); -input_sync (input); - }return 0 ; }#endif
drivers/acpi/sleep.c +32 -15
78 lines not on this page · raw message
include/acpi/button.h +16 -0
@@ -15,4 +15,20 @@ static inline int acpi_lid_open(void) }#endif /* IS_ENABLED(CONFIG_ACPI_BUTTON) */ +/* +* Set in acpi_suspend_enter() when the fixed power-button status bit is +* observed (IRQ-disabled context). Consumed by the button driver on resume +* so KEY_WAKEUP can be reported without calling into the button driver from +* that early path. Implemented in sleep.c (built-in); safe when +* CONFIG_ACPI_BUTTON=m. +*/ +#ifdef CONFIG_ACPI_SYSTEM_POWER_STATES_SUPPORT +bool acpi_sleep_pwr_btn_wakeup_pending (void ); +#else +static inline bool acpi_sleep_pwr_btn_wakeup_pending (void ) +{ +return false; +} +#endif +#endif /* ACPI_BUTTON_H */
Signature · 1 line
2.34.1
kernel test robot
build botkernel test robot noticed the following build errors:
Hi Baorui.Liu,
kernel test robot noticed the following build errors:
[auto build test ERROR on rafael-pm/linux-next]
[also build test ERROR on rafael-pm/bleeding-edge linus/master v7.3 next-20261030] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Baorui-Liu/ACPI-button-Report-wakeup-key-only-for-power-button-wakeups/20261102-171049 base: https://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm.git linux-next patch link: https://lore.kernel.org/r/20261102091049.856-1-baorliu%40amd.com patch subject: [PATCH v6] ACPI: button: Report wakeup key only for power button wakeups config: parisc64-defconfig (https://download.01.org/0day-ci/archive/20261103/[email protected]/config) compiler: hppa64-linux-gcc (GCC) 16.2.0 reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20261103/[email protected]/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <[email protected]>
| Closes: https://lore.kernel.org/oe-kbuild-all/[email protected]/
All errors (new ones prefixed by >>):
In file included from drivers/gpu/drm/nouveau/nouveau_connector.c:27:
>> include/acpi/button.h:28:15: error: unknown type name 'bool'
28 | static inline bool acpi_sleep_pwr_btn_wakeup_pending(void)
| ^~~~
include/acpi/button.h:1:1: note: 'bool' is defined in header '<stdbool.h>'; this is probably fixable by adding '#include <stdbool.h>'
+++ |+#include <stdbool.h>
1 | /* SPDX-License-Identifier: GPL-2.0 */
include/acpi/button.h: In function 'acpi_sleep_pwr_btn_wakeup_pending':
>> include/acpi/button.h:30:16: error: 'false' undeclared (first use in this function)
30 | return false;
| ^~~~~ include/acpi/button.h:30:16: note: 'false' is defined in header '<stdbool.h>'; this is probably fixable by adding '#include <stdbool.h>' include/acpi/button.h:30:16: note: each undeclared identifier is reported only once for each function it appears in
vim +/bool +28 include/acpi/button.h
17
18 /*
19 * Set in acpi_suspend_enter() when the fixed power-button status bit is
20 * observed (IRQ-disabled context). Consumed by the button driver on resume
21 * so KEY_WAKEUP can be reported without calling into the button driver from
22 * that early path. Implemented in sleep.c (built-in); safe when
23 * CONFIG_ACPI_BUTTON=m.
24 */
25 #ifdef CONFIG_ACPI_SYSTEM_POWER_STATES_SUPPORT
26 bool acpi_sleep_pwr_btn_wakeup_pending(void);
27 #else
> 28 static inline bool acpi_sleep_pwr_btn_wakeup_pending(void)
29 {
> 30 return false;
31 }
32 #endif
33 --
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
kernel test robot
build botkernel test robot noticed the following build errors:
Hi Baorui.Liu,
kernel test robot noticed the following build errors:
[auto build test ERROR on rafael-pm/linux-next]
[also build test ERROR on rafael-pm/bleeding-edge linus/master v7.3 next-20261030] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Baorui-Liu/ACPI-button-Report-wakeup-key-only-for-power-button-wakeups/20261102-171049 base: https://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm.git linux-next patch link: https://lore.kernel.org/r/20261102091049.856-1-baorliu%40amd.com patch subject: [PATCH v6] ACPI: button: Report wakeup key only for power button wakeups config: arm64-randconfig-1001-20261102 (https://download.01.org/0day-ci/archive/20261103/[email protected]/config) compiler: clang version 19.1.7 (https://github.com/llvm/llvm-project cd708029e0b2869e80abe31ddb175f7c35361f90) reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20261103/[email protected]/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <[email protected]>
| Closes: https://lore.kernel.org/oe-kbuild-all/[email protected]/
All errors (new ones prefixed by >>):
In file included from drivers/gpu/drm/nouveau/nouveau_connector.c:27:
include/acpi/button.h:28:15: error: unknown type name 'bool'
28 | static inline bool acpi_sleep_pwr_btn_wakeup_pending(void)
| ^
include/acpi/button.h:30:9: error: use of undeclared identifier 'false'; did you mean 'else'?
30 | return false;
| ^~~~~
| else
>> include/acpi/button.h:30:9: error: expected expression
In file included from drivers/gpu/drm/nouveau/nouveau_connector.c:29:
In file included from include/linux/pm_runtime.h:11:
In file included from include/linux/device.h:32:
In file included from include/linux/device/driver.h:21:
In file included from include/linux/module.h:20:
In file included from include/linux/elf.h:6:
In file included from arch/arm64/include/asm/elf.h:133:
In file included from include/linux/fs.h:5:
In file included from include/linux/fs/super.h:5:
In file included from include/linux/fs/super_types.h:13:
In file included from include/linux/percpu-rwsem.h:7:
In file included from include/linux/rcuwait.h:6:
In file included from include/linux/sched/signal.h:7:
include/linux/signal.h:98:11: warning: array index 3 is past the end of the array (that has type 'unsigned long[1]') [-Warray-bounds]
98 | return (set->sig[3] | set->sig[2] |
| ^ ~
include/uapi/asm-generic/signal.h:62:2: note: array 'sig' declared here
62 | unsigned long sig[_NSIG_WORDS];
| ^
In file included from drivers/gpu/drm/nouveau/nouveau_connector.c:29:
In file included from include/linux/pm_runtime.h:11:
In file included from include/linux/device.h:32:
In file included from include/linux/device/driver.h:21:
In file included from include/linux/module.h:20:
In file included from include/linux/elf.h:6:
In file included from arch/arm64/include/asm/elf.h:133:
In file included from include/linux/fs.h:5:
In file included from include/linux/fs/super.h:5:
In file included from include/linux/fs/super_types.h:13:
In file included from include/linux/percpu-rwsem.h:7:
In file included from include/linux/rcuwait.h:6:
In file included from include/linux/sched/signal.h:7:
include/linux/signal.h:98:25: warning: array index 2 is past the end of the array (that has type 'unsigned long[1]') [-Warray-bounds]
98 | return (set->sig[3] | set->sig[2] |
| ^ ~
include/uapi/asm-generic/signal.h:62:2: note: array 'sig' declared here
62 | unsigned long sig[_NSIG_WORDS];
| ^
In file included from drivers/gpu/drm/nouveau/nouveau_connector.c:29:
In file included from include/linux/pm_runtime.h:11:
In file included from include/linux/device.h:32:
In file included from include/linux/device/driver.h:21:
In file included from include/linux/module.h:20:
In file included from include/linux/elf.h:6:
In file included from arch/arm64/include/asm/elf.h:133:
In file included from include/linux/fs.h:5:
In file included from include/linux/fs/super.h:5:
In file included from include/linux/fs/super_types.h:13:
In file included from include/linux/percpu-rwsem.h:7:
In file included from include/linux/rcuwait.h:6:
In file included from include/linux/sched/signal.h:7:
include/linux/signal.h:99:4: warning: array index 1 is past the end of the array (that has type 'unsigned long[1]') [-Warray-bounds]
99 | set->sig[1] | set->sig[0]) == 0;
| ^ ~
include/uapi/asm-generic/signal.h:62:2: note: array 'sig' declared here
62 | unsigned long sig[_NSIG_WORDS];
| ^
In file included from drivers/gpu/drm/nouveau/nouveau_connector.c:29:
In file included from include/linux/pm_runtime.h:11:
In file included from include/linux/device.h:32:
In file included from include/linux/device/driver.h:21:
In file included from include/linux/module.h:20:
In file included from include/linux/elf.h:6:
In file included from arch/arm64/include/asm/elf.h:133:
In file included from include/linux/fs.h:5:
In file included from include/linux/fs/super.h:5:
In file included from include/linux/fs/super_types.h:13:
In file included from include/linux/percpu-rwsem.h:7:
In file included from include/linux/rcuwait.h:6:
In file included from include/linux/sched/signal.h:7:
include/linux/signal.h:101:11: warning: array index 1 is past the end of the array (that has type 'unsigned long[1]') [-Warray-bounds]
101 | return (set->sig[1] | set->sig[0]) == 0;
| ^ ~
include/uapi/asm-generic/signal.h:62:2: note: array 'sig' declared here
62 | unsigned long sig[_NSIG_WORDS];
| ^
In file included from drivers/gpu/drm/nouveau/nouveau_connector.c:29:
In file included from include/linux/pm_runtime.h:11:
In file included from include/linux/device.h:32:
In file included from include/linux/device/driver.h:21:
In file included from include/linux/module.h:20:
In file included from include/linux/elf.h:6:
In file included from arch/arm64/include/asm/elf.h:133:
In file included from include/linux/fs.h:5:
In file included from include/linux/fs/super.h:5:
In file included from include/linux/fs/super_types.h:13:
In file included from include/linux/percpu-rwsem.h:7:
In file included from include/linux/rcuwait.h:6:
In file included from include/linux/sched/signal.h:7:
include/linux/signal.h:114:11: warning: array index 3 is past the end of the array (that has type 'const unsigned long[1]') [-Warray-bounds]
114 | return (set1->sig[3] == set2->sig[3]) &&
| ^ ~
include/uapi/asm-generic/signal.h:62:2: note: array 'sig' declared here
62 | unsigned long sig[_NSIG_WORDS];
| ^ In file included from drivers/gpu/drm/nouveau/nouveau_connector.c:29:
In file included from include/linux/pm_runtime.h:11:
In file included from include/linux/device.h:32:
In file included from include/linux/device/driver.h:21:
In file included from include/linux/module.h:20:
vim +30 include/acpi/button.h
17
18 /*
19 * Set in acpi_suspend_enter() when the fixed power-button status bit is
20 * observed (IRQ-disabled context). Consumed by the button driver on resume
21 * so KEY_WAKEUP can be reported without calling into the button driver from
22 * that early path. Implemented in sleep.c (built-in); safe when
23 * CONFIG_ACPI_BUTTON=m.
24 */
25 #ifdef CONFIG_ACPI_SYSTEM_POWER_STATES_SUPPORT
26 bool acpi_sleep_pwr_btn_wakeup_pending(void);
27 #else
28 static inline bool acpi_sleep_pwr_btn_wakeup_pending(void)
29 {
> 30 return false;
31 }
32 #endif
33 --
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
PATCH v7 1/1 ACPI: button: Report wakeup key only for power button wakeups
In reply to Baorui.LiuThe ACPI button driver reports KEY_WAKEUP from the Power Button input device to let userspace know that the system was resumed by a power button wakeup.
However, reporting KEY_WAKEUP from generic system resume paths can make userspace observe a Power Button wakeup even when the system was resumed by a different wake source.
This is reproducible on an AMD Android 15 Xen guest. With a kernel without this fix, a non-power-button S3 resume:
echo mem > /sys/power/state
xl trigger android s3resume
makes the Power Button input device report KEY_WAKEUP. The same test on a kernel with this fix no longer reports KEY_WAKEUP from the Power Button input device.
Track whether a power button event/notify is observed while the ACPI button device is suspended, and report KEY_WAKEUP on resume only in that case.
When the fixed power-button status bit is set during early S3 resume, clear it to avoid a deferred KEY_POWER delivery to userspace, and set a sleep.c-owned flag so button resume can still report KEY_WAKEUP. Do not call into the button driver from that IRQ-disabled path. Also clear wakeup_pending before setting suspended, with a compiler barrier, and drain ACPI notify work before clearing suspended on resume. Report KEY_WAKEUP before clearing suspended so a late notify cannot drop both KEY_WAKEUP and KEY_POWER.
Changes since v6:
- Include <linux/types.h> in include/acpi/button.h so the bool declaration/stub of acpi_sleep_pwr_btn_wakeup_pending() builds when the header is included without prior bool typedefs (0day/LKP ERROR via nouveau_connector.c on parisc64-defconfig and arm64-randconfig).
--- drivers/acpi/button.c | 63 ++++++++++++++++++++++++++++++++++++------- drivers/acpi/sleep.c | 47 +++++++++++++++++++++----------- include/acpi/button.h | 18 +++++++++++++ 3 files changed, 103 insertions(+), 25 deletions(-)
drivers/acpi/button.c +53 -10
@@ -203,6 +203,7 @@ struct acpi_button {bool last_state;ktime_t last_time;bool suspended; +bool wakeup_pending;bool lid_state_initialized;bool gpe_enabled; };
@@ -488,7 +489,11 @@ static void acpi_button_notify(acpi_handle handle, u32 event, void *data)acpi_pm_wakeup_event (button->dev); -if (button->suspended || event ==ACPI_BUTTON_NOTIFY_WAKE ) +if (button->type ==ACPI_BUTTON_TYPE_POWER && +READ_ONCE (button->suspended)) +WRITE_ONCE (button->wakeup_pending, true); + +if (READ_ONCE (button->suspended) || event ==ACPI_BUTTON_NOTIFY_WAKE )return ; input = button->input;
@@ -510,6 +515,12 @@ static void acpi_button_notify_run(void *data)static u32 acpi_button_event (void *data) { +struct acpi_button *button = data; + +if (button->type ==ACPI_BUTTON_TYPE_POWER && +READ_ONCE (button->suspended)) +WRITE_ONCE (button->wakeup_pending, true); +acpi_os_execute (OSL_NOTIFY_HANDLER , acpi_button_notify_run, data);return ACPI_INTERRUPT_HANDLED ; }
@@ -519,7 +530,15 @@ static int acpi_button_suspend(struct device *dev) {struct acpi_button *button =dev_get_drvdata (dev); - button->suspended = true; +/* +* Clear wakeup_pending before marking suspended. Otherwise a power +* button SCI between the two stores could set wakeup_pending and then +* be overwritten here, losing a real wakeup. barrier() prevents the +* compiler from reordering the stores relative to that IRQ handler. +*/ +WRITE_ONCE (button->wakeup_pending, false); +barrier (); +WRITE_ONCE (button->suspended, true);return 0 ; }
@@ -527,21 +546,45 @@ static int acpi_button_resume(struct device *dev) {struct acpi_button *button =dev_get_drvdata (dev);struct input_dev *input; +bool report_wakeup = false; + +/* +* Drain notify work queued while suspended so a late power-button +* notify still observes suspended == true, sets wakeup_pending, and +* does not synthesize KEY_POWER for userspace. +*/ +acpi_os_wait_events_complete (); + +/* +* Sample and report KEY_WAKEUP before clearing suspended. Otherwise a +* notify that arrives after sampling but before suspended is cleared +* can set wakeup_pending and then be dropped without KEY_WAKEUP or +* KEY_POWER. Clear suspended before wakeup_pending so a new SCI sees +* suspended == false and takes the normal KEY_POWER path; barrier() +* keeps that store order. +*/ +if (button->type ==ACPI_BUTTON_TYPE_POWER ) { + report_wakeup =READ_ONCE (button->wakeup_pending) || +acpi_sleep_pwr_btn_wakeup_pending (); +if (report_wakeup) { + input = button->input; +input_report_key (input,KEY_WAKEUP ,1 ); +input_sync (input); +input_report_key (input,KEY_WAKEUP ,0 ); +input_sync (input); + } + } + +WRITE_ONCE (button->suspended, false); +barrier (); +WRITE_ONCE (button->wakeup_pending, false); - button->suspended = false;if (button->type ==ACPI_BUTTON_TYPE_LID ) { button->last_state = !!acpi_lid_evaluate_state (ACPI_HANDLE (dev)); button->last_time =ktime_get ();acpi_lid_initialize_state (button); } -if (button->type ==ACPI_BUTTON_TYPE_POWER ) { - input = button->input; -input_report_key (input,KEY_WAKEUP ,1 ); -input_sync (input); -input_report_key (input,KEY_WAKEUP ,0 ); -input_sync (input); - }return 0 ; }#endif
drivers/acpi/sleep.c +32 -15
78 lines not on this page · raw message
include/acpi/button.h +18 -0
@@ -2,6 +2,8 @@#ifndef ACPI_BUTTON_H #define ACPI_BUTTON_H +#include <linux/types.h> /* for bool */ +#define ACPI_BUTTON_HID_POWER "PNP0C0C"#define ACPI_BUTTON_HID_LID "PNP0C0D"#define ACPI_BUTTON_HID_SLEEP "PNP0C0E"
@@ -15,4 +17,20 @@ static inline int acpi_lid_open(void) }#endif /* IS_ENABLED(CONFIG_ACPI_BUTTON) */ +/* +* Set in acpi_suspend_enter() when the fixed power-button status bit is +* observed (IRQ-disabled context). Consumed by the button driver on resume +* so KEY_WAKEUP can be reported without calling into the button driver from +* that early path. Implemented in sleep.c (built-in); safe when +* CONFIG_ACPI_BUTTON=m. +*/ +#ifdef CONFIG_ACPI_SYSTEM_POWER_STATES_SUPPORT +bool acpi_sleep_pwr_btn_wakeup_pending (void ); +#else +static inline bool acpi_sleep_pwr_btn_wakeup_pending (void ) +{ +return false; +} +#endif +#endif /* ACPI_BUTTON_H */
base-commit: ee3b82351a306b559fd132b9b168185fee641a18
Signature · 1 line
2.34.1
PATCH v7 1/1 ACPI: button: Report wakeup key only for power button wakeups
In reply to Baorui.LiuThe ACPI button driver reports KEY_WAKEUP from the Power Button input device to let userspace know that the system was resumed by a power button wakeup.
However, reporting KEY_WAKEUP from generic system resume paths can make userspace observe a Power Button wakeup even when the system was resumed by a different wake source.
This is reproducible on an AMD Android 15 Xen guest. With a kernel without this fix, a non-power-button S3 resume:
echo mem > /sys/power/state
xl trigger android s3resume
makes the Power Button input device report KEY_WAKEUP. The same test on a kernel with this fix no longer reports KEY_WAKEUP from the Power Button input device.
Track whether a power button event/notify is observed while the ACPI button device is suspended, and report KEY_WAKEUP on resume only in that case.
When the fixed power-button status bit is set during early S3 resume, clear it to avoid a deferred KEY_POWER delivery to userspace, and set a sleep.c-owned flag so button resume can still report KEY_WAKEUP. Do not call into the button driver from that IRQ-disabled path. Also clear wakeup_pending before setting suspended, with a compiler barrier, and drain ACPI notify work before clearing suspended on resume. Report KEY_WAKEUP before clearing suspended so a late notify cannot drop both KEY_WAKEUP and KEY_POWER.
Changes since v6:
- Include <linux/types.h> in include/acpi/button.h so the bool declaration/stub of acpi_sleep_pwr_btn_wakeup_pending() builds when the header is included without prior bool typedefs (0day/LKP ERROR via nouveau_connector.c on parisc64-defconfig and arm64-randconfig).
--- drivers/acpi/button.c | 63 ++++++++++++++++++++++++++++++++++++------- drivers/acpi/sleep.c | 47 +++++++++++++++++++++----------- include/acpi/button.h | 18 +++++++++++++ 3 files changed, 103 insertions(+), 25 deletions(-)
drivers/acpi/button.c +53 -10
@@ -203,6 +203,7 @@ struct acpi_button {bool last_state;ktime_t last_time;bool suspended; +bool wakeup_pending;bool lid_state_initialized;bool gpe_enabled; };
@@ -488,7 +489,11 @@ static void acpi_button_notify(acpi_handle handle, u32 event, void *data)acpi_pm_wakeup_event (button->dev); -if (button->suspended || event ==ACPI_BUTTON_NOTIFY_WAKE ) +if (button->type ==ACPI_BUTTON_TYPE_POWER && +READ_ONCE (button->suspended)) +WRITE_ONCE (button->wakeup_pending, true); + +if (READ_ONCE (button->suspended) || event ==ACPI_BUTTON_NOTIFY_WAKE )return ; input = button->input;
@@ -510,6 +515,12 @@ static void acpi_button_notify_run(void *data)static u32 acpi_button_event (void *data) { +struct acpi_button *button = data; + +if (button->type ==ACPI_BUTTON_TYPE_POWER && +READ_ONCE (button->suspended)) +WRITE_ONCE (button->wakeup_pending, true); +acpi_os_execute (OSL_NOTIFY_HANDLER , acpi_button_notify_run, data);return ACPI_INTERRUPT_HANDLED ; }
@@ -519,7 +530,15 @@ static int acpi_button_suspend(struct device *dev) {struct acpi_button *button =dev_get_drvdata (dev); - button->suspended = true; +/* +* Clear wakeup_pending before marking suspended. Otherwise a power +* button SCI between the two stores could set wakeup_pending and then +* be overwritten here, losing a real wakeup. barrier() prevents the +* compiler from reordering the stores relative to that IRQ handler. +*/ +WRITE_ONCE (button->wakeup_pending, false); +barrier (); +WRITE_ONCE (button->suspended, true);return 0 ; }
@@ -527,21 +546,45 @@ static int acpi_button_resume(struct device *dev) {struct acpi_button *button =dev_get_drvdata (dev);struct input_dev *input; +bool report_wakeup = false; + +/* +* Drain notify work queued while suspended so a late power-button +* notify still observes suspended == true, sets wakeup_pending, and +* does not synthesize KEY_POWER for userspace. +*/ +acpi_os_wait_events_complete (); + +/* +* Sample and report KEY_WAKEUP before clearing suspended. Otherwise a +* notify that arrives after sampling but before suspended is cleared +* can set wakeup_pending and then be dropped without KEY_WAKEUP or +* KEY_POWER. Clear suspended before wakeup_pending so a new SCI sees +* suspended == false and takes the normal KEY_POWER path; barrier() +* keeps that store order. +*/ +if (button->type ==ACPI_BUTTON_TYPE_POWER ) { + report_wakeup =READ_ONCE (button->wakeup_pending) || +acpi_sleep_pwr_btn_wakeup_pending (); +if (report_wakeup) { + input = button->input; +input_report_key (input,KEY_WAKEUP ,1 ); +input_sync (input); +input_report_key (input,KEY_WAKEUP ,0 ); +input_sync (input); + } + } + +WRITE_ONCE (button->suspended, false); +barrier (); +WRITE_ONCE (button->wakeup_pending, false); - button->suspended = false;if (button->type ==ACPI_BUTTON_TYPE_LID ) { button->last_state = !!acpi_lid_evaluate_state (ACPI_HANDLE (dev)); button->last_time =ktime_get ();acpi_lid_initialize_state (button); } -if (button->type ==ACPI_BUTTON_TYPE_POWER ) { - input = button->input; -input_report_key (input,KEY_WAKEUP ,1 ); -input_sync (input); -input_report_key (input,KEY_WAKEUP ,0 ); -input_sync (input); - }return 0 ; }#endif
drivers/acpi/sleep.c +32 -15
78 lines not on this page · raw message
include/acpi/button.h +18 -0
@@ -2,6 +2,8 @@#ifndef ACPI_BUTTON_H #define ACPI_BUTTON_H +#include <linux/types.h> /* for bool */ +#define ACPI_BUTTON_HID_POWER "PNP0C0C"#define ACPI_BUTTON_HID_LID "PNP0C0D"#define ACPI_BUTTON_HID_SLEEP "PNP0C0E"
@@ -15,4 +17,20 @@ static inline int acpi_lid_open(void) }#endif /* IS_ENABLED(CONFIG_ACPI_BUTTON) */ +/* +* Set in acpi_suspend_enter() when the fixed power-button status bit is +* observed (IRQ-disabled context). Consumed by the button driver on resume +* so KEY_WAKEUP can be reported without calling into the button driver from +* that early path. Implemented in sleep.c (built-in); safe when +* CONFIG_ACPI_BUTTON=m. +*/ +#ifdef CONFIG_ACPI_SYSTEM_POWER_STATES_SUPPORT +bool acpi_sleep_pwr_btn_wakeup_pending (void ); +#else +static inline bool acpi_sleep_pwr_btn_wakeup_pending (void ) +{ +return false; +} +#endif +#endif /* ACPI_BUTTON_H */
base-commit: ee3b82351a306b559fd132b9b168185fee641a18
Signature · 1 line
2.34.1
kernel test robot
build botkernel test robot noticed the following build errors:
Hi Baorui.Liu,
kernel test robot noticed the following build errors:
[auto build test ERROR on ee3b82351a306b559fd132b9b168185fee641a18]
url: https://github.com/intel-lab-lkp/linux/commits/Baorui-Liu/ACPI-button-Report-wakeup-key-only-for-power-button-wakeups/20261109-162832 base: ee3b82351a306b559fd132b9b168185fee641a18 patch link: https://lore.kernel.org/r/20261109082832.496-1-baorliu%40amd.com patch subject: [PATCH v7] ACPI: button: Report wakeup key only for power button wakeups config: x86_64-randconfig-1015-20261109 (https://download.01.org/0day-ci/archive/20261110/[email protected]/config) compiler: gcc-14 (Debian 14.2.0-19) 14.2.0 reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20261110/[email protected]/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <[email protected]>
| Closes: https://lore.kernel.org/oe-kbuild-all/[email protected]/
All errors (new ones prefixed by >>):
ld: drivers/acpi/button.o: in function `acpi_button_resume':
>> drivers/acpi/button.c:568:(.text+0x2384): undefined reference to `acpi_sleep_pwr_btn_wakeup_pending'
vim +568 drivers/acpi/button.c
544
545 static int acpi_button_resume(struct device *dev)
546 {
547 struct acpi_button *button = dev_get_drvdata(dev);
548 struct input_dev *input;
549 bool report_wakeup = false;
550
551 /*
552 * Drain notify work queued while suspended so a late power-button
553 * notify still observes suspended == true, sets wakeup_pending, and
554 * does not synthesize KEY_POWER for userspace.
555 */
556 acpi_os_wait_events_complete();
557
558 /*
559 * Sample and report KEY_WAKEUP before clearing suspended. Otherwise a
560 * notify that arrives after sampling but before suspended is cleared
561 * can set wakeup_pending and then be dropped without KEY_WAKEUP or
562 * KEY_POWER. Clear suspended before wakeup_pending so a new SCI sees
563 * suspended == false and takes the normal KEY_POWER path; barrier()
564 * keeps that store order.
565 */
566 if (button->type == ACPI_BUTTON_TYPE_POWER) {
567 report_wakeup = READ_ONCE(button->wakeup_pending) ||
> 568 acpi_sleep_pwr_btn_wakeup_pending();
569 if (report_wakeup) {
570 input = button->input;
571 input_report_key(input, KEY_WAKEUP, 1);
572 input_sync(input);
573 input_report_key(input, KEY_WAKEUP, 0);
574 input_sync(input);
575 }
576 }
577
578 WRITE_ONCE(button->suspended, false);
579 barrier();
580 WRITE_ONCE(button->wakeup_pending, false);
581
582 if (button->type == ACPI_BUTTON_TYPE_LID) {
583 button->last_state = !!acpi_lid_evaluate_state(ACPI_HANDLE(dev));
584 button->last_time = ktime_get();
585 acpi_lid_initialize_state(button);
586 }
587
588 return 0;
589 }
590 #endif
591
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
kernel test robot
build botkernel test robot noticed the following build warnings:
Hi Baorui,
kernel test robot noticed the following build warnings:
[auto build test WARNING on 4d823c9d06aaa91476b58e56e4d44c4112da2811]
url: https://github.com/intel-lab-lkp/linux/commits/Baorui-Liu/ACPI-button-Report-wakeup-key-only-for-power-button-wakeups/20260806-202732 base: 4d823c9d06aaa91476b58e56e4d44c4112da2811 patch link: https://lore.kernel.org/r/20260806122732.533-1-baorliu%40amd.com patch subject: [PATCH] ACPI: button: Report wakeup key only for power button wakeups config: csky-allmodconfig (https://download.01.org/0day-ci/archive/20260820/[email protected]/config) compiler: csky-linux-gcc (GCC) 16.1.0 reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260820/[email protected]/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <[email protected]>
| Closes: https://lore.kernel.org/oe-kbuild-all/[email protected]/
All warnings (new ones prefixed by >>):
In file included from drivers/gpu/drm/nouveau/nouveau_connector.c:27:
>> include/acpi/button.h:18:52: warning: 'struct acpi_device' declared inside parameter list will not be visible outside of this definition or declaration
18 | static inline void acpi_power_button_wakeup(struct acpi_device *device)
| ^~~~~~~~~~~
vim +18 include/acpi/button.h
17
> 18 static inline void acpi_power_button_wakeup(struct acpi_device *device)
19 {
20 }
21 #endif /* IS_ENABLED(CONFIG_ACPI_BUTTON) */
22 --
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki