Patchwork [Ext4,punch,hole,1/4,v4] Ext4 Punch Hole Support: Convert Blocks to Uninit Exts

login
register
mail settings
Submitter Allison Henderson
Date March 19, 2011, 3:03 a.m.
Message ID <4D841D10.80900@linux.vnet.ibm.com>
Download mbox | patch
Permalink /patch/87596/
State Superseded
Headers show

Comments

Allison Henderson - March 19, 2011, 3:03 a.m.
This patch adds a function to convert a range of blocks
to an uninitialized extent.  This function will
be used to first convert the blocks to extents before
punching them out.

This function also adds a routine to split an extent at
a given block.  This routine is used when a range of
blocks to be converted is only partially contained in
an extent.

Signed-off-by: Allison Henderson <achender@us.ibm.com>
---
:100644 100644 9375e7c... b78b41f... M	fs/ext4/extents.c
 fs/ext4/extents.c |  209 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 209 insertions(+), 0 deletions(-)
Mingming Cao - March 20, 2011, 3:24 p.m.
On Fri, 2011-03-18 at 20:03 -0700, Allison Henderson wrote:
> This patch adds a function to convert a range of blocks
> to an uninitialized extent.  This function will
> be used to first convert the blocks to extents before
> punching them out.
> 
> This function also adds a routine to split an extent at
> a given block.  This routine is used when a range of
> blocks to be converted is only partially contained in
> an extent.
> 

We could factor out the extent split and covert code in the current
mainline code later. But at this moment, this part looks fine with me.

Reviewed-by: Mingming Cao<cmm@us.ibm.com>
> Signed-off-by: Allison Henderson <achender@us.ibm.com>
> ---
> :100644 100644 9375e7c... b78b41f... M	fs/ext4/extents.c
>  fs/ext4/extents.c |  209 +++++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 files changed, 209 insertions(+), 0 deletions(-)
> 
> diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
> index 9375e7c..b78b41f 100644
> --- a/fs/ext4/extents.c
> +++ b/fs/ext4/extents.c
> @@ -2169,6 +2169,109 @@ static int ext4_remove_blocks(handle_t *handle, struct inode *inode,
>  	return 0;
>  }
> 
> +/*
> + * ext4_split_extents() Splits a given extent at the block "split"
> + * @handle: The journal handle
> + * @inode: The file inode
> + * @split: The block where the extent will be split
> + * @path: The path to the extent
> + * @flags: flags used to insert the new extent
> + */
> +static int ext4_split_extents(handle_t *handle,
> +					struct inode *inode,
> +					ext4_lblk_t split,
> +					struct ext4_ext_path *path,
> +					int flags)
> +{
> +	struct ext4_extent *ex, newex, orig_ex;
> +	struct ext4_extent *ex1 = NULL;
> +	struct ext4_extent *ex2 = NULL;
> +	struct ext4_extent_header *eh;
> +	ext4_lblk_t ee_block;
> +	unsigned int ee_len, depth;
> +	ext4_fsblk_t newblock, origblock;
> +	int err = 0;
> +
> +	ext_debug("ext4_split_extent: inode %lu, split %u\n",
> +				inode->i_ino, split);
> +	ext4_ext_show_leaf(inode, path);
> +
> +	depth = ext_depth(inode);
> +	eh = path[depth].p_hdr;
> +	ex = path[depth].p_ext;
> +	ee_block = le32_to_cpu(ex->ee_block);
> +	ee_len = ext4_ext_get_actual_len(ex);
> +
> +	/* if the block is not actually in the extent, go to out */
> +	if (split > ee_block+ee_len || split < ee_block)
> +		goto out;
> +
> +	origblock = ee_block + ext4_ext_pblock(ex);
> +	newblock = split - ee_block + ext4_ext_pblock(ex);
> +	ext_debug("The new block is %llu, the orig block is: %llu\n",
> +				newblock, origblock);
> +
> +	/* save original block in case split fails */
> +	orig_ex.ee_block = ex->ee_block;
> +	orig_ex.ee_len   = cpu_to_le16(ee_len);
> +	ext4_ext_store_pblock(&orig_ex, ext4_ext_pblock(ex));
> +
> +	err = ext4_ext_get_access(handle, inode, path + depth);
> +	if (err)
> +		goto out;
> +
> +	ex1 = ex;
> +	ex1->ee_len = cpu_to_le16(split-ee_block);
> +
> +	ex2 = &newex;
> +	ex2->ee_len = cpu_to_le16(ee_len-(split-ee_block));
> +	ex2->ee_block = split;
> +	ext4_ext_store_pblock(ex2, newblock);
> +
> +	if (ex1->ee_block == ex2->ee_block) {
> +		/* Mark modified extent as dirty */
> +		err = ext4_ext_dirty(handle, inode, path + depth);
> +		ext_debug("out here\n");
> +		goto out;
> +	}
> +
> +	/*
> +	 * If this leaf cannot fit in any more extents
> +	 * insert it into another leaf
> +	 */
> +	if (EXT_LAST_EXTENT(eh) >=  EXT_MAX_EXTENT(eh)) {
> +		err = ext4_ext_insert_extent(handle, inode, path,
> +				&newex, flags);
> +		if (err)
> +			goto fix_extent_len;
> +
> +		depth = ext_depth(inode);
> +		path = ext4_ext_find_extent(inode, split, path);
> +	}
> +
> +	/* otherwise just scoot all ther other extents down  */
> +	else{
> +		memmove(ex1+2, ex1+1,
> +			(EXT_LAST_EXTENT(eh) - ex1) *
> +			sizeof(struct ext4_extent));
> +		memcpy(ex1+1, ex2, sizeof(struct ext4_extent));
> +		le16_add_cpu(&(eh->eh_entries), 1);
> +	}
> +
> +out:
> +	ext4_ext_show_leaf(inode, path);
> +	return err;
> +
> +fix_extent_len:
> +	ex->ee_block = orig_ex.ee_block;
> +	ex->ee_len   = orig_ex.ee_len;
> +	ext4_ext_store_pblock(ex, ext4_ext_pblock(&orig_ex));
> +	ext4_ext_mark_uninitialized(ex);
> +	ext4_ext_dirty(handle, inode, path + depth);
> +
> +	return err;
> +}
> +
>  static int
>  ext4_ext_rm_leaf(handle_t *handle, struct inode *inode,
>  		struct ext4_ext_path *path, ext4_lblk_t start)
> @@ -3598,6 +3701,112 @@ out_stop:
>  	ext4_journal_stop(handle);
>  }
> 
> +/*
> + * ext4_ext_convert_blocks_uninit()
> + * Converts a range of blocks to uninitialized
> + *
> + * @handle: The journal handle
> + * @inode:  The files inode
> + * @lblock: The logical block where the conversion will start
> + * @len:    The max number of blocks to convert
> + *
> + * Returns the number of blocks successfully converted
> + */
> +static int ext4_ext_convert_blocks_uninit(handle_t *handle,
> +				struct inode *inode,
> +				ext4_lblk_t lblock,
> +				ext4_lblk_t len){
> +
> +	ext4_lblk_t ee_block, iblock, blocks_checked;
> +	struct ext4_ext_path *path;
> +	struct ext4_extent *ex;
> +	unsigned int ee_len;
> +	int ret = 0;
> +	int err = 0;
> +
> +	iblock = lblock;
> +	while (iblock < lblock+len) {
> +		blocks_checked = 1;
> +		path = ext4_ext_find_extent(inode, iblock, NULL);
> +
> +		if (path == NULL)
> +			goto next;
> +
> +		err = ext4_ext_get_access(handle, inode, path);
> +		if (err < 0)
> +			goto next;
> +
> +		ex = path->p_ext;
> +		if (ex == NULL)
> +			goto next;
> +
> +		ee_block = le32_to_cpu(ex->ee_block);
> +		ee_len = ext4_ext_get_actual_len(ex);
> +
> +		if (ee_block > iblock ||
> +			ee_block+ee_len <= iblock)
> +			goto next;
> +
> +		/*
> +		 * If the block is in the middle of the extent,
> +		 * split the extent to remove the head.
> +		 * Then find the new extent that contains the block
> +		 */
> +		if (ee_block < iblock) {
> +			err = ext4_split_extents(handle, inode,
> +						iblock, path, 0);
> +
> +			if (err)
> +				goto next;
> +
> +			/* Release this path before we get the next one */
> +			ext4_ext_drop_refs(path);
> +			kfree(path);
> +
> +			path = ext4_ext_find_extent(inode, iblock, NULL);
> +
> +			if (path == NULL)
> +				goto next;
> +
> +			ex = path->p_ext;
> +			if (ex == NULL)
> +				goto next;
> +
> +			ee_block = le32_to_cpu(ex->ee_block);
> +			ee_len = ext4_ext_get_actual_len(ex);
> +		}
> +
> +		/*
> +		 * If the extent exceeds len, split
> +		 * the extent to remove the tail
> +		 */
> +		if (ee_len > len) {
> +			err = ext4_split_extents(handle, inode,
> +					lblock+len, path, 0);
> +
> +			if (err)
> +				goto next;
> +
> +			ee_len = ext4_ext_get_actual_len(ex);
> +		}
> +
> +		/* Mark the extent uninitialized */
> +		ext4_ext_mark_uninitialized(ex);
> +
> +		blocks_checked = ee_len;
> +		ret += ee_len;
> +next:
> +		if (path) {
> +			ext4_ext_drop_refs(path);
> +			kfree(path);
> +		}
> +		iblock += blocks_checked;
> +	}
> +
> +	return ret;
> +}
> +
> +
>  static void ext4_falloc_update_inode(struct inode *inode,
>  				int mode, loff_t new_size, int update_ctime)
>  {


--
To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Patch

diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index 9375e7c..b78b41f 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -2169,6 +2169,109 @@  static int ext4_remove_blocks(handle_t *handle, struct inode *inode,
 	return 0;
 }
 
+/*
+ * ext4_split_extents() Splits a given extent at the block "split"
+ * @handle: The journal handle
+ * @inode: The file inode
+ * @split: The block where the extent will be split
+ * @path: The path to the extent
+ * @flags: flags used to insert the new extent
+ */
+static int ext4_split_extents(handle_t *handle,
+					struct inode *inode,
+					ext4_lblk_t split,
+					struct ext4_ext_path *path,
+					int flags)
+{
+	struct ext4_extent *ex, newex, orig_ex;
+	struct ext4_extent *ex1 = NULL;
+	struct ext4_extent *ex2 = NULL;
+	struct ext4_extent_header *eh;
+	ext4_lblk_t ee_block;
+	unsigned int ee_len, depth;
+	ext4_fsblk_t newblock, origblock;
+	int err = 0;
+
+	ext_debug("ext4_split_extent: inode %lu, split %u\n",
+				inode->i_ino, split);
+	ext4_ext_show_leaf(inode, path);
+
+	depth = ext_depth(inode);
+	eh = path[depth].p_hdr;
+	ex = path[depth].p_ext;
+	ee_block = le32_to_cpu(ex->ee_block);
+	ee_len = ext4_ext_get_actual_len(ex);
+
+	/* if the block is not actually in the extent, go to out */
+	if (split > ee_block+ee_len || split < ee_block)
+		goto out;
+
+	origblock = ee_block + ext4_ext_pblock(ex);
+	newblock = split - ee_block + ext4_ext_pblock(ex);
+	ext_debug("The new block is %llu, the orig block is: %llu\n",
+				newblock, origblock);
+
+	/* save original block in case split fails */
+	orig_ex.ee_block = ex->ee_block;
+	orig_ex.ee_len   = cpu_to_le16(ee_len);
+	ext4_ext_store_pblock(&orig_ex, ext4_ext_pblock(ex));
+
+	err = ext4_ext_get_access(handle, inode, path + depth);
+	if (err)
+		goto out;
+
+	ex1 = ex;
+	ex1->ee_len = cpu_to_le16(split-ee_block);
+
+	ex2 = &newex;
+	ex2->ee_len = cpu_to_le16(ee_len-(split-ee_block));
+	ex2->ee_block = split;
+	ext4_ext_store_pblock(ex2, newblock);
+
+	if (ex1->ee_block == ex2->ee_block) {
+		/* Mark modified extent as dirty */
+		err = ext4_ext_dirty(handle, inode, path + depth);
+		ext_debug("out here\n");
+		goto out;
+	}
+
+	/*
+	 * If this leaf cannot fit in any more extents
+	 * insert it into another leaf
+	 */
+	if (EXT_LAST_EXTENT(eh) >=  EXT_MAX_EXTENT(eh)) {
+		err = ext4_ext_insert_extent(handle, inode, path,
+				&newex, flags);
+		if (err)
+			goto fix_extent_len;
+
+		depth = ext_depth(inode);
+		path = ext4_ext_find_extent(inode, split, path);
+	}
+
+	/* otherwise just scoot all ther other extents down  */
+	else{
+		memmove(ex1+2, ex1+1,
+			(EXT_LAST_EXTENT(eh) - ex1) *
+			sizeof(struct ext4_extent));
+		memcpy(ex1+1, ex2, sizeof(struct ext4_extent));
+		le16_add_cpu(&(eh->eh_entries), 1);
+	}
+
+out:
+	ext4_ext_show_leaf(inode, path);
+	return err;
+
+fix_extent_len:
+	ex->ee_block = orig_ex.ee_block;
+	ex->ee_len   = orig_ex.ee_len;
+	ext4_ext_store_pblock(ex, ext4_ext_pblock(&orig_ex));
+	ext4_ext_mark_uninitialized(ex);
+	ext4_ext_dirty(handle, inode, path + depth);
+
+	return err;
+}
+
 static int
 ext4_ext_rm_leaf(handle_t *handle, struct inode *inode,
 		struct ext4_ext_path *path, ext4_lblk_t start)
@@ -3598,6 +3701,112 @@  out_stop:
 	ext4_journal_stop(handle);
 }
 
+/*
+ * ext4_ext_convert_blocks_uninit()
+ * Converts a range of blocks to uninitialized
+ *
+ * @handle: The journal handle
+ * @inode:  The files inode
+ * @lblock: The logical block where the conversion will start
+ * @len:    The max number of blocks to convert
+ *
+ * Returns the number of blocks successfully converted
+ */
+static int ext4_ext_convert_blocks_uninit(handle_t *handle,
+				struct inode *inode,
+				ext4_lblk_t lblock,
+				ext4_lblk_t len){
+
+	ext4_lblk_t ee_block, iblock, blocks_checked;
+	struct ext4_ext_path *path;
+	struct ext4_extent *ex;
+	unsigned int ee_len;
+	int ret = 0;
+	int err = 0;
+
+	iblock = lblock;
+	while (iblock < lblock+len) {
+		blocks_checked = 1;
+		path = ext4_ext_find_extent(inode, iblock, NULL);
+
+		if (path == NULL)
+			goto next;
+
+		err = ext4_ext_get_access(handle, inode, path);
+		if (err < 0)
+			goto next;
+
+		ex = path->p_ext;
+		if (ex == NULL)
+			goto next;
+
+		ee_block = le32_to_cpu(ex->ee_block);
+		ee_len = ext4_ext_get_actual_len(ex);
+
+		if (ee_block > iblock ||
+			ee_block+ee_len <= iblock)
+			goto next;
+
+		/*
+		 * If the block is in the middle of the extent,
+		 * split the extent to remove the head.
+		 * Then find the new extent that contains the block
+		 */
+		if (ee_block < iblock) {
+			err = ext4_split_extents(handle, inode,
+						iblock, path, 0);
+
+			if (err)
+				goto next;
+
+			/* Release this path before we get the next one */
+			ext4_ext_drop_refs(path);
+			kfree(path);
+
+			path = ext4_ext_find_extent(inode, iblock, NULL);
+
+			if (path == NULL)
+				goto next;
+
+			ex = path->p_ext;
+			if (ex == NULL)
+				goto next;
+
+			ee_block = le32_to_cpu(ex->ee_block);
+			ee_len = ext4_ext_get_actual_len(ex);
+		}
+
+		/*
+		 * If the extent exceeds len, split
+		 * the extent to remove the tail
+		 */
+		if (ee_len > len) {
+			err = ext4_split_extents(handle, inode,
+					lblock+len, path, 0);
+
+			if (err)
+				goto next;
+
+			ee_len = ext4_ext_get_actual_len(ex);
+		}
+
+		/* Mark the extent uninitialized */
+		ext4_ext_mark_uninitialized(ex);
+
+		blocks_checked = ee_len;
+		ret += ee_len;
+next:
+		if (path) {
+			ext4_ext_drop_refs(path);
+			kfree(path);
+		}
+		iblock += blocks_checked;
+	}
+
+	return ret;
+}
+
+
 static void ext4_falloc_update_inode(struct inode *inode,
 				int mode, loff_t new_size, int update_ctime)
 {