--- old/usr/src/uts/common/fs/zfs/dmu_objset.c	2014-10-16 19:15:50.553714282 +0200
+++ new/usr/src/uts/common/fs/zfs/dmu_objset.c	2014-10-16 19:15:50.558636350 +0200
@@ -47,6 +47,7 @@
 #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
@@ -466,14 +467,14 @@
  * 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);
@@ -491,20 +492,32 @@
 	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);
@@ -527,6 +540,20 @@
 	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)
 {
@@ -1773,6 +1800,144 @@
 		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);
 }
 
