btrfs: introduce btrfs_write_check()
authorGoldwyn Rodrigues <rgoldwyn@suse.com>
Thu, 24 Sep 2020 16:39:15 +0000 (11:39 -0500)
committerDavid Sterba <dsterba@suse.com>
Tue, 8 Dec 2020 14:53:47 +0000 (15:53 +0100)
btrfs_write_check() checks write parameters in one place before
beginning a write. This does away with inode_unlock() after every check.
In the later patches, it will help push inode_lock/unlock() in buffered
and direct write functions respectively.

generic_write_checks needs to be called before as it could truncate
iov_iter and its return used as count.

Signed-off-by: Goldwyn Rodrigues <rgoldwyn@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/file.c

index f5d02b4cdc05948c48f1248b32f5d1deb1d1578a..b30ce769ae8b0f8a721fc9e26b257d39ac9d3824 100644 (file)
@@ -1564,6 +1564,79 @@ void btrfs_check_nocow_unlock(struct btrfs_inode *inode)
        btrfs_drew_write_unlock(&inode->root->snapshot_lock);
 }
 
+static void update_time_for_write(struct inode *inode)
+{
+       struct timespec64 now;
+
+       if (IS_NOCMTIME(inode))
+               return;
+
+       now = current_time(inode);
+       if (!timespec64_equal(&inode->i_mtime, &now))
+               inode->i_mtime = now;
+
+       if (!timespec64_equal(&inode->i_ctime, &now))
+               inode->i_ctime = now;
+
+       if (IS_I_VERSION(inode))
+               inode_inc_iversion(inode);
+}
+
+static int btrfs_write_check(struct kiocb *iocb, struct iov_iter *from,
+                            size_t count)
+{
+       struct file *file = iocb->ki_filp;
+       struct inode *inode = file_inode(file);
+       struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
+       loff_t pos = iocb->ki_pos;
+       int ret;
+       loff_t oldsize;
+       loff_t start_pos;
+
+       if (iocb->ki_flags & IOCB_NOWAIT) {
+               size_t nocow_bytes = count;
+
+               /* We will allocate space in case nodatacow is not set, so bail */
+               if (check_nocow_nolock(BTRFS_I(inode), pos, &nocow_bytes) <= 0)
+                       return -EAGAIN;
+               /*
+                * There are holes in the range or parts of the range that must
+                * be COWed (shared extents, RO block groups, etc), so just bail
+                * out.
+                */
+               if (nocow_bytes < count)
+                       return -EAGAIN;
+       }
+
+       current->backing_dev_info = inode_to_bdi(inode);
+       ret = file_remove_privs(file);
+       if (ret)
+               return ret;
+
+       /*
+        * We reserve space for updating the inode when we reserve space for the
+        * extent we are going to write, so we will enospc out there.  We don't
+        * need to start yet another transaction to update the inode as we will
+        * update the inode when we finish writing whatever data we write.
+        */
+       update_time_for_write(inode);
+
+       start_pos = round_down(pos, fs_info->sectorsize);
+       oldsize = i_size_read(inode);
+       if (start_pos > oldsize) {
+               /* Expand hole size to cover write data, preventing empty gap */
+               loff_t end_pos = round_up(pos + count, fs_info->sectorsize);
+
+               ret = btrfs_cont_expand(inode, oldsize, end_pos);
+               if (ret) {
+                       current->backing_dev_info = NULL;
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+
 static noinline ssize_t btrfs_buffered_write(struct kiocb *iocb,
                                               struct iov_iter *i)
 {
@@ -1873,24 +1946,6 @@ out:
        return written ? written : err;
 }
 
-static void update_time_for_write(struct inode *inode)
-{
-       struct timespec64 now;
-
-       if (IS_NOCMTIME(inode))
-               return;
-
-       now = current_time(inode);
-       if (!timespec64_equal(&inode->i_mtime, &now))
-               inode->i_mtime = now;
-
-       if (!timespec64_equal(&inode->i_ctime, &now))
-               inode->i_ctime = now;
-
-       if (IS_I_VERSION(inode))
-               inode_inc_iversion(inode);
-}
-
 static ssize_t btrfs_file_write_iter(struct kiocb *iocb,
                                    struct iov_iter *from)
 {
@@ -1898,14 +1953,9 @@ static ssize_t btrfs_file_write_iter(struct kiocb *iocb,
        struct inode *inode = file_inode(file);
        struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
        struct btrfs_root *root = BTRFS_I(inode)->root;
-       u64 start_pos;
-       u64 end_pos;
        ssize_t num_written = 0;
        const bool sync = iocb->ki_flags & IOCB_DSYNC;
        ssize_t err;
-       loff_t pos;
-       size_t count;
-       loff_t oldsize;
 
        /*
         * If the fs flips readonly due to some impossible error, although we
@@ -1932,57 +1982,10 @@ static ssize_t btrfs_file_write_iter(struct kiocb *iocb,
                return err;
        }
 
-       pos = iocb->ki_pos;
-       count = iov_iter_count(from);
-       if (iocb->ki_flags & IOCB_NOWAIT) {
-               size_t nocow_bytes = count;
-
-               /*
-                * We will allocate space in case nodatacow is not set,
-                * so bail
-                */
-               if (check_nocow_nolock(BTRFS_I(inode), pos, &nocow_bytes)
-                   <= 0) {
-                       inode_unlock(inode);
-                       return -EAGAIN;
-               }
-               /*
-                * There are holes in the range or parts of the range that must
-                * be COWed (shared extents, RO block groups, etc), so just bail
-                * out.
-                */
-               if (nocow_bytes < count) {
-                       inode_unlock(inode);
-                       return -EAGAIN;
-               }
-       }
-
-       current->backing_dev_info = inode_to_bdi(inode);
-       err = file_remove_privs(file);
-       if (err) {
+       err = btrfs_write_check(iocb, from, err);
+       if (err < 0) {
                inode_unlock(inode);
-               goto out;
-       }
-
-       /*
-        * We reserve space for updating the inode when we reserve space for the
-        * extent we are going to write, so we will enospc out there.  We don't
-        * need to start yet another transaction to update the inode as we will
-        * update the inode when we finish writing whatever data we write.
-        */
-       update_time_for_write(inode);
-
-       start_pos = round_down(pos, fs_info->sectorsize);
-       oldsize = i_size_read(inode);
-       if (start_pos > oldsize) {
-               /* Expand hole size to cover write data, preventing empty gap */
-               end_pos = round_up(pos + count,
-                                  fs_info->sectorsize);
-               err = btrfs_cont_expand(inode, oldsize, end_pos);
-               if (err) {
-                       inode_unlock(inode);
-                       goto out;
-               }
+               return err;
        }
 
        if (sync)
@@ -2042,7 +2045,7 @@ static ssize_t btrfs_file_write_iter(struct kiocb *iocb,
 
        if (sync)
                atomic_dec(&BTRFS_I(inode)->sync_writers);
-out:
+
        current->backing_dev_info = NULL;
        return num_written ? num_written : err;
 }