Blame view

kernel/jump_label.c 19.8 KB
bf5438fca   Jason Baron   jump label: Base ...
1
2
3
4
  /*
   * jump label support
   *
   * Copyright (C) 2009 Jason Baron <jbaron@redhat.com>
90eec103b   Peter Zijlstra   treewide: Remove ...
5
   * Copyright (C) 2011 Peter Zijlstra
bf5438fca   Jason Baron   jump label: Base ...
6
7
   *
   */
bf5438fca   Jason Baron   jump label: Base ...
8
9
10
11
  #include <linux/memory.h>
  #include <linux/uaccess.h>
  #include <linux/module.h>
  #include <linux/list.h>
bf5438fca   Jason Baron   jump label: Base ...
12
13
14
  #include <linux/slab.h>
  #include <linux/sort.h>
  #include <linux/err.h>
c5905afb0   Ingo Molnar   static keys: Intr...
15
  #include <linux/static_key.h>
851cf6e7d   Andrew Jones   jump_label: Split...
16
  #include <linux/jump_label_ratelimit.h>
1f69bf9c6   Jason Baron   jump_label: remov...
17
  #include <linux/bug.h>
f2545b2d4   Thomas Gleixner   jump_label: Reord...
18
  #include <linux/cpu.h>
578ae447e   Josh Poimboeuf   jump_label: Disab...
19
  #include <asm/sections.h>
bf5438fca   Jason Baron   jump label: Base ...
20
21
  
  #ifdef HAVE_JUMP_LABEL
bf5438fca   Jason Baron   jump label: Base ...
22
23
  /* mutex to protect coming/going of the the jump_label table */
  static DEFINE_MUTEX(jump_label_mutex);
91bad2f8d   Jason Baron   jump label: Fix d...
24
25
26
27
28
29
30
31
32
  void jump_label_lock(void)
  {
  	mutex_lock(&jump_label_mutex);
  }
  
  void jump_label_unlock(void)
  {
  	mutex_unlock(&jump_label_mutex);
  }
bf5438fca   Jason Baron   jump label: Base ...
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
  static int jump_label_cmp(const void *a, const void *b)
  {
  	const struct jump_entry *jea = a;
  	const struct jump_entry *jeb = b;
  
  	if (jea->key < jeb->key)
  		return -1;
  
  	if (jea->key > jeb->key)
  		return 1;
  
  	return 0;
  }
  
  static void
d430d3d7e   Jason Baron   jump label: Intro...
48
  jump_label_sort_entries(struct jump_entry *start, struct jump_entry *stop)
bf5438fca   Jason Baron   jump label: Base ...
49
50
51
52
53
54
55
  {
  	unsigned long size;
  
  	size = (((unsigned long)stop - (unsigned long)start)
  					/ sizeof(struct jump_entry));
  	sort(start, size, sizeof(struct jump_entry), jump_label_cmp, NULL);
  }
706249c22   Peter Zijlstra   locking/static_ke...
56
  static void jump_label_update(struct static_key *key);
a1efb01fe   Peter Zijlstra   jump_label, locki...
57

1f69bf9c6   Jason Baron   jump_label: remov...
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
  /*
   * There are similar definitions for the !HAVE_JUMP_LABEL case in jump_label.h.
   * The use of 'atomic_read()' requires atomic.h and its problematic for some
   * kernel headers such as kernel.h and others. Since static_key_count() is not
   * used in the branch statements as it is for the !HAVE_JUMP_LABEL case its ok
   * to have it be a function here. Similarly, for 'static_key_enable()' and
   * 'static_key_disable()', which require bug.h. This should allow jump_label.h
   * to be included from most/all places for HAVE_JUMP_LABEL.
   */
  int static_key_count(struct static_key *key)
  {
  	/*
  	 * -1 means the first static_key_slow_inc() is in progress.
  	 *  static_key_enabled() must return true, so return 1 here.
  	 */
  	int n = atomic_read(&key->enabled);
  
  	return n >= 0 ? n : 1;
  }
  EXPORT_SYMBOL_GPL(static_key_count);
ce48c1464   Peter Zijlstra   sched/core: Fix c...
78
  void static_key_slow_inc_cpuslocked(struct static_key *key)
bf5438fca   Jason Baron   jump label: Base ...
79
  {
4c5ea0a9c   Paolo Bonzini   locking/static_ke...
80
  	int v, v1;
5cdda5117   Borislav Petkov   locking/static_ke...
81
  	STATIC_KEY_CHECK_USE(key);
4c5ea0a9c   Paolo Bonzini   locking/static_ke...
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
  
  	/*
  	 * Careful if we get concurrent static_key_slow_inc() calls;
  	 * later calls must wait for the first one to _finish_ the
  	 * jump_label_update() process.  At the same time, however,
  	 * the jump_label_update() call below wants to see
  	 * static_key_enabled(&key) for jumps to be updated properly.
  	 *
  	 * So give a special meaning to negative key->enabled: it sends
  	 * static_key_slow_inc() down the slow path, and it is non-zero
  	 * so it counts as "enabled" in jump_label_update().  Note that
  	 * atomic_inc_unless_negative() checks >= 0, so roll our own.
  	 */
  	for (v = atomic_read(&key->enabled); v > 0; v = v1) {
  		v1 = atomic_cmpxchg(&key->enabled, v, v + 1);
8b7b41280   Marc Zyngier   jump_label: Split...
97
  		if (likely(v1 == v))
4c5ea0a9c   Paolo Bonzini   locking/static_ke...
98
99
  			return;
  	}
bf5438fca   Jason Baron   jump label: Base ...
100

d430d3d7e   Jason Baron   jump label: Intro...
101
  	jump_label_lock();
4c5ea0a9c   Paolo Bonzini   locking/static_ke...
102
103
  	if (atomic_read(&key->enabled) == 0) {
  		atomic_set(&key->enabled, -1);
706249c22   Peter Zijlstra   locking/static_ke...
104
  		jump_label_update(key);
d0646a6f5   Peter Zijlstra   jump_label: Add R...
105
106
107
108
109
  		/*
  		 * Ensure that if the above cmpxchg loop observes our positive
  		 * value, it must also observe all the text changes.
  		 */
  		atomic_set_release(&key->enabled, 1);
4c5ea0a9c   Paolo Bonzini   locking/static_ke...
110
111
112
  	} else {
  		atomic_inc(&key->enabled);
  	}
d430d3d7e   Jason Baron   jump label: Intro...
113
  	jump_label_unlock();
8b7b41280   Marc Zyngier   jump_label: Split...
114
115
116
117
118
119
  }
  
  void static_key_slow_inc(struct static_key *key)
  {
  	cpus_read_lock();
  	static_key_slow_inc_cpuslocked(key);
f2545b2d4   Thomas Gleixner   jump_label: Reord...
120
  	cpus_read_unlock();
bf5438fca   Jason Baron   jump label: Base ...
121
  }
c5905afb0   Ingo Molnar   static keys: Intr...
122
  EXPORT_SYMBOL_GPL(static_key_slow_inc);
bf5438fca   Jason Baron   jump label: Base ...
123

5a40527f8   Marc Zyngier   jump_label: Provi...
124
  void static_key_enable_cpuslocked(struct static_key *key)
1dbb6704d   Paolo Bonzini   jump_label: Fix c...
125
  {
5cdda5117   Borislav Petkov   locking/static_ke...
126
  	STATIC_KEY_CHECK_USE(key);
5a40527f8   Marc Zyngier   jump_label: Provi...
127

1dbb6704d   Paolo Bonzini   jump_label: Fix c...
128
129
130
131
  	if (atomic_read(&key->enabled) > 0) {
  		WARN_ON_ONCE(atomic_read(&key->enabled) != 1);
  		return;
  	}
1dbb6704d   Paolo Bonzini   jump_label: Fix c...
132
133
134
135
  	jump_label_lock();
  	if (atomic_read(&key->enabled) == 0) {
  		atomic_set(&key->enabled, -1);
  		jump_label_update(key);
d0646a6f5   Peter Zijlstra   jump_label: Add R...
136
137
138
139
  		/*
  		 * See static_key_slow_inc().
  		 */
  		atomic_set_release(&key->enabled, 1);
1dbb6704d   Paolo Bonzini   jump_label: Fix c...
140
141
  	}
  	jump_label_unlock();
5a40527f8   Marc Zyngier   jump_label: Provi...
142
143
144
145
146
147
148
  }
  EXPORT_SYMBOL_GPL(static_key_enable_cpuslocked);
  
  void static_key_enable(struct static_key *key)
  {
  	cpus_read_lock();
  	static_key_enable_cpuslocked(key);
1dbb6704d   Paolo Bonzini   jump_label: Fix c...
149
150
151
  	cpus_read_unlock();
  }
  EXPORT_SYMBOL_GPL(static_key_enable);
5a40527f8   Marc Zyngier   jump_label: Provi...
152
  void static_key_disable_cpuslocked(struct static_key *key)
1dbb6704d   Paolo Bonzini   jump_label: Fix c...
153
  {
5cdda5117   Borislav Petkov   locking/static_ke...
154
  	STATIC_KEY_CHECK_USE(key);
5a40527f8   Marc Zyngier   jump_label: Provi...
155

1dbb6704d   Paolo Bonzini   jump_label: Fix c...
156
157
158
159
  	if (atomic_read(&key->enabled) != 1) {
  		WARN_ON_ONCE(atomic_read(&key->enabled) != 0);
  		return;
  	}
1dbb6704d   Paolo Bonzini   jump_label: Fix c...
160
161
162
163
  	jump_label_lock();
  	if (atomic_cmpxchg(&key->enabled, 1, 0))
  		jump_label_update(key);
  	jump_label_unlock();
5a40527f8   Marc Zyngier   jump_label: Provi...
164
165
166
167
168
169
170
  }
  EXPORT_SYMBOL_GPL(static_key_disable_cpuslocked);
  
  void static_key_disable(struct static_key *key)
  {
  	cpus_read_lock();
  	static_key_disable_cpuslocked(key);
1dbb6704d   Paolo Bonzini   jump_label: Fix c...
171
172
173
  	cpus_read_unlock();
  }
  EXPORT_SYMBOL_GPL(static_key_disable);
ce48c1464   Peter Zijlstra   sched/core: Fix c...
174
  static void __static_key_slow_dec_cpuslocked(struct static_key *key,
8b7b41280   Marc Zyngier   jump_label: Split...
175
176
  					   unsigned long rate_limit,
  					   struct delayed_work *work)
bf5438fca   Jason Baron   jump label: Base ...
177
  {
4c5ea0a9c   Paolo Bonzini   locking/static_ke...
178
179
180
181
182
183
184
  	/*
  	 * The negative count check is valid even when a negative
  	 * key->enabled is in use by static_key_slow_inc(); a
  	 * __static_key_slow_dec() before the first static_key_slow_inc()
  	 * returns is unbalanced, because all other static_key_slow_inc()
  	 * instances block while the update is in progress.
  	 */
fadf0464b   Jason Baron   jump label: Add a...
185
186
187
188
  	if (!atomic_dec_and_mutex_lock(&key->enabled, &jump_label_mutex)) {
  		WARN(atomic_read(&key->enabled) < 0,
  		     "jump label: negative count!
  ");
d430d3d7e   Jason Baron   jump label: Intro...
189
  		return;
fadf0464b   Jason Baron   jump label: Add a...
190
  	}
bf5438fca   Jason Baron   jump label: Base ...
191

b20295207   Gleb Natapov   perf, core: Rate ...
192
193
194
  	if (rate_limit) {
  		atomic_inc(&key->enabled);
  		schedule_delayed_work(work, rate_limit);
c5905afb0   Ingo Molnar   static keys: Intr...
195
  	} else {
706249c22   Peter Zijlstra   locking/static_ke...
196
  		jump_label_update(key);
c5905afb0   Ingo Molnar   static keys: Intr...
197
  	}
91bad2f8d   Jason Baron   jump label: Fix d...
198
  	jump_label_unlock();
8b7b41280   Marc Zyngier   jump_label: Split...
199
200
201
202
203
204
205
  }
  
  static void __static_key_slow_dec(struct static_key *key,
  				  unsigned long rate_limit,
  				  struct delayed_work *work)
  {
  	cpus_read_lock();
ce48c1464   Peter Zijlstra   sched/core: Fix c...
206
  	__static_key_slow_dec_cpuslocked(key, rate_limit, work);
f2545b2d4   Thomas Gleixner   jump_label: Reord...
207
  	cpus_read_unlock();
bf5438fca   Jason Baron   jump label: Base ...
208
  }
b20295207   Gleb Natapov   perf, core: Rate ...
209
210
  static void jump_label_update_timeout(struct work_struct *work)
  {
c5905afb0   Ingo Molnar   static keys: Intr...
211
212
213
  	struct static_key_deferred *key =
  		container_of(work, struct static_key_deferred, work.work);
  	__static_key_slow_dec(&key->key, 0, NULL);
b20295207   Gleb Natapov   perf, core: Rate ...
214
  }
c5905afb0   Ingo Molnar   static keys: Intr...
215
  void static_key_slow_dec(struct static_key *key)
b20295207   Gleb Natapov   perf, core: Rate ...
216
  {
5cdda5117   Borislav Petkov   locking/static_ke...
217
  	STATIC_KEY_CHECK_USE(key);
c5905afb0   Ingo Molnar   static keys: Intr...
218
  	__static_key_slow_dec(key, 0, NULL);
b20295207   Gleb Natapov   perf, core: Rate ...
219
  }
c5905afb0   Ingo Molnar   static keys: Intr...
220
  EXPORT_SYMBOL_GPL(static_key_slow_dec);
b20295207   Gleb Natapov   perf, core: Rate ...
221

ce48c1464   Peter Zijlstra   sched/core: Fix c...
222
223
224
225
226
  void static_key_slow_dec_cpuslocked(struct static_key *key)
  {
  	STATIC_KEY_CHECK_USE(key);
  	__static_key_slow_dec_cpuslocked(key, 0, NULL);
  }
c5905afb0   Ingo Molnar   static keys: Intr...
227
  void static_key_slow_dec_deferred(struct static_key_deferred *key)
b20295207   Gleb Natapov   perf, core: Rate ...
228
  {
5cdda5117   Borislav Petkov   locking/static_ke...
229
  	STATIC_KEY_CHECK_USE(key);
c5905afb0   Ingo Molnar   static keys: Intr...
230
  	__static_key_slow_dec(&key->key, key->timeout, &key->work);
b20295207   Gleb Natapov   perf, core: Rate ...
231
  }
c5905afb0   Ingo Molnar   static keys: Intr...
232
  EXPORT_SYMBOL_GPL(static_key_slow_dec_deferred);
b20295207   Gleb Natapov   perf, core: Rate ...
233

b6416e610   David Matlack   jump_labels: API ...
234
235
  void static_key_deferred_flush(struct static_key_deferred *key)
  {
5cdda5117   Borislav Petkov   locking/static_ke...
236
  	STATIC_KEY_CHECK_USE(key);
b6416e610   David Matlack   jump_labels: API ...
237
238
239
  	flush_delayed_work(&key->work);
  }
  EXPORT_SYMBOL_GPL(static_key_deferred_flush);
c5905afb0   Ingo Molnar   static keys: Intr...
240
  void jump_label_rate_limit(struct static_key_deferred *key,
b20295207   Gleb Natapov   perf, core: Rate ...
241
242
  		unsigned long rl)
  {
5cdda5117   Borislav Petkov   locking/static_ke...
243
  	STATIC_KEY_CHECK_USE(key);
b20295207   Gleb Natapov   perf, core: Rate ...
244
245
246
  	key->timeout = rl;
  	INIT_DELAYED_WORK(&key->work, jump_label_update_timeout);
  }
a181dc14e   Gleb Natapov   jump_label: Expor...
247
  EXPORT_SYMBOL_GPL(jump_label_rate_limit);
b20295207   Gleb Natapov   perf, core: Rate ...
248

4c3ef6d79   Jason Baron   jump label: Add j...
249
250
251
252
253
254
255
256
  static int addr_conflict(struct jump_entry *entry, void *start, void *end)
  {
  	if (entry->code <= (unsigned long)end &&
  		entry->code + JUMP_LABEL_NOP_SIZE > (unsigned long)start)
  		return 1;
  
  	return 0;
  }
d430d3d7e   Jason Baron   jump label: Intro...
257
258
  static int __jump_label_text_reserved(struct jump_entry *iter_start,
  		struct jump_entry *iter_stop, void *start, void *end)
4c3ef6d79   Jason Baron   jump label: Add j...
259
  {
4c3ef6d79   Jason Baron   jump label: Add j...
260
  	struct jump_entry *iter;
4c3ef6d79   Jason Baron   jump label: Add j...
261

4c3ef6d79   Jason Baron   jump label: Add j...
262
263
  	iter = iter_start;
  	while (iter < iter_stop) {
d430d3d7e   Jason Baron   jump label: Intro...
264
265
  		if (addr_conflict(iter, start, end))
  			return 1;
4c3ef6d79   Jason Baron   jump label: Add j...
266
267
  		iter++;
  	}
d430d3d7e   Jason Baron   jump label: Intro...
268
269
  	return 0;
  }
706249c22   Peter Zijlstra   locking/static_ke...
270
  /*
20284aa77   Jeremy Fitzhardinge   jump_label: add a...
271
272
273
274
275
   * Update code which is definitely not currently executing.
   * Architectures which need heavyweight synchronization to modify
   * running code can override this to make the non-live update case
   * cheaper.
   */
9cdbe1cba   Peter Zijlstra   jump_label, x86: ...
276
  void __weak __init_or_module arch_jump_label_transform_static(struct jump_entry *entry,
20284aa77   Jeremy Fitzhardinge   jump_label: add a...
277
278
  					    enum jump_label_type type)
  {
706249c22   Peter Zijlstra   locking/static_ke...
279
  	arch_jump_label_transform(entry, type);
20284aa77   Jeremy Fitzhardinge   jump_label: add a...
280
  }
706249c22   Peter Zijlstra   locking/static_ke...
281
  static inline struct jump_entry *static_key_entries(struct static_key *key)
d430d3d7e   Jason Baron   jump label: Intro...
282
  {
3821fd35b   Jason Baron   jump_label: Reduc...
283
284
  	WARN_ON_ONCE(key->type & JUMP_TYPE_LINKED);
  	return (struct jump_entry *)(key->type & ~JUMP_TYPE_MASK);
4c3ef6d79   Jason Baron   jump label: Add j...
285
  }
706249c22   Peter Zijlstra   locking/static_ke...
286
  static inline bool static_key_type(struct static_key *key)
c5905afb0   Ingo Molnar   static keys: Intr...
287
  {
3821fd35b   Jason Baron   jump_label: Reduc...
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
  	return key->type & JUMP_TYPE_TRUE;
  }
  
  static inline bool static_key_linked(struct static_key *key)
  {
  	return key->type & JUMP_TYPE_LINKED;
  }
  
  static inline void static_key_clear_linked(struct static_key *key)
  {
  	key->type &= ~JUMP_TYPE_LINKED;
  }
  
  static inline void static_key_set_linked(struct static_key *key)
  {
  	key->type |= JUMP_TYPE_LINKED;
a1efb01fe   Peter Zijlstra   jump_label, locki...
304
  }
c5905afb0   Ingo Molnar   static keys: Intr...
305

7dcfd915b   Peter Zijlstra   jump_label: Add j...
306
307
  static inline struct static_key *jump_entry_key(struct jump_entry *entry)
  {
11276d530   Peter Zijlstra   locking/static_ke...
308
309
310
311
312
313
  	return (struct static_key *)((unsigned long)entry->key & ~1UL);
  }
  
  static bool jump_entry_branch(struct jump_entry *entry)
  {
  	return (unsigned long)entry->key & 1UL;
7dcfd915b   Peter Zijlstra   jump_label: Add j...
314
  }
3821fd35b   Jason Baron   jump_label: Reduc...
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
  /***
   * A 'struct static_key' uses a union such that it either points directly
   * to a table of 'struct jump_entry' or to a linked list of modules which in
   * turn point to 'struct jump_entry' tables.
   *
   * The two lower bits of the pointer are used to keep track of which pointer
   * type is in use and to store the initial branch direction, we use an access
   * function which preserves these bits.
   */
  static void static_key_set_entries(struct static_key *key,
  				   struct jump_entry *entries)
  {
  	unsigned long type;
  
  	WARN_ON_ONCE((unsigned long)entries & JUMP_TYPE_MASK);
  	type = key->type & JUMP_TYPE_MASK;
  	key->entries = entries;
  	key->type |= type;
  }
706249c22   Peter Zijlstra   locking/static_ke...
334
  static enum jump_label_type jump_label_type(struct jump_entry *entry)
a1efb01fe   Peter Zijlstra   jump_label, locki...
335
  {
706249c22   Peter Zijlstra   locking/static_ke...
336
  	struct static_key *key = jump_entry_key(entry);
a1efb01fe   Peter Zijlstra   jump_label, locki...
337
  	bool enabled = static_key_enabled(key);
11276d530   Peter Zijlstra   locking/static_ke...
338
  	bool branch = jump_entry_branch(entry);
c5905afb0   Ingo Molnar   static keys: Intr...
339

11276d530   Peter Zijlstra   locking/static_ke...
340
341
  	/* See the comment in linux/jump_label.h */
  	return enabled ^ branch;
c5905afb0   Ingo Molnar   static keys: Intr...
342
  }
706249c22   Peter Zijlstra   locking/static_ke...
343
344
345
346
347
348
  static void __jump_label_update(struct static_key *key,
  				struct jump_entry *entry,
  				struct jump_entry *stop)
  {
  	for (; (entry < stop) && (jump_entry_key(entry) == key); entry++) {
  		/*
dc1dd184c   Josh Poimboeuf   jump_label: Warn ...
349
350
  		 * An entry->code of 0 indicates an entry which has been
  		 * disabled because it was in an init text area.
706249c22   Peter Zijlstra   locking/static_ke...
351
  		 */
dc1dd184c   Josh Poimboeuf   jump_label: Warn ...
352
353
354
355
  		if (entry->code) {
  			if (kernel_text_address(entry->code))
  				arch_jump_label_transform(entry, jump_label_type(entry));
  			else
af1d830bf   Josh Poimboeuf   jump_label: Fix s...
356
357
  				WARN_ONCE(1, "can't patch jump_label at %pS",
  					  (void *)(unsigned long)entry->code);
dc1dd184c   Josh Poimboeuf   jump_label: Warn ...
358
  		}
706249c22   Peter Zijlstra   locking/static_ke...
359
360
  	}
  }
97ce2c88f   Jeremy Fitzhardinge   jump-label: initi...
361
  void __init jump_label_init(void)
bf5438fca   Jason Baron   jump label: Base ...
362
  {
bf5438fca   Jason Baron   jump label: Base ...
363
364
  	struct jump_entry *iter_start = __start___jump_table;
  	struct jump_entry *iter_stop = __stop___jump_table;
c5905afb0   Ingo Molnar   static keys: Intr...
365
  	struct static_key *key = NULL;
bf5438fca   Jason Baron   jump label: Base ...
366
  	struct jump_entry *iter;
1f69bf9c6   Jason Baron   jump_label: remov...
367
368
369
370
371
372
373
374
  	/*
  	 * Since we are initializing the static_key.enabled field with
  	 * with the 'raw' int values (to avoid pulling in atomic.h) in
  	 * jump_label.h, let's make sure that is safe. There are only two
  	 * cases to check since we initialize to 0 or 1.
  	 */
  	BUILD_BUG_ON((int)ATOMIC_INIT(0) != 0);
  	BUILD_BUG_ON((int)ATOMIC_INIT(1) != 1);
e3f91083f   Kevin Hao   jump_label: Make ...
375
376
  	if (static_key_initialized)
  		return;
f2545b2d4   Thomas Gleixner   jump_label: Reord...
377
  	cpus_read_lock();
91bad2f8d   Jason Baron   jump label: Fix d...
378
  	jump_label_lock();
d430d3d7e   Jason Baron   jump label: Intro...
379
380
381
  	jump_label_sort_entries(iter_start, iter_stop);
  
  	for (iter = iter_start; iter < iter_stop; iter++) {
c5905afb0   Ingo Molnar   static keys: Intr...
382
  		struct static_key *iterk;
37348804e   Jeremy Fitzhardinge   jump_label: if a ...
383

11276d530   Peter Zijlstra   locking/static_ke...
384
385
386
  		/* rewrite NOPs */
  		if (jump_label_type(iter) == JUMP_LABEL_NOP)
  			arch_jump_label_transform_static(iter, JUMP_LABEL_NOP);
7dcfd915b   Peter Zijlstra   jump_label: Add j...
387
  		iterk = jump_entry_key(iter);
37348804e   Jeremy Fitzhardinge   jump_label: if a ...
388
  		if (iterk == key)
d430d3d7e   Jason Baron   jump label: Intro...
389
  			continue;
37348804e   Jeremy Fitzhardinge   jump_label: if a ...
390
  		key = iterk;
3821fd35b   Jason Baron   jump_label: Reduc...
391
  		static_key_set_entries(key, iter);
bf5438fca   Jason Baron   jump label: Base ...
392
  	}
c4b2c0c5f   Hannes Frederic Sowa   static_key: WARN ...
393
  	static_key_initialized = true;
91bad2f8d   Jason Baron   jump label: Fix d...
394
  	jump_label_unlock();
f2545b2d4   Thomas Gleixner   jump_label: Reord...
395
  	cpus_read_unlock();
bf5438fca   Jason Baron   jump label: Base ...
396
  }
bf5438fca   Jason Baron   jump label: Base ...
397

578ae447e   Josh Poimboeuf   jump_label: Disab...
398
399
  /* Disable any jump label entries in __init/__exit code */
  void __init jump_label_invalidate_initmem(void)
333522447   Josh Poimboeuf   jump_label: Expli...
400
401
402
403
404
405
  {
  	struct jump_entry *iter_start = __start___jump_table;
  	struct jump_entry *iter_stop = __stop___jump_table;
  	struct jump_entry *iter;
  
  	for (iter = iter_start; iter < iter_stop; iter++) {
578ae447e   Josh Poimboeuf   jump_label: Disab...
406
  		if (init_section_contains((void *)(unsigned long)iter->code, 1))
333522447   Josh Poimboeuf   jump_label: Expli...
407
408
409
  			iter->code = 0;
  	}
  }
bf5438fca   Jason Baron   jump label: Base ...
410
  #ifdef CONFIG_MODULES
11276d530   Peter Zijlstra   locking/static_ke...
411
412
413
414
415
416
417
418
419
  static enum jump_label_type jump_label_init_type(struct jump_entry *entry)
  {
  	struct static_key *key = jump_entry_key(entry);
  	bool type = static_key_type(key);
  	bool branch = jump_entry_branch(entry);
  
  	/* See the comment in linux/jump_label.h */
  	return type ^ branch;
  }
c5905afb0   Ingo Molnar   static keys: Intr...
420
421
  struct static_key_mod {
  	struct static_key_mod *next;
d430d3d7e   Jason Baron   jump label: Intro...
422
423
424
  	struct jump_entry *entries;
  	struct module *mod;
  };
3821fd35b   Jason Baron   jump_label: Reduc...
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
  static inline struct static_key_mod *static_key_mod(struct static_key *key)
  {
  	WARN_ON_ONCE(!(key->type & JUMP_TYPE_LINKED));
  	return (struct static_key_mod *)(key->type & ~JUMP_TYPE_MASK);
  }
  
  /***
   * key->type and key->next are the same via union.
   * This sets key->next and preserves the type bits.
   *
   * See additional comments above static_key_set_entries().
   */
  static void static_key_set_mod(struct static_key *key,
  			       struct static_key_mod *mod)
  {
  	unsigned long type;
  
  	WARN_ON_ONCE((unsigned long)mod & JUMP_TYPE_MASK);
  	type = key->type & JUMP_TYPE_MASK;
  	key->next = mod;
  	key->type |= type;
  }
d430d3d7e   Jason Baron   jump label: Intro...
447
448
449
  static int __jump_label_mod_text_reserved(void *start, void *end)
  {
  	struct module *mod;
bdc9f3735   Rusty Russell   jump_label: disab...
450
  	preempt_disable();
d430d3d7e   Jason Baron   jump label: Intro...
451
  	mod = __module_text_address((unsigned long)start);
bdc9f3735   Rusty Russell   jump_label: disab...
452
453
  	WARN_ON_ONCE(__module_text_address((unsigned long)end) != mod);
  	preempt_enable();
d430d3d7e   Jason Baron   jump label: Intro...
454
455
  	if (!mod)
  		return 0;
d430d3d7e   Jason Baron   jump label: Intro...
456
457
458
459
460
  
  	return __jump_label_text_reserved(mod->jump_entries,
  				mod->jump_entries + mod->num_jump_entries,
  				start, end);
  }
706249c22   Peter Zijlstra   locking/static_ke...
461
  static void __jump_label_mod_update(struct static_key *key)
d430d3d7e   Jason Baron   jump label: Intro...
462
  {
706249c22   Peter Zijlstra   locking/static_ke...
463
  	struct static_key_mod *mod;
d430d3d7e   Jason Baron   jump label: Intro...
464

3821fd35b   Jason Baron   jump_label: Reduc...
465
466
467
468
469
470
471
472
473
474
  	for (mod = static_key_mod(key); mod; mod = mod->next) {
  		struct jump_entry *stop;
  		struct module *m;
  
  		/*
  		 * NULL if the static_key is defined in a module
  		 * that does not use it
  		 */
  		if (!mod->entries)
  			continue;
7cbc5b8d4   Jiri Olsa   jump_label: Check...
475

3821fd35b   Jason Baron   jump_label: Reduc...
476
477
478
479
480
481
  		m = mod->mod;
  		if (!m)
  			stop = __stop___jump_table;
  		else
  			stop = m->jump_entries + m->num_jump_entries;
  		__jump_label_update(key, mod->entries, stop);
d430d3d7e   Jason Baron   jump label: Intro...
482
483
484
485
486
487
488
489
490
491
492
493
  	}
  }
  
  /***
   * apply_jump_label_nops - patch module jump labels with arch_get_jump_label_nop()
   * @mod: module to patch
   *
   * Allow for run-time selection of the optimal nops. Before the module
   * loads patch these with arch_get_jump_label_nop(), which is specified by
   * the arch specific jump label code.
   */
  void jump_label_apply_nops(struct module *mod)
bf5438fca   Jason Baron   jump label: Base ...
494
  {
d430d3d7e   Jason Baron   jump label: Intro...
495
496
497
498
499
500
501
  	struct jump_entry *iter_start = mod->jump_entries;
  	struct jump_entry *iter_stop = iter_start + mod->num_jump_entries;
  	struct jump_entry *iter;
  
  	/* if the module doesn't have jump label entries, just return */
  	if (iter_start == iter_stop)
  		return;
11276d530   Peter Zijlstra   locking/static_ke...
502
503
504
505
506
  	for (iter = iter_start; iter < iter_stop; iter++) {
  		/* Only write NOPs for arch_branch_static(). */
  		if (jump_label_init_type(iter) == JUMP_LABEL_NOP)
  			arch_jump_label_transform_static(iter, JUMP_LABEL_NOP);
  	}
bf5438fca   Jason Baron   jump label: Base ...
507
  }
d430d3d7e   Jason Baron   jump label: Intro...
508
  static int jump_label_add_module(struct module *mod)
bf5438fca   Jason Baron   jump label: Base ...
509
  {
d430d3d7e   Jason Baron   jump label: Intro...
510
511
512
  	struct jump_entry *iter_start = mod->jump_entries;
  	struct jump_entry *iter_stop = iter_start + mod->num_jump_entries;
  	struct jump_entry *iter;
c5905afb0   Ingo Molnar   static keys: Intr...
513
  	struct static_key *key = NULL;
3821fd35b   Jason Baron   jump_label: Reduc...
514
  	struct static_key_mod *jlm, *jlm2;
bf5438fca   Jason Baron   jump label: Base ...
515
516
  
  	/* if the module doesn't have jump label entries, just return */
d430d3d7e   Jason Baron   jump label: Intro...
517
  	if (iter_start == iter_stop)
bf5438fca   Jason Baron   jump label: Base ...
518
  		return 0;
d430d3d7e   Jason Baron   jump label: Intro...
519
520
521
  	jump_label_sort_entries(iter_start, iter_stop);
  
  	for (iter = iter_start; iter < iter_stop; iter++) {
c5905afb0   Ingo Molnar   static keys: Intr...
522
  		struct static_key *iterk;
d430d3d7e   Jason Baron   jump label: Intro...
523

7dcfd915b   Peter Zijlstra   jump_label: Add j...
524
  		iterk = jump_entry_key(iter);
c5905afb0   Ingo Molnar   static keys: Intr...
525
526
  		if (iterk == key)
  			continue;
d430d3d7e   Jason Baron   jump label: Intro...
527

c5905afb0   Ingo Molnar   static keys: Intr...
528
  		key = iterk;
bed831f9a   Peter Zijlstra   module, jump_labe...
529
  		if (within_module(iter->key, mod)) {
3821fd35b   Jason Baron   jump_label: Reduc...
530
  			static_key_set_entries(key, iter);
d430d3d7e   Jason Baron   jump label: Intro...
531
  			continue;
bf5438fca   Jason Baron   jump label: Base ...
532
  		}
c5905afb0   Ingo Molnar   static keys: Intr...
533
  		jlm = kzalloc(sizeof(struct static_key_mod), GFP_KERNEL);
d430d3d7e   Jason Baron   jump label: Intro...
534
535
  		if (!jlm)
  			return -ENOMEM;
3821fd35b   Jason Baron   jump_label: Reduc...
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
  		if (!static_key_linked(key)) {
  			jlm2 = kzalloc(sizeof(struct static_key_mod),
  				       GFP_KERNEL);
  			if (!jlm2) {
  				kfree(jlm);
  				return -ENOMEM;
  			}
  			preempt_disable();
  			jlm2->mod = __module_address((unsigned long)key);
  			preempt_enable();
  			jlm2->entries = static_key_entries(key);
  			jlm2->next = NULL;
  			static_key_set_mod(key, jlm2);
  			static_key_set_linked(key);
  		}
d430d3d7e   Jason Baron   jump label: Intro...
551
552
  		jlm->mod = mod;
  		jlm->entries = iter;
3821fd35b   Jason Baron   jump_label: Reduc...
553
554
555
  		jlm->next = static_key_mod(key);
  		static_key_set_mod(key, jlm);
  		static_key_set_linked(key);
d430d3d7e   Jason Baron   jump label: Intro...
556

11276d530   Peter Zijlstra   locking/static_ke...
557
558
  		/* Only update if we've changed from our initial state */
  		if (jump_label_type(iter) != jump_label_init_type(iter))
706249c22   Peter Zijlstra   locking/static_ke...
559
  			__jump_label_update(key, iter, iter_stop);
bf5438fca   Jason Baron   jump label: Base ...
560
  	}
d430d3d7e   Jason Baron   jump label: Intro...
561

bf5438fca   Jason Baron   jump label: Base ...
562
563
  	return 0;
  }
d430d3d7e   Jason Baron   jump label: Intro...
564
  static void jump_label_del_module(struct module *mod)
bf5438fca   Jason Baron   jump label: Base ...
565
  {
d430d3d7e   Jason Baron   jump label: Intro...
566
567
568
  	struct jump_entry *iter_start = mod->jump_entries;
  	struct jump_entry *iter_stop = iter_start + mod->num_jump_entries;
  	struct jump_entry *iter;
c5905afb0   Ingo Molnar   static keys: Intr...
569
570
  	struct static_key *key = NULL;
  	struct static_key_mod *jlm, **prev;
bf5438fca   Jason Baron   jump label: Base ...
571

d430d3d7e   Jason Baron   jump label: Intro...
572
  	for (iter = iter_start; iter < iter_stop; iter++) {
7dcfd915b   Peter Zijlstra   jump_label: Add j...
573
  		if (jump_entry_key(iter) == key)
d430d3d7e   Jason Baron   jump label: Intro...
574
  			continue;
7dcfd915b   Peter Zijlstra   jump_label: Add j...
575
  		key = jump_entry_key(iter);
d430d3d7e   Jason Baron   jump label: Intro...
576

bed831f9a   Peter Zijlstra   module, jump_labe...
577
  		if (within_module(iter->key, mod))
d430d3d7e   Jason Baron   jump label: Intro...
578
  			continue;
3821fd35b   Jason Baron   jump_label: Reduc...
579
580
581
  		/* No memory during module load */
  		if (WARN_ON(!static_key_linked(key)))
  			continue;
d430d3d7e   Jason Baron   jump label: Intro...
582
  		prev = &key->next;
3821fd35b   Jason Baron   jump_label: Reduc...
583
  		jlm = static_key_mod(key);
bf5438fca   Jason Baron   jump label: Base ...
584

d430d3d7e   Jason Baron   jump label: Intro...
585
586
587
588
  		while (jlm && jlm->mod != mod) {
  			prev = &jlm->next;
  			jlm = jlm->next;
  		}
3821fd35b   Jason Baron   jump_label: Reduc...
589
590
591
592
593
594
595
  		/* No memory during module load */
  		if (WARN_ON(!jlm))
  			continue;
  
  		if (prev == &key->next)
  			static_key_set_mod(key, jlm->next);
  		else
d430d3d7e   Jason Baron   jump label: Intro...
596
  			*prev = jlm->next;
3821fd35b   Jason Baron   jump_label: Reduc...
597
598
599
600
601
602
603
604
  
  		kfree(jlm);
  
  		jlm = static_key_mod(key);
  		/* if only one etry is left, fold it back into the static_key */
  		if (jlm->next == NULL) {
  			static_key_set_entries(key, jlm->entries);
  			static_key_clear_linked(key);
d430d3d7e   Jason Baron   jump label: Intro...
605
  			kfree(jlm);
bf5438fca   Jason Baron   jump label: Base ...
606
607
608
  		}
  	}
  }
333522447   Josh Poimboeuf   jump_label: Expli...
609
  /* Disable any jump label entries in module init code */
d430d3d7e   Jason Baron   jump label: Intro...
610
  static void jump_label_invalidate_module_init(struct module *mod)
b842f8faf   Jason Baron   jump label: Fix m...
611
  {
d430d3d7e   Jason Baron   jump label: Intro...
612
613
  	struct jump_entry *iter_start = mod->jump_entries;
  	struct jump_entry *iter_stop = iter_start + mod->num_jump_entries;
b842f8faf   Jason Baron   jump label: Fix m...
614
  	struct jump_entry *iter;
b842f8faf   Jason Baron   jump label: Fix m...
615

d430d3d7e   Jason Baron   jump label: Intro...
616
617
618
  	for (iter = iter_start; iter < iter_stop; iter++) {
  		if (within_module_init(iter->code, mod))
  			iter->code = 0;
b842f8faf   Jason Baron   jump label: Fix m...
619
620
  	}
  }
bf5438fca   Jason Baron   jump label: Base ...
621
622
623
624
625
626
  static int
  jump_label_module_notify(struct notifier_block *self, unsigned long val,
  			 void *data)
  {
  	struct module *mod = data;
  	int ret = 0;
f2545b2d4   Thomas Gleixner   jump_label: Reord...
627
628
  	cpus_read_lock();
  	jump_label_lock();
bf5438fca   Jason Baron   jump label: Base ...
629
630
  	switch (val) {
  	case MODULE_STATE_COMING:
d430d3d7e   Jason Baron   jump label: Intro...
631
  		ret = jump_label_add_module(mod);
3821fd35b   Jason Baron   jump_label: Reduc...
632
  		if (ret) {
da260fe12   Borislav Petkov   jump_label: Fix t...
633
634
  			WARN(1, "Failed to allocate memory: jump_label may not work properly.
  ");
d430d3d7e   Jason Baron   jump label: Intro...
635
  			jump_label_del_module(mod);
3821fd35b   Jason Baron   jump_label: Reduc...
636
  		}
bf5438fca   Jason Baron   jump label: Base ...
637
638
  		break;
  	case MODULE_STATE_GOING:
d430d3d7e   Jason Baron   jump label: Intro...
639
  		jump_label_del_module(mod);
bf5438fca   Jason Baron   jump label: Base ...
640
  		break;
b842f8faf   Jason Baron   jump label: Fix m...
641
  	case MODULE_STATE_LIVE:
d430d3d7e   Jason Baron   jump label: Intro...
642
  		jump_label_invalidate_module_init(mod);
b842f8faf   Jason Baron   jump label: Fix m...
643
  		break;
bf5438fca   Jason Baron   jump label: Base ...
644
  	}
bf5438fca   Jason Baron   jump label: Base ...
645

f2545b2d4   Thomas Gleixner   jump_label: Reord...
646
647
  	jump_label_unlock();
  	cpus_read_unlock();
d430d3d7e   Jason Baron   jump label: Intro...
648
  	return notifier_from_errno(ret);
bf5438fca   Jason Baron   jump label: Base ...
649
  }
885885f6b   Wei Yongjun   locking/static_ke...
650
  static struct notifier_block jump_label_module_nb = {
bf5438fca   Jason Baron   jump label: Base ...
651
  	.notifier_call = jump_label_module_notify,
d430d3d7e   Jason Baron   jump label: Intro...
652
  	.priority = 1, /* higher than tracepoints */
bf5438fca   Jason Baron   jump label: Base ...
653
  };
d430d3d7e   Jason Baron   jump label: Intro...
654
  static __init int jump_label_init_module(void)
bf5438fca   Jason Baron   jump label: Base ...
655
656
657
  {
  	return register_module_notifier(&jump_label_module_nb);
  }
d430d3d7e   Jason Baron   jump label: Intro...
658
  early_initcall(jump_label_init_module);
bf5438fca   Jason Baron   jump label: Base ...
659
660
  
  #endif /* CONFIG_MODULES */
d430d3d7e   Jason Baron   jump label: Intro...
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
  /***
   * jump_label_text_reserved - check if addr range is reserved
   * @start: start text addr
   * @end: end text addr
   *
   * checks if the text addr located between @start and @end
   * overlaps with any of the jump label patch addresses. Code
   * that wants to modify kernel text should first verify that
   * it does not overlap with any of the jump label addresses.
   * Caller must hold jump_label_mutex.
   *
   * returns 1 if there is an overlap, 0 otherwise
   */
  int jump_label_text_reserved(void *start, void *end)
  {
  	int ret = __jump_label_text_reserved(__start___jump_table,
  			__stop___jump_table, start, end);
  
  	if (ret)
  		return ret;
  
  #ifdef CONFIG_MODULES
  	ret = __jump_label_mod_text_reserved(start, end);
  #endif
  	return ret;
  }
706249c22   Peter Zijlstra   locking/static_ke...
687
  static void jump_label_update(struct static_key *key)
d430d3d7e   Jason Baron   jump label: Intro...
688
  {
c5905afb0   Ingo Molnar   static keys: Intr...
689
  	struct jump_entry *stop = __stop___jump_table;
3821fd35b   Jason Baron   jump_label: Reduc...
690
  	struct jump_entry *entry;
d430d3d7e   Jason Baron   jump label: Intro...
691
  #ifdef CONFIG_MODULES
bed831f9a   Peter Zijlstra   module, jump_labe...
692
  	struct module *mod;
140fe3b1a   Xiao Guangrong   jump_label: Fix j...
693

3821fd35b   Jason Baron   jump_label: Reduc...
694
695
696
697
  	if (static_key_linked(key)) {
  		__jump_label_mod_update(key);
  		return;
  	}
140fe3b1a   Xiao Guangrong   jump_label: Fix j...
698

bed831f9a   Peter Zijlstra   module, jump_labe...
699
700
  	preempt_disable();
  	mod = __module_address((unsigned long)key);
140fe3b1a   Xiao Guangrong   jump_label: Fix j...
701
702
  	if (mod)
  		stop = mod->jump_entries + mod->num_jump_entries;
bed831f9a   Peter Zijlstra   module, jump_labe...
703
  	preempt_enable();
d430d3d7e   Jason Baron   jump label: Intro...
704
  #endif
3821fd35b   Jason Baron   jump_label: Reduc...
705
  	entry = static_key_entries(key);
140fe3b1a   Xiao Guangrong   jump_label: Fix j...
706
707
  	/* if there are no users, entry can be NULL */
  	if (entry)
706249c22   Peter Zijlstra   locking/static_ke...
708
  		__jump_label_update(key, entry, stop);
d430d3d7e   Jason Baron   jump label: Intro...
709
  }
1987c947d   Peter Zijlstra   locking/static_ke...
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
  #ifdef CONFIG_STATIC_KEYS_SELFTEST
  static DEFINE_STATIC_KEY_TRUE(sk_true);
  static DEFINE_STATIC_KEY_FALSE(sk_false);
  
  static __init int jump_label_test(void)
  {
  	int i;
  
  	for (i = 0; i < 2; i++) {
  		WARN_ON(static_key_enabled(&sk_true.key) != true);
  		WARN_ON(static_key_enabled(&sk_false.key) != false);
  
  		WARN_ON(!static_branch_likely(&sk_true));
  		WARN_ON(!static_branch_unlikely(&sk_true));
  		WARN_ON(static_branch_likely(&sk_false));
  		WARN_ON(static_branch_unlikely(&sk_false));
  
  		static_branch_disable(&sk_true);
  		static_branch_enable(&sk_false);
  
  		WARN_ON(static_key_enabled(&sk_true.key) == true);
  		WARN_ON(static_key_enabled(&sk_false.key) == false);
  
  		WARN_ON(static_branch_likely(&sk_true));
  		WARN_ON(static_branch_unlikely(&sk_true));
  		WARN_ON(!static_branch_likely(&sk_false));
  		WARN_ON(!static_branch_unlikely(&sk_false));
  
  		static_branch_enable(&sk_true);
  		static_branch_disable(&sk_false);
  	}
  
  	return 0;
  }
92ee46efe   Jason Baron   jump_label: Invok...
744
  early_initcall(jump_label_test);
1987c947d   Peter Zijlstra   locking/static_ke...
745
746
747
  #endif /* STATIC_KEYS_SELFTEST */
  
  #endif /* HAVE_JUMP_LABEL */