1e0ad97e3SJonathan Adams /*
2e0ad97e3SJonathan Adams  * CDDL HEADER START
3e0ad97e3SJonathan Adams  *
4e0ad97e3SJonathan Adams  * The contents of this file are subject to the terms of the
5e0ad97e3SJonathan Adams  * Common Development and Distribution License (the "License").
6e0ad97e3SJonathan Adams  * You may not use this file except in compliance with the License.
7e0ad97e3SJonathan Adams  *
8e0ad97e3SJonathan Adams  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9e0ad97e3SJonathan Adams  * or http://www.opensolaris.org/os/licensing.
10e0ad97e3SJonathan Adams  * See the License for the specific language governing permissions
11e0ad97e3SJonathan Adams  * and limitations under the License.
12e0ad97e3SJonathan Adams  *
13e0ad97e3SJonathan Adams  * When distributing Covered Code, include this CDDL HEADER in each
14e0ad97e3SJonathan Adams  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15e0ad97e3SJonathan Adams  * If applicable, add the following below this CDDL HEADER, with the
16e0ad97e3SJonathan Adams  * fields enclosed by brackets "[]" replaced with your own identifying
17e0ad97e3SJonathan Adams  * information: Portions Copyright [yyyy] [name of copyright owner]
18e0ad97e3SJonathan Adams  *
19e0ad97e3SJonathan Adams  * CDDL HEADER END
20e0ad97e3SJonathan Adams  */
21e0ad97e3SJonathan Adams /*
22e0ad97e3SJonathan Adams  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23e0ad97e3SJonathan Adams  * Use is subject to license terms.
24e0ad97e3SJonathan Adams  */
25e0ad97e3SJonathan Adams 
26e0ad97e3SJonathan Adams #include <mdb/mdb_param.h>
27e0ad97e3SJonathan Adams #include <mdb/mdb_modapi.h>
28e0ad97e3SJonathan Adams #include <mdb/mdb_ks.h>
29e0ad97e3SJonathan Adams #include <sys/taskq.h>
30e0ad97e3SJonathan Adams #include <sys/taskq_impl.h>
31e0ad97e3SJonathan Adams 
32e0ad97e3SJonathan Adams #include "taskq.h"
33e0ad97e3SJonathan Adams 
34e0ad97e3SJonathan Adams typedef struct tqarray_ent {
35e0ad97e3SJonathan Adams 	uintptr_t	tq_addr;
36e0ad97e3SJonathan Adams 	char		tq_name[TASKQ_NAMELEN + 1];
37e0ad97e3SJonathan Adams 	int		tq_instance;
38e0ad97e3SJonathan Adams 	uint_t		tq_flags;
39e0ad97e3SJonathan Adams } tqarray_ent_t;
40e0ad97e3SJonathan Adams 
41e0ad97e3SJonathan Adams typedef struct tq_info {
42e0ad97e3SJonathan Adams 	tqarray_ent_t	*tqi_array;
43e0ad97e3SJonathan Adams 	size_t		tqi_count;
44e0ad97e3SJonathan Adams 	size_t		tqi_size;
45e0ad97e3SJonathan Adams } tq_info_t;
46e0ad97e3SJonathan Adams 
47e0ad97e3SJonathan Adams /*
48e0ad97e3SJonathan Adams  * We sort taskqs as follows:
49e0ad97e3SJonathan Adams  *
50e0ad97e3SJonathan Adams  *	DYNAMIC last
51e0ad97e3SJonathan Adams  *	NOINSTANCE first
52e0ad97e3SJonathan Adams  *	within NOINSTANCE, sort by order of creation (instance #)
53e0ad97e3SJonathan Adams  *	within non-NOINSTANCE, sort by name (case-insensitive) then instance #
54e0ad97e3SJonathan Adams  */
55e0ad97e3SJonathan Adams int
tqcmp(const void * lhs,const void * rhs)56e0ad97e3SJonathan Adams tqcmp(const void *lhs, const void *rhs)
57e0ad97e3SJonathan Adams {
58e0ad97e3SJonathan Adams 	const tqarray_ent_t *l = lhs;
59e0ad97e3SJonathan Adams 	const tqarray_ent_t *r = rhs;
60e0ad97e3SJonathan Adams 	uint_t lflags = l->tq_flags;
61e0ad97e3SJonathan Adams 	uint_t rflags = r->tq_flags;
62e0ad97e3SJonathan Adams 	int ret;
63e0ad97e3SJonathan Adams 
64e0ad97e3SJonathan Adams 	if ((lflags & TASKQ_DYNAMIC) && !(rflags & TASKQ_DYNAMIC))
65e0ad97e3SJonathan Adams 		return (1);
66e0ad97e3SJonathan Adams 	if (!(lflags & TASKQ_DYNAMIC) && (rflags & TASKQ_DYNAMIC))
67e0ad97e3SJonathan Adams 		return (-1);
68e0ad97e3SJonathan Adams 
69e0ad97e3SJonathan Adams 	if ((lflags & TASKQ_NOINSTANCE) && !(rflags & TASKQ_NOINSTANCE))
70e0ad97e3SJonathan Adams 		return (-1);
71e0ad97e3SJonathan Adams 	if (!(lflags & TASKQ_NOINSTANCE) && (rflags & TASKQ_NOINSTANCE))
72e0ad97e3SJonathan Adams 		return (1);
73e0ad97e3SJonathan Adams 
74e0ad97e3SJonathan Adams 	if (!(lflags & TASKQ_NOINSTANCE) &&
75e0ad97e3SJonathan Adams 	    (ret = strcasecmp(l->tq_name, r->tq_name)) != 0)
76e0ad97e3SJonathan Adams 		return (ret);
77e0ad97e3SJonathan Adams 
78e0ad97e3SJonathan Adams 	if (l->tq_instance < r->tq_instance)
79e0ad97e3SJonathan Adams 		return (-1);
80e0ad97e3SJonathan Adams 	if (l->tq_instance > r->tq_instance)
81e0ad97e3SJonathan Adams 		return (1);
82e0ad97e3SJonathan Adams 	return (0);
83e0ad97e3SJonathan Adams }
84e0ad97e3SJonathan Adams 
85e0ad97e3SJonathan Adams /*ARGSUSED*/
86e0ad97e3SJonathan Adams int
tq_count(uintptr_t addr,const void * ignored,void * arg)87e0ad97e3SJonathan Adams tq_count(uintptr_t addr, const void *ignored, void *arg)
88e0ad97e3SJonathan Adams {
89e0ad97e3SJonathan Adams 	tq_info_t *ti = arg;
90e0ad97e3SJonathan Adams 
91e0ad97e3SJonathan Adams 	ti->tqi_size++;
92e0ad97e3SJonathan Adams 	return (WALK_NEXT);
93e0ad97e3SJonathan Adams }
94e0ad97e3SJonathan Adams 
95e0ad97e3SJonathan Adams /*ARGSUSED*/
96e0ad97e3SJonathan Adams int
tq_fill(uintptr_t addr,const void * ignored,tq_info_t * ti)97e0ad97e3SJonathan Adams tq_fill(uintptr_t addr, const void *ignored, tq_info_t *ti)
98e0ad97e3SJonathan Adams {
99e0ad97e3SJonathan Adams 	int idx = ti->tqi_count;
100e0ad97e3SJonathan Adams 	taskq_t tq;
101e0ad97e3SJonathan Adams 	tqarray_ent_t *tqe = &ti->tqi_array[idx];
102e0ad97e3SJonathan Adams 
103e0ad97e3SJonathan Adams 	if (idx == ti->tqi_size) {
104e0ad97e3SJonathan Adams 		mdb_warn("taskq: inadequate slop\n");
105e0ad97e3SJonathan Adams 		return (WALK_ERR);
106e0ad97e3SJonathan Adams 	}
107e0ad97e3SJonathan Adams 	if (mdb_vread(&tq, sizeof (tq), addr) == -1) {
108e0ad97e3SJonathan Adams 		mdb_warn("unable to read taskq_t at %p", addr);
109e0ad97e3SJonathan Adams 		return (WALK_NEXT);
110e0ad97e3SJonathan Adams 	}
111e0ad97e3SJonathan Adams 
112e0ad97e3SJonathan Adams 	ti->tqi_count++;
113e0ad97e3SJonathan Adams 	tqe->tq_addr = addr;
114e0ad97e3SJonathan Adams 	strncpy(tqe->tq_name, tq.tq_name, TASKQ_NAMELEN);
115e0ad97e3SJonathan Adams 	tqe->tq_instance = tq.tq_instance;
116e0ad97e3SJonathan Adams 	tqe->tq_flags = tq.tq_flags;
117e0ad97e3SJonathan Adams 
118e0ad97e3SJonathan Adams 	return (WALK_NEXT);
119e0ad97e3SJonathan Adams }
120e0ad97e3SJonathan Adams 
121e0ad97e3SJonathan Adams void
taskq_help(void)122e0ad97e3SJonathan Adams taskq_help(void)
123e0ad97e3SJonathan Adams {
124e0ad97e3SJonathan Adams 	mdb_printf("%s",
125e0ad97e3SJonathan Adams 	    "  -a    Only show taskqs with active threads.\n"
126e0ad97e3SJonathan Adams 	    "  -t    Display active thread stacks in each taskq.\n"
127e0ad97e3SJonathan Adams 	    "  -T    Display all thread stacks in each taskq.\n"
128e0ad97e3SJonathan Adams 	    "  -m min_maxq\n"
129e0ad97e3SJonathan Adams 	    "        Only show Dynamic taskqs and taskqs with a MAXQ of at\n"
130e0ad97e3SJonathan Adams 	    "        least min_maxq.\n"
131e0ad97e3SJonathan Adams 	    "  -n name\n"
132e0ad97e3SJonathan Adams 	    "        Only show taskqs which contain name somewhere in their\n"
133e0ad97e3SJonathan Adams 	    "        name.\n");
134e0ad97e3SJonathan Adams }
135e0ad97e3SJonathan Adams 
136e0ad97e3SJonathan Adams int
taskq(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)137e0ad97e3SJonathan Adams taskq(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
138e0ad97e3SJonathan Adams {
139e0ad97e3SJonathan Adams 	taskq_t tq;
140e0ad97e3SJonathan Adams 
141e0ad97e3SJonathan Adams 	const char *name = NULL;
142e0ad97e3SJonathan Adams 	uintptr_t minmaxq = 0;
143e0ad97e3SJonathan Adams 	uint_t	active = FALSE;
144e0ad97e3SJonathan Adams 	uint_t	print_threads = FALSE;
145e0ad97e3SJonathan Adams 	uint_t	print_threads_all = FALSE;
146e0ad97e3SJonathan Adams 
147e0ad97e3SJonathan Adams 	size_t tact, tcount, queued, maxq;
148e0ad97e3SJonathan Adams 
149e0ad97e3SJonathan Adams 	if (mdb_getopts(argc, argv,
150e0ad97e3SJonathan Adams 	    'a', MDB_OPT_SETBITS, TRUE, &active,
151e0ad97e3SJonathan Adams 	    'm', MDB_OPT_UINTPTR, &minmaxq,
152e0ad97e3SJonathan Adams 	    'n', MDB_OPT_STR, &name,
153e0ad97e3SJonathan Adams 	    't', MDB_OPT_SETBITS, TRUE, &print_threads,
154e0ad97e3SJonathan Adams 	    'T', MDB_OPT_SETBITS, TRUE, &print_threads_all,
155e0ad97e3SJonathan Adams 	    NULL) != argc)
156e0ad97e3SJonathan Adams 		return (DCMD_USAGE);
157e0ad97e3SJonathan Adams 
158e0ad97e3SJonathan Adams 	if (!(flags & DCMD_ADDRSPEC)) {
159e0ad97e3SJonathan Adams 		size_t idx;
160e0ad97e3SJonathan Adams 		tq_info_t tqi;
161e0ad97e3SJonathan Adams 
162e0ad97e3SJonathan Adams 		bzero(&tqi, sizeof (tqi));
163e0ad97e3SJonathan Adams 
164e0ad97e3SJonathan Adams 		if (mdb_walk("taskq_cache", tq_count, &tqi) == -1) {
165e0ad97e3SJonathan Adams 			mdb_warn("unable to walk taskq_cache");
166e0ad97e3SJonathan Adams 			return (DCMD_ERR);
167e0ad97e3SJonathan Adams 		}
168*892ad162SToomas Soome 		tqi.tqi_size += 10;	/* slop */
169e0ad97e3SJonathan Adams 		tqi.tqi_array = mdb_zalloc(
170e0ad97e3SJonathan Adams 		    sizeof (*tqi.tqi_array) * tqi.tqi_size, UM_SLEEP|UM_GC);
171e0ad97e3SJonathan Adams 
172e0ad97e3SJonathan Adams 		if (mdb_walk("taskq_cache", (mdb_walk_cb_t)tq_fill,
173e0ad97e3SJonathan Adams 		    &tqi) == -1) {
174e0ad97e3SJonathan Adams 			mdb_warn("unable to walk taskq_cache");
175e0ad97e3SJonathan Adams 			return (DCMD_ERR);
176e0ad97e3SJonathan Adams 		}
177e0ad97e3SJonathan Adams 		qsort(tqi.tqi_array, tqi.tqi_count, sizeof (*tqi.tqi_array),
178e0ad97e3SJonathan Adams 		    tqcmp);
179e0ad97e3SJonathan Adams 
180e0ad97e3SJonathan Adams 		flags &= ~DCMD_PIPE;
181e0ad97e3SJonathan Adams 		flags |= DCMD_LOOP | DCMD_LOOPFIRST | DCMD_ADDRSPEC;
182e0ad97e3SJonathan Adams 		for (idx = 0; idx < tqi.tqi_count; idx++) {
183e0ad97e3SJonathan Adams 			int ret = taskq(tqi.tqi_array[idx].tq_addr, flags,
184e0ad97e3SJonathan Adams 			    argc, argv);
185e0ad97e3SJonathan Adams 			if (ret != DCMD_OK)
186e0ad97e3SJonathan Adams 				return (ret);
187e0ad97e3SJonathan Adams 			flags &= ~DCMD_LOOPFIRST;
188e0ad97e3SJonathan Adams 		}
189e0ad97e3SJonathan Adams 
190e0ad97e3SJonathan Adams 		return (DCMD_OK);
191e0ad97e3SJonathan Adams 	}
192e0ad97e3SJonathan Adams 
193e0ad97e3SJonathan Adams 	if (DCMD_HDRSPEC(flags) && !(flags & DCMD_PIPE_OUT)) {
194e0ad97e3SJonathan Adams 		mdb_printf("%<u>%-?s %-31s %4s/%4s %4s %5s %4s%</u>\n",
195e0ad97e3SJonathan Adams 		    "ADDR", "NAME", "ACT", "THDS",
196e0ad97e3SJonathan Adams 		    "Q'ED", "MAXQ", "INST");
197e0ad97e3SJonathan Adams 	}
198e0ad97e3SJonathan Adams 
199e0ad97e3SJonathan Adams 	if (mdb_vread(&tq, sizeof (tq), addr) == -1) {
200e0ad97e3SJonathan Adams 		mdb_warn("failed to read taskq_t at %p", addr);
201e0ad97e3SJonathan Adams 		return (DCMD_ERR);
202e0ad97e3SJonathan Adams 	}
203e0ad97e3SJonathan Adams 
204e0ad97e3SJonathan Adams 	/* terminate the name, just in case */
205e0ad97e3SJonathan Adams 	tq.tq_name[sizeof (tq.tq_name) - 1] = 0;
206e0ad97e3SJonathan Adams 
207e0ad97e3SJonathan Adams 	tact = tq.tq_active;
208e0ad97e3SJonathan Adams 	tcount = tq.tq_nthreads;
209e0ad97e3SJonathan Adams 	queued = tq.tq_tasks - tq.tq_executed;
210e0ad97e3SJonathan Adams 	maxq = tq.tq_maxtasks;
211e0ad97e3SJonathan Adams 
212e0ad97e3SJonathan Adams 	if (tq.tq_flags & TASKQ_DYNAMIC) {
213e0ad97e3SJonathan Adams 		size_t bsize = tq.tq_nbuckets * sizeof (*tq.tq_buckets);
214e0ad97e3SJonathan Adams 		size_t idx;
215e0ad97e3SJonathan Adams 		taskq_bucket_t *b = mdb_zalloc(bsize, UM_SLEEP | UM_GC);
216e0ad97e3SJonathan Adams 
217e0ad97e3SJonathan Adams 		if (mdb_vread(b, bsize, (uintptr_t)tq.tq_buckets) == -1) {
218e0ad97e3SJonathan Adams 			mdb_warn("unable to read buckets for taskq %p", addr);
219e0ad97e3SJonathan Adams 			return (DCMD_ERR);
220e0ad97e3SJonathan Adams 		}
221e0ad97e3SJonathan Adams 
222e0ad97e3SJonathan Adams 		tcount += (tq.tq_tcreates - tq.tq_tdeaths);
223e0ad97e3SJonathan Adams 
224e0ad97e3SJonathan Adams 		for (idx = 0; idx < tq.tq_nbuckets; idx++) {
225e0ad97e3SJonathan Adams 			tact += b[idx].tqbucket_nalloc;
226e0ad97e3SJonathan Adams 		}
227e0ad97e3SJonathan Adams 	}
228e0ad97e3SJonathan Adams 
229e0ad97e3SJonathan Adams 	/* filter out taskqs that aren't of interest. */
230e0ad97e3SJonathan Adams 	if (name != NULL && strstr(tq.tq_name, name) == NULL)
231e0ad97e3SJonathan Adams 		return (DCMD_OK);
232e0ad97e3SJonathan Adams 	if (active && tact == 0 && queued == 0)
233e0ad97e3SJonathan Adams 		return (DCMD_OK);
234e0ad97e3SJonathan Adams 	if (!(tq.tq_flags & TASKQ_DYNAMIC) && maxq < minmaxq)
235e0ad97e3SJonathan Adams 		return (DCMD_OK);
236e0ad97e3SJonathan Adams 
237e0ad97e3SJonathan Adams 	if (flags & DCMD_PIPE_OUT) {
238e0ad97e3SJonathan Adams 		mdb_printf("%#lr\n", addr);
239e0ad97e3SJonathan Adams 		return (DCMD_OK);
240e0ad97e3SJonathan Adams 	}
241e0ad97e3SJonathan Adams 
242e0ad97e3SJonathan Adams 	mdb_printf("%?p %-31s %4d/%4d %4d ",
243e0ad97e3SJonathan Adams 	    addr, tq.tq_name, tact, tcount, queued);
244e0ad97e3SJonathan Adams 
245e0ad97e3SJonathan Adams 	if (tq.tq_flags & TASKQ_DYNAMIC)
246e0ad97e3SJonathan Adams 		mdb_printf("%5s ", "-");
247e0ad97e3SJonathan Adams 	else
248e0ad97e3SJonathan Adams 		mdb_printf("%5d ", maxq);
249e0ad97e3SJonathan Adams 
250e0ad97e3SJonathan Adams 	if (tq.tq_flags & TASKQ_NOINSTANCE)
251e0ad97e3SJonathan Adams 		mdb_printf("%4s", "-");
252e0ad97e3SJonathan Adams 	else
253e0ad97e3SJonathan Adams 		mdb_printf("%4x", tq.tq_instance);
254e0ad97e3SJonathan Adams 
255e0ad97e3SJonathan Adams 	mdb_printf("\n");
256e0ad97e3SJonathan Adams 
257e0ad97e3SJonathan Adams 	if (print_threads || print_threads_all) {
258e0ad97e3SJonathan Adams 		int ret;
259e0ad97e3SJonathan Adams 		char strbuf[128];
260e0ad97e3SJonathan Adams 		const char *arg =
261e0ad97e3SJonathan Adams 		    print_threads_all ? "" : "-C \"taskq_thread_wait\"";
262e0ad97e3SJonathan Adams 
263e0ad97e3SJonathan Adams 		/*
264e0ad97e3SJonathan Adams 		 * We can't use mdb_pwalk_dcmd() here, because ::stacks needs
265e0ad97e3SJonathan Adams 		 * to get the full pipeline.
266e0ad97e3SJonathan Adams 		 */
267e0ad97e3SJonathan Adams 		mdb_snprintf(strbuf, sizeof (strbuf),
268e0ad97e3SJonathan Adams 		    "%p::walk taskq_thread | ::stacks -a %s",
269e0ad97e3SJonathan Adams 		    addr, arg);
270e0ad97e3SJonathan Adams 
271e0ad97e3SJonathan Adams 		(void) mdb_inc_indent(4);
272e0ad97e3SJonathan Adams 		ret = mdb_eval(strbuf);
273e0ad97e3SJonathan Adams 		(void) mdb_dec_indent(4);
274e0ad97e3SJonathan Adams 
275e0ad97e3SJonathan Adams 		/* abort, since they could have control-Ced the eval */
276e0ad97e3SJonathan Adams 		if (ret == -1)
277e0ad97e3SJonathan Adams 			return (DCMD_ABORT);
278e0ad97e3SJonathan Adams 	}
279e0ad97e3SJonathan Adams 
280e0ad97e3SJonathan Adams 	return (DCMD_OK);
281e0ad97e3SJonathan Adams }
282e0ad97e3SJonathan Adams 
283e0ad97e3SJonathan Adams /*
284e0ad97e3SJonathan Adams  * Dump a taskq_ent_t given its address.
285e0ad97e3SJonathan Adams  */
286e0ad97e3SJonathan Adams /*ARGSUSED*/
287e0ad97e3SJonathan Adams int
taskq_ent(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)288e0ad97e3SJonathan Adams taskq_ent(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
289e0ad97e3SJonathan Adams {
290e0ad97e3SJonathan Adams 	taskq_ent_t	taskq_ent;
291e0ad97e3SJonathan Adams 
292e0ad97e3SJonathan Adams 	if (!(flags & DCMD_ADDRSPEC)) {
293e0ad97e3SJonathan Adams 		return (DCMD_USAGE);
294e0ad97e3SJonathan Adams 	}
295e0ad97e3SJonathan Adams 
296e0ad97e3SJonathan Adams 	if (mdb_vread(&taskq_ent, sizeof (taskq_ent_t), addr) == -1) {
297e0ad97e3SJonathan Adams 		mdb_warn("failed to read taskq_ent_t at %p", addr);
298e0ad97e3SJonathan Adams 		return (DCMD_ERR);
299e0ad97e3SJonathan Adams 	}
300e0ad97e3SJonathan Adams 
301e0ad97e3SJonathan Adams 	if (DCMD_HDRSPEC(flags)) {
302e0ad97e3SJonathan Adams 		mdb_printf("%<u>%-?s %-?s %-s%</u>\n",
303e0ad97e3SJonathan Adams 		"ENTRY", "ARG", "FUNCTION");
304e0ad97e3SJonathan Adams 	}
305e0ad97e3SJonathan Adams 
306e0ad97e3SJonathan Adams 	mdb_printf("%-?p %-?p %a\n", addr, taskq_ent.tqent_arg,
307e0ad97e3SJonathan Adams 	    taskq_ent.tqent_func);
308e0ad97e3SJonathan Adams 
309e0ad97e3SJonathan Adams 	return (DCMD_OK);
310e0ad97e3SJonathan Adams }
311e0ad97e3SJonathan Adams 
312e0ad97e3SJonathan Adams 
313e0ad97e3SJonathan Adams /*
314e0ad97e3SJonathan Adams  * Given the address of the (taskq_t) task queue head, walk the queue listing
315e0ad97e3SJonathan Adams  * the address of every taskq_ent_t.
316e0ad97e3SJonathan Adams  */
317e0ad97e3SJonathan Adams int
taskq_ent_walk_init(mdb_walk_state_t * wsp)318e0ad97e3SJonathan Adams taskq_ent_walk_init(mdb_walk_state_t *wsp)
319e0ad97e3SJonathan Adams {
320e0ad97e3SJonathan Adams 	taskq_t	tq_head;
321e0ad97e3SJonathan Adams 
322e0ad97e3SJonathan Adams 
323*892ad162SToomas Soome 	if (wsp->walk_addr == 0) {
324e0ad97e3SJonathan Adams 		mdb_warn("start address required\n");
325e0ad97e3SJonathan Adams 		return (WALK_ERR);
326e0ad97e3SJonathan Adams 	}
327e0ad97e3SJonathan Adams 
328e0ad97e3SJonathan Adams 
329e0ad97e3SJonathan Adams 	/*
330e0ad97e3SJonathan Adams 	 * Save the address of the list head entry.  This terminates the list.
331e0ad97e3SJonathan Adams 	 */
332e0ad97e3SJonathan Adams 	wsp->walk_data = (void *)
333e0ad97e3SJonathan Adams 	    ((size_t)wsp->walk_addr + OFFSETOF(taskq_t, tq_task));
334e0ad97e3SJonathan Adams 
335e0ad97e3SJonathan Adams 
336e0ad97e3SJonathan Adams 	/*
337e0ad97e3SJonathan Adams 	 * Read in taskq head, set walk_addr to point to first taskq_ent_t.
338e0ad97e3SJonathan Adams 	 */
339e0ad97e3SJonathan Adams 	if (mdb_vread((void *)&tq_head, sizeof (taskq_t), wsp->walk_addr) ==
340e0ad97e3SJonathan Adams 	    -1) {
341e0ad97e3SJonathan Adams 		mdb_warn("failed to read taskq list head at %p",
342e0ad97e3SJonathan Adams 		    wsp->walk_addr);
343e0ad97e3SJonathan Adams 	}
344e0ad97e3SJonathan Adams 	wsp->walk_addr = (uintptr_t)tq_head.tq_task.tqent_next;
345e0ad97e3SJonathan Adams 
346e0ad97e3SJonathan Adams 
347e0ad97e3SJonathan Adams 	/*
348e0ad97e3SJonathan Adams 	 * Check for null list (next=head)
349e0ad97e3SJonathan Adams 	 */
350e0ad97e3SJonathan Adams 	if (wsp->walk_addr == (uintptr_t)wsp->walk_data) {
351e0ad97e3SJonathan Adams 		return (WALK_DONE);
352e0ad97e3SJonathan Adams 	}
353e0ad97e3SJonathan Adams 
354e0ad97e3SJonathan Adams 	return (WALK_NEXT);
355e0ad97e3SJonathan Adams }
356e0ad97e3SJonathan Adams 
357e0ad97e3SJonathan Adams 
358e0ad97e3SJonathan Adams int
taskq_ent_walk_step(mdb_walk_state_t * wsp)359e0ad97e3SJonathan Adams taskq_ent_walk_step(mdb_walk_state_t *wsp)
360e0ad97e3SJonathan Adams {
361e0ad97e3SJonathan Adams 	taskq_ent_t	tq_ent;
362e0ad97e3SJonathan Adams 	int		status;
363e0ad97e3SJonathan Adams 
364e0ad97e3SJonathan Adams 
365e0ad97e3SJonathan Adams 	if (mdb_vread((void *)&tq_ent, sizeof (taskq_ent_t), wsp->walk_addr) ==
366e0ad97e3SJonathan Adams 	    -1) {
367e0ad97e3SJonathan Adams 		mdb_warn("failed to read taskq_ent_t at %p", wsp->walk_addr);
368e0ad97e3SJonathan Adams 		return (DCMD_ERR);
369e0ad97e3SJonathan Adams 	}
370e0ad97e3SJonathan Adams 
371e0ad97e3SJonathan Adams 	status = wsp->walk_callback(wsp->walk_addr, (void *)&tq_ent,
372e0ad97e3SJonathan Adams 	    wsp->walk_cbdata);
373e0ad97e3SJonathan Adams 
374e0ad97e3SJonathan Adams 	wsp->walk_addr = (uintptr_t)tq_ent.tqent_next;
375e0ad97e3SJonathan Adams 
376e0ad97e3SJonathan Adams 
377e0ad97e3SJonathan Adams 	/* Check if we're at the last element (next=head) */
378e0ad97e3SJonathan Adams 	if (wsp->walk_addr == (uintptr_t)wsp->walk_data) {
379e0ad97e3SJonathan Adams 		return (WALK_DONE);
380e0ad97e3SJonathan Adams 	}
381e0ad97e3SJonathan Adams 
382e0ad97e3SJonathan Adams 	return (status);
383e0ad97e3SJonathan Adams }
384e0ad97e3SJonathan Adams 
385e0ad97e3SJonathan Adams typedef struct taskq_thread_info {
386e0ad97e3SJonathan Adams 	uintptr_t	tti_addr;
387e0ad97e3SJonathan Adams 	uintptr_t	*tti_tlist;
388e0ad97e3SJonathan Adams 	size_t		tti_nthreads;
389e0ad97e3SJonathan Adams 	size_t		tti_idx;
390e0ad97e3SJonathan Adams 
391e0ad97e3SJonathan Adams 	kthread_t	tti_thread;
392e0ad97e3SJonathan Adams } taskq_thread_info_t;
393e0ad97e3SJonathan Adams 
394e0ad97e3SJonathan Adams int
taskq_thread_walk_init(mdb_walk_state_t * wsp)395e0ad97e3SJonathan Adams taskq_thread_walk_init(mdb_walk_state_t *wsp)
396e0ad97e3SJonathan Adams {
397e0ad97e3SJonathan Adams 	taskq_thread_info_t	*tti;
398e0ad97e3SJonathan Adams 	taskq_t			tq;
399e0ad97e3SJonathan Adams 	uintptr_t		*tlist;
400e0ad97e3SJonathan Adams 	size_t			nthreads;
401e0ad97e3SJonathan Adams 
402e0ad97e3SJonathan Adams 	tti = wsp->walk_data = mdb_zalloc(sizeof (*tti), UM_SLEEP);
403e0ad97e3SJonathan Adams 	tti->tti_addr = wsp->walk_addr;
404e0ad97e3SJonathan Adams 
405*892ad162SToomas Soome 	if (wsp->walk_addr != 0 &&
406e0ad97e3SJonathan Adams 	    mdb_vread(&tq, sizeof (tq), wsp->walk_addr) != -1 &&
407e0ad97e3SJonathan Adams 	    !(tq.tq_flags & TASKQ_DYNAMIC)) {
408e0ad97e3SJonathan Adams 
409e0ad97e3SJonathan Adams 		nthreads = tq.tq_nthreads;
410e0ad97e3SJonathan Adams 		tlist = mdb_alloc(nthreads * sizeof (*tlist), UM_SLEEP);
411e0ad97e3SJonathan Adams 		if (tq.tq_nthreads_max == 1) {
412e0ad97e3SJonathan Adams 			tlist[0] = (uintptr_t)tq.tq_thread;
413e0ad97e3SJonathan Adams 
414e0ad97e3SJonathan Adams 		} else if (mdb_vread(tlist, nthreads * sizeof (*tlist),
415e0ad97e3SJonathan Adams 		    (uintptr_t)tq.tq_threadlist) == -1) {
416e0ad97e3SJonathan Adams 			mdb_warn("unable to read threadlist for taskq_t %p",
417e0ad97e3SJonathan Adams 			    wsp->walk_addr);
418e0ad97e3SJonathan Adams 			mdb_free(tlist, nthreads * sizeof (*tlist));
419e0ad97e3SJonathan Adams 			return (WALK_ERR);
420e0ad97e3SJonathan Adams 		}
421e0ad97e3SJonathan Adams 
422e0ad97e3SJonathan Adams 		tti->tti_tlist = tlist;
423e0ad97e3SJonathan Adams 		tti->tti_nthreads = nthreads;
424e0ad97e3SJonathan Adams 		return (WALK_NEXT);
425e0ad97e3SJonathan Adams 	}
426e0ad97e3SJonathan Adams 
427e0ad97e3SJonathan Adams 	wsp->walk_addr = 0;
428e0ad97e3SJonathan Adams 	if (mdb_layered_walk("thread", wsp) == -1) {
429e0ad97e3SJonathan Adams 		mdb_warn("can't walk \"thread\"");
430e0ad97e3SJonathan Adams 		return (WALK_ERR);
431e0ad97e3SJonathan Adams 	}
432e0ad97e3SJonathan Adams 	return (0);
433e0ad97e3SJonathan Adams }
434e0ad97e3SJonathan Adams 
435e0ad97e3SJonathan Adams int
taskq_thread_walk_step(mdb_walk_state_t * wsp)436e0ad97e3SJonathan Adams taskq_thread_walk_step(mdb_walk_state_t *wsp)
437e0ad97e3SJonathan Adams {
438e0ad97e3SJonathan Adams 	taskq_thread_info_t	*tti = wsp->walk_data;
439e0ad97e3SJonathan Adams 
440e0ad97e3SJonathan Adams 	const kthread_t *kt = wsp->walk_layer;
441e0ad97e3SJonathan Adams 	taskq_t *tq = (taskq_t *)tti->tti_addr;
442e0ad97e3SJonathan Adams 
443e0ad97e3SJonathan Adams 	if (kt == NULL) {
444e0ad97e3SJonathan Adams 		uintptr_t addr;
445e0ad97e3SJonathan Adams 
446e0ad97e3SJonathan Adams 		if (tti->tti_idx >= tti->tti_nthreads)
447e0ad97e3SJonathan Adams 			return (WALK_DONE);
448e0ad97e3SJonathan Adams 
449e0ad97e3SJonathan Adams 		addr = tti->tti_tlist[tti->tti_idx];
450e0ad97e3SJonathan Adams 		tti->tti_idx++;
451e0ad97e3SJonathan Adams 
452*892ad162SToomas Soome 		if (addr == 0)
453e0ad97e3SJonathan Adams 			return (WALK_NEXT);
454e0ad97e3SJonathan Adams 
455e0ad97e3SJonathan Adams 		if (mdb_vread(&tti->tti_thread, sizeof (kthread_t),
456e0ad97e3SJonathan Adams 		    addr) == -1) {
457e0ad97e3SJonathan Adams 			mdb_warn("unable to read kthread_t at %p", addr);
458e0ad97e3SJonathan Adams 			return (WALK_ERR);
459e0ad97e3SJonathan Adams 		}
460e0ad97e3SJonathan Adams 		return (wsp->walk_callback(addr, &tti->tti_thread,
461e0ad97e3SJonathan Adams 		    wsp->walk_cbdata));
462e0ad97e3SJonathan Adams 	}
463e0ad97e3SJonathan Adams 
464e0ad97e3SJonathan Adams 	if (kt->t_taskq == NULL)
465e0ad97e3SJonathan Adams 		return (WALK_NEXT);
466e0ad97e3SJonathan Adams 
467e0ad97e3SJonathan Adams 	if (tq != NULL && kt->t_taskq != tq)
468e0ad97e3SJonathan Adams 		return (WALK_NEXT);
469e0ad97e3SJonathan Adams 
470e0ad97e3SJonathan Adams 	return (wsp->walk_callback(wsp->walk_addr, kt, wsp->walk_cbdata));
471e0ad97e3SJonathan Adams }
472e0ad97e3SJonathan Adams 
473e0ad97e3SJonathan Adams void
taskq_thread_walk_fini(mdb_walk_state_t * wsp)474e0ad97e3SJonathan Adams taskq_thread_walk_fini(mdb_walk_state_t *wsp)
475e0ad97e3SJonathan Adams {
476e0ad97e3SJonathan Adams 	taskq_thread_info_t	*tti = wsp->walk_data;
477e0ad97e3SJonathan Adams 
478e0ad97e3SJonathan Adams 	if (tti->tti_nthreads > 0) {
479e0ad97e3SJonathan Adams 		mdb_free(tti->tti_tlist,
480e0ad97e3SJonathan Adams 		    tti->tti_nthreads * sizeof (*tti->tti_tlist));
481e0ad97e3SJonathan Adams 	}
482e0ad97e3SJonathan Adams 	mdb_free(tti, sizeof (*tti));
483e0ad97e3SJonathan Adams }
484