From: Masami Hiramatsu (Google) <[email protected]>

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: [email protected]
Signed-off-by: Masami Hiramatsu (Google) <[email protected]>
---
 kernel/trace/fprobe.c |   53 ++++++++++++++++++-------------------------------
 1 file changed, 19 insertions(+), 34 deletions(-)

diff --git a/kernel/trace/fprobe.c b/kernel/trace/fprobe.c
index 56d145017902..058cf6ef7ebb 100644
--- a/kernel/trace/fprobe.c
+++ b/kernel/trace/fprobe.c
@@ -536,7 +536,7 @@ static void fprobe_graph_remove_ips(unsigned long *addrs, 
int num)
 
 #ifdef CONFIG_MODULES
 
-#define FPROBE_IPS_BATCH_INIT 8
+#define FPROBE_IPS_BATCH_INIT 128
 /* instruction pointer address list */
 struct fprobe_addr_list {
        int index;
@@ -544,45 +544,21 @@ struct fprobe_addr_list {
        unsigned long *addrs;
 };
 
-static int fprobe_addr_list_add(struct fprobe_addr_list *alist, unsigned long 
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 == alist->size)
-               return -ENOSPC;
-
-       alist->addrs[alist->index++] = addr;
-       if (alist->index < alist->size)
+       if (!within_module(node->addr, mod))
                return 0;
 
-       /* Expand the address list */
-       addrs = kcalloc(alist->size * 2, sizeof(*addrs), GFP_KERNEL);
-       if (!addrs)
-               return -ENOMEM;
-
-       memcpy(addrs, alist->addrs, alist->size * sizeof(*addrs));
-       alist->size *= 2;
-       kfree(alist->addrs);
-       alist->addrs = addrs;
+       if (delete_fprobe_node(node))
+               return 0;
 
+       alist->addrs[alist->index++] = node->addr;
+       if (alist->index == alist->size)
+               return -ENOSPC;
        return 0;
 }
 
-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)
@@ -591,6 +567,7 @@ static int fprobe_module_callback(struct notifier_block *nb,
        struct fprobe_hlist_node *node;
        struct rhashtable_iter iter;
        struct module *mod = data;
+       bool retry;
 
        if (val != MODULE_STATE_GOING)
                return NOTIFY_DONE;
@@ -600,13 +577,19 @@ static int fprobe_module_callback(struct notifier_block 
*nb,
        if (!alist.addrs)
                return NOTIFY_DONE;
 
+retry:
+       retry = false;
+       alist.index = 0;
        mutex_lock(&fprobe_mutex);
        rhltable_walk_enter(&fprobe_ip_table, &iter);
        do {
                rhashtable_walk_start(&iter);
 
                while ((node = 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 = true;
+                               break;
+                       }
 
                rhashtable_walk_stop(&iter);
        } while (node == ERR_PTR(-EAGAIN));
@@ -615,6 +598,8 @@ static int fprobe_module_callback(struct notifier_block *nb,
        if (alist.index > 0)
                fprobe_set_ips(alist.addrs, alist.index, 1, 0);
        mutex_unlock(&fprobe_mutex);
+       if (retry)
+               goto retry;
 
        kfree(alist.addrs);
 


Reply via email to