drivers/clocksource/hyperv_timer.c

Source file repositories/reference/linux-study-clean/drivers/clocksource/hyperv_timer.c

File Facts

System
Linux kernel
Corpus path
drivers/clocksource/hyperv_timer.c
Extension
.c
Size
18451 bytes
Lines
669
Domain
Driver Families
Bucket
drivers/clocksource
Inferred role
Driver Families: exported/initcall integration point
Status
integration implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

static __always_inline void hv_setup_sched_clock(void *sched_clock) {}
#endif /* CONFIG_GENERIC_SCHED_CLOCK */

static void __init hv_init_tsc_clocksource(void)
{
	union hv_reference_tsc_msr tsc_msr;

	/*
	 * When running as a guest partition:
	 *
	 * If Hyper-V offers TSC_INVARIANT, then the virtualized TSC correctly
	 * handles frequency and offset changes due to live migration,
	 * pause/resume, and other VM management operations.  So lower the
	 * Hyper-V Reference TSC rating, causing the generic TSC to be used.
	 * TSC_INVARIANT is not offered on ARM64, so the Hyper-V Reference
	 * TSC will be preferred over the virtualized ARM64 arch counter.
	 *
	 * When running as the root partition:
	 *
	 * There is no HV_ACCESS_TSC_INVARIANT feature. Always lower the rating
	 * of the Hyper-V Reference TSC.
	 */
	if ((ms_hyperv.features & HV_ACCESS_TSC_INVARIANT) ||
	    hv_root_partition()) {
		hyperv_cs_tsc.rating = 250;
		hyperv_cs_msr.rating = 245;
	}

	if (!(ms_hyperv.features & HV_MSR_REFERENCE_TSC_AVAILABLE))
		return;

	hv_read_reference_counter = read_hv_clock_tsc;

	/*
	 * TSC page mapping works differently in root compared to guest.
	 * - In guest partition the guest PFN has to be passed to the
	 *   hypervisor.
	 * - In root partition it's other way around: it has to map the PFN
	 *   provided by the hypervisor.
	 *   But it can't be mapped right here as it's too early and MMU isn't
	 *   ready yet. So, we only set the enable bit here and will remap the
	 *   page later in hv_remap_tsc_clocksource().
	 *
	 * It worth mentioning, that TSC clocksource read function
	 * (read_hv_clock_tsc) has a MSR-based fallback mechanism, used when
	 * TSC page is zeroed (which is the case until the PFN is remapped) and
	 * thus TSC clocksource will work even without the real TSC page
	 * mapped.
	 */
	tsc_msr.as_uint64 = hv_get_msr(HV_MSR_REFERENCE_TSC);
	if (hv_root_partition())
		tsc_pfn = tsc_msr.pfn;
	else
		tsc_pfn = HVPFN_DOWN(virt_to_phys(tsc_page));
	tsc_msr.enable = 1;
	tsc_msr.pfn = tsc_pfn;
	hv_set_msr(HV_MSR_REFERENCE_TSC, tsc_msr.as_uint64);

	clocksource_register_hz(&hyperv_cs_tsc, NSEC_PER_SEC/100);

	/*
	 * If TSC is invariant, then let it stay as the sched clock since it
	 * will be faster than reading the TSC page. But if not invariant, use
	 * the TSC page so that live migrations across hosts with different
	 * frequencies is handled correctly.
	 */
	if (!(ms_hyperv.features & HV_ACCESS_TSC_INVARIANT)) {
		hv_sched_clock_offset = hv_read_reference_counter();
		hv_setup_sched_clock(read_hv_sched_clock_tsc);
	}
}

void __init hv_init_clocksource(void)
{
	/*
	 * Try to set up the TSC page clocksource, then the MSR clocksource.
	 * At least one of these will always be available except on very old
	 * versions of Hyper-V on x86.  In that case we won't have a Hyper-V
	 * clocksource, but Linux will still run with a clocksource based
	 * on the emulated PIT or LAPIC timer.
	 *
	 * Never use the MSR clocksource as sched clock.  It's too slow.
	 * Better to use the native sched clock as the fallback.
	 */
	hv_init_tsc_clocksource();

	if (ms_hyperv.features & HV_MSR_TIME_REF_COUNT_AVAILABLE)
		clocksource_register_hz(&hyperv_cs_msr, NSEC_PER_SEC/100);
}

Annotation

Implementation Notes