]> git.proxmox.com Git - mirror_zfs.git/blobdiff - cmd/ztest/ztest.c
Restrict kstats and print real pointers
[mirror_zfs.git] / cmd / ztest / ztest.c
index da763d74d46bfc405d50c8410e88df693b682826..9c2cf9501831b423c827d5a071a6ff4dc011c80a 100644 (file)
 #include <sys/vdev_impl.h>
 #include <sys/vdev_file.h>
 #include <sys/vdev_initialize.h>
+#include <sys/vdev_trim.h>
 #include <sys/spa_impl.h>
 #include <sys/metaslab_impl.h>
 #include <sys/dsl_prop.h>
@@ -183,8 +184,8 @@ typedef struct ztest_shared_opts {
 } ztest_shared_opts_t;
 
 static const ztest_shared_opts_t ztest_opts_defaults = {
-       .zo_pool = { 'z', 't', 'e', 's', 't', '\0' },
-       .zo_dir = { '/', 't', 'm', 'p', '\0' },
+       .zo_pool = "ztest",
+       .zo_dir = "/tmp",
        .zo_alt_ztest = { '\0' },
        .zo_alt_libpath = { '\0' },
        .zo_vdevs = 5,
@@ -374,6 +375,7 @@ ztest_func_t ztest_spa_upgrade;
 ztest_func_t ztest_device_removal;
 ztest_func_t ztest_spa_checkpoint_create_discard;
 ztest_func_t ztest_initialize;
+ztest_func_t ztest_trim;
 ztest_func_t ztest_fletcher;
 ztest_func_t ztest_fletcher_incr;
 ztest_func_t ztest_verify_dnode_bt;
@@ -427,6 +429,7 @@ ztest_info_t ztest_info[] = {
        ZTI_INIT(ztest_device_removal, 1, &zopt_sometimes),
        ZTI_INIT(ztest_spa_checkpoint_create_discard, 1, &zopt_rarely),
        ZTI_INIT(ztest_initialize, 1, &zopt_sometimes),
+       ZTI_INIT(ztest_trim, 1, &zopt_sometimes),
        ZTI_INIT(ztest_fletcher, 1, &zopt_rarely),
        ZTI_INIT(ztest_fletcher_incr, 1, &zopt_rarely),
        ZTI_INIT(ztest_verify_dnode_bt, 1, &zopt_sometimes),
@@ -476,6 +479,7 @@ static ztest_ds_t *ztest_ds;
 
 static kmutex_t ztest_vdev_lock;
 static boolean_t ztest_device_removal_active = B_FALSE;
+static boolean_t ztest_pool_scrubbed = B_FALSE;
 static kmutex_t ztest_checkpoint_lock;
 
 /*
@@ -515,6 +519,7 @@ enum ztest_object {
 };
 
 static void usage(boolean_t) __NORETURN;
+static int ztest_scrub_impl(spa_t *spa);
 
 /*
  * These libumem hooks provide a reasonable set of defaults for the allocator's
@@ -2761,6 +2766,13 @@ ztest_mmp_enable_disable(ztest_ds_t *zd, uint64_t id)
        if (zo->zo_mmp_test)
                return;
 
+       /*
+        * Since enabling MMP involves setting a property, it could not be done
+        * while the pool is suspended.
+        */
+       if (spa_suspended(spa))
+               return;
+
        spa_config_enter(spa, SCL_CONFIG, FTAG, RW_READER);
        mutex_enter(&spa->spa_props_lock);
 
@@ -3427,10 +3439,17 @@ ztest_vdev_attach_detach(ztest_ds_t *zd, uint64_t id)
        pguid = pvd->vdev_guid;
 
        /*
-        * If oldvd has siblings, then half of the time, detach it.
+        * If oldvd has siblings, then half of the time, detach it.  Prior
+        * to the detach the pool is scrubbed in order to prevent creating
+        * unrepairable blocks as a result of the data corruption injection.
         */
        if (oldvd_has_siblings && ztest_random(2) == 0) {
                spa_config_exit(spa, SCL_ALL, FTAG);
+
+               error = ztest_scrub_impl(spa);
+               if (error)
+                       goto out;
+
                error = spa_vdev_detach(spa, oldguid, pguid, B_FALSE);
                if (error != 0 && error != ENODEV && error != EBUSY &&
                    error != ENOTSUP && error != ZFS_ERR_CHECKPOINT_EXISTS &&
@@ -4847,14 +4866,14 @@ ztest_dmu_read_write_zcopy(ztest_ds_t *zd, uint64_t id)
                                    FTAG, &dbt, DMU_READ_NO_PREFETCH) == 0);
                        }
                        if (i != 5 || chunksize < (SPA_MINBLOCKSIZE * 2)) {
-                               dmu_assign_arcbuf_by_dbuf(bonus_db, off,
-                                   bigbuf_arcbufs[j], tx);
+                               VERIFY0(dmu_assign_arcbuf_by_dbuf(bonus_db,
+                                   off, bigbuf_arcbufs[j], tx));
                        } else {
-                               dmu_assign_arcbuf_by_dbuf(bonus_db, off,
-                                   bigbuf_arcbufs[2 * j], tx);
-                               dmu_assign_arcbuf_by_dbuf(bonus_db,
+                               VERIFY0(dmu_assign_arcbuf_by_dbuf(bonus_db,
+                                   off, bigbuf_arcbufs[2 * j], tx));
+                               VERIFY0(dmu_assign_arcbuf_by_dbuf(bonus_db,
                                    off + chunksize / 2,
-                                   bigbuf_arcbufs[2 * j + 1], tx);
+                                   bigbuf_arcbufs[2 * j + 1], tx));
                        }
                        if (i == 1) {
                                dmu_buf_rele(dbt, FTAG);
@@ -4881,7 +4900,7 @@ ztest_dmu_read_write_zcopy(ztest_ds_t *zd, uint64_t id)
                        umem_free(bigcheck, bigsize);
                }
                if (i == 2) {
-                       txg_wait_open(dmu_objset_pool(os), 0);
+                       txg_wait_open(dmu_objset_pool(os), 0, B_TRUE);
                } else if (i == 3) {
                        txg_wait_synced(dmu_objset_pool(os), 0);
                }
@@ -5558,6 +5577,8 @@ ztest_spa_prop_get_set(ztest_ds_t *zd, uint64_t id)
        (void) ztest_spa_prop_set_uint64(ZPOOL_PROP_DEDUPDITTO,
            ZIO_DEDUPDITTO_MIN + ztest_random(ZIO_DEDUPDITTO_MIN));
 
+       (void) ztest_spa_prop_set_uint64(ZPOOL_PROP_AUTOTRIM, ztest_random(2));
+
        VERIFY0(spa_prop_get(ztest_spa, &props));
 
        if (ztest_opts.zo_verbose >= 6)
@@ -5757,6 +5778,19 @@ ztest_fault_inject(ztest_ds_t *zd, uint64_t id)
 
        ASSERT(leaves >= 1);
 
+       /*
+        * While ztest is running the number of leaves will not change.  This
+        * is critical for the fault injection logic as it determines where
+        * errors can be safely injected such that they are always repairable.
+        *
+        * When restarting ztest a different number of leaves may be requested
+        * which will shift the regions to be damaged.  This is fine as long
+        * as the pool has been scrubbed prior to using the new mapping.
+        * Failure to do can result in non-repairable damage being injected.
+        */
+       if (ztest_pool_scrubbed == B_FALSE)
+               goto out;
+
        /*
         * Grab the name lock as reader. There are some operations
         * which don't like to have their vdevs changed while
@@ -6113,6 +6147,32 @@ ztest_ddt_repair(ztest_ds_t *zd, uint64_t id)
        umem_free(od, sizeof (ztest_od_t));
 }
 
+/*
+ * By design ztest will never inject uncorrectable damage in to the pool.
+ * Issue a scrub, wait for it to complete, and verify there is never any
+ * any persistent damage.
+ *
+ * Only after a full scrub has been completed is it safe to start injecting
+ * data corruption.  See the comment in zfs_fault_inject().
+ */
+static int
+ztest_scrub_impl(spa_t *spa)
+{
+       int error = spa_scan(spa, POOL_SCAN_SCRUB);
+       if (error)
+               return (error);
+
+       while (dsl_scan_scrubbing(spa_get_dsl(spa)))
+               txg_wait_synced(spa_get_dsl(spa), 0);
+
+       if (spa_get_errlog_size(spa) > 0)
+               return (ECKSUM);
+
+       ztest_pool_scrubbed = B_TRUE;
+
+       return (0);
+}
+
 /*
  * Scrub the pool.
  */
@@ -6121,6 +6181,7 @@ void
 ztest_scrub(ztest_ds_t *zd, uint64_t id)
 {
        spa_t *spa = ztest_spa;
+       int error;
 
        /*
         * Scrub in progress by device removal.
@@ -6128,9 +6189,16 @@ ztest_scrub(ztest_ds_t *zd, uint64_t id)
        if (ztest_device_removal_active)
                return;
 
+       /*
+        * Start a scrub, wait a moment, then force a restart.
+        */
        (void) spa_scan(spa, POOL_SCAN_SCRUB);
-       (void) poll(NULL, 0, 100); /* wait a moment, then force a restart */
-       (void) spa_scan(spa, POOL_SCAN_SCRUB);
+       (void) poll(NULL, 0, 100);
+
+       error = ztest_scrub_impl(spa);
+       if (error == EBUSY)
+               error = 0;
+       ASSERT0(error);
 }
 
 /*
@@ -6400,7 +6468,7 @@ ztest_initialize(ztest_ds_t *zd, uint64_t id)
        char *path = strdup(rand_vd->vdev_path);
        boolean_t active = rand_vd->vdev_initialize_thread != NULL;
 
-       zfs_dbgmsg("vd %p, guid %llu", rand_vd, guid);
+       zfs_dbgmsg("vd %px, guid %llu", rand_vd, guid);
        spa_config_exit(spa, SCL_VDEV, FTAG);
 
        uint64_t cmd = ztest_random(POOL_INITIALIZE_FUNCS);
@@ -6421,7 +6489,7 @@ ztest_initialize(ztest_ds_t *zd, uint64_t id)
                        (void) printf("\n");
                }
                break;
-       case POOL_INITIALIZE_DO:
+       case POOL_INITIALIZE_START:
                if (ztest_opts.zo_verbose >= 4) {
                        (void) printf("Start initialize %s", path);
                        if (active && error == 0)
@@ -6444,6 +6512,82 @@ ztest_initialize(ztest_ds_t *zd, uint64_t id)
        mutex_exit(&ztest_vdev_lock);
 }
 
+/* ARGSUSED */
+void
+ztest_trim(ztest_ds_t *zd, uint64_t id)
+{
+       spa_t *spa = ztest_spa;
+       int error = 0;
+
+       mutex_enter(&ztest_vdev_lock);
+
+       spa_config_enter(spa, SCL_VDEV, FTAG, RW_READER);
+
+       /* Random leaf vdev */
+       vdev_t *rand_vd = ztest_random_concrete_vdev_leaf(spa->spa_root_vdev);
+       if (rand_vd == NULL) {
+               spa_config_exit(spa, SCL_VDEV, FTAG);
+               mutex_exit(&ztest_vdev_lock);
+               return;
+       }
+
+       /*
+        * The random vdev we've selected may change as soon as we
+        * drop the spa_config_lock. We create local copies of things
+        * we're interested in.
+        */
+       uint64_t guid = rand_vd->vdev_guid;
+       char *path = strdup(rand_vd->vdev_path);
+       boolean_t active = rand_vd->vdev_trim_thread != NULL;
+
+       zfs_dbgmsg("vd %p, guid %llu", rand_vd, guid);
+       spa_config_exit(spa, SCL_VDEV, FTAG);
+
+       uint64_t cmd = ztest_random(POOL_TRIM_FUNCS);
+       uint64_t rate = 1 << ztest_random(30);
+       boolean_t partial = (ztest_random(5) > 0);
+       boolean_t secure = (ztest_random(5) > 0);
+
+       nvlist_t *vdev_guids = fnvlist_alloc();
+       nvlist_t *vdev_errlist = fnvlist_alloc();
+       fnvlist_add_uint64(vdev_guids, path, guid);
+       error = spa_vdev_trim(spa, vdev_guids, cmd, rate, partial,
+           secure, vdev_errlist);
+       fnvlist_free(vdev_guids);
+       fnvlist_free(vdev_errlist);
+
+       switch (cmd) {
+       case POOL_TRIM_CANCEL:
+               if (ztest_opts.zo_verbose >= 4) {
+                       (void) printf("Cancel TRIM %s", path);
+                       if (!active)
+                               (void) printf(" failed (no TRIM active)");
+                       (void) printf("\n");
+               }
+               break;
+       case POOL_TRIM_START:
+               if (ztest_opts.zo_verbose >= 4) {
+                       (void) printf("Start TRIM %s", path);
+                       if (active && error == 0)
+                               (void) printf(" failed (already active)");
+                       else if (error != 0)
+                               (void) printf(" failed (error %d)", error);
+                       (void) printf("\n");
+               }
+               break;
+       case POOL_TRIM_SUSPEND:
+               if (ztest_opts.zo_verbose >= 4) {
+                       (void) printf("Suspend TRIM %s", path);
+                       if (!active)
+                               (void) printf(" failed (no TRIM active)");
+                       (void) printf("\n");
+               }
+               break;
+       }
+       free(path);
+       mutex_exit(&ztest_vdev_lock);
+}
+
 /*
  * Verify pool integrity by running zdb.
  */
@@ -7014,9 +7158,10 @@ ztest_run(ztest_shared_t *zs)
                while (spa->spa_removing_phys.sr_state == DSS_SCANNING)
                        txg_wait_synced(spa_get_dsl(spa), 0);
 
-               (void) spa_scan(spa, POOL_SCAN_SCRUB);
-               while (dsl_scan_scrubbing(spa_get_dsl(spa)))
-                       txg_wait_synced(spa_get_dsl(spa), 0);
+               error = ztest_scrub_impl(spa);
+               if (error == EBUSY)
+                       error = 0;
+               ASSERT0(error);
        }
 
        run_threads = umem_zalloc(ztest_opts.zo_threads * sizeof (kthread_t *),