xref: /linux-6.15/kernel/jump_label.c (revision 72ee1c20)
1457c8996SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2bf5438fcSJason Baron /*
3bf5438fcSJason Baron  * jump label support
4bf5438fcSJason Baron  *
5bf5438fcSJason Baron  * Copyright (C) 2009 Jason Baron <[email protected]>
690eec103SPeter Zijlstra  * Copyright (C) 2011 Peter Zijlstra
7bf5438fcSJason Baron  *
8bf5438fcSJason Baron  */
9bf5438fcSJason Baron #include <linux/memory.h>
10bf5438fcSJason Baron #include <linux/uaccess.h>
11bf5438fcSJason Baron #include <linux/module.h>
12bf5438fcSJason Baron #include <linux/list.h>
13bf5438fcSJason Baron #include <linux/slab.h>
14bf5438fcSJason Baron #include <linux/sort.h>
15bf5438fcSJason Baron #include <linux/err.h>
16c5905afbSIngo Molnar #include <linux/static_key.h>
17851cf6e7SAndrew Jones #include <linux/jump_label_ratelimit.h>
181f69bf9cSJason Baron #include <linux/bug.h>
19f2545b2dSThomas Gleixner #include <linux/cpu.h>
20578ae447SJosh Poimboeuf #include <asm/sections.h>
21bf5438fcSJason Baron 
227b7b8a2cSRandy Dunlap /* mutex to protect coming/going of the jump_label table */
23bf5438fcSJason Baron static DEFINE_MUTEX(jump_label_mutex);
24bf5438fcSJason Baron 
jump_label_lock(void)2591bad2f8SJason Baron void jump_label_lock(void)
2691bad2f8SJason Baron {
2791bad2f8SJason Baron 	mutex_lock(&jump_label_mutex);
2891bad2f8SJason Baron }
2991bad2f8SJason Baron 
jump_label_unlock(void)3091bad2f8SJason Baron void jump_label_unlock(void)
3191bad2f8SJason Baron {
3291bad2f8SJason Baron 	mutex_unlock(&jump_label_mutex);
3391bad2f8SJason Baron }
3491bad2f8SJason Baron 
jump_label_cmp(const void * a,const void * b)35bf5438fcSJason Baron static int jump_label_cmp(const void *a, const void *b)
36bf5438fcSJason Baron {
37bf5438fcSJason Baron 	const struct jump_entry *jea = a;
38bf5438fcSJason Baron 	const struct jump_entry *jeb = b;
39bf5438fcSJason Baron 
400f133021SDaniel Bristot de Oliveira 	/*
410f133021SDaniel Bristot de Oliveira 	 * Entrires are sorted by key.
420f133021SDaniel Bristot de Oliveira 	 */
439ae033acSArd Biesheuvel 	if (jump_entry_key(jea) < jump_entry_key(jeb))
44bf5438fcSJason Baron 		return -1;
45bf5438fcSJason Baron 
469ae033acSArd Biesheuvel 	if (jump_entry_key(jea) > jump_entry_key(jeb))
47bf5438fcSJason Baron 		return 1;
48bf5438fcSJason Baron 
490f133021SDaniel Bristot de Oliveira 	/*
500f133021SDaniel Bristot de Oliveira 	 * In the batching mode, entries should also be sorted by the code
510f133021SDaniel Bristot de Oliveira 	 * inside the already sorted list of entries, enabling a bsearch in
520f133021SDaniel Bristot de Oliveira 	 * the vector.
530f133021SDaniel Bristot de Oliveira 	 */
540f133021SDaniel Bristot de Oliveira 	if (jump_entry_code(jea) < jump_entry_code(jeb))
550f133021SDaniel Bristot de Oliveira 		return -1;
560f133021SDaniel Bristot de Oliveira 
570f133021SDaniel Bristot de Oliveira 	if (jump_entry_code(jea) > jump_entry_code(jeb))
580f133021SDaniel Bristot de Oliveira 		return 1;
590f133021SDaniel Bristot de Oliveira 
60bf5438fcSJason Baron 	return 0;
61bf5438fcSJason Baron }
62bf5438fcSJason Baron 
jump_label_swap(void * a,void * b,int size)6350ff18abSArd Biesheuvel static void jump_label_swap(void *a, void *b, int size)
6450ff18abSArd Biesheuvel {
6550ff18abSArd Biesheuvel 	long delta = (unsigned long)a - (unsigned long)b;
6650ff18abSArd Biesheuvel 	struct jump_entry *jea = a;
6750ff18abSArd Biesheuvel 	struct jump_entry *jeb = b;
6850ff18abSArd Biesheuvel 	struct jump_entry tmp = *jea;
6950ff18abSArd Biesheuvel 
7050ff18abSArd Biesheuvel 	jea->code	= jeb->code - delta;
7150ff18abSArd Biesheuvel 	jea->target	= jeb->target - delta;
7250ff18abSArd Biesheuvel 	jea->key	= jeb->key - delta;
7350ff18abSArd Biesheuvel 
7450ff18abSArd Biesheuvel 	jeb->code	= tmp.code + delta;
7550ff18abSArd Biesheuvel 	jeb->target	= tmp.target + delta;
7650ff18abSArd Biesheuvel 	jeb->key	= tmp.key + delta;
7750ff18abSArd Biesheuvel }
7850ff18abSArd Biesheuvel 
79bf5438fcSJason Baron static void
jump_label_sort_entries(struct jump_entry * start,struct jump_entry * stop)80d430d3d7SJason Baron jump_label_sort_entries(struct jump_entry *start, struct jump_entry *stop)
81bf5438fcSJason Baron {
82bf5438fcSJason Baron 	unsigned long size;
8350ff18abSArd Biesheuvel 	void *swapfn = NULL;
8450ff18abSArd Biesheuvel 
8550ff18abSArd Biesheuvel 	if (IS_ENABLED(CONFIG_HAVE_ARCH_JUMP_LABEL_RELATIVE))
8650ff18abSArd Biesheuvel 		swapfn = jump_label_swap;
87bf5438fcSJason Baron 
88bf5438fcSJason Baron 	size = (((unsigned long)stop - (unsigned long)start)
89bf5438fcSJason Baron 					/ sizeof(struct jump_entry));
9050ff18abSArd Biesheuvel 	sort(start, size, sizeof(struct jump_entry), jump_label_cmp, swapfn);
91bf5438fcSJason Baron }
92bf5438fcSJason Baron 
93706249c2SPeter Zijlstra static void jump_label_update(struct static_key *key);
94a1efb01fSPeter Zijlstra 
951f69bf9cSJason Baron /*
96e9666d10SMasahiro Yamada  * There are similar definitions for the !CONFIG_JUMP_LABEL case in jump_label.h.
971f69bf9cSJason Baron  * The use of 'atomic_read()' requires atomic.h and its problematic for some
981f69bf9cSJason Baron  * kernel headers such as kernel.h and others. Since static_key_count() is not
99e9666d10SMasahiro Yamada  * used in the branch statements as it is for the !CONFIG_JUMP_LABEL case its ok
1001f69bf9cSJason Baron  * to have it be a function here. Similarly, for 'static_key_enable()' and
1011f69bf9cSJason Baron  * 'static_key_disable()', which require bug.h. This should allow jump_label.h
102e9666d10SMasahiro Yamada  * to be included from most/all places for CONFIG_JUMP_LABEL.
1031f69bf9cSJason Baron  */
static_key_count(struct static_key * key)1041f69bf9cSJason Baron int static_key_count(struct static_key *key)
1051f69bf9cSJason Baron {
1061f69bf9cSJason Baron 	/*
1071f69bf9cSJason Baron 	 * -1 means the first static_key_slow_inc() is in progress.
1081f69bf9cSJason Baron 	 *  static_key_enabled() must return true, so return 1 here.
1091f69bf9cSJason Baron 	 */
1101f69bf9cSJason Baron 	int n = atomic_read(&key->enabled);
1111f69bf9cSJason Baron 
1121f69bf9cSJason Baron 	return n >= 0 ? n : 1;
1131f69bf9cSJason Baron }
1141f69bf9cSJason Baron EXPORT_SYMBOL_GPL(static_key_count);
1151f69bf9cSJason Baron 
116eb8c5072SDmitry Safonov /*
117eb8c5072SDmitry Safonov  * static_key_fast_inc_not_disabled - adds a user for a static key
118eb8c5072SDmitry Safonov  * @key: static key that must be already enabled
119eb8c5072SDmitry Safonov  *
120eb8c5072SDmitry Safonov  * The caller must make sure that the static key can't get disabled while
121eb8c5072SDmitry Safonov  * in this function. It doesn't patch jump labels, only adds a user to
122eb8c5072SDmitry Safonov  * an already enabled static key.
123eb8c5072SDmitry Safonov  *
124eb8c5072SDmitry Safonov  * Returns true if the increment was done. Unlike refcount_t the ref counter
125eb8c5072SDmitry Safonov  * is not saturated, but will fail to increment on overflow.
126eb8c5072SDmitry Safonov  */
static_key_fast_inc_not_disabled(struct static_key * key)127eb8c5072SDmitry Safonov bool static_key_fast_inc_not_disabled(struct static_key *key)
128bf5438fcSJason Baron {
129eb8c5072SDmitry Safonov 	int v;
130eb8c5072SDmitry Safonov 
1315cdda511SBorislav Petkov 	STATIC_KEY_CHECK_USE(key);
132eb8c5072SDmitry Safonov 	/*
133eb8c5072SDmitry Safonov 	 * Negative key->enabled has a special meaning: it sends
13483ab38efSThomas Gleixner 	 * static_key_slow_inc/dec() down the slow path, and it is non-zero
135695ef796SThomas Gleixner 	 * so it counts as "enabled" in jump_label_update().
136695ef796SThomas Gleixner 	 *
137695ef796SThomas Gleixner 	 * The INT_MAX overflow condition is either used by the networking
138695ef796SThomas Gleixner 	 * code to reset or detected in the slow path of
139695ef796SThomas Gleixner 	 * static_key_slow_inc_cpuslocked().
140eb8c5072SDmitry Safonov 	 */
141eb8c5072SDmitry Safonov 	v = atomic_read(&key->enabled);
142eb8c5072SDmitry Safonov 	do {
143695ef796SThomas Gleixner 		if (v <= 0 || v == INT_MAX)
144eb8c5072SDmitry Safonov 			return false;
145eb8c5072SDmitry Safonov 	} while (!likely(atomic_try_cmpxchg(&key->enabled, &v, v + 1)));
146eb8c5072SDmitry Safonov 
147eb8c5072SDmitry Safonov 	return true;
148eb8c5072SDmitry Safonov }
149eb8c5072SDmitry Safonov EXPORT_SYMBOL_GPL(static_key_fast_inc_not_disabled);
150eb8c5072SDmitry Safonov 
static_key_slow_inc_cpuslocked(struct static_key * key)151eb8c5072SDmitry Safonov bool static_key_slow_inc_cpuslocked(struct static_key *key)
152eb8c5072SDmitry Safonov {
153cb538267SPeter Zijlstra 	lockdep_assert_cpus_held();
1544c5ea0a9SPaolo Bonzini 
1554c5ea0a9SPaolo Bonzini 	/*
15683ab38efSThomas Gleixner 	 * Careful if we get concurrent static_key_slow_inc/dec() calls;
1574c5ea0a9SPaolo Bonzini 	 * later calls must wait for the first one to _finish_ the
1584c5ea0a9SPaolo Bonzini 	 * jump_label_update() process.  At the same time, however,
1594c5ea0a9SPaolo Bonzini 	 * the jump_label_update() call below wants to see
1604c5ea0a9SPaolo Bonzini 	 * static_key_enabled(&key) for jumps to be updated properly.
1614c5ea0a9SPaolo Bonzini 	 */
162eb8c5072SDmitry Safonov 	if (static_key_fast_inc_not_disabled(key))
163eb8c5072SDmitry Safonov 		return true;
164bf5438fcSJason Baron 
1659bc2ff87SThomas Gleixner 	guard(mutex)(&jump_label_mutex);
1669bc2ff87SThomas Gleixner 	/* Try to mark it as 'enabling in progress. */
1679bc2ff87SThomas Gleixner 	if (!atomic_cmpxchg(&key->enabled, 0, -1)) {
168706249c2SPeter Zijlstra 		jump_label_update(key);
169d0646a6fSPeter Zijlstra 		/*
1709bc2ff87SThomas Gleixner 		 * Ensure that when static_key_fast_inc_not_disabled() or
1711d7f856cSPeter Zijlstra 		 * static_key_dec_not_one() observe the positive value,
1729bc2ff87SThomas Gleixner 		 * they must also observe all the text changes.
173d0646a6fSPeter Zijlstra 		 */
174d0646a6fSPeter Zijlstra 		atomic_set_release(&key->enabled, 1);
1754c5ea0a9SPaolo Bonzini 	} else {
1769bc2ff87SThomas Gleixner 		/*
1779bc2ff87SThomas Gleixner 		 * While holding the mutex this should never observe
1789bc2ff87SThomas Gleixner 		 * anything else than a value >= 1 and succeed
1799bc2ff87SThomas Gleixner 		 */
1809bc2ff87SThomas Gleixner 		if (WARN_ON_ONCE(!static_key_fast_inc_not_disabled(key)))
181eb8c5072SDmitry Safonov 			return false;
182eb8c5072SDmitry Safonov 	}
183eb8c5072SDmitry Safonov 	return true;
1848b7b4128SMarc Zyngier }
1858b7b4128SMarc Zyngier 
static_key_slow_inc(struct static_key * key)186eb8c5072SDmitry Safonov bool static_key_slow_inc(struct static_key *key)
1878b7b4128SMarc Zyngier {
188eb8c5072SDmitry Safonov 	bool ret;
189eb8c5072SDmitry Safonov 
1908b7b4128SMarc Zyngier 	cpus_read_lock();
191eb8c5072SDmitry Safonov 	ret = static_key_slow_inc_cpuslocked(key);
192f2545b2dSThomas Gleixner 	cpus_read_unlock();
193eb8c5072SDmitry Safonov 	return ret;
194bf5438fcSJason Baron }
195c5905afbSIngo Molnar EXPORT_SYMBOL_GPL(static_key_slow_inc);
196d430d3d7SJason Baron 
static_key_enable_cpuslocked(struct static_key * key)1975a40527fSMarc Zyngier void static_key_enable_cpuslocked(struct static_key *key)
1981dbb6704SPaolo Bonzini {
1995cdda511SBorislav Petkov 	STATIC_KEY_CHECK_USE(key);
200cb538267SPeter Zijlstra 	lockdep_assert_cpus_held();
2015a40527fSMarc Zyngier 
2021dbb6704SPaolo Bonzini 	if (atomic_read(&key->enabled) > 0) {
2031dbb6704SPaolo Bonzini 		WARN_ON_ONCE(atomic_read(&key->enabled) != 1);
2041dbb6704SPaolo Bonzini 		return;
2051dbb6704SPaolo Bonzini 	}
2061dbb6704SPaolo Bonzini 
2071dbb6704SPaolo Bonzini 	jump_label_lock();
2081dbb6704SPaolo Bonzini 	if (atomic_read(&key->enabled) == 0) {
2091dbb6704SPaolo Bonzini 		atomic_set(&key->enabled, -1);
2101dbb6704SPaolo Bonzini 		jump_label_update(key);
211d0646a6fSPeter Zijlstra 		/*
212d0646a6fSPeter Zijlstra 		 * See static_key_slow_inc().
213d0646a6fSPeter Zijlstra 		 */
214d0646a6fSPeter Zijlstra 		atomic_set_release(&key->enabled, 1);
2151dbb6704SPaolo Bonzini 	}
2161dbb6704SPaolo Bonzini 	jump_label_unlock();
2175a40527fSMarc Zyngier }
2185a40527fSMarc Zyngier EXPORT_SYMBOL_GPL(static_key_enable_cpuslocked);
2195a40527fSMarc Zyngier 
static_key_enable(struct static_key * key)2205a40527fSMarc Zyngier void static_key_enable(struct static_key *key)
2215a40527fSMarc Zyngier {
2225a40527fSMarc Zyngier 	cpus_read_lock();
2235a40527fSMarc Zyngier 	static_key_enable_cpuslocked(key);
2241dbb6704SPaolo Bonzini 	cpus_read_unlock();
2251dbb6704SPaolo Bonzini }
2261dbb6704SPaolo Bonzini EXPORT_SYMBOL_GPL(static_key_enable);
2271dbb6704SPaolo Bonzini 
static_key_disable_cpuslocked(struct static_key * key)2285a40527fSMarc Zyngier void static_key_disable_cpuslocked(struct static_key *key)
2291dbb6704SPaolo Bonzini {
2305cdda511SBorislav Petkov 	STATIC_KEY_CHECK_USE(key);
231cb538267SPeter Zijlstra 	lockdep_assert_cpus_held();
2325a40527fSMarc Zyngier 
2331dbb6704SPaolo Bonzini 	if (atomic_read(&key->enabled) != 1) {
2341dbb6704SPaolo Bonzini 		WARN_ON_ONCE(atomic_read(&key->enabled) != 0);
2351dbb6704SPaolo Bonzini 		return;
2361dbb6704SPaolo Bonzini 	}
2371dbb6704SPaolo Bonzini 
2381dbb6704SPaolo Bonzini 	jump_label_lock();
239224fa355SPeter Zijlstra 	if (atomic_cmpxchg(&key->enabled, 1, 0) == 1)
2401dbb6704SPaolo Bonzini 		jump_label_update(key);
2411dbb6704SPaolo Bonzini 	jump_label_unlock();
2425a40527fSMarc Zyngier }
2435a40527fSMarc Zyngier EXPORT_SYMBOL_GPL(static_key_disable_cpuslocked);
2445a40527fSMarc Zyngier 
static_key_disable(struct static_key * key)2455a40527fSMarc Zyngier void static_key_disable(struct static_key *key)
2465a40527fSMarc Zyngier {
2475a40527fSMarc Zyngier 	cpus_read_lock();
2485a40527fSMarc Zyngier 	static_key_disable_cpuslocked(key);
2491dbb6704SPaolo Bonzini 	cpus_read_unlock();
2501dbb6704SPaolo Bonzini }
2511dbb6704SPaolo Bonzini EXPORT_SYMBOL_GPL(static_key_disable);
2521dbb6704SPaolo Bonzini 
static_key_dec_not_one(struct static_key * key)2531d7f856cSPeter Zijlstra static bool static_key_dec_not_one(struct static_key *key)
254d430d3d7SJason Baron {
25583ab38efSThomas Gleixner 	int v;
256cb538267SPeter Zijlstra 
2574c5ea0a9SPaolo Bonzini 	/*
25883ab38efSThomas Gleixner 	 * Go into the slow path if key::enabled is less than or equal than
25983ab38efSThomas Gleixner 	 * one. One is valid to shut down the key, anything less than one
26083ab38efSThomas Gleixner 	 * is an imbalance, which is handled at the call site.
26183ab38efSThomas Gleixner 	 *
26283ab38efSThomas Gleixner 	 * That includes the special case of '-1' which is set in
26383ab38efSThomas Gleixner 	 * static_key_slow_inc_cpuslocked(), but that's harmless as it is
26483ab38efSThomas Gleixner 	 * fully serialized in the slow path below. By the time this task
26583ab38efSThomas Gleixner 	 * acquires the jump label lock the value is back to one and the
26683ab38efSThomas Gleixner 	 * retry under the lock must succeed.
2674c5ea0a9SPaolo Bonzini 	 */
26883ab38efSThomas Gleixner 	v = atomic_read(&key->enabled);
26983ab38efSThomas Gleixner 	do {
27083ab38efSThomas Gleixner 		/*
27183ab38efSThomas Gleixner 		 * Warn about the '-1' case though; since that means a
27283ab38efSThomas Gleixner 		 * decrement is concurrent with a first (0->1) increment. IOW
27383ab38efSThomas Gleixner 		 * people are trying to disable something that wasn't yet fully
27483ab38efSThomas Gleixner 		 * enabled. This suggests an ordering problem on the user side.
27583ab38efSThomas Gleixner 		 */
27683ab38efSThomas Gleixner 		WARN_ON_ONCE(v < 0);
2771d7f856cSPeter Zijlstra 
2781d7f856cSPeter Zijlstra 		/*
2791d7f856cSPeter Zijlstra 		 * Warn about underflow, and lie about success in an attempt to
2801d7f856cSPeter Zijlstra 		 * not make things worse.
2811d7f856cSPeter Zijlstra 		 */
2821d7f856cSPeter Zijlstra 		if (WARN_ON_ONCE(v == 0))
2831d7f856cSPeter Zijlstra 			return true;
2841d7f856cSPeter Zijlstra 
28583ab38efSThomas Gleixner 		if (v <= 1)
28683ab38efSThomas Gleixner 			return false;
28783ab38efSThomas Gleixner 	} while (!likely(atomic_try_cmpxchg(&key->enabled, &v, v - 1)));
28883ab38efSThomas Gleixner 
289b92e793bSJakub Kicinski 	return true;
290fadf0464SJason Baron }
291d430d3d7SJason Baron 
__static_key_slow_dec_cpuslocked(struct static_key * key)29294b5f312SJakub Kicinski static void __static_key_slow_dec_cpuslocked(struct static_key *key)
293b92e793bSJakub Kicinski {
294b92e793bSJakub Kicinski 	lockdep_assert_cpus_held();
2951d7f856cSPeter Zijlstra 	int val;
296b92e793bSJakub Kicinski 
2971d7f856cSPeter Zijlstra 	if (static_key_dec_not_one(key))
298b92e793bSJakub Kicinski 		return;
299b92e793bSJakub Kicinski 
30083ab38efSThomas Gleixner 	guard(mutex)(&jump_label_mutex);
3011d7f856cSPeter Zijlstra 	val = atomic_read(&key->enabled);
3021d7f856cSPeter Zijlstra 	/*
3031d7f856cSPeter Zijlstra 	 * It should be impossible to observe -1 with jump_label_mutex held,
3041d7f856cSPeter Zijlstra 	 * see static_key_slow_inc_cpuslocked().
3051d7f856cSPeter Zijlstra 	 */
3061d7f856cSPeter Zijlstra 	if (WARN_ON_ONCE(val == -1))
3071d7f856cSPeter Zijlstra 		return;
3081d7f856cSPeter Zijlstra 	/*
3091d7f856cSPeter Zijlstra 	 * Cannot already be 0, something went sideways.
3101d7f856cSPeter Zijlstra 	 */
3111d7f856cSPeter Zijlstra 	if (WARN_ON_ONCE(val == 0))
3121d7f856cSPeter Zijlstra 		return;
3131d7f856cSPeter Zijlstra 
3141d7f856cSPeter Zijlstra 	if (atomic_dec_and_test(&key->enabled))
315706249c2SPeter Zijlstra 		jump_label_update(key);
3168b7b4128SMarc Zyngier }
3178b7b4128SMarc Zyngier 
__static_key_slow_dec(struct static_key * key)31894b5f312SJakub Kicinski static void __static_key_slow_dec(struct static_key *key)
3198b7b4128SMarc Zyngier {
3208b7b4128SMarc Zyngier 	cpus_read_lock();
32194b5f312SJakub Kicinski 	__static_key_slow_dec_cpuslocked(key);
322f2545b2dSThomas Gleixner 	cpus_read_unlock();
323bf5438fcSJason Baron }
324bf5438fcSJason Baron 
jump_label_update_timeout(struct work_struct * work)325ad282a81SJakub Kicinski void jump_label_update_timeout(struct work_struct *work)
326b2029520SGleb Natapov {
327c5905afbSIngo Molnar 	struct static_key_deferred *key =
328c5905afbSIngo Molnar 		container_of(work, struct static_key_deferred, work.work);
32994b5f312SJakub Kicinski 	__static_key_slow_dec(&key->key);
330b2029520SGleb Natapov }
331ad282a81SJakub Kicinski EXPORT_SYMBOL_GPL(jump_label_update_timeout);
332b2029520SGleb Natapov 
static_key_slow_dec(struct static_key * key)333c5905afbSIngo Molnar void static_key_slow_dec(struct static_key *key)
334b2029520SGleb Natapov {
3355cdda511SBorislav Petkov 	STATIC_KEY_CHECK_USE(key);
33694b5f312SJakub Kicinski 	__static_key_slow_dec(key);
337b2029520SGleb Natapov }
338c5905afbSIngo Molnar EXPORT_SYMBOL_GPL(static_key_slow_dec);
339b2029520SGleb Natapov 
static_key_slow_dec_cpuslocked(struct static_key * key)340ce48c146SPeter Zijlstra void static_key_slow_dec_cpuslocked(struct static_key *key)
341ce48c146SPeter Zijlstra {
342ce48c146SPeter Zijlstra 	STATIC_KEY_CHECK_USE(key);
34394b5f312SJakub Kicinski 	__static_key_slow_dec_cpuslocked(key);
344ce48c146SPeter Zijlstra }
345ce48c146SPeter Zijlstra 
__static_key_slow_dec_deferred(struct static_key * key,struct delayed_work * work,unsigned long timeout)346ad282a81SJakub Kicinski void __static_key_slow_dec_deferred(struct static_key *key,
347ad282a81SJakub Kicinski 				    struct delayed_work *work,
348ad282a81SJakub Kicinski 				    unsigned long timeout)
349b2029520SGleb Natapov {
3505cdda511SBorislav Petkov 	STATIC_KEY_CHECK_USE(key);
35194b5f312SJakub Kicinski 
3521d7f856cSPeter Zijlstra 	if (static_key_dec_not_one(key))
35394b5f312SJakub Kicinski 		return;
35494b5f312SJakub Kicinski 
35594b5f312SJakub Kicinski 	schedule_delayed_work(work, timeout);
356b2029520SGleb Natapov }
357ad282a81SJakub Kicinski EXPORT_SYMBOL_GPL(__static_key_slow_dec_deferred);
358b2029520SGleb Natapov 
__static_key_deferred_flush(void * key,struct delayed_work * work)359ad282a81SJakub Kicinski void __static_key_deferred_flush(void *key, struct delayed_work *work)
360b6416e61SDavid Matlack {
3615cdda511SBorislav Petkov 	STATIC_KEY_CHECK_USE(key);
362ad282a81SJakub Kicinski 	flush_delayed_work(work);
363b6416e61SDavid Matlack }
364ad282a81SJakub Kicinski EXPORT_SYMBOL_GPL(__static_key_deferred_flush);
365b6416e61SDavid Matlack 
jump_label_rate_limit(struct static_key_deferred * key,unsigned long rl)366c5905afbSIngo Molnar void jump_label_rate_limit(struct static_key_deferred *key,
367b2029520SGleb Natapov 		unsigned long rl)
368b2029520SGleb Natapov {
3695cdda511SBorislav Petkov 	STATIC_KEY_CHECK_USE(key);
370b2029520SGleb Natapov 	key->timeout = rl;
371b2029520SGleb Natapov 	INIT_DELAYED_WORK(&key->work, jump_label_update_timeout);
372b2029520SGleb Natapov }
373a181dc14SGleb Natapov EXPORT_SYMBOL_GPL(jump_label_rate_limit);
374b2029520SGleb Natapov 
addr_conflict(struct jump_entry * entry,void * start,void * end)3754c3ef6d7SJason Baron static int addr_conflict(struct jump_entry *entry, void *start, void *end)
3764c3ef6d7SJason Baron {
3779ae033acSArd Biesheuvel 	if (jump_entry_code(entry) <= (unsigned long)end &&
378fa5e5dc3SPeter Zijlstra 	    jump_entry_code(entry) + jump_entry_size(entry) > (unsigned long)start)
3794c3ef6d7SJason Baron 		return 1;
3804c3ef6d7SJason Baron 
3814c3ef6d7SJason Baron 	return 0;
3824c3ef6d7SJason Baron }
3834c3ef6d7SJason Baron 
__jump_label_text_reserved(struct jump_entry * iter_start,struct jump_entry * iter_stop,void * start,void * end,bool init)384d430d3d7SJason Baron static int __jump_label_text_reserved(struct jump_entry *iter_start,
3859e667624SPeter Zijlstra 		struct jump_entry *iter_stop, void *start, void *end, bool init)
3864c3ef6d7SJason Baron {
3874c3ef6d7SJason Baron 	struct jump_entry *iter;
3884c3ef6d7SJason Baron 
389d430d3d7SJason Baron 	iter = iter_start;
390d430d3d7SJason Baron 	while (iter < iter_stop) {
3919e667624SPeter Zijlstra 		if (init || !jump_entry_is_init(iter)) {
392d430d3d7SJason Baron 			if (addr_conflict(iter, start, end))
393d430d3d7SJason Baron 				return 1;
3949e667624SPeter Zijlstra 		}
3954c3ef6d7SJason Baron 		iter++;
3964c3ef6d7SJason Baron 	}
397d430d3d7SJason Baron 
398d430d3d7SJason Baron 	return 0;
3994c3ef6d7SJason Baron }
4004c3ef6d7SJason Baron 
4017e6b9db2SArd Biesheuvel #ifndef arch_jump_label_transform_static
arch_jump_label_transform_static(struct jump_entry * entry,enum jump_label_type type)4027e6b9db2SArd Biesheuvel static void arch_jump_label_transform_static(struct jump_entry *entry,
40320284aa7SJeremy Fitzhardinge 					     enum jump_label_type type)
40420284aa7SJeremy Fitzhardinge {
4057e6b9db2SArd Biesheuvel 	/* nothing to do on most architectures */
40620284aa7SJeremy Fitzhardinge }
4077e6b9db2SArd Biesheuvel #endif
40820284aa7SJeremy Fitzhardinge 
static_key_entries(struct static_key * key)409a1efb01fSPeter Zijlstra static inline struct jump_entry *static_key_entries(struct static_key *key)
410a1efb01fSPeter Zijlstra {
4113821fd35SJason Baron 	WARN_ON_ONCE(key->type & JUMP_TYPE_LINKED);
4123821fd35SJason Baron 	return (struct jump_entry *)(key->type & ~JUMP_TYPE_MASK);
413a1efb01fSPeter Zijlstra }
414a1efb01fSPeter Zijlstra 
static_key_type(struct static_key * key)415706249c2SPeter Zijlstra static inline bool static_key_type(struct static_key *key)
416706249c2SPeter Zijlstra {
4173821fd35SJason Baron 	return key->type & JUMP_TYPE_TRUE;
4183821fd35SJason Baron }
4193821fd35SJason Baron 
static_key_linked(struct static_key * key)4203821fd35SJason Baron static inline bool static_key_linked(struct static_key *key)
4213821fd35SJason Baron {
4223821fd35SJason Baron 	return key->type & JUMP_TYPE_LINKED;
4233821fd35SJason Baron }
4243821fd35SJason Baron 
static_key_clear_linked(struct static_key * key)4253821fd35SJason Baron static inline void static_key_clear_linked(struct static_key *key)
4263821fd35SJason Baron {
4273821fd35SJason Baron 	key->type &= ~JUMP_TYPE_LINKED;
4283821fd35SJason Baron }
4293821fd35SJason Baron 
static_key_set_linked(struct static_key * key)4303821fd35SJason Baron static inline void static_key_set_linked(struct static_key *key)
4313821fd35SJason Baron {
4323821fd35SJason Baron 	key->type |= JUMP_TYPE_LINKED;
433706249c2SPeter Zijlstra }
434706249c2SPeter Zijlstra 
4353821fd35SJason Baron /***
4363821fd35SJason Baron  * A 'struct static_key' uses a union such that it either points directly
4373821fd35SJason Baron  * to a table of 'struct jump_entry' or to a linked list of modules which in
4383821fd35SJason Baron  * turn point to 'struct jump_entry' tables.
4393821fd35SJason Baron  *
4403821fd35SJason Baron  * The two lower bits of the pointer are used to keep track of which pointer
4413821fd35SJason Baron  * type is in use and to store the initial branch direction, we use an access
4423821fd35SJason Baron  * function which preserves these bits.
4433821fd35SJason Baron  */
static_key_set_entries(struct static_key * key,struct jump_entry * entries)4443821fd35SJason Baron static void static_key_set_entries(struct static_key *key,
4453821fd35SJason Baron 				   struct jump_entry *entries)
4463821fd35SJason Baron {
4473821fd35SJason Baron 	unsigned long type;
4483821fd35SJason Baron 
4493821fd35SJason Baron 	WARN_ON_ONCE((unsigned long)entries & JUMP_TYPE_MASK);
4503821fd35SJason Baron 	type = key->type & JUMP_TYPE_MASK;
4513821fd35SJason Baron 	key->entries = entries;
4523821fd35SJason Baron 	key->type |= type;
4533821fd35SJason Baron }
4543821fd35SJason Baron 
jump_label_type(struct jump_entry * entry)455706249c2SPeter Zijlstra static enum jump_label_type jump_label_type(struct jump_entry *entry)
456c5905afbSIngo Molnar {
457706249c2SPeter Zijlstra 	struct static_key *key = jump_entry_key(entry);
458a1efb01fSPeter Zijlstra 	bool enabled = static_key_enabled(key);
4599ae033acSArd Biesheuvel 	bool branch = jump_entry_is_branch(entry);
460c5905afbSIngo Molnar 
46111276d53SPeter Zijlstra 	/* See the comment in linux/jump_label.h */
46211276d53SPeter Zijlstra 	return enabled ^ branch;
463c5905afbSIngo Molnar }
464c5905afbSIngo Molnar 
jump_label_can_update(struct jump_entry * entry,bool init)465e1aacb3fSDaniel Bristot de Oliveira static bool jump_label_can_update(struct jump_entry *entry, bool init)
466e1aacb3fSDaniel Bristot de Oliveira {
467e1aacb3fSDaniel Bristot de Oliveira 	/*
468e1aacb3fSDaniel Bristot de Oliveira 	 * Cannot update code that was in an init text area.
469e1aacb3fSDaniel Bristot de Oliveira 	 */
470e1aacb3fSDaniel Bristot de Oliveira 	if (!init && jump_entry_is_init(entry))
471e1aacb3fSDaniel Bristot de Oliveira 		return false;
472e1aacb3fSDaniel Bristot de Oliveira 
473e1aacb3fSDaniel Bristot de Oliveira 	if (!kernel_text_address(jump_entry_code(entry))) {
47438c93587SPeter Zijlstra 		/*
47538c93587SPeter Zijlstra 		 * This skips patching built-in __exit, which
47638c93587SPeter Zijlstra 		 * is part of init_section_contains() but is
47738c93587SPeter Zijlstra 		 * not part of kernel_text_address().
47838c93587SPeter Zijlstra 		 *
47938c93587SPeter Zijlstra 		 * Skipping built-in __exit is fine since it
48038c93587SPeter Zijlstra 		 * will never be executed.
48138c93587SPeter Zijlstra 		 */
4828f35eaa5SAndrew Murray 		WARN_ONCE(!jump_entry_is_init(entry),
4838f35eaa5SAndrew Murray 			  "can't patch jump_label at %pS",
4848f35eaa5SAndrew Murray 			  (void *)jump_entry_code(entry));
485e1aacb3fSDaniel Bristot de Oliveira 		return false;
486e1aacb3fSDaniel Bristot de Oliveira 	}
487e1aacb3fSDaniel Bristot de Oliveira 
488e1aacb3fSDaniel Bristot de Oliveira 	return true;
489e1aacb3fSDaniel Bristot de Oliveira }
490e1aacb3fSDaniel Bristot de Oliveira 
491c2ba8a15SDaniel Bristot de Oliveira #ifndef HAVE_JUMP_LABEL_BATCH
__jump_label_update(struct static_key * key,struct jump_entry * entry,struct jump_entry * stop,bool init)492706249c2SPeter Zijlstra static void __jump_label_update(struct static_key *key,
493706249c2SPeter Zijlstra 				struct jump_entry *entry,
49419483677SArd Biesheuvel 				struct jump_entry *stop,
49519483677SArd Biesheuvel 				bool init)
496706249c2SPeter Zijlstra {
497706249c2SPeter Zijlstra 	for (; (entry < stop) && (jump_entry_key(entry) == key); entry++) {
498e1aacb3fSDaniel Bristot de Oliveira 		if (jump_label_can_update(entry, init))
499706249c2SPeter Zijlstra 			arch_jump_label_transform(entry, jump_label_type(entry));
500706249c2SPeter Zijlstra 	}
501706249c2SPeter Zijlstra }
502c2ba8a15SDaniel Bristot de Oliveira #else
__jump_label_update(struct static_key * key,struct jump_entry * entry,struct jump_entry * stop,bool init)503c2ba8a15SDaniel Bristot de Oliveira static void __jump_label_update(struct static_key *key,
504c2ba8a15SDaniel Bristot de Oliveira 				struct jump_entry *entry,
505c2ba8a15SDaniel Bristot de Oliveira 				struct jump_entry *stop,
506c2ba8a15SDaniel Bristot de Oliveira 				bool init)
507c2ba8a15SDaniel Bristot de Oliveira {
508c2ba8a15SDaniel Bristot de Oliveira 	for (; (entry < stop) && (jump_entry_key(entry) == key); entry++) {
509c2ba8a15SDaniel Bristot de Oliveira 
510c2ba8a15SDaniel Bristot de Oliveira 		if (!jump_label_can_update(entry, init))
511c2ba8a15SDaniel Bristot de Oliveira 			continue;
512c2ba8a15SDaniel Bristot de Oliveira 
513c2ba8a15SDaniel Bristot de Oliveira 		if (!arch_jump_label_transform_queue(entry, jump_label_type(entry))) {
514c2ba8a15SDaniel Bristot de Oliveira 			/*
515c2ba8a15SDaniel Bristot de Oliveira 			 * Queue is full: Apply the current queue and try again.
516c2ba8a15SDaniel Bristot de Oliveira 			 */
517c2ba8a15SDaniel Bristot de Oliveira 			arch_jump_label_transform_apply();
518c2ba8a15SDaniel Bristot de Oliveira 			BUG_ON(!arch_jump_label_transform_queue(entry, jump_label_type(entry)));
519c2ba8a15SDaniel Bristot de Oliveira 		}
520c2ba8a15SDaniel Bristot de Oliveira 	}
521c2ba8a15SDaniel Bristot de Oliveira 	arch_jump_label_transform_apply();
522c2ba8a15SDaniel Bristot de Oliveira }
523c2ba8a15SDaniel Bristot de Oliveira #endif
524706249c2SPeter Zijlstra 
jump_label_init(void)52597ce2c88SJeremy Fitzhardinge void __init jump_label_init(void)
526d430d3d7SJason Baron {
527d430d3d7SJason Baron 	struct jump_entry *iter_start = __start___jump_table;
528d430d3d7SJason Baron 	struct jump_entry *iter_stop = __stop___jump_table;
529c5905afbSIngo Molnar 	struct static_key *key = NULL;
530d430d3d7SJason Baron 	struct jump_entry *iter;
531d430d3d7SJason Baron 
5321f69bf9cSJason Baron 	/*
5331f69bf9cSJason Baron 	 * Since we are initializing the static_key.enabled field with
5341f69bf9cSJason Baron 	 * with the 'raw' int values (to avoid pulling in atomic.h) in
5351f69bf9cSJason Baron 	 * jump_label.h, let's make sure that is safe. There are only two
5361f69bf9cSJason Baron 	 * cases to check since we initialize to 0 or 1.
5371f69bf9cSJason Baron 	 */
5381f69bf9cSJason Baron 	BUILD_BUG_ON((int)ATOMIC_INIT(0) != 0);
5391f69bf9cSJason Baron 	BUILD_BUG_ON((int)ATOMIC_INIT(1) != 1);
5401f69bf9cSJason Baron 
541e3f91083SKevin Hao 	if (static_key_initialized)
542e3f91083SKevin Hao 		return;
543e3f91083SKevin Hao 
544f2545b2dSThomas Gleixner 	cpus_read_lock();
545d430d3d7SJason Baron 	jump_label_lock();
546d430d3d7SJason Baron 	jump_label_sort_entries(iter_start, iter_stop);
547d430d3d7SJason Baron 
548d430d3d7SJason Baron 	for (iter = iter_start; iter < iter_stop; iter++) {
549c5905afbSIngo Molnar 		struct static_key *iterk;
5505af0ea29SPeter Zijlstra 		bool in_init;
55137348804SJeremy Fitzhardinge 
55211276d53SPeter Zijlstra 		/* rewrite NOPs */
55311276d53SPeter Zijlstra 		if (jump_label_type(iter) == JUMP_LABEL_NOP)
55411276d53SPeter Zijlstra 			arch_jump_label_transform_static(iter, JUMP_LABEL_NOP);
55511276d53SPeter Zijlstra 
5565af0ea29SPeter Zijlstra 		in_init = init_section_contains((void *)jump_entry_code(iter), 1);
5575af0ea29SPeter Zijlstra 		jump_entry_set_init(iter, in_init);
55819483677SArd Biesheuvel 
5597dcfd915SPeter Zijlstra 		iterk = jump_entry_key(iter);
56037348804SJeremy Fitzhardinge 		if (iterk == key)
561d430d3d7SJason Baron 			continue;
562d430d3d7SJason Baron 
56337348804SJeremy Fitzhardinge 		key = iterk;
5643821fd35SJason Baron 		static_key_set_entries(key, iter);
565d430d3d7SJason Baron 	}
566c4b2c0c5SHannes Frederic Sowa 	static_key_initialized = true;
567d430d3d7SJason Baron 	jump_label_unlock();
568f2545b2dSThomas Gleixner 	cpus_read_unlock();
569d430d3d7SJason Baron }
570d430d3d7SJason Baron 
static_key_sealed(struct static_key * key)57191a1d97eSPeter Zijlstra static inline bool static_key_sealed(struct static_key *key)
57291a1d97eSPeter Zijlstra {
57391a1d97eSPeter Zijlstra 	return (key->type & JUMP_TYPE_LINKED) && !(key->type & ~JUMP_TYPE_MASK);
57491a1d97eSPeter Zijlstra }
57591a1d97eSPeter Zijlstra 
static_key_seal(struct static_key * key)57691a1d97eSPeter Zijlstra static inline void static_key_seal(struct static_key *key)
57791a1d97eSPeter Zijlstra {
57891a1d97eSPeter Zijlstra 	unsigned long type = key->type & JUMP_TYPE_TRUE;
57991a1d97eSPeter Zijlstra 	key->type = JUMP_TYPE_LINKED | type;
58091a1d97eSPeter Zijlstra }
58191a1d97eSPeter Zijlstra 
jump_label_init_ro(void)58291a1d97eSPeter Zijlstra void jump_label_init_ro(void)
58391a1d97eSPeter Zijlstra {
58491a1d97eSPeter Zijlstra 	struct jump_entry *iter_start = __start___jump_table;
58591a1d97eSPeter Zijlstra 	struct jump_entry *iter_stop = __stop___jump_table;
58691a1d97eSPeter Zijlstra 	struct jump_entry *iter;
58791a1d97eSPeter Zijlstra 
58891a1d97eSPeter Zijlstra 	if (WARN_ON_ONCE(!static_key_initialized))
58991a1d97eSPeter Zijlstra 		return;
59091a1d97eSPeter Zijlstra 
59191a1d97eSPeter Zijlstra 	cpus_read_lock();
59291a1d97eSPeter Zijlstra 	jump_label_lock();
59391a1d97eSPeter Zijlstra 
59491a1d97eSPeter Zijlstra 	for (iter = iter_start; iter < iter_stop; iter++) {
59591a1d97eSPeter Zijlstra 		struct static_key *iterk = jump_entry_key(iter);
59691a1d97eSPeter Zijlstra 
59791a1d97eSPeter Zijlstra 		if (!is_kernel_ro_after_init((unsigned long)iterk))
59891a1d97eSPeter Zijlstra 			continue;
59991a1d97eSPeter Zijlstra 
60091a1d97eSPeter Zijlstra 		if (static_key_sealed(iterk))
60191a1d97eSPeter Zijlstra 			continue;
60291a1d97eSPeter Zijlstra 
60391a1d97eSPeter Zijlstra 		static_key_seal(iterk);
60491a1d97eSPeter Zijlstra 	}
60591a1d97eSPeter Zijlstra 
60691a1d97eSPeter Zijlstra 	jump_label_unlock();
60791a1d97eSPeter Zijlstra 	cpus_read_unlock();
60891a1d97eSPeter Zijlstra }
60991a1d97eSPeter Zijlstra 
610d430d3d7SJason Baron #ifdef CONFIG_MODULES
611d430d3d7SJason Baron 
jump_label_init_type(struct jump_entry * entry)612fdfd4289SArd Biesheuvel enum jump_label_type jump_label_init_type(struct jump_entry *entry)
61311276d53SPeter Zijlstra {
61411276d53SPeter Zijlstra 	struct static_key *key = jump_entry_key(entry);
61511276d53SPeter Zijlstra 	bool type = static_key_type(key);
6169ae033acSArd Biesheuvel 	bool branch = jump_entry_is_branch(entry);
61711276d53SPeter Zijlstra 
61811276d53SPeter Zijlstra 	/* See the comment in linux/jump_label.h */
61911276d53SPeter Zijlstra 	return type ^ branch;
62011276d53SPeter Zijlstra }
62111276d53SPeter Zijlstra 
622c5905afbSIngo Molnar struct static_key_mod {
623c5905afbSIngo Molnar 	struct static_key_mod *next;
624d430d3d7SJason Baron 	struct jump_entry *entries;
625d430d3d7SJason Baron 	struct module *mod;
626d430d3d7SJason Baron };
627d430d3d7SJason Baron 
static_key_mod(struct static_key * key)6283821fd35SJason Baron static inline struct static_key_mod *static_key_mod(struct static_key *key)
6293821fd35SJason Baron {
63034e12b86SBorislav Petkov 	WARN_ON_ONCE(!static_key_linked(key));
6313821fd35SJason Baron 	return (struct static_key_mod *)(key->type & ~JUMP_TYPE_MASK);
6323821fd35SJason Baron }
6333821fd35SJason Baron 
6343821fd35SJason Baron /***
6353821fd35SJason Baron  * key->type and key->next are the same via union.
6363821fd35SJason Baron  * This sets key->next and preserves the type bits.
6373821fd35SJason Baron  *
6383821fd35SJason Baron  * See additional comments above static_key_set_entries().
6393821fd35SJason Baron  */
static_key_set_mod(struct static_key * key,struct static_key_mod * mod)6403821fd35SJason Baron static void static_key_set_mod(struct static_key *key,
6413821fd35SJason Baron 			       struct static_key_mod *mod)
6423821fd35SJason Baron {
6433821fd35SJason Baron 	unsigned long type;
6443821fd35SJason Baron 
6453821fd35SJason Baron 	WARN_ON_ONCE((unsigned long)mod & JUMP_TYPE_MASK);
6463821fd35SJason Baron 	type = key->type & JUMP_TYPE_MASK;
6473821fd35SJason Baron 	key->next = mod;
6483821fd35SJason Baron 	key->type |= type;
6493821fd35SJason Baron }
6503821fd35SJason Baron 
__jump_label_mod_text_reserved(void * start,void * end)651d430d3d7SJason Baron static int __jump_label_mod_text_reserved(void *start, void *end)
652d430d3d7SJason Baron {
653d430d3d7SJason Baron 	struct module *mod;
6540db6e373SPeter Zijlstra 	int ret;
655d430d3d7SJason Baron 
656*72ee1c20SSebastian Andrzej Siewior 	scoped_guard(rcu) {
657d430d3d7SJason Baron 		mod = __module_text_address((unsigned long)start);
658bdc9f373SRusty Russell 		WARN_ON_ONCE(__module_text_address((unsigned long)end) != mod);
6590db6e373SPeter Zijlstra 		if (!try_module_get(mod))
6600db6e373SPeter Zijlstra 			mod = NULL;
661*72ee1c20SSebastian Andrzej Siewior 	}
662d430d3d7SJason Baron 	if (!mod)
663d430d3d7SJason Baron 		return 0;
664d430d3d7SJason Baron 
6650db6e373SPeter Zijlstra 	ret = __jump_label_text_reserved(mod->jump_entries,
666d430d3d7SJason Baron 				mod->jump_entries + mod->num_jump_entries,
6679e667624SPeter Zijlstra 				start, end, mod->state == MODULE_STATE_COMING);
6680db6e373SPeter Zijlstra 
6690db6e373SPeter Zijlstra 	module_put(mod);
6700db6e373SPeter Zijlstra 
6710db6e373SPeter Zijlstra 	return ret;
672d430d3d7SJason Baron }
673d430d3d7SJason Baron 
__jump_label_mod_update(struct static_key * key)674706249c2SPeter Zijlstra static void __jump_label_mod_update(struct static_key *key)
675d430d3d7SJason Baron {
676706249c2SPeter Zijlstra 	struct static_key_mod *mod;
677d430d3d7SJason Baron 
6783821fd35SJason Baron 	for (mod = static_key_mod(key); mod; mod = mod->next) {
6793821fd35SJason Baron 		struct jump_entry *stop;
6803821fd35SJason Baron 		struct module *m;
6817cbc5b8dSJiri Olsa 
6823821fd35SJason Baron 		/*
6833821fd35SJason Baron 		 * NULL if the static_key is defined in a module
6843821fd35SJason Baron 		 * that does not use it
6853821fd35SJason Baron 		 */
6863821fd35SJason Baron 		if (!mod->entries)
6873821fd35SJason Baron 			continue;
6883821fd35SJason Baron 
6893821fd35SJason Baron 		m = mod->mod;
6903821fd35SJason Baron 		if (!m)
6913821fd35SJason Baron 			stop = __stop___jump_table;
6923821fd35SJason Baron 		else
6933821fd35SJason Baron 			stop = m->jump_entries + m->num_jump_entries;
69419483677SArd Biesheuvel 		__jump_label_update(key, mod->entries, stop,
69577ac1c02SArd Biesheuvel 				    m && m->state == MODULE_STATE_COMING);
696d430d3d7SJason Baron 	}
697d430d3d7SJason Baron }
698d430d3d7SJason Baron 
jump_label_add_module(struct module * mod)699d430d3d7SJason Baron static int jump_label_add_module(struct module *mod)
700d430d3d7SJason Baron {
701d430d3d7SJason Baron 	struct jump_entry *iter_start = mod->jump_entries;
702d430d3d7SJason Baron 	struct jump_entry *iter_stop = iter_start + mod->num_jump_entries;
703d430d3d7SJason Baron 	struct jump_entry *iter;
704c5905afbSIngo Molnar 	struct static_key *key = NULL;
7053821fd35SJason Baron 	struct static_key_mod *jlm, *jlm2;
706d430d3d7SJason Baron 
707d430d3d7SJason Baron 	/* if the module doesn't have jump label entries, just return */
708d430d3d7SJason Baron 	if (iter_start == iter_stop)
709d430d3d7SJason Baron 		return 0;
710d430d3d7SJason Baron 
711d430d3d7SJason Baron 	jump_label_sort_entries(iter_start, iter_stop);
712d430d3d7SJason Baron 
713d430d3d7SJason Baron 	for (iter = iter_start; iter < iter_stop; iter++) {
714c5905afbSIngo Molnar 		struct static_key *iterk;
7155af0ea29SPeter Zijlstra 		bool in_init;
716c5905afbSIngo Molnar 
7175af0ea29SPeter Zijlstra 		in_init = within_module_init(jump_entry_code(iter), mod);
7185af0ea29SPeter Zijlstra 		jump_entry_set_init(iter, in_init);
71919483677SArd Biesheuvel 
7207dcfd915SPeter Zijlstra 		iterk = jump_entry_key(iter);
721c5905afbSIngo Molnar 		if (iterk == key)
722d430d3d7SJason Baron 			continue;
723d430d3d7SJason Baron 
724c5905afbSIngo Molnar 		key = iterk;
7259ae033acSArd Biesheuvel 		if (within_module((unsigned long)key, mod)) {
7263821fd35SJason Baron 			static_key_set_entries(key, iter);
727d430d3d7SJason Baron 			continue;
728d430d3d7SJason Baron 		}
72991a1d97eSPeter Zijlstra 
73091a1d97eSPeter Zijlstra 		/*
73191a1d97eSPeter Zijlstra 		 * If the key was sealed at init, then there's no need to keep a
73291a1d97eSPeter Zijlstra 		 * reference to its module entries - just patch them now and be
73391a1d97eSPeter Zijlstra 		 * done with it.
73491a1d97eSPeter Zijlstra 		 */
73591a1d97eSPeter Zijlstra 		if (static_key_sealed(key))
73691a1d97eSPeter Zijlstra 			goto do_poke;
73791a1d97eSPeter Zijlstra 
738c5905afbSIngo Molnar 		jlm = kzalloc(sizeof(struct static_key_mod), GFP_KERNEL);
739d430d3d7SJason Baron 		if (!jlm)
740d430d3d7SJason Baron 			return -ENOMEM;
7413821fd35SJason Baron 		if (!static_key_linked(key)) {
7423821fd35SJason Baron 			jlm2 = kzalloc(sizeof(struct static_key_mod),
7433821fd35SJason Baron 				       GFP_KERNEL);
7443821fd35SJason Baron 			if (!jlm2) {
7453821fd35SJason Baron 				kfree(jlm);
7463821fd35SJason Baron 				return -ENOMEM;
7473821fd35SJason Baron 			}
7484038131fSSebastian Andrzej Siewior 			scoped_guard(rcu)
7493821fd35SJason Baron 				jlm2->mod = __module_address((unsigned long)key);
7504038131fSSebastian Andrzej Siewior 
7513821fd35SJason Baron 			jlm2->entries = static_key_entries(key);
7523821fd35SJason Baron 			jlm2->next = NULL;
7533821fd35SJason Baron 			static_key_set_mod(key, jlm2);
7543821fd35SJason Baron 			static_key_set_linked(key);
7553821fd35SJason Baron 		}
756d430d3d7SJason Baron 		jlm->mod = mod;
757d430d3d7SJason Baron 		jlm->entries = iter;
7583821fd35SJason Baron 		jlm->next = static_key_mod(key);
7593821fd35SJason Baron 		static_key_set_mod(key, jlm);
7603821fd35SJason Baron 		static_key_set_linked(key);
761d430d3d7SJason Baron 
76211276d53SPeter Zijlstra 		/* Only update if we've changed from our initial state */
76391a1d97eSPeter Zijlstra do_poke:
76411276d53SPeter Zijlstra 		if (jump_label_type(iter) != jump_label_init_type(iter))
76519483677SArd Biesheuvel 			__jump_label_update(key, iter, iter_stop, true);
766d430d3d7SJason Baron 	}
767d430d3d7SJason Baron 
768d430d3d7SJason Baron 	return 0;
769d430d3d7SJason Baron }
770d430d3d7SJason Baron 
jump_label_del_module(struct module * mod)771d430d3d7SJason Baron static void jump_label_del_module(struct module *mod)
772d430d3d7SJason Baron {
773d430d3d7SJason Baron 	struct jump_entry *iter_start = mod->jump_entries;
774d430d3d7SJason Baron 	struct jump_entry *iter_stop = iter_start + mod->num_jump_entries;
775d430d3d7SJason Baron 	struct jump_entry *iter;
776c5905afbSIngo Molnar 	struct static_key *key = NULL;
777c5905afbSIngo Molnar 	struct static_key_mod *jlm, **prev;
778d430d3d7SJason Baron 
779d430d3d7SJason Baron 	for (iter = iter_start; iter < iter_stop; iter++) {
7807dcfd915SPeter Zijlstra 		if (jump_entry_key(iter) == key)
781d430d3d7SJason Baron 			continue;
782d430d3d7SJason Baron 
7837dcfd915SPeter Zijlstra 		key = jump_entry_key(iter);
784d430d3d7SJason Baron 
7859ae033acSArd Biesheuvel 		if (within_module((unsigned long)key, mod))
786d430d3d7SJason Baron 			continue;
787d430d3d7SJason Baron 
78891a1d97eSPeter Zijlstra 		/* No @jlm allocated because key was sealed at init. */
78991a1d97eSPeter Zijlstra 		if (static_key_sealed(key))
79091a1d97eSPeter Zijlstra 			continue;
79191a1d97eSPeter Zijlstra 
7923821fd35SJason Baron 		/* No memory during module load */
7933821fd35SJason Baron 		if (WARN_ON(!static_key_linked(key)))
7943821fd35SJason Baron 			continue;
7953821fd35SJason Baron 
796d430d3d7SJason Baron 		prev = &key->next;
7973821fd35SJason Baron 		jlm = static_key_mod(key);
798d430d3d7SJason Baron 
799d430d3d7SJason Baron 		while (jlm && jlm->mod != mod) {
800d430d3d7SJason Baron 			prev = &jlm->next;
801d430d3d7SJason Baron 			jlm = jlm->next;
802d430d3d7SJason Baron 		}
803d430d3d7SJason Baron 
8043821fd35SJason Baron 		/* No memory during module load */
8053821fd35SJason Baron 		if (WARN_ON(!jlm))
8063821fd35SJason Baron 			continue;
8073821fd35SJason Baron 
8083821fd35SJason Baron 		if (prev == &key->next)
8093821fd35SJason Baron 			static_key_set_mod(key, jlm->next);
8103821fd35SJason Baron 		else
811d430d3d7SJason Baron 			*prev = jlm->next;
8123821fd35SJason Baron 
8133821fd35SJason Baron 		kfree(jlm);
8143821fd35SJason Baron 
8153821fd35SJason Baron 		jlm = static_key_mod(key);
8163821fd35SJason Baron 		/* if only one etry is left, fold it back into the static_key */
8173821fd35SJason Baron 		if (jlm->next == NULL) {
8183821fd35SJason Baron 			static_key_set_entries(key, jlm->entries);
8193821fd35SJason Baron 			static_key_clear_linked(key);
820d430d3d7SJason Baron 			kfree(jlm);
821d430d3d7SJason Baron 		}
822d430d3d7SJason Baron 	}
823d430d3d7SJason Baron }
824d430d3d7SJason Baron 
825d430d3d7SJason Baron static int
jump_label_module_notify(struct notifier_block * self,unsigned long val,void * data)826d430d3d7SJason Baron jump_label_module_notify(struct notifier_block *self, unsigned long val,
827d430d3d7SJason Baron 			 void *data)
828d430d3d7SJason Baron {
829d430d3d7SJason Baron 	struct module *mod = data;
830d430d3d7SJason Baron 	int ret = 0;
831d430d3d7SJason Baron 
832f2545b2dSThomas Gleixner 	cpus_read_lock();
833f2545b2dSThomas Gleixner 	jump_label_lock();
834f2545b2dSThomas Gleixner 
835d430d3d7SJason Baron 	switch (val) {
836d430d3d7SJason Baron 	case MODULE_STATE_COMING:
837d430d3d7SJason Baron 		ret = jump_label_add_module(mod);
8383821fd35SJason Baron 		if (ret) {
839da260fe1SBorislav Petkov 			WARN(1, "Failed to allocate memory: jump_label may not work properly.\n");
840d430d3d7SJason Baron 			jump_label_del_module(mod);
8413821fd35SJason Baron 		}
842d430d3d7SJason Baron 		break;
843d430d3d7SJason Baron 	case MODULE_STATE_GOING:
844d430d3d7SJason Baron 		jump_label_del_module(mod);
845d430d3d7SJason Baron 		break;
846d430d3d7SJason Baron 	}
847d430d3d7SJason Baron 
848f2545b2dSThomas Gleixner 	jump_label_unlock();
849f2545b2dSThomas Gleixner 	cpus_read_unlock();
850f2545b2dSThomas Gleixner 
851d430d3d7SJason Baron 	return notifier_from_errno(ret);
852d430d3d7SJason Baron }
853d430d3d7SJason Baron 
854885885f6SWei Yongjun static struct notifier_block jump_label_module_nb = {
855d430d3d7SJason Baron 	.notifier_call = jump_label_module_notify,
856d430d3d7SJason Baron 	.priority = 1, /* higher than tracepoints */
857d430d3d7SJason Baron };
858d430d3d7SJason Baron 
jump_label_init_module(void)859d430d3d7SJason Baron static __init int jump_label_init_module(void)
860d430d3d7SJason Baron {
861d430d3d7SJason Baron 	return register_module_notifier(&jump_label_module_nb);
862d430d3d7SJason Baron }
863d430d3d7SJason Baron early_initcall(jump_label_init_module);
864d430d3d7SJason Baron 
865d430d3d7SJason Baron #endif /* CONFIG_MODULES */
8664c3ef6d7SJason Baron 
8674c3ef6d7SJason Baron /***
8684c3ef6d7SJason Baron  * jump_label_text_reserved - check if addr range is reserved
8694c3ef6d7SJason Baron  * @start: start text addr
8704c3ef6d7SJason Baron  * @end: end text addr
8714c3ef6d7SJason Baron  *
8724c3ef6d7SJason Baron  * checks if the text addr located between @start and @end
8734c3ef6d7SJason Baron  * overlaps with any of the jump label patch addresses. Code
8744c3ef6d7SJason Baron  * that wants to modify kernel text should first verify that
8754c3ef6d7SJason Baron  * it does not overlap with any of the jump label addresses.
87691bad2f8SJason Baron  * Caller must hold jump_label_mutex.
8774c3ef6d7SJason Baron  *
8784c3ef6d7SJason Baron  * returns 1 if there is an overlap, 0 otherwise
8794c3ef6d7SJason Baron  */
jump_label_text_reserved(void * start,void * end)8804c3ef6d7SJason Baron int jump_label_text_reserved(void *start, void *end)
8814c3ef6d7SJason Baron {
8829e667624SPeter Zijlstra 	bool init = system_state < SYSTEM_RUNNING;
883d430d3d7SJason Baron 	int ret = __jump_label_text_reserved(__start___jump_table,
8849e667624SPeter Zijlstra 			__stop___jump_table, start, end, init);
8854c3ef6d7SJason Baron 
886bf5438fcSJason Baron 	if (ret)
887d430d3d7SJason Baron 		return ret;
888d430d3d7SJason Baron 
889d430d3d7SJason Baron #ifdef CONFIG_MODULES
890d430d3d7SJason Baron 	ret = __jump_label_mod_text_reserved(start, end);
891d430d3d7SJason Baron #endif
892bf5438fcSJason Baron 	return ret;
893bf5438fcSJason Baron }
894bf5438fcSJason Baron 
jump_label_update(struct static_key * key)895706249c2SPeter Zijlstra static void jump_label_update(struct static_key *key)
896bf5438fcSJason Baron {
897c5905afbSIngo Molnar 	struct jump_entry *stop = __stop___jump_table;
89855d2eba8SPeter Zijlstra 	bool init = system_state < SYSTEM_RUNNING;
8993821fd35SJason Baron 	struct jump_entry *entry;
900d430d3d7SJason Baron #ifdef CONFIG_MODULES
901bed831f9SPeter Zijlstra 	struct module *mod;
902140fe3b1SXiao Guangrong 
9033821fd35SJason Baron 	if (static_key_linked(key)) {
904706249c2SPeter Zijlstra 		__jump_label_mod_update(key);
9053821fd35SJason Baron 		return;
9063821fd35SJason Baron 	}
907140fe3b1SXiao Guangrong 
9084038131fSSebastian Andrzej Siewior 	scoped_guard(rcu) {
909bed831f9SPeter Zijlstra 		mod = __module_address((unsigned long)key);
91055d2eba8SPeter Zijlstra 		if (mod) {
911140fe3b1SXiao Guangrong 			stop = mod->jump_entries + mod->num_jump_entries;
91255d2eba8SPeter Zijlstra 			init = mod->state == MODULE_STATE_COMING;
91355d2eba8SPeter Zijlstra 		}
9144038131fSSebastian Andrzej Siewior 	}
915d430d3d7SJason Baron #endif
9163821fd35SJason Baron 	entry = static_key_entries(key);
917140fe3b1SXiao Guangrong 	/* if there are no users, entry can be NULL */
918140fe3b1SXiao Guangrong 	if (entry)
91955d2eba8SPeter Zijlstra 		__jump_label_update(key, entry, stop, init);
920bf5438fcSJason Baron }
921bf5438fcSJason Baron 
9221987c947SPeter Zijlstra #ifdef CONFIG_STATIC_KEYS_SELFTEST
9231987c947SPeter Zijlstra static DEFINE_STATIC_KEY_TRUE(sk_true);
9241987c947SPeter Zijlstra static DEFINE_STATIC_KEY_FALSE(sk_false);
9251987c947SPeter Zijlstra 
jump_label_test(void)9261987c947SPeter Zijlstra static __init int jump_label_test(void)
9271987c947SPeter Zijlstra {
9281987c947SPeter Zijlstra 	int i;
9291987c947SPeter Zijlstra 
9301987c947SPeter Zijlstra 	for (i = 0; i < 2; i++) {
9311987c947SPeter Zijlstra 		WARN_ON(static_key_enabled(&sk_true.key) != true);
9321987c947SPeter Zijlstra 		WARN_ON(static_key_enabled(&sk_false.key) != false);
9331987c947SPeter Zijlstra 
9341987c947SPeter Zijlstra 		WARN_ON(!static_branch_likely(&sk_true));
9351987c947SPeter Zijlstra 		WARN_ON(!static_branch_unlikely(&sk_true));
9361987c947SPeter Zijlstra 		WARN_ON(static_branch_likely(&sk_false));
9371987c947SPeter Zijlstra 		WARN_ON(static_branch_unlikely(&sk_false));
9381987c947SPeter Zijlstra 
9391987c947SPeter Zijlstra 		static_branch_disable(&sk_true);
9401987c947SPeter Zijlstra 		static_branch_enable(&sk_false);
9411987c947SPeter Zijlstra 
9421987c947SPeter Zijlstra 		WARN_ON(static_key_enabled(&sk_true.key) == true);
9431987c947SPeter Zijlstra 		WARN_ON(static_key_enabled(&sk_false.key) == false);
9441987c947SPeter Zijlstra 
9451987c947SPeter Zijlstra 		WARN_ON(static_branch_likely(&sk_true));
9461987c947SPeter Zijlstra 		WARN_ON(static_branch_unlikely(&sk_true));
9471987c947SPeter Zijlstra 		WARN_ON(!static_branch_likely(&sk_false));
9481987c947SPeter Zijlstra 		WARN_ON(!static_branch_unlikely(&sk_false));
9491987c947SPeter Zijlstra 
9501987c947SPeter Zijlstra 		static_branch_enable(&sk_true);
9511987c947SPeter Zijlstra 		static_branch_disable(&sk_false);
9521987c947SPeter Zijlstra 	}
9531987c947SPeter Zijlstra 
9541987c947SPeter Zijlstra 	return 0;
9551987c947SPeter Zijlstra }
95692ee46efSJason Baron early_initcall(jump_label_test);
9571987c947SPeter Zijlstra #endif /* STATIC_KEYS_SELFTEST */
958