diff options
author | Tianyu Lan <Tianyu.Lan@microsoft.com> | 2019-08-14 08:32:16 -0400 |
---|---|---|
committer | Thomas Gleixner <tglx@linutronix.de> | 2019-08-23 10:59:54 -0400 |
commit | bd00cd52d5be655a2f217e2ed74b91a71cb2b14f (patch) | |
tree | 90b8fe8d71a849f6066d1c2ba7500dba0fca9819 | |
parent | adb87ff4f96c9700718e09c97a804124d5cd61ff (diff) |
clocksource/drivers/hyperv: Add Hyper-V specific sched clock function
Hyper-V guests use the default native_sched_clock() in
pv_ops.time.sched_clock on x86. But native_sched_clock() directly uses the
raw TSC value, which can be discontinuous in a Hyper-V VM.
Add the generic hv_setup_sched_clock() to set the sched clock function
appropriately. On x86, this sets pv_ops.time.sched_clock to read the
Hyper-V reference TSC value that is scaled and adjusted to be continuous.
Also move the Hyper-V reference TSC initialization much earlier in the boot
process so no discontinuity is observed when pv_ops.time.sched_clock
calculates its offset.
[ tglx: Folded build fix ]
Signed-off-by: Tianyu Lan <Tianyu.Lan@microsoft.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Michael Kelley <mikelley@microsoft.com>
Link: https://lkml.kernel.org/r/20190814123216.32245-3-Tianyu.Lan@microsoft.com
-rw-r--r-- | arch/x86/hyperv/hv_init.c | 2 | ||||
-rw-r--r-- | arch/x86/kernel/cpu/mshyperv.c | 8 | ||||
-rw-r--r-- | drivers/clocksource/hyperv_timer.c | 22 | ||||
-rw-r--r-- | include/asm-generic/mshyperv.h | 1 |
4 files changed, 21 insertions, 12 deletions
diff --git a/arch/x86/hyperv/hv_init.c b/arch/x86/hyperv/hv_init.c index 0d258688c8cf..866dfb3dca48 100644 --- a/arch/x86/hyperv/hv_init.c +++ b/arch/x86/hyperv/hv_init.c | |||
@@ -301,8 +301,6 @@ void __init hyperv_init(void) | |||
301 | 301 | ||
302 | x86_init.pci.arch_init = hv_pci_init; | 302 | x86_init.pci.arch_init = hv_pci_init; |
303 | 303 | ||
304 | /* Register Hyper-V specific clocksource */ | ||
305 | hv_init_clocksource(); | ||
306 | return; | 304 | return; |
307 | 305 | ||
308 | remove_cpuhp_state: | 306 | remove_cpuhp_state: |
diff --git a/arch/x86/kernel/cpu/mshyperv.c b/arch/x86/kernel/cpu/mshyperv.c index 062f77279ce3..53afd33990eb 100644 --- a/arch/x86/kernel/cpu/mshyperv.c +++ b/arch/x86/kernel/cpu/mshyperv.c | |||
@@ -29,6 +29,7 @@ | |||
29 | #include <asm/timer.h> | 29 | #include <asm/timer.h> |
30 | #include <asm/reboot.h> | 30 | #include <asm/reboot.h> |
31 | #include <asm/nmi.h> | 31 | #include <asm/nmi.h> |
32 | #include <clocksource/hyperv_timer.h> | ||
32 | 33 | ||
33 | struct ms_hyperv_info ms_hyperv; | 34 | struct ms_hyperv_info ms_hyperv; |
34 | EXPORT_SYMBOL_GPL(ms_hyperv); | 35 | EXPORT_SYMBOL_GPL(ms_hyperv); |
@@ -338,9 +339,16 @@ static void __init ms_hyperv_init_platform(void) | |||
338 | x2apic_phys = 1; | 339 | x2apic_phys = 1; |
339 | # endif | 340 | # endif |
340 | 341 | ||
342 | /* Register Hyper-V specific clocksource */ | ||
343 | hv_init_clocksource(); | ||
341 | #endif | 344 | #endif |
342 | } | 345 | } |
343 | 346 | ||
347 | void hv_setup_sched_clock(void *sched_clock) | ||
348 | { | ||
349 | pv_ops.time.sched_clock = sched_clock; | ||
350 | } | ||
351 | |||
344 | const __initconst struct hypervisor_x86 x86_hyper_ms_hyperv = { | 352 | const __initconst struct hypervisor_x86 x86_hyper_ms_hyperv = { |
345 | .name = "Microsoft Hyper-V", | 353 | .name = "Microsoft Hyper-V", |
346 | .detect = ms_hyperv_platform, | 354 | .detect = ms_hyperv_platform, |
diff --git a/drivers/clocksource/hyperv_timer.c b/drivers/clocksource/hyperv_timer.c index 432aa331df04..c322ab4d3689 100644 --- a/drivers/clocksource/hyperv_timer.c +++ b/drivers/clocksource/hyperv_timer.c | |||
@@ -22,6 +22,7 @@ | |||
22 | #include <asm/mshyperv.h> | 22 | #include <asm/mshyperv.h> |
23 | 23 | ||
24 | static struct clock_event_device __percpu *hv_clock_event; | 24 | static struct clock_event_device __percpu *hv_clock_event; |
25 | static u64 hv_sched_clock_offset __ro_after_init; | ||
25 | 26 | ||
26 | /* | 27 | /* |
27 | * If false, we're using the old mechanism for stimer0 interrupts | 28 | * If false, we're using the old mechanism for stimer0 interrupts |
@@ -222,7 +223,7 @@ struct ms_hyperv_tsc_page *hv_get_tsc_page(void) | |||
222 | } | 223 | } |
223 | EXPORT_SYMBOL_GPL(hv_get_tsc_page); | 224 | EXPORT_SYMBOL_GPL(hv_get_tsc_page); |
224 | 225 | ||
225 | static u64 notrace read_hv_sched_clock_tsc(void) | 226 | static u64 notrace read_hv_clock_tsc(struct clocksource *arg) |
226 | { | 227 | { |
227 | u64 current_tick = hv_read_tsc_page(&tsc_pg); | 228 | u64 current_tick = hv_read_tsc_page(&tsc_pg); |
228 | 229 | ||
@@ -232,9 +233,9 @@ static u64 notrace read_hv_sched_clock_tsc(void) | |||
232 | return current_tick; | 233 | return current_tick; |
233 | } | 234 | } |
234 | 235 | ||
235 | static u64 read_hv_clock_tsc(struct clocksource *arg) | 236 | static u64 read_hv_sched_clock_tsc(void) |
236 | { | 237 | { |
237 | return read_hv_sched_clock_tsc(); | 238 | return read_hv_clock_tsc(NULL) - hv_sched_clock_offset; |
238 | } | 239 | } |
239 | 240 | ||
240 | static struct clocksource hyperv_cs_tsc = { | 241 | static struct clocksource hyperv_cs_tsc = { |
@@ -246,7 +247,7 @@ static struct clocksource hyperv_cs_tsc = { | |||
246 | }; | 247 | }; |
247 | #endif | 248 | #endif |
248 | 249 | ||
249 | static u64 notrace read_hv_sched_clock_msr(void) | 250 | static u64 notrace read_hv_clock_msr(struct clocksource *arg) |
250 | { | 251 | { |
251 | u64 current_tick; | 252 | u64 current_tick; |
252 | /* | 253 | /* |
@@ -258,9 +259,9 @@ static u64 notrace read_hv_sched_clock_msr(void) | |||
258 | return current_tick; | 259 | return current_tick; |
259 | } | 260 | } |
260 | 261 | ||
261 | static u64 read_hv_clock_msr(struct clocksource *arg) | 262 | static u64 read_hv_sched_clock_msr(void) |
262 | { | 263 | { |
263 | return read_hv_sched_clock_msr(); | 264 | return read_hv_clock_msr(NULL) - hv_sched_clock_offset; |
264 | } | 265 | } |
265 | 266 | ||
266 | static struct clocksource hyperv_cs_msr = { | 267 | static struct clocksource hyperv_cs_msr = { |
@@ -298,8 +299,9 @@ static bool __init hv_init_tsc_clocksource(void) | |||
298 | hv_set_clocksource_vdso(hyperv_cs_tsc); | 299 | hv_set_clocksource_vdso(hyperv_cs_tsc); |
299 | clocksource_register_hz(&hyperv_cs_tsc, NSEC_PER_SEC/100); | 300 | clocksource_register_hz(&hyperv_cs_tsc, NSEC_PER_SEC/100); |
300 | 301 | ||
301 | /* sched_clock_register is needed on ARM64 but is a no-op on x86 */ | 302 | hv_sched_clock_offset = hyperv_cs->read(hyperv_cs); |
302 | sched_clock_register(read_hv_sched_clock_tsc, 64, HV_CLOCK_HZ); | 303 | hv_setup_sched_clock(read_hv_sched_clock_tsc); |
304 | |||
303 | return true; | 305 | return true; |
304 | } | 306 | } |
305 | #else | 307 | #else |
@@ -329,7 +331,7 @@ void __init hv_init_clocksource(void) | |||
329 | hyperv_cs = &hyperv_cs_msr; | 331 | hyperv_cs = &hyperv_cs_msr; |
330 | clocksource_register_hz(&hyperv_cs_msr, NSEC_PER_SEC/100); | 332 | clocksource_register_hz(&hyperv_cs_msr, NSEC_PER_SEC/100); |
331 | 333 | ||
332 | /* sched_clock_register is needed on ARM64 but is a no-op on x86 */ | 334 | hv_sched_clock_offset = hyperv_cs->read(hyperv_cs); |
333 | sched_clock_register(read_hv_sched_clock_msr, 64, HV_CLOCK_HZ); | 335 | hv_setup_sched_clock(read_hv_sched_clock_msr); |
334 | } | 336 | } |
335 | EXPORT_SYMBOL_GPL(hv_init_clocksource); | 337 | EXPORT_SYMBOL_GPL(hv_init_clocksource); |
diff --git a/include/asm-generic/mshyperv.h b/include/asm-generic/mshyperv.h index 0becb7d9704d..18d8e2d8210f 100644 --- a/include/asm-generic/mshyperv.h +++ b/include/asm-generic/mshyperv.h | |||
@@ -167,6 +167,7 @@ void hyperv_report_panic(struct pt_regs *regs, long err); | |||
167 | void hyperv_report_panic_msg(phys_addr_t pa, size_t size); | 167 | void hyperv_report_panic_msg(phys_addr_t pa, size_t size); |
168 | bool hv_is_hyperv_initialized(void); | 168 | bool hv_is_hyperv_initialized(void); |
169 | void hyperv_cleanup(void); | 169 | void hyperv_cleanup(void); |
170 | void hv_setup_sched_clock(void *sched_clock); | ||
170 | #else /* CONFIG_HYPERV */ | 171 | #else /* CONFIG_HYPERV */ |
171 | static inline bool hv_is_hyperv_initialized(void) { return false; } | 172 | static inline bool hv_is_hyperv_initialized(void) { return false; } |
172 | static inline void hyperv_cleanup(void) {} | 173 | static inline void hyperv_cleanup(void) {} |