6609196 'zfs destroy -r' can needlessly iterate over all filesystems in pool
authorrm160521
Fri, 15 Feb 2008 19:55:15 -0800
changeset 6027 68b03551f113
parent 6026 4898f2ba5e8b
child 6028 72bc433f666e
6609196 'zfs destroy -r' can needlessly iterate over all filesystems in pool 6612830 zpool import shouldn't slowly iterate over all filesystems
usr/src/lib/libzfs/common/libzfs_changelist.c
usr/src/lib/libzfs/common/libzfs_graph.c
usr/src/lib/libzfs/common/libzfs_impl.h
usr/src/lib/libzfs/common/libzfs_mount.c
--- a/usr/src/lib/libzfs/common/libzfs_changelist.c	Fri Feb 15 17:04:27 2008 -0800
+++ b/usr/src/lib/libzfs/common/libzfs_changelist.c	Fri Feb 15 19:55:15 2008 -0800
@@ -20,7 +20,7 @@
  */
 
 /*
- * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  *
  * Portions Copyright 2007 Ramprakash Jelari
@@ -284,7 +284,7 @@
 /*
  * Is this "dataset" a child of "parent"?
  */
-static boolean_t
+boolean_t
 isa_child_of(const char *dataset, const char *parent)
 {
 	int len;
--- a/usr/src/lib/libzfs/common/libzfs_graph.c	Fri Feb 15 17:04:27 2008 -0800
+++ b/usr/src/lib/libzfs/common/libzfs_graph.c	Fri Feb 15 19:55:15 2008 -0800
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -126,6 +126,8 @@
 	zfs_vertex_t		**zg_hash;
 	size_t			zg_size;
 	size_t			zg_nvertex;
+	const char		*zg_root;
+	int			zg_clone_count;
 } zfs_graph_t;
 
 /*
@@ -255,7 +257,7 @@
  * datasets in the pool.
  */
 static zfs_graph_t *
-zfs_graph_create(libzfs_handle_t *hdl, size_t size)
+zfs_graph_create(libzfs_handle_t *hdl, const char *dataset, size_t size)
 {
 	zfs_graph_t *zgp = zfs_alloc(hdl, sizeof (zfs_graph_t));
 
@@ -269,6 +271,9 @@
 		return (NULL);
 	}
 
+	zgp->zg_root = dataset;
+	zgp->zg_clone_count = 0;
+
 	return (zgp);
 }
 
@@ -367,17 +372,16 @@
 }
 
 /*
- * Iterate over all children of the given dataset, adding any vertices as
- * necessary.  Returns 0 if no cloned snapshots were seen, -1 if there was an
- * error, or 1 otherwise.  This is a simple recursive algorithm - the ZFS
- * namespace typically is very flat.  We manually invoke the necessary ioctl()
- * calls to avoid the overhead and additional semantics of zfs_open().
+ * Iterate over all children of the given dataset, adding any vertices
+ * as necessary.  Returns -1 if there was an error, or 0 otherwise.
+ * This is a simple recursive algorithm - the ZFS namespace typically
+ * is very flat.  We manually invoke the necessary ioctl() calls to
+ * avoid the overhead and additional semantics of zfs_open().
  */
 static int
 iterate_children(libzfs_handle_t *hdl, zfs_graph_t *zgp, const char *dataset)
 {
 	zfs_cmd_t zc = { 0 };
-	int ret = 0, err;
 	zfs_vertex_t *zvp;
 
 	/*
@@ -390,18 +394,8 @@
 		return (0);
 
 	/*
-	 * We check the clone parent here instead of within the loop, so that if
-	 * the root dataset has been promoted from a clone, we find its parent
-	 * appropriately.
+	 * Iterate over all children
 	 */
-	(void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
-	if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) == 0 &&
-	    zc.zc_objset_stats.dds_origin[0] != '\0') {
-		if (zfs_graph_add(hdl, zgp, zc.zc_objset_stats.dds_origin,
-		    zc.zc_name, zc.zc_objset_stats.dds_creation_txg) != 0)
-			return (-1);
-	}
-
 	for ((void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
 	    ioctl(hdl->libzfs_fd, ZFS_IOC_DATASET_LIST_NEXT, &zc) == 0;
 	    (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name))) {
@@ -417,9 +411,23 @@
 		 * dataset and clone statistics.  If this fails, the dataset has
 		 * since been removed, and we're pretty much screwed anyway.
 		 */
+		zc.zc_objset_stats.dds_origin[0] = '\0';
 		if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0)
 			continue;
 
+		if (zc.zc_objset_stats.dds_origin[0] != '\0') {
+			if (zfs_graph_add(hdl, zgp,
+			    zc.zc_objset_stats.dds_origin, zc.zc_name,
+			    zc.zc_objset_stats.dds_creation_txg) != 0)
+				return (-1);
+			/*
+			 * Count origins only if they are contained in the graph
+			 */
+			if (isa_child_of(zc.zc_objset_stats.dds_origin,
+			    zgp->zg_root))
+				zgp->zg_clone_count--;
+		}
+
 		/*
 		 * Add an edge between the parent and the child.
 		 */
@@ -428,19 +436,10 @@
 			return (-1);
 
 		/*
-		 * Iterate over all children
+		 * Recursively visit child
 		 */
-		err = iterate_children(hdl, zgp, zc.zc_name);
-		if (err == -1)
+		if (iterate_children(hdl, zgp, zc.zc_name))
 			return (-1);
-		else if (err == 1)
-			ret = 1;
-
-		/*
-		 * Indicate if we found a dataset with a non-zero clone count.
-		 */
-		if (zc.zc_objset_stats.dds_num_clones != 0)
-			ret = 1;
 	}
 
 	/*
@@ -467,43 +466,65 @@
 		    zc.zc_objset_stats.dds_creation_txg) != 0)
 			return (-1);
 
-		/*
-		 * Indicate if we found a dataset with a non-zero clone count.
-		 */
-		if (zc.zc_objset_stats.dds_num_clones != 0)
-			ret = 1;
+		zgp->zg_clone_count += zc.zc_objset_stats.dds_num_clones;
 	}
 
 	zvp->zv_visited = VISIT_SEEN;
 
-	return (ret);
+	return (0);
 }
 
 /*
- * Construct a complete graph of all necessary vertices.  First, we iterate over
- * only our object's children.  If we don't find any cloned snapshots, then we
- * simple return that.  Otherwise, we have to start at the pool root and iterate
- * over all datasets.
+ * Returns false if there are no snapshots with dependent clones in this
+ * subtree or if all of those clones are also in this subtree.  Returns
+ * true if there is an error or there are external dependents.
+ */
+static boolean_t
+external_dependents(libzfs_handle_t *hdl, zfs_graph_t *zgp, const char *dataset)
+{
+	zfs_cmd_t zc = { 0 };
+
+	/*
+	 * Check whether this dataset is a clone or has clones since
+	 * iterate_children() only checks the children.
+	 */
+	(void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
+	if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0)
+		return (B_TRUE);
+
+	if (zc.zc_objset_stats.dds_origin[0] != '\0') {
+		if (zfs_graph_add(hdl, zgp,
+		    zc.zc_objset_stats.dds_origin, zc.zc_name,
+		    zc.zc_objset_stats.dds_creation_txg) != 0)
+			return (B_TRUE);
+		if (isa_child_of(zc.zc_objset_stats.dds_origin, dataset))
+			zgp->zg_clone_count--;
+	}
+
+	if ((zc.zc_objset_stats.dds_num_clones) ||
+	    iterate_children(hdl, zgp, dataset))
+		return (B_TRUE);
+
+	return (zgp->zg_clone_count != 0);
+}
+
+/*
+ * Construct a complete graph of all necessary vertices.  First, iterate over
+ * only our object's children.  If no cloned snapshots are found, or all of
+ * the cloned snapshots are in this subtree then return a graph of the subtree.
+ * Otherwise, start at the root of the pool and iterate over all datasets.
  */
 static zfs_graph_t *
 construct_graph(libzfs_handle_t *hdl, const char *dataset)
 {
-	zfs_graph_t *zgp = zfs_graph_create(hdl, ZFS_GRAPH_SIZE);
-	zfs_cmd_t zc = { 0 };
+	zfs_graph_t *zgp = zfs_graph_create(hdl, dataset, ZFS_GRAPH_SIZE);
 	int ret = 0;
 
 	if (zgp == NULL)
 		return (zgp);
 
-	/*
-	 * We need to explicitly check whether this dataset has clones or not,
-	 * since iterate_children() only checks the children.
-	 */
-	(void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
-	(void) ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc);
-
-	if (zc.zc_objset_stats.dds_num_clones != 0 ||
-	    (ret = iterate_children(hdl, zgp, dataset)) != 0) {
+	if ((strchr(dataset, '/') == NULL) ||
+	    (external_dependents(hdl, zgp, dataset))) {
 		/*
 		 * Determine pool name and try again.
 		 */
--- a/usr/src/lib/libzfs/common/libzfs_impl.h	Fri Feb 15 17:04:27 2008 -0800
+++ b/usr/src/lib/libzfs/common/libzfs_impl.h	Fri Feb 15 19:55:15 2008 -0800
@@ -151,6 +151,7 @@
 
 void remove_mountpoint(zfs_handle_t *);
 int create_parents(libzfs_handle_t *, char *, int);
+boolean_t isa_child_of(const char *dataset, const char *parent);
 
 zfs_handle_t *make_dataset_handle(libzfs_handle_t *, const char *);
 
--- a/usr/src/lib/libzfs/common/libzfs_mount.c	Fri Feb 15 17:04:27 2008 -0800
+++ b/usr/src/lib/libzfs/common/libzfs_mount.c	Fri Feb 15 19:55:15 2008 -0800
@@ -1117,7 +1117,7 @@
 
 	cbp->cb_datasets[cbp->cb_used++] = zhp;
 
-	return (zfs_iter_children(zhp, mount_cb, cbp));
+	return (zfs_iter_filesystems(zhp, mount_cb, cbp));
 }
 
 static int
@@ -1166,7 +1166,7 @@
 	int *good;
 
 	/*
-	 * Gather all datasets within the pool.
+	 * Gather all non-snap datasets within the pool.
 	 */
 	if ((cb.cb_datasets = zfs_alloc(hdl, 4 * sizeof (void *))) == NULL)
 		return (-1);
@@ -1178,7 +1178,7 @@
 	cb.cb_datasets[0] = zfsp;
 	cb.cb_used = 1;
 
-	if (zfs_iter_children(zfsp, mount_cb, &cb) != 0)
+	if (zfs_iter_filesystems(zfsp, mount_cb, &cb) != 0)
 		goto out;
 
 	/*