--- old/usr/src/uts/common/fs/zfs/zfs_vfsops.c	Mon Aug  5 17:27:11 2019
+++ new/usr/src/uts/common/fs/zfs/zfs_vfsops.c	Mon Aug  5 17:27:11 2019
@@ -45,6 +45,7 @@
 #include <sys/zil.h>
 #include <sys/fs/zfs.h>
 #include <sys/dmu.h>
+#include <sys/dsl_dir.h>
 #include <sys/dsl_prop.h>
 #include <sys/dsl_dataset.h>
 #include <sys/dsl_deleg.h>
@@ -396,6 +397,16 @@
 	zfsvfs->z_acl_inherit = newval;
 }
 
+static void
+rate_changed_cb(void *arg, uint64_t newval)
+{
+	zfsvfs_t *zfsvfs = arg;
+
+	if (newval == UINT64_MAX)
+		newval = 0;
+	zfsvfs->z_rate.rate_cap = newval;
+}
+
 static int
 zfs_register_callbacks(vfs_t *vfsp)
 {
@@ -533,6 +544,9 @@
 	    zfsvfs);
 	error = error ? error : dsl_prop_register(ds,
 	    zfs_prop_to_name(ZFS_PROP_VSCAN), vscan_changed_cb, zfsvfs);
+	error = error ? error : dsl_prop_register(ds,
+	    zfs_prop_to_name(ZFS_PROP_RATE_LIMIT), rate_changed_cb, zfsvfs);
+
 	dsl_pool_config_exit(dmu_objset_pool(os), FTAG);
 	if (error)
 		goto unregister;
@@ -993,6 +1007,7 @@
 zfsvfs_create_impl(zfsvfs_t **zfvp, zfsvfs_t *zfsvfs, objset_t *os)
 {
 	int error;
+	int size = spa_get_obj_mtx_sz(dmu_objset_spa(os));
 
 	zfsvfs->z_vfs = NULL;
 	zfsvfs->z_parent = zfsvfs;
@@ -1004,12 +1019,17 @@
 	rrm_init(&zfsvfs->z_teardown_lock, B_FALSE);
 	rw_init(&zfsvfs->z_teardown_inactive_lock, NULL, RW_DEFAULT, NULL);
 	rw_init(&zfsvfs->z_fuid_lock, NULL, RW_DEFAULT, NULL);
-	for (int i = 0; i != ZFS_OBJ_MTX_SZ; i++)
+	zfsvfs->z_hold_mtx_sz = size;
+	zfsvfs->z_hold_mtx = kmem_zalloc(sizeof (kmutex_t) * size, KM_SLEEP);
+	for (int i = 0; i != size; i++)
 		mutex_init(&zfsvfs->z_hold_mtx[i], NULL, MUTEX_DEFAULT, NULL);
+	mutex_init(&zfsvfs->z_drain_lock, NULL, MUTEX_DEFAULT, NULL);
+	cv_init(&zfsvfs->z_drain_cv, NULL, CV_DEFAULT, NULL);
 
 	error = zfsvfs_init(zfsvfs, os);
 	if (error != 0) {
 		*zfvp = NULL;
+		kmem_free(zfsvfs->z_hold_mtx, sizeof (kmutex_t) * size);
 		kmem_free(zfsvfs, sizeof (zfsvfs_t));
 		return (error);
 	}
@@ -1042,10 +1062,11 @@
 		 * allow replays to succeed.
 		 */
 		readonly = zfsvfs->z_vfs->vfs_flag & VFS_RDONLY;
-		if (readonly != 0)
+		if (readonly)
 			zfsvfs->z_vfs->vfs_flag &= ~VFS_RDONLY;
-		else
+		else {
 			zfs_unlinked_drain(zfsvfs);
+		}
 
 		/*
 		 * Parse and replay the intent log.
@@ -1084,7 +1105,10 @@
 				zfsvfs->z_replay = B_FALSE;
 			}
 		}
-		zfsvfs->z_vfs->vfs_flag |= readonly; /* restore readonly bit */
+
+		/* restore readonly bit */
+		if (readonly)
+			zfsvfs->z_vfs->vfs_flag |= VFS_RDONLY;
 	}
 
 	/*
@@ -1112,8 +1136,12 @@
 	rw_enter(&zfsvfs_lock, RW_READER);
 	rw_exit(&zfsvfs_lock);
 
+	VERIFY0(zfsvfs->z_znodes_freeing_cnt);
+
 	zfs_fuid_destroy(zfsvfs);
 
+	cv_destroy(&zfsvfs->z_drain_cv);
+	mutex_destroy(&zfsvfs->z_drain_lock);
 	mutex_destroy(&zfsvfs->z_znodes_lock);
 	mutex_destroy(&zfsvfs->z_lock);
 	list_destroy(&zfsvfs->z_all_znodes);
@@ -1120,8 +1148,11 @@
 	rrm_destroy(&zfsvfs->z_teardown_lock);
 	rw_destroy(&zfsvfs->z_teardown_inactive_lock);
 	rw_destroy(&zfsvfs->z_fuid_lock);
-	for (i = 0; i != ZFS_OBJ_MTX_SZ; i++)
+	for (i = 0; i != zfsvfs->z_hold_mtx_sz; i++)
 		mutex_destroy(&zfsvfs->z_hold_mtx[i]);
+
+	kmem_free(zfsvfs->z_hold_mtx,
+	    sizeof (kmutex_t) * zfsvfs->z_hold_mtx_sz);
 	kmem_free(zfsvfs, sizeof (zfsvfs_t));
 }
 
@@ -1156,6 +1187,7 @@
 	uint64_t recordsize, fsid_guid;
 	int error = 0;
 	zfsvfs_t *zfsvfs;
+	char	worminfo[13] = {0};
 
 	ASSERT(vfsp);
 	ASSERT(osname);
@@ -1179,6 +1211,14 @@
 	    NULL))
 		goto out;
 
+	if (dsl_prop_get(osname, "nms:worm", 1, 12, &worminfo, NULL) == 0 &&
+	    worminfo[0] && strcmp(worminfo, "0") != 0 &&
+	    strcmp(worminfo, "off") != 0 && strcmp(worminfo, "-") != 0) {
+		zfsvfs->z_isworm = B_TRUE;
+	} else {
+		zfsvfs->z_isworm = B_FALSE;
+	}
+
 	vfsp->vfs_dev = mount_dev;
 	vfsp->vfs_fstype = zfsfstype;
 	vfsp->vfs_bsize = recordsize;
@@ -1745,6 +1785,7 @@
 {
 	znode_t	*zp;
 
+	zfs_unlinked_drain_stop_wait(zfsvfs);
 	rrm_enter(&zfsvfs->z_teardown_lock, RW_WRITER, FTAG);
 
 	if (!unmounting) {
@@ -1825,7 +1866,7 @@
 	if (dsl_dataset_is_dirty(dmu_objset_ds(zfsvfs->z_os)) &&
 	    !(zfsvfs->z_vfs->vfs_flag & VFS_RDONLY))
 		txg_wait_synced(dmu_objset_pool(zfsvfs->z_os), 0);
-	dmu_objset_evict_dbufs(zfsvfs->z_os);
+	(void) dmu_objset_evict_dbufs(zfsvfs->z_os);
 
 	return (0);
 }
@@ -1863,6 +1904,8 @@
 	}
 
 	if (!(fflag & MS_FORCE)) {
+		uint_t active_vnodes;
+
 		/*
 		 * Check the number of active vnodes in the file system.
 		 * Our count is maintained in the vfs structure, but the
@@ -1872,12 +1915,18 @@
 		 * The '.zfs' directory maintains a reference of its
 		 * own, and any active references underneath are
 		 * reflected in the vnode count.
+		 *
+		 * Active vnodes: vnodes that were held by an user
 		 */
+
+		active_vnodes =
+		    vfsp->vfs_count - zfsvfs->z_znodes_freeing_cnt;
+
 		if (zfsvfs->z_ctldir == NULL) {
-			if (vfsp->vfs_count > 1)
+			if (active_vnodes > 1)
 				return (SET_ERROR(EBUSY));
 		} else {
-			if (vfsp->vfs_count > 2 ||
+			if (active_vnodes > 2 ||
 			    zfsvfs->z_ctldir->v_count > 1)
 				return (SET_ERROR(EBUSY));
 		}
@@ -2014,8 +2063,20 @@
 {
 	int error;
 
-	if ((error = zfsvfs_teardown(zfsvfs, B_FALSE)) != 0)
+	mutex_enter(&zfsvfs->z_lock);
+	if (zfsvfs->z_busy) {
+		mutex_exit(&zfsvfs->z_lock);
+		return (SET_ERROR(EBUSY));
+	}
+	zfsvfs->z_busy = B_TRUE;
+	mutex_exit(&zfsvfs->z_lock);
+
+	if ((error = zfsvfs_teardown(zfsvfs, B_FALSE)) != 0) {
+		mutex_enter(&zfsvfs->z_lock);
+		zfsvfs->z_busy = B_FALSE;
+		mutex_exit(&zfsvfs->z_lock);
 		return (error);
+	}
 
 	return (0);
 }
@@ -2066,6 +2127,16 @@
 	}
 	mutex_exit(&zfsvfs->z_znodes_lock);
 
+	if (((zfsvfs->z_vfs->vfs_flag & VFS_RDONLY) == 0) &&
+	    !zfsvfs->z_unmounted) {
+		/*
+		 * zfs_suspend_fs() could have interrupted freeing
+		 * of dnodes. We need to restart this freeing so
+		 * that we don't "leak" the space.
+		 */
+		zfs_unlinked_drain(zfsvfs);
+	}
+
 bail:
 	/* release the VOPs */
 	rw_exit(&zfsvfs->z_teardown_inactive_lock);
@@ -2079,6 +2150,10 @@
 		if (vn_vfswlock(zfsvfs->z_vfs->vfs_vnodecovered) == 0)
 			(void) dounmount(zfsvfs->z_vfs, MS_FORCE, CRED());
 	}
+	mutex_enter(&zfsvfs->z_lock);
+	zfsvfs->z_busy = B_FALSE;
+	mutex_exit(&zfsvfs->z_lock);
+
 	return (err);
 }
 
@@ -2232,6 +2307,7 @@
 	dmu_tx_commit(tx);
 
 	zfsvfs->z_version = newvers;
+	os->os_version = newvers;
 
 	zfs_set_fuid_feature(zfsvfs);
 
@@ -2244,17 +2320,47 @@
 int
 zfs_get_zplprop(objset_t *os, zfs_prop_t prop, uint64_t *value)
 {
-	const char *pname;
-	int error = ENOENT;
+	uint64_t *cached_copy = NULL;
 
 	/*
-	 * Look up the file system's value for the property.  For the
-	 * version property, we look up a slightly different string.
+	 * Figure out where in the objset_t the cached copy would live, if it
+	 * is available for the requested property.
 	 */
-	if (prop == ZFS_PROP_VERSION)
+	if (os != NULL) {
+		switch (prop) {
+		case ZFS_PROP_VERSION:
+			cached_copy = &os->os_version;
+			break;
+		case ZFS_PROP_NORMALIZE:
+			cached_copy = &os->os_normalization;
+			break;
+		case ZFS_PROP_UTF8ONLY:
+			cached_copy = &os->os_utf8only;
+			break;
+		case ZFS_PROP_CASE:
+			cached_copy = &os->os_casesensitivity;
+			break;
+		default:
+			break;
+		}
+	}
+	if (cached_copy != NULL && *cached_copy != OBJSET_PROP_UNINITIALIZED) {
+		*value = *cached_copy;
+		return (0);
+	}
+
+	/*
+	 * If the property wasn't cached, look up the file system's value for
+	 * the property. For the version property, we look up a slightly
+	 * different string.
+	 */
+	const char *pname;
+	int error = ENOENT;
+	if (prop == ZFS_PROP_VERSION) {
 		pname = ZPL_VERSION_STR;
-	else
+	} else {
 		pname = zfs_prop_to_name(prop);
+	}
 
 	if (os != NULL) {
 		ASSERT3U(os->os_phys->os_type, ==, DMU_OST_ZFS);
@@ -2279,6 +2385,15 @@
 		}
 		error = 0;
 	}
+
+	/*
+	 * If one of the methods for getting the property value above worked,
+	 * copy it into the objset_t's cache.
+	 */
+	if (error == 0 && cached_copy != NULL) {
+		*cached_copy = *value;
+	}
+
 	return (error);
 }
 
