aboutsummaryrefslogtreecommitdiffstats
path: root/fs/ext4
diff options
context:
space:
mode:
authorLukas Czerner <lczerner@redhat.com>2013-05-27 23:32:35 -0400
committerTheodore Ts'o <tytso@mit.edu>2013-05-27 23:32:35 -0400
commita87dd18ce24dee5da1e9eb44bf8d8d48e0957efd (patch)
treef05a1b28fc0ee8a26007a7da20805b3f643f4923 /fs/ext4
parent55f252c9f50e998f6bc3aadc7806f049f7443d21 (diff)
ext4: use ext4_zero_partial_blocks in punch_hole
We're doing to get rid of ext4_discard_partial_page_buffers() since it is duplicating some code and also partially duplicating work of truncate_pagecache_range(), moreover the old implementation was much clearer. Now when the truncate_inode_pages_range() can handle truncating non page aligned regions we can use this to invalidate and zero out block aligned region of the punched out range and then use ext4_block_truncate_page() to zero the unaligned blocks on the start and end of the range. This will greatly simplify the punch hole code. Moreover after this commit we can get rid of the ext4_discard_partial_page_buffers() completely. We also introduce function ext4_prepare_punch_hole() to do come common operations before we attempt to do the actual punch hole on indirect or extent file which saves us some code duplication. This has been tested on ppc64 with 1k block size with fsx and xfstests without any problems. Signed-off-by: Lukas Czerner <lczerner@redhat.com> Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Diffstat (limited to 'fs/ext4')
-rw-r--r--fs/ext4/ext4.h2
-rw-r--r--fs/ext4/inode.c118
2 files changed, 48 insertions, 72 deletions
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 9f9719f08490..2d4b0aa74859 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -2100,6 +2100,8 @@ extern int ext4_block_truncate_page(handle_t *handle,
2100 struct address_space *mapping, loff_t from); 2100 struct address_space *mapping, loff_t from);
2101extern int ext4_block_zero_page_range(handle_t *handle, 2101extern int ext4_block_zero_page_range(handle_t *handle,
2102 struct address_space *mapping, loff_t from, loff_t length); 2102 struct address_space *mapping, loff_t from, loff_t length);
2103extern int ext4_zero_partial_blocks(handle_t *handle, struct inode *inode,
2104 loff_t lstart, loff_t lend);
2103extern int ext4_discard_partial_page_buffers(handle_t *handle, 2105extern int ext4_discard_partial_page_buffers(handle_t *handle,
2104 struct address_space *mapping, loff_t from, 2106 struct address_space *mapping, loff_t from,
2105 loff_t length, int flags); 2107 loff_t length, int flags);
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index a7f6b3194467..3acf353493d4 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -3693,6 +3693,41 @@ unlock:
3693 return err; 3693 return err;
3694} 3694}
3695 3695
3696int ext4_zero_partial_blocks(handle_t *handle, struct inode *inode,
3697 loff_t lstart, loff_t length)
3698{
3699 struct super_block *sb = inode->i_sb;
3700 struct address_space *mapping = inode->i_mapping;
3701 unsigned partial = lstart & (sb->s_blocksize - 1);
3702 ext4_fsblk_t start, end;
3703 loff_t byte_end = (lstart + length - 1);
3704 int err = 0;
3705
3706 start = lstart >> sb->s_blocksize_bits;
3707 end = byte_end >> sb->s_blocksize_bits;
3708
3709 /* Handle partial zero within the single block */
3710 if (start == end) {
3711 err = ext4_block_zero_page_range(handle, mapping,
3712 lstart, length);
3713 return err;
3714 }
3715 /* Handle partial zero out on the start of the range */
3716 if (partial) {
3717 err = ext4_block_zero_page_range(handle, mapping,
3718 lstart, sb->s_blocksize);
3719 if (err)
3720 return err;
3721 }
3722 /* Handle partial zero out on the end of the range */
3723 partial = byte_end & (sb->s_blocksize - 1);
3724 if (partial != sb->s_blocksize - 1)
3725 err = ext4_block_zero_page_range(handle, mapping,
3726 byte_end - partial,
3727 partial + 1);
3728 return err;
3729}
3730
3696int ext4_can_truncate(struct inode *inode) 3731int ext4_can_truncate(struct inode *inode)
3697{ 3732{
3698 if (S_ISREG(inode->i_mode)) 3733 if (S_ISREG(inode->i_mode))
@@ -3721,8 +3756,7 @@ int ext4_punch_hole(struct file *file, loff_t offset, loff_t length)
3721 struct super_block *sb = inode->i_sb; 3756 struct super_block *sb = inode->i_sb;
3722 ext4_lblk_t first_block, stop_block; 3757 ext4_lblk_t first_block, stop_block;
3723 struct address_space *mapping = inode->i_mapping; 3758 struct address_space *mapping = inode->i_mapping;
3724 loff_t first_page, last_page, page_len; 3759 loff_t first_block_offset, last_block_offset;
3725 loff_t first_page_offset, last_page_offset;
3726 handle_t *handle; 3760 handle_t *handle;
3727 unsigned int credits; 3761 unsigned int credits;
3728 int ret = 0; 3762 int ret = 0;
@@ -3773,17 +3807,13 @@ int ext4_punch_hole(struct file *file, loff_t offset, loff_t length)
3773 offset; 3807 offset;
3774 } 3808 }
3775 3809
3776 first_page = (offset + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; 3810 first_block_offset = round_up(offset, sb->s_blocksize);
3777 last_page = (offset + length) >> PAGE_CACHE_SHIFT; 3811 last_block_offset = round_down((offset + length), sb->s_blocksize) - 1;
3778 3812
3779 first_page_offset = first_page << PAGE_CACHE_SHIFT; 3813 /* Now release the pages and zero block aligned part of pages*/
3780 last_page_offset = last_page << PAGE_CACHE_SHIFT; 3814 if (last_block_offset > first_block_offset)
3781 3815 truncate_pagecache_range(inode, first_block_offset,
3782 /* Now release the pages */ 3816 last_block_offset);
3783 if (last_page_offset > first_page_offset) {
3784 truncate_pagecache_range(inode, first_page_offset,
3785 last_page_offset - 1);
3786 }
3787 3817
3788 /* Wait all existing dio workers, newcomers will block on i_mutex */ 3818 /* Wait all existing dio workers, newcomers will block on i_mutex */
3789 ext4_inode_block_unlocked_dio(inode); 3819 ext4_inode_block_unlocked_dio(inode);
@@ -3803,66 +3833,10 @@ int ext4_punch_hole(struct file *file, loff_t offset, loff_t length)
3803 goto out_dio; 3833 goto out_dio;
3804 } 3834 }
3805 3835
3806 /* 3836 ret = ext4_zero_partial_blocks(handle, inode, offset,
3807 * Now we need to zero out the non-page-aligned data in the 3837 length);
3808 * pages at the start and tail of the hole, and unmap the 3838 if (ret)
3809 * buffer heads for the block aligned regions of the page that 3839 goto out_stop;
3810 * were completely zeroed.
3811 */
3812 if (first_page > last_page) {
3813 /*
3814 * If the file space being truncated is contained
3815 * within a page just zero out and unmap the middle of
3816 * that page
3817 */
3818 ret = ext4_discard_partial_page_buffers(handle,
3819 mapping, offset, length, 0);
3820
3821 if (ret)
3822 goto out_stop;
3823 } else {
3824 /*
3825 * zero out and unmap the partial page that contains
3826 * the start of the hole
3827 */
3828 page_len = first_page_offset - offset;
3829 if (page_len > 0) {
3830 ret = ext4_discard_partial_page_buffers(handle, mapping,
3831 offset, page_len, 0);
3832 if (ret)
3833 goto out_stop;
3834 }
3835
3836 /*
3837 * zero out and unmap the partial page that contains
3838 * the end of the hole
3839 */
3840 page_len = offset + length - last_page_offset;
3841 if (page_len > 0) {
3842 ret = ext4_discard_partial_page_buffers(handle, mapping,
3843 last_page_offset, page_len, 0);
3844 if (ret)
3845 goto out_stop;
3846 }
3847 }
3848
3849 /*
3850 * If i_size is contained in the last page, we need to
3851 * unmap and zero the partial page after i_size
3852 */
3853 if (inode->i_size >> PAGE_CACHE_SHIFT == last_page &&
3854 inode->i_size % PAGE_CACHE_SIZE != 0) {
3855 page_len = PAGE_CACHE_SIZE -
3856 (inode->i_size & (PAGE_CACHE_SIZE - 1));
3857
3858 if (page_len > 0) {
3859 ret = ext4_discard_partial_page_buffers(handle,
3860 mapping, inode->i_size, page_len, 0);
3861
3862 if (ret)
3863 goto out_stop;
3864 }
3865 }
3866 3840
3867 first_block = (offset + sb->s_blocksize - 1) >> 3841 first_block = (offset + sb->s_blocksize - 1) >>
3868 EXT4_BLOCK_SIZE_BITS(sb); 3842 EXT4_BLOCK_SIZE_BITS(sb);