Trace Clock v3
Subsystems (8)
cover Trace Clock v3
Hi,
In this new version, I have integrated the changes I did following the comments I received for v2. I also reimplemented the "generic" trace clock so it is less intrusive and simply use a standard timer. I is not as bound to the xtime_lock as it previously was and it does not have to modify kernel/time.c anymore.
I think this is pretty close to a mergeable state. I plan to keep more exotic features, such as dealing with non-synchronized TSCs without cache-line bouncing, for future improvement. As a reminder, when the trace clock detects an unsynchronized TSC on the machine, it prints the information about which kernel command line arguments must be used so the user can have synchronized timestamp counters. However, if the user wants to use his system with non-synchronized TSCs, the cache-line bouncing fallback is used.
This patchset applies on top of 2.6.28-rc4.
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
@@ -56,6 +56,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 tsc_khz; +} + +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
Re: [patch 02/17] get_cycles() : x86 HAVE_GET_CYCLES
arch/x86/include/asm/tsc.h extern void mark_tsc_unstable(char *reas · line 63
Quoted 11 lines · Mathieu Desnoyers
arch/x86/include/asm/tsc.h +5
-0@@ -56,6 +56,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 tsc_khz;
^^^
The comment in Kconfig says:
| get_cycles_rate() : cycle counter rate, in HZ
^^
So what should it be? Hz or kHz?
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- [email protected]
In personal conversations with technical people, I call myself a hacker. But when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
Re: [patch 02/17] get_cycles() : x86 HAVE_GET_CYCLES
Quoted 14 lines · * Geert Uytterhoeven ([email protected]) wrote:
arch/x86/include/asm/tsc.h extern void mark_tsc_unstable(char *reas · line 63
Quoted 11 lines · Mathieu Desnoyers
arch/x86/include/asm/tsc.h +5
-0@@ -56,6 +56,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 tsc_khz;^^^
Quoted 7 lines
The comment in
Kconfigsays:|
get_cycles_rate(): cycle counter rate, in HZ^^So what should it be? Hz or kHz?
HZ, for consistency.
So it becomes :
+static inline cycles_t get_cycles_rate(void)
+{
+ if (check_tsc_unstable())
+ return 0;
+ return (cycles_t)tsc_khz * 1000;
+}
+
Thanks for spotting this, will be integrated in the next release.
As a side-note, I noticed that I used CLOCK_TICK_RATE on MIPS when I should use mips_hpt_frequency. This too will be fixed in the next release.
Mathieu
Quoted 10 lines
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- [email protected]In personal conversations with technical people, I call myself a hacker. But when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
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 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 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).
--- arch/mips/Kconfig | 4 ++++ arch/mips/include/asm/timex.h | 25 +++++++++++++++++++++++++ 2 files changed, 29 insertions(+)
arch/mips/include/asm/timex.h +25 -0
@@ -29,14 +29,39 @@* 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 CLOCK_TICK_RATE ; +} +#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 6/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/Makefile | 3 kernel/trace/Makefile | 1 kernel/trace/trace-clock-32-to-64.c | 281 ++++++++++++++++++++++++++++++++++++ 4 files changed, 295 insertions(+), 2 deletions(-)
kernel/trace/Makefile +1 -0
@@ -24,5 +24,6 @@ obj-$(CONFIG_NOP_TRACER) += trace_nop.o obj-$(CONFIG_STACK_TRACER) += trace_stack.o obj-$(CONFIG_MMIOTRACE) += trace_mmiotrace.o obj-$(CONFIG_BOOT_TRACER) += trace_boot.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
kernel/Makefile +1 -2
@@ -88,8 +88,7 @@ obj-$(CONFIG_MARKERS) += marker.o obj-$(CONFIG_TRACEPOINTS) += tracepoint.o obj-$(CONFIG_LATENCYTOP) += latencytop.o obj-$(CONFIG_HAVE_GENERIC_DMA_COHERENT) += dma-coherent.o -obj-$(CONFIG_FUNCTION_TRACER) += trace/ -obj-$(CONFIG_TRACING) += trace/ +obj-y += trace/ obj-$(CONFIG_SMP) += sched_cpupri.o ifneq ($(CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER),y)
Signature · 2 lines
Mathieu Desnoyers OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F BA06 3F25 A8FE 3BAE 9A68
PATCH 7/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
@@ -25,5 +25,6 @@ obj-$(CONFIG_STACK_TRACER) += trace_stac obj-$(CONFIG_MMIOTRACE) += trace_mmiotrace.o obj-$(CONFIG_BOOT_TRACER) += trace_boot.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 8/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 9/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 10/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-07 00:34:10.000000000 -0500
+++ linux.trees.git/arch/sh/include/asm/timex.h 2008-11-12 18:01:56.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-07 00:34:10.000000000 -0500
+++ linux.trees.git/arch/sh/Kconfig 2008-11-12 18:01:56.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-12 18:01:56.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 unsigned int trace_clock_frequency(void)
+{
+ unsigned long rate;
+ struct clk *tmu1_clk;
+
+ tmu1_clk = clk_get(NULL, "tmu1_clk");
+ rate = clk_get_rate(tmu1_clk);
+
+ return (unsigned int)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 11/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
@@ -765,6 +765,10 @@ and is between 256 and 4096 characters. parameter will force ia64_sal_cache_flush to call ia64_pal_cache_flush instead of SAL_CACHE_FLUSH. + 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 12/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 | 2 arch/x86/include/asm/tsc.h | 8 + arch/x86/kernel/Makefile | 4 arch/x86/kernel/tsc_sync.c | 189 --------------------------------------------- 4 files changed, 11 insertions(+), 192 deletions(-)
arch/x86/kernel/Makefile +2 -2
@@ -56,9 +56,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 -189
189 lines not on this page · raw message
arch/x86/Kconfig +2 -0
@@ -169,6 +169,7 @@ config X86_SMP bool depends on SMP && ((X86_32 && !X86_VOYAGER) || X86_64) select USE_GENERIC_SMP_HELPERS + select HAVE_UNSYNCHRONIZED_TSC default y config X86_32_SMP
@@ -178,6 +179,7 @@ config X86_32_SMP config X86_64_SMP def_bool y depends on X86_64 && SMP + select HAVE_UNSYNCHRONIZED_TSC config X86_HT bool
arch/x86/include/asm/tsc.h +7 -1
@@ -54,7 +54,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 ) {
@@ -77,4 +77,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 13/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
@@ -56,13 +56,39 @@ static inline cycles_t get_cycles_rate(v {return CLOCK_TICK_RATE ; } + +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 14/17 MIPS : export hpt frequency for trace_clock.
In reply to Mathieu DesnoyersTrace_clock 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
@@ -89,6 +89,8 @@ static inline void write_tsc(u32 val1, uwrite_c0_compare (read_c0_count () +DELAY_INTERRUPT ); } +extern unsigned int mips_hpt_frequency; +#endif /* __KERNEL__ */ #endif /* _ASM_TIMEX_H */
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 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 | 2 arch/mips/kernel/trace-clock.c | 172 ++++++++++++++++++++++++++++++++++++ 5 files changed, 258 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
@@ -42,7 +42,7 @@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, uextern unsigned int mips_hpt_frequency; +/* +* 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 +2 -0
@@ -85,6 +85,8 @@ obj-$(CONFIG_GPIO_TXX9) += gpio_txx9.o obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o obj-$(CONFIG_EARLY_PRINTK) += early_printk.o +obj-$(CONFIG_HAVE_TRACE_CLOCK) += trace-clock.o + 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.
--- arch/x86/Kconfig | 1 arch/x86/kernel/Makefile | 1 arch/x86/kernel/trace-clock.c | 248 ++++++++++++++++++++++++++++++++++++++++++ include/asm-x86/trace-clock.h | 70 +++++++++++ 4 files changed, 320 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 +1 -0
@@ -36,6 +36,7 @@ obj-y += bootflag.o e820.o obj-y += pci-dma.o quirks.o i8237.o topology.o kdebugfs.o obj-y += alternative.o i8253.o pci-nommu.o obj-y += tsc.o io_delay.o rtc.o +obj-$(CONFIG_HAVE_TRACE_CLOCK) += trace-clock.o obj-$(CONFIG_X86_TRAMPOLINE) += trampoline.o obj-y += process.o
arch/x86/kernel/trace-clock.c new file +248 -0
248 lines not on this page · raw message
include/asm-x86/trace-clock.h new file +70 -0
70 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