From nobody Wed Dec 17 19:25:35 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id DDF7D21ABCC; Mon, 17 Mar 2025 08:03:20 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1742198601; cv=none; b=uRb9UWi7/jG9d6r+NcW5k7r+3I1hFKbNFr1djon0pdzGEYQSsfBizavSJq7FpyzXpEEbrQ7MCkCS0HBUlcdMP7/nExbs1U5hzsRlKS8zmfav1u40t2CqDapHKokOjvDahl+YwnZUqX5jUyDrS9rDa3jFSsWb9gIRd3UEYFD2Q5k= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1742198601; c=relaxed/simple; bh=tl7g7tnLHVkIpMwuft/E0gPaRqKFHy7aKobfLmft7/8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=i/8h1KPrJRFyC5HQyu3qLcX+HWWxCcMpHFmS8JlaF5uFX3YmL8e4Iq/z5616sQc4ADRD65x+mZW8adGu7p4O5TvSv2IBtC2kGMifG7/yprOvnzo1kTV6X4faee/FulvTkNBqrNN83XdgDgQc2wWlIxtXVx4ZJALcizqVdtF7TrI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=E6jyJ4Jg; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="E6jyJ4Jg" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 15EC3C4CEE3; Mon, 17 Mar 2025 08:03:18 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1742198600; bh=tl7g7tnLHVkIpMwuft/E0gPaRqKFHy7aKobfLmft7/8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=E6jyJ4JgnJqWcyOKsNzUpfJrRqsU4ztN8Bsdxrli9HjMUSaiWS+TslQaFbiK04kbG DBMjE6MKkz6WrGTXyc0cEJbx3kxbbRh0OVZtViS24SlaAGJ56HH2EL0rnUucwhJGTZ Xgh4wpDFezE8n/YOgJgt1QmpJ03Z3FXFJleaSnzeyEQEk8X2nZtS9u4EwujYcGD+37 VzM2PVRT6dhundulCq2Ys4MDvm88LheKBf41RnJDiruO2yimrXegJgfa5Z2Nffnz1I ya8d1FcbWQ0ucksPfjFjW9PNIs+HoU5kYlEO0WiOyCizzqqM1raaFi+xTKjfVH+6qF WjPWpvYbP7SCA== From: "Masami Hiramatsu (Google)" To: Steven Rostedt Cc: Masami Hiramatsu , Mathieu Desnoyers , linux-kernel@vger.kernel.org, linux-trace-kernel@vger.kernel.org Subject: [RFC PATCH] tracing: tprobe-events: Register tracepoint when enable tprobe event Date: Mon, 17 Mar 2025 17:03:16 +0900 Message-ID: <174219859655.549159.12584048778295152488.stgit@devnote2> X-Mailer: git-send-email 2.43.0 In-Reply-To: <174212767109.348872.18231451508464729427.stgit@devnote2> References: <174212767109.348872.18231451508464729427.stgit@devnote2> User-Agent: StGit/0.19 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable From: Masami Hiramatsu (Google) As same as fprobe, register tracepoint stub function only when enabling tprobe events. The major changes are introducing a list of tracepoint_user and its lock, and tprobe_event_module_nb, which is another module notifier for module loading/unloading. By spliting the lock from event_mutex and a module notifier for trace_fprobe, it solved AB-BA lock dependency issue between event_mutex and tracepoint_module_list_mutex. Signed-off-by: Masami Hiramatsu (Google) --- kernel/trace/trace_fprobe.c | 382 +++++++++++++++++++++++++--------------= ---- 1 file changed, 218 insertions(+), 164 deletions(-) diff --git a/kernel/trace/trace_fprobe.c b/kernel/trace/trace_fprobe.c index ac3b0a34810a..2c4352f4dc11 100644 --- a/kernel/trace/trace_fprobe.c +++ b/kernel/trace/trace_fprobe.c @@ -7,7 +7,9 @@ #include =20 #include +#include #include +#include #include #include #include @@ -37,15 +39,21 @@ static struct dyn_event_operations trace_fprobe_ops =3D= { .match =3D trace_fprobe_match, }; =20 +/* 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 !=3D 0.= */ struct tracepoint_user { + struct list_head list; + const char *name; struct tracepoint *tpoint; unsigned int refcount; }; =20 -static bool tracepoint_user_is_registered(struct tracepoint_user *tuser) -{ - return tuser && tuser->tpoint; -} +/* NOTE: you must lock tracepoint_user_mutex. */ +#define for_each_tracepoint_user(tuser) \ + list_for_each_entry(tuser, &tracepoint_user_list, list) =20 static int tracepoint_user_register(struct tracepoint_user *tuser) { @@ -75,58 +83,112 @@ static unsigned long tracepoint_user_ip(struct tracepo= int_user *tuser) return (unsigned long)tuser->tpoint->probestub; } =20 -static bool tracepoint_user_within_module(struct tracepoint_user *tuser, - struct module *mod) +static void __tracepoint_user_free(struct tracepoint_user *tuser) { - return within_module(tracepoint_user_ip(tuser), mod); + if (!tuser) + return; + kfree(tuser->name); + kfree(tuser); } =20 -static struct tracepoint_user *tracepoint_user_allocate(struct tracepoint = *tpoint) +DEFINE_FREE(tuser_free, struct tracepoint_user *, __tracepoint_user_free(_= T)) + +static struct tracepoint_user *__tracepoint_user_init(const char *name, st= ruct tracepoint *tpoint) { - struct tracepoint_user *tuser __free(kfree) =3D NULL; + struct tracepoint_user *tuser __free(tuser_free) =3D NULL; + int ret; =20 tuser =3D kzalloc(sizeof(*tuser), GFP_KERNEL); if (!tuser) return NULL; + tuser->name =3D kstrdup(name, GFP_KERNEL); + if (!tuser->name) + return NULL; + + if (tpoint) { + ret =3D tracepoint_user_register(tuser); + if (ret) + return ERR_PTR(ret); + } + tuser->tpoint =3D tpoint; tuser->refcount =3D 1; + INIT_LIST_HEAD(&tuser->list); + list_add(&tuser->list, &tracepoint_user_list); =20 return_ptr(tuser); } =20 -/* These must be called with event_mutex */ -static void tracepoint_user_get(struct tracepoint_user *tuser) -{ - tuser->refcount++; -} +static struct tracepoint *find_tracepoint(const char *tp_name, + struct module **tp_mod); =20 -static void tracepoint_user_put(struct tracepoint_user *tuser) +/* + * 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) { - if (--tuser->refcount > 0) - return; + struct module *mod __free(module_put) =3D NULL; + struct tracepoint_user *tuser; + struct tracepoint *tpoint; =20 - if (tracepoint_user_is_registered(tuser)) - tracepoint_user_unregister(tuser); - kfree(tuser); + /* Get and lock the module which has tracepoint. */ + tpoint =3D find_tracepoint(name, &mod); + if (mod && !try_module_get(mod)) { + mod =3D NULL; + tpoint =3D NULL; + } + + guard(mutex)(&tracepoint_user_mutex); + /* Search existing tracepoint_user */ + for_each_tracepoint_user(tuser) { + if (!strcmp(tuser->name, name)) { + tuser->refcount++; + *pmod =3D no_free_ptr(mod); + return tuser; + } + } + + /* The corresponding tracepoint_user is not found. */ + tuser =3D __tracepoint_user_init(name, tpoint); + if (!IS_ERR_OR_NULL(tuser)) + *pmod =3D no_free_ptr(mod); + + return tuser; } =20 -static const char *tracepoint_user_lookup(struct tracepoint_user *tuser, c= har *buf) +static void tracepoint_user_put(struct tracepoint_user *tuser) { - struct tracepoint *tpoint =3D tuser->tpoint; + scoped_guard(mutex, &tracepoint_user_mutex) { + if (--tuser->refcount > 0) + return; =20 - if (!tpoint) - return NULL; + list_del(&tuser->list); + tracepoint_user_unregister(tuser); + } =20 - return kallsyms_lookup((unsigned long)tpoint->probestub, NULL, NULL, NULL= , buf); + __tracepoint_user_free(tuser); } =20 +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 !=3D NULL, this trace_fprobe is enabled. + */ struct trace_fprobe { struct dyn_event devent; struct fprobe fp; const char *symbol; + bool tprobe; struct tracepoint_user *tuser; struct trace_probe tp; }; @@ -157,7 +219,7 @@ static bool trace_fprobe_is_return(struct trace_fprobe = *tf) =20 static bool trace_fprobe_is_tracepoint(struct trace_fprobe *tf) { - return tf->tuser !=3D NULL; + return tf->tprobe; } =20 static const char *trace_fprobe_symbol(struct trace_fprobe *tf) @@ -207,57 +269,6 @@ static bool trace_fprobe_is_registered(struct trace_fp= robe *tf) return fprobe_is_registered(&tf->fp); } =20 -static struct tracepoint *find_tracepoint(const char *tp_name, - struct module **tp_mod); - -/* - * Get tracepoint_user if exist, or allocate new one. If tracepoint is on a - * module, get its refcounter. - */ -static struct tracepoint_user * -trace_fprobe_get_tracepoint_user(const char *name, struct module **pmod) -{ - struct tracepoint_user *tuser __free(kfree) =3D NULL; - struct tracepoint *tpoint; - struct trace_fprobe *tf; - struct dyn_event *dpos; - struct module *mod __free(module_put) =3D NULL; - int ret; - - tpoint =3D find_tracepoint(name, &mod); - if (mod && !try_module_get(mod)) { - mod =3D NULL; - tpoint =3D NULL; - } - /* tpoint can be NULL, but we don't care here. */ - - /* Search existing tracepoint_user */ - for_each_trace_fprobe(tf, dpos) { - if (!trace_fprobe_is_tracepoint(tf)) - continue; - if (!strcmp(tf->symbol, name)) { - tracepoint_user_get(tf->tuser); - *pmod =3D no_free_ptr(mod); - return tf->tuser; - } - } - - /* Not found, allocate and register new tracepoint_user. */ - tuser =3D tracepoint_user_allocate(tpoint); - if (!tuser) - return NULL; - - if (tpoint) { - /* If the tracepoint is not loaded, tpoint can be NULL. */ - ret =3D tracepoint_user_register(tuser); - if (ret) - return ERR_PTR(ret); - } - - *pmod =3D no_free_ptr(mod); - return_ptr(tuser); -} - /* * Note that we don't verify the fetch_insn code, since it does not come * from user space. @@ -559,8 +570,8 @@ DEFINE_FREE(free_trace_fprobe, struct trace_fprobe *, i= f (!IS_ERR_OR_NULL(_T)) f static struct trace_fprobe *alloc_trace_fprobe(const char *group, const char *event, const char *symbol, - struct tracepoint_user *tuser, - int nargs, bool is_return) + int nargs, bool is_return, + bool is_tracepoint) { struct trace_fprobe *tf __free(free_trace_fprobe) =3D NULL; int ret =3D -ENOMEM; @@ -578,7 +589,7 @@ static struct trace_fprobe *alloc_trace_fprobe(const ch= ar *group, else tf->fp.entry_handler =3D fentry_dispatcher; =20 - tf->tuser =3D tuser; + tf->tprobe =3D is_tracepoint; =20 ret =3D trace_probe_init(&tf->tp, event, group, false, nargs); if (ret < 0) @@ -752,12 +763,35 @@ static int unregister_fprobe_event(struct trace_fprob= e *tf) =20 static int __regsiter_tracepoint_fprobe(struct trace_fprobe *tf) { - unsigned long ip =3D tracepoint_user_ip(tf->tuser); + struct tracepoint_user *tuser __free(tuser_put) =3D NULL; + struct module *mod __free(module_put) =3D NULL; + unsigned long ip; + int ret; =20 - if (!ip) - return -ENOENT; + if (WARN_ON_ONCE(tf->tuser)) + return -EINVAL; + + /* If the tracepoint is in a module, it must be locked in this function. = */ + tuser =3D 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 =3D tracepoint_user_ip(tuser); + if (WARN_ON_ONCE(!ip)) + return -ENOENT; =20 - return register_fprobe_ips(&tf->fp, &ip, 1); + ret =3D register_fprobe_ips(&tf->fp, &ip, 1); + if (ret < 0) + return ret; + } + + tf->tuser =3D no_free_ptr(tuser); + return 0; } =20 /* Returns an error if the target function is not available, or 0 */ @@ -766,15 +800,9 @@ static int trace_fprobe_verify_target(struct trace_fpr= obe *tf) unsigned long *addrs __free(kfree) =3D NULL; int ret; =20 - if (trace_fprobe_is_tracepoint(tf)) { - - /* This tracepoint is not loaded yet */ - if (!tracepoint_user_is_registered(tf->tuser)) - return 0; - - /* We assume all stab function is tracable. */ - return tracepoint_user_ip(tf->tuser) ? 0 : -ENOENT; - } + /* Tracepoint should have a stub function. */ + if (trace_fprobe_is_tracepoint(tf)) + return 0; =20 ret =3D fprobe_alloc_ip_list_from_filter(tf->symbol, NULL, &addrs); return (ret < 0) ? ret : 0; @@ -801,14 +829,8 @@ static int __register_trace_fprobe(struct trace_fprobe= *tf) =20 tf->fp.flags &=3D ~FPROBE_FL_DISABLED; =20 - if (trace_fprobe_is_tracepoint(tf)) { - - /* This tracepoint is not loaded yet */ - if (!tracepoint_user_is_registered(tf->tuser)) - return 0; - + if (trace_fprobe_is_tracepoint(tf)) return __regsiter_tracepoint_fprobe(tf); - } =20 /* TODO: handle filter, nofilter or symbol list */ return register_fprobe(&tf->fp, tf->symbol, NULL); @@ -817,11 +839,9 @@ 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)) { + if (tf->tuser) { tracepoint_user_put(tf->tuser); tf->tuser =3D NULL; } @@ -1012,63 +1032,52 @@ static struct tracepoint *find_tracepoint_in_module= (struct module *mod, return data.tpoint; } =20 +/* These are CONFIG_MODULES=3Dy 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 =3D tpoint; + return tracepoint_user_register(tuser); +} + +static void tracepoint_user_unregister_clear(struct tracepoint_user *tuser) +{ + tracepoint_user_unregister(tuser); + tuser->tpoint =3D 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 =3D data; struct tracepoint_user *tuser; - struct trace_fprobe *tf; - struct dyn_event *pos; + struct tracepoint *tpoint; =20 if (val !=3D MODULE_STATE_GOING && val !=3D MODULE_STATE_COMING) return NOTIFY_DONE; =20 - mutex_lock(&event_mutex); - for_each_trace_fprobe(tf, pos) { - if (!trace_fprobe_is_tracepoint(tf)) - continue; - + mutex_lock(&tracepoint_user_mutex); + for_each_tracepoint_user(tuser) { if (val =3D=3D MODULE_STATE_COMING) { - /* - * If any tracepoint used by tprobe is in the module, - * register the stub. - */ - struct tracepoint *tpoint; - - tpoint =3D find_tracepoint_in_module(tp_mod->mod, tf->symbol); /* This is not a tracepoint in this module. Skip it. */ + tpoint =3D find_tracepoint_in_module(tp_mod->mod, tuser->name); if (!tpoint) continue; - - tuser =3D tf->tuser; - /* If the tracepoint is no registered yet, register it. */ - if (!tracepoint_user_is_registered(tuser)) { - tuser->tpoint =3D tpoint; - if (WARN_ON_ONCE(tracepoint_user_register(tuser))) - continue; - } - - /* Finally enable fprobe on this module. */ - if (trace_probe_is_enabled(&tf->tp) && !trace_fprobe_is_registered(tf)) - WARN_ON_ONCE(__regsiter_tracepoint_fprobe(tf)); - } else if (val =3D=3D MODULE_STATE_GOING) { - tuser =3D tf->tuser; + WARN_ON_ONCE(tracepoint_user_register_again(tuser, tpoint)); + } else if (val =3D=3D MODULE_STATE_GOING && + tracepoint_user_within_module(tuser, tp_mod->mod)) { /* Unregister all tracepoint_user in this module. */ - if (tracepoint_user_is_registered(tuser) && - tracepoint_user_within_module(tuser, tp_mod->mod)) - tracepoint_user_unregister(tuser); - - /* - * Here we need to handle shared tracepoint_user case. - * Such tuser is unregistered, but trace_fprobe itself - * is registered. (Note this only handles tprobes.) - */ - if (!tracepoint_user_is_registered(tuser) && - trace_fprobe_is_registered(tf)) - unregister_fprobe(&tf->fp); + tracepoint_user_unregister_clear(tuser); } } - mutex_unlock(&event_mutex); + mutex_unlock(&tracepoint_user_mutex); =20 return NOTIFY_DONE; } @@ -1076,6 +1085,54 @@ static int __tracepoint_probe_module_cb(struct notif= ier_block *self, static struct notifier_block tracepoint_module_nb =3D { .notifier_call =3D __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 =3D data; + + if (val !=3D MODULE_STATE_GOING && val !=3D MODULE_STATE_COMING) + return NOTIFY_DONE; + + mutex_lock(&event_mutex); + for_each_trace_fprobe(tf, pos) { + /* 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 =3D=3D MODULE_STATE_COMING && + tracepoint_user_within_module(tf->tuser, mod)) { + unsigned long ip =3D tracepoint_user_ip(tf->tuser); + + WARN_ON_ONCE(register_fprobe_ips(&tf->fp, &ip, 1)); + } else if (val =3D=3D 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); + } + } + mutex_unlock(&event_mutex); + + return NOTIFY_DONE; +} + +/* NOTE: this must be called after tracepoint callback */ +static struct notifier_block tprobe_event_module_nb =3D { + .notifier_call =3D __tprobe_event_module_cb, + /* Make sure this is later than tracepoint module notifier. */ + .priority =3D -10, +}; #endif /* CONFIG_MODULES */ =20 static int parse_symbol_and_return(int argc, const char *argv[], @@ -1134,10 +1191,6 @@ static int parse_symbol_and_return(int argc, const c= har *argv[], return 0; } =20 -DEFINE_FREE(tuser_put, struct tracepoint_user *, - if (!IS_ERR_OR_NULL(_T)) - tracepoint_user_put(_T)) - static int trace_fprobe_create_internal(int argc, const char *argv[], struct traceprobe_parse_context *ctx) { @@ -1166,7 +1219,6 @@ static int trace_fprobe_create_internal(int argc, con= st char *argv[], * FETCHARG:TYPE : use TYPE instead of unsigned long. */ struct trace_fprobe *tf __free(free_trace_fprobe) =3D NULL; - struct tracepoint_user *tuser __free(tuser_put) =3D NULL; struct module *mod __free(module_put) =3D NULL; int i, new_argc =3D 0, ret =3D 0; bool is_return =3D false; @@ -1229,21 +1281,21 @@ static int trace_fprobe_create_internal(int argc, c= onst char *argv[], else ctx->flags |=3D TPARG_FL_FENTRY; =20 + ctx->funcname =3D NULL; if (is_tracepoint) { + /* Get tracepoint and lock its module until the end of the registration.= */ + struct tracepoint *tpoint =3D find_tracepoint(symbol, &mod); + ctx->flags |=3D TPARG_FL_TPOINT; - tuser =3D trace_fprobe_get_tracepoint_user(symbol, &mod); - if (!tuser) - return -ENOMEM; - if (IS_ERR(tuser)) { - trace_probe_log_set_index(1); - trace_probe_log_err(0, NO_TRACEPOINT); - return PTR_ERR(tuser); + if (mod && !try_module_get(mod)) { + mod =3D NULL; + tpoint =3D NULL; } - ctx->funcname =3D tracepoint_user_lookup(tuser, sbuf); - /* If tracepoint is not loaded yet, use symbol name as funcname. */ - if (!ctx->funcname) - ctx->funcname =3D symbol; - } else + if (tpoint) + ctx->funcname =3D kallsyms_lookup((unsigned long)tpoint->probestub, + NULL, NULL, NULL, sbuf); + } + if (!ctx->funcname) ctx->funcname =3D symbol; =20 argc -=3D 2; argv +=3D 2; @@ -1266,14 +1318,13 @@ static int trace_fprobe_create_internal(int argc, c= onst char *argv[], return ret; =20 /* setup a probe */ - tf =3D alloc_trace_fprobe(group, event, symbol, tuser, argc, is_return); + tf =3D alloc_trace_fprobe(group, event, symbol, argc, is_return, is_trace= point); if (IS_ERR(tf)) { ret =3D PTR_ERR(tf); /* This must return -ENOMEM, else there is a bug */ WARN_ON_ONCE(ret !=3D -ENOMEM); return ret; } - tuser =3D NULL; /* Move tuser to tf. */ =20 /* parse arguments */ for (i =3D 0; i < argc; i++) { @@ -1491,6 +1542,9 @@ static __init int init_fprobe_trace_early(void) ret =3D register_tracepoint_module_notifier(&tracepoint_module_nb); if (ret) return ret; + ret =3D register_module_notifier(&tprobe_event_module_nb); + if (ret) + return ret; #endif =20 return 0;