2023-03-16 17:38:43

by Florent Revest

[permalink] [raw]
Subject: [PATCH 0/7] Refactor ftrace direct call APIs

This series refactors ftrace direct call APIs in preparation for arm64 support.
It is roughly a subset of [1] rebased on v6.3-rc2 and meant to be taken by
Steven's tree before all the arm64 specific bits.

The first patch was suggested by Steven in a review of [1], it makes it more
obvious to the caller that filters probably need to be freed when unregistering
a direct call.

The next three patches consolidate the two existing ftrace APIs for registering
direct calls. They are only split to make the reviewer's life easier.
Currently, there is both a _ftrace_direct and _ftrace_direct_multi API. Apart
from samples and selftests, there are no users of the _ftrace_direct API left
in-tree so this deletes it and renames the _ftrace_direct_multi API to
_ftrace_direct for simplicity.

The main benefit of this refactoring is that, with the API that's left, an
ftrace_ops backing a direct call will only ever point to one direct call. We can
therefore store the direct called trampoline address in the ops (patch 5) and,
in the future arm64 series, look it up from the ftrace trampoline. (in the
meantime, it makes call_direct_funcs a bit simpler too)

Ftrace direct calls technically don't need DYNAMIC_FTRACE_WITH_REGS so this
extends its support to DYNAMIC_FTRACE_WITH_ARGS (patch 6). arm64 won't support
DYNAMIC_FTRACE_WITH_REGS.

Finally, it fixes the ABI of the stub direct call trampoline used in ftrace
selftests.

This has been tested on x86_64 with:
1- CONFIG_FTRACE_SELFTEST
2- samples/ftrace/*.ko

1: https://lore.kernel.org/all/[email protected]/T/#t

Florent Revest (6):
ftrace: Let unregister_ftrace_direct_multi() call ftrace_free_filter()
ftrace: Replace uses of _ftrace_direct APIs with _ftrace_direct_multi
ftrace: Remove the legacy _ftrace_direct API
ftrace: Rename _ftrace_direct_multi APIs to _ftrace_direct APIs
ftrace: Store direct called addresses in their ops
ftrace: Make DIRECT_CALLS work WITH_ARGS and !WITH_REGS

Mark Rutland (1):
ftrace: selftest: remove broken trace_direct_tramp

arch/s390/kernel/mcount.S | 5 +
arch/x86/kernel/ftrace_32.S | 5 +
arch/x86/kernel/ftrace_64.S | 4 +
include/linux/ftrace.h | 61 +--
kernel/bpf/trampoline.c | 12 +-
kernel/trace/Kconfig | 2 +-
kernel/trace/ftrace.c | 438 ++------------------
kernel/trace/trace_selftest.c | 19 +-
samples/Kconfig | 2 +-
samples/ftrace/ftrace-direct-modify.c | 10 +-
samples/ftrace/ftrace-direct-multi-modify.c | 9 +-
samples/ftrace/ftrace-direct-multi.c | 5 +-
samples/ftrace/ftrace-direct-too.c | 10 +-
samples/ftrace/ftrace-direct.c | 10 +-
14 files changed, 101 insertions(+), 491 deletions(-)

--
2.40.0.rc2.332.ga46443480c-goog



2023-03-16 17:38:45

by Florent Revest

[permalink] [raw]
Subject: [PATCH 1/7] ftrace: Let unregister_ftrace_direct_multi() call ftrace_free_filter()

A common pattern when using the ftrace_direct_multi API is to unregister
the ops and also immediately free its filter. We've noticed it's very
easy for users to miss calling ftrace_free_filter().

This adds a "free_filters" argument to unregister_ftrace_direct_multi()
to both remind the user they should free filters and also to make their
life easier.

Suggested-by: Steven Rostedt <[email protected]>
Signed-off-by: Florent Revest <[email protected]>
---
include/linux/ftrace.h | 6 ++++--
kernel/bpf/trampoline.c | 2 +-
kernel/trace/ftrace.c | 6 +++++-
samples/ftrace/ftrace-direct-multi-modify.c | 3 +--
samples/ftrace/ftrace-direct-multi.c | 3 +--
5 files changed, 12 insertions(+), 8 deletions(-)

diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h
index 366c730beaa3..5b68ee874bc1 100644
--- a/include/linux/ftrace.h
+++ b/include/linux/ftrace.h
@@ -407,7 +407,8 @@ int ftrace_modify_direct_caller(struct ftrace_func_entry *entry,
unsigned long new_addr);
unsigned long ftrace_find_rec_direct(unsigned long ip);
int register_ftrace_direct_multi(struct ftrace_ops *ops, unsigned long addr);
-int unregister_ftrace_direct_multi(struct ftrace_ops *ops, unsigned long addr);
+int unregister_ftrace_direct_multi(struct ftrace_ops *ops, unsigned long addr,
+ bool free_filters);
int modify_ftrace_direct_multi(struct ftrace_ops *ops, unsigned long addr);
int modify_ftrace_direct_multi_nolock(struct ftrace_ops *ops, unsigned long addr);

@@ -446,7 +447,8 @@ static inline int register_ftrace_direct_multi(struct ftrace_ops *ops, unsigned
{
return -ENODEV;
}
-static inline int unregister_ftrace_direct_multi(struct ftrace_ops *ops, unsigned long addr)
+static inline int unregister_ftrace_direct_multi(struct ftrace_ops *ops, unsigned long addr,
+ bool free_filters)
{
return -ENODEV;
}
diff --git a/kernel/bpf/trampoline.c b/kernel/bpf/trampoline.c
index d0ed7d6f5eec..88bc23f1e10a 100644
--- a/kernel/bpf/trampoline.c
+++ b/kernel/bpf/trampoline.c
@@ -198,7 +198,7 @@ static int unregister_fentry(struct bpf_trampoline *tr, void *old_addr)
int ret;

if (tr->func.ftrace_managed)
- ret = unregister_ftrace_direct_multi(tr->fops, (long)old_addr);
+ ret = unregister_ftrace_direct_multi(tr->fops, (long)old_addr, false);
else
ret = bpf_arch_text_poke(ip, BPF_MOD_CALL, old_addr, NULL);

diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index 29baa97d0d53..fa379cf91fdb 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -5804,7 +5804,8 @@ EXPORT_SYMBOL_GPL(register_ftrace_direct_multi);
* 0 on success
* -EINVAL - The @ops object was not properly registered.
*/
-int unregister_ftrace_direct_multi(struct ftrace_ops *ops, unsigned long addr)
+int unregister_ftrace_direct_multi(struct ftrace_ops *ops, unsigned long addr,
+ bool free_filters)
{
struct ftrace_hash *hash = ops->func_hash->filter_hash;
int err;
@@ -5822,6 +5823,9 @@ int unregister_ftrace_direct_multi(struct ftrace_ops *ops, unsigned long addr)
/* cleanup for possible another register call */
ops->func = NULL;
ops->trampoline = 0;
+
+ if (free_filters)
+ ftrace_free_filter(ops);
return err;
}
EXPORT_SYMBOL_GPL(unregister_ftrace_direct_multi);
diff --git a/samples/ftrace/ftrace-direct-multi-modify.c b/samples/ftrace/ftrace-direct-multi-modify.c
index b58c594efb51..196b43971cb5 100644
--- a/samples/ftrace/ftrace-direct-multi-modify.c
+++ b/samples/ftrace/ftrace-direct-multi-modify.c
@@ -151,8 +151,7 @@ static int __init ftrace_direct_multi_init(void)
static void __exit ftrace_direct_multi_exit(void)
{
kthread_stop(simple_tsk);
- unregister_ftrace_direct_multi(&direct, my_tramp);
- ftrace_free_filter(&direct);
+ unregister_ftrace_direct_multi(&direct, my_tramp, true);
}

module_init(ftrace_direct_multi_init);
diff --git a/samples/ftrace/ftrace-direct-multi.c b/samples/ftrace/ftrace-direct-multi.c
index c27cf130c319..ea0e88ee5e43 100644
--- a/samples/ftrace/ftrace-direct-multi.c
+++ b/samples/ftrace/ftrace-direct-multi.c
@@ -78,8 +78,7 @@ static int __init ftrace_direct_multi_init(void)

static void __exit ftrace_direct_multi_exit(void)
{
- unregister_ftrace_direct_multi(&direct, (unsigned long) my_tramp);
- ftrace_free_filter(&direct);
+ unregister_ftrace_direct_multi(&direct, (unsigned long) my_tramp, true);
}

module_init(ftrace_direct_multi_init);
--
2.40.0.rc2.332.ga46443480c-goog


2023-03-16 17:38:48

by Florent Revest

[permalink] [raw]
Subject: [PATCH 2/7] ftrace: Replace uses of _ftrace_direct APIs with _ftrace_direct_multi

The _multi API requires that users keep their own ops but can enforce
that an op is only associated to one direct call.

Signed-off-by: Florent Revest <[email protected]>
Acked-by: Mark Rutland <[email protected]>
Tested-by: Mark Rutland <[email protected]>
---
kernel/trace/trace_selftest.c | 10 ++++++----
samples/ftrace/ftrace-direct-modify.c | 12 ++++++++----
samples/ftrace/ftrace-direct-too.c | 12 +++++++-----
samples/ftrace/ftrace-direct.c | 12 +++++++-----
4 files changed, 28 insertions(+), 18 deletions(-)

diff --git a/kernel/trace/trace_selftest.c b/kernel/trace/trace_selftest.c
index ff0536cea968..9ce80b3ad06d 100644
--- a/kernel/trace/trace_selftest.c
+++ b/kernel/trace/trace_selftest.c
@@ -785,6 +785,7 @@ static struct fgraph_ops fgraph_ops __initdata = {
};

#ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS
+static struct ftrace_ops direct;
#ifndef CALL_DEPTH_ACCOUNT
#define CALL_DEPTH_ACCOUNT ""
#endif
@@ -870,8 +871,8 @@ trace_selftest_startup_function_graph(struct tracer *trace,
* Register direct function together with graph tracer
* and make sure we get graph trace.
*/
- ret = register_ftrace_direct((unsigned long) DYN_FTRACE_TEST_NAME,
- (unsigned long) trace_direct_tramp);
+ ftrace_set_filter_ip(&direct, (unsigned long)DYN_FTRACE_TEST_NAME, 0, 0);
+ ret = register_ftrace_direct_multi(&direct, (unsigned long)trace_direct_tramp);
if (ret)
goto out;

@@ -891,8 +892,9 @@ trace_selftest_startup_function_graph(struct tracer *trace,

unregister_ftrace_graph(&fgraph_ops);

- ret = unregister_ftrace_direct((unsigned long) DYN_FTRACE_TEST_NAME,
- (unsigned long) trace_direct_tramp);
+ ret = unregister_ftrace_direct_multi(&direct,
+ (unsigned long) trace_direct_tramp,
+ true);
if (ret)
goto out;

diff --git a/samples/ftrace/ftrace-direct-modify.c b/samples/ftrace/ftrace-direct-modify.c
index d93abbcb1f4c..f01ac74bac10 100644
--- a/samples/ftrace/ftrace-direct-modify.c
+++ b/samples/ftrace/ftrace-direct-modify.c
@@ -96,6 +96,8 @@ asm (

#endif /* CONFIG_S390 */

+static struct ftrace_ops direct;
+
static unsigned long my_tramp = (unsigned long)my_tramp1;
static unsigned long tramps[2] = {
(unsigned long)my_tramp1,
@@ -114,7 +116,7 @@ static int simple_thread(void *arg)
if (ret)
continue;
t ^= 1;
- ret = modify_ftrace_direct(my_ip, my_tramp, tramps[t]);
+ ret = modify_ftrace_direct_multi(&direct, tramps[t]);
if (!ret)
my_tramp = tramps[t];
WARN_ON_ONCE(ret);
@@ -129,7 +131,9 @@ static int __init ftrace_direct_init(void)
{
int ret;

- ret = register_ftrace_direct(my_ip, my_tramp);
+ ftrace_set_filter_ip(&direct, (unsigned long) my_ip, 0, 0);
+ ret = register_ftrace_direct_multi(&direct, my_tramp);
+
if (!ret)
simple_tsk = kthread_run(simple_thread, NULL, "event-sample-fn");
return ret;
@@ -138,12 +142,12 @@ static int __init ftrace_direct_init(void)
static void __exit ftrace_direct_exit(void)
{
kthread_stop(simple_tsk);
- unregister_ftrace_direct(my_ip, my_tramp);
+ unregister_ftrace_direct_multi(&direct, my_tramp, true);
}

module_init(ftrace_direct_init);
module_exit(ftrace_direct_exit);

MODULE_AUTHOR("Steven Rostedt");
-MODULE_DESCRIPTION("Example use case of using modify_ftrace_direct()");
+MODULE_DESCRIPTION("Example use case of using modify_ftrace_direct_multi()");
MODULE_LICENSE("GPL");
diff --git a/samples/ftrace/ftrace-direct-too.c b/samples/ftrace/ftrace-direct-too.c
index 8139dce2a31c..05c3585ac15e 100644
--- a/samples/ftrace/ftrace-direct-too.c
+++ b/samples/ftrace/ftrace-direct-too.c
@@ -70,21 +70,23 @@ asm (

#endif /* CONFIG_S390 */

+static struct ftrace_ops direct;
+
static int __init ftrace_direct_init(void)
{
- return register_ftrace_direct((unsigned long)handle_mm_fault,
- (unsigned long)my_tramp);
+ ftrace_set_filter_ip(&direct, (unsigned long) handle_mm_fault, 0, 0);
+
+ return register_ftrace_direct_multi(&direct, (unsigned long) my_tramp);
}

static void __exit ftrace_direct_exit(void)
{
- unregister_ftrace_direct((unsigned long)handle_mm_fault,
- (unsigned long)my_tramp);
+ unregister_ftrace_direct_multi(&direct, (unsigned long)my_tramp, true);
}

module_init(ftrace_direct_init);
module_exit(ftrace_direct_exit);

MODULE_AUTHOR("Steven Rostedt");
-MODULE_DESCRIPTION("Another example use case of using register_ftrace_direct()");
+MODULE_DESCRIPTION("Another example use case of using register_ftrace_direct_multi()");
MODULE_LICENSE("GPL");
diff --git a/samples/ftrace/ftrace-direct.c b/samples/ftrace/ftrace-direct.c
index 1d3d307ca33d..42ec9e39453b 100644
--- a/samples/ftrace/ftrace-direct.c
+++ b/samples/ftrace/ftrace-direct.c
@@ -63,21 +63,23 @@ asm (

#endif /* CONFIG_S390 */

+static struct ftrace_ops direct;
+
static int __init ftrace_direct_init(void)
{
- return register_ftrace_direct((unsigned long)wake_up_process,
- (unsigned long)my_tramp);
+ ftrace_set_filter_ip(&direct, (unsigned long) wake_up_process, 0, 0);
+
+ return register_ftrace_direct_multi(&direct, (unsigned long) my_tramp);
}

static void __exit ftrace_direct_exit(void)
{
- unregister_ftrace_direct((unsigned long)wake_up_process,
- (unsigned long)my_tramp);
+ unregister_ftrace_direct_multi(&direct, (unsigned long)my_tramp, true);
}

module_init(ftrace_direct_init);
module_exit(ftrace_direct_exit);

MODULE_AUTHOR("Steven Rostedt");
-MODULE_DESCRIPTION("Example use case of using register_ftrace_direct()");
+MODULE_DESCRIPTION("Example use case of using register_ftrace_direct_multi()");
MODULE_LICENSE("GPL");
--
2.40.0.rc2.332.ga46443480c-goog


2023-03-16 17:38:53

by Florent Revest

[permalink] [raw]
Subject: [PATCH 3/7] ftrace: Remove the legacy _ftrace_direct API

This API relies on a single global ops, used for all direct calls
registered with it. However, to implement arm64 direct calls, we need
each ops to point to a single direct call trampoline.

Signed-off-by: Florent Revest <[email protected]>
Acked-by: Mark Rutland <[email protected]>
Tested-by: Mark Rutland <[email protected]>
---
include/linux/ftrace.h | 32 ----
kernel/trace/ftrace.c | 393 -----------------------------------------
2 files changed, 425 deletions(-)

diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h
index 5b68ee874bc1..2f400c9f0787 100644
--- a/include/linux/ftrace.h
+++ b/include/linux/ftrace.h
@@ -397,14 +397,6 @@ struct ftrace_func_entry {

#ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS
extern int ftrace_direct_func_count;
-int register_ftrace_direct(unsigned long ip, unsigned long addr);
-int unregister_ftrace_direct(unsigned long ip, unsigned long addr);
-int modify_ftrace_direct(unsigned long ip, unsigned long old_addr, unsigned long new_addr);
-struct ftrace_direct_func *ftrace_find_direct_func(unsigned long addr);
-int ftrace_modify_direct_caller(struct ftrace_func_entry *entry,
- struct dyn_ftrace *rec,
- unsigned long old_addr,
- unsigned long new_addr);
unsigned long ftrace_find_rec_direct(unsigned long ip);
int register_ftrace_direct_multi(struct ftrace_ops *ops, unsigned long addr);
int unregister_ftrace_direct_multi(struct ftrace_ops *ops, unsigned long addr,
@@ -415,30 +407,6 @@ int modify_ftrace_direct_multi_nolock(struct ftrace_ops *ops, unsigned long addr
#else
struct ftrace_ops;
# define ftrace_direct_func_count 0
-static inline int register_ftrace_direct(unsigned long ip, unsigned long addr)
-{
- return -ENOTSUPP;
-}
-static inline int unregister_ftrace_direct(unsigned long ip, unsigned long addr)
-{
- return -ENOTSUPP;
-}
-static inline int modify_ftrace_direct(unsigned long ip,
- unsigned long old_addr, unsigned long new_addr)
-{
- return -ENOTSUPP;
-}
-static inline struct ftrace_direct_func *ftrace_find_direct_func(unsigned long addr)
-{
- return NULL;
-}
-static inline int ftrace_modify_direct_caller(struct ftrace_func_entry *entry,
- struct dyn_ftrace *rec,
- unsigned long old_addr,
- unsigned long new_addr)
-{
- return -ENODEV;
-}
static inline unsigned long ftrace_find_rec_direct(unsigned long ip)
{
return 0;
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index fa379cf91fdb..fca478396d31 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -2590,20 +2590,6 @@ static void call_direct_funcs(unsigned long ip, unsigned long pip,

arch_ftrace_set_direct_caller(fregs, addr);
}
-
-struct ftrace_ops direct_ops = {
- .func = call_direct_funcs,
- .flags = FTRACE_OPS_FL_DIRECT | FTRACE_OPS_FL_SAVE_REGS
- | FTRACE_OPS_FL_PERMANENT,
- /*
- * By declaring the main trampoline as this trampoline
- * it will never have one allocated for it. Allocated
- * trampolines should not call direct functions.
- * The direct_ops should only be called by the builtin
- * ftrace_regs_caller trampoline.
- */
- .trampoline = FTRACE_REGS_ADDR,
-};
#endif /* CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS */

/**
@@ -5300,387 +5286,8 @@ struct ftrace_direct_func {

static LIST_HEAD(ftrace_direct_funcs);

-/**
- * ftrace_find_direct_func - test an address if it is a registered direct caller
- * @addr: The address of a registered direct caller
- *
- * This searches to see if a ftrace direct caller has been registered
- * at a specific address, and if so, it returns a descriptor for it.
- *
- * This can be used by architecture code to see if an address is
- * a direct caller (trampoline) attached to a fentry/mcount location.
- * This is useful for the function_graph tracer, as it may need to
- * do adjustments if it traced a location that also has a direct
- * trampoline attached to it.
- */
-struct ftrace_direct_func *ftrace_find_direct_func(unsigned long addr)
-{
- struct ftrace_direct_func *entry;
- bool found = false;
-
- /* May be called by fgraph trampoline (protected by rcu tasks) */
- list_for_each_entry_rcu(entry, &ftrace_direct_funcs, next) {
- if (entry->addr == addr) {
- found = true;
- break;
- }
- }
- if (found)
- return entry;
-
- return NULL;
-}
-
-static struct ftrace_direct_func *ftrace_alloc_direct_func(unsigned long addr)
-{
- struct ftrace_direct_func *direct;
-
- direct = kmalloc(sizeof(*direct), GFP_KERNEL);
- if (!direct)
- return NULL;
- direct->addr = addr;
- direct->count = 0;
- list_add_rcu(&direct->next, &ftrace_direct_funcs);
- ftrace_direct_func_count++;
- return direct;
-}
-
static int register_ftrace_function_nolock(struct ftrace_ops *ops);

-/**
- * register_ftrace_direct - Call a custom trampoline directly
- * @ip: The address of the nop at the beginning of a function
- * @addr: The address of the trampoline to call at @ip
- *
- * This is used to connect a direct call from the nop location (@ip)
- * at the start of ftrace traced functions. The location that it calls
- * (@addr) must be able to handle a direct call, and save the parameters
- * of the function being traced, and restore them (or inject new ones
- * if needed), before returning.
- *
- * Returns:
- * 0 on success
- * -EBUSY - Another direct function is already attached (there can be only one)
- * -ENODEV - @ip does not point to a ftrace nop location (or not supported)
- * -ENOMEM - There was an allocation failure.
- */
-int register_ftrace_direct(unsigned long ip, unsigned long addr)
-{
- struct ftrace_direct_func *direct;
- struct ftrace_func_entry *entry;
- struct ftrace_hash *free_hash = NULL;
- struct dyn_ftrace *rec;
- int ret = -ENODEV;
-
- mutex_lock(&direct_mutex);
-
- ip = ftrace_location(ip);
- if (!ip)
- goto out_unlock;
-
- /* See if there's a direct function at @ip already */
- ret = -EBUSY;
- if (ftrace_find_rec_direct(ip))
- goto out_unlock;
-
- ret = -ENODEV;
- rec = lookup_rec(ip, ip);
- if (!rec)
- goto out_unlock;
-
- /*
- * Check if the rec says it has a direct call but we didn't
- * find one earlier?
- */
- if (WARN_ON(rec->flags & FTRACE_FL_DIRECT))
- goto out_unlock;
-
- /* Make sure the ip points to the exact record */
- if (ip != rec->ip) {
- ip = rec->ip;
- /* Need to check this ip for a direct. */
- if (ftrace_find_rec_direct(ip))
- goto out_unlock;
- }
-
- ret = -ENOMEM;
- direct = ftrace_find_direct_func(addr);
- if (!direct) {
- direct = ftrace_alloc_direct_func(addr);
- if (!direct)
- goto out_unlock;
- }
-
- entry = ftrace_add_rec_direct(ip, addr, &free_hash);
- if (!entry)
- goto out_unlock;
-
- ret = ftrace_set_filter_ip(&direct_ops, ip, 0, 0);
-
- if (!ret && !(direct_ops.flags & FTRACE_OPS_FL_ENABLED)) {
- ret = register_ftrace_function_nolock(&direct_ops);
- if (ret)
- ftrace_set_filter_ip(&direct_ops, ip, 1, 0);
- }
-
- if (ret) {
- remove_hash_entry(direct_functions, entry);
- kfree(entry);
- if (!direct->count) {
- list_del_rcu(&direct->next);
- synchronize_rcu_tasks();
- kfree(direct);
- if (free_hash)
- free_ftrace_hash(free_hash);
- free_hash = NULL;
- ftrace_direct_func_count--;
- }
- } else {
- direct->count++;
- }
- out_unlock:
- mutex_unlock(&direct_mutex);
-
- if (free_hash) {
- synchronize_rcu_tasks();
- free_ftrace_hash(free_hash);
- }
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(register_ftrace_direct);
-
-static struct ftrace_func_entry *find_direct_entry(unsigned long *ip,
- struct dyn_ftrace **recp)
-{
- struct ftrace_func_entry *entry;
- struct dyn_ftrace *rec;
-
- rec = lookup_rec(*ip, *ip);
- if (!rec)
- return NULL;
-
- entry = __ftrace_lookup_ip(direct_functions, rec->ip);
- if (!entry) {
- WARN_ON(rec->flags & FTRACE_FL_DIRECT);
- return NULL;
- }
-
- WARN_ON(!(rec->flags & FTRACE_FL_DIRECT));
-
- /* Passed in ip just needs to be on the call site */
- *ip = rec->ip;
-
- if (recp)
- *recp = rec;
-
- return entry;
-}
-
-int unregister_ftrace_direct(unsigned long ip, unsigned long addr)
-{
- struct ftrace_direct_func *direct;
- struct ftrace_func_entry *entry;
- struct ftrace_hash *hash;
- int ret = -ENODEV;
-
- mutex_lock(&direct_mutex);
-
- ip = ftrace_location(ip);
- if (!ip)
- goto out_unlock;
-
- entry = find_direct_entry(&ip, NULL);
- if (!entry)
- goto out_unlock;
-
- hash = direct_ops.func_hash->filter_hash;
- if (hash->count == 1)
- unregister_ftrace_function(&direct_ops);
-
- ret = ftrace_set_filter_ip(&direct_ops, ip, 1, 0);
-
- WARN_ON(ret);
-
- remove_hash_entry(direct_functions, entry);
-
- direct = ftrace_find_direct_func(addr);
- if (!WARN_ON(!direct)) {
- /* This is the good path (see the ! before WARN) */
- direct->count--;
- WARN_ON(direct->count < 0);
- if (!direct->count) {
- list_del_rcu(&direct->next);
- synchronize_rcu_tasks();
- kfree(direct);
- kfree(entry);
- ftrace_direct_func_count--;
- }
- }
- out_unlock:
- mutex_unlock(&direct_mutex);
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(unregister_ftrace_direct);
-
-static struct ftrace_ops stub_ops = {
- .func = ftrace_stub,
-};
-
-/**
- * ftrace_modify_direct_caller - modify ftrace nop directly
- * @entry: The ftrace hash entry of the direct helper for @rec
- * @rec: The record representing the function site to patch
- * @old_addr: The location that the site at @rec->ip currently calls
- * @new_addr: The location that the site at @rec->ip should call
- *
- * An architecture may overwrite this function to optimize the
- * changing of the direct callback on an ftrace nop location.
- * This is called with the ftrace_lock mutex held, and no other
- * ftrace callbacks are on the associated record (@rec). Thus,
- * it is safe to modify the ftrace record, where it should be
- * currently calling @old_addr directly, to call @new_addr.
- *
- * This is called with direct_mutex locked.
- *
- * Safety checks should be made to make sure that the code at
- * @rec->ip is currently calling @old_addr. And this must
- * also update entry->direct to @new_addr.
- */
-int __weak ftrace_modify_direct_caller(struct ftrace_func_entry *entry,
- struct dyn_ftrace *rec,
- unsigned long old_addr,
- unsigned long new_addr)
-{
- unsigned long ip = rec->ip;
- int ret;
-
- lockdep_assert_held(&direct_mutex);
-
- /*
- * The ftrace_lock was used to determine if the record
- * had more than one registered user to it. If it did,
- * we needed to prevent that from changing to do the quick
- * switch. But if it did not (only a direct caller was attached)
- * then this function is called. But this function can deal
- * with attached callers to the rec that we care about, and
- * since this function uses standard ftrace calls that take
- * the ftrace_lock mutex, we need to release it.
- */
- mutex_unlock(&ftrace_lock);
-
- /*
- * By setting a stub function at the same address, we force
- * the code to call the iterator and the direct_ops helper.
- * This means that @ip does not call the direct call, and
- * we can simply modify it.
- */
- ret = ftrace_set_filter_ip(&stub_ops, ip, 0, 0);
- if (ret)
- goto out_lock;
-
- ret = register_ftrace_function_nolock(&stub_ops);
- if (ret) {
- ftrace_set_filter_ip(&stub_ops, ip, 1, 0);
- goto out_lock;
- }
-
- entry->direct = new_addr;
-
- /*
- * By removing the stub, we put back the direct call, calling
- * the @new_addr.
- */
- unregister_ftrace_function(&stub_ops);
- ftrace_set_filter_ip(&stub_ops, ip, 1, 0);
-
- out_lock:
- mutex_lock(&ftrace_lock);
-
- return ret;
-}
-
-/**
- * modify_ftrace_direct - Modify an existing direct call to call something else
- * @ip: The instruction pointer to modify
- * @old_addr: The address that the current @ip calls directly
- * @new_addr: The address that the @ip should call
- *
- * This modifies a ftrace direct caller at an instruction pointer without
- * having to disable it first. The direct call will switch over to the
- * @new_addr without missing anything.
- *
- * Returns: zero on success. Non zero on error, which includes:
- * -ENODEV : the @ip given has no direct caller attached
- * -EINVAL : the @old_addr does not match the current direct caller
- */
-int modify_ftrace_direct(unsigned long ip,
- unsigned long old_addr, unsigned long new_addr)
-{
- struct ftrace_direct_func *direct, *new_direct = NULL;
- struct ftrace_func_entry *entry;
- struct dyn_ftrace *rec;
- int ret = -ENODEV;
-
- mutex_lock(&direct_mutex);
-
- mutex_lock(&ftrace_lock);
-
- ip = ftrace_location(ip);
- if (!ip)
- goto out_unlock;
-
- entry = find_direct_entry(&ip, &rec);
- if (!entry)
- goto out_unlock;
-
- ret = -EINVAL;
- if (entry->direct != old_addr)
- goto out_unlock;
-
- direct = ftrace_find_direct_func(old_addr);
- if (WARN_ON(!direct))
- goto out_unlock;
- if (direct->count > 1) {
- ret = -ENOMEM;
- new_direct = ftrace_alloc_direct_func(new_addr);
- if (!new_direct)
- goto out_unlock;
- direct->count--;
- new_direct->count++;
- } else {
- direct->addr = new_addr;
- }
-
- /*
- * If there's no other ftrace callback on the rec->ip location,
- * then it can be changed directly by the architecture.
- * If there is another caller, then we just need to change the
- * direct caller helper to point to @new_addr.
- */
- if (ftrace_rec_count(rec) == 1) {
- ret = ftrace_modify_direct_caller(entry, rec, old_addr, new_addr);
- } else {
- entry->direct = new_addr;
- ret = 0;
- }
-
- if (unlikely(ret && new_direct)) {
- direct->count++;
- list_del_rcu(&new_direct->next);
- synchronize_rcu_tasks();
- kfree(new_direct);
- ftrace_direct_func_count--;
- }
-
- out_unlock:
- mutex_unlock(&ftrace_lock);
- mutex_unlock(&direct_mutex);
- return ret;
-}
-EXPORT_SYMBOL_GPL(modify_ftrace_direct);
-
#define MULTI_FLAGS (FTRACE_OPS_FL_DIRECT | FTRACE_OPS_FL_SAVE_REGS)

static int check_direct_multi(struct ftrace_ops *ops)
--
2.40.0.rc2.332.ga46443480c-goog


2023-03-16 17:38:58

by Florent Revest

[permalink] [raw]
Subject: [PATCH 5/7] ftrace: Store direct called addresses in their ops

All direct calls are now registered using the register_ftrace_direct API
so each ops can jump to only one direct-called trampoline.

By storing the direct called trampoline address directly in the ops we
can save one hashmap lookup in the direct call ops and implement arm64
direct calls on top of call ops.

Signed-off-by: Florent Revest <[email protected]>
---
include/linux/ftrace.h | 3 +++
kernel/trace/ftrace.c | 7 +++++--
2 files changed, 8 insertions(+), 2 deletions(-)

diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h
index abee60865fc7..6a532dd6789e 100644
--- a/include/linux/ftrace.h
+++ b/include/linux/ftrace.h
@@ -321,6 +321,9 @@ struct ftrace_ops {
unsigned long trampoline_size;
struct list_head list;
ftrace_ops_func_t ops_func;
+#ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS
+ unsigned long direct_call;
+#endif
#endif
};

diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index 33530198d1ca..66c91fa4b6ab 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -2582,9 +2582,8 @@ ftrace_add_rec_direct(unsigned long ip, unsigned long addr,
static void call_direct_funcs(unsigned long ip, unsigned long pip,
struct ftrace_ops *ops, struct ftrace_regs *fregs)
{
- unsigned long addr;
+ unsigned long addr = ops->direct_call;

- addr = ftrace_find_rec_direct(ip);
if (!addr)
return;

@@ -5380,6 +5379,7 @@ int register_ftrace_direct(struct ftrace_ops *ops, unsigned long addr)
ops->func = call_direct_funcs;
ops->flags = MULTI_FLAGS;
ops->trampoline = FTRACE_REGS_ADDR;
+ ops->direct_call = addr;

err = register_ftrace_function_nolock(ops);

@@ -5454,6 +5454,7 @@ __modify_ftrace_direct(struct ftrace_ops *ops, unsigned long addr)
/* 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)
@@ -5475,6 +5476,8 @@ __modify_ftrace_direct(struct ftrace_ops *ops, unsigned long addr)
entry->direct = addr;
}
}
+ /* Prevent store tearing if a trampoline concurrently accesses the value */
+ WRITE_ONCE(ops->direct_call, addr);

mutex_unlock(&ftrace_lock);

--
2.40.0.rc2.332.ga46443480c-goog


2023-03-16 17:39:02

by Florent Revest

[permalink] [raw]
Subject: [PATCH 4/7] ftrace: Rename _ftrace_direct_multi APIs to _ftrace_direct APIs

Now that the original _ftrace_direct APIs are gone, the "_multi"
suffixes only add confusion.

Signed-off-by: Florent Revest <[email protected]>
Acked-by: Mark Rutland <[email protected]>
Tested-by: Mark Rutland <[email protected]>
---
include/linux/ftrace.h | 20 ++++++------
kernel/bpf/trampoline.c | 12 ++++----
kernel/trace/ftrace.c | 34 ++++++++++-----------
kernel/trace/trace_selftest.c | 9 +++---
samples/Kconfig | 2 +-
samples/ftrace/ftrace-direct-modify.c | 8 ++---
samples/ftrace/ftrace-direct-multi-modify.c | 8 ++---
samples/ftrace/ftrace-direct-multi.c | 4 +--
samples/ftrace/ftrace-direct-too.c | 6 ++--
samples/ftrace/ftrace-direct.c | 6 ++--
10 files changed, 55 insertions(+), 54 deletions(-)

diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h
index 2f400c9f0787..abee60865fc7 100644
--- a/include/linux/ftrace.h
+++ b/include/linux/ftrace.h
@@ -398,11 +398,11 @@ struct ftrace_func_entry {
#ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS
extern int ftrace_direct_func_count;
unsigned long ftrace_find_rec_direct(unsigned long ip);
-int register_ftrace_direct_multi(struct ftrace_ops *ops, unsigned long addr);
-int unregister_ftrace_direct_multi(struct ftrace_ops *ops, unsigned long addr,
- bool free_filters);
-int modify_ftrace_direct_multi(struct ftrace_ops *ops, unsigned long addr);
-int modify_ftrace_direct_multi_nolock(struct ftrace_ops *ops, unsigned long addr);
+int register_ftrace_direct(struct ftrace_ops *ops, unsigned long addr);
+int unregister_ftrace_direct(struct ftrace_ops *ops, 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);

#else
struct ftrace_ops;
@@ -411,20 +411,20 @@ static inline unsigned long ftrace_find_rec_direct(unsigned long ip)
{
return 0;
}
-static inline int register_ftrace_direct_multi(struct ftrace_ops *ops, unsigned long addr)
+static inline int register_ftrace_direct(struct ftrace_ops *ops, unsigned long addr)
{
return -ENODEV;
}
-static inline int unregister_ftrace_direct_multi(struct ftrace_ops *ops, unsigned long addr,
- bool free_filters)
+static inline int unregister_ftrace_direct(struct ftrace_ops *ops, unsigned long addr,
+ bool free_filters)
{
return -ENODEV;
}
-static inline int modify_ftrace_direct_multi(struct ftrace_ops *ops, unsigned long addr)
+static inline int modify_ftrace_direct(struct ftrace_ops *ops, unsigned long addr)
{
return -ENODEV;
}
-static inline int modify_ftrace_direct_multi_nolock(struct ftrace_ops *ops, unsigned long addr)
+static inline int modify_ftrace_direct_nolock(struct ftrace_ops *ops, unsigned long addr)
{
return -ENODEV;
}
diff --git a/kernel/bpf/trampoline.c b/kernel/bpf/trampoline.c
index 88bc23f1e10a..a14d0af534b3 100644
--- a/kernel/bpf/trampoline.c
+++ b/kernel/bpf/trampoline.c
@@ -45,8 +45,8 @@ static int bpf_tramp_ftrace_ops_func(struct ftrace_ops *ops, enum ftrace_ops_cmd
lockdep_assert_held_once(&tr->mutex);

/* Instead of updating the trampoline here, we propagate
- * -EAGAIN to register_ftrace_direct_multi(). Then we can
- * retry register_ftrace_direct_multi() after updating the
+ * -EAGAIN to register_ftrace_direct(). Then we can
+ * retry register_ftrace_direct() after updating the
* trampoline.
*/
if ((tr->flags & BPF_TRAMP_F_CALL_ORIG) &&
@@ -198,7 +198,7 @@ static int unregister_fentry(struct bpf_trampoline *tr, void *old_addr)
int ret;

if (tr->func.ftrace_managed)
- ret = unregister_ftrace_direct_multi(tr->fops, (long)old_addr, false);
+ ret = unregister_ftrace_direct(tr->fops, (long)old_addr, false);
else
ret = bpf_arch_text_poke(ip, BPF_MOD_CALL, old_addr, NULL);

@@ -215,9 +215,9 @@ static int modify_fentry(struct bpf_trampoline *tr, void *old_addr, void *new_ad

if (tr->func.ftrace_managed) {
if (lock_direct_mutex)
- ret = modify_ftrace_direct_multi(tr->fops, (long)new_addr);
+ ret = modify_ftrace_direct(tr->fops, (long)new_addr);
else
- ret = modify_ftrace_direct_multi_nolock(tr->fops, (long)new_addr);
+ ret = modify_ftrace_direct_nolock(tr->fops, (long)new_addr);
} else {
ret = bpf_arch_text_poke(ip, BPF_MOD_CALL, old_addr, new_addr);
}
@@ -243,7 +243,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_multi(tr->fops, (long)new_addr);
+ ret = register_ftrace_direct(tr->fops, (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 fca478396d31..33530198d1ca 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -5317,7 +5317,7 @@ static void remove_direct_functions_hash(struct ftrace_hash *hash, unsigned long
}

/**
- * register_ftrace_direct_multi - Call a custom trampoline directly
+ * register_ftrace_direct - Call a custom trampoline directly
* for multiple functions registered in @ops
* @ops: The address of the struct ftrace_ops object
* @addr: The address of the trampoline to call at @ops functions
@@ -5338,7 +5338,7 @@ static void remove_direct_functions_hash(struct ftrace_hash *hash, unsigned long
* -ENODEV - @ip does not point to a ftrace nop location (or not supported)
* -ENOMEM - There was an allocation failure.
*/
-int register_ftrace_direct_multi(struct ftrace_ops *ops, unsigned long addr)
+int register_ftrace_direct(struct ftrace_ops *ops, unsigned long addr)
{
struct ftrace_hash *hash, *free_hash = NULL;
struct ftrace_func_entry *entry, *new;
@@ -5396,11 +5396,11 @@ int register_ftrace_direct_multi(struct ftrace_ops *ops, unsigned long addr)
}
return err;
}
-EXPORT_SYMBOL_GPL(register_ftrace_direct_multi);
+EXPORT_SYMBOL_GPL(register_ftrace_direct);

/**
- * unregister_ftrace_direct_multi - Remove calls to custom trampoline
- * previously registered by register_ftrace_direct_multi for @ops object.
+ * unregister_ftrace_direct - Remove calls to custom trampoline
+ * previously registered by register_ftrace_direct for @ops object.
* @ops: The address of the struct ftrace_ops object
*
* This is used to remove a direct calls to @addr from the nop locations
@@ -5411,8 +5411,8 @@ EXPORT_SYMBOL_GPL(register_ftrace_direct_multi);
* 0 on success
* -EINVAL - The @ops object was not properly registered.
*/
-int unregister_ftrace_direct_multi(struct ftrace_ops *ops, unsigned long addr,
- bool free_filters)
+int unregister_ftrace_direct(struct ftrace_ops *ops, unsigned long addr,
+ bool free_filters)
{
struct ftrace_hash *hash = ops->func_hash->filter_hash;
int err;
@@ -5435,10 +5435,10 @@ int unregister_ftrace_direct_multi(struct ftrace_ops *ops, unsigned long addr,
ftrace_free_filter(ops);
return err;
}
-EXPORT_SYMBOL_GPL(unregister_ftrace_direct_multi);
+EXPORT_SYMBOL_GPL(unregister_ftrace_direct);

static int
-__modify_ftrace_direct_multi(struct ftrace_ops *ops, unsigned long addr)
+__modify_ftrace_direct(struct ftrace_ops *ops, unsigned long addr)
{
struct ftrace_hash *hash;
struct ftrace_func_entry *entry, *iter;
@@ -5485,7 +5485,7 @@ __modify_ftrace_direct_multi(struct ftrace_ops *ops, unsigned long addr)
}

/**
- * modify_ftrace_direct_multi_nolock - Modify an existing direct 'multi' call
+ * 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
@@ -5502,19 +5502,19 @@ __modify_ftrace_direct_multi(struct ftrace_ops *ops, unsigned long addr)
* Returns: zero on success. Non zero on error, which includes:
* -EINVAL - The @ops object was not properly registered.
*/
-int modify_ftrace_direct_multi_nolock(struct ftrace_ops *ops, unsigned long addr)
+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_multi(ops, addr);
+ return __modify_ftrace_direct(ops, addr);
}
-EXPORT_SYMBOL_GPL(modify_ftrace_direct_multi_nolock);
+EXPORT_SYMBOL_GPL(modify_ftrace_direct_nolock);

/**
- * modify_ftrace_direct_multi - Modify an existing direct 'multi' call
+ * modify_ftrace_direct - 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
@@ -5528,7 +5528,7 @@ EXPORT_SYMBOL_GPL(modify_ftrace_direct_multi_nolock);
* Returns: zero on success. Non zero on error, which includes:
* -EINVAL - The @ops object was not properly registered.
*/
-int modify_ftrace_direct_multi(struct ftrace_ops *ops, unsigned long addr)
+int modify_ftrace_direct(struct ftrace_ops *ops, unsigned long addr)
{
int err;

@@ -5538,11 +5538,11 @@ int modify_ftrace_direct_multi(struct ftrace_ops *ops, unsigned long addr)
return -EINVAL;

mutex_lock(&direct_mutex);
- err = __modify_ftrace_direct_multi(ops, addr);
+ err = __modify_ftrace_direct(ops, addr);
mutex_unlock(&direct_mutex);
return err;
}
-EXPORT_SYMBOL_GPL(modify_ftrace_direct_multi);
+EXPORT_SYMBOL_GPL(modify_ftrace_direct);
#endif /* CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS */

/**
diff --git a/kernel/trace/trace_selftest.c b/kernel/trace/trace_selftest.c
index 9ce80b3ad06d..84cd7ba31d27 100644
--- a/kernel/trace/trace_selftest.c
+++ b/kernel/trace/trace_selftest.c
@@ -872,7 +872,8 @@ trace_selftest_startup_function_graph(struct tracer *trace,
* and make sure we get graph trace.
*/
ftrace_set_filter_ip(&direct, (unsigned long)DYN_FTRACE_TEST_NAME, 0, 0);
- ret = register_ftrace_direct_multi(&direct, (unsigned long)trace_direct_tramp);
+ ret = register_ftrace_direct(&direct,
+ (unsigned long)trace_direct_tramp);
if (ret)
goto out;

@@ -892,9 +893,9 @@ trace_selftest_startup_function_graph(struct tracer *trace,

unregister_ftrace_graph(&fgraph_ops);

- ret = unregister_ftrace_direct_multi(&direct,
- (unsigned long) trace_direct_tramp,
- true);
+ ret = unregister_ftrace_direct(&direct,
+ (unsigned long) trace_direct_tramp,
+ true);
if (ret)
goto out;

diff --git a/samples/Kconfig b/samples/Kconfig
index 30ef8bd48ba3..fd24daa99f34 100644
--- a/samples/Kconfig
+++ b/samples/Kconfig
@@ -38,7 +38,7 @@ config SAMPLE_FTRACE_DIRECT
that hooks to wake_up_process and prints the parameters.

config SAMPLE_FTRACE_DIRECT_MULTI
- tristate "Build register_ftrace_direct_multi() example"
+ tristate "Build register_ftrace_direct() on multiple ips example"
depends on DYNAMIC_FTRACE_WITH_DIRECT_CALLS && m
depends on HAVE_SAMPLE_FTRACE_DIRECT_MULTI
help
diff --git a/samples/ftrace/ftrace-direct-modify.c b/samples/ftrace/ftrace-direct-modify.c
index f01ac74bac10..25fba66f61c0 100644
--- a/samples/ftrace/ftrace-direct-modify.c
+++ b/samples/ftrace/ftrace-direct-modify.c
@@ -116,7 +116,7 @@ static int simple_thread(void *arg)
if (ret)
continue;
t ^= 1;
- ret = modify_ftrace_direct_multi(&direct, tramps[t]);
+ ret = modify_ftrace_direct(&direct, tramps[t]);
if (!ret)
my_tramp = tramps[t];
WARN_ON_ONCE(ret);
@@ -132,7 +132,7 @@ static int __init ftrace_direct_init(void)
int ret;

ftrace_set_filter_ip(&direct, (unsigned long) my_ip, 0, 0);
- ret = register_ftrace_direct_multi(&direct, my_tramp);
+ ret = register_ftrace_direct(&direct, my_tramp);

if (!ret)
simple_tsk = kthread_run(simple_thread, NULL, "event-sample-fn");
@@ -142,12 +142,12 @@ static int __init ftrace_direct_init(void)
static void __exit ftrace_direct_exit(void)
{
kthread_stop(simple_tsk);
- unregister_ftrace_direct_multi(&direct, my_tramp, true);
+ unregister_ftrace_direct(&direct, my_tramp, true);
}

module_init(ftrace_direct_init);
module_exit(ftrace_direct_exit);

MODULE_AUTHOR("Steven Rostedt");
-MODULE_DESCRIPTION("Example use case of using modify_ftrace_direct_multi()");
+MODULE_DESCRIPTION("Example use case of using modify_ftrace_direct()");
MODULE_LICENSE("GPL");
diff --git a/samples/ftrace/ftrace-direct-multi-modify.c b/samples/ftrace/ftrace-direct-multi-modify.c
index 196b43971cb5..f72623899602 100644
--- a/samples/ftrace/ftrace-direct-multi-modify.c
+++ b/samples/ftrace/ftrace-direct-multi-modify.c
@@ -123,7 +123,7 @@ static int simple_thread(void *arg)
if (ret)
continue;
t ^= 1;
- ret = modify_ftrace_direct_multi(&direct, tramps[t]);
+ ret = modify_ftrace_direct(&direct, tramps[t]);
if (!ret)
my_tramp = tramps[t];
WARN_ON_ONCE(ret);
@@ -141,7 +141,7 @@ static int __init ftrace_direct_multi_init(void)
ftrace_set_filter_ip(&direct, (unsigned long) wake_up_process, 0, 0);
ftrace_set_filter_ip(&direct, (unsigned long) schedule, 0, 0);

- ret = register_ftrace_direct_multi(&direct, my_tramp);
+ ret = register_ftrace_direct(&direct, my_tramp);

if (!ret)
simple_tsk = kthread_run(simple_thread, NULL, "event-sample-fn");
@@ -151,12 +151,12 @@ static int __init ftrace_direct_multi_init(void)
static void __exit ftrace_direct_multi_exit(void)
{
kthread_stop(simple_tsk);
- unregister_ftrace_direct_multi(&direct, my_tramp, true);
+ unregister_ftrace_direct(&direct, my_tramp, true);
}

module_init(ftrace_direct_multi_init);
module_exit(ftrace_direct_multi_exit);

MODULE_AUTHOR("Jiri Olsa");
-MODULE_DESCRIPTION("Example use case of using modify_ftrace_direct_multi()");
+MODULE_DESCRIPTION("Example use case of using modify_ftrace_direct()");
MODULE_LICENSE("GPL");
diff --git a/samples/ftrace/ftrace-direct-multi.c b/samples/ftrace/ftrace-direct-multi.c
index ea0e88ee5e43..1547c2c6be02 100644
--- a/samples/ftrace/ftrace-direct-multi.c
+++ b/samples/ftrace/ftrace-direct-multi.c
@@ -73,12 +73,12 @@ static int __init ftrace_direct_multi_init(void)
ftrace_set_filter_ip(&direct, (unsigned long) wake_up_process, 0, 0);
ftrace_set_filter_ip(&direct, (unsigned long) schedule, 0, 0);

- return register_ftrace_direct_multi(&direct, (unsigned long) my_tramp);
+ return register_ftrace_direct(&direct, (unsigned long) my_tramp);
}

static void __exit ftrace_direct_multi_exit(void)
{
- unregister_ftrace_direct_multi(&direct, (unsigned long) my_tramp, true);
+ unregister_ftrace_direct(&direct, (unsigned long) my_tramp, true);
}

module_init(ftrace_direct_multi_init);
diff --git a/samples/ftrace/ftrace-direct-too.c b/samples/ftrace/ftrace-direct-too.c
index 05c3585ac15e..f28e7b99840f 100644
--- a/samples/ftrace/ftrace-direct-too.c
+++ b/samples/ftrace/ftrace-direct-too.c
@@ -76,17 +76,17 @@ static int __init ftrace_direct_init(void)
{
ftrace_set_filter_ip(&direct, (unsigned long) handle_mm_fault, 0, 0);

- return register_ftrace_direct_multi(&direct, (unsigned long) my_tramp);
+ return register_ftrace_direct(&direct, (unsigned long) my_tramp);
}

static void __exit ftrace_direct_exit(void)
{
- unregister_ftrace_direct_multi(&direct, (unsigned long)my_tramp, true);
+ unregister_ftrace_direct(&direct, (unsigned long)my_tramp, true);
}

module_init(ftrace_direct_init);
module_exit(ftrace_direct_exit);

MODULE_AUTHOR("Steven Rostedt");
-MODULE_DESCRIPTION("Another example use case of using register_ftrace_direct_multi()");
+MODULE_DESCRIPTION("Another example use case of using register_ftrace_direct()");
MODULE_LICENSE("GPL");
diff --git a/samples/ftrace/ftrace-direct.c b/samples/ftrace/ftrace-direct.c
index 42ec9e39453b..d81a9473b585 100644
--- a/samples/ftrace/ftrace-direct.c
+++ b/samples/ftrace/ftrace-direct.c
@@ -69,17 +69,17 @@ static int __init ftrace_direct_init(void)
{
ftrace_set_filter_ip(&direct, (unsigned long) wake_up_process, 0, 0);

- return register_ftrace_direct_multi(&direct, (unsigned long) my_tramp);
+ return register_ftrace_direct(&direct, (unsigned long) my_tramp);
}

static void __exit ftrace_direct_exit(void)
{
- unregister_ftrace_direct_multi(&direct, (unsigned long)my_tramp, true);
+ unregister_ftrace_direct(&direct, (unsigned long)my_tramp, true);
}

module_init(ftrace_direct_init);
module_exit(ftrace_direct_exit);

MODULE_AUTHOR("Steven Rostedt");
-MODULE_DESCRIPTION("Example use case of using register_ftrace_direct_multi()");
+MODULE_DESCRIPTION("Example use case of using register_ftrace_direct()");
MODULE_LICENSE("GPL");
--
2.40.0.rc2.332.ga46443480c-goog


2023-03-16 17:39:06

by Florent Revest

[permalink] [raw]
Subject: [PATCH 6/7] ftrace: Make DIRECT_CALLS work WITH_ARGS and !WITH_REGS

Direct called trampolines can be called in two ways:
- either from the ftrace callsite. In this case, they do not access any
struct ftrace_regs nor pt_regs
- Or, if a ftrace ops is also attached, from the end of a ftrace
trampoline. In this case, the call_direct_funcs ops is in charge of
setting the direct call trampoline's address in a struct ftrace_regs

Since:

commit 9705bc709604 ("ftrace: pass fregs to arch_ftrace_set_direct_caller()")

The later case no longer requires a full pt_regs. It only needs a struct
ftrace_regs so DIRECT_CALLS can work with both WITH_ARGS or WITH_REGS.
With architectures like arm64 already abandoning WITH_REGS in favor of
WITH_ARGS, it's important to have DIRECT_CALLS work WITH_ARGS only.

Signed-off-by: Florent Revest <[email protected]>
Co-developed-by: Mark Rutland <[email protected]>
Signed-off-by: Mark Rutland <[email protected]>
---
include/linux/ftrace.h | 6 ++++++
kernel/trace/Kconfig | 2 +-
kernel/trace/ftrace.c | 2 +-
3 files changed, 8 insertions(+), 2 deletions(-)

diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h
index 6a532dd6789e..31f1e1df2af3 100644
--- a/include/linux/ftrace.h
+++ b/include/linux/ftrace.h
@@ -241,6 +241,12 @@ enum {
FTRACE_OPS_FL_DIRECT = BIT(17),
};

+#ifndef CONFIG_DYNAMIC_FTRACE_WITH_ARGS
+#define FTRACE_OPS_FL_SAVE_ARGS FTRACE_OPS_FL_SAVE_REGS
+#else
+#define FTRACE_OPS_FL_SAVE_ARGS 0
+#endif
+
/*
* FTRACE_OPS_CMD_* commands allow the ftrace core logic to request changes
* to a ftrace_ops. Note, the requests may fail.
diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig
index a856d4a34c67..5b1e7fa41ca8 100644
--- a/kernel/trace/Kconfig
+++ b/kernel/trace/Kconfig
@@ -257,7 +257,7 @@ config DYNAMIC_FTRACE_WITH_REGS

config DYNAMIC_FTRACE_WITH_DIRECT_CALLS
def_bool y
- depends on DYNAMIC_FTRACE_WITH_REGS
+ depends on DYNAMIC_FTRACE_WITH_REGS || DYNAMIC_FTRACE_WITH_ARGS
depends on HAVE_DYNAMIC_FTRACE_WITH_DIRECT_CALLS

config DYNAMIC_FTRACE_WITH_CALL_OPS
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index 66c91fa4b6ab..7a0c7eddf25b 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -5287,7 +5287,7 @@ static LIST_HEAD(ftrace_direct_funcs);

static int register_ftrace_function_nolock(struct ftrace_ops *ops);

-#define MULTI_FLAGS (FTRACE_OPS_FL_DIRECT | FTRACE_OPS_FL_SAVE_REGS)
+#define MULTI_FLAGS (FTRACE_OPS_FL_DIRECT | FTRACE_OPS_FL_SAVE_ARGS)

static int check_direct_multi(struct ftrace_ops *ops)
{
--
2.40.0.rc2.332.ga46443480c-goog


2023-03-16 17:39:09

by Florent Revest

[permalink] [raw]
Subject: [PATCH 7/7] ftrace: selftest: remove broken trace_direct_tramp

From: Mark Rutland <[email protected]>

The ftrace selftest code has a trace_direct_tramp() function which it
uses as a direct call trampoline. This happens to work on x86, since the
direct call's return address is in the usual place, and can be returned
to via a RET, but in general the calling convention for direct calls is
different from regular function calls, and requires a trampoline written
in assembly.

On s390, regular function calls place the return address in %r14, and an
ftrace patch-site in an instrumented function places the trampoline's
return address (which is within the instrumented function) in %r0,
preserving the original %r14 value in-place. As a regular C function
will return to the address in %r14, using a C function as the trampoline
results in the trampoline returning to the caller of the instrumented
function, skipping the body of the instrumented function.

Note that the s390 issue is not detcted by the ftrace selftest code, as
the instrumented function is trivial, and returning back into the caller
happens to be equivalent.

On arm64, regular function calls place the return address in x30, and
an ftrace patch-site in an instrumented function saves this into r9
and places the trampoline's return address (within the instrumented
function) in x30. A regular C function will return to the address in
x30, but will not restore x9 into x30. Consequently, using a C function
as the trampoline results in returning to the trampoline's return
address having corrupted x30, such that when the instrumented function
returns, it will return back into itself.

To avoid future issues in this area, remove the trace_direct_tramp()
function, and require that each architecture with direct calls provides
a stub trampoline, named ftrace_stub_direct_tramp. This can be written
to handle the architecture's trampoline calling convention, and in
future could be used elsewhere (e.g. in the ftrace ops sample, to
measure the overhead of direct calls), so we may as well always build it
in.

Signed-off-by: Mark Rutland <[email protected]>
Cc: Li Huafei <[email protected]>
Cc: Xu Kuohai <[email protected]>
Cc: Steven Rostedt (Google) <[email protected]>
Cc: Florent Revest <[email protected]>
Signed-off-by: Florent Revest <[email protected]>
---
arch/s390/kernel/mcount.S | 5 +++++
arch/x86/kernel/ftrace_32.S | 5 +++++
arch/x86/kernel/ftrace_64.S | 4 ++++
include/linux/ftrace.h | 2 ++
kernel/trace/trace_selftest.c | 12 ++----------
5 files changed, 18 insertions(+), 10 deletions(-)

diff --git a/arch/s390/kernel/mcount.S b/arch/s390/kernel/mcount.S
index 43ff91073d2a..6c10da43b538 100644
--- a/arch/s390/kernel/mcount.S
+++ b/arch/s390/kernel/mcount.S
@@ -32,6 +32,11 @@ ENTRY(ftrace_stub)
BR_EX %r14
ENDPROC(ftrace_stub)

+SYM_CODE_START(ftrace_stub_direct_tramp)
+ lgr %r1, %r0
+ BR_EX %r1
+SYM_CODE_END(ftrace_stub_direct_tramp)
+
.macro ftrace_regs_entry, allregs=0
stg %r14,(__SF_GPRS+8*8)(%r15) # save traced function caller

diff --git a/arch/x86/kernel/ftrace_32.S b/arch/x86/kernel/ftrace_32.S
index a0ed0e4a2c0c..0d9a14528176 100644
--- a/arch/x86/kernel/ftrace_32.S
+++ b/arch/x86/kernel/ftrace_32.S
@@ -163,6 +163,11 @@ SYM_INNER_LABEL(ftrace_regs_call, SYM_L_GLOBAL)
jmp .Lftrace_ret
SYM_CODE_END(ftrace_regs_caller)

+SYM_FUNC_START(ftrace_stub_direct_tramp)
+ CALL_DEPTH_ACCOUNT
+ RET
+SYM_FUNC_END(ftrace_stub_direct_tramp)
+
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
SYM_CODE_START(ftrace_graph_caller)
pushl %eax
diff --git a/arch/x86/kernel/ftrace_64.S b/arch/x86/kernel/ftrace_64.S
index 1265ad519249..8fc77e3e039c 100644
--- a/arch/x86/kernel/ftrace_64.S
+++ b/arch/x86/kernel/ftrace_64.S
@@ -307,6 +307,10 @@ SYM_INNER_LABEL(ftrace_regs_caller_end, SYM_L_GLOBAL)
SYM_FUNC_END(ftrace_regs_caller)
STACK_FRAME_NON_STANDARD_FP(ftrace_regs_caller)

+SYM_FUNC_START(ftrace_stub_direct_tramp)
+ CALL_DEPTH_ACCOUNT
+ RET
+SYM_FUNC_END(ftrace_stub_direct_tramp)

#else /* ! CONFIG_DYNAMIC_FTRACE */

diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h
index 31f1e1df2af3..931f3d904529 100644
--- a/include/linux/ftrace.h
+++ b/include/linux/ftrace.h
@@ -413,6 +413,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);

+void ftrace_stub_direct_tramp(void);
+
#else
struct ftrace_ops;
# define ftrace_direct_func_count 0
diff --git a/kernel/trace/trace_selftest.c b/kernel/trace/trace_selftest.c
index 84cd7ba31d27..a931d9aaea26 100644
--- a/kernel/trace/trace_selftest.c
+++ b/kernel/trace/trace_selftest.c
@@ -786,14 +786,6 @@ static struct fgraph_ops fgraph_ops __initdata = {

#ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS
static struct ftrace_ops direct;
-#ifndef CALL_DEPTH_ACCOUNT
-#define CALL_DEPTH_ACCOUNT ""
-#endif
-
-noinline __noclone static void trace_direct_tramp(void)
-{
- asm(CALL_DEPTH_ACCOUNT);
-}
#endif

/*
@@ -873,7 +865,7 @@ trace_selftest_startup_function_graph(struct tracer *trace,
*/
ftrace_set_filter_ip(&direct, (unsigned long)DYN_FTRACE_TEST_NAME, 0, 0);
ret = register_ftrace_direct(&direct,
- (unsigned long)trace_direct_tramp);
+ (unsigned long)ftrace_stub_direct_tramp);
if (ret)
goto out;

@@ -894,7 +886,7 @@ trace_selftest_startup_function_graph(struct tracer *trace,
unregister_ftrace_graph(&fgraph_ops);

ret = unregister_ftrace_direct(&direct,
- (unsigned long) trace_direct_tramp,
+ (unsigned long)ftrace_stub_direct_tramp,
true);
if (ret)
goto out;
--
2.40.0.rc2.332.ga46443480c-goog


2023-03-19 15:29:36

by Jiri Olsa

[permalink] [raw]
Subject: Re: [PATCH 5/7] ftrace: Store direct called addresses in their ops

On Thu, Mar 16, 2023 at 06:38:09PM +0100, Florent Revest wrote:
> All direct calls are now registered using the register_ftrace_direct API
> so each ops can jump to only one direct-called trampoline.
>
> By storing the direct called trampoline address directly in the ops we
> can save one hashmap lookup in the direct call ops and implement arm64
> direct calls on top of call ops.
>
> Signed-off-by: Florent Revest <[email protected]>
> ---
> include/linux/ftrace.h | 3 +++
> kernel/trace/ftrace.c | 7 +++++--
> 2 files changed, 8 insertions(+), 2 deletions(-)
>
> diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h
> index abee60865fc7..6a532dd6789e 100644
> --- a/include/linux/ftrace.h
> +++ b/include/linux/ftrace.h
> @@ -321,6 +321,9 @@ struct ftrace_ops {
> unsigned long trampoline_size;
> struct list_head list;
> ftrace_ops_func_t ops_func;
> +#ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS
> + unsigned long direct_call;
> +#endif
> #endif
> };
>
> diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
> index 33530198d1ca..66c91fa4b6ab 100644
> --- a/kernel/trace/ftrace.c
> +++ b/kernel/trace/ftrace.c
> @@ -2582,9 +2582,8 @@ ftrace_add_rec_direct(unsigned long ip, unsigned long addr,
> static void call_direct_funcs(unsigned long ip, unsigned long pip,
> struct ftrace_ops *ops, struct ftrace_regs *fregs)
> {
> - unsigned long addr;
> + unsigned long addr = ops->direct_call;

nice, should it be read with READ_ONCE ?

jirka

>
> - addr = ftrace_find_rec_direct(ip);
> if (!addr)
> return;
>
> @@ -5380,6 +5379,7 @@ int register_ftrace_direct(struct ftrace_ops *ops, unsigned long addr)
> ops->func = call_direct_funcs;
> ops->flags = MULTI_FLAGS;
> ops->trampoline = FTRACE_REGS_ADDR;
> + ops->direct_call = addr;
>
> err = register_ftrace_function_nolock(ops);
>
> @@ -5454,6 +5454,7 @@ __modify_ftrace_direct(struct ftrace_ops *ops, unsigned long addr)
> /* 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)
> @@ -5475,6 +5476,8 @@ __modify_ftrace_direct(struct ftrace_ops *ops, unsigned long addr)
> entry->direct = addr;
> }
> }
> + /* Prevent store tearing if a trampoline concurrently accesses the value */
> + WRITE_ONCE(ops->direct_call, addr);
>
> mutex_unlock(&ftrace_lock);
>
> --
> 2.40.0.rc2.332.ga46443480c-goog
>

2023-03-19 15:29:59

by Jiri Olsa

[permalink] [raw]
Subject: Re: [PATCH 4/7] ftrace: Rename _ftrace_direct_multi APIs to _ftrace_direct APIs

On Thu, Mar 16, 2023 at 06:38:08PM +0100, Florent Revest wrote:

SNIP

> diff --git a/samples/Kconfig b/samples/Kconfig
> index 30ef8bd48ba3..fd24daa99f34 100644
> --- a/samples/Kconfig
> +++ b/samples/Kconfig
> @@ -38,7 +38,7 @@ config SAMPLE_FTRACE_DIRECT
> that hooks to wake_up_process and prints the parameters.
>
> config SAMPLE_FTRACE_DIRECT_MULTI

nit, we could perhaps remove this config option as well
and use SAMPLE_FTRACE_DIRECT_MULTI

jirka

> - tristate "Build register_ftrace_direct_multi() example"
> + tristate "Build register_ftrace_direct() on multiple ips example"
> depends on DYNAMIC_FTRACE_WITH_DIRECT_CALLS && m
> depends on HAVE_SAMPLE_FTRACE_DIRECT_MULTI
> help

2023-03-19 17:54:59

by Steven Rostedt

[permalink] [raw]
Subject: Re: [PATCH 5/7] ftrace: Store direct called addresses in their ops

On Sun, 19 Mar 2023 16:29:22 +0100
Jiri Olsa <[email protected]> wrote:

> > +++ b/kernel/trace/ftrace.c
> > @@ -2582,9 +2582,8 @@ ftrace_add_rec_direct(unsigned long ip, unsigned long addr,
> > static void call_direct_funcs(unsigned long ip, unsigned long pip,
> > struct ftrace_ops *ops, struct ftrace_regs *fregs)
> > {
> > - unsigned long addr;
> > + unsigned long addr = ops->direct_call;
>
> nice, should it be read with READ_ONCE ?

Is there a "read tearing" too?

-- Steve

>
> jirka
>
> >
> > - addr = ftrace_find_rec_direct(ip);
> > if (!addr)
> > return;
> >
> > @@ -5380,6 +5379,7 @@ int register_ftrace_direct(struct ftrace_ops *ops, unsigned long addr)
> > ops->func = call_direct_funcs;
> > ops->flags = MULTI_FLAGS;
> > ops->trampoline = FTRACE_REGS_ADDR;
> > + ops->direct_call = addr;
> >
> > err = register_ftrace_function_nolock(ops);
> >
> > @@ -5454,6 +5454,7 @@ __modify_ftrace_direct(struct ftrace_ops *ops, unsigned long addr)
> > /* 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)
> > @@ -5475,6 +5476,8 @@ __modify_ftrace_direct(struct ftrace_ops *ops, unsigned long addr)
> > entry->direct = addr;
> > }
> > }
> > + /* Prevent store tearing if a trampoline concurrently accesses the value */
> > + WRITE_ONCE(ops->direct_call, addr);
> >
> > mutex_unlock(&ftrace_lock);

2023-03-19 17:55:58

by Steven Rostedt

[permalink] [raw]
Subject: Re: [PATCH 4/7] ftrace: Rename _ftrace_direct_multi APIs to _ftrace_direct APIs

On Sun, 19 Mar 2023 16:29:30 +0100
Jiri Olsa <[email protected]> wrote:

> On Thu, Mar 16, 2023 at 06:38:08PM +0100, Florent Revest wrote:
>
> SNIP
>
> > diff --git a/samples/Kconfig b/samples/Kconfig
> > index 30ef8bd48ba3..fd24daa99f34 100644
> > --- a/samples/Kconfig
> > +++ b/samples/Kconfig
> > @@ -38,7 +38,7 @@ config SAMPLE_FTRACE_DIRECT
> > that hooks to wake_up_process and prints the parameters.
> >
> > config SAMPLE_FTRACE_DIRECT_MULTI
>
> nit, we could perhaps remove this config option as well
> and use SAMPLE_FTRACE_DIRECT_MULTI

Remove SAMPLE_FTRACE_DIRECT_MULTI for SAMPLE_FTRACE_DIRECT_MULTI?

-- Steve

>
> jirka
>
> > - tristate "Build register_ftrace_direct_multi() example"
> > + tristate "Build register_ftrace_direct() on multiple ips example"
> > depends on DYNAMIC_FTRACE_WITH_DIRECT_CALLS && m
> > depends on HAVE_SAMPLE_FTRACE_DIRECT_MULTI
> > help


2023-03-19 18:23:09

by Jiri Olsa

[permalink] [raw]
Subject: Re: [PATCH 4/7] ftrace: Rename _ftrace_direct_multi APIs to _ftrace_direct APIs

On Sun, Mar 19, 2023 at 01:55:50PM -0400, Steven Rostedt wrote:
> On Sun, 19 Mar 2023 16:29:30 +0100
> Jiri Olsa <[email protected]> wrote:
>
> > On Thu, Mar 16, 2023 at 06:38:08PM +0100, Florent Revest wrote:
> >
> > SNIP
> >
> > > diff --git a/samples/Kconfig b/samples/Kconfig
> > > index 30ef8bd48ba3..fd24daa99f34 100644
> > > --- a/samples/Kconfig
> > > +++ b/samples/Kconfig
> > > @@ -38,7 +38,7 @@ config SAMPLE_FTRACE_DIRECT
> > > that hooks to wake_up_process and prints the parameters.
> > >
> > > config SAMPLE_FTRACE_DIRECT_MULTI
> >
> > nit, we could perhaps remove this config option as well
> > and use SAMPLE_FTRACE_DIRECT_MULTI
>
> Remove SAMPLE_FTRACE_DIRECT_MULTI for SAMPLE_FTRACE_DIRECT_MULTI?
>

sorry typo, I meant SAMPLE_FTRACE_DIRECT

jirka

> -- Steve
>
> >
> > jirka
> >
> > > - tristate "Build register_ftrace_direct_multi() example"
> > > + tristate "Build register_ftrace_direct() on multiple ips example"
> > > depends on DYNAMIC_FTRACE_WITH_DIRECT_CALLS && m
> > > depends on HAVE_SAMPLE_FTRACE_DIRECT_MULTI
> > > help
>

2023-03-19 18:55:19

by Jiri Olsa

[permalink] [raw]
Subject: Re: [PATCH 5/7] ftrace: Store direct called addresses in their ops

On Sun, Mar 19, 2023 at 01:54:43PM -0400, Steven Rostedt wrote:
> On Sun, 19 Mar 2023 16:29:22 +0100
> Jiri Olsa <[email protected]> wrote:
>
> > > +++ b/kernel/trace/ftrace.c
> > > @@ -2582,9 +2582,8 @@ ftrace_add_rec_direct(unsigned long ip, unsigned long addr,
> > > static void call_direct_funcs(unsigned long ip, unsigned long pip,
> > > struct ftrace_ops *ops, struct ftrace_regs *fregs)
> > > {
> > > - unsigned long addr;
> > > + unsigned long addr = ops->direct_call;
> >
> > nice, should it be read with READ_ONCE ?
>
> Is there a "read tearing" too?

don't know, saw the comment in __modify_ftrace_direct and got curious
why it's not in here.. feel free to ignore, I'll look it up

jirka

>
> -- Steve
>
> >
> > jirka
> >
> > >
> > > - addr = ftrace_find_rec_direct(ip);
> > > if (!addr)
> > > return;
> > >
> > > @@ -5380,6 +5379,7 @@ int register_ftrace_direct(struct ftrace_ops *ops, unsigned long addr)
> > > ops->func = call_direct_funcs;
> > > ops->flags = MULTI_FLAGS;
> > > ops->trampoline = FTRACE_REGS_ADDR;
> > > + ops->direct_call = addr;
> > >
> > > err = register_ftrace_function_nolock(ops);
> > >
> > > @@ -5454,6 +5454,7 @@ __modify_ftrace_direct(struct ftrace_ops *ops, unsigned long addr)
> > > /* 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)
> > > @@ -5475,6 +5476,8 @@ __modify_ftrace_direct(struct ftrace_ops *ops, unsigned long addr)
> > > entry->direct = addr;
> > > }
> > > }
> > > + /* Prevent store tearing if a trampoline concurrently accesses the value */
> > > + WRITE_ONCE(ops->direct_call, addr);
> > >
> > > mutex_unlock(&ftrace_lock);

2023-03-20 00:47:46

by Steven Rostedt

[permalink] [raw]
Subject: Re: [PATCH 4/7] ftrace: Rename _ftrace_direct_multi APIs to _ftrace_direct APIs

On Sun, 19 Mar 2023 19:22:59 +0100
Jiri Olsa <[email protected]> wrote:

> > > > config SAMPLE_FTRACE_DIRECT_MULTI
> > >
> > > nit, we could perhaps remove this config option as well
> > > and use SAMPLE_FTRACE_DIRECT_MULTI
> >
> > Remove SAMPLE_FTRACE_DIRECT_MULTI for SAMPLE_FTRACE_DIRECT_MULTI?
> >
>
> sorry typo, I meant SAMPLE_FTRACE_DIRECT

I believe this was discussed before, and I thought we decided to keep
them separate. Or perhaps that was at least for testing?

Anyway, we could merge this in the future, but I don't think that's
necessary now.

-- Steve

2023-03-20 08:05:49

by Jiri Olsa

[permalink] [raw]
Subject: Re: [PATCH 4/7] ftrace: Rename _ftrace_direct_multi APIs to _ftrace_direct APIs

On Sun, Mar 19, 2023 at 08:47:31PM -0400, Steven Rostedt wrote:
> On Sun, 19 Mar 2023 19:22:59 +0100
> Jiri Olsa <[email protected]> wrote:
>
> > > > > config SAMPLE_FTRACE_DIRECT_MULTI
> > > >
> > > > nit, we could perhaps remove this config option as well
> > > > and use SAMPLE_FTRACE_DIRECT_MULTI
> > >
> > > Remove SAMPLE_FTRACE_DIRECT_MULTI for SAMPLE_FTRACE_DIRECT_MULTI?
> > >
> >
> > sorry typo, I meant SAMPLE_FTRACE_DIRECT
>
> I believe this was discussed before, and I thought we decided to keep
> them separate. Or perhaps that was at least for testing?
>
> Anyway, we could merge this in the future, but I don't think that's
> necessary now.

yes, I said it's nit earlier

jirka

2023-03-20 17:46:45

by Florent Revest

[permalink] [raw]
Subject: Re: [PATCH 4/7] ftrace: Rename _ftrace_direct_multi APIs to _ftrace_direct APIs

On Mon, Mar 20, 2023 at 9:05 AM Jiri Olsa <[email protected]> wrote:
>
> On Sun, Mar 19, 2023 at 08:47:31PM -0400, Steven Rostedt wrote:
> > On Sun, 19 Mar 2023 19:22:59 +0100
> > Jiri Olsa <[email protected]> wrote:
> >
> > > > > > config SAMPLE_FTRACE_DIRECT_MULTI
> > > > >
> > > > > nit, we could perhaps remove this config option as well
> > > > > and use SAMPLE_FTRACE_DIRECT_MULTI
> > > >
> > > > Remove SAMPLE_FTRACE_DIRECT_MULTI for SAMPLE_FTRACE_DIRECT_MULTI?
> > > >
> > >
> > > sorry typo, I meant SAMPLE_FTRACE_DIRECT
> >
> > I believe this was discussed before, and I thought we decided to keep
> > them separate. Or perhaps that was at least for testing?
> >
> > Anyway, we could merge this in the future, but I don't think that's
> > necessary now.
>
> yes, I said it's nit earlier

Happy to send a follow up cleanup later on if that helps. :)

2023-03-20 17:51:25

by Florent Revest

[permalink] [raw]
Subject: Re: [PATCH 5/7] ftrace: Store direct called addresses in their ops

On Sun, Mar 19, 2023 at 7:55 PM Jiri Olsa <[email protected]> wrote:
>
> On Sun, Mar 19, 2023 at 01:54:43PM -0400, Steven Rostedt wrote:
> > On Sun, 19 Mar 2023 16:29:22 +0100
> > Jiri Olsa <[email protected]> wrote:
> >
> > > > +++ b/kernel/trace/ftrace.c
> > > > @@ -2582,9 +2582,8 @@ ftrace_add_rec_direct(unsigned long ip, unsigned long addr,
> > > > static void call_direct_funcs(unsigned long ip, unsigned long pip,
> > > > struct ftrace_ops *ops, struct ftrace_regs *fregs)
> > > > {
> > > > - unsigned long addr;
> > > > + unsigned long addr = ops->direct_call;
> > >
> > > nice, should it be read with READ_ONCE ?
> >
> > Is there a "read tearing" too?
>
> don't know, saw the comment in __modify_ftrace_direct and got curious
> why it's not in here.. feel free to ignore, I'll look it up
>
> jirka

Mhh, that's a good question. Based on my current understanding, it
seems that it should have a READ_ONCE, indeed. However, I'd like Mark
to confirm/deny this. :)

If this should be a READ_ONCE, I can send a v2 series with this fixed.

2023-03-20 21:33:12

by Steven Rostedt

[permalink] [raw]
Subject: Re: [PATCH 5/7] ftrace: Store direct called addresses in their ops

On Mon, 20 Mar 2023 18:45:08 +0100
Florent Revest <[email protected]> wrote:

> On Sun, Mar 19, 2023 at 7:55 PM Jiri Olsa <[email protected]> wrote:
> >
> > On Sun, Mar 19, 2023 at 01:54:43PM -0400, Steven Rostedt wrote:
> > > On Sun, 19 Mar 2023 16:29:22 +0100
> > > Jiri Olsa <[email protected]> wrote:
> > >
> > > > > +++ b/kernel/trace/ftrace.c
> > > > > @@ -2582,9 +2582,8 @@ ftrace_add_rec_direct(unsigned long ip, unsigned long addr,
> > > > > static void call_direct_funcs(unsigned long ip, unsigned long pip,
> > > > > struct ftrace_ops *ops, struct ftrace_regs *fregs)
> > > > > {
> > > > > - unsigned long addr;
> > > > > + unsigned long addr = ops->direct_call;
> > > >
> > > > nice, should it be read with READ_ONCE ?
> > >
> > > Is there a "read tearing" too?
> >
> > don't know, saw the comment in __modify_ftrace_direct and got curious
> > why it's not in here.. feel free to ignore, I'll look it up
> >
> > jirka
>
> Mhh, that's a good question. Based on my current understanding, it
> seems that it should have a READ_ONCE, indeed. However, I'd like Mark
> to confirm/deny this. :)
>
> If this should be a READ_ONCE, I can send a v2 series with this fixed.

After re-reading: https://lwn.net/Articles/793253/

I think we should add the READ_ONCE() (also with a comment).

-- Steve

2023-03-21 09:47:29

by Mark Rutland

[permalink] [raw]
Subject: Re: [PATCH 1/7] ftrace: Let unregister_ftrace_direct_multi() call ftrace_free_filter()

On Thu, Mar 16, 2023 at 06:38:05PM +0100, Florent Revest wrote:
> A common pattern when using the ftrace_direct_multi API is to unregister
> the ops and also immediately free its filter. We've noticed it's very
> easy for users to miss calling ftrace_free_filter().
>
> This adds a "free_filters" argument to unregister_ftrace_direct_multi()
> to both remind the user they should free filters and also to make their
> life easier.
>
> Suggested-by: Steven Rostedt <[email protected]>
> Signed-off-by: Florent Revest <[email protected]>
> ---
> include/linux/ftrace.h | 6 ++++--
> kernel/bpf/trampoline.c | 2 +-
> kernel/trace/ftrace.c | 6 +++++-
> samples/ftrace/ftrace-direct-multi-modify.c | 3 +--
> samples/ftrace/ftrace-direct-multi.c | 3 +--
> 5 files changed, 12 insertions(+), 8 deletions(-)

This looks good to me; I see that the BPF code frees the filter in
bpf_trampoline_put(), so it not doing so via unregister_ftrace_direct_multi()
looks fine. FWIW:

Acked-by: Mark Rutland <[email protected]>

Mark.

>
> diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h
> index 366c730beaa3..5b68ee874bc1 100644
> --- a/include/linux/ftrace.h
> +++ b/include/linux/ftrace.h
> @@ -407,7 +407,8 @@ int ftrace_modify_direct_caller(struct ftrace_func_entry *entry,
> unsigned long new_addr);
> unsigned long ftrace_find_rec_direct(unsigned long ip);
> int register_ftrace_direct_multi(struct ftrace_ops *ops, unsigned long addr);
> -int unregister_ftrace_direct_multi(struct ftrace_ops *ops, unsigned long addr);
> +int unregister_ftrace_direct_multi(struct ftrace_ops *ops, unsigned long addr,
> + bool free_filters);
> int modify_ftrace_direct_multi(struct ftrace_ops *ops, unsigned long addr);
> int modify_ftrace_direct_multi_nolock(struct ftrace_ops *ops, unsigned long addr);
>
> @@ -446,7 +447,8 @@ static inline int register_ftrace_direct_multi(struct ftrace_ops *ops, unsigned
> {
> return -ENODEV;
> }
> -static inline int unregister_ftrace_direct_multi(struct ftrace_ops *ops, unsigned long addr)
> +static inline int unregister_ftrace_direct_multi(struct ftrace_ops *ops, unsigned long addr,
> + bool free_filters)
> {
> return -ENODEV;
> }
> diff --git a/kernel/bpf/trampoline.c b/kernel/bpf/trampoline.c
> index d0ed7d6f5eec..88bc23f1e10a 100644
> --- a/kernel/bpf/trampoline.c
> +++ b/kernel/bpf/trampoline.c
> @@ -198,7 +198,7 @@ static int unregister_fentry(struct bpf_trampoline *tr, void *old_addr)
> int ret;
>
> if (tr->func.ftrace_managed)
> - ret = unregister_ftrace_direct_multi(tr->fops, (long)old_addr);
> + ret = unregister_ftrace_direct_multi(tr->fops, (long)old_addr, false);
> else
> ret = bpf_arch_text_poke(ip, BPF_MOD_CALL, old_addr, NULL);
>
> diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
> index 29baa97d0d53..fa379cf91fdb 100644
> --- a/kernel/trace/ftrace.c
> +++ b/kernel/trace/ftrace.c
> @@ -5804,7 +5804,8 @@ EXPORT_SYMBOL_GPL(register_ftrace_direct_multi);
> * 0 on success
> * -EINVAL - The @ops object was not properly registered.
> */
> -int unregister_ftrace_direct_multi(struct ftrace_ops *ops, unsigned long addr)
> +int unregister_ftrace_direct_multi(struct ftrace_ops *ops, unsigned long addr,
> + bool free_filters)
> {
> struct ftrace_hash *hash = ops->func_hash->filter_hash;
> int err;
> @@ -5822,6 +5823,9 @@ int unregister_ftrace_direct_multi(struct ftrace_ops *ops, unsigned long addr)
> /* cleanup for possible another register call */
> ops->func = NULL;
> ops->trampoline = 0;
> +
> + if (free_filters)
> + ftrace_free_filter(ops);
> return err;
> }
> EXPORT_SYMBOL_GPL(unregister_ftrace_direct_multi);
> diff --git a/samples/ftrace/ftrace-direct-multi-modify.c b/samples/ftrace/ftrace-direct-multi-modify.c
> index b58c594efb51..196b43971cb5 100644
> --- a/samples/ftrace/ftrace-direct-multi-modify.c
> +++ b/samples/ftrace/ftrace-direct-multi-modify.c
> @@ -151,8 +151,7 @@ static int __init ftrace_direct_multi_init(void)
> static void __exit ftrace_direct_multi_exit(void)
> {
> kthread_stop(simple_tsk);
> - unregister_ftrace_direct_multi(&direct, my_tramp);
> - ftrace_free_filter(&direct);
> + unregister_ftrace_direct_multi(&direct, my_tramp, true);
> }
>
> module_init(ftrace_direct_multi_init);
> diff --git a/samples/ftrace/ftrace-direct-multi.c b/samples/ftrace/ftrace-direct-multi.c
> index c27cf130c319..ea0e88ee5e43 100644
> --- a/samples/ftrace/ftrace-direct-multi.c
> +++ b/samples/ftrace/ftrace-direct-multi.c
> @@ -78,8 +78,7 @@ static int __init ftrace_direct_multi_init(void)
>
> static void __exit ftrace_direct_multi_exit(void)
> {
> - unregister_ftrace_direct_multi(&direct, (unsigned long) my_tramp);
> - ftrace_free_filter(&direct);
> + unregister_ftrace_direct_multi(&direct, (unsigned long) my_tramp, true);
> }
>
> module_init(ftrace_direct_multi_init);
> --
> 2.40.0.rc2.332.ga46443480c-goog
>

2023-03-21 10:20:36

by Mark Rutland

[permalink] [raw]
Subject: Re: [PATCH 5/7] ftrace: Store direct called addresses in their ops

On Mon, Mar 20, 2023 at 05:31:55PM -0400, Steven Rostedt wrote:
> On Mon, 20 Mar 2023 18:45:08 +0100
> Florent Revest <[email protected]> wrote:
>
> > On Sun, Mar 19, 2023 at 7:55 PM Jiri Olsa <[email protected]> wrote:
> > >
> > > On Sun, Mar 19, 2023 at 01:54:43PM -0400, Steven Rostedt wrote:
> > > > On Sun, 19 Mar 2023 16:29:22 +0100
> > > > Jiri Olsa <[email protected]> wrote:
> > > >
> > > > > > +++ b/kernel/trace/ftrace.c
> > > > > > @@ -2582,9 +2582,8 @@ ftrace_add_rec_direct(unsigned long ip, unsigned long addr,
> > > > > > static void call_direct_funcs(unsigned long ip, unsigned long pip,
> > > > > > struct ftrace_ops *ops, struct ftrace_regs *fregs)
> > > > > > {
> > > > > > - unsigned long addr;
> > > > > > + unsigned long addr = ops->direct_call;
> > > > >
> > > > > nice, should it be read with READ_ONCE ?
> > > >
> > > > Is there a "read tearing" too?
> > >
> > > don't know, saw the comment in __modify_ftrace_direct and got curious
> > > why it's not in here.. feel free to ignore, I'll look it up
> > >
> > > jirka
> >
> > Mhh, that's a good question. Based on my current understanding, it
> > seems that it should have a READ_ONCE, indeed. However, I'd like Mark
> > to confirm/deny this. :)
> >
> > If this should be a READ_ONCE, I can send a v2 series with this fixed.
>
> After re-reading: https://lwn.net/Articles/793253/
>
> I think we should add the READ_ONCE() (also with a comment).

I think so, too.

AFAICT there's nothing that prevents __modify_ftrace_direct() and
call_direct_funcs() from concurrently accessing ftrace_ops::direct_call, so we
need READ_ONCE() in call_direct_funcs() to prevent load tearing and other
issues mentioned in the article linked above.

The existing code has a similar pattern where __modify_ftrace_direct() and
ftrace_find_rec_direct() access ftrace_func_entry::direct concurrently. Do we
want a preparatory patch fixing that for stable?

Thanks,
Mark.

2023-03-21 10:40:43

by Jiri Olsa

[permalink] [raw]
Subject: Re: [PATCH 1/7] ftrace: Let unregister_ftrace_direct_multi() call ftrace_free_filter()

On Tue, Mar 21, 2023 at 09:47:17AM +0000, Mark Rutland wrote:
> On Thu, Mar 16, 2023 at 06:38:05PM +0100, Florent Revest wrote:
> > A common pattern when using the ftrace_direct_multi API is to unregister
> > the ops and also immediately free its filter. We've noticed it's very
> > easy for users to miss calling ftrace_free_filter().
> >
> > This adds a "free_filters" argument to unregister_ftrace_direct_multi()
> > to both remind the user they should free filters and also to make their
> > life easier.
> >
> > Suggested-by: Steven Rostedt <[email protected]>
> > Signed-off-by: Florent Revest <[email protected]>
> > ---
> > include/linux/ftrace.h | 6 ++++--
> > kernel/bpf/trampoline.c | 2 +-
> > kernel/trace/ftrace.c | 6 +++++-
> > samples/ftrace/ftrace-direct-multi-modify.c | 3 +--
> > samples/ftrace/ftrace-direct-multi.c | 3 +--
> > 5 files changed, 12 insertions(+), 8 deletions(-)
>
> This looks good to me; I see that the BPF code frees the filter in
> bpf_trampoline_put(), so it not doing so via unregister_ftrace_direct_multi()
> looks fine. FWIW:
>
> Acked-by: Mark Rutland <[email protected]>

yes, I was going to ack the next version ;-)

thanks,
jirka

>
> Mark.
>
> >
> > diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h
> > index 366c730beaa3..5b68ee874bc1 100644
> > --- a/include/linux/ftrace.h
> > +++ b/include/linux/ftrace.h
> > @@ -407,7 +407,8 @@ int ftrace_modify_direct_caller(struct ftrace_func_entry *entry,
> > unsigned long new_addr);
> > unsigned long ftrace_find_rec_direct(unsigned long ip);
> > int register_ftrace_direct_multi(struct ftrace_ops *ops, unsigned long addr);
> > -int unregister_ftrace_direct_multi(struct ftrace_ops *ops, unsigned long addr);
> > +int unregister_ftrace_direct_multi(struct ftrace_ops *ops, unsigned long addr,
> > + bool free_filters);
> > int modify_ftrace_direct_multi(struct ftrace_ops *ops, unsigned long addr);
> > int modify_ftrace_direct_multi_nolock(struct ftrace_ops *ops, unsigned long addr);
> >
> > @@ -446,7 +447,8 @@ static inline int register_ftrace_direct_multi(struct ftrace_ops *ops, unsigned
> > {
> > return -ENODEV;
> > }
> > -static inline int unregister_ftrace_direct_multi(struct ftrace_ops *ops, unsigned long addr)
> > +static inline int unregister_ftrace_direct_multi(struct ftrace_ops *ops, unsigned long addr,
> > + bool free_filters)
> > {
> > return -ENODEV;
> > }
> > diff --git a/kernel/bpf/trampoline.c b/kernel/bpf/trampoline.c
> > index d0ed7d6f5eec..88bc23f1e10a 100644
> > --- a/kernel/bpf/trampoline.c
> > +++ b/kernel/bpf/trampoline.c
> > @@ -198,7 +198,7 @@ static int unregister_fentry(struct bpf_trampoline *tr, void *old_addr)
> > int ret;
> >
> > if (tr->func.ftrace_managed)
> > - ret = unregister_ftrace_direct_multi(tr->fops, (long)old_addr);
> > + ret = unregister_ftrace_direct_multi(tr->fops, (long)old_addr, false);
> > else
> > ret = bpf_arch_text_poke(ip, BPF_MOD_CALL, old_addr, NULL);
> >
> > diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
> > index 29baa97d0d53..fa379cf91fdb 100644
> > --- a/kernel/trace/ftrace.c
> > +++ b/kernel/trace/ftrace.c
> > @@ -5804,7 +5804,8 @@ EXPORT_SYMBOL_GPL(register_ftrace_direct_multi);
> > * 0 on success
> > * -EINVAL - The @ops object was not properly registered.
> > */
> > -int unregister_ftrace_direct_multi(struct ftrace_ops *ops, unsigned long addr)
> > +int unregister_ftrace_direct_multi(struct ftrace_ops *ops, unsigned long addr,
> > + bool free_filters)
> > {
> > struct ftrace_hash *hash = ops->func_hash->filter_hash;
> > int err;
> > @@ -5822,6 +5823,9 @@ int unregister_ftrace_direct_multi(struct ftrace_ops *ops, unsigned long addr)
> > /* cleanup for possible another register call */
> > ops->func = NULL;
> > ops->trampoline = 0;
> > +
> > + if (free_filters)
> > + ftrace_free_filter(ops);
> > return err;
> > }
> > EXPORT_SYMBOL_GPL(unregister_ftrace_direct_multi);
> > diff --git a/samples/ftrace/ftrace-direct-multi-modify.c b/samples/ftrace/ftrace-direct-multi-modify.c
> > index b58c594efb51..196b43971cb5 100644
> > --- a/samples/ftrace/ftrace-direct-multi-modify.c
> > +++ b/samples/ftrace/ftrace-direct-multi-modify.c
> > @@ -151,8 +151,7 @@ static int __init ftrace_direct_multi_init(void)
> > static void __exit ftrace_direct_multi_exit(void)
> > {
> > kthread_stop(simple_tsk);
> > - unregister_ftrace_direct_multi(&direct, my_tramp);
> > - ftrace_free_filter(&direct);
> > + unregister_ftrace_direct_multi(&direct, my_tramp, true);
> > }
> >
> > module_init(ftrace_direct_multi_init);
> > diff --git a/samples/ftrace/ftrace-direct-multi.c b/samples/ftrace/ftrace-direct-multi.c
> > index c27cf130c319..ea0e88ee5e43 100644
> > --- a/samples/ftrace/ftrace-direct-multi.c
> > +++ b/samples/ftrace/ftrace-direct-multi.c
> > @@ -78,8 +78,7 @@ static int __init ftrace_direct_multi_init(void)
> >
> > static void __exit ftrace_direct_multi_exit(void)
> > {
> > - unregister_ftrace_direct_multi(&direct, (unsigned long) my_tramp);
> > - ftrace_free_filter(&direct);
> > + unregister_ftrace_direct_multi(&direct, (unsigned long) my_tramp, true);
> > }
> >
> > module_init(ftrace_direct_multi_init);
> > --
> > 2.40.0.rc2.332.ga46443480c-goog
> >