Re: [PATCH 4/9] ftrace: Add enable/disable ftrace_ops controlinterface
From: Steven Rostedt
Date: Mon Nov 28 2011 - 14:26:39 EST
[ Added Peter Z ]
On Sun, 2011-11-27 at 19:04 +0100, Jiri Olsa wrote:
> Adding a way to temporarily enable/disable ftrace_ops.
>
> When there is a ftrace_ops with FTRACE_OPS_FL_CONTROL flag
> registered, the ftrace_ops_list_func processing function
> is used as ftrace function in order to have all registered
> ftrace_ops under control.
>
> Also using jump label not to introduce overhead to current
> ftrace_ops_list_func processing.
>
Are jump labels safe in NMI context yet? If not, this will need to wait
till we make it so.
-- Steve
> Signed-off-by: Jiri Olsa <jolsa@xxxxxxxxxx>
> ---
> include/linux/ftrace.h | 12 ++++++++++++
> kernel/trace/ftrace.c | 39 +++++++++++++++++++++++++++++----------
> 2 files changed, 41 insertions(+), 10 deletions(-)
>
> diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h
> index 26eafce..28b59f1 100644
> --- a/include/linux/ftrace.h
> +++ b/include/linux/ftrace.h
> @@ -35,12 +35,14 @@ enum {
> FTRACE_OPS_FL_ENABLED = 1 << 0,
> FTRACE_OPS_FL_GLOBAL = 1 << 1,
> FTRACE_OPS_FL_DYNAMIC = 1 << 2,
> + FTRACE_OPS_FL_CONTROL = 1 << 3,
> };
>
> struct ftrace_ops {
> ftrace_func_t func;
> struct ftrace_ops *next;
> unsigned long flags;
> + atomic_t disabled;
> #ifdef CONFIG_DYNAMIC_FTRACE
> struct ftrace_hash *notrace_hash;
> struct ftrace_hash *filter_hash;
> @@ -97,6 +99,16 @@ int register_ftrace_function(struct ftrace_ops *ops);
> int unregister_ftrace_function(struct ftrace_ops *ops);
> void clear_ftrace_function(void);
>
> +static inline void enable_ftrace_function(struct ftrace_ops *ops)
> +{
> + atomic_dec(&ops->disabled);
> +}
> +
> +static inline void disable_ftrace_function(struct ftrace_ops *ops)
> +{
> + atomic_inc(&ops->disabled);
> +}
> +
> extern void ftrace_stub(unsigned long a0, unsigned long a1);
>
> #else /* !CONFIG_FUNCTION_TRACER */
> diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
> index 0ca0c0d..e5a9498 100644
> --- a/kernel/trace/ftrace.c
> +++ b/kernel/trace/ftrace.c
> @@ -30,6 +30,7 @@
> #include <linux/list.h>
> #include <linux/hash.h>
> #include <linux/rcupdate.h>
> +#include <linux/jump_label.h>
>
> #include <trace/events/sched.h>
>
> @@ -94,6 +95,8 @@ ftrace_func_t __ftrace_trace_function __read_mostly = ftrace_stub;
> ftrace_func_t ftrace_pid_function __read_mostly = ftrace_stub;
> static struct ftrace_ops global_ops;
>
> +static struct jump_label_key ftrace_ops_control;
> +
> static void
> ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip);
>
> @@ -196,17 +199,21 @@ static void update_ftrace_function(void)
>
> update_global_ops();
>
> - /*
> - * If we are at the end of the list and this ops is
> - * not dynamic, then have the mcount trampoline call
> - * the function directly
> - */
> - if (ftrace_ops_list == &ftrace_list_end ||
> - (ftrace_ops_list->next == &ftrace_list_end &&
> - !(ftrace_ops_list->flags & FTRACE_OPS_FL_DYNAMIC)))
> - func = ftrace_ops_list->func;
> - else
> + if (jump_label_enabled(&ftrace_ops_control))
> func = ftrace_ops_list_func;
> + else {
> + /*
> + * If we are at the end of the list and this ops is
> + * not dynamic, then have the mcount trampoline call
> + * the function directly
> + */
> + if (ftrace_ops_list == &ftrace_list_end ||
> + (ftrace_ops_list->next == &ftrace_list_end &&
> + !(ftrace_ops_list->flags & FTRACE_OPS_FL_DYNAMIC)))
> + func = ftrace_ops_list->func;
> + else
> + func = ftrace_ops_list_func;
> + }
>
> #ifdef CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST
> ftrace_trace_function = func;
> @@ -280,6 +287,9 @@ static int __register_ftrace_function(struct ftrace_ops *ops)
> } else
> add_ftrace_ops(&ftrace_ops_list, ops);
>
> + if (ops->flags & FTRACE_OPS_FL_CONTROL)
> + jump_label_inc(&ftrace_ops_control);
> +
> if (ftrace_enabled)
> update_ftrace_function();
>
> @@ -311,6 +321,9 @@ static int __unregister_ftrace_function(struct ftrace_ops *ops)
> if (ret < 0)
> return ret;
>
> + if (ops->flags & FTRACE_OPS_FL_CONTROL)
> + jump_label_dec(&ftrace_ops_control);
> +
> if (ftrace_enabled)
> update_ftrace_function();
>
> @@ -3577,8 +3590,14 @@ ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip)
> preempt_disable_notrace();
> op = rcu_dereference_raw(ftrace_ops_list);
> while (op != &ftrace_list_end) {
> + if (static_branch(&ftrace_ops_control))
> + if ((op->flags & FTRACE_OPS_FL_CONTROL) &&
> + atomic_read(&op->disabled))
> + goto next;
> +
> if (ftrace_ops_test(op, ip))
> op->func(ip, parent_ip);
> + next:
> op = rcu_dereference_raw(op->next);
> };
> preempt_enable_notrace();
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/