From: Tony Breeds Date: Thu, 18 Oct 2007 10:04:57 +0000 (-0700) Subject: Fix discrepancy between VDSO based gettimeofday() and sys_gettimeofday(). X-Git-Tag: v2.6.24-rc1~416 X-Git-Url: http://pilppa.com/gitweb/?a=commitdiff_plain;h=2c6221483169ddd4c04797cd7296ed4fe52fcdd7;p=linux-2.6-omap-h63xx.git Fix discrepancy between VDSO based gettimeofday() and sys_gettimeofday(). On platforms that copy sys_tz into the vdso (currently only x86_64, soon to include powerpc), it is possible for the vdso to get out of sync if a user calls (admittedly unusual) settimeofday(NULL, ptr). This patch adds a hook for architectures that set CONFIG_GENERIC_TIME_VSYSCALL to ensure when sys_tz is updated they can also updatee their copy in the vdso. Signed-off-by: Tony Breeds Cc: Andi Kleen Cc: Tony Luck Acked-by: John Stultz Cc: Paul Mackerras Cc: Benjamin Herrenschmidt Cc: Thomas Gleixner Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- diff --git a/arch/ia64/kernel/time.c b/arch/ia64/kernel/time.c index 98cfc90cab1..2bb84214e5f 100644 --- a/arch/ia64/kernel/time.c +++ b/arch/ia64/kernel/time.c @@ -371,6 +371,11 @@ ia64_setup_printk_clock(void) ia64_printk_clock = ia64_itc_printk_clock; } +/* IA64 doesn't cache the timezone */ +void update_vsyscall_tz(void) +{ +} + void update_vsyscall(struct timespec *wall, struct clocksource *c) { unsigned long flags; diff --git a/arch/x86/kernel/vsyscall_64.c b/arch/x86/kernel/vsyscall_64.c index 8a67e282cb5..f24e8acdec9 100644 --- a/arch/x86/kernel/vsyscall_64.c +++ b/arch/x86/kernel/vsyscall_64.c @@ -64,6 +64,16 @@ struct vsyscall_gtod_data __vsyscall_gtod_data __section_vsyscall_gtod_data = .sysctl_enabled = 1, }; +void update_vsyscall_tz(void) +{ + unsigned long flags; + + write_seqlock_irqsave(&vsyscall_gtod_data.lock, flags); + /* sys_tz has changed */ + vsyscall_gtod_data.sys_tz = sys_tz; + write_sequnlock_irqrestore(&vsyscall_gtod_data.lock, flags); +} + void update_vsyscall(struct timespec *wall_time, struct clocksource *clock) { unsigned long flags; @@ -77,7 +87,6 @@ void update_vsyscall(struct timespec *wall_time, struct clocksource *clock) vsyscall_gtod_data.clock.shift = clock->shift; vsyscall_gtod_data.wall_time_sec = wall_time->tv_sec; vsyscall_gtod_data.wall_time_nsec = wall_time->tv_nsec; - vsyscall_gtod_data.sys_tz = sys_tz; vsyscall_gtod_data.wall_to_monotonic = wall_to_monotonic; write_sequnlock_irqrestore(&vsyscall_gtod_data.lock, flags); } diff --git a/include/linux/clocksource.h b/include/linux/clocksource.h index 16ea3374ddd..107787aacb6 100644 --- a/include/linux/clocksource.h +++ b/include/linux/clocksource.h @@ -221,10 +221,15 @@ extern void clocksource_resume(void); #ifdef CONFIG_GENERIC_TIME_VSYSCALL extern void update_vsyscall(struct timespec *ts, struct clocksource *c); +extern void update_vsyscall_tz(void); #else static inline void update_vsyscall(struct timespec *ts, struct clocksource *c) { } + +static inline void update_vsyscall_tz(void) +{ +} #endif #endif /* _LINUX_CLOCKSOURCE_H */ diff --git a/kernel/time.c b/kernel/time.c index 2d5b6a68213..d9725bdcd04 100644 --- a/kernel/time.c +++ b/kernel/time.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -158,6 +159,7 @@ int do_sys_settimeofday(struct timespec *tv, struct timezone *tz) if (tz) { /* SMP safe, global irq locking makes it work. */ sys_tz = *tz; + update_vsyscall_tz(); if (firsttime) { firsttime = 0; if (!tv)