diff options
-rw-r--r-- | include/linux/notifier.h | 43 | ||||
-rw-r--r-- | include/linux/srcu.h | 6 | ||||
-rw-r--r-- | kernel/sys.c | 124 |
3 files changed, 166 insertions, 7 deletions
diff --git a/include/linux/notifier.h b/include/linux/notifier.h index 7ff386a6ae87..10a43ed0527e 100644 --- a/include/linux/notifier.h +++ b/include/linux/notifier.h | |||
@@ -12,9 +12,10 @@ | |||
12 | #include <linux/errno.h> | 12 | #include <linux/errno.h> |
13 | #include <linux/mutex.h> | 13 | #include <linux/mutex.h> |
14 | #include <linux/rwsem.h> | 14 | #include <linux/rwsem.h> |
15 | #include <linux/srcu.h> | ||
15 | 16 | ||
16 | /* | 17 | /* |
17 | * Notifier chains are of three types: | 18 | * Notifier chains are of four types: |
18 | * | 19 | * |
19 | * Atomic notifier chains: Chain callbacks run in interrupt/atomic | 20 | * Atomic notifier chains: Chain callbacks run in interrupt/atomic |
20 | * context. Callouts are not allowed to block. | 21 | * context. Callouts are not allowed to block. |
@@ -23,13 +24,27 @@ | |||
23 | * Raw notifier chains: There are no restrictions on callbacks, | 24 | * Raw notifier chains: There are no restrictions on callbacks, |
24 | * registration, or unregistration. All locking and protection | 25 | * registration, or unregistration. All locking and protection |
25 | * must be provided by the caller. | 26 | * must be provided by the caller. |
27 | * SRCU notifier chains: A variant of blocking notifier chains, with | ||
28 | * the same restrictions. | ||
26 | * | 29 | * |
27 | * atomic_notifier_chain_register() may be called from an atomic context, | 30 | * atomic_notifier_chain_register() may be called from an atomic context, |
28 | * but blocking_notifier_chain_register() must be called from a process | 31 | * but blocking_notifier_chain_register() and srcu_notifier_chain_register() |
29 | * context. Ditto for the corresponding _unregister() routines. | 32 | * must be called from a process context. Ditto for the corresponding |
33 | * _unregister() routines. | ||
30 | * | 34 | * |
31 | * atomic_notifier_chain_unregister() and blocking_notifier_chain_unregister() | 35 | * atomic_notifier_chain_unregister(), blocking_notifier_chain_unregister(), |
32 | * _must not_ be called from within the call chain. | 36 | * and srcu_notifier_chain_unregister() _must not_ be called from within |
37 | * the call chain. | ||
38 | * | ||
39 | * SRCU notifier chains are an alternative form of blocking notifier chains. | ||
40 | * They use SRCU (Sleepable Read-Copy Update) instead of rw-semaphores for | ||
41 | * protection of the chain links. This means there is _very_ low overhead | ||
42 | * in srcu_notifier_call_chain(): no cache bounces and no memory barriers. | ||
43 | * As compensation, srcu_notifier_chain_unregister() is rather expensive. | ||
44 | * SRCU notifier chains should be used when the chain will be called very | ||
45 | * often but notifier_blocks will seldom be removed. Also, SRCU notifier | ||
46 | * chains are slightly more difficult to use because they require special | ||
47 | * runtime initialization. | ||
33 | */ | 48 | */ |
34 | 49 | ||
35 | struct notifier_block { | 50 | struct notifier_block { |
@@ -52,6 +67,12 @@ struct raw_notifier_head { | |||
52 | struct notifier_block *head; | 67 | struct notifier_block *head; |
53 | }; | 68 | }; |
54 | 69 | ||
70 | struct srcu_notifier_head { | ||
71 | struct mutex mutex; | ||
72 | struct srcu_struct srcu; | ||
73 | struct notifier_block *head; | ||
74 | }; | ||
75 | |||
55 | #define ATOMIC_INIT_NOTIFIER_HEAD(name) do { \ | 76 | #define ATOMIC_INIT_NOTIFIER_HEAD(name) do { \ |
56 | spin_lock_init(&(name)->lock); \ | 77 | spin_lock_init(&(name)->lock); \ |
57 | (name)->head = NULL; \ | 78 | (name)->head = NULL; \ |
@@ -64,6 +85,11 @@ struct raw_notifier_head { | |||
64 | (name)->head = NULL; \ | 85 | (name)->head = NULL; \ |
65 | } while (0) | 86 | } while (0) |
66 | 87 | ||
88 | /* srcu_notifier_heads must be initialized and cleaned up dynamically */ | ||
89 | extern void srcu_init_notifier_head(struct srcu_notifier_head *nh); | ||
90 | #define srcu_cleanup_notifier_head(name) \ | ||
91 | cleanup_srcu_struct(&(name)->srcu); | ||
92 | |||
67 | #define ATOMIC_NOTIFIER_INIT(name) { \ | 93 | #define ATOMIC_NOTIFIER_INIT(name) { \ |
68 | .lock = __SPIN_LOCK_UNLOCKED(name.lock), \ | 94 | .lock = __SPIN_LOCK_UNLOCKED(name.lock), \ |
69 | .head = NULL } | 95 | .head = NULL } |
@@ -72,6 +98,7 @@ struct raw_notifier_head { | |||
72 | .head = NULL } | 98 | .head = NULL } |
73 | #define RAW_NOTIFIER_INIT(name) { \ | 99 | #define RAW_NOTIFIER_INIT(name) { \ |
74 | .head = NULL } | 100 | .head = NULL } |
101 | /* srcu_notifier_heads cannot be initialized statically */ | ||
75 | 102 | ||
76 | #define ATOMIC_NOTIFIER_HEAD(name) \ | 103 | #define ATOMIC_NOTIFIER_HEAD(name) \ |
77 | struct atomic_notifier_head name = \ | 104 | struct atomic_notifier_head name = \ |
@@ -91,6 +118,8 @@ extern int blocking_notifier_chain_register(struct blocking_notifier_head *, | |||
91 | struct notifier_block *); | 118 | struct notifier_block *); |
92 | extern int raw_notifier_chain_register(struct raw_notifier_head *, | 119 | extern int raw_notifier_chain_register(struct raw_notifier_head *, |
93 | struct notifier_block *); | 120 | struct notifier_block *); |
121 | extern int srcu_notifier_chain_register(struct srcu_notifier_head *, | ||
122 | struct notifier_block *); | ||
94 | 123 | ||
95 | extern int atomic_notifier_chain_unregister(struct atomic_notifier_head *, | 124 | extern int atomic_notifier_chain_unregister(struct atomic_notifier_head *, |
96 | struct notifier_block *); | 125 | struct notifier_block *); |
@@ -98,6 +127,8 @@ extern int blocking_notifier_chain_unregister(struct blocking_notifier_head *, | |||
98 | struct notifier_block *); | 127 | struct notifier_block *); |
99 | extern int raw_notifier_chain_unregister(struct raw_notifier_head *, | 128 | extern int raw_notifier_chain_unregister(struct raw_notifier_head *, |
100 | struct notifier_block *); | 129 | struct notifier_block *); |
130 | extern int srcu_notifier_chain_unregister(struct srcu_notifier_head *, | ||
131 | struct notifier_block *); | ||
101 | 132 | ||
102 | extern int atomic_notifier_call_chain(struct atomic_notifier_head *, | 133 | extern int atomic_notifier_call_chain(struct atomic_notifier_head *, |
103 | unsigned long val, void *v); | 134 | unsigned long val, void *v); |
@@ -105,6 +136,8 @@ extern int blocking_notifier_call_chain(struct blocking_notifier_head *, | |||
105 | unsigned long val, void *v); | 136 | unsigned long val, void *v); |
106 | extern int raw_notifier_call_chain(struct raw_notifier_head *, | 137 | extern int raw_notifier_call_chain(struct raw_notifier_head *, |
107 | unsigned long val, void *v); | 138 | unsigned long val, void *v); |
139 | extern int srcu_notifier_call_chain(struct srcu_notifier_head *, | ||
140 | unsigned long val, void *v); | ||
108 | 141 | ||
109 | #define NOTIFY_DONE 0x0000 /* Don't care */ | 142 | #define NOTIFY_DONE 0x0000 /* Don't care */ |
110 | #define NOTIFY_OK 0x0001 /* Suits me */ | 143 | #define NOTIFY_OK 0x0001 /* Suits me */ |
diff --git a/include/linux/srcu.h b/include/linux/srcu.h index 947fdab2ddb0..8a45367b5f3a 100644 --- a/include/linux/srcu.h +++ b/include/linux/srcu.h | |||
@@ -24,6 +24,9 @@ | |||
24 | * | 24 | * |
25 | */ | 25 | */ |
26 | 26 | ||
27 | #ifndef _LINUX_SRCU_H | ||
28 | #define _LINUX_SRCU_H | ||
29 | |||
27 | struct srcu_struct_array { | 30 | struct srcu_struct_array { |
28 | int c[2]; | 31 | int c[2]; |
29 | }; | 32 | }; |
@@ -46,4 +49,5 @@ int srcu_read_lock(struct srcu_struct *sp) __acquires(sp); | |||
46 | void srcu_read_unlock(struct srcu_struct *sp, int idx) __releases(sp); | 49 | void srcu_read_unlock(struct srcu_struct *sp, int idx) __releases(sp); |
47 | void synchronize_srcu(struct srcu_struct *sp); | 50 | void synchronize_srcu(struct srcu_struct *sp); |
48 | long srcu_batches_completed(struct srcu_struct *sp); | 51 | long srcu_batches_completed(struct srcu_struct *sp); |
49 | void cleanup_srcu_struct(struct srcu_struct *sp); | 52 | |
53 | #endif | ||
diff --git a/kernel/sys.c b/kernel/sys.c index 2314867ae34f..fd5c71006775 100644 --- a/kernel/sys.c +++ b/kernel/sys.c | |||
@@ -153,7 +153,7 @@ static int __kprobes notifier_call_chain(struct notifier_block **nl, | |||
153 | 153 | ||
154 | /* | 154 | /* |
155 | * Atomic notifier chain routines. Registration and unregistration | 155 | * Atomic notifier chain routines. Registration and unregistration |
156 | * use a mutex, and call_chain is synchronized by RCU (no locks). | 156 | * use a spinlock, and call_chain is synchronized by RCU (no locks). |
157 | */ | 157 | */ |
158 | 158 | ||
159 | /** | 159 | /** |
@@ -401,6 +401,128 @@ int raw_notifier_call_chain(struct raw_notifier_head *nh, | |||
401 | 401 | ||
402 | EXPORT_SYMBOL_GPL(raw_notifier_call_chain); | 402 | EXPORT_SYMBOL_GPL(raw_notifier_call_chain); |
403 | 403 | ||
404 | /* | ||
405 | * SRCU notifier chain routines. Registration and unregistration | ||
406 | * use a mutex, and call_chain is synchronized by SRCU (no locks). | ||
407 | */ | ||
408 | |||
409 | /** | ||
410 | * srcu_notifier_chain_register - Add notifier to an SRCU notifier chain | ||
411 | * @nh: Pointer to head of the SRCU notifier chain | ||
412 | * @n: New entry in notifier chain | ||
413 | * | ||
414 | * Adds a notifier to an SRCU notifier chain. | ||
415 | * Must be called in process context. | ||
416 | * | ||
417 | * Currently always returns zero. | ||
418 | */ | ||
419 | |||
420 | int srcu_notifier_chain_register(struct srcu_notifier_head *nh, | ||
421 | struct notifier_block *n) | ||
422 | { | ||
423 | int ret; | ||
424 | |||
425 | /* | ||
426 | * This code gets used during boot-up, when task switching is | ||
427 | * not yet working and interrupts must remain disabled. At | ||
428 | * such times we must not call mutex_lock(). | ||
429 | */ | ||
430 | if (unlikely(system_state == SYSTEM_BOOTING)) | ||
431 | return notifier_chain_register(&nh->head, n); | ||
432 | |||
433 | mutex_lock(&nh->mutex); | ||
434 | ret = notifier_chain_register(&nh->head, n); | ||
435 | mutex_unlock(&nh->mutex); | ||
436 | return ret; | ||
437 | } | ||
438 | |||
439 | EXPORT_SYMBOL_GPL(srcu_notifier_chain_register); | ||
440 | |||
441 | /** | ||
442 | * srcu_notifier_chain_unregister - Remove notifier from an SRCU notifier chain | ||
443 | * @nh: Pointer to head of the SRCU notifier chain | ||
444 | * @n: Entry to remove from notifier chain | ||
445 | * | ||
446 | * Removes a notifier from an SRCU notifier chain. | ||
447 | * Must be called from process context. | ||
448 | * | ||
449 | * Returns zero on success or %-ENOENT on failure. | ||
450 | */ | ||
451 | int srcu_notifier_chain_unregister(struct srcu_notifier_head *nh, | ||
452 | struct notifier_block *n) | ||
453 | { | ||
454 | int ret; | ||
455 | |||
456 | /* | ||
457 | * This code gets used during boot-up, when task switching is | ||
458 | * not yet working and interrupts must remain disabled. At | ||
459 | * such times we must not call mutex_lock(). | ||
460 | */ | ||
461 | if (unlikely(system_state == SYSTEM_BOOTING)) | ||
462 | return notifier_chain_unregister(&nh->head, n); | ||
463 | |||
464 | mutex_lock(&nh->mutex); | ||
465 | ret = notifier_chain_unregister(&nh->head, n); | ||
466 | mutex_unlock(&nh->mutex); | ||
467 | synchronize_srcu(&nh->srcu); | ||
468 | return ret; | ||
469 | } | ||
470 | |||
471 | EXPORT_SYMBOL_GPL(srcu_notifier_chain_unregister); | ||
472 | |||
473 | /** | ||
474 | * srcu_notifier_call_chain - Call functions in an SRCU notifier chain | ||
475 | * @nh: Pointer to head of the SRCU notifier chain | ||
476 | * @val: Value passed unmodified to notifier function | ||
477 | * @v: Pointer passed unmodified to notifier function | ||
478 | * | ||
479 | * Calls each function in a notifier chain in turn. The functions | ||
480 | * run in a process context, so they are allowed to block. | ||
481 | * | ||
482 | * If the return value of the notifier can be and'ed | ||
483 | * with %NOTIFY_STOP_MASK then srcu_notifier_call_chain | ||
484 | * will return immediately, with the return value of | ||
485 | * the notifier function which halted execution. | ||
486 | * Otherwise the return value is the return value | ||
487 | * of the last notifier function called. | ||
488 | */ | ||
489 | |||
490 | int srcu_notifier_call_chain(struct srcu_notifier_head *nh, | ||
491 | unsigned long val, void *v) | ||
492 | { | ||
493 | int ret; | ||
494 | int idx; | ||
495 | |||
496 | idx = srcu_read_lock(&nh->srcu); | ||
497 | ret = notifier_call_chain(&nh->head, val, v); | ||
498 | srcu_read_unlock(&nh->srcu, idx); | ||
499 | return ret; | ||
500 | } | ||
501 | |||
502 | EXPORT_SYMBOL_GPL(srcu_notifier_call_chain); | ||
503 | |||
504 | /** | ||
505 | * srcu_init_notifier_head - Initialize an SRCU notifier head | ||
506 | * @nh: Pointer to head of the srcu notifier chain | ||
507 | * | ||
508 | * Unlike other sorts of notifier heads, SRCU notifier heads require | ||
509 | * dynamic initialization. Be sure to call this routine before | ||
510 | * calling any of the other SRCU notifier routines for this head. | ||
511 | * | ||
512 | * If an SRCU notifier head is deallocated, it must first be cleaned | ||
513 | * up by calling srcu_cleanup_notifier_head(). Otherwise the head's | ||
514 | * per-cpu data (used by the SRCU mechanism) will leak. | ||
515 | */ | ||
516 | |||
517 | void srcu_init_notifier_head(struct srcu_notifier_head *nh) | ||
518 | { | ||
519 | mutex_init(&nh->mutex); | ||
520 | init_srcu_struct(&nh->srcu); | ||
521 | nh->head = NULL; | ||
522 | } | ||
523 | |||
524 | EXPORT_SYMBOL_GPL(srcu_init_notifier_head); | ||
525 | |||
404 | /** | 526 | /** |
405 | * register_reboot_notifier - Register function to be called at reboot time | 527 | * register_reboot_notifier - Register function to be called at reboot time |
406 | * @nb: Info about notifier function to be called | 528 | * @nb: Info about notifier function to be called |