From 853116e64f56d81c1bcebd512a313a5036bec2df Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 29 Jul 2025 12:28:04 +0200 Subject: [PATCH 01/10] ftrace: Make alloc_and_copy_ftrace_hash direct friendly Make alloc_and_copy_ftrace_hash to copy also direct address for each hash entry. Signed-off-by: Jiri Olsa --- kernel/trace/ftrace.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 4203fad56b6c5..5b8f565a12589 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -1190,7 +1190,7 @@ static void __add_hash_entry(struct ftrace_hash *hash, } static struct ftrace_func_entry * -add_hash_entry(struct ftrace_hash *hash, unsigned long ip) +add_hash_entry_direct(struct ftrace_hash *hash, unsigned long ip, unsigned long direct) { struct ftrace_func_entry *entry; @@ -1199,11 +1199,18 @@ add_hash_entry(struct ftrace_hash *hash, unsigned long ip) return NULL; entry->ip = ip; + entry->direct = direct; __add_hash_entry(hash, entry); return entry; } +static struct ftrace_func_entry * +add_hash_entry(struct ftrace_hash *hash, unsigned long ip) +{ + return add_hash_entry_direct(hash, ip, 0); +} + static void free_hash_entry(struct ftrace_hash *hash, struct ftrace_func_entry *entry) @@ -1376,7 +1383,7 @@ alloc_and_copy_ftrace_hash(int size_bits, struct ftrace_hash *hash) size = 1 << hash->size_bits; for (i = 0; i < size; i++) { hlist_for_each_entry(entry, &hash->buckets[i], hlist) { - if (add_hash_entry(new_hash, entry->ip) == NULL) + if (add_hash_entry_direct(new_hash, entry->ip, entry->direct) == NULL) goto free_hash; } } From ad29c4311879529864c45f04dd08e8b4b3ab9c77 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 29 Jul 2025 12:28:05 +0200 Subject: [PATCH 02/10] ftrace: Add register_ftrace_direct_hash function Adding register_ftrace_direct_hash function that registers all entries (ip -> direct) provided in hash argument. The difference to current register_ftrace_direct is - hash argument that allows to register multiple ip -> direct entries at once - we can call register_ftrace_direct_hash multiple times on the same ftrace_ops object, becase after first registration with register_ftrace_function_nolock, it uses ftrace_update_ops to update the ftrace_ops object This change will allow us to have simple ftrace_ops for all bpf direct interface users in following changes. Signed-off-by: Jiri Olsa --- include/linux/ftrace.h | 7 +++ kernel/trace/ftrace.c | 123 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 130 insertions(+) diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index b672ca15f2655..e45bcc9de53bf 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -526,6 +526,8 @@ int unregister_ftrace_direct(struct ftrace_ops *ops, unsigned long addr, int modify_ftrace_direct(struct ftrace_ops *ops, unsigned long addr); int modify_ftrace_direct_nolock(struct ftrace_ops *ops, unsigned long addr); +int register_ftrace_direct_hash(struct ftrace_ops *ops, struct ftrace_hash *hash); + void ftrace_stub_direct_tramp(void); #else @@ -552,6 +554,11 @@ static inline int modify_ftrace_direct_nolock(struct ftrace_ops *ops, unsigned l return -ENODEV; } +int register_ftrace_direct_hash(struct ftrace_ops *ops, struct ftrace_hash *hash) +{ + return -ENODEV; +} + /* * This must be implemented by the architecture. * It is the way the ftrace direct_ops helper, when called diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 5b8f565a12589..1dbb113f4e9c2 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -6219,6 +6219,129 @@ int modify_ftrace_direct(struct ftrace_ops *ops, unsigned long addr) return err; } EXPORT_SYMBOL_GPL(modify_ftrace_direct); + +static unsigned long hash_count(struct ftrace_hash *hash) +{ + return hash ? hash->count : 0; +} + +/** + * hash_add - adds two struct ftrace_hash and returns the result + * @a: struct ftrace_hash object + * @b: struct ftrace_hash object + * + * Returns struct ftrace_hash object on success, NULL on error. + */ +static struct ftrace_hash *hash_add(struct ftrace_hash *a, struct ftrace_hash *b) +{ + struct ftrace_func_entry *entry; + struct ftrace_hash *add; + int size, i; + + size = hash_count(a) + hash_count(b); + if (size > 32) + size = 32; + + add = alloc_and_copy_ftrace_hash(fls(size), a); + if (!add) + goto error; + + size = 1 << b->size_bits; + for (i = 0; i < size; i++) { + hlist_for_each_entry(entry, &b->buckets[i], hlist) { + if (add_hash_entry_direct(add, entry->ip, entry->direct) == NULL) + goto error; + } + } + return add; + + error: + free_ftrace_hash(add); + return NULL; +} + +static void call_direct_funcs_hash(unsigned long ip, unsigned long pip, + struct ftrace_ops *ops, struct ftrace_regs *fregs) +{ + unsigned long addr; + + addr = ftrace_find_rec_direct(ip); + if (!addr) + return; + + arch_ftrace_set_direct_caller(fregs, addr); +} + +int register_ftrace_direct_hash(struct ftrace_ops *ops, struct ftrace_hash *hash) +{ + struct ftrace_hash *filter_hash = NULL, *new_hash = NULL, *free_hash = NULL; + struct ftrace_func_entry *entry; + int i, size, err; + bool reg; + + if (!hash_count(hash)) + return 0; + + mutex_lock(&direct_mutex); + + /* Make sure requested entry is not already registered. */ + size = 1 << hash->size_bits; + for (i = 0; i < size; i++) { + hlist_for_each_entry(entry, &hash->buckets[i], hlist) { + if (__ftrace_lookup_ip(direct_functions, entry->ip)) + goto out_unlock; + } + } + + filter_hash = ops->func_hash ? ops->func_hash->filter_hash : NULL; + + /* If there's nothing in filter_hash we need to register the ops. */ + reg = hash_count(filter_hash) == 0; + if (reg) { + if (ops->func || ops->trampoline) + goto out_unlock; + if (ops->flags & FTRACE_OPS_FL_ENABLED) + goto out_unlock; + } + + filter_hash = hash_add(filter_hash, hash); + if (!filter_hash) + goto out_unlock; + + new_hash = hash_add(direct_functions, hash); + if (!new_hash) + goto out_unlock; + + free_hash = direct_functions; + rcu_assign_pointer(direct_functions, new_hash); + new_hash = NULL; + + if (reg) { + ops->func = call_direct_funcs_hash; + ops->flags = MULTI_FLAGS; + ops->trampoline = FTRACE_REGS_ADDR; + ops->local_hash.filter_hash = filter_hash; + + err = register_ftrace_function_nolock(ops); + if (!err) + filter_hash = NULL; + } else { + err = ftrace_update_ops(ops, filter_hash, EMPTY_HASH); + } + + out_unlock: + mutex_unlock(&direct_mutex); + + if (free_hash && free_hash != EMPTY_HASH) + call_rcu_tasks(&free_hash->rcu, register_ftrace_direct_cb); + + if (filter_hash) + free_ftrace_hash(filter_hash); + + return err; +} +EXPORT_SYMBOL_GPL(register_ftrace_direct_hash); + #endif /* CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS */ /** From bf347e81e71a3a3496ea745e4067a48bfcef121f Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 29 Jul 2025 12:28:06 +0200 Subject: [PATCH 03/10] ftrace: Add unregister_ftrace_direct_hash function Adding unregister_ftrace_direct_hash function that unregisters all entries (ip -> direct) provided in hash argument. The difference to current unregister_ftrace_direct is - hash argument that allows to unregister multiple ip -> direct entries at once - we can call unregister_ftrace_direct_hash multiple times on the same ftrace_ops object, becase we do not need to unregister all entries at once, we can do it gradualy with the help of ftrace_update_ops function This change will allow us to have simple ftrace_ops for all bpf direct interface users in following changes. Signed-off-by: Jiri Olsa --- include/linux/ftrace.h | 6 +++ kernel/trace/ftrace.c | 98 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+) diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index e45bcc9de53bf..7ff6004498c05 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -527,6 +527,7 @@ int modify_ftrace_direct(struct ftrace_ops *ops, unsigned long addr); int modify_ftrace_direct_nolock(struct ftrace_ops *ops, unsigned long addr); int register_ftrace_direct_hash(struct ftrace_ops *ops, struct ftrace_hash *hash); +int unregister_ftrace_direct_hash(struct ftrace_ops *ops, struct ftrace_hash *hash); void ftrace_stub_direct_tramp(void); @@ -559,6 +560,11 @@ int register_ftrace_direct_hash(struct ftrace_ops *ops, struct ftrace_hash *hash return -ENODEV; } +int unregister_ftrace_direct_hash(struct ftrace_ops *ops, struct ftrace_hash *hash) +{ + return -ENODEV; +} + /* * This must be implemented by the architecture. * It is the way the ftrace direct_ops helper, when called diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 1dbb113f4e9c2..d761237ec70f1 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -6342,6 +6342,104 @@ int register_ftrace_direct_hash(struct ftrace_ops *ops, struct ftrace_hash *hash } EXPORT_SYMBOL_GPL(register_ftrace_direct_hash); +/** + * hash_sub - substracts @b from @a and returns the result + * @a: struct ftrace_hash object + * @b: struct ftrace_hash object + * + * Returns struct ftrace_hash object on success, NULL on error. + */ +static struct ftrace_hash *hash_sub(struct ftrace_hash *a, struct ftrace_hash *b) +{ + struct ftrace_func_entry *entry, *del; + struct ftrace_hash *sub; + int size, i; + + sub = alloc_and_copy_ftrace_hash(a->size_bits, a); + if (!sub) + goto error; + + size = 1 << b->size_bits; + for (i = 0; i < size; i++) { + hlist_for_each_entry(entry, &b->buckets[i], hlist) { + del = __ftrace_lookup_ip(sub, entry->ip); + if (WARN_ON_ONCE(!del)) + goto error; + remove_hash_entry(sub, del); + kfree(del); + } + } + return sub; + + error: + free_ftrace_hash(sub); + return NULL; +} + +int unregister_ftrace_direct_hash(struct ftrace_ops *ops, struct ftrace_hash *hash) +{ + struct ftrace_hash *new_hash = NULL, *filter_hash = NULL, *free_hash = NULL; + struct ftrace_func_entry *del, *entry; + unsigned long size, i; + int err = -EINVAL; + + if (!hash_count(hash)) + return 0; + if (check_direct_multi(ops)) + return -EINVAL; + if (!(ops->flags & FTRACE_OPS_FL_ENABLED)) + return -EINVAL; + if (direct_functions == EMPTY_HASH) + return -EINVAL; + + mutex_lock(&direct_mutex); + + /* Make sure requested entries are already registered. */ + size = 1 << hash->size_bits; + for (i = 0; i < size; i++) { + hlist_for_each_entry(entry, &hash->buckets[i], hlist) { + del = __ftrace_lookup_ip(direct_functions, entry->ip); + if (!del || del->direct != entry->direct) + goto out_unlock; + } + } + + filter_hash = hash_sub(ops->func_hash->filter_hash, hash); + if (!filter_hash) + goto out_unlock; + + new_hash = hash_sub(direct_functions, hash); + if (!new_hash) + goto out_unlock; + + /* If there's nothing left, we need to unregister the ops. */ + if (ftrace_hash_empty(filter_hash)) { + err = unregister_ftrace_function(ops); + /* cleanup for possible another register call */ + ops->func = NULL; + ops->trampoline = 0; + ftrace_free_filter(ops); + ops->func_hash->filter_hash = NULL; + } else { + err = ftrace_update_ops(ops, filter_hash, EMPTY_HASH); + } + + free_hash = direct_functions; + rcu_assign_pointer(direct_functions, new_hash); + + out_unlock: + mutex_unlock(&direct_mutex); + + if (free_hash && free_hash != EMPTY_HASH) + call_rcu_tasks(&free_hash->rcu, register_ftrace_direct_cb); + + if (filter_hash) + free_ftrace_hash(filter_hash); + + return err; +} +EXPORT_SYMBOL_GPL(unregister_ftrace_direct_hash); + #endif /* CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS */ /** From dbe0b552a4500ea13837d22bff98ed68e968793c Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 29 Jul 2025 12:28:07 +0200 Subject: [PATCH 04/10] ftrace: Add modify_ftrace_direct_hash function Adding modify_ftrace_direct_hash function that modifies all entries (ip -> direct) provided in hash argument. The difference to current unregister_ftrace_direct is - hash argument that allows to modify multiple ip -> direct entries at once This change will allow us to have simple ftrace_ops for all bpf direct interface users in following changes. Signed-off-by: Jiri Olsa --- include/linux/ftrace.h | 6 +++++ kernel/trace/ftrace.c | 58 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index 7ff6004498c05..8761765d9abcf 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -528,6 +528,7 @@ int modify_ftrace_direct_nolock(struct ftrace_ops *ops, unsigned long addr); int register_ftrace_direct_hash(struct ftrace_ops *ops, struct ftrace_hash *hash); int unregister_ftrace_direct_hash(struct ftrace_ops *ops, struct ftrace_hash *hash); +int modify_ftrace_direct_hash(struct ftrace_ops *ops, struct ftrace_hash *hash, bool do_direct_lock); void ftrace_stub_direct_tramp(void); @@ -565,6 +566,11 @@ int unregister_ftrace_direct_hash(struct ftrace_ops *ops, struct ftrace_hash *ha return -ENODEV; } +int modify_ftrace_direct_hash(struct ftrace_ops *ops, struct ftrace_hash *hash, bool do_direct_lock) +{ + return -ENODEV; +} + /* * This must be implemented by the architecture. * It is the way the ftrace direct_ops helper, when called diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index d761237ec70f1..755d5550ac449 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -6440,6 +6440,64 @@ int unregister_ftrace_direct_hash(struct ftrace_ops *ops, struct ftrace_hash *ha } EXPORT_SYMBOL_GPL(unregister_ftrace_direct_hash); +int modify_ftrace_direct_hash(struct ftrace_ops *ops, struct ftrace_hash *hash, bool do_direct_lock) +{ + struct ftrace_func_entry *entry, *tmp; + static struct ftrace_ops tmp_ops = { + .func = ftrace_stub, + .flags = FTRACE_OPS_FL_STUB, + }; + unsigned long size, i; + int err; + + if (!hash_count(hash)) + return 0; + if (check_direct_multi(ops)) + return -EINVAL; + if (!(ops->flags & FTRACE_OPS_FL_ENABLED)) + return -EINVAL; + if (direct_functions == EMPTY_HASH) + return -EINVAL; + + if (do_direct_lock) + mutex_lock(&direct_mutex); + + /* Enable the tmp_ops to have the same functions as the direct ops */ + ftrace_ops_init(&tmp_ops); + tmp_ops.func_hash = ops->func_hash; + + err = register_ftrace_function_nolock(&tmp_ops); + if (err) + goto unlock; + + /* + * Now the ftrace_ops_list_func() is called to do the direct callers. + * We can safely change the direct functions attached to each entry. + */ + mutex_lock(&ftrace_lock); + + size = 1 << hash->size_bits; + for (i = 0; i < size; i++) { + hlist_for_each_entry(entry, &hash->buckets[i], hlist) { + tmp = __ftrace_lookup_ip(direct_functions, entry->ip); + if (!tmp) + continue; + tmp->direct = entry->direct; + } + } + + mutex_unlock(&ftrace_lock); + + /* Removing the tmp_ops will add the updated direct callers to the functions */ + unregister_ftrace_function(&tmp_ops); + +unlock: + if (do_direct_lock) + mutex_unlock(&direct_mutex); + return err; +} +EXPORT_SYMBOL_GPL(modify_ftrace_direct_hash); + #endif /* CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS */ /** From 451d89e903b4040494c3807505115ccb741695ea Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 29 Jul 2025 12:28:08 +0200 Subject: [PATCH 05/10] ftrace: Export some of hash related functions We are going to use these functions in following changes. Signed-off-by: Jiri Olsa --- include/linux/ftrace.h | 16 ++++++++++++++++ kernel/trace/ftrace.c | 7 +++---- kernel/trace/trace.h | 8 -------- 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index 8761765d9abcf..9a6fcdafeda28 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -397,6 +397,22 @@ enum ftrace_ops_cmd { typedef int (*ftrace_ops_func_t)(struct ftrace_ops *op, enum ftrace_ops_cmd cmd); #ifdef CONFIG_DYNAMIC_FTRACE + +#define FTRACE_HASH_DEFAULT_BITS 10 + +struct ftrace_hash { + unsigned long size_bits; + struct hlist_head *buckets; + unsigned long count; + unsigned long flags; + struct rcu_head rcu; +}; + +struct ftrace_hash *alloc_ftrace_hash(int size_bits); +void free_ftrace_hash(struct ftrace_hash *hash); +struct ftrace_func_entry *add_hash_entry_direct(struct ftrace_hash *hash, + unsigned long ip, unsigned long direct); + /* The hash used to know what functions callbacks trace */ struct ftrace_ops_hash { struct ftrace_hash __rcu *notrace_hash; diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 755d5550ac449..fcb8f2d3172be 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -68,7 +68,6 @@ }) /* hash bits for specific function selection */ -#define FTRACE_HASH_DEFAULT_BITS 10 #define FTRACE_HASH_MAX_BITS 12 #ifdef CONFIG_DYNAMIC_FTRACE @@ -1189,7 +1188,7 @@ static void __add_hash_entry(struct ftrace_hash *hash, hash->count++; } -static struct ftrace_func_entry * +struct ftrace_func_entry * add_hash_entry_direct(struct ftrace_hash *hash, unsigned long ip, unsigned long direct) { struct ftrace_func_entry *entry; @@ -1269,7 +1268,7 @@ static void clear_ftrace_mod_list(struct list_head *head) mutex_unlock(&ftrace_lock); } -static void free_ftrace_hash(struct ftrace_hash *hash) +void free_ftrace_hash(struct ftrace_hash *hash) { if (!hash || hash == EMPTY_HASH) return; @@ -1309,7 +1308,7 @@ void ftrace_free_filter(struct ftrace_ops *ops) } EXPORT_SYMBOL_GPL(ftrace_free_filter); -static struct ftrace_hash *alloc_ftrace_hash(int size_bits) +struct ftrace_hash *alloc_ftrace_hash(int size_bits) { struct ftrace_hash *hash; int size; diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index bd084953a98be..74ef7755f361a 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h @@ -899,14 +899,6 @@ enum { FTRACE_HASH_FL_MOD = (1 << 0), }; -struct ftrace_hash { - unsigned long size_bits; - struct hlist_head *buckets; - unsigned long count; - unsigned long flags; - struct rcu_head rcu; -}; - struct ftrace_func_entry * ftrace_lookup_ip(struct ftrace_hash *hash, unsigned long ip); From d82e682d37bf16025ed65d83a09bf3114c4abcca Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 29 Jul 2025 12:28:09 +0200 Subject: [PATCH 06/10] ftrace: Use direct hash interface in direct functions Implement current *_ftrace_direct function with their *_hash function counterparts. Signed-off-by: Jiri Olsa --- include/linux/ftrace.h | 17 +-- kernel/bpf/trampoline.c | 10 +- kernel/trace/ftrace.c | 242 +++++----------------------------- kernel/trace/trace_selftest.c | 5 +- 4 files changed, 45 insertions(+), 229 deletions(-) diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index 9a6fcdafeda28..85f4ab1a1e72f 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -536,11 +536,10 @@ struct ftrace_func_entry { #ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS unsigned long ftrace_find_rec_direct(unsigned long ip); -int register_ftrace_direct(struct ftrace_ops *ops, unsigned long addr); -int unregister_ftrace_direct(struct ftrace_ops *ops, unsigned long addr, +int register_ftrace_direct(struct ftrace_ops *ops, unsigned long ip, unsigned long addr); +int unregister_ftrace_direct(struct ftrace_ops *ops, unsigned long ip, unsigned long addr, bool free_filters); -int modify_ftrace_direct(struct ftrace_ops *ops, unsigned long addr); -int modify_ftrace_direct_nolock(struct ftrace_ops *ops, unsigned long addr); +int modify_ftrace_direct(struct ftrace_ops *ops, unsigned long ip, unsigned long addr, bool lock_direct_mutex); int register_ftrace_direct_hash(struct ftrace_ops *ops, struct ftrace_hash *hash); int unregister_ftrace_direct_hash(struct ftrace_ops *ops, struct ftrace_hash *hash); @@ -554,20 +553,16 @@ static inline unsigned long ftrace_find_rec_direct(unsigned long ip) { return 0; } -static inline int register_ftrace_direct(struct ftrace_ops *ops, unsigned long addr) +static inline int register_ftrace_direct(struct ftrace_ops *ops, unsigned long ip, unsigned long addr) { return -ENODEV; } -static inline int unregister_ftrace_direct(struct ftrace_ops *ops, unsigned long addr, +static inline int unregister_ftrace_direct(struct ftrace_ops *ops, unsigned long ip, unsigned long addr, bool free_filters) { return -ENODEV; } -static inline int modify_ftrace_direct(struct ftrace_ops *ops, unsigned long addr) -{ - return -ENODEV; -} -static inline int modify_ftrace_direct_nolock(struct ftrace_ops *ops, unsigned long addr) +static inline int modify_ftrace_direct(struct ftrace_ops *ops, unsigned long ip, unsigned long addr, bool lock_direct_mutex) { return -ENODEV; } diff --git a/kernel/bpf/trampoline.c b/kernel/bpf/trampoline.c index 0e364614c3a29..6bf272715f0e3 100644 --- a/kernel/bpf/trampoline.c +++ b/kernel/bpf/trampoline.c @@ -181,7 +181,7 @@ static int unregister_fentry(struct bpf_trampoline *tr, void *old_addr) int ret; if (tr->func.ftrace_managed) - ret = unregister_ftrace_direct(tr->fops, (long)old_addr, false); + ret = unregister_ftrace_direct(tr->fops, (unsigned long) ip, (long)old_addr, false); else ret = bpf_arch_text_poke(ip, BPF_MOD_CALL, old_addr, NULL); @@ -195,10 +195,7 @@ static int modify_fentry(struct bpf_trampoline *tr, void *old_addr, void *new_ad int ret; if (tr->func.ftrace_managed) { - if (lock_direct_mutex) - ret = modify_ftrace_direct(tr->fops, (long)new_addr); - else - ret = modify_ftrace_direct_nolock(tr->fops, (long)new_addr); + ret = modify_ftrace_direct(tr->fops, (unsigned long) ip, (long)new_addr, lock_direct_mutex); } else { ret = bpf_arch_text_poke(ip, BPF_MOD_CALL, old_addr, new_addr); } @@ -220,8 +217,7 @@ static int register_fentry(struct bpf_trampoline *tr, void *new_addr) } if (tr->func.ftrace_managed) { - ftrace_set_filter_ip(tr->fops, (unsigned long)ip, 0, 1); - ret = register_ftrace_direct(tr->fops, (long)new_addr); + ret = register_ftrace_direct(tr->fops, (unsigned long)ip, (long)new_addr); } else { ret = bpf_arch_text_poke(ip, BPF_MOD_CALL, NULL, new_addr); } diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index fcb8f2d3172be..151ca94f496ab 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -2593,16 +2593,6 @@ unsigned long ftrace_find_rec_direct(unsigned long ip) return entry->direct; } -static void call_direct_funcs(unsigned long ip, unsigned long pip, - struct ftrace_ops *ops, struct ftrace_regs *fregs) -{ - unsigned long addr = READ_ONCE(ops->direct_call); - - if (!addr) - return; - - arch_ftrace_set_direct_caller(fregs, addr); -} #endif /* CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS */ /** @@ -5935,28 +5925,24 @@ static int check_direct_multi(struct ftrace_ops *ops) return 0; } -static void remove_direct_functions_hash(struct ftrace_hash *hash, unsigned long addr) +static void register_ftrace_direct_cb(struct rcu_head *rhp) { - struct ftrace_func_entry *entry, *del; - int size, i; + struct ftrace_hash *fhp = container_of(rhp, struct ftrace_hash, rcu); - size = 1 << hash->size_bits; - for (i = 0; i < size; i++) { - hlist_for_each_entry(entry, &hash->buckets[i], hlist) { - del = __ftrace_lookup_ip(direct_functions, entry->ip); - if (del && del->direct == addr) { - remove_hash_entry(direct_functions, del); - kfree(del); - } - } - } + free_ftrace_hash(fhp); } -static void register_ftrace_direct_cb(struct rcu_head *rhp) +static struct ftrace_hash *hash_from_ip(unsigned long ip, unsigned long addr) { - struct ftrace_hash *fhp = container_of(rhp, struct ftrace_hash, rcu); + struct ftrace_hash *hash; - free_ftrace_hash(fhp); + ip = ftrace_location(ip); + if (!ip) + return NULL; + hash = alloc_ftrace_hash(FTRACE_HASH_DEFAULT_BITS); + if (!hash || !add_hash_entry_direct(hash, ip, addr)) + return NULL; + return hash; } /** @@ -5981,89 +5967,17 @@ static void register_ftrace_direct_cb(struct rcu_head *rhp) * -ENODEV - @ip does not point to a ftrace nop location (or not supported) * -ENOMEM - There was an allocation failure. */ -int register_ftrace_direct(struct ftrace_ops *ops, unsigned long addr) +int register_ftrace_direct(struct ftrace_ops *ops, unsigned long ip, unsigned long addr) { - struct ftrace_hash *hash, *new_hash = NULL, *free_hash = NULL; - struct ftrace_func_entry *entry, *new; - int err = -EBUSY, size, i; - - if (ops->func || ops->trampoline) - return -EINVAL; - if (!(ops->flags & FTRACE_OPS_FL_INITIALIZED)) - return -EINVAL; - if (ops->flags & FTRACE_OPS_FL_ENABLED) - return -EINVAL; - - hash = ops->func_hash->filter_hash; - if (ftrace_hash_empty(hash)) - return -EINVAL; - - mutex_lock(&direct_mutex); - - /* Make sure requested entries are not already registered.. */ - size = 1 << hash->size_bits; - for (i = 0; i < size; i++) { - hlist_for_each_entry(entry, &hash->buckets[i], hlist) { - if (ftrace_find_rec_direct(entry->ip)) - goto out_unlock; - } - } - - err = -ENOMEM; - - /* Make a copy hash to place the new and the old entries in */ - size = hash->count + direct_functions->count; - size = fls(size); - if (size > FTRACE_HASH_MAX_BITS) - size = FTRACE_HASH_MAX_BITS; - new_hash = alloc_ftrace_hash(size); - if (!new_hash) - goto out_unlock; - - /* Now copy over the existing direct entries */ - size = 1 << direct_functions->size_bits; - for (i = 0; i < size; i++) { - hlist_for_each_entry(entry, &direct_functions->buckets[i], hlist) { - new = add_hash_entry(new_hash, entry->ip); - if (!new) - goto out_unlock; - new->direct = entry->direct; - } - } - - /* ... and add the new entries */ - size = 1 << hash->size_bits; - for (i = 0; i < size; i++) { - hlist_for_each_entry(entry, &hash->buckets[i], hlist) { - new = add_hash_entry(new_hash, entry->ip); - if (!new) - goto out_unlock; - /* Update both the copy and the hash entry */ - new->direct = addr; - entry->direct = addr; - } - } - - free_hash = direct_functions; - rcu_assign_pointer(direct_functions, new_hash); - new_hash = NULL; - - ops->func = call_direct_funcs; - ops->flags = MULTI_FLAGS; - ops->trampoline = FTRACE_REGS_ADDR; - ops->direct_call = addr; - - err = register_ftrace_function_nolock(ops); - - out_unlock: - mutex_unlock(&direct_mutex); - - if (free_hash && free_hash != EMPTY_HASH) - call_rcu_tasks(&free_hash->rcu, register_ftrace_direct_cb); + struct ftrace_hash *hash; + int err; - if (new_hash) - free_ftrace_hash(new_hash); + hash = hash_from_ip(ip, addr); + if (!hash) + return -ENOMEM; + err = register_ftrace_direct_hash(ops, hash); + free_ftrace_hash(hash); return err; } EXPORT_SYMBOL_GPL(register_ftrace_direct); @@ -6083,111 +5997,24 @@ EXPORT_SYMBOL_GPL(register_ftrace_direct); * 0 on success * -EINVAL - The @ops object was not properly registered. */ -int unregister_ftrace_direct(struct ftrace_ops *ops, unsigned long addr, +int unregister_ftrace_direct(struct ftrace_ops *ops, unsigned long ip, unsigned long addr, bool free_filters) { - struct ftrace_hash *hash = ops->func_hash->filter_hash; + struct ftrace_hash *hash; int err; - if (check_direct_multi(ops)) - return -EINVAL; - if (!(ops->flags & FTRACE_OPS_FL_ENABLED)) - return -EINVAL; - - mutex_lock(&direct_mutex); - err = unregister_ftrace_function(ops); - remove_direct_functions_hash(hash, addr); - mutex_unlock(&direct_mutex); - - /* cleanup for possible another register call */ - ops->func = NULL; - ops->trampoline = 0; + hash = hash_from_ip(ip, addr); + if (!hash) + return -ENOMEM; + err = unregister_ftrace_direct_hash(ops, hash); + free_ftrace_hash(hash); if (free_filters) ftrace_free_filter(ops); return err; } EXPORT_SYMBOL_GPL(unregister_ftrace_direct); -static int -__modify_ftrace_direct(struct ftrace_ops *ops, unsigned long addr) -{ - struct ftrace_hash *hash; - struct ftrace_func_entry *entry, *iter; - static struct ftrace_ops tmp_ops = { - .func = ftrace_stub, - .flags = FTRACE_OPS_FL_STUB, - }; - int i, size; - int err; - - lockdep_assert_held_once(&direct_mutex); - - /* Enable the tmp_ops to have the same functions as the direct ops */ - ftrace_ops_init(&tmp_ops); - tmp_ops.func_hash = ops->func_hash; - tmp_ops.direct_call = addr; - - err = register_ftrace_function_nolock(&tmp_ops); - if (err) - return err; - - /* - * Now the ftrace_ops_list_func() is called to do the direct callers. - * We can safely change the direct functions attached to each entry. - */ - mutex_lock(&ftrace_lock); - - hash = ops->func_hash->filter_hash; - size = 1 << hash->size_bits; - for (i = 0; i < size; i++) { - hlist_for_each_entry(iter, &hash->buckets[i], hlist) { - entry = __ftrace_lookup_ip(direct_functions, iter->ip); - if (!entry) - continue; - entry->direct = addr; - } - } - /* Prevent store tearing if a trampoline concurrently accesses the value */ - WRITE_ONCE(ops->direct_call, addr); - - mutex_unlock(&ftrace_lock); - - /* Removing the tmp_ops will add the updated direct callers to the functions */ - unregister_ftrace_function(&tmp_ops); - - return err; -} - -/** - * modify_ftrace_direct_nolock - Modify an existing direct 'multi' call - * to call something else - * @ops: The address of the struct ftrace_ops object - * @addr: The address of the new trampoline to call at @ops functions - * - * This is used to unregister currently registered direct caller and - * register new one @addr on functions registered in @ops object. - * - * Note there's window between ftrace_shutdown and ftrace_startup calls - * where there will be no callbacks called. - * - * Caller should already have direct_mutex locked, so we don't lock - * direct_mutex here. - * - * Returns: zero on success. Non zero on error, which includes: - * -EINVAL - The @ops object was not properly registered. - */ -int modify_ftrace_direct_nolock(struct ftrace_ops *ops, unsigned long addr) -{ - if (check_direct_multi(ops)) - return -EINVAL; - if (!(ops->flags & FTRACE_OPS_FL_ENABLED)) - return -EINVAL; - - return __modify_ftrace_direct(ops, addr); -} -EXPORT_SYMBOL_GPL(modify_ftrace_direct_nolock); - /** * modify_ftrace_direct - Modify an existing direct 'multi' call * to call something else @@ -6203,18 +6030,17 @@ EXPORT_SYMBOL_GPL(modify_ftrace_direct_nolock); * Returns: zero on success. Non zero on error, which includes: * -EINVAL - The @ops object was not properly registered. */ -int modify_ftrace_direct(struct ftrace_ops *ops, unsigned long addr) +int modify_ftrace_direct(struct ftrace_ops *ops, unsigned long ip, unsigned long addr, bool lock_direct_mutex) { + struct ftrace_hash *hash; int err; - if (check_direct_multi(ops)) - return -EINVAL; - if (!(ops->flags & FTRACE_OPS_FL_ENABLED)) - return -EINVAL; + hash = hash_from_ip(ip, addr); + if (!hash) + return -ENOMEM; - mutex_lock(&direct_mutex); - err = __modify_ftrace_direct(ops, addr); - mutex_unlock(&direct_mutex); + err = modify_ftrace_direct_hash(ops, hash, lock_direct_mutex); + free_ftrace_hash(hash); return err; } EXPORT_SYMBOL_GPL(modify_ftrace_direct); diff --git a/kernel/trace/trace_selftest.c b/kernel/trace/trace_selftest.c index d88c44f1dfa55..37f5eb1f252b9 100644 --- a/kernel/trace/trace_selftest.c +++ b/kernel/trace/trace_selftest.c @@ -1135,8 +1135,7 @@ trace_selftest_startup_function_graph(struct tracer *trace, * Register direct function together with graph tracer * and make sure we get graph trace. */ - ftrace_set_filter_ip(&direct, (unsigned long)DYN_FTRACE_TEST_NAME, 0, 0); - ret = register_ftrace_direct(&direct, + ret = register_ftrace_direct(&direct, (unsigned long)DYN_FTRACE_TEST_NAME, (unsigned long)ftrace_stub_direct_tramp); if (ret) goto out; @@ -1159,7 +1158,7 @@ trace_selftest_startup_function_graph(struct tracer *trace, unregister_ftrace_graph(&fgraph_ops); - ret = unregister_ftrace_direct(&direct, + ret = unregister_ftrace_direct(&direct, (unsigned long)DYN_FTRACE_TEST_NAME, (unsigned long)ftrace_stub_direct_tramp, true); if (ret) From d22f2e4fe02f4a3e8edeace951701c627bc13bf6 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 29 Jul 2025 12:28:10 +0200 Subject: [PATCH 07/10] bpf: Add trampoline ip hash table Following changes need to lookup trampoline based on its ip address, adding hash table for that. Signed-off-by: Jiri Olsa --- include/linux/bpf.h | 7 +++++-- kernel/bpf/trampoline.c | 30 +++++++++++++++++++----------- 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/include/linux/bpf.h b/include/linux/bpf.h index f9cd2164ed238..c14bde400d97e 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -1253,14 +1253,17 @@ struct bpf_tramp_image { }; struct bpf_trampoline { - /* hlist for trampoline_table */ - struct hlist_node hlist; + /* hlist for trampoline_key_table */ + struct hlist_node hlist_key; + /* hlist for trampoline_ip_table */ + struct hlist_node hlist_ip; struct ftrace_ops *fops; /* serializes access to fields of this trampoline */ struct mutex mutex; refcount_t refcnt; u32 flags; u64 key; + unsigned long ip; struct { struct btf_func_model model; void *addr; diff --git a/kernel/bpf/trampoline.c b/kernel/bpf/trampoline.c index 6bf272715f0e3..84bcd9f6bd746 100644 --- a/kernel/bpf/trampoline.c +++ b/kernel/bpf/trampoline.c @@ -24,9 +24,10 @@ const struct bpf_prog_ops bpf_extension_prog_ops = { #define TRAMPOLINE_HASH_BITS 10 #define TRAMPOLINE_TABLE_SIZE (1 << TRAMPOLINE_HASH_BITS) -static struct hlist_head trampoline_table[TRAMPOLINE_TABLE_SIZE]; +static struct hlist_head trampoline_key_table[TRAMPOLINE_TABLE_SIZE]; +static struct hlist_head trampoline_ip_table[TRAMPOLINE_TABLE_SIZE]; -/* serializes access to trampoline_table */ +/* serializes access to trampoline tables */ static DEFINE_MUTEX(trampoline_mutex); #ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS @@ -135,15 +136,15 @@ void bpf_image_ksym_del(struct bpf_ksym *ksym) PAGE_SIZE, true, ksym->name); } -static struct bpf_trampoline *bpf_trampoline_lookup(u64 key) +static struct bpf_trampoline *bpf_trampoline_lookup(u64 key, unsigned long ip) { struct bpf_trampoline *tr; struct hlist_head *head; int i; mutex_lock(&trampoline_mutex); - head = &trampoline_table[hash_64(key, TRAMPOLINE_HASH_BITS)]; - hlist_for_each_entry(tr, head, hlist) { + head = &trampoline_key_table[hash_64(key, TRAMPOLINE_HASH_BITS)]; + hlist_for_each_entry(tr, head, hlist_key) { if (tr->key == key) { refcount_inc(&tr->refcnt); goto out; @@ -164,8 +165,12 @@ static struct bpf_trampoline *bpf_trampoline_lookup(u64 key) #endif tr->key = key; - INIT_HLIST_NODE(&tr->hlist); - hlist_add_head(&tr->hlist, head); + tr->ip = ip; + INIT_HLIST_NODE(&tr->hlist_key); + INIT_HLIST_NODE(&tr->hlist_ip); + hlist_add_head(&tr->hlist_key, head); + head = &trampoline_ip_table[hash_64(ip, TRAMPOLINE_HASH_BITS)]; + hlist_add_head(&tr->hlist_ip, head); refcount_set(&tr->refcnt, 1); mutex_init(&tr->mutex); for (i = 0; i < BPF_TRAMP_MAX; i++) @@ -800,7 +805,7 @@ void bpf_trampoline_unlink_cgroup_shim(struct bpf_prog *prog) prog->aux->attach_btf_id); bpf_lsm_find_cgroup_shim(prog, &bpf_func); - tr = bpf_trampoline_lookup(key); + tr = bpf_trampoline_lookup(key, 0); if (WARN_ON_ONCE(!tr)) return; @@ -820,7 +825,7 @@ struct bpf_trampoline *bpf_trampoline_get(u64 key, { struct bpf_trampoline *tr; - tr = bpf_trampoline_lookup(key); + tr = bpf_trampoline_lookup(key, tgt_info->tgt_addr); if (!tr) return NULL; @@ -856,7 +861,8 @@ void bpf_trampoline_put(struct bpf_trampoline *tr) * fexit progs. The fentry-only trampoline will be freed via * multiple rcu callbacks. */ - hlist_del(&tr->hlist); + hlist_del(&tr->hlist_key); + hlist_del(&tr->hlist_ip); if (tr->fops) { ftrace_free_filter(tr->fops); kfree(tr->fops); @@ -1135,7 +1141,9 @@ static int __init init_trampolines(void) int i; for (i = 0; i < TRAMPOLINE_TABLE_SIZE; i++) - INIT_HLIST_HEAD(&trampoline_table[i]); + INIT_HLIST_HEAD(&trampoline_key_table[i]); + for (i = 0; i < TRAMPOLINE_TABLE_SIZE; i++) + INIT_HLIST_HEAD(&trampoline_ip_table[i]); return 0; } late_initcall(init_trampolines); From a8592cc96c6f7cafe38240e7611e267fbc937281 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 29 Jul 2025 12:28:11 +0200 Subject: [PATCH 08/10] ftrace: Factor ftrace_ops ops_func interface We are going to remove "ftrace_ops->private == bpf_trampoline" setup in following changes. Adding ip argument to ftrace_ops_func_t callback function, so we can use it to look up the trampoline. Signed-off-by: Jiri Olsa --- include/linux/ftrace.h | 2 +- kernel/bpf/trampoline.c | 26 ++++++++++++++++++++++++-- kernel/trace/ftrace.c | 6 +++--- 3 files changed, 28 insertions(+), 6 deletions(-) diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index 85f4ab1a1e72f..1a61f969550de 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -394,7 +394,7 @@ enum ftrace_ops_cmd { * Negative on failure. The return value is dependent on the * callback. */ -typedef int (*ftrace_ops_func_t)(struct ftrace_ops *op, enum ftrace_ops_cmd cmd); +typedef int (*ftrace_ops_func_t)(struct ftrace_ops *op, unsigned long ip, enum ftrace_ops_cmd cmd); #ifdef CONFIG_DYNAMIC_FTRACE diff --git a/kernel/bpf/trampoline.c b/kernel/bpf/trampoline.c index 84bcd9f6bd746..398c1a722d833 100644 --- a/kernel/bpf/trampoline.c +++ b/kernel/bpf/trampoline.c @@ -33,11 +33,33 @@ static DEFINE_MUTEX(trampoline_mutex); #ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS static int bpf_trampoline_update(struct bpf_trampoline *tr, bool lock_direct_mutex); -static int bpf_tramp_ftrace_ops_func(struct ftrace_ops *ops, enum ftrace_ops_cmd cmd) +static struct bpf_trampoline *bpf_trampoline_ip_lookup(unsigned long ip) { - struct bpf_trampoline *tr = ops->private; + struct hlist_head *head_ip; + struct bpf_trampoline *tr; + + mutex_lock(&trampoline_mutex); + head_ip = &trampoline_ip_table[hash_64(ip, TRAMPOLINE_HASH_BITS)]; + hlist_for_each_entry(tr, head_ip, hlist_ip) { + if (tr->func.addr == (void *) ip) + goto out; + } + tr = NULL; +out: + mutex_unlock(&trampoline_mutex); + return tr; +} + +static int bpf_tramp_ftrace_ops_func(struct ftrace_ops *ops, unsigned long ip, + enum ftrace_ops_cmd cmd) +{ + struct bpf_trampoline *tr; int ret = 0; + tr = bpf_trampoline_ip_lookup(ip); + if (!tr) + return -EINVAL; + if (cmd == FTRACE_OPS_CMD_ENABLE_SHARE_IPMODIFY_SELF) { /* This is called inside register_ftrace_direct_multi(), so * tr->mutex is already locked. diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 151ca94f496ab..943feabdd5e63 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -2040,7 +2040,7 @@ static int __ftrace_hash_update_ipmodify(struct ftrace_ops *ops, */ if (!ops->ops_func) return -EBUSY; - ret = ops->ops_func(ops, FTRACE_OPS_CMD_ENABLE_SHARE_IPMODIFY_SELF); + ret = ops->ops_func(ops, rec->ip, FTRACE_OPS_CMD_ENABLE_SHARE_IPMODIFY_SELF); if (ret) return ret; } else if (is_ipmodify) { @@ -8746,7 +8746,7 @@ static int prepare_direct_functions_for_ipmodify(struct ftrace_ops *ops) if (!op->ops_func) return -EBUSY; - ret = op->ops_func(op, FTRACE_OPS_CMD_ENABLE_SHARE_IPMODIFY_PEER); + ret = op->ops_func(op, ip, FTRACE_OPS_CMD_ENABLE_SHARE_IPMODIFY_PEER); if (ret) return ret; } @@ -8793,7 +8793,7 @@ static void cleanup_direct_functions_after_ipmodify(struct ftrace_ops *ops) /* The cleanup is optional, ignore any errors */ if (found_op && op->ops_func) - op->ops_func(op, FTRACE_OPS_CMD_DISABLE_SHARE_IPMODIFY_PEER); + op->ops_func(op, ip, FTRACE_OPS_CMD_DISABLE_SHARE_IPMODIFY_PEER); } } mutex_unlock(&direct_mutex); From e306b6966eb265eb63be810a4811686c39185eab Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 29 Jul 2025 12:28:12 +0200 Subject: [PATCH 09/10] bpf: Remove ftrace_ops from bpf_trampoline object We no longer need ftrace_ops in each bpf_trampoline object, we can manage with just single ftrace_ops for all direct trampoline attachments. Signed-off-by: Jiri Olsa --- include/linux/bpf.h | 1 - kernel/bpf/trampoline.c | 34 ++++++++++------------------------ 2 files changed, 10 insertions(+), 25 deletions(-) diff --git a/include/linux/bpf.h b/include/linux/bpf.h index c14bde400d97e..bad29fe38a128 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -1257,7 +1257,6 @@ struct bpf_trampoline { struct hlist_node hlist_key; /* hlist for trampoline_ip_table */ struct hlist_node hlist_ip; - struct ftrace_ops *fops; /* serializes access to fields of this trampoline */ struct mutex mutex; refcount_t refcnt; diff --git a/kernel/bpf/trampoline.c b/kernel/bpf/trampoline.c index 398c1a722d833..e6a0e7b20bb62 100644 --- a/kernel/bpf/trampoline.c +++ b/kernel/bpf/trampoline.c @@ -175,16 +175,6 @@ static struct bpf_trampoline *bpf_trampoline_lookup(u64 key, unsigned long ip) tr = kzalloc(sizeof(*tr), GFP_KERNEL); if (!tr) goto out; -#ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS - tr->fops = kzalloc(sizeof(struct ftrace_ops), GFP_KERNEL); - if (!tr->fops) { - kfree(tr); - tr = NULL; - goto out; - } - tr->fops->private = tr; - tr->fops->ops_func = bpf_tramp_ftrace_ops_func; -#endif tr->key = key; tr->ip = ip; @@ -202,13 +192,19 @@ static struct bpf_trampoline *bpf_trampoline_lookup(u64 key, unsigned long ip) return tr; } +struct ftrace_ops direct_ops = { +#ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS + .ops_func = bpf_tramp_ftrace_ops_func, +#endif +}; + static int unregister_fentry(struct bpf_trampoline *tr, void *old_addr) { void *ip = tr->func.addr; int ret; if (tr->func.ftrace_managed) - ret = unregister_ftrace_direct(tr->fops, (unsigned long) ip, (long)old_addr, false); + ret = unregister_ftrace_direct(&direct_ops, (unsigned long) ip, (long)old_addr, false); else ret = bpf_arch_text_poke(ip, BPF_MOD_CALL, old_addr, NULL); @@ -222,7 +218,7 @@ static int modify_fentry(struct bpf_trampoline *tr, void *old_addr, void *new_ad int ret; if (tr->func.ftrace_managed) { - ret = modify_ftrace_direct(tr->fops, (unsigned long) ip, (long)new_addr, lock_direct_mutex); + ret = modify_ftrace_direct(&direct_ops, (unsigned long) ip, (long)new_addr, lock_direct_mutex); } else { ret = bpf_arch_text_poke(ip, BPF_MOD_CALL, old_addr, new_addr); } @@ -237,14 +233,11 @@ static int register_fentry(struct bpf_trampoline *tr, void *new_addr) int ret; faddr = ftrace_location((unsigned long)ip); - if (faddr) { - if (!tr->fops) - return -ENOTSUPP; + if (faddr) tr->func.ftrace_managed = true; - } if (tr->func.ftrace_managed) { - ret = register_ftrace_direct(tr->fops, (unsigned long)ip, (long)new_addr); + ret = register_ftrace_direct(&direct_ops, (unsigned long)ip, (long)new_addr); } else { ret = bpf_arch_text_poke(ip, BPF_MOD_CALL, NULL, new_addr); } @@ -502,9 +495,6 @@ static int bpf_trampoline_update(struct bpf_trampoline *tr, bool lock_direct_mut * BPF_TRAMP_F_SHARE_IPMODIFY is set, we can generate the * trampoline again, and retry register. */ - /* reset fops->func and fops->trampoline for re-register */ - tr->fops->func = NULL; - tr->fops->trampoline = 0; /* free im memory and reallocate later */ bpf_tramp_image_free(im); @@ -885,10 +875,6 @@ void bpf_trampoline_put(struct bpf_trampoline *tr) */ hlist_del(&tr->hlist_key); hlist_del(&tr->hlist_ip); - if (tr->fops) { - ftrace_free_filter(tr->fops); - kfree(tr->fops); - } kfree(tr); out: mutex_unlock(&trampoline_mutex); From 632ce939603a5a343760c0ff34d45570b1d8e6e7 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 29 Jul 2025 12:28:13 +0200 Subject: [PATCH 10/10] Revert "ftrace: Store direct called addresses in their ops" This reverts commit dbaccb618fabde8b8596e341f8d76da63a9b0c2f. Current code uses ip address to lookup the trampoline and we need the ops to point multiple trampolines, hence this is no longer needed. TODO this probably breaks arm. Signed-off-by: Jiri Olsa --- include/linux/ftrace.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index 1a61f969550de..27b26a87231c2 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -457,9 +457,6 @@ struct ftrace_ops { struct list_head subop_list; ftrace_ops_func_t ops_func; struct ftrace_ops *managed; -#ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS - unsigned long direct_call; -#endif #endif };