Illumos #3498 panic in arc_read()
[zfs.git] / module / zfs / spa.c
index 0a785f7..e986e92 100644 (file)
 #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_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_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,17 +104,30 @@ 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;
@@ -794,48 +809,146 @@ 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(value, >=, 1);
-               value = MAX(value, 1);
-               break;
+       ASSERT3U(count, >, 0);
 
-       case zti_mode_batch:
-               batch = B_TRUE;
-               flags |= TASKQ_THREADS_CPU_PCT;
-               value = zio_taskq_batch_pct;
-               break;
+       tqs->stqs_count = count;
+       tqs->stqs_taskq = kmem_alloc(count * sizeof (taskq_t *), KM_SLEEP);
 
-       case zti_mode_online_percent:
-               flags |= TASKQ_THREADS_CPU_PCT;
-               break;
+       for (i = 0; i < count; i++) {
+               taskq_t *tq;
 
-       default:
-               panic("unrecognized mode for %s taskq (%u:%u) in "
-                   "spa_activate()",
-                   name, mode, value);
-               break;
+               switch (mode) {
+               case ZTI_MODE_FIXED:
+                       ASSERT3U(value, >=, 1);
+                       value = MAX(value, 1);
+                       break;
+
+               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_%s taskq (%u:%u) in "
+                           "spa_activate()",
+                           zio_type_name[t], zio_taskq_types[q], mode, value);
+                       break;
+               }
+
+               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 {
+                       tq = taskq_create_proc(name, value, maxclsyspri, 50,
+                           INT_MAX, spa->spa_proc, flags);
+               }
+
+               tqs->stqs_taskq[i] = tq;
        }
+}
+
+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 (zio_taskq_sysdc && spa->spa_proc != &p0) {
-               if (batch)
-                       flags |= TASKQ_DC_BATCH;
+       if (tqs->stqs_taskq == NULL) {
+               ASSERT3U(tqs->stqs_count, ==, 0);
+               return;
+       }
 
-               return (taskq_create_sysdc(name, value, 50, INT_MAX,
-                   spa->spa_proc, zio_taskq_basedc, flags));
+       for (i = 0; i < tqs->stqs_count; i++) {
+               ASSERT3P(tqs->stqs_taskq[i], !=, NULL);
+               taskq_destroy(tqs->stqs_taskq[i]);
        }
-       return (taskq_create_proc(name, value, maxclsyspri, 50, INT_MAX,
-           spa->spa_proc, flags));
+
+       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 +958,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 +1117,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);
                }
        }
 
@@ -1752,7 +1856,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;
@@ -5058,7 +5162,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);
@@ -5742,6 +5846,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)
@@ -5762,7 +5874,7 @@ spa_sync_version(void *arg1, void *arg2, dmu_tx_t *tx)
         */
        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;
@@ -6009,6 +6121,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_SLEEP, 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.
@@ -6045,7 +6163,7 @@ spa_sync(spa_t *spa, uint64_t txg)
                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);
+               VERIFY0(zio_wait(zio));
        }
 
        /*
@@ -6062,7 +6180,7 @@ spa_sync(spa_t *spa, uint64_t txg)
                spa_errlog_sync(spa, txg);
                dsl_pool_sync(dp, txg);
 
-               if (pass <= SYNC_PASS_DEFERRED_FREE) {
+               if (pass < zfs_sync_pass_deferred_free) {
                        zio_t *zio = zio_root(spa, NULL, NULL, 0);
                        bplist_iterate(free_bpl, spa_free_sync_cb,
                            zio, tx);
@@ -6137,6 +6255,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 +6407,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;