diff options
Diffstat (limited to 'freed-ora/current/f23/ext4-fix-races-of-writeback-with-punch-hole-and-zero.patch')
-rw-r--r-- | freed-ora/current/f23/ext4-fix-races-of-writeback-with-punch-hole-and-zero.patch | 110 |
1 files changed, 110 insertions, 0 deletions
diff --git a/freed-ora/current/f23/ext4-fix-races-of-writeback-with-punch-hole-and-zero.patch b/freed-ora/current/f23/ext4-fix-races-of-writeback-with-punch-hole-and-zero.patch new file mode 100644 index 000000000..9ff9e2761 --- /dev/null +++ b/freed-ora/current/f23/ext4-fix-races-of-writeback-with-punch-hole-and-zero.patch @@ -0,0 +1,110 @@ +From 011278485ecc3cd2a3954b5d4c73101d919bf1fa Mon Sep 17 00:00:00 2001 +From: Jan Kara <jack@suse.com> +Date: Mon, 7 Dec 2015 14:34:49 -0500 +Subject: [PATCH 4/4] 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> +--- + fs/ext4/ext4.h | 3 +++ + fs/ext4/extents.c | 5 +++++ + fs/ext4/inode.c | 35 ++++++++++++++++++++++++++++++++++- + 3 files changed, 42 insertions(+), 1 deletion(-) + +diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h +index 348a5ff4a0e2..80f76f092079 100644 +--- a/fs/ext4/ext4.h ++++ b/fs/ext4/ext4.h +@@ -2858,6 +2858,9 @@ static inline int ext4_update_inode_size(struct inode *inode, loff_t newsize) + 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; +diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c +index 4b105c96df08..3578b25fccfd 100644 +--- a/fs/ext4/extents.c ++++ b/fs/ext4/extents.c +@@ -4847,6 +4847,11 @@ static long ext4_zero_range(struct file *file, loff_t offset, + * 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); +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) + } + + /* ++ * 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 + * +@@ -3636,9 +3665,13 @@ int ext4_punch_hole(struct inode *inode, loff_t offset, loff_t length) + 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); +-- +2.5.5 + |