7a2192ac10
Create a bag structure for refcount information that uses the refcount bag btree defined in the previous patch. Signed-off-by: Darrick J. Wong <djwong@kernel.org> Reviewed-by: Christoph Hellwig <hch@lst.de>
371 lines
8.7 KiB
C
371 lines
8.7 KiB
C
// 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);
|
|
}
|