fs: btrfs: Crossport btrfs_read_sys_array() and btrfs_read_chunk_tree()
authorQu Wenruo <wqu@suse.com>
Wed, 24 Jun 2020 16:03:00 +0000 (18:03 +0200)
committerTom Rini <trini@konsulko.com>
Tue, 8 Sep 2020 00:57:27 +0000 (20:57 -0400)
These two functions play a big role in btrfs bootstrap.

The following function is removed:
- Seed device support

Although in theory we can still support multiple devices, we don't have
a facility in U-Boot to do device scan without opening them.

Signed-off-by: Qu Wenruo <wqu@suse.com>
Reviewed-by: Marek BehĂșn <marek.behun@nic.cz>
fs/btrfs/btrfs.c
fs/btrfs/btrfs.h
fs/btrfs/chunk-map.c
fs/btrfs/volumes.c
fs/btrfs/volumes.h

index b4535c9..f5266f1 100644 (file)
@@ -100,7 +100,7 @@ int btrfs_probe(struct blk_desc *fs_dev_desc,
        btrfs_info.chunk_root.objectid = 0;
        btrfs_info.chunk_root.bytenr = btrfs_info.sb.chunk_root;
 
-       if (btrfs_read_chunk_tree()) {
+       if (__btrfs_read_chunk_tree()) {
                printf("%s: failed to read chunk tree\n", __func__);
                return -1;
        }
index ae29226..7dfdaf7 100644 (file)
@@ -33,7 +33,7 @@ int btrfs_devread(u64, int, void *);
 u64 btrfs_map_logical_to_physical(u64);
 int btrfs_chunk_map_init(void);
 void btrfs_chunk_map_exit(void);
-int btrfs_read_chunk_tree(void);
+int __btrfs_read_chunk_tree(void);
 
 /* compression.c */
 u32 btrfs_decompress(u8 type, const char *, u32, char *, u32);
index 8e3d13c..bff83e4 100644 (file)
@@ -142,7 +142,7 @@ int btrfs_chunk_map_init(void)
        return 0;
 }
 
-int btrfs_read_chunk_tree(void)
+int __btrfs_read_chunk_tree(void)
 {
        struct __btrfs_path path;
        struct btrfs_key key, *found_key;
index c671b53..fcf52d4 100644 (file)
@@ -5,6 +5,7 @@
 #include "ctree.h"
 #include "disk-io.h"
 #include "volumes.h"
+#include "extent-io.h"
 
 const struct btrfs_raid_attr btrfs_raid_array[BTRFS_NR_RAID_TYPES] = {
        [BTRFS_RAID_RAID10] = {
@@ -369,6 +370,14 @@ struct btrfs_device *btrfs_find_device(struct btrfs_fs_info *fs_info, u64 devid,
        return NULL;
 }
 
+static struct btrfs_device *fill_missing_device(u64 devid)
+{
+       struct btrfs_device *device;
+
+       device = kzalloc(sizeof(*device), GFP_NOFS);
+       return device;
+}
+
 /*
  * slot == -1: SYSTEM chunk
  * return -EIO on error, otherwise return 0
@@ -498,6 +507,298 @@ int btrfs_check_chunk_valid(struct btrfs_fs_info *fs_info,
        return 0;
 }
 
+/*
+ * Slot is used to verify the chunk item is valid
+ *
+ * For sys chunk in superblock, pass -1 to indicate sys chunk.
+ */
+static int read_one_chunk(struct btrfs_fs_info *fs_info, struct btrfs_key *key,
+                         struct extent_buffer *leaf,
+                         struct btrfs_chunk *chunk, int slot)
+{
+       struct btrfs_mapping_tree *map_tree = &fs_info->mapping_tree;
+       struct map_lookup *map;
+       struct cache_extent *ce;
+       u64 logical;
+       u64 length;
+       u64 devid;
+       u8 uuid[BTRFS_UUID_SIZE];
+       int num_stripes;
+       int ret;
+       int i;
+
+       logical = key->offset;
+       length = btrfs_chunk_length(leaf, chunk);
+       num_stripes = btrfs_chunk_num_stripes(leaf, chunk);
+       /* Validation check */
+       ret = btrfs_check_chunk_valid(fs_info, leaf, chunk, slot, logical);
+       if (ret) {
+               error("%s checksums match, but it has an invalid chunk, %s",
+                     (slot == -1) ? "Superblock" : "Metadata",
+                     (slot == -1) ? "try btrfsck --repair -s <superblock> ie, 0,1,2" : "");
+               return ret;
+       }
+
+       ce = search_cache_extent(&map_tree->cache_tree, logical);
+
+       /* already mapped? */
+       if (ce && ce->start <= logical && ce->start + ce->size > logical) {
+               return 0;
+       }
+
+       map = kmalloc(btrfs_map_lookup_size(num_stripes), GFP_NOFS);
+       if (!map)
+               return -ENOMEM;
+
+       map->ce.start = logical;
+       map->ce.size = length;
+       map->num_stripes = num_stripes;
+       map->io_width = btrfs_chunk_io_width(leaf, chunk);
+       map->io_align = btrfs_chunk_io_align(leaf, chunk);
+       map->sector_size = btrfs_chunk_sector_size(leaf, chunk);
+       map->stripe_len = btrfs_chunk_stripe_len(leaf, chunk);
+       map->type = btrfs_chunk_type(leaf, chunk);
+       map->sub_stripes = btrfs_chunk_sub_stripes(leaf, chunk);
+
+       for (i = 0; i < num_stripes; i++) {
+               map->stripes[i].physical =
+                       btrfs_stripe_offset_nr(leaf, chunk, i);
+               devid = btrfs_stripe_devid_nr(leaf, chunk, i);
+               read_extent_buffer(leaf, uuid, (unsigned long)
+                                  btrfs_stripe_dev_uuid_nr(chunk, i),
+                                  BTRFS_UUID_SIZE);
+               map->stripes[i].dev = btrfs_find_device(fs_info, devid, uuid,
+                                                       NULL);
+               if (!map->stripes[i].dev) {
+                       map->stripes[i].dev = fill_missing_device(devid);
+                       printf("warning, device %llu is missing\n",
+                              (unsigned long long)devid);
+                       list_add(&map->stripes[i].dev->dev_list,
+                                &fs_info->fs_devices->devices);
+               }
+
+       }
+       ret = insert_cache_extent(&map_tree->cache_tree, &map->ce);
+       if (ret < 0) {
+               errno = -ret;
+               error("failed to add chunk map start=%llu len=%llu: %d (%m)",
+                     map->ce.start, map->ce.size, ret);
+       }
+
+       return ret;
+}
+
+static int fill_device_from_item(struct extent_buffer *leaf,
+                                struct btrfs_dev_item *dev_item,
+                                struct btrfs_device *device)
+{
+       unsigned long ptr;
+
+       device->devid = btrfs_device_id(leaf, dev_item);
+       device->total_bytes = btrfs_device_total_bytes(leaf, dev_item);
+       device->bytes_used = btrfs_device_bytes_used(leaf, dev_item);
+       device->type = btrfs_device_type(leaf, dev_item);
+       device->io_align = btrfs_device_io_align(leaf, dev_item);
+       device->io_width = btrfs_device_io_width(leaf, dev_item);
+       device->sector_size = btrfs_device_sector_size(leaf, dev_item);
+
+       ptr = (unsigned long)btrfs_device_uuid(dev_item);
+       read_extent_buffer(leaf, device->uuid, ptr, BTRFS_UUID_SIZE);
+
+       return 0;
+}
+
+static int read_one_dev(struct btrfs_fs_info *fs_info,
+                       struct extent_buffer *leaf,
+                       struct btrfs_dev_item *dev_item)
+{
+       struct btrfs_device *device;
+       u64 devid;
+       int ret = 0;
+       u8 fs_uuid[BTRFS_UUID_SIZE];
+       u8 dev_uuid[BTRFS_UUID_SIZE];
+
+       devid = btrfs_device_id(leaf, dev_item);
+       read_extent_buffer(leaf, dev_uuid,
+                          (unsigned long)btrfs_device_uuid(dev_item),
+                          BTRFS_UUID_SIZE);
+       read_extent_buffer(leaf, fs_uuid,
+                          (unsigned long)btrfs_device_fsid(dev_item),
+                          BTRFS_FSID_SIZE);
+
+       if (memcmp(fs_uuid, fs_info->fs_devices->fsid, BTRFS_UUID_SIZE)) {
+               error("Seed device is not yet supported\n");
+               return -ENOTSUPP;
+       }
+
+       device = btrfs_find_device(fs_info, devid, dev_uuid, fs_uuid);
+       if (!device) {
+               device = kzalloc(sizeof(*device), GFP_NOFS);
+               if (!device)
+                       return -ENOMEM;
+               list_add(&device->dev_list,
+                        &fs_info->fs_devices->devices);
+       }
+
+       fill_device_from_item(leaf, dev_item, device);
+       fs_info->fs_devices->total_rw_bytes +=
+               btrfs_device_total_bytes(leaf, dev_item);
+       return ret;
+}
+
+int btrfs_read_sys_array(struct btrfs_fs_info *fs_info)
+{
+       struct btrfs_super_block *super_copy = fs_info->super_copy;
+       struct extent_buffer *sb;
+       struct btrfs_disk_key *disk_key;
+       struct btrfs_chunk *chunk;
+       u8 *array_ptr;
+       unsigned long sb_array_offset;
+       int ret = 0;
+       u32 num_stripes;
+       u32 array_size;
+       u32 len = 0;
+       u32 cur_offset;
+       struct btrfs_key key;
+
+       if (fs_info->nodesize < BTRFS_SUPER_INFO_SIZE) {
+               printf("ERROR: nodesize %u too small to read superblock\n",
+                               fs_info->nodesize);
+               return -EINVAL;
+       }
+       sb = alloc_dummy_extent_buffer(fs_info, BTRFS_SUPER_INFO_OFFSET,
+                                      BTRFS_SUPER_INFO_SIZE);
+       if (!sb)
+               return -ENOMEM;
+       btrfs_set_buffer_uptodate(sb);
+       write_extent_buffer(sb, super_copy, 0, sizeof(*super_copy));
+       array_size = btrfs_super_sys_array_size(super_copy);
+
+       array_ptr = super_copy->sys_chunk_array;
+       sb_array_offset = offsetof(struct btrfs_super_block, sys_chunk_array);
+       cur_offset = 0;
+
+       while (cur_offset < array_size) {
+               disk_key = (struct btrfs_disk_key *)array_ptr;
+               len = sizeof(*disk_key);
+               if (cur_offset + len > array_size)
+                       goto out_short_read;
+
+               btrfs_disk_key_to_cpu(&key, disk_key);
+
+               array_ptr += len;
+               sb_array_offset += len;
+               cur_offset += len;
+
+               if (key.type == BTRFS_CHUNK_ITEM_KEY) {
+                       chunk = (struct btrfs_chunk *)sb_array_offset;
+                       /*
+                        * At least one btrfs_chunk with one stripe must be
+                        * present, exact stripe count check comes afterwards
+                        */
+                       len = btrfs_chunk_item_size(1);
+                       if (cur_offset + len > array_size)
+                               goto out_short_read;
+
+                       num_stripes = btrfs_chunk_num_stripes(sb, chunk);
+                       if (!num_stripes) {
+                               printk(
+           "ERROR: invalid number of stripes %u in sys_array at offset %u\n",
+                                       num_stripes, cur_offset);
+                               ret = -EIO;
+                               break;
+                       }
+
+                       len = btrfs_chunk_item_size(num_stripes);
+                       if (cur_offset + len > array_size)
+                               goto out_short_read;
+
+                       ret = read_one_chunk(fs_info, &key, sb, chunk, -1);
+                       if (ret)
+                               break;
+               } else {
+                       printk(
+               "ERROR: unexpected item type %u in sys_array at offset %u\n",
+                               (u32)key.type, cur_offset);
+                       ret = -EIO;
+                       break;
+               }
+               array_ptr += len;
+               sb_array_offset += len;
+               cur_offset += len;
+       }
+       free_extent_buffer(sb);
+       return ret;
+
+out_short_read:
+       printk("ERROR: sys_array too short to read %u bytes at offset %u\n",
+                       len, cur_offset);
+       free_extent_buffer(sb);
+       return -EIO;
+}
+
+int btrfs_read_chunk_tree(struct btrfs_fs_info *fs_info)
+{
+       struct btrfs_path *path;
+       struct extent_buffer *leaf;
+       struct btrfs_key key;
+       struct btrfs_key found_key;
+       struct btrfs_root *root = fs_info->chunk_root;
+       int ret;
+       int slot;
+
+       path = btrfs_alloc_path();
+       if (!path)
+               return -ENOMEM;
+
+       /*
+        * Read all device items, and then all the chunk items. All
+        * device items are found before any chunk item (their object id
+        * is smaller than the lowest possible object id for a chunk
+        * item - BTRFS_FIRST_CHUNK_TREE_OBJECTID).
+        */
+       key.objectid = BTRFS_DEV_ITEMS_OBJECTID;
+       key.offset = 0;
+       key.type = 0;
+       ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
+       if (ret < 0)
+               goto error;
+       while(1) {
+               leaf = path->nodes[0];
+               slot = path->slots[0];
+               if (slot >= btrfs_header_nritems(leaf)) {
+                       ret = btrfs_next_leaf(root, path);
+                       if (ret == 0)
+                               continue;
+                       if (ret < 0)
+                               goto error;
+                       break;
+               }
+               btrfs_item_key_to_cpu(leaf, &found_key, slot);
+               if (found_key.type == BTRFS_DEV_ITEM_KEY) {
+                       struct btrfs_dev_item *dev_item;
+                       dev_item = btrfs_item_ptr(leaf, slot,
+                                                 struct btrfs_dev_item);
+                       ret = read_one_dev(fs_info, leaf, dev_item);
+                       if (ret < 0)
+                               goto error;
+               } else if (found_key.type == BTRFS_CHUNK_ITEM_KEY) {
+                       struct btrfs_chunk *chunk;
+                       chunk = btrfs_item_ptr(leaf, slot, struct btrfs_chunk);
+                       ret = read_one_chunk(fs_info, &found_key, leaf, chunk,
+                                            slot);
+                       if (ret < 0)
+                               goto error;
+               }
+               path->slots[0]++;
+       }
+
+       ret = 0;
+error:
+       btrfs_free_path(path);
+       return ret;
+}
+
 /*
  * Get stripe length from chunk item and its stripe items
  *
index 32e3938..9d1a07a 100644 (file)
@@ -182,6 +182,8 @@ static inline int btrfs_next_bg_system(struct btrfs_fs_info *fs_info,
        return btrfs_next_bg(fs_info, logical, size,
                        BTRFS_BLOCK_GROUP_SYSTEM);
 }
+int btrfs_read_sys_array(struct btrfs_fs_info *fs_info);
+int btrfs_read_chunk_tree(struct btrfs_fs_info *fs_info);
 int btrfs_open_devices(struct btrfs_fs_devices *fs_devices);
 int btrfs_close_devices(struct btrfs_fs_devices *fs_devices);
 void btrfs_close_all_devices(void);