Merge branch 'work.sendmsg' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
[sfrench/cifs-2.6.git] / fs / xfs / xfs_iomap.c
index 1aa3abd67b36670f60b9daf7672c243307c77438..41662fb14e87d8b1546c42d6a65508db5c5a76bf 100644 (file)
@@ -162,7 +162,7 @@ xfs_iomap_write_direct(
        xfs_fileoff_t   last_fsb;
        xfs_filblks_t   count_fsb, resaligned;
        xfs_fsblock_t   firstfsb;
-       xfs_extlen_t    extsz, temp;
+       xfs_extlen_t    extsz;
        int             nimaps;
        int             quota_flag;
        int             rt;
@@ -203,14 +203,7 @@ xfs_iomap_write_direct(
        }
        count_fsb = last_fsb - offset_fsb;
        ASSERT(count_fsb > 0);
-
-       resaligned = count_fsb;
-       if (unlikely(extsz)) {
-               if ((temp = do_mod(offset_fsb, extsz)))
-                       resaligned += temp;
-               if ((temp = do_mod(resaligned, extsz)))
-                       resaligned += extsz - temp;
-       }
+       resaligned = xfs_aligned_fsb_count(offset_fsb, count_fsb, extsz);
 
        if (unlikely(rt)) {
                resrtextents = qblocks = resaligned;
@@ -685,7 +678,7 @@ xfs_iomap_write_allocate(
        int             nres;
 
        if (whichfork == XFS_COW_FORK)
-               flags |= XFS_BMAPI_COWFORK;
+               flags |= XFS_BMAPI_COWFORK | XFS_BMAPI_PREALLOC;
 
        /*
         * Make sure that the dquots are there.
@@ -1002,47 +995,31 @@ xfs_file_iomap_begin(
        offset_fsb = XFS_B_TO_FSBT(mp, offset);
        end_fsb = XFS_B_TO_FSB(mp, offset + length);
 
-       if (xfs_is_reflink_inode(ip) &&
-           (flags & IOMAP_WRITE) && (flags & IOMAP_DIRECT)) {
-               shared = xfs_reflink_find_cow_mapping(ip, offset, &imap);
-               if (shared) {
-                       xfs_iunlock(ip, lockmode);
-                       goto alloc_done;
-               }
-               ASSERT(!isnullstartblock(imap.br_startblock));
-       }
-
        error = xfs_bmapi_read(ip, offset_fsb, end_fsb - offset_fsb, &imap,
                               &nimaps, 0);
        if (error)
                goto out_unlock;
 
-       if ((flags & IOMAP_REPORT) ||
-           (xfs_is_reflink_inode(ip) &&
-            (flags & IOMAP_WRITE) && (flags & IOMAP_DIRECT))) {
+       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;
-
-               /*
-                * We're here because we're trying to do a directio write to a
-                * region that isn't aligned to a filesystem block.  If the
-                * extent is shared, fall back to buffered mode to handle the
-                * RMW.
-                */
-               if (!(flags & IOMAP_REPORT) && shared) {
-                       trace_xfs_reflink_bounce_dio_write(ip, &imap);
-                       error = -EREMCHG;
-                       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;
+               if (flags & IOMAP_DIRECT) {
+                       /* may drop and re-acquire the ilock */
+                       error = xfs_reflink_allocate_cow(ip, &imap, &shared,
+                                       &lockmode);
+                       if (error)
+                               goto out_unlock;
+               } else {
+                       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;
@@ -1071,7 +1048,6 @@ xfs_file_iomap_begin(
                if (error)
                        return error;
 
-alloc_done:
                iomap->flags = IOMAP_F_NEW;
                trace_xfs_iomap_alloc(ip, offset, length, 0, &imap);
        } else {
@@ -1102,7 +1078,19 @@ xfs_file_iomap_end_delalloc(
        xfs_fileoff_t           end_fsb;
        int                     error = 0;
 
-       start_fsb = XFS_B_TO_FSB(mp, offset + written);
+       /* behave as if the write failed if drop writes is enabled */
+       if (xfs_mp_drop_writes(mp))
+               written = 0;
+
+       /*
+        * start_fsb refers to the first unused block after a short write. If
+        * nothing was written, round offset down to point at the first block in
+        * the range.
+        */
+       if (unlikely(!written))
+               start_fsb = XFS_B_TO_FSBT(mp, offset);
+       else
+               start_fsb = XFS_B_TO_FSB(mp, offset + written);
        end_fsb = XFS_B_TO_FSB(mp, offset + length);
 
        /*
@@ -1114,6 +1102,9 @@ xfs_file_iomap_end_delalloc(
         * blocks in the range, they are ours.
         */
        if (start_fsb < end_fsb) {
+               truncate_pagecache_range(VFS_I(ip), XFS_FSB_TO_B(mp, start_fsb),
+                                        XFS_FSB_TO_B(mp, end_fsb) - 1);
+
                xfs_ilock(ip, XFS_ILOCK_EXCL);
                error = xfs_bmap_punch_delalloc_range(ip, start_fsb,
                                               end_fsb - start_fsb);
@@ -1144,7 +1135,7 @@ xfs_file_iomap_end(
        return 0;
 }
 
-struct iomap_ops xfs_iomap_ops = {
+const struct iomap_ops xfs_iomap_ops = {
        .iomap_begin            = xfs_file_iomap_begin,
        .iomap_end              = xfs_file_iomap_end,
 };
@@ -1190,6 +1181,6 @@ out_unlock:
        return error;
 }
 
-struct iomap_ops xfs_xattr_iomap_ops = {
+const struct iomap_ops xfs_xattr_iomap_ops = {
        .iomap_begin            = xfs_xattr_iomap_begin,
 };