aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorJan Kara <jack@suse.cz>2014-04-07 10:54:21 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2014-05-06 10:55:32 -0400
commitf78e2d2db6082212ca85a25e9d51d0bd126cb0a8 (patch)
tree917afc220256552619c415d66c0e6611a64d85f7 /fs
parent5da43e877ea4e51b1096a8973ff6e7a03554fcb0 (diff)
ext4: fix jbd2 warning under heavy xattr load
commit ec4cb1aa2b7bae18dd8164f2e9c7c51abcf61280 upstream. When heavily exercising xattr code the assertion that jbd2_journal_dirty_metadata() shouldn't return error was triggered: WARNING: at /srv/autobuild-ceph/gitbuilder.git/build/fs/jbd2/transaction.c:1237 jbd2_journal_dirty_metadata+0x1ba/0x260() CPU: 0 PID: 8877 Comm: ceph-osd Tainted: G W 3.10.0-ceph-00049-g68d04c9 #1 Hardware name: Dell Inc. PowerEdge R410/01V648, BIOS 1.6.3 02/07/2011 ffffffff81a1d3c8 ffff880214469928 ffffffff816311b0 ffff880214469968 ffffffff8103fae0 ffff880214469958 ffff880170a9dc30 ffff8802240fbe80 0000000000000000 ffff88020b366000 ffff8802256e7510 ffff880214469978 Call Trace: [<ffffffff816311b0>] dump_stack+0x19/0x1b [<ffffffff8103fae0>] warn_slowpath_common+0x70/0xa0 [<ffffffff8103fb2a>] warn_slowpath_null+0x1a/0x20 [<ffffffff81267c2a>] jbd2_journal_dirty_metadata+0x1ba/0x260 [<ffffffff81245093>] __ext4_handle_dirty_metadata+0xa3/0x140 [<ffffffff812561f3>] ext4_xattr_release_block+0x103/0x1f0 [<ffffffff81256680>] ext4_xattr_block_set+0x1e0/0x910 [<ffffffff8125795b>] ext4_xattr_set_handle+0x38b/0x4a0 [<ffffffff810a319d>] ? trace_hardirqs_on+0xd/0x10 [<ffffffff81257b32>] ext4_xattr_set+0xc2/0x140 [<ffffffff81258547>] ext4_xattr_user_set+0x47/0x50 [<ffffffff811935ce>] generic_setxattr+0x6e/0x90 [<ffffffff81193ecb>] __vfs_setxattr_noperm+0x7b/0x1c0 [<ffffffff811940d4>] vfs_setxattr+0xc4/0xd0 [<ffffffff8119421e>] setxattr+0x13e/0x1e0 [<ffffffff811719c7>] ? __sb_start_write+0xe7/0x1b0 [<ffffffff8118f2e8>] ? mnt_want_write_file+0x28/0x60 [<ffffffff8118c65c>] ? fget_light+0x3c/0x130 [<ffffffff8118f2e8>] ? mnt_want_write_file+0x28/0x60 [<ffffffff8118f1f8>] ? __mnt_want_write+0x58/0x70 [<ffffffff811946be>] SyS_fsetxattr+0xbe/0x100 [<ffffffff816407c2>] system_call_fastpath+0x16/0x1b The reason for the warning is that buffer_head passed into jbd2_journal_dirty_metadata() didn't have journal_head attached. This is caused by the following race of two ext4_xattr_release_block() calls: CPU1 CPU2 ext4_xattr_release_block() ext4_xattr_release_block() lock_buffer(bh); /* False */ if (BHDR(bh)->h_refcount == cpu_to_le32(1)) } else { le32_add_cpu(&BHDR(bh)->h_refcount, -1); unlock_buffer(bh); lock_buffer(bh); /* True */ if (BHDR(bh)->h_refcount == cpu_to_le32(1)) get_bh(bh); ext4_free_blocks() ... jbd2_journal_forget() jbd2_journal_unfile_buffer() -> JH is gone error = ext4_handle_dirty_xattr_block(handle, inode, bh); -> triggers the warning We fix the problem by moving ext4_handle_dirty_xattr_block() under the buffer lock. Sadly this cannot be done in nojournal mode as that function can call sync_dirty_buffer() which would deadlock. Luckily in nojournal mode the race is harmless (we only dirty already freed buffer) and thus for nojournal mode we leave the dirtying outside of the buffer lock. Reported-by: Sage Weil <sage@inktank.com> Signed-off-by: Jan Kara <jack@suse.cz> Signed-off-by: "Theodore Ts'o" <tytso@mit.edu> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'fs')
-rw-r--r--fs/ext4/xattr.c23
1 files changed, 19 insertions, 4 deletions
diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c
index 1423c4816a47..298e9c8da364 100644
--- a/fs/ext4/xattr.c
+++ b/fs/ext4/xattr.c
@@ -517,8 +517,8 @@ static void ext4_xattr_update_super_block(handle_t *handle,
517} 517}
518 518
519/* 519/*
520 * Release the xattr block BH: If the reference count is > 1, decrement 520 * Release the xattr block BH: If the reference count is > 1, decrement it;
521 * it; otherwise free the block. 521 * otherwise free the block.
522 */ 522 */
523static void 523static void
524ext4_xattr_release_block(handle_t *handle, struct inode *inode, 524ext4_xattr_release_block(handle_t *handle, struct inode *inode,
@@ -538,16 +538,31 @@ ext4_xattr_release_block(handle_t *handle, struct inode *inode,
538 if (ce) 538 if (ce)
539 mb_cache_entry_free(ce); 539 mb_cache_entry_free(ce);
540 get_bh(bh); 540 get_bh(bh);
541 unlock_buffer(bh);
541 ext4_free_blocks(handle, inode, bh, 0, 1, 542 ext4_free_blocks(handle, inode, bh, 0, 1,
542 EXT4_FREE_BLOCKS_METADATA | 543 EXT4_FREE_BLOCKS_METADATA |
543 EXT4_FREE_BLOCKS_FORGET); 544 EXT4_FREE_BLOCKS_FORGET);
544 unlock_buffer(bh);
545 } else { 545 } else {
546 le32_add_cpu(&BHDR(bh)->h_refcount, -1); 546 le32_add_cpu(&BHDR(bh)->h_refcount, -1);
547 if (ce) 547 if (ce)
548 mb_cache_entry_release(ce); 548 mb_cache_entry_release(ce);
549 /*
550 * Beware of this ugliness: Releasing of xattr block references
551 * from different inodes can race and so we have to protect
552 * from a race where someone else frees the block (and releases
553 * its journal_head) before we are done dirtying the buffer. In
554 * nojournal mode this race is harmless and we actually cannot
555 * call ext4_handle_dirty_xattr_block() with locked buffer as
556 * that function can call sync_dirty_buffer() so for that case
557 * we handle the dirtying after unlocking the buffer.
558 */
559 if (ext4_handle_valid(handle))
560 error = ext4_handle_dirty_xattr_block(handle, inode,
561 bh);
549 unlock_buffer(bh); 562 unlock_buffer(bh);
550 error = ext4_handle_dirty_xattr_block(handle, inode, bh); 563 if (!ext4_handle_valid(handle))
564 error = ext4_handle_dirty_xattr_block(handle, inode,
565 bh);
551 if (IS_SYNC(inode)) 566 if (IS_SYNC(inode))
552 ext4_handle_sync(handle); 567 ext4_handle_sync(handle);
553 dquot_free_block(inode, EXT4_C2B(EXT4_SB(inode->i_sb), 1)); 568 dquot_free_block(inode, EXT4_C2B(EXT4_SB(inode->i_sb), 1));