Skip to content

Commit e5313f1

Browse files
kelleymhdlezcano
authored andcommitted
clocksource/drivers/hyper-v: Rework clocksource and sched clock setup
Current code assigns either the Hyper-V TSC page or MSR-based ref counter as the sched clock. This may be sub-optimal in two cases. First, if there is hardware support to ensure consistent TSC frequency across live migrations and Hyper-V is using that support, the raw TSC is a faster source of time than the Hyper-V TSC page. Second, the MSR-based ref counter is relatively slow because reads require a trap to the hypervisor. As such, it should never be used as the sched clock. The native sched clock based on the raw TSC or jiffies is much better. Rework the sched clock setup so it is set to the TSC page only if Hyper-V indicates that the TSC may have inconsistent frequency across live migrations. Also, remove the code that sets the sched clock to the MSR-based ref counter. In the cases where it is not set, the sched clock will then be the native sched clock. As part of the rework, always enable both the TSC page clocksource and the MSR-based ref counter clocksource. Set the ratings so the TSC page clocksource is preferred. While the MSR-based ref counter clocksource is unlikely to ever be the default, having it available for manual selection is convenient for development purposes. Signed-off-by: Michael Kelley <mikelley@microsoft.com> Reviewed-by: Dexuan Cui <decui@microsoft.com> Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org> Link: https://lore.kernel.org/r/1687201360-16003-1-git-send-email-mikelley@microsoft.com
1 parent 038d454 commit e5313f1

File tree

1 file changed

+23
-31
lines changed

1 file changed

+23
-31
lines changed

drivers/clocksource/hyperv_timer.c

Lines changed: 23 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -475,15 +475,9 @@ static u64 notrace read_hv_clock_msr_cs(struct clocksource *arg)
475475
return read_hv_clock_msr();
476476
}
477477

478-
static u64 notrace read_hv_sched_clock_msr(void)
479-
{
480-
return (read_hv_clock_msr() - hv_sched_clock_offset) *
481-
(NSEC_PER_SEC / HV_CLOCK_HZ);
482-
}
483-
484478
static struct clocksource hyperv_cs_msr = {
485479
.name = "hyperv_clocksource_msr",
486-
.rating = 500,
480+
.rating = 495,
487481
.read = read_hv_clock_msr_cs,
488482
.mask = CLOCKSOURCE_MASK(64),
489483
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
@@ -513,7 +507,7 @@ static __always_inline void hv_setup_sched_clock(void *sched_clock)
513507
static __always_inline void hv_setup_sched_clock(void *sched_clock) {}
514508
#endif /* CONFIG_GENERIC_SCHED_CLOCK */
515509

516-
static bool __init hv_init_tsc_clocksource(void)
510+
static void __init hv_init_tsc_clocksource(void)
517511
{
518512
union hv_reference_tsc_msr tsc_msr;
519513

@@ -524,17 +518,14 @@ static bool __init hv_init_tsc_clocksource(void)
524518
* Hyper-V Reference TSC rating, causing the generic TSC to be used.
525519
* TSC_INVARIANT is not offered on ARM64, so the Hyper-V Reference
526520
* TSC will be preferred over the virtualized ARM64 arch counter.
527-
* While the Hyper-V MSR clocksource won't be used since the
528-
* Reference TSC clocksource is present, change its rating as
529-
* well for consistency.
530521
*/
531522
if (ms_hyperv.features & HV_ACCESS_TSC_INVARIANT) {
532523
hyperv_cs_tsc.rating = 250;
533-
hyperv_cs_msr.rating = 250;
524+
hyperv_cs_msr.rating = 245;
534525
}
535526

536527
if (!(ms_hyperv.features & HV_MSR_REFERENCE_TSC_AVAILABLE))
537-
return false;
528+
return;
538529

539530
hv_read_reference_counter = read_hv_clock_tsc;
540531

@@ -565,33 +556,34 @@ static bool __init hv_init_tsc_clocksource(void)
565556

566557
clocksource_register_hz(&hyperv_cs_tsc, NSEC_PER_SEC/100);
567558

568-
hv_sched_clock_offset = hv_read_reference_counter();
569-
hv_setup_sched_clock(read_hv_sched_clock_tsc);
570-
571-
return true;
559+
/*
560+
* If TSC is invariant, then let it stay as the sched clock since it
561+
* will be faster than reading the TSC page. But if not invariant, use
562+
* the TSC page so that live migrations across hosts with different
563+
* frequencies is handled correctly.
564+
*/
565+
if (!(ms_hyperv.features & HV_ACCESS_TSC_INVARIANT)) {
566+
hv_sched_clock_offset = hv_read_reference_counter();
567+
hv_setup_sched_clock(read_hv_sched_clock_tsc);
568+
}
572569
}
573570

574571
void __init hv_init_clocksource(void)
575572
{
576573
/*
577-
* Try to set up the TSC page clocksource. If it succeeds, we're
578-
* done. Otherwise, set up the MSR clocksource. At least one of
579-
* these will always be available except on very old versions of
580-
* Hyper-V on x86. In that case we won't have a Hyper-V
574+
* Try to set up the TSC page clocksource, then the MSR clocksource.
575+
* At least one of these will always be available except on very old
576+
* versions of Hyper-V on x86. In that case we won't have a Hyper-V
581577
* clocksource, but Linux will still run with a clocksource based
582578
* on the emulated PIT or LAPIC timer.
579+
*
580+
* Never use the MSR clocksource as sched clock. It's too slow.
581+
* Better to use the native sched clock as the fallback.
583582
*/
584-
if (hv_init_tsc_clocksource())
585-
return;
586-
587-
if (!(ms_hyperv.features & HV_MSR_TIME_REF_COUNT_AVAILABLE))
588-
return;
589-
590-
hv_read_reference_counter = read_hv_clock_msr;
591-
clocksource_register_hz(&hyperv_cs_msr, NSEC_PER_SEC/100);
583+
hv_init_tsc_clocksource();
592584

593-
hv_sched_clock_offset = hv_read_reference_counter();
594-
hv_setup_sched_clock(read_hv_sched_clock_msr);
585+
if (ms_hyperv.features & HV_MSR_TIME_REF_COUNT_AVAILABLE)
586+
clocksource_register_hz(&hyperv_cs_msr, NSEC_PER_SEC/100);
595587
}
596588

597589
void __init hv_remap_tsc_clocksource(void)

0 commit comments

Comments
 (0)