summaryrefslogtreecommitdiffstats
path: root/cddl/contrib/opensolaris/cmd/ztest/ztest.c
diff options
context:
space:
mode:
Diffstat (limited to 'cddl/contrib/opensolaris/cmd/ztest/ztest.c')
-rw-r--r--cddl/contrib/opensolaris/cmd/ztest/ztest.c1640
1 files changed, 889 insertions, 751 deletions
diff --git a/cddl/contrib/opensolaris/cmd/ztest/ztest.c b/cddl/contrib/opensolaris/cmd/ztest/ztest.c
index 5d9f028..b7ca302 100644
--- a/cddl/contrib/opensolaris/cmd/ztest/ztest.c
+++ b/cddl/contrib/opensolaris/cmd/ztest/ztest.c
@@ -19,12 +19,10 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
/*
* The objective of this program is to provide a DMU/ZAP/SPA stress test
* that runs entirely in userland, is easy to use, and easy to extend.
@@ -92,6 +90,7 @@
#include <sys/zio_compress.h>
#include <sys/zil.h>
#include <sys/vdev_impl.h>
+#include <sys/vdev_file.h>
#include <sys/spa_impl.h>
#include <sys/dsl_prop.h>
#include <sys/refcount.h>
@@ -128,8 +127,18 @@ static char *zopt_dir = "/tmp";
static uint64_t zopt_time = 300; /* 5 minutes */
static int zopt_maxfaults;
+typedef struct ztest_block_tag {
+ uint64_t bt_objset;
+ uint64_t bt_object;
+ uint64_t bt_offset;
+ uint64_t bt_txg;
+ uint64_t bt_thread;
+ uint64_t bt_seq;
+} ztest_block_tag_t;
+
typedef struct ztest_args {
- char *za_pool;
+ char za_pool[MAXNAMELEN];
+ spa_t *za_spa;
objset_t *za_os;
zilog_t *za_zilog;
thread_t za_thread;
@@ -142,6 +151,13 @@ typedef struct ztest_args {
hrtime_t za_stop;
hrtime_t za_kill;
traverse_handle_t *za_th;
+ /*
+ * Thread-local variables can go here to aid debugging.
+ */
+ ztest_block_tag_t za_rbt;
+ ztest_block_tag_t za_wbt;
+ dmu_object_info_t za_doi;
+ dmu_buf_t *za_dbuf;
} ztest_args_t;
typedef void ztest_func_t(ztest_args_t *);
@@ -160,14 +176,16 @@ ztest_func_t ztest_dmu_objset_create_destroy;
ztest_func_t ztest_dmu_snapshot_create_destroy;
ztest_func_t ztest_spa_create_destroy;
ztest_func_t ztest_fault_inject;
+ztest_func_t ztest_spa_rename;
ztest_func_t ztest_vdev_attach_detach;
ztest_func_t ztest_vdev_LUN_growth;
ztest_func_t ztest_vdev_add_remove;
+ztest_func_t ztest_vdev_aux_add_remove;
ztest_func_t ztest_scrub;
-ztest_func_t ztest_spa_rename;
typedef struct ztest_info {
ztest_func_t *zi_func; /* test function */
+ uint64_t zi_iters; /* iterations per execution */
uint64_t *zi_interval; /* execute every <interval> seconds */
uint64_t zi_calls; /* per-pass count */
uint64_t zi_call_time; /* per-pass time */
@@ -181,22 +199,23 @@ uint64_t zopt_sometimes = 10; /* every 10 seconds */
uint64_t zopt_rarely = 60; /* every 60 seconds */
ztest_info_t ztest_info[] = {
- { ztest_dmu_read_write, &zopt_always },
- { ztest_dmu_write_parallel, &zopt_always },
- { ztest_dmu_object_alloc_free, &zopt_always },
- { ztest_zap, &zopt_always },
- { ztest_zap_parallel, &zopt_always },
- { ztest_traverse, &zopt_often },
- { ztest_dsl_prop_get_set, &zopt_sometimes },
- { ztest_dmu_objset_create_destroy, &zopt_sometimes },
- { ztest_dmu_snapshot_create_destroy, &zopt_rarely },
- { ztest_spa_create_destroy, &zopt_sometimes },
- { ztest_fault_inject, &zopt_sometimes },
- { ztest_spa_rename, &zopt_rarely },
- { ztest_vdev_attach_detach, &zopt_rarely },
- { ztest_vdev_LUN_growth, &zopt_rarely },
- { ztest_vdev_add_remove, &zopt_vdevtime },
- { ztest_scrub, &zopt_vdevtime },
+ { ztest_dmu_read_write, 1, &zopt_always },
+ { ztest_dmu_write_parallel, 30, &zopt_always },
+ { ztest_dmu_object_alloc_free, 1, &zopt_always },
+ { ztest_zap, 30, &zopt_always },
+ { ztest_zap_parallel, 100, &zopt_always },
+ { ztest_traverse, 1, &zopt_often },
+ { ztest_dsl_prop_get_set, 1, &zopt_sometimes },
+ { ztest_dmu_objset_create_destroy, 1, &zopt_sometimes },
+ { ztest_dmu_snapshot_create_destroy, 1, &zopt_sometimes },
+ { ztest_spa_create_destroy, 1, &zopt_sometimes },
+ { ztest_fault_inject, 1, &zopt_sometimes },
+ { ztest_spa_rename, 1, &zopt_rarely },
+ { ztest_vdev_attach_detach, 1, &zopt_rarely },
+ { ztest_vdev_LUN_growth, 1, &zopt_rarely },
+ { ztest_vdev_add_remove, 1, &zopt_vdevtime },
+ { ztest_vdev_aux_add_remove, 1, &zopt_vdevtime },
+ { ztest_scrub, 1, &zopt_vdevtime },
};
#define ZTEST_FUNCS (sizeof (ztest_info) / sizeof (ztest_info_t))
@@ -210,34 +229,27 @@ typedef struct ztest_shared {
mutex_t zs_vdev_lock;
rwlock_t zs_name_lock;
uint64_t zs_vdev_primaries;
+ uint64_t zs_vdev_aux;
uint64_t zs_enospc_count;
hrtime_t zs_start_time;
hrtime_t zs_stop_time;
uint64_t zs_alloc;
uint64_t zs_space;
- uint64_t zs_txg;
ztest_info_t zs_info[ZTEST_FUNCS];
mutex_t zs_sync_lock[ZTEST_SYNC_LOCKS];
uint64_t zs_seq[ZTEST_SYNC_LOCKS];
} ztest_shared_t;
-typedef struct ztest_block_tag {
- uint64_t bt_objset;
- uint64_t bt_object;
- uint64_t bt_offset;
- uint64_t bt_txg;
- uint64_t bt_thread;
- uint64_t bt_seq;
-} ztest_block_tag_t;
-
static char ztest_dev_template[] = "%s/%s.%llua";
+static char ztest_aux_template[] = "%s/%s.%s.%llu";
static ztest_shared_t *ztest_shared;
static int ztest_random_fd;
static int ztest_dump_core = 1;
-extern uint64_t zio_gang_bang;
-extern uint16_t zio_zil_fail_shift;
+static boolean_t ztest_exiting;
+
+extern uint64_t metaslab_gang_bang;
#define ZTEST_DIROBJ 1
#define ZTEST_MICROZAP_OBJ 2
@@ -357,7 +369,7 @@ usage(boolean_t requested)
FILE *fp = requested ? stdout : stderr;
nicenum(zopt_vdev_size, nice_vdev_size);
- nicenum(zio_gang_bang, nice_gang_bang);
+ nicenum(metaslab_gang_bang, nice_gang_bang);
(void) fprintf(fp, "Usage: %s\n"
"\t[-v vdevs (default: %llu)]\n"
@@ -377,26 +389,24 @@ usage(boolean_t requested)
"\t[-E(xisting)] (use existing pool instead of creating new one)\n"
"\t[-T time] total run time (default: %llu sec)\n"
"\t[-P passtime] time per pass (default: %llu sec)\n"
- "\t[-z zil failure rate (default: fail every 2^%llu allocs)]\n"
"\t[-h] (print help)\n"
"",
cmdname,
- (u_longlong_t)zopt_vdevs, /* -v */
- nice_vdev_size, /* -s */
- zopt_ashift, /* -a */
- zopt_mirrors, /* -m */
- zopt_raidz, /* -r */
- zopt_raidz_parity, /* -R */
- zopt_datasets, /* -d */
- zopt_threads, /* -t */
- nice_gang_bang, /* -g */
- zopt_init, /* -i */
- (u_longlong_t)zopt_killrate, /* -k */
- zopt_pool, /* -p */
- zopt_dir, /* -f */
- (u_longlong_t)zopt_time, /* -T */
- (u_longlong_t)zopt_passtime, /* -P */
- (u_longlong_t)zio_zil_fail_shift); /* -z */
+ (u_longlong_t)zopt_vdevs, /* -v */
+ nice_vdev_size, /* -s */
+ zopt_ashift, /* -a */
+ zopt_mirrors, /* -m */
+ zopt_raidz, /* -r */
+ zopt_raidz_parity, /* -R */
+ zopt_datasets, /* -d */
+ zopt_threads, /* -t */
+ nice_gang_bang, /* -g */
+ zopt_init, /* -i */
+ (u_longlong_t)zopt_killrate, /* -k */
+ zopt_pool, /* -p */
+ zopt_dir, /* -f */
+ (u_longlong_t)zopt_time, /* -T */
+ (u_longlong_t)zopt_passtime); /* -P */
exit(requested ? 0 : 1);
}
@@ -431,91 +441,84 @@ process_options(int argc, char **argv)
progname = argv[0];
/* By default, test gang blocks for blocks 32K and greater */
- zio_gang_bang = 32 << 10;
-
- /* Default value, fail every 32nd allocation */
- zio_zil_fail_shift = 5;
+ metaslab_gang_bang = 32 << 10;
while ((opt = getopt(argc, argv,
- "v:s:a:m:r:R:d:t:g:i:k:p:f:VET:P:z:h")) != EOF) {
+ "v:s:a:m:r:R:d:t:g:i:k:p:f:VET:P:h")) != EOF) {
value = 0;
switch (opt) {
- case 'v':
- case 's':
- case 'a':
- case 'm':
- case 'r':
- case 'R':
- case 'd':
- case 't':
- case 'g':
- case 'i':
- case 'k':
- case 'T':
- case 'P':
- case 'z':
+ case 'v':
+ case 's':
+ case 'a':
+ case 'm':
+ case 'r':
+ case 'R':
+ case 'd':
+ case 't':
+ case 'g':
+ case 'i':
+ case 'k':
+ case 'T':
+ case 'P':
value = nicenumtoull(optarg);
}
switch (opt) {
- case 'v':
+ case 'v':
zopt_vdevs = value;
break;
- case 's':
+ case 's':
zopt_vdev_size = MAX(SPA_MINDEVSIZE, value);
break;
- case 'a':
+ case 'a':
zopt_ashift = value;
break;
- case 'm':
+ case 'm':
zopt_mirrors = value;
break;
- case 'r':
+ case 'r':
zopt_raidz = MAX(1, value);
break;
- case 'R':
+ case 'R':
zopt_raidz_parity = MIN(MAX(value, 1), 2);
break;
- case 'd':
+ case 'd':
zopt_datasets = MAX(1, value);
break;
- case 't':
+ case 't':
zopt_threads = MAX(1, value);
break;
- case 'g':
- zio_gang_bang = MAX(SPA_MINBLOCKSIZE << 1, value);
+ case 'g':
+ metaslab_gang_bang = MAX(SPA_MINBLOCKSIZE << 1, value);
break;
- case 'i':
+ case 'i':
zopt_init = value;
break;
- case 'k':
+ case 'k':
zopt_killrate = value;
break;
- case 'p':
+ case 'p':
zopt_pool = strdup(optarg);
break;
- case 'f':
+ case 'f':
zopt_dir = strdup(optarg);
break;
- case 'V':
+ case 'V':
zopt_verbose++;
break;
- case 'E':
+ case 'E':
zopt_init = 0;
break;
- case 'T':
+ case 'T':
zopt_time = value;
break;
- case 'P':
+ case 'P':
zopt_passtime = MAX(1, value);
break;
- case 'z':
- zio_zil_fail_shift = MIN(value, 16);
- break;
- case 'h':
+ case 'h':
usage(B_TRUE);
break;
- case '?':
- default:
+ case '?':
+ default:
usage(B_FALSE);
break;
}
@@ -536,51 +539,58 @@ ztest_get_ashift(void)
}
static nvlist_t *
-make_vdev_file(size_t size)
+make_vdev_file(char *path, char *aux, size_t size, uint64_t ashift)
{
- char dev_name[MAXPATHLEN];
+ char pathbuf[MAXPATHLEN];
uint64_t vdev;
- uint64_t ashift = ztest_get_ashift();
- int fd;
nvlist_t *file;
- if (size == 0) {
- (void) snprintf(dev_name, sizeof (dev_name), "%s",
- "/dev/bogus");
- } else {
- vdev = ztest_shared->zs_vdev_primaries++;
- (void) sprintf(dev_name, ztest_dev_template,
- zopt_dir, zopt_pool, vdev);
+ if (ashift == 0)
+ ashift = ztest_get_ashift();
+
+ if (path == NULL) {
+ path = pathbuf;
+
+ if (aux != NULL) {
+ vdev = ztest_shared->zs_vdev_aux;
+ (void) sprintf(path, ztest_aux_template,
+ zopt_dir, zopt_pool, aux, vdev);
+ } else {
+ vdev = ztest_shared->zs_vdev_primaries++;
+ (void) sprintf(path, ztest_dev_template,
+ zopt_dir, zopt_pool, vdev);
+ }
+ }
- fd = open(dev_name, O_RDWR | O_CREAT | O_TRUNC, 0666);
+ if (size != 0) {
+ int fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0666);
if (fd == -1)
- fatal(1, "can't open %s", dev_name);
+ fatal(1, "can't open %s", path);
if (ftruncate(fd, size) != 0)
- fatal(1, "can't ftruncate %s", dev_name);
+ fatal(1, "can't ftruncate %s", path);
(void) close(fd);
}
VERIFY(nvlist_alloc(&file, NV_UNIQUE_NAME, 0) == 0);
VERIFY(nvlist_add_string(file, ZPOOL_CONFIG_TYPE, VDEV_TYPE_FILE) == 0);
- VERIFY(nvlist_add_string(file, ZPOOL_CONFIG_PATH, dev_name) == 0);
+ VERIFY(nvlist_add_string(file, ZPOOL_CONFIG_PATH, path) == 0);
VERIFY(nvlist_add_uint64(file, ZPOOL_CONFIG_ASHIFT, ashift) == 0);
return (file);
}
static nvlist_t *
-make_vdev_raidz(size_t size, int r)
+make_vdev_raidz(char *path, char *aux, size_t size, uint64_t ashift, int r)
{
nvlist_t *raidz, **child;
int c;
if (r < 2)
- return (make_vdev_file(size));
-
+ return (make_vdev_file(path, aux, size, ashift));
child = umem_alloc(r * sizeof (nvlist_t *), UMEM_NOFAIL);
for (c = 0; c < r; c++)
- child[c] = make_vdev_file(size);
+ child[c] = make_vdev_file(path, aux, size, ashift);
VERIFY(nvlist_alloc(&raidz, NV_UNIQUE_NAME, 0) == 0);
VERIFY(nvlist_add_string(raidz, ZPOOL_CONFIG_TYPE,
@@ -599,18 +609,19 @@ make_vdev_raidz(size_t size, int r)
}
static nvlist_t *
-make_vdev_mirror(size_t size, int r, int m)
+make_vdev_mirror(char *path, char *aux, size_t size, uint64_t ashift,
+ int r, int m)
{
nvlist_t *mirror, **child;
int c;
if (m < 1)
- return (make_vdev_raidz(size, r));
+ return (make_vdev_raidz(path, aux, size, ashift, r));
child = umem_alloc(m * sizeof (nvlist_t *), UMEM_NOFAIL);
for (c = 0; c < m; c++)
- child[c] = make_vdev_raidz(size, r);
+ child[c] = make_vdev_raidz(path, aux, size, ashift, r);
VERIFY(nvlist_alloc(&mirror, NV_UNIQUE_NAME, 0) == 0);
VERIFY(nvlist_add_string(mirror, ZPOOL_CONFIG_TYPE,
@@ -627,7 +638,8 @@ make_vdev_mirror(size_t size, int r, int m)
}
static nvlist_t *
-make_vdev_root(size_t size, int r, int m, int t)
+make_vdev_root(char *path, char *aux, size_t size, uint64_t ashift,
+ int log, int r, int m, int t)
{
nvlist_t *root, **child;
int c;
@@ -636,12 +648,15 @@ make_vdev_root(size_t size, int r, int m, int t)
child = umem_alloc(t * sizeof (nvlist_t *), UMEM_NOFAIL);
- for (c = 0; c < t; c++)
- child[c] = make_vdev_mirror(size, r, m);
+ for (c = 0; c < t; c++) {
+ child[c] = make_vdev_mirror(path, aux, size, ashift, r, m);
+ VERIFY(nvlist_add_uint64(child[c], ZPOOL_CONFIG_IS_LOG,
+ log) == 0);
+ }
VERIFY(nvlist_alloc(&root, NV_UNIQUE_NAME, 0) == 0);
VERIFY(nvlist_add_string(root, ZPOOL_CONFIG_TYPE, VDEV_TYPE_ROOT) == 0);
- VERIFY(nvlist_add_nvlist_array(root, ZPOOL_CONFIG_CHILDREN,
+ VERIFY(nvlist_add_nvlist_array(root, aux ? aux : ZPOOL_CONFIG_CHILDREN,
child, t) == 0);
for (c = 0; c < t; c++)
@@ -785,8 +800,8 @@ ztest_spa_create_destroy(ztest_args_t *za)
/*
* Attempt to create using a bad file.
*/
- nvroot = make_vdev_root(0, 0, 0, 1);
- error = spa_create("ztest_bad_file", nvroot, NULL);
+ nvroot = make_vdev_root("/dev/bogus", NULL, 0, 0, 0, 0, 0, 1);
+ error = spa_create("ztest_bad_file", nvroot, NULL, NULL, NULL);
nvlist_free(nvroot);
if (error != ENOENT)
fatal(0, "spa_create(bad_file) = %d", error);
@@ -794,8 +809,8 @@ ztest_spa_create_destroy(ztest_args_t *za)
/*
* Attempt to create using a bad mirror.
*/
- nvroot = make_vdev_root(0, 0, 2, 1);
- error = spa_create("ztest_bad_mirror", nvroot, NULL);
+ nvroot = make_vdev_root("/dev/bogus", NULL, 0, 0, 0, 0, 2, 1);
+ error = spa_create("ztest_bad_mirror", nvroot, NULL, NULL, NULL);
nvlist_free(nvroot);
if (error != ENOENT)
fatal(0, "spa_create(bad_mirror) = %d", error);
@@ -805,8 +820,8 @@ ztest_spa_create_destroy(ztest_args_t *za)
* what's in the nvroot; we should fail with EEXIST.
*/
(void) rw_rdlock(&ztest_shared->zs_name_lock);
- nvroot = make_vdev_root(0, 0, 0, 1);
- error = spa_create(za->za_pool, nvroot, NULL);
+ nvroot = make_vdev_root("/dev/bogus", NULL, 0, 0, 0, 0, 0, 1);
+ error = spa_create(za->za_pool, nvroot, NULL, NULL, NULL);
nvlist_free(nvroot);
if (error != EEXIST)
fatal(0, "spa_create(whatever) = %d", error);
@@ -823,30 +838,48 @@ ztest_spa_create_destroy(ztest_args_t *za)
(void) rw_unlock(&ztest_shared->zs_name_lock);
}
+static vdev_t *
+vdev_lookup_by_path(vdev_t *vd, const char *path)
+{
+ vdev_t *mvd;
+
+ if (vd->vdev_path != NULL && strcmp(path, vd->vdev_path) == 0)
+ return (vd);
+
+ for (int c = 0; c < vd->vdev_children; c++)
+ if ((mvd = vdev_lookup_by_path(vd->vdev_child[c], path)) !=
+ NULL)
+ return (mvd);
+
+ return (NULL);
+}
+
/*
* Verify that vdev_add() works as expected.
*/
void
ztest_vdev_add_remove(ztest_args_t *za)
{
- spa_t *spa = dmu_objset_spa(za->za_os);
+ spa_t *spa = za->za_spa;
uint64_t leaves = MAX(zopt_mirrors, 1) * zopt_raidz;
nvlist_t *nvroot;
int error;
- if (zopt_verbose >= 6)
- (void) printf("adding vdev\n");
-
(void) mutex_lock(&ztest_shared->zs_vdev_lock);
- spa_config_enter(spa, RW_READER, FTAG);
+ spa_config_enter(spa, SCL_VDEV, FTAG, RW_READER);
ztest_shared->zs_vdev_primaries =
spa->spa_root_vdev->vdev_children * leaves;
- spa_config_exit(spa, FTAG);
+ spa_config_exit(spa, SCL_VDEV, FTAG);
+
+ /*
+ * Make 1/4 of the devices be log devices.
+ */
+ nvroot = make_vdev_root(NULL, NULL, zopt_vdev_size, 0,
+ ztest_random(4) == 0, zopt_raidz, zopt_mirrors, 1);
- nvroot = make_vdev_root(zopt_vdev_size, zopt_raidz, zopt_mirrors, 1);
error = spa_vdev_add(spa, nvroot);
nvlist_free(nvroot);
@@ -856,37 +889,86 @@ ztest_vdev_add_remove(ztest_args_t *za)
ztest_record_enospc("spa_vdev_add");
else if (error != 0)
fatal(0, "spa_vdev_add() = %d", error);
-
- if (zopt_verbose >= 6)
- (void) printf("spa_vdev_add = %d, as expected\n", error);
}
-static vdev_t *
-vdev_lookup_by_path(vdev_t *vd, const char *path)
+/*
+ * Verify that adding/removing aux devices (l2arc, hot spare) works as expected.
+ */
+void
+ztest_vdev_aux_add_remove(ztest_args_t *za)
{
- int c;
- vdev_t *mvd;
+ spa_t *spa = za->za_spa;
+ vdev_t *rvd = spa->spa_root_vdev;
+ spa_aux_vdev_t *sav;
+ char *aux;
+ uint64_t guid = 0;
+ int error;
- if (vd->vdev_path != NULL) {
- if (vd->vdev_wholedisk == 1) {
- /*
- * For whole disks, the internal path has 's0', but the
- * path passed in by the user doesn't.
- */
- if (strlen(path) == strlen(vd->vdev_path) - 2 &&
- strncmp(path, vd->vdev_path, strlen(path)) == 0)
- return (vd);
- } else if (strcmp(path, vd->vdev_path) == 0) {
- return (vd);
+ if (ztest_random(2) == 0) {
+ sav = &spa->spa_spares;
+ aux = ZPOOL_CONFIG_SPARES;
+ } else {
+ sav = &spa->spa_l2cache;
+ aux = ZPOOL_CONFIG_L2CACHE;
+ }
+
+ (void) mutex_lock(&ztest_shared->zs_vdev_lock);
+
+ spa_config_enter(spa, SCL_VDEV, FTAG, RW_READER);
+
+ if (sav->sav_count != 0 && ztest_random(4) == 0) {
+ /*
+ * Pick a random device to remove.
+ */
+ guid = sav->sav_vdevs[ztest_random(sav->sav_count)]->vdev_guid;
+ } else {
+ /*
+ * Find an unused device we can add.
+ */
+ ztest_shared->zs_vdev_aux = 0;
+ for (;;) {
+ char path[MAXPATHLEN];
+ int c;
+ (void) sprintf(path, ztest_aux_template, zopt_dir,
+ zopt_pool, aux, ztest_shared->zs_vdev_aux);
+ for (c = 0; c < sav->sav_count; c++)
+ if (strcmp(sav->sav_vdevs[c]->vdev_path,
+ path) == 0)
+ break;
+ if (c == sav->sav_count &&
+ vdev_lookup_by_path(rvd, path) == NULL)
+ break;
+ ztest_shared->zs_vdev_aux++;
}
}
- for (c = 0; c < vd->vdev_children; c++)
- if ((mvd = vdev_lookup_by_path(vd->vdev_child[c], path)) !=
- NULL)
- return (mvd);
+ spa_config_exit(spa, SCL_VDEV, FTAG);
- return (NULL);
+ if (guid == 0) {
+ /*
+ * Add a new device.
+ */
+ nvlist_t *nvroot = make_vdev_root(NULL, aux,
+ (zopt_vdev_size * 5) / 4, 0, 0, 0, 0, 1);
+ error = spa_vdev_add(spa, nvroot);
+ if (error != 0)
+ fatal(0, "spa_vdev_add(%p) = %d", nvroot, error);
+ nvlist_free(nvroot);
+ } else {
+ /*
+ * Remove an existing device. Sometimes, dirty its
+ * vdev state first to make sure we handle removal
+ * of devices that have pending state changes.
+ */
+ if (ztest_random(2) == 0)
+ (void) vdev_online(spa, guid, B_FALSE, NULL);
+
+ error = spa_vdev_remove(spa, guid, B_FALSE);
+ if (error != 0 && error != EBUSY)
+ fatal(0, "spa_vdev_remove(%llu) = %d", guid, error);
+ }
+
+ (void) mutex_unlock(&ztest_shared->zs_vdev_lock);
}
/*
@@ -895,22 +977,26 @@ vdev_lookup_by_path(vdev_t *vd, const char *path)
void
ztest_vdev_attach_detach(ztest_args_t *za)
{
- spa_t *spa = dmu_objset_spa(za->za_os);
+ spa_t *spa = za->za_spa;
+ spa_aux_vdev_t *sav = &spa->spa_spares;
vdev_t *rvd = spa->spa_root_vdev;
vdev_t *oldvd, *newvd, *pvd;
- nvlist_t *root, *file;
+ nvlist_t *root;
uint64_t leaves = MAX(zopt_mirrors, 1) * zopt_raidz;
uint64_t leaf, top;
uint64_t ashift = ztest_get_ashift();
+ uint64_t oldguid;
size_t oldsize, newsize;
char oldpath[MAXPATHLEN], newpath[MAXPATHLEN];
int replacing;
+ int oldvd_has_siblings = B_FALSE;
+ int newvd_is_spare = B_FALSE;
+ int oldvd_is_log;
int error, expected_error;
- int fd;
(void) mutex_lock(&ztest_shared->zs_vdev_lock);
- spa_config_enter(spa, RW_READER, FTAG);
+ spa_config_enter(spa, SCL_VDEV, FTAG, RW_READER);
/*
* Decide whether to do an attach or a replace.
@@ -928,39 +1014,70 @@ ztest_vdev_attach_detach(ztest_args_t *za)
leaf = ztest_random(leaves);
/*
- * Generate the path to this leaf. The filename will end with 'a'.
- * We'll alternate replacements with a filename that ends with 'b'.
+ * Locate this vdev.
*/
- (void) snprintf(oldpath, sizeof (oldpath),
- ztest_dev_template, zopt_dir, zopt_pool, top * leaves + leaf);
-
- bcopy(oldpath, newpath, MAXPATHLEN);
+ oldvd = rvd->vdev_child[top];
+ if (zopt_mirrors >= 1)
+ oldvd = oldvd->vdev_child[leaf / zopt_raidz];
+ if (zopt_raidz > 1)
+ oldvd = oldvd->vdev_child[leaf % zopt_raidz];
/*
- * If the 'a' file isn't part of the pool, the 'b' file must be.
+ * If we're already doing an attach or replace, oldvd may be a
+ * mirror vdev -- in which case, pick a random child.
*/
- if (vdev_lookup_by_path(rvd, oldpath) == NULL)
- oldpath[strlen(oldpath) - 1] = 'b';
- else
- newpath[strlen(newpath) - 1] = 'b';
+ while (oldvd->vdev_children != 0) {
+ oldvd_has_siblings = B_TRUE;
+ ASSERT(oldvd->vdev_children == 2);
+ oldvd = oldvd->vdev_child[ztest_random(2)];
+ }
+
+ oldguid = oldvd->vdev_guid;
+ oldsize = vdev_get_rsize(oldvd);
+ oldvd_is_log = oldvd->vdev_top->vdev_islog;
+ (void) strcpy(oldpath, oldvd->vdev_path);
+ pvd = oldvd->vdev_parent;
/*
- * Now oldpath represents something that's already in the pool,
- * and newpath is the thing we'll try to attach.
+ * If oldvd has siblings, then half of the time, detach it.
*/
- oldvd = vdev_lookup_by_path(rvd, oldpath);
- newvd = vdev_lookup_by_path(rvd, newpath);
- ASSERT(oldvd != NULL);
- pvd = oldvd->vdev_parent;
+ if (oldvd_has_siblings && ztest_random(2) == 0) {
+ spa_config_exit(spa, SCL_VDEV, FTAG);
+ error = spa_vdev_detach(spa, oldguid, B_FALSE);
+ if (error != 0 && error != ENODEV && error != EBUSY)
+ fatal(0, "detach (%s) returned %d",
+ oldpath, error);
+ (void) mutex_unlock(&ztest_shared->zs_vdev_lock);
+ return;
+ }
/*
- * Make newsize a little bigger or smaller than oldsize.
- * If it's smaller, the attach should fail.
- * If it's larger, and we're doing a replace,
- * we should get dynamic LUN growth when we're done.
+ * For the new vdev, choose with equal probability between the two
+ * standard paths (ending in either 'a' or 'b') or a random hot spare.
*/
- oldsize = vdev_get_rsize(oldvd);
- newsize = 10 * oldsize / (9 + ztest_random(3));
+ if (sav->sav_count != 0 && ztest_random(3) == 0) {
+ newvd = sav->sav_vdevs[ztest_random(sav->sav_count)];
+ newvd_is_spare = B_TRUE;
+ (void) strcpy(newpath, newvd->vdev_path);
+ } else {
+ (void) snprintf(newpath, sizeof (newpath), ztest_dev_template,
+ zopt_dir, zopt_pool, top * leaves + leaf);
+ if (ztest_random(2) == 0)
+ newpath[strlen(newpath) - 1] = 'b';
+ newvd = vdev_lookup_by_path(rvd, newpath);
+ }
+
+ if (newvd) {
+ newsize = vdev_get_rsize(newvd);
+ } else {
+ /*
+ * Make newsize a little bigger or smaller than oldsize.
+ * If it's smaller, the attach should fail.
+ * If it's larger, and we're doing a replace,
+ * we should get dynamic LUN growth when we're done.
+ */
+ newsize = 10 * oldsize / (9 + ztest_random(3));
+ }
/*
* If pvd is not a mirror or root, the attach should fail with ENOTSUP,
@@ -970,12 +1087,17 @@ ztest_vdev_attach_detach(ztest_args_t *za)
*
* If newvd is too small, it should fail with EOVERFLOW.
*/
- if (newvd != NULL)
- expected_error = EBUSY;
- else if (pvd->vdev_ops != &vdev_mirror_ops &&
- pvd->vdev_ops != &vdev_root_ops &&
- (!replacing || pvd->vdev_ops == &vdev_replacing_ops))
+ if (pvd->vdev_ops != &vdev_mirror_ops &&
+ pvd->vdev_ops != &vdev_root_ops && (!replacing ||
+ pvd->vdev_ops == &vdev_replacing_ops ||
+ pvd->vdev_ops == &vdev_spare_ops))
+ expected_error = ENOTSUP;
+ else if (newvd_is_spare && (!replacing || oldvd_is_log))
expected_error = ENOTSUP;
+ else if (newvd == oldvd)
+ expected_error = replacing ? 0 : EBUSY;
+ else if (vdev_lookup_by_path(rvd, newpath) != NULL)
+ expected_error = EBUSY;
else if (newsize < oldsize)
expected_error = EOVERFLOW;
else if (ashift > oldvd->vdev_top->vdev_ashift)
@@ -983,36 +1105,16 @@ ztest_vdev_attach_detach(ztest_args_t *za)
else
expected_error = 0;
- /*
- * If newvd isn't already part of the pool, create it.
- */
- if (newvd == NULL) {
- fd = open(newpath, O_RDWR | O_CREAT | O_TRUNC, 0666);
- if (fd == -1)
- fatal(1, "can't open %s", newpath);
- if (ftruncate(fd, newsize) != 0)
- fatal(1, "can't ftruncate %s", newpath);
- (void) close(fd);
- }
-
- spa_config_exit(spa, FTAG);
+ spa_config_exit(spa, SCL_VDEV, FTAG);
/*
* Build the nvlist describing newpath.
*/
- VERIFY(nvlist_alloc(&file, NV_UNIQUE_NAME, 0) == 0);
- VERIFY(nvlist_add_string(file, ZPOOL_CONFIG_TYPE, VDEV_TYPE_FILE) == 0);
- VERIFY(nvlist_add_string(file, ZPOOL_CONFIG_PATH, newpath) == 0);
- VERIFY(nvlist_add_uint64(file, ZPOOL_CONFIG_ASHIFT, ashift) == 0);
+ root = make_vdev_root(newpath, NULL, newvd == NULL ? newsize : 0,
+ ashift, 0, 0, 0, 1);
- VERIFY(nvlist_alloc(&root, NV_UNIQUE_NAME, 0) == 0);
- VERIFY(nvlist_add_string(root, ZPOOL_CONFIG_TYPE, VDEV_TYPE_ROOT) == 0);
- VERIFY(nvlist_add_nvlist_array(root, ZPOOL_CONFIG_CHILDREN,
- &file, 1) == 0);
-
- error = spa_vdev_attach(spa, oldvd->vdev_guid, root, replacing);
+ error = spa_vdev_attach(spa, oldguid, root, replacing);
- nvlist_free(file);
nvlist_free(root);
/*
@@ -1027,12 +1129,15 @@ ztest_vdev_attach_detach(ztest_args_t *za)
/*
* If someone grew the LUN, the replacement may be too small.
*/
- if (error == EOVERFLOW)
+ if (error == EOVERFLOW || error == EBUSY)
expected_error = error;
- if (error != expected_error) {
- fatal(0, "attach (%s, %s, %d) returned %d, expected %d",
- oldpath, newpath, replacing, error, expected_error);
+ /* XXX workaround 6690467 */
+ if (error != expected_error && expected_error != EBUSY) {
+ fatal(0, "attach (%s %llu, %s %llu, %d) "
+ "returned %d, expected %d",
+ oldpath, (longlong_t)oldsize, newpath,
+ (longlong_t)newsize, replacing, error, expected_error);
}
(void) mutex_unlock(&ztest_shared->zs_vdev_lock);
@@ -1045,7 +1150,7 @@ ztest_vdev_attach_detach(ztest_args_t *za)
void
ztest_vdev_LUN_growth(ztest_args_t *za)
{
- spa_t *spa = dmu_objset_spa(za->za_os);
+ spa_t *spa = za->za_spa;
char dev_name[MAXPATHLEN];
uint64_t leaves = MAX(zopt_mirrors, 1) * zopt_raidz;
uint64_t vdev;
@@ -1057,9 +1162,9 @@ ztest_vdev_LUN_growth(ztest_args_t *za)
/*
* Pick a random leaf vdev.
*/
- spa_config_enter(spa, RW_READER, FTAG);
+ spa_config_enter(spa, SCL_VDEV, FTAG, RW_READER);
vdev = ztest_random(spa->spa_root_vdev->vdev_children * leaves);
- spa_config_exit(spa, FTAG);
+ spa_config_exit(spa, SCL_VDEV, FTAG);
(void) sprintf(dev_name, ztest_dev_template, zopt_dir, zopt_pool, vdev);
@@ -1088,14 +1193,14 @@ ztest_vdev_LUN_growth(ztest_args_t *za)
/* ARGSUSED */
static void
-ztest_create_cb(objset_t *os, void *arg, dmu_tx_t *tx)
+ztest_create_cb(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx)
{
/*
* Create the directory object.
*/
VERIFY(dmu_object_claim(os, ZTEST_DIROBJ,
DMU_OT_UINT64_OTHER, ZTEST_DIROBJ_BLOCKSIZE,
- DMU_OT_UINT64_OTHER, sizeof (ztest_block_tag_t), tx) == 0);
+ DMU_OT_UINT64_OTHER, 5 * sizeof (ztest_block_tag_t), tx) == 0);
VERIFY(zap_create_claim(os, ZTEST_MICROZAP_OBJ,
DMU_OT_ZAP_OTHER, DMU_OT_NONE, 0, tx) == 0);
@@ -1104,26 +1209,26 @@ ztest_create_cb(objset_t *os, void *arg, dmu_tx_t *tx)
DMU_OT_ZAP_OTHER, DMU_OT_NONE, 0, tx) == 0);
}
-/* ARGSUSED */
static int
ztest_destroy_cb(char *name, void *arg)
{
+ ztest_args_t *za = arg;
objset_t *os;
- dmu_object_info_t doi;
+ dmu_object_info_t *doi = &za->za_doi;
int error;
/*
* Verify that the dataset contains a directory object.
*/
error = dmu_objset_open(name, DMU_OST_OTHER,
- DS_MODE_STANDARD | DS_MODE_READONLY, &os);
+ DS_MODE_USER | DS_MODE_READONLY, &os);
ASSERT3U(error, ==, 0);
- error = dmu_object_info(os, ZTEST_DIROBJ, &doi);
+ error = dmu_object_info(os, ZTEST_DIROBJ, doi);
if (error != ENOENT) {
/* We could have crashed in the middle of destroying it */
ASSERT3U(error, ==, 0);
- ASSERT3U(doi.doi_type, ==, DMU_OT_UINT64_OTHER);
- ASSERT3S(doi.doi_physical_blks, >=, 0);
+ ASSERT3U(doi->doi_type, ==, DMU_OT_UINT64_OTHER);
+ ASSERT3S(doi->doi_physical_blks, >=, 0);
}
dmu_objset_close(os);
@@ -1131,7 +1236,11 @@ ztest_destroy_cb(char *name, void *arg)
* Destroy the dataset.
*/
error = dmu_objset_destroy(name);
- ASSERT3U(error, ==, 0);
+ if (error) {
+ (void) dmu_objset_open(name, DMU_OST_OTHER,
+ DS_MODE_USER | DS_MODE_READONLY, &os);
+ fatal(0, "dmu_objset_destroy(os=%p) = %d\n", &os, error);
+ }
return (0);
}
@@ -1171,9 +1280,9 @@ void
ztest_dmu_objset_create_destroy(ztest_args_t *za)
{
int error;
- objset_t *os;
+ objset_t *os, *os2;
char name[100];
- int mode, basemode, expected_error;
+ int basemode, expected_error;
zilog_t *zilog;
uint64_t seq;
uint64_t objects;
@@ -1183,9 +1292,9 @@ ztest_dmu_objset_create_destroy(ztest_args_t *za)
(void) snprintf(name, 100, "%s/%s_temp_%llu", za->za_pool, za->za_pool,
(u_longlong_t)za->za_instance);
- basemode = DS_MODE_LEVEL(za->za_instance);
- if (basemode == DS_MODE_NONE)
- basemode++;
+ basemode = DS_MODE_TYPE(za->za_instance);
+ if (basemode != DS_MODE_USER && basemode != DS_MODE_OWNER)
+ basemode = DS_MODE_USER;
/*
* If this dataset exists from a previous run, process its replay log
@@ -1193,9 +1302,9 @@ ztest_dmu_objset_create_destroy(ztest_args_t *za)
* (invoked from ztest_destroy_cb() below) should just throw it away.
*/
if (ztest_random(2) == 0 &&
- dmu_objset_open(name, DMU_OST_OTHER, DS_MODE_PRIMARY, &os) == 0) {
+ dmu_objset_open(name, DMU_OST_OTHER, DS_MODE_OWNER, &os) == 0) {
zr.zr_os = os;
- zil_replay(os, &zr, &zr.zr_assign, ztest_replay_vector);
+ zil_replay(os, &zr, &zr.zr_assign, ztest_replay_vector, NULL);
dmu_objset_close(os);
}
@@ -1204,7 +1313,7 @@ ztest_dmu_objset_create_destroy(ztest_args_t *za)
* create lying around from a previous run. If so, destroy it
* and all of its snapshots.
*/
- (void) dmu_objset_find(name, ztest_destroy_cb, NULL,
+ (void) dmu_objset_find(name, ztest_destroy_cb, za,
DS_FIND_CHILDREN | DS_FIND_SNAPSHOTS);
/*
@@ -1218,8 +1327,8 @@ ztest_dmu_objset_create_destroy(ztest_args_t *za)
/*
* Verify that we can create a new dataset.
*/
- error = dmu_objset_create(name, DMU_OST_OTHER, NULL, ztest_create_cb,
- NULL);
+ error = dmu_objset_create(name, DMU_OST_OTHER, NULL, 0,
+ ztest_create_cb, NULL);
if (error) {
if (error == ENOSPC) {
ztest_record_enospc("dmu_objset_create");
@@ -1274,26 +1383,29 @@ ztest_dmu_objset_create_destroy(ztest_args_t *za)
/*
* Verify that we cannot create an existing dataset.
*/
- error = dmu_objset_create(name, DMU_OST_OTHER, NULL, NULL, NULL);
+ error = dmu_objset_create(name, DMU_OST_OTHER, NULL, 0, NULL, NULL);
if (error != EEXIST)
fatal(0, "created existing dataset, error = %d", error);
/*
- * Verify that multiple dataset opens are allowed, but only when
+ * Verify that multiple dataset holds are allowed, but only when
* the new access mode is compatible with the base mode.
- * We use a mixture of typed and typeless opens, and when the
- * open succeeds, verify that the discovered type is correct.
- */
- for (mode = DS_MODE_STANDARD; mode < DS_MODE_LEVELS; mode++) {
- objset_t *os2;
- error = dmu_objset_open(name, DMU_OST_OTHER, mode, &os2);
- expected_error = (basemode + mode < DS_MODE_LEVELS) ? 0 : EBUSY;
- if (error != expected_error)
- fatal(0, "dmu_objset_open('%s') = %d, expected %d",
- name, error, expected_error);
- if (error == 0)
+ */
+ if (basemode == DS_MODE_OWNER) {
+ error = dmu_objset_open(name, DMU_OST_OTHER, DS_MODE_USER,
+ &os2);
+ if (error)
+ fatal(0, "dmu_objset_open('%s') = %d", name, error);
+ else
dmu_objset_close(os2);
}
+ error = dmu_objset_open(name, DMU_OST_OTHER, DS_MODE_OWNER, &os2);
+ expected_error = (basemode == DS_MODE_OWNER) ? EBUSY : 0;
+ if (error != expected_error)
+ fatal(0, "dmu_objset_open('%s') = %d, expected %d",
+ name, error, expected_error);
+ if (error == 0)
+ dmu_objset_close(os2);
zil_close(zilog);
dmu_objset_close(os);
@@ -1417,7 +1529,7 @@ ztest_blk_cb(traverse_blk_cache_t *bc, spa_t *spa, void *arg)
void
ztest_traverse(ztest_args_t *za)
{
- spa_t *spa = dmu_objset_spa(za->za_os);
+ spa_t *spa = za->za_spa;
traverse_handle_t *th = za->za_th;
int rc, advance;
uint64_t cbstart, cblimit;
@@ -1489,7 +1601,7 @@ ztest_dmu_object_alloc_free(ztest_args_t *za)
dmu_tx_t *tx;
uint64_t batchobj, object, batchsize, endoff, temp;
int b, c, error, bonuslen;
- dmu_object_info_t doi;
+ dmu_object_info_t *doi = &za->za_doi;
char osname[MAXNAMELEN];
dmu_objset_name(os, osname);
@@ -1500,7 +1612,7 @@ ztest_dmu_object_alloc_free(ztest_args_t *za)
/*
* Create a batch object if necessary, and record it in the directory.
*/
- VERIFY(0 == dmu_read(os, ZTEST_DIROBJ, za->za_diroff,
+ VERIFY3U(0, ==, dmu_read(os, ZTEST_DIROBJ, za->za_diroff,
sizeof (uint64_t), &batchobj));
if (batchobj == 0) {
tx = dmu_tx_create(os);
@@ -1525,7 +1637,7 @@ ztest_dmu_object_alloc_free(ztest_args_t *za)
* Destroy the previous batch of objects.
*/
for (b = 0; b < batchsize; b++) {
- VERIFY(0 == dmu_read(os, batchobj, b * sizeof (uint64_t),
+ VERIFY3U(0, ==, dmu_read(os, batchobj, b * sizeof (uint64_t),
sizeof (uint64_t), &object));
if (object == 0)
continue;
@@ -1534,13 +1646,14 @@ ztest_dmu_object_alloc_free(ztest_args_t *za)
* We expect the nth byte of the bonus buffer to be n.
*/
VERIFY(0 == dmu_bonus_hold(os, object, FTAG, &db));
+ za->za_dbuf = db;
- dmu_object_info_from_db(db, &doi);
- ASSERT(doi.doi_type == DMU_OT_UINT64_OTHER);
- ASSERT(doi.doi_bonus_type == DMU_OT_PLAIN_OTHER);
- ASSERT3S(doi.doi_physical_blks, >=, 0);
+ dmu_object_info_from_db(db, doi);
+ ASSERT(doi->doi_type == DMU_OT_UINT64_OTHER);
+ ASSERT(doi->doi_bonus_type == DMU_OT_PLAIN_OTHER);
+ ASSERT3S(doi->doi_physical_blks, >=, 0);
- bonuslen = db->db_size;
+ bonuslen = doi->doi_bonus_size;
for (c = 0; c < bonuslen; c++) {
if (((uint8_t *)db->db_data)[c] !=
@@ -1554,6 +1667,7 @@ ztest_dmu_object_alloc_free(ztest_args_t *za)
}
dmu_buf_rele(db, FTAG);
+ za->za_dbuf = NULL;
/*
* We expect the word at endoff to be our object number.
@@ -1658,8 +1772,9 @@ ztest_dmu_object_alloc_free(ztest_args_t *za)
/*
* Write to both the bonus buffer and the regular data.
*/
- VERIFY(0 == dmu_bonus_hold(os, object, FTAG, &db));
- ASSERT3U(bonuslen, ==, db->db_size);
+ VERIFY(dmu_bonus_hold(os, object, FTAG, &db) == 0);
+ za->za_dbuf = db;
+ ASSERT3U(bonuslen, <=, db->db_size);
dmu_object_size_from_db(db, &va_blksize, &va_nblocks);
ASSERT3S(va_nblocks, >=, 0);
@@ -1670,10 +1785,11 @@ ztest_dmu_object_alloc_free(ztest_args_t *za)
* See comments above regarding the contents of
* the bonus buffer and the word at endoff.
*/
- for (c = 0; c < db->db_size; c++)
+ for (c = 0; c < bonuslen; c++)
((uint8_t *)db->db_data)[c] = (uint8_t)(c + bonuslen);
dmu_buf_rele(db, FTAG);
+ za->za_dbuf = NULL;
/*
* Write to a large offset to increase indirection.
@@ -1928,226 +2044,240 @@ ztest_dmu_read_write(ztest_args_t *za)
}
void
-ztest_dmu_check_future_leak(objset_t *os, uint64_t txg)
+ztest_dmu_check_future_leak(ztest_args_t *za)
{
+ objset_t *os = za->za_os;
dmu_buf_t *db;
- ztest_block_tag_t rbt;
-
- if (zopt_verbose >= 3) {
- char osname[MAXNAMELEN];
- dmu_objset_name(os, osname);
- (void) printf("checking %s for future leaks in txg %lld...\n",
- osname, (u_longlong_t)txg);
- }
+ ztest_block_tag_t *bt;
+ dmu_object_info_t *doi = &za->za_doi;
/*
* Make sure that, if there is a write record in the bonus buffer
* of the ZTEST_DIROBJ, that the txg for this record is <= the
* last synced txg of the pool.
*/
-
- VERIFY(0 == dmu_bonus_hold(os, ZTEST_DIROBJ, FTAG, &db));
- ASSERT3U(db->db_size, ==, sizeof (rbt));
- bcopy(db->db_data, &rbt, db->db_size);
- if (rbt.bt_objset != 0) {
- ASSERT3U(rbt.bt_objset, ==, dmu_objset_id(os));
- ASSERT3U(rbt.bt_object, ==, ZTEST_DIROBJ);
- ASSERT3U(rbt.bt_offset, ==, -1ULL);
- if (rbt.bt_txg > txg) {
- fatal(0,
- "future leak: got %llx, last synced txg is %llx",
- rbt.bt_txg, txg);
- }
+ VERIFY(dmu_bonus_hold(os, ZTEST_DIROBJ, FTAG, &db) == 0);
+ za->za_dbuf = db;
+ VERIFY(dmu_object_info(os, ZTEST_DIROBJ, doi) == 0);
+ ASSERT3U(doi->doi_bonus_size, >=, sizeof (*bt));
+ ASSERT3U(doi->doi_bonus_size, <=, db->db_size);
+ ASSERT3U(doi->doi_bonus_size % sizeof (*bt), ==, 0);
+ bt = (void *)((char *)db->db_data + doi->doi_bonus_size - sizeof (*bt));
+ if (bt->bt_objset != 0) {
+ ASSERT3U(bt->bt_objset, ==, dmu_objset_id(os));
+ ASSERT3U(bt->bt_object, ==, ZTEST_DIROBJ);
+ ASSERT3U(bt->bt_offset, ==, -1ULL);
+ ASSERT3U(bt->bt_txg, <, spa_first_txg(za->za_spa));
}
dmu_buf_rele(db, FTAG);
+ za->za_dbuf = NULL;
}
void
ztest_dmu_write_parallel(ztest_args_t *za)
{
objset_t *os = za->za_os;
- dmu_tx_t *tx;
+ ztest_block_tag_t *rbt = &za->za_rbt;
+ ztest_block_tag_t *wbt = &za->za_wbt;
+ const size_t btsize = sizeof (ztest_block_tag_t);
dmu_buf_t *db;
- int i, b, error, do_free, bs;
- uint64_t off, txg_how, txg;
+ int b, error;
+ int bs = ZTEST_DIROBJ_BLOCKSIZE;
+ int do_free = 0;
+ uint64_t off, txg, txg_how;
mutex_t *lp;
char osname[MAXNAMELEN];
char iobuf[SPA_MAXBLOCKSIZE];
- ztest_block_tag_t rbt, wbt;
+ blkptr_t blk = { 0 };
+ uint64_t blkoff;
+ zbookmark_t zb;
+ dmu_tx_t *tx = dmu_tx_create(os);
dmu_objset_name(os, osname);
- bs = ZTEST_DIROBJ_BLOCKSIZE;
/*
* Have multiple threads write to large offsets in ZTEST_DIROBJ
* to verify that having multiple threads writing to the same object
* in parallel doesn't cause any trouble.
- * Also do parallel writes to the bonus buffer on occasion.
*/
- for (i = 0; i < 50; i++) {
+ if (ztest_random(4) == 0) {
+ /*
+ * Do the bonus buffer instead of a regular block.
+ * We need a lock to serialize resize vs. others,
+ * so we hash on the objset ID.
+ */
+ b = dmu_objset_id(os) % ZTEST_SYNC_LOCKS;
+ off = -1ULL;
+ dmu_tx_hold_bonus(tx, ZTEST_DIROBJ);
+ } else {
b = ztest_random(ZTEST_SYNC_LOCKS);
- lp = &ztest_shared->zs_sync_lock[b];
-
- do_free = (ztest_random(4) == 0);
-
- off = za->za_diroff_shared + ((uint64_t)b << SPA_MAXBLOCKSHIFT);
-
+ off = za->za_diroff_shared + (b << SPA_MAXBLOCKSHIFT);
if (ztest_random(4) == 0) {
- /*
- * Do the bonus buffer instead of a regular block.
- */
- do_free = 0;
- off = -1ULL;
- }
-
- tx = dmu_tx_create(os);
-
- if (off == -1ULL)
- dmu_tx_hold_bonus(tx, ZTEST_DIROBJ);
- else if (do_free)
+ do_free = 1;
dmu_tx_hold_free(tx, ZTEST_DIROBJ, off, bs);
- else
+ } else {
dmu_tx_hold_write(tx, ZTEST_DIROBJ, off, bs);
+ }
+ }
- txg_how = ztest_random(2) == 0 ? TXG_WAIT : TXG_NOWAIT;
- error = dmu_tx_assign(tx, txg_how);
- if (error) {
- if (error == ERESTART) {
- ASSERT(txg_how == TXG_NOWAIT);
- dmu_tx_wait(tx);
- dmu_tx_abort(tx);
- continue;
- }
- dmu_tx_abort(tx);
+ txg_how = ztest_random(2) == 0 ? TXG_WAIT : TXG_NOWAIT;
+ error = dmu_tx_assign(tx, txg_how);
+ if (error) {
+ if (error == ERESTART) {
+ ASSERT(txg_how == TXG_NOWAIT);
+ dmu_tx_wait(tx);
+ } else {
ztest_record_enospc("dmu write parallel");
- return;
}
- txg = dmu_tx_get_txg(tx);
+ dmu_tx_abort(tx);
+ return;
+ }
+ txg = dmu_tx_get_txg(tx);
- if (do_free) {
- (void) mutex_lock(lp);
- VERIFY(0 == dmu_free_range(os, ZTEST_DIROBJ, off,
- bs, tx));
- (void) mutex_unlock(lp);
- dmu_tx_commit(tx);
- continue;
+ lp = &ztest_shared->zs_sync_lock[b];
+ (void) mutex_lock(lp);
+
+ wbt->bt_objset = dmu_objset_id(os);
+ wbt->bt_object = ZTEST_DIROBJ;
+ wbt->bt_offset = off;
+ wbt->bt_txg = txg;
+ wbt->bt_thread = za->za_instance;
+ wbt->bt_seq = ztest_shared->zs_seq[b]++; /* protected by lp */
+
+ /*
+ * Occasionally, write an all-zero block to test the behavior
+ * of blocks that compress into holes.
+ */
+ if (off != -1ULL && ztest_random(8) == 0)
+ bzero(wbt, btsize);
+
+ if (off == -1ULL) {
+ dmu_object_info_t *doi = &za->za_doi;
+ char *dboff;
+
+ VERIFY(dmu_bonus_hold(os, ZTEST_DIROBJ, FTAG, &db) == 0);
+ za->za_dbuf = db;
+ dmu_object_info_from_db(db, doi);
+ ASSERT3U(doi->doi_bonus_size, <=, db->db_size);
+ ASSERT3U(doi->doi_bonus_size, >=, btsize);
+ ASSERT3U(doi->doi_bonus_size % btsize, ==, 0);
+ dboff = (char *)db->db_data + doi->doi_bonus_size - btsize;
+ bcopy(dboff, rbt, btsize);
+ if (rbt->bt_objset != 0) {
+ ASSERT3U(rbt->bt_objset, ==, wbt->bt_objset);
+ ASSERT3U(rbt->bt_object, ==, wbt->bt_object);
+ ASSERT3U(rbt->bt_offset, ==, wbt->bt_offset);
+ ASSERT3U(rbt->bt_txg, <=, wbt->bt_txg);
}
-
- wbt.bt_objset = dmu_objset_id(os);
- wbt.bt_object = ZTEST_DIROBJ;
- wbt.bt_offset = off;
- wbt.bt_txg = txg;
- wbt.bt_thread = za->za_instance;
-
- if (off == -1ULL) {
- wbt.bt_seq = 0;
- VERIFY(0 == dmu_bonus_hold(os, ZTEST_DIROBJ,
- FTAG, &db));
- ASSERT3U(db->db_size, ==, sizeof (wbt));
- bcopy(db->db_data, &rbt, db->db_size);
- if (rbt.bt_objset != 0) {
- ASSERT3U(rbt.bt_objset, ==, wbt.bt_objset);
- ASSERT3U(rbt.bt_object, ==, wbt.bt_object);
- ASSERT3U(rbt.bt_offset, ==, wbt.bt_offset);
- ASSERT3U(rbt.bt_txg, <=, wbt.bt_txg);
- }
- dmu_buf_will_dirty(db, tx);
- bcopy(&wbt, db->db_data, db->db_size);
- dmu_buf_rele(db, FTAG);
- dmu_tx_commit(tx);
- continue;
+ if (ztest_random(10) == 0) {
+ int newsize = (ztest_random(db->db_size /
+ btsize) + 1) * btsize;
+
+ ASSERT3U(newsize, >=, btsize);
+ ASSERT3U(newsize, <=, db->db_size);
+ VERIFY3U(dmu_set_bonus(db, newsize, tx), ==, 0);
+ dboff = (char *)db->db_data + newsize - btsize;
}
+ dmu_buf_will_dirty(db, tx);
+ bcopy(wbt, dboff, btsize);
+ dmu_buf_rele(db, FTAG);
+ za->za_dbuf = NULL;
+ } else if (do_free) {
+ VERIFY(dmu_free_range(os, ZTEST_DIROBJ, off, bs, tx) == 0);
+ } else {
+ dmu_write(os, ZTEST_DIROBJ, off, btsize, wbt, tx);
+ }
- (void) mutex_lock(lp);
+ (void) mutex_unlock(lp);
- wbt.bt_seq = ztest_shared->zs_seq[b]++;
+ if (ztest_random(1000) == 0)
+ (void) poll(NULL, 0, 1); /* open dn_notxholds window */
- dmu_write(os, ZTEST_DIROBJ, off, sizeof (wbt), &wbt, tx);
+ dmu_tx_commit(tx);
+
+ if (ztest_random(10000) == 0)
+ txg_wait_synced(dmu_objset_pool(os), txg);
+
+ if (off == -1ULL || do_free)
+ return;
+ if (ztest_random(2) != 0)
+ return;
+
+ /*
+ * dmu_sync() the block we just wrote.
+ */
+ (void) mutex_lock(lp);
+
+ blkoff = P2ALIGN_TYPED(off, bs, uint64_t);
+ error = dmu_buf_hold(os, ZTEST_DIROBJ, blkoff, FTAG, &db);
+ za->za_dbuf = db;
+ if (error) {
+ dprintf("dmu_buf_hold(%s, %d, %llx) = %d\n",
+ osname, ZTEST_DIROBJ, blkoff, error);
(void) mutex_unlock(lp);
+ return;
+ }
+ blkoff = off - blkoff;
+ error = dmu_sync(NULL, db, &blk, txg, NULL, NULL);
+ dmu_buf_rele(db, FTAG);
+ za->za_dbuf = NULL;
- if (ztest_random(100) == 0)
- (void) poll(NULL, 0, 1); /* open dn_notxholds window */
+ (void) mutex_unlock(lp);
- dmu_tx_commit(tx);
+ if (error) {
+ dprintf("dmu_sync(%s, %d, %llx) = %d\n",
+ osname, ZTEST_DIROBJ, off, error);
+ return;
+ }
- if (ztest_random(1000) == 0)
- txg_wait_synced(dmu_objset_pool(os), txg);
-
- if (ztest_random(2) == 0) {
- blkptr_t blk = { 0 };
- uint64_t blkoff;
- zbookmark_t zb;
-
- (void) mutex_lock(lp);
- blkoff = P2ALIGN_TYPED(off, bs, uint64_t);
- error = dmu_buf_hold(os,
- ZTEST_DIROBJ, blkoff, FTAG, &db);
- if (error) {
- dprintf("dmu_buf_hold(%s, %d, %llx) = %d\n",
- osname, ZTEST_DIROBJ, blkoff, error);
- (void) mutex_unlock(lp);
- continue;
- }
- blkoff = off - blkoff;
- error = dmu_sync(NULL, db, &blk, txg, NULL, NULL);
- dmu_buf_rele(db, FTAG);
- (void) mutex_unlock(lp);
- if (error) {
- dprintf("dmu_sync(%s, %d, %llx) = %d\n",
- osname, ZTEST_DIROBJ, off, error);
- continue;
- }
+ if (blk.blk_birth == 0) /* concurrent free */
+ return;
- if (blk.blk_birth == 0) { /* concurrent free */
- continue;
- }
- txg_suspend(dmu_objset_pool(os));
+ txg_suspend(dmu_objset_pool(os));
- ASSERT(blk.blk_fill == 1);
- ASSERT3U(BP_GET_TYPE(&blk), ==, DMU_OT_UINT64_OTHER);
- ASSERT3U(BP_GET_LEVEL(&blk), ==, 0);
- ASSERT3U(BP_GET_LSIZE(&blk), ==, bs);
+ ASSERT(blk.blk_fill == 1);
+ ASSERT3U(BP_GET_TYPE(&blk), ==, DMU_OT_UINT64_OTHER);
+ ASSERT3U(BP_GET_LEVEL(&blk), ==, 0);
+ ASSERT3U(BP_GET_LSIZE(&blk), ==, bs);
- /*
- * Read the block that dmu_sync() returned to
- * make sure its contents match what we wrote.
- * We do this while still txg_suspend()ed to ensure
- * that the block can't be reused before we read it.
- */
- zb.zb_objset = dmu_objset_id(os);
- zb.zb_object = ZTEST_DIROBJ;
- zb.zb_level = 0;
- zb.zb_blkid = off / bs;
- error = zio_wait(zio_read(NULL, dmu_objset_spa(os),
- &blk, iobuf, bs, NULL, NULL,
- ZIO_PRIORITY_SYNC_READ, ZIO_FLAG_MUSTSUCCEED, &zb));
- ASSERT(error == 0);
+ /*
+ * Read the block that dmu_sync() returned to make sure its contents
+ * match what we wrote. We do this while still txg_suspend()ed
+ * to ensure that the block can't be reused before we read it.
+ */
+ zb.zb_objset = dmu_objset_id(os);
+ zb.zb_object = ZTEST_DIROBJ;
+ zb.zb_level = 0;
+ zb.zb_blkid = off / bs;
+ error = zio_wait(zio_read(NULL, za->za_spa, &blk, iobuf, bs,
+ NULL, NULL, ZIO_PRIORITY_SYNC_READ, ZIO_FLAG_MUSTSUCCEED, &zb));
+ ASSERT3U(error, ==, 0);
- txg_resume(dmu_objset_pool(os));
+ txg_resume(dmu_objset_pool(os));
- bcopy(&iobuf[blkoff], &rbt, sizeof (rbt));
+ bcopy(&iobuf[blkoff], rbt, btsize);
- if (rbt.bt_objset == 0) /* concurrent free */
- continue;
+ if (rbt->bt_objset == 0) /* concurrent free */
+ return;
- ASSERT3U(rbt.bt_objset, ==, wbt.bt_objset);
- ASSERT3U(rbt.bt_object, ==, wbt.bt_object);
- ASSERT3U(rbt.bt_offset, ==, wbt.bt_offset);
+ if (wbt->bt_objset == 0) /* all-zero overwrite */
+ return;
- /*
- * The semantic of dmu_sync() is that we always
- * push the most recent version of the data,
- * so in the face of concurrent updates we may
- * see a newer version of the block. That's OK.
- */
- ASSERT3U(rbt.bt_txg, >=, wbt.bt_txg);
- if (rbt.bt_thread == wbt.bt_thread)
- ASSERT3U(rbt.bt_seq, ==, wbt.bt_seq);
- else
- ASSERT3U(rbt.bt_seq, >, wbt.bt_seq);
- }
- }
+ ASSERT3U(rbt->bt_objset, ==, wbt->bt_objset);
+ ASSERT3U(rbt->bt_object, ==, wbt->bt_object);
+ ASSERT3U(rbt->bt_offset, ==, wbt->bt_offset);
+
+ /*
+ * The semantic of dmu_sync() is that we always push the most recent
+ * version of the data, so in the face of concurrent updates we may
+ * see a newer version of the block. That's OK.
+ */
+ ASSERT3U(rbt->bt_txg, >=, wbt->bt_txg);
+ if (rbt->bt_thread == wbt->bt_thread)
+ ASSERT3U(rbt->bt_seq, ==, wbt->bt_seq);
+ else
+ ASSERT3U(rbt->bt_seq, >, wbt->bt_seq);
}
/*
@@ -2166,7 +2296,6 @@ ztest_zap(ztest_args_t *za)
uint64_t value[ZTEST_ZAP_MAX_INTS];
uint64_t zl_ints, zl_intsize, prop;
int i, ints;
- int iters = 100;
dmu_tx_t *tx;
char propname[100], txgname[100];
int error;
@@ -2230,122 +2359,113 @@ ztest_zap(ztest_args_t *za)
ints = MAX(ZTEST_ZAP_MIN_INTS, object % ZTEST_ZAP_MAX_INTS);
- while (--iters >= 0) {
- prop = ztest_random(ZTEST_ZAP_MAX_PROPS);
- (void) sprintf(propname, "prop_%llu", (u_longlong_t)prop);
- (void) sprintf(txgname, "txg_%llu", (u_longlong_t)prop);
- bzero(value, sizeof (value));
- last_txg = 0;
-
- /*
- * If these zap entries already exist, validate their contents.
- */
- error = zap_length(os, object, txgname, &zl_intsize, &zl_ints);
- if (error == 0) {
- ASSERT3U(zl_intsize, ==, sizeof (uint64_t));
- ASSERT3U(zl_ints, ==, 1);
-
- error = zap_lookup(os, object, txgname, zl_intsize,
- zl_ints, &last_txg);
+ prop = ztest_random(ZTEST_ZAP_MAX_PROPS);
+ (void) sprintf(propname, "prop_%llu", (u_longlong_t)prop);
+ (void) sprintf(txgname, "txg_%llu", (u_longlong_t)prop);
+ bzero(value, sizeof (value));
+ last_txg = 0;
- ASSERT3U(error, ==, 0);
+ /*
+ * If these zap entries already exist, validate their contents.
+ */
+ error = zap_length(os, object, txgname, &zl_intsize, &zl_ints);
+ if (error == 0) {
+ ASSERT3U(zl_intsize, ==, sizeof (uint64_t));
+ ASSERT3U(zl_ints, ==, 1);
- error = zap_length(os, object, propname, &zl_intsize,
- &zl_ints);
+ VERIFY(zap_lookup(os, object, txgname, zl_intsize,
+ zl_ints, &last_txg) == 0);
- ASSERT3U(error, ==, 0);
- ASSERT3U(zl_intsize, ==, sizeof (uint64_t));
- ASSERT3U(zl_ints, ==, ints);
+ VERIFY(zap_length(os, object, propname, &zl_intsize,
+ &zl_ints) == 0);
- error = zap_lookup(os, object, propname, zl_intsize,
- zl_ints, value);
+ ASSERT3U(zl_intsize, ==, sizeof (uint64_t));
+ ASSERT3U(zl_ints, ==, ints);
- ASSERT3U(error, ==, 0);
+ VERIFY(zap_lookup(os, object, propname, zl_intsize,
+ zl_ints, value) == 0);
- for (i = 0; i < ints; i++) {
- ASSERT3U(value[i], ==, last_txg + object + i);
- }
- } else {
- ASSERT3U(error, ==, ENOENT);
+ for (i = 0; i < ints; i++) {
+ ASSERT3U(value[i], ==, last_txg + object + i);
}
+ } else {
+ ASSERT3U(error, ==, ENOENT);
+ }
- /*
- * Atomically update two entries in our zap object.
- * The first is named txg_%llu, and contains the txg
- * in which the property was last updated. The second
- * is named prop_%llu, and the nth element of its value
- * should be txg + object + n.
- */
- tx = dmu_tx_create(os);
- dmu_tx_hold_zap(tx, object, TRUE, NULL);
- error = dmu_tx_assign(tx, TXG_WAIT);
- if (error) {
- ztest_record_enospc("create zap entry");
- dmu_tx_abort(tx);
- return;
- }
- txg = dmu_tx_get_txg(tx);
+ /*
+ * Atomically update two entries in our zap object.
+ * The first is named txg_%llu, and contains the txg
+ * in which the property was last updated. The second
+ * is named prop_%llu, and the nth element of its value
+ * should be txg + object + n.
+ */
+ tx = dmu_tx_create(os);
+ dmu_tx_hold_zap(tx, object, TRUE, NULL);
+ error = dmu_tx_assign(tx, TXG_WAIT);
+ if (error) {
+ ztest_record_enospc("create zap entry");
+ dmu_tx_abort(tx);
+ return;
+ }
+ txg = dmu_tx_get_txg(tx);
- if (last_txg > txg)
- fatal(0, "zap future leak: old %llu new %llu",
- last_txg, txg);
+ if (last_txg > txg)
+ fatal(0, "zap future leak: old %llu new %llu", last_txg, txg);
- for (i = 0; i < ints; i++)
- value[i] = txg + object + i;
+ for (i = 0; i < ints; i++)
+ value[i] = txg + object + i;
- error = zap_update(os, object, txgname, sizeof (uint64_t),
- 1, &txg, tx);
- if (error)
- fatal(0, "zap_update('%s', %llu, '%s') = %d",
- osname, object, txgname, error);
+ error = zap_update(os, object, txgname, sizeof (uint64_t), 1, &txg, tx);
+ if (error)
+ fatal(0, "zap_update('%s', %llu, '%s') = %d",
+ osname, object, txgname, error);
- error = zap_update(os, object, propname, sizeof (uint64_t),
- ints, value, tx);
- if (error)
- fatal(0, "zap_update('%s', %llu, '%s') = %d",
- osname, object, propname, error);
+ error = zap_update(os, object, propname, sizeof (uint64_t),
+ ints, value, tx);
+ if (error)
+ fatal(0, "zap_update('%s', %llu, '%s') = %d",
+ osname, object, propname, error);
- dmu_tx_commit(tx);
+ dmu_tx_commit(tx);
- /*
- * Remove a random pair of entries.
- */
- prop = ztest_random(ZTEST_ZAP_MAX_PROPS);
- (void) sprintf(propname, "prop_%llu", (u_longlong_t)prop);
- (void) sprintf(txgname, "txg_%llu", (u_longlong_t)prop);
+ /*
+ * Remove a random pair of entries.
+ */
+ prop = ztest_random(ZTEST_ZAP_MAX_PROPS);
+ (void) sprintf(propname, "prop_%llu", (u_longlong_t)prop);
+ (void) sprintf(txgname, "txg_%llu", (u_longlong_t)prop);
- error = zap_length(os, object, txgname, &zl_intsize, &zl_ints);
+ error = zap_length(os, object, txgname, &zl_intsize, &zl_ints);
- if (error == ENOENT)
- continue;
+ if (error == ENOENT)
+ return;
- ASSERT3U(error, ==, 0);
+ ASSERT3U(error, ==, 0);
- tx = dmu_tx_create(os);
- dmu_tx_hold_zap(tx, object, TRUE, NULL);
- error = dmu_tx_assign(tx, TXG_WAIT);
- if (error) {
- ztest_record_enospc("remove zap entry");
- dmu_tx_abort(tx);
- return;
- }
- error = zap_remove(os, object, txgname, tx);
- if (error)
- fatal(0, "zap_remove('%s', %llu, '%s') = %d",
- osname, object, txgname, error);
+ tx = dmu_tx_create(os);
+ dmu_tx_hold_zap(tx, object, TRUE, NULL);
+ error = dmu_tx_assign(tx, TXG_WAIT);
+ if (error) {
+ ztest_record_enospc("remove zap entry");
+ dmu_tx_abort(tx);
+ return;
+ }
+ error = zap_remove(os, object, txgname, tx);
+ if (error)
+ fatal(0, "zap_remove('%s', %llu, '%s') = %d",
+ osname, object, txgname, error);
- error = zap_remove(os, object, propname, tx);
- if (error)
- fatal(0, "zap_remove('%s', %llu, '%s') = %d",
- osname, object, propname, error);
+ error = zap_remove(os, object, propname, tx);
+ if (error)
+ fatal(0, "zap_remove('%s', %llu, '%s') = %d",
+ osname, object, propname, error);
- dmu_tx_commit(tx);
- }
+ dmu_tx_commit(tx);
/*
* Once in a while, destroy the object.
*/
- if (ztest_random(100) != 0)
+ if (ztest_random(1000) != 0)
return;
tx = dmu_tx_create(os);
@@ -2372,111 +2492,107 @@ ztest_zap_parallel(ztest_args_t *za)
{
objset_t *os = za->za_os;
uint64_t txg, object, count, wsize, wc, zl_wsize, zl_wc;
- int iters = 100;
dmu_tx_t *tx;
int i, namelen, error;
char name[20], string_value[20];
void *data;
- while (--iters >= 0) {
- /*
- * Generate a random name of the form 'xxx.....' where each
- * x is a random printable character and the dots are dots.
- * There are 94 such characters, and the name length goes from
- * 6 to 20, so there are 94^3 * 15 = 12,458,760 possible names.
- */
- namelen = ztest_random(sizeof (name) - 5) + 5 + 1;
+ /*
+ * Generate a random name of the form 'xxx.....' where each
+ * x is a random printable character and the dots are dots.
+ * There are 94 such characters, and the name length goes from
+ * 6 to 20, so there are 94^3 * 15 = 12,458,760 possible names.
+ */
+ namelen = ztest_random(sizeof (name) - 5) + 5 + 1;
- for (i = 0; i < 3; i++)
- name[i] = '!' + ztest_random('~' - '!' + 1);
- for (; i < namelen - 1; i++)
- name[i] = '.';
- name[i] = '\0';
+ for (i = 0; i < 3; i++)
+ name[i] = '!' + ztest_random('~' - '!' + 1);
+ for (; i < namelen - 1; i++)
+ name[i] = '.';
+ name[i] = '\0';
- if (ztest_random(2) == 0)
- object = ZTEST_MICROZAP_OBJ;
- else
- object = ZTEST_FATZAP_OBJ;
+ if (ztest_random(2) == 0)
+ object = ZTEST_MICROZAP_OBJ;
+ else
+ object = ZTEST_FATZAP_OBJ;
- if ((namelen & 1) || object == ZTEST_MICROZAP_OBJ) {
- wsize = sizeof (txg);
- wc = 1;
- data = &txg;
- } else {
- wsize = 1;
- wc = namelen;
- data = string_value;
- }
+ if ((namelen & 1) || object == ZTEST_MICROZAP_OBJ) {
+ wsize = sizeof (txg);
+ wc = 1;
+ data = &txg;
+ } else {
+ wsize = 1;
+ wc = namelen;
+ data = string_value;
+ }
- count = -1ULL;
- VERIFY(zap_count(os, object, &count) == 0);
- ASSERT(count != -1ULL);
+ count = -1ULL;
+ VERIFY(zap_count(os, object, &count) == 0);
+ ASSERT(count != -1ULL);
- /*
- * Select an operation: length, lookup, add, update, remove.
- */
- i = ztest_random(5);
-
- if (i >= 2) {
- tx = dmu_tx_create(os);
- dmu_tx_hold_zap(tx, object, TRUE, NULL);
- error = dmu_tx_assign(tx, TXG_WAIT);
- if (error) {
- ztest_record_enospc("zap parallel");
- dmu_tx_abort(tx);
- return;
- }
- txg = dmu_tx_get_txg(tx);
- bcopy(name, string_value, namelen);
- } else {
- tx = NULL;
- txg = 0;
- bzero(string_value, namelen);
- }
+ /*
+ * Select an operation: length, lookup, add, update, remove.
+ */
+ i = ztest_random(5);
- switch (i) {
+ if (i >= 2) {
+ tx = dmu_tx_create(os);
+ dmu_tx_hold_zap(tx, object, TRUE, NULL);
+ error = dmu_tx_assign(tx, TXG_WAIT);
+ if (error) {
+ ztest_record_enospc("zap parallel");
+ dmu_tx_abort(tx);
+ return;
+ }
+ txg = dmu_tx_get_txg(tx);
+ bcopy(name, string_value, namelen);
+ } else {
+ tx = NULL;
+ txg = 0;
+ bzero(string_value, namelen);
+ }
- case 0:
- error = zap_length(os, object, name, &zl_wsize, &zl_wc);
- if (error == 0) {
- ASSERT3U(wsize, ==, zl_wsize);
- ASSERT3U(wc, ==, zl_wc);
- } else {
- ASSERT3U(error, ==, ENOENT);
- }
- break;
+ switch (i) {
- case 1:
- error = zap_lookup(os, object, name, wsize, wc, data);
- if (error == 0) {
- if (data == string_value &&
- bcmp(name, data, namelen) != 0)
- fatal(0, "name '%s' != val '%s' len %d",
- name, data, namelen);
- } else {
- ASSERT3U(error, ==, ENOENT);
- }
- break;
+ case 0:
+ error = zap_length(os, object, name, &zl_wsize, &zl_wc);
+ if (error == 0) {
+ ASSERT3U(wsize, ==, zl_wsize);
+ ASSERT3U(wc, ==, zl_wc);
+ } else {
+ ASSERT3U(error, ==, ENOENT);
+ }
+ break;
- case 2:
- error = zap_add(os, object, name, wsize, wc, data, tx);
- ASSERT(error == 0 || error == EEXIST);
- break;
+ case 1:
+ error = zap_lookup(os, object, name, wsize, wc, data);
+ if (error == 0) {
+ if (data == string_value &&
+ bcmp(name, data, namelen) != 0)
+ fatal(0, "name '%s' != val '%s' len %d",
+ name, data, namelen);
+ } else {
+ ASSERT3U(error, ==, ENOENT);
+ }
+ break;
- case 3:
- VERIFY(zap_update(os, object, name, wsize, wc,
- data, tx) == 0);
- break;
+ case 2:
+ error = zap_add(os, object, name, wsize, wc, data, tx);
+ ASSERT(error == 0 || error == EEXIST);
+ break;
- case 4:
- error = zap_remove(os, object, name, tx);
- ASSERT(error == 0 || error == ENOENT);
- break;
- }
+ case 3:
+ VERIFY(zap_update(os, object, name, wsize, wc, data, tx) == 0);
+ break;
- if (tx != NULL)
- dmu_tx_commit(tx);
+ case 4:
+ error = zap_remove(os, object, name, tx);
+ ASSERT(error == 0 || error == ENOENT);
+ break;
}
+
+ if (tx != NULL)
+ dmu_tx_commit(tx);
}
void
@@ -2532,21 +2648,6 @@ ztest_dsl_prop_get_set(ztest_args_t *za)
(void) rw_unlock(&ztest_shared->zs_name_lock);
}
-static void
-ztest_error_setup(vdev_t *vd, int mode, int mask, uint64_t arg)
-{
- int c;
-
- for (c = 0; c < vd->vdev_children; c++)
- ztest_error_setup(vd->vdev_child[c], mode, mask, arg);
-
- if (vd->vdev_path != NULL) {
- vd->vdev_fault_mode = mode;
- vd->vdev_fault_mask = mask;
- vd->vdev_fault_arg = arg;
- }
-}
-
/*
* Inject random faults into the on-disk data.
*/
@@ -2561,67 +2662,97 @@ ztest_fault_inject(ztest_args_t *za)
char path0[MAXPATHLEN];
char pathrand[MAXPATHLEN];
size_t fsize;
- spa_t *spa = dmu_objset_spa(za->za_os);
+ spa_t *spa = za->za_spa;
int bshift = SPA_MAXBLOCKSHIFT + 2; /* don't scrog all labels */
int iters = 1000;
- vdev_t *vd0;
+ int maxfaults = zopt_maxfaults;
+ vdev_t *vd0 = NULL;
uint64_t guid0 = 0;
- /*
- * We can't inject faults when we have no fault tolerance.
- */
- if (zopt_maxfaults == 0)
- return;
-
- ASSERT(leaves >= 2);
+ ASSERT(leaves >= 1);
/*
- * Pick a random top-level vdev.
+ * We need SCL_STATE here because we're going to look at vd0->vdev_tsd.
*/
- spa_config_enter(spa, RW_READER, FTAG);
- top = ztest_random(spa->spa_root_vdev->vdev_children);
- spa_config_exit(spa, FTAG);
+ spa_config_enter(spa, SCL_STATE, FTAG, RW_READER);
- /*
- * Pick a random leaf.
- */
- leaf = ztest_random(leaves);
+ if (ztest_random(2) == 0) {
+ /*
+ * Inject errors on a normal data device.
+ */
+ top = ztest_random(spa->spa_root_vdev->vdev_children);
+ leaf = ztest_random(leaves);
- /*
- * Generate paths to the first two leaves in this top-level vdev,
- * and to the random leaf we selected. We'll induce transient
- * I/O errors and random online/offline activity on leaf 0,
- * and we'll write random garbage to the randomly chosen leaf.
- */
- (void) snprintf(path0, sizeof (path0),
- ztest_dev_template, zopt_dir, zopt_pool, top * leaves + 0);
- (void) snprintf(pathrand, sizeof (pathrand),
- ztest_dev_template, zopt_dir, zopt_pool, top * leaves + leaf);
+ /*
+ * Generate paths to the first leaf in this top-level vdev,
+ * and to the random leaf we selected. We'll induce transient
+ * write failures and random online/offline activity on leaf 0,
+ * and we'll write random garbage to the randomly chosen leaf.
+ */
+ (void) snprintf(path0, sizeof (path0), ztest_dev_template,
+ zopt_dir, zopt_pool, top * leaves + 0);
+ (void) snprintf(pathrand, sizeof (pathrand), ztest_dev_template,
+ zopt_dir, zopt_pool, top * leaves + leaf);
- dprintf("damaging %s and %s\n", path0, pathrand);
+ vd0 = vdev_lookup_by_path(spa->spa_root_vdev, path0);
+ if (vd0 != NULL && maxfaults != 1) {
+ /*
+ * Make vd0 explicitly claim to be unreadable,
+ * or unwriteable, or reach behind its back
+ * and close the underlying fd. We can do this if
+ * maxfaults == 0 because we'll fail and reexecute,
+ * and we can do it if maxfaults >= 2 because we'll
+ * have enough redundancy. If maxfaults == 1, the
+ * combination of this with injection of random data
+ * corruption below exceeds the pool's fault tolerance.
+ */
+ vdev_file_t *vf = vd0->vdev_tsd;
- spa_config_enter(spa, RW_READER, FTAG);
+ if (vf != NULL && ztest_random(3) == 0) {
+ (void) close(vf->vf_vnode->v_fd);
+ vf->vf_vnode->v_fd = -1;
+ } else if (ztest_random(2) == 0) {
+ vd0->vdev_cant_read = B_TRUE;
+ } else {
+ vd0->vdev_cant_write = B_TRUE;
+ }
+ guid0 = vd0->vdev_guid;
+ }
+ } else {
+ /*
+ * Inject errors on an l2cache device.
+ */
+ spa_aux_vdev_t *sav = &spa->spa_l2cache;
- /*
- * If we can tolerate two or more faults, make vd0 fail randomly.
- */
- vd0 = vdev_lookup_by_path(spa->spa_root_vdev, path0);
- if (vd0 != NULL && zopt_maxfaults >= 2) {
+ if (sav->sav_count == 0) {
+ spa_config_exit(spa, SCL_STATE, FTAG);
+ return;
+ }
+ vd0 = sav->sav_vdevs[ztest_random(sav->sav_count)];
guid0 = vd0->vdev_guid;
- ztest_error_setup(vd0, VDEV_FAULT_COUNT,
- (1U << ZIO_TYPE_READ) | (1U << ZIO_TYPE_WRITE), 100);
+ (void) strcpy(path0, vd0->vdev_path);
+ (void) strcpy(pathrand, vd0->vdev_path);
+
+ leaf = 0;
+ leaves = 1;
+ maxfaults = INT_MAX; /* no limit on cache devices */
}
- spa_config_exit(spa, FTAG);
+ dprintf("damaging %s and %s\n", path0, pathrand);
+
+ spa_config_exit(spa, SCL_STATE, FTAG);
+
+ if (maxfaults == 0)
+ return;
/*
* If we can tolerate two or more faults, randomly online/offline vd0.
*/
- if (zopt_maxfaults >= 2 && guid0 != 0) {
+ if (maxfaults >= 2 && guid0 != 0) {
if (ztest_random(10) < 6)
(void) vdev_offline(spa, guid0, B_TRUE);
else
- (void) vdev_online(spa, guid0);
+ (void) vdev_online(spa, guid0, B_FALSE, NULL);
}
/*
@@ -2660,11 +2791,11 @@ ztest_fault_inject(ztest_args_t *za)
void
ztest_scrub(ztest_args_t *za)
{
- spa_t *spa = dmu_objset_spa(za->za_os);
+ spa_t *spa = za->za_spa;
- (void) spa_scrub(spa, POOL_SCRUB_EVERYTHING, B_FALSE);
+ (void) spa_scrub(spa, POOL_SCRUB_EVERYTHING);
(void) poll(NULL, 0, 1000); /* wait a second, then force a restart */
- (void) spa_scrub(spa, POOL_SCRUB_EVERYTHING, B_FALSE);
+ (void) spa_scrub(spa, POOL_SCRUB_EVERYTHING);
}
/*
@@ -2706,7 +2837,7 @@ ztest_spa_rename(ztest_args_t *za)
if (error != 0)
fatal(0, "spa_open('%s') = %d", newname, error);
- ASSERT(spa == dmu_objset_spa(za->za_os));
+ ASSERT(spa == za->za_spa);
spa_close(spa, FTAG);
/*
@@ -2724,7 +2855,7 @@ ztest_spa_rename(ztest_args_t *za)
if (error != 0)
fatal(0, "spa_open('%s') = %d", oldname, error);
- ASSERT(spa == dmu_objset_spa(za->za_os));
+ ASSERT(spa == za->za_spa);
spa_close(spa, FTAG);
umem_free(newname, strlen(newname) + 1);
@@ -2778,10 +2909,9 @@ static void
ztest_replace_one_disk(spa_t *spa, uint64_t vdev)
{
char dev_name[MAXPATHLEN];
- nvlist_t *file, *root;
+ nvlist_t *root;
int error;
uint64_t guid;
- uint64_t ashift = ztest_get_ashift();
vdev_t *vd;
(void) sprintf(dev_name, ztest_dev_template, zopt_dir, zopt_pool, vdev);
@@ -2789,22 +2919,14 @@ ztest_replace_one_disk(spa_t *spa, uint64_t vdev)
/*
* Build the nvlist describing dev_name.
*/
- VERIFY(nvlist_alloc(&file, NV_UNIQUE_NAME, 0) == 0);
- VERIFY(nvlist_add_string(file, ZPOOL_CONFIG_TYPE, VDEV_TYPE_FILE) == 0);
- VERIFY(nvlist_add_string(file, ZPOOL_CONFIG_PATH, dev_name) == 0);
- VERIFY(nvlist_add_uint64(file, ZPOOL_CONFIG_ASHIFT, ashift) == 0);
-
- VERIFY(nvlist_alloc(&root, NV_UNIQUE_NAME, 0) == 0);
- VERIFY(nvlist_add_string(root, ZPOOL_CONFIG_TYPE, VDEV_TYPE_ROOT) == 0);
- VERIFY(nvlist_add_nvlist_array(root, ZPOOL_CONFIG_CHILDREN,
- &file, 1) == 0);
+ root = make_vdev_root(dev_name, NULL, 0, 0, 0, 0, 0, 1);
- spa_config_enter(spa, RW_READER, FTAG);
+ spa_config_enter(spa, SCL_VDEV, FTAG, RW_READER);
if ((vd = vdev_lookup_by_path(spa->spa_root_vdev, dev_name)) == NULL)
guid = 0;
else
guid = vd->vdev_guid;
- spa_config_exit(spa, FTAG);
+ spa_config_exit(spa, SCL_VDEV, FTAG);
error = spa_vdev_attach(spa, guid, root, B_TRUE);
if (error != 0 &&
error != EBUSY &&
@@ -2813,7 +2935,6 @@ ztest_replace_one_disk(spa_t *spa, uint64_t vdev)
error != EDOM)
fatal(0, "spa_vdev_attach(in-place) = %d", error);
- nvlist_free(file);
nvlist_free(root);
}
@@ -2824,6 +2945,9 @@ ztest_verify_blocks(char *pool)
char zdb[MAXPATHLEN + MAXNAMELEN + 20];
char zbuf[1024];
char *bin;
+ char *ztest;
+ char *isa;
+ int isalen;
FILE *fp;
if (realpath(progname, zdb) == NULL)
@@ -2831,13 +2955,19 @@ ztest_verify_blocks(char *pool)
/* zdb lives in /usr/sbin, while ztest lives in /usr/bin */
bin = strstr(zdb, "/usr/bin/");
- if (bin == NULL)
- bin = zdb;
+ ztest = strstr(bin, "/ztest");
+ isa = bin + 8;
+ isalen = ztest - isa;
+ isa = strdup(isa);
/* LINTED */
- (void) sprintf(bin, "/usr/sbin/zdb -bc%s%s -U -O %s %s",
+ (void) sprintf(bin,
+ "/usr/sbin%.*s/zdb -bc%s%s -U /tmp/zpool.cache -O %s %s",
+ isalen,
+ isa,
zopt_verbose >= 3 ? "s" : "",
zopt_verbose >= 4 ? "v" : "",
ztest_random(2) == 0 ? "pre" : "post", pool);
+ free(isa);
if (zopt_verbose >= 5)
(void) printf("Executing %s\n", strstr(zdb, "zdb "));
@@ -2909,7 +3039,7 @@ ztest_spa_import_export(char *oldname, char *newname)
/*
* Export it.
*/
- error = spa_export(oldname, &config);
+ error = spa_export(oldname, &config, B_FALSE);
if (error)
fatal(0, "spa_export('%s') = %d", oldname, error);
@@ -2958,35 +3088,41 @@ ztest_spa_import_export(char *oldname, char *newname)
}
static void *
+ztest_resume(void *arg)
+{
+ spa_t *spa = arg;
+
+ while (!ztest_exiting) {
+ (void) poll(NULL, 0, 1000);
+
+ if (!spa_suspended(spa))
+ continue;
+
+ spa_vdev_state_enter(spa);
+ vdev_clear(spa, NULL);
+ (void) spa_vdev_state_exit(spa, NULL, 0);
+
+ zio_resume(spa);
+ }
+ return (NULL);
+}
+
+static void *
ztest_thread(void *arg)
{
ztest_args_t *za = arg;
ztest_shared_t *zs = ztest_shared;
hrtime_t now, functime;
ztest_info_t *zi;
- int f;
+ int f, i;
while ((now = gethrtime()) < za->za_stop) {
/*
* See if it's time to force a crash.
*/
if (now > za->za_kill) {
- dmu_tx_t *tx;
- uint64_t txg;
-
- mutex_enter(&spa_namespace_lock);
- tx = dmu_tx_create(za->za_os);
- VERIFY(0 == dmu_tx_assign(tx, TXG_NOWAIT));
- txg = dmu_tx_get_txg(tx);
- dmu_tx_commit(tx);
- zs->zs_txg = txg;
- if (zopt_verbose >= 3)
- (void) printf(
- "killing process after txg %lld\n",
- (u_longlong_t)txg);
- txg_wait_synced(dmu_objset_pool(za->za_os), txg);
- zs->zs_alloc = spa_get_alloc(dmu_objset_spa(za->za_os));
- zs->zs_space = spa_get_space(dmu_objset_spa(za->za_os));
+ zs->zs_alloc = spa_get_alloc(za->za_spa);
+ zs->zs_space = spa_get_space(za->za_spa);
(void) kill(getpid(), SIGKILL);
}
@@ -3011,9 +3147,8 @@ ztest_thread(void *arg)
ZTEST_DIRSIZE;
za->za_diroff_shared = (1ULL << 63);
- ztest_dmu_write_parallel(za);
-
- zi->zi_func(za);
+ for (i = 0; i < zi->zi_iters; i++)
+ zi->zi_func(za);
functime = gethrtime() - now;
@@ -3047,6 +3182,9 @@ ztest_run(char *pool)
ztest_args_t *za;
spa_t *spa;
char name[100];
+ thread_t resume_tid;
+
+ ztest_exiting = B_FALSE;
(void) _mutex_init(&zs->zs_vdev_lock, USYNC_THREAD, NULL);
(void) rwlock_init(&zs->zs_name_lock, USYNC_THREAD, NULL);
@@ -3071,9 +3209,7 @@ ztest_run(char *pool)
* Kick off a replacement of the disk we just obliterated.
*/
kernel_init(FREAD | FWRITE);
- error = spa_open(pool, &spa, FTAG);
- if (error)
- fatal(0, "spa_open(%s) = %d", pool, error);
+ VERIFY(spa_open(pool, &spa, FTAG) == 0);
ztest_replace_one_disk(spa, 0);
if (zopt_verbose >= 5)
show_pool_stats(spa);
@@ -3106,9 +3242,13 @@ ztest_run(char *pool)
/*
* Open our pool.
*/
- error = spa_open(pool, &spa, FTAG);
- if (error)
- fatal(0, "spa_open() = %d", error);
+ VERIFY(spa_open(pool, &spa, FTAG) == 0);
+
+ /*
+ * Create a thread to periodically resume suspended I/O.
+ */
+ VERIFY(thr_create(0, 0, ztest_resume, spa, THR_BOUND,
+ &resume_tid) == 0);
/*
* Verify that we can safely inquire about about any object,
@@ -3144,71 +3284,62 @@ ztest_run(char *pool)
for (t = 0; t < zopt_threads; t++) {
d = t % zopt_datasets;
+
+ (void) strcpy(za[t].za_pool, pool);
+ za[t].za_os = za[d].za_os;
+ za[t].za_spa = spa;
+ za[t].za_zilog = za[d].za_zilog;
+ za[t].za_instance = t;
+ za[t].za_random = ztest_random(-1ULL);
+ za[t].za_start = za[0].za_start;
+ za[t].za_stop = za[0].za_stop;
+ za[t].za_kill = za[0].za_kill;
+
if (t < zopt_datasets) {
ztest_replay_t zr;
int test_future = FALSE;
(void) rw_rdlock(&ztest_shared->zs_name_lock);
(void) snprintf(name, 100, "%s/%s_%d", pool, pool, d);
- error = dmu_objset_create(name, DMU_OST_OTHER, NULL,
+ error = dmu_objset_create(name, DMU_OST_OTHER, NULL, 0,
ztest_create_cb, NULL);
if (error == EEXIST) {
test_future = TRUE;
+ } else if (error == ENOSPC) {
+ zs->zs_enospc_count++;
+ (void) rw_unlock(&ztest_shared->zs_name_lock);
+ break;
} else if (error != 0) {
- if (error == ENOSPC) {
- zs->zs_enospc_count++;
- (void) rw_unlock(
- &ztest_shared->zs_name_lock);
- break;
- }
fatal(0, "dmu_objset_create(%s) = %d",
name, error);
}
error = dmu_objset_open(name, DMU_OST_OTHER,
- DS_MODE_STANDARD, &za[d].za_os);
+ DS_MODE_USER, &za[d].za_os);
if (error)
fatal(0, "dmu_objset_open('%s') = %d",
name, error);
(void) rw_unlock(&ztest_shared->zs_name_lock);
- if (test_future && ztest_shared->zs_txg > 0)
- ztest_dmu_check_future_leak(za[d].za_os,
- ztest_shared->zs_txg);
+ if (test_future)
+ ztest_dmu_check_future_leak(&za[t]);
zr.zr_os = za[d].za_os;
zil_replay(zr.zr_os, &zr, &zr.zr_assign,
- ztest_replay_vector);
+ ztest_replay_vector, NULL);
za[d].za_zilog = zil_open(za[d].za_os, NULL);
}
- za[t].za_pool = spa_strdup(pool);
- za[t].za_os = za[d].za_os;
- za[t].za_zilog = za[d].za_zilog;
- za[t].za_instance = t;
- za[t].za_random = ztest_random(-1ULL);
- za[t].za_start = za[0].za_start;
- za[t].za_stop = za[0].za_stop;
- za[t].za_kill = za[0].za_kill;
- error = thr_create(0, 0, ztest_thread, &za[t], THR_BOUND,
- &za[t].za_thread);
- if (error)
- fatal(0, "can't create thread %d: error %d",
- t, error);
+ VERIFY(thr_create(0, 0, ztest_thread, &za[t], THR_BOUND,
+ &za[t].za_thread) == 0);
}
- ztest_shared->zs_txg = 0;
while (--t >= 0) {
- error = thr_join(za[t].za_thread, NULL, NULL);
- if (error)
- fatal(0, "thr_join(%d) = %d", t, error);
+ VERIFY(thr_join(za[t].za_thread, NULL, NULL) == 0);
if (za[t].za_th)
traverse_fini(za[t].za_th);
if (t < zopt_datasets) {
zil_close(za[t].za_zilog);
dmu_objset_close(za[t].za_os);
}
- spa_strfree(za[t].za_pool);
}
- umem_free(za, zopt_threads * sizeof (ztest_args_t));
-
if (zopt_verbose >= 3)
show_pool_stats(spa);
@@ -3218,21 +3349,27 @@ ztest_run(char *pool)
zs->zs_space = spa_get_space(spa);
/*
- * Did we have out-of-space errors? If so, destroy a random objset.
+ * If we had out-of-space errors, destroy a random objset.
*/
if (zs->zs_enospc_count != 0) {
(void) rw_rdlock(&ztest_shared->zs_name_lock);
- (void) snprintf(name, 100, "%s/%s_%d", pool, pool,
- (int)ztest_random(zopt_datasets));
+ d = (int)ztest_random(zopt_datasets);
+ (void) snprintf(name, 100, "%s/%s_%d", pool, pool, d);
if (zopt_verbose >= 3)
(void) printf("Destroying %s to free up space\n", name);
- (void) dmu_objset_find(name, ztest_destroy_cb, NULL,
+ (void) dmu_objset_find(name, ztest_destroy_cb, &za[d],
DS_FIND_SNAPSHOTS | DS_FIND_CHILDREN);
(void) rw_unlock(&ztest_shared->zs_name_lock);
}
txg_wait_synced(spa_get_dsl(spa), 0);
+ umem_free(za, zopt_threads * sizeof (ztest_args_t));
+
+ /* Kill the resume thread */
+ ztest_exiting = B_TRUE;
+ VERIFY(thr_join(resume_tid, NULL, NULL) == 0);
+
/*
* Right before closing the pool, kick off a bunch of async I/O;
* spa_close() should wait for it to complete.
@@ -3288,8 +3425,9 @@ ztest_init(char *pool)
*/
(void) spa_destroy(pool);
ztest_shared->zs_vdev_primaries = 0;
- nvroot = make_vdev_root(zopt_vdev_size, zopt_raidz, zopt_mirrors, 1);
- error = spa_create(pool, nvroot, NULL);
+ nvroot = make_vdev_root(NULL, NULL, zopt_vdev_size, 0,
+ 0, zopt_raidz, zopt_mirrors, 1);
+ error = spa_create(pool, nvroot, NULL, NULL, NULL);
nvlist_free(nvroot);
if (error)
@@ -3320,7 +3458,7 @@ main(int argc, char **argv)
(void) setvbuf(stdout, NULL, _IOLBF, 0);
/* Override location of zpool.cache */
- spa_config_dir = "/tmp";
+ spa_config_path = "/tmp/zpool.cache";
ztest_random_fd = open("/dev/urandom", O_RDONLY);
OpenPOWER on IntegriCloud