aboutsummaryrefslogtreecommitdiffstats
path: root/include/linux
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
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')
-rw-r--r--include/linux/jump_label.h139
-rw-r--r--include/linux/netdevice.h4
-rw-r--r--include/linux/netfilter.h6
-rw-r--r--include/linux/perf_event.h12
-rw-r--r--include/linux/static_key.h1
-rw-r--r--include/linux/tracepoint.h8
6 files changed, 116 insertions, 54 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 */
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 0eac07c95255..7dfaae7846ab 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -214,8 +214,8 @@ enum {
214#include <linux/skbuff.h> 214#include <linux/skbuff.h>
215 215
216#ifdef CONFIG_RPS 216#ifdef CONFIG_RPS
217#include <linux/jump_label.h> 217#include <linux/static_key.h>
218extern struct jump_label_key rps_needed; 218extern struct static_key rps_needed;
219#endif 219#endif
220 220
221struct neighbour; 221struct neighbour;
diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h
index b809265607d0..29734be334c1 100644
--- a/include/linux/netfilter.h
+++ b/include/linux/netfilter.h
@@ -163,13 +163,13 @@ extern struct ctl_path nf_net_ipv4_netfilter_sysctl_path[];
163extern struct list_head nf_hooks[NFPROTO_NUMPROTO][NF_MAX_HOOKS]; 163extern struct list_head nf_hooks[NFPROTO_NUMPROTO][NF_MAX_HOOKS];
164 164
165#if defined(CONFIG_JUMP_LABEL) 165#if defined(CONFIG_JUMP_LABEL)
166#include <linux/jump_label.h> 166#include <linux/static_key.h>
167extern struct jump_label_key nf_hooks_needed[NFPROTO_NUMPROTO][NF_MAX_HOOKS]; 167extern struct static_key nf_hooks_needed[NFPROTO_NUMPROTO][NF_MAX_HOOKS];
168static inline bool nf_hooks_active(u_int8_t pf, unsigned int hook) 168static inline bool nf_hooks_active(u_int8_t pf, unsigned int hook)
169{ 169{
170 if (__builtin_constant_p(pf) && 170 if (__builtin_constant_p(pf) &&
171 __builtin_constant_p(hook)) 171 __builtin_constant_p(hook))
172 return static_branch(&nf_hooks_needed[pf][hook]); 172 return static_key_false(&nf_hooks_needed[pf][hook]);
173 173
174 return !list_empty(&nf_hooks[pf][hook]); 174 return !list_empty(&nf_hooks[pf][hook]);
175} 175}
diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h
index 412b790f5da6..0d21e6f1cf53 100644
--- a/include/linux/perf_event.h
+++ b/include/linux/perf_event.h
@@ -514,7 +514,7 @@ struct perf_guest_info_callbacks {
514#include <linux/ftrace.h> 514#include <linux/ftrace.h>
515#include <linux/cpu.h> 515#include <linux/cpu.h>
516#include <linux/irq_work.h> 516#include <linux/irq_work.h>
517#include <linux/jump_label.h> 517#include <linux/static_key.h>
518#include <linux/atomic.h> 518#include <linux/atomic.h>
519#include <asm/local.h> 519#include <asm/local.h>
520 520
@@ -1038,7 +1038,7 @@ static inline int is_software_event(struct perf_event *event)
1038 return event->pmu->task_ctx_nr == perf_sw_context; 1038 return event->pmu->task_ctx_nr == perf_sw_context;
1039} 1039}
1040 1040
1041extern struct jump_label_key perf_swevent_enabled[PERF_COUNT_SW_MAX]; 1041extern struct static_key perf_swevent_enabled[PERF_COUNT_SW_MAX];
1042 1042
1043extern void __perf_sw_event(u32, u64, struct pt_regs *, u64); 1043extern void __perf_sw_event(u32, u64, struct pt_regs *, u64);
1044 1044
@@ -1066,7 +1066,7 @@ perf_sw_event(u32 event_id, u64 nr, struct pt_regs *regs, u64 addr)
1066{ 1066{
1067 struct pt_regs hot_regs; 1067 struct pt_regs hot_regs;
1068 1068
1069 if (static_branch(&perf_swevent_enabled[event_id])) { 1069 if (static_key_false(&perf_swevent_enabled[event_id])) {
1070 if (!regs) { 1070 if (!regs) {
1071 perf_fetch_caller_regs(&hot_regs); 1071 perf_fetch_caller_regs(&hot_regs);
1072 regs = &hot_regs; 1072 regs = &hot_regs;
@@ -1075,12 +1075,12 @@ perf_sw_event(u32 event_id, u64 nr, struct pt_regs *regs, u64 addr)
1075 } 1075 }
1076} 1076}
1077 1077
1078extern struct jump_label_key_deferred perf_sched_events; 1078extern struct static_key_deferred perf_sched_events;
1079 1079
1080static inline void perf_event_task_sched_in(struct task_struct *prev, 1080static inline void perf_event_task_sched_in(struct task_struct *prev,
1081 struct task_struct *task) 1081 struct task_struct *task)
1082{ 1082{
1083 if (static_branch(&perf_sched_events.key)) 1083 if (static_key_false(&perf_sched_events.key))
1084 __perf_event_task_sched_in(prev, task); 1084 __perf_event_task_sched_in(prev, task);
1085} 1085}
1086 1086
@@ -1089,7 +1089,7 @@ static inline void perf_event_task_sched_out(struct task_struct *prev,
1089{ 1089{
1090 perf_sw_event(PERF_COUNT_SW_CONTEXT_SWITCHES, 1, NULL, 0); 1090 perf_sw_event(PERF_COUNT_SW_CONTEXT_SWITCHES, 1, NULL, 0);
1091 1091
1092 if (static_branch(&perf_sched_events.key)) 1092 if (static_key_false(&perf_sched_events.key))
1093 __perf_event_task_sched_out(prev, next); 1093 __perf_event_task_sched_out(prev, next);
1094} 1094}
1095 1095
diff --git a/include/linux/static_key.h b/include/linux/static_key.h
new file mode 100644
index 000000000000..27bd3f8a0857
--- /dev/null
+++ b/include/linux/static_key.h
@@ -0,0 +1 @@
#include <linux/jump_label.h>
diff --git a/include/linux/tracepoint.h b/include/linux/tracepoint.h
index fc36da97ff7e..bd96ecd0e05c 100644
--- a/include/linux/tracepoint.h
+++ b/include/linux/tracepoint.h
@@ -17,7 +17,7 @@
17#include <linux/errno.h> 17#include <linux/errno.h>
18#include <linux/types.h> 18#include <linux/types.h>
19#include <linux/rcupdate.h> 19#include <linux/rcupdate.h>
20#include <linux/jump_label.h> 20#include <linux/static_key.h>
21 21
22struct module; 22struct module;
23struct tracepoint; 23struct tracepoint;
@@ -29,7 +29,7 @@ struct tracepoint_func {
29 29
30struct tracepoint { 30struct tracepoint {
31 const char *name; /* Tracepoint name */ 31 const char *name; /* Tracepoint name */
32 struct jump_label_key key; 32 struct static_key key;
33 void (*regfunc)(void); 33 void (*regfunc)(void);
34 void (*unregfunc)(void); 34 void (*unregfunc)(void);
35 struct tracepoint_func __rcu *funcs; 35 struct tracepoint_func __rcu *funcs;
@@ -145,7 +145,7 @@ static inline void tracepoint_synchronize_unregister(void)
145 extern struct tracepoint __tracepoint_##name; \ 145 extern struct tracepoint __tracepoint_##name; \
146 static inline void trace_##name(proto) \ 146 static inline void trace_##name(proto) \
147 { \ 147 { \
148 if (static_branch(&__tracepoint_##name.key)) \ 148 if (static_key_false(&__tracepoint_##name.key)) \
149 __DO_TRACE(&__tracepoint_##name, \ 149 __DO_TRACE(&__tracepoint_##name, \
150 TP_PROTO(data_proto), \ 150 TP_PROTO(data_proto), \
151 TP_ARGS(data_args), \ 151 TP_ARGS(data_args), \
@@ -188,7 +188,7 @@ static inline void tracepoint_synchronize_unregister(void)
188 __attribute__((section("__tracepoints_strings"))) = #name; \ 188 __attribute__((section("__tracepoints_strings"))) = #name; \
189 struct tracepoint __tracepoint_##name \ 189 struct tracepoint __tracepoint_##name \
190 __attribute__((section("__tracepoints"))) = \ 190 __attribute__((section("__tracepoints"))) = \
191 { __tpstrtab_##name, JUMP_LABEL_INIT, reg, unreg, NULL };\ 191 { __tpstrtab_##name, STATIC_KEY_INIT_FALSE, reg, unreg, NULL };\
192 static struct tracepoint * const __tracepoint_ptr_##name __used \ 192 static struct tracepoint * const __tracepoint_ptr_##name __used \
193 __attribute__((section("__tracepoints_ptrs"))) = \ 193 __attribute__((section("__tracepoints_ptrs"))) = \
194 &__tracepoint_##name; 194 &__tracepoint_##name;