xfs: don't start out with the exclusive ilock for direct I/O
[linux-2.6-microblaze.git] / fs / xfs / xfs_iomap.c
index 66e1edb..ee01859 100644 (file)
@@ -955,16 +955,21 @@ static inline bool imap_needs_alloc(struct inode *inode,
                (IS_DAX(inode) && imap->br_state == XFS_EXT_UNWRITTEN);
 }
 
+static inline bool needs_cow_for_zeroing(struct xfs_bmbt_irec *imap, int nimaps)
+{
+       return nimaps &&
+               imap->br_startblock != HOLESTARTBLOCK &&
+               imap->br_state != XFS_EXT_UNWRITTEN;
+}
+
 static inline bool need_excl_ilock(struct xfs_inode *ip, unsigned flags)
 {
        /*
-        * COW writes will allocate delalloc space, so we need to make sure
-        * to take the lock exclusively here.
+        * COW writes may allocate delalloc space or convert unwritten COW
+        * extents, so we need to make sure to take the lock exclusively here.
         */
        if (xfs_is_reflink_inode(ip) && (flags & (IOMAP_WRITE | IOMAP_ZERO)))
                return true;
-       if ((flags & IOMAP_DIRECT) && (flags & IOMAP_WRITE))
-               return true;
        return false;
 }
 
@@ -1024,7 +1029,9 @@ xfs_file_iomap_begin(
                        goto out_unlock;
        }
 
-       if ((flags & (IOMAP_WRITE | IOMAP_ZERO)) && xfs_is_reflink_inode(ip)) {
+       if (xfs_is_reflink_inode(ip) &&
+           ((flags & IOMAP_WRITE) ||
+            ((flags & IOMAP_ZERO) && needs_cow_for_zeroing(&imap, nimaps)))) {
                if (flags & IOMAP_DIRECT) {
                        /*
                         * A reflinked inode will result in CoW alloc.