diff options
author | Gautham R Shenoy <ego@in.ibm.com> | 2007-05-09 05:34:02 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-05-09 15:30:51 -0400 |
commit | 6f7cc11aa6c7d5002e16096c7590944daece70ed (patch) | |
tree | 68a11e4b67189c44ac0f3ab579e52e134d479cf5 | |
parent | 7c9cb38302e78d24e37f7d8a2ea7eed4ae5f2fa7 (diff) |
Extend notifier_call_chain to count nr_calls made
Since 2.6.18-something, the community has been bugged by the problem to
provide a clean and a stable mechanism to postpone a cpu-hotplug event as
lock_cpu_hotplug was badly broken.
This is another proposal towards solving that problem. This one is along the
lines of the solution provided in kernel/workqueue.c
Instead of having a global mechanism like lock_cpu_hotplug, we allow the
subsytems to define their own per-subsystem hot cpu mutexes. These would be
taken(released) where ever we are currently calling
lock_cpu_hotplug(unlock_cpu_hotplug).
Also, in the per-subsystem hotcpu callback function,we take this mutex before
we handle any pre-cpu-hotplug events and release it once we finish handling
the post-cpu-hotplug events. A standard means for doing this has been
provided in [PATCH 2/4] and demonstrated in [PATCH 3/4].
The ordering of these per-subsystem mutexes might still prove to be a
problem, but hopefully lockdep should help us get out of that muddle.
The patch set to be applied against linux-2.6.19-rc5 is as follows:
[PATCH 1/4] : Extend notifier_call_chain with an option to specify the
number of notifications to be sent and also count the
number of notifications actually sent.
[PATCH 2/4] : Define events CPU_LOCK_ACQUIRE and CPU_LOCK_RELEASE
and send out notifications for these in _cpu_up and
_cpu_down. This would help us standardise the acquire and
release of the subsystem locks in the hotcpu
callback functions of these subsystems.
[PATCH 3/4] : Eliminate lock_cpu_hotplug from kernel/sched.c.
[PATCH 4/4] : In workqueue_cpu_callback function, acquire(release) the
workqueue_mutex while handling
CPU_LOCK_ACQUIRE(CPU_LOCK_RELEASE).
If the per-subsystem-locking approach survives the test of time, we can expect
a slow phasing out of lock_cpu_hotplug, which has not yet been eliminated in
these patches :)
This patch:
Provide notifier_call_chain with an option to call only a specified number of
notifiers and also record the number of call to notifiers made.
The need for this enhancement was identified in the post entitled
"Slab - Eliminate lock_cpu_hotplug from slab"
(http://lkml.org/lkml/2006/10/28/92) by Ravikiran G Thirumalai and
Andrew Morton.
This patch adds two additional parameters to notifier_call_chain API namely
- int nr_to_calls : Number of notifier_functions to be called.
The don't care value is -1.
- unsigned int *nr_calls : Records the total number of notifier_funtions
called by notifier_call_chain. The don't care
value is NULL.
[michal.k.k.piotrowski@gmail.com: build fix]
Credit: Andrew Morton <akpm@osdl.org>
Signed-off-by: Gautham R Shenoy <ego@in.ibm.com>
Signed-off-by: Michal Piotrowski <michal.k.k.piotrowski@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | include/linux/notifier.h | 52 | ||||
-rw-r--r-- | kernel/sys.c | 94 |
2 files changed, 107 insertions, 39 deletions
diff --git a/include/linux/notifier.h b/include/linux/notifier.h index 10a43ed0527e..e34221bf8946 100644 --- a/include/linux/notifier.h +++ b/include/linux/notifier.h | |||
@@ -112,32 +112,40 @@ extern void srcu_init_notifier_head(struct srcu_notifier_head *nh); | |||
112 | 112 | ||
113 | #ifdef __KERNEL__ | 113 | #ifdef __KERNEL__ |
114 | 114 | ||
115 | extern int atomic_notifier_chain_register(struct atomic_notifier_head *, | 115 | extern int atomic_notifier_chain_register(struct atomic_notifier_head *nh, |
116 | struct notifier_block *); | 116 | struct notifier_block *nb); |
117 | extern int blocking_notifier_chain_register(struct blocking_notifier_head *, | 117 | extern int blocking_notifier_chain_register(struct blocking_notifier_head *nh, |
118 | struct notifier_block *); | 118 | struct notifier_block *nb); |
119 | extern int raw_notifier_chain_register(struct raw_notifier_head *, | 119 | extern int raw_notifier_chain_register(struct raw_notifier_head *nh, |
120 | struct notifier_block *); | 120 | struct notifier_block *nb); |
121 | extern int srcu_notifier_chain_register(struct srcu_notifier_head *, | 121 | extern int srcu_notifier_chain_register(struct srcu_notifier_head *nh, |
122 | struct notifier_block *); | 122 | struct notifier_block *nb); |
123 | 123 | ||
124 | extern int atomic_notifier_chain_unregister(struct atomic_notifier_head *, | 124 | extern int atomic_notifier_chain_unregister(struct atomic_notifier_head *nh, |
125 | struct notifier_block *); | 125 | struct notifier_block *nb); |
126 | extern int blocking_notifier_chain_unregister(struct blocking_notifier_head *, | 126 | extern int blocking_notifier_chain_unregister(struct blocking_notifier_head *nh, |
127 | struct notifier_block *); | 127 | struct notifier_block *nb); |
128 | extern int raw_notifier_chain_unregister(struct raw_notifier_head *, | 128 | extern int raw_notifier_chain_unregister(struct raw_notifier_head *nh, |
129 | struct notifier_block *); | 129 | struct notifier_block *nb); |
130 | extern int srcu_notifier_chain_unregister(struct srcu_notifier_head *, | 130 | extern int srcu_notifier_chain_unregister(struct srcu_notifier_head *nh, |
131 | struct notifier_block *); | 131 | struct notifier_block *nb); |
132 | 132 | ||
133 | extern int atomic_notifier_call_chain(struct atomic_notifier_head *, | 133 | extern int atomic_notifier_call_chain(struct atomic_notifier_head *nh, |
134 | unsigned long val, void *v); | 134 | unsigned long val, void *v); |
135 | extern int blocking_notifier_call_chain(struct blocking_notifier_head *, | 135 | extern int __atomic_notifier_call_chain(struct atomic_notifier_head *nh, |
136 | unsigned long val, void *v, int nr_to_call, int *nr_calls); | ||
137 | extern int blocking_notifier_call_chain(struct blocking_notifier_head *nh, | ||
136 | unsigned long val, void *v); | 138 | unsigned long val, void *v); |
137 | extern int raw_notifier_call_chain(struct raw_notifier_head *, | 139 | extern int __blocking_notifier_call_chain(struct blocking_notifier_head *nh, |
140 | unsigned long val, void *v, int nr_to_call, int *nr_calls); | ||
141 | extern int raw_notifier_call_chain(struct raw_notifier_head *nh, | ||
138 | unsigned long val, void *v); | 142 | unsigned long val, void *v); |
139 | extern int srcu_notifier_call_chain(struct srcu_notifier_head *, | 143 | extern int __raw_notifier_call_chain(struct raw_notifier_head *nh, |
144 | unsigned long val, void *v, int nr_to_call, int *nr_calls); | ||
145 | extern int srcu_notifier_call_chain(struct srcu_notifier_head *nh, | ||
140 | unsigned long val, void *v); | 146 | unsigned long val, void *v); |
147 | extern int __srcu_notifier_call_chain(struct srcu_notifier_head *nh, | ||
148 | unsigned long val, void *v, int nr_to_call, int *nr_calls); | ||
141 | 149 | ||
142 | #define NOTIFY_DONE 0x0000 /* Don't care */ | 150 | #define NOTIFY_DONE 0x0000 /* Don't care */ |
143 | #define NOTIFY_OK 0x0001 /* Suits me */ | 151 | #define NOTIFY_OK 0x0001 /* Suits me */ |
diff --git a/kernel/sys.c b/kernel/sys.c index 8ecfe3473779..d4985df21b60 100644 --- a/kernel/sys.c +++ b/kernel/sys.c | |||
@@ -134,19 +134,39 @@ static int notifier_chain_unregister(struct notifier_block **nl, | |||
134 | return -ENOENT; | 134 | return -ENOENT; |
135 | } | 135 | } |
136 | 136 | ||
137 | /** | ||
138 | * notifier_call_chain - Informs the registered notifiers about an event. | ||
139 | * @nl: Pointer to head of the blocking notifier chain | ||
140 | * @val: Value passed unmodified to notifier function | ||
141 | * @v: Pointer passed unmodified to notifier function | ||
142 | * @nr_to_call: Number of notifier functions to be called. Don't care | ||
143 | * value of this parameter is -1. | ||
144 | * @nr_calls: Records the number of notifications sent. Don't care | ||
145 | * value of this field is NULL. | ||
146 | * @returns: notifier_call_chain returns the value returned by the | ||
147 | * last notifier function called. | ||
148 | */ | ||
149 | |||
137 | static int __kprobes notifier_call_chain(struct notifier_block **nl, | 150 | static int __kprobes notifier_call_chain(struct notifier_block **nl, |
138 | unsigned long val, void *v) | 151 | unsigned long val, void *v, |
152 | int nr_to_call, int *nr_calls) | ||
139 | { | 153 | { |
140 | int ret = NOTIFY_DONE; | 154 | int ret = NOTIFY_DONE; |
141 | struct notifier_block *nb, *next_nb; | 155 | struct notifier_block *nb, *next_nb; |
142 | 156 | ||
143 | nb = rcu_dereference(*nl); | 157 | nb = rcu_dereference(*nl); |
144 | while (nb) { | 158 | |
159 | while (nb && nr_to_call) { | ||
145 | next_nb = rcu_dereference(nb->next); | 160 | next_nb = rcu_dereference(nb->next); |
146 | ret = nb->notifier_call(nb, val, v); | 161 | ret = nb->notifier_call(nb, val, v); |
162 | |||
163 | if (nr_calls) | ||
164 | (*nr_calls)++; | ||
165 | |||
147 | if ((ret & NOTIFY_STOP_MASK) == NOTIFY_STOP_MASK) | 166 | if ((ret & NOTIFY_STOP_MASK) == NOTIFY_STOP_MASK) |
148 | break; | 167 | break; |
149 | nb = next_nb; | 168 | nb = next_nb; |
169 | nr_to_call--; | ||
150 | } | 170 | } |
151 | return ret; | 171 | return ret; |
152 | } | 172 | } |
@@ -205,10 +225,12 @@ int atomic_notifier_chain_unregister(struct atomic_notifier_head *nh, | |||
205 | EXPORT_SYMBOL_GPL(atomic_notifier_chain_unregister); | 225 | EXPORT_SYMBOL_GPL(atomic_notifier_chain_unregister); |
206 | 226 | ||
207 | /** | 227 | /** |
208 | * atomic_notifier_call_chain - Call functions in an atomic notifier chain | 228 | * __atomic_notifier_call_chain - Call functions in an atomic notifier chain |
209 | * @nh: Pointer to head of the atomic notifier chain | 229 | * @nh: Pointer to head of the atomic notifier chain |
210 | * @val: Value passed unmodified to notifier function | 230 | * @val: Value passed unmodified to notifier function |
211 | * @v: Pointer passed unmodified to notifier function | 231 | * @v: Pointer passed unmodified to notifier function |
232 | * @nr_to_call: See the comment for notifier_call_chain. | ||
233 | * @nr_calls: See the comment for notifier_call_chain. | ||
212 | * | 234 | * |
213 | * Calls each function in a notifier chain in turn. The functions | 235 | * Calls each function in a notifier chain in turn. The functions |
214 | * run in an atomic context, so they must not block. | 236 | * run in an atomic context, so they must not block. |
@@ -222,19 +244,27 @@ EXPORT_SYMBOL_GPL(atomic_notifier_chain_unregister); | |||
222 | * of the last notifier function called. | 244 | * of the last notifier function called. |
223 | */ | 245 | */ |
224 | 246 | ||
225 | int __kprobes atomic_notifier_call_chain(struct atomic_notifier_head *nh, | 247 | int __kprobes __atomic_notifier_call_chain(struct atomic_notifier_head *nh, |
226 | unsigned long val, void *v) | 248 | unsigned long val, void *v, |
249 | int nr_to_call, int *nr_calls) | ||
227 | { | 250 | { |
228 | int ret; | 251 | int ret; |
229 | 252 | ||
230 | rcu_read_lock(); | 253 | rcu_read_lock(); |
231 | ret = notifier_call_chain(&nh->head, val, v); | 254 | ret = notifier_call_chain(&nh->head, val, v, nr_to_call, nr_calls); |
232 | rcu_read_unlock(); | 255 | rcu_read_unlock(); |
233 | return ret; | 256 | return ret; |
234 | } | 257 | } |
235 | 258 | ||
236 | EXPORT_SYMBOL_GPL(atomic_notifier_call_chain); | 259 | EXPORT_SYMBOL_GPL(__atomic_notifier_call_chain); |
260 | |||
261 | int __kprobes atomic_notifier_call_chain(struct atomic_notifier_head *nh, | ||
262 | unsigned long val, void *v) | ||
263 | { | ||
264 | return __atomic_notifier_call_chain(nh, val, v, -1, NULL); | ||
265 | } | ||
237 | 266 | ||
267 | EXPORT_SYMBOL_GPL(atomic_notifier_call_chain); | ||
238 | /* | 268 | /* |
239 | * Blocking notifier chain routines. All access to the chain is | 269 | * Blocking notifier chain routines. All access to the chain is |
240 | * synchronized by an rwsem. | 270 | * synchronized by an rwsem. |
@@ -304,10 +334,12 @@ int blocking_notifier_chain_unregister(struct blocking_notifier_head *nh, | |||
304 | EXPORT_SYMBOL_GPL(blocking_notifier_chain_unregister); | 334 | EXPORT_SYMBOL_GPL(blocking_notifier_chain_unregister); |
305 | 335 | ||
306 | /** | 336 | /** |
307 | * blocking_notifier_call_chain - Call functions in a blocking notifier chain | 337 | * __blocking_notifier_call_chain - Call functions in a blocking notifier chain |
308 | * @nh: Pointer to head of the blocking notifier chain | 338 | * @nh: Pointer to head of the blocking notifier chain |
309 | * @val: Value passed unmodified to notifier function | 339 | * @val: Value passed unmodified to notifier function |
310 | * @v: Pointer passed unmodified to notifier function | 340 | * @v: Pointer passed unmodified to notifier function |
341 | * @nr_to_call: See comment for notifier_call_chain. | ||
342 | * @nr_calls: See comment for notifier_call_chain. | ||
311 | * | 343 | * |
312 | * Calls each function in a notifier chain in turn. The functions | 344 | * Calls each function in a notifier chain in turn. The functions |
313 | * run in a process context, so they are allowed to block. | 345 | * run in a process context, so they are allowed to block. |
@@ -320,8 +352,9 @@ EXPORT_SYMBOL_GPL(blocking_notifier_chain_unregister); | |||
320 | * of the last notifier function called. | 352 | * of the last notifier function called. |
321 | */ | 353 | */ |
322 | 354 | ||
323 | int blocking_notifier_call_chain(struct blocking_notifier_head *nh, | 355 | int __blocking_notifier_call_chain(struct blocking_notifier_head *nh, |
324 | unsigned long val, void *v) | 356 | unsigned long val, void *v, |
357 | int nr_to_call, int *nr_calls) | ||
325 | { | 358 | { |
326 | int ret = NOTIFY_DONE; | 359 | int ret = NOTIFY_DONE; |
327 | 360 | ||
@@ -332,12 +365,19 @@ int blocking_notifier_call_chain(struct blocking_notifier_head *nh, | |||
332 | */ | 365 | */ |
333 | if (rcu_dereference(nh->head)) { | 366 | if (rcu_dereference(nh->head)) { |
334 | down_read(&nh->rwsem); | 367 | down_read(&nh->rwsem); |
335 | ret = notifier_call_chain(&nh->head, val, v); | 368 | ret = notifier_call_chain(&nh->head, val, v, nr_to_call, |
369 | nr_calls); | ||
336 | up_read(&nh->rwsem); | 370 | up_read(&nh->rwsem); |
337 | } | 371 | } |
338 | return ret; | 372 | return ret; |
339 | } | 373 | } |
374 | EXPORT_SYMBOL_GPL(__blocking_notifier_call_chain); | ||
340 | 375 | ||
376 | int blocking_notifier_call_chain(struct blocking_notifier_head *nh, | ||
377 | unsigned long val, void *v) | ||
378 | { | ||
379 | return __blocking_notifier_call_chain(nh, val, v, -1, NULL); | ||
380 | } | ||
341 | EXPORT_SYMBOL_GPL(blocking_notifier_call_chain); | 381 | EXPORT_SYMBOL_GPL(blocking_notifier_call_chain); |
342 | 382 | ||
343 | /* | 383 | /* |
@@ -383,10 +423,12 @@ int raw_notifier_chain_unregister(struct raw_notifier_head *nh, | |||
383 | EXPORT_SYMBOL_GPL(raw_notifier_chain_unregister); | 423 | EXPORT_SYMBOL_GPL(raw_notifier_chain_unregister); |
384 | 424 | ||
385 | /** | 425 | /** |
386 | * raw_notifier_call_chain - Call functions in a raw notifier chain | 426 | * __raw_notifier_call_chain - Call functions in a raw notifier chain |
387 | * @nh: Pointer to head of the raw notifier chain | 427 | * @nh: Pointer to head of the raw notifier chain |
388 | * @val: Value passed unmodified to notifier function | 428 | * @val: Value passed unmodified to notifier function |
389 | * @v: Pointer passed unmodified to notifier function | 429 | * @v: Pointer passed unmodified to notifier function |
430 | * @nr_to_call: See comment for notifier_call_chain. | ||
431 | * @nr_calls: See comment for notifier_call_chain | ||
390 | * | 432 | * |
391 | * Calls each function in a notifier chain in turn. The functions | 433 | * Calls each function in a notifier chain in turn. The functions |
392 | * run in an undefined context. | 434 | * run in an undefined context. |
@@ -400,10 +442,19 @@ EXPORT_SYMBOL_GPL(raw_notifier_chain_unregister); | |||
400 | * of the last notifier function called. | 442 | * of the last notifier function called. |
401 | */ | 443 | */ |
402 | 444 | ||
445 | int __raw_notifier_call_chain(struct raw_notifier_head *nh, | ||
446 | unsigned long val, void *v, | ||
447 | int nr_to_call, int *nr_calls) | ||
448 | { | ||
449 | return notifier_call_chain(&nh->head, val, v, nr_to_call, nr_calls); | ||
450 | } | ||
451 | |||
452 | EXPORT_SYMBOL_GPL(__raw_notifier_call_chain); | ||
453 | |||
403 | int raw_notifier_call_chain(struct raw_notifier_head *nh, | 454 | int raw_notifier_call_chain(struct raw_notifier_head *nh, |
404 | unsigned long val, void *v) | 455 | unsigned long val, void *v) |
405 | { | 456 | { |
406 | return notifier_call_chain(&nh->head, val, v); | 457 | return __raw_notifier_call_chain(nh, val, v, -1, NULL); |
407 | } | 458 | } |
408 | 459 | ||
409 | EXPORT_SYMBOL_GPL(raw_notifier_call_chain); | 460 | EXPORT_SYMBOL_GPL(raw_notifier_call_chain); |
@@ -478,10 +529,12 @@ int srcu_notifier_chain_unregister(struct srcu_notifier_head *nh, | |||
478 | EXPORT_SYMBOL_GPL(srcu_notifier_chain_unregister); | 529 | EXPORT_SYMBOL_GPL(srcu_notifier_chain_unregister); |
479 | 530 | ||
480 | /** | 531 | /** |
481 | * srcu_notifier_call_chain - Call functions in an SRCU notifier chain | 532 | * __srcu_notifier_call_chain - Call functions in an SRCU notifier chain |
482 | * @nh: Pointer to head of the SRCU notifier chain | 533 | * @nh: Pointer to head of the SRCU notifier chain |
483 | * @val: Value passed unmodified to notifier function | 534 | * @val: Value passed unmodified to notifier function |
484 | * @v: Pointer passed unmodified to notifier function | 535 | * @v: Pointer passed unmodified to notifier function |
536 | * @nr_to_call: See comment for notifier_call_chain. | ||
537 | * @nr_calls: See comment for notifier_call_chain | ||
485 | * | 538 | * |
486 | * Calls each function in a notifier chain in turn. The functions | 539 | * Calls each function in a notifier chain in turn. The functions |
487 | * run in a process context, so they are allowed to block. | 540 | * run in a process context, so they are allowed to block. |
@@ -494,18 +547,25 @@ EXPORT_SYMBOL_GPL(srcu_notifier_chain_unregister); | |||
494 | * of the last notifier function called. | 547 | * of the last notifier function called. |
495 | */ | 548 | */ |
496 | 549 | ||
497 | int srcu_notifier_call_chain(struct srcu_notifier_head *nh, | 550 | int __srcu_notifier_call_chain(struct srcu_notifier_head *nh, |
498 | unsigned long val, void *v) | 551 | unsigned long val, void *v, |
552 | int nr_to_call, int *nr_calls) | ||
499 | { | 553 | { |
500 | int ret; | 554 | int ret; |
501 | int idx; | 555 | int idx; |
502 | 556 | ||
503 | idx = srcu_read_lock(&nh->srcu); | 557 | idx = srcu_read_lock(&nh->srcu); |
504 | ret = notifier_call_chain(&nh->head, val, v); | 558 | ret = notifier_call_chain(&nh->head, val, v, nr_to_call, nr_calls); |
505 | srcu_read_unlock(&nh->srcu, idx); | 559 | srcu_read_unlock(&nh->srcu, idx); |
506 | return ret; | 560 | return ret; |
507 | } | 561 | } |
562 | EXPORT_SYMBOL_GPL(__srcu_notifier_call_chain); | ||
508 | 563 | ||
564 | int srcu_notifier_call_chain(struct srcu_notifier_head *nh, | ||
565 | unsigned long val, void *v) | ||
566 | { | ||
567 | return __srcu_notifier_call_chain(nh, val, v, -1, NULL); | ||
568 | } | ||
509 | EXPORT_SYMBOL_GPL(srcu_notifier_call_chain); | 569 | EXPORT_SYMBOL_GPL(srcu_notifier_call_chain); |
510 | 570 | ||
511 | /** | 571 | /** |