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