aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorLukas Czerner <lczerner@redhat.com>2014-03-18 17:44:35 -0400
committerTheodore Ts'o <tytso@mit.edu>2014-03-18 17:44:35 -0400
commitf282ac19d86f0507e91759dcf3d15fcb3a964d2a (patch)
tree94b95749285fed3c2e50d4fea027fa0e1f122f8b /fs
parentc06344939422bbd032ac967223a7863de57496b5 (diff)
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(-)
Diffstat (limited to 'fs')
-rw-r--r--fs/ext4/extents.c96
1 files changed, 42 insertions, 54 deletions
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index e35f93b4cb13..e4be6b79121d 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -4546,36 +4546,6 @@ retry:
4546 ext4_std_error(inode->i_sb, err); 4546 ext4_std_error(inode->i_sb, err);
4547} 4547}
4548 4548
4549static 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
4579/* 4549/*
4580 * preallocate space for a file. This implements ext4's fallocate file 4550 * preallocate space for a file. This implements ext4's fallocate file
4581 * operation, which gets called from sys_fallocate system call. 4551 * 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)
4587{ 4557{
4588 struct inode *inode = file_inode(file); 4558 struct inode *inode = file_inode(file);
4589 handle_t *handle; 4559 handle_t *handle;
4590 loff_t new_size; 4560 loff_t new_size = 0;
4591 unsigned int max_blocks; 4561 unsigned int max_blocks;
4592 int ret = 0; 4562 int ret = 0;
4593 int ret2 = 0; 4563 int ret2 = 0;
4594 int retries = 0; 4564 int retries = 0;
4595 int flags; 4565 int flags;
4596 struct ext4_map_blocks map; 4566 struct ext4_map_blocks map;
4567 struct timespec tv;
4597 unsigned int credits, blkbits = inode->i_blkbits; 4568 unsigned int credits, blkbits = inode->i_blkbits;
4598 4569
4599 /* Return error if mode is not supported */ 4570 /* 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)
4631 */ 4602 */
4632 credits = ext4_chunk_trans_blocks(inode, max_blocks); 4603 credits = ext4_chunk_trans_blocks(inode, max_blocks);
4633 mutex_lock(&inode->i_mutex); 4604 mutex_lock(&inode->i_mutex);
4634 ret = inode_newsize_ok(inode, (len + offset)); 4605
4635 if (ret) { 4606 if (!(mode & FALLOC_FL_KEEP_SIZE) &&
4636 mutex_unlock(&inode->i_mutex); 4607 offset + len > i_size_read(inode)) {
4637 trace_ext4_fallocate_exit(inode, offset, max_blocks, ret); 4608 new_size = offset + len;
4638 return ret; 4609 ret = inode_newsize_ok(inode, new_size);
4610 if (ret)
4611 goto out;
4639 } 4612 }
4613
4640 flags = EXT4_GET_BLOCKS_CREATE_UNINIT_EXT; 4614 flags = EXT4_GET_BLOCKS_CREATE_UNINIT_EXT;
4641 if (mode & FALLOC_FL_KEEP_SIZE) 4615 if (mode & FALLOC_FL_KEEP_SIZE)
4642 flags |= EXT4_GET_BLOCKS_KEEP_SIZE; 4616 flags |= EXT4_GET_BLOCKS_KEEP_SIZE;
@@ -4660,28 +4634,14 @@ retry:
4660 } 4634 }
4661 ret = ext4_map_blocks(handle, inode, &map, flags); 4635 ret = ext4_map_blocks(handle, inode, &map, flags);
4662 if (ret <= 0) { 4636 if (ret <= 0) {
4663#ifdef EXT4FS_DEBUG 4637 ext4_debug("inode #%lu: block %u: len %u: "
4664 ext4_warning(inode->i_sb, 4638 "ext4_ext_map_blocks returned %d",
4665 "inode #%lu: block %u: len %u: " 4639 inode->i_ino, map.m_lblk,
4666 "ext4_ext_map_blocks returned %d", 4640 map.m_len, ret);
4667 inode->i_ino, map.m_lblk,
4668 map.m_len, ret);
4669#endif
4670 ext4_mark_inode_dirty(handle, inode); 4641 ext4_mark_inode_dirty(handle, inode);
4671 ret2 = ext4_journal_stop(handle); 4642 ret2 = ext4_journal_stop(handle);
4672 break; 4643 break;
4673 } 4644 }
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);
4685 ret2 = ext4_journal_stop(handle); 4645 ret2 = ext4_journal_stop(handle);
4686 if (ret2) 4646 if (ret2)
4687 break; 4647 break;
@@ -4691,6 +4651,34 @@ retry:
4691 ret = 0; 4651 ret = 0;
4692 goto retry; 4652 goto retry;
4693 } 4653 }
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);
4681out:
4694 mutex_unlock(&inode->i_mutex); 4682 mutex_unlock(&inode->i_mutex);
4695 trace_ext4_fallocate_exit(inode, offset, max_blocks, 4683 trace_ext4_fallocate_exit(inode, offset, max_blocks,
4696 ret > 0 ? ret2 : ret); 4684 ret > 0 ? ret2 : ret);