ftrace: Introduce FTRACE_OPS_FL_JMP

JIRA: https://issues.redhat.com/browse/RHEL-139985

Conflicts: Context change from missing following commits:
- 78eb4ea25c ("sysctl: treewide: constify the ctl_table argument of
proc_handlers")
- 4346ba160409 ("fprobe: Rewrite fprobe on function-graph tracer")
- 4d6d0a6263ba ("tracing: Remove redundant config
HAVE_FTRACE_MCOUNT_RECORD")
- 88cefd99ee26 ("ftrace: Show subops in enabled_functions")

commit 25e4e3565d45f567f78089f38822fa64abee5230
Author: Menglong Dong <menglong8.dong@gmail.com>
Date:   Tue Nov 18 20:36:29 2025 +0800

    ftrace: Introduce FTRACE_OPS_FL_JMP

    For now, the "nop" will be replaced with a "call" instruction when a
    function is hooked by the ftrace. However, sometimes the "call" can break
    the RSB and introduce extra overhead. Therefore, introduce the flag
    FTRACE_OPS_FL_JMP, which indicate that the ftrace_ops should be called
    with a "jmp" instead of "call". For now, it is only used by the direct
    call case.

    When a direct ftrace_ops is marked with FTRACE_OPS_FL_JMP, the last bit of
    the ops->direct_call will be set to 1. Therefore, we can tell if we should
    use "jmp" for the callback in ftrace_call_replace().

    Signed-off-by: Menglong Dong <dongml2@chinatelecom.cn>
    Acked-by: Steven Rostedt (Google) <rostedt@goodmis.org>
    Link: https://lore.kernel.org/r/20251118123639.688444-2-dongml2@chinatelecom.cn
    Signed-off-by: Alexei Starovoitov <ast@kernel.org>

Signed-off-by: Jerome Marchand <jmarchan@redhat.com>
This commit is contained in:
Jerome Marchand
2026-05-07 17:58:09 +02:00
parent 50e257c8fc
commit 183875897d
3 changed files with 61 additions and 1 deletions
+33
View File
@@ -334,6 +334,7 @@ enum {
FTRACE_OPS_FL_PERMANENT = BIT(16),
FTRACE_OPS_FL_DIRECT = BIT(17),
FTRACE_OPS_FL_SUBOP = BIT(18),
FTRACE_OPS_FL_JMP = BIT(20),
};
#ifndef CONFIG_DYNAMIC_FTRACE_WITH_ARGS
@@ -552,6 +553,38 @@ static inline void arch_ftrace_set_direct_caller(struct ftrace_regs *fregs,
unsigned long addr) { }
#endif /* CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS */
#ifdef CONFIG_DYNAMIC_FTRACE_WITH_JMP
static inline bool ftrace_is_jmp(unsigned long addr)
{
return addr & 1;
}
static inline unsigned long ftrace_jmp_set(unsigned long addr)
{
return addr | 1UL;
}
static inline unsigned long ftrace_jmp_get(unsigned long addr)
{
return addr & ~1UL;
}
#else
static inline bool ftrace_is_jmp(unsigned long addr)
{
return false;
}
static inline unsigned long ftrace_jmp_set(unsigned long addr)
{
return addr;
}
static inline unsigned long ftrace_jmp_get(unsigned long addr)
{
return addr;
}
#endif /* CONFIG_DYNAMIC_FTRACE_WITH_JMP */
#ifdef CONFIG_STACK_TRACER
extern int stack_tracer_enabled;
+12
View File
@@ -63,6 +63,12 @@ config HAVE_DYNAMIC_FTRACE_NO_PATCHABLE
If the architecture generates __patchable_function_entries sections
but does not want them included in the ftrace locations.
config HAVE_DYNAMIC_FTRACE_WITH_JMP
bool
help
If the architecture supports to replace the __fentry__ with a
"jmp" instruction.
config HAVE_FTRACE_MCOUNT_RECORD
bool
help
@@ -284,6 +290,12 @@ config DYNAMIC_FTRACE_WITH_ARGS
depends on DYNAMIC_FTRACE
depends on HAVE_DYNAMIC_FTRACE_WITH_ARGS
config DYNAMIC_FTRACE_WITH_JMP
def_bool y
depends on DYNAMIC_FTRACE
depends on DYNAMIC_FTRACE_WITH_DIRECT_CALLS
depends on HAVE_DYNAMIC_FTRACE_WITH_JMP
config FPROBE
bool "Kernel Function Probe (fprobe)"
depends on FUNCTION_TRACER
+16 -1
View File
@@ -5892,7 +5892,8 @@ static void remove_direct_functions_hash(struct ftrace_hash *hash, unsigned long
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) {
if (del && ftrace_jmp_get(del->direct) ==
ftrace_jmp_get(addr)) {
remove_hash_entry(direct_functions, del);
kfree(del);
}
@@ -5957,8 +5958,15 @@ int register_ftrace_direct(struct ftrace_ops *ops, unsigned long addr)
if (ftrace_hash_empty(hash))
return -EINVAL;
/* This is a "raw" address, and this should never happen. */
if (WARN_ON_ONCE(ftrace_is_jmp(addr)))
return -EINVAL;
mutex_lock(&direct_mutex);
if (ops->flags & FTRACE_OPS_FL_JMP)
addr = ftrace_jmp_set(addr);
/* Make sure requested entries are not already registered.. */
size = 1 << hash->size_bits;
for (i = 0; i < size; i++) {
@@ -6079,6 +6087,13 @@ __modify_ftrace_direct(struct ftrace_ops *ops, unsigned long addr)
lockdep_assert_held_once(&direct_mutex);
/* This is a "raw" address, and this should never happen. */
if (WARN_ON_ONCE(ftrace_is_jmp(addr)))
return -EINVAL;
if (ops->flags & FTRACE_OPS_FL_JMP)
addr = ftrace_jmp_set(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;