| 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
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) {
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(-)