9891 libbe slow building snapshot list
Reviewed by: Dominik Hassler <hadfl@omniosce.org>
Reviewed by: Garrett D'Amore <garrett@damore.org>
Reviewed by: Jim Klimov <jim@cos.ru>
Reviewed by: Dan McDonald <danmcd@joyent.com>
Approved by: Gordon Ross <gordon.w.ross@gmail.com>
diff --git a/usr/src/lib/libbe/common/be_list.c b/usr/src/lib/libbe/common/be_list.c
index c8e052a..7dce228 100644
--- a/usr/src/lib/libbe/common/be_list.c
+++ b/usr/src/lib/libbe/common/be_list.c
@@ -55,6 +55,8 @@
 	char *be_name;
 	be_node_list_t *be_nodes_head;
 	be_node_list_t *be_nodes;
+	be_dataset_list_t **be_datasets_tail;
+	be_snapshot_list_t **be_snapshots_tail;
 	char current_be[MAXPATHLEN];
 	struct be_defaults be_defaults;
 } list_callback_data_t;
@@ -82,6 +84,7 @@
 static int be_qsort_compare_snapshots(const void *x, const void *y);
 static int be_qsort_compare_datasets(const void *x, const void *y);
 static void *be_list_alloc(int *, size_t);
+static int be_allocate_callback_nodes(list_callback_data_t *);
 
 /*
  * Private data.
@@ -384,13 +387,9 @@
 
 	(void) strcpy(be_container_ds, zone_be_container_ds);
 
-	if (cb.be_nodes_head == NULL) {
-		if ((cb.be_nodes_head = be_list_alloc(&ret,
-		    sizeof (be_node_list_t))) == NULL) {
-			ZFS_CLOSE(zhp);
-			goto cleanup;
-		}
-		cb.be_nodes = cb.be_nodes_head;
+	if ((ret = be_allocate_callback_nodes(&cb)) != BE_SUCCESS) {
+		ZFS_CLOSE(zhp);
+		goto cleanup;
 	}
 	if (ret == 0) {
 		be_get_defaults(&cb.be_defaults);
@@ -491,14 +490,10 @@
 	 * within the pool
 	 */
 	if (cb->be_name != NULL) {
-		if (cb->be_nodes_head == NULL) {
-			if ((cb->be_nodes_head = be_list_alloc(&ret,
-			    sizeof (be_node_list_t))) == NULL) {
-				ZFS_CLOSE(zhp);
-				zpool_close(zlp);
-				return (ret);
-			}
-			cb->be_nodes = cb->be_nodes_head;
+		if ((ret = be_allocate_callback_nodes(cb)) != BE_SUCCESS) {
+			ZFS_CLOSE(zhp);
+			zpool_close(zlp);
+			return (ret);
 		}
 
 		if ((ret = be_get_node_data(zhp, cb->be_nodes, cb->be_name,
@@ -520,6 +515,38 @@
 }
 
 /*
+ * Function:	be_allocate_callback_nodes
+ * Description:	Function to create the be_nodes list in the callback data
+ *		structure, and set up tail pointers to the dataset and
+ *		snapshot lists.
+ * Parameters:
+ *		data - pointer to the callback data.
+ * Returns:
+ *		0 - Success
+ *		be_errno_t - Failure
+ * Scope:
+ *		Private
+ */
+static int
+be_allocate_callback_nodes(list_callback_data_t *cb)
+{
+	int ret = BE_SUCCESS;
+
+	if (cb->be_nodes_head != NULL)
+		return (BE_SUCCESS);
+
+	if ((cb->be_nodes_head = be_list_alloc(&ret, sizeof (be_node_list_t)))
+	    == NULL)
+		return (ret);
+
+	cb->be_nodes = cb->be_nodes_head;
+	cb->be_snapshots_tail = &cb->be_nodes->be_node_snapshots;
+	cb->be_datasets_tail = &cb->be_nodes->be_node_datasets;
+
+	return (BE_SUCCESS);
+}
+
+/*
  * Function:	be_add_children_callback
  * Description:	Callback function used by zfs_iter to look through all
  *		the datasets and snapshots for each BE and add them to
@@ -554,54 +581,32 @@
 			return (BE_SUCCESS);
 	}
 
-	if (cb->be_nodes_head == NULL) {
-		if ((cb->be_nodes_head = be_list_alloc(&ret,
-		    sizeof (be_node_list_t))) == NULL) {
-			ZFS_CLOSE(zhp);
-			return (ret);
-		}
-		cb->be_nodes = cb->be_nodes_head;
+	if (cb->be_nodes_head == NULL &&
+	    (ret = be_allocate_callback_nodes(cb)) != BE_SUCCESS) {
+		ZFS_CLOSE(zhp);
+		return (ret);
 	}
 
 	if (zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT && !zone_be) {
-		be_snapshot_list_t *snapshots = NULL;
-		if (cb->be_nodes->be_node_snapshots == NULL) {
-			if ((cb->be_nodes->be_node_snapshots =
-			    be_list_alloc(&ret, sizeof (be_snapshot_list_t)))
-			    == NULL || ret != BE_SUCCESS) {
-				ZFS_CLOSE(zhp);
-				return (ret);
-			}
-			cb->be_nodes->be_node_snapshots->be_next_snapshot =
-			    NULL;
-			snapshots = cb->be_nodes->be_node_snapshots;
-		} else {
-			for (snapshots = cb->be_nodes->be_node_snapshots;
-			    snapshots != NULL;
-			    snapshots = snapshots->be_next_snapshot) {
-				if (snapshots->be_next_snapshot != NULL)
-					continue;
-				/*
-				 * We're at the end of the list add the
-				 * new snapshot.
-				 */
-				if ((snapshots->be_next_snapshot =
-				    be_list_alloc(&ret,
-				    sizeof (be_snapshot_list_t))) == NULL ||
-				    ret != BE_SUCCESS) {
-					ZFS_CLOSE(zhp);
-					return (ret);
-				}
-				snapshots = snapshots->be_next_snapshot;
-				snapshots->be_next_snapshot = NULL;
-				break;
-			}
-		}
-		if ((ret = be_get_ss_data(zhp, str, snapshots,
-		    cb->be_nodes)) != BE_SUCCESS) {
+		be_snapshot_list_t *snapshot;
+
+		if ((snapshot = be_list_alloc(&ret,
+		    sizeof (be_snapshot_list_t))) == NULL ||
+		    ret != BE_SUCCESS) {
 			ZFS_CLOSE(zhp);
 			return (ret);
 		}
+
+		if ((ret = be_get_ss_data(zhp, str, snapshot,
+		    cb->be_nodes)) != BE_SUCCESS) {
+			free(snapshot);
+			ZFS_CLOSE(zhp);
+			return (ret);
+		}
+
+		snapshot->be_next_snapshot = NULL;
+		*cb->be_snapshots_tail = snapshot;
+		cb->be_snapshots_tail = &snapshot->be_next_snapshot;
 	} else if (strchr(str, '/') == NULL) {
 		if (cb->be_nodes->be_node_name != NULL) {
 			if ((cb->be_nodes->be_next_node =
@@ -631,44 +636,25 @@
 			return (ret);
 		}
 	} else if (strchr(str, '/') != NULL && !zone_be) {
-		be_dataset_list_t *datasets = NULL;
-		if (cb->be_nodes->be_node_datasets == NULL) {
-			if ((cb->be_nodes->be_node_datasets =
-			    be_list_alloc(&ret, sizeof (be_dataset_list_t)))
-			    == NULL || ret != BE_SUCCESS) {
-				ZFS_CLOSE(zhp);
-				return (ret);
-			}
-			cb->be_nodes->be_node_datasets->be_next_dataset = NULL;
-			datasets = cb->be_nodes->be_node_datasets;
-		} else {
-			for (datasets = cb->be_nodes->be_node_datasets;
-			    datasets != NULL;
-			    datasets = datasets->be_next_dataset) {
-				if (datasets->be_next_dataset != NULL)
-					continue;
-				/*
-				 * We're at the end of the list add
-				 * the new dataset.
-				 */
-				if ((datasets->be_next_dataset =
-				    be_list_alloc(&ret,
-				    sizeof (be_dataset_list_t)))
-				    == NULL || ret != BE_SUCCESS) {
-					ZFS_CLOSE(zhp);
-					return (ret);
-				}
-				datasets = datasets->be_next_dataset;
-				datasets->be_next_dataset = NULL;
-				break;
-			}
-		}
+		be_dataset_list_t *dataset;
 
-		if ((ret = be_get_ds_data(zhp, str,
-		    datasets, cb->be_nodes)) != BE_SUCCESS) {
+		if ((dataset = be_list_alloc(&ret,
+		    sizeof (be_dataset_list_t))) == NULL ||
+		    ret != BE_SUCCESS) {
 			ZFS_CLOSE(zhp);
 			return (ret);
 		}
+
+		if ((ret = be_get_ds_data(zhp, str,
+		    dataset, cb->be_nodes)) != BE_SUCCESS) {
+			free(dataset);
+			ZFS_CLOSE(zhp);
+			return (ret);
+		}
+
+		dataset->be_next_dataset = NULL;
+		*cb->be_datasets_tail = dataset;
+		cb->be_datasets_tail = &dataset->be_next_dataset;
 	}
 	ret = zfs_iter_children(zhp, be_add_children_callback, cb);
 	if (ret != 0) {