xref: /linux-6.15/include/linux/jump_label.h (revision e2b6e5e4)
1b2441318SGreg Kroah-Hartman /* SPDX-License-Identifier: GPL-2.0 */
2bf5438fcSJason Baron #ifndef _LINUX_JUMP_LABEL_H
3bf5438fcSJason Baron #define _LINUX_JUMP_LABEL_H
4bf5438fcSJason Baron 
5efb3040dSPeter Zijlstra /*
6efb3040dSPeter Zijlstra  * Jump label support
7efb3040dSPeter Zijlstra  *
8efb3040dSPeter Zijlstra  * Copyright (C) 2009-2012 Jason Baron <[email protected]>
990eec103SPeter Zijlstra  * Copyright (C) 2011-2012 Red Hat, Inc., Peter Zijlstra
10efb3040dSPeter Zijlstra  *
11412758cbSJason Baron  * DEPRECATED API:
12412758cbSJason Baron  *
13412758cbSJason Baron  * The use of 'struct static_key' directly, is now DEPRECATED. In addition
14412758cbSJason Baron  * static_key_{true,false}() is also DEPRECATED. IE DO NOT use the following:
15412758cbSJason Baron  *
16412758cbSJason Baron  * struct static_key false = STATIC_KEY_INIT_FALSE;
17412758cbSJason Baron  * struct static_key true = STATIC_KEY_INIT_TRUE;
18412758cbSJason Baron  * static_key_true()
19412758cbSJason Baron  * static_key_false()
20412758cbSJason Baron  *
21412758cbSJason Baron  * The updated API replacements are:
22412758cbSJason Baron  *
23412758cbSJason Baron  * DEFINE_STATIC_KEY_TRUE(key);
24412758cbSJason Baron  * DEFINE_STATIC_KEY_FALSE(key);
25ef0da55aSCatalin Marinas  * DEFINE_STATIC_KEY_ARRAY_TRUE(keys, count);
26ef0da55aSCatalin Marinas  * DEFINE_STATIC_KEY_ARRAY_FALSE(keys, count);
271975dbc2SJonathan Corbet  * static_branch_likely()
281975dbc2SJonathan Corbet  * static_branch_unlikely()
29412758cbSJason Baron  *
30efb3040dSPeter Zijlstra  * Jump labels provide an interface to generate dynamic branches using
31412758cbSJason Baron  * self-modifying code. Assuming toolchain and architecture support, if we
32412758cbSJason Baron  * define a "key" that is initially false via "DEFINE_STATIC_KEY_FALSE(key)",
33412758cbSJason Baron  * an "if (static_branch_unlikely(&key))" statement is an unconditional branch
34412758cbSJason Baron  * (which defaults to false - and the true block is placed out of line).
35412758cbSJason Baron  * Similarly, we can define an initially true key via
36412758cbSJason Baron  * "DEFINE_STATIC_KEY_TRUE(key)", and use it in the same
37412758cbSJason Baron  * "if (static_branch_unlikely(&key))", in which case we will generate an
38412758cbSJason Baron  * unconditional branch to the out-of-line true branch. Keys that are
39412758cbSJason Baron  * initially true or false can be using in both static_branch_unlikely()
40412758cbSJason Baron  * and static_branch_likely() statements.
41efb3040dSPeter Zijlstra  *
42412758cbSJason Baron  * At runtime we can change the branch target by setting the key
43412758cbSJason Baron  * to true via a call to static_branch_enable(), or false using
44412758cbSJason Baron  * static_branch_disable(). If the direction of the branch is switched by
45412758cbSJason Baron  * these calls then we run-time modify the branch target via a
46412758cbSJason Baron  * no-op -> jump or jump -> no-op conversion. For example, for an
47412758cbSJason Baron  * initially false key that is used in an "if (static_branch_unlikely(&key))"
48412758cbSJason Baron  * statement, setting the key to true requires us to patch in a jump
49412758cbSJason Baron  * to the out-of-line of true branch.
50efb3040dSPeter Zijlstra  *
511975dbc2SJonathan Corbet  * In addition to static_branch_{enable,disable}, we can also reference count
52412758cbSJason Baron  * the key or branch direction via static_branch_{inc,dec}. Thus,
53412758cbSJason Baron  * static_branch_inc() can be thought of as a 'make more true' and
541975dbc2SJonathan Corbet  * static_branch_dec() as a 'make more false'.
55412758cbSJason Baron  *
56412758cbSJason Baron  * Since this relies on modifying code, the branch modifying functions
57efb3040dSPeter Zijlstra  * must be considered absolute slow paths (machine wide synchronization etc.).
58fd3cbdc0SIngo Molnar  * OTOH, since the affected branches are unconditional, their runtime overhead
59efb3040dSPeter Zijlstra  * will be absolutely minimal, esp. in the default (off) case where the total
60efb3040dSPeter Zijlstra  * effect is a single NOP of appropriate size. The on case will patch in a jump
61efb3040dSPeter Zijlstra  * to the out-of-line block.
62efb3040dSPeter Zijlstra  *
63fd3cbdc0SIngo Molnar  * When the control is directly exposed to userspace, it is prudent to delay the
64efb3040dSPeter Zijlstra  * decrement to avoid high frequency code modifications which can (and do)
65c5905afbSIngo Molnar  * cause significant performance degradation. Struct static_key_deferred and
66c5905afbSIngo Molnar  * static_key_slow_dec_deferred() provide for this.
67efb3040dSPeter Zijlstra  *
68412758cbSJason Baron  * Lacking toolchain and or architecture support, static keys fall back to a
69412758cbSJason Baron  * simple conditional branch.
70c5905afbSIngo Molnar  *
718e2a46a4SMauro Carvalho Chehab  * Additional babbling in: Documentation/staging/static-keys.rst
72efb3040dSPeter Zijlstra  */
73efb3040dSPeter Zijlstra 
74c0ccf6f9SAnton Blanchard #ifndef __ASSEMBLY__
75c0ccf6f9SAnton Blanchard 
76d430d3d7SJason Baron #include <linux/types.h>
77d430d3d7SJason Baron #include <linux/compiler.h>
78*e2b6e5e4SMasami Hiramatsu (Google) #include <linux/cleanup.h>
79c4b2c0c5SHannes Frederic Sowa 
80c4b2c0c5SHannes Frederic Sowa extern bool static_key_initialized;
81c4b2c0c5SHannes Frederic Sowa 
825cdda511SBorislav Petkov #define STATIC_KEY_CHECK_USE(key) WARN(!static_key_initialized,		      \
835cdda511SBorislav Petkov 				    "%s(): static key '%pS' used before call to jump_label_init()", \
845cdda511SBorislav Petkov 				    __func__, (key))
85d430d3d7SJason Baron 
86c5905afbSIngo Molnar struct static_key {
87d430d3d7SJason Baron 	atomic_t enabled;
88cd27ccfcSMasahiro Yamada #ifdef CONFIG_JUMP_LABEL
893821fd35SJason Baron /*
90b17ef2edSSteven Rostedt (VMware)  * Note:
91b17ef2edSSteven Rostedt (VMware)  *   To make anonymous unions work with old compilers, the static
92b17ef2edSSteven Rostedt (VMware)  *   initialization of them requires brackets. This creates a dependency
93b17ef2edSSteven Rostedt (VMware)  *   on the order of the struct with the initializers. If any fields
94b17ef2edSSteven Rostedt (VMware)  *   are added, STATIC_KEY_INIT_TRUE and STATIC_KEY_INIT_FALSE may need
95b17ef2edSSteven Rostedt (VMware)  *   to be modified.
96b17ef2edSSteven Rostedt (VMware)  *
973821fd35SJason Baron  * bit 0 => 1 if key is initially true
983821fd35SJason Baron  *	    0 if initially false
993821fd35SJason Baron  * bit 1 => 1 if points to struct static_key_mod
1003821fd35SJason Baron  *	    0 if points to struct jump_entry
1013821fd35SJason Baron  */
1023821fd35SJason Baron 	union {
1033821fd35SJason Baron 		unsigned long type;
104d430d3d7SJason Baron 		struct jump_entry *entries;
105c5905afbSIngo Molnar 		struct static_key_mod *next;
1063821fd35SJason Baron 	};
107cd27ccfcSMasahiro Yamada #endif	/* CONFIG_JUMP_LABEL */
108d430d3d7SJason Baron };
109d430d3d7SJason Baron 
110c0ccf6f9SAnton Blanchard #endif /* __ASSEMBLY__ */
111c0ccf6f9SAnton Blanchard 
112e9666d10SMasahiro Yamada #ifdef CONFIG_JUMP_LABEL
113c0ccf6f9SAnton Blanchard #include <asm/jump_label.h>
1149ae033acSArd Biesheuvel 
1159ae033acSArd Biesheuvel #ifndef __ASSEMBLY__
11650ff18abSArd Biesheuvel #ifdef CONFIG_HAVE_ARCH_JUMP_LABEL_RELATIVE
11750ff18abSArd Biesheuvel 
11850ff18abSArd Biesheuvel struct jump_entry {
11950ff18abSArd Biesheuvel 	s32 code;
12050ff18abSArd Biesheuvel 	s32 target;
12150ff18abSArd Biesheuvel 	long key;	// key may be far away from the core kernel under KASLR
12250ff18abSArd Biesheuvel };
12350ff18abSArd Biesheuvel 
jump_entry_code(const struct jump_entry * entry)12450ff18abSArd Biesheuvel static inline unsigned long jump_entry_code(const struct jump_entry *entry)
12550ff18abSArd Biesheuvel {
12650ff18abSArd Biesheuvel 	return (unsigned long)&entry->code + entry->code;
12750ff18abSArd Biesheuvel }
12850ff18abSArd Biesheuvel 
jump_entry_target(const struct jump_entry * entry)12950ff18abSArd Biesheuvel static inline unsigned long jump_entry_target(const struct jump_entry *entry)
13050ff18abSArd Biesheuvel {
13150ff18abSArd Biesheuvel 	return (unsigned long)&entry->target + entry->target;
13250ff18abSArd Biesheuvel }
13350ff18abSArd Biesheuvel 
jump_entry_key(const struct jump_entry * entry)13450ff18abSArd Biesheuvel static inline struct static_key *jump_entry_key(const struct jump_entry *entry)
13550ff18abSArd Biesheuvel {
13619483677SArd Biesheuvel 	long offset = entry->key & ~3L;
13750ff18abSArd Biesheuvel 
13850ff18abSArd Biesheuvel 	return (struct static_key *)((unsigned long)&entry->key + offset);
13950ff18abSArd Biesheuvel }
14050ff18abSArd Biesheuvel 
14150ff18abSArd Biesheuvel #else
1429ae033acSArd Biesheuvel 
jump_entry_code(const struct jump_entry * entry)1439ae033acSArd Biesheuvel static inline unsigned long jump_entry_code(const struct jump_entry *entry)
1449ae033acSArd Biesheuvel {
1459ae033acSArd Biesheuvel 	return entry->code;
1469ae033acSArd Biesheuvel }
1479ae033acSArd Biesheuvel 
jump_entry_target(const struct jump_entry * entry)1489ae033acSArd Biesheuvel static inline unsigned long jump_entry_target(const struct jump_entry *entry)
1499ae033acSArd Biesheuvel {
1509ae033acSArd Biesheuvel 	return entry->target;
1519ae033acSArd Biesheuvel }
1529ae033acSArd Biesheuvel 
jump_entry_key(const struct jump_entry * entry)1539ae033acSArd Biesheuvel static inline struct static_key *jump_entry_key(const struct jump_entry *entry)
1549ae033acSArd Biesheuvel {
15519483677SArd Biesheuvel 	return (struct static_key *)((unsigned long)entry->key & ~3UL);
1569ae033acSArd Biesheuvel }
1579ae033acSArd Biesheuvel 
15850ff18abSArd Biesheuvel #endif
15950ff18abSArd Biesheuvel 
jump_entry_is_branch(const struct jump_entry * entry)1609ae033acSArd Biesheuvel static inline bool jump_entry_is_branch(const struct jump_entry *entry)
1619ae033acSArd Biesheuvel {
1629ae033acSArd Biesheuvel 	return (unsigned long)entry->key & 1UL;
1639ae033acSArd Biesheuvel }
1649ae033acSArd Biesheuvel 
jump_entry_is_init(const struct jump_entry * entry)1659ae033acSArd Biesheuvel static inline bool jump_entry_is_init(const struct jump_entry *entry)
1669ae033acSArd Biesheuvel {
16719483677SArd Biesheuvel 	return (unsigned long)entry->key & 2UL;
1689ae033acSArd Biesheuvel }
1699ae033acSArd Biesheuvel 
jump_entry_set_init(struct jump_entry * entry,bool set)1705af0ea29SPeter Zijlstra static inline void jump_entry_set_init(struct jump_entry *entry, bool set)
1719ae033acSArd Biesheuvel {
1725af0ea29SPeter Zijlstra 	if (set)
17319483677SArd Biesheuvel 		entry->key |= 2;
1745af0ea29SPeter Zijlstra 	else
1755af0ea29SPeter Zijlstra 		entry->key &= ~2;
1769ae033acSArd Biesheuvel }
1779ae033acSArd Biesheuvel 
jump_entry_size(struct jump_entry * entry)178fa5e5dc3SPeter Zijlstra static inline int jump_entry_size(struct jump_entry *entry)
179fa5e5dc3SPeter Zijlstra {
180fa5e5dc3SPeter Zijlstra #ifdef JUMP_LABEL_NOP_SIZE
181fa5e5dc3SPeter Zijlstra 	return JUMP_LABEL_NOP_SIZE;
182fa5e5dc3SPeter Zijlstra #else
183fa5e5dc3SPeter Zijlstra 	return arch_jump_entry_size(entry);
184fa5e5dc3SPeter Zijlstra #endif
185fa5e5dc3SPeter Zijlstra }
186fa5e5dc3SPeter Zijlstra 
1879ae033acSArd Biesheuvel #endif
188c0ccf6f9SAnton Blanchard #endif
189c0ccf6f9SAnton Blanchard 
190c0ccf6f9SAnton Blanchard #ifndef __ASSEMBLY__
191bf5438fcSJason Baron 
192bf5438fcSJason Baron enum jump_label_type {
19376b235c6SPeter Zijlstra 	JUMP_LABEL_NOP = 0,
19476b235c6SPeter Zijlstra 	JUMP_LABEL_JMP,
195bf5438fcSJason Baron };
196bf5438fcSJason Baron 
197bf5438fcSJason Baron struct module;
198bf5438fcSJason Baron 
199e9666d10SMasahiro Yamada #ifdef CONFIG_JUMP_LABEL
2004c5ea0a9SPaolo Bonzini 
201a1efb01fSPeter Zijlstra #define JUMP_TYPE_FALSE		0UL
202a1efb01fSPeter Zijlstra #define JUMP_TYPE_TRUE		1UL
2033821fd35SJason Baron #define JUMP_TYPE_LINKED	2UL
2043821fd35SJason Baron #define JUMP_TYPE_MASK		3UL
205c5905afbSIngo Molnar 
static_key_false(struct static_key * key)206c5905afbSIngo Molnar static __always_inline bool static_key_false(struct static_key *key)
207c5905afbSIngo Molnar {
20811276d53SPeter Zijlstra 	return arch_static_branch(key, false);
209c5905afbSIngo Molnar }
210c5905afbSIngo Molnar 
static_key_true(struct static_key * key)211c5905afbSIngo Molnar static __always_inline bool static_key_true(struct static_key *key)
212c5905afbSIngo Molnar {
21311276d53SPeter Zijlstra 	return !arch_static_branch(key, true);
214c5905afbSIngo Molnar }
215c5905afbSIngo Molnar 
216bf5438fcSJason Baron extern struct jump_entry __start___jump_table[];
217bf5438fcSJason Baron extern struct jump_entry __stop___jump_table[];
218bf5438fcSJason Baron 
21997ce2c88SJeremy Fitzhardinge extern void jump_label_init(void);
22091a1d97eSPeter Zijlstra extern void jump_label_init_ro(void);
22191bad2f8SJason Baron extern void jump_label_lock(void);
22291bad2f8SJason Baron extern void jump_label_unlock(void);
223bf5438fcSJason Baron extern void arch_jump_label_transform(struct jump_entry *entry,
224bf5438fcSJason Baron 				      enum jump_label_type type);
225c2ba8a15SDaniel Bristot de Oliveira extern bool arch_jump_label_transform_queue(struct jump_entry *entry,
226c2ba8a15SDaniel Bristot de Oliveira 					    enum jump_label_type type);
227c2ba8a15SDaniel Bristot de Oliveira extern void arch_jump_label_transform_apply(void);
2284c3ef6d7SJason Baron extern int jump_label_text_reserved(void *start, void *end);
229eb8c5072SDmitry Safonov extern bool static_key_slow_inc(struct static_key *key);
230eb8c5072SDmitry Safonov extern bool static_key_fast_inc_not_disabled(struct static_key *key);
231c5905afbSIngo Molnar extern void static_key_slow_dec(struct static_key *key);
232eb8c5072SDmitry Safonov extern bool static_key_slow_inc_cpuslocked(struct static_key *key);
233ce48c146SPeter Zijlstra extern void static_key_slow_dec_cpuslocked(struct static_key *key);
2341f69bf9cSJason Baron extern int static_key_count(struct static_key *key);
2351f69bf9cSJason Baron extern void static_key_enable(struct static_key *key);
2361f69bf9cSJason Baron extern void static_key_disable(struct static_key *key);
2375a40527fSMarc Zyngier extern void static_key_enable_cpuslocked(struct static_key *key);
2385a40527fSMarc Zyngier extern void static_key_disable_cpuslocked(struct static_key *key);
239fdfd4289SArd Biesheuvel extern enum jump_label_type jump_label_init_type(struct jump_entry *entry);
240c5905afbSIngo Molnar 
2411f69bf9cSJason Baron /*
2421f69bf9cSJason Baron  * We should be using ATOMIC_INIT() for initializing .enabled, but
2431f69bf9cSJason Baron  * the inclusion of atomic.h is problematic for inclusion of jump_label.h
2441f69bf9cSJason Baron  * in 'low-level' headers. Thus, we are initializing .enabled with a
2451f69bf9cSJason Baron  * raw value, but have added a BUILD_BUG_ON() to catch any issues in
2461f69bf9cSJason Baron  * jump_label_init() see: kernel/jump_label.c.
2471f69bf9cSJason Baron  */
24811276d53SPeter Zijlstra #define STATIC_KEY_INIT_TRUE					\
2491f69bf9cSJason Baron 	{ .enabled = { 1 },					\
250fe65deb5SMasahiro Yamada 	  { .type = JUMP_TYPE_TRUE } }
25111276d53SPeter Zijlstra #define STATIC_KEY_INIT_FALSE					\
2521f69bf9cSJason Baron 	{ .enabled = { 0 },					\
253fe65deb5SMasahiro Yamada 	  { .type = JUMP_TYPE_FALSE } }
254bf5438fcSJason Baron 
255e9666d10SMasahiro Yamada #else  /* !CONFIG_JUMP_LABEL */
256bf5438fcSJason Baron 
2571f69bf9cSJason Baron #include <linux/atomic.h>
2581f69bf9cSJason Baron #include <linux/bug.h>
2591f69bf9cSJason Baron 
static_key_count(struct static_key * key)260656d054eSPeter Zijlstra static __always_inline int static_key_count(struct static_key *key)
2614c5ea0a9SPaolo Bonzini {
2620f613bfaSMark Rutland 	return raw_atomic_read(&key->enabled);
2634c5ea0a9SPaolo Bonzini }
2644c5ea0a9SPaolo Bonzini 
jump_label_init(void)26597ce2c88SJeremy Fitzhardinge static __always_inline void jump_label_init(void)
26697ce2c88SJeremy Fitzhardinge {
267c4b2c0c5SHannes Frederic Sowa 	static_key_initialized = true;
26897ce2c88SJeremy Fitzhardinge }
26997ce2c88SJeremy Fitzhardinge 
jump_label_init_ro(void)27091a1d97eSPeter Zijlstra static __always_inline void jump_label_init_ro(void) { }
27191a1d97eSPeter Zijlstra 
static_key_false(struct static_key * key)272c5905afbSIngo Molnar static __always_inline bool static_key_false(struct static_key *key)
273bf5438fcSJason Baron {
2742f0df49cSSteven Rostedt (VMware) 	if (unlikely_notrace(static_key_count(key) > 0))
275d430d3d7SJason Baron 		return true;
276d430d3d7SJason Baron 	return false;
277d430d3d7SJason Baron }
278d430d3d7SJason Baron 
static_key_true(struct static_key * key)279c5905afbSIngo Molnar static __always_inline bool static_key_true(struct static_key *key)
280c5905afbSIngo Molnar {
2812f0df49cSSteven Rostedt (VMware) 	if (likely_notrace(static_key_count(key) > 0))
282c5905afbSIngo Molnar 		return true;
283c5905afbSIngo Molnar 	return false;
284c5905afbSIngo Molnar }
285c5905afbSIngo Molnar 
static_key_fast_inc_not_disabled(struct static_key * key)286eb8c5072SDmitry Safonov static inline bool static_key_fast_inc_not_disabled(struct static_key *key)
287d430d3d7SJason Baron {
288eb8c5072SDmitry Safonov 	int v;
289eb8c5072SDmitry Safonov 
2905cdda511SBorislav Petkov 	STATIC_KEY_CHECK_USE(key);
291eb8c5072SDmitry Safonov 	/*
292eb8c5072SDmitry Safonov 	 * Prevent key->enabled getting negative to follow the same semantics
293eb8c5072SDmitry Safonov 	 * as for CONFIG_JUMP_LABEL=y, see kernel/jump_label.c comment.
294eb8c5072SDmitry Safonov 	 */
295eb8c5072SDmitry Safonov 	v = atomic_read(&key->enabled);
296eb8c5072SDmitry Safonov 	do {
297eb8c5072SDmitry Safonov 		if (v < 0 || (v + 1) < 0)
298eb8c5072SDmitry Safonov 			return false;
299eb8c5072SDmitry Safonov 	} while (!likely(atomic_try_cmpxchg(&key->enabled, &v, v + 1)));
300eb8c5072SDmitry Safonov 	return true;
301d430d3d7SJason Baron }
302eb8c5072SDmitry Safonov #define static_key_slow_inc(key)	static_key_fast_inc_not_disabled(key)
303d430d3d7SJason Baron 
static_key_slow_dec(struct static_key * key)304c5905afbSIngo Molnar static inline void static_key_slow_dec(struct static_key *key)
305d430d3d7SJason Baron {
3065cdda511SBorislav Petkov 	STATIC_KEY_CHECK_USE(key);
307d430d3d7SJason Baron 	atomic_dec(&key->enabled);
308bf5438fcSJason Baron }
309bf5438fcSJason Baron 
310ce48c146SPeter Zijlstra #define static_key_slow_inc_cpuslocked(key) static_key_slow_inc(key)
311ce48c146SPeter Zijlstra #define static_key_slow_dec_cpuslocked(key) static_key_slow_dec(key)
312ce48c146SPeter Zijlstra 
jump_label_text_reserved(void * start,void * end)3134c3ef6d7SJason Baron static inline int jump_label_text_reserved(void *start, void *end)
3144c3ef6d7SJason Baron {
3154c3ef6d7SJason Baron 	return 0;
3164c3ef6d7SJason Baron }
3174c3ef6d7SJason Baron 
jump_label_lock(void)31891bad2f8SJason Baron static inline void jump_label_lock(void) {}
jump_label_unlock(void)31991bad2f8SJason Baron static inline void jump_label_unlock(void) {}
32091bad2f8SJason Baron 
static_key_enable(struct static_key * key)321e33886b3SPeter Zijlstra static inline void static_key_enable(struct static_key *key)
322e33886b3SPeter Zijlstra {
3235cdda511SBorislav Petkov 	STATIC_KEY_CHECK_USE(key);
324e33886b3SPeter Zijlstra 
3251dbb6704SPaolo Bonzini 	if (atomic_read(&key->enabled) != 0) {
3261dbb6704SPaolo Bonzini 		WARN_ON_ONCE(atomic_read(&key->enabled) != 1);
3271dbb6704SPaolo Bonzini 		return;
3281dbb6704SPaolo Bonzini 	}
3291dbb6704SPaolo Bonzini 	atomic_set(&key->enabled, 1);
330e33886b3SPeter Zijlstra }
331e33886b3SPeter Zijlstra 
static_key_disable(struct static_key * key)332e33886b3SPeter Zijlstra static inline void static_key_disable(struct static_key *key)
333e33886b3SPeter Zijlstra {
3345cdda511SBorislav Petkov 	STATIC_KEY_CHECK_USE(key);
335e33886b3SPeter Zijlstra 
3361dbb6704SPaolo Bonzini 	if (atomic_read(&key->enabled) != 1) {
3371dbb6704SPaolo Bonzini 		WARN_ON_ONCE(atomic_read(&key->enabled) != 0);
3381dbb6704SPaolo Bonzini 		return;
3391dbb6704SPaolo Bonzini 	}
3401dbb6704SPaolo Bonzini 	atomic_set(&key->enabled, 0);
341e33886b3SPeter Zijlstra }
342e33886b3SPeter Zijlstra 
3435a40527fSMarc Zyngier #define static_key_enable_cpuslocked(k)		static_key_enable((k))
3445a40527fSMarc Zyngier #define static_key_disable_cpuslocked(k)	static_key_disable((k))
3455a40527fSMarc Zyngier 
3461f69bf9cSJason Baron #define STATIC_KEY_INIT_TRUE	{ .enabled = ATOMIC_INIT(1) }
3471f69bf9cSJason Baron #define STATIC_KEY_INIT_FALSE	{ .enabled = ATOMIC_INIT(0) }
3481f69bf9cSJason Baron 
349e9666d10SMasahiro Yamada #endif	/* CONFIG_JUMP_LABEL */
3501f69bf9cSJason Baron 
351*e2b6e5e4SMasami Hiramatsu (Google) DEFINE_LOCK_GUARD_0(jump_label_lock, jump_label_lock(), jump_label_unlock())
352*e2b6e5e4SMasami Hiramatsu (Google) 
3531f69bf9cSJason Baron #define STATIC_KEY_INIT STATIC_KEY_INIT_FALSE
3541f69bf9cSJason Baron #define jump_label_enabled static_key_enabled
3551f69bf9cSJason Baron 
35611276d53SPeter Zijlstra /* -------------------------------------------------------------------------- */
35711276d53SPeter Zijlstra 
35811276d53SPeter Zijlstra /*
35911276d53SPeter Zijlstra  * Two type wrappers around static_key, such that we can use compile time
36011276d53SPeter Zijlstra  * type differentiation to emit the right code.
36111276d53SPeter Zijlstra  *
36211276d53SPeter Zijlstra  * All the below code is macros in order to play type games.
36311276d53SPeter Zijlstra  */
36411276d53SPeter Zijlstra 
36511276d53SPeter Zijlstra struct static_key_true {
36611276d53SPeter Zijlstra 	struct static_key key;
36711276d53SPeter Zijlstra };
36811276d53SPeter Zijlstra 
36911276d53SPeter Zijlstra struct static_key_false {
37011276d53SPeter Zijlstra 	struct static_key key;
37111276d53SPeter Zijlstra };
37211276d53SPeter Zijlstra 
37311276d53SPeter Zijlstra #define STATIC_KEY_TRUE_INIT  (struct static_key_true) { .key = STATIC_KEY_INIT_TRUE,  }
37411276d53SPeter Zijlstra #define STATIC_KEY_FALSE_INIT (struct static_key_false){ .key = STATIC_KEY_INIT_FALSE, }
37511276d53SPeter Zijlstra 
37611276d53SPeter Zijlstra #define DEFINE_STATIC_KEY_TRUE(name)	\
37711276d53SPeter Zijlstra 	struct static_key_true name = STATIC_KEY_TRUE_INIT
37811276d53SPeter Zijlstra 
379b5cb15d9SChris von Recklinghausen #define DEFINE_STATIC_KEY_TRUE_RO(name)	\
380b5cb15d9SChris von Recklinghausen 	struct static_key_true name __ro_after_init = STATIC_KEY_TRUE_INIT
381b5cb15d9SChris von Recklinghausen 
382b8fb0378STony Luck #define DECLARE_STATIC_KEY_TRUE(name)	\
383b8fb0378STony Luck 	extern struct static_key_true name
384b8fb0378STony Luck 
38511276d53SPeter Zijlstra #define DEFINE_STATIC_KEY_FALSE(name)	\
38611276d53SPeter Zijlstra 	struct static_key_false name = STATIC_KEY_FALSE_INIT
38711276d53SPeter Zijlstra 
388b5cb15d9SChris von Recklinghausen #define DEFINE_STATIC_KEY_FALSE_RO(name)	\
389b5cb15d9SChris von Recklinghausen 	struct static_key_false name __ro_after_init = STATIC_KEY_FALSE_INIT
390b5cb15d9SChris von Recklinghausen 
391b8fb0378STony Luck #define DECLARE_STATIC_KEY_FALSE(name)	\
392b8fb0378STony Luck 	extern struct static_key_false name
393b8fb0378STony Luck 
394ef0da55aSCatalin Marinas #define DEFINE_STATIC_KEY_ARRAY_TRUE(name, count)		\
395ef0da55aSCatalin Marinas 	struct static_key_true name[count] = {			\
396ef0da55aSCatalin Marinas 		[0 ... (count) - 1] = STATIC_KEY_TRUE_INIT,	\
397ef0da55aSCatalin Marinas 	}
398ef0da55aSCatalin Marinas 
399ef0da55aSCatalin Marinas #define DEFINE_STATIC_KEY_ARRAY_FALSE(name, count)		\
400ef0da55aSCatalin Marinas 	struct static_key_false name[count] = {			\
401ef0da55aSCatalin Marinas 		[0 ... (count) - 1] = STATIC_KEY_FALSE_INIT,	\
402ef0da55aSCatalin Marinas 	}
403ef0da55aSCatalin Marinas 
4040d66ccc1SKees Cook #define _DEFINE_STATIC_KEY_1(name)	DEFINE_STATIC_KEY_TRUE(name)
4050d66ccc1SKees Cook #define _DEFINE_STATIC_KEY_0(name)	DEFINE_STATIC_KEY_FALSE(name)
4060d66ccc1SKees Cook #define DEFINE_STATIC_KEY_MAYBE(cfg, name)			\
4070d66ccc1SKees Cook 	__PASTE(_DEFINE_STATIC_KEY_, IS_ENABLED(cfg))(name)
4080d66ccc1SKees Cook 
4090d66ccc1SKees Cook #define _DEFINE_STATIC_KEY_RO_1(name)	DEFINE_STATIC_KEY_TRUE_RO(name)
4100d66ccc1SKees Cook #define _DEFINE_STATIC_KEY_RO_0(name)	DEFINE_STATIC_KEY_FALSE_RO(name)
4110d66ccc1SKees Cook #define DEFINE_STATIC_KEY_MAYBE_RO(cfg, name)			\
4120d66ccc1SKees Cook 	__PASTE(_DEFINE_STATIC_KEY_RO_, IS_ENABLED(cfg))(name)
4130d66ccc1SKees Cook 
4140d66ccc1SKees Cook #define _DECLARE_STATIC_KEY_1(name)	DECLARE_STATIC_KEY_TRUE(name)
4150d66ccc1SKees Cook #define _DECLARE_STATIC_KEY_0(name)	DECLARE_STATIC_KEY_FALSE(name)
4160d66ccc1SKees Cook #define DECLARE_STATIC_KEY_MAYBE(cfg, name)			\
4170d66ccc1SKees Cook 	__PASTE(_DECLARE_STATIC_KEY_, IS_ENABLED(cfg))(name)
4180d66ccc1SKees Cook 
419fa128fd7STejun Heo extern bool ____wrong_branch_error(void);
420fa128fd7STejun Heo 
421fa128fd7STejun Heo #define static_key_enabled(x)							\
422fa128fd7STejun Heo ({										\
423fa128fd7STejun Heo 	if (!__builtin_types_compatible_p(typeof(*x), struct static_key) &&	\
424fa128fd7STejun Heo 	    !__builtin_types_compatible_p(typeof(*x), struct static_key_true) &&\
425fa128fd7STejun Heo 	    !__builtin_types_compatible_p(typeof(*x), struct static_key_false))	\
426fa128fd7STejun Heo 		____wrong_branch_error();					\
427fa128fd7STejun Heo 	static_key_count((struct static_key *)x) > 0;				\
428fa128fd7STejun Heo })
429fa128fd7STejun Heo 
430e9666d10SMasahiro Yamada #ifdef CONFIG_JUMP_LABEL
43111276d53SPeter Zijlstra 
43211276d53SPeter Zijlstra /*
43311276d53SPeter Zijlstra  * Combine the right initial value (type) with the right branch order
43411276d53SPeter Zijlstra  * to generate the desired result.
43511276d53SPeter Zijlstra  *
43611276d53SPeter Zijlstra  *
43711276d53SPeter Zijlstra  * type\branch|	likely (1)	      |	unlikely (0)
43811276d53SPeter Zijlstra  * -----------+-----------------------+------------------
43911276d53SPeter Zijlstra  *            |                       |
44011276d53SPeter Zijlstra  *  true (1)  |	   ...		      |	   ...
44111276d53SPeter Zijlstra  *            |    NOP		      |	   JMP L
44211276d53SPeter Zijlstra  *            |    <br-stmts>	      |	1: ...
44311276d53SPeter Zijlstra  *            |	L: ...		      |
44411276d53SPeter Zijlstra  *            |			      |
44511276d53SPeter Zijlstra  *            |			      |	L: <br-stmts>
44611276d53SPeter Zijlstra  *            |			      |	   jmp 1b
44711276d53SPeter Zijlstra  *            |                       |
44811276d53SPeter Zijlstra  * -----------+-----------------------+------------------
44911276d53SPeter Zijlstra  *            |                       |
45011276d53SPeter Zijlstra  *  false (0) |	   ...		      |	   ...
45111276d53SPeter Zijlstra  *            |    JMP L	      |	   NOP
45211276d53SPeter Zijlstra  *            |    <br-stmts>	      |	1: ...
45311276d53SPeter Zijlstra  *            |	L: ...		      |
45411276d53SPeter Zijlstra  *            |			      |
45511276d53SPeter Zijlstra  *            |			      |	L: <br-stmts>
45611276d53SPeter Zijlstra  *            |			      |	   jmp 1b
45711276d53SPeter Zijlstra  *            |                       |
45811276d53SPeter Zijlstra  * -----------+-----------------------+------------------
45911276d53SPeter Zijlstra  *
46011276d53SPeter Zijlstra  * The initial value is encoded in the LSB of static_key::entries,
46111276d53SPeter Zijlstra  * type: 0 = false, 1 = true.
46211276d53SPeter Zijlstra  *
46311276d53SPeter Zijlstra  * The branch type is encoded in the LSB of jump_entry::key,
46411276d53SPeter Zijlstra  * branch: 0 = unlikely, 1 = likely.
46511276d53SPeter Zijlstra  *
46611276d53SPeter Zijlstra  * This gives the following logic table:
46711276d53SPeter Zijlstra  *
46811276d53SPeter Zijlstra  *	enabled	type	branch	  instuction
46911276d53SPeter Zijlstra  * -----------------------------+-----------
47011276d53SPeter Zijlstra  *	0	0	0	| NOP
47111276d53SPeter Zijlstra  *	0	0	1	| JMP
47211276d53SPeter Zijlstra  *	0	1	0	| NOP
47311276d53SPeter Zijlstra  *	0	1	1	| JMP
47411276d53SPeter Zijlstra  *
47511276d53SPeter Zijlstra  *	1	0	0	| JMP
47611276d53SPeter Zijlstra  *	1	0	1	| NOP
47711276d53SPeter Zijlstra  *	1	1	0	| JMP
47811276d53SPeter Zijlstra  *	1	1	1	| NOP
47911276d53SPeter Zijlstra  *
48011276d53SPeter Zijlstra  * Which gives the following functions:
48111276d53SPeter Zijlstra  *
48211276d53SPeter Zijlstra  *   dynamic: instruction = enabled ^ branch
48311276d53SPeter Zijlstra  *   static:  instruction = type ^ branch
48411276d53SPeter Zijlstra  *
48511276d53SPeter Zijlstra  * See jump_label_type() / jump_label_init_type().
48611276d53SPeter Zijlstra  */
48711276d53SPeter Zijlstra 
48811276d53SPeter Zijlstra #define static_branch_likely(x)							\
48911276d53SPeter Zijlstra ({										\
49011276d53SPeter Zijlstra 	bool branch;								\
49111276d53SPeter Zijlstra 	if (__builtin_types_compatible_p(typeof(*x), struct static_key_true))	\
49211276d53SPeter Zijlstra 		branch = !arch_static_branch(&(x)->key, true);			\
49311276d53SPeter Zijlstra 	else if (__builtin_types_compatible_p(typeof(*x), struct static_key_false)) \
49411276d53SPeter Zijlstra 		branch = !arch_static_branch_jump(&(x)->key, true);		\
49511276d53SPeter Zijlstra 	else									\
49611276d53SPeter Zijlstra 		branch = ____wrong_branch_error();				\
4972f0df49cSSteven Rostedt (VMware) 	likely_notrace(branch);								\
49811276d53SPeter Zijlstra })
49911276d53SPeter Zijlstra 
50011276d53SPeter Zijlstra #define static_branch_unlikely(x)						\
50111276d53SPeter Zijlstra ({										\
50211276d53SPeter Zijlstra 	bool branch;								\
50311276d53SPeter Zijlstra 	if (__builtin_types_compatible_p(typeof(*x), struct static_key_true))	\
50411276d53SPeter Zijlstra 		branch = arch_static_branch_jump(&(x)->key, false);		\
50511276d53SPeter Zijlstra 	else if (__builtin_types_compatible_p(typeof(*x), struct static_key_false)) \
50611276d53SPeter Zijlstra 		branch = arch_static_branch(&(x)->key, false);			\
50711276d53SPeter Zijlstra 	else									\
50811276d53SPeter Zijlstra 		branch = ____wrong_branch_error();				\
5092f0df49cSSteven Rostedt (VMware) 	unlikely_notrace(branch);							\
51011276d53SPeter Zijlstra })
51111276d53SPeter Zijlstra 
512e9666d10SMasahiro Yamada #else /* !CONFIG_JUMP_LABEL */
51311276d53SPeter Zijlstra 
5142f0df49cSSteven Rostedt (VMware) #define static_branch_likely(x)		likely_notrace(static_key_enabled(&(x)->key))
5152f0df49cSSteven Rostedt (VMware) #define static_branch_unlikely(x)	unlikely_notrace(static_key_enabled(&(x)->key))
51611276d53SPeter Zijlstra 
517e9666d10SMasahiro Yamada #endif /* CONFIG_JUMP_LABEL */
51811276d53SPeter Zijlstra 
5190d66ccc1SKees Cook #define static_branch_maybe(config, x)					\
5200d66ccc1SKees Cook 	(IS_ENABLED(config) ? static_branch_likely(x)			\
5210d66ccc1SKees Cook 			    : static_branch_unlikely(x))
5220d66ccc1SKees Cook 
52311276d53SPeter Zijlstra /*
52411276d53SPeter Zijlstra  * Advanced usage; refcount, branch is enabled when: count != 0
52511276d53SPeter Zijlstra  */
52611276d53SPeter Zijlstra 
52711276d53SPeter Zijlstra #define static_branch_inc(x)		static_key_slow_inc(&(x)->key)
52811276d53SPeter Zijlstra #define static_branch_dec(x)		static_key_slow_dec(&(x)->key)
529ce48c146SPeter Zijlstra #define static_branch_inc_cpuslocked(x)	static_key_slow_inc_cpuslocked(&(x)->key)
530ce48c146SPeter Zijlstra #define static_branch_dec_cpuslocked(x)	static_key_slow_dec_cpuslocked(&(x)->key)
53111276d53SPeter Zijlstra 
53211276d53SPeter Zijlstra /*
53311276d53SPeter Zijlstra  * Normal usage; boolean enable/disable.
53411276d53SPeter Zijlstra  */
53511276d53SPeter Zijlstra 
53611276d53SPeter Zijlstra #define static_branch_enable(x)			static_key_enable(&(x)->key)
53711276d53SPeter Zijlstra #define static_branch_disable(x)		static_key_disable(&(x)->key)
5385a40527fSMarc Zyngier #define static_branch_enable_cpuslocked(x)	static_key_enable_cpuslocked(&(x)->key)
5395a40527fSMarc Zyngier #define static_branch_disable_cpuslocked(x)	static_key_disable_cpuslocked(&(x)->key)
54011276d53SPeter Zijlstra 
541c0ccf6f9SAnton Blanchard #endif /* __ASSEMBLY__ */
54285b36c93SLuis R. Rodriguez 
54385b36c93SLuis R. Rodriguez #endif	/* _LINUX_JUMP_LABEL_H */
544