6596190 "zfs list" is slow due to version property
authorrm160521
Fri, 28 Sep 2007 15:53:20 -0700
changeset 5147 5e950ccc9585
parent 5146 27fbcf92f645
child 5148 88124f6d8a4f
6596190 "zfs list" is slow due to version property
usr/src/lib/libzfs/common/libzfs_dataset.c
usr/src/uts/common/fs/zfs/dmu_objset.c
usr/src/uts/common/fs/zfs/sys/zfs_znode.h
usr/src/uts/common/fs/zfs/zfs_ioctl.c
usr/src/uts/common/fs/zfs/zfs_vfsops.c
usr/src/uts/common/sys/fs/zfs.h
--- a/usr/src/lib/libzfs/common/libzfs_dataset.c	Fri Sep 28 11:01:47 2007 -0700
+++ b/usr/src/lib/libzfs/common/libzfs_dataset.c	Fri Sep 28 15:53:20 2007 -0700
@@ -1788,6 +1788,7 @@
 get_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zprop_source_t *src,
     char **source, uint64_t *val)
 {
+	zfs_cmd_t zc = { 0 };
 	struct mnttab mnt;
 	char *mntopt_on = NULL;
 	char *mntopt_off = NULL;
@@ -1900,6 +1901,18 @@
 		*val = zhp->zfs_dmustats.dds_num_clones;
 		break;
 
+	case ZFS_PROP_VERSION:
+		(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
+		if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_OBJSET_VERSION, &zc) ||
+		    (zc.zc_cookie == 0)) {
+			zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
+			    "unable to get version property"));
+			return (zfs_error(zhp->zfs_hdl, EZFS_BADVERSION,
+			    dgettext(TEXT_DOMAIN, "internal error")));
+		}
+		*val = zc.zc_cookie;
+		break;
+
 	default:
 		switch (zfs_prop_get_type(prop)) {
 		case PROP_TYPE_NUMBER:
--- a/usr/src/uts/common/fs/zfs/dmu_objset.c	Fri Sep 28 11:01:47 2007 -0700
+++ b/usr/src/uts/common/fs/zfs/dmu_objset.c	Fri Sep 28 15:53:20 2007 -0700
@@ -177,7 +177,8 @@
 			return (err);
 		}
 		osi->os_phys = osi->os_phys_buf->b_data;
-		arc_release(osi->os_phys_buf, &osi->os_phys_buf);
+		if (ds == NULL || dsl_dataset_is_snapshot(ds) == 0)
+			arc_release(osi->os_phys_buf, &osi->os_phys_buf);
 	} else {
 		osi->os_phys_buf = arc_buf_alloc(spa, sizeof (objset_phys_t),
 		    &osi->os_phys_buf, ARC_BUFC_METADATA);
--- a/usr/src/uts/common/fs/zfs/sys/zfs_znode.h	Fri Sep 28 11:01:47 2007 -0700
+++ b/usr/src/uts/common/fs/zfs/sys/zfs_znode.h	Fri Sep 28 15:53:20 2007 -0700
@@ -257,7 +257,7 @@
 extern int	zfs_create_op_tables();
 extern int	zfs_sync(vfs_t *vfsp, short flag, cred_t *cr);
 extern dev_t	zfs_cmpldev(uint64_t);
-extern int	zfs_get_stats(objset_t *os, nvlist_t *nv);
+extern int	zfs_get_version(objset_t *os, uint64_t *version);
 extern int	zfs_set_version(const char *name, uint64_t newvers);
 
 extern void zfs_log_create(zilog_t *zilog, dmu_tx_t *tx, int txtype,
--- a/usr/src/uts/common/fs/zfs/zfs_ioctl.c	Fri Sep 28 11:01:47 2007 -0700
+++ b/usr/src/uts/common/fs/zfs/zfs_ioctl.c	Fri Sep 28 15:53:20 2007 -0700
@@ -1093,7 +1093,7 @@
 	    (error = dsl_prop_get_all(os, &nv)) == 0) {
 		dmu_objset_stats(os, nv);
 		/*
-		 * NB: {zpl,zvol}_get_stats() will read the objset contents,
+		 * NB: zvol_get_stats() will read the objset contents,
 		 * which we aren't supposed to do with a
 		 * DS_MODE_STANDARD open, because it could be
 		 * inconsistent.  So this is a bit of a workaround...
@@ -1101,8 +1101,6 @@
 		if (!zc->zc_objset_stats.dds_inconsistent) {
 			if (dmu_objset_type(os) == DMU_OST_ZVOL)
 				VERIFY(zvol_get_stats(os, nv) == 0);
-			else if (dmu_objset_type(os) == DMU_OST_ZFS)
-				(void) zfs_get_stats(os, nv);
 		}
 		error = put_nvlist(zc, nv);
 		nvlist_free(nv);
@@ -1115,6 +1113,47 @@
 }
 
 static int
+zfs_ioc_objset_version(zfs_cmd_t *zc)
+{
+	objset_t *os = NULL;
+	int error;
+
+retry:
+	error = dmu_objset_open(zc->zc_name, DMU_OST_ANY,
+	    DS_MODE_STANDARD | DS_MODE_READONLY, &os);
+	if (error != 0) {
+		/*
+		 * This is ugly: dmu_objset_open() can return EBUSY if
+		 * the objset is held exclusively. Fortunately this hold is
+		 * only for a short while, so we retry here.
+		 * This avoids user code having to handle EBUSY,
+		 * for example for a "zfs list".
+		 */
+		if (error == EBUSY) {
+			delay(1);
+			goto retry;
+		}
+		return (error);
+	}
+
+	dmu_objset_fast_stat(os, &zc->zc_objset_stats);
+
+	/*
+	 * NB: zfs_get_version() will read the objset contents,
+	 * which we aren't supposed to do with a
+	 * DS_MODE_STANDARD open, because it could be
+	 * inconsistent.  So this is a bit of a workaround...
+	 */
+	zc->zc_cookie = 0;
+	if (!zc->zc_objset_stats.dds_inconsistent)
+		if (dmu_objset_type(os) == DMU_OST_ZFS)
+			(void) zfs_get_version(os, &zc->zc_cookie);
+
+	dmu_objset_close(os);
+	return (0);
+}
+
+static int
 zfs_ioc_dataset_list_next(zfs_cmd_t *zc)
 {
 	objset_t *os;
@@ -2087,6 +2126,7 @@
 	{ zfs_ioc_vdev_detach, zfs_secpolicy_config, POOL_NAME, B_TRUE },
 	{ zfs_ioc_vdev_setpath,	zfs_secpolicy_config, POOL_NAME, B_FALSE },
 	{ zfs_ioc_objset_stats,	zfs_secpolicy_read, DATASET_NAME, B_FALSE },
+	{ zfs_ioc_objset_version, zfs_secpolicy_read, DATASET_NAME, B_FALSE },
 	{ zfs_ioc_dataset_list_next, zfs_secpolicy_read,
 	    DATASET_NAME, B_FALSE },
 	{ zfs_ioc_snapshot_list_next, zfs_secpolicy_read,
--- a/usr/src/uts/common/fs/zfs/zfs_vfsops.c	Fri Sep 28 11:01:47 2007 -0700
+++ b/usr/src/uts/common/fs/zfs/zfs_vfsops.c	Fri Sep 28 15:53:20 2007 -0700
@@ -1322,15 +1322,11 @@
 }
 
 int
-zfs_get_stats(objset_t *os, nvlist_t *nv)
+zfs_get_version(objset_t *os, uint64_t *version)
 {
 	int error;
-	uint64_t val;
 
-	error = zap_lookup(os, MASTER_NODE_OBJ, ZPL_VERSION_STR, 8, 1, &val);
-	if (error == 0)
-		dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_VERSION, val);
-
+	error = zap_lookup(os, MASTER_NODE_OBJ, ZPL_VERSION_STR, 8, 1, version);
 	return (error);
 }
 
--- a/usr/src/uts/common/sys/fs/zfs.h	Fri Sep 28 11:01:47 2007 -0700
+++ b/usr/src/uts/common/sys/fs/zfs.h	Fri Sep 28 15:53:20 2007 -0700
@@ -462,6 +462,7 @@
 	ZFS_IOC_VDEV_DETACH,
 	ZFS_IOC_VDEV_SETPATH,
 	ZFS_IOC_OBJSET_STATS,
+	ZFS_IOC_OBJSET_VERSION,
 	ZFS_IOC_DATASET_LIST_NEXT,
 	ZFS_IOC_SNAPSHOT_LIST_NEXT,
 	ZFS_IOC_SET_PROP,