Implement msleep(). This function is similar to the FreeBSD msleep() except
authorMatthew Dillon <dillon@dragonflybsd.org>
Sat, 27 May 2006 01:51:27 +0000 (01:51 +0000)
committerMatthew Dillon <dillon@dragonflybsd.org>
Sat, 27 May 2006 01:51:27 +0000 (01:51 +0000)
it interlocks with a spinlock instead of a mutex.  The spinlock must be
exclusively held on entry.  msleep() will atomically sleep and release the
spinlock, then reacquire the spinlock when it wakes up.

A novel approach to the interlock is used.  DragonFly's tsleep/wakeup
mechanism is a per-cpu mechanism, with a local array of cpu masks, one
entry per hash index.  A wakeup simpy sends an IPI message to each target
cpu whos bitmap bit is set in the ident's hash entry.

This allows us to interlock simply by entering a critical section and
setting our bit, then releasing the mutex, then tsleep()ing as per normal.
No additional locks are required.  The critical section will delay any wakeup
race with us simply by delaying the IPI message that is potentially
in-transit to our cpu.

Requested-by: Numerous people, and its time has come now.
sys/kern/kern_synch.c
sys/sys/systm.h

index cd602e6..8d9bf1e 100644 (file)
@@ -37,7 +37,7 @@
  *
  *     @(#)kern_synch.c        8.9 (Berkeley) 5/19/95
  * $FreeBSD: src/sys/kern/kern_synch.c,v 1.87.2.6 2002/10/13 07:29:53 kbyanc Exp $
- * $DragonFly: src/sys/kern/kern_synch.c,v 1.61 2006/05/25 07:36:34 dillon Exp $
+ * $DragonFly: src/sys/kern/kern_synch.c,v 1.62 2006/05/27 01:51:26 dillon Exp $
  */
 
 #include "opt_ktrace.h"
@@ -50,7 +50,6 @@
 #include <sys/resourcevar.h>
 #include <sys/vmmeter.h>
 #include <sys/sysctl.h>
-#include <sys/thread2.h>
 #include <sys/lock.h>
 #ifdef KTRACE
 #include <sys/uio.h>
@@ -59,6 +58,9 @@
 #include <sys/xwait.h>
 #include <sys/ktr.h>
 
+#include <sys/thread2.h>
+#include <sys/spinlock2.h>
+
 #include <machine/cpu.h>
 #include <machine/ipl.h>
 #include <machine/smp.h>
@@ -515,12 +517,44 @@ resume:
  * There isn't much of a point to this function unless you call it while
  * holding a critical section.
  */
+static __inline void
+_tsleep_interlock(globaldata_t gd, void *ident)
+{
+       int id = LOOKUP(ident);
+
+       atomic_set_int(&slpque_cpumasks[id], gd->gd_cpumask);
+}
+
 void
 tsleep_interlock(void *ident)
 {
-       int id = LOOKUP(ident);
+       _tsleep_interlock(mycpu, ident);
+}
 
-       atomic_set_int(&slpque_cpumasks[id], mycpu->gd_cpumask);
+/*
+ * Interlocked spinlock sleep.  An exclusively held spinlock must
+ * be passed to msleep().  The function will atomically release the
+ * spinlock and tsleep on the ident, then reacquire the spinlock and
+ * return.
+ *
+ * This routine is fairly important along the critical path, so optimize it
+ * heavily.
+ */
+int
+msleep(void *ident, struct spinlock *spin, int flags,
+       const char *wmesg, int timo)
+{
+       globaldata_t gd = mycpu;
+       int error;
+
+       crit_enter_gd(gd);
+       _tsleep_interlock(gd, ident);
+       spin_unlock_wr_quick(gd, spin);
+       error = tsleep(ident, flags, wmesg, timo);
+       spin_lock_wr_quick(gd, spin);
+       crit_exit_gd(gd);
+
+       return (error);
 }
 
 /*
index 88b18ef..e0cfaba 100644 (file)
@@ -37,7 +37,7 @@
  *
  *     @(#)systm.h     8.7 (Berkeley) 3/29/95
  * $FreeBSD: src/sys/sys/systm.h,v 1.111.2.18 2002/12/17 18:04:02 sam Exp $
- * $DragonFly: src/sys/sys/systm.h,v 1.37 2006/05/21 03:43:47 dillon Exp $
+ * $DragonFly: src/sys/sys/systm.h,v 1.38 2006/05/27 01:51:27 dillon Exp $
  */
 
 #ifndef _SYS_SYSTM_H_
@@ -109,6 +109,7 @@ extern int clocks_running;  /* timing/timeout subsystem is operational */
  */
 
 struct intrframe;
+struct spinlock;
 struct malloc_type;
 struct proc;
 struct xwait;
@@ -296,7 +297,8 @@ extern watchdog_tickle_fn   wdog_tickler;
  * Common `proc' functions are declared here so that proc.h can be included
  * less often.
  */
-int    tsleep (void *chan, int slpflags, const char *wmesg, int timo);
+int    tsleep (void *, int, const char *, int);
+int    msleep (void *, struct spinlock *, int, const char *, int);
 void   tsleep_interlock (void *chan);
 void   tstop (struct proc *);
 void   wakeup (void *chan);