diff options
author | Goldwyn Rodrigues <rgoldwyn@suse.com> | 2020-09-24 11:39:20 -0500 |
---|---|---|
committer | David Sterba <dsterba@suse.com> | 2020-12-08 15:53:49 +0100 |
commit | a42fa643169d2325602572633fcaa16862990e28 (patch) | |
tree | ba42307de34a252dacd2534af8dc77a123183d89 /fs/btrfs/file.c | |
parent | 502756b380938022c848761837f8fa3976906aa1 (diff) | |
download | linux-a42fa643169d2325602572633fcaa16862990e28.tar.gz linux-a42fa643169d2325602572633fcaa16862990e28.tar.bz2 linux-a42fa643169d2325602572633fcaa16862990e28.zip |
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>
Diffstat (limited to 'fs/btrfs/file.c')
-rw-r--r-- | fs/btrfs/file.c | 24 |
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; |