]> git.proxmox.com Git - mirror_zfs.git/blobdiff - module/zfs/spa.c
cstyle: Resolve C style issues
[mirror_zfs.git] / module / zfs / spa.c
index 0a785f78a6cb51e7665d9fd80a59b7578699685c..7052eec4ababd8bd5cc69dbe3a905bcc52b226f3 100644 (file)
 
 /*
  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
- * Copyright (c) 2012 by Delphix. All rights reserved.
+ * Copyright (c) 2013 by Delphix. All rights reserved.
+ * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
  */
 
 /*
+ * SPA: Storage Pool Allocator
+ *
  * This file contains all the routines used when modifying on-disk SPA state.
  * This includes opening, importing, destroying, exporting a pool, and syncing a
  * pool.
@@ -64,6 +66,8 @@
 #include <sys/zfs_ioctl.h>
 #include <sys/dsl_scan.h>
 #include <sys/zfeature.h>
+#include <sys/dsl_destroy.h>
+#include <sys/zvol.h>
 
 #ifdef _KERNEL
 #include <sys/bootprops.h>
 #include "zfs_comutil.h"
 
 typedef enum zti_modes {
-       zti_mode_fixed,                 /* value is # of threads (min 1) */
-       zti_mode_online_percent,        /* value is % of online CPUs */
-       zti_mode_batch,                 /* cpu-intensive; value is ignored */
-       zti_mode_null,                  /* don't create a taskq */
-       zti_nmodes
+       ZTI_MODE_FIXED,                 /* value is # of threads (min 1) */
+       ZTI_MODE_BATCH,                 /* cpu-intensive; value is ignored */
+       ZTI_MODE_NULL,                  /* don't create a taskq */
+       ZTI_NMODES
 } zti_modes_t;
 
-#define        ZTI_FIX(n)      { zti_mode_fixed, (n) }
-#define        ZTI_PCT(n)      { zti_mode_online_percent, (n) }
-#define        ZTI_BATCH       { zti_mode_batch, 0 }
-#define        ZTI_NULL        { zti_mode_null, 0 }
+#define        ZTI_P(n, q)     { ZTI_MODE_FIXED, (n), (q) }
+#define        ZTI_PCT(n)      { ZTI_MODE_ONLINE_PERCENT, (n), 1 }
+#define        ZTI_BATCH       { ZTI_MODE_BATCH, 0, 1 }
+#define        ZTI_NULL        { ZTI_MODE_NULL, 0, 0 }
 
-#define        ZTI_ONE         ZTI_FIX(1)
+#define        ZTI_N(n)        ZTI_P(n, 1)
+#define        ZTI_ONE         ZTI_N(1)
 
 typedef struct zio_taskq_info {
-       enum zti_modes zti_mode;
+       zti_modes_t zti_mode;
        uint_t zti_value;
+       uint_t zti_count;
 } zio_taskq_info_t;
 
 static const char *const zio_taskq_types[ZIO_TASKQ_TYPES] = {
@@ -102,30 +107,41 @@ static const char *const zio_taskq_types[ZIO_TASKQ_TYPES] = {
 };
 
 /*
- * Define the taskq threads for the following I/O types:
- *     NULL, READ, WRITE, FREE, CLAIM, and IOCTL
+ * This table defines the taskq settings for each ZFS I/O type. When
+ * initializing a pool, we use this table to create an appropriately sized
+ * taskq. Some operations are low volume and therefore have a small, static
+ * number of threads assigned to their taskqs using the ZTI_N(#) or ZTI_ONE
+ * macros. Other operations process a large amount of data; the ZTI_BATCH
+ * macro causes us to create a taskq oriented for throughput. Some operations
+ * are so high frequency and short-lived that the taskq itself can become a a
+ * point of lock contention. The ZTI_P(#, #) macro indicates that we need an
+ * additional degree of parallelism specified by the number of threads per-
+ * taskq and the number of taskqs; when dispatching an event in this case, the
+ * particular taskq is chosen at random.
+ *
+ * The different taskq priorities are to handle the different contexts (issue
+ * and interrupt) and then to reserve threads for ZIO_PRIORITY_NOW I/Os that
+ * need to be handled with minimum delay.
  */
 const zio_taskq_info_t zio_taskqs[ZIO_TYPES][ZIO_TASKQ_TYPES] = {
        /* ISSUE        ISSUE_HIGH      INTR            INTR_HIGH */
-       { ZTI_ONE,      ZTI_NULL,       ZTI_ONE,        ZTI_NULL },
-       { ZTI_FIX(8),   ZTI_NULL,       ZTI_BATCH,      ZTI_NULL },
-       { ZTI_BATCH,    ZTI_FIX(5),     ZTI_FIX(16),    ZTI_FIX(5) },
-       { ZTI_PCT(100), ZTI_NULL,       ZTI_ONE,        ZTI_NULL },
-       { ZTI_ONE,      ZTI_NULL,       ZTI_ONE,        ZTI_NULL },
-       { ZTI_ONE,      ZTI_NULL,       ZTI_ONE,        ZTI_NULL },
+       { ZTI_ONE,      ZTI_NULL,       ZTI_ONE,        ZTI_NULL }, /* NULL */
+       { ZTI_N(8),     ZTI_NULL,       ZTI_BATCH,      ZTI_NULL }, /* READ */
+       { ZTI_BATCH,    ZTI_N(5),       ZTI_N(16),      ZTI_N(5) }, /* WRITE */
+       { ZTI_P(4, 8),  ZTI_NULL,       ZTI_ONE,        ZTI_NULL }, /* FREE */
+       { ZTI_ONE,      ZTI_NULL,       ZTI_ONE,        ZTI_NULL }, /* CLAIM */
+       { ZTI_ONE,      ZTI_NULL,       ZTI_ONE,        ZTI_NULL }, /* IOCTL */
 };
 
-static dsl_syncfunc_t spa_sync_version;
-static dsl_syncfunc_t spa_sync_props;
-static dsl_checkfunc_t spa_change_guid_check;
-static dsl_syncfunc_t spa_change_guid_sync;
+static void spa_sync_version(void *arg, dmu_tx_t *tx);
+static void spa_sync_props(void *arg, dmu_tx_t *tx);
 static boolean_t spa_has_active_shared_spare(spa_t *spa);
 static inline int spa_load_impl(spa_t *spa, uint64_t, nvlist_t *config,
     spa_load_state_t state, spa_import_type_t type, boolean_t mosconfig,
     char **ereport);
 static void spa_vdev_resilver_done(spa_t *spa);
 
-uint_t         zio_taskq_batch_pct = 100;      /* 1 thread per cpu in pset */
+uint_t         zio_taskq_batch_pct = 75;       /* 1 thread per cpu in pset */
 id_t           zio_taskq_psrset_bind = PS_NONE;
 boolean_t      zio_taskq_sysdc = B_TRUE;       /* use SDC scheduling class */
 uint_t         zio_taskq_basedc = 80;          /* base duty cycle */
@@ -272,7 +288,7 @@ spa_prop_get(spa_t *spa, nvlist_t **nvp)
 
        err = nvlist_alloc(nvp, NV_UNIQUE_NAME, KM_PUSHPAGE);
        if (err)
-               return err;
+               return (err);
 
        mutex_enter(&spa->spa_props_lock);
 
@@ -313,10 +329,10 @@ spa_prop_get(spa_t *spa, nvlist_t **nvp)
                                dsl_dataset_t *ds = NULL;
 
                                dp = spa_get_dsl(spa);
-                               rw_enter(&dp->dp_config_rwlock, RW_READER);
+                               dsl_pool_config_enter(dp, FTAG);
                                if ((err = dsl_dataset_hold_obj(dp,
                                    za.za_first_integer, FTAG, &ds))) {
-                                       rw_exit(&dp->dp_config_rwlock);
+                                       dsl_pool_config_exit(dp, FTAG);
                                        break;
                                }
 
@@ -325,7 +341,7 @@ spa_prop_get(spa_t *spa, nvlist_t **nvp)
                                    KM_PUSHPAGE);
                                dsl_dataset_name(ds, strval);
                                dsl_dataset_rele(ds, FTAG);
-                               rw_exit(&dp->dp_config_rwlock);
+                               dsl_pool_config_exit(dp, FTAG);
                        } else {
                                strval = NULL;
                                intval = za.za_first_integer;
@@ -390,7 +406,7 @@ spa_prop_validate(spa_t *spa, nvlist_t *props)
                switch ((int)prop) {
                case ZPROP_INVAL:
                        if (!zpool_prop_feature(propname)) {
-                               error = EINVAL;
+                               error = SET_ERROR(EINVAL);
                                break;
                        }
 
@@ -398,23 +414,23 @@ spa_prop_validate(spa_t *spa, nvlist_t *props)
                         * Sanitize the input.
                         */
                        if (nvpair_type(elem) != DATA_TYPE_UINT64) {
-                               error = EINVAL;
+                               error = SET_ERROR(EINVAL);
                                break;
                        }
 
                        if (nvpair_value_uint64(elem, &intval) != 0) {
-                               error = EINVAL;
+                               error = SET_ERROR(EINVAL);
                                break;
                        }
 
                        if (intval != 0) {
-                               error = EINVAL;
+                               error = SET_ERROR(EINVAL);
                                break;
                        }
 
                        fname = strchr(propname, '@') + 1;
                        if (zfeature_lookup_name(fname, NULL) != 0) {
-                               error = EINVAL;
+                               error = SET_ERROR(EINVAL);
                                break;
                        }
 
@@ -427,7 +443,7 @@ spa_prop_validate(spa_t *spa, nvlist_t *props)
                            (intval < spa_version(spa) ||
                            intval > SPA_VERSION_BEFORE_FEATURES ||
                            has_feature))
-                               error = EINVAL;
+                               error = SET_ERROR(EINVAL);
                        break;
 
                case ZPOOL_PROP_DELEGATION:
@@ -436,7 +452,7 @@ spa_prop_validate(spa_t *spa, nvlist_t *props)
                case ZPOOL_PROP_AUTOEXPAND:
                        error = nvpair_value_uint64(elem, &intval);
                        if (!error && intval > 1)
-                               error = EINVAL;
+                               error = SET_ERROR(EINVAL);
                        break;
 
                case ZPOOL_PROP_BOOTFS:
@@ -446,7 +462,7 @@ spa_prop_validate(spa_t *spa, nvlist_t *props)
                         * the bootfs property cannot be set.
                         */
                        if (spa_version(spa) < SPA_VERSION_BOOTFS) {
-                               error = ENOTSUP;
+                               error = SET_ERROR(ENOTSUP);
                                break;
                        }
 
@@ -454,7 +470,7 @@ spa_prop_validate(spa_t *spa, nvlist_t *props)
                         * Make sure the vdev config is bootable
                         */
                        if (!vdev_is_bootable(spa->spa_root_vdev)) {
-                               error = ENOTSUP;
+                               error = SET_ERROR(ENOTSUP);
                                break;
                        }
 
@@ -472,18 +488,20 @@ spa_prop_validate(spa_t *spa, nvlist_t *props)
                                        break;
                                }
 
-                               if ((error = dmu_objset_hold(strval,FTAG,&os)))
+                               error = dmu_objset_hold(strval, FTAG, &os);
+                               if (error)
                                        break;
 
                                /* Must be ZPL and not gzip compressed. */
 
                                if (dmu_objset_type(os) != DMU_OST_ZFS) {
-                                       error = ENOTSUP;
-                               } else if ((error = dsl_prop_get_integer(strval,
+                                       error = SET_ERROR(ENOTSUP);
+                               } else if ((error =
+                                   dsl_prop_get_int_ds(dmu_objset_ds(os),
                                    zfs_prop_to_name(ZFS_PROP_COMPRESSION),
-                                   &compress, NULL)) == 0 &&
+                                   &compress)) == 0 &&
                                    !BOOTFS_COMPRESS_VALID(compress)) {
-                                       error = ENOTSUP;
+                                       error = SET_ERROR(ENOTSUP);
                                } else {
                                        objnum = dmu_objset_id(os);
                                }
@@ -495,7 +513,7 @@ spa_prop_validate(spa_t *spa, nvlist_t *props)
                        error = nvpair_value_uint64(elem, &intval);
                        if (!error && (intval < ZIO_FAILURE_MODE_WAIT ||
                            intval > ZIO_FAILURE_MODE_PANIC))
-                               error = EINVAL;
+                               error = SET_ERROR(EINVAL);
 
                        /*
                         * This is a special case which only occurs when
@@ -509,7 +527,7 @@ spa_prop_validate(spa_t *spa, nvlist_t *props)
                         */
                        if (!error && spa_suspended(spa)) {
                                spa->spa_failmode = intval;
-                               error = EIO;
+                               error = SET_ERROR(EIO);
                        }
                        break;
 
@@ -524,7 +542,7 @@ spa_prop_validate(spa_t *spa, nvlist_t *props)
                                break;
 
                        if (strval[0] != '/') {
-                               error = EINVAL;
+                               error = SET_ERROR(EINVAL);
                                break;
                        }
 
@@ -533,7 +551,7 @@ spa_prop_validate(spa_t *spa, nvlist_t *props)
 
                        if (slash[1] == '\0' || strcmp(slash, "/.") == 0 ||
                            strcmp(slash, "/..") == 0)
-                               error = EINVAL;
+                               error = SET_ERROR(EINVAL);
                        break;
 
                case ZPOOL_PROP_COMMENT:
@@ -541,23 +559,23 @@ spa_prop_validate(spa_t *spa, nvlist_t *props)
                                break;
                        for (check = strval; *check != '\0'; check++) {
                                if (!isprint(*check)) {
-                                       error = EINVAL;
+                                       error = SET_ERROR(EINVAL);
                                        break;
                                }
                                check++;
                        }
                        if (strlen(strval) > ZPROP_MAX_COMMENT)
-                               error = E2BIG;
+                               error = SET_ERROR(E2BIG);
                        break;
 
                case ZPOOL_PROP_DEDUPDITTO:
                        if (spa_version(spa) < SPA_VERSION_DEDUP)
-                               error = ENOTSUP;
+                               error = SET_ERROR(ENOTSUP);
                        else
                                error = nvpair_value_uint64(elem, &intval);
                        if (error == 0 &&
                            intval != 0 && intval < ZIO_DEDUPDITTO_MIN)
-                               error = EINVAL;
+                               error = SET_ERROR(EINVAL);
                        break;
 
                default:
@@ -645,8 +663,8 @@ spa_prop_set(spa_t *spa, nvlist_t *nvp)
                         * read object, the features for write object, or the
                         * feature descriptions object.
                         */
-                       error = dsl_sync_task_do(spa_get_dsl(spa), NULL,
-                           spa_sync_version, spa, &ver, 6);
+                       error = dsl_sync_task(spa->spa_name, NULL,
+                           spa_sync_version, &ver, 6);
                        if (error)
                                return (error);
                        continue;
@@ -657,8 +675,8 @@ spa_prop_set(spa_t *spa, nvlist_t *nvp)
        }
 
        if (need_sync) {
-               return (dsl_sync_task_do(spa_get_dsl(spa), NULL, spa_sync_props,
-                   spa, nvp, 6));
+               return (dsl_sync_task(spa->spa_name, NULL, spa_sync_props,
+                   nvp, 6));
        }
 
        return (0);
@@ -680,19 +698,19 @@ spa_prop_clear_bootfs(spa_t *spa, uint64_t dsobj, dmu_tx_t *tx)
 
 /*ARGSUSED*/
 static int
-spa_change_guid_check(void *arg1, void *arg2, dmu_tx_t *tx)
+spa_change_guid_check(void *arg, dmu_tx_t *tx)
 {
-       spa_t *spa = arg1;
+       spa_t *spa = dmu_tx_pool(tx)->dp_spa;
        vdev_t *rvd = spa->spa_root_vdev;
        uint64_t vdev_state;
-       ASSERTV(uint64_t *newguid = arg2);
+       ASSERTV(uint64_t *newguid = arg);
 
        spa_config_enter(spa, SCL_STATE, FTAG, RW_READER);
        vdev_state = rvd->vdev_state;
        spa_config_exit(spa, SCL_STATE, FTAG);
 
        if (vdev_state != VDEV_STATE_HEALTHY)
-               return (ENXIO);
+               return (SET_ERROR(ENXIO));
 
        ASSERT3U(spa_guid(spa), !=, *newguid);
 
@@ -700,10 +718,10 @@ spa_change_guid_check(void *arg1, void *arg2, dmu_tx_t *tx)
 }
 
 static void
-spa_change_guid_sync(void *arg1, void *arg2, dmu_tx_t *tx)
+spa_change_guid_sync(void *arg, dmu_tx_t *tx)
 {
-       spa_t *spa = arg1;
-       uint64_t *newguid = arg2;
+       uint64_t *newguid = arg;
+       spa_t *spa = dmu_tx_pool(tx)->dp_spa;
        uint64_t oldguid;
        vdev_t *rvd = spa->spa_root_vdev;
 
@@ -715,8 +733,8 @@ spa_change_guid_sync(void *arg1, void *arg2, dmu_tx_t *tx)
        vdev_config_dirty(rvd);
        spa_config_exit(spa, SCL_STATE, FTAG);
 
-       spa_history_log_internal(LOG_POOL_GUID_CHANGE, spa, tx,
-           "old=%lld new=%lld", oldguid, *newguid);
+       spa_history_log_internal(spa, "guid change", tx, "old=%llu new=%llu",
+           oldguid, *newguid);
 }
 
 /*
@@ -734,11 +752,12 @@ spa_change_guid(spa_t *spa)
        int error;
        uint64_t guid;
 
+       mutex_enter(&spa->spa_vdev_top_lock);
        mutex_enter(&spa_namespace_lock);
        guid = spa_generate_guid(NULL);
 
-       error = dsl_sync_task_do(spa_get_dsl(spa), spa_change_guid_check,
-           spa_change_guid_sync, spa, &guid, 5);
+       error = dsl_sync_task(spa->spa_name, spa_change_guid_check,
+           spa_change_guid_sync, &guid, 5);
 
        if (error == 0) {
                spa_config_sync(spa, B_FALSE, B_TRUE);
@@ -746,6 +765,7 @@ spa_change_guid(spa_t *spa)
        }
 
        mutex_exit(&spa_namespace_lock);
+       mutex_exit(&spa->spa_vdev_top_lock);
 
        return (error);
 }
@@ -794,48 +814,151 @@ spa_get_errlists(spa_t *spa, avl_tree_t *last, avl_tree_t *scrub)
            offsetof(spa_error_entry_t, se_avl));
 }
 
-static taskq_t *
-spa_taskq_create(spa_t *spa, const char *name, enum zti_modes mode,
-    uint_t value)
+static void
+spa_taskqs_init(spa_t *spa, zio_type_t t, zio_taskq_type_t q)
 {
-       uint_t flags = TASKQ_PREPOPULATE;
+       const zio_taskq_info_t *ztip = &zio_taskqs[t][q];
+       enum zti_modes mode = ztip->zti_mode;
+       uint_t value = ztip->zti_value;
+       uint_t count = ztip->zti_count;
+       spa_taskqs_t *tqs = &spa->spa_zio_taskq[t][q];
+       char name[32];
+       uint_t i, flags = 0;
        boolean_t batch = B_FALSE;
 
-       switch (mode) {
-       case zti_mode_null:
-               return (NULL);          /* no taskq needed */
+       if (mode == ZTI_MODE_NULL) {
+               tqs->stqs_count = 0;
+               tqs->stqs_taskq = NULL;
+               return;
+       }
 
-       case zti_mode_fixed:
+       ASSERT3U(count, >, 0);
+
+       tqs->stqs_count = count;
+       tqs->stqs_taskq = kmem_alloc(count * sizeof (taskq_t *), KM_SLEEP);
+
+       switch (mode) {
+       case ZTI_MODE_FIXED:
                ASSERT3U(value, >=, 1);
                value = MAX(value, 1);
                break;
 
-       case zti_mode_batch:
+       case ZTI_MODE_BATCH:
                batch = B_TRUE;
                flags |= TASKQ_THREADS_CPU_PCT;
                value = zio_taskq_batch_pct;
                break;
 
-       case zti_mode_online_percent:
-               flags |= TASKQ_THREADS_CPU_PCT;
-               break;
-
        default:
-               panic("unrecognized mode for %s taskq (%u:%u) in "
+               panic("unrecognized mode for %s_%s taskq (%u:%u) in "
                    "spa_activate()",
-                   name, mode, value);
+                   zio_type_name[t], zio_taskq_types[q], mode, value);
                break;
        }
 
-       if (zio_taskq_sysdc && spa->spa_proc != &p0) {
-               if (batch)
-                       flags |= TASKQ_DC_BATCH;
+       for (i = 0; i < count; i++) {
+               taskq_t *tq;
 
-               return (taskq_create_sysdc(name, value, 50, INT_MAX,
-                   spa->spa_proc, zio_taskq_basedc, flags));
+               if (count > 1) {
+                       (void) snprintf(name, sizeof (name), "%s_%s_%u",
+                           zio_type_name[t], zio_taskq_types[q], i);
+               } else {
+                       (void) snprintf(name, sizeof (name), "%s_%s",
+                           zio_type_name[t], zio_taskq_types[q]);
+               }
+
+               if (zio_taskq_sysdc && spa->spa_proc != &p0) {
+                       if (batch)
+                               flags |= TASKQ_DC_BATCH;
+
+                       tq = taskq_create_sysdc(name, value, 50, INT_MAX,
+                           spa->spa_proc, zio_taskq_basedc, flags);
+               } else {
+                       pri_t pri = maxclsyspri;
+                       /*
+                        * The write issue taskq can be extremely CPU
+                        * intensive.  Run it at slightly lower priority
+                        * than the other taskqs.
+                        */
+                       if (t == ZIO_TYPE_WRITE && q == ZIO_TASKQ_ISSUE)
+                               pri--;
+
+                       tq = taskq_create_proc(name, value, pri, 50,
+                           INT_MAX, spa->spa_proc, flags);
+               }
+
+               tqs->stqs_taskq[i] = tq;
        }
-       return (taskq_create_proc(name, value, maxclsyspri, 50, INT_MAX,
-           spa->spa_proc, flags));
+}
+
+static void
+spa_taskqs_fini(spa_t *spa, zio_type_t t, zio_taskq_type_t q)
+{
+       spa_taskqs_t *tqs = &spa->spa_zio_taskq[t][q];
+       uint_t i;
+
+       if (tqs->stqs_taskq == NULL) {
+               ASSERT3U(tqs->stqs_count, ==, 0);
+               return;
+       }
+
+       for (i = 0; i < tqs->stqs_count; i++) {
+               ASSERT3P(tqs->stqs_taskq[i], !=, NULL);
+               taskq_destroy(tqs->stqs_taskq[i]);
+       }
+
+       kmem_free(tqs->stqs_taskq, tqs->stqs_count * sizeof (taskq_t *));
+       tqs->stqs_taskq = NULL;
+}
+
+/*
+ * Dispatch a task to the appropriate taskq for the ZFS I/O type and priority.
+ * Note that a type may have multiple discrete taskqs to avoid lock contention
+ * on the taskq itself. In that case we choose which taskq at random by using
+ * the low bits of gethrtime().
+ */
+void
+spa_taskq_dispatch_ent(spa_t *spa, zio_type_t t, zio_taskq_type_t q,
+    task_func_t *func, void *arg, uint_t flags, taskq_ent_t *ent)
+{
+       spa_taskqs_t *tqs = &spa->spa_zio_taskq[t][q];
+       taskq_t *tq;
+
+       ASSERT3P(tqs->stqs_taskq, !=, NULL);
+       ASSERT3U(tqs->stqs_count, !=, 0);
+
+       if (tqs->stqs_count == 1) {
+               tq = tqs->stqs_taskq[0];
+       } else {
+               tq = tqs->stqs_taskq[((uint64_t)gethrtime()) % tqs->stqs_count];
+       }
+
+       taskq_dispatch_ent(tq, func, arg, flags, ent);
+}
+
+/*
+ * Same as spa_taskq_dispatch_ent() but block on the task until completion.
+ */
+void
+spa_taskq_dispatch_sync(spa_t *spa, zio_type_t t, zio_taskq_type_t q,
+    task_func_t *func, void *arg, uint_t flags)
+{
+       spa_taskqs_t *tqs = &spa->spa_zio_taskq[t][q];
+       taskq_t *tq;
+       taskqid_t id;
+
+       ASSERT3P(tqs->stqs_taskq, !=, NULL);
+       ASSERT3U(tqs->stqs_count, !=, 0);
+
+       if (tqs->stqs_count == 1) {
+               tq = tqs->stqs_taskq[0];
+       } else {
+               tq = tqs->stqs_taskq[((uint64_t)gethrtime()) % tqs->stqs_count];
+       }
+
+       id = taskq_dispatch(tq, func, arg, flags);
+       if (id)
+               taskq_wait_id(tq, id);
 }
 
 static void
@@ -845,16 +968,7 @@ spa_create_zio_taskqs(spa_t *spa)
 
        for (t = 0; t < ZIO_TYPES; t++) {
                for (q = 0; q < ZIO_TASKQ_TYPES; q++) {
-                       const zio_taskq_info_t *ztip = &zio_taskqs[t][q];
-                       enum zti_modes mode = ztip->zti_mode;
-                       uint_t value = ztip->zti_value;
-                       char name[32];
-
-                       (void) snprintf(name, sizeof (name),
-                           "%s_%s", zio_type_name[t], zio_taskq_types[q]);
-
-                       spa->spa_zio_taskq[t][q] =
-                           spa_taskq_create(spa, name, mode, value);
+                       spa_taskqs_init(spa, t, q);
                }
        }
 }
@@ -1013,11 +1127,11 @@ spa_deactivate(spa_t *spa)
        list_destroy(&spa->spa_config_dirty_list);
        list_destroy(&spa->spa_state_dirty_list);
 
+       taskq_cancel_id(system_taskq, spa->spa_deadman_tqid);
+
        for (t = 0; t < ZIO_TYPES; t++) {
                for (q = 0; q < ZIO_TASKQ_TYPES; q++) {
-                       if (spa->spa_zio_taskq[t][q] != NULL)
-                               taskq_destroy(spa->spa_zio_taskq[t][q]);
-                       spa->spa_zio_taskq[t][q] = NULL;
+                       spa_taskqs_fini(spa, t, q);
                }
        }
 
@@ -1094,7 +1208,7 @@ spa_config_parse(spa_t *spa, vdev_t **vdp, nvlist_t *nv, vdev_t *parent,
        if (error) {
                vdev_free(*vdp);
                *vdp = NULL;
-               return (EINVAL);
+               return (SET_ERROR(EINVAL));
        }
 
        for (c = 0; c < children; c++) {
@@ -1338,7 +1452,7 @@ spa_load_l2cache(spa_t *spa)
        uint_t nl2cache;
        int i, j, oldnvdevs;
        uint64_t guid;
-       vdev_t *vd, **oldvdevs, **newvdevs = NULL;
+       vdev_t *vd, **oldvdevs, **newvdevs;
        spa_aux_vdev_t *sav = &spa->spa_l2cache;
 
        ASSERT(spa_config_held(spa, SCL_ALL, RW_WRITER) == SCL_ALL);
@@ -1349,6 +1463,7 @@ spa_load_l2cache(spa_t *spa)
                newvdevs = kmem_alloc(nl2cache * sizeof (void *), KM_PUSHPAGE);
        } else {
                nl2cache = 0;
+               newvdevs = NULL;
        }
 
        oldvdevs = sav->sav_vdevs;
@@ -1491,7 +1606,8 @@ spa_check_removed(vdev_t *vd)
        for (c = 0; c < vd->vdev_children; c++)
                spa_check_removed(vd->vdev_child[c]);
 
-       if (vd->vdev_ops->vdev_op_leaf && vdev_is_dead(vd)) {
+       if (vd->vdev_ops->vdev_op_leaf && vdev_is_dead(vd) &&
+           !vd->vdev_ishole) {
                zfs_ereport_post(FM_EREPORT_RESOURCE_AUTOREPLACE,
                    vd->vdev_spa, vd, NULL, 0, 0);
                spa_event_notify(vd->vdev_spa, vd, FM_EREPORT_ZFS_DEVICE_CHECK);
@@ -1624,23 +1740,24 @@ spa_config_valid(spa_t *spa, nvlist_t *config)
 /*
  * Check for missing log devices
  */
-static int
+static boolean_t
 spa_check_logs(spa_t *spa)
 {
+       boolean_t rv = B_FALSE;
+
        switch (spa->spa_log_state) {
        default:
                break;
        case SPA_LOG_MISSING:
                /* need to recheck in case slog has been restored */
        case SPA_LOG_UNKNOWN:
-               if (dmu_objset_find(spa->spa_name, zil_check_log_chain, NULL,
-                   DS_FIND_CHILDREN)) {
+               rv = (dmu_objset_find(spa->spa_name, zil_check_log_chain,
+                   NULL, DS_FIND_CHILDREN) != 0);
+               if (rv)
                        spa_set_log_state(spa, SPA_LOG_MISSING);
-                       return (1);
-               }
                break;
        }
-       return (0);
+       return (rv);
 }
 
 static boolean_t
@@ -1688,11 +1805,11 @@ spa_activate_log(spa_t *spa)
 int
 spa_offline_log(spa_t *spa)
 {
-       int error = 0;
-
-       if ((error = dmu_objset_find(spa_name(spa), zil_vdev_offline,
-           NULL, DS_FIND_CHILDREN)) == 0) {
+       int error;
 
+       error = dmu_objset_find(spa_name(spa), zil_vdev_offline,
+           NULL, DS_FIND_CHILDREN);
+       if (error == 0) {
                /*
                 * We successfully offlined the log device, sync out the
                 * current txg so that the "stubby" block can be removed
@@ -1752,7 +1869,7 @@ spa_load_verify_done(zio_t *zio)
 /*ARGSUSED*/
 static int
 spa_load_verify_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)
 {
        if (bp != NULL) {
                zio_t *rio = arg;
@@ -1813,7 +1930,7 @@ spa_load_verify(spa_t *spa)
 
        if (error) {
                if (error != ENXIO && error != EIO)
-                       error = EIO;
+                       error = SET_ERROR(EIO);
                return (error);
        }
 
@@ -1941,7 +2058,7 @@ spa_load(spa_t *spa, spa_load_state_t state, spa_import_type_t type,
        nvlist_t *nvl;
 
        if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID, &pool_guid))
-               return (EINVAL);
+               return (SET_ERROR(EINVAL));
 
        ASSERT(spa->spa_comment == NULL);
        if (nvlist_lookup_string(config, ZPOOL_CONFIG_COMMENT, &comment) == 0)
@@ -1960,7 +2077,7 @@ spa_load(spa_t *spa, spa_load_state_t state, spa_import_type_t type,
 
        if ((state == SPA_LOAD_IMPORT || state == SPA_LOAD_TRYIMPORT) &&
            spa_guid_exists(pool_guid, 0)) {
-               error = EEXIST;
+               error = SET_ERROR(EEXIST);
        } else {
                spa->spa_config_guid = pool_guid;
 
@@ -2027,7 +2144,7 @@ spa_load_impl(spa_t *spa, uint64_t pool_guid, nvlist_t *config,
        spa->spa_load_state = state;
 
        if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, &nvroot))
-               return (EINVAL);
+               return (SET_ERROR(EINVAL));
 
        parse = (type == SPA_IMPORT_EXISTING ?
            VDEV_ALLOC_LOAD : VDEV_ALLOC_SPLIT);
@@ -2087,7 +2204,7 @@ spa_load_impl(spa_t *spa, uint64_t pool_guid, nvlist_t *config,
                        return (error);
 
                if (rvd->vdev_state <= VDEV_STATE_CANT_OPEN)
-                       return (ENXIO);
+                       return (SET_ERROR(ENXIO));
        }
 
        /*
@@ -2318,12 +2435,12 @@ spa_load_impl(spa_t *spa, uint64_t pool_guid, nvlist_t *config,
                            hostid != myhostid) {
                                nvlist_free(nvconfig);
                                cmn_err(CE_WARN, "pool '%s' could not be "
-                                   "loaded as it was last accessed by "
-                                   "another system (host: %s hostid: 0x%lx). "
-                                   "See: http://zfsonlinux.org/msg/ZFS-8000-EY",
+                                   "loaded as it was last accessed by another "
+                                   "system (host: %s hostid: 0x%lx). See: "
+                                   "http://zfsonlinux.org/msg/ZFS-8000-EY",
                                    spa_name(spa), hostname,
                                    (unsigned long)hostid);
-                               return (EBADF);
+                               return (SET_ERROR(EBADF));
                        }
                }
                if (nvlist_lookup_nvlist(spa->spa_config,
@@ -2512,7 +2629,7 @@ spa_load_impl(spa_t *spa, uint64_t pool_guid, nvlist_t *config,
                 * more toplevel vdevs are faulted.
                 */
                if (rvd->vdev_state <= VDEV_STATE_CANT_OPEN)
-                       return (ENXIO);
+                       return (SET_ERROR(ENXIO));
 
                if (spa_check_logs(spa)) {
                        *ereport = FM_EREPORT_ZFS_LOG_REPLAY;
@@ -2610,6 +2727,12 @@ spa_load_impl(spa_t *spa, uint64_t pool_guid, nvlist_t *config,
                    vdev_resilver_needed(rvd, NULL, NULL))
                        spa_async_request(spa, SPA_ASYNC_RESILVER);
 
+               /*
+                * Log the fact that we booted up (so that we can detect if
+                * we rebooted in the middle of an operation).
+                */
+               spa_history_log_version(spa, "open");
+
                /*
                 * Delete any inconsistent datasets.
                 */
@@ -2752,6 +2875,7 @@ spa_open_common(const char *pool, spa_t **spapp, void *tag, nvlist_t *nvpolicy,
        spa_load_state_t state = SPA_LOAD_OPEN;
        int error;
        int locked = B_FALSE;
+       int firstopen = B_FALSE;
 
        *spapp = NULL;
 
@@ -2769,12 +2893,14 @@ spa_open_common(const char *pool, spa_t **spapp, void *tag, nvlist_t *nvpolicy,
        if ((spa = spa_lookup(pool)) == NULL) {
                if (locked)
                        mutex_exit(&spa_namespace_lock);
-               return (ENOENT);
+               return (SET_ERROR(ENOENT));
        }
 
        if (spa->spa_state == POOL_STATE_UNINITIALIZED) {
                zpool_rewind_policy_t policy;
 
+               firstopen = B_TRUE;
+
                zpool_get_rewind_policy(nvpolicy ? nvpolicy : spa->spa_config,
                    &policy);
                if (policy.zrp_request & ZPOOL_DO_REWIND)
@@ -2802,7 +2928,7 @@ spa_open_common(const char *pool, spa_t **spapp, void *tag, nvlist_t *nvpolicy,
                        spa_remove(spa);
                        if (locked)
                                mutex_exit(&spa_namespace_lock);
-                       return (ENOENT);
+                       return (SET_ERROR(ENOENT));
                }
 
                if (error) {
@@ -2849,6 +2975,11 @@ spa_open_common(const char *pool, spa_t **spapp, void *tag, nvlist_t *nvpolicy,
                mutex_exit(&spa_namespace_lock);
        }
 
+#ifdef _KERNEL
+       if (firstopen)
+               zvol_create_minors(spa->spa_name);
+#endif
+
        *spapp = spa;
 
        return (0);
@@ -3133,14 +3264,14 @@ spa_validate_aux_devs(spa_t *spa, nvlist_t *nvroot, uint64_t crtxg, int mode,
                return (0);
 
        if (ndev == 0)
-               return (EINVAL);
+               return (SET_ERROR(EINVAL));
 
        /*
         * Make sure the pool is formatted with a version that supports this
         * device type.
         */
        if (spa_version(spa) < version)
-               return (ENOTSUP);
+               return (SET_ERROR(ENOTSUP));
 
        /*
         * Set the pending device list so we correctly handle device in-use
@@ -3156,7 +3287,7 @@ spa_validate_aux_devs(spa_t *spa, nvlist_t *nvroot, uint64_t crtxg, int mode,
 
                if (!vd->vdev_ops->vdev_op_leaf) {
                        vdev_free(vd);
-                       error = EINVAL;
+                       error = SET_ERROR(EINVAL);
                        goto out;
                }
 
@@ -3167,7 +3298,7 @@ spa_validate_aux_devs(spa_t *spa, nvlist_t *nvroot, uint64_t crtxg, int mode,
 #ifdef _KERNEL
                if ((strcmp(config, ZPOOL_CONFIG_L2CACHE) == 0) &&
                    strcmp(vd->vdev_ops->vdev_op_type, VDEV_TYPE_DISK) != 0) {
-                       error = ENOTBLK;
+                       error = SET_ERROR(ENOTBLK);
                        vdev_free(vd);
                        goto out;
                }
@@ -3286,7 +3417,7 @@ spa_l2cache_drop(spa_t *spa)
  */
 int
 spa_create(const char *pool, nvlist_t *nvroot, nvlist_t *props,
-    const char *history_str, nvlist_t *zplprops)
+    nvlist_t *zplprops)
 {
        spa_t *spa;
        char *altroot = NULL;
@@ -3308,7 +3439,7 @@ spa_create(const char *pool, nvlist_t *nvroot, nvlist_t *props,
        mutex_enter(&spa_namespace_lock);
        if (spa_lookup(pool) != NULL) {
                mutex_exit(&spa_namespace_lock);
-               return (EEXIST);
+               return (SET_ERROR(EEXIST));
        }
 
        /*
@@ -3361,7 +3492,7 @@ spa_create(const char *pool, nvlist_t *nvroot, nvlist_t *props,
        ASSERT(error != 0 || spa->spa_root_vdev == rvd);
 
        if (error == 0 && !zfs_allocatable_devs(nvroot))
-               error = EINVAL;
+               error = SET_ERROR(EINVAL);
 
        if (error == 0 &&
            (error = vdev_create(rvd, txg, B_FALSE)) == 0 &&
@@ -3491,7 +3622,7 @@ spa_create(const char *pool, nvlist_t *nvroot, nvlist_t *props,
 
        if (props != NULL) {
                spa_configfile_set(spa, props, B_FALSE);
-               spa_sync_props(spa, props, tx);
+               spa_sync_props(props, tx);
        }
 
        dmu_tx_commit(tx);
@@ -3507,9 +3638,7 @@ spa_create(const char *pool, nvlist_t *nvroot, nvlist_t *props,
 
        spa_config_sync(spa, B_FALSE, B_TRUE);
 
-       if (version >= SPA_VERSION_ZPOOL_HISTORY && history_str != NULL)
-               (void) spa_history_log(spa, history_str, LOG_CMD_POOL_CREATE);
-       spa_history_log_version(spa, LOG_POOL_CREATE);
+       spa_history_log_version(spa, "create");
 
        spa->spa_minref = refcount_count(&spa->spa_refcount);
 
@@ -3637,7 +3766,7 @@ spa_import_rootpool(char *devpath, char *devid)
        if (config == NULL) {
                cmn_err(CE_NOTE, "Cannot read the pool label from '%s'",
                    devpath);
-               return (EIO);
+               return (SET_ERROR(EIO));
        }
 
        VERIFY(nvlist_lookup_string(config, ZPOOL_CONFIG_POOL_NAME,
@@ -3680,7 +3809,7 @@ spa_import_rootpool(char *devpath, char *devid)
        if ((bvd = vdev_lookup_by_guid(rvd, guid)) == NULL) {
                cmn_err(CE_NOTE, "Can not find the boot vdev for guid %llu",
                    (u_longlong_t)guid);
-               error = ENOENT;
+               error = SET_ERROR(ENOENT);
                goto out;
        }
 
@@ -3692,7 +3821,7 @@ spa_import_rootpool(char *devpath, char *devid)
        if (avd != bvd) {
                cmn_err(CE_NOTE, "The boot device is 'degraded'. Please "
                    "try booting from '%s'", avd->vdev_path);
-               error = EINVAL;
+               error = SET_ERROR(EINVAL);
                goto out;
        }
 
@@ -3706,12 +3835,11 @@ spa_import_rootpool(char *devpath, char *devid)
                    "try booting from '%s'",
                    bvd->vdev_parent->
                    vdev_child[bvd->vdev_parent->vdev_children - 1]->vdev_path);
-               error = EINVAL;
+               error = SET_ERROR(EINVAL);
                goto out;
        }
 
        error = 0;
-       spa_history_log_version(spa, LOG_POOL_IMPORT);
 out:
        spa_config_enter(spa, SCL_ALL, FTAG, RW_WRITER);
        vdev_free(rvd);
@@ -3728,7 +3856,7 @@ out:
  * Import a non-root pool into the system.
  */
 int
-spa_import(const char *pool, nvlist_t *config, nvlist_t *props, uint64_t flags)
+spa_import(char *pool, nvlist_t *config, nvlist_t *props, uint64_t flags)
 {
        spa_t *spa;
        char *altroot = NULL;
@@ -3747,7 +3875,7 @@ spa_import(const char *pool, nvlist_t *config, nvlist_t *props, uint64_t flags)
        mutex_enter(&spa_namespace_lock);
        if (spa_lookup(pool) != NULL) {
                mutex_exit(&spa_namespace_lock);
-               return (EEXIST);
+               return (SET_ERROR(EEXIST));
        }
 
        /*
@@ -3773,7 +3901,7 @@ spa_import(const char *pool, nvlist_t *config, nvlist_t *props, uint64_t flags)
                spa_config_sync(spa, B_FALSE, B_TRUE);
 
                mutex_exit(&spa_namespace_lock);
-               spa_history_log_version(spa, LOG_POOL_IMPORT);
+               spa_history_log_version(spa, "import");
 
                return (0);
        }
@@ -3904,7 +4032,11 @@ spa_import(const char *pool, nvlist_t *config, nvlist_t *props, uint64_t flags)
        spa_async_request(spa, SPA_ASYNC_AUTOEXPAND);
 
        mutex_exit(&spa_namespace_lock);
-       spa_history_log_version(spa, LOG_POOL_IMPORT);
+       spa_history_log_version(spa, "import");
+
+#ifdef _KERNEL
+       zvol_create_minors(pool);
+#endif
 
        return (0);
 }
@@ -3967,7 +4099,9 @@ spa_tryimport(nvlist_t *tryconfig)
                        if (dsl_dsobj_to_dsname(spa_name(spa),
                            spa->spa_bootfs, tmpname) == 0) {
                                char *cp;
-                               char *dsname = kmem_alloc(MAXPATHLEN, KM_PUSHPAGE);
+                               char *dsname;
+
+                               dsname = kmem_alloc(MAXPATHLEN, KM_PUSHPAGE);
 
                                cp = strchr(tmpname, '/');
                                if (cp == NULL) {
@@ -4020,12 +4154,12 @@ spa_export_common(char *pool, int new_state, nvlist_t **oldconfig,
                *oldconfig = NULL;
 
        if (!(spa_mode_global & FWRITE))
-               return (EROFS);
+               return (SET_ERROR(EROFS));
 
        mutex_enter(&spa_namespace_lock);
        if ((spa = spa_lookup(pool)) == NULL) {
                mutex_exit(&spa_namespace_lock);
-               return (ENOENT);
+               return (SET_ERROR(ENOENT));
        }
 
        /*
@@ -4059,7 +4193,7 @@ spa_export_common(char *pool, int new_state, nvlist_t **oldconfig,
                    new_state != POOL_STATE_UNINITIALIZED)) {
                        spa_async_resume(spa);
                        mutex_exit(&spa_namespace_lock);
-                       return (EBUSY);
+                       return (SET_ERROR(EBUSY));
                }
 
                /*
@@ -4072,7 +4206,7 @@ spa_export_common(char *pool, int new_state, nvlist_t **oldconfig,
                    spa_has_active_shared_spare(spa)) {
                        spa_async_resume(spa);
                        mutex_exit(&spa_namespace_lock);
-                       return (EXDEV);
+                       return (SET_ERROR(EXDEV));
                }
 
                /*
@@ -4268,12 +4402,12 @@ int
 spa_vdev_attach(spa_t *spa, uint64_t guid, nvlist_t *nvroot, int replacing)
 {
        uint64_t txg, dtl_max_txg;
-       ASSERTV(vdev_t *rvd = spa->spa_root_vdev;)
        vdev_t *oldvd, *newvd, *newrootvd, *pvd, *tvd;
        vdev_ops_t *pvops;
        char *oldvdpath, *newvdpath;
        int newvd_isspare;
        int error;
+       ASSERTV(vdev_t *rvd = spa->spa_root_vdev);
 
        ASSERT(spa_writeable(spa));
 
@@ -4382,7 +4516,7 @@ spa_vdev_attach(spa_t *spa, uint64_t guid, nvlist_t *nvroot, int replacing)
        }
 
        /* mark the device being resilvered */
-       newvd->vdev_resilvering = B_TRUE;
+       newvd->vdev_resilver_txg = txg;
 
        /*
         * If the parent is not a mirror, or if we're replacing, insert the new
@@ -4443,7 +4577,7 @@ spa_vdev_attach(spa_t *spa, uint64_t guid, nvlist_t *nvroot, int replacing)
         */
        (void) spa_vdev_exit(spa, newrootvd, dtl_max_txg, 0);
 
-       spa_history_log_internal(LOG_POOL_VDEV_ATTACH, spa, NULL,
+       spa_history_log_internal(spa, "vdev attach", NULL,
            "%s vdev=%s %s vdev=%s",
            replacing && newvd_isspare ? "spare in" :
            replacing ? "replace" : "attach", newvdpath,
@@ -4460,6 +4594,7 @@ spa_vdev_attach(spa_t *spa, uint64_t guid, nvlist_t *nvroot, int replacing)
 
 /*
  * Detach a device from a mirror or replacing vdev.
+ *
  * If 'replace_done' is specified, only detach if the parent
  * is a replacing vdev.
  */
@@ -4468,13 +4603,12 @@ spa_vdev_detach(spa_t *spa, uint64_t guid, uint64_t pguid, int replace_done)
 {
        uint64_t txg;
        int error;
-       ASSERTV(vdev_t *rvd = spa->spa_root_vdev;)
        vdev_t *vd, *pvd, *cvd, *tvd;
        boolean_t unspare = B_FALSE;
        uint64_t unspare_guid = 0;
        char *vdpath;
        int c, t;
-
+       ASSERTV(vdev_t *rvd = spa->spa_root_vdev);
        ASSERT(spa_writeable(spa));
 
        txg = spa_vdev_enter(spa);
@@ -4612,7 +4746,6 @@ spa_vdev_detach(spa_t *spa, uint64_t guid, uint64_t pguid, int replace_done)
                if (pvd->vdev_ops == &vdev_spare_ops)
                        cvd->vdev_unspare = B_FALSE;
                vdev_remove_parent(cvd);
-               cvd->vdev_resilvering = B_FALSE;
        }
 
 
@@ -4661,7 +4794,7 @@ spa_vdev_detach(spa_t *spa, uint64_t guid, uint64_t pguid, int replace_done)
 
        error = spa_vdev_exit(spa, vd, txg, 0);
 
-       spa_history_log_internal(LOG_POOL_VDEV_DETACH, spa, NULL,
+       spa_history_log_internal(spa, "detach", NULL,
            "vdev=%s", vdpath);
        spa_strfree(vdpath);
 
@@ -4782,7 +4915,7 @@ spa_vdev_split_mirror(spa_t *spa, char *newname, nvlist_t *config,
                            spa->spa_root_vdev->vdev_child[c]->vdev_islog) {
                                continue;
                        } else {
-                               error = EINVAL;
+                               error = SET_ERROR(EINVAL);
                                break;
                        }
                }
@@ -4790,14 +4923,14 @@ spa_vdev_split_mirror(spa_t *spa, char *newname, nvlist_t *config,
                /* which disk is going to be split? */
                if (nvlist_lookup_uint64(child[c], ZPOOL_CONFIG_GUID,
                    &glist[c]) != 0) {
-                       error = EINVAL;
+                       error = SET_ERROR(EINVAL);
                        break;
                }
 
                /* look it up in the spa */
                vml[c] = spa_lookup_by_guid(spa, glist[c], B_FALSE);
                if (vml[c] == NULL) {
-                       error = ENODEV;
+                       error = SET_ERROR(ENODEV);
                        break;
                }
 
@@ -4811,12 +4944,12 @@ spa_vdev_split_mirror(spa_t *spa, char *newname, nvlist_t *config,
                    vml[c]->vdev_children != 0 ||
                    vml[c]->vdev_state != VDEV_STATE_HEALTHY ||
                    c != spa->spa_root_vdev->vdev_child[c]->vdev_id) {
-                       error = EINVAL;
+                       error = SET_ERROR(EINVAL);
                        break;
                }
 
                if (vdev_dtl_required(vml[c])) {
-                       error = EBUSY;
+                       error = SET_ERROR(EBUSY);
                        break;
                }
 
@@ -4930,9 +5063,8 @@ spa_vdev_split_mirror(spa_t *spa, char *newname, nvlist_t *config,
                if (vml[c] != NULL) {
                        vdev_split(vml[c]);
                        if (error == 0)
-                               spa_history_log_internal(LOG_POOL_VDEV_DETACH,
-                                   spa, tx, "vdev=%s",
-                                   vml[c]->vdev_path);
+                               spa_history_log_internal(spa, "detach", tx,
+                                   "vdev=%s", vml[c]->vdev_path);
                        vdev_free(vml[c]);
                }
        }
@@ -4947,8 +5079,8 @@ spa_vdev_split_mirror(spa_t *spa, char *newname, nvlist_t *config,
                zio_handle_panic_injection(spa, FTAG, 3);
 
        /* split is complete; log a history record */
-       spa_history_log_internal(LOG_POOL_SPLIT, newspa, NULL,
-           "split new pool %s from pool %s", newname, spa_name(spa));
+       spa_history_log_internal(newspa, "split", NULL,
+           "from pool %s", spa_name(spa));
 
        kmem_free(vml, children * sizeof (vdev_t *));
 
@@ -5048,7 +5180,7 @@ spa_vdev_remove_evacuate(spa_t *spa, vdev_t *vd)
                if (vd->vdev_stat.vs_alloc != 0)
                        error = spa_offline_log(spa);
        } else {
-               error = ENOTSUP;
+               error = SET_ERROR(ENOTSUP);
        }
 
        if (error)
@@ -5058,7 +5190,7 @@ spa_vdev_remove_evacuate(spa_t *spa, vdev_t *vd)
         * The evacuation succeeded.  Remove any remaining MOS metadata
         * associated with this vdev, and wait for these changes to sync.
         */
-       ASSERT3U(vd->vdev_stat.vs_alloc, ==, 0);
+       ASSERT0(vd->vdev_stat.vs_alloc);
        txg = spa_vdev_config_enter(spa);
        vd->vdev_removing = B_TRUE;
        vdev_dirty(vd, 0, NULL, txg);
@@ -5119,11 +5251,9 @@ spa_vdev_remove_from_namespace(spa_t *spa, vdev_t *vd)
  * the spa_vdev_config_[enter/exit] functions which allow us to
  * grab and release the spa_config_lock while still holding the namespace
  * lock.  During each step the configuration is synced out.
- */
-
-/*
- * Remove a device from the pool.  Currently, this supports removing only hot
- * spares, slogs, and level 2 ARC devices.
+ *
+ * Currently, this supports removing only hot spares, slogs, and level 2 ARC
+ * devices.
  */
 int
 spa_vdev_remove(spa_t *spa, uint64_t guid, boolean_t unspare)
@@ -5157,7 +5287,7 @@ spa_vdev_remove(spa_t *spa, uint64_t guid, boolean_t unspare)
                        spa_load_spares(spa);
                        spa->spa_spares.sav_sync = B_TRUE;
                } else {
-                       error = EBUSY;
+                       error = SET_ERROR(EBUSY);
                }
        } else if (spa->spa_l2cache.sav_vdevs != NULL &&
            nvlist_lookup_nvlist_array(spa->spa_l2cache.sav_config,
@@ -5217,12 +5347,12 @@ spa_vdev_remove(spa_t *spa, uint64_t guid, boolean_t unspare)
                /*
                 * Normal vdevs cannot be removed (yet).
                 */
-               error = ENOTSUP;
+               error = SET_ERROR(ENOTSUP);
        } else {
                /*
                 * There is no vdev of any kind with the specified guid.
                 */
-               error = ENOENT;
+               error = SET_ERROR(ENOENT);
        }
 
        if (!locked)
@@ -5233,7 +5363,7 @@ spa_vdev_remove(spa_t *spa, uint64_t guid, boolean_t unspare)
 
 /*
  * Find any device that's done replacing, or a vdev marked 'unspare' that's
- * current spared, so we can detach it.
+ * currently spared, so we can detach it.
  */
 static vdev_t *
 spa_vdev_resilver_done_hunt(vdev_t *vd)
@@ -5336,6 +5466,8 @@ spa_vdev_resilver_done(spa_t *spa)
                        ASSERT(pvd->vdev_ops == &vdev_replacing_ops);
                        sguid = ppvd->vdev_child[1]->vdev_guid;
                }
+               ASSERT(vd->vdev_resilver_txg == 0 || !vdev_dtl_required(vd));
+
                spa_config_exit(spa, SCL_ALL, FTAG);
                if (spa_vdev_detach(spa, guid, pguid, B_TRUE) != 0)
                        return;
@@ -5410,7 +5542,7 @@ spa_scan_stop(spa_t *spa)
 {
        ASSERT(spa_config_held(spa, SCL_ALL, RW_WRITER) == 0);
        if (dsl_scan_resilvering(spa->spa_dsl_pool))
-               return (EBUSY);
+               return (SET_ERROR(EBUSY));
        return (dsl_scan_cancel(spa->spa_dsl_pool));
 }
 
@@ -5420,7 +5552,7 @@ spa_scan(spa_t *spa, pool_scan_func_t func)
        ASSERT(spa_config_held(spa, SCL_ALL, RW_WRITER) == 0);
 
        if (func >= POOL_SCAN_FUNCS || func == POOL_SCAN_NONE)
-               return (ENOTSUP);
+               return (SET_ERROR(ENOTSUP));
 
        /*
         * If a resilver was requested, but there is no DTL on a
@@ -5530,8 +5662,7 @@ spa_async_thread(spa_t *spa)
                 * then log an internal history event.
                 */
                if (new_space != old_space) {
-                       spa_history_log_internal(LOG_POOL_VDEV_ONLINE,
-                           spa, NULL,
+                       spa_history_log_internal(spa, "vdev online", NULL,
                            "pool '%s' size: %llu(+%llu)",
                            spa_name(spa), new_space, new_space - old_space);
                }
@@ -5651,6 +5782,31 @@ spa_free_sync_cb(void *arg, const blkptr_t *bp, dmu_tx_t *tx)
        return (0);
 }
 
+/*
+ * Note: this simple function is not inlined to make it easier to dtrace the
+ * amount of time spent syncing frees.
+ */
+static void
+spa_sync_frees(spa_t *spa, bplist_t *bpl, dmu_tx_t *tx)
+{
+       zio_t *zio = zio_root(spa, NULL, NULL, 0);
+       bplist_iterate(bpl, spa_free_sync_cb, zio, tx);
+       VERIFY(zio_wait(zio) == 0);
+}
+
+/*
+ * Note: this simple function is not inlined to make it easier to dtrace the
+ * amount of time spent syncing deferred frees.
+ */
+static void
+spa_sync_deferred_frees(spa_t *spa, dmu_tx_t *tx)
+{
+       zio_t *zio = zio_root(spa, NULL, NULL, 0);
+       VERIFY3U(bpobj_iterate(&spa->spa_deferred_bpobj,
+           spa_free_sync_cb, zio, tx), ==, 0);
+       VERIFY0(zio_wait(zio));
+}
+
 static void
 spa_sync_nvlist(spa_t *spa, uint64_t obj, nvlist_t *nv, dmu_tx_t *tx)
 {
@@ -5712,7 +5868,7 @@ spa_sync_aux_dev(spa_t *spa, spa_aux_vdev_t *sav, dmu_tx_t *tx,
        if (sav->sav_count == 0) {
                VERIFY(nvlist_add_nvlist_array(nvroot, config, NULL, 0) == 0);
        } else {
-               list = kmem_alloc(sav->sav_count * sizeof (void *), KM_PUSHPAGE);
+               list = kmem_alloc(sav->sav_count*sizeof (void *), KM_PUSHPAGE);
                for (i = 0; i < sav->sav_count; i++)
                        list[i] = vdev_config_generate(spa, sav->sav_vdevs[i],
                            B_FALSE, VDEV_CONFIG_L2CACHE);
@@ -5742,6 +5898,14 @@ spa_sync_config_object(spa_t *spa, dmu_tx_t *tx)
        config = spa_config_generate(spa, spa->spa_root_vdev,
            dmu_tx_get_txg(tx), B_FALSE);
 
+       /*
+        * If we're upgrading the spa version then make sure that
+        * the config object gets updated with the correct version.
+        */
+       if (spa->spa_ubsync.ub_version < spa->spa_uberblock.ub_version)
+               fnvlist_add_uint64(config, ZPOOL_CONFIG_VERSION,
+                   spa->spa_uberblock.ub_version);
+
        spa_config_exit(spa, SCL_STATE, FTAG);
 
        if (spa->spa_config_syncing)
@@ -5752,32 +5916,34 @@ spa_sync_config_object(spa_t *spa, dmu_tx_t *tx)
 }
 
 static void
-spa_sync_version(void *arg1, void *arg2, dmu_tx_t *tx)
+spa_sync_version(void *arg, dmu_tx_t *tx)
 {
-       spa_t *spa = arg1;
-       uint64_t version = *(uint64_t *)arg2;
+       uint64_t *versionp = arg;
+       uint64_t version = *versionp;
+       spa_t *spa = dmu_tx_pool(tx)->dp_spa;
 
        /*
         * Setting the version is special cased when first creating the pool.
         */
        ASSERT(tx->tx_txg != TXG_INITIAL);
 
-       ASSERT(version <= SPA_VERSION);
+       ASSERT(SPA_VERSION_IS_SUPPORTED(version));
        ASSERT(version >= spa_version(spa));
 
        spa->spa_uberblock.ub_version = version;
        vdev_config_dirty(spa->spa_root_vdev);
+       spa_history_log_internal(spa, "set", tx, "version=%lld", version);
 }
 
 /*
  * Set zpool properties.
  */
 static void
-spa_sync_props(void *arg1, void *arg2, dmu_tx_t *tx)
+spa_sync_props(void *arg, dmu_tx_t *tx)
 {
-       spa_t *spa = arg1;
+       nvlist_t *nvp = arg;
+       spa_t *spa = dmu_tx_pool(tx)->dp_spa;
        objset_t *mos = spa->spa_meta_objset;
-       nvlist_t *nvp = arg2;
        nvpair_t *elem = NULL;
 
        mutex_enter(&spa->spa_props_lock);
@@ -5802,6 +5968,8 @@ spa_sync_props(void *arg1, void *arg2, dmu_tx_t *tx)
                        VERIFY3U(0, ==, zfeature_lookup_name(fname, &feature));
 
                        spa_feature_enable(spa, feature, tx);
+                       spa_history_log_internal(spa, "set", tx,
+                           "%s=enabled", nvpair_name(elem));
                        break;
 
                case ZPOOL_PROP_VERSION:
@@ -5841,6 +6009,8 @@ spa_sync_props(void *arg1, void *arg2, dmu_tx_t *tx)
                         */
                        if (tx->tx_txg != TXG_INITIAL)
                                vdev_config_dirty(spa->spa_root_vdev);
+                       spa_history_log_internal(spa, "set", tx,
+                           "%s=%s", nvpair_name(elem), strval);
                        break;
                default:
                        /*
@@ -5863,7 +6033,8 @@ spa_sync_props(void *arg1, void *arg2, dmu_tx_t *tx)
                                VERIFY(zap_update(mos,
                                    spa->spa_pool_props_object, propname,
                                    1, strlen(strval) + 1, strval, tx) == 0);
-
+                               spa_history_log_internal(spa, "set", tx,
+                                   "%s=%s", nvpair_name(elem), strval);
                        } else if (nvpair_type(elem) == DATA_TYPE_UINT64) {
                                VERIFY(nvpair_value_uint64(elem, &intval) == 0);
 
@@ -5875,6 +6046,8 @@ spa_sync_props(void *arg1, void *arg2, dmu_tx_t *tx)
                                VERIFY(zap_update(mos,
                                    spa->spa_pool_props_object, propname,
                                    8, 1, &intval, tx) == 0);
+                               spa_history_log_internal(spa, "set", tx,
+                                   "%s=%lld", nvpair_name(elem), intval);
                        } else {
                                ASSERT(0); /* not allowed */
                        }
@@ -5903,13 +6076,6 @@ spa_sync_props(void *arg1, void *arg2, dmu_tx_t *tx)
                        }
                }
 
-               /* log internal history if this is not a zpool create */
-               if (spa_version(spa) >= SPA_VERSION_ZPOOL_HISTORY &&
-                   tx->tx_txg != TXG_INITIAL) {
-                       spa_history_log_internal(LOG_POOL_PROPSET,
-                           spa, tx, "%s %lld %s",
-                           nvpair_name(elem), intval, spa_name(spa));
-               }
        }
 
        mutex_exit(&spa->spa_props_lock);
@@ -5929,6 +6095,8 @@ spa_sync_upgrades(spa_t *spa, dmu_tx_t *tx)
 
        ASSERT(spa->spa_sync_pass == 1);
 
+       rrw_enter(&dp->dp_config_rwlock, RW_WRITER, FTAG);
+
        if (spa->spa_ubsync.ub_version < SPA_VERSION_ORIGIN &&
            spa->spa_uberblock.ub_version >= SPA_VERSION_ORIGIN) {
                dsl_pool_create_origin(dp, tx);
@@ -5954,6 +6122,7 @@ spa_sync_upgrades(spa_t *spa, dmu_tx_t *tx)
            spa->spa_uberblock.ub_version >= SPA_VERSION_FEATURES) {
                spa_feature_create_zap_objects(spa, tx);
        }
+       rrw_exit(&dp->dp_config_rwlock, FTAG);
 }
 
 /*
@@ -5965,7 +6134,6 @@ spa_sync(spa_t *spa, uint64_t txg)
 {
        dsl_pool_t *dp = spa->spa_dsl_pool;
        objset_t *mos = spa->spa_meta_objset;
-       bpobj_t *defer_bpo = &spa->spa_deferred_bpobj;
        bplist_t *free_bpl = &spa->spa_free_bplist[txg & TXG_MASK];
        vdev_t *rvd = spa->spa_root_vdev;
        vdev_t *vd;
@@ -6009,6 +6177,12 @@ spa_sync(spa_t *spa, uint64_t txg)
 
        tx = dmu_tx_create_assigned(dp, txg);
 
+       spa->spa_sync_starttime = gethrtime();
+       taskq_cancel_id(system_taskq, spa->spa_deadman_tqid);
+       spa->spa_deadman_tqid = taskq_dispatch_delay(system_taskq,
+           spa_deadman, spa, TQ_PUSHPAGE, ddi_get_lbolt() +
+           NSEC_TO_TICK(spa->spa_deadman_synctime));
+
        /*
         * If we are upgrading to SPA_VERSION_RAIDZ_DEFLATE this txg,
         * set spa_deflate if we have no raid-z vdevs.
@@ -6042,10 +6216,7 @@ spa_sync(spa_t *spa, uint64_t txg)
            !txg_list_empty(&dp->dp_sync_tasks, txg) ||
            ((dsl_scan_active(dp->dp_scan) ||
            txg_sync_waiting(dp)) && !spa_shutting_down(spa))) {
-               zio_t *zio = zio_root(spa, NULL, NULL, 0);
-               VERIFY3U(bpobj_iterate(defer_bpo,
-                   spa_free_sync_cb, zio, tx), ==, 0);
-               VERIFY3U(zio_wait(zio), ==, 0);
+               spa_sync_deferred_frees(spa, tx);
        }
 
        /*
@@ -6062,14 +6233,11 @@ spa_sync(spa_t *spa, uint64_t txg)
                spa_errlog_sync(spa, txg);
                dsl_pool_sync(dp, txg);
 
-               if (pass <= SYNC_PASS_DEFERRED_FREE) {
-                       zio_t *zio = zio_root(spa, NULL, NULL, 0);
-                       bplist_iterate(free_bpl, spa_free_sync_cb,
-                           zio, tx);
-                       VERIFY(zio_wait(zio) == 0);
+               if (pass < zfs_sync_pass_deferred_free) {
+                       spa_sync_frees(spa, free_bpl, tx);
                } else {
                        bplist_iterate(free_bpl, bpobj_enqueue_cb,
-                           defer_bpo, tx);
+                           &spa->spa_deferred_bpobj, tx);
                }
 
                ddt_sync(spa, txg);
@@ -6137,6 +6305,9 @@ spa_sync(spa_t *spa, uint64_t txg)
        }
        dmu_tx_commit(tx);
 
+       taskq_cancel_id(system_taskq, spa->spa_deadman_tqid);
+       spa->spa_deadman_tqid = 0;
+
        /*
         * Clear the dirty config list.
         */
@@ -6286,7 +6457,7 @@ spa_upgrade(spa_t *spa, uint64_t version)
         * future version would result in an unopenable pool, this shouldn't be
         * possible.
         */
-       ASSERT(spa->spa_uberblock.ub_version <= SPA_VERSION);
+       ASSERT(SPA_VERSION_IS_SUPPORTED(spa->spa_uberblock.ub_version));
        ASSERT(version >= spa->spa_uberblock.ub_version);
 
        spa->spa_uberblock.ub_version = version;