aboutsummaryrefslogtreecommitdiffstats
path: root/include/linux/jump_label.h
diff options
context:
space:
mode:
authorIngo Molnar <mingo@elte.hu>2012-02-24 02:31:31 -0500
committerIngo Molnar <mingo@elte.hu>2012-02-24 04:05:59 -0500
commitc5905afb0ee6550b42c49213da1c22d67316c194 (patch)
tree253fdb322e6e5b257ffda3b9b66bce90a473a6f7 /include/linux/jump_label.h
parent1cfa60dc7d7c7cc774a44eee47ff135a644a1f31 (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.h139
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
42struct jump_label_key { 56struct 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
50struct jump_label_key_deferred { 65struct 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 86static
72#define JUMP_LABEL_INIT {ATOMIC_INIT(0), NULL} 87inline 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
93static 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
100static __always_inline bool static_key_false(struct static_key *key)
101{
102 return arch_static_branch(key);
103}
74 104
75static __always_inline bool static_branch(struct jump_label_key *key) 105static __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. */
111static __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,
88extern void arch_jump_label_transform_static(struct jump_entry *entry, 124extern void arch_jump_label_transform_static(struct jump_entry *entry,
89 enum jump_label_type type); 125 enum jump_label_type type);
90extern int jump_label_text_reserved(void *start, void *end); 126extern int jump_label_text_reserved(void *start, void *end);
91extern void jump_label_inc(struct jump_label_key *key); 127extern void static_key_slow_inc(struct static_key *key);
92extern void jump_label_dec(struct jump_label_key *key); 128extern void static_key_slow_dec(struct static_key *key);
93extern void jump_label_dec_deferred(struct jump_label_key_deferred *key); 129extern void static_key_slow_dec_deferred(struct static_key_deferred *key);
94extern bool jump_label_enabled(struct jump_label_key *key); 130extern bool static_key_enabled(struct static_key *key);
95extern void jump_label_apply_nops(struct module *mod); 131extern void jump_label_apply_nops(struct module *mod);
96extern void jump_label_rate_limit(struct jump_label_key_deferred *key, 132extern void
97 unsigned long rl); 133jump_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)} 144struct static_key {
104
105struct 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
113struct jump_label_key_deferred { 152struct static_key_deferred {
114 struct jump_label_key key; 153 struct static_key key;
115}; 154};
116 155
117static __always_inline bool static_branch(struct jump_label_key *key) 156static __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
163static __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
124static inline void jump_label_inc(struct jump_label_key *key) 170/* Deprecated. Please use 'static_key_false() instead. */
171static __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
178static 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
129static inline void jump_label_dec(struct jump_label_key *key) 183static 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
134static inline void jump_label_dec_deferred(struct jump_label_key_deferred *key) 188static 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
139static inline int jump_label_text_reserved(void *start, void *end) 193static 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)
144static inline void jump_label_lock(void) {} 198static inline void jump_label_lock(void) {}
145static inline void jump_label_unlock(void) {} 199static inline void jump_label_unlock(void) {}
146 200
147static inline bool jump_label_enabled(struct jump_label_key *key) 201static 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
152static inline int jump_label_apply_nops(struct module *mod) 206static 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
157static inline void jump_label_rate_limit(struct jump_label_key_deferred *key, 211static inline void
212jump_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 */