Blame view

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

1f69bf9c6   Jason Baron   jump_label: remov...
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
  /*
   * 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);
  
  void static_key_enable(struct static_key *key)
  {
  	int count = static_key_count(key);
  
  	WARN_ON_ONCE(count < 0 || count > 1);
  
  	if (!count)
  		static_key_slow_inc(key);
  }
  EXPORT_SYMBOL_GPL(static_key_enable);
  
  void static_key_disable(struct static_key *key)
  {
  	int count = static_key_count(key);
  
  	WARN_ON_ONCE(count < 0 || count > 1);
  
  	if (count)
  		static_key_slow_dec(key);
  }
  EXPORT_SYMBOL_GPL(static_key_disable);
c5905afb0   Ingo Molnar   static keys: Intr...
98
  void static_key_slow_inc(struct static_key *key)
bf5438fca   Jason Baron   jump label: Base ...
99
  {
4c5ea0a9c   Paolo Bonzini   locking/static_ke...
100
  	int v, v1;
c4b2c0c5f   Hannes Frederic Sowa   static_key: WARN ...
101
  	STATIC_KEY_CHECK_USE();
4c5ea0a9c   Paolo Bonzini   locking/static_ke...
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
  
  	/*
  	 * 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);
  		if (likely(v1 == v))
  			return;
  	}
bf5438fca   Jason Baron   jump label: Base ...
120

d430d3d7e   Jason Baron   jump label: Intro...
121
  	jump_label_lock();
4c5ea0a9c   Paolo Bonzini   locking/static_ke...
122
123
  	if (atomic_read(&key->enabled) == 0) {
  		atomic_set(&key->enabled, -1);
706249c22   Peter Zijlstra   locking/static_ke...
124
  		jump_label_update(key);
4c5ea0a9c   Paolo Bonzini   locking/static_ke...
125
126
127
128
  		atomic_set(&key->enabled, 1);
  	} else {
  		atomic_inc(&key->enabled);
  	}
d430d3d7e   Jason Baron   jump label: Intro...
129
  	jump_label_unlock();
bf5438fca   Jason Baron   jump label: Base ...
130
  }
c5905afb0   Ingo Molnar   static keys: Intr...
131
  EXPORT_SYMBOL_GPL(static_key_slow_inc);
bf5438fca   Jason Baron   jump label: Base ...
132

c5905afb0   Ingo Molnar   static keys: Intr...
133
  static void __static_key_slow_dec(struct static_key *key,
b20295207   Gleb Natapov   perf, core: Rate ...
134
  		unsigned long rate_limit, struct delayed_work *work)
bf5438fca   Jason Baron   jump label: Base ...
135
  {
4c5ea0a9c   Paolo Bonzini   locking/static_ke...
136
137
138
139
140
141
142
  	/*
  	 * 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...
143
144
145
146
  	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...
147
  		return;
fadf0464b   Jason Baron   jump label: Add a...
148
  	}
bf5438fca   Jason Baron   jump label: Base ...
149

b20295207   Gleb Natapov   perf, core: Rate ...
150
151
152
  	if (rate_limit) {
  		atomic_inc(&key->enabled);
  		schedule_delayed_work(work, rate_limit);
c5905afb0   Ingo Molnar   static keys: Intr...
153
  	} else {
706249c22   Peter Zijlstra   locking/static_ke...
154
  		jump_label_update(key);
c5905afb0   Ingo Molnar   static keys: Intr...
155
  	}
91bad2f8d   Jason Baron   jump label: Fix d...
156
  	jump_label_unlock();
bf5438fca   Jason Baron   jump label: Base ...
157
  }
b20295207   Gleb Natapov   perf, core: Rate ...
158
159
  static void jump_label_update_timeout(struct work_struct *work)
  {
c5905afb0   Ingo Molnar   static keys: Intr...
160
161
162
  	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 ...
163
  }
c5905afb0   Ingo Molnar   static keys: Intr...
164
  void static_key_slow_dec(struct static_key *key)
b20295207   Gleb Natapov   perf, core: Rate ...
165
  {
c4b2c0c5f   Hannes Frederic Sowa   static_key: WARN ...
166
  	STATIC_KEY_CHECK_USE();
c5905afb0   Ingo Molnar   static keys: Intr...
167
  	__static_key_slow_dec(key, 0, NULL);
b20295207   Gleb Natapov   perf, core: Rate ...
168
  }
c5905afb0   Ingo Molnar   static keys: Intr...
169
  EXPORT_SYMBOL_GPL(static_key_slow_dec);
b20295207   Gleb Natapov   perf, core: Rate ...
170

c5905afb0   Ingo Molnar   static keys: Intr...
171
  void static_key_slow_dec_deferred(struct static_key_deferred *key)
b20295207   Gleb Natapov   perf, core: Rate ...
172
  {
c4b2c0c5f   Hannes Frederic Sowa   static_key: WARN ...
173
  	STATIC_KEY_CHECK_USE();
c5905afb0   Ingo Molnar   static keys: Intr...
174
  	__static_key_slow_dec(&key->key, key->timeout, &key->work);
b20295207   Gleb Natapov   perf, core: Rate ...
175
  }
c5905afb0   Ingo Molnar   static keys: Intr...
176
  EXPORT_SYMBOL_GPL(static_key_slow_dec_deferred);
b20295207   Gleb Natapov   perf, core: Rate ...
177

483ecebb2   David Matlack   jump_labels: API ...
178
179
180
181
182
183
  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...
184
  void jump_label_rate_limit(struct static_key_deferred *key,
b20295207   Gleb Natapov   perf, core: Rate ...
185
186
  		unsigned long rl)
  {
c4b2c0c5f   Hannes Frederic Sowa   static_key: WARN ...
187
  	STATIC_KEY_CHECK_USE();
b20295207   Gleb Natapov   perf, core: Rate ...
188
189
190
  	key->timeout = rl;
  	INIT_DELAYED_WORK(&key->work, jump_label_update_timeout);
  }
a181dc14e   Gleb Natapov   jump_label: Expor...
191
  EXPORT_SYMBOL_GPL(jump_label_rate_limit);
b20295207   Gleb Natapov   perf, core: Rate ...
192

4c3ef6d79   Jason Baron   jump label: Add j...
193
194
195
196
197
198
199
200
  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...
201
202
  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...
203
  {
4c3ef6d79   Jason Baron   jump label: Add j...
204
  	struct jump_entry *iter;
4c3ef6d79   Jason Baron   jump label: Add j...
205

4c3ef6d79   Jason Baron   jump label: Add j...
206
207
  	iter = iter_start;
  	while (iter < iter_stop) {
d430d3d7e   Jason Baron   jump label: Intro...
208
209
  		if (addr_conflict(iter, start, end))
  			return 1;
4c3ef6d79   Jason Baron   jump label: Add j...
210
211
  		iter++;
  	}
d430d3d7e   Jason Baron   jump label: Intro...
212
213
  	return 0;
  }
706249c22   Peter Zijlstra   locking/static_ke...
214
  /*
20284aa77   Jeremy Fitzhardinge   jump_label: add a...
215
216
217
218
219
   * 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: ...
220
  void __weak __init_or_module arch_jump_label_transform_static(struct jump_entry *entry,
20284aa77   Jeremy Fitzhardinge   jump_label: add a...
221
222
  					    enum jump_label_type type)
  {
706249c22   Peter Zijlstra   locking/static_ke...
223
  	arch_jump_label_transform(entry, type);
20284aa77   Jeremy Fitzhardinge   jump_label: add a...
224
  }
706249c22   Peter Zijlstra   locking/static_ke...
225
  static inline struct jump_entry *static_key_entries(struct static_key *key)
d430d3d7e   Jason Baron   jump label: Intro...
226
  {
706249c22   Peter Zijlstra   locking/static_ke...
227
  	return (struct jump_entry *)((unsigned long)key->entries & ~JUMP_TYPE_MASK);
4c3ef6d79   Jason Baron   jump label: Add j...
228
  }
706249c22   Peter Zijlstra   locking/static_ke...
229
  static inline bool static_key_type(struct static_key *key)
c5905afb0   Ingo Molnar   static keys: Intr...
230
  {
706249c22   Peter Zijlstra   locking/static_ke...
231
  	return (unsigned long)key->entries & JUMP_TYPE_MASK;
a1efb01fe   Peter Zijlstra   jump_label, locki...
232
  }
c5905afb0   Ingo Molnar   static keys: Intr...
233

7dcfd915b   Peter Zijlstra   jump_label: Add j...
234
235
  static inline struct static_key *jump_entry_key(struct jump_entry *entry)
  {
11276d530   Peter Zijlstra   locking/static_ke...
236
237
238
239
240
241
  	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...
242
  }
706249c22   Peter Zijlstra   locking/static_ke...
243
  static enum jump_label_type jump_label_type(struct jump_entry *entry)
a1efb01fe   Peter Zijlstra   jump_label, locki...
244
  {
706249c22   Peter Zijlstra   locking/static_ke...
245
  	struct static_key *key = jump_entry_key(entry);
a1efb01fe   Peter Zijlstra   jump_label, locki...
246
  	bool enabled = static_key_enabled(key);
11276d530   Peter Zijlstra   locking/static_ke...
247
  	bool branch = jump_entry_branch(entry);
c5905afb0   Ingo Molnar   static keys: Intr...
248

11276d530   Peter Zijlstra   locking/static_ke...
249
250
  	/* See the comment in linux/jump_label.h */
  	return enabled ^ branch;
c5905afb0   Ingo Molnar   static keys: Intr...
251
  }
706249c22   Peter Zijlstra   locking/static_ke...
252
253
254
255
256
257
258
259
260
261
262
263
264
265
  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...
266
  void __init jump_label_init(void)
bf5438fca   Jason Baron   jump label: Base ...
267
  {
bf5438fca   Jason Baron   jump label: Base ...
268
269
  	struct jump_entry *iter_start = __start___jump_table;
  	struct jump_entry *iter_stop = __stop___jump_table;
c5905afb0   Ingo Molnar   static keys: Intr...
270
  	struct static_key *key = NULL;
bf5438fca   Jason Baron   jump label: Base ...
271
  	struct jump_entry *iter;
1f69bf9c6   Jason Baron   jump_label: remov...
272
273
274
275
276
277
278
279
  	/*
  	 * 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 ...
280
281
  	if (static_key_initialized)
  		return;
91bad2f8d   Jason Baron   jump label: Fix d...
282
  	jump_label_lock();
d430d3d7e   Jason Baron   jump label: Intro...
283
284
285
  	jump_label_sort_entries(iter_start, iter_stop);
  
  	for (iter = iter_start; iter < iter_stop; iter++) {
c5905afb0   Ingo Molnar   static keys: Intr...
286
  		struct static_key *iterk;
37348804e   Jeremy Fitzhardinge   jump_label: if a ...
287

11276d530   Peter Zijlstra   locking/static_ke...
288
289
290
  		/* 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...
291
  		iterk = jump_entry_key(iter);
37348804e   Jeremy Fitzhardinge   jump_label: if a ...
292
  		if (iterk == key)
d430d3d7e   Jason Baron   jump label: Intro...
293
  			continue;
37348804e   Jeremy Fitzhardinge   jump_label: if a ...
294
  		key = iterk;
c5905afb0   Ingo Molnar   static keys: Intr...
295
296
297
298
  		/*
  		 * Set key->entries to iter, but preserve JUMP_LABEL_TRUE_BRANCH.
  		 */
  		*((unsigned long *)&key->entries) += (unsigned long)iter;
d430d3d7e   Jason Baron   jump label: Intro...
299
300
301
  #ifdef CONFIG_MODULES
  		key->next = NULL;
  #endif
bf5438fca   Jason Baron   jump label: Base ...
302
  	}
c4b2c0c5f   Hannes Frederic Sowa   static_key: WARN ...
303
  	static_key_initialized = true;
91bad2f8d   Jason Baron   jump label: Fix d...
304
  	jump_label_unlock();
bf5438fca   Jason Baron   jump label: Base ...
305
  }
bf5438fca   Jason Baron   jump label: Base ...
306
307
  
  #ifdef CONFIG_MODULES
11276d530   Peter Zijlstra   locking/static_ke...
308
309
310
311
312
313
314
315
316
  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...
317
318
  struct static_key_mod {
  	struct static_key_mod *next;
d430d3d7e   Jason Baron   jump label: Intro...
319
320
321
322
323
324
325
  	struct jump_entry *entries;
  	struct module *mod;
  };
  
  static int __jump_label_mod_text_reserved(void *start, void *end)
  {
  	struct module *mod;
bdc9f3735   Rusty Russell   jump_label: disab...
326
  	preempt_disable();
d430d3d7e   Jason Baron   jump label: Intro...
327
  	mod = __module_text_address((unsigned long)start);
bdc9f3735   Rusty Russell   jump_label: disab...
328
329
  	WARN_ON_ONCE(__module_text_address((unsigned long)end) != mod);
  	preempt_enable();
d430d3d7e   Jason Baron   jump label: Intro...
330
331
  	if (!mod)
  		return 0;
d430d3d7e   Jason Baron   jump label: Intro...
332
333
334
335
336
  
  	return __jump_label_text_reserved(mod->jump_entries,
  				mod->jump_entries + mod->num_jump_entries,
  				start, end);
  }
706249c22   Peter Zijlstra   locking/static_ke...
337
  static void __jump_label_mod_update(struct static_key *key)
d430d3d7e   Jason Baron   jump label: Intro...
338
  {
706249c22   Peter Zijlstra   locking/static_ke...
339
  	struct static_key_mod *mod;
d430d3d7e   Jason Baron   jump label: Intro...
340

706249c22   Peter Zijlstra   locking/static_ke...
341
  	for (mod = key->next; mod; mod = mod->next) {
7cbc5b8d4   Jiri Olsa   jump_label: Check...
342
343
344
  		struct module *m = mod->mod;
  
  		__jump_label_update(key, mod->entries,
706249c22   Peter Zijlstra   locking/static_ke...
345
  				    m->jump_entries + m->num_jump_entries);
d430d3d7e   Jason Baron   jump label: Intro...
346
347
348
349
350
351
352
353
354
355
356
357
  	}
  }
  
  /***
   * 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 ...
358
  {
d430d3d7e   Jason Baron   jump label: Intro...
359
360
361
362
363
364
365
  	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...
366
367
368
369
370
  	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 ...
371
  }
d430d3d7e   Jason Baron   jump label: Intro...
372
  static int jump_label_add_module(struct module *mod)
bf5438fca   Jason Baron   jump label: Base ...
373
  {
d430d3d7e   Jason Baron   jump label: Intro...
374
375
376
  	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...
377
378
  	struct static_key *key = NULL;
  	struct static_key_mod *jlm;
bf5438fca   Jason Baron   jump label: Base ...
379
380
  
  	/* if the module doesn't have jump label entries, just return */
d430d3d7e   Jason Baron   jump label: Intro...
381
  	if (iter_start == iter_stop)
bf5438fca   Jason Baron   jump label: Base ...
382
  		return 0;
d430d3d7e   Jason Baron   jump label: Intro...
383
384
385
  	jump_label_sort_entries(iter_start, iter_stop);
  
  	for (iter = iter_start; iter < iter_stop; iter++) {
c5905afb0   Ingo Molnar   static keys: Intr...
386
  		struct static_key *iterk;
d430d3d7e   Jason Baron   jump label: Intro...
387

7dcfd915b   Peter Zijlstra   jump_label: Add j...
388
  		iterk = jump_entry_key(iter);
c5905afb0   Ingo Molnar   static keys: Intr...
389
390
  		if (iterk == key)
  			continue;
d430d3d7e   Jason Baron   jump label: Intro...
391

c5905afb0   Ingo Molnar   static keys: Intr...
392
  		key = iterk;
bed831f9a   Peter Zijlstra   module, jump_labe...
393
  		if (within_module(iter->key, mod)) {
c5905afb0   Ingo Molnar   static keys: Intr...
394
395
396
397
  			/*
  			 * Set key->entries to iter, but preserve JUMP_LABEL_TRUE_BRANCH.
  			 */
  			*((unsigned long *)&key->entries) += (unsigned long)iter;
d430d3d7e   Jason Baron   jump label: Intro...
398
399
  			key->next = NULL;
  			continue;
bf5438fca   Jason Baron   jump label: Base ...
400
  		}
c5905afb0   Ingo Molnar   static keys: Intr...
401
  		jlm = kzalloc(sizeof(struct static_key_mod), GFP_KERNEL);
d430d3d7e   Jason Baron   jump label: Intro...
402
403
  		if (!jlm)
  			return -ENOMEM;
d430d3d7e   Jason Baron   jump label: Intro...
404
405
406
407
  		jlm->mod = mod;
  		jlm->entries = iter;
  		jlm->next = key->next;
  		key->next = jlm;
11276d530   Peter Zijlstra   locking/static_ke...
408
409
  		/* 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...
410
  			__jump_label_update(key, iter, iter_stop);
bf5438fca   Jason Baron   jump label: Base ...
411
  	}
d430d3d7e   Jason Baron   jump label: Intro...
412

bf5438fca   Jason Baron   jump label: Base ...
413
414
  	return 0;
  }
d430d3d7e   Jason Baron   jump label: Intro...
415
  static void jump_label_del_module(struct module *mod)
bf5438fca   Jason Baron   jump label: Base ...
416
  {
d430d3d7e   Jason Baron   jump label: Intro...
417
418
419
  	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...
420
421
  	struct static_key *key = NULL;
  	struct static_key_mod *jlm, **prev;
bf5438fca   Jason Baron   jump label: Base ...
422

d430d3d7e   Jason Baron   jump label: Intro...
423
  	for (iter = iter_start; iter < iter_stop; iter++) {
7dcfd915b   Peter Zijlstra   jump_label: Add j...
424
  		if (jump_entry_key(iter) == key)
d430d3d7e   Jason Baron   jump label: Intro...
425
  			continue;
7dcfd915b   Peter Zijlstra   jump_label: Add j...
426
  		key = jump_entry_key(iter);
d430d3d7e   Jason Baron   jump label: Intro...
427

bed831f9a   Peter Zijlstra   module, jump_labe...
428
  		if (within_module(iter->key, mod))
d430d3d7e   Jason Baron   jump label: Intro...
429
430
431
432
  			continue;
  
  		prev = &key->next;
  		jlm = key->next;
bf5438fca   Jason Baron   jump label: Base ...
433

d430d3d7e   Jason Baron   jump label: Intro...
434
435
436
437
438
439
440
441
  		while (jlm && jlm->mod != mod) {
  			prev = &jlm->next;
  			jlm = jlm->next;
  		}
  
  		if (jlm) {
  			*prev = jlm->next;
  			kfree(jlm);
bf5438fca   Jason Baron   jump label: Base ...
442
443
444
  		}
  	}
  }
d430d3d7e   Jason Baron   jump label: Intro...
445
  static void jump_label_invalidate_module_init(struct module *mod)
b842f8faf   Jason Baron   jump label: Fix m...
446
  {
d430d3d7e   Jason Baron   jump label: Intro...
447
448
  	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...
449
  	struct jump_entry *iter;
b842f8faf   Jason Baron   jump label: Fix m...
450

d430d3d7e   Jason Baron   jump label: Intro...
451
452
453
  	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...
454
455
  	}
  }
bf5438fca   Jason Baron   jump label: Base ...
456
457
458
459
460
461
462
463
464
  static int
  jump_label_module_notify(struct notifier_block *self, unsigned long val,
  			 void *data)
  {
  	struct module *mod = data;
  	int ret = 0;
  
  	switch (val) {
  	case MODULE_STATE_COMING:
91bad2f8d   Jason Baron   jump label: Fix d...
465
  		jump_label_lock();
d430d3d7e   Jason Baron   jump label: Intro...
466
  		ret = jump_label_add_module(mod);
bf5438fca   Jason Baron   jump label: Base ...
467
  		if (ret)
d430d3d7e   Jason Baron   jump label: Intro...
468
  			jump_label_del_module(mod);
91bad2f8d   Jason Baron   jump label: Fix d...
469
  		jump_label_unlock();
bf5438fca   Jason Baron   jump label: Base ...
470
471
  		break;
  	case MODULE_STATE_GOING:
91bad2f8d   Jason Baron   jump label: Fix d...
472
  		jump_label_lock();
d430d3d7e   Jason Baron   jump label: Intro...
473
  		jump_label_del_module(mod);
91bad2f8d   Jason Baron   jump label: Fix d...
474
  		jump_label_unlock();
bf5438fca   Jason Baron   jump label: Base ...
475
  		break;
b842f8faf   Jason Baron   jump label: Fix m...
476
  	case MODULE_STATE_LIVE:
91bad2f8d   Jason Baron   jump label: Fix d...
477
  		jump_label_lock();
d430d3d7e   Jason Baron   jump label: Intro...
478
  		jump_label_invalidate_module_init(mod);
91bad2f8d   Jason Baron   jump label: Fix d...
479
  		jump_label_unlock();
b842f8faf   Jason Baron   jump label: Fix m...
480
  		break;
bf5438fca   Jason Baron   jump label: Base ...
481
  	}
bf5438fca   Jason Baron   jump label: Base ...
482

d430d3d7e   Jason Baron   jump label: Intro...
483
  	return notifier_from_errno(ret);
bf5438fca   Jason Baron   jump label: Base ...
484
  }
885885f6b   Wei Yongjun   locking/static_ke...
485
  static struct notifier_block jump_label_module_nb = {
bf5438fca   Jason Baron   jump label: Base ...
486
  	.notifier_call = jump_label_module_notify,
d430d3d7e   Jason Baron   jump label: Intro...
487
  	.priority = 1, /* higher than tracepoints */
bf5438fca   Jason Baron   jump label: Base ...
488
  };
d430d3d7e   Jason Baron   jump label: Intro...
489
  static __init int jump_label_init_module(void)
bf5438fca   Jason Baron   jump label: Base ...
490
491
492
  {
  	return register_module_notifier(&jump_label_module_nb);
  }
d430d3d7e   Jason Baron   jump label: Intro...
493
  early_initcall(jump_label_init_module);
bf5438fca   Jason Baron   jump label: Base ...
494
495
  
  #endif /* CONFIG_MODULES */
d430d3d7e   Jason Baron   jump label: Intro...
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
  /***
   * 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...
522
  static void jump_label_update(struct static_key *key)
d430d3d7e   Jason Baron   jump label: Intro...
523
  {
c5905afb0   Ingo Molnar   static keys: Intr...
524
  	struct jump_entry *stop = __stop___jump_table;
a1efb01fe   Peter Zijlstra   jump_label, locki...
525
  	struct jump_entry *entry = static_key_entries(key);
d430d3d7e   Jason Baron   jump label: Intro...
526
  #ifdef CONFIG_MODULES
bed831f9a   Peter Zijlstra   module, jump_labe...
527
  	struct module *mod;
140fe3b1a   Xiao Guangrong   jump_label: Fix j...
528

706249c22   Peter Zijlstra   locking/static_ke...
529
  	__jump_label_mod_update(key);
140fe3b1a   Xiao Guangrong   jump_label: Fix j...
530

bed831f9a   Peter Zijlstra   module, jump_labe...
531
532
  	preempt_disable();
  	mod = __module_address((unsigned long)key);
140fe3b1a   Xiao Guangrong   jump_label: Fix j...
533
534
  	if (mod)
  		stop = mod->jump_entries + mod->num_jump_entries;
bed831f9a   Peter Zijlstra   module, jump_labe...
535
  	preempt_enable();
d430d3d7e   Jason Baron   jump label: Intro...
536
  #endif
140fe3b1a   Xiao Guangrong   jump_label: Fix j...
537
538
  	/* if there are no users, entry can be NULL */
  	if (entry)
706249c22   Peter Zijlstra   locking/static_ke...
539
  		__jump_label_update(key, entry, stop);
d430d3d7e   Jason Baron   jump label: Intro...
540
  }
1987c947d   Peter Zijlstra   locking/static_ke...
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
  #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;
  }
  late_initcall(jump_label_test);
  #endif /* STATIC_KEYS_SELFTEST */
  
  #endif /* HAVE_JUMP_LABEL */