From: Josef Bacik <josef@toxicpanda.com>
To: Qu Wenruo <wqu@suse.com>
Cc: linux-btrfs@vger.kernel.org
Subject: Re: [PATCH 3/3] btrfs-progs: introduce btrfs_rebuild_uuid_tree() for mkfs and btrfs-convert
Date: Fri, 26 Jul 2024 10:49:16 -0400 [thread overview]
Message-ID: <20240726144916.GB3432726@perftesting> (raw)
In-Reply-To: <8e33931a4d078d1e1aa620aa5fe717f35146ef31.1721987605.git.wqu@suse.com>
On Fri, Jul 26, 2024 at 07:29:55PM +0930, Qu Wenruo wrote:
> Currently mkfs uses its own create_uuid_tree(), but that function is
> only handling FS_TREE.
>
> This means for btrfs-convert, we do not generate the uuid tree, nor
> add the UUID of the image subvolume.
>
> This can be a problem if we're going to support multiple subvolumes
> during mkfs time.
>
> To address this inconvenience, this patch introduces a new helper,
> btrfs_rebuild_uuid_tree(), which will:
>
> - Create a new uuid tree if there is not one
>
> - Empty the existing uuid tree
>
> - Iterate through all subvolumes
> * If the subvolume has no valid UUID, regenerate one
> * Add the uuid entry for the subvolume UUID
> * If the subvolume has received UUID, also add it to UUID tree
>
> By this, this new helper can handle all the uuid tree generation needs for:
>
> - Current mkfs
> Only one uuid entry for FS_TREE
>
> - Current btrfs-convert
> Only FS_TREE and the image subvolume
>
> - Future multi-subvolume mkfs
> As we do the scan for all subvolumes.
>
> - Future "btrfs rescue rebuild-uuid-tree"
>
> Signed-off-by: Qu Wenruo <wqu@suse.com>
> ---
> common/root-tree-utils.c | 265 +++++++++++++++++++++++++++++++++++++++
> common/root-tree-utils.h | 1 +
> convert/main.c | 5 +
> mkfs/main.c | 37 +-----
> 4 files changed, 274 insertions(+), 34 deletions(-)
>
> diff --git a/common/root-tree-utils.c b/common/root-tree-utils.c
> index 6a57c51a8a74..13f89dbd5293 100644
> --- a/common/root-tree-utils.c
> +++ b/common/root-tree-utils.c
> @@ -15,9 +15,11 @@
> */
>
> #include <time.h>
> +#include <uuid/uuid.h>
> #include "common/root-tree-utils.h"
> #include "common/messages.h"
> #include "kernel-shared/disk-io.h"
> +#include "kernel-shared/uuid-tree.h"
>
> int btrfs_make_root_dir(struct btrfs_trans_handle *trans,
> struct btrfs_root *root, u64 objectid)
> @@ -212,3 +214,266 @@ abort:
> btrfs_abort_transaction(trans, ret);
> return ret;
> }
> +
> +static int empty_tree(struct btrfs_root *root)
> +{
> + struct btrfs_trans_handle *trans;
> + struct btrfs_path path = { 0 };
> + struct btrfs_key key = { 0 };
> + int ret;
> +
> + trans = btrfs_start_transaction(root, 1);
> + if (IS_ERR(trans)) {
> + ret = PTR_ERR(trans);
> + errno = -ret;
> + error_msg(ERROR_MSG_START_TRANS, "emptry tree: %m");
> + return ret;
> + }
> + while (true) {
> + int nr_items;
> +
> + ret = btrfs_search_slot(trans, root, &key, &path, -1, 1);
> + if (ret < 0) {
> + errno = -ret;
> + error("failed to locate the first key of root %lld: %m",
> + root->root_key.objectid);
> + btrfs_abort_transaction(trans, ret);
> + return ret;
> + }
> + UASSERT(ret > 0);
> + nr_items = btrfs_header_nritems(path.nodes[0]);
> + /* The tree is empty. */
> + if (nr_items == 0) {
> + btrfs_release_path(&path);
> + break;
> + }
> + ret = btrfs_del_items(trans, root, &path, 0, nr_items);
> + btrfs_release_path(&path);
> + if (ret < 0) {
> + errno = -ret;
> + error("failed to empty the first leaf of root %lld: %m",
> + root->root_key.objectid);
> + btrfs_abort_transaction(trans, ret);
> + return ret;
> + }
> + }
> + ret = btrfs_commit_transaction(trans, root);
> + if (ret < 0) {
> + errno = -ret;
> + error_msg(ERROR_MSG_COMMIT_TRANS, "empty tree: %m");
> + }
> + return ret;
> +}
> +
> +static int rescan_subvol_uuid(struct btrfs_trans_handle *trans,
> + struct btrfs_key *subvol_key)
> +{
> + struct btrfs_fs_info *fs_info = trans->fs_info;
> + struct btrfs_root *subvol;
> + int ret;
> +
> + UASSERT(is_fstree(subvol_key->objectid));
> +
> + /*
> + * Read out the subvolume root and updates root::root_item.
> + * This is to avoid de-sync between in-memory and on-disk root_items.
> + */
> + subvol = btrfs_read_fs_root(fs_info, subvol_key);
> + if (IS_ERR(subvol)) {
> + ret = PTR_ERR(subvol);
> + error("failed to read subvolume %llu: %m",
> + subvol_key->objectid);
> + btrfs_abort_transaction(trans, ret);
> + return ret;
> + }
> + /* The uuid is not set, regenerate one. */
> + if (uuid_is_null(subvol->root_item.uuid)) {
> + uuid_generate(subvol->root_item.uuid);
> + ret = btrfs_update_root(trans, fs_info->tree_root, &subvol->root_key,
> + &subvol->root_item);
> + if (ret < 0) {
> + error("failed to update subvolume %llu: %m",
> + subvol_key->objectid);
> + btrfs_abort_transaction(trans, ret);
> + return ret;
> + }
> + }
> + ret = btrfs_uuid_tree_add(trans, subvol->root_item.uuid,
> + BTRFS_UUID_KEY_SUBVOL,
> + subvol->root_key.objectid);
> + if (ret < 0) {
> + errno = -ret;
> + error("failed to add uuid for subvolume %llu: %m",
> + subvol_key->objectid);
> + btrfs_abort_transaction(trans, ret);
> + return ret;
> + }
> + if (!uuid_is_null(subvol->root_item.received_uuid)) {
> + ret = btrfs_uuid_tree_add(trans, subvol->root_item.uuid,
> + BTRFS_UUID_KEY_RECEIVED_SUBVOL,
> + subvol->root_key.objectid);
> + if (ret < 0) {
> + errno = -ret;
> + error("failed to add received_uuid for subvol %llu: %m",
> + subvol->root_key.objectid);
> + btrfs_abort_transaction(trans, ret);
> + return ret;
> + }
> + }
> + return 0;
> +}
> +
> +static int rescan_uuid_tree(struct btrfs_fs_info *fs_info)
> +{
> + struct btrfs_root *tree_root = fs_info->tree_root;
> + struct btrfs_root *uuid_root = fs_info->uuid_root;
> + struct btrfs_trans_handle *trans;
> + struct btrfs_path path = { 0 };
> + struct btrfs_key key = { 0 };
> + int ret;
> +
> + UASSERT(uuid_root);
> + trans = btrfs_start_transaction(uuid_root, 1);
> + if (IS_ERR(trans)) {
> + ret = PTR_ERR(trans);
> + errno = -ret;
> + error_msg(ERROR_MSG_START_TRANS, "rescan uuid tree: %m");
> + return ret;
> + }
> + key.objectid = BTRFS_LAST_FREE_OBJECTID;
> + key.type = BTRFS_ROOT_ITEM_KEY;
> + key.offset = (u64)-1;
> + /* Iterate through all subvolumes except fs tree. */
> + while (true) {
> + struct btrfs_key found_key;
> + struct extent_buffer *leaf;
> + int slot;
> +
> + /* No more subvolume. */
> + if (key.objectid < BTRFS_FIRST_FREE_OBJECTID) {
> + ret = 0;
> + break;
> + }
> + ret = btrfs_search_slot(NULL, tree_root, &key, &path, 0, 0);
> + if (ret < 0) {
> + errno = -ret;
> + error_msg(ERROR_MSG_READ, "iterate subvolumes: %m");
> + btrfs_abort_transaction(trans, ret);
> + return ret;
> + }
> + if (ret > 0) {
> + ret = btrfs_previous_item(tree_root, &path,
> + BTRFS_FIRST_FREE_OBJECTID,
> + BTRFS_ROOT_ITEM_KEY);
> + if (ret < 0) {
> + errno = -ret;
> + btrfs_release_path(&path);
> + error_msg(ERROR_MSG_READ, "iterate subvolumes: %m");
> + btrfs_abort_transaction(trans, ret);
> + return ret;
> + }
> + /* No more subvolume. */
> + if (ret > 0) {
> + ret = 0;
> + btrfs_release_path(&path);
> + break;
> + }
> + }
> + leaf = path.nodes[0];
> + slot = path.slots[0];
> + btrfs_item_key_to_cpu(leaf, &found_key, slot);
> + btrfs_release_path(&path);
> + key.objectid = found_key.objectid - 1;
> +
> + ret = rescan_subvol_uuid(trans, &found_key);
> + if (ret < 0) {
> + errno = -ret;
> + error("failed to rescan the uuid of subvolume %llu: %m",
> + found_key.objectid);
> + btrfs_abort_transaction(trans, ret);
> + return ret;
> + }
> + }
> +
> + /* Update fs tree uuid. */
> + key.objectid = BTRFS_FS_TREE_OBJECTID;
> + key.type = BTRFS_ROOT_ITEM_KEY;
> + key.offset = 0;
> + ret = rescan_subvol_uuid(trans, &key);
> + if (ret < 0) {
> + errno = -ret;
> + error("failed to rescan the uuid of subvolume %llu: %m",
> + key.objectid);
> + btrfs_abort_transaction(trans, ret);
> + return ret;
> + }
> + ret = btrfs_commit_transaction(trans, uuid_root);
> + if (ret < 0) {
> + errno = -ret;
> + error_msg(ERROR_MSG_COMMIT_TRANS, "rescan uuid tree: %m");
> + }
> + return ret;
> +}
> +
> +/*
> + * Rebuild the whole uuid tree.
> + *
> + * If no uuid tree is present, create a new one.
> + * If there is an existing uuid tree, all items would be deleted first.
> + *
> + * For all existing subvolumes (except fs tree), any uninitialized uuid
> + * (all zero) be generated using a random uuid, and inserted into the new tree.
> + * And if a subvolume has its UUID initialized, it would not be touched and
> + * be added to the new uuid tree.
> + */
> +int btrfs_rebuild_uuid_tree(struct btrfs_fs_info *fs_info)
> +{
> + struct btrfs_root *uuid_root;
> + struct btrfs_key key;
> + int ret;
> +
> + if (!fs_info->uuid_root) {
> + struct btrfs_trans_handle *trans;
> +
> + trans = btrfs_start_transaction(fs_info->tree_root, 1);
> + if (IS_ERR(trans)) {
> + ret = PTR_ERR(trans);
> + errno = -ret;
> + error_msg(ERROR_MSG_START_TRANS, "create uuid tree: %m");
> + return ret;
> + }
> + key.objectid = BTRFS_UUID_TREE_OBJECTID;
> + key.type = BTRFS_ROOT_ITEM_KEY;
> + key.offset = 0;
> + uuid_root = btrfs_create_tree(trans, &key);
> + if (IS_ERR(uuid_root)) {
> + ret = PTR_ERR(uuid_root);
> + errno = -ret;
> + error("failed to create uuid root: %m");
> + btrfs_abort_transaction(trans, ret);
> + return ret;
> + }
> + add_root_to_dirty_list(uuid_root);
> + fs_info->uuid_root = uuid_root;
> + ret = btrfs_commit_transaction(trans, fs_info->tree_root);
> + if (ret < 0) {
> + errno = -ret;
> + error_msg(ERROR_MSG_COMMIT_TRANS, "create uuid tree: %m");
> + return ret;
> + }
> + }
This can just be
} else {
ret = empty_tree();
}
ret = rescan_uuid_tree(fs_info).
Thanks,
Josef
next prev parent reply other threads:[~2024-07-26 14:49 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-07-26 9:59 [PATCH 0/3] btrfs-progs: a more generic uuid tree rebuild ability Qu Wenruo
2024-07-26 9:59 ` [PATCH 1/3] btrfs-progs: move uuid-tree definitions to kernel-shared/uuid-tree.h Qu Wenruo
2024-07-26 9:59 ` [PATCH 2/3] btrfs-progs: cross-port btrfs_uuid_tree_add() from kernel Qu Wenruo
2024-07-26 9:59 ` [PATCH 3/3] btrfs-progs: introduce btrfs_rebuild_uuid_tree() for mkfs and btrfs-convert Qu Wenruo
2024-07-26 10:03 ` Qu Wenruo
2024-07-26 14:49 ` Josef Bacik [this message]
2024-07-26 15:35 ` David Sterba
2024-07-26 22:33 ` Qu Wenruo
2024-07-26 22:58 ` David Sterba
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20240726144916.GB3432726@perftesting \
--to=josef@toxicpanda.com \
--cc=linux-btrfs@vger.kernel.org \
--cc=wqu@suse.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.