Skip to content

Commit

Permalink
btrfs: Factor out tree roots initialization during mount
Browse files Browse the repository at this point in the history
The code responsible for reading and initializing tree roots is
scattered in open_ctree among 2 labels, emulating a loop. This is rather
confusing to reason about. Instead, factor the code to a new function,
init_tree_roots which implements the same logical flow.

There are a couple of notable differences, namely:

* Instead of using next_backup_root it's using the newly introduced
  read_backup_root.

* If read_backup_root returns an error init_tree_roots propagates the
  error and there is no special handling of that case e.g. the code jumps
  straight to 'fail_tree_roots' label. The old code, however, was
  (erroneously) jumping to 'fail_block_groups' label if next_backup_root
  did fail, this was unnecessary since the tree roots init logic doesn't
  modify the state of block groups.

Signed-off-by: Nikolay Borisov <nborisov@suse.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
  • Loading branch information
lorddoskias authored and kdave committed Nov 18, 2019
1 parent bd2336b commit b8522a1
Showing 1 changed file with 85 additions and 57 deletions.
142 changes: 85 additions & 57 deletions fs/btrfs/disk-io.c
Original file line number Diff line number Diff line change
Expand Up @@ -2610,6 +2610,89 @@ static int btrfs_validate_write_super(struct btrfs_fs_info *fs_info,
return ret;
}

int __cold init_tree_roots(struct btrfs_fs_info *fs_info)
{
struct btrfs_super_block *sb = fs_info->super_copy;
struct btrfs_root *tree_root = fs_info->tree_root;
bool handle_error = false;
int ret = 0;
int i;

for (i = 0; i < BTRFS_NUM_BACKUP_ROOTS; i++) {
u64 generation;
int level;

if (handle_error) {
if (!IS_ERR(tree_root->node))
free_extent_buffer(tree_root->node);
tree_root->node = NULL;

if (!btrfs_test_opt(fs_info, USEBACKUPROOT))
break;

free_root_pointers(fs_info, 0);

/*
* Don't use the log in recovery mode, it won't be
* valid
*/
btrfs_set_super_log_root(sb, 0);

/* We can't trust the free space cache either */
btrfs_set_opt(fs_info->mount_opt, CLEAR_CACHE);

ret = read_backup_root(fs_info, i);
if (ret < 0)
return ret;
}
generation = btrfs_super_generation(sb);
level = btrfs_super_root_level(sb);
tree_root->node = read_tree_block(fs_info, btrfs_super_root(sb),
generation, level, NULL);
if (IS_ERR(tree_root->node) ||
!extent_buffer_uptodate(tree_root->node)) {
handle_error = true;

if (IS_ERR(tree_root->node))
ret = PTR_ERR(tree_root->node);
else if (!extent_buffer_uptodate(tree_root->node))
ret = -EUCLEAN;

btrfs_warn(fs_info, "failed to read tree root");
continue;
}

btrfs_set_root_node(&tree_root->root_item, tree_root->node);
tree_root->commit_root = btrfs_root_node(tree_root);
btrfs_set_root_refs(&tree_root->root_item, 1);

mutex_lock(&tree_root->objectid_mutex);
ret = btrfs_find_highest_objectid(tree_root,
&tree_root->highest_objectid);
if (ret < 0) {
mutex_unlock(&tree_root->objectid_mutex);
handle_error = true;
continue;
}

ASSERT(tree_root->highest_objectid <= BTRFS_LAST_FREE_OBJECTID);
mutex_unlock(&tree_root->objectid_mutex);

ret = btrfs_read_roots(fs_info);
if (ret < 0) {
handle_error = true;
continue;
}

/* All successful */
fs_info->generation = generation;
fs_info->last_trans_committed = generation;
break;
}

return ret;
}

int __cold open_ctree(struct super_block *sb,
struct btrfs_fs_devices *fs_devices,
char *options)
Expand All @@ -2628,8 +2711,6 @@ int __cold open_ctree(struct super_block *sb,
struct btrfs_root *chunk_root;
int ret;
int err = -EINVAL;
int num_backups_tried = 0;
int backup_index = 0;
int clear_free_space_tree = 0;
int level;

Expand Down Expand Up @@ -3051,44 +3132,9 @@ int __cold open_ctree(struct super_block *sb,
goto fail_tree_roots;
}

retry_root_backup:
generation = btrfs_super_generation(disk_super);
level = btrfs_super_root_level(disk_super);

tree_root->node = read_tree_block(fs_info,
btrfs_super_root(disk_super),
generation, level, NULL);
if (IS_ERR(tree_root->node) ||
!extent_buffer_uptodate(tree_root->node)) {
btrfs_warn(fs_info, "failed to read tree root");
if (!IS_ERR(tree_root->node))
free_extent_buffer(tree_root->node);
tree_root->node = NULL;
goto recovery_tree_root;
}

btrfs_set_root_node(&tree_root->root_item, tree_root->node);
tree_root->commit_root = btrfs_root_node(tree_root);
btrfs_set_root_refs(&tree_root->root_item, 1);

mutex_lock(&tree_root->objectid_mutex);
ret = btrfs_find_highest_objectid(tree_root,
&tree_root->highest_objectid);
if (ret) {
mutex_unlock(&tree_root->objectid_mutex);
goto recovery_tree_root;
}

ASSERT(tree_root->highest_objectid <= BTRFS_LAST_FREE_OBJECTID);

mutex_unlock(&tree_root->objectid_mutex);

ret = btrfs_read_roots(fs_info);
ret = init_tree_roots(fs_info);
if (ret)
goto recovery_tree_root;

fs_info->generation = generation;
fs_info->last_trans_committed = generation;
goto fail_tree_roots;

ret = btrfs_verify_dev_extents(fs_info);
if (ret) {
Expand Down Expand Up @@ -3383,24 +3429,6 @@ int __cold open_ctree(struct super_block *sb,
btrfs_free_stripe_hash_table(fs_info);
btrfs_close_devices(fs_info->fs_devices);
return err;

recovery_tree_root:
if (!btrfs_test_opt(fs_info, USEBACKUPROOT))
goto fail_tree_roots;

free_root_pointers(fs_info, false);

/* don't use the log in recovery mode, it won't be valid */
btrfs_set_super_log_root(disk_super, 0);

/* we can't trust the free space cache either */
btrfs_set_opt(fs_info->mount_opt, CLEAR_CACHE);

ret = next_root_backup(fs_info, fs_info->super_copy,
&num_backups_tried, &backup_index);
if (ret == -1)
goto fail_block_groups;
goto retry_root_backup;
}
ALLOW_ERROR_INJECTION(open_ctree, ERRNO);

Expand Down

0 comments on commit b8522a1

Please sign in to comment.