btrfs-progs: track csum, extent, and free space trees in a rb tree

We are going to have multiples of these trees with extent tree v2, so
add a rb tree to track them based on their root key value.  This works
for both v1 and v2, so we can remove the direct pointers to these roots
in our fs_info.

Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Signed-off-by: David Sterba <dsterba@suse.com>
This commit is contained in:
Josef Bacik 2021-11-08 14:26:43 -05:00 committed by David Sterba
parent 0b23744de5
commit b057607325
5 changed files with 261 additions and 83 deletions

View file

@ -1163,15 +1163,13 @@ struct btrfs_fs_info {
u8 chunk_tree_uuid[BTRFS_UUID_SIZE];
u8 *new_chunk_tree_uuid;
struct btrfs_root *fs_root;
struct btrfs_root *_extent_root;
struct btrfs_root *tree_root;
struct btrfs_root *chunk_root;
struct btrfs_root *dev_root;
struct btrfs_root *_csum_root;
struct btrfs_root *quota_root;
struct btrfs_root *_free_space_root;
struct btrfs_root *uuid_root;
struct rb_root global_roots_tree;
struct rb_root fs_root_tree;
/* the log root tree is a directory of all the other log roots */

View file

@ -732,6 +732,23 @@ insert:
return root;
}
static int btrfs_global_roots_compare_keys(struct rb_node *node,
void *data)
{
struct btrfs_key *key = (struct btrfs_key *)data;
struct btrfs_root *root;
root = rb_entry(node, struct btrfs_root, rb_node);
return btrfs_comp_cpu_keys(key, &root->root_key);
}
static int btrfs_global_roots_compare(struct rb_node *node1,
struct rb_node *node2)
{
struct btrfs_root *root = rb_entry(node2, struct btrfs_root, rb_node);
return btrfs_global_roots_compare_keys(node1, &root->root_key);
}
static int btrfs_fs_roots_compare_objectids(struct rb_node *node,
void *data)
{
@ -755,16 +772,54 @@ int btrfs_fs_roots_compare_roots(struct rb_node *node1, struct rb_node *node2)
return btrfs_fs_roots_compare_objectids(node1, (void *)&root->objectid);
}
int btrfs_global_root_insert(struct btrfs_fs_info *fs_info,
struct btrfs_root *root)
{
return rb_insert(&fs_info->global_roots_tree, &root->rb_node,
btrfs_global_roots_compare);
}
struct btrfs_root *btrfs_global_root(struct btrfs_fs_info *fs_info,
struct btrfs_key *key)
{
struct rb_node *node;
/*
* Some callers use the key->offset = (u64)-1 convention for looking up
* roots, so set this to 0 if we ended up here from that.
*/
if (key->offset == (u64)-1)
key->offset = 0;
node = rb_search(&fs_info->global_roots_tree, (void *)key,
btrfs_global_roots_compare_keys, NULL);
if (node)
return rb_entry(node, struct btrfs_root, rb_node);
return NULL;
}
struct btrfs_root *btrfs_csum_root(struct btrfs_fs_info *fs_info,
u64 bytenr)
{
return fs_info->_csum_root;
struct btrfs_key key = {
.objectid = BTRFS_CSUM_TREE_OBJECTID,
.type = BTRFS_ROOT_ITEM_KEY,
.offset = 0,
};
return btrfs_global_root(fs_info, &key);
}
struct btrfs_root *btrfs_extent_root(struct btrfs_fs_info *fs_info,
u64 bytenr)
{
return fs_info->_extent_root;
struct btrfs_key key = {
.objectid = BTRFS_EXTENT_TREE_OBJECTID,
.type = BTRFS_ROOT_ITEM_KEY,
.offset = 0,
};
return btrfs_global_root(fs_info, &key);
}
struct btrfs_root *btrfs_read_fs_root(struct btrfs_fs_info *fs_info,
@ -778,21 +833,22 @@ struct btrfs_root *btrfs_read_fs_root(struct btrfs_fs_info *fs_info,
if (location->objectid == BTRFS_ROOT_TREE_OBJECTID)
return fs_info->tree_root;
if (location->objectid == BTRFS_EXTENT_TREE_OBJECTID)
return fs_info->_extent_root;
return btrfs_global_root(fs_info, location);
if (location->objectid == BTRFS_CHUNK_TREE_OBJECTID)
return fs_info->chunk_root;
if (location->objectid == BTRFS_DEV_TREE_OBJECTID)
return fs_info->dev_root;
if (location->objectid == BTRFS_CSUM_TREE_OBJECTID)
return btrfs_csum_root(fs_info, location->offset);
return btrfs_global_root(fs_info, location);
if (location->objectid == BTRFS_UUID_TREE_OBJECTID)
return fs_info->uuid_root ? fs_info->uuid_root : ERR_PTR(-ENOENT);
if (location->objectid == BTRFS_QUOTA_TREE_OBJECTID)
return fs_info->quota_enabled ? fs_info->quota_root :
ERR_PTR(-ENOENT);
if (location->objectid == BTRFS_FREE_SPACE_TREE_OBJECTID)
return fs_info->_free_space_root ? fs_info->_free_space_root :
ERR_PTR(-ENOENT);
if (location->objectid == BTRFS_FREE_SPACE_TREE_OBJECTID) {
root = btrfs_global_root(fs_info, location);
return root ? root : ERR_PTR(-ENOENT);
}
BUG_ON(location->objectid == BTRFS_TREE_RELOC_OBJECTID);
@ -811,17 +867,25 @@ struct btrfs_root *btrfs_read_fs_root(struct btrfs_fs_info *fs_info,
return root;
}
static void __free_global_root(struct rb_node *node)
{
struct btrfs_root *root;
root = rb_entry(node, struct btrfs_root, rb_node);
kfree(root);
}
FREE_RB_BASED_TREE(global_roots, __free_global_root);
void btrfs_free_fs_info(struct btrfs_fs_info *fs_info)
{
if (fs_info->quota_root)
free(fs_info->quota_root);
free_global_roots_tree(&fs_info->global_roots_tree);
free(fs_info->tree_root);
free(fs_info->_extent_root);
free(fs_info->chunk_root);
free(fs_info->dev_root);
free(fs_info->_csum_root);
free(fs_info->_free_space_root);
free(fs_info->uuid_root);
free(fs_info->super_copy);
free(fs_info->log_root_tree);
@ -837,20 +901,14 @@ struct btrfs_fs_info *btrfs_new_fs_info(int writable, u64 sb_bytenr)
return NULL;
fs_info->tree_root = calloc(1, sizeof(struct btrfs_root));
fs_info->_extent_root = calloc(1, sizeof(struct btrfs_root));
fs_info->chunk_root = calloc(1, sizeof(struct btrfs_root));
fs_info->dev_root = calloc(1, sizeof(struct btrfs_root));
fs_info->_csum_root = calloc(1, sizeof(struct btrfs_root));
fs_info->quota_root = calloc(1, sizeof(struct btrfs_root));
fs_info->_free_space_root = calloc(1, sizeof(struct btrfs_root));
fs_info->uuid_root = calloc(1, sizeof(struct btrfs_root));
fs_info->super_copy = calloc(1, BTRFS_SUPER_INFO_SIZE);
if (!fs_info->tree_root || !fs_info->_extent_root ||
!fs_info->chunk_root || !fs_info->dev_root ||
!fs_info->_csum_root || !fs_info->quota_root ||
!fs_info->_free_space_root || !fs_info->uuid_root ||
!fs_info->super_copy)
if (!fs_info->tree_root || !fs_info->chunk_root || !fs_info->dev_root ||
!fs_info->quota_root || !fs_info->uuid_root || !fs_info->super_copy)
goto free_all;
extent_io_tree_init(&fs_info->extent_cache);
@ -940,15 +998,14 @@ static int find_best_backup_root(struct btrfs_super_block *super)
return best_index;
}
static int setup_root_or_create_block(struct btrfs_fs_info *fs_info,
unsigned flags,
struct btrfs_root *info_root,
u64 objectid, char *str)
static int read_root_or_create_block(struct btrfs_fs_info *fs_info,
struct btrfs_root *root, u64 bytenr,
u64 gen, int level, unsigned flags,
char *str)
{
struct btrfs_root *root = fs_info->tree_root;
int ret;
ret = find_and_setup_root(root, fs_info, objectid, info_root);
ret = read_root_node(fs_info, root, bytenr, gen, level);
if (ret) {
if (!(flags & OPEN_CTREE_PARTIAL)) {
error("could not setup %s tree", str);
@ -959,16 +1016,128 @@ static int setup_root_or_create_block(struct btrfs_fs_info *fs_info,
* Need a blank node here just so we don't screw up in the
* million of places that assume a root has a valid ->node
*/
info_root->node =
btrfs_find_create_tree_block(fs_info, 0);
if (!info_root->node)
root->node = btrfs_find_create_tree_block(fs_info, 0);
if (!root->node)
return -ENOMEM;
clear_extent_buffer_uptodate(info_root->node);
clear_extent_buffer_uptodate(root->node);
}
return 0;
}
static inline bool maybe_load_block_groups(struct btrfs_fs_info *fs_info,
u64 flags)
{
struct btrfs_root *root = btrfs_extent_root(fs_info, 0);
if (flags & OPEN_CTREE_NO_BLOCK_GROUPS)
return false;
if (root && extent_buffer_uptodate(root->node))
return true;
return false;
}
static int load_global_roots_objectid(struct btrfs_fs_info *fs_info,
struct btrfs_path *path, u64 objectid,
unsigned flags, char *str)
{
struct btrfs_root *tree_root = fs_info->tree_root;
struct btrfs_root *root;
int ret;
struct btrfs_key key = {
.objectid = objectid,
.type = BTRFS_ROOT_ITEM_KEY,
.offset = 0,
};
ret = btrfs_search_slot(NULL, tree_root, &key, path, 0, 0);
if (ret < 0) {
error("could not find %s tree", str);
return ret;
}
ret = 0;
while (1) {
if (path->slots[0] >= btrfs_header_nritems(path->nodes[0])) {
ret = btrfs_next_leaf(tree_root, path);
if (ret) {
if (ret > 0)
ret = 0;
break;
}
}
btrfs_item_key_to_cpu(path->nodes[0], &key,
path->slots[0]);
if (key.objectid != objectid)
break;
root = calloc(1, sizeof(*root));
if (!root) {
ret = -ENOMEM;
break;
}
btrfs_setup_root(root, fs_info, objectid);
read_extent_buffer(path->nodes[0], &root->root_item,
btrfs_item_ptr_offset(path->nodes[0],
path->slots[0]),
sizeof(root->root_item));
memcpy(&root->root_key, &key, sizeof(key));
ret = read_root_or_create_block(fs_info, root,
btrfs_root_bytenr(&root->root_item),
btrfs_root_generation(&root->root_item),
btrfs_root_level(&root->root_item),
flags, str);
if (ret) {
free(root);
break;
}
root->track_dirty = 1;
ret = btrfs_global_root_insert(fs_info, root);
if (ret) {
free_extent_buffer(root->node);
free(root);
break;
}
path->slots[0]++;
}
btrfs_release_path(path);
return ret;
}
static int load_global_roots(struct btrfs_fs_info *fs_info, unsigned flags)
{
struct btrfs_path *path;
int ret = 0;
path = btrfs_alloc_path();
if (!path)
return -ENOMEM;
ret = load_global_roots_objectid(fs_info, path,
BTRFS_EXTENT_TREE_OBJECTID, flags,
"extent");
if (ret)
goto out;
ret = load_global_roots_objectid(fs_info, path,
BTRFS_CSUM_TREE_OBJECTID, flags,
"csum");
if (ret)
goto out;
ret = load_global_roots_objectid(fs_info, path,
BTRFS_FREE_SPACE_TREE_OBJECTID, flags,
"free space");
out:
btrfs_free_path(path);
return ret;
}
int btrfs_setup_all_roots(struct btrfs_fs_info *fs_info, u64 root_tree_bytenr,
unsigned flags)
{
@ -1006,11 +1175,9 @@ int btrfs_setup_all_roots(struct btrfs_fs_info *fs_info, u64 root_tree_bytenr,
return -EIO;
}
ret = setup_root_or_create_block(fs_info, flags, fs_info->_extent_root,
BTRFS_EXTENT_TREE_OBJECTID, "extent");
ret = load_global_roots(fs_info, flags);
if (ret)
return ret;
fs_info->_extent_root->track_dirty = 1;
ret = find_and_setup_root(root, fs_info, BTRFS_DEV_TREE_OBJECTID,
fs_info->dev_root);
@ -1020,12 +1187,6 @@ int btrfs_setup_all_roots(struct btrfs_fs_info *fs_info, u64 root_tree_bytenr,
}
fs_info->dev_root->track_dirty = 1;
ret = setup_root_or_create_block(fs_info, flags, fs_info->_csum_root,
BTRFS_CSUM_TREE_OBJECTID, "csum");
if (ret)
return ret;
fs_info->_csum_root->track_dirty = 1;
ret = find_and_setup_root(root, fs_info, BTRFS_UUID_TREE_OBJECTID,
fs_info->uuid_root);
if (ret) {
@ -1044,21 +1205,6 @@ int btrfs_setup_all_roots(struct btrfs_fs_info *fs_info, u64 root_tree_bytenr,
fs_info->quota_enabled = 1;
}
if (btrfs_fs_compat_ro(fs_info, FREE_SPACE_TREE)) {
ret = find_and_setup_root(root, fs_info, BTRFS_FREE_SPACE_TREE_OBJECTID,
fs_info->_free_space_root);
if (ret) {
free(fs_info->_free_space_root);
fs_info->_free_space_root = NULL;
printk("Couldn't read free space tree\n");
return -EIO;
}
fs_info->_free_space_root->track_dirty = 1;
} else {
free(fs_info->_free_space_root);
fs_info->_free_space_root = NULL;
}
ret = find_and_setup_log_root(root, fs_info, sb);
if (ret) {
printk("Couldn't setup log root tree\n");
@ -1068,8 +1214,7 @@ int btrfs_setup_all_roots(struct btrfs_fs_info *fs_info, u64 root_tree_bytenr,
fs_info->generation = generation;
fs_info->last_trans_committed = generation;
if (extent_buffer_uptodate(fs_info->_extent_root->node) &&
!(flags & OPEN_CTREE_NO_BLOCK_GROUPS)) {
if (maybe_load_block_groups(fs_info, flags)) {
ret = btrfs_read_block_groups(fs_info);
/*
* If we don't find any blockgroups (ENOENT) we're either
@ -1093,18 +1238,29 @@ int btrfs_setup_all_roots(struct btrfs_fs_info *fs_info, u64 root_tree_bytenr,
return 0;
}
static void release_global_roots(struct btrfs_fs_info *fs_info)
{
struct btrfs_root *root;
struct rb_node *n;
for (n = rb_first(&fs_info->global_roots_tree); n; n = rb_next(n)) {
root = rb_entry(n, struct btrfs_root, rb_node);
if (root->node)
free_extent_buffer(root->node);
if (root->commit_root)
free_extent_buffer(root->commit_root);
root->node = NULL;
root->commit_root = NULL;
}
}
void btrfs_release_all_roots(struct btrfs_fs_info *fs_info)
{
if (fs_info->_free_space_root)
free_extent_buffer(fs_info->_free_space_root->node);
release_global_roots(fs_info);
if (fs_info->quota_root)
free_extent_buffer(fs_info->quota_root->node);
if (fs_info->_csum_root)
free_extent_buffer(fs_info->_csum_root->node);
if (fs_info->dev_root)
free_extent_buffer(fs_info->dev_root->node);
if (fs_info->_extent_root)
free_extent_buffer(fs_info->_extent_root->node);
if (fs_info->tree_root)
free_extent_buffer(fs_info->tree_root->node);
if (fs_info->log_root_tree)
@ -1827,6 +1983,8 @@ static int write_dev_supers(struct btrfs_fs_info *fs_info,
static void backup_super_roots(struct btrfs_fs_info *info)
{
struct btrfs_root_backup *root_backup;
struct btrfs_root *csum_root = btrfs_csum_root(info, 0);
struct btrfs_root *extent_root = btrfs_extent_root(info, 0);
int next_backup;
int last_backup;
@ -1858,11 +2016,11 @@ static void backup_super_roots(struct btrfs_fs_info *info)
btrfs_set_backup_chunk_root_level(root_backup,
btrfs_header_level(info->chunk_root->node));
btrfs_set_backup_extent_root(root_backup, info->_extent_root->node->start);
btrfs_set_backup_extent_root(root_backup, extent_root->node->start);
btrfs_set_backup_extent_root_gen(root_backup,
btrfs_header_generation(info->_extent_root->node));
btrfs_header_generation(extent_root->node));
btrfs_set_backup_extent_root_level(root_backup,
btrfs_header_level(info->_extent_root->node));
btrfs_header_level(extent_root->node));
/*
* we might commit during log recovery, which happens before we set
* the fs_root. Make sure it is valid before we fill it in.
@ -1882,11 +2040,11 @@ static void backup_super_roots(struct btrfs_fs_info *info)
btrfs_set_backup_dev_root_level(root_backup,
btrfs_header_level(info->dev_root->node));
btrfs_set_backup_csum_root(root_backup, info->_csum_root->node->start);
btrfs_set_backup_csum_root(root_backup, csum_root->node->start);
btrfs_set_backup_csum_root_gen(root_backup,
btrfs_header_generation(info->_csum_root->node));
btrfs_header_generation(csum_root->node));
btrfs_set_backup_csum_root_level(root_backup,
btrfs_header_level(info->_csum_root->node));
btrfs_header_level(csum_root->node));
btrfs_set_backup_total_bytes(root_backup,
btrfs_super_total_bytes(info->super_copy));
@ -1894,7 +2052,7 @@ static void backup_super_roots(struct btrfs_fs_info *info)
btrfs_super_bytes_used(info->super_copy));
btrfs_set_backup_num_devices(root_backup,
btrfs_super_num_devices(info->super_copy));
};
}
int write_all_supers(struct btrfs_fs_info *fs_info)
{

View file

@ -216,4 +216,9 @@ struct btrfs_root *btrfs_create_tree(struct btrfs_trans_handle *trans,
u64 objectid);
struct btrfs_root *btrfs_csum_root(struct btrfs_fs_info *fs_info, u64 bytenr);
struct btrfs_root *btrfs_extent_root(struct btrfs_fs_info *fs_inf, u64 bytenr);
struct btrfs_root *btrfs_block_group_root(struct btrfs_fs_info *fs_info);
struct btrfs_root *btrfs_global_root(struct btrfs_fs_info *fs_info,
struct btrfs_key *key);
int btrfs_global_root_insert(struct btrfs_fs_info *fs_info,
struct btrfs_root *root);
#endif

View file

@ -28,7 +28,13 @@
static struct btrfs_root *btrfs_free_space_root(struct btrfs_fs_info *fs_info,
struct btrfs_block_group *block_group)
{
return fs_info->_free_space_root;
struct btrfs_key key = {
.objectid = BTRFS_FREE_SPACE_TREE_OBJECTID,
.type = BTRFS_ROOT_ITEM_KEY,
.offset = 0,
};
return btrfs_global_root(fs_info, &key);
}
static struct btrfs_free_space_info *
@ -1238,7 +1244,6 @@ int btrfs_clear_free_space_tree(struct btrfs_fs_info *fs_info)
features &= ~(BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE_VALID |
BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE);
btrfs_set_super_compat_ro_flags(fs_info->super_copy, features);
fs_info->_free_space_root = NULL;
ret = clear_free_space_tree(trans, free_space_root);
if (ret)
@ -1258,6 +1263,7 @@ int btrfs_clear_free_space_tree(struct btrfs_fs_info *fs_info)
if (ret)
goto abort;
rb_erase(&free_space_root->rb_node, &fs_info->global_roots_tree);
free_extent_buffer(free_space_root->node);
free_extent_buffer(free_space_root->commit_root);
kfree(free_space_root);
@ -1474,7 +1480,10 @@ int btrfs_create_free_space_tree(struct btrfs_fs_info *fs_info)
ret = PTR_ERR(free_space_root);
goto abort;
}
fs_info->_free_space_root = free_space_root;
ret = btrfs_global_root_insert(fs_info, free_space_root);
if (ret)
goto abort;
add_root_to_dirty_list(free_space_root);
do {

View file

@ -264,20 +264,33 @@ out:
return ret;
}
static int recow_global_roots(struct btrfs_trans_handle *trans)
{
struct btrfs_fs_info *fs_info = trans->fs_info;
struct btrfs_root *root;
struct rb_node *n;
int ret = 0;
for (n = rb_first(&fs_info->global_roots_tree); n; n = rb_next(n)) {
root = rb_entry(n, struct btrfs_root, rb_node);
ret = __recow_root(trans, root);
if (ret)
return ret;
}
return ret;
}
static int recow_roots(struct btrfs_trans_handle *trans,
struct btrfs_root *root)
{
struct btrfs_fs_info *info = root->fs_info;
struct btrfs_root *csum_root = btrfs_csum_root(info, 0);
int ret;
ret = __recow_root(trans, info->fs_root);
if (ret)
return ret;
ret = __recow_root(trans, info->tree_root);
if (ret)
return ret;
ret = __recow_root(trans, info->_extent_root);
if (ret)
return ret;
ret = __recow_root(trans, info->chunk_root);
@ -286,14 +299,9 @@ static int recow_roots(struct btrfs_trans_handle *trans,
ret = __recow_root(trans, info->dev_root);
if (ret)
return ret;
ret = __recow_root(trans, csum_root);
ret = recow_global_roots(trans);
if (ret)
return ret;
if (info->_free_space_root) {
ret = __recow_root(trans, info->_free_space_root);
if (ret)
return ret;
}
return 0;
}