[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <4D6D5C1C.8080204@linux.vnet.ibm.com>
Date: Tue, 01 Mar 2011 13:50:36 -0700
From: Allison Henderson <achender@...ux.vnet.ibm.com>
To: Dave Young <hidave.darkstar@...il.com>
CC: linux-ext4@...r.kernel.org
Subject: Re: [Ext4 punch hole 3/5] Ext4 Punch Hole Support: Punch out extents
On 3/1/2011 12:38 AM, Dave Young wrote:
> On Tue, Mar 1, 2011 at 11:08 AM, Allison Henderson
> <achender@...ux.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@...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@...r.kernel.org
>> More majordomo info at http://vger.kernel.org/majordomo-info.html
>>
>
>
>
Thx for the feed back :)
Allison Henderson
--
To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Powered by blists - more mailing lists