arch/x86/kernel/tsc.c

Source file repositories/reference/linux-study-clean/arch/x86/kernel/tsc.c

File Facts

System
Linux kernel
Corpus path
arch/x86/kernel/tsc.c
Extension
.c
Size
41716 bytes
Lines
1597
Domain
Architecture Layer
Bucket
arch/x86
Inferred role
Architecture Layer: exported/initcall integration point
Status
integration implementation candidate

Why This File Exists

CPU and platform-specific kernel glue: boot entry, traps, syscall entry, interrupts, page tables, context switch, and low-level barriers.

Dependency Surface

Detected Declarations

Annotated Snippet

core_initcall(cpufreq_register_tsc_scaling);

#endif /* CONFIG_CPU_FREQ */

#define ART_MIN_DENOMINATOR (1)

/*
 * If ART is present detect the numerator:denominator to convert to TSC
 */
static void __init detect_art(void)
{
	unsigned int unused;

	if (boot_cpu_data.cpuid_level < CPUID_LEAF_TSC)
		return;

	/*
	 * Don't enable ART in a VM, non-stop TSC and TSC_ADJUST required,
	 * and the TSC counter resets must not occur asynchronously.
	 */
	if (boot_cpu_has(X86_FEATURE_HYPERVISOR) ||
	    !boot_cpu_has(X86_FEATURE_NONSTOP_TSC) ||
	    !boot_cpu_has(X86_FEATURE_TSC_ADJUST) ||
	    tsc_async_resets)
		return;

	cpuid(CPUID_LEAF_TSC, &art_base_clk.denominator,
	      &art_base_clk.numerator, &art_base_clk.freq_khz, &unused);

	art_base_clk.freq_khz /= KHZ;
	if (art_base_clk.denominator < ART_MIN_DENOMINATOR)
		return;

	rdmsrq(MSR_IA32_TSC_ADJUST, art_base_clk.offset);

	/* Make this sticky over multiple CPU init calls */
	setup_force_cpu_cap(X86_FEATURE_ART);
}


/* clocksource code */

static void tsc_resume(struct clocksource *cs)
{
	tsc_verify_tsc_adjust(true);
}

/*
 * We used to compare the TSC to the cycle_last value in the clocksource
 * structure to avoid a nasty time-warp. This can be observed in a
 * very small window right after one CPU updated cycle_last under
 * xtime/vsyscall_gtod lock and the other CPU reads a TSC value which
 * is smaller than the cycle_last reference value due to a TSC which
 * is slightly behind. This delta is nowhere else observable, but in
 * that case it results in a forward time jump in the range of hours
 * due to the unsigned delta calculation of the time keeping core
 * code, which is necessary to support wrapping clocksources like pm
 * timer.
 *
 * This sanity check is now done in the core timekeeping code.
 * checking the result of read_tsc() - cycle_last for being negative.
 * That works because CLOCKSOURCE_MASK(64) does not mask out any bit.
 */
static u64 read_tsc(struct clocksource *cs)
{
	return (u64)rdtsc_ordered();
}

static void tsc_cs_mark_unstable(struct clocksource *cs)
{
	if (tsc_unstable)
		return;

	tsc_unstable = 1;
	if (using_native_sched_clock())
		clear_sched_clock_stable();
	pr_info("Marking TSC unstable due to clocksource watchdog\n");
}

static void tsc_cs_tick_stable(struct clocksource *cs)
{
	if (tsc_unstable)
		return;

	if (using_native_sched_clock())
		sched_clock_tick_stable();
}

static int tsc_cs_enable(struct clocksource *cs)
{

Annotation

Implementation Notes