[PATCH 2/2] erofs-utils: lib: refine on-disk meta arrangement again

Gao Xiang hsiangkao at linux.alibaba.com
Sat Apr 6 16:37:17 AEDT 2024


Most workloads like `ls -R` and `tar -c` traverse in depth-first mode.
However, it still arranges sub-directory inodes more closely so that
it isn't a simple reversion compared to pre-BFS old versions.

Also the build performance out of linux-6.1.53 source code is greatly
improved by 91.2% (33.040s->2.861s) as well as the new image size is
decreased by 0.0094% (120 KiB), which is minor.

Signed-off-by: Gao Xiang <hsiangkao at linux.alibaba.com>
---
 lib/inode.c | 192 +++++++++++++++++++++++++---------------------------
 1 file changed, 91 insertions(+), 101 deletions(-)

diff --git a/lib/inode.c b/lib/inode.c
index f419f3c..79626ab 100644
--- a/lib/inode.c
+++ b/lib/inode.c
@@ -162,7 +162,8 @@ struct erofs_dentry *erofs_d_alloc(struct erofs_inode *parent,
 
 	strncpy(d->name, name, EROFS_NAME_LEN - 1);
 	d->name[EROFS_NAME_LEN - 1] = '\0';
-
+	d->inode = NULL;
+	d->type = EROFS_FT_UNKNOWN;
 	list_add_tail(&d->d_child, &parent->i_subdirs);
 	return d;
 }
@@ -1131,7 +1132,7 @@ static int erofs_mkfs_handle_nondirectory(struct erofs_inode *inode)
 	return 0;
 }
 
-static int erofs_mkfs_handle_directory(struct erofs_inode *dir, struct list_head *dirs)
+static int erofs_mkfs_handle_directory(struct erofs_inode *dir)
 {
 	DIR *_dir;
 	struct dirent *dp;
@@ -1147,18 +1148,28 @@ static int erofs_mkfs_handle_directory(struct erofs_inode *dir, struct list_head
 	}
 
 	nr_subdirs = 0;
+	i_nlink = 0;
 	while (1) {
+		char buf[PATH_MAX];
+		struct erofs_inode *inode;
+
 		/*
 		 * set errno to 0 before calling readdir() in order to
 		 * distinguish end of stream and from an error.
 		 */
 		errno = 0;
 		dp = readdir(_dir);
-		if (!dp)
-			break;
+		if (!dp) {
+			if (!errno)
+				break;
+			ret = -errno;
+			goto err_closedir;
+		}
 
-		if (is_dot_dotdot(dp->d_name))
+		if (is_dot_dotdot(dp->d_name)) {
+			++i_nlink;
 			continue;
+		}
 
 		/* skip if it's a exclude file */
 		if (erofs_is_exclude_path(dir->i_srcpath, dp->d_name))
@@ -1169,70 +1180,28 @@ static int erofs_mkfs_handle_directory(struct erofs_inode *dir, struct list_head
 			ret = PTR_ERR(d);
 			goto err_closedir;
 		}
-		nr_subdirs++;
-	}
-
-	if (errno) {
-		ret = -errno;
-		goto err_closedir;
-	}
-	closedir(_dir);
-
-	ret = erofs_prepare_dir_file(dir, nr_subdirs);
-	if (ret)
-		return ret;
-
-	ret = erofs_prepare_inode_buffer(dir);
-	if (ret)
-		return ret;
-	dir->bh->op = &erofs_skip_write_bhops;
-
-	if (IS_ROOT(dir))
-		erofs_fixup_meta_blkaddr(dir);
-
-	i_nlink = 0;
-	list_for_each_entry(d, &dir->i_subdirs, d_child) {
-		char buf[PATH_MAX];
-		unsigned char ftype;
-		struct erofs_inode *inode;
-
-		if (is_dot_dotdot(d->name)) {
-			++i_nlink;
-			continue;
-		}
 
-		ret = snprintf(buf, PATH_MAX, "%s/%s",
-			       dir->i_srcpath, d->name);
-		if (ret < 0 || ret >= PATH_MAX) {
-			/* ignore the too long path */
-			goto fail;
-		}
+		ret = snprintf(buf, PATH_MAX, "%s/%s", dir->i_srcpath, d->name);
+		if (ret < 0 || ret >= PATH_MAX)
+			goto err_closedir;
 
 		inode = erofs_iget_from_path(buf, true);
-
 		if (IS_ERR(inode)) {
 			ret = PTR_ERR(inode);
-fail:
-			d->inode = NULL;
-			d->type = EROFS_FT_UNKNOWN;
-			return ret;
-		}
-
-		/* a hardlink to the existed inode */
-		if (inode->i_parent) {
-			++inode->i_nlink;
-		} else {
-			inode->i_parent = dir;
-			erofs_igrab(inode);
-			list_add_tail(&inode->i_subdirs, dirs);
+			goto err_closedir;
 		}
-		ftype = erofs_mode_to_ftype(inode->i_mode);
-		i_nlink += (ftype == EROFS_FT_DIR);
 		d->inode = inode;
-		d->type = ftype;
-		erofs_info("file %s/%s dumped (type %u)",
-			   dir->i_srcpath, d->name, d->type);
+		d->type = erofs_mode_to_ftype(inode->i_mode);
+		i_nlink += S_ISDIR(inode->i_mode);
+		erofs_dbg("file %s added (type %u)", buf, d->type);
+		nr_subdirs++;
 	}
+	closedir(_dir);
+
+	ret = erofs_prepare_dir_file(dir, nr_subdirs);
+	if (ret)
+		return ret;
+
 	/*
 	 * if there're too many subdirs as compact form, set nlink=1
 	 * rather than upgrade to use extented form instead.
@@ -1242,6 +1211,11 @@ fail:
 		dir->i_nlink = 1;
 	else
 		dir->i_nlink = i_nlink;
+
+	ret = erofs_prepare_inode_buffer(dir);
+	if (ret)
+		return ret;
+	dir->bh->op = &erofs_skip_write_bhops;
 	return 0;
 
 err_closedir:
@@ -1249,10 +1223,16 @@ err_closedir:
 	return ret;
 }
 
-static int erofs_mkfs_build_tree(struct erofs_inode *inode, struct list_head *dirs)
+static int erofs_mkfs_build_tree(struct erofs_inode *inode)
 {
+	char *trimmed;
 	int ret;
 
+	trimmed = erofs_trim_for_progressinfo(erofs_fspath(inode->i_srcpath),
+					      sizeof("Processing  ...") - 1);
+	erofs_update_progressinfo("Processing %s ...", trimmed);
+	free(trimmed);
+
 	ret = erofs_scan_file_xattrs(inode);
 	if (ret < 0)
 		return ret;
@@ -1262,60 +1242,70 @@ static int erofs_mkfs_build_tree(struct erofs_inode *inode, struct list_head *di
 		return ret;
 
 	if (!S_ISDIR(inode->i_mode))
-		return erofs_mkfs_handle_nondirectory(inode);
-	return erofs_mkfs_handle_directory(inode, dirs);
+		ret = erofs_mkfs_handle_nondirectory(inode);
+	else
+		ret = erofs_mkfs_handle_directory(inode);
+	erofs_info("file %s dumped", erofs_fspath(inode->i_srcpath));
+	return ret;
 }
 
 struct erofs_inode *erofs_mkfs_build_tree_from_path(const char *path)
 {
-	LIST_HEAD(dirs);
-	struct erofs_inode *inode, *root, *dumpdir;
+	struct erofs_inode *root, *dumpdir;
+	int err;
 
 	root = erofs_iget_from_path(path, true);
 	if (IS_ERR(root))
 		return root;
 
-	(void)erofs_igrab(root);
 	root->i_parent = root;	/* rootdir mark */
-	list_add(&root->i_subdirs, &dirs);
+	root->next_dirwrite = NULL;
+	(void)erofs_igrab(root);
+	dumpdir = root;
+
+	err = erofs_mkfs_build_tree(root);
+	if (err)
+		return ERR_PTR(err);
+	erofs_fixup_meta_blkaddr(root);
 
-	dumpdir = NULL;
 	do {
 		int err;
-		char *trimmed;
-
-		inode = list_first_entry(&dirs, struct erofs_inode, i_subdirs);
-		list_del(&inode->i_subdirs);
-		init_list_head(&inode->i_subdirs);
-
-		trimmed = erofs_trim_for_progressinfo(
-				erofs_fspath(inode->i_srcpath),
-				sizeof("Processing  ...") - 1);
-		erofs_update_progressinfo("Processing %s ...", trimmed);
-		free(trimmed);
-
-		err = erofs_mkfs_build_tree(inode, &dirs);
-		if (err) {
-			root = ERR_PTR(err);
-			break;
+		struct erofs_inode *dir = dumpdir;
+		/* used for adding sub-directories in reverse order due to FIFO */
+		struct erofs_inode *head, **last = &head;
+		struct erofs_dentry *d;
+
+		dumpdir = dir->next_dirwrite;
+		list_for_each_entry(d, &dir->i_subdirs, d_child) {
+			struct erofs_inode *inode = d->inode;
+
+			if (is_dot_dotdot(d->name))
+				continue;
+			if (inode->i_parent) {
+				++inode->i_nlink;
+			} else {
+				inode->i_parent = dir;
+
+				err = erofs_mkfs_build_tree(inode);
+				if (err) {
+					root = ERR_PTR(err);
+					break;
+				}
+				if (S_ISDIR(inode->i_mode)) {
+					*last = inode;
+					last = &inode->next_dirwrite;
+					(void)erofs_igrab(inode);
+				}
+			}
 		}
+		*last = dumpdir;	/* fixup the last (or the only) one */
+		dumpdir = head;
+		erofs_write_dir_file(dir);
+		erofs_write_tail_end(dir);
+		dir->bh->op = &erofs_write_inode_bhops;
+		erofs_iput(dir);
+	} while (dumpdir);
 
-		if (S_ISDIR(inode->i_mode)) {
-			inode->next_dirwrite = dumpdir;
-			dumpdir = inode;
-		} else {
-			erofs_iput(inode);
-		}
-	} while (!list_empty(&dirs));
-
-	while (dumpdir) {
-		inode = dumpdir;
-		erofs_write_dir_file(inode);
-		erofs_write_tail_end(inode);
-		inode->bh->op = &erofs_write_inode_bhops;
-		dumpdir = inode->next_dirwrite;
-		erofs_iput(inode);
-	}
 	return root;
 }
 
-- 
2.39.3



More information about the Linux-erofs mailing list