[2/2] ext2fs: improve expand_dir performance

Message ID 1504079245-49002-2-git-send-email-adilger@dilger.ca
State New
Headers show
Series
  • [1/2] e2fsck: set dir_nlink feature if large dir exists
Related show

Commit Message

Andreas Dilger Aug. 30, 2017, 7:47 a.m.
Previously, ext2fs_expand_dir() in mke2fs and debugfs could
only add one block at a time after scanning the whole directory,
which was very slow if a large number of directory leaf blocks
are being added at once (in particular for the f_large_dir test).

Add a new ext2fs_expand_dir2() function that takes the number
of blocks to add to the directory and add them all at once, and
call that from mke2fs (to create lost+found) and debugfs (with
an optional 3rd argument to the "expand_dir" command).

Fix expand_dir_proc() to expand inline directories before trying
to add more blocks, to distinguish between adding blocks and
clusters, and not count added indirect/index blocks as leaves.

Have create_lost_and_found() round up to a full bigalloc chunk,
as there is little benefit if unused blocks in the same chunk
are left "free" since they can't be used for anything else.

This speeds up f_large_dir with 65000 files from 4232s to 4141s.

Signed-off-by: Andreas Dilger <adilger@dilger.ca>
---
 debugfs/debugfs.8.in     |  9 ++++++--
 debugfs/debugfs.c        | 23 +++++++++++++++++----
 lib/ext2fs/expanddir.c   | 53 ++++++++++++++++++++++++++++++++----------------
 lib/ext2fs/ext2fs.h      |  2 ++
 misc/mke2fs.c            | 27 +++++++++++++-----------
 tests/f_large_dir/script |  4 +---
 6 files changed, 79 insertions(+), 39 deletions(-)

Patch

diff --git a/debugfs/debugfs.8.in b/debugfs/debugfs.8.in
index 87d487e..e3f54c1 100644
--- a/debugfs/debugfs.8.in
+++ b/debugfs/debugfs.8.in
@@ -316,9 +316,14 @@  Remove the extended attribute
 .I attr_name
 from the file \fIfilespec\fR.
 .TP
-.BI expand_dir " filespec"
+.B expand_dir
+.I filespec
+.RI [ blocks_to_add ]
 Expand the directory
-.IR filespec .
+.I filespec
+by
+.I blocks_to_add
+blocks, or 1 block if unspecified.
 .TP
 .BI fallocate " filespec start_block [end_block]
 Allocate and map uninitialized blocks into \fIfilespec\fR between
diff --git a/debugfs/debugfs.c b/debugfs/debugfs.c
index 0a4b536..868cbbd 100644
--- a/debugfs/debugfs.c
+++ b/debugfs/debugfs.c
@@ -1965,15 +1965,30 @@  void do_show_debugfs_params(int argc EXT2FS_ATTR((unused)),
 #ifndef READ_ONLY
 void do_expand_dir(int argc, char *argv[])
 {
-	ext2_ino_t inode;
+	ext2_ino_t ino;
+	int add_blocks;
 	int retval;
 
-	if (common_inode_args_process(argc, argv, &inode, CHECK_FS_RW))
+	if (common_args_process(argc, argv, 2, 3, argv[0],
+				"<file> [blocks_to_add]",
+				CHECK_FS_RW | CHECK_FS_BITMAPS))
+		return;
+
+	ino = string_to_inode(argv[1]);
+	if (!ino)
 		return;
 
-	retval = ext2fs_expand_dir(current_fs, inode);
+	if (argc == 3) {
+		add_blocks = parse_ulong(argv[2], argv[1], "blocks_to_add",
+					 &retval);
+		if (retval)
+			return;
+	} else {
+		add_blocks = 1;
+	}
+	retval = ext2fs_expand_dir2(current_fs, ino, add_blocks);
 	if (retval)
-		com_err("ext2fs_expand_dir", retval, 0);
+		com_err("ext2fs_expand_dir2", retval, 0);
 	return;
 }
 
diff --git a/lib/ext2fs/expanddir.c b/lib/ext2fs/expanddir.c
index 9f02312..d88861e 100644
--- a/lib/ext2fs/expanddir.c
+++ b/lib/ext2fs/expanddir.c
@@ -21,11 +21,13 @@ 
 #include "ext2fsP.h"
 
 struct expand_dir_struct {
-	int		done;
-	int		newblocks;
+	unsigned	done:1;
+	unsigned	clusters_added;
 	blk64_t		goal;
 	errcode_t	err;
 	ext2_ino_t	dir;
+	unsigned	blocks_to_add;
+	unsigned	blocks_added;
 };
 
 static int expand_dir_proc(ext2_filsys	fs,
@@ -35,7 +37,7 @@  static int expand_dir_proc(ext2_filsys	fs,
 			   int		ref_offset EXT2FS_ATTR((unused)),
 			   void		*priv_data)
 {
-	struct expand_dir_struct *es = (struct expand_dir_struct *) priv_data;
+	struct expand_dir_struct *es = priv_data;
 	blk64_t	new_blk;
 	char		*block;
 	errcode_t	retval;
@@ -46,30 +48,33 @@  static int expand_dir_proc(ext2_filsys	fs,
 		return 0;
 	}
 	if (blockcnt &&
-	    (EXT2FS_B2C(fs, es->goal) == EXT2FS_B2C(fs, es->goal+1)))
-		new_blk = es->goal+1;
-	else {
+	    (EXT2FS_B2C(fs, es->goal) == EXT2FS_B2C(fs, es->goal + 1))) {
+		new_blk = es->goal + 1;
+	} else {
 		es->goal &= ~EXT2FS_CLUSTER_MASK(fs);
 		retval = ext2fs_new_block2(fs, es->goal, 0, &new_blk);
 		if (retval) {
 			es->err = retval;
 			return BLOCK_ABORT;
 		}
-		es->newblocks++;
+		es->clusters_added++;
 		ext2fs_block_alloc_stats2(fs, new_blk, +1);
 	}
 	if (blockcnt > 0) {
+		es->blocks_added++;
 		retval = ext2fs_new_dir_block(fs, 0, 0, &block);
 		if (retval) {
 			es->err = retval;
 			return BLOCK_ABORT;
 		}
-		es->done = 1;
+		es->done = (es->blocks_added >= es->blocks_to_add);
 		retval = ext2fs_write_dir_block4(fs, new_blk, block, 0,
 						 es->dir);
 		ext2fs_free_mem(&block);
-	} else
+	} else {
+		/* indirect or index block */
 		retval = ext2fs_zero_blocks2(fs, new_blk, 1, NULL, NULL);
+	}
 	if (blockcnt >= 0)
 		es->goal = new_blk;
 	if (retval) {
@@ -84,10 +89,11 @@  static int expand_dir_proc(ext2_filsys	fs,
 		return BLOCK_CHANGED;
 }
 
-errcode_t ext2fs_expand_dir(ext2_filsys fs, ext2_ino_t dir)
+errcode_t ext2fs_expand_dir2(ext2_filsys fs, ext2_ino_t dir,
+			     unsigned blocks_to_add)
 {
 	errcode_t	retval;
-	struct expand_dir_struct es;
+	struct expand_dir_struct es = { 0 };
 	struct ext2_inode	inode;
 
 	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
@@ -98,6 +104,9 @@  errcode_t ext2fs_expand_dir(ext2_filsys fs, ext2_ino_t dir)
 	if (!fs->block_map)
 		return EXT2_ET_NO_BLOCK_BITMAP;
 
+	if (blocks_to_add > fs->super->s_free_blocks_count)
+		return EXT2_ET_DIR_NO_SPACE;
+
 	retval = ext2fs_check_directory(fs, dir);
 	if (retval)
 		return retval;
@@ -106,16 +115,19 @@  errcode_t ext2fs_expand_dir(ext2_filsys fs, ext2_ino_t dir)
 	if (retval)
 		return retval;
 
-	es.done = 0;
-	es.err = 0;
+	/* expand inode inline data before starting iteration */
+	if (inode.i_flags & EXT4_INLINE_DATA_FL) {
+		retval = ext2fs_inline_data_expand(fs, dir);
+		if (retval)
+			return retval;
+		blocks_to_add--;
+	}
 	es.goal = ext2fs_find_inode_goal(fs, dir, &inode, 0);
-	es.newblocks = 0;
 	es.dir = dir;
+	es.blocks_to_add = blocks_to_add;
 
 	retval = ext2fs_block_iterate3(fs, dir, BLOCK_FLAG_APPEND,
 				       0, expand_dir_proc, &es);
-	if (retval == EXT2_ET_INLINE_DATA_CANT_ITERATE)
-		return ext2fs_inline_data_expand(fs, dir);
 
 	if (es.err)
 		return es.err;
@@ -129,8 +141,8 @@  errcode_t ext2fs_expand_dir(ext2_filsys fs, ext2_ino_t dir)
 	if (retval)
 		return retval;
 
-	inode.i_size += fs->blocksize;
-	ext2fs_iblk_add_blocks(fs, &inode, es.newblocks);
+	inode.i_size += fs->blocksize * es.blocks_added;
+	ext2fs_iblk_add_blocks(fs, &inode, es.clusters_added);
 
 	retval = ext2fs_write_inode(fs, dir, &inode);
 	if (retval)
@@ -138,3 +150,8 @@  errcode_t ext2fs_expand_dir(ext2_filsys fs, ext2_ino_t dir)
 
 	return 0;
 }
+
+errcode_t ext2fs_expand_dir(ext2_filsys fs, ext2_ino_t dir)
+{
+	return ext2fs_expand_dir2(fs, dir, 1);
+}
diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index b734f1a..0267660 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -1194,6 +1194,8 @@  extern errcode_t ext2fs_dup_handle(ext2_filsys src, ext2_filsys *dest);
 
 /* expanddir.c */
 extern errcode_t ext2fs_expand_dir(ext2_filsys fs, ext2_ino_t dir);
+extern errcode_t ext2fs_expand_dir2(ext2_filsys fs, ext2_ino_t dir,
+				    unsigned blocks_to_add);
 
 /* ext_attr.c */
 extern __u32 ext2fs_ext_attr_hash_entry(struct ext2_ext_attr_entry *entry,
diff --git a/misc/mke2fs.c b/misc/mke2fs.c
index 85d88ed..2a83040 100644
--- a/misc/mke2fs.c
+++ b/misc/mke2fs.c
@@ -507,18 +507,21 @@  static void create_lost_and_found(ext2_filsys fs)
 		exit(1);
 	}
 
-	for (i=1; i < EXT2_NDIR_BLOCKS; i++) {
-		/* Ensure that lost+found is at least 2 blocks, so we always
-		 * test large empty blocks for big-block filesystems.  */
-		if ((lpf_size += fs->blocksize) >= 16*1024 &&
-		    lpf_size >= 2 * fs->blocksize)
-			break;
-		retval = ext2fs_expand_dir(fs, ino);
-		if (retval) {
-			com_err("ext2fs_expand_dir", retval, "%s",
-				_("while expanding /lost+found"));
-			exit(1);
-		}
+	/* Ensure that lost+found is at least 2 blocks, so we always
+	 * test large empty blocks for big-block filesystems.  */
+	lpf_size = EXT2_NDIR_BLOCKS;
+	if (lpf_size * fs->blocksize > 16 * 1024)
+		lpf_size = 16 * 1024 / fs->blocksize;
+	if (lpf_size < 2)
+		lpf_size = 2;
+
+	/* round up size to full cluster, no point in making it smaller */
+	lpf_size = EXT2FS_C2B(fs, EXT2FS_B2C(fs, lpf_size - 1) + 1);
+	retval = ext2fs_expand_dir2(fs, ino, lpf_size - 1 /* initial block */);
+	if (retval) {
+		com_err("ext2fs_expand_dir", retval, "%s",
+			_("while expanding /lost+found"));
+		exit(1);
 	}
 }
 
diff --git a/tests/f_large_dir/script b/tests/f_large_dir/script
index b25e106..1824778 100644
--- a/tests/f_large_dir/script
+++ b/tests/f_large_dir/script
@@ -32,11 +32,9 @@  if [ $RC -eq 0 ]; then
 	echo "cd /foo"
 	touch $TMPFILE.tmp
 	echo "write $TMPFILE.tmp foofile"
+	echo "expand ./ $((DIRBLK))"
 	i=0
 	while test $i -lt $ENTRIES ; do
-	    if test $((i % DIRENT_PER_LEAF)) -eq 0; then
-	    	echo "expand ./"
-	    fi
 	    if test $((i % 5000)) -eq 0 -a $SECONDS -ne $START; then
 		ELAPSED=$((SECONDS - START))
 		RATE=$((i / ELAPSED))