diff --git a/mkfs/common.c b/mkfs/common.c index 6a243c8d..728f407e 100644 --- a/mkfs/common.c +++ b/mkfs/common.c @@ -37,6 +37,7 @@ static u64 reference_root_table[] = { [MKFS_DEV_TREE] = BTRFS_DEV_TREE_OBJECTID, [MKFS_FS_TREE] = BTRFS_FS_TREE_OBJECTID, [MKFS_CSUM_TREE] = BTRFS_CSUM_TREE_OBJECTID, + [MKFS_FREE_SPACE_TREE] = BTRFS_FREE_SPACE_TREE_OBJECTID, }; static int btrfs_write_empty_tree(int fd, struct btrfs_mkfs_config *cfg, @@ -139,6 +140,52 @@ static int btrfs_create_tree_root(int fd, struct btrfs_mkfs_config *cfg, return ret; } +static int create_free_space_tree(int fd, struct btrfs_mkfs_config *cfg, + struct extent_buffer *buf, u64 group_start, + u64 group_size, u64 free_start) +{ + struct btrfs_free_space_info *info; + struct btrfs_disk_key disk_key; + int itemoff = __BTRFS_LEAF_DATA_SIZE(cfg->nodesize); + int nritems = 0; + int ret; + + memset(buf->data + sizeof(struct btrfs_header), 0, + cfg->nodesize - sizeof(struct btrfs_header)); + itemoff -= sizeof(*info); + + btrfs_set_disk_key_objectid(&disk_key, group_start); + btrfs_set_disk_key_offset(&disk_key, group_size); + btrfs_set_disk_key_type(&disk_key, BTRFS_FREE_SPACE_INFO_KEY); + btrfs_set_item_key(buf, &disk_key, nritems); + btrfs_set_item_offset(buf, btrfs_item_nr(nritems), itemoff); + btrfs_set_item_size(buf, btrfs_item_nr(nritems), sizeof(*info)); + + info = btrfs_item_ptr(buf, nritems, struct btrfs_free_space_info); + btrfs_set_free_space_extent_count(buf, info, 1); + btrfs_set_free_space_flags(buf, info, 0); + + nritems++; + btrfs_set_disk_key_objectid(&disk_key, free_start); + btrfs_set_disk_key_offset(&disk_key, group_start + group_size - free_start); + btrfs_set_disk_key_type(&disk_key, BTRFS_FREE_SPACE_EXTENT_KEY); + btrfs_set_item_key(buf, &disk_key, nritems); + btrfs_set_item_offset(buf, btrfs_item_nr(nritems), itemoff); + btrfs_set_item_size(buf, btrfs_item_nr(nritems), 0); + + nritems++; + btrfs_set_header_bytenr(buf, cfg->blocks[MKFS_FREE_SPACE_TREE]); + btrfs_set_header_owner(buf, BTRFS_FREE_SPACE_TREE_OBJECTID); + btrfs_set_header_nritems(buf, nritems); + csum_tree_block_size(buf, btrfs_csum_type_size(cfg->csum_type), 0, + cfg->csum_type); + ret = pwrite(fd, buf->data, cfg->nodesize, + cfg->blocks[MKFS_FREE_SPACE_TREE]); + if (ret != cfg->nodesize) + return ret < 0 ? -errno : -EIO; + return 0; +} + /* * @fs_uuid - if NULL, generates a UUID, returns back the new filesystem UUID * @@ -189,6 +236,12 @@ int make_btrfs(int fd, struct btrfs_mkfs_config *cfg) u64 system_group_offset = BTRFS_BLOCK_RESERVED_1M_FOR_SUPER; u64 system_group_size = BTRFS_MKFS_SYSTEM_GROUP_SIZE; bool add_block_group = true; + bool free_space_tree = !!(cfg->runtime_features & + BTRFS_RUNTIME_FEATURE_FREE_SPACE_TREE); + + /* Don't include the free space tree in the blocks to process. */ + if (!free_space_tree) + blocks_nr--; if ((cfg->features & BTRFS_FEATURE_INCOMPAT_ZONED)) { system_group_offset = cfg->zone_size * BTRFS_NR_SB_LOG_ZONES; @@ -248,6 +301,12 @@ int make_btrfs(int fd, struct btrfs_mkfs_config *cfg) else btrfs_set_super_cache_generation(&super, -1); btrfs_set_super_incompat_flags(&super, cfg->features); + if (free_space_tree) { + u64 ro_flags = BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE | + BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE_VALID; + + btrfs_set_super_compat_ro_flags(&super, ro_flags); + } if (cfg->label) __strncpy_null(super.label, cfg->label, BTRFS_LABEL_SIZE - 1); @@ -507,6 +566,14 @@ int make_btrfs(int fd, struct btrfs_mkfs_config *cfg) if (ret) goto out; + if (free_space_tree) { + ret = create_free_space_tree(fd, cfg, buf, system_group_offset, + system_group_size, + system_group_offset + total_used); + if (ret) + goto out; + } + /* and write out the super block */ memset(buf->data, 0, BTRFS_SUPER_INFO_SIZE); memcpy(buf->data, &super, sizeof(super)); diff --git a/mkfs/common.h b/mkfs/common.h index f2d28057..f31b4ae4 100644 --- a/mkfs/common.h +++ b/mkfs/common.h @@ -51,6 +51,7 @@ enum btrfs_mkfs_block { MKFS_DEV_TREE, MKFS_FS_TREE, MKFS_CSUM_TREE, + MKFS_FREE_SPACE_TREE, MKFS_BLOCK_COUNT }; @@ -61,6 +62,12 @@ static const enum btrfs_mkfs_block extent_tree_v1_blocks[] = { MKFS_DEV_TREE, MKFS_FS_TREE, MKFS_CSUM_TREE, + + /* + * Since the free space tree is optional with v1 it must always be last + * in this array. + */ + MKFS_FREE_SPACE_TREE, }; struct btrfs_mkfs_config { @@ -72,6 +79,8 @@ struct btrfs_mkfs_config { u32 stripesize; /* Bitfield of incompat features, BTRFS_FEATURE_INCOMPAT_* */ u64 features; + /* Bitfield of BTRFS_RUNTIME_FEATURE_* */ + u64 runtime_features; /* Size of the filesystem in bytes */ u64 num_bytes; /* checksum algorithm to use */ diff --git a/mkfs/main.c b/mkfs/main.c index 6582c03b..bf274ea3 100644 --- a/mkfs/main.c +++ b/mkfs/main.c @@ -135,7 +135,6 @@ static int create_metadata_block_groups(struct btrfs_root *root, int mixed, root->fs_info->system_allocs = 0; ret = btrfs_commit_transaction(trans, root); - err: return ret; } @@ -252,7 +251,9 @@ static int recow_roots(struct btrfs_trans_handle *trans, ret = __recow_root(trans, info->csum_root); if (ret) return ret; - + ret = __recow_root(trans, info->free_space_root); + if (ret) + return ret; return 0; } @@ -1364,6 +1365,7 @@ int BOX_MAIN(mkfs)(int argc, char **argv) mkfs_cfg.sectorsize = sectorsize; mkfs_cfg.stripesize = stripesize; mkfs_cfg.features = features; + mkfs_cfg.runtime_features = runtime_features; mkfs_cfg.csum_type = csum_type; mkfs_cfg.zone_size = zone_size(file); @@ -1527,13 +1529,6 @@ raid_groups: goto out; } } - if (runtime_features & BTRFS_RUNTIME_FEATURE_FREE_SPACE_TREE) { - ret = btrfs_create_free_space_tree(fs_info); - if (ret < 0) { - error("failed to create free space tree: %d (%m)", ret); - goto out; - } - } if (verbose) { char features_buf[64];