ext4_get_blocks_wrap take the truncate_mutex early. From: Aneesh Kumar K.V When doing a migrate from ext3 to ext4 inode we need to make sure the test for inode type and walking inode data happens inside lock. To make this happen move truncate_mutex early before checking the i_flags. This actually should enable us to remove the verify_chain(). Signed-off-by: Aneesh Kumar K.V --- fs/ext4/extents.c | 7 ++-- fs/ext4/inode.c | 69 +++++------------------------------------------- include/linux/ext4_fs.h | 15 +++++++--- 3 files changed, 23 insertions(+), 68 deletions(-) Index: linux-2.6.23-rc6/fs/ext4/extents.c =================================================================== --- linux-2.6.23-rc6.orig/fs/ext4/extents.c 2007-09-18 17:18:59.000000000 -0700 +++ linux-2.6.23-rc6/fs/ext4/extents.c 2007-09-20 17:26:12.000000000 -0700 @@ -2227,6 +2227,10 @@ out: return err ? err : allocated; } +/* + * Need to be called with + * mutex_lock(&EXT4_I(inode)->truncate_mutex); + */ int ext4_ext_get_blocks(handle_t *handle, struct inode *inode, ext4_fsblk_t iblock, unsigned long max_blocks, struct buffer_head *bh_result, @@ -2242,7 +2246,6 @@ int ext4_ext_get_blocks(handle_t *handle __clear_bit(BH_New, &bh_result->b_state); ext_debug("blocks %d/%lu requested for inode %u\n", (int) iblock, max_blocks, (unsigned) inode->i_ino); - mutex_lock(&EXT4_I(inode)->truncate_mutex); /* check in cache */ goal = ext4_ext_in_cache(inode, iblock, &newex); @@ -2416,8 +2419,6 @@ out2: ext4_ext_drop_refs(path); kfree(path); } - mutex_unlock(&EXT4_I(inode)->truncate_mutex); - return err ? err : allocated; } Index: linux-2.6.23-rc6/fs/ext4/inode.c =================================================================== --- linux-2.6.23-rc6.orig/fs/ext4/inode.c 2007-09-20 17:26:09.000000000 -0700 +++ linux-2.6.23-rc6/fs/ext4/inode.c 2007-09-20 17:26:12.000000000 -0700 @@ -245,13 +245,6 @@ static inline void add_chain(Indirect *p p->bh = bh; } -static int verify_chain(Indirect *from, Indirect *to) -{ - while (from <= to && from->key == *from->p) - from++; - return (from > to); -} - /** * ext4_block_to_path - parse the block number into array of offsets * @inode: inode in question (we are only interested in its superblock) @@ -346,10 +339,11 @@ static int ext4_block_to_path(struct ino * (pointer to last triple returned, *@err == 0) * or when it gets an IO error reading an indirect block * (ditto, *@err == -EIO) - * or when it notices that chain had been changed while it was reading - * (ditto, *@err == -EAGAIN) * or when it reads all @depth-1 indirect blocks successfully and finds * the whole chain, all way to the data (returns %NULL, *err == 0). + * + * Need to be called with + * mutex_lock(&EXT4_I(inode)->truncate_mutex) */ static Indirect *ext4_get_branch(struct inode *inode, int depth, int *offsets, Indirect chain[4], int *err) @@ -367,9 +361,6 @@ static Indirect *ext4_get_branch(struct bh = sb_bread(sb, le32_to_cpu(p->key)); if (!bh) goto failure; - /* Reader: pointers */ - if (!verify_chain(chain, p)) - goto changed; add_chain(++p, bh, (__le32*)bh->b_data + *++offsets); /* Reader: end */ if (!p->key) @@ -377,10 +368,6 @@ static Indirect *ext4_get_branch(struct } return NULL; -changed: - brelse(bh); - *err = -EAGAIN; - goto no_block; failure: *err = -EIO; no_block: @@ -784,6 +771,10 @@ err_out: * return > 0, # of blocks mapped or allocated. * return = 0, if plain lookup failed. * return < 0, error case. + * + * + * Need to be called with + * mutex_lock(&EXT4_I(inode)->truncate_mutex) */ int ext4_get_blocks_handle(handle_t *handle, struct inode *inode, sector_t iblock, unsigned long maxblocks, @@ -821,18 +812,6 @@ int ext4_get_blocks_handle(handle_t *han while (count < maxblocks && count <= blocks_to_boundary) { ext4_fsblk_t blk; - if (!verify_chain(chain, partial)) { - /* - * Indirect block might be removed by - * truncate while we were reading it. - * Handling of that case: forget what we've - * got now. Flag the err as EAGAIN, so it - * will reread. - */ - err = -EAGAIN; - count = 0; - break; - } blk = le32_to_cpu(*(chain[depth-1].p + count)); if (blk == first_block + count) @@ -840,44 +819,13 @@ int ext4_get_blocks_handle(handle_t *han else break; } - if (err != -EAGAIN) - goto got_it; + goto got_it; } /* Next simple case - plain lookup or failed read of indirect block */ if (!create || err == -EIO) goto cleanup; - mutex_lock(&ei->truncate_mutex); - - /* - * If the indirect block is missing while we are reading - * the chain(ext4_get_branch() returns -EAGAIN err), or - * if the chain has been changed after we grab the semaphore, - * (either because another process truncated this branch, or - * another get_block allocated this branch) re-grab the chain to see if - * the request block has been allocated or not. - * - * Since we already block the truncate/other get_block - * at this point, we will have the current copy of the chain when we - * splice the branch into the tree. - */ - if (err == -EAGAIN || !verify_chain(chain, partial)) { - while (partial > chain) { - brelse(partial->bh); - partial--; - } - partial = ext4_get_branch(inode, depth, offsets, chain, &err); - if (!partial) { - count++; - mutex_unlock(&ei->truncate_mutex); - if (err) - goto cleanup; - clear_buffer_new(bh_result); - goto got_it; - } - } - /* * Okay, we need to do block allocation. Lazily initialize the block * allocation info here if necessary @@ -919,7 +867,6 @@ int ext4_get_blocks_handle(handle_t *han */ if (!err && extend_disksize && inode->i_size > ei->i_disksize) ei->i_disksize = inode->i_size; - mutex_unlock(&ei->truncate_mutex); if (err) goto cleanup; Index: linux-2.6.23-rc6/include/linux/ext4_fs.h =================================================================== --- linux-2.6.23-rc6.orig/include/linux/ext4_fs.h 2007-09-20 17:26:09.000000000 -0700 +++ linux-2.6.23-rc6/include/linux/ext4_fs.h 2007-09-20 17:26:12.000000000 -0700 @@ -1099,11 +1099,18 @@ ext4_get_blocks_wrap(handle_t *handle, s unsigned long max_blocks, struct buffer_head *bh, int create, int extend_disksize) { - if (EXT4_I(inode)->i_flags & EXT4_EXTENTS_FL) - return ext4_ext_get_blocks(handle, inode, block, max_blocks, + int retval; + mutex_lock(&EXT4_I(inode)->truncate_mutex); + if (EXT4_I(inode)->i_flags & EXT4_EXTENTS_FL) { + retval = ext4_ext_get_blocks(handle, inode, block, max_blocks, bh, create, extend_disksize); - return ext4_get_blocks_handle(handle, inode, block, max_blocks, bh, - create, extend_disksize); + } else { + retval = ext4_get_blocks_handle(handle, inode, block, max_blocks, + bh, create, extend_disksize); + } + mutex_unlock(&EXT4_I(inode)->truncate_mutex); + + return retval; }