diff options
author | Dmitry Monakhov <dmonakhov@openvz.org> | 2014-08-27 18:40:00 -0400 |
---|---|---|
committer | Theodore Ts'o <tytso@mit.edu> | 2014-08-27 18:40:00 -0400 |
commit | c174e6d6979a04b7b77b93f244396be4b81f8bfb (patch) | |
tree | 2a4b4b6e339268948f04f0940f17a33e3bb72d88 /fs | |
parent | 69dc9536405213c1d545fcace1fc15c481d00aae (diff) |
ext4: fix transaction issues for ext4_fallocate and ext_zero_range
After commit f282ac19d86f we use different transactions for
preallocation and i_disksize update which result in complain from fsck
after power-failure. spotted by generic/019. IMHO this is regression
because fs becomes inconsistent, even more 'e2fsck -p' will no longer
works (which drives admins go crazy) Same transaction requirement
applies ctime,mtime updates
testcase: xfstest generic/019
Signed-off-by: Dmitry Monakhov <dmonakhov@openvz.org>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Cc: stable@vger.kernel.org
Diffstat (limited to 'fs')
-rw-r--r-- | fs/ext4/extents.c | 68 |
1 files changed, 35 insertions, 33 deletions
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index 0e9de2328bd2..74292a71b384 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c | |||
@@ -4665,7 +4665,8 @@ retry: | |||
4665 | } | 4665 | } |
4666 | 4666 | ||
4667 | static int ext4_alloc_file_blocks(struct file *file, ext4_lblk_t offset, | 4667 | static int ext4_alloc_file_blocks(struct file *file, ext4_lblk_t offset, |
4668 | ext4_lblk_t len, int flags, int mode) | 4668 | ext4_lblk_t len, loff_t new_size, |
4669 | int flags, int mode) | ||
4669 | { | 4670 | { |
4670 | struct inode *inode = file_inode(file); | 4671 | struct inode *inode = file_inode(file); |
4671 | handle_t *handle; | 4672 | handle_t *handle; |
@@ -4674,8 +4675,10 @@ static int ext4_alloc_file_blocks(struct file *file, ext4_lblk_t offset, | |||
4674 | int retries = 0; | 4675 | int retries = 0; |
4675 | struct ext4_map_blocks map; | 4676 | struct ext4_map_blocks map; |
4676 | unsigned int credits; | 4677 | unsigned int credits; |
4678 | loff_t epos; | ||
4677 | 4679 | ||
4678 | map.m_lblk = offset; | 4680 | map.m_lblk = offset; |
4681 | map.m_len = len; | ||
4679 | /* | 4682 | /* |
4680 | * Don't normalize the request if it can fit in one extent so | 4683 | * Don't normalize the request if it can fit in one extent so |
4681 | * that it doesn't get unnecessarily split into multiple | 4684 | * that it doesn't get unnecessarily split into multiple |
@@ -4690,9 +4693,7 @@ static int ext4_alloc_file_blocks(struct file *file, ext4_lblk_t offset, | |||
4690 | credits = ext4_chunk_trans_blocks(inode, len); | 4693 | credits = ext4_chunk_trans_blocks(inode, len); |
4691 | 4694 | ||
4692 | retry: | 4695 | retry: |
4693 | while (ret >= 0 && ret < len) { | 4696 | while (ret >= 0 && len) { |
4694 | map.m_lblk = map.m_lblk + ret; | ||
4695 | map.m_len = len = len - ret; | ||
4696 | handle = ext4_journal_start(inode, EXT4_HT_MAP_BLOCKS, | 4697 | handle = ext4_journal_start(inode, EXT4_HT_MAP_BLOCKS, |
4697 | credits); | 4698 | credits); |
4698 | if (IS_ERR(handle)) { | 4699 | if (IS_ERR(handle)) { |
@@ -4709,6 +4710,21 @@ retry: | |||
4709 | ret2 = ext4_journal_stop(handle); | 4710 | ret2 = ext4_journal_stop(handle); |
4710 | break; | 4711 | break; |
4711 | } | 4712 | } |
4713 | map.m_lblk += ret; | ||
4714 | map.m_len = len = len - ret; | ||
4715 | epos = (loff_t)map.m_lblk << inode->i_blkbits; | ||
4716 | inode->i_ctime = ext4_current_time(inode); | ||
4717 | if (new_size) { | ||
4718 | if (epos > new_size) | ||
4719 | epos = new_size; | ||
4720 | if (ext4_update_inode_size(inode, epos) & 0x1) | ||
4721 | inode->i_mtime = inode->i_ctime; | ||
4722 | } else { | ||
4723 | if (epos > inode->i_size) | ||
4724 | ext4_set_inode_flag(inode, | ||
4725 | EXT4_INODE_EOFBLOCKS); | ||
4726 | } | ||
4727 | ext4_mark_inode_dirty(handle, inode); | ||
4712 | ret2 = ext4_journal_stop(handle); | 4728 | ret2 = ext4_journal_stop(handle); |
4713 | if (ret2) | 4729 | if (ret2) |
4714 | break; | 4730 | break; |
@@ -4732,7 +4748,7 @@ static long ext4_zero_range(struct file *file, loff_t offset, | |||
4732 | int ret = 0; | 4748 | int ret = 0; |
4733 | int flags; | 4749 | int flags; |
4734 | int credits; | 4750 | int credits; |
4735 | int partial; | 4751 | int partial_begin, partial_end; |
4736 | loff_t start, end; | 4752 | loff_t start, end; |
4737 | ext4_lblk_t lblk; | 4753 | ext4_lblk_t lblk; |
4738 | struct address_space *mapping = inode->i_mapping; | 4754 | struct address_space *mapping = inode->i_mapping; |
@@ -4772,7 +4788,8 @@ static long ext4_zero_range(struct file *file, loff_t offset, | |||
4772 | 4788 | ||
4773 | if (start < offset || end > offset + len) | 4789 | if (start < offset || end > offset + len) |
4774 | return -EINVAL; | 4790 | return -EINVAL; |
4775 | partial = (offset + len) & ((1 << blkbits) - 1); | 4791 | partial_begin = offset & ((1 << blkbits) - 1); |
4792 | partial_end = (offset + len) & ((1 << blkbits) - 1); | ||
4776 | 4793 | ||
4777 | lblk = start >> blkbits; | 4794 | lblk = start >> blkbits; |
4778 | max_blocks = (end >> blkbits); | 4795 | max_blocks = (end >> blkbits); |
@@ -4806,7 +4823,7 @@ static long ext4_zero_range(struct file *file, loff_t offset, | |||
4806 | * If we have a partial block after EOF we have to allocate | 4823 | * If we have a partial block after EOF we have to allocate |
4807 | * the entire block. | 4824 | * the entire block. |
4808 | */ | 4825 | */ |
4809 | if (partial) | 4826 | if (partial_end) |
4810 | max_blocks += 1; | 4827 | max_blocks += 1; |
4811 | } | 4828 | } |
4812 | 4829 | ||
@@ -4814,6 +4831,7 @@ static long ext4_zero_range(struct file *file, loff_t offset, | |||
4814 | 4831 | ||
4815 | /* Now release the pages and zero block aligned part of pages*/ | 4832 | /* Now release the pages and zero block aligned part of pages*/ |
4816 | truncate_pagecache_range(inode, start, end - 1); | 4833 | truncate_pagecache_range(inode, start, end - 1); |
4834 | inode->i_mtime = inode->i_ctime = ext4_current_time(inode); | ||
4817 | 4835 | ||
4818 | /* Wait all existing dio workers, newcomers will block on i_mutex */ | 4836 | /* Wait all existing dio workers, newcomers will block on i_mutex */ |
4819 | ext4_inode_block_unlocked_dio(inode); | 4837 | ext4_inode_block_unlocked_dio(inode); |
@@ -4826,11 +4844,14 @@ static long ext4_zero_range(struct file *file, loff_t offset, | |||
4826 | if (ret) | 4844 | if (ret) |
4827 | goto out_dio; | 4845 | goto out_dio; |
4828 | 4846 | ||
4829 | ret = ext4_alloc_file_blocks(file, lblk, max_blocks, flags, | 4847 | ret = ext4_alloc_file_blocks(file, lblk, max_blocks, new_size, |
4830 | mode); | 4848 | flags, mode); |
4831 | if (ret) | 4849 | if (ret) |
4832 | goto out_dio; | 4850 | goto out_dio; |
4833 | } | 4851 | } |
4852 | if (!partial_begin && !partial_end) | ||
4853 | goto out_dio; | ||
4854 | |||
4834 | /* | 4855 | /* |
4835 | * In worst case we have to writeout two nonadjacent unwritten | 4856 | * In worst case we have to writeout two nonadjacent unwritten |
4836 | * blocks and update the inode | 4857 | * blocks and update the inode |
@@ -4856,7 +4877,6 @@ static long ext4_zero_range(struct file *file, loff_t offset, | |||
4856 | if ((offset + len) > i_size_read(inode)) | 4877 | if ((offset + len) > i_size_read(inode)) |
4857 | ext4_set_inode_flag(inode, EXT4_INODE_EOFBLOCKS); | 4878 | ext4_set_inode_flag(inode, EXT4_INODE_EOFBLOCKS); |
4858 | } | 4879 | } |
4859 | |||
4860 | ext4_mark_inode_dirty(handle, inode); | 4880 | ext4_mark_inode_dirty(handle, inode); |
4861 | 4881 | ||
4862 | /* Zero out partial block at the edges of the range */ | 4882 | /* Zero out partial block at the edges of the range */ |
@@ -4883,7 +4903,6 @@ out_mutex: | |||
4883 | long ext4_fallocate(struct file *file, int mode, loff_t offset, loff_t len) | 4903 | long ext4_fallocate(struct file *file, int mode, loff_t offset, loff_t len) |
4884 | { | 4904 | { |
4885 | struct inode *inode = file_inode(file); | 4905 | struct inode *inode = file_inode(file); |
4886 | handle_t *handle; | ||
4887 | loff_t new_size = 0; | 4906 | loff_t new_size = 0; |
4888 | unsigned int max_blocks; | 4907 | unsigned int max_blocks; |
4889 | int ret = 0; | 4908 | int ret = 0; |
@@ -4939,32 +4958,15 @@ long ext4_fallocate(struct file *file, int mode, loff_t offset, loff_t len) | |||
4939 | goto out; | 4958 | goto out; |
4940 | } | 4959 | } |
4941 | 4960 | ||
4942 | ret = ext4_alloc_file_blocks(file, lblk, max_blocks, flags, mode); | 4961 | ret = ext4_alloc_file_blocks(file, lblk, max_blocks, new_size, |
4962 | flags, mode); | ||
4943 | if (ret) | 4963 | if (ret) |
4944 | goto out; | 4964 | goto out; |
4945 | 4965 | ||
4946 | handle = ext4_journal_start(inode, EXT4_HT_INODE, 2); | 4966 | if (file->f_flags & O_SYNC && EXT4_SB(inode->i_sb)->s_journal) { |
4947 | if (IS_ERR(handle)) | 4967 | ret = jbd2_complete_transaction(EXT4_SB(inode->i_sb)->s_journal, |
4948 | goto out; | 4968 | EXT4_I(inode)->i_sync_tid); |
4949 | |||
4950 | inode->i_ctime = ext4_current_time(inode); | ||
4951 | |||
4952 | if (new_size) { | ||
4953 | if (ext4_update_inode_size(inode, new_size) & 0x1) | ||
4954 | inode->i_mtime = inode->i_ctime; | ||
4955 | } else { | ||
4956 | /* | ||
4957 | * Mark that we allocate beyond EOF so the subsequent truncate | ||
4958 | * can proceed even if the new size is the same as i_size. | ||
4959 | */ | ||
4960 | if ((offset + len) > i_size_read(inode)) | ||
4961 | ext4_set_inode_flag(inode, EXT4_INODE_EOFBLOCKS); | ||
4962 | } | 4969 | } |
4963 | ext4_mark_inode_dirty(handle, inode); | ||
4964 | if (file->f_flags & O_SYNC) | ||
4965 | ext4_handle_sync(handle); | ||
4966 | |||
4967 | ext4_journal_stop(handle); | ||
4968 | out: | 4970 | out: |
4969 | mutex_unlock(&inode->i_mutex); | 4971 | mutex_unlock(&inode->i_mutex); |
4970 | trace_ext4_fallocate_exit(inode, offset, max_blocks, ret); | 4972 | trace_ext4_fallocate_exit(inode, offset, max_blocks, ret); |