Ext4 Punch Hole Support: Convert Blocks to Uninit Exts
PATCH 1/5 Ext4 Punch Hole Support: Convert Blocks to Uninit Exts
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 <[email protected]>
---
:100644 100644 9375e7c... b78b41f... M fs/ext4/extents.c
fs/ext4/extents.c | 209 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 209 insertions(+), 0 deletions(-)
fs/ext4/extents.c +207 -0
@@ -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) {
Signature · 1 line
1.7.1