struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
loff_t pos;
ssize_t written = 0;
- bool relock = false;
ssize_t written_buffered;
loff_t endbyte;
ssize_t err;
if (iocb->ki_flags & IOCB_NOWAIT)
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);
if (err < 0)
return err;
}
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)) {
btrfs_inode_unlock(inode, ilock_flags);
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);
/*
written = 0;
up_read(&BTRFS_I(inode)->dio_sem);
- if (relock)
- btrfs_inode_lock(inode, 0);
+ btrfs_inode_unlock(inode, ilock_flags);
if (written < 0 || !iov_iter_count(from)) {
err = written;