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