timekeeping: Ignore the bogus sleep time if pm_trace is enabled
Subsystems (8)
RFC PATCH v5 1/1 timekeeping: Ignore the bogus sleep time if pm_trace is enabled
Previously we encountered some memory overflow issues due to the bogus sleep time brought by inconsistent rtc, which is triggered when pm_trace is enabled, and we have fixed it in recent kernel. However it's improper in the first place to call __timekeeping_inject_sleeptime() in case that pm_trace is enabled simply because that "hash" time value will wreckage the timekeeping subsystem.
So this patch ignores the sleep time if pm_trace is enabled in the following situation:
1. rtc is used as persist clock to compensate for sleep time, or
2. rtc is used to calculate the sleep time in rtc_resume.
--- arch/x86/kernel/rtc.c | 12 ++++++++++++ kernel/time/timekeeping.c | 3 ++- 2 files changed, 14 insertions(+), 1 deletion(-)
arch/x86/kernel/rtc.c +12 -0
@@ -8,6 +8,7 @@#include <linux/export.h> #include <linux/pnp.h> #include <linux/of.h> +#include <linux/pm-trace.h> #include <asm/vsyscall.h> #include <asm/x86_init.h>
@@ -144,6 +145,17 @@ int update_persistent_clock(struct timespec now)void read_persistent_clock (struct timespec *ts) { x86_platform.get_wallclock (ts); + +/* +* Make rtc-based persistent clock unusable +* if pm_trace is enabled, only take effect +* for timekeeping_suspend/resume. +*/ +if (pm_trace_is_enabled () && + x86_platform.get_wallclock == mach_get_cmos_time) { + ts->tv_sec =0 ; + ts->tv_nsec =0 ; + } }
kernel/time/timekeeping.c +2 -1
@@ -23,6 +23,7 @@#include <linux/stop_machine.h> #include <linux/pvclock_gtod.h> #include <linux/compiler.h> +#include <linux/pm-trace.h> #include "tick-internal.h" #include "ntp_internal.h"
@@ -1551,7 +1552,7 @@ static void __timekeeping_inject_sleeptime(struct timekeeper *tk,*/ bool timekeeping_rtc_skipresume (void ) { -return sleeptime_injected; +return sleeptime_injected ||pm_trace_is_enabled (); }/**
Signature · 1 line
2.7.4
Quoted 49 lines · Chen Yu
Previously we encountered some memory overflow issues due to the bogus sleep time brought by inconsistent rtc, which is triggered when
pm_traceis enabled, and we have fixed it in recent kernel. However it's improper in the first place to call__timekeeping_inject_sleeptime()in case thatpm_traceis enabled simply because that "hash" time value will wreckage the timekeeping subsystem.So this patch ignores the sleep time if
pm_traceis enabled in the following situation:
1. rtc is used as persist clock to compensate for sleep time, or
2. rtc is used to calculate the sleep time inrtc_resume.Cc [email protected] (3.17+)Cc Rafael J. Wysocki <[email protected]>Cc John Stultz <[email protected]>Cc Thomas Gleixner <[email protected]>Cc Xunlei Pang <[email protected]>Cc Zhang Rui <[email protected]>Suggested-by Xunlei Pang <[email protected]>Suggested-by Rafael J. Wysocki <[email protected]>Suggested-by Thomas Gleixner <[email protected]>Reported-by Janek Kozicki <[email protected]>Signed-off-by Chen Yu <[email protected]>--- arch/x86/kernel/rtc.c | 12 ++++++++++++ kernel/time/timekeeping.c | 3 ++- 2 files changed, 14 insertions(+), 1 deletion(-)arch/x86/kernel/rtc.c +2
-0@@ -8,6 +8,7 @@#include <linux/export.h> #include <linux/pnp.h> #include <linux/of.h> +#include <linux/pm-trace.h> #include <asm/vsyscall.h> #include <asm/x86_init.h> @@ -144,6 +145,17 @@ int update_persistent_clock(struct timespec now)void read_persistent_clock (struct timespec *ts) { x86_platform.get_wallclock (ts); +
arch/x86/kernel/rtc.c int update_persistent_clock(struct timespec now) · line 158
Quoted 10 lines
arch/x86/kernel/rtc.c +10
-0+/* +* Make rtc-based persistent clock unusable +* if pm_trace is enabled, only take effect +* for timekeeping_suspend/resume. +*/ +if (pm_trace_is_enabled () && + x86_platform.get_wallclock == mach_get_cmos_time) { + ts->tv_sec =0 ; + ts->tv_nsec =0 ; + }
I'm not sure about this. Looks hackish.
Quoted 16 lines
arch/x86/kernel/rtc.c +0
-0}kernel/time/timekeeping.c +1
-0@@ -23,6 +23,7 @@#include <linux/stop_machine.h> #include <linux/pvclock_gtod.h> #include <linux/compiler.h> +#include <linux/pm-trace.h> #include "tick-internal.h" #include "ntp_internal.h"
kernel/time/timekeeping.c static void __timekeeping_inject_sleeptime(struct timekeeper *tk, · line 1558
Quoted 8 lines
kernel/time/timekeeping.c +1
-1@@ -1551,7 +1552,7 @@ static void __timekeeping_inject_sleeptime(struct timekeeper *tk,*/ bool timekeeping_rtc_skipresume (void ) { -return sleeptime_injected; +return sleeptime_injected ||pm_trace_is_enabled (); }/**
Thanks,
Rafael
Quoted 14 lines · Rafael J. Wysocki
arch/x86/kernel/rtc.c int update_persistent_clock(struct timespec now) · line 158
Quoted 11 lines · Chen Yu
arch/x86/kernel/rtc.c +11
-0+ +/* +* Make rtc-based persistent clock unusable +* if pm_trace is enabled, only take effect +* for timekeeping_suspend/resume. +*/ +if (pm_trace_is_enabled () && + x86_platform.get_wallclock == mach_get_cmos_time) { + ts->tv_sec =0 ; + ts->tv_nsec =0 ; + }I'm not sure about this. Looks hackish.
Indeed. Can't you just keep track that pm_trace fiddled with the cmos clock and then discard the value either in the core or in mach_get_cmos_time()
Thanks,
tglx
Hi Thomas, Rafael,
Quoted 16 lines · Thomas Gleixner
Quoted 14 lines · Rafael J. Wysocki
arch/x86/kernel/rtc.c int update_persistent_clock(struct timespec now) · line 158
Quoted 11 lines · Chen Yu
arch/x86/kernel/rtc.c +11
-0+ +/* +* Make rtc-based persistent clock unusable +* if pm_trace is enabled, only take effect +* for timekeeping_suspend/resume. +*/ +if (pm_trace_is_enabled () && + x86_platform.get_wallclock == mach_get_cmos_time) { + ts->tv_sec =0 ; + ts->tv_nsec =0 ; + }I'm not sure about this. Looks hackish.
Quoted 2 lines
Indeed. Can't you just keep track that
pm_tracefiddled with the cmos clock and then discard the value either in the core or inmach_get_cmos_time()
The previous version is more straightforward, since it ignored the bogus rtc in core. Would you please take a glance at it too, thanks:
https://patchwork.kernel.org/patch/9287347/
Thanks,
Yu
Quoted 18 lines · Chen Yu
Hi Thomas, Rafael,
Quoted 16 lines · Thomas Gleixner
Quoted 14 lines · Rafael J. Wysocki
arch/x86/kernel/rtc.c int update_persistent_clock(struct timespec now) · line 158
Quoted 11 lines · Chen Yu
arch/x86/kernel/rtc.c +11
-0+ +/* +* Make rtc-based persistent clock unusable +* if pm_trace is enabled, only take effect +* for timekeeping_suspend/resume. +*/ +if (pm_trace_is_enabled () && + x86_platform.get_wallclock == mach_get_cmos_time) { + ts->tv_sec =0 ; + ts->tv_nsec =0 ; + }I'm not sure about this. Looks hackish.
Quoted 6 lines
Quoted 2 lines
Indeed. Can't you just keep track that
pm_tracefiddled with the cmos clock and then discard the value either in the core or inmach_get_cmos_time()The previous version is more straightforward, since it ignored the bogus rtc in core. Would you please take a glance at it too, thanks:
https://patchwork.kernel.org/patch/9287347/
This is the same hackery just different:
Review
Quoted 6 lines
(quoted diff) +6
-0+bool persistent_clock_is_usable(void) +{ + /* Unusable if pm_trace is enabled. */ + return !((x86_platform.get_wallclock == mach_get_cmos_time) && + pm_trace_is_enabled()); +}
I really have no idea why this is burried in x86 land. The pm_trace hackery issues mc146818_set_time() to fiddle with the RTC. So any implementation of this is affected.
So that very piece of pmtrace code should keep track of the wreckage it did to the RTC and provide the fact to the core timekeeping code which can then skip the update.
Thanks,
tglx
Hi,
Quoted 28 lines · Thomas Gleixner
Quoted 18 lines · Chen Yu
Hi Thomas, Rafael,
Quoted 16 lines · Thomas Gleixner
Quoted 14 lines · Rafael J. Wysocki
Review
Quoted 11 lines · Chen Yu
(quoted diff) +11
-0+ + /* + * Make rtc-based persistent clock unusable + * if pm_trace is enabled, only take effect + * for timekeeping_suspend/resume. + */ + if (pm_trace_is_enabled() && + x86_platform.get_wallclock == mach_get_cmos_time) { + ts->tv_sec = 0; + ts->tv_nsec = 0; + }I'm not sure about this. Looks hackish.
Quoted 6 lines
Quoted 2 lines
Indeed. Can't you just keep track that
pm_tracefiddled with the cmos clock and then discard the value either in the core or inmach_get_cmos_time()The previous version is more straightforward, since it ignored the bogus rtc in core. Would you please take a glance at it too, thanks:
https://patchwork.kernel.org/patch/9287347/This is the same hackery just different:
Quoted 10 lines
Review
Quoted 6 lines
(quoted diff) +6
-0+bool persistent_clock_is_usable(void) +{ + /* Unusable if pm_trace is enabled. */ + return !((x86_platform.get_wallclock == mach_get_cmos_time) && + pm_trace_is_enabled()); +}I really have no idea why this is burried in x86 land. The
pm_tracehackery issuesmc146818_set_time()to fiddle with the RTC. So any implementation of this is affected.
OK, I've changed this patch according to this suggestion.
Previously I tried to deal with the case that x86 uses RTC-CMOS as its presistent clock, not kvm_clock, nor other get_wallclock, and this seems only be related to x86. So this piece of code looks hackish.
Quoted 5 lines
So that very piece of pmtrace code should keep track of the wreckage it did to the RTC and provide the fact to the core timekeeping code which can then skip the update.
OK. I've moved most of the logic into the pm_trace component, Once the mc146818_set_time has modified the RTC by pm_trace, related flags will be set which indicates the unusable of RTC. And timekeeping system is able to query these flags to decide whether it should inject the sleep time. (We tried to make this patch as simple as possible, but it looks like we have to deal with persistent clock for x86, which makes this patch a little more complicated). Here's the trial version of it, any suggestion would be appreciated:
drivers/base/power/trace.c +22 -0
@@ -75,6 +75,27 @@#define DEVSEED (7919)static unsigned int dev_hash_value; +unsigned int timekeeping_tainted; + +/* Is the persistent clock effected by pm_trace? */ +int __weak arch_pm_trace_taint_pclock (void ) +{ +return 0 ; +} + +void pm_trace_untaint_timekeeping (void ) +{ + timekeeping_tainted =0 ; +} + +void pm_trace_taint_timekeeping (void ) +{ +if (pm_trace_is_enabled ()) { + timekeeping_tainted |=TIMEKEEPING_RTC_TAINTED ; +if (arch_pm_trace_taint_pclock ()) + timekeeping_tainted |=TIMEKEEPING_PERSISTENT_CLOCK_TAINTED ; + } +}static int set_magic_time (unsigned int user,unsigned int file,unsigned int device) {
@@ -104,6 +125,7 @@ static int set_magic_time(unsigned int u time.tm_min = (n %20 ) *3 ; n /=20 ;mc146818_set_time (&time); +pm_trace_taint_timekeeping ();return n ?-1 :0 ; }
kernel/power/main.c +1 -0
@@ -548,6 +548,7 @@ pm_trace_store(struct kobject *kobj, strif (sscanf (buf,"%d" , &val) ==1 ) { pm_trace_enabled = !!val;if (pm_trace_enabled) { +pm_trace_untaint_timekeeping ();pr_warn ("PM: Enabling pm_trace changes system date and time during resume.\n" "PM: Correct system time has to be restored manually after resume.\n" ); }
include/linux/pm-trace.h +19 -0
@@ -1,10 +1,14 @@#ifndef PM_TRACE_H #define PM_TRACE_H +#define TIMEKEEPING_RTC_TAINTED 0x1 +#define TIMEKEEPING_PERSISTENT_CLOCK_TAINTED 0x2 +#ifdef CONFIG_PM_TRACE #include <asm/pm-trace.h> #include <linux/types.h> +extern unsigned int timekeeping_tainted;extern int pm_trace_enabled;static inline int pm_trace_is_enabled (void )
@@ -12,10 +16,23 @@ static inline int pm_trace_is_enabled(voreturn pm_trace_enabled; } +static inline int pm_trace_rtc_is_tainted (void ) +{ +return (timekeeping_tainted &TIMEKEEPING_RTC_TAINTED ) ? +1 :0 ; +} + +static inline int pm_trace_pclock_is_tainted (void ) +{ +return (timekeeping_tainted &TIMEKEEPING_PERSISTENT_CLOCK_TAINTED ) ? +1 :0 ; +} +struct device ;extern void set_trace_device (struct device *);extern void generate_pm_trace (const void *tracedata,unsigned int user);extern int show_trace_dev_match (char *buf,size_t size); +extern void pm_trace_untaint_timekeeping (void );#define TRACE_DEVICE (dev) do { \ if (pm_trace_enabled) \
@@ -25,6 +42,8 @@ extern int show_trace_dev_match(char *bu#else static inline int pm_trace_is_enabled (void ) {return 0 ; } +static inline int pm_trace_rtc_is_tainted (void ) {return 0 ; } +static inline int pm_trace_pclock_is_tainted (void ) {return 0 ; }#define TRACE_DEVICE (dev) do { } while (0)#define TRACE_RESUME (dev) do { } while (0)
arch/x86/kernel/rtc.c +5 -0
@@ -8,6 +8,7 @@#include <linux/export.h> #include <linux/pnp.h> #include <linux/of.h> +#include <linux/pm-trace.h> #include <asm/vsyscall.h> #include <asm/x86_init.h>
@@ -146,6 +147,10 @@ void read_persistent_clock(struct timesp x86_platform.get_wallclock (ts); } +int arch_pm_trace_taint_pclock (void ) +{ +return (x86_platform.get_wallclock == mach_get_cmos_time); +}static struct resource rtc_resources[] = { [0 ] = {
kernel/time/timekeeping.c +3 -2
@@ -23,6 +23,7 @@#include <linux/stop_machine.h> #include <linux/pvclock_gtod.h> #include <linux/compiler.h> +#include <linux/pm-trace.h> #include "tick-internal.h" #include "ntp_internal.h"
@@ -1554,7 +1555,7 @@ static void __timekeeping_inject_sleepti*/ bool timekeeping_rtc_skipresume (void ) { -return sleeptime_injected; +return sleeptime_injected ||pm_trace_rtc_is_tainted (); }/**
@@ -1664,7 +1665,7 @@ void timekeeping_resume(void) sleeptime_injected = true; }else if (timespec64_compare (&ts_new, &timekeeping_suspend_time) >0 ) { ts_delta =timespec64_sub (ts_new, timekeeping_suspend_time); - sleeptime_injected = true; + sleeptime_injected =pm_trace_pclock_is_tainted () ? false : true; }if (sleeptime_injected)
Quoted 4 lines · Chen Yu
Quoted 3 lines
I really have no idea why this is burried in x86 land. The
pm_tracehackery issuesmc146818_set_time()to fiddle with the RTC. So any implementation of this is affected.OK, I've changed this patch according to this suggestion.
No you did not! You still have that silly x86 hackery in place.
Quoted 8 lines
OK. I've moved most of the logic into the
pm_tracecomponent, Once themc146818_set_timehas modified the RTC bypm_trace, related flags will be set which indicates the unusable of RTC. And timekeeping system is able to query these flags to decide whether it should inject the sleep time. (We tried to make this patch as simple as possible, but it looks like we have to deal with persistent clock for x86, which makes this patch a little more complicated). Here's the trial version of it, any suggestion would be appreciated:
It's overengineered and horrible.
Quoted 1 line
+unsigned int
timekeeping_tainted;
It does not taint timekeeping. It just wreckages the RTC.
Review
Quoted 7 lines
(quoted diff) +7
-0+void pm_trace_taint_timekeeping(void) +{ + if (pm_trace_is_enabled()) { + timekeeping_tainted |= TIMEKEEPING_RTC_TAINTED; + if (arch_pm_trace_taint_pclock()) + timekeeping_tainted |= TIMEKEEPING_PERSISTENT_CLOCK_TAINTED; + }
Why would you need all these flags?
Review
Quoted 4 lines
(quoted diff) +4
-0+static inline int pm_trace_rtc_is_tainted(void) +{ + return (timekeeping_tainted & TIMEKEEPING_RTC_TAINTED) ? + 1 : 0;
ever heard about bool?
Review
Quoted 4 lines
(quoted diff) +3
-0+} + +extern void pm_trace_untaint_timekeeping(void);
And how exactly do you untaint it? Just by clearing the flags. That makes the RTC time magically correct again?
Review
Quoted 4 lines
(quoted diff) +4
-0+int arch_pm_trace_taint_pclock(void) +{ + return (x86_platform.get_wallclock == mach_get_cmos_time); +}
Groan. I told you to do it in the mc14xxx related places. There are not that many in the kernel
Here is a completely uncompiled/untested patch which should address the issue in a halfways clean way.
Thanks,
tglx
arch/x86/kernel/rtc.c +9 -0
@@ -64,6 +64,15 @@ void mach_get_cmos_time(struct timespecunsigned int status, year, mon, day, hour, min, sec, century =0 ;unsigned long flags; +/* +* If pm trace abused the RTC as storage set the timespec to 0 +* which tells the caller that this RTC value is bogus. +*/ +if (!pm_trace_rtc_valid ()) { + now->tv_sec = now->tv_nsec =0 ; +return ; + } +spin_lock_irqsave (&rtc_lock, flags);/*
drivers/base/power/trace.c +2 -0
@@ -74,6 +74,7 @@#define DEVSEED (7919) +bool pm_trace_rtc_abused__read_mostly ;static unsigned int dev_hash_value;static int set_magic_time (unsigned int user,unsigned int file,unsigned int device)
@@ -104,6 +105,7 @@ static int set_magic_time(unsigned int u time.tm_min = (n %20 ) *3 ; n /=20 ;mc146818_set_time (&time); + pm_trace_rtc_abused = true;return n ?-1 :0 ; }
drivers/rtc/rtc-cmos.c +7 -0
@@ -189,6 +189,13 @@ static inline void cmos_write_bank2(unsistatic int cmos_read_time (struct device *dev,struct rtc_time *t) { +/* +* If pmtrace abused the RTC for storage tell the caller that it is +* unusable. +*/ +if (!pm_trace_rtc_valid ()) +return -EIO ; +/* REVISIT: if the clock has a "century" register, use * that instead of the heuristic in mc146818_get_time(). * That'll make Y3K compatility (year > 2070) easy!
include/linux/mc146818rtc.h +1 -0
@@ -16,6 +16,7 @@#include <asm/mc146818rtc.h> /* register access macros */ #include <linux/bcd.h> #include <linux/delay.h> +#include <linux/pm-trace.h> #ifdef __KERNEL__#include <linux/spinlock.h> /* spinlock_t */
include/linux/pm-trace.h +7 -0
@@ -6,6 +6,12 @@#include <linux/types.h> extern int pm_trace_enabled; +extern bool pm_trace_rtc_abused; + +static inline bool pm_trace_rtc_valid (void ) +{ +return !pm_trace_rtc_abused; +}static inline int pm_trace_is_enabled (void ) {
@@ -24,6 +30,7 @@ extern int show_trace_dev_match(char *bu#else +static inline bool pm_trace_rtc_valid (void ) {return true; }static inline int pm_trace_is_enabled (void ) {return 0 ; }#define TRACE_DEVICE (dev) do { } while (0)
Hi,
Quoted 15 lines · Thomas Gleixner
Quoted 1 line · Chen Yu
+extern void
pm_trace_untaint_timekeeping(void);And how exactly do you untaint it? Just by clearing the flags. That makes the RTC time magically correct again?
include/linux/pm-trace.h line 14
Quoted 4 lines
include/linux/pm-trace.h +4
-0+int arch_pm_trace_taint_pclock (void ) +{ +return (x86_platform.get_wallclock == mach_get_cmos_time); +}Groan. I told you to do it in the mc14xxx related places. There are not that many in the kernel
Quoted 7 lines
Here is a completely uncompiled/untested patch which should address the issue in a halfways clean way.
Thanks,
tglx
OK, I made some small adjustment to make it compiled without pm_trace configured, and this version is absolutely more professional than my previous one. I'll do some testing based on it.
Besides I have another question related to the untain
of the pm_trace_rtc_abused flag: after resume back, if the user disabled the pm_trace, then the following suspend/resume sleep time should become valid again IMO, because we use the delta rather than the RTC itself for suspend/resume. So in this version it disable the injection of sleep time once pm_trace has been used?
Thanks,
Yu