From c73eb6f727414a0a78ee993eccd6c2d2065e9571 Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Sun, 6 Sep 2026 16:33:37 +0930 Subject: [PATCH] btrfs: always use the second newest slot for rescue=usebackuproot [RESCUE FAILURE] There is a bug report that "rescue=usebackuproot" failed to properly mount the fs, but if using btrfs-mod-sb to use the second newest slot, then the fs can even pass the full "btrfs check" and be mounted RW. [CAUSE] During init_tree_roots(), "rescue=usebackuproot" only makes a difference if the tree and chunk roots failed to be load. The loading only requires the root node/leaf to be properly loaded: - tree root - extent tree - block group tree - device tree - data reloc tree - quota tree (optional) - uuid tree - raid stripe tree (for RST feature) And it doesn't really check any child tree blocks, thus for a lot of cases, "rescue=usebackuproot" won't make a difference. [ENHANCEMENT] Instead of trying every slot and settling down on the first passing backup slot, always use the second newest slot. The newest slot should always match the root info in the super block, and if it worked the end user should not even need to bother to use backup root. Older backup slots normally have a very low chance to work. Link: https://github.com/kdave/btrfs-progs/issues/1162#issuecomment-5475477108 Reviewed-by: Boris Burkov Signed-off-by: Qu Wenruo Signed-off-by: David Sterba --- fs/btrfs/disk-io.c | 102 +++++++++++++++++++-------------------------- 1 file changed, 42 insertions(+), 60 deletions(-) diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index 90cf0647c47e..d3d3d67ced3e 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -2711,73 +2711,55 @@ static int __cold init_tree_roots(struct btrfs_fs_info *fs_info) int backup_index = find_newest_super_backup(fs_info); struct btrfs_super_block *sb = fs_info->super_copy; struct btrfs_root *tree_root = fs_info->tree_root; - bool handle_error = false; + bool use_backup = btrfs_test_opt(fs_info, USEBACKUPROOT); int ret = 0; - int i; - for (i = 0; i < BTRFS_NUM_BACKUP_ROOTS; i++) { - 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); - - btrfs_warn(fs_info, "try to load backup roots slot %d", i); - ret = read_backup_root(fs_info, i); - backup_index = ret; - if (ret < 0) - return ret; + if (use_backup) { + ret = read_backup_root(fs_info, 1); + if (ret < 0) { + btrfs_err(fs_info, + "failed to load backup roots at slot %d", 1); + return ret; } - - ret = load_important_roots(fs_info); - if (ret) { - handle_error = true; - continue; - } - /* - * No need to hold btrfs_root::objectid_mutex since the fs - * hasn't been fully initialised and we are the only user + * Don't use the log in recovery mode, it won't be + * valid */ - ret = btrfs_init_root_free_objectid(tree_root); - if (ret < 0) { - handle_error = true; - continue; - } - - ASSERT(tree_root->free_objectid <= BTRFS_LAST_FREE_OBJECTID); - - ret = btrfs_read_roots(fs_info); - if (ret < 0) { - handle_error = true; - continue; - } - - /* All successful */ - fs_info->generation = btrfs_header_generation(tree_root->node); - btrfs_set_last_trans_committed(fs_info, fs_info->generation); - fs_info->last_reloc_trans = 0; - - /* Always begin writing backup roots after the one being used */ - if (backup_index < 0) { - fs_info->backup_root_index = 0; - } else { - fs_info->backup_root_index = backup_index + 1; - fs_info->backup_root_index %= BTRFS_NUM_BACKUP_ROOTS; - } - break; + btrfs_set_super_log_root(sb, 0); + backup_index = ret; + btrfs_warn(fs_info, "loaded backup roots at slot %d", 1); } + ret = load_important_roots(fs_info); + if (ret) + return ret; + + /* + * No need to hold btrfs_root::objectid_mutex since the fs + * hasn't been fully initialised and we are the only user + */ + ret = btrfs_init_root_free_objectid(tree_root); + if (ret < 0) + return ret; + + ASSERT(tree_root->free_objectid <= BTRFS_LAST_FREE_OBJECTID); + + ret = btrfs_read_roots(fs_info); + if (ret < 0) + return ret; + + /* All successful */ + fs_info->generation = btrfs_header_generation(tree_root->node); + btrfs_set_last_trans_committed(fs_info, fs_info->generation); + fs_info->last_reloc_trans = 0; + + /* Always begin writing backup roots after the one being used */ + if (backup_index < 0) { + fs_info->backup_root_index = 0; + } else { + fs_info->backup_root_index = backup_index + 1; + fs_info->backup_root_index %= BTRFS_NUM_BACKUP_ROOTS; + } return ret; }