imap);
        }
 
-       if (ioend->io_type == XFS_IO_UNWRITTEN)
+       if (ioend->io_type == XFS_IO_UNWRITTEN || xfs_ioend_is_append(ioend))
                set_buffer_defer_completion(bh_result);
 }
 
        struct xfs_mount        *mp = ip->i_mount;
        struct xfs_ioend        *ioend = private;
 
+       trace_xfs_gbmap_direct_endio(ip, offset, size, ioend->io_type, NULL);
+
        if (XFS_FORCED_SHUTDOWN(mp))
-               goto out_destroy_ioend;
+               goto out_end_io;
 
        /*
         * dio completion end_io functions are only called on writes if more
        ioend->io_offset = offset;
 
        /*
-        * While the generic direct I/O code updates the inode size, it does
-        * so only after the end_io handler is called, which means our
-        * end_io handler thinks the on-disk size is outside the in-core
-        * size.  To prevent this just update it a little bit earlier here.
+        * The ioend tells us whether we are doing unwritten extent conversion
+        * or an append transaction that updates the on-disk file size. These
+        * cases are the only cases where we should *potentially* be needing
+        * to update the VFS inode size. When the ioend indicates this, we
+        * are *guaranteed* to be running in non-interrupt context.
+        *
+        * We need to update the in-core inode size here so that we don't end up
+        * with the on-disk inode size being outside the in-core inode size.
+        * While we can do this in the process context after the IO has
+        * completed, this does not work for AIO and hence we always update
+        * the in-core inode size here if necessary.
         */
-       if (offset + size > i_size_read(inode))
-               i_size_write(inode, offset + size);
+       if (ioend->io_type == XFS_IO_UNWRITTEN || xfs_ioend_is_append(ioend)) {
+               if (offset + size > i_size_read(inode))
+                       i_size_write(inode, offset + size);
+       } else
+               ASSERT(offset + size <= i_size_read(inode));
 
        /*
-        * For direct I/O we do not know if we need to allocate blocks or not,
-        * so we can't preallocate an append transaction, as that results in
-        * nested reservations and log space deadlocks. Hence allocate the
-        * transaction here. While this is sub-optimal and can block IO
-        * completion for some time, we're stuck with doing it this way until
-        * we can pass the ioend to the direct IO allocation callbacks and
-        * avoid nesting that way.
+        * If we are doing an append IO that needs to update the EOF on disk,
+        * do the transaction reserve now so we can use common end io
+        * processing. Stashing the error (if there is one) in the ioend will
+        * result in the ioend processing passing on the error if it is
+        * possible as we can't return it from here.
         */
-       if (ioend->io_type == XFS_IO_UNWRITTEN) {
-               xfs_iomap_write_unwritten(ip, offset, size);
-       } else if (offset + size > ip->i_d.di_size) {
-               struct xfs_trans        *tp;
-               int                     error;
-
-               tp = xfs_trans_alloc(mp, XFS_TRANS_FSYNC_TS);
-               error = xfs_trans_reserve(tp, &M_RES(mp)->tr_fsyncts, 0, 0);
-               if (error) {
-                       xfs_trans_cancel(tp, 0);
-                       goto out_destroy_ioend;
-               }
+       if (ioend->io_type == XFS_IO_OVERWRITE && xfs_ioend_is_append(ioend))
+               ioend->io_error = xfs_setfilesize_trans_alloc(ioend);
 
-               xfs_setfilesize(ip, tp, offset, size);
-       }
-out_destroy_ioend:
-       xfs_destroy_ioend(ioend);
+out_end_io:
+       xfs_end_io(&ioend->io_work);
+       return;
 }
 
 STATIC ssize_t