Blame view

kernel/jump_label.c 19.2 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>
bf5438fca   Jason Baron   jump label: Base ...
19
20
  
  #ifdef HAVE_JUMP_LABEL
bf5438fca   Jason Baron   jump label: Base ...
21
22
  /* mutex to protect coming/going of the the jump_label table */
  static DEFINE_MUTEX(jump_label_mutex);
91bad2f8d   Jason Baron   jump label: Fix d...
23
24
25
26
27
28
29
30
31
  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 ...
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
  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...
47
  jump_label_sort_entries(struct jump_entry *start, struct jump_entry *stop)
bf5438fca   Jason Baron   jump label: Base ...
48
49
50
51
52
53
54
  {
  	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...
55
  static void jump_label_update(struct static_key *key);
a1efb01fe   Peter Zijlstra   jump_label, locki...
56

1f69bf9c6   Jason Baron   jump_label: remov...
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
  /*
   * 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);
8b7b41280   Marc Zyngier   jump_label: Split...
77
  static void static_key_slow_inc_cpuslocked(struct static_key *key)
bf5438fca   Jason Baron   jump label: Base ...
78
  {
4c5ea0a9c   Paolo Bonzini   locking/static_ke...
79
  	int v, v1;
c4b2c0c5f   Hannes Frederic Sowa   static_key: WARN ...
80
  	STATIC_KEY_CHECK_USE();
4c5ea0a9c   Paolo Bonzini   locking/static_ke...
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
  
  	/*
  	 * 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...
96
  		if (likely(v1 == v))
4c5ea0a9c   Paolo Bonzini   locking/static_ke...
97
98
  			return;
  	}
bf5438fca   Jason Baron   jump label: Base ...
99

d430d3d7e   Jason Baron   jump label: Intro...
100
  	jump_label_lock();
4c5ea0a9c   Paolo Bonzini   locking/static_ke...
101
102
  	if (atomic_read(&key->enabled) == 0) {
  		atomic_set(&key->enabled, -1);
706249c22   Peter Zijlstra   locking/static_ke...
103
  		jump_label_update(key);
d0646a6f5   Peter Zijlstra   jump_label: Add R...
104
105
106
107
108
  		/*
  		 * 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...
109
110
111
  	} else {
  		atomic_inc(&key->enabled);
  	}
d430d3d7e   Jason Baron   jump label: Intro...
112
  	jump_label_unlock();
8b7b41280   Marc Zyngier   jump_label: Split...
113
114
115
116
117
118
  }
  
  void static_key_slow_inc(struct static_key *key)
  {
  	cpus_read_lock();
  	static_key_slow_inc_cpuslocked(key);
f2545b2d4   Thomas Gleixner   jump_label: Reord...
119
  	cpus_read_unlock();
bf5438fca   Jason Baron   jump label: Base ...
120
  }
c5905afb0   Ingo Molnar   static keys: Intr...
121
  EXPORT_SYMBOL_GPL(static_key_slow_inc);
bf5438fca   Jason Baron   jump label: Base ...
122

5a40527f8   Marc Zyngier   jump_label: Provi...
123
  void static_key_enable_cpuslocked(struct static_key *key)
1dbb6704d   Paolo Bonzini   jump_label: Fix c...
124
125
  {
  	STATIC_KEY_CHECK_USE();
5a40527f8   Marc Zyngier   jump_label: Provi...
126

1dbb6704d   Paolo Bonzini   jump_label: Fix c...
127
128
129
130
  	if (atomic_read(&key->enabled) > 0) {
  		WARN_ON_ONCE(atomic_read(&key->enabled) != 1);
  		return;
  	}
1dbb6704d   Paolo Bonzini   jump_label: Fix c...
131
132
133
134
  	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...
135
136
137
138
  		/*
  		 * See static_key_slow_inc().
  		 */
  		atomic_set_release(&key->enabled, 1);
1dbb6704d   Paolo Bonzini   jump_label: Fix c...
139
140
  	}
  	jump_label_unlock();
5a40527f8   Marc Zyngier   jump_label: Provi...
141
142
143
144
145
146
147
  }
  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...
148
149
150
  	cpus_read_unlock();
  }
  EXPORT_SYMBOL_GPL(static_key_enable);
5a40527f8   Marc Zyngier   jump_label: Provi...
151
  void static_key_disable_cpuslocked(struct static_key *key)
1dbb6704d   Paolo Bonzini   jump_label: Fix c...
152
153
  {
  	STATIC_KEY_CHECK_USE();
5a40527f8   Marc Zyngier   jump_label: Provi...
154

1dbb6704d   Paolo Bonzini   jump_label: Fix c...
155
156
157
158
  	if (atomic_read(&key->enabled) != 1) {
  		WARN_ON_ONCE(atomic_read(&key->enabled) != 0);
  		return;
  	}
1dbb6704d   Paolo Bonzini   jump_label: Fix c...
159
160
161
162
  	jump_label_lock();
  	if (atomic_cmpxchg(&key->enabled, 1, 0))
  		jump_label_update(key);
  	jump_label_unlock();
5a40527f8   Marc Zyngier   jump_label: Provi...
163
164
165
166
167
168
169
  }
  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...
170
171
172
  	cpus_read_unlock();
  }
  EXPORT_SYMBOL_GPL(static_key_disable);
8b7b41280   Marc Zyngier   jump_label: Split...
173
174
175
  static void static_key_slow_dec_cpuslocked(struct static_key *key,
  					   unsigned long rate_limit,
  					   struct delayed_work *work)
bf5438fca   Jason Baron   jump label: Base ...
176
  {
4c5ea0a9c   Paolo Bonzini   locking/static_ke...
177
178
179
180
181
182
183
  	/*
  	 * 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...
184
185
186
187
  	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...
188
  		return;
fadf0464b   Jason Baron   jump label: Add a...
189
  	}
bf5438fca   Jason Baron   jump label: Base ...
190

b20295207   Gleb Natapov   perf, core: Rate ...
191
192
193
  	if (rate_limit) {
  		atomic_inc(&key->enabled);
  		schedule_delayed_work(work, rate_limit);
c5905afb0   Ingo Molnar   static keys: Intr...
194
  	} else {
706249c22   Peter Zijlstra   locking/static_ke...
195
  		jump_label_update(key);
c5905afb0   Ingo Molnar   static keys: Intr...
196
  	}
91bad2f8d   Jason Baron   jump label: Fix d...
197
  	jump_label_unlock();
8b7b41280   Marc Zyngier   jump_label: Split...
198
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();
  	static_key_slow_dec_cpuslocked(key, rate_limit, work);
f2545b2d4   Thomas Gleixner   jump_label: Reord...
206
  	cpus_read_unlock();
bf5438fca   Jason Baron   jump label: Base ...
207
  }
b20295207   Gleb Natapov   perf, core: Rate ...
208
209
  static void jump_label_update_timeout(struct work_struct *work)
  {
c5905afb0   Ingo Molnar   static keys: Intr...
210
211
212
  	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 ...
213
  }
c5905afb0   Ingo Molnar   static keys: Intr...
214
  void static_key_slow_dec(struct static_key *key)
b20295207   Gleb Natapov   perf, core: Rate ...
215
  {
c4b2c0c5f   Hannes Frederic Sowa   static_key: WARN ...
216
  	STATIC_KEY_CHECK_USE();
c5905afb0   Ingo Molnar   static keys: Intr...
217
  	__static_key_slow_dec(key, 0, NULL);
b20295207   Gleb Natapov   perf, core: Rate ...
218
  }
c5905afb0   Ingo Molnar   static keys: Intr...
219
  EXPORT_SYMBOL_GPL(static_key_slow_dec);
b20295207   Gleb Natapov   perf, core: Rate ...
220

c5905afb0   Ingo Molnar   static keys: Intr...
221
  void static_key_slow_dec_deferred(struct static_key_deferred *key)
b20295207   Gleb Natapov   perf, core: Rate ...
222
  {
c4b2c0c5f   Hannes Frederic Sowa   static_key: WARN ...
223
  	STATIC_KEY_CHECK_USE();
c5905afb0   Ingo Molnar   static keys: Intr...
224
  	__static_key_slow_dec(&key->key, key->timeout, &key->work);
b20295207   Gleb Natapov   perf, core: Rate ...
225
  }
c5905afb0   Ingo Molnar   static keys: Intr...
226
  EXPORT_SYMBOL_GPL(static_key_slow_dec_deferred);
b20295207   Gleb Natapov   perf, core: Rate ...
227

b6416e610   David Matlack   jump_labels: API ...
228
229
230
231
232
233
  void static_key_deferred_flush(struct static_key_deferred *key)
  {
  	STATIC_KEY_CHECK_USE();
  	flush_delayed_work(&key->work);
  }
  EXPORT_SYMBOL_GPL(static_key_deferred_flush);
c5905afb0   Ingo Molnar   static keys: Intr...
234
  void jump_label_rate_limit(struct static_key_deferred *key,
b20295207   Gleb Natapov   perf, core: Rate ...
235
236
  		unsigned long rl)
  {
c4b2c0c5f   Hannes Frederic Sowa   static_key: WARN ...
237
  	STATIC_KEY_CHECK_USE();
b20295207   Gleb Natapov   perf, core: Rate ...
238
239
240
  	key->timeout = rl;
  	INIT_DELAYED_WORK(&key->work, jump_label_update_timeout);
  }
a181dc14e   Gleb Natapov   jump_label: Expor...
241
  EXPORT_SYMBOL_GPL(jump_label_rate_limit);
b20295207   Gleb Natapov   perf, core: Rate ...
242

4c3ef6d79   Jason Baron   jump label: Add j...
243
244
245
246
247
248
249
250
  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...
251
252
  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...
253
  {
4c3ef6d79   Jason Baron   jump label: Add j...
254
  	struct jump_entry *iter;
4c3ef6d79   Jason Baron   jump label: Add j...
255

4c3ef6d79   Jason Baron   jump label: Add j...
256
257
  	iter = iter_start;
  	while (iter < iter_stop) {
d430d3d7e   Jason Baron   jump label: Intro...
258
259
  		if (addr_conflict(iter, start, end))
  			return 1;
4c3ef6d79   Jason Baron   jump label: Add j...
260
261
  		iter++;
  	}
d430d3d7e   Jason Baron   jump label: Intro...
262
263
  	return 0;
  }
706249c22   Peter Zijlstra   locking/static_ke...
264
  /*
20284aa77   Jeremy Fitzhardinge   jump_label: add a...
265
266
267
268
269
   * 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: ...
270
  void __weak __init_or_module arch_jump_label_transform_static(struct jump_entry *entry,
20284aa77   Jeremy Fitzhardinge   jump_label: add a...
271
272
  					    enum jump_label_type type)
  {
706249c22   Peter Zijlstra   locking/static_ke...
273
  	arch_jump_label_transform(entry, type);
20284aa77   Jeremy Fitzhardinge   jump_label: add a...
274
  }
706249c22   Peter Zijlstra   locking/static_ke...
275
  static inline struct jump_entry *static_key_entries(struct static_key *key)
d430d3d7e   Jason Baron   jump label: Intro...
276
  {
3821fd35b   Jason Baron   jump_label: Reduc...
277
278
  	WARN_ON_ONCE(key->type & JUMP_TYPE_LINKED);
  	return (struct jump_entry *)(key->type & ~JUMP_TYPE_MASK);
4c3ef6d79   Jason Baron   jump label: Add j...
279
  }
706249c22   Peter Zijlstra   locking/static_ke...
280
  static inline bool static_key_type(struct static_key *key)
c5905afb0   Ingo Molnar   static keys: Intr...
281
  {
3821fd35b   Jason Baron   jump_label: Reduc...
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
  	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...
298
  }
c5905afb0   Ingo Molnar   static keys: Intr...
299

7dcfd915b   Peter Zijlstra   jump_label: Add j...
300
301
  static inline struct static_key *jump_entry_key(struct jump_entry *entry)
  {
11276d530   Peter Zijlstra   locking/static_ke...
302
303
304
305
306
307
  	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...
308
  }
3821fd35b   Jason Baron   jump_label: Reduc...
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
  /***
   * 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...
328
  static enum jump_label_type jump_label_type(struct jump_entry *entry)
a1efb01fe   Peter Zijlstra   jump_label, locki...
329
  {
706249c22   Peter Zijlstra   locking/static_ke...
330
  	struct static_key *key = jump_entry_key(entry);
a1efb01fe   Peter Zijlstra   jump_label, locki...
331
  	bool enabled = static_key_enabled(key);
11276d530   Peter Zijlstra   locking/static_ke...
332
  	bool branch = jump_entry_branch(entry);
c5905afb0   Ingo Molnar   static keys: Intr...
333

11276d530   Peter Zijlstra   locking/static_ke...
334
335
  	/* See the comment in linux/jump_label.h */
  	return enabled ^ branch;
c5905afb0   Ingo Molnar   static keys: Intr...
336
  }
706249c22   Peter Zijlstra   locking/static_ke...
337
338
339
340
341
342
343
344
345
346
347
348
349
350
  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++) {
  		/*
  		 * entry->code set to 0 invalidates module init text sections
  		 * kernel_text_address() verifies we are not in core kernel
  		 * init code, see jump_label_invalidate_module_init().
  		 */
  		if (entry->code && kernel_text_address(entry->code))
  			arch_jump_label_transform(entry, jump_label_type(entry));
  	}
  }
97ce2c88f   Jeremy Fitzhardinge   jump-label: initi...
351
  void __init jump_label_init(void)
bf5438fca   Jason Baron   jump label: Base ...
352
  {
bf5438fca   Jason Baron   jump label: Base ...
353
354
  	struct jump_entry *iter_start = __start___jump_table;
  	struct jump_entry *iter_stop = __stop___jump_table;
c5905afb0   Ingo Molnar   static keys: Intr...
355
  	struct static_key *key = NULL;
bf5438fca   Jason Baron   jump label: Base ...
356
  	struct jump_entry *iter;
1f69bf9c6   Jason Baron   jump_label: remov...
357
358
359
360
361
362
363
364
  	/*
  	 * 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 ...
365
366
  	if (static_key_initialized)
  		return;
f2545b2d4   Thomas Gleixner   jump_label: Reord...
367
  	cpus_read_lock();
91bad2f8d   Jason Baron   jump label: Fix d...
368
  	jump_label_lock();
d430d3d7e   Jason Baron   jump label: Intro...
369
370
371
  	jump_label_sort_entries(iter_start, iter_stop);
  
  	for (iter = iter_start; iter < iter_stop; iter++) {
c5905afb0   Ingo Molnar   static keys: Intr...
372
  		struct static_key *iterk;
37348804e   Jeremy Fitzhardinge   jump_label: if a ...
373

11276d530   Peter Zijlstra   locking/static_ke...
374
375
376
  		/* 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...
377
  		iterk = jump_entry_key(iter);
37348804e   Jeremy Fitzhardinge   jump_label: if a ...
378
  		if (iterk == key)
d430d3d7e   Jason Baron   jump label: Intro...
379
  			continue;
37348804e   Jeremy Fitzhardinge   jump_label: if a ...
380
  		key = iterk;
3821fd35b   Jason Baron   jump_label: Reduc...
381
  		static_key_set_entries(key, iter);
bf5438fca   Jason Baron   jump label: Base ...
382
  	}
c4b2c0c5f   Hannes Frederic Sowa   static_key: WARN ...
383
  	static_key_initialized = true;
91bad2f8d   Jason Baron   jump label: Fix d...
384
  	jump_label_unlock();
f2545b2d4   Thomas Gleixner   jump_label: Reord...
385
  	cpus_read_unlock();
bf5438fca   Jason Baron   jump label: Base ...
386
  }
bf5438fca   Jason Baron   jump label: Base ...
387
388
  
  #ifdef CONFIG_MODULES
11276d530   Peter Zijlstra   locking/static_ke...
389
390
391
392
393
394
395
396
397
  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...
398
399
  struct static_key_mod {
  	struct static_key_mod *next;
d430d3d7e   Jason Baron   jump label: Intro...
400
401
402
  	struct jump_entry *entries;
  	struct module *mod;
  };
3821fd35b   Jason Baron   jump_label: Reduc...
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
  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...
425
426
427
  static int __jump_label_mod_text_reserved(void *start, void *end)
  {
  	struct module *mod;
bdc9f3735   Rusty Russell   jump_label: disab...
428
  	preempt_disable();
d430d3d7e   Jason Baron   jump label: Intro...
429
  	mod = __module_text_address((unsigned long)start);
bdc9f3735   Rusty Russell   jump_label: disab...
430
431
  	WARN_ON_ONCE(__module_text_address((unsigned long)end) != mod);
  	preempt_enable();
d430d3d7e   Jason Baron   jump label: Intro...
432
433
  	if (!mod)
  		return 0;
d430d3d7e   Jason Baron   jump label: Intro...
434
435
436
437
438
  
  	return __jump_label_text_reserved(mod->jump_entries,
  				mod->jump_entries + mod->num_jump_entries,
  				start, end);
  }
706249c22   Peter Zijlstra   locking/static_ke...
439
  static void __jump_label_mod_update(struct static_key *key)
d430d3d7e   Jason Baron   jump label: Intro...
440
  {
706249c22   Peter Zijlstra   locking/static_ke...
441
  	struct static_key_mod *mod;
d430d3d7e   Jason Baron   jump label: Intro...
442

3821fd35b   Jason Baron   jump_label: Reduc...
443
444
445
446
447
448
449
450
451
452
  	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...
453

3821fd35b   Jason Baron   jump_label: Reduc...
454
455
456
457
458
459
  		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...
460
461
462
463
464
465
466
467
468
469
470
471
  	}
  }
  
  /***
   * 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 ...
472
  {
d430d3d7e   Jason Baron   jump label: Intro...
473
474
475
476
477
478
479
  	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...
480
481
482
483
484
  	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 ...
485
  }
d430d3d7e   Jason Baron   jump label: Intro...
486
  static int jump_label_add_module(struct module *mod)
bf5438fca   Jason Baron   jump label: Base ...
487
  {
d430d3d7e   Jason Baron   jump label: Intro...
488
489
490
  	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...
491
  	struct static_key *key = NULL;
3821fd35b   Jason Baron   jump_label: Reduc...
492
  	struct static_key_mod *jlm, *jlm2;
bf5438fca   Jason Baron   jump label: Base ...
493
494
  
  	/* if the module doesn't have jump label entries, just return */
d430d3d7e   Jason Baron   jump label: Intro...
495
  	if (iter_start == iter_stop)
bf5438fca   Jason Baron   jump label: Base ...
496
  		return 0;
d430d3d7e   Jason Baron   jump label: Intro...
497
498
499
  	jump_label_sort_entries(iter_start, iter_stop);
  
  	for (iter = iter_start; iter < iter_stop; iter++) {
c5905afb0   Ingo Molnar   static keys: Intr...
500
  		struct static_key *iterk;
d430d3d7e   Jason Baron   jump label: Intro...
501

7dcfd915b   Peter Zijlstra   jump_label: Add j...
502
  		iterk = jump_entry_key(iter);
c5905afb0   Ingo Molnar   static keys: Intr...
503
504
  		if (iterk == key)
  			continue;
d430d3d7e   Jason Baron   jump label: Intro...
505

c5905afb0   Ingo Molnar   static keys: Intr...
506
  		key = iterk;
bed831f9a   Peter Zijlstra   module, jump_labe...
507
  		if (within_module(iter->key, mod)) {
3821fd35b   Jason Baron   jump_label: Reduc...
508
  			static_key_set_entries(key, iter);
d430d3d7e   Jason Baron   jump label: Intro...
509
  			continue;
bf5438fca   Jason Baron   jump label: Base ...
510
  		}
c5905afb0   Ingo Molnar   static keys: Intr...
511
  		jlm = kzalloc(sizeof(struct static_key_mod), GFP_KERNEL);
d430d3d7e   Jason Baron   jump label: Intro...
512
513
  		if (!jlm)
  			return -ENOMEM;
3821fd35b   Jason Baron   jump_label: Reduc...
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
  		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...
529
530
  		jlm->mod = mod;
  		jlm->entries = iter;
3821fd35b   Jason Baron   jump_label: Reduc...
531
532
533
  		jlm->next = static_key_mod(key);
  		static_key_set_mod(key, jlm);
  		static_key_set_linked(key);
d430d3d7e   Jason Baron   jump label: Intro...
534

11276d530   Peter Zijlstra   locking/static_ke...
535
536
  		/* 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...
537
  			__jump_label_update(key, iter, iter_stop);
bf5438fca   Jason Baron   jump label: Base ...
538
  	}
d430d3d7e   Jason Baron   jump label: Intro...
539

bf5438fca   Jason Baron   jump label: Base ...
540
541
  	return 0;
  }
d430d3d7e   Jason Baron   jump label: Intro...
542
  static void jump_label_del_module(struct module *mod)
bf5438fca   Jason Baron   jump label: Base ...
543
  {
d430d3d7e   Jason Baron   jump label: Intro...
544
545
546
  	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...
547
548
  	struct static_key *key = NULL;
  	struct static_key_mod *jlm, **prev;
bf5438fca   Jason Baron   jump label: Base ...
549

d430d3d7e   Jason Baron   jump label: Intro...
550
  	for (iter = iter_start; iter < iter_stop; iter++) {
7dcfd915b   Peter Zijlstra   jump_label: Add j...
551
  		if (jump_entry_key(iter) == key)
d430d3d7e   Jason Baron   jump label: Intro...
552
  			continue;
7dcfd915b   Peter Zijlstra   jump_label: Add j...
553
  		key = jump_entry_key(iter);
d430d3d7e   Jason Baron   jump label: Intro...
554

bed831f9a   Peter Zijlstra   module, jump_labe...
555
  		if (within_module(iter->key, mod))
d430d3d7e   Jason Baron   jump label: Intro...
556
  			continue;
3821fd35b   Jason Baron   jump_label: Reduc...
557
558
559
  		/* No memory during module load */
  		if (WARN_ON(!static_key_linked(key)))
  			continue;
d430d3d7e   Jason Baron   jump label: Intro...
560
  		prev = &key->next;
3821fd35b   Jason Baron   jump_label: Reduc...
561
  		jlm = static_key_mod(key);
bf5438fca   Jason Baron   jump label: Base ...
562

d430d3d7e   Jason Baron   jump label: Intro...
563
564
565
566
  		while (jlm && jlm->mod != mod) {
  			prev = &jlm->next;
  			jlm = jlm->next;
  		}
3821fd35b   Jason Baron   jump_label: Reduc...
567
568
569
570
571
572
573
  		/* 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...
574
  			*prev = jlm->next;
3821fd35b   Jason Baron   jump_label: Reduc...
575
576
577
578
579
580
581
582
  
  		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...
583
  			kfree(jlm);
bf5438fca   Jason Baron   jump label: Base ...
584
585
586
  		}
  	}
  }
d430d3d7e   Jason Baron   jump label: Intro...
587
  static void jump_label_invalidate_module_init(struct module *mod)
b842f8faf   Jason Baron   jump label: Fix m...
588
  {
d430d3d7e   Jason Baron   jump label: Intro...
589
590
  	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...
591
  	struct jump_entry *iter;
b842f8faf   Jason Baron   jump label: Fix m...
592

d430d3d7e   Jason Baron   jump label: Intro...
593
594
595
  	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...
596
597
  	}
  }
bf5438fca   Jason Baron   jump label: Base ...
598
599
600
601
602
603
  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...
604
605
  	cpus_read_lock();
  	jump_label_lock();
bf5438fca   Jason Baron   jump label: Base ...
606
607
  	switch (val) {
  	case MODULE_STATE_COMING:
d430d3d7e   Jason Baron   jump label: Intro...
608
  		ret = jump_label_add_module(mod);
3821fd35b   Jason Baron   jump_label: Reduc...
609
610
611
  		if (ret) {
  			WARN(1, "Failed to allocatote memory: jump_label may not work properly.
  ");
d430d3d7e   Jason Baron   jump label: Intro...
612
  			jump_label_del_module(mod);
3821fd35b   Jason Baron   jump_label: Reduc...
613
  		}
bf5438fca   Jason Baron   jump label: Base ...
614
615
  		break;
  	case MODULE_STATE_GOING:
d430d3d7e   Jason Baron   jump label: Intro...
616
  		jump_label_del_module(mod);
bf5438fca   Jason Baron   jump label: Base ...
617
  		break;
b842f8faf   Jason Baron   jump label: Fix m...
618
  	case MODULE_STATE_LIVE:
d430d3d7e   Jason Baron   jump label: Intro...
619
  		jump_label_invalidate_module_init(mod);
b842f8faf   Jason Baron   jump label: Fix m...
620
  		break;
bf5438fca   Jason Baron   jump label: Base ...
621
  	}
bf5438fca   Jason Baron   jump label: Base ...
622

f2545b2d4   Thomas Gleixner   jump_label: Reord...
623
624
  	jump_label_unlock();
  	cpus_read_unlock();
d430d3d7e   Jason Baron   jump label: Intro...
625
  	return notifier_from_errno(ret);
bf5438fca   Jason Baron   jump label: Base ...
626
  }
885885f6b   Wei Yongjun   locking/static_ke...
627
  static struct notifier_block jump_label_module_nb = {
bf5438fca   Jason Baron   jump label: Base ...
628
  	.notifier_call = jump_label_module_notify,
d430d3d7e   Jason Baron   jump label: Intro...
629
  	.priority = 1, /* higher than tracepoints */
bf5438fca   Jason Baron   jump label: Base ...
630
  };
d430d3d7e   Jason Baron   jump label: Intro...
631
  static __init int jump_label_init_module(void)
bf5438fca   Jason Baron   jump label: Base ...
632
633
634
  {
  	return register_module_notifier(&jump_label_module_nb);
  }
d430d3d7e   Jason Baron   jump label: Intro...
635
  early_initcall(jump_label_init_module);
bf5438fca   Jason Baron   jump label: Base ...
636
637
  
  #endif /* CONFIG_MODULES */
d430d3d7e   Jason Baron   jump label: Intro...
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
  /***
   * 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...
664
  static void jump_label_update(struct static_key *key)
d430d3d7e   Jason Baron   jump label: Intro...
665
  {
c5905afb0   Ingo Molnar   static keys: Intr...
666
  	struct jump_entry *stop = __stop___jump_table;
3821fd35b   Jason Baron   jump_label: Reduc...
667
  	struct jump_entry *entry;
d430d3d7e   Jason Baron   jump label: Intro...
668
  #ifdef CONFIG_MODULES
bed831f9a   Peter Zijlstra   module, jump_labe...
669
  	struct module *mod;
140fe3b1a   Xiao Guangrong   jump_label: Fix j...
670

3821fd35b   Jason Baron   jump_label: Reduc...
671
672
673
674
  	if (static_key_linked(key)) {
  		__jump_label_mod_update(key);
  		return;
  	}
140fe3b1a   Xiao Guangrong   jump_label: Fix j...
675

bed831f9a   Peter Zijlstra   module, jump_labe...
676
677
  	preempt_disable();
  	mod = __module_address((unsigned long)key);
140fe3b1a   Xiao Guangrong   jump_label: Fix j...
678
679
  	if (mod)
  		stop = mod->jump_entries + mod->num_jump_entries;
bed831f9a   Peter Zijlstra   module, jump_labe...
680
  	preempt_enable();
d430d3d7e   Jason Baron   jump label: Intro...
681
  #endif
3821fd35b   Jason Baron   jump_label: Reduc...
682
  	entry = static_key_entries(key);
140fe3b1a   Xiao Guangrong   jump_label: Fix j...
683
684
  	/* if there are no users, entry can be NULL */
  	if (entry)
706249c22   Peter Zijlstra   locking/static_ke...
685
  		__jump_label_update(key, entry, stop);
d430d3d7e   Jason Baron   jump label: Intro...
686
  }
1987c947d   Peter Zijlstra   locking/static_ke...
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
  #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;
  }
2de359062   Jason Baron   jump_label: Invok...
721
  early_initcall(jump_label_test);
1987c947d   Peter Zijlstra   locking/static_ke...
722
723
724
  #endif /* STATIC_KEYS_SELFTEST */
  
  #endif /* HAVE_JUMP_LABEL */