linux/arch/x86/kernel/tsc_sync.c

// SPDX-License-Identifier: GPL-2.0
/*
 * check TSC synchronization.
 *
 * Copyright (C) 2006, Red Hat, Inc., Ingo Molnar
 *
 * We check whether all boot CPUs have their TSC's synchronized,
 * print a warning if not and turn off the TSC clock-source.
 *
 * The warp-check is point-to-point between two CPUs, the CPU
 * initiating the bootup is the 'source CPU', the freshly booting
 * CPU is the 'target CPU'.
 *
 * Only two CPUs may participate - they can enter in any order.
 * ( The serial nature of the boot logic and the CPU hotplug lock
 *   protects against more than 2 CPUs entering this code. )
 */
#include <linux/workqueue.h>
#include <linux/topology.h>
#include <linux/spinlock.h>
#include <linux/kernel.h>
#include <linux/smp.h>
#include <linux/nmi.h>
#include <asm/tsc.h>

struct tsc_adjust {};

static DEFINE_PER_CPU(struct tsc_adjust, tsc_adjust);
static struct timer_list tsc_sync_check_timer;

/*
 * TSC's on different sockets may be reset asynchronously.
 * This may cause the TSC ADJUST value on socket 0 to be NOT 0.
 */
bool __read_mostly tsc_async_resets;

void mark_tsc_async_resets(char *reason)
{}

void tsc_verify_tsc_adjust(bool resume)
{}

/*
 * Normally the tsc_sync will be checked every time system enters idle
 * state, but there is still caveat that a system won't enter idle,
 * either because it's too busy or configured purposely to not enter
 * idle.
 *
 * So setup a periodic timer (every 10 minutes) to make sure the check
 * is always on.
 */

#define SYNC_CHECK_INTERVAL

static void tsc_sync_check_timer_fn(struct timer_list *unused)
{}

static int __init start_sync_check_timer(void)
{}
late_initcall(start_sync_check_timer);

static void tsc_sanitize_first_cpu(struct tsc_adjust *cur, s64 bootval,
				   unsigned int cpu, bool bootcpu)
{}

#ifndef CONFIG_SMP
bool __init tsc_store_and_check_tsc_adjust(bool bootcpu)
{
	struct tsc_adjust *cur = this_cpu_ptr(&tsc_adjust);
	s64 bootval;

	if (!boot_cpu_has(X86_FEATURE_TSC_ADJUST))
		return false;

	/* Skip unnecessary error messages if TSC already unstable */
	if (check_tsc_unstable())
		return false;

	rdmsrl(MSR_IA32_TSC_ADJUST, bootval);
	cur->bootval = bootval;
	cur->nextcheck = jiffies + HZ;
	tsc_sanitize_first_cpu(cur, bootval, smp_processor_id(), bootcpu);
	return false;
}

#else /* !CONFIG_SMP */

/*
 * Store and check the TSC ADJUST MSR if available
 */
bool tsc_store_and_check_tsc_adjust(bool bootcpu)
{}

/*
 * Entry/exit counters that make sure that both CPUs
 * run the measurement code at once:
 */
static atomic_t start_count;
static atomic_t stop_count;
static atomic_t test_runs;

/*
 * We use a raw spinlock in this exceptional case, because
 * we want to have the fastest, inlined, non-debug version
 * of a critical section, to be able to prove TSC time-warps:
 */
static arch_spinlock_t sync_lock =;

static cycles_t last_tsc;
static cycles_t max_warp;
static int nr_warps;
static int random_warps;

/*
 * TSC-warp measurement loop running on both CPUs.  This is not called
 * if there is no TSC.
 */
static cycles_t check_tsc_warp(unsigned int timeout)
{}

/*
 * If the target CPU coming online doesn't have any of its core-siblings
 * online, a timeout of 20msec will be used for the TSC-warp measurement
 * loop. Otherwise a smaller timeout of 2msec will be used, as we have some
 * information about this socket already (and this information grows as we
 * have more and more logical-siblings in that socket).
 *
 * Ideally we should be able to skip the TSC sync check on the other
 * core-siblings, if the first logical CPU in a socket passed the sync test.
 * But as the TSC is per-logical CPU and can potentially be modified wrongly
 * by the bios, TSC sync test for smaller duration should be able
 * to catch such errors. Also this will catch the condition where all the
 * cores in the socket don't get reset at the same time.
 */
static inline unsigned int loop_timeout(int cpu)
{}

static void tsc_sync_mark_tsc_unstable(struct work_struct *work)
{}

static DECLARE_WORK(tsc_sync_work, tsc_sync_mark_tsc_unstable);

/*
 * The freshly booted CPU initiates this via an async SMP function call.
 */
static void check_tsc_sync_source(void *__cpu)
{}

/*
 * Freshly booted CPUs call into this:
 */
void check_tsc_sync_target(void)
{}

#endif /* CONFIG_SMP */