|
@@ -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
|
|
static void
|
|
ext4_xattr_release_block(handle_t *handle, struct inode *inode,
|
|
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)
|
|
if (ce)
|
|
mb_cache_entry_free(ce);
|
|
mb_cache_entry_free(ce);
|
|
get_bh(bh);
|
|
get_bh(bh);
|
|
|
|
+ unlock_buffer(bh);
|
|
ext4_free_blocks(handle, inode, bh, 0, 1,
|
|
ext4_free_blocks(handle, inode, bh, 0, 1,
|
|
EXT4_FREE_BLOCKS_METADATA |
|
|
EXT4_FREE_BLOCKS_METADATA |
|
|
EXT4_FREE_BLOCKS_FORGET);
|
|
EXT4_FREE_BLOCKS_FORGET);
|
|
- unlock_buffer(bh);
|
|
|
|
} else {
|
|
} else {
|
|
le32_add_cpu(&BHDR(bh)->h_refcount, -1);
|
|
le32_add_cpu(&BHDR(bh)->h_refcount, -1);
|
|
if (ce)
|
|
if (ce)
|
|
mb_cache_entry_release(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);
|
|
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))
|
|
if (IS_SYNC(inode))
|
|
ext4_handle_sync(handle);
|
|
ext4_handle_sync(handle);
|
|
dquot_free_block(inode, EXT4_C2B(EXT4_SB(inode->i_sb), 1));
|
|
dquot_free_block(inode, EXT4_C2B(EXT4_SB(inode->i_sb), 1));
|