1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 #include <mdb/mdb_modapi.h>
27 #include <sys/types.h>
28 #include <sys/mutex.h>
29 #include <sys/thread.h>
30 #include <sys/condvar.h>
31 #include <sys/sleepq.h>
32 #include <sys/sobject.h>
33 #include <sys/rwlock_impl.h>
34 #include <sys/turnstile.h>
35 #include <sys/proc.h>
36 #include <sys/mutex_impl.h>
37
38 #include <stdio.h>
39
40 struct sobj_type_info {
41 int sobj_type;
42 const char *sobj_name;
43 const char *sobj_ops_name;
44 } sobj_types[] = {
45 { SOBJ_MUTEX, "mutex", "mutex_sobj_ops" },
46 { SOBJ_RWLOCK, "rwlock", "rw_sobj_ops" },
47 { SOBJ_CV, "cv", "cv_sobj_ops" },
48 { SOBJ_SEMA, "sema", "sema_sobj_ops" },
49 { SOBJ_USER, "user", "lwp_sobj_ops" },
50 { SOBJ_USER_PI, "user_pi", "lwp_sobj_pi_ops" },
51 { SOBJ_SHUTTLE, "shuttle", "shuttle_sobj_ops" }
52 };
53 #define NUM_SOBJ_TYPES (sizeof (sobj_types) / sizeof (*sobj_types))
54
55 void
sobj_type_to_text(int type,char * out,size_t sz)56 sobj_type_to_text(int type, char *out, size_t sz)
57 {
58 int idx;
59 if (type == SOBJ_NONE) {
60 mdb_snprintf(out, sz, "<none>");
61 return;
62 }
63
64 for (idx = 0; idx < NUM_SOBJ_TYPES; idx++) {
65 struct sobj_type_info *info = &sobj_types[idx];
66 if (info->sobj_type == type) {
67 mdb_snprintf(out, sz, "%s",
68 sobj_types[idx].sobj_name);
69 return;
70 }
71 }
72 mdb_snprintf(out, sz, "<unk:%02x>", type);
73 }
74
75 void
sobj_ops_to_text(uintptr_t addr,char * out,size_t sz)76 sobj_ops_to_text(uintptr_t addr, char *out, size_t sz)
77 {
78 sobj_ops_t ops;
79
80 if (addr == 0) {
81 mdb_snprintf(out, sz, "<none>");
82 return;
83 }
84 if (mdb_vread(&ops, sizeof (ops), addr) == -1) {
85 mdb_snprintf(out, sz, "??", ops.sobj_type);
86 return;
87 }
88
89 sobj_type_to_text(ops.sobj_type, out, sz);
90 }
91
92 int
sobj_text_to_ops(const char * name,uintptr_t * sobj_ops_out)93 sobj_text_to_ops(const char *name, uintptr_t *sobj_ops_out)
94 {
95 int idx;
96 GElf_Sym sym;
97
98 for (idx = 0; idx < NUM_SOBJ_TYPES; idx++) {
99 struct sobj_type_info *info = &sobj_types[idx];
100 if (strcasecmp(info->sobj_name, name) == 0) {
101 if (mdb_lookup_by_name(info->sobj_ops_name,
102 &sym) == -1) {
103 mdb_warn("unable to find symbol \"%s\"",
104 info->sobj_ops_name);
105 return (-1);
106 }
107 *sobj_ops_out = (uintptr_t)sym.st_value;
108 return (0);
109 }
110 }
111
112 mdb_warn("sobj type \"%s\" unknown\n", name);
113 return (-1);
114 }
115
116 void
sobj_type_walk(void (* cbfunc)(int,const char *,const char *,void *),void * cbarg)117 sobj_type_walk(void (*cbfunc)(int, const char *, const char *, void *),
118 void *cbarg)
119 {
120 int idx;
121
122 for (idx = 0; idx < NUM_SOBJ_TYPES; idx++) {
123 struct sobj_type_info *info = &sobj_types[idx];
124 cbfunc(info->sobj_type, info->sobj_name, info->sobj_ops_name,
125 cbarg);
126 }
127 }
128
129 typedef struct wchan_walk_data {
130 caddr_t *ww_seen;
131 int ww_seen_size;
132 int ww_seen_ndx;
133 uintptr_t ww_thr;
134 sleepq_head_t ww_sleepq[NSLEEPQ];
135 int ww_sleepq_ndx;
136 uintptr_t ww_compare;
137 } wchan_walk_data_t;
138
139 int
wchan_walk_init(mdb_walk_state_t * wsp)140 wchan_walk_init(mdb_walk_state_t *wsp)
141 {
142 wchan_walk_data_t *ww =
143 mdb_zalloc(sizeof (wchan_walk_data_t), UM_SLEEP);
144
145 if (mdb_readvar(&ww->ww_sleepq[0], "sleepq_head") == -1) {
146 mdb_warn("failed to read sleepq");
147 mdb_free(ww, sizeof (wchan_walk_data_t));
148 return (WALK_ERR);
149 }
150
151 if ((ww->ww_compare = wsp->walk_addr) == NULL) {
152 if (mdb_readvar(&ww->ww_seen_size, "nthread") == -1) {
153 mdb_warn("failed to read nthread");
154 mdb_free(ww, sizeof (wchan_walk_data_t));
155 return (WALK_ERR);
156 }
157
158 ww->ww_seen = mdb_alloc(ww->ww_seen_size *
159 sizeof (caddr_t), UM_SLEEP);
160 } else {
161 ww->ww_sleepq_ndx = SQHASHINDEX(wsp->walk_addr);
162 }
163
164 wsp->walk_data = ww;
165 return (WALK_NEXT);
166 }
167
168 int
wchan_walk_step(mdb_walk_state_t * wsp)169 wchan_walk_step(mdb_walk_state_t *wsp)
170 {
171 wchan_walk_data_t *ww = wsp->walk_data;
172 sleepq_head_t *sq;
173 kthread_t thr;
174 uintptr_t t;
175 int i;
176
177 again:
178 /*
179 * Get the address of the first thread on the next sleepq in the
180 * sleepq hash. If ww_compare is set, ww_sleepq_ndx is already
181 * set to the appropriate sleepq index for the desired cv.
182 */
183 for (t = ww->ww_thr; t == NULL; ) {
184 if (ww->ww_sleepq_ndx == NSLEEPQ)
185 return (WALK_DONE);
186
187 sq = &ww->ww_sleepq[ww->ww_sleepq_ndx++];
188 t = (uintptr_t)sq->sq_queue.sq_first;
189
190 /*
191 * If we were looking for a specific cv and we're at the end
192 * of its sleepq, we're done walking.
193 */
194 if (t == NULL && ww->ww_compare != NULL)
195 return (WALK_DONE);
196 }
197
198 /*
199 * Read in the thread. If it's t_wchan pointer is NULL, the thread has
200 * woken up since we took a snapshot of the sleepq (i.e. we are probably
201 * being applied to a live system); we can't believe the t_link pointer
202 * anymore either, so just skip to the next sleepq index.
203 */
204 if (mdb_vread(&thr, sizeof (thr), t) != sizeof (thr)) {
205 mdb_warn("failed to read thread at %p", t);
206 return (WALK_ERR);
207 }
208
209 if (thr.t_wchan == NULL) {
210 ww->ww_thr = NULL;
211 goto again;
212 }
213
214 /*
215 * Set ww_thr to the address of the next thread in the sleepq list.
216 */
217 ww->ww_thr = (uintptr_t)thr.t_link;
218
219 /*
220 * If we're walking a specific cv, invoke the callback if we've
221 * found a match, or loop back to the top and read the next thread.
222 */
223 if (ww->ww_compare != NULL) {
224 if (ww->ww_compare == (uintptr_t)thr.t_wchan)
225 return (wsp->walk_callback(t, &thr, wsp->walk_cbdata));
226
227 if (ww->ww_thr == NULL)
228 return (WALK_DONE);
229
230 goto again;
231 }
232
233 /*
234 * If we're walking all cvs, seen if we've already encountered this one
235 * on the current sleepq. If we have, skip to the next thread.
236 */
237 for (i = 0; i < ww->ww_seen_ndx; i++) {
238 if (ww->ww_seen[i] == thr.t_wchan)
239 goto again;
240 }
241
242 /*
243 * If we're not at the end of a sleepq, save t_wchan; otherwise reset
244 * the seen index so our array is empty at the start of the next sleepq.
245 * If we hit seen_size this is a live kernel and nthread is now larger,
246 * cope by replacing the final element in our memory.
247 */
248 if (ww->ww_thr != NULL) {
249 if (ww->ww_seen_ndx < ww->ww_seen_size)
250 ww->ww_seen[ww->ww_seen_ndx++] = thr.t_wchan;
251 else
252 ww->ww_seen[ww->ww_seen_size - 1] = thr.t_wchan;
253 } else
254 ww->ww_seen_ndx = 0;
255
256 return (wsp->walk_callback((uintptr_t)thr.t_wchan,
257 NULL, wsp->walk_cbdata));
258 }
259
260 void
wchan_walk_fini(mdb_walk_state_t * wsp)261 wchan_walk_fini(mdb_walk_state_t *wsp)
262 {
263 wchan_walk_data_t *ww = wsp->walk_data;
264
265 mdb_free(ww->ww_seen, ww->ww_seen_size * sizeof (uintptr_t));
266 mdb_free(ww, sizeof (wchan_walk_data_t));
267 }
268
269 struct wcdata {
270 sobj_ops_t sobj;
271 int nwaiters;
272 };
273
274 /*ARGSUSED*/
275 static int
wchaninfo_twalk(uintptr_t addr,const kthread_t * t,struct wcdata * wc)276 wchaninfo_twalk(uintptr_t addr, const kthread_t *t, struct wcdata *wc)
277 {
278 if (wc->sobj.sobj_type == SOBJ_NONE) {
279 (void) mdb_vread(&wc->sobj, sizeof (sobj_ops_t),
280 (uintptr_t)t->t_sobj_ops);
281 }
282
283 wc->nwaiters++;
284 return (WALK_NEXT);
285 }
286
287 static int
wchaninfo_vtwalk(uintptr_t addr,const kthread_t * t,int * first)288 wchaninfo_vtwalk(uintptr_t addr, const kthread_t *t, int *first)
289 {
290 proc_t p;
291
292 (void) mdb_vread(&p, sizeof (p), (uintptr_t)t->t_procp);
293
294 if (*first) {
295 *first = 0;
296 mdb_printf(": %0?p %s\n", addr, p.p_user.u_comm);
297 } else {
298 mdb_printf("%*s%0?p %s\n", (int)(sizeof (uintptr_t) * 2 + 17),
299 "", addr, p.p_user.u_comm);
300 }
301
302 return (WALK_NEXT);
303 }
304
305 /*ARGSUSED*/
306 static int
wchaninfo_walk(uintptr_t addr,void * ignored,uint_t * verbose)307 wchaninfo_walk(uintptr_t addr, void *ignored, uint_t *verbose)
308 {
309 struct wcdata wc;
310 int first = 1;
311
312 bzero(&wc, sizeof (wc));
313 wc.sobj.sobj_type = SOBJ_NONE;
314
315 if (mdb_pwalk("wchan", (mdb_walk_cb_t)wchaninfo_twalk, &wc, addr) < 0) {
316 mdb_warn("failed to walk wchan %p", addr);
317 return (WALK_NEXT);
318 }
319
320 mdb_printf("%0?p %4s %8d%s", addr,
321 wc.sobj.sobj_type == SOBJ_CV ? "cond" :
322 wc.sobj.sobj_type == SOBJ_SEMA ? "sema" : "??",
323 wc.nwaiters, (*verbose) ? "" : "\n");
324
325 if (*verbose != 0 && wc.nwaiters != 0 && mdb_pwalk("wchan",
326 (mdb_walk_cb_t)wchaninfo_vtwalk, &first, addr) == -1) {
327 mdb_warn("failed to walk waiters for wchan %p", addr);
328 mdb_printf("\n");
329 }
330
331 return (WALK_NEXT);
332 }
333
334 int
wchaninfo(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)335 wchaninfo(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
336 {
337 uint_t v = FALSE;
338
339 if (mdb_getopts(argc, argv,
340 'v', MDB_OPT_SETBITS, TRUE, &v, NULL) != argc)
341 return (DCMD_USAGE);
342
343 if (v == TRUE) {
344 mdb_printf("%-?s %-4s %8s %-?s %s\n",
345 "ADDR", "TYPE", "NWAITERS", "THREAD", "PROC");
346 } else
347 mdb_printf("%-?s %-4s %8s\n", "ADDR", "TYPE", "NWAITERS");
348
349 if (flags & DCMD_ADDRSPEC) {
350 if (wchaninfo_walk(addr, NULL, &v) == WALK_ERR)
351 return (DCMD_ERR);
352 } else if (mdb_walk("wchan", (mdb_walk_cb_t)wchaninfo_walk, &v) == -1) {
353 mdb_warn("failed to walk wchans");
354 return (DCMD_ERR);
355 }
356
357 return (DCMD_OK);
358 }
359
360 int
blocked_walk_init(mdb_walk_state_t * wsp)361 blocked_walk_init(mdb_walk_state_t *wsp)
362 {
363 if ((wsp->walk_data = (void *)wsp->walk_addr) == NULL) {
364 mdb_warn("must specify a sobj * for blocked walk");
365 return (WALK_ERR);
366 }
367
368 wsp->walk_addr = NULL;
369
370 if (mdb_layered_walk("thread", wsp) == -1) {
371 mdb_warn("couldn't walk 'thread'");
372 return (WALK_ERR);
373 }
374
375 return (WALK_NEXT);
376 }
377
378 int
blocked_walk_step(mdb_walk_state_t * wsp)379 blocked_walk_step(mdb_walk_state_t *wsp)
380 {
381 uintptr_t addr = (uintptr_t)((const kthread_t *)wsp->walk_layer)->t_ts;
382 uintptr_t taddr = wsp->walk_addr;
383 turnstile_t ts;
384
385 if (mdb_vread(&ts, sizeof (ts), addr) == -1) {
386 mdb_warn("couldn't read %p's turnstile at %p", taddr, addr);
387 return (WALK_ERR);
388 }
389
390 if (ts.ts_waiters == 0 || ts.ts_sobj != wsp->walk_data)
391 return (WALK_NEXT);
392
393 return (wsp->walk_callback(taddr, wsp->walk_layer, wsp->walk_cbdata));
394 }
395
396 typedef struct rwlock_block {
397 struct rwlock_block *rw_next;
398 int rw_qnum;
399 uintptr_t rw_thread;
400 } rwlock_block_t;
401
402 static int
rwlock_walk(uintptr_t taddr,const kthread_t * t,rwlock_block_t ** rwp)403 rwlock_walk(uintptr_t taddr, const kthread_t *t, rwlock_block_t **rwp)
404 {
405 turnstile_t ts;
406 uintptr_t addr = (uintptr_t)t->t_ts;
407 rwlock_block_t *rw;
408 int state, i;
409
410 if (mdb_vread(&ts, sizeof (ts), addr) == -1) {
411 mdb_warn("couldn't read %p's turnstile at %p", taddr, addr);
412 return (WALK_ERR);
413 }
414
415 for (i = 0; i < TS_NUM_Q; i++) {
416 if ((uintptr_t)t->t_sleepq ==
417 (uintptr_t)&ts.ts_sleepq[i] - (uintptr_t)&ts + addr)
418 break;
419 }
420
421 if (i == TS_NUM_Q) {
422 if ((state = mdb_get_state()) == MDB_STATE_DEAD ||
423 state == MDB_STATE_STOPPED) {
424 /*
425 * This shouldn't happen post-mortem or under kmdb;
426 * the blocked walk returned a thread which wasn't
427 * actually blocked on its turnstile. This may happen
428 * in-situ if the thread wakes up during the ::rwlock.
429 */
430 mdb_warn("thread %p isn't blocked on ts %p\n",
431 taddr, addr);
432 return (WALK_ERR);
433 }
434
435 return (WALK_NEXT);
436 }
437
438 rw = mdb_alloc(sizeof (rwlock_block_t), UM_SLEEP | UM_GC);
439
440 rw->rw_next = *rwp;
441 rw->rw_qnum = i;
442 rw->rw_thread = taddr;
443 *rwp = rw;
444
445 return (WALK_NEXT);
446 }
447
448 /*
449 * > rwd_rwlock::rwlock
450 * ADDR OWNER/COUNT FLAGS WAITERS
451 * 7835dee8 READERS=1 B011 30004393d20 (W)
452 * ||
453 * WRITE_WANTED -------+|
454 * HAS_WAITERS --------+
455 *
456 * |--ADDR_WIDTH--| |--OWNR_WIDTH--|
457 * |--LBL_OFFSET--||-LBL_WIDTH|
458 * |--------------LONG-------------|
459 * |------------WAITER_OFFSET------------|
460 */
461
462 #ifdef _LP64
463 #define RW_ADDR_WIDTH 16
464 #define RW_OWNR_WIDTH 16
465 #else
466 #define RW_ADDR_WIDTH 8
467 #define RW_OWNR_WIDTH 11
468 #endif
469
470 #define RW_LONG (RW_ADDR_WIDTH + 1 + RW_OWNR_WIDTH)
471 #define RW_LBL_WIDTH 12
472 #define RW_LBL_OFFSET (RW_ADDR_WIDTH + RW_OWNR_WIDTH - 3 - RW_LBL_WIDTH)
473 #define RW_WAITER_OFFSET (RW_LONG + 6)
474
475 /* Access rwlock bits */
476 #define RW_BIT(n, offon) (wwwh & (1 << (n)) ? offon[1] : offon[0])
477 #define RW_BIT_SET(n) (wwwh & (1 << (n)))
478
479 /* Print a waiter (if any) and a newline */
480 #define RW_NEWLINE \
481 if (rw != NULL) { \
482 int q = rw->rw_qnum; \
483 mdb_printf(" %?p (%s)", rw->rw_thread, \
484 q == TS_READER_Q ? "R" : q == TS_WRITER_Q ? "W" : "?"); \
485 rw = rw->rw_next; \
486 } \
487 mdb_printf("\n");
488
489 /*ARGSUSED*/
490 int
rwlock(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)491 rwlock(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
492 {
493 rwlock_impl_t lock;
494 rwlock_block_t *rw = NULL;
495 uintptr_t wwwh;
496
497 if (!(flags & DCMD_ADDRSPEC) || addr == NULL || argc != 0)
498 return (DCMD_USAGE);
499
500 if (mdb_vread(&lock, sizeof (lock), addr) == -1) {
501 mdb_warn("failed to read rwlock at 0x%p", addr);
502 return (DCMD_ERR);
503 }
504
505 if (mdb_pwalk("blocked", (mdb_walk_cb_t)rwlock_walk, &rw, addr) == -1) {
506 mdb_warn("couldn't walk 'blocked' for sobj %p", addr);
507 return (WALK_ERR);
508 }
509
510 mdb_printf("%?s %*s %5s %?s\n", "ADDR",
511 RW_OWNR_WIDTH, "OWNER/COUNT", "FLAGS", "WAITERS");
512
513 mdb_printf("%?p ", addr);
514
515 if (((wwwh = lock.rw_wwwh) & RW_DOUBLE_LOCK) == RW_DOUBLE_LOCK)
516 mdb_printf("%*s", RW_OWNR_WIDTH, "1");
517 else if ((wwwh = lock.rw_wwwh) & RW_WRITE_LOCKED)
518 mdb_printf("%*p", RW_OWNR_WIDTH, wwwh & RW_OWNER);
519 else {
520 uintptr_t count = (wwwh & RW_HOLD_COUNT) >> RW_HOLD_COUNT_SHIFT;
521 char c[20];
522
523 mdb_snprintf(c, 20, "READERS=%ld", count);
524 mdb_printf("%*s", RW_OWNR_WIDTH, count ? c : "-");
525 }
526
527 mdb_printf(" B%c%c%c",
528 RW_BIT(2, "01"), RW_BIT(1, "01"), RW_BIT(0, "01"));
529 RW_NEWLINE;
530
531 mdb_printf("%*s%c %c%c%c", RW_LONG - 1, "",
532 " |"[(wwwh & RW_DOUBLE_LOCK) == RW_DOUBLE_LOCK],
533 RW_BIT(2, " |"), RW_BIT(1, " |"), RW_BIT(0, " |"));
534 RW_NEWLINE;
535
536 if ((wwwh & RW_DOUBLE_LOCK) == RW_DOUBLE_LOCK) {
537 mdb_printf("%*s%*s --+---+", RW_LBL_OFFSET, "", RW_LBL_WIDTH,
538 "DESTROYED");
539 goto no_zero;
540 }
541
542 if (!RW_BIT_SET(2))
543 goto no_two;
544
545 mdb_printf("%*s%*s ------+%c%c", RW_LBL_OFFSET, "", RW_LBL_WIDTH,
546 "WRITE_LOCKED", RW_BIT(1, " |"), RW_BIT(0, " |"));
547 RW_NEWLINE;
548
549 no_two:
550 if (!RW_BIT_SET(1))
551 goto no_one;
552
553 mdb_printf("%*s%*s -------+%c", RW_LBL_OFFSET, "", RW_LBL_WIDTH,
554 "WRITE_WANTED", RW_BIT(0, " |"));
555 RW_NEWLINE;
556
557 no_one:
558 if (!RW_BIT_SET(0))
559 goto no_zero;
560
561 mdb_printf("%*s%*s --------+", RW_LBL_OFFSET, "", RW_LBL_WIDTH,
562 "HAS_WAITERS");
563 RW_NEWLINE;
564
565 no_zero:
566 while (rw != NULL) {
567 mdb_printf("%*s", RW_WAITER_OFFSET, "");
568 RW_NEWLINE;
569 }
570
571 return (DCMD_OK);
572 }
573
574 int
mutex(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)575 mutex(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
576 {
577 mutex_impl_t lock;
578 uint_t force = FALSE;
579
580 if (!(flags & DCMD_ADDRSPEC)) {
581 return (DCMD_USAGE);
582 }
583
584 if (mdb_getopts(argc, argv,
585 'f', MDB_OPT_SETBITS, TRUE, &force, NULL) != argc) {
586 return (DCMD_USAGE);
587 }
588
589 if (mdb_vread(&lock, sizeof (lock), addr) == -1) {
590 mdb_warn("failed to read mutex at 0x%0?p", addr);
591 return (DCMD_ERR);
592 }
593
594 if (DCMD_HDRSPEC(flags)) {
595 mdb_printf("%<u>%?s %5s %?s %6s %6s %7s%</u>\n",
596 "ADDR", "TYPE", "HELD", "MINSPL", "OLDSPL", "WAITERS");
597 }
598
599 if (MUTEX_TYPE_SPIN(&lock)) {
600 struct spin_mutex *sp = &lock.m_spin;
601
602 if (!force && (sp->m_filler != 0 ||
603 sp->m_minspl > PIL_MAX || sp->m_oldspl > PIL_MAX ||
604 (sp->m_spinlock != 0 && sp->m_spinlock != 0xff))) {
605 mdb_warn("%a: invalid spin lock "
606 "(-f to dump anyway)\n", addr);
607 return (DCMD_ERR);
608 }
609
610 if (sp->m_spinlock == 0xff) {
611 mdb_printf("%0?p %5s %?s %6d %6d %7s\n",
612 addr, "spin", "yes", sp->m_minspl, sp->m_oldspl,
613 "-");
614 } else {
615 mdb_printf("%0?p %5s %?s %6d %6s %7s\n",
616 addr, "spin", "no", sp->m_minspl, "-", "-");
617 }
618
619 } else {
620 kthread_t *owner = MUTEX_OWNER(&lock);
621 char *waiters = MUTEX_HAS_WAITERS(&lock) ? "yes" : "no";
622
623 if (!force && (!MUTEX_TYPE_ADAPTIVE(&lock) ||
624 (owner == NULL && MUTEX_HAS_WAITERS(&lock)))) {
625 mdb_warn("%a: invalid adaptive mutex "
626 "(-f to dump anyway)\n", addr);
627 return (DCMD_ERR);
628 }
629
630 if (owner != NULL) {
631 mdb_printf("%0?p %5s %?p %6s %6s %7s\n",
632 addr, "adapt", owner, "-", "-", waiters);
633 } else {
634 mdb_printf("%0?p %5s %?s %6s %6s %7s\n",
635 addr, "adapt", "no", "-", "-", waiters);
636 }
637 }
638 return (DCMD_OK);
639 }
640
641 void
mutex_help(void)642 mutex_help(void)
643 {
644 mdb_printf("Options:\n"
645 " -f force printing even if the data seems to be"
646 " inconsistent\n");
647 }
648
649 int
turnstile(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)650 turnstile(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
651 {
652 turnstile_t t;
653
654 if (argc != 0)
655 return (DCMD_USAGE);
656
657 if (!(flags & DCMD_ADDRSPEC)) {
658 if (mdb_walk_dcmd("turnstile_cache", "turnstile", argc, argv)
659 == -1) {
660 mdb_warn("can't walk turnstiles");
661 return (DCMD_ERR);
662 }
663 return (DCMD_OK);
664 }
665
666 if (DCMD_HDRSPEC(flags))
667 mdb_printf("%<u>%?s %?s %5s %4s %?s %?s%</u>\n",
668 "ADDR", "SOBJ", "WTRS", "EPRI", "ITOR", "PRIOINV");
669
670 if (mdb_vread(&t, sizeof (turnstile_t), addr) == -1) {
671 mdb_warn("can't read turnstile_t at %p", addr);
672 return (DCMD_ERR);
673 }
674
675 mdb_printf("%0?p %?p %5d %4d %?p %?p\n",
676 addr, t.ts_sobj, t.ts_waiters, t.ts_epri,
677 t.ts_inheritor, t.ts_prioinv);
678
679 return (DCMD_OK);
680 }
681
682 /*
683 * Macros and structure definition copied from turnstile.c.
684 * This is unfortunate, but half the macros we need aren't usable from
685 * within mdb anyway.
686 */
687 #define TURNSTILE_HASH_SIZE 128 /* must be power of 2 */
688 #define TURNSTILE_HASH_MASK (TURNSTILE_HASH_SIZE - 1)
689 #define TURNSTILE_SOBJ_HASH(sobj) \
690 ((((int)sobj >> 2) + ((int)sobj >> 9)) & TURNSTILE_HASH_MASK)
691
692 typedef struct turnstile_chain {
693 turnstile_t *tc_first; /* first turnstile on hash chain */
694 disp_lock_t tc_lock; /* lock for this hash chain */
695 } turnstile_chain_t;
696
697 /*
698 * Given the address of a blocked-upon synchronization object, return
699 * the address of its turnstile.
700 */
701
702 /*ARGSUSED*/
703 int
sobj2ts(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)704 sobj2ts(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
705 {
706 GElf_Sym sym;
707 int isupi;
708 int ttoff;
709 uintptr_t ttable;
710 turnstile_t ts, *tsp;
711 turnstile_chain_t tc;
712
713 if (!(flags & DCMD_ADDRSPEC) || argc != 0)
714 return (DCMD_USAGE);
715
716 if (mdb_lookup_by_name("upimutextab", &sym) == -1) {
717 mdb_warn("unable to reference upimutextab\n");
718 return (DCMD_ERR);
719 }
720 isupi = addr - (uintptr_t)sym.st_value < sym.st_size;
721 ttoff = (isupi ? 0 : TURNSTILE_HASH_SIZE) + TURNSTILE_SOBJ_HASH(addr);
722
723 if (mdb_lookup_by_name("turnstile_table", &sym) == -1) {
724 mdb_warn("unable to reference turnstile_table");
725 return (DCMD_ERR);
726 }
727 ttable = (uintptr_t)sym.st_value + sizeof (turnstile_chain_t) * ttoff;
728
729 if (mdb_vread(&tc, sizeof (turnstile_chain_t), ttable) == -1) {
730 mdb_warn("unable to read turnstile_chain_t at %#lx", ttable);
731 return (DCMD_ERR);
732 }
733
734 for (tsp = tc.tc_first; tsp != NULL; tsp = ts.ts_next) {
735 if (mdb_vread(&ts, sizeof (turnstile_t),
736 (uintptr_t)tsp) == -1) {
737 mdb_warn("unable to read turnstile_t at %#p", tsp);
738 return (DCMD_ERR);
739 }
740 if ((uintptr_t)ts.ts_sobj == addr) {
741 mdb_printf("%p\n", tsp);
742 break;
743 }
744 }
745
746 return (DCMD_OK);
747 }
748