diff options
Diffstat (limited to 'fs/ext3/inode.c')
| -rw-r--r-- | fs/ext3/inode.c | 28 | 
1 files changed, 24 insertions, 4 deletions
diff --git a/fs/ext3/inode.c b/fs/ext3/inode.c index b49908a167a..cd098a7b77f 100644 --- a/fs/ext3/inode.c +++ b/fs/ext3/inode.c @@ -172,10 +172,21 @@ static int try_to_extend_transaction(handle_t *handle, struct inode *inode)   * so before we call here everything must be consistently dirtied against   * this transaction.   */ -static int ext3_journal_test_restart(handle_t *handle, struct inode *inode) +static int truncate_restart_transaction(handle_t *handle, struct inode *inode)  { +	int ret; +  	jbd_debug(2, "restarting handle %p\n", handle); -	return ext3_journal_restart(handle, blocks_for_truncate(inode)); +	/* +	 * Drop truncate_mutex to avoid deadlock with ext3_get_blocks_handle +	 * At this moment, get_block can be called only for blocks inside +	 * i_size since page cache has been already dropped and writes are +	 * blocked by i_mutex. So we can safely drop the truncate_mutex. +	 */ +	mutex_unlock(&EXT3_I(inode)->truncate_mutex); +	ret = ext3_journal_restart(handle, blocks_for_truncate(inode)); +	mutex_lock(&EXT3_I(inode)->truncate_mutex); +	return ret;  }  /* @@ -2072,7 +2083,7 @@ static void ext3_clear_blocks(handle_t *handle, struct inode *inode,  			ext3_journal_dirty_metadata(handle, bh);  		}  		ext3_mark_inode_dirty(handle, inode); -		ext3_journal_test_restart(handle, inode); +		truncate_restart_transaction(handle, inode);  		if (bh) {  			BUFFER_TRACE(bh, "retaking write access");  			ext3_journal_get_write_access(handle, bh); @@ -2282,7 +2293,7 @@ static void ext3_free_branches(handle_t *handle, struct inode *inode,  				return;  			if (try_to_extend_transaction(handle, inode)) {  				ext3_mark_inode_dirty(handle, inode); -				ext3_journal_test_restart(handle, inode); +				truncate_restart_transaction(handle, inode);  			}  			ext3_free_blocks(handle, inode, nr, 1); @@ -2892,6 +2903,10 @@ static int ext3_do_update_inode(handle_t *handle,  	struct buffer_head *bh = iloc->bh;  	int err = 0, rc, block; +again: +	/* we can't allow multiple procs in here at once, its a bit racey */ +	lock_buffer(bh); +  	/* For fields not not tracking in the in-memory inode,  	 * initialise them to zero for new inodes. */  	if (ei->i_state & EXT3_STATE_NEW) @@ -2951,16 +2966,20 @@ static int ext3_do_update_inode(handle_t *handle,  			       /* If this is the first large file  				* created, add a flag to the superblock.  				*/ +				unlock_buffer(bh);  				err = ext3_journal_get_write_access(handle,  						EXT3_SB(sb)->s_sbh);  				if (err)  					goto out_brelse; +  				ext3_update_dynamic_rev(sb);  				EXT3_SET_RO_COMPAT_FEATURE(sb,  					EXT3_FEATURE_RO_COMPAT_LARGE_FILE);  				handle->h_sync = 1;  				err = ext3_journal_dirty_metadata(handle,  						EXT3_SB(sb)->s_sbh); +				/* get our lock and start over */ +				goto again;  			}  		}  	} @@ -2983,6 +3002,7 @@ static int ext3_do_update_inode(handle_t *handle,  		raw_inode->i_extra_isize = cpu_to_le16(ei->i_extra_isize);  	BUFFER_TRACE(bh, "call ext3_journal_dirty_metadata"); +	unlock_buffer(bh);  	rc = ext3_journal_dirty_metadata(handle, bh);  	if (!err)  		err = rc;  | 
