Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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

authored by

Tianyu Lan and committed by
Thomas Gleixner
bd00cd52 adb87ff4

+21 -12
-2
arch/x86/hyperv/hv_init.c
··· 301 301 302 302 x86_init.pci.arch_init = hv_pci_init; 303 303 304 - /* Register Hyper-V specific clocksource */ 305 - hv_init_clocksource(); 306 304 return; 307 305 308 306 remove_cpuhp_state:
+8
arch/x86/kernel/cpu/mshyperv.c
··· 29 29 #include <asm/timer.h> 30 30 #include <asm/reboot.h> 31 31 #include <asm/nmi.h> 32 + #include <clocksource/hyperv_timer.h> 32 33 33 34 struct ms_hyperv_info ms_hyperv; 34 35 EXPORT_SYMBOL_GPL(ms_hyperv); ··· 339 338 x2apic_phys = 1; 340 339 # endif 341 340 341 + /* Register Hyper-V specific clocksource */ 342 + hv_init_clocksource(); 342 343 #endif 344 + } 345 + 346 + void hv_setup_sched_clock(void *sched_clock) 347 + { 348 + pv_ops.time.sched_clock = sched_clock; 343 349 } 344 350 345 351 const __initconst struct hypervisor_x86 x86_hyper_ms_hyperv = {
+12 -10
drivers/clocksource/hyperv_timer.c
··· 22 22 #include <asm/mshyperv.h> 23 23 24 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 28 * If false, we're using the old mechanism for stimer0 interrupts ··· 223 222 } 224 223 EXPORT_SYMBOL_GPL(hv_get_tsc_page); 225 224 226 - static u64 notrace read_hv_sched_clock_tsc(void) 225 + static u64 notrace read_hv_clock_tsc(struct clocksource *arg) 227 226 { 228 227 u64 current_tick = hv_read_tsc_page(&tsc_pg); 229 228 ··· 233 232 return current_tick; 234 233 } 235 234 236 - static u64 read_hv_clock_tsc(struct clocksource *arg) 235 + static u64 read_hv_sched_clock_tsc(void) 237 236 { 238 - return read_hv_sched_clock_tsc(); 237 + return read_hv_clock_tsc(NULL) - hv_sched_clock_offset; 239 238 } 240 239 241 240 static struct clocksource hyperv_cs_tsc = { ··· 247 246 }; 248 247 #endif 249 248 250 - static u64 notrace read_hv_sched_clock_msr(void) 249 + static u64 notrace read_hv_clock_msr(struct clocksource *arg) 251 250 { 252 251 u64 current_tick; 253 252 /* ··· 259 258 return current_tick; 260 259 } 261 260 262 - static u64 read_hv_clock_msr(struct clocksource *arg) 261 + static u64 read_hv_sched_clock_msr(void) 263 262 { 264 - return read_hv_sched_clock_msr(); 263 + return read_hv_clock_msr(NULL) - hv_sched_clock_offset; 265 264 } 266 265 267 266 static struct clocksource hyperv_cs_msr = { ··· 299 298 hv_set_clocksource_vdso(hyperv_cs_tsc); 300 299 clocksource_register_hz(&hyperv_cs_tsc, NSEC_PER_SEC/100); 301 300 302 - /* sched_clock_register is needed on ARM64 but is a no-op on x86 */ 303 - sched_clock_register(read_hv_sched_clock_tsc, 64, HV_CLOCK_HZ); 301 + hv_sched_clock_offset = hyperv_cs->read(hyperv_cs); 302 + hv_setup_sched_clock(read_hv_sched_clock_tsc); 303 + 304 304 return true; 305 305 } 306 306 #else ··· 331 329 hyperv_cs = &hyperv_cs_msr; 332 330 clocksource_register_hz(&hyperv_cs_msr, NSEC_PER_SEC/100); 333 331 334 - /* sched_clock_register is needed on ARM64 but is a no-op on x86 */ 335 - sched_clock_register(read_hv_sched_clock_msr, 64, HV_CLOCK_HZ); 332 + hv_sched_clock_offset = hyperv_cs->read(hyperv_cs); 333 + hv_setup_sched_clock(read_hv_sched_clock_msr); 336 334 } 337 335 EXPORT_SYMBOL_GPL(hv_init_clocksource);
+1
include/asm-generic/mshyperv.h
··· 167 167 void hyperv_report_panic_msg(phys_addr_t pa, size_t size); 168 168 bool hv_is_hyperv_initialized(void); 169 169 void hyperv_cleanup(void); 170 + void hv_setup_sched_clock(void *sched_clock); 170 171 #else /* CONFIG_HYPERV */ 171 172 static inline bool hv_is_hyperv_initialized(void) { return false; } 172 173 static inline void hyperv_cleanup(void) {}