]> exis.tech > repos - linux.git/blobdiff - fs/xfs/xfs_bmap_util.c
Merge tag 'hwmon-for-v7.2-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/groec...
[linux.git] / fs / xfs / xfs_bmap_util.c
index 3b9f262f8e9128c3e9e8ab3a229b066b7c0591bd..c88b9ade7389dd42f44388194cd28e00315bc77e 100644 (file)
@@ -1744,3 +1744,92 @@ out_trans_cancel:
        xfs_trans_cancel(tp);
        goto out_unlock_ilock;
 }
+
+/*
+ * Given a CoW fork mapping @got and a replacement mapping @rep, map the space
+ * described by @rep into the cow fork, pushing aside @got as necessary.  @icur
+ * must point to iext tree leaf containing @got.
+ */
+void
+xfs_bmap_replace_cow_mapping(
+       struct xfs_inode        *ip,
+       struct xfs_iext_cursor  *icur,
+       struct xfs_bmbt_irec    *got,
+       struct xfs_bmbt_irec    *rep)
+{
+       struct xfs_ifork        *ifp = xfs_ifork_ptr(ip, XFS_COW_FORK);
+       xfs_fileoff_t           rep_endoff =
+                       rep->br_startoff + rep->br_blockcount;
+       xfs_fileoff_t           got_endoff =
+                       got->br_startoff + got->br_blockcount;
+       uint32_t                state = BMAP_COWFORK;
+
+       ASSERT(rep->br_blockcount > 0);
+       ASSERT(!isnullstartblock(got->br_startblock));
+       ASSERT(got->br_startoff <= rep->br_startoff);
+       ASSERT(got_endoff >= rep_endoff);
+
+       trace_xfs_bmap_replace_cow_mapping(ip, got, rep);
+
+       if (got->br_startoff == rep->br_startoff)
+               state |= BMAP_LEFT_FILLING;
+       if (got_endoff == rep_endoff)
+               state |= BMAP_RIGHT_FILLING;
+
+       switch (state & (BMAP_LEFT_FILLING | BMAP_RIGHT_FILLING)) {
+       case BMAP_LEFT_FILLING | BMAP_RIGHT_FILLING:
+               /*
+                * Replacement matches the whole mapping, update the record.
+                */
+               xfs_iext_update_extent(ip, state, icur, rep);
+               break;
+       case BMAP_LEFT_FILLING:
+               /*
+                * Replace the first part of the mapping: Update the cursor
+                * position with the new mapping, then add a record with the
+                * tail of the old mapping.
+                */
+               got->br_startoff = rep_endoff;
+               got->br_blockcount -= rep->br_blockcount;
+               got->br_startblock += rep->br_blockcount;
+
+               xfs_iext_update_extent(ip, state, icur, rep);
+               xfs_iext_next(ifp, icur);
+               xfs_iext_insert(ip, icur, got, state);
+               break;
+       case BMAP_RIGHT_FILLING:
+               /*
+                * Replacing the last part of the mapping.  Shorten the current
+                * mapping then add a record with the new mapping.
+                */
+               got->br_blockcount -= rep->br_blockcount;
+
+               xfs_iext_update_extent(ip, state, icur, got);
+               xfs_iext_next(ifp, icur);
+               xfs_iext_insert(ip, icur, rep, state);
+               break;
+       case 0:
+               /*
+                * Replacing the middle of the extent.  Shorten the current
+                * mapping, add a new record with the new mapping, and add a
+                * second new record with the tail of the old mapping.
+                */
+               got->br_blockcount = rep->br_startoff - got->br_startoff;
+
+               struct xfs_bmbt_irec    new = {
+                       .br_startoff    = rep_endoff,
+                       .br_blockcount  = got_endoff - rep_endoff,
+                       .br_state       = got->br_state,
+                       .br_startblock  = got->br_startblock +
+                                               rep->br_blockcount +
+                                               got->br_blockcount,
+               };
+
+               xfs_iext_update_extent(ip, state, icur, got);
+               xfs_iext_next(ifp, icur);
+               xfs_iext_insert(ip, icur, rep, state);
+               xfs_iext_next(ifp, icur);
+               xfs_iext_insert(ip, icur, &new, state);
+               break;
+       }
+}