diff options
Diffstat (limited to 'kernel/trace/trace_kprobe.c')
| -rw-r--r-- | kernel/trace/trace_kprobe.c | 1721 | 
1 files changed, 685 insertions, 1036 deletions
diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c index 2dec9bcde8b..282f6e4e553 100644 --- a/kernel/trace/trace_kprobe.c +++ b/kernel/trace/trace_kprobe.c @@ -19,186 +19,134 @@  #include <linux/module.h>  #include <linux/uaccess.h> -#include <linux/kprobes.h> -#include <linux/seq_file.h> -#include <linux/slab.h> -#include <linux/smp.h> -#include <linux/debugfs.h> -#include <linux/types.h> -#include <linux/string.h> -#include <linux/ctype.h> -#include <linux/ptrace.h> -#include <linux/perf_event.h> -#include <linux/stringify.h> -#include <linux/limits.h> -#include <asm/bitsperlong.h> - -#include "trace.h" -#include "trace_output.h" - -#define MAX_TRACE_ARGS 128 -#define MAX_ARGSTR_LEN 63 -#define MAX_EVENT_NAME_LEN 64 -#define MAX_STRING_SIZE PATH_MAX + +#include "trace_probe.h" +  #define KPROBE_EVENT_SYSTEM "kprobes" -/* Reserved field names */ -#define FIELD_STRING_IP "__probe_ip" -#define FIELD_STRING_RETIP "__probe_ret_ip" -#define FIELD_STRING_FUNC "__probe_func" - -const char *reserved_field_names[] = { -	"common_type", -	"common_flags", -	"common_preempt_count", -	"common_pid", -	"common_tgid", -	"common_lock_depth", -	FIELD_STRING_IP, -	FIELD_STRING_RETIP, -	FIELD_STRING_FUNC, +/** + * Kprobe event core functions + */ +struct trace_kprobe { +	struct list_head	list; +	struct kretprobe	rp;	/* Use rp.kp for kprobe use */ +	unsigned long 		nhit; +	const char		*symbol;	/* symbol name */ +	struct trace_probe	tp;  }; -/* Printing function type */ -typedef int (*print_type_func_t)(struct trace_seq *, const char *, void *, -				 void *); -#define PRINT_TYPE_FUNC_NAME(type)	print_type_##type -#define PRINT_TYPE_FMT_NAME(type)	print_type_format_##type - -/* Printing  in basic type function template */ -#define DEFINE_BASIC_PRINT_TYPE_FUNC(type, fmt, cast)			\ -static __kprobes int PRINT_TYPE_FUNC_NAME(type)(struct trace_seq *s,	\ -						const char *name,	\ -						void *data, void *ent)\ -{									\ -	return trace_seq_printf(s, " %s=" fmt, name, (cast)*(type *)data);\ -}									\ -static const char PRINT_TYPE_FMT_NAME(type)[] = fmt; - -DEFINE_BASIC_PRINT_TYPE_FUNC(u8, "%x", unsigned int) -DEFINE_BASIC_PRINT_TYPE_FUNC(u16, "%x", unsigned int) -DEFINE_BASIC_PRINT_TYPE_FUNC(u32, "%lx", unsigned long) -DEFINE_BASIC_PRINT_TYPE_FUNC(u64, "%llx", unsigned long long) -DEFINE_BASIC_PRINT_TYPE_FUNC(s8, "%d", int) -DEFINE_BASIC_PRINT_TYPE_FUNC(s16, "%d", int) -DEFINE_BASIC_PRINT_TYPE_FUNC(s32, "%ld", long) -DEFINE_BASIC_PRINT_TYPE_FUNC(s64, "%lld", long long) - -/* data_rloc: data relative location, compatible with u32 */ -#define make_data_rloc(len, roffs)	\ -	(((u32)(len) << 16) | ((u32)(roffs) & 0xffff)) -#define get_rloc_len(dl)	((u32)(dl) >> 16) -#define get_rloc_offs(dl)	((u32)(dl) & 0xffff) - -static inline void *get_rloc_data(u32 *dl) +#define SIZEOF_TRACE_KPROBE(n)				\ +	(offsetof(struct trace_kprobe, tp.args) +	\ +	(sizeof(struct probe_arg) * (n))) + + +static nokprobe_inline bool trace_kprobe_is_return(struct trace_kprobe *tk)  { -	return (u8 *)dl + get_rloc_offs(*dl); +	return tk->rp.handler != NULL;  } -/* For data_loc conversion */ -static inline void *get_loc_data(u32 *dl, void *ent) +static nokprobe_inline const char *trace_kprobe_symbol(struct trace_kprobe *tk)  { -	return (u8 *)ent + get_rloc_offs(*dl); +	return tk->symbol ? tk->symbol : "unknown";  } -/* - * Convert data_rloc to data_loc: - *  data_rloc stores the offset from data_rloc itself, but data_loc - *  stores the offset from event entry. - */ -#define convert_rloc_to_loc(dl, offs)	((u32)(dl) + (offs)) +static nokprobe_inline unsigned long trace_kprobe_offset(struct trace_kprobe *tk) +{ +	return tk->rp.kp.offset; +} -/* For defining macros, define string/string_size types */ -typedef u32 string; -typedef u32 string_size; +static nokprobe_inline bool trace_kprobe_has_gone(struct trace_kprobe *tk) +{ +	return !!(kprobe_gone(&tk->rp.kp)); +} -/* Print type function for string type */ -static __kprobes int PRINT_TYPE_FUNC_NAME(string)(struct trace_seq *s, -						  const char *name, -						  void *data, void *ent) +static nokprobe_inline bool trace_kprobe_within_module(struct trace_kprobe *tk, +						 struct module *mod)  { -	int len = *(u32 *)data >> 16; +	int len = strlen(mod->name); +	const char *name = trace_kprobe_symbol(tk); +	return strncmp(mod->name, name, len) == 0 && name[len] == ':'; +} -	if (!len) -		return trace_seq_printf(s, " %s=(fault)", name); -	else -		return trace_seq_printf(s, " %s=\"%s\"", name, -					(const char *)get_loc_data(data, ent)); +static nokprobe_inline bool trace_kprobe_is_on_module(struct trace_kprobe *tk) +{ +	return !!strchr(trace_kprobe_symbol(tk), ':');  } -static const char PRINT_TYPE_FMT_NAME(string)[] = "\\\"%s\\\""; -/* Data fetch function type */ -typedef	void (*fetch_func_t)(struct pt_regs *, void *, void *); +static int register_kprobe_event(struct trace_kprobe *tk); +static int unregister_kprobe_event(struct trace_kprobe *tk); -struct fetch_param { -	fetch_func_t	fn; -	void *data; +static DEFINE_MUTEX(probe_lock); +static LIST_HEAD(probe_list); + +static int kprobe_dispatcher(struct kprobe *kp, struct pt_regs *regs); +static int kretprobe_dispatcher(struct kretprobe_instance *ri, +				struct pt_regs *regs); + +/* Memory fetching by symbol */ +struct symbol_cache { +	char		*symbol; +	long		offset; +	unsigned long	addr;  }; -static __kprobes void call_fetch(struct fetch_param *fprm, -				 struct pt_regs *regs, void *dest) +unsigned long update_symbol_cache(struct symbol_cache *sc)  { -	return fprm->fn(regs, fprm->data, dest); +	sc->addr = (unsigned long)kallsyms_lookup_name(sc->symbol); + +	if (sc->addr) +		sc->addr += sc->offset; + +	return sc->addr;  } -#define FETCH_FUNC_NAME(method, type)	fetch_##method##_##type -/* - * Define macro for basic types - we don't need to define s* types, because - * we have to care only about bitwidth at recording time. - */ -#define DEFINE_BASIC_FETCH_FUNCS(method) \ -DEFINE_FETCH_##method(u8)		\ -DEFINE_FETCH_##method(u16)		\ -DEFINE_FETCH_##method(u32)		\ -DEFINE_FETCH_##method(u64) - -#define CHECK_FETCH_FUNCS(method, fn)			\ -	(((FETCH_FUNC_NAME(method, u8) == fn) ||	\ -	  (FETCH_FUNC_NAME(method, u16) == fn) ||	\ -	  (FETCH_FUNC_NAME(method, u32) == fn) ||	\ -	  (FETCH_FUNC_NAME(method, u64) == fn) ||	\ -	  (FETCH_FUNC_NAME(method, string) == fn) ||	\ -	  (FETCH_FUNC_NAME(method, string_size) == fn)) \ -	 && (fn != NULL)) - -/* Data fetch function templates */ -#define DEFINE_FETCH_reg(type)						\ -static __kprobes void FETCH_FUNC_NAME(reg, type)(struct pt_regs *regs,	\ -					void *offset, void *dest)	\ -{									\ -	*(type *)dest = (type)regs_get_register(regs,			\ -				(unsigned int)((unsigned long)offset));	\ +void free_symbol_cache(struct symbol_cache *sc) +{ +	kfree(sc->symbol); +	kfree(sc);  } -DEFINE_BASIC_FETCH_FUNCS(reg) -/* No string on the register */ -#define fetch_reg_string NULL -#define fetch_reg_string_size NULL +struct symbol_cache *alloc_symbol_cache(const char *sym, long offset) +{ +	struct symbol_cache *sc; + +	if (!sym || strlen(sym) == 0) +		return NULL; + +	sc = kzalloc(sizeof(struct symbol_cache), GFP_KERNEL); +	if (!sc) +		return NULL; + +	sc->symbol = kstrdup(sym, GFP_KERNEL); +	if (!sc->symbol) { +		kfree(sc); +		return NULL; +	} +	sc->offset = offset; +	update_symbol_cache(sc); + +	return sc; +} + +/* + * Kprobes-specific fetch functions + */  #define DEFINE_FETCH_stack(type)					\ -static __kprobes void FETCH_FUNC_NAME(stack, type)(struct pt_regs *regs,\ +static void FETCH_FUNC_NAME(stack, type)(struct pt_regs *regs,		\  					  void *offset, void *dest)	\  {									\  	*(type *)dest = (type)regs_get_kernel_stack_nth(regs,		\  				(unsigned int)((unsigned long)offset));	\ -} +}									\ +NOKPROBE_SYMBOL(FETCH_FUNC_NAME(stack, type)); +  DEFINE_BASIC_FETCH_FUNCS(stack)  /* No string on the stack entry */ -#define fetch_stack_string NULL -#define fetch_stack_string_size NULL - -#define DEFINE_FETCH_retval(type)					\ -static __kprobes void FETCH_FUNC_NAME(retval, type)(struct pt_regs *regs,\ -					  void *dummy, void *dest)	\ -{									\ -	*(type *)dest = (type)regs_return_value(regs);			\ -} -DEFINE_BASIC_FETCH_FUNCS(retval) -/* No string on the retval */ -#define fetch_retval_string NULL -#define fetch_retval_string_size NULL +#define fetch_stack_string	NULL +#define fetch_stack_string_size	NULL  #define DEFINE_FETCH_memory(type)					\ -static __kprobes void FETCH_FUNC_NAME(memory, type)(struct pt_regs *regs,\ +static void FETCH_FUNC_NAME(memory, type)(struct pt_regs *regs,		\  					  void *addr, void *dest)	\  {									\  	type retval;							\ @@ -206,31 +154,37 @@ static __kprobes void FETCH_FUNC_NAME(memory, type)(struct pt_regs *regs,\  		*(type *)dest = 0;					\  	else								\  		*(type *)dest = retval;					\ -} +}									\ +NOKPROBE_SYMBOL(FETCH_FUNC_NAME(memory, type)); +  DEFINE_BASIC_FETCH_FUNCS(memory)  /*   * Fetch a null-terminated string. Caller MUST set *(u32 *)dest with max   * length and relative data location.   */ -static __kprobes void FETCH_FUNC_NAME(memory, string)(struct pt_regs *regs, -						      void *addr, void *dest) +static void FETCH_FUNC_NAME(memory, string)(struct pt_regs *regs, +					    void *addr, void *dest)  {  	long ret;  	int maxlen = get_rloc_len(*(u32 *)dest);  	u8 *dst = get_rloc_data(dest);  	u8 *src = addr;  	mm_segment_t old_fs = get_fs(); +  	if (!maxlen)  		return; +  	/*  	 * Try to get string again, since the string can be changed while  	 * probing.  	 */  	set_fs(KERNEL_DS);  	pagefault_disable(); +  	do  		ret = __copy_from_user_inatomic(dst++, src++, 1);  	while (dst[-1] && ret == 0 && src - (u8 *)addr < maxlen); +  	dst[-1] = '\0';  	pagefault_enable();  	set_fs(old_fs); @@ -238,24 +192,30 @@ static __kprobes void FETCH_FUNC_NAME(memory, string)(struct pt_regs *regs,  	if (ret < 0) {	/* Failed to fetch string */  		((u8 *)get_rloc_data(dest))[0] = '\0';  		*(u32 *)dest = make_data_rloc(0, get_rloc_offs(*(u32 *)dest)); -	} else +	} else {  		*(u32 *)dest = make_data_rloc(src - (u8 *)addr,  					      get_rloc_offs(*(u32 *)dest)); +	}  } +NOKPROBE_SYMBOL(FETCH_FUNC_NAME(memory, string)); +  /* Return the length of string -- including null terminal byte */ -static __kprobes void FETCH_FUNC_NAME(memory, string_size)(struct pt_regs *regs, -							void *addr, void *dest) +static void FETCH_FUNC_NAME(memory, string_size)(struct pt_regs *regs, +						 void *addr, void *dest)  { +	mm_segment_t old_fs;  	int ret, len = 0;  	u8 c; -	mm_segment_t old_fs = get_fs(); +	old_fs = get_fs();  	set_fs(KERNEL_DS);  	pagefault_disable(); +  	do {  		ret = __copy_from_user_inatomic(&c, (u8 *)addr + len, 1);  		len++;  	} while (c && ret == 0 && len < MAX_STRING_SIZE); +  	pagefault_enable();  	set_fs(old_fs); @@ -264,149 +224,33 @@ static __kprobes void FETCH_FUNC_NAME(memory, string_size)(struct pt_regs *regs,  	else  		*(u32 *)dest = len;  } - -/* Memory fetching by symbol */ -struct symbol_cache { -	char *symbol; -	long offset; -	unsigned long addr; -}; - -static unsigned long update_symbol_cache(struct symbol_cache *sc) -{ -	sc->addr = (unsigned long)kallsyms_lookup_name(sc->symbol); -	if (sc->addr) -		sc->addr += sc->offset; -	return sc->addr; -} - -static void free_symbol_cache(struct symbol_cache *sc) -{ -	kfree(sc->symbol); -	kfree(sc); -} - -static struct symbol_cache *alloc_symbol_cache(const char *sym, long offset) -{ -	struct symbol_cache *sc; - -	if (!sym || strlen(sym) == 0) -		return NULL; -	sc = kzalloc(sizeof(struct symbol_cache), GFP_KERNEL); -	if (!sc) -		return NULL; - -	sc->symbol = kstrdup(sym, GFP_KERNEL); -	if (!sc->symbol) { -		kfree(sc); -		return NULL; -	} -	sc->offset = offset; - -	update_symbol_cache(sc); -	return sc; -} +NOKPROBE_SYMBOL(FETCH_FUNC_NAME(memory, string_size));  #define DEFINE_FETCH_symbol(type)					\ -static __kprobes void FETCH_FUNC_NAME(symbol, type)(struct pt_regs *regs,\ -					  void *data, void *dest)	\ +void FETCH_FUNC_NAME(symbol, type)(struct pt_regs *regs, void *data, void *dest)\  {									\  	struct symbol_cache *sc = data;					\  	if (sc->addr)							\  		fetch_memory_##type(regs, (void *)sc->addr, dest);	\  	else								\  		*(type *)dest = 0;					\ -} +}									\ +NOKPROBE_SYMBOL(FETCH_FUNC_NAME(symbol, type)); +  DEFINE_BASIC_FETCH_FUNCS(symbol)  DEFINE_FETCH_symbol(string)  DEFINE_FETCH_symbol(string_size) -/* Dereference memory access function */ -struct deref_fetch_param { -	struct fetch_param orig; -	long offset; -}; - -#define DEFINE_FETCH_deref(type)					\ -static __kprobes void FETCH_FUNC_NAME(deref, type)(struct pt_regs *regs,\ -					    void *data, void *dest)	\ -{									\ -	struct deref_fetch_param *dprm = data;				\ -	unsigned long addr;						\ -	call_fetch(&dprm->orig, regs, &addr);				\ -	if (addr) {							\ -		addr += dprm->offset;					\ -		fetch_memory_##type(regs, (void *)addr, dest);		\ -	} else								\ -		*(type *)dest = 0;					\ -} -DEFINE_BASIC_FETCH_FUNCS(deref) -DEFINE_FETCH_deref(string) -DEFINE_FETCH_deref(string_size) - -static __kprobes void free_deref_fetch_param(struct deref_fetch_param *data) -{ -	if (CHECK_FETCH_FUNCS(deref, data->orig.fn)) -		free_deref_fetch_param(data->orig.data); -	else if (CHECK_FETCH_FUNCS(symbol, data->orig.fn)) -		free_symbol_cache(data->orig.data); -	kfree(data); -} - -/* Default (unsigned long) fetch type */ -#define __DEFAULT_FETCH_TYPE(t) u##t -#define _DEFAULT_FETCH_TYPE(t) __DEFAULT_FETCH_TYPE(t) -#define DEFAULT_FETCH_TYPE _DEFAULT_FETCH_TYPE(BITS_PER_LONG) -#define DEFAULT_FETCH_TYPE_STR __stringify(DEFAULT_FETCH_TYPE) - -/* Fetch types */ -enum { -	FETCH_MTD_reg = 0, -	FETCH_MTD_stack, -	FETCH_MTD_retval, -	FETCH_MTD_memory, -	FETCH_MTD_symbol, -	FETCH_MTD_deref, -	FETCH_MTD_END, -}; - -#define ASSIGN_FETCH_FUNC(method, type)	\ -	[FETCH_MTD_##method] = FETCH_FUNC_NAME(method, type) - -#define __ASSIGN_FETCH_TYPE(_name, ptype, ftype, _size, sign, _fmttype)	\ -	{.name = _name,				\ -	 .size = _size,					\ -	 .is_signed = sign,				\ -	 .print = PRINT_TYPE_FUNC_NAME(ptype),		\ -	 .fmt = PRINT_TYPE_FMT_NAME(ptype),		\ -	 .fmttype = _fmttype,				\ -	 .fetch = {					\ -ASSIGN_FETCH_FUNC(reg, ftype),				\ -ASSIGN_FETCH_FUNC(stack, ftype),			\ -ASSIGN_FETCH_FUNC(retval, ftype),			\ -ASSIGN_FETCH_FUNC(memory, ftype),			\ -ASSIGN_FETCH_FUNC(symbol, ftype),			\ -ASSIGN_FETCH_FUNC(deref, ftype),			\ -	  }						\ -	} - -#define ASSIGN_FETCH_TYPE(ptype, ftype, sign)			\ -	__ASSIGN_FETCH_TYPE(#ptype, ptype, ftype, sizeof(ftype), sign, #ptype) - -#define FETCH_TYPE_STRING 0 -#define FETCH_TYPE_STRSIZE 1 +/* kprobes don't support file_offset fetch methods */ +#define fetch_file_offset_u8		NULL +#define fetch_file_offset_u16		NULL +#define fetch_file_offset_u32		NULL +#define fetch_file_offset_u64		NULL +#define fetch_file_offset_string	NULL +#define fetch_file_offset_string_size	NULL  /* Fetch type information table */ -static const struct fetch_type { -	const char	*name;		/* Name of type */ -	size_t		size;		/* Byte size of type */ -	int		is_signed;	/* Signed flag */ -	print_type_func_t	print;	/* Print functions */ -	const char	*fmt;		/* Fromat string */ -	const char	*fmttype;	/* Name in format file */ -	/* Fetch functions */ -	fetch_func_t	fetch[FETCH_MTD_END]; -} fetch_type_table[] = { +const struct fetch_type kprobes_fetch_type_table[] = {  	/* Special types */  	[FETCH_TYPE_STRING] = __ASSIGN_FETCH_TYPE("string", string, string,  					sizeof(u32), 1, "__data_loc char[]"), @@ -421,149 +265,49 @@ static const struct fetch_type {  	ASSIGN_FETCH_TYPE(s16, u16, 1),  	ASSIGN_FETCH_TYPE(s32, u32, 1),  	ASSIGN_FETCH_TYPE(s64, u64, 1), -}; - -static const struct fetch_type *find_fetch_type(const char *type) -{ -	int i; -	if (!type) -		type = DEFAULT_FETCH_TYPE_STR; - -	for (i = 0; i < ARRAY_SIZE(fetch_type_table); i++) -		if (strcmp(type, fetch_type_table[i].name) == 0) -			return &fetch_type_table[i]; -	return NULL; -} - -/* Special function : only accept unsigned long */ -static __kprobes void fetch_stack_address(struct pt_regs *regs, -					  void *dummy, void *dest) -{ -	*(unsigned long *)dest = kernel_stack_pointer(regs); -} - -static fetch_func_t get_fetch_size_function(const struct fetch_type *type, -					    fetch_func_t orig_fn) -{ -	int i; - -	if (type != &fetch_type_table[FETCH_TYPE_STRING]) -		return NULL;	/* Only string type needs size function */ -	for (i = 0; i < FETCH_MTD_END; i++) -		if (type->fetch[i] == orig_fn) -			return fetch_type_table[FETCH_TYPE_STRSIZE].fetch[i]; - -	WARN_ON(1);	/* This should not happen */ -	return NULL; -} - -/** - * Kprobe event core functions - */ - -struct probe_arg { -	struct fetch_param	fetch; -	struct fetch_param	fetch_size; -	unsigned int		offset;	/* Offset from argument entry */ -	const char		*name;	/* Name of this argument */ -	const char		*comm;	/* Command of this argument */ -	const struct fetch_type	*type;	/* Type of this argument */ -}; - -/* Flags for trace_probe */ -#define TP_FLAG_TRACE	1 -#define TP_FLAG_PROFILE	2 - -struct trace_probe { -	struct list_head	list; -	struct kretprobe	rp;	/* Use rp.kp for kprobe use */ -	unsigned long 		nhit; -	unsigned int		flags;	/* For TP_FLAG_* */ -	const char		*symbol;	/* symbol name */ -	struct ftrace_event_class	class; -	struct ftrace_event_call	call; -	ssize_t			size;		/* trace entry size */ -	unsigned int		nr_args; -	struct probe_arg	args[]; +	ASSIGN_FETCH_TYPE_END  }; -#define SIZEOF_TRACE_PROBE(n)			\ -	(offsetof(struct trace_probe, args) +	\ -	(sizeof(struct probe_arg) * (n))) - - -static __kprobes int probe_is_return(struct trace_probe *tp) -{ -	return tp->rp.handler != NULL; -} - -static __kprobes const char *probe_symbol(struct trace_probe *tp) -{ -	return tp->symbol ? tp->symbol : "unknown"; -} - -static int register_probe_event(struct trace_probe *tp); -static void unregister_probe_event(struct trace_probe *tp); - -static DEFINE_MUTEX(probe_lock); -static LIST_HEAD(probe_list); - -static int kprobe_dispatcher(struct kprobe *kp, struct pt_regs *regs); -static int kretprobe_dispatcher(struct kretprobe_instance *ri, -				struct pt_regs *regs); - -/* Check the name is good for event/group/fields */ -static int is_good_name(const char *name) -{ -	if (!isalpha(*name) && *name != '_') -		return 0; -	while (*++name != '\0') { -		if (!isalpha(*name) && !isdigit(*name) && *name != '_') -			return 0; -	} -	return 1; -} -  /*   * Allocate new trace_probe and initialize it (including kprobes).   */ -static struct trace_probe *alloc_trace_probe(const char *group, +static struct trace_kprobe *alloc_trace_kprobe(const char *group,  					     const char *event,  					     void *addr,  					     const char *symbol,  					     unsigned long offs, -					     int nargs, int is_return) +					     int nargs, bool is_return)  { -	struct trace_probe *tp; +	struct trace_kprobe *tk;  	int ret = -ENOMEM; -	tp = kzalloc(SIZEOF_TRACE_PROBE(nargs), GFP_KERNEL); -	if (!tp) +	tk = kzalloc(SIZEOF_TRACE_KPROBE(nargs), GFP_KERNEL); +	if (!tk)  		return ERR_PTR(ret);  	if (symbol) { -		tp->symbol = kstrdup(symbol, GFP_KERNEL); -		if (!tp->symbol) +		tk->symbol = kstrdup(symbol, GFP_KERNEL); +		if (!tk->symbol)  			goto error; -		tp->rp.kp.symbol_name = tp->symbol; -		tp->rp.kp.offset = offs; +		tk->rp.kp.symbol_name = tk->symbol; +		tk->rp.kp.offset = offs;  	} else -		tp->rp.kp.addr = addr; +		tk->rp.kp.addr = addr;  	if (is_return) -		tp->rp.handler = kretprobe_dispatcher; +		tk->rp.handler = kretprobe_dispatcher;  	else -		tp->rp.kp.pre_handler = kprobe_dispatcher; +		tk->rp.kp.pre_handler = kprobe_dispatcher;  	if (!event || !is_good_name(event)) {  		ret = -EINVAL;  		goto error;  	} -	tp->call.class = &tp->class; -	tp->call.name = kstrdup(event, GFP_KERNEL); -	if (!tp->call.name) +	tk->tp.call.class = &tk->tp.class; +	tk->tp.call.name = kstrdup(event, GFP_KERNEL); +	if (!tk->tp.call.name)  		goto error;  	if (!group || !is_good_name(group)) { @@ -571,299 +315,283 @@ static struct trace_probe *alloc_trace_probe(const char *group,  		goto error;  	} -	tp->class.system = kstrdup(group, GFP_KERNEL); -	if (!tp->class.system) +	tk->tp.class.system = kstrdup(group, GFP_KERNEL); +	if (!tk->tp.class.system)  		goto error; -	INIT_LIST_HEAD(&tp->list); -	return tp; +	INIT_LIST_HEAD(&tk->list); +	INIT_LIST_HEAD(&tk->tp.files); +	return tk;  error: -	kfree(tp->call.name); -	kfree(tp->symbol); -	kfree(tp); +	kfree(tk->tp.call.name); +	kfree(tk->symbol); +	kfree(tk);  	return ERR_PTR(ret);  } -static void free_probe_arg(struct probe_arg *arg) -{ -	if (CHECK_FETCH_FUNCS(deref, arg->fetch.fn)) -		free_deref_fetch_param(arg->fetch.data); -	else if (CHECK_FETCH_FUNCS(symbol, arg->fetch.fn)) -		free_symbol_cache(arg->fetch.data); -	kfree(arg->name); -	kfree(arg->comm); -} - -static void free_trace_probe(struct trace_probe *tp) +static void free_trace_kprobe(struct trace_kprobe *tk)  {  	int i; -	for (i = 0; i < tp->nr_args; i++) -		free_probe_arg(&tp->args[i]); +	for (i = 0; i < tk->tp.nr_args; i++) +		traceprobe_free_probe_arg(&tk->tp.args[i]); -	kfree(tp->call.class->system); -	kfree(tp->call.name); -	kfree(tp->symbol); -	kfree(tp); +	kfree(tk->tp.call.class->system); +	kfree(tk->tp.call.name); +	kfree(tk->symbol); +	kfree(tk);  } -static struct trace_probe *find_probe_event(const char *event, -					    const char *group) +static struct trace_kprobe *find_trace_kprobe(const char *event, +					      const char *group)  { -	struct trace_probe *tp; +	struct trace_kprobe *tk; -	list_for_each_entry(tp, &probe_list, list) -		if (strcmp(tp->call.name, event) == 0 && -		    strcmp(tp->call.class->system, group) == 0) -			return tp; +	list_for_each_entry(tk, &probe_list, list) +		if (strcmp(ftrace_event_name(&tk->tp.call), event) == 0 && +		    strcmp(tk->tp.call.class->system, group) == 0) +			return tk;  	return NULL;  } -/* Unregister a trace_probe and probe_event: call with locking probe_lock */ -static void unregister_trace_probe(struct trace_probe *tp) +/* + * Enable trace_probe + * if the file is NULL, enable "perf" handler, or enable "trace" handler. + */ +static int +enable_trace_kprobe(struct trace_kprobe *tk, struct ftrace_event_file *file)  { -	if (probe_is_return(tp)) -		unregister_kretprobe(&tp->rp); -	else -		unregister_kprobe(&tp->rp.kp); -	list_del(&tp->list); -	unregister_probe_event(tp); -} +	int ret = 0; -/* Register a trace_probe and probe_event */ -static int register_trace_probe(struct trace_probe *tp) -{ -	struct trace_probe *old_tp; -	int ret; +	if (file) { +		struct event_file_link *link; -	mutex_lock(&probe_lock); +		link = kmalloc(sizeof(*link), GFP_KERNEL); +		if (!link) { +			ret = -ENOMEM; +			goto out; +		} -	/* register as an event */ -	old_tp = find_probe_event(tp->call.name, tp->call.class->system); -	if (old_tp) { -		/* delete old event */ -		unregister_trace_probe(old_tp); -		free_trace_probe(old_tp); -	} -	ret = register_probe_event(tp); -	if (ret) { -		pr_warning("Failed to register probe event(%d)\n", ret); -		goto end; +		link->file = file; +		list_add_tail_rcu(&link->list, &tk->tp.files); + +		tk->tp.flags |= TP_FLAG_TRACE; +	} else +		tk->tp.flags |= TP_FLAG_PROFILE; + +	if (trace_probe_is_registered(&tk->tp) && !trace_kprobe_has_gone(tk)) { +		if (trace_kprobe_is_return(tk)) +			ret = enable_kretprobe(&tk->rp); +		else +			ret = enable_kprobe(&tk->rp.kp);  	} + out: +	return ret; +} -	tp->rp.kp.flags |= KPROBE_FLAG_DISABLED; -	if (probe_is_return(tp)) -		ret = register_kretprobe(&tp->rp); -	else -		ret = register_kprobe(&tp->rp.kp); +/* + * Disable trace_probe + * if the file is NULL, disable "perf" handler, or disable "trace" handler. + */ +static int +disable_trace_kprobe(struct trace_kprobe *tk, struct ftrace_event_file *file) +{ +	struct event_file_link *link = NULL; +	int wait = 0; +	int ret = 0; -	if (ret) { -		pr_warning("Could not insert probe(%d)\n", ret); -		if (ret == -EILSEQ) { -			pr_warning("Probing address(0x%p) is not an " -				   "instruction boundary.\n", -				   tp->rp.kp.addr); +	if (file) { +		link = find_event_file_link(&tk->tp, file); +		if (!link) {  			ret = -EINVAL; +			goto out;  		} -		unregister_probe_event(tp); + +		list_del_rcu(&link->list); +		wait = 1; +		if (!list_empty(&tk->tp.files)) +			goto out; + +		tk->tp.flags &= ~TP_FLAG_TRACE;  	} else -		list_add_tail(&tp->list, &probe_list); -end: -	mutex_unlock(&probe_lock); +		tk->tp.flags &= ~TP_FLAG_PROFILE; + +	if (!trace_probe_is_enabled(&tk->tp) && trace_probe_is_registered(&tk->tp)) { +		if (trace_kprobe_is_return(tk)) +			disable_kretprobe(&tk->rp); +		else +			disable_kprobe(&tk->rp.kp); +		wait = 1; +	} + out: +	if (wait) { +		/* +		 * Synchronize with kprobe_trace_func/kretprobe_trace_func +		 * to ensure disabled (all running handlers are finished). +		 * This is not only for kfree(), but also the caller, +		 * trace_remove_event_call() supposes it for releasing +		 * event_call related objects, which will be accessed in +		 * the kprobe_trace_func/kretprobe_trace_func. +		 */ +		synchronize_sched(); +		kfree(link);	/* Ignored if link == NULL */ +	} +  	return ret;  } -/* Split symbol and offset. */ -static int split_symbol_offset(char *symbol, unsigned long *offset) +/* Internal register function - just handle k*probes and flags */ +static int __register_trace_kprobe(struct trace_kprobe *tk)  { -	char *tmp; -	int ret; +	int i, ret; -	if (!offset) +	if (trace_probe_is_registered(&tk->tp))  		return -EINVAL; -	tmp = strchr(symbol, '+'); -	if (tmp) { -		/* skip sign because strict_strtol doesn't accept '+' */ -		ret = strict_strtoul(tmp + 1, 0, offset); -		if (ret) -			return ret; -		*tmp = '\0'; -	} else -		*offset = 0; -	return 0; -} +	for (i = 0; i < tk->tp.nr_args; i++) +		traceprobe_update_arg(&tk->tp.args[i]); -#define PARAM_MAX_ARGS 16 -#define PARAM_MAX_STACK (THREAD_SIZE / sizeof(unsigned long)) +	/* Set/clear disabled flag according to tp->flag */ +	if (trace_probe_is_enabled(&tk->tp)) +		tk->rp.kp.flags &= ~KPROBE_FLAG_DISABLED; +	else +		tk->rp.kp.flags |= KPROBE_FLAG_DISABLED; -static int parse_probe_vars(char *arg, const struct fetch_type *t, -			    struct fetch_param *f, int is_return) -{ -	int ret = 0; -	unsigned long param; +	if (trace_kprobe_is_return(tk)) +		ret = register_kretprobe(&tk->rp); +	else +		ret = register_kprobe(&tk->rp.kp); -	if (strcmp(arg, "retval") == 0) { -		if (is_return) -			f->fn = t->fetch[FETCH_MTD_retval]; -		else -			ret = -EINVAL; -	} else if (strncmp(arg, "stack", 5) == 0) { -		if (arg[5] == '\0') { -			if (strcmp(t->name, DEFAULT_FETCH_TYPE_STR) == 0) -				f->fn = fetch_stack_address; -			else -				ret = -EINVAL; -		} else if (isdigit(arg[5])) { -			ret = strict_strtoul(arg + 5, 10, ¶m); -			if (ret || param > PARAM_MAX_STACK) -				ret = -EINVAL; -			else { -				f->fn = t->fetch[FETCH_MTD_stack]; -				f->data = (void *)param; -			} -		} else +	if (ret == 0) +		tk->tp.flags |= TP_FLAG_REGISTERED; +	else { +		pr_warning("Could not insert probe at %s+%lu: %d\n", +			   trace_kprobe_symbol(tk), trace_kprobe_offset(tk), ret); +		if (ret == -ENOENT && trace_kprobe_is_on_module(tk)) { +			pr_warning("This probe might be able to register after" +				   "target module is loaded. Continue.\n"); +			ret = 0; +		} else if (ret == -EILSEQ) { +			pr_warning("Probing address(0x%p) is not an " +				   "instruction boundary.\n", +				   tk->rp.kp.addr);  			ret = -EINVAL; -	} else -		ret = -EINVAL; +		} +	} +  	return ret;  } -/* Recursive argument parser */ -static int __parse_probe_arg(char *arg, const struct fetch_type *t, -			     struct fetch_param *f, int is_return) +/* Internal unregister function - just handle k*probes and flags */ +static void __unregister_trace_kprobe(struct trace_kprobe *tk)  { -	int ret = 0; -	unsigned long param; -	long offset; -	char *tmp; - -	switch (arg[0]) { -	case '$': -		ret = parse_probe_vars(arg + 1, t, f, is_return); -		break; -	case '%':	/* named register */ -		ret = regs_query_register_offset(arg + 1); -		if (ret >= 0) { -			f->fn = t->fetch[FETCH_MTD_reg]; -			f->data = (void *)(unsigned long)ret; -			ret = 0; -		} -		break; -	case '@':	/* memory or symbol */ -		if (isdigit(arg[1])) { -			ret = strict_strtoul(arg + 1, 0, ¶m); -			if (ret) -				break; -			f->fn = t->fetch[FETCH_MTD_memory]; -			f->data = (void *)param; -		} else { -			ret = split_symbol_offset(arg + 1, &offset); -			if (ret) -				break; -			f->data = alloc_symbol_cache(arg + 1, offset); -			if (f->data) -				f->fn = t->fetch[FETCH_MTD_symbol]; -		} -		break; -	case '+':	/* deref memory */ -	case '-': -		tmp = strchr(arg, '('); -		if (!tmp) -			break; -		*tmp = '\0'; -		ret = strict_strtol(arg + 1, 0, &offset); -		if (ret) -			break; -		if (arg[0] == '-') -			offset = -offset; -		arg = tmp + 1; -		tmp = strrchr(arg, ')'); -		if (tmp) { -			struct deref_fetch_param *dprm; -			const struct fetch_type *t2 = find_fetch_type(NULL); -			*tmp = '\0'; -			dprm = kzalloc(sizeof(struct deref_fetch_param), -				       GFP_KERNEL); -			if (!dprm) -				return -ENOMEM; -			dprm->offset = offset; -			ret = __parse_probe_arg(arg, t2, &dprm->orig, -						is_return); -			if (ret) -				kfree(dprm); -			else { -				f->fn = t->fetch[FETCH_MTD_deref]; -				f->data = (void *)dprm; -			} -		} -		break; -	} -	if (!ret && !f->fn) {	/* Parsed, but do not find fetch method */ -		pr_info("%s type has no corresponding fetch method.\n", -			t->name); -		ret = -EINVAL; +	if (trace_probe_is_registered(&tk->tp)) { +		if (trace_kprobe_is_return(tk)) +			unregister_kretprobe(&tk->rp); +		else +			unregister_kprobe(&tk->rp.kp); +		tk->tp.flags &= ~TP_FLAG_REGISTERED; +		/* Cleanup kprobe for reuse */ +		if (tk->rp.kp.symbol_name) +			tk->rp.kp.addr = NULL;  	} -	return ret;  } -/* String length checking wrapper */ -static int parse_probe_arg(char *arg, struct trace_probe *tp, -			   struct probe_arg *parg, int is_return) +/* Unregister a trace_probe and probe_event: call with locking probe_lock */ +static int unregister_trace_kprobe(struct trace_kprobe *tk)  { -	const char *t; +	/* Enabled event can not be unregistered */ +	if (trace_probe_is_enabled(&tk->tp)) +		return -EBUSY; + +	/* Will fail if probe is being used by ftrace or perf */ +	if (unregister_kprobe_event(tk)) +		return -EBUSY; + +	__unregister_trace_kprobe(tk); +	list_del(&tk->list); + +	return 0; +} + +/* Register a trace_probe and probe_event */ +static int register_trace_kprobe(struct trace_kprobe *tk) +{ +	struct trace_kprobe *old_tk;  	int ret; -	if (strlen(arg) > MAX_ARGSTR_LEN) { -		pr_info("Argument is too long.: %s\n",  arg); -		return -ENOSPC; -	} -	parg->comm = kstrdup(arg, GFP_KERNEL); -	if (!parg->comm) { -		pr_info("Failed to allocate memory for command '%s'.\n", arg); -		return -ENOMEM; -	} -	t = strchr(parg->comm, ':'); -	if (t) { -		arg[t - parg->comm] = '\0'; -		t++; -	} -	parg->type = find_fetch_type(t); -	if (!parg->type) { -		pr_info("Unsupported type: %s\n", t); -		return -EINVAL; +	mutex_lock(&probe_lock); + +	/* Delete old (same name) event if exist */ +	old_tk = find_trace_kprobe(ftrace_event_name(&tk->tp.call), +			tk->tp.call.class->system); +	if (old_tk) { +		ret = unregister_trace_kprobe(old_tk); +		if (ret < 0) +			goto end; +		free_trace_kprobe(old_tk);  	} -	parg->offset = tp->size; -	tp->size += parg->type->size; -	ret = __parse_probe_arg(arg, parg->type, &parg->fetch, is_return); -	if (ret >= 0) { -		parg->fetch_size.fn = get_fetch_size_function(parg->type, -							      parg->fetch.fn); -		parg->fetch_size.data = parg->fetch.data; + +	/* Register new event */ +	ret = register_kprobe_event(tk); +	if (ret) { +		pr_warning("Failed to register probe event(%d)\n", ret); +		goto end;  	} + +	/* Register k*probe */ +	ret = __register_trace_kprobe(tk); +	if (ret < 0) +		unregister_kprobe_event(tk); +	else +		list_add_tail(&tk->list, &probe_list); + +end: +	mutex_unlock(&probe_lock);  	return ret;  } -/* Return 1 if name is reserved or already used by another argument */ -static int conflict_field_name(const char *name, -			       struct probe_arg *args, int narg) +/* Module notifier call back, checking event on the module */ +static int trace_kprobe_module_callback(struct notifier_block *nb, +				       unsigned long val, void *data)  { -	int i; -	for (i = 0; i < ARRAY_SIZE(reserved_field_names); i++) -		if (strcmp(reserved_field_names[i], name) == 0) -			return 1; -	for (i = 0; i < narg; i++) -		if (strcmp(args[i].name, name) == 0) -			return 1; -	return 0; +	struct module *mod = data; +	struct trace_kprobe *tk; +	int ret; + +	if (val != MODULE_STATE_COMING) +		return NOTIFY_DONE; + +	/* Update probes on coming module */ +	mutex_lock(&probe_lock); +	list_for_each_entry(tk, &probe_list, list) { +		if (trace_kprobe_within_module(tk, mod)) { +			/* Don't need to check busy - this should have gone. */ +			__unregister_trace_kprobe(tk); +			ret = __register_trace_kprobe(tk); +			if (ret) +				pr_warning("Failed to re-register probe %s on" +					   "%s: %d\n", +					   ftrace_event_name(&tk->tp.call), +					   mod->name, ret); +		} +	} +	mutex_unlock(&probe_lock); + +	return NOTIFY_DONE;  } -static int create_trace_probe(int argc, char **argv) +static struct notifier_block trace_kprobe_module_nb = { +	.notifier_call = trace_kprobe_module_callback, +	.priority = 1	/* Invoked after kprobe module callback */ +}; + +static int create_trace_kprobe(int argc, char **argv)  {  	/*  	 * Argument syntax: -	 *  - Add kprobe: p[:[GRP/]EVENT] KSYM[+OFFS]|KADDR [FETCHARGS] -	 *  - Add kretprobe: r[:[GRP/]EVENT] KSYM[+0] [FETCHARGS] +	 *  - Add kprobe: p[:[GRP/]EVENT] [MOD:]KSYM[+OFFS]|KADDR [FETCHARGS] +	 *  - Add kretprobe: r[:[GRP/]EVENT] [MOD:]KSYM[+0] [FETCHARGS]  	 * Fetch args:  	 *  $retval	: fetch return value  	 *  $stack	: fetch stack address @@ -878,9 +606,9 @@ static int create_trace_probe(int argc, char **argv)  	 * Type of args:  	 *  FETCHARG:TYPE : use TYPE instead of unsigned long.  	 */ -	struct trace_probe *tp; +	struct trace_kprobe *tk;  	int i, ret = 0; -	int is_return = 0, is_delete = 0; +	bool is_return = false, is_delete = false;  	char *symbol = NULL, *event = NULL, *group = NULL;  	char *arg;  	unsigned long offset = 0; @@ -889,11 +617,11 @@ static int create_trace_probe(int argc, char **argv)  	/* argc must be >= 1 */  	if (argv[0][0] == 'p') -		is_return = 0; +		is_return = false;  	else if (argv[0][0] == 'r') -		is_return = 1; +		is_return = true;  	else if (argv[0][0] == '-') -		is_delete = 1; +		is_delete = true;  	else {  		pr_info("Probe definition must be started with 'p', 'r' or"  			" '-'.\n"); @@ -925,17 +653,18 @@ static int create_trace_probe(int argc, char **argv)  			return -EINVAL;  		}  		mutex_lock(&probe_lock); -		tp = find_probe_event(event, group); -		if (!tp) { +		tk = find_trace_kprobe(event, group); +		if (!tk) {  			mutex_unlock(&probe_lock);  			pr_info("Event %s/%s doesn't exist.\n", group, event);  			return -ENOENT;  		}  		/* delete an event */ -		unregister_trace_probe(tp); -		free_trace_probe(tp); +		ret = unregister_trace_kprobe(tk); +		if (ret == 0) +			free_trace_kprobe(tk);  		mutex_unlock(&probe_lock); -		return 0; +		return ret;  	}  	if (argc < 2) { @@ -948,7 +677,7 @@ static int create_trace_probe(int argc, char **argv)  			return -EINVAL;  		}  		/* an address specified */ -		ret = strict_strtoul(&argv[1][0], 0, (unsigned long *)&addr); +		ret = kstrtoul(&argv[1][0], 0, (unsigned long *)&addr);  		if (ret) {  			pr_info("Failed to parse address.\n");  			return ret; @@ -957,7 +686,7 @@ static int create_trace_probe(int argc, char **argv)  		/* a symbol specified */  		symbol = argv[1];  		/* TODO: support .init module functions */ -		ret = split_symbol_offset(symbol, &offset); +		ret = traceprobe_split_symbol_offset(symbol, &offset);  		if (ret) {  			pr_info("Failed to parse symbol.\n");  			return ret; @@ -980,46 +709,49 @@ static int create_trace_probe(int argc, char **argv)  				 is_return ? 'r' : 'p', addr);  		event = buf;  	} -	tp = alloc_trace_probe(group, event, addr, symbol, offset, argc, +	tk = alloc_trace_kprobe(group, event, addr, symbol, offset, argc,  			       is_return); -	if (IS_ERR(tp)) { +	if (IS_ERR(tk)) {  		pr_info("Failed to allocate trace_probe.(%d)\n", -			(int)PTR_ERR(tp)); -		return PTR_ERR(tp); +			(int)PTR_ERR(tk)); +		return PTR_ERR(tk);  	}  	/* parse arguments */  	ret = 0;  	for (i = 0; i < argc && i < MAX_TRACE_ARGS; i++) { +		struct probe_arg *parg = &tk->tp.args[i]; +  		/* Increment count for freeing args in error case */ -		tp->nr_args++; +		tk->tp.nr_args++;  		/* Parse argument name */  		arg = strchr(argv[i], '=');  		if (arg) {  			*arg++ = '\0'; -			tp->args[i].name = kstrdup(argv[i], GFP_KERNEL); +			parg->name = kstrdup(argv[i], GFP_KERNEL);  		} else {  			arg = argv[i];  			/* If argument name is omitted, set "argN" */  			snprintf(buf, MAX_EVENT_NAME_LEN, "arg%d", i + 1); -			tp->args[i].name = kstrdup(buf, GFP_KERNEL); +			parg->name = kstrdup(buf, GFP_KERNEL);  		} -		if (!tp->args[i].name) { +		if (!parg->name) {  			pr_info("Failed to allocate argument[%d] name.\n", i);  			ret = -ENOMEM;  			goto error;  		} -		if (!is_good_name(tp->args[i].name)) { +		if (!is_good_name(parg->name)) {  			pr_info("Invalid argument[%d] name: %s\n", -				i, tp->args[i].name); +				i, parg->name);  			ret = -EINVAL;  			goto error;  		} -		if (conflict_field_name(tp->args[i].name, tp->args, i)) { +		if (traceprobe_conflict_field_name(parg->name, +							tk->tp.args, i)) {  			pr_info("Argument[%d] name '%s' conflicts with "  				"another field.\n", i, argv[i]);  			ret = -EINVAL; @@ -1027,37 +759,50 @@ static int create_trace_probe(int argc, char **argv)  		}  		/* Parse fetch argument */ -		ret = parse_probe_arg(arg, tp, &tp->args[i], is_return); +		ret = traceprobe_parse_probe_arg(arg, &tk->tp.size, parg, +						is_return, true);  		if (ret) {  			pr_info("Parse error at argument[%d]. (%d)\n", i, ret);  			goto error;  		}  	} -	ret = register_trace_probe(tp); +	ret = register_trace_kprobe(tk);  	if (ret)  		goto error;  	return 0;  error: -	free_trace_probe(tp); +	free_trace_kprobe(tk);  	return ret;  } -static void cleanup_all_probes(void) +static int release_all_trace_kprobes(void)  { -	struct trace_probe *tp; +	struct trace_kprobe *tk; +	int ret = 0;  	mutex_lock(&probe_lock); +	/* Ensure no probe is in use. */ +	list_for_each_entry(tk, &probe_list, list) +		if (trace_probe_is_enabled(&tk->tp)) { +			ret = -EBUSY; +			goto end; +		}  	/* TODO: Use batch unregistration */  	while (!list_empty(&probe_list)) { -		tp = list_entry(probe_list.next, struct trace_probe, list); -		unregister_trace_probe(tp); -		free_trace_probe(tp); +		tk = list_entry(probe_list.next, struct trace_kprobe, list); +		ret = unregister_trace_kprobe(tk); +		if (ret) +			goto end; +		free_trace_kprobe(tk);  	} + +end:  	mutex_unlock(&probe_lock); -} +	return ret; +}  /* Probes listing interfaces */  static void *probes_seq_start(struct seq_file *m, loff_t *pos) @@ -1078,21 +823,23 @@ static void probes_seq_stop(struct seq_file *m, void *v)  static int probes_seq_show(struct seq_file *m, void *v)  { -	struct trace_probe *tp = v; +	struct trace_kprobe *tk = v;  	int i; -	seq_printf(m, "%c", probe_is_return(tp) ? 'r' : 'p'); -	seq_printf(m, ":%s/%s", tp->call.class->system, tp->call.name); +	seq_printf(m, "%c", trace_kprobe_is_return(tk) ? 'r' : 'p'); +	seq_printf(m, ":%s/%s", tk->tp.call.class->system, +			ftrace_event_name(&tk->tp.call)); -	if (!tp->symbol) -		seq_printf(m, " 0x%p", tp->rp.kp.addr); -	else if (tp->rp.kp.offset) -		seq_printf(m, " %s+%u", probe_symbol(tp), tp->rp.kp.offset); +	if (!tk->symbol) +		seq_printf(m, " 0x%p", tk->rp.kp.addr); +	else if (tk->rp.kp.offset) +		seq_printf(m, " %s+%u", trace_kprobe_symbol(tk), +			   tk->rp.kp.offset);  	else -		seq_printf(m, " %s", probe_symbol(tp)); +		seq_printf(m, " %s", trace_kprobe_symbol(tk)); -	for (i = 0; i < tp->nr_args; i++) -		seq_printf(m, " %s=%s", tp->args[i].name, tp->args[i].comm); +	for (i = 0; i < tk->tp.nr_args; i++) +		seq_printf(m, " %s=%s", tk->tp.args[i].name, tk->tp.args[i].comm);  	seq_printf(m, "\n");  	return 0; @@ -1107,77 +854,22 @@ static const struct seq_operations probes_seq_op = {  static int probes_open(struct inode *inode, struct file *file)  { -	if ((file->f_mode & FMODE_WRITE) && -	    (file->f_flags & O_TRUNC)) -		cleanup_all_probes(); - -	return seq_open(file, &probes_seq_op); -} - -static int command_trace_probe(const char *buf) -{ -	char **argv; -	int argc = 0, ret = 0; - -	argv = argv_split(GFP_KERNEL, buf, &argc); -	if (!argv) -		return -ENOMEM; +	int ret; -	if (argc) -		ret = create_trace_probe(argc, argv); +	if ((file->f_mode & FMODE_WRITE) && (file->f_flags & O_TRUNC)) { +		ret = release_all_trace_kprobes(); +		if (ret < 0) +			return ret; +	} -	argv_free(argv); -	return ret; +	return seq_open(file, &probes_seq_op);  } -#define WRITE_BUFSIZE 128 -  static ssize_t probes_write(struct file *file, const char __user *buffer,  			    size_t count, loff_t *ppos)  { -	char *kbuf, *tmp; -	int ret; -	size_t done; -	size_t size; - -	kbuf = kmalloc(WRITE_BUFSIZE, GFP_KERNEL); -	if (!kbuf) -		return -ENOMEM; - -	ret = done = 0; -	while (done < count) { -		size = count - done; -		if (size >= WRITE_BUFSIZE) -			size = WRITE_BUFSIZE - 1; -		if (copy_from_user(kbuf, buffer + done, size)) { -			ret = -EFAULT; -			goto out; -		} -		kbuf[size] = '\0'; -		tmp = strchr(kbuf, '\n'); -		if (tmp) { -			*tmp = '\0'; -			size = tmp - kbuf + 1; -		} else if (done + size < count) { -			pr_warning("Line length is too long: " -				   "Should be less than %d.", WRITE_BUFSIZE); -			ret = -EINVAL; -			goto out; -		} -		done += size; -		/* Remove comments */ -		tmp = strchr(kbuf, '#'); -		if (tmp) -			*tmp = '\0'; - -		ret = command_trace_probe(kbuf); -		if (ret) -			goto out; -	} -	ret = done; -out: -	kfree(kbuf); -	return ret; +	return traceprobe_probes_write(file, buffer, count, ppos, +			create_trace_kprobe);  }  static const struct file_operations kprobe_events_ops = { @@ -1192,10 +884,11 @@ static const struct file_operations kprobe_events_ops = {  /* Probes profiling interfaces */  static int probes_profile_seq_show(struct seq_file *m, void *v)  { -	struct trace_probe *tp = v; +	struct trace_kprobe *tk = v; -	seq_printf(m, "  %-44s %15lu %15lu\n", tp->call.name, tp->nhit, -		   tp->rp.kp.nmissed); +	seq_printf(m, "  %-44s %15lu %15lu\n", +		   ftrace_event_name(&tk->tp.call), tk->nhit, +		   tk->rp.kp.nmissed);  	return 0;  } @@ -1220,120 +913,105 @@ static const struct file_operations kprobe_profile_ops = {  	.release        = seq_release,  }; -/* Sum up total data length for dynamic arraies (strings) */ -static __kprobes int __get_data_size(struct trace_probe *tp, -				     struct pt_regs *regs) -{ -	int i, ret = 0; -	u32 len; - -	for (i = 0; i < tp->nr_args; i++) -		if (unlikely(tp->args[i].fetch_size.fn)) { -			call_fetch(&tp->args[i].fetch_size, regs, &len); -			ret += len; -		} - -	return ret; -} - -/* Store the value of each argument */ -static __kprobes void store_trace_args(int ent_size, struct trace_probe *tp, -				       struct pt_regs *regs, -				       u8 *data, int maxlen) -{ -	int i; -	u32 end = tp->size; -	u32 *dl;	/* Data (relative) location */ - -	for (i = 0; i < tp->nr_args; i++) { -		if (unlikely(tp->args[i].fetch_size.fn)) { -			/* -			 * First, we set the relative location and -			 * maximum data length to *dl -			 */ -			dl = (u32 *)(data + tp->args[i].offset); -			*dl = make_data_rloc(maxlen, end - tp->args[i].offset); -			/* Then try to fetch string or dynamic array data */ -			call_fetch(&tp->args[i].fetch, regs, dl); -			/* Reduce maximum length */ -			end += get_rloc_len(*dl); -			maxlen -= get_rloc_len(*dl); -			/* Trick here, convert data_rloc to data_loc */ -			*dl = convert_rloc_to_loc(*dl, -				 ent_size + tp->args[i].offset); -		} else -			/* Just fetching data normally */ -			call_fetch(&tp->args[i].fetch, regs, -				   data + tp->args[i].offset); -	} -} -  /* Kprobe handler */ -static __kprobes void kprobe_trace_func(struct kprobe *kp, struct pt_regs *regs) +static nokprobe_inline void +__kprobe_trace_func(struct trace_kprobe *tk, struct pt_regs *regs, +		    struct ftrace_event_file *ftrace_file)  { -	struct trace_probe *tp = container_of(kp, struct trace_probe, rp.kp);  	struct kprobe_trace_entry_head *entry;  	struct ring_buffer_event *event;  	struct ring_buffer *buffer;  	int size, dsize, pc;  	unsigned long irq_flags; -	struct ftrace_event_call *call = &tp->call; +	struct ftrace_event_call *call = &tk->tp.call; -	tp->nhit++; +	WARN_ON(call != ftrace_file->event_call); + +	if (ftrace_trigger_soft_disabled(ftrace_file)) +		return;  	local_save_flags(irq_flags);  	pc = preempt_count(); -	dsize = __get_data_size(tp, regs); -	size = sizeof(*entry) + tp->size + dsize; +	dsize = __get_data_size(&tk->tp, regs); +	size = sizeof(*entry) + tk->tp.size + dsize; -	event = trace_current_buffer_lock_reserve(&buffer, call->event.type, -						  size, irq_flags, pc); +	event = trace_event_buffer_lock_reserve(&buffer, ftrace_file, +						call->event.type, +						size, irq_flags, pc);  	if (!event)  		return;  	entry = ring_buffer_event_data(event); -	entry->ip = (unsigned long)kp->addr; -	store_trace_args(sizeof(*entry), tp, regs, (u8 *)&entry[1], dsize); +	entry->ip = (unsigned long)tk->rp.kp.addr; +	store_trace_args(sizeof(*entry), &tk->tp, regs, (u8 *)&entry[1], dsize); + +	event_trigger_unlock_commit_regs(ftrace_file, buffer, event, +					 entry, irq_flags, pc, regs); +} -	if (!filter_current_check_discard(buffer, call, entry, event)) -		trace_nowake_buffer_unlock_commit(buffer, event, irq_flags, pc); +static void +kprobe_trace_func(struct trace_kprobe *tk, struct pt_regs *regs) +{ +	struct event_file_link *link; + +	list_for_each_entry_rcu(link, &tk->tp.files, list) +		__kprobe_trace_func(tk, regs, link->file);  } +NOKPROBE_SYMBOL(kprobe_trace_func);  /* Kretprobe handler */ -static __kprobes void kretprobe_trace_func(struct kretprobe_instance *ri, -					  struct pt_regs *regs) +static nokprobe_inline void +__kretprobe_trace_func(struct trace_kprobe *tk, struct kretprobe_instance *ri, +		       struct pt_regs *regs, +		       struct ftrace_event_file *ftrace_file)  { -	struct trace_probe *tp = container_of(ri->rp, struct trace_probe, rp);  	struct kretprobe_trace_entry_head *entry;  	struct ring_buffer_event *event;  	struct ring_buffer *buffer;  	int size, pc, dsize;  	unsigned long irq_flags; -	struct ftrace_event_call *call = &tp->call; +	struct ftrace_event_call *call = &tk->tp.call; + +	WARN_ON(call != ftrace_file->event_call); + +	if (ftrace_trigger_soft_disabled(ftrace_file)) +		return;  	local_save_flags(irq_flags);  	pc = preempt_count(); -	dsize = __get_data_size(tp, regs); -	size = sizeof(*entry) + tp->size + dsize; +	dsize = __get_data_size(&tk->tp, regs); +	size = sizeof(*entry) + tk->tp.size + dsize; -	event = trace_current_buffer_lock_reserve(&buffer, call->event.type, -						  size, irq_flags, pc); +	event = trace_event_buffer_lock_reserve(&buffer, ftrace_file, +						call->event.type, +						size, irq_flags, pc);  	if (!event)  		return;  	entry = ring_buffer_event_data(event); -	entry->func = (unsigned long)tp->rp.kp.addr; +	entry->func = (unsigned long)tk->rp.kp.addr;  	entry->ret_ip = (unsigned long)ri->ret_addr; -	store_trace_args(sizeof(*entry), tp, regs, (u8 *)&entry[1], dsize); +	store_trace_args(sizeof(*entry), &tk->tp, regs, (u8 *)&entry[1], dsize); -	if (!filter_current_check_discard(buffer, call, entry, event)) -		trace_nowake_buffer_unlock_commit(buffer, event, irq_flags, pc); +	event_trigger_unlock_commit_regs(ftrace_file, buffer, event, +					 entry, irq_flags, pc, regs);  } +static void +kretprobe_trace_func(struct trace_kprobe *tk, struct kretprobe_instance *ri, +		     struct pt_regs *regs) +{ +	struct event_file_link *link; + +	list_for_each_entry_rcu(link, &tk->tp.files, list) +		__kretprobe_trace_func(tk, ri, regs, link->file); +} +NOKPROBE_SYMBOL(kretprobe_trace_func); +  /* Event entry printers */ -enum print_line_t +static enum print_line_t  print_kprobe_event(struct trace_iterator *iter, int flags,  		   struct trace_event *event)  { @@ -1346,7 +1024,7 @@ print_kprobe_event(struct trace_iterator *iter, int flags,  	field = (struct kprobe_trace_entry_head *)iter->ent;  	tp = container_of(event, struct trace_probe, call.event); -	if (!trace_seq_printf(s, "%s: (", tp->call.name)) +	if (!trace_seq_printf(s, "%s: (", ftrace_event_name(&tp->call)))  		goto partial;  	if (!seq_print_ip_sym(s, field->ip, flags | TRACE_ITER_SYM_OFFSET)) @@ -1369,7 +1047,7 @@ partial:  	return TRACE_TYPE_PARTIAL_LINE;  } -enum print_line_t +static enum print_line_t  print_kretprobe_event(struct trace_iterator *iter, int flags,  		      struct trace_event *event)  { @@ -1382,7 +1060,7 @@ print_kretprobe_event(struct trace_iterator *iter, int flags,  	field = (struct kretprobe_trace_entry_head *)iter->ent;  	tp = container_of(event, struct trace_probe, call.event); -	if (!trace_seq_printf(s, "%s: (", tp->call.name)) +	if (!trace_seq_printf(s, "%s: (", ftrace_event_name(&tp->call)))  		goto partial;  	if (!seq_print_ip_sym(s, field->ret_ip, flags | TRACE_ITER_SYM_OFFSET)) @@ -1411,55 +1089,23 @@ partial:  	return TRACE_TYPE_PARTIAL_LINE;  } -static int probe_event_enable(struct ftrace_event_call *call) -{ -	struct trace_probe *tp = (struct trace_probe *)call->data; - -	tp->flags |= TP_FLAG_TRACE; -	if (probe_is_return(tp)) -		return enable_kretprobe(&tp->rp); -	else -		return enable_kprobe(&tp->rp.kp); -} - -static void probe_event_disable(struct ftrace_event_call *call) -{ -	struct trace_probe *tp = (struct trace_probe *)call->data; - -	tp->flags &= ~TP_FLAG_TRACE; -	if (!(tp->flags & (TP_FLAG_TRACE | TP_FLAG_PROFILE))) { -		if (probe_is_return(tp)) -			disable_kretprobe(&tp->rp); -		else -			disable_kprobe(&tp->rp.kp); -	} -} - -#undef DEFINE_FIELD -#define DEFINE_FIELD(type, item, name, is_signed)			\ -	do {								\ -		ret = trace_define_field(event_call, #type, name,	\ -					 offsetof(typeof(field), item),	\ -					 sizeof(field.item), is_signed, \ -					 FILTER_OTHER);			\ -		if (ret)						\ -			return ret;					\ -	} while (0)  static int kprobe_event_define_fields(struct ftrace_event_call *event_call)  {  	int ret, i;  	struct kprobe_trace_entry_head field; -	struct trace_probe *tp = (struct trace_probe *)event_call->data; +	struct trace_kprobe *tk = (struct trace_kprobe *)event_call->data;  	DEFINE_FIELD(unsigned long, ip, FIELD_STRING_IP, 0);  	/* Set argument names as fields */ -	for (i = 0; i < tp->nr_args; i++) { -		ret = trace_define_field(event_call, tp->args[i].type->fmttype, -					 tp->args[i].name, -					 sizeof(field) + tp->args[i].offset, -					 tp->args[i].type->size, -					 tp->args[i].type->is_signed, +	for (i = 0; i < tk->tp.nr_args; i++) { +		struct probe_arg *parg = &tk->tp.args[i]; + +		ret = trace_define_field(event_call, parg->type->fmttype, +					 parg->name, +					 sizeof(field) + parg->offset, +					 parg->type->size, +					 parg->type->is_signed,  					 FILTER_OTHER);  		if (ret)  			return ret; @@ -1471,17 +1117,19 @@ static int kretprobe_event_define_fields(struct ftrace_event_call *event_call)  {  	int ret, i;  	struct kretprobe_trace_entry_head field; -	struct trace_probe *tp = (struct trace_probe *)event_call->data; +	struct trace_kprobe *tk = (struct trace_kprobe *)event_call->data;  	DEFINE_FIELD(unsigned long, func, FIELD_STRING_FUNC, 0);  	DEFINE_FIELD(unsigned long, ret_ip, FIELD_STRING_RETIP, 0);  	/* Set argument names as fields */ -	for (i = 0; i < tp->nr_args; i++) { -		ret = trace_define_field(event_call, tp->args[i].type->fmttype, -					 tp->args[i].name, -					 sizeof(field) + tp->args[i].offset, -					 tp->args[i].type->size, -					 tp->args[i].type->is_signed, +	for (i = 0; i < tk->tp.nr_args; i++) { +		struct probe_arg *parg = &tk->tp.args[i]; + +		ret = trace_define_field(event_call, parg->type->fmttype, +					 parg->name, +					 sizeof(field) + parg->offset, +					 parg->type->size, +					 parg->type->is_signed,  					 FILTER_OTHER);  		if (ret)  			return ret; @@ -1489,206 +1137,135 @@ static int kretprobe_event_define_fields(struct ftrace_event_call *event_call)  	return 0;  } -static int __set_print_fmt(struct trace_probe *tp, char *buf, int len) -{ -	int i; -	int pos = 0; - -	const char *fmt, *arg; - -	if (!probe_is_return(tp)) { -		fmt = "(%lx)"; -		arg = "REC->" FIELD_STRING_IP; -	} else { -		fmt = "(%lx <- %lx)"; -		arg = "REC->" FIELD_STRING_FUNC ", REC->" FIELD_STRING_RETIP; -	} - -	/* When len=0, we just calculate the needed length */ -#define LEN_OR_ZERO (len ? len - pos : 0) - -	pos += snprintf(buf + pos, LEN_OR_ZERO, "\"%s", fmt); - -	for (i = 0; i < tp->nr_args; i++) { -		pos += snprintf(buf + pos, LEN_OR_ZERO, " %s=%s", -				tp->args[i].name, tp->args[i].type->fmt); -	} - -	pos += snprintf(buf + pos, LEN_OR_ZERO, "\", %s", arg); - -	for (i = 0; i < tp->nr_args; i++) { -		if (strcmp(tp->args[i].type->name, "string") == 0) -			pos += snprintf(buf + pos, LEN_OR_ZERO, -					", __get_str(%s)", -					tp->args[i].name); -		else -			pos += snprintf(buf + pos, LEN_OR_ZERO, ", REC->%s", -					tp->args[i].name); -	} - -#undef LEN_OR_ZERO - -	/* return the length of print_fmt */ -	return pos; -} - -static int set_print_fmt(struct trace_probe *tp) -{ -	int len; -	char *print_fmt; - -	/* First: called with 0 length to calculate the needed length */ -	len = __set_print_fmt(tp, NULL, 0); -	print_fmt = kmalloc(len + 1, GFP_KERNEL); -	if (!print_fmt) -		return -ENOMEM; - -	/* Second: actually write the @print_fmt */ -	__set_print_fmt(tp, print_fmt, len + 1); -	tp->call.print_fmt = print_fmt; - -	return 0; -} -  #ifdef CONFIG_PERF_EVENTS  /* Kprobe profile handler */ -static __kprobes void kprobe_perf_func(struct kprobe *kp, -					 struct pt_regs *regs) +static void +kprobe_perf_func(struct trace_kprobe *tk, struct pt_regs *regs)  { -	struct trace_probe *tp = container_of(kp, struct trace_probe, rp.kp); -	struct ftrace_event_call *call = &tp->call; +	struct ftrace_event_call *call = &tk->tp.call;  	struct kprobe_trace_entry_head *entry;  	struct hlist_head *head;  	int size, __size, dsize;  	int rctx; -	dsize = __get_data_size(tp, regs); -	__size = sizeof(*entry) + tp->size + dsize; +	head = this_cpu_ptr(call->perf_events); +	if (hlist_empty(head)) +		return; + +	dsize = __get_data_size(&tk->tp, regs); +	__size = sizeof(*entry) + tk->tp.size + dsize;  	size = ALIGN(__size + sizeof(u32), sizeof(u64));  	size -= sizeof(u32); -	if (WARN_ONCE(size > PERF_MAX_TRACE_SIZE, -		     "profile buffer not large enough")) -		return;  	entry = perf_trace_buf_prepare(size, call->event.type, regs, &rctx);  	if (!entry)  		return; -	entry->ip = (unsigned long)kp->addr; +	entry->ip = (unsigned long)tk->rp.kp.addr;  	memset(&entry[1], 0, dsize); -	store_trace_args(sizeof(*entry), tp, regs, (u8 *)&entry[1], dsize); - -	head = this_cpu_ptr(call->perf_events); -	perf_trace_buf_submit(entry, size, rctx, entry->ip, 1, regs, head); +	store_trace_args(sizeof(*entry), &tk->tp, regs, (u8 *)&entry[1], dsize); +	perf_trace_buf_submit(entry, size, rctx, 0, 1, regs, head, NULL);  } +NOKPROBE_SYMBOL(kprobe_perf_func);  /* Kretprobe profile handler */ -static __kprobes void kretprobe_perf_func(struct kretprobe_instance *ri, -					    struct pt_regs *regs) +static void +kretprobe_perf_func(struct trace_kprobe *tk, struct kretprobe_instance *ri, +		    struct pt_regs *regs)  { -	struct trace_probe *tp = container_of(ri->rp, struct trace_probe, rp); -	struct ftrace_event_call *call = &tp->call; +	struct ftrace_event_call *call = &tk->tp.call;  	struct kretprobe_trace_entry_head *entry;  	struct hlist_head *head;  	int size, __size, dsize;  	int rctx; -	dsize = __get_data_size(tp, regs); -	__size = sizeof(*entry) + tp->size + dsize; +	head = this_cpu_ptr(call->perf_events); +	if (hlist_empty(head)) +		return; + +	dsize = __get_data_size(&tk->tp, regs); +	__size = sizeof(*entry) + tk->tp.size + dsize;  	size = ALIGN(__size + sizeof(u32), sizeof(u64));  	size -= sizeof(u32); -	if (WARN_ONCE(size > PERF_MAX_TRACE_SIZE, -		     "profile buffer not large enough")) -		return;  	entry = perf_trace_buf_prepare(size, call->event.type, regs, &rctx);  	if (!entry)  		return; -	entry->func = (unsigned long)tp->rp.kp.addr; +	entry->func = (unsigned long)tk->rp.kp.addr;  	entry->ret_ip = (unsigned long)ri->ret_addr; -	store_trace_args(sizeof(*entry), tp, regs, (u8 *)&entry[1], dsize); - -	head = this_cpu_ptr(call->perf_events); -	perf_trace_buf_submit(entry, size, rctx, entry->ret_ip, 1, regs, head); -} - -static int probe_perf_enable(struct ftrace_event_call *call) -{ -	struct trace_probe *tp = (struct trace_probe *)call->data; - -	tp->flags |= TP_FLAG_PROFILE; - -	if (probe_is_return(tp)) -		return enable_kretprobe(&tp->rp); -	else -		return enable_kprobe(&tp->rp.kp); -} - -static void probe_perf_disable(struct ftrace_event_call *call) -{ -	struct trace_probe *tp = (struct trace_probe *)call->data; - -	tp->flags &= ~TP_FLAG_PROFILE; - -	if (!(tp->flags & TP_FLAG_TRACE)) { -		if (probe_is_return(tp)) -			disable_kretprobe(&tp->rp); -		else -			disable_kprobe(&tp->rp.kp); -	} +	store_trace_args(sizeof(*entry), &tk->tp, regs, (u8 *)&entry[1], dsize); +	perf_trace_buf_submit(entry, size, rctx, 0, 1, regs, head, NULL);  } +NOKPROBE_SYMBOL(kretprobe_perf_func);  #endif	/* CONFIG_PERF_EVENTS */ -static __kprobes -int kprobe_register(struct ftrace_event_call *event, enum trace_reg type) +/* + * called by perf_trace_init() or __ftrace_set_clr_event() under event_mutex. + * + * kprobe_trace_self_tests_init() does enable_trace_probe/disable_trace_probe + * lockless, but we can't race with this __init function. + */ +static int kprobe_register(struct ftrace_event_call *event, +			   enum trace_reg type, void *data)  { +	struct trace_kprobe *tk = (struct trace_kprobe *)event->data; +	struct ftrace_event_file *file = data; +  	switch (type) {  	case TRACE_REG_REGISTER: -		return probe_event_enable(event); +		return enable_trace_kprobe(tk, file);  	case TRACE_REG_UNREGISTER: -		probe_event_disable(event); -		return 0; +		return disable_trace_kprobe(tk, file);  #ifdef CONFIG_PERF_EVENTS  	case TRACE_REG_PERF_REGISTER: -		return probe_perf_enable(event); +		return enable_trace_kprobe(tk, NULL);  	case TRACE_REG_PERF_UNREGISTER: -		probe_perf_disable(event); +		return disable_trace_kprobe(tk, NULL); +	case TRACE_REG_PERF_OPEN: +	case TRACE_REG_PERF_CLOSE: +	case TRACE_REG_PERF_ADD: +	case TRACE_REG_PERF_DEL:  		return 0;  #endif  	}  	return 0;  } -static __kprobes -int kprobe_dispatcher(struct kprobe *kp, struct pt_regs *regs) +static int kprobe_dispatcher(struct kprobe *kp, struct pt_regs *regs)  { -	struct trace_probe *tp = container_of(kp, struct trace_probe, rp.kp); +	struct trace_kprobe *tk = container_of(kp, struct trace_kprobe, rp.kp); + +	tk->nhit++; -	if (tp->flags & TP_FLAG_TRACE) -		kprobe_trace_func(kp, regs); +	if (tk->tp.flags & TP_FLAG_TRACE) +		kprobe_trace_func(tk, regs);  #ifdef CONFIG_PERF_EVENTS -	if (tp->flags & TP_FLAG_PROFILE) -		kprobe_perf_func(kp, regs); +	if (tk->tp.flags & TP_FLAG_PROFILE) +		kprobe_perf_func(tk, regs);  #endif  	return 0;	/* We don't tweek kernel, so just return 0 */  } +NOKPROBE_SYMBOL(kprobe_dispatcher); -static __kprobes -int kretprobe_dispatcher(struct kretprobe_instance *ri, struct pt_regs *regs) +static int +kretprobe_dispatcher(struct kretprobe_instance *ri, struct pt_regs *regs)  { -	struct trace_probe *tp = container_of(ri->rp, struct trace_probe, rp); +	struct trace_kprobe *tk = container_of(ri->rp, struct trace_kprobe, rp); -	if (tp->flags & TP_FLAG_TRACE) -		kretprobe_trace_func(ri, regs); +	tk->nhit++; + +	if (tk->tp.flags & TP_FLAG_TRACE) +		kretprobe_trace_func(tk, ri, regs);  #ifdef CONFIG_PERF_EVENTS -	if (tp->flags & TP_FLAG_PROFILE) -		kretprobe_perf_func(ri, regs); +	if (tk->tp.flags & TP_FLAG_PROFILE) +		kretprobe_perf_func(tk, ri, regs);  #endif  	return 0;	/* We don't tweek kernel, so just return 0 */  } +NOKPROBE_SYMBOL(kretprobe_dispatcher);  static struct trace_event_functions kretprobe_funcs = {  	.trace		= print_kretprobe_event @@ -1698,21 +1275,21 @@ static struct trace_event_functions kprobe_funcs = {  	.trace		= print_kprobe_event  }; -static int register_probe_event(struct trace_probe *tp) +static int register_kprobe_event(struct trace_kprobe *tk)  { -	struct ftrace_event_call *call = &tp->call; +	struct ftrace_event_call *call = &tk->tp.call;  	int ret;  	/* Initialize ftrace_event_call */  	INIT_LIST_HEAD(&call->class->fields); -	if (probe_is_return(tp)) { +	if (trace_kprobe_is_return(tk)) {  		call->event.funcs = &kretprobe_funcs;  		call->class->define_fields = kretprobe_event_define_fields;  	} else {  		call->event.funcs = &kprobe_funcs;  		call->class->define_fields = kprobe_event_define_fields;  	} -	if (set_print_fmt(tp) < 0) +	if (set_print_fmt(&tk->tp, trace_kprobe_is_return(tk)) < 0)  		return -ENOMEM;  	ret = register_ftrace_event(&call->event);  	if (!ret) { @@ -1721,29 +1298,37 @@ static int register_probe_event(struct trace_probe *tp)  	}  	call->flags = 0;  	call->class->reg = kprobe_register; -	call->data = tp; +	call->data = tk;  	ret = trace_add_event_call(call);  	if (ret) { -		pr_info("Failed to register kprobe event: %s\n", call->name); +		pr_info("Failed to register kprobe event: %s\n", +			ftrace_event_name(call));  		kfree(call->print_fmt);  		unregister_ftrace_event(&call->event);  	}  	return ret;  } -static void unregister_probe_event(struct trace_probe *tp) +static int unregister_kprobe_event(struct trace_kprobe *tk)  { +	int ret; +  	/* tp->event is unregistered in trace_remove_event_call() */ -	trace_remove_event_call(&tp->call); -	kfree(tp->call.print_fmt); +	ret = trace_remove_event_call(&tk->tp.call); +	if (!ret) +		kfree(tk->tp.call.print_fmt); +	return ret;  } -/* Make a debugfs interface for controling probe points */ +/* Make a debugfs interface for controlling probe points */  static __init int init_kprobe_trace(void)  {  	struct dentry *d_tracer;  	struct dentry *entry; +	if (register_module_notifier(&trace_kprobe_module_nb)) +		return -EINVAL; +  	d_tracer = tracing_init_dentry();  	if (!d_tracer)  		return 0; @@ -1770,50 +1355,87 @@ fs_initcall(init_kprobe_trace);  #ifdef CONFIG_FTRACE_STARTUP_TEST -static int kprobe_trace_selftest_target(int a1, int a2, int a3, -					int a4, int a5, int a6) +/* + * The "__used" keeps gcc from removing the function symbol + * from the kallsyms table. + */ +static __used int kprobe_trace_selftest_target(int a1, int a2, int a3, +					       int a4, int a5, int a6)  {  	return a1 + a2 + a3 + a4 + a5 + a6;  } +static struct ftrace_event_file * +find_trace_probe_file(struct trace_kprobe *tk, struct trace_array *tr) +{ +	struct ftrace_event_file *file; + +	list_for_each_entry(file, &tr->events, list) +		if (file->event_call == &tk->tp.call) +			return file; + +	return NULL; +} + +/* + * Nobody but us can call enable_trace_kprobe/disable_trace_kprobe at this + * stage, we can do this lockless. + */  static __init int kprobe_trace_self_tests_init(void)  {  	int ret, warn = 0;  	int (*target)(int, int, int, int, int, int); -	struct trace_probe *tp; +	struct trace_kprobe *tk; +	struct ftrace_event_file *file; + +	if (tracing_is_disabled()) +		return -ENODEV;  	target = kprobe_trace_selftest_target;  	pr_info("Testing kprobe tracing: "); -	ret = command_trace_probe("p:testprobe kprobe_trace_selftest_target " -				  "$stack $stack0 +0($stack)"); +	ret = traceprobe_command("p:testprobe kprobe_trace_selftest_target " +				  "$stack $stack0 +0($stack)", +				  create_trace_kprobe);  	if (WARN_ON_ONCE(ret)) { -		pr_warning("error on probing function entry.\n"); +		pr_warn("error on probing function entry.\n");  		warn++;  	} else {  		/* Enable trace point */ -		tp = find_probe_event("testprobe", KPROBE_EVENT_SYSTEM); -		if (WARN_ON_ONCE(tp == NULL)) { -			pr_warning("error on getting new probe.\n"); +		tk = find_trace_kprobe("testprobe", KPROBE_EVENT_SYSTEM); +		if (WARN_ON_ONCE(tk == NULL)) { +			pr_warn("error on getting new probe.\n");  			warn++; -		} else -			probe_event_enable(&tp->call); +		} else { +			file = find_trace_probe_file(tk, top_trace_array()); +			if (WARN_ON_ONCE(file == NULL)) { +				pr_warn("error on getting probe file.\n"); +				warn++; +			} else +				enable_trace_kprobe(tk, file); +		}  	} -	ret = command_trace_probe("r:testprobe2 kprobe_trace_selftest_target " -				  "$retval"); +	ret = traceprobe_command("r:testprobe2 kprobe_trace_selftest_target " +				  "$retval", create_trace_kprobe);  	if (WARN_ON_ONCE(ret)) { -		pr_warning("error on probing function return.\n"); +		pr_warn("error on probing function return.\n");  		warn++;  	} else {  		/* Enable trace point */ -		tp = find_probe_event("testprobe2", KPROBE_EVENT_SYSTEM); -		if (WARN_ON_ONCE(tp == NULL)) { -			pr_warning("error on getting new probe.\n"); +		tk = find_trace_kprobe("testprobe2", KPROBE_EVENT_SYSTEM); +		if (WARN_ON_ONCE(tk == NULL)) { +			pr_warn("error on getting 2nd new probe.\n");  			warn++; -		} else -			probe_event_enable(&tp->call); +		} else { +			file = find_trace_probe_file(tk, top_trace_array()); +			if (WARN_ON_ONCE(file == NULL)) { +				pr_warn("error on getting probe file.\n"); +				warn++; +			} else +				enable_trace_kprobe(tk, file); +		}  	}  	if (warn) @@ -1821,20 +1443,47 @@ static __init int kprobe_trace_self_tests_init(void)  	ret = target(1, 2, 3, 4, 5, 6); -	ret = command_trace_probe("-:testprobe"); +	/* Disable trace points before removing it */ +	tk = find_trace_kprobe("testprobe", KPROBE_EVENT_SYSTEM); +	if (WARN_ON_ONCE(tk == NULL)) { +		pr_warn("error on getting test probe.\n"); +		warn++; +	} else { +		file = find_trace_probe_file(tk, top_trace_array()); +		if (WARN_ON_ONCE(file == NULL)) { +			pr_warn("error on getting probe file.\n"); +			warn++; +		} else +			disable_trace_kprobe(tk, file); +	} + +	tk = find_trace_kprobe("testprobe2", KPROBE_EVENT_SYSTEM); +	if (WARN_ON_ONCE(tk == NULL)) { +		pr_warn("error on getting 2nd test probe.\n"); +		warn++; +	} else { +		file = find_trace_probe_file(tk, top_trace_array()); +		if (WARN_ON_ONCE(file == NULL)) { +			pr_warn("error on getting probe file.\n"); +			warn++; +		} else +			disable_trace_kprobe(tk, file); +	} + +	ret = traceprobe_command("-:testprobe", create_trace_kprobe);  	if (WARN_ON_ONCE(ret)) { -		pr_warning("error on deleting a probe.\n"); +		pr_warn("error on deleting a probe.\n");  		warn++;  	} -	ret = command_trace_probe("-:testprobe2"); +	ret = traceprobe_command("-:testprobe2", create_trace_kprobe);  	if (WARN_ON_ONCE(ret)) { -		pr_warning("error on deleting a probe.\n"); +		pr_warn("error on deleting a probe.\n");  		warn++;  	}  end: -	cleanup_all_probes(); +	release_all_trace_kprobes();  	if (warn)  		pr_cont("NG: Some tests are failed. Please check them.\n");  	else  | 
