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