[Devel] [RH7 PATCH] ext4: fallocate mode - convert and extend

Dmitry Monakhov dmonakhov at openvz.org
Tue Jun 23 08:14:05 PDT 2015


Original patch rh6/diff-ext4-fallocate-mode-convert-and-extend-v3

The patch introduces new fallocate mode: FALLOC_FL_CONVERT_AND_EXTEND. It
performs two actions:
 - convert all uninitialized extends in the range <offset, offset + length>
 - set i_size to "offset + length".

The feature will be used by ploop io_direct module for optimizing submit_alloc
path.

Changed in v2 (thanks to Dima for findings):
 - moved journal start/stop into while(){...}
 - added update_fsync_trans call

Changed in v3 (thanks again to Dima for findings):
 - protected operations on extnet tree by i_data_sem

https://jira.sw.ru/browse/PSBM-22381

Signed-off-by: Dmitry Monakhov <dmonakhov at openvz.org>
---
 fs/ext4/extents.c           |  135 ++++++++++++++++++++++++++++++++++++++++++-
 include/uapi/linux/falloc.h |    3 +
 2 files changed, 136 insertions(+), 2 deletions(-)

diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index 606a47c..dfa4e7a 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -4448,6 +4448,131 @@ static void ext4_falloc_update_inode(struct inode *inode,
 
 }
 
+
+static int ext4_convert_and_extend_locked(struct inode *inode, loff_t offset,
+					  loff_t len)
+{
+	struct ext4_ext_path *path = NULL;
+	loff_t new_size = offset + len;
+	ext4_lblk_t iblock = offset >> inode->i_blkbits;
+	ext4_lblk_t new_iblock = new_size >> inode->i_blkbits;
+	unsigned int max_blocks = new_iblock - iblock;
+	handle_t *handle;
+	unsigned int credits;
+	int err = 0;
+	int ret = 0;
+
+	if ((loff_t)iblock << inode->i_blkbits != offset ||
+	    (loff_t)new_iblock << inode->i_blkbits != new_size)
+		return -EINVAL;
+
+	while (max_blocks > 0) {
+		struct ext4_extent *ex;
+		ext4_lblk_t ee_block;
+		ext4_fsblk_t ee_start;
+		unsigned short ee_len;
+		int depth;
+
+		/*
+		 * credits to insert 1 extents into extent tree
+		 */
+		credits = ext4_chunk_trans_blocks(inode, max_blocks);
+		handle = ext4_journal_start(inode, EXT4_HT_MAP_BLOCKS, credits);
+		if (IS_ERR(handle))
+		       return PTR_ERR(handle);
+
+		down_write((&EXT4_I(inode)->i_data_sem));
+
+		/* find extent for this block */
+		path = ext4_ext_find_extent(inode, iblock, NULL);
+		if (IS_ERR(path)) {
+			err = PTR_ERR(path);
+			goto done;
+		}
+
+		depth = ext_depth(inode);
+		ex = path[depth].p_ext;
+		BUG_ON(ex == NULL && depth != 0);
+
+		if (ex == NULL) {
+			err = -ENOENT;
+			goto done;
+		}
+
+		ee_block = le32_to_cpu(ex->ee_block);
+		ee_start = ext4_ext_pblock(ex);
+		ee_len = ext4_ext_get_actual_len(ex);
+		if (!in_range(iblock, ee_block, ee_len)) {
+			err = -ERANGE;
+			goto done;
+		}
+
+		if (ext4_ext_is_uninitialized(ex)) {
+			struct ext4_map_blocks map = {0};
+
+			map.m_lblk = iblock;
+			map.m_len = max_blocks;
+			err = ext4_convert_unwritten_extents_endio(handle, inode,
+								   &map,
+								   path);
+			if (err < 0)
+				goto done;
+
+			ext4_update_inode_fsync_trans(handle, inode, 1);
+			err = check_eofblocks_fl(handle, inode, iblock, path,
+						 max_blocks);
+			if (err)
+				goto done;
+		}
+
+
+		up_write((&EXT4_I(inode)->i_data_sem));
+
+		iblock += ee_len;
+		max_blocks -= (ee_len < max_blocks) ? ee_len : max_blocks;
+
+		if (!max_blocks && new_size > i_size_read(inode)) {
+			i_size_write(inode, new_size);
+			ext4_update_i_disksize(inode, new_size);
+		}
+
+		ret = ext4_mark_inode_dirty(handle, inode);
+done:
+		if (err)
+			up_write((&EXT4_I(inode)->i_data_sem));
+		else
+			err = ret;
+
+		if (path) {
+			ext4_ext_drop_refs(path);
+			kfree(path);
+		}
+
+		ret = ext4_journal_stop(handle);
+		if (!err && ret)
+			err = ret;
+		if (err)
+			return err;
+	}
+
+	return 0;
+}
+
+static int ext4_convert_and_extend(struct inode *inode, loff_t offset,
+				   loff_t len)
+{
+	int err;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EACCES;
+
+	mutex_lock(&inode->i_mutex);
+	err = ext4_convert_and_extend_locked(inode, offset, len);
+	mutex_unlock(&inode->i_mutex);
+
+	return err;
+}
+
 /*
  * preallocate space for a file. This implements ext4's fallocate file
  * operation, which gets called from sys_fallocate system call.
@@ -4474,20 +4599,26 @@ long ext4_fallocate(struct file *file, int mode, loff_t offset, loff_t len)
 	 */
 	if (!(ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)))
 		return -EOPNOTSUPP;
-
+ 
 	/* Return error if mode is not supported */
-	if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE))
+	if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE |
+		     FALLOC_FL_CONVERT_AND_EXTEND))
 		return -EOPNOTSUPP;
 
 	if (mode & FALLOC_FL_PUNCH_HOLE)
 		return ext4_punch_hole(file, offset, len);
 
+	if (mode & FALLOC_FL_CONVERT_AND_EXTEND)
+		return ext4_convert_and_extend(inode, offset, len);
+
 	ret = ext4_convert_inline_data(inode);
 	if (ret)
 		return ret;
 
 	trace_ext4_fallocate_enter(inode, offset, len, mode);
 	map.m_lblk = offset >> blkbits;
+
+
 	/*
 	 * We can't just convert len to max_blocks because
 	 * If blocksize = 4096 offset = 3072 and len = 2048
diff --git a/include/uapi/linux/falloc.h b/include/uapi/linux/falloc.h
index 990c4cc..17b618f 100644
--- a/include/uapi/linux/falloc.h
+++ b/include/uapi/linux/falloc.h
@@ -4,6 +4,9 @@
 #define FALLOC_FL_KEEP_SIZE	0x01 /* default is extend size */
 #define FALLOC_FL_PUNCH_HOLE	0x02 /* de-allocates range */
 #define FALLOC_FL_NO_HIDE_STALE	0x04 /* reserved codepoint */
+#define FALLOC_FL_CONVERT_AND_EXTEND 0x100 /* mark extents as initialized
+					    * and extend i_size */
+
 
 
 #endif /* _UAPI_FALLOC_H_ */
-- 
1.7.1




More information about the Devel mailing list