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);
