• Jan Kara's avatar
    ext4: fix jbd2 warning under heavy xattr load · ec4cb1aa
    Jan Kara authored
    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: default avatarSage Weil <sage@inktank.com>
    Signed-off-by: default avatarJan Kara <jack@suse.cz>
    Signed-off-by: default avatar"Theodore Ts'o" <tytso@mit.edu>
    Cc: stable@vger.kernel.org
    ec4cb1aa
xattr.c 45.6 KB