diff options
author | Ingo Molnar <mingo@elte.hu> | 2012-02-24 02:31:31 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2012-02-24 04:05:59 -0500 |
commit | c5905afb0ee6550b42c49213da1c22d67316c194 (patch) | |
tree | 253fdb322e6e5b257ffda3b9b66bce90a473a6f7 /include/linux/jump_label.h | |
parent | 1cfa60dc7d7c7cc774a44eee47ff135a644a1f31 (diff) |
static keys: Introduce 'struct static_key', static_key_true()/false() and static_key_slow_[inc|dec]()
So here's a boot tested patch on top of Jason's series that does
all the cleanups I talked about and turns jump labels into a
more intuitive to use facility. It should also address the
various misconceptions and confusions that surround jump labels.
Typical usage scenarios:
#include <linux/static_key.h>
struct static_key key = STATIC_KEY_INIT_TRUE;
if (static_key_false(&key))
do unlikely code
else
do likely code
Or:
if (static_key_true(&key))
do likely code
else
do unlikely code
The static key is modified via:
static_key_slow_inc(&key);
...
static_key_slow_dec(&key);
The 'slow' prefix makes it abundantly clear that this is an
expensive operation.
I've updated all in-kernel code to use this everywhere. Note
that I (intentionally) have not pushed through the rename
blindly through to the lowest levels: the actual jump-label
patching arch facility should be named like that, so we want to
decouple jump labels from the static-key facility a bit.
On non-jump-label enabled architectures static keys default to
likely()/unlikely() branches.
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Acked-by: Jason Baron <jbaron@redhat.com>
Acked-by: Steven Rostedt <rostedt@goodmis.org>
Cc: a.p.zijlstra@chello.nl
Cc: mathieu.desnoyers@efficios.com
Cc: davem@davemloft.net
Cc: ddaney.cavm@gmail.com
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Link: http://lkml.kernel.org/r/20120222085809.GA26397@elte.hu
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'include/linux/jump_label.h')
-rw-r--r-- | include/linux/jump_label.h | 139 |
1 files changed, 100 insertions, 39 deletions
diff --git a/include/linux/jump_label.h b/include/linux/jump_label.h index f7c69580fea7..2172da2d9bb4 100644 --- a/include/linux/jump_label.h +++ b/include/linux/jump_label.h | |||
@@ -9,15 +9,15 @@ | |||
9 | * | 9 | * |
10 | * Jump labels provide an interface to generate dynamic branches using | 10 | * Jump labels provide an interface to generate dynamic branches using |
11 | * self-modifying code. Assuming toolchain and architecture support the result | 11 | * self-modifying code. Assuming toolchain and architecture support the result |
12 | * of a "if (static_branch(&key))" statement is a unconditional branch (which | 12 | * of a "if (static_key_false(&key))" statement is a unconditional branch (which |
13 | * defaults to false - and the true block is placed out of line). | 13 | * defaults to false - and the true block is placed out of line). |
14 | * | 14 | * |
15 | * However at runtime we can change the 'static' branch target using | 15 | * However at runtime we can change the branch target using |
16 | * jump_label_{inc,dec}(). These function as a 'reference' count on the key | 16 | * static_key_slow_{inc,dec}(). These function as a 'reference' count on the key |
17 | * object and for as long as there are references all branches referring to | 17 | * object and for as long as there are references all branches referring to |
18 | * that particular key will point to the (out of line) true block. | 18 | * that particular key will point to the (out of line) true block. |
19 | * | 19 | * |
20 | * Since this relies on modifying code the jump_label_{inc,dec}() functions | 20 | * Since this relies on modifying code the static_key_slow_{inc,dec}() functions |
21 | * must be considered absolute slow paths (machine wide synchronization etc.). | 21 | * must be considered absolute slow paths (machine wide synchronization etc.). |
22 | * OTOH, since the affected branches are unconditional their runtime overhead | 22 | * OTOH, since the affected branches are unconditional their runtime overhead |
23 | * will be absolutely minimal, esp. in the default (off) case where the total | 23 | * will be absolutely minimal, esp. in the default (off) case where the total |
@@ -26,12 +26,26 @@ | |||
26 | * | 26 | * |
27 | * When the control is directly exposed to userspace it is prudent to delay the | 27 | * When the control is directly exposed to userspace it is prudent to delay the |
28 | * decrement to avoid high frequency code modifications which can (and do) | 28 | * decrement to avoid high frequency code modifications which can (and do) |
29 | * cause significant performance degradation. Struct jump_label_key_deferred and | 29 | * cause significant performance degradation. Struct static_key_deferred and |
30 | * jump_label_dec_deferred() provide for this. | 30 | * static_key_slow_dec_deferred() provide for this. |
31 | * | 31 | * |
32 | * Lacking toolchain and or architecture support, it falls back to a simple | 32 | * Lacking toolchain and or architecture support, it falls back to a simple |
33 | * conditional branch. | 33 | * conditional branch. |
34 | */ | 34 | * |
35 | * struct static_key my_key = STATIC_KEY_INIT_TRUE; | ||
36 | * | ||
37 | * if (static_key_true(&my_key)) { | ||
38 | * } | ||
39 | * | ||
40 | * will result in the true case being in-line and starts the key with a single | ||
41 | * reference. Mixing static_key_true() and static_key_false() on the same key is not | ||
42 | * allowed. | ||
43 | * | ||
44 | * Not initializing the key (static data is initialized to 0s anyway) is the | ||
45 | * same as using STATIC_KEY_INIT_FALSE and static_key_false() is | ||
46 | * equivalent with static_branch(). | ||
47 | * | ||
48 | */ | ||
35 | 49 | ||
36 | #include <linux/types.h> | 50 | #include <linux/types.h> |
37 | #include <linux/compiler.h> | 51 | #include <linux/compiler.h> |
@@ -39,16 +53,17 @@ | |||
39 | 53 | ||
40 | #if defined(CC_HAVE_ASM_GOTO) && defined(CONFIG_JUMP_LABEL) | 54 | #if defined(CC_HAVE_ASM_GOTO) && defined(CONFIG_JUMP_LABEL) |
41 | 55 | ||
42 | struct jump_label_key { | 56 | struct static_key { |
43 | atomic_t enabled; | 57 | atomic_t enabled; |
58 | /* Set lsb bit to 1 if branch is default true, 0 ot */ | ||
44 | struct jump_entry *entries; | 59 | struct jump_entry *entries; |
45 | #ifdef CONFIG_MODULES | 60 | #ifdef CONFIG_MODULES |
46 | struct jump_label_mod *next; | 61 | struct static_key_mod *next; |
47 | #endif | 62 | #endif |
48 | }; | 63 | }; |
49 | 64 | ||
50 | struct jump_label_key_deferred { | 65 | struct static_key_deferred { |
51 | struct jump_label_key key; | 66 | struct static_key key; |
52 | unsigned long timeout; | 67 | unsigned long timeout; |
53 | struct delayed_work work; | 68 | struct delayed_work work; |
54 | }; | 69 | }; |
@@ -66,13 +81,34 @@ struct module; | |||
66 | 81 | ||
67 | #ifdef HAVE_JUMP_LABEL | 82 | #ifdef HAVE_JUMP_LABEL |
68 | 83 | ||
69 | #ifdef CONFIG_MODULES | 84 | #define JUMP_LABEL_TRUE_BRANCH 1UL |
70 | #define JUMP_LABEL_INIT {ATOMIC_INIT(0), NULL, NULL} | 85 | |
71 | #else | 86 | static |
72 | #define JUMP_LABEL_INIT {ATOMIC_INIT(0), NULL} | 87 | inline struct jump_entry *jump_label_get_entries(struct static_key *key) |
73 | #endif | 88 | { |
89 | return (struct jump_entry *)((unsigned long)key->entries | ||
90 | & ~JUMP_LABEL_TRUE_BRANCH); | ||
91 | } | ||
92 | |||
93 | static inline bool jump_label_get_branch_default(struct static_key *key) | ||
94 | { | ||
95 | if ((unsigned long)key->entries & JUMP_LABEL_TRUE_BRANCH) | ||
96 | return true; | ||
97 | return false; | ||
98 | } | ||
99 | |||
100 | static __always_inline bool static_key_false(struct static_key *key) | ||
101 | { | ||
102 | return arch_static_branch(key); | ||
103 | } | ||
74 | 104 | ||
75 | static __always_inline bool static_branch(struct jump_label_key *key) | 105 | static __always_inline bool static_key_true(struct static_key *key) |
106 | { | ||
107 | return !static_key_false(key); | ||
108 | } | ||
109 | |||
110 | /* Deprecated. Please use 'static_key_false() instead. */ | ||
111 | static __always_inline bool static_branch(struct static_key *key) | ||
76 | { | 112 | { |
77 | return arch_static_branch(key); | 113 | return arch_static_branch(key); |
78 | } | 114 | } |
@@ -88,21 +124,24 @@ extern void arch_jump_label_transform(struct jump_entry *entry, | |||
88 | extern void arch_jump_label_transform_static(struct jump_entry *entry, | 124 | extern void arch_jump_label_transform_static(struct jump_entry *entry, |
89 | enum jump_label_type type); | 125 | enum jump_label_type type); |
90 | extern int jump_label_text_reserved(void *start, void *end); | 126 | extern int jump_label_text_reserved(void *start, void *end); |
91 | extern void jump_label_inc(struct jump_label_key *key); | 127 | extern void static_key_slow_inc(struct static_key *key); |
92 | extern void jump_label_dec(struct jump_label_key *key); | 128 | extern void static_key_slow_dec(struct static_key *key); |
93 | extern void jump_label_dec_deferred(struct jump_label_key_deferred *key); | 129 | extern void static_key_slow_dec_deferred(struct static_key_deferred *key); |
94 | extern bool jump_label_enabled(struct jump_label_key *key); | 130 | extern bool static_key_enabled(struct static_key *key); |
95 | extern void jump_label_apply_nops(struct module *mod); | 131 | extern void jump_label_apply_nops(struct module *mod); |
96 | extern void jump_label_rate_limit(struct jump_label_key_deferred *key, | 132 | extern void |
97 | unsigned long rl); | 133 | jump_label_rate_limit(struct static_key_deferred *key, unsigned long rl); |
134 | |||
135 | #define STATIC_KEY_INIT_TRUE ((struct static_key) \ | ||
136 | { .enabled = ATOMIC_INIT(1), .entries = (void *)1 }) | ||
137 | #define STATIC_KEY_INIT_FALSE ((struct static_key) \ | ||
138 | { .enabled = ATOMIC_INIT(0), .entries = (void *)0 }) | ||
98 | 139 | ||
99 | #else /* !HAVE_JUMP_LABEL */ | 140 | #else /* !HAVE_JUMP_LABEL */ |
100 | 141 | ||
101 | #include <linux/atomic.h> | 142 | #include <linux/atomic.h> |
102 | 143 | ||
103 | #define JUMP_LABEL_INIT {ATOMIC_INIT(0)} | 144 | struct static_key { |
104 | |||
105 | struct jump_label_key { | ||
106 | atomic_t enabled; | 145 | atomic_t enabled; |
107 | }; | 146 | }; |
108 | 147 | ||
@@ -110,30 +149,45 @@ static __always_inline void jump_label_init(void) | |||
110 | { | 149 | { |
111 | } | 150 | } |
112 | 151 | ||
113 | struct jump_label_key_deferred { | 152 | struct static_key_deferred { |
114 | struct jump_label_key key; | 153 | struct static_key key; |
115 | }; | 154 | }; |
116 | 155 | ||
117 | static __always_inline bool static_branch(struct jump_label_key *key) | 156 | static __always_inline bool static_key_false(struct static_key *key) |
157 | { | ||
158 | if (unlikely(atomic_read(&key->enabled)) > 0) | ||
159 | return true; | ||
160 | return false; | ||
161 | } | ||
162 | |||
163 | static __always_inline bool static_key_true(struct static_key *key) | ||
118 | { | 164 | { |
119 | if (unlikely(atomic_read(&key->enabled))) | 165 | if (likely(atomic_read(&key->enabled)) > 0) |
120 | return true; | 166 | return true; |
121 | return false; | 167 | return false; |
122 | } | 168 | } |
123 | 169 | ||
124 | static inline void jump_label_inc(struct jump_label_key *key) | 170 | /* Deprecated. Please use 'static_key_false() instead. */ |
171 | static __always_inline bool static_branch(struct static_key *key) | ||
172 | { | ||
173 | if (unlikely(atomic_read(&key->enabled)) > 0) | ||
174 | return true; | ||
175 | return false; | ||
176 | } | ||
177 | |||
178 | static inline void static_key_slow_inc(struct static_key *key) | ||
125 | { | 179 | { |
126 | atomic_inc(&key->enabled); | 180 | atomic_inc(&key->enabled); |
127 | } | 181 | } |
128 | 182 | ||
129 | static inline void jump_label_dec(struct jump_label_key *key) | 183 | static inline void static_key_slow_dec(struct static_key *key) |
130 | { | 184 | { |
131 | atomic_dec(&key->enabled); | 185 | atomic_dec(&key->enabled); |
132 | } | 186 | } |
133 | 187 | ||
134 | static inline void jump_label_dec_deferred(struct jump_label_key_deferred *key) | 188 | static inline void static_key_slow_dec_deferred(struct static_key_deferred *key) |
135 | { | 189 | { |
136 | jump_label_dec(&key->key); | 190 | static_key_slow_dec(&key->key); |
137 | } | 191 | } |
138 | 192 | ||
139 | static inline int jump_label_text_reserved(void *start, void *end) | 193 | static inline int jump_label_text_reserved(void *start, void *end) |
@@ -144,9 +198,9 @@ static inline int jump_label_text_reserved(void *start, void *end) | |||
144 | static inline void jump_label_lock(void) {} | 198 | static inline void jump_label_lock(void) {} |
145 | static inline void jump_label_unlock(void) {} | 199 | static inline void jump_label_unlock(void) {} |
146 | 200 | ||
147 | static inline bool jump_label_enabled(struct jump_label_key *key) | 201 | static inline bool static_key_enabled(struct static_key *key) |
148 | { | 202 | { |
149 | return !!atomic_read(&key->enabled); | 203 | return (atomic_read(&key->enabled) > 0); |
150 | } | 204 | } |
151 | 205 | ||
152 | static inline int jump_label_apply_nops(struct module *mod) | 206 | static inline int jump_label_apply_nops(struct module *mod) |
@@ -154,13 +208,20 @@ static inline int jump_label_apply_nops(struct module *mod) | |||
154 | return 0; | 208 | return 0; |
155 | } | 209 | } |
156 | 210 | ||
157 | static inline void jump_label_rate_limit(struct jump_label_key_deferred *key, | 211 | static inline void |
212 | jump_label_rate_limit(struct static_key_deferred *key, | ||
158 | unsigned long rl) | 213 | unsigned long rl) |
159 | { | 214 | { |
160 | } | 215 | } |
216 | |||
217 | #define STATIC_KEY_INIT_TRUE ((struct static_key) \ | ||
218 | { .enabled = ATOMIC_INIT(1) }) | ||
219 | #define STATIC_KEY_INIT_FALSE ((struct static_key) \ | ||
220 | { .enabled = ATOMIC_INIT(0) }) | ||
221 | |||
161 | #endif /* HAVE_JUMP_LABEL */ | 222 | #endif /* HAVE_JUMP_LABEL */ |
162 | 223 | ||
163 | #define jump_label_key_enabled ((struct jump_label_key){ .enabled = ATOMIC_INIT(1), }) | 224 | #define STATIC_KEY_INIT STATIC_KEY_INIT_FALSE |
164 | #define jump_label_key_disabled ((struct jump_label_key){ .enabled = ATOMIC_INIT(0), }) | 225 | #define jump_label_enabled static_key_enabled |
165 | 226 | ||
166 | #endif /* _LINUX_JUMP_LABEL_H */ | 227 | #endif /* _LINUX_JUMP_LABEL_H */ |