static_call: Handle tail-calls

GCC can turn our static_call(name)(args...) into a tail call, in which
case we get a JMP.d32 into the trampoline (which then does a further
tail-call).

Teach objtool to recognise and mark these in .static_call_sites and
adjust the code patching to deal with this.

Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Link: https://lore.kernel.org/r/20200818135805.101186767@infradead.org
This commit is contained in:
Peter Zijlstra
2020-08-18 15:57:49 +02:00
committed by Ingo Molnar
parent 452cddbff7
commit 5b06fd3bb9
6 changed files with 60 additions and 18 deletions

View File

@@ -15,8 +15,6 @@ extern struct static_call_site __start_static_call_sites[],
static bool static_call_initialized;
#define STATIC_CALL_INIT 1UL
/* mutex to protect key modules/sites */
static DEFINE_MUTEX(static_call_mutex);
@@ -39,18 +37,23 @@ static inline void *static_call_addr(struct static_call_site *site)
static inline struct static_call_key *static_call_key(const struct static_call_site *site)
{
return (struct static_call_key *)
(((long)site->key + (long)&site->key) & ~STATIC_CALL_INIT);
(((long)site->key + (long)&site->key) & ~STATIC_CALL_SITE_FLAGS);
}
/* These assume the key is word-aligned. */
static inline bool static_call_is_init(struct static_call_site *site)
{
return ((long)site->key + (long)&site->key) & STATIC_CALL_INIT;
return ((long)site->key + (long)&site->key) & STATIC_CALL_SITE_INIT;
}
static inline bool static_call_is_tail(struct static_call_site *site)
{
return ((long)site->key + (long)&site->key) & STATIC_CALL_SITE_TAIL;
}
static inline void static_call_set_init(struct static_call_site *site)
{
site->key = ((long)static_call_key(site) | STATIC_CALL_INIT) -
site->key = ((long)static_call_key(site) | STATIC_CALL_SITE_INIT) -
(long)&site->key;
}
@@ -104,7 +107,7 @@ void __static_call_update(struct static_call_key *key, void *tramp, void *func)
key->func = func;
arch_static_call_transform(NULL, tramp, func);
arch_static_call_transform(NULL, tramp, func, false);
/*
* If uninitialized, we'll not update the callsites, but they still
@@ -154,7 +157,8 @@ void __static_call_update(struct static_call_key *key, void *tramp, void *func)
continue;
}
arch_static_call_transform(site_addr, NULL, func);
arch_static_call_transform(site_addr, NULL, func,
static_call_is_tail(site));
}
}
@@ -198,7 +202,8 @@ static int __static_call_init(struct module *mod,
key->mods = site_mod;
}
arch_static_call_transform(site_addr, NULL, key->func);
arch_static_call_transform(site_addr, NULL, key->func,
static_call_is_tail(site));
}
return 0;