]> git.proxmox.com Git - mirror_zfs.git/blobdiff - cmd/zdb/zdb.c
cstyle: Resolve C style issues
[mirror_zfs.git] / cmd / zdb / zdb.c
index 70b4ba2683824b7d63121b7ade7e6962f6c8f33f..8e60b9b1a879bd47a3838bf3235d86cc64e4f6d4 100644 (file)
@@ -57,6 +57,7 @@
 #include <sys/arc.h>
 #include <sys/ddt.h>
 #include <sys/zfeature.h>
+#include <zfs_comutil.h>
 #undef ZFS_MAXNAMELEN
 #include <libzfs.h>
 
@@ -86,6 +87,7 @@ extern void dump_intent_log(zilog_t *);
 uint64_t *zopt_object = NULL;
 int zopt_objects = 0;
 libzfs_handle_t *g_zfs;
+uint64_t max_inflight = 200;
 
 /*
  * These libumem hooks provide a reasonable set of defaults for the allocator's
@@ -108,13 +110,14 @@ usage(void)
 {
        (void) fprintf(stderr,
            "Usage: %s [-CumdibcsDvhLXFPA] [-t txg] [-e [-p path...]] "
-           "poolname [object...]\n"
-           "       %s [-divPA] [-e -p path...] dataset [object...]\n"
-           "       %s -m [-LXFPA] [-t txg] [-e [-p path...]] "
+           "[-U config] [-M inflight I/Os] poolname [object...]\n"
+           "       %s [-divPA] [-e -p path...] [-U config] dataset "
+           "[object...]\n"
+           "       %s -m [-LXFPA] [-t txg] [-e [-p path...]] [-U config] "
            "poolname [vdev [metaslab...]]\n"
            "       %s -R [-A] [-e [-p path...]] poolname "
            "vdev:offset:size[:flags]\n"
-           "       %s -S [-PA] [-e [-p path...]] poolname\n"
+           "       %s -S [-PA] [-e [-p path...]] [-U config] poolname\n"
            "       %s -l [-uA] device\n"
            "       %s -C [-A] [-U config]\n\n",
            cmdname, cmdname, cmdname, cmdname, cmdname, cmdname, cmdname);
@@ -161,6 +164,9 @@ usage(void)
        (void) fprintf(stderr, "        -P print numbers in parseable form\n");
        (void) fprintf(stderr, "        -t <txg> -- highest txg to use when "
            "searching for uberblocks\n");
+       (void) fprintf(stderr, "        -M <number of inflight I/Os> -- "
+           "specify the maximum number of checksumming I/Os "
+           "[default is 200]\n");
        (void) fprintf(stderr, "Specify an option more than once (e.g. -bb) "
            "to make only that option verbose\n");
        (void) fprintf(stderr, "Default is to dump everything non-verbosely\n");
@@ -205,6 +211,27 @@ dump_packed_nvlist(objset_t *os, uint64_t object, void *data, size_t size)
        nvlist_free(nv);
 }
 
+/* ARGSUSED */
+static void
+dump_history_offsets(objset_t *os, uint64_t object, void *data, size_t size)
+{
+       spa_history_phys_t *shp = data;
+
+       if (shp == NULL)
+               return;
+
+       (void) printf("\t\tpool_create_len = %llu\n",
+           (u_longlong_t)shp->sh_pool_create_len);
+       (void) printf("\t\tphys_max_off = %llu\n",
+           (u_longlong_t)shp->sh_phys_max_off);
+       (void) printf("\t\tbof = %llu\n",
+           (u_longlong_t)shp->sh_bof);
+       (void) printf("\t\teof = %llu\n",
+           (u_longlong_t)shp->sh_eof);
+       (void) printf("\t\trecords_lost = %llu\n",
+           (u_longlong_t)shp->sh_records_lost);
+}
+
 static void
 zdb_nicenum(uint64_t num, char *buf)
 {
@@ -544,7 +571,7 @@ static void
 dump_metaslab_stats(metaslab_t *msp)
 {
        char maxbuf[32];
-       space_map_t *sm = &msp->ms_map;
+       space_map_t *sm = msp->ms_map;
        avl_tree_t *t = sm->sm_pp_root;
        int free_pct = sm->sm_space * 100 / sm->sm_size;
 
@@ -560,7 +587,7 @@ dump_metaslab(metaslab_t *msp)
 {
        vdev_t *vd = msp->ms_group->mg_vd;
        spa_t *spa = vd->vdev_spa;
-       space_map_t *sm = &msp->ms_map;
+       space_map_t *sm = msp->ms_map;
        space_map_obj_t *smo = &msp->ms_smo;
        char freebuf[32];
 
@@ -865,21 +892,22 @@ dump_history(spa_t *spa)
        for (i = 0; i < num; i++) {
                uint64_t time, txg, ievent;
                char *cmd, *intstr;
+               boolean_t printed = B_FALSE;
 
                if (nvlist_lookup_uint64(events[i], ZPOOL_HIST_TIME,
                    &time) != 0)
-                       continue;
+                       goto next;
                if (nvlist_lookup_string(events[i], ZPOOL_HIST_CMD,
                    &cmd) != 0) {
                        if (nvlist_lookup_uint64(events[i],
                            ZPOOL_HIST_INT_EVENT, &ievent) != 0)
-                               continue;
+                               goto next;
                        verify(nvlist_lookup_uint64(events[i],
                            ZPOOL_HIST_TXG, &txg) == 0);
                        verify(nvlist_lookup_string(events[i],
                            ZPOOL_HIST_INT_STR, &intstr) == 0);
-                       if (ievent >= LOG_END)
-                               continue;
+                       if (ievent >= ZFS_NUM_LEGACY_HISTORY_EVENTS)
+                               goto next;
 
                        (void) snprintf(internalstr,
                            sizeof (internalstr),
@@ -892,6 +920,14 @@ dump_history(spa_t *spa)
                (void) localtime_r(&tsec, &t);
                (void) strftime(tbuf, sizeof (tbuf), "%F.%T", &t);
                (void) printf("%s %s\n", tbuf, cmd);
+               printed = B_TRUE;
+
+next:
+               if (dump_opt['h'] > 1) {
+                       if (!printed)
+                               (void) printf("unrecognized record:\n");
+                       dump_nvlist(events[i], 2);
+               }
        }
 }
 
@@ -992,7 +1028,7 @@ visit_indirect(spa_t *spa, const dnode_phys_t *dnp,
                arc_buf_t *buf;
                uint64_t fill = 0;
 
-               err = arc_read_nolock(NULL, spa, bp, arc_getbuf_func, &buf,
+               err = arc_read(NULL, spa, bp, arc_getbuf_func, &buf,
                    ZIO_PRIORITY_ASYNC_READ, ZIO_FLAG_CANFAIL, &flags, zb);
                if (err)
                        return (err);
@@ -1198,41 +1234,67 @@ dump_bpobj_cb(void *arg, const blkptr_t *bp, dmu_tx_t *tx)
 }
 
 static void
-dump_bpobj(bpobj_t *bpo, char *name)
+dump_bpobj(bpobj_t *bpo, char *name, int indent)
 {
        char bytes[32];
        char comp[32];
        char uncomp[32];
+       uint64_t i;
 
        if (dump_opt['d'] < 3)
                return;
 
        zdb_nicenum(bpo->bpo_phys->bpo_bytes, bytes);
-       if (bpo->bpo_havesubobj) {
+       if (bpo->bpo_havesubobj && bpo->bpo_phys->bpo_subobjs != 0) {
                zdb_nicenum(bpo->bpo_phys->bpo_comp, comp);
                zdb_nicenum(bpo->bpo_phys->bpo_uncomp, uncomp);
-               (void) printf("\n    %s: %llu local blkptrs, %llu subobjs, "
-                   "%s (%s/%s comp)\n",
-                   name, (u_longlong_t)bpo->bpo_phys->bpo_num_blkptrs,
+               (void) printf("    %*s: object %llu, %llu local blkptrs, "
+                   "%llu subobjs, %s (%s/%s comp)\n",
+                   indent * 8, name,
+                   (u_longlong_t)bpo->bpo_object,
+                   (u_longlong_t)bpo->bpo_phys->bpo_num_blkptrs,
                    (u_longlong_t)bpo->bpo_phys->bpo_num_subobjs,
                    bytes, comp, uncomp);
+
+               for (i = 0; i < bpo->bpo_phys->bpo_num_subobjs; i++) {
+                       uint64_t subobj;
+                       bpobj_t subbpo;
+                       int error;
+                       VERIFY0(dmu_read(bpo->bpo_os,
+                           bpo->bpo_phys->bpo_subobjs,
+                           i * sizeof (subobj), sizeof (subobj), &subobj, 0));
+                       error = bpobj_open(&subbpo, bpo->bpo_os, subobj);
+                       if (error != 0) {
+                               (void) printf("ERROR %u while trying to open "
+                                   "subobj id %llu\n",
+                                   error, (u_longlong_t)subobj);
+                               continue;
+                       }
+                       dump_bpobj(&subbpo, "subobj", indent + 1);
+               }
        } else {
-               (void) printf("\n    %s: %llu blkptrs, %s\n",
-                   name, (u_longlong_t)bpo->bpo_phys->bpo_num_blkptrs, bytes);
+               (void) printf("    %*s: object %llu, %llu blkptrs, %s\n",
+                   indent * 8, name,
+                   (u_longlong_t)bpo->bpo_object,
+                   (u_longlong_t)bpo->bpo_phys->bpo_num_blkptrs,
+                   bytes);
        }
 
        if (dump_opt['d'] < 5)
                return;
 
-       (void) printf("\n");
 
-       (void) bpobj_iterate_nofree(bpo, dump_bpobj_cb, NULL, NULL);
+       if (indent == 0) {
+               (void) bpobj_iterate_nofree(bpo, dump_bpobj_cb, NULL, NULL);
+               (void) printf("\n");
+       }
 }
 
 static void
 dump_deadlist(dsl_deadlist_t *dl)
 {
        dsl_deadlist_entry_t *dle;
+       uint64_t unused;
        char bytes[32];
        char comp[32];
        char uncomp[32];
@@ -1251,14 +1313,25 @@ dump_deadlist(dsl_deadlist_t *dl)
 
        (void) printf("\n");
 
+       /* force the tree to be loaded */
+       dsl_deadlist_space_range(dl, 0, UINT64_MAX, &unused, &unused, &unused);
+
        for (dle = avl_first(&dl->dl_tree); dle;
            dle = AVL_NEXT(&dl->dl_tree, dle)) {
-               (void) printf("      mintxg %llu -> obj %llu\n",
-                   (longlong_t)dle->dle_mintxg,
-                   (longlong_t)dle->dle_bpobj.bpo_object);
+               if (dump_opt['d'] >= 5) {
+                       char buf[128];
+                       (void) snprintf(buf, sizeof (buf),
+                           "mintxg %llu -> obj %llu",
+                           (longlong_t)dle->dle_mintxg,
+                           (longlong_t)dle->dle_bpobj.bpo_object);
+
+                       dump_bpobj(&dle->dle_bpobj, buf, 0);
+               } else {
+                       (void) printf("mintxg %llu -> obj %llu\n",
+                           (longlong_t)dle->dle_mintxg,
+                           (longlong_t)dle->dle_bpobj.bpo_object);
 
-               if (dump_opt['d'] >= 5)
-                       dump_bpobj(&dle->dle_bpobj, "");
+               }
        }
 }
 
@@ -1281,7 +1354,7 @@ fuid_table_destroy(void)
  * print uid or gid information.
  * For normal POSIX id just the id is printed in decimal format.
  * For CIFS files with FUID the fuid is printed in hex followed by
- * the doman-rid string.
+ * the domain-rid string.
  */
 static void
 print_idstr(uint64_t id, const char *id_type)
@@ -1323,6 +1396,61 @@ dump_uidgid(objset_t *os, uint64_t uid, uint64_t gid)
        print_idstr(gid, "gid");
 }
 
+static void
+dump_znode_sa_xattr(sa_handle_t *hdl)
+{
+       nvlist_t *sa_xattr;
+       nvpair_t *elem = NULL;
+       int sa_xattr_size = 0;
+       int sa_xattr_entries = 0;
+       int error;
+       char *sa_xattr_packed;
+
+       error = sa_size(hdl, sa_attr_table[ZPL_DXATTR], &sa_xattr_size);
+       if (error || sa_xattr_size == 0)
+               return;
+
+       sa_xattr_packed = malloc(sa_xattr_size);
+       if (sa_xattr_packed == NULL)
+               return;
+
+       error = sa_lookup(hdl, sa_attr_table[ZPL_DXATTR],
+           sa_xattr_packed, sa_xattr_size);
+       if (error) {
+               free(sa_xattr_packed);
+               return;
+       }
+
+       error = nvlist_unpack(sa_xattr_packed, sa_xattr_size, &sa_xattr, 0);
+       if (error) {
+               free(sa_xattr_packed);
+               return;
+       }
+
+       while ((elem = nvlist_next_nvpair(sa_xattr, elem)) != NULL)
+               sa_xattr_entries++;
+
+       (void) printf("\tSA xattrs: %d bytes, %d entries\n\n",
+           sa_xattr_size, sa_xattr_entries);
+       while ((elem = nvlist_next_nvpair(sa_xattr, elem)) != NULL) {
+               uchar_t *value;
+               uint_t cnt, idx;
+
+               (void) printf("\t\t%s = ", nvpair_name(elem));
+               nvpair_value_byte_array(elem, &value, &cnt);
+               for (idx = 0; idx < cnt; ++idx) {
+                       if (isprint(value[idx]))
+                               (void) putchar(value[idx]);
+                       else
+                               (void) printf("\\%3.3o", value[idx]);
+               }
+               (void) putchar('\n');
+       }
+
+       nvlist_free(sa_xattr);
+       free(sa_xattr_packed);
+}
+
 /*ARGSUSED*/
 static void
 dump_znode(objset_t *os, uint64_t object, void *data, size_t size)
@@ -1423,6 +1551,7 @@ dump_znode(objset_t *os, uint64_t object, void *data, size_t size)
        if (sa_lookup(hdl, sa_attr_table[ZPL_RDEV], &rdev,
            sizeof (uint64_t)) == 0)
                (void) printf("\trdev   0x%016llx\n", (u_longlong_t)rdev);
+       dump_znode_sa_xattr(hdl);
        sa_handle_destroy(hdl);
 }
 
@@ -1469,7 +1598,7 @@ static object_viewer_t *object_viewer[DMU_OT_NUMTYPES + 1] = {
        dump_zap,               /* other ZAP                    */
        dump_zap,               /* persistent error log         */
        dump_uint8,             /* SPA history                  */
-       dump_uint64,            /* SPA history offsets          */
+       dump_history_offsets,   /* SPA history offsets          */
        dump_zap,               /* Pool properties              */
        dump_zap,               /* DSL permissions              */
        dump_acl,               /* ZFS ACL                      */
@@ -1634,7 +1763,9 @@ dump_dir(objset_t *os)
        int print_header = 1;
        int i, error;
 
+       dsl_pool_config_enter(dmu_objset_pool(os), FTAG);
        dmu_objset_fast_stat(os, &dds);
+       dsl_pool_config_exit(dmu_objset_pool(os), FTAG);
 
        if (dds.dds_type < DMU_OST_NUMTYPES)
                type = objset_types[dds.dds_type];
@@ -2005,9 +2136,47 @@ zdb_count_block(zdb_cb_t *zcb, zilog_t *zilog, const blkptr_t *bp,
            bp, NULL, NULL, ZIO_FLAG_CANFAIL)), ==, 0);
 }
 
-/* ARGSUSED */
+static void
+zdb_blkptr_done(zio_t *zio)
+{
+       spa_t *spa = zio->io_spa;
+       blkptr_t *bp = zio->io_bp;
+       int ioerr = zio->io_error;
+       zdb_cb_t *zcb = zio->io_private;
+       zbookmark_t *zb = &zio->io_bookmark;
+
+       zio_data_buf_free(zio->io_data, zio->io_size);
+
+       mutex_enter(&spa->spa_scrub_lock);
+       spa->spa_scrub_inflight--;
+       cv_broadcast(&spa->spa_scrub_io_cv);
+
+       if (ioerr && !(zio->io_flags & ZIO_FLAG_SPECULATIVE)) {
+               char blkbuf[BP_SPRINTF_LEN];
+
+               zcb->zcb_haderrors = 1;
+               zcb->zcb_errors[ioerr]++;
+
+               if (dump_opt['b'] >= 2)
+                       sprintf_blkptr(blkbuf, bp);
+               else
+                       blkbuf[0] = '\0';
+
+               (void) printf("zdb_blkptr_cb: "
+                   "Got error %d reading "
+                   "<%llu, %llu, %lld, %llx> %s -- skipping\n",
+                   ioerr,
+                   (u_longlong_t)zb->zb_objset,
+                   (u_longlong_t)zb->zb_object,
+                   (u_longlong_t)zb->zb_level,
+                   (u_longlong_t)zb->zb_blkid,
+                   blkbuf);
+       }
+       mutex_exit(&spa->spa_scrub_lock);
+}
+
 static int
-zdb_blkptr_cb(spa_t *spa, zilog_t *zilog, const blkptr_t *bp, arc_buf_t *pbuf,
+zdb_blkptr_cb(spa_t *spa, zilog_t *zilog, const blkptr_t *bp,
     const zbookmark_t *zb, const dnode_phys_t *dnp, void *arg)
 {
        zdb_cb_t *zcb = arg;
@@ -2026,39 +2195,22 @@ zdb_blkptr_cb(spa_t *spa, zilog_t *zilog, const blkptr_t *bp, arc_buf_t *pbuf,
        is_metadata = (BP_GET_LEVEL(bp) != 0 || DMU_OT_IS_METADATA(type));
 
        if (dump_opt['c'] > 1 || (dump_opt['c'] && is_metadata)) {
-               int ioerr;
                size_t size = BP_GET_PSIZE(bp);
-               void *data = malloc(size);
+               void *data = zio_data_buf_alloc(size);
                int flags = ZIO_FLAG_CANFAIL | ZIO_FLAG_SCRUB | ZIO_FLAG_RAW;
 
                /* If it's an intent log block, failure is expected. */
                if (zb->zb_level == ZB_ZIL_LEVEL)
                        flags |= ZIO_FLAG_SPECULATIVE;
 
-               ioerr = zio_wait(zio_read(NULL, spa, bp, data, size,
-                   NULL, NULL, ZIO_PRIORITY_ASYNC_READ, flags, zb));
-
-               free(data);
-
-               if (ioerr && !(flags & ZIO_FLAG_SPECULATIVE)) {
-                       zcb->zcb_haderrors = 1;
-                       zcb->zcb_errors[ioerr]++;
+               mutex_enter(&spa->spa_scrub_lock);
+               while (spa->spa_scrub_inflight > max_inflight)
+                       cv_wait(&spa->spa_scrub_io_cv, &spa->spa_scrub_lock);
+               spa->spa_scrub_inflight++;
+               mutex_exit(&spa->spa_scrub_lock);
 
-                       if (dump_opt['b'] >= 2)
-                               sprintf_blkptr(blkbuf, bp);
-                       else
-                               blkbuf[0] = '\0';
-
-                       (void) printf("zdb_blkptr_cb: "
-                           "Got error %d reading "
-                           "<%llu, %llu, %lld, %llx> %s -- skipping\n",
-                           ioerr,
-                           (u_longlong_t)zb->zb_objset,
-                           (u_longlong_t)zb->zb_object,
-                           (u_longlong_t)zb->zb_level,
-                           (u_longlong_t)zb->zb_blkid,
-                           blkbuf);
-               }
+               zio_nowait(zio_read(NULL, spa, bp, data, size,
+                   zdb_blkptr_done, zcb, ZIO_PRIORITY_ASYNC_READ, flags, zb));
        }
 
        zcb->zcb_readfails = 0;
@@ -2167,11 +2319,11 @@ zdb_leak_init(spa_t *spa, zdb_cb_t *zcb)
                        for (m = 0; m < vd->vdev_ms_count; m++) {
                                metaslab_t *msp = vd->vdev_ms[m];
                                mutex_enter(&msp->ms_lock);
-                               space_map_unload(&msp->ms_map);
-                               VERIFY(space_map_load(&msp->ms_map,
+                               space_map_unload(msp->ms_map);
+                               VERIFY(space_map_load(msp->ms_map,
                                    &zdb_space_map_ops, SM_ALLOC, &msp->ms_smo,
                                    spa->spa_meta_objset) == 0);
-                               msp->ms_map.sm_ppd = vd;
+                               msp->ms_map->sm_ppd = vd;
                                mutex_exit(&msp->ms_lock);
                        }
                }
@@ -2196,7 +2348,7 @@ zdb_leak_fini(spa_t *spa)
                        for (m = 0; m < vd->vdev_ms_count; m++) {
                                metaslab_t *msp = vd->vdev_ms[m];
                                mutex_enter(&msp->ms_lock);
-                               space_map_unload(&msp->ms_map);
+                               space_map_unload(msp->ms_map);
                                mutex_exit(&msp->ms_lock);
                        }
                }
@@ -2244,7 +2396,7 @@ dump_block_stats(spa_t *spa)
         * it's not part of any space map) is a double allocation,
         * reference to a freed block, or an unclaimed log block.
         */
-       bzero(&zcb, sizeof(zdb_cb_t));
+       bzero(&zcb, sizeof (zdb_cb_t));
        zdb_leak_init(spa, &zcb);
 
        /*
@@ -2252,8 +2404,10 @@ dump_block_stats(spa_t *spa)
         */
        (void) bpobj_iterate_nofree(&spa->spa_deferred_bpobj,
            count_block_cb, &zcb, NULL);
-       (void) bpobj_iterate_nofree(&spa->spa_dsl_pool->dp_free_bpobj,
-           count_block_cb, &zcb, NULL);
+       if (spa_version(spa) >= SPA_VERSION_DEADLISTS) {
+               (void) bpobj_iterate_nofree(&spa->spa_dsl_pool->dp_free_bpobj,
+                   count_block_cb, &zcb, NULL);
+       }
        if (spa_feature_is_active(spa,
            &spa_feature_table[SPA_FEATURE_ASYNC_DESTROY])) {
                VERIFY3U(0, ==, bptree_iterate(spa->spa_meta_objset,
@@ -2266,6 +2420,18 @@ dump_block_stats(spa_t *spa)
 
        zcb.zcb_haderrors |= traverse_pool(spa, 0, flags, zdb_blkptr_cb, &zcb);
 
+       /*
+        * If we've traversed the data blocks then we need to wait for those
+        * I/Os to complete. We leverage "The Godfather" zio to wait on
+        * all async I/Os to complete.
+        */
+       if (dump_opt['c']) {
+               (void) zio_wait(spa->spa_async_zio_root);
+               spa->spa_async_zio_root = zio_root(spa, NULL, NULL,
+                   ZIO_FLAG_CANFAIL | ZIO_FLAG_SPECULATIVE |
+                   ZIO_FLAG_GODFATHER);
+       }
+
        if (zcb.zcb_haderrors) {
                (void) printf("\nError counts:\n\n");
                (void) printf("\t%5s  %s\n", "errno", "count");
@@ -2418,7 +2584,7 @@ typedef struct zdb_ddt_entry {
 /* ARGSUSED */
 static int
 zdb_ddt_add_cb(spa_t *spa, zilog_t *zilog, const blkptr_t *bp,
-    arc_buf_t *pbuf, const zbookmark_t *zb, const dnode_phys_t *dnp, void *arg)
+    const zbookmark_t *zb, const dnode_phys_t *dnp, void *arg)
 {
        avl_tree_t *t = arg;
        avl_index_t where;
@@ -2541,10 +2707,11 @@ dump_zpool(spa_t *spa)
        if (dump_opt['d'] || dump_opt['i']) {
                dump_dir(dp->dp_meta_objset);
                if (dump_opt['d'] >= 3) {
-                       dump_bpobj(&spa->spa_deferred_bpobj, "Deferred frees");
+                       dump_bpobj(&spa->spa_deferred_bpobj,
+                           "Deferred frees", 0);
                        if (spa_version(spa) >= SPA_VERSION_DEADLISTS) {
                                dump_bpobj(&spa->spa_dsl_pool->dp_free_bpobj,
-                                   "Pool snapshot frees");
+                                   "Pool snapshot frees", 0);
                        }
 
                        if (spa_feature_is_active(spa,
@@ -2806,7 +2973,7 @@ zdb_read_block(char *thing, spa_t *spa)
        psize = size;
        lsize = size;
 
-       pbuf = umem_alloc(SPA_MAXBLOCKSIZE, UMEM_NOFAIL);
+       pbuf = umem_alloc_aligned(SPA_MAXBLOCKSIZE, 512, UMEM_NOFAIL);
        lbuf = umem_alloc(SPA_MAXBLOCKSIZE, UMEM_NOFAIL);
 
        BP_ZERO(bp);
@@ -3020,13 +3187,23 @@ main(int argc, char **argv)
        nvlist_t *policy = NULL;
        uint64_t max_txg = UINT64_MAX;
        int rewind = ZPOOL_NEVER_REWIND;
+       char *spa_config_path_env;
 
        (void) setrlimit(RLIMIT_NOFILE, &rl);
        (void) enable_extended_FILE_stdio(-1, -1);
 
        dprintf_setup(&argc, argv);
 
-       while ((c = getopt(argc, argv, "bcdhilmsuCDRSAFLXevp:t:U:P")) != -1) {
+       /*
+        * If there is an environment variable SPA_CONFIG_PATH it overrides
+        * default spa_config_path setting. If -U flag is specified it will
+        * override this environment variable settings once again.
+        */
+       spa_config_path_env = getenv("SPA_CONFIG_PATH");
+       if (spa_config_path_env != NULL)
+               spa_config_path = spa_config_path_env;
+
+       while ((c = getopt(argc, argv, "bcdhilmM:suCDRSAFLXevp:t:U:P")) != -1) {
                switch (c) {
                case 'b':
                case 'c':
@@ -3055,6 +3232,15 @@ main(int argc, char **argv)
                case 'v':
                        verbose++;
                        break;
+               case 'M':
+                       max_inflight = strtoull(optarg, NULL, 0);
+                       if (max_inflight == 0) {
+                               (void) fprintf(stderr, "maximum number "
+                                   "of inflight I/Os must be greater "
+                                   "than 0\n");
+                               usage();
+                       }
+                       break;
                case 'p':
                        if (searchdirs == NULL) {
                                searchdirs = umem_alloc(sizeof (char *),