1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
  23  * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
  24  */
  25 
  26 /* Portions Copyright 2010 Robert Milkowski */
  27 
  28 #include <mdb/mdb_ctf.h>
  29 #include <sys/zfs_context.h>
  30 #include <sys/mdb_modapi.h>
  31 #include <sys/dbuf.h>
  32 #include <sys/dmu_objset.h>
  33 #include <sys/dsl_dir.h>
  34 #include <sys/dsl_pool.h>
  35 #include <sys/metaslab_impl.h>
  36 #include <sys/space_map.h>
  37 #include <sys/list.h>
  38 #include <sys/spa_impl.h>
  39 #include <sys/vdev_impl.h>
  40 #include <sys/zap_leaf.h>
  41 #include <sys/zap_impl.h>
  42 #include <ctype.h>
  43 #include <sys/zfs_acl.h>
  44 #include <sys/sa_impl.h>
  45 
  46 #ifdef _KERNEL
  47 #define ZFS_OBJ_NAME    "zfs"
  48 #else
  49 #define ZFS_OBJ_NAME    "libzpool.so.1"
  50 #endif
  51 
  52 #ifndef _KERNEL
  53 int aok;
  54 #endif
  55 
  56 static int
  57 getmember(uintptr_t addr, const char *type, mdb_ctf_id_t *idp,
  58     const char *member, int len, void *buf)
  59 {
  60         mdb_ctf_id_t id;
  61         ulong_t off;
  62         char name[64];
  63 
  64         if (idp == NULL) {
  65                 if (mdb_ctf_lookup_by_name(type, &id) == -1) {
  66                         mdb_warn("couldn't find type %s", type);
  67                         return (DCMD_ERR);
  68                 }
  69                 idp = &id;
  70         } else {
  71                 type = name;
  72                 mdb_ctf_type_name(*idp, name, sizeof (name));
  73         }
  74 
  75         if (mdb_ctf_offsetof(*idp, member, &off) == -1) {
  76                 mdb_warn("couldn't find member %s of type %s\n", member, type);
  77                 return (DCMD_ERR);
  78         }
  79         if (off % 8 != 0) {
  80                 mdb_warn("member %s of type %s is unsupported bitfield",
  81                     member, type);
  82                 return (DCMD_ERR);
  83         }
  84         off /= 8;
  85 
  86         if (mdb_vread(buf, len, addr + off) == -1) {
  87                 mdb_warn("failed to read %s from %s at %p",
  88                     member, type, addr + off);
  89                 return (DCMD_ERR);
  90         }
  91         /* mdb_warn("read %s from %s at %p+%llx\n", member, type, addr, off); */
  92 
  93         return (0);
  94 }
  95 
  96 #define GETMEMB(addr, type, member, dest) \
  97         getmember(addr, #type, NULL, #member, sizeof (dest), &(dest))
  98 
  99 #define GETMEMBID(addr, ctfid, member, dest) \
 100         getmember(addr, NULL, ctfid, #member, sizeof (dest), &(dest))
 101 
 102 static int
 103 getrefcount(uintptr_t addr, mdb_ctf_id_t *id,
 104     const char *member, uint64_t *rc)
 105 {
 106         static int gotid;
 107         static mdb_ctf_id_t rc_id;
 108         ulong_t off;
 109 
 110         if (!gotid) {
 111                 if (mdb_ctf_lookup_by_name("struct refcount", &rc_id) == -1) {
 112                         mdb_warn("couldn't find struct refcount");
 113                         return (DCMD_ERR);
 114                 }
 115                 gotid = TRUE;
 116         }
 117 
 118         if (mdb_ctf_offsetof(*id, member, &off) == -1) {
 119                 char name[64];
 120                 mdb_ctf_type_name(*id, name, sizeof (name));
 121                 mdb_warn("couldn't find member %s of type %s\n", member, name);
 122                 return (DCMD_ERR);
 123         }
 124         off /= 8;
 125 
 126         return (GETMEMBID(addr + off, &rc_id, rc_count, *rc));
 127 }
 128 
 129 static boolean_t
 130 strisprint(const char *cp)
 131 {
 132         for (; *cp; cp++) {
 133                 if (!isprint(*cp))
 134                         return (B_FALSE);
 135         }
 136         return (B_TRUE);
 137 }
 138 
 139 static int verbose;
 140 
 141 static int
 142 freelist_walk_init(mdb_walk_state_t *wsp)
 143 {
 144         if (wsp->walk_addr == NULL) {
 145                 mdb_warn("must supply starting address\n");
 146                 return (WALK_ERR);
 147         }
 148 
 149         wsp->walk_data = 0;  /* Index into the freelist */
 150         return (WALK_NEXT);
 151 }
 152 
 153 static int
 154 freelist_walk_step(mdb_walk_state_t *wsp)
 155 {
 156         uint64_t entry;
 157         uintptr_t number = (uintptr_t)wsp->walk_data;
 158         char *ddata[] = { "ALLOC", "FREE", "CONDENSE", "INVALID",
 159                             "INVALID", "INVALID", "INVALID", "INVALID" };
 160         int mapshift = SPA_MINBLOCKSHIFT;
 161 
 162         if (mdb_vread(&entry, sizeof (entry), wsp->walk_addr) == -1) {
 163                 mdb_warn("failed to read freelist entry %p", wsp->walk_addr);
 164                 return (WALK_DONE);
 165         }
 166         wsp->walk_addr += sizeof (entry);
 167         wsp->walk_data = (void *)(number + 1);
 168 
 169         if (SM_DEBUG_DECODE(entry)) {
 170                 mdb_printf("DEBUG: %3u  %10s: txg=%llu  pass=%llu\n",
 171                     number,
 172                     ddata[SM_DEBUG_ACTION_DECODE(entry)],
 173                     SM_DEBUG_TXG_DECODE(entry),
 174                     SM_DEBUG_SYNCPASS_DECODE(entry));
 175         } else {
 176                 mdb_printf("Entry: %3u  offsets=%08llx-%08llx  type=%c  "
 177                     "size=%06llx", number,
 178                     SM_OFFSET_DECODE(entry) << mapshift,
 179                     (SM_OFFSET_DECODE(entry) + SM_RUN_DECODE(entry)) <<
 180                     mapshift,
 181                     SM_TYPE_DECODE(entry) == SM_ALLOC ? 'A' : 'F',
 182                     SM_RUN_DECODE(entry) << mapshift);
 183                 if (verbose)
 184                         mdb_printf("      (raw=%012llx)\n", entry);
 185                 mdb_printf("\n");
 186         }
 187         return (WALK_NEXT);
 188 }
 189 
 190 
 191 static int
 192 dataset_name(uintptr_t addr, char *buf)
 193 {
 194         static int gotid;
 195         static mdb_ctf_id_t dd_id;
 196         uintptr_t dd_parent;
 197         char dd_myname[MAXNAMELEN];
 198 
 199         if (!gotid) {
 200                 if (mdb_ctf_lookup_by_name("struct dsl_dir",
 201                     &dd_id) == -1) {
 202                         mdb_warn("couldn't find struct dsl_dir");
 203                         return (DCMD_ERR);
 204                 }
 205                 gotid = TRUE;
 206         }
 207         if (GETMEMBID(addr, &dd_id, dd_parent, dd_parent) ||
 208             GETMEMBID(addr, &dd_id, dd_myname, dd_myname)) {
 209                 return (DCMD_ERR);
 210         }
 211 
 212         if (dd_parent) {
 213                 if (dataset_name(dd_parent, buf))
 214                         return (DCMD_ERR);
 215                 strcat(buf, "/");
 216         }
 217 
 218         if (dd_myname[0])
 219                 strcat(buf, dd_myname);
 220         else
 221                 strcat(buf, "???");
 222 
 223         return (0);
 224 }
 225 
 226 static int
 227 objset_name(uintptr_t addr, char *buf)
 228 {
 229         static int gotid;
 230         static mdb_ctf_id_t os_id, ds_id;
 231         uintptr_t os_dsl_dataset;
 232         char ds_snapname[MAXNAMELEN];
 233         uintptr_t ds_dir;
 234 
 235         buf[0] = '\0';
 236 
 237         if (!gotid) {
 238                 if (mdb_ctf_lookup_by_name("struct objset",
 239                     &os_id) == -1) {
 240                         mdb_warn("couldn't find struct objset");
 241                         return (DCMD_ERR);
 242                 }
 243                 if (mdb_ctf_lookup_by_name("struct dsl_dataset",
 244                     &ds_id) == -1) {
 245                         mdb_warn("couldn't find struct dsl_dataset");
 246                         return (DCMD_ERR);
 247                 }
 248 
 249                 gotid = TRUE;
 250         }
 251 
 252         if (GETMEMBID(addr, &os_id, os_dsl_dataset, os_dsl_dataset))
 253                 return (DCMD_ERR);
 254 
 255         if (os_dsl_dataset == 0) {
 256                 strcat(buf, "mos");
 257                 return (0);
 258         }
 259 
 260         if (GETMEMBID(os_dsl_dataset, &ds_id, ds_snapname, ds_snapname) ||
 261             GETMEMBID(os_dsl_dataset, &ds_id, ds_dir, ds_dir)) {
 262                 return (DCMD_ERR);
 263         }
 264 
 265         if (ds_dir && dataset_name(ds_dir, buf))
 266                 return (DCMD_ERR);
 267 
 268         if (ds_snapname[0]) {
 269                 strcat(buf, "@");
 270                 strcat(buf, ds_snapname);
 271         }
 272         return (0);
 273 }
 274 
 275 static void
 276 enum_lookup(char *out, size_t size, mdb_ctf_id_t id, int val,
 277     const char *prefix)
 278 {
 279         const char *cp;
 280         size_t len = strlen(prefix);
 281 
 282         if ((cp = mdb_ctf_enum_name(id, val)) != NULL) {
 283                 if (strncmp(cp, prefix, len) == 0)
 284                         cp += len;
 285                 (void) strncpy(out, cp, size);
 286         } else {
 287                 mdb_snprintf(out, size, "? (%d)", val);
 288         }
 289 }
 290 
 291 /* ARGSUSED */
 292 static int
 293 zfs_params(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
 294 {
 295         /*
 296          * This table can be approximately generated by running:
 297          * egrep "^[a-z0-9_]+ [a-z0-9_]+( =.*)?;" *.c | cut -d ' ' -f 2
 298          */
 299         static const char *params[] = {
 300                 "arc_reduce_dnlc_percent",
 301                 "zfs_arc_max",
 302                 "zfs_arc_min",
 303                 "arc_shrink_shift",
 304                 "zfs_mdcomp_disable",
 305                 "zfs_prefetch_disable",
 306                 "zfetch_max_streams",
 307                 "zfetch_min_sec_reap",
 308                 "zfetch_block_cap",
 309                 "zfetch_array_rd_sz",
 310                 "zfs_default_bs",
 311                 "zfs_default_ibs",
 312                 "metaslab_aliquot",
 313                 "reference_tracking_enable",
 314                 "reference_history",
 315                 "spa_max_replication_override",
 316                 "spa_mode_global",
 317                 "zfs_flags",
 318                 "zfs_txg_synctime_ms",
 319                 "zfs_txg_timeout",
 320                 "zfs_write_limit_min",
 321                 "zfs_write_limit_max",
 322                 "zfs_write_limit_shift",
 323                 "zfs_write_limit_override",
 324                 "zfs_no_write_throttle",
 325                 "zfs_vdev_cache_max",
 326                 "zfs_vdev_cache_size",
 327                 "zfs_vdev_cache_bshift",
 328                 "vdev_mirror_shift",
 329                 "zfs_vdev_max_pending",
 330                 "zfs_vdev_min_pending",
 331                 "zfs_scrub_limit",
 332                 "zfs_no_scrub_io",
 333                 "zfs_no_scrub_prefetch",
 334                 "zfs_vdev_time_shift",
 335                 "zfs_vdev_ramp_rate",
 336                 "zfs_vdev_aggregation_limit",
 337                 "fzap_default_block_shift",
 338                 "zfs_immediate_write_sz",
 339                 "zfs_read_chunk_size",
 340                 "zfs_nocacheflush",
 341                 "zil_replay_disable",
 342                 "metaslab_gang_bang",
 343                 "metaslab_df_alloc_threshold",
 344                 "metaslab_df_free_pct",
 345                 "zio_injection_enabled",
 346                 "zvol_immediate_write_sz",
 347         };
 348 
 349         for (int i = 0; i < sizeof (params) / sizeof (params[0]); i++) {
 350                 int sz;
 351                 uint64_t val64;
 352                 uint32_t *val32p = (uint32_t *)&val64;
 353 
 354                 sz = mdb_readvar(&val64, params[i]);
 355                 if (sz == 4) {
 356                         mdb_printf("%s = 0x%x\n", params[i], *val32p);
 357                 } else if (sz == 8) {
 358                         mdb_printf("%s = 0x%llx\n", params[i], val64);
 359                 } else {
 360                         mdb_warn("variable %s not found", params[i]);
 361                 }
 362         }
 363 
 364         return (DCMD_OK);
 365 }
 366 
 367 /* ARGSUSED */
 368 static int
 369 blkptr(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
 370 {
 371         mdb_ctf_id_t type_enum, checksum_enum, compress_enum;
 372         char type[80], checksum[80], compress[80];
 373         blkptr_t blk, *bp = &blk;
 374         char buf[BP_SPRINTF_LEN];
 375 
 376         if (mdb_vread(&blk, sizeof (blkptr_t), addr) == -1) {
 377                 mdb_warn("failed to read blkptr_t");
 378                 return (DCMD_ERR);
 379         }
 380 
 381         if (mdb_ctf_lookup_by_name("enum dmu_object_type", &type_enum) == -1 ||
 382             mdb_ctf_lookup_by_name("enum zio_checksum", &checksum_enum) == -1 ||
 383             mdb_ctf_lookup_by_name("enum zio_compress", &compress_enum) == -1) {
 384                 mdb_warn("Could not find blkptr enumerated types");
 385                 return (DCMD_ERR);
 386         }
 387 
 388         enum_lookup(type, sizeof (type), type_enum,
 389             BP_GET_TYPE(bp), "DMU_OT_");
 390         enum_lookup(checksum, sizeof (checksum), checksum_enum,
 391             BP_GET_CHECKSUM(bp), "ZIO_CHECKSUM_");
 392         enum_lookup(compress, sizeof (compress), compress_enum,
 393             BP_GET_COMPRESS(bp), "ZIO_COMPRESS_");
 394 
 395         SPRINTF_BLKPTR(mdb_snprintf, '\n', buf, bp, type, checksum, compress);
 396 
 397         mdb_printf("%s\n", buf);
 398 
 399         return (DCMD_OK);
 400 }
 401 
 402 /* ARGSUSED */
 403 static int
 404 dbuf(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
 405 {
 406         mdb_ctf_id_t id;
 407         dmu_buf_t db;
 408         uintptr_t objset;
 409         uint8_t level;
 410         uint64_t blkid;
 411         uint64_t holds;
 412         char objectname[32];
 413         char blkidname[32];
 414         char path[MAXNAMELEN];
 415 
 416         if (DCMD_HDRSPEC(flags)) {
 417                 mdb_printf("        addr object lvl blkid holds os\n");
 418         }
 419 
 420         if (mdb_ctf_lookup_by_name("struct dmu_buf_impl", &id) == -1) {
 421                 mdb_warn("couldn't find struct dmu_buf_impl_t");
 422                 return (DCMD_ERR);
 423         }
 424 
 425         if (GETMEMBID(addr, &id, db_objset, objset) ||
 426             GETMEMBID(addr, &id, db, db) ||
 427             GETMEMBID(addr, &id, db_level, level) ||
 428             GETMEMBID(addr, &id, db_blkid, blkid)) {
 429                 return (WALK_ERR);
 430         }
 431 
 432         if (getrefcount(addr, &id, "db_holds", &holds)) {
 433                 return (WALK_ERR);
 434         }
 435 
 436         if (db.db_object == DMU_META_DNODE_OBJECT)
 437                 (void) strcpy(objectname, "mdn");
 438         else
 439                 (void) mdb_snprintf(objectname, sizeof (objectname), "%llx",
 440                     (u_longlong_t)db.db_object);
 441 
 442         if (blkid == DMU_BONUS_BLKID)
 443                 (void) strcpy(blkidname, "bonus");
 444         else
 445                 (void) mdb_snprintf(blkidname, sizeof (blkidname), "%llx",
 446                     (u_longlong_t)blkid);
 447 
 448         if (objset_name(objset, path)) {
 449                 return (WALK_ERR);
 450         }
 451 
 452         mdb_printf("%p %8s %1u %9s %2llu %s\n",
 453             addr, objectname, level, blkidname, holds, path);
 454 
 455         return (DCMD_OK);
 456 }
 457 
 458 /* ARGSUSED */
 459 static int
 460 dbuf_stats(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
 461 {
 462 #define HISTOSZ 32
 463         uintptr_t dbp;
 464         dmu_buf_impl_t db;
 465         dbuf_hash_table_t ht;
 466         uint64_t bucket, ndbufs;
 467         uint64_t histo[HISTOSZ];
 468         uint64_t histo2[HISTOSZ];
 469         int i, maxidx;
 470 
 471         if (mdb_readvar(&ht, "dbuf_hash_table") == -1) {
 472                 mdb_warn("failed to read 'dbuf_hash_table'");
 473                 return (DCMD_ERR);
 474         }
 475 
 476         for (i = 0; i < HISTOSZ; i++) {
 477                 histo[i] = 0;
 478                 histo2[i] = 0;
 479         }
 480 
 481         ndbufs = 0;
 482         for (bucket = 0; bucket < ht.hash_table_mask+1; bucket++) {
 483                 int len;
 484 
 485                 if (mdb_vread(&dbp, sizeof (void *),
 486                     (uintptr_t)(ht.hash_table+bucket)) == -1) {
 487                         mdb_warn("failed to read hash bucket %u at %p",
 488                             bucket, ht.hash_table+bucket);
 489                         return (DCMD_ERR);
 490                 }
 491 
 492                 len = 0;
 493                 while (dbp != 0) {
 494                         if (mdb_vread(&db, sizeof (dmu_buf_impl_t),
 495                             dbp) == -1) {
 496                                 mdb_warn("failed to read dbuf at %p", dbp);
 497                                 return (DCMD_ERR);
 498                         }
 499                         dbp = (uintptr_t)db.db_hash_next;
 500                         for (i = MIN(len, HISTOSZ - 1); i >= 0; i--)
 501                                 histo2[i]++;
 502                         len++;
 503                         ndbufs++;
 504                 }
 505 
 506                 if (len >= HISTOSZ)
 507                         len = HISTOSZ-1;
 508                 histo[len]++;
 509         }
 510 
 511         mdb_printf("hash table has %llu buckets, %llu dbufs "
 512             "(avg %llu buckets/dbuf)\n",
 513             ht.hash_table_mask+1, ndbufs,
 514             (ht.hash_table_mask+1)/ndbufs);
 515 
 516         mdb_printf("\n");
 517         maxidx = 0;
 518         for (i = 0; i < HISTOSZ; i++)
 519                 if (histo[i] > 0)
 520                         maxidx = i;
 521         mdb_printf("hash chain length   number of buckets\n");
 522         for (i = 0; i <= maxidx; i++)
 523                 mdb_printf("%u                  %llu\n", i, histo[i]);
 524 
 525         mdb_printf("\n");
 526         maxidx = 0;
 527         for (i = 0; i < HISTOSZ; i++)
 528                 if (histo2[i] > 0)
 529                         maxidx = i;
 530         mdb_printf("hash chain depth    number of dbufs\n");
 531         for (i = 0; i <= maxidx; i++)
 532                 mdb_printf("%u or more          %llu    %llu%%\n",
 533                     i, histo2[i], histo2[i]*100/ndbufs);
 534 
 535 
 536         return (DCMD_OK);
 537 }
 538 
 539 #define CHAIN_END 0xffff
 540 /*
 541  * ::zap_leaf [-v]
 542  *
 543  * Print a zap_leaf_phys_t, assumed to be 16k
 544  */
 545 /* ARGSUSED */
 546 static int
 547 zap_leaf(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
 548 {
 549         char buf[16*1024];
 550         int verbose = B_FALSE;
 551         int four = B_FALSE;
 552         zap_leaf_t l;
 553         zap_leaf_phys_t *zlp = (void *)buf;
 554         int i;
 555 
 556         if (mdb_getopts(argc, argv,
 557             'v', MDB_OPT_SETBITS, TRUE, &verbose,
 558             '4', MDB_OPT_SETBITS, TRUE, &four,
 559             NULL) != argc)
 560                 return (DCMD_USAGE);
 561 
 562         l.l_phys = zlp;
 563         l.l_bs = 14; /* assume 16k blocks */
 564         if (four)
 565                 l.l_bs = 12;
 566 
 567         if (!(flags & DCMD_ADDRSPEC)) {
 568                 return (DCMD_USAGE);
 569         }
 570 
 571         if (mdb_vread(buf, sizeof (buf), addr) == -1) {
 572                 mdb_warn("failed to read zap_leaf_phys_t at %p", addr);
 573                 return (DCMD_ERR);
 574         }
 575 
 576         if (zlp->l_hdr.lh_block_type != ZBT_LEAF ||
 577             zlp->l_hdr.lh_magic != ZAP_LEAF_MAGIC) {
 578                 mdb_warn("This does not appear to be a zap_leaf_phys_t");
 579                 return (DCMD_ERR);
 580         }
 581 
 582         mdb_printf("zap_leaf_phys_t at %p:\n", addr);
 583         mdb_printf("    lh_prefix_len = %u\n", zlp->l_hdr.lh_prefix_len);
 584         mdb_printf("    lh_prefix = %llx\n", zlp->l_hdr.lh_prefix);
 585         mdb_printf("    lh_nentries = %u\n", zlp->l_hdr.lh_nentries);
 586         mdb_printf("    lh_nfree = %u\n", zlp->l_hdr.lh_nfree,
 587             zlp->l_hdr.lh_nfree * 100 / (ZAP_LEAF_NUMCHUNKS(&l)));
 588         mdb_printf("    lh_freelist = %u\n", zlp->l_hdr.lh_freelist);
 589         mdb_printf("    lh_flags = %x (%s)\n", zlp->l_hdr.lh_flags,
 590             zlp->l_hdr.lh_flags & ZLF_ENTRIES_CDSORTED ?
 591             "ENTRIES_CDSORTED" : "");
 592 
 593         if (verbose) {
 594                 mdb_printf(" hash table:\n");
 595                 for (i = 0; i < ZAP_LEAF_HASH_NUMENTRIES(&l); i++) {
 596                         if (zlp->l_hash[i] != CHAIN_END)
 597                                 mdb_printf("    %u: %u\n", i, zlp->l_hash[i]);
 598                 }
 599         }
 600 
 601         mdb_printf(" chunks:\n");
 602         for (i = 0; i < ZAP_LEAF_NUMCHUNKS(&l); i++) {
 603                 /* LINTED: alignment */
 604                 zap_leaf_chunk_t *zlc = &ZAP_LEAF_CHUNK(&l, i);
 605                 switch (zlc->l_entry.le_type) {
 606                 case ZAP_CHUNK_FREE:
 607                         if (verbose) {
 608                                 mdb_printf("    %u: free; lf_next = %u\n",
 609                                     i, zlc->l_free.lf_next);
 610                         }
 611                         break;
 612                 case ZAP_CHUNK_ENTRY:
 613                         mdb_printf("    %u: entry\n", i);
 614                         if (verbose) {
 615                                 mdb_printf("        le_next = %u\n",
 616                                     zlc->l_entry.le_next);
 617                         }
 618                         mdb_printf("        le_name_chunk = %u\n",
 619                             zlc->l_entry.le_name_chunk);
 620                         mdb_printf("        le_name_numints = %u\n",
 621                             zlc->l_entry.le_name_numints);
 622                         mdb_printf("        le_value_chunk = %u\n",
 623                             zlc->l_entry.le_value_chunk);
 624                         mdb_printf("        le_value_intlen = %u\n",
 625                             zlc->l_entry.le_value_intlen);
 626                         mdb_printf("        le_value_numints = %u\n",
 627                             zlc->l_entry.le_value_numints);
 628                         mdb_printf("        le_cd = %u\n",
 629                             zlc->l_entry.le_cd);
 630                         mdb_printf("        le_hash = %llx\n",
 631                             zlc->l_entry.le_hash);
 632                         break;
 633                 case ZAP_CHUNK_ARRAY:
 634                         mdb_printf("    %u: array", i);
 635                         if (strisprint((char *)zlc->l_array.la_array))
 636                                 mdb_printf(" \"%s\"", zlc->l_array.la_array);
 637                         mdb_printf("\n");
 638                         if (verbose) {
 639                                 int j;
 640                                 mdb_printf("        ");
 641                                 for (j = 0; j < ZAP_LEAF_ARRAY_BYTES; j++) {
 642                                         mdb_printf("%02x ",
 643                                             zlc->l_array.la_array[j]);
 644                                 }
 645                                 mdb_printf("\n");
 646                         }
 647                         if (zlc->l_array.la_next != CHAIN_END) {
 648                                 mdb_printf("        lf_next = %u\n",
 649                                     zlc->l_array.la_next);
 650                         }
 651                         break;
 652                 default:
 653                         mdb_printf("    %u: undefined type %u\n",
 654                             zlc->l_entry.le_type);
 655                 }
 656         }
 657 
 658         return (DCMD_OK);
 659 }
 660 
 661 typedef struct dbufs_data {
 662         mdb_ctf_id_t id;
 663         uint64_t objset;
 664         uint64_t object;
 665         uint64_t level;
 666         uint64_t blkid;
 667         char *osname;
 668 } dbufs_data_t;
 669 
 670 #define DBUFS_UNSET     (0xbaddcafedeadbeefULL)
 671 
 672 /* ARGSUSED */
 673 static int
 674 dbufs_cb(uintptr_t addr, const void *unknown, void *arg)
 675 {
 676         dbufs_data_t *data = arg;
 677         uintptr_t objset;
 678         dmu_buf_t db;
 679         uint8_t level;
 680         uint64_t blkid;
 681         char osname[MAXNAMELEN];
 682 
 683         if (GETMEMBID(addr, &data->id, db_objset, objset) ||
 684             GETMEMBID(addr, &data->id, db, db) ||
 685             GETMEMBID(addr, &data->id, db_level, level) ||
 686             GETMEMBID(addr, &data->id, db_blkid, blkid)) {
 687                 return (WALK_ERR);
 688         }
 689 
 690         if ((data->objset == DBUFS_UNSET || data->objset == objset) &&
 691             (data->osname == NULL || (objset_name(objset, osname) == 0 &&
 692             strcmp(data->osname, osname) == 0)) &&
 693             (data->object == DBUFS_UNSET || data->object == db.db_object) &&
 694             (data->level == DBUFS_UNSET || data->level == level) &&
 695             (data->blkid == DBUFS_UNSET || data->blkid == blkid)) {
 696                 mdb_printf("%#lr\n", addr);
 697         }
 698         return (WALK_NEXT);
 699 }
 700 
 701 /* ARGSUSED */
 702 static int
 703 dbufs(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
 704 {
 705         dbufs_data_t data;
 706         char *object = NULL;
 707         char *blkid = NULL;
 708 
 709         data.objset = data.object = data.level = data.blkid = DBUFS_UNSET;
 710         data.osname = NULL;
 711 
 712         if (mdb_getopts(argc, argv,
 713             'O', MDB_OPT_UINT64, &data.objset,
 714             'n', MDB_OPT_STR, &data.osname,
 715             'o', MDB_OPT_STR, &object,
 716             'l', MDB_OPT_UINT64, &data.level,
 717             'b', MDB_OPT_STR, &blkid) != argc) {
 718                 return (DCMD_USAGE);
 719         }
 720 
 721         if (object) {
 722                 if (strcmp(object, "mdn") == 0) {
 723                         data.object = DMU_META_DNODE_OBJECT;
 724                 } else {
 725                         data.object = mdb_strtoull(object);
 726                 }
 727         }
 728 
 729         if (blkid) {
 730                 if (strcmp(blkid, "bonus") == 0) {
 731                         data.blkid = DMU_BONUS_BLKID;
 732                 } else {
 733                         data.blkid = mdb_strtoull(blkid);
 734                 }
 735         }
 736 
 737         if (mdb_ctf_lookup_by_name("struct dmu_buf_impl", &data.id) == -1) {
 738                 mdb_warn("couldn't find struct dmu_buf_impl_t");
 739                 return (DCMD_ERR);
 740         }
 741 
 742         if (mdb_walk("dmu_buf_impl_t", dbufs_cb, &data) != 0) {
 743                 mdb_warn("can't walk dbufs");
 744                 return (DCMD_ERR);
 745         }
 746 
 747         return (DCMD_OK);
 748 }
 749 
 750 typedef struct abuf_find_data {
 751         dva_t dva;
 752         mdb_ctf_id_t id;
 753 } abuf_find_data_t;
 754 
 755 /* ARGSUSED */
 756 static int
 757 abuf_find_cb(uintptr_t addr, const void *unknown, void *arg)
 758 {
 759         abuf_find_data_t *data = arg;
 760         dva_t dva;
 761 
 762         if (GETMEMBID(addr, &data->id, b_dva, dva)) {
 763                 return (WALK_ERR);
 764         }
 765 
 766         if (dva.dva_word[0] == data->dva.dva_word[0] &&
 767             dva.dva_word[1] == data->dva.dva_word[1]) {
 768                 mdb_printf("%#lr\n", addr);
 769         }
 770         return (WALK_NEXT);
 771 }
 772 
 773 /* ARGSUSED */
 774 static int
 775 abuf_find(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
 776 {
 777         abuf_find_data_t data;
 778         GElf_Sym sym;
 779         int i;
 780         const char *syms[] = {
 781                 "ARC_mru",
 782                 "ARC_mru_ghost",
 783                 "ARC_mfu",
 784                 "ARC_mfu_ghost",
 785         };
 786 
 787         if (argc != 2)
 788                 return (DCMD_USAGE);
 789 
 790         for (i = 0; i < 2; i ++) {
 791                 switch (argv[i].a_type) {
 792                 case MDB_TYPE_STRING:
 793                         data.dva.dva_word[i] = mdb_strtoull(argv[i].a_un.a_str);
 794                         break;
 795                 case MDB_TYPE_IMMEDIATE:
 796                         data.dva.dva_word[i] = argv[i].a_un.a_val;
 797                         break;
 798                 default:
 799                         return (DCMD_USAGE);
 800                 }
 801         }
 802 
 803         if (mdb_ctf_lookup_by_name("struct arc_buf_hdr", &data.id) == -1) {
 804                 mdb_warn("couldn't find struct arc_buf_hdr");
 805                 return (DCMD_ERR);
 806         }
 807 
 808         for (i = 0; i < sizeof (syms) / sizeof (syms[0]); i++) {
 809                 if (mdb_lookup_by_name(syms[i], &sym)) {
 810                         mdb_warn("can't find symbol %s", syms[i]);
 811                         return (DCMD_ERR);
 812                 }
 813 
 814                 if (mdb_pwalk("list", abuf_find_cb, &data, sym.st_value) != 0) {
 815                         mdb_warn("can't walk %s", syms[i]);
 816                         return (DCMD_ERR);
 817                 }
 818         }
 819 
 820         return (DCMD_OK);
 821 }
 822 
 823 /* ARGSUSED */
 824 static int
 825 dbgmsg_cb(uintptr_t addr, const void *unknown, void *arg)
 826 {
 827         static mdb_ctf_id_t id;
 828         static boolean_t gotid;
 829         static ulong_t off;
 830 
 831         int *verbosep = arg;
 832         time_t timestamp;
 833         char buf[1024];
 834 
 835         if (!gotid) {
 836                 if (mdb_ctf_lookup_by_name("struct zfs_dbgmsg", &id) == -1) {
 837                         mdb_warn("couldn't find struct zfs_dbgmsg");
 838                         return (WALK_ERR);
 839                 }
 840                 gotid = TRUE;
 841                 if (mdb_ctf_offsetof(id, "zdm_msg", &off) == -1) {
 842                         mdb_warn("couldn't find zdm_msg");
 843                         return (WALK_ERR);
 844                 }
 845                 off /= 8;
 846         }
 847 
 848 
 849         if (GETMEMBID(addr, &id, zdm_timestamp, timestamp)) {
 850                 return (WALK_ERR);
 851         }
 852 
 853         if (mdb_readstr(buf, sizeof (buf), addr + off) == -1) {
 854                 mdb_warn("failed to read zdm_msg at %p\n", addr + off);
 855                 return (DCMD_ERR);
 856         }
 857 
 858         if (*verbosep)
 859                 mdb_printf("%Y ", timestamp);
 860 
 861         mdb_printf("%s\n", buf);
 862 
 863         if (*verbosep)
 864                 (void) mdb_call_dcmd("whatis", addr, DCMD_ADDRSPEC, 0, NULL);
 865 
 866         return (WALK_NEXT);
 867 }
 868 
 869 /* ARGSUSED */
 870 static int
 871 dbgmsg(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
 872 {
 873         GElf_Sym sym;
 874         int verbose = FALSE;
 875 
 876         if (mdb_getopts(argc, argv,
 877             'v', MDB_OPT_SETBITS, TRUE, &verbose,
 878             NULL) != argc)
 879                 return (DCMD_USAGE);
 880 
 881         if (mdb_lookup_by_name("zfs_dbgmsgs", &sym)) {
 882                 mdb_warn("can't find zfs_dbgmsgs");
 883                 return (DCMD_ERR);
 884         }
 885 
 886         if (mdb_pwalk("list", dbgmsg_cb, &verbose, sym.st_value) != 0) {
 887                 mdb_warn("can't walk zfs_dbgmsgs");
 888                 return (DCMD_ERR);
 889         }
 890 
 891         return (DCMD_OK);
 892 }
 893 
 894 /*ARGSUSED*/
 895 static int
 896 arc_print(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
 897 {
 898         kstat_named_t *stats;
 899         GElf_Sym sym;
 900         int nstats, i;
 901         uint_t opt_a = FALSE;
 902         uint_t opt_b = FALSE;
 903         uint_t shift = 0;
 904         const char *suffix;
 905 
 906         static const char *bytestats[] = {
 907                 "p", "c", "c_min", "c_max", "size", NULL
 908         };
 909 
 910         static const char *extras[] = {
 911                 "arc_no_grow", "arc_tempreserve",
 912                 "arc_meta_used", "arc_meta_limit", "arc_meta_max",
 913                 NULL
 914         };
 915 
 916         if (mdb_lookup_by_name("arc_stats", &sym) == -1) {
 917                 mdb_warn("failed to find 'arc_stats'");
 918                 return (DCMD_ERR);
 919         }
 920 
 921         stats = mdb_zalloc(sym.st_size, UM_SLEEP | UM_GC);
 922 
 923         if (mdb_vread(stats, sym.st_size, sym.st_value) == -1) {
 924                 mdb_warn("couldn't read 'arc_stats' at %p", sym.st_value);
 925                 return (DCMD_ERR);
 926         }
 927 
 928         nstats = sym.st_size / sizeof (kstat_named_t);
 929 
 930         /* NB: -a / opt_a are ignored for backwards compatability */
 931         if (mdb_getopts(argc, argv,
 932             'a', MDB_OPT_SETBITS, TRUE, &opt_a,
 933             'b', MDB_OPT_SETBITS, TRUE, &opt_b,
 934             'k', MDB_OPT_SETBITS, 10, &shift,
 935             'm', MDB_OPT_SETBITS, 20, &shift,
 936             'g', MDB_OPT_SETBITS, 30, &shift,
 937             NULL) != argc)
 938                 return (DCMD_USAGE);
 939 
 940         if (!opt_b && !shift)
 941                 shift = 20;
 942 
 943         switch (shift) {
 944         case 0:
 945                 suffix = "B";
 946                 break;
 947         case 10:
 948                 suffix = "KB";
 949                 break;
 950         case 20:
 951                 suffix = "MB";
 952                 break;
 953         case 30:
 954                 suffix = "GB";
 955                 break;
 956         default:
 957                 suffix = "XX";
 958         }
 959 
 960         for (i = 0; i < nstats; i++) {
 961                 int j;
 962                 boolean_t bytes = B_FALSE;
 963 
 964                 for (j = 0; bytestats[j]; j++) {
 965                         if (strcmp(stats[i].name, bytestats[j]) == 0) {
 966                                 bytes = B_TRUE;
 967                                 break;
 968                         }
 969                 }
 970 
 971                 if (bytes) {
 972                         mdb_printf("%-25s = %9llu %s\n", stats[i].name,
 973                             stats[i].value.ui64 >> shift, suffix);
 974                 } else {
 975                         mdb_printf("%-25s = %9llu\n", stats[i].name,
 976                             stats[i].value.ui64);
 977                 }
 978         }
 979 
 980         for (i = 0; extras[i]; i++) {
 981                 uint64_t buf;
 982 
 983                 if (mdb_lookup_by_name(extras[i], &sym) == -1) {
 984                         mdb_warn("failed to find '%s'", extras[i]);
 985                         return (DCMD_ERR);
 986                 }
 987 
 988                 if (sym.st_size != sizeof (uint64_t) &&
 989                     sym.st_size != sizeof (uint32_t)) {
 990                         mdb_warn("expected scalar for variable '%s'\n",
 991                             extras[i]);
 992                         return (DCMD_ERR);
 993                 }
 994 
 995                 if (mdb_vread(&buf, sym.st_size, sym.st_value) == -1) {
 996                         mdb_warn("couldn't read '%s'", extras[i]);
 997                         return (DCMD_ERR);
 998                 }
 999 
1000                 mdb_printf("%-25s = ", extras[i]);
1001 
1002                 /* NB: all the 64-bit extras happen to be byte counts */
1003                 if (sym.st_size == sizeof (uint64_t))
1004                         mdb_printf("%9llu %s\n", buf >> shift, suffix);
1005 
1006                 if (sym.st_size == sizeof (uint32_t))
1007                         mdb_printf("%9d\n", *((uint32_t *)&buf));
1008         }
1009         return (DCMD_OK);
1010 }
1011 
1012 /*
1013  * ::spa
1014  *
1015  *      -c      Print configuration information as well
1016  *      -v      Print vdev state
1017  *      -e      Print vdev error stats
1018  *
1019  * Print a summarized spa_t.  When given no arguments, prints out a table of all
1020  * active pools on the system.
1021  */
1022 /* ARGSUSED */
1023 static int
1024 spa_print(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1025 {
1026         spa_t spa;
1027         const char *statetab[] = { "ACTIVE", "EXPORTED", "DESTROYED",
1028                 "SPARE", "L2CACHE", "UNINIT", "UNAVAIL", "POTENTIAL" };
1029         const char *state;
1030         int config = FALSE;
1031         int vdevs = FALSE;
1032         int errors = FALSE;
1033 
1034         if (mdb_getopts(argc, argv,
1035             'c', MDB_OPT_SETBITS, TRUE, &config,
1036             'v', MDB_OPT_SETBITS, TRUE, &vdevs,
1037             'e', MDB_OPT_SETBITS, TRUE, &errors,
1038             NULL) != argc)
1039                 return (DCMD_USAGE);
1040 
1041         if (!(flags & DCMD_ADDRSPEC)) {
1042                 if (mdb_walk_dcmd("spa", "spa", argc, argv) == -1) {
1043                         mdb_warn("can't walk spa");
1044                         return (DCMD_ERR);
1045                 }
1046 
1047                 return (DCMD_OK);
1048         }
1049 
1050         if (flags & DCMD_PIPE_OUT) {
1051                 mdb_printf("%#lr\n", addr);
1052                 return (DCMD_OK);
1053         }
1054 
1055         if (DCMD_HDRSPEC(flags))
1056                 mdb_printf("%<u>%-?s %9s %-*s%</u>\n", "ADDR", "STATE",
1057                     sizeof (uintptr_t) == 4 ? 60 : 52, "NAME");
1058 
1059         if (mdb_vread(&spa, sizeof (spa), addr) == -1) {
1060                 mdb_warn("failed to read spa_t at %p", addr);
1061                 return (DCMD_ERR);
1062         }
1063 
1064         if (spa.spa_state < 0 || spa.spa_state > POOL_STATE_UNAVAIL)
1065                 state = "UNKNOWN";
1066         else
1067                 state = statetab[spa.spa_state];
1068 
1069         mdb_printf("%0?p %9s %s\n", addr, state, spa.spa_name);
1070 
1071         if (config) {
1072                 mdb_printf("\n");
1073                 mdb_inc_indent(4);
1074                 if (mdb_call_dcmd("spa_config", addr, flags, 0,
1075                     NULL) != DCMD_OK)
1076                         return (DCMD_ERR);
1077                 mdb_dec_indent(4);
1078         }
1079 
1080         if (vdevs || errors) {
1081                 mdb_arg_t v;
1082 
1083                 v.a_type = MDB_TYPE_STRING;
1084                 v.a_un.a_str = "-e";
1085 
1086                 mdb_printf("\n");
1087                 mdb_inc_indent(4);
1088                 if (mdb_call_dcmd("spa_vdevs", addr, flags, errors ? 1 : 0,
1089                     &v) != DCMD_OK)
1090                         return (DCMD_ERR);
1091                 mdb_dec_indent(4);
1092         }
1093 
1094         return (DCMD_OK);
1095 }
1096 
1097 /*
1098  * ::spa_config
1099  *
1100  * Given a spa_t, print the configuration information stored in spa_config.
1101  * Since it's just an nvlist, format it as an indented list of name=value pairs.
1102  * We simply read the value of spa_config and pass off to ::nvlist.
1103  */
1104 /* ARGSUSED */
1105 static int
1106 spa_print_config(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1107 {
1108         spa_t spa;
1109 
1110         if (argc != 0 || !(flags & DCMD_ADDRSPEC))
1111                 return (DCMD_USAGE);
1112 
1113         if (mdb_vread(&spa, sizeof (spa), addr) == -1) {
1114                 mdb_warn("failed to read spa_t at %p", addr);
1115                 return (DCMD_ERR);
1116         }
1117 
1118         if (spa.spa_config == NULL) {
1119                 mdb_printf("(none)\n");
1120                 return (DCMD_OK);
1121         }
1122 
1123         return (mdb_call_dcmd("nvlist", (uintptr_t)spa.spa_config, flags,
1124             0, NULL));
1125 }
1126 
1127 /*
1128  * ::vdev
1129  *
1130  * Print out a summarized vdev_t, in the following form:
1131  *
1132  * ADDR             STATE       AUX            DESC
1133  * fffffffbcde23df0 HEALTHY     -              /dev/dsk/c0t0d0
1134  *
1135  * If '-r' is specified, recursively visit all children.
1136  *
1137  * With '-e', the statistics associated with the vdev are printed as well.
1138  */
1139 static int
1140 do_print_vdev(uintptr_t addr, int flags, int depth, int stats,
1141     int recursive)
1142 {
1143         vdev_t vdev;
1144         char desc[MAXNAMELEN];
1145         int c, children;
1146         uintptr_t *child;
1147         const char *state, *aux;
1148 
1149         if (mdb_vread(&vdev, sizeof (vdev), (uintptr_t)addr) == -1) {
1150                 mdb_warn("failed to read vdev_t at %p\n", (uintptr_t)addr);
1151                 return (DCMD_ERR);
1152         }
1153 
1154         if (flags & DCMD_PIPE_OUT) {
1155                 mdb_printf("%#lr", addr);
1156         } else {
1157                 if (vdev.vdev_path != NULL) {
1158                         if (mdb_readstr(desc, sizeof (desc),
1159                             (uintptr_t)vdev.vdev_path) == -1) {
1160                                 mdb_warn("failed to read vdev_path at %p\n",
1161                                     vdev.vdev_path);
1162                                 return (DCMD_ERR);
1163                         }
1164                 } else if (vdev.vdev_ops != NULL) {
1165                         vdev_ops_t ops;
1166                         if (mdb_vread(&ops, sizeof (ops),
1167                             (uintptr_t)vdev.vdev_ops) == -1) {
1168                                 mdb_warn("failed to read vdev_ops at %p\n",
1169                                     vdev.vdev_ops);
1170                                 return (DCMD_ERR);
1171                         }
1172                         (void) strcpy(desc, ops.vdev_op_type);
1173                 } else {
1174                         (void) strcpy(desc, "<unknown>");
1175                 }
1176 
1177                 if (depth == 0 && DCMD_HDRSPEC(flags))
1178                         mdb_printf("%<u>%-?s %-9s %-12s %-*s%</u>\n",
1179                             "ADDR", "STATE", "AUX",
1180                             sizeof (uintptr_t) == 4 ? 43 : 35,
1181                             "DESCRIPTION");
1182 
1183                 mdb_printf("%0?p ", addr);
1184 
1185                 switch (vdev.vdev_state) {
1186                 case VDEV_STATE_CLOSED:
1187                         state = "CLOSED";
1188                         break;
1189                 case VDEV_STATE_OFFLINE:
1190                         state = "OFFLINE";
1191                         break;
1192                 case VDEV_STATE_CANT_OPEN:
1193                         state = "CANT_OPEN";
1194                         break;
1195                 case VDEV_STATE_DEGRADED:
1196                         state = "DEGRADED";
1197                         break;
1198                 case VDEV_STATE_HEALTHY:
1199                         state = "HEALTHY";
1200                         break;
1201                 case VDEV_STATE_REMOVED:
1202                         state = "REMOVED";
1203                         break;
1204                 case VDEV_STATE_FAULTED:
1205                         state = "FAULTED";
1206                         break;
1207                 default:
1208                         state = "UNKNOWN";
1209                         break;
1210                 }
1211 
1212                 switch (vdev.vdev_stat.vs_aux) {
1213                 case VDEV_AUX_NONE:
1214                         aux = "-";
1215                         break;
1216                 case VDEV_AUX_OPEN_FAILED:
1217                         aux = "OPEN_FAILED";
1218                         break;
1219                 case VDEV_AUX_CORRUPT_DATA:
1220                         aux = "CORRUPT_DATA";
1221                         break;
1222                 case VDEV_AUX_NO_REPLICAS:
1223                         aux = "NO_REPLICAS";
1224                         break;
1225                 case VDEV_AUX_BAD_GUID_SUM:
1226                         aux = "BAD_GUID_SUM";
1227                         break;
1228                 case VDEV_AUX_TOO_SMALL:
1229                         aux = "TOO_SMALL";
1230                         break;
1231                 case VDEV_AUX_BAD_LABEL:
1232                         aux = "BAD_LABEL";
1233                         break;
1234                 case VDEV_AUX_VERSION_NEWER:
1235                         aux = "VERS_NEWER";
1236                         break;
1237                 case VDEV_AUX_VERSION_OLDER:
1238                         aux = "VERS_OLDER";
1239                         break;
1240                 case VDEV_AUX_SPARED:
1241                         aux = "SPARED";
1242                         break;
1243                 case VDEV_AUX_ERR_EXCEEDED:
1244                         aux = "ERR_EXCEEDED";
1245                         break;
1246                 case VDEV_AUX_IO_FAILURE:
1247                         aux = "IO_FAILURE";
1248                         break;
1249                 case VDEV_AUX_BAD_LOG:
1250                         aux = "BAD_LOG";
1251                         break;
1252                 case VDEV_AUX_EXTERNAL:
1253                         aux = "EXTERNAL";
1254                         break;
1255                 case VDEV_AUX_SPLIT_POOL:
1256                         aux = "SPLIT_POOL";
1257                         break;
1258                 default:
1259                         aux = "UNKNOWN";
1260                         break;
1261                 }
1262 
1263                 mdb_printf("%-9s %-12s %*s%s\n", state, aux, depth, "", desc);
1264 
1265                 if (stats) {
1266                         vdev_stat_t *vs = &vdev.vdev_stat;
1267                         int i;
1268 
1269                         mdb_inc_indent(4);
1270                         mdb_printf("\n");
1271                         mdb_printf("%<u>       %12s %12s %12s %12s "
1272                             "%12s%</u>\n", "READ", "WRITE", "FREE", "CLAIM",
1273                             "IOCTL");
1274                         mdb_printf("OPS     ");
1275                         for (i = 1; i < ZIO_TYPES; i++)
1276                                 mdb_printf("%11#llx%s", vs->vs_ops[i],
1277                                     i == ZIO_TYPES - 1 ? "" : "  ");
1278                         mdb_printf("\n");
1279                         mdb_printf("BYTES   ");
1280                         for (i = 1; i < ZIO_TYPES; i++)
1281                                 mdb_printf("%11#llx%s", vs->vs_bytes[i],
1282                                     i == ZIO_TYPES - 1 ? "" : "  ");
1283 
1284 
1285                         mdb_printf("\n");
1286                         mdb_printf("EREAD    %10#llx\n", vs->vs_read_errors);
1287                         mdb_printf("EWRITE   %10#llx\n", vs->vs_write_errors);
1288                         mdb_printf("ECKSUM   %10#llx\n",
1289                             vs->vs_checksum_errors);
1290                         mdb_dec_indent(4);
1291                 }
1292 
1293                 if (stats)
1294                         mdb_printf("\n");
1295         }
1296 
1297         children = vdev.vdev_children;
1298 
1299         if (children == 0 || !recursive)
1300                 return (DCMD_OK);
1301 
1302         child = mdb_alloc(children * sizeof (void *), UM_SLEEP | UM_GC);
1303         if (mdb_vread(child, children * sizeof (void *),
1304             (uintptr_t)vdev.vdev_child) == -1) {
1305                 mdb_warn("failed to read vdev children at %p", vdev.vdev_child);
1306                 return (DCMD_ERR);
1307         }
1308 
1309         for (c = 0; c < children; c++) {
1310                 if (do_print_vdev(child[c], flags, depth + 2, stats,
1311                     recursive))
1312                         return (DCMD_ERR);
1313         }
1314 
1315         return (DCMD_OK);
1316 }
1317 
1318 static int
1319 vdev_print(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1320 {
1321         int recursive = FALSE;
1322         int stats = FALSE;
1323         uint64_t depth = 0;
1324 
1325         if (mdb_getopts(argc, argv,
1326             'r', MDB_OPT_SETBITS, TRUE, &recursive,
1327             'e', MDB_OPT_SETBITS, TRUE, &stats,
1328             'd', MDB_OPT_UINT64, &depth,
1329             NULL) != argc)
1330                 return (DCMD_USAGE);
1331 
1332         if (!(flags & DCMD_ADDRSPEC)) {
1333                 mdb_warn("no vdev_t address given\n");
1334                 return (DCMD_ERR);
1335         }
1336 
1337         return (do_print_vdev(addr, flags, (int)depth, stats, recursive));
1338 }
1339 
1340 typedef struct metaslab_walk_data {
1341         uint64_t mw_numvdevs;
1342         uintptr_t *mw_vdevs;
1343         int mw_curvdev;
1344         uint64_t mw_nummss;
1345         uintptr_t *mw_mss;
1346         int mw_curms;
1347 } metaslab_walk_data_t;
1348 
1349 static int
1350 metaslab_walk_step(mdb_walk_state_t *wsp)
1351 {
1352         metaslab_walk_data_t *mw = wsp->walk_data;
1353         metaslab_t ms;
1354         uintptr_t msp;
1355 
1356         if (mw->mw_curvdev >= mw->mw_numvdevs)
1357                 return (WALK_DONE);
1358 
1359         if (mw->mw_mss == NULL) {
1360                 uintptr_t mssp;
1361                 uintptr_t vdevp;
1362 
1363                 ASSERT(mw->mw_curms == 0);
1364                 ASSERT(mw->mw_nummss == 0);
1365 
1366                 vdevp = mw->mw_vdevs[mw->mw_curvdev];
1367                 if (GETMEMB(vdevp, struct vdev, vdev_ms, mssp) ||
1368                     GETMEMB(vdevp, struct vdev, vdev_ms_count, mw->mw_nummss)) {
1369                         return (WALK_ERR);
1370                 }
1371 
1372                 mw->mw_mss = mdb_alloc(mw->mw_nummss * sizeof (void*),
1373                     UM_SLEEP | UM_GC);
1374                 if (mdb_vread(mw->mw_mss, mw->mw_nummss * sizeof (void*),
1375                     mssp) == -1) {
1376                         mdb_warn("failed to read vdev_ms at %p", mssp);
1377                         return (WALK_ERR);
1378                 }
1379         }
1380 
1381         if (mw->mw_curms >= mw->mw_nummss) {
1382                 mw->mw_mss = NULL;
1383                 mw->mw_curms = 0;
1384                 mw->mw_nummss = 0;
1385                 mw->mw_curvdev++;
1386                 return (WALK_NEXT);
1387         }
1388 
1389         msp = mw->mw_mss[mw->mw_curms];
1390         if (mdb_vread(&ms, sizeof (metaslab_t), msp) == -1) {
1391                 mdb_warn("failed to read metaslab_t at %p", msp);
1392                 return (WALK_ERR);
1393         }
1394 
1395         mw->mw_curms++;
1396 
1397         return (wsp->walk_callback(msp, &ms, wsp->walk_cbdata));
1398 }
1399 
1400 /* ARGSUSED */
1401 static int
1402 metaslab_walk_init(mdb_walk_state_t *wsp)
1403 {
1404         metaslab_walk_data_t *mw;
1405         uintptr_t root_vdevp;
1406         uintptr_t childp;
1407 
1408         if (wsp->walk_addr == NULL) {
1409                 mdb_warn("must supply address of spa_t\n");
1410                 return (WALK_ERR);
1411         }
1412 
1413         mw = mdb_zalloc(sizeof (metaslab_walk_data_t), UM_SLEEP | UM_GC);
1414 
1415         if (GETMEMB(wsp->walk_addr, struct spa, spa_root_vdev, root_vdevp) ||
1416             GETMEMB(root_vdevp, struct vdev, vdev_children, mw->mw_numvdevs) ||
1417             GETMEMB(root_vdevp, struct vdev, vdev_child, childp)) {
1418                 return (DCMD_ERR);
1419         }
1420 
1421         mw->mw_vdevs = mdb_alloc(mw->mw_numvdevs * sizeof (void *),
1422             UM_SLEEP | UM_GC);
1423         if (mdb_vread(mw->mw_vdevs, mw->mw_numvdevs * sizeof (void *),
1424             childp) == -1) {
1425                 mdb_warn("failed to read root vdev children at %p", childp);
1426                 return (DCMD_ERR);
1427         }
1428 
1429         wsp->walk_data = mw;
1430 
1431         return (WALK_NEXT);
1432 }
1433 
1434 typedef struct mdb_spa {
1435         uintptr_t spa_dsl_pool;
1436         uintptr_t spa_root_vdev;
1437 } mdb_spa_t;
1438 
1439 typedef struct mdb_dsl_dir {
1440         uintptr_t dd_phys;
1441         int64_t dd_space_towrite[TXG_SIZE];
1442 } mdb_dsl_dir_t;
1443 
1444 typedef struct mdb_dsl_dir_phys {
1445         uint64_t dd_used_bytes;
1446         uint64_t dd_compressed_bytes;
1447         uint64_t dd_uncompressed_bytes;
1448 } mdb_dsl_dir_phys_t;
1449 
1450 typedef struct mdb_vdev {
1451         uintptr_t vdev_parent;
1452         uintptr_t vdev_ms;
1453         uint64_t vdev_ms_count;
1454         vdev_stat_t vdev_stat;
1455 } mdb_vdev_t;
1456 
1457 typedef struct mdb_metaslab {
1458         space_map_t ms_allocmap[TXG_SIZE];
1459         space_map_t ms_freemap[TXG_SIZE];
1460         space_map_t ms_map;
1461         space_map_obj_t ms_smo;
1462         space_map_obj_t ms_smo_syncing;
1463 } mdb_metaslab_t;
1464 
1465 typedef struct space_data {
1466         uint64_t ms_allocmap[TXG_SIZE];
1467         uint64_t ms_freemap[TXG_SIZE];
1468         uint64_t ms_map;
1469         uint64_t avail;
1470         uint64_t nowavail;
1471 } space_data_t;
1472 
1473 /* ARGSUSED */
1474 static int
1475 space_cb(uintptr_t addr, const void *unknown, void *arg)
1476 {
1477         space_data_t *sd = arg;
1478         mdb_metaslab_t ms;
1479 
1480         if (GETMEMB(addr, struct metaslab, ms_allocmap, ms.ms_allocmap) ||
1481             GETMEMB(addr, struct metaslab, ms_freemap, ms.ms_freemap) ||
1482             GETMEMB(addr, struct metaslab, ms_map, ms.ms_map) ||
1483             GETMEMB(addr, struct metaslab, ms_smo, ms.ms_smo) ||
1484             GETMEMB(addr, struct metaslab, ms_smo_syncing, ms.ms_smo_syncing)) {
1485                 return (WALK_ERR);
1486         }
1487 
1488         sd->ms_allocmap[0] += ms.ms_allocmap[0].sm_space;
1489         sd->ms_allocmap[1] += ms.ms_allocmap[1].sm_space;
1490         sd->ms_allocmap[2] += ms.ms_allocmap[2].sm_space;
1491         sd->ms_allocmap[3] += ms.ms_allocmap[3].sm_space;
1492         sd->ms_freemap[0] += ms.ms_freemap[0].sm_space;
1493         sd->ms_freemap[1] += ms.ms_freemap[1].sm_space;
1494         sd->ms_freemap[2] += ms.ms_freemap[2].sm_space;
1495         sd->ms_freemap[3] += ms.ms_freemap[3].sm_space;
1496         sd->ms_map += ms.ms_map.sm_space;
1497         sd->avail += ms.ms_map.sm_size - ms.ms_smo.smo_alloc;
1498         sd->nowavail += ms.ms_map.sm_size - ms.ms_smo_syncing.smo_alloc;
1499 
1500         return (WALK_NEXT);
1501 }
1502 
1503 /*
1504  * ::spa_space [-b]
1505  *
1506  * Given a spa_t, print out it's on-disk space usage and in-core
1507  * estimates of future usage.  If -b is given, print space in bytes.
1508  * Otherwise print in megabytes.
1509  */
1510 /* ARGSUSED */
1511 static int
1512 spa_space(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1513 {
1514         mdb_spa_t spa;
1515         uintptr_t dp_root_dir;
1516         mdb_dsl_dir_t dd;
1517         mdb_dsl_dir_phys_t dsp;
1518         uint64_t children;
1519         uintptr_t childaddr;
1520         space_data_t sd;
1521         int shift = 20;
1522         char *suffix = "M";
1523         int bits = FALSE;
1524 
1525         if (mdb_getopts(argc, argv, 'b', MDB_OPT_SETBITS, TRUE, &bits, NULL) !=
1526             argc)
1527                 return (DCMD_USAGE);
1528         if (!(flags & DCMD_ADDRSPEC))
1529                 return (DCMD_USAGE);
1530 
1531         if (bits) {
1532                 shift = 0;
1533                 suffix = "";
1534         }
1535 
1536         if (GETMEMB(addr, struct spa, spa_dsl_pool, spa.spa_dsl_pool) ||
1537             GETMEMB(addr, struct spa, spa_root_vdev, spa.spa_root_vdev) ||
1538             GETMEMB(spa.spa_root_vdev, struct vdev, vdev_children, children) ||
1539             GETMEMB(spa.spa_root_vdev, struct vdev, vdev_child, childaddr) ||
1540             GETMEMB(spa.spa_dsl_pool, struct dsl_pool,
1541             dp_root_dir, dp_root_dir) ||
1542             GETMEMB(dp_root_dir, struct dsl_dir, dd_phys, dd.dd_phys) ||
1543             GETMEMB(dp_root_dir, struct dsl_dir,
1544             dd_space_towrite, dd.dd_space_towrite) ||
1545             GETMEMB(dd.dd_phys, struct dsl_dir_phys,
1546             dd_used_bytes, dsp.dd_used_bytes) ||
1547             GETMEMB(dd.dd_phys, struct dsl_dir_phys,
1548             dd_compressed_bytes, dsp.dd_compressed_bytes) ||
1549             GETMEMB(dd.dd_phys, struct dsl_dir_phys,
1550             dd_uncompressed_bytes, dsp.dd_uncompressed_bytes)) {
1551                 return (DCMD_ERR);
1552         }
1553 
1554         mdb_printf("dd_space_towrite = %llu%s %llu%s %llu%s %llu%s\n",
1555             dd.dd_space_towrite[0] >> shift, suffix,
1556             dd.dd_space_towrite[1] >> shift, suffix,
1557             dd.dd_space_towrite[2] >> shift, suffix,
1558             dd.dd_space_towrite[3] >> shift, suffix);
1559 
1560         mdb_printf("dd_phys.dd_used_bytes = %llu%s\n",
1561             dsp.dd_used_bytes >> shift, suffix);
1562         mdb_printf("dd_phys.dd_compressed_bytes = %llu%s\n",
1563             dsp.dd_compressed_bytes >> shift, suffix);
1564         mdb_printf("dd_phys.dd_uncompressed_bytes = %llu%s\n",
1565             dsp.dd_uncompressed_bytes >> shift, suffix);
1566 
1567         bzero(&sd, sizeof (sd));
1568         if (mdb_pwalk("metaslab", space_cb, &sd, addr) != 0) {
1569                 mdb_warn("can't walk metaslabs");
1570                 return (DCMD_ERR);
1571         }
1572 
1573         mdb_printf("ms_allocmap = %llu%s %llu%s %llu%s %llu%s\n",
1574             sd.ms_allocmap[0] >> shift, suffix,
1575             sd.ms_allocmap[1] >> shift, suffix,
1576             sd.ms_allocmap[2] >> shift, suffix,
1577             sd.ms_allocmap[3] >> shift, suffix);
1578         mdb_printf("ms_freemap = %llu%s %llu%s %llu%s %llu%s\n",
1579             sd.ms_freemap[0] >> shift, suffix,
1580             sd.ms_freemap[1] >> shift, suffix,
1581             sd.ms_freemap[2] >> shift, suffix,
1582             sd.ms_freemap[3] >> shift, suffix);
1583         mdb_printf("ms_map = %llu%s\n", sd.ms_map >> shift, suffix);
1584         mdb_printf("last synced avail = %llu%s\n", sd.avail >> shift, suffix);
1585         mdb_printf("current syncing avail = %llu%s\n",
1586             sd.nowavail >> shift, suffix);
1587 
1588         return (DCMD_OK);
1589 }
1590 
1591 /*
1592  * ::spa_verify
1593  *
1594  * Given a spa_t, verify that that the pool is self-consistent.
1595  * Currently, it only checks to make sure that the vdev tree exists.
1596  */
1597 /* ARGSUSED */
1598 static int
1599 spa_verify(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1600 {
1601         spa_t spa;
1602 
1603         if (argc != 0 || !(flags & DCMD_ADDRSPEC))
1604                 return (DCMD_USAGE);
1605 
1606         if (mdb_vread(&spa, sizeof (spa), addr) == -1) {
1607                 mdb_warn("failed to read spa_t at %p", addr);
1608                 return (DCMD_ERR);
1609         }
1610 
1611         if (spa.spa_root_vdev == NULL) {
1612                 mdb_printf("no vdev tree present\n");
1613                 return (DCMD_OK);
1614         }
1615 
1616         return (DCMD_OK);
1617 }
1618 
1619 static int
1620 spa_print_aux(spa_aux_vdev_t *sav, uint_t flags, mdb_arg_t *v,
1621     const char *name)
1622 {
1623         uintptr_t *aux;
1624         size_t len;
1625         int ret, i;
1626 
1627         /*
1628          * Iterate over aux vdevs and print those out as well.  This is a
1629          * little annoying because we don't have a root vdev to pass to ::vdev.
1630          * Instead, we print a single line and then call it for each child
1631          * vdev.
1632          */
1633         if (sav->sav_count != 0) {
1634                 v[1].a_type = MDB_TYPE_STRING;
1635                 v[1].a_un.a_str = "-d";
1636                 v[2].a_type = MDB_TYPE_IMMEDIATE;
1637                 v[2].a_un.a_val = 2;
1638 
1639                 len = sav->sav_count * sizeof (uintptr_t);
1640                 aux = mdb_alloc(len, UM_SLEEP);
1641                 if (mdb_vread(aux, len,
1642                     (uintptr_t)sav->sav_vdevs) == -1) {
1643                         mdb_free(aux, len);
1644                         mdb_warn("failed to read l2cache vdevs at %p",
1645                             sav->sav_vdevs);
1646                         return (DCMD_ERR);
1647                 }
1648 
1649                 mdb_printf("%-?s %-9s %-12s %s\n", "-", "-", "-", name);
1650 
1651                 for (i = 0; i < sav->sav_count; i++) {
1652                         ret = mdb_call_dcmd("vdev", aux[i], flags, 3, v);
1653                         if (ret != DCMD_OK) {
1654                                 mdb_free(aux, len);
1655                                 return (ret);
1656                         }
1657                 }
1658 
1659                 mdb_free(aux, len);
1660         }
1661 
1662         return (0);
1663 }
1664 
1665 /*
1666  * ::spa_vdevs
1667  *
1668  *      -e      Include error stats
1669  *
1670  * Print out a summarized list of vdevs for the given spa_t.
1671  * This is accomplished by invoking "::vdev -re" on the root vdev, as well as
1672  * iterating over the cache devices.
1673  */
1674 /* ARGSUSED */
1675 static int
1676 spa_vdevs(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1677 {
1678         spa_t spa;
1679         mdb_arg_t v[3];
1680         int errors = FALSE;
1681         int ret;
1682 
1683         if (mdb_getopts(argc, argv,
1684             'e', MDB_OPT_SETBITS, TRUE, &errors,
1685             NULL) != argc)
1686                 return (DCMD_USAGE);
1687 
1688         if (!(flags & DCMD_ADDRSPEC))
1689                 return (DCMD_USAGE);
1690 
1691         if (mdb_vread(&spa, sizeof (spa), addr) == -1) {
1692                 mdb_warn("failed to read spa_t at %p", addr);
1693                 return (DCMD_ERR);
1694         }
1695 
1696         /*
1697          * Unitialized spa_t structures can have a NULL root vdev.
1698          */
1699         if (spa.spa_root_vdev == NULL) {
1700                 mdb_printf("no associated vdevs\n");
1701                 return (DCMD_OK);
1702         }
1703 
1704         v[0].a_type = MDB_TYPE_STRING;
1705         v[0].a_un.a_str = errors ? "-re" : "-r";
1706 
1707         ret = mdb_call_dcmd("vdev", (uintptr_t)spa.spa_root_vdev,
1708             flags, 1, v);
1709         if (ret != DCMD_OK)
1710                 return (ret);
1711 
1712         if (spa_print_aux(&spa.spa_l2cache, flags, v, "cache") != 0 ||
1713             spa_print_aux(&spa.spa_spares, flags, v, "spares") != 0)
1714                 return (DCMD_ERR);
1715 
1716         return (DCMD_OK);
1717 }
1718 
1719 /*
1720  * ::zio
1721  *
1722  * Print a summary of zio_t and all its children.  This is intended to display a
1723  * zio tree, and hence we only pick the most important pieces of information for
1724  * the main summary.  More detailed information can always be found by doing a
1725  * '::print zio' on the underlying zio_t.  The columns we display are:
1726  *
1727  *      ADDRESS         TYPE    STAGE           WAITER
1728  *
1729  * The 'address' column is indented by one space for each depth level as we
1730  * descend down the tree.
1731  */
1732 
1733 #define ZIO_MAXINDENT   24
1734 #define ZIO_MAXWIDTH    (sizeof (uintptr_t) * 2 + ZIO_MAXINDENT)
1735 #define ZIO_WALK_SELF   0
1736 #define ZIO_WALK_CHILD  1
1737 #define ZIO_WALK_PARENT 2
1738 
1739 typedef struct zio_print_args {
1740         int     zpa_current_depth;
1741         int     zpa_min_depth;
1742         int     zpa_max_depth;
1743         int     zpa_type;
1744         uint_t  zpa_flags;
1745 } zio_print_args_t;
1746 
1747 static int zio_child_cb(uintptr_t addr, const void *unknown, void *arg);
1748 
1749 static int
1750 zio_print_cb(uintptr_t addr, const void *data, void *priv)
1751 {
1752         const zio_t *zio = data;
1753         zio_print_args_t *zpa = priv;
1754         mdb_ctf_id_t type_enum, stage_enum;
1755         int indent = zpa->zpa_current_depth;
1756         const char *type, *stage;
1757         uintptr_t laddr;
1758 
1759         if (indent > ZIO_MAXINDENT)
1760                 indent = ZIO_MAXINDENT;
1761 
1762         if (mdb_ctf_lookup_by_name("enum zio_type", &type_enum) == -1 ||
1763             mdb_ctf_lookup_by_name("enum zio_stage", &stage_enum) == -1) {
1764                 mdb_warn("failed to lookup zio enums");
1765                 return (WALK_ERR);
1766         }
1767 
1768         if ((type = mdb_ctf_enum_name(type_enum, zio->io_type)) != NULL)
1769                 type += sizeof ("ZIO_TYPE_") - 1;
1770         else
1771                 type = "?";
1772 
1773         if ((stage = mdb_ctf_enum_name(stage_enum, zio->io_stage)) != NULL)
1774                 stage += sizeof ("ZIO_STAGE_") - 1;
1775         else
1776                 stage = "?";
1777 
1778         if (zpa->zpa_current_depth >= zpa->zpa_min_depth) {
1779                 if (zpa->zpa_flags & DCMD_PIPE_OUT) {
1780                         mdb_printf("%?p\n", addr);
1781                 } else {
1782                         mdb_printf("%*s%-*p %-5s %-16s ", indent, "",
1783                             ZIO_MAXWIDTH - indent, addr, type, stage);
1784                         if (zio->io_waiter)
1785                                 mdb_printf("%?p\n", zio->io_waiter);
1786                         else
1787                                 mdb_printf("-\n");
1788                 }
1789         }
1790 
1791         if (zpa->zpa_current_depth >= zpa->zpa_max_depth)
1792                 return (WALK_NEXT);
1793 
1794         if (zpa->zpa_type == ZIO_WALK_PARENT)
1795                 laddr = addr + OFFSETOF(zio_t, io_parent_list);
1796         else
1797                 laddr = addr + OFFSETOF(zio_t, io_child_list);
1798 
1799         zpa->zpa_current_depth++;
1800         if (mdb_pwalk("list", zio_child_cb, zpa, laddr) != 0) {
1801                 mdb_warn("failed to walk zio_t children at %p\n", laddr);
1802                 return (WALK_ERR);
1803         }
1804         zpa->zpa_current_depth--;
1805 
1806         return (WALK_NEXT);
1807 }
1808 
1809 /* ARGSUSED */
1810 static int
1811 zio_child_cb(uintptr_t addr, const void *unknown, void *arg)
1812 {
1813         zio_link_t zl;
1814         zio_t zio;
1815         uintptr_t ziop;
1816         zio_print_args_t *zpa = arg;
1817 
1818         if (mdb_vread(&zl, sizeof (zl), addr) == -1) {
1819                 mdb_warn("failed to read zio_link_t at %p", addr);
1820                 return (WALK_ERR);
1821         }
1822 
1823         if (zpa->zpa_type == ZIO_WALK_PARENT)
1824                 ziop = (uintptr_t)zl.zl_parent;
1825         else
1826                 ziop = (uintptr_t)zl.zl_child;
1827 
1828         if (mdb_vread(&zio, sizeof (zio_t), ziop) == -1) {
1829                 mdb_warn("failed to read zio_t at %p", ziop);
1830                 return (WALK_ERR);
1831         }
1832 
1833         return (zio_print_cb(ziop, &zio, arg));
1834 }
1835 
1836 /* ARGSUSED */
1837 static int
1838 zio_print(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1839 {
1840         zio_t zio;
1841         zio_print_args_t zpa = { 0 };
1842 
1843         if (!(flags & DCMD_ADDRSPEC))
1844                 return (DCMD_USAGE);
1845 
1846         if (mdb_getopts(argc, argv,
1847             'r', MDB_OPT_SETBITS, INT_MAX, &zpa.zpa_max_depth,
1848             'c', MDB_OPT_SETBITS, ZIO_WALK_CHILD, &zpa.zpa_type,
1849             'p', MDB_OPT_SETBITS, ZIO_WALK_PARENT, &zpa.zpa_type,
1850             NULL) != argc)
1851                 return (DCMD_USAGE);
1852 
1853         zpa.zpa_flags = flags;
1854         if (zpa.zpa_max_depth != 0) {
1855                 if (zpa.zpa_type == ZIO_WALK_SELF)
1856                         zpa.zpa_type = ZIO_WALK_CHILD;
1857         } else if (zpa.zpa_type != ZIO_WALK_SELF) {
1858                 zpa.zpa_min_depth = 1;
1859                 zpa.zpa_max_depth = 1;
1860         }
1861 
1862         if (mdb_vread(&zio, sizeof (zio_t), addr) == -1) {
1863                 mdb_warn("failed to read zio_t at %p", addr);
1864                 return (DCMD_ERR);
1865         }
1866 
1867         if (!(flags & DCMD_PIPE_OUT) && DCMD_HDRSPEC(flags))
1868                 mdb_printf("%<u>%-*s %-5s %-16s %-?s%</u>\n", ZIO_MAXWIDTH,
1869                     "ADDRESS", "TYPE", "STAGE", "WAITER");
1870 
1871         if (zio_print_cb(addr, &zio, &zpa) != WALK_NEXT)
1872                 return (DCMD_ERR);
1873 
1874         return (DCMD_OK);
1875 }
1876 
1877 /*
1878  * [addr]::zio_state
1879  *
1880  * Print a summary of all zio_t structures on the system, or for a particular
1881  * pool.  This is equivalent to '::walk zio_root | ::zio'.
1882  */
1883 /*ARGSUSED*/
1884 static int
1885 zio_state(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1886 {
1887         /*
1888          * MDB will remember the last address of the pipeline, so if we don't
1889          * zero this we'll end up trying to walk zio structures for a
1890          * non-existent spa_t.
1891          */
1892         if (!(flags & DCMD_ADDRSPEC))
1893                 addr = 0;
1894 
1895         return (mdb_pwalk_dcmd("zio_root", "zio", argc, argv, addr));
1896 }
1897 
1898 typedef struct txg_list_walk_data {
1899         uintptr_t lw_head[TXG_SIZE];
1900         int     lw_txgoff;
1901         int     lw_maxoff;
1902         size_t  lw_offset;
1903         void    *lw_obj;
1904 } txg_list_walk_data_t;
1905 
1906 static int
1907 txg_list_walk_init_common(mdb_walk_state_t *wsp, int txg, int maxoff)
1908 {
1909         txg_list_walk_data_t *lwd;
1910         txg_list_t list;
1911         int i;
1912 
1913         lwd = mdb_alloc(sizeof (txg_list_walk_data_t), UM_SLEEP | UM_GC);
1914         if (mdb_vread(&list, sizeof (txg_list_t), wsp->walk_addr) == -1) {
1915                 mdb_warn("failed to read txg_list_t at %#lx", wsp->walk_addr);
1916                 return (WALK_ERR);
1917         }
1918 
1919         for (i = 0; i < TXG_SIZE; i++)
1920                 lwd->lw_head[i] = (uintptr_t)list.tl_head[i];
1921         lwd->lw_offset = list.tl_offset;
1922         lwd->lw_obj = mdb_alloc(lwd->lw_offset + sizeof (txg_node_t),
1923             UM_SLEEP | UM_GC);
1924         lwd->lw_txgoff = txg;
1925         lwd->lw_maxoff = maxoff;
1926 
1927         wsp->walk_addr = lwd->lw_head[lwd->lw_txgoff];
1928         wsp->walk_data = lwd;
1929 
1930         return (WALK_NEXT);
1931 }
1932 
1933 static int
1934 txg_list_walk_init(mdb_walk_state_t *wsp)
1935 {
1936         return (txg_list_walk_init_common(wsp, 0, TXG_SIZE-1));
1937 }
1938 
1939 static int
1940 txg_list0_walk_init(mdb_walk_state_t *wsp)
1941 {
1942         return (txg_list_walk_init_common(wsp, 0, 0));
1943 }
1944 
1945 static int
1946 txg_list1_walk_init(mdb_walk_state_t *wsp)
1947 {
1948         return (txg_list_walk_init_common(wsp, 1, 1));
1949 }
1950 
1951 static int
1952 txg_list2_walk_init(mdb_walk_state_t *wsp)
1953 {
1954         return (txg_list_walk_init_common(wsp, 2, 2));
1955 }
1956 
1957 static int
1958 txg_list3_walk_init(mdb_walk_state_t *wsp)
1959 {
1960         return (txg_list_walk_init_common(wsp, 3, 3));
1961 }
1962 
1963 static int
1964 txg_list_walk_step(mdb_walk_state_t *wsp)
1965 {
1966         txg_list_walk_data_t *lwd = wsp->walk_data;
1967         uintptr_t addr;
1968         txg_node_t *node;
1969         int status;
1970 
1971         while (wsp->walk_addr == NULL && lwd->lw_txgoff < lwd->lw_maxoff) {
1972                 lwd->lw_txgoff++;
1973                 wsp->walk_addr = lwd->lw_head[lwd->lw_txgoff];
1974         }
1975 
1976         if (wsp->walk_addr == NULL)
1977                 return (WALK_DONE);
1978 
1979         addr = wsp->walk_addr - lwd->lw_offset;
1980 
1981         if (mdb_vread(lwd->lw_obj,
1982             lwd->lw_offset + sizeof (txg_node_t), addr) == -1) {
1983                 mdb_warn("failed to read list element at %#lx", addr);
1984                 return (WALK_ERR);
1985         }
1986 
1987         status = wsp->walk_callback(addr, lwd->lw_obj, wsp->walk_cbdata);
1988         node = (txg_node_t *)((uintptr_t)lwd->lw_obj + lwd->lw_offset);
1989         wsp->walk_addr = (uintptr_t)node->tn_next[lwd->lw_txgoff];
1990 
1991         return (status);
1992 }
1993 
1994 /*
1995  * ::walk spa
1996  *
1997  * Walk all named spa_t structures in the namespace.  This is nothing more than
1998  * a layered avl walk.
1999  */
2000 static int
2001 spa_walk_init(mdb_walk_state_t *wsp)
2002 {
2003         GElf_Sym sym;
2004 
2005         if (wsp->walk_addr != NULL) {
2006                 mdb_warn("spa walk only supports global walks\n");
2007                 return (WALK_ERR);
2008         }
2009 
2010         if (mdb_lookup_by_obj(ZFS_OBJ_NAME, "spa_namespace_avl", &sym) == -1) {
2011                 mdb_warn("failed to find symbol 'spa_namespace_avl'");
2012                 return (WALK_ERR);
2013         }
2014 
2015         wsp->walk_addr = (uintptr_t)sym.st_value;
2016 
2017         if (mdb_layered_walk("avl", wsp) == -1) {
2018                 mdb_warn("failed to walk 'avl'\n");
2019                 return (WALK_ERR);
2020         }
2021 
2022         return (WALK_NEXT);
2023 }
2024 
2025 static int
2026 spa_walk_step(mdb_walk_state_t *wsp)
2027 {
2028         spa_t   spa;
2029 
2030         if (mdb_vread(&spa, sizeof (spa), wsp->walk_addr) == -1) {
2031                 mdb_warn("failed to read spa_t at %p", wsp->walk_addr);
2032                 return (WALK_ERR);
2033         }
2034 
2035         return (wsp->walk_callback(wsp->walk_addr, &spa, wsp->walk_cbdata));
2036 }
2037 
2038 /*
2039  * [addr]::walk zio
2040  *
2041  * Walk all active zio_t structures on the system.  This is simply a layered
2042  * walk on top of ::walk zio_cache, with the optional ability to limit the
2043  * structures to a particular pool.
2044  */
2045 static int
2046 zio_walk_init(mdb_walk_state_t *wsp)
2047 {
2048         wsp->walk_data = (void *)wsp->walk_addr;
2049 
2050         if (mdb_layered_walk("zio_cache", wsp) == -1) {
2051                 mdb_warn("failed to walk 'zio_cache'\n");
2052                 return (WALK_ERR);
2053         }
2054 
2055         return (WALK_NEXT);
2056 }
2057 
2058 static int
2059 zio_walk_step(mdb_walk_state_t *wsp)
2060 {
2061         zio_t zio;
2062 
2063         if (mdb_vread(&zio, sizeof (zio), wsp->walk_addr) == -1) {
2064                 mdb_warn("failed to read zio_t at %p", wsp->walk_addr);
2065                 return (WALK_ERR);
2066         }
2067 
2068         if (wsp->walk_data != NULL && wsp->walk_data != zio.io_spa)
2069                 return (WALK_NEXT);
2070 
2071         return (wsp->walk_callback(wsp->walk_addr, &zio, wsp->walk_cbdata));
2072 }
2073 
2074 /*
2075  * [addr]::walk zio_root
2076  *
2077  * Walk only root zio_t structures, optionally for a particular spa_t.
2078  */
2079 static int
2080 zio_walk_root_step(mdb_walk_state_t *wsp)
2081 {
2082         zio_t zio;
2083 
2084         if (mdb_vread(&zio, sizeof (zio), wsp->walk_addr) == -1) {
2085                 mdb_warn("failed to read zio_t at %p", wsp->walk_addr);
2086                 return (WALK_ERR);
2087         }
2088 
2089         if (wsp->walk_data != NULL && wsp->walk_data != zio.io_spa)
2090                 return (WALK_NEXT);
2091 
2092         /* If the parent list is not empty, ignore */
2093         if (zio.io_parent_list.list_head.list_next !=
2094             &((zio_t *)wsp->walk_addr)->io_parent_list.list_head)
2095                 return (WALK_NEXT);
2096 
2097         return (wsp->walk_callback(wsp->walk_addr, &zio, wsp->walk_cbdata));
2098 }
2099 
2100 #define NICENUM_BUFLEN 6
2101 
2102 static int
2103 snprintfrac(char *buf, int len,
2104     uint64_t numerator, uint64_t denom, int frac_digits)
2105 {
2106         int mul = 1;
2107         int whole, frac, i;
2108 
2109         for (i = frac_digits; i; i--)
2110                 mul *= 10;
2111         whole = numerator / denom;
2112         frac = mul * numerator / denom - mul * whole;
2113         return (mdb_snprintf(buf, len, "%u.%0*u", whole, frac_digits, frac));
2114 }
2115 
2116 static void
2117 mdb_nicenum(uint64_t num, char *buf)
2118 {
2119         uint64_t n = num;
2120         int index = 0;
2121         char *u;
2122 
2123         while (n >= 1024) {
2124                 n = (n + (1024 / 2)) / 1024; /* Round up or down */
2125                 index++;
2126         }
2127 
2128         u = &" \0K\0M\0G\0T\0P\0E\0"[index*2];
2129 
2130         if (index == 0) {
2131                 (void) mdb_snprintf(buf, NICENUM_BUFLEN, "%llu",
2132                     (u_longlong_t)n);
2133         } else if (n < 10 && (num & (num - 1)) != 0) {
2134                 (void) snprintfrac(buf, NICENUM_BUFLEN,
2135                     num, 1ULL << 10 * index, 2);
2136                 strcat(buf, u);
2137         } else if (n < 100 && (num & (num - 1)) != 0) {
2138                 (void) snprintfrac(buf, NICENUM_BUFLEN,
2139                     num, 1ULL << 10 * index, 1);
2140                 strcat(buf, u);
2141         } else {
2142                 (void) mdb_snprintf(buf, NICENUM_BUFLEN, "%llu%s",
2143                     (u_longlong_t)n, u);
2144         }
2145 }
2146 
2147 /*
2148  * ::zfs_blkstats
2149  *
2150  *      -v      print verbose per-level information
2151  *
2152  */
2153 static int
2154 zfs_blkstats(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2155 {
2156         boolean_t verbose = B_FALSE;
2157         zfs_all_blkstats_t stats;
2158         dmu_object_type_t t;
2159         zfs_blkstat_t *tzb;
2160         uint64_t ditto;
2161         dmu_object_type_info_t dmu_ot[DMU_OT_NUMTYPES + 10];
2162         /* +10 in case it grew */
2163 
2164         if (mdb_readvar(&dmu_ot, "dmu_ot") == -1) {
2165                 mdb_warn("failed to read 'dmu_ot'");
2166                 return (DCMD_ERR);
2167         }
2168 
2169         if (mdb_getopts(argc, argv,
2170             'v', MDB_OPT_SETBITS, TRUE, &verbose,
2171             NULL) != argc)
2172                 return (DCMD_USAGE);
2173 
2174         if (!(flags & DCMD_ADDRSPEC))
2175                 return (DCMD_USAGE);
2176 
2177         if (GETMEMB(addr, struct spa, spa_dsl_pool, addr) ||
2178             GETMEMB(addr, struct dsl_pool, dp_blkstats, addr) ||
2179             mdb_vread(&stats, sizeof (zfs_all_blkstats_t), addr) == -1) {
2180                 mdb_warn("failed to read data at %p;", addr);
2181                 mdb_printf("maybe no stats? run \"zpool scrub\" first.");
2182                 return (DCMD_ERR);
2183         }
2184 
2185         tzb = &stats.zab_type[DN_MAX_LEVELS][DMU_OT_NUMTYPES];
2186         if (tzb->zb_gangs != 0) {
2187                 mdb_printf("Ganged blocks: %llu\n",
2188                     (longlong_t)tzb->zb_gangs);
2189         }
2190 
2191         ditto = tzb->zb_ditto_2_of_2_samevdev + tzb->zb_ditto_2_of_3_samevdev +
2192             tzb->zb_ditto_3_of_3_samevdev;
2193         if (ditto != 0) {
2194                 mdb_printf("Dittoed blocks on same vdev: %llu\n",
2195                     (longlong_t)ditto);
2196         }
2197 
2198         mdb_printf("\nBlocks\tLSIZE\tPSIZE\tASIZE"
2199             "\t  avg\t comp\t%%Total\tType\n");
2200 
2201         for (t = 0; t <= DMU_OT_NUMTYPES; t++) {
2202                 char csize[NICENUM_BUFLEN], lsize[NICENUM_BUFLEN];
2203                 char psize[NICENUM_BUFLEN], asize[NICENUM_BUFLEN];
2204                 char avg[NICENUM_BUFLEN];
2205                 char comp[NICENUM_BUFLEN], pct[NICENUM_BUFLEN];
2206                 char typename[64];
2207                 int l;
2208 
2209 
2210                 if (t == DMU_OT_DEFERRED)
2211                         strcpy(typename, "deferred free");
2212                 else if (t == DMU_OT_TOTAL)
2213                         strcpy(typename, "Total");
2214                 else if (mdb_readstr(typename, sizeof (typename),
2215                     (uintptr_t)dmu_ot[t].ot_name) == -1) {
2216                         mdb_warn("failed to read type name");
2217                         return (DCMD_ERR);
2218                 }
2219 
2220                 if (stats.zab_type[DN_MAX_LEVELS][t].zb_asize == 0)
2221                         continue;
2222 
2223                 for (l = -1; l < DN_MAX_LEVELS; l++) {
2224                         int level = (l == -1 ? DN_MAX_LEVELS : l);
2225                         zfs_blkstat_t *zb = &stats.zab_type[level][t];
2226 
2227                         if (zb->zb_asize == 0)
2228                                 continue;
2229 
2230                         /*
2231                          * Don't print each level unless requested.
2232                          */
2233                         if (!verbose && level != DN_MAX_LEVELS)
2234                                 continue;
2235 
2236                         /*
2237                          * If all the space is level 0, don't print the
2238                          * level 0 separately.
2239                          */
2240                         if (level == 0 && zb->zb_asize ==
2241                             stats.zab_type[DN_MAX_LEVELS][t].zb_asize)
2242                                 continue;
2243 
2244                         mdb_nicenum(zb->zb_count, csize);
2245                         mdb_nicenum(zb->zb_lsize, lsize);
2246                         mdb_nicenum(zb->zb_psize, psize);
2247                         mdb_nicenum(zb->zb_asize, asize);
2248                         mdb_nicenum(zb->zb_asize / zb->zb_count, avg);
2249                         (void) snprintfrac(comp, NICENUM_BUFLEN,
2250                             zb->zb_lsize, zb->zb_psize, 2);
2251                         (void) snprintfrac(pct, NICENUM_BUFLEN,
2252                             100 * zb->zb_asize, tzb->zb_asize, 2);
2253 
2254                         mdb_printf("%6s\t%5s\t%5s\t%5s\t%5s"
2255                             "\t%5s\t%6s\t",
2256                             csize, lsize, psize, asize, avg, comp, pct);
2257 
2258                         if (level == DN_MAX_LEVELS)
2259                                 mdb_printf("%s\n", typename);
2260                         else
2261                                 mdb_printf("  L%d %s\n",
2262                                     level, typename);
2263                 }
2264         }
2265 
2266         return (DCMD_OK);
2267 }
2268 
2269 /* ARGSUSED */
2270 static int
2271 reference_cb(uintptr_t addr, const void *ignored, void *arg)
2272 {
2273         static int gotid;
2274         static mdb_ctf_id_t ref_id;
2275         uintptr_t ref_holder;
2276         uintptr_t ref_removed;
2277         uint64_t ref_number;
2278         boolean_t holder_is_str = B_FALSE;
2279         char holder_str[128];
2280         boolean_t removed = (boolean_t)arg;
2281 
2282         if (!gotid) {
2283                 if (mdb_ctf_lookup_by_name("struct reference", &ref_id) == -1) {
2284                         mdb_warn("couldn't find struct reference");
2285                         return (WALK_ERR);
2286                 }
2287                 gotid = TRUE;
2288         }
2289 
2290         if (GETMEMBID(addr, &ref_id, ref_holder, ref_holder) ||
2291             GETMEMBID(addr, &ref_id, ref_removed, ref_removed) ||
2292             GETMEMBID(addr, &ref_id, ref_number, ref_number))
2293                 return (WALK_ERR);
2294 
2295         if (mdb_readstr(holder_str, sizeof (holder_str), ref_holder) != -1)
2296                 holder_is_str = strisprint(holder_str);
2297 
2298         if (removed)
2299                 mdb_printf("removed ");
2300         mdb_printf("reference ");
2301         if (ref_number != 1)
2302                 mdb_printf("with count=%llu ", ref_number);
2303         mdb_printf("with tag %p", (void*)ref_holder);
2304         if (holder_is_str)
2305                 mdb_printf(" \"%s\"", holder_str);
2306         mdb_printf(", held at:\n");
2307 
2308         (void) mdb_call_dcmd("whatis", addr, DCMD_ADDRSPEC, 0, NULL);
2309 
2310         if (removed) {
2311                 mdb_printf("removed at:\n");
2312                 (void) mdb_call_dcmd("whatis", ref_removed,
2313                     DCMD_ADDRSPEC, 0, NULL);
2314         }
2315 
2316         mdb_printf("\n");
2317 
2318         return (WALK_NEXT);
2319 }
2320 
2321 /* ARGSUSED */
2322 static int
2323 refcount(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2324 {
2325         uint64_t rc_count, rc_removed_count;
2326         uintptr_t rc_list, rc_removed;
2327         static int gotid;
2328         static mdb_ctf_id_t rc_id;
2329         ulong_t off;
2330 
2331         if (!(flags & DCMD_ADDRSPEC))
2332                 return (DCMD_USAGE);
2333 
2334         if (!gotid) {
2335                 if (mdb_ctf_lookup_by_name("struct refcount", &rc_id) == -1) {
2336                         mdb_warn("couldn't find struct refcount");
2337                         return (DCMD_ERR);
2338                 }
2339                 gotid = TRUE;
2340         }
2341 
2342         if (GETMEMBID(addr, &rc_id, rc_count, rc_count) ||
2343             GETMEMBID(addr, &rc_id, rc_removed_count, rc_removed_count))
2344                 return (DCMD_ERR);
2345 
2346         mdb_printf("refcount_t at %p has %llu current holds, "
2347             "%llu recently released holds\n",
2348             addr, (longlong_t)rc_count, (longlong_t)rc_removed_count);
2349 
2350         if (rc_count > 0)
2351                 mdb_printf("current holds:\n");
2352         if (mdb_ctf_offsetof(rc_id, "rc_list", &off) == -1)
2353                 return (DCMD_ERR);
2354         rc_list = addr + off/NBBY;
2355         mdb_pwalk("list", reference_cb, (void*)B_FALSE, rc_list);
2356 
2357         if (rc_removed_count > 0)
2358                 mdb_printf("released holds:\n");
2359         if (mdb_ctf_offsetof(rc_id, "rc_removed", &off) == -1)
2360                 return (DCMD_ERR);
2361         rc_removed = addr + off/NBBY;
2362         mdb_pwalk("list", reference_cb, (void*)B_TRUE, rc_removed);
2363 
2364         return (DCMD_OK);
2365 }
2366 
2367 /* ARGSUSED */
2368 static int
2369 sa_attr_table(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2370 {
2371         sa_attr_table_t *table;
2372         sa_os_t sa_os;
2373         char *name;
2374         int i;
2375 
2376         if (mdb_vread(&sa_os, sizeof (sa_os_t), addr) == -1) {
2377                 mdb_warn("failed to read sa_os at %p", addr);
2378                 return (DCMD_ERR);
2379         }
2380 
2381         table = mdb_alloc(sizeof (sa_attr_table_t) * sa_os.sa_num_attrs,
2382             UM_SLEEP | UM_GC);
2383         name = mdb_alloc(MAXPATHLEN, UM_SLEEP | UM_GC);
2384 
2385         if (mdb_vread(table, sizeof (sa_attr_table_t) * sa_os.sa_num_attrs,
2386             (uintptr_t)sa_os.sa_attr_table) == -1) {
2387                 mdb_warn("failed to read sa_os at %p", addr);
2388                 return (DCMD_ERR);
2389         }
2390 
2391         mdb_printf("%<u>%-10s %-10s %-10s %-10s %s%</u>\n",
2392             "ATTR ID", "REGISTERED", "LENGTH", "BSWAP", "NAME");
2393         for (i = 0; i != sa_os.sa_num_attrs; i++) {
2394                 mdb_readstr(name, MAXPATHLEN, (uintptr_t)table[i].sa_name);
2395                 mdb_printf("%5x   %8x %8x %8x          %-s\n",
2396                     (int)table[i].sa_attr, (int)table[i].sa_registered,
2397                     (int)table[i].sa_length, table[i].sa_byteswap, name);
2398         }
2399 
2400         return (DCMD_OK);
2401 }
2402 
2403 static int
2404 sa_get_off_table(uintptr_t addr, uint32_t **off_tab, int attr_count)
2405 {
2406         uintptr_t idx_table;
2407 
2408         if (GETMEMB(addr, struct sa_idx_tab, sa_idx_tab, idx_table)) {
2409                 mdb_printf("can't find offset table in sa_idx_tab\n");
2410                 return (-1);
2411         }
2412 
2413         *off_tab = mdb_alloc(attr_count * sizeof (uint32_t),
2414             UM_SLEEP | UM_GC);
2415 
2416         if (mdb_vread(*off_tab,
2417             attr_count * sizeof (uint32_t), idx_table) == -1) {
2418                 mdb_warn("failed to attribute offset table %p", idx_table);
2419                 return (-1);
2420         }
2421 
2422         return (DCMD_OK);
2423 }
2424 
2425 /*ARGSUSED*/
2426 static int
2427 sa_attr_print(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2428 {
2429         uint32_t *offset_tab;
2430         int attr_count;
2431         uint64_t attr_id;
2432         uintptr_t attr_addr;
2433         uintptr_t bonus_tab, spill_tab;
2434         uintptr_t db_bonus, db_spill;
2435         uintptr_t os, os_sa;
2436         uintptr_t db_data;
2437 
2438         if (argc != 1)
2439                 return (DCMD_USAGE);
2440 
2441         if (argv[0].a_type == MDB_TYPE_STRING)
2442                 attr_id = mdb_strtoull(argv[0].a_un.a_str);
2443         else
2444                 return (DCMD_USAGE);
2445 
2446         if (GETMEMB(addr, struct sa_handle, sa_bonus_tab, bonus_tab) ||
2447             GETMEMB(addr, struct sa_handle, sa_spill_tab, spill_tab) ||
2448             GETMEMB(addr, struct sa_handle, sa_os, os) ||
2449             GETMEMB(addr, struct sa_handle, sa_bonus, db_bonus) ||
2450             GETMEMB(addr, struct sa_handle, sa_spill, db_spill)) {
2451                 mdb_printf("Can't find necessary information in sa_handle "
2452                     "in sa_handle\n");
2453                 return (DCMD_ERR);
2454         }
2455 
2456         if (GETMEMB(os, struct objset, os_sa, os_sa)) {
2457                 mdb_printf("Can't find os_sa in objset\n");
2458                 return (DCMD_ERR);
2459         }
2460 
2461         if (GETMEMB(os_sa, struct sa_os, sa_num_attrs, attr_count)) {
2462                 mdb_printf("Can't find sa_num_attrs\n");
2463                 return (DCMD_ERR);
2464         }
2465 
2466         if (attr_id > attr_count) {
2467                 mdb_printf("attribute id number is out of range\n");
2468                 return (DCMD_ERR);
2469         }
2470 
2471         if (bonus_tab) {
2472                 if (sa_get_off_table(bonus_tab, &offset_tab,
2473                     attr_count) == -1) {
2474                         return (DCMD_ERR);
2475                 }
2476 
2477                 if (GETMEMB(db_bonus, struct dmu_buf, db_data, db_data)) {
2478                         mdb_printf("can't find db_data in bonus dbuf\n");
2479                         return (DCMD_ERR);
2480                 }
2481         }
2482 
2483         if (bonus_tab && !TOC_ATTR_PRESENT(offset_tab[attr_id]) &&
2484             spill_tab == NULL) {
2485                 mdb_printf("Attribute does not exist\n");
2486                 return (DCMD_ERR);
2487         } else if (!TOC_ATTR_PRESENT(offset_tab[attr_id]) && spill_tab) {
2488                 if (sa_get_off_table(spill_tab, &offset_tab,
2489                     attr_count) == -1) {
2490                         return (DCMD_ERR);
2491                 }
2492                 if (GETMEMB(db_spill, struct dmu_buf, db_data, db_data)) {
2493                         mdb_printf("can't find db_data in spill dbuf\n");
2494                         return (DCMD_ERR);
2495                 }
2496                 if (!TOC_ATTR_PRESENT(offset_tab[attr_id])) {
2497                         mdb_printf("Attribute does not exist\n");
2498                         return (DCMD_ERR);
2499                 }
2500         }
2501         attr_addr = db_data + TOC_OFF(offset_tab[attr_id]);
2502         mdb_printf("%p\n", attr_addr);
2503         return (DCMD_OK);
2504 }
2505 
2506 /* ARGSUSED */
2507 static int
2508 zfs_ace_print_common(uintptr_t addr, uint_t flags,
2509     uint64_t id, uint32_t access_mask, uint16_t ace_flags,
2510     uint16_t ace_type, int verbose)
2511 {
2512         if (DCMD_HDRSPEC(flags) && !verbose)
2513                 mdb_printf("%<u>%-?s %-8s %-8s %-8s %s%</u>\n",
2514                     "ADDR", "FLAGS", "MASK", "TYPE", "ID");
2515 
2516         if (!verbose) {
2517                 mdb_printf("%0?p %-8x %-8x %-8x %-llx\n", addr,
2518                     ace_flags, access_mask, ace_type, id);
2519                 return (DCMD_OK);
2520         }
2521 
2522         switch (ace_flags & ACE_TYPE_FLAGS) {
2523         case ACE_OWNER:
2524                 mdb_printf("owner@:");
2525                 break;
2526         case (ACE_IDENTIFIER_GROUP | ACE_GROUP):
2527                 mdb_printf("group@:");
2528                 break;
2529         case ACE_EVERYONE:
2530                 mdb_printf("everyone@:");
2531                 break;
2532         case ACE_IDENTIFIER_GROUP:
2533                 mdb_printf("group:%llx:", (u_longlong_t)id);
2534                 break;
2535         case 0: /* User entry */
2536                 mdb_printf("user:%llx:", (u_longlong_t)id);
2537                 break;
2538         }
2539 
2540         /* print out permission mask */
2541         if (access_mask & ACE_READ_DATA)
2542                 mdb_printf("r");
2543         else
2544                 mdb_printf("-");
2545         if (access_mask & ACE_WRITE_DATA)
2546                 mdb_printf("w");
2547         else
2548                 mdb_printf("-");
2549         if (access_mask & ACE_EXECUTE)
2550                 mdb_printf("x");
2551         else
2552                 mdb_printf("-");
2553         if (access_mask & ACE_APPEND_DATA)
2554                 mdb_printf("p");
2555         else
2556                 mdb_printf("-");
2557         if (access_mask & ACE_DELETE)
2558                 mdb_printf("d");
2559         else
2560                 mdb_printf("-");
2561         if (access_mask & ACE_DELETE_CHILD)
2562                 mdb_printf("D");
2563         else
2564                 mdb_printf("-");
2565         if (access_mask & ACE_READ_ATTRIBUTES)
2566                 mdb_printf("a");
2567         else
2568                 mdb_printf("-");
2569         if (access_mask & ACE_WRITE_ATTRIBUTES)
2570                 mdb_printf("A");
2571         else
2572                 mdb_printf("-");
2573         if (access_mask & ACE_READ_NAMED_ATTRS)
2574                 mdb_printf("R");
2575         else
2576                 mdb_printf("-");
2577         if (access_mask & ACE_WRITE_NAMED_ATTRS)
2578                 mdb_printf("W");
2579         else
2580                 mdb_printf("-");
2581         if (access_mask & ACE_READ_ACL)
2582                 mdb_printf("c");
2583         else
2584                 mdb_printf("-");
2585         if (access_mask & ACE_WRITE_ACL)
2586                 mdb_printf("C");
2587         else
2588                 mdb_printf("-");
2589         if (access_mask & ACE_WRITE_OWNER)
2590                 mdb_printf("o");
2591         else
2592                 mdb_printf("-");
2593         if (access_mask & ACE_SYNCHRONIZE)
2594                 mdb_printf("s");
2595         else
2596                 mdb_printf("-");
2597 
2598         mdb_printf(":");
2599 
2600         /* Print out inheritance flags */
2601         if (ace_flags & ACE_FILE_INHERIT_ACE)
2602                 mdb_printf("f");
2603         else
2604                 mdb_printf("-");
2605         if (ace_flags & ACE_DIRECTORY_INHERIT_ACE)
2606                 mdb_printf("d");
2607         else
2608                 mdb_printf("-");
2609         if (ace_flags & ACE_INHERIT_ONLY_ACE)
2610                 mdb_printf("i");
2611         else
2612                 mdb_printf("-");
2613         if (ace_flags & ACE_NO_PROPAGATE_INHERIT_ACE)
2614                 mdb_printf("n");
2615         else
2616                 mdb_printf("-");
2617         if (ace_flags & ACE_SUCCESSFUL_ACCESS_ACE_FLAG)
2618                 mdb_printf("S");
2619         else
2620                 mdb_printf("-");
2621         if (ace_flags & ACE_FAILED_ACCESS_ACE_FLAG)
2622                 mdb_printf("F");
2623         else
2624                 mdb_printf("-");
2625         if (ace_flags & ACE_INHERITED_ACE)
2626                 mdb_printf("I");
2627         else
2628                 mdb_printf("-");
2629 
2630         switch (ace_type) {
2631         case ACE_ACCESS_ALLOWED_ACE_TYPE:
2632                 mdb_printf(":allow\n");
2633                 break;
2634         case ACE_ACCESS_DENIED_ACE_TYPE:
2635                 mdb_printf(":deny\n");
2636                 break;
2637         case ACE_SYSTEM_AUDIT_ACE_TYPE:
2638                 mdb_printf(":audit\n");
2639                 break;
2640         case ACE_SYSTEM_ALARM_ACE_TYPE:
2641                 mdb_printf(":alarm\n");
2642                 break;
2643         default:
2644                 mdb_printf(":?\n");
2645         }
2646         return (DCMD_OK);
2647 }
2648 
2649 /* ARGSUSED */
2650 static int
2651 zfs_ace_print(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2652 {
2653         zfs_ace_t zace;
2654         int verbose = FALSE;
2655         uint64_t id;
2656 
2657         if (!(flags & DCMD_ADDRSPEC))
2658                 return (DCMD_USAGE);
2659 
2660         if (mdb_getopts(argc, argv,
2661             'v', MDB_OPT_SETBITS, TRUE, &verbose, TRUE, NULL) != argc)
2662                 return (DCMD_USAGE);
2663 
2664         if (mdb_vread(&zace, sizeof (zfs_ace_t), addr) == -1) {
2665                 mdb_warn("failed to read zfs_ace_t");
2666                 return (DCMD_ERR);
2667         }
2668 
2669         if ((zace.z_hdr.z_flags & ACE_TYPE_FLAGS) == 0 ||
2670             (zace.z_hdr.z_flags & ACE_TYPE_FLAGS) == ACE_IDENTIFIER_GROUP)
2671                 id = zace.z_fuid;
2672         else
2673                 id = -1;
2674 
2675         return (zfs_ace_print_common(addr, flags, id, zace.z_hdr.z_access_mask,
2676             zace.z_hdr.z_flags, zace.z_hdr.z_type, verbose));
2677 }
2678 
2679 /* ARGSUSED */
2680 static int
2681 zfs_ace0_print(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2682 {
2683         ace_t ace;
2684         uint64_t id;
2685         int verbose = FALSE;
2686 
2687         if (!(flags & DCMD_ADDRSPEC))
2688                 return (DCMD_USAGE);
2689 
2690         if (mdb_getopts(argc, argv,
2691             'v', MDB_OPT_SETBITS, TRUE, &verbose, TRUE, NULL) != argc)
2692                 return (DCMD_USAGE);
2693 
2694         if (mdb_vread(&ace, sizeof (ace_t), addr) == -1) {
2695                 mdb_warn("failed to read ace_t");
2696                 return (DCMD_ERR);
2697         }
2698 
2699         if ((ace.a_flags & ACE_TYPE_FLAGS) == 0 ||
2700             (ace.a_flags & ACE_TYPE_FLAGS) == ACE_IDENTIFIER_GROUP)
2701                 id = ace.a_who;
2702         else
2703                 id = -1;
2704 
2705         return (zfs_ace_print_common(addr, flags, id, ace.a_access_mask,
2706             ace.a_flags, ace.a_type, verbose));
2707 }
2708 
2709 typedef struct acl_dump_args {
2710         int a_argc;
2711         const mdb_arg_t *a_argv;
2712         uint16_t a_version;
2713         int a_flags;
2714 } acl_dump_args_t;
2715 
2716 /* ARGSUSED */
2717 static int
2718 acl_aces_cb(uintptr_t addr, const void *unknown, void *arg)
2719 {
2720         acl_dump_args_t *acl_args = (acl_dump_args_t *)arg;
2721 
2722         if (acl_args->a_version == 1) {
2723                 if (mdb_call_dcmd("zfs_ace", addr,
2724                     DCMD_ADDRSPEC|acl_args->a_flags, acl_args->a_argc,
2725                     acl_args->a_argv) != DCMD_OK) {
2726                         return (WALK_ERR);
2727                 }
2728         } else {
2729                 if (mdb_call_dcmd("zfs_ace0", addr,
2730                     DCMD_ADDRSPEC|acl_args->a_flags, acl_args->a_argc,
2731                     acl_args->a_argv) != DCMD_OK) {
2732                         return (WALK_ERR);
2733                 }
2734         }
2735         acl_args->a_flags = DCMD_LOOP;
2736         return (WALK_NEXT);
2737 }
2738 
2739 /* ARGSUSED */
2740 static int
2741 acl_cb(uintptr_t addr, const void *unknown, void *arg)
2742 {
2743         acl_dump_args_t *acl_args = (acl_dump_args_t *)arg;
2744 
2745         if (acl_args->a_version == 1) {
2746                 if (mdb_pwalk("zfs_acl_node_aces", acl_aces_cb,
2747                     arg, addr) != 0) {
2748                         mdb_warn("can't walk ACEs");
2749                         return (DCMD_ERR);
2750                 }
2751         } else {
2752                 if (mdb_pwalk("zfs_acl_node_aces0", acl_aces_cb,
2753                     arg, addr) != 0) {
2754                         mdb_warn("can't walk ACEs");
2755                         return (DCMD_ERR);
2756                 }
2757         }
2758         return (WALK_NEXT);
2759 }
2760 
2761 /* ARGSUSED */
2762 static int
2763 zfs_acl_dump(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2764 {
2765         zfs_acl_t zacl;
2766         int verbose = FALSE;
2767         acl_dump_args_t acl_args;
2768 
2769         if (!(flags & DCMD_ADDRSPEC))
2770                 return (DCMD_USAGE);
2771 
2772         if (mdb_getopts(argc, argv,
2773             'v', MDB_OPT_SETBITS, TRUE, &verbose, TRUE, NULL) != argc)
2774                 return (DCMD_USAGE);
2775 
2776         if (mdb_vread(&zacl, sizeof (zfs_acl_t), addr) == -1) {
2777                 mdb_warn("failed to read zfs_acl_t");
2778                 return (DCMD_ERR);
2779         }
2780 
2781         acl_args.a_argc = argc;
2782         acl_args.a_argv = argv;
2783         acl_args.a_version = zacl.z_version;
2784         acl_args.a_flags = DCMD_LOOPFIRST;
2785 
2786         if (mdb_pwalk("zfs_acl_node", acl_cb, &acl_args, addr) != 0) {
2787                 mdb_warn("can't walk ACL");
2788                 return (DCMD_ERR);
2789         }
2790 
2791         return (DCMD_OK);
2792 }
2793 
2794 /* ARGSUSED */
2795 static int
2796 zfs_acl_node_walk_init(mdb_walk_state_t *wsp)
2797 {
2798         if (wsp->walk_addr == NULL) {
2799                 mdb_warn("must supply address of zfs_acl_node_t\n");
2800                 return (WALK_ERR);
2801         }
2802 
2803         wsp->walk_addr += OFFSETOF(zfs_acl_t, z_acl);
2804 
2805         if (mdb_layered_walk("list", wsp) == -1) {
2806                 mdb_warn("failed to walk 'list'\n");
2807                 return (WALK_ERR);
2808         }
2809 
2810         return (WALK_NEXT);
2811 }
2812 
2813 static int
2814 zfs_acl_node_walk_step(mdb_walk_state_t *wsp)
2815 {
2816         zfs_acl_node_t  aclnode;
2817 
2818         if (mdb_vread(&aclnode, sizeof (zfs_acl_node_t),
2819             wsp->walk_addr) == -1) {
2820                 mdb_warn("failed to read zfs_acl_node at %p", wsp->walk_addr);
2821                 return (WALK_ERR);
2822         }
2823 
2824         return (wsp->walk_callback(wsp->walk_addr, &aclnode, wsp->walk_cbdata));
2825 }
2826 
2827 typedef struct ace_walk_data {
2828         int             ace_count;
2829         int             ace_version;
2830 } ace_walk_data_t;
2831 
2832 static int
2833 zfs_aces_walk_init_common(mdb_walk_state_t *wsp, int version,
2834     int ace_count, uintptr_t ace_data)
2835 {
2836         ace_walk_data_t *ace_walk_data;
2837 
2838         if (wsp->walk_addr == NULL) {
2839                 mdb_warn("must supply address of zfs_acl_node_t\n");
2840                 return (WALK_ERR);
2841         }
2842 
2843         ace_walk_data = mdb_alloc(sizeof (ace_walk_data_t), UM_SLEEP | UM_GC);
2844 
2845         ace_walk_data->ace_count = ace_count;
2846         ace_walk_data->ace_version = version;
2847 
2848         wsp->walk_addr = ace_data;
2849         wsp->walk_data = ace_walk_data;
2850 
2851         return (WALK_NEXT);
2852 }
2853 
2854 static int
2855 zfs_acl_node_aces_walk_init_common(mdb_walk_state_t *wsp, int version)
2856 {
2857         static int gotid;
2858         static mdb_ctf_id_t acl_id;
2859         int z_ace_count;
2860         uintptr_t z_acldata;
2861 
2862         if (!gotid) {
2863                 if (mdb_ctf_lookup_by_name("struct zfs_acl_node",
2864                     &acl_id) == -1) {
2865                         mdb_warn("couldn't find struct zfs_acl_node");
2866                         return (DCMD_ERR);
2867                 }
2868                 gotid = TRUE;
2869         }
2870 
2871         if (GETMEMBID(wsp->walk_addr, &acl_id, z_ace_count, z_ace_count)) {
2872                 return (DCMD_ERR);
2873         }
2874         if (GETMEMBID(wsp->walk_addr, &acl_id, z_acldata, z_acldata)) {
2875                 return (DCMD_ERR);
2876         }
2877 
2878         return (zfs_aces_walk_init_common(wsp, version,
2879             z_ace_count, z_acldata));
2880 }
2881 
2882 /* ARGSUSED */
2883 static int
2884 zfs_acl_node_aces_walk_init(mdb_walk_state_t *wsp)
2885 {
2886         return (zfs_acl_node_aces_walk_init_common(wsp, 1));
2887 }
2888 
2889 /* ARGSUSED */
2890 static int
2891 zfs_acl_node_aces0_walk_init(mdb_walk_state_t *wsp)
2892 {
2893         return (zfs_acl_node_aces_walk_init_common(wsp, 0));
2894 }
2895 
2896 static int
2897 zfs_aces_walk_step(mdb_walk_state_t *wsp)
2898 {
2899         ace_walk_data_t *ace_data = wsp->walk_data;
2900         zfs_ace_t zace;
2901         ace_t *acep;
2902         int status;
2903         int entry_type;
2904         int allow_type;
2905         uintptr_t ptr;
2906 
2907         if (ace_data->ace_count == 0)
2908                 return (WALK_DONE);
2909 
2910         if (mdb_vread(&zace, sizeof (zfs_ace_t), wsp->walk_addr) == -1) {
2911                 mdb_warn("failed to read zfs_ace_t at %#lx",
2912                     wsp->walk_addr);
2913                 return (WALK_ERR);
2914         }
2915 
2916         switch (ace_data->ace_version) {
2917         case 0:
2918                 acep = (ace_t *)&zace;
2919                 entry_type = acep->a_flags & ACE_TYPE_FLAGS;
2920                 allow_type = acep->a_type;
2921                 break;
2922         case 1:
2923                 entry_type = zace.z_hdr.z_flags & ACE_TYPE_FLAGS;
2924                 allow_type = zace.z_hdr.z_type;
2925                 break;
2926         default:
2927                 return (WALK_ERR);
2928         }
2929 
2930         ptr = (uintptr_t)wsp->walk_addr;
2931         switch (entry_type) {
2932         case ACE_OWNER:
2933         case ACE_EVERYONE:
2934         case (ACE_IDENTIFIER_GROUP | ACE_GROUP):
2935                 ptr += ace_data->ace_version == 0 ?
2936                     sizeof (ace_t) : sizeof (zfs_ace_hdr_t);
2937                 break;
2938         case ACE_IDENTIFIER_GROUP:
2939         default:
2940                 switch (allow_type) {
2941                 case ACE_ACCESS_ALLOWED_OBJECT_ACE_TYPE:
2942                 case ACE_ACCESS_DENIED_OBJECT_ACE_TYPE:
2943                 case ACE_SYSTEM_AUDIT_OBJECT_ACE_TYPE:
2944                 case ACE_SYSTEM_ALARM_OBJECT_ACE_TYPE:
2945                         ptr += ace_data->ace_version == 0 ?
2946                             sizeof (ace_t) : sizeof (zfs_object_ace_t);
2947                         break;
2948                 default:
2949                         ptr += ace_data->ace_version == 0 ?
2950                             sizeof (ace_t) : sizeof (zfs_ace_t);
2951                         break;
2952                 }
2953         }
2954 
2955         ace_data->ace_count--;
2956         status = wsp->walk_callback(wsp->walk_addr,
2957             (void *)(uintptr_t)&zace, wsp->walk_cbdata);
2958 
2959         wsp->walk_addr = ptr;
2960         return (status);
2961 }
2962 
2963 /*
2964  * MDB module linkage information:
2965  *
2966  * We declare a list of structures describing our dcmds, and a function
2967  * named _mdb_init to return a pointer to our module information.
2968  */
2969 
2970 static const mdb_dcmd_t dcmds[] = {
2971         { "arc", "[-bkmg]", "print ARC variables", arc_print },
2972         { "blkptr", ":", "print blkptr_t", blkptr },
2973         { "dbuf", ":", "print dmu_buf_impl_t", dbuf },
2974         { "dbuf_stats", ":", "dbuf stats", dbuf_stats },
2975         { "dbufs",
2976             "\t[-O objset_t*] [-n objset_name | \"mos\"] "
2977             "[-o object | \"mdn\"] \n"
2978             "\t[-l level] [-b blkid | \"bonus\"]",
2979             "find dmu_buf_impl_t's that match specified criteria", dbufs },
2980         { "abuf_find", "dva_word[0] dva_word[1]",
2981             "find arc_buf_hdr_t of a specified DVA",
2982             abuf_find },
2983         { "spa", "?[-cv]", "spa_t summary", spa_print },
2984         { "spa_config", ":", "print spa_t configuration", spa_print_config },
2985         { "spa_verify", ":", "verify spa_t consistency", spa_verify },
2986         { "spa_space", ":[-b]", "print spa_t on-disk space usage", spa_space },
2987         { "spa_vdevs", ":", "given a spa_t, print vdev summary", spa_vdevs },
2988         { "vdev", ":[-re]\n"
2989             "\t-r display recursively\n"
2990             "\t-e print statistics",
2991             "vdev_t summary", vdev_print },
2992         { "zio", ":[cpr]\n"
2993             "\t-c display children\n"
2994             "\t-p display parents\n"
2995             "\t-r display recursively",
2996             "zio_t summary", zio_print },
2997         { "zio_state", "?", "print out all zio_t structures on system or "
2998             "for a particular pool", zio_state },
2999         { "zfs_blkstats", ":[-v]",
3000             "given a spa_t, print block type stats from last scrub",
3001             zfs_blkstats },
3002         { "zfs_params", "", "print zfs tunable parameters", zfs_params },
3003         { "refcount", "", "print refcount_t holders", refcount },
3004         { "zap_leaf", "", "print zap_leaf_phys_t", zap_leaf },
3005         { "zfs_aces", ":[-v]", "print all ACEs from a zfs_acl_t",
3006             zfs_acl_dump },
3007         { "zfs_ace", ":[-v]", "print zfs_ace", zfs_ace_print },
3008         { "zfs_ace0", ":[-v]", "print zfs_ace0", zfs_ace0_print },
3009         { "sa_attr_table", ":", "print SA attribute table from sa_os_t",
3010             sa_attr_table},
3011         { "sa_attr", ": attr_id",
3012             "print SA attribute address when given sa_handle_t", sa_attr_print},
3013         { "zfs_dbgmsg", ":[-v]",
3014             "print zfs debug log", dbgmsg},
3015         { NULL }
3016 };
3017 
3018 static const mdb_walker_t walkers[] = {
3019         { "zms_freelist", "walk ZFS metaslab freelist",
3020                 freelist_walk_init, freelist_walk_step, NULL },
3021         { "txg_list", "given any txg_list_t *, walk all entries in all txgs",
3022                 txg_list_walk_init, txg_list_walk_step, NULL },
3023         { "txg_list0", "given any txg_list_t *, walk all entries in txg 0",
3024                 txg_list0_walk_init, txg_list_walk_step, NULL },
3025         { "txg_list1", "given any txg_list_t *, walk all entries in txg 1",
3026                 txg_list1_walk_init, txg_list_walk_step, NULL },
3027         { "txg_list2", "given any txg_list_t *, walk all entries in txg 2",
3028                 txg_list2_walk_init, txg_list_walk_step, NULL },
3029         { "txg_list3", "given any txg_list_t *, walk all entries in txg 3",
3030                 txg_list3_walk_init, txg_list_walk_step, NULL },
3031         { "zio", "walk all zio structures, optionally for a particular spa_t",
3032                 zio_walk_init, zio_walk_step, NULL },
3033         { "zio_root", "walk all root zio_t structures, optionally for a "
3034             "particular spa_t",
3035                 zio_walk_init, zio_walk_root_step, NULL },
3036         { "spa", "walk all spa_t entries in the namespace",
3037                 spa_walk_init, spa_walk_step, NULL },
3038         { "metaslab", "given a spa_t *, walk all metaslab_t structures",
3039                 metaslab_walk_init, metaslab_walk_step, NULL },
3040         { "zfs_acl_node", "given a zfs_acl_t, walk all zfs_acl_nodes",
3041             zfs_acl_node_walk_init, zfs_acl_node_walk_step, NULL },
3042         { "zfs_acl_node_aces", "given a zfs_acl_node_t, walk all ACEs",
3043             zfs_acl_node_aces_walk_init, zfs_aces_walk_step, NULL },
3044         { "zfs_acl_node_aces0",
3045             "given a zfs_acl_node_t, walk all ACEs as ace_t",
3046             zfs_acl_node_aces0_walk_init, zfs_aces_walk_step, NULL },
3047         { NULL }
3048 };
3049 
3050 static const mdb_modinfo_t modinfo = {
3051         MDB_API_VERSION, dcmds, walkers
3052 };
3053 
3054 const mdb_modinfo_t *
3055 _mdb_init(void)
3056 {
3057         return (&modinfo);
3058 }