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
40struct 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
55void
56sobj_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
75void
76sobj_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
92int
93sobj_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
116void
117sobj_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
129typedef 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
139int
140wchan_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
168int
169wchan_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
177again:
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
260void
261wchan_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
269struct wcdata {
270	sobj_ops_t sobj;
271	int nwaiters;
272};
273
274/*ARGSUSED*/
275static int
276wchaninfo_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
287static int
288wchaninfo_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*/
306static int
307wchaninfo_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
334int
335wchaninfo(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
360int
361blocked_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
378int
379blocked_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
396typedef struct rwlock_block {
397	struct rwlock_block *rw_next;
398	int rw_qnum;
399	uintptr_t rw_thread;
400} rwlock_block_t;
401
402static int
403rwlock_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*/
490int
491rwlock(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
549no_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
557no_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
565no_zero:
566	while (rw != NULL) {
567		mdb_printf("%*s", RW_WAITER_OFFSET, "");
568		RW_NEWLINE;
569	}
570
571	return (DCMD_OK);
572}
573
574int
575mutex(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
641void
642mutex_help(void)
643{
644	mdb_printf("Options:\n"
645	    "   -f    force printing even if the data seems to be"
646	    " inconsistent\n");
647}
648
649int
650turnstile(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
692typedef 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*/
703int
704sobj2ts(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