Re: LTTng on ARM - timestamps
Mathieu Desnoyers <[email protected]> Thu, 26 Jul 2007 19:06:35 -0400
| Newsgroups | gmane.linux.kernel.tracing |
|---|---|
| Message-ID | <20070726230635.GB22408@Krystal> |
* Mathieu Desnoyers ([email protected]) wrote: > * Richard Purdie ([email protected]) wrote: > > On Thu, 2007-07-26 at 14:15 -0400, Mathieu Desnoyers wrote: > > > * Richard Purdie ([email protected]) wrote: > > > > It turns out that there is instrumentation in do_timer() in > > > > kernel/timer.c which requires the xtime_lock write seqlock to be held. > > > > The default ARM timer implementation uses jiffies which is read using > > > > the same xtime_lock() and hence always fails for this event (and > > > > possibly others if there is more instrumentation in xtime_lock paths). > > > > > > Yes, using jiffies as a time source is only a best effort. But I think > > > we can do it better. > > > > > > do_timer should be the only code that traces over the xtime write > > > seqlock, except NMIs and, potentially, the lockdep instrumentation > > > (Locking facility), which you don't have on ARM AFAIK. > > > > > > > Basically this means LTTng is broken on ARM (and anything else using > > > > jiffies based timer implementations for their timestamps). > > > > > > > AFAIK, ARM is the only architecture where I use this. Other > > > architectures provide some sort of TSC or external timer to read from. > > > > PPC seems to use jiffies as a fallback for some processors. MIPS used to > > but doesn't now looking at the code. > > > > > > The best solution I can see is to add a finer grained seqlock only held > > > > with jiffies is changed and to use this in the timestamps for systems > > > > without a TSC. Is this problem known about and is there any alternative? > > > > > > Yes, I have always considered that taking a lock, even a read seqlock, > > > in the tracing code should be made illegal ;) I have an alternative idea > > > that would fix the problem: > > > > > > - Use only the 32 LSB of the jiffies. No need to take a read seqlock. > > > > Then the clock isn't of high enough resolution? When we discussed this > > originally, the problem was that we needed an effectively unique counter > > if I remember correctly and jiffies isn't of high enough resolution? > > > > > - At 1000HZ, the maximum trace duration would be: > > > 2^32 cycles / 1000 cycles/s = 4294967.296s = 49.71 days (a little bit > > > too low) > > > > > > Which is not bad, but could be much better. Actually, I keep 13 bits > > > (max of 8192 events per timer tick) for the "logical clock" and use the > > > other 51 bits for the jiffies. (max trace duration of 71355.23 years) > > > However, it requires to have a coherent 64 bits jiffies counter trucated > > > to 51 bits. > > > > The problem is combining the logical clock atomically with jiffies. That > > is why we have the seqlock, even in the 32 bit case and is where the > > problem arises. > > > > Oh, right I forgot about this locking issue. We could get away with no > locking by changing it slightly: > > Instead of using the jiffies counter _at all_, we could keep our own 32 > bits counter, updated atomically by the timer interrupt and the > timestamp readers like this: > > #define LTT_CLOCK_SHIFT 13 > atomic_t ltt_clock; > > * timer interrupt: > > int old_ltt_clock; > > do { > old_ltt_clock = atomic_read(<t_clock); > } while(atomic_cmpxchg(<t_clock, old_ltt_clock, > (old_ltt_clock + (1<<LTT_CLOCK_SHIFT)) & ((1<<LTT_CLOCK_SHIFT)-1)) Sorry, I mean: (old_ltt_clock + (1<<LTT_CLOCK_SHIFT)) & (~((1<<LTT_CLOCK_SHIFT)-1))) (missing ~) > != old_ltt_clock); > > timestamp read (32 bits) > > return atomic_inc_return(<t_clock); > > so we have 32-13 = 19 bits before the ltt jiffies overflows. Upon > overflow, notice that we mask out the 13 LSBs, so the overflow won't > bother us, it will simply reset to 0. > > Duration between overflows: > 2^19 / 1000 tick/s = 524.288s = 8.73 minutes. > > Therefore, we _really_ have to use the synthetic clock to extend this 32 > bit clock to a 64 bit one. > > About the test if (tsc == 0), note that the code goes like this > (intentionally): > > > tsc = ltt_get_timestamp64(); > if (tsc == 0) { > /* > * Error in getting the timestamp : should not happen : > * it would mean we are called from an NMI during a > * write seqlock on xtime. > */ > return; > } > > The test is done in the full 64 bits counter, _never_ the 32 bits one, > so we don't have to worry about periodic 32 bits overflows. > > What do you think about this ? > > Mathieu > > > > In order to do that correctly, using the ltt synthetic TSC which extends > > > a 32 bits counter into a 64 bits counter should let you do that without > > > locks. See ltt/ltt-heartbeat.c. The only "different" thing would be that > > > you would use the 32 jiffies LSB instead of get_cycles() to feed the 32 LSBs > > > to ltt-heartbeat.c. See asm-mips/ltt.h for an example of how it is used. > > > > On ARM, some processors do have things that can be used as a 32 bit TSC > > and I've used that code to turn them into 64 bit registers. See above > > for the problem of combining the logical clock with jiffies even in the > > 32 bit case. > > > > > About the "zero" test, 64 bits (even 51 bits) counters should suffice to > > > insure that it will never wrap to 0 in the computer's life. I have seen > > > that kind of test done elsewhere in the core kernel code also. > > > > Ok, I'm still not sure I like it but that is a convincing argument for > > 64 bit assuming the counter starts from zero. We're also doing this for > > 32 bit counters though - every 50 days as you mention above... > > > > Anyhow, I tried a quick experiment of introducing my own seqlock > > specifically to solve the ARM jiffies problem. I can confirm the patch > > below does mean no events get lost. It needs a bit of cleanup for > > non-arm but illustrates one possible way this could be solved. The > > seqlock itself should be very low impact since we can use the _begin and > > _end functions in this context thanks to the surrounding xtime_lock. > > > > > > Richard > > > > > > Index: linux/arch/arm/kernel/time.c > > =================================================================== > > --- linux.orig/arch/arm/kernel/time.c 2007-07-26 18:22:39.000000000 +0100 > > +++ linux/arch/arm/kernel/time.c 2007-07-26 18:24:58.000000000 +0100 > > @@ -34,7 +34,6 @@ > > #include <asm/leds.h> > > #include <asm/thread_info.h> > > #include <asm/mach/time.h> > > -#include <asm/ltt.h> > > > > /* > > * Our system timer. > > @@ -337,9 +336,6 @@ void timer_tick(void) > > do_leds(); > > do_set_rtc(); > > do_timer(1); > > -#ifdef CONFIG_LTT > > - ltt_reset_timestamp(); > > -#endif > > #ifndef CONFIG_SMP > > update_process_times(user_mode(get_irq_regs())); > > #endif > > Index: linux/include/asm-arm/ltt.h > > =================================================================== > > --- linux.orig/include/asm-arm/ltt.h 2007-07-26 17:58:43.000000000 +0100 > > +++ linux/include/asm-arm/ltt.h 2007-07-26 20:15:04.000000000 +0100 > > @@ -58,10 +58,10 @@ static inline u32 ltt_get_timestamp32(vo > > u32 ret; > > > > do { > > - seq = read_seqbegin(&xtime_lock); > > + seq = read_seqbegin(<t_jiffy_lock); > > ret = (jiffies << LTTNG_LOGICAL_SHIFT) > > | (atomic_add_return(1, <tng_logical_clock)); > > - } while (read_seqretry(&xtime_lock, seq) && (--try) > 0); > > + } while (read_seqretry(<t_jiffy_lock, seq) && (--try) > 0); > > > > if (try == 0) { > > printk(KERN_ERR "Exhausted tries 32 %d\n", ret); > > @@ -80,10 +79,10 @@ static inline u64 ltt_get_timestamp64(vo > > u64 ret; > > > > do { > > - seq = read_seqbegin(&xtime_lock); > > + seq = read_seqbegin(<t_jiffy_lock); > > ret = (jiffies_64 << LTTNG_LOGICAL_SHIFT) > > | (atomic_add_return(1, <tng_logical_clock)); > > - } while (read_seqretry(&xtime_lock, seq) && (--try) > 0); > > + } while (read_seqretry(<t_jiffy_lock, seq) && (--try) > 0); > > > > if (try == 0) { > > printk(KERN_ERR "Exhausted tries 64 %llu\n", ret); > > Index: linux/include/linux/time.h > > =================================================================== > > --- linux.orig/include/linux/time.h 2007-07-26 18:01:24.000000000 +0100 > > +++ linux/include/linux/time.h 2007-07-26 18:01:54.000000000 +0100 > > @@ -91,6 +91,7 @@ static inline struct timespec timespec_s > > extern struct timespec xtime; > > extern struct timespec wall_to_monotonic; > > extern seqlock_t xtime_lock __attribute__((weak)); > > +extern seqlock_t ltt_jiffy_lock __attribute__((weak)); > > > > extern unsigned long read_persistent_clock(void); > > void timekeeping_init(void); > > Index: linux/kernel/timer.c > > =================================================================== > > --- linux.orig/kernel/timer.c 2007-07-26 17:52:25.000000000 +0100 > > +++ linux/kernel/timer.c 2007-07-26 18:24:50.000000000 +0100 > > @@ -43,6 +43,7 @@ > > #include <asm/timex.h> > > #include <asm/io.h> > > #include <asm/irq_regs.h> > > +#include <asm/ltt.h> > > > > u64 jiffies_64 __cacheline_aligned_in_smp = INITIAL_JIFFIES; > > > > @@ -1345,7 +1346,10 @@ static inline void update_times(unsigned > > update_wall_time(); > > calc_load(ticks); > > } > > - > > + > > +__attribute__((weak)) __cacheline_aligned_in_smp DEFINE_SEQLOCK(ltt_jiffy_lock); > > +EXPORT_SYMBOL(ltt_jiffy_lock); > > + > > /* > > * The 64-bit jiffies value is not atomic - you MUST NOT read it > > * without sampling the sequence number in xtime_lock. > > @@ -1354,7 +1358,12 @@ static inline void update_times(unsigned > > > > void do_timer(unsigned long ticks) > > { > > + write_seqcount_begin(<t_jiffy_lock); > > jiffies_64 += ticks; > > +#ifdef CONFIG_LTT > > + ltt_reset_timestamp(); > > +#endif > > + write_seqcount_end(<t_jiffy_lock); > > update_times(ticks); > > MARK(kernel_timer_update_time, > > "%8b %*.*r %*.*r", > > > > -- > Mathieu Desnoyers > Computer Engineering Ph.D. Student, Ecole Polytechnique de Montreal > OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F BA06 3F25 A8FE 3BAE 9A68 -- Mathieu Desnoyers Computer Engineering Ph.D. Student, Ecole Polytechnique de Montreal OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F BA06 3F25 A8FE 3BAE 9A68