Skip to content

Commit 845947a

Browse files
committed
tracing/fprobe: Remove fprobe from hash in failure path
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. Link: https://lore.kernel.org/all/177669366417.132053.17874946321744910456.stgit@mhiramat.tok.corp.google.com/ Fixes: 4346ba1 ("fprobe: Rewrite fprobe on function-graph tracer") Cc: stable@vger.kernel.org Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
1 parent 1aec9e5 commit 845947a

1 file changed

Lines changed: 47 additions & 43 deletions

File tree

kernel/trace/fprobe.c

Lines changed: 47 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -79,20 +79,27 @@ static const struct rhashtable_params fprobe_rht_params = {
7979
};
8080

8181
/* Node insertion and deletion requires the fprobe_mutex */
82-
static int insert_fprobe_node(struct fprobe_hlist_node *node)
82+
static int insert_fprobe_node(struct fprobe_hlist_node *node, struct fprobe *fp)
8383
{
84+
int ret;
85+
8486
lockdep_assert_held(&fprobe_mutex);
8587

86-
return rhltable_insert(&fprobe_ip_table, &node->hlist, fprobe_rht_params);
88+
ret = rhltable_insert(&fprobe_ip_table, &node->hlist, fprobe_rht_params);
89+
/* Set the fprobe pointer if insertion was successful. */
90+
if (!ret)
91+
WRITE_ONCE(node->fp, fp);
92+
return ret;
8793
}
8894

8995
/* Return true if there are synonims */
9096
static bool delete_fprobe_node(struct fprobe_hlist_node *node)
9197
{
92-
lockdep_assert_held(&fprobe_mutex);
9398
bool ret;
9499

95-
/* Avoid double deleting */
100+
lockdep_assert_held(&fprobe_mutex);
101+
102+
/* Avoid double deleting and non-inserted nodes */
96103
if (READ_ONCE(node->fp) != NULL) {
97104
WRITE_ONCE(node->fp, NULL);
98105
rhltable_remove(&fprobe_ip_table, &node->hlist,
@@ -756,7 +763,6 @@ static int fprobe_init(struct fprobe *fp, unsigned long *addrs, int num)
756763
fp->hlist_array = hlist_array;
757764
hlist_array->fp = fp;
758765
for (i = 0; i < num; i++) {
759-
hlist_array->array[i].fp = fp;
760766
addr = ftrace_location(addrs[i]);
761767
if (!addr) {
762768
fprobe_fail_cleanup(fp);
@@ -820,6 +826,8 @@ int register_fprobe(struct fprobe *fp, const char *filter, const char *notfilter
820826
}
821827
EXPORT_SYMBOL_GPL(register_fprobe);
822828

829+
static int unregister_fprobe_nolock(struct fprobe *fp);
830+
823831
/**
824832
* register_fprobe_ips() - Register fprobe to ftrace by address.
825833
* @fp: A fprobe data structure to be registered.
@@ -846,28 +854,25 @@ int register_fprobe_ips(struct fprobe *fp, unsigned long *addrs, int num)
846854
if (ret)
847855
return ret;
848856

849-
hlist_array = fp->hlist_array;
850857
if (fprobe_is_ftrace(fp))
851858
ret = fprobe_ftrace_add_ips(addrs, num);
852859
else
853860
ret = fprobe_graph_add_ips(addrs, num);
854-
855-
if (!ret) {
856-
add_fprobe_hash(fp);
857-
for (i = 0; i < hlist_array->size; i++) {
858-
ret = insert_fprobe_node(&hlist_array->array[i]);
859-
if (ret)
860-
break;
861-
}
862-
/* fallback on insert error */
863-
if (ret) {
864-
for (i--; i >= 0; i--)
865-
delete_fprobe_node(&hlist_array->array[i]);
866-
}
861+
if (ret) {
862+
fprobe_fail_cleanup(fp);
863+
return ret;
867864
}
868865

869-
if (ret)
870-
fprobe_fail_cleanup(fp);
866+
hlist_array = fp->hlist_array;
867+
ret = add_fprobe_hash(fp);
868+
for (i = 0; i < hlist_array->size && !ret; i++)
869+
ret = insert_fprobe_node(&hlist_array->array[i], fp);
870+
871+
if (ret) {
872+
unregister_fprobe_nolock(fp);
873+
/* In error case, wait for clean up safely. */
874+
synchronize_rcu();
875+
}
871876

872877
return ret;
873878
}
@@ -911,27 +916,12 @@ bool fprobe_is_registered(struct fprobe *fp)
911916
return true;
912917
}
913918

914-
/**
915-
* unregister_fprobe() - Unregister fprobe.
916-
* @fp: A fprobe data structure to be unregistered.
917-
*
918-
* Unregister fprobe (and remove ftrace hooks from the function entries).
919-
*
920-
* Return 0 if @fp is unregistered successfully, -errno if not.
921-
*/
922-
int unregister_fprobe(struct fprobe *fp)
919+
static int unregister_fprobe_nolock(struct fprobe *fp)
923920
{
924-
struct fprobe_hlist *hlist_array;
921+
struct fprobe_hlist *hlist_array = fp->hlist_array;
925922
unsigned long *addrs = NULL;
926-
int ret = 0, i, count;
923+
int i, count;
927924

928-
mutex_lock(&fprobe_mutex);
929-
if (!fp || !fprobe_registered(fp)) {
930-
ret = -EINVAL;
931-
goto out;
932-
}
933-
934-
hlist_array = fp->hlist_array;
935925
addrs = kcalloc(hlist_array->size, sizeof(unsigned long), GFP_KERNEL);
936926
/*
937927
* This will remove fprobe_hash_node from the hash table even if
@@ -957,12 +947,26 @@ int unregister_fprobe(struct fprobe *fp)
957947

958948
kfree_rcu(hlist_array, rcu);
959949
fp->hlist_array = NULL;
950+
kfree(addrs);
960951

961-
out:
962-
mutex_unlock(&fprobe_mutex);
952+
return 0;
953+
}
963954

964-
kfree(addrs);
965-
return ret;
955+
/**
956+
* unregister_fprobe() - Unregister fprobe.
957+
* @fp: A fprobe data structure to be unregistered.
958+
*
959+
* Unregister fprobe (and remove ftrace hooks from the function entries).
960+
*
961+
* Return 0 if @fp is unregistered successfully, -errno if not.
962+
*/
963+
int unregister_fprobe(struct fprobe *fp)
964+
{
965+
guard(mutex)(&fprobe_mutex);
966+
if (!fp || !fprobe_registered(fp))
967+
return -EINVAL;
968+
969+
return unregister_fprobe_nolock(fp);
966970
}
967971
EXPORT_SYMBOL_GPL(unregister_fprobe);
968972

0 commit comments

Comments
 (0)