Commit e9adabb9 authored by Goldwyn Rodrigues's avatar Goldwyn Rodrigues Committed by David Sterba

btrfs: use shared lock for direct writes within EOF

Direct writes within EOF are safe to be performed with inode shared lock
to improve parallelization with other direct writes or reads because EOF
is not changed and there is no race with truncate().

Direct reads are already performed under shared inode lock.

This patch is precursor to removing btrfs_inode->dio_sem.
Signed-off-by: default avatarGoldwyn Rodrigues <rgoldwyn@suse.com>
Reviewed-by: default avatarDavid Sterba <dsterba@suse.com>
Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
parent c3523706
...@@ -1902,7 +1902,6 @@ static ssize_t btrfs_direct_write(struct kiocb *iocb, struct iov_iter *from) ...@@ -1902,7 +1902,6 @@ static ssize_t btrfs_direct_write(struct kiocb *iocb, struct iov_iter *from)
struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb); struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
loff_t pos; loff_t pos;
ssize_t written = 0; ssize_t written = 0;
bool relock = false;
ssize_t written_buffered; ssize_t written_buffered;
loff_t endbyte; loff_t endbyte;
ssize_t err; ssize_t err;
...@@ -1911,6 +1910,11 @@ static ssize_t btrfs_direct_write(struct kiocb *iocb, struct iov_iter *from) ...@@ -1911,6 +1910,11 @@ static ssize_t btrfs_direct_write(struct kiocb *iocb, struct iov_iter *from)
if (iocb->ki_flags & IOCB_NOWAIT) if (iocb->ki_flags & IOCB_NOWAIT)
ilock_flags |= BTRFS_ILOCK_TRY; ilock_flags |= BTRFS_ILOCK_TRY;
/* If the write DIO is within EOF, use a shared lock */
if (iocb->ki_pos + iov_iter_count(from) <= i_size_read(inode))
ilock_flags |= BTRFS_ILOCK_SHARED;
relock:
err = btrfs_inode_lock(inode, ilock_flags); err = btrfs_inode_lock(inode, ilock_flags);
if (err < 0) if (err < 0)
return err; return err;
...@@ -1928,20 +1932,22 @@ static ssize_t btrfs_direct_write(struct kiocb *iocb, struct iov_iter *from) ...@@ -1928,20 +1932,22 @@ static ssize_t btrfs_direct_write(struct kiocb *iocb, struct iov_iter *from)
} }
pos = iocb->ki_pos; pos = iocb->ki_pos;
/*
* Re-check since file size may have changed just before taking the
* lock or pos may have changed because of O_APPEND in generic_write_check()
*/
if ((ilock_flags & BTRFS_ILOCK_SHARED) &&
pos + iov_iter_count(from) > i_size_read(inode)) {
btrfs_inode_unlock(inode, ilock_flags);
ilock_flags &= ~BTRFS_ILOCK_SHARED;
goto relock;
}
if (check_direct_IO(fs_info, from, pos)) { if (check_direct_IO(fs_info, from, pos)) {
btrfs_inode_unlock(inode, ilock_flags); btrfs_inode_unlock(inode, ilock_flags);
goto buffered; goto buffered;
} }
/*
* If the write DIO is beyond EOF, we need to update the isize, but it
* is protected by inode lock. So we cannot unlock it here.
*/
if (pos + iov_iter_count(from) <= inode->i_size) {
btrfs_inode_unlock(inode, 0);
relock = true;
}
down_read(&BTRFS_I(inode)->dio_sem); down_read(&BTRFS_I(inode)->dio_sem);
/* /*
...@@ -1959,8 +1965,7 @@ static ssize_t btrfs_direct_write(struct kiocb *iocb, struct iov_iter *from) ...@@ -1959,8 +1965,7 @@ static ssize_t btrfs_direct_write(struct kiocb *iocb, struct iov_iter *from)
written = 0; written = 0;
up_read(&BTRFS_I(inode)->dio_sem); up_read(&BTRFS_I(inode)->dio_sem);
if (relock) btrfs_inode_unlock(inode, ilock_flags);
btrfs_inode_lock(inode, 0);
if (written < 0 || !iov_iter_count(from)) { if (written < 0 || !iov_iter_count(from)) {
err = written; err = written;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment