changeset 10889:8b6ec68049bd

6888456 zfs command line utilities should have CTF data 6887356 ::stacks is slow in pipelines on live kernels 6884079 mdb's enum p2 printing should elide common prefixes 6887160 ::offsetof and ::sizeof still can't handle forward decls 6551113 ::taskq would be nice
author Jonathan Adams <Jonathan.Adams@Sun.COM>
date Tue, 27 Oct 2009 13:44:45 -0700
parents a32f4a9013e5
children 499786962772
files usr/src/cmd/Makefile.ctf usr/src/cmd/cmd-inet/usr.lib/inetd/Makefile usr/src/cmd/cmd-inet/usr.lib/inetd/req.flg usr/src/cmd/mdb/common/mdb/mdb_cmds.c usr/src/cmd/mdb/common/mdb/mdb_ctf.c usr/src/cmd/mdb/common/mdb/mdb_print.c usr/src/cmd/mdb/common/mdb/mdb_print.h usr/src/cmd/mdb/common/modules/genunix/Makefile.files usr/src/cmd/mdb/common/modules/genunix/findstack.c usr/src/cmd/mdb/common/modules/genunix/genunix.c usr/src/cmd/mdb/common/modules/genunix/taskq.c usr/src/cmd/mdb/common/modules/genunix/taskq.h usr/src/cmd/svc/Makefile.ctf usr/src/cmd/svc/configd/Makefile usr/src/cmd/svc/prophist/Makefile usr/src/cmd/svc/req.flg usr/src/cmd/svc/servinfo/Makefile usr/src/cmd/svc/startd/Makefile usr/src/cmd/svc/svccfg/Makefile usr/src/cmd/svc/svcprop/Makefile usr/src/cmd/svc/svcs/Makefile usr/src/cmd/zdb/Makefile.com usr/src/cmd/zfs/Makefile usr/src/cmd/zpool/Makefile usr/src/cmd/ztest/Makefile.com usr/src/uts/common/os/taskq.c
diffstat 26 files changed, 939 insertions(+), 322 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/Makefile.ctf	Tue Oct 27 13:44:45 2009 -0700
@@ -0,0 +1,31 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+POST_PROCESS += ; $(CTFMERGE) -L VERSION -o $@ $(OBJS)
+POST_PROCESS_O += ; $(CTFCONVERT_O)
+
+CFLAGS += $(CTF_FLAGS)
+CFLAGS64 += $(CTF_FLAGS)
+NATIVE_CFLAGS += $(CTF_FLAGS)
--- a/usr/src/cmd/cmd-inet/usr.lib/inetd/Makefile	Tue Oct 27 11:26:10 2009 -0700
+++ b/usr/src/cmd/cmd-inet/usr.lib/inetd/Makefile	Tue Oct 27 13:44:45 2009 -0700
@@ -19,13 +19,9 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-# ident	"%Z%%M%	%I%	%E% SMI"
-#
-# cmd/cmd-inet/usr.lib/inetd/%M%
-#
 
 PROG	=	inetd
 MANIFEST=	inetd.xml inetd-upgrade.xml
@@ -36,7 +32,7 @@
 
 include ../../../Makefile.cmd
 include ../../Makefile.cmd-inet
-include ../../../svc/Makefile.ctf
+include ../../../Makefile.ctf
 
 ROOTMANIFESTDIR=	$(ROOTSVCNETWORK)
 
@@ -58,7 +54,7 @@
 all:		$(PROG) $(SVCMETHOD)
 
 $(PROG): 	$(OBJS)
-		$(LINK.c) $(OBJS) -o $@ $(LDLIBS) $(CTFMERGE_HOOK)
+		$(LINK.c) $(OBJS) -o $@ $(LDLIBS)
 		$(POST_PROCESS)
 
 include ../Makefile.lib
--- a/usr/src/cmd/cmd-inet/usr.lib/inetd/req.flg	Tue Oct 27 11:26:10 2009 -0700
+++ b/usr/src/cmd/cmd-inet/usr.lib/inetd/req.flg	Tue Oct 27 13:44:45 2009 -0700
@@ -3,9 +3,8 @@
 # CDDL HEADER START
 #
 # The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License").  You may not use this file except in compliance
-# with the License.
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
 #
 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 # or http://www.opensolaris.org/os/licensing.
@@ -21,9 +20,8 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-#ident	"%Z%%M%	%I%	%E% SMI"
 
-echo_file usr/src/cmd/svc/Makefile.ctf
+echo_file usr/src/cmd/Makefile.ctf
--- a/usr/src/cmd/mdb/common/mdb/mdb_cmds.c	Tue Oct 27 11:26:10 2009 -0700
+++ b/usr/src/cmd/mdb/common/mdb/mdb_cmds.c	Tue Oct 27 13:44:45 2009 -0700
@@ -2906,7 +2906,8 @@
 	{ "dump", "?[-eqrstu] [-f|-p] [-g bytes] [-w paragraphs]",
 	    "dump memory from specified address", cmd_dump, dump_help },
 	{ "echo", "args ...", "echo arguments", cmd_echo },
-	{ "enum", "?[-x] enum [name]", "print an enumeration", cmd_enum },
+	{ "enum", "?[-ex] enum [name]", "print an enumeration", cmd_enum,
+	    enum_help },
 	{ "eval", "command", "evaluate the specified command", cmd_eval },
 	{ "events", "[-av]", "list traced software events",
 	    cmd_events, events_help },
--- a/usr/src/cmd/mdb/common/mdb/mdb_ctf.c	Tue Oct 27 11:26:10 2009 -0700
+++ b/usr/src/cmd/mdb/common/mdb/mdb_ctf.c	Tue Oct 27 13:44:45 2009 -0700
@@ -446,6 +446,10 @@
 	mdb_ctf_impl_t *idp = (mdb_ctf_impl_t *)&id;
 	ssize_t ret;
 
+	/* resolve the type in case there's a forward declaration */
+	if ((ret = mdb_ctf_type_resolve(id, &id)) != 0)
+		return (ret);
+
 	if ((ret = ctf_type_size(idp->mci_fp, idp->mci_id)) == CTF_ERR)
 		return (set_errno(ctf_to_errno(ctf_errno(idp->mci_fp))));
 
@@ -586,6 +590,10 @@
 	mdb_ctf_impl_t *idp = (mdb_ctf_impl_t *)&id;
 	const char *ret;
 
+	/* resolve the type in case there's a forward declaration */
+	if (mdb_ctf_type_resolve(id, &id) != 0)
+		return (NULL);
+
 	if ((ret = ctf_enum_name(idp->mci_fp, idp->mci_id, value)) == NULL)
 		(void) set_errno(ctf_to_errno(ctf_errno(idp->mci_fp)));
 
@@ -613,6 +621,10 @@
 	member_iter_t mi;
 	int ret;
 
+	/* resolve the type in case there's a forward declaration */
+	if ((ret = mdb_ctf_type_resolve(id, &id)) != 0)
+		return (ret);
+
 	mi.mi_cb = cb;
 	mi.mi_arg = data;
 	mi.mi_fp = idp->mci_fp;
@@ -629,6 +641,11 @@
 mdb_ctf_enum_iter(mdb_ctf_id_t id, mdb_ctf_enum_f *cb, void *data)
 {
 	mdb_ctf_impl_t *idp = (mdb_ctf_impl_t *)&id;
+	int ret;
+
+	/* resolve the type in case there's a forward declaration */
+	if ((ret = mdb_ctf_type_resolve(id, &id)) != 0)
+		return (ret);
 
 	return (ctf_enum_iter(idp->mci_fp, idp->mci_id, cb, data));
 }
--- a/usr/src/cmd/mdb/common/mdb/mdb_print.c	Tue Oct 27 11:26:10 2009 -0700
+++ b/usr/src/cmd/mdb/common/mdb/mdb_print.c	Tue Oct 27 13:44:45 2009 -0700
@@ -217,6 +217,7 @@
 	mdb_ctf_id_t id;
 	ulong_t off;
 	char tn[MDB_SYM_NAMLEN];
+	ssize_t sz;
 	int ret;
 
 	if (flags & DCMD_ADDRSPEC)
@@ -235,35 +236,82 @@
 
 	member = argv[1].a_un.a_str;
 
-	if (mdb_ctf_offsetof(id, member, &off) != 0) {
+	if (mdb_ctf_member_info(id, member, &off, &id) != 0) {
 		mdb_warn("failed to find member %s of type %s", member, tn);
 		return (DCMD_ERR);
 	}
 
-	if (off % NBBY == 0)
-		mdb_printf("offsetof (%s, %s) = %#lr\n",
-		    tn, member, off / NBBY);
-	else
-		mdb_printf("offsetof (%s, %s) = %#lr bits\n",
-		    tn, member, off);
+	if (flags & DCMD_PIPE_OUT) {
+		if (off % NBBY != 0) {
+			mdb_warn("member %s of type %s is not byte-aligned\n",
+			    member, tn);
+			return (DCMD_ERR);
+		}
+		mdb_printf("%#lr", off / NBBY);
+		return (DCMD_OK);
+	}
+
+	mdb_printf("offsetof (%s, %s) = %#lr",
+	    tn, member, off / NBBY);
+	if (off % NBBY != 0)
+		mdb_printf(".%lr", off % NBBY);
+
+	if ((sz = mdb_ctf_type_size(id)) > 0)
+		mdb_printf(", sizeof (...->%s) = %#lr", member, sz);
+
+	mdb_printf("\n");
 
 	return (DCMD_OK);
 }
 
+/*ARGSUSED*/
+static int
+enum_prefix_scan_cb(const char *name, int value, void *arg)
+{
+	char *str = arg;
+
+	/*
+	 * This function is called with every name in the enum.  We make
+	 * "arg" be the common prefix, if any.
+	 */
+	if (str[0] == 0) {
+		if (strlcpy(arg, name, MDB_SYM_NAMLEN) >= MDB_SYM_NAMLEN)
+			return (1);
+		return (0);
+	}
+
+	while (*name == *str) {
+		if (*str == 0) {
+			if (str != arg) {
+				str--;	/* don't smother a name completely */
+			}
+			break;
+		}
+		name++;
+		str++;
+	}
+	*str = 0;
+
+	return (str == arg);	/* only continue if prefix is non-empty */
+}
+
 struct enum_p2_info {
-	int	e_value;
-	char	*e_buf;
-	size_t	e_size;
-	uint_t	e_bits;
-	uint8_t	e_found;
-	uint8_t	e_zero;
+	intmax_t e_value;	/* value we're processing */
+	char	*e_buf;		/* buffer for holding names */
+	size_t	e_size;		/* size of buffer */
+	size_t	e_prefix;	/* length of initial prefix */
+	uint_t	e_allprefix;	/* apply prefix to first guy, too */
+	uint_t	e_bits;		/* bits seen */
+	uint8_t	e_found;	/* have we seen anything? */
+	uint8_t	e_first;	/* does buf contain the first one? */
+	uint8_t	e_zero;		/* have we seen a zero value? */
 };
 
 static int
 enum_p2_cb(const char *name, int bit_arg, void *arg)
 {
 	struct enum_p2_info *eiip = arg;
-	uint_t bit = bit_arg;
+	uintmax_t bit = bit_arg;
 
 	if (bit != 0 && !ISP2(bit))
 		return (1);	/* non-power-of-2; abort processing */
@@ -279,47 +327,84 @@
 		eiip->e_bits |= bit;
 
 	if (eiip->e_buf != NULL && (eiip->e_value & bit) != 0) {
-		if (eiip->e_found)
-			(void) strlcat(eiip->e_buf, "|", eiip->e_size);
+		char *buf = eiip->e_buf;
+		size_t prefix = eiip->e_prefix;
+
+		if (eiip->e_found) {
+			(void) strlcat(buf, "|", eiip->e_size);
 
-		if (strlcat(eiip->e_buf, name, eiip->e_size) >=
-		    eiip->e_size)
-			return (1);	/* overflowed */
+			if (eiip->e_first && !eiip->e_allprefix && prefix > 0) {
+				char c1 = buf[prefix];
+				char c2 = buf[prefix + 1];
+				buf[prefix] = '{';
+				buf[prefix + 1] = 0;
+				mdb_printf("%s", buf);
+				buf[prefix] = c1;
+				buf[prefix + 1] = c2;
+				mdb_printf("%s", buf + prefix);
+			} else {
+				mdb_printf("%s", buf);
+			}
 
+		}
+		/* skip the common prefix as necessary */
+		if ((eiip->e_found || eiip->e_allprefix) &&
+		    strlen(name) > prefix)
+			name += prefix;
+
+		(void) strlcpy(eiip->e_buf, name, eiip->e_size);
+		eiip->e_first = !eiip->e_found;
 		eiip->e_found = 1;
 	}
 	return (0);
 }
 
 static int
-enum_value_to_name_p2(mdb_ctf_id_t id, int v, char *buf, size_t size)
+enum_is_p2(mdb_ctf_id_t id)
 {
 	struct enum_p2_info eii;
+	bzero(&eii, sizeof (eii));
+
+	return (mdb_ctf_type_kind(id) == CTF_K_ENUM &&
+	    mdb_ctf_enum_iter(id, enum_p2_cb, &eii) == 0 &&
+	    eii.e_bits != 0);
+}
+
+static int
+enum_value_print_p2(mdb_ctf_id_t id, intmax_t value, uint_t allprefix)
+{
+	struct enum_p2_info eii;
+	char prefix[MDB_SYM_NAMLEN + 2];
+	intmax_t missed;
 
 	bzero(&eii, sizeof (eii));
 
-	eii.e_value = v;
-	eii.e_buf = buf;
-	eii.e_size = size;
+	eii.e_value = value;
+	eii.e_buf = prefix;
+	eii.e_size = sizeof (prefix);
+	eii.e_allprefix = allprefix;
 
-	if (buf != NULL && size > 0)
-		buf[0] = '\0';
+	prefix[0] = 0;
+	if (mdb_ctf_enum_iter(id, enum_prefix_scan_cb, prefix) == 0)
+		eii.e_prefix = strlen(prefix);
 
-	if (mdb_ctf_type_kind(id) != CTF_K_ENUM ||
-	    mdb_ctf_enum_iter(id, enum_p2_cb, &eii) != 0 ||
-	    eii.e_bits == 0)
+	if (mdb_ctf_enum_iter(id, enum_p2_cb, &eii) != 0 || eii.e_bits == 0)
 		return (-1);
 
-	if (buf != NULL && (!eii.e_found || (v & ~eii.e_bits) != 0)) {
-		char val[16];
+	missed = (value & ~(intmax_t)eii.e_bits);
 
-		(void) mdb_snprintf(val, sizeof (val),
-		    "0x%x", (v & ~eii.e_bits));
+	if (eii.e_found) {
+		/* push out any final value, with a | if we missed anything */
+		if (!eii.e_first)
+			(void) strlcat(prefix, "}", sizeof (prefix));
+		if (missed != 0)
+			(void) strlcat(prefix, "|", sizeof (prefix));
 
-		if (eii.e_found)
-			(void) strlcat(buf, "|", size);
-		if (strlcat(buf, val, size) >= size)
-			return (-1);
+		mdb_printf("%s", prefix);
+	}
+
+	if (!eii.e_found || missed) {
+		mdb_printf("%#llx", missed);
 	}
 
 	return (0);
@@ -328,24 +413,39 @@
 struct enum_cbinfo {
 	uint_t		e_flags;
 	const char	*e_string;	/* NULL for value searches */
-	int		e_value;
+	size_t		e_prefix;
+	intmax_t	e_value;
 	uint_t		e_found;
+	mdb_ctf_id_t	e_id;
 };
-#define	E_PRETTY		0x1
-#define	E_HEX			0x2
-#define	E_SEARCH_STRING		0x4
-#define	E_SEARCH_VALUE		0x8
+#define	E_PRETTY		0x01
+#define	E_HEX			0x02
+#define	E_SEARCH_STRING		0x04
+#define	E_SEARCH_VALUE		0x08
+#define	E_ELIDE_PREFIX		0x10
 
 static void
 enum_print(struct enum_cbinfo *info, const char *name, int value)
 {
 	uint_t flags = info->e_flags;
+	uint_t elide_prefix = (info->e_flags & E_ELIDE_PREFIX);
+
+	if (name != NULL && info->e_prefix && strlen(name) > info->e_prefix)
+		name += info->e_prefix;
 
 	if (flags & E_PRETTY) {
-		if (flags & E_HEX)
-			mdb_printf("%-8x %s\n", value, name);
-		else
-			mdb_printf("%-11d %s\n", value, name);
+		uint_t indent = 5 + ((flags & E_HEX) ? 8 : 11);
+
+		mdb_printf((flags & E_HEX)? "%8x " : "%11d ", value);
+		(void) mdb_inc_indent(indent);
+		if (name != NULL) {
+			mdb_iob_puts(mdb.m_out, name);
+		} else {
+			(void) enum_value_print_p2(info->e_id, value,
+			    elide_prefix);
+		}
+		(void) mdb_dec_indent(indent);
+		mdb_printf("\n");
 	} else {
 		mdb_printf("%#r\n", value);
 	}
@@ -372,6 +472,23 @@
 	return (0);
 }
 
+void
+enum_help(void)
+{
+	mdb_printf("%s",
+"Without an address and name, print all values for the enumeration \"enum\".\n"
+"With an address, look up a particular value in \"enum\".  With a name, look\n"
+"up a particular name in \"enum\".\n");
+
+	(void) mdb_dec_indent(2);
+	mdb_printf("\n%<b>OPTIONS%</b>\n");
+	(void) mdb_inc_indent(2);
+
+	mdb_printf("%s",
+"   -e    remove common prefixes from enum names\n"
+"   -x    report enum values in hexadecimal\n");
+}
+
 /*ARGSUSED*/
 int
 cmd_enum(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
@@ -380,11 +497,13 @@
 
 	char type[MDB_SYM_NAMLEN + sizeof ("enum ")];
 	char tn2[MDB_SYM_NAMLEN + sizeof ("enum ")];
+	char prefix[MDB_SYM_NAMLEN];
 	mdb_ctf_id_t id;
 	mdb_ctf_id_t idr;
 
 	int i;
 	intmax_t search;
+	uint_t isp2;
 
 	info.e_flags = (flags & DCMD_PIPE_OUT)? 0 : E_PRETTY;
 	info.e_string = NULL;
@@ -392,6 +511,7 @@
 	info.e_found = 0;
 
 	i = mdb_getopts(argc, argv,
+	    'e', MDB_OPT_SETBITS, E_ELIDE_PREFIX, &info.e_flags,
 	    'x', MDB_OPT_SETBITS, E_HEX, &info.e_flags,
 	    NULL);
 
@@ -433,6 +553,8 @@
 		return (DCMD_ERR);
 	}
 
+	info.e_id = idr;
+
 	if (argc > 2)
 		return (DCMD_USAGE);
 
@@ -462,25 +584,32 @@
 		if ((int)search != search) {
 			mdb_warn("value '%lld' out of enumeration range\n",
 			    search);
-			return (DCMD_ERR);
 		}
 		info.e_value = search;
 	}
 
+	isp2 = enum_is_p2(idr);
+	if (isp2)
+		info.e_flags |= E_HEX;
+
 	if (DCMD_HDRSPEC(flags) && (info.e_flags & E_PRETTY)) {
 		if (info.e_flags & E_HEX)
-			mdb_printf("%<b>%-8s %s%</b>\n", "VALUE", "NAME");
+			mdb_printf("%<u>%8s %-64s%</u>\n", "VALUE", "NAME");
 		else
-			mdb_printf("%<b>%-11s %s%</b>\n", "VALUE", "NAME");
+			mdb_printf("%<u>%11s %-64s%</u>\n", "VALUE", "NAME");
 	}
 
 	/* if the enum is a power-of-two one, process it that way */
-	if ((info.e_flags & E_SEARCH_VALUE) &&
-	    enum_value_to_name_p2(idr, info.e_value, tn2, sizeof (tn2)) == 0) {
-		enum_print(&info, tn2, info.e_value);
+	if ((info.e_flags & E_SEARCH_VALUE) && isp2) {
+		enum_print(&info, NULL, info.e_value);
 		return (DCMD_OK);
 	}
 
+	prefix[0] = 0;
+	if ((info.e_flags & E_ELIDE_PREFIX) &&
+	    mdb_ctf_enum_iter(id, enum_prefix_scan_cb, prefix) == 0)
+		info.e_prefix = strlen(prefix);
+
 	if (mdb_ctf_enum_iter(idr, enum_cb, &info) == -1) {
 		mdb_warn("cannot walk '%s' as enum", type);
 		return (DCMD_ERR);
@@ -492,7 +621,8 @@
 			mdb_warn("name \"%s\" not in '%s'\n", info.e_string,
 			    type);
 		else
-			mdb_warn("value %#d not in '%s'\n", info.e_value, type);
+			mdb_warn("value %#lld not in '%s'\n", info.e_value,
+			    type);
 
 		return (DCMD_ERR);
 	}
@@ -1153,11 +1283,12 @@
     mdb_ctf_id_t base, ulong_t off, printarg_t *pap)
 {
 	mdb_tgt_addr_t addr = pap->pa_addr + off / NBBY;
-	char vname[MDB_SYM_NAMLEN];
 	const char *ename;
 	int value;
+	int isp2 = enum_is_p2(base);
+	int flags = pap->pa_flags | (isp2 ? PA_INTHEX : 0);
 
-	if (!(pap->pa_flags & PA_SHOWVAL))
+	if (!(flags & PA_SHOWVAL))
 		return (0);
 
 	if (mdb_tgt_aread(pap->pa_tgt, pap->pa_as,
@@ -1166,19 +1297,23 @@
 		return (1);
 	}
 
-	if (pap->pa_flags & PA_INTHEX)
+	if (flags & PA_INTHEX)
 		mdb_printf("%#x", value);
 	else
 		mdb_printf("%#d", value);
 
-	ename = mdb_ctf_enum_name(base, value);
+	(void) mdb_inc_indent(8);
+	mdb_printf(" (");
 
-	/* If it wasn't an exact match, check if we can do P2 matching */
-	if (ename == NULL &&
-	    enum_value_to_name_p2(base, value, vname, sizeof (vname)) == 0)
-		ename = vname;
-
-	mdb_printf(" (%s)", (ename != NULL)? ename : "???");
+	if (!isp2 || enum_value_print_p2(base, value, 0) != 0) {
+		ename = mdb_ctf_enum_name(base, value);
+		if (ename == NULL) {
+			ename = "???";
+		}
+		mdb_printf("%s", ename);
+	}
+	mdb_printf(")");
+	(void) mdb_dec_indent(8);
 
 	return (0);
 }
--- a/usr/src/cmd/mdb/common/mdb/mdb_print.h	Tue Oct 27 11:26:10 2009 -0700
+++ b/usr/src/cmd/mdb/common/mdb/mdb_print.h	Tue Oct 27 13:44:45 2009 -0700
@@ -2,9 +2,8 @@
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
  *
  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  * or http://www.opensolaris.org/os/licensing.
@@ -20,15 +19,13 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
 #ifndef	_MDB_PRINT_H
 #define	_MDB_PRINT_H
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #ifdef	__cplusplus
 extern "C" {
 #endif
@@ -36,6 +33,7 @@
 #ifdef	_MDB
 
 extern int cmd_enum(uintptr_t, uint_t, int, const mdb_arg_t *);
+extern void enum_help(void);
 extern int cmd_sizeof(uintptr_t, uint_t, int, const mdb_arg_t *);
 extern int cmd_offsetof(uintptr_t, uint_t, int, const mdb_arg_t *);
 extern int cmd_list(uintptr_t, uint_t, int, const mdb_arg_t *);
--- a/usr/src/cmd/mdb/common/modules/genunix/Makefile.files	Tue Oct 27 11:26:10 2009 -0700
+++ b/usr/src/cmd/mdb/common/modules/genunix/Makefile.files	Tue Oct 27 13:44:45 2009 -0700
@@ -66,6 +66,7 @@
 	sobj.c		\
 	streams.c	\
 	sysevent.c	\
+	taskq.c		\
 	thread.c	\
 	tsd.c		\
 	tsol.c		\
--- a/usr/src/cmd/mdb/common/modules/genunix/findstack.c	Tue Oct 27 11:26:10 2009 -0700
+++ b/usr/src/cmd/mdb/common/modules/genunix/findstack.c	Tue Oct 27 13:44:45 2009 -0700
@@ -690,7 +690,36 @@
 }
 
 int
-stacks_run(int verbose)
+stacks_run_tlist(mdb_pipe_t *tlist, stacks_info_t *si)
+{
+	size_t idx;
+	size_t found = 0;
+	kthread_t kt;
+	int ret;
+
+	for (idx = 0; idx < tlist->pipe_len; idx++) {
+		uintptr_t addr = tlist->pipe_data[idx];
+
+		if (mdb_vread(&kt, sizeof (kt), addr) == -1) {
+			mdb_warn("unable to read kthread_t at %p", addr);
+			continue;
+		}
+		found++;
+
+		ret = stacks_thread_cb(addr, &kt, si);
+		if (ret == WALK_DONE)
+			break;
+		if (ret != WALK_NEXT)
+			return (-1);
+	}
+
+	if (found)
+		return (0);
+	return (-1);
+}
+
+int
+stacks_run(int verbose, mdb_pipe_t *tlist)
 {
 	stacks_info_t si;
 	findstack_info_t *fsip = &si.si_fsi;
@@ -714,9 +743,14 @@
 	if (verbose)
 		mdb_warn("stacks: processing kernel threads\n");
 
-	if (mdb_walk("thread", stacks_thread_cb, &si) != 0) {
-		mdb_warn("cannot walk \"thread\"");
-		return (DCMD_ERR);
+	if (tlist != NULL) {
+		if (stacks_run_tlist(tlist, &si))
+			return (DCMD_ERR);
+	} else {
+		if (mdb_walk("thread", stacks_thread_cb, &si) != 0) {
+			mdb_warn("cannot walk \"thread\"");
+			return (DCMD_ERR);
+		}
 	}
 
 	if (verbose)
@@ -745,7 +779,8 @@
 	stacks_hash = NULL;
 	mdb_free(si.si_hash, STACKS_HSIZE * sizeof (*si.si_hash));
 
-	stacks_state = STACKS_STATE_DONE;
+	if (tlist == NULL)
+		stacks_state = STACKS_STATE_DONE;
 
 	if (verbose)
 		mdb_warn("stacks: done\n");
@@ -959,6 +994,7 @@
 	const char *excl_tstate_str = NULL;
 	uint_t tstate = -1U;
 	uint_t excl_tstate = -1U;
+	uint_t printed = 0;
 
 	uint_t all = 0;
 	uint_t force = 0;
@@ -1066,27 +1102,6 @@
 	}
 
 	/*
-	 * Force a cleanup if we're connected to a live system. Never
-	 * do a cleanup after the first invocation around the loop.
-	 */
-	force |= (mdb_get_state() == MDB_STATE_RUNNING);
-	if (force && (flags & (DCMD_LOOPFIRST|DCMD_LOOP)) == DCMD_LOOP)
-		force = 0;
-
-	stacks_cleanup(force);
-
-	if (stacks_state == STACKS_STATE_CLEAN) {
-		int res = stacks_run(verbose);
-		if (res != DCMD_OK)
-			return (res);
-	}
-
-	if (!all && DCMD_HDRSPEC(flags) && !(flags & DCMD_PIPE_OUT)) {
-		mdb_printf("%<u>%-?s %-8s %-?s %8s%</u>\n",
-		    "THREAD", "STATE", "SOBJ", "COUNT");
-	}
-
-	/*
 	 * If there's an address specified, we're going to further filter
 	 * to only entries which have an address in the input.  To reduce
 	 * overhead (and make the sorted output come out right), we
@@ -1120,6 +1135,22 @@
 		seen = mdb_zalloc(p.pipe_len, UM_SLEEP | UM_GC);
 	}
 
+	/*
+	 * Force a cleanup if we're connected to a live system. Never
+	 * do a cleanup after the first invocation around the loop.
+	 */
+	force |= (mdb_get_state() == MDB_STATE_RUNNING);
+	if (force && (flags & (DCMD_LOOPFIRST|DCMD_LOOP)) == DCMD_LOOP)
+		force = 0;
+
+	stacks_cleanup(force);
+
+	if (stacks_state == STACKS_STATE_CLEAN) {
+		int res = stacks_run(verbose, addrspec ? &p : NULL);
+		if (res != DCMD_OK)
+			return (res);
+	}
+
 	for (idx = 0; idx < stacks_array_size; idx++) {
 		stacks_entry_t *sep = stacks_array[idx];
 		stacks_entry_t *cur = sep;
@@ -1209,9 +1240,10 @@
 			continue;
 		}
 
-		if (all) {
+		if (all || !printed) {
 			mdb_printf("%<u>%-?s %-8s %-?s %8s%</u>\n",
-			    "THREAD", "STATE", "SOBJTYPE", "COUNT");
+			    "THREAD", "STATE", "SOBJ", "COUNT");
+			printed = 1;
 		}
 
 		do {
--- a/usr/src/cmd/mdb/common/modules/genunix/genunix.c	Tue Oct 27 11:26:10 2009 -0700
+++ b/usr/src/cmd/mdb/common/modules/genunix/genunix.c	Tue Oct 27 13:44:45 2009 -0700
@@ -58,8 +58,6 @@
 #include <sys/sysconf.h>
 #include <sys/task.h>
 #include <sys/project.h>
-#include <sys/taskq.h>
-#include <sys/taskq_impl.h>
 #include <sys/errorq_impl.h>
 #include <sys/cred_impl.h>
 #include <sys/zone.h>
@@ -101,6 +99,7 @@
 #include "sobj.h"
 #include "streams.h"
 #include "sysevent.h"
+#include "taskq.h"
 #include "thread.h"
 #include "tsd.h"
 #include "tsol.h"
@@ -3853,115 +3852,6 @@
 	return (DCMD_OK);
 }
 
-/*
- * Dump a taskq_ent_t given its address.
- */
-/*ARGSUSED*/
-int
-taskq_ent(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
-{
-	taskq_ent_t	taskq_ent;
-	GElf_Sym	sym;
-	char		buf[MDB_SYM_NAMLEN+1];
-
-
-	if (!(flags & DCMD_ADDRSPEC)) {
-		mdb_warn("expected explicit taskq_ent_t address before ::\n");
-		return (DCMD_USAGE);
-	}
-
-	if (mdb_vread(&taskq_ent, sizeof (taskq_ent_t), addr) == -1) {
-		mdb_warn("failed to read taskq_ent_t at %p", addr);
-		return (DCMD_ERR);
-	}
-
-	if (DCMD_HDRSPEC(flags)) {
-		mdb_printf("%<u>%-?s    %-?s    %-s%</u>\n",
-		"ENTRY", "ARG", "FUNCTION");
-	}
-
-	if (mdb_lookup_by_addr((uintptr_t)taskq_ent.tqent_func, MDB_SYM_EXACT,
-	    buf, sizeof (buf), &sym) == -1) {
-		(void) strcpy(buf, "????");
-	}
-
-	mdb_printf("%-?p    %-?p    %s\n", addr, taskq_ent.tqent_arg, buf);
-
-	return (DCMD_OK);
-}
-
-/*
- * Given the address of the (taskq_t) task queue head, walk the queue listing
- * the address of every taskq_ent_t.
- */
-int
-taskq_walk_init(mdb_walk_state_t *wsp)
-{
-	taskq_t	tq_head;
-
-
-	if (wsp->walk_addr == NULL) {
-		mdb_warn("start address required\n");
-		return (WALK_ERR);
-	}
-
-
-	/*
-	 * Save the address of the list head entry.  This terminates the list.
-	 */
-	wsp->walk_data = (void *)
-	    ((size_t)wsp->walk_addr + offsetof(taskq_t, tq_task));
-
-
-	/*
-	 * Read in taskq head, set walk_addr to point to first taskq_ent_t.
-	 */
-	if (mdb_vread((void *)&tq_head, sizeof (taskq_t), wsp->walk_addr) ==
-	    -1) {
-		mdb_warn("failed to read taskq list head at %p",
-		    wsp->walk_addr);
-	}
-	wsp->walk_addr = (uintptr_t)tq_head.tq_task.tqent_next;
-
-
-	/*
-	 * Check for null list (next=head)
-	 */
-	if (wsp->walk_addr == (uintptr_t)wsp->walk_data) {
-		return (WALK_DONE);
-	}
-
-	return (WALK_NEXT);
-}
-
-
-int
-taskq_walk_step(mdb_walk_state_t *wsp)
-{
-	taskq_ent_t	tq_ent;
-	int		status;
-
-
-	if (mdb_vread((void *)&tq_ent, sizeof (taskq_ent_t), wsp->walk_addr) ==
-	    -1) {
-		mdb_warn("failed to read taskq_ent_t at %p", wsp->walk_addr);
-		return (DCMD_ERR);
-	}
-
-	status = wsp->walk_callback(wsp->walk_addr, (void *)&tq_ent,
-	    wsp->walk_cbdata);
-
-	wsp->walk_addr = (uintptr_t)tq_ent.tqent_next;
-
-
-	/* Check if we're at the last element (next=head) */
-	if (wsp->walk_addr == (uintptr_t)wsp->walk_data) {
-		return (WALK_DONE);
-	}
-
-	return (status);
-}
-
 int
 didmatch(uintptr_t addr, const kthread_t *thr, kt_did_t *didp)
 {
@@ -4325,7 +4215,6 @@
 		"print sysevent subclass list", sysevent_subclass_list},
 	{ "system", NULL, "print contents of /etc/system file", sysfile },
 	{ "task", NULL, "display kernel task(s)", task },
-	{ "taskq_entry", ":", "display a taskq_ent_t", taskq_ent },
 	{ "vnode2path", ":[-F]", "vnode address to pathname", vnode2path },
 	{ "vnode2smap", ":[offset]", "translate vnode to smap", vnode2smap },
 	{ "whereopen", ":", "given a vnode, dumps procs which have it open",
@@ -4571,6 +4460,11 @@
 		"filter and display STREAM sync queue", syncq, syncq_help },
 	{ "syncq2q", ":", "print queue for a given syncq", syncq2q },
 
+	/* from taskq.c */
+	{ "taskq", ":[-atT] [-m min_maxq] [-n name]",
+	    "display a taskq", taskq, taskq_help },
+	{ "taskq_entry", ":", "display a taskq_ent_t", taskq_ent },
+
 	/* from thread.c */
 	{ "thread", "?[-bdfimps]", "display a summarized kthread_t", thread,
 		thread_help },
@@ -4672,8 +4566,6 @@
 		sysevent_subclass_list_walk_fini},
 	{ "task", "given a task pointer, walk its processes",
 		task_walk_init, task_walk_step, NULL },
-	{ "taskq_entry", "given a taskq_t*, list all taskq_ent_t in the list",
-		taskq_walk_init, taskq_walk_step, NULL, NULL },
 
 	/* from avl.c */
 	{ AVL_WALK_NAME, AVL_WALK_DESC,
@@ -4929,6 +4821,14 @@
 	{ "writeq", "walk write queue side of stdata",
 		str_walk_init, strw_walk_step, str_walk_fini },
 
+	/* from taskq.c */
+	{ "taskq_thread", "given a taskq_t, list all of its threads",
+		taskq_thread_walk_init,
+		taskq_thread_walk_step,
+		taskq_thread_walk_fini },
+	{ "taskq_entry", "given a taskq_t*, list all taskq_ent_t in the list",
+		taskq_ent_walk_init, taskq_ent_walk_step, NULL },
+
 	/* from thread.c */
 	{ "deathrow", "walk threads on both lwp_ and thread_deathrow",
 		deathrow_walk_init, deathrow_walk_step, NULL },
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/mdb/common/modules/genunix/taskq.c	Tue Oct 27 13:44:45 2009 -0700
@@ -0,0 +1,483 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <mdb/mdb_param.h>
+#include <mdb/mdb_modapi.h>
+#include <mdb/mdb_ks.h>
+#include <sys/taskq.h>
+#include <sys/taskq_impl.h>
+
+#include "taskq.h"
+
+typedef struct tqarray_ent {
+	uintptr_t	tq_addr;
+	char		tq_name[TASKQ_NAMELEN + 1];
+	int		tq_instance;
+	uint_t		tq_flags;
+} tqarray_ent_t;
+
+typedef struct tq_info {
+	tqarray_ent_t	*tqi_array;
+	size_t		tqi_count;
+	size_t		tqi_size;
+} tq_info_t;
+
+/*
+ * We sort taskqs as follows:
+ *
+ *	DYNAMIC last
+ *	NOINSTANCE first
+ *	within NOINSTANCE, sort by order of creation (instance #)
+ *	within non-NOINSTANCE, sort by name (case-insensitive) then instance #
+ */
+int
+tqcmp(const void *lhs, const void *rhs)
+{
+	const tqarray_ent_t *l = lhs;
+	const tqarray_ent_t *r = rhs;
+	uint_t lflags = l->tq_flags;
+	uint_t rflags = r->tq_flags;
+	int ret;
+
+	if ((lflags & TASKQ_DYNAMIC) && !(rflags & TASKQ_DYNAMIC))
+		return (1);
+	if (!(lflags & TASKQ_DYNAMIC) && (rflags & TASKQ_DYNAMIC))
+		return (-1);
+
+	if ((lflags & TASKQ_NOINSTANCE) && !(rflags & TASKQ_NOINSTANCE))
+		return (-1);
+	if (!(lflags & TASKQ_NOINSTANCE) && (rflags & TASKQ_NOINSTANCE))
+		return (1);
+
+	if (!(lflags & TASKQ_NOINSTANCE) &&
+	    (ret = strcasecmp(l->tq_name, r->tq_name)) != 0)
+		return (ret);
+
+	if (l->tq_instance < r->tq_instance)
+		return (-1);
+	if (l->tq_instance > r->tq_instance)
+		return (1);
+	return (0);
+}
+
+/*ARGSUSED*/
+int
+tq_count(uintptr_t addr, const void *ignored, void *arg)
+{
+	tq_info_t *ti = arg;
+
+	ti->tqi_size++;
+	return (WALK_NEXT);
+}
+
+/*ARGSUSED*/
+int
+tq_fill(uintptr_t addr, const void *ignored, tq_info_t *ti)
+{
+	int idx = ti->tqi_count;
+	taskq_t tq;
+	tqarray_ent_t *tqe = &ti->tqi_array[idx];
+
+	if (idx == ti->tqi_size) {
+		mdb_warn("taskq: inadequate slop\n");
+		return (WALK_ERR);
+	}
+	if (mdb_vread(&tq, sizeof (tq), addr) == -1) {
+		mdb_warn("unable to read taskq_t at %p", addr);
+		return (WALK_NEXT);
+	}
+
+	ti->tqi_count++;
+	tqe->tq_addr = addr;
+	strncpy(tqe->tq_name, tq.tq_name, TASKQ_NAMELEN);
+	tqe->tq_instance = tq.tq_instance;
+	tqe->tq_flags = tq.tq_flags;
+
+	return (WALK_NEXT);
+}
+
+void
+taskq_help(void)
+{
+	mdb_printf("%s",
+	    "  -a    Only show taskqs with active threads.\n"
+	    "  -t    Display active thread stacks in each taskq.\n"
+	    "  -T    Display all thread stacks in each taskq.\n"
+	    "  -m min_maxq\n"
+	    "        Only show Dynamic taskqs and taskqs with a MAXQ of at\n"
+	    "        least min_maxq.\n"
+	    "  -n name\n"
+	    "        Only show taskqs which contain name somewhere in their\n"
+	    "        name.\n");
+}
+
+int
+taskq(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+	taskq_t tq;
+
+	const char *name = NULL;
+	uintptr_t minmaxq = 0;
+	uint_t	active = FALSE;
+	uint_t	print_threads = FALSE;
+	uint_t	print_threads_all = FALSE;
+
+	size_t tact, tcount, queued, maxq;
+
+	if (mdb_getopts(argc, argv,
+	    'a', MDB_OPT_SETBITS, TRUE, &active,
+	    'm', MDB_OPT_UINTPTR, &minmaxq,
+	    'n', MDB_OPT_STR, &name,
+	    't', MDB_OPT_SETBITS, TRUE, &print_threads,
+	    'T', MDB_OPT_SETBITS, TRUE, &print_threads_all,
+	    NULL) != argc)
+		return (DCMD_USAGE);
+
+	if (!(flags & DCMD_ADDRSPEC)) {
+		size_t idx;
+		tq_info_t tqi;
+
+		bzero(&tqi, sizeof (tqi));
+
+		if (mdb_walk("taskq_cache", tq_count, &tqi) == -1) {
+			mdb_warn("unable to walk taskq_cache");
+			return (DCMD_ERR);
+		}
+		tqi.tqi_size += 10; 	/* slop */
+		tqi.tqi_array = mdb_zalloc(
+		    sizeof (*tqi.tqi_array) * tqi.tqi_size, UM_SLEEP|UM_GC);
+
+		if (mdb_walk("taskq_cache", (mdb_walk_cb_t)tq_fill,
+		    &tqi) == -1) {
+			mdb_warn("unable to walk taskq_cache");
+			return (DCMD_ERR);
+		}
+		qsort(tqi.tqi_array, tqi.tqi_count, sizeof (*tqi.tqi_array),
+		    tqcmp);
+
+		flags &= ~DCMD_PIPE;
+		flags |= DCMD_LOOP | DCMD_LOOPFIRST | DCMD_ADDRSPEC;
+		for (idx = 0; idx < tqi.tqi_count; idx++) {
+			int ret = taskq(tqi.tqi_array[idx].tq_addr, flags,
+			    argc, argv);
+			if (ret != DCMD_OK)
+				return (ret);
+			flags &= ~DCMD_LOOPFIRST;
+		}
+
+		return (DCMD_OK);
+	}
+
+	if (DCMD_HDRSPEC(flags) && !(flags & DCMD_PIPE_OUT)) {
+		mdb_printf("%<u>%-?s %-31s %4s/%4s %4s %5s %4s%</u>\n",
+		    "ADDR", "NAME", "ACT", "THDS",
+		    "Q'ED", "MAXQ", "INST");
+	}
+
+	if (mdb_vread(&tq, sizeof (tq), addr) == -1) {
+		mdb_warn("failed to read taskq_t at %p", addr);
+		return (DCMD_ERR);
+	}
+
+	/* terminate the name, just in case */
+	tq.tq_name[sizeof (tq.tq_name) - 1] = 0;
+
+	tact = tq.tq_active;
+	tcount = tq.tq_nthreads;
+	queued = tq.tq_tasks - tq.tq_executed;
+	maxq = tq.tq_maxtasks;
+
+	if (tq.tq_flags & TASKQ_DYNAMIC) {
+		size_t bsize = tq.tq_nbuckets * sizeof (*tq.tq_buckets);
+		size_t idx;
+		taskq_bucket_t *b = mdb_zalloc(bsize, UM_SLEEP | UM_GC);
+
+		if (mdb_vread(b, bsize, (uintptr_t)tq.tq_buckets) == -1) {
+			mdb_warn("unable to read buckets for taskq %p", addr);
+			return (DCMD_ERR);
+		}
+
+		tcount += (tq.tq_tcreates - tq.tq_tdeaths);
+
+		for (idx = 0; idx < tq.tq_nbuckets; idx++) {
+			tact += b[idx].tqbucket_nalloc;
+		}
+	}
+
+	/* filter out taskqs that aren't of interest. */
+	if (name != NULL && strstr(tq.tq_name, name) == NULL)
+		return (DCMD_OK);
+	if (active && tact == 0 && queued == 0)
+		return (DCMD_OK);
+	if (!(tq.tq_flags & TASKQ_DYNAMIC) && maxq < minmaxq)
+		return (DCMD_OK);
+
+	if (flags & DCMD_PIPE_OUT) {
+		mdb_printf("%#lr\n", addr);
+		return (DCMD_OK);
+	}
+
+	mdb_printf("%?p %-31s %4d/%4d %4d ",
+	    addr, tq.tq_name, tact, tcount, queued);
+
+	if (tq.tq_flags & TASKQ_DYNAMIC)
+		mdb_printf("%5s ", "-");
+	else
+		mdb_printf("%5d ", maxq);
+
+	if (tq.tq_flags & TASKQ_NOINSTANCE)
+		mdb_printf("%4s", "-");
+	else
+		mdb_printf("%4x", tq.tq_instance);
+
+	mdb_printf("\n");
+
+	if (print_threads || print_threads_all) {
+		int ret;
+		char strbuf[128];
+		const char *arg =
+		    print_threads_all ? "" : "-C \"taskq_thread_wait\"";
+
+		/*
+		 * We can't use mdb_pwalk_dcmd() here, because ::stacks needs
+		 * to get the full pipeline.
+		 */
+		mdb_snprintf(strbuf, sizeof (strbuf),
+		    "%p::walk taskq_thread | ::stacks -a %s",
+		    addr, arg);
+
+		(void) mdb_inc_indent(4);
+		ret = mdb_eval(strbuf);
+		(void) mdb_dec_indent(4);
+
+		/* abort, since they could have control-Ced the eval */
+		if (ret == -1)
+			return (DCMD_ABORT);
+	}
+
+	return (DCMD_OK);
+}
+
+/*
+ * Dump a taskq_ent_t given its address.
+ */
+/*ARGSUSED*/
+int
+taskq_ent(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+	taskq_ent_t	taskq_ent;
+
+	if (!(flags & DCMD_ADDRSPEC)) {
+		return (DCMD_USAGE);
+	}
+
+	if (mdb_vread(&taskq_ent, sizeof (taskq_ent_t), addr) == -1) {
+		mdb_warn("failed to read taskq_ent_t at %p", addr);
+		return (DCMD_ERR);
+	}
+
+	if (DCMD_HDRSPEC(flags)) {
+		mdb_printf("%<u>%-?s %-?s %-s%</u>\n",
+		"ENTRY", "ARG", "FUNCTION");
+	}
+
+	mdb_printf("%-?p %-?p %a\n", addr, taskq_ent.tqent_arg,
+	    taskq_ent.tqent_func);
+
+	return (DCMD_OK);
+}
+
+
+/*
+ * Given the address of the (taskq_t) task queue head, walk the queue listing
+ * the address of every taskq_ent_t.
+ */
+int
+taskq_ent_walk_init(mdb_walk_state_t *wsp)
+{
+	taskq_t	tq_head;
+
+
+	if (wsp->walk_addr == NULL) {
+		mdb_warn("start address required\n");
+		return (WALK_ERR);
+	}
+
+
+	/*
+	 * Save the address of the list head entry.  This terminates the list.
+	 */
+	wsp->walk_data = (void *)
+	    ((size_t)wsp->walk_addr + OFFSETOF(taskq_t, tq_task));
+
+
+	/*
+	 * Read in taskq head, set walk_addr to point to first taskq_ent_t.
+	 */
+	if (mdb_vread((void *)&tq_head, sizeof (taskq_t), wsp->walk_addr) ==
+	    -1) {
+		mdb_warn("failed to read taskq list head at %p",
+		    wsp->walk_addr);
+	}
+	wsp->walk_addr = (uintptr_t)tq_head.tq_task.tqent_next;
+
+
+	/*
+	 * Check for null list (next=head)
+	 */
+	if (wsp->walk_addr == (uintptr_t)wsp->walk_data) {
+		return (WALK_DONE);
+	}
+
+	return (WALK_NEXT);
+}
+
+
+int
+taskq_ent_walk_step(mdb_walk_state_t *wsp)
+{
+	taskq_ent_t	tq_ent;
+	int		status;
+
+
+	if (mdb_vread((void *)&tq_ent, sizeof (taskq_ent_t), wsp->walk_addr) ==
+	    -1) {
+		mdb_warn("failed to read taskq_ent_t at %p", wsp->walk_addr);
+		return (DCMD_ERR);
+	}
+
+	status = wsp->walk_callback(wsp->walk_addr, (void *)&tq_ent,
+	    wsp->walk_cbdata);
+
+	wsp->walk_addr = (uintptr_t)tq_ent.tqent_next;
+
+
+	/* Check if we're at the last element (next=head) */
+	if (wsp->walk_addr == (uintptr_t)wsp->walk_data) {
+		return (WALK_DONE);
+	}
+
+	return (status);
+}
+
+typedef struct taskq_thread_info {
+	uintptr_t	tti_addr;
+	uintptr_t	*tti_tlist;
+	size_t		tti_nthreads;
+	size_t		tti_idx;
+
+	kthread_t	tti_thread;
+} taskq_thread_info_t;
+
+int
+taskq_thread_walk_init(mdb_walk_state_t *wsp)
+{
+	taskq_thread_info_t	*tti;
+	taskq_t			tq;
+	uintptr_t		*tlist;
+	size_t			nthreads;
+
+	tti = wsp->walk_data = mdb_zalloc(sizeof (*tti), UM_SLEEP);
+	tti->tti_addr = wsp->walk_addr;
+
+	if (wsp->walk_addr != NULL &&
+	    mdb_vread(&tq, sizeof (tq), wsp->walk_addr) != -1 &&
+	    !(tq.tq_flags & TASKQ_DYNAMIC)) {
+
+		nthreads = tq.tq_nthreads;
+		tlist = mdb_alloc(nthreads * sizeof (*tlist), UM_SLEEP);
+		if (tq.tq_nthreads_max == 1) {
+			tlist[0] = (uintptr_t)tq.tq_thread;
+
+		} else if (mdb_vread(tlist, nthreads * sizeof (*tlist),
+		    (uintptr_t)tq.tq_threadlist) == -1) {
+			mdb_warn("unable to read threadlist for taskq_t %p",
+			    wsp->walk_addr);
+			mdb_free(tlist, nthreads * sizeof (*tlist));
+			return (WALK_ERR);
+		}
+
+		tti->tti_tlist = tlist;
+		tti->tti_nthreads = nthreads;
+		return (WALK_NEXT);
+	}
+
+	wsp->walk_addr = 0;
+	if (mdb_layered_walk("thread", wsp) == -1) {
+		mdb_warn("can't walk \"thread\"");
+		return (WALK_ERR);
+	}
+	return (0);
+}
+
+int
+taskq_thread_walk_step(mdb_walk_state_t *wsp)
+{
+	taskq_thread_info_t	*tti = wsp->walk_data;
+
+	const kthread_t *kt = wsp->walk_layer;
+	taskq_t *tq = (taskq_t *)tti->tti_addr;
+
+	if (kt == NULL) {
+		uintptr_t addr;
+
+		if (tti->tti_idx >= tti->tti_nthreads)
+			return (WALK_DONE);
+
+		addr = tti->tti_tlist[tti->tti_idx];
+		tti->tti_idx++;
+
+		if (addr == NULL)
+			return (WALK_NEXT);
+
+		if (mdb_vread(&tti->tti_thread, sizeof (kthread_t),
+		    addr) == -1) {
+			mdb_warn("unable to read kthread_t at %p", addr);
+			return (WALK_ERR);
+		}
+		return (wsp->walk_callback(addr, &tti->tti_thread,
+		    wsp->walk_cbdata));
+	}
+
+	if (kt->t_taskq == NULL)
+		return (WALK_NEXT);
+
+	if (tq != NULL && kt->t_taskq != tq)
+		return (WALK_NEXT);
+
+	return (wsp->walk_callback(wsp->walk_addr, kt, wsp->walk_cbdata));
+}
+
+void
+taskq_thread_walk_fini(mdb_walk_state_t *wsp)
+{
+	taskq_thread_info_t	*tti = wsp->walk_data;
+
+	if (tti->tti_nthreads > 0) {
+		mdb_free(tti->tti_tlist,
+		    tti->tti_nthreads * sizeof (*tti->tti_tlist));
+	}
+	mdb_free(tti, sizeof (*tti));
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/mdb/common/modules/genunix/taskq.h	Tue Oct 27 13:44:45 2009 -0700
@@ -0,0 +1,49 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef	_TASKQ_H
+#define	_TASKQ_H
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+extern int taskq(uintptr_t, uint_t, int, const mdb_arg_t *);
+extern void taskq_help(void);
+
+extern int taskq_ent(uintptr_t, uint_t, int, const mdb_arg_t *);
+
+extern int taskq_ent_walk_init(mdb_walk_state_t *);
+extern int taskq_ent_walk_step(mdb_walk_state_t *);
+
+extern int taskq_thread_walk_init(mdb_walk_state_t *);
+extern int taskq_thread_walk_step(mdb_walk_state_t *);
+extern void taskq_thread_walk_fini(mdb_walk_state_t *);
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _TASKQ_H */
--- a/usr/src/cmd/svc/Makefile.ctf	Tue Oct 27 11:26:10 2009 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,33 +0,0 @@
-#
-# CDDL HEADER START
-#
-# The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License").  You may not use this file except in compliance
-# with the License.
-#
-# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
-# or http://www.opensolaris.org/os/licensing.
-# See the License for the specific language governing permissions
-# and limitations under the License.
-#
-# When distributing Covered Code, include this CDDL HEADER in each
-# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
-# If applicable, add the following below this CDDL HEADER, with the
-# fields enclosed by brackets "[]" replaced with your own identifying
-# information: Portions Copyright [yyyy] [name of copyright owner]
-#
-# CDDL HEADER END
-#
-#
-# Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
-#
-# ident	"%Z%%M%	%I%	%E% SMI"
-#
-
-CTFMERGE_HOOK = && $(CTFMERGE) -L VERSION -o $@ $(OBJS)
-CTFCONVERT_HOOK = && $(CTFCONVERT_O)
-CFLAGS += $(CTF_FLAGS)
-CFLAGS64 += $(CTF_FLAGS)
-NATIVE_CFLAGS += $(CTF_FLAGS)
--- a/usr/src/cmd/svc/configd/Makefile	Tue Oct 27 11:26:10 2009 -0700
+++ b/usr/src/cmd/svc/configd/Makefile	Tue Oct 27 13:44:45 2009 -0700
@@ -40,7 +40,7 @@
 SRCS = $(MYOBJS:%.o=%.c)
 
 include ../../Makefile.cmd
-include ../Makefile.ctf
+include ../../Makefile.ctf
 
 NATIVE_BUILD=$(POUND_SIGN)
 $(NATIVE_BUILD)PROG = $(MYPROG:%=%-native)
@@ -95,11 +95,11 @@
 	@NATIVE_BUILD= $(MAKE) $(MFLAGS) all
 
 $(PROG): $(OBJS)
-	$(LINK.c) -o $@ $(OBJS) $(LDLIBS) $(CTFMERGE_HOOK)
+	$(LINK.c) -o $@ $(OBJS) $(LDLIBS)
 	$(POST_PROCESS)
 
 %-native.o: %.c
-	$(COMPILE.c) -o $@ $< $(CTFCONVERT_HOOK)
+	$(COMPILE.c) -o $@ $<
 	$(POST_PROCESS_O)
 
 $(ROOTCMDDIR)/%: %.sh
--- a/usr/src/cmd/svc/prophist/Makefile	Tue Oct 27 11:26:10 2009 -0700
+++ b/usr/src/cmd/svc/prophist/Makefile	Tue Oct 27 13:44:45 2009 -0700
@@ -2,9 +2,8 @@
 # CDDL HEADER START
 #
 # The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License").  You may not use this file except in compliance
-# with the License.
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
 #
 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 # or http://www.opensolaris.org/os/licensing.
@@ -20,10 +19,9 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-#ident	"%Z%%M%	%I%	%E% SMI"
 
 PROG =		prophist
 OBJS =		prophist.o \
@@ -37,7 +35,7 @@
 POFILES = 	$(OBJS:.o=.po)
 
 include ../../Makefile.cmd
-include ../Makefile.ctf
+include ../../Makefile.ctf
 
 ROOTCMDDIR=     $(ROOT)/lib/svc/bin
 
@@ -56,7 +54,7 @@
 all: $(PROG)
 
 $(PROG): $(OBJS)
-	$(LINK.c) -o $@ $(OBJS) $(LDLIBS) $(CTFMERGE_HOOK)
+	$(LINK.c) -o $@ $(OBJS) $(LDLIBS)
 	$(POST_PROCESS)
 
 install: all $(ROOTCMD) $(ROOTPROPHIST)
@@ -68,7 +66,7 @@
 	$(LINT.c) $(LINTFLAGS) $(LNTS) $(LDLIBS)
 
 %.o: ../common/%.c
-	$(COMPILE.c) $(OUTPUT_OPTION) $< $(CTFCONVERT_HOOK)
+	$(COMPILE.c) $(OUTPUT_OPTION) $<
 	$(POST_PROCESS_O)
 
 %.ln: ../common/%.c
--- a/usr/src/cmd/svc/req.flg	Tue Oct 27 11:26:10 2009 -0700
+++ b/usr/src/cmd/svc/req.flg	Tue Oct 27 13:44:45 2009 -0700
@@ -3,9 +3,8 @@
 # CDDL HEADER START
 #
 # The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License").  You may not use this file except in compliance
-# with the License.
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
 #
 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 # or http://www.opensolaris.org/os/licensing.
@@ -21,13 +20,11 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-# ident	"%Z%%M%	%I%	%E% SMI"
-#
 
-echo_file usr/src/cmd/svc/Makefile.ctf
+echo_file usr/src/cmd/Makefile.ctf
 find_files "s.*" \
 	usr/src/cmd/svc/common \
 	usr/src/common/svc \
--- a/usr/src/cmd/svc/servinfo/Makefile	Tue Oct 27 11:26:10 2009 -0700
+++ b/usr/src/cmd/svc/servinfo/Makefile	Tue Oct 27 13:44:45 2009 -0700
@@ -29,7 +29,7 @@
 POFILES = 	$(OBJS:.o=.po)
 
 include ../../Makefile.cmd
-include ../Makefile.ctf
+include ../../Makefile.ctf
 
 LDLIBS +=	-lnsl -lsocket
 
@@ -40,7 +40,7 @@
 all: $(PROG)
 
 $(PROG): $(OBJS)
-	$(LINK.c) -o $@ $(OBJS) $(LDLIBS) $(CTFMERGE_HOOK)
+	$(LINK.c) -o $@ $(OBJS) $(LDLIBS)
 	$(POST_PROCESS)
 
 install: all $(ROOTLIBPROG)
--- a/usr/src/cmd/svc/startd/Makefile	Tue Oct 27 11:26:10 2009 -0700
+++ b/usr/src/cmd/svc/startd/Makefile	Tue Oct 27 13:44:45 2009 -0700
@@ -52,7 +52,7 @@
 POFILES = $(ALLOBJS:%.o=%.po)
 
 include ../../Makefile.cmd
-include ../Makefile.ctf
+include ../../Makefile.ctf
 
 ROOTCMDDIR=	$(ROOT)/lib/svc/bin
 
@@ -93,7 +93,7 @@
 all: $(PROG)
 
 $(PROG): $(ALLOBJS)
-	$(LINK.c) -o $@ $(ALLOBJS) $(LDLIBS) $(CTFMERGE_HOOK)
+	$(LINK.c) -o $@ $(ALLOBJS) $(LDLIBS)
 	$(POST_PROCESS)
 
 $(POFILE): $(POFILES)
--- a/usr/src/cmd/svc/svccfg/Makefile	Tue Oct 27 11:26:10 2009 -0700
+++ b/usr/src/cmd/svc/svccfg/Makefile	Tue Oct 27 13:44:45 2009 -0700
@@ -50,7 +50,7 @@
 		../common/manifest_hash.po
 
 include ../../Makefile.cmd
-include ../Makefile.ctf
+include ../../Makefile.ctf
 
 POFILE =	$(PROG)_all.po
 
@@ -120,7 +120,7 @@
 	@NATIVE_BUILD= $(MAKE) $(MFLAGS) all
 
 $(PROG): $(OBJS) $(MAPFILES)
-	$(LINK.c) -o $@ $(OBJS) $(LDLIBS) $(CTFMERGE_HOOK)
+	$(LINK.c) -o $@ $(OBJS) $(LDLIBS)
 	$(POST_PROCESS)
 
 $(POFILE): $(POFILES)
@@ -146,15 +146,15 @@
 	$(LINT.c) $(LINTFLAGS) $(LNTS) $(LDLIBS)
 
 %-native.o: %.c
-	$(COMPILE.c) -o $@ $< $(CTFCONVERT_HOOK)
+	$(COMPILE.c) -o $@ $<
 	$(POST_PROCESS_O)
 
 %-native.o: ../common/%.c
-	$(COMPILE.c) -o $@ $< $(CTFCONVERT_HOOK)
+	$(COMPILE.c) -o $@ $<
 	$(POST_PROCESS_O)
 
 %.o: ../common/%.c
-	$(COMPILE.c) $(OUTPUT_OPTION) $< $(CTFCONVERT_HOOK)
+	$(COMPILE.c) $(OUTPUT_OPTION) $<
 	$(POST_PROCESS_O)
 
 %.ln: ../common/%.c
--- a/usr/src/cmd/svc/svcprop/Makefile	Tue Oct 27 11:26:10 2009 -0700
+++ b/usr/src/cmd/svc/svcprop/Makefile	Tue Oct 27 13:44:45 2009 -0700
@@ -2,9 +2,8 @@
 # CDDL HEADER START
 #
 # The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License").  You may not use this file except in compliance
-# with the License.
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
 #
 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 # or http://www.opensolaris.org/os/licensing.
@@ -20,10 +19,9 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-#ident	"%Z%%M%	%I%	%E% SMI"
 
 PROG =		svcprop
 OBJS =		svcprop.o
@@ -31,7 +29,7 @@
 POFILES = 	$(OBJS:.o=.po)
 
 include ../../Makefile.cmd
-include ../Makefile.ctf
+include ../../Makefile.ctf
 
 LDLIBS +=	-lscf -luutil
 
@@ -42,7 +40,7 @@
 all: $(PROG)
 
 $(PROG): $(OBJS)
-	$(LINK.c) -o $@ $(OBJS) $(LDLIBS) $(CTFMERGE_HOOK)
+	$(LINK.c) -o $@ $(OBJS) $(LDLIBS)
 	$(POST_PROCESS)
 
 install: all $(ROOTPROG)
--- a/usr/src/cmd/svc/svcs/Makefile	Tue Oct 27 11:26:10 2009 -0700
+++ b/usr/src/cmd/svc/svcs/Makefile	Tue Oct 27 13:44:45 2009 -0700
@@ -2,9 +2,8 @@
 # CDDL HEADER START
 #
 # The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License").  You may not use this file except in compliance
-# with the License.
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
 #
 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 # or http://www.opensolaris.org/os/licensing.
@@ -20,10 +19,9 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-#ident	"%Z%%M%	%I%	%E% SMI"
 
 PROG =		svcs
 OBJS =		svcs.o explain.o
@@ -31,7 +29,7 @@
 POFILES = 	$(OBJS:.o=.po)
 
 include ../../Makefile.cmd
-include ../Makefile.ctf
+include ../../Makefile.ctf
 
 POFILE =	$(PROG)_all.po
 LDLIBS +=	-lcontract -lscf -luutil -lumem
@@ -43,7 +41,7 @@
 all: $(PROG)
 
 $(PROG): $(OBJS)
-	$(LINK.c) -o $@ $(OBJS) $(LDLIBS) $(CTFMERGE_HOOK)
+	$(LINK.c) -o $@ $(OBJS) $(LDLIBS)
 	$(POST_PROCESS)
 
 $(POFILE): $(POFILES)
--- a/usr/src/cmd/zdb/Makefile.com	Tue Oct 27 11:26:10 2009 -0700
+++ b/usr/src/cmd/zdb/Makefile.com	Tue Oct 27 13:44:45 2009 -0700
@@ -29,6 +29,7 @@
 OBJS= $(PROG).o zdb_il.o
 
 include ../../Makefile.cmd
+include ../../Makefile.ctf
 
 INCS += -I../../../lib/libzpool/common 
 INCS +=	-I../../../uts/common/fs/zfs
--- a/usr/src/cmd/zfs/Makefile	Tue Oct 27 11:26:10 2009 -0700
+++ b/usr/src/cmd/zfs/Makefile	Tue Oct 27 13:44:45 2009 -0700
@@ -30,6 +30,7 @@
 POFILE=		zfs.po
 
 include ../Makefile.cmd
+include ../Makefile.ctf
 
 FSTYPE=         zfs
 LINKPROGS=	mount umount
--- a/usr/src/cmd/zpool/Makefile	Tue Oct 27 11:26:10 2009 -0700
+++ b/usr/src/cmd/zpool/Makefile	Tue Oct 27 13:44:45 2009 -0700
@@ -30,6 +30,7 @@
 POFILE= zpool.po
 
 include ../Makefile.cmd
+include ../Makefile.ctf
 
 STATCOMMONDIR = $(SRC)/cmd/stat/common
 
--- a/usr/src/cmd/ztest/Makefile.com	Tue Oct 27 11:26:10 2009 -0700
+++ b/usr/src/cmd/ztest/Makefile.com	Tue Oct 27 13:44:45 2009 -0700
@@ -2,9 +2,8 @@
 # CDDL HEADER START
 #
 # The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License").  You may not use this file except in compliance
-# with the License.
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
 #
 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 # or http://www.opensolaris.org/os/licensing.
@@ -20,16 +19,16 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-# ident	"%Z%%M%	%I%	%E% SMI"
-#
 
 PROG= ztest
-SRCS= ../$(PROG).c
+OBJS= $(PROG).o
+SRCS= $(OBJS:%.o=../%.c)
 
 include ../../Makefile.cmd
+include ../../Makefile.ctf
 
 INCS += -I../../../lib/libzpool/common 
 INCS += -I../../../uts/common/fs/zfs 
@@ -50,8 +49,8 @@
 
 all: $(PROG)
 
-$(PROG): $(SRCS)
-	$(LINK.c) -o $(PROG) $(SRCS) $(LDLIBS)
+$(PROG): $(OBJS)
+	$(LINK.c) -o $(PROG) $(OBJS) $(LDLIBS)
 	$(POST_PROCESS)
 
 clean:
@@ -59,3 +58,7 @@
 lint:	lint_SRCS
 
 include ../../Makefile.targ
+
+%.o: ../%.c
+	$(COMPILE.c) $<
+	$(POST_PROCESS_O)
--- a/usr/src/uts/common/os/taskq.c	Tue Oct 27 11:26:10 2009 -0700
+++ b/usr/src/uts/common/os/taskq.c	Tue Oct 27 13:44:45 2009 -0700
@@ -203,7 +203,7 @@
  *
  *   taskq:
  *   +-------------+
- *   |tq_lock      | +---< taskq_ent_free()
+ *   | tq_lock     | +---< taskq_ent_free()
  *   +-------------+ |
  *   |...          | | tqent:                  tqent:
  *   +-------------+ | +------------+          +------------+
@@ -1238,16 +1238,29 @@
 	t->t_taskq = tq;
 }
 
-static void
-taskq_thread_wait(taskq_t *tq, kcondvar_t *cv, callb_cpr_t *cprinfo)
+/*
+ * Common "sleep taskq thread" function, which handles CPR stuff, as well
+ * as giving a nice common point for debuggers to find inactive threads.
+ */
+static clock_t
+taskq_thread_wait(taskq_t *tq, kmutex_t *mx, kcondvar_t *cv,
+    callb_cpr_t *cprinfo, clock_t timeout)
 {
-	if (tq->tq_flags & TASKQ_CPR_SAFE) {
-		cv_wait(cv, &tq->tq_lock);
-	} else {
+	clock_t ret = 0;
+
+	if (!(tq->tq_flags & TASKQ_CPR_SAFE)) {
 		CALLB_CPR_SAFE_BEGIN(cprinfo);
-		cv_wait(cv, &tq->tq_lock);
-		CALLB_CPR_SAFE_END(cprinfo, &tq->tq_lock);
 	}
+	if (timeout < 0)
+		cv_wait(cv, mx);
+	else
+		ret = cv_timedwait(cv, mx, lbolt + timeout);
+
+	if (!(tq->tq_flags & TASKQ_CPR_SAFE)) {
+		CALLB_CPR_SAFE_END(cprinfo, mx);
+	}
+
+	return (ret);
 }
 
 /*
@@ -1312,15 +1325,16 @@
 					break;
 
 				/* Wait for higher thread_ids to exit */
-				taskq_thread_wait(tq, &tq->tq_exit_cv,
-				    &cprinfo);
+				(void) taskq_thread_wait(tq, &tq->tq_lock,
+				    &tq->tq_exit_cv, &cprinfo, -1);
 				continue;
 			}
 		}
 		if ((tqe = tq->tq_task.tqent_next) == &tq->tq_task) {
 			if (--tq->tq_active == 0)
 				cv_broadcast(&tq->tq_wait_cv);
-			taskq_thread_wait(tq, &tq->tq_dispatch_cv, &cprinfo);
+			(void) taskq_thread_wait(tq, &tq->tq_lock,
+			    &tq->tq_dispatch_cv, &cprinfo, -1);
 			tq->tq_active++;
 			continue;
 		}
@@ -1441,10 +1455,8 @@
 		 * If a thread is sleeping too long, it dies.
 		 */
 		if (! (bucket->tqbucket_flags & TQBUCKET_CLOSE)) {
-			CALLB_CPR_SAFE_BEGIN(&cprinfo);
-			w = cv_timedwait(cv, lock, lbolt +
-			    taskq_thread_timeout * hz);
-			CALLB_CPR_SAFE_END(&cprinfo, lock);
+			w = taskq_thread_wait(tq, lock, cv,
+			    &cprinfo, taskq_thread_timeout * hz);
 		}
 
 		/*