Print this page
zpool import speedup

@@ -45,10 +45,11 @@
 #include <sys/dmu_impl.h>
 #include <sys/zfs_ioctl.h>
 #include <sys/sa.h>
 #include <sys/zfs_onexit.h>
 #include <sys/dsl_destroy.h>
+#include <sys/vdev.h>
 
 /*
  * Needed to close a window in dnode_move() that allows the objset to be freed
  * before it can be safely accessed.
  */

@@ -464,18 +465,18 @@
 
 /*
  * Holds the pool while the objset is held.  Therefore only one objset
  * can be held at a time.
  */
-int
-dmu_objset_hold(const char *name, void *tag, objset_t **osp)
+static int
+dmu_objset_hold_impl(const char *name, void *tag, objset_t **osp, int lock)
 {
         dsl_pool_t *dp;
         dsl_dataset_t *ds;
         int err;
 
-        err = dsl_pool_hold(name, tag, &dp);
+        err = dsl_pool_hold_lock(name, tag, &dp, lock);
         if (err != 0)
                 return (err);
         err = dsl_dataset_hold(dp, name, tag, &ds);
         if (err != 0) {
                 dsl_pool_rele(dp, tag);

@@ -489,24 +490,36 @@
         }
 
         return (err);
 }
 
+int
+dmu_objset_hold(const char *name, void *tag, objset_t **osp)
+{
+        return (dmu_objset_hold_impl(name, tag, osp, 1));
+}
+
+int
+dmu_objset_hold_nolock(const char *name, void *tag, objset_t **osp)
+{
+        return (dmu_objset_hold_impl(name, tag, osp, 0));
+}
+
 /*
  * dsl_pool must not be held when this is called.
  * Upon successful return, there will be a longhold on the dataset,
  * and the dsl_pool will not be held.
  */
-int
-dmu_objset_own(const char *name, dmu_objset_type_t type,
-    boolean_t readonly, void *tag, objset_t **osp)
+static int
+dmu_objset_own_impl(const char *name, dmu_objset_type_t type,
+    boolean_t readonly, void *tag, objset_t **osp, int lock)
 {
         dsl_pool_t *dp;
         dsl_dataset_t *ds;
         int err;
 
-        err = dsl_pool_hold(name, FTAG, &dp);
+        err = dsl_pool_hold_lock(name, FTAG, &dp, lock);
         if (err != 0)
                 return (err);
         err = dsl_dataset_own(dp, name, tag, &ds);
         if (err != 0) {
                 dsl_pool_rele(dp, FTAG);

@@ -525,10 +538,24 @@
                 return (SET_ERROR(EROFS));
         }
         return (err);
 }
 
+int
+dmu_objset_own(const char *name, dmu_objset_type_t type,
+    boolean_t readonly, void *tag, objset_t **osp)
+{
+        return (dmu_objset_own_impl(name, type, readonly, tag, osp, 1));
+}
+
+int
+dmu_objset_own_nolock(const char *name, dmu_objset_type_t type,
+    boolean_t readonly, void *tag, objset_t **osp)
+{
+        return (dmu_objset_own_impl(name, type, readonly, tag, osp, 0));
+}
+
 void
 dmu_objset_rele(objset_t *os, void *tag)
 {
         dsl_pool_t *dp = dmu_objset_pool(os);
         dsl_dataset_rele(os->os_dsl_dataset, tag);

@@ -1771,10 +1798,148 @@
         error = spa_open(name, &spa, FTAG);
         if (error != 0)
                 return (error);
         error = dmu_objset_find_impl(spa, name, func, arg, flags);
         spa_close(spa, FTAG);
+
+        return (error);
+}
+
+typedef struct dmu_objset_find_ctx {
+        taskq_t         *dc_tq;
+        spa_t           *dc_spa;
+        char            *dc_name;
+        int             (*dc_func)(const char *, void *);
+        void            *dc_arg;
+        int             dc_flags;
+        kmutex_t        *dc_error_lock;
+        int             *dc_error;
+} dmu_objset_find_ctx_t;
+
+static void
+dmu_objset_find_parallel_impl(void *arg)
+{
+        dmu_objset_find_ctx_t *dcp = arg;
+        dsl_dir_t *dd;
+        dsl_pool_t *dp = spa_get_dsl(dcp->dc_spa);
+        dsl_dataset_t *ds;
+        zap_cursor_t zc;
+        zap_attribute_t *attr;
+        char *child;
+        dmu_objset_find_ctx_t *child_dcp;
+        uint64_t thisobj;
+        int err;
+
+        /* don't process if there already was an error */
+        if (*dcp->dc_error)
+                goto out;
+
+        dsl_pool_config_enter(dp, FTAG);
+
+        err = dsl_dir_hold(dp, dcp->dc_name, FTAG, &dd, NULL);
+        if (err != 0) {
+                dsl_pool_config_exit(dp, FTAG);
+                goto fail;
+        }
+
+        /* Don't visit hidden ($MOS & $ORIGIN) objsets. */
+        if (dd->dd_myname[0] == '$') {
+                dsl_dir_rele(dd, FTAG);
+                dsl_pool_config_exit(dp, FTAG);
+                goto out;
+        }
+
+        thisobj = dd->dd_phys->dd_head_dataset_obj;
+        attr = kmem_alloc(sizeof (zap_attribute_t), KM_SLEEP);
+
+        /*
+         * Iterate over all children.
+         */
+        if (dcp->dc_flags & DS_FIND_CHILDREN) {
+                for (zap_cursor_init(&zc, dp->dp_meta_objset,
+                    dd->dd_phys->dd_child_dir_zapobj);
+                    zap_cursor_retrieve(&zc, attr) == 0;
+                    (void) zap_cursor_advance(&zc)) {
+                        ASSERT3U(attr->za_integer_length, ==,
+                            sizeof (uint64_t));
+                        ASSERT3U(attr->za_num_integers, ==, 1);
+
+                        child = kmem_asprintf("%s/%s", dcp->dc_name,
+                            attr->za_name);
+                        dsl_pool_config_exit(dp, FTAG);
+                        child_dcp = kmem_alloc(sizeof(*child_dcp), KM_SLEEP);
+                        *child_dcp = *dcp;
+                        child_dcp->dc_name = child;
+                        taskq_dispatch(dcp->dc_tq,
+                            dmu_objset_find_parallel_impl, child_dcp, TQ_SLEEP);
+                        dsl_pool_config_enter(dp, FTAG);
+                }
+                zap_cursor_fini(&zc);
+        }
+
+        dsl_dir_rele(dd, FTAG);
+        kmem_free(attr, sizeof (zap_attribute_t));
+        dsl_pool_config_exit(dp, FTAG);
+
+        err = dcp->dc_func(dcp->dc_name, dcp->dc_arg);
+
+fail:
+        if (err) {
+                mutex_enter(dcp->dc_error_lock);
+                /* only keep first error */
+                if (*dcp->dc_error == 0)
+                        *dcp->dc_error = err;
+                mutex_exit(dcp->dc_error_lock);
+        }
+
+out:
+        strfree(dcp->dc_name);
+        kmem_free(dcp, sizeof(*dcp));
+}
+
+int
+dmu_objset_find_parallel(char *name, int func(const char *, void *), void *arg,
+    int flags)
+{
+        spa_t *spa;
+        int error;
+        taskq_t *tq = NULL;
+        int ntasks;
+        dmu_objset_find_ctx_t *dcp;
+        kmutex_t err_lock;
+
+        error = spa_open(name, &spa, FTAG);
+        if (error != 0)
+                return (error);
+
+        ntasks = vdev_count_leaves(spa) * 4;
+        tq = taskq_create("dmu_objset_find", ntasks, minclsyspri, ntasks,
+            INT_MAX, 0);
+        if (!tq) {
+                spa_close(spa, FTAG);
+                return (dmu_objset_find(name, func, arg, flags));
+        }
+
+        mutex_init(&err_lock, NULL, MUTEX_DEFAULT, NULL);
+        dcp = kmem_alloc(sizeof(*dcp), KM_SLEEP);
+        dcp->dc_tq = tq;
+        dcp->dc_spa = spa;
+        dcp->dc_name = strdup(name);
+        dcp->dc_func = func;
+        dcp->dc_arg = arg;
+        dcp->dc_flags = flags;
+        dcp->dc_error_lock = &err_lock;
+        dcp->dc_error = &error;
+        /* dcp and dc_name will be freed by task */
+        taskq_dispatch(tq, dmu_objset_find_parallel_impl, dcp, TQ_SLEEP);
+
+        taskq_wait(tq);
+        taskq_destroy(tq);
+        mutex_destroy(&err_lock);
+
+        spa_close(spa, FTAG);
+
         return (error);
 }
 
 void
 dmu_objset_set_user(objset_t *os, void *user_ptr)