summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGoldwyn Rodrigues <rgoldwyn@suse.com>2020-09-24 11:39:20 -0500
committerDavid Sterba <dsterba@suse.com>2020-12-08 15:53:49 +0100
commita42fa643169d2325602572633fcaa16862990e28 (patch)
treeba42307de34a252dacd2534af8dc77a123183d89
parent502756b380938022c848761837f8fa3976906aa1 (diff)
btrfs: call iomap_dio_complete() without inode_lock
If direct writes are called with O_DIRECT | O_DSYNC, it will result in a deadlock because iomap_dio_rw() is called under i_rwsem which calls: iomap_dio_complete() generic_write_sync() btrfs_sync_file() btrfs_sync_file() requires i_rwsem, so call __iomap_dio_rw() with the i_rwsem locked, and call iomap_dio_complete() after unlocking i_rwsem. Reviewed-by: Josef Bacik <josef@toxicpanda.com> Signed-off-by: Goldwyn Rodrigues <rgoldwyn@suse.com> Signed-off-by: David Sterba <dsterba@suse.com>
-rw-r--r--fs/btrfs/file.c24
1 files changed, 11 insertions, 13 deletions
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
index 93191ea5d1fd..40b7886c23f5 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -1906,6 +1906,7 @@ static ssize_t btrfs_direct_write(struct kiocb *iocb, struct iov_iter *from)
loff_t endbyte;
ssize_t err;
unsigned int ilock_flags = 0;
+ struct iomap_dio *dio = NULL;
if (iocb->ki_flags & IOCB_NOWAIT)
ilock_flags |= BTRFS_ILOCK_TRY;
@@ -1948,22 +1949,19 @@ relock:
goto buffered;
}
- /*
- * This is actually a sync iocb, so we need our fancy endio to know if
- * we need to sync.
- */
- if (current->journal_info)
- written = iomap_dio_rw(iocb, from, &btrfs_dio_iomap_ops,
- &btrfs_sync_dops, is_sync_kiocb(iocb));
- else
- written = iomap_dio_rw(iocb, from, &btrfs_dio_iomap_ops,
- &btrfs_dio_ops, is_sync_kiocb(iocb));
-
- if (written == -ENOTBLK)
- written = 0;
+ dio = __iomap_dio_rw(iocb, from, &btrfs_dio_iomap_ops,
+ &btrfs_dio_ops, is_sync_kiocb(iocb));
btrfs_inode_unlock(inode, ilock_flags);
+ if (IS_ERR_OR_NULL(dio)) {
+ err = PTR_ERR_OR_ZERO(dio);
+ if (err < 0 && err != -ENOTBLK)
+ goto out;
+ } else {
+ written = iomap_dio_complete(dio);
+ }
+
if (written < 0 || !iov_iter_count(from)) {
err = written;
goto out;