diff options
Diffstat (limited to 'fs/btrfs/chunk-map.c')
-rw-r--r-- | fs/btrfs/chunk-map.c | 178 |
1 files changed, 178 insertions, 0 deletions
diff --git a/fs/btrfs/chunk-map.c b/fs/btrfs/chunk-map.c new file mode 100644 index 0000000000..48407f3331 --- /dev/null +++ b/fs/btrfs/chunk-map.c @@ -0,0 +1,178 @@ +/* + * BTRFS filesystem implementation for U-Boot + * + * 2017 Marek Behun, CZ.NIC, marek.behun@nic.cz + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include "btrfs.h" +#include <malloc.h> + +struct chunk_map_item { + struct rb_node node; + u64 logical; + u64 length; + u64 physical; +}; + +static int add_chunk_mapping(struct btrfs_key *key, struct btrfs_chunk *chunk) +{ + struct btrfs_stripe *stripe; + u64 block_profile = chunk->type & BTRFS_BLOCK_GROUP_PROFILE_MASK; + struct rb_node **new = &(btrfs_info.chunks_root.rb_node), *prnt = NULL; + struct chunk_map_item *map_item; + + if (block_profile && block_profile != BTRFS_BLOCK_GROUP_DUP) { + printf("%s: unsupported chunk profile %llu\n", __func__, + block_profile); + return -1; + } else if (!chunk->length) { + printf("%s: zero length chunk\n", __func__); + return -1; + } + + stripe = &chunk->stripe; + btrfs_stripe_to_cpu(stripe); + + while (*new) { + struct chunk_map_item *this; + + this = rb_entry(*new, struct chunk_map_item, node); + + prnt = *new; + if (key->offset < this->logical) { + new = &((*new)->rb_left); + } else if (key->offset > this->logical) { + new = &((*new)->rb_right); + } else { + debug("%s: Logical address %llu already in map!\n", + __func__, key->offset); + return 0; + } + } + + map_item = malloc(sizeof(struct chunk_map_item)); + if (!map_item) + return -1; + + map_item->logical = key->offset; + map_item->length = chunk->length; + map_item->physical = le64_to_cpu(chunk->stripe.offset); + rb_link_node(&map_item->node, prnt, new); + rb_insert_color(&map_item->node, &btrfs_info.chunks_root); + + debug("%s: Mapping %llu to %llu\n", __func__, map_item->logical, + map_item->physical); + + return 0; +} + +u64 btrfs_map_logical_to_physical(u64 logical) +{ + struct rb_node *node = btrfs_info.chunks_root.rb_node; + + while (node) { + struct chunk_map_item *item; + + item = rb_entry(node, struct chunk_map_item, node); + + if (item->logical > logical) + node = node->rb_left; + else if (logical > item->logical + item->length) + node = node->rb_right; + else + return item->physical + logical - item->logical; + } + + printf("%s: Cannot map logical address %llu to physical\n", __func__, + logical); + + return -1ULL; +} + +void btrfs_chunk_map_exit(void) +{ + struct rb_node *now, *next; + struct chunk_map_item *item; + + for (now = rb_first_postorder(&btrfs_info.chunks_root); now; now = next) + { + item = rb_entry(now, struct chunk_map_item, node); + next = rb_next_postorder(now); + free(item); + } +} + +int btrfs_chunk_map_init(void) +{ + u8 sys_chunk_array_copy[sizeof(btrfs_info.sb.sys_chunk_array)]; + u8 * const start = sys_chunk_array_copy; + u8 * const end = start + btrfs_info.sb.sys_chunk_array_size; + u8 *cur; + struct btrfs_key *key; + struct btrfs_chunk *chunk; + + btrfs_info.chunks_root = RB_ROOT; + + memcpy(sys_chunk_array_copy, btrfs_info.sb.sys_chunk_array, + sizeof(sys_chunk_array_copy)); + + for (cur = start; cur < end;) { + key = (struct btrfs_key *) cur; + cur += sizeof(struct btrfs_key); + chunk = (struct btrfs_chunk *) cur; + + btrfs_key_to_cpu(key); + btrfs_chunk_to_cpu(chunk); + + if (key->type != BTRFS_CHUNK_ITEM_KEY) { + printf("%s: invalid key type %u\n", __func__, + key->type); + return -1; + } + + if (add_chunk_mapping(key, chunk)) + return -1; + + cur += sizeof(struct btrfs_chunk); + cur += sizeof(struct btrfs_stripe) * (chunk->num_stripes - 1); + } + + return 0; +} + +int btrfs_read_chunk_tree(void) +{ + struct btrfs_path path; + struct btrfs_key key, *found_key; + struct btrfs_chunk *chunk; + int res; + + key.objectid = BTRFS_FIRST_CHUNK_TREE_OBJECTID; + key.type = BTRFS_CHUNK_ITEM_KEY; + key.offset = 0; + + if (btrfs_search_tree(&btrfs_info.chunk_root, &key, &path)) + return -1; + + do { + found_key = btrfs_path_leaf_key(&path); + if (btrfs_comp_keys_type(&key, found_key)) + break; + + chunk = btrfs_path_item_ptr(&path, struct btrfs_chunk); + btrfs_chunk_to_cpu(chunk); + if (add_chunk_mapping(found_key, chunk)) { + res = -1; + break; + } + } while (!(res = btrfs_next_slot(&path))); + + btrfs_free_path(&path); + + if (res < 0) + return -1; + + return 0; +} |