update atomically-set-inode-flags
[ext4-patch-queue.git] / update-i_size-after-the-preallocation
blobfcb3d7c7d8f73d5c8ec06e78795f0e1a4b72b7f6
1 ext4: Update inode i_size after the preallocation
3 From: Lukas Czerner <lczerner@redhat.com>
5 Currently in ext4_fallocate we would update inode size, c_time and sync
6 the file with every partial allocation which is entirely unnecessary. It
7 is true that if the crash happens in the middle of truncate we might end
8 up with unchanged i size, or c_time which I do not think is really a
9 problem - it does not mean file system corruption in any way. Note that
10 xfs is doing things the same way e.g. update all of the mentioned after
11 the allocation is done.
13 This commit moves all the updates after the allocation is done. In
14 addition we also need to change m_time as not only inode has been change
15 bot also data regions might have changed (unwritten extents). However
16 m_time will be only updated when i_size changed.
18 Also we do not need to be paranoid about changing the c_time only if the
19 actual allocation have happened, we can change it even if we try to
20 allocate only to find out that there are already block allocated. It's
21 not really a big deal and it will save us some additional complexity.
23 Also use ext4_debug, instead of ext4_warning in #ifdef EXT4FS_DEBUG
24 section.
26 Signed-off-by: Lukas Czerner <lczerner@redhat.com>
27 Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>-
29 v3: Do not remove the code to set EXT4_INODE_EOFBLOCKS flag
31  fs/ext4/extents.c | 96 ++++++++++++++++++++++++-------------------------------
32  1 file changed, 42 insertions(+), 54 deletions(-)
34 diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
35 index 202e8e3..ce3d371 100644
36 --- a/fs/ext4/extents.c
37 +++ b/fs/ext4/extents.c
38 @@ -4546,36 +4546,6 @@ retry:
39         ext4_std_error(inode->i_sb, err);
40  }
42 -static void ext4_falloc_update_inode(struct inode *inode,
43 -                               int mode, loff_t new_size, int update_ctime)
45 -       struct timespec now;
47 -       if (update_ctime) {
48 -               now = current_fs_time(inode->i_sb);
49 -               if (!timespec_equal(&inode->i_ctime, &now))
50 -                       inode->i_ctime = now;
51 -       }
52 -       /*
53 -        * Update only when preallocation was requested beyond
54 -        * the file size.
55 -        */
56 -       if (!(mode & FALLOC_FL_KEEP_SIZE)) {
57 -               if (new_size > i_size_read(inode))
58 -                       i_size_write(inode, new_size);
59 -               if (new_size > EXT4_I(inode)->i_disksize)
60 -                       ext4_update_i_disksize(inode, new_size);
61 -       } else {
62 -               /*
63 -                * Mark that we allocate beyond EOF so the subsequent truncate
64 -                * can proceed even if the new size is the same as i_size.
65 -                */
66 -               if (new_size > i_size_read(inode))
67 -                       ext4_set_inode_flag(inode, EXT4_INODE_EOFBLOCKS);
68 -       }
72  /*
73   * preallocate space for a file. This implements ext4's fallocate file
74   * operation, which gets called from sys_fallocate system call.
75 @@ -4587,13 +4557,14 @@ long ext4_fallocate(struct file *file, int mode, loff_t offset, loff_t len)
76  {
77         struct inode *inode = file_inode(file);
78         handle_t *handle;
79 -       loff_t new_size;
80 +       loff_t new_size = 0;
81         unsigned int max_blocks;
82         int ret = 0;
83         int ret2 = 0;
84         int retries = 0;
85         int flags;
86         struct ext4_map_blocks map;
87 +       struct timespec tv;
88         unsigned int credits, blkbits = inode->i_blkbits;
90         /* Return error if mode is not supported */
91 @@ -4631,12 +4602,15 @@ long ext4_fallocate(struct file *file, int mode, loff_t offset, loff_t len)
92          */
93         credits = ext4_chunk_trans_blocks(inode, max_blocks);
94         mutex_lock(&inode->i_mutex);
95 -       ret = inode_newsize_ok(inode, (len + offset));
96 -       if (ret) {
97 -               mutex_unlock(&inode->i_mutex);
98 -               trace_ext4_fallocate_exit(inode, offset, max_blocks, ret);
99 -               return ret;
101 +       if (!(mode & FALLOC_FL_KEEP_SIZE) &&
102 +            offset + len > i_size_read(inode)) {
103 +               new_size = offset + len;
104 +               ret = inode_newsize_ok(inode, new_size);
105 +               if (ret)
106 +                       goto out;
107         }
109         flags = EXT4_GET_BLOCKS_CREATE_UNINIT_EXT;
110         if (mode & FALLOC_FL_KEEP_SIZE)
111                 flags |= EXT4_GET_BLOCKS_KEEP_SIZE;
112 @@ -4660,28 +4634,14 @@ retry:
113                 }
114                 ret = ext4_map_blocks(handle, inode, &map, flags);
115                 if (ret <= 0) {
116 -#ifdef EXT4FS_DEBUG
117 -                       ext4_warning(inode->i_sb,
118 -                                    "inode #%lu: block %u: len %u: "
119 -                                    "ext4_ext_map_blocks returned %d",
120 -                                    inode->i_ino, map.m_lblk,
121 -                                    map.m_len, ret);
122 -#endif
123 +                       ext4_debug("inode #%lu: block %u: len %u: "
124 +                                  "ext4_ext_map_blocks returned %d",
125 +                                  inode->i_ino, map.m_lblk,
126 +                                  map.m_len, ret);
127                         ext4_mark_inode_dirty(handle, inode);
128                         ret2 = ext4_journal_stop(handle);
129                         break;
130                 }
131 -               if ((map.m_lblk + ret) >= (EXT4_BLOCK_ALIGN(offset + len,
132 -                                               blkbits) >> blkbits))
133 -                       new_size = offset + len;
134 -               else
135 -                       new_size = ((loff_t) map.m_lblk + ret) << blkbits;
137 -               ext4_falloc_update_inode(inode, mode, new_size,
138 -                                        (map.m_flags & EXT4_MAP_NEW));
139 -               ext4_mark_inode_dirty(handle, inode);
140 -               if ((file->f_flags & O_SYNC) && ret >= max_blocks)
141 -                       ext4_handle_sync(handle);
142                 ret2 = ext4_journal_stop(handle);
143                 if (ret2)
144                         break;
145 @@ -4691,6 +4651,34 @@ retry:
146                 ret = 0;
147                 goto retry;
148         }
150 +       handle = ext4_journal_start(inode, EXT4_HT_INODE, 2);
151 +       if (IS_ERR(handle))
152 +               goto out;
154 +       tv = inode->i_ctime = ext4_current_time(inode);
156 +       if (ret > 0 && new_size) {
157 +               if (new_size > i_size_read(inode)) {
158 +                       i_size_write(inode, new_size);
159 +                       inode->i_mtime = tv;
160 +               }
161 +               if (new_size > EXT4_I(inode)->i_disksize)
162 +                       ext4_update_i_disksize(inode, new_size);
163 +       } else if (ret > 0 && !new_size) {
164 +               /*
165 +               * Mark that we allocate beyond EOF so the subsequent truncate
166 +               * can proceed even if the new size is the same as i_size.
167 +               */
168 +               if ((offset + len) > i_size_read(inode))
169 +                       ext4_set_inode_flag(inode, EXT4_INODE_EOFBLOCKS);
170 +       }
171 +       ext4_mark_inode_dirty(handle, inode);
172 +       if (file->f_flags & O_SYNC)
173 +               ext4_handle_sync(handle);
175 +       ext4_journal_stop(handle);
176 +out:
177         mutex_unlock(&inode->i_mutex);
178         trace_ext4_fallocate_exit(inode, offset, max_blocks,
179                                 ret > 0 ? ret2 : ret);