ext4: exchange the blocks between two inodes
Subsystems (2)
RFC PATCH 4/8 ext4: exchange the blocks between two inodes
ext4: online defrag-- Exchange the blocks between two inodes
From: Akira Fujita <[email protected]>
Exchange the data blocks between the temporary inode and the original inode.
Signed-off-by Akira Fujita <[email protected]>
Signed-off-by Takashi Sato <[email protected]>
--- fs/ext4/defrag.c | 240 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 240 insertions(+), 0 deletions(-)
fs/ext4/defrag.c +240 -0
@@ -134,6 +134,188 @@ int ext4_defrag_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, }/** +* ext4_defrag_merge_across_blocks - Merge extents across leaf block +* +* @handle: journal handle +* @org_inode: original inode +* @o_start: first original extent to be defraged +* @o_end: last original extent to be defraged +* @start_ext: first new extent to be merged +* @new_ext: middle of new extent to be merged +* @end_ext: last new extent to be merged +* +* This function returns 0 if succeed, otherwise returns error value. +*/ +static int +ext4_defrag_merge_across_blocks (handle_t *handle,struct inode *org_inode, +struct ext4_extent *o_start,struct ext4_extent *o_end, +struct ext4_extent *start_ext,struct ext4_extent *new_ext, +struct ext4_extent *end_ext) +{ +struct ext4_ext_path *org_path =NULL ; +ext4_lblk_t eblock =0 ; +int new_flag =0 ; +int end_flag =0 ; +int err; + +if (le16_to_cpu (start_ext->ee_len) && +le16_to_cpu (new_ext->ee_len) && +le16_to_cpu (end_ext->ee_len)) { + +if (o_start == o_end) { + +/* start_ext new_ext end_ext +* dest |---------|-----------|--------| +* org |------------------------------| +*/ + + end_flag =1 ; + }else { + +/* start_ext new_ext end_ext +* dest |---------|----------|---------| +* org |---------------|--------------| +*/ + + o_end->ee_block = end_ext->ee_block; + o_end->ee_len = end_ext->ee_len; +ext4_ext_store_pblock (o_end,ext_pblock (end_ext)); + } + + o_start->ee_len = start_ext->ee_len; + new_flag =1 ; + + }else if (le16_to_cpu (start_ext->ee_len) && +le16_to_cpu (new_ext->ee_len) && + !le16_to_cpu (end_ext->ee_len) && + o_start == o_end) { + +/* start_ext new_ext +* dest |--------------|---------------| +* org |------------------------------| +*/ + + o_start->ee_len = start_ext->ee_len; + new_flag =1 ; + + }else if (!le16_to_cpu (start_ext->ee_len) && +le16_to_cpu (new_ext->ee_len) && +le16_to_cpu (end_ext->ee_len) && + o_start == o_end) { + +/* new_ext end_ext +* dest |--------------|---------------| +* org |------------------------------| +*/ + + o_end->ee_block = end_ext->ee_block; + o_end->ee_len = end_ext->ee_len; +ext4_ext_store_pblock (o_end,ext_pblock (end_ext)); + +/* +* Set 0 to the extent block if new_ext was +* the first block. +*/ +if (!new_ext->ee_block) + eblock =0 ; +else + eblock =le32_to_cpu (new_ext->ee_block); + + new_flag =1 ; + }else { +printk (KERN_ERR "ext4 defrag: Unexpected merge case\n" ); +return -EIO ; + } + +if (new_flag) { + org_path =ext4_ext_find_extent (org_inode, eblock,NULL ); +if (IS_ERR (org_path)) { + err =PTR_ERR (org_path); + org_path =NULL ; + goto out; + } + err =ext4_ext_insert_extent (handle, org_inode, + org_path, new_ext); +if (err) + goto out; + } + +if (end_flag) { + org_path =ext4_ext_find_extent (org_inode, +le32_to_cpu (end_ext->ee_block) -1 , org_path); +if (IS_ERR (org_path)) { + err =PTR_ERR (org_path); + org_path =NULL ; + goto out; + } + err =ext4_ext_insert_extent (handle, org_inode, + org_path, end_ext); +if (err) + goto out; + } +out: +if (org_path) { +ext4_ext_drop_refs (org_path); +kfree (org_path); + } + +return err; + +} + +/** +* ext4_defrag_merge_inside_block - Merge new extent to the extent block +* +* @o_start: first original extent to be merged +* @o_end: last original extent to be merged +* @start_ext: first new extent to be merged +* @new_ext: middle of new extent to be merged +* @end_ext: last new extent to be merged +* @eh: extent header of target leaf block +* @replaced: the number of blocks which will be replaced with new_ext +* @range_to_move: used to decide how to merge +* +* This function always returns 0. +*/ +static int +ext4_defrag_merge_inside_block (struct ext4_extent *o_start, +struct ext4_extent *o_end,struct ext4_extent *start_ext, +struct ext4_extent *new_ext,struct ext4_extent *end_ext, +struct ext4_extent_header *eh,ext4_fsblk_t replaced, +int range_to_move) +{ +int i =0 ; +unsigned len; + +/* Move the existing extents */ +if (range_to_move && o_end <EXT_LAST_EXTENT (eh)) { + len = (unsigned long )(EXT_LAST_EXTENT (eh) +1 ) - + (unsigned long )(o_end +1 ); +memmove (o_end +1 + range_to_move, o_end +1 , len); + } + +/* Insert start entry */ +if (le16_to_cpu (start_ext->ee_len)) + o_start[i++].ee_len = start_ext->ee_len; + +/* Insert new entry */ +if (le16_to_cpu (new_ext->ee_len)) { + o_start[i].ee_block = new_ext->ee_block; + o_start[i].ee_len =cpu_to_le16 (replaced); +ext4_ext_store_pblock (&o_start[i++],ext_pblock (new_ext)); + } + +/* Insert end entry */ +if (end_ext->ee_len) + o_start[i] = *end_ext; + +/* Increment the total entries counter on the extent block */ +le16_add_cpu (&eh->eh_entries, range_to_move); + +return 0 ; +} + +/** * ext4_defrag_merge_extents - Merge new extent * * @handle: journal handle
@@ -155,7 +337,65 @@ ext4_defrag_merge_extents(handle_t *handle, struct inode *org_inode, struct ext4_extent *start_ext, struct ext4_extent *new_ext, struct ext4_extent *end_ext, ext4_fsblk_t replaced) { + struct ext4_extent_header *eh; +unsigned need_slots, slots_range; +int range_to_move, depth, ret; + +/* +* The extents need to be inserted +* start_extent + new_extent + end_extent. +*/ + need_slots = (le16_to_cpu (start_ext->ee_len) ?1 :0 ) + + (le16_to_cpu (end_ext->ee_len) ?1 :0 ) + + (le16_to_cpu (new_ext->ee_len) ?1 :0 ); + +/* The number of slots between start and end */ + slots_range = ((unsigned long )(o_end +1 ) - (unsigned long )o_start +1 ) + /sizeof (struct ext4_extent ); + +/* Range to move the end of extent */ + range_to_move = need_slots - slots_range; + depth = org_path->p_depth; + org_path += depth; + eh = org_path->p_hdr; + +if (depth) { +/* Register to journal */ + ret =ext4_journal_get_write_access (handle, org_path->p_bh); +if (ret) +return ret; + } + +/* Expansion */ +if (range_to_move >0 && + (range_to_move >le16_to_cpu (eh->eh_max) + -le16_to_cpu (eh->eh_entries))) { + + ret =ext4_defrag_merge_across_blocks (handle, org_inode, + o_start, o_end, start_ext, new_ext, + end_ext); +if (ret <0 ) +return ret; + }else { + ret =ext4_defrag_merge_inside_block (o_start, o_end, + start_ext, new_ext, end_ext, eh, + replaced, range_to_move); +if (ret <0 ) +return ret; + } + +if (depth) { + ret =ext4_journal_dirty_metadata (handle, org_path->p_bh); +if (ret) +return ret; + }else { + ret =ext4_mark_inode_dirty (handle, org_inode); +if (ret <0 ) +return ret; + } +return 0 ; + }/**