Skip to content

Commit a3dc298

Browse files
committed
tracing: fprobe: Cleanup fprobe hash when module unloading
Cleanup fprobe address hash table on module unloading because the target symbols will be disappeared when unloading module and not sure the same symbol is mapped on the same address. Note that this is at least disables the fprobes if a part of target symbols on the unloaded modules. Unlike kprobes, fprobe does not re-enable the probe point by itself. To do that, the caller should take care register/unregister fprobe when loading/unloading modules. This simplifies the fprobe state managememt related to the module loading/unloading. Link: https://lore.kernel.org/all/174343534473.843280.13988101014957210732.stgit@devnote2/ Fixes: 4346ba1 ("fprobe: Rewrite fprobe on function-graph tracer") Signed-off-by: Masami Hiramatsu (Google) <[email protected]>
1 parent dd94150 commit a3dc298

File tree

1 file changed

+101
-2
lines changed

1 file changed

+101
-2
lines changed

kernel/trace/fprobe.c

Lines changed: 101 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,11 @@ static bool delete_fprobe_node(struct fprobe_hlist_node *node)
8989
{
9090
lockdep_assert_held(&fprobe_mutex);
9191

92-
WRITE_ONCE(node->fp, NULL);
93-
hlist_del_rcu(&node->hlist);
92+
/* Avoid double deleting */
93+
if (READ_ONCE(node->fp) != NULL) {
94+
WRITE_ONCE(node->fp, NULL);
95+
hlist_del_rcu(&node->hlist);
96+
}
9497
return !!find_first_fprobe_node(node->addr);
9598
}
9699

@@ -411,6 +414,102 @@ static void fprobe_graph_remove_ips(unsigned long *addrs, int num)
411414
ftrace_set_filter_ips(&fprobe_graph_ops.ops, addrs, num, 1, 0);
412415
}
413416

417+
#ifdef CONFIG_MODULES
418+
419+
#define FPROBE_IPS_BATCH_INIT 8
420+
/* instruction pointer address list */
421+
struct fprobe_addr_list {
422+
int index;
423+
int size;
424+
unsigned long *addrs;
425+
};
426+
427+
static int fprobe_addr_list_add(struct fprobe_addr_list *alist, unsigned long addr)
428+
{
429+
unsigned long *addrs;
430+
431+
if (alist->index >= alist->size)
432+
return -ENOMEM;
433+
434+
alist->addrs[alist->index++] = addr;
435+
if (alist->index < alist->size)
436+
return 0;
437+
438+
/* Expand the address list */
439+
addrs = kcalloc(alist->size * 2, sizeof(*addrs), GFP_KERNEL);
440+
if (!addrs)
441+
return -ENOMEM;
442+
443+
memcpy(addrs, alist->addrs, alist->size * sizeof(*addrs));
444+
alist->size *= 2;
445+
kfree(alist->addrs);
446+
alist->addrs = addrs;
447+
448+
return 0;
449+
}
450+
451+
static void fprobe_remove_node_in_module(struct module *mod, struct hlist_head *head,
452+
struct fprobe_addr_list *alist)
453+
{
454+
struct fprobe_hlist_node *node;
455+
int ret = 0;
456+
457+
hlist_for_each_entry_rcu(node, head, hlist) {
458+
if (!within_module(node->addr, mod))
459+
continue;
460+
if (delete_fprobe_node(node))
461+
continue;
462+
/*
463+
* If failed to update alist, just continue to update hlist.
464+
* Therefore, at list user handler will not hit anymore.
465+
*/
466+
if (!ret)
467+
ret = fprobe_addr_list_add(alist, node->addr);
468+
}
469+
}
470+
471+
/* Handle module unloading to manage fprobe_ip_table. */
472+
static int fprobe_module_callback(struct notifier_block *nb,
473+
unsigned long val, void *data)
474+
{
475+
struct fprobe_addr_list alist = {.size = FPROBE_IPS_BATCH_INIT};
476+
struct module *mod = data;
477+
int i;
478+
479+
if (val != MODULE_STATE_GOING)
480+
return NOTIFY_DONE;
481+
482+
alist.addrs = kcalloc(alist.size, sizeof(*alist.addrs), GFP_KERNEL);
483+
/* If failed to alloc memory, we can not remove ips from hash. */
484+
if (!alist.addrs)
485+
return NOTIFY_DONE;
486+
487+
mutex_lock(&fprobe_mutex);
488+
for (i = 0; i < FPROBE_IP_TABLE_SIZE; i++)
489+
fprobe_remove_node_in_module(mod, &fprobe_ip_table[i], &alist);
490+
491+
if (alist.index < alist.size && alist.index > 0)
492+
ftrace_set_filter_ips(&fprobe_graph_ops.ops,
493+
alist.addrs, alist.index, 1, 0);
494+
mutex_unlock(&fprobe_mutex);
495+
496+
kfree(alist.addrs);
497+
498+
return NOTIFY_DONE;
499+
}
500+
501+
static struct notifier_block fprobe_module_nb = {
502+
.notifier_call = fprobe_module_callback,
503+
.priority = 0,
504+
};
505+
506+
static int __init init_fprobe_module(void)
507+
{
508+
return register_module_notifier(&fprobe_module_nb);
509+
}
510+
early_initcall(init_fprobe_module);
511+
#endif
512+
414513
static int symbols_cmp(const void *a, const void *b)
415514
{
416515
const char **str_a = (const char **) a;

0 commit comments

Comments
 (0)