Commit d89b2f76 authored by Dmitry Monakhov's avatar Dmitry Monakhov Committed by Greg Kroah-Hartman

ext4: fix transaction issues for ext4_fallocate and ext_zero_range

commit c174e6d6 upstream.

After commit f282ac19 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: default avatarDmitry Monakhov <dmonakhov@openvz.org>
Signed-off-by: default avatarTheodore Ts'o <tytso@mit.edu>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 69fa118c
...@@ -4664,7 +4664,8 @@ void ext4_ext_truncate(handle_t *handle, struct inode *inode) ...@@ -4664,7 +4664,8 @@ void ext4_ext_truncate(handle_t *handle, struct inode *inode)
} }
static int ext4_alloc_file_blocks(struct file *file, ext4_lblk_t offset, static int ext4_alloc_file_blocks(struct file *file, ext4_lblk_t offset,
ext4_lblk_t len, int flags, int mode) ext4_lblk_t len, loff_t new_size,
int flags, int mode)
{ {
struct inode *inode = file_inode(file); struct inode *inode = file_inode(file);
handle_t *handle; handle_t *handle;
...@@ -4673,8 +4674,10 @@ static int ext4_alloc_file_blocks(struct file *file, ext4_lblk_t offset, ...@@ -4673,8 +4674,10 @@ static int ext4_alloc_file_blocks(struct file *file, ext4_lblk_t offset,
int retries = 0; int retries = 0;
struct ext4_map_blocks map; struct ext4_map_blocks map;
unsigned int credits; unsigned int credits;
loff_t epos;
map.m_lblk = offset; map.m_lblk = offset;
map.m_len = len;
/* /*
* Don't normalize the request if it can fit in one extent so * Don't normalize the request if it can fit in one extent so
* that it doesn't get unnecessarily split into multiple * that it doesn't get unnecessarily split into multiple
...@@ -4689,9 +4692,7 @@ static int ext4_alloc_file_blocks(struct file *file, ext4_lblk_t offset, ...@@ -4689,9 +4692,7 @@ static int ext4_alloc_file_blocks(struct file *file, ext4_lblk_t offset,
credits = ext4_chunk_trans_blocks(inode, len); credits = ext4_chunk_trans_blocks(inode, len);
retry: retry:
while (ret >= 0 && ret < len) { while (ret >= 0 && len) {
map.m_lblk = map.m_lblk + ret;
map.m_len = len = len - ret;
handle = ext4_journal_start(inode, EXT4_HT_MAP_BLOCKS, handle = ext4_journal_start(inode, EXT4_HT_MAP_BLOCKS,
credits); credits);
if (IS_ERR(handle)) { if (IS_ERR(handle)) {
...@@ -4708,6 +4709,21 @@ static int ext4_alloc_file_blocks(struct file *file, ext4_lblk_t offset, ...@@ -4708,6 +4709,21 @@ static int ext4_alloc_file_blocks(struct file *file, ext4_lblk_t offset,
ret2 = ext4_journal_stop(handle); ret2 = ext4_journal_stop(handle);
break; break;
} }
map.m_lblk += ret;
map.m_len = len = len - ret;
epos = (loff_t)map.m_lblk << inode->i_blkbits;
inode->i_ctime = ext4_current_time(inode);
if (new_size) {
if (epos > new_size)
epos = new_size;
if (ext4_update_inode_size(inode, epos) & 0x1)
inode->i_mtime = inode->i_ctime;
} else {
if (epos > inode->i_size)
ext4_set_inode_flag(inode,
EXT4_INODE_EOFBLOCKS);
}
ext4_mark_inode_dirty(handle, inode);
ret2 = ext4_journal_stop(handle); ret2 = ext4_journal_stop(handle);
if (ret2) if (ret2)
break; break;
...@@ -4731,7 +4747,7 @@ static long ext4_zero_range(struct file *file, loff_t offset, ...@@ -4731,7 +4747,7 @@ static long ext4_zero_range(struct file *file, loff_t offset,
int ret = 0; int ret = 0;
int flags; int flags;
int credits; int credits;
int partial; int partial_begin, partial_end;
loff_t start, end; loff_t start, end;
ext4_lblk_t lblk; ext4_lblk_t lblk;
struct address_space *mapping = inode->i_mapping; struct address_space *mapping = inode->i_mapping;
...@@ -4771,7 +4787,8 @@ static long ext4_zero_range(struct file *file, loff_t offset, ...@@ -4771,7 +4787,8 @@ static long ext4_zero_range(struct file *file, loff_t offset,
if (start < offset || end > offset + len) if (start < offset || end > offset + len)
return -EINVAL; return -EINVAL;
partial = (offset + len) & ((1 << blkbits) - 1); partial_begin = offset & ((1 << blkbits) - 1);
partial_end = (offset + len) & ((1 << blkbits) - 1);
lblk = start >> blkbits; lblk = start >> blkbits;
max_blocks = (end >> blkbits); max_blocks = (end >> blkbits);
...@@ -4805,7 +4822,7 @@ static long ext4_zero_range(struct file *file, loff_t offset, ...@@ -4805,7 +4822,7 @@ static long ext4_zero_range(struct file *file, loff_t offset,
* If we have a partial block after EOF we have to allocate * If we have a partial block after EOF we have to allocate
* the entire block. * the entire block.
*/ */
if (partial) if (partial_end)
max_blocks += 1; max_blocks += 1;
} }
...@@ -4813,6 +4830,7 @@ static long ext4_zero_range(struct file *file, loff_t offset, ...@@ -4813,6 +4830,7 @@ static long ext4_zero_range(struct file *file, loff_t offset,
/* Now release the pages and zero block aligned part of pages*/ /* Now release the pages and zero block aligned part of pages*/
truncate_pagecache_range(inode, start, end - 1); truncate_pagecache_range(inode, start, end - 1);
inode->i_mtime = inode->i_ctime = ext4_current_time(inode);
/* Wait all existing dio workers, newcomers will block on i_mutex */ /* Wait all existing dio workers, newcomers will block on i_mutex */
ext4_inode_block_unlocked_dio(inode); ext4_inode_block_unlocked_dio(inode);
...@@ -4825,11 +4843,14 @@ static long ext4_zero_range(struct file *file, loff_t offset, ...@@ -4825,11 +4843,14 @@ static long ext4_zero_range(struct file *file, loff_t offset,
if (ret) if (ret)
goto out_dio; goto out_dio;
ret = ext4_alloc_file_blocks(file, lblk, max_blocks, flags, ret = ext4_alloc_file_blocks(file, lblk, max_blocks, new_size,
mode); flags, mode);
if (ret) if (ret)
goto out_dio; goto out_dio;
} }
if (!partial_begin && !partial_end)
goto out_dio;
/* /*
* In worst case we have to writeout two nonadjacent unwritten * In worst case we have to writeout two nonadjacent unwritten
* blocks and update the inode * blocks and update the inode
...@@ -4855,7 +4876,6 @@ static long ext4_zero_range(struct file *file, loff_t offset, ...@@ -4855,7 +4876,6 @@ static long ext4_zero_range(struct file *file, loff_t offset,
if ((offset + len) > i_size_read(inode)) if ((offset + len) > i_size_read(inode))
ext4_set_inode_flag(inode, EXT4_INODE_EOFBLOCKS); ext4_set_inode_flag(inode, EXT4_INODE_EOFBLOCKS);
} }
ext4_mark_inode_dirty(handle, inode); ext4_mark_inode_dirty(handle, inode);
/* Zero out partial block at the edges of the range */ /* Zero out partial block at the edges of the range */
...@@ -4882,7 +4902,6 @@ static long ext4_zero_range(struct file *file, loff_t offset, ...@@ -4882,7 +4902,6 @@ static long ext4_zero_range(struct file *file, loff_t offset,
long ext4_fallocate(struct file *file, int mode, loff_t offset, loff_t len) long ext4_fallocate(struct file *file, int mode, loff_t offset, loff_t len)
{ {
struct inode *inode = file_inode(file); struct inode *inode = file_inode(file);
handle_t *handle;
loff_t new_size = 0; loff_t new_size = 0;
unsigned int max_blocks; unsigned int max_blocks;
int ret = 0; int ret = 0;
...@@ -4938,32 +4957,15 @@ long ext4_fallocate(struct file *file, int mode, loff_t offset, loff_t len) ...@@ -4938,32 +4957,15 @@ long ext4_fallocate(struct file *file, int mode, loff_t offset, loff_t len)
goto out; goto out;
} }
ret = ext4_alloc_file_blocks(file, lblk, max_blocks, flags, mode); ret = ext4_alloc_file_blocks(file, lblk, max_blocks, new_size,
flags, mode);
if (ret) if (ret)
goto out; goto out;
handle = ext4_journal_start(inode, EXT4_HT_INODE, 2); if (file->f_flags & O_SYNC && EXT4_SB(inode->i_sb)->s_journal) {
if (IS_ERR(handle)) ret = jbd2_complete_transaction(EXT4_SB(inode->i_sb)->s_journal,
goto out; EXT4_I(inode)->i_sync_tid);
inode->i_ctime = ext4_current_time(inode);
if (new_size) {
if (ext4_update_inode_size(inode, new_size) & 0x1)
inode->i_mtime = inode->i_ctime;
} else {
/*
* Mark that we allocate beyond EOF so the subsequent truncate
* can proceed even if the new size is the same as i_size.
*/
if ((offset + len) > i_size_read(inode))
ext4_set_inode_flag(inode, EXT4_INODE_EOFBLOCKS);
} }
ext4_mark_inode_dirty(handle, inode);
if (file->f_flags & O_SYNC)
ext4_handle_sync(handle);
ext4_journal_stop(handle);
out: out:
mutex_unlock(&inode->i_mutex); mutex_unlock(&inode->i_mutex);
trace_ext4_fallocate_exit(inode, offset, max_blocks, ret); trace_ext4_fallocate_exit(inode, offset, max_blocks, ret);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment