btrfs: push inode locking and unlocking into buffered/direct write
authorGoldwyn Rodrigues <rgoldwyn@suse.com>
Thu, 24 Sep 2020 16:39:17 +0000 (11:39 -0500)
committerDavid Sterba <dsterba@suse.com>
Tue, 8 Dec 2020 14:53:48 +0000 (15:53 +0100)
Push inode locking and unlocking closer to where we perform the I/O. For
this we need to move the write checks inside the respective functions as
well.

pos is evaluated after generic_write_checks because O_APPEND can change
iocb->ki_pos.

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

index fe1daa44f325b5048f8161883807090cd36b0b91..60cdad1b495284750a3b4d93a57fb2dba2983ea8 100644 (file)
@@ -1641,7 +1641,7 @@ static noinline ssize_t btrfs_buffered_write(struct kiocb *iocb,
                                               struct iov_iter *i)
 {
        struct file *file = iocb->ki_filp;
-       loff_t pos = iocb->ki_pos;
+       loff_t pos;
        struct inode *inode = file_inode(file);
        struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
        struct page **pages = NULL;
@@ -1651,18 +1651,37 @@ static noinline ssize_t btrfs_buffered_write(struct kiocb *iocb,
        u64 lockend;
        size_t num_written = 0;
        int nrptrs;
-       int ret = 0;
+       ssize_t ret;
        bool only_release_metadata = false;
        bool force_page_uptodate = false;
        loff_t old_isize = i_size_read(inode);
+       unsigned int ilock_flags = 0;
+
+       if (iocb->ki_flags & IOCB_NOWAIT)
+               ilock_flags |= BTRFS_ILOCK_TRY;
+
+       ret = btrfs_inode_lock(inode, ilock_flags);
+       if (ret < 0)
+               return ret;
 
+       ret = generic_write_checks(iocb, i);
+       if (ret <= 0)
+               goto out;
+
+       ret = btrfs_write_check(iocb, i, ret);
+       if (ret < 0)
+               goto out;
+
+       pos = iocb->ki_pos;
        nrptrs = min(DIV_ROUND_UP(iov_iter_count(i), PAGE_SIZE),
                        PAGE_SIZE / (sizeof(struct page *)));
        nrptrs = min(nrptrs, current->nr_dirtied_pause - current->nr_dirtied);
        nrptrs = max(nrptrs, 8);
        pages = kmalloc_array(nrptrs, sizeof(struct page *), GFP_KERNEL);
-       if (!pages)
-               return -ENOMEM;
+       if (!pages) {
+               ret = -ENOMEM;
+               goto out;
+       }
 
        while (iov_iter_count(i) > 0) {
                struct extent_state *cached_state = NULL;
@@ -1857,6 +1876,8 @@ again:
                pagecache_isize_extended(inode, old_isize, iocb->ki_pos);
                iocb->ki_pos += num_written;
        }
+out:
+       btrfs_inode_unlock(inode, ilock_flags);
        return num_written ? num_written : ret;
 }
 
@@ -1879,15 +1900,39 @@ static ssize_t btrfs_direct_write(struct kiocb *iocb, struct iov_iter *from)
        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;
+       loff_t pos;
        ssize_t written = 0;
        bool relock = false;
        ssize_t written_buffered;
        loff_t endbyte;
-       int err;
+       ssize_t err;
+       unsigned int ilock_flags = 0;
+
+       if (iocb->ki_flags & IOCB_NOWAIT)
+               ilock_flags |= BTRFS_ILOCK_TRY;
 
-       if (check_direct_IO(fs_info, from, pos))
+       err = btrfs_inode_lock(inode, ilock_flags);
+       if (err < 0)
+               return err;
+
+       err = generic_write_checks(iocb, from);
+       if (err <= 0) {
+               btrfs_inode_unlock(inode, ilock_flags);
+               return err;
+       }
+
+       err = btrfs_write_check(iocb, from, err);
+       if (err < 0) {
+               btrfs_inode_unlock(inode, ilock_flags);
+               goto out;
+       }
+
+       pos = iocb->ki_pos;
+
+       if (check_direct_IO(fs_info, from, pos)) {
+               btrfs_inode_unlock(inode, ilock_flags);
                goto buffered;
+       }
 
        /*
         * If the write DIO is beyond EOF, we need to update the isize, but it
@@ -1917,8 +1962,10 @@ static ssize_t btrfs_direct_write(struct kiocb *iocb, struct iov_iter *from)
        if (relock)
                btrfs_inode_lock(inode, 0);
 
-       if (written < 0 || !iov_iter_count(from))
-               return written;
+       if (written < 0 || !iov_iter_count(from)) {
+               err = written;
+               goto out;
+       }
 
 buffered:
        pos = iocb->ki_pos;
@@ -1955,8 +2002,6 @@ static ssize_t btrfs_file_write_iter(struct kiocb *iocb,
        struct btrfs_root *root = BTRFS_I(inode)->root;
        ssize_t num_written = 0;
        const bool sync = iocb->ki_flags & IOCB_DSYNC;
-       ssize_t err;
-       unsigned int ilock_flags = 0;
 
        /*
         * If the fs flips readonly due to some impossible error, although we
@@ -1970,25 +2015,6 @@ static ssize_t btrfs_file_write_iter(struct kiocb *iocb,
            (iocb->ki_flags & IOCB_NOWAIT))
                return -EOPNOTSUPP;
 
-       if (iocb->ki_flags & IOCB_NOWAIT)
-               ilock_flags |= BTRFS_ILOCK_TRY;
-
-       err = btrfs_inode_lock(inode, ilock_flags);
-       if (err < 0)
-               return err;
-
-       err = generic_write_checks(iocb, from);
-       if (err <= 0) {
-               btrfs_inode_unlock(inode, ilock_flags);
-               return err;
-       }
-
-       err = btrfs_write_check(iocb, from, err);
-       if (err < 0) {
-               btrfs_inode_unlock(inode, ilock_flags);
-               return err;
-       }
-
        if (sync)
                atomic_inc(&BTRFS_I(inode)->sync_writers);
 
@@ -2031,8 +2057,6 @@ static ssize_t btrfs_file_write_iter(struct kiocb *iocb,
                num_written = btrfs_buffered_write(iocb, from);
        }
 
-       btrfs_inode_unlock(inode, ilock_flags);
-
        /*
         * We also have to set last_sub_trans to the current log transid,
         * otherwise subsequent syncs to a file that's been synced in this
@@ -2048,7 +2072,7 @@ static ssize_t btrfs_file_write_iter(struct kiocb *iocb,
                atomic_dec(&BTRFS_I(inode)->sync_writers);
 
        current->backing_dev_info = NULL;
-       return num_written ? num_written : err;
+       return num_written;
 }
 
 int btrfs_release_file(struct inode *inode, struct file *filp)