Trace Clock v4
Subsystems (8)
cover Trace Clock v4
Hi Ingo,
Can we give it a try in -tip ?
This tracer clock infrastructure is rebased against current -tip and includes the comments received in the previous posts. MIPS should be tested carefully, because this patchset adds TSC synchronicity test to the architecture and I don't have any MIPS board to test (I rely on the LTTng community for that).
Mathieu
Signature · 2 lines
Mathieu Desnoyers OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F BA06 3F25 A8FE 3BAE 9A68
PATCH 1/17 get_cycles() : kconfig HAVE_GET_CYCLES
Create a new "HAVE_GET_CYCLES" architecture option to specify which architectures provide 64-bits TSC counters readable with get_cycles(). It's principally useful to only enable high-precision tracing code only on such architectures and don't even bother building it on architectures which lack such support.
It also requires architectures to provide get_cycles_barrier() and get_cycles_rate().
I mainly use it for the "priority-sifting rwlock" latency tracing code, which traces worse-case latency induced by the locking. It also provides the basic changes needed for the LTTng timestamping infrastructure.
--- init/Kconfig | 10 ++++++++++ 1 file changed, 10 insertions(+)
init/Kconfig +10 -0
@@ -330,6 +330,16 @@ config CPUSETS config HAVE_UNSTABLE_SCHED_CLOCK bool +# +# Architectures with a 64-bits get_cycles() should select this. +# They should also define +# get_cycles_barrier() : instruction synchronization barrier if required +# get_cycles_rate() : cycle counter rate, in HZ. If 0, TSC are not synchronized +# across CPUs or their frequency may vary due to frequency scaling. +# +config HAVE_GET_CYCLES + def_bool n + config GROUP_SCHED bool "Group CPU scheduler" depends on EXPERIMENTAL
Signature · 2 lines
Mathieu Desnoyers OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F BA06 3F25 A8FE 3BAE 9A68
PATCH 2/17 get_cycles() : x86 HAVE_GET_CYCLES
In reply to Mathieu DesnoyersThis patch selects HAVE_GET_CYCLES and makes sure get_cycles_barrier() and get_cycles_rate() are implemented.
--- arch/x86/Kconfig | 1 + arch/x86/include/asm/tsc.h | 12 ++++++++++++ 2 files changed, 13 insertions(+)
arch/x86/Kconfig +1 -0
@@ -20,6 +20,7 @@ config X86 def_bool y select HAVE_AOUT if X86_32 select HAVE_UNSTABLE_SCHED_CLOCK + select HAVE_GET_CYCLES select HAVE_IDE select HAVE_OPROFILE select HAVE_IOREMAP_PROT
arch/x86/include/asm/tsc.h +12 -0
@@ -50,6 +50,18 @@ extern void mark_tsc_unstable(char *reasextern int unsynchronized_tsc (void );int check_tsc_unstable (void ); +static inline cycles_t get_cycles_rate (void ) +{ +if (check_tsc_unstable ()) +return 0 ; +return (cycles_t )tsc_khz *1000 ; +} + +static inline void get_cycles_barrier (void ) +{ +rdtsc_barrier (); +} +/* * Boot-time check whether the TSCs are synchronized across * all CPUs/cores:
Signature · 2 lines
Mathieu Desnoyers OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F BA06 3F25 A8FE 3BAE 9A68
PATCH 3/17 get_cycles() : sparc64 HAVE_GET_CYCLES
In reply to Mathieu DesnoyersThis patch selects HAVE_GET_CYCLES and makes sure get_cycles_barrier() and get_cycles_rate() are implemented.
Changelog :
- Use tb_ticks_per_usec * 1000000 in get_cycles_rate().
--- arch/sparc/include/asm/timex_64.h | 19 ++++++++++++++++++- arch/sparc64/Kconfig | 1 + arch/sparc64/kernel/time.c | 3 ++- 3 files changed, 21 insertions(+), 2 deletions(-)
arch/sparc64/Kconfig +1 -0
@@ -13,6 +13,7 @@ config SPARC64 default y select HAVE_FUNCTION_TRACER select HAVE_IDE + select HAVE_GET_CYCLES select HAVE_LMB select HAVE_ARCH_KGDB select USE_GENERIC_SMP_HELPERS if SMP
arch/sparc/include/asm/timex_64.h +18 -1
@@ -12,7 +12,24 @@/* Getting on the cycle counter on sparc64. */ typedef unsigned long cycles_t ; -#define get_cycles () tick_ops->get_tick() + +static inline cycles_t get_cycles (void ) +{ +return tick_ops->get_tick (); +} + +/* get_cycles instruction is synchronized on sparc64 */ +static inline void get_cycles_barrier (void ) +{ +return ; +} + +extern unsigned long tb_ticks_per_usec; + +static inline cycles_t get_cycles_rate (void ) +{ +return (cycles_t )tb_ticks_per_usec *1000000UL ; +}#define ARCH_HAS_READ_CURRENT_TIMER
arch/sparc64/kernel/time.c +2 -1
@@ -793,7 +793,8 @@ static void __init setup_clockevent_mult sparc64_clockevent.mult = mult; } -static unsigned long tb_ticks_per_usec__read_mostly ; +unsigned long tb_ticks_per_usec__read_mostly ; +EXPORT_SYMBOL_GPL (tb_ticks_per_usec);void __delay (unsigned long loops) {
Signature · 2 lines
Mathieu Desnoyers OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F BA06 3F25 A8FE 3BAE 9A68
PATCH 4/17 get_cycles() : powerpc64 HAVE_GET_CYCLES
In reply to Mathieu DesnoyersThis patch selects HAVE_GET_CYCLES and makes sure get_cycles_barrier() and get_cycles_rate() are implemented.
--- arch/powerpc/Kconfig | 1 + arch/powerpc/include/asm/timex.h | 11 +++++++++++ 2 files changed, 12 insertions(+)
arch/powerpc/Kconfig +1 -0
@@ -121,6 +121,7 @@ config PPC select HAVE_DMA_ATTRS if PPC64 select USE_GENERIC_SMP_HELPERS if SMP select HAVE_OPROFILE + select HAVE_GET_CYCLES if PPC64 config EARLY_PRINTK bool
arch/powerpc/include/asm/timex.h +11 -0
@@ -7,6 +7,7 @@* PowerPC architecture timex specifications */ +#include <asm/time.h> #include <asm/cputable.h> #include <asm/reg.h>
@@ -46,5 +47,15 @@ static inline cycles_t get_cycles(void)#endif } +static inline cycles_t get_cycles_rate (void ) +{ +return tb_ticks_per_sec; +} + +static inline void get_cycles_barrier (void ) +{ +isync (); +} +#endif /* __KERNEL__ */ #endif /* _ASM_POWERPC_TIMEX_H */
Signature · 2 lines
Mathieu Desnoyers OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F BA06 3F25 A8FE 3BAE 9A68
PATCH 5/17 MIPS : export hpt frequency for get_cycles
In reply to Mathieu Desnoyersget_cycles_rate() needs to export the hpt frequency to modules (e.g. LTTng).
--- arch/mips/include/asm/timex.h | 2 ++ arch/mips/kernel/time.c | 1 + 2 files changed, 3 insertions(+)
arch/mips/include/asm/timex.h +2 -0
@@ -20,6 +20,8 @@*/ #define CLOCK_TICK_RATE 1193182 +extern unsigned int mips_hpt_frequency; +/* * Standard way to access the cycle counter. * Currently only used on SMP for scheduling.
arch/mips/kernel/time.c +1 -0
@@ -70,6 +70,7 @@ EXPORT_SYMBOL(perf_irq);*/ unsigned int mips_hpt_frequency; +EXPORT_SYMBOL (mips_hpt_frequency);void __init clocksource_set_clock (struct clocksource *cs,unsigned int clock) {
Signature · 2 lines
Mathieu Desnoyers OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F BA06 3F25 A8FE 3BAE 9A68
PATCH 6/17 get_cycles() : MIPS HAVE_GET_CYCLES_32
In reply to Mathieu Desnoyerspartly reverts commit efb9ca08b5a2374b29938cdcab417ce4feb14b54. Selects HAVE_GET_CYCLES_32 only on CPUs where it is safe to use it.
Currently consider the "_WORKAROUND" cases for 4000 and 4400 to be unsafe, but should probably add other sub-architecture to the blacklist.
Do not define HAVE_GET_CYCLES because MIPS does not provide 64-bit tsc (only 32-bits).
get_cycles_rate() now uses mips_hpt_frequency.
--- arch/mips/Kconfig | 4 ++++ arch/mips/include/asm/timex.h | 25 +++++++++++++++++++++++++ 2 files changed, 29 insertions(+)
arch/mips/include/asm/timex.h +25 -0
@@ -31,14 +31,39 @@ extern unsigned int mips_hpt_frequency;* which isn't an evil thing. * * We know that all SMP capable CPUs have cycle counters. +* +* Mathieu Desnoyers <[email protected]> +* HAVE_GET_CYCLES makes sure that this case is handled properly : +* +* Ralf Baechle <[email protected]> : +* This avoids us executing an mfc0 c0_count instruction on processors which +* don't have but also on certain R4000 and R4400 versions where reading from +* the count register just in the very moment when its value equals c0_compare +* will result in the timer interrupt getting lost. */ typedef unsigned int cycles_t ; +#ifdef HAVE_GET_CYCLES_32 +static inline cycles_t get_cycles (void ) +{ +return read_c0_count (); +} + +static inline void get_cycles_barrier (void ) +{ +} + +static inline cycles_t get_cycles_rate (void ) +{ +return mips_hpt_frequency; +} +#else static inline cycles_t get_cycles (void ) {return 0 ; } +#endif #endif /* __KERNEL__ */
arch/mips/Kconfig +4 -0
@@ -1611,6 +1611,10 @@ config CPU_R4000_WORKAROUNDS config CPU_R4400_WORKAROUNDS bool +config HAVE_GET_CYCLES_32 + def_bool y + depends on !CPU_R4400_WORKAROUNDS + # # Use the generic interrupt handling code in kernel/irq/: #
Signature · 2 lines
Mathieu Desnoyers OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F BA06 3F25 A8FE 3BAE 9A68
PATCH 7/17 Trace clock core
In reply to Mathieu Desnoyers32 to 64 bits clock extension. Extracts 64 bits tsc from a [1..32] bits counter, kept up to date by periodical timer interrupt. Lockless.
It's actually a specialized version of cnt_32_to_63.h which does the following in addition :
- Uses per-cpu data to keep track of counters.
- It limits cache-line bouncing
- I supports machines with non-synchronized TSCs.
- Does not require read barriers, which can be slow on some architectures.
- Supports a full 64-bits counter (well, just one bit more than 63 is not really a big deal when we talk about timestamp counters). If 2^64 is considered long enough between overflows, 2^63 is normally considered long enough too.
- The periodical update of the value is insured by the infrastructure. There is no assumption that the counter is read frequently, because we cannot assume that given the events for which tracing is enabled can be dynamically selected.
- Supports counters of various width (32-bits and below) by changing the HW_BITS define.
What cnt_32_to_63.h does that this patch doesn't do :
- It has a global counter, which removes the need to do an update periodically on _each_ cpu. This can be important in a dynamic tick system where CPUs need to sleep to save power. It is therefore well suited for systems reading a global clock expected to be _exactly_ synchronized across cores (where time can never ever go backward).
Q:
Quoted 3 lines
do you actually use the RCU internals? or do you just reimplement an RCU algorithm?
A:
Nope, I don't use RCU internals in this code. Preempt disable seemed like the best way to handle this utterly short code path and I wanted the write side to be fast enough to be called periodically. What I do is:
- Disable preemption at the read-side :
it makes sure the pointer I get will point to a data structure that will never change while I am in the preempt disabled code. (see *)
- I use per-cpu data to allow the read-side to be as fast as possible (only need to disable preemption, does not race against other CPUs and won't generate cache line bouncing). It also allows dealing with unsynchronized TSCs if needed.
- Periodical write side : it's called from an IPI running on each CPU.
(*) We expect the read-side (preempt off region) to last shorter than the interval between IPI updates so we can guarantee the data structure it uses won't be modified underneath it. Since the IPI update is launched each seconds or so (depends on the frequency of the counter we are trying to extend), it's more than ok.
Changelog:
- Support [1..32] bits -> 64 bits.
I volountarily limit the code to use at most 32 bits of the hardware clock for performance considerations. If this is a problem it could be changed. Also, the algorithm is aimed at a 32 bits architecture. The code becomes muuuch simpler on a 64 bits arch, since we can do the updates atomically.
--- init/Kconfig | 12 + kernel/trace/Makefile | 1 kernel/trace/trace-clock-32-to-64.c | 281 ++++++++++++++++++++++++++++++++++++ 3 files changed, 294 insertions(+)
kernel/trace/Makefile +1 -0
@@ -33,5 +33,6 @@ obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += t obj-$(CONFIG_TRACE_BRANCH_PROFILING) += trace_branch.o obj-$(CONFIG_BTS_TRACER) += trace_bts.o obj-$(CONFIG_POWER_TRACER) += trace_power.o +obj-$(CONFIG_HAVE_TRACE_CLOCK_32_TO_64) += trace-clock-32-to-64.o libftrace-y := ftrace.o
kernel/trace/trace-clock-32-to-64.c new file +281 -0
281 lines not on this page · raw message
init/Kconfig +12 -0
@@ -340,6 +340,18 @@ config HAVE_UNSTABLE_SCHED_CLOCK config HAVE_GET_CYCLES def_bool n +# +# Architectures with a specialized tracing clock should select this. +# +config HAVE_TRACE_CLOCK + def_bool n + +# +# Architectures with only a 32-bits clock source should select this. +# +config HAVE_TRACE_CLOCK_32_TO_64 + def_bool n + config GROUP_SCHED bool "Group CPU scheduler" depends on EXPERIMENTAL
Signature · 2 lines
Mathieu Desnoyers OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F BA06 3F25 A8FE 3BAE 9A68
PATCH 8/17 Trace clock generic
In reply to Mathieu DesnoyersWrapper to use the lower level clock sources available on the systems. Fall-back on a counter incremented by a timer interrupt every jiffy or'd with a logical clock for architectures lacking CPU timestamp counters.
A generic fallback based on a logical clock and the timer interrupt is available.
generic - Uses jiffies or'd with a logical clock extended to 64 bits by
trace-clock-32-to-64.
i386 - Uses TSC. If detects non synchronized TSC, uses mixed TSC-logical clock. mips - Uses TSC extended atomically from 32 to 64 bits by trace-clock-32-to-64. powerpc - Uses TSC.
sparc64 - Uses TSC.x86_64 - Uses TSC. If detects non synchronized TSC, uses mixed TSC-logical clock
--- include/asm-generic/trace-clock.h | 64 +++++++++++++++++++++++++ include/linux/trace-clock.h | 17 ++++++ init/Kconfig | 6 ++ kernel/trace/Makefile | 1 kernel/trace/trace-clock.c | 97 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 185 insertions(+)
include/asm-generic/trace-clock.h new file +64 -0
@@ -0,0 +1,64 @@ +#ifndef _ASM_GENERIC_TRACE_CLOCK_H +#define _ASM_GENERIC_TRACE_CLOCK_H + +/* +* include/asm-generic/trace-clock.h +* +* Copyright (C) 2007 - Mathieu Desnoyers ([email protected]) +* +* Generic tracing clock for architectures without TSC. +*/ + +#include <linux/param.h> /* For HZ */ +#include <asm/atomic.h> + +#define TRACE_CLOCK_SHIFT 13 + +extern atomic_long_t trace_clock; + +static inline u32 trace_clock_read32 (void ) +{ +return (u32 )atomic_long_add_return (1 , &trace_clock); +} + +#ifdef CONFIG_HAVE_TRACE_CLOCK_32_TO_64 +extern u64 trace_clock_read_synthetic_tsc (void ); +extern void get_synthetic_tsc (void ); +extern void put_synthetic_tsc (void ); + +static inline u64 trace_clock_read64 (void ) +{ +return trace_clock_read_synthetic_tsc (); +} +#else +static inline void get_synthetic_tsc (void ) +{ +} + +static inline void put_synthetic_tsc (void ) +{ +} + +static inline u64 trace_clock_read64 (void ) +{ +return atomic_long_add_return (1 , &trace_clock); +} +#endif + +static inline unsigned int trace_clock_frequency (void ) +{ +return HZ <<TRACE_CLOCK_SHIFT ; +} + +static inline u32 trace_clock_freq_scale (void ) +{ +return 1 ; +} + +extern void get_trace_clock (void ); +extern void put_trace_clock (void ); + +static inline void set_trace_clock_is_sync (int state) +{ +} +#endif /* _ASM_GENERIC_TRACE_CLOCK_H */
include/linux/trace-clock.h new file +17 -0
@@ -0,0 +1,17 @@ +#ifndef _LINUX_TRACE_CLOCK_H +#define _LINUX_TRACE_CLOCK_H + +/* +* Trace clock +* +* Chooses between an architecture specific clock or an atomic logical clock. +* +* Copyright (C) 2007,2008 Mathieu Desnoyers ([email protected]) +*/ + +#ifdef CONFIG_HAVE_TRACE_CLOCK +#include <asm/trace-clock.h> +#else +#include <asm-generic/trace-clock.h> +#endif /* CONFIG_HAVE_TRACE_CLOCK */ +#endif /* _LINUX_TRACE_CLOCK_H */
kernel/trace/trace-clock.c new file +97 -0
97 lines not on this page · raw message
init/Kconfig +6 -0
@@ -346,6 +346,12 @@ config HAVE_GET_CYCLES config HAVE_TRACE_CLOCK def_bool n +config HAVE_TRACE_CLOCK_GENERIC + bool + default y if (!HAVE_TRACE_CLOCK) + default n if HAVE_TRACE_CLOCK + select HAVE_TRACE_CLOCK_32_TO_64 if (!64BIT) + # # Architectures with only a 32-bits clock source should select this. #
kernel/trace/Makefile +1 -0
@@ -34,5 +34,6 @@ obj-$(CONFIG_TRACE_BRANCH_PROFILING) += obj-$(CONFIG_BTS_TRACER) += trace_bts.o obj-$(CONFIG_POWER_TRACER) += trace_power.o obj-$(CONFIG_HAVE_TRACE_CLOCK_32_TO_64) += trace-clock-32-to-64.o +obj-$(CONFIG_HAVE_TRACE_CLOCK_GENERIC) += trace-clock.o libftrace-y := ftrace.o
Signature · 2 lines
Mathieu Desnoyers OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F BA06 3F25 A8FE 3BAE 9A68
PATCH 9/17 Powerpc : Trace clock
In reply to Mathieu DesnoyersPowerpc implementation of trace clock with get_tb().
--- arch/powerpc/Kconfig | 1 arch/powerpc/include/asm/trace-clock.h | 47 +++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+)
arch/powerpc/Kconfig +1 -0
@@ -114,6 +114,7 @@ config PPC select HAVE_IOREMAP_PROT select HAVE_EFFICIENT_UNALIGNED_ACCESS select HAVE_KPROBES + select HAVE_TRACE_CLOCK select HAVE_ARCH_KGDB select HAVE_KRETPROBES select HAVE_ARCH_TRACEHOOK
arch/powerpc/include/asm/trace-clock.h new file +47 -0
47 lines not on this page · raw message
Signature · 2 lines
Mathieu Desnoyers OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F BA06 3F25 A8FE 3BAE 9A68
PATCH 10/17 Sparc64 : Trace clock
In reply to Mathieu DesnoyersImplement sparc64 trace clock.
--- arch/sparc/include/asm/trace-clock.h | 43 +++++++++++++++++++++++++++++++++++ arch/sparc64/Kconfig | 1 2 files changed, 44 insertions(+)
arch/sparc64/Kconfig +1 -0
@@ -18,6 +18,7 @@ config SPARC64 select HAVE_ARCH_KGDB select USE_GENERIC_SMP_HELPERS if SMP select HAVE_ARCH_TRACEHOOK + select HAVE_TRACE_CLOCK select ARCH_WANT_OPTIONAL_GPIOLIB select RTC_CLASS select RTC_DRV_M48T59
arch/sparc/include/asm/trace-clock.h new file +43 -0
43 lines not on this page · raw message
Signature · 2 lines
Mathieu Desnoyers OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F BA06 3F25 A8FE 3BAE 9A68
PATCH 11/17 LTTng timestamp sh
In reply to Mathieu DesnoyersThis patch adds the timestamping mechanism in the trace-clock.h arch header file. The new timestamp functions use the TMU channel 1.
This code only works if the TMU channel 1 is initialized during the kernel boot.
Big fat warning(TM) from Mathieu Desnoyers :
This patch seems to assume TMU channel 1 is setup at boot. Is it always true on all SuperH boards ? Is there some Kconfig selection that should be done here ? Make sure this patch does not break get_cycles on SuperH before merging.
From: Giuseppe Cavallaro <[email protected]>
--- arch/sh/Kconfig | 2 + arch/sh/include/asm/timex.h | 7 +++- arch/sh/include/asm/trace-clock.h | 58 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+), 2 deletions(-)
Index: linux.trees.git/arch/sh/include/asm/timex.h
=================================--- linux.trees.git.orig/arch/sh/include/asm/timex.h 2008-11-12 18:15:48.000000000 -0500
+++ linux.trees.git/arch/sh/include/asm/timex.h 2008-11-26 06:52:23.000000000 -0500
@@ -6,13 +6,16 @@
#ifndef __ASM_SH_TIMEX_H
#define __ASM_SH_TIMEX_H
-#define CLOCK_TICK_RATE (CONFIG_SH_PCLK_FREQ / 4) /* Underlying HZ */
+#include <linux/io.h>
+#include <asm/cpu/timer.h>
+
+#define CLOCK_TICK_RATE (HZ * 100000UL)
typedef unsigned long long cycles_t;
static __inline__ cycles_t get_cycles (void)
{
- return 0;
+ return 0xffffffff - ctrl_inl(TMU1_TCNT);
}
#endif /* __ASM_SH_TIMEX_H */
Index: linux.trees.git/arch/sh/Kconfig
=================================--- linux.trees.git.orig/arch/sh/Kconfig 2008-11-12 18:15:47.000000000 -0500
+++ linux.trees.git/arch/sh/Kconfig 2008-11-26 06:52:23.000000000 -0500
@@ -11,6 +11,8 @@ config SUPERH
select HAVE_CLK
select HAVE_IDE
select HAVE_OPROFILE
+ select HAVE_TRACE_CLOCK
+ select HAVE_TRACE_CLOCK_32_TO_64
select HAVE_GENERIC_DMA_COHERENT
select HAVE_IOREMAP_PROT if MMU
help
Index: linux.trees.git/arch/sh/include/asm/trace-clock.h
=================================--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux.trees.git/arch/sh/include/asm/trace-clock.h 2008-11-26 06:52:23.000000000 -0500
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2007,2008 Giuseppe Cavallaro <[email protected]>
+ * Mathieu Desnoyers <[email protected]>
+ *
+ * Trace clock definitions for SuperH.
+ */
+
+#ifndef _ASM_SH_TRACE_CLOCK_H
+#define _ASM_SH_TRACE_CLOCK_H
+
+#include <linux/timer.h>
+#include <asm/clock.h>
+
+extern u64 trace_clock_read_synthetic_tsc(void);
+
+static inline u32 trace_clock_get_timestamp32(void)
+{
+ return get_cycles();
+}
+
+static inline u64 trace_clock_get_timestamp64(void)
+{
+ return trace_clock_read_synthetic_tsc();
+}
+
+static inline u64 trace_clock_frequency(void)
+{
+ u64 rate;
+ struct clk *tmu1_clk;
+
+ tmu1_clk = clk_get(NULL, "tmu1_clk");
+ rate = clk_get_rate(tmu1_clk);
+
+ return rate;
+}
+
+static inline u32 trace_clock_freq_scale(void)
+{
+ return 1;
+}
+
+extern void get_synthetic_tsc(void);
+extern void put_synthetic_tsc(void);
+
+static inline void get_trace_clock(void)
+{
+ get_synthetic_tsc();
+}
+
+static inline void put_trace_clock(void)
+{
+ put_synthetic_tsc();
+}
+
+static inline void set_trace_clock_is_sync(int state)
+{
+}
+#endif /* _ASM_SH_TRACE_CLOCK_H */
Signature · 2 lines
Mathieu Desnoyers OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F BA06 3F25 A8FE 3BAE 9A68
PATCH 12/17 LTTng - TSC synchronicity test
In reply to Mathieu DesnoyersTest TSC synchronization across CPUs. Architecture independant and can therefore be used on various architectures. Aims at testing the TSC synchronization on a running system (not only at early boot), with minimal impact on interrupt latency.
I've written this code before x86 tsc_sync.c existed and given it worked well for my needs, I never switched to tsc_sync.c. Although it has the same goal, it does it a bit differently :
tsc_sync looks at the cycle counters on two CPUs to see if one compared to the other are going backward when read in loop. The LTTng code synchronizes both cores with a counter used as a memory barrier and then reads the two TSCs at a delta equal to the cache line exchange. Instruction and data caches are primed. This test is repeated in loops to insure we deal with MCE, NMIs which could skew the results.
The problem I see with tsc_sync.c is that is one of the two CPUs is delayed by an interrupt handler (for way too long) while the other CPU is doing its check_tsc_warp() execution, and if the CPU with the lowest TSC values runs first, this code will fail to detect unsynchronized CPUs.
This sync test code does not have this problem.
A following patch replaces the x86 tsc_sync.c code by this architecture independant code.
This code also adds the kernel parameter
force_tsc_sync=1
which forces resynchronization of CPU TSCs when a CPU is hotplugged.
--- Documentation/kernel-parameters.txt | 4 init/Kconfig | 7 kernel/time/Makefile | 1 kernel/time/tsc-sync.c | 313 ++++++++++++++++++++++++++++++++++++ 4 files changed, 325 insertions(+)
kernel/time/tsc-sync.c new file +313 -0
@@ -0,0 +1,313 @@ +/* +* kernel/time/tsc-sync.c +* +* Test TSC synchronization +* +* marks the tsc as unstable _and_ keep a simple "_tsc_is_sync" variable, which +* is fast to read when a simple test must determine which clock source to use +* for kernel tracing. +* +* - CPU init : +* +* We check whether all boot CPUs have their TSC's synchronized, +* print a warning if not and turn off the TSC clock-source. +* +* 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. +* +* - When CPUs are up : +* +* TSC synchronicity of all CPUs can be checked later at run-time by calling +* test_tsc_synchronization(). +* +* Copyright 2007, 2008 +* Mathieu Desnoyers <[email protected]> +*/ +#include <linux/module.h> +#include <linux/timer.h> +#include <linux/timex.h> +#include <linux/jiffies.h> +#include <linux/trace-clock.h> +#include <linux/cpu.h> +#include <linux/kthread.h> +#include <linux/mutex.h> +#include <linux/cpu.h> + +#define MAX_CYCLES_DELTA 1000ULL + +/* +* Number of loops to take care of MCE, NMIs, SMIs. +*/ +#define NR_LOOPS 10 + +static DEFINE_MUTEX (tscsync_mutex); + +struct sync_data { +int nr_waits; +int wait_sync; +cycles_t tsc_count; +}____cacheline_aligned ; + +/* 0 is master, 1 is slave */ +static struct sync_data sync_data[2 ] = { + [0 ...1 ] = { + .nr_waits =3 *NR_LOOPS +1 , + .wait_sync =3 *NR_LOOPS +1 , + }, +}; + +int _tsc_is_sync =1 ; +EXPORT_SYMBOL (_tsc_is_sync); + +static int force_tsc_sync; +static cycles_t slave_offset; +static int slave_offset_ready;/* for 32-bits architectures */ + +static int __init force_tsc_sync_setup (char *str) +{ + force_tsc_sync =simple_strtoul (str,NULL ,0 ); +return 1 ; +} +__setup ("force_tsc_sync=" , force_tsc_sync_setup); + +/* +* Mark it noinline so we make sure it is not unrolled. +* Wait until value is reached. +*/ +static noinline void tsc_barrier(long this_cpu) +{ +sync_core (); + sync_data[this_cpu].wait_sync--; +smp_mb ();/* order master/slave sync_data read/write */ +while (unlikely (sync_data[1 - this_cpu].wait_sync >= + sync_data[this_cpu].nr_waits)) +barrier ();/* +* barrier is used because faster and +* more predictable than cpu_idle(). +*/ +smp_mb ();/* order master/slave sync_data read/write */ + sync_data[this_cpu].nr_waits--; +get_cycles_barrier (); + sync_data[this_cpu].tsc_count =get_cycles (); +get_cycles_barrier (); +} + +/* +* Worker thread called on each CPU. +* First wait with interrupts enabled, then wait with interrupt disabled, +* for precision. We are already bound to one CPU. +* this_cpu 0 : master +* this_cpu 1 : slave +*/ +static void test_sync (void *arg) +{ +long this_cpu = (long )arg; +unsigned long flags; + +local_irq_save (flags); +/* Make sure the instructions are in I-CACHE */ +tsc_barrier (this_cpu); +tsc_barrier (this_cpu); + sync_data[this_cpu].wait_sync--; +smp_mb ();/* order master/slave sync_data read/write */ +while (unlikely (sync_data[1 - this_cpu].wait_sync >= + sync_data[this_cpu].nr_waits)) +barrier ();/* +* barrier is used because faster and +* more predictable than cpu_idle(). +*/ +smp_mb ();/* order master/slave sync_data read/write */ + sync_data[this_cpu].nr_waits--; +/* +* Here, only the master will wait for the slave to reach this barrier. +* This makes sure that the master, which holds the mutex and will reset +* the barriers, waits for the slave to stop using the barrier values +* before it continues. This is only done at the complete end of all the +* loops. This is why there is a + 1 in original wait_sync value. +*/ +if (sync_data[this_cpu].nr_waits ==1 ) + sync_data[this_cpu].wait_sync--; +local_irq_restore (flags); +} + +/* +* Each CPU (master and target) must decrement the wait_sync value twice (one +* for priming in cache), and also once after the get_cycles. After all the +* loops, one last synchronization is required to make sure the master waits +* for the slave before resetting the barriers. +*/ +static void reset_barriers (void ) +{ +int i; + +/* +* Wait until slave is done so that we don't overwrite +* wait_end_sync prematurely. +*/ +smp_mb ();/* order master/slave sync_data read/write */ +while (unlikely (sync_data[1 ].wait_sync >= sync_data[0 ].nr_waits)) +barrier ();/* +* barrier is used because faster and +* more predictable than cpu_idle(). +*/ +smp_mb ();/* order master/slave sync_data read/write */ + +for (i =0 ; i <2 ; i++) { +WARN_ON (sync_data[i].wait_sync !=0 ); +WARN_ON (sync_data[i].nr_waits !=1 ); + sync_data[i].wait_sync =3 *NR_LOOPS +1 ; + sync_data[i].nr_waits =3 *NR_LOOPS +1 ; + } +} + +/* +* Do loops (making sure no unexpected event changes the timing), keep the best +* one. The result of each loop is the highest tsc delta between the master CPU +* and the slaves. Stop CPU hotplug when this code is executed to make sure we +* are concurrency-safe wrt CPU hotplug also using this code. Test TSC +* synchronization even if we already "know" CPUs were not synchronized. This +* can be used as a test to check if, for some reason, the CPUs eventually got +* in sync after a CPU has been unplugged. This code is kept separate from the +* CPU hotplug code because the slave CPU executes in an IPI, which we want to +* keep as short as possible (this is happening while the system is running). +* Therefore, we do not send a single IPI for all the test loops, but rather +* send one IPI per loop. +*/ +int test_tsc_synchronization (void ) +{ +long cpu, master; +cycles_t max_diff =0 , diff, best_loop, worse_loop =0 ; +int i; + +mutex_lock (&tscsync_mutex); +get_online_cpus (); + +printk (KERN_INFO +"checking TSC synchronization across all online CPUs:" ); + +preempt_disable (); + master =smp_processor_id (); +for_each_online_cpu (cpu) { +if (master == cpu) +continue ; + best_loop = (cycles_t )ULLONG_MAX ; +for (i =0 ; i <NR_LOOPS ; i++) { +smp_call_function_single (cpu, test_sync, + (void *)1UL ,0 ); +test_sync ((void *)0UL ); + diff =abs (sync_data[1 ].tsc_count + - sync_data[0 ].tsc_count); + best_loop =min (best_loop, diff); + worse_loop =max (worse_loop, diff); + } +reset_barriers (); + max_diff =max (best_loop, max_diff); + } +preempt_enable (); +if (max_diff >=MAX_CYCLES_DELTA ) { +printk (KERN_WARNING +"Measured %llu cycles TSC offset between CPUs," +" turning off TSC clock.\n" , (u64 )max_diff); +mark_tsc_unstable ("check_tsc_sync_source failed" ); + _tsc_is_sync =0 ; + }else { +printk (" passed.\n" ); + } +put_online_cpus (); +mutex_unlock (&tscsync_mutex); +return max_diff <MAX_CYCLES_DELTA ; +} +EXPORT_SYMBOL_GPL (test_tsc_synchronization); + +/* +* Test synchronicity of a single core when it is hotplugged. +* Source CPU calls into this - waits for the freshly booted target CPU to +* arrive and then start the measurement: +*/ +void __cpuinit check_tsc_sync_source (int cpu) +{ +cycles_t diff, abs_diff, + best_loop = (cycles_t )ULLONG_MAX , worse_loop =0 ; +int i; + +/* +* No need to check if we already know that the TSC is not synchronized: +*/ +if (!force_tsc_sync &&unsynchronized_tsc ()) { +/* +* Make sure we mark _tsc_is_sync to 0 if the TSC is found +* to be unsynchronized for other causes than non-synchronized +* TSCs across CPUs. +*/ + _tsc_is_sync =0 ; +set_trace_clock_is_sync (0 ); +return ; + } + +printk (KERN_INFO "checking TSC synchronization [CPU#%d -> CPU#%d]:" , +smp_processor_id (), cpu); + +for (i =0 ; i <NR_LOOPS ; i++) { +test_sync ((void *)0UL ); + diff = sync_data[1 ].tsc_count - sync_data[0 ].tsc_count; + abs_diff =abs (diff); + best_loop =min (best_loop, abs_diff); + worse_loop =max (worse_loop, abs_diff); +if (force_tsc_sync && best_loop == abs_diff) + slave_offset = diff; + } +reset_barriers (); + +if (!force_tsc_sync && best_loop >=MAX_CYCLES_DELTA ) { +printk (" failed.\n" ); +printk (KERN_WARNING +"Measured %llu cycles TSC offset between CPUs," +" turning off TSC clock.\n" , (u64 )best_loop); +mark_tsc_unstable ("check_tsc_sync_source failed" ); + _tsc_is_sync =0 ; +set_trace_clock_is_sync (0 ); + }else { +printk (" %s.\n" , !force_tsc_sync ?"passed" :"forced" ); + } +if (force_tsc_sync) { +/* order slave_offset and slave_offset_ready writes */ +smp_wmb (); + slave_offset_ready =1 ; + } +} + +/* +* Freshly booted CPUs call into this: +*/ +void __cpuinit check_tsc_sync_target (void ) +{ +int i; + +if (!force_tsc_sync &&unsynchronized_tsc ()) +return ; + +for (i =0 ; i <NR_LOOPS ; i++) +test_sync ((void *)1UL ); + +/* +* Force slave synchronization if requested. +*/ +if (force_tsc_sync) { +unsigned long flags; +cycles_t new_tsc; + +while (!slave_offset_ready) +cpu_relax (); +/* order slave_offset and slave_offset_ready reads */ +smp_rmb (); +local_irq_save (flags); +/* +* slave_offset is read when master has finished writing to it, +* and is protected by cpu hotplug serialization. +*/ + new_tsc =get_cycles () - slave_offset; +write_tsc ((u32 )new_tsc, (u32)((u64 )new_tsc >>32 )); +local_irq_restore (flags); + } +}
kernel/time/Makefile +1 -0
@@ -6,3 +6,4 @@ obj-$(CONFIG_GENERIC_CLOCKEVENTS_BROADCA obj-$(CONFIG_TICK_ONESHOT) += tick-oneshot.o obj-$(CONFIG_TICK_ONESHOT) += tick-sched.o obj-$(CONFIG_TIMER_STATS) += timer_stats.o +obj-$(CONFIG_HAVE_UNSYNCHRONIZED_TSC) += tsc-sync.o
init/Kconfig +7 -0
@@ -358,6 +358,13 @@ config HAVE_TRACE_CLOCK_GENERIC config HAVE_TRACE_CLOCK_32_TO_64 def_bool n +# +# Architectures which need to dynamically detect if their TSC is unsynchronized +# across cpus should select this. +# +config HAVE_UNSYNCHRONIZED_TSC + def_bool n + config GROUP_SCHED bool "Group CPU scheduler" depends on EXPERIMENTAL
Documentation/kernel-parameters.txt +4 -0
@@ -758,6 +758,10 @@ and is between 256 and 4096 characters. ftrace_dump_on_oops [ftrace] will dump the trace buffers on oops. + force_tsc_sync + Force TSC resynchronization when SMP CPUs go online. + See also idle=poll and disable frequency scaling. + gamecon.map[2|3]= [HW,JOY] Multisystem joystick and NES/SNES/PSX pad support via parallel port (up to 5 devices per port)
Signature · 2 lines
Mathieu Desnoyers OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F BA06 3F25 A8FE 3BAE 9A68
PATCH 13/17 x86 : remove arch-specific tsc_sync.c
In reply to Mathieu DesnoyersDepends on the new arch. independent kernel/time/tsc-sync.c
--- arch/x86/Kconfig | 1 arch/x86/include/asm/tsc.h | 8 + arch/x86/kernel/Makefile | 4 arch/x86/kernel/tsc_sync.c | 199 --------------------------------------------- 4 files changed, 10 insertions(+), 202 deletions(-)
arch/x86/kernel/Makefile +2 -2
@@ -63,9 +63,9 @@ obj-$(CONFIG_PCI) += early-quirks.o apm-y := apm_32.o obj-$(CONFIG_APM) += apm.o obj-$(CONFIG_X86_SMP) += smp.o -obj-$(CONFIG_X86_SMP) += smpboot.o tsc_sync.o ipi.o tlb_$(BITS).o +obj-$(CONFIG_X86_SMP) += smpboot.o ipi.o tlb_$(BITS).o obj-$(CONFIG_X86_32_SMP) += smpcommon.o -obj-$(CONFIG_X86_64_SMP) += tsc_sync.o smpcommon.o +obj-$(CONFIG_X86_64_SMP) += smpcommon.o obj-$(CONFIG_X86_TRAMPOLINE) += trampoline_$(BITS).o obj-$(CONFIG_X86_MPPARSE) += mpparse.o obj-$(CONFIG_X86_LOCAL_APIC) += apic.o nmi.o
arch/x86/kernel/tsc_sync.c deleted +0 -199
199 lines not on this page · raw message
arch/x86/Kconfig +1 -0
@@ -171,6 +171,7 @@ config GENERIC_PENDING_IRQ config X86_SMP bool depends on SMP && ((X86_32 && !X86_VOYAGER) || X86_64) + select HAVE_UNSYNCHRONIZED_TSC default y config USE_GENERIC_SMP_HELPERS
arch/x86/include/asm/tsc.h +7 -1
@@ -48,7 +48,7 @@ static __always_inline cycles_t vget_cycextern void tsc_init (void );extern void mark_tsc_unstable (char *reason);extern int unsynchronized_tsc (void ); -int check_tsc_unstable (void ); +extern int check_tsc_unstable (void );static inline cycles_t get_cycles_rate (void ) {
@@ -71,4 +71,10 @@ extern void check_tsc_sync_target(void);extern int notsc_setup (char *); +extern int test_tsc_synchronization (void ); +extern int _tsc_is_sync; +static inline int tsc_is_sync (void ) +{ +return _tsc_is_sync; +}#endif /* _ASM_X86_TSC_H */
Signature · 2 lines
Mathieu Desnoyers OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F BA06 3F25 A8FE 3BAE 9A68
PATCH 14/17 MIPS use tsc_sync.c
In reply to Mathieu Desnoyerstsc-sync.c is now available to test if TSC is synchronized across cores. Given I currently don't have access to a MIPS board myself, help trying to use it when CPUs go online and testing the implementation would be welcome.
--- arch/mips/include/asm/timex.h | 26 ++++++++++++++++++++++++++ arch/mips/kernel/smp.c | 1 + 2 files changed, 27 insertions(+)
arch/mips/kernel/smp.c +1 -0
@@ -178,6 +178,7 @@ void __init smp_cpus_done(unsigned int m { mp_ops->cpus_done ();synchronise_count_master (); +test_tsc_synchronization (); }/* called from main before smp_init() */
arch/mips/include/asm/timex.h +26 -0
@@ -58,13 +58,39 @@ static inline cycles_t get_cycles_rate(v {return mips_hpt_frequency; } + +extern int test_tsc_synchronization (void ); +extern int _tsc_is_sync; +static inline int tsc_is_sync (void ) +{ +return _tsc_is_sync; +}#else static inline cycles_t get_cycles (void ) {return 0 ; } +static inline int test_tsc_synchronization (void ) +{ +return 0 ; +} +static inline int tsc_is_sync (void ) +{ +return 0 ; +}#endif +#define DELAY_INTERRUPT 100 +/* +* Only updates 32 LSB. +*/ +static inline void write_tsc (u32 val1,u32 val2) +{ +write_c0_count (val1); +/* Arrange for an interrupt in a short while */ +write_c0_compare (read_c0_count () +DELAY_INTERRUPT ); +} +#endif /* __KERNEL__ */ #endif /* _ASM_TIMEX_H */
Signature · 2 lines
Mathieu Desnoyers OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F BA06 3F25 A8FE 3BAE 9A68
PATCH 15/17 MIPS create empty sync_core()
In reply to Mathieu DesnoyersNeeded by architecture-independant tsc-sync.c.
--- arch/mips/include/asm/barrier.h | 6 ++++++ 1 file changed, 6 insertions(+)
arch/mips/include/asm/barrier.h +6 -0
@@ -152,4 +152,10 @@#define smp_llsc_rmb () __asm__ __volatile__(__WEAK_LLSC_MB : : :"memory")#define smp_llsc_wmb () __asm__ __volatile__(__WEAK_LLSC_MB : : :"memory") +/* +* MIPS does not have any instruction to serialize instruction execution on the +* core. +*/ +#define sync_core () +#endif /* __ASM_BARRIER_H */
Signature · 2 lines
Mathieu Desnoyers OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F BA06 3F25 A8FE 3BAE 9A68
PATCH 16/17 MIPS : Trace clock
In reply to Mathieu DesnoyersMIPS get_cycles only returns a 32 bits TSC (see timex.h). The assumption there is that the reschedule is done every 8 seconds or so. Given that tracing needs to detect delays longer than 8 seconds, we need a full 64-bits TSC, which is provided by trace-clock-32-to-64.
I leave the "depends on !CPU_R4400_WORKAROUNDS" in Kconfig because the solution proposed by Ralf to deal with the R4400 bug is racy, so let's just not support this broken architecture. :(
This patch uses the same cache-line bouncing algorithm used for x86. This is a best-effort to support architectures lacking synchronized TSC without adding a lot of complexity too soon. This keeps room for improvement in a second phase.
--- arch/mips/Kconfig | 3 arch/mips/include/asm/timex.h | 17 +++ arch/mips/include/asm/trace-clock.h | 65 +++++++++++++ arch/mips/kernel/Makefile | 4 arch/mips/kernel/trace-clock.c | 172 ++++++++++++++++++++++++++++++++++++ 5 files changed, 260 insertions(+), 1 deletion(-)
arch/mips/Kconfig +3 -0
@@ -1614,6 +1614,9 @@ config CPU_R4400_WORKAROUNDS config HAVE_GET_CYCLES_32 def_bool y depends on !CPU_R4400_WORKAROUNDS + select HAVE_TRACE_CLOCK + select HAVE_TRACE_CLOCK_32_TO_64 + select HAVE_UNSYNCHRONIZED_TSC # # Use the generic interrupt handling code in kernel/irq/:
arch/mips/include/asm/trace-clock.h new file +65 -0
65 lines not on this page · raw message
arch/mips/kernel/trace-clock.c new file +172 -0
172 lines not on this page · raw message
arch/mips/include/asm/timex.h +16 -1
@@ -44,7 +44,7 @@ extern unsigned int mips_hpt_frequency;typedef unsigned int cycles_t ; -#ifdef HAVE_GET_CYCLES_32 +#ifdef CONFIG_HAVE_GET_CYCLES_32 static inline cycles_t get_cycles (void ) {return read_c0_count ();
@@ -91,6 +91,21 @@ static inline void write_tsc(u32 val1, uwrite_c0_compare (read_c0_count () +DELAY_INTERRUPT ); } +/* +* Currently unused, should update internal tsc-related timekeeping sources. +*/ +static inline void mark_tsc_unstable (char *reason) +{ +} + +/* +* Currently simply use the tsc_is_sync value. +*/ +static inline int unsynchronized_tsc (void ) +{ +return !tsc_is_sync (); +} +#endif /* __KERNEL__ */ #endif /* _ASM_TIMEX_H */
arch/mips/kernel/Makefile +4 -0
@@ -85,6 +85,10 @@ obj-$(CONFIG_GPIO_TXX9) += gpio_txx9.o obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o obj-$(CONFIG_EARLY_PRINTK) += early_printk.o +ifdef CONFIG_TRACING +obj-$(CONFIG_HAVE_TRACE_CLOCK) += trace-clock.o +endif + CFLAGS_cpu-bugs64.o = $(shell if $(CC) $(KBUILD_CFLAGS) -Wa,-mdaddi -c -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-DHAVE_AS_SET_DADDI"; fi) obj-$(CONFIG_HAVE_STD_PC_SERIAL_PORT) += 8250-platform.o
Signature · 2 lines
Mathieu Desnoyers OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F BA06 3F25 A8FE 3BAE 9A68
PATCH 17/17 x86 trace clock
In reply to Mathieu DesnoyersX86 trace clock. Depends on tsc_sync to detect if timestamp counters are synchronized on the machine.
I am leaving this poorly scalable solution for now as this is the simplest, yet working, solution I found (compared to using the HPET which also scales very poorly, probably due to bus contention). This should be a good start and let us trace a good amount of machines out there.
A "Big Fat" (TM) warning is shown on the console when the trace clock is used on systems without synchronized TSCs to tell the user to
- use force_tsc_sync=1
- use idle=poll
- disable Powernow or Speedstep
In order to get accurate and fast timestamps.
This keeps room for further improvement in a second phase.
Changelog:
- freq_scale is not used as a divisor rather than multiplier to support systems with frequency < 1HZ.
--- arch/x86/Kconfig | 1 arch/x86/include/asm/trace-clock.h | 76 +++++++++++ arch/x86/kernel/Makefile | 4 arch/x86/kernel/trace-clock.c | 248 +++++++++++++++++++++++++++++++++++++ 4 files changed, 329 insertions(+)
arch/x86/Kconfig +1 -0
@@ -27,6 +27,7 @@ config X86 select HAVE_KPROBES select ARCH_WANT_OPTIONAL_GPIOLIB select HAVE_KRETPROBES + select HAVE_TRACE_CLOCK select HAVE_FTRACE_MCOUNT_RECORD select HAVE_DYNAMIC_FTRACE select HAVE_FUNCTION_TRACER
arch/x86/kernel/Makefile +4 -0
@@ -20,6 +20,10 @@ ifdef CONFIG_FUNCTION_GRAPH_TRACER CFLAGS_REMOVE_process_32.o = -pg endif +ifdef CONFIG_TRACING +obj-$(CONFIG_HAVE_TRACE_CLOCK) += trace-clock.o +endif + # # vsyscalls (which work on the user stack) should have # no stack-protector checks:
arch/x86/kernel/trace-clock.c new file +248 -0
248 lines not on this page · raw message
arch/x86/include/asm/trace-clock.h new file +76 -0
76 lines not on this page · raw message
Signature · 2 lines
Mathieu Desnoyers OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F BA06 3F25 A8FE 3BAE 9A68
Re: [patch 17/17] x86 trace clock
Quoted 2 lines · Mathieu Desnoyers
X86 trace clock. Depends on
tsc_syncto detect if timestamp counters are synchronized on the machine.
For that tsc_sync needs to be fixed first? e.g. see thread about it firing incorrectly on vmware some time ago.
Quoted 3 lines
A "Big Fat" (TM) warning is shown on the console when the trace clock is used on systems without synchronized TSCs to tell the user to
How about Intel systems where the TSC only stops in C3 and deeper? You don't seem to handle that case well.
On modern Intel systems that's a common case.
arch/x86/kernel/trace-clock.c line 86
Quoted 8 lines
arch/x86/kernel/trace-clock.c new file +8
-0+ new_tsc = last_tsc +TRACE_CLOCK_MIN_PROBE_DURATION ; +/* +* If cmpxchg fails with a value higher than the new_tsc, don't +* retry : the value has been incremented and the events +* happened almost at the same time. +* We must retry if cmpxchg fails with a lower value : +* it means that we are the CPU with highest frequency and +* therefore MUST update the value.
Sorry but any global TSC measurements without scaling when the TSCs run on different frequencies just don't make sense. The results will be always poor. You really have to scale appropimately then and also handle the "instable period"
-Andi
Signature · 1 line
[email protected]
Re: [patch 17/17] x86 trace clock
Quoted 8 lines · * Andi Kleen ([email protected]) wrote:
Quoted 2 lines · Mathieu Desnoyers
X86 trace clock. Depends on
tsc_syncto detect if timestamp counters are synchronized on the machine.For that
tsc_syncneeds to be fixed first? e.g. see thread about it firing incorrectly on vmware some time ago.
I assume you are talking about this thread:
http://lkml.org/lkml/2008/10/20/467
Thanks for pointing it out to me.
Yes, the x86 trace clock depends on the tsc_sync detection code. Note that the patchset I propose here includes a new tsc_sync detection code which behaves differently from the current x86 tsc_sync code. We can specify an acceptable cycle delta between the CPUs and also, given I retry 10 times for each CPU and take the _best_ result of the test, it should make sure it does not suffer from NMI, MCE, SMI nor hypervisor delays.
Quoted 7 lines
Quoted 3 lines
A "Big Fat" (TM) warning is shown on the console when the trace clock is used on systems without synchronized TSCs to tell the user to
How about Intel systems where the TSC only stops in C3 and deeper? You don't seem to handle that case well.
The thing is : if the users specify the idle=poll kernel command line option, the cycle counters will stay synchronized even if it would stop in C3 and deeper halt states. This code assumes that the idle thread has already executed on at least one of the CPUs before the synchronization code is called. Maybe it would be safer to explicitly call the idle loop on one CPU to insure we would detect delta caused by it ?
But hrm, if we have a virtualized environment, with one guest using idle=poll and then a second guest using the halt states starting later, I wonder what the result would be ?
Also, all this stuff about vmware giving TSCs "synchronized enough" for timekeeping does not work work tracing. NTP precision, if I recall well, was too loose for tracing needs.
Quoted 14 lines
On modern Intel systems that's a common case.
arch/x86/kernel/trace-clock.c line 86
Quoted 8 lines
arch/x86/kernel/trace-clock.c new file +8
-0+ new_tsc = last_tsc +TRACE_CLOCK_MIN_PROBE_DURATION ; +/* +* If cmpxchg fails with a value higher than the new_tsc, don't +* retry : the value has been incremented and the events +* happened almost at the same time. +* We must retry if cmpxchg fails with a lower value : +* it means that we are the CPU with highest frequency and +* therefore MUST update the value. Sorry but any global TSC measurements without scaling when the TSCs run on different frequencies just don't make sense. The results will be always poor. You really have to scale appropimately then and also handle the "instable period"
This scheme only offers the following guarantees :
- the max time imprecision is 1 jiffy.
- the cycles counter values read will increase monotonically across the system.
And also, yes, if all CPUs happen to go slower than their original speed, the cpu_khz value used will be wrong and time will appear to go slower than it does.
Given that, we could do "better" by also keep per cpu offset/frequency and scale each frequency read using these reference values, so the TSCs are almost in sync, but this would not deal correctly with the C3 and lower halt states anyway.. so ther might be limited interest in this.
And yes, we would eventually have to record the frequency change events in a specific tracing buffer, which would let us reconstruct the CPU speed along the execution, but I think starting with such less precise but still accurate (in term of event order) tracing data would be a simpler start. We can always add this frequency change monitoring layer in a second phase.
So, given that, what would you think would be "good enough" for a first shot as a tracing clock, given we keep room for later improvement ?
Considering your comments here, we may want to add some knowledge about the current CPU frequency in the trace clock tsc read primitive so we go at a rate which follows the real speed more closely, even though this would not take care of the C3 and - halt states. And also given that, we might want to resynchronize the TSC values based on HPET reads periodically (e.g. every time we go out of idle, but at most once per jiffy) to deal with halt states. We could wrap that into the current cmpxchg-based method I proposed here to deal with the "instable period".
Hm ?
Mathieu
Quoted 5 lines
-Andi
Signature · 1 line
[email protected]
Signature · 2 lines
Mathieu Desnoyers OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F BA06 3F25 A8FE 3BAE 9A68