It is reported the hibernation fails at 2nd attempt, which hangs at hibernate() -> syscore_resume() -> i8237A_resume() -> claim_dma_lock(), because the lock has already been taken. However there is actually no other process would like to grab this lock on that problematic platform. Further investigation shows that, the problem is triggered by setting /sys/power/pm_trace to 1 before the 1st hibernation. Since once pm_trace is enabled, the rtc becomes unmeaningful after suspend, and meanwhile some BIOSes would like to adjust the 'invalid' tsc(e.g, smaller than 1970) to the release date of that motherboard during POST stage, thus after resumed, a significant long sleep time might be generated due to meaningless tsc delta, thus in timekeeping_resume -> tk_debug_account_sleep_time, if the bit31 happened to be set to 1, the fls returns 32 and then we add 1 to sleep_time_bin[32], which caused a memory overwritten. As depicted by System.map: ffffffff81c9d080 b sleep_time_bin ffffffff81c9d100 B dma_spin_lock the dma_spin_lock.val is set to 1, which caused this problem. In theory we can avoid the overflow by ignoring the idle injection if pm_trace is enabled, but we might still miss other cases which might also break the rtc, e.g, buggy clocksoure/rtc driver, or even user space tool such as hwclock -- so there is no generic method to dertermin whether we should trust the tsc. A simpler way is to set the threshold for the sleep time, and ignore those abnormal ones. This patch sets the upper limit of sleep seconds to 0x7fffffff, since no one is likely to sleep that long(68 years). Cc: Stable <stable@xxxxxxxxxxxxxxx> # 3.17+ Cc: Thomas Gleixner <tglx@xxxxxxxxxxxxx> Cc: Rafael J. Wysocki <rjw@xxxxxxxxxxxxx> Cc: John Stultz <john.stultz@xxxxxxxxxx> Cc: Xunlei Pang <xpang@xxxxxxxxxx> Cc: Zhang Rui <rui.zhang@xxxxxxxxx> Cc: linux-kernel@xxxxxxxxxxxxxxx Cc: linux-pm@xxxxxxxxxxxxxxx Reported-and-tested-by: Janek Kozicki <cosurgi@xxxxxxxxx> Signed-off-by: Chen Yu <yu.c.chen@xxxxxxxxx> --- kernel/time/timekeeping.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index 3b65746..17bc72c 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -1509,6 +1509,7 @@ void __init timekeeping_init(void) /* time in seconds when suspend began for persistent clock */ static struct timespec64 timekeeping_suspend_time; +#define MAX_SLEEP_TIME 0x7fffffff /** * __timekeeping_inject_sleeptime - Internal function to add sleep interval @@ -1520,7 +1521,8 @@ static struct timespec64 timekeeping_suspend_time; static void __timekeeping_inject_sleeptime(struct timekeeper *tk, struct timespec64 *delta) { - if (!timespec64_valid_strict(delta)) { + if (!timespec64_valid_strict(delta) || + delta->tv_sec > MAX_SLEEP_TIME) { printk_deferred(KERN_WARNING "__timekeeping_inject_sleeptime: Invalid " "sleep delta value!\n"); -- 2.7.4 -- To unsubscribe from this list: send the line "unsubscribe stable" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html