locking/static_keys: Add a new static_key interface
There are various problems and short-comings with the current
static_key interface:
- static_key_{true,false}() read like a branch depending on the key
value, instead of the actual likely/unlikely branch depending on
init value.
- static_key_{true,false}() are, as stated above, tied to the
static_key init values STATIC_KEY_INIT_{TRUE,FALSE}.
- we're limited to the 2 (out of 4) possible options that compile to
a default NOP because that's what our arch_static_branch() assembly
emits.
So provide a new static_key interface:
DEFINE_STATIC_KEY_TRUE(name);
DEFINE_STATIC_KEY_FALSE(name);
Which define a key of different types with an initial true/false
value.
Then allow:
static_branch_likely()
static_branch_unlikely()
to take a key of either type and emit the right instruction for the
case.
This means adding a second arch_static_branch_jump() assembly helper
which emits a JMP per default.
In order to determine the right instruction for the right state,
encode the branch type in the LSB of jump_entry::key.
This is the final step in removing the naming confusion that has led to
a stream of avoidable bugs such as:
a833581e37
("x86, perf: Fix static_key bug in load_mm_cr4()")
... but it also allows new static key combinations that will give us
performance enhancements in the subsequent patches.
Tested-by: Rabin Vincent <rabin@rab.in> # arm
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Acked-by: Michael Ellerman <mpe@ellerman.id.au> # ppc
Acked-by: Heiko Carstens <heiko.carstens@de.ibm.com> # s390
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: linux-kernel@vger.kernel.org
Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:

committed by
Ingo Molnar

parent
706249c222
commit
11276d5306
@@ -107,12 +107,12 @@ static inline int static_key_count(struct static_key *key)
|
||||
|
||||
static __always_inline bool static_key_false(struct static_key *key)
|
||||
{
|
||||
return arch_static_branch(key);
|
||||
return arch_static_branch(key, false);
|
||||
}
|
||||
|
||||
static __always_inline bool static_key_true(struct static_key *key)
|
||||
{
|
||||
return !static_key_false(key);
|
||||
return !arch_static_branch(key, true);
|
||||
}
|
||||
|
||||
extern struct jump_entry __start___jump_table[];
|
||||
@@ -130,12 +130,12 @@ extern void static_key_slow_inc(struct static_key *key);
|
||||
extern void static_key_slow_dec(struct static_key *key);
|
||||
extern void jump_label_apply_nops(struct module *mod);
|
||||
|
||||
#define STATIC_KEY_INIT_TRUE ((struct static_key) \
|
||||
#define STATIC_KEY_INIT_TRUE \
|
||||
{ .enabled = ATOMIC_INIT(1), \
|
||||
.entries = (void *)JUMP_TYPE_TRUE })
|
||||
#define STATIC_KEY_INIT_FALSE ((struct static_key) \
|
||||
.entries = (void *)JUMP_TYPE_TRUE }
|
||||
#define STATIC_KEY_INIT_FALSE \
|
||||
{ .enabled = ATOMIC_INIT(0), \
|
||||
.entries = (void *)JUMP_TYPE_FALSE })
|
||||
.entries = (void *)JUMP_TYPE_FALSE }
|
||||
|
||||
#else /* !HAVE_JUMP_LABEL */
|
||||
|
||||
@@ -183,10 +183,8 @@ static inline int jump_label_apply_nops(struct module *mod)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define STATIC_KEY_INIT_TRUE ((struct static_key) \
|
||||
{ .enabled = ATOMIC_INIT(1) })
|
||||
#define STATIC_KEY_INIT_FALSE ((struct static_key) \
|
||||
{ .enabled = ATOMIC_INIT(0) })
|
||||
#define STATIC_KEY_INIT_TRUE { .enabled = ATOMIC_INIT(1) }
|
||||
#define STATIC_KEY_INIT_FALSE { .enabled = ATOMIC_INIT(0) }
|
||||
|
||||
#endif /* HAVE_JUMP_LABEL */
|
||||
|
||||
@@ -218,6 +216,137 @@ static inline void static_key_disable(struct static_key *key)
|
||||
static_key_slow_dec(key);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
/*
|
||||
* Two type wrappers around static_key, such that we can use compile time
|
||||
* type differentiation to emit the right code.
|
||||
*
|
||||
* All the below code is macros in order to play type games.
|
||||
*/
|
||||
|
||||
struct static_key_true {
|
||||
struct static_key key;
|
||||
};
|
||||
|
||||
struct static_key_false {
|
||||
struct static_key key;
|
||||
};
|
||||
|
||||
#define STATIC_KEY_TRUE_INIT (struct static_key_true) { .key = STATIC_KEY_INIT_TRUE, }
|
||||
#define STATIC_KEY_FALSE_INIT (struct static_key_false){ .key = STATIC_KEY_INIT_FALSE, }
|
||||
|
||||
#define DEFINE_STATIC_KEY_TRUE(name) \
|
||||
struct static_key_true name = STATIC_KEY_TRUE_INIT
|
||||
|
||||
#define DEFINE_STATIC_KEY_FALSE(name) \
|
||||
struct static_key_false name = STATIC_KEY_FALSE_INIT
|
||||
|
||||
#ifdef HAVE_JUMP_LABEL
|
||||
|
||||
/*
|
||||
* Combine the right initial value (type) with the right branch order
|
||||
* to generate the desired result.
|
||||
*
|
||||
*
|
||||
* type\branch| likely (1) | unlikely (0)
|
||||
* -----------+-----------------------+------------------
|
||||
* | |
|
||||
* true (1) | ... | ...
|
||||
* | NOP | JMP L
|
||||
* | <br-stmts> | 1: ...
|
||||
* | L: ... |
|
||||
* | |
|
||||
* | | L: <br-stmts>
|
||||
* | | jmp 1b
|
||||
* | |
|
||||
* -----------+-----------------------+------------------
|
||||
* | |
|
||||
* false (0) | ... | ...
|
||||
* | JMP L | NOP
|
||||
* | <br-stmts> | 1: ...
|
||||
* | L: ... |
|
||||
* | |
|
||||
* | | L: <br-stmts>
|
||||
* | | jmp 1b
|
||||
* | |
|
||||
* -----------+-----------------------+------------------
|
||||
*
|
||||
* The initial value is encoded in the LSB of static_key::entries,
|
||||
* type: 0 = false, 1 = true.
|
||||
*
|
||||
* The branch type is encoded in the LSB of jump_entry::key,
|
||||
* branch: 0 = unlikely, 1 = likely.
|
||||
*
|
||||
* This gives the following logic table:
|
||||
*
|
||||
* enabled type branch instuction
|
||||
* -----------------------------+-----------
|
||||
* 0 0 0 | NOP
|
||||
* 0 0 1 | JMP
|
||||
* 0 1 0 | NOP
|
||||
* 0 1 1 | JMP
|
||||
*
|
||||
* 1 0 0 | JMP
|
||||
* 1 0 1 | NOP
|
||||
* 1 1 0 | JMP
|
||||
* 1 1 1 | NOP
|
||||
*
|
||||
* Which gives the following functions:
|
||||
*
|
||||
* dynamic: instruction = enabled ^ branch
|
||||
* static: instruction = type ^ branch
|
||||
*
|
||||
* See jump_label_type() / jump_label_init_type().
|
||||
*/
|
||||
|
||||
extern bool ____wrong_branch_error(void);
|
||||
|
||||
#define static_branch_likely(x) \
|
||||
({ \
|
||||
bool branch; \
|
||||
if (__builtin_types_compatible_p(typeof(*x), struct static_key_true)) \
|
||||
branch = !arch_static_branch(&(x)->key, true); \
|
||||
else if (__builtin_types_compatible_p(typeof(*x), struct static_key_false)) \
|
||||
branch = !arch_static_branch_jump(&(x)->key, true); \
|
||||
else \
|
||||
branch = ____wrong_branch_error(); \
|
||||
branch; \
|
||||
})
|
||||
|
||||
#define static_branch_unlikely(x) \
|
||||
({ \
|
||||
bool branch; \
|
||||
if (__builtin_types_compatible_p(typeof(*x), struct static_key_true)) \
|
||||
branch = arch_static_branch_jump(&(x)->key, false); \
|
||||
else if (__builtin_types_compatible_p(typeof(*x), struct static_key_false)) \
|
||||
branch = arch_static_branch(&(x)->key, false); \
|
||||
else \
|
||||
branch = ____wrong_branch_error(); \
|
||||
branch; \
|
||||
})
|
||||
|
||||
#else /* !HAVE_JUMP_LABEL */
|
||||
|
||||
#define static_branch_likely(x) likely(static_key_enabled(&(x)->key))
|
||||
#define static_branch_unlikely(x) unlikely(static_key_enabled(&(x)->key))
|
||||
|
||||
#endif /* HAVE_JUMP_LABEL */
|
||||
|
||||
/*
|
||||
* Advanced usage; refcount, branch is enabled when: count != 0
|
||||
*/
|
||||
|
||||
#define static_branch_inc(x) static_key_slow_inc(&(x)->key)
|
||||
#define static_branch_dec(x) static_key_slow_dec(&(x)->key)
|
||||
|
||||
/*
|
||||
* Normal usage; boolean enable/disable.
|
||||
*/
|
||||
|
||||
#define static_branch_enable(x) static_key_enable(&(x)->key)
|
||||
#define static_branch_disable(x) static_key_disable(&(x)->key)
|
||||
|
||||
#endif /* _LINUX_JUMP_LABEL_H */
|
||||
|
||||
#endif /* __ASSEMBLY__ */
|
||||
|
Reference in New Issue
Block a user