Home | History | Annotate | Line # | Download | only in kern
subr_lockdebug.c revision 1.1.2.2
      1 /*	$NetBSD: subr_lockdebug.c,v 1.1.2.2 2006/10/24 19:07:49 ad Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2006 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * This code is derived from software contributed to The NetBSD Foundation
      8  * by Andrew Doran.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  * 3. All advertising materials mentioning features or use of this software
     19  *    must display the following acknowledgement:
     20  *	This product includes software developed by the NetBSD
     21  *	Foundation, Inc. and its contributors.
     22  * 4. Neither the name of The NetBSD Foundation nor the names of its
     23  *    contributors may be used to endorse or promote products derived
     24  *    from this software without specific prior written permission.
     25  *
     26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     36  * POSSIBILITY OF SUCH DAMAGE.
     37  */
     38 
     39 /*
     40  * Basic lock debugging code shared among lock primatives.
     41  *
     42  * XXX malloc() may want to initialize new mutexes.  To be fixed.
     43  */
     44 
     45 #include "opt_multiprocessor.h"
     46 
     47 #include <sys/cdefs.h>
     48 __KERNEL_RCSID(0, "$NetBSD: subr_lockdebug.c,v 1.1.2.2 2006/10/24 19:07:49 ad Exp $");
     49 
     50 #include <sys/param.h>
     51 #include <sys/proc.h>
     52 #include <sys/systm.h>
     53 #include <sys/malloc.h>
     54 #include <sys/lock.h>
     55 #include <sys/lockdebug.h>
     56 
     57 #include <machine/cpu.h>
     58 
     59 #ifdef LOCKDEBUG
     60 
     61 #define	LD_BATCH_SHIFT	9
     62 #define	LD_BATCH	(1 << LD_BATCH_SHIFT)
     63 #define	LD_BATCH_MASK	(LD_BATCH - 1)
     64 #define	LD_MAX_LOCKS	1048576
     65 #define	LD_SLOP		16
     66 
     67 #define	LD_LOCKED	0x01
     68 #define	LD_SLEEPER	0x02
     69 
     70 typedef struct lockdebuglk {
     71 	__cpu_simple_lock_t	lk_lock;
     72 	int			lk_oldspl;
     73 } volatile lockdebuglk_t;
     74 
     75 typedef struct lockdebug {
     76 	_TAILQ_ENTRY(struct lockdebug, volatile) ld_chain;
     77 	void		*ld_lock;
     78 	lockops_t	*ld_lockops;
     79 	struct lwp	*ld_lwp;
     80 	uintptr_t	ld_locked;
     81 	uintptr_t	ld_unlocked;
     82 	u_int		ld_id;
     83 	u_short		ld_cpu;
     84 	u_short		ld_shares;
     85 	u_char		ld_flags;
     86 } volatile lockdebug_t;
     87 
     88 typedef _TAILQ_HEAD(lockdebuglist, struct lockdebug, volatile) lockdebuglist_t;
     89 
     90 lockdebuglk_t		ld_sleeper_lk;
     91 lockdebuglk_t		ld_spinner_lk;
     92 lockdebuglk_t		ld_free_lk;
     93 
     94 lockdebuglist_t		ld_sleepers;
     95 lockdebuglist_t		ld_spinners;
     96 lockdebuglist_t		ld_free;
     97 int			ld_nfree;
     98 int			ld_freeptr;
     99 int			ld_recurse;
    100 lockdebug_t		*ld_table[LD_MAX_LOCKS / LD_BATCH];
    101 
    102 lockdebug_t		ld_prime[LD_BATCH];
    103 
    104 MALLOC_DEFINE(M_LOCKDEBUG, "lockdebug", "lockdebug structures");
    105 
    106 void	lockdebug_abort1(lockdebug_t *, lockdebuglk_t *lk, const char *,
    107 			 const char *);
    108 void	lockdebug_more(void);
    109 
    110 static inline void
    111 lockdebug_lock(lockdebuglk_t *lk)
    112 {
    113 	int s;
    114 
    115 	s = spllock();
    116 	__cpu_simple_lock(&lk->lk_lock);
    117 	lk->lk_oldspl = s;
    118 }
    119 
    120 static inline void
    121 lockdebug_unlock(lockdebuglk_t *lk)
    122 {
    123 	int s;
    124 
    125 	s = lk->lk_oldspl;
    126 	__cpu_simple_unlock(&lk->lk_lock);
    127 	splx(s);
    128 }
    129 
    130 /*
    131  * lockdebug_lookup:
    132  *
    133  *	Find a lockdebug structure by ID and return it locked.
    134  */
    135 static inline lockdebug_t *
    136 lockdebug_lookup(u_int id, lockdebuglk_t **lk)
    137 {
    138 	lockdebug_t *ld;
    139 
    140 	ld = ld_table[id >> LD_BATCH_SHIFT] + (id & LD_BATCH_MASK);
    141 
    142 	if (id == 0 || id >= LD_MAX_LOCKS || ld == NULL || ld->ld_lock == NULL)
    143 		panic("lockdebug_lookup: uninitialized lock (id=%d)", id);
    144 
    145 	if (ld->ld_id != id)
    146 		panic("lockdebug_lookup: corrupt table");
    147 
    148 	if ((ld->ld_flags & LD_SLEEPER) != 0)
    149 		*lk = &ld_sleeper_lk;
    150 	else
    151 		*lk = &ld_spinner_lk;
    152 
    153 	lockdebug_lock(*lk);
    154 	return ld;
    155 }
    156 
    157 /*
    158  * lockdebug_init:
    159  *
    160  *	Initialize the lockdebug system.  Allocate an initial pool of
    161  *	lockdebug structures before the VM system is up and running.
    162  */
    163 void
    164 lockdebug_init(void)
    165 {
    166 	lockdebug_t *ld;
    167 	int i;
    168 
    169 	__cpu_simple_lock_init(&ld_sleeper_lk.lk_lock);
    170 	__cpu_simple_lock_init(&ld_spinner_lk.lk_lock);
    171 	__cpu_simple_lock_init(&ld_free_lk.lk_lock);
    172 
    173 	TAILQ_INIT(&ld_free);
    174 	TAILQ_INIT(&ld_sleepers);
    175 	TAILQ_INIT(&ld_spinners);
    176 
    177 	ld = ld_prime;
    178 	ld_table[0] = ld;
    179 	for (i = 1, ld++; i < LD_BATCH; i++, ld++) {
    180 		ld->ld_id = i;
    181 		TAILQ_INSERT_TAIL(&ld_free, ld, ld_chain);
    182 	}
    183 	ld_freeptr = 1;
    184 	ld_nfree = LD_BATCH;
    185 }
    186 
    187 /*
    188  * lockdebug_alloc:
    189  *
    190  *	A lock is being initialized, so allocate an associated debug
    191  *	structure.
    192  */
    193 u_int
    194 lockdebug_alloc(void *lock, lockops_t *lo, int sleeplock)
    195 {
    196 	lockdebug_t *ld;
    197 
    198 	if (panicstr != NULL)
    199 		return 0;
    200 
    201 	/* Pinch a new debug structure. */
    202 	lockdebug_lock(&ld_free_lk);
    203 	if (TAILQ_EMPTY(&ld_free))
    204 		lockdebug_more();
    205 	ld = TAILQ_FIRST(&ld_free);
    206 	TAILQ_REMOVE(&ld_free, ld, ld_chain);
    207 	ld_nfree--;
    208 	lockdebug_unlock(&ld_free_lk);
    209 
    210 	if (ld->ld_lock != NULL)
    211 		panic("lockdebug_alloc: corrupt table");
    212 
    213 	if (sleeplock)
    214 		lockdebug_lock(&ld_sleeper_lk);
    215 	else
    216 		lockdebug_lock(&ld_spinner_lk);
    217 
    218 	/* Initialise the structure. */
    219 	ld->ld_lock = lock;
    220 	ld->ld_lockops = lo;
    221 	ld->ld_locked = 0;
    222 	ld->ld_unlocked = 0;
    223 	ld->ld_lwp = NULL;
    224 
    225 	if (sleeplock) {
    226 		ld->ld_flags = LD_SLEEPER;
    227 		lockdebug_unlock(&ld_sleeper_lk);
    228 	} else {
    229 		ld->ld_flags = 0;
    230 		lockdebug_unlock(&ld_spinner_lk);
    231 	}
    232 
    233 	return ld->ld_id;
    234 }
    235 
    236 /*
    237  * lockdebug_free:
    238  *
    239  *	A lock is being destroyed, so release debugging resources.
    240  */
    241 void
    242 lockdebug_free(void *lock, u_int id)
    243 {
    244 	lockdebug_t *ld;
    245 	lockdebuglk_t *lk;
    246 
    247 	if (panicstr != NULL)
    248 		return;
    249 
    250 	ld = lockdebug_lookup(id, &lk);
    251 
    252 	if (ld->ld_lock != lock) {
    253 		panic("lockdebug_free: destroying uninitialized lock %p"
    254 		    "(ld_id=%d ld_lock=%p)", lock, id, ld->ld_lock);
    255 		lockdebug_abort1(ld, lk, __FUNCTION__, "lock record follows");
    256 	}
    257 	if ((ld->ld_flags & LD_LOCKED) != 0)
    258 		lockdebug_abort1(ld, lk, __FUNCTION__, "is locked");
    259 
    260 	ld->ld_lock = NULL;
    261 
    262 	lockdebug_unlock(lk);
    263 
    264 	lockdebug_lock(&ld_free_lk);
    265 	TAILQ_INSERT_TAIL(&ld_free, ld, ld_chain);
    266 	ld_nfree++;
    267 	lockdebug_unlock(&ld_free_lk);
    268 }
    269 
    270 /*
    271  * lockdebug_more:
    272  *
    273  *	Allocate a batch of debug structures and add to the free list.  Must
    274  *	be called with ld_free_lk held.
    275  */
    276 void
    277 lockdebug_more(void)
    278 {
    279 	lockdebug_t *ld;
    280 	void *block;
    281 	int i, base;
    282 
    283 	while (TAILQ_EMPTY(&ld_free)) {
    284 		lockdebug_unlock(&ld_free_lk);
    285 		block = malloc(LD_BATCH * sizeof(lockdebug_t), M_LOCKDEBUG,
    286 		    M_NOWAIT | M_ZERO); /* XXX M_NOWAIT */
    287 		lockdebug_lock(&ld_free_lk);
    288 
    289 		base = ld_freeptr;
    290 		if (ld_table[base] != NULL) {
    291 			/* Somebody beat us to it. */
    292 			lockdebug_unlock(&ld_free_lk);
    293 			free(block, M_LOCKDEBUG);
    294 			lockdebug_lock(&ld_free_lk);
    295 			continue;
    296 		}
    297 		ld_table[base] = block;
    298 		ld_freeptr++;
    299 		ld = block;
    300 		base <<= LD_BATCH_SHIFT;
    301 
    302 		for (i = 0; i < LD_BATCH; i++, ld++) {
    303 			ld->ld_id = i + base;
    304 			ld->ld_lock = NULL;
    305 			TAILQ_INSERT_TAIL(&ld_free, ld, ld_chain);
    306 		}
    307 
    308 		ld_table[base] = ld;
    309 		mb_write();
    310 	}
    311 }
    312 
    313 /*
    314  * lockdebug_locked:
    315  *
    316  *	Process a lock acquire operation.
    317  */
    318 void
    319 lockdebug_locked(u_int id, uintptr_t where, int shared)
    320 {
    321 	struct lwp *l = curlwp;
    322 	lockdebuglk_t *lk;
    323 	lockdebug_t *ld;
    324 
    325 	if (panicstr != NULL)
    326 		return;
    327 
    328 	ld = lockdebug_lookup(id, &lk);
    329 
    330 	if ((ld->ld_flags & LD_LOCKED) != 0)
    331 		lockdebug_abort1(ld, lk, __FUNCTION__, "already locked");
    332 
    333 	if (shared) {
    334 		if (l == NULL)
    335 			lockdebug_abort1(ld, lk, __FUNCTION__, "releasing "
    336 			    "shared lock from interrupt context");
    337 
    338 		l->l_shlocks++;
    339 		ld->ld_shares++;
    340 	} else {
    341 		ld->ld_flags |= LD_LOCKED;
    342 		ld->ld_locked = where;
    343 		ld->ld_cpu = (u_short)cpu_number();
    344 		ld->ld_lwp = l;
    345 
    346 		if ((ld->ld_flags & LD_SLEEPER) != 0) {
    347 			l->l_exlocks++;
    348 			TAILQ_INSERT_TAIL(&ld_sleepers, ld, ld_chain);
    349 		} else {
    350 			curcpu()->ci_spin_locks2++;
    351 			TAILQ_INSERT_TAIL(&ld_spinners, ld, ld_chain);
    352 		}
    353 	}
    354 
    355 	lockdebug_unlock(lk);
    356 }
    357 
    358 /*
    359  * lockdebug_unlocked:
    360  *
    361  *	Process a lock release operation.
    362  */
    363 void
    364 lockdebug_unlocked(u_int id, uintptr_t where, int shared)
    365 {
    366 	struct lwp *l = curlwp;
    367 	lockdebuglk_t *lk;
    368 	lockdebug_t *ld;
    369 
    370 	if (panicstr != NULL)
    371 		return;
    372 
    373 	ld = lockdebug_lookup(id, &lk);
    374 
    375 	if (shared) {
    376 		if (l == NULL)
    377 			lockdebug_abort1(ld, lk, __FUNCTION__, "acquiring "
    378 			    "shared lock from interrupt context");
    379 		if (l->l_shlocks == 0)
    380 			lockdebug_abort1(ld, lk, __FUNCTION__, "no shared "
    381 			    "locks held by LWP");
    382 		if (ld->ld_shares == 0)
    383 			lockdebug_abort1(ld, lk, __FUNCTION__, "no shared "
    384 			    "holds on this lock");
    385 		l->l_shlocks--;
    386 		ld->ld_shares--;
    387 	} else {
    388 		if ((ld->ld_flags & LD_LOCKED) == 0)
    389 			lockdebug_abort1(ld, lk, __FUNCTION__, "not locked");
    390 
    391 		if ((ld->ld_flags & LD_SLEEPER) != 0) {
    392 			if (ld->ld_lwp != curlwp)
    393 				lockdebug_abort1(ld, lk, __FUNCTION__,
    394 				    "not held by current LWP");
    395 			ld->ld_flags &= ~LD_LOCKED;
    396 			ld->ld_unlocked = where;
    397 			ld->ld_lwp = NULL;
    398 			curlwp->l_exlocks--;
    399 			TAILQ_REMOVE(&ld_sleepers, ld, ld_chain);
    400 		} else {
    401 			if (ld->ld_cpu != (u_short)cpu_number())
    402 				lockdebug_abort1(ld, lk, __FUNCTION__,
    403 				    "not held by current CPU");
    404 			ld->ld_flags &= ~LD_LOCKED;
    405 			ld->ld_unlocked = where;
    406 			ld->ld_lwp = NULL;
    407 			curcpu()->ci_spin_locks2--;
    408 			TAILQ_REMOVE(&ld_spinners, ld, ld_chain);
    409 		}
    410 	}
    411 
    412 	lockdebug_unlock(lk);
    413 }
    414 
    415 /*
    416  * lockdebug_barrier:
    417  *
    418  *	Panic if we hold more than one specified spin lock, and optionally,
    419  *	if we hold sleep locks.
    420  */
    421 void
    422 lockdebug_barrier(void *spinlock, int slplocks)
    423 {
    424 	struct lwp *l = curlwp;
    425 	lockdebug_t *ld;
    426 	u_short cpuno;
    427 
    428 	if (panicstr != NULL)
    429 		return;
    430 
    431 	if (curcpu()->ci_spin_locks2 != 0) {
    432 		cpuno = (u_short)cpu_number();
    433 
    434 		lockdebug_lock(&ld_spinner_lk);
    435 		TAILQ_FOREACH(ld, &ld_spinners, ld_chain) {
    436 			if (ld->ld_lock == spinlock) {
    437 				if (ld->ld_cpu != cpuno)
    438 					lockdebug_abort1(ld, &ld_spinner_lk,
    439 					    __FUNCTION__,
    440 					    "not held by current CPU");
    441 				continue;
    442 			}
    443 			if (ld->ld_cpu == cpuno)
    444 				lockdebug_abort1(ld, &ld_spinner_lk,
    445 				    __FUNCTION__, "spin lock held");
    446 		}
    447 		lockdebug_unlock(&ld_spinner_lk);
    448 	}
    449 
    450 	if (!slplocks) {
    451 		if (l->l_exlocks != 0) {
    452 			lockdebug_lock(&ld_sleeper_lk);
    453 			TAILQ_FOREACH(ld, &ld_sleepers, ld_chain) {
    454 				if (ld->ld_lwp == l)
    455 					lockdebug_abort1(ld, &ld_sleeper_lk,
    456 					    __FUNCTION__, "sleep lock held");
    457 			}
    458 			lockdebug_unlock(&ld_sleeper_lk);
    459 		}
    460 		if (l->l_shlocks != 0)
    461 			panic("lockdebug_barrier: holding %d shared locks",
    462 			    l->l_shlocks);
    463 	}
    464 }
    465 
    466 /*
    467  * lockdebug_abort:
    468  *
    469  *	An error has been trapped - dump lock info call panic().
    470  */
    471 void
    472 lockdebug_abort(int id, void *lock, lockops_t *ops, const char *func,
    473 		const char *msg)
    474 {
    475 	lockdebug_t *ld;
    476 	lockdebuglk_t *lk;
    477 
    478 	(void)lock;
    479 	(void)ops;
    480 
    481 	ld = lockdebug_lookup(id, &lk);
    482 
    483 	lockdebug_abort1(ld, lk, func, msg);
    484 }
    485 
    486 void
    487 lockdebug_abort1(lockdebug_t *ld, lockdebuglk_t *lk, const char *func,
    488 		 const char *msg)
    489 {
    490 	static char buf[1024];
    491 	int p;
    492 
    493 	/*
    494 	 * The kernel is about to fall flat on its face, so assume that 1k
    495 	 * will be enough to hold the dump and abuse the return value from
    496 	 * snprintf.
    497 	 */
    498 	p = snprintf(buf, sizeof(buf), "%s error: %s: %s\n",
    499 	    ld->ld_lockops->lo_name, func, msg);
    500 
    501 	p += snprintf(buf + p, sizeof(buf) - p,
    502 	    "lock address : %#018lx type     : %18s\n"
    503 	    "shared holds : %18d exclusive: %12slocked\n"
    504 	    "last locked  : %#018lx unlocked : %#018lx\n"
    505 	    "current cpu  : %18d last held: %18d\n"
    506 	    "current lwp  : %#018lx last held: %#018lx\n",
    507 	    (long)ld->ld_lock,
    508 	    ((ld->ld_flags & LD_SLEEPER) == 0 ? "spin" : "sleep"),
    509 	    ld->ld_shares, ((ld->ld_flags & LD_LOCKED) == 0 ? "un" : " "),
    510 	    (long)ld->ld_locked, (long)ld->ld_unlocked,
    511 	    (int)cpu_number(), (int)ld->ld_cpu,
    512 	    (long)curlwp, (long)ld->ld_lwp);
    513 
    514 	(void)(*ld->ld_lockops->lo_dump)(ld->ld_lock, buf + p, sizeof(buf) - p);
    515 
    516 	lockdebug_unlock(lk);
    517 
    518 	printf("%s", buf);
    519 	panic("LOCKDEBUG");
    520 }
    521 
    522 #else	/* LOCKDEBUG */
    523 
    524 /*
    525  * lockdebug_abort:
    526  *
    527  *	An error has been trapped - dump lock info and call panic().
    528  *	Called in the non-LOCKDEBUG case to print basic information.
    529  */
    530 void
    531 lockdebug_abort(int id, void *lock, lockops_t *ops, const char *func,
    532 		const char *msg)
    533 {
    534 	static char buf[1024];
    535 	int p;
    536 
    537 	/*
    538 	 * The kernel is about to fall flat on its face, so assume that 1k
    539 	 * will be enough to hold the dump and abuse the return value from
    540 	 * snprintf.
    541 	 */
    542 	p = snprintf(buf, sizeof(buf), "%s error: %s: %s\n",
    543 	    ops->lo_name, func, msg);
    544 
    545 	p += snprintf(buf + p, sizeof(buf) - p,
    546 	    "lock address : %#018lx\n"
    547 	    "current cpu  : %18d\n"
    548 	    "current lwp  : %#018lx\n",
    549 	    (long)lock, (int)cpu_number(), (long)curlwp);
    550 
    551 	(void)(*ops->lo_dump)(lock, buf + p, sizeof(buf) - p);
    552 
    553 	printf("%s", buf);
    554 	panic("lock error");
    555 }
    556 
    557 #endif	/* LOCKDEBUG */
    558