Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 83b84503 authored by Ingo Molnar's avatar Ingo Molnar
Browse files

Merge branch 'tip/perf/core' of...

Merge branch 'tip/perf/core' of git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-trace into perf/core
parents 4ff16c25 5500fa51
Loading
Loading
Loading
Loading
+70 −3
Original line number Original line Diff line number Diff line
@@ -31,16 +31,33 @@ ftrace_enable_sysctl(struct ctl_table *table, int write,


typedef void (*ftrace_func_t)(unsigned long ip, unsigned long parent_ip);
typedef void (*ftrace_func_t)(unsigned long ip, unsigned long parent_ip);


/*
 * FTRACE_OPS_FL_* bits denote the state of ftrace_ops struct and are
 * set in the flags member.
 *
 * ENABLED - set/unset when ftrace_ops is registered/unregistered
 * GLOBAL  - set manualy by ftrace_ops user to denote the ftrace_ops
 *           is part of the global tracers sharing the same filter
 *           via set_ftrace_* debugfs files.
 * DYNAMIC - set when ftrace_ops is registered to denote dynamically
 *           allocated ftrace_ops which need special care
 * CONTROL - set manualy by ftrace_ops user to denote the ftrace_ops
 *           could be controled by following calls:
 *             ftrace_function_local_enable
 *             ftrace_function_local_disable
 */
enum {
enum {
	FTRACE_OPS_FL_ENABLED		= 1 << 0,
	FTRACE_OPS_FL_ENABLED		= 1 << 0,
	FTRACE_OPS_FL_GLOBAL		= 1 << 1,
	FTRACE_OPS_FL_GLOBAL		= 1 << 1,
	FTRACE_OPS_FL_DYNAMIC		= 1 << 2,
	FTRACE_OPS_FL_DYNAMIC		= 1 << 2,
	FTRACE_OPS_FL_CONTROL		= 1 << 3,
};
};


struct ftrace_ops {
struct ftrace_ops {
	ftrace_func_t			func;
	ftrace_func_t			func;
	struct ftrace_ops		*next;
	struct ftrace_ops		*next;
	unsigned long			flags;
	unsigned long			flags;
	int __percpu			*disabled;
#ifdef CONFIG_DYNAMIC_FTRACE
#ifdef CONFIG_DYNAMIC_FTRACE
	struct ftrace_hash		*notrace_hash;
	struct ftrace_hash		*notrace_hash;
	struct ftrace_hash		*filter_hash;
	struct ftrace_hash		*filter_hash;
@@ -97,6 +114,55 @@ int register_ftrace_function(struct ftrace_ops *ops);
int unregister_ftrace_function(struct ftrace_ops *ops);
int unregister_ftrace_function(struct ftrace_ops *ops);
void clear_ftrace_function(void);
void clear_ftrace_function(void);


/**
 * ftrace_function_local_enable - enable controlled ftrace_ops on current cpu
 *
 * This function enables tracing on current cpu by decreasing
 * the per cpu control variable.
 * It must be called with preemption disabled and only on ftrace_ops
 * registered with FTRACE_OPS_FL_CONTROL. If called without preemption
 * disabled, this_cpu_ptr will complain when CONFIG_DEBUG_PREEMPT is enabled.
 */
static inline void ftrace_function_local_enable(struct ftrace_ops *ops)
{
	if (WARN_ON_ONCE(!(ops->flags & FTRACE_OPS_FL_CONTROL)))
		return;

	(*this_cpu_ptr(ops->disabled))--;
}

/**
 * ftrace_function_local_disable - enable controlled ftrace_ops on current cpu
 *
 * This function enables tracing on current cpu by decreasing
 * the per cpu control variable.
 * It must be called with preemption disabled and only on ftrace_ops
 * registered with FTRACE_OPS_FL_CONTROL. If called without preemption
 * disabled, this_cpu_ptr will complain when CONFIG_DEBUG_PREEMPT is enabled.
 */
static inline void ftrace_function_local_disable(struct ftrace_ops *ops)
{
	if (WARN_ON_ONCE(!(ops->flags & FTRACE_OPS_FL_CONTROL)))
		return;

	(*this_cpu_ptr(ops->disabled))++;
}

/**
 * ftrace_function_local_disabled - returns ftrace_ops disabled value
 *                                  on current cpu
 *
 * This function returns value of ftrace_ops::disabled on current cpu.
 * It must be called with preemption disabled and only on ftrace_ops
 * registered with FTRACE_OPS_FL_CONTROL. If called without preemption
 * disabled, this_cpu_ptr will complain when CONFIG_DEBUG_PREEMPT is enabled.
 */
static inline int ftrace_function_local_disabled(struct ftrace_ops *ops)
{
	WARN_ON_ONCE(!(ops->flags & FTRACE_OPS_FL_CONTROL));
	return *this_cpu_ptr(ops->disabled);
}

extern void ftrace_stub(unsigned long a0, unsigned long a1);
extern void ftrace_stub(unsigned long a0, unsigned long a1);


#else /* !CONFIG_FUNCTION_TRACER */
#else /* !CONFIG_FUNCTION_TRACER */
@@ -184,6 +250,7 @@ int ftrace_set_notrace(struct ftrace_ops *ops, unsigned char *buf,
			int len, int reset);
			int len, int reset);
void ftrace_set_global_filter(unsigned char *buf, int len, int reset);
void ftrace_set_global_filter(unsigned char *buf, int len, int reset);
void ftrace_set_global_notrace(unsigned char *buf, int len, int reset);
void ftrace_set_global_notrace(unsigned char *buf, int len, int reset);
void ftrace_free_filter(struct ftrace_ops *ops);


int register_ftrace_command(struct ftrace_func_command *cmd);
int register_ftrace_command(struct ftrace_func_command *cmd);
int unregister_ftrace_command(struct ftrace_func_command *cmd);
int unregister_ftrace_command(struct ftrace_func_command *cmd);
@@ -314,9 +381,6 @@ extern void ftrace_enable_daemon(void);
#else
#else
static inline int skip_trace(unsigned long ip) { return 0; }
static inline int skip_trace(unsigned long ip) { return 0; }
static inline int ftrace_force_update(void) { return 0; }
static inline int ftrace_force_update(void) { return 0; }
static inline void ftrace_set_filter(unsigned char *buf, int len, int reset)
{
}
static inline void ftrace_disable_daemon(void) { }
static inline void ftrace_disable_daemon(void) { }
static inline void ftrace_enable_daemon(void) { }
static inline void ftrace_enable_daemon(void) { }
static inline void ftrace_release_mod(struct module *mod) {}
static inline void ftrace_release_mod(struct module *mod) {}
@@ -340,6 +404,9 @@ static inline int ftrace_text_reserved(void *start, void *end)
 */
 */
#define ftrace_regex_open(ops, flag, inod, file) ({ -ENODEV; })
#define ftrace_regex_open(ops, flag, inod, file) ({ -ENODEV; })
#define ftrace_set_early_filter(ops, buf, enable) do { } while (0)
#define ftrace_set_early_filter(ops, buf, enable) do { } while (0)
#define ftrace_set_filter(ops, buf, len, reset) ({ -ENODEV; })
#define ftrace_set_notrace(ops, buf, len, reset) ({ -ENODEV; })
#define ftrace_free_filter(ops) do { } while (0)


static inline ssize_t ftrace_filter_write(struct file *file, const char __user *ubuf,
static inline ssize_t ftrace_filter_write(struct file *file, const char __user *ubuf,
			    size_t cnt, loff_t *ppos) { return -ENODEV; }
			    size_t cnt, loff_t *ppos) { return -ENODEV; }
+7 −2
Original line number Original line Diff line number Diff line
@@ -146,6 +146,10 @@ enum trace_reg {
	TRACE_REG_UNREGISTER,
	TRACE_REG_UNREGISTER,
	TRACE_REG_PERF_REGISTER,
	TRACE_REG_PERF_REGISTER,
	TRACE_REG_PERF_UNREGISTER,
	TRACE_REG_PERF_UNREGISTER,
	TRACE_REG_PERF_OPEN,
	TRACE_REG_PERF_CLOSE,
	TRACE_REG_PERF_ADD,
	TRACE_REG_PERF_DEL,
};
};


struct ftrace_event_call;
struct ftrace_event_call;
@@ -157,7 +161,7 @@ struct ftrace_event_class {
	void			*perf_probe;
	void			*perf_probe;
#endif
#endif
	int			(*reg)(struct ftrace_event_call *event,
	int			(*reg)(struct ftrace_event_call *event,
				       enum trace_reg type);
				       enum trace_reg type, void *data);
	int			(*define_fields)(struct ftrace_event_call *);
	int			(*define_fields)(struct ftrace_event_call *);
	struct list_head	*(*get_fields)(struct ftrace_event_call *);
	struct list_head	*(*get_fields)(struct ftrace_event_call *);
	struct list_head	fields;
	struct list_head	fields;
@@ -165,7 +169,7 @@ struct ftrace_event_class {
};
};


extern int ftrace_event_reg(struct ftrace_event_call *event,
extern int ftrace_event_reg(struct ftrace_event_call *event,
			    enum trace_reg type);
			    enum trace_reg type, void *data);


enum {
enum {
	TRACE_EVENT_FL_ENABLED_BIT,
	TRACE_EVENT_FL_ENABLED_BIT,
@@ -241,6 +245,7 @@ enum {
	FILTER_STATIC_STRING,
	FILTER_STATIC_STRING,
	FILTER_DYN_STRING,
	FILTER_DYN_STRING,
	FILTER_PTR_STRING,
	FILTER_PTR_STRING,
	FILTER_TRACE_FN,
};
};


#define EVENT_STORAGE_SIZE 128
#define EVENT_STORAGE_SIZE 128
+3 −0
Original line number Original line Diff line number Diff line
@@ -859,6 +859,9 @@ struct perf_event {
#ifdef CONFIG_EVENT_TRACING
#ifdef CONFIG_EVENT_TRACING
	struct ftrace_event_call	*tp_event;
	struct ftrace_event_call	*tp_event;
	struct event_filter		*filter;
	struct event_filter		*filter;
#ifdef CONFIG_FUNCTION_TRACER
	struct ftrace_ops               ftrace_ops;
#endif
#endif
#endif


#ifdef CONFIG_CGROUP_PERF
#ifdef CONFIG_CGROUP_PERF
+110 −7
Original line number Original line Diff line number Diff line
@@ -62,6 +62,8 @@
#define FTRACE_HASH_DEFAULT_BITS 10
#define FTRACE_HASH_DEFAULT_BITS 10
#define FTRACE_HASH_MAX_BITS 12
#define FTRACE_HASH_MAX_BITS 12


#define FL_GLOBAL_CONTROL_MASK (FTRACE_OPS_FL_GLOBAL | FTRACE_OPS_FL_CONTROL)

/* ftrace_enabled is a method to turn ftrace on or off */
/* ftrace_enabled is a method to turn ftrace on or off */
int ftrace_enabled __read_mostly;
int ftrace_enabled __read_mostly;
static int last_ftrace_enabled;
static int last_ftrace_enabled;
@@ -89,12 +91,14 @@ static struct ftrace_ops ftrace_list_end __read_mostly = {
};
};


static struct ftrace_ops *ftrace_global_list __read_mostly = &ftrace_list_end;
static struct ftrace_ops *ftrace_global_list __read_mostly = &ftrace_list_end;
static struct ftrace_ops *ftrace_control_list __read_mostly = &ftrace_list_end;
static struct ftrace_ops *ftrace_ops_list __read_mostly = &ftrace_list_end;
static struct ftrace_ops *ftrace_ops_list __read_mostly = &ftrace_list_end;
ftrace_func_t ftrace_trace_function __read_mostly = ftrace_stub;
ftrace_func_t ftrace_trace_function __read_mostly = ftrace_stub;
static ftrace_func_t __ftrace_trace_function_delay __read_mostly = ftrace_stub;
static ftrace_func_t __ftrace_trace_function_delay __read_mostly = ftrace_stub;
ftrace_func_t __ftrace_trace_function __read_mostly = ftrace_stub;
ftrace_func_t __ftrace_trace_function __read_mostly = ftrace_stub;
ftrace_func_t ftrace_pid_function __read_mostly = ftrace_stub;
ftrace_func_t ftrace_pid_function __read_mostly = ftrace_stub;
static struct ftrace_ops global_ops;
static struct ftrace_ops global_ops;
static struct ftrace_ops control_ops;


static void
static void
ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip);
ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip);
@@ -168,6 +172,32 @@ static void ftrace_test_stop_func(unsigned long ip, unsigned long parent_ip)
}
}
#endif
#endif


static void control_ops_disable_all(struct ftrace_ops *ops)
{
	int cpu;

	for_each_possible_cpu(cpu)
		*per_cpu_ptr(ops->disabled, cpu) = 1;
}

static int control_ops_alloc(struct ftrace_ops *ops)
{
	int __percpu *disabled;

	disabled = alloc_percpu(int);
	if (!disabled)
		return -ENOMEM;

	ops->disabled = disabled;
	control_ops_disable_all(ops);
	return 0;
}

static void control_ops_free(struct ftrace_ops *ops)
{
	free_percpu(ops->disabled);
}

static void update_global_ops(void)
static void update_global_ops(void)
{
{
	ftrace_func_t func;
	ftrace_func_t func;
@@ -259,6 +289,26 @@ static int remove_ftrace_ops(struct ftrace_ops **list, struct ftrace_ops *ops)
	return 0;
	return 0;
}
}


static void add_ftrace_list_ops(struct ftrace_ops **list,
				struct ftrace_ops *main_ops,
				struct ftrace_ops *ops)
{
	int first = *list == &ftrace_list_end;
	add_ftrace_ops(list, ops);
	if (first)
		add_ftrace_ops(&ftrace_ops_list, main_ops);
}

static int remove_ftrace_list_ops(struct ftrace_ops **list,
				  struct ftrace_ops *main_ops,
				  struct ftrace_ops *ops)
{
	int ret = remove_ftrace_ops(list, ops);
	if (!ret && *list == &ftrace_list_end)
		ret = remove_ftrace_ops(&ftrace_ops_list, main_ops);
	return ret;
}

static int __register_ftrace_function(struct ftrace_ops *ops)
static int __register_ftrace_function(struct ftrace_ops *ops)
{
{
	if (ftrace_disabled)
	if (ftrace_disabled)
@@ -270,15 +320,20 @@ static int __register_ftrace_function(struct ftrace_ops *ops)
	if (WARN_ON(ops->flags & FTRACE_OPS_FL_ENABLED))
	if (WARN_ON(ops->flags & FTRACE_OPS_FL_ENABLED))
		return -EBUSY;
		return -EBUSY;


	/* We don't support both control and global flags set. */
	if ((ops->flags & FL_GLOBAL_CONTROL_MASK) == FL_GLOBAL_CONTROL_MASK)
		return -EINVAL;

	if (!core_kernel_data((unsigned long)ops))
	if (!core_kernel_data((unsigned long)ops))
		ops->flags |= FTRACE_OPS_FL_DYNAMIC;
		ops->flags |= FTRACE_OPS_FL_DYNAMIC;


	if (ops->flags & FTRACE_OPS_FL_GLOBAL) {
	if (ops->flags & FTRACE_OPS_FL_GLOBAL) {
		int first = ftrace_global_list == &ftrace_list_end;
		add_ftrace_list_ops(&ftrace_global_list, &global_ops, ops);
		add_ftrace_ops(&ftrace_global_list, ops);
		ops->flags |= FTRACE_OPS_FL_ENABLED;
		ops->flags |= FTRACE_OPS_FL_ENABLED;
		if (first)
	} else if (ops->flags & FTRACE_OPS_FL_CONTROL) {
			add_ftrace_ops(&ftrace_ops_list, &global_ops);
		if (control_ops_alloc(ops))
			return -ENOMEM;
		add_ftrace_list_ops(&ftrace_control_list, &control_ops, ops);
	} else
	} else
		add_ftrace_ops(&ftrace_ops_list, ops);
		add_ftrace_ops(&ftrace_ops_list, ops);


@@ -302,11 +357,23 @@ static int __unregister_ftrace_function(struct ftrace_ops *ops)
		return -EINVAL;
		return -EINVAL;


	if (ops->flags & FTRACE_OPS_FL_GLOBAL) {
	if (ops->flags & FTRACE_OPS_FL_GLOBAL) {
		ret = remove_ftrace_ops(&ftrace_global_list, ops);
		ret = remove_ftrace_list_ops(&ftrace_global_list,
		if (!ret && ftrace_global_list == &ftrace_list_end)
					     &global_ops, ops);
			ret = remove_ftrace_ops(&ftrace_ops_list, &global_ops);
		if (!ret)
		if (!ret)
			ops->flags &= ~FTRACE_OPS_FL_ENABLED;
			ops->flags &= ~FTRACE_OPS_FL_ENABLED;
	} else if (ops->flags & FTRACE_OPS_FL_CONTROL) {
		ret = remove_ftrace_list_ops(&ftrace_control_list,
					     &control_ops, ops);
		if (!ret) {
			/*
			 * The ftrace_ops is now removed from the list,
			 * so there'll be no new users. We must ensure
			 * all current users are done before we free
			 * the control data.
			 */
			synchronize_sched();
			control_ops_free(ops);
		}
	} else
	} else
		ret = remove_ftrace_ops(&ftrace_ops_list, ops);
		ret = remove_ftrace_ops(&ftrace_ops_list, ops);


@@ -1119,6 +1186,12 @@ static void free_ftrace_hash_rcu(struct ftrace_hash *hash)
	call_rcu_sched(&hash->rcu, __free_ftrace_hash_rcu);
	call_rcu_sched(&hash->rcu, __free_ftrace_hash_rcu);
}
}


void ftrace_free_filter(struct ftrace_ops *ops)
{
	free_ftrace_hash(ops->filter_hash);
	free_ftrace_hash(ops->notrace_hash);
}

static struct ftrace_hash *alloc_ftrace_hash(int size_bits)
static struct ftrace_hash *alloc_ftrace_hash(int size_bits)
{
{
	struct ftrace_hash *hash;
	struct ftrace_hash *hash;
@@ -3873,6 +3946,36 @@ ftrace_ops_test(struct ftrace_ops *ops, unsigned long ip)


#endif /* CONFIG_DYNAMIC_FTRACE */
#endif /* CONFIG_DYNAMIC_FTRACE */


static void
ftrace_ops_control_func(unsigned long ip, unsigned long parent_ip)
{
	struct ftrace_ops *op;

	if (unlikely(trace_recursion_test(TRACE_CONTROL_BIT)))
		return;

	/*
	 * Some of the ops may be dynamically allocated,
	 * they must be freed after a synchronize_sched().
	 */
	preempt_disable_notrace();
	trace_recursion_set(TRACE_CONTROL_BIT);
	op = rcu_dereference_raw(ftrace_control_list);
	while (op != &ftrace_list_end) {
		if (!ftrace_function_local_disabled(op) &&
		    ftrace_ops_test(op, ip))
			op->func(ip, parent_ip);

		op = rcu_dereference_raw(op->next);
	};
	trace_recursion_clear(TRACE_CONTROL_BIT);
	preempt_enable_notrace();
}

static struct ftrace_ops control_ops = {
	.func = ftrace_ops_control_func,
};

static void
static void
ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip)
ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip)
{
{
+28 −10
Original line number Original line Diff line number Diff line
@@ -56,7 +56,7 @@ enum trace_type {
#define F_STRUCT(args...)		args
#define F_STRUCT(args...)		args


#undef FTRACE_ENTRY
#undef FTRACE_ENTRY
#define FTRACE_ENTRY(name, struct_name, id, tstruct, print)	\
#define FTRACE_ENTRY(name, struct_name, id, tstruct, print, filter)	\
	struct struct_name {						\
	struct struct_name {						\
		struct trace_entry	ent;				\
		struct trace_entry	ent;				\
		tstruct							\
		tstruct							\
@@ -66,7 +66,13 @@ enum trace_type {
#define TP_ARGS(args...)	args
#define TP_ARGS(args...)	args


#undef FTRACE_ENTRY_DUP
#undef FTRACE_ENTRY_DUP
#define FTRACE_ENTRY_DUP(name, name_struct, id, tstruct, printk)
#define FTRACE_ENTRY_DUP(name, name_struct, id, tstruct, printk, filter)

#undef FTRACE_ENTRY_REG
#define FTRACE_ENTRY_REG(name, struct_name, id, tstruct, print,	\
			 filter, regfn) \
	FTRACE_ENTRY(name, struct_name, id, PARAMS(tstruct), PARAMS(print), \
		     filter)


#include "trace_entries.h"
#include "trace_entries.h"


@@ -288,6 +294,8 @@ struct tracer {
/* for function tracing recursion */
/* for function tracing recursion */
#define TRACE_INTERNAL_BIT		(1<<11)
#define TRACE_INTERNAL_BIT		(1<<11)
#define TRACE_GLOBAL_BIT		(1<<12)
#define TRACE_GLOBAL_BIT		(1<<12)
#define TRACE_CONTROL_BIT		(1<<13)

/*
/*
 * Abuse of the trace_recursion.
 * Abuse of the trace_recursion.
 * As we need a way to maintain state if we are tracing the function
 * As we need a way to maintain state if we are tracing the function
@@ -589,6 +597,8 @@ static inline int ftrace_trace_task(struct task_struct *task)
static inline int ftrace_is_dead(void) { return 0; }
static inline int ftrace_is_dead(void) { return 0; }
#endif
#endif


int ftrace_event_is_function(struct ftrace_event_call *call);

/*
/*
 * struct trace_parser - servers for reading the user input separated by spaces
 * struct trace_parser - servers for reading the user input separated by spaces
 * @cont: set if the input is not complete - no final space char was found
 * @cont: set if the input is not complete - no final space char was found
@@ -766,9 +776,7 @@ struct filter_pred {
	u64 			val;
	u64 			val;
	struct regex		regex;
	struct regex		regex;
	unsigned short		*ops;
	unsigned short		*ops;
#ifdef CONFIG_FTRACE_STARTUP_TEST
	struct ftrace_event_field *field;
	struct ftrace_event_field *field;
#endif
	int 			offset;
	int 			offset;
	int 			not;
	int 			not;
	int 			op;
	int 			op;
@@ -818,12 +826,22 @@ extern const char *__start___trace_bprintk_fmt[];
extern const char *__stop___trace_bprintk_fmt[];
extern const char *__stop___trace_bprintk_fmt[];


#undef FTRACE_ENTRY
#undef FTRACE_ENTRY
#define FTRACE_ENTRY(call, struct_name, id, tstruct, print)		\
#define FTRACE_ENTRY(call, struct_name, id, tstruct, print, filter)	\
	extern struct ftrace_event_call					\
	extern struct ftrace_event_call					\
	__attribute__((__aligned__(4))) event_##call;
	__attribute__((__aligned__(4))) event_##call;
#undef FTRACE_ENTRY_DUP
#undef FTRACE_ENTRY_DUP
#define FTRACE_ENTRY_DUP(call, struct_name, id, tstruct, print)		\
#define FTRACE_ENTRY_DUP(call, struct_name, id, tstruct, print, filter)	\
	FTRACE_ENTRY(call, struct_name, id, PARAMS(tstruct), PARAMS(print))
	FTRACE_ENTRY(call, struct_name, id, PARAMS(tstruct), PARAMS(print), \
		     filter)
#include "trace_entries.h"
#include "trace_entries.h"


#ifdef CONFIG_PERF_EVENTS
#ifdef CONFIG_FUNCTION_TRACER
int perf_ftrace_event_register(struct ftrace_event_call *call,
			       enum trace_reg type, void *data);
#else
#define perf_ftrace_event_register NULL
#endif /* CONFIG_FUNCTION_TRACER */
#endif /* CONFIG_PERF_EVENTS */

#endif /* _LINUX_KERNEL_TRACE_H */
#endif /* _LINUX_KERNEL_TRACE_H */
Loading