aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/static-keys.txt4
-rw-r--r--include/linux/jump_label.h23
-rw-r--r--kernel/jump_label.c153
3 files changed, 145 insertions, 35 deletions
diff --git a/Documentation/static-keys.txt b/Documentation/static-keys.txt
index ea8d7b4e53f0..32a25fad0c1b 100644
--- a/Documentation/static-keys.txt
+++ b/Documentation/static-keys.txt
@@ -155,7 +155,9 @@ or:
155 155
156There are a few functions and macros that architectures must implement in order 156There are a few functions and macros that architectures must implement in order
157to take advantage of this optimization. If there is no architecture support, we 157to take advantage of this optimization. If there is no architecture support, we
158simply fall back to a traditional, load, test, and jump sequence. 158simply fall back to a traditional, load, test, and jump sequence. Also, the
159struct jump_entry table must be at least 4-byte aligned because the
160static_key->entry field makes use of the two least significant bits.
159 161
160* select HAVE_ARCH_JUMP_LABEL, see: arch/x86/Kconfig 162* select HAVE_ARCH_JUMP_LABEL, see: arch/x86/Kconfig
161 163
diff --git a/include/linux/jump_label.h b/include/linux/jump_label.h
index a0547c571800..680c98b2f41c 100644
--- a/include/linux/jump_label.h
+++ b/include/linux/jump_label.h
@@ -89,11 +89,17 @@ extern bool static_key_initialized;
89 89
90struct static_key { 90struct static_key {
91 atomic_t enabled; 91 atomic_t enabled;
92/* Set lsb bit to 1 if branch is default true, 0 ot */ 92/*
93 struct jump_entry *entries; 93 * bit 0 => 1 if key is initially true
94#ifdef CONFIG_MODULES 94 * 0 if initially false
95 struct static_key_mod *next; 95 * bit 1 => 1 if points to struct static_key_mod
96#endif 96 * 0 if points to struct jump_entry
97 */
98 union {
99 unsigned long type;
100 struct jump_entry *entries;
101 struct static_key_mod *next;
102 };
97}; 103};
98 104
99#else 105#else
@@ -118,9 +124,10 @@ struct module;
118 124
119#ifdef HAVE_JUMP_LABEL 125#ifdef HAVE_JUMP_LABEL
120 126
121#define JUMP_TYPE_FALSE 0UL 127#define JUMP_TYPE_FALSE 0UL
122#define JUMP_TYPE_TRUE 1UL 128#define JUMP_TYPE_TRUE 1UL
123#define JUMP_TYPE_MASK 1UL 129#define JUMP_TYPE_LINKED 2UL
130#define JUMP_TYPE_MASK 3UL
124 131
125static __always_inline bool static_key_false(struct static_key *key) 132static __always_inline bool static_key_false(struct static_key *key)
126{ 133{
diff --git a/kernel/jump_label.c b/kernel/jump_label.c
index 93ad6c1fb9b6..953411f5ba7f 100644
--- a/kernel/jump_label.c
+++ b/kernel/jump_label.c
@@ -229,12 +229,28 @@ void __weak __init_or_module arch_jump_label_transform_static(struct jump_entry
229 229
230static inline struct jump_entry *static_key_entries(struct static_key *key) 230static inline struct jump_entry *static_key_entries(struct static_key *key)
231{ 231{
232 return (struct jump_entry *)((unsigned long)key->entries & ~JUMP_TYPE_MASK); 232 WARN_ON_ONCE(key->type & JUMP_TYPE_LINKED);
233 return (struct jump_entry *)(key->type & ~JUMP_TYPE_MASK);
233} 234}
234 235
235static inline bool static_key_type(struct static_key *key) 236static inline bool static_key_type(struct static_key *key)
236{ 237{
237 return (unsigned long)key->entries & JUMP_TYPE_MASK; 238 return key->type & JUMP_TYPE_TRUE;
239}
240
241static inline bool static_key_linked(struct static_key *key)
242{
243 return key->type & JUMP_TYPE_LINKED;
244}
245
246static inline void static_key_clear_linked(struct static_key *key)
247{
248 key->type &= ~JUMP_TYPE_LINKED;
249}
250
251static inline void static_key_set_linked(struct static_key *key)
252{
253 key->type |= JUMP_TYPE_LINKED;
238} 254}
239 255
240static inline struct static_key *jump_entry_key(struct jump_entry *entry) 256static inline struct static_key *jump_entry_key(struct jump_entry *entry)
@@ -247,6 +263,26 @@ static bool jump_entry_branch(struct jump_entry *entry)
247 return (unsigned long)entry->key & 1UL; 263 return (unsigned long)entry->key & 1UL;
248} 264}
249 265
266/***
267 * A 'struct static_key' uses a union such that it either points directly
268 * to a table of 'struct jump_entry' or to a linked list of modules which in
269 * turn point to 'struct jump_entry' tables.
270 *
271 * The two lower bits of the pointer are used to keep track of which pointer
272 * type is in use and to store the initial branch direction, we use an access
273 * function which preserves these bits.
274 */
275static void static_key_set_entries(struct static_key *key,
276 struct jump_entry *entries)
277{
278 unsigned long type;
279
280 WARN_ON_ONCE((unsigned long)entries & JUMP_TYPE_MASK);
281 type = key->type & JUMP_TYPE_MASK;
282 key->entries = entries;
283 key->type |= type;
284}
285
250static enum jump_label_type jump_label_type(struct jump_entry *entry) 286static enum jump_label_type jump_label_type(struct jump_entry *entry)
251{ 287{
252 struct static_key *key = jump_entry_key(entry); 288 struct static_key *key = jump_entry_key(entry);
@@ -306,13 +342,7 @@ void __init jump_label_init(void)
306 continue; 342 continue;
307 343
308 key = iterk; 344 key = iterk;
309 /* 345 static_key_set_entries(key, iter);
310 * Set key->entries to iter, but preserve JUMP_LABEL_TRUE_BRANCH.
311 */
312 *((unsigned long *)&key->entries) += (unsigned long)iter;
313#ifdef CONFIG_MODULES
314 key->next = NULL;
315#endif
316 } 346 }
317 static_key_initialized = true; 347 static_key_initialized = true;
318 jump_label_unlock(); 348 jump_label_unlock();
@@ -336,6 +366,29 @@ struct static_key_mod {
336 struct module *mod; 366 struct module *mod;
337}; 367};
338 368
369static inline struct static_key_mod *static_key_mod(struct static_key *key)
370{
371 WARN_ON_ONCE(!(key->type & JUMP_TYPE_LINKED));
372 return (struct static_key_mod *)(key->type & ~JUMP_TYPE_MASK);
373}
374
375/***
376 * key->type and key->next are the same via union.
377 * This sets key->next and preserves the type bits.
378 *
379 * See additional comments above static_key_set_entries().
380 */
381static void static_key_set_mod(struct static_key *key,
382 struct static_key_mod *mod)
383{
384 unsigned long type;
385
386 WARN_ON_ONCE((unsigned long)mod & JUMP_TYPE_MASK);
387 type = key->type & JUMP_TYPE_MASK;
388 key->next = mod;
389 key->type |= type;
390}
391
339static int __jump_label_mod_text_reserved(void *start, void *end) 392static int __jump_label_mod_text_reserved(void *start, void *end)
340{ 393{
341 struct module *mod; 394 struct module *mod;
@@ -358,11 +411,23 @@ static void __jump_label_mod_update(struct static_key *key)
358{ 411{
359 struct static_key_mod *mod; 412 struct static_key_mod *mod;
360 413
361 for (mod = key->next; mod; mod = mod->next) { 414 for (mod = static_key_mod(key); mod; mod = mod->next) {
362 struct module *m = mod->mod; 415 struct jump_entry *stop;
416 struct module *m;
417
418 /*
419 * NULL if the static_key is defined in a module
420 * that does not use it
421 */
422 if (!mod->entries)
423 continue;
363 424
364 __jump_label_update(key, mod->entries, 425 m = mod->mod;
365 m->jump_entries + m->num_jump_entries); 426 if (!m)
427 stop = __stop___jump_table;
428 else
429 stop = m->jump_entries + m->num_jump_entries;
430 __jump_label_update(key, mod->entries, stop);
366 } 431 }
367} 432}
368 433
@@ -397,7 +462,7 @@ static int jump_label_add_module(struct module *mod)
397 struct jump_entry *iter_stop = iter_start + mod->num_jump_entries; 462 struct jump_entry *iter_stop = iter_start + mod->num_jump_entries;
398 struct jump_entry *iter; 463 struct jump_entry *iter;
399 struct static_key *key = NULL; 464 struct static_key *key = NULL;
400 struct static_key_mod *jlm; 465 struct static_key_mod *jlm, *jlm2;
401 466
402 /* if the module doesn't have jump label entries, just return */ 467 /* if the module doesn't have jump label entries, just return */
403 if (iter_start == iter_stop) 468 if (iter_start == iter_stop)
@@ -414,20 +479,32 @@ static int jump_label_add_module(struct module *mod)
414 479
415 key = iterk; 480 key = iterk;
416 if (within_module(iter->key, mod)) { 481 if (within_module(iter->key, mod)) {
417 /* 482 static_key_set_entries(key, iter);
418 * Set key->entries to iter, but preserve JUMP_LABEL_TRUE_BRANCH.
419 */
420 *((unsigned long *)&key->entries) += (unsigned long)iter;
421 key->next = NULL;
422 continue; 483 continue;
423 } 484 }
424 jlm = kzalloc(sizeof(struct static_key_mod), GFP_KERNEL); 485 jlm = kzalloc(sizeof(struct static_key_mod), GFP_KERNEL);
425 if (!jlm) 486 if (!jlm)
426 return -ENOMEM; 487 return -ENOMEM;
488 if (!static_key_linked(key)) {
489 jlm2 = kzalloc(sizeof(struct static_key_mod),
490 GFP_KERNEL);
491 if (!jlm2) {
492 kfree(jlm);
493 return -ENOMEM;
494 }
495 preempt_disable();
496 jlm2->mod = __module_address((unsigned long)key);
497 preempt_enable();
498 jlm2->entries = static_key_entries(key);
499 jlm2->next = NULL;
500 static_key_set_mod(key, jlm2);
501 static_key_set_linked(key);
502 }
427 jlm->mod = mod; 503 jlm->mod = mod;
428 jlm->entries = iter; 504 jlm->entries = iter;
429 jlm->next = key->next; 505 jlm->next = static_key_mod(key);
430 key->next = jlm; 506 static_key_set_mod(key, jlm);
507 static_key_set_linked(key);
431 508
432 /* Only update if we've changed from our initial state */ 509 /* Only update if we've changed from our initial state */
433 if (jump_label_type(iter) != jump_label_init_type(iter)) 510 if (jump_label_type(iter) != jump_label_init_type(iter))
@@ -454,16 +531,34 @@ static void jump_label_del_module(struct module *mod)
454 if (within_module(iter->key, mod)) 531 if (within_module(iter->key, mod))
455 continue; 532 continue;
456 533
534 /* No memory during module load */
535 if (WARN_ON(!static_key_linked(key)))
536 continue;
537
457 prev = &key->next; 538 prev = &key->next;
458 jlm = key->next; 539 jlm = static_key_mod(key);
459 540
460 while (jlm && jlm->mod != mod) { 541 while (jlm && jlm->mod != mod) {
461 prev = &jlm->next; 542 prev = &jlm->next;
462 jlm = jlm->next; 543 jlm = jlm->next;
463 } 544 }
464 545
465 if (jlm) { 546 /* No memory during module load */
547 if (WARN_ON(!jlm))
548 continue;
549
550 if (prev == &key->next)
551 static_key_set_mod(key, jlm->next);
552 else
466 *prev = jlm->next; 553 *prev = jlm->next;
554
555 kfree(jlm);
556
557 jlm = static_key_mod(key);
558 /* if only one etry is left, fold it back into the static_key */
559 if (jlm->next == NULL) {
560 static_key_set_entries(key, jlm->entries);
561 static_key_clear_linked(key);
467 kfree(jlm); 562 kfree(jlm);
468 } 563 }
469 } 564 }
@@ -492,8 +587,10 @@ jump_label_module_notify(struct notifier_block *self, unsigned long val,
492 case MODULE_STATE_COMING: 587 case MODULE_STATE_COMING:
493 jump_label_lock(); 588 jump_label_lock();
494 ret = jump_label_add_module(mod); 589 ret = jump_label_add_module(mod);
495 if (ret) 590 if (ret) {
591 WARN(1, "Failed to allocatote memory: jump_label may not work properly.\n");
496 jump_label_del_module(mod); 592 jump_label_del_module(mod);
593 }
497 jump_label_unlock(); 594 jump_label_unlock();
498 break; 595 break;
499 case MODULE_STATE_GOING: 596 case MODULE_STATE_GOING:
@@ -554,11 +651,14 @@ int jump_label_text_reserved(void *start, void *end)
554static void jump_label_update(struct static_key *key) 651static void jump_label_update(struct static_key *key)
555{ 652{
556 struct jump_entry *stop = __stop___jump_table; 653 struct jump_entry *stop = __stop___jump_table;
557 struct jump_entry *entry = static_key_entries(key); 654 struct jump_entry *entry;
558#ifdef CONFIG_MODULES 655#ifdef CONFIG_MODULES
559 struct module *mod; 656 struct module *mod;
560 657
561 __jump_label_mod_update(key); 658 if (static_key_linked(key)) {
659 __jump_label_mod_update(key);
660 return;
661 }
562 662
563 preempt_disable(); 663 preempt_disable();
564 mod = __module_address((unsigned long)key); 664 mod = __module_address((unsigned long)key);
@@ -566,6 +666,7 @@ static void jump_label_update(struct static_key *key)
566 stop = mod->jump_entries + mod->num_jump_entries; 666 stop = mod->jump_entries + mod->num_jump_entries;
567 preempt_enable(); 667 preempt_enable();
568#endif 668#endif
669 entry = static_key_entries(key);
569 /* if there are no users, entry can be NULL */ 670 /* if there are no users, entry can be NULL */
570 if (entry) 671 if (entry)
571 __jump_label_update(key, entry, stop); 672 __jump_label_update(key, entry, stop);