xref: /onnv-gate/usr/src/cmd/mdb/common/modules/zfs/zfs.c (revision 2459:7511d9859fcd)
1789Sahrens /*
2789Sahrens  * CDDL HEADER START
3789Sahrens  *
4789Sahrens  * The contents of this file are subject to the terms of the
51544Seschrock  * Common Development and Distribution License (the "License").
61544Seschrock  * You may not use this file except in compliance with the License.
7789Sahrens  *
8789Sahrens  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9789Sahrens  * or http://www.opensolaris.org/os/licensing.
10789Sahrens  * See the License for the specific language governing permissions
11789Sahrens  * and limitations under the License.
12789Sahrens  *
13789Sahrens  * When distributing Covered Code, include this CDDL HEADER in each
14789Sahrens  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15789Sahrens  * If applicable, add the following below this CDDL HEADER, with the
16789Sahrens  * fields enclosed by brackets "[]" replaced with your own identifying
17789Sahrens  * information: Portions Copyright [yyyy] [name of copyright owner]
18789Sahrens  *
19789Sahrens  * CDDL HEADER END
20789Sahrens  */
21789Sahrens /*
221544Seschrock  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23789Sahrens  * Use is subject to license terms.
24789Sahrens  */
25789Sahrens 
26789Sahrens #pragma ident	"%Z%%M%	%I%	%E% SMI"
27789Sahrens 
28789Sahrens #include <mdb/mdb_ctf.h>
29789Sahrens #include <sys/zfs_context.h>
30789Sahrens #include <sys/mdb_modapi.h>
31789Sahrens #include <sys/dbuf.h>
32789Sahrens #include <sys/dmu_objset.h>
33789Sahrens #include <sys/dsl_dir.h>
34789Sahrens #include <sys/dsl_pool.h>
35789Sahrens #include <sys/metaslab_impl.h>
36789Sahrens #include <sys/space_map.h>
37789Sahrens #include <sys/list.h>
38789Sahrens #include <sys/spa_impl.h>
39789Sahrens #include <sys/vdev_impl.h>
40789Sahrens #include <sys/zio_compress.h>
41789Sahrens 
42789Sahrens #ifndef _KERNEL
43789Sahrens #include "../genunix/list.h"
44789Sahrens #endif
45789Sahrens 
46789Sahrens #ifdef _KERNEL
47789Sahrens #define	ZFS_OBJ_NAME	"zfs"
48789Sahrens #else
49789Sahrens #define	ZFS_OBJ_NAME	"libzpool.so.1"
50789Sahrens #endif
51789Sahrens 
52789Sahrens static char *
53789Sahrens local_strdup(const char *s)
54789Sahrens {
55789Sahrens 	char *s1 = mdb_alloc(strlen(s) + 1, UM_SLEEP);
56789Sahrens 
57789Sahrens 	(void) strcpy(s1, s);
58789Sahrens 	return (s1);
59789Sahrens }
60789Sahrens 
61789Sahrens static int
62789Sahrens getmember(uintptr_t addr, const char *type, mdb_ctf_id_t *idp,
63789Sahrens     const char *member, int len, void *buf)
64789Sahrens {
65789Sahrens 	mdb_ctf_id_t id;
66789Sahrens 	ulong_t off;
67789Sahrens 	char name[64];
68789Sahrens 
69789Sahrens 	if (idp == NULL) {
70789Sahrens 		if (mdb_ctf_lookup_by_name(type, &id) == -1) {
71789Sahrens 			mdb_warn("couldn't find type %s", type);
72789Sahrens 			return (DCMD_ERR);
73789Sahrens 		}
74789Sahrens 		idp = &id;
75789Sahrens 	} else {
76789Sahrens 		type = name;
77789Sahrens 		mdb_ctf_type_name(*idp, name, sizeof (name));
78789Sahrens 	}
79789Sahrens 
80789Sahrens 	if (mdb_ctf_offsetof(*idp, member, &off) == -1) {
81789Sahrens 		mdb_warn("couldn't find member %s of type %s\n", member, type);
82789Sahrens 		return (DCMD_ERR);
83789Sahrens 	}
84789Sahrens 	if (off % 8 != 0) {
85789Sahrens 		mdb_warn("member %s of type %s is unsupported bitfield",
86789Sahrens 		    member, type);
87789Sahrens 		return (DCMD_ERR);
88789Sahrens 	}
89789Sahrens 	off /= 8;
90789Sahrens 
91789Sahrens 	if (mdb_vread(buf, len, addr + off) == -1) {
92789Sahrens 		mdb_warn("failed to read %s from %s at %p",
93789Sahrens 		    member, type, addr + off);
94789Sahrens 		return (DCMD_ERR);
95789Sahrens 	}
96789Sahrens 	/* mdb_warn("read %s from %s at %p+%llx\n", member, type, addr, off); */
97789Sahrens 
98789Sahrens 	return (0);
99789Sahrens }
100789Sahrens 
101789Sahrens #define	GETMEMB(addr, type, member, dest) \
102789Sahrens 	getmember(addr, #type, NULL, #member, sizeof (dest), &(dest))
103789Sahrens 
104789Sahrens #define	GETMEMBID(addr, ctfid, member, dest) \
105789Sahrens 	getmember(addr, NULL, ctfid, #member, sizeof (dest), &(dest))
106789Sahrens 
107789Sahrens static int
108789Sahrens getrefcount(uintptr_t addr, mdb_ctf_id_t *id,
109789Sahrens     const char *member, uint64_t *rc)
110789Sahrens {
111789Sahrens 	static int gotid;
112789Sahrens 	static mdb_ctf_id_t rc_id;
113789Sahrens 	ulong_t off;
114789Sahrens 
115789Sahrens 	if (!gotid) {
116789Sahrens 		if (mdb_ctf_lookup_by_name("struct refcount", &rc_id) == -1) {
117789Sahrens 			mdb_warn("couldn't find struct refcount");
118789Sahrens 			return (DCMD_ERR);
119789Sahrens 		}
120789Sahrens 		gotid = TRUE;
121789Sahrens 	}
122789Sahrens 
123789Sahrens 	if (mdb_ctf_offsetof(*id, member, &off) == -1) {
124789Sahrens 		char name[64];
125789Sahrens 		mdb_ctf_type_name(*id, name, sizeof (name));
126789Sahrens 		mdb_warn("couldn't find member %s of type %s\n", member, name);
127789Sahrens 		return (DCMD_ERR);
128789Sahrens 	}
129789Sahrens 	off /= 8;
130789Sahrens 
131789Sahrens 	return (GETMEMBID(addr + off, &rc_id, rc_count, *rc));
132789Sahrens }
133789Sahrens 
134789Sahrens static int
135789Sahrens read_symbol(char *sym_name, void **bufp)
136789Sahrens {
137789Sahrens 	GElf_Sym sym;
138789Sahrens 
139789Sahrens 	if (mdb_lookup_by_obj(MDB_TGT_OBJ_EVERY, sym_name, &sym)) {
140789Sahrens 		mdb_warn("can't find symbol %s", sym_name);
141789Sahrens 		return (DCMD_ERR);
142789Sahrens 	}
143789Sahrens 
144789Sahrens 	*bufp = mdb_alloc(sym.st_size, UM_SLEEP);
145789Sahrens 
146789Sahrens 	if (mdb_vread(*bufp, sym.st_size, sym.st_value) == -1) {
147789Sahrens 		mdb_warn("can't read data for symbol %s", sym_name);
148789Sahrens 		mdb_free(*bufp, sym.st_size);
149789Sahrens 		return (DCMD_ERR);
150789Sahrens 	}
151789Sahrens 
152789Sahrens 	return (DCMD_OK);
153789Sahrens }
154789Sahrens 
155789Sahrens static int verbose;
156789Sahrens 
157789Sahrens static int
158789Sahrens freelist_walk_init(mdb_walk_state_t *wsp)
159789Sahrens {
160789Sahrens 	if (wsp->walk_addr == NULL) {
161789Sahrens 		mdb_warn("must supply starting address\n");
162789Sahrens 		return (WALK_ERR);
163789Sahrens 	}
164789Sahrens 
165789Sahrens 	wsp->walk_data = 0;  /* Index into the freelist */
166789Sahrens 	return (WALK_NEXT);
167789Sahrens }
168789Sahrens 
169789Sahrens static int
170789Sahrens freelist_walk_step(mdb_walk_state_t *wsp)
171789Sahrens {
172789Sahrens 	uint64_t entry;
173789Sahrens 	uintptr_t number = (uintptr_t)wsp->walk_data;
174789Sahrens 	char *ddata[] = { "ALLOC", "FREE", "CONDENSE", "INVALID" };
175789Sahrens 	int mapshift = SPA_MINBLOCKSHIFT;
176789Sahrens 
177789Sahrens 	if (mdb_vread(&entry, sizeof (entry), wsp->walk_addr) == -1) {
178789Sahrens 		mdb_warn("failed to read freelist entry %p", wsp->walk_addr);
179789Sahrens 		return (WALK_DONE);
180789Sahrens 	}
181789Sahrens 	wsp->walk_addr += sizeof (entry);
182789Sahrens 	wsp->walk_data = (void *)(number + 1);
183789Sahrens 
184789Sahrens 	if (SM_DEBUG_DECODE(entry)) {
185789Sahrens 		mdb_printf("DEBUG: %3u  %10s: txg=%llu  pass=%llu\n",
186789Sahrens 		    number,
187789Sahrens 		    ddata[SM_DEBUG_ACTION_DECODE(entry)],
188789Sahrens 		    SM_DEBUG_TXG_DECODE(entry),
189789Sahrens 		    SM_DEBUG_SYNCPASS_DECODE(entry));
190789Sahrens 	} else {
191789Sahrens 		mdb_printf("Entry: %3u  offsets=%08llx-%08llx  type=%c  "
192789Sahrens 		    "size=%06llx", number,
193789Sahrens 		    SM_OFFSET_DECODE(entry) << mapshift,
194789Sahrens 		    (SM_OFFSET_DECODE(entry) + SM_RUN_DECODE(entry)) <<
195789Sahrens 		    mapshift,
196789Sahrens 		    SM_TYPE_DECODE(entry) == SM_ALLOC ? 'A' : 'F',
197789Sahrens 		    SM_RUN_DECODE(entry) << mapshift);
198789Sahrens 		if (verbose)
199789Sahrens 			mdb_printf("      (raw=%012llx)\n", entry);
200789Sahrens 		mdb_printf("\n");
201789Sahrens 	}
202789Sahrens 	return (WALK_NEXT);
203789Sahrens }
204789Sahrens 
205789Sahrens 
206789Sahrens static int
207789Sahrens dataset_name(uintptr_t addr, char *buf)
208789Sahrens {
209789Sahrens 	static int gotid;
210789Sahrens 	static mdb_ctf_id_t dd_id;
211789Sahrens 	uintptr_t dd_parent;
212789Sahrens 	char dd_myname[MAXNAMELEN];
213789Sahrens 
214789Sahrens 	if (!gotid) {
215789Sahrens 		if (mdb_ctf_lookup_by_name("struct dsl_dir",
216789Sahrens 		    &dd_id) == -1) {
217789Sahrens 			mdb_warn("couldn't find struct dsl_dir");
218789Sahrens 			return (DCMD_ERR);
219789Sahrens 		}
220789Sahrens 		gotid = TRUE;
221789Sahrens 	}
222789Sahrens 	if (GETMEMBID(addr, &dd_id, dd_parent, dd_parent) ||
223789Sahrens 	    GETMEMBID(addr, &dd_id, dd_myname, dd_myname)) {
224789Sahrens 		return (DCMD_ERR);
225789Sahrens 	}
226789Sahrens 
227789Sahrens 	if (dd_parent) {
228789Sahrens 		if (dataset_name(dd_parent, buf))
229789Sahrens 			return (DCMD_ERR);
230789Sahrens 		strcat(buf, "/");
231789Sahrens 	}
232789Sahrens 
233789Sahrens 	if (dd_myname[0])
234789Sahrens 		strcat(buf, dd_myname);
235789Sahrens 	else
236789Sahrens 		strcat(buf, "???");
237789Sahrens 
238789Sahrens 	return (0);
239789Sahrens }
240789Sahrens 
241789Sahrens static int
242789Sahrens objset_name(uintptr_t addr, char *buf)
243789Sahrens {
244789Sahrens 	static int gotid;
245789Sahrens 	static mdb_ctf_id_t osi_id, ds_id;
246789Sahrens 	uintptr_t os_dsl_dataset;
247789Sahrens 	char ds_snapname[MAXNAMELEN];
248789Sahrens 	uintptr_t ds_dir;
249789Sahrens 
250789Sahrens 	buf[0] = '\0';
251789Sahrens 
252789Sahrens 	if (!gotid) {
253789Sahrens 		if (mdb_ctf_lookup_by_name("struct objset_impl",
254789Sahrens 		    &osi_id) == -1) {
255789Sahrens 			mdb_warn("couldn't find struct objset_impl");
256789Sahrens 			return (DCMD_ERR);
257789Sahrens 		}
258789Sahrens 		if (mdb_ctf_lookup_by_name("struct dsl_dataset",
259789Sahrens 		    &ds_id) == -1) {
260789Sahrens 			mdb_warn("couldn't find struct dsl_dataset");
261789Sahrens 			return (DCMD_ERR);
262789Sahrens 		}
263789Sahrens 
264789Sahrens 		gotid = TRUE;
265789Sahrens 	}
266789Sahrens 
267789Sahrens 	if (GETMEMBID(addr, &osi_id, os_dsl_dataset, os_dsl_dataset))
268789Sahrens 		return (DCMD_ERR);
269789Sahrens 
270789Sahrens 	if (os_dsl_dataset == 0) {
271789Sahrens 		strcat(buf, "mos");
272789Sahrens 		return (0);
273789Sahrens 	}
274789Sahrens 
275789Sahrens 	if (GETMEMBID(os_dsl_dataset, &ds_id, ds_snapname, ds_snapname) ||
276789Sahrens 	    GETMEMBID(os_dsl_dataset, &ds_id, ds_dir, ds_dir)) {
277789Sahrens 		return (DCMD_ERR);
278789Sahrens 	}
279789Sahrens 
280789Sahrens 	if (ds_dir && dataset_name(ds_dir, buf))
281789Sahrens 		return (DCMD_ERR);
282789Sahrens 
283789Sahrens 	if (ds_snapname[0]) {
284789Sahrens 		strcat(buf, "@");
285789Sahrens 		strcat(buf, ds_snapname);
286789Sahrens 	}
287789Sahrens 	return (0);
288789Sahrens }
289789Sahrens 
290789Sahrens static void
291789Sahrens enum_lookup(char *out, size_t size, mdb_ctf_id_t id, int val,
292789Sahrens     const char *prefix)
293789Sahrens {
294789Sahrens 	const char *cp;
295789Sahrens 	size_t len = strlen(prefix);
296789Sahrens 
297789Sahrens 	if ((cp = mdb_ctf_enum_name(id, val)) != NULL) {
298789Sahrens 		if (strncmp(cp, prefix, len) == 0)
299789Sahrens 			cp += len;
300789Sahrens 		(void) strncpy(out, cp, size);
301789Sahrens 	} else {
302789Sahrens 		mdb_snprintf(out, size, "? (%d)", val);
303789Sahrens 	}
304789Sahrens }
305789Sahrens 
306789Sahrens /* ARGSUSED */
307789Sahrens static int
308789Sahrens zio_pipeline(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
309789Sahrens {
310789Sahrens 	mdb_ctf_id_t pipe_enum;
311789Sahrens 	int i;
312789Sahrens 	char stage[1024];
313789Sahrens 
314789Sahrens 	if (mdb_ctf_lookup_by_name("enum zio_stage", &pipe_enum) == -1) {
315789Sahrens 		mdb_warn("Could not find enum zio_stage");
316789Sahrens 		return (DCMD_ERR);
317789Sahrens 	}
318789Sahrens 
319789Sahrens 	for (i = 0; i < 32; i++) {
320789Sahrens 		if (addr & (1U << i)) {
321789Sahrens 			enum_lookup(stage, sizeof (stage), pipe_enum, i,
322789Sahrens 			    "ZIO_STAGE_");
323789Sahrens 			mdb_printf("    %s\n", stage);
324789Sahrens 		}
325789Sahrens 	}
326789Sahrens 
327789Sahrens 	return (DCMD_OK);
328789Sahrens }
329789Sahrens 
330789Sahrens /* ARGSUSED */
331789Sahrens static int
332789Sahrens blkptr(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
333789Sahrens {
334789Sahrens 	blkptr_t bp;
335789Sahrens 	dmu_object_type_info_t *doti;
336789Sahrens 	zio_compress_info_t *zct;
337789Sahrens 	zio_checksum_info_t *zci;
338789Sahrens 	int i;
339789Sahrens 	char buf[MAXPATHLEN];
340789Sahrens 
341789Sahrens 	if (mdb_vread(&bp, sizeof (blkptr_t), addr) == -1) {
342789Sahrens 		mdb_warn("failed to read blkptr_t");
343789Sahrens 		return (DCMD_ERR);
344789Sahrens 	}
345789Sahrens 
346789Sahrens 	if (read_symbol("dmu_ot", (void **)&doti) != DCMD_OK)
347789Sahrens 		return (DCMD_ERR);
348789Sahrens 	for (i = 0; i < DMU_OT_NUMTYPES; i++) {
349789Sahrens 		mdb_readstr(buf, sizeof (buf), (uintptr_t)doti[i].ot_name);
350789Sahrens 		doti[i].ot_name = local_strdup(buf);
351789Sahrens 	}
352789Sahrens 
353789Sahrens 	if (read_symbol("zio_checksum_table", (void **)&zci) != DCMD_OK)
354789Sahrens 		return (DCMD_ERR);
355789Sahrens 	for (i = 0; i < ZIO_CHECKSUM_FUNCTIONS; i++) {
356789Sahrens 		mdb_readstr(buf, sizeof (buf), (uintptr_t)zci[i].ci_name);
357789Sahrens 		zci[i].ci_name = local_strdup(buf);
358789Sahrens 	}
359789Sahrens 
360789Sahrens 	if (read_symbol("zio_compress_table", (void **)&zct) != DCMD_OK)
361789Sahrens 		return (DCMD_ERR);
362789Sahrens 	for (i = 0; i < ZIO_COMPRESS_FUNCTIONS; i++) {
363789Sahrens 		mdb_readstr(buf, sizeof (buf), (uintptr_t)zct[i].ci_name);
364789Sahrens 		zct[i].ci_name = local_strdup(buf);
365789Sahrens 	}
366789Sahrens 
3671775Sbillm 	/*
3681775Sbillm 	 * Super-ick warning:  This code is also duplicated in
3691775Sbillm 	 * cmd/zdb.c .   Yeah, I hate code replication, too.
3701775Sbillm 	 */
3711775Sbillm 	for (i = 0; i < BP_GET_NDVAS(&bp); i++) {
3721732Sbonwick 		dva_t *dva = &bp.blk_dva[i];
3731775Sbillm 
3741775Sbillm 		mdb_printf("DVA[%d]: vdev_id %lld / %llx\n", i,
3751775Sbillm 		    DVA_GET_VDEV(dva), DVA_GET_OFFSET(dva));
3761775Sbillm 		mdb_printf("DVA[%d]:       GANG: %-5s  GRID:  %04x\t"
3771775Sbillm 		    "ASIZE: %llx\n", i, DVA_GET_GANG(dva) ? "TRUE" : "FALSE",
3781775Sbillm 		    DVA_GET_GRID(dva), DVA_GET_ASIZE(dva));
3791775Sbillm 		mdb_printf("DVA[%d]: :%llu:%llx:%llx:%s%s%s%s\n", i,
3801775Sbillm 		    DVA_GET_VDEV(dva), DVA_GET_OFFSET(dva), BP_GET_PSIZE(&bp),
3811775Sbillm 		    BP_SHOULD_BYTESWAP(&bp) ? "e" : "",
3821775Sbillm 		    !DVA_GET_GANG(dva) && BP_GET_LEVEL(&bp) != 0 ? "i" : "",
3831775Sbillm 		    DVA_GET_GANG(dva) ? "g" : "",
3841775Sbillm 		    BP_GET_COMPRESS(&bp) != 0 ? "d" : "");
385789Sahrens 	}
386789Sahrens 	mdb_printf("LSIZE:  %-16llx\t\tPSIZE: %llx\n",
387789Sahrens 	    BP_GET_LSIZE(&bp), BP_GET_PSIZE(&bp));
3881775Sbillm 	mdb_printf("ENDIAN: %6s\t\t\t\t\tTYPE:  %s\n",
389789Sahrens 	    BP_GET_BYTEORDER(&bp) ? "LITTLE" : "BIG",
390789Sahrens 	    doti[BP_GET_TYPE(&bp)].ot_name);
391789Sahrens 	mdb_printf("BIRTH:  %-16llx   LEVEL: %-2d\tFILL:  %llx\n",
392789Sahrens 	    bp.blk_birth, BP_GET_LEVEL(&bp), bp.blk_fill);
393789Sahrens 	mdb_printf("CKFUNC: %-16s\t\tCOMP:  %s\n",
394789Sahrens 	    zci[BP_GET_CHECKSUM(&bp)].ci_name,
395789Sahrens 	    zct[BP_GET_COMPRESS(&bp)].ci_name);
396789Sahrens 	mdb_printf("CKSUM:  %llx:%llx:%llx:%llx\n",
397789Sahrens 	    bp.blk_cksum.zc_word[0],
398789Sahrens 	    bp.blk_cksum.zc_word[1],
399789Sahrens 	    bp.blk_cksum.zc_word[2],
400789Sahrens 	    bp.blk_cksum.zc_word[3]);
401789Sahrens 
402789Sahrens 	return (DCMD_OK);
403789Sahrens }
404789Sahrens 
405789Sahrens /* ARGSUSED */
406789Sahrens static int
407789Sahrens dbuf(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
408789Sahrens {
409789Sahrens 	mdb_ctf_id_t id;
410789Sahrens 	dmu_buf_t db;
411789Sahrens 	uintptr_t objset;
412789Sahrens 	uint8_t level;
413789Sahrens 	uint64_t blkid;
414789Sahrens 	uint64_t holds;
415789Sahrens 	char objectname[32];
416789Sahrens 	char blkidname[32];
417789Sahrens 	char path[MAXNAMELEN];
418789Sahrens 
419789Sahrens 	if (DCMD_HDRSPEC(flags)) {
420789Sahrens 		mdb_printf("        addr object lvl blkid holds os\n");
421789Sahrens 	}
422789Sahrens 
423789Sahrens 	if (mdb_ctf_lookup_by_name("struct dmu_buf_impl", &id) == -1) {
424789Sahrens 		mdb_warn("couldn't find struct dmu_buf_impl_t");
425789Sahrens 		return (DCMD_ERR);
426789Sahrens 	}
427789Sahrens 
428789Sahrens 	if (GETMEMBID(addr, &id, db_objset, objset) ||
429789Sahrens 	    GETMEMBID(addr, &id, db, db) ||
430789Sahrens 	    GETMEMBID(addr, &id, db_level, level) ||
431789Sahrens 	    GETMEMBID(addr, &id, db_blkid, blkid)) {
432789Sahrens 		return (WALK_ERR);
433789Sahrens 	}
434789Sahrens 
435789Sahrens 	if (getrefcount(addr, &id, "db_holds", &holds)) {
436789Sahrens 		return (WALK_ERR);
437789Sahrens 	}
438789Sahrens 
439789Sahrens 	if (db.db_object == DMU_META_DNODE_OBJECT)
440789Sahrens 		(void) strcpy(objectname, "mdn");
441789Sahrens 	else
442789Sahrens 		(void) mdb_snprintf(objectname, sizeof (objectname), "%llx",
443789Sahrens 		    (u_longlong_t)db.db_object);
444789Sahrens 
445789Sahrens 	if (blkid == DB_BONUS_BLKID)
446789Sahrens 		(void) strcpy(blkidname, "bonus");
447789Sahrens 	else
448789Sahrens 		(void) mdb_snprintf(blkidname, sizeof (blkidname), "%llx",
449789Sahrens 		    (u_longlong_t)blkid);
450789Sahrens 
451789Sahrens 	if (objset_name(objset, path)) {
452789Sahrens 		return (WALK_ERR);
453789Sahrens 	}
454789Sahrens 
455789Sahrens 	mdb_printf("%p %8s %1u %9s %2llu %s\n",
456789Sahrens 	    addr, objectname, level, blkidname, holds, path);
457789Sahrens 
458789Sahrens 	return (DCMD_OK);
459789Sahrens }
460789Sahrens 
461789Sahrens /* ARGSUSED */
462789Sahrens static int
463789Sahrens dbuf_stats(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
464789Sahrens {
465789Sahrens #define	HISTOSZ 32
466789Sahrens 	uintptr_t dbp;
467789Sahrens 	dmu_buf_impl_t db;
468789Sahrens 	dbuf_hash_table_t ht;
469789Sahrens 	uint64_t bucket, ndbufs;
470789Sahrens 	uint64_t histo[HISTOSZ];
471789Sahrens 	uint64_t histo2[HISTOSZ];
472789Sahrens 	int i, maxidx;
473789Sahrens 
474789Sahrens 	if (mdb_readvar(&ht, "dbuf_hash_table") == -1) {
475789Sahrens 		mdb_warn("failed to read 'dbuf_hash_table'");
476789Sahrens 		return (DCMD_ERR);
477789Sahrens 	}
478789Sahrens 
479789Sahrens 	for (i = 0; i < HISTOSZ; i++) {
480789Sahrens 		histo[i] = 0;
481789Sahrens 		histo2[i] = 0;
482789Sahrens 	}
483789Sahrens 
484789Sahrens 	ndbufs = 0;
485789Sahrens 	for (bucket = 0; bucket < ht.hash_table_mask+1; bucket++) {
486789Sahrens 		int len;
487789Sahrens 
488789Sahrens 		if (mdb_vread(&dbp, sizeof (void *),
489789Sahrens 		    (uintptr_t)(ht.hash_table+bucket)) == -1) {
490789Sahrens 			mdb_warn("failed to read hash bucket %u at %p",
491789Sahrens 			    bucket, ht.hash_table+bucket);
492789Sahrens 			return (DCMD_ERR);
493789Sahrens 		}
494789Sahrens 
495789Sahrens 		len = 0;
496789Sahrens 		while (dbp != 0) {
497789Sahrens 			if (mdb_vread(&db, sizeof (dmu_buf_impl_t),
498789Sahrens 			    dbp) == -1) {
499789Sahrens 				mdb_warn("failed to read dbuf at %p", dbp);
500789Sahrens 				return (DCMD_ERR);
501789Sahrens 			}
502789Sahrens 			dbp = (uintptr_t)db.db_hash_next;
503789Sahrens 			for (i = MIN(len, HISTOSZ - 1); i >= 0; i--)
504789Sahrens 				histo2[i]++;
505789Sahrens 			len++;
506789Sahrens 			ndbufs++;
507789Sahrens 		}
508789Sahrens 
509789Sahrens 		if (len >= HISTOSZ)
510789Sahrens 			len = HISTOSZ-1;
511789Sahrens 		histo[len]++;
512789Sahrens 	}
513789Sahrens 
514789Sahrens 	mdb_printf("hash table has %llu buckets, %llu dbufs "
515789Sahrens 	    "(avg %llu buckets/dbuf)\n",
516789Sahrens 	    ht.hash_table_mask+1, ndbufs,
517789Sahrens 	    (ht.hash_table_mask+1)/ndbufs);
518789Sahrens 
519789Sahrens 	mdb_printf("\n");
520789Sahrens 	maxidx = 0;
521789Sahrens 	for (i = 0; i < HISTOSZ; i++)
522789Sahrens 		if (histo[i] > 0)
523789Sahrens 			maxidx = i;
524789Sahrens 	mdb_printf("hash chain length	number of buckets\n");
525789Sahrens 	for (i = 0; i <= maxidx; i++)
526789Sahrens 		mdb_printf("%u			%llu\n", i, histo[i]);
527789Sahrens 
528789Sahrens 	mdb_printf("\n");
529789Sahrens 	maxidx = 0;
530789Sahrens 	for (i = 0; i < HISTOSZ; i++)
531789Sahrens 		if (histo2[i] > 0)
532789Sahrens 			maxidx = i;
533789Sahrens 	mdb_printf("hash chain depth	number of dbufs\n");
534789Sahrens 	for (i = 0; i <= maxidx; i++)
535789Sahrens 		mdb_printf("%u or more		%llu	%llu%%\n",
536789Sahrens 		    i, histo2[i], histo2[i]*100/ndbufs);
537789Sahrens 
538789Sahrens 
539789Sahrens 	return (DCMD_OK);
540789Sahrens }
541789Sahrens 
542789Sahrens typedef struct dbufs_data {
543789Sahrens 	mdb_ctf_id_t id;
544789Sahrens 	uint64_t objset;
545789Sahrens 	uint64_t object;
546789Sahrens 	uint64_t level;
547789Sahrens 	uint64_t blkid;
548789Sahrens 	char *osname;
549789Sahrens } dbufs_data_t;
550789Sahrens 
551789Sahrens #define	DBUFS_UNSET	(0xbaddcafedeadbeefULL)
552789Sahrens 
553789Sahrens /* ARGSUSED */
554789Sahrens static int
555789Sahrens dbufs_cb(uintptr_t addr, const void *unknown, void *arg)
556789Sahrens {
557789Sahrens 	dbufs_data_t *data = arg;
558789Sahrens 	uintptr_t objset;
559789Sahrens 	dmu_buf_t db;
560789Sahrens 	uint8_t level;
561789Sahrens 	uint64_t blkid;
562789Sahrens 	char osname[MAXNAMELEN];
563789Sahrens 
564789Sahrens 	if (GETMEMBID(addr, &data->id, db_objset, objset) ||
565789Sahrens 	    GETMEMBID(addr, &data->id, db, db) ||
566789Sahrens 	    GETMEMBID(addr, &data->id, db_level, level) ||
567789Sahrens 	    GETMEMBID(addr, &data->id, db_blkid, blkid)) {
568789Sahrens 		return (WALK_ERR);
569789Sahrens 	}
570789Sahrens 
571789Sahrens 	if ((data->objset == DBUFS_UNSET || data->objset == objset) &&
572789Sahrens 	    (data->osname == NULL || (objset_name(objset, osname) == 0 &&
573789Sahrens 		strcmp(data->osname, osname) == 0)) &&
574789Sahrens 	    (data->object == DBUFS_UNSET || data->object == db.db_object) &&
575789Sahrens 	    (data->level == DBUFS_UNSET || data->level == level) &&
576789Sahrens 	    (data->blkid == DBUFS_UNSET || data->blkid == blkid)) {
577789Sahrens 		mdb_printf("%#lr\n", addr);
578789Sahrens 	}
579789Sahrens 	return (WALK_NEXT);
580789Sahrens }
581789Sahrens 
582789Sahrens /* ARGSUSED */
583789Sahrens static int
584789Sahrens dbufs(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
585789Sahrens {
586789Sahrens 	dbufs_data_t data;
587789Sahrens 	char *object = NULL;
588789Sahrens 	char *blkid = NULL;
589789Sahrens 
590789Sahrens 	data.objset = data.object = data.level = data.blkid = DBUFS_UNSET;
591789Sahrens 	data.osname = NULL;
592789Sahrens 
593789Sahrens 	if (mdb_getopts(argc, argv,
594789Sahrens 	    'O', MDB_OPT_UINT64, &data.objset,
595789Sahrens 	    'n', MDB_OPT_STR, &data.osname,
596789Sahrens 	    'o', MDB_OPT_STR, &object,
597789Sahrens 	    'l', MDB_OPT_UINT64, &data.level,
598789Sahrens 	    'b', MDB_OPT_STR, &blkid) != argc) {
599789Sahrens 		return (DCMD_USAGE);
600789Sahrens 	}
601789Sahrens 
602789Sahrens 	if (object) {
603789Sahrens 		if (strcmp(object, "mdn") == 0) {
604789Sahrens 			data.object = DMU_META_DNODE_OBJECT;
605789Sahrens 		} else {
606789Sahrens 			data.object = mdb_strtoull(object);
607789Sahrens 		}
608789Sahrens 	}
609789Sahrens 
610789Sahrens 	if (blkid) {
611789Sahrens 		if (strcmp(blkid, "bonus") == 0) {
612789Sahrens 			data.blkid = DB_BONUS_BLKID;
613789Sahrens 		} else {
614789Sahrens 			data.blkid = mdb_strtoull(blkid);
615789Sahrens 		}
616789Sahrens 	}
617789Sahrens 
618789Sahrens 	if (mdb_ctf_lookup_by_name("struct dmu_buf_impl", &data.id) == -1) {
619789Sahrens 		mdb_warn("couldn't find struct dmu_buf_impl_t");
620789Sahrens 		return (DCMD_ERR);
621789Sahrens 	}
622789Sahrens 
623*2459Sahrens 	if (mdb_walk("dmu_buf_impl_t", dbufs_cb, &data) != 0) {
624789Sahrens 		mdb_warn("can't walk dbufs");
625789Sahrens 		return (DCMD_ERR);
626789Sahrens 	}
627789Sahrens 
628789Sahrens 	return (DCMD_OK);
629789Sahrens }
630789Sahrens 
631789Sahrens typedef struct abuf_find_data {
632789Sahrens 	dva_t dva;
633789Sahrens 	mdb_ctf_id_t id;
634789Sahrens } abuf_find_data_t;
635789Sahrens 
636789Sahrens /* ARGSUSED */
637789Sahrens static int
638789Sahrens abuf_find_cb(uintptr_t addr, const void *unknown, void *arg)
639789Sahrens {
640789Sahrens 	abuf_find_data_t *data = arg;
641789Sahrens 	dva_t dva;
642789Sahrens 
643789Sahrens 	if (GETMEMBID(addr, &data->id, b_dva, dva)) {
644789Sahrens 		return (WALK_ERR);
645789Sahrens 	}
646789Sahrens 
647789Sahrens 	if (dva.dva_word[0] == data->dva.dva_word[0] &&
648789Sahrens 	    dva.dva_word[1] == data->dva.dva_word[1]) {
649789Sahrens 		mdb_printf("%#lr\n", addr);
650789Sahrens 	}
651789Sahrens 	return (WALK_NEXT);
652789Sahrens }
653789Sahrens 
654789Sahrens /* ARGSUSED */
655789Sahrens static int
656789Sahrens abuf_find(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
657789Sahrens {
658789Sahrens 	abuf_find_data_t data;
659789Sahrens 	GElf_Sym sym;
660789Sahrens 	int i;
661789Sahrens 	const char *syms[] = {
662789Sahrens 		"ARC_mru_top",
663789Sahrens 		"ARC_mru_bot",
664789Sahrens 		"ARC_mfu_top",
665789Sahrens 		"ARC_mfu_bot",
666789Sahrens 	};
667789Sahrens 
668789Sahrens 	if (argc != 2)
669789Sahrens 		return (DCMD_USAGE);
670789Sahrens 
671789Sahrens 	for (i = 0; i < 2; i ++) {
672789Sahrens 		switch (argv[i].a_type) {
673789Sahrens 		case MDB_TYPE_STRING:
674789Sahrens 			data.dva.dva_word[i] = mdb_strtoull(argv[i].a_un.a_str);
675789Sahrens 			break;
676789Sahrens 		case MDB_TYPE_IMMEDIATE:
677789Sahrens 			data.dva.dva_word[i] = argv[i].a_un.a_val;
678789Sahrens 			break;
679789Sahrens 		default:
680789Sahrens 			return (DCMD_USAGE);
681789Sahrens 		}
682789Sahrens 	}
683789Sahrens 
684789Sahrens 	if (mdb_ctf_lookup_by_name("struct arc_buf_hdr", &data.id) == -1) {
685789Sahrens 		mdb_warn("couldn't find struct arc_buf_hdr");
686789Sahrens 		return (DCMD_ERR);
687789Sahrens 	}
688789Sahrens 
689789Sahrens 	for (i = 0; i < sizeof (syms) / sizeof (syms[0]); i++) {
690789Sahrens 		if (mdb_lookup_by_name(syms[i], &sym)) {
691789Sahrens 			mdb_warn("can't find symbol %s", syms[i]);
692789Sahrens 			return (DCMD_ERR);
693789Sahrens 		}
694789Sahrens 
695789Sahrens 		if (mdb_pwalk("list", abuf_find_cb, &data, sym.st_value) != 0) {
696789Sahrens 			mdb_warn("can't walk %s", syms[i]);
697789Sahrens 			return (DCMD_ERR);
698789Sahrens 		}
699789Sahrens 	}
700789Sahrens 
701789Sahrens 	return (DCMD_OK);
702789Sahrens }
703789Sahrens 
704789Sahrens void
705789Sahrens abuf_help(void)
706789Sahrens {
707789Sahrens 	mdb_printf("::abuf_find dva_word[0] dva_word[1]\n");
708789Sahrens }
709789Sahrens 
710789Sahrens /*
711789Sahrens  * ::spa
712789Sahrens  *
713789Sahrens  * 	-c	Print configuration information as well
714789Sahrens  * 	-v	Print vdev state
715789Sahrens  * 	-e	Print vdev error stats
716789Sahrens  *
717789Sahrens  * Print a summarized spa_t.  When given no arguments, prints out a table of all
718789Sahrens  * active pools on the system.
719789Sahrens  */
720789Sahrens /* ARGSUSED */
721789Sahrens static int
722789Sahrens spa_print(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
723789Sahrens {
724789Sahrens 	spa_t spa;
725789Sahrens 	char poolname[MAXNAMELEN];
726789Sahrens 	const char *statetab[] = { "ACTIVE", "EXPORTED", "DESTROYED",
727789Sahrens 		"UNINIT", "UNAVAIL" };
728789Sahrens 	const char *state;
729789Sahrens 	int config = FALSE;
730789Sahrens 	int vdevs = FALSE;
731789Sahrens 	int errors = FALSE;
732789Sahrens 
733789Sahrens 	if (mdb_getopts(argc, argv,
734789Sahrens 	    'c', MDB_OPT_SETBITS, TRUE, &config,
735789Sahrens 	    'v', MDB_OPT_SETBITS, TRUE, &vdevs,
736789Sahrens 	    'e', MDB_OPT_SETBITS, TRUE, &errors,
737789Sahrens 	    NULL) != argc)
738789Sahrens 		return (DCMD_USAGE);
739789Sahrens 
740789Sahrens 	if (!(flags & DCMD_ADDRSPEC)) {
741789Sahrens 		if (mdb_walk_dcmd("spa", "spa", argc, argv) == -1) {
742789Sahrens 			mdb_warn("can't walk spa");
743789Sahrens 			return (DCMD_ERR);
744789Sahrens 		}
745789Sahrens 
746789Sahrens 		return (DCMD_OK);
747789Sahrens 	}
748789Sahrens 
749789Sahrens 	if (flags & DCMD_PIPE_OUT) {
750789Sahrens 		mdb_printf("%#lr\n", addr);
751789Sahrens 		return (DCMD_OK);
752789Sahrens 	}
753789Sahrens 
754789Sahrens 	if (DCMD_HDRSPEC(flags))
755789Sahrens 		mdb_printf("%<u>%-?s %9s %-*s%</u>\n", "ADDR", "STATE",
756789Sahrens 		    sizeof (uintptr_t) == 4 ? 60 : 52, "NAME");
757789Sahrens 
758789Sahrens 	if (mdb_vread(&spa, sizeof (spa), addr) == -1) {
759789Sahrens 		mdb_warn("failed to read spa_t at %p", addr);
760789Sahrens 		return (DCMD_ERR);
761789Sahrens 	}
762789Sahrens 
763789Sahrens 	if (mdb_readstr(poolname, sizeof (poolname), (uintptr_t)spa.spa_name)
764789Sahrens 	    == -1) {
765789Sahrens 		mdb_warn("failed to read pool name at %p", spa.spa_name);
766789Sahrens 		return (DCMD_ERR);
767789Sahrens 	}
768789Sahrens 
769789Sahrens 	if (spa.spa_state < 0 || spa.spa_state > POOL_STATE_UNAVAIL)
7701544Seschrock 		state = "UNKNOWN";
771789Sahrens 	else
772789Sahrens 		state = statetab[spa.spa_state];
773789Sahrens 
774789Sahrens 	mdb_printf("%0?p %9s %s\n", addr, state, poolname);
775789Sahrens 
776789Sahrens 	if (config) {
777789Sahrens 		mdb_printf("\n");
778789Sahrens 		mdb_inc_indent(4);
779789Sahrens 		if (mdb_call_dcmd("spa_config", addr, flags, 0,
780789Sahrens 		    NULL) != DCMD_OK)
781789Sahrens 			return (DCMD_ERR);
782789Sahrens 		mdb_dec_indent(4);
783789Sahrens 	}
784789Sahrens 
785789Sahrens 	if (vdevs || errors) {
786789Sahrens 		mdb_arg_t v;
787789Sahrens 
788789Sahrens 		v.a_type = MDB_TYPE_STRING;
789789Sahrens 		v.a_un.a_str = "-e";
790789Sahrens 
791789Sahrens 		mdb_printf("\n");
792789Sahrens 		mdb_inc_indent(4);
793789Sahrens 		if (mdb_call_dcmd("spa_vdevs", addr, flags, errors ? 1 : 0,
794789Sahrens 		    &v) != DCMD_OK)
795789Sahrens 			return (DCMD_ERR);
796789Sahrens 		mdb_dec_indent(4);
797789Sahrens 	}
798789Sahrens 
799789Sahrens 	return (DCMD_OK);
800789Sahrens }
801789Sahrens 
802789Sahrens /*
803789Sahrens  * ::spa_config
804789Sahrens  *
805789Sahrens  * Given a spa_t, print the configuration information stored in spa_config.
806789Sahrens  * Since it's just an nvlist, format it as an indented list of name=value pairs.
807789Sahrens  * We simply read the value of spa_config and pass off to ::nvlist.
808789Sahrens  */
809789Sahrens /* ARGSUSED */
810789Sahrens static int
811789Sahrens spa_print_config(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
812789Sahrens {
813789Sahrens 	spa_t spa;
814789Sahrens 
815789Sahrens 	if (argc != 0 || !(flags & DCMD_ADDRSPEC))
816789Sahrens 		return (DCMD_USAGE);
817789Sahrens 
818789Sahrens 	if (mdb_vread(&spa, sizeof (spa), addr) == -1) {
819789Sahrens 		mdb_warn("failed to read spa_t at %p", addr);
820789Sahrens 		return (DCMD_ERR);
821789Sahrens 	}
822789Sahrens 
823789Sahrens 	if (spa.spa_config == NULL) {
824789Sahrens 		mdb_printf("(none)\n");
825789Sahrens 		return (DCMD_OK);
826789Sahrens 	}
827789Sahrens 
828789Sahrens 	return (mdb_call_dcmd("nvlist", (uintptr_t)spa.spa_config, flags,
829789Sahrens 	    0, NULL));
830789Sahrens }
831789Sahrens 
832789Sahrens void
833789Sahrens vdev_help(void)
834789Sahrens {
835789Sahrens 	mdb_printf("[vdev_t*]::vdev [-qr]\n"
836789Sahrens 		"\t-> -q display vdev_queue parameters\n"
837789Sahrens 		"\t-> -r recursive (visit all children)\n");
838789Sahrens }
839789Sahrens 
840789Sahrens /*
841789Sahrens  * ::vdev
842789Sahrens  *
843789Sahrens  * Print out a summarized vdev_t, in the following form:
844789Sahrens  *
845789Sahrens  * ADDR             STATE	AUX            DESC
846789Sahrens  * fffffffbcde23df0 HEALTHY	-              /dev/dsk/c0t0d0
847789Sahrens  *
848789Sahrens  * or with "-q" to print out a vdev_t's vdev_queue parameters:
849789Sahrens  *
850789Sahrens  *  vdev_t: c26ae4c0
851789Sahrens  *     c26ae73c min pending         0x2
852789Sahrens  *     c26ae744 max pending         0x23
853789Sahrens  *     c26ae74c agg limit           0x20000
854789Sahrens  *     c26ae754 time shift          0x4
855789Sahrens  *     c26ae75c ramp rate           0x2
856789Sahrens  *
857789Sahrens  * If '-r' is specified, recursively visit all children.
858789Sahrens  *
859789Sahrens  * With '-e', the statistics associated with the vdev are printed as well.
860789Sahrens  */
861789Sahrens static int
862789Sahrens do_print_vdev(uintptr_t addr, int flags, int depth, int queue, int stats,
863789Sahrens     int recursive)
864789Sahrens {
865789Sahrens 	vdev_t vdev;
866789Sahrens 	char desc[MAXNAMELEN];
867789Sahrens 	int c, children;
868789Sahrens 	uintptr_t *child;
869789Sahrens 	const char *state, *aux;
870789Sahrens 
871789Sahrens 	if (mdb_vread(&vdev, sizeof (vdev), (uintptr_t)addr) == -1) {
872789Sahrens 		mdb_warn("failed to read vdev_t at %p\n", (uintptr_t)addr);
873789Sahrens 		return (DCMD_ERR);
874789Sahrens 	}
875789Sahrens 
876789Sahrens 	if (flags & DCMD_PIPE_OUT) {
877789Sahrens 		mdb_printf("%#lr", addr);
878789Sahrens 	} else {
879789Sahrens 		if (vdev.vdev_path != NULL) {
880789Sahrens 			if (mdb_readstr(desc, sizeof (desc),
881789Sahrens 			    (uintptr_t)vdev.vdev_path) == -1) {
882789Sahrens 				mdb_warn("failed to read vdev_path at %p\n",
883789Sahrens 				    vdev.vdev_path);
884789Sahrens 				return (DCMD_ERR);
885789Sahrens 			}
886789Sahrens 		} else if (vdev.vdev_ops != NULL) {
887789Sahrens 			vdev_ops_t ops;
888789Sahrens 			if (mdb_vread(&ops, sizeof (ops),
889789Sahrens 			    (uintptr_t)vdev.vdev_ops) == -1) {
890789Sahrens 				mdb_warn("failed to read vdev_ops at %p\n",
891789Sahrens 				    vdev.vdev_ops);
892789Sahrens 				return (DCMD_ERR);
893789Sahrens 			}
894789Sahrens 			(void) strcpy(desc, ops.vdev_op_type);
895789Sahrens 		} else {
896789Sahrens 			(void) strcpy(desc, "<unknown>");
897789Sahrens 		}
898789Sahrens 
899789Sahrens 		if (depth == 0 && DCMD_HDRSPEC(flags))
900789Sahrens 			mdb_printf("%<u>%-?s %-9s %-12s %-*s%</u>\n",
901789Sahrens 			    "ADDR", "STATE", "AUX",
902789Sahrens 			    sizeof (uintptr_t) == 4 ? 43 : 35,
903789Sahrens 			    "DESCRIPTION");
904789Sahrens 
905789Sahrens 		mdb_printf("%0?p ", addr);
906789Sahrens 
907789Sahrens 		switch (vdev.vdev_state) {
908789Sahrens 		case VDEV_STATE_CLOSED:
909789Sahrens 		    state = "CLOSED";
910789Sahrens 		    break;
911789Sahrens 		case VDEV_STATE_OFFLINE:
912789Sahrens 		    state = "OFFLINE";
913789Sahrens 		    break;
914789Sahrens 		case VDEV_STATE_CANT_OPEN:
915789Sahrens 		    state = "CANT_OPEN";
916789Sahrens 		    break;
917789Sahrens 		case VDEV_STATE_DEGRADED:
918789Sahrens 		    state = "DEGRADED";
919789Sahrens 		    break;
920789Sahrens 		case VDEV_STATE_HEALTHY:
921789Sahrens 		    state = "HEALTHY";
922789Sahrens 		    break;
923789Sahrens 		default:
924789Sahrens 		    state = "UNKNOWN";
925789Sahrens 		    break;
926789Sahrens 		}
927789Sahrens 
928789Sahrens 		switch (vdev.vdev_stat.vs_aux) {
929789Sahrens 		case VDEV_AUX_NONE:
930789Sahrens 			aux = "-";
931789Sahrens 			break;
932789Sahrens 		case VDEV_AUX_OPEN_FAILED:
933789Sahrens 			aux = "OPEN_FAILED";
934789Sahrens 			break;
935789Sahrens 		case VDEV_AUX_CORRUPT_DATA:
936789Sahrens 			aux = "CORRUPT_DATA";
937789Sahrens 			break;
938789Sahrens 		case VDEV_AUX_NO_REPLICAS:
939789Sahrens 			aux = "NO_REPLICAS";
940789Sahrens 			break;
941789Sahrens 		case VDEV_AUX_BAD_GUID_SUM:
942789Sahrens 			aux = "BAD_GUID_SUM";
943789Sahrens 			break;
944789Sahrens 		case VDEV_AUX_TOO_SMALL:
945789Sahrens 			aux = "TOO_SMALL";
946789Sahrens 			break;
947789Sahrens 		case VDEV_AUX_BAD_LABEL:
948789Sahrens 			aux = "BAD_LABEL";
949789Sahrens 			break;
950789Sahrens 		default:
951789Sahrens 			aux = "UNKNOWN";
952789Sahrens 			break;
953789Sahrens 		}
954789Sahrens 
955789Sahrens 		mdb_printf("%-9s %-12s %*s%s\n", state, aux, depth, "", desc);
956789Sahrens 
957789Sahrens 		if (queue) {
958789Sahrens 			mdb_inc_indent(4);
959789Sahrens 			mdb_printf("\n");
960789Sahrens 			mdb_printf("%p min pending		0x%llx\n",
961789Sahrens 			    (uintptr_t)(addr + offsetof(vdev_t,
962789Sahrens 			    vdev_queue.vq_min_pending)),
963789Sahrens 			    vdev.vdev_queue.vq_min_pending);
964789Sahrens 			mdb_printf("%p max pending		0x%llx\n",
965789Sahrens 			    (uintptr_t)(addr + offsetof(vdev_t,
966789Sahrens 			    vdev_queue.vq_max_pending)),
967789Sahrens 			    vdev.vdev_queue.vq_max_pending);
968789Sahrens 			mdb_printf("%p agg limit		0x%llx\n",
969789Sahrens 			    (uintptr_t)(addr + offsetof(vdev_t,
970789Sahrens 			    vdev_queue.vq_agg_limit)),
971789Sahrens 			    vdev.vdev_queue.vq_agg_limit);
972789Sahrens 			mdb_printf("%p time shift		0x%llx\n",
973789Sahrens 			    (uintptr_t)(addr + offsetof(vdev_t,
974789Sahrens 			    vdev_queue.vq_time_shift)),
975789Sahrens 			    vdev.vdev_queue.vq_time_shift);
976789Sahrens 			mdb_printf("%p ramp rate 		0x%llx\n",
977789Sahrens 			    (uintptr_t)(addr + offsetof(vdev_t,
978789Sahrens 			    vdev_queue.vq_ramp_rate)),
979789Sahrens 			    vdev.vdev_queue.vq_ramp_rate);
980789Sahrens 			mdb_dec_indent(4);
981789Sahrens 		}
982789Sahrens 
983789Sahrens 		if (stats) {
984789Sahrens 			vdev_stat_t *vs = &vdev.vdev_stat;
985789Sahrens 			int i;
986789Sahrens 
987789Sahrens 			mdb_inc_indent(4);
988789Sahrens 			mdb_printf("\n");
989789Sahrens 			mdb_printf("%<u>       %12s %12s %12s %12s "
990789Sahrens 			    "%12s%</u>\n", "READ", "WRITE", "FREE", "CLAIM",
991789Sahrens 			    "IOCTL");
992789Sahrens 			mdb_printf("OPS     ");
993789Sahrens 			for (i = 1; i < ZIO_TYPES; i++)
994789Sahrens 				mdb_printf("%11#llx%s", vs->vs_ops[i],
995789Sahrens 				    i == ZIO_TYPES - 1 ? "" : "  ");
996789Sahrens 			mdb_printf("\n");
997789Sahrens 			mdb_printf("BYTES   ");
998789Sahrens 			for (i = 1; i < ZIO_TYPES; i++)
999789Sahrens 				mdb_printf("%11#llx%s", vs->vs_bytes[i],
1000789Sahrens 				    i == ZIO_TYPES - 1 ? "" : "  ");
1001789Sahrens 
1002789Sahrens 
1003789Sahrens 			mdb_printf("\n");
1004789Sahrens 			mdb_printf("EREAD    %10#llx\n", vs->vs_read_errors);
1005789Sahrens 			mdb_printf("EWRITE   %10#llx\n", vs->vs_write_errors);
1006789Sahrens 			mdb_printf("ECKSUM   %10#llx\n",
1007789Sahrens 			    vs->vs_checksum_errors);
1008789Sahrens 			mdb_dec_indent(4);
1009789Sahrens 		}
1010789Sahrens 
1011789Sahrens 		if (queue || stats)
1012789Sahrens 			mdb_printf("\n");
1013789Sahrens 	}
1014789Sahrens 
1015789Sahrens 	children = vdev.vdev_children;
1016789Sahrens 
1017789Sahrens 	if (children == 0 || !recursive)
1018789Sahrens 		return (DCMD_OK);
1019789Sahrens 
1020789Sahrens 	child = mdb_alloc(children * sizeof (void *), UM_SLEEP | UM_GC);
1021789Sahrens 	if (mdb_vread(child, children * sizeof (void *),
1022789Sahrens 	    (uintptr_t)vdev.vdev_child) == -1) {
1023789Sahrens 		mdb_warn("failed to read vdev children at %p", vdev.vdev_child);
1024789Sahrens 		return (DCMD_ERR);
1025789Sahrens 	}
1026789Sahrens 
1027789Sahrens 	for (c = 0; c < children; c++) {
1028789Sahrens 		if (do_print_vdev(child[c], flags, depth + 2, queue, stats,
1029789Sahrens 		    recursive))
1030789Sahrens 			return (DCMD_ERR);
1031789Sahrens 	}
1032789Sahrens 
1033789Sahrens 	return (DCMD_OK);
1034789Sahrens }
1035789Sahrens 
1036789Sahrens static int
1037789Sahrens vdev_print(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1038789Sahrens {
1039789Sahrens 	int print_queue = FALSE;
1040789Sahrens 	int recursive = FALSE;
1041789Sahrens 	int stats = FALSE;
1042789Sahrens 
1043789Sahrens 	if (mdb_getopts(argc, argv,
1044789Sahrens 	    'q', MDB_OPT_SETBITS, TRUE, &print_queue,
1045789Sahrens 	    'r', MDB_OPT_SETBITS, TRUE, &recursive,
1046789Sahrens 	    'e', MDB_OPT_SETBITS, TRUE, &stats,
1047789Sahrens 	    NULL) != argc)
1048789Sahrens 		return (DCMD_USAGE);
1049789Sahrens 
1050789Sahrens 	if (!(flags & DCMD_ADDRSPEC)) {
1051789Sahrens 		mdb_warn("no vdev_t address given\n");
1052789Sahrens 		return (DCMD_ERR);
1053789Sahrens 	}
1054789Sahrens 
1055789Sahrens 	return (do_print_vdev(addr, flags, 0, print_queue, stats, recursive));
1056789Sahrens }
1057789Sahrens 
1058*2459Sahrens typedef struct metaslab_walk_data {
1059*2459Sahrens 	uint64_t mw_numvdevs;
1060*2459Sahrens 	uintptr_t *mw_vdevs;
1061*2459Sahrens 	int mw_curvdev;
1062*2459Sahrens 	uint64_t mw_nummss;
1063*2459Sahrens 	uintptr_t *mw_mss;
1064*2459Sahrens 	int mw_curms;
1065*2459Sahrens } metaslab_walk_data_t;
1066*2459Sahrens 
1067*2459Sahrens static int
1068*2459Sahrens metaslab_walk_step(mdb_walk_state_t *wsp)
1069*2459Sahrens {
1070*2459Sahrens 	metaslab_walk_data_t *mw = wsp->walk_data;
1071*2459Sahrens 	metaslab_t ms;
1072*2459Sahrens 	uintptr_t msp;
1073*2459Sahrens 
1074*2459Sahrens 	if (mw->mw_curvdev >= mw->mw_numvdevs)
1075*2459Sahrens 		return (WALK_DONE);
1076*2459Sahrens 
1077*2459Sahrens 	if (mw->mw_mss == NULL) {
1078*2459Sahrens 		uintptr_t mssp;
1079*2459Sahrens 		uintptr_t vdevp;
1080*2459Sahrens 
1081*2459Sahrens 		ASSERT(mw->mw_curms == 0);
1082*2459Sahrens 		ASSERT(mw->mw_nummss == 0);
1083*2459Sahrens 
1084*2459Sahrens 		vdevp = mw->mw_vdevs[mw->mw_curvdev];
1085*2459Sahrens 		if (GETMEMB(vdevp, struct vdev, vdev_ms, mssp) ||
1086*2459Sahrens 		    GETMEMB(vdevp, struct vdev, vdev_ms_count, mw->mw_nummss)) {
1087*2459Sahrens 			return (WALK_ERR);
1088*2459Sahrens 		}
1089*2459Sahrens 
1090*2459Sahrens 		mw->mw_mss = mdb_alloc(mw->mw_nummss * sizeof (void*),
1091*2459Sahrens 		    UM_SLEEP | UM_GC);
1092*2459Sahrens 		if (mdb_vread(mw->mw_mss, mw->mw_nummss * sizeof (void*),
1093*2459Sahrens 		    mssp) == -1) {
1094*2459Sahrens 			mdb_warn("failed to read vdev_ms at %p", mssp);
1095*2459Sahrens 			return (WALK_ERR);
1096*2459Sahrens 		}
1097*2459Sahrens 	}
1098*2459Sahrens 
1099*2459Sahrens 	if (mw->mw_curms >= mw->mw_nummss) {
1100*2459Sahrens 		mw->mw_mss = NULL;
1101*2459Sahrens 		mw->mw_curms = 0;
1102*2459Sahrens 		mw->mw_nummss = 0;
1103*2459Sahrens 		mw->mw_curvdev++;
1104*2459Sahrens 		return (WALK_NEXT);
1105*2459Sahrens 	}
1106*2459Sahrens 
1107*2459Sahrens 	msp = mw->mw_mss[mw->mw_curms];
1108*2459Sahrens 	if (mdb_vread(&ms, sizeof (metaslab_t), msp) == -1) {
1109*2459Sahrens 		mdb_warn("failed to read metaslab_t at %p", msp);
1110*2459Sahrens 		return (WALK_ERR);
1111*2459Sahrens 	}
1112*2459Sahrens 
1113*2459Sahrens 	mw->mw_curms++;
1114*2459Sahrens 
1115*2459Sahrens 	return (wsp->walk_callback(msp, &ms, wsp->walk_cbdata));
1116*2459Sahrens }
1117*2459Sahrens 
1118*2459Sahrens /* ARGSUSED */
1119*2459Sahrens static int
1120*2459Sahrens metaslab_walk_init(mdb_walk_state_t *wsp)
1121*2459Sahrens {
1122*2459Sahrens 	metaslab_walk_data_t *mw;
1123*2459Sahrens 	uintptr_t root_vdevp;
1124*2459Sahrens 	uintptr_t childp;
1125*2459Sahrens 
1126*2459Sahrens 	if (wsp->walk_addr == NULL) {
1127*2459Sahrens 		mdb_warn("must supply address of spa_t\n");
1128*2459Sahrens 		return (WALK_ERR);
1129*2459Sahrens 	}
1130*2459Sahrens 
1131*2459Sahrens 	mw = mdb_zalloc(sizeof (metaslab_walk_data_t), UM_SLEEP | UM_GC);
1132*2459Sahrens 
1133*2459Sahrens 	if (GETMEMB(wsp->walk_addr, struct spa, spa_root_vdev, root_vdevp) ||
1134*2459Sahrens 	    GETMEMB(root_vdevp, struct vdev, vdev_children, mw->mw_numvdevs) ||
1135*2459Sahrens 	    GETMEMB(root_vdevp, struct vdev, vdev_child, childp)) {
1136*2459Sahrens 		return (DCMD_ERR);
1137*2459Sahrens 	}
1138*2459Sahrens 
1139*2459Sahrens 	mw->mw_vdevs = mdb_alloc(mw->mw_numvdevs * sizeof (void *),
1140*2459Sahrens 	    UM_SLEEP | UM_GC);
1141*2459Sahrens 	if (mdb_vread(mw->mw_vdevs, mw->mw_numvdevs * sizeof (void *),
1142*2459Sahrens 	    childp) == -1) {
1143*2459Sahrens 		mdb_warn("failed to read root vdev children at %p", childp);
1144*2459Sahrens 		return (DCMD_ERR);
1145*2459Sahrens 	}
1146*2459Sahrens 
1147*2459Sahrens 	wsp->walk_data = mw;
1148*2459Sahrens 
1149*2459Sahrens 	return (WALK_NEXT);
1150*2459Sahrens }
1151*2459Sahrens 
1152789Sahrens typedef struct mdb_spa {
1153789Sahrens 	uintptr_t spa_dsl_pool;
1154789Sahrens 	uintptr_t spa_root_vdev;
1155789Sahrens } mdb_spa_t;
1156789Sahrens 
1157789Sahrens typedef struct mdb_dsl_dir {
1158789Sahrens 	uintptr_t dd_phys;
1159789Sahrens 	uint64_t dd_used_bytes;
1160789Sahrens 	int64_t dd_space_towrite[TXG_SIZE];
1161789Sahrens } mdb_dsl_dir_t;
1162789Sahrens 
1163789Sahrens typedef struct mdb_dsl_dir_phys {
1164789Sahrens 	uint64_t dd_used_bytes;
1165789Sahrens 	uint64_t dd_compressed_bytes;
1166789Sahrens 	uint64_t dd_uncompressed_bytes;
1167789Sahrens } mdb_dsl_dir_phys_t;
1168789Sahrens 
1169789Sahrens typedef struct mdb_vdev {
1170789Sahrens 	uintptr_t vdev_parent;
1171789Sahrens 	uintptr_t vdev_ms;
1172789Sahrens 	uint64_t vdev_ms_count;
1173789Sahrens 	vdev_stat_t vdev_stat;
1174789Sahrens } mdb_vdev_t;
1175789Sahrens 
1176789Sahrens typedef struct mdb_metaslab {
1177789Sahrens 	space_map_t ms_allocmap[TXG_SIZE];
1178789Sahrens 	space_map_t ms_freemap[TXG_SIZE];
1179789Sahrens 	space_map_t ms_map;
11801732Sbonwick 	space_map_obj_t ms_smo;
1181*2459Sahrens 	space_map_obj_t ms_smo_syncing;
1182789Sahrens } mdb_metaslab_t;
1183789Sahrens 
1184*2459Sahrens typedef struct space_data {
1185*2459Sahrens 	uint64_t ms_allocmap[TXG_SIZE];
1186*2459Sahrens 	uint64_t ms_freemap[TXG_SIZE];
1187*2459Sahrens 	uint64_t ms_map;
1188*2459Sahrens 	uint64_t avail;
1189*2459Sahrens 	uint64_t nowavail;
1190*2459Sahrens } space_data_t;
1191*2459Sahrens 
1192*2459Sahrens /* ARGSUSED */
1193*2459Sahrens static int
1194*2459Sahrens space_cb(uintptr_t addr, const void *unknown, void *arg)
1195*2459Sahrens {
1196*2459Sahrens 	space_data_t *sd = arg;
1197*2459Sahrens 	mdb_metaslab_t ms;
1198*2459Sahrens 
1199*2459Sahrens 	if (GETMEMB(addr, struct metaslab, ms_allocmap, ms.ms_allocmap) ||
1200*2459Sahrens 	    GETMEMB(addr, struct metaslab, ms_freemap, ms.ms_freemap) ||
1201*2459Sahrens 	    GETMEMB(addr, struct metaslab, ms_map, ms.ms_map) ||
1202*2459Sahrens 	    GETMEMB(addr, struct metaslab, ms_smo, ms.ms_smo) ||
1203*2459Sahrens 	    GETMEMB(addr, struct metaslab, ms_smo_syncing, ms.ms_smo_syncing)) {
1204*2459Sahrens 		return (WALK_ERR);
1205*2459Sahrens 	}
1206*2459Sahrens 
1207*2459Sahrens 	sd->ms_allocmap[0] += ms.ms_allocmap[0].sm_space;
1208*2459Sahrens 	sd->ms_allocmap[1] += ms.ms_allocmap[1].sm_space;
1209*2459Sahrens 	sd->ms_allocmap[2] += ms.ms_allocmap[2].sm_space;
1210*2459Sahrens 	sd->ms_allocmap[3] += ms.ms_allocmap[3].sm_space;
1211*2459Sahrens 	sd->ms_freemap[0] += ms.ms_freemap[0].sm_space;
1212*2459Sahrens 	sd->ms_freemap[1] += ms.ms_freemap[1].sm_space;
1213*2459Sahrens 	sd->ms_freemap[2] += ms.ms_freemap[2].sm_space;
1214*2459Sahrens 	sd->ms_freemap[3] += ms.ms_freemap[3].sm_space;
1215*2459Sahrens 	sd->ms_map += ms.ms_map.sm_space;
1216*2459Sahrens 	sd->avail += ms.ms_map.sm_size - ms.ms_smo.smo_alloc;
1217*2459Sahrens 	sd->nowavail += ms.ms_map.sm_size - ms.ms_smo_syncing.smo_alloc;
1218*2459Sahrens 
1219*2459Sahrens 	return (WALK_NEXT);
1220*2459Sahrens }
1221*2459Sahrens 
1222789Sahrens /*
1223789Sahrens  * ::spa_space [-b]
1224789Sahrens  *
1225789Sahrens  * Given a spa_t, print out it's on-disk space usage and in-core
1226789Sahrens  * estimates of future usage.  If -b is given, print space in bytes.
1227789Sahrens  * Otherwise print in megabytes.
1228789Sahrens  */
1229789Sahrens /* ARGSUSED */
1230789Sahrens static int
1231789Sahrens spa_space(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1232789Sahrens {
1233789Sahrens 	mdb_spa_t spa;
1234789Sahrens 	uintptr_t dp_root_dir;
1235789Sahrens 	mdb_dsl_dir_t dd;
1236789Sahrens 	mdb_dsl_dir_phys_t dsp;
1237789Sahrens 	uint64_t children;
1238789Sahrens 	uintptr_t childaddr;
1239*2459Sahrens 	space_data_t sd;
1240789Sahrens 	int shift = 20;
1241789Sahrens 	char *suffix = "M";
1242789Sahrens 	int bits = FALSE;
1243789Sahrens 
1244789Sahrens 	if (mdb_getopts(argc, argv, 'b', MDB_OPT_SETBITS, TRUE, &bits, NULL) !=
1245789Sahrens 	    argc)
1246789Sahrens 		return (DCMD_USAGE);
1247789Sahrens 	if (!(flags & DCMD_ADDRSPEC))
1248789Sahrens 		return (DCMD_USAGE);
1249789Sahrens 
1250789Sahrens 	if (bits) {
1251789Sahrens 		shift = 0;
1252789Sahrens 		suffix = "";
1253789Sahrens 	}
1254789Sahrens 
1255789Sahrens 	if (GETMEMB(addr, struct spa, spa_dsl_pool, spa.spa_dsl_pool) ||
1256789Sahrens 	    GETMEMB(addr, struct spa, spa_root_vdev, spa.spa_root_vdev) ||
1257789Sahrens 	    GETMEMB(spa.spa_root_vdev, struct vdev, vdev_children, children) ||
1258789Sahrens 	    GETMEMB(spa.spa_root_vdev, struct vdev, vdev_child, childaddr) ||
1259789Sahrens 	    GETMEMB(spa.spa_dsl_pool, struct dsl_pool,
1260789Sahrens 	    dp_root_dir, dp_root_dir) ||
1261789Sahrens 	    GETMEMB(dp_root_dir, struct dsl_dir, dd_phys, dd.dd_phys) ||
1262789Sahrens 	    GETMEMB(dp_root_dir, struct dsl_dir,
1263789Sahrens 	    dd_used_bytes, dd.dd_used_bytes) ||
1264789Sahrens 	    GETMEMB(dp_root_dir, struct dsl_dir,
1265789Sahrens 	    dd_space_towrite, dd.dd_space_towrite) ||
1266789Sahrens 	    GETMEMB(dd.dd_phys, struct dsl_dir_phys,
1267*2459Sahrens 	    dd_used_bytes, dsp.dd_used_bytes) ||
1268*2459Sahrens 	    GETMEMB(dd.dd_phys, struct dsl_dir_phys,
1269789Sahrens 	    dd_compressed_bytes, dsp.dd_compressed_bytes) ||
1270789Sahrens 	    GETMEMB(dd.dd_phys, struct dsl_dir_phys,
1271789Sahrens 	    dd_uncompressed_bytes, dsp.dd_uncompressed_bytes)) {
1272789Sahrens 		return (DCMD_ERR);
1273789Sahrens 	}
1274789Sahrens 
1275789Sahrens 	mdb_printf("dd_space_towrite = %llu%s %llu%s %llu%s %llu%s\n",
1276789Sahrens 	    dd.dd_space_towrite[0] >> shift, suffix,
1277789Sahrens 	    dd.dd_space_towrite[1] >> shift, suffix,
1278789Sahrens 	    dd.dd_space_towrite[2] >> shift, suffix,
1279789Sahrens 	    dd.dd_space_towrite[3] >> shift, suffix);
1280789Sahrens 	mdb_printf("dd_used_bytes = %llu%s\n",
1281789Sahrens 	    dd.dd_used_bytes >> shift, suffix);
1282789Sahrens 
1283789Sahrens 	mdb_printf("dd_phys.dd_used_bytes = %llu%s\n",
1284789Sahrens 	    dsp.dd_used_bytes >> shift, suffix);
1285*2459Sahrens 	mdb_printf("dd_phys.dd_compressed_bytes = %llu%s\n",
1286*2459Sahrens 	    dsp.dd_compressed_bytes >> shift, suffix);
1287*2459Sahrens 	mdb_printf("dd_phys.dd_uncompressed_bytes = %llu%s\n",
1288*2459Sahrens 	    dsp.dd_uncompressed_bytes >> shift, suffix);
1289789Sahrens 
1290*2459Sahrens 	bzero(&sd, sizeof (sd));
1291*2459Sahrens 	if (mdb_pwalk("metaslab", space_cb, &sd, addr) != 0) {
1292*2459Sahrens 		mdb_warn("can't walk metaslabs");
1293*2459Sahrens 		return (DCMD_ERR);
1294789Sahrens 	}
1295789Sahrens 
1296789Sahrens 	mdb_printf("ms_allocmap = %llu%s %llu%s %llu%s %llu%s\n",
1297*2459Sahrens 	    sd.ms_allocmap[0] >> shift, suffix,
1298*2459Sahrens 	    sd.ms_allocmap[1] >> shift, suffix,
1299*2459Sahrens 	    sd.ms_allocmap[2] >> shift, suffix,
1300*2459Sahrens 	    sd.ms_allocmap[3] >> shift, suffix);
1301789Sahrens 	mdb_printf("ms_freemap = %llu%s %llu%s %llu%s %llu%s\n",
1302*2459Sahrens 	    sd.ms_freemap[0] >> shift, suffix,
1303*2459Sahrens 	    sd.ms_freemap[1] >> shift, suffix,
1304*2459Sahrens 	    sd.ms_freemap[2] >> shift, suffix,
1305*2459Sahrens 	    sd.ms_freemap[3] >> shift, suffix);
1306*2459Sahrens 	mdb_printf("ms_map = %llu%s\n", sd.ms_map >> shift, suffix);
1307*2459Sahrens 	mdb_printf("last synced avail = %llu%s\n", sd.avail >> shift, suffix);
1308*2459Sahrens 	mdb_printf("current syncing avail = %llu%s\n",
1309*2459Sahrens 	    sd.nowavail >> shift, suffix);
1310789Sahrens 
1311789Sahrens 	return (DCMD_OK);
1312789Sahrens }
1313789Sahrens 
1314789Sahrens /*
1315789Sahrens  * ::spa_verify
1316789Sahrens  *
1317789Sahrens  * Given a spa_t, verify that that the pool is self-consistent.
1318789Sahrens  * Currently, it only checks to make sure that the vdev tree exists.
1319789Sahrens  */
1320789Sahrens /* ARGSUSED */
1321789Sahrens static int
1322789Sahrens spa_verify(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1323789Sahrens {
1324789Sahrens 	spa_t spa;
1325789Sahrens 
1326789Sahrens 	if (argc != 0 || !(flags & DCMD_ADDRSPEC))
1327789Sahrens 		return (DCMD_USAGE);
1328789Sahrens 
1329789Sahrens 	if (mdb_vread(&spa, sizeof (spa), addr) == -1) {
1330789Sahrens 		mdb_warn("failed to read spa_t at %p", addr);
1331789Sahrens 		return (DCMD_ERR);
1332789Sahrens 	}
1333789Sahrens 
1334789Sahrens 	if (spa.spa_root_vdev == NULL) {
1335789Sahrens 		mdb_printf("no vdev tree present\n");
1336789Sahrens 		return (DCMD_OK);
1337789Sahrens 	}
1338789Sahrens 
1339789Sahrens 	return (DCMD_OK);
1340789Sahrens }
1341789Sahrens 
1342789Sahrens /*
1343789Sahrens  * ::spa_vdevs
1344789Sahrens  *
1345789Sahrens  * 	-e	Include error stats
1346789Sahrens  *
1347789Sahrens  * Print out a summarized list of vdevs for the given spa_t.
1348789Sahrens  * This is accomplished by invoking "::vdev -re" on the root vdev.
1349789Sahrens  */
1350789Sahrens /* ARGSUSED */
1351789Sahrens static int
1352789Sahrens spa_vdevs(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1353789Sahrens {
1354789Sahrens 	spa_t spa;
1355789Sahrens 	mdb_arg_t v;
1356789Sahrens 	int errors = FALSE;
1357789Sahrens 
1358789Sahrens 	if (mdb_getopts(argc, argv,
1359789Sahrens 	    'e', MDB_OPT_SETBITS, TRUE, &errors,
1360789Sahrens 	    NULL) != argc)
1361789Sahrens 		return (DCMD_USAGE);
1362789Sahrens 
1363789Sahrens 	if (!(flags & DCMD_ADDRSPEC))
1364789Sahrens 		return (DCMD_USAGE);
1365789Sahrens 
1366789Sahrens 	if (mdb_vread(&spa, sizeof (spa), addr) == -1) {
1367789Sahrens 		mdb_warn("failed to read spa_t at %p", addr);
1368789Sahrens 		return (DCMD_ERR);
1369789Sahrens 	}
1370789Sahrens 
1371952Seschrock 	/*
1372952Seschrock 	 * Unitialized spa_t structures can have a NULL root vdev.
1373952Seschrock 	 */
1374952Seschrock 	if (spa.spa_root_vdev == NULL) {
1375952Seschrock 		mdb_printf("no associated vdevs\n");
1376952Seschrock 		return (DCMD_OK);
1377952Seschrock 	}
1378952Seschrock 
1379789Sahrens 	v.a_type = MDB_TYPE_STRING;
1380789Sahrens 	v.a_un.a_str = errors ? "-re" : "-r";
1381789Sahrens 
1382789Sahrens 	return (mdb_call_dcmd("vdev", (uintptr_t)spa.spa_root_vdev,
1383789Sahrens 	    flags, 1, &v));
1384789Sahrens }
1385789Sahrens 
1386789Sahrens typedef struct txg_list_walk_data {
1387789Sahrens 	uintptr_t lw_head[TXG_SIZE];
1388789Sahrens 	int	lw_txgoff;
1389789Sahrens 	int	lw_maxoff;
1390789Sahrens 	size_t	lw_offset;
1391789Sahrens 	void	*lw_obj;
1392789Sahrens } txg_list_walk_data_t;
1393789Sahrens 
1394789Sahrens static int
1395789Sahrens txg_list_walk_init_common(mdb_walk_state_t *wsp, int txg, int maxoff)
1396789Sahrens {
1397789Sahrens 	txg_list_walk_data_t *lwd;
1398789Sahrens 	txg_list_t list;
1399789Sahrens 	int i;
1400789Sahrens 
1401789Sahrens 	lwd = mdb_alloc(sizeof (txg_list_walk_data_t), UM_SLEEP | UM_GC);
1402789Sahrens 	if (mdb_vread(&list, sizeof (txg_list_t), wsp->walk_addr) == -1) {
1403789Sahrens 		mdb_warn("failed to read txg_list_t at %#lx", wsp->walk_addr);
1404789Sahrens 		return (WALK_ERR);
1405789Sahrens 	}
1406789Sahrens 
1407789Sahrens 	for (i = 0; i < TXG_SIZE; i++)
1408789Sahrens 		lwd->lw_head[i] = (uintptr_t)list.tl_head[i];
1409789Sahrens 	lwd->lw_offset = list.tl_offset;
1410789Sahrens 	lwd->lw_obj = mdb_alloc(lwd->lw_offset + sizeof (txg_node_t),
1411789Sahrens 	    UM_SLEEP | UM_GC);
1412789Sahrens 	lwd->lw_txgoff = txg;
1413789Sahrens 	lwd->lw_maxoff = maxoff;
1414789Sahrens 
1415789Sahrens 	wsp->walk_addr = lwd->lw_head[lwd->lw_txgoff];
1416789Sahrens 	wsp->walk_data = lwd;
1417789Sahrens 
1418789Sahrens 	return (WALK_NEXT);
1419789Sahrens }
1420789Sahrens 
1421789Sahrens static int
1422789Sahrens txg_list_walk_init(mdb_walk_state_t *wsp)
1423789Sahrens {
1424789Sahrens 	return (txg_list_walk_init_common(wsp, 0, TXG_SIZE-1));
1425789Sahrens }
1426789Sahrens 
1427789Sahrens static int
1428789Sahrens txg_list0_walk_init(mdb_walk_state_t *wsp)
1429789Sahrens {
1430789Sahrens 	return (txg_list_walk_init_common(wsp, 0, 0));
1431789Sahrens }
1432789Sahrens 
1433789Sahrens static int
1434789Sahrens txg_list1_walk_init(mdb_walk_state_t *wsp)
1435789Sahrens {
1436789Sahrens 	return (txg_list_walk_init_common(wsp, 1, 1));
1437789Sahrens }
1438789Sahrens 
1439789Sahrens static int
1440789Sahrens txg_list2_walk_init(mdb_walk_state_t *wsp)
1441789Sahrens {
1442789Sahrens 	return (txg_list_walk_init_common(wsp, 2, 2));
1443789Sahrens }
1444789Sahrens 
1445789Sahrens static int
1446789Sahrens txg_list3_walk_init(mdb_walk_state_t *wsp)
1447789Sahrens {
1448789Sahrens 	return (txg_list_walk_init_common(wsp, 3, 3));
1449789Sahrens }
1450789Sahrens 
1451789Sahrens static int
1452789Sahrens txg_list_walk_step(mdb_walk_state_t *wsp)
1453789Sahrens {
1454789Sahrens 	txg_list_walk_data_t *lwd = wsp->walk_data;
1455789Sahrens 	uintptr_t addr;
1456789Sahrens 	txg_node_t *node;
1457789Sahrens 	int status;
1458789Sahrens 
1459789Sahrens 	while (wsp->walk_addr == NULL && lwd->lw_txgoff < lwd->lw_maxoff) {
1460789Sahrens 		lwd->lw_txgoff++;
1461789Sahrens 		wsp->walk_addr = lwd->lw_head[lwd->lw_txgoff];
1462789Sahrens 	}
1463789Sahrens 
1464789Sahrens 	if (wsp->walk_addr == NULL)
1465789Sahrens 		return (WALK_DONE);
1466789Sahrens 
1467789Sahrens 	addr = wsp->walk_addr - lwd->lw_offset;
1468789Sahrens 
1469789Sahrens 	if (mdb_vread(lwd->lw_obj,
1470789Sahrens 	    lwd->lw_offset + sizeof (txg_node_t), addr) == -1) {
1471789Sahrens 		mdb_warn("failed to read list element at %#lx", addr);
1472789Sahrens 		return (WALK_ERR);
1473789Sahrens 	}
1474789Sahrens 
1475789Sahrens 	status = wsp->walk_callback(addr, lwd->lw_obj, wsp->walk_cbdata);
1476789Sahrens 	node = (txg_node_t *)((uintptr_t)lwd->lw_obj + lwd->lw_offset);
1477789Sahrens 	wsp->walk_addr = (uintptr_t)node->tn_next[lwd->lw_txgoff];
1478789Sahrens 
1479789Sahrens 	return (status);
1480789Sahrens }
1481789Sahrens 
1482789Sahrens /*
1483789Sahrens  * ::walk spa
1484789Sahrens  *
1485789Sahrens  * Walk all named spa_t structures in the namespace.  This is nothing more than
1486789Sahrens  * a layered avl walk.
1487789Sahrens  */
1488789Sahrens static int
1489789Sahrens spa_walk_init(mdb_walk_state_t *wsp)
1490789Sahrens {
1491789Sahrens 	GElf_Sym sym;
1492789Sahrens 
1493789Sahrens 	if (wsp->walk_addr != NULL) {
1494789Sahrens 		mdb_warn("spa walk only supports global walks\n");
1495789Sahrens 		return (WALK_ERR);
1496789Sahrens 	}
1497789Sahrens 
1498789Sahrens 	if (mdb_lookup_by_obj(ZFS_OBJ_NAME, "spa_namespace_avl", &sym) == -1) {
1499789Sahrens 		mdb_warn("failed to find symbol 'spa_namespace_avl'");
1500789Sahrens 		return (WALK_ERR);
1501789Sahrens 	}
1502789Sahrens 
1503789Sahrens 	wsp->walk_addr = (uintptr_t)sym.st_value;
1504789Sahrens 
1505789Sahrens 	if (mdb_layered_walk("avl", wsp) == -1) {
1506789Sahrens 		mdb_warn("failed to walk 'avl'\n");
1507789Sahrens 		return (WALK_ERR);
1508789Sahrens 	}
1509789Sahrens 
1510789Sahrens 	return (WALK_NEXT);
1511789Sahrens }
1512789Sahrens 
1513789Sahrens static int
1514789Sahrens spa_walk_step(mdb_walk_state_t *wsp)
1515789Sahrens {
1516789Sahrens 	spa_t	spa;
1517789Sahrens 
1518789Sahrens 	if (mdb_vread(&spa, sizeof (spa), wsp->walk_addr) == -1) {
1519789Sahrens 		mdb_warn("failed to read spa_t at %p", wsp->walk_addr);
1520789Sahrens 		return (WALK_ERR);
1521789Sahrens 	}
1522789Sahrens 
1523789Sahrens 	return (wsp->walk_callback(wsp->walk_addr, &spa, wsp->walk_cbdata));
1524789Sahrens }
1525789Sahrens 
1526789Sahrens /*
1527789Sahrens  * MDB module linkage information:
1528789Sahrens  *
1529789Sahrens  * We declare a list of structures describing our dcmds, and a function
1530789Sahrens  * named _mdb_init to return a pointer to our module information.
1531789Sahrens  */
1532789Sahrens 
1533789Sahrens static const mdb_dcmd_t dcmds[] = {
1534789Sahrens 	{ "blkptr", ":", "print blkptr_t", blkptr },
1535789Sahrens 	{ "dbuf", ":", "print dmu_buf_impl_t", dbuf },
1536789Sahrens 	{ "dbuf_stats", ":", "dbuf stats", dbuf_stats },
1537789Sahrens 	{ "dbufs",
1538789Sahrens 	"\t[-O objset_t*] [-n objset_name | \"mos\"] [-o object | \"mdn\"] \n"
1539789Sahrens 	"\t[-l level] [-b blkid | \"bonus\"]",
1540789Sahrens 	"find dmu_buf_impl_t's that meet criterion", dbufs },
1541789Sahrens 	{ "abuf_find", "dva_word[0] dva_word[1]",
1542789Sahrens 	"find arc_buf_hdr_t of a specified DVA",
1543789Sahrens 	abuf_find },
1544789Sahrens 	{ "spa", "?[-cv]", "spa_t summary", spa_print },
1545789Sahrens 	{ "spa_config", ":", "print spa_t configuration", spa_print_config },
1546789Sahrens 	{ "spa_verify", ":", "verify spa_t consistency", spa_verify },
1547789Sahrens 	{ "spa_space", ":[-b]", "print spa_t on-disk space usage", spa_space },
1548789Sahrens 	{ "spa_vdevs", ":", "given a spa_t, print vdev summary", spa_vdevs },
1549789Sahrens 	{ "vdev", ":[-qre]", "vdev_t summary", vdev_print },
1550789Sahrens 	{ "zio_pipeline", ":", "decode a zio pipeline", zio_pipeline },
1551789Sahrens 	{ NULL }
1552789Sahrens };
1553789Sahrens 
1554789Sahrens static const mdb_walker_t walkers[] = {
1555789Sahrens 	/*
1556789Sahrens 	 * In userland, there is no generic provider of list_t walkers, so we
1557789Sahrens 	 * need to add it.
1558789Sahrens 	 */
1559789Sahrens #ifndef _KERNEL
1560789Sahrens 	{ LIST_WALK_NAME, LIST_WALK_DESC,
1561789Sahrens 		list_walk_init, list_walk_step, list_walk_fini },
1562789Sahrens #endif
1563789Sahrens 	{ "zms_freelist", "walk ZFS metaslab freelist",
1564*2459Sahrens 		freelist_walk_init, freelist_walk_step, NULL },
1565789Sahrens 	{ "txg_list", "given any txg_list_t *, walk all entries in all txgs",
1566*2459Sahrens 		txg_list_walk_init, txg_list_walk_step, NULL },
1567789Sahrens 	{ "txg_list0", "given any txg_list_t *, walk all entries in txg 0",
1568*2459Sahrens 		txg_list0_walk_init, txg_list_walk_step, NULL },
1569789Sahrens 	{ "txg_list1", "given any txg_list_t *, walk all entries in txg 1",
1570*2459Sahrens 		txg_list1_walk_init, txg_list_walk_step, NULL },
1571789Sahrens 	{ "txg_list2", "given any txg_list_t *, walk all entries in txg 2",
1572*2459Sahrens 		txg_list2_walk_init, txg_list_walk_step, NULL },
1573789Sahrens 	{ "txg_list3", "given any txg_list_t *, walk all entries in txg 3",
1574*2459Sahrens 		txg_list3_walk_init, txg_list_walk_step, NULL },
1575789Sahrens 	{ "spa", "walk all spa_t entries in the namespace",
1576789Sahrens 		spa_walk_init, spa_walk_step, NULL },
1577*2459Sahrens 	{ "metaslab", "given a spa_t *, walk all metaslab_t structures",
1578*2459Sahrens 		metaslab_walk_init, metaslab_walk_step, NULL },
1579789Sahrens 	{ NULL }
1580789Sahrens };
1581789Sahrens 
1582789Sahrens static const mdb_modinfo_t modinfo = {
1583789Sahrens 	MDB_API_VERSION, dcmds, walkers
1584789Sahrens };
1585789Sahrens 
1586789Sahrens const mdb_modinfo_t *
1587789Sahrens _mdb_init(void)
1588789Sahrens {
1589789Sahrens 	return (&modinfo);
1590789Sahrens }
1591