static_call: Add static_call_cond()
Extend the static_call infrastructure to optimize the following common pattern: if (func_ptr) func_ptr(args...) For the trampoline (which is in effect a tail-call), we patch the JMP.d32 into a RET, which then directly consumes the trampoline call. For the in-line sites we replace the CALL with a NOP5. NOTE: this is 'obviously' limited to functions with a 'void' return type. NOTE: DEFINE_STATIC_COND_CALL() only requires a typename, as opposed to a full function. 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.042977182@infradead.org
This commit is contained in:

committed by
Ingo Molnar

parent
c43a43e439
commit
452cddbff7
@@ -16,7 +16,9 @@
|
||||
*
|
||||
* DECLARE_STATIC_CALL(name, func);
|
||||
* DEFINE_STATIC_CALL(name, func);
|
||||
* DEFINE_STATIC_CALL_NULL(name, typename);
|
||||
* static_call(name)(args...);
|
||||
* static_call_cond(name)(args...);
|
||||
* static_call_update(name, func);
|
||||
*
|
||||
* Usage example:
|
||||
@@ -52,6 +54,43 @@
|
||||
* rather than calling through the trampoline. This requires objtool or a
|
||||
* compiler plugin to detect all the static_call() sites and annotate them
|
||||
* in the .static_call_sites section.
|
||||
*
|
||||
*
|
||||
* Notes on NULL function pointers:
|
||||
*
|
||||
* Static_call()s support NULL functions, with many of the caveats that
|
||||
* regular function pointers have.
|
||||
*
|
||||
* Clearly calling a NULL function pointer is 'BAD', so too for
|
||||
* static_call()s (although when HAVE_STATIC_CALL it might not be immediately
|
||||
* fatal). A NULL static_call can be the result of:
|
||||
*
|
||||
* DECLARE_STATIC_CALL_NULL(my_static_call, void (*)(int));
|
||||
*
|
||||
* which is equivalent to declaring a NULL function pointer with just a
|
||||
* typename:
|
||||
*
|
||||
* void (*my_func_ptr)(int arg1) = NULL;
|
||||
*
|
||||
* or using static_call_update() with a NULL function. In both cases the
|
||||
* HAVE_STATIC_CALL implementation will patch the trampoline with a RET
|
||||
* instruction, instead of an immediate tail-call JMP. HAVE_STATIC_CALL_INLINE
|
||||
* architectures can patch the trampoline call to a NOP.
|
||||
*
|
||||
* In all cases, any argument evaluation is unconditional. Unlike a regular
|
||||
* conditional function pointer call:
|
||||
*
|
||||
* if (my_func_ptr)
|
||||
* my_func_ptr(arg1)
|
||||
*
|
||||
* where the argument evaludation also depends on the pointer value.
|
||||
*
|
||||
* When calling a static_call that can be NULL, use:
|
||||
*
|
||||
* static_call_cond(name)(arg1);
|
||||
*
|
||||
* which will include the required value tests to avoid NULL-pointer
|
||||
* dereferences.
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
@@ -120,7 +159,16 @@ extern int static_call_text_reserved(void *start, void *end);
|
||||
}; \
|
||||
ARCH_DEFINE_STATIC_CALL_TRAMP(name, _func)
|
||||
|
||||
#define DEFINE_STATIC_CALL_NULL(name, _func) \
|
||||
DECLARE_STATIC_CALL(name, _func); \
|
||||
struct static_call_key STATIC_CALL_KEY(name) = { \
|
||||
.func = NULL, \
|
||||
.type = 1, \
|
||||
}; \
|
||||
ARCH_DEFINE_STATIC_CALL_NULL_TRAMP(name)
|
||||
|
||||
#define static_call(name) __static_call(name)
|
||||
#define static_call_cond(name) (void)__static_call(name)
|
||||
|
||||
#define EXPORT_STATIC_CALL(name) \
|
||||
EXPORT_SYMBOL(STATIC_CALL_KEY(name)); \
|
||||
@@ -143,7 +191,15 @@ struct static_call_key {
|
||||
}; \
|
||||
ARCH_DEFINE_STATIC_CALL_TRAMP(name, _func)
|
||||
|
||||
#define DEFINE_STATIC_CALL_NULL(name, _func) \
|
||||
DECLARE_STATIC_CALL(name, _func); \
|
||||
struct static_call_key STATIC_CALL_KEY(name) = { \
|
||||
.func = NULL, \
|
||||
}; \
|
||||
ARCH_DEFINE_STATIC_CALL_NULL_TRAMP(name)
|
||||
|
||||
#define static_call(name) __static_call(name)
|
||||
#define static_call_cond(name) (void)__static_call(name)
|
||||
|
||||
static inline
|
||||
void __static_call_update(struct static_call_key *key, void *tramp, void *func)
|
||||
@@ -179,9 +235,39 @@ struct static_call_key {
|
||||
.func = _func, \
|
||||
}
|
||||
|
||||
#define DEFINE_STATIC_CALL_NULL(name, _func) \
|
||||
DECLARE_STATIC_CALL(name, _func); \
|
||||
struct static_call_key STATIC_CALL_KEY(name) = { \
|
||||
.func = NULL, \
|
||||
}
|
||||
|
||||
#define static_call(name) \
|
||||
((typeof(STATIC_CALL_TRAMP(name))*)(STATIC_CALL_KEY(name).func))
|
||||
|
||||
static inline void __static_call_nop(void) { }
|
||||
|
||||
/*
|
||||
* This horrific hack takes care of two things:
|
||||
*
|
||||
* - it ensures the compiler will only load the function pointer ONCE,
|
||||
* which avoids a reload race.
|
||||
*
|
||||
* - it ensures the argument evaluation is unconditional, similar
|
||||
* to the HAVE_STATIC_CALL variant.
|
||||
*
|
||||
* Sadly current GCC/Clang (10 for both) do not optimize this properly
|
||||
* and will emit an indirect call for the NULL case :-(
|
||||
*/
|
||||
#define __static_call_cond(name) \
|
||||
({ \
|
||||
void *func = READ_ONCE(STATIC_CALL_KEY(name).func); \
|
||||
if (!func) \
|
||||
func = &__static_call_nop; \
|
||||
(typeof(STATIC_CALL_TRAMP(name))*)func; \
|
||||
})
|
||||
|
||||
#define static_call_cond(name) (void)__static_call_cond(name)
|
||||
|
||||
static inline
|
||||
void __static_call_update(struct static_call_key *key, void *tramp, void *func)
|
||||
{
|
||||
|
Reference in New Issue
Block a user