sparc/jump_label: drop arch_jump_label_text_poke_early()
[linux-2.6-block.git] / kernel / jump_label.c
CommitLineData
bf5438fc
JB
1/*
2 * jump label support
3 *
4 * Copyright (C) 2009 Jason Baron <jbaron@redhat.com>
d430d3d7 5 * Copyright (C) 2011 Peter Zijlstra <pzijlstr@redhat.com>
bf5438fc
JB
6 *
7 */
bf5438fc
JB
8#include <linux/memory.h>
9#include <linux/uaccess.h>
10#include <linux/module.h>
11#include <linux/list.h>
bf5438fc
JB
12#include <linux/slab.h>
13#include <linux/sort.h>
14#include <linux/err.h>
d430d3d7 15#include <linux/jump_label.h>
bf5438fc
JB
16
17#ifdef HAVE_JUMP_LABEL
18
bf5438fc
JB
19/* mutex to protect coming/going of the the jump_label table */
20static DEFINE_MUTEX(jump_label_mutex);
21
91bad2f8
JB
22void jump_label_lock(void)
23{
24 mutex_lock(&jump_label_mutex);
25}
26
27void jump_label_unlock(void)
28{
29 mutex_unlock(&jump_label_mutex);
30}
31
d430d3d7
JB
32bool jump_label_enabled(struct jump_label_key *key)
33{
34 return !!atomic_read(&key->enabled);
35}
36
bf5438fc
JB
37static int jump_label_cmp(const void *a, const void *b)
38{
39 const struct jump_entry *jea = a;
40 const struct jump_entry *jeb = b;
41
42 if (jea->key < jeb->key)
43 return -1;
44
45 if (jea->key > jeb->key)
46 return 1;
47
48 return 0;
49}
50
51static void
d430d3d7 52jump_label_sort_entries(struct jump_entry *start, struct jump_entry *stop)
bf5438fc
JB
53{
54 unsigned long size;
55
56 size = (((unsigned long)stop - (unsigned long)start)
57 / sizeof(struct jump_entry));
58 sort(start, size, sizeof(struct jump_entry), jump_label_cmp, NULL);
59}
60
d430d3d7 61static void jump_label_update(struct jump_label_key *key, int enable);
bf5438fc 62
d430d3d7 63void jump_label_inc(struct jump_label_key *key)
bf5438fc 64{
d430d3d7
JB
65 if (atomic_inc_not_zero(&key->enabled))
66 return;
bf5438fc 67
d430d3d7
JB
68 jump_label_lock();
69 if (atomic_add_return(1, &key->enabled) == 1)
70 jump_label_update(key, JUMP_LABEL_ENABLE);
71 jump_label_unlock();
bf5438fc
JB
72}
73
d430d3d7 74void jump_label_dec(struct jump_label_key *key)
bf5438fc 75{
d430d3d7
JB
76 if (!atomic_dec_and_mutex_lock(&key->enabled, &jump_label_mutex))
77 return;
bf5438fc 78
d430d3d7 79 jump_label_update(key, JUMP_LABEL_DISABLE);
91bad2f8 80 jump_label_unlock();
bf5438fc
JB
81}
82
4c3ef6d7
JB
83static int addr_conflict(struct jump_entry *entry, void *start, void *end)
84{
85 if (entry->code <= (unsigned long)end &&
86 entry->code + JUMP_LABEL_NOP_SIZE > (unsigned long)start)
87 return 1;
88
89 return 0;
90}
91
d430d3d7
JB
92static int __jump_label_text_reserved(struct jump_entry *iter_start,
93 struct jump_entry *iter_stop, void *start, void *end)
4c3ef6d7 94{
4c3ef6d7 95 struct jump_entry *iter;
4c3ef6d7 96
4c3ef6d7
JB
97 iter = iter_start;
98 while (iter < iter_stop) {
d430d3d7
JB
99 if (addr_conflict(iter, start, end))
100 return 1;
4c3ef6d7
JB
101 iter++;
102 }
103
d430d3d7
JB
104 return 0;
105}
106
107static void __jump_label_update(struct jump_label_key *key,
7cbc5b8d
JO
108 struct jump_entry *entry,
109 struct jump_entry *stop, int enable)
d430d3d7 110{
7cbc5b8d
JO
111 for (; (entry < stop) &&
112 (entry->key == (jump_label_t)(unsigned long)key);
113 entry++) {
d430d3d7
JB
114 /*
115 * entry->code set to 0 invalidates module init text sections
116 * kernel_text_address() verifies we are not in core kernel
117 * init code, see jump_label_invalidate_module_init().
118 */
119 if (entry->code && kernel_text_address(entry->code))
120 arch_jump_label_transform(entry, enable);
121 }
4c3ef6d7
JB
122}
123
d430d3d7 124static __init int jump_label_init(void)
bf5438fc 125{
bf5438fc
JB
126 struct jump_entry *iter_start = __start___jump_table;
127 struct jump_entry *iter_stop = __stop___jump_table;
d430d3d7 128 struct jump_label_key *key = NULL;
bf5438fc
JB
129 struct jump_entry *iter;
130
91bad2f8 131 jump_label_lock();
d430d3d7
JB
132 jump_label_sort_entries(iter_start, iter_stop);
133
134 for (iter = iter_start; iter < iter_stop; iter++) {
37348804
JF
135 struct jump_label_key *iterk;
136
137 iterk = (struct jump_label_key *)(unsigned long)iter->key;
138 arch_jump_label_transform(iter, jump_label_enabled(iterk) ?
139 JUMP_LABEL_ENABLE : JUMP_LABEL_DISABLE);
140 if (iterk == key)
d430d3d7
JB
141 continue;
142
37348804 143 key = iterk;
d430d3d7
JB
144 key->entries = iter;
145#ifdef CONFIG_MODULES
146 key->next = NULL;
147#endif
bf5438fc 148 }
91bad2f8 149 jump_label_unlock();
d430d3d7
JB
150
151 return 0;
bf5438fc 152}
d430d3d7 153early_initcall(jump_label_init);
bf5438fc
JB
154
155#ifdef CONFIG_MODULES
156
d430d3d7
JB
157struct jump_label_mod {
158 struct jump_label_mod *next;
159 struct jump_entry *entries;
160 struct module *mod;
161};
162
163static int __jump_label_mod_text_reserved(void *start, void *end)
164{
165 struct module *mod;
166
167 mod = __module_text_address((unsigned long)start);
168 if (!mod)
169 return 0;
170
171 WARN_ON_ONCE(__module_text_address((unsigned long)end) != mod);
172
173 return __jump_label_text_reserved(mod->jump_entries,
174 mod->jump_entries + mod->num_jump_entries,
175 start, end);
176}
177
178static void __jump_label_mod_update(struct jump_label_key *key, int enable)
179{
180 struct jump_label_mod *mod = key->next;
181
182 while (mod) {
7cbc5b8d
JO
183 struct module *m = mod->mod;
184
185 __jump_label_update(key, mod->entries,
186 m->jump_entries + m->num_jump_entries,
187 enable);
d430d3d7
JB
188 mod = mod->next;
189 }
190}
191
192/***
193 * apply_jump_label_nops - patch module jump labels with arch_get_jump_label_nop()
194 * @mod: module to patch
195 *
196 * Allow for run-time selection of the optimal nops. Before the module
197 * loads patch these with arch_get_jump_label_nop(), which is specified by
198 * the arch specific jump label code.
199 */
200void jump_label_apply_nops(struct module *mod)
bf5438fc 201{
d430d3d7
JB
202 struct jump_entry *iter_start = mod->jump_entries;
203 struct jump_entry *iter_stop = iter_start + mod->num_jump_entries;
204 struct jump_entry *iter;
205
206 /* if the module doesn't have jump label entries, just return */
207 if (iter_start == iter_stop)
208 return;
209
210 for (iter = iter_start; iter < iter_stop; iter++)
37348804 211 arch_jump_label_transform(iter, JUMP_LABEL_DISABLE);
bf5438fc
JB
212}
213
d430d3d7 214static int jump_label_add_module(struct module *mod)
bf5438fc 215{
d430d3d7
JB
216 struct jump_entry *iter_start = mod->jump_entries;
217 struct jump_entry *iter_stop = iter_start + mod->num_jump_entries;
218 struct jump_entry *iter;
219 struct jump_label_key *key = NULL;
220 struct jump_label_mod *jlm;
bf5438fc
JB
221
222 /* if the module doesn't have jump label entries, just return */
d430d3d7 223 if (iter_start == iter_stop)
bf5438fc
JB
224 return 0;
225
d430d3d7
JB
226 jump_label_sort_entries(iter_start, iter_stop);
227
228 for (iter = iter_start; iter < iter_stop; iter++) {
229 if (iter->key == (jump_label_t)(unsigned long)key)
230 continue;
231
232 key = (struct jump_label_key *)(unsigned long)iter->key;
233
234 if (__module_address(iter->key) == mod) {
235 atomic_set(&key->enabled, 0);
236 key->entries = iter;
237 key->next = NULL;
238 continue;
bf5438fc 239 }
d430d3d7
JB
240
241 jlm = kzalloc(sizeof(struct jump_label_mod), GFP_KERNEL);
242 if (!jlm)
243 return -ENOMEM;
244
245 jlm->mod = mod;
246 jlm->entries = iter;
247 jlm->next = key->next;
248 key->next = jlm;
249
250 if (jump_label_enabled(key))
7cbc5b8d
JO
251 __jump_label_update(key, iter, iter_stop,
252 JUMP_LABEL_ENABLE);
bf5438fc 253 }
d430d3d7 254
bf5438fc
JB
255 return 0;
256}
257
d430d3d7 258static void jump_label_del_module(struct module *mod)
bf5438fc 259{
d430d3d7
JB
260 struct jump_entry *iter_start = mod->jump_entries;
261 struct jump_entry *iter_stop = iter_start + mod->num_jump_entries;
262 struct jump_entry *iter;
263 struct jump_label_key *key = NULL;
264 struct jump_label_mod *jlm, **prev;
bf5438fc 265
d430d3d7
JB
266 for (iter = iter_start; iter < iter_stop; iter++) {
267 if (iter->key == (jump_label_t)(unsigned long)key)
268 continue;
269
270 key = (struct jump_label_key *)(unsigned long)iter->key;
271
272 if (__module_address(iter->key) == mod)
273 continue;
274
275 prev = &key->next;
276 jlm = key->next;
bf5438fc 277
d430d3d7
JB
278 while (jlm && jlm->mod != mod) {
279 prev = &jlm->next;
280 jlm = jlm->next;
281 }
282
283 if (jlm) {
284 *prev = jlm->next;
285 kfree(jlm);
bf5438fc
JB
286 }
287 }
288}
289
d430d3d7 290static void jump_label_invalidate_module_init(struct module *mod)
b842f8fa 291{
d430d3d7
JB
292 struct jump_entry *iter_start = mod->jump_entries;
293 struct jump_entry *iter_stop = iter_start + mod->num_jump_entries;
b842f8fa 294 struct jump_entry *iter;
b842f8fa 295
d430d3d7
JB
296 for (iter = iter_start; iter < iter_stop; iter++) {
297 if (within_module_init(iter->code, mod))
298 iter->code = 0;
b842f8fa
JB
299 }
300}
301
bf5438fc
JB
302static int
303jump_label_module_notify(struct notifier_block *self, unsigned long val,
304 void *data)
305{
306 struct module *mod = data;
307 int ret = 0;
308
309 switch (val) {
310 case MODULE_STATE_COMING:
91bad2f8 311 jump_label_lock();
d430d3d7 312 ret = jump_label_add_module(mod);
bf5438fc 313 if (ret)
d430d3d7 314 jump_label_del_module(mod);
91bad2f8 315 jump_label_unlock();
bf5438fc
JB
316 break;
317 case MODULE_STATE_GOING:
91bad2f8 318 jump_label_lock();
d430d3d7 319 jump_label_del_module(mod);
91bad2f8 320 jump_label_unlock();
bf5438fc 321 break;
b842f8fa 322 case MODULE_STATE_LIVE:
91bad2f8 323 jump_label_lock();
d430d3d7 324 jump_label_invalidate_module_init(mod);
91bad2f8 325 jump_label_unlock();
b842f8fa 326 break;
bf5438fc 327 }
bf5438fc 328
d430d3d7 329 return notifier_from_errno(ret);
bf5438fc
JB
330}
331
332struct notifier_block jump_label_module_nb = {
333 .notifier_call = jump_label_module_notify,
d430d3d7 334 .priority = 1, /* higher than tracepoints */
bf5438fc
JB
335};
336
d430d3d7 337static __init int jump_label_init_module(void)
bf5438fc
JB
338{
339 return register_module_notifier(&jump_label_module_nb);
340}
d430d3d7 341early_initcall(jump_label_init_module);
bf5438fc
JB
342
343#endif /* CONFIG_MODULES */
344
d430d3d7
JB
345/***
346 * jump_label_text_reserved - check if addr range is reserved
347 * @start: start text addr
348 * @end: end text addr
349 *
350 * checks if the text addr located between @start and @end
351 * overlaps with any of the jump label patch addresses. Code
352 * that wants to modify kernel text should first verify that
353 * it does not overlap with any of the jump label addresses.
354 * Caller must hold jump_label_mutex.
355 *
356 * returns 1 if there is an overlap, 0 otherwise
357 */
358int jump_label_text_reserved(void *start, void *end)
359{
360 int ret = __jump_label_text_reserved(__start___jump_table,
361 __stop___jump_table, start, end);
362
363 if (ret)
364 return ret;
365
366#ifdef CONFIG_MODULES
367 ret = __jump_label_mod_text_reserved(start, end);
368#endif
369 return ret;
370}
371
372static void jump_label_update(struct jump_label_key *key, int enable)
373{
140fe3b1 374 struct jump_entry *entry = key->entries, *stop = __stop___jump_table;
d430d3d7
JB
375
376#ifdef CONFIG_MODULES
140fe3b1
XG
377 struct module *mod = __module_address((jump_label_t)key);
378
d430d3d7 379 __jump_label_mod_update(key, enable);
140fe3b1
XG
380
381 if (mod)
382 stop = mod->jump_entries + mod->num_jump_entries;
d430d3d7 383#endif
140fe3b1
XG
384 /* if there are no users, entry can be NULL */
385 if (entry)
386 __jump_label_update(key, entry, stop, enable);
d430d3d7
JB
387}
388
bf5438fc 389#endif