OSDN Git Service

btrfs: call iomap_dio_complete() without inode_lock
authorGoldwyn Rodrigues <rgoldwyn@suse.com>
Thu, 24 Sep 2020 16:39:20 +0000 (11:39 -0500)
committerDavid Sterba <dsterba@suse.com>
Tue, 8 Dec 2020 14:53:49 +0000 (15:53 +0100)
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>
fs/btrfs/file.c

index 93191ea..40b7886 100644 (file)
@@ -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;