| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370 |
- // SPDX-License-Identifier: GPL-2.0-or-later
- /*
- * Copyright (c) 2022-2024 Oracle. All Rights Reserved.
- * Author: Darrick J. Wong <djwong@kernel.org>
- */
- #include "xfs.h"
- #include "xfs_fs.h"
- #include "xfs_shared.h"
- #include "xfs_format.h"
- #include "xfs_trans_resv.h"
- #include "xfs_mount.h"
- #include "xfs_defer.h"
- #include "xfs_btree.h"
- #include "xfs_buf_mem.h"
- #include "xfs_btree_mem.h"
- #include "xfs_error.h"
- #include "scrub/rcbag_btree.h"
- #include "scrub/trace.h"
- static struct kmem_cache *rcbagbt_cur_cache;
- STATIC void
- rcbagbt_init_key_from_rec(
- union xfs_btree_key *key,
- const union xfs_btree_rec *rec)
- {
- struct rcbag_key *bag_key = (struct rcbag_key *)key;
- const struct rcbag_rec *bag_rec = (const struct rcbag_rec *)rec;
- BUILD_BUG_ON(sizeof(struct rcbag_key) > sizeof(union xfs_btree_key));
- BUILD_BUG_ON(sizeof(struct rcbag_rec) > sizeof(union xfs_btree_rec));
- bag_key->rbg_startblock = bag_rec->rbg_startblock;
- bag_key->rbg_blockcount = bag_rec->rbg_blockcount;
- }
- STATIC void
- rcbagbt_init_rec_from_cur(
- struct xfs_btree_cur *cur,
- union xfs_btree_rec *rec)
- {
- struct rcbag_rec *bag_rec = (struct rcbag_rec *)rec;
- struct rcbag_rec *bag_irec = (struct rcbag_rec *)&cur->bc_rec;
- bag_rec->rbg_startblock = bag_irec->rbg_startblock;
- bag_rec->rbg_blockcount = bag_irec->rbg_blockcount;
- bag_rec->rbg_refcount = bag_irec->rbg_refcount;
- }
- STATIC int64_t
- rcbagbt_key_diff(
- struct xfs_btree_cur *cur,
- const union xfs_btree_key *key)
- {
- struct rcbag_rec *rec = (struct rcbag_rec *)&cur->bc_rec;
- const struct rcbag_key *kp = (const struct rcbag_key *)key;
- if (kp->rbg_startblock > rec->rbg_startblock)
- return 1;
- if (kp->rbg_startblock < rec->rbg_startblock)
- return -1;
- if (kp->rbg_blockcount > rec->rbg_blockcount)
- return 1;
- if (kp->rbg_blockcount < rec->rbg_blockcount)
- return -1;
- return 0;
- }
- STATIC int64_t
- rcbagbt_diff_two_keys(
- struct xfs_btree_cur *cur,
- const union xfs_btree_key *k1,
- const union xfs_btree_key *k2,
- const union xfs_btree_key *mask)
- {
- const struct rcbag_key *kp1 = (const struct rcbag_key *)k1;
- const struct rcbag_key *kp2 = (const struct rcbag_key *)k2;
- ASSERT(mask == NULL);
- if (kp1->rbg_startblock > kp2->rbg_startblock)
- return 1;
- if (kp1->rbg_startblock < kp2->rbg_startblock)
- return -1;
- if (kp1->rbg_blockcount > kp2->rbg_blockcount)
- return 1;
- if (kp1->rbg_blockcount < kp2->rbg_blockcount)
- return -1;
- return 0;
- }
- STATIC int
- rcbagbt_keys_inorder(
- struct xfs_btree_cur *cur,
- const union xfs_btree_key *k1,
- const union xfs_btree_key *k2)
- {
- const struct rcbag_key *kp1 = (const struct rcbag_key *)k1;
- const struct rcbag_key *kp2 = (const struct rcbag_key *)k2;
- if (kp1->rbg_startblock > kp2->rbg_startblock)
- return 0;
- if (kp1->rbg_startblock < kp2->rbg_startblock)
- return 1;
- if (kp1->rbg_blockcount > kp2->rbg_blockcount)
- return 0;
- if (kp1->rbg_blockcount < kp2->rbg_blockcount)
- return 1;
- return 0;
- }
- STATIC int
- rcbagbt_recs_inorder(
- struct xfs_btree_cur *cur,
- const union xfs_btree_rec *r1,
- const union xfs_btree_rec *r2)
- {
- const struct rcbag_rec *rp1 = (const struct rcbag_rec *)r1;
- const struct rcbag_rec *rp2 = (const struct rcbag_rec *)r2;
- if (rp1->rbg_startblock > rp2->rbg_startblock)
- return 0;
- if (rp1->rbg_startblock < rp2->rbg_startblock)
- return 1;
- if (rp1->rbg_blockcount > rp2->rbg_blockcount)
- return 0;
- if (rp1->rbg_blockcount < rp2->rbg_blockcount)
- return 1;
- return 0;
- }
- static xfs_failaddr_t
- rcbagbt_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;
- unsigned int maxrecs;
- if (!xfs_verify_magic(bp, block->bb_magic))
- return __this_address;
- fa = xfs_btree_fsblock_v5hdr_verify(bp, XFS_RMAP_OWN_UNKNOWN);
- if (fa)
- return fa;
- level = be16_to_cpu(block->bb_level);
- if (level >= rcbagbt_maxlevels_possible())
- return __this_address;
- maxrecs = rcbagbt_maxrecs(mp, XFBNO_BLOCKSIZE, level == 0);
- return xfs_btree_memblock_verify(bp, maxrecs);
- }
- static void
- rcbagbt_rw_verify(
- struct xfs_buf *bp)
- {
- xfs_failaddr_t fa = rcbagbt_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 rcbagbt_mem_buf_ops = {
- .name = "rcbagbt_mem",
- .magic = { 0, cpu_to_be32(RCBAG_MAGIC) },
- .verify_read = rcbagbt_rw_verify,
- .verify_write = rcbagbt_rw_verify,
- .verify_struct = rcbagbt_verify,
- };
- static const struct xfs_btree_ops rcbagbt_mem_ops = {
- .name = "rcbag",
- .type = XFS_BTREE_TYPE_MEM,
- .rec_len = sizeof(struct rcbag_rec),
- .key_len = sizeof(struct rcbag_key),
- .ptr_len = XFS_BTREE_LONG_PTR_LEN,
- .lru_refs = 1,
- .statoff = XFS_STATS_CALC_INDEX(xs_rcbag_2),
- .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 = rcbagbt_init_key_from_rec,
- .init_rec_from_cur = rcbagbt_init_rec_from_cur,
- .init_ptr_from_cur = xfbtree_init_ptr_from_cur,
- .key_diff = rcbagbt_key_diff,
- .buf_ops = &rcbagbt_mem_buf_ops,
- .diff_two_keys = rcbagbt_diff_two_keys,
- .keys_inorder = rcbagbt_keys_inorder,
- .recs_inorder = rcbagbt_recs_inorder,
- };
- /* Create a cursor for an in-memory btree. */
- struct xfs_btree_cur *
- rcbagbt_mem_cursor(
- struct xfs_mount *mp,
- struct xfs_trans *tp,
- struct xfbtree *xfbtree)
- {
- struct xfs_btree_cur *cur;
- cur = xfs_btree_alloc_cursor(mp, tp, &rcbagbt_mem_ops,
- rcbagbt_maxlevels_possible(), rcbagbt_cur_cache);
- cur->bc_mem.xfbtree = xfbtree;
- cur->bc_nlevels = xfbtree->nlevels;
- return cur;
- }
- /* Create an in-memory refcount bag btree. */
- int
- rcbagbt_mem_init(
- struct xfs_mount *mp,
- struct xfbtree *xfbt,
- struct xfs_buftarg *btp)
- {
- xfbt->owner = 0;
- return xfbtree_init(mp, xfbt, btp, &rcbagbt_mem_ops);
- }
- /* Calculate number of records in a refcount bag btree block. */
- static inline unsigned int
- rcbagbt_block_maxrecs(
- unsigned int blocklen,
- bool leaf)
- {
- if (leaf)
- return blocklen / sizeof(struct rcbag_rec);
- return blocklen /
- (sizeof(struct rcbag_key) + sizeof(rcbag_ptr_t));
- }
- /*
- * Calculate number of records in an refcount bag btree block.
- */
- unsigned int
- rcbagbt_maxrecs(
- struct xfs_mount *mp,
- unsigned int blocklen,
- bool leaf)
- {
- blocklen -= RCBAG_BLOCK_LEN;
- return rcbagbt_block_maxrecs(blocklen, leaf);
- }
- /* Compute the max possible height for refcount bag btrees. */
- unsigned int
- rcbagbt_maxlevels_possible(void)
- {
- unsigned int minrecs[2];
- unsigned int blocklen;
- blocklen = XFBNO_BLOCKSIZE - XFS_BTREE_LBLOCK_CRC_LEN;
- minrecs[0] = rcbagbt_block_maxrecs(blocklen, true) / 2;
- minrecs[1] = rcbagbt_block_maxrecs(blocklen, false) / 2;
- return xfs_btree_space_to_height(minrecs, ULLONG_MAX);
- }
- /* Calculate the refcount bag btree size for some records. */
- unsigned long long
- rcbagbt_calc_size(
- unsigned long long nr_records)
- {
- unsigned int minrecs[2];
- unsigned int blocklen;
- blocklen = XFBNO_BLOCKSIZE - XFS_BTREE_LBLOCK_CRC_LEN;
- minrecs[0] = rcbagbt_block_maxrecs(blocklen, true) / 2;
- minrecs[1] = rcbagbt_block_maxrecs(blocklen, false) / 2;
- return xfs_btree_calc_size(minrecs, nr_records);
- }
- int __init
- rcbagbt_init_cur_cache(void)
- {
- rcbagbt_cur_cache = kmem_cache_create("xfs_rcbagbt_cur",
- xfs_btree_cur_sizeof(rcbagbt_maxlevels_possible()),
- 0, 0, NULL);
- if (!rcbagbt_cur_cache)
- return -ENOMEM;
- return 0;
- }
- void
- rcbagbt_destroy_cur_cache(void)
- {
- kmem_cache_destroy(rcbagbt_cur_cache);
- rcbagbt_cur_cache = NULL;
- }
- /* Look up the refcount bag record corresponding to this reverse mapping. */
- int
- rcbagbt_lookup_eq(
- struct xfs_btree_cur *cur,
- const struct xfs_rmap_irec *rmap,
- int *success)
- {
- struct rcbag_rec *rec = (struct rcbag_rec *)&cur->bc_rec;
- rec->rbg_startblock = rmap->rm_startblock;
- rec->rbg_blockcount = rmap->rm_blockcount;
- return xfs_btree_lookup(cur, XFS_LOOKUP_EQ, success);
- }
- /* Get the data from the pointed-to record. */
- int
- rcbagbt_get_rec(
- struct xfs_btree_cur *cur,
- struct rcbag_rec *rec,
- int *has)
- {
- union xfs_btree_rec *btrec;
- int error;
- error = xfs_btree_get_rec(cur, &btrec, has);
- if (error || !(*has))
- return error;
- memcpy(rec, btrec, sizeof(struct rcbag_rec));
- return 0;
- }
- /* Update the record referred to by cur to the value given. */
- int
- rcbagbt_update(
- struct xfs_btree_cur *cur,
- const struct rcbag_rec *rec)
- {
- union xfs_btree_rec btrec;
- memcpy(&btrec, rec, sizeof(struct rcbag_rec));
- return xfs_btree_update(cur, &btrec);
- }
- /* Update the record referred to by cur to the value given. */
- int
- rcbagbt_insert(
- struct xfs_btree_cur *cur,
- const struct rcbag_rec *rec,
- int *success)
- {
- struct rcbag_rec *btrec = (struct rcbag_rec *)&cur->bc_rec;
- memcpy(btrec, rec, sizeof(struct rcbag_rec));
- return xfs_btree_insert(cur, success);
- }
|