For big filesystems, there are many items in trees(extent tree
specially).
For example, to dump one extent, we usually dump extent tree then pipe
result to grep. The time-consuming part is that dump tree traverses
items. And it eats cpu and memory too.
This patch introduces an option '-k u64,u8,u64' for users(developer more
likely) to appoint a specific key to search and dump, then the path
from root node down the leaf will be dumped.
The search of the key costs most time of this way.
Signed-off-by: Su Yue <suy.fnst@xxxxxxxxxxxxxx>
---
In my experiment, not usual case though.
Image was provided by Chris Murphy, thanks.
#btrfs filesystem df /mnt
Data, single: total=767.00GiB, used=766.58GiB
System, DUP: total=32.00MiB, used=112.00KiB
Metadata, DUP: total=5.50GiB, used=4.69GiB
Metadata, single: total=16.00MiB, used=0.00B
GlobalReserve, single: total=512.00MiB, used=0.00B
Before:
#time bash -c 'btrfs inspect-internal dump-tree -t 2 ~/test/test.img | grep "1295194308608" >> /dev/null'
real 0m34.024s
user 0m3.269s
sys 0m4.047s
Patched and use -k:
#time bash -c './btrfs inspect-internal dump-tree -t 2 -k 1295194308608,0,0 ~/test/test.img >> /dev/null'
real 0m1.408s
user 0m0.006s
sys 0m0.014s
The new way is 34 times faster than 'grep' way, uses less memory and
cpu, and it prints path useful for debug.
---
cmds-inspect-dump-tree.c | 54 +++++++++++++++++--
print-tree.c | 112 +++++++++++++++++++++++++++++++++++++++
print-tree.h | 2 +
3 files changed, 163 insertions(+), 5 deletions(-)
diff --git a/cmds-inspect-dump-tree.c b/cmds-inspect-dump-tree.c
index c8acd55a0c3a..2a1a40c8270d 100644
--- a/cmds-inspect-dump-tree.c
+++ b/cmds-inspect-dump-tree.c
@@ -184,6 +184,21 @@ static u64 treeid_from_string(const char *str, const char **end)
return id;
}
+static int parse_key(struct btrfs_key *key)
+{
+
+ int ret = sscanf(optarg, "%llu,%hhu,%llu", &key->objectid,
+ &key->type, &key->offset);
+ if (ret != 3) {
+ error("error parsing key '%s'\n", optarg);
+ ret = -EINVAL;
+ } else {
+ ret = 0;
+ }
+
+ return ret;
+}
+
const char * const cmd_inspect_dump_tree_usage[] = {
"btrfs inspect-internal dump-tree [options] device",
"Dump tree structures from a given device",
@@ -199,6 +214,7 @@ const char * const cmd_inspect_dump_tree_usage[] = {
"-u|--uuid print only the uuid tree",
"-b|--block <block_num> print info from the specified block only",
"-t|--tree <tree_id> print only tree with the given id (string or number)",
+ "-k|--key <u64,u8,u64> search the specific key then print path, must with -t",
"--follow use with -b, to show all children tree blocks of <block_num>",
NULL
};
@@ -224,8 +240,10 @@ int cmd_inspect_dump_tree(int argc, char **argv)
unsigned open_ctree_flags;
u64 block_only = 0;
struct btrfs_root *tree_root_scan;
+ struct btrfs_key spec_key = { 0 };
u64 tree_id = 0;
bool follow = false;
+ bool is_key_spec = false;
/*
* For debug-tree, we care nothing about extent tree (it's just backref
@@ -248,10 +266,11 @@ int cmd_inspect_dump_tree(int argc, char **argv)
{ "block", required_argument, NULL, 'b'},
{ "tree", required_argument, NULL, 't'},
{ "follow", no_argument, NULL, GETOPT_VAL_FOLLOW },
+ {"key", required_argument, NULL, 'k'},
{ NULL, 0, NULL, 0 }
};
- c = getopt_long(argc, argv, "deb:rRut:", long_options, NULL);
+ c = getopt_long(argc, argv, "deb:rRut:k:", long_options, NULL);
if (c < 0)
break;
switch (c) {
@@ -300,6 +319,12 @@ int cmd_inspect_dump_tree(int argc, char **argv)
}
break;
}
+ case 'k':
+ ret = parse_key(&spec_key);
+ if (ret)
+ exit(1);
+ is_key_spec = true;
+ break;
case GETOPT_VAL_FOLLOW:
follow = true;
break;
@@ -308,6 +333,9 @@ int cmd_inspect_dump_tree(int argc, char **argv)
}
}
+ if (!tree_id && is_key_spec)
+ usage(cmd_inspect_dump_tree_usage);
+
if (check_argc_exact(argc - optind, 1))
usage(cmd_inspect_dump_tree_usage);
@@ -398,7 +426,11 @@ again:
goto close_root;
}
printf("root tree\n");
- btrfs_print_tree(info->tree_root->node, 1);
+ if (is_key_spec)
+ btrfs_print_spec_key(info, BTRFS_ROOT_TREE_OBJECTID,
+ &spec_key);
+ else
+ btrfs_print_tree(info->tree_root->node, 1);
goto close_root;
}
@@ -408,7 +440,11 @@ again:
goto close_root;
}
printf("chunk tree\n");
- btrfs_print_tree(info->chunk_root->node, 1);
+ if (is_key_spec)
+ btrfs_print_spec_key(info, BTRFS_CHUNK_TREE_OBJECTID,
+ &spec_key);
+ else
+ btrfs_print_tree(info->chunk_root->node, 1);
goto close_root;
}
@@ -418,7 +454,11 @@ again:
goto close_root;
}
printf("log root tree\n");
- btrfs_print_tree(info->log_root_tree->node, 1);
+ if (is_key_spec)
+ btrfs_print_spec_key(info, BTRFS_TREE_LOG_OBJECTID,
+ &spec_key);
+ else
+ btrfs_print_tree(info->log_root_tree->node, 1);
goto close_root;
}
@@ -564,7 +604,11 @@ again:
btrfs_header_level(buf));
} else {
printf(" \n");
- btrfs_print_tree(buf, 1);
+ if (is_key_spec)
+ btrfs_print_spec_key(info,
+ found_key.objectid, &spec_key);
+ else
+ btrfs_print_tree(buf, 1);
}
}
free_extent_buffer(buf);
diff --git a/print-tree.c b/print-tree.c
index a09ecfbb28f0..f3a19fed8591 100644
--- a/print-tree.c
+++ b/print-tree.c
@@ -1459,3 +1459,115 @@ void btrfs_print_tree(struct extent_buffer *eb, int follow)
return;
}
+
+void btrfs_print_spec_key(struct btrfs_fs_info *fs_info, u64 root_objectid,
+ struct btrfs_key *skey)
+{
+ int i;
+ u32 nr;
+ u32 ptr_num;
+ int ret;
+ int level;
+ struct btrfs_root *root;
+ struct btrfs_disk_key disk_key;
+ struct btrfs_key key;
+ struct extent_buffer *next;
+ struct extent_buffer *eb;
+ struct btrfs_path path;
+
+ key.objectid = root_objectid;
+ key.type = BTRFS_ROOT_ITEM_KEY;
+ key.offset = (u64)-1;
+
+ if (key.objectid == BTRFS_TREE_RELOC_OBJECTID)
+ root = btrfs_read_fs_root_no_cache(fs_info, &key);
+ else
+ root = btrfs_read_fs_root(fs_info, &key);
+
+ if (IS_ERR(root) || !root) {
+ error("failed to read tree: %lld", key.objectid);
+ return;
+ }
+
+ btrfs_init_path(&path);
+ ret = btrfs_search_slot(NULL, root, skey, &path, 0, 0);
+ if (ret < 0) {
+ error("error search the key [%llu, %u, %llu] in root %llu",
+ skey->objectid, skey->type, skey->offset, root->objectid);
+ goto out;
+ }
+
+ if (ret > 0) {
+ if (path.slots[0] > btrfs_header_nritems(path.nodes[0])) {
+ ret = btrfs_next_item(root, &path);
+ if (ret) {
+ error(
+ "error search the key [%llu, %u, %llu] in root %llu, no next key",
+ skey->objectid, skey->type,
+ skey->offset, root->objectid);
+ goto out;
+ }
+ }
+
+ btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
+ printf("next to search key is [%llu, %u, %llu]\n",
+ key.objectid, key.type, key.offset);
+ }
+
+ level = btrfs_header_level(root->node);
+ for (; level >= 0 ; --level) {
+ eb = path.nodes[level];
+ next = path.nodes[level - 1];
+ nr = btrfs_header_nritems(eb);
+ if (btrfs_is_leaf(eb)) {
+ btrfs_print_leaf(eb);
+ goto out;
+ }
+
+ /* We are crossing eb boundary, this node must be corrupted */
+ if (nr > BTRFS_NODEPTRS_PER_EXTENT_BUFFER(eb))
+ warning(
+ "node nr_items corrupted, has %u limit %u, continue anyway",
+ nr, BTRFS_NODEPTRS_PER_EXTENT_BUFFER(eb));
+ printf(
+ "node %llu level %d items %d free %u generation %llu owner ",
+ (unsigned long long)eb->start,
+ btrfs_header_level(eb), nr,
+ (u32)BTRFS_NODEPTRS_PER_EXTENT_BUFFER(eb) - nr,
+ (unsigned long long)btrfs_header_generation(eb));
+ print_objectid(stdout, btrfs_header_owner(eb), 0);
+ printf("\n");
+ print_uuids(eb);
+ fflush(stdout);
+ ptr_num = BTRFS_NODEPTRS_PER_EXTENT_BUFFER(eb);
+ for (i = 0; i < nr && i < ptr_num; i++) {
+ u64 blocknr = btrfs_node_blockptr(eb, i);
+
+ btrfs_node_key(eb, &disk_key, i);
+ btrfs_disk_key_to_cpu(&key, &disk_key);
+ printf("\t");
+ btrfs_print_key(&disk_key);
+ printf(" block %llu (%llu) gen %llu\n",
+ (unsigned long long)blocknr,
+ (unsigned long long)blocknr / eb->len,
+ (unsigned long long)btrfs_node_ptr_generation(eb, i));
+ fflush(stdout);
+ }
+
+ if (btrfs_header_level(next) != btrfs_header_level(eb) - 1) {
+ warning(
+ "eb corrupted: parent bytenr %llu slot %d level %d child bytenr %llu level has %d expect %d, skipping the slot",
+ btrfs_header_bytenr(eb), i,
+ btrfs_header_level(eb),
+ btrfs_header_bytenr(next),
+ btrfs_header_level(next),
+ btrfs_header_level(eb) - 1);
+ continue;
+ }
+ }
+
+out:
+ if (root_objectid == BTRFS_TREE_RELOC_OBJECTID)
+ btrfs_free_fs_root(root);
+ btrfs_release_path(&path);
+}
diff --git a/print-tree.h b/print-tree.h
index 62667d7f6195..94f231875a6c 100644
--- a/print-tree.h
+++ b/print-tree.h
@@ -26,4 +26,6 @@ void print_chunk_item(struct extent_buffer *eb, struct btrfs_chunk *chunk);
void print_extent_item(struct extent_buffer *eb, int slot, int metadata);
void print_objectid(FILE *stream, u64 objectid, u8 type);
void print_key_type(FILE *stream, u64 objectid, u8 type);
+void btrfs_print_spec_key(struct btrfs_fs_info *info, u64 root_objectid,
+ struct btrfs_key *key);
#endif
--
2.17.1
--
To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html