Merge tag 'xfs-fixes-for-linus-4.9-rc3' of git://git.kernel.org/pub/scm/linux/kernel...
[cascardo/linux.git] / fs / xfs / xfs_iomap.c
index c08253e..436e109 100644 (file)
@@ -39,6 +39,7 @@
 #include "xfs_quota.h"
 #include "xfs_dquot_item.h"
 #include "xfs_dquot.h"
+#include "xfs_reflink.h"
 
 
 #define XFS_WRITEIO_ALIGN(mp,off)      (((off) >> mp->m_writeio_log) \
@@ -70,7 +71,7 @@ xfs_bmbt_to_iomap(
        iomap->bdev = xfs_find_bdev_for_inode(VFS_I(ip));
 }
 
-static xfs_extlen_t
+xfs_extlen_t
 xfs_eof_alignment(
        struct xfs_inode        *ip,
        xfs_extlen_t            extsize)
@@ -565,6 +566,17 @@ xfs_file_iomap_begin_delay(
        xfs_bmap_search_extents(ip, offset_fsb, XFS_DATA_FORK, &eof, &idx,
                        &got, &prev);
        if (!eof && got.br_startoff <= offset_fsb) {
+               if (xfs_is_reflink_inode(ip)) {
+                       bool            shared;
+
+                       end_fsb = min(XFS_B_TO_FSB(mp, offset + count),
+                                       maxbytes_fsb);
+                       xfs_trim_extent(&got, offset_fsb, end_fsb - offset_fsb);
+                       error = xfs_reflink_reserve_cow(ip, &got, &shared);
+                       if (error)
+                               goto out_unlock;
+               }
+
                trace_xfs_iomap_found(ip, offset, count, 0, &got);
                goto done;
        }
@@ -609,7 +621,7 @@ xfs_file_iomap_begin_delay(
        }
 
 retry:
-       error = xfs_bmapi_reserve_delalloc(ip, offset_fsb,
+       error = xfs_bmapi_reserve_delalloc(ip, XFS_DATA_FORK, offset_fsb,
                        end_fsb - offset_fsb, &got,
                        &prev, &idx, eof);
        switch (error) {
@@ -666,6 +678,7 @@ out_unlock:
 int
 xfs_iomap_write_allocate(
        xfs_inode_t     *ip,
+       int             whichfork,
        xfs_off_t       offset,
        xfs_bmbt_irec_t *imap)
 {
@@ -678,8 +691,12 @@ xfs_iomap_write_allocate(
        xfs_trans_t     *tp;
        int             nimaps;
        int             error = 0;
+       int             flags = 0;
        int             nres;
 
+       if (whichfork == XFS_COW_FORK)
+               flags |= XFS_BMAPI_COWFORK;
+
        /*
         * Make sure that the dquots are there.
         */
@@ -773,7 +790,7 @@ xfs_iomap_write_allocate(
                         * pointer that the caller gave to us.
                         */
                        error = xfs_bmapi_write(tp, ip, map_start_fsb,
-                                               count_fsb, 0, &first_block,
+                                               count_fsb, flags, &first_block,
                                                nres, imap, &nimaps,
                                                &dfops);
                        if (error)
@@ -956,18 +973,29 @@ xfs_file_iomap_begin(
        struct xfs_bmbt_irec    imap;
        xfs_fileoff_t           offset_fsb, end_fsb;
        int                     nimaps = 1, error = 0;
+       bool                    shared = false, trimmed = false;
        unsigned                lockmode;
 
        if (XFS_FORCED_SHUTDOWN(mp))
                return -EIO;
 
-       if ((flags & IOMAP_WRITE) &&
-           !IS_DAX(inode) && !xfs_get_extsz_hint(ip)) {
+       if ((flags & IOMAP_WRITE) && !IS_DAX(inode) &&
+                  !xfs_get_extsz_hint(ip)) {
+               /* Reserve delalloc blocks for regular writeback. */
                return xfs_file_iomap_begin_delay(inode, offset, length, flags,
                                iomap);
        }
 
-       lockmode = xfs_ilock_data_map_shared(ip);
+       /*
+        * COW writes will allocate delalloc space, so we need to make sure
+        * to take the lock exclusively here.
+        */
+       if ((flags & (IOMAP_WRITE | IOMAP_ZERO)) && xfs_is_reflink_inode(ip)) {
+               lockmode = XFS_ILOCK_EXCL;
+               xfs_ilock(ip, XFS_ILOCK_EXCL);
+       } else {
+               lockmode = xfs_ilock_data_map_shared(ip);
+       }
 
        ASSERT(offset <= mp->m_super->s_maxbytes);
        if ((xfs_fsize_t)offset + length > mp->m_super->s_maxbytes)
@@ -976,10 +1004,25 @@ xfs_file_iomap_begin(
        end_fsb = XFS_B_TO_FSB(mp, offset + length);
 
        error = xfs_bmapi_read(ip, offset_fsb, end_fsb - offset_fsb, &imap,
-                              &nimaps, XFS_BMAPI_ENTIRE);
-       if (error) {
-               xfs_iunlock(ip, lockmode);
-               return error;
+                              &nimaps, 0);
+       if (error)
+               goto out_unlock;
+
+       if (flags & IOMAP_REPORT) {
+               /* Trim the mapping to the nearest shared extent boundary. */
+               error = xfs_reflink_trim_around_shared(ip, &imap, &shared,
+                               &trimmed);
+               if (error)
+                       goto out_unlock;
+       }
+
+       if ((flags & (IOMAP_WRITE | IOMAP_ZERO)) && xfs_is_reflink_inode(ip)) {
+               error = xfs_reflink_reserve_cow(ip, &imap, &shared);
+               if (error)
+                       goto out_unlock;
+
+               end_fsb = imap.br_startoff + imap.br_blockcount;
+               length = XFS_FSB_TO_B(mp, end_fsb) - offset;
        }
 
        if ((flags & IOMAP_WRITE) && imap_needs_alloc(inode, &imap, nimaps)) {
@@ -1015,7 +1058,12 @@ xfs_file_iomap_begin(
        }
 
        xfs_bmbt_to_iomap(ip, iomap, &imap);
+       if (shared)
+               iomap->flags |= IOMAP_F_SHARED;
        return 0;
+out_unlock:
+       xfs_iunlock(ip, lockmode);
+       return error;
 }
 
 static int