lkml.org 
[lkml]   [2017]   [Jun]   [1]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Date
    Subject[PATCH 3.16 027/212] ext4: avoid deadlock when expanding inode size
    3.16.44-rc1 review patch.  If anyone has any objections, please let me know.

    ------------------

    From: Jan Kara <jack@suse.cz>

    commit 2e81a4eeedcaa66e35f58b81e0755b87057ce392 upstream.

    When we need to move xattrs into external xattr block, we call
    ext4_xattr_block_set() from ext4_expand_extra_isize_ea(). That may end
    up calling ext4_mark_inode_dirty() again which will recurse back into
    the inode expansion code leading to deadlocks.

    Protect from recursion using EXT4_STATE_NO_EXPAND inode flag and move
    its management into ext4_expand_extra_isize_ea() since its manipulation
    is safe there (due to xattr_sem) from possible races with
    ext4_xattr_set_handle() which plays with it as well.

    Signed-off-by: Jan Kara <jack@suse.cz>
    Signed-off-by: Theodore Ts'o <tytso@mit.edu>
    [bwh: Backported to 3.16: adjust context[
    Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
    ---
    fs/ext4/inode.c | 2 --
    fs/ext4/xattr.c | 19 +++++++++++++------
    2 files changed, 13 insertions(+), 8 deletions(-)

    --- a/fs/ext4/inode.c
    +++ b/fs/ext4/inode.c
    @@ -5062,8 +5062,6 @@ int ext4_mark_inode_dirty(handle_t *hand
    sbi->s_want_extra_isize,
    iloc, handle);
    if (ret) {
    - ext4_set_inode_state(inode,
    - EXT4_STATE_NO_EXPAND);
    if (mnt_count !=
    le16_to_cpu(sbi->s_es->s_mnt_count)) {
    ext4_warning(inode->i_sb,
    --- a/fs/ext4/xattr.c
    +++ b/fs/ext4/xattr.c
    @@ -1277,11 +1277,13 @@ int ext4_expand_extra_isize_ea(struct in
    int s_min_extra_isize = le16_to_cpu(EXT4_SB(inode->i_sb)->s_es->s_min_extra_isize);

    down_write(&EXT4_I(inode)->xattr_sem);
    + /*
    + * Set EXT4_STATE_NO_EXPAND to avoid recursion when marking inode dirty
    + */
    + ext4_set_inode_state(inode, EXT4_STATE_NO_EXPAND);
    retry:
    - if (EXT4_I(inode)->i_extra_isize >= new_extra_isize) {
    - up_write(&EXT4_I(inode)->xattr_sem);
    - return 0;
    - }
    + if (EXT4_I(inode)->i_extra_isize >= new_extra_isize)
    + goto out;

    header = IHDR(inode, raw_inode);
    entry = IFIRST(header);
    @@ -1306,8 +1308,7 @@ retry:
    (void *)header, total_ino,
    inode->i_sb->s_blocksize);
    EXT4_I(inode)->i_extra_isize = new_extra_isize;
    - error = 0;
    - goto cleanup;
    + goto out;
    }

    /*
    @@ -1467,6 +1468,8 @@ retry:
    kfree(bs);
    }
    brelse(bh);
    +out:
    + ext4_clear_inode_state(inode, EXT4_STATE_NO_EXPAND);
    up_write(&EXT4_I(inode)->xattr_sem);
    return 0;

    @@ -1478,6 +1481,10 @@ cleanup:
    kfree(is);
    kfree(bs);
    brelse(bh);
    + /*
    + * We deliberately leave EXT4_STATE_NO_EXPAND set here since inode
    + * size expansion failed.
    + */
    up_write(&EXT4_I(inode)->xattr_sem);
    return error;
    }
    \
     
     \ /
      Last update: 2017-06-01 18:42    [W:3.112 / U:0.020 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site