Commit a42fa643 authored by Goldwyn Rodrigues's avatar Goldwyn Rodrigues Committed by David Sterba
Browse files

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: default avatarJosef Bacik <josef@toxicpanda.com>
Signed-off-by: default avatarGoldwyn Rodrigues <rgoldwyn@suse.com>
Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
parent 502756b3
Loading
Loading
Loading
Loading
+11 −13
Original line number Diff line number Diff line
@@ -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 @@ static ssize_t btrfs_direct_write(struct kiocb *iocb, struct iov_iter *from)
		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,
	dio = __iomap_dio_rw(iocb, from, &btrfs_dio_iomap_ops,
			     &btrfs_dio_ops, is_sync_kiocb(iocb));

	if (written == -ENOTBLK)
		written = 0;

	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;