summaryrefslogtreecommitdiff
path: root/fs/btrfs/disk-io.c
diff options
context:
space:
mode:
authorQu Wenruo <wqu@suse.com>2020-06-24 19:03:01 +0300
committerTom Rini <trini@konsulko.com>2020-09-08 03:57:27 +0300
commitf06bfcf54d0e91892fcd1379e04aae9cd031fc78 (patch)
treeaac26e518b93db18f3a3557e78340f9ad5483465 /fs/btrfs/disk-io.c
parent57f24f10733ac57754d4eae3666919480718b032 (diff)
downloadu-boot-f06bfcf54d0e91892fcd1379e04aae9cd031fc78.tar.xz
fs: btrfs: Crossport open_ctree_fs_info() from btrfs-progs
open_ctree_fs_info() is the main entry point to open btrfs. This version is a simplfied version of __open_ctree_fd() of btrfs-progs, the main differences are: - Parameters on how to specify a block device Instead of @fd and @path, U-Boot uses blk_desc and disk_partition_t. - Remove open_ctree flags There won't be multiple open ctree modes in U-Boot. Otherwise functions structures are all kept the same. With open_ctree_fs_info() implemented, also introduce the global current_fs_info pointer to show the current opened btrfs. Signed-off-by: Qu Wenruo <wqu@suse.com> Reviewed-by: Marek BehĂșn <marek.behun@nic.cz>
Diffstat (limited to 'fs/btrfs/disk-io.c')
-rw-r--r--fs/btrfs/disk-io.c509
1 files changed, 491 insertions, 18 deletions
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index 65b4020ebe..4d08b0204a 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -4,6 +4,7 @@
#include <uuid.h>
#include <memalign.h>
#include "kernel-shared/btrfs_tree.h"
+#include "common/rbtree-utils.h"
#include "disk-io.h"
#include "ctree.h"
#include "btrfs.h"
@@ -424,24 +425,6 @@ out:
}
-int btrfs_buffer_uptodate(struct extent_buffer *buf, u64 parent_transid)
-{
- int ret;
-
- ret = extent_buffer_uptodate(buf);
- if (!ret)
- return ret;
-
- ret = verify_parent_transid(&buf->fs_info->extent_cache, buf,
- parent_transid, 1);
- return !ret;
-}
-
-int btrfs_set_buffer_uptodate(struct extent_buffer *eb)
-{
- return set_extent_buffer_uptodate(eb);
-}
-
int read_whole_eb(struct btrfs_fs_info *info, struct extent_buffer *eb, int mirror)
{
unsigned long offset = 0;
@@ -581,3 +564,493 @@ struct extent_buffer* read_tree_block(struct btrfs_fs_info *fs_info, u64 bytenr,
free_extent_buffer(eb);
return ERR_PTR(ret);
}
+
+void btrfs_setup_root(struct btrfs_root *root, struct btrfs_fs_info *fs_info,
+ u64 objectid)
+{
+ root->node = NULL;
+ root->track_dirty = 0;
+
+ root->fs_info = fs_info;
+ root->objectid = objectid;
+ root->last_trans = 0;
+ root->last_inode_alloc = 0;
+
+ memset(&root->root_key, 0, sizeof(root->root_key));
+ memset(&root->root_item, 0, sizeof(root->root_item));
+ root->root_key.objectid = objectid;
+}
+
+static int find_and_setup_root(struct btrfs_root *tree_root,
+ struct btrfs_fs_info *fs_info,
+ u64 objectid, struct btrfs_root *root)
+{
+ int ret;
+ u64 generation;
+
+ btrfs_setup_root(root, fs_info, objectid);
+ ret = btrfs_find_last_root(tree_root, objectid,
+ &root->root_item, &root->root_key);
+ if (ret)
+ return ret;
+
+ generation = btrfs_root_generation(&root->root_item);
+ root->node = read_tree_block(fs_info,
+ btrfs_root_bytenr(&root->root_item), generation);
+ if (!extent_buffer_uptodate(root->node))
+ return -EIO;
+
+ return 0;
+}
+
+int btrfs_free_fs_root(struct btrfs_root *root)
+{
+ if (root->node)
+ free_extent_buffer(root->node);
+ kfree(root);
+ return 0;
+}
+
+static void __free_fs_root(struct rb_node *node)
+{
+ struct btrfs_root *root;
+
+ root = container_of(node, struct btrfs_root, rb_node);
+ btrfs_free_fs_root(root);
+}
+
+FREE_RB_BASED_TREE(fs_roots, __free_fs_root);
+
+struct btrfs_root *btrfs_read_fs_root_no_cache(struct btrfs_fs_info *fs_info,
+ struct btrfs_key *location)
+{
+ struct btrfs_root *root;
+ struct btrfs_root *tree_root = fs_info->tree_root;
+ struct btrfs_path *path;
+ struct extent_buffer *l;
+ u64 generation;
+ int ret = 0;
+
+ root = calloc(1, sizeof(*root));
+ if (!root)
+ return ERR_PTR(-ENOMEM);
+ if (location->offset == (u64)-1) {
+ ret = find_and_setup_root(tree_root, fs_info,
+ location->objectid, root);
+ if (ret) {
+ free(root);
+ return ERR_PTR(ret);
+ }
+ goto insert;
+ }
+
+ btrfs_setup_root(root, fs_info,
+ location->objectid);
+
+ path = btrfs_alloc_path();
+ if (!path) {
+ free(root);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ ret = btrfs_search_slot(NULL, tree_root, location, path, 0, 0);
+ if (ret != 0) {
+ if (ret > 0)
+ ret = -ENOENT;
+ goto out;
+ }
+ l = path->nodes[0];
+ read_extent_buffer(l, &root->root_item,
+ btrfs_item_ptr_offset(l, path->slots[0]),
+ sizeof(root->root_item));
+ memcpy(&root->root_key, location, sizeof(*location));
+
+ /* If this root is already an orphan, no need to read */
+ if (btrfs_root_refs(&root->root_item) == 0) {
+ ret = -ENOENT;
+ goto out;
+ }
+ ret = 0;
+out:
+ btrfs_free_path(path);
+ if (ret) {
+ free(root);
+ return ERR_PTR(ret);
+ }
+ generation = btrfs_root_generation(&root->root_item);
+ root->node = read_tree_block(fs_info,
+ btrfs_root_bytenr(&root->root_item), generation);
+ if (!extent_buffer_uptodate(root->node)) {
+ free(root);
+ return ERR_PTR(-EIO);
+ }
+insert:
+ root->ref_cows = 1;
+ return root;
+}
+
+static int btrfs_fs_roots_compare_objectids(struct rb_node *node,
+ void *data)
+{
+ u64 objectid = *((u64 *)data);
+ struct btrfs_root *root;
+
+ root = rb_entry(node, struct btrfs_root, rb_node);
+ if (objectid > root->objectid)
+ return 1;
+ else if (objectid < root->objectid)
+ return -1;
+ else
+ return 0;
+}
+
+int btrfs_fs_roots_compare_roots(struct rb_node *node1, struct rb_node *node2)
+{
+ struct btrfs_root *root;
+
+ root = rb_entry(node2, struct btrfs_root, rb_node);
+ return btrfs_fs_roots_compare_objectids(node1, (void *)&root->objectid);
+}
+
+struct btrfs_root *btrfs_read_fs_root(struct btrfs_fs_info *fs_info,
+ struct btrfs_key *location)
+{
+ struct btrfs_root *root;
+ struct rb_node *node;
+ int ret;
+ u64 objectid = location->objectid;
+
+ if (location->objectid == BTRFS_ROOT_TREE_OBJECTID)
+ return fs_info->tree_root;
+ if (location->objectid == BTRFS_CHUNK_TREE_OBJECTID)
+ return fs_info->chunk_root;
+ if (location->objectid == BTRFS_CSUM_TREE_OBJECTID)
+ return fs_info->csum_root;
+ BUG_ON(location->objectid == BTRFS_TREE_RELOC_OBJECTID ||
+ location->offset != (u64)-1);
+
+ node = rb_search(&fs_info->fs_root_tree, (void *)&objectid,
+ btrfs_fs_roots_compare_objectids, NULL);
+ if (node)
+ return container_of(node, struct btrfs_root, rb_node);
+
+ root = btrfs_read_fs_root_no_cache(fs_info, location);
+ if (IS_ERR(root))
+ return root;
+
+ ret = rb_insert(&fs_info->fs_root_tree, &root->rb_node,
+ btrfs_fs_roots_compare_roots);
+ BUG_ON(ret);
+ return root;
+}
+
+void btrfs_free_fs_info(struct btrfs_fs_info *fs_info)
+{
+ free(fs_info->tree_root);
+ free(fs_info->chunk_root);
+ free(fs_info->csum_root);
+ free(fs_info->super_copy);
+ free(fs_info);
+}
+
+struct btrfs_fs_info *btrfs_new_fs_info(void)
+{
+ struct btrfs_fs_info *fs_info;
+
+ fs_info = calloc(1, sizeof(struct btrfs_fs_info));
+ if (!fs_info)
+ return NULL;
+
+ fs_info->tree_root = calloc(1, sizeof(struct btrfs_root));
+ fs_info->chunk_root = calloc(1, sizeof(struct btrfs_root));
+ fs_info->csum_root = calloc(1, sizeof(struct btrfs_root));
+ fs_info->super_copy = calloc(1, BTRFS_SUPER_INFO_SIZE);
+
+ if (!fs_info->tree_root || !fs_info->chunk_root ||
+ !fs_info->csum_root || !fs_info->super_copy)
+ goto free_all;
+
+ extent_io_tree_init(&fs_info->extent_cache);
+
+ fs_info->fs_root_tree = RB_ROOT;
+ cache_tree_init(&fs_info->mapping_tree.cache_tree);
+
+ mutex_init(&fs_info->fs_mutex);
+
+ return fs_info;
+free_all:
+ btrfs_free_fs_info(fs_info);
+ return NULL;
+}
+
+static int setup_root_or_create_block(struct btrfs_fs_info *fs_info,
+ struct btrfs_root *info_root,
+ u64 objectid, char *str)
+{
+ struct btrfs_root *root = fs_info->tree_root;
+ int ret;
+
+ ret = find_and_setup_root(root, fs_info, objectid, info_root);
+ if (ret) {
+ error("could not setup %s tree", str);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+int btrfs_setup_all_roots(struct btrfs_fs_info *fs_info)
+{
+ struct btrfs_super_block *sb = fs_info->super_copy;
+ struct btrfs_root *root;
+ struct btrfs_key key;
+ u64 root_tree_bytenr;
+ u64 generation;
+ int ret;
+
+ root = fs_info->tree_root;
+ btrfs_setup_root(root, fs_info, BTRFS_ROOT_TREE_OBJECTID);
+ generation = btrfs_super_generation(sb);
+
+ root_tree_bytenr = btrfs_super_root(sb);
+
+ root->node = read_tree_block(fs_info, root_tree_bytenr, generation);
+ if (!extent_buffer_uptodate(root->node)) {
+ fprintf(stderr, "Couldn't read tree root\n");
+ return -EIO;
+ }
+
+ ret = setup_root_or_create_block(fs_info, fs_info->csum_root,
+ BTRFS_CSUM_TREE_OBJECTID, "csum");
+ if (ret)
+ return ret;
+ fs_info->csum_root->track_dirty = 1;
+
+ fs_info->last_trans_committed = generation;
+
+ key.objectid = BTRFS_FS_TREE_OBJECTID;
+ key.type = BTRFS_ROOT_ITEM_KEY;
+ key.offset = (u64)-1;
+ fs_info->fs_root = btrfs_read_fs_root(fs_info, &key);
+
+ if (IS_ERR(fs_info->fs_root))
+ return -EIO;
+ return 0;
+}
+
+void btrfs_release_all_roots(struct btrfs_fs_info *fs_info)
+{
+ if (fs_info->csum_root)
+ free_extent_buffer(fs_info->csum_root->node);
+ if (fs_info->tree_root)
+ free_extent_buffer(fs_info->tree_root->node);
+ if (fs_info->chunk_root)
+ free_extent_buffer(fs_info->chunk_root->node);
+}
+
+static void free_map_lookup(struct cache_extent *ce)
+{
+ struct map_lookup *map;
+
+ map = container_of(ce, struct map_lookup, ce);
+ kfree(map);
+}
+
+FREE_EXTENT_CACHE_BASED_TREE(mapping_cache, free_map_lookup);
+
+void btrfs_cleanup_all_caches(struct btrfs_fs_info *fs_info)
+{
+ free_mapping_cache_tree(&fs_info->mapping_tree.cache_tree);
+ extent_io_tree_cleanup(&fs_info->extent_cache);
+}
+
+static int btrfs_scan_fs_devices(struct blk_desc *desc,
+ struct disk_partition *part,
+ struct btrfs_fs_devices **fs_devices)
+{
+ u64 total_devs;
+ int ret;
+
+ if (round_up(BTRFS_SUPER_INFO_SIZE + BTRFS_SUPER_INFO_OFFSET,
+ desc->blksz) > (part->size << desc->log2blksz)) {
+ error("superblock end %u is larger than device size " LBAFU,
+ BTRFS_SUPER_INFO_SIZE + BTRFS_SUPER_INFO_OFFSET,
+ part->size << desc->log2blksz);
+ return -EINVAL;
+ }
+
+ ret = btrfs_scan_one_device(desc, part, fs_devices, &total_devs);
+ if (ret) {
+ fprintf(stderr, "No valid Btrfs found\n");
+ return ret;
+ }
+ return 0;
+}
+
+int btrfs_check_fs_compatibility(struct btrfs_super_block *sb)
+{
+ u64 features;
+
+ features = btrfs_super_incompat_flags(sb) &
+ ~BTRFS_FEATURE_INCOMPAT_SUPP;
+ if (features) {
+ printk("couldn't open because of unsupported "
+ "option features (%llx).\n",
+ (unsigned long long)features);
+ return -ENOTSUPP;
+ }
+
+ features = btrfs_super_incompat_flags(sb);
+ if (!(features & BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF)) {
+ features |= BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF;
+ btrfs_set_super_incompat_flags(sb, features);
+ }
+
+ return 0;
+}
+
+static int btrfs_setup_chunk_tree_and_device_map(struct btrfs_fs_info *fs_info)
+{
+ struct btrfs_super_block *sb = fs_info->super_copy;
+ u64 chunk_root_bytenr;
+ u64 generation;
+ int ret;
+
+ btrfs_setup_root(fs_info->chunk_root, fs_info,
+ BTRFS_CHUNK_TREE_OBJECTID);
+
+ ret = btrfs_read_sys_array(fs_info);
+ if (ret)
+ return ret;
+
+ generation = btrfs_super_chunk_root_generation(sb);
+ chunk_root_bytenr = btrfs_super_chunk_root(sb);
+
+ fs_info->chunk_root->node = read_tree_block(fs_info,
+ chunk_root_bytenr,
+ generation);
+ if (!extent_buffer_uptodate(fs_info->chunk_root->node)) {
+ error("cannot read chunk root");
+ return -EIO;
+ }
+
+ ret = btrfs_read_chunk_tree(fs_info);
+ if (ret) {
+ fprintf(stderr, "Couldn't read chunk tree\n");
+ return ret;
+ }
+ return 0;
+}
+
+struct btrfs_fs_info *open_ctree_fs_info(struct blk_desc *desc,
+ struct disk_partition *part)
+{
+ struct btrfs_fs_info *fs_info;
+ struct btrfs_super_block *disk_super;
+ struct btrfs_fs_devices *fs_devices = NULL;
+ struct extent_buffer *eb;
+ int ret;
+
+ fs_info = btrfs_new_fs_info();
+ if (!fs_info) {
+ fprintf(stderr, "Failed to allocate memory for fs_info\n");
+ return NULL;
+ }
+
+ ret = btrfs_scan_fs_devices(desc, part, &fs_devices);
+ if (ret)
+ goto out;
+
+ fs_info->fs_devices = fs_devices;
+
+ ret = btrfs_open_devices(fs_devices);
+ if (ret)
+ goto out;
+
+ disk_super = fs_info->super_copy;
+ ret = btrfs_read_dev_super(desc, part, disk_super);
+ if (ret) {
+ printk("No valid btrfs found\n");
+ goto out_devices;
+ }
+
+ if (btrfs_super_flags(disk_super) & BTRFS_SUPER_FLAG_CHANGING_FSID) {
+ fprintf(stderr, "ERROR: Filesystem UUID change in progress\n");
+ goto out_devices;
+ }
+
+ ASSERT(!memcmp(disk_super->fsid, fs_devices->fsid, BTRFS_FSID_SIZE));
+ if (btrfs_fs_incompat(fs_info, METADATA_UUID))
+ ASSERT(!memcmp(disk_super->metadata_uuid,
+ fs_devices->metadata_uuid, BTRFS_FSID_SIZE));
+
+ fs_info->sectorsize = btrfs_super_sectorsize(disk_super);
+ fs_info->nodesize = btrfs_super_nodesize(disk_super);
+ fs_info->stripesize = btrfs_super_stripesize(disk_super);
+
+ ret = btrfs_check_fs_compatibility(fs_info->super_copy);
+ if (ret)
+ goto out_devices;
+
+ ret = btrfs_setup_chunk_tree_and_device_map(fs_info);
+ if (ret)
+ goto out_chunk;
+
+ /* Chunk tree root is unable to read, return directly */
+ if (!fs_info->chunk_root)
+ return fs_info;
+
+ eb = fs_info->chunk_root->node;
+ read_extent_buffer(eb, fs_info->chunk_tree_uuid,
+ btrfs_header_chunk_tree_uuid(eb),
+ BTRFS_UUID_SIZE);
+
+ ret = btrfs_setup_all_roots(fs_info);
+ if (ret)
+ goto out_chunk;
+
+ return fs_info;
+
+out_chunk:
+ btrfs_release_all_roots(fs_info);
+ btrfs_cleanup_all_caches(fs_info);
+out_devices:
+ btrfs_close_devices(fs_devices);
+out:
+ btrfs_free_fs_info(fs_info);
+ return NULL;
+}
+
+int close_ctree_fs_info(struct btrfs_fs_info *fs_info)
+{
+ int ret;
+ int err = 0;
+
+ free_fs_roots_tree(&fs_info->fs_root_tree);
+
+ btrfs_release_all_roots(fs_info);
+ ret = btrfs_close_devices(fs_info->fs_devices);
+ btrfs_cleanup_all_caches(fs_info);
+ btrfs_free_fs_info(fs_info);
+ if (!err)
+ err = ret;
+ return err;
+}
+
+int btrfs_buffer_uptodate(struct extent_buffer *buf, u64 parent_transid)
+{
+ int ret;
+
+ ret = extent_buffer_uptodate(buf);
+ if (!ret)
+ return ret;
+
+ ret = verify_parent_transid(&buf->fs_info->extent_cache, buf,
+ parent_transid, 1);
+ return !ret;
+}
+
+int btrfs_set_buffer_uptodate(struct extent_buffer *eb)
+{
+ return set_extent_buffer_uptodate(eb);
+}