while (bno < end && n < *nmap) {
                bool                    need_alloc = false, wasdelay = false;
 
-               /* in hole or beyoned EOF? */
+               /* in hole or beyond EOF? */
                if (eof || bma.got.br_startoff > bno) {
+                       /*
+                        * CoW fork conversions should /never/ hit EOF or
+                        * holes.  There should always be something for us
+                        * to work on.
+                        */
+                       ASSERT(!((flags & XFS_BMAPI_CONVERT) &&
+                                (flags & XFS_BMAPI_COWFORK)));
+
                        if (flags & XFS_BMAPI_DELALLOC) {
                                /*
                                 * For the COW fork we can reasonably get a
 
 }
 
 /*
- * Automatic CoW Reservation Freeing
- *
- * These functions automatically garbage collect leftover CoW reservations
- * that were made on behalf of a cowextsize hint when we start to run out
- * of quota or when the reservations sit around for too long.  If the file
- * has dirty pages or is undergoing writeback, its CoW reservations will
- * be retained.
- *
- * The actual garbage collection piggybacks off the same code that runs
- * the speculative EOF preallocation garbage collector.
+ * Set ourselves up to free CoW blocks from this file.  If it's already clean
+ * then we can bail out quickly, but otherwise we must back off if the file
+ * is undergoing some kind of write.
  */
-STATIC int
-xfs_inode_free_cowblocks(
+static bool
+xfs_prep_free_cowblocks(
        struct xfs_inode        *ip,
-       int                     flags,
-       void                    *args)
+       struct xfs_ifork        *ifp)
 {
-       int ret;
-       struct xfs_eofblocks *eofb = args;
-       int match;
-       struct xfs_ifork        *ifp = XFS_IFORK_PTR(ip, XFS_COW_FORK);
-
        /*
         * Just clear the tag if we have an empty cow fork or none at all. It's
         * possible the inode was fully unshared since it was originally tagged.
        if (!xfs_is_reflink_inode(ip) || !ifp->if_bytes) {
                trace_xfs_inode_free_cowblocks_invalid(ip);
                xfs_inode_clear_cowblocks_tag(ip);
-               return 0;
+               return false;
        }
 
        /*
            mapping_tagged(VFS_I(ip)->i_mapping, PAGECACHE_TAG_DIRTY) ||
            mapping_tagged(VFS_I(ip)->i_mapping, PAGECACHE_TAG_WRITEBACK) ||
            atomic_read(&VFS_I(ip)->i_dio_count))
+               return false;
+
+       return true;
+}
+
+/*
+ * Automatic CoW Reservation Freeing
+ *
+ * These functions automatically garbage collect leftover CoW reservations
+ * that were made on behalf of a cowextsize hint when we start to run out
+ * of quota or when the reservations sit around for too long.  If the file
+ * has dirty pages or is undergoing writeback, its CoW reservations will
+ * be retained.
+ *
+ * The actual garbage collection piggybacks off the same code that runs
+ * the speculative EOF preallocation garbage collector.
+ */
+STATIC int
+xfs_inode_free_cowblocks(
+       struct xfs_inode        *ip,
+       int                     flags,
+       void                    *args)
+{
+       struct xfs_eofblocks    *eofb = args;
+       struct xfs_ifork        *ifp = XFS_IFORK_PTR(ip, XFS_COW_FORK);
+       int                     match;
+       int                     ret = 0;
+
+       if (!xfs_prep_free_cowblocks(ip, ifp))
                return 0;
 
        if (eofb) {
        xfs_ilock(ip, XFS_IOLOCK_EXCL);
        xfs_ilock(ip, XFS_MMAPLOCK_EXCL);
 
-       ret = xfs_reflink_cancel_cow_range(ip, 0, NULLFILEOFF, false);
+       /*
+        * Check again, nobody else should be able to dirty blocks or change
+        * the reflink iflag now that we have the first two locks held.
+        */
+       if (xfs_prep_free_cowblocks(ip, ifp))
+               ret = xfs_reflink_cancel_cow_range(ip, 0, NULLFILEOFF, false);
 
        xfs_iunlock(ip, XFS_MMAPLOCK_EXCL);
        xfs_iunlock(ip, XFS_IOLOCK_EXCL);