summaryrefslogtreecommitdiff
path: root/fs/btrfs
diff options
context:
space:
mode:
authorQu Wenruo <wqu@suse.com>2026-09-06 16:33:38 +0930
committerDavid Sterba <dsterba@suse.com>2026-10-12 19:16:24 +0200
commit9421d1897ba2185cd2c3cf7bf517fd99d4f58516 (patch)
tree9688029ae4a5abe8deb1019a4bdc35419e1b7518 /fs/btrfs
parentc73eb6f727414a0a78ee993eccd6c2d2065e9571 (diff)
btrfs: introduce more accurate usebackuproot options
Previously we had no way to tell btrfs to use which backup slot, but always use the newest backup, and retry until all backup roots are exhausted. This doesn't give any control to the end user, even if the end user wants to use a specific backup slot. To address this problem, allow more fine-control on usebackuproot rescue option by introducing the following 4 rescue mount options: - rescue=usebackuproot_0 - rescue=usebackuproot_1 - rescue=usebackuproot_2 - rescue=usebackuproot_3 The number 0 means the newest slot, which should be the same as the one recorded in the super block and no need to load any backup root. 1/2/3 means the second/third/fourth(oldest) backup slot to use. The existing "rescue=usebackuproot" is just an alias of "rescue=usebackuproot_1". This will allow users to choose which backup root to use, thus can have a better control than blindly choose any slot that can pass the very basic root node checks. Reviewed-by: Boris Burkov <boris@bur.io> Signed-off-by: Qu Wenruo <wqu@suse.com> Signed-off-by: David Sterba <dsterba@suse.com>
Diffstat (limited to 'fs/btrfs')
-rw-r--r--fs/btrfs/disk-io.c12
-rw-r--r--fs/btrfs/fs.h1
-rw-r--r--fs/btrfs/super.c28
3 files changed, 36 insertions, 5 deletions
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index d3d3d67ced3e..66b054eb98c6 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -2715,10 +2715,14 @@ static int __cold init_tree_roots(struct btrfs_fs_info *fs_info)
int ret = 0;
if (use_backup) {
- ret = read_backup_root(fs_info, 1);
+ ASSERT(fs_info->use_backup_slot >= 0 &&
+ fs_info->use_backup_slot < BTRFS_NUM_BACKUP_ROOTS);
+
+ ret = read_backup_root(fs_info, fs_info->use_backup_slot);
if (ret < 0) {
btrfs_err(fs_info,
- "failed to load backup roots at slot %d", 1);
+ "failed to load backup roots at slot %d",
+ fs_info->use_backup_slot);
return ret;
}
/*
@@ -2727,7 +2731,8 @@ static int __cold init_tree_roots(struct btrfs_fs_info *fs_info)
*/
btrfs_set_super_log_root(sb, 0);
backup_index = ret;
- btrfs_warn(fs_info, "loaded backup roots at slot %d", 1);
+ btrfs_warn(fs_info, "loaded backup roots at slot %d",
+ fs_info->use_backup_slot);
}
ret = load_important_roots(fs_info);
@@ -2898,6 +2903,7 @@ void btrfs_init_fs_info(struct btrfs_fs_info *fs_info)
init_waitqueue_head(&fs_info->async_submit_wait);
init_waitqueue_head(&fs_info->delayed_iputs_wait);
+ fs_info->use_backup_slot = -1;
/* Usable values until the real ones are cached from the superblock */
fs_info->nodesize = 4096;
fs_info->sectorsize = 4096;
diff --git a/fs/btrfs/fs.h b/fs/btrfs/fs.h
index 3eba8438593c..649623c603e8 100644
--- a/fs/btrfs/fs.h
+++ b/fs/btrfs/fs.h
@@ -857,6 +857,7 @@ struct btrfs_fs_info {
/* Next backup root to be overwritten */
int backup_root_index;
+ int use_backup_slot;
/* Device replace state */
struct btrfs_dev_replace dev_replace;
diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
index ddb620ac241b..8d01409ed382 100644
--- a/fs/btrfs/super.c
+++ b/fs/btrfs/super.c
@@ -87,6 +87,7 @@ struct btrfs_fs_context {
unsigned long long mount_opt;
unsigned long compress_type:4;
int compress_level;
+ int use_backup_slot;
refcount_t refs;
};
@@ -175,6 +176,10 @@ static const struct constant_table btrfs_parameter_space_cache[] = {
enum {
Opt_rescue_usebackuproot,
+ Opt_rescue_usebackup_slot_0,
+ Opt_rescue_usebackup_slot_1,
+ Opt_rescue_usebackup_slot_2,
+ Opt_rescue_usebackup_slot_3,
Opt_rescue_nologreplay,
Opt_rescue_ignorebadroots,
Opt_rescue_ignoredatacsums,
@@ -185,6 +190,10 @@ enum {
static const struct constant_table btrfs_parameter_rescue[] = {
{ "usebackuproot", Opt_rescue_usebackuproot },
+ { "usebackuproot_0", Opt_rescue_usebackup_slot_0},
+ { "usebackuproot_1", Opt_rescue_usebackup_slot_1},
+ { "usebackuproot_2", Opt_rescue_usebackup_slot_2},
+ { "usebackuproot_3", Opt_rescue_usebackup_slot_3},
{ "nologreplay", Opt_rescue_nologreplay },
{ "ignorebadroots", Opt_rescue_ignorebadroots },
{ "ibadroots", Opt_rescue_ignorebadroots },
@@ -588,6 +597,14 @@ static int btrfs_parse_param(struct fs_context *fc, struct fs_parameter *param)
switch (result.uint_32) {
case Opt_rescue_usebackuproot:
btrfs_set_opt(ctx->mount_opt, USEBACKUPROOT);
+ ctx->use_backup_slot = 1;
+ break;
+ case Opt_rescue_usebackup_slot_0:
+ case Opt_rescue_usebackup_slot_1:
+ case Opt_rescue_usebackup_slot_2:
+ case Opt_rescue_usebackup_slot_3:
+ btrfs_set_opt(ctx->mount_opt, USEBACKUPROOT);
+ ctx->use_backup_slot = result.uint_32 - Opt_rescue_usebackup_slot_0;
break;
case Opt_rescue_nologreplay:
btrfs_set_opt(ctx->mount_opt, NOLOGREPLAY);
@@ -611,6 +628,7 @@ static int btrfs_parse_param(struct fs_context *fc, struct fs_parameter *param)
btrfs_set_opt(ctx->mount_opt, IGNOREBADROOTS);
btrfs_set_opt(ctx->mount_opt, NOLOGREPLAY);
btrfs_set_opt(ctx->mount_opt, USEBACKUPROOT);
+ ctx->use_backup_slot = 1;
break;
default:
btrfs_info(NULL, "unrecognized rescue option '%s'",
@@ -1089,8 +1107,11 @@ static int btrfs_show_options(struct seq_file *seq, struct dentry *dentry)
seq_puts(seq, ",notreelog");
if (btrfs_test_opt(info, NOLOGREPLAY))
print_rescue_option(seq, "nologreplay", &printed);
- if (btrfs_test_opt(info, USEBACKUPROOT))
- print_rescue_option(seq, "usebackuproot", &printed);
+ if (btrfs_test_opt(info, USEBACKUPROOT)) {
+ seq_printf(seq, "%susebackuproot_%d", printed ? ":" : ",rescue=",
+ info->use_backup_slot);
+ printed = true;
+ }
if (btrfs_test_opt(info, IGNOREBADROOTS))
print_rescue_option(seq, "ignorebadroots", &printed);
if (btrfs_test_opt(info, IGNOREDATACSUMS))
@@ -1406,6 +1427,7 @@ static void btrfs_ctx_to_info(struct btrfs_fs_info *fs_info, struct btrfs_fs_con
fs_info->mount_opt = ctx->mount_opt;
fs_info->compress_type = ctx->compress_type;
fs_info->compress_level = ctx->compress_level;
+ fs_info->use_backup_slot = ctx->use_backup_slot;
}
static void btrfs_info_to_ctx(struct btrfs_fs_info *fs_info, struct btrfs_fs_context *ctx)
@@ -1417,6 +1439,7 @@ static void btrfs_info_to_ctx(struct btrfs_fs_info *fs_info, struct btrfs_fs_con
ctx->mount_opt = fs_info->mount_opt;
ctx->compress_type = fs_info->compress_type;
ctx->compress_level = fs_info->compress_level;
+ ctx->use_backup_slot = fs_info->use_backup_slot;
}
#define btrfs_info_if_set(fs_info, old_ctx, opt, fmt, args...) \
@@ -2209,6 +2232,7 @@ static int btrfs_init_fs_context(struct fs_context *fc)
if (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE) {
btrfs_info_to_ctx(btrfs_sb(fc->root->d_sb), ctx);
} else {
+ ctx->use_backup_slot = -1;
ctx->thread_pool_size =
min_t(unsigned long, num_online_cpus() + 2, 8);
ctx->max_inline = BTRFS_DEFAULT_MAX_INLINE;