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 <jbaron@redhat.com>
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
134fc5cdbe1SThomas Gleixner * static_key_slow_inc/dec() down the slow path, and it is non-zero
135eb8c5072SDmitry Safonov * so it counts as "enabled" in jump_label_update(). Note that
136eb8c5072SDmitry Safonov * atomic_inc_unless_negative() checks >= 0, so roll our own.
137eb8c5072SDmitry Safonov */
138eb8c5072SDmitry Safonov v = atomic_read(&key->enabled);
139eb8c5072SDmitry Safonov do {
140eb8c5072SDmitry Safonov if (v <= 0 || (v + 1) < 0)
141eb8c5072SDmitry Safonov return false;
142eb8c5072SDmitry Safonov } while (!likely(atomic_try_cmpxchg(&key->enabled, &v, v + 1)));
143eb8c5072SDmitry Safonov
144eb8c5072SDmitry Safonov return true;
145eb8c5072SDmitry Safonov }
146eb8c5072SDmitry Safonov EXPORT_SYMBOL_GPL(static_key_fast_inc_not_disabled);
147eb8c5072SDmitry Safonov
static_key_slow_inc_cpuslocked(struct static_key * key)148eb8c5072SDmitry Safonov bool static_key_slow_inc_cpuslocked(struct static_key *key)
149eb8c5072SDmitry Safonov {
150cb538267SPeter Zijlstra lockdep_assert_cpus_held();
1514c5ea0a9SPaolo Bonzini
1524c5ea0a9SPaolo Bonzini /*
153fc5cdbe1SThomas Gleixner * Careful if we get concurrent static_key_slow_inc/dec() calls;
1544c5ea0a9SPaolo Bonzini * later calls must wait for the first one to _finish_ the
1554c5ea0a9SPaolo Bonzini * jump_label_update() process. At the same time, however,
1564c5ea0a9SPaolo Bonzini * the jump_label_update() call below wants to see
1574c5ea0a9SPaolo Bonzini * static_key_enabled(&key) for jumps to be updated properly.
1584c5ea0a9SPaolo Bonzini */
159eb8c5072SDmitry Safonov if (static_key_fast_inc_not_disabled(key))
160eb8c5072SDmitry Safonov return true;
161bf5438fcSJason Baron
16233f3e832SThomas Gleixner guard(mutex)(&jump_label_mutex);
16333f3e832SThomas Gleixner /* Try to mark it as 'enabling in progress. */
16433f3e832SThomas Gleixner if (!atomic_cmpxchg(&key->enabled, 0, -1)) {
165706249c2SPeter Zijlstra jump_label_update(key);
166d0646a6fSPeter Zijlstra /*
16733f3e832SThomas Gleixner * Ensure that when static_key_fast_inc_not_disabled() or
168*86fdd180SPeter Zijlstra * static_key_dec_not_one() observe the positive value,
16933f3e832SThomas Gleixner * they must also observe all the text changes.
170d0646a6fSPeter Zijlstra */
171d0646a6fSPeter Zijlstra atomic_set_release(&key->enabled, 1);
1724c5ea0a9SPaolo Bonzini } else {
17333f3e832SThomas Gleixner /*
17433f3e832SThomas Gleixner * While holding the mutex this should never observe
17533f3e832SThomas Gleixner * anything else than a value >= 1 and succeed
17633f3e832SThomas Gleixner */
17733f3e832SThomas Gleixner if (WARN_ON_ONCE(!static_key_fast_inc_not_disabled(key)))
178eb8c5072SDmitry Safonov return false;
179eb8c5072SDmitry Safonov }
180eb8c5072SDmitry Safonov return true;
1818b7b4128SMarc Zyngier }
1828b7b4128SMarc Zyngier
static_key_slow_inc(struct static_key * key)183eb8c5072SDmitry Safonov bool static_key_slow_inc(struct static_key *key)
1848b7b4128SMarc Zyngier {
185eb8c5072SDmitry Safonov bool ret;
186eb8c5072SDmitry Safonov
1878b7b4128SMarc Zyngier cpus_read_lock();
188eb8c5072SDmitry Safonov ret = static_key_slow_inc_cpuslocked(key);
189f2545b2dSThomas Gleixner cpus_read_unlock();
190eb8c5072SDmitry Safonov return ret;
191bf5438fcSJason Baron }
192c5905afbSIngo Molnar EXPORT_SYMBOL_GPL(static_key_slow_inc);
193d430d3d7SJason Baron
static_key_enable_cpuslocked(struct static_key * key)1945a40527fSMarc Zyngier void static_key_enable_cpuslocked(struct static_key *key)
1951dbb6704SPaolo Bonzini {
1965cdda511SBorislav Petkov STATIC_KEY_CHECK_USE(key);
197cb538267SPeter Zijlstra lockdep_assert_cpus_held();
1985a40527fSMarc Zyngier
1991dbb6704SPaolo Bonzini if (atomic_read(&key->enabled) > 0) {
2001dbb6704SPaolo Bonzini WARN_ON_ONCE(atomic_read(&key->enabled) != 1);
2011dbb6704SPaolo Bonzini return;
2021dbb6704SPaolo Bonzini }
2031dbb6704SPaolo Bonzini
2041dbb6704SPaolo Bonzini jump_label_lock();
2051dbb6704SPaolo Bonzini if (atomic_read(&key->enabled) == 0) {
2061dbb6704SPaolo Bonzini atomic_set(&key->enabled, -1);
2071dbb6704SPaolo Bonzini jump_label_update(key);
208d0646a6fSPeter Zijlstra /*
209d0646a6fSPeter Zijlstra * See static_key_slow_inc().
210d0646a6fSPeter Zijlstra */
211d0646a6fSPeter Zijlstra atomic_set_release(&key->enabled, 1);
2121dbb6704SPaolo Bonzini }
2131dbb6704SPaolo Bonzini jump_label_unlock();
2145a40527fSMarc Zyngier }
2155a40527fSMarc Zyngier EXPORT_SYMBOL_GPL(static_key_enable_cpuslocked);
2165a40527fSMarc Zyngier
static_key_enable(struct static_key * key)2175a40527fSMarc Zyngier void static_key_enable(struct static_key *key)
2185a40527fSMarc Zyngier {
2195a40527fSMarc Zyngier cpus_read_lock();
2205a40527fSMarc Zyngier static_key_enable_cpuslocked(key);
2211dbb6704SPaolo Bonzini cpus_read_unlock();
2221dbb6704SPaolo Bonzini }
2231dbb6704SPaolo Bonzini EXPORT_SYMBOL_GPL(static_key_enable);
2241dbb6704SPaolo Bonzini
static_key_disable_cpuslocked(struct static_key * key)2255a40527fSMarc Zyngier void static_key_disable_cpuslocked(struct static_key *key)
2261dbb6704SPaolo Bonzini {
2275cdda511SBorislav Petkov STATIC_KEY_CHECK_USE(key);
228cb538267SPeter Zijlstra lockdep_assert_cpus_held();
2295a40527fSMarc Zyngier
2301dbb6704SPaolo Bonzini if (atomic_read(&key->enabled) != 1) {
2311dbb6704SPaolo Bonzini WARN_ON_ONCE(atomic_read(&key->enabled) != 0);
2321dbb6704SPaolo Bonzini return;
2331dbb6704SPaolo Bonzini }
2341dbb6704SPaolo Bonzini
2351dbb6704SPaolo Bonzini jump_label_lock();
236c7b46f69SPeter Zijlstra if (atomic_cmpxchg(&key->enabled, 1, 0) == 1)
2371dbb6704SPaolo Bonzini jump_label_update(key);
2381dbb6704SPaolo Bonzini jump_label_unlock();
2395a40527fSMarc Zyngier }
2405a40527fSMarc Zyngier EXPORT_SYMBOL_GPL(static_key_disable_cpuslocked);
2415a40527fSMarc Zyngier
static_key_disable(struct static_key * key)2425a40527fSMarc Zyngier void static_key_disable(struct static_key *key)
2435a40527fSMarc Zyngier {
2445a40527fSMarc Zyngier cpus_read_lock();
2455a40527fSMarc Zyngier static_key_disable_cpuslocked(key);
2461dbb6704SPaolo Bonzini cpus_read_unlock();
2471dbb6704SPaolo Bonzini }
2481dbb6704SPaolo Bonzini EXPORT_SYMBOL_GPL(static_key_disable);
2491dbb6704SPaolo Bonzini
static_key_dec_not_one(struct static_key * key)250*86fdd180SPeter Zijlstra static bool static_key_dec_not_one(struct static_key *key)
251d430d3d7SJason Baron {
252fc5cdbe1SThomas Gleixner int v;
253cb538267SPeter Zijlstra
2544c5ea0a9SPaolo Bonzini /*
255fc5cdbe1SThomas Gleixner * Go into the slow path if key::enabled is less than or equal than
256fc5cdbe1SThomas Gleixner * one. One is valid to shut down the key, anything less than one
257fc5cdbe1SThomas Gleixner * is an imbalance, which is handled at the call site.
258fc5cdbe1SThomas Gleixner *
259fc5cdbe1SThomas Gleixner * That includes the special case of '-1' which is set in
260fc5cdbe1SThomas Gleixner * static_key_slow_inc_cpuslocked(), but that's harmless as it is
261fc5cdbe1SThomas Gleixner * fully serialized in the slow path below. By the time this task
262fc5cdbe1SThomas Gleixner * acquires the jump label lock the value is back to one and the
263fc5cdbe1SThomas Gleixner * retry under the lock must succeed.
2644c5ea0a9SPaolo Bonzini */
265fc5cdbe1SThomas Gleixner v = atomic_read(&key->enabled);
266fc5cdbe1SThomas Gleixner do {
267fc5cdbe1SThomas Gleixner /*
268fc5cdbe1SThomas Gleixner * Warn about the '-1' case though; since that means a
269fc5cdbe1SThomas Gleixner * decrement is concurrent with a first (0->1) increment. IOW
270fc5cdbe1SThomas Gleixner * people are trying to disable something that wasn't yet fully
271fc5cdbe1SThomas Gleixner * enabled. This suggests an ordering problem on the user side.
272fc5cdbe1SThomas Gleixner */
273fc5cdbe1SThomas Gleixner WARN_ON_ONCE(v < 0);
274*86fdd180SPeter Zijlstra
275*86fdd180SPeter Zijlstra /*
276*86fdd180SPeter Zijlstra * Warn about underflow, and lie about success in an attempt to
277*86fdd180SPeter Zijlstra * not make things worse.
278*86fdd180SPeter Zijlstra */
279*86fdd180SPeter Zijlstra if (WARN_ON_ONCE(v == 0))
280*86fdd180SPeter Zijlstra return true;
281*86fdd180SPeter Zijlstra
282fc5cdbe1SThomas Gleixner if (v <= 1)
283fc5cdbe1SThomas Gleixner return false;
284fc5cdbe1SThomas Gleixner } while (!likely(atomic_try_cmpxchg(&key->enabled, &v, v - 1)));
285fc5cdbe1SThomas Gleixner
286b92e793bSJakub Kicinski return true;
287fadf0464SJason Baron }
288d430d3d7SJason Baron
__static_key_slow_dec_cpuslocked(struct static_key * key)28994b5f312SJakub Kicinski static void __static_key_slow_dec_cpuslocked(struct static_key *key)
290b92e793bSJakub Kicinski {
291b92e793bSJakub Kicinski lockdep_assert_cpus_held();
292*86fdd180SPeter Zijlstra int val;
293b92e793bSJakub Kicinski
294*86fdd180SPeter Zijlstra if (static_key_dec_not_one(key))
295b92e793bSJakub Kicinski return;
296b92e793bSJakub Kicinski
297fc5cdbe1SThomas Gleixner guard(mutex)(&jump_label_mutex);
298*86fdd180SPeter Zijlstra val = atomic_read(&key->enabled);
299*86fdd180SPeter Zijlstra /*
300*86fdd180SPeter Zijlstra * It should be impossible to observe -1 with jump_label_mutex held,
301*86fdd180SPeter Zijlstra * see static_key_slow_inc_cpuslocked().
302*86fdd180SPeter Zijlstra */
303*86fdd180SPeter Zijlstra if (WARN_ON_ONCE(val == -1))
304*86fdd180SPeter Zijlstra return;
305*86fdd180SPeter Zijlstra /*
306*86fdd180SPeter Zijlstra * Cannot already be 0, something went sideways.
307*86fdd180SPeter Zijlstra */
308*86fdd180SPeter Zijlstra if (WARN_ON_ONCE(val == 0))
309*86fdd180SPeter Zijlstra return;
310*86fdd180SPeter Zijlstra
311*86fdd180SPeter Zijlstra if (atomic_dec_and_test(&key->enabled))
312706249c2SPeter Zijlstra jump_label_update(key);
3138b7b4128SMarc Zyngier }
3148b7b4128SMarc Zyngier
__static_key_slow_dec(struct static_key * key)31594b5f312SJakub Kicinski static void __static_key_slow_dec(struct static_key *key)
3168b7b4128SMarc Zyngier {
3178b7b4128SMarc Zyngier cpus_read_lock();
31894b5f312SJakub Kicinski __static_key_slow_dec_cpuslocked(key);
319f2545b2dSThomas Gleixner cpus_read_unlock();
320bf5438fcSJason Baron }
321bf5438fcSJason Baron
jump_label_update_timeout(struct work_struct * work)322ad282a81SJakub Kicinski void jump_label_update_timeout(struct work_struct *work)
323b2029520SGleb Natapov {
324c5905afbSIngo Molnar struct static_key_deferred *key =
325c5905afbSIngo Molnar container_of(work, struct static_key_deferred, work.work);
32694b5f312SJakub Kicinski __static_key_slow_dec(&key->key);
327b2029520SGleb Natapov }
328ad282a81SJakub Kicinski EXPORT_SYMBOL_GPL(jump_label_update_timeout);
329b2029520SGleb Natapov
static_key_slow_dec(struct static_key * key)330c5905afbSIngo Molnar void static_key_slow_dec(struct static_key *key)
331b2029520SGleb Natapov {
3325cdda511SBorislav Petkov STATIC_KEY_CHECK_USE(key);
33394b5f312SJakub Kicinski __static_key_slow_dec(key);
334b2029520SGleb Natapov }
335c5905afbSIngo Molnar EXPORT_SYMBOL_GPL(static_key_slow_dec);
336b2029520SGleb Natapov
static_key_slow_dec_cpuslocked(struct static_key * key)337ce48c146SPeter Zijlstra void static_key_slow_dec_cpuslocked(struct static_key *key)
338ce48c146SPeter Zijlstra {
339ce48c146SPeter Zijlstra STATIC_KEY_CHECK_USE(key);
34094b5f312SJakub Kicinski __static_key_slow_dec_cpuslocked(key);
341ce48c146SPeter Zijlstra }
342ce48c146SPeter Zijlstra
__static_key_slow_dec_deferred(struct static_key * key,struct delayed_work * work,unsigned long timeout)343ad282a81SJakub Kicinski void __static_key_slow_dec_deferred(struct static_key *key,
344ad282a81SJakub Kicinski struct delayed_work *work,
345ad282a81SJakub Kicinski unsigned long timeout)
346b2029520SGleb Natapov {
3475cdda511SBorislav Petkov STATIC_KEY_CHECK_USE(key);
34894b5f312SJakub Kicinski
349*86fdd180SPeter Zijlstra if (static_key_dec_not_one(key))
35094b5f312SJakub Kicinski return;
35194b5f312SJakub Kicinski
35294b5f312SJakub Kicinski schedule_delayed_work(work, timeout);
353b2029520SGleb Natapov }
354ad282a81SJakub Kicinski EXPORT_SYMBOL_GPL(__static_key_slow_dec_deferred);
355b2029520SGleb Natapov
__static_key_deferred_flush(void * key,struct delayed_work * work)356ad282a81SJakub Kicinski void __static_key_deferred_flush(void *key, struct delayed_work *work)
357b6416e61SDavid Matlack {
3585cdda511SBorislav Petkov STATIC_KEY_CHECK_USE(key);
359ad282a81SJakub Kicinski flush_delayed_work(work);
360b6416e61SDavid Matlack }
361ad282a81SJakub Kicinski EXPORT_SYMBOL_GPL(__static_key_deferred_flush);
362b6416e61SDavid Matlack
jump_label_rate_limit(struct static_key_deferred * key,unsigned long rl)363c5905afbSIngo Molnar void jump_label_rate_limit(struct static_key_deferred *key,
364b2029520SGleb Natapov unsigned long rl)
365b2029520SGleb Natapov {
3665cdda511SBorislav Petkov STATIC_KEY_CHECK_USE(key);
367b2029520SGleb Natapov key->timeout = rl;
368b2029520SGleb Natapov INIT_DELAYED_WORK(&key->work, jump_label_update_timeout);
369b2029520SGleb Natapov }
370a181dc14SGleb Natapov EXPORT_SYMBOL_GPL(jump_label_rate_limit);
371b2029520SGleb Natapov
addr_conflict(struct jump_entry * entry,void * start,void * end)3724c3ef6d7SJason Baron static int addr_conflict(struct jump_entry *entry, void *start, void *end)
3734c3ef6d7SJason Baron {
3749ae033acSArd Biesheuvel if (jump_entry_code(entry) <= (unsigned long)end &&
375fa5e5dc3SPeter Zijlstra jump_entry_code(entry) + jump_entry_size(entry) > (unsigned long)start)
3764c3ef6d7SJason Baron return 1;
3774c3ef6d7SJason Baron
3784c3ef6d7SJason Baron return 0;
3794c3ef6d7SJason Baron }
3804c3ef6d7SJason Baron
__jump_label_text_reserved(struct jump_entry * iter_start,struct jump_entry * iter_stop,void * start,void * end,bool init)381d430d3d7SJason Baron static int __jump_label_text_reserved(struct jump_entry *iter_start,
3829e667624SPeter Zijlstra struct jump_entry *iter_stop, void *start, void *end, bool init)
3834c3ef6d7SJason Baron {
3844c3ef6d7SJason Baron struct jump_entry *iter;
3854c3ef6d7SJason Baron
386d430d3d7SJason Baron iter = iter_start;
387d430d3d7SJason Baron while (iter < iter_stop) {
3889e667624SPeter Zijlstra if (init || !jump_entry_is_init(iter)) {
389d430d3d7SJason Baron if (addr_conflict(iter, start, end))
390d430d3d7SJason Baron return 1;
3919e667624SPeter Zijlstra }
3924c3ef6d7SJason Baron iter++;
3934c3ef6d7SJason Baron }
394d430d3d7SJason Baron
395d430d3d7SJason Baron return 0;
3964c3ef6d7SJason Baron }
3974c3ef6d7SJason Baron
3987e6b9db2SArd Biesheuvel #ifndef arch_jump_label_transform_static
arch_jump_label_transform_static(struct jump_entry * entry,enum jump_label_type type)3997e6b9db2SArd Biesheuvel static void arch_jump_label_transform_static(struct jump_entry *entry,
40020284aa7SJeremy Fitzhardinge enum jump_label_type type)
40120284aa7SJeremy Fitzhardinge {
4027e6b9db2SArd Biesheuvel /* nothing to do on most architectures */
40320284aa7SJeremy Fitzhardinge }
4047e6b9db2SArd Biesheuvel #endif
40520284aa7SJeremy Fitzhardinge
static_key_entries(struct static_key * key)406a1efb01fSPeter Zijlstra static inline struct jump_entry *static_key_entries(struct static_key *key)
407a1efb01fSPeter Zijlstra {
4083821fd35SJason Baron WARN_ON_ONCE(key->type & JUMP_TYPE_LINKED);
4093821fd35SJason Baron return (struct jump_entry *)(key->type & ~JUMP_TYPE_MASK);
410a1efb01fSPeter Zijlstra }
411a1efb01fSPeter Zijlstra
static_key_type(struct static_key * key)412706249c2SPeter Zijlstra static inline bool static_key_type(struct static_key *key)
413706249c2SPeter Zijlstra {
4143821fd35SJason Baron return key->type & JUMP_TYPE_TRUE;
4153821fd35SJason Baron }
4163821fd35SJason Baron
static_key_linked(struct static_key * key)4173821fd35SJason Baron static inline bool static_key_linked(struct static_key *key)
4183821fd35SJason Baron {
4193821fd35SJason Baron return key->type & JUMP_TYPE_LINKED;
4203821fd35SJason Baron }
4213821fd35SJason Baron
static_key_clear_linked(struct static_key * key)4223821fd35SJason Baron static inline void static_key_clear_linked(struct static_key *key)
4233821fd35SJason Baron {
4243821fd35SJason Baron key->type &= ~JUMP_TYPE_LINKED;
4253821fd35SJason Baron }
4263821fd35SJason Baron
static_key_set_linked(struct static_key * key)4273821fd35SJason Baron static inline void static_key_set_linked(struct static_key *key)
4283821fd35SJason Baron {
4293821fd35SJason Baron key->type |= JUMP_TYPE_LINKED;
430706249c2SPeter Zijlstra }
431706249c2SPeter Zijlstra
4323821fd35SJason Baron /***
4333821fd35SJason Baron * A 'struct static_key' uses a union such that it either points directly
4343821fd35SJason Baron * to a table of 'struct jump_entry' or to a linked list of modules which in
4353821fd35SJason Baron * turn point to 'struct jump_entry' tables.
4363821fd35SJason Baron *
4373821fd35SJason Baron * The two lower bits of the pointer are used to keep track of which pointer
4383821fd35SJason Baron * type is in use and to store the initial branch direction, we use an access
4393821fd35SJason Baron * function which preserves these bits.
4403821fd35SJason Baron */
static_key_set_entries(struct static_key * key,struct jump_entry * entries)4413821fd35SJason Baron static void static_key_set_entries(struct static_key *key,
4423821fd35SJason Baron struct jump_entry *entries)
4433821fd35SJason Baron {
4443821fd35SJason Baron unsigned long type;
4453821fd35SJason Baron
4463821fd35SJason Baron WARN_ON_ONCE((unsigned long)entries & JUMP_TYPE_MASK);
4473821fd35SJason Baron type = key->type & JUMP_TYPE_MASK;
4483821fd35SJason Baron key->entries = entries;
4493821fd35SJason Baron key->type |= type;
4503821fd35SJason Baron }
4513821fd35SJason Baron
jump_label_type(struct jump_entry * entry)452706249c2SPeter Zijlstra static enum jump_label_type jump_label_type(struct jump_entry *entry)
453c5905afbSIngo Molnar {
454706249c2SPeter Zijlstra struct static_key *key = jump_entry_key(entry);
455a1efb01fSPeter Zijlstra bool enabled = static_key_enabled(key);
4569ae033acSArd Biesheuvel bool branch = jump_entry_is_branch(entry);
457c5905afbSIngo Molnar
45811276d53SPeter Zijlstra /* See the comment in linux/jump_label.h */
45911276d53SPeter Zijlstra return enabled ^ branch;
460c5905afbSIngo Molnar }
461c5905afbSIngo Molnar
jump_label_can_update(struct jump_entry * entry,bool init)462e1aacb3fSDaniel Bristot de Oliveira static bool jump_label_can_update(struct jump_entry *entry, bool init)
463e1aacb3fSDaniel Bristot de Oliveira {
464e1aacb3fSDaniel Bristot de Oliveira /*
465e1aacb3fSDaniel Bristot de Oliveira * Cannot update code that was in an init text area.
466e1aacb3fSDaniel Bristot de Oliveira */
467e1aacb3fSDaniel Bristot de Oliveira if (!init && jump_entry_is_init(entry))
468e1aacb3fSDaniel Bristot de Oliveira return false;
469e1aacb3fSDaniel Bristot de Oliveira
470e1aacb3fSDaniel Bristot de Oliveira if (!kernel_text_address(jump_entry_code(entry))) {
47138c93587SPeter Zijlstra /*
47238c93587SPeter Zijlstra * This skips patching built-in __exit, which
47338c93587SPeter Zijlstra * is part of init_section_contains() but is
47438c93587SPeter Zijlstra * not part of kernel_text_address().
47538c93587SPeter Zijlstra *
47638c93587SPeter Zijlstra * Skipping built-in __exit is fine since it
47738c93587SPeter Zijlstra * will never be executed.
47838c93587SPeter Zijlstra */
4798f35eaa5SAndrew Murray WARN_ONCE(!jump_entry_is_init(entry),
4808f35eaa5SAndrew Murray "can't patch jump_label at %pS",
4818f35eaa5SAndrew Murray (void *)jump_entry_code(entry));
482e1aacb3fSDaniel Bristot de Oliveira return false;
483e1aacb3fSDaniel Bristot de Oliveira }
484e1aacb3fSDaniel Bristot de Oliveira
485e1aacb3fSDaniel Bristot de Oliveira return true;
486e1aacb3fSDaniel Bristot de Oliveira }
487e1aacb3fSDaniel Bristot de Oliveira
488c2ba8a15SDaniel 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)489706249c2SPeter Zijlstra static void __jump_label_update(struct static_key *key,
490706249c2SPeter Zijlstra struct jump_entry *entry,
49119483677SArd Biesheuvel struct jump_entry *stop,
49219483677SArd Biesheuvel bool init)
493706249c2SPeter Zijlstra {
494706249c2SPeter Zijlstra for (; (entry < stop) && (jump_entry_key(entry) == key); entry++) {
495e1aacb3fSDaniel Bristot de Oliveira if (jump_label_can_update(entry, init))
496706249c2SPeter Zijlstra arch_jump_label_transform(entry, jump_label_type(entry));
497706249c2SPeter Zijlstra }
498706249c2SPeter Zijlstra }
499c2ba8a15SDaniel Bristot de Oliveira #else
__jump_label_update(struct static_key * key,struct jump_entry * entry,struct jump_entry * stop,bool init)500c2ba8a15SDaniel Bristot de Oliveira static void __jump_label_update(struct static_key *key,
501c2ba8a15SDaniel Bristot de Oliveira struct jump_entry *entry,
502c2ba8a15SDaniel Bristot de Oliveira struct jump_entry *stop,
503c2ba8a15SDaniel Bristot de Oliveira bool init)
504c2ba8a15SDaniel Bristot de Oliveira {
505c2ba8a15SDaniel Bristot de Oliveira for (; (entry < stop) && (jump_entry_key(entry) == key); entry++) {
506c2ba8a15SDaniel Bristot de Oliveira
507c2ba8a15SDaniel Bristot de Oliveira if (!jump_label_can_update(entry, init))
508c2ba8a15SDaniel Bristot de Oliveira continue;
509c2ba8a15SDaniel Bristot de Oliveira
510c2ba8a15SDaniel Bristot de Oliveira if (!arch_jump_label_transform_queue(entry, jump_label_type(entry))) {
511c2ba8a15SDaniel Bristot de Oliveira /*
512c2ba8a15SDaniel Bristot de Oliveira * Queue is full: Apply the current queue and try again.
513c2ba8a15SDaniel Bristot de Oliveira */
514c2ba8a15SDaniel Bristot de Oliveira arch_jump_label_transform_apply();
515c2ba8a15SDaniel Bristot de Oliveira BUG_ON(!arch_jump_label_transform_queue(entry, jump_label_type(entry)));
516c2ba8a15SDaniel Bristot de Oliveira }
517c2ba8a15SDaniel Bristot de Oliveira }
518c2ba8a15SDaniel Bristot de Oliveira arch_jump_label_transform_apply();
519c2ba8a15SDaniel Bristot de Oliveira }
520c2ba8a15SDaniel Bristot de Oliveira #endif
521706249c2SPeter Zijlstra
jump_label_init(void)52297ce2c88SJeremy Fitzhardinge void __init jump_label_init(void)
523d430d3d7SJason Baron {
524d430d3d7SJason Baron struct jump_entry *iter_start = __start___jump_table;
525d430d3d7SJason Baron struct jump_entry *iter_stop = __stop___jump_table;
526c5905afbSIngo Molnar struct static_key *key = NULL;
527d430d3d7SJason Baron struct jump_entry *iter;
528d430d3d7SJason Baron
5291f69bf9cSJason Baron /*
5301f69bf9cSJason Baron * Since we are initializing the static_key.enabled field with
5311f69bf9cSJason Baron * with the 'raw' int values (to avoid pulling in atomic.h) in
5321f69bf9cSJason Baron * jump_label.h, let's make sure that is safe. There are only two
5331f69bf9cSJason Baron * cases to check since we initialize to 0 or 1.
5341f69bf9cSJason Baron */
5351f69bf9cSJason Baron BUILD_BUG_ON((int)ATOMIC_INIT(0) != 0);
5361f69bf9cSJason Baron BUILD_BUG_ON((int)ATOMIC_INIT(1) != 1);
5371f69bf9cSJason Baron
538e3f91083SKevin Hao if (static_key_initialized)
539e3f91083SKevin Hao return;
540e3f91083SKevin Hao
541f2545b2dSThomas Gleixner cpus_read_lock();
542d430d3d7SJason Baron jump_label_lock();
543d430d3d7SJason Baron jump_label_sort_entries(iter_start, iter_stop);
544d430d3d7SJason Baron
545d430d3d7SJason Baron for (iter = iter_start; iter < iter_stop; iter++) {
546c5905afbSIngo Molnar struct static_key *iterk;
5475af0ea29SPeter Zijlstra bool in_init;
54837348804SJeremy Fitzhardinge
54911276d53SPeter Zijlstra /* rewrite NOPs */
55011276d53SPeter Zijlstra if (jump_label_type(iter) == JUMP_LABEL_NOP)
55111276d53SPeter Zijlstra arch_jump_label_transform_static(iter, JUMP_LABEL_NOP);
55211276d53SPeter Zijlstra
5535af0ea29SPeter Zijlstra in_init = init_section_contains((void *)jump_entry_code(iter), 1);
5545af0ea29SPeter Zijlstra jump_entry_set_init(iter, in_init);
55519483677SArd Biesheuvel
5567dcfd915SPeter Zijlstra iterk = jump_entry_key(iter);
55737348804SJeremy Fitzhardinge if (iterk == key)
558d430d3d7SJason Baron continue;
559d430d3d7SJason Baron
56037348804SJeremy Fitzhardinge key = iterk;
5613821fd35SJason Baron static_key_set_entries(key, iter);
562d430d3d7SJason Baron }
563c4b2c0c5SHannes Frederic Sowa static_key_initialized = true;
564d430d3d7SJason Baron jump_label_unlock();
565f2545b2dSThomas Gleixner cpus_read_unlock();
566d430d3d7SJason Baron }
567d430d3d7SJason Baron
568d430d3d7SJason Baron #ifdef CONFIG_MODULES
569d430d3d7SJason Baron
jump_label_init_type(struct jump_entry * entry)570fdfd4289SArd Biesheuvel enum jump_label_type jump_label_init_type(struct jump_entry *entry)
57111276d53SPeter Zijlstra {
57211276d53SPeter Zijlstra struct static_key *key = jump_entry_key(entry);
57311276d53SPeter Zijlstra bool type = static_key_type(key);
5749ae033acSArd Biesheuvel bool branch = jump_entry_is_branch(entry);
57511276d53SPeter Zijlstra
57611276d53SPeter Zijlstra /* See the comment in linux/jump_label.h */
57711276d53SPeter Zijlstra return type ^ branch;
57811276d53SPeter Zijlstra }
57911276d53SPeter Zijlstra
580c5905afbSIngo Molnar struct static_key_mod {
581c5905afbSIngo Molnar struct static_key_mod *next;
582d430d3d7SJason Baron struct jump_entry *entries;
583d430d3d7SJason Baron struct module *mod;
584d430d3d7SJason Baron };
585d430d3d7SJason Baron
static_key_mod(struct static_key * key)5863821fd35SJason Baron static inline struct static_key_mod *static_key_mod(struct static_key *key)
5873821fd35SJason Baron {
58834e12b86SBorislav Petkov WARN_ON_ONCE(!static_key_linked(key));
5893821fd35SJason Baron return (struct static_key_mod *)(key->type & ~JUMP_TYPE_MASK);
5903821fd35SJason Baron }
5913821fd35SJason Baron
5923821fd35SJason Baron /***
5933821fd35SJason Baron * key->type and key->next are the same via union.
5943821fd35SJason Baron * This sets key->next and preserves the type bits.
5953821fd35SJason Baron *
5963821fd35SJason Baron * See additional comments above static_key_set_entries().
5973821fd35SJason Baron */
static_key_set_mod(struct static_key * key,struct static_key_mod * mod)5983821fd35SJason Baron static void static_key_set_mod(struct static_key *key,
5993821fd35SJason Baron struct static_key_mod *mod)
6003821fd35SJason Baron {
6013821fd35SJason Baron unsigned long type;
6023821fd35SJason Baron
6033821fd35SJason Baron WARN_ON_ONCE((unsigned long)mod & JUMP_TYPE_MASK);
6043821fd35SJason Baron type = key->type & JUMP_TYPE_MASK;
6053821fd35SJason Baron key->next = mod;
6063821fd35SJason Baron key->type |= type;
6073821fd35SJason Baron }
6083821fd35SJason Baron
__jump_label_mod_text_reserved(void * start,void * end)609d430d3d7SJason Baron static int __jump_label_mod_text_reserved(void *start, void *end)
610d430d3d7SJason Baron {
611d430d3d7SJason Baron struct module *mod;
6120db6e373SPeter Zijlstra int ret;
613d430d3d7SJason Baron
614bdc9f373SRusty Russell preempt_disable();
615d430d3d7SJason Baron mod = __module_text_address((unsigned long)start);
616bdc9f373SRusty Russell WARN_ON_ONCE(__module_text_address((unsigned long)end) != mod);
6170db6e373SPeter Zijlstra if (!try_module_get(mod))
6180db6e373SPeter Zijlstra mod = NULL;
619bdc9f373SRusty Russell preempt_enable();
620bdc9f373SRusty Russell
621d430d3d7SJason Baron if (!mod)
622d430d3d7SJason Baron return 0;
623d430d3d7SJason Baron
6240db6e373SPeter Zijlstra ret = __jump_label_text_reserved(mod->jump_entries,
625d430d3d7SJason Baron mod->jump_entries + mod->num_jump_entries,
6269e667624SPeter Zijlstra start, end, mod->state == MODULE_STATE_COMING);
6270db6e373SPeter Zijlstra
6280db6e373SPeter Zijlstra module_put(mod);
6290db6e373SPeter Zijlstra
6300db6e373SPeter Zijlstra return ret;
631d430d3d7SJason Baron }
632d430d3d7SJason Baron
__jump_label_mod_update(struct static_key * key)633706249c2SPeter Zijlstra static void __jump_label_mod_update(struct static_key *key)
634d430d3d7SJason Baron {
635706249c2SPeter Zijlstra struct static_key_mod *mod;
636d430d3d7SJason Baron
6373821fd35SJason Baron for (mod = static_key_mod(key); mod; mod = mod->next) {
6383821fd35SJason Baron struct jump_entry *stop;
6393821fd35SJason Baron struct module *m;
6407cbc5b8dSJiri Olsa
6413821fd35SJason Baron /*
6423821fd35SJason Baron * NULL if the static_key is defined in a module
6433821fd35SJason Baron * that does not use it
6443821fd35SJason Baron */
6453821fd35SJason Baron if (!mod->entries)
6463821fd35SJason Baron continue;
6473821fd35SJason Baron
6483821fd35SJason Baron m = mod->mod;
6493821fd35SJason Baron if (!m)
6503821fd35SJason Baron stop = __stop___jump_table;
6513821fd35SJason Baron else
6523821fd35SJason Baron stop = m->jump_entries + m->num_jump_entries;
65319483677SArd Biesheuvel __jump_label_update(key, mod->entries, stop,
65477ac1c02SArd Biesheuvel m && m->state == MODULE_STATE_COMING);
655d430d3d7SJason Baron }
656d430d3d7SJason Baron }
657d430d3d7SJason Baron
jump_label_add_module(struct module * mod)658d430d3d7SJason Baron static int jump_label_add_module(struct module *mod)
659d430d3d7SJason Baron {
660d430d3d7SJason Baron struct jump_entry *iter_start = mod->jump_entries;
661d430d3d7SJason Baron struct jump_entry *iter_stop = iter_start + mod->num_jump_entries;
662d430d3d7SJason Baron struct jump_entry *iter;
663c5905afbSIngo Molnar struct static_key *key = NULL;
6643821fd35SJason Baron struct static_key_mod *jlm, *jlm2;
665d430d3d7SJason Baron
666d430d3d7SJason Baron /* if the module doesn't have jump label entries, just return */
667d430d3d7SJason Baron if (iter_start == iter_stop)
668d430d3d7SJason Baron return 0;
669d430d3d7SJason Baron
670d430d3d7SJason Baron jump_label_sort_entries(iter_start, iter_stop);
671d430d3d7SJason Baron
672d430d3d7SJason Baron for (iter = iter_start; iter < iter_stop; iter++) {
673c5905afbSIngo Molnar struct static_key *iterk;
6745af0ea29SPeter Zijlstra bool in_init;
675c5905afbSIngo Molnar
6765af0ea29SPeter Zijlstra in_init = within_module_init(jump_entry_code(iter), mod);
6775af0ea29SPeter Zijlstra jump_entry_set_init(iter, in_init);
67819483677SArd Biesheuvel
6797dcfd915SPeter Zijlstra iterk = jump_entry_key(iter);
680c5905afbSIngo Molnar if (iterk == key)
681d430d3d7SJason Baron continue;
682d430d3d7SJason Baron
683c5905afbSIngo Molnar key = iterk;
6849ae033acSArd Biesheuvel if (within_module((unsigned long)key, mod)) {
6853821fd35SJason Baron static_key_set_entries(key, iter);
686d430d3d7SJason Baron continue;
687d430d3d7SJason Baron }
688c5905afbSIngo Molnar jlm = kzalloc(sizeof(struct static_key_mod), GFP_KERNEL);
689d430d3d7SJason Baron if (!jlm)
690d430d3d7SJason Baron return -ENOMEM;
6913821fd35SJason Baron if (!static_key_linked(key)) {
6923821fd35SJason Baron jlm2 = kzalloc(sizeof(struct static_key_mod),
6933821fd35SJason Baron GFP_KERNEL);
6943821fd35SJason Baron if (!jlm2) {
6953821fd35SJason Baron kfree(jlm);
6963821fd35SJason Baron return -ENOMEM;
6973821fd35SJason Baron }
6983821fd35SJason Baron preempt_disable();
6993821fd35SJason Baron jlm2->mod = __module_address((unsigned long)key);
7003821fd35SJason Baron preempt_enable();
7013821fd35SJason Baron jlm2->entries = static_key_entries(key);
7023821fd35SJason Baron jlm2->next = NULL;
7033821fd35SJason Baron static_key_set_mod(key, jlm2);
7043821fd35SJason Baron static_key_set_linked(key);
7053821fd35SJason Baron }
706d430d3d7SJason Baron jlm->mod = mod;
707d430d3d7SJason Baron jlm->entries = iter;
7083821fd35SJason Baron jlm->next = static_key_mod(key);
7093821fd35SJason Baron static_key_set_mod(key, jlm);
7103821fd35SJason Baron static_key_set_linked(key);
711d430d3d7SJason Baron
71211276d53SPeter Zijlstra /* Only update if we've changed from our initial state */
71311276d53SPeter Zijlstra if (jump_label_type(iter) != jump_label_init_type(iter))
71419483677SArd Biesheuvel __jump_label_update(key, iter, iter_stop, true);
715d430d3d7SJason Baron }
716d430d3d7SJason Baron
717d430d3d7SJason Baron return 0;
718d430d3d7SJason Baron }
719d430d3d7SJason Baron
jump_label_del_module(struct module * mod)720d430d3d7SJason Baron static void jump_label_del_module(struct module *mod)
721d430d3d7SJason Baron {
722d430d3d7SJason Baron struct jump_entry *iter_start = mod->jump_entries;
723d430d3d7SJason Baron struct jump_entry *iter_stop = iter_start + mod->num_jump_entries;
724d430d3d7SJason Baron struct jump_entry *iter;
725c5905afbSIngo Molnar struct static_key *key = NULL;
726c5905afbSIngo Molnar struct static_key_mod *jlm, **prev;
727d430d3d7SJason Baron
728d430d3d7SJason Baron for (iter = iter_start; iter < iter_stop; iter++) {
7297dcfd915SPeter Zijlstra if (jump_entry_key(iter) == key)
730d430d3d7SJason Baron continue;
731d430d3d7SJason Baron
7327dcfd915SPeter Zijlstra key = jump_entry_key(iter);
733d430d3d7SJason Baron
7349ae033acSArd Biesheuvel if (within_module((unsigned long)key, mod))
735d430d3d7SJason Baron continue;
736d430d3d7SJason Baron
7373821fd35SJason Baron /* No memory during module load */
7383821fd35SJason Baron if (WARN_ON(!static_key_linked(key)))
7393821fd35SJason Baron continue;
7403821fd35SJason Baron
741d430d3d7SJason Baron prev = &key->next;
7423821fd35SJason Baron jlm = static_key_mod(key);
743d430d3d7SJason Baron
744d430d3d7SJason Baron while (jlm && jlm->mod != mod) {
745d430d3d7SJason Baron prev = &jlm->next;
746d430d3d7SJason Baron jlm = jlm->next;
747d430d3d7SJason Baron }
748d430d3d7SJason Baron
7493821fd35SJason Baron /* No memory during module load */
7503821fd35SJason Baron if (WARN_ON(!jlm))
7513821fd35SJason Baron continue;
7523821fd35SJason Baron
7533821fd35SJason Baron if (prev == &key->next)
7543821fd35SJason Baron static_key_set_mod(key, jlm->next);
7553821fd35SJason Baron else
756d430d3d7SJason Baron *prev = jlm->next;
7573821fd35SJason Baron
7583821fd35SJason Baron kfree(jlm);
7593821fd35SJason Baron
7603821fd35SJason Baron jlm = static_key_mod(key);
7613821fd35SJason Baron /* if only one etry is left, fold it back into the static_key */
7623821fd35SJason Baron if (jlm->next == NULL) {
7633821fd35SJason Baron static_key_set_entries(key, jlm->entries);
7643821fd35SJason Baron static_key_clear_linked(key);
765d430d3d7SJason Baron kfree(jlm);
766d430d3d7SJason Baron }
767d430d3d7SJason Baron }
768d430d3d7SJason Baron }
769d430d3d7SJason Baron
770d430d3d7SJason Baron static int
jump_label_module_notify(struct notifier_block * self,unsigned long val,void * data)771d430d3d7SJason Baron jump_label_module_notify(struct notifier_block *self, unsigned long val,
772d430d3d7SJason Baron void *data)
773d430d3d7SJason Baron {
774d430d3d7SJason Baron struct module *mod = data;
775d430d3d7SJason Baron int ret = 0;
776d430d3d7SJason Baron
777f2545b2dSThomas Gleixner cpus_read_lock();
778f2545b2dSThomas Gleixner jump_label_lock();
779f2545b2dSThomas Gleixner
780d430d3d7SJason Baron switch (val) {
781d430d3d7SJason Baron case MODULE_STATE_COMING:
782d430d3d7SJason Baron ret = jump_label_add_module(mod);
7833821fd35SJason Baron if (ret) {
784da260fe1SBorislav Petkov WARN(1, "Failed to allocate memory: jump_label may not work properly.\n");
785d430d3d7SJason Baron jump_label_del_module(mod);
7863821fd35SJason Baron }
787d430d3d7SJason Baron break;
788d430d3d7SJason Baron case MODULE_STATE_GOING:
789d430d3d7SJason Baron jump_label_del_module(mod);
790d430d3d7SJason Baron break;
791d430d3d7SJason Baron }
792d430d3d7SJason Baron
793f2545b2dSThomas Gleixner jump_label_unlock();
794f2545b2dSThomas Gleixner cpus_read_unlock();
795f2545b2dSThomas Gleixner
796d430d3d7SJason Baron return notifier_from_errno(ret);
797d430d3d7SJason Baron }
798d430d3d7SJason Baron
799885885f6SWei Yongjun static struct notifier_block jump_label_module_nb = {
800d430d3d7SJason Baron .notifier_call = jump_label_module_notify,
801d430d3d7SJason Baron .priority = 1, /* higher than tracepoints */
802d430d3d7SJason Baron };
803d430d3d7SJason Baron
jump_label_init_module(void)804d430d3d7SJason Baron static __init int jump_label_init_module(void)
805d430d3d7SJason Baron {
806d430d3d7SJason Baron return register_module_notifier(&jump_label_module_nb);
807d430d3d7SJason Baron }
808d430d3d7SJason Baron early_initcall(jump_label_init_module);
809d430d3d7SJason Baron
810d430d3d7SJason Baron #endif /* CONFIG_MODULES */
8114c3ef6d7SJason Baron
8124c3ef6d7SJason Baron /***
8134c3ef6d7SJason Baron * jump_label_text_reserved - check if addr range is reserved
8144c3ef6d7SJason Baron * @start: start text addr
8154c3ef6d7SJason Baron * @end: end text addr
8164c3ef6d7SJason Baron *
8174c3ef6d7SJason Baron * checks if the text addr located between @start and @end
8184c3ef6d7SJason Baron * overlaps with any of the jump label patch addresses. Code
8194c3ef6d7SJason Baron * that wants to modify kernel text should first verify that
8204c3ef6d7SJason Baron * it does not overlap with any of the jump label addresses.
82191bad2f8SJason Baron * Caller must hold jump_label_mutex.
8224c3ef6d7SJason Baron *
8234c3ef6d7SJason Baron * returns 1 if there is an overlap, 0 otherwise
8244c3ef6d7SJason Baron */
jump_label_text_reserved(void * start,void * end)8254c3ef6d7SJason Baron int jump_label_text_reserved(void *start, void *end)
8264c3ef6d7SJason Baron {
8279e667624SPeter Zijlstra bool init = system_state < SYSTEM_RUNNING;
828d430d3d7SJason Baron int ret = __jump_label_text_reserved(__start___jump_table,
8299e667624SPeter Zijlstra __stop___jump_table, start, end, init);
8304c3ef6d7SJason Baron
831bf5438fcSJason Baron if (ret)
832d430d3d7SJason Baron return ret;
833d430d3d7SJason Baron
834d430d3d7SJason Baron #ifdef CONFIG_MODULES
835d430d3d7SJason Baron ret = __jump_label_mod_text_reserved(start, end);
836d430d3d7SJason Baron #endif
837bf5438fcSJason Baron return ret;
838bf5438fcSJason Baron }
839bf5438fcSJason Baron
jump_label_update(struct static_key * key)840706249c2SPeter Zijlstra static void jump_label_update(struct static_key *key)
841bf5438fcSJason Baron {
842c5905afbSIngo Molnar struct jump_entry *stop = __stop___jump_table;
84355d2eba8SPeter Zijlstra bool init = system_state < SYSTEM_RUNNING;
8443821fd35SJason Baron struct jump_entry *entry;
845d430d3d7SJason Baron #ifdef CONFIG_MODULES
846bed831f9SPeter Zijlstra struct module *mod;
847140fe3b1SXiao Guangrong
8483821fd35SJason Baron if (static_key_linked(key)) {
849706249c2SPeter Zijlstra __jump_label_mod_update(key);
8503821fd35SJason Baron return;
8513821fd35SJason Baron }
852140fe3b1SXiao Guangrong
853bed831f9SPeter Zijlstra preempt_disable();
854bed831f9SPeter Zijlstra mod = __module_address((unsigned long)key);
85555d2eba8SPeter Zijlstra if (mod) {
856140fe3b1SXiao Guangrong stop = mod->jump_entries + mod->num_jump_entries;
85755d2eba8SPeter Zijlstra init = mod->state == MODULE_STATE_COMING;
85855d2eba8SPeter Zijlstra }
859bed831f9SPeter Zijlstra preempt_enable();
860d430d3d7SJason Baron #endif
8613821fd35SJason Baron entry = static_key_entries(key);
862140fe3b1SXiao Guangrong /* if there are no users, entry can be NULL */
863140fe3b1SXiao Guangrong if (entry)
86455d2eba8SPeter Zijlstra __jump_label_update(key, entry, stop, init);
865bf5438fcSJason Baron }
866bf5438fcSJason Baron
8671987c947SPeter Zijlstra #ifdef CONFIG_STATIC_KEYS_SELFTEST
8681987c947SPeter Zijlstra static DEFINE_STATIC_KEY_TRUE(sk_true);
8691987c947SPeter Zijlstra static DEFINE_STATIC_KEY_FALSE(sk_false);
8701987c947SPeter Zijlstra
jump_label_test(void)8711987c947SPeter Zijlstra static __init int jump_label_test(void)
8721987c947SPeter Zijlstra {
8731987c947SPeter Zijlstra int i;
8741987c947SPeter Zijlstra
8751987c947SPeter Zijlstra for (i = 0; i < 2; i++) {
8761987c947SPeter Zijlstra WARN_ON(static_key_enabled(&sk_true.key) != true);
8771987c947SPeter Zijlstra WARN_ON(static_key_enabled(&sk_false.key) != false);
8781987c947SPeter Zijlstra
8791987c947SPeter Zijlstra WARN_ON(!static_branch_likely(&sk_true));
8801987c947SPeter Zijlstra WARN_ON(!static_branch_unlikely(&sk_true));
8811987c947SPeter Zijlstra WARN_ON(static_branch_likely(&sk_false));
8821987c947SPeter Zijlstra WARN_ON(static_branch_unlikely(&sk_false));
8831987c947SPeter Zijlstra
8841987c947SPeter Zijlstra static_branch_disable(&sk_true);
8851987c947SPeter Zijlstra static_branch_enable(&sk_false);
8861987c947SPeter Zijlstra
8871987c947SPeter Zijlstra WARN_ON(static_key_enabled(&sk_true.key) == true);
8881987c947SPeter Zijlstra WARN_ON(static_key_enabled(&sk_false.key) == false);
8891987c947SPeter Zijlstra
8901987c947SPeter Zijlstra WARN_ON(static_branch_likely(&sk_true));
8911987c947SPeter Zijlstra WARN_ON(static_branch_unlikely(&sk_true));
8921987c947SPeter Zijlstra WARN_ON(!static_branch_likely(&sk_false));
8931987c947SPeter Zijlstra WARN_ON(!static_branch_unlikely(&sk_false));
8941987c947SPeter Zijlstra
8951987c947SPeter Zijlstra static_branch_enable(&sk_true);
8961987c947SPeter Zijlstra static_branch_disable(&sk_false);
8971987c947SPeter Zijlstra }
8981987c947SPeter Zijlstra
8991987c947SPeter Zijlstra return 0;
9001987c947SPeter Zijlstra }
90192ee46efSJason Baron early_initcall(jump_label_test);
9021987c947SPeter Zijlstra #endif /* STATIC_KEYS_SELFTEST */
903