diff --git a/utils/src/format.h b/utils/src/format.h index a80e95b0..6f80059e 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -19,6 +19,7 @@ */ #define SCOUTFS_SUPER_BLKNO ((64 * 1024) >> SCOUTFS_BLOCK_SHIFT) #define SCOUTFS_SUPER_NR 2 +#define SCOUTFS_BUDDY_BLKNO (SCOUTFS_SUPER_BLKNO + SCOUTFS_SUPER_NR) /* * This header is found at the start of every block so that we can @@ -102,6 +103,38 @@ struct scoutfs_btree_item { #define SCOUTFS_UUID_BYTES 16 +/* + * Arbitrarily choose a reasonably fine grained 64byte chunk. This is a + * balance between write amplification of writing chunks with a single + * modified bit, storage overhead of partial blocks losing a chunk to + * make room for the block header and having a pos field per chunk, and + * runtime memory overhead of a bit per chunk. + */ +#define SCOUTFS_BUDDY_CHUNK_LE64S 8 +#define SCOUTFS_BUDDY_CHUNK_BYTES (SCOUTFS_BUDDY_CHUNK_LE64S * 8) +#define SCOUTFS_BUDDY_CHUNK_BITS (SCOUTFS_BUDDY_CHUNK_BYTES * 8) + +/* + * After the pair of super blocks are a preallocated ring of blocks + * which record modified regions of the buddy bitmap allocator. + * + * The seq's header needs to match the unwrapped ring index of the + * block. + */ +struct scoutfs_buddy_block { + struct scoutfs_block_header hdr; + u8 nr_chunks; + struct scoutfs_buddy_chunk { + __le32 pos; + __le64 bits[SCOUTFS_BUDDY_CHUNK_LE64S]; + } __packed chunks[0]; +} __packed; + +#define SCOUTFS_BUDDY_CHUNKS_PER_BLOCK \ + ((SCOUTFS_BLOCK_SIZE - offsetof(struct scoutfs_buddy_block, chunks)) /\ + SCOUTFS_BUDDY_CHUNK_BYTES) + + /* * The super is stored in a pair of blocks in the first chunk on the * device. @@ -116,6 +149,11 @@ struct scoutfs_super_block { struct scoutfs_block_header hdr; __le64 id; __u8 uuid[SCOUTFS_UUID_BYTES]; + __le64 total_blocks; + __le32 buddy_blocks; + __le32 buddy_sweep_bit; + __le64 buddy_head; + __le64 buddy_tail; struct scoutfs_btree_root btree_root; } __packed; diff --git a/utils/src/mkfs.c b/utils/src/mkfs.c index ac0c196e..116e8abf 100644 --- a/utils/src/mkfs.c +++ b/utils/src/mkfs.c @@ -39,6 +39,24 @@ static int write_block(int fd, u64 blkno, struct scoutfs_block_header *hdr) return 0; } +/* + * Calculate the number of buddy blocks that are needed to track the + * allocation of a device with the given byte size. We need an even + * number of buddy blocks that contain 8 bits for every device block. This + * is a bit overly conservative in that it doesn't subtract the buddy + * blocks and super block from the calculation. + */ +static u32 calc_buddy_blocks(u64 total_blocks) +{ + u64 buddy_bits = total_blocks * 8; + u64 chunks = DIV_ROUND_UP(buddy_bits, SCOUTFS_BUDDY_CHUNK_BITS); + u64 blocks = DIV_ROUND_UP(chunks, SCOUTFS_BUDDY_CHUNKS_PER_BLOCK); + + /* XXX check u32 overflow? */ + + return round_up(blocks, 2); +} + static int write_new_fs(char *path, int fd) { struct scoutfs_super_block *super; @@ -51,6 +69,8 @@ static int write_new_fs(char *path, int fd) unsigned int i; u64 size; u64 blkno; + u64 total_blocks; + u64 buddy_blocks; void *buf; int ret; @@ -72,6 +92,15 @@ static int write_new_fs(char *path, int fd) goto out; } + /* the block limit is totally arbitrary */ + total_blocks = size / SCOUTFS_BLOCK_SIZE; + if (total_blocks < 32) { + fprintf(stderr, "%llu byte device only has room for %llu %u byte blocks, needs at least 32 blocks\n", + size, total_blocks, SCOUTFS_BLOCK_SIZE); + goto out; + } + buddy_blocks = calc_buddy_blocks(total_blocks); + root_key.inode = cpu_to_le64(SCOUTFS_ROOT_INO); root_key.type = SCOUTFS_INODE_KEY; root_key.offset = 0; @@ -85,6 +114,8 @@ static int write_new_fs(char *path, int fd) super->hdr.seq = cpu_to_le64(1); super->id = cpu_to_le64(SCOUTFS_SUPER_ID); uuid_generate(super->uuid); + super->total_blocks = cpu_to_le64(total_blocks); + super->buddy_blocks = cpu_to_le32(buddy_blocks); /* write a btree leaf root inode item */ memset(buf, 0, SCOUTFS_BLOCK_SIZE); @@ -142,10 +173,12 @@ static int write_new_fs(char *path, int fd) uuid_unparse(super->uuid, uuid_str); printf("Created scoutfs filesystem:\n" - " block size: %u\n" + " total blocks: %llu\n" + " buddy blocks: %llu\n" " fsid: %llx\n" " uuid: %s\n", - SCOUTFS_BLOCK_SIZE, le64_to_cpu(super->hdr.fsid), uuid_str); + total_blocks, buddy_blocks, + le64_to_cpu(super->hdr.fsid), uuid_str); ret = 0; out: diff --git a/utils/src/print.c b/utils/src/print.c index 3aa6277d..36097cfe 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -178,6 +178,49 @@ static int print_btree_block(int fd, __le64 blkno, u8 level) return ret; } +static int print_buddy_blocks(int fd, struct scoutfs_super_block *super) +{ + struct scoutfs_buddy_chunk *chunk; + struct scoutfs_buddy_block *bb; + u64 blkno; + u64 blocks; + u64 head; + u64 tail; + int i; + int j; + + blocks = le32_to_cpu(super->buddy_blocks); + head = le64_to_cpu(super->buddy_head); + tail = le64_to_cpu(super->buddy_tail); + + /* XXX make sure values are sane */ + + for (; head < tail; head++) { + + blkno = SCOUTFS_BUDDY_BLKNO + (head % blocks); + bb = read_block(fd, blkno); + if (!bb) + return -ENOMEM; + + printf("buddy blkno %llu\n", blkno); + print_block_header(&bb->hdr); + printf(" nr_chunks %u\n", bb->nr_chunks); + for (i = 0; i < bb->nr_chunks; i++) { + chunk = &bb->chunks[i]; + + printf(" [%u]: pos %u bits ", + i, le32_to_cpu(chunk->pos)); + for (j = 0; j < SCOUTFS_BUDDY_CHUNK_LE64S; j++) + printf("%016llx", le64_to_cpu(chunk->bits[j])); + printf("\n"); + } + + free(bb); + } + + return 0; +} + static int print_super_blocks(int fd) { struct scoutfs_super_block *super; @@ -198,6 +241,13 @@ static int print_super_blocks(int fd) print_block_header(&super->hdr); printf(" id %llx uuid %s\n", le64_to_cpu(super->id), uuid_str); + printf(" total_blocks %llu buddy_blocks %u buddy_sweep_bit %u\n" + " buddy_head %llu buddy_tail %llu\n", + le64_to_cpu(super->total_blocks), + le32_to_cpu(super->buddy_blocks), + le32_to_cpu(super->buddy_sweep_bit), + le64_to_cpu(super->buddy_head), + le64_to_cpu(super->buddy_tail)); printf(" btree_root: height %u seq %llu blkno %llu\n", super->btree_root.height, le64_to_cpu(super->btree_root.ref.seq), @@ -211,11 +261,15 @@ static int print_super_blocks(int fd) super = &recent; - if (super->btree_root.height) + ret = print_buddy_blocks(fd, super); + + if (super->btree_root.height) { err = print_btree_block(fd, super->btree_root.ref.blkno, super->btree_root.height - 1); - if (err && !ret) - ret = err; + if (err && !ret) + ret = err; + } + return ret; }