--- linux-2.6.9-full/include/linux/ext3_fs.h 2007-03-23 15:57:00.000000000 +0300 +++ linux-2.6.9-full/include/linux/ext3_fs.h 2007-02-16 17:16:23.000000000 +0300 @@ -850,7 +850,7 @@ extern struct inode_operations ext3_fast /* extents.c */ extern int ext3_ext_writepage_trans_blocks(struct inode *, int); -extern int ext3_ext_get_block(handle_t *, struct inode *, long, +extern int ext3_ext_get_block(handle_t *, struct inode *, long, int, struct buffer_head *, int, int); extern void ext3_ext_truncate(struct inode *, struct page *); extern void ext3_ext_init(struct super_block *); --- linux-2.6.9-full/fs/ext3/extents.c 2007-03-23 15:57:00.000000000 +0300 +++ linux-2.6.9-full/fs/ext3/extents.c 2007-02-22 17:45:05.000000000 +0300 @@ -2031,7 +2168,8 @@ void ext3_init_tree_desc(struct ext3_ext } int ext3_ext_get_block(handle_t *handle, struct inode *inode, - long iblock, struct buffer_head *bh_result, + long iblock, int max_blocks, + struct buffer_head *bh_result, int create, int extend_disksize) { struct ext3_ext_path *path = NULL; @@ -2039,6 +2177,11 @@ int ext3_ext_get_block(handle_t *handle, struct ext3_extent *ex; int goal, newblock, err = 0, depth; struct ext3_extents_tree tree; + unsigned long next; + int allocated = 0; + + /* until we have multiblock allocation */ + max_blocks = 1; clear_buffer_new(bh_result); ext3_init_tree_desc(&tree, inode); @@ -2058,6 +2201,9 @@ int ext3_ext_get_block(handle_t *handle, } else if (goal == EXT3_EXT_CACHE_EXTENT) { /* block is already allocated */ newblock = iblock - newex.ee_block + newex.ee_start; + /* number of remaining blocks in the extent */ + EXT_ASSERT(iblock >= newex.ee_block); + allocated = newex.ee_len - (iblock - newex.ee_block); goto out; } else { EXT_ASSERT(0); @@ -2085,6 +2231,8 @@ int ext3_ext_get_block(handle_t *handle, /* if found exent covers block, simple return it */ if (iblock >= ex->ee_block && iblock < ex->ee_block + ex->ee_len) { newblock = iblock - ex->ee_block + ex->ee_start; + /* number of remaining blocks in the extent */ + allocated = ex->ee_len - (iblock - ex->ee_block); ext_debug(&tree, "%d fit into %d:%d -> %d\n", (int) iblock, ex->ee_block, ex->ee_len, newblock); @@ -2105,6 +2253,15 @@ int ext3_ext_get_block(handle_t *handle, goto out2; } + /* find next allocated block so that we know how many + * blocks we can allocate without ovelapping next extent */ + EXT_ASSERT(iblock >= ex->ee_block + ex->ee_len); + next = ext3_ext_next_allocated_block(path); + EXT_ASSERT(next > iblock); + allocated = next - iblock; + if (allocated > max_blocks) + allocated = max_blocks; + /* allocate new block */ goal = ext3_ext_find_goal(inode, path, iblock); newblock = ext3_new_block(handle, inode, goal, &err); @@ -2119,8 +2276,11 @@ int ext3_ext_get_block(handle_t *handle, newex.ee_start_hi = 0; newex.ee_len = 1; err = ext3_ext_insert_extent(handle, &tree, path, &newex); - if (err) + if (err) { + /* free data blocks we just allocated */ + ext3_free_blocks(handle, inode, newex.ee_start, newex.ee_len); goto out2; + } if (extend_disksize && inode->i_size > EXT3_I(inode)->i_disksize) EXT3_I(inode)->i_disksize = inode->i_size; @@ -2132,8 +2292,11 @@ int ext3_ext_get_block(handle_t *handle, ext3_ext_put_in_cache(&tree, newex.ee_block, newex.ee_len, newex.ee_start, EXT3_EXT_CACHE_EXTENT); out: + if (allocated > max_blocks) + allocated = max_blocks; ext3_ext_show_leaf(&tree, path); map_bh(bh_result, inode->i_sb, newblock); + bh_result->b_size = (allocated << inode->i_blkbits); out2: if (path) { ext3_ext_drop_refs(path); --- linux-2.6.9-full/fs/ext3/inode.c 2007-03-23 15:57:00.000000000 +0300 +++ linux-2.6.9-full/fs/ext3/inode.c 2007-02-16 17:17:03.000000000 +0300 @@ -798,13 +798,17 @@ changed: static inline int ext3_get_block_wrap(handle_t *handle, struct inode *inode, long block, - struct buffer_head *bh, int create, int extend_disksize) + int max_blocks, struct buffer_head *bh, int create, + int extend_disksize) { + int ret; if (EXT3_I(inode)->i_flags & EXT3_EXTENTS_FL) - return ext3_ext_get_block(handle, inode, block, bh, create, - extend_disksize); - return ext3_get_block_handle(handle, inode, block, bh, create, + return ext3_ext_get_block(handle, inode, block, max_blocks, + bh, create, extend_disksize); + ret = ext3_get_block_handle(handle, inode, block, bh, create, extend_disksize); + bh->b_size = (1 << inode->i_blkbits); + return ret; } static int ext3_get_block(struct inode *inode, sector_t iblock, @@ -817,7 +821,7 @@ static int ext3_get_block(struct inode * handle = ext3_journal_current_handle(); J_ASSERT(handle != 0); } - ret = ext3_get_block_wrap(handle, inode, iblock, + ret = ext3_get_block_wrap(handle, inode, iblock, 1, bh_result, create, 1); return ret; } @@ -862,9 +866,8 @@ ext3_direct_io_get_blocks(struct inode * get_block: if (ret == 0) - ret = ext3_get_block_wrap(handle, inode, iblock, + ret = ext3_get_block_wrap(handle, inode, iblock, max_blocks, bh_result, create, 0); - bh_result->b_size = (1 << inode->i_blkbits); return ret; } @@ -882,7 +885,7 @@ struct buffer_head *ext3_getblk(handle_t dummy.b_state = 0; dummy.b_blocknr = -1000; buffer_trace_init(&dummy.b_history); - *errp = ext3_get_block_wrap(handle, inode, block, &dummy, create, 1); + *errp = ext3_get_block_wrap(handle, inode, block, 1, &dummy, create, 1); if (!*errp && buffer_mapped(&dummy)) { struct buffer_head *bh; bh = sb_getblk(inode->i_sb, dummy.b_blocknr);