lkml.org 
[lkml]   [2016]   [May]   [2]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 4.4 149/163] ext4: fix races of writeback with punch hole and zero range
    Date
    4.4-stable review patch.  If anyone has any objections, please let me know.

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

    From: Jan Kara <jack@suse.com>

    commit 011278485ecc3cd2a3954b5d4c73101d919bf1fa upstream.

    When doing delayed allocation, update of on-disk inode size is postponed
    until IO submission time. However hole punch or zero range fallocate
    calls can end up discarding the tail page cache page and thus on-disk
    inode size would never be properly updated.

    Make sure the on-disk inode size is updated before truncating page
    cache.

    Signed-off-by: Jan Kara <jack@suse.com>
    Signed-off-by: Theodore Ts'o <tytso@mit.edu>
    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

    ---
    fs/ext4/ext4.h | 3 +++
    fs/ext4/extents.c | 5 +++++
    fs/ext4/inode.c | 35 ++++++++++++++++++++++++++++++++++-
    3 files changed, 42 insertions(+), 1 deletion(-)

    --- a/fs/ext4/ext4.h
    +++ b/fs/ext4/ext4.h
    @@ -2881,6 +2881,9 @@ static inline int ext4_update_inode_size
    return changed;
    }

    +int ext4_update_disksize_before_punch(struct inode *inode, loff_t offset,
    + loff_t len);
    +
    struct ext4_group_info {
    unsigned long bb_state;
    struct rb_root bb_free_root;
    --- a/fs/ext4/extents.c
    +++ b/fs/ext4/extents.c
    @@ -4847,6 +4847,11 @@ static long ext4_zero_range(struct file
    * released from page cache.
    */
    down_write(&EXT4_I(inode)->i_mmap_sem);
    + ret = ext4_update_disksize_before_punch(inode, offset, len);
    + if (ret) {
    + up_write(&EXT4_I(inode)->i_mmap_sem);
    + goto out_dio;
    + }
    /* Now release the pages and zero block aligned part of pages */
    truncate_pagecache_range(inode, start, end - 1);
    inode->i_mtime = inode->i_ctime = ext4_current_time(inode);
    --- a/fs/ext4/inode.c
    +++ b/fs/ext4/inode.c
    @@ -3587,6 +3587,35 @@ int ext4_can_truncate(struct inode *inod
    }

    /*
    + * We have to make sure i_disksize gets properly updated before we truncate
    + * page cache due to hole punching or zero range. Otherwise i_disksize update
    + * can get lost as it may have been postponed to submission of writeback but
    + * that will never happen after we truncate page cache.
    + */
    +int ext4_update_disksize_before_punch(struct inode *inode, loff_t offset,
    + loff_t len)
    +{
    + handle_t *handle;
    + loff_t size = i_size_read(inode);
    +
    + WARN_ON(!mutex_is_locked(&inode->i_mutex));
    + if (offset > size || offset + len < size)
    + return 0;
    +
    + if (EXT4_I(inode)->i_disksize >= size)
    + return 0;
    +
    + handle = ext4_journal_start(inode, EXT4_HT_MISC, 1);
    + if (IS_ERR(handle))
    + return PTR_ERR(handle);
    + ext4_update_i_disksize(inode, size);
    + ext4_mark_inode_dirty(handle, inode);
    + ext4_journal_stop(handle);
    +
    + return 0;
    +}
    +
    +/*
    * ext4_punch_hole: punches a hole in a file by releaseing the blocks
    * associated with the given offset and length
    *
    @@ -3664,9 +3693,13 @@ int ext4_punch_hole(struct inode *inode,
    last_block_offset = round_down((offset + length), sb->s_blocksize) - 1;

    /* Now release the pages and zero block aligned part of pages*/
    - if (last_block_offset > first_block_offset)
    + if (last_block_offset > first_block_offset) {
    + ret = ext4_update_disksize_before_punch(inode, offset, length);
    + if (ret)
    + goto out_dio;
    truncate_pagecache_range(inode, first_block_offset,
    last_block_offset);
    + }

    if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))
    credits = ext4_writepage_trans_blocks(inode);

    \
     
     \ /
      Last update: 2016-05-03 03:01    [W:4.023 / U:0.160 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site