[RFC PATCH 3/6] notifiers: Add support for reverse invocation ofnotifier chains

From: Srivatsa S. Bhat
Date: Wed Jul 25 2012 - 07:54:17 EST


In certain scenarios, it is useful to be able to invoke notifiers in the
reverse order. One such example is CPU hotplug, where we would like to
invoke the notifiers in one order during CPU online and in the reverse
order during CPU offline. So add support for reverse invocation of
notifier chains.

Signed-off-by: Srivatsa S. Bhat <srivatsa.bhat@xxxxxxxxxxxxxxxxxx>
---

include/linux/notifier.h | 4 ++
kernel/notifier.c | 82 ++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 86 insertions(+), 0 deletions(-)

diff --git a/include/linux/notifier.h b/include/linux/notifier.h
index 67f9a3a..4626d17 100644
--- a/include/linux/notifier.h
+++ b/include/linux/notifier.h
@@ -145,8 +145,12 @@ 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);
+extern int raw_notifier_call_chain_reverse(struct raw_notifier_head *nh,
+ unsigned long val, void *v);
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 __raw_notifier_call_chain_reverse(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);
extern int __srcu_notifier_call_chain(struct srcu_notifier_head *nh,
diff --git a/kernel/notifier.c b/kernel/notifier.c
index ad6feab..536f32c 100644
--- a/kernel/notifier.c
+++ b/kernel/notifier.c
@@ -105,6 +105,50 @@ static int __kprobes notifier_call_chain(struct list_head *nl,
return ret;
}

+/**
+ * notifier_call_chain_reverse - Informs the registered notifiers about an
+ * event, by invoking the notifiers in the reverse order.
+ *
+ * @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_reverse returns the value returned
+ * by the last notifier function called.
+ */
+static int __kprobes notifier_call_chain_reverse(struct list_head *nl,
+ unsigned long val, void *v,
+ int nr_to_call, int *nr_calls)
+{
+ int ret = NOTIFY_DONE;
+ struct notifier_block *nb;
+
+ list_for_each_entry_reverse_rcu(nb, nl, list) {
+ if (!nr_to_call)
+ break;
+
+#ifdef CONFIG_DEBUG_NOTIFIERS
+ if (unlikely(!func_ptr_is_kernel_text(nb->notifier_call))) {
+ WARN(1, "Invalid notifier called!");
+ continue;
+ }
+#endif
+ ret = nb->notifier_call(nb, val, v);
+
+ if (nr_calls)
+ (*nr_calls)++;
+
+ if ((ret & NOTIFY_STOP_MASK) == NOTIFY_STOP_MASK)
+ break;
+ nr_to_call--;
+ }
+
+ return ret;
+}
+
/*
* Atomic notifier chain routines. Registration and unregistration
* use a spinlock, and call_chain is synchronized by RCU (no locks).
@@ -402,6 +446,44 @@ int raw_notifier_call_chain(struct raw_notifier_head *nh,
}
EXPORT_SYMBOL_GPL(raw_notifier_call_chain);

+
+/**
+ * __raw_notifier_call_chain_reverse - Call functions in a raw notifier
+ * chain in the reverse order
+ *
+ * @nh: Pointer to head of the raw notifier chain
+ * @val: Value passed unmodified to notifier function
+ * @v: Pointer passed unmodified to notifier function
+ * @nr_to_call: See comment for notifier_call_chain_reverse.
+ * @nr_calls: See comment for notifier_call_chain_reverse
+ *
+ * Calls each function in a notifier chain in turn, in the reverse order.
+ * The functions run in an undefined context.
+ * All locking must be provided by the caller.
+ *
+ * If the return value of the notifier can be and'ed
+ * with %NOTIFY_STOP_MASK then __raw_notifier_call_chain_reverse()
+ * will return immediately, with the return value of
+ * the notifier function which halted execution.
+ * Otherwise the return value is the return value
+ * of the last notifier function called.
+ */
+int __raw_notifier_call_chain_reverse(struct raw_notifier_head *nh,
+ unsigned long val, void *v,
+ int nr_to_call, int *nr_calls)
+{
+ return notifier_call_chain_reverse(&nh->list, val, v,
+ nr_to_call, nr_calls);
+}
+EXPORT_SYMBOL_GPL(__raw_notifier_call_chain_reverse);
+
+int raw_notifier_call_chain_reverse(struct raw_notifier_head *nh,
+ unsigned long val, void *v)
+{
+ return __raw_notifier_call_chain_reverse(nh, val, v, -1, NULL);
+}
+EXPORT_SYMBOL_GPL(raw_notifier_call_chain_reverse);
+
/*
* SRCU notifier chain routines. Registration and unregistration
* use a mutex, and call_chain is synchronized by SRCU (no locks).

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/