Commit 6f7cc11a authored by Gautham R Shenoy's avatar Gautham R Shenoy Committed by Linus Torvalds

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: default avatarGautham R Shenoy <ego@in.ibm.com>
Signed-off-by: default avatarMichal Piotrowski <michal.k.k.piotrowski@gmail.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 7c9cb383
...@@ -112,32 +112,40 @@ extern void srcu_init_notifier_head(struct srcu_notifier_head *nh); ...@@ -112,32 +112,40 @@ extern void srcu_init_notifier_head(struct srcu_notifier_head *nh);
#ifdef __KERNEL__ #ifdef __KERNEL__
extern int atomic_notifier_chain_register(struct atomic_notifier_head *, extern int atomic_notifier_chain_register(struct atomic_notifier_head *nh,
struct notifier_block *); struct notifier_block *nb);
extern int blocking_notifier_chain_register(struct blocking_notifier_head *, extern int blocking_notifier_chain_register(struct blocking_notifier_head *nh,
struct notifier_block *); struct notifier_block *nb);
extern int raw_notifier_chain_register(struct raw_notifier_head *, extern int raw_notifier_chain_register(struct raw_notifier_head *nh,
struct notifier_block *); struct notifier_block *nb);
extern int srcu_notifier_chain_register(struct srcu_notifier_head *, extern int srcu_notifier_chain_register(struct srcu_notifier_head *nh,
struct notifier_block *); struct notifier_block *nb);
extern int atomic_notifier_chain_unregister(struct atomic_notifier_head *, extern int atomic_notifier_chain_unregister(struct atomic_notifier_head *nh,
struct notifier_block *); struct notifier_block *nb);
extern int blocking_notifier_chain_unregister(struct blocking_notifier_head *, extern int blocking_notifier_chain_unregister(struct blocking_notifier_head *nh,
struct notifier_block *); struct notifier_block *nb);
extern int raw_notifier_chain_unregister(struct raw_notifier_head *, extern int raw_notifier_chain_unregister(struct raw_notifier_head *nh,
struct notifier_block *); struct notifier_block *nb);
extern int srcu_notifier_chain_unregister(struct srcu_notifier_head *, extern int srcu_notifier_chain_unregister(struct srcu_notifier_head *nh,
struct notifier_block *); struct notifier_block *nb);
extern int atomic_notifier_call_chain(struct atomic_notifier_head *, extern int atomic_notifier_call_chain(struct atomic_notifier_head *nh,
unsigned long val, void *v); unsigned long val, void *v);
extern int blocking_notifier_call_chain(struct blocking_notifier_head *, extern int __atomic_notifier_call_chain(struct atomic_notifier_head *nh,
unsigned long val, void *v, int nr_to_call, int *nr_calls);
extern int blocking_notifier_call_chain(struct blocking_notifier_head *nh,
unsigned long val, void *v); unsigned long val, void *v);
extern int raw_notifier_call_chain(struct raw_notifier_head *, extern int __blocking_notifier_call_chain(struct blocking_notifier_head *nh,
unsigned long val, void *v, int nr_to_call, int *nr_calls);
extern int raw_notifier_call_chain(struct raw_notifier_head *nh,
unsigned long val, void *v); unsigned long val, void *v);
extern int srcu_notifier_call_chain(struct srcu_notifier_head *, extern int __raw_notifier_call_chain(struct raw_notifier_head *nh,
unsigned long val, void *v, int nr_to_call, int *nr_calls);
extern int srcu_notifier_call_chain(struct srcu_notifier_head *nh,
unsigned long val, void *v); unsigned long val, void *v);
extern int __srcu_notifier_call_chain(struct srcu_notifier_head *nh,
unsigned long val, void *v, int nr_to_call, int *nr_calls);
#define NOTIFY_DONE 0x0000 /* Don't care */ #define NOTIFY_DONE 0x0000 /* Don't care */
#define NOTIFY_OK 0x0001 /* Suits me */ #define NOTIFY_OK 0x0001 /* Suits me */
......
...@@ -134,19 +134,39 @@ static int notifier_chain_unregister(struct notifier_block **nl, ...@@ -134,19 +134,39 @@ static int notifier_chain_unregister(struct notifier_block **nl,
return -ENOENT; return -ENOENT;
} }
/**
* notifier_call_chain - Informs the registered notifiers about an event.
* @nl: Pointer to head of the blocking notifier chain
* @val: Value passed unmodified to notifier function
* @v: Pointer passed unmodified to notifier function
* @nr_to_call: Number of notifier functions to be called. Don't care
* value of this parameter is -1.
* @nr_calls: Records the number of notifications sent. Don't care
* value of this field is NULL.
* @returns: notifier_call_chain returns the value returned by the
* last notifier function called.
*/
static int __kprobes notifier_call_chain(struct notifier_block **nl, static int __kprobes notifier_call_chain(struct notifier_block **nl,
unsigned long val, void *v) unsigned long val, void *v,
int nr_to_call, int *nr_calls)
{ {
int ret = NOTIFY_DONE; int ret = NOTIFY_DONE;
struct notifier_block *nb, *next_nb; struct notifier_block *nb, *next_nb;
nb = rcu_dereference(*nl); nb = rcu_dereference(*nl);
while (nb) {
while (nb && nr_to_call) {
next_nb = rcu_dereference(nb->next); next_nb = rcu_dereference(nb->next);
ret = nb->notifier_call(nb, val, v); ret = nb->notifier_call(nb, val, v);
if (nr_calls)
(*nr_calls)++;
if ((ret & NOTIFY_STOP_MASK) == NOTIFY_STOP_MASK) if ((ret & NOTIFY_STOP_MASK) == NOTIFY_STOP_MASK)
break; break;
nb = next_nb; nb = next_nb;
nr_to_call--;
} }
return ret; return ret;
} }
...@@ -205,10 +225,12 @@ int atomic_notifier_chain_unregister(struct atomic_notifier_head *nh, ...@@ -205,10 +225,12 @@ int atomic_notifier_chain_unregister(struct atomic_notifier_head *nh,
EXPORT_SYMBOL_GPL(atomic_notifier_chain_unregister); EXPORT_SYMBOL_GPL(atomic_notifier_chain_unregister);
/** /**
* atomic_notifier_call_chain - Call functions in an atomic notifier chain * __atomic_notifier_call_chain - Call functions in an atomic notifier chain
* @nh: Pointer to head of the atomic notifier chain * @nh: Pointer to head of the atomic notifier chain
* @val: Value passed unmodified to notifier function * @val: Value passed unmodified to notifier function
* @v: Pointer passed unmodified to notifier function * @v: Pointer passed unmodified to notifier function
* @nr_to_call: See the comment for notifier_call_chain.
* @nr_calls: See the comment for notifier_call_chain.
* *
* Calls each function in a notifier chain in turn. The functions * Calls each function in a notifier chain in turn. The functions
* run in an atomic context, so they must not block. * run in an atomic context, so they must not block.
...@@ -222,19 +244,27 @@ EXPORT_SYMBOL_GPL(atomic_notifier_chain_unregister); ...@@ -222,19 +244,27 @@ EXPORT_SYMBOL_GPL(atomic_notifier_chain_unregister);
* of the last notifier function called. * of the last notifier function called.
*/ */
int __kprobes atomic_notifier_call_chain(struct atomic_notifier_head *nh, int __kprobes __atomic_notifier_call_chain(struct atomic_notifier_head *nh,
unsigned long val, void *v) unsigned long val, void *v,
int nr_to_call, int *nr_calls)
{ {
int ret; int ret;
rcu_read_lock(); rcu_read_lock();
ret = notifier_call_chain(&nh->head, val, v); ret = notifier_call_chain(&nh->head, val, v, nr_to_call, nr_calls);
rcu_read_unlock(); rcu_read_unlock();
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(atomic_notifier_call_chain); EXPORT_SYMBOL_GPL(__atomic_notifier_call_chain);
int __kprobes atomic_notifier_call_chain(struct atomic_notifier_head *nh,
unsigned long val, void *v)
{
return __atomic_notifier_call_chain(nh, val, v, -1, NULL);
}
EXPORT_SYMBOL_GPL(atomic_notifier_call_chain);
/* /*
* Blocking notifier chain routines. All access to the chain is * Blocking notifier chain routines. All access to the chain is
* synchronized by an rwsem. * synchronized by an rwsem.
...@@ -304,10 +334,12 @@ int blocking_notifier_chain_unregister(struct blocking_notifier_head *nh, ...@@ -304,10 +334,12 @@ int blocking_notifier_chain_unregister(struct blocking_notifier_head *nh,
EXPORT_SYMBOL_GPL(blocking_notifier_chain_unregister); EXPORT_SYMBOL_GPL(blocking_notifier_chain_unregister);
/** /**
* blocking_notifier_call_chain - Call functions in a blocking notifier chain * __blocking_notifier_call_chain - Call functions in a blocking notifier chain
* @nh: Pointer to head of the blocking notifier chain * @nh: Pointer to head of the blocking notifier chain
* @val: Value passed unmodified to notifier function * @val: Value passed unmodified to notifier function
* @v: Pointer passed unmodified to notifier function * @v: Pointer passed unmodified to notifier function
* @nr_to_call: See comment for notifier_call_chain.
* @nr_calls: See comment for notifier_call_chain.
* *
* Calls each function in a notifier chain in turn. The functions * Calls each function in a notifier chain in turn. The functions
* run in a process context, so they are allowed to block. * run in a process context, so they are allowed to block.
...@@ -320,8 +352,9 @@ EXPORT_SYMBOL_GPL(blocking_notifier_chain_unregister); ...@@ -320,8 +352,9 @@ EXPORT_SYMBOL_GPL(blocking_notifier_chain_unregister);
* of the last notifier function called. * of the last notifier function called.
*/ */
int blocking_notifier_call_chain(struct blocking_notifier_head *nh, int __blocking_notifier_call_chain(struct blocking_notifier_head *nh,
unsigned long val, void *v) unsigned long val, void *v,
int nr_to_call, int *nr_calls)
{ {
int ret = NOTIFY_DONE; int ret = NOTIFY_DONE;
...@@ -332,12 +365,19 @@ int blocking_notifier_call_chain(struct blocking_notifier_head *nh, ...@@ -332,12 +365,19 @@ int blocking_notifier_call_chain(struct blocking_notifier_head *nh,
*/ */
if (rcu_dereference(nh->head)) { if (rcu_dereference(nh->head)) {
down_read(&nh->rwsem); down_read(&nh->rwsem);
ret = notifier_call_chain(&nh->head, val, v); ret = notifier_call_chain(&nh->head, val, v, nr_to_call,
nr_calls);
up_read(&nh->rwsem); up_read(&nh->rwsem);
} }
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(__blocking_notifier_call_chain);
int blocking_notifier_call_chain(struct blocking_notifier_head *nh,
unsigned long val, void *v)
{
return __blocking_notifier_call_chain(nh, val, v, -1, NULL);
}
EXPORT_SYMBOL_GPL(blocking_notifier_call_chain); EXPORT_SYMBOL_GPL(blocking_notifier_call_chain);
/* /*
...@@ -383,10 +423,12 @@ int raw_notifier_chain_unregister(struct raw_notifier_head *nh, ...@@ -383,10 +423,12 @@ int raw_notifier_chain_unregister(struct raw_notifier_head *nh,
EXPORT_SYMBOL_GPL(raw_notifier_chain_unregister); EXPORT_SYMBOL_GPL(raw_notifier_chain_unregister);
/** /**
* raw_notifier_call_chain - Call functions in a raw notifier chain * __raw_notifier_call_chain - Call functions in a raw notifier chain
* @nh: Pointer to head of the raw notifier chain * @nh: Pointer to head of the raw notifier chain
* @val: Value passed unmodified to notifier function * @val: Value passed unmodified to notifier function
* @v: Pointer passed unmodified to notifier function * @v: Pointer passed unmodified to notifier function
* @nr_to_call: See comment for notifier_call_chain.
* @nr_calls: See comment for notifier_call_chain
* *
* Calls each function in a notifier chain in turn. The functions * Calls each function in a notifier chain in turn. The functions
* run in an undefined context. * run in an undefined context.
...@@ -400,10 +442,19 @@ EXPORT_SYMBOL_GPL(raw_notifier_chain_unregister); ...@@ -400,10 +442,19 @@ EXPORT_SYMBOL_GPL(raw_notifier_chain_unregister);
* of the last notifier function called. * of the last notifier function called.
*/ */
int __raw_notifier_call_chain(struct raw_notifier_head *nh,
unsigned long val, void *v,
int nr_to_call, int *nr_calls)
{
return notifier_call_chain(&nh->head, val, v, nr_to_call, nr_calls);
}
EXPORT_SYMBOL_GPL(__raw_notifier_call_chain);
int raw_notifier_call_chain(struct raw_notifier_head *nh, int raw_notifier_call_chain(struct raw_notifier_head *nh,
unsigned long val, void *v) unsigned long val, void *v)
{ {
return notifier_call_chain(&nh->head, val, v); return __raw_notifier_call_chain(nh, val, v, -1, NULL);
} }
EXPORT_SYMBOL_GPL(raw_notifier_call_chain); EXPORT_SYMBOL_GPL(raw_notifier_call_chain);
...@@ -478,10 +529,12 @@ int srcu_notifier_chain_unregister(struct srcu_notifier_head *nh, ...@@ -478,10 +529,12 @@ int srcu_notifier_chain_unregister(struct srcu_notifier_head *nh,
EXPORT_SYMBOL_GPL(srcu_notifier_chain_unregister); EXPORT_SYMBOL_GPL(srcu_notifier_chain_unregister);
/** /**
* srcu_notifier_call_chain - Call functions in an SRCU notifier chain * __srcu_notifier_call_chain - Call functions in an SRCU notifier chain
* @nh: Pointer to head of the SRCU notifier chain * @nh: Pointer to head of the SRCU notifier chain
* @val: Value passed unmodified to notifier function * @val: Value passed unmodified to notifier function
* @v: Pointer passed unmodified to notifier function * @v: Pointer passed unmodified to notifier function
* @nr_to_call: See comment for notifier_call_chain.
* @nr_calls: See comment for notifier_call_chain
* *
* Calls each function in a notifier chain in turn. The functions * Calls each function in a notifier chain in turn. The functions
* run in a process context, so they are allowed to block. * run in a process context, so they are allowed to block.
...@@ -494,18 +547,25 @@ EXPORT_SYMBOL_GPL(srcu_notifier_chain_unregister); ...@@ -494,18 +547,25 @@ EXPORT_SYMBOL_GPL(srcu_notifier_chain_unregister);
* of the last notifier function called. * of the last notifier function called.
*/ */
int srcu_notifier_call_chain(struct srcu_notifier_head *nh, int __srcu_notifier_call_chain(struct srcu_notifier_head *nh,
unsigned long val, void *v) unsigned long val, void *v,
int nr_to_call, int *nr_calls)
{ {
int ret; int ret;
int idx; int idx;
idx = srcu_read_lock(&nh->srcu); idx = srcu_read_lock(&nh->srcu);
ret = notifier_call_chain(&nh->head, val, v); ret = notifier_call_chain(&nh->head, val, v, nr_to_call, nr_calls);
srcu_read_unlock(&nh->srcu, idx); srcu_read_unlock(&nh->srcu, idx);
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(__srcu_notifier_call_chain);
int srcu_notifier_call_chain(struct srcu_notifier_head *nh,
unsigned long val, void *v)
{
return __srcu_notifier_call_chain(nh, val, v, -1, NULL);
}
EXPORT_SYMBOL_GPL(srcu_notifier_call_chain); EXPORT_SYMBOL_GPL(srcu_notifier_call_chain);
/** /**
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment