Skip to content

Commit 567cd4d

Browse files
Steven Rostedtrostedt
authored andcommitted
ring-buffer: User context bit recursion checking
Using context bit recursion checking, we can help increase the performance of the ring buffer. Before this patch: # echo function > /debug/tracing/current_tracer # for i in `seq 10`; do ./hackbench 50; done Time: 10.285 Time: 10.407 Time: 10.243 Time: 10.372 Time: 10.380 Time: 10.198 Time: 10.272 Time: 10.354 Time: 10.248 Time: 10.253 (average: 10.3012) Now we have: # echo function > /debug/tracing/current_tracer # for i in `seq 10`; do ./hackbench 50; done Time: 9.712 Time: 9.824 Time: 9.861 Time: 9.827 Time: 9.962 Time: 9.905 Time: 9.886 Time: 10.088 Time: 9.861 Time: 9.834 (average: 9.876) a 4% savings! Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
1 parent 897f68a commit 567cd4d

File tree

2 files changed

+67
-31
lines changed

2 files changed

+67
-31
lines changed

kernel/trace/ring_buffer.c

Lines changed: 60 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2432,41 +2432,76 @@ rb_reserve_next_event(struct ring_buffer *buffer,
24322432

24332433
#ifdef CONFIG_TRACING
24342434

2435-
#define TRACE_RECURSIVE_DEPTH 16
2435+
/*
2436+
* The lock and unlock are done within a preempt disable section.
2437+
* The current_context per_cpu variable can only be modified
2438+
* by the current task between lock and unlock. But it can
2439+
* be modified more than once via an interrupt. To pass this
2440+
* information from the lock to the unlock without having to
2441+
* access the 'in_interrupt()' functions again (which do show
2442+
* a bit of overhead in something as critical as function tracing,
2443+
* we use a bitmask trick.
2444+
*
2445+
* bit 0 = NMI context
2446+
* bit 1 = IRQ context
2447+
* bit 2 = SoftIRQ context
2448+
* bit 3 = normal context.
2449+
*
2450+
* This works because this is the order of contexts that can
2451+
* preempt other contexts. A SoftIRQ never preempts an IRQ
2452+
* context.
2453+
*
2454+
* When the context is determined, the corresponding bit is
2455+
* checked and set (if it was set, then a recursion of that context
2456+
* happened).
2457+
*
2458+
* On unlock, we need to clear this bit. To do so, just subtract
2459+
* 1 from the current_context and AND it to itself.
2460+
*
2461+
* (binary)
2462+
* 101 - 1 = 100
2463+
* 101 & 100 = 100 (clearing bit zero)
2464+
*
2465+
* 1010 - 1 = 1001
2466+
* 1010 & 1001 = 1000 (clearing bit 1)
2467+
*
2468+
* The least significant bit can be cleared this way, and it
2469+
* just so happens that it is the same bit corresponding to
2470+
* the current context.
2471+
*/
2472+
static DEFINE_PER_CPU(unsigned int, current_context);
24362473

2437-
/* Keep this code out of the fast path cache */
2438-
static noinline void trace_recursive_fail(void)
2474+
static __always_inline int trace_recursive_lock(void)
24392475
{
2440-
/* Disable all tracing before we do anything else */
2441-
tracing_off_permanent();
2442-
2443-
printk_once(KERN_WARNING "Tracing recursion: depth[%ld]:"
2444-
"HC[%lu]:SC[%lu]:NMI[%lu]\n",
2445-
trace_recursion_buffer(),
2446-
hardirq_count() >> HARDIRQ_SHIFT,
2447-
softirq_count() >> SOFTIRQ_SHIFT,
2448-
in_nmi());
2476+
unsigned int val = this_cpu_read(current_context);
2477+
int bit;
24492478

2450-
WARN_ON_ONCE(1);
2451-
}
2452-
2453-
static inline int trace_recursive_lock(void)
2454-
{
2455-
trace_recursion_inc();
2479+
if (in_interrupt()) {
2480+
if (in_nmi())
2481+
bit = 0;
2482+
else if (in_irq())
2483+
bit = 1;
2484+
else
2485+
bit = 2;
2486+
} else
2487+
bit = 3;
24562488

2457-
if (likely(trace_recursion_buffer() < TRACE_RECURSIVE_DEPTH))
2458-
return 0;
2489+
if (unlikely(val & (1 << bit)))
2490+
return 1;
24592491

2460-
trace_recursive_fail();
2492+
val |= (1 << bit);
2493+
this_cpu_write(current_context, val);
24612494

2462-
return -1;
2495+
return 0;
24632496
}
24642497

2465-
static inline void trace_recursive_unlock(void)
2498+
static __always_inline void trace_recursive_unlock(void)
24662499
{
2467-
WARN_ON_ONCE(!trace_recursion_buffer());
2500+
unsigned int val = this_cpu_read(current_context);
24682501

2469-
trace_recursion_dec();
2502+
val--;
2503+
val &= this_cpu_read(current_context);
2504+
this_cpu_write(current_context, val);
24702505
}
24712506

24722507
#else

kernel/trace/trace.h

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -291,11 +291,6 @@ struct tracer {
291291

292292

293293
/* Only current can touch trace_recursion */
294-
#define trace_recursion_inc() do { (current)->trace_recursion++; } while (0)
295-
#define trace_recursion_dec() do { (current)->trace_recursion--; } while (0)
296-
297-
/* Ring buffer has the 10 LSB bits to count */
298-
#define trace_recursion_buffer() ((current)->trace_recursion & 0x3ff)
299294

300295
/*
301296
* For function tracing recursion:
@@ -323,7 +318,13 @@ struct tracer {
323318
* caller, and we can skip the current check.
324319
*/
325320
enum {
326-
TRACE_FTRACE_BIT = 11,
321+
TRACE_BUFFER_BIT,
322+
TRACE_BUFFER_NMI_BIT,
323+
TRACE_BUFFER_IRQ_BIT,
324+
TRACE_BUFFER_SIRQ_BIT,
325+
326+
/* Start of function recursion bits */
327+
TRACE_FTRACE_BIT,
327328
TRACE_FTRACE_NMI_BIT,
328329
TRACE_FTRACE_IRQ_BIT,
329330
TRACE_FTRACE_SIRQ_BIT,

0 commit comments

Comments
 (0)