Skip to content

Commit f282ac1

Browse files
Lukas Czernertytso
authored andcommitted
ext4: Update inode i_size after the preallocation
Currently in ext4_fallocate we would update inode size, c_time and sync the file with every partial allocation which is entirely unnecessary. It is true that if the crash happens in the middle of truncate we might end up with unchanged i size, or c_time which I do not think is really a problem - it does not mean file system corruption in any way. Note that xfs is doing things the same way e.g. update all of the mentioned after the allocation is done. This commit moves all the updates after the allocation is done. In addition we also need to change m_time as not only inode has been change bot also data regions might have changed (unwritten extents). However m_time will be only updated when i_size changed. Also we do not need to be paranoid about changing the c_time only if the actual allocation have happened, we can change it even if we try to allocate only to find out that there are already block allocated. It's not really a big deal and it will save us some additional complexity. Also use ext4_debug, instead of ext4_warning in #ifdef EXT4FS_DEBUG section. Signed-off-by: Lukas Czerner <lczerner@redhat.com> Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>- -- v3: Do not remove the code to set EXT4_INODE_EOFBLOCKS flag fs/ext4/extents.c | 96 ++++++++++++++++++++++++------------------------------- 1 file changed, 42 insertions(+), 54 deletions(-)
1 parent c063449 commit f282ac1

File tree

1 file changed

+42
-54
lines changed

1 file changed

+42
-54
lines changed

fs/ext4/extents.c

Lines changed: 42 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -4546,36 +4546,6 @@ void ext4_ext_truncate(handle_t *handle, struct inode *inode)
45464546
ext4_std_error(inode->i_sb, err);
45474547
}
45484548

4549-
static void ext4_falloc_update_inode(struct inode *inode,
4550-
int mode, loff_t new_size, int update_ctime)
4551-
{
4552-
struct timespec now;
4553-
4554-
if (update_ctime) {
4555-
now = current_fs_time(inode->i_sb);
4556-
if (!timespec_equal(&inode->i_ctime, &now))
4557-
inode->i_ctime = now;
4558-
}
4559-
/*
4560-
* Update only when preallocation was requested beyond
4561-
* the file size.
4562-
*/
4563-
if (!(mode & FALLOC_FL_KEEP_SIZE)) {
4564-
if (new_size > i_size_read(inode))
4565-
i_size_write(inode, new_size);
4566-
if (new_size > EXT4_I(inode)->i_disksize)
4567-
ext4_update_i_disksize(inode, new_size);
4568-
} else {
4569-
/*
4570-
* Mark that we allocate beyond EOF so the subsequent truncate
4571-
* can proceed even if the new size is the same as i_size.
4572-
*/
4573-
if (new_size > i_size_read(inode))
4574-
ext4_set_inode_flag(inode, EXT4_INODE_EOFBLOCKS);
4575-
}
4576-
4577-
}
4578-
45794549
/*
45804550
* preallocate space for a file. This implements ext4's fallocate file
45814551
* operation, which gets called from sys_fallocate system call.
@@ -4587,13 +4557,14 @@ long ext4_fallocate(struct file *file, int mode, loff_t offset, loff_t len)
45874557
{
45884558
struct inode *inode = file_inode(file);
45894559
handle_t *handle;
4590-
loff_t new_size;
4560+
loff_t new_size = 0;
45914561
unsigned int max_blocks;
45924562
int ret = 0;
45934563
int ret2 = 0;
45944564
int retries = 0;
45954565
int flags;
45964566
struct ext4_map_blocks map;
4567+
struct timespec tv;
45974568
unsigned int credits, blkbits = inode->i_blkbits;
45984569

45994570
/* Return error if mode is not supported */
@@ -4631,12 +4602,15 @@ long ext4_fallocate(struct file *file, int mode, loff_t offset, loff_t len)
46314602
*/
46324603
credits = ext4_chunk_trans_blocks(inode, max_blocks);
46334604
mutex_lock(&inode->i_mutex);
4634-
ret = inode_newsize_ok(inode, (len + offset));
4635-
if (ret) {
4636-
mutex_unlock(&inode->i_mutex);
4637-
trace_ext4_fallocate_exit(inode, offset, max_blocks, ret);
4638-
return ret;
4605+
4606+
if (!(mode & FALLOC_FL_KEEP_SIZE) &&
4607+
offset + len > i_size_read(inode)) {
4608+
new_size = offset + len;
4609+
ret = inode_newsize_ok(inode, new_size);
4610+
if (ret)
4611+
goto out;
46394612
}
4613+
46404614
flags = EXT4_GET_BLOCKS_CREATE_UNINIT_EXT;
46414615
if (mode & FALLOC_FL_KEEP_SIZE)
46424616
flags |= EXT4_GET_BLOCKS_KEEP_SIZE;
@@ -4660,28 +4634,14 @@ long ext4_fallocate(struct file *file, int mode, loff_t offset, loff_t len)
46604634
}
46614635
ret = ext4_map_blocks(handle, inode, &map, flags);
46624636
if (ret <= 0) {
4663-
#ifdef EXT4FS_DEBUG
4664-
ext4_warning(inode->i_sb,
4665-
"inode #%lu: block %u: len %u: "
4666-
"ext4_ext_map_blocks returned %d",
4667-
inode->i_ino, map.m_lblk,
4668-
map.m_len, ret);
4669-
#endif
4637+
ext4_debug("inode #%lu: block %u: len %u: "
4638+
"ext4_ext_map_blocks returned %d",
4639+
inode->i_ino, map.m_lblk,
4640+
map.m_len, ret);
46704641
ext4_mark_inode_dirty(handle, inode);
46714642
ret2 = ext4_journal_stop(handle);
46724643
break;
46734644
}
4674-
if ((map.m_lblk + ret) >= (EXT4_BLOCK_ALIGN(offset + len,
4675-
blkbits) >> blkbits))
4676-
new_size = offset + len;
4677-
else
4678-
new_size = ((loff_t) map.m_lblk + ret) << blkbits;
4679-
4680-
ext4_falloc_update_inode(inode, mode, new_size,
4681-
(map.m_flags & EXT4_MAP_NEW));
4682-
ext4_mark_inode_dirty(handle, inode);
4683-
if ((file->f_flags & O_SYNC) && ret >= max_blocks)
4684-
ext4_handle_sync(handle);
46854645
ret2 = ext4_journal_stop(handle);
46864646
if (ret2)
46874647
break;
@@ -4691,6 +4651,34 @@ long ext4_fallocate(struct file *file, int mode, loff_t offset, loff_t len)
46914651
ret = 0;
46924652
goto retry;
46934653
}
4654+
4655+
handle = ext4_journal_start(inode, EXT4_HT_INODE, 2);
4656+
if (IS_ERR(handle))
4657+
goto out;
4658+
4659+
tv = inode->i_ctime = ext4_current_time(inode);
4660+
4661+
if (ret > 0 && new_size) {
4662+
if (new_size > i_size_read(inode)) {
4663+
i_size_write(inode, new_size);
4664+
inode->i_mtime = tv;
4665+
}
4666+
if (new_size > EXT4_I(inode)->i_disksize)
4667+
ext4_update_i_disksize(inode, new_size);
4668+
} else if (ret > 0 && !new_size) {
4669+
/*
4670+
* Mark that we allocate beyond EOF so the subsequent truncate
4671+
* can proceed even if the new size is the same as i_size.
4672+
*/
4673+
if ((offset + len) > i_size_read(inode))
4674+
ext4_set_inode_flag(inode, EXT4_INODE_EOFBLOCKS);
4675+
}
4676+
ext4_mark_inode_dirty(handle, inode);
4677+
if (file->f_flags & O_SYNC)
4678+
ext4_handle_sync(handle);
4679+
4680+
ext4_journal_stop(handle);
4681+
out:
46944682
mutex_unlock(&inode->i_mutex);
46954683
trace_ext4_fallocate_exit(inode, offset, max_blocks,
46964684
ret > 0 ? ret2 : ret);

0 commit comments

Comments
 (0)