Current backup root handling has extra check on super generation: static int find_best_backup_root(struct btrfs_super_block *super) { u64 orig_gen = btrfs_super_generation(super); ... if (btrfs_backup_tree_root_gen(backup) != orig_gen && btrfs_backup_tree_root_gen(backup) > gen) { best_index = i; gen = btrfs_backup_tree_root_gen(backup);
This check is to ensure we don't get backup root with current generation, but it can still return backup root newer than current root. So for the following super: generation: 10 backup[0] generation: 8 backup[1] generation: 9 backup[2] generation: 10 backup[3] generation: 11 If we're calling find_best_backup_root() then we can pick up slot 3 which is newer than current generation. This patch introduce a new parameter for find_best_backup_root() to specify the max generation. So with above superblock, calling find_best_backup_root(sb, sb_gen - 1) will ensure we get slot 1, other than slot 3. This also affects how we update backup roots. Furthermore, due to the change in the return value, find_best_backup_root() can now return -1 to indicates error (no valid backup found), so change callers to co-operate. Signed-off-by: Qu Wenruo <w...@suse.com> --- disk-io.c | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/disk-io.c b/disk-io.c index be44eead5cef..36db1be264cd 100644 --- a/disk-io.c +++ b/disk-io.c @@ -845,17 +845,22 @@ int btrfs_check_fs_compatibility(struct btrfs_super_block *sb, return 0; } -static int find_best_backup_root(struct btrfs_super_block *super) +/* + * Find the newest backup slot whose generation <= @max_gen + * + * Can return <0 for error, indicating no valid backup slot for @max_gen. + */ +static int find_best_backup_root(struct btrfs_super_block *super, + u64 max_gen) { struct btrfs_root_backup *backup; - u64 orig_gen = btrfs_super_generation(super); u64 gen = 0; - int best_index = 0; + int best_index = -1; int i; for (i = 0; i < BTRFS_NUM_BACKUP_ROOTS; i++) { backup = super->super_roots + i; - if (btrfs_backup_tree_root_gen(backup) != orig_gen && + if (btrfs_backup_tree_root_gen(backup) <= max_gen && btrfs_backup_tree_root_gen(backup) > gen) { best_index = i; gen = btrfs_backup_tree_root_gen(backup); @@ -908,9 +913,10 @@ int btrfs_setup_all_roots(struct btrfs_fs_info *fs_info, u64 root_tree_bytenr, root_tree_bytenr = btrfs_super_root(sb); } else if (flags & OPEN_CTREE_BACKUP_ROOT) { struct btrfs_root_backup *backup; - int index = find_best_backup_root(sb); - if (index >= BTRFS_NUM_BACKUP_ROOTS) { - fprintf(stderr, "Invalid backup root number\n"); + int index = find_best_backup_root(sb, + btrfs_super_generation(sb) - 1); + if (index < 0) { + error("can't find any valid backup root"); return -EIO; } backup = fs_info->super_copy->super_roots + index; @@ -1707,10 +1713,22 @@ 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; + u64 current_gen = btrfs_super_generation(info->super_copy); int next_backup; int last_backup; - last_backup = find_best_backup_root(info->super_copy); + last_backup = find_best_backup_root(info->super_copy, current_gen - 1); + /* No older backups, retry current gen */ + if (last_backup < 0) { + last_backup = find_best_backup_root(info->super_copy, + current_gen); + /* + * Still failed, means no valid backup root at all, restart + * from slot 0. + */ + if (last_backup < 0) + last_backup = 0; + } next_backup = (last_backup + 1) % BTRFS_NUM_BACKUP_ROOTS; /* just overwrite the last backup if we're at the same generation */ -- 2.23.0