From 0e6f876894759615a15e193903de315c628ce1b5 Mon Sep 17 00:00:00 2001 From: Michael Jeanson Date: Thu, 17 Oct 2024 11:56:02 -0400 Subject: [PATCH] Fix: uprobes: make uprobe_register() return struct uprobe * (v6.12) See upstream commits : commit 3c83a9ad0295eb63bdeb81d821b8c3b9417fbcac Author: Oleg Nesterov Date: Thu Aug 1 15:27:34 2024 +0200 uprobes: make uprobe_register() return struct uprobe * This way uprobe_unregister() and uprobe_apply() can use "struct uprobe *" rather than inode + offset. This simplifies the code and allows to avoid the unnecessary find_uprobe() + put_uprobe() in these functions. TODO: uprobe_unregister() still needs get_uprobe/put_uprobe to ensure that this uprobe can't be freed before up_write(&uprobe->register_rwsem). commit 04b01625da130c7521b768996cd5e48052198b97 Author: Peter Zijlstra Date: Tue Sep 3 10:46:00 2024 -0700 perf/uprobe: split uprobe_unregister() With uprobe_unregister() having grown a synchronize_srcu(), it becomes fairly slow to call. Esp. since both users of this API call it in a loop. Peel off the sync_srcu() and do it once, after the loop. We also need to add uprobe_unregister_sync() into uprobe_register()'s error handling path, as we need to be careful about returning to the caller before we have a guarantee that partially attached consumer won't be called anymore. This is an unlikely slow path and this should be totally fine to be slow in the case of a failed attach. commit e04332ebc8ac128fa551e83f1161ab1c094d13a9 Author: Oleg Nesterov Date: Thu Aug 1 15:27:28 2024 +0200 uprobes: kill uprobe_register_refctr() It doesn't make any sense to have 2 versions of _register(). Note that trace_uprobe_enable(), the only user of uprobe_register(), doesn't need to check tu->ref_ctr_offset to decide which one should be used, it could safely pass ref_ctr_offset == 0 to uprobe_register_refctr(). Add this argument to uprobe_register(), update the callers, and kill uprobe_register_refctr(). Change-Id: I8d1f9a5db1f19c2bc2029709ae36f82e86f6fe58 Signed-off-by: Michael Jeanson Signed-off-by: Mathieu Desnoyers --- include/lttng/events-internal.h | 1 + src/lttng-uprobes.c | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/include/lttng/events-internal.h b/include/lttng/events-internal.h index 224bfec1..002ed6af 100644 --- a/include/lttng/events-internal.h +++ b/include/lttng/events-internal.h @@ -49,6 +49,7 @@ struct lttng_krp; /* Kretprobe handling */ struct lttng_uprobe_handler { struct lttng_kernel_event_common *event; loff_t offset; + struct uprobe *uprobe; struct uprobe_consumer up_consumer; struct list_head node; }; diff --git a/src/lttng-uprobes.c b/src/lttng-uprobes.c index 1c81b9de..2d97155e 100644 --- a/src/lttng-uprobes.c +++ b/src/lttng-uprobes.c @@ -232,8 +232,16 @@ int lttng_uprobes_add_callsite(struct lttng_uprobe *uprobe, goto register_error; } +#if (LTTNG_LINUX_VERSION_CODE >= LTTNG_KERNEL_VERSION(6,12,0)) + ret = 0; + uprobe_handler->uprobe = uprobe_register(uprobe->inode, + uprobe_handler->offset, 0, &uprobe_handler->up_consumer); + if (IS_ERR(uprobe_handler->uprobe)) + ret = -1; +#else ret = uprobe_register(uprobe->inode, uprobe_handler->offset, &uprobe_handler->up_consumer); +#endif if (ret) { printk(KERN_WARNING "LTTng: Error registering probe on inode %lu " "and offset 0x%llx\n", uprobe->inode->i_ino, @@ -304,15 +312,39 @@ void lttng_uprobes_unregister(struct inode *inode, struct list_head *head) { struct lttng_uprobe_handler *iter, *tmp; +#if (LTTNG_LINUX_VERSION_CODE >= LTTNG_KERNEL_VERSION(6,12,0)) + /* + * Iterate over the list of handler, unregister each uprobe. + */ + list_for_each_entry(iter, head, node) { + uprobe_unregister_nosync(iter->uprobe, &iter->up_consumer); + iter->uprobe = NULL; + } + + /* + * Call synchronize_srcu() on uprobes_srcu. + */ + uprobe_unregister_sync(); + /* * Iterate over the list of handler, remove each handler from the list * and free the struct. */ + list_for_each_entry_safe(iter, tmp, head, node) { + list_del(&iter->node); + kfree(iter); + } +#else + /* + * Iterate over the list of handler, unregister each uprobe, remove + * each handler from the list and free the struct. + */ list_for_each_entry_safe(iter, tmp, head, node) { uprobe_unregister(inode, iter->offset, &iter->up_consumer); list_del(&iter->node); kfree(iter); } +#endif } void lttng_uprobes_unregister_event(struct lttng_kernel_event_common *event) -- 2.34.1