Search contiguous free blocks with Alex's mutil-block allocation and allocate them for the temporary inode. This patch applies on top of Alex's patches. "[RFC] delayed allocation, mballoc, etc" http://marc.theaimsgroup.com/?l=linux-ext4&m=116493228301966&w=2 Signed-off-by: Takashi Sato <t-sato@xxxxxxxxxxxxx> Signed-off-by: Akira Fujita <a-fujita@xxxxxxxxxxxxx> --- diff -Nrup -X linux-2.6.19-rc6-Alex/Documentation/dontdiff linux-2.6.19-rc6-Alex/fs/ext4/extents.c linux-2.6.19-rc6-1-alloc/fs/ext4/extents.c --- linux-2.6.19-rc6-Alex/fs/ext4/extents.c 2007-06-19 20:50:56.000000000 +0900 +++ linux-2.6.19-rc6-1-alloc/fs/ext4/extents.c 2007-06-20 10:54:11.000000000 +0900 @@ -2335,6 +2335,713 @@ int ext4_ext_calc_metadata_amount(struct return num; } +/* + * this structure is used to gather extents from the tree via ioctl + */ +struct ext4_extent_buf { + ext4_fsblk_t start; + int buflen; + void *buffer; + void *cur; + int err; +}; + +/* + * this structure is used to collect stats info about the tree + */ +struct ext4_extent_tree_stats { + int depth; + int extents_num; + int leaf_num; +}; + +static int +ext4_ext_store_extent_cb(struct inode *inode, + struct ext4_ext_path *path, + struct ext4_ext_cache *newex, + struct ext4_extent_buf *buf) +{ + + if (newex->ec_type != EXT4_EXT_CACHE_EXTENT) + return EXT_CONTINUE; + + if (buf->err < 0) + return EXT_BREAK; + if (buf->cur - buf->buffer + sizeof(*newex) > buf->buflen) + return EXT_BREAK; + + if (!copy_to_user(buf->cur, newex, sizeof(*newex))) { + buf->err++; + buf->cur += sizeof(*newex); + } else { + buf->err = -EFAULT; + return EXT_BREAK; + } + return EXT_CONTINUE; +} + +static int +ext4_ext_collect_stats_cb(struct inode *inode, + struct ext4_ext_path *path, + struct ext4_ext_cache *ex, + struct ext4_extent_tree_stats *buf) +{ + int depth; + + if (ex->ec_type != EXT4_EXT_CACHE_EXTENT) + return EXT_CONTINUE; + + depth = ext_depth(inode); + buf->extents_num++; + if (path[depth].p_ext == EXT_FIRST_EXTENT(path[depth].p_hdr)) + buf->leaf_num++; + return EXT_CONTINUE; +} + +/** + * ext4_ext_next_extent - search for next extent and set it to "extent" + * @inode: inode of the the original file + * @path: this will obtain data for next extent + * @extent: pointer to next extent we have just gotten + * + * This function returns 0 or 1(last_entry) if succeeded, otherwise + * returns -EIO + */ +static int +ext4_ext_next_extent(struct inode *inode, + struct ext4_ext_path *path, + struct ext4_extent **extent) +{ + int ppos; + int leaf_ppos = path->p_depth; + + ppos = leaf_ppos; + if (EXT_LAST_EXTENT(path[ppos].p_hdr) > path[ppos].p_ext) { + /* leaf block */ + *extent = ++path[ppos].p_ext; + return 0; + } + + while (--ppos >= 0) { + if (EXT_LAST_INDEX(path[ppos].p_hdr) > + path[ppos].p_idx) { + int cur_ppos = ppos; + + /* index block */ + path[ppos].p_idx++; + path[ppos].p_block = + idx_pblock(path[ppos].p_idx); + if (path[ppos+1].p_bh) + brelse(path[ppos+1].p_bh); + path[ppos+1].p_bh = + sb_bread(inode->i_sb, path[ppos].p_block); + if (!path[ppos+1].p_bh) + return -EIO; + path[ppos+1].p_hdr = + ext_block_hdr(path[ppos+1].p_bh); + + /* halfway index block */ + while (++cur_ppos < leaf_ppos) { + path[cur_ppos].p_idx = + EXT_FIRST_INDEX(path[cur_ppos].p_hdr); + path[cur_ppos].p_block = + idx_pblock(path[cur_ppos].p_idx); + if (path[cur_ppos+1].p_bh) + brelse(path[cur_ppos+1].p_bh); + path[cur_ppos+1].p_bh = sb_bread(inode->i_sb, + path[cur_ppos].p_block); + if (!path[cur_ppos+1].p_bh) + return -EIO; + path[cur_ppos+1].p_hdr = + ext_block_hdr(path[cur_ppos+1].p_bh); + } + + /* leaf block */ + path[leaf_ppos].p_ext = *extent = + EXT_FIRST_EXTENT(path[leaf_ppos].p_hdr); + return 0; + } + } + /* last_extent */ + return 1; +} + +int ext4_ext_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + int err = 0; + if (!(EXT4_I(inode)->i_flags & EXT4_EXTENTS_FL)) + return -EINVAL; + + if (cmd == EXT4_IOC_GET_EXTENTS) { + struct ext4_extent_buf buf; + + if (copy_from_user(&buf, (void *) arg, sizeof(buf))) + return -EFAULT; + + buf.cur = buf.buffer; + buf.err = 0; + mutex_lock(&EXT4_I(inode)->truncate_mutex); + err = ext4_ext_walk_space(inode, buf.start, EXT_MAX_BLOCK, + (void *)ext4_ext_store_extent_cb, &buf); + mutex_unlock(&EXT4_I(inode)->truncate_mutex); + if (err == 0) + err = buf.err; + } else if (cmd == EXT4_IOC_GET_TREE_STATS) { + struct ext4_extent_tree_stats buf; + + mutex_lock(&EXT4_I(inode)->truncate_mutex); + buf.depth = ext_depth(inode); + buf.extents_num = 0; + buf.leaf_num = 0; + err = ext4_ext_walk_space(inode, 0, EXT_MAX_BLOCK, + (void *)ext4_ext_collect_stats_cb, &buf); + mutex_unlock(&EXT4_I(inode)->truncate_mutex); + if (!err) + err = copy_to_user((void *) arg, &buf, sizeof(buf)); + } else if (cmd == EXT4_IOC_GET_TREE_DEPTH) { + mutex_lock(&EXT4_I(inode)->truncate_mutex); + err = ext_depth(inode); + mutex_unlock(&EXT4_I(inode)->truncate_mutex); + } else 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; + + lock_kernel(); + block = ext4_bmap(mapping, block); + unlock_kernel(); + + return put_user(block, p); + } else if (cmd == EXT4_IOC_DEFRAG) { + struct ext4_ext_defrag_data defrag; + + if (copy_from_user(&defrag, + (struct ext4_ext_defrag_data __user *)arg, + sizeof(defrag))) + return -EFAULT; + err = ext4_ext_defrag(filp, defrag.start_offset, + defrag.defrag_size, defrag.goal, defrag.flag, + &defrag.ext); + } + + return err; +} + +/** + * ext4_ext_alloc_blocks - allocate contiguous blocks to temporary inode + * @dest_inode temporary inode for multiple block allocation + * @org_inode original inode + * @iblock file related offset + * @total_blocks contiguous blocks count + * @goal block offset for allocation + * @phase phase of create free space mode + * + * If succeed, fuction returns count of extent we got, + * otherwise returns err. + */ +static int ext4_ext_alloc_blocks(struct inode *dest_inode, + struct inode *org_inode, ext4_fsblk_t iblock, + ext4_fsblk_t total_blocks, ext4_fsblk_t goal, int phase) +{ + handle_t *handle = NULL; + struct ext4_ext_path *dest_path = NULL; + struct ext4_ext_path *org_path = NULL; + struct ext4_extent newex; + struct ext4_allocation_request ar; + ext4_fsblk_t newblock = 0; + ext4_fsblk_t rest = total_blocks; + ext4_fsblk_t alloc_total = 0; + unsigned long dest_grp_no, org_grp_no, org_len, goal_grp_no; + ext4_grpblk_t dest_blk_off, org_blk_off, goal_blk_off; + int org_depth = ext_depth(org_inode); + int metadata = 1; + int count = 0; + int credits = 0; + int err = 0; + int err2 = 0; + + ar.len = total_blocks; + org_len = ar.len; + + /* Calculate group nubmer of org_inode block */ + if (phase == DEFRAG_RESERVE_BLOCKS_SECOND) { + org_path = ext4_ext_find_extent(org_inode, iblock, org_path); + if (IS_ERR(org_path)) { + err = PTR_ERR(org_path); + org_path = NULL; + goto out2; + } + ext4_get_group_no_and_offset(org_inode->i_sb, + ext_pblock(org_path[org_depth].p_ext), + &org_grp_no, &org_blk_off); + ar.excepted_group = org_grp_no; + } else { + ar.excepted_group = -1; + } + + /* Find first extent. */ + dest_path = ext4_ext_find_extent(dest_inode, iblock, dest_path); + if (IS_ERR(dest_path)) { + err = PTR_ERR(dest_path); + dest_path = NULL; + goto out2; + } + + ar.inode = dest_inode; + ar.flags = EXT4_MB_HINT_DATA | EXT4_MB_HINT_RESERVED + | EXT4_MB_HINT_NOPREALLOC; + + if (goal) + ar.goal = goal; + else + ar.goal = ext4_ext_find_goal(dest_inode, dest_path, iblock); + + ar.logical = iblock; + ar.lleft = 0; + ar.pleft = 0; + ar.lright = 0; + ar.pright = 0; + + handle = ext4_journal_start(dest_inode, credits); + if (IS_ERR(handle)) { + err = PTR_ERR(handle); + goto out2; + } + + while (alloc_total != total_blocks) { + credits = ext4_ext_calc_credits_for_insert(dest_inode, + dest_path); + handle = ext4_ext_journal_restart(handle, + credits + EXT4_TRANS_META_BLOCKS); + + if (IS_ERR(handle)) + return PTR_ERR(handle); + + newblock = ext4_mb_new_blocks(handle, &ar, &err); + + if (err) { + /* Faild to get contiguous blocks*/ + goto out; + } else if ((ar.len != org_len) && + (phase == DEFRAG_RESERVE_BLOCKS_FIRST)) { + ext4_free_blocks(handle, org_inode, newblock, + ar.len, metadata); + err = -ENOSPC; + goto out; + } else { + alloc_total += ar.len; + ext4_get_group_no_and_offset(dest_inode->i_sb, + goal, &goal_grp_no, &goal_blk_off); + ext4_get_group_no_and_offset(dest_inode->i_sb, + newblock, &dest_grp_no, &dest_blk_off); + /* We can't allocate at same block group */ + switch (phase) { + case DEFRAG_RESERVE_BLOCKS_SECOND: + if (dest_grp_no == org_grp_no) { + printk(KERN_ERR "defrag: Can't allocate" + " in same block group\n"); + ext4_free_blocks(handle, org_inode, + newblock, ar.len, metadata); + err = -ENOSPC; + goto out; + } + break; + case DEFRAG_FIXED_BLOCKS_MODE: + if (dest_grp_no != goal_grp_no + || alloc_total != total_blocks) { + printk(KERN_ERR "defrag: Already used" + " the specified blocks\n"); + ext4_free_blocks(handle, org_inode, + newblock, ar.len, metadata); + err = -EIO; + goto out; + } + break; + } + + newex.ee_block = cpu_to_le32(alloc_total -ar.len); + ext4_ext_store_pblock(&newex, newblock); + newex.ee_len = cpu_to_le16(ar.len); + + if (!phase) + ar.goal = newblock + ar.len; + rest = rest - ar.len; + ar.len = rest; + + err = ext4_ext_insert_extent(handle, dest_inode, + dest_path, &newex); + if (!err) { + count++; + } else { + ext4_free_blocks(handle, org_inode, + newblock, ar.len, metadata); + goto out; + } + } + } + +out: + /* Faild case: We have to remove halfway blocks */ + if (err) { + err2 = ext4_ext_remove_space(dest_inode, 0); + ext4_release_blocks(dest_inode->i_sb, org_len); + } + + /* Successful case: return extents count */ + if (dest_path) { + ext4_ext_drop_refs(dest_path); + kfree(dest_path); + } + if (org_path) { + ext4_ext_drop_refs(org_path); + kfree(org_path); + } +out2: + ext4_journal_stop(handle); + + if (!err && !err2) + return count; + else if (!err2) + return err; + else + return err2; +} + +/** + * ext4_ext_new_extent_tree - allocate contiguous blocks + * @inode: inode of the original file + * @tmp_inode: inode of the temporary file + * @path: the structure holding some info about + * original extent tree + * @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 + * @flag: phase of create free space mode + * + * This function returns the value as below: + * 0(succeeded) + * 1(not improved) + * negative value(error) + */ +static int +ext4_ext_new_extent_tree(struct inode *inode, struct inode *tmp_inode, + struct ext4_ext_path *path, ext4_fsblk_t tar_start, + ext4_fsblk_t tar_blocks, ext4_fsblk_t iblock, + ext4_fsblk_t goal, int flag) +{ + struct ext4_extent *ext = NULL; + struct ext4_extent_header *eh = NULL; + ext4_fsblk_t tar_end = tar_start + tar_blocks - 1; + int sum_org = 0, sum_tmp = 0; + int ret = 0, depth; + int last_extent = 0; + + eh = ext_inode_hdr(tmp_inode); + eh->eh_depth = 0; + + /* allocate contiguous blocks */ + if ((sum_tmp = ext4_ext_alloc_blocks(tmp_inode, inode, iblock, + tar_blocks, goal, flag)) < 0) { + ret = sum_tmp; + goto ERR; + } + + depth = ext_depth(inode); + ext = path[depth].p_ext; + while (1) { + if (!last_extent) + ++sum_org; + + if (tar_end <= le32_to_cpu(ext->ee_block) + + le32_to_cpu(ext->ee_len) - 1 || + last_extent) { + + if ((sum_org == sum_tmp) && !goal) { + /* not improved */ + if (!(ret = + ext4_ext_remove_space(tmp_inode, 0))) + ret = 1; + } else if (sum_org < sum_tmp && + flag != DEFRAG_RESERVE_BLOCKS_SECOND) { + /* fragment increased */ + if (!(ret = + ext4_ext_remove_space(tmp_inode, 0))) + ret = -ENOSPC; + printk("defrag failed due to no space\n"); + } + break; + } + if ((last_extent = + ext4_ext_next_extent(tmp_inode, + path, &ext)) < 0) { + ret = last_extent; + break; + } + } +ERR: + return ret; +} + +/** + * ext4_ext_defrag - defrag whole file + * @filp: pointer to file + * @from: starting offset to defrag in blocks + * @defrag_size: size of defrag in blocks + * @goal: block offset for allocation + * @flag: phase of create free space mode + * @ext: extent to move(only -f) + * + * This function returns the number of blocks if succeeded, otherwise + * returns error value + */ +int +ext4_ext_defrag(struct file *filp, ext4_fsblk_t block_start, + ext4_fsblk_t defrag_size, ext4_fsblk_t goal, + int flag, struct ext4_extent_data *ext) +{ + 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; + ext4_fsblk_t block_end = block_start + defrag_size - 1; + ext4_fsblk_t seq_blocks = 0, seq_start = 0; + ext4_fsblk_t add_blocks = 0; + ext4_fsblk_t file_end = (inode->i_size - 1) >> inode->i_blkbits; + pgoff_t page_offset = 0; + pgoff_t dest_offset = 0; + pgoff_t seq_end_page = 0; + int ret = 0, depth = 0, last_extent = 0, seq_extents = 0; + + /* check goal offset */ + if (((0 < goal) && (ext4_blocks_count(es) < goal)) && (goal != -1)) { + printk(KERN_ERR "defrag: incorrect goal number %llu, " + "you can set goal until %llu\n", goal, + ext4_blocks_count(es)); + ret = -EINVAL; + goto ERR1; + } + + /* Setup for fixed blocks mode */ + if (ext->len) { + if (ext->len < defrag_size) { + printk("Cannot defrag due to the insufficient" + " specified free blocks\n"); + return -EINVAL; + } + flag = DEFRAG_FIXED_BLOCKS_MODE; + goal = ext->start; + } + + if (file_end < block_end) + defrag_size -= block_end - file_end; + + mutex_lock(&inode->i_mutex); + mutex_lock(&EXT4_I(inode)->truncate_mutex); + + path = ext4_ext_find_extent(inode, block_start, NULL); + if (IS_ERR(path)) { + ret = PTR_ERR(path); + path = NULL; + goto ERR2; + } + + /* get path structure to check hole */ + holecheck_path = ext4_ext_find_extent(inode, block_start, NULL); + if (IS_ERR(holecheck_path)) { + ret = PTR_ERR(holecheck_path); + holecheck_path = NULL; + goto ERR2; + } + + depth = ext_depth(inode); + ext_cur = holecheck_path[depth].p_ext; + if (ext_cur == NULL) + goto ERR2; + + /* + * if block_start was within the hole, get proper extent whose ee_block + * is beyond block_start + */ + if (ext_cur->ee_block + ext_cur->ee_len - 1 < block_start) { + if ((last_extent = + ext4_ext_next_extent(inode, holecheck_path, + &ext_cur)) < 0) { + ret = last_extent; + goto ERR2; + } + if ((last_extent = + ext4_ext_next_extent(inode, path, + &ext_dummy)) < 0) { + ret = last_extent; + goto ERR2; + } + } + seq_extents = 1; + seq_start = ext_cur->ee_block; + + /* no blocks existed within designated range */ + if (ext_cur->ee_block > block_end) { + printk("nothing done due to the lack of contiguous blocks\n"); + goto ERR2; + } + + /* adjust start blocks */ + add_blocks = min((ext4_fsblk_t)(ext_cur->ee_block + + ext_cur->ee_len), block_end + 1) - + max((ext4_fsblk_t)ext_cur->ee_block, block_start); + + while (!last_extent && ext_cur->ee_block <= block_end) { + seq_blocks += add_blocks; + + handle = ext4_journal_start(inode, + EXT4_DATA_TRANS_BLOCKS(inode->i_sb) + + EXT4_INDEX_EXTRA_TRANS_BLOCKS + 3 + + 2 * EXT4_QUOTA_INIT_BLOCKS(inode->i_sb) + 1); + if (IS_ERR(handle)) { + ret = PTR_ERR(handle); + goto ERR1; + } + tmp_inode = ext4_new_inode(handle, + inode->i_sb->s_root->d_inode, S_IFREG); + if (IS_ERR(tmp_inode)) { + ret = -ENOMEM; + ext4_journal_stop(handle); + tmp_inode = NULL; + goto ERR1; + } + + i_size_write(tmp_inode, i_size_read(inode)); + tmp_inode->i_nlink = 0; + ext4_ext_tree_init(handle, tmp_inode); + ext4_orphan_add(handle, tmp_inode); + ext4_journal_stop(handle); + + /* adjust tail blocks */ + if (seq_start + seq_blocks - 1 > block_end) { + seq_blocks = block_end - seq_start + 1; + } + + ext_prev = ext_cur; + if ((last_extent = + ext4_ext_next_extent(inode, holecheck_path, + &ext_cur)) < 0) { + ret = last_extent; + break; + } + if (!last_extent) + seq_extents++; + add_blocks = ext_cur->ee_len; + + /* found hole or reached the tail of either a designated range + * or the file + */ + if ((ext_prev->ee_block + ext_prev->ee_len == + ext_cur->ee_block && + block_end >= ext_cur->ee_block && + !last_extent)) { + if (tmp_inode) { + iput(tmp_inode); + tmp_inode = NULL; + } + continue; + } + + /* found an isolated block */ + if ((seq_extents == 1) && !goal) { + seq_start = ext_cur->ee_block; + goto CLEANUP; + } + + ret = ext4_ext_new_extent_tree(inode, tmp_inode, path, + seq_start, seq_blocks, block_start, goal, flag); + + if (ret < 0) { + break; + } else if ((ret == 1) && (!goal || (goal && !flag))) { + ret = 0; + seq_start = ext_cur->ee_block; + goto CLEANUP; + } + + page_offset = seq_start >> + (PAGE_CACHE_SHIFT - inode->i_blkbits); + seq_end_page = (seq_start + seq_blocks - 1) >> + (PAGE_CACHE_SHIFT - inode->i_blkbits); + + dest_offset = 0; + seq_start = ext_cur->ee_block; + + /* Discard all preallocations. + * This is provisional solution. + * When true ext4_mb_return_to_preallocation() is + * implemented, this will be removed. + */ + ext4_mb_discard_inode_preallocations(inode); + + while (page_offset <= seq_end_page) { + /* replace original branches for new branches */ + if ((ret = ext4_ext_defrag_partial(tmp_inode, filp, + page_offset, dest_offset, flag)) < 0) + goto ERR2; + + page_offset++; + dest_offset++; + } + + holecheck_path = + ext4_ext_find_extent(inode, seq_start, holecheck_path); + if (IS_ERR(holecheck_path)) { + ret = PTR_ERR(holecheck_path); + holecheck_path = NULL; + break; + } + depth = holecheck_path->p_depth; + +CLEANUP: + path = ext4_ext_find_extent(inode, seq_start, path); + if (IS_ERR(path)) { + ret = PTR_ERR(path); + path = NULL; + break; + } + + ext_cur = holecheck_path[depth].p_ext; + add_blocks = ext_cur->ee_len; + seq_blocks = 0; + dest_offset = 0; + seq_extents = 1; + + if (tmp_inode) { + iput(tmp_inode); + tmp_inode = NULL; + } + } +ERR2: + if (path) { + ext4_ext_drop_refs(path); + kfree(path); + } + if (holecheck_path) { + ext4_ext_drop_refs(holecheck_path); + kfree(holecheck_path); + } +ERR1: + mutex_unlock(&EXT4_I(inode)->truncate_mutex); + mutex_unlock(&inode->i_mutex); + + if (tmp_inode) + iput(tmp_inode); + + return (ret ? ret : defrag_size); +} + EXPORT_SYMBOL(ext4_mark_inode_dirty); EXPORT_SYMBOL(ext4_ext_invalidate_cache); EXPORT_SYMBOL(ext4_ext_insert_extent); diff -Nrup -X linux-2.6.19-rc6-Alex/Documentation/dontdiff linux-2.6.19-rc6-Alex/fs/ext4/inode.c linux-2.6.19-rc6-1-alloc/fs/ext4/inode.c --- linux-2.6.19-rc6-Alex/fs/ext4/inode.c 2007-06-19 20:50:56.000000000 +0900 +++ linux-2.6.19-rc6-1-alloc/fs/ext4/inode.c 2007-06-19 20:10:13.000000000 +0900 @@ -1305,7 +1305,7 @@ static int ext4_journalled_commit_write( * 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; diff -Nrup -X linux-2.6.19-rc6-Alex/Documentation/dontdiff linux-2.6.19-rc6-Alex/fs/ext4/ioctl.c linux-2.6.19-rc6-1-alloc/fs/ext4/ioctl.c --- linux-2.6.19-rc6-Alex/fs/ext4/ioctl.c 2007-06-19 20:50:56.000000000 +0900 +++ linux-2.6.19-rc6-1-alloc/fs/ext4/ioctl.c 2007-06-19 20:10:13.000000000 +0900 @@ -249,6 +249,19 @@ flags_err: return err; } + case EXT4_IOC_GET_EXTENTS: + case EXT4_IOC_GET_TREE_STATS: + case EXT4_IOC_GET_TREE_DEPTH: + case EXT4_IOC_FIBMAP: + case EXT4_IOC_DEFRAG: + case EXT4_IOC_GROUP_INFO: + case EXT4_IOC_FREE_BLOCKS_INFO: + case EXT4_IOC_EXTENTS_INFO: + case EXT4_IOC_RESERVE_BLOCK: + case EXT4_IOC_MOVE_VICTIM: + case EXT4_IOC_BLOCK_RELEASE: { + return ext4_ext_ioctl(inode, filp, cmd, arg); + } case EXT4_IOC_GET_BUDDY: { unsigned char *buddy = NULL, *bitmap = NULL; struct super_block *sb = inode->i_sb; diff -Nrup -X linux-2.6.19-rc6-Alex/Documentation/dontdiff linux-2.6.19-rc6-Alex/fs/ext4/mballoc.c linux-2.6.19-rc6-1-alloc/fs/ext4/mballoc.c --- linux-2.6.19-rc6-Alex/fs/ext4/mballoc.c 2007-06-19 20:50:56.000000000 +0900 +++ linux-2.6.19-rc6-1-alloc/fs/ext4/mballoc.c 2007-06-20 07:28:34.000000000 +0900 @@ -252,6 +252,7 @@ struct ext4_allocation_context { struct page *ac_bitmap_page; struct page *ac_buddy_page; struct ext4_prealloc_space *ac_pa; + long long ac_excepted_group; }; #define AC_STATUS_CONTINUE 1 @@ -1581,6 +1582,11 @@ repeat: if (group == EXT4_SB(sb)->s_groups_count) group = 0; + if (ac->ac_excepted_group != -1 && + group == ac->ac_excepted_group) { + continue; + } + /* quick check to skip empty groups */ grp = EXT4_GROUP_INFO(ac->ac_sb, group); if (grp->bb_free == 0) @@ -3630,6 +3636,7 @@ int ext4_mb_initialize_context(struct ex ac->ac_pa = NULL; ac->ac_bitmap_page = NULL; ac->ac_buddy_page = NULL; + ac->ac_excepted_group = ar->excepted_group; if (len == 1 && sbi->s_stripe) { /* looks like a metadata, let's use a dirty hack for raid5 diff -Nrup -X linux-2.6.19-rc6-Alex/Documentation/dontdiff linux-2.6.19-rc6-Alex/include/linux/ext4_fs.h linux-2.6.19-rc6-1-alloc/include/linux/ext4_fs.h --- linux-2.6.19-rc6-Alex/include/linux/ext4_fs.h 2007-06-19 20:50:56.000000000 +0900 +++ linux-2.6.19-rc6-1-alloc/include/linux/ext4_fs.h 2007-06-20 07:54:58.000000000 +0900 @@ -83,6 +83,7 @@ struct ext4_allocation_request { ext4_fsblk_t pright; /* phys. block for ^^^ */ unsigned long len; /* how many blocks we want to allocate */ unsigned long flags; /* flags. see above EXT4_MB_HINT_* */ + long long excepted_group; }; /* @@ -279,6 +280,11 @@ struct ext4_get_buddy_request { #define EXT4_IOC_GETRSVSZ _IOR('f', 5, long) #define EXT4_IOC_SETRSVSZ _IOW('f', 6, long) #define EXT4_IOC_GET_BUDDY _IOR('f', 98, struct ext4_get_buddy_request) +#define EXT4_IOC_GET_EXTENTS _IOR('f', 7, long) +#define EXT4_IOC_GET_TREE_DEPTH _IOR('f', 8, long) +#define EXT4_IOC_GET_TREE_STATS _IOR('f', 9, long) +#define EXT4_IOC_FIBMAP _IOW('f', 9, ext4_fsblk_t) +#define EXT4_IOC_DEFRAG _IOW('f', 10, struct ext4_ext_defrag_data) /* * ioctl commands in 32 bit emulation @@ -296,6 +302,23 @@ struct ext4_get_buddy_request { #define EXT4_IOC32_GETVERSION_OLD FS_IOC32_GETVERSION #define EXT4_IOC32_SETVERSION_OLD FS_IOC32_SETVERSION +/* Used for defrag */ + +struct ext4_extent_data { + unsigned long long block; /* start logical block number */ + ext4_fsblk_t start; /* start physical block number */ + int len; /* blocks count */ +}; + +struct ext4_ext_defrag_data { + ext4_fsblk_t start_offset; /* start offset to defrag in blocks */ + ext4_fsblk_t defrag_size; /* size of defrag in blocks */ + ext4_fsblk_t goal; /* block offset for allocation */ + int flag; /* free space mode flag */ + struct ext4_extent_data ext; +}; + +#define EXT4_TRANS_META_BLOCKS 4 /* bitmap + group desc + sb + inode */ /* * Mount options @@ -930,6 +953,7 @@ struct buffer_head * ext4_bread (handle_ int ext4_get_blocks_handle(handle_t *handle, struct inode *inode, sector_t iblock, unsigned long maxblocks, struct buffer_head *bh_result, int create, int extend_disksize); +sector_t ext4_bmap(struct address_space *mapping, sector_t block); extern void ext4_read_inode (struct inode *); extern int ext4_write_inode (struct inode *, int); @@ -951,6 +975,8 @@ extern int ext4_block_truncate_page(hand extern int ext4_ioctl (struct inode *, struct file *, unsigned int, unsigned long); extern long ext4_compat_ioctl (struct file *, unsigned int, unsigned long); +extern int ext4_ext_defrag(struct file *, ext4_fsblk_t, ext4_fsblk_t, + ext4_fsblk_t, int, struct ext4_extent_data *); /* namei.c */ extern int ext4_orphan_add(handle_t *, struct inode *); @@ -1063,6 +1089,8 @@ extern int ext4_ext_get_blocks(handle_t extern void ext4_ext_truncate(struct inode *, struct page *); extern void ext4_ext_init(struct super_block *); extern void ext4_ext_release(struct super_block *); +extern int ext4_ext_ioctl(struct inode *, struct file *, unsigned int, + unsigned long); static inline int ext4_get_blocks_wrap(handle_t *handle, struct inode *inode, sector_t block, unsigned long max_blocks, struct buffer_head *bh, - To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html