Re: [PATCH v9 5/7] tracing: Centralize preemptirq tracepoints and unify their usage

From: Steven Rostedt
Date: Fri Jul 06 2018 - 18:06:21 EST



Peter,

Want to ack this? It touches Lockdep.

Joel,

I got to this patch and I'm still reviewing it. I'll hopefully have my
full review done by next week. I'll make it a priority. But I still
would like Peter's ack on this one, as he's the maintainer of lockdep.

Thanks,

-- Steve


On Thu, 28 Jun 2018 11:21:47 -0700
Joel Fernandes <joel@xxxxxxxxxxxxxxxxx> wrote:

> From: "Joel Fernandes (Google)" <joel@xxxxxxxxxxxxxxxxx>
>
> This patch detaches the preemptirq tracepoints from the tracers and
> keeps it separate.
>
> Advantages:
> * Lockdep and irqsoff event can now run in parallel since they no longer
> have their own calls.
>
> * This unifies the usecase of adding hooks to an irqsoff and irqson
> event, and a preemptoff and preempton event.
> 3 users of the events exist:
> - Lockdep
> - irqsoff and preemptoff tracers
> - irqs and preempt trace events
>
> The unification cleans up several ifdefs and makes the code in preempt
> tracer and irqsoff tracers simpler. It gets rid of all the horrific
> ifdeferry around PROVE_LOCKING and makes configuration of the different
> users of the tracepoints more easy and understandable. It also gets rid
> of the time_* function calls from the lockdep hooks used to call into
> the preemptirq tracer which is not needed anymore. The negative delta in
> lines of code in this patch is quite large too.
>
> In the patch we introduce a new CONFIG option PREEMPTIRQ_TRACEPOINTS
> as a single point for registering probes onto the tracepoints. With
> this,
> the web of config options for preempt/irq toggle tracepoints and its
> users becomes:
>
> PREEMPT_TRACER PREEMPTIRQ_EVENTS IRQSOFF_TRACER PROVE_LOCKING
> | | \ | |
> \ (selects) / \ \ (selects) /
> TRACE_PREEMPT_TOGGLE ----> TRACE_IRQFLAGS
> \ /
> \ (depends on) /
> PREEMPTIRQ_TRACEPOINTS
>
> One note, I have to check for lockdep recursion in the code that calls
> the trace events API and bail out if we're in lockdep recursion
> protection to prevent something like the following case: a spin_lock is
> taken. Then lockdep_acquired is called. That does a raw_local_irq_save
> and then sets lockdep_recursion, and then calls __lockdep_acquired. In
> this function, a call to get_lock_stats happens which calls
> preempt_disable, which calls trace IRQS off somewhere which enters my
> tracepoint code and sets the tracing_irq_cpu flag to prevent recursion.
> This flag is then never cleared causing lockdep paths to never be
> entered and thus causing splats and other bad things.
>
> Other than the performance tests mentioned in the previous patch, I also
> ran the locking API test suite. I verified that all tests cases are
> passing.
>
> I also injected issues by not registering lockdep probes onto the
> tracepoints and I see failures to confirm that the probes are indeed
> working.
>
> This series + lockdep probes not registered (just to inject errors):
> [ 0.000000] hard-irqs-on + irq-safe-A/21: ok | ok | ok |
> [ 0.000000] soft-irqs-on + irq-safe-A/21: ok | ok | ok |
> [ 0.000000] sirq-safe-A => hirqs-on/12:FAILED|FAILED| ok |
> [ 0.000000] sirq-safe-A => hirqs-on/21:FAILED|FAILED| ok |
> [ 0.000000] hard-safe-A + irqs-on/12:FAILED|FAILED| ok |
> [ 0.000000] soft-safe-A + irqs-on/12:FAILED|FAILED| ok |
> [ 0.000000] hard-safe-A + irqs-on/21:FAILED|FAILED| ok |
> [ 0.000000] soft-safe-A + irqs-on/21:FAILED|FAILED| ok |
> [ 0.000000] hard-safe-A + unsafe-B #1/123: ok | ok | ok |
> [ 0.000000] soft-safe-A + unsafe-B #1/123: ok | ok | ok |
>
> With this series + lockdep probes registered, all locking tests pass:
>
> [ 0.000000] hard-irqs-on + irq-safe-A/21: ok | ok | ok |
> [ 0.000000] soft-irqs-on + irq-safe-A/21: ok | ok | ok |
> [ 0.000000] sirq-safe-A => hirqs-on/12: ok | ok | ok |
> [ 0.000000] sirq-safe-A => hirqs-on/21: ok | ok | ok |
> [ 0.000000] hard-safe-A + irqs-on/12: ok | ok | ok |
> [ 0.000000] soft-safe-A + irqs-on/12: ok | ok | ok |
> [ 0.000000] hard-safe-A + irqs-on/21: ok | ok | ok |
> [ 0.000000] soft-safe-A + irqs-on/21: ok | ok | ok |
> [ 0.000000] hard-safe-A + unsafe-B #1/123: ok | ok | ok |
> [ 0.000000] soft-safe-A + unsafe-B #1/123: ok | ok | ok |
>
> Reviewed-by: Namhyung Kim <namhyung@xxxxxxxxxx>
> Signed-off-by: Joel Fernandes (Google) <joel@xxxxxxxxxxxxxxxxx>
> ---
> include/linux/ftrace.h | 11 +-
> include/linux/irqflags.h | 11 +-
> include/linux/lockdep.h | 8 +-
> include/linux/preempt.h | 2 +-
> include/trace/events/preemptirq.h | 23 +--
> init/main.c | 5 +-
> kernel/locking/lockdep.c | 35 ++---
> kernel/sched/core.c | 2 +-
> kernel/trace/Kconfig | 22 ++-
> kernel/trace/Makefile | 2 +-
> kernel/trace/trace_irqsoff.c | 231 ++++++++----------------------
> kernel/trace/trace_preemptirq.c | 71 +++++++++
> 12 files changed, 194 insertions(+), 229 deletions(-)
> create mode 100644 kernel/trace/trace_preemptirq.c
>
> diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h
> index 8154f4920fcb..f32e3c81407e 100644
> --- a/include/linux/ftrace.h
> +++ b/include/linux/ftrace.h
> @@ -709,16 +709,7 @@ static inline unsigned long get_lock_parent_ip(void)
> return CALLER_ADDR2;
> }
>
> -#ifdef CONFIG_IRQSOFF_TRACER
> - extern void time_hardirqs_on(unsigned long a0, unsigned long a1);
> - extern void time_hardirqs_off(unsigned long a0, unsigned long a1);
> -#else
> - static inline void time_hardirqs_on(unsigned long a0, unsigned long a1) { }
> - static inline void time_hardirqs_off(unsigned long a0, unsigned long a1) { }
> -#endif
> -
> -#if defined(CONFIG_PREEMPT_TRACER) || \
> - (defined(CONFIG_DEBUG_PREEMPT) && defined(CONFIG_PREEMPTIRQ_EVENTS))
> +#ifdef CONFIG_TRACE_PREEMPT_TOGGLE
> extern void trace_preempt_on(unsigned long a0, unsigned long a1);
> extern void trace_preempt_off(unsigned long a0, unsigned long a1);
> #else
> diff --git a/include/linux/irqflags.h b/include/linux/irqflags.h
> index 9700f00bbc04..50edb9cbbd26 100644
> --- a/include/linux/irqflags.h
> +++ b/include/linux/irqflags.h
> @@ -15,9 +15,16 @@
> #include <linux/typecheck.h>
> #include <asm/irqflags.h>
>
> -#ifdef CONFIG_TRACE_IRQFLAGS
> +/* Currently trace_softirqs_on/off is used only by lockdep */
> +#ifdef CONFIG_PROVE_LOCKING
> extern void trace_softirqs_on(unsigned long ip);
> extern void trace_softirqs_off(unsigned long ip);
> +#else
> +# define trace_softirqs_on(ip) do { } while (0)
> +# define trace_softirqs_off(ip) do { } while (0)
> +#endif
> +
> +#ifdef CONFIG_TRACE_IRQFLAGS
> extern void trace_hardirqs_on(void);
> extern void trace_hardirqs_off(void);
> # define trace_hardirq_context(p) ((p)->hardirq_context)
> @@ -43,8 +50,6 @@ do { \
> #else
> # define trace_hardirqs_on() do { } while (0)
> # define trace_hardirqs_off() do { } while (0)
> -# define trace_softirqs_on(ip) do { } while (0)
> -# define trace_softirqs_off(ip) do { } while (0)
> # define trace_hardirq_context(p) 0
> # define trace_softirq_context(p) 0
> # define trace_hardirqs_enabled(p) 0
> diff --git a/include/linux/lockdep.h b/include/linux/lockdep.h
> index 6fc77d4dbdcd..a8113357ceeb 100644
> --- a/include/linux/lockdep.h
> +++ b/include/linux/lockdep.h
> @@ -266,7 +266,8 @@ struct held_lock {
> /*
> * Initialization, self-test and debugging-output methods:
> */
> -extern void lockdep_info(void);
> +extern void lockdep_init(void);
> +extern void lockdep_init_early(void);
> extern void lockdep_reset(void);
> extern void lockdep_reset_lock(struct lockdep_map *lock);
> extern void lockdep_free_key_range(void *start, unsigned long size);
> @@ -406,7 +407,8 @@ static inline void lockdep_on(void)
> # define lock_downgrade(l, i) do { } while (0)
> # define lock_set_class(l, n, k, s, i) do { } while (0)
> # define lock_set_subclass(l, s, i) do { } while (0)
> -# define lockdep_info() do { } while (0)
> +# define lockdep_init() do { } while (0)
> +# define lockdep_init_early() do { } while (0)
> # define lockdep_init_map(lock, name, key, sub) \
> do { (void)(name); (void)(key); } while (0)
> # define lockdep_set_class(lock, key) do { (void)(key); } while (0)
> @@ -532,7 +534,7 @@ do { \
>
> #endif /* CONFIG_LOCKDEP */
>
> -#ifdef CONFIG_TRACE_IRQFLAGS
> +#ifdef CONFIG_PROVE_LOCKING
> extern void print_irqtrace_events(struct task_struct *curr);
> #else
> static inline void print_irqtrace_events(struct task_struct *curr)
> diff --git a/include/linux/preempt.h b/include/linux/preempt.h
> index 5bd3f151da78..c01813c3fbe9 100644
> --- a/include/linux/preempt.h
> +++ b/include/linux/preempt.h
> @@ -150,7 +150,7 @@
> */
> #define in_atomic_preempt_off() (preempt_count() != PREEMPT_DISABLE_OFFSET)
>
> -#if defined(CONFIG_DEBUG_PREEMPT) || defined(CONFIG_PREEMPT_TRACER)
> +#if defined(CONFIG_DEBUG_PREEMPT) || defined(CONFIG_TRACE_PREEMPT_TOGGLE)
> extern void preempt_count_add(int val);
> extern void preempt_count_sub(int val);
> #define preempt_count_dec_and_test() \
> diff --git a/include/trace/events/preemptirq.h b/include/trace/events/preemptirq.h
> index 9c4eb33c5a1d..9a0d4ceeb166 100644
> --- a/include/trace/events/preemptirq.h
> +++ b/include/trace/events/preemptirq.h
> @@ -1,4 +1,4 @@
> -#ifdef CONFIG_PREEMPTIRQ_EVENTS
> +#ifdef CONFIG_PREEMPTIRQ_TRACEPOINTS
>
> #undef TRACE_SYSTEM
> #define TRACE_SYSTEM preemptirq
> @@ -32,7 +32,7 @@ DECLARE_EVENT_CLASS(preemptirq_template,
> (void *)((unsigned long)(_stext) + __entry->parent_offs))
> );
>
> -#ifndef CONFIG_PROVE_LOCKING
> +#ifdef CONFIG_TRACE_IRQFLAGS
> DEFINE_EVENT(preemptirq_template, irq_disable,
> TP_PROTO(unsigned long ip, unsigned long parent_ip),
> TP_ARGS(ip, parent_ip));
> @@ -40,9 +40,14 @@ DEFINE_EVENT(preemptirq_template, irq_disable,
> DEFINE_EVENT(preemptirq_template, irq_enable,
> TP_PROTO(unsigned long ip, unsigned long parent_ip),
> TP_ARGS(ip, parent_ip));
> +#else
> +#define trace_irq_enable(...)
> +#define trace_irq_disable(...)
> +#define trace_irq_enable_rcuidle(...)
> +#define trace_irq_disable_rcuidle(...)
> #endif
>
> -#ifdef CONFIG_DEBUG_PREEMPT
> +#ifdef CONFIG_TRACE_PREEMPT_TOGGLE
> DEFINE_EVENT(preemptirq_template, preempt_disable,
> TP_PROTO(unsigned long ip, unsigned long parent_ip),
> TP_ARGS(ip, parent_ip));
> @@ -50,22 +55,22 @@ DEFINE_EVENT(preemptirq_template, preempt_disable,
> DEFINE_EVENT(preemptirq_template, preempt_enable,
> TP_PROTO(unsigned long ip, unsigned long parent_ip),
> TP_ARGS(ip, parent_ip));
> +#else
> +#define trace_preempt_enable(...)
> +#define trace_preempt_disable(...)
> +#define trace_preempt_enable_rcuidle(...)
> +#define trace_preempt_disable_rcuidle(...)
> #endif
>
> #endif /* _TRACE_PREEMPTIRQ_H */
>
> #include <trace/define_trace.h>
>
> -#endif /* !CONFIG_PREEMPTIRQ_EVENTS */
> -
> -#if !defined(CONFIG_PREEMPTIRQ_EVENTS) || defined(CONFIG_PROVE_LOCKING)
> +#else /* !CONFIG_PREEMPTIRQ_TRACEPOINTS */
> #define trace_irq_enable(...)
> #define trace_irq_disable(...)
> #define trace_irq_enable_rcuidle(...)
> #define trace_irq_disable_rcuidle(...)
> -#endif
> -
> -#if !defined(CONFIG_PREEMPTIRQ_EVENTS) || !defined(CONFIG_DEBUG_PREEMPT)
> #define trace_preempt_enable(...)
> #define trace_preempt_disable(...)
> #define trace_preempt_enable_rcuidle(...)
> diff --git a/init/main.c b/init/main.c
> index 3b4ada11ed52..44fe43be84c1 100644
> --- a/init/main.c
> +++ b/init/main.c
> @@ -648,6 +648,9 @@ asmlinkage __visible void __init start_kernel(void)
> profile_init();
> call_function_init();
> WARN(!irqs_disabled(), "Interrupts were enabled early\n");
> +
> + lockdep_init_early();
> +
> early_boot_irqs_disabled = false;
> local_irq_enable();
>
> @@ -663,7 +666,7 @@ asmlinkage __visible void __init start_kernel(void)
> panic("Too many boot %s vars at `%s'", panic_later,
> panic_param);
>
> - lockdep_info();
> + lockdep_init();
>
> /*
> * Need to run this when irqs are enabled, because it wants
> diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c
> index 5fa4d3138bf1..b961a1698e98 100644
> --- a/kernel/locking/lockdep.c
> +++ b/kernel/locking/lockdep.c
> @@ -55,6 +55,7 @@
>
> #include "lockdep_internals.h"
>
> +#include <trace/events/preemptirq.h>
> #define CREATE_TRACE_POINTS
> #include <trace/events/lock.h>
>
> @@ -2845,10 +2846,9 @@ static void __trace_hardirqs_on_caller(unsigned long ip)
> debug_atomic_inc(hardirqs_on_events);
> }
>
> -__visible void trace_hardirqs_on_caller(unsigned long ip)
> +static void lockdep_hardirqs_on(void *none, unsigned long ignore,
> + unsigned long ip)
> {
> - time_hardirqs_on(CALLER_ADDR0, ip);
> -
> if (unlikely(!debug_locks || current->lockdep_recursion))
> return;
>
> @@ -2887,23 +2887,15 @@ __visible void trace_hardirqs_on_caller(unsigned long ip)
> __trace_hardirqs_on_caller(ip);
> current->lockdep_recursion = 0;
> }
> -EXPORT_SYMBOL(trace_hardirqs_on_caller);
> -
> -void trace_hardirqs_on(void)
> -{
> - trace_hardirqs_on_caller(CALLER_ADDR0);
> -}
> -EXPORT_SYMBOL(trace_hardirqs_on);
>
> /*
> * Hardirqs were disabled:
> */
> -__visible void trace_hardirqs_off_caller(unsigned long ip)
> +static void lockdep_hardirqs_off(void *none, unsigned long ignore,
> + unsigned long ip)
> {
> struct task_struct *curr = current;
>
> - time_hardirqs_off(CALLER_ADDR0, ip);
> -
> if (unlikely(!debug_locks || current->lockdep_recursion))
> return;
>
> @@ -2925,13 +2917,6 @@ __visible void trace_hardirqs_off_caller(unsigned long ip)
> } else
> debug_atomic_inc(redundant_hardirqs_off);
> }
> -EXPORT_SYMBOL(trace_hardirqs_off_caller);
> -
> -void trace_hardirqs_off(void)
> -{
> - trace_hardirqs_off_caller(CALLER_ADDR0);
> -}
> -EXPORT_SYMBOL(trace_hardirqs_off);
>
> /*
> * Softirqs will be enabled:
> @@ -4338,7 +4323,15 @@ void lockdep_reset_lock(struct lockdep_map *lock)
> raw_local_irq_restore(flags);
> }
>
> -void __init lockdep_info(void)
> +void __init lockdep_init_early(void)
> +{
> +#ifdef CONFIG_PROVE_LOCKING
> + register_trace_prio_irq_disable(lockdep_hardirqs_off, NULL, INT_MAX);
> + register_trace_prio_irq_enable(lockdep_hardirqs_on, NULL, INT_MIN);
> +#endif
> +}
> +
> +void __init lockdep_init(void)
> {
> printk("Lock dependency validator: Copyright (c) 2006 Red Hat, Inc., Ingo Molnar\n");
>
> diff --git a/kernel/sched/core.c b/kernel/sched/core.c
> index 78d8facba456..4c956f6849ec 100644
> --- a/kernel/sched/core.c
> +++ b/kernel/sched/core.c
> @@ -3192,7 +3192,7 @@ static inline void sched_tick_stop(int cpu) { }
> #endif
>
> #if defined(CONFIG_PREEMPT) && (defined(CONFIG_DEBUG_PREEMPT) || \
> - defined(CONFIG_PREEMPT_TRACER))
> + defined(CONFIG_TRACE_PREEMPT_TOGGLE))
> /*
> * If the value passed in is equal to the current preempt count
> * then we just disabled preemption. Start timing the latency.
> diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig
> index dcc0166d1997..8d51351e3149 100644
> --- a/kernel/trace/Kconfig
> +++ b/kernel/trace/Kconfig
> @@ -82,6 +82,15 @@ config RING_BUFFER_ALLOW_SWAP
> Allow the use of ring_buffer_swap_cpu.
> Adds a very slight overhead to tracing when enabled.
>
> +config PREEMPTIRQ_TRACEPOINTS
> + bool
> + depends on TRACE_PREEMPT_TOGGLE || TRACE_IRQFLAGS
> + select TRACING
> + default y
> + help
> + Create preempt/irq toggle tracepoints if needed, so that other parts
> + of the kernel can use them to generate or add hooks to them.
> +
> # All tracer options should select GENERIC_TRACER. For those options that are
> # enabled by all tracers (context switch and event tracer) they select TRACING.
> # This allows those options to appear when no other tracer is selected. But the
> @@ -155,18 +164,20 @@ config FUNCTION_GRAPH_TRACER
> the return value. This is done by setting the current return
> address on the current task structure into a stack of calls.
>
> +config TRACE_PREEMPT_TOGGLE
> + bool
> + help
> + Enables hooks which will be called when preemption is first disabled,
> + and last enabled.
>
> config PREEMPTIRQ_EVENTS
> bool "Enable trace events for preempt and irq disable/enable"
> select TRACE_IRQFLAGS
> - depends on DEBUG_PREEMPT || !PROVE_LOCKING
> - depends on TRACING
> + select TRACE_PREEMPT_TOGGLE if PREEMPT
> + select GENERIC_TRACER
> default n
> help
> Enable tracing of disable and enable events for preemption and irqs.
> - For tracing preempt disable/enable events, DEBUG_PREEMPT must be
> - enabled. For tracing irq disable/enable events, PROVE_LOCKING must
> - be disabled.
>
> config IRQSOFF_TRACER
> bool "Interrupts-off Latency Tracer"
> @@ -203,6 +214,7 @@ config PREEMPT_TRACER
> select RING_BUFFER_ALLOW_SWAP
> select TRACER_SNAPSHOT
> select TRACER_SNAPSHOT_PER_CPU_SWAP
> + select TRACE_PREEMPT_TOGGLE
> help
> This option measures the time spent in preemption-off critical
> sections, with microsecond accuracy.
> diff --git a/kernel/trace/Makefile b/kernel/trace/Makefile
> index e2538c7638d4..84a0cb222f20 100644
> --- a/kernel/trace/Makefile
> +++ b/kernel/trace/Makefile
> @@ -35,7 +35,7 @@ obj-$(CONFIG_TRACING) += trace_printk.o
> obj-$(CONFIG_TRACING_MAP) += tracing_map.o
> obj-$(CONFIG_CONTEXT_SWITCH_TRACER) += trace_sched_switch.o
> obj-$(CONFIG_FUNCTION_TRACER) += trace_functions.o
> -obj-$(CONFIG_PREEMPTIRQ_EVENTS) += trace_irqsoff.o
> +obj-$(CONFIG_PREEMPTIRQ_TRACEPOINTS) += trace_preemptirq.o
> obj-$(CONFIG_IRQSOFF_TRACER) += trace_irqsoff.o
> obj-$(CONFIG_PREEMPT_TRACER) += trace_irqsoff.o
> obj-$(CONFIG_SCHED_TRACER) += trace_sched_wakeup.o
> diff --git a/kernel/trace/trace_irqsoff.c b/kernel/trace/trace_irqsoff.c
> index f8daa754cce2..770cd30cda40 100644
> --- a/kernel/trace/trace_irqsoff.c
> +++ b/kernel/trace/trace_irqsoff.c
> @@ -16,7 +16,6 @@
>
> #include "trace.h"
>
> -#define CREATE_TRACE_POINTS
> #include <trace/events/preemptirq.h>
>
> #if defined(CONFIG_IRQSOFF_TRACER) || defined(CONFIG_PREEMPT_TRACER)
> @@ -450,66 +449,6 @@ void stop_critical_timings(void)
> }
> EXPORT_SYMBOL_GPL(stop_critical_timings);
>
> -#ifdef CONFIG_IRQSOFF_TRACER
> -#ifdef CONFIG_PROVE_LOCKING
> -void time_hardirqs_on(unsigned long a0, unsigned long a1)
> -{
> - if (!preempt_trace() && irq_trace())
> - stop_critical_timing(a0, a1);
> -}
> -
> -void time_hardirqs_off(unsigned long a0, unsigned long a1)
> -{
> - if (!preempt_trace() && irq_trace())
> - start_critical_timing(a0, a1);
> -}
> -
> -#else /* !CONFIG_PROVE_LOCKING */
> -
> -/*
> - * We are only interested in hardirq on/off events:
> - */
> -static inline void tracer_hardirqs_on(void)
> -{
> - if (!preempt_trace() && irq_trace())
> - stop_critical_timing(CALLER_ADDR0, CALLER_ADDR1);
> -}
> -
> -static inline void tracer_hardirqs_off(void)
> -{
> - if (!preempt_trace() && irq_trace())
> - start_critical_timing(CALLER_ADDR0, CALLER_ADDR1);
> -}
> -
> -static inline void tracer_hardirqs_on_caller(unsigned long caller_addr)
> -{
> - if (!preempt_trace() && irq_trace())
> - stop_critical_timing(CALLER_ADDR0, caller_addr);
> -}
> -
> -static inline void tracer_hardirqs_off_caller(unsigned long caller_addr)
> -{
> - if (!preempt_trace() && irq_trace())
> - start_critical_timing(CALLER_ADDR0, caller_addr);
> -}
> -
> -#endif /* CONFIG_PROVE_LOCKING */
> -#endif /* CONFIG_IRQSOFF_TRACER */
> -
> -#ifdef CONFIG_PREEMPT_TRACER
> -static inline void tracer_preempt_on(unsigned long a0, unsigned long a1)
> -{
> - if (preempt_trace() && !irq_trace())
> - stop_critical_timing(a0, a1);
> -}
> -
> -static inline void tracer_preempt_off(unsigned long a0, unsigned long a1)
> -{
> - if (preempt_trace() && !irq_trace())
> - start_critical_timing(a0, a1);
> -}
> -#endif /* CONFIG_PREEMPT_TRACER */
> -
> #ifdef CONFIG_FUNCTION_TRACER
> static bool function_enabled;
>
> @@ -659,15 +598,34 @@ static void irqsoff_tracer_stop(struct trace_array *tr)
> }
>
> #ifdef CONFIG_IRQSOFF_TRACER
> +/*
> + * We are only interested in hardirq on/off events:
> + */
> +static void tracer_hardirqs_on(void *none, unsigned long a0, unsigned long a1)
> +{
> + if (!preempt_trace() && irq_trace())
> + stop_critical_timing(a0, a1);
> +}
> +
> +static void tracer_hardirqs_off(void *none, unsigned long a0, unsigned long a1)
> +{
> + if (!preempt_trace() && irq_trace())
> + start_critical_timing(a0, a1);
> +}
> +
> static int irqsoff_tracer_init(struct trace_array *tr)
> {
> trace_type = TRACER_IRQS_OFF;
>
> + register_trace_irq_disable(tracer_hardirqs_off, NULL);
> + register_trace_irq_enable(tracer_hardirqs_on, NULL);
> return __irqsoff_tracer_init(tr);
> }
>
> static void irqsoff_tracer_reset(struct trace_array *tr)
> {
> + unregister_trace_irq_disable(tracer_hardirqs_off, NULL);
> + unregister_trace_irq_enable(tracer_hardirqs_on, NULL);
> __irqsoff_tracer_reset(tr);
> }
>
> @@ -690,21 +648,34 @@ static struct tracer irqsoff_tracer __read_mostly =
> .allow_instances = true,
> .use_max_tr = true,
> };
> -# define register_irqsoff(trace) register_tracer(&trace)
> -#else
> -# define register_irqsoff(trace) do { } while (0)
> -#endif
> +#endif /* CONFIG_IRQSOFF_TRACER */
>
> #ifdef CONFIG_PREEMPT_TRACER
> +static void tracer_preempt_on(void *none, unsigned long a0, unsigned long a1)
> +{
> + if (preempt_trace() && !irq_trace())
> + stop_critical_timing(a0, a1);
> +}
> +
> +static void tracer_preempt_off(void *none, unsigned long a0, unsigned long a1)
> +{
> + if (preempt_trace() && !irq_trace())
> + start_critical_timing(a0, a1);
> +}
> +
> static int preemptoff_tracer_init(struct trace_array *tr)
> {
> trace_type = TRACER_PREEMPT_OFF;
>
> + register_trace_preempt_disable(tracer_preempt_off, NULL);
> + register_trace_preempt_enable(tracer_preempt_on, NULL);
> return __irqsoff_tracer_init(tr);
> }
>
> static void preemptoff_tracer_reset(struct trace_array *tr)
> {
> + unregister_trace_preempt_disable(tracer_preempt_off, NULL);
> + unregister_trace_preempt_enable(tracer_preempt_on, NULL);
> __irqsoff_tracer_reset(tr);
> }
>
> @@ -727,23 +698,29 @@ static struct tracer preemptoff_tracer __read_mostly =
> .allow_instances = true,
> .use_max_tr = true,
> };
> -# define register_preemptoff(trace) register_tracer(&trace)
> -#else
> -# define register_preemptoff(trace) do { } while (0)
> -#endif
> +#endif /* CONFIG_PREEMPT_TRACER */
>
> -#if defined(CONFIG_IRQSOFF_TRACER) && \
> - defined(CONFIG_PREEMPT_TRACER)
> +#if defined(CONFIG_IRQSOFF_TRACER) && defined(CONFIG_PREEMPT_TRACER)
>
> static int preemptirqsoff_tracer_init(struct trace_array *tr)
> {
> trace_type = TRACER_IRQS_OFF | TRACER_PREEMPT_OFF;
>
> + register_trace_irq_disable(tracer_hardirqs_off, NULL);
> + register_trace_irq_enable(tracer_hardirqs_on, NULL);
> + register_trace_preempt_disable(tracer_preempt_off, NULL);
> + register_trace_preempt_enable(tracer_preempt_on, NULL);
> +
> return __irqsoff_tracer_init(tr);
> }
>
> static void preemptirqsoff_tracer_reset(struct trace_array *tr)
> {
> + unregister_trace_irq_disable(tracer_hardirqs_off, NULL);
> + unregister_trace_irq_enable(tracer_hardirqs_on, NULL);
> + unregister_trace_preempt_disable(tracer_preempt_off, NULL);
> + unregister_trace_preempt_enable(tracer_preempt_on, NULL);
> +
> __irqsoff_tracer_reset(tr);
> }
>
> @@ -766,115 +743,21 @@ static struct tracer preemptirqsoff_tracer __read_mostly =
> .allow_instances = true,
> .use_max_tr = true,
> };
> -
> -# define register_preemptirqsoff(trace) register_tracer(&trace)
> -#else
> -# define register_preemptirqsoff(trace) do { } while (0)
> #endif
>
> __init static int init_irqsoff_tracer(void)
> {
> - register_irqsoff(irqsoff_tracer);
> - register_preemptoff(preemptoff_tracer);
> - register_preemptirqsoff(preemptirqsoff_tracer);
> -
> - return 0;
> -}
> -core_initcall(init_irqsoff_tracer);
> -#endif /* IRQSOFF_TRACER || PREEMPTOFF_TRACER */
> -
> -#ifndef CONFIG_IRQSOFF_TRACER
> -static inline void tracer_hardirqs_on(void) { }
> -static inline void tracer_hardirqs_off(void) { }
> -static inline void tracer_hardirqs_on_caller(unsigned long caller_addr) { }
> -static inline void tracer_hardirqs_off_caller(unsigned long caller_addr) { }
> +#ifdef CONFIG_IRQSOFF_TRACER
> + register_tracer(&irqsoff_tracer);
> #endif
> -
> -#ifndef CONFIG_PREEMPT_TRACER
> -static inline void tracer_preempt_on(unsigned long a0, unsigned long a1) { }
> -static inline void tracer_preempt_off(unsigned long a0, unsigned long a1) { }
> +#ifdef CONFIG_PREEMPT_TRACER
> + register_tracer(&preemptoff_tracer);
> #endif
> -
> -#if defined(CONFIG_TRACE_IRQFLAGS) && !defined(CONFIG_PROVE_LOCKING)
> -/* Per-cpu variable to prevent redundant calls when IRQs already off */
> -static DEFINE_PER_CPU(int, tracing_irq_cpu);
> -
> -void trace_hardirqs_on(void)
> -{
> - if (!this_cpu_read(tracing_irq_cpu))
> - return;
> -
> - trace_irq_enable_rcuidle(CALLER_ADDR0, CALLER_ADDR1);
> - tracer_hardirqs_on();
> -
> - this_cpu_write(tracing_irq_cpu, 0);
> -}
> -EXPORT_SYMBOL(trace_hardirqs_on);
> -
> -void trace_hardirqs_off(void)
> -{
> - if (this_cpu_read(tracing_irq_cpu))
> - return;
> -
> - this_cpu_write(tracing_irq_cpu, 1);
> -
> - trace_irq_disable_rcuidle(CALLER_ADDR0, CALLER_ADDR1);
> - tracer_hardirqs_off();
> -}
> -EXPORT_SYMBOL(trace_hardirqs_off);
> -
> -__visible void trace_hardirqs_on_caller(unsigned long caller_addr)
> -{
> - if (!this_cpu_read(tracing_irq_cpu))
> - return;
> -
> - trace_irq_enable_rcuidle(CALLER_ADDR0, caller_addr);
> - tracer_hardirqs_on_caller(caller_addr);
> -
> - this_cpu_write(tracing_irq_cpu, 0);
> -}
> -EXPORT_SYMBOL(trace_hardirqs_on_caller);
> -
> -__visible void trace_hardirqs_off_caller(unsigned long caller_addr)
> -{
> - if (this_cpu_read(tracing_irq_cpu))
> - return;
> -
> - this_cpu_write(tracing_irq_cpu, 1);
> -
> - trace_irq_disable_rcuidle(CALLER_ADDR0, caller_addr);
> - tracer_hardirqs_off_caller(caller_addr);
> -}
> -EXPORT_SYMBOL(trace_hardirqs_off_caller);
> -
> -/*
> - * Stubs:
> - */
> -
> -void trace_softirqs_on(unsigned long ip)
> -{
> -}
> -
> -void trace_softirqs_off(unsigned long ip)
> -{
> -}
> -
> -inline void print_irqtrace_events(struct task_struct *curr)
> -{
> -}
> +#if defined(CONFIG_IRQSOFF_TRACER) && defined(CONFIG_PREEMPT_TRACER)
> + register_tracer(&preemptirqsoff_tracer);
> #endif
>
> -#if defined(CONFIG_PREEMPT_TRACER) || \
> - (defined(CONFIG_DEBUG_PREEMPT) && defined(CONFIG_PREEMPTIRQ_EVENTS))
> -void trace_preempt_on(unsigned long a0, unsigned long a1)
> -{
> - trace_preempt_enable_rcuidle(a0, a1);
> - tracer_preempt_on(a0, a1);
> -}
> -
> -void trace_preempt_off(unsigned long a0, unsigned long a1)
> -{
> - trace_preempt_disable_rcuidle(a0, a1);
> - tracer_preempt_off(a0, a1);
> + return 0;
> }
> -#endif
> +core_initcall(init_irqsoff_tracer);
> +#endif /* IRQSOFF_TRACER || PREEMPTOFF_TRACER */
> diff --git a/kernel/trace/trace_preemptirq.c b/kernel/trace/trace_preemptirq.c
> new file mode 100644
> index 000000000000..dc01c7f4d326
> --- /dev/null
> +++ b/kernel/trace/trace_preemptirq.c
> @@ -0,0 +1,71 @@
> +/*
> + * preemptoff and irqoff tracepoints
> + *
> + * Copyright (C) Joel Fernandes (Google) <joel@xxxxxxxxxxxxxxxxx>
> + */
> +
> +#include <linux/kallsyms.h>
> +#include <linux/uaccess.h>
> +#include <linux/module.h>
> +#include <linux/ftrace.h>
> +
> +#define CREATE_TRACE_POINTS
> +#include <trace/events/preemptirq.h>
> +
> +#ifdef CONFIG_TRACE_IRQFLAGS
> +/* Per-cpu variable to prevent redundant calls when IRQs already off */
> +static DEFINE_PER_CPU(int, tracing_irq_cpu);
> +
> +void trace_hardirqs_on(void)
> +{
> + if (lockdep_recursing(current) || !this_cpu_read(tracing_irq_cpu))
> + return;
> +
> + trace_irq_enable_rcuidle(CALLER_ADDR0, CALLER_ADDR1);
> + this_cpu_write(tracing_irq_cpu, 0);
> +}
> +EXPORT_SYMBOL(trace_hardirqs_on);
> +
> +void trace_hardirqs_off(void)
> +{
> + if (lockdep_recursing(current) || this_cpu_read(tracing_irq_cpu))
> + return;
> +
> + this_cpu_write(tracing_irq_cpu, 1);
> + trace_irq_disable_rcuidle(CALLER_ADDR0, CALLER_ADDR1);
> +}
> +EXPORT_SYMBOL(trace_hardirqs_off);
> +
> +__visible void trace_hardirqs_on_caller(unsigned long caller_addr)
> +{
> + if (lockdep_recursing(current) || !this_cpu_read(tracing_irq_cpu))
> + return;
> +
> + trace_irq_enable_rcuidle(CALLER_ADDR0, caller_addr);
> + this_cpu_write(tracing_irq_cpu, 0);
> +}
> +EXPORT_SYMBOL(trace_hardirqs_on_caller);
> +
> +__visible void trace_hardirqs_off_caller(unsigned long caller_addr)
> +{
> + if (lockdep_recursing(current) || this_cpu_read(tracing_irq_cpu))
> + return;
> +
> + this_cpu_write(tracing_irq_cpu, 1);
> + trace_irq_disable_rcuidle(CALLER_ADDR0, caller_addr);
> +}
> +EXPORT_SYMBOL(trace_hardirqs_off_caller);
> +#endif /* CONFIG_TRACE_IRQFLAGS */
> +
> +#ifdef CONFIG_TRACE_PREEMPT_TOGGLE
> +
> +void trace_preempt_on(unsigned long a0, unsigned long a1)
> +{
> + trace_preempt_enable_rcuidle(a0, a1);
> +}
> +
> +void trace_preempt_off(unsigned long a0, unsigned long a1)
> +{
> + trace_preempt_disable_rcuidle(a0, a1);
> +}
> +#endif