diff options
| author | Thomas Gleixner <tglx@linutronix.de> | 2007-10-11 11:17:24 +0200 | 
|---|---|---|
| committer | Thomas Gleixner <tglx@linutronix.de> | 2007-10-11 11:17:24 +0200 | 
| commit | 250c22777fe1ccd7ac588579a6c16db4c0161cc5 (patch) | |
| tree | 55c317efb7d792ec6fdae1d1937c67a502c48dec /arch/x86/kernel/tsc_sync.c | |
| parent | 2db55d344e529492545cb3b755c7e9ba8e4fa94e (diff) | |
| download | olio-linux-3.10-250c22777fe1ccd7ac588579a6c16db4c0161cc5.tar.xz olio-linux-3.10-250c22777fe1ccd7ac588579a6c16db4c0161cc5.zip  | |
x86_64: move kernel
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'arch/x86/kernel/tsc_sync.c')
| -rw-r--r-- | arch/x86/kernel/tsc_sync.c | 188 | 
1 files changed, 187 insertions, 1 deletions
diff --git a/arch/x86/kernel/tsc_sync.c b/arch/x86/kernel/tsc_sync.c index 12424629af8..355f5f506c8 100644 --- a/arch/x86/kernel/tsc_sync.c +++ b/arch/x86/kernel/tsc_sync.c @@ -1 +1,187 @@ -#include "../../x86_64/kernel/tsc_sync.c" +/* + * arch/x86_64/kernel/tsc_sync.c: 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/spinlock.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/smp.h> +#include <linux/nmi.h> +#include <asm/tsc.h> + +/* + * Entry/exit counters that make sure that both CPUs + * run the measurement code at once: + */ +static __cpuinitdata atomic_t start_count; +static __cpuinitdata atomic_t stop_count; + +/* + * 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 __cpuinitdata raw_spinlock_t sync_lock = __RAW_SPIN_LOCK_UNLOCKED; +static __cpuinitdata cycles_t last_tsc; +static __cpuinitdata cycles_t max_warp; +static __cpuinitdata int nr_warps; + +/* + * TSC-warp measurement loop running on both CPUs: + */ +static __cpuinit void check_tsc_warp(void) +{ +	cycles_t start, now, prev, end; +	int i; + +	start = get_cycles_sync(); +	/* +	 * The measurement runs for 20 msecs: +	 */ +	end = start + tsc_khz * 20ULL; +	now = start; + +	for (i = 0; ; i++) { +		/* +		 * We take the global lock, measure TSC, save the +		 * previous TSC that was measured (possibly on +		 * another CPU) and update the previous TSC timestamp. +		 */ +		__raw_spin_lock(&sync_lock); +		prev = last_tsc; +		now = get_cycles_sync(); +		last_tsc = now; +		__raw_spin_unlock(&sync_lock); + +		/* +		 * Be nice every now and then (and also check whether +		 * measurement is done [we also insert a 100 million +		 * loops safety exit, so we dont lock up in case the +		 * TSC readout is totally broken]): +		 */ +		if (unlikely(!(i & 7))) { +			if (now > end || i > 100000000) +				break; +			cpu_relax(); +			touch_nmi_watchdog(); +		} +		/* +		 * Outside the critical section we can now see whether +		 * we saw a time-warp of the TSC going backwards: +		 */ +		if (unlikely(prev > now)) { +			__raw_spin_lock(&sync_lock); +			max_warp = max(max_warp, prev - now); +			nr_warps++; +			__raw_spin_unlock(&sync_lock); +		} + +	} +} + +/* + * Source CPU calls into this - it waits for the freshly booted + * target CPU to arrive and then starts the measurement: + */ +void __cpuinit check_tsc_sync_source(int cpu) +{ +	int cpus = 2; + +	/* +	 * No need to check if we already know that the TSC is not +	 * synchronized: +	 */ +	if (unsynchronized_tsc()) +		return; + +	printk(KERN_INFO "checking TSC synchronization [CPU#%d -> CPU#%d]:", +			  smp_processor_id(), cpu); + +	/* +	 * Reset it - in case this is a second bootup: +	 */ +	atomic_set(&stop_count, 0); + +	/* +	 * Wait for the target to arrive: +	 */ +	while (atomic_read(&start_count) != cpus-1) +		cpu_relax(); +	/* +	 * Trigger the target to continue into the measurement too: +	 */ +	atomic_inc(&start_count); + +	check_tsc_warp(); + +	while (atomic_read(&stop_count) != cpus-1) +		cpu_relax(); + +	/* +	 * Reset it - just in case we boot another CPU later: +	 */ +	atomic_set(&start_count, 0); + +	if (nr_warps) { +		printk("\n"); +		printk(KERN_WARNING "Measured %Ld cycles TSC warp between CPUs," +				    " turning off TSC clock.\n", max_warp); +		mark_tsc_unstable("check_tsc_sync_source failed"); +		nr_warps = 0; +		max_warp = 0; +		last_tsc = 0; +	} else { +		printk(" passed.\n"); +	} + +	/* +	 * Let the target continue with the bootup: +	 */ +	atomic_inc(&stop_count); +} + +/* + * Freshly booted CPUs call into this: + */ +void __cpuinit check_tsc_sync_target(void) +{ +	int cpus = 2; + +	if (unsynchronized_tsc()) +		return; + +	/* +	 * Register this CPU's participation and wait for the +	 * source CPU to start the measurement: +	 */ +	atomic_inc(&start_count); +	while (atomic_read(&start_count) != cpus) +		cpu_relax(); + +	check_tsc_warp(); + +	/* +	 * Ok, we are done: +	 */ +	atomic_inc(&stop_count); + +	/* +	 * Wait for the source CPU to print stuff: +	 */ +	while (atomic_read(&stop_count) != cpus) +		cpu_relax(); +} +#undef NR_LOOPS +  |