diff options
Diffstat (limited to 'fs/ext4/xattr.c')
| -rw-r--r-- | fs/ext4/xattr.c | 23 | 
1 files changed, 19 insertions, 4 deletions
diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c index 1f5cf5880718..4eec399ec807 100644 --- a/fs/ext4/xattr.c +++ b/fs/ext4/xattr.c @@ -520,8 +520,8 @@ static void ext4_xattr_update_super_block(handle_t *handle,  }  /* - * Release the xattr block BH: If the reference count is > 1, decrement - * it; otherwise free the block. + * Release the xattr block BH: If the reference count is > 1, decrement it; + * otherwise free the block.   */  static void  ext4_xattr_release_block(handle_t *handle, struct inode *inode, @@ -542,16 +542,31 @@ ext4_xattr_release_block(handle_t *handle, struct inode *inode,  		if (ce)  			mb_cache_entry_free(ce);  		get_bh(bh); +		unlock_buffer(bh);  		ext4_free_blocks(handle, inode, bh, 0, 1,  				 EXT4_FREE_BLOCKS_METADATA |  				 EXT4_FREE_BLOCKS_FORGET); -		unlock_buffer(bh);  	} else {  		le32_add_cpu(&BHDR(bh)->h_refcount, -1);  		if (ce)  			mb_cache_entry_release(ce); +		/* +		 * Beware of this ugliness: Releasing of xattr block references +		 * from different inodes can race and so we have to protect +		 * from a race where someone else frees the block (and releases +		 * its journal_head) before we are done dirtying the buffer. In +		 * nojournal mode this race is harmless and we actually cannot +		 * call ext4_handle_dirty_xattr_block() with locked buffer as +		 * that function can call sync_dirty_buffer() so for that case +		 * we handle the dirtying after unlocking the buffer. +		 */ +		if (ext4_handle_valid(handle)) +			error = ext4_handle_dirty_xattr_block(handle, inode, +							      bh);  		unlock_buffer(bh); -		error = ext4_handle_dirty_xattr_block(handle, inode, bh); +		if (!ext4_handle_valid(handle)) +			error = ext4_handle_dirty_xattr_block(handle, inode, +							      bh);  		if (IS_SYNC(inode))  			ext4_handle_sync(handle);  		dquot_free_block(inode, EXT4_C2B(EXT4_SB(inode->i_sb), 1));  | 
