diff --git a/utils/src/format.h b/utils/src/format.h index c91b4a4b..86a29814 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -236,17 +236,17 @@ struct scoutfs_manifest_btree_val { struct scoutfs_key last_key; } __packed; -#define SCOUTFS_ALLOC_REGION_SHIFT 8 -#define SCOUTFS_ALLOC_REGION_BITS (1 << SCOUTFS_ALLOC_REGION_SHIFT) -#define SCOUTFS_ALLOC_REGION_MASK (SCOUTFS_ALLOC_REGION_BITS - 1) - -struct scoutfs_alloc_region_btree_key { - __be64 index; -} __packed; - -/* The bits need to be aligned so that the hosts can use native long bit ops */ -struct scoutfs_alloc_region_btree_val { - __le64 bits[SCOUTFS_ALLOC_REGION_BITS / 64]; +/* + * Free extents are stored in the server in an allocation btree. The + * type differentiates whether start or length is in stored in the major + * value and is the primary sort key. 'start' is set to the final block + * in the extent so that overlaping queries can be done with next + * instead prev. + */ +struct scoutfs_extent_btree_key { + __u8 type; + __be64 major; + __be64 minor; } __packed; /* @@ -303,7 +303,7 @@ struct scoutfs_segment_block { #define SCOUTFS_INODE_INDEX_DATA_SEQ_TYPE 2 #define SCOUTFS_INODE_INDEX_NR 3 /* don't forget to update */ -/* node zone */ +/* node zone (also used in server alloc btree) */ #define SCOUTFS_FREE_EXTENT_BLKNO_TYPE 1 #define SCOUTFS_FREE_EXTENT_BLOCKS_TYPE 2 @@ -367,9 +367,9 @@ struct scoutfs_super_block { __u8 uuid[SCOUTFS_UUID_BYTES]; __le64 next_ino; __le64 next_seq; - __le64 alloc_uninit; - __le64 total_segs; - __le64 free_segs; + __le64 total_blocks; + __le64 free_blocks; + __le64 alloc_cursor; struct scoutfs_btree_ring bring; __le64 next_seg_seq; struct scoutfs_btree_root alloc_root; @@ -555,18 +555,10 @@ struct scoutfs_net_manifest_entry { __u8 keys[0]; } __packed; -/* XXX I dunno, totally made up */ -#define SCOUTFS_BULK_ALLOC_COUNT 32 - -struct scoutfs_net_segnos { - __le16 nr; - __le64 segnos[0]; -} __packed; - struct scoutfs_net_statfs { - __le64 total_segs; /* total segments in device */ + __le64 total_blocks; /* total blocks in device */ __le64 next_ino; /* next unused inode number */ - __le64 bfree; /* total free small blocks */ + __le64 bfree; /* free blocks */ __u8 uuid[SCOUTFS_UUID_BYTES]; /* logical volume uuid */ } __packed; @@ -582,9 +574,9 @@ struct scoutfs_net_statfs { enum { SCOUTFS_NET_ALLOC_INODES = 0, + SCOUTFS_NET_ALLOC_EXTENT, SCOUTFS_NET_ALLOC_SEGNO, SCOUTFS_NET_RECORD_SEGMENT, - SCOUTFS_NET_BULK_ALLOC, SCOUTFS_NET_ADVANCE_SEQ, SCOUTFS_NET_GET_LAST_SEQ, SCOUTFS_NET_GET_MANIFEST_ROOT, diff --git a/utils/src/mkfs.c b/utils/src/mkfs.c index f01b0e6c..69946449 100644 --- a/utils/src/mkfs.c +++ b/utils/src/mkfs.c @@ -9,6 +9,7 @@ #include #include #include +#include #include "sparse.h" #include "cmd.h" @@ -94,20 +95,27 @@ static u64 calc_btree_blocks(u64 nr, u64 max_key, u64 max_val) /* * Figure out how many btree ring blocks we'll need for all the btree - * items that could be needed to describe this many segments. The - * allocator regions are nice and dense but the manifest entries can be - * absolutely enormous. + * items that could be needed to describe this many segments. + * + * We can have either a free extent or manifest ref for every segment in + * the system. Free extent items are smaller than manifest refs, and + * they merge if they're adjacent, so the largest possible tree is a ref + * for every segment. */ static u64 calc_btree_ring_blocks(u64 total_segs) { u64 blocks; - blocks = calc_btree_blocks(DIV_ROUND_UP(total_segs, - SCOUTFS_ALLOC_REGION_BITS), - sizeof(struct scoutfs_alloc_region_btree_key), - sizeof(struct scoutfs_alloc_region_btree_val)); + /* key is smaller for wider parent fanout */ + assert(sizeof(struct scoutfs_extent_btree_key) <= + sizeof(struct scoutfs_manifest_btree_key)); - blocks += calc_btree_blocks(total_segs, + /* 2 extent items is smaller than a manifest ref */ + assert((2 * sizeof(struct scoutfs_extent_btree_key)) <= + (sizeof(struct scoutfs_manifest_btree_key) + + sizeof(struct scoutfs_manifest_btree_val))); + + blocks = calc_btree_blocks(total_segs, sizeof(struct scoutfs_manifest_btree_key), sizeof(struct scoutfs_manifest_btree_val)); @@ -158,6 +166,7 @@ static int write_new_fs(char *path, int fd) struct scoutfs_segment_block *sblk; struct scoutfs_manifest_btree_key *mkey; struct scoutfs_manifest_btree_val *mval; + struct scoutfs_extent_btree_key *ebk; struct scoutfs_btree_block *bt; struct scoutfs_btree_item *btitem; struct scoutfs_segment_item *item; @@ -170,7 +179,10 @@ static int write_new_fs(char *path, int fd) u64 size; u64 ring_blocks; u64 total_segs; + u64 total_blocks; u64 first_segno; + u64 free_start; + u64 free_len; int ret; u64 i; @@ -202,6 +214,7 @@ static int write_new_fs(char *path, int fd) } total_segs = size / SCOUTFS_SEGMENT_SIZE; + total_blocks = size / SCOUTFS_BLOCK_SIZE; /* partially initialize the super so we can use it to init others */ memset(super, 0, SCOUTFS_BLOCK_SIZE); @@ -212,7 +225,7 @@ static int write_new_fs(char *path, int fd) uuid_generate(super->uuid); super->next_ino = cpu_to_le64(SCOUTFS_ROOT_INO + 1); super->next_seq = cpu_to_le64(1); - super->total_segs = cpu_to_le64(total_segs); + super->total_blocks = cpu_to_le64(total_blocks); super->next_seg_seq = cpu_to_le64(2); /* align the btree ring to the segment after the supers */ @@ -221,16 +234,57 @@ static int write_new_fs(char *path, int fd) /* first usable segno follows manifest ring */ ring_blocks = calc_btree_ring_blocks(total_segs); first_segno = (blkno + ring_blocks) / SCOUTFS_SEGMENT_BLOCKS; + free_start = ((first_segno + 1) << SCOUTFS_SEGMENT_BLOCK_SHIFT); + free_len = total_blocks - free_start; + super->free_blocks = cpu_to_le64(free_len); super->bring.first_blkno = cpu_to_le64(blkno); super->bring.nr_blocks = cpu_to_le64(ring_blocks); - super->bring.next_block = cpu_to_le64(1); + super->bring.next_block = cpu_to_le64(2); super->bring.next_seq = cpu_to_le64(2); - /* allocator btree is empty, allocations start from super fields */ - super->alloc_root.ref.blkno = cpu_to_le64(0); - super->alloc_root.ref.seq = cpu_to_le64(0); - super->alloc_root.height = 0; + /* allocator btree has item with space after first segno */ + super->alloc_root.ref.blkno = cpu_to_le64(blkno); + super->alloc_root.ref.seq = cpu_to_le64(1); + super->alloc_root.height = 1; + + memset(bt, 0, SCOUTFS_BLOCK_SIZE); + bt->fsid = super->hdr.fsid; + bt->blkno = cpu_to_le64(blkno); + bt->seq = cpu_to_le64(1); + bt->nr_items = cpu_to_le16(2); + + /* btree item allocated from the back of the block */ + ebk = (void *)bt + SCOUTFS_BLOCK_SIZE - sizeof(*ebk); + btitem = (void *)ebk - sizeof(*btitem); + + bt->item_hdrs[0].off = cpu_to_le16((long)btitem - (long)bt); + bt->free_end = bt->item_hdrs[0].off; + btitem->key_len = cpu_to_le16(sizeof(*ebk)); + btitem->val_len = cpu_to_le16(0); + + ebk->type = SCOUTFS_FREE_EXTENT_BLKNO_TYPE; + ebk->major = cpu_to_be64(free_start + free_len - 1); + ebk->minor = cpu_to_be64(free_len); + + ebk = (void *)btitem - sizeof(*ebk); + btitem = (void *)ebk - sizeof(*btitem); + + bt->item_hdrs[1].off = cpu_to_le16((long)btitem - (long)bt); + bt->free_end = bt->item_hdrs[1].off; + btitem->key_len = cpu_to_le16(sizeof(*ebk)); + btitem->val_len = cpu_to_le16(0); + + ebk->type = SCOUTFS_FREE_EXTENT_BLOCKS_TYPE; + ebk->major = cpu_to_be64(free_len); + ebk->minor = cpu_to_be64(free_start + free_len - 1); + + bt->crc = cpu_to_le32(crc_btree_block(bt)); + + ret = write_raw_block(fd, blkno, bt); + if (ret) + goto out; + blkno++; /* manifest btree has a block with an item for the segment */ super->manifest.root.ref.blkno = cpu_to_le64(blkno); @@ -276,10 +330,6 @@ static int write_new_fs(char *path, int fd) goto out; blkno += ring_blocks; - /* alloc from uninit, don't need regions yet */ - super->alloc_uninit = cpu_to_le64(first_segno + 1); - super->free_segs = cpu_to_le64(total_segs - (first_segno + 1)); - /* write seg with root inode */ sblk->segno = cpu_to_le64(first_segno); sblk->seq = cpu_to_le64(1); @@ -359,17 +409,19 @@ static int write_new_fs(char *path, int fd) " format hash: %llx\n" " uuid: %s\n" " device bytes: "SIZE_FMT"\n" + " device blocks: "SIZE_FMT"\n" " btree ring blocks: "SIZE_FMT"\n" - " usable segments: "SIZE_FMT"\n", + " free blocks: "SIZE_FMT"\n", path, le64_to_cpu(super->hdr.fsid), le64_to_cpu(super->format_hash), uuid_str, SIZE_ARGS(size, 1), + SIZE_ARGS(total_blocks, SCOUTFS_BLOCK_SIZE), SIZE_ARGS(le64_to_cpu(super->bring.nr_blocks), SCOUTFS_BLOCK_SIZE), - SIZE_ARGS(le64_to_cpu(super->free_segs) + 1, - SCOUTFS_SEGMENT_SIZE)); + SIZE_ARGS(le64_to_cpu(super->free_blocks), + SCOUTFS_BLOCK_SIZE)); ret = 0; out: diff --git a/utils/src/print.c b/utils/src/print.c index 10dc7122..7df3f46b 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -317,23 +317,24 @@ static int print_manifest_entry(void *key, unsigned key_len, void *val, return 0; } -static int print_alloc_region(void *key, unsigned key_len, void *val, - unsigned val_len, void *arg) +static int print_alloc_item(void *key, unsigned key_len, void *val, + unsigned val_len, void *arg) { - struct scoutfs_alloc_region_btree_key *reg_key = key; - struct scoutfs_alloc_region_btree_val *reg_val = val; - int i; + struct scoutfs_extent_btree_key *ebk = key; + u64 start; + u64 len; /* XXX check sizes */ - printf(" index %llu bits", be64_to_cpu(reg_key->index)); + len = be64_to_cpu(ebk->minor); + start = be64_to_cpu(ebk->major); + if (ebk->type == SCOUTFS_FREE_EXTENT_BLOCKS_TYPE) + swap(start, len); + start -= len - 1; - if (val == NULL) - return 0; - - for (i = 0; i < array_size(reg_val->bits); i++) - printf(" %016llx", le64_to_cpu(reg_val->bits[i])); - printf("\n"); + printf(" type %u major %llu minor %llu (start %llu len %llu)\n", + ebk->type, be64_to_cpu(ebk->major), + be64_to_cpu(ebk->minor), start, len); return 0; } @@ -456,7 +457,7 @@ static void print_super_block(struct scoutfs_super_block *super, u64 blkno) /* XXX these are all in a crazy order */ printf(" next_ino %llu next_seq %llu next_seg_seq %llu\n" - " alloc_uninit %llu total_segs %llu free_segs %llu\n" + " total_blocks %llu free_blocks %llu alloc_cursor %llu\n" " btree ring: first_blkno %llu nr_blocks %llu next_block %llu " "next_seq %llu\n" " alloc btree root: height %u blkno %llu seq %llu mig_len %u\n" @@ -464,9 +465,9 @@ static void print_super_block(struct scoutfs_super_block *super, u64 blkno) le64_to_cpu(super->next_ino), le64_to_cpu(super->next_seq), le64_to_cpu(super->next_seg_seq), - le64_to_cpu(super->alloc_uninit), - le64_to_cpu(super->total_segs), - le64_to_cpu(super->free_segs), + le64_to_cpu(super->total_blocks), + le64_to_cpu(super->free_blocks), + le64_to_cpu(super->alloc_cursor), le64_to_cpu(super->bring.first_blkno), le64_to_cpu(super->bring.nr_blocks), le64_to_cpu(super->bring.next_block), @@ -494,6 +495,7 @@ static int print_super_blocks(int fd) struct scoutfs_super_block *super; struct scoutfs_super_block recent = { .hdr.seq = 0 }; unsigned long *seg_map; + u64 nr_segs; int ret = 0; int err; int i; @@ -516,24 +518,24 @@ static int print_super_blocks(int fd) print_super_block(super, SCOUTFS_SUPER_BLKNO + r); - seg_map = alloc_bits(le64_to_cpu(super->total_segs)); + nr_segs = le64_to_cpu(super->total_blocks) / SCOUTFS_SEGMENT_BLOCKS; + seg_map = alloc_bits(nr_segs); if (!seg_map) { ret = -ENOMEM; fprintf(stderr, "failed to alloc %llu seg map: %s (%d)\n", - le64_to_cpu(super->total_segs), - strerror(errno), errno); + nr_segs, strerror(errno), errno); return ret; } ret = print_btree(fd, super, "alloc", &super->alloc_root, - print_alloc_region, NULL); + print_alloc_item, NULL); err = print_btree(fd, super, "manifest", &super->manifest.root, print_manifest_entry, seg_map); if (err && !ret) ret = err; - err = print_segments(fd, seg_map, le64_to_cpu(super->total_segs)); + err = print_segments(fd, seg_map, nr_segs); if (err && !ret) ret = err;