Re: [Ext4 punch hole 3/5] Ext4 Punch Hole Support: Punch out extents

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Tue, Mar 1, 2011 at 11:08 AM, Allison Henderson
<achender@xxxxxxxxxxxxxxxxxx> 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@xxxxxxxxxx>
> ---
> :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@xxxxxxxxxxxxxxx
> More majordomo info at Âhttp://vger.kernel.org/majordomo-info.html
>



-- 
Regards
dave
ÿô.nlj·Ÿ®‰­†+%ŠË±é¥Šwÿº{.nlj·¥Š{±ý¶›¡Ü}©ž²ÆzÚj:+v‰¨þø®w¥þŠàÞ¨è&¢)ß«a¶Úÿûz¹ÞúŽŠÝjÿŠwèf



[Index of Archives]     [Reiser Filesystem Development]     [Ceph FS]     [Kernel Newbies]     [Security]     [Netfilter]     [Bugtraq]     [Linux FS]     [Yosemite National Park]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Device Mapper]     [Linux Media]

  Powered by Linux