diff options
Diffstat (limited to 'kernel/trace/trace_fprobe.c')
| -rw-r--r-- | kernel/trace/trace_fprobe.c | 614 | 
1 files changed, 412 insertions, 202 deletions
| diff --git a/kernel/trace/trace_fprobe.c b/kernel/trace/trace_fprobe.c index b40fa59159ac..b36ade43d4b3 100644 --- a/kernel/trace/trace_fprobe.c +++ b/kernel/trace/trace_fprobe.c @@ -4,15 +4,18 @@   * Copyright (C) 2022 Google LLC.   */  #define pr_fmt(fmt)	"trace_fprobe: " fmt -#include <asm/ptrace.h>  #include <linux/fprobe.h> +#include <linux/list.h>  #include <linux/module.h> +#include <linux/mutex.h>  #include <linux/rculist.h>  #include <linux/security.h>  #include <linux/tracepoint.h>  #include <linux/uaccess.h> +#include <asm/ptrace.h> +  #include "trace_dynevent.h"  #include "trace_probe.h"  #include "trace_probe_kernel.h" @@ -21,7 +24,6 @@  #define FPROBE_EVENT_SYSTEM "fprobes"  #define TRACEPOINT_EVENT_SYSTEM "tracepoints"  #define RETHOOK_MAXACTIVE_MAX 4096 -#define TRACEPOINT_STUB ERR_PTR(-ENOENT)  static int trace_fprobe_create(const char *raw_command);  static int trace_fprobe_show(struct seq_file *m, struct dyn_event *ev); @@ -38,15 +40,156 @@ static struct dyn_event_operations trace_fprobe_ops = {  	.match = trace_fprobe_match,  }; +/* List of tracepoint_user */ +static LIST_HEAD(tracepoint_user_list); +static DEFINE_MUTEX(tracepoint_user_mutex); + +/* While living tracepoint_user, @tpoint can be NULL and @refcount != 0. */ +struct tracepoint_user { +	struct list_head	list; +	const char		*name; +	struct tracepoint	*tpoint; +	unsigned int		refcount; +}; + +/* NOTE: you must lock tracepoint_user_mutex. */ +#define for_each_tracepoint_user(tuser)		\ +	list_for_each_entry(tuser, &tracepoint_user_list, list) + +static int tracepoint_user_register(struct tracepoint_user *tuser) +{ +	struct tracepoint *tpoint = tuser->tpoint; + +	if (!tpoint) +		return 0; + +	return tracepoint_probe_register_prio_may_exist(tpoint, +					tpoint->probestub, NULL, 0); +} + +static void tracepoint_user_unregister(struct tracepoint_user *tuser) +{ +	if (!tuser->tpoint) +		return; + +	WARN_ON_ONCE(tracepoint_probe_unregister(tuser->tpoint, tuser->tpoint->probestub, NULL)); +	tuser->tpoint = NULL; +} + +static unsigned long tracepoint_user_ip(struct tracepoint_user *tuser) +{ +	if (!tuser->tpoint) +		return 0UL; + +	return (unsigned long)tuser->tpoint->probestub; +} + +static void __tracepoint_user_free(struct tracepoint_user *tuser) +{ +	if (!tuser) +		return; +	kfree(tuser->name); +	kfree(tuser); +} + +DEFINE_FREE(tuser_free, struct tracepoint_user *, __tracepoint_user_free(_T)) + +static struct tracepoint_user *__tracepoint_user_init(const char *name, struct tracepoint *tpoint) +{ +	struct tracepoint_user *tuser __free(tuser_free) = NULL; +	int ret; + +	tuser = kzalloc(sizeof(*tuser), GFP_KERNEL); +	if (!tuser) +		return NULL; +	tuser->name = kstrdup(name, GFP_KERNEL); +	if (!tuser->name) +		return NULL; + +	if (tpoint) { +		ret = tracepoint_user_register(tuser); +		if (ret) +			return ERR_PTR(ret); +	} + +	tuser->tpoint = tpoint; +	tuser->refcount = 1; +	INIT_LIST_HEAD(&tuser->list); +	list_add(&tuser->list, &tracepoint_user_list); + +	return_ptr(tuser); +} + +static struct tracepoint *find_tracepoint(const char *tp_name, +	struct module **tp_mod); + +/* + * Get tracepoint_user if exist, or allocate new one and register it. + * If tracepoint is on a module, get its refcounter too. + * This returns errno or NULL (not loaded yet) or tracepoint_user. + */ +static struct tracepoint_user *tracepoint_user_find_get(const char *name, struct module **pmod) +{ +	struct module *mod __free(module_put) = NULL; +	struct tracepoint_user *tuser; +	struct tracepoint *tpoint; + +	if (!name || !pmod) +		return ERR_PTR(-EINVAL); + +	/* Get and lock the module which has tracepoint. */ +	tpoint = find_tracepoint(name, &mod); + +	guard(mutex)(&tracepoint_user_mutex); +	/* Search existing tracepoint_user */ +	for_each_tracepoint_user(tuser) { +		if (!strcmp(tuser->name, name)) { +			tuser->refcount++; +			*pmod = no_free_ptr(mod); +			return tuser; +		} +	} + +	/* The corresponding tracepoint_user is not found. */ +	tuser = __tracepoint_user_init(name, tpoint); +	if (!IS_ERR_OR_NULL(tuser)) +		*pmod = no_free_ptr(mod); + +	return tuser; +} + +static void tracepoint_user_put(struct tracepoint_user *tuser) +{ +	scoped_guard(mutex, &tracepoint_user_mutex) { +		if (--tuser->refcount > 0) +			return; + +		list_del(&tuser->list); +		tracepoint_user_unregister(tuser); +	} + +	__tracepoint_user_free(tuser); +} + +DEFINE_FREE(tuser_put, struct tracepoint_user *, +	if (!IS_ERR_OR_NULL(_T)) +		tracepoint_user_put(_T)) +  /*   * Fprobe event core functions   */ + +/* + * @tprobe is true for tracepoint probe. + * @tuser can be NULL if the trace_fprobe is disabled or the tracepoint is not + * loaded with a module. If @tuser != NULL, this trace_fprobe is enabled. + */  struct trace_fprobe {  	struct dyn_event	devent;  	struct fprobe		fp;  	const char		*symbol; -	struct tracepoint	*tpoint; -	struct module		*mod; +	bool			tprobe; +	struct tracepoint_user	*tuser;  	struct trace_probe	tp;  }; @@ -76,7 +219,7 @@ static bool trace_fprobe_is_return(struct trace_fprobe *tf)  static bool trace_fprobe_is_tracepoint(struct trace_fprobe *tf)  { -	return tf->tpoint != NULL; +	return tf->tprobe;  }  static const char *trace_fprobe_symbol(struct trace_fprobe *tf) @@ -411,6 +554,8 @@ static void free_trace_fprobe(struct trace_fprobe *tf)  {  	if (tf) {  		trace_probe_cleanup(&tf->tp); +		if (tf->tuser) +			tracepoint_user_put(tf->tuser);  		kfree(tf->symbol);  		kfree(tf);  	} @@ -425,9 +570,8 @@ DEFINE_FREE(free_trace_fprobe, struct trace_fprobe *, if (!IS_ERR_OR_NULL(_T)) f  static struct trace_fprobe *alloc_trace_fprobe(const char *group,  					       const char *event,  					       const char *symbol, -					       struct tracepoint *tpoint, -					       struct module *mod, -					       int nargs, bool is_return) +					       int nargs, bool is_return, +					       bool is_tracepoint)  {  	struct trace_fprobe *tf __free(free_trace_fprobe) = NULL;  	int ret = -ENOMEM; @@ -445,8 +589,7 @@ static struct trace_fprobe *alloc_trace_fprobe(const char *group,  	else  		tf->fp.entry_handler = fentry_dispatcher; -	tf->tpoint = tpoint; -	tf->mod = mod; +	tf->tprobe = is_tracepoint;  	ret = trace_probe_init(&tf->tp, event, group, false, nargs);  	if (ret < 0) @@ -469,98 +612,6 @@ static struct trace_fprobe *find_trace_fprobe(const char *event,  	return NULL;  } -static inline int __enable_trace_fprobe(struct trace_fprobe *tf) -{ -	if (trace_fprobe_is_registered(tf)) -		enable_fprobe(&tf->fp); - -	return 0; -} - -static void __disable_trace_fprobe(struct trace_probe *tp) -{ -	struct trace_fprobe *tf; - -	list_for_each_entry(tf, trace_probe_probe_list(tp), tp.list) { -		if (!trace_fprobe_is_registered(tf)) -			continue; -		disable_fprobe(&tf->fp); -	} -} - -/* - * Enable trace_probe - * if the file is NULL, enable "perf" handler, or enable "trace" handler. - */ -static int enable_trace_fprobe(struct trace_event_call *call, -			       struct trace_event_file *file) -{ -	struct trace_probe *tp; -	struct trace_fprobe *tf; -	bool enabled; -	int ret = 0; - -	tp = trace_probe_primary_from_call(call); -	if (WARN_ON_ONCE(!tp)) -		return -ENODEV; -	enabled = trace_probe_is_enabled(tp); - -	/* This also changes "enabled" state */ -	if (file) { -		ret = trace_probe_add_file(tp, file); -		if (ret) -			return ret; -	} else -		trace_probe_set_flag(tp, TP_FLAG_PROFILE); - -	if (!enabled) { -		list_for_each_entry(tf, trace_probe_probe_list(tp), tp.list) { -			/* TODO: check the fprobe is gone */ -			__enable_trace_fprobe(tf); -		} -	} - -	return 0; -} - -/* - * Disable trace_probe - * if the file is NULL, disable "perf" handler, or disable "trace" handler. - */ -static int disable_trace_fprobe(struct trace_event_call *call, -				struct trace_event_file *file) -{ -	struct trace_probe *tp; - -	tp = trace_probe_primary_from_call(call); -	if (WARN_ON_ONCE(!tp)) -		return -ENODEV; - -	if (file) { -		if (!trace_probe_get_file_link(tp, file)) -			return -ENOENT; -		if (!trace_probe_has_single_file(tp)) -			goto out; -		trace_probe_clear_flag(tp, TP_FLAG_TRACE); -	} else -		trace_probe_clear_flag(tp, TP_FLAG_PROFILE); - -	if (!trace_probe_is_enabled(tp)) -		__disable_trace_fprobe(tp); - - out: -	if (file) -		/* -		 * Synchronization is done in below function. For perf event, -		 * file == NULL and perf_trace_event_unreg() calls -		 * tracepoint_synchronize_unregister() to ensure synchronize -		 * event. We don't need to care about it. -		 */ -		trace_probe_remove_file(tp, file); - -	return 0; -} -  /* Event entry printers */  static enum print_line_t  print_fentry_event(struct trace_iterator *iter, int flags, @@ -712,20 +763,52 @@ static int unregister_fprobe_event(struct trace_fprobe *tf)  static int __regsiter_tracepoint_fprobe(struct trace_fprobe *tf)  { -	struct tracepoint *tpoint = tf->tpoint; -	unsigned long ip = (unsigned long)tpoint->probestub; +	struct tracepoint_user *tuser __free(tuser_put) = NULL; +	struct module *mod __free(module_put) = NULL; +	unsigned long ip;  	int ret; +	if (WARN_ON_ONCE(tf->tuser)) +		return -EINVAL; + +	/* If the tracepoint is in a module, it must be locked in this function. */ +	tuser = tracepoint_user_find_get(tf->symbol, &mod); +	/* This tracepoint is not loaded yet */ +	if (IS_ERR(tuser)) +		return PTR_ERR(tuser); +	if (!tuser) +		return -ENOMEM; + +	/* Register fprobe only if the tracepoint is loaded. */ +	if (tuser->tpoint) { +		ip = tracepoint_user_ip(tuser); +		if (WARN_ON_ONCE(!ip)) +			return -ENOENT; + +		ret = register_fprobe_ips(&tf->fp, &ip, 1); +		if (ret < 0) +			return ret; +	} + +	tf->tuser = no_free_ptr(tuser); +	return 0; +} + +/* Returns an error if the target function is not available, or 0 */ +static int trace_fprobe_verify_target(struct trace_fprobe *tf) +{ +	int ret; + +	/* Tracepoint should have a stub function. */ +	if (trace_fprobe_is_tracepoint(tf)) +		return 0; +  	/* -	 * Here, we do 2 steps to enable fprobe on a tracepoint. -	 * At first, put __probestub_##TP function on the tracepoint -	 * and put a fprobe on the stub function. +	 * Note: since we don't lock the module, even if this succeeded, +	 * register_fprobe() later can fail.  	 */ -	ret = tracepoint_probe_register_prio_may_exist(tpoint, -				tpoint->probestub, NULL, 0); -	if (ret < 0) -		return ret; -	return register_fprobe_ips(&tf->fp, &ip, 1); +	ret = fprobe_count_ips_from_filter(tf->symbol, NULL); +	return (ret < 0) ? ret : 0;  }  /* Internal register function - just handle fprobe and flags */ @@ -747,20 +830,10 @@ static int __register_trace_fprobe(struct trace_fprobe *tf)  			return ret;  	} -	/* Set/clear disabled flag according to tp->flag */ -	if (trace_probe_is_enabled(&tf->tp)) -		tf->fp.flags &= ~FPROBE_FL_DISABLED; -	else -		tf->fp.flags |= FPROBE_FL_DISABLED; - -	if (trace_fprobe_is_tracepoint(tf)) { - -		/* This tracepoint is not loaded yet */ -		if (tf->tpoint == TRACEPOINT_STUB) -			return 0; +	tf->fp.flags &= ~FPROBE_FL_DISABLED; +	if (trace_fprobe_is_tracepoint(tf))  		return __regsiter_tracepoint_fprobe(tf); -	}  	/* TODO: handle filter, nofilter or symbol list */  	return register_fprobe(&tf->fp, tf->symbol, NULL); @@ -769,15 +842,11 @@ static int __register_trace_fprobe(struct trace_fprobe *tf)  /* Internal unregister function - just handle fprobe and flags */  static void __unregister_trace_fprobe(struct trace_fprobe *tf)  { -	if (trace_fprobe_is_registered(tf)) { +	if (trace_fprobe_is_registered(tf))  		unregister_fprobe(&tf->fp); -		memset(&tf->fp, 0, sizeof(tf->fp)); -		if (trace_fprobe_is_tracepoint(tf)) { -			tracepoint_probe_unregister(tf->tpoint, -					tf->tpoint->probestub, NULL); -			tf->tpoint = NULL; -			tf->mod = NULL; -		} +	if (tf->tuser) { +		tracepoint_user_put(tf->tuser); +		tf->tuser = NULL;  	}  } @@ -837,7 +906,7 @@ static bool trace_fprobe_has_same_fprobe(struct trace_fprobe *orig,  	return false;  } -static int append_trace_fprobe(struct trace_fprobe *tf, struct trace_fprobe *to) +static int append_trace_fprobe_event(struct trace_fprobe *tf, struct trace_fprobe *to)  {  	int ret; @@ -865,7 +934,7 @@ static int append_trace_fprobe(struct trace_fprobe *tf, struct trace_fprobe *to)  	if (ret)  		return ret; -	ret = __register_trace_fprobe(tf); +	ret = trace_fprobe_verify_target(tf);  	if (ret)  		trace_probe_unlink(&tf->tp);  	else @@ -874,8 +943,8 @@ static int append_trace_fprobe(struct trace_fprobe *tf, struct trace_fprobe *to)  	return ret;  } -/* Register a trace_probe and probe_event */ -static int register_trace_fprobe(struct trace_fprobe *tf) +/* Register a trace_probe and probe_event, and check the fprobe is available. */ +static int register_trace_fprobe_event(struct trace_fprobe *tf)  {  	struct trace_fprobe *old_tf;  	int ret; @@ -885,7 +954,7 @@ static int register_trace_fprobe(struct trace_fprobe *tf)  	old_tf = find_trace_fprobe(trace_probe_name(&tf->tp),  				   trace_probe_group_name(&tf->tp));  	if (old_tf) -		return append_trace_fprobe(tf, old_tf); +		return append_trace_fprobe_event(tf, old_tf);  	/* Register new event */  	ret = register_fprobe_event(tf); @@ -898,8 +967,8 @@ static int register_trace_fprobe(struct trace_fprobe *tf)  		return ret;  	} -	/* Register fprobe */ -	ret = __register_trace_fprobe(tf); +	/* Verify fprobe is sane. */ +	ret = trace_fprobe_verify_target(tf);  	if (ret < 0)  		unregister_fprobe_event(tf);  	else @@ -963,15 +1032,6 @@ static struct tracepoint *find_tracepoint(const char *tp_name,  }  #ifdef CONFIG_MODULES -static void reenable_trace_fprobe(struct trace_fprobe *tf) -{ -	struct trace_probe *tp = &tf->tp; - -	list_for_each_entry(tf, trace_probe_probe_list(tp), tp.list) { -		__enable_trace_fprobe(tf); -	} -} -  /*   * Find a tracepoint from specified module. In this case, this does not get the   * module's refcount. The caller must ensure the module is not freed. @@ -988,36 +1048,94 @@ static struct tracepoint *find_tracepoint_in_module(struct module *mod,  	return data.tpoint;  } +/* These are CONFIG_MODULES=y specific functions. */ +static bool tracepoint_user_within_module(struct tracepoint_user *tuser, +					  struct module *mod) +{ +	return within_module(tracepoint_user_ip(tuser), mod); +} + +static int tracepoint_user_register_again(struct tracepoint_user *tuser, +					  struct tracepoint *tpoint) +{ +	tuser->tpoint = tpoint; +	return tracepoint_user_register(tuser); +} + +static void tracepoint_user_unregister_clear(struct tracepoint_user *tuser) +{ +	tracepoint_user_unregister(tuser); +	tuser->tpoint = NULL; +} + +/* module callback for tracepoint_user */  static int __tracepoint_probe_module_cb(struct notifier_block *self,  					unsigned long val, void *data)  {  	struct tp_module *tp_mod = data; +	struct tracepoint_user *tuser;  	struct tracepoint *tpoint; + +	if (val != MODULE_STATE_GOING && val != MODULE_STATE_COMING) +		return NOTIFY_DONE; + +	mutex_lock(&tracepoint_user_mutex); +	for_each_tracepoint_user(tuser) { +		if (val == MODULE_STATE_COMING) { +			/* This is not a tracepoint in this module. Skip it. */ +			tpoint = find_tracepoint_in_module(tp_mod->mod, tuser->name); +			if (!tpoint) +				continue; +			WARN_ON_ONCE(tracepoint_user_register_again(tuser, tpoint)); +		} else if (val == MODULE_STATE_GOING && +			  tracepoint_user_within_module(tuser, tp_mod->mod)) { +			/* Unregister all tracepoint_user in this module. */ +			tracepoint_user_unregister_clear(tuser); +		} +	} +	mutex_unlock(&tracepoint_user_mutex); + +	return NOTIFY_DONE; +} + +static struct notifier_block tracepoint_module_nb = { +	.notifier_call = __tracepoint_probe_module_cb, +}; + +/* module callback for tprobe events */ +static int __tprobe_event_module_cb(struct notifier_block *self, +				     unsigned long val, void *data) +{  	struct trace_fprobe *tf;  	struct dyn_event *pos; +	struct module *mod = data;  	if (val != MODULE_STATE_GOING && val != MODULE_STATE_COMING)  		return NOTIFY_DONE;  	mutex_lock(&event_mutex);  	for_each_trace_fprobe(tf, pos) { -		if (val == MODULE_STATE_COMING && tf->tpoint == TRACEPOINT_STUB) { -			tpoint = find_tracepoint_in_module(tp_mod->mod, tf->symbol); -			if (tpoint) { -				tf->tpoint = tpoint; -				tf->mod = tp_mod->mod; -				if (!WARN_ON_ONCE(__regsiter_tracepoint_fprobe(tf)) && -				    trace_probe_is_enabled(&tf->tp)) -					reenable_trace_fprobe(tf); -			} -		} else if (val == MODULE_STATE_GOING && tp_mod->mod == tf->mod) { +		/* Skip fprobe and disabled tprobe events. */ +		if (!trace_fprobe_is_tracepoint(tf) || !tf->tuser) +			continue; + +		/* Before this notification, tracepoint notifier has already done. */ +		if (val == MODULE_STATE_COMING && +		    tracepoint_user_within_module(tf->tuser, mod)) { +			unsigned long ip = tracepoint_user_ip(tf->tuser); + +			WARN_ON_ONCE(register_fprobe_ips(&tf->fp, &ip, 1)); +		} else if (val == MODULE_STATE_GOING && +			   /* +			    * tracepoint_user_within_module() does not work here because +			    * tracepoint_user is already unregistered and cleared tpoint. +			    * Instead, checking whether the fprobe is registered but +			    * tpoint is cleared(unregistered). Such unbalance probes +			    * must be adjusted anyway. +			    */ +			    trace_fprobe_is_registered(tf) && +			    !tf->tuser->tpoint) {  			unregister_fprobe(&tf->fp); -			if (trace_fprobe_is_tracepoint(tf)) { -				tracepoint_probe_unregister(tf->tpoint, -					tf->tpoint->probestub, NULL); -				tf->tpoint = TRACEPOINT_STUB; -				tf->mod = NULL; -			}  		}  	}  	mutex_unlock(&event_mutex); @@ -1025,8 +1143,11 @@ static int __tracepoint_probe_module_cb(struct notifier_block *self,  	return NOTIFY_DONE;  } -static struct notifier_block tracepoint_module_nb = { -	.notifier_call = __tracepoint_probe_module_cb, +/* NOTE: this must be called after tracepoint callback */ +static struct notifier_block tprobe_event_module_nb = { +	.notifier_call = __tprobe_event_module_cb, +	/* Make sure this is later than tracepoint module notifier. */ +	.priority = -10,  };  #endif /* CONFIG_MODULES */ @@ -1086,8 +1207,6 @@ static int parse_symbol_and_return(int argc, const char *argv[],  	return 0;  } -DEFINE_FREE(module_put, struct module *, if (_T) module_put(_T)) -  static int trace_fprobe_create_internal(int argc, const char *argv[],  					struct traceprobe_parse_context *ctx)  { @@ -1116,19 +1235,18 @@ static int trace_fprobe_create_internal(int argc, const char *argv[],  	 *  FETCHARG:TYPE : use TYPE instead of unsigned long.  	 */  	struct trace_fprobe *tf __free(free_trace_fprobe) = NULL; -	int i, new_argc = 0, ret = 0; -	bool is_return = false; -	char *symbol __free(kfree) = NULL;  	const char *event = NULL, *group = FPROBE_EVENT_SYSTEM; +	struct module *mod __free(module_put) = NULL;  	const char **new_argv __free(kfree) = NULL; -	char buf[MAX_EVENT_NAME_LEN]; -	char gbuf[MAX_EVENT_NAME_LEN]; -	char sbuf[KSYM_NAME_LEN]; -	char abuf[MAX_BTF_ARGS_LEN]; +	char *symbol __free(kfree) = NULL; +	char *ebuf __free(kfree) = NULL; +	char *gbuf __free(kfree) = NULL; +	char *sbuf __free(kfree) = NULL; +	char *abuf __free(kfree) = NULL;  	char *dbuf __free(kfree) = NULL; +	int i, new_argc = 0, ret = 0;  	bool is_tracepoint = false; -	struct module *tp_mod __free(module_put) = NULL; -	struct tracepoint *tpoint = NULL; +	bool is_return = false;  	if ((argv[0][0] != 'f' && argv[0][0] != 't') || argc < 2)  		return -ECANCELED; @@ -1156,6 +1274,9 @@ static int trace_fprobe_create_internal(int argc, const char *argv[],  	trace_probe_log_set_index(0);  	if (event) { +		gbuf = kmalloc(MAX_EVENT_NAME_LEN, GFP_KERNEL); +		if (!gbuf) +			return -ENOMEM;  		ret = traceprobe_parse_event_name(&event, &group, gbuf,  						  event - argv[0]);  		if (ret) @@ -1163,15 +1284,18 @@ static int trace_fprobe_create_internal(int argc, const char *argv[],  	}  	if (!event) { +		ebuf = kmalloc(MAX_EVENT_NAME_LEN, GFP_KERNEL); +		if (!ebuf) +			return -ENOMEM;  		/* Make a new event name */  		if (is_tracepoint) -			snprintf(buf, MAX_EVENT_NAME_LEN, "%s%s", +			snprintf(ebuf, MAX_EVENT_NAME_LEN, "%s%s",  				 isdigit(*symbol) ? "_" : "", symbol);  		else -			snprintf(buf, MAX_EVENT_NAME_LEN, "%s__%s", symbol, +			snprintf(ebuf, MAX_EVENT_NAME_LEN, "%s__%s", symbol,  				 is_return ? "exit" : "entry"); -		sanitize_event_name(buf); -		event = buf; +		sanitize_event_name(ebuf); +		event = ebuf;  	}  	if (is_return) @@ -1179,25 +1303,28 @@ static int trace_fprobe_create_internal(int argc, const char *argv[],  	else  		ctx->flags |= TPARG_FL_FENTRY; +	ctx->funcname = NULL;  	if (is_tracepoint) { +		/* Get tracepoint and lock its module until the end of the registration. */ +		struct tracepoint *tpoint; +  		ctx->flags |= TPARG_FL_TPOINT; -		tpoint = find_tracepoint(symbol, &tp_mod); +		mod = NULL; +		tpoint = find_tracepoint(symbol, &mod);  		if (tpoint) { -			ctx->funcname = kallsyms_lookup( -				(unsigned long)tpoint->probestub, -				NULL, NULL, NULL, sbuf); -		} else if (IS_ENABLED(CONFIG_MODULES)) { -				/* This *may* be loaded afterwards */ -				tpoint = TRACEPOINT_STUB; -				ctx->funcname = symbol; -		} else { -			trace_probe_log_set_index(1); -			trace_probe_log_err(0, NO_TRACEPOINT); -			return -EINVAL; +			sbuf = kmalloc(KSYM_NAME_LEN, GFP_KERNEL); +			if (!sbuf) +				return -ENOMEM; +			ctx->funcname = kallsyms_lookup((unsigned long)tpoint->probestub, +							NULL, NULL, NULL, sbuf);  		} -	} else +	} +	if (!ctx->funcname)  		ctx->funcname = symbol; +	abuf = kmalloc(MAX_BTF_ARGS_LEN, GFP_KERNEL); +	if (!abuf) +		return -ENOMEM;  	argc -= 2; argv += 2;  	new_argv = traceprobe_expand_meta_args(argc, argv, &new_argc,  					       abuf, MAX_BTF_ARGS_LEN, ctx); @@ -1218,8 +1345,7 @@ static int trace_fprobe_create_internal(int argc, const char *argv[],  		return ret;  	/* setup a probe */ -	tf = alloc_trace_fprobe(group, event, symbol, tpoint, tp_mod, -				argc, is_return); +	tf = alloc_trace_fprobe(group, event, symbol, argc, is_return, is_tracepoint);  	if (IS_ERR(tf)) {  		ret = PTR_ERR(tf);  		/* This must return -ENOMEM, else there is a bug */ @@ -1251,7 +1377,7 @@ static int trace_fprobe_create_internal(int argc, const char *argv[],  	if (ret < 0)  		return ret; -	ret = register_trace_fprobe(tf); +	ret = register_trace_fprobe_event(tf);  	if (ret) {  		trace_probe_log_set_index(1);  		if (ret == -EILSEQ) @@ -1271,14 +1397,17 @@ static int trace_fprobe_create_internal(int argc, const char *argv[],  static int trace_fprobe_create_cb(int argc, const char *argv[])  { -	struct traceprobe_parse_context ctx = { -		.flags = TPARG_FL_KERNEL | TPARG_FL_FPROBE, -	}; +	struct traceprobe_parse_context *ctx __free(traceprobe_parse_context) = NULL;  	int ret; +	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); +	if (!ctx) +		return -ENOMEM; + +	ctx->flags = TPARG_FL_KERNEL | TPARG_FL_FPROBE; +  	trace_probe_log_init("trace_fprobe", argc, argv); -	ret = trace_fprobe_create_internal(argc, argv, &ctx); -	traceprobe_finish_parse(&ctx); +	ret = trace_fprobe_create_internal(argc, argv, ctx);  	trace_probe_log_clear();  	return ret;  } @@ -1321,6 +1450,84 @@ static int trace_fprobe_show(struct seq_file *m, struct dyn_event *ev)  }  /* + * Enable trace_probe + * if the file is NULL, enable "perf" handler, or enable "trace" handler. + */ +static int enable_trace_fprobe(struct trace_event_call *call, +			       struct trace_event_file *file) +{ +	struct trace_probe *tp; +	struct trace_fprobe *tf; +	bool enabled; +	int ret = 0; + +	tp = trace_probe_primary_from_call(call); +	if (WARN_ON_ONCE(!tp)) +		return -ENODEV; +	enabled = trace_probe_is_enabled(tp); + +	/* This also changes "enabled" state */ +	if (file) { +		ret = trace_probe_add_file(tp, file); +		if (ret) +			return ret; +	} else +		trace_probe_set_flag(tp, TP_FLAG_PROFILE); + +	if (!enabled) { +		list_for_each_entry(tf, trace_probe_probe_list(tp), tp.list) { +			ret = __register_trace_fprobe(tf); +			if (ret < 0) +				return ret; +		} +	} + +	return 0; +} + +/* + * Disable trace_probe + * if the file is NULL, disable "perf" handler, or disable "trace" handler. + */ +static int disable_trace_fprobe(struct trace_event_call *call, +				struct trace_event_file *file) +{ +	struct trace_fprobe *tf; +	struct trace_probe *tp; + +	tp = trace_probe_primary_from_call(call); +	if (WARN_ON_ONCE(!tp)) +		return -ENODEV; + +	if (file) { +		if (!trace_probe_get_file_link(tp, file)) +			return -ENOENT; +		if (!trace_probe_has_single_file(tp)) +			goto out; +		trace_probe_clear_flag(tp, TP_FLAG_TRACE); +	} else +		trace_probe_clear_flag(tp, TP_FLAG_PROFILE); + +	if (!trace_probe_is_enabled(tp)) { +		list_for_each_entry(tf, trace_probe_probe_list(tp), tp.list) { +			unregister_fprobe(&tf->fp); +		} +	} + + out: +	if (file) +		/* +		 * Synchronization is done in below function. For perf event, +		 * file == NULL and perf_trace_event_unreg() calls +		 * tracepoint_synchronize_unregister() to ensure synchronize +		 * event. We don't need to care about it. +		 */ +		trace_probe_remove_file(tp, file); + +	return 0; +} + +/*   * called by perf_trace_init() or __ftrace_set_clr_event() under event_mutex.   */  static int fprobe_register(struct trace_event_call *event, @@ -1365,6 +1572,9 @@ static __init int init_fprobe_trace_early(void)  	ret = register_tracepoint_module_notifier(&tracepoint_module_nb);  	if (ret)  		return ret; +	ret = register_module_notifier(&tprobe_event_module_nb); +	if (ret) +		return ret;  #endif  	return 0; | 
