if (!create && direct && offset >= i_size_read(inode))
                return 0;
 
-       if (create) {
+       /*
+        * Direct I/O is usually done on preallocated files, so try getting
+        * a block mapping without an exclusive lock first.  For buffered
+        * writes we already have the exclusive iolock anyway, so avoiding
+        * a lock roundtrip here by taking the ilock exclusive from the
+        * beginning is a useful micro optimization.
+        */
+       if (create && !direct) {
                lockmode = XFS_ILOCK_EXCL;
                xfs_ilock(ip, lockmode);
        } else {
             (imap.br_startblock == HOLESTARTBLOCK ||
              imap.br_startblock == DELAYSTARTBLOCK))) {
                if (direct) {
+                       /*
+                        * Drop the ilock in preparation for starting the block
+                        * allocation transaction.  It will be retaken
+                        * exclusively inside xfs_iomap_write_direct for the
+                        * actual allocation.
+                        */
+                       xfs_iunlock(ip, lockmode);
                        error = xfs_iomap_write_direct(ip, offset, size,
                                                       &imap, nimaps);
+                       if (error)
+                               return -error;
                } else {
+                       /*
+                        * Delalloc reservations do not require a transaction,
+                        * we can go on without dropping the lock here.
+                        */
                        error = xfs_iomap_write_delay(ip, offset, size, &imap);
+                       if (error)
+                               goto out_unlock;
+
+                       xfs_iunlock(ip, lockmode);
                }
-               if (error)
-                       goto out_unlock;
 
                trace_xfs_get_blocks_alloc(ip, offset, size, 0, &imap);
        } else if (nimaps) {
                trace_xfs_get_blocks_found(ip, offset, size, 0, &imap);
+               xfs_iunlock(ip, lockmode);
        } else {
                trace_xfs_get_blocks_notfound(ip, offset, size);
                goto out_unlock;
        }
-       xfs_iunlock(ip, lockmode);
 
        if (imap.br_startblock != HOLESTARTBLOCK &&
            imap.br_startblock != DELAYSTARTBLOCK) {
 
        int             committed;
        int             error;
 
-       /*
-        * Make sure that the dquots are there. This doesn't hold
-        * the ilock across a disk read.
-        */
-       error = xfs_qm_dqattach_locked(ip, 0);
+       error = xfs_qm_dqattach(ip, 0);
        if (error)
                return XFS_ERROR(error);
 
        if ((offset + count) > XFS_ISIZE(ip)) {
                error = xfs_iomap_eof_align_last_fsb(mp, ip, extsz, &last_fsb);
                if (error)
-                       goto error_out;
+                       return XFS_ERROR(error);
        } else {
                if (nmaps && (imap->br_startblock == HOLESTARTBLOCK))
                        last_fsb = MIN(last_fsb, (xfs_fileoff_t)
        /*
         * Allocate and setup the transaction
         */
-       xfs_iunlock(ip, XFS_ILOCK_EXCL);
        tp = xfs_trans_alloc(mp, XFS_TRANS_DIOSTRAT);
        error = xfs_trans_reserve(tp, resblks,
                        XFS_WRITE_LOG_RES(mp), resrtextents,
        /*
         * Check for running out of space, note: need lock to return
         */
-       if (error)
+       if (error) {
                xfs_trans_cancel(tp, 0);
+               return XFS_ERROR(error);
+       }
+
        xfs_ilock(ip, XFS_ILOCK_EXCL);
-       if (error)
-               goto error_out;
 
        error = xfs_trans_reserve_quota_nblks(tp, ip, qblocks, 0, quota_flag);
        if (error)
-               goto error1;
+               goto out_trans_cancel;
 
        xfs_trans_ijoin(tp, ip, 0);
 
        error = xfs_bmapi_write(tp, ip, offset_fsb, count_fsb, bmapi_flag,
                                &firstfsb, 0, imap, &nimaps, &free_list);
        if (error)
-               goto error0;
+               goto out_bmap_cancel;
 
        /*
         * Complete the transaction
         */
        error = xfs_bmap_finish(&tp, &free_list, &committed);
        if (error)
-               goto error0;
+               goto out_bmap_cancel;
        error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES);
        if (error)
-               goto error_out;
+               goto out_unlock;
 
        /*
         * Copy any maps to caller's array and return any error.
         */
        if (nimaps == 0) {
-               error = ENOSPC;
-               goto error_out;
+               error = XFS_ERROR(ENOSPC);
+               goto out_unlock;
        }
 
-       if (!(imap->br_startblock || XFS_IS_REALTIME_INODE(ip))) {
+       if (!(imap->br_startblock || XFS_IS_REALTIME_INODE(ip)))
                error = xfs_alert_fsblock_zero(ip, imap);
-               goto error_out;
-       }
 
-       return 0;
+out_unlock:
+       xfs_iunlock(ip, XFS_ILOCK_EXCL);
+       return error;
 
-error0:        /* Cancel bmap, unlock inode, unreserve quota blocks, cancel trans */
+out_bmap_cancel:
        xfs_bmap_cancel(&free_list);
        xfs_trans_unreserve_quota_nblks(tp, ip, qblocks, 0, quota_flag);
-
-error1:        /* Just cancel transaction */
+out_trans_cancel:
        xfs_trans_cancel(tp, XFS_TRANS_RELEASE_LOG_RES | XFS_TRANS_ABORT);
-
-error_out:
-       return XFS_ERROR(error);
+       goto out_unlock;
 }
 
 /*