mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
synced 2025-01-07 22:42:04 +00:00
a095686a23
Adapt the generic btree cursor code to be able to create a btree whose buffers come from a (presumably in-memory) buftarg with a header block that's specific to in-memory btrees. We'll connect this to other parts of online scrub in the next patches. Note that in-memory btrees always have a block size matching the system memory page size for efficiency reasons. There are also a few things we need to do to finalize a btree update; that's covered in the next patch. Signed-off-by: Darrick J. Wong <djwong@kernel.org> Reviewed-by: Christoph Hellwig <hch@lst.de>
229 lines
4.9 KiB
C
229 lines
4.9 KiB
C
/* SPDX-License-Identifier: GPL-2.0 */
|
|
/*
|
|
* Copyright (c) 2021-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_log_format.h"
|
|
#include "xfs_trans_resv.h"
|
|
#include "xfs_mount.h"
|
|
#include "xfs_trans.h"
|
|
#include "xfs_btree.h"
|
|
#include "xfs_error.h"
|
|
#include "xfs_buf_mem.h"
|
|
#include "xfs_btree_mem.h"
|
|
#include "xfs_ag.h"
|
|
#include "xfs_buf_item.h"
|
|
#include "xfs_trace.h"
|
|
|
|
/* Set the root of an in-memory btree. */
|
|
void
|
|
xfbtree_set_root(
|
|
struct xfs_btree_cur *cur,
|
|
const union xfs_btree_ptr *ptr,
|
|
int inc)
|
|
{
|
|
ASSERT(cur->bc_ops->type == XFS_BTREE_TYPE_MEM);
|
|
|
|
cur->bc_mem.xfbtree->root = *ptr;
|
|
cur->bc_mem.xfbtree->nlevels += inc;
|
|
}
|
|
|
|
/* Initialize a pointer from the in-memory btree header. */
|
|
void
|
|
xfbtree_init_ptr_from_cur(
|
|
struct xfs_btree_cur *cur,
|
|
union xfs_btree_ptr *ptr)
|
|
{
|
|
ASSERT(cur->bc_ops->type == XFS_BTREE_TYPE_MEM);
|
|
|
|
*ptr = cur->bc_mem.xfbtree->root;
|
|
}
|
|
|
|
/* Duplicate an in-memory btree cursor. */
|
|
struct xfs_btree_cur *
|
|
xfbtree_dup_cursor(
|
|
struct xfs_btree_cur *cur)
|
|
{
|
|
struct xfs_btree_cur *ncur;
|
|
|
|
ASSERT(cur->bc_ops->type == XFS_BTREE_TYPE_MEM);
|
|
|
|
ncur = xfs_btree_alloc_cursor(cur->bc_mp, cur->bc_tp, cur->bc_ops,
|
|
cur->bc_maxlevels, cur->bc_cache);
|
|
ncur->bc_flags = cur->bc_flags;
|
|
ncur->bc_nlevels = cur->bc_nlevels;
|
|
ncur->bc_mem.xfbtree = cur->bc_mem.xfbtree;
|
|
|
|
if (cur->bc_mem.pag)
|
|
ncur->bc_mem.pag = xfs_perag_hold(cur->bc_mem.pag);
|
|
|
|
return ncur;
|
|
}
|
|
|
|
/* Close the btree xfile and release all resources. */
|
|
void
|
|
xfbtree_destroy(
|
|
struct xfbtree *xfbt)
|
|
{
|
|
xfs_buftarg_drain(xfbt->target);
|
|
}
|
|
|
|
/* Compute the number of bytes available for records. */
|
|
static inline unsigned int
|
|
xfbtree_rec_bytes(
|
|
struct xfs_mount *mp,
|
|
const struct xfs_btree_ops *ops)
|
|
{
|
|
return XMBUF_BLOCKSIZE - XFS_BTREE_LBLOCK_CRC_LEN;
|
|
}
|
|
|
|
/* Initialize an empty leaf block as the btree root. */
|
|
STATIC int
|
|
xfbtree_init_leaf_block(
|
|
struct xfs_mount *mp,
|
|
struct xfbtree *xfbt,
|
|
const struct xfs_btree_ops *ops)
|
|
{
|
|
struct xfs_buf *bp;
|
|
xfbno_t bno = xfbt->highest_bno++;
|
|
int error;
|
|
|
|
error = xfs_buf_get(xfbt->target, xfbno_to_daddr(bno), XFBNO_BBSIZE,
|
|
&bp);
|
|
if (error)
|
|
return error;
|
|
|
|
trace_xfbtree_create_root_buf(xfbt, bp);
|
|
|
|
bp->b_ops = ops->buf_ops;
|
|
xfs_btree_init_buf(mp, bp, ops, 0, 0, xfbt->owner);
|
|
xfs_buf_relse(bp);
|
|
|
|
xfbt->root.l = cpu_to_be64(bno);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Create an in-memory btree root that can be used with the given xmbuf.
|
|
* Callers must set xfbt->owner.
|
|
*/
|
|
int
|
|
xfbtree_init(
|
|
struct xfs_mount *mp,
|
|
struct xfbtree *xfbt,
|
|
struct xfs_buftarg *btp,
|
|
const struct xfs_btree_ops *ops)
|
|
{
|
|
unsigned int blocklen = xfbtree_rec_bytes(mp, ops);
|
|
unsigned int keyptr_len;
|
|
int error;
|
|
|
|
/* Requires a long-format CRC-format btree */
|
|
if (!xfs_has_crc(mp)) {
|
|
ASSERT(xfs_has_crc(mp));
|
|
return -EINVAL;
|
|
}
|
|
if (ops->ptr_len != XFS_BTREE_LONG_PTR_LEN) {
|
|
ASSERT(ops->ptr_len == XFS_BTREE_LONG_PTR_LEN);
|
|
return -EINVAL;
|
|
}
|
|
|
|
memset(xfbt, 0, sizeof(*xfbt));
|
|
xfbt->target = btp;
|
|
|
|
/* Set up min/maxrecs for this btree. */
|
|
keyptr_len = ops->key_len + sizeof(__be64);
|
|
xfbt->maxrecs[0] = blocklen / ops->rec_len;
|
|
xfbt->maxrecs[1] = blocklen / keyptr_len;
|
|
xfbt->minrecs[0] = xfbt->maxrecs[0] / 2;
|
|
xfbt->minrecs[1] = xfbt->maxrecs[1] / 2;
|
|
xfbt->highest_bno = 0;
|
|
xfbt->nlevels = 1;
|
|
|
|
/* Initialize the empty btree. */
|
|
error = xfbtree_init_leaf_block(mp, xfbt, ops);
|
|
if (error)
|
|
goto err_freesp;
|
|
|
|
trace_xfbtree_init(mp, xfbt, ops);
|
|
|
|
return 0;
|
|
|
|
err_freesp:
|
|
xfs_buftarg_drain(xfbt->target);
|
|
return error;
|
|
}
|
|
|
|
/* Allocate a block to our in-memory btree. */
|
|
int
|
|
xfbtree_alloc_block(
|
|
struct xfs_btree_cur *cur,
|
|
const union xfs_btree_ptr *start,
|
|
union xfs_btree_ptr *new,
|
|
int *stat)
|
|
{
|
|
struct xfbtree *xfbt = cur->bc_mem.xfbtree;
|
|
xfbno_t bno = xfbt->highest_bno++;
|
|
|
|
ASSERT(cur->bc_ops->type == XFS_BTREE_TYPE_MEM);
|
|
|
|
trace_xfbtree_alloc_block(xfbt, cur, bno);
|
|
|
|
/* Fail if the block address exceeds the maximum for the buftarg. */
|
|
if (!xfbtree_verify_bno(xfbt, bno)) {
|
|
ASSERT(xfbtree_verify_bno(xfbt, bno));
|
|
*stat = 0;
|
|
return 0;
|
|
}
|
|
|
|
new->l = cpu_to_be64(bno);
|
|
*stat = 1;
|
|
return 0;
|
|
}
|
|
|
|
/* Free a block from our in-memory btree. */
|
|
int
|
|
xfbtree_free_block(
|
|
struct xfs_btree_cur *cur,
|
|
struct xfs_buf *bp)
|
|
{
|
|
struct xfbtree *xfbt = cur->bc_mem.xfbtree;
|
|
xfs_daddr_t daddr = xfs_buf_daddr(bp);
|
|
xfbno_t bno = xfs_daddr_to_xfbno(daddr);
|
|
|
|
ASSERT(cur->bc_ops->type == XFS_BTREE_TYPE_MEM);
|
|
|
|
trace_xfbtree_free_block(xfbt, cur, bno);
|
|
|
|
if (bno + 1 == xfbt->highest_bno)
|
|
xfbt->highest_bno--;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Return the minimum number of records for a btree block. */
|
|
int
|
|
xfbtree_get_minrecs(
|
|
struct xfs_btree_cur *cur,
|
|
int level)
|
|
{
|
|
struct xfbtree *xfbt = cur->bc_mem.xfbtree;
|
|
|
|
return xfbt->minrecs[level != 0];
|
|
}
|
|
|
|
/* Return the maximum number of records for a btree block. */
|
|
int
|
|
xfbtree_get_maxrecs(
|
|
struct xfs_btree_cur *cur,
|
|
int level)
|
|
{
|
|
struct xfbtree *xfbt = cur->bc_mem.xfbtree;
|
|
|
|
return xfbt->maxrecs[level != 0];
|
|
}
|