diff options
author | Peter Zijlstra <peterz@infradead.org> | 2015-07-24 09:09:55 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2015-08-03 05:34:15 -0400 |
commit | 11276d5306b8e5b438a36bbff855fe792d7eaa61 (patch) | |
tree | 9ddfc5e1bec6174e838cce8eb67af9e911c4e5f8 | |
parent | 706249c222f68471b6f8e9e8e9b77665c404b226 (diff) |
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:
a833581e372a ("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>
-rw-r--r-- | arch/arm/include/asm/jump_label.h | 25 | ||||
-rw-r--r-- | arch/arm64/include/asm/jump_label.h | 18 | ||||
-rw-r--r-- | arch/mips/include/asm/jump_label.h | 19 | ||||
-rw-r--r-- | arch/powerpc/include/asm/jump_label.h | 19 | ||||
-rw-r--r-- | arch/s390/include/asm/jump_label.h | 19 | ||||
-rw-r--r-- | arch/sparc/include/asm/jump_label.h | 35 | ||||
-rw-r--r-- | arch/x86/include/asm/jump_label.h | 21 | ||||
-rw-r--r-- | include/linux/jump_label.h | 149 | ||||
-rw-r--r-- | kernel/jump_label.c | 37 |
9 files changed, 298 insertions, 44 deletions
diff --git a/arch/arm/include/asm/jump_label.h b/arch/arm/include/asm/jump_label.h index 5f337dc5c108..34f7b6980d21 100644 --- a/arch/arm/include/asm/jump_label.h +++ b/arch/arm/include/asm/jump_label.h | |||
@@ -4,23 +4,32 @@ | |||
4 | #ifndef __ASSEMBLY__ | 4 | #ifndef __ASSEMBLY__ |
5 | 5 | ||
6 | #include <linux/types.h> | 6 | #include <linux/types.h> |
7 | #include <asm/unified.h> | ||
7 | 8 | ||
8 | #define JUMP_LABEL_NOP_SIZE 4 | 9 | #define JUMP_LABEL_NOP_SIZE 4 |
9 | 10 | ||
10 | #ifdef CONFIG_THUMB2_KERNEL | 11 | static __always_inline bool arch_static_branch(struct static_key *key, bool branch) |
11 | #define JUMP_LABEL_NOP "nop.w" | 12 | { |
12 | #else | 13 | asm_volatile_goto("1:\n\t" |
13 | #define JUMP_LABEL_NOP "nop" | 14 | WASM(nop) "\n\t" |
14 | #endif | 15 | ".pushsection __jump_table, \"aw\"\n\t" |
16 | ".word 1b, %l[l_yes], %c0\n\t" | ||
17 | ".popsection\n\t" | ||
18 | : : "i" (&((char *)key)[branch]) : : l_yes); | ||
19 | |||
20 | return false; | ||
21 | l_yes: | ||
22 | return true; | ||
23 | } | ||
15 | 24 | ||
16 | static __always_inline bool arch_static_branch(struct static_key *key) | 25 | static __always_inline bool arch_static_branch_jump(struct static_key *key, bool branch) |
17 | { | 26 | { |
18 | asm_volatile_goto("1:\n\t" | 27 | asm_volatile_goto("1:\n\t" |
19 | JUMP_LABEL_NOP "\n\t" | 28 | WASM(b) " %l[l_yes]\n\t" |
20 | ".pushsection __jump_table, \"aw\"\n\t" | 29 | ".pushsection __jump_table, \"aw\"\n\t" |
21 | ".word 1b, %l[l_yes], %c0\n\t" | 30 | ".word 1b, %l[l_yes], %c0\n\t" |
22 | ".popsection\n\t" | 31 | ".popsection\n\t" |
23 | : : "i" (key) : : l_yes); | 32 | : : "i" (&((char *)key)[branch]) : : l_yes); |
24 | 33 | ||
25 | return false; | 34 | return false; |
26 | l_yes: | 35 | l_yes: |
diff --git a/arch/arm64/include/asm/jump_label.h b/arch/arm64/include/asm/jump_label.h index c0e5165c2f76..1b5e0e843c3a 100644 --- a/arch/arm64/include/asm/jump_label.h +++ b/arch/arm64/include/asm/jump_label.h | |||
@@ -26,14 +26,28 @@ | |||
26 | 26 | ||
27 | #define JUMP_LABEL_NOP_SIZE AARCH64_INSN_SIZE | 27 | #define JUMP_LABEL_NOP_SIZE AARCH64_INSN_SIZE |
28 | 28 | ||
29 | static __always_inline bool arch_static_branch(struct static_key *key) | 29 | static __always_inline bool arch_static_branch(struct static_key *key, bool branch) |
30 | { | 30 | { |
31 | asm goto("1: nop\n\t" | 31 | asm goto("1: nop\n\t" |
32 | ".pushsection __jump_table, \"aw\"\n\t" | 32 | ".pushsection __jump_table, \"aw\"\n\t" |
33 | ".align 3\n\t" | 33 | ".align 3\n\t" |
34 | ".quad 1b, %l[l_yes], %c0\n\t" | 34 | ".quad 1b, %l[l_yes], %c0\n\t" |
35 | ".popsection\n\t" | 35 | ".popsection\n\t" |
36 | : : "i"(key) : : l_yes); | 36 | : : "i"(&((char *)key)[branch]) : : l_yes); |
37 | |||
38 | return false; | ||
39 | l_yes: | ||
40 | return true; | ||
41 | } | ||
42 | |||
43 | static __always_inline bool arch_static_branch_jump(struct static_key *key, bool branch) | ||
44 | { | ||
45 | asm goto("1: b %l[l_yes]\n\t" | ||
46 | ".pushsection __jump_table, \"aw\"\n\t" | ||
47 | ".align 3\n\t" | ||
48 | ".quad 1b, %l[l_yes], %c0\n\t" | ||
49 | ".popsection\n\t" | ||
50 | : : "i"(&((char *)key)[branch]) : : l_yes); | ||
37 | 51 | ||
38 | return false; | 52 | return false; |
39 | l_yes: | 53 | l_yes: |
diff --git a/arch/mips/include/asm/jump_label.h b/arch/mips/include/asm/jump_label.h index 608aa57799c8..e77672539e8e 100644 --- a/arch/mips/include/asm/jump_label.h +++ b/arch/mips/include/asm/jump_label.h | |||
@@ -26,14 +26,29 @@ | |||
26 | #define NOP_INSN "nop" | 26 | #define NOP_INSN "nop" |
27 | #endif | 27 | #endif |
28 | 28 | ||
29 | static __always_inline bool arch_static_branch(struct static_key *key) | 29 | static __always_inline bool arch_static_branch(struct static_key *key, bool branch) |
30 | { | 30 | { |
31 | asm_volatile_goto("1:\t" NOP_INSN "\n\t" | 31 | asm_volatile_goto("1:\t" NOP_INSN "\n\t" |
32 | "nop\n\t" | 32 | "nop\n\t" |
33 | ".pushsection __jump_table, \"aw\"\n\t" | 33 | ".pushsection __jump_table, \"aw\"\n\t" |
34 | WORD_INSN " 1b, %l[l_yes], %0\n\t" | 34 | WORD_INSN " 1b, %l[l_yes], %0\n\t" |
35 | ".popsection\n\t" | 35 | ".popsection\n\t" |
36 | : : "i" (key) : : l_yes); | 36 | : : "i" (&((char *)key)[branch]) : : l_yes); |
37 | |||
38 | return false; | ||
39 | l_yes: | ||
40 | return true; | ||
41 | } | ||
42 | |||
43 | static __always_inline bool arch_static_branch_jump(struct static_key *key, bool branch) | ||
44 | { | ||
45 | asm_volatile_goto("1:\tj %l[l_yes]\n\t" | ||
46 | "nop\n\t" | ||
47 | ".pushsection __jump_table, \"aw\"\n\t" | ||
48 | WORD_INSN " 1b, %l[l_yes], %0\n\t" | ||
49 | ".popsection\n\t" | ||
50 | : : "i" (&((char *)key)[branch]) : : l_yes); | ||
51 | |||
37 | return false; | 52 | return false; |
38 | l_yes: | 53 | l_yes: |
39 | return true; | 54 | return true; |
diff --git a/arch/powerpc/include/asm/jump_label.h b/arch/powerpc/include/asm/jump_label.h index efbf9a322a23..47e155f15433 100644 --- a/arch/powerpc/include/asm/jump_label.h +++ b/arch/powerpc/include/asm/jump_label.h | |||
@@ -18,14 +18,29 @@ | |||
18 | #define JUMP_ENTRY_TYPE stringify_in_c(FTR_ENTRY_LONG) | 18 | #define JUMP_ENTRY_TYPE stringify_in_c(FTR_ENTRY_LONG) |
19 | #define JUMP_LABEL_NOP_SIZE 4 | 19 | #define JUMP_LABEL_NOP_SIZE 4 |
20 | 20 | ||
21 | static __always_inline bool arch_static_branch(struct static_key *key) | 21 | static __always_inline bool arch_static_branch(struct static_key *key, bool branch) |
22 | { | 22 | { |
23 | asm_volatile_goto("1:\n\t" | 23 | asm_volatile_goto("1:\n\t" |
24 | "nop\n\t" | 24 | "nop\n\t" |
25 | ".pushsection __jump_table, \"aw\"\n\t" | 25 | ".pushsection __jump_table, \"aw\"\n\t" |
26 | JUMP_ENTRY_TYPE "1b, %l[l_yes], %c0\n\t" | 26 | JUMP_ENTRY_TYPE "1b, %l[l_yes], %c0\n\t" |
27 | ".popsection \n\t" | 27 | ".popsection \n\t" |
28 | : : "i" (key) : : l_yes); | 28 | : : "i" (&((char *)key)[branch]) : : l_yes); |
29 | |||
30 | return false; | ||
31 | l_yes: | ||
32 | return true; | ||
33 | } | ||
34 | |||
35 | static __always_inline bool arch_static_branch_jump(struct static_key *key, bool branch) | ||
36 | { | ||
37 | asm_volatile_goto("1:\n\t" | ||
38 | "b %l[l_yes]\n\t" | ||
39 | ".pushsection __jump_table, \"aw\"\n\t" | ||
40 | JUMP_ENTRY_TYPE "1b, %l[l_yes], %c0\n\t" | ||
41 | ".popsection \n\t" | ||
42 | : : "i" (&((char *)key)[branch]) : : l_yes); | ||
43 | |||
29 | return false; | 44 | return false; |
30 | l_yes: | 45 | l_yes: |
31 | return true; | 46 | return true; |
diff --git a/arch/s390/include/asm/jump_label.h b/arch/s390/include/asm/jump_label.h index 69972b7957ee..7f9fd5e3f1bf 100644 --- a/arch/s390/include/asm/jump_label.h +++ b/arch/s390/include/asm/jump_label.h | |||
@@ -12,14 +12,29 @@ | |||
12 | * We use a brcl 0,2 instruction for jump labels at compile time so it | 12 | * We use a brcl 0,2 instruction for jump labels at compile time so it |
13 | * can be easily distinguished from a hotpatch generated instruction. | 13 | * can be easily distinguished from a hotpatch generated instruction. |
14 | */ | 14 | */ |
15 | static __always_inline bool arch_static_branch(struct static_key *key) | 15 | static __always_inline bool arch_static_branch(struct static_key *key, bool branch) |
16 | { | 16 | { |
17 | asm_volatile_goto("0: brcl 0,"__stringify(JUMP_LABEL_NOP_OFFSET)"\n" | 17 | asm_volatile_goto("0: brcl 0,"__stringify(JUMP_LABEL_NOP_OFFSET)"\n" |
18 | ".pushsection __jump_table, \"aw\"\n" | 18 | ".pushsection __jump_table, \"aw\"\n" |
19 | ".balign 8\n" | 19 | ".balign 8\n" |
20 | ".quad 0b, %l[label], %0\n" | 20 | ".quad 0b, %l[label], %0\n" |
21 | ".popsection\n" | 21 | ".popsection\n" |
22 | : : "X" (key) : : label); | 22 | : : "X" (&((char *)key)[branch]) : : label); |
23 | |||
24 | return false; | ||
25 | label: | ||
26 | return true; | ||
27 | } | ||
28 | |||
29 | static __always_inline bool arch_static_branch_jump(struct static_key *key, bool branch) | ||
30 | { | ||
31 | asm_volatile_goto("0: brcl 15, %l[label]\n" | ||
32 | ".pushsection __jump_table, \"aw\"\n" | ||
33 | ".balign 8\n" | ||
34 | ".quad 0b, %l[label], %0\n" | ||
35 | ".popsection\n" | ||
36 | : : "X" (&((char *)key)[branch]) : : label); | ||
37 | |||
23 | return false; | 38 | return false; |
24 | label: | 39 | label: |
25 | return true; | 40 | return true; |
diff --git a/arch/sparc/include/asm/jump_label.h b/arch/sparc/include/asm/jump_label.h index cc9b04a2b11b..62d0354d1727 100644 --- a/arch/sparc/include/asm/jump_label.h +++ b/arch/sparc/include/asm/jump_label.h | |||
@@ -7,16 +7,33 @@ | |||
7 | 7 | ||
8 | #define JUMP_LABEL_NOP_SIZE 4 | 8 | #define JUMP_LABEL_NOP_SIZE 4 |
9 | 9 | ||
10 | static __always_inline bool arch_static_branch(struct static_key *key) | 10 | static __always_inline bool arch_static_branch(struct static_key *key, bool branch) |
11 | { | 11 | { |
12 | asm_volatile_goto("1:\n\t" | 12 | asm_volatile_goto("1:\n\t" |
13 | "nop\n\t" | 13 | "nop\n\t" |
14 | "nop\n\t" | 14 | "nop\n\t" |
15 | ".pushsection __jump_table, \"aw\"\n\t" | 15 | ".pushsection __jump_table, \"aw\"\n\t" |
16 | ".align 4\n\t" | 16 | ".align 4\n\t" |
17 | ".word 1b, %l[l_yes], %c0\n\t" | 17 | ".word 1b, %l[l_yes], %c0\n\t" |
18 | ".popsection \n\t" | 18 | ".popsection \n\t" |
19 | : : "i" (key) : : l_yes); | 19 | : : "i" (&((char *)key)[branch]) : : l_yes); |
20 | |||
21 | return false; | ||
22 | l_yes: | ||
23 | return true; | ||
24 | } | ||
25 | |||
26 | static __always_inline bool arch_static_branch_jump(struct static_key *key, bool branch) | ||
27 | { | ||
28 | asm_volatile_goto("1:\n\t" | ||
29 | "b %l[l_yes]\n\t" | ||
30 | "nop\n\t" | ||
31 | ".pushsection __jump_table, \"aw\"\n\t" | ||
32 | ".align 4\n\t" | ||
33 | ".word 1b, %l[l_yes], %c0\n\t" | ||
34 | ".popsection \n\t" | ||
35 | : : "i" (&((char *)key)[branch]) : : l_yes); | ||
36 | |||
20 | return false; | 37 | return false; |
21 | l_yes: | 38 | l_yes: |
22 | return true; | 39 | return true; |
diff --git a/arch/x86/include/asm/jump_label.h b/arch/x86/include/asm/jump_label.h index a4c1cf7e93f8..28d7a857f9d1 100644 --- a/arch/x86/include/asm/jump_label.h +++ b/arch/x86/include/asm/jump_label.h | |||
@@ -16,7 +16,7 @@ | |||
16 | # define STATIC_KEY_INIT_NOP GENERIC_NOP5_ATOMIC | 16 | # define STATIC_KEY_INIT_NOP GENERIC_NOP5_ATOMIC |
17 | #endif | 17 | #endif |
18 | 18 | ||
19 | static __always_inline bool arch_static_branch(struct static_key *key) | 19 | static __always_inline bool arch_static_branch(struct static_key *key, bool branch) |
20 | { | 20 | { |
21 | asm_volatile_goto("1:" | 21 | asm_volatile_goto("1:" |
22 | ".byte " __stringify(STATIC_KEY_INIT_NOP) "\n\t" | 22 | ".byte " __stringify(STATIC_KEY_INIT_NOP) "\n\t" |
@@ -24,7 +24,24 @@ static __always_inline bool arch_static_branch(struct static_key *key) | |||
24 | _ASM_ALIGN "\n\t" | 24 | _ASM_ALIGN "\n\t" |
25 | _ASM_PTR "1b, %l[l_yes], %c0 \n\t" | 25 | _ASM_PTR "1b, %l[l_yes], %c0 \n\t" |
26 | ".popsection \n\t" | 26 | ".popsection \n\t" |
27 | : : "i" (key) : : l_yes); | 27 | : : "i" (&((char *)key)[branch]) : : l_yes); |
28 | |||
29 | return false; | ||
30 | l_yes: | ||
31 | return true; | ||
32 | } | ||
33 | |||
34 | static __always_inline bool arch_static_branch_jump(struct static_key *key, bool branch) | ||
35 | { | ||
36 | asm_volatile_goto("1:" | ||
37 | ".byte 0xe9\n\t .long %l[l_yes] - 2f\n\t" | ||
38 | "2:\n\t" | ||
39 | ".pushsection __jump_table, \"aw\" \n\t" | ||
40 | _ASM_ALIGN "\n\t" | ||
41 | _ASM_PTR "1b, %l[l_yes], %c0 \n\t" | ||
42 | ".popsection \n\t" | ||
43 | : : "i" (&((char *)key)[branch]) : : l_yes); | ||
44 | |||
28 | return false; | 45 | return false; |
29 | l_yes: | 46 | l_yes: |
30 | return true; | 47 | return true; |
diff --git a/include/linux/jump_label.h b/include/linux/jump_label.h index 65f0ebac63cf..e337a1961933 100644 --- a/include/linux/jump_label.h +++ b/include/linux/jump_label.h | |||
@@ -107,12 +107,12 @@ static inline int static_key_count(struct static_key *key) | |||
107 | 107 | ||
108 | static __always_inline bool static_key_false(struct static_key *key) | 108 | static __always_inline bool static_key_false(struct static_key *key) |
109 | { | 109 | { |
110 | return arch_static_branch(key); | 110 | return arch_static_branch(key, false); |
111 | } | 111 | } |
112 | 112 | ||
113 | static __always_inline bool static_key_true(struct static_key *key) | 113 | static __always_inline bool static_key_true(struct static_key *key) |
114 | { | 114 | { |
115 | return !static_key_false(key); | 115 | return !arch_static_branch(key, true); |
116 | } | 116 | } |
117 | 117 | ||
118 | extern struct jump_entry __start___jump_table[]; | 118 | extern struct jump_entry __start___jump_table[]; |
@@ -130,12 +130,12 @@ extern void static_key_slow_inc(struct static_key *key); | |||
130 | extern void static_key_slow_dec(struct static_key *key); | 130 | extern void static_key_slow_dec(struct static_key *key); |
131 | extern void jump_label_apply_nops(struct module *mod); | 131 | extern void jump_label_apply_nops(struct module *mod); |
132 | 132 | ||
133 | #define STATIC_KEY_INIT_TRUE ((struct static_key) \ | 133 | #define STATIC_KEY_INIT_TRUE \ |
134 | { .enabled = ATOMIC_INIT(1), \ | 134 | { .enabled = ATOMIC_INIT(1), \ |
135 | .entries = (void *)JUMP_TYPE_TRUE }) | 135 | .entries = (void *)JUMP_TYPE_TRUE } |
136 | #define STATIC_KEY_INIT_FALSE ((struct static_key) \ | 136 | #define STATIC_KEY_INIT_FALSE \ |
137 | { .enabled = ATOMIC_INIT(0), \ | 137 | { .enabled = ATOMIC_INIT(0), \ |
138 | .entries = (void *)JUMP_TYPE_FALSE }) | 138 | .entries = (void *)JUMP_TYPE_FALSE } |
139 | 139 | ||
140 | #else /* !HAVE_JUMP_LABEL */ | 140 | #else /* !HAVE_JUMP_LABEL */ |
141 | 141 | ||
@@ -183,10 +183,8 @@ static inline int jump_label_apply_nops(struct module *mod) | |||
183 | return 0; | 183 | return 0; |
184 | } | 184 | } |
185 | 185 | ||
186 | #define STATIC_KEY_INIT_TRUE ((struct static_key) \ | 186 | #define STATIC_KEY_INIT_TRUE { .enabled = ATOMIC_INIT(1) } |
187 | { .enabled = ATOMIC_INIT(1) }) | 187 | #define STATIC_KEY_INIT_FALSE { .enabled = ATOMIC_INIT(0) } |
188 | #define STATIC_KEY_INIT_FALSE ((struct static_key) \ | ||
189 | { .enabled = ATOMIC_INIT(0) }) | ||
190 | 188 | ||
191 | #endif /* HAVE_JUMP_LABEL */ | 189 | #endif /* HAVE_JUMP_LABEL */ |
192 | 190 | ||
@@ -218,6 +216,137 @@ static inline void static_key_disable(struct static_key *key) | |||
218 | static_key_slow_dec(key); | 216 | static_key_slow_dec(key); |
219 | } | 217 | } |
220 | 218 | ||
219 | /* -------------------------------------------------------------------------- */ | ||
220 | |||
221 | /* | ||
222 | * Two type wrappers around static_key, such that we can use compile time | ||
223 | * type differentiation to emit the right code. | ||
224 | * | ||
225 | * All the below code is macros in order to play type games. | ||
226 | */ | ||
227 | |||
228 | struct static_key_true { | ||
229 | struct static_key key; | ||
230 | }; | ||
231 | |||
232 | struct static_key_false { | ||
233 | struct static_key key; | ||
234 | }; | ||
235 | |||
236 | #define STATIC_KEY_TRUE_INIT (struct static_key_true) { .key = STATIC_KEY_INIT_TRUE, } | ||
237 | #define STATIC_KEY_FALSE_INIT (struct static_key_false){ .key = STATIC_KEY_INIT_FALSE, } | ||
238 | |||
239 | #define DEFINE_STATIC_KEY_TRUE(name) \ | ||
240 | struct static_key_true name = STATIC_KEY_TRUE_INIT | ||
241 | |||
242 | #define DEFINE_STATIC_KEY_FALSE(name) \ | ||
243 | struct static_key_false name = STATIC_KEY_FALSE_INIT | ||
244 | |||
245 | #ifdef HAVE_JUMP_LABEL | ||
246 | |||
247 | /* | ||
248 | * Combine the right initial value (type) with the right branch order | ||
249 | * to generate the desired result. | ||
250 | * | ||
251 | * | ||
252 | * type\branch| likely (1) | unlikely (0) | ||
253 | * -----------+-----------------------+------------------ | ||
254 | * | | | ||
255 | * true (1) | ... | ... | ||
256 | * | NOP | JMP L | ||
257 | * | <br-stmts> | 1: ... | ||
258 | * | L: ... | | ||
259 | * | | | ||
260 | * | | L: <br-stmts> | ||
261 | * | | jmp 1b | ||
262 | * | | | ||
263 | * -----------+-----------------------+------------------ | ||
264 | * | | | ||
265 | * false (0) | ... | ... | ||
266 | * | JMP L | NOP | ||
267 | * | <br-stmts> | 1: ... | ||
268 | * | L: ... | | ||
269 | * | | | ||
270 | * | | L: <br-stmts> | ||
271 | * | | jmp 1b | ||
272 | * | | | ||
273 | * -----------+-----------------------+------------------ | ||
274 | * | ||
275 | * The initial value is encoded in the LSB of static_key::entries, | ||
276 | * type: 0 = false, 1 = true. | ||
277 | * | ||
278 | * The branch type is encoded in the LSB of jump_entry::key, | ||
279 | * branch: 0 = unlikely, 1 = likely. | ||
280 | * | ||
281 | * This gives the following logic table: | ||
282 | * | ||
283 | * enabled type branch instuction | ||
284 | * -----------------------------+----------- | ||
285 | * 0 0 0 | NOP | ||
286 | * 0 0 1 | JMP | ||
287 | * 0 1 0 | NOP | ||
288 | * 0 1 1 | JMP | ||
289 | * | ||
290 | * 1 0 0 | JMP | ||
291 | * 1 0 1 | NOP | ||
292 | * 1 1 0 | JMP | ||
293 | * 1 1 1 | NOP | ||
294 | * | ||
295 | * Which gives the following functions: | ||
296 | * | ||
297 | * dynamic: instruction = enabled ^ branch | ||
298 | * static: instruction = type ^ branch | ||
299 | * | ||
300 | * See jump_label_type() / jump_label_init_type(). | ||
301 | */ | ||
302 | |||
303 | extern bool ____wrong_branch_error(void); | ||
304 | |||
305 | #define static_branch_likely(x) \ | ||
306 | ({ \ | ||
307 | bool branch; \ | ||
308 | if (__builtin_types_compatible_p(typeof(*x), struct static_key_true)) \ | ||
309 | branch = !arch_static_branch(&(x)->key, true); \ | ||
310 | else if (__builtin_types_compatible_p(typeof(*x), struct static_key_false)) \ | ||
311 | branch = !arch_static_branch_jump(&(x)->key, true); \ | ||
312 | else \ | ||
313 | branch = ____wrong_branch_error(); \ | ||
314 | branch; \ | ||
315 | }) | ||
316 | |||
317 | #define static_branch_unlikely(x) \ | ||
318 | ({ \ | ||
319 | bool branch; \ | ||
320 | if (__builtin_types_compatible_p(typeof(*x), struct static_key_true)) \ | ||
321 | branch = arch_static_branch_jump(&(x)->key, false); \ | ||
322 | else if (__builtin_types_compatible_p(typeof(*x), struct static_key_false)) \ | ||
323 | branch = arch_static_branch(&(x)->key, false); \ | ||
324 | else \ | ||
325 | branch = ____wrong_branch_error(); \ | ||
326 | branch; \ | ||
327 | }) | ||
328 | |||
329 | #else /* !HAVE_JUMP_LABEL */ | ||
330 | |||
331 | #define static_branch_likely(x) likely(static_key_enabled(&(x)->key)) | ||
332 | #define static_branch_unlikely(x) unlikely(static_key_enabled(&(x)->key)) | ||
333 | |||
334 | #endif /* HAVE_JUMP_LABEL */ | ||
335 | |||
336 | /* | ||
337 | * Advanced usage; refcount, branch is enabled when: count != 0 | ||
338 | */ | ||
339 | |||
340 | #define static_branch_inc(x) static_key_slow_inc(&(x)->key) | ||
341 | #define static_branch_dec(x) static_key_slow_dec(&(x)->key) | ||
342 | |||
343 | /* | ||
344 | * Normal usage; boolean enable/disable. | ||
345 | */ | ||
346 | |||
347 | #define static_branch_enable(x) static_key_enable(&(x)->key) | ||
348 | #define static_branch_disable(x) static_key_disable(&(x)->key) | ||
349 | |||
221 | #endif /* _LINUX_JUMP_LABEL_H */ | 350 | #endif /* _LINUX_JUMP_LABEL_H */ |
222 | 351 | ||
223 | #endif /* __ASSEMBLY__ */ | 352 | #endif /* __ASSEMBLY__ */ |
diff --git a/kernel/jump_label.c b/kernel/jump_label.c index 2e7cc1e4b4b5..8fd00d892286 100644 --- a/kernel/jump_label.c +++ b/kernel/jump_label.c | |||
@@ -165,16 +165,22 @@ static inline bool static_key_type(struct static_key *key) | |||
165 | 165 | ||
166 | static inline struct static_key *jump_entry_key(struct jump_entry *entry) | 166 | static inline struct static_key *jump_entry_key(struct jump_entry *entry) |
167 | { | 167 | { |
168 | return (struct static_key *)((unsigned long)entry->key); | 168 | return (struct static_key *)((unsigned long)entry->key & ~1UL); |
169 | } | ||
170 | |||
171 | static bool jump_entry_branch(struct jump_entry *entry) | ||
172 | { | ||
173 | return (unsigned long)entry->key & 1UL; | ||
169 | } | 174 | } |
170 | 175 | ||
171 | static enum jump_label_type jump_label_type(struct jump_entry *entry) | 176 | static enum jump_label_type jump_label_type(struct jump_entry *entry) |
172 | { | 177 | { |
173 | struct static_key *key = jump_entry_key(entry); | 178 | struct static_key *key = jump_entry_key(entry); |
174 | bool enabled = static_key_enabled(key); | 179 | bool enabled = static_key_enabled(key); |
175 | bool type = static_key_type(key); | 180 | bool branch = jump_entry_branch(entry); |
176 | 181 | ||
177 | return enabled ^ type; | 182 | /* See the comment in linux/jump_label.h */ |
183 | return enabled ^ branch; | ||
178 | } | 184 | } |
179 | 185 | ||
180 | static void __jump_label_update(struct static_key *key, | 186 | static void __jump_label_update(struct static_key *key, |
@@ -205,7 +211,10 @@ void __init jump_label_init(void) | |||
205 | for (iter = iter_start; iter < iter_stop; iter++) { | 211 | for (iter = iter_start; iter < iter_stop; iter++) { |
206 | struct static_key *iterk; | 212 | struct static_key *iterk; |
207 | 213 | ||
208 | arch_jump_label_transform_static(iter, jump_label_type(iter)); | 214 | /* rewrite NOPs */ |
215 | if (jump_label_type(iter) == JUMP_LABEL_NOP) | ||
216 | arch_jump_label_transform_static(iter, JUMP_LABEL_NOP); | ||
217 | |||
209 | iterk = jump_entry_key(iter); | 218 | iterk = jump_entry_key(iter); |
210 | if (iterk == key) | 219 | if (iterk == key) |
211 | continue; | 220 | continue; |
@@ -225,6 +234,16 @@ void __init jump_label_init(void) | |||
225 | 234 | ||
226 | #ifdef CONFIG_MODULES | 235 | #ifdef CONFIG_MODULES |
227 | 236 | ||
237 | static enum jump_label_type jump_label_init_type(struct jump_entry *entry) | ||
238 | { | ||
239 | struct static_key *key = jump_entry_key(entry); | ||
240 | bool type = static_key_type(key); | ||
241 | bool branch = jump_entry_branch(entry); | ||
242 | |||
243 | /* See the comment in linux/jump_label.h */ | ||
244 | return type ^ branch; | ||
245 | } | ||
246 | |||
228 | struct static_key_mod { | 247 | struct static_key_mod { |
229 | struct static_key_mod *next; | 248 | struct static_key_mod *next; |
230 | struct jump_entry *entries; | 249 | struct jump_entry *entries; |
@@ -276,8 +295,11 @@ void jump_label_apply_nops(struct module *mod) | |||
276 | if (iter_start == iter_stop) | 295 | if (iter_start == iter_stop) |
277 | return; | 296 | return; |
278 | 297 | ||
279 | for (iter = iter_start; iter < iter_stop; iter++) | 298 | for (iter = iter_start; iter < iter_stop; iter++) { |
280 | arch_jump_label_transform_static(iter, JUMP_LABEL_NOP); | 299 | /* Only write NOPs for arch_branch_static(). */ |
300 | if (jump_label_init_type(iter) == JUMP_LABEL_NOP) | ||
301 | arch_jump_label_transform_static(iter, JUMP_LABEL_NOP); | ||
302 | } | ||
281 | } | 303 | } |
282 | 304 | ||
283 | static int jump_label_add_module(struct module *mod) | 305 | static int jump_label_add_module(struct module *mod) |
@@ -318,7 +340,8 @@ static int jump_label_add_module(struct module *mod) | |||
318 | jlm->next = key->next; | 340 | jlm->next = key->next; |
319 | key->next = jlm; | 341 | key->next = jlm; |
320 | 342 | ||
321 | if (jump_label_type(iter) == JUMP_LABEL_JMP) | 343 | /* Only update if we've changed from our initial state */ |
344 | if (jump_label_type(iter) != jump_label_init_type(iter)) | ||
322 | __jump_label_update(key, iter, iter_stop); | 345 | __jump_label_update(key, iter, iter_stop); |
323 | } | 346 | } |
324 | 347 | ||