|
@@ -148,6 +148,37 @@ done:
|
|
|
return error;
|
|
|
}
|
|
|
|
|
|
+STATIC int
|
|
|
+xfs_rmap_delete(
|
|
|
+ struct xfs_btree_cur *rcur,
|
|
|
+ xfs_agblock_t agbno,
|
|
|
+ xfs_extlen_t len,
|
|
|
+ uint64_t owner,
|
|
|
+ uint64_t offset,
|
|
|
+ unsigned int flags)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+ int error;
|
|
|
+
|
|
|
+ trace_xfs_rmap_delete(rcur->bc_mp, rcur->bc_private.a.agno, agbno,
|
|
|
+ len, owner, offset, flags);
|
|
|
+
|
|
|
+ error = xfs_rmap_lookup_eq(rcur, agbno, len, owner, offset, flags, &i);
|
|
|
+ if (error)
|
|
|
+ goto done;
|
|
|
+ XFS_WANT_CORRUPTED_GOTO(rcur->bc_mp, i == 1, done);
|
|
|
+
|
|
|
+ error = xfs_btree_delete(rcur, &i);
|
|
|
+ if (error)
|
|
|
+ goto done;
|
|
|
+ XFS_WANT_CORRUPTED_GOTO(rcur->bc_mp, i == 1, done);
|
|
|
+done:
|
|
|
+ if (error)
|
|
|
+ trace_xfs_rmap_delete_error(rcur->bc_mp,
|
|
|
+ rcur->bc_private.a.agno, error, _RET_IP_);
|
|
|
+ return error;
|
|
|
+}
|
|
|
+
|
|
|
static int
|
|
|
xfs_rmap_btrec_to_irec(
|
|
|
union xfs_btree_rec *rec,
|
|
@@ -180,6 +211,160 @@ xfs_rmap_get_rec(
|
|
|
return xfs_rmap_btrec_to_irec(rec, irec);
|
|
|
}
|
|
|
|
|
|
+struct xfs_find_left_neighbor_info {
|
|
|
+ struct xfs_rmap_irec high;
|
|
|
+ struct xfs_rmap_irec *irec;
|
|
|
+ int *stat;
|
|
|
+};
|
|
|
+
|
|
|
+/* For each rmap given, figure out if it matches the key we want. */
|
|
|
+STATIC int
|
|
|
+xfs_rmap_find_left_neighbor_helper(
|
|
|
+ struct xfs_btree_cur *cur,
|
|
|
+ struct xfs_rmap_irec *rec,
|
|
|
+ void *priv)
|
|
|
+{
|
|
|
+ struct xfs_find_left_neighbor_info *info = priv;
|
|
|
+
|
|
|
+ trace_xfs_rmap_find_left_neighbor_candidate(cur->bc_mp,
|
|
|
+ cur->bc_private.a.agno, rec->rm_startblock,
|
|
|
+ rec->rm_blockcount, rec->rm_owner, rec->rm_offset,
|
|
|
+ rec->rm_flags);
|
|
|
+
|
|
|
+ if (rec->rm_owner != info->high.rm_owner)
|
|
|
+ return XFS_BTREE_QUERY_RANGE_CONTINUE;
|
|
|
+ if (!XFS_RMAP_NON_INODE_OWNER(rec->rm_owner) &&
|
|
|
+ !(rec->rm_flags & XFS_RMAP_BMBT_BLOCK) &&
|
|
|
+ rec->rm_offset + rec->rm_blockcount - 1 != info->high.rm_offset)
|
|
|
+ return XFS_BTREE_QUERY_RANGE_CONTINUE;
|
|
|
+
|
|
|
+ *info->irec = *rec;
|
|
|
+ *info->stat = 1;
|
|
|
+ return XFS_BTREE_QUERY_RANGE_ABORT;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Find the record to the left of the given extent, being careful only to
|
|
|
+ * return a match with the same owner and adjacent physical and logical
|
|
|
+ * block ranges.
|
|
|
+ */
|
|
|
+int
|
|
|
+xfs_rmap_find_left_neighbor(
|
|
|
+ struct xfs_btree_cur *cur,
|
|
|
+ xfs_agblock_t bno,
|
|
|
+ uint64_t owner,
|
|
|
+ uint64_t offset,
|
|
|
+ unsigned int flags,
|
|
|
+ struct xfs_rmap_irec *irec,
|
|
|
+ int *stat)
|
|
|
+{
|
|
|
+ struct xfs_find_left_neighbor_info info;
|
|
|
+ int error;
|
|
|
+
|
|
|
+ *stat = 0;
|
|
|
+ if (bno == 0)
|
|
|
+ return 0;
|
|
|
+ info.high.rm_startblock = bno - 1;
|
|
|
+ info.high.rm_owner = owner;
|
|
|
+ if (!XFS_RMAP_NON_INODE_OWNER(owner) &&
|
|
|
+ !(flags & XFS_RMAP_BMBT_BLOCK)) {
|
|
|
+ if (offset == 0)
|
|
|
+ return 0;
|
|
|
+ info.high.rm_offset = offset - 1;
|
|
|
+ } else
|
|
|
+ info.high.rm_offset = 0;
|
|
|
+ info.high.rm_flags = flags;
|
|
|
+ info.high.rm_blockcount = 0;
|
|
|
+ info.irec = irec;
|
|
|
+ info.stat = stat;
|
|
|
+
|
|
|
+ trace_xfs_rmap_find_left_neighbor_query(cur->bc_mp,
|
|
|
+ cur->bc_private.a.agno, bno, 0, owner, offset, flags);
|
|
|
+
|
|
|
+ error = xfs_rmap_query_range(cur, &info.high, &info.high,
|
|
|
+ xfs_rmap_find_left_neighbor_helper, &info);
|
|
|
+ if (error == XFS_BTREE_QUERY_RANGE_ABORT)
|
|
|
+ error = 0;
|
|
|
+ if (*stat)
|
|
|
+ trace_xfs_rmap_find_left_neighbor_result(cur->bc_mp,
|
|
|
+ cur->bc_private.a.agno, irec->rm_startblock,
|
|
|
+ irec->rm_blockcount, irec->rm_owner,
|
|
|
+ irec->rm_offset, irec->rm_flags);
|
|
|
+ return error;
|
|
|
+}
|
|
|
+
|
|
|
+/* For each rmap given, figure out if it matches the key we want. */
|
|
|
+STATIC int
|
|
|
+xfs_rmap_lookup_le_range_helper(
|
|
|
+ struct xfs_btree_cur *cur,
|
|
|
+ struct xfs_rmap_irec *rec,
|
|
|
+ void *priv)
|
|
|
+{
|
|
|
+ struct xfs_find_left_neighbor_info *info = priv;
|
|
|
+
|
|
|
+ trace_xfs_rmap_lookup_le_range_candidate(cur->bc_mp,
|
|
|
+ cur->bc_private.a.agno, rec->rm_startblock,
|
|
|
+ rec->rm_blockcount, rec->rm_owner, rec->rm_offset,
|
|
|
+ rec->rm_flags);
|
|
|
+
|
|
|
+ if (rec->rm_owner != info->high.rm_owner)
|
|
|
+ return XFS_BTREE_QUERY_RANGE_CONTINUE;
|
|
|
+ if (!XFS_RMAP_NON_INODE_OWNER(rec->rm_owner) &&
|
|
|
+ !(rec->rm_flags & XFS_RMAP_BMBT_BLOCK) &&
|
|
|
+ (rec->rm_offset > info->high.rm_offset ||
|
|
|
+ rec->rm_offset + rec->rm_blockcount <= info->high.rm_offset))
|
|
|
+ return XFS_BTREE_QUERY_RANGE_CONTINUE;
|
|
|
+
|
|
|
+ *info->irec = *rec;
|
|
|
+ *info->stat = 1;
|
|
|
+ return XFS_BTREE_QUERY_RANGE_ABORT;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Find the record to the left of the given extent, being careful only to
|
|
|
+ * return a match with the same owner and overlapping physical and logical
|
|
|
+ * block ranges. This is the overlapping-interval version of
|
|
|
+ * xfs_rmap_lookup_le.
|
|
|
+ */
|
|
|
+int
|
|
|
+xfs_rmap_lookup_le_range(
|
|
|
+ struct xfs_btree_cur *cur,
|
|
|
+ xfs_agblock_t bno,
|
|
|
+ uint64_t owner,
|
|
|
+ uint64_t offset,
|
|
|
+ unsigned int flags,
|
|
|
+ struct xfs_rmap_irec *irec,
|
|
|
+ int *stat)
|
|
|
+{
|
|
|
+ struct xfs_find_left_neighbor_info info;
|
|
|
+ int error;
|
|
|
+
|
|
|
+ info.high.rm_startblock = bno;
|
|
|
+ info.high.rm_owner = owner;
|
|
|
+ if (!XFS_RMAP_NON_INODE_OWNER(owner) && !(flags & XFS_RMAP_BMBT_BLOCK))
|
|
|
+ info.high.rm_offset = offset;
|
|
|
+ else
|
|
|
+ info.high.rm_offset = 0;
|
|
|
+ info.high.rm_flags = flags;
|
|
|
+ info.high.rm_blockcount = 0;
|
|
|
+ *stat = 0;
|
|
|
+ info.irec = irec;
|
|
|
+ info.stat = stat;
|
|
|
+
|
|
|
+ trace_xfs_rmap_lookup_le_range(cur->bc_mp,
|
|
|
+ cur->bc_private.a.agno, bno, 0, owner, offset, flags);
|
|
|
+ error = xfs_rmap_query_range(cur, &info.high, &info.high,
|
|
|
+ xfs_rmap_lookup_le_range_helper, &info);
|
|
|
+ if (error == XFS_BTREE_QUERY_RANGE_ABORT)
|
|
|
+ error = 0;
|
|
|
+ if (*stat)
|
|
|
+ trace_xfs_rmap_lookup_le_range_result(cur->bc_mp,
|
|
|
+ cur->bc_private.a.agno, irec->rm_startblock,
|
|
|
+ irec->rm_blockcount, irec->rm_owner,
|
|
|
+ irec->rm_offset, irec->rm_flags);
|
|
|
+ return error;
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* Find the extent in the rmap btree and remove it.
|
|
|
*
|
|
@@ -1098,6 +1283,321 @@ done:
|
|
|
#undef RIGHT
|
|
|
#undef PREV
|
|
|
|
|
|
+/*
|
|
|
+ * Find an extent in the rmap btree and unmap it. For rmap extent types that
|
|
|
+ * can overlap (data fork rmaps on reflink filesystems) we must be careful
|
|
|
+ * that the prev/next records in the btree might belong to another owner.
|
|
|
+ * Therefore we must use delete+insert to alter any of the key fields.
|
|
|
+ *
|
|
|
+ * For every other situation there can only be one owner for a given extent,
|
|
|
+ * so we can call the regular _free function.
|
|
|
+ */
|
|
|
+STATIC int
|
|
|
+xfs_rmap_unmap_shared(
|
|
|
+ struct xfs_btree_cur *cur,
|
|
|
+ xfs_agblock_t bno,
|
|
|
+ xfs_extlen_t len,
|
|
|
+ bool unwritten,
|
|
|
+ struct xfs_owner_info *oinfo)
|
|
|
+{
|
|
|
+ struct xfs_mount *mp = cur->bc_mp;
|
|
|
+ struct xfs_rmap_irec ltrec;
|
|
|
+ uint64_t ltoff;
|
|
|
+ int error = 0;
|
|
|
+ int i;
|
|
|
+ uint64_t owner;
|
|
|
+ uint64_t offset;
|
|
|
+ unsigned int flags;
|
|
|
+
|
|
|
+ xfs_owner_info_unpack(oinfo, &owner, &offset, &flags);
|
|
|
+ if (unwritten)
|
|
|
+ flags |= XFS_RMAP_UNWRITTEN;
|
|
|
+ trace_xfs_rmap_unmap(mp, cur->bc_private.a.agno, bno, len,
|
|
|
+ unwritten, oinfo);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * We should always have a left record because there's a static record
|
|
|
+ * for the AG headers at rm_startblock == 0 created by mkfs/growfs that
|
|
|
+ * will not ever be removed from the tree.
|
|
|
+ */
|
|
|
+ error = xfs_rmap_lookup_le_range(cur, bno, owner, offset, flags,
|
|
|
+ <rec, &i);
|
|
|
+ if (error)
|
|
|
+ goto out_error;
|
|
|
+ XFS_WANT_CORRUPTED_GOTO(mp, i == 1, out_error);
|
|
|
+ ltoff = ltrec.rm_offset;
|
|
|
+
|
|
|
+ /* Make sure the extent we found covers the entire freeing range. */
|
|
|
+ XFS_WANT_CORRUPTED_GOTO(mp, ltrec.rm_startblock <= bno &&
|
|
|
+ ltrec.rm_startblock + ltrec.rm_blockcount >=
|
|
|
+ bno + len, out_error);
|
|
|
+
|
|
|
+ /* Make sure the owner matches what we expect to find in the tree. */
|
|
|
+ XFS_WANT_CORRUPTED_GOTO(mp, owner == ltrec.rm_owner, out_error);
|
|
|
+
|
|
|
+ /* Make sure the unwritten flag matches. */
|
|
|
+ XFS_WANT_CORRUPTED_GOTO(mp, (flags & XFS_RMAP_UNWRITTEN) ==
|
|
|
+ (ltrec.rm_flags & XFS_RMAP_UNWRITTEN), out_error);
|
|
|
+
|
|
|
+ /* Check the offset. */
|
|
|
+ XFS_WANT_CORRUPTED_GOTO(mp, ltrec.rm_offset <= offset, out_error);
|
|
|
+ XFS_WANT_CORRUPTED_GOTO(mp, offset <= ltoff + ltrec.rm_blockcount,
|
|
|
+ out_error);
|
|
|
+
|
|
|
+ if (ltrec.rm_startblock == bno && ltrec.rm_blockcount == len) {
|
|
|
+ /* Exact match, simply remove the record from rmap tree. */
|
|
|
+ error = xfs_rmap_delete(cur, ltrec.rm_startblock,
|
|
|
+ ltrec.rm_blockcount, ltrec.rm_owner,
|
|
|
+ ltrec.rm_offset, ltrec.rm_flags);
|
|
|
+ if (error)
|
|
|
+ goto out_error;
|
|
|
+ } else if (ltrec.rm_startblock == bno) {
|
|
|
+ /*
|
|
|
+ * Overlap left hand side of extent: move the start, trim the
|
|
|
+ * length and update the current record.
|
|
|
+ *
|
|
|
+ * ltbno ltlen
|
|
|
+ * Orig: |oooooooooooooooooooo|
|
|
|
+ * Freeing: |fffffffff|
|
|
|
+ * Result: |rrrrrrrrrr|
|
|
|
+ * bno len
|
|
|
+ */
|
|
|
+
|
|
|
+ /* Delete prev rmap. */
|
|
|
+ error = xfs_rmap_delete(cur, ltrec.rm_startblock,
|
|
|
+ ltrec.rm_blockcount, ltrec.rm_owner,
|
|
|
+ ltrec.rm_offset, ltrec.rm_flags);
|
|
|
+ if (error)
|
|
|
+ goto out_error;
|
|
|
+
|
|
|
+ /* Add an rmap at the new offset. */
|
|
|
+ ltrec.rm_startblock += len;
|
|
|
+ ltrec.rm_blockcount -= len;
|
|
|
+ ltrec.rm_offset += len;
|
|
|
+ error = xfs_rmap_insert(cur, ltrec.rm_startblock,
|
|
|
+ ltrec.rm_blockcount, ltrec.rm_owner,
|
|
|
+ ltrec.rm_offset, ltrec.rm_flags);
|
|
|
+ if (error)
|
|
|
+ goto out_error;
|
|
|
+ } else if (ltrec.rm_startblock + ltrec.rm_blockcount == bno + len) {
|
|
|
+ /*
|
|
|
+ * Overlap right hand side of extent: trim the length and
|
|
|
+ * update the current record.
|
|
|
+ *
|
|
|
+ * ltbno ltlen
|
|
|
+ * Orig: |oooooooooooooooooooo|
|
|
|
+ * Freeing: |fffffffff|
|
|
|
+ * Result: |rrrrrrrrrr|
|
|
|
+ * bno len
|
|
|
+ */
|
|
|
+ error = xfs_rmap_lookup_eq(cur, ltrec.rm_startblock,
|
|
|
+ ltrec.rm_blockcount, ltrec.rm_owner,
|
|
|
+ ltrec.rm_offset, ltrec.rm_flags, &i);
|
|
|
+ if (error)
|
|
|
+ goto out_error;
|
|
|
+ XFS_WANT_CORRUPTED_GOTO(mp, i == 1, out_error);
|
|
|
+ ltrec.rm_blockcount -= len;
|
|
|
+ error = xfs_rmap_update(cur, <rec);
|
|
|
+ if (error)
|
|
|
+ goto out_error;
|
|
|
+ } else {
|
|
|
+ /*
|
|
|
+ * Overlap middle of extent: trim the length of the existing
|
|
|
+ * record to the length of the new left-extent size, increment
|
|
|
+ * the insertion position so we can insert a new record
|
|
|
+ * containing the remaining right-extent space.
|
|
|
+ *
|
|
|
+ * ltbno ltlen
|
|
|
+ * Orig: |oooooooooooooooooooo|
|
|
|
+ * Freeing: |fffffffff|
|
|
|
+ * Result: |rrrrr| |rrrr|
|
|
|
+ * bno len
|
|
|
+ */
|
|
|
+ xfs_extlen_t orig_len = ltrec.rm_blockcount;
|
|
|
+
|
|
|
+ /* Shrink the left side of the rmap */
|
|
|
+ error = xfs_rmap_lookup_eq(cur, ltrec.rm_startblock,
|
|
|
+ ltrec.rm_blockcount, ltrec.rm_owner,
|
|
|
+ ltrec.rm_offset, ltrec.rm_flags, &i);
|
|
|
+ if (error)
|
|
|
+ goto out_error;
|
|
|
+ XFS_WANT_CORRUPTED_GOTO(mp, i == 1, out_error);
|
|
|
+ ltrec.rm_blockcount = bno - ltrec.rm_startblock;
|
|
|
+ error = xfs_rmap_update(cur, <rec);
|
|
|
+ if (error)
|
|
|
+ goto out_error;
|
|
|
+
|
|
|
+ /* Add an rmap at the new offset */
|
|
|
+ error = xfs_rmap_insert(cur, bno + len,
|
|
|
+ orig_len - len - ltrec.rm_blockcount,
|
|
|
+ ltrec.rm_owner, offset + len,
|
|
|
+ ltrec.rm_flags);
|
|
|
+ if (error)
|
|
|
+ goto out_error;
|
|
|
+ }
|
|
|
+
|
|
|
+ trace_xfs_rmap_unmap_done(mp, cur->bc_private.a.agno, bno, len,
|
|
|
+ unwritten, oinfo);
|
|
|
+out_error:
|
|
|
+ if (error)
|
|
|
+ trace_xfs_rmap_unmap_error(cur->bc_mp,
|
|
|
+ cur->bc_private.a.agno, error, _RET_IP_);
|
|
|
+ return error;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Find an extent in the rmap btree and map it. For rmap extent types that
|
|
|
+ * can overlap (data fork rmaps on reflink filesystems) we must be careful
|
|
|
+ * that the prev/next records in the btree might belong to another owner.
|
|
|
+ * Therefore we must use delete+insert to alter any of the key fields.
|
|
|
+ *
|
|
|
+ * For every other situation there can only be one owner for a given extent,
|
|
|
+ * so we can call the regular _alloc function.
|
|
|
+ */
|
|
|
+STATIC int
|
|
|
+xfs_rmap_map_shared(
|
|
|
+ struct xfs_btree_cur *cur,
|
|
|
+ xfs_agblock_t bno,
|
|
|
+ xfs_extlen_t len,
|
|
|
+ bool unwritten,
|
|
|
+ struct xfs_owner_info *oinfo)
|
|
|
+{
|
|
|
+ struct xfs_mount *mp = cur->bc_mp;
|
|
|
+ struct xfs_rmap_irec ltrec;
|
|
|
+ struct xfs_rmap_irec gtrec;
|
|
|
+ int have_gt;
|
|
|
+ int have_lt;
|
|
|
+ int error = 0;
|
|
|
+ int i;
|
|
|
+ uint64_t owner;
|
|
|
+ uint64_t offset;
|
|
|
+ unsigned int flags = 0;
|
|
|
+
|
|
|
+ xfs_owner_info_unpack(oinfo, &owner, &offset, &flags);
|
|
|
+ if (unwritten)
|
|
|
+ flags |= XFS_RMAP_UNWRITTEN;
|
|
|
+ trace_xfs_rmap_map(mp, cur->bc_private.a.agno, bno, len,
|
|
|
+ unwritten, oinfo);
|
|
|
+
|
|
|
+ /* Is there a left record that abuts our range? */
|
|
|
+ error = xfs_rmap_find_left_neighbor(cur, bno, owner, offset, flags,
|
|
|
+ <rec, &have_lt);
|
|
|
+ if (error)
|
|
|
+ goto out_error;
|
|
|
+ if (have_lt &&
|
|
|
+ !xfs_rmap_is_mergeable(<rec, owner, flags))
|
|
|
+ have_lt = 0;
|
|
|
+
|
|
|
+ /* Is there a right record that abuts our range? */
|
|
|
+ error = xfs_rmap_lookup_eq(cur, bno + len, len, owner, offset + len,
|
|
|
+ flags, &have_gt);
|
|
|
+ if (error)
|
|
|
+ goto out_error;
|
|
|
+ if (have_gt) {
|
|
|
+ error = xfs_rmap_get_rec(cur, >rec, &have_gt);
|
|
|
+ if (error)
|
|
|
+ goto out_error;
|
|
|
+ XFS_WANT_CORRUPTED_GOTO(mp, have_gt == 1, out_error);
|
|
|
+ trace_xfs_rmap_find_right_neighbor_result(cur->bc_mp,
|
|
|
+ cur->bc_private.a.agno, gtrec.rm_startblock,
|
|
|
+ gtrec.rm_blockcount, gtrec.rm_owner,
|
|
|
+ gtrec.rm_offset, gtrec.rm_flags);
|
|
|
+
|
|
|
+ if (!xfs_rmap_is_mergeable(>rec, owner, flags))
|
|
|
+ have_gt = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (have_lt &&
|
|
|
+ ltrec.rm_startblock + ltrec.rm_blockcount == bno &&
|
|
|
+ ltrec.rm_offset + ltrec.rm_blockcount == offset) {
|
|
|
+ /*
|
|
|
+ * Left edge contiguous, merge into left record.
|
|
|
+ *
|
|
|
+ * ltbno ltlen
|
|
|
+ * orig: |ooooooooo|
|
|
|
+ * adding: |aaaaaaaaa|
|
|
|
+ * result: |rrrrrrrrrrrrrrrrrrr|
|
|
|
+ * bno len
|
|
|
+ */
|
|
|
+ ltrec.rm_blockcount += len;
|
|
|
+ if (have_gt &&
|
|
|
+ bno + len == gtrec.rm_startblock &&
|
|
|
+ offset + len == gtrec.rm_offset) {
|
|
|
+ /*
|
|
|
+ * Right edge also contiguous, delete right record
|
|
|
+ * and merge into left record.
|
|
|
+ *
|
|
|
+ * ltbno ltlen gtbno gtlen
|
|
|
+ * orig: |ooooooooo| |ooooooooo|
|
|
|
+ * adding: |aaaaaaaaa|
|
|
|
+ * result: |rrrrrrrrrrrrrrrrrrrrrrrrrrrrr|
|
|
|
+ */
|
|
|
+ ltrec.rm_blockcount += gtrec.rm_blockcount;
|
|
|
+ error = xfs_rmap_delete(cur, gtrec.rm_startblock,
|
|
|
+ gtrec.rm_blockcount, gtrec.rm_owner,
|
|
|
+ gtrec.rm_offset, gtrec.rm_flags);
|
|
|
+ if (error)
|
|
|
+ goto out_error;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Point the cursor back to the left record and update. */
|
|
|
+ error = xfs_rmap_lookup_eq(cur, ltrec.rm_startblock,
|
|
|
+ ltrec.rm_blockcount, ltrec.rm_owner,
|
|
|
+ ltrec.rm_offset, ltrec.rm_flags, &i);
|
|
|
+ if (error)
|
|
|
+ goto out_error;
|
|
|
+ XFS_WANT_CORRUPTED_GOTO(mp, i == 1, out_error);
|
|
|
+
|
|
|
+ error = xfs_rmap_update(cur, <rec);
|
|
|
+ if (error)
|
|
|
+ goto out_error;
|
|
|
+ } else if (have_gt &&
|
|
|
+ bno + len == gtrec.rm_startblock &&
|
|
|
+ offset + len == gtrec.rm_offset) {
|
|
|
+ /*
|
|
|
+ * Right edge contiguous, merge into right record.
|
|
|
+ *
|
|
|
+ * gtbno gtlen
|
|
|
+ * Orig: |ooooooooo|
|
|
|
+ * adding: |aaaaaaaaa|
|
|
|
+ * Result: |rrrrrrrrrrrrrrrrrrr|
|
|
|
+ * bno len
|
|
|
+ */
|
|
|
+ /* Delete the old record. */
|
|
|
+ error = xfs_rmap_delete(cur, gtrec.rm_startblock,
|
|
|
+ gtrec.rm_blockcount, gtrec.rm_owner,
|
|
|
+ gtrec.rm_offset, gtrec.rm_flags);
|
|
|
+ if (error)
|
|
|
+ goto out_error;
|
|
|
+
|
|
|
+ /* Move the start and re-add it. */
|
|
|
+ gtrec.rm_startblock = bno;
|
|
|
+ gtrec.rm_blockcount += len;
|
|
|
+ gtrec.rm_offset = offset;
|
|
|
+ error = xfs_rmap_insert(cur, gtrec.rm_startblock,
|
|
|
+ gtrec.rm_blockcount, gtrec.rm_owner,
|
|
|
+ gtrec.rm_offset, gtrec.rm_flags);
|
|
|
+ if (error)
|
|
|
+ goto out_error;
|
|
|
+ } else {
|
|
|
+ /*
|
|
|
+ * No contiguous edge with identical owner, insert
|
|
|
+ * new record at current cursor position.
|
|
|
+ */
|
|
|
+ error = xfs_rmap_insert(cur, bno, len, owner, offset, flags);
|
|
|
+ if (error)
|
|
|
+ goto out_error;
|
|
|
+ }
|
|
|
+
|
|
|
+ trace_xfs_rmap_map_done(mp, cur->bc_private.a.agno, bno, len,
|
|
|
+ unwritten, oinfo);
|
|
|
+out_error:
|
|
|
+ if (error)
|
|
|
+ trace_xfs_rmap_map_error(cur->bc_mp,
|
|
|
+ cur->bc_private.a.agno, error, _RET_IP_);
|
|
|
+ return error;
|
|
|
+}
|
|
|
+
|
|
|
struct xfs_rmap_query_range_info {
|
|
|
xfs_rmap_query_range_fn fn;
|
|
|
void *priv;
|
|
@@ -1237,11 +1737,19 @@ xfs_rmap_finish_one(
|
|
|
case XFS_RMAP_MAP:
|
|
|
error = xfs_rmap_map(rcur, bno, blockcount, unwritten, &oinfo);
|
|
|
break;
|
|
|
+ case XFS_RMAP_MAP_SHARED:
|
|
|
+ error = xfs_rmap_map_shared(rcur, bno, blockcount, unwritten,
|
|
|
+ &oinfo);
|
|
|
+ break;
|
|
|
case XFS_RMAP_FREE:
|
|
|
case XFS_RMAP_UNMAP:
|
|
|
error = xfs_rmap_unmap(rcur, bno, blockcount, unwritten,
|
|
|
&oinfo);
|
|
|
break;
|
|
|
+ case XFS_RMAP_UNMAP_SHARED:
|
|
|
+ error = xfs_rmap_unmap_shared(rcur, bno, blockcount, unwritten,
|
|
|
+ &oinfo);
|
|
|
+ break;
|
|
|
case XFS_RMAP_CONVERT:
|
|
|
error = xfs_rmap_convert(rcur, bno, blockcount, !unwritten,
|
|
|
&oinfo);
|
|
@@ -1315,7 +1823,8 @@ xfs_rmap_map_extent(
|
|
|
if (!xfs_rmap_update_is_needed(mp, whichfork))
|
|
|
return 0;
|
|
|
|
|
|
- return __xfs_rmap_add(mp, dfops, XFS_RMAP_MAP, ip->i_ino,
|
|
|
+ return __xfs_rmap_add(mp, dfops, xfs_is_reflink_inode(ip) ?
|
|
|
+ XFS_RMAP_MAP_SHARED : XFS_RMAP_MAP, ip->i_ino,
|
|
|
whichfork, PREV);
|
|
|
}
|
|
|
|
|
@@ -1331,7 +1840,8 @@ xfs_rmap_unmap_extent(
|
|
|
if (!xfs_rmap_update_is_needed(mp, whichfork))
|
|
|
return 0;
|
|
|
|
|
|
- return __xfs_rmap_add(mp, dfops, XFS_RMAP_UNMAP, ip->i_ino,
|
|
|
+ return __xfs_rmap_add(mp, dfops, xfs_is_reflink_inode(ip) ?
|
|
|
+ XFS_RMAP_UNMAP_SHARED : XFS_RMAP_UNMAP, ip->i_ino,
|
|
|
whichfork, PREV);
|
|
|
}
|
|
|
|