summaryrefslogtreecommitdiff
path: root/fs/xfs/libxfs/xfs_rmap_btree.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/xfs/libxfs/xfs_rmap_btree.c')
-rw-r--r--fs/xfs/libxfs/xfs_rmap_btree.c157
1 files changed, 157 insertions, 0 deletions
diff --git a/fs/xfs/libxfs/xfs_rmap_btree.c b/fs/xfs/libxfs/xfs_rmap_btree.c
index cb080479fec2..42f7fe5eeec0 100644
--- a/fs/xfs/libxfs/xfs_rmap_btree.c
+++ b/fs/xfs/libxfs/xfs_rmap_btree.c
@@ -21,6 +21,9 @@
#include "xfs_extent_busy.h"
#include "xfs_ag.h"
#include "xfs_ag_resv.h"
+#include "scrub/xfile.h"
+#include "scrub/xfbtree.h"
+#include "xfs_btree_mem.h"
static struct kmem_cache *xfs_rmapbt_cur_cache;
@@ -567,6 +570,160 @@ xfs_rmapbt_stage_cursor(
return cur;
}
+#ifdef CONFIG_XFS_IN_MEMORY_BTREE
+/*
+ * Validate an in-memory rmap btree block. Callers are allowed to generate an
+ * in-memory btree even if the ondisk feature is not enabled.
+ */
+static xfs_failaddr_t
+xfs_rmapbt_mem_verify(
+ struct xfs_buf *bp)
+{
+ struct xfs_mount *mp = bp->b_mount;
+ struct xfs_btree_block *block = XFS_BUF_TO_BLOCK(bp);
+ xfs_failaddr_t fa;
+ unsigned int level;
+
+ if (!xfs_verify_magic(bp, block->bb_magic))
+ return __this_address;
+
+ fa = xfs_btree_sblock_v5hdr_verify(bp);
+ if (fa)
+ return fa;
+
+ level = be16_to_cpu(block->bb_level);
+ if (xfs_has_rmapbt(mp)) {
+ if (level >= mp->m_rmap_maxlevels)
+ return __this_address;
+ } else {
+ if (level >= xfs_rmapbt_maxlevels_ondisk())
+ return __this_address;
+ }
+
+ return xfbtree_sblock_verify(bp,
+ xfs_rmapbt_maxrecs(xfo_to_b(1), level == 0));
+}
+
+static void
+xfs_rmapbt_mem_rw_verify(
+ struct xfs_buf *bp)
+{
+ xfs_failaddr_t fa = xfs_rmapbt_mem_verify(bp);
+
+ if (fa)
+ xfs_verifier_error(bp, -EFSCORRUPTED, fa);
+}
+
+/* skip crc checks on in-memory btrees to save time */
+static const struct xfs_buf_ops xfs_rmapbt_mem_buf_ops = {
+ .name = "xfs_rmapbt_mem",
+ .magic = { 0, cpu_to_be32(XFS_RMAP_CRC_MAGIC) },
+ .verify_read = xfs_rmapbt_mem_rw_verify,
+ .verify_write = xfs_rmapbt_mem_rw_verify,
+ .verify_struct = xfs_rmapbt_mem_verify,
+};
+
+static const struct xfs_btree_ops xfs_rmapbt_mem_ops = {
+ .rec_len = sizeof(struct xfs_rmap_rec),
+ .key_len = 2 * sizeof(struct xfs_rmap_key),
+
+ .dup_cursor = xfbtree_dup_cursor,
+ .set_root = xfbtree_set_root,
+ .alloc_block = xfbtree_alloc_block,
+ .free_block = xfbtree_free_block,
+ .get_minrecs = xfbtree_get_minrecs,
+ .get_maxrecs = xfbtree_get_maxrecs,
+ .init_key_from_rec = xfs_rmapbt_init_key_from_rec,
+ .init_high_key_from_rec = xfs_rmapbt_init_high_key_from_rec,
+ .init_rec_from_cur = xfs_rmapbt_init_rec_from_cur,
+ .init_ptr_from_cur = xfbtree_init_ptr_from_cur,
+ .key_diff = xfs_rmapbt_key_diff,
+ .buf_ops = &xfs_rmapbt_mem_buf_ops,
+ .diff_two_keys = xfs_rmapbt_diff_two_keys,
+ .keys_inorder = xfs_rmapbt_keys_inorder,
+ .recs_inorder = xfs_rmapbt_recs_inorder,
+ .has_key_gap = xfs_rmapbt_has_key_gap,
+ .mask_key = xfs_rmapbt_mask_key,
+};
+
+/* Create a cursor for an in-memory btree. */
+struct xfs_btree_cur *
+xfs_rmapbt_mem_cursor(
+ struct xfs_mount *mp,
+ struct xfs_trans *tp,
+ struct xfs_buf *head_bp,
+ struct xfbtree *xfbtree)
+{
+ struct xfs_btree_cur *cur;
+
+ /* Overlapping btree; 2 keys per pointer. */
+ cur = xfs_btree_alloc_cursor(mp, tp, XFS_BTNUM_RMAP,
+ mp->m_rmap_maxlevels, xfs_rmapbt_cur_cache);
+ cur->bc_flags = XFS_BTREE_CRC_BLOCKS | XFS_BTREE_OVERLAPPING |
+ XFS_BTREE_IN_MEMORY;
+ cur->bc_statoff = XFS_STATS_CALC_INDEX(xs_rmap_2);
+ cur->bc_ops = &xfs_rmapbt_mem_ops;
+ cur->bc_mem.xfbtree = xfbtree;
+ cur->bc_mem.head_bp = head_bp;
+ cur->bc_nlevels = xfs_btree_mem_head_nlevels(head_bp);
+
+ return cur;
+}
+
+/* Create an in-memory rmap btree. */
+int
+xfs_rmapbt_mem_create(
+ struct xfs_mount *mp,
+ xfs_agnumber_t agno,
+ struct xfs_buftarg *target,
+ struct xfbtree **xfbtreep)
+{
+ struct xfbtree_config cfg = {
+ .btree_ops = &xfs_rmapbt_mem_ops,
+ .target = target,
+ .btnum = XFS_BTNUM_RMAP,
+ .owner = agno,
+ };
+
+ return xfbtree_create(mp, &cfg, xfbtreep);
+}
+
+/*
+ * Validate that a rmap record stored in an in-memory btree points somewhere
+ * within the ondisk filesystem.
+ */
+bool
+xfs_rmapbt_mem_verify_rec(
+ struct xfs_btree_cur *cur,
+ const struct xfs_rmap_irec *irec)
+{
+ struct xfs_mount *mp = cur->bc_mp;
+ struct xfs_perag *pag;
+ xfs_agnumber_t agno = cur->bc_mem.xfbtree->owner;
+ bool ret = false;
+
+ if (irec->rm_startblock <= XFS_AGFL_BLOCK(mp)) {
+ if (irec->rm_owner != XFS_RMAP_OWN_FS)
+ return false;
+ return irec->rm_blockcount == XFS_AGFL_BLOCK(mp) + 1;
+ }
+
+ /* check for valid extent range, including overflow */
+ pag = xfs_perag_get(mp, agno);
+ if (!xfs_verify_agbno(pag, irec->rm_startblock))
+ goto out;
+ if (irec->rm_startblock > irec->rm_startblock + irec->rm_blockcount)
+ goto out;
+ if (!xfs_verify_agbno(pag,
+ irec->rm_startblock + irec->rm_blockcount - 1))
+ goto out;
+ ret = true;
+out:
+ xfs_perag_put(pag);
+ return ret;
+}
+#endif /* CONFIG_XFS_IN_MEMORY_BTREE */
+
/*
* Install a new reverse mapping btree root. Caller is responsible for
* invalidating and freeing the old btree blocks.