Skip to content

Commit 25e4e35

Browse files
image-dragonAlexei Starovoitov
authored andcommitted
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 <[email protected]> Acked-by: Steven Rostedt (Google) <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Alexei Starovoitov <[email protected]>
1 parent fad8040 commit 25e4e35

File tree

3 files changed

+61
-1
lines changed

3 files changed

+61
-1
lines changed

include/linux/ftrace.h

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,7 @@ enum {
359359
FTRACE_OPS_FL_DIRECT = BIT(17),
360360
FTRACE_OPS_FL_SUBOP = BIT(18),
361361
FTRACE_OPS_FL_GRAPH = BIT(19),
362+
FTRACE_OPS_FL_JMP = BIT(20),
362363
};
363364

364365
#ifndef CONFIG_DYNAMIC_FTRACE_WITH_ARGS
@@ -577,6 +578,38 @@ static inline void arch_ftrace_set_direct_caller(struct ftrace_regs *fregs,
577578
unsigned long addr) { }
578579
#endif /* CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS */
579580

581+
#ifdef CONFIG_DYNAMIC_FTRACE_WITH_JMP
582+
static inline bool ftrace_is_jmp(unsigned long addr)
583+
{
584+
return addr & 1;
585+
}
586+
587+
static inline unsigned long ftrace_jmp_set(unsigned long addr)
588+
{
589+
return addr | 1UL;
590+
}
591+
592+
static inline unsigned long ftrace_jmp_get(unsigned long addr)
593+
{
594+
return addr & ~1UL;
595+
}
596+
#else
597+
static inline bool ftrace_is_jmp(unsigned long addr)
598+
{
599+
return false;
600+
}
601+
602+
static inline unsigned long ftrace_jmp_set(unsigned long addr)
603+
{
604+
return addr;
605+
}
606+
607+
static inline unsigned long ftrace_jmp_get(unsigned long addr)
608+
{
609+
return addr;
610+
}
611+
#endif /* CONFIG_DYNAMIC_FTRACE_WITH_JMP */
612+
580613
#ifdef CONFIG_STACK_TRACER
581614

582615
int stack_trace_sysctl(const struct ctl_table *table, int write, void *buffer,

kernel/trace/Kconfig

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,12 @@ config HAVE_DYNAMIC_FTRACE_NO_PATCHABLE
8080
If the architecture generates __patchable_function_entries sections
8181
but does not want them included in the ftrace locations.
8282

83+
config HAVE_DYNAMIC_FTRACE_WITH_JMP
84+
bool
85+
help
86+
If the architecture supports to replace the __fentry__ with a
87+
"jmp" instruction.
88+
8389
config HAVE_SYSCALL_TRACEPOINTS
8490
bool
8591
help
@@ -330,6 +336,12 @@ config DYNAMIC_FTRACE_WITH_ARGS
330336
depends on DYNAMIC_FTRACE
331337
depends on HAVE_DYNAMIC_FTRACE_WITH_ARGS
332338

339+
config DYNAMIC_FTRACE_WITH_JMP
340+
def_bool y
341+
depends on DYNAMIC_FTRACE
342+
depends on DYNAMIC_FTRACE_WITH_DIRECT_CALLS
343+
depends on HAVE_DYNAMIC_FTRACE_WITH_JMP
344+
333345
config FPROBE
334346
bool "Kernel Function Probe (fprobe)"
335347
depends on HAVE_FUNCTION_GRAPH_FREGS && HAVE_FTRACE_GRAPH_FUNC

kernel/trace/ftrace.c

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5951,7 +5951,8 @@ static void remove_direct_functions_hash(struct ftrace_hash *hash, unsigned long
59515951
for (i = 0; i < size; i++) {
59525952
hlist_for_each_entry(entry, &hash->buckets[i], hlist) {
59535953
del = __ftrace_lookup_ip(direct_functions, entry->ip);
5954-
if (del && del->direct == addr) {
5954+
if (del && ftrace_jmp_get(del->direct) ==
5955+
ftrace_jmp_get(addr)) {
59555956
remove_hash_entry(direct_functions, del);
59565957
kfree(del);
59575958
}
@@ -6016,8 +6017,15 @@ int register_ftrace_direct(struct ftrace_ops *ops, unsigned long addr)
60166017
if (ftrace_hash_empty(hash))
60176018
return -EINVAL;
60186019

6020+
/* This is a "raw" address, and this should never happen. */
6021+
if (WARN_ON_ONCE(ftrace_is_jmp(addr)))
6022+
return -EINVAL;
6023+
60196024
mutex_lock(&direct_mutex);
60206025

6026+
if (ops->flags & FTRACE_OPS_FL_JMP)
6027+
addr = ftrace_jmp_set(addr);
6028+
60216029
/* Make sure requested entries are not already registered.. */
60226030
size = 1 << hash->size_bits;
60236031
for (i = 0; i < size; i++) {
@@ -6138,6 +6146,13 @@ __modify_ftrace_direct(struct ftrace_ops *ops, unsigned long addr)
61386146

61396147
lockdep_assert_held_once(&direct_mutex);
61406148

6149+
/* This is a "raw" address, and this should never happen. */
6150+
if (WARN_ON_ONCE(ftrace_is_jmp(addr)))
6151+
return -EINVAL;
6152+
6153+
if (ops->flags & FTRACE_OPS_FL_JMP)
6154+
addr = ftrace_jmp_set(addr);
6155+
61416156
/* Enable the tmp_ops to have the same functions as the direct ops */
61426157
ftrace_ops_init(&tmp_ops);
61436158
tmp_ops.func_hash = ops->func_hash;

0 commit comments

Comments
 (0)