Subject: spl checks in simple_lock [was Re: v_interlock/splbio protocol violations]
To: Chuck Silvers <chuq@chuq.com>
From: Darrin B. Jewell <dbj@netbsd.org>
List: tech-kern
Date: 03/09/2004 14:44:12
--=-=-=
Chuck Silvers <chuq@chuq.com> writes:
> I'm hoping that eventually we get some better MP locking primitives
> that don't require the calling code to know about the particular IPL
> prerequisites for each lock acquisition. then we would need to
> convert all the MP locking to use the new primitives once, but
> ideally after that we could change the MP locking implementation
> without changing all the callers again. I don't know if that's the
> discussion you wanted to have right now though.
Well, it wasn't the dicussion I was looking for, but I offer to take
the following step in that direction:
I've added an argument to simple_lock_init(9), SIMPLELOCK_INITIALIZER,
and pool_init(9) which encodes a minimum spl level which is required
for all uses of that lock or pool.
For now, this will only be used by LOCKDEBUG to ensure that the spl
requirements for simplelocks are being consistently met. If we
choose, we can eventually use this argument to take the additional
step of having the lock handle the interrupt management itself.
I include below the highlights of a larger patch which can be found at
<URL:ftp://ftp.netbsd.org/pub/NetBSD/misc/dbj/lockspl.diff>
The highlights show the changes necessary to kern_lock.c and
subr_pool.c to support this check. Since the check only affects
LOCKDEBUG, the changes are relatively minor.
The larger patch includes a full pass on the tree which adds the
arguments to all the lock and pool calls. Calls which have not yet
had the new argument verified by hand are marked with
/*XXXUNVERIFIED*/, which can be removed once it has been checked that
the apropriate spl level is specified at initializtion time.
Incorrect unverified spl levels will not cause problems unless
LOCKDEBUG is defined, and in that case they will be flagged as a lock
problem and can be easily verified by hand. With a little help, I'm
sure we can get all of the XXXUNVERIFIED cases checked pretty quickly.
In addition to this new argument to simple_lock and pool_init, I add
support for a couple of functions to be used when SPLDEBUG is defined.
I include these functions in this patch because they are used to
implement the spl checks in kern_lock.c
These functions are spl_is_blocked(level), which returns true if the
specified interrupt level is blocked, and spl_in_context() which
returns true if the function is invoked from an interrupt handler
itself. I would like to add a level argument to spl_in_context() but
am not sure how to quickly detect what is the highest priority
interrupt handler actually running.
These functions can be used inside SPL_ASSERT() to encode the
constraints for functions being invoked in interrupt context or at a
particular spl level. So far, they are only implemented on i386, but
once we determine they are the correct functions, I would like to see
them added to the MD intr.h of other architectures.
Please take a look at these changes. I would like to eventually
incorporate them into the tree if they are indeed a step in the
right direction.
Thanks,
Darrin
--=-=-=
Content-Type: text/x-patch
Content-Disposition: attachment; filename=lockspl.highlights.diff
Content-Description: highlights of changes to simple_lock_init and pool_init
Index: src/sys/arch/x86/include/intr.h
===================================================================
RCS file: /cvsroot/src/sys/arch/x86/include/intr.h,v
retrieving revision 1.12
diff -u -u -p -r1.12 intr.h
--- src/sys/arch/x86/include/intr.h 4 Mar 2004 19:10:10 -0000 1.12
+++ src/sys/arch/x86/include/intr.h 9 Mar 2004 18:58:45 -0000
@@ -41,6 +41,7 @@
#ifdef _KERNEL_OPT
#include "opt_multiprocessor.h"
+#include "opt_ddb.h"
#endif
#include <machine/intrdefs.h>
@@ -116,6 +117,8 @@ extern void Xspllower(int);
static __inline int splraise(int);
static __inline void spllower(int);
static __inline void softintr(int);
+static __inline int spl_is_blocked(int);
+static __inline int spl_in_context(void);
/*
* Convert spl level to local APIC level
@@ -168,6 +171,31 @@ spllower(int nlevel)
Xspllower(nlevel);
}
+static __inline int
+spl_is_blocked(int level)
+{
+ struct cpu_info *ci = curcpu();
+ return (ci->ci_ilevel >= level);
+}
+
+static __inline int
+spl_in_context()
+{
+ struct cpu_info *ci = curcpu();
+ return (ci->ci_idepth > 0);
+}
+
+#ifdef SPLDEBUG
+#ifdef DDB
+extern label_t *db_recover;
+#define SPL_ASSERT(x) do { if (!cold && !db_recover) KASSERT(x); } while(0)
+#else
+#define SPL_ASSERT(x) do { if (!cold) KASSERT(x); } while(0)
+#endif
+#else
+#define SPL_ASSERT(x)
+#endif
+
/*
* Hardware interrupt masks
*/
Index: src/sys/arch/x86/include/intrdefs.h
===================================================================
RCS file: /cvsroot/src/sys/arch/x86/include/intrdefs.h,v
retrieving revision 1.3
diff -u -u -p -r1.3 intrdefs.h
--- src/sys/arch/x86/include/intrdefs.h 16 Jun 2003 20:01:06 -0000 1.3
+++ src/sys/arch/x86/include/intrdefs.h 9 Mar 2004 18:58:45 -0000
@@ -32,6 +32,7 @@
#define IPL_CLOCK 0xc /* clock */
#define IPL_SCHED IPL_CLOCK
#define IPL_HIGH 0xd /* everything */
+#define IPL_LOCK IPL_HIGH
#define IPL_SERIAL 0xd /* serial */
#define IPL_IPI 0xe /* inter-processor interrupts */
#define NIPL 16
Index: src/sys/kern/subr_pool.c
===================================================================
RCS file: /cvsroot/src/sys/kern/subr_pool.c,v
retrieving revision 1.93
diff -u -u -p -r1.93 subr_pool.c
--- src/sys/kern/subr_pool.c 8 Mar 2004 22:48:09 -0000 1.93
+++ src/sys/kern/subr_pool.c 9 Mar 2004 18:59:54 -0000
@@ -87,7 +87,7 @@ int pool_inactive_time = 10;
static struct pool *drainpp;
/* This spin lock protects both pool_head and drainpp. */
-struct simplelock pool_head_slock = SIMPLELOCK_INITIALIZER;
+struct simplelock pool_head_slock = SIMPLELOCK_INITIALIZER(IPL_NONE);
struct pool_item_header {
/* Page headers */
@@ -364,7 +364,7 @@ pr_rmpage(struct pool *pp, struct pool_i
*/
void
pool_init(struct pool *pp, size_t size, u_int align, u_int ioff, int flags,
- const char *wchan, struct pool_allocator *palloc)
+ const char *wchan, struct pool_allocator *palloc, int spl)
{
int off, slack;
size_t trysize, phsize;
@@ -410,7 +410,7 @@ pool_init(struct pool *pp, size_t size,
TAILQ_INIT(&palloc->pa_list);
- simple_lock_init(&palloc->pa_slock);
+ simple_lock_init(&palloc->pa_slock, IPL_VM);
palloc->pa_pagemask = ~(palloc->pa_pagesz - 1);
palloc->pa_pageshift = ffs(palloc->pa_pagesz) - 1;
palloc->pa_flags |= PA_INITIALIZED;
@@ -525,7 +525,7 @@ pool_init(struct pool *pp, size_t size,
pp->pr_entered_file = NULL;
pp->pr_entered_line = 0;
- simple_lock_init(&pp->pr_slock);
+ simple_lock_init(&pp->pr_slock, spl);
/*
* Initialize private page header pool and cache magazine pool if we
@@ -535,15 +535,15 @@ pool_init(struct pool *pp, size_t size,
if (phpool.pr_size == 0) {
#ifdef POOL_SUBPAGE
pool_init(&phpool, sizeof(struct pool_item_header), 0, 0, 0,
- "phpool", &pool_allocator_kmem);
+ "phpool", &pool_allocator_kmem, IPL_VM);
pool_init(&psppool, POOL_SUBPAGE, POOL_SUBPAGE, 0,
- PR_RECURSIVE, "psppool", &pool_allocator_kmem);
+ PR_RECURSIVE, "psppool", &pool_allocator_kmem, IPL_VM);
#else
pool_init(&phpool, sizeof(struct pool_item_header), 0, 0,
- 0, "phpool", NULL);
+ 0, "phpool", NULL, IPL_VM);
#endif
pool_init(&pcgpool, sizeof(struct pool_cache_group), 0, 0,
- 0, "pcgpool", NULL);
+ 0, "pcgpool", NULL, IPL_VM);
}
/* Insert into the list of all pools. */
@@ -1643,7 +1643,15 @@ pool_cache_init(struct pool_cache *pc, s
{
TAILQ_INIT(&pc->pc_grouplist);
- simple_lock_init(&pc->pc_slock);
+#ifdef LOCKDEBUG
+ /* XXX Maybe the pool should keep a copy of the spl level
+ * or else have it passed in as an arg to pool_cache_init
+ * as well.
+ */
+ simple_lock_init(&pc->pc_slock, pp->pr_slock.lock_spl);
+#else
+ simple_lock_init(&pc->pc_slock, );
+#endif
pc->pc_allocfrom = NULL;
pc->pc_freeto = NULL;
Index: src/sys/sys/lock.h
===================================================================
RCS file: /cvsroot/src/sys/sys/lock.h,v
retrieving revision 1.52
diff -u -u -p -r1.52 lock.h
--- src/sys/sys/lock.h 14 Jan 2004 11:34:48 -0000 1.52
+++ src/sys/sys/lock.h 9 Mar 2004 19:00:36 -0000
@@ -100,14 +100,15 @@ struct simplelock {
short unlock_line;
TAILQ_ENTRY(simplelock) list;
cpuid_t lock_holder; /* CPU ID */
+ int lock_spl;
#endif
};
#ifdef LOCKDEBUG
-#define SIMPLELOCK_INITIALIZER { __SIMPLELOCK_UNLOCKED, NULL, NULL, 0, \
- 0, { NULL, NULL }, LK_NOCPU }
+#define SIMPLELOCK_INITIALIZER(x) { __SIMPLELOCK_UNLOCKED, NULL, NULL, 0, \
+ 0, { NULL, NULL }, LK_NOCPU, (x) }
#else
-#define SIMPLELOCK_INITIALIZER { __SIMPLELOCK_UNLOCKED }
+#define SIMPLELOCK_INITIALIZER(x) { __SIMPLELOCK_UNLOCKED }
#endif
/*
@@ -169,7 +170,7 @@ struct lock {
};
#define LOCK_INITIALIZER(prio, wmesg, timo, flags) \
- { SIMPLELOCK_INITIALIZER, \
+ { SIMPLELOCK_INITIALIZER(IPL_NONE), \
(flags), \
0, \
0, \
@@ -340,12 +341,12 @@ void simple_lock_only_held(__volatile st
#define LOCK_ASSERT(x) KASSERT(x)
-void simple_lock_init(struct simplelock *);
+void simple_lock_init(struct simplelock *, int);
void simple_lock_dump(void);
void simple_lock_freecheck(void *, void *);
void simple_lock_switchcheck(void);
#elif defined(MULTIPROCESSOR)
-#define simple_lock_init(alp) __cpu_simple_lock_init(&(alp)->lock_data)
+#define simple_lock_init(alp, x) __cpu_simple_lock_init(&(alp)->lock_data)
#define simple_lock(alp) __cpu_simple_lock(&(alp)->lock_data)
#define simple_lock_try(alp) __cpu_simple_lock_try(&(alp)->lock_data)
#define simple_unlock(alp) __cpu_simple_unlock(&(alp)->lock_data)
@@ -354,11 +355,11 @@ void simple_lock_switchcheck(void);
#else
#define simple_lock_try(alp) (1)
#ifndef lint
-#define simple_lock_init(alp) (void)(alp)
+#define simple_lock_init(alp, x) (void)(alp)
#define simple_lock(alp) (void)(alp)
#define simple_unlock(alp) (void)(alp)
#else /* lint */
-#define simple_lock_init(alp) /* nothing */
+#define simple_lock_init(alp, x) /* nothing */
#define simple_lock(alp) /* nothing */
#define simple_unlock(alp) /* nothing */
#define simple_lock_only_held(x,y) /* nothing */
Index: src/sys/sys/pool.h
===================================================================
RCS file: /cvsroot/src/sys/sys/pool.h,v
retrieving revision 1.42
diff -u -u -p -r1.42 pool.h
--- src/sys/sys/pool.h 9 Jan 2004 19:00:16 -0000 1.42
+++ src/sys/sys/pool.h 9 Mar 2004 19:00:36 -0000
@@ -217,7 +217,7 @@ extern struct pool_allocator pool_alloca
extern struct pool_allocator pool_allocator_nointr;
void pool_init(struct pool *, size_t, u_int, u_int,
- int, const char *, struct pool_allocator *);
+ int, const char *, struct pool_allocator *, int);
void pool_destroy(struct pool *);
void pool_set_drain_hook(struct pool *,
--=-=-=--