mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Keith Owens <kaos@sgi.com>
To: Alan Cox <alan@lxorguk.ukuu.org.uk>
Cc: Andi Kleen <ak@suse.de>, Corey Minyard <minyard@acm.org>,
	Andrew Morton <akpm@osdl.org>,
	paulmck@us.ibm.com, greg@kroah.com, sekharan@us.ibm.com,
	linux-kernel@vger.kernel.org, lse-tech@lists.sourceforge.net,
	Douglas_Warzecha@dell.com, Abhay_Salunke@dell.com,
	achim_leubner@adaptec.com, dmp@davidmpye.dyndns.org
Subject: Re: [Lse-tech] Re: [PATCH 0/7]: Fix for unsafe notifier chain
Date: Wed, 07 Dec 2005 13:43:51 +1100	[thread overview]
Message-ID: <5893.1133923431@kao2.melbourne.sgi.com> (raw)
In-Reply-To: Your message of "Wed, 07 Dec 2005 10:38:44 +1100." <20749.1133912324@ocs3.ocs.com.au>

On Wed, 07 Dec 2005 10:38:44 +1100, 
Keith Owens <kaos@sgi.com> wrote:
>On Sun, 04 Dec 2005 16:19:57 +0000, 
>Alan Cox <alan@lxorguk.ukuu.org.uk> wrote:
>>On Llu, 2005-11-28 at 19:31 +1100, Keith Owens wrote:
>>> >Or just don't unregister. That is what I did for the debug notifiers.
>>> 
>>> Unregister is not the only problem.  Chain traversal races with
>>> register as well.
>>
>>There are some NMI handler registration functions and attempts at safe
>>code for it in the unmerged experimental part of the bluesmoke
>>(bluesmoke.sf.net) project that may be useful perhaps ?
>
>Thanks Alan, the bluesmoke NMI handlers look very similar to the code
>that I have just written.  However bluesmoke only handles a single
>notifier chain, it has only one walking_handler_list array.  The kernel
>is getting to the stage where it needs multiple notifier chains that
>can be traversed without locks.  The patch below against 2.6.15-rc5
>gives us lockfree traversal of notifier chains and supports multiple
>chains.

My previous patch was way too complicated, this is much simpler.  Based
on Corey Minyard's patch of http://lkml.org/lkml/2004/8/19/140,
generalized to support multiple lockfree notifier chains, with a few
extra synchronization calls added.

Again, for review only.  Compiled but not tested yet.

 include/linux/notifier.h |    7 +++
 kernel/sys.c             |  105 +++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 112 insertions(+)

Index: linux/include/linux/notifier.h
===================================================================
--- linux.orig/include/linux/notifier.h	2005-12-07 12:00:57.569908107 +1100
+++ linux/include/linux/notifier.h	2005-12-07 13:07:18.643526153 +1100
@@ -24,6 +24,13 @@ struct notifier_block
 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);
+static inline int
+notifier_chain_register_lockfree(struct notifier_block **list, struct notifier_block *n)
+{
+	return notifier_chain_register(list, n);
+}
+extern int notifier_chain_unregister_lockfree(struct notifier_block **nl, struct notifier_block *n);
+extern int notifier_call_chain_lockfree(struct notifier_block **n, unsigned long val, void *v);
 
 #define NOTIFY_DONE		0x0000		/* Don't care */
 #define NOTIFY_OK		0x0001		/* Suits me */
Index: linux/kernel/sys.c
===================================================================
--- linux.orig/kernel/sys.c	2005-12-07 12:00:57.570884536 +1100
+++ linux/kernel/sys.c	2005-12-07 13:37:53.773534284 +1100
@@ -116,6 +116,7 @@ int notifier_chain_register(struct notif
 		list= &((*list)->next);
 	}
 	n->next = *list;
+	smp_wmb();
 	*list=n;
 	write_unlock(&notifier_lock);
 	return 0;
@@ -187,6 +188,110 @@ int notifier_call_chain(struct notifier_
 
 EXPORT_SYMBOL(notifier_call_chain);
 
+/* The notifier_chain_*_lockfree functions below are based on the formal
+ * notifier_chain_* functions above, but allow the notifier chain to be
+ * traversed in situations where locks cannot be taken to protect the list,
+ * typically in the various notifier_die() handlers.  The 'lockfree' suffix
+ * only refers to the list traversal; register and unregister still take locks
+ * to protect against concurrent list update.  Register and unregister can only
+ * be called from contexts that can sleep.
+ */
+
+/* Array notifier_chain_lockfree_inuse is shared between all lockfree notifier
+ * chains.   Unregistration of any chain entry must be delayed if any cpu is
+ * executing a lockfree callback, even if that callback is on a different
+ * chain.  No big deal, unregister is a rare event.
+ *
+ * Each element is only updated from one cpu so the elements do not need to be
+ * atomic.  This avoids problems on architectures that use a hash of spinlocks
+ * to implement atomic variables.
+ *
+ * This array could be replaced by a per cpu variable, but cpu hotplug may want
+ * to use these functions.  Before converting to a per cpu variable, review the
+ * cpu hotplug code, paying particular attention to where cpu_online() is set
+ * or cleared and where cpu hotplug runs any notify chains.  For the same
+ * reason, these functions do not check cpu_online() at the moment.
+ */
+
+static int notifier_chain_lockfree_inuse[NR_CPUS];
+
+/**
+ *	notifier_chain_unregister_lockfree - Remove notifier from a lockfree
+ *	traversal notifier chain
+ *	@list: Pointer to root list pointer
+ *	@n: New entry in notifier chain
+ *
+ *	Removes a notifier from a lockfree traversal notifier chain.
+ *
+ *	Returns zero on success, or %-ENOENT on failure.
+ */
+
+int notifier_chain_unregister_lockfree(struct notifier_block **list,
+				       struct notifier_block *n)
+{
+	int i;
+	write_lock(&notifier_lock);
+	while (*list) {
+		if (*list == n) {
+			*list = n->next;
+			smp_wmb();
+			for (i = 0; i < NR_CPUS; ++i) {
+				while (unlikely(notifier_chain_lockfree_inuse[i])) {
+					barrier();
+					cpu_relax();
+				}
+			}
+			n->next = NULL;
+			write_unlock(&notifier_lock);
+			return 0;
+		}
+		list = &((*list)->next);
+	}
+	write_unlock(&notifier_lock);
+	return -ENOENT;
+}
+
+EXPORT_SYMBOL(notifier_chain_unregister_lockfree);
+
+/**
+ *	notifier_call_chain_lockfree - Call functions in a lockfree traversal
+ *	notifier chain
+ *	@list: Pointer to root pointer of notifier chain
+ *	@val: Value passed unmodified to notifier function
+ *	@v: Pointer passed unmodified to notifier function
+ *
+ *	Calls each function in a lockfree traversal notifier chain in turn.
+ *
+ *	If the return value of the notifier can be and'd with
+ *	%NOTIFY_STOP_MASK, then notifier_call_chain 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 notifier_call_chain_lockfree(struct notifier_block **list,
+				 unsigned long val, void *v)
+{
+	int ret = NOTIFY_DONE, cpu = smp_processor_id(), nested;
+	struct notifier_block *nb;
+	nested = notifier_chain_lockfree_inuse[cpu];
+	notifier_chain_lockfree_inuse[cpu] = 1;
+	wmb();
+	nb = *list;
+	while (nb) {
+		smp_read_barrier_depends();
+		ret = nb->notifier_call(nb, val, v);
+		if (ret & NOTIFY_STOP_MASK)
+			break;
+		nb = nb->next;
+	}
+	barrier();
+	notifier_chain_lockfree_inuse[cpu] = nested;
+	return ret;
+}
+
+EXPORT_SYMBOL(notifier_call_chain_lockfree);
+
 /**
  *	register_reboot_notifier - Register function to be called at reboot time
  *	@nb: Info about notifier function to be called


  reply	other threads:[~2005-12-07  2:46 UTC|newest]

Thread overview: 21+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2005-11-23 23:37 Chandra Seetharaman
2005-11-27  4:07 ` Andrew Morton
2005-11-27 13:47   ` [Lse-tech] " Andi Kleen
2005-11-27 15:59     ` Keith Owens
2005-11-27 17:27       ` Andi Kleen
2005-11-27 17:39         ` Keith Owens
2005-11-27 19:56           ` Andrew Morton
2005-11-27 22:03             ` Greg KH
2005-11-28  2:43               ` Paul E. McKenney
2005-11-28  4:57                 ` Andrew Morton
2005-11-28  4:59                   ` Andi Kleen
2005-11-28  5:05                     ` Paul E. McKenney
2005-11-28  5:15                       ` Andi Kleen
2005-11-28  8:31                     ` Keith Owens
2005-11-28 12:07                       ` Andi Kleen
2005-11-28 19:55                         ` Paul E. McKenney
2005-12-04 16:19                       ` Alan Cox
2005-12-06 23:38                         ` Keith Owens
2005-12-07  2:43                           ` Keith Owens [this message]
2005-11-28  1:19             ` Keith Owens
2005-11-28 18:58   ` Chandra Seetharaman

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=5893.1133923431@kao2.melbourne.sgi.com \
    --to=kaos@sgi.com \
    --cc=Abhay_Salunke@dell.com \
    --cc=Douglas_Warzecha@dell.com \
    --cc=achim_leubner@adaptec.com \
    --cc=ak@suse.de \
    --cc=akpm@osdl.org \
    --cc=alan@lxorguk.ukuu.org.uk \
    --cc=dmp@davidmpye.dyndns.org \
    --cc=greg@kroah.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=lse-tech@lists.sourceforge.net \
    --cc=minyard@acm.org \
    --cc=paulmck@us.ibm.com \
    --cc=sekharan@us.ibm.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

Powered by JetHome