All of lore.kernel.org
 help / color / mirror / Atom feed
* [Ext4 punch hole 3/5] Ext4 Punch Hole Support: Punch out extents
@ 2011-03-01  3:08 Allison Henderson
  2011-03-01  7:38 ` Dave Young
  0 siblings, 1 reply; 3+ messages in thread
From: Allison Henderson @ 2011-03-01  3:08 UTC (permalink / raw)
  To: linux-ext4

This patch modifes the truncate routines to support hole punching
Below is a brief summary of the pacthes changes:

- Added new function "ext_ext4_rm_leaf_punch_hole".
        This routine is very similar to ext_ext4_rm_leaf except that
        it punches a hole in a leaf instead of just removing the tail
        or removing the entire leaf all together

- Implemented the "remove head" case in the ext_remove_blocks routine
        This routine is used by ext_ext4_rm_leaf to remove the tail
        of an extent during a truncate.  The new ext_ext4_rm_leaf_punch_hole
        routine will now also use it to remove the head of an extent in the
        case that the hole covers a region of blocks at the beginning
        of an extent.

- Added "stop" param to ext4_ext_remove_space routine
        This function has been modified to accept a stop parameter.  If stop
        is not EXT_MAX_BLOCK, the routine will call ext_ext4_rm_leaf_punch_hole
        instead of ext_ext4_rm_leaf.

- Added new "ext4_ext_release_blocks" routine
        This routine is basically the ext4_ext_truncate routine, but
        modified to accept a "stop" param in addition to "start".  The existing
        ext4_ext_truncate routine has now become a wrapper to this
        function.  The stop parameter just passed through to ext4_ext_remove_space

Signed-off-by: Allison Henderson <achender@us.ibm.com>
---
:100644 100644 ab2e42e... efbc3ef... M	fs/ext4/extents.c
 fs/ext4/extents.c |  265 +++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 files changed, 256 insertions(+), 9 deletions(-)

diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index ab2e42e..efbc3ef 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -2159,8 +2159,16 @@ static int ext4_remove_blocks(handle_t *handle, struct inode *inode,
 		ext4_free_blocks(handle, inode, 0, start, num, flags);
 	} else if (from == le32_to_cpu(ex->ee_block)
 		   && to <= le32_to_cpu(ex->ee_block) + ee_len - 1) {
-		printk(KERN_INFO "strange request: removal %u-%u from %u:%u\n",
-			from, to, le32_to_cpu(ex->ee_block), ee_len);
+		/* head removal */
+		ext4_lblk_t num;
+		ext4_fsblk_t start;
+
+		num = to - from;
+		start = ext4_ext_pblock(ex);
+
+		ext_debug("free first %u blocks starting %llu\n", num, start);
+		ext4_free_blocks(handle, inode, 0, start, num, flags);
+	
 	} else {
 		printk(KERN_INFO "strange request: removal(2) "
 				"%u-%u from %u:%u\n",
@@ -2401,6 +2409,214 @@ out:
 }
 
 /*
+ * ext4_ext_rm_leaf_punch_hole() Removes the extents associated with the 
+ * blocks appearing between "start" and "stop", and splits the extents
+ * if "start" and "stop" appear in the same extent
+ */
+static int
+ext4_ext_rm_leaf_punch_hole(handle_t *handle, struct inode *inode,
+		struct ext4_ext_path *path, ext4_lblk_t start, ext4_lblk_t stop)
+{
+	int err = 0, correct_index = 0;
+	int depth = ext_depth(inode), credits;
+	struct ext4_extent_header *eh;
+	ext4_lblk_t a, b, block;
+	unsigned int num;
+	ext4_lblk_t ex_ee_block;
+	unsigned short ex_ee_len;
+	unsigned int uninitialized = 0;
+	struct ext4_extent *ex, *i_ex;
+
+	/* the header must be checked already in ext4_ext_remove_space() */
+	ext_debug("truncate since %u in leaf\n", start);
+	if (!path[depth].p_hdr)
+		path[depth].p_hdr = ext_block_hdr(path[depth].p_bh);
+
+	eh = path[depth].p_hdr;
+	if (unlikely(path[depth].p_hdr == NULL)) {
+		EXT4_ERROR_INODE(inode, "path[%d].p_hdr == NULL", depth);
+		return -EIO;
+	}
+
+	/* find where to start removing */
+	ex = EXT_LAST_EXTENT(eh);
+
+	ex_ee_block = le32_to_cpu(ex->ee_block);
+	ex_ee_len = ext4_ext_get_actual_len(ex);
+
+	while (ex >= EXT_FIRST_EXTENT(eh) &&
+			ex_ee_block + ex_ee_len > start ) {
+		if (ext4_ext_is_uninitialized(ex))
+			uninitialized = 1;
+		else
+			uninitialized = 0;
+
+		ext_debug("remove ext %u:[%d]%d\n", ex_ee_block,
+			 uninitialized, ex_ee_len);
+		path[depth].p_ext = ex;
+
+		a = ex_ee_block > start ? ex_ee_block : start;
+		b = ex_ee_block+ex_ee_len - 1 < stop ? ex_ee_block+ex_ee_len - 1 : stop;
+
+		ext_debug("  border %u:%u\n", a, b);
+
+		/* If this extent is beyond the end of the hole, skip it  */
+		if(stop <= ex_ee_block){
+			ex--;
+			ex_ee_block = le32_to_cpu(ex->ee_block);
+			ex_ee_len = ext4_ext_get_actual_len(ex);
+			continue;
+		}
+		else if (a != ex_ee_block && b != ex_ee_block + ex_ee_len - 1) {
+			/*
+			 * If this is a truncate, then this condition should 
+			 * never happen becase at least one of the end points
+			 * needs to be on the edge of the extent.
+			 */
+			if(stop == EXT_MAX_BLOCK){
+				ext_debug("  bad truncate %u:%u\n", start, stop);
+				block = 0;
+				num = 0;
+				err = -EIO;
+				goto out;
+			}
+			/* 
+			 * else this is a hole punch, so the extent needs to 
+			 * be split since neither edge of the hole is on the 
+			 * extent edge
+			 */
+			else{
+				err = ext4_split_extents(handle,inode, b,path,0);
+				if(err)
+					goto out;
+
+				ex_ee_len = ext4_ext_get_actual_len(ex);
+
+				b = ex_ee_block+ex_ee_len - 1 < stop ? ex_ee_block+ex_ee_len - 1 : stop;
+
+				/* Then remove tail of this extent */
+				block = ex_ee_block;
+				num = a - block;
+			}
+		}
+		else if (a != ex_ee_block) {
+			/* remove tail of the extent */
+			block = ex_ee_block;
+			num = a - block;
+		}
+		else if (b != ex_ee_block + ex_ee_len - 1) {
+			/* remove head of the extent */
+			block = b;
+			num =  ex_ee_block+ex_ee_len - b;
+
+			/* If this is a truncate, this condition should never happen */
+			if(stop == EXT_MAX_BLOCK){
+				err = -EIO;
+				goto out;
+			}
+		}
+		else {
+			/* remove whole extent: excellent! */
+			block = ex_ee_block;
+			num = 0;
+			if (a != ex_ee_block){
+				err = -EIO;
+				goto out;
+			}
+
+			if (b != ex_ee_block + ex_ee_len - 1){
+				err = -EIO;
+				goto out;
+			}
+		}
+
+		/*
+		 * 3 for leaf, sb, and inode plus 2 (bmap and group
+		 * descriptor) for each block group; assume two block
+		 * groups plus ex_ee_len/blocks_per_block_group for
+		 * the worst case
+		 */
+		credits = 7 + 2*(ex_ee_len/EXT4_BLOCKS_PER_GROUP(inode->i_sb));
+		if (ex == EXT_FIRST_EXTENT(eh)) {
+			correct_index = 1;
+			credits += (ext_depth(inode)) + 1;
+		}
+		credits += EXT4_MAXQUOTAS_TRANS_BLOCKS(inode->i_sb);
+		err = ext4_ext_truncate_extend_restart(handle, inode, credits);
+		if (err)
+			goto out;
+
+		err = ext4_ext_get_access(handle, inode, path + depth);
+		if (err)
+			goto out;
+		
+		err = ext4_remove_blocks(handle, inode, ex, a, b);
+		if (err)
+			goto out;
+
+		ex->ee_block = cpu_to_le32(block);
+		ex->ee_len = cpu_to_le16(num);
+		
+		/* 
+		 * If this was a head removal, then we need to update 
+		 * the physical block since it is now at a different  
+		 * location   
+		 */ 
+		if (block != ex_ee_block)
+			ext4_ext_store_pblock(ex, ext4_ext_pblock(ex)+(b-a) );
+
+		/*
+		 * Do not mark uninitialized if all the blocks in the
+		 * extent have been removed.
+		 */
+		if (uninitialized && num)
+			ext4_ext_mark_uninitialized(ex);
+
+		err = ext4_ext_dirty(handle, inode, path + depth);
+		if (err)
+			goto out;
+
+		ext_debug("new extent: %u:%u:%llu\n", block, num,
+				ext4_ext_pblock(ex));
+
+		if (num == 0) {
+
+			/* 
+			 * For hole punching, we need to scoot all the   
+			 * extents up so that we dont have blank extents 
+			 * in the middle 
+			 */
+			for(i_ex = ex; i_ex < EXT_LAST_EXTENT(eh); i_ex++){
+				memcpy(i_ex, i_ex+1, sizeof(struct ext4_extent));
+			}
+
+			/* now get rid of the extent at the end  */
+			memset(i_ex, 0, sizeof(struct ext4_extent));
+				
+			le16_add_cpu(&eh->eh_entries, -1);
+		}
+
+		ex--;
+		ex_ee_block = le32_to_cpu(ex->ee_block);
+		ex_ee_len = ext4_ext_get_actual_len(ex);
+
+	}
+
+	if (correct_index && eh->eh_entries)
+		err = ext4_ext_correct_indexes(handle, inode, path);
+
+	/* 
+	 * If this leaf is free, then we should
+	 * remove it from index block above 
+	 */
+	if (err == 0 && eh->eh_entries == 0 && path[depth].p_bh != NULL)
+		err = ext4_ext_rm_idx(handle, inode, path + depth);
+
+out:
+	return err;
+}
+
+/*
  * ext4_ext_more_to_rm:
  * returns 1 if current index has to be freed (even partial)
  */
@@ -2421,7 +2637,7 @@ ext4_ext_more_to_rm(struct ext4_ext_path *path)
 	return 1;
 }
 
-static int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start)
+static int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start, ext4_lblk_t stop)
 {
 	struct super_block *sb = inode->i_sb;
 	int depth = ext_depth(inode);
@@ -2460,7 +2676,10 @@ again:
 	while (i >= 0 && err == 0) {
 		if (i == depth) {
 			/* this is leaf block */
-			err = ext4_ext_rm_leaf(handle, inode, path, start);
+			if(stop == EXT_MAX_BLOCK)
+				err = ext4_ext_rm_leaf(handle, inode, path, start);
+			else
+				err = ext4_ext_rm_leaf_punch_hole(handle, inode, path, start, stop);
 			/* root level has p_bh == NULL, brelse() eats this */
 			brelse(path[i].p_bh);
 			path[i].p_bh = NULL;
@@ -3627,11 +3846,23 @@ out2:
 	return err ? err : allocated;
 }
 
-void ext4_ext_truncate(struct inode *inode)
+/*
+ * ext4_ext_release_blocks
+ *
+ * Releases the blocks in a file starting at block "start"
+ * and ending at block "stop".  Pass EXT_MAX_BLOCK
+ * for "stop" to just truncate the file to the
+ * "start" block
+ *
+ * @inode: The inode of the file to release blocks from 
+ * @start: The starting block of the hole
+ * @stop:  The ending block of the hole
+ *
+ */  
+static void ext4_ext_release_blocks(struct inode *inode, ext4_lblk_t start, ext4_lblk_t stop)
 {
 	struct address_space *mapping = inode->i_mapping;
 	struct super_block *sb = inode->i_sb;
-	ext4_lblk_t last_block;
 	handle_t *handle;
 	int err = 0;
 
@@ -3670,9 +3901,7 @@ void ext4_ext_truncate(struct inode *inode)
 	EXT4_I(inode)->i_disksize = inode->i_size;
 	ext4_mark_inode_dirty(handle, inode);
 
-	last_block = (inode->i_size + sb->s_blocksize - 1)
-			>> EXT4_BLOCK_SIZE_BITS(sb);
-	err = ext4_ext_remove_space(inode, last_block);
+	err = ext4_ext_remove_space(inode, start, stop);
 
 	/* In a multi-transaction truncate, we only make the final
 	 * transaction synchronous.
@@ -3698,6 +3927,24 @@ out_stop:
 }
 
 /*
+ * ext4_ext_truncate
+ *
+ * Truncate the file to the current i_size
+ *
+ * @inode: The file inode
+ */
+void ext4_ext_truncate(struct inode *inode)
+{
+	struct super_block *sb = inode->i_sb;
+	ext4_lblk_t last_block;
+
+	last_block = (inode->i_size + sb->s_blocksize - 1)
+			>> EXT4_BLOCK_SIZE_BITS(sb);
+
+	ext4_ext_release_blocks(inode, last_block, EXT_MAX_BLOCK);
+
+}
+/*
  * ext4_ext_convert_blocks_uninit()
  * Converts a range of blocks to uninitialized
  * 
-- 
1.7.1



^ permalink raw reply related	[flat|nested] 3+ messages in thread

* Re: [Ext4 punch hole 3/5] Ext4 Punch Hole Support: Punch out extents
  2011-03-01  3:08 [Ext4 punch hole 3/5] Ext4 Punch Hole Support: Punch out extents Allison Henderson
@ 2011-03-01  7:38 ` Dave Young
  2011-03-01 20:50   ` Allison Henderson
  0 siblings, 1 reply; 3+ messages in thread
From: Dave Young @ 2011-03-01  7:38 UTC (permalink / raw)
  To: Allison Henderson; +Cc: linux-ext4

On Tue, Mar 1, 2011 at 11:08 AM, Allison Henderson
<achender@linux.vnet.ibm.com> wrote:
> This patch modifes the truncate routines to support hole punching
> Below is a brief summary of the pacthes changes:
>
> - Added new function "ext_ext4_rm_leaf_punch_hole".
>        This routine is very similar to ext_ext4_rm_leaf except that
>        it punches a hole in a leaf instead of just removing the tail
>        or removing the entire leaf all together

These two functions are almost duplicate,
merge them as one function would be better

Also as Andreas Dilger said coding style problems need fix as well

>
> - Implemented the "remove head" case in the ext_remove_blocks routine
>        This routine is used by ext_ext4_rm_leaf to remove the tail
>        of an extent during a truncate.  The new ext_ext4_rm_leaf_punch_hole
>        routine will now also use it to remove the head of an extent in the
>        case that the hole covers a region of blocks at the beginning
>        of an extent.
>
> - Added "stop" param to ext4_ext_remove_space routine
>        This function has been modified to accept a stop parameter.  If stop
>        is not EXT_MAX_BLOCK, the routine will call ext_ext4_rm_leaf_punch_hole
>        instead of ext_ext4_rm_leaf.
>
> - Added new "ext4_ext_release_blocks" routine
>        This routine is basically the ext4_ext_truncate routine, but
>        modified to accept a "stop" param in addition to "start".  The existing
>        ext4_ext_truncate routine has now become a wrapper to this
>        function.  The stop parameter just passed through to ext4_ext_remove_space
>
> Signed-off-by: Allison Henderson <achender@us.ibm.com>
> ---
> :100644 100644 ab2e42e... efbc3ef... M  fs/ext4/extents.c
>  fs/ext4/extents.c |  265 +++++++++++++++++++++++++++++++++++++++++++++++++++--
>  1 files changed, 256 insertions(+), 9 deletions(-)
>
> diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
> index ab2e42e..efbc3ef 100644
> --- a/fs/ext4/extents.c
> +++ b/fs/ext4/extents.c
> @@ -2159,8 +2159,16 @@ static int ext4_remove_blocks(handle_t *handle, struct inode *inode,
>                ext4_free_blocks(handle, inode, 0, start, num, flags);
>        } else if (from == le32_to_cpu(ex->ee_block)
>                   && to <= le32_to_cpu(ex->ee_block) + ee_len - 1) {
> -               printk(KERN_INFO "strange request: removal %u-%u from %u:%u\n",
> -                       from, to, le32_to_cpu(ex->ee_block), ee_len);
> +               /* head removal */
> +               ext4_lblk_t num;
> +               ext4_fsblk_t start;
> +
> +               num = to - from;
> +               start = ext4_ext_pblock(ex);
> +
> +               ext_debug("free first %u blocks starting %llu\n", num, start);
> +               ext4_free_blocks(handle, inode, 0, start, num, flags);
> +
>        } else {
>                printk(KERN_INFO "strange request: removal(2) "
>                                "%u-%u from %u:%u\n",
> @@ -2401,6 +2409,214 @@ out:
>  }
>
>  /*
> + * ext4_ext_rm_leaf_punch_hole() Removes the extents associated with the
> + * blocks appearing between "start" and "stop", and splits the extents
> + * if "start" and "stop" appear in the same extent
> + */
> +static int
> +ext4_ext_rm_leaf_punch_hole(handle_t *handle, struct inode *inode,
> +               struct ext4_ext_path *path, ext4_lblk_t start, ext4_lblk_t stop)
> +{
> +       int err = 0, correct_index = 0;
> +       int depth = ext_depth(inode), credits;
> +       struct ext4_extent_header *eh;
> +       ext4_lblk_t a, b, block;
> +       unsigned int num;
> +       ext4_lblk_t ex_ee_block;
> +       unsigned short ex_ee_len;
> +       unsigned int uninitialized = 0;
> +       struct ext4_extent *ex, *i_ex;
> +
> +       /* the header must be checked already in ext4_ext_remove_space() */
> +       ext_debug("truncate since %u in leaf\n", start);
> +       if (!path[depth].p_hdr)
> +               path[depth].p_hdr = ext_block_hdr(path[depth].p_bh);
> +
> +       eh = path[depth].p_hdr;
> +       if (unlikely(path[depth].p_hdr == NULL)) {
> +               EXT4_ERROR_INODE(inode, "path[%d].p_hdr == NULL", depth);
> +               return -EIO;
> +       }
> +
> +       /* find where to start removing */
> +       ex = EXT_LAST_EXTENT(eh);
> +
> +       ex_ee_block = le32_to_cpu(ex->ee_block);
> +       ex_ee_len = ext4_ext_get_actual_len(ex);
> +
> +       while (ex >= EXT_FIRST_EXTENT(eh) &&
> +                       ex_ee_block + ex_ee_len > start ) {
> +               if (ext4_ext_is_uninitialized(ex))
> +                       uninitialized = 1;
> +               else
> +                       uninitialized = 0;
> +
> +               ext_debug("remove ext %u:[%d]%d\n", ex_ee_block,
> +                        uninitialized, ex_ee_len);
> +               path[depth].p_ext = ex;
> +
> +               a = ex_ee_block > start ? ex_ee_block : start;
> +               b = ex_ee_block+ex_ee_len - 1 < stop ? ex_ee_block+ex_ee_len - 1 : stop;
> +
> +               ext_debug("  border %u:%u\n", a, b);
> +
> +               /* If this extent is beyond the end of the hole, skip it  */
> +               if(stop <= ex_ee_block){
> +                       ex--;
> +                       ex_ee_block = le32_to_cpu(ex->ee_block);
> +                       ex_ee_len = ext4_ext_get_actual_len(ex);
> +                       continue;
> +               }
> +               else if (a != ex_ee_block && b != ex_ee_block + ex_ee_len - 1) {
> +                       /*
> +                        * If this is a truncate, then this condition should
> +                        * never happen becase at least one of the end points
> +                        * needs to be on the edge of the extent.
> +                        */
> +                       if(stop == EXT_MAX_BLOCK){
> +                               ext_debug("  bad truncate %u:%u\n", start, stop);
> +                               block = 0;
> +                               num = 0;
> +                               err = -EIO;
> +                               goto out;
> +                       }
> +                       /*
> +                        * else this is a hole punch, so the extent needs to
> +                        * be split since neither edge of the hole is on the
> +                        * extent edge
> +                        */
> +                       else{
> +                               err = ext4_split_extents(handle,inode, b,path,0);
> +                               if(err)
> +                                       goto out;
> +
> +                               ex_ee_len = ext4_ext_get_actual_len(ex);
> +
> +                               b = ex_ee_block+ex_ee_len - 1 < stop ? ex_ee_block+ex_ee_len - 1 : stop;
> +
> +                               /* Then remove tail of this extent */
> +                               block = ex_ee_block;
> +                               num = a - block;
> +                       }
> +               }
> +               else if (a != ex_ee_block) {
> +                       /* remove tail of the extent */
> +                       block = ex_ee_block;
> +                       num = a - block;
> +               }
> +               else if (b != ex_ee_block + ex_ee_len - 1) {
> +                       /* remove head of the extent */
> +                       block = b;
> +                       num =  ex_ee_block+ex_ee_len - b;
> +
> +                       /* If this is a truncate, this condition should never happen */
> +                       if(stop == EXT_MAX_BLOCK){
> +                               err = -EIO;
> +                               goto out;
> +                       }
> +               }
> +               else {
> +                       /* remove whole extent: excellent! */
> +                       block = ex_ee_block;
> +                       num = 0;
> +                       if (a != ex_ee_block){
> +                               err = -EIO;
> +                               goto out;
> +                       }
> +
> +                       if (b != ex_ee_block + ex_ee_len - 1){
> +                               err = -EIO;
> +                               goto out;
> +                       }
> +               }
> +
> +               /*
> +                * 3 for leaf, sb, and inode plus 2 (bmap and group
> +                * descriptor) for each block group; assume two block
> +                * groups plus ex_ee_len/blocks_per_block_group for
> +                * the worst case
> +                */
> +               credits = 7 + 2*(ex_ee_len/EXT4_BLOCKS_PER_GROUP(inode->i_sb));
> +               if (ex == EXT_FIRST_EXTENT(eh)) {
> +                       correct_index = 1;
> +                       credits += (ext_depth(inode)) + 1;
> +               }
> +               credits += EXT4_MAXQUOTAS_TRANS_BLOCKS(inode->i_sb);
> +               err = ext4_ext_truncate_extend_restart(handle, inode, credits);
> +               if (err)
> +                       goto out;
> +
> +               err = ext4_ext_get_access(handle, inode, path + depth);
> +               if (err)
> +                       goto out;
> +
> +               err = ext4_remove_blocks(handle, inode, ex, a, b);
> +               if (err)
> +                       goto out;
> +
> +               ex->ee_block = cpu_to_le32(block);
> +               ex->ee_len = cpu_to_le16(num);
> +
> +               /*
> +                * If this was a head removal, then we need to update
> +                * the physical block since it is now at a different
> +                * location
> +                */
> +               if (block != ex_ee_block)
> +                       ext4_ext_store_pblock(ex, ext4_ext_pblock(ex)+(b-a) );
> +
> +               /*
> +                * Do not mark uninitialized if all the blocks in the
> +                * extent have been removed.
> +                */
> +               if (uninitialized && num)
> +                       ext4_ext_mark_uninitialized(ex);
> +
> +               err = ext4_ext_dirty(handle, inode, path + depth);
> +               if (err)
> +                       goto out;
> +
> +               ext_debug("new extent: %u:%u:%llu\n", block, num,
> +                               ext4_ext_pblock(ex));
> +
> +               if (num == 0) {
> +
> +                       /*
> +                        * For hole punching, we need to scoot all the
> +                        * extents up so that we dont have blank extents
> +                        * in the middle
> +                        */
> +                       for(i_ex = ex; i_ex < EXT_LAST_EXTENT(eh); i_ex++){
> +                               memcpy(i_ex, i_ex+1, sizeof(struct ext4_extent));
> +                       }
> +
> +                       /* now get rid of the extent at the end  */
> +                       memset(i_ex, 0, sizeof(struct ext4_extent));
> +
> +                       le16_add_cpu(&eh->eh_entries, -1);
> +               }
> +
> +               ex--;
> +               ex_ee_block = le32_to_cpu(ex->ee_block);
> +               ex_ee_len = ext4_ext_get_actual_len(ex);
> +
> +       }
> +
> +       if (correct_index && eh->eh_entries)
> +               err = ext4_ext_correct_indexes(handle, inode, path);
> +
> +       /*
> +        * If this leaf is free, then we should
> +        * remove it from index block above
> +        */
> +       if (err == 0 && eh->eh_entries == 0 && path[depth].p_bh != NULL)
> +               err = ext4_ext_rm_idx(handle, inode, path + depth);
> +
> +out:
> +       return err;
> +}
> +
> +/*
>  * ext4_ext_more_to_rm:
>  * returns 1 if current index has to be freed (even partial)
>  */
> @@ -2421,7 +2637,7 @@ ext4_ext_more_to_rm(struct ext4_ext_path *path)
>        return 1;
>  }
>
> -static int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start)
> +static int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start, ext4_lblk_t stop)
>  {
>        struct super_block *sb = inode->i_sb;
>        int depth = ext_depth(inode);
> @@ -2460,7 +2676,10 @@ again:
>        while (i >= 0 && err == 0) {
>                if (i == depth) {
>                        /* this is leaf block */
> -                       err = ext4_ext_rm_leaf(handle, inode, path, start);
> +                       if(stop == EXT_MAX_BLOCK)
> +                               err = ext4_ext_rm_leaf(handle, inode, path, start);
> +                       else
> +                               err = ext4_ext_rm_leaf_punch_hole(handle, inode, path, start, stop);
>                        /* root level has p_bh == NULL, brelse() eats this */
>                        brelse(path[i].p_bh);
>                        path[i].p_bh = NULL;
> @@ -3627,11 +3846,23 @@ out2:
>        return err ? err : allocated;
>  }
>
> -void ext4_ext_truncate(struct inode *inode)
> +/*
> + * ext4_ext_release_blocks
> + *
> + * Releases the blocks in a file starting at block "start"
> + * and ending at block "stop".  Pass EXT_MAX_BLOCK
> + * for "stop" to just truncate the file to the
> + * "start" block
> + *
> + * @inode: The inode of the file to release blocks from
> + * @start: The starting block of the hole
> + * @stop:  The ending block of the hole
> + *
> + */
> +static void ext4_ext_release_blocks(struct inode *inode, ext4_lblk_t start, ext4_lblk_t stop)
>  {
>        struct address_space *mapping = inode->i_mapping;
>        struct super_block *sb = inode->i_sb;
> -       ext4_lblk_t last_block;
>        handle_t *handle;
>        int err = 0;
>
> @@ -3670,9 +3901,7 @@ void ext4_ext_truncate(struct inode *inode)
>        EXT4_I(inode)->i_disksize = inode->i_size;
>        ext4_mark_inode_dirty(handle, inode);
>
> -       last_block = (inode->i_size + sb->s_blocksize - 1)
> -                       >> EXT4_BLOCK_SIZE_BITS(sb);
> -       err = ext4_ext_remove_space(inode, last_block);
> +       err = ext4_ext_remove_space(inode, start, stop);
>
>        /* In a multi-transaction truncate, we only make the final
>         * transaction synchronous.
> @@ -3698,6 +3927,24 @@ out_stop:
>  }
>
>  /*
> + * ext4_ext_truncate
> + *
> + * Truncate the file to the current i_size
> + *
> + * @inode: The file inode
> + */
> +void ext4_ext_truncate(struct inode *inode)
> +{
> +       struct super_block *sb = inode->i_sb;
> +       ext4_lblk_t last_block;
> +
> +       last_block = (inode->i_size + sb->s_blocksize - 1)
> +                       >> EXT4_BLOCK_SIZE_BITS(sb);
> +
> +       ext4_ext_release_blocks(inode, last_block, EXT_MAX_BLOCK);
> +
> +}
> +/*
>  * ext4_ext_convert_blocks_uninit()
>  * Converts a range of blocks to uninitialized
>  *
> --
> 1.7.1
>
>
> --
> 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
>



-- 
Regards
dave

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [Ext4 punch hole 3/5] Ext4 Punch Hole Support: Punch out extents
  2011-03-01  7:38 ` Dave Young
@ 2011-03-01 20:50   ` Allison Henderson
  0 siblings, 0 replies; 3+ messages in thread
From: Allison Henderson @ 2011-03-01 20:50 UTC (permalink / raw)
  To: Dave Young; +Cc: linux-ext4

On 3/1/2011 12:38 AM, Dave Young wrote:
> On Tue, Mar 1, 2011 at 11:08 AM, Allison Henderson
> <achender@linux.vnet.ibm.com>  wrote:
>> This patch modifes the truncate routines to support hole punching
>> Below is a brief summary of the pacthes changes:
>>
>> - Added new function "ext_ext4_rm_leaf_punch_hole".
>>         This routine is very similar to ext_ext4_rm_leaf except that
>>         it punches a hole in a leaf instead of just removing the tail
>>         or removing the entire leaf all together
> 
> These two functions are almost duplicate,
> merge them as one function would be better

Alrighty, initially they were separated to make development easier, but it hasnt deviated a whole lot from the original ext_ext4_rm_leaf routine, so I will see if I can get them merged back together.

> 
> Also as Andreas Dilger said coding style problems need fix as well
> 
>>
>> - Implemented the "remove head" case in the ext_remove_blocks routine
>>         This routine is used by ext_ext4_rm_leaf to remove the tail
>>         of an extent during a truncate.  The new ext_ext4_rm_leaf_punch_hole
>>         routine will now also use it to remove the head of an extent in the
>>         case that the hole covers a region of blocks at the beginning
>>         of an extent.
>>
>> - Added "stop" param to ext4_ext_remove_space routine
>>         This function has been modified to accept a stop parameter.  If stop
>>         is not EXT_MAX_BLOCK, the routine will call ext_ext4_rm_leaf_punch_hole
>>         instead of ext_ext4_rm_leaf.
>>
>> - Added new "ext4_ext_release_blocks" routine
>>         This routine is basically the ext4_ext_truncate routine, but
>>         modified to accept a "stop" param in addition to "start".  The existing
>>         ext4_ext_truncate routine has now become a wrapper to this
>>         function.  The stop parameter just passed through to ext4_ext_remove_space
>>
>> Signed-off-by: Allison Henderson<achender@us.ibm.com>
>> ---
>> :100644 100644 ab2e42e... efbc3ef... M  fs/ext4/extents.c
>>   fs/ext4/extents.c |  265 +++++++++++++++++++++++++++++++++++++++++++++++++++--
>>   1 files changed, 256 insertions(+), 9 deletions(-)
>>
>> diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
>> index ab2e42e..efbc3ef 100644
>> --- a/fs/ext4/extents.c
>> +++ b/fs/ext4/extents.c
>> @@ -2159,8 +2159,16 @@ static int ext4_remove_blocks(handle_t *handle, struct inode *inode,
>>                 ext4_free_blocks(handle, inode, 0, start, num, flags);
>>         } else if (from == le32_to_cpu(ex->ee_block)
>>                    &&  to<= le32_to_cpu(ex->ee_block) + ee_len - 1) {
>> -               printk(KERN_INFO "strange request: removal %u-%u from %u:%u\n",
>> -                       from, to, le32_to_cpu(ex->ee_block), ee_len);
>> +               /* head removal */
>> +               ext4_lblk_t num;
>> +               ext4_fsblk_t start;
>> +
>> +               num = to - from;
>> +               start = ext4_ext_pblock(ex);
>> +
>> +               ext_debug("free first %u blocks starting %llu\n", num, start);
>> +               ext4_free_blocks(handle, inode, 0, start, num, flags);
>> +
>>         } else {
>>                 printk(KERN_INFO "strange request: removal(2) "
>>                                 "%u-%u from %u:%u\n",
>> @@ -2401,6 +2409,214 @@ out:
>>   }
>>
>>   /*
>> + * ext4_ext_rm_leaf_punch_hole() Removes the extents associated with the
>> + * blocks appearing between "start" and "stop", and splits the extents
>> + * if "start" and "stop" appear in the same extent
>> + */
>> +static int
>> +ext4_ext_rm_leaf_punch_hole(handle_t *handle, struct inode *inode,
>> +               struct ext4_ext_path *path, ext4_lblk_t start, ext4_lblk_t stop)
>> +{
>> +       int err = 0, correct_index = 0;
>> +       int depth = ext_depth(inode), credits;
>> +       struct ext4_extent_header *eh;
>> +       ext4_lblk_t a, b, block;
>> +       unsigned int num;
>> +       ext4_lblk_t ex_ee_block;
>> +       unsigned short ex_ee_len;
>> +       unsigned int uninitialized = 0;
>> +       struct ext4_extent *ex, *i_ex;
>> +
>> +       /* the header must be checked already in ext4_ext_remove_space() */
>> +       ext_debug("truncate since %u in leaf\n", start);
>> +       if (!path[depth].p_hdr)
>> +               path[depth].p_hdr = ext_block_hdr(path[depth].p_bh);
>> +
>> +       eh = path[depth].p_hdr;
>> +       if (unlikely(path[depth].p_hdr == NULL)) {
>> +               EXT4_ERROR_INODE(inode, "path[%d].p_hdr == NULL", depth);
>> +               return -EIO;
>> +       }
>> +
>> +       /* find where to start removing */
>> +       ex = EXT_LAST_EXTENT(eh);
>> +
>> +       ex_ee_block = le32_to_cpu(ex->ee_block);
>> +       ex_ee_len = ext4_ext_get_actual_len(ex);
>> +
>> +       while (ex>= EXT_FIRST_EXTENT(eh)&&
>> +                       ex_ee_block + ex_ee_len>  start ) {
>> +               if (ext4_ext_is_uninitialized(ex))
>> +                       uninitialized = 1;
>> +               else
>> +                       uninitialized = 0;
>> +
>> +               ext_debug("remove ext %u:[%d]%d\n", ex_ee_block,
>> +                        uninitialized, ex_ee_len);
>> +               path[depth].p_ext = ex;
>> +
>> +               a = ex_ee_block>  start ? ex_ee_block : start;
>> +               b = ex_ee_block+ex_ee_len - 1<  stop ? ex_ee_block+ex_ee_len - 1 : stop;
>> +
>> +               ext_debug("  border %u:%u\n", a, b);
>> +
>> +               /* If this extent is beyond the end of the hole, skip it  */
>> +               if(stop<= ex_ee_block){
>> +                       ex--;
>> +                       ex_ee_block = le32_to_cpu(ex->ee_block);
>> +                       ex_ee_len = ext4_ext_get_actual_len(ex);
>> +                       continue;
>> +               }
>> +               else if (a != ex_ee_block&&  b != ex_ee_block + ex_ee_len - 1) {
>> +                       /*
>> +                        * If this is a truncate, then this condition should
>> +                        * never happen becase at least one of the end points
>> +                        * needs to be on the edge of the extent.
>> +                        */
>> +                       if(stop == EXT_MAX_BLOCK){
>> +                               ext_debug("  bad truncate %u:%u\n", start, stop);
>> +                               block = 0;
>> +                               num = 0;
>> +                               err = -EIO;
>> +                               goto out;
>> +                       }
>> +                       /*
>> +                        * else this is a hole punch, so the extent needs to
>> +                        * be split since neither edge of the hole is on the
>> +                        * extent edge
>> +                        */
>> +                       else{
>> +                               err = ext4_split_extents(handle,inode, b,path,0);
>> +                               if(err)
>> +                                       goto out;
>> +
>> +                               ex_ee_len = ext4_ext_get_actual_len(ex);
>> +
>> +                               b = ex_ee_block+ex_ee_len - 1<  stop ? ex_ee_block+ex_ee_len - 1 : stop;
>> +
>> +                               /* Then remove tail of this extent */
>> +                               block = ex_ee_block;
>> +                               num = a - block;
>> +                       }
>> +               }
>> +               else if (a != ex_ee_block) {
>> +                       /* remove tail of the extent */
>> +                       block = ex_ee_block;
>> +                       num = a - block;
>> +               }
>> +               else if (b != ex_ee_block + ex_ee_len - 1) {
>> +                       /* remove head of the extent */
>> +                       block = b;
>> +                       num =  ex_ee_block+ex_ee_len - b;
>> +
>> +                       /* If this is a truncate, this condition should never happen */
>> +                       if(stop == EXT_MAX_BLOCK){
>> +                               err = -EIO;
>> +                               goto out;
>> +                       }
>> +               }
>> +               else {
>> +                       /* remove whole extent: excellent! */
>> +                       block = ex_ee_block;
>> +                       num = 0;
>> +                       if (a != ex_ee_block){
>> +                               err = -EIO;
>> +                               goto out;
>> +                       }
>> +
>> +                       if (b != ex_ee_block + ex_ee_len - 1){
>> +                               err = -EIO;
>> +                               goto out;
>> +                       }
>> +               }
>> +
>> +               /*
>> +                * 3 for leaf, sb, and inode plus 2 (bmap and group
>> +                * descriptor) for each block group; assume two block
>> +                * groups plus ex_ee_len/blocks_per_block_group for
>> +                * the worst case
>> +                */
>> +               credits = 7 + 2*(ex_ee_len/EXT4_BLOCKS_PER_GROUP(inode->i_sb));
>> +               if (ex == EXT_FIRST_EXTENT(eh)) {
>> +                       correct_index = 1;
>> +                       credits += (ext_depth(inode)) + 1;
>> +               }
>> +               credits += EXT4_MAXQUOTAS_TRANS_BLOCKS(inode->i_sb);
>> +               err = ext4_ext_truncate_extend_restart(handle, inode, credits);
>> +               if (err)
>> +                       goto out;
>> +
>> +               err = ext4_ext_get_access(handle, inode, path + depth);
>> +               if (err)
>> +                       goto out;
>> +
>> +               err = ext4_remove_blocks(handle, inode, ex, a, b);
>> +               if (err)
>> +                       goto out;
>> +
>> +               ex->ee_block = cpu_to_le32(block);
>> +               ex->ee_len = cpu_to_le16(num);
>> +
>> +               /*
>> +                * If this was a head removal, then we need to update
>> +                * the physical block since it is now at a different
>> +                * location
>> +                */
>> +               if (block != ex_ee_block)
>> +                       ext4_ext_store_pblock(ex, ext4_ext_pblock(ex)+(b-a) );
>> +
>> +               /*
>> +                * Do not mark uninitialized if all the blocks in the
>> +                * extent have been removed.
>> +                */
>> +               if (uninitialized&&  num)
>> +                       ext4_ext_mark_uninitialized(ex);
>> +
>> +               err = ext4_ext_dirty(handle, inode, path + depth);
>> +               if (err)
>> +                       goto out;
>> +
>> +               ext_debug("new extent: %u:%u:%llu\n", block, num,
>> +                               ext4_ext_pblock(ex));
>> +
>> +               if (num == 0) {
>> +
>> +                       /*
>> +                        * For hole punching, we need to scoot all the
>> +                        * extents up so that we dont have blank extents
>> +                        * in the middle
>> +                        */
>> +                       for(i_ex = ex; i_ex<  EXT_LAST_EXTENT(eh); i_ex++){
>> +                               memcpy(i_ex, i_ex+1, sizeof(struct ext4_extent));
>> +                       }
>> +
>> +                       /* now get rid of the extent at the end  */
>> +                       memset(i_ex, 0, sizeof(struct ext4_extent));
>> +
>> +                       le16_add_cpu(&eh->eh_entries, -1);
>> +               }
>> +
>> +               ex--;
>> +               ex_ee_block = le32_to_cpu(ex->ee_block);
>> +               ex_ee_len = ext4_ext_get_actual_len(ex);
>> +
>> +       }
>> +
>> +       if (correct_index&&  eh->eh_entries)
>> +               err = ext4_ext_correct_indexes(handle, inode, path);
>> +
>> +       /*
>> +        * If this leaf is free, then we should
>> +        * remove it from index block above
>> +        */
>> +       if (err == 0&&  eh->eh_entries == 0&&  path[depth].p_bh != NULL)
>> +               err = ext4_ext_rm_idx(handle, inode, path + depth);
>> +
>> +out:
>> +       return err;
>> +}
>> +
>> +/*
>>   * ext4_ext_more_to_rm:
>>   * returns 1 if current index has to be freed (even partial)
>>   */
>> @@ -2421,7 +2637,7 @@ ext4_ext_more_to_rm(struct ext4_ext_path *path)
>>         return 1;
>>   }
>>
>> -static int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start)
>> +static int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start, ext4_lblk_t stop)
>>   {
>>         struct super_block *sb = inode->i_sb;
>>         int depth = ext_depth(inode);
>> @@ -2460,7 +2676,10 @@ again:
>>         while (i>= 0&&  err == 0) {
>>                 if (i == depth) {
>>                         /* this is leaf block */
>> -                       err = ext4_ext_rm_leaf(handle, inode, path, start);
>> +                       if(stop == EXT_MAX_BLOCK)
>> +                               err = ext4_ext_rm_leaf(handle, inode, path, start);
>> +                       else
>> +                               err = ext4_ext_rm_leaf_punch_hole(handle, inode, path, start, stop);
>>                         /* root level has p_bh == NULL, brelse() eats this */
>>                         brelse(path[i].p_bh);
>>                         path[i].p_bh = NULL;
>> @@ -3627,11 +3846,23 @@ out2:
>>         return err ? err : allocated;
>>   }
>>
>> -void ext4_ext_truncate(struct inode *inode)
>> +/*
>> + * ext4_ext_release_blocks
>> + *
>> + * Releases the blocks in a file starting at block "start"
>> + * and ending at block "stop".  Pass EXT_MAX_BLOCK
>> + * for "stop" to just truncate the file to the
>> + * "start" block
>> + *
>> + * @inode: The inode of the file to release blocks from
>> + * @start: The starting block of the hole
>> + * @stop:  The ending block of the hole
>> + *
>> + */
>> +static void ext4_ext_release_blocks(struct inode *inode, ext4_lblk_t start, ext4_lblk_t stop)
>>   {
>>         struct address_space *mapping = inode->i_mapping;
>>         struct super_block *sb = inode->i_sb;
>> -       ext4_lblk_t last_block;
>>         handle_t *handle;
>>         int err = 0;
>>
>> @@ -3670,9 +3901,7 @@ void ext4_ext_truncate(struct inode *inode)
>>         EXT4_I(inode)->i_disksize = inode->i_size;
>>         ext4_mark_inode_dirty(handle, inode);
>>
>> -       last_block = (inode->i_size + sb->s_blocksize - 1)
>> ->>  EXT4_BLOCK_SIZE_BITS(sb);
>> -       err = ext4_ext_remove_space(inode, last_block);
>> +       err = ext4_ext_remove_space(inode, start, stop);
>>
>>         /* In a multi-transaction truncate, we only make the final
>>          * transaction synchronous.
>> @@ -3698,6 +3927,24 @@ out_stop:
>>   }
>>
>>   /*
>> + * ext4_ext_truncate
>> + *
>> + * Truncate the file to the current i_size
>> + *
>> + * @inode: The file inode
>> + */
>> +void ext4_ext_truncate(struct inode *inode)
>> +{
>> +       struct super_block *sb = inode->i_sb;
>> +       ext4_lblk_t last_block;
>> +
>> +       last_block = (inode->i_size + sb->s_blocksize - 1)
>> +>>  EXT4_BLOCK_SIZE_BITS(sb);
>> +
>> +       ext4_ext_release_blocks(inode, last_block, EXT_MAX_BLOCK);
>> +
>> +}
>> +/*
>>   * ext4_ext_convert_blocks_uninit()
>>   * Converts a range of blocks to uninitialized
>>   *
>> --
>> 1.7.1
>>
>>
>> --
>> 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
>>
> 
> 
> 

Thx for the feed back :)

Allison Henderson


^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2011-03-01 20:50 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-03-01  3:08 [Ext4 punch hole 3/5] Ext4 Punch Hole Support: Punch out extents Allison Henderson
2011-03-01  7:38 ` Dave Young
2011-03-01 20:50   ` Allison Henderson

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.