Commit 7825451f authored by Peter Zijlstra's avatar Peter Zijlstra

static_call: Add call depth tracking support

When indirect calls are switched to direct calls then it has to be ensured
that the call target is not the function, but the call thunk when call
depth tracking is enabled. But static calls are available before call
thunks have been set up.

Ensure a second run through the static call patching code after call thunks
have been created. When call thunks are not enabled this has no side
effects.
Signed-off-by: default avatarPeter Zijlstra (Intel) <peterz@infradead.org>
Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
Signed-off-by: default avatarPeter Zijlstra (Intel) <peterz@infradead.org>
Link: https://lore.kernel.org/r/20220915111148.306100465@infradead.org
parent f5c1bb2a
...@@ -91,11 +91,16 @@ struct callthunk_sites { ...@@ -91,11 +91,16 @@ struct callthunk_sites {
extern void callthunks_patch_builtin_calls(void); extern void callthunks_patch_builtin_calls(void);
extern void callthunks_patch_module_calls(struct callthunk_sites *sites, extern void callthunks_patch_module_calls(struct callthunk_sites *sites,
struct module *mod); struct module *mod);
extern void *callthunks_translate_call_dest(void *dest);
#else #else
static __always_inline void callthunks_patch_builtin_calls(void) {} static __always_inline void callthunks_patch_builtin_calls(void) {}
static __always_inline void static __always_inline void
callthunks_patch_module_calls(struct callthunk_sites *sites, callthunks_patch_module_calls(struct callthunk_sites *sites,
struct module *mod) {} struct module *mod) {}
static __always_inline void *callthunks_translate_call_dest(void *dest)
{
return dest;
}
#endif #endif
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include <linux/kallsyms.h> #include <linux/kallsyms.h>
#include <linux/memory.h> #include <linux/memory.h>
#include <linux/moduleloader.h> #include <linux/moduleloader.h>
#include <linux/static_call.h>
#include <asm/alternative.h> #include <asm/alternative.h>
#include <asm/asm-offsets.h> #include <asm/asm-offsets.h>
...@@ -271,10 +272,27 @@ void __init callthunks_patch_builtin_calls(void) ...@@ -271,10 +272,27 @@ void __init callthunks_patch_builtin_calls(void)
pr_info("Setting up call depth tracking\n"); pr_info("Setting up call depth tracking\n");
mutex_lock(&text_mutex); mutex_lock(&text_mutex);
callthunks_setup(&cs, &builtin_coretext); callthunks_setup(&cs, &builtin_coretext);
static_call_force_reinit();
thunks_initialized = true; thunks_initialized = true;
mutex_unlock(&text_mutex); mutex_unlock(&text_mutex);
} }
void *callthunks_translate_call_dest(void *dest)
{
void *target;
lockdep_assert_held(&text_mutex);
if (!thunks_initialized || skip_addr(dest))
return dest;
if (!is_coretext(NULL, dest))
return dest;
target = patch_dest(dest, false);
return target ? : dest;
}
#ifdef CONFIG_MODULES #ifdef CONFIG_MODULES
void noinline callthunks_patch_module_calls(struct callthunk_sites *cs, void noinline callthunks_patch_module_calls(struct callthunk_sites *cs,
struct module *mod) struct module *mod)
......
...@@ -34,6 +34,7 @@ static void __ref __static_call_transform(void *insn, enum insn_type type, ...@@ -34,6 +34,7 @@ static void __ref __static_call_transform(void *insn, enum insn_type type,
switch (type) { switch (type) {
case CALL: case CALL:
func = callthunks_translate_call_dest(func);
code = text_gen_insn(CALL_INSN_OPCODE, insn, func); code = text_gen_insn(CALL_INSN_OPCODE, insn, func);
if (func == &__static_call_return0) { if (func == &__static_call_return0) {
emulate = code; emulate = code;
......
...@@ -162,6 +162,8 @@ extern void arch_static_call_transform(void *site, void *tramp, void *func, bool ...@@ -162,6 +162,8 @@ extern void arch_static_call_transform(void *site, void *tramp, void *func, bool
extern int __init static_call_init(void); extern int __init static_call_init(void);
extern void static_call_force_reinit(void);
struct static_call_mod { struct static_call_mod {
struct static_call_mod *next; struct static_call_mod *next;
struct module *mod; /* for vmlinux, mod == NULL */ struct module *mod; /* for vmlinux, mod == NULL */
......
...@@ -15,7 +15,18 @@ extern struct static_call_site __start_static_call_sites[], ...@@ -15,7 +15,18 @@ extern struct static_call_site __start_static_call_sites[],
extern struct static_call_tramp_key __start_static_call_tramp_key[], extern struct static_call_tramp_key __start_static_call_tramp_key[],
__stop_static_call_tramp_key[]; __stop_static_call_tramp_key[];
static bool static_call_initialized; static int static_call_initialized;
/*
* Must be called before early_initcall() to be effective.
*/
void static_call_force_reinit(void)
{
if (WARN_ON_ONCE(!static_call_initialized))
return;
static_call_initialized++;
}
/* mutex to protect key modules/sites */ /* mutex to protect key modules/sites */
static DEFINE_MUTEX(static_call_mutex); static DEFINE_MUTEX(static_call_mutex);
...@@ -475,7 +486,8 @@ int __init static_call_init(void) ...@@ -475,7 +486,8 @@ int __init static_call_init(void)
{ {
int ret; int ret;
if (static_call_initialized) /* See static_call_force_reinit(). */
if (static_call_initialized == 1)
return 0; return 0;
cpus_read_lock(); cpus_read_lock();
...@@ -490,11 +502,12 @@ int __init static_call_init(void) ...@@ -490,11 +502,12 @@ int __init static_call_init(void)
BUG(); BUG();
} }
static_call_initialized = true;
#ifdef CONFIG_MODULES #ifdef CONFIG_MODULES
register_module_notifier(&static_call_module_nb); if (!static_call_initialized)
register_module_notifier(&static_call_module_nb);
#endif #endif
static_call_initialized = 1;
return 0; return 0;
} }
early_initcall(static_call_init); early_initcall(static_call_init);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment