Skip to content

Commit aa72812

Browse files
committed
tracing/fprobe: Avoid kcalloc() in rcu_read_lock section
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. Link: https://lore.kernel.org/all/177669367206.132053.1493637946869032744.stgit@mhiramat.tok.corp.google.com/ Fixes: 0de4c70 ("tracing: fprobe: use rhltable for fprobe_ip_table") Cc: stable@vger.kernel.org Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
1 parent 845947a commit aa72812

1 file changed

Lines changed: 45 additions & 47 deletions

File tree

kernel/trace/fprobe.c

Lines changed: 45 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -344,11 +344,10 @@ static bool fprobe_is_ftrace(struct fprobe *fp)
344344
}
345345

346346
#ifdef CONFIG_MODULES
347-
static void fprobe_set_ips(unsigned long *ips, unsigned int cnt, int remove,
348-
int reset)
347+
static void fprobe_remove_ips(unsigned long *ips, unsigned int cnt)
349348
{
350-
ftrace_set_filter_ips(&fprobe_graph_ops.ops, ips, cnt, remove, reset);
351-
ftrace_set_filter_ips(&fprobe_ftrace_ops, ips, cnt, remove, reset);
349+
ftrace_set_filter_ips(&fprobe_graph_ops.ops, ips, cnt, 1, 0);
350+
ftrace_set_filter_ips(&fprobe_ftrace_ops, ips, cnt, 1, 0);
352351
}
353352
#endif
354353
#else
@@ -367,10 +366,9 @@ static bool fprobe_is_ftrace(struct fprobe *fp)
367366
}
368367

369368
#ifdef CONFIG_MODULES
370-
static void fprobe_set_ips(unsigned long *ips, unsigned int cnt, int remove,
371-
int reset)
369+
static void fprobe_remove_ips(unsigned long *ips, unsigned int cnt)
372370
{
373-
ftrace_set_filter_ips(&fprobe_graph_ops.ops, ips, cnt, remove, reset);
371+
ftrace_set_filter_ips(&fprobe_graph_ops.ops, ips, cnt, 1, 0);
374372
}
375373
#endif
376374
#endif /* !CONFIG_DYNAMIC_FTRACE_WITH_ARGS && !CONFIG_DYNAMIC_FTRACE_WITH_REGS */
@@ -542,53 +540,32 @@ static void fprobe_graph_remove_ips(unsigned long *addrs, int num)
542540

543541
#ifdef CONFIG_MODULES
544542

545-
#define FPROBE_IPS_BATCH_INIT 8
543+
#define FPROBE_IPS_BATCH_INIT 128
546544
/* instruction pointer address list */
547545
struct fprobe_addr_list {
548546
int index;
549547
int size;
550548
unsigned long *addrs;
551549
};
552550

553-
static int fprobe_addr_list_add(struct fprobe_addr_list *alist, unsigned long addr)
551+
static int fprobe_remove_node_in_module(struct module *mod, struct fprobe_hlist_node *node,
552+
struct fprobe_addr_list *alist)
554553
{
555-
unsigned long *addrs;
556-
557-
/* Previously we failed to expand the list. */
558-
if (alist->index == alist->size)
559-
return -ENOSPC;
560-
561-
alist->addrs[alist->index++] = addr;
562-
if (alist->index < alist->size)
554+
if (!within_module(node->addr, mod))
563555
return 0;
564556

565-
/* Expand the address list */
566-
addrs = kcalloc(alist->size * 2, sizeof(*addrs), GFP_KERNEL);
567-
if (!addrs)
568-
return -ENOMEM;
569-
570-
memcpy(addrs, alist->addrs, alist->size * sizeof(*addrs));
571-
alist->size *= 2;
572-
kfree(alist->addrs);
573-
alist->addrs = addrs;
557+
if (delete_fprobe_node(node))
558+
return 0;
559+
/* If no address list is available, we can't track this address. */
560+
if (!alist->addrs)
561+
return 0;
574562

563+
alist->addrs[alist->index++] = node->addr;
564+
if (alist->index == alist->size)
565+
return -ENOSPC;
575566
return 0;
576567
}
577568

578-
static void fprobe_remove_node_in_module(struct module *mod, struct fprobe_hlist_node *node,
579-
struct fprobe_addr_list *alist)
580-
{
581-
if (!within_module(node->addr, mod))
582-
return;
583-
if (delete_fprobe_node(node))
584-
return;
585-
/*
586-
* If failed to update alist, just continue to update hlist.
587-
* Therefore, at list user handler will not hit anymore.
588-
*/
589-
fprobe_addr_list_add(alist, node->addr);
590-
}
591-
592569
/* Handle module unloading to manage fprobe_ip_table. */
593570
static int fprobe_module_callback(struct notifier_block *nb,
594571
unsigned long val, void *data)
@@ -597,29 +574,50 @@ static int fprobe_module_callback(struct notifier_block *nb,
597574
struct fprobe_hlist_node *node;
598575
struct rhashtable_iter iter;
599576
struct module *mod = data;
577+
bool retry;
600578

601579
if (val != MODULE_STATE_GOING)
602580
return NOTIFY_DONE;
603581

604582
alist.addrs = kcalloc(alist.size, sizeof(*alist.addrs), GFP_KERNEL);
605-
/* If failed to alloc memory, we can not remove ips from hash. */
606-
if (!alist.addrs)
607-
return NOTIFY_DONE;
583+
/*
584+
* If failed to alloc memory, ftrace_ops will not be able to remove ips from
585+
* hash, but we can still remove nodes from fprobe_ip_table, so we can avoid
586+
* the potential wrong callback. So just print a warning here and try to
587+
* continue without address list.
588+
*/
589+
WARN_ONCE(!alist.addrs,
590+
"Failed to allocate memory for fprobe_addr_list, ftrace_ops will not be updated");
608591

609592
mutex_lock(&fprobe_mutex);
593+
again:
594+
retry = false;
595+
alist.index = 0;
610596
rhltable_walk_enter(&fprobe_ip_table, &iter);
611597
do {
612598
rhashtable_walk_start(&iter);
613599

614600
while ((node = rhashtable_walk_next(&iter)) && !IS_ERR(node))
615-
fprobe_remove_node_in_module(mod, node, &alist);
601+
if (fprobe_remove_node_in_module(mod, node, &alist) < 0) {
602+
retry = true;
603+
break;
604+
}
616605

617606
rhashtable_walk_stop(&iter);
618-
} while (node == ERR_PTR(-EAGAIN));
607+
} while (node == ERR_PTR(-EAGAIN) && !retry);
619608
rhashtable_walk_exit(&iter);
609+
/* Remove any ips from hash table(s) */
610+
if (alist.index > 0) {
611+
fprobe_remove_ips(alist.addrs, alist.index);
612+
/*
613+
* If we break rhashtable walk loop except for -EAGAIN, we need
614+
* to restart looping from start for safety. Anyway, this is
615+
* not a hotpath.
616+
*/
617+
if (retry)
618+
goto again;
619+
}
620620

621-
if (alist.index > 0)
622-
fprobe_set_ips(alist.addrs, alist.index, 1, 0);
623621
mutex_unlock(&fprobe_mutex);
624622

625623
kfree(alist.addrs);

0 commit comments

Comments
 (0)