aboutsummaryrefslogtreecommitdiffstats
path: root/fs/ext4/inode.c
diff options
context:
space:
mode:
authorJan Kara <jack@suse.com>2015-12-07 14:34:49 -0500
committerTheodore Ts'o <tytso@mit.edu>2015-12-07 14:34:49 -0500
commit011278485ecc3cd2a3954b5d4c73101d919bf1fa (patch)
tree740938e00aa0c972db386b5576656e646c02e36d /fs/ext4/inode.c
parent32ebffd3bbb4162da5ff88f9a35dd32d0a28ea70 (diff)
ext4: fix races of writeback with punch hole and zero range
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>
Diffstat (limited to 'fs/ext4/inode.c')
-rw-r--r--fs/ext4/inode.c35
1 files changed, 34 insertions, 1 deletions
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index d1207d03c961..472e608da13d 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -3559,6 +3559,35 @@ int ext4_can_truncate(struct inode *inode)
3559} 3559}
3560 3560
3561/* 3561/*
3562 * We have to make sure i_disksize gets properly updated before we truncate
3563 * page cache due to hole punching or zero range. Otherwise i_disksize update
3564 * can get lost as it may have been postponed to submission of writeback but
3565 * that will never happen after we truncate page cache.
3566 */
3567int ext4_update_disksize_before_punch(struct inode *inode, loff_t offset,
3568 loff_t len)
3569{
3570 handle_t *handle;
3571 loff_t size = i_size_read(inode);
3572
3573 WARN_ON(!mutex_is_locked(&inode->i_mutex));
3574 if (offset > size || offset + len < size)
3575 return 0;
3576
3577 if (EXT4_I(inode)->i_disksize >= size)
3578 return 0;
3579
3580 handle = ext4_journal_start(inode, EXT4_HT_MISC, 1);
3581 if (IS_ERR(handle))
3582 return PTR_ERR(handle);
3583 ext4_update_i_disksize(inode, size);
3584 ext4_mark_inode_dirty(handle, inode);
3585 ext4_journal_stop(handle);
3586
3587 return 0;
3588}
3589
3590/*
3562 * ext4_punch_hole: punches a hole in a file by releaseing the blocks 3591 * ext4_punch_hole: punches a hole in a file by releaseing the blocks
3563 * associated with the given offset and length 3592 * associated with the given offset and length
3564 * 3593 *
@@ -3636,9 +3665,13 @@ int ext4_punch_hole(struct inode *inode, loff_t offset, loff_t length)
3636 last_block_offset = round_down((offset + length), sb->s_blocksize) - 1; 3665 last_block_offset = round_down((offset + length), sb->s_blocksize) - 1;
3637 3666
3638 /* Now release the pages and zero block aligned part of pages*/ 3667 /* Now release the pages and zero block aligned part of pages*/
3639 if (last_block_offset > first_block_offset) 3668 if (last_block_offset > first_block_offset) {
3669 ret = ext4_update_disksize_before_punch(inode, offset, length);
3670 if (ret)
3671 goto out_dio;
3640 truncate_pagecache_range(inode, first_block_offset, 3672 truncate_pagecache_range(inode, first_block_offset,
3641 last_block_offset); 3673 last_block_offset);
3674 }
3642 3675
3643 if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)) 3676 if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))
3644 credits = ext4_writepage_trans_blocks(inode); 3677 credits = ext4_writepage_trans_blocks(inode);