From 997f9977c24397eb6980bb91e7f9cbc6fe21815a Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Wed, 10 Jan 2018 12:56:47 +0800 Subject: [PATCH] btrfs-progs: mkfs: Prevent temporary system chunk to use space in reserved 1M range When creating btrfs, mkfs.btrfs will firstly create a temporary system chunk as basis, and then created needed trees or new devices. However the layout temporary system chunk is hard-coded and uses reserved [0, 1M) range of devid 1. Change the temporary chunk layout from old: 0 1M 4M 5M |<----------- temp chunk -------------->| And it's 1:1 mapped, which means it's a SINGLE chunk, and stripe offset is also 0. to new layout: 0 1M 4M 5M |<----------- temp chunk -------------->| And still keeps the 1:1 mapping. However this also affects btrfs_min_dev_size() which still assume temporary chunks starts at device offset 0. The problem can only be exposed by "-m single" or "-M" where we reuse the temporary chunk. With other meta profiles, system and meta chunks are allocated by later btrfs_alloc_chunk() call, and old SINGLE chunks are removed, so it will be no such problem for other meta profiles. Reported-by: Nikolay Borisov Signed-off-by: Qu Wenruo Tested-by: Nikolay Borisov Reviewed-by: Nikolay Borisov [ folded fix for the minimal device size calculation ] Signed-off-by: David Sterba --- mkfs/common.c | 34 +++++++++++++++++++++++++++------- mkfs/main.c | 7 ++++++- 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/mkfs/common.c b/mkfs/common.c index dd5e7ecf..8e85942e 100644 --- a/mkfs/common.c +++ b/mkfs/common.c @@ -100,6 +100,21 @@ static int btrfs_create_tree_root(int fd, struct btrfs_mkfs_config *cfg, * * The superblock signature is not valid, denotes a partially created * filesystem, needs to be finalized. + * + * The temporary fs will have the following chunk layout: + * Device extent: + * 0 1M 5M ...... + * | Reserved | dev extent for SYS chunk | + * + * And chunk mapping will be: + * Chunk mapping: + * 0 1M 5M + * | | System chunk, 1:1 mapped | + * + * That's to say, there will only be *ONE* system chunk, mapped to + * [1M, 5M) physical offset. + * And the only chunk is also in logical address [1M, 5M), containing + * all essential tree blocks. */ int make_btrfs(int fd, struct btrfs_mkfs_config *cfg) { @@ -154,8 +169,8 @@ int make_btrfs(int fd, struct btrfs_mkfs_config *cfg) cfg->blocks[MKFS_SUPER_BLOCK] = BTRFS_SUPER_INFO_OFFSET; for (i = 1; i < MKFS_BLOCK_COUNT; i++) { - cfg->blocks[i] = BTRFS_SUPER_INFO_OFFSET + SZ_1M + - cfg->nodesize * i; + cfg->blocks[i] = BTRFS_BLOCK_RESERVED_1M_FOR_SUPER + + cfg->nodesize * (i - 1); } btrfs_set_super_bytenr(&super, cfg->blocks[MKFS_SUPER_BLOCK]); @@ -309,7 +324,7 @@ int make_btrfs(int fd, struct btrfs_mkfs_config *cfg) /* then we have chunk 0 */ btrfs_set_disk_key_objectid(&disk_key, BTRFS_FIRST_CHUNK_TREE_OBJECTID); - btrfs_set_disk_key_offset(&disk_key, 0); + btrfs_set_disk_key_offset(&disk_key, BTRFS_BLOCK_RESERVED_1M_FOR_SUPER); btrfs_set_disk_key_type(&disk_key, BTRFS_CHUNK_ITEM_KEY); btrfs_set_item_key(buf, &disk_key, nritems); btrfs_set_item_offset(buf, btrfs_item_nr(nritems), itemoff); @@ -325,7 +340,8 @@ int make_btrfs(int fd, struct btrfs_mkfs_config *cfg) btrfs_set_chunk_sector_size(buf, chunk, cfg->sectorsize); btrfs_set_chunk_num_stripes(buf, chunk, 1); btrfs_set_stripe_devid_nr(buf, chunk, 0, 1); - btrfs_set_stripe_offset_nr(buf, chunk, 0, 0); + btrfs_set_stripe_offset_nr(buf, chunk, 0, + BTRFS_BLOCK_RESERVED_1M_FOR_SUPER); nritems++; write_extent_buffer(buf, super.dev_item.uuid, @@ -363,7 +379,7 @@ int make_btrfs(int fd, struct btrfs_mkfs_config *cfg) sizeof(struct btrfs_dev_extent); btrfs_set_disk_key_objectid(&disk_key, 1); - btrfs_set_disk_key_offset(&disk_key, 0); + btrfs_set_disk_key_offset(&disk_key, BTRFS_BLOCK_RESERVED_1M_FOR_SUPER); btrfs_set_disk_key_type(&disk_key, BTRFS_DEV_EXTENT_KEY); btrfs_set_item_key(buf, &disk_key, nritems); btrfs_set_item_offset(buf, btrfs_item_nr(nritems), itemoff); @@ -374,7 +390,8 @@ int make_btrfs(int fd, struct btrfs_mkfs_config *cfg) BTRFS_CHUNK_TREE_OBJECTID); btrfs_set_dev_extent_chunk_objectid(buf, dev_extent, BTRFS_FIRST_CHUNK_TREE_OBJECTID); - btrfs_set_dev_extent_chunk_offset(buf, dev_extent, 0); + btrfs_set_dev_extent_chunk_offset(buf, dev_extent, + BTRFS_BLOCK_RESERVED_1M_FOR_SUPER); write_extent_buffer(buf, chunk_tree_uuid, (unsigned long)btrfs_dev_extent_chunk_tree_uuid(dev_extent), @@ -466,6 +483,8 @@ u64 btrfs_min_dev_size(u32 nodesize, int mixed, u64 meta_profile, /* * Minimal size calculation is complex due to several factors: + * 0) Reserved 1M range. + * * 1) Temporary chunk reuse * If specified chunk profile is SINGLE, we can reuse * temporary chunks, no need to allocate new chunks. @@ -484,7 +503,8 @@ u64 btrfs_min_dev_size(u32 nodesize, int mixed, u64 meta_profile, * The latter two are all 8M, accroding to @calc_size of * btrfs_alloc_chunk(). */ - reserved += BTRFS_MKFS_SYSTEM_GROUP_SIZE + SZ_8M * 2; + reserved += BTRFS_BLOCK_RESERVED_1M_FOR_SUPER + + BTRFS_MKFS_SYSTEM_GROUP_SIZE + SZ_8M * 2; /* * For real chunks, we need to select different sizes: diff --git a/mkfs/main.c b/mkfs/main.c index 1a3852e4..9bb1f602 100644 --- a/mkfs/main.c +++ b/mkfs/main.c @@ -66,10 +66,15 @@ static int create_metadata_block_groups(struct btrfs_root *root, int mixed, bytes_used = btrfs_super_bytes_used(fs_info->super_copy); root->fs_info->system_allocs = 1; + /* + * First temporary system chunk must match the chunk layout + * created in make_btrfs(). + */ ret = btrfs_make_block_group(trans, fs_info, bytes_used, BTRFS_BLOCK_GROUP_SYSTEM, BTRFS_FIRST_CHUNK_TREE_OBJECTID, - 0, BTRFS_MKFS_SYSTEM_GROUP_SIZE); + BTRFS_BLOCK_RESERVED_1M_FOR_SUPER, + BTRFS_MKFS_SYSTEM_GROUP_SIZE); allocation->system += BTRFS_MKFS_SYSTEM_GROUP_SIZE; if (ret) return ret;