From nobody Tue Jun 16 09:01:24 2026 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 210F131F9A7; Fri, 17 Apr 2026 16:17:55 +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=1776442675; cv=none; b=IcoODeIdCVOk1IAaJNimmzcUfp+g54NMK4dRmAx4Q6asoBr1ctqkjf0UtPH+5CSFQ32JT6wibq8BZ10uxImRryeNtnTQbRlXtB9RgXl2zR02rzXmOQPGe/xeG6b71IcRQBjI3zFJHSFgm1x2ShxwgXuVtfclN8Dby3Zwrh7BU30= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776442675; c=relaxed/simple; bh=O73fWV9BDB514QImUXrNixIWJt61Gq+Vakfsm1Ay7I0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=qqqTG+3t/1SlsJ0J6b34C0IIyodVMvxiWKF7VfVzfQv2aSNG/N8OLjENg2c1E9/uSmU25Yir2B02wYNmOrHrISes2JWY3pGyqbrJzfHG2m/91nZcmYsu9yXKk3KoZCW4cUbo2JW3wL0UZeggVmyGueB1BckWZC5gU0fVGRLDZU4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Yo8awadf; 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="Yo8awadf" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 0CC22C19425; Fri, 17 Apr 2026 16:17:53 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1776442675; bh=O73fWV9BDB514QImUXrNixIWJt61Gq+Vakfsm1Ay7I0=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Yo8awadfTJCENRj7E5/NQn2oAexES/lSk5X01HhNYcRr84qNydDXOfKCYVr4kYfjC dD9VMAaiqhoKYYXenfHfhu5TTpsUP2c/Uv+YrX6zqs060bKXQS0QAUYsclqtXzBeGB aE80qrSxTU4EOZW6izhPB99w1zqOLCSKV11Gqq902AouHaAPIpAuNKV89fxYe/Tkq1 42iSXCH2R2isLggknPw83zw6ScC0Ik1o9jhQ66KkO6l5W4leAsUsKmxPeReCJBqEuw /UMz4ZmyMDhC2TpMX3muE6rcLD6clOtExAOYjzwFqszCfdMBVyF4iopzHiXDjETU9+ Tmt+zO85V4ACQ== From: "Masami Hiramatsu (Google)" To: Steven Rostedt , Masami Hiramatsu Cc: Menglong Dong , Mathieu Desnoyers , jiang.biao@linux.dev, linux-kernel@vger.kernel.org, linux-trace-kernel@vger.kernel.org Subject: [PATCH v9 1/8] tracing/fprobe: Reject registration of a registered fprobe before init Date: Sat, 18 Apr 2026 01:17:52 +0900 Message-ID: <177644267234.584467.13912302304022705772.stgit@mhiramat.tok.corp.google.com> X-Mailer: git-send-email 2.54.0.rc1.513.gad8abe7a5a-goog In-Reply-To: <177644266147.584467.8179035927318998910.stgit@mhiramat.tok.corp.google.com> References: <177644266147.584467.8179035927318998910.stgit@mhiramat.tok.corp.google.com> 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) Reject registration of a registered fprobe which is on the fprobe hash table before initializing fprobe. The add_fprobe_hash() checks this re-register fprobe, but since fprobe_init() clears hlist_array field, it is too late to check it. It has to check the re-registration before touncing fprobe. Fixes: 4346ba160409 ("fprobe: Rewrite fprobe on function-graph tracer") Cc: stable@vger.kernel.org Signed-off-by: Masami Hiramatsu (Google) --- Changes in v6: - Newly added. --- kernel/trace/fprobe.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/kernel/trace/fprobe.c b/kernel/trace/fprobe.c index 56d145017902..af9ba7250874 100644 --- a/kernel/trace/fprobe.c +++ b/kernel/trace/fprobe.c @@ -4,6 +4,7 @@ */ #define pr_fmt(fmt) "fprobe: " fmt =20 +#include #include #include #include @@ -107,7 +108,7 @@ static bool delete_fprobe_node(struct fprobe_hlist_node= *node) } =20 /* Check existence of the fprobe */ -static bool is_fprobe_still_exist(struct fprobe *fp) +static bool fprobe_registered(struct fprobe *fp) { struct hlist_head *head; struct fprobe_hlist *fph; @@ -120,7 +121,7 @@ static bool is_fprobe_still_exist(struct fprobe *fp) } return false; } -NOKPROBE_SYMBOL(is_fprobe_still_exist); +NOKPROBE_SYMBOL(fprobe_registered); =20 static int add_fprobe_hash(struct fprobe *fp) { @@ -132,9 +133,6 @@ static int add_fprobe_hash(struct fprobe *fp) if (WARN_ON_ONCE(!fph)) return -EINVAL; =20 - if (is_fprobe_still_exist(fp)) - return -EEXIST; - head =3D &fprobe_table[hash_ptr(fp, FPROBE_HASH_BITS)]; hlist_add_head_rcu(&fp->hlist_array->hlist, head); return 0; @@ -149,7 +147,7 @@ static int del_fprobe_hash(struct fprobe *fp) if (WARN_ON_ONCE(!fph)) return -EINVAL; =20 - if (!is_fprobe_still_exist(fp)) + if (!fprobe_registered(fp)) return -ENOENT; =20 fph->fp =3D NULL; @@ -480,7 +478,7 @@ static void fprobe_return(struct ftrace_graph_ret *trac= e, if (!fp) break; curr +=3D FPROBE_HEADER_SIZE_IN_LONG; - if (is_fprobe_still_exist(fp) && !fprobe_disabled(fp)) { + if (fprobe_registered(fp) && !fprobe_disabled(fp)) { if (WARN_ON_ONCE(curr + size > size_words)) break; fp->exit_handler(fp, trace->func, ret_ip, fregs, @@ -839,12 +837,14 @@ int register_fprobe_ips(struct fprobe *fp, unsigned l= ong *addrs, int num) struct fprobe_hlist *hlist_array; int ret, i; =20 + guard(mutex)(&fprobe_mutex); + if (fprobe_registered(fp)) + return -EEXIST; + ret =3D fprobe_init(fp, addrs, num); if (ret) return ret; =20 - mutex_lock(&fprobe_mutex); - hlist_array =3D fp->hlist_array; if (fprobe_is_ftrace(fp)) ret =3D fprobe_ftrace_add_ips(addrs, num); @@ -864,7 +864,6 @@ int register_fprobe_ips(struct fprobe *fp, unsigned lon= g *addrs, int num) delete_fprobe_node(&hlist_array->array[i]); } } - mutex_unlock(&fprobe_mutex); =20 if (ret) fprobe_fail_cleanup(fp); @@ -926,7 +925,7 @@ int unregister_fprobe(struct fprobe *fp) int ret =3D 0, i, count; =20 mutex_lock(&fprobe_mutex); - if (!fp || !is_fprobe_still_exist(fp)) { + if (!fp || !fprobe_registered(fp)) { ret =3D -EINVAL; goto out; } From nobody Tue Jun 16 09:01:24 2026 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 1E4B434D915; Fri, 17 Apr 2026 16:18:02 +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=1776442683; cv=none; b=pElqk1JV3nOT1WfbP6ay4jYXgdJkB4FZhzi1dveM6kY5QWrExtQMtiKzY/azDZ0yYH6uflq4byxQXC3xnQ9X8Xd1U7qT+AXi6eKmIQblJjOcK9Kx3yiiAM03eGfd8FCc3zz9M7ssckS9pO3cfXs5r/pC2TeKU7UYj2pOLoeEhug= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776442683; c=relaxed/simple; bh=B0rnKng8LQIVDP+HvxGHLoFM8lnqHJQo4FhLDaFMJFs=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=muYQgF8r+euY4Upjqs9ELKqfnZRT0iS/6ERkqvg3KQZlYO4GaDox7WTzI5gVR7HYdasP4Se7EPodKSTFv66jm+JYsO5qHpU1j5tg9zqfA5ev3wbvxh/3wYgwlpUNKUPTnjKnxOwzoPMyFFHPxiU17DXXqU4iaCbwy35dLK8AW8k= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=tZiNzDOX; 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="tZiNzDOX" Received: by smtp.kernel.org (Postfix) with ESMTPSA id E0FDFC2BCB4; Fri, 17 Apr 2026 16:18:01 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1776442682; bh=B0rnKng8LQIVDP+HvxGHLoFM8lnqHJQo4FhLDaFMJFs=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=tZiNzDOX5ykwDSrIDJ7nKwH6vJ4b5D2P2IwurBzD3YIXCLISqdbxjHcd+R4RhcYDP g5J6I84MCNG8yzzD5DrBqNSsvRfJ5xLT7P5sxFgouU4Y3imP4C3NJGl5B/U1ehtMkt 09NRhzfxZwh2OaWWlKfNNMAVCr7IiTsQlWDMWH4APIE6ZhUOE3/6a+/Z9ArTtTXzm0 zEvDjXFDC+pzanJ83JPKmvP+LQ4uaNDfybRptasZirZb5jI4BShCH4kd+hKe5IMSNB uwS7V+0Tuh/oO1YEqTY8Eq/9PMWVvDbyT7OAH3Z8OuphOh1/FICMNJbsnjlu2iLYzo SXI+ueTHDq52Q== From: "Masami Hiramatsu (Google)" To: Steven Rostedt , Masami Hiramatsu Cc: Menglong Dong , Mathieu Desnoyers , jiang.biao@linux.dev, linux-kernel@vger.kernel.org, linux-trace-kernel@vger.kernel.org Subject: [PATCH v9 2/8] tracing/fprobe: Unregister fprobe even if memory allocation fails Date: Sat, 18 Apr 2026 01:18:00 +0900 Message-ID: <177644268024.584467.6649630976225700663.stgit@mhiramat.tok.corp.google.com> X-Mailer: git-send-email 2.54.0.rc1.513.gad8abe7a5a-goog In-Reply-To: <177644266147.584467.8179035927318998910.stgit@mhiramat.tok.corp.google.com> References: <177644266147.584467.8179035927318998910.stgit@mhiramat.tok.corp.google.com> 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) unregister_fprobe() can fail under memory pressure because of memory allocation failure, but this maybe called from module unloading, and usually there is no way to retry it. Moreover. trace_fprobe does not check the return value. To fix this problem, unregister fprobe and fprobe_hash_node even if working memory allocation fails. Anyway, if the last fprobe is removed, the filter will be freed. Fixes: 4346ba160409 ("fprobe: Rewrite fprobe on function-graph tracer") Cc: stable@vger.kernel.org Signed-off-by: Masami Hiramatsu (Google) --- Changes in v9: - Clear ftrace_ops filter when unregister it. Changes in v7: - Newly added. --- kernel/trace/fprobe.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/kernel/trace/fprobe.c b/kernel/trace/fprobe.c index af9ba7250874..a2b659006e0e 100644 --- a/kernel/trace/fprobe.c +++ b/kernel/trace/fprobe.c @@ -324,9 +324,10 @@ static void fprobe_ftrace_remove_ips(unsigned long *ad= drs, int num) lockdep_assert_held(&fprobe_mutex); =20 fprobe_ftrace_active--; - if (!fprobe_ftrace_active) + if (!fprobe_ftrace_active) { unregister_ftrace_function(&fprobe_ftrace_ops); - if (num) + ftrace_free_filter(&fprobe_ftrace_ops); + } else if (num) ftrace_set_filter_ips(&fprobe_ftrace_ops, addrs, num, 1, 0); } =20 @@ -525,10 +526,10 @@ static void fprobe_graph_remove_ips(unsigned long *ad= drs, int num) =20 fprobe_graph_active--; /* Q: should we unregister it ? */ - if (!fprobe_graph_active) + if (!fprobe_graph_active) { unregister_ftrace_graph(&fprobe_graph_ops); - - if (num) + ftrace_free_filter(&fprobe_graph_ops.ops); + } else if (num) ftrace_set_filter_ips(&fprobe_graph_ops.ops, addrs, num, 1, 0); } =20 @@ -932,15 +933,19 @@ int unregister_fprobe(struct fprobe *fp) =20 hlist_array =3D fp->hlist_array; addrs =3D kcalloc(hlist_array->size, sizeof(unsigned long), GFP_KERNEL); - if (!addrs) { - ret =3D -ENOMEM; /* TODO: Fallback to one-by-one loop */ - goto out; - } + /* + * This will remove fprobe_hash_node from the hash table even if + * memory allocation fails. However, ftrace_ops will not be updated. + * Anyway, when the last fprobe is unregistered, ftrace_ops is also + * unregistered. + */ + if (!addrs) + pr_warn("Failed to allocate working array. ftrace_ops may not sync.\n"); =20 /* Remove non-synonim ips from table and hash */ count =3D 0; for (i =3D 0; i < hlist_array->size; i++) { - if (!delete_fprobe_node(&hlist_array->array[i])) + if (!delete_fprobe_node(&hlist_array->array[i]) && addrs) addrs[count++] =3D hlist_array->array[i].addr; } del_fprobe_hash(fp); From nobody Tue Jun 16 09:01:24 2026 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 1A39528F5; Fri, 17 Apr 2026 16:18:10 +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=1776442691; cv=none; b=FU16G0Ce263ToKt06AIXwNJIMskb6pd/f0XZkrHFLrRX6utn7WNgAl7j8J7mU405xbBX79bLp55x/iebt/r2HDoF3ftKxcHbl9ZDhx1pfmMhxSDl/BR7eN+joLIV1dRXBtPpwGP8G4XEyYz6WQSNEKkhr3dN0MAlu78uAFv5l3M= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776442691; c=relaxed/simple; bh=LolnzKvnymhTfQS/YHJNLIaPqF8ZolErG4t6mUdOL3M=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=G1Qvm9kgVLICWLo3GZLZrWKoMyF1pQXkFXim4vznBfIMAwnU27sHWJDgbB3Yqj93jN0H8md1VYkAYWJ4hCUoH3gX8yZ4LI6ZtIG2alTyaMVy4lViCv5YttrhS84ORnZ7+XLC4/r1WJ1mZY+eDYvXbHw+Mz7sNIi1GbDOm/muh5w= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=lGWpOYMR; 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="lGWpOYMR" Received: by smtp.kernel.org (Postfix) with ESMTPSA id D20ADC19425; Fri, 17 Apr 2026 16:18:09 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1776442690; bh=LolnzKvnymhTfQS/YHJNLIaPqF8ZolErG4t6mUdOL3M=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=lGWpOYMRdzYXKkdUfTmtdbeayiKofLJh1xNBQdIyinuIRL1HT1yc8FU6VFR6Bqjv0 xAMlDmlrg2Bmdru0NcK/3olNHqFxxVoWViNXg7CqWQBmyI8K8uXk92rypYcEzDceX+ GJOwD+b9Z0lnN5lynSBcIWLAWFFJzYjRNrpVrDqAsrbK46D8NMRhYkgl+ka/QKM0di PXcTzVJUJf7ECYFjVsQZXwQf7d6biA7FHjMc2BJWMJ/3OPW1ojImGGi0YDmKZXEkV6 NCIuhm7x9k/5JpDf6qeg9ZLOmx93D7Ua8RKY86X7jHfNyMYte1Jn20g9uP1Ft51nkz b1j2slPMIipoQ== From: "Masami Hiramatsu (Google)" To: Steven Rostedt , Masami Hiramatsu Cc: Menglong Dong , Mathieu Desnoyers , jiang.biao@linux.dev, linux-kernel@vger.kernel.org, linux-trace-kernel@vger.kernel.org Subject: [PATCH v9 3/8] tracing/fprobe: Remove fprobe from hash in failure path Date: Sat, 18 Apr 2026 01:18:08 +0900 Message-ID: <177644268811.584467.1100088009838466401.stgit@mhiramat.tok.corp.google.com> X-Mailer: git-send-email 2.54.0.rc1.513.gad8abe7a5a-goog In-Reply-To: <177644266147.584467.8179035927318998910.stgit@mhiramat.tok.corp.google.com> References: <177644266147.584467.8179035927318998910.stgit@mhiramat.tok.corp.google.com> 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) When register_fprobe_ips() fails, it tries to remove a list of fprobe_hash_node from fprobe_ip_table, but it missed to remove fprobe itself from fprobe_table. Moreover, when removing the fprobe_hash_node which is added to rhltable once, it must use kfree_rcu() after removing from rhltable. To fix these issues, this reuses unregister_fprobe() internal code to rollback the half-way registered fprobe. Fixes: 4346ba160409 ("fprobe: Rewrite fprobe on function-graph tracer") Cc: stable@vger.kernel.org Signed-off-by: Masami Hiramatsu (Google) --- Changes in v8: - Fix to check return value of add_fprobe_hash() and break loop if insert_fprobe_node() is failed. Changes in v7: - Remove RCU grace period wait, since fprobe itself is not that is not needed. Changes in v6: - Wait for an RCU grace period before returning error in unregister_fprobe_nolock(). Changes in v5: - When rolling back an fprobe that failed to register, the fprobe_hash_node are forcibly removed and warn if failure. Changes in v4: - Remove short-cut case because we always need to upadte ftrace_ops. - Use guard(mutex) in register_fprobe_ips() to unlock it correctly. - Remove redundant !ret check in register_fprobe_ips(). - Do not set hlist_array->size in failure case, instead, hlist_array->array[i].fp is set only when insertion is succeeded. Changes in v3: - Newly added. --- kernel/trace/fprobe.c | 85 +++++++++++++++++++++++++--------------------= ---- 1 file changed, 43 insertions(+), 42 deletions(-) diff --git a/kernel/trace/fprobe.c b/kernel/trace/fprobe.c index a2b659006e0e..2e232342cbd4 100644 --- a/kernel/trace/fprobe.c +++ b/kernel/trace/fprobe.c @@ -79,20 +79,27 @@ static const struct rhashtable_params fprobe_rht_params= =3D { }; =20 /* Node insertion and deletion requires the fprobe_mutex */ -static int insert_fprobe_node(struct fprobe_hlist_node *node) +static int insert_fprobe_node(struct fprobe_hlist_node *node, struct fprob= e *fp) { + int ret; + lockdep_assert_held(&fprobe_mutex); =20 - return rhltable_insert(&fprobe_ip_table, &node->hlist, fprobe_rht_params); + ret =3D rhltable_insert(&fprobe_ip_table, &node->hlist, fprobe_rht_params= ); + /* Set the fprobe pointer if insertion was successful. */ + if (!ret) + WRITE_ONCE(node->fp, fp); + return ret; } =20 /* Return true if there are synonims */ static bool delete_fprobe_node(struct fprobe_hlist_node *node) { - lockdep_assert_held(&fprobe_mutex); bool ret; =20 - /* Avoid double deleting */ + lockdep_assert_held(&fprobe_mutex); + + /* Avoid double deleting and non-inserted nodes */ if (READ_ONCE(node->fp) !=3D NULL) { WRITE_ONCE(node->fp, NULL); rhltable_remove(&fprobe_ip_table, &node->hlist, @@ -756,7 +763,6 @@ static int fprobe_init(struct fprobe *fp, unsigned long= *addrs, int num) fp->hlist_array =3D hlist_array; hlist_array->fp =3D fp; for (i =3D 0; i < num; i++) { - hlist_array->array[i].fp =3D fp; addr =3D ftrace_location(addrs[i]); if (!addr) { fprobe_fail_cleanup(fp); @@ -820,6 +826,8 @@ int register_fprobe(struct fprobe *fp, const char *filt= er, const char *notfilter } EXPORT_SYMBOL_GPL(register_fprobe); =20 +static int unregister_fprobe_nolock(struct fprobe *fp); + /** * register_fprobe_ips() - Register fprobe to ftrace by address. * @fp: A fprobe data structure to be registered. @@ -846,28 +854,22 @@ int register_fprobe_ips(struct fprobe *fp, unsigned l= ong *addrs, int num) if (ret) return ret; =20 - hlist_array =3D fp->hlist_array; if (fprobe_is_ftrace(fp)) ret =3D fprobe_ftrace_add_ips(addrs, num); else ret =3D fprobe_graph_add_ips(addrs, num); - - if (!ret) { - add_fprobe_hash(fp); - for (i =3D 0; i < hlist_array->size; i++) { - ret =3D insert_fprobe_node(&hlist_array->array[i]); - if (ret) - break; - } - /* fallback on insert error */ - if (ret) { - for (i--; i >=3D 0; i--) - delete_fprobe_node(&hlist_array->array[i]); - } + if (ret) { + fprobe_fail_cleanup(fp); + return ret; } =20 + hlist_array =3D fp->hlist_array; + ret =3D add_fprobe_hash(fp); + for (i =3D 0; i < hlist_array->size && !ret; i++) + ret =3D insert_fprobe_node(&hlist_array->array[i], fp); + if (ret) - fprobe_fail_cleanup(fp); + unregister_fprobe_nolock(fp); =20 return ret; } @@ -911,27 +913,12 @@ bool fprobe_is_registered(struct fprobe *fp) return true; } =20 -/** - * unregister_fprobe() - Unregister fprobe. - * @fp: A fprobe data structure to be unregistered. - * - * Unregister fprobe (and remove ftrace hooks from the function entries). - * - * Return 0 if @fp is unregistered successfully, -errno if not. - */ -int unregister_fprobe(struct fprobe *fp) +static int unregister_fprobe_nolock(struct fprobe *fp) { - struct fprobe_hlist *hlist_array; + struct fprobe_hlist *hlist_array =3D fp->hlist_array; unsigned long *addrs =3D NULL; - int ret =3D 0, i, count; + int i, count; =20 - mutex_lock(&fprobe_mutex); - if (!fp || !fprobe_registered(fp)) { - ret =3D -EINVAL; - goto out; - } - - hlist_array =3D fp->hlist_array; addrs =3D kcalloc(hlist_array->size, sizeof(unsigned long), GFP_KERNEL); /* * This will remove fprobe_hash_node from the hash table even if @@ -957,12 +944,26 @@ int unregister_fprobe(struct fprobe *fp) =20 kfree_rcu(hlist_array, rcu); fp->hlist_array =3D NULL; + kfree(addrs); =20 -out: - mutex_unlock(&fprobe_mutex); + return 0; +} =20 - kfree(addrs); - return ret; +/** + * unregister_fprobe() - Unregister fprobe. + * @fp: A fprobe data structure to be unregistered. + * + * Unregister fprobe (and remove ftrace hooks from the function entries). + * + * Return 0 if @fp is unregistered successfully, -errno if not. + */ +int unregister_fprobe(struct fprobe *fp) +{ + guard(mutex)(&fprobe_mutex); + if (!fp || !fprobe_registered(fp)) + return -EINVAL; + + return unregister_fprobe_nolock(fp); } EXPORT_SYMBOL_GPL(unregister_fprobe); From nobody Tue Jun 16 09:01:24 2026 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 E4EB934F48E; Fri, 17 Apr 2026 16:18:18 +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=1776442699; cv=none; b=sD+9SweM4bWqQ/6mtCC23kPUCluy9bJuXGlWZrbNx/E+J8Of6zBVEekLmgFMAChjJjKCJ7fXJl9nC00nEzIiqXHuwpW3d7CvtpAyd8frwXIIAkQq+GHLygBJObf3g+FTnXnIpCHmsV2p7wC3iCK8Ln1qxDCeTS7cbWjmaspepzc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776442699; c=relaxed/simple; bh=Ozbgj3qKPCewIzlt8nY4/os3K52BQZWHD5bKERcpEBM=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=F9j+5rytC5MdYJyZEglBXhJC2MMnVbO+MhS3xmi49gGHOXerzGOcJ2DXDxqKZafAnCueM69wkLKQyNyJYB42QIyq/ksUtjWe/w34lkzIFSvV0U53H5I9Qxx/GnnwJOz1C6tNe4WvszdB15lRg/dEMoZ9TCmDQb16GANHfdQ62Zs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=KOsw2CQm; 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="KOsw2CQm" Received: by smtp.kernel.org (Postfix) with ESMTPSA id AA184C19425; Fri, 17 Apr 2026 16:18:17 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1776442698; bh=Ozbgj3qKPCewIzlt8nY4/os3K52BQZWHD5bKERcpEBM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=KOsw2CQmLqMzMYSsF+e1bZCiIt/Nf3Hv13xDG5/xyqGmIt6PqyND9XDNg/SRvNwOq w+jQ1vB5h8PyODasByFAXcAn0smocpzb7DcLBU+MAMLaSe0gjQ3ME2q1f5JHFgvWye Jny9NG5QxJHOkbXY4FrSa3HIRRwy2Hse8PZ52Bhn2GrVzbTHbauq26dLA7ca2l7FlL eaFp5hHdBqHUeI31dyzhaePO84J/zB6MZ2Ip4PgOGy4zEuNlYG6yEK3Mb7raa+j4h4 6DwipHry/NLj9ldZUCMUk5GG8BrwTeFZQaMccPGVk3u25AwO/Mr1rBQx0RyJd/YLV9 G6RRb7RiNhrCg== From: "Masami Hiramatsu (Google)" To: Steven Rostedt , Masami Hiramatsu Cc: Menglong Dong , Mathieu Desnoyers , jiang.biao@linux.dev, linux-kernel@vger.kernel.org, linux-trace-kernel@vger.kernel.org Subject: [PATCH v9 4/8] tracing/fprobe: Avoid kcalloc() in rcu_read_lock section Date: Sat, 18 Apr 2026 01:18:16 +0900 Message-ID: <177644269606.584467.6015518447478011551.stgit@mhiramat.tok.corp.google.com> X-Mailer: git-send-email 2.54.0.rc1.513.gad8abe7a5a-goog In-Reply-To: <177644266147.584467.8179035927318998910.stgit@mhiramat.tok.corp.google.com> References: <177644266147.584467.8179035927318998910.stgit@mhiramat.tok.corp.google.com> 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) fprobe_remove_node_in_module() is called under RCU read locked, but this invokes kcalloc() if there are more than 8 fprobes installed on the module. Sashiko warns it because kcalloc() can sleep [1]. [1] https://sashiko.dev/#/patchset/177552432201.853249.5125045538812833325= .stgit%40mhiramat.tok.corp.google.com To fix this issue, expand the batch size to 128 and do not expand the fprobe_addr_list, but just cancel walking on fprobe_ip_table, update fgraph/ftrace_ops and retry the loop again. Fixes: 0de4c70d04a4 ("tracing: fprobe: use rhltable for fprobe_ip_table") Cc: stable@vger.kernel.org Signed-off-by: Masami Hiramatsu (Google) --- Changes in v6: - Retry outside rhltable_walk_enter/exit() again. Changes in v5: - Skip updating ftrace_ops when fails to allocate memory in module unloading. Changes in v4: - fix a build error typo in case of CONFIG_DYNAMIC_FTRACE=3Dn. Changes in v3: - Retry inside rhltable_walk_enter/exit(). - Rename fprobe_set_ips() to fprobe_remove_ips(). - Rename 'retry' label to 'again'. --- kernel/trace/fprobe.c | 92 ++++++++++++++++++++++++---------------------= ---- 1 file changed, 45 insertions(+), 47 deletions(-) diff --git a/kernel/trace/fprobe.c b/kernel/trace/fprobe.c index 2e232342cbd4..49016c3e7cd9 100644 --- a/kernel/trace/fprobe.c +++ b/kernel/trace/fprobe.c @@ -344,11 +344,10 @@ static bool fprobe_is_ftrace(struct fprobe *fp) } =20 #ifdef CONFIG_MODULES -static void fprobe_set_ips(unsigned long *ips, unsigned int cnt, int remov= e, - int reset) +static void fprobe_remove_ips(unsigned long *ips, unsigned int cnt) { - ftrace_set_filter_ips(&fprobe_graph_ops.ops, ips, cnt, remove, reset); - ftrace_set_filter_ips(&fprobe_ftrace_ops, ips, cnt, remove, reset); + ftrace_set_filter_ips(&fprobe_graph_ops.ops, ips, cnt, 1, 0); + ftrace_set_filter_ips(&fprobe_ftrace_ops, ips, cnt, 1, 0); } #endif #else @@ -367,10 +366,9 @@ static bool fprobe_is_ftrace(struct fprobe *fp) } =20 #ifdef CONFIG_MODULES -static void fprobe_set_ips(unsigned long *ips, unsigned int cnt, int remov= e, - int reset) +static void fprobe_remove_ips(unsigned long *ips, unsigned int cnt) { - ftrace_set_filter_ips(&fprobe_graph_ops.ops, ips, cnt, remove, reset); + ftrace_set_filter_ips(&fprobe_graph_ops.ops, ips, cnt, 1, 0); } #endif #endif /* !CONFIG_DYNAMIC_FTRACE_WITH_ARGS && !CONFIG_DYNAMIC_FTRACE_WITH_= REGS */ @@ -542,7 +540,7 @@ static void fprobe_graph_remove_ips(unsigned long *addr= s, int num) =20 #ifdef CONFIG_MODULES =20 -#define FPROBE_IPS_BATCH_INIT 8 +#define FPROBE_IPS_BATCH_INIT 128 /* instruction pointer address list */ struct fprobe_addr_list { int index; @@ -550,45 +548,24 @@ struct fprobe_addr_list { unsigned long *addrs; }; =20 -static int fprobe_addr_list_add(struct fprobe_addr_list *alist, unsigned l= ong addr) +static int fprobe_remove_node_in_module(struct module *mod, struct fprobe_= hlist_node *node, + struct fprobe_addr_list *alist) { - unsigned long *addrs; - - /* Previously we failed to expand the list. */ - if (alist->index =3D=3D alist->size) - return -ENOSPC; - - alist->addrs[alist->index++] =3D addr; - if (alist->index < alist->size) + if (!within_module(node->addr, mod)) return 0; =20 - /* Expand the address list */ - addrs =3D kcalloc(alist->size * 2, sizeof(*addrs), GFP_KERNEL); - if (!addrs) - return -ENOMEM; - - memcpy(addrs, alist->addrs, alist->size * sizeof(*addrs)); - alist->size *=3D 2; - kfree(alist->addrs); - alist->addrs =3D addrs; + if (delete_fprobe_node(node)) + return 0; + /* If no address list is available, we can't track this address. */ + if (!alist->addrs) + return 0; =20 + alist->addrs[alist->index++] =3D node->addr; + if (alist->index =3D=3D alist->size) + return -ENOSPC; return 0; } =20 -static void fprobe_remove_node_in_module(struct module *mod, struct fprobe= _hlist_node *node, - struct fprobe_addr_list *alist) -{ - if (!within_module(node->addr, mod)) - return; - if (delete_fprobe_node(node)) - return; - /* - * If failed to update alist, just continue to update hlist. - * Therefore, at list user handler will not hit anymore. - */ - fprobe_addr_list_add(alist, node->addr); -} - /* Handle module unloading to manage fprobe_ip_table. */ static int fprobe_module_callback(struct notifier_block *nb, unsigned long val, void *data) @@ -597,29 +574,50 @@ static int fprobe_module_callback(struct notifier_blo= ck *nb, struct fprobe_hlist_node *node; struct rhashtable_iter iter; struct module *mod =3D data; + bool retry; =20 if (val !=3D MODULE_STATE_GOING) return NOTIFY_DONE; =20 alist.addrs =3D kcalloc(alist.size, sizeof(*alist.addrs), GFP_KERNEL); - /* If failed to alloc memory, we can not remove ips from hash. */ - if (!alist.addrs) - return NOTIFY_DONE; + /* + * If failed to alloc memory, ftrace_ops will not be able to remove ips f= rom + * hash, but we can still remove nodes from fprobe_ip_table, so we can av= oid + * the potential wrong callback. So just print a warning here and try to + * continue without address list. + */ + WARN_ONCE(!alist.addrs, + "Failed to allocate memory for fprobe_addr_list, ftrace_ops will not be = updated"); =20 mutex_lock(&fprobe_mutex); +again: + retry =3D false; + alist.index =3D 0; rhltable_walk_enter(&fprobe_ip_table, &iter); do { rhashtable_walk_start(&iter); =20 while ((node =3D rhashtable_walk_next(&iter)) && !IS_ERR(node)) - fprobe_remove_node_in_module(mod, node, &alist); + if (fprobe_remove_node_in_module(mod, node, &alist) < 0) { + retry =3D true; + break; + } =20 rhashtable_walk_stop(&iter); - } while (node =3D=3D ERR_PTR(-EAGAIN)); + } while (node =3D=3D ERR_PTR(-EAGAIN) && !retry); rhashtable_walk_exit(&iter); + /* Remove any ips from hash table(s) */ + if (alist.index > 0) { + fprobe_remove_ips(alist.addrs, alist.index); + /* + * If we break rhashtable walk loop except for -EAGAIN, we need + * to restart looping from start for safety. Anyway, this is + * not a hotpath. + */ + if (retry) + goto again; + } =20 - if (alist.index > 0) - fprobe_set_ips(alist.addrs, alist.index, 1, 0); mutex_unlock(&fprobe_mutex); =20 kfree(alist.addrs); From nobody Tue Jun 16 09:01:24 2026 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 DEA4228F5; Fri, 17 Apr 2026 16:18:26 +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=1776442706; cv=none; b=Wc8U41GBKtRZNGi1fxhx+4c1FkHh5vRqtN1bP6tmuXugG5Wc4H6853kQtAKuHDYoM4XHzNeH/al3643e+dpQIZS4hFIyYC6glKiyYrx+sZ9NlMNTcIaA8Y0j0hMdyEoobxZ2kQskqZyvGNU6ILLT9E2GBn+wIg92jspeXzUyKCI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776442706; c=relaxed/simple; bh=eeES11lgf0nTDqzaN74/ZJnkz3BMn13J0Rm2hlMjCPc=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=JvUINwR9zmgTl0GDAHYPJIDwm2uiZsPgYBlvggwogAnoVYCflDwQ2rbg/JTS9fXStaKtGdGMWfzI5X5NeNF/dUKJJliT2OaUYEiJNS/GLMUxvl0u2mZ+QualwWR0+G3tRy5Xhnq1QSbWnli71uHI9BRkogXFK1Qmqury3oBVnyE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=cOMnlree; 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="cOMnlree" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 8AB10C19425; Fri, 17 Apr 2026 16:18:25 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1776442706; bh=eeES11lgf0nTDqzaN74/ZJnkz3BMn13J0Rm2hlMjCPc=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=cOMnlreemuhHdTV6Z/k36rNTnbijhpNfZaxaoSjCt18lao6S5lFyRcQbK54AenCUu hTEL3s3BNUJsciWvYBuduWa++x4mDFCY4IrSAt8AwnjgcuolZF31ZNhwwP88+9GBMq Vcl/9nFclmLfNXxWgLE350nFsTGxoP99ohLaTMLPTJpQzb90VLqdRfAo6mL+ZFD+s3 QsRaZ6attjZxr/WFQvnloKmMDhj8erw/1Ltp//vuYFJqrP/mI6X6YeVBDR6RZCe83D xfr5vXdQP6WU79pqW6P566985M8cvf3dv47yYLxu2Y1FN5atcY5inLslkm0aoyI3+H aprcTp5PEWbqw== From: "Masami Hiramatsu (Google)" To: Steven Rostedt , Masami Hiramatsu Cc: Menglong Dong , Mathieu Desnoyers , jiang.biao@linux.dev, linux-kernel@vger.kernel.org, linux-trace-kernel@vger.kernel.org Subject: [PATCH v9 5/8] tracing/fprobe: Check the same type fprobe on table as the unregistered one Date: Sat, 18 Apr 2026 01:18:24 +0900 Message-ID: <177644270386.584467.15176436463551918968.stgit@mhiramat.tok.corp.google.com> X-Mailer: git-send-email 2.54.0.rc1.513.gad8abe7a5a-goog In-Reply-To: <177644266147.584467.8179035927318998910.stgit@mhiramat.tok.corp.google.com> References: <177644266147.584467.8179035927318998910.stgit@mhiramat.tok.corp.google.com> 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) Commit 2c67dc457bc6 ("tracing: fprobe: optimization for entry only case") introduced a different ftrace_ops for entry-only fprobes. However, when unregistering an fprobe, the kernel only checks if another fprobe exists at the same address, without checking which type of fprobe it is. If different fprobes are registered at the same address, the same address will be registered in both fgraph_ops and ftrace_ops, but only one of them will be deleted when unregistering. (the one removed first will not be deleted from the ops). This results in junk entries remaining in either fgraph_ops or ftrace_ops. For example: =3D=3D=3D=3D=3D=3D=3D cd /sys/kernel/tracing # 'Add entry and exit events on the same place' echo 'f:event1 vfs_read' >> dynamic_events echo 'f:event2 vfs_read%return' >> dynamic_events # 'Enable both of them' echo 1 > events/fprobes/enable cat enabled_functions vfs_read (2) ->arch_ftrace_ops_list_func+0x0/0x210 # 'Disable and remove exit event' echo 0 > events/fprobes/event2/enable echo -:event2 >> dynamic_events # 'Disable and remove all events' echo 0 > events/fprobes/enable echo > dynamic_events # 'Add another event' echo 'f:event3 vfs_open%return' > dynamic_events cat dynamic_events f:fprobes/event3 vfs_open%return echo 1 > events/fprobes/enable cat enabled_functions vfs_open (1) tramp: 0xffffffffa0001000 (ftrace_graph_func+0x0/0x= 60) ->ftrace_graph_func+0x0/0x60 subops: {ent:fprobe_fgraph_entry+0x0/0x= 620 ret:fprobe_return+0x0/0x150} vfs_read (1) tramp: 0xffffffffa0001000 (ftrace_graph_func+0x0/0x= 60) ->ftrace_graph_func+0x0/0x60 subops: {ent:fprobe_fgraph_entry+0x0/0x= 620 ret:fprobe_return+0x0/0x150} =3D=3D=3D=3D=3D=3D=3D As you can see, an entry for the vfs_read remains. To fix this issue, when unregistering, the kernel should also check if there is the same type of fprobes still exist at the same address, and if not, delete its entry from either fgraph_ops or ftrace_ops. Fixes: 2c67dc457bc6 ("tracing: fprobe: optimization for entry only case") Cc: stable@vger.kernel.org Signed-off-by: Masami Hiramatsu (Google) --- kernel/trace/fprobe.c | 82 +++++++++++++++++++++++++++++++++++++++------= ---- 1 file changed, 65 insertions(+), 17 deletions(-) diff --git a/kernel/trace/fprobe.c b/kernel/trace/fprobe.c index 49016c3e7cd9..e3b5cc76151e 100644 --- a/kernel/trace/fprobe.c +++ b/kernel/trace/fprobe.c @@ -92,11 +92,8 @@ static int insert_fprobe_node(struct fprobe_hlist_node *= node, struct fprobe *fp) return ret; } =20 -/* Return true if there are synonims */ -static bool delete_fprobe_node(struct fprobe_hlist_node *node) +static void delete_fprobe_node(struct fprobe_hlist_node *node) { - bool ret; - lockdep_assert_held(&fprobe_mutex); =20 /* Avoid double deleting and non-inserted nodes */ @@ -105,13 +102,6 @@ static bool delete_fprobe_node(struct fprobe_hlist_nod= e *node) rhltable_remove(&fprobe_ip_table, &node->hlist, fprobe_rht_params); } - - rcu_read_lock(); - ret =3D !!rhltable_lookup(&fprobe_ip_table, &node->addr, - fprobe_rht_params); - rcu_read_unlock(); - - return ret; } =20 /* Check existence of the fprobe */ @@ -343,6 +333,32 @@ static bool fprobe_is_ftrace(struct fprobe *fp) return !fp->exit_handler; } =20 +static bool fprobe_exists_on_hash(unsigned long ip, bool ftrace) +{ + struct rhlist_head *head, *pos; + struct fprobe_hlist_node *node; + struct fprobe *fp; + + guard(rcu)(); + head =3D rhltable_lookup(&fprobe_ip_table, &ip, + fprobe_rht_params); + if (!head) + return false; + /* We have to check the same type on the list. */ + rhl_for_each_entry_rcu(node, pos, head, hlist) { + if (node->addr !=3D ip) + break; + fp =3D READ_ONCE(node->fp); + if (likely(fp)) { + if ((!ftrace && fp->exit_handler) || + (ftrace && !fp->exit_handler)) + return true; + } + } + + return false; +} + #ifdef CONFIG_MODULES static void fprobe_remove_ips(unsigned long *ips, unsigned int cnt) { @@ -365,6 +381,29 @@ static bool fprobe_is_ftrace(struct fprobe *fp) return false; } =20 +static bool fprobe_exists_on_hash(unsigned long ip, bool ftrace __maybe_un= used) +{ + struct rhlist_head *head, *pos; + struct fprobe_hlist_node *node; + struct fprobe *fp; + + guard(rcu)(); + head =3D rhltable_lookup(&fprobe_ip_table, &ip, + fprobe_rht_params); + if (!head) + return false; + /* We only need to check fp is there. */ + rhl_for_each_entry_rcu(node, pos, head, hlist) { + if (node->addr !=3D ip) + break; + fp =3D READ_ONCE(node->fp); + if (likely(fp)) + return true; + } + + return false; +} + #ifdef CONFIG_MODULES static void fprobe_remove_ips(unsigned long *ips, unsigned int cnt) { @@ -551,18 +590,25 @@ struct fprobe_addr_list { static int fprobe_remove_node_in_module(struct module *mod, struct fprobe_= hlist_node *node, struct fprobe_addr_list *alist) { + lockdep_assert_in_rcu_read_lock(); + if (!within_module(node->addr, mod)) return 0; =20 - if (delete_fprobe_node(node)) - return 0; + delete_fprobe_node(node); /* If no address list is available, we can't track this address. */ if (!alist->addrs) return 0; + /* + * Don't care the type here, because all fprobes on the same + * address must be removed eventually. + */ + if (!rhltable_lookup(&fprobe_ip_table, &node->addr, fprobe_rht_params)) { + alist->addrs[alist->index++] =3D node->addr; + if (alist->index =3D=3D alist->size) + return -ENOSPC; + } =20 - alist->addrs[alist->index++] =3D node->addr; - if (alist->index =3D=3D alist->size) - return -ENOSPC; return 0; } =20 @@ -930,7 +976,9 @@ static int unregister_fprobe_nolock(struct fprobe *fp) /* Remove non-synonim ips from table and hash */ count =3D 0; for (i =3D 0; i < hlist_array->size; i++) { - if (!delete_fprobe_node(&hlist_array->array[i]) && addrs) + delete_fprobe_node(&hlist_array->array[i]); + if (addrs && !fprobe_exists_on_hash(hlist_array->array[i].addr, + fprobe_is_ftrace(fp))) addrs[count++] =3D hlist_array->array[i].addr; } del_fprobe_hash(fp); From nobody Tue Jun 16 09:01:24 2026 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 B293528F5; Fri, 17 Apr 2026 16:18:34 +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=1776442714; cv=none; b=nawee12Gmz6hTuqlVU5pnhBFXPdgqMckmE5IE8jQhqleHvL7GWSJkQ9YY2r2NoM/mgIbqxjuqXiL7sZ32GsGi+CLAreZxoKVFGwQStQc2+u3ucYfRk9SOhEMtFbXFRnRiixIl7tR8p6hLHIsb+SbSDLC92Tx7JArQcLD9hpCO3U= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776442714; c=relaxed/simple; bh=AxNeNAyL/6BHAbljZCKYBK8EnELRZQ8y3F7zFDAkdOc=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=u9ThdoYSIdfjmOY0c7TxrU6YM8Goi4Wz1FeA15rzv7f475PLcNr8JEbFwBsVVRQt9TEUQnIrL9NjQbRmy3A2EmIXNXGtX8YWaQQNgsmrhtDzPs6UtMKd3aH8xQlrzxhPXRhcIqPxMHBCz/9ha0/HU6gDJ0DGPwKSt5aNYvnPJPE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=a9dl8EhS; 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="a9dl8EhS" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 74D7FC19425; Fri, 17 Apr 2026 16:18:33 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1776442714; bh=AxNeNAyL/6BHAbljZCKYBK8EnELRZQ8y3F7zFDAkdOc=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=a9dl8EhS+xDe7X0Wf3H5d7ZaZMs/dT1tLAIFBCibLzYxcdrlU9qs+JI9ONFUCbYpF 8PW6k9tHSSjA2wAse1uTW+TjvR4wszdnt+hGy8tzniOf6zMXt/Tb0CZOcL4X5/MHmc tL8quCpMgrIdwgsRC8K/TYCCcatNqTlhz09bSoKySEt+hGqxsstZPg9w8G0GWHBs3/ vJUWXf+5Eau2uX6SalI4GtQN7vymgodJH8XcZtQf28rtzQ2J2VDl/DnTSXqio3HZtm xTO4E93rR9gAWFIwzCZDuVT+tgVlUaKwYEoMf5uFitLWWwLS5zqAXLWaJLzaH3yXrO UIpuPPp78qtpA== From: "Masami Hiramatsu (Google)" To: Steven Rostedt , Masami Hiramatsu Cc: Menglong Dong , Mathieu Desnoyers , jiang.biao@linux.dev, linux-kernel@vger.kernel.org, linux-trace-kernel@vger.kernel.org Subject: [PATCH v9 6/8] tracing/fprobe: Fix to unregister ftrace_ops if it is empty on module unloading Date: Sat, 18 Apr 2026 01:18:31 +0900 Message-ID: <177644271176.584467.7530496355395544191.stgit@mhiramat.tok.corp.google.com> X-Mailer: git-send-email 2.54.0.rc1.513.gad8abe7a5a-goog In-Reply-To: <177644266147.584467.8179035927318998910.stgit@mhiramat.tok.corp.google.com> References: <177644266147.584467.8179035927318998910.stgit@mhiramat.tok.corp.google.com> 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) Fix fprobe to unregister ftrace_ops if corresponding type of fprobe does not exist on the fprobe_ip_table and it is expected to be empty when unloading modules. Since ftrace thinks that the empty hash means everything to be traced, if we set fprobes only on the unloaded module, all functions are traced unexpectedly after unloading module. e.g. # modprobe xt_LOG.ko # echo 'f:test log_tg*' > dynamic_events # echo 1 > events/fprobes/test/enable # cat enabled_functions log_tg [xt_LOG] (1) tramp: 0xffffffffa0004000 (fprobe_ftrace_en= try+0x0/0x490) ->fprobe_ftrace_entry+0x0/0x490 log_tg_check [xt_LOG] (1) tramp: 0xffffffffa0004000 (fprobe_f= trace_entry+0x0/0x490) ->fprobe_ftrace_entry+0x0/0x490 log_tg_destroy [xt_LOG] (1) tramp: 0xffffffffa0004000 (fprobe_f= trace_entry+0x0/0x490) ->fprobe_ftrace_entry+0x0/0x490 # rmmod xt_LOG # wc -l enabled_functions 34085 enabled_functions Signed-off-by: Masami Hiramatsu (Google) --- Changes in v9: - Remove fprobe_graph_active and fprobe_ftrace_active to fix remove fprobe after unload module case. Changes in v8: - Fix to check fprobe_graph/ftrace_registered flag directly when registering ftrace_ops. Changes in v7: - Fix to split checking whether ftrace_ops is registered from the number of registered fprobes, because ftrace_ops can be unregistered in module unloading. Changes in v6: - Newly added. --- kernel/trace/fprobe.c | 206 ++++++++++++++++++++++++++++++++++++---------= ---- 1 file changed, 151 insertions(+), 55 deletions(-) diff --git a/kernel/trace/fprobe.c b/kernel/trace/fprobe.c index e3b5cc76151e..6a392936238a 100644 --- a/kernel/trace/fprobe.c +++ b/kernel/trace/fprobe.c @@ -79,7 +79,7 @@ static const struct rhashtable_params fprobe_rht_params = =3D { }; =20 /* Node insertion and deletion requires the fprobe_mutex */ -static int insert_fprobe_node(struct fprobe_hlist_node *node, struct fprob= e *fp) +static int __insert_fprobe_node(struct fprobe_hlist_node *node, struct fpr= obe *fp) { int ret; =20 @@ -92,7 +92,7 @@ static int insert_fprobe_node(struct fprobe_hlist_node *n= ode, struct fprobe *fp) return ret; } =20 -static void delete_fprobe_node(struct fprobe_hlist_node *node) +static void __delete_fprobe_node(struct fprobe_hlist_node *node) { lockdep_assert_held(&fprobe_mutex); =20 @@ -250,7 +250,65 @@ static inline int __fprobe_kprobe_handler(unsigned lon= g ip, unsigned long parent return ret; } =20 +static int fprobe_fgraph_entry(struct ftrace_graph_ent *trace, struct fgra= ph_ops *gops, + struct ftrace_regs *fregs); +static void fprobe_return(struct ftrace_graph_ret *trace, + struct fgraph_ops *gops, + struct ftrace_regs *fregs); + +static struct fgraph_ops fprobe_graph_ops =3D { + .entryfunc =3D fprobe_fgraph_entry, + .retfunc =3D fprobe_return, +}; +/* Number of fgraph fprobe nodes */ +static int nr_fgraph_fprobes; +/* Is fprobe_graph_ops registered? */ +static bool fprobe_graph_registered; + +/* Add @addrs to the ftrace filter and register fgraph if needed. */ +static int fprobe_graph_add_ips(unsigned long *addrs, int num) +{ + int ret; + + lockdep_assert_held(&fprobe_mutex); + + ret =3D ftrace_set_filter_ips(&fprobe_graph_ops.ops, addrs, num, 0, 0); + if (ret) + return ret; + + if (!fprobe_graph_registered) { + ret =3D register_ftrace_graph(&fprobe_graph_ops); + if (WARN_ON_ONCE(ret)) { + ftrace_free_filter(&fprobe_graph_ops.ops); + return ret; + } + fprobe_graph_registered =3D true; + } + return 0; +} + +static void __fprobe_graph_unregister(void) +{ + if (fprobe_graph_registered) { + unregister_ftrace_graph(&fprobe_graph_ops); + ftrace_free_filter(&fprobe_graph_ops.ops); + fprobe_graph_registered =3D false; + } +} + +/* Remove @addrs from the ftrace filter and unregister fgraph if possible.= */ +static void fprobe_graph_remove_ips(unsigned long *addrs, int num) +{ + lockdep_assert_held(&fprobe_mutex); + + if (!nr_fgraph_fprobes) + __fprobe_graph_unregister(); + else if (num) + ftrace_set_filter_ips(&fprobe_graph_ops.ops, addrs, num, 1, 0); +} + #if defined(CONFIG_DYNAMIC_FTRACE_WITH_ARGS) || defined(CONFIG_DYNAMIC_FTR= ACE_WITH_REGS) + /* ftrace_ops callback, this processes fprobes which have only entry_handl= er. */ static void fprobe_ftrace_entry(unsigned long ip, unsigned long parent_ip, struct ftrace_ops *ops, struct ftrace_regs *fregs) @@ -293,7 +351,10 @@ static struct ftrace_ops fprobe_ftrace_ops =3D { .func =3D fprobe_ftrace_entry, .flags =3D FTRACE_OPS_FL_SAVE_ARGS, }; -static int fprobe_ftrace_active; +/* Number of ftrace fprobe nodes */ +static int nr_ftrace_fprobes; +/* Is fprobe_ftrace_ops registered? */ +static bool fprobe_ftrace_registered; =20 static int fprobe_ftrace_add_ips(unsigned long *addrs, int num) { @@ -305,26 +366,33 @@ static int fprobe_ftrace_add_ips(unsigned long *addrs= , int num) if (ret) return ret; =20 - if (!fprobe_ftrace_active) { + if (!fprobe_ftrace_registered) { ret =3D register_ftrace_function(&fprobe_ftrace_ops); if (ret) { ftrace_free_filter(&fprobe_ftrace_ops); return ret; } + fprobe_ftrace_registered =3D true; } - fprobe_ftrace_active++; return 0; } =20 +static void __fprobe_ftrace_unregister(void) +{ + if (fprobe_ftrace_registered) { + unregister_ftrace_function(&fprobe_ftrace_ops); + ftrace_free_filter(&fprobe_ftrace_ops); + fprobe_ftrace_registered =3D false; + } +} + static void fprobe_ftrace_remove_ips(unsigned long *addrs, int num) { lockdep_assert_held(&fprobe_mutex); =20 - fprobe_ftrace_active--; - if (!fprobe_ftrace_active) { - unregister_ftrace_function(&fprobe_ftrace_ops); - ftrace_free_filter(&fprobe_ftrace_ops); - } else if (num) + if (!nr_ftrace_fprobes) + __fprobe_ftrace_unregister(); + else if (num) ftrace_set_filter_ips(&fprobe_ftrace_ops, addrs, num, 1, 0); } =20 @@ -333,6 +401,40 @@ static bool fprobe_is_ftrace(struct fprobe *fp) return !fp->exit_handler; } =20 +/* Node insertion and deletion requires the fprobe_mutex */ +static int insert_fprobe_node(struct fprobe_hlist_node *node, struct fprob= e *fp) +{ + int ret; + + lockdep_assert_held(&fprobe_mutex); + + ret =3D __insert_fprobe_node(node, fp); + if (!ret) { + if (fprobe_is_ftrace(fp)) + nr_ftrace_fprobes++; + else + nr_fgraph_fprobes++; + } + + return ret; +} + +static void delete_fprobe_node(struct fprobe_hlist_node *node) +{ + struct fprobe *fp; + + lockdep_assert_held(&fprobe_mutex); + + fp =3D READ_ONCE(node->fp); + if (fp) { + if (fprobe_is_ftrace(fp)) + nr_ftrace_fprobes--; + else + nr_fgraph_fprobes--; + } + __delete_fprobe_node(node); +} + static bool fprobe_exists_on_hash(unsigned long ip, bool ftrace) { struct rhlist_head *head, *pos; @@ -362,8 +464,15 @@ static bool fprobe_exists_on_hash(unsigned long ip, bo= ol ftrace) #ifdef CONFIG_MODULES static void fprobe_remove_ips(unsigned long *ips, unsigned int cnt) { - ftrace_set_filter_ips(&fprobe_graph_ops.ops, ips, cnt, 1, 0); - ftrace_set_filter_ips(&fprobe_ftrace_ops, ips, cnt, 1, 0); + if (!nr_fgraph_fprobes) + __fprobe_graph_unregister(); + else + ftrace_set_filter_ips(&fprobe_graph_ops.ops, ips, cnt, 1, 0); + + if (!nr_ftrace_fprobes) + __fprobe_ftrace_unregister(); + else + ftrace_set_filter_ips(&fprobe_ftrace_ops, ips, cnt, 1, 0); } #endif #else @@ -381,6 +490,32 @@ static bool fprobe_is_ftrace(struct fprobe *fp) return false; } =20 +/* Node insertion and deletion requires the fprobe_mutex */ +static int insert_fprobe_node(struct fprobe_hlist_node *node, struct fprob= e *fp) +{ + int ret; + + lockdep_assert_held(&fprobe_mutex); + + ret =3D __insert_fprobe_node(node, fp); + if (!ret) + nr_fgraph_fprobes++; + + return ret; +} + +static void delete_fprobe_node(struct fprobe_hlist_node *node) +{ + struct fprobe *fp; + + lockdep_assert_held(&fprobe_mutex); + + fp =3D READ_ONCE(node->fp); + if (fp) + nr_fgraph_fprobes--; + __delete_fprobe_node(node); +} + static bool fprobe_exists_on_hash(unsigned long ip, bool ftrace __maybe_un= used) { struct rhlist_head *head, *pos; @@ -407,7 +542,10 @@ static bool fprobe_exists_on_hash(unsigned long ip, bo= ol ftrace __maybe_unused) #ifdef CONFIG_MODULES static void fprobe_remove_ips(unsigned long *ips, unsigned int cnt) { - ftrace_set_filter_ips(&fprobe_graph_ops.ops, ips, cnt, 1, 0); + if (!nr_fgraph_fprobes) + __fprobe_graph_unregister(); + else + ftrace_set_filter_ips(&fprobe_graph_ops.ops, ips, cnt, 1, 0); } #endif #endif /* !CONFIG_DYNAMIC_FTRACE_WITH_ARGS && !CONFIG_DYNAMIC_FTRACE_WITH_= REGS */ @@ -535,48 +673,6 @@ static void fprobe_return(struct ftrace_graph_ret *tra= ce, } NOKPROBE_SYMBOL(fprobe_return); =20 -static struct fgraph_ops fprobe_graph_ops =3D { - .entryfunc =3D fprobe_fgraph_entry, - .retfunc =3D fprobe_return, -}; -static int fprobe_graph_active; - -/* Add @addrs to the ftrace filter and register fgraph if needed. */ -static int fprobe_graph_add_ips(unsigned long *addrs, int num) -{ - int ret; - - lockdep_assert_held(&fprobe_mutex); - - ret =3D ftrace_set_filter_ips(&fprobe_graph_ops.ops, addrs, num, 0, 0); - if (ret) - return ret; - - if (!fprobe_graph_active) { - ret =3D register_ftrace_graph(&fprobe_graph_ops); - if (WARN_ON_ONCE(ret)) { - ftrace_free_filter(&fprobe_graph_ops.ops); - return ret; - } - } - fprobe_graph_active++; - return 0; -} - -/* Remove @addrs from the ftrace filter and unregister fgraph if possible.= */ -static void fprobe_graph_remove_ips(unsigned long *addrs, int num) -{ - lockdep_assert_held(&fprobe_mutex); - - fprobe_graph_active--; - /* Q: should we unregister it ? */ - if (!fprobe_graph_active) { - unregister_ftrace_graph(&fprobe_graph_ops); - ftrace_free_filter(&fprobe_graph_ops.ops); - } else if (num) - ftrace_set_filter_ips(&fprobe_graph_ops.ops, addrs, num, 1, 0); -} - #ifdef CONFIG_MODULES =20 #define FPROBE_IPS_BATCH_INIT 128 From nobody Tue Jun 16 09:01:24 2026 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 8B90634753F; Fri, 17 Apr 2026 16:18:42 +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=1776442722; cv=none; b=WP760Jm5AhIyGqxcEP6USzcUzdPvssXdgMTLfMRQzl16hn/ZcdcbzPset0AawlQGc5/DYk2jg4Vmyfjk0JQ0Z+mhzXc2PM7+FUPyYe+dvRJlAca+PuumVvYHPzSRPlsC9sk9A0t/x3G6Oybvi7KSeV6tYe9a7UnO1gX2ANq+zHU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776442722; c=relaxed/simple; bh=pEVTDWITMqNKfplBBLj1hjBHsy+PgbkTjXTBWjivCB0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=nH/dPjoF11HQQx9LmAL5WrnsDjZWVk/2KDbIvbJndju4ERe5HZp0PyM+cMt7tkM8+Ewe+1YenIHwLM/kmkwJk+7FSq82SoDj2Zqb7BZo3ubYxZBgQ7ai4QU9i1olWKO0CgE7GTNMgvKivv1DATyfuAY+jJ7boXmAz7kpCaNgeTk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=JQjMxOmi; 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="JQjMxOmi" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 4DB6CC19425; Fri, 17 Apr 2026 16:18:41 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1776442722; bh=pEVTDWITMqNKfplBBLj1hjBHsy+PgbkTjXTBWjivCB0=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=JQjMxOmiMZCK9QXuTFER17Elf8ao0b8gnsXwC8G3sH2hVHDYDVVWGsjJsyy3pkKNo I1Grm5I+3LjWkJbmbEc0vghrWu9RyXt7Hc6bsQAxgy3FggL+ZU84GkXwN+IX0SinCC lLHg2f4DfyHZBJaXpYZqF46J9anEOtRaCWD4LGftBmdVjx/vqFHAoQkJBd1nP7eJDP hYmDww+wU2Y+X91HtAk02ZxzisUnJ28o0Q1xZZOHys5/2Cmb8HM2XqaApECJLxGcMG JpROaE0XG09A9yZYngJdsT87fL3SG5JlGEgAAg8PQtw7UPc3M6jHJj5R7Ql4mjKec+ GAWsX21SMDEJQ== From: "Masami Hiramatsu (Google)" To: Steven Rostedt , Masami Hiramatsu Cc: Menglong Dong , Mathieu Desnoyers , jiang.biao@linux.dev, linux-kernel@vger.kernel.org, linux-trace-kernel@vger.kernel.org Subject: [PATCH v9 7/8] selftests/ftrace: Add a testcase for fprobe events on module Date: Sat, 18 Apr 2026 01:18:39 +0900 Message-ID: <177644271967.584467.9751522686479464647.stgit@mhiramat.tok.corp.google.com> X-Mailer: git-send-email 2.54.0.rc1.513.gad8abe7a5a-goog In-Reply-To: <177644266147.584467.8179035927318998910.stgit@mhiramat.tok.corp.google.com> References: <177644266147.584467.8179035927318998910.stgit@mhiramat.tok.corp.google.com> 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) Add a testcase for fprobe events on module, which unloads a kernel module on which fprobe events are probing and ensure the ftrace hash map is cleared correctly. Signed-off-by: Masami Hiramatsu (Google) --- Changes in v9: - Use "trace-events-sample" instead of "trace_events_sample" - Add checking unload module and remove core-kernel event case. - Check test module exists when unloading it in EXIT. Changes in v8: - Newly added. --- .../test.d/dynevent/add_remove_fprobe_module.tc | 87 ++++++++++++++++= ++++ 1 file changed, 87 insertions(+) create mode 100644 tools/testing/selftests/ftrace/test.d/dynevent/add_remo= ve_fprobe_module.tc diff --git a/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_fpro= be_module.tc b/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_fp= robe_module.tc new file mode 100644 index 000000000000..c358c5071f15 --- /dev/null +++ b/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_fprobe_modu= le.tc @@ -0,0 +1,87 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# description: Generic dynamic event - add/remove fprobe events on module +# requires: dynamic_events "f[:[/][]] [%return] [= ]":README enabled_functions + +rmmod trace-events-sample ||: +if ! modprobe trace-events-sample ; then + echo "No trace-events sample module - please make CONFIG_SAMPLE_TRACE_EV= ENTS=3Dm" + exit_unresolved; +fi +trap "lsmod | grep -q trace-event-sample && rmmod trace-events-sample" EXIT + +echo 0 > events/enable +echo > dynamic_events + +FUNC1=3D'foo_bar*' +FUNC2=3D'vfs_read' + +:;: "Add an event on the test module" ;: +echo "f:test1 $FUNC1" >> dynamic_events +echo 1 > events/fprobes/test1/enable + +:;: "Ensure it is enabled" ;: +funcs=3D`cat enabled_functions | wc -l` +test $funcs -ne 0 + +:;: "Check the enabled_functions is cleared on unloading" ;: +rmmod trace-events-sample +funcs=3D`cat enabled_functions | wc -l` +test $funcs -eq 0 + +:;: "Check it is kept clean" ;: +modprobe trace-events-sample +echo 1 > events/fprobes/test1/enable || echo "OK" +funcs=3D`cat enabled_functions | wc -l` +test $funcs -eq 0 + +:;: "Add another event not on the test module" ;: +echo "f:test2 $FUNC2" >> dynamic_events +echo 1 > events/fprobes/test2/enable + +:;: "Ensure it is enabled" ;: +ofuncs=3D`cat enabled_functions | wc -l` +test $ofuncs -ne 0 + +:;: "Disable and remove the first event" +echo 0 > events/fprobes/test1/enable +echo "-:fprobes/test1" >> dynamic_events +funcs=3D`cat enabled_functions | wc -l` +test $ofuncs -eq $funcs + +:;: "Disable and remove other events" ;: +echo 0 > events/fprobes/enable +echo > dynamic_events +funcs=3D`cat enabled_functions | wc -l` +test $funcs -eq 0 + +rmmod trace-events-sample + +:;: "Add events on kernel and test module" ;: +modprobe trace-events-sample +echo "f:test1 $FUNC1" >> dynamic_events +echo 1 > events/fprobes/test1/enable +echo "f:test2 $FUNC2" >> dynamic_events +echo 1 > events/fprobes/test2/enable +ofuncs=3D`cat enabled_functions | wc -l` +test $ofuncs -ne 0 + +:;: "Unload module (ftrace entry should be removed)" ;: +rmmod trace-events-sample +funcs=3D`cat enabled_functions | wc -l` +test $funcs -ne 0 +test $ofuncs -ne $funcs + +:;: "Disable and remove core-kernel fprobe event" ;: +echo 0 > events/fprobes/test2/enable +echo "-:fprobes/test2" >> dynamic_events + +:;: "Ensure ftrace is disabled." ;: +funcs=3D`cat enabled_functions | wc -l` +test $funcs -eq 0 + +echo 0 > events/fprobes/enable +echo > dynamic_events + +trap "" EXIT +clear_trace From nobody Tue Jun 16 09:01:24 2026 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 F4033349B16; Fri, 17 Apr 2026 16:18:51 +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=1776442732; cv=none; b=DsUP2IRPufAhvb+cBf1q03q8VSjaUcGURXOZxiIYX1yRaTi06eUVj+wKDXwwTYJbV/A6XAx3pVmZ8/lDLXaGBjbDyhyQwxirmomVWqdGttKIX5Xv6syxX6NGut0rYoqE3hcifarn+l0bDKThTfDQ17AOOI7FHPNqyKkbva1TNrU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776442732; c=relaxed/simple; bh=tZR2BVMsPTdwmjNRnZOzYXc0zoDf5+38hsPHRSxqelg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=YFvjAKEmInwB+2uBkytASdmwOxiRK6LxGo1sQRHYmUYz6EL9vgV5IT1QjwtGvj8GhuJfRHSKVsseM5QRpaM6C7jf7geHJGjDTltYRGaTOOBxwSr8D+DTtlC7I4daWV/jDWS+WexqLvzx3DKQDdsTpDFMwtL5fITqCvbdNpOv2fI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=VH53Myut; 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="VH53Myut" Received: by smtp.kernel.org (Postfix) with ESMTPSA id B4072C19425; Fri, 17 Apr 2026 16:18:50 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1776442731; bh=tZR2BVMsPTdwmjNRnZOzYXc0zoDf5+38hsPHRSxqelg=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=VH53Myutt5rbJWZeGBhrlUbkn0FRvkolVNCTeBP/fFFC9lvGUQKn95COEVhNO31+M hC0tyFPKX25aoOviCMw2VULMvtyMe6mUUDzDuGT7mLbRgaaEZA3ibQIuqX5atycsH3 0AuKf8SLZBq7ghULY12IZvVune8mwnmr43TA1vsE+DidUDJgnqYPFsBIFXn0Z8o6Vx 44vI66Jc24SIZNxnZWzRLB+3fdXbVMXkMngSohxqyMpmf5RWzEHEjvp/VnQHasCnNz ccaFUwVIwE9OuMlbxZBDZnECEnS80KGMuTrct3LxqlENwLK0ZSpvRzNyfn1+RniFpK Oe7xFa7EyuRLg== From: "Masami Hiramatsu (Google)" To: Steven Rostedt , Masami Hiramatsu Cc: Menglong Dong , Mathieu Desnoyers , jiang.biao@linux.dev, linux-kernel@vger.kernel.org, linux-trace-kernel@vger.kernel.org Subject: [PATCH v9 8/8] selftests/ftrace: Add a testcase for multiple fprobe events Date: Sat, 18 Apr 2026 01:18:47 +0900 Message-ID: <177644272751.584467.1899994392012441962.stgit@mhiramat.tok.corp.google.com> X-Mailer: git-send-email 2.54.0.rc1.513.gad8abe7a5a-goog In-Reply-To: <177644266147.584467.8179035927318998910.stgit@mhiramat.tok.corp.google.com> References: <177644266147.584467.8179035927318998910.stgit@mhiramat.tok.corp.google.com> 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) Add a testcase for multiple fprobe events on the same function so that it clears ftrace hash map correctly when removing the events. Signed-off-by: Masami Hiramatsu (Google) --- .../test.d/dynevent/add_remove_multiple_fprobe.tc | 69 ++++++++++++++++= ++++ 1 file changed, 69 insertions(+) create mode 100644 tools/testing/selftests/ftrace/test.d/dynevent/add_remo= ve_multiple_fprobe.tc diff --git a/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_mult= iple_fprobe.tc b/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_= multiple_fprobe.tc new file mode 100644 index 000000000000..f2cbf2ffd29b --- /dev/null +++ b/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_multiple_fp= robe.tc @@ -0,0 +1,69 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# description: Generic dynamic event - add/remove multiple fprobe events o= n the same function +# requires: dynamic_events "f[:[/][]] [%return] [= ]":README enabled_functions + +echo 0 > events/enable +echo > dynamic_events + +PLACE=3Dvfs_read +PLACE2=3Dvfs_open + +:;: 'Ensure no other ftrace user' ;: +test `cat enabled_functions | wc -l` -eq 0 || exit_unresolved + +:;: 'Test case 1: leave entry event' ;: +:;: 'Add entry and exit events on the same place' ;: +echo "f:event1 ${PLACE}" >> dynamic_events +echo "f:event2 ${PLACE}%return" >> dynamic_events + +:;: 'Enable both of them' ;: +echo 1 > events/fprobes/enable +test `cat enabled_functions | wc -l` -eq 1 + +:;: 'Disable and remove exit event' ;: +echo 0 > events/fprobes/event2/enable +echo -:event2 >> dynamic_events + +:;: 'Disable and remove all events' ;: +echo 0 > events/fprobes/enable +echo > dynamic_events + +:;: 'Add another event' ;: +echo "f:event3 ${PLACE2}%return" > dynamic_events +echo 1 > events/fprobes/enable +test `cat enabled_functions | wc -l` -eq 1 + +:;: 'No other ftrace user' ;: +echo 0 > events/fprobes/enable +echo > dynamic_events +test `cat enabled_functions | wc -l` -eq 0 + +:;: 'Test case 2: leave exit event' ;: +:;: 'Add entry and exit events on the same place' ;: +echo "f:event1 ${PLACE}" >> dynamic_events +echo "f:event2 ${PLACE}%return" >> dynamic_events + +:;: 'Enable both of them' ;: +echo 1 > events/fprobes/enable +test `cat enabled_functions | wc -l` -eq 1 + +:;: 'Disable and remove entry event' ;: +echo 0 > events/fprobes/event1/enable +echo -:event1 >> dynamic_events + +:;: 'Disable and remove all events' ;: +echo 0 > events/fprobes/enable +echo > dynamic_events + +:;: 'Add another event' ;: +echo "f:event3 ${PLACE2}" > dynamic_events +echo 1 > events/fprobes/enable +test `cat enabled_functions | wc -l` -eq 1 + +:;: 'No other ftrace user' ;: +echo 0 > events/fprobes/enable +echo > dynamic_events +test `cat enabled_functions | wc -l` -eq 0 + +clear_trace