--- 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);
 }
 
--- old/usr/src/uts/common/fs/zfs/dsl_pool.c	2014-10-16 19:15:50.797452527 +0200
+++ new/usr/src/uts/common/fs/zfs/dsl_pool.c	2014-10-16 19:15:50.803712880 +0200
@@ -1003,12 +1003,12 @@
  */
 
 int
-dsl_pool_hold(const char *name, void *tag, dsl_pool_t **dp)
+dsl_pool_hold_lock(const char *name, void *tag, dsl_pool_t **dp, int lock)
 {
 	spa_t *spa;
 	int error;
 
-	error = spa_open(name, &spa, tag);
+	error = spa_open_lock(name, &spa, tag, lock);
 	if (error == 0) {
 		*dp = spa_get_dsl(spa);
 		dsl_pool_config_enter(*dp, tag);
@@ -1016,6 +1016,12 @@
 	return (error);
 }
 
+int
+dsl_pool_hold(const char *name, void *tag, dsl_pool_t **dp)
+{
+	return (dsl_pool_hold_lock(name, tag, dp, 1));
+}
+
 void
 dsl_pool_rele(dsl_pool_t *dp, void *tag)
 {
--- old/usr/src/uts/common/fs/zfs/spa.c	2014-10-16 19:15:50.998712281 +0200
+++ new/usr/src/uts/common/fs/zfs/spa.c	2014-10-16 19:15:51.004146238 +0200
@@ -1717,8 +1717,8 @@
 	case SPA_LOG_MISSING:
 		/* need to recheck in case slog has been restored */
 	case SPA_LOG_UNKNOWN:
-		rv = (dmu_objset_find(spa->spa_name, zil_check_log_chain,
-		    NULL, DS_FIND_CHILDREN) != 0);
+		rv = (dmu_objset_find_parallel(spa->spa_name,
+		    zil_check_log_chain, NULL, DS_FIND_CHILDREN) != 0);
 		if (rv)
 			spa_set_log_state(spa, SPA_LOG_MISSING);
 		break;
@@ -2660,7 +2660,7 @@
 
 		tx = dmu_tx_create_assigned(spa_get_dsl(spa),
 		    spa_first_txg(spa));
-		(void) dmu_objset_find(spa_name(spa),
+		(void) dmu_objset_find_parallel(spa_name(spa),
 		    zil_claim, tx, DS_FIND_CHILDREN);
 		dmu_tx_commit(tx);
 
@@ -2852,7 +2852,7 @@
  */
 static int
 spa_open_common(const char *pool, spa_t **spapp, void *tag, nvlist_t *nvpolicy,
-    nvlist_t **config)
+    nvlist_t **config, int lock)
 {
 	spa_t *spa;
 	spa_load_state_t state = SPA_LOAD_OPEN;
@@ -2867,7 +2867,7 @@
 	 * up calling spa_open() again.  The real fix is to figure out how to
 	 * avoid dsl_dir_open() calling this in the first place.
 	 */
-	if (mutex_owner(&spa_namespace_lock) != curthread) {
+	if (lock && (mutex_owner(&spa_namespace_lock) != curthread)) {
 		mutex_enter(&spa_namespace_lock);
 		locked = B_TRUE;
 	}
@@ -2964,13 +2964,19 @@
 spa_open_rewind(const char *name, spa_t **spapp, void *tag, nvlist_t *policy,
     nvlist_t **config)
 {
-	return (spa_open_common(name, spapp, tag, policy, config));
+	return (spa_open_common(name, spapp, tag, policy, config, 1));
 }
 
 int
 spa_open(const char *name, spa_t **spapp, void *tag)
 {
-	return (spa_open_common(name, spapp, tag, NULL, NULL));
+	return (spa_open_common(name, spapp, tag, NULL, NULL, 1));
+}
+
+int
+spa_open_lock(const char *name, spa_t **spapp, void *tag, int lock)
+{
+	return (spa_open_common(name, spapp, tag, NULL, NULL, lock));
 }
 
 /*
@@ -3154,7 +3160,7 @@
 	spa_t *spa;
 
 	*config = NULL;
-	error = spa_open_common(name, &spa, FTAG, NULL, config);
+	error = spa_open_common(name, &spa, FTAG, NULL, config, 1);
 
 	if (spa != NULL) {
 		/*
--- old/usr/src/uts/common/fs/zfs/spa_misc.c	2014-10-16 19:15:51.352747041 +0200
+++ new/usr/src/uts/common/fs/zfs/spa_misc.c	2014-10-16 19:15:51.358146017 +0200
@@ -458,24 +458,25 @@
 spa_t *
 spa_lookup(const char *name)
 {
-	static spa_t search;	/* spa_t is large; don't allocate on stack */
+	spa_t *search;
 	spa_t *spa;
 	avl_index_t where;
 	char *cp;
 
-	ASSERT(MUTEX_HELD(&spa_namespace_lock));
+	search = kmem_alloc(sizeof(*search), KM_SLEEP);
 
-	(void) strlcpy(search.spa_name, name, sizeof (search.spa_name));
+	(void) strlcpy(search->spa_name, name, sizeof (search->spa_name));
 
 	/*
 	 * If it's a full dataset name, figure out the pool name and
 	 * just use that.
 	 */
-	cp = strpbrk(search.spa_name, "/@#");
+	cp = strpbrk(search->spa_name, "/@#");
 	if (cp != NULL)
 		*cp = '\0';
 
-	spa = avl_find(&spa_namespace_avl, &search, &where);
+	spa = avl_find(&spa_namespace_avl, search, &where);
+	kmem_free(search, sizeof(*search));
 
 	return (spa);
 }
--- old/usr/src/uts/common/fs/zfs/sys/dmu.h	2014-10-16 19:15:51.589381843 +0200
+++ new/usr/src/uts/common/fs/zfs/sys/dmu.h	2014-10-16 19:15:51.595469419 +0200
@@ -263,6 +263,7 @@
  * Public routines to create, destroy, open, and close objsets.
  */
 int dmu_objset_hold(const char *name, void *tag, objset_t **osp);
+int dmu_objset_hold_nolock(const char *name, void *tag, objset_t **osp);
 int dmu_objset_own(const char *name, dmu_objset_type_t type,
     boolean_t readonly, void *tag, objset_t **osp);
 void dmu_objset_rele(objset_t *os, void *tag);
@@ -279,6 +280,8 @@
 int dmu_objset_snapshot_tmp(const char *, const char *, int);
 int dmu_objset_find(char *name, int func(const char *, void *), void *arg,
     int flags);
+int dmu_objset_find_parallel(char *name, int func(const char *, void *),
+    void *arg, int flags);
 void dmu_objset_byteswap(void *buf, size_t size);
 int dsl_dataset_rename_snapshot(const char *fsname,
     const char *oldsnapname, const char *newsnapname, boolean_t recursive);
--- old/usr/src/uts/common/fs/zfs/sys/dmu_objset.h	2014-10-16 19:15:51.790152302 +0200
+++ new/usr/src/uts/common/fs/zfs/sys/dmu_objset.h	2014-10-16 19:15:51.795401328 +0200
@@ -135,8 +135,11 @@
 
 /* called from zpl */
 int dmu_objset_hold(const char *name, void *tag, objset_t **osp);
+int dmu_objset_hold_nolock(const char *name, void *tag, objset_t **osp);
 int dmu_objset_own(const char *name, dmu_objset_type_t type,
     boolean_t readonly, void *tag, objset_t **osp);
+int dmu_objset_own_nolock(const char *name, dmu_objset_type_t type,
+    boolean_t readonly, void *tag, objset_t **osp);
 void dmu_objset_refresh_ownership(objset_t *os, void *tag);
 void dmu_objset_rele(objset_t *os, void *tag);
 void dmu_objset_disown(objset_t *os, void *tag);
--- old/usr/src/uts/common/fs/zfs/sys/dsl_pool.h	2014-10-16 19:15:51.973527721 +0200
+++ new/usr/src/uts/common/fs/zfs/sys/dsl_pool.h	2014-10-16 19:15:51.978396325 +0200
@@ -165,7 +165,9 @@
 void dsl_pool_clean_tmp_userrefs(dsl_pool_t *dp);
 int dsl_pool_open_special_dir(dsl_pool_t *dp, const char *name, dsl_dir_t **);
 int dsl_pool_hold(const char *name, void *tag, dsl_pool_t **dp);
+int dsl_pool_hold_lock(const char *name, void *tag, dsl_pool_t **dp, int lock);
 void dsl_pool_rele(dsl_pool_t *dp, void *tag);
+void dsl_pool_rele_spa(dsl_pool_t *dp, void *tag);
 
 #ifdef	__cplusplus
 }
--- old/usr/src/uts/common/fs/zfs/sys/spa.h	2014-10-16 19:15:52.156911647 +0200
+++ new/usr/src/uts/common/fs/zfs/sys/spa.h	2014-10-16 19:15:52.162268690 +0200
@@ -570,6 +570,7 @@
 
 /* state manipulation functions */
 extern int spa_open(const char *pool, spa_t **, void *tag);
+extern int spa_open_lock(const char *pool, spa_t **, void *tag, int lock);
 extern int spa_open_rewind(const char *pool, spa_t **, void *tag,
     nvlist_t *policy, nvlist_t **config);
 extern int spa_get_stats(const char *pool, nvlist_t **config, char *altroot,
--- old/usr/src/uts/common/fs/zfs/sys/vdev.h	2014-10-16 19:15:52.363783524 +0200
+++ new/usr/src/uts/common/fs/zfs/sys/vdev.h	2014-10-16 19:15:52.369120495 +0200
@@ -60,6 +60,7 @@
 extern boolean_t vdev_is_bootable(vdev_t *vd);
 extern vdev_t *vdev_lookup_top(spa_t *spa, uint64_t vdev);
 extern vdev_t *vdev_lookup_by_guid(vdev_t *vd, uint64_t guid);
+extern int vdev_count_leaves(spa_t *spa);
 extern void vdev_dtl_dirty(vdev_t *vd, vdev_dtl_type_t d,
     uint64_t txg, uint64_t size);
 extern boolean_t vdev_dtl_contains(vdev_t *vd, vdev_dtl_type_t d,
--- old/usr/src/uts/common/fs/zfs/vdev.c	2014-10-16 19:15:52.543372984 +0200
+++ new/usr/src/uts/common/fs/zfs/vdev.c	2014-10-16 19:15:52.548382500 +0200
@@ -172,6 +172,27 @@
 	return (NULL);
 }
 
+int
+vdev_count_leaves_impl(vdev_t *vd)
+{
+	vdev_t *mvd;
+	int n = 0;
+
+	if (vd->vdev_children == 0)
+		return (1);
+
+	for (int c = 0; c < vd->vdev_children; c++)
+		n += vdev_count_leaves_impl(vd->vdev_child[c]);
+
+	return (n);
+}
+
+int
+vdev_count_leaves(spa_t *spa)
+{
+	return (vdev_count_leaves_impl(spa->spa_root_vdev));
+}
+
 void
 vdev_add_child(vdev_t *pvd, vdev_t *cvd)
 {
--- old/usr/src/uts/common/fs/zfs/zil.c	2014-10-16 19:15:52.821335739 +0200
+++ new/usr/src/uts/common/fs/zfs/zil.c	2014-10-16 19:15:52.827859719 +0200
@@ -633,7 +633,7 @@
 	objset_t *os;
 	int error;
 
-	error = dmu_objset_own(osname, DMU_OST_ANY, B_FALSE, FTAG, &os);
+	error = dmu_objset_own_nolock(osname, DMU_OST_ANY, B_FALSE, FTAG, &os);
 	if (error != 0) {
 		cmn_err(CE_WARN, "can't open objset for %s", osname);
 		return (0);
@@ -691,7 +691,7 @@
 
 	ASSERT(tx == NULL);
 
-	error = dmu_objset_hold(osname, FTAG, &os);
+	error = dmu_objset_hold_nolock(osname, FTAG, &os);
 	if (error != 0) {
 		cmn_err(CE_WARN, "can't open objset for %s", osname);
 		return (0);
