Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 20 Mar 2008 21:32:25 -0700
From:      Alfred Perlstein <alfred@freebsd.org>
To:        smp@freebsd.org
Subject:   request for review, callout fix.
Message-ID:  <20080321043225.GZ67856@elvis.mu.org>

next in thread | raw e-mail | index | archive | help

--6lXr1rPCNTf1w0X8
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline

Hi guys, attached is a fix for the timeout/untimeout race with
Giant locked code.

Basically the old code would make the callout inaccessable
right before calling it inside of softclock.

However only the callout lock is held, so when switching to
the callout's associated mutex (in this case Giant) there's
a race where a "local" (timeout/untimeout) callout would be
fired even if stopped.

This patch fixes that.  We've run several hours of regression
testing on a version of this for 6.x.

People internal to Juniper and iedowse@ helped with this.

Please review/comment.

thank you,
-- 
- Alfred Perlstein

--6lXr1rPCNTf1w0X8
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment; filename="kern_timeout.diff"

cvs diff: Diffing .
Index: kern_timeout.c
===================================================================
RCS file: /home/ncvs/src/sys/kern/kern_timeout.c,v
retrieving revision 1.110
diff -u -r1.110 kern_timeout.c
--- kern_timeout.c	12 Mar 2008 06:31:06 -0000	1.110
+++ kern_timeout.c	21 Mar 2008 04:28:35 -0000
@@ -230,11 +230,8 @@
 				c_arg = c->c_arg;
 				c_flags = c->c_flags;
 				if (c->c_flags & CALLOUT_LOCAL_ALLOC) {
-					c->c_func = NULL;
 					c->c_flags = CALLOUT_LOCAL_ALLOC;
-					SLIST_INSERT_HEAD(&callfree, c,
-							  c_links.sle);
-					curr_callout = NULL;
+					curr_callout = c;
 				} else {
 					c->c_flags =
 					    (c->c_flags & ~CALLOUT_PENDING);
@@ -299,6 +296,24 @@
 					class->lc_unlock(c_lock);
 			skip:
 				mtx_lock_spin(&callout_lock);
+				/*
+				 * If the current callout is locally
+				 * allocated (timeout(9)) and if it has not
+				 * been killed by untimeout(9)
+				 * then put it on the freelist.
+				 *
+				 * Note: we need to check the cached
+				 * copy of c_flags because if it was not
+				 * local, then it's not safe to deref the
+				 * callout pointer.
+				 */
+				if (c_flags & CALLOUT_LOCAL_ALLOC &&
+				    !(c->c_flags &
+					(CALLOUT_PENDING | CALLOUT_ACTIVE))) {
+					c->c_func = NULL;
+					SLIST_INSERT_HEAD(&callfree, c,
+					    c_links.sle);
+				}
 				curr_callout = NULL;
 				if (callout_wait) {
 					/*

--6lXr1rPCNTf1w0X8--



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20080321043225.GZ67856>