Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

PM: hibernate: Avoid missing wakeup events during hibernation

Wakeup events that occur in the hibernation process's
hibernation_platform_enter() cannot wake up the system. Although the
current hibernation framework will execute part of the recovery process
after a wakeup event occurs, it ultimately performs a shutdown operation
because the system does not check the return value of
hibernation_platform_enter(). In short, if a wakeup event occurs before
putting the system into the final low-power state, it will be missed.

To solve this problem, check the return value of
hibernation_platform_enter(). When it returns -EAGAIN or -EBUSY (indicate
the occurrence of a wakeup event), execute the hibernation recovery
process, discard the previously saved image, and ultimately return to the
working state.

Signed-off-by: Chris Feng <chris.feng@mediatek.com>
[ rjw: Rephrase the message printed when going back to the working state ]
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>

authored by

Chris Feng and committed by
Rafael J. Wysocki
0c4cae1b 4ac934b1

+10 -2
+8 -2
kernel/power/hibernate.c
··· 642 642 */ 643 643 static void power_down(void) 644 644 { 645 - #ifdef CONFIG_SUSPEND 646 645 int error; 647 646 647 + #ifdef CONFIG_SUSPEND 648 648 if (hibernation_mode == HIBERNATION_SUSPEND) { 649 649 error = suspend_devices_and_enter(mem_sleep_current); 650 650 if (error) { ··· 667 667 kernel_restart(NULL); 668 668 break; 669 669 case HIBERNATION_PLATFORM: 670 - hibernation_platform_enter(); 670 + error = hibernation_platform_enter(); 671 + if (error == -EAGAIN || error == -EBUSY) { 672 + swsusp_unmark(); 673 + events_check_enabled = false; 674 + pr_info("Wakeup event detected during hibernation, rolling back.\n"); 675 + return; 676 + } 671 677 fallthrough; 672 678 case HIBERNATION_SHUTDOWN: 673 679 if (kernel_can_power_off())
+2
kernel/power/power.h
··· 175 175 void swsusp_close(void); 176 176 #ifdef CONFIG_SUSPEND 177 177 extern int swsusp_unmark(void); 178 + #else 179 + static inline int swsusp_unmark(void) { return 0; } 178 180 #endif 179 181 180 182 struct __kernel_old_timeval;