2009-11-17 06:05:52

by Lin Ming

[permalink] [raw]
Subject: [PATCH] Fix clock_gettime vsyscall time warp

Hi, all

clock_gettime vsyscall time warp was seen on a x86_64 machine with 16
logical cpus and an IA64 machine with 32 logical CPUS.

time-warp-test was downloaded from
http://people.redhat.com/mingo/time-warp-test/

I wrote a simple patch to port this test tool to non-x86 platform.
See the attachment time-warp-test.non-x86.patch.

[x86_64]$ ./time-warp-test 4
16 CPUs, running 4 parallel test-tasks.
checking for time-warps via:
- clock_gettime(CLOCK_MONOTONIC) syscall (nsec resolution)

new CLOCK-warp maximum: -120 nsecs, 00000025c337c537 -> 00000025c337c4bf
new CLOCK-warp maximum: -147 nsecs, 00000025e1052a7d -> 00000025e10529ea
new CLOCK-warp maximum: -174 nsecs, 0000002693d58856 -> 0000002693d587a8
new CLOCK-warp maximum: -176 nsecs, 000000270b0b1c41 -> 000000270b0b1b91
new CLOCK-warp maximum: -206 nsecs, 0000002746a5e740 -> 0000002746a5e672
new CLOCK-warp maximum: -209 nsecs, 000000295f170082 -> 000000295f16ffb1
| CLK: 0.13us, fail:52 /

[ia64]$ ./time-warp-test
32 CPUs, running 32 parallel test-tasks.
checking for time-warps via:
- clock_gettime(CLOCK_MONOTONIC) syscall (nsec resolution)

new CLOCK-warp maximum: -54 nsecs, 0000001a8178a9c1 -> 0000001a8178a98b
new CLOCK-warp maximum: -55 nsecs, 0000001a8d2769fe -> 0000001a8d2769c7
| CLK: 2.60us, fail:5 |

The root cause is the NTP adjusted clock multiplier(timekeeper.mult) is
not updated to vsyscall gtod data(vsyscall_gtod_data.clock.mult).

Since commit 0a544198 "timekeeping: Move NTP adjusted clock multiplier
to struct timekeeper", clock->mult is only the unmodified multiplier.

Below patch passes the adjusted clock multiplier to update_vsyscall.
-void update_vsyscall(struct timespec *wall_time, struct clocksource *clock)
+void update_vsyscall(struct timespec *wall_time, struct clocksource *clock, u32 mult)

With this patch applied to 2.6.32-rc7, I have been running
time-warp-test for many hours without any time warp on both x86_64 and
ia64 machines.

This patch also touches the powerpc code, so it would be very
appreciated if anyone can help to test it on powerpc.

Signed-off-by: Lin Ming <[email protected]>
---
arch/ia64/kernel/time.c | 4 ++--
arch/powerpc/kernel/time.c | 4 ++--
arch/s390/kernel/time.c | 2 +-
arch/x86/kernel/vsyscall_64.c | 4 ++--
include/linux/clocksource.h | 4 ++--
kernel/time/timekeeping.c | 6 +++---
6 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/arch/ia64/kernel/time.c b/arch/ia64/kernel/time.c
index 4990495..a35c661 100644
--- a/arch/ia64/kernel/time.c
+++ b/arch/ia64/kernel/time.c
@@ -473,7 +473,7 @@ void update_vsyscall_tz(void)
{
}

-void update_vsyscall(struct timespec *wall, struct clocksource *c)
+void update_vsyscall(struct timespec *wall, struct clocksource *c, u32 mult)
{
unsigned long flags;

@@ -481,7 +481,7 @@ void update_vsyscall(struct timespec *wall, struct clocksource *c)

/* copy fsyscall clock data */
fsyscall_gtod_data.clk_mask = c->mask;
- fsyscall_gtod_data.clk_mult = c->mult;
+ fsyscall_gtod_data.clk_mult = mult;
fsyscall_gtod_data.clk_shift = c->shift;
fsyscall_gtod_data.clk_fsys_mmio = c->fsys_mmio;
fsyscall_gtod_data.clk_cycle_last = c->cycle_last;
diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c
index a136a11..ae4d55f 100644
--- a/arch/powerpc/kernel/time.c
+++ b/arch/powerpc/kernel/time.c
@@ -828,7 +828,7 @@ static cycle_t timebase_read(struct clocksource *cs)
return (cycle_t)get_tb();
}

-void update_vsyscall(struct timespec *wall_time, struct clocksource *clock)
+void update_vsyscall(struct timespec *wall_time, struct clocksource *clock, u32 mult)
{
u64 t2x, stamp_xsec;

@@ -841,7 +841,7 @@ void update_vsyscall(struct timespec *wall_time, struct clocksource *clock)

/* XXX this assumes clock->shift == 22 */
/* 4611686018 ~= 2^(20+64-22) / 1e9 */
- t2x = (u64) clock->mult * 4611686018ULL;
+ t2x = (u64) mult * 4611686018ULL;
stamp_xsec = (u64) xtime.tv_nsec * XSEC_PER_SEC;
do_div(stamp_xsec, 1000000000);
stamp_xsec += (u64) xtime.tv_sec * XSEC_PER_SEC;
diff --git a/arch/s390/kernel/time.c b/arch/s390/kernel/time.c
index 34162a0..7b1a3a6 100644
--- a/arch/s390/kernel/time.c
+++ b/arch/s390/kernel/time.c
@@ -214,7 +214,7 @@ struct clocksource * __init clocksource_default_clock(void)
return &clocksource_tod;
}

-void update_vsyscall(struct timespec *wall_time, struct clocksource *clock)
+void update_vsyscall(struct timespec *wall_time, struct clocksource *clock, u32 mult)
{
if (clock != &clocksource_tod)
return;
diff --git a/arch/x86/kernel/vsyscall_64.c b/arch/x86/kernel/vsyscall_64.c
index 8cb4974..0d156eb 100644
--- a/arch/x86/kernel/vsyscall_64.c
+++ b/arch/x86/kernel/vsyscall_64.c
@@ -73,7 +73,7 @@ void update_vsyscall_tz(void)
write_sequnlock_irqrestore(&vsyscall_gtod_data.lock, flags);
}

-void update_vsyscall(struct timespec *wall_time, struct clocksource *clock)
+void update_vsyscall(struct timespec *wall_time, struct clocksource *clock, u32 mult)
{
unsigned long flags;

@@ -82,7 +82,7 @@ void update_vsyscall(struct timespec *wall_time, struct clocksource *clock)
vsyscall_gtod_data.clock.vread = clock->vread;
vsyscall_gtod_data.clock.cycle_last = clock->cycle_last;
vsyscall_gtod_data.clock.mask = clock->mask;
- vsyscall_gtod_data.clock.mult = clock->mult;
+ vsyscall_gtod_data.clock.mult = mult;
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;
diff --git a/include/linux/clocksource.h b/include/linux/clocksource.h
index 83d2fbd..b18e8a2 100644
--- a/include/linux/clocksource.h
+++ b/include/linux/clocksource.h
@@ -280,10 +280,10 @@ extern struct clocksource * __init __weak clocksource_default_clock(void);
extern void clocksource_mark_unstable(struct clocksource *cs);

#ifdef CONFIG_GENERIC_TIME_VSYSCALL
-extern void update_vsyscall(struct timespec *ts, struct clocksource *c);
+extern void update_vsyscall(struct timespec *ts, struct clocksource *c, u32 mult);
extern void update_vsyscall_tz(void);
#else
-static inline void update_vsyscall(struct timespec *ts, struct clocksource *c)
+static inline void update_vsyscall(struct timespec *ts, struct clocksource *c, u32 mult)
{
}

diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c
index c3a4e29..2a6d3e3 100644
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -177,7 +177,7 @@ void timekeeping_leap_insert(int leapsecond)
{
xtime.tv_sec += leapsecond;
wall_to_monotonic.tv_sec -= leapsecond;
- update_vsyscall(&xtime, timekeeper.clock);
+ update_vsyscall(&xtime, timekeeper.clock, timekeeper.mult);
}

#ifdef CONFIG_GENERIC_TIME
@@ -337,7 +337,7 @@ int do_settimeofday(struct timespec *tv)
timekeeper.ntp_error = 0;
ntp_clear();

- update_vsyscall(&xtime, timekeeper.clock);
+ update_vsyscall(&xtime, timekeeper.clock, timekeeper.mult);

write_sequnlock_irqrestore(&xtime_lock, flags);

@@ -811,7 +811,7 @@ void update_wall_time(void)
update_xtime_cache(nsecs);

/* check to see if there is a new clocksource to use */
- update_vsyscall(&xtime, timekeeper.clock);
+ update_vsyscall(&xtime, timekeeper.clock, timekeeper.mult);
}

/**


2009-11-17 06:14:44

by Lin Ming

[permalink] [raw]
Subject: Re: [PATCH] Fix clock_gettime vsyscall time warp

On Tue, 2009-11-17 at 13:49 +0800, Lin Ming wrote:
> Hi, all
>
> clock_gettime vsyscall time warp was seen on a x86_64 machine with 16
> logical cpus and an IA64 machine with 32 logical CPUS.
>
> time-warp-test was downloaded from
> http://people.redhat.com/mingo/time-warp-test/
>
> I wrote a simple patch to port this test tool to non-x86 platform.
> See the attachment time-warp-test.non-x86.patch.

Forgot the attachment.
Attached.

Lin Ming

>
> [x86_64]$ ./time-warp-test 4
> 16 CPUs, running 4 parallel test-tasks.
> checking for time-warps via:
> - clock_gettime(CLOCK_MONOTONIC) syscall (nsec resolution)
>
> new CLOCK-warp maximum: -120 nsecs, 00000025c337c537 -> 00000025c337c4bf
> new CLOCK-warp maximum: -147 nsecs, 00000025e1052a7d -> 00000025e10529ea
> new CLOCK-warp maximum: -174 nsecs, 0000002693d58856 -> 0000002693d587a8
> new CLOCK-warp maximum: -176 nsecs, 000000270b0b1c41 -> 000000270b0b1b91
> new CLOCK-warp maximum: -206 nsecs, 0000002746a5e740 -> 0000002746a5e672
> new CLOCK-warp maximum: -209 nsecs, 000000295f170082 -> 000000295f16ffb1
> | CLK: 0.13us, fail:52 /
>
> [ia64]$ ./time-warp-test
> 32 CPUs, running 32 parallel test-tasks.
> checking for time-warps via:
> - clock_gettime(CLOCK_MONOTONIC) syscall (nsec resolution)
>
> new CLOCK-warp maximum: -54 nsecs, 0000001a8178a9c1 -> 0000001a8178a98b
> new CLOCK-warp maximum: -55 nsecs, 0000001a8d2769fe -> 0000001a8d2769c7
> | CLK: 2.60us, fail:5 |
>
> The root cause is the NTP adjusted clock multiplier(timekeeper.mult) is
> not updated to vsyscall gtod data(vsyscall_gtod_data.clock.mult).
>
> Since commit 0a544198 "timekeeping: Move NTP adjusted clock multiplier
> to struct timekeeper", clock->mult is only the unmodified multiplier.
>
> Below patch passes the adjusted clock multiplier to update_vsyscall.
> -void update_vsyscall(struct timespec *wall_time, struct clocksource *clock)
> +void update_vsyscall(struct timespec *wall_time, struct clocksource *clock, u32 mult)
>
> With this patch applied to 2.6.32-rc7, I have been running
> time-warp-test for many hours without any time warp on both x86_64 and
> ia64 machines.
>
> This patch also touches the powerpc code, so it would be very
> appreciated if anyone can help to test it on powerpc.
>
> Signed-off-by: Lin Ming <[email protected]>
> ---
> arch/ia64/kernel/time.c | 4 ++--
> arch/powerpc/kernel/time.c | 4 ++--
> arch/s390/kernel/time.c | 2 +-
> arch/x86/kernel/vsyscall_64.c | 4 ++--
> include/linux/clocksource.h | 4 ++--
> kernel/time/timekeeping.c | 6 +++---
> 6 files changed, 12 insertions(+), 12 deletions(-)
>
> diff --git a/arch/ia64/kernel/time.c b/arch/ia64/kernel/time.c
> index 4990495..a35c661 100644
> --- a/arch/ia64/kernel/time.c
> +++ b/arch/ia64/kernel/time.c
> @@ -473,7 +473,7 @@ void update_vsyscall_tz(void)
> {
> }
>
> -void update_vsyscall(struct timespec *wall, struct clocksource *c)
> +void update_vsyscall(struct timespec *wall, struct clocksource *c, u32 mult)
> {
> unsigned long flags;
>
> @@ -481,7 +481,7 @@ void update_vsyscall(struct timespec *wall, struct clocksource *c)
>
> /* copy fsyscall clock data */
> fsyscall_gtod_data.clk_mask = c->mask;
> - fsyscall_gtod_data.clk_mult = c->mult;
> + fsyscall_gtod_data.clk_mult = mult;
> fsyscall_gtod_data.clk_shift = c->shift;
> fsyscall_gtod_data.clk_fsys_mmio = c->fsys_mmio;
> fsyscall_gtod_data.clk_cycle_last = c->cycle_last;
> diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c
> index a136a11..ae4d55f 100644
> --- a/arch/powerpc/kernel/time.c
> +++ b/arch/powerpc/kernel/time.c
> @@ -828,7 +828,7 @@ static cycle_t timebase_read(struct clocksource *cs)
> return (cycle_t)get_tb();
> }
>
> -void update_vsyscall(struct timespec *wall_time, struct clocksource *clock)
> +void update_vsyscall(struct timespec *wall_time, struct clocksource *clock, u32 mult)
> {
> u64 t2x, stamp_xsec;
>
> @@ -841,7 +841,7 @@ void update_vsyscall(struct timespec *wall_time, struct clocksource *clock)
>
> /* XXX this assumes clock->shift == 22 */
> /* 4611686018 ~= 2^(20+64-22) / 1e9 */
> - t2x = (u64) clock->mult * 4611686018ULL;
> + t2x = (u64) mult * 4611686018ULL;
> stamp_xsec = (u64) xtime.tv_nsec * XSEC_PER_SEC;
> do_div(stamp_xsec, 1000000000);
> stamp_xsec += (u64) xtime.tv_sec * XSEC_PER_SEC;
> diff --git a/arch/s390/kernel/time.c b/arch/s390/kernel/time.c
> index 34162a0..7b1a3a6 100644
> --- a/arch/s390/kernel/time.c
> +++ b/arch/s390/kernel/time.c
> @@ -214,7 +214,7 @@ struct clocksource * __init clocksource_default_clock(void)
> return &clocksource_tod;
> }
>
> -void update_vsyscall(struct timespec *wall_time, struct clocksource *clock)
> +void update_vsyscall(struct timespec *wall_time, struct clocksource *clock, u32 mult)
> {
> if (clock != &clocksource_tod)
> return;
> diff --git a/arch/x86/kernel/vsyscall_64.c b/arch/x86/kernel/vsyscall_64.c
> index 8cb4974..0d156eb 100644
> --- a/arch/x86/kernel/vsyscall_64.c
> +++ b/arch/x86/kernel/vsyscall_64.c
> @@ -73,7 +73,7 @@ void update_vsyscall_tz(void)
> write_sequnlock_irqrestore(&vsyscall_gtod_data.lock, flags);
> }
>
> -void update_vsyscall(struct timespec *wall_time, struct clocksource *clock)
> +void update_vsyscall(struct timespec *wall_time, struct clocksource *clock, u32 mult)
> {
> unsigned long flags;
>
> @@ -82,7 +82,7 @@ void update_vsyscall(struct timespec *wall_time, struct clocksource *clock)
> vsyscall_gtod_data.clock.vread = clock->vread;
> vsyscall_gtod_data.clock.cycle_last = clock->cycle_last;
> vsyscall_gtod_data.clock.mask = clock->mask;
> - vsyscall_gtod_data.clock.mult = clock->mult;
> + vsyscall_gtod_data.clock.mult = mult;
> 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;
> diff --git a/include/linux/clocksource.h b/include/linux/clocksource.h
> index 83d2fbd..b18e8a2 100644
> --- a/include/linux/clocksource.h
> +++ b/include/linux/clocksource.h
> @@ -280,10 +280,10 @@ extern struct clocksource * __init __weak clocksource_default_clock(void);
> extern void clocksource_mark_unstable(struct clocksource *cs);
>
> #ifdef CONFIG_GENERIC_TIME_VSYSCALL
> -extern void update_vsyscall(struct timespec *ts, struct clocksource *c);
> +extern void update_vsyscall(struct timespec *ts, struct clocksource *c, u32 mult);
> extern void update_vsyscall_tz(void);
> #else
> -static inline void update_vsyscall(struct timespec *ts, struct clocksource *c)
> +static inline void update_vsyscall(struct timespec *ts, struct clocksource *c, u32 mult)
> {
> }
>
> diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c
> index c3a4e29..2a6d3e3 100644
> --- a/kernel/time/timekeeping.c
> +++ b/kernel/time/timekeeping.c
> @@ -177,7 +177,7 @@ void timekeeping_leap_insert(int leapsecond)
> {
> xtime.tv_sec += leapsecond;
> wall_to_monotonic.tv_sec -= leapsecond;
> - update_vsyscall(&xtime, timekeeper.clock);
> + update_vsyscall(&xtime, timekeeper.clock, timekeeper.mult);
> }
>
> #ifdef CONFIG_GENERIC_TIME
> @@ -337,7 +337,7 @@ int do_settimeofday(struct timespec *tv)
> timekeeper.ntp_error = 0;
> ntp_clear();
>
> - update_vsyscall(&xtime, timekeeper.clock);
> + update_vsyscall(&xtime, timekeeper.clock, timekeeper.mult);
>
> write_sequnlock_irqrestore(&xtime_lock, flags);
>
> @@ -811,7 +811,7 @@ void update_wall_time(void)
> update_xtime_cache(nsecs);
>
> /* check to see if there is a new clocksource to use */
> - update_vsyscall(&xtime, timekeeper.clock);
> + update_vsyscall(&xtime, timekeeper.clock, timekeeper.mult);
> }
>
> /**
>


Attachments:
time-warp-test.non-x86.patch (3.11 kB)

2009-11-17 08:59:51

by Martin Schwidefsky

[permalink] [raw]
Subject: Re: [PATCH] Fix clock_gettime vsyscall time warp

On Tue, 17 Nov 2009 13:49:50 +0800
Lin Ming <[email protected]> wrote:

> The root cause is the NTP adjusted clock multiplier(timekeeper.mult) is
> not updated to vsyscall gtod data(vsyscall_gtod_data.clock.mult).
>
> Since commit 0a544198 "timekeeping: Move NTP adjusted clock multiplier
> to struct timekeeper", clock->mult is only the unmodified multiplier.
>
> Below patch passes the adjusted clock multiplier to update_vsyscall.
> -void update_vsyscall(struct timespec *wall_time, struct clocksource *clock)
> +void update_vsyscall(struct timespec *wall_time, struct clocksource *clock, u32 mult)
>
> With this patch applied to 2.6.32-rc7, I have been running
> time-warp-test for many hours without any time warp on both x86_64 and
> ia64 machines.
>
> This patch also touches the powerpc code, so it would be very
> appreciated if anyone can help to test it on powerpc.

Ouch! Yes, good catch. That patch needs to go into 2.6.32.

--
blue skies,
Martin.

"Reality continues to ruin my life." - Calvin.

2009-11-17 10:55:23

by Lin Ming

[permalink] [raw]
Subject: [tip:timers/urgent] timekeeping: Fix clock_gettime vsyscall time warp

Commit-ID: 0696b711e4be45fa104c12329f617beb29c03f78
Gitweb: http://git.kernel.org/tip/0696b711e4be45fa104c12329f617beb29c03f78
Author: Lin Ming <[email protected]>
AuthorDate: Tue, 17 Nov 2009 13:49:50 +0800
Committer: Thomas Gleixner <[email protected]>
CommitDate: Tue, 17 Nov 2009 11:52:34 +0100

timekeeping: Fix clock_gettime vsyscall time warp

Since commit 0a544198 "timekeeping: Move NTP adjusted clock multiplier
to struct timekeeper" the clock multiplier of vsyscall is updated with
the unmodified clock multiplier of the clock source and not with the
NTP adjusted multiplier of the timekeeper.

This causes user space observerable time warps:
new CLOCK-warp maximum: 120 nsecs, 00000025c337c537 -> 00000025c337c4bf

Add a new argument "mult" to update_vsyscall() and hand in the
timekeeping internal NTP adjusted multiplier.

Signed-off-by: Lin Ming <[email protected]>
Cc: "Zhang Yanmin" <[email protected]>
Cc: Martin Schwidefsky <[email protected]>
Cc: Benjamin Herrenschmidt <[email protected]>
Cc: Tony Luck <[email protected]>
LKML-Reference: <[email protected]>
Signed-off-by: Thomas Gleixner <[email protected]>
---
arch/ia64/kernel/time.c | 4 ++--
arch/powerpc/kernel/time.c | 5 +++--
arch/s390/kernel/time.c | 3 ++-
arch/x86/kernel/vsyscall_64.c | 5 +++--
include/linux/clocksource.h | 6 ++++--
kernel/time/timekeeping.c | 6 +++---
6 files changed, 17 insertions(+), 12 deletions(-)

diff --git a/arch/ia64/kernel/time.c b/arch/ia64/kernel/time.c
index 4990495..a35c661 100644
--- a/arch/ia64/kernel/time.c
+++ b/arch/ia64/kernel/time.c
@@ -473,7 +473,7 @@ void update_vsyscall_tz(void)
{
}

-void update_vsyscall(struct timespec *wall, struct clocksource *c)
+void update_vsyscall(struct timespec *wall, struct clocksource *c, u32 mult)
{
unsigned long flags;

@@ -481,7 +481,7 @@ void update_vsyscall(struct timespec *wall, struct clocksource *c)

/* copy fsyscall clock data */
fsyscall_gtod_data.clk_mask = c->mask;
- fsyscall_gtod_data.clk_mult = c->mult;
+ fsyscall_gtod_data.clk_mult = mult;
fsyscall_gtod_data.clk_shift = c->shift;
fsyscall_gtod_data.clk_fsys_mmio = c->fsys_mmio;
fsyscall_gtod_data.clk_cycle_last = c->cycle_last;
diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c
index a136a11..3971331 100644
--- a/arch/powerpc/kernel/time.c
+++ b/arch/powerpc/kernel/time.c
@@ -828,7 +828,8 @@ static cycle_t timebase_read(struct clocksource *cs)
return (cycle_t)get_tb();
}

-void update_vsyscall(struct timespec *wall_time, struct clocksource *clock)
+void update_vsyscall(struct timespec *wall_time, struct clocksource *clock,
+ u32 mult)
{
u64 t2x, stamp_xsec;

@@ -841,7 +842,7 @@ void update_vsyscall(struct timespec *wall_time, struct clocksource *clock)

/* XXX this assumes clock->shift == 22 */
/* 4611686018 ~= 2^(20+64-22) / 1e9 */
- t2x = (u64) clock->mult * 4611686018ULL;
+ t2x = (u64) mult * 4611686018ULL;
stamp_xsec = (u64) xtime.tv_nsec * XSEC_PER_SEC;
do_div(stamp_xsec, 1000000000);
stamp_xsec += (u64) xtime.tv_sec * XSEC_PER_SEC;
diff --git a/arch/s390/kernel/time.c b/arch/s390/kernel/time.c
index 34162a0..68e1ecf 100644
--- a/arch/s390/kernel/time.c
+++ b/arch/s390/kernel/time.c
@@ -214,7 +214,8 @@ struct clocksource * __init clocksource_default_clock(void)
return &clocksource_tod;
}

-void update_vsyscall(struct timespec *wall_time, struct clocksource *clock)
+void update_vsyscall(struct timespec *wall_time, struct clocksource *clock,
+ u32 mult)
{
if (clock != &clocksource_tod)
return;
diff --git a/arch/x86/kernel/vsyscall_64.c b/arch/x86/kernel/vsyscall_64.c
index 8cb4974..62f39d7 100644
--- a/arch/x86/kernel/vsyscall_64.c
+++ b/arch/x86/kernel/vsyscall_64.c
@@ -73,7 +73,8 @@ void update_vsyscall_tz(void)
write_sequnlock_irqrestore(&vsyscall_gtod_data.lock, flags);
}

-void update_vsyscall(struct timespec *wall_time, struct clocksource *clock)
+void update_vsyscall(struct timespec *wall_time, struct clocksource *clock,
+ u32 mult)
{
unsigned long flags;

@@ -82,7 +83,7 @@ void update_vsyscall(struct timespec *wall_time, struct clocksource *clock)
vsyscall_gtod_data.clock.vread = clock->vread;
vsyscall_gtod_data.clock.cycle_last = clock->cycle_last;
vsyscall_gtod_data.clock.mask = clock->mask;
- vsyscall_gtod_data.clock.mult = clock->mult;
+ vsyscall_gtod_data.clock.mult = mult;
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;
diff --git a/include/linux/clocksource.h b/include/linux/clocksource.h
index 83d2fbd..95e4995 100644
--- a/include/linux/clocksource.h
+++ b/include/linux/clocksource.h
@@ -280,10 +280,12 @@ extern struct clocksource * __init __weak clocksource_default_clock(void);
extern void clocksource_mark_unstable(struct clocksource *cs);

#ifdef CONFIG_GENERIC_TIME_VSYSCALL
-extern void update_vsyscall(struct timespec *ts, struct clocksource *c);
+extern void
+update_vsyscall(struct timespec *ts, struct clocksource *c, u32 mult);
extern void update_vsyscall_tz(void);
#else
-static inline void update_vsyscall(struct timespec *ts, struct clocksource *c)
+static inline void
+update_vsyscall(struct timespec *ts, struct clocksource *c, u32 mult)
{
}

diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c
index c3a4e29..2a6d3e3 100644
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -177,7 +177,7 @@ void timekeeping_leap_insert(int leapsecond)
{
xtime.tv_sec += leapsecond;
wall_to_monotonic.tv_sec -= leapsecond;
- update_vsyscall(&xtime, timekeeper.clock);
+ update_vsyscall(&xtime, timekeeper.clock, timekeeper.mult);
}

#ifdef CONFIG_GENERIC_TIME
@@ -337,7 +337,7 @@ int do_settimeofday(struct timespec *tv)
timekeeper.ntp_error = 0;
ntp_clear();

- update_vsyscall(&xtime, timekeeper.clock);
+ update_vsyscall(&xtime, timekeeper.clock, timekeeper.mult);

write_sequnlock_irqrestore(&xtime_lock, flags);

@@ -811,7 +811,7 @@ void update_wall_time(void)
update_xtime_cache(nsecs);

/* check to see if there is a new clocksource to use */
- update_vsyscall(&xtime, timekeeper.clock);
+ update_vsyscall(&xtime, timekeeper.clock, timekeeper.mult);
}

/**