diff options
author | David Vrabel <david.vrabel@citrix.com> | 2013-05-13 13:56:06 -0400 |
---|---|---|
committer | John Stultz <john.stultz@linaro.org> | 2013-05-28 17:00:59 -0400 |
commit | 3565184ed0c1ea46bea5b792da5f72a83c43e49b (patch) | |
tree | 4846a589a6aa295634ef585e5637872f15c19993 | |
parent | 0a0a7e66fa269de78975ea8d4e825a66d92b8d70 (diff) |
x86: Increase precision of x86_platform.get/set_wallclock()
All the virtualized platforms (KVM, lguest and Xen) have persistent
wallclocks that have more than one second of precision.
read_persistent_wallclock() and update_persistent_wallclock() allow
for nanosecond precision but their implementation on x86 with
x86_platform.get/set_wallclock() only allows for one second precision.
This means guests may see a wallclock time that is off by up to 1
second.
Make set_wallclock() and get_wallclock() take a struct timespec
parameter (which allows for nanosecond precision) so KVM and Xen
guests may start with a more accurate wallclock time and a Xen dom0
can maintain a more accurate wallclock for guests.
Signed-off-by: David Vrabel <david.vrabel@citrix.com>
Signed-off-by: John Stultz <john.stultz@linaro.org>
-rw-r--r-- | arch/x86/include/asm/mc146818rtc.h | 4 | ||||
-rw-r--r-- | arch/x86/include/asm/x86_init.h | 6 | ||||
-rw-r--r-- | arch/x86/kernel/kvmclock.c | 9 | ||||
-rw-r--r-- | arch/x86/kernel/rtc.c | 17 | ||||
-rw-r--r-- | arch/x86/lguest/boot.c | 4 | ||||
-rw-r--r-- | arch/x86/platform/efi/efi.c | 10 | ||||
-rw-r--r-- | arch/x86/xen/time.c | 19 | ||||
-rw-r--r-- | include/linux/efi.h | 4 |
8 files changed, 32 insertions, 41 deletions
diff --git a/arch/x86/include/asm/mc146818rtc.h b/arch/x86/include/asm/mc146818rtc.h index d354fb781c57..a55c7efcc4ed 100644 --- a/arch/x86/include/asm/mc146818rtc.h +++ b/arch/x86/include/asm/mc146818rtc.h | |||
@@ -95,8 +95,8 @@ static inline unsigned char current_lock_cmos_reg(void) | |||
95 | unsigned char rtc_cmos_read(unsigned char addr); | 95 | unsigned char rtc_cmos_read(unsigned char addr); |
96 | void rtc_cmos_write(unsigned char val, unsigned char addr); | 96 | void rtc_cmos_write(unsigned char val, unsigned char addr); |
97 | 97 | ||
98 | extern int mach_set_rtc_mmss(unsigned long nowtime); | 98 | extern int mach_set_rtc_mmss(const struct timespec *now); |
99 | extern unsigned long mach_get_cmos_time(void); | 99 | extern void mach_get_cmos_time(struct timespec *now); |
100 | 100 | ||
101 | #define RTC_IRQ 8 | 101 | #define RTC_IRQ 8 |
102 | 102 | ||
diff --git a/arch/x86/include/asm/x86_init.h b/arch/x86/include/asm/x86_init.h index d8d99222b36a..828a1565ba57 100644 --- a/arch/x86/include/asm/x86_init.h +++ b/arch/x86/include/asm/x86_init.h | |||
@@ -142,6 +142,8 @@ struct x86_cpuinit_ops { | |||
142 | void (*fixup_cpu_id)(struct cpuinfo_x86 *c, int node); | 142 | void (*fixup_cpu_id)(struct cpuinfo_x86 *c, int node); |
143 | }; | 143 | }; |
144 | 144 | ||
145 | struct timespec; | ||
146 | |||
145 | /** | 147 | /** |
146 | * struct x86_platform_ops - platform specific runtime functions | 148 | * struct x86_platform_ops - platform specific runtime functions |
147 | * @calibrate_tsc: calibrate TSC | 149 | * @calibrate_tsc: calibrate TSC |
@@ -156,8 +158,8 @@ struct x86_cpuinit_ops { | |||
156 | */ | 158 | */ |
157 | struct x86_platform_ops { | 159 | struct x86_platform_ops { |
158 | unsigned long (*calibrate_tsc)(void); | 160 | unsigned long (*calibrate_tsc)(void); |
159 | unsigned long (*get_wallclock)(void); | 161 | void (*get_wallclock)(struct timespec *ts); |
160 | int (*set_wallclock)(unsigned long nowtime); | 162 | int (*set_wallclock)(const struct timespec *ts); |
161 | void (*iommu_shutdown)(void); | 163 | void (*iommu_shutdown)(void); |
162 | bool (*is_untracked_pat_range)(u64 start, u64 end); | 164 | bool (*is_untracked_pat_range)(u64 start, u64 end); |
163 | void (*nmi_init)(void); | 165 | void (*nmi_init)(void); |
diff --git a/arch/x86/kernel/kvmclock.c b/arch/x86/kernel/kvmclock.c index d2c381280e3c..0db81ab511cc 100644 --- a/arch/x86/kernel/kvmclock.c +++ b/arch/x86/kernel/kvmclock.c | |||
@@ -48,10 +48,9 @@ static struct pvclock_wall_clock wall_clock; | |||
48 | * have elapsed since the hypervisor wrote the data. So we try to account for | 48 | * have elapsed since the hypervisor wrote the data. So we try to account for |
49 | * that with system time | 49 | * that with system time |
50 | */ | 50 | */ |
51 | static unsigned long kvm_get_wallclock(void) | 51 | static void kvm_get_wallclock(struct timespec *now) |
52 | { | 52 | { |
53 | struct pvclock_vcpu_time_info *vcpu_time; | 53 | struct pvclock_vcpu_time_info *vcpu_time; |
54 | struct timespec ts; | ||
55 | int low, high; | 54 | int low, high; |
56 | int cpu; | 55 | int cpu; |
57 | 56 | ||
@@ -64,14 +63,12 @@ static unsigned long kvm_get_wallclock(void) | |||
64 | cpu = smp_processor_id(); | 63 | cpu = smp_processor_id(); |
65 | 64 | ||
66 | vcpu_time = &hv_clock[cpu].pvti; | 65 | vcpu_time = &hv_clock[cpu].pvti; |
67 | pvclock_read_wallclock(&wall_clock, vcpu_time, &ts); | 66 | pvclock_read_wallclock(&wall_clock, vcpu_time, now); |
68 | 67 | ||
69 | preempt_enable(); | 68 | preempt_enable(); |
70 | |||
71 | return ts.tv_sec; | ||
72 | } | 69 | } |
73 | 70 | ||
74 | static int kvm_set_wallclock(unsigned long now) | 71 | static int kvm_set_wallclock(const struct timespec *now) |
75 | { | 72 | { |
76 | return -1; | 73 | return -1; |
77 | } | 74 | } |
diff --git a/arch/x86/kernel/rtc.c b/arch/x86/kernel/rtc.c index 198eb201ed3b..0aa29394ed6f 100644 --- a/arch/x86/kernel/rtc.c +++ b/arch/x86/kernel/rtc.c | |||
@@ -38,8 +38,9 @@ EXPORT_SYMBOL(rtc_lock); | |||
38 | * jump to the next second precisely 500 ms later. Check the Motorola | 38 | * jump to the next second precisely 500 ms later. Check the Motorola |
39 | * MC146818A or Dallas DS12887 data sheet for details. | 39 | * MC146818A or Dallas DS12887 data sheet for details. |
40 | */ | 40 | */ |
41 | int mach_set_rtc_mmss(unsigned long nowtime) | 41 | int mach_set_rtc_mmss(const struct timespec *now) |
42 | { | 42 | { |
43 | unsigned long nowtime = now->tv_sec; | ||
43 | struct rtc_time tm; | 44 | struct rtc_time tm; |
44 | int retval = 0; | 45 | int retval = 0; |
45 | 46 | ||
@@ -58,7 +59,7 @@ int mach_set_rtc_mmss(unsigned long nowtime) | |||
58 | return retval; | 59 | return retval; |
59 | } | 60 | } |
60 | 61 | ||
61 | unsigned long mach_get_cmos_time(void) | 62 | void mach_get_cmos_time(struct timespec *now) |
62 | { | 63 | { |
63 | unsigned int status, year, mon, day, hour, min, sec, century = 0; | 64 | unsigned int status, year, mon, day, hour, min, sec, century = 0; |
64 | unsigned long flags; | 65 | unsigned long flags; |
@@ -107,7 +108,8 @@ unsigned long mach_get_cmos_time(void) | |||
107 | } else | 108 | } else |
108 | year += CMOS_YEARS_OFFS; | 109 | year += CMOS_YEARS_OFFS; |
109 | 110 | ||
110 | return mktime(year, mon, day, hour, min, sec); | 111 | now->tv_sec = mktime(year, mon, day, hour, min, sec); |
112 | now->tv_nsec = 0; | ||
111 | } | 113 | } |
112 | 114 | ||
113 | /* Routines for accessing the CMOS RAM/RTC. */ | 115 | /* Routines for accessing the CMOS RAM/RTC. */ |
@@ -135,18 +137,13 @@ EXPORT_SYMBOL(rtc_cmos_write); | |||
135 | 137 | ||
136 | int update_persistent_clock(struct timespec now) | 138 | int update_persistent_clock(struct timespec now) |
137 | { | 139 | { |
138 | return x86_platform.set_wallclock(now.tv_sec); | 140 | return x86_platform.set_wallclock(&now); |
139 | } | 141 | } |
140 | 142 | ||
141 | /* not static: needed by APM */ | 143 | /* not static: needed by APM */ |
142 | void read_persistent_clock(struct timespec *ts) | 144 | void read_persistent_clock(struct timespec *ts) |
143 | { | 145 | { |
144 | unsigned long retval; | 146 | x86_platform.get_wallclock(ts); |
145 | |||
146 | retval = x86_platform.get_wallclock(); | ||
147 | |||
148 | ts->tv_sec = retval; | ||
149 | ts->tv_nsec = 0; | ||
150 | } | 147 | } |
151 | 148 | ||
152 | 149 | ||
diff --git a/arch/x86/lguest/boot.c b/arch/x86/lguest/boot.c index 7114c63f047d..8424d5adcfa2 100644 --- a/arch/x86/lguest/boot.c +++ b/arch/x86/lguest/boot.c | |||
@@ -882,9 +882,9 @@ int lguest_setup_irq(unsigned int irq) | |||
882 | * It would be far better for everyone if the Guest had its own clock, but | 882 | * It would be far better for everyone if the Guest had its own clock, but |
883 | * until then the Host gives us the time on every interrupt. | 883 | * until then the Host gives us the time on every interrupt. |
884 | */ | 884 | */ |
885 | static unsigned long lguest_get_wallclock(void) | 885 | static void lguest_get_wallclock(struct timespec *now) |
886 | { | 886 | { |
887 | return lguest_data.time.tv_sec; | 887 | *now = lguest_data.time; |
888 | } | 888 | } |
889 | 889 | ||
890 | /* | 890 | /* |
diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index 55856b2310d3..dd3b82530145 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c | |||
@@ -352,8 +352,9 @@ static efi_status_t __init phys_efi_get_time(efi_time_t *tm, | |||
352 | return status; | 352 | return status; |
353 | } | 353 | } |
354 | 354 | ||
355 | int efi_set_rtc_mmss(unsigned long nowtime) | 355 | int efi_set_rtc_mmss(const struct timespec *now) |
356 | { | 356 | { |
357 | unsigned long nowtime = now->tv_sec; | ||
357 | efi_status_t status; | 358 | efi_status_t status; |
358 | efi_time_t eft; | 359 | efi_time_t eft; |
359 | efi_time_cap_t cap; | 360 | efi_time_cap_t cap; |
@@ -388,7 +389,7 @@ int efi_set_rtc_mmss(unsigned long nowtime) | |||
388 | return 0; | 389 | return 0; |
389 | } | 390 | } |
390 | 391 | ||
391 | unsigned long efi_get_time(void) | 392 | void efi_get_time(struct timespec *now) |
392 | { | 393 | { |
393 | efi_status_t status; | 394 | efi_status_t status; |
394 | efi_time_t eft; | 395 | efi_time_t eft; |
@@ -398,8 +399,9 @@ unsigned long efi_get_time(void) | |||
398 | if (status != EFI_SUCCESS) | 399 | if (status != EFI_SUCCESS) |
399 | pr_err("Oops: efitime: can't read time!\n"); | 400 | pr_err("Oops: efitime: can't read time!\n"); |
400 | 401 | ||
401 | return mktime(eft.year, eft.month, eft.day, eft.hour, | 402 | now->tv_sec = mktime(eft.year, eft.month, eft.day, eft.hour, |
402 | eft.minute, eft.second); | 403 | eft.minute, eft.second); |
404 | now->tv_nsec = 0; | ||
403 | } | 405 | } |
404 | 406 | ||
405 | /* | 407 | /* |
diff --git a/arch/x86/xen/time.c b/arch/x86/xen/time.c index 3d88bfdf9e1c..a1947ac2da82 100644 --- a/arch/x86/xen/time.c +++ b/arch/x86/xen/time.c | |||
@@ -191,32 +191,25 @@ static void xen_read_wallclock(struct timespec *ts) | |||
191 | put_cpu_var(xen_vcpu); | 191 | put_cpu_var(xen_vcpu); |
192 | } | 192 | } |
193 | 193 | ||
194 | static unsigned long xen_get_wallclock(void) | 194 | static void xen_get_wallclock(struct timespec *now) |
195 | { | 195 | { |
196 | struct timespec ts; | 196 | xen_read_wallclock(now); |
197 | |||
198 | xen_read_wallclock(&ts); | ||
199 | return ts.tv_sec; | ||
200 | } | 197 | } |
201 | 198 | ||
202 | static int xen_set_wallclock(unsigned long now) | 199 | static int xen_set_wallclock(const struct timespec *now) |
203 | { | 200 | { |
204 | struct xen_platform_op op; | 201 | struct xen_platform_op op; |
205 | int rc; | ||
206 | 202 | ||
207 | /* do nothing for domU */ | 203 | /* do nothing for domU */ |
208 | if (!xen_initial_domain()) | 204 | if (!xen_initial_domain()) |
209 | return -1; | 205 | return -1; |
210 | 206 | ||
211 | op.cmd = XENPF_settime; | 207 | op.cmd = XENPF_settime; |
212 | op.u.settime.secs = now; | 208 | op.u.settime.secs = now->tv_sec; |
213 | op.u.settime.nsecs = 0; | 209 | op.u.settime.nsecs = now->tv_nsec; |
214 | op.u.settime.system_time = xen_clocksource_read(); | 210 | op.u.settime.system_time = xen_clocksource_read(); |
215 | 211 | ||
216 | rc = HYPERVISOR_dom0_op(&op); | 212 | return HYPERVISOR_dom0_op(&op); |
217 | WARN(rc != 0, "XENPF_settime failed: now=%ld\n", now); | ||
218 | |||
219 | return rc; | ||
220 | } | 213 | } |
221 | 214 | ||
222 | static struct clocksource xen_clocksource __read_mostly = { | 215 | static struct clocksource xen_clocksource __read_mostly = { |
diff --git a/include/linux/efi.h b/include/linux/efi.h index 2bc0ad78d058..0068bba6f8b6 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h | |||
@@ -594,8 +594,8 @@ extern u64 efi_mem_attribute (unsigned long phys_addr, unsigned long size); | |||
594 | extern int __init efi_uart_console_only (void); | 594 | extern int __init efi_uart_console_only (void); |
595 | extern void efi_initialize_iomem_resources(struct resource *code_resource, | 595 | extern void efi_initialize_iomem_resources(struct resource *code_resource, |
596 | struct resource *data_resource, struct resource *bss_resource); | 596 | struct resource *data_resource, struct resource *bss_resource); |
597 | extern unsigned long efi_get_time(void); | 597 | extern void efi_get_time(struct timespec *now); |
598 | extern int efi_set_rtc_mmss(unsigned long nowtime); | 598 | extern int efi_set_rtc_mmss(const struct timespec *now); |
599 | extern void efi_reserve_boot_services(void); | 599 | extern void efi_reserve_boot_services(void); |
600 | extern struct efi_memory_map memmap; | 600 | extern struct efi_memory_map memmap; |
601 | 601 | ||