xref: /onnv-gate/usr/src/cmd/mdb/common/modules/genunix/sobj.c (revision 8721:4fd0a1c6e9ce)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*8721SJonathan.Adams@Sun.COM  * Common Development and Distribution License (the "License").
6*8721SJonathan.Adams@Sun.COM  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
22*8721SJonathan.Adams@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate #include <mdb/mdb_modapi.h>
270Sstevel@tonic-gate #include <sys/types.h>
280Sstevel@tonic-gate #include <sys/mutex.h>
290Sstevel@tonic-gate #include <sys/thread.h>
300Sstevel@tonic-gate #include <sys/condvar.h>
310Sstevel@tonic-gate #include <sys/sleepq.h>
320Sstevel@tonic-gate #include <sys/sobject.h>
330Sstevel@tonic-gate #include <sys/rwlock_impl.h>
340Sstevel@tonic-gate #include <sys/turnstile.h>
350Sstevel@tonic-gate #include <sys/proc.h>
360Sstevel@tonic-gate #include <sys/mutex_impl.h>
370Sstevel@tonic-gate 
380Sstevel@tonic-gate #include <stdio.h>
390Sstevel@tonic-gate 
40*8721SJonathan.Adams@Sun.COM struct sobj_type_info {
41*8721SJonathan.Adams@Sun.COM 	int		sobj_type;
42*8721SJonathan.Adams@Sun.COM 	const char	*sobj_name;
43*8721SJonathan.Adams@Sun.COM 	const char	*sobj_ops_name;
44*8721SJonathan.Adams@Sun.COM } sobj_types[] = {
45*8721SJonathan.Adams@Sun.COM 	{ SOBJ_MUTEX,	"mutex",	"mutex_sobj_ops" },
46*8721SJonathan.Adams@Sun.COM 	{ SOBJ_RWLOCK,	"rwlock",	"rw_sobj_ops" },
47*8721SJonathan.Adams@Sun.COM 	{ SOBJ_CV,	"cv",		"cv_sobj_ops" },
48*8721SJonathan.Adams@Sun.COM 	{ SOBJ_SEMA,	"sema",		"sema_sobj_ops" },
49*8721SJonathan.Adams@Sun.COM 	{ SOBJ_USER,	"user",		"lwp_sobj_ops" },
50*8721SJonathan.Adams@Sun.COM 	{ SOBJ_USER_PI,	"user_pi",	"lwp_sobj_pi_ops" },
51*8721SJonathan.Adams@Sun.COM 	{ SOBJ_SHUTTLE,	"shuttle",	"shuttle_sobj_ops" }
52*8721SJonathan.Adams@Sun.COM };
53*8721SJonathan.Adams@Sun.COM #define	NUM_SOBJ_TYPES (sizeof (sobj_types) / sizeof (*sobj_types))
54*8721SJonathan.Adams@Sun.COM 
55*8721SJonathan.Adams@Sun.COM void
sobj_type_to_text(int type,char * out,size_t sz)56*8721SJonathan.Adams@Sun.COM sobj_type_to_text(int type, char *out, size_t sz)
57*8721SJonathan.Adams@Sun.COM {
58*8721SJonathan.Adams@Sun.COM 	int idx;
59*8721SJonathan.Adams@Sun.COM 	if (type == SOBJ_NONE) {
60*8721SJonathan.Adams@Sun.COM 		mdb_snprintf(out, sz, "<none>");
61*8721SJonathan.Adams@Sun.COM 		return;
62*8721SJonathan.Adams@Sun.COM 	}
63*8721SJonathan.Adams@Sun.COM 
64*8721SJonathan.Adams@Sun.COM 	for (idx = 0; idx < NUM_SOBJ_TYPES; idx++) {
65*8721SJonathan.Adams@Sun.COM 		struct sobj_type_info *info = &sobj_types[idx];
66*8721SJonathan.Adams@Sun.COM 		if (info->sobj_type == type) {
67*8721SJonathan.Adams@Sun.COM 			mdb_snprintf(out, sz, "%s",
68*8721SJonathan.Adams@Sun.COM 			    sobj_types[idx].sobj_name);
69*8721SJonathan.Adams@Sun.COM 			return;
70*8721SJonathan.Adams@Sun.COM 		}
71*8721SJonathan.Adams@Sun.COM 	}
72*8721SJonathan.Adams@Sun.COM 	mdb_snprintf(out, sz, "<unk:%02x>", type);
73*8721SJonathan.Adams@Sun.COM }
74*8721SJonathan.Adams@Sun.COM 
75*8721SJonathan.Adams@Sun.COM void
sobj_ops_to_text(uintptr_t addr,char * out,size_t sz)76*8721SJonathan.Adams@Sun.COM sobj_ops_to_text(uintptr_t addr, char *out, size_t sz)
77*8721SJonathan.Adams@Sun.COM {
78*8721SJonathan.Adams@Sun.COM 	sobj_ops_t ops;
79*8721SJonathan.Adams@Sun.COM 
80*8721SJonathan.Adams@Sun.COM 	if (addr == 0) {
81*8721SJonathan.Adams@Sun.COM 		mdb_snprintf(out, sz, "<none>");
82*8721SJonathan.Adams@Sun.COM 		return;
83*8721SJonathan.Adams@Sun.COM 	}
84*8721SJonathan.Adams@Sun.COM 	if (mdb_vread(&ops, sizeof (ops), addr) == -1) {
85*8721SJonathan.Adams@Sun.COM 		mdb_snprintf(out, sz, "??", ops.sobj_type);
86*8721SJonathan.Adams@Sun.COM 		return;
87*8721SJonathan.Adams@Sun.COM 	}
88*8721SJonathan.Adams@Sun.COM 
89*8721SJonathan.Adams@Sun.COM 	sobj_type_to_text(ops.sobj_type, out, sz);
90*8721SJonathan.Adams@Sun.COM }
91*8721SJonathan.Adams@Sun.COM 
92*8721SJonathan.Adams@Sun.COM int
sobj_text_to_ops(const char * name,uintptr_t * sobj_ops_out)93*8721SJonathan.Adams@Sun.COM sobj_text_to_ops(const char *name, uintptr_t *sobj_ops_out)
94*8721SJonathan.Adams@Sun.COM {
95*8721SJonathan.Adams@Sun.COM 	int idx;
96*8721SJonathan.Adams@Sun.COM 	GElf_Sym sym;
97*8721SJonathan.Adams@Sun.COM 
98*8721SJonathan.Adams@Sun.COM 	for (idx = 0; idx < NUM_SOBJ_TYPES; idx++) {
99*8721SJonathan.Adams@Sun.COM 		struct sobj_type_info *info = &sobj_types[idx];
100*8721SJonathan.Adams@Sun.COM 		if (strcasecmp(info->sobj_name, name) == 0) {
101*8721SJonathan.Adams@Sun.COM 			if (mdb_lookup_by_name(info->sobj_ops_name,
102*8721SJonathan.Adams@Sun.COM 			    &sym) == -1) {
103*8721SJonathan.Adams@Sun.COM 				mdb_warn("unable to find symbol \"%s\"",
104*8721SJonathan.Adams@Sun.COM 				    info->sobj_ops_name);
105*8721SJonathan.Adams@Sun.COM 				return (-1);
106*8721SJonathan.Adams@Sun.COM 			}
107*8721SJonathan.Adams@Sun.COM 			*sobj_ops_out = (uintptr_t)sym.st_value;
108*8721SJonathan.Adams@Sun.COM 			return (0);
109*8721SJonathan.Adams@Sun.COM 		}
110*8721SJonathan.Adams@Sun.COM 	}
111*8721SJonathan.Adams@Sun.COM 
112*8721SJonathan.Adams@Sun.COM 	mdb_warn("sobj type \"%s\" unknown\n", name);
113*8721SJonathan.Adams@Sun.COM 	return (-1);
114*8721SJonathan.Adams@Sun.COM }
115*8721SJonathan.Adams@Sun.COM 
116*8721SJonathan.Adams@Sun.COM void
sobj_type_walk(void (* cbfunc)(int,const char *,const char *,void *),void * cbarg)117*8721SJonathan.Adams@Sun.COM sobj_type_walk(void (*cbfunc)(int, const char *, const char *, void *),
118*8721SJonathan.Adams@Sun.COM     void *cbarg)
119*8721SJonathan.Adams@Sun.COM {
120*8721SJonathan.Adams@Sun.COM 	int idx;
121*8721SJonathan.Adams@Sun.COM 
122*8721SJonathan.Adams@Sun.COM 	for (idx = 0; idx < NUM_SOBJ_TYPES; idx++) {
123*8721SJonathan.Adams@Sun.COM 		struct sobj_type_info *info = &sobj_types[idx];
124*8721SJonathan.Adams@Sun.COM 		cbfunc(info->sobj_type, info->sobj_name, info->sobj_ops_name,
125*8721SJonathan.Adams@Sun.COM 		    cbarg);
126*8721SJonathan.Adams@Sun.COM 	}
127*8721SJonathan.Adams@Sun.COM }
1280Sstevel@tonic-gate 
1290Sstevel@tonic-gate typedef struct wchan_walk_data {
1300Sstevel@tonic-gate 	caddr_t *ww_seen;
1310Sstevel@tonic-gate 	int ww_seen_size;
1320Sstevel@tonic-gate 	int ww_seen_ndx;
1330Sstevel@tonic-gate 	uintptr_t ww_thr;
1340Sstevel@tonic-gate 	sleepq_head_t ww_sleepq[NSLEEPQ];
1350Sstevel@tonic-gate 	int ww_sleepq_ndx;
1360Sstevel@tonic-gate 	uintptr_t ww_compare;
1370Sstevel@tonic-gate } wchan_walk_data_t;
1380Sstevel@tonic-gate 
1390Sstevel@tonic-gate int
wchan_walk_init(mdb_walk_state_t * wsp)1400Sstevel@tonic-gate wchan_walk_init(mdb_walk_state_t *wsp)
1410Sstevel@tonic-gate {
1420Sstevel@tonic-gate 	wchan_walk_data_t *ww =
1430Sstevel@tonic-gate 	    mdb_zalloc(sizeof (wchan_walk_data_t), UM_SLEEP);
1440Sstevel@tonic-gate 
1450Sstevel@tonic-gate 	if (mdb_readvar(&ww->ww_sleepq[0], "sleepq_head") == -1) {
1460Sstevel@tonic-gate 		mdb_warn("failed to read sleepq");
1470Sstevel@tonic-gate 		mdb_free(ww, sizeof (wchan_walk_data_t));
1480Sstevel@tonic-gate 		return (WALK_ERR);
1490Sstevel@tonic-gate 	}
1500Sstevel@tonic-gate 
1510Sstevel@tonic-gate 	if ((ww->ww_compare = wsp->walk_addr) == NULL) {
1520Sstevel@tonic-gate 		if (mdb_readvar(&ww->ww_seen_size, "nthread") == -1) {
1530Sstevel@tonic-gate 			mdb_warn("failed to read nthread");
1540Sstevel@tonic-gate 			mdb_free(ww, sizeof (wchan_walk_data_t));
1550Sstevel@tonic-gate 			return (WALK_ERR);
1560Sstevel@tonic-gate 		}
1570Sstevel@tonic-gate 
1580Sstevel@tonic-gate 		ww->ww_seen = mdb_alloc(ww->ww_seen_size *
1590Sstevel@tonic-gate 		    sizeof (caddr_t), UM_SLEEP);
1600Sstevel@tonic-gate 	} else {
1610Sstevel@tonic-gate 		ww->ww_sleepq_ndx = SQHASHINDEX(wsp->walk_addr);
1620Sstevel@tonic-gate 	}
1630Sstevel@tonic-gate 
1640Sstevel@tonic-gate 	wsp->walk_data = ww;
1650Sstevel@tonic-gate 	return (WALK_NEXT);
1660Sstevel@tonic-gate }
1670Sstevel@tonic-gate 
1680Sstevel@tonic-gate int
wchan_walk_step(mdb_walk_state_t * wsp)1690Sstevel@tonic-gate wchan_walk_step(mdb_walk_state_t *wsp)
1700Sstevel@tonic-gate {
1710Sstevel@tonic-gate 	wchan_walk_data_t *ww = wsp->walk_data;
1720Sstevel@tonic-gate 	sleepq_head_t *sq;
1730Sstevel@tonic-gate 	kthread_t thr;
1740Sstevel@tonic-gate 	uintptr_t t;
1750Sstevel@tonic-gate 	int i;
1760Sstevel@tonic-gate 
1770Sstevel@tonic-gate again:
1780Sstevel@tonic-gate 	/*
1790Sstevel@tonic-gate 	 * Get the address of the first thread on the next sleepq in the
1800Sstevel@tonic-gate 	 * sleepq hash.  If ww_compare is set, ww_sleepq_ndx is already
1810Sstevel@tonic-gate 	 * set to the appropriate sleepq index for the desired cv.
1820Sstevel@tonic-gate 	 */
1830Sstevel@tonic-gate 	for (t = ww->ww_thr; t == NULL; ) {
1840Sstevel@tonic-gate 		if (ww->ww_sleepq_ndx == NSLEEPQ)
1850Sstevel@tonic-gate 			return (WALK_DONE);
1860Sstevel@tonic-gate 
1870Sstevel@tonic-gate 		sq = &ww->ww_sleepq[ww->ww_sleepq_ndx++];
1880Sstevel@tonic-gate 		t = (uintptr_t)sq->sq_queue.sq_first;
1890Sstevel@tonic-gate 
1900Sstevel@tonic-gate 		/*
1910Sstevel@tonic-gate 		 * If we were looking for a specific cv and we're at the end
1920Sstevel@tonic-gate 		 * of its sleepq, we're done walking.
1930Sstevel@tonic-gate 		 */
1940Sstevel@tonic-gate 		if (t == NULL && ww->ww_compare != NULL)
1950Sstevel@tonic-gate 			return (WALK_DONE);
1960Sstevel@tonic-gate 	}
1970Sstevel@tonic-gate 
1980Sstevel@tonic-gate 	/*
1990Sstevel@tonic-gate 	 * Read in the thread.  If it's t_wchan pointer is NULL, the thread has
2000Sstevel@tonic-gate 	 * woken up since we took a snapshot of the sleepq (i.e. we are probably
2010Sstevel@tonic-gate 	 * being applied to a live system); we can't believe the t_link pointer
2020Sstevel@tonic-gate 	 * anymore either, so just skip to the next sleepq index.
2030Sstevel@tonic-gate 	 */
2040Sstevel@tonic-gate 	if (mdb_vread(&thr, sizeof (thr), t) != sizeof (thr)) {
2050Sstevel@tonic-gate 		mdb_warn("failed to read thread at %p", t);
2060Sstevel@tonic-gate 		return (WALK_ERR);
2070Sstevel@tonic-gate 	}
2080Sstevel@tonic-gate 
2090Sstevel@tonic-gate 	if (thr.t_wchan == NULL) {
2100Sstevel@tonic-gate 		ww->ww_thr = NULL;
2110Sstevel@tonic-gate 		goto again;
2120Sstevel@tonic-gate 	}
2130Sstevel@tonic-gate 
2140Sstevel@tonic-gate 	/*
2150Sstevel@tonic-gate 	 * Set ww_thr to the address of the next thread in the sleepq list.
2160Sstevel@tonic-gate 	 */
2170Sstevel@tonic-gate 	ww->ww_thr = (uintptr_t)thr.t_link;
2180Sstevel@tonic-gate 
2190Sstevel@tonic-gate 	/*
2200Sstevel@tonic-gate 	 * If we're walking a specific cv, invoke the callback if we've
2210Sstevel@tonic-gate 	 * found a match, or loop back to the top and read the next thread.
2220Sstevel@tonic-gate 	 */
2230Sstevel@tonic-gate 	if (ww->ww_compare != NULL) {
2240Sstevel@tonic-gate 		if (ww->ww_compare == (uintptr_t)thr.t_wchan)
2250Sstevel@tonic-gate 			return (wsp->walk_callback(t, &thr, wsp->walk_cbdata));
2260Sstevel@tonic-gate 
2270Sstevel@tonic-gate 		if (ww->ww_thr == NULL)
2280Sstevel@tonic-gate 			return (WALK_DONE);
2290Sstevel@tonic-gate 
2300Sstevel@tonic-gate 		goto again;
2310Sstevel@tonic-gate 	}
2320Sstevel@tonic-gate 
2330Sstevel@tonic-gate 	/*
2340Sstevel@tonic-gate 	 * If we're walking all cvs, seen if we've already encountered this one
2350Sstevel@tonic-gate 	 * on the current sleepq.  If we have, skip to the next thread.
2360Sstevel@tonic-gate 	 */
2370Sstevel@tonic-gate 	for (i = 0; i < ww->ww_seen_ndx; i++) {
2380Sstevel@tonic-gate 		if (ww->ww_seen[i] == thr.t_wchan)
2390Sstevel@tonic-gate 			goto again;
2400Sstevel@tonic-gate 	}
2410Sstevel@tonic-gate 
2420Sstevel@tonic-gate 	/*
2430Sstevel@tonic-gate 	 * If we're not at the end of a sleepq, save t_wchan; otherwise reset
2440Sstevel@tonic-gate 	 * the seen index so our array is empty at the start of the next sleepq.
2450Sstevel@tonic-gate 	 * If we hit seen_size this is a live kernel and nthread is now larger,
2460Sstevel@tonic-gate 	 * cope by replacing the final element in our memory.
2470Sstevel@tonic-gate 	 */
2480Sstevel@tonic-gate 	if (ww->ww_thr != NULL) {
2490Sstevel@tonic-gate 		if (ww->ww_seen_ndx < ww->ww_seen_size)
2500Sstevel@tonic-gate 			ww->ww_seen[ww->ww_seen_ndx++] = thr.t_wchan;
2510Sstevel@tonic-gate 		else
2520Sstevel@tonic-gate 			ww->ww_seen[ww->ww_seen_size - 1] = thr.t_wchan;
2530Sstevel@tonic-gate 	} else
2540Sstevel@tonic-gate 		ww->ww_seen_ndx = 0;
2550Sstevel@tonic-gate 
2560Sstevel@tonic-gate 	return (wsp->walk_callback((uintptr_t)thr.t_wchan,
2570Sstevel@tonic-gate 	    NULL, wsp->walk_cbdata));
2580Sstevel@tonic-gate }
2590Sstevel@tonic-gate 
2600Sstevel@tonic-gate void
wchan_walk_fini(mdb_walk_state_t * wsp)2610Sstevel@tonic-gate wchan_walk_fini(mdb_walk_state_t *wsp)
2620Sstevel@tonic-gate {
2630Sstevel@tonic-gate 	wchan_walk_data_t *ww = wsp->walk_data;
2640Sstevel@tonic-gate 
2650Sstevel@tonic-gate 	mdb_free(ww->ww_seen, ww->ww_seen_size * sizeof (uintptr_t));
2660Sstevel@tonic-gate 	mdb_free(ww, sizeof (wchan_walk_data_t));
2670Sstevel@tonic-gate }
2680Sstevel@tonic-gate 
2690Sstevel@tonic-gate struct wcdata {
2700Sstevel@tonic-gate 	sobj_ops_t sobj;
2710Sstevel@tonic-gate 	int nwaiters;
2720Sstevel@tonic-gate };
2730Sstevel@tonic-gate 
2740Sstevel@tonic-gate /*ARGSUSED*/
2750Sstevel@tonic-gate static int
wchaninfo_twalk(uintptr_t addr,const kthread_t * t,struct wcdata * wc)2760Sstevel@tonic-gate wchaninfo_twalk(uintptr_t addr, const kthread_t *t, struct wcdata *wc)
2770Sstevel@tonic-gate {
2780Sstevel@tonic-gate 	if (wc->sobj.sobj_type == SOBJ_NONE) {
2790Sstevel@tonic-gate 		(void) mdb_vread(&wc->sobj, sizeof (sobj_ops_t),
2800Sstevel@tonic-gate 		    (uintptr_t)t->t_sobj_ops);
2810Sstevel@tonic-gate 	}
2820Sstevel@tonic-gate 
2830Sstevel@tonic-gate 	wc->nwaiters++;
2840Sstevel@tonic-gate 	return (WALK_NEXT);
2850Sstevel@tonic-gate }
2860Sstevel@tonic-gate 
2870Sstevel@tonic-gate static int
wchaninfo_vtwalk(uintptr_t addr,const kthread_t * t,int * first)2880Sstevel@tonic-gate wchaninfo_vtwalk(uintptr_t addr, const kthread_t *t, int *first)
2890Sstevel@tonic-gate {
2900Sstevel@tonic-gate 	proc_t p;
2910Sstevel@tonic-gate 
2920Sstevel@tonic-gate 	(void) mdb_vread(&p, sizeof (p), (uintptr_t)t->t_procp);
2930Sstevel@tonic-gate 
2940Sstevel@tonic-gate 	if (*first) {
2950Sstevel@tonic-gate 		*first = 0;
2960Sstevel@tonic-gate 		mdb_printf(":  %0?p %s\n", addr, p.p_user.u_comm);
2970Sstevel@tonic-gate 	} else {
2980Sstevel@tonic-gate 		mdb_printf("%*s%0?p %s\n", (int)(sizeof (uintptr_t) * 2 + 17),
2990Sstevel@tonic-gate 		    "", addr, p.p_user.u_comm);
3000Sstevel@tonic-gate 	}
3010Sstevel@tonic-gate 
3020Sstevel@tonic-gate 	return (WALK_NEXT);
3030Sstevel@tonic-gate }
3040Sstevel@tonic-gate 
3050Sstevel@tonic-gate /*ARGSUSED*/
3060Sstevel@tonic-gate static int
wchaninfo_walk(uintptr_t addr,void * ignored,uint_t * verbose)3070Sstevel@tonic-gate wchaninfo_walk(uintptr_t addr, void *ignored, uint_t *verbose)
3080Sstevel@tonic-gate {
3090Sstevel@tonic-gate 	struct wcdata wc;
3100Sstevel@tonic-gate 	int first = 1;
3110Sstevel@tonic-gate 
3120Sstevel@tonic-gate 	bzero(&wc, sizeof (wc));
3130Sstevel@tonic-gate 	wc.sobj.sobj_type = SOBJ_NONE;
3140Sstevel@tonic-gate 
3150Sstevel@tonic-gate 	if (mdb_pwalk("wchan", (mdb_walk_cb_t)wchaninfo_twalk, &wc, addr) < 0) {
3160Sstevel@tonic-gate 		mdb_warn("failed to walk wchan %p", addr);
3170Sstevel@tonic-gate 		return (WALK_NEXT);
3180Sstevel@tonic-gate 	}
3190Sstevel@tonic-gate 
3200Sstevel@tonic-gate 	mdb_printf("%0?p %4s %8d%s", addr,
3210Sstevel@tonic-gate 	    wc.sobj.sobj_type == SOBJ_CV ? "cond" :
3220Sstevel@tonic-gate 	    wc.sobj.sobj_type == SOBJ_SEMA ? "sema" : "??",
3230Sstevel@tonic-gate 	    wc.nwaiters, (*verbose) ? "" : "\n");
3240Sstevel@tonic-gate 
3250Sstevel@tonic-gate 	if (*verbose != 0 && wc.nwaiters != 0 && mdb_pwalk("wchan",
3260Sstevel@tonic-gate 	    (mdb_walk_cb_t)wchaninfo_vtwalk, &first, addr) == -1) {
3270Sstevel@tonic-gate 		mdb_warn("failed to walk waiters for wchan %p", addr);
3280Sstevel@tonic-gate 		mdb_printf("\n");
3290Sstevel@tonic-gate 	}
3300Sstevel@tonic-gate 
3310Sstevel@tonic-gate 	return (WALK_NEXT);
3320Sstevel@tonic-gate }
3330Sstevel@tonic-gate 
3340Sstevel@tonic-gate int
wchaninfo(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)3350Sstevel@tonic-gate wchaninfo(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
3360Sstevel@tonic-gate {
3370Sstevel@tonic-gate 	uint_t v = FALSE;
3380Sstevel@tonic-gate 
3390Sstevel@tonic-gate 	if (mdb_getopts(argc, argv,
3400Sstevel@tonic-gate 	    'v', MDB_OPT_SETBITS, TRUE, &v, NULL) != argc)
3410Sstevel@tonic-gate 		return (DCMD_USAGE);
3420Sstevel@tonic-gate 
3430Sstevel@tonic-gate 	if (v == TRUE) {
3440Sstevel@tonic-gate 		mdb_printf("%-?s %-4s %8s   %-?s %s\n",
3450Sstevel@tonic-gate 		    "ADDR", "TYPE", "NWAITERS", "THREAD", "PROC");
3460Sstevel@tonic-gate 	} else
3470Sstevel@tonic-gate 		mdb_printf("%-?s %-4s %8s\n", "ADDR", "TYPE", "NWAITERS");
3480Sstevel@tonic-gate 
3490Sstevel@tonic-gate 	if (flags & DCMD_ADDRSPEC) {
3500Sstevel@tonic-gate 		if (wchaninfo_walk(addr, NULL, &v) == WALK_ERR)
3510Sstevel@tonic-gate 			return (DCMD_ERR);
3520Sstevel@tonic-gate 	} else if (mdb_walk("wchan", (mdb_walk_cb_t)wchaninfo_walk, &v) == -1) {
3530Sstevel@tonic-gate 		mdb_warn("failed to walk wchans");
3540Sstevel@tonic-gate 		return (DCMD_ERR);
3550Sstevel@tonic-gate 	}
3560Sstevel@tonic-gate 
3570Sstevel@tonic-gate 	return (DCMD_OK);
3580Sstevel@tonic-gate }
3590Sstevel@tonic-gate 
3600Sstevel@tonic-gate int
blocked_walk_init(mdb_walk_state_t * wsp)3610Sstevel@tonic-gate blocked_walk_init(mdb_walk_state_t *wsp)
3620Sstevel@tonic-gate {
3630Sstevel@tonic-gate 	if ((wsp->walk_data = (void *)wsp->walk_addr) == NULL) {
3640Sstevel@tonic-gate 		mdb_warn("must specify a sobj * for blocked walk");
3650Sstevel@tonic-gate 		return (WALK_ERR);
3660Sstevel@tonic-gate 	}
3670Sstevel@tonic-gate 
3680Sstevel@tonic-gate 	wsp->walk_addr = NULL;
3690Sstevel@tonic-gate 
3700Sstevel@tonic-gate 	if (mdb_layered_walk("thread", wsp) == -1) {
3710Sstevel@tonic-gate 		mdb_warn("couldn't walk 'thread'");
3720Sstevel@tonic-gate 		return (WALK_ERR);
3730Sstevel@tonic-gate 	}
3740Sstevel@tonic-gate 
3750Sstevel@tonic-gate 	return (WALK_NEXT);
3760Sstevel@tonic-gate }
3770Sstevel@tonic-gate 
3780Sstevel@tonic-gate int
blocked_walk_step(mdb_walk_state_t * wsp)3790Sstevel@tonic-gate blocked_walk_step(mdb_walk_state_t *wsp)
3800Sstevel@tonic-gate {
3810Sstevel@tonic-gate 	uintptr_t addr = (uintptr_t)((const kthread_t *)wsp->walk_layer)->t_ts;
3820Sstevel@tonic-gate 	uintptr_t taddr = wsp->walk_addr;
3830Sstevel@tonic-gate 	turnstile_t ts;
3840Sstevel@tonic-gate 
3850Sstevel@tonic-gate 	if (mdb_vread(&ts, sizeof (ts), addr) == -1) {
3860Sstevel@tonic-gate 		mdb_warn("couldn't read %p's turnstile at %p", taddr, addr);
3870Sstevel@tonic-gate 		return (WALK_ERR);
3880Sstevel@tonic-gate 	}
3890Sstevel@tonic-gate 
3900Sstevel@tonic-gate 	if (ts.ts_waiters == 0 || ts.ts_sobj != wsp->walk_data)
3910Sstevel@tonic-gate 		return (WALK_NEXT);
3920Sstevel@tonic-gate 
3930Sstevel@tonic-gate 	return (wsp->walk_callback(taddr, wsp->walk_layer, wsp->walk_cbdata));
3940Sstevel@tonic-gate }
3950Sstevel@tonic-gate 
3960Sstevel@tonic-gate typedef struct rwlock_block {
3970Sstevel@tonic-gate 	struct rwlock_block *rw_next;
3980Sstevel@tonic-gate 	int rw_qnum;
3990Sstevel@tonic-gate 	uintptr_t rw_thread;
4000Sstevel@tonic-gate } rwlock_block_t;
4010Sstevel@tonic-gate 
4020Sstevel@tonic-gate static int
rwlock_walk(uintptr_t taddr,const kthread_t * t,rwlock_block_t ** rwp)4030Sstevel@tonic-gate rwlock_walk(uintptr_t taddr, const kthread_t *t, rwlock_block_t **rwp)
4040Sstevel@tonic-gate {
4050Sstevel@tonic-gate 	turnstile_t ts;
4060Sstevel@tonic-gate 	uintptr_t addr = (uintptr_t)t->t_ts;
4070Sstevel@tonic-gate 	rwlock_block_t *rw;
4080Sstevel@tonic-gate 	int state, i;
4090Sstevel@tonic-gate 
4100Sstevel@tonic-gate 	if (mdb_vread(&ts, sizeof (ts), addr) == -1) {
4110Sstevel@tonic-gate 		mdb_warn("couldn't read %p's turnstile at %p", taddr, addr);
4120Sstevel@tonic-gate 		return (WALK_ERR);
4130Sstevel@tonic-gate 	}
4140Sstevel@tonic-gate 
4150Sstevel@tonic-gate 	for (i = 0; i < TS_NUM_Q; i++) {
4160Sstevel@tonic-gate 		if ((uintptr_t)t->t_sleepq ==
4170Sstevel@tonic-gate 		    (uintptr_t)&ts.ts_sleepq[i] - (uintptr_t)&ts + addr)
4180Sstevel@tonic-gate 			break;
4190Sstevel@tonic-gate 	}
4200Sstevel@tonic-gate 
4210Sstevel@tonic-gate 	if (i == TS_NUM_Q) {
4220Sstevel@tonic-gate 		if ((state = mdb_get_state()) == MDB_STATE_DEAD ||
4230Sstevel@tonic-gate 		    state == MDB_STATE_STOPPED) {
4240Sstevel@tonic-gate 			/*
4250Sstevel@tonic-gate 			 * This shouldn't happen post-mortem or under kmdb;
4260Sstevel@tonic-gate 			 * the blocked walk returned a thread which wasn't
4270Sstevel@tonic-gate 			 * actually blocked on its turnstile.  This may happen
4280Sstevel@tonic-gate 			 * in-situ if the thread wakes up during the ::rwlock.
4290Sstevel@tonic-gate 			 */
4300Sstevel@tonic-gate 			mdb_warn("thread %p isn't blocked on ts %p\n",
4310Sstevel@tonic-gate 			    taddr, addr);
4320Sstevel@tonic-gate 			return (WALK_ERR);
4330Sstevel@tonic-gate 		}
4340Sstevel@tonic-gate 
4350Sstevel@tonic-gate 		return (WALK_NEXT);
4360Sstevel@tonic-gate 	}
4370Sstevel@tonic-gate 
4380Sstevel@tonic-gate 	rw = mdb_alloc(sizeof (rwlock_block_t), UM_SLEEP | UM_GC);
4390Sstevel@tonic-gate 
4400Sstevel@tonic-gate 	rw->rw_next = *rwp;
4410Sstevel@tonic-gate 	rw->rw_qnum = i;
4420Sstevel@tonic-gate 	rw->rw_thread = taddr;
4430Sstevel@tonic-gate 	*rwp = rw;
4440Sstevel@tonic-gate 
4450Sstevel@tonic-gate 	return (WALK_NEXT);
4460Sstevel@tonic-gate }
4470Sstevel@tonic-gate 
4480Sstevel@tonic-gate /*
4490Sstevel@tonic-gate  * > rwd_rwlock::rwlock
4500Sstevel@tonic-gate  *             ADDR      OWNER/COUNT FLAGS          WAITERS
4510Sstevel@tonic-gate  *         7835dee8        READERS=1  B011      30004393d20 (W)
4520Sstevel@tonic-gate  *                                     ||
4530Sstevel@tonic-gate  *                 WRITE_WANTED -------+|
4540Sstevel@tonic-gate  *                  HAS_WAITERS --------+
4550Sstevel@tonic-gate  *
4560Sstevel@tonic-gate  * |--ADDR_WIDTH--| |--OWNR_WIDTH--|
4570Sstevel@tonic-gate  * |--LBL_OFFSET--||-LBL_WIDTH|
4580Sstevel@tonic-gate  * |--------------LONG-------------|
4590Sstevel@tonic-gate  * |------------WAITER_OFFSET------------|
4600Sstevel@tonic-gate  */
4610Sstevel@tonic-gate 
4620Sstevel@tonic-gate #ifdef _LP64
4630Sstevel@tonic-gate #define	RW_ADDR_WIDTH	16
4640Sstevel@tonic-gate #define	RW_OWNR_WIDTH	16
4650Sstevel@tonic-gate #else
4660Sstevel@tonic-gate #define	RW_ADDR_WIDTH	8
4670Sstevel@tonic-gate #define	RW_OWNR_WIDTH	11
4680Sstevel@tonic-gate #endif
4690Sstevel@tonic-gate 
4700Sstevel@tonic-gate #define	RW_LONG (RW_ADDR_WIDTH + 1 + RW_OWNR_WIDTH)
4710Sstevel@tonic-gate #define	RW_LBL_WIDTH 12
4720Sstevel@tonic-gate #define	RW_LBL_OFFSET (RW_ADDR_WIDTH + RW_OWNR_WIDTH - 3 - RW_LBL_WIDTH)
4730Sstevel@tonic-gate #define	RW_WAITER_OFFSET (RW_LONG + 6)
4740Sstevel@tonic-gate 
4750Sstevel@tonic-gate /* Access rwlock bits */
4760Sstevel@tonic-gate #define	RW_BIT(n, offon) (wwwh & (1 << (n)) ? offon[1] : offon[0])
4770Sstevel@tonic-gate #define	RW_BIT_SET(n) (wwwh & (1 << (n)))
4780Sstevel@tonic-gate 
4790Sstevel@tonic-gate /* Print a waiter (if any) and a newline */
4800Sstevel@tonic-gate #define	RW_NEWLINE \
4810Sstevel@tonic-gate 	if (rw != NULL) { \
4820Sstevel@tonic-gate 		int q = rw->rw_qnum; \
4830Sstevel@tonic-gate 		mdb_printf(" %?p (%s)", rw->rw_thread, \
4840Sstevel@tonic-gate 		    q == TS_READER_Q ? "R" : q == TS_WRITER_Q ? "W" : "?"); \
4850Sstevel@tonic-gate 		rw = rw->rw_next; \
4860Sstevel@tonic-gate 	} \
4870Sstevel@tonic-gate 	mdb_printf("\n");
4880Sstevel@tonic-gate 
4890Sstevel@tonic-gate /*ARGSUSED*/
4900Sstevel@tonic-gate int
rwlock(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)4910Sstevel@tonic-gate rwlock(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
4920Sstevel@tonic-gate {
4930Sstevel@tonic-gate 	rwlock_impl_t lock;
4940Sstevel@tonic-gate 	rwlock_block_t *rw = NULL;
4950Sstevel@tonic-gate 	uintptr_t wwwh;
4960Sstevel@tonic-gate 
4970Sstevel@tonic-gate 	if (!(flags & DCMD_ADDRSPEC) || addr == NULL || argc != 0)
4980Sstevel@tonic-gate 		return (DCMD_USAGE);
4990Sstevel@tonic-gate 
5000Sstevel@tonic-gate 	if (mdb_vread(&lock, sizeof (lock), addr) == -1) {
5010Sstevel@tonic-gate 		mdb_warn("failed to read rwlock at 0x%p", addr);
5020Sstevel@tonic-gate 		return (DCMD_ERR);
5030Sstevel@tonic-gate 	}
5040Sstevel@tonic-gate 
5050Sstevel@tonic-gate 	if (mdb_pwalk("blocked", (mdb_walk_cb_t)rwlock_walk, &rw, addr) == -1) {
5060Sstevel@tonic-gate 		mdb_warn("couldn't walk 'blocked' for sobj %p", addr);
5070Sstevel@tonic-gate 		return (WALK_ERR);
5080Sstevel@tonic-gate 	}
5090Sstevel@tonic-gate 
5100Sstevel@tonic-gate 	mdb_printf("%?s %*s %5s %?s\n", "ADDR",
5110Sstevel@tonic-gate 	    RW_OWNR_WIDTH, "OWNER/COUNT", "FLAGS", "WAITERS");
5120Sstevel@tonic-gate 
5130Sstevel@tonic-gate 	mdb_printf("%?p ", addr);
5140Sstevel@tonic-gate 
5150Sstevel@tonic-gate 	if (((wwwh = lock.rw_wwwh) & RW_DOUBLE_LOCK) == RW_DOUBLE_LOCK)
5160Sstevel@tonic-gate 		mdb_printf("%*s", RW_OWNR_WIDTH, "1");
5170Sstevel@tonic-gate 	else if ((wwwh = lock.rw_wwwh) & RW_WRITE_LOCKED)
5180Sstevel@tonic-gate 		mdb_printf("%*p", RW_OWNR_WIDTH, wwwh & RW_OWNER);
5190Sstevel@tonic-gate 	else {
5200Sstevel@tonic-gate 		uintptr_t count = (wwwh & RW_HOLD_COUNT) >> RW_HOLD_COUNT_SHIFT;
5210Sstevel@tonic-gate 		char c[20];
5220Sstevel@tonic-gate 
5230Sstevel@tonic-gate 		mdb_snprintf(c, 20, "READERS=%ld", count);
5240Sstevel@tonic-gate 		mdb_printf("%*s", RW_OWNR_WIDTH, count ? c : "-");
5250Sstevel@tonic-gate 	}
5260Sstevel@tonic-gate 
5270Sstevel@tonic-gate 	mdb_printf("  B%c%c%c",
5280Sstevel@tonic-gate 	    RW_BIT(2, "01"), RW_BIT(1, "01"), RW_BIT(0, "01"));
5290Sstevel@tonic-gate 	RW_NEWLINE;
5300Sstevel@tonic-gate 
5310Sstevel@tonic-gate 	mdb_printf("%*s%c   %c%c%c", RW_LONG - 1, "",
5320Sstevel@tonic-gate 	    " |"[(wwwh & RW_DOUBLE_LOCK) == RW_DOUBLE_LOCK],
5330Sstevel@tonic-gate 	    RW_BIT(2, " |"), RW_BIT(1, " |"), RW_BIT(0, " |"));
5340Sstevel@tonic-gate 	RW_NEWLINE;
5350Sstevel@tonic-gate 
5360Sstevel@tonic-gate 	if ((wwwh & RW_DOUBLE_LOCK) == RW_DOUBLE_LOCK) {
5370Sstevel@tonic-gate 		mdb_printf("%*s%*s --+---+", RW_LBL_OFFSET, "", RW_LBL_WIDTH,
5380Sstevel@tonic-gate 		    "DESTROYED");
5390Sstevel@tonic-gate 		goto no_zero;
5400Sstevel@tonic-gate 	}
5410Sstevel@tonic-gate 
5420Sstevel@tonic-gate 	if (!RW_BIT_SET(2))
5430Sstevel@tonic-gate 		goto no_two;
5440Sstevel@tonic-gate 
5450Sstevel@tonic-gate 	mdb_printf("%*s%*s ------+%c%c", RW_LBL_OFFSET, "", RW_LBL_WIDTH,
5460Sstevel@tonic-gate 	    "WRITE_LOCKED", RW_BIT(1, " |"), RW_BIT(0, " |"));
5470Sstevel@tonic-gate 	RW_NEWLINE;
5480Sstevel@tonic-gate 
5490Sstevel@tonic-gate no_two:
5500Sstevel@tonic-gate 	if (!RW_BIT_SET(1))
5510Sstevel@tonic-gate 		goto no_one;
5520Sstevel@tonic-gate 
5530Sstevel@tonic-gate 	mdb_printf("%*s%*s -------+%c", RW_LBL_OFFSET, "", RW_LBL_WIDTH,
5540Sstevel@tonic-gate 	    "WRITE_WANTED", RW_BIT(0, " |"));
5550Sstevel@tonic-gate 	RW_NEWLINE;
5560Sstevel@tonic-gate 
5570Sstevel@tonic-gate no_one:
5580Sstevel@tonic-gate 	if (!RW_BIT_SET(0))
5590Sstevel@tonic-gate 		goto no_zero;
5600Sstevel@tonic-gate 
5610Sstevel@tonic-gate 	mdb_printf("%*s%*s --------+", RW_LBL_OFFSET, "", RW_LBL_WIDTH,
5620Sstevel@tonic-gate 	    "HAS_WAITERS");
5630Sstevel@tonic-gate 	RW_NEWLINE;
5640Sstevel@tonic-gate 
5650Sstevel@tonic-gate no_zero:
5660Sstevel@tonic-gate 	while (rw != NULL) {
5670Sstevel@tonic-gate 		mdb_printf("%*s", RW_WAITER_OFFSET, "");
5680Sstevel@tonic-gate 		RW_NEWLINE;
5690Sstevel@tonic-gate 	}
5700Sstevel@tonic-gate 
5710Sstevel@tonic-gate 	return (DCMD_OK);
5720Sstevel@tonic-gate }
5730Sstevel@tonic-gate 
5740Sstevel@tonic-gate int
mutex(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)5750Sstevel@tonic-gate mutex(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
5760Sstevel@tonic-gate {
5770Sstevel@tonic-gate 	mutex_impl_t	lock;
5780Sstevel@tonic-gate 	uint_t		force = FALSE;
5790Sstevel@tonic-gate 
5800Sstevel@tonic-gate 	if (!(flags & DCMD_ADDRSPEC)) {
5810Sstevel@tonic-gate 		return (DCMD_USAGE);
5820Sstevel@tonic-gate 	}
5830Sstevel@tonic-gate 
5840Sstevel@tonic-gate 	if (mdb_getopts(argc, argv,
5850Sstevel@tonic-gate 	    'f', MDB_OPT_SETBITS, TRUE, &force, NULL) != argc) {
5860Sstevel@tonic-gate 		return (DCMD_USAGE);
5870Sstevel@tonic-gate 	}
5880Sstevel@tonic-gate 
5890Sstevel@tonic-gate 	if (mdb_vread(&lock, sizeof (lock), addr) == -1) {
5900Sstevel@tonic-gate 		mdb_warn("failed to read mutex at 0x%0?p", addr);
5910Sstevel@tonic-gate 		return (DCMD_ERR);
5920Sstevel@tonic-gate 	}
5930Sstevel@tonic-gate 
5940Sstevel@tonic-gate 	if (DCMD_HDRSPEC(flags)) {
5950Sstevel@tonic-gate 		mdb_printf("%<u>%?s %5s %?s %6s %6s %7s%</u>\n",
5960Sstevel@tonic-gate 		    "ADDR", "TYPE", "HELD", "MINSPL", "OLDSPL", "WAITERS");
5970Sstevel@tonic-gate 	}
5980Sstevel@tonic-gate 
5990Sstevel@tonic-gate 	if (MUTEX_TYPE_SPIN(&lock)) {
6000Sstevel@tonic-gate 		struct spin_mutex *sp = &lock.m_spin;
6010Sstevel@tonic-gate 
6020Sstevel@tonic-gate 		if (!force && (sp->m_filler != 0 ||
6030Sstevel@tonic-gate 		    sp->m_minspl > PIL_MAX || sp->m_oldspl > PIL_MAX ||
6040Sstevel@tonic-gate 		    (sp->m_spinlock != 0 && sp->m_spinlock != 0xff))) {
6050Sstevel@tonic-gate 			mdb_warn("%a: invalid spin lock "
6060Sstevel@tonic-gate 			    "(-f to dump anyway)\n", addr);
6070Sstevel@tonic-gate 			return (DCMD_ERR);
6080Sstevel@tonic-gate 		}
6090Sstevel@tonic-gate 
6100Sstevel@tonic-gate 		if (sp->m_spinlock == 0xff) {
6110Sstevel@tonic-gate 			mdb_printf("%0?p %5s %?s %6d %6d %7s\n",
6120Sstevel@tonic-gate 			    addr, "spin", "yes", sp->m_minspl, sp->m_oldspl,
6130Sstevel@tonic-gate 			    "-");
6140Sstevel@tonic-gate 		} else {
6150Sstevel@tonic-gate 			mdb_printf("%0?p %5s %?s %6d %6s %7s\n",
6160Sstevel@tonic-gate 			    addr, "spin", "no", sp->m_minspl, "-", "-");
6170Sstevel@tonic-gate 		}
6180Sstevel@tonic-gate 
6190Sstevel@tonic-gate 	} else {
6200Sstevel@tonic-gate 		kthread_t *owner = MUTEX_OWNER(&lock);
6210Sstevel@tonic-gate 		char *waiters = MUTEX_HAS_WAITERS(&lock) ? "yes" : "no";
6220Sstevel@tonic-gate 
6230Sstevel@tonic-gate 		if (!force && (!MUTEX_TYPE_ADAPTIVE(&lock) ||
6240Sstevel@tonic-gate 		    (owner == NULL && MUTEX_HAS_WAITERS(&lock)))) {
6250Sstevel@tonic-gate 			mdb_warn("%a: invalid adaptive mutex "
6260Sstevel@tonic-gate 			    "(-f to dump anyway)\n", addr);
6270Sstevel@tonic-gate 			return (DCMD_ERR);
6280Sstevel@tonic-gate 		}
6290Sstevel@tonic-gate 
6300Sstevel@tonic-gate 		if (owner != NULL) {
6310Sstevel@tonic-gate 			mdb_printf("%0?p %5s %?p %6s %6s %7s\n",
6320Sstevel@tonic-gate 			    addr, "adapt", owner, "-", "-", waiters);
6330Sstevel@tonic-gate 		} else {
6340Sstevel@tonic-gate 			mdb_printf("%0?p %5s %?s %6s %6s %7s\n",
6350Sstevel@tonic-gate 			    addr, "adapt", "no", "-", "-", waiters);
6360Sstevel@tonic-gate 		}
6370Sstevel@tonic-gate 	}
6380Sstevel@tonic-gate 	return (DCMD_OK);
6390Sstevel@tonic-gate }
6400Sstevel@tonic-gate 
6410Sstevel@tonic-gate void
mutex_help(void)6420Sstevel@tonic-gate mutex_help(void)
6430Sstevel@tonic-gate {
6440Sstevel@tonic-gate 	mdb_printf("Options:\n"
6450Sstevel@tonic-gate 	    "   -f    force printing even if the data seems to be"
6460Sstevel@tonic-gate 	    " inconsistent\n");
6470Sstevel@tonic-gate }
6480Sstevel@tonic-gate 
6490Sstevel@tonic-gate int
turnstile(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)6500Sstevel@tonic-gate turnstile(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
6510Sstevel@tonic-gate {
6520Sstevel@tonic-gate 	turnstile_t	t;
6530Sstevel@tonic-gate 
6540Sstevel@tonic-gate 	if (argc != 0)
6550Sstevel@tonic-gate 		return (DCMD_USAGE);
6560Sstevel@tonic-gate 
6570Sstevel@tonic-gate 	if (!(flags & DCMD_ADDRSPEC)) {
6580Sstevel@tonic-gate 		if (mdb_walk_dcmd("turnstile_cache", "turnstile", argc, argv)
6590Sstevel@tonic-gate 		    == -1) {
6600Sstevel@tonic-gate 			mdb_warn("can't walk turnstiles");
6610Sstevel@tonic-gate 			return (DCMD_ERR);
6620Sstevel@tonic-gate 		}
6630Sstevel@tonic-gate 		return (DCMD_OK);
6640Sstevel@tonic-gate 	}
6650Sstevel@tonic-gate 
6660Sstevel@tonic-gate 	if (DCMD_HDRSPEC(flags))
6670Sstevel@tonic-gate 		mdb_printf("%<u>%?s %?s %5s %4s %?s %?s%</u>\n",
6680Sstevel@tonic-gate 		    "ADDR", "SOBJ", "WTRS", "EPRI", "ITOR", "PRIOINV");
6690Sstevel@tonic-gate 
6700Sstevel@tonic-gate 	if (mdb_vread(&t, sizeof (turnstile_t), addr) == -1) {
6710Sstevel@tonic-gate 		mdb_warn("can't read turnstile_t at %p", addr);
6720Sstevel@tonic-gate 		return (DCMD_ERR);
6730Sstevel@tonic-gate 	}
6740Sstevel@tonic-gate 
6750Sstevel@tonic-gate 	mdb_printf("%0?p %?p %5d %4d %?p %?p\n",
6760Sstevel@tonic-gate 	    addr, t.ts_sobj, t.ts_waiters, t.ts_epri,
6770Sstevel@tonic-gate 	    t.ts_inheritor, t.ts_prioinv);
6780Sstevel@tonic-gate 
6790Sstevel@tonic-gate 	return (DCMD_OK);
6800Sstevel@tonic-gate }
6810Sstevel@tonic-gate 
6820Sstevel@tonic-gate /*
6830Sstevel@tonic-gate  * Macros and structure definition copied from turnstile.c.
6840Sstevel@tonic-gate  * This is unfortunate, but half the macros we need aren't usable from
6850Sstevel@tonic-gate  * within mdb anyway.
6860Sstevel@tonic-gate  */
6870Sstevel@tonic-gate #define	TURNSTILE_HASH_SIZE	128		/* must be power of 2 */
6880Sstevel@tonic-gate #define	TURNSTILE_HASH_MASK	(TURNSTILE_HASH_SIZE - 1)
6890Sstevel@tonic-gate #define	TURNSTILE_SOBJ_HASH(sobj)	\
6900Sstevel@tonic-gate 	((((int)sobj >> 2) + ((int)sobj >> 9)) & TURNSTILE_HASH_MASK)
6910Sstevel@tonic-gate 
6920Sstevel@tonic-gate typedef struct turnstile_chain {
6930Sstevel@tonic-gate 	turnstile_t	*tc_first;	/* first turnstile on hash chain */
6940Sstevel@tonic-gate 	disp_lock_t	tc_lock;	/* lock for this hash chain */
6950Sstevel@tonic-gate } turnstile_chain_t;
6960Sstevel@tonic-gate 
6970Sstevel@tonic-gate /*
6980Sstevel@tonic-gate  * Given the address of a blocked-upon synchronization object, return
6990Sstevel@tonic-gate  * the address of its turnstile.
7000Sstevel@tonic-gate  */
7010Sstevel@tonic-gate 
7020Sstevel@tonic-gate /*ARGSUSED*/
7030Sstevel@tonic-gate int
sobj2ts(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)7040Sstevel@tonic-gate sobj2ts(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
7050Sstevel@tonic-gate {
7060Sstevel@tonic-gate 	GElf_Sym	sym;
7070Sstevel@tonic-gate 	int		isupi;
7080Sstevel@tonic-gate 	int		ttoff;
7090Sstevel@tonic-gate 	uintptr_t	ttable;
7100Sstevel@tonic-gate 	turnstile_t	ts, *tsp;
7110Sstevel@tonic-gate 	turnstile_chain_t tc;
7120Sstevel@tonic-gate 
7130Sstevel@tonic-gate 	if (!(flags & DCMD_ADDRSPEC) || argc != 0)
7140Sstevel@tonic-gate 		return (DCMD_USAGE);
7150Sstevel@tonic-gate 
7160Sstevel@tonic-gate 	if (mdb_lookup_by_name("upimutextab", &sym) == -1) {
7170Sstevel@tonic-gate 		mdb_warn("unable to reference upimutextab\n");
7180Sstevel@tonic-gate 		return (DCMD_ERR);
7190Sstevel@tonic-gate 	}
7200Sstevel@tonic-gate 	isupi = addr - (uintptr_t)sym.st_value < sym.st_size;
7210Sstevel@tonic-gate 	ttoff = (isupi ? 0 : TURNSTILE_HASH_SIZE) + TURNSTILE_SOBJ_HASH(addr);
7220Sstevel@tonic-gate 
7230Sstevel@tonic-gate 	if (mdb_lookup_by_name("turnstile_table", &sym) == -1) {
7240Sstevel@tonic-gate 		mdb_warn("unable to reference turnstile_table");
7250Sstevel@tonic-gate 		return (DCMD_ERR);
7260Sstevel@tonic-gate 	}
7270Sstevel@tonic-gate 	ttable = (uintptr_t)sym.st_value + sizeof (turnstile_chain_t) * ttoff;
7280Sstevel@tonic-gate 
7290Sstevel@tonic-gate 	if (mdb_vread(&tc, sizeof (turnstile_chain_t), ttable) == -1) {
7300Sstevel@tonic-gate 		mdb_warn("unable to read turnstile_chain_t at %#lx", ttable);
7310Sstevel@tonic-gate 		return (DCMD_ERR);
7320Sstevel@tonic-gate 	}
7330Sstevel@tonic-gate 
7340Sstevel@tonic-gate 	for (tsp = tc.tc_first; tsp != NULL; tsp = ts.ts_next) {
7350Sstevel@tonic-gate 		if (mdb_vread(&ts, sizeof (turnstile_t),
7360Sstevel@tonic-gate 		    (uintptr_t)tsp) == -1)  {
7370Sstevel@tonic-gate 			mdb_warn("unable to read turnstile_t at %#p", tsp);
7380Sstevel@tonic-gate 			return (DCMD_ERR);
7390Sstevel@tonic-gate 		}
7400Sstevel@tonic-gate 		if ((uintptr_t)ts.ts_sobj == addr) {
7410Sstevel@tonic-gate 			mdb_printf("%p\n", tsp);
7420Sstevel@tonic-gate 			break;
7430Sstevel@tonic-gate 		}
7440Sstevel@tonic-gate 	}
7450Sstevel@tonic-gate 
7460Sstevel@tonic-gate 	return (DCMD_OK);
7470Sstevel@tonic-gate }
748