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 | |
| 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
| -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); |
