| From 9373c724d0d890b4015f4fe1146e251222c0b951 Mon Sep 17 00:00:00 2001 |
| From: "Steven Rostedt (VMware)" <rostedt@goodmis.org> |
| Date: Fri, 22 Sep 2017 16:59:02 -0400 |
| Subject: [PATCH 1750/1795] ring-buffer: Rewrite trace_recursive_(un)lock() to |
| be simpler |
| |
| The current method to prevent the ring buffer from entering into a recursize |
| loop is to use a bitmask and set the bit that maps to the current context |
| (normal, softirq, irq or NMI), and if that bit was already set, it is |
| considered a recursive loop. |
| |
| New code is being added that may require the ring buffer to be entered a |
| second time in the current context. The recursive locking prevents that from |
| happening. Instead of mapping a bitmask to the current context, just allow 4 |
| levels of nesting in the ring buffer. This matches the 4 context levels that |
| it can already nest. It is highly unlikely to have more than two levels, |
| thus it should be fine when we add the second entry into the ring buffer. If |
| that proves to be a problem, we can always up the number to 8. |
| |
| An added benefit is that reading preempt_count() to get the current level |
| adds a very slight but noticeable overhead. This removes that need. |
| |
| Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org> |
| (cherry picked from commit 1a149d7d3f45d311da1f63473736c05f30ae8a75) |
| Signed-off-by: Hirotaka MOTAI <Motai.Hirotaka@aj.MitsubishiElectric.co.jp> |
| Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be> |
| --- |
| kernel/trace/ring_buffer.c | 64 ++++++++++---------------------------- |
| 1 file changed, 17 insertions(+), 47 deletions(-) |
| |
| diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c |
| index fd7809004297..c3b6b47fd44a 100644 |
| --- a/kernel/trace/ring_buffer.c |
| +++ b/kernel/trace/ring_buffer.c |
| @@ -2545,61 +2545,29 @@ rb_wakeups(struct ring_buffer *buffer, struct ring_buffer_per_cpu *cpu_buffer) |
| * The lock and unlock are done within a preempt disable section. |
| * The current_context per_cpu variable can only be modified |
| * by the current task between lock and unlock. But it can |
| - * be modified more than once via an interrupt. To pass this |
| - * information from the lock to the unlock without having to |
| - * access the 'in_interrupt()' functions again (which do show |
| - * a bit of overhead in something as critical as function tracing, |
| - * we use a bitmask trick. |
| + * be modified more than once via an interrupt. There are four |
| + * different contexts that we need to consider. |
| * |
| - * bit 0 = NMI context |
| - * bit 1 = IRQ context |
| - * bit 2 = SoftIRQ context |
| - * bit 3 = normal context. |
| + * Normal context. |
| + * SoftIRQ context |
| + * IRQ context |
| + * NMI context |
| * |
| - * This works because this is the order of contexts that can |
| - * preempt other contexts. A SoftIRQ never preempts an IRQ |
| - * context. |
| - * |
| - * When the context is determined, the corresponding bit is |
| - * checked and set (if it was set, then a recursion of that context |
| - * happened). |
| - * |
| - * On unlock, we need to clear this bit. To do so, just subtract |
| - * 1 from the current_context and AND it to itself. |
| - * |
| - * (binary) |
| - * 101 - 1 = 100 |
| - * 101 & 100 = 100 (clearing bit zero) |
| - * |
| - * 1010 - 1 = 1001 |
| - * 1010 & 1001 = 1000 (clearing bit 1) |
| - * |
| - * The least significant bit can be cleared this way, and it |
| - * just so happens that it is the same bit corresponding to |
| - * the current context. |
| + * If for some reason the ring buffer starts to recurse, we |
| + * only allow that to happen at most 4 times (one for each |
| + * context). If it happens 5 times, then we consider this a |
| + * recusive loop and do not let it go further. |
| */ |
| |
| static __always_inline int |
| trace_recursive_lock(struct ring_buffer_per_cpu *cpu_buffer) |
| { |
| - unsigned int val = cpu_buffer->current_context; |
| - int bit; |
| - |
| - if (in_interrupt()) { |
| - if (in_nmi()) |
| - bit = RB_CTX_NMI; |
| - else if (in_irq()) |
| - bit = RB_CTX_IRQ; |
| - else |
| - bit = RB_CTX_SOFTIRQ; |
| - } else |
| - bit = RB_CTX_NORMAL; |
| - |
| - if (unlikely(val & (1 << bit))) |
| + if (cpu_buffer->current_context >= 4) |
| return 1; |
| |
| - val |= (1 << bit); |
| - cpu_buffer->current_context = val; |
| + cpu_buffer->current_context++; |
| + /* Interrupts must see this update */ |
| + barrier(); |
| |
| return 0; |
| } |
| @@ -2607,7 +2575,9 @@ trace_recursive_lock(struct ring_buffer_per_cpu *cpu_buffer) |
| static __always_inline void |
| trace_recursive_unlock(struct ring_buffer_per_cpu *cpu_buffer) |
| { |
| - cpu_buffer->current_context &= cpu_buffer->current_context - 1; |
| + /* Don't let the dec leak out */ |
| + barrier(); |
| + cpu_buffer->current_context--; |
| } |
| |
| /** |
| -- |
| 2.19.0 |
| |