Message ID | 20211111024138.41687-1-wqu@suse.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | btrfs-progs: rescue: introduce clear-uuid-tree | expand |
On 2021/11/11 10:41, Qu Wenruo wrote: > [BUG] > There is a bug report that a corrupted key type (expected > UUID_KEY_SUBVOL, has EXTENT_ITEM) causing newer kernel to reject a > mount. > > Although the root cause is not determined yet, with roll out of v5.11 > kernel to various distros, such problem should be prevented by > tree-checker, no matter if it's hardware problem or not. > > And older kernel with "-o uuid_rescan" mount option won't help, as > uuid_rescan will only delete items with > UUID_KEY_SUBVOL/UUID_KEY_RECEIVED_SUBVOL key types, not deleting such > corrupted key. > > [FIX] > To fix such problem we have to rely on offline tool, thus there we > introduce a new rescue tool, clear-uuid-tree, to empty and then remove > uuid tree. > > Kernel will re-generate the correct uuid tree at next mount. > > Reported-by: S. <sb56637@gmail.com> > Signed-off-by: Qu Wenruo <wqu@suse.com> > --- > cmds/rescue.c | 104 ++++++++++++++++++++++++++++++++++++++++++++++++++ Oh no, forgot doc again.... > 1 file changed, 104 insertions(+) > > diff --git a/cmds/rescue.c b/cmds/rescue.c > index a98b255ad328..8b5b619da4f6 100644 > --- a/cmds/rescue.c > +++ b/cmds/rescue.c > @@ -296,6 +296,109 @@ static int cmd_rescue_create_control_device(const struct cmd_struct *cmd, > } > static DEFINE_SIMPLE_COMMAND(rescue_create_control_device, "create-control-device"); > > +static int clear_uuid_tree(struct btrfs_fs_info *fs_info) > +{ > + struct btrfs_root *uuid_root = fs_info->uuid_root; > + struct btrfs_trans_handle *trans; > + struct btrfs_path path = {}; > + struct btrfs_key key = {}; > + int ret; > + > + if (!uuid_root) > + return 0; > + > + fs_info->uuid_root = NULL; > + trans = btrfs_start_transaction(fs_info->tree_root, 0); > + if (IS_ERR(trans)) > + return PTR_ERR(trans); > + > + while (1) { > + int nr; > + > + ret = btrfs_search_slot(trans, uuid_root, &key, &path, -1, 1); > + if (ret < 0) > + goto out; > + ASSERT(ret > 0); > + ASSERT(path.slots[0] == 0); > + > + nr = btrfs_header_nritems(path.nodes[0]); > + if (nr == 0) { > + btrfs_release_path(&path); > + break; > + } > + > + ret = btrfs_del_items(trans, uuid_root, &path, 0, nr); > + btrfs_release_path(&path); > + if (ret < 0) > + goto out; > + } > + ret = btrfs_del_root(trans, fs_info->tree_root, &uuid_root->root_key); > + if (ret < 0) > + goto out; > + list_del(&uuid_root->dirty_list); > + ret = clean_tree_block(uuid_root->node); > + if (ret < 0) > + goto out; > + ret = btrfs_free_tree_block(trans, uuid_root, uuid_root->node, 0, 1); > + if (ret < 0) > + goto out; > + free_extent_buffer(uuid_root->node); > + free_extent_buffer(uuid_root->commit_root); > + kfree(uuid_root); > +out: > + if (ret < 0) > + btrfs_abort_transaction(trans, ret); > + else > + ret = btrfs_commit_transaction(trans, fs_info->tree_root); > + return ret; > +} > + > +static const char * const cmd_rescue_clear_uuid_tree_usage[] = { > + "btrfs rescue clear-uuid-tree", > + "Delete uuid tree so that kernel can rebuild it at mount time", > + NULL, > +}; > + > +static int cmd_rescue_clear_uuid_tree(const struct cmd_struct *cmd, > + int argc, char **argv) > +{ > + struct btrfs_fs_info *fs_info; > + struct open_ctree_flags ocf = {}; > + char *devname; > + int ret; > + > + clean_args_no_options(cmd, argc, argv); > + if (check_argc_exact(argc, 2)) > + return -EINVAL; > + > + devname = argv[optind]; > + ret = check_mounted(devname); > + if (ret < 0) { > + errno = -ret; > + error("could not check mount status: %m"); > + goto out; > + } else if (ret) { > + error("%s is currently mounted", devname); > + ret = -EBUSY; > + goto out; > + } > + ocf.filename = devname; > + ocf.flags = OPEN_CTREE_WRITES | OPEN_CTREE_PARTIAL; > + fs_info = open_ctree_fs_info(&ocf); > + if (!fs_info) { > + error("could not open btrfs"); > + ret = -EIO; > + goto out; > + } > + > + ret = clear_uuid_tree(fs_info); > + close_ctree(fs_info->tree_root); > +out: > + return !!ret; > +} > + > +static DEFINE_SIMPLE_COMMAND(rescue_clear_uuid_tree, "clear-uuid-tree"); > + > static const char rescue_cmd_group_info[] = > "toolbox for specific rescue operations"; > > @@ -306,6 +409,7 @@ static const struct cmd_group rescue_cmd_group = { > &cmd_struct_rescue_zero_log, > &cmd_struct_rescue_fix_device_size, > &cmd_struct_rescue_create_control_device, > + &cmd_struct_rescue_clear_uuid_tree, > NULL > } > }; >
On 11.11.21 г. 4:41, Qu Wenruo wrote: > [BUG] > There is a bug report that a corrupted key type (expected > UUID_KEY_SUBVOL, has EXTENT_ITEM) causing newer kernel to reject a > mount. > > Although the root cause is not determined yet, with roll out of v5.11 > kernel to various distros, such problem should be prevented by > tree-checker, no matter if it's hardware problem or not. > > And older kernel with "-o uuid_rescan" mount option won't help, as > uuid_rescan will only delete items with > UUID_KEY_SUBVOL/UUID_KEY_RECEIVED_SUBVOL key types, not deleting such > corrupted key. > > [FIX] > To fix such problem we have to rely on offline tool, thus there we > introduce a new rescue tool, clear-uuid-tree, to empty and then remove > uuid tree. > > Kernel will re-generate the correct uuid tree at next mount. SHouldn't this be made part of btrfs repair, why do we need specific rescue subcom ? > > Reported-by: S. <sb56637@gmail.com> > Signed-off-by: Qu Wenruo <wqu@suse.com> > --- > cmds/rescue.c | 104 ++++++++++++++++++++++++++++++++++++++++++++++++++ > 1 file changed, 104 insertions(+) > > diff --git a/cmds/rescue.c b/cmds/rescue.c > index a98b255ad328..8b5b619da4f6 100644 > --- a/cmds/rescue.c > +++ b/cmds/rescue.c > @@ -296,6 +296,109 @@ static int cmd_rescue_create_control_device(const struct cmd_struct *cmd, > } > static DEFINE_SIMPLE_COMMAND(rescue_create_control_device, "create-control-device"); > > +static int clear_uuid_tree(struct btrfs_fs_info *fs_info) > +{ > + struct btrfs_root *uuid_root = fs_info->uuid_root; > + struct btrfs_trans_handle *trans; > + struct btrfs_path path = {}; > + struct btrfs_key key = {}; > + int ret; > + > + if (!uuid_root) > + return 0; > + > + fs_info->uuid_root = NULL; > + trans = btrfs_start_transaction(fs_info->tree_root, 0); > + if (IS_ERR(trans)) > + return PTR_ERR(trans); > + > + while (1) { > + int nr; > + > + ret = btrfs_search_slot(trans, uuid_root, &key, &path, -1, 1); > + if (ret < 0) > + goto out; > + ASSERT(ret > 0); > + ASSERT(path.slots[0] == 0); > + > + nr = btrfs_header_nritems(path.nodes[0]); > + if (nr == 0) { > + btrfs_release_path(&path); > + break; > + } > + > + ret = btrfs_del_items(trans, uuid_root, &path, 0, nr); > + btrfs_release_path(&path); > + if (ret < 0) > + goto out; > + } > + ret = btrfs_del_root(trans, fs_info->tree_root, &uuid_root->root_key); > + if (ret < 0) > + goto out; > + list_del(&uuid_root->dirty_list); > + ret = clean_tree_block(uuid_root->node); > + if (ret < 0) > + goto out; > + ret = btrfs_free_tree_block(trans, uuid_root, uuid_root->node, 0, 1); > + if (ret < 0) > + goto out; > + free_extent_buffer(uuid_root->node); > + free_extent_buffer(uuid_root->commit_root); > + kfree(uuid_root); > +out: > + if (ret < 0) > + btrfs_abort_transaction(trans, ret); > + else > + ret = btrfs_commit_transaction(trans, fs_info->tree_root); > + return ret; > +} > + > +static const char * const cmd_rescue_clear_uuid_tree_usage[] = { > + "btrfs rescue clear-uuid-tree", > + "Delete uuid tree so that kernel can rebuild it at mount time", > + NULL, > +}; > + > +static int cmd_rescue_clear_uuid_tree(const struct cmd_struct *cmd, > + int argc, char **argv) > +{ > + struct btrfs_fs_info *fs_info; > + struct open_ctree_flags ocf = {}; > + char *devname; > + int ret; > + > + clean_args_no_options(cmd, argc, argv); > + if (check_argc_exact(argc, 2)) > + return -EINVAL; > + > + devname = argv[optind]; > + ret = check_mounted(devname); > + if (ret < 0) { > + errno = -ret; > + error("could not check mount status: %m"); > + goto out; > + } else if (ret) { > + error("%s is currently mounted", devname); > + ret = -EBUSY; > + goto out; > + } > + ocf.filename = devname; > + ocf.flags = OPEN_CTREE_WRITES | OPEN_CTREE_PARTIAL; > + fs_info = open_ctree_fs_info(&ocf); > + if (!fs_info) { > + error("could not open btrfs"); > + ret = -EIO; > + goto out; > + } > + > + ret = clear_uuid_tree(fs_info); > + close_ctree(fs_info->tree_root); > +out: > + return !!ret; > +} > + > +static DEFINE_SIMPLE_COMMAND(rescue_clear_uuid_tree, "clear-uuid-tree"); > + > static const char rescue_cmd_group_info[] = > "toolbox for specific rescue operations"; > > @@ -306,6 +409,7 @@ static const struct cmd_group rescue_cmd_group = { > &cmd_struct_rescue_zero_log, > &cmd_struct_rescue_fix_device_size, > &cmd_struct_rescue_create_control_device, > + &cmd_struct_rescue_clear_uuid_tree, > NULL > } > }; >
On 2021/11/11 14:37, Nikolay Borisov wrote: > > > On 11.11.21 г. 4:41, Qu Wenruo wrote: >> [BUG] >> There is a bug report that a corrupted key type (expected >> UUID_KEY_SUBVOL, has EXTENT_ITEM) causing newer kernel to reject a >> mount. >> >> Although the root cause is not determined yet, with roll out of v5.11 >> kernel to various distros, such problem should be prevented by >> tree-checker, no matter if it's hardware problem or not. >> >> And older kernel with "-o uuid_rescan" mount option won't help, as >> uuid_rescan will only delete items with >> UUID_KEY_SUBVOL/UUID_KEY_RECEIVED_SUBVOL key types, not deleting such >> corrupted key. >> >> [FIX] >> To fix such problem we have to rely on offline tool, thus there we >> introduce a new rescue tool, clear-uuid-tree, to empty and then remove >> uuid tree. >> >> Kernel will re-generate the correct uuid tree at next mount. > > SHouldn't this be made part of btrfs repair, why do we need specific > rescue subcom ? Because btrfs check doesn't yet check uuid tree at all, and the problem we hit here should be pretty rare, thus I prefer to just remove the uuid tree and let newer kernel to regenerate it. Thanks, Qu > >> >> Reported-by: S. <sb56637@gmail.com> >> Signed-off-by: Qu Wenruo <wqu@suse.com> >> --- >> cmds/rescue.c | 104 ++++++++++++++++++++++++++++++++++++++++++++++++++ >> 1 file changed, 104 insertions(+) >> >> diff --git a/cmds/rescue.c b/cmds/rescue.c >> index a98b255ad328..8b5b619da4f6 100644 >> --- a/cmds/rescue.c >> +++ b/cmds/rescue.c >> @@ -296,6 +296,109 @@ static int cmd_rescue_create_control_device(const struct cmd_struct *cmd, >> } >> static DEFINE_SIMPLE_COMMAND(rescue_create_control_device, "create-control-device"); >> >> +static int clear_uuid_tree(struct btrfs_fs_info *fs_info) >> +{ >> + struct btrfs_root *uuid_root = fs_info->uuid_root; >> + struct btrfs_trans_handle *trans; >> + struct btrfs_path path = {}; >> + struct btrfs_key key = {}; >> + int ret; >> + >> + if (!uuid_root) >> + return 0; >> + >> + fs_info->uuid_root = NULL; >> + trans = btrfs_start_transaction(fs_info->tree_root, 0); >> + if (IS_ERR(trans)) >> + return PTR_ERR(trans); >> + >> + while (1) { >> + int nr; >> + >> + ret = btrfs_search_slot(trans, uuid_root, &key, &path, -1, 1); >> + if (ret < 0) >> + goto out; >> + ASSERT(ret > 0); >> + ASSERT(path.slots[0] == 0); >> + >> + nr = btrfs_header_nritems(path.nodes[0]); >> + if (nr == 0) { >> + btrfs_release_path(&path); >> + break; >> + } >> + >> + ret = btrfs_del_items(trans, uuid_root, &path, 0, nr); >> + btrfs_release_path(&path); >> + if (ret < 0) >> + goto out; >> + } >> + ret = btrfs_del_root(trans, fs_info->tree_root, &uuid_root->root_key); >> + if (ret < 0) >> + goto out; >> + list_del(&uuid_root->dirty_list); >> + ret = clean_tree_block(uuid_root->node); >> + if (ret < 0) >> + goto out; >> + ret = btrfs_free_tree_block(trans, uuid_root, uuid_root->node, 0, 1); >> + if (ret < 0) >> + goto out; >> + free_extent_buffer(uuid_root->node); >> + free_extent_buffer(uuid_root->commit_root); >> + kfree(uuid_root); >> +out: >> + if (ret < 0) >> + btrfs_abort_transaction(trans, ret); >> + else >> + ret = btrfs_commit_transaction(trans, fs_info->tree_root); >> + return ret; >> +} >> + >> +static const char * const cmd_rescue_clear_uuid_tree_usage[] = { >> + "btrfs rescue clear-uuid-tree", >> + "Delete uuid tree so that kernel can rebuild it at mount time", >> + NULL, >> +}; >> + >> +static int cmd_rescue_clear_uuid_tree(const struct cmd_struct *cmd, >> + int argc, char **argv) >> +{ >> + struct btrfs_fs_info *fs_info; >> + struct open_ctree_flags ocf = {}; >> + char *devname; >> + int ret; >> + >> + clean_args_no_options(cmd, argc, argv); >> + if (check_argc_exact(argc, 2)) >> + return -EINVAL; >> + >> + devname = argv[optind]; >> + ret = check_mounted(devname); >> + if (ret < 0) { >> + errno = -ret; >> + error("could not check mount status: %m"); >> + goto out; >> + } else if (ret) { >> + error("%s is currently mounted", devname); >> + ret = -EBUSY; >> + goto out; >> + } >> + ocf.filename = devname; >> + ocf.flags = OPEN_CTREE_WRITES | OPEN_CTREE_PARTIAL; >> + fs_info = open_ctree_fs_info(&ocf); >> + if (!fs_info) { >> + error("could not open btrfs"); >> + ret = -EIO; >> + goto out; >> + } >> + >> + ret = clear_uuid_tree(fs_info); >> + close_ctree(fs_info->tree_root); >> +out: >> + return !!ret; >> +} >> + >> +static DEFINE_SIMPLE_COMMAND(rescue_clear_uuid_tree, "clear-uuid-tree"); >> + >> static const char rescue_cmd_group_info[] = >> "toolbox for specific rescue operations"; >> >> @@ -306,6 +409,7 @@ static const struct cmd_group rescue_cmd_group = { >> &cmd_struct_rescue_zero_log, >> &cmd_struct_rescue_fix_device_size, >> &cmd_struct_rescue_create_control_device, >> + &cmd_struct_rescue_clear_uuid_tree, >> NULL >> } >> }; >>
On Thu, Nov 11, 2021 at 08:37:24AM +0200, Nikolay Borisov wrote: > On 11.11.21 г. 4:41, Qu Wenruo wrote: > > [BUG] > > There is a bug report that a corrupted key type (expected > > UUID_KEY_SUBVOL, has EXTENT_ITEM) causing newer kernel to reject a > > mount. > > > > Although the root cause is not determined yet, with roll out of v5.11 > > kernel to various distros, such problem should be prevented by > > tree-checker, no matter if it's hardware problem or not. > > > > And older kernel with "-o uuid_rescan" mount option won't help, as > > uuid_rescan will only delete items with > > UUID_KEY_SUBVOL/UUID_KEY_RECEIVED_SUBVOL key types, not deleting such > > corrupted key. > > > > [FIX] > > To fix such problem we have to rely on offline tool, thus there we > > introduce a new rescue tool, clear-uuid-tree, to empty and then remove > > uuid tree. > > > > Kernel will re-generate the correct uuid tree at next mount. > > SHouldn't this be made part of btrfs repair, why do we need specific > rescue subcom ? This is a one-shot fix for that we have the rescue subcommand, check could do a normal pass and verify the tree but that's a bit different what this patch does and requiring --repair goes opposite to what we recommend to do.
diff --git a/cmds/rescue.c b/cmds/rescue.c index a98b255ad328..8b5b619da4f6 100644 --- a/cmds/rescue.c +++ b/cmds/rescue.c @@ -296,6 +296,109 @@ static int cmd_rescue_create_control_device(const struct cmd_struct *cmd, } static DEFINE_SIMPLE_COMMAND(rescue_create_control_device, "create-control-device"); +static int clear_uuid_tree(struct btrfs_fs_info *fs_info) +{ + struct btrfs_root *uuid_root = fs_info->uuid_root; + struct btrfs_trans_handle *trans; + struct btrfs_path path = {}; + struct btrfs_key key = {}; + int ret; + + if (!uuid_root) + return 0; + + fs_info->uuid_root = NULL; + trans = btrfs_start_transaction(fs_info->tree_root, 0); + if (IS_ERR(trans)) + return PTR_ERR(trans); + + while (1) { + int nr; + + ret = btrfs_search_slot(trans, uuid_root, &key, &path, -1, 1); + if (ret < 0) + goto out; + ASSERT(ret > 0); + ASSERT(path.slots[0] == 0); + + nr = btrfs_header_nritems(path.nodes[0]); + if (nr == 0) { + btrfs_release_path(&path); + break; + } + + ret = btrfs_del_items(trans, uuid_root, &path, 0, nr); + btrfs_release_path(&path); + if (ret < 0) + goto out; + } + ret = btrfs_del_root(trans, fs_info->tree_root, &uuid_root->root_key); + if (ret < 0) + goto out; + list_del(&uuid_root->dirty_list); + ret = clean_tree_block(uuid_root->node); + if (ret < 0) + goto out; + ret = btrfs_free_tree_block(trans, uuid_root, uuid_root->node, 0, 1); + if (ret < 0) + goto out; + free_extent_buffer(uuid_root->node); + free_extent_buffer(uuid_root->commit_root); + kfree(uuid_root); +out: + if (ret < 0) + btrfs_abort_transaction(trans, ret); + else + ret = btrfs_commit_transaction(trans, fs_info->tree_root); + return ret; +} + +static const char * const cmd_rescue_clear_uuid_tree_usage[] = { + "btrfs rescue clear-uuid-tree", + "Delete uuid tree so that kernel can rebuild it at mount time", + NULL, +}; + +static int cmd_rescue_clear_uuid_tree(const struct cmd_struct *cmd, + int argc, char **argv) +{ + struct btrfs_fs_info *fs_info; + struct open_ctree_flags ocf = {}; + char *devname; + int ret; + + clean_args_no_options(cmd, argc, argv); + if (check_argc_exact(argc, 2)) + return -EINVAL; + + devname = argv[optind]; + ret = check_mounted(devname); + if (ret < 0) { + errno = -ret; + error("could not check mount status: %m"); + goto out; + } else if (ret) { + error("%s is currently mounted", devname); + ret = -EBUSY; + goto out; + } + ocf.filename = devname; + ocf.flags = OPEN_CTREE_WRITES | OPEN_CTREE_PARTIAL; + fs_info = open_ctree_fs_info(&ocf); + if (!fs_info) { + error("could not open btrfs"); + ret = -EIO; + goto out; + } + + ret = clear_uuid_tree(fs_info); + close_ctree(fs_info->tree_root); +out: + return !!ret; +} + +static DEFINE_SIMPLE_COMMAND(rescue_clear_uuid_tree, "clear-uuid-tree"); + static const char rescue_cmd_group_info[] = "toolbox for specific rescue operations"; @@ -306,6 +409,7 @@ static const struct cmd_group rescue_cmd_group = { &cmd_struct_rescue_zero_log, &cmd_struct_rescue_fix_device_size, &cmd_struct_rescue_create_control_device, + &cmd_struct_rescue_clear_uuid_tree, NULL } };
[BUG] There is a bug report that a corrupted key type (expected UUID_KEY_SUBVOL, has EXTENT_ITEM) causing newer kernel to reject a mount. Although the root cause is not determined yet, with roll out of v5.11 kernel to various distros, such problem should be prevented by tree-checker, no matter if it's hardware problem or not. And older kernel with "-o uuid_rescan" mount option won't help, as uuid_rescan will only delete items with UUID_KEY_SUBVOL/UUID_KEY_RECEIVED_SUBVOL key types, not deleting such corrupted key. [FIX] To fix such problem we have to rely on offline tool, thus there we introduce a new rescue tool, clear-uuid-tree, to empty and then remove uuid tree. Kernel will re-generate the correct uuid tree at next mount. Reported-by: S. <sb56637@gmail.com> Signed-off-by: Qu Wenruo <wqu@suse.com> --- cmds/rescue.c | 104 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+)