ext4: online defrag-- Defragmentation for the relevant files (-r mode) From: Akira Fujita Relevant file fragmentation could be solved by moving the files under the specified directory close together with the block containing the directory data. Signed-off-by: Akira Fujita Signed-off-by: Takashi Sato Signed-off-by: Mingming Cao --- fs/ext4/defrag.c | 51 ++++++++++++++++++++++++++++++++++++++++----------- fs/ext4/ext4.h | 4 +++- fs/ext4/inode.c | 2 +- fs/ext4/ioctl.c | 1 + 4 files changed, 45 insertions(+), 13 deletions(-) Index: linux-2.6.26-rc1/fs/ext4/defrag.c =================================================================== --- linux-2.6.26-rc1.orig/fs/ext4/defrag.c 2008-05-05 17:10:20.000000000 -0700 +++ linux-2.6.26-rc1/fs/ext4/defrag.c 2008-05-05 17:10:20.000000000 -0700 @@ -93,10 +93,23 @@ int ext4_defrag_ioctl(struct inode *inod unsigned long arg) { int err = 0; - if (!(EXT4_I(inode)->i_flags & EXT4_EXTENTS_FL)) + if (!(EXT4_I(inode)->i_flags & EXT4_EXTENTS_FL || + cmd == EXT4_IOC_FIBMAP)) return -EINVAL; - if (cmd == EXT4_IOC_DEFRAG) { + if (cmd == EXT4_IOC_FIBMAP) { + ext4_fsblk_t __user *p = (ext4_fsblk_t __user *)arg; + ext4_fsblk_t block = 0; + struct address_space *mapping = filp->f_mapping; + + if (copy_from_user(&block, (ext4_fsblk_t __user *)arg, + sizeof(block))) + return -EFAULT; + + block = ext4_bmap(mapping, block); + + return put_user(block, p); + } else if (cmd == EXT4_IOC_DEFRAG) { struct ext4_ext_defrag_data defrag; if (copy_from_user(&defrag, @@ -104,7 +117,7 @@ int ext4_defrag_ioctl(struct inode *inod sizeof(defrag))) return -EFAULT; err = ext4_defrag(filp, defrag.start_offset, - defrag.defrag_size); + defrag.defrag_size, defrag.goal); } return err; @@ -729,13 +742,14 @@ out: * @org_inode original inode * @iblock file related offset * @total_blocks contiguous blocks count + * @goal block offset for allocation * * If succeed, fuction returns count of extent we got, * otherwise returns err. */ static int ext4_defrag_alloc_blocks(struct inode *dest_inode, struct inode *org_inode, ext4_lblk_t iblock, - ext4_fsblk_t total_blocks) + ext4_fsblk_t total_blocks, ext4_fsblk_t goal) { handle_t *handle = NULL; struct ext4_ext_path *dest_path = NULL; @@ -772,7 +786,10 @@ static int ext4_defrag_alloc_blocks(stru ar.flags = EXT4_MB_HINT_DATA | EXT4_MB_HINT_RESERVED | EXT4_MB_HINT_NOPREALLOC; - ar.goal = ext4_ext_find_goal(dest_inode, dest_path, iblock); + if (goal) + ar.goal = goal; + else + ar.goal = ext4_ext_find_goal(dest_inode, dest_path, iblock); ar.logical = iblock; ar.lleft = 0; @@ -994,6 +1011,7 @@ out: * @tar_start: starting offset to allocate in blocks * @tar_blocks: the number of blocks to allocate * @iblock: file related offset + * @goal: block offset for allocaton * * This function returns the value as below: * 0(succeeded) @@ -1003,7 +1021,8 @@ out: static int ext4_defrag_new_extent_tree(struct inode *inode, struct inode *tmp_inode, struct ext4_ext_path *path, ext4_lblk_t tar_start, - ext4_lblk_t tar_blocks, ext4_lblk_t iblock) + ext4_lblk_t tar_blocks, ext4_lblk_t iblock, + ext4_fsblk_t goal) { struct ext4_extent *ext = NULL; struct ext4_extent_header *eh = NULL; @@ -1017,7 +1036,7 @@ ext4_defrag_new_extent_tree(struct inode /* Allocate contiguous blocks */ sum_tmp = ext4_defrag_alloc_blocks(tmp_inode, inode, iblock, - tar_blocks); + tar_blocks, goal); if (sum_tmp < 0) { ret = sum_tmp; goto out; @@ -1034,7 +1053,7 @@ ext4_defrag_new_extent_tree(struct inode le16_to_cpu(ext->ee_len) - 1 || last_extent) { - if (sum_org == sum_tmp) { + if ((sum_org == sum_tmp) && !goal) { /* Not improved */ ret = ext4_ext_remove_space(tmp_inode, 0); if (!ret) @@ -1065,15 +1084,17 @@ out: * @filp: pointer to file * @block_start: starting offset to defrag in blocks * @defrag_size: size of defrag in blocks + * @goal: block offset for allocation * * This function returns the number of blocks if succeeded, otherwise * returns error value. */ int ext4_defrag(struct file *filp, ext4_lblk_t block_start, - ext4_lblk_t defrag_size) + ext4_lblk_t defrag_size, ext4_fsblk_t goal) { struct inode *inode = filp->f_dentry->d_inode, *tmp_inode = NULL; + struct ext4_super_block *es = EXT4_SB(inode->i_sb)->s_es; struct ext4_ext_path *path = NULL, *holecheck_path = NULL; struct ext4_extent *ext_prev = NULL, *ext_cur = NULL, *ext_dummy = NULL; handle_t *handle; @@ -1098,6 +1119,14 @@ ext4_defrag(struct file *filp, ext4_lblk return -EINVAL; } + /* Check goal offset if goal offset was given from userspace */ + if (((0 < goal) && (ext4_blocks_count(es) < goal)) && (goal != -1)) { + printk(KERN_ERR "ext4 defrag: Invalid goal offset %llu, " + "you can set goal offset up to %llu\n", goal, + ext4_blocks_count(es) - 1); + return -EINVAL; + } + if (file_end < block_end) defrag_size -= block_end - file_end; @@ -1215,13 +1244,13 @@ ext4_defrag(struct file *filp, ext4_lblk } /* Found an isolated block */ - if (seq_extents == 1) { + if ((seq_extents == 1) && !goal) { seq_start = le32_to_cpu(ext_cur->ee_block); goto CLEANUP; } ret = ext4_defrag_new_extent_tree(inode, tmp_inode, path, - seq_start, seq_blocks, block_start); + seq_start, seq_blocks, block_start, goal); if (ret < 0) { break; Index: linux-2.6.26-rc1/fs/ext4/ext4.h =================================================================== --- linux-2.6.26-rc1.orig/fs/ext4/ext4.h 2008-05-05 17:10:20.000000000 -0700 +++ linux-2.6.26-rc1/fs/ext4/ext4.h 2008-05-05 17:10:20.000000000 -0700 @@ -298,6 +298,7 @@ struct ext4_new_group_data { #define EXT4_IOC_GETRSVSZ _IOR('f', 5, long) #define EXT4_IOC_SETRSVSZ _IOW('f', 6, long) #define EXT4_IOC_MIGRATE _IO('f', 7) +#define EXT4_IOC_FIBMAP _IOW('f', 9, ext4_fsblk_t) #define EXT4_IOC_DEFRAG _IOW('f', 10, struct ext4_ext_defrag_data) /* @@ -1013,6 +1014,7 @@ extern int ext4_htree_store_dirent(struc __u32 minor_hash, struct ext4_dir_entry_2 *dirent); extern void ext4_htree_free_dir_info(struct dir_private_info *p); +extern sector_t ext4_bmap(struct address_space *mapping, sector_t block); /* fsync.c */ extern int ext4_sync_file (struct file *, struct dentry *, int); @@ -1128,7 +1130,7 @@ extern void ext4_inode_table_set(struct extern handle_t *ext4_ext_journal_restart(handle_t *handle, int needed); /* defrag.c */ extern int ext4_defrag(struct file *filp, ext4_lblk_t block_start, - ext4_lblk_t defrag_size); + ext4_lblk_t defrag_size, ext4_fsblk_t goal); extern int ext4_defrag_ioctl(struct inode *, struct file *, unsigned int, unsigned long); Index: linux-2.6.26-rc1/fs/ext4/inode.c =================================================================== --- linux-2.6.26-rc1.orig/fs/ext4/inode.c 2008-05-05 17:10:19.000000000 -0700 +++ linux-2.6.26-rc1/fs/ext4/inode.c 2008-05-05 17:10:20.000000000 -0700 @@ -1607,7 +1607,7 @@ out: * So, if we see any bmap calls here on a modified, data-journaled file, * take extra steps to flush any blocks which might be in the cache. */ -static sector_t ext4_bmap(struct address_space *mapping, sector_t block) +sector_t ext4_bmap(struct address_space *mapping, sector_t block) { struct inode *inode = mapping->host; journal_t *journal; Index: linux-2.6.26-rc1/fs/ext4/ioctl.c =================================================================== --- linux-2.6.26-rc1.orig/fs/ext4/ioctl.c 2008-05-05 17:10:20.000000000 -0700 +++ linux-2.6.26-rc1/fs/ext4/ioctl.c 2008-05-05 17:10:20.000000000 -0700 @@ -241,6 +241,7 @@ setversion_out: return err; } + case EXT4_IOC_FIBMAP: case EXT4_IOC_DEFRAG: { return ext4_defrag_ioctl(inode, filp, cmd, arg); }