// SPDX-License-Identifier: GPL-2.0
#include <linux/sizes.h>
#include "btrfs-tests.h"
#include "../transaction.h"
#include "../delayed-ref.h"
#include "../extent-tree.h"
#define FAKE_ROOT_OBJECTID 256
#define FAKE_BYTENR 0
#define FAKE_LEVEL 1
#define FAKE_INO 256
#define FAKE_FILE_OFFSET 0
#define FAKE_PARENT SZ_1M
struct ref_head_check {
u64 bytenr;
u64 num_bytes;
int ref_mod;
int total_ref_mod;
int must_insert;
};
struct ref_node_check {
u64 bytenr;
u64 num_bytes;
int ref_mod;
enum btrfs_delayed_ref_action action;
u8 type;
u64 parent;
u64 root;
u64 owner;
u64 offset;
};
static enum btrfs_ref_type ref_type_from_disk_ref_type(u8 type)
{
if ((type == BTRFS_TREE_BLOCK_REF_KEY) ||
(type == BTRFS_SHARED_BLOCK_REF_KEY))
return BTRFS_REF_METADATA;
return BTRFS_REF_DATA;
}
static void delete_delayed_ref_head(struct btrfs_trans_handle *trans,
struct btrfs_delayed_ref_head *head)
{
struct btrfs_fs_info *fs_info = trans->fs_info;
struct btrfs_delayed_ref_root *delayed_refs =
&trans->transaction->delayed_refs;
spin_lock(&delayed_refs->lock);
spin_lock(&head->lock);
btrfs_delete_ref_head(fs_info, delayed_refs, head);
spin_unlock(&head->lock);
spin_unlock(&delayed_refs->lock);
btrfs_delayed_ref_unlock(head);
btrfs_put_delayed_ref_head(head);
}
static void delete_delayed_ref_node(struct btrfs_delayed_ref_head *head,
struct btrfs_delayed_ref_node *node)
{
rb_erase_cached(&node->ref_node, &head->ref_tree);
RB_CLEAR_NODE(&node->ref_node);
if (!list_empty(&node->add_list))
list_del_init(&node->add_list);
btrfs_put_delayed_ref(node);
}
static int validate_ref_head(struct btrfs_delayed_ref_head *head,
struct ref_head_check *check)
{
if (head->bytenr != check->bytenr) {
test_err("invalid bytenr have: %llu want: %llu", head->bytenr,
check->bytenr);
return -EINVAL;
}
if (head->num_bytes != check->num_bytes) {
test_err("invalid num_bytes have: %llu want: %llu",
head->num_bytes, check->num_bytes);
return -EINVAL;
}
if (head->ref_mod != check->ref_mod) {
test_err("invalid ref_mod have: %d want: %d", head->ref_mod,
check->ref_mod);
return -EINVAL;
}
if (head->total_ref_mod != check->total_ref_mod) {
test_err("invalid total_ref_mod have: %d want: %d",
head->total_ref_mod, check->total_ref_mod);
return -EINVAL;
}
if (head->must_insert_reserved != check->must_insert) {
test_err("invalid must_insert have: %d want: %d",
head->must_insert_reserved, check->must_insert);
return -EINVAL;
}
return 0;
}
static int validate_ref_node(struct btrfs_delayed_ref_node *node,
struct ref_node_check *check)
{
if (node->bytenr != check->bytenr) {
test_err("invalid bytenr have: %llu want: %llu", node->bytenr,
check->bytenr);
return -EINVAL;
}
if (node->num_bytes != check->num_bytes) {
test_err("invalid num_bytes have: %llu want: %llu",
node->num_bytes, check->num_bytes);
return -EINVAL;
}
if (node->ref_mod != check->ref_mod) {
test_err("invalid ref_mod have: %d want: %d", node->ref_mod,
check->ref_mod);
return -EINVAL;
}
if (node->action != check->action) {
test_err("invalid action have: %d want: %d", node->action,
check->action);
return -EINVAL;
}
if (node->parent != check->parent) {
test_err("invalid parent have: %llu want: %llu", node->parent,
check->parent);
return -EINVAL;
}
if (node->ref_root != check->root) {
test_err("invalid root have: %llu want: %llu", node->ref_root,
check->root);
return -EINVAL;
}
if (node->type != check->type) {
test_err("invalid type have: %d want: %d", node->type,
check->type);
return -EINVAL;
}
if (btrfs_delayed_ref_owner(node) != check->owner) {
test_err("invalid owner have: %llu want: %llu",
btrfs_delayed_ref_owner(node), check->owner);
return -EINVAL;
}
if (btrfs_delayed_ref_offset(node) != check->offset) {
test_err("invalid offset have: %llu want: %llu",
btrfs_delayed_ref_offset(node), check->offset);
return -EINVAL;
}
return 0;
}
static int simple_test(struct btrfs_trans_handle *trans,
struct ref_head_check *head_check,
struct ref_node_check *node_check)
{
struct btrfs_delayed_ref_root *delayed_refs =
&trans->transaction->delayed_refs;
struct btrfs_fs_info *fs_info = trans->fs_info;
struct btrfs_delayed_ref_head *head;
struct btrfs_delayed_ref_node *node;
struct btrfs_ref ref = {
.type = ref_type_from_disk_ref_type(node_check->type),
.action = node_check->action,