*/
        btrfs_inode_unlock(inode, BTRFS_ILOCK_MMAP);
 
-       if (ret != BTRFS_NO_LOG_SYNC) {
+       if (ret == BTRFS_NO_LOG_SYNC) {
+               ret = btrfs_end_transaction(trans);
+               goto out;
+       }
+
+       /* We successfully logged the inode, attempt to sync the log. */
+       if (!ret) {
+               ret = btrfs_sync_log(trans, root, &ctx);
                if (!ret) {
-                       ret = btrfs_sync_log(trans, root, &ctx);
-                       if (!ret) {
-                               ret = btrfs_end_transaction(trans);
-                               goto out;
-                       }
-               }
-               if (!full_sync) {
-                       ret = btrfs_wait_ordered_range(inode, start, len);
-                       if (ret) {
-                               btrfs_end_transaction(trans);
-                               goto out;
-                       }
+                       ret = btrfs_end_transaction(trans);
+                       goto out;
                }
-               ret = btrfs_commit_transaction(trans);
-       } else {
+       }
+
+       /*
+        * At this point we need to commit the transaction because we had
+        * btrfs_need_log_full_commit() or some other error.
+        *
+        * If we didn't do a full sync we have to stop the trans handle, wait on
+        * the ordered extents, start it again and commit the transaction.  If
+        * we attempt to wait on the ordered extents here we could deadlock with
+        * something like fallocate() that is holding the extent lock trying to
+        * start a transaction while some other thread is trying to commit the
+        * transaction while we (fsync) are currently holding the transaction
+        * open.
+        */
+       if (!full_sync) {
                ret = btrfs_end_transaction(trans);
+               if (ret)
+                       goto out;
+               ret = btrfs_wait_ordered_range(inode, start, len);
+               if (ret)
+                       goto out;
+
+               /*
+                * This is safe to use here because we're only interested in
+                * making sure the transaction that had the ordered extents is
+                * committed.  We aren't waiting on anything past this point,
+                * we're purely getting the transaction and committing it.
+                */
+               trans = btrfs_attach_transaction_barrier(root);
+               if (IS_ERR(trans)) {
+                       ret = PTR_ERR(trans);
+
+                       /*
+                        * We committed the transaction and there's no currently
+                        * running transaction, this means everything we care
+                        * about made it to disk and we are done.
+                        */
+                       if (ret == -ENOENT)
+                               ret = 0;
+                       goto out;
+               }
        }
+
+       ret = btrfs_commit_transaction(trans);
 out:
        ASSERT(list_empty(&ctx.list));
        err = file_check_and_advance_wb_err(file);