Guys:
Here's an example patch showing how the existing notifier chain code can
be reimplemented in a safe manner (and how the code's appearance can be
improved!).
I haven't tested this, although kernel/sys.c source file compiles okay. A
complete patch would have to adjust the definitions of all 22 existing
notifier chains.
Is this worth pursuing?
Alan Stern
Index: usb-2.6/kernel/sys.c
===================================================================
--- usb-2.6.orig/kernel/sys.c
+++ usb-2.6/kernel/sys.c
@@ -92,68 +92,86 @@ int cad_pid = 1;
* and the like.
*/
-static struct notifier_block *reboot_notifier_list;
-static DEFINE_RWLOCK(notifier_lock);
+static DEFINE_NOTIFIER_HEAD(reboot_notifier_list);
/**
* notifier_chain_register - Add notifier to a notifier chain
- * @list: Pointer to root list pointer
+ * @nh: Pointer to head of the notifier chain
* @n: New entry in notifier chain
*
* Adds a notifier to a notifier chain.
*
* Currently always returns zero.
*/
-
-int notifier_chain_register(struct notifier_block **list, struct
notifier_block *n)
+
+int notifier_chain_register(struct notifier_head *nh,
+ struct notifier_block *n)
{
- write_lock(¬ifier_lock);
- while(*list)
- {
- if(n->priority > (*list)->priority)
+ struct notifier_block **p;
+
+ spin_lock(&nh->lock);
+ for (p = &nh->first; *p; p = &((*p)->next)) {
+ if (n->priority > (*p)->priority)
break;
- list= &((*list)->next);
}
- n->next = *list;
- *list=n;
- write_unlock(¬ifier_lock);
+ n->next = *p;
+ *p = n;
+ spin_unlock(&nh->lock);
return 0;
}
EXPORT_SYMBOL(notifier_chain_register);
+struct notifier_caller {
+ struct notifier_block *next;
+ struct list_head node;
+};
+
/**
* notifier_chain_unregister - Remove notifier from a notifier chain
- * @nl: Pointer to root list pointer
- * @n: New entry in notifier chain
+ * @nh: Pointer to head of the notifier chain
+ * @n: Entry to remove from the chain
*
* Removes a notifier from a notifier chain.
*
* Returns zero on success, or %-ENOENT on failure.
*/
-int notifier_chain_unregister(struct notifier_block **nl, struct
notifier_block *n)
+int notifier_chain_unregister(struct notifier_head *nh,
+ struct notifier_block *n)
{
- write_lock(¬ifier_lock);
- while((*nl)!=NULL)
- {
- if((*nl)==n)
- {
- *nl=n->next;
- write_unlock(¬ifier_lock);
- return 0;
- }
- nl=&((*nl)->next);
+ int ret = 0;
+ struct notifier_block **p;
+ struct list_head *pos;
+ struct notifier_caller *caller;
+
+ spin_lock(&nh->lock);
+ for (p = &nh->first; *p; p = &((*p)->next)) {
+ if (*p == n)
+ break;
}
- write_unlock(¬ifier_lock);
- return -ENOENT;
+ if (!*p) {
+ ret = -ENOENT;
+ goto done;
+ }
+ *p = n->next;
+
+ list_for_each(pos, &nh->callers) {
+ caller = list_entry(pos, struct notifier_caller, node);
+ if (caller->next == n)
+ caller->next = n->next;
+ }
+
+done:
+ spin_unlock(&nh->lock);
+ return ret;
}
EXPORT_SYMBOL(notifier_chain_unregister);
/**
* notifier_call_chain - Call functions in a notifier chain
- * @n: Pointer to root pointer of notifier chain
+ * @nh: Pointer to head of the notifier chain
* @val: Value passed unmodified to notifier function
* @v: Pointer passed unmodified to notifier function
*
@@ -167,20 +185,28 @@ EXPORT_SYMBOL(notifier_chain_unregister)
* of the last notifier function called.
*/
-int notifier_call_chain(struct notifier_block **n, unsigned long val, void *v)
+int notifier_call_chain(struct notifier_head *nh, unsigned long val, void *v)
{
- int ret=NOTIFY_DONE;
- struct notifier_block *nb = *n;
+ int ret = NOTIFY_DONE;
+ struct notifier_caller caller;
+ struct notifier_block *n;
- while(nb)
- {
- ret=nb->notifier_call(nb,val,v);
- if(ret&NOTIFY_STOP_MASK)
- {
- return ret;
- }
- nb=nb->next;
+ spin_lock(&nh->lock);
+ caller.next = nh->first;
+ list_add(&caller.node, &nh->callers);
+
+ while (caller.next) {
+ n = caller.next;
+ caller.next = n->next;
+ spin_unlock(&nh->lock);
+ ret = n->notifier_call(n, val, v);
+ spin_lock(&nh->lock);
+ if (ret & NOTIFY_STOP_MASK)
+ break;
}
+
+ list_del(&caller.node);
+ spin_unlock(&nh->lock);
return ret;
}
Index: usb-2.6/include/linux/notifier.h
===================================================================
--- usb-2.6.orig/include/linux/notifier.h
+++ usb-2.6/include/linux/notifier.h
@@ -6,10 +6,20 @@
*
* Alan Cox <[EMAIL PROTECTED]>
*/
-
+
+/*
+ * This implementation allows entries to be safely added to or removed
+ * from a notifier chain at any time (in a context that can sleep),
+ * including while they are running or another process is calling
+ * along the chain. The main assumption is that the number of
+ * simultaneous calls for a chain will be fairly small.
+ */
+
#ifndef _LINUX_NOTIFIER_H
#define _LINUX_NOTIFIER_H
#include <linux/errno.h>
+#include <linux/spinlock.h>
+#include <linux/list.h>
struct notifier_block
{
@@ -18,12 +28,27 @@ struct notifier_block
int priority;
};
+struct notifier_head {
+ spinlock_t lock;
+ struct notifier_block *first;
+ struct list_head callers;
+};
+
+#define NOTIFIER_HEAD_INIT(name) { .lock = SPIN_LOCK_UNLOCKED, \
+ .first = NULL, .callers = LIST_HEAD_INIT((name).callers) }
+
+#define DEFINE_NOTIFIER_HEAD(name) \
+ struct notifier_head name = NOTIFIER_HEAD_INIT(name)
+
#ifdef __KERNEL__
-extern int notifier_chain_register(struct notifier_block **list, struct
notifier_block *n);
-extern int notifier_chain_unregister(struct notifier_block **nl, struct
notifier_block *n);
-extern int notifier_call_chain(struct notifier_block **n, unsigned long val,
void *v);
+extern int notifier_chain_register(struct notifier_head *nh,
+ struct notifier_block *n);
+extern int notifier_chain_unregister(struct notifier_head *nh,
+ struct notifier_block *n);
+extern int notifier_call_chain(struct notifier_head *nh,
+ unsigned long val, void *v);
#define NOTIFY_DONE 0x0000 /* Don't care */
#define NOTIFY_OK 0x0001 /* Suits me */
-------------------------------------------------------
This SF.Net email is sponsored by:
Power Architecture Resource Center: Free content, downloads, discussions,
and more. http://solutions.newsforge.com/ibmarch.tmpl
_______________________________________________
[email protected]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel