blob: f7c69580fea79d6b2e4096bf3fefbea89247aa71 [file] [log] [blame]
Jason Baronbf5438fc2010-09-17 11:09:00 -04001#ifndef _LINUX_JUMP_LABEL_H
2#define _LINUX_JUMP_LABEL_H
3
Peter Zijlstraefb30402012-01-26 13:32:15 +01004/*
5 * Jump label support
6 *
7 * Copyright (C) 2009-2012 Jason Baron <jbaron@redhat.com>
8 * Copyright (C) 2011-2012 Peter Zijlstra <pzijlstr@redhat.com>
9 *
10 * Jump labels provide an interface to generate dynamic branches using
11 * self-modifying code. Assuming toolchain and architecture support the result
12 * of a "if (static_branch(&key))" statement is a unconditional branch (which
13 * defaults to false - and the true block is placed out of line).
14 *
15 * However at runtime we can change the 'static' branch target using
16 * jump_label_{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
18 * that particular key will point to the (out of line) true block.
19 *
20 * Since this relies on modifying code the jump_label_{inc,dec}() functions
21 * must be considered absolute slow paths (machine wide synchronization etc.).
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
24 * effect is a single NOP of appropriate size. The on case will patch in a jump
25 * to the out-of-line block.
26 *
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)
29 * cause significant performance degradation. Struct jump_label_key_deferred and
30 * jump_label_dec_deferred() provide for this.
31 *
32 * Lacking toolchain and or architecture support, it falls back to a simple
33 * conditional branch.
34 */
35
Jason Barond430d3d2011-03-16 17:29:47 -040036#include <linux/types.h>
37#include <linux/compiler.h>
Gleb Natapovb2029522011-11-27 17:59:09 +020038#include <linux/workqueue.h>
Jason Barond430d3d2011-03-16 17:29:47 -040039
Steven Rostedt45f81b12010-10-29 12:33:43 -040040#if defined(CC_HAVE_ASM_GOTO) && defined(CONFIG_JUMP_LABEL)
Jason Barond430d3d2011-03-16 17:29:47 -040041
42struct jump_label_key {
43 atomic_t enabled;
44 struct jump_entry *entries;
45#ifdef CONFIG_MODULES
46 struct jump_label_mod *next;
47#endif
48};
49
Gleb Natapovb2029522011-11-27 17:59:09 +020050struct jump_label_key_deferred {
51 struct jump_label_key key;
52 unsigned long timeout;
53 struct delayed_work work;
54};
55
Jason Baronbf5438fc2010-09-17 11:09:00 -040056# include <asm/jump_label.h>
57# define HAVE_JUMP_LABEL
Jeremy Fitzhardinge97ce2c82011-10-12 16:17:54 -070058#endif /* CC_HAVE_ASM_GOTO && CONFIG_JUMP_LABEL */
Jason Baronbf5438fc2010-09-17 11:09:00 -040059
60enum jump_label_type {
Jason Barond430d3d2011-03-16 17:29:47 -040061 JUMP_LABEL_DISABLE = 0,
Jason Baronbf5438fc2010-09-17 11:09:00 -040062 JUMP_LABEL_ENABLE,
Jason Baronbf5438fc2010-09-17 11:09:00 -040063};
64
65struct module;
66
67#ifdef HAVE_JUMP_LABEL
68
Jason Barond430d3d2011-03-16 17:29:47 -040069#ifdef CONFIG_MODULES
Jeremy Fitzhardinged5d9a3b2011-09-28 17:45:15 -070070#define JUMP_LABEL_INIT {ATOMIC_INIT(0), NULL, NULL}
Jason Barond430d3d2011-03-16 17:29:47 -040071#else
Jeremy Fitzhardinged5d9a3b2011-09-28 17:45:15 -070072#define JUMP_LABEL_INIT {ATOMIC_INIT(0), NULL}
Jason Barond430d3d2011-03-16 17:29:47 -040073#endif
74
75static __always_inline bool static_branch(struct jump_label_key *key)
76{
77 return arch_static_branch(key);
78}
79
Jason Baronbf5438fc2010-09-17 11:09:00 -040080extern struct jump_entry __start___jump_table[];
81extern struct jump_entry __stop___jump_table[];
82
Jeremy Fitzhardinge97ce2c82011-10-12 16:17:54 -070083extern void jump_label_init(void);
Jason Baron91bad2f2010-10-01 17:23:48 -040084extern void jump_label_lock(void);
85extern void jump_label_unlock(void);
Jason Baronbf5438fc2010-09-17 11:09:00 -040086extern void arch_jump_label_transform(struct jump_entry *entry,
Jeremy Fitzhardinge37348802011-09-29 11:10:05 -070087 enum jump_label_type type);
Jeremy Fitzhardinge20284aa2011-10-03 11:01:46 -070088extern void arch_jump_label_transform_static(struct jump_entry *entry,
89 enum jump_label_type type);
Jason Baron4c3ef6d2010-09-17 11:09:08 -040090extern int jump_label_text_reserved(void *start, void *end);
Jason Barond430d3d2011-03-16 17:29:47 -040091extern void jump_label_inc(struct jump_label_key *key);
92extern void jump_label_dec(struct jump_label_key *key);
Gleb Natapovb2029522011-11-27 17:59:09 +020093extern void jump_label_dec_deferred(struct jump_label_key_deferred *key);
Jason Barond430d3d2011-03-16 17:29:47 -040094extern bool jump_label_enabled(struct jump_label_key *key);
95extern void jump_label_apply_nops(struct module *mod);
Gleb Natapovb2029522011-11-27 17:59:09 +020096extern void jump_label_rate_limit(struct jump_label_key_deferred *key,
97 unsigned long rl);
Jason Baronbf5438fc2010-09-17 11:09:00 -040098
Jeremy Fitzhardinge97ce2c82011-10-12 16:17:54 -070099#else /* !HAVE_JUMP_LABEL */
Jason Baronbf5438fc2010-09-17 11:09:00 -0400100
Arun Sharma600634972011-07-26 16:09:06 -0700101#include <linux/atomic.h>
Jason Baronbf5438fc2010-09-17 11:09:00 -0400102
Jason Barond430d3d2011-03-16 17:29:47 -0400103#define JUMP_LABEL_INIT {ATOMIC_INIT(0)}
Jason Baronbf5438fc2010-09-17 11:09:00 -0400104
Jason Barond430d3d2011-03-16 17:29:47 -0400105struct jump_label_key {
106 atomic_t enabled;
107};
Jason Baronbf5438fc2010-09-17 11:09:00 -0400108
Jeremy Fitzhardinge97ce2c82011-10-12 16:17:54 -0700109static __always_inline void jump_label_init(void)
110{
111}
112
Gleb Natapovb2029522011-11-27 17:59:09 +0200113struct jump_label_key_deferred {
114 struct jump_label_key key;
115};
116
Jason Barond430d3d2011-03-16 17:29:47 -0400117static __always_inline bool static_branch(struct jump_label_key *key)
Jason Baronbf5438fc2010-09-17 11:09:00 -0400118{
Jason Barond430d3d2011-03-16 17:29:47 -0400119 if (unlikely(atomic_read(&key->enabled)))
120 return true;
121 return false;
122}
123
124static inline void jump_label_inc(struct jump_label_key *key)
125{
126 atomic_inc(&key->enabled);
127}
128
129static inline void jump_label_dec(struct jump_label_key *key)
130{
131 atomic_dec(&key->enabled);
Jason Baronbf5438fc2010-09-17 11:09:00 -0400132}
133
Gleb Natapovb2029522011-11-27 17:59:09 +0200134static inline void jump_label_dec_deferred(struct jump_label_key_deferred *key)
135{
136 jump_label_dec(&key->key);
137}
138
Jason Baron4c3ef6d2010-09-17 11:09:08 -0400139static inline int jump_label_text_reserved(void *start, void *end)
140{
141 return 0;
142}
143
Jason Baron91bad2f2010-10-01 17:23:48 -0400144static inline void jump_label_lock(void) {}
145static inline void jump_label_unlock(void) {}
146
Jason Barond430d3d2011-03-16 17:29:47 -0400147static inline bool jump_label_enabled(struct jump_label_key *key)
148{
149 return !!atomic_read(&key->enabled);
150}
Jason Baronbf5438fc2010-09-17 11:09:00 -0400151
Jason Barond430d3d2011-03-16 17:29:47 -0400152static inline int jump_label_apply_nops(struct module *mod)
153{
154 return 0;
155}
Gleb Natapovb2029522011-11-27 17:59:09 +0200156
157static inline void jump_label_rate_limit(struct jump_label_key_deferred *key,
158 unsigned long rl)
159{
160}
Jeremy Fitzhardinge97ce2c82011-10-12 16:17:54 -0700161#endif /* HAVE_JUMP_LABEL */
Jason Barond430d3d2011-03-16 17:29:47 -0400162
Peter Zijlstraac99b862011-07-06 14:20:14 +0200163#define jump_label_key_enabled ((struct jump_label_key){ .enabled = ATOMIC_INIT(1), })
164#define jump_label_key_disabled ((struct jump_label_key){ .enabled = ATOMIC_INIT(0), })
165
Jeremy Fitzhardinge97ce2c82011-10-12 16:17:54 -0700166#endif /* _LINUX_JUMP_LABEL_H */