From 2c2f090168124b5b302bc72abcfb2c5b174ed57d Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Fri, 12 Feb 2016 14:35:27 -0800 Subject: [PATCH 001/235] Initial commit This initial commit has enough to make a new file system and print out it's structures. Signed-off-by: Zach Brown --- utils/.gitignore | 7 + utils/Makefile | 33 ++++ utils/sparse.sh | 58 +++++++ utils/src/cmd.c | 82 +++++++++ utils/src/cmd.h | 9 + utils/src/crc.c | 39 +++++ utils/src/crc.h | 12 ++ utils/src/format.h | 234 +++++++++++++++++++++++++ utils/src/lebitmap.c | 38 ++++ utils/src/lebitmap.h | 11 ++ utils/src/main.c | 24 +++ utils/src/mkfs.c | 250 +++++++++++++++++++++++++++ utils/src/print.c | 402 +++++++++++++++++++++++++++++++++++++++++++ utils/src/rand.c | 30 ++++ utils/src/rand.h | 10 ++ utils/src/sparse.h | 105 +++++++++++ utils/src/util.h | 73 ++++++++ 17 files changed, 1417 insertions(+) create mode 100644 utils/.gitignore create mode 100644 utils/Makefile create mode 100755 utils/sparse.sh create mode 100644 utils/src/cmd.c create mode 100644 utils/src/cmd.h create mode 100644 utils/src/crc.c create mode 100644 utils/src/crc.h create mode 100644 utils/src/format.h create mode 100644 utils/src/lebitmap.c create mode 100644 utils/src/lebitmap.h create mode 100644 utils/src/main.c create mode 100644 utils/src/mkfs.c create mode 100644 utils/src/print.c create mode 100644 utils/src/rand.c create mode 100644 utils/src/rand.h create mode 100644 utils/src/sparse.h create mode 100644 utils/src/util.h diff --git a/utils/.gitignore b/utils/.gitignore new file mode 100644 index 00000000..0a68a563 --- /dev/null +++ b/utils/.gitignore @@ -0,0 +1,7 @@ +*.o +*.d +*.swp +src/scoutfs +.sparse* +.mock.build* +cscope.* diff --git a/utils/Makefile b/utils/Makefile new file mode 100644 index 00000000..6a15bbdd --- /dev/null +++ b/utils/Makefile @@ -0,0 +1,33 @@ +CFLAGS := -Wall -O2 -Werror -D_FILE_OFFSET_BITS=64 -g -mrdrnd -msse4.2 + +BIN := src/scoutfs +OBJ := $(patsubst %.c,%.o,$(wildcard src/*.c)) +DEPS := $(wildcard */*.d) + +all: $(BIN) + +ifneq ($(DEPS),) +-include $(DEPS) +endif + +ifeq ($(V), ) +QU = @echo +VE = @ +else +QU = @: +VE = +endif + +$(BIN): $(OBJ) + $(QU) [BIN $@] + $(VE)gcc -o $@ $^ -luuid + +%.o %.d: %.c Makefile sparse.sh + $(QU) [CC $<] + $(VE)gcc $(CFLAGS) -MD -MP -MF $*.d -c $< -o $*.o + $(QU) [SP $<] + $(VE)./sparse.sh -Wbitwise -D__CHECKER__ $(CFLAGS) $< + +.PHONY: clean +clean: + @rm -f $(BIN) $(OBJ) $(DEPS) .sparse.* diff --git a/utils/sparse.sh b/utils/sparse.sh new file mode 100755 index 00000000..61c7bd33 --- /dev/null +++ b/utils/sparse.sh @@ -0,0 +1,58 @@ +#!/bin/bash + +# can we find sparse? If not, we're done. +which sparse > /dev/null 2>&1 || exit 0 + +# +# one of the problems with using sparse in userspace is that it picks up +# things in system headers that we don't care about. We're willing to +# take on the burden of filtering them out so that we can have it tell +# us about problems in our code. +# +# system headers using __transparent_union__ +RE="^/.*error: ignoring attribute __transparent_union__" + +# we don't care if system headers have gcc attributes sparse doesn't +# know about +RE="$RE|error: attribute '__leaf__': unknown attribute" + +# yes, sparse, that's the size of memseting a 4 meg buffer all right +RE="$RE|warning: memset with byte count of 4194304" + +# +# don't filter out 'too many errors' here, it can signify that +# sparse doesn't understand something and is throwing a *ton* +# of useless errors before giving up and existing. Check +# unfiltered sparse output. +# + +# +# I'm not sure this is needed. +# +search=$(gcc -print-search-dirs | awk '($1 == "install:"){print "-I" $2}') + +# +# We're trying to use sparse against glibc headers which go wild trying to +# use internal compiler macros to test features. We copy gcc's and give +# them to sparse. But not __SIZE_TYPE__ 'cause sparse defines that one. +# +defines=".sparse.gcc-defines.h" +gcc -dM -E -x c - < /dev/null | grep -v __SIZE_TYPE__ > $defines +include="-include $defines" + +# +# sparse doesn't seem to notice when it's on a 64bit host. It warns that +# 64bit values don't fit in 'unsigned long' without this. +# +if grep -q "__LP64__ 1" $defines; then + m64="-m64" +else + m64="" +fi + +sparse $m64 $include $search/include "$@" 2>&1 | egrep -v "($RE)" | tee .sparse.output +if [ -s .sparse.output ]; then + exit 1 +else + exit 0 +fi diff --git a/utils/src/cmd.c b/utils/src/cmd.c new file mode 100644 index 00000000..2dc0cbd7 --- /dev/null +++ b/utils/src/cmd.c @@ -0,0 +1,82 @@ +#include +#include +#include +#include +#include +#include + +#include "cmd.h" +#include "util.h" + +static struct command { + char *name; + char *opts; + char *summary; + int (*func)(int argc, char **argv); +} cmds[100], *next_cmd = cmds; + +#define cmd_for_each(com) for (com = cmds; com->func; com++) + +void cmd_register(char *name, char *opts, char *summary, + int (*func)(int argc, char **argv)) +{ + struct command *com = next_cmd++; + + assert((com - cmds) < array_size(cmds)); + + com->name = name; + com->opts = opts; + com->summary = summary; + com->func = func; +} + +static struct command *find_command(char *name) +{ + struct command *com; + + cmd_for_each(com) { + if (!strcmp(name, com->name)) + return com; + } + + return NULL; +} + +static void usage(void) +{ + struct command *com; + + fprintf(stderr, "usage: scoutfs []\n" + "Commands:\n"); + + cmd_for_each(com) { + fprintf(stderr, " %8s %12s - %s\n", + com->name, com->opts, com->summary); + } +} + +int cmd_execute(int argc, char **argv) +{ + struct command *com = NULL; + int ret; + + if (argc > 1) { + com = find_command(argv[1]); + if (!com) + fprintf(stderr, "scoutfs: unrecognized command: '%s'\n", + argv[1]); + } + if (!com) { + usage(); + return 1; + } + + ret = com->func(argc - 2, argv + 2); + if (ret < 0) { + fprintf(stderr, "scoutfs: %s failed: %s (%d)\n", + com->name, strerror(-ret), -ret); + return 1; + } + + return 0; +} diff --git a/utils/src/cmd.h b/utils/src/cmd.h new file mode 100644 index 00000000..53515b36 --- /dev/null +++ b/utils/src/cmd.h @@ -0,0 +1,9 @@ +#ifndef _CMD_H_ +#define _CMD_H_ + +void cmd_register(char *name, char *opts, char *summary, + int (*func)(int argc, char **argv)); + +int cmd_execute(int argc, char **argv); + +#endif diff --git a/utils/src/crc.c b/utils/src/crc.c new file mode 100644 index 00000000..6f40350a --- /dev/null +++ b/utils/src/crc.c @@ -0,0 +1,39 @@ +#include "crc.h" +#include "util.h" +#include "format.h" + +u32 crc32c(u32 crc, const void *data, unsigned int len) +{ + while (len >= 8) { + crc = __builtin_ia32_crc32di(crc, *(u64 *)data); + len -= 8; + data += 8; + } + if (len & 4) { + crc = __builtin_ia32_crc32si(crc, *(u32 *)data); + data += 4; + } + if (len & 2) { + crc = __builtin_ia32_crc32hi(crc, *(u16 *)data); + data += 2; + } + if (len & 1) + crc = __builtin_ia32_crc32qi(crc, *(u8 *)data); + + return crc; +} + +/* A simple hack to get reasonably solid 64bit hash values */ +u64 crc32c_64(u32 crc, const void *data, unsigned int len) +{ + unsigned int half = (len + 1) / 2; + + return ((u64)crc32c(crc, data, half) << 32) | + crc32c(~crc, data + len - half, half); +} + +u32 crc_header(struct scoutfs_header *hdr, size_t size) +{ + return crc32c(~0, (char *)hdr + sizeof(hdr->crc), + size - sizeof(hdr->crc)); +} diff --git a/utils/src/crc.h b/utils/src/crc.h new file mode 100644 index 00000000..d9e370e7 --- /dev/null +++ b/utils/src/crc.h @@ -0,0 +1,12 @@ +#ifndef _CRC_H_ +#define _CRC_H_ + +#include "sparse.h" +#include "util.h" +#include "format.h" + +u32 crc32c(u32 crc, const void *data, unsigned int len); +u64 crc32c_64(u32 crc, const void *data, unsigned int len); +u32 crc_header(struct scoutfs_header *hdr, size_t size); + +#endif diff --git a/utils/src/format.h b/utils/src/format.h new file mode 100644 index 00000000..bff0b7cb --- /dev/null +++ b/utils/src/format.h @@ -0,0 +1,234 @@ +#ifndef _SCOUTFS_FORMAT_H_ +#define _SCOUTFS_FORMAT_H_ + +/* statfs(2) f_type */ +#define SCOUTFS_SUPER_MAGIC 0x554f4353 /* "SCOU" */ +/* super block id */ +#define SCOUTFS_SUPER_ID 0x2e736674756f6373ULL /* "scoutfs." */ + +/* + * Some fs structures are stored in smaller fixed size 4k bricks. + */ +#define SCOUTFS_BRICK_SHIFT 12 +#define SCOUTFS_BRICK_SIZE (1 << SCOUTFS_BRICK_SHIFT) + +/* + * A large block size reduces the amount of per-block overhead throughout + * the system: block IO, manifest communications and storage, etc. + */ +#define SCOUTFS_BLOCK_SHIFT 22 +#define SCOUTFS_BLOCK_SIZE (1 << SCOUTFS_BLOCK_SHIFT) + +/* for shifting between brick and block numbers */ +#define SCOUTFS_BLOCK_BRICK (SCOUTFS_BLOCK_SHIFT - SCOUTFS_BRICK_SHIFT) + +/* + * The super bricks leave a bunch of room at the start of the first + * block for platform structures like boot loaders. + */ +#define SCOUTFS_SUPER_BRICK 16 + +/* + * This header is found at the start of every brick and block + * so that we can verify that it's what we were looking for. + */ +struct scoutfs_header { + __le32 crc; + __le64 fsid; + __le64 seq; + __le64 nr; +} __packed; + +#define SCOUTFS_UUID_BYTES 16 + +/* + * The super is stored in a pair of bricks in the first block. + */ +struct scoutfs_super { + struct scoutfs_header hdr; + __le64 id; + __u8 uuid[SCOUTFS_UUID_BYTES]; + __le64 total_blocks; + __le64 ring_layout_block; + __le64 ring_layout_seq; + __le64 last_ring_brick; + __le64 last_ring_seq; + __le64 last_block_seq; +} __packed; + +/* + * We should be able to make the offset smaller if neither dirents nor + * data items use the full 64 bits. + */ +struct scoutfs_key { + __le64 inode; + u8 type; + __le64 offset; +} __packed; + +#define SCOUTFS_ROOT_INO 1 + +#define SCOUTFS_INODE_KEY 128 +#define SCOUTFS_DIRENT_KEY 192 + +struct scoutfs_ring_layout { + struct scoutfs_header hdr; + __le32 nr_blocks; + __le64 blocks[0]; +} __packed; + +struct scoutfs_ring_entry { + u8 type; + __le16 len; +} __packed; + +/* + * Ring blocks are 4k blocks stored inside the large ring blocks + * referenced by the ring descriptor block. + * + * The manifest entries describe the position of a given block in the + * manifest. They're keyed by the block number so that we can log + * movement of a block in the manifest with one log entry and we can log + * deletion with just the block number. + */ +struct scoutfs_ring_brick { + struct scoutfs_header hdr; + __le16 nr_entries; +} __packed; + +enum { + SCOUTFS_RING_REMOVE_MANIFEST = 0, + SCOUTFS_RING_ADD_MANIFEST, + SCOUTFS_RING_BITMAP, +}; + +/* + * Manifest entries are logged by their block number. This lets us log + * a change with one entry and a removal with a tiny block number + * without the key. + */ +struct scoutfs_ring_remove_manifest { + __le64 block; +} __packed; + +/* + * Including both keys might make the manifest too large. It might be + * better to only include one key and infer a block's range from the + * neighbour's key. The downside of that is that we assume that there + * isn't unused key space between blocks in a level. We might search + * blocks when we didn't need to. + */ +struct scoutfs_ring_add_manifest { + __le64 block; + __le64 seq; + __u8 level; + struct scoutfs_key first; + struct scoutfs_key last; +} __packed; + +struct scoutfs_ring_bitmap { + __le32 offset; + __le64 bits[2]; +} __packed; + +/* + * This bloom size is chosen to have a roughly 1% false positive rate + * for ~90k items which is roughly the worst case for a block full of + * dirents with reasonably small names. Pathologically smaller items + * could be even more dense. + */ +#define SCOUTFS_BLOOM_FILTER_BYTES (128 * 1024) +#define SCOUTFS_BLOOM_FILTER_BITS (SCOUTFS_BLOOM_FILTER_BYTES * 8) +#define SCOUTFS_BLOOM_INDEX_BITS (ilog2(SCOUTFS_BLOOM_FILTER_BITS)) +#define SCOUTFS_BLOOM_INDEX_MASK ((1 << SCOUTFS_BLOOM_INDEX_BITS) - 1) +#define SCOUTFS_BLOOM_INDEX_NR 7 + +struct scoutfs_lsm_block { + struct scoutfs_header hdr; + struct scoutfs_key first; + struct scoutfs_key last; + __le32 nr_items; + /* u8 bloom[SCOUTFS_BLOOM_BYTES]; */ + /* struct scoutfs_item_header items[0] .. */ +} __packed; + +struct scoutfs_item_header { + struct scoutfs_key key; + __le16 len; +} __packed; + +struct scoutfs_timespec { + __le64 sec; + __le32 nsec; +} __packed; + +/* + * XXX + * - otime? + * - compat flags? + * - version? + * - generation? + * - be more careful with rdev? + */ +struct scoutfs_inode { + __le64 size; + __le64 blocks; + __le32 nlink; + __le32 uid; + __le32 gid; + __le32 mode; + __le32 rdev; + __le32 salt; + struct scoutfs_timespec atime; + struct scoutfs_timespec ctime; + struct scoutfs_timespec mtime; +} __packed; + +#define SCOUTFS_ROOT_INO 1 + +/* + * Dirents are stored in items with an offset of the hash of their name. + * Colliding names are packed into the value. + */ +struct scoutfs_dirent { + __le64 ino; +#if defined(__LITTLE_ENDIAN_BITFIELD) + __u8 type:4, + coll_nr:4; +#else + __u8 coll_nr:4, + type:4; +#endif + __u8 name_len; + __u8 name[0]; +} __packed; + +#define SCOUTFS_NAME_LEN 255 + +/* + * We only use 31 bits for readdir positions so that we don't confuse + * old signed 32bit f_pos applications or those on the other side of + * network protocols that have limited readir positions. + */ + +#define SCOUTFS_DIRENT_OFF_BITS 27 +#define SCOUTFS_DIRENT_OFF_MASK ((1 << SCOUTFS_DIRENT_OFF_BITS) - 1) +#define SCOUTFS_DIRENT_COLL_BITS 4 +#define SCOUTFS_DIRENT_COLL_MASK ((1 << SCOUTFS_DIRENT_COLL_BITS) - 1) + +/* getdents returns the *next* pos with each entry. so we can't return ~0 */ +#define SCOUTFS_DIRENT_MAX_POS \ + (((1 << (SCOUTFS_DIRENT_OFF_BITS + SCOUTFS_DIRENT_COLL_BITS)) - 1) - 1) + +enum { + SCOUTFS_DT_FIFO = 0, + SCOUTFS_DT_CHR, + SCOUTFS_DT_DIR, + SCOUTFS_DT_BLK, + SCOUTFS_DT_REG, + SCOUTFS_DT_LNK, + SCOUTFS_DT_SOCK, + SCOUTFS_DT_WHT, +}; + +#endif diff --git a/utils/src/lebitmap.c b/utils/src/lebitmap.c new file mode 100644 index 00000000..e7848947 --- /dev/null +++ b/utils/src/lebitmap.c @@ -0,0 +1,38 @@ +#define _GNU_SOURCE /* ffsll */ +#include + +#include "lebitmap.h" + +void set_le_bit(__le64 *bits, u64 nr) +{ + bits += nr / 64; + + *bits = cpu_to_le64(le64_to_cpu(*bits) | (1ULL << (nr & 63))); +} + +void clear_le_bit(__le64 *bits, u64 nr) +{ + bits += nr / 64; + + *bits = cpu_to_le64(le64_to_cpu(*bits) & ~(1ULL << (nr & 63))); +} + +int test_le_bit(__le64 *bits, u64 nr) +{ + bits += nr / 64; + + return !!(le64_to_cpu(*bits) & (1ULL << (nr & 63))); +} + +/* returns -1 or nr */ +s64 find_first_le_bit(__le64 *bits, s64 count) +{ + long nr; + + for (nr = 0; count > 0; bits++, nr += 64, count -= 64) { + if (*bits) + return nr + ffsll(le64_to_cpu(*bits)) - 1; + } + + return -1; +} diff --git a/utils/src/lebitmap.h b/utils/src/lebitmap.h new file mode 100644 index 00000000..9e399d33 --- /dev/null +++ b/utils/src/lebitmap.h @@ -0,0 +1,11 @@ +#ifndef _LEBITMAP_H_ +#define _LEBITMAP_H_ + +#include "sparse.h" + +void set_le_bit(__le64 *bits, u64 nr); +void clear_le_bit(__le64 *bits, u64 nr); +int test_le_bit(__le64 *bits, u64 nr); +s64 find_first_le_bit(__le64 *bits, s64 count); + +#endif diff --git a/utils/src/main.c b/utils/src/main.c new file mode 100644 index 00000000..599babb0 --- /dev/null +++ b/utils/src/main.c @@ -0,0 +1,24 @@ +#include +#include +#include +#include +#include +#include + +#include "cmd.h" +#include "util.h" + +int main(int argc, char **argv) +{ + int ret; + + /* + * XXX parse global options, env, configs, etc. + */ + + ret = cmd_execute(argc, argv); + if (ret < 0) + return 1; + + return 0; +} diff --git a/utils/src/mkfs.c b/utils/src/mkfs.c new file mode 100644 index 00000000..c5957408 --- /dev/null +++ b/utils/src/mkfs.c @@ -0,0 +1,250 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sparse.h" +#include "cmd.h" +#include "util.h" +#include "format.h" +#include "crc.h" +#include "rand.h" + + +/* + * Update the buffer's header and write it out. + */ +static int write_header(int fd, u64 nr, struct scoutfs_header *hdr, size_t size) +{ + off_t off = nr * size; + ssize_t ret; + + hdr->nr = cpu_to_le64(nr); + hdr->crc = cpu_to_le32(crc_header(hdr, size)); + + ret = pwrite(fd, hdr, size, off); + if (ret != size) { + fprintf(stderr, "write at nr %llu (offset %llu, size %zu) returned %zd: %s (%d)\n", + nr, (long long)off, size, ret, strerror(errno), errno); + return -errno; + } + + return 0; +} + +static int write_brick(int fd, u64 nr, struct scoutfs_header *hdr) +{ + return write_header(fd, nr, hdr, SCOUTFS_BRICK_SIZE); +} + +static int write_block(int fd, u64 nr, struct scoutfs_header *hdr) +{ + return write_header(fd, nr, hdr, SCOUTFS_BLOCK_SIZE); +} + +/* + * - config blocks that describe ring + * - ring entries for lots of free blocks + * - manifest that references single block + * - block with inode + */ +/* + * So what does mkfs really need to do? + * + * - super blocks that describe ring log + * - ring log with free bitmap entries + * - ring log with manifest entries + * - single item block with root dir + */ +static int write_new_fs(char *path, int fd) +{ + struct scoutfs_super *super; + struct scoutfs_inode *inode; + struct scoutfs_ring_layout *rlo; + struct scoutfs_ring_brick *ring; + struct scoutfs_ring_entry *ent; + struct scoutfs_ring_add_manifest *mani; + struct scoutfs_ring_bitmap *bm; + struct scoutfs_lsm_block *lblk; + struct scoutfs_item_header *ihdr; + struct scoutfs_key root_key; + struct timeval tv; + char uuid_str[37]; + struct stat st; + unsigned int i; + u64 total_blocks; + void *buf; + int ret; + + gettimeofday(&tv, NULL); + + super = malloc(SCOUTFS_BRICK_SIZE); + buf = malloc(SCOUTFS_BLOCK_SIZE); + if (!super || !buf) { + ret = -errno; + fprintf(stderr, "failed to allocate a block: %s (%d)\n", + strerror(errno), errno); + goto out; + } + + if (fstat(fd, &st)) { + ret = -errno; + fprintf(stderr, "failed to stat '%s': %s (%d)\n", + path, strerror(errno), errno); + goto out; + } + + total_blocks = st.st_size >> SCOUTFS_BLOCK_SHIFT; + + root_key.inode = cpu_to_le64(SCOUTFS_ROOT_INO); + root_key.type = SCOUTFS_INODE_KEY; + root_key.offset = 0; + + /* initialize the super */ + memset(super, 0, sizeof(struct scoutfs_super)); + pseudo_random_bytes(&super->hdr.fsid, sizeof(super->hdr.fsid)); + 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->ring_layout_block = cpu_to_le64(1); + super->ring_layout_seq = cpu_to_le64(1); + super->last_ring_brick = cpu_to_le64(1); + super->last_ring_seq = cpu_to_le64(1); + super->last_block_seq = cpu_to_le64(1); + + /* the ring has a single block for now */ + memset(buf, 0, SCOUTFS_BLOCK_SIZE); + rlo = buf; + rlo->hdr.fsid = super->hdr.fsid; + rlo->hdr.seq = super->ring_layout_seq; + rlo->nr_blocks = cpu_to_le32(1); + rlo->blocks[0] = cpu_to_le64(2); + + ret = write_block(fd, 1, &rlo->hdr); + if (ret) + goto out; + + /* log the root inode block manifest and free bitmap */ + memset(buf, 0, SCOUTFS_BLOCK_SIZE); + ring = buf; + ring->hdr.fsid = super->hdr.fsid; + ring->hdr.seq = super->last_ring_seq; + ring->nr_entries = cpu_to_le16(2); + ent = (void *)(ring + 1); + ent->type = SCOUTFS_RING_ADD_MANIFEST; + ent->len = cpu_to_le16(sizeof(*mani)); + mani = (void *)(ent + 1); + mani->block = cpu_to_le64(3); + mani->seq = super->last_block_seq; + mani->level = 0; + mani->first = root_key; + mani->last = root_key; + ent = (void *)(mani + 1); + ent->type = SCOUTFS_RING_BITMAP; + ent->len = cpu_to_le16(sizeof(*bm)); + bm = (void *)(ent + 1); + memset(bm->bits, 0xff, sizeof(bm->bits)); + /* the first three blocks are allocated */ + bm->bits[0] = cpu_to_le64(~7ULL); + bm->bits[1] = cpu_to_le64(~0ULL); + + ret = write_block(fd, 2, &ring->hdr); + if (ret) + goto out; + + /* write a single lsm block with the root inode item */ + memset(buf, 0, SCOUTFS_BLOCK_SIZE); + lblk = buf; + lblk->hdr.fsid = super->hdr.fsid; + lblk->hdr.seq = super->last_block_seq; + lblk->first = root_key; + lblk->last = root_key; + lblk->nr_items = cpu_to_le32(1); + /* XXX set bloom */ + ihdr = (void *)((char *)(lblk + 1) + SCOUTFS_BLOOM_FILTER_BYTES); + ihdr->key = root_key; + ihdr->len = cpu_to_le16(sizeof(struct scoutfs_inode)); + inode = (void *)(ihdr + 1); + inode->nlink = cpu_to_le32(2); + inode->mode = cpu_to_le32(0755 | 0040000); + inode->atime.sec = cpu_to_le64(tv.tv_sec); + inode->atime.nsec = cpu_to_le32(tv.tv_usec * 1000); + inode->ctime.sec = inode->atime.sec; + inode->ctime.nsec = inode->atime.nsec; + inode->mtime.sec = inode->atime.sec; + inode->mtime.nsec = inode->atime.nsec; + + ret = write_block(fd, 3, &ring->hdr); + if (ret) + goto out; + + /* write the two super bricks */ + for (i = 0; i < 2; i++) { + super->hdr.seq = cpu_to_le64(i); + ret = write_brick(fd, SCOUTFS_SUPER_BRICK + i, &super->hdr); + if (ret) + goto out; + } + + if (fsync(fd)) { + ret = -errno; + fprintf(stderr, "failed to fsync '%s': %s (%d)\n", + path, strerror(errno), errno); + goto out; + } + + uuid_unparse(super->uuid, uuid_str); + + printf("Created scoutfs filesystem:\n" + " total blocks: %llu\n" + " fsid: %llx\n" + " uuid: %s\n", + total_blocks, le64_to_cpu(super->hdr.fsid), uuid_str); + + ret = 0; +out: + free(super); + free(buf); + return ret; +} + +static int mkfs_func(int argc, char *argv[]) +{ + char *path = argv[0]; + int ret; + int fd; + + if (argc != 1) { + printf("scoutfs: mkfs: a single path argument is required\n"); + return -EINVAL; + } + + fd = open(path, O_RDWR | O_EXCL); + if (fd < 0) { + ret = -errno; + fprintf(stderr, "failed to open '%s': %s (%d)\n", + path, strerror(errno), errno); + return ret; + } + + ret = write_new_fs(path, fd); + close(fd); + + return ret; +} + +static void __attribute__((constructor)) mkfs_ctor(void) +{ + cmd_register("mkfs", "", "write a new file system", mkfs_func); + + /* for lack of some other place to put these.. */ + build_assert(sizeof(uuid_t) == SCOUTFS_UUID_BYTES); +} diff --git a/utils/src/print.c b/utils/src/print.c new file mode 100644 index 00000000..df74bc67 --- /dev/null +++ b/utils/src/print.c @@ -0,0 +1,402 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sparse.h" +#include "util.h" +#include "format.h" +#include "cmd.h" +#include "crc.h" +#include "lebitmap.h" + +/* XXX maybe these go somewhere */ +#define SKF "%llu.%u.%llu" +#define SKA(k) le64_to_cpu((k)->inode), (k)->type, \ + le64_to_cpu((k)->offset) + +static void *read_buf(int fd, u64 nr, size_t size) +{ + off_t off = nr * size; + ssize_t ret; + void *buf; + + buf = malloc(size); + if (!buf) + return NULL; + + ret = pread(fd, buf, size, off); + if (ret != size) { + fprintf(stderr, "read at blkno %llu (offset %llu) returned %zd: %s (%d)\n", + nr, (long long)off, ret, strerror(errno), errno); + free(buf); + buf = NULL; + } + + return buf; +} + +static void *read_brick(int fd, u64 nr) +{ + return read_buf(fd, nr, SCOUTFS_BRICK_SIZE); +} + +static void *read_block(int fd, u64 nr) +{ + return read_buf(fd, nr, SCOUTFS_BLOCK_SIZE); +} + +static void print_header(struct scoutfs_header *hdr, size_t size) +{ + u32 crc = crc_header(hdr, size); + char valid_str[40]; + + if (crc != le32_to_cpu(hdr->crc)) + sprintf(valid_str, "# != %08x", crc); + else + valid_str[0] = '\0'; + + printf(" header:\n" + " crc: %08x %s\n" + " fsid: %llx\n" + " seq: %llu\n" + " nr: %llu\n", + le32_to_cpu(hdr->crc), valid_str, le64_to_cpu(hdr->fsid), + le64_to_cpu(hdr->seq), le64_to_cpu(hdr->nr)); +} + +static void print_brick_header(struct scoutfs_header *hdr) +{ + return print_header(hdr, SCOUTFS_BRICK_SIZE); +} + +static void print_block_header(struct scoutfs_header *hdr) +{ + return print_header(hdr, SCOUTFS_BLOCK_SIZE); +} + +static void print_inode(struct scoutfs_inode *inode) +{ + printf(" inode:\n" + " size: %llu\n" + " blocks: %llu\n" + " nlink: %u\n" + " uid: %u\n" + " gid: %u\n" + " mode: 0%o\n" + " rdev: 0x%x\n" + " salt: 0x%x\n" + " atime: %llu.%08u\n" + " ctime: %llu.%08u\n" + " mtime: %llu.%08u\n", + le64_to_cpu(inode->size), le64_to_cpu(inode->blocks), + le32_to_cpu(inode->nlink), le32_to_cpu(inode->uid), + le32_to_cpu(inode->gid), le32_to_cpu(inode->mode), + le32_to_cpu(inode->rdev), le32_to_cpu(inode->salt), + le64_to_cpu(inode->atime.sec), + le32_to_cpu(inode->atime.nsec), + le64_to_cpu(inode->ctime.sec), + le32_to_cpu(inode->ctime.nsec), + le64_to_cpu(inode->mtime.sec), + le32_to_cpu(inode->mtime.nsec)); +} + +static void print_item(struct scoutfs_item_header *ihdr, size_t off) +{ + printf(" item: &%zu\n" + " key: "SKF"\n" + " len: %u\n", + off, SKA(&ihdr->key), le16_to_cpu(ihdr->len)); + + switch(ihdr->key.type) { + case SCOUTFS_INODE_KEY: + print_inode((void *)(ihdr + 1)); + break; + } +} + +static int print_block(int fd, u64 nr) +{ + struct scoutfs_item_header *ihdr; + struct scoutfs_lsm_block *lblk; + size_t off; + int i; + + lblk = read_block(fd, nr); + if (!lblk) + return -ENOMEM; + + printf("block: &%llu\n", le64_to_cpu(lblk->hdr.nr)); + print_block_header(&lblk->hdr); + printf(" first: "SKF"\n" + " last: "SKF"\n" + " nr_items: %u\n", + SKA(&lblk->first), SKA(&lblk->last), + le32_to_cpu(lblk->nr_items)); + off = (char *)(lblk + 1) - (char *)lblk + SCOUTFS_BLOOM_FILTER_BYTES; + + for (i = 0; i < le32_to_cpu(lblk->nr_items); i++) { + ihdr = (void *)((char *)lblk + off); + print_item(ihdr, off); + + off += sizeof(struct scoutfs_item_header) + + le16_to_cpu(ihdr->len); + } + + free(lblk); + + return 0; +} + +static int print_blocks(int fd, __le64 *live_blocks, u64 total_blocks) +{ + int ret = 0; + int err; + s64 nr; + + while ((nr = find_first_le_bit(live_blocks, total_blocks)) >= 0) { + clear_le_bit(live_blocks, nr); + + err = print_block(fd, nr); + if (!ret && err) + ret = err; + } + + return ret; +} + +static char *ent_type_str(u8 type) +{ + switch (type) { + case SCOUTFS_RING_REMOVE_MANIFEST: + return "REMOVE_MANIFEST"; + case SCOUTFS_RING_ADD_MANIFEST: + return "ADD_MANIFEST"; + case SCOUTFS_RING_BITMAP: + return "BITMAP"; + default: + return "(unknown)"; + } +} + +static void print_ring_entry(int fd, struct scoutfs_ring_entry *ent, + size_t off) +{ + struct scoutfs_ring_remove_manifest *rem; + struct scoutfs_ring_add_manifest *add; + struct scoutfs_ring_bitmap *bm; + + printf(" entry: &%zu\n" + " type: %u # %s\n" + " len: %u\n", + off, ent->type, ent_type_str(ent->type), le16_to_cpu(ent->len)); + + switch(ent->type) { + case SCOUTFS_RING_REMOVE_MANIFEST: + rem = (void *)(ent + 1); + printf(" block: %llu\n", + le64_to_cpu(rem->block)); + break; + case SCOUTFS_RING_ADD_MANIFEST: + add = (void *)(ent + 1); + printf(" block: %llu\n" + " seq: %llu\n" + " level: %u\n" + " first: "SKF"\n" + " last: "SKF"\n", + le64_to_cpu(add->block), le64_to_cpu(add->seq), + add->level, SKA(&add->first), SKA(&add->last)); + break; + case SCOUTFS_RING_BITMAP: + bm = (void *)(ent + 1); + printf(" offset: %u\n" + " bits: 0x%llx%llx\n", + le32_to_cpu(bm->offset), + le64_to_cpu(bm->bits[1]), le64_to_cpu(bm->bits[0])); + break; + } +} + +static void update_live_blocks(struct scoutfs_ring_entry *ent, + __le64 *live_blocks) +{ + struct scoutfs_ring_remove_manifest *rem; + struct scoutfs_ring_add_manifest *add; + + switch(ent->type) { + case SCOUTFS_RING_REMOVE_MANIFEST: + rem = (void *)(ent + 1); + clear_le_bit(live_blocks, le64_to_cpu(rem->block)); + break; + case SCOUTFS_RING_ADD_MANIFEST: + add = (void *)(ent + 1); + set_le_bit(live_blocks, le64_to_cpu(add->block)); + break; + } +} + +static int print_ring_block(int fd, u64 block_nr, __le64 *live_blocks) +{ + struct scoutfs_ring_brick *ring; + struct scoutfs_ring_entry *ent; + size_t off; + int ret = 0; + u64 nr; + int i; + + /* XXX just printing the first brick for now */ + + nr = block_nr << SCOUTFS_BLOCK_BRICK; + ring = read_brick(fd, nr); + if (!ring) + return -ENOMEM; + + printf("ring brick: &%llu\n", nr); + print_brick_header(&ring->hdr); + printf(" nr_entries: %u\n", le16_to_cpu(ring->nr_entries)); + + off = sizeof(struct scoutfs_ring_brick); + for (i = 0; i < le16_to_cpu(ring->nr_entries); i++) { + ent = (void *)((char *)ring + off); + + update_live_blocks(ent, live_blocks); + + print_ring_entry(fd, ent, off); + + off += sizeof(struct scoutfs_ring_entry) + + le16_to_cpu(ent->len); + } + + free(ring); + return ret; +} + +static int print_ring_layout(int fd, u64 blkno, __le64 *live_blocks) +{ + struct scoutfs_ring_layout *rlo; + int ret = 0; + int err; + int i; + + rlo = read_block(fd, blkno); + if (!rlo) + return -ENOMEM; + + printf("ring layout: &%llu\n", blkno); + print_block_header(&rlo->hdr); + printf(" nr_blocks: %u\n", le32_to_cpu(rlo->nr_blocks)); + + printf(" blocks: "); + for (i = 0; i < le32_to_cpu(rlo->nr_blocks); i++) + printf(" %llu\n", le64_to_cpu(rlo->blocks[i])); + + for (i = 0; i < le32_to_cpu(rlo->nr_blocks); i++) { + err = print_ring_block(fd, le64_to_cpu(rlo->blocks[i]), + live_blocks); + if (err && !ret) + ret = err; + } + + free(rlo); + return 0; +} + +static int print_super_brick(int fd) +{ + struct scoutfs_super *super; + char uuid_str[37]; + __le64 *live_blocks; + u64 total_blocks; + size_t bytes; + int ret = 0; + int err; + + /* XXX print both */ + super = read_brick(fd, SCOUTFS_SUPER_BRICK); + if (!super) + return -ENOMEM; + + uuid_unparse(super->uuid, uuid_str); + + total_blocks = le64_to_cpu(super->total_blocks); + + printf("super: &%llu\n", le64_to_cpu(super->hdr.nr)); + print_brick_header(&super->hdr); + printf(" id: %llx\n" + " uuid: %s\n" + " total_blocks: %llu\n" + " ring_layout_block: %llu\n" + " ring_layout_seq: %llu\n" + " last_ring_brick: %llu\n" + " last_ring_seq: %llu\n" + " last_block_seq: %llu\n", + le64_to_cpu(super->id), + uuid_str, + total_blocks, + le64_to_cpu(super->ring_layout_block), + le64_to_cpu(super->ring_layout_seq), + le64_to_cpu(super->last_ring_brick), + le64_to_cpu(super->last_ring_seq), + le64_to_cpu(super->last_block_seq)); + + /* XXX by hand? */ + bytes = (total_blocks + 63) / 8; + live_blocks = malloc(bytes); + if (!live_blocks) { + ret = -ENOMEM; + goto out; + } + memset(live_blocks, 0, bytes); + + err = print_ring_layout(fd, le64_to_cpu(super->ring_layout_block), + live_blocks); + if (err && !ret) + ret = err; + + err = print_blocks(fd, live_blocks, total_blocks); + if (err && !ret) + ret = err; + +out: + free(super); + free(live_blocks); + return ret; +} + +static int print_cmd(int argc, char **argv) +{ + char *path; + int ret; + int fd; + + if (argc != 1) { + printf("scoutfs print: a single path argument is required\n"); + return -EINVAL; + } + path = argv[0]; + + fd = open(path, O_RDONLY); + if (fd < 0) { + ret = -errno; + fprintf(stderr, "failed to open '%s': %s (%d)\n", + path, strerror(errno), errno); + return ret; + } + + ret = print_super_brick(fd); + close(fd); + return ret; +}; + +static void __attribute__((constructor)) print_ctor(void) +{ + cmd_register("print", "", "print metadata structures", + print_cmd); +} diff --git a/utils/src/rand.c b/utils/src/rand.c new file mode 100644 index 00000000..82b17e11 --- /dev/null +++ b/utils/src/rand.c @@ -0,0 +1,30 @@ +#include + +#include "rand.h" +#include "sparse.h" +#include "util.h" + +void pseudo_random_bytes(void *data, unsigned int len) +{ + unsigned long long tmp; + unsigned long long *ll = data; + unsigned int sz = sizeof(*ll); + unsigned int unaligned; + + /* see if the initial buffer is unaligned */ + unaligned = min((unsigned long)data & (sz - 1), len); + if (unaligned) { + __builtin_ia32_rdrand64_step(&tmp); + memcpy(data, &tmp, unaligned); + data += unaligned; + len -= unaligned; + } + + for (ll = data; len >= sz; ll++, len -= sz) + __builtin_ia32_rdrand64_step(ll); + + if (len) { + __builtin_ia32_rdrand64_step(&tmp); + memcpy(data, &tmp, len); + } +} diff --git a/utils/src/rand.h b/utils/src/rand.h new file mode 100644 index 00000000..cbc74eb7 --- /dev/null +++ b/utils/src/rand.h @@ -0,0 +1,10 @@ +#ifndef _RAND_H_ +#define _RAND_H_ + +/* + * We could play around a bit with some macros to get aligned constant + * word sized buffers filled by single instructions. + */ +void pseudo_random_bytes(void *data, unsigned int len); + +#endif diff --git a/utils/src/sparse.h b/utils/src/sparse.h new file mode 100644 index 00000000..63b66ca9 --- /dev/null +++ b/utils/src/sparse.h @@ -0,0 +1,105 @@ +#ifndef _SPARSE_H_ +#define _SPARSE_H_ + +#include +#include + +#ifdef __CHECKER__ +# undef __force +# define __force __attribute__((force)) +# undef __bitwise +# define __bitwise __attribute__((bitwise)) +/* sparse seems to get confused by some builtins */ +extern __builtin_ia32_rdrand64_step(unsigned long long *); +extern unsigned int __builtin_ia32_crc32di(unsigned int, unsigned long long); +extern unsigned int __builtin_ia32_crc32si(unsigned int, unsigned int); +extern unsigned int __builtin_ia32_crc32hi(unsigned int, unsigned short); +extern unsigned int __builtin_ia32_crc32qi(unsigned int, unsigned char); + +#else +# define __force +# define __bitwise +#endif + +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +typedef unsigned long long u64; +typedef signed long long s64; + +typedef u8 __u8; +typedef u16 __u16; +typedef u32 __u32; +typedef u64 __u64; + +typedef u16 __bitwise __le16; +typedef u16 __bitwise __be16; +typedef u32 __bitwise __le32; +typedef u32 __bitwise __be32; +typedef u64 __bitwise __le64; +typedef u64 __bitwise __be64; + +static inline u16 ___swab16(u16 x) +{ + return ((x & (u16)0x00ffU) << 8) | + ((x & (u16)0xff00U) >> 8); +} + +static inline u32 ___swab32(u32 x) +{ + return ((x & (u32)0x000000ffUL) << 24) | + ((x & (u32)0x0000ff00UL) << 8) | + ((x & (u32)0x00ff0000UL) >> 8) | + ((x & (u32)0xff000000UL) >> 24); +} + +static inline u64 ___swab64(u64 x) +{ + return (u64)((x & (u64)0x00000000000000ffULL) << 56) | + (u64)((x & (u64)0x000000000000ff00ULL) << 40) | + (u64)((x & (u64)0x0000000000ff0000ULL) << 24) | + (u64)((x & (u64)0x00000000ff000000ULL) << 8) | + (u64)((x & (u64)0x000000ff00000000ULL) >> 8) | + (u64)((x & (u64)0x0000ff0000000000ULL) >> 24) | + (u64)((x & (u64)0x00ff000000000000ULL) >> 40) | + (u64)((x & (u64)0xff00000000000000ULL) >> 56); +} + +#define __gen_cast_tofrom(end, size) \ +static inline __##end##size cpu_to_##end##size(u##size x) \ +{ \ + return (__force __##end##size)x; \ +} \ +static inline u##size end##size##_to_cpu(__##end##size x) \ +{ \ + return (__force u##size)x; \ +} + +#define __gen_swap_tofrom(end, size) \ +static inline __##end##size cpu_to_##end##size(u##size x) \ +{ \ + return (__force __##end##size)___swab##size(x); \ +} \ +static inline u##size end##size##_to_cpu(__##end##size x) \ +{ \ + return ___swab##size((__force u##size) x); \ +} + +#define __gen_functions(which, end) \ + __gen_##which##_tofrom(end, 16) \ + __gen_##which##_tofrom(end, 32) \ + __gen_##which##_tofrom(end, 64) + +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define __LITTLE_ENDIAN_BITFIELD +__gen_functions(swap, be) +__gen_functions(cast, le) +#elif __BYTE_ORDER == __BIG_ENDIAN +#define __BIG_ENDIAN_BITFIELD +__gen_functions(swap, le) +__gen_functions(cast, be) +#else +#error "machine is neither BIG_ENDIAN nor LITTLE_ENDIAN" +#endif + +#endif diff --git a/utils/src/util.h b/utils/src/util.h new file mode 100644 index 00000000..f4dec4f0 --- /dev/null +++ b/utils/src/util.h @@ -0,0 +1,73 @@ +#ifndef _UTIL_H_ +#define _UTIL_H_ + +#include +#include +#include + +/* + * Generate build warnings if the condition is false but generate no + * code at run time if it's true. + */ +#define build_assert(cond) ((void)sizeof(char[1 - 2*!(cond)])) + +#define min(a, b) \ +({ \ + __typeof__(a) _a = (a); \ + __typeof__(b) _b = (b); \ + \ + _a < _b ? _a : _b; \ +}) + +#define max(a, b) \ +({ \ + __typeof__(a) _a = (a); \ + __typeof__(b) _b = (b); \ + \ + _a > _b ? _a : _b; \ +}) + +#define swap(a, b) \ +do { \ + __typeof__(a) _t = (a); \ + (a) = (b); \ + (b) = (_t); \ +} while (0) + +#define array_size(arr) (sizeof(arr) / sizeof(arr[0])) + +#define __packed __attribute__((packed)) + +/* + * Round the 'a' value up to the next 'b' power of two boundary. It + * casts the mask to the value type before masking to avoid truncation + * problems. + */ +#define round_up(a, b) \ +({ \ + __typeof__(a) _b = (b); \ + \ + ((a) + _b - 1) & ~(_b - 1); \ +}) + +#ifndef offsetof +#define offsetof(type, memb) ((unsigned long)&((type *)0)->memb) +#endif + +#define container_of(ptr, type, memb) \ + ((type *)((void *)(ptr) - offsetof(type, memb))) + +/* + * return -1,0,+1 based on the memcmp comparison of the minimum of their + * two lengths. If their min shared bytes are equal but the lengths + * are not then the larger length is considered greater. + */ +static inline int memcmp_lens(const void *a, int a_len, + const void *b, int b_len) +{ + unsigned int len = min(a_len, b_len); + + return memcmp(a, b, len) ?: a_len - b_len; +} + +#endif From a7b8f955fe18ddfc9ee56c639b4ef8aa49ae6796 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Fri, 19 Feb 2016 08:53:14 -0800 Subject: [PATCH 002/235] write ring brick as brick in mkfs The only ring brick was being written as a full block which made its brick checksum cover the entire block instead of just the brick. --- utils/src/mkfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/src/mkfs.c b/utils/src/mkfs.c index c5957408..0c4e0198 100644 --- a/utils/src/mkfs.c +++ b/utils/src/mkfs.c @@ -156,7 +156,7 @@ static int write_new_fs(char *path, int fd) bm->bits[0] = cpu_to_le64(~7ULL); bm->bits[1] = cpu_to_le64(~0ULL); - ret = write_block(fd, 2, &ring->hdr); + ret = write_brick(fd, 2 << SCOUTFS_BLOCK_BRICK, &ring->hdr); if (ret) goto out; From de1bf39614818e70633d73605b8b05f3e699063d Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Fri, 19 Feb 2016 15:40:04 -0800 Subject: [PATCH 003/235] Get rid of bricks Get rid of the explicit distinction between brick and block numbers. The format is now defined it terms of fixed 4k blocks. Logs become a logical structure that's made up of a fixed number of blocks. The allocator still manages large log sized regions. --- utils/src/crc.c | 4 +- utils/src/crc.h | 2 +- utils/src/format.h | 101 +++++++++++----------- utils/src/mkfs.c | 201 ++++++++++++++++++++----------------------- utils/src/print.c | 208 ++++++++++++++++++++------------------------- 5 files changed, 240 insertions(+), 276 deletions(-) diff --git a/utils/src/crc.c b/utils/src/crc.c index 6f40350a..38640fbc 100644 --- a/utils/src/crc.c +++ b/utils/src/crc.c @@ -32,8 +32,8 @@ u64 crc32c_64(u32 crc, const void *data, unsigned int len) crc32c(~crc, data + len - half, half); } -u32 crc_header(struct scoutfs_header *hdr, size_t size) +u32 crc_block(struct scoutfs_block_header *hdr) { return crc32c(~0, (char *)hdr + sizeof(hdr->crc), - size - sizeof(hdr->crc)); + SCOUTFS_BLOCK_SIZE - sizeof(hdr->crc)); } diff --git a/utils/src/crc.h b/utils/src/crc.h index d9e370e7..6878bf2f 100644 --- a/utils/src/crc.h +++ b/utils/src/crc.h @@ -7,6 +7,6 @@ u32 crc32c(u32 crc, const void *data, unsigned int len); u64 crc32c_64(u32 crc, const void *data, unsigned int len); -u32 crc_header(struct scoutfs_header *hdr, size_t size); +u32 crc_block(struct scoutfs_block_header *hdr); #endif diff --git a/utils/src/format.h b/utils/src/format.h index bff0b7cb..88d228a8 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -7,53 +7,66 @@ #define SCOUTFS_SUPER_ID 0x2e736674756f6373ULL /* "scoutfs." */ /* - * Some fs structures are stored in smaller fixed size 4k bricks. + * Structures are stored and referenced in fixed 4k chunks to + * simplify block buffer access at run time. */ -#define SCOUTFS_BRICK_SHIFT 12 -#define SCOUTFS_BRICK_SIZE (1 << SCOUTFS_BRICK_SHIFT) - -/* - * A large block size reduces the amount of per-block overhead throughout - * the system: block IO, manifest communications and storage, etc. - */ -#define SCOUTFS_BLOCK_SHIFT 22 +#define SCOUTFS_BLOCK_SHIFT 12 #define SCOUTFS_BLOCK_SIZE (1 << SCOUTFS_BLOCK_SHIFT) -/* for shifting between brick and block numbers */ -#define SCOUTFS_BLOCK_BRICK (SCOUTFS_BLOCK_SHIFT - SCOUTFS_BRICK_SHIFT) +/* + * Logs are a logical structure that is made up of a fixed number of + * contiguously allocated blocks. + * + * The allocator manages log-sized regions. Smaller metadata blocks + * like the ring and super blocks are stored inside large log + * allocations. + */ +#define SCOUTFS_LOG_SHIFT 22 +#define SCOUTFS_LOG_SIZE (1 << SCOUTFS_LOG_SHIFT) +#define SCOUTFS_LOG_BLOCK_SHIFT (SCOUTFS_LOG_SHIFT - SCOUTFS_BLOCK_SHIFT) +#define SCOUTFS_BLOCKS_PER_LOG (1 << SCOUTFS_LOG_BLOCK_SHIFT) /* - * The super bricks leave a bunch of room at the start of the first - * block for platform structures like boot loaders. + * The super blocks leave some room at the start of the first block for + * platform structures like boot loaders. */ -#define SCOUTFS_SUPER_BRICK 16 +#define SCOUTFS_SUPER_BLKNO ((64 * 1024) >> SCOUTFS_BLOCK_SHIFT) +#define SCOUTFS_SUPER_NR 2 /* - * This header is found at the start of every brick and block - * so that we can verify that it's what we were looking for. + * This header is found at the start of every block so that we can + * verify that it's what we were looking for. The crc and padding + * starts the block so that its calculation operations on a nice 64bit + * aligned region. */ -struct scoutfs_header { +struct scoutfs_block_header { __le32 crc; + __le32 _pad; __le64 fsid; __le64 seq; - __le64 nr; + __le64 blkno; } __packed; #define SCOUTFS_UUID_BYTES 16 /* - * The super is stored in a pair of bricks in the first block. + * The super is stored in a pair of blocks in log 0 on the device. + * + * The ring layout blocks describe the location of the ring blocks. The + * ring start and length refers to the logical ring blocks within that + * storage which contain live data. */ -struct scoutfs_super { - struct scoutfs_header hdr; +struct scoutfs_super_block { + struct scoutfs_block_header hdr; __le64 id; __u8 uuid[SCOUTFS_UUID_BYTES]; - __le64 total_blocks; - __le64 ring_layout_block; + __le64 total_logs; + __le64 ring_layout_blkno; + __le64 ring_layout_nr_blocks; __le64 ring_layout_seq; - __le64 last_ring_brick; - __le64 last_ring_seq; - __le64 last_block_seq; + __le64 ring_block; + __le64 ring_nr_blocks; + __le64 ring_seq; } __packed; /* @@ -71,10 +84,10 @@ struct scoutfs_key { #define SCOUTFS_INODE_KEY 128 #define SCOUTFS_DIRENT_KEY 192 -struct scoutfs_ring_layout { - struct scoutfs_header hdr; +struct scoutfs_layout_block { + struct scoutfs_block_header hdr; __le32 nr_blocks; - __le64 blocks[0]; + __le64 blknos[0]; } __packed; struct scoutfs_ring_entry { @@ -83,16 +96,16 @@ struct scoutfs_ring_entry { } __packed; /* - * Ring blocks are 4k blocks stored inside the large ring blocks - * referenced by the ring descriptor block. + * Ring blocks are 4k blocks stored inside the regions described by the + * ring layout block referenced by the super. * * The manifest entries describe the position of a given block in the * manifest. They're keyed by the block number so that we can log * movement of a block in the manifest with one log entry and we can log * deletion with just the block number. */ -struct scoutfs_ring_brick { - struct scoutfs_header hdr; +struct scoutfs_ring_block { + struct scoutfs_block_header hdr; __le16 nr_entries; } __packed; @@ -108,7 +121,7 @@ enum { * without the key. */ struct scoutfs_ring_remove_manifest { - __le64 block; + __le64 blkno; } __packed; /* @@ -119,7 +132,7 @@ struct scoutfs_ring_remove_manifest { * blocks when we didn't need to. */ struct scoutfs_ring_add_manifest { - __le64 block; + __le64 blkno; __le64 seq; __u8 level; struct scoutfs_key first; @@ -132,23 +145,15 @@ struct scoutfs_ring_bitmap { } __packed; /* - * This bloom size is chosen to have a roughly 1% false positive rate - * for ~90k items which is roughly the worst case for a block full of - * dirents with reasonably small names. Pathologically smaller items - * could be even more dense. + * To start the logs are a trivial single item block. We'll flesh this out + * into larger blocks once the rest of the architecture is in + * place. */ -#define SCOUTFS_BLOOM_FILTER_BYTES (128 * 1024) -#define SCOUTFS_BLOOM_FILTER_BITS (SCOUTFS_BLOOM_FILTER_BYTES * 8) -#define SCOUTFS_BLOOM_INDEX_BITS (ilog2(SCOUTFS_BLOOM_FILTER_BITS)) -#define SCOUTFS_BLOOM_INDEX_MASK ((1 << SCOUTFS_BLOOM_INDEX_BITS) - 1) -#define SCOUTFS_BLOOM_INDEX_NR 7 - -struct scoutfs_lsm_block { - struct scoutfs_header hdr; +struct scoutfs_item_block { + struct scoutfs_block_header hdr; struct scoutfs_key first; struct scoutfs_key last; __le32 nr_items; - /* u8 bloom[SCOUTFS_BLOOM_BYTES]; */ /* struct scoutfs_item_header items[0] .. */ } __packed; diff --git a/utils/src/mkfs.c b/utils/src/mkfs.c index 0c4e0198..60eb83dd 100644 --- a/utils/src/mkfs.c +++ b/utils/src/mkfs.c @@ -17,77 +17,56 @@ #include "crc.h" #include "rand.h" - /* - * Update the buffer's header and write it out. + * Update the block's header and write it out. */ -static int write_header(int fd, u64 nr, struct scoutfs_header *hdr, size_t size) +static int write_block(int fd, u64 blkno, struct scoutfs_block_header *hdr) { - off_t off = nr * size; ssize_t ret; - hdr->nr = cpu_to_le64(nr); - hdr->crc = cpu_to_le32(crc_header(hdr, size)); + hdr->blkno = cpu_to_le64(blkno); + hdr->crc = cpu_to_le32(crc_block(hdr)); - ret = pwrite(fd, hdr, size, off); - if (ret != size) { - fprintf(stderr, "write at nr %llu (offset %llu, size %zu) returned %zd: %s (%d)\n", - nr, (long long)off, size, ret, strerror(errno), errno); + ret = pwrite(fd, hdr, SCOUTFS_BLOCK_SIZE, blkno << SCOUTFS_BLOCK_SHIFT); + if (ret != SCOUTFS_BLOCK_SIZE) { + fprintf(stderr, "write to blkno %llu returned %zd: %s (%d)\n", + blkno, ret, strerror(errno), errno); return -errno; } return 0; } -static int write_brick(int fd, u64 nr, struct scoutfs_header *hdr) -{ - return write_header(fd, nr, hdr, SCOUTFS_BRICK_SIZE); -} - -static int write_block(int fd, u64 nr, struct scoutfs_header *hdr) -{ - return write_header(fd, nr, hdr, SCOUTFS_BLOCK_SIZE); -} - -/* - * - config blocks that describe ring - * - ring entries for lots of free blocks - * - manifest that references single block - * - block with inode - */ -/* - * So what does mkfs really need to do? - * - * - super blocks that describe ring log - * - ring log with free bitmap entries - * - ring log with manifest entries - * - single item block with root dir - */ static int write_new_fs(char *path, int fd) { - struct scoutfs_super *super; + struct scoutfs_super_block *super; + struct scoutfs_block_header hdr; struct scoutfs_inode *inode; - struct scoutfs_ring_layout *rlo; - struct scoutfs_ring_brick *ring; + struct scoutfs_layout_block *lout; + struct scoutfs_ring_block *ring; struct scoutfs_ring_entry *ent; struct scoutfs_ring_add_manifest *mani; struct scoutfs_ring_bitmap *bm; - struct scoutfs_lsm_block *lblk; + struct scoutfs_item_block *iblk; struct scoutfs_item_header *ihdr; struct scoutfs_key root_key; struct timeval tv; char uuid_str[37]; struct stat st; unsigned int i; - u64 total_blocks; + u64 total_logs; + u64 blkno; void *buf; int ret; gettimeofday(&tv, NULL); + /* crc and blkno written for each write */ + hdr._pad = 0; + pseudo_random_bytes(&hdr.fsid, sizeof(hdr.fsid)); + hdr.seq = cpu_to_le64(1); - super = malloc(SCOUTFS_BRICK_SIZE); buf = malloc(SCOUTFS_BLOCK_SIZE); - if (!super || !buf) { + if (!buf) { ret = -errno; fprintf(stderr, "failed to allocate a block: %s (%d)\n", strerror(errno), errno); @@ -101,75 +80,23 @@ static int write_new_fs(char *path, int fd) goto out; } - total_blocks = st.st_size >> SCOUTFS_BLOCK_SHIFT; + total_logs = st.st_size >> SCOUTFS_LOG_SHIFT; root_key.inode = cpu_to_le64(SCOUTFS_ROOT_INO); root_key.type = SCOUTFS_INODE_KEY; root_key.offset = 0; - /* initialize the super */ - memset(super, 0, sizeof(struct scoutfs_super)); - pseudo_random_bytes(&super->hdr.fsid, sizeof(super->hdr.fsid)); - 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->ring_layout_block = cpu_to_le64(1); - super->ring_layout_seq = cpu_to_le64(1); - super->last_ring_brick = cpu_to_le64(1); - super->last_ring_seq = cpu_to_le64(1); - super->last_block_seq = cpu_to_le64(1); + /* super in log 0, first fs log block in log 1 */ + blkno = 1 << SCOUTFS_LOG_BLOCK_SHIFT; - /* the ring has a single block for now */ + /* write a single log block with the root inode item */ memset(buf, 0, SCOUTFS_BLOCK_SIZE); - rlo = buf; - rlo->hdr.fsid = super->hdr.fsid; - rlo->hdr.seq = super->ring_layout_seq; - rlo->nr_blocks = cpu_to_le32(1); - rlo->blocks[0] = cpu_to_le64(2); - - ret = write_block(fd, 1, &rlo->hdr); - if (ret) - goto out; - - /* log the root inode block manifest and free bitmap */ - memset(buf, 0, SCOUTFS_BLOCK_SIZE); - ring = buf; - ring->hdr.fsid = super->hdr.fsid; - ring->hdr.seq = super->last_ring_seq; - ring->nr_entries = cpu_to_le16(2); - ent = (void *)(ring + 1); - ent->type = SCOUTFS_RING_ADD_MANIFEST; - ent->len = cpu_to_le16(sizeof(*mani)); - mani = (void *)(ent + 1); - mani->block = cpu_to_le64(3); - mani->seq = super->last_block_seq; - mani->level = 0; - mani->first = root_key; - mani->last = root_key; - ent = (void *)(mani + 1); - ent->type = SCOUTFS_RING_BITMAP; - ent->len = cpu_to_le16(sizeof(*bm)); - bm = (void *)(ent + 1); - memset(bm->bits, 0xff, sizeof(bm->bits)); - /* the first three blocks are allocated */ - bm->bits[0] = cpu_to_le64(~7ULL); - bm->bits[1] = cpu_to_le64(~0ULL); - - ret = write_brick(fd, 2 << SCOUTFS_BLOCK_BRICK, &ring->hdr); - if (ret) - goto out; - - /* write a single lsm block with the root inode item */ - memset(buf, 0, SCOUTFS_BLOCK_SIZE); - lblk = buf; - lblk->hdr.fsid = super->hdr.fsid; - lblk->hdr.seq = super->last_block_seq; - lblk->first = root_key; - lblk->last = root_key; - lblk->nr_items = cpu_to_le32(1); - /* XXX set bloom */ - ihdr = (void *)((char *)(lblk + 1) + SCOUTFS_BLOOM_FILTER_BYTES); + iblk = buf; + iblk->hdr = hdr; + iblk->first = root_key; + iblk->last = root_key; + iblk->nr_items = cpu_to_le32(1); + ihdr = (void *)(iblk + 1); ihdr->key = root_key; ihdr->len = cpu_to_le16(sizeof(struct scoutfs_inode)); inode = (void *)(ihdr + 1); @@ -182,14 +109,67 @@ static int write_new_fs(char *path, int fd) inode->mtime.sec = inode->atime.sec; inode->mtime.nsec = inode->atime.nsec; - ret = write_block(fd, 3, &ring->hdr); + ret = write_block(fd, blkno, &iblk->hdr); if (ret) goto out; - /* write the two super bricks */ - for (i = 0; i < 2; i++) { - super->hdr.seq = cpu_to_le64(i); - ret = write_brick(fd, SCOUTFS_SUPER_BRICK + i, &super->hdr); + /* write the ring block whose manifest entry references the log block */ + memset(buf, 0, SCOUTFS_BLOCK_SIZE); + ring = buf; + ring->hdr = hdr; + ring->nr_entries = cpu_to_le16(2); + ent = (void *)(ring + 1); + ent->type = SCOUTFS_RING_ADD_MANIFEST; + ent->len = cpu_to_le16(sizeof(*mani)); + mani = (void *)(ent + 1); + mani->blkno = cpu_to_le64(blkno); + mani->seq = hdr.seq; + mani->level = 0; + mani->first = root_key; + mani->last = root_key; + ent = (void *)(mani + 1); + ent->type = SCOUTFS_RING_BITMAP; + ent->len = cpu_to_le16(sizeof(*bm)); + bm = (void *)(ent + 1); + memset(bm->bits, 0xff, sizeof(bm->bits)); + /* the first three blocks are allocated */ + bm->bits[0] = cpu_to_le64(~7ULL); + bm->bits[1] = cpu_to_le64(~0ULL); + + blkno += SCOUTFS_BLOCKS_PER_LOG; + ret = write_block(fd, blkno, &ring->hdr); + if (ret) + goto out; + + /* the ring has a single block for now */ + memset(buf, 0, SCOUTFS_BLOCK_SIZE); + lout = buf; + lout->hdr = hdr; + lout->nr_blocks = cpu_to_le32(1); + lout->blknos[0] = cpu_to_le64(blkno); + + blkno += SCOUTFS_BLOCKS_PER_LOG; + ret = write_block(fd, blkno, &lout->hdr); + if (ret) + goto out; + + /* write the two super blocks */ + memset(buf, 0, SCOUTFS_BLOCK_SIZE); + super = buf; + super->hdr = hdr; + super->id = cpu_to_le64(SCOUTFS_SUPER_ID); + uuid_generate(super->uuid); + super->total_logs = cpu_to_le64(total_logs); + super->ring_layout_blkno = cpu_to_le64(blkno); + super->ring_layout_nr_blocks = cpu_to_le64(1); + super->ring_layout_seq = hdr.seq; + super->ring_block = cpu_to_le64(0); + super->ring_nr_blocks = cpu_to_le64(1); + super->ring_seq = hdr.seq; + + for (i = 0; i < SCOUTFS_SUPER_NR; i++) { + super->hdr.seq = cpu_to_le64(i + 1); + ret = write_block(fd, SCOUTFS_SUPER_BLKNO + i, &super->hdr); if (ret) goto out; } @@ -204,14 +184,13 @@ static int write_new_fs(char *path, int fd) uuid_unparse(super->uuid, uuid_str); printf("Created scoutfs filesystem:\n" - " total blocks: %llu\n" + " total logs: %llu\n" " fsid: %llx\n" " uuid: %s\n", - total_blocks, le64_to_cpu(super->hdr.fsid), uuid_str); + total_logs, le64_to_cpu(super->hdr.fsid), uuid_str); ret = 0; out: - free(super); free(buf); return ret; } diff --git a/utils/src/print.c b/utils/src/print.c index df74bc67..503eacda 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -22,20 +22,19 @@ #define SKA(k) le64_to_cpu((k)->inode), (k)->type, \ le64_to_cpu((k)->offset) -static void *read_buf(int fd, u64 nr, size_t size) +static void *read_block(int fd, u64 blkno) { - off_t off = nr * size; ssize_t ret; void *buf; - buf = malloc(size); + buf = malloc(SCOUTFS_BLOCK_SIZE); if (!buf) return NULL; - ret = pread(fd, buf, size, off); - if (ret != size) { - fprintf(stderr, "read at blkno %llu (offset %llu) returned %zd: %s (%d)\n", - nr, (long long)off, ret, strerror(errno), errno); + ret = pread(fd, buf, SCOUTFS_BLOCK_SIZE, blkno << SCOUTFS_BLOCK_SHIFT); + if (ret != SCOUTFS_BLOCK_SIZE) { + fprintf(stderr, "read blkno %llu returned %zd: %s (%d)\n", + blkno, ret, strerror(errno), errno); free(buf); buf = NULL; } @@ -43,19 +42,9 @@ static void *read_buf(int fd, u64 nr, size_t size) return buf; } -static void *read_brick(int fd, u64 nr) +static void print_block_header(struct scoutfs_block_header *hdr) { - return read_buf(fd, nr, SCOUTFS_BRICK_SIZE); -} - -static void *read_block(int fd, u64 nr) -{ - return read_buf(fd, nr, SCOUTFS_BLOCK_SIZE); -} - -static void print_header(struct scoutfs_header *hdr, size_t size) -{ - u32 crc = crc_header(hdr, size); + u32 crc = crc_block(hdr); char valid_str[40]; if (crc != le32_to_cpu(hdr->crc)) @@ -67,19 +56,9 @@ static void print_header(struct scoutfs_header *hdr, size_t size) " crc: %08x %s\n" " fsid: %llx\n" " seq: %llu\n" - " nr: %llu\n", + " blkno: %llu\n", le32_to_cpu(hdr->crc), valid_str, le64_to_cpu(hdr->fsid), - le64_to_cpu(hdr->seq), le64_to_cpu(hdr->nr)); -} - -static void print_brick_header(struct scoutfs_header *hdr) -{ - return print_header(hdr, SCOUTFS_BRICK_SIZE); -} - -static void print_block_header(struct scoutfs_header *hdr) -{ - return print_header(hdr, SCOUTFS_BLOCK_SIZE); + le64_to_cpu(hdr->seq), le64_to_cpu(hdr->blkno)); } static void print_inode(struct scoutfs_inode *inode) @@ -108,12 +87,12 @@ static void print_inode(struct scoutfs_inode *inode) le32_to_cpu(inode->mtime.nsec)); } -static void print_item(struct scoutfs_item_header *ihdr, size_t off) +static void print_item(struct scoutfs_item_header *ihdr) { - printf(" item: &%zu\n" + printf(" item:\n" " key: "SKF"\n" " len: %u\n", - off, SKA(&ihdr->key), le16_to_cpu(ihdr->len)); + SKA(&ihdr->key), le16_to_cpu(ihdr->len)); switch(ihdr->key.type) { case SCOUTFS_INODE_KEY: @@ -122,49 +101,49 @@ static void print_item(struct scoutfs_item_header *ihdr, size_t off) } } -static int print_block(int fd, u64 nr) +static int print_item_block(int fd, u64 nr) { struct scoutfs_item_header *ihdr; - struct scoutfs_lsm_block *lblk; + struct scoutfs_item_block *iblk; size_t off; int i; - lblk = read_block(fd, nr); - if (!lblk) + iblk = read_block(fd, nr); + if (!iblk) return -ENOMEM; - printf("block: &%llu\n", le64_to_cpu(lblk->hdr.nr)); - print_block_header(&lblk->hdr); + printf("item block:\n"); + print_block_header(&iblk->hdr); printf(" first: "SKF"\n" " last: "SKF"\n" " nr_items: %u\n", - SKA(&lblk->first), SKA(&lblk->last), - le32_to_cpu(lblk->nr_items)); - off = (char *)(lblk + 1) - (char *)lblk + SCOUTFS_BLOOM_FILTER_BYTES; + SKA(&iblk->first), SKA(&iblk->last), + le32_to_cpu(iblk->nr_items)); - for (i = 0; i < le32_to_cpu(lblk->nr_items); i++) { - ihdr = (void *)((char *)lblk + off); - print_item(ihdr, off); + off = sizeof(struct scoutfs_item_block); + for (i = 0; i < le32_to_cpu(iblk->nr_items); i++) { + ihdr = (void *)((char *)iblk + off); + print_item(ihdr); off += sizeof(struct scoutfs_item_header) + le16_to_cpu(ihdr->len); } - free(lblk); + free(iblk); return 0; } -static int print_blocks(int fd, __le64 *live_blocks, u64 total_blocks) +static int print_log_blocks(int fd, __le64 *live_logs, u64 total_logs) { int ret = 0; int err; s64 nr; - while ((nr = find_first_le_bit(live_blocks, total_blocks)) >= 0) { - clear_le_bit(live_blocks, nr); + while ((nr = find_first_le_bit(live_logs, total_logs)) >= 0) { + clear_le_bit(live_logs, nr); - err = print_block(fd, nr); + err = print_item_block(fd, nr << SCOUTFS_LOG_BLOCK_SHIFT); if (!ret && err) ret = err; } @@ -186,32 +165,31 @@ static char *ent_type_str(u8 type) } } -static void print_ring_entry(int fd, struct scoutfs_ring_entry *ent, - size_t off) +static void print_ring_entry(int fd, struct scoutfs_ring_entry *ent) { struct scoutfs_ring_remove_manifest *rem; struct scoutfs_ring_add_manifest *add; struct scoutfs_ring_bitmap *bm; - printf(" entry: &%zu\n" + printf(" entry:\n" " type: %u # %s\n" " len: %u\n", - off, ent->type, ent_type_str(ent->type), le16_to_cpu(ent->len)); + ent->type, ent_type_str(ent->type), le16_to_cpu(ent->len)); switch(ent->type) { case SCOUTFS_RING_REMOVE_MANIFEST: rem = (void *)(ent + 1); - printf(" block: %llu\n", - le64_to_cpu(rem->block)); + printf(" blkno: %llu\n", + le64_to_cpu(rem->blkno)); break; case SCOUTFS_RING_ADD_MANIFEST: add = (void *)(ent + 1); - printf(" block: %llu\n" + printf(" blkno: %llu\n" " seq: %llu\n" " level: %u\n" " first: "SKF"\n" " last: "SKF"\n", - le64_to_cpu(add->block), le64_to_cpu(add->seq), + le64_to_cpu(add->blkno), le64_to_cpu(add->seq), add->level, SKA(&add->first), SKA(&add->last)); break; case SCOUTFS_RING_BITMAP: @@ -224,51 +202,51 @@ static void print_ring_entry(int fd, struct scoutfs_ring_entry *ent, } } -static void update_live_blocks(struct scoutfs_ring_entry *ent, - __le64 *live_blocks) +static void update_live_logs(struct scoutfs_ring_entry *ent, + __le64 *live_logs) { struct scoutfs_ring_remove_manifest *rem; struct scoutfs_ring_add_manifest *add; + u64 bit; switch(ent->type) { case SCOUTFS_RING_REMOVE_MANIFEST: rem = (void *)(ent + 1); - clear_le_bit(live_blocks, le64_to_cpu(rem->block)); + bit = le64_to_cpu(rem->blkno) >> SCOUTFS_LOG_BLOCK_SHIFT; + clear_le_bit(live_logs, bit); break; case SCOUTFS_RING_ADD_MANIFEST: add = (void *)(ent + 1); - set_le_bit(live_blocks, le64_to_cpu(add->block)); + bit = le64_to_cpu(add->blkno) >> SCOUTFS_LOG_BLOCK_SHIFT; + set_le_bit(live_logs, bit); break; } } -static int print_ring_block(int fd, u64 block_nr, __le64 *live_blocks) +static int print_ring_block(int fd, u64 blkno, __le64 *live_logs) { - struct scoutfs_ring_brick *ring; + struct scoutfs_ring_block *ring; struct scoutfs_ring_entry *ent; size_t off; int ret = 0; - u64 nr; int i; - /* XXX just printing the first brick for now */ + /* XXX just printing the first block for now */ - nr = block_nr << SCOUTFS_BLOCK_BRICK; - ring = read_brick(fd, nr); + ring = read_block(fd, blkno); if (!ring) return -ENOMEM; - printf("ring brick: &%llu\n", nr); - print_brick_header(&ring->hdr); + printf("ring block:\n"); + print_block_header(&ring->hdr); printf(" nr_entries: %u\n", le16_to_cpu(ring->nr_entries)); - off = sizeof(struct scoutfs_ring_brick); + off = sizeof(struct scoutfs_ring_block); for (i = 0; i < le16_to_cpu(ring->nr_entries); i++) { ent = (void *)((char *)ring + off); - update_live_blocks(ent, live_blocks); - - print_ring_entry(fd, ent, off); + update_live_logs(ent, live_logs); + print_ring_entry(fd, ent); off += sizeof(struct scoutfs_ring_entry) + le16_to_cpu(ent->len); @@ -278,95 +256,97 @@ static int print_ring_block(int fd, u64 block_nr, __le64 *live_blocks) return ret; } -static int print_ring_layout(int fd, u64 blkno, __le64 *live_blocks) +static int print_layout_block(int fd, u64 blkno, __le64 *live_logs) { - struct scoutfs_ring_layout *rlo; + struct scoutfs_layout_block *lout; int ret = 0; int err; int i; - rlo = read_block(fd, blkno); - if (!rlo) + lout = read_block(fd, blkno); + if (!lout) return -ENOMEM; - printf("ring layout: &%llu\n", blkno); - print_block_header(&rlo->hdr); - printf(" nr_blocks: %u\n", le32_to_cpu(rlo->nr_blocks)); + printf("layout block:\n"); + print_block_header(&lout->hdr); + printf(" nr_blocks: %u\n", le32_to_cpu(lout->nr_blocks)); - printf(" blocks: "); - for (i = 0; i < le32_to_cpu(rlo->nr_blocks); i++) - printf(" %llu\n", le64_to_cpu(rlo->blocks[i])); + printf(" blknos: "); + for (i = 0; i < le32_to_cpu(lout->nr_blocks); i++) + printf(" %llu\n", le64_to_cpu(lout->blknos[i])); - for (i = 0; i < le32_to_cpu(rlo->nr_blocks); i++) { - err = print_ring_block(fd, le64_to_cpu(rlo->blocks[i]), - live_blocks); + for (i = 0; i < le32_to_cpu(lout->nr_blocks); i++) { + err = print_ring_block(fd, le64_to_cpu(lout->blknos[i]), + live_logs); if (err && !ret) ret = err; } - free(rlo); + free(lout); return 0; } static int print_super_brick(int fd) { - struct scoutfs_super *super; + struct scoutfs_super_block *super; char uuid_str[37]; - __le64 *live_blocks; - u64 total_blocks; + __le64 *live_logs; + u64 total_logs; size_t bytes; int ret = 0; int err; /* XXX print both */ - super = read_brick(fd, SCOUTFS_SUPER_BRICK); + super = read_block(fd, SCOUTFS_SUPER_BLKNO); if (!super) return -ENOMEM; uuid_unparse(super->uuid, uuid_str); - total_blocks = le64_to_cpu(super->total_blocks); + total_logs = le64_to_cpu(super->total_logs); - printf("super: &%llu\n", le64_to_cpu(super->hdr.nr)); - print_brick_header(&super->hdr); + printf("super:\n"); + print_block_header(&super->hdr); printf(" id: %llx\n" " uuid: %s\n" - " total_blocks: %llu\n" - " ring_layout_block: %llu\n" + " total_logs: %llu\n" + " ring_layout_blkno: %llu\n" + " ring_layout_nr_blocks: %llu\n" " ring_layout_seq: %llu\n" - " last_ring_brick: %llu\n" - " last_ring_seq: %llu\n" - " last_block_seq: %llu\n", + " ring_block: %llu\n" + " ring_seq: %llu\n" + " ring_nr_blocks: %llu\n", le64_to_cpu(super->id), uuid_str, - total_blocks, - le64_to_cpu(super->ring_layout_block), + total_logs, + le64_to_cpu(super->ring_layout_blkno), + le64_to_cpu(super->ring_layout_nr_blocks), le64_to_cpu(super->ring_layout_seq), - le64_to_cpu(super->last_ring_brick), - le64_to_cpu(super->last_ring_seq), - le64_to_cpu(super->last_block_seq)); + le64_to_cpu(super->ring_block), + le64_to_cpu(super->ring_nr_blocks), + le64_to_cpu(super->ring_seq)); /* XXX by hand? */ - bytes = (total_blocks + 63) / 8; - live_blocks = malloc(bytes); - if (!live_blocks) { + bytes = (total_logs + 63) / 8; + live_logs = malloc(bytes); + if (!live_logs) { ret = -ENOMEM; goto out; } - memset(live_blocks, 0, bytes); + memset(live_logs, 0, bytes); - err = print_ring_layout(fd, le64_to_cpu(super->ring_layout_block), - live_blocks); + err = print_layout_block(fd, le64_to_cpu(super->ring_layout_blkno), + live_logs); if (err && !ret) ret = err; - err = print_blocks(fd, live_blocks, total_blocks); + err = print_log_blocks(fd, live_logs, total_logs); if (err && !ret) ret = err; out: free(super); - free(live_blocks); + free(live_logs); return ret; } From e9baa4559bdd60a8822528c2b9f65eed965ab0dd Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Tue, 23 Feb 2016 17:04:28 -0800 Subject: [PATCH 004/235] Introduce chunk and segment terminology The use of 'log' for all the large sizes was pretty confusing. Let's use 'chunk' to describe the large alloc size. Other things live in them as well as logs. Then use 'log segment' to describe the larger log structure stored in a chunk that's made up of all the little blocks. --- utils/src/format.h | 71 ++++++++++++++++----------------- utils/src/mkfs.c | 42 ++++++++++---------- utils/src/print.c | 97 +++++++++++++++++++++++----------------------- 3 files changed, 104 insertions(+), 106 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index 88d228a8..d27748c0 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -7,24 +7,24 @@ #define SCOUTFS_SUPER_ID 0x2e736674756f6373ULL /* "scoutfs." */ /* - * Structures are stored and referenced in fixed 4k chunks to - * simplify block buffer access at run time. + * Everything is stored in and addressed as 4k fixed size blocks. This + * avoids having to manage contiguous cpu mappings of larger blocks. + * Larger structures are read and written as multiple blocks. */ #define SCOUTFS_BLOCK_SHIFT 12 #define SCOUTFS_BLOCK_SIZE (1 << SCOUTFS_BLOCK_SHIFT) /* - * Logs are a logical structure that is made up of a fixed number of - * contiguously allocated blocks. + * The allocator works on larger chunks. Smaller metadata structures + * like the super blocks and the ring are stored in chunks. * - * The allocator manages log-sized regions. Smaller metadata blocks - * like the ring and super blocks are stored inside large log - * allocations. + * A log segment is a collection of smaller blocks (bloom filter, item blocks) + * stored in a chunk. */ -#define SCOUTFS_LOG_SHIFT 22 -#define SCOUTFS_LOG_SIZE (1 << SCOUTFS_LOG_SHIFT) -#define SCOUTFS_LOG_BLOCK_SHIFT (SCOUTFS_LOG_SHIFT - SCOUTFS_BLOCK_SHIFT) -#define SCOUTFS_BLOCKS_PER_LOG (1 << SCOUTFS_LOG_BLOCK_SHIFT) +#define SCOUTFS_CHUNK_SHIFT 22 +#define SCOUTFS_CHUNK_SIZE (1 << SCOUTFS_CHUNK_SHIFT) +#define SCOUTFS_CHUNK_BLOCK_SHIFT (SCOUTFS_CHUNK_SHIFT - SCOUTFS_BLOCK_SHIFT) +#define SCOUTFS_BLOCKS_PER_CHUNK (1 << SCOUTFS_CHUNK_BLOCK_SHIFT) /* * The super blocks leave some room at the start of the first block for @@ -50,22 +50,25 @@ struct scoutfs_block_header { #define SCOUTFS_UUID_BYTES 16 /* - * The super is stored in a pair of blocks in log 0 on the device. + * The super is stored in a pair of blocks in the first chunk on the + * device. * - * The ring layout blocks describe the location of the ring blocks. The - * ring start and length refers to the logical ring blocks within that - * storage which contain live data. + * The ring map blocks describe the chunks that make up the ring. + * + * The rest of the ring fields describe the state of the ring blocks + * that are stored in their chunks. The active portion of the ring + * describes the current state of the system and is replayed on mount. */ struct scoutfs_super_block { struct scoutfs_block_header hdr; __le64 id; __u8 uuid[SCOUTFS_UUID_BYTES]; - __le64 total_logs; - __le64 ring_layout_blkno; - __le64 ring_layout_nr_blocks; - __le64 ring_layout_seq; - __le64 ring_block; - __le64 ring_nr_blocks; + __le64 total_chunks; + __le64 ring_map_blkno; + __le64 ring_map_seq; + __le64 ring_first_block; + __le64 ring_active_blocks; + __le64 ring_total_blocks; __le64 ring_seq; } __packed; @@ -84,9 +87,9 @@ struct scoutfs_key { #define SCOUTFS_INODE_KEY 128 #define SCOUTFS_DIRENT_KEY 192 -struct scoutfs_layout_block { +struct scoutfs_ring_map_block { struct scoutfs_block_header hdr; - __le32 nr_blocks; + __le32 nr_chunks; __le64 blknos[0]; } __packed; @@ -96,13 +99,12 @@ struct scoutfs_ring_entry { } __packed; /* - * Ring blocks are 4k blocks stored inside the regions described by the - * ring layout block referenced by the super. + * Ring blocks are stored in chunks described by the ring map blocks. * - * The manifest entries describe the position of a given block in the - * manifest. They're keyed by the block number so that we can log - * movement of a block in the manifest with one log entry and we can log - * deletion with just the block number. + * The manifest entries describe the position of a given log segment in + * the manifest. They're keyed by the block number so that we can + * record movement of a log segment in the manifest with one ring entry + * and we can record deletion with just the block number. */ struct scoutfs_ring_block { struct scoutfs_block_header hdr; @@ -115,11 +117,6 @@ enum { SCOUTFS_RING_BITMAP, }; -/* - * Manifest entries are logged by their block number. This lets us log - * a change with one entry and a removal with a tiny block number - * without the key. - */ struct scoutfs_ring_remove_manifest { __le64 blkno; } __packed; @@ -145,9 +142,9 @@ struct scoutfs_ring_bitmap { } __packed; /* - * To start the logs are a trivial single item block. We'll flesh this out - * into larger blocks once the rest of the architecture is in - * place. + * To start the log segments are a trivial single item block. We'll + * flesh this out into larger blocks once the rest of the architecture + * is in place. */ struct scoutfs_item_block { struct scoutfs_block_header hdr; diff --git a/utils/src/mkfs.c b/utils/src/mkfs.c index 60eb83dd..36909acb 100644 --- a/utils/src/mkfs.c +++ b/utils/src/mkfs.c @@ -42,7 +42,7 @@ static int write_new_fs(char *path, int fd) struct scoutfs_super_block *super; struct scoutfs_block_header hdr; struct scoutfs_inode *inode; - struct scoutfs_layout_block *lout; + struct scoutfs_ring_map_block *map; struct scoutfs_ring_block *ring; struct scoutfs_ring_entry *ent; struct scoutfs_ring_add_manifest *mani; @@ -54,7 +54,7 @@ static int write_new_fs(char *path, int fd) char uuid_str[37]; struct stat st; unsigned int i; - u64 total_logs; + u64 total_chunks; u64 blkno; void *buf; int ret; @@ -80,14 +80,14 @@ static int write_new_fs(char *path, int fd) goto out; } - total_logs = st.st_size >> SCOUTFS_LOG_SHIFT; + total_chunks = st.st_size >> SCOUTFS_CHUNK_SHIFT; root_key.inode = cpu_to_le64(SCOUTFS_ROOT_INO); root_key.type = SCOUTFS_INODE_KEY; root_key.offset = 0; /* super in log 0, first fs log block in log 1 */ - blkno = 1 << SCOUTFS_LOG_BLOCK_SHIFT; + blkno = 1 << SCOUTFS_CHUNK_BLOCK_SHIFT; /* write a single log block with the root inode item */ memset(buf, 0, SCOUTFS_BLOCK_SIZE); @@ -136,20 +136,20 @@ static int write_new_fs(char *path, int fd) bm->bits[0] = cpu_to_le64(~7ULL); bm->bits[1] = cpu_to_le64(~0ULL); - blkno += SCOUTFS_BLOCKS_PER_LOG; + blkno += SCOUTFS_BLOCKS_PER_CHUNK; ret = write_block(fd, blkno, &ring->hdr); if (ret) goto out; - /* the ring has a single block for now */ + /* the ring has a single chunk for now */ memset(buf, 0, SCOUTFS_BLOCK_SIZE); - lout = buf; - lout->hdr = hdr; - lout->nr_blocks = cpu_to_le32(1); - lout->blknos[0] = cpu_to_le64(blkno); + map = buf; + map->hdr = hdr; + map->nr_chunks = cpu_to_le32(1); + map->blknos[0] = cpu_to_le64(blkno); - blkno += SCOUTFS_BLOCKS_PER_LOG; - ret = write_block(fd, blkno, &lout->hdr); + blkno += SCOUTFS_BLOCKS_PER_CHUNK; + ret = write_block(fd, blkno, &map->hdr); if (ret) goto out; @@ -159,12 +159,12 @@ static int write_new_fs(char *path, int fd) super->hdr = hdr; super->id = cpu_to_le64(SCOUTFS_SUPER_ID); uuid_generate(super->uuid); - super->total_logs = cpu_to_le64(total_logs); - super->ring_layout_blkno = cpu_to_le64(blkno); - super->ring_layout_nr_blocks = cpu_to_le64(1); - super->ring_layout_seq = hdr.seq; - super->ring_block = cpu_to_le64(0); - super->ring_nr_blocks = cpu_to_le64(1); + super->total_chunks = cpu_to_le64(total_chunks); + super->ring_map_blkno = cpu_to_le64(blkno); + super->ring_map_seq = hdr.seq; + super->ring_first_block = cpu_to_le64(0); + super->ring_active_blocks = cpu_to_le64(1); + super->ring_total_blocks = cpu_to_le64(SCOUTFS_BLOCKS_PER_CHUNK); super->ring_seq = hdr.seq; for (i = 0; i < SCOUTFS_SUPER_NR; i++) { @@ -184,10 +184,12 @@ static int write_new_fs(char *path, int fd) uuid_unparse(super->uuid, uuid_str); printf("Created scoutfs filesystem:\n" - " total logs: %llu\n" + " chunk bytes: %u\n" + " total chunks: %llu\n" " fsid: %llx\n" " uuid: %s\n", - total_logs, le64_to_cpu(super->hdr.fsid), uuid_str); + SCOUTFS_CHUNK_SIZE, total_chunks, + le64_to_cpu(super->hdr.fsid), uuid_str); ret = 0; out: diff --git a/utils/src/print.c b/utils/src/print.c index 503eacda..324db059 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -134,16 +134,16 @@ static int print_item_block(int fd, u64 nr) return 0; } -static int print_log_blocks(int fd, __le64 *live_logs, u64 total_logs) +static int print_log_segments(int fd, __le64 *log_segs, u64 total_chunks) { int ret = 0; int err; s64 nr; - while ((nr = find_first_le_bit(live_logs, total_logs)) >= 0) { - clear_le_bit(live_logs, nr); + while ((nr = find_first_le_bit(log_segs, total_chunks)) >= 0) { + clear_le_bit(log_segs, nr); - err = print_item_block(fd, nr << SCOUTFS_LOG_BLOCK_SHIFT); + err = print_item_block(fd, nr << SCOUTFS_CHUNK_BLOCK_SHIFT); if (!ret && err) ret = err; } @@ -202,8 +202,8 @@ static void print_ring_entry(int fd, struct scoutfs_ring_entry *ent) } } -static void update_live_logs(struct scoutfs_ring_entry *ent, - __le64 *live_logs) +static void update_log_segs(struct scoutfs_ring_entry *ent, + __le64 *log_segs) { struct scoutfs_ring_remove_manifest *rem; struct scoutfs_ring_add_manifest *add; @@ -212,18 +212,18 @@ static void update_live_logs(struct scoutfs_ring_entry *ent, switch(ent->type) { case SCOUTFS_RING_REMOVE_MANIFEST: rem = (void *)(ent + 1); - bit = le64_to_cpu(rem->blkno) >> SCOUTFS_LOG_BLOCK_SHIFT; - clear_le_bit(live_logs, bit); + bit = le64_to_cpu(rem->blkno) >> SCOUTFS_CHUNK_BLOCK_SHIFT; + clear_le_bit(log_segs, bit); break; case SCOUTFS_RING_ADD_MANIFEST: add = (void *)(ent + 1); - bit = le64_to_cpu(add->blkno) >> SCOUTFS_LOG_BLOCK_SHIFT; - set_le_bit(live_logs, bit); + bit = le64_to_cpu(add->blkno) >> SCOUTFS_CHUNK_BLOCK_SHIFT; + set_le_bit(log_segs, bit); break; } } -static int print_ring_block(int fd, u64 blkno, __le64 *live_logs) +static int print_ring_block(int fd, u64 blkno, __le64 *log_segs) { struct scoutfs_ring_block *ring; struct scoutfs_ring_entry *ent; @@ -245,7 +245,7 @@ static int print_ring_block(int fd, u64 blkno, __le64 *live_logs) for (i = 0; i < le16_to_cpu(ring->nr_entries); i++) { ent = (void *)((char *)ring + off); - update_live_logs(ent, live_logs); + update_log_segs(ent, log_segs); print_ring_entry(fd, ent); off += sizeof(struct scoutfs_ring_entry) + @@ -256,33 +256,33 @@ static int print_ring_block(int fd, u64 blkno, __le64 *live_logs) return ret; } -static int print_layout_block(int fd, u64 blkno, __le64 *live_logs) +static int print_map_block(int fd, u64 blkno, __le64 *log_segs) { - struct scoutfs_layout_block *lout; + struct scoutfs_ring_map_block *map; int ret = 0; int err; int i; - lout = read_block(fd, blkno); - if (!lout) + map = read_block(fd, blkno); + if (!map) return -ENOMEM; - printf("layout block:\n"); - print_block_header(&lout->hdr); - printf(" nr_blocks: %u\n", le32_to_cpu(lout->nr_blocks)); + printf("map block:\n"); + print_block_header(&map->hdr); + printf(" nr_chunks: %u\n", le32_to_cpu(map->nr_chunks)); printf(" blknos: "); - for (i = 0; i < le32_to_cpu(lout->nr_blocks); i++) - printf(" %llu\n", le64_to_cpu(lout->blknos[i])); + for (i = 0; i < le32_to_cpu(map->nr_chunks); i++) + printf(" %llu\n", le64_to_cpu(map->blknos[i])); - for (i = 0; i < le32_to_cpu(lout->nr_blocks); i++) { - err = print_ring_block(fd, le64_to_cpu(lout->blknos[i]), - live_logs); + for (i = 0; i < le32_to_cpu(map->nr_chunks); i++) { + err = print_ring_block(fd, le64_to_cpu(map->blknos[i]), + log_segs); if (err && !ret) ret = err; } - free(lout); + free(map); return 0; } @@ -290,8 +290,8 @@ static int print_super_brick(int fd) { struct scoutfs_super_block *super; char uuid_str[37]; - __le64 *live_logs; - u64 total_logs; + __le64 *log_segs; + u64 total_chunks; size_t bytes; int ret = 0; int err; @@ -303,50 +303,49 @@ static int print_super_brick(int fd) uuid_unparse(super->uuid, uuid_str); - total_logs = le64_to_cpu(super->total_logs); + total_chunks = le64_to_cpu(super->total_chunks); printf("super:\n"); print_block_header(&super->hdr); printf(" id: %llx\n" " uuid: %s\n" - " total_logs: %llu\n" - " ring_layout_blkno: %llu\n" - " ring_layout_nr_blocks: %llu\n" - " ring_layout_seq: %llu\n" - " ring_block: %llu\n" - " ring_seq: %llu\n" - " ring_nr_blocks: %llu\n", + " total_chunks: %llu\n" + " ring_map_blkno: %llu\n" + " ring_map_seq: %llu\n" + " ring_first_block: %llu\n" + " ring_active_blocks: %llu\n" + " ring_total_blocks: %llu\n" + " ring_seq: %llu\n", le64_to_cpu(super->id), uuid_str, - total_logs, - le64_to_cpu(super->ring_layout_blkno), - le64_to_cpu(super->ring_layout_nr_blocks), - le64_to_cpu(super->ring_layout_seq), - le64_to_cpu(super->ring_block), - le64_to_cpu(super->ring_nr_blocks), + total_chunks, + le64_to_cpu(super->ring_map_blkno), + le64_to_cpu(super->ring_map_seq), + le64_to_cpu(super->ring_first_block), + le64_to_cpu(super->ring_active_blocks), + le64_to_cpu(super->ring_total_blocks), le64_to_cpu(super->ring_seq)); /* XXX by hand? */ - bytes = (total_logs + 63) / 8; - live_logs = malloc(bytes); - if (!live_logs) { + bytes = (total_chunks + 63) / 8; + log_segs = malloc(bytes); + if (!log_segs) { ret = -ENOMEM; goto out; } - memset(live_logs, 0, bytes); + memset(log_segs, 0, bytes); - err = print_layout_block(fd, le64_to_cpu(super->ring_layout_blkno), - live_logs); + err = print_map_block(fd, le64_to_cpu(super->ring_map_blkno), log_segs); if (err && !ret) ret = err; - err = print_log_blocks(fd, live_logs, total_logs); + err = print_log_segments(fd, log_segs, total_chunks); if (err && !ret) ret = err; out: free(super); - free(live_logs); + free(log_segs); return ret; } From 906c0186bc0e47f897a2912d3941940a8bec58a5 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Thu, 25 Feb 2016 22:40:48 -0800 Subject: [PATCH 005/235] Get path size with stat or ioctl If we're making a file system in a real device then we need to get the device size with an ioctl. --- utils/src/dev.c | 42 ++++++++++++++++++++++++++++++++++++++++++ utils/src/dev.h | 6 ++++++ utils/src/mkfs.c | 9 +++++---- 3 files changed, 53 insertions(+), 4 deletions(-) create mode 100644 utils/src/dev.c create mode 100644 utils/src/dev.h diff --git a/utils/src/dev.c b/utils/src/dev.c new file mode 100644 index 00000000..f1fdaada --- /dev/null +++ b/utils/src/dev.c @@ -0,0 +1,42 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sparse.h" +#include "dev.h" + +int device_size(char *path, int fd, u64 *size) +{ + struct stat st; + int ret; + + if (fstat(fd, &st)) { + ret = -errno; + fprintf(stderr, "failed to stat '%s': %s (%d)\n", + path, strerror(errno), errno); + return ret; + } + + if (S_ISREG(st.st_mode)) { + *size = st.st_size; + } else if (S_ISBLK(st.st_mode)) { + if (ioctl(fd, BLKGETSIZE64, size)) { + ret = -errno; + fprintf(stderr, "BLKGETSIZE64 failed '%s': %s (%d)\n", + path, strerror(errno), errno); + return ret; + } + } else { + fprintf(stderr, "path isn't regular or device file '%s'\n", + path); + return -EINVAL; + } + + return 0; +} + diff --git a/utils/src/dev.h b/utils/src/dev.h new file mode 100644 index 00000000..1fcf92fa --- /dev/null +++ b/utils/src/dev.h @@ -0,0 +1,6 @@ +#ifndef _DEV_H_ +#define _DEV_H_ + +int device_size(char *path, int fd, u64 *size); + +#endif diff --git a/utils/src/mkfs.c b/utils/src/mkfs.c index 36909acb..f42fbe04 100644 --- a/utils/src/mkfs.c +++ b/utils/src/mkfs.c @@ -16,6 +16,7 @@ #include "format.h" #include "crc.h" #include "rand.h" +#include "dev.h" /* * Update the block's header and write it out. @@ -52,8 +53,8 @@ static int write_new_fs(char *path, int fd) struct scoutfs_key root_key; struct timeval tv; char uuid_str[37]; - struct stat st; unsigned int i; + u64 size; u64 total_chunks; u64 blkno; void *buf; @@ -73,14 +74,14 @@ static int write_new_fs(char *path, int fd) goto out; } - if (fstat(fd, &st)) { - ret = -errno; + ret = device_size(path, fd, &size); + if (ret) { fprintf(stderr, "failed to stat '%s': %s (%d)\n", path, strerror(errno), errno); goto out; } - total_chunks = st.st_size >> SCOUTFS_CHUNK_SHIFT; + total_chunks = size >> SCOUTFS_CHUNK_SHIFT; root_key.inode = cpu_to_le64(SCOUTFS_ROOT_INO); root_key.type = SCOUTFS_INODE_KEY; From d8f76cb893091d4b2e1f863dcaa1aa0851337cf7 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Thu, 25 Feb 2016 22:45:06 -0800 Subject: [PATCH 006/235] Minor ring manifest format updates Update to the format changes that were made while implementing ring replay in the kernel. --- utils/src/format.h | 22 +++++++++++++++------- utils/src/mkfs.c | 2 +- utils/src/print.c | 38 +++++++++++++++++++------------------- 3 files changed, 35 insertions(+), 27 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index d27748c0..bafaef80 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -24,6 +24,7 @@ #define SCOUTFS_CHUNK_SHIFT 22 #define SCOUTFS_CHUNK_SIZE (1 << SCOUTFS_CHUNK_SHIFT) #define SCOUTFS_CHUNK_BLOCK_SHIFT (SCOUTFS_CHUNK_SHIFT - SCOUTFS_BLOCK_SHIFT) +#define SCOUTFS_CHUNK_BLOCK_MASK ((1 << SCOUTFS_CHUNK_BLOCK_SHIFT) - 1) #define SCOUTFS_BLOCKS_PER_CHUNK (1 << SCOUTFS_CHUNK_BLOCK_SHIFT) /* @@ -93,6 +94,10 @@ struct scoutfs_ring_map_block { __le64 blknos[0]; } __packed; +#define SCOUTFS_RING_MAP_BLOCKS \ + ((SCOUTFS_BLOCK_SIZE - sizeof(struct scoutfs_ring_map_block)) / \ + sizeof(__le64)) + struct scoutfs_ring_entry { u8 type; __le16 len; @@ -112,15 +117,11 @@ struct scoutfs_ring_block { } __packed; enum { - SCOUTFS_RING_REMOVE_MANIFEST = 0, - SCOUTFS_RING_ADD_MANIFEST, + SCOUTFS_RING_ADD_MANIFEST = 0, + SCOUTFS_RING_DEL_MANIFEST, SCOUTFS_RING_BITMAP, }; -struct scoutfs_ring_remove_manifest { - __le64 blkno; -} __packed; - /* * Including both keys might make the manifest too large. It might be * better to only include one key and infer a block's range from the @@ -128,7 +129,7 @@ struct scoutfs_ring_remove_manifest { * isn't unused key space between blocks in a level. We might search * blocks when we didn't need to. */ -struct scoutfs_ring_add_manifest { +struct scoutfs_ring_manifest_entry { __le64 blkno; __le64 seq; __u8 level; @@ -136,6 +137,13 @@ struct scoutfs_ring_add_manifest { struct scoutfs_key last; } __packed; +struct scoutfs_ring_del_manifest { + __le64 blkno; +} __packed; + +/* 2^22 * 10^13 > 2^64 */ +#define SCOUTFS_MAX_LEVEL 13 + struct scoutfs_ring_bitmap { __le32 offset; __le64 bits[2]; diff --git a/utils/src/mkfs.c b/utils/src/mkfs.c index f42fbe04..396b3997 100644 --- a/utils/src/mkfs.c +++ b/utils/src/mkfs.c @@ -46,7 +46,7 @@ static int write_new_fs(char *path, int fd) struct scoutfs_ring_map_block *map; struct scoutfs_ring_block *ring; struct scoutfs_ring_entry *ent; - struct scoutfs_ring_add_manifest *mani; + struct scoutfs_ring_manifest_entry *mani; struct scoutfs_ring_bitmap *bm; struct scoutfs_item_block *iblk; struct scoutfs_item_header *ihdr; diff --git a/utils/src/print.c b/utils/src/print.c index 324db059..f00ef3e3 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -154,10 +154,10 @@ static int print_log_segments(int fd, __le64 *log_segs, u64 total_chunks) static char *ent_type_str(u8 type) { switch (type) { - case SCOUTFS_RING_REMOVE_MANIFEST: - return "REMOVE_MANIFEST"; case SCOUTFS_RING_ADD_MANIFEST: return "ADD_MANIFEST"; + case SCOUTFS_RING_DEL_MANIFEST: + return "DEL_MANIFEST"; case SCOUTFS_RING_BITMAP: return "BITMAP"; default: @@ -167,8 +167,8 @@ static char *ent_type_str(u8 type) static void print_ring_entry(int fd, struct scoutfs_ring_entry *ent) { - struct scoutfs_ring_remove_manifest *rem; - struct scoutfs_ring_add_manifest *add; + struct scoutfs_ring_manifest_entry *ment; + struct scoutfs_ring_del_manifest *del; struct scoutfs_ring_bitmap *bm; printf(" entry:\n" @@ -177,20 +177,20 @@ static void print_ring_entry(int fd, struct scoutfs_ring_entry *ent) ent->type, ent_type_str(ent->type), le16_to_cpu(ent->len)); switch(ent->type) { - case SCOUTFS_RING_REMOVE_MANIFEST: - rem = (void *)(ent + 1); - printf(" blkno: %llu\n", - le64_to_cpu(rem->blkno)); - break; case SCOUTFS_RING_ADD_MANIFEST: - add = (void *)(ent + 1); + ment = (void *)(ent + 1); printf(" blkno: %llu\n" " seq: %llu\n" " level: %u\n" " first: "SKF"\n" " last: "SKF"\n", - le64_to_cpu(add->blkno), le64_to_cpu(add->seq), - add->level, SKA(&add->first), SKA(&add->last)); + le64_to_cpu(ment->blkno), le64_to_cpu(ment->seq), + ment->level, SKA(&ment->first), SKA(&ment->last)); + break; + case SCOUTFS_RING_DEL_MANIFEST: + del = (void *)(ent + 1); + printf(" blkno: %llu\n", + le64_to_cpu(del->blkno)); break; case SCOUTFS_RING_BITMAP: bm = (void *)(ent + 1); @@ -205,21 +205,21 @@ static void print_ring_entry(int fd, struct scoutfs_ring_entry *ent) static void update_log_segs(struct scoutfs_ring_entry *ent, __le64 *log_segs) { - struct scoutfs_ring_remove_manifest *rem; - struct scoutfs_ring_add_manifest *add; + struct scoutfs_ring_manifest_entry *add; + struct scoutfs_ring_del_manifest *del; u64 bit; switch(ent->type) { - case SCOUTFS_RING_REMOVE_MANIFEST: - rem = (void *)(ent + 1); - bit = le64_to_cpu(rem->blkno) >> SCOUTFS_CHUNK_BLOCK_SHIFT; - clear_le_bit(log_segs, bit); - break; case SCOUTFS_RING_ADD_MANIFEST: add = (void *)(ent + 1); bit = le64_to_cpu(add->blkno) >> SCOUTFS_CHUNK_BLOCK_SHIFT; set_le_bit(log_segs, bit); break; + case SCOUTFS_RING_DEL_MANIFEST: + del = (void *)(ent + 1); + bit = le64_to_cpu(del->blkno) >> SCOUTFS_CHUNK_BLOCK_SHIFT; + clear_le_bit(log_segs, bit); + break; } } From e59d0af19936b23c788fc717f7f4fda7c4d4a537 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Thu, 17 Mar 2016 16:28:44 -0700 Subject: [PATCH 007/235] Print full map and ring blocks In the first pass we'd only printed the first map and ring blocks. This reads the number of used map blocks into an allocation large enough for the maximum number of map blocks. Then we use the block numbers from the map blocks to print the active ring blocks which are described by the super. Signed-off-by: Zach Brown --- utils/src/print.c | 95 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 70 insertions(+), 25 deletions(-) diff --git a/utils/src/print.c b/utils/src/print.c index f00ef3e3..fe64f18a 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -256,33 +256,68 @@ static int print_ring_block(int fd, u64 blkno, __le64 *log_segs) return ret; } -static int print_map_block(int fd, u64 blkno, __le64 *log_segs) +/* + * Print all the active ring blocks that are referenced by the super + * and which were mapped by the map blocks that we printed. + */ +static int print_ring_blocks(int fd, struct scoutfs_super_block *super, + u64 *ring_blknos, __le64 *log_segs) { - struct scoutfs_ring_map_block *map; + u64 block; + u64 blkno; + u64 i; int ret = 0; int err; - int i; - map = read_block(fd, blkno); - if (!map) - return -ENOMEM; + block = le64_to_cpu(super->ring_first_block); - printf("map block:\n"); - print_block_header(&map->hdr); - printf(" nr_chunks: %u\n", le32_to_cpu(map->nr_chunks)); + for (i = 0; i < le64_to_cpu(super->ring_active_blocks); i++) { + blkno = ring_blknos[block >> SCOUTFS_CHUNK_BLOCK_SHIFT] + + (block & SCOUTFS_CHUNK_BLOCK_MASK); - printf(" blknos: "); - for (i = 0; i < le32_to_cpu(map->nr_chunks); i++) - printf(" %llu\n", le64_to_cpu(map->blknos[i])); - - for (i = 0; i < le32_to_cpu(map->nr_chunks); i++) { - err = print_ring_block(fd, le64_to_cpu(map->blknos[i]), - log_segs); + err = print_ring_block(fd, blkno, log_segs); if (err && !ret) ret = err; + + if (++block == le64_to_cpu(super->ring_total_blocks)) + block = 0; + } + + return ret; +} + +/* + * print a chunk's worth of map blocks and stop if we hit a partial + * block. + */ +static int print_map_blocks(int fd, u64 blkno, u64 *ring_blknos) +{ + struct scoutfs_ring_map_block *map; + int r = 0; + int b; + int i; + + for (b = 0; SCOUTFS_BLOCKS_PER_CHUNK; b++) { + map = read_block(fd, blkno + b); + if (!map) + return -ENOMEM; + + printf("map block:\n"); + print_block_header(&map->hdr); + printf(" nr_chunks: %u\n", le32_to_cpu(map->nr_chunks)); + + printf(" blknos: "); + for (i = 0; i < le32_to_cpu(map->nr_chunks); i++, r++) { + printf(" %llu\n", le64_to_cpu(map->blknos[i])); + ring_blknos[r] = le64_to_cpu(map->blknos[i]); + } + + free(map); + + if (i != SCOUTFS_RING_MAP_BLOCKS) + break; } - free(map); return 0; } @@ -291,8 +326,8 @@ static int print_super_brick(int fd) struct scoutfs_super_block *super; char uuid_str[37]; __le64 *log_segs; + u64 *ring_blknos; u64 total_chunks; - size_t bytes; int ret = 0; int err; @@ -326,16 +361,23 @@ static int print_super_brick(int fd) le64_to_cpu(super->ring_total_blocks), le64_to_cpu(super->ring_seq)); - /* XXX by hand? */ - bytes = (total_chunks + 63) / 8; - log_segs = malloc(bytes); - if (!log_segs) { + /* + * Allocate a bitmap big enough to describe all the chunks and + * we can have at most a full chunk worth of map blocks. + */ + log_segs = calloc(1, (total_chunks + 63) / 8); + ring_blknos = calloc(1, SCOUTFS_CHUNK_SIZE); + if (!log_segs || !ring_blknos) { ret = -ENOMEM; goto out; } - memset(log_segs, 0, bytes); - err = print_map_block(fd, le64_to_cpu(super->ring_map_blkno), log_segs); + err = print_map_blocks(fd, le64_to_cpu(super->ring_map_blkno), + ring_blknos); + if (err && !ret) + ret = err; + + err = print_ring_blocks(fd, super, ring_blknos, log_segs); if (err && !ret) ret = err; @@ -344,8 +386,11 @@ static int print_super_brick(int fd) ret = err; out: + if (log_segs) + free(log_segs); + if (ring_blknos) + free(ring_blknos); free(super); - free(log_segs); return ret; } From a0a3ef96757a25a9a5a11f6d2d7e60a92b55ccc6 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Thu, 17 Mar 2016 17:05:24 -0700 Subject: [PATCH 008/235] Mark all mkfs chunks allocated in bitmap The initial bitmap entry written in the ring by mkfs was off by one. Three chunks were written but the 0th chunk is also free for the supers. It has to mark the first four chunks as allocated. Signed-off-by: Zach Brown --- utils/src/mkfs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/utils/src/mkfs.c b/utils/src/mkfs.c index 396b3997..ec181656 100644 --- a/utils/src/mkfs.c +++ b/utils/src/mkfs.c @@ -133,8 +133,8 @@ static int write_new_fs(char *path, int fd) ent->len = cpu_to_le16(sizeof(*bm)); bm = (void *)(ent + 1); memset(bm->bits, 0xff, sizeof(bm->bits)); - /* the first three blocks are allocated */ - bm->bits[0] = cpu_to_le64(~7ULL); + /* the first four chunks are allocated */ + bm->bits[0] = cpu_to_le64(~15ULL); bm->bits[1] = cpu_to_le64(~0ULL); blkno += SCOUTFS_BLOCKS_PER_CHUNK; From f3de3b1817493858074db7ed4b3c39cf15b2edce Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Wed, 23 Mar 2016 13:58:59 -0700 Subject: [PATCH 009/235] Add DIV_ROUND_UP() to util.h We're going to need this in some upcoming format.h changes. Signed-off-by: Zach Brown --- utils/src/util.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/utils/src/util.h b/utils/src/util.h index f4dec4f0..daa28a98 100644 --- a/utils/src/util.h +++ b/utils/src/util.h @@ -50,6 +50,8 @@ do { \ ((a) + _b - 1) & ~(_b - 1); \ }) +#define DIV_ROUND_UP(x, y) (((x) + (y) - 1) / (y)) + #ifndef offsetof #define offsetof(type, memb) ((unsigned long)&((type *)0)->memb) #endif From 8471134328b646f4ba5b9b8de32e8e55f6ed3f2b Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Wed, 23 Mar 2016 14:00:07 -0700 Subject: [PATCH 010/235] Add trivial set_bit_le in bitops.h We're going to need to start setting bloom filters bits in mkfs so we'll add this trivial inline. It might grow later. Signed-off-by: Zach Brown --- utils/src/bitops.h | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 utils/src/bitops.h diff --git a/utils/src/bitops.h b/utils/src/bitops.h new file mode 100644 index 00000000..dfe46a1c --- /dev/null +++ b/utils/src/bitops.h @@ -0,0 +1,20 @@ +#ifndef _BITOPS_H_ +#define _BITOPS_H_ + +#define BITS_PER_LONG (sizeof(long) * 8) +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define BITOP_LE_SWIZZLE 0 +#else +#define BITOP_LE_SWIZZLE ((BITS_PER_LONG-1) & ~0x7) +#endif + +static inline void set_bit_le(int nr, void *addr) +{ + u64 *dwords = addr; + + nr ^= BITOP_LE_SWIZZLE; + + dwords[nr / 64] |= 1 << (nr & 63); +} + +#endif From d0429e1c8845cdd1852598f6dbe3203b4fbd9c8e Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Wed, 23 Mar 2016 14:01:16 -0700 Subject: [PATCH 011/235] Add minimal bloom filter helpers mkfs just needs to initialize bloom filter blocks with the bits for the single root inode key. We can get away with these skeletal functions for now. Signed-off-by: Zach Brown --- utils/src/bloom.c | 66 +++++++++++++++++++++++++++++++++++++++++++++++ utils/src/bloom.h | 13 ++++++++++ 2 files changed, 79 insertions(+) create mode 100644 utils/src/bloom.c create mode 100644 utils/src/bloom.h diff --git a/utils/src/bloom.c b/utils/src/bloom.c new file mode 100644 index 00000000..22fa62a1 --- /dev/null +++ b/utils/src/bloom.c @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2016 Versity Software, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License v2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ +#include "sparse.h" +#include "util.h" +#include "format.h" +#include "bloom.h" +#include "crc.h" +#include "bitops.h" + +/* XXX garbage hack until we have siphash */ +static u32 bloom_hash(struct scoutfs_key *key, __le32 salt) +{ + return crc32c(le32_to_cpu(salt), key, sizeof(struct scoutfs_key)); +} + +/* + * Find the bits in the bloom filter for the given key. The caller calculates + * these once and uses them to test all the blocks. + */ +void scoutfs_calc_bloom_bits(struct scoutfs_bloom_bits *bits, + struct scoutfs_key *key, __le32 *salts) +{ + unsigned h_bits = 0; + unsigned s = 0; + u64 h = 0; + int i; + + for (i = 0; i < SCOUTFS_BLOOM_BITS; i++) { + if (h_bits < SCOUTFS_BLOOM_BIT_WIDTH) { + h = (h << 32) | bloom_hash(key, salts[s++]); + h += 32; + } + + bits->nr[i] = h & SCOUTFS_BLOOM_BIT_MASK; + h >>= SCOUTFS_BLOOM_BIT_WIDTH; + h_bits -= SCOUTFS_BLOOM_BIT_WIDTH; + } +} + +/* + * This interface is different than in the kernel because we don't + * have a block IO interface here yet. The caller gives us each + * bloom block and we set each bit that falls in the block. + */ +void scoutfs_set_bloom_bits(struct scoutfs_bloom_block *blm, unsigned int nr, + struct scoutfs_bloom_bits *bits) +{ + int i; + + for (i = 0; i < SCOUTFS_BLOOM_BITS; i++) { + if (nr == (bits->nr[i] / SCOUTFS_BLOOM_BITS_PER_BLOCK)) { + set_bit_le(bits->nr[i] % SCOUTFS_BLOOM_BITS_PER_BLOCK, + blm->bits); + } + } +} diff --git a/utils/src/bloom.h b/utils/src/bloom.h new file mode 100644 index 00000000..cad1639f --- /dev/null +++ b/utils/src/bloom.h @@ -0,0 +1,13 @@ +#ifndef _BLOOM_H_ +#define _BLOOM_H_ + +struct scoutfs_bloom_bits { + u32 nr[SCOUTFS_BLOOM_BITS]; +}; + +void scoutfs_calc_bloom_bits(struct scoutfs_bloom_bits *bits, + struct scoutfs_key *key, __le32 *salts); +void scoutfs_set_bloom_bits(struct scoutfs_bloom_block *blm, unsigned int nr, + struct scoutfs_bloom_bits *bits); + +#endif From 463f5e5a072b7f6ce5e6dfb463bb9e766ea28950 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Wed, 23 Mar 2016 15:16:58 -0700 Subject: [PATCH 012/235] Correctly store last random word pseudo_random_bytes() was accidentally copying the last partial long to the beggining of the buffer instead of the end. The final partial long bytes weren't being filled. Signed-off-by: Zach Brown --- utils/src/rand.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/src/rand.c b/utils/src/rand.c index 82b17e11..6305d660 100644 --- a/utils/src/rand.c +++ b/utils/src/rand.c @@ -25,6 +25,6 @@ void pseudo_random_bytes(void *data, unsigned int len) if (len) { __builtin_ia32_rdrand64_step(&tmp); - memcpy(data, &tmp, len); + memcpy(ll, &tmp, len); } } From 502783e1bc3094a2e0d461d895c8490ea19fbde2 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Wed, 23 Mar 2016 15:23:54 -0700 Subject: [PATCH 013/235] Update to segment format with skiplists and bloom Update to the format rev which has large log segments that start with bloom filter blocks, have items linked in a skip list, and item values stored at offsets in the block. Signed-off-by: Zach Brown --- utils/src/format.h | 48 +++++++++++++++--- utils/src/mkfs.c | 102 +++++++++++++++++++++++-------------- utils/src/print.c | 123 ++++++++++++++++++++++++++++++++++++--------- 3 files changed, 205 insertions(+), 68 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index bafaef80..efdfa6b9 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -13,6 +13,7 @@ */ #define SCOUTFS_BLOCK_SHIFT 12 #define SCOUTFS_BLOCK_SIZE (1 << SCOUTFS_BLOCK_SHIFT) +#define SCOUTFS_BLOCK_MASK (SCOUTFS_BLOCK_SIZE - 1) /* * The allocator works on larger chunks. Smaller metadata structures @@ -34,6 +35,19 @@ #define SCOUTFS_SUPER_BLKNO ((64 * 1024) >> SCOUTFS_BLOCK_SHIFT) #define SCOUTFS_SUPER_NR 2 +/* + * 7 bits in a ~76k bloom filter gives ~1% false positive for our max + * of 64k items. + * + * n = 65,536, p = 0.01 (1 in 100) → m = 628,167 (76.68KB), k = 7 + */ +#define SCOUTFS_BLOOM_BITS 7 +#define SCOUTFS_BLOOM_BIT_WIDTH 20 /* 2^20 > m */ +#define SCOUTFS_BLOOM_BIT_MASK ((1 << SCOUTFS_BLOOM_BIT_WIDTH) - 1) +#define SCOUTFS_BLOOM_BLOCKS ((76 * 1024) / SCOUTFS_BLOCK_SIZE) +#define SCOUTFS_BLOOM_SALTS \ + DIV_ROUND_UP(SCOUTFS_BLOOM_BITS * SCOUTFS_BLOOM_BIT_WIDTH, 32) + /* * This header is found at the start of every block so that we can * verify that it's what we were looking for. The crc and padding @@ -64,6 +78,7 @@ struct scoutfs_super_block { struct scoutfs_block_header hdr; __le64 id; __u8 uuid[SCOUTFS_UUID_BYTES]; + __le32 bloom_salts[SCOUTFS_BLOOM_SALTS]; __le64 total_chunks; __le64 ring_map_blkno; __le64 ring_map_seq; @@ -149,22 +164,43 @@ struct scoutfs_ring_bitmap { __le64 bits[2]; } __packed; + +struct scoutfs_bloom_block { + struct scoutfs_block_header hdr; + __le64 bits[0]; +} __packed; + +#define SCOUTFS_BLOOM_BITS_PER_BLOCK \ + (((SCOUTFS_BLOCK_SIZE - sizeof(struct scoutfs_block_header)) / 8) * 64) + /* - * To start the log segments are a trivial single item block. We'll - * flesh this out into larger blocks once the rest of the architecture - * is in place. + * Items in log segments are sorted in a skip list by their key. We + * have a rough limit of 64k items. + */ +#define SCOUTFS_SKIP_HEIGHT 16 +struct scoutfs_skip_root { + __le32 next[SCOUTFS_SKIP_HEIGHT]; +} __packed; + +/* + * An item block follows the bloom filters blocks at the start of a log + * segment chunk. Its skip list root references the item structs which + * reference the item values in the rest of the block. The references + * are byte offsets from the start of the chunk. */ struct scoutfs_item_block { struct scoutfs_block_header hdr; struct scoutfs_key first; struct scoutfs_key last; - __le32 nr_items; - /* struct scoutfs_item_header items[0] .. */ + struct scoutfs_skip_root skip_root; } __packed; -struct scoutfs_item_header { +struct scoutfs_item { struct scoutfs_key key; + __le32 offset; __le16 len; + u8 skip_height; + __le32 skip_next[0]; } __packed; struct scoutfs_timespec { diff --git a/utils/src/mkfs.c b/utils/src/mkfs.c index ec181656..c48de7f9 100644 --- a/utils/src/mkfs.c +++ b/utils/src/mkfs.c @@ -17,6 +17,8 @@ #include "crc.h" #include "rand.h" #include "dev.h" +#include "bloom.h" +#include "bitops.h" /* * Update the block's header and write it out. @@ -41,7 +43,6 @@ static int write_block(int fd, u64 blkno, struct scoutfs_block_header *hdr) static int write_new_fs(char *path, int fd) { struct scoutfs_super_block *super; - struct scoutfs_block_header hdr; struct scoutfs_inode *inode; struct scoutfs_ring_map_block *map; struct scoutfs_ring_block *ring; @@ -49,7 +50,9 @@ static int write_new_fs(char *path, int fd) struct scoutfs_ring_manifest_entry *mani; struct scoutfs_ring_bitmap *bm; struct scoutfs_item_block *iblk; - struct scoutfs_item_header *ihdr; + struct scoutfs_bloom_bits bits; + struct scoutfs_bloom_block *blm; + struct scoutfs_item *item; struct scoutfs_key root_key; struct timeval tv; char uuid_str[37]; @@ -61,13 +64,10 @@ static int write_new_fs(char *path, int fd) int ret; gettimeofday(&tv, NULL); - /* crc and blkno written for each write */ - hdr._pad = 0; - pseudo_random_bytes(&hdr.fsid, sizeof(hdr.fsid)); - hdr.seq = cpu_to_le64(1); buf = malloc(SCOUTFS_BLOCK_SIZE); - if (!buf) { + super = malloc(SCOUTFS_BLOCK_SIZE); + if (!buf || !super) { ret = -errno; fprintf(stderr, "failed to allocate a block: %s (%d)\n", strerror(errno), errno); @@ -87,20 +87,55 @@ static int write_new_fs(char *path, int fd) root_key.type = SCOUTFS_INODE_KEY; root_key.offset = 0; - /* super in log 0, first fs log block in log 1 */ + /* first chunk has super blocks, log segment chunk is next */ blkno = 1 << SCOUTFS_CHUNK_BLOCK_SHIFT; - /* write a single log block with the root inode item */ + /* first initialize the super so we can use it to build structures */ + memset(super, 0, SCOUTFS_BLOCK_SIZE); + pseudo_random_bytes(&super->hdr.fsid, sizeof(super->hdr.fsid)); + super->hdr.seq = cpu_to_le64(1); + super->id = cpu_to_le64(SCOUTFS_SUPER_ID); + uuid_generate(super->uuid); + pseudo_random_bytes(super->bloom_salts, sizeof(super->bloom_salts)); + super->total_chunks = cpu_to_le64(total_chunks); + super->ring_map_seq = super->hdr.seq; + super->ring_first_block = cpu_to_le64(0); + super->ring_active_blocks = cpu_to_le64(1); + super->ring_total_blocks = cpu_to_le64(SCOUTFS_BLOCKS_PER_CHUNK); + super->ring_seq = super->hdr.seq; + + /* + * There's only the root item so we check for its bloom bits as + * we write the bloom blocks. + */ + scoutfs_calc_bloom_bits(&bits, &root_key, super->bloom_salts); + for (i = 0; i < SCOUTFS_BLOOM_BLOCKS; i++) { + memset(buf, 0, SCOUTFS_BLOCK_SIZE); + blm = buf; + blm->hdr = super->hdr; + + scoutfs_set_bloom_bits(blm, i, &bits); + + ret = write_block(fd, blkno, &blm->hdr); + if (ret) + goto out; + blkno++; + } + + /* write a single log segment with the root inode item */ memset(buf, 0, SCOUTFS_BLOCK_SIZE); iblk = buf; - iblk->hdr = hdr; - iblk->first = root_key; - iblk->last = root_key; - iblk->nr_items = cpu_to_le32(1); - ihdr = (void *)(iblk + 1); - ihdr->key = root_key; - ihdr->len = cpu_to_le16(sizeof(struct scoutfs_inode)); - inode = (void *)(ihdr + 1); + iblk->hdr = super->hdr; + iblk->skip_root.next[0] = cpu_to_le32((SCOUTFS_BLOOM_BLOCKS << + SCOUTFS_BLOCK_SHIFT) + + sizeof(struct scoutfs_item_block)); + item = (void *)(iblk + 1); + item->key = root_key; + item->offset = cpu_to_le32(le32_to_cpu(iblk->skip_root.next[0]) + + sizeof(struct scoutfs_item)); + item->len = cpu_to_le16(sizeof(struct scoutfs_inode)); + item->skip_height = 1; + inode = (void *)(item + 1); inode->nlink = cpu_to_le32(2); inode->mode = cpu_to_le32(0755 | 0040000); inode->atime.sec = cpu_to_le64(tv.tv_sec); @@ -113,18 +148,19 @@ static int write_new_fs(char *path, int fd) ret = write_block(fd, blkno, &iblk->hdr); if (ret) goto out; + blkno = round_up(blkno, SCOUTFS_BLOCKS_PER_CHUNK); /* write the ring block whose manifest entry references the log block */ memset(buf, 0, SCOUTFS_BLOCK_SIZE); ring = buf; - ring->hdr = hdr; + ring->hdr = super->hdr; ring->nr_entries = cpu_to_le16(2); ent = (void *)(ring + 1); ent->type = SCOUTFS_RING_ADD_MANIFEST; ent->len = cpu_to_le16(sizeof(*mani)); mani = (void *)(ent + 1); - mani->blkno = cpu_to_le64(blkno); - mani->seq = hdr.seq; + mani->blkno = cpu_to_le64(blkno - SCOUTFS_BLOCKS_PER_CHUNK); + mani->seq = super->hdr.seq; mani->level = 0; mani->first = root_key; mani->last = root_key; @@ -137,37 +173,26 @@ static int write_new_fs(char *path, int fd) bm->bits[0] = cpu_to_le64(~15ULL); bm->bits[1] = cpu_to_le64(~0ULL); - blkno += SCOUTFS_BLOCKS_PER_CHUNK; ret = write_block(fd, blkno, &ring->hdr); if (ret) goto out; + blkno += SCOUTFS_BLOCKS_PER_CHUNK; /* the ring has a single chunk for now */ memset(buf, 0, SCOUTFS_BLOCK_SIZE); map = buf; - map->hdr = hdr; + map->hdr = super->hdr; map->nr_chunks = cpu_to_le32(1); - map->blknos[0] = cpu_to_le64(blkno); + map->blknos[0] = cpu_to_le64(blkno - SCOUTFS_BLOCKS_PER_CHUNK); - blkno += SCOUTFS_BLOCKS_PER_CHUNK; ret = write_block(fd, blkno, &map->hdr); if (ret) goto out; - /* write the two super blocks */ - memset(buf, 0, SCOUTFS_BLOCK_SIZE); - super = buf; - super->hdr = hdr; - super->id = cpu_to_le64(SCOUTFS_SUPER_ID); - uuid_generate(super->uuid); - super->total_chunks = cpu_to_le64(total_chunks); + /* make sure the super references everything we just wrote */ super->ring_map_blkno = cpu_to_le64(blkno); - super->ring_map_seq = hdr.seq; - super->ring_first_block = cpu_to_le64(0); - super->ring_active_blocks = cpu_to_le64(1); - super->ring_total_blocks = cpu_to_le64(SCOUTFS_BLOCKS_PER_CHUNK); - super->ring_seq = hdr.seq; + /* write the two super blocks */ for (i = 0; i < SCOUTFS_SUPER_NR; i++) { super->hdr.seq = cpu_to_le64(i + 1); ret = write_block(fd, SCOUTFS_SUPER_BLKNO + i, &super->hdr); @@ -194,7 +219,10 @@ static int write_new_fs(char *path, int fd) ret = 0; out: - free(buf); + if (super) + free(super); + if (buf) + free(buf); return ret; } diff --git a/utils/src/print.c b/utils/src/print.c index fe64f18a..0a174fce 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -42,6 +42,57 @@ static void *read_block(int fd, u64 blkno) return buf; } +static void *read_chunk(int fd, u64 blkno) +{ + ssize_t ret; + void *buf; + + buf = malloc(SCOUTFS_CHUNK_SIZE); + if (!buf) + return NULL; + + ret = pread(fd, buf, SCOUTFS_CHUNK_SIZE, blkno << SCOUTFS_BLOCK_SHIFT); + if (ret != SCOUTFS_CHUNK_SIZE) { + fprintf(stderr, "read blkno %llu returned %zd: %s (%d)\n", + blkno, ret, strerror(errno), errno); + free(buf); + buf = NULL; + } + + return buf; +} + +static void print_le32_list(int indent, __le32 *data, int nr) +{ + char *fmt; + int pos; + int len; + int i; + u32 d; + + printf("["); + + pos = indent; + for (i = 0; i < nr; i++) { + if (i + 1 < nr) + fmt = "%u, "; + else + fmt = "%u"; + + d = le32_to_cpu(data[i]); + len = snprintf(NULL, 0, fmt, d); + if (pos + len > 78) { + printf("\n%*c", indent, ' '); + pos = indent; + } + + printf(fmt, d); + pos += len; + } + + printf("]\n"); +} + static void print_block_header(struct scoutfs_block_header *hdr) { u32 crc = crc_block(hdr); @@ -87,49 +138,69 @@ static void print_inode(struct scoutfs_inode *inode) le32_to_cpu(inode->mtime.nsec)); } -static void print_item(struct scoutfs_item_header *ihdr) +static void print_item(struct scoutfs_item *item, void *val) { printf(" item:\n" " key: "SKF"\n" - " len: %u\n", - SKA(&ihdr->key), le16_to_cpu(ihdr->len)); + " offset: %u\n" + " len: %u\n" + " skip_height: %u\n" + " skip_next[]: ", + SKA(&item->key), + le32_to_cpu(item->offset), + le16_to_cpu(item->len), + item->skip_height); - switch(ihdr->key.type) { + print_le32_list(22, item->skip_next, item->skip_height); + + switch(item->key.type) { case SCOUTFS_INODE_KEY: - print_inode((void *)(ihdr + 1)); + print_inode(val); break; } } -static int print_item_block(int fd, u64 nr) +static int print_log_segment(int fd, u64 nr) { - struct scoutfs_item_header *ihdr; struct scoutfs_item_block *iblk; - size_t off; + struct scoutfs_bloom_block *blm; + struct scoutfs_item *item; + char *buf; + char *val; + __le32 next; int i; - iblk = read_block(fd, nr); - if (!iblk) + buf = read_chunk(fd, nr); + if (!buf) return -ENOMEM; + for (i = 0; i < SCOUTFS_BLOOM_BLOCKS; i++) { + + blm = (void *)(buf + (i << SCOUTFS_BLOCK_SHIFT)); + + printf("bloom block:\n"); + print_block_header(&blm->hdr); + } + + iblk = (void *)(buf + (SCOUTFS_BLOOM_BLOCKS << SCOUTFS_BLOCK_SHIFT)); + printf("item block:\n"); print_block_header(&iblk->hdr); printf(" first: "SKF"\n" " last: "SKF"\n" - " nr_items: %u\n", - SKA(&iblk->first), SKA(&iblk->last), - le32_to_cpu(iblk->nr_items)); + " skip_root.next[]: ", + SKA(&iblk->first), SKA(&iblk->last)); + print_le32_list(23, iblk->skip_root.next, SCOUTFS_SKIP_HEIGHT); - off = sizeof(struct scoutfs_item_block); - for (i = 0; i < le32_to_cpu(iblk->nr_items); i++) { - ihdr = (void *)((char *)iblk + off); - print_item(ihdr); - - off += sizeof(struct scoutfs_item_header) + - le16_to_cpu(ihdr->len); + next = iblk->skip_root.next[0]; + while (next) { + item = (void *)(buf + le32_to_cpu(next)); + val = (void *)(buf + le32_to_cpu(item->offset)); + print_item(item, val); + next = item->skip_next[0]; } - free(iblk); + free(buf); return 0; } @@ -143,7 +214,7 @@ static int print_log_segments(int fd, __le64 *log_segs, u64 total_chunks) while ((nr = find_first_le_bit(log_segs, total_chunks)) >= 0) { clear_le_bit(log_segs, nr); - err = print_item_block(fd, nr << SCOUTFS_CHUNK_BLOCK_SHIFT); + err = print_log_segment(fd, nr << SCOUTFS_CHUNK_BLOCK_SHIFT); if (!ret && err) ret = err; } @@ -344,15 +415,17 @@ static int print_super_brick(int fd) print_block_header(&super->hdr); printf(" id: %llx\n" " uuid: %s\n" - " total_chunks: %llu\n" + " bloom_salts: ", + le64_to_cpu(super->id), + uuid_str); + print_le32_list(18, super->bloom_salts, SCOUTFS_BLOOM_SALTS); + printf(" total_chunks: %llu\n" " ring_map_blkno: %llu\n" " ring_map_seq: %llu\n" " ring_first_block: %llu\n" " ring_active_blocks: %llu\n" " ring_total_blocks: %llu\n" " ring_seq: %llu\n", - le64_to_cpu(super->id), - uuid_str, total_chunks, le64_to_cpu(super->ring_map_blkno), le64_to_cpu(super->ring_map_seq), From ddf5ef101775284a65fb28ec8303bd92f0690306 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Wed, 23 Mar 2016 22:21:11 -0400 Subject: [PATCH 014/235] Fix set_bit_le() type width problems The swizzle value was defined in terms of longs but the code used u64s. And the bare shifted value was an int so it'd get truncated. Switch it all to using longs. The ratio of bugs to lines of code in that first attempt was through the roof! Signed-off-by: Zach Brown --- utils/src/bitops.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/utils/src/bitops.h b/utils/src/bitops.h index dfe46a1c..5ac429fc 100644 --- a/utils/src/bitops.h +++ b/utils/src/bitops.h @@ -10,11 +10,11 @@ static inline void set_bit_le(int nr, void *addr) { - u64 *dwords = addr; + unsigned long *longs = addr; nr ^= BITOP_LE_SWIZZLE; - dwords[nr / 64] |= 1 << (nr & 63); + longs[nr / BITS_PER_LONG] |= 1UL << (nr & (BITS_PER_LONG - 1)); } #endif From e0e6179156e76dc5bca2736014fcf0f9b8c7bf53 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Wed, 23 Mar 2016 22:23:21 -0400 Subject: [PATCH 015/235] Fix bloom filter bugs The bloom filter had two bad bugs. First the calculation was adding the bit width of newly hashed data to the hash value instead of the record of the hashed bits available. And the block offset calculation for each bit wasn't truncated to the number of bloom blocks. While fixing this we can clean up the code and make it faster by recording the bits in terms of their block and bit offset instead of their large bit value. Signed-off-by: Zach Brown --- utils/src/bloom.c | 16 ++++++++++------ utils/src/bloom.h | 3 ++- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/utils/src/bloom.c b/utils/src/bloom.c index 22fa62a1..f513acff 100644 --- a/utils/src/bloom.c +++ b/utils/src/bloom.c @@ -31,6 +31,7 @@ void scoutfs_calc_bloom_bits(struct scoutfs_bloom_bits *bits, struct scoutfs_key *key, __le32 *salts) { unsigned h_bits = 0; + unsigned int b; unsigned s = 0; u64 h = 0; int i; @@ -38,12 +39,16 @@ void scoutfs_calc_bloom_bits(struct scoutfs_bloom_bits *bits, for (i = 0; i < SCOUTFS_BLOOM_BITS; i++) { if (h_bits < SCOUTFS_BLOOM_BIT_WIDTH) { h = (h << 32) | bloom_hash(key, salts[s++]); - h += 32; + h_bits += 32; } - bits->nr[i] = h & SCOUTFS_BLOOM_BIT_MASK; + b = h & SCOUTFS_BLOOM_BIT_MASK; h >>= SCOUTFS_BLOOM_BIT_WIDTH; h_bits -= SCOUTFS_BLOOM_BIT_WIDTH; + + bits->block[i] = (b / SCOUTFS_BLOOM_BITS_PER_BLOCK) % + SCOUTFS_BLOOM_BLOCKS; + bits->bit_off[i] = b % SCOUTFS_BLOOM_BITS_PER_BLOCK; } } @@ -51,16 +56,15 @@ void scoutfs_calc_bloom_bits(struct scoutfs_bloom_bits *bits, * This interface is different than in the kernel because we don't * have a block IO interface here yet. The caller gives us each * bloom block and we set each bit that falls in the block. - */ + */ void scoutfs_set_bloom_bits(struct scoutfs_bloom_block *blm, unsigned int nr, struct scoutfs_bloom_bits *bits) { int i; for (i = 0; i < SCOUTFS_BLOOM_BITS; i++) { - if (nr == (bits->nr[i] / SCOUTFS_BLOOM_BITS_PER_BLOCK)) { - set_bit_le(bits->nr[i] % SCOUTFS_BLOOM_BITS_PER_BLOCK, - blm->bits); + if (nr == bits->block[i]) { + set_bit_le(bits->bit_off[i], blm->bits); } } } diff --git a/utils/src/bloom.h b/utils/src/bloom.h index cad1639f..fd9246cb 100644 --- a/utils/src/bloom.h +++ b/utils/src/bloom.h @@ -2,7 +2,8 @@ #define _BLOOM_H_ struct scoutfs_bloom_bits { - u32 nr[SCOUTFS_BLOOM_BITS]; + u16 bit_off[SCOUTFS_BLOOM_BITS]; + u8 block[SCOUTFS_BLOOM_BITS]; }; void scoutfs_calc_bloom_bits(struct scoutfs_bloom_bits *bits, From e1c1c50ead2924df294c8db711fbb67184d8116a Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Thu, 24 Mar 2016 21:09:51 -0700 Subject: [PATCH 016/235] Update to multiple dirent hash format Update print to show the inode fields in the newer dirent hashing scheme. mkfs doesn't create directory entries. Signed-off-by: Zach Brown --- utils/src/format.h | 33 +++++++++++++++------------------ utils/src/print.c | 2 ++ 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index efdfa6b9..989592e3 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -183,8 +183,8 @@ struct scoutfs_skip_root { } __packed; /* - * An item block follows the bloom filters blocks at the start of a log - * segment chunk. Its skip list root references the item structs which + * An item block follows the bloom filter blocks at the start of a log + * segment. Its skip root references the item structs which then * reference the item values in the rest of the block. The references * are byte offsets from the start of the chunk. */ @@ -225,6 +225,7 @@ struct scoutfs_inode { __le32 mode; __le32 rdev; __le32 salt; + __u8 max_dirent_hash_nr; struct scoutfs_timespec atime; struct scoutfs_timespec ctime; struct scoutfs_timespec mtime; @@ -238,17 +239,17 @@ struct scoutfs_inode { */ struct scoutfs_dirent { __le64 ino; -#if defined(__LITTLE_ENDIAN_BITFIELD) - __u8 type:4, - coll_nr:4; -#else - __u8 coll_nr:4, - type:4; -#endif - __u8 name_len; + __u8 type; __u8 name[0]; } __packed; +/* + * The max number of dirent hash values determines the overhead of + * lookups in very large directories. With 31bit offsets the number + * of entries stored before enospc tends to plateau around 200 million + * entries around 8 functions. That seems OK for now. + */ +#define SCOUTFS_MAX_DENT_HASH_NR 8 #define SCOUTFS_NAME_LEN 255 /* @@ -257,14 +258,10 @@ struct scoutfs_dirent { * network protocols that have limited readir positions. */ -#define SCOUTFS_DIRENT_OFF_BITS 27 -#define SCOUTFS_DIRENT_OFF_MASK ((1 << SCOUTFS_DIRENT_OFF_BITS) - 1) -#define SCOUTFS_DIRENT_COLL_BITS 4 -#define SCOUTFS_DIRENT_COLL_MASK ((1 << SCOUTFS_DIRENT_COLL_BITS) - 1) - -/* getdents returns the *next* pos with each entry. so we can't return ~0 */ -#define SCOUTFS_DIRENT_MAX_POS \ - (((1 << (SCOUTFS_DIRENT_OFF_BITS + SCOUTFS_DIRENT_COLL_BITS)) - 1) - 1) +#define SCOUTFS_DIRENT_OFF_BITS 31 +#define SCOUTFS_DIRENT_OFF_MASK ((1U << SCOUTFS_DIRENT_OFF_BITS) - 1) +/* getdents returns next pos with an entry, no entry at (f_pos)~0 */ +#define SCOUTFS_DIRENT_LAST_POS (INT_MAX - 1) enum { SCOUTFS_DT_FIFO = 0, diff --git a/utils/src/print.c b/utils/src/print.c index 0a174fce..f8dcf49e 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -123,6 +123,7 @@ static void print_inode(struct scoutfs_inode *inode) " mode: 0%o\n" " rdev: 0x%x\n" " salt: 0x%x\n" + " max_dirent_hash_nr: %u\n" " atime: %llu.%08u\n" " ctime: %llu.%08u\n" " mtime: %llu.%08u\n", @@ -130,6 +131,7 @@ static void print_inode(struct scoutfs_inode *inode) le32_to_cpu(inode->nlink), le32_to_cpu(inode->uid), le32_to_cpu(inode->gid), le32_to_cpu(inode->mode), le32_to_cpu(inode->rdev), le32_to_cpu(inode->salt), + inode->max_dirent_hash_nr, le64_to_cpu(inode->atime.sec), le32_to_cpu(inode->atime.nsec), le64_to_cpu(inode->ctime.sec), From 339c719e4eb0c5a4925d6225170e260f00f2ac11 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Fri, 25 Mar 2016 00:24:48 -0400 Subject: [PATCH 017/235] Print dirents in print command Add support for printing dirent items to scoutfs print. We're careful to change non-printable characters to ".". Signed-off-by: Zach Brown --- utils/src/print.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/utils/src/print.c b/utils/src/print.c index f8dcf49e..0b051f1e 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -140,6 +140,23 @@ static void print_inode(struct scoutfs_inode *inode) le32_to_cpu(inode->mtime.nsec)); } +static void print_dirent(struct scoutfs_dirent *dent, unsigned int val_len) +{ + unsigned int name_len = val_len - sizeof(*dent); + char name[SCOUTFS_NAME_LEN + 1]; + int i; + + for (i = 0; i < min(SCOUTFS_NAME_LEN, name_len); i++) + name[i] = isprint(dent->name[i]) ? dent->name[i] : '.'; + name[i] = '\0'; + + printf(" dirent:\n" + " ino: %llu\n" + " type: %u\n" + " name: \"%.*s\"\n", + le64_to_cpu(dent->ino), dent->type, i, name); +} + static void print_item(struct scoutfs_item *item, void *val) { printf(" item:\n" @@ -159,6 +176,9 @@ static void print_item(struct scoutfs_item *item, void *val) case SCOUTFS_INODE_KEY: print_inode(val); break; + case SCOUTFS_DIRENT_KEY: + print_dirent(val, le16_to_cpu(item->len)); + break; } } From 10cf83ffc5cab08e9de6fab12feb868b75349fb2 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Sat, 26 Mar 2016 14:00:19 -0400 Subject: [PATCH 018/235] Update key type value format change Adding file data items changed the item key values. Signed-off-by: Zach Brown --- utils/src/format.h | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index 989592e3..8808eb04 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -100,8 +100,14 @@ struct scoutfs_key { #define SCOUTFS_ROOT_INO 1 -#define SCOUTFS_INODE_KEY 128 -#define SCOUTFS_DIRENT_KEY 192 +/* + * Currently we sort keys by the numeric value of the types, but that + * isn't necessary. We could have an arbitrary sort order. So we don't + * have to stress about cleverly allocating the types. + */ +#define SCOUTFS_INODE_KEY 1 +#define SCOUTFS_DIRENT_KEY 2 +#define SCOUTFS_DATA_KEY 3 struct scoutfs_ring_map_block { struct scoutfs_block_header hdr; @@ -203,6 +209,13 @@ struct scoutfs_item { __le32 skip_next[0]; } __packed; +/* + * Item size caps item file data item length so that they fit in checksummed + * 4k blocks with a bit of expansion room. + */ +#define SCOUTFS_MAX_ITEM_LEN \ + (SCOUTFS_BLOCK_SIZE - sizeof(struct scoutfs_block_header) - 32) + struct scoutfs_timespec { __le64 sec; __le32 nsec; From 7ea78502c8000315572cc38ead2ecbc5146db12d Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Tue, 29 Mar 2016 13:07:00 -0400 Subject: [PATCH 019/235] Read both super blocks and use current When printing try to read both super blocks and use the most recent one instead of just using the first one. Signed-off-by: Zach Brown --- utils/src/print.c | 70 ++++++++++++++++++++++++++--------------------- 1 file changed, 39 insertions(+), 31 deletions(-) diff --git a/utils/src/print.c b/utils/src/print.c index 0b051f1e..4bfd2244 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -414,48 +414,57 @@ static int print_map_blocks(int fd, u64 blkno, u64 *ring_blknos) return 0; } -static int print_super_brick(int fd) +static int print_super_blocks(int fd) { struct scoutfs_super_block *super; + struct scoutfs_super_block recent = { .hdr.seq = 0 }; char uuid_str[37]; __le64 *log_segs; u64 *ring_blknos; u64 total_chunks; int ret = 0; int err; + int i; - /* XXX print both */ - super = read_block(fd, SCOUTFS_SUPER_BLKNO); - if (!super) - return -ENOMEM; + for (i = 0; i < SCOUTFS_SUPER_NR; i++) { + super = read_block(fd, SCOUTFS_SUPER_BLKNO + i); + if (!super) + return -ENOMEM; - uuid_unparse(super->uuid, uuid_str); + uuid_unparse(super->uuid, uuid_str); + printf("super:\n"); + print_block_header(&super->hdr); + printf(" id: %llx\n" + " uuid: %s\n" + " bloom_salts: ", + le64_to_cpu(super->id), + uuid_str); + print_le32_list(18, super->bloom_salts, SCOUTFS_BLOOM_SALTS); + printf(" total_chunks: %llu\n" + " ring_map_blkno: %llu\n" + " ring_map_seq: %llu\n" + " ring_first_block: %llu\n" + " ring_active_blocks: %llu\n" + " ring_total_blocks: %llu\n" + " ring_seq: %llu\n", + le64_to_cpu(super->total_chunks), + le64_to_cpu(super->ring_map_blkno), + le64_to_cpu(super->ring_map_seq), + le64_to_cpu(super->ring_first_block), + le64_to_cpu(super->ring_active_blocks), + le64_to_cpu(super->ring_total_blocks), + le64_to_cpu(super->ring_seq)); + + if (le64_to_cpu(super->hdr.seq) > le64_to_cpu(recent.hdr.seq)) + memcpy(&recent, super, sizeof(recent)); + + free(super); + } + + super = &recent; total_chunks = le64_to_cpu(super->total_chunks); - printf("super:\n"); - print_block_header(&super->hdr); - printf(" id: %llx\n" - " uuid: %s\n" - " bloom_salts: ", - le64_to_cpu(super->id), - uuid_str); - print_le32_list(18, super->bloom_salts, SCOUTFS_BLOOM_SALTS); - printf(" total_chunks: %llu\n" - " ring_map_blkno: %llu\n" - " ring_map_seq: %llu\n" - " ring_first_block: %llu\n" - " ring_active_blocks: %llu\n" - " ring_total_blocks: %llu\n" - " ring_seq: %llu\n", - total_chunks, - le64_to_cpu(super->ring_map_blkno), - le64_to_cpu(super->ring_map_seq), - le64_to_cpu(super->ring_first_block), - le64_to_cpu(super->ring_active_blocks), - le64_to_cpu(super->ring_total_blocks), - le64_to_cpu(super->ring_seq)); - /* * Allocate a bitmap big enough to describe all the chunks and * we can have at most a full chunk worth of map blocks. @@ -485,7 +494,6 @@ out: free(log_segs); if (ring_blknos) free(ring_blknos); - free(super); return ret; } @@ -509,7 +517,7 @@ static int print_cmd(int argc, char **argv) return ret; } - ret = print_super_brick(fd); + ret = print_super_blocks(fd); close(fd); return ret; }; From af2975111a7a5c84ae2cf0e21bed711c4234c8b8 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Tue, 29 Mar 2016 13:10:45 -0400 Subject: [PATCH 020/235] Update format for smaller bloom Update our format for the smaller bloom sizes. Signed-off-by: Zach Brown --- utils/src/format.h | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index 8808eb04..d5e69bcd 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -36,15 +36,18 @@ #define SCOUTFS_SUPER_NR 2 /* - * 7 bits in a ~76k bloom filter gives ~1% false positive for our max - * of 64k items. + * The bloom filters are statically sized. It's a tradeoff between + * storage overhead and false positive rate. At the moment we have + * as few as 1000 and as many as 18000 items in a segment. We can + * get a ~1% false positive rate (triggering header search) rate at + * the high end with a ~20k bloom filter. * - * n = 65,536, p = 0.01 (1 in 100) → m = 628,167 (76.68KB), k = 7 + * n = 18,000, p = 0.01 (1 in 100) → m = 172,532 (21.06KB), k = 7 */ #define SCOUTFS_BLOOM_BITS 7 -#define SCOUTFS_BLOOM_BIT_WIDTH 20 /* 2^20 > m */ +#define SCOUTFS_BLOOM_BIT_WIDTH 18 /* 2^18 > m */ #define SCOUTFS_BLOOM_BIT_MASK ((1 << SCOUTFS_BLOOM_BIT_WIDTH) - 1) -#define SCOUTFS_BLOOM_BLOCKS ((76 * 1024) / SCOUTFS_BLOCK_SIZE) +#define SCOUTFS_BLOOM_BLOCKS ((20 * 1024) / SCOUTFS_BLOCK_SIZE) #define SCOUTFS_BLOOM_SALTS \ DIV_ROUND_UP(SCOUTFS_BLOOM_BITS * SCOUTFS_BLOOM_BIT_WIDTH, 32) From 544fd1ba9a8fdad905e298998410f48d1dd27917 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Thu, 31 Mar 2016 23:56:41 -0400 Subject: [PATCH 021/235] Add ctrstat command Like vmstat and iostat, this prints out our counters over time. Signed-off-by: Zach Brown --- utils/Makefile | 2 +- utils/src/ctrstat.c | 248 +++++++++++++++++++++++++++++++++++ utils/src/list.h | 310 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 559 insertions(+), 1 deletion(-) create mode 100644 utils/src/ctrstat.c create mode 100644 utils/src/list.h diff --git a/utils/Makefile b/utils/Makefile index 6a15bbdd..aad55eed 100644 --- a/utils/Makefile +++ b/utils/Makefile @@ -20,7 +20,7 @@ endif $(BIN): $(OBJ) $(QU) [BIN $@] - $(VE)gcc -o $@ $^ -luuid + $(VE)gcc -o $@ $^ -luuid -lm %.o %.d: %.c Makefile sparse.sh $(QU) [CC $<] diff --git a/utils/src/ctrstat.c b/utils/src/ctrstat.c new file mode 100644 index 00000000..cfad0740 --- /dev/null +++ b/utils/src/ctrstat.c @@ -0,0 +1,248 @@ +#define _XOPEN_SOURCE 700 /* 600: floorf, strtof, 700: openat */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "util.h" +#include "cmd.h" +#include "list.h" + +#define SCOUTFS_SYSFS_PATH "/sys/fs/scoutfs" + +struct string_item { + struct list_head head; + int dir_fd; + char *str; + int len; +}; + +static int add_string(char *str, struct list_head *list) +{ + struct string_item *sitem; + int ret = -ENOMEM; + + sitem = malloc(sizeof(struct string_item)); + if (sitem) { + sitem->str = strdup(str); + if (sitem->str) { + sitem->dir_fd = -1; + list_add_tail(&sitem->head, list); + sitem->len = strlen(str); + ret = 0; + } + } + + if (ret) + fprintf(stderr, "failed to alloc mem for string '%s'\n", str); + + return ret; +} + +/* + * Iterate over all the mounted ids and use their open dirfds to open + * and read each counter. We have to open each time we want updated counters. + * We reflect the counter length in the column's label length. + */ +static int read_and_print_counters(struct list_head *labels, + struct list_head *id_list, int print) +{ + struct string_item *label; + struct string_item *id; + char buf[25]; + ssize_t bytes; + int ret = 0; + int fd; + + list_for_each_entry(id, id_list, head) { + list_for_each_entry(label, labels, head) { + /* id column */ + if (label->str[0] == '\0') { + label->len = max(label->len, id->len); + if (print) + printf("%*s ", label->len, id->str); + continue; + } + + /* have to open each time we want current counter :/ */ + fd = openat(id->dir_fd, label->str, O_RDONLY); + if (fd < 0) { + ret = -errno; + goto out; + } + + bytes = pread(fd, buf, sizeof(buf), 0); + close(fd); + + if (bytes <= 1 || bytes >= sizeof(buf) || + buf[bytes - 1] != '\n') { + fprintf(stderr, "counter file %s/%s read returned %zd\n", + id->str, label->str, bytes); + ret = -EIO; + goto out; + } + + label->len = max(label->len, bytes - 1); + + if (print) { + buf[bytes - 1] = '\0'; + printf("%*s ", label->len, buf); + } + } + if (print) + printf("\n"); + } + +out: + return ret; +} + +static int dots(char *name) +{ + return name[0] == '.' && + (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')); +} + +/* + * XXX deal with unmount ;) + */ +static int ctrstat_cmd(int argc, char **argv) +{ + struct string_item *label; + struct string_item *id; + LIST_HEAD(label_list); + char path[PATH_MAX]; + LIST_HEAD(id_list); + struct dirent *dent; + float seconds = 1.0; + struct timespec ts; + DIR *dirp; + int iter; + int ret; + + if (argc > 1) { + printf("scoutfs ctrstat: too many arguments\n"); + return -EINVAL; + } + + /* set the sleep duration */ + if (argc == 1) { + seconds = strtof(argv[0], NULL); + if (fpclassify(seconds) != FP_NORMAL || seconds <= 0) { + printf("invalid sleep duration float: %s\n", argv[0]); + return -EINVAL; + } + } + ts.tv_sec = (int)floorf(seconds); + ts.tv_nsec = (seconds - floorf(seconds)) * 1000000000; + + /* find all the mounted ids */ + dirp = opendir(SCOUTFS_SYSFS_PATH); + if (!dirp) { + ret = -errno; + fprintf(stderr, "failed to open '%s': %s (%d)\n", + SCOUTFS_SYSFS_PATH, strerror(errno), errno); + goto out; + } + while ((dent = readdir(dirp))) { + if (dots(dent->d_name)) + continue; + ret = add_string(dent->d_name, &id_list); + if (ret) + goto out; + + } + closedir(dirp); + dirp = NULL; + + /* add a dummy label for the id column */ + ret = add_string("", &label_list); + if (ret) + goto out; + + iter = 1; + list_for_each_entry(id, &id_list, head) { + snprintf(path, PATH_MAX, SCOUTFS_SYSFS_PATH"/%s/counters", + id->str); + + dirp = opendir(path); + if (!dirp) { + ret = -errno; + fprintf(stderr, "failed to open '%s': %s (%d)\n", + path, strerror(errno), errno); + goto out; + } + + /* hold a dir fd open for each id */ + id->dir_fd = dup(dirfd(dirp)); + if (id->dir_fd < 0) { + ret = -errno; + fprintf(stderr, "couldn't dup fd for '%s': %s (%d)\n", + path, strerror(errno), errno); + goto out; + } + + /* find all the counters, assume all ids have same */ + while (iter && (dent = readdir(dirp))) { + if (dots(dent->d_name)) + continue; + + ret = add_string(dent->d_name, &label_list); + if (ret) + goto out; + } + closedir(dirp); + dirp = NULL; + iter = 0; + } + + /* initial read pass to find the max lengths */ + ret = read_and_print_counters(&label_list, &id_list, 0); + if (ret) + goto out; + + for (iter = 0; ; iter++) { + /* print row of column labels */ + if (!(iter % 25)) { + list_for_each_entry(label, &label_list, head) + printf("%*s ", label->len, label->str); + printf("\n"); + } + + /* print each id and its stats */ + ret = read_and_print_counters(&label_list, &id_list, 1); + if (ret) + goto out; + + nanosleep(&ts, NULL); + } + ret = 0; +out: + if (dirp) + closedir(dirp); + + /* squish together and free all */ + list_splice(&label_list, &id_list); + list_for_each_entry_safe(id, label, &id_list, head) { + list_del_init(&id->head); + if (id->dir_fd >= 0) + close(id->dir_fd); + free(id); + } + return ret; +}; + +static void __attribute__((constructor)) ctrstat_ctor(void) +{ + cmd_register("ctrstat", "", "print counters over time", + ctrstat_cmd); +} diff --git a/utils/src/list.h b/utils/src/list.h new file mode 100644 index 00000000..231807df --- /dev/null +++ b/utils/src/list.h @@ -0,0 +1,310 @@ +/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*- + * vim:expandtab:shiftwidth=8:tabstop=8: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * yanked from the linux kernel.. + */ +#ifndef _LIST_H_ +#define _LIST_H_ +/* + * Simple doubly linked list implementation. + * + * Some of the internal functions ("__xxx") are useful when + * manipulating whole lists rather than single entries, as + * sometimes we already know the next/prev entries and we can + * generate better code by using them directly rather than + * using the generic single-entry routines. + */ + +struct list_head { + struct list_head *next, *prev; +}; + +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +#define LIST_HEAD(name) \ + struct list_head name = LIST_HEAD_INIT(name) + +#define INIT_LIST_HEAD(ptr) do { \ + (ptr)->next = (ptr); (ptr)->prev = (ptr); \ +} while (0) + +/* + * Insert a new entry between two known consecutive entries. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_add(struct list_head *new, + struct list_head *prev, + struct list_head *next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +/** + * list_add - add a new entry + * @new: new entry to be added + * @head: list head to add it after + * + * Insert a new entry after the specified head. + * This is good for implementing stacks. + */ +static inline void list_add(struct list_head *new, struct list_head *head) +{ + __list_add(new, head, head->next); +} + +/** + * list_add_tail - add a new entry + * @new: new entry to be added + * @head: list head to add it before + * + * Insert a new entry before the specified head. + * This is useful for implementing queues. + */ +static inline void list_add_tail(struct list_head *new, struct list_head *head) +{ + __list_add(new, head->prev, head); +} + +/* + * Insert a new entry between two known consecutive entries. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static __inline__ void __list_add_rcu(struct list_head * new, + struct list_head * prev, + struct list_head * next) +{ + new->next = next; + new->prev = prev; + next->prev = new; + prev->next = new; +} + +/* + * Delete a list entry by making the prev/next entries + * point to each other. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_del(struct list_head * prev, struct list_head * next) +{ + next->prev = prev; + prev->next = next; +} + +/** + * list_del - deletes entry from list. + * @entry: the element to delete from the list. + * Note: list_empty on entry does not return true after this, the entry is + * in an undefined state. + */ +static inline void list_del(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); +} + +/** + * list_del_init - deletes entry from list and reinitialize it. + * @entry: the element to delete from the list. + */ +static inline void list_del_init(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + INIT_LIST_HEAD(entry); +} + +/** + * list_move - delete from one list and add as another's head + * @list: the entry to move + * @head: the head that will precede our entry + */ +static inline void list_move(struct list_head *list, struct list_head *head) +{ + __list_del(list->prev, list->next); + list_add(list, head); +} + +/** + * list_move_tail - delete from one list and add as another's tail + * @list: the entry to move + * @head: the head that will follow our entry + */ +static inline void list_move_tail(struct list_head *list, + struct list_head *head) +{ + __list_del(list->prev, list->next); + list_add_tail(list, head); +} + +/** + * list_empty - tests whether a list is empty + * @head: the list to test. + */ +static inline int list_empty(struct list_head *head) +{ + return head->next == head; +} + +static inline void __list_splice(struct list_head *list, + struct list_head *head) +{ + struct list_head *first = list->next; + struct list_head *last = list->prev; + struct list_head *at = head->next; + + first->prev = head; + head->next = first; + + last->next = at; + at->prev = last; +} + +/** + * list_splice - join two lists + * @list: the new list to add. + * @head: the place to add it in the first list. + */ +static inline void list_splice(struct list_head *list, struct list_head *head) +{ + if (!list_empty(list)) + __list_splice(list, head); +} + +/** + * list_splice_init - join two lists and reinitialise the emptied list. + * @list: the new list to add. + * @head: the place to add it in the first list. + * + * The list at @list is reinitialised + */ +static inline void list_splice_init(struct list_head *list, + struct list_head *head) +{ + if (!list_empty(list)) { + __list_splice(list, head); + INIT_LIST_HEAD(list); + } +} + +/** + * list_entry - get the struct for this entry + * @ptr: the &struct list_head pointer. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_struct within the struct. + */ +#define list_entry(ptr, type, member) \ + ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) + + +/** + * list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop counter. + * @head: the head for your list. + */ +#define list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) + +/** + * list_for_each_prev - iterate over a list backwards + * @pos: the &struct list_head to use as a loop counter. + * @head: the head for your list. + */ +#define list_for_each_prev(pos, head) \ + for (pos = (head)->prev; pos != (head); pos = pos->prev) + +/** + * list_for_each_safe - iterate over a list safe against removal of list entry + * @pos: the &struct list_head to use as a loop counter. + * @n: another &struct list_head to use as temporary storage + * @head: the head for your list. + */ +#define list_for_each_safe(pos, n, head) \ + for (pos = (head)->next, n = pos->next; pos != (head); \ + pos = n, n = pos->next) + +/** + * list_for_each_entry - iterate over list of given type + * @pos: the type * to use as a loop counter. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry(pos, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member)) + +/** + * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_safe(pos, n, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member), \ + n = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) + +/** + * list_first_entry - get the first element from a list + * @ptr: the list head to take the element from. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_head within the struct. + * + * Note, that list is expected to be not empty. + */ +#define list_first_entry(ptr, type, member) \ + list_entry((ptr)->next, type, member) + +/** + * list_last_entry - get the last element from a list + * @ptr: the list head to take the element from. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_head within the struct. + * + * Note, that list is expected to be not empty. + */ +#define list_last_entry(ptr, type, member) \ + list_entry((ptr)->prev, type, member) + +/** + * list_first_entry_or_null - get the first element from a list + * @ptr: the list head to take the element from. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_head within the struct. + * + * Note that if the list is empty, it returns NULL. + */ +#define list_first_entry_or_null(ptr, type, member) \ + (!list_empty(ptr) ? list_first_entry(ptr, type, member) : NULL) + +/** + * list_next_entry - get the next element in list + * @pos: the type * to cursor + * @member: the name of the list_head within the struct. + */ +#define list_next_entry(pos, member) \ + list_entry((pos)->member.next, typeof(*(pos)), member) + +#endif From c4fcf40097266b275c1e8936c493fe04b3ccdf16 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Sat, 2 Apr 2016 20:30:45 -0400 Subject: [PATCH 022/235] Update ring manifest deletion entries The ring now contains stores full manifest entries that are deleted rather than just their block number. Signed-off-by: Zach Brown --- utils/src/format.h | 6 ++---- utils/src/mkfs.c | 2 +- utils/src/print.c | 25 ++++++++++++++----------- 3 files changed, 17 insertions(+), 16 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index d5e69bcd..c097d0c4 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -153,7 +153,7 @@ enum { * isn't unused key space between blocks in a level. We might search * blocks when we didn't need to. */ -struct scoutfs_ring_manifest_entry { +struct scoutfs_manifest_entry { __le64 blkno; __le64 seq; __u8 level; @@ -161,9 +161,7 @@ struct scoutfs_ring_manifest_entry { struct scoutfs_key last; } __packed; -struct scoutfs_ring_del_manifest { - __le64 blkno; -} __packed; +#define SCOUTFS_MANIFESTS_PER_LEVEL 10 /* 2^22 * 10^13 > 2^64 */ #define SCOUTFS_MAX_LEVEL 13 diff --git a/utils/src/mkfs.c b/utils/src/mkfs.c index c48de7f9..5658d3d9 100644 --- a/utils/src/mkfs.c +++ b/utils/src/mkfs.c @@ -47,7 +47,7 @@ static int write_new_fs(char *path, int fd) struct scoutfs_ring_map_block *map; struct scoutfs_ring_block *ring; struct scoutfs_ring_entry *ent; - struct scoutfs_ring_manifest_entry *mani; + struct scoutfs_manifest_entry *mani; struct scoutfs_ring_bitmap *bm; struct scoutfs_item_block *iblk; struct scoutfs_bloom_bits bits; diff --git a/utils/src/print.c b/utils/src/print.c index 4bfd2244..9dc322ac 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -260,8 +260,7 @@ static char *ent_type_str(u8 type) static void print_ring_entry(int fd, struct scoutfs_ring_entry *ent) { - struct scoutfs_ring_manifest_entry *ment; - struct scoutfs_ring_del_manifest *del; + struct scoutfs_manifest_entry *ment; struct scoutfs_ring_bitmap *bm; printf(" entry:\n" @@ -281,9 +280,14 @@ static void print_ring_entry(int fd, struct scoutfs_ring_entry *ent) ment->level, SKA(&ment->first), SKA(&ment->last)); break; case SCOUTFS_RING_DEL_MANIFEST: - del = (void *)(ent + 1); - printf(" blkno: %llu\n", - le64_to_cpu(del->blkno)); + ment = (void *)(ent + 1); + printf(" blkno: %llu\n" + " seq: %llu\n" + " level: %u\n" + " first: "SKF"\n" + " last: "SKF"\n", + le64_to_cpu(ment->blkno), le64_to_cpu(ment->seq), + ment->level, SKA(&ment->first), SKA(&ment->last)); break; case SCOUTFS_RING_BITMAP: bm = (void *)(ent + 1); @@ -298,19 +302,18 @@ static void print_ring_entry(int fd, struct scoutfs_ring_entry *ent) static void update_log_segs(struct scoutfs_ring_entry *ent, __le64 *log_segs) { - struct scoutfs_ring_manifest_entry *add; - struct scoutfs_ring_del_manifest *del; + struct scoutfs_manifest_entry *ment; u64 bit; switch(ent->type) { case SCOUTFS_RING_ADD_MANIFEST: - add = (void *)(ent + 1); - bit = le64_to_cpu(add->blkno) >> SCOUTFS_CHUNK_BLOCK_SHIFT; + ment = (void *)(ent + 1); + bit = le64_to_cpu(ment->blkno) >> SCOUTFS_CHUNK_BLOCK_SHIFT; set_le_bit(log_segs, bit); break; case SCOUTFS_RING_DEL_MANIFEST: - del = (void *)(ent + 1); - bit = le64_to_cpu(del->blkno) >> SCOUTFS_CHUNK_BLOCK_SHIFT; + ment = (void *)(ent + 1); + bit = le64_to_cpu(ment->blkno) >> SCOUTFS_CHUNK_BLOCK_SHIFT; clear_le_bit(log_segs, bit); break; } From 56077b61a1ee15c86500f049f07ea8e029ceb756 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Tue, 12 Apr 2016 15:02:02 -0700 Subject: [PATCH 023/235] Move to btree blocks Update mkfs and printing for the btree experiment. Signed-off-by: Zach Brown --- utils/src/bloom.c | 70 -------- utils/src/bloom.h | 14 -- utils/src/format.h | 236 ++++++++----------------- utils/src/mkfs.c | 122 +++---------- utils/src/print.c | 431 ++++++++------------------------------------- 5 files changed, 165 insertions(+), 708 deletions(-) delete mode 100644 utils/src/bloom.c delete mode 100644 utils/src/bloom.h diff --git a/utils/src/bloom.c b/utils/src/bloom.c deleted file mode 100644 index f513acff..00000000 --- a/utils/src/bloom.c +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2016 Versity Software, Inc. All rights reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public - * License v2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - */ -#include "sparse.h" -#include "util.h" -#include "format.h" -#include "bloom.h" -#include "crc.h" -#include "bitops.h" - -/* XXX garbage hack until we have siphash */ -static u32 bloom_hash(struct scoutfs_key *key, __le32 salt) -{ - return crc32c(le32_to_cpu(salt), key, sizeof(struct scoutfs_key)); -} - -/* - * Find the bits in the bloom filter for the given key. The caller calculates - * these once and uses them to test all the blocks. - */ -void scoutfs_calc_bloom_bits(struct scoutfs_bloom_bits *bits, - struct scoutfs_key *key, __le32 *salts) -{ - unsigned h_bits = 0; - unsigned int b; - unsigned s = 0; - u64 h = 0; - int i; - - for (i = 0; i < SCOUTFS_BLOOM_BITS; i++) { - if (h_bits < SCOUTFS_BLOOM_BIT_WIDTH) { - h = (h << 32) | bloom_hash(key, salts[s++]); - h_bits += 32; - } - - b = h & SCOUTFS_BLOOM_BIT_MASK; - h >>= SCOUTFS_BLOOM_BIT_WIDTH; - h_bits -= SCOUTFS_BLOOM_BIT_WIDTH; - - bits->block[i] = (b / SCOUTFS_BLOOM_BITS_PER_BLOCK) % - SCOUTFS_BLOOM_BLOCKS; - bits->bit_off[i] = b % SCOUTFS_BLOOM_BITS_PER_BLOCK; - } -} - -/* - * This interface is different than in the kernel because we don't - * have a block IO interface here yet. The caller gives us each - * bloom block and we set each bit that falls in the block. - */ -void scoutfs_set_bloom_bits(struct scoutfs_bloom_block *blm, unsigned int nr, - struct scoutfs_bloom_bits *bits) -{ - int i; - - for (i = 0; i < SCOUTFS_BLOOM_BITS; i++) { - if (nr == bits->block[i]) { - set_bit_le(bits->bit_off[i], blm->bits); - } - } -} diff --git a/utils/src/bloom.h b/utils/src/bloom.h deleted file mode 100644 index fd9246cb..00000000 --- a/utils/src/bloom.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef _BLOOM_H_ -#define _BLOOM_H_ - -struct scoutfs_bloom_bits { - u16 bit_off[SCOUTFS_BLOOM_BITS]; - u8 block[SCOUTFS_BLOOM_BITS]; -}; - -void scoutfs_calc_bloom_bits(struct scoutfs_bloom_bits *bits, - struct scoutfs_key *key, __le32 *salts); -void scoutfs_set_bloom_bits(struct scoutfs_bloom_block *blm, unsigned int nr, - struct scoutfs_bloom_bits *bits); - -#endif diff --git a/utils/src/format.h b/utils/src/format.h index c097d0c4..a80e95b0 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -6,27 +6,12 @@ /* super block id */ #define SCOUTFS_SUPER_ID 0x2e736674756f6373ULL /* "scoutfs." */ -/* - * Everything is stored in and addressed as 4k fixed size blocks. This - * avoids having to manage contiguous cpu mappings of larger blocks. - * Larger structures are read and written as multiple blocks. - */ -#define SCOUTFS_BLOCK_SHIFT 12 +#define SCOUTFS_BLOCK_SHIFT 14 #define SCOUTFS_BLOCK_SIZE (1 << SCOUTFS_BLOCK_SHIFT) #define SCOUTFS_BLOCK_MASK (SCOUTFS_BLOCK_SIZE - 1) -/* - * The allocator works on larger chunks. Smaller metadata structures - * like the super blocks and the ring are stored in chunks. - * - * A log segment is a collection of smaller blocks (bloom filter, item blocks) - * stored in a chunk. - */ -#define SCOUTFS_CHUNK_SHIFT 22 -#define SCOUTFS_CHUNK_SIZE (1 << SCOUTFS_CHUNK_SHIFT) -#define SCOUTFS_CHUNK_BLOCK_SHIFT (SCOUTFS_CHUNK_SHIFT - SCOUTFS_BLOCK_SHIFT) -#define SCOUTFS_CHUNK_BLOCK_MASK ((1 << SCOUTFS_CHUNK_BLOCK_SHIFT) - 1) -#define SCOUTFS_BLOCKS_PER_CHUNK (1 << SCOUTFS_CHUNK_BLOCK_SHIFT) +#define SCOUTFS_PAGES_PER_BLOCK (SCOUTFS_BLOCK_SIZE / PAGE_SIZE) +#define SCOUTFS_BLOCK_PAGE_ORDER (SCOUTFS_BLOCK_SHIFT - PAGE_SHIFT) /* * The super blocks leave some room at the start of the first block for @@ -35,22 +20,6 @@ #define SCOUTFS_SUPER_BLKNO ((64 * 1024) >> SCOUTFS_BLOCK_SHIFT) #define SCOUTFS_SUPER_NR 2 -/* - * The bloom filters are statically sized. It's a tradeoff between - * storage overhead and false positive rate. At the moment we have - * as few as 1000 and as many as 18000 items in a segment. We can - * get a ~1% false positive rate (triggering header search) rate at - * the high end with a ~20k bloom filter. - * - * n = 18,000, p = 0.01 (1 in 100) → m = 172,532 (21.06KB), k = 7 - */ -#define SCOUTFS_BLOOM_BITS 7 -#define SCOUTFS_BLOOM_BIT_WIDTH 18 /* 2^18 > m */ -#define SCOUTFS_BLOOM_BIT_MASK ((1 << SCOUTFS_BLOOM_BIT_WIDTH) - 1) -#define SCOUTFS_BLOOM_BLOCKS ((20 * 1024) / SCOUTFS_BLOCK_SIZE) -#define SCOUTFS_BLOOM_SALTS \ - DIV_ROUND_UP(SCOUTFS_BLOOM_BITS * SCOUTFS_BLOOM_BIT_WIDTH, 32) - /* * This header is found at the start of every block so that we can * verify that it's what we were looking for. The crc and padding @@ -65,6 +34,72 @@ struct scoutfs_block_header { __le64 blkno; } __packed; +/* + * We should be able to make the offset smaller if neither dirents nor + * data items use the full 64 bits. + */ +struct scoutfs_key { + __le64 inode; + u8 type; + __le64 offset; +} __packed; + +/* + * Currently we sort keys by the numeric value of the types, but that + * isn't necessary. We could have an arbitrary sort order. So we don't + * have to stress about cleverly allocating the types. + */ +#define SCOUTFS_INODE_KEY 1 +#define SCOUTFS_DIRENT_KEY 2 +#define SCOUTFS_DATA_KEY 3 + +#define SCOUTFS_MAX_ITEM_LEN 2048 + +/* + * Block references include the sequence number so that we can detect + * readers racing with writers and so that we can tell that we don't + * need to follow a reference when traversing based on seqs. + */ +struct scoutfs_block_ref { + __le64 blkno; + __le64 seq; +} __packed; + +struct scoutfs_treap_root { + __le16 off; +} __packed; + +struct scoutfs_treap_node { + __le16 parent; + __le16 left; + __le16 right; + __le32 prio; +} __packed; + +struct scoutfs_btree_root { + u8 height; + struct scoutfs_block_ref ref; +} __packed; + +struct scoutfs_btree_block { + struct scoutfs_block_header hdr; + struct scoutfs_treap_root treap; + __le16 total_free; + __le16 tail_free; + __le16 nr_items; +} __packed; + +struct scoutfs_btree_item { + struct scoutfs_key key; + struct scoutfs_treap_node tnode; + __le16 val_len; + char val[0]; +} __packed; + +/* Blocks are no more than half free. */ +#define SCOUTFS_BTREE_FREE_LIMIT \ + ((SCOUTFS_BLOCK_SIZE - sizeof(struct scoutfs_btree_block)) / 2) + #define SCOUTFS_UUID_BYTES 16 /* @@ -81,142 +116,11 @@ struct scoutfs_super_block { struct scoutfs_block_header hdr; __le64 id; __u8 uuid[SCOUTFS_UUID_BYTES]; - __le32 bloom_salts[SCOUTFS_BLOOM_SALTS]; - __le64 total_chunks; - __le64 ring_map_blkno; - __le64 ring_map_seq; - __le64 ring_first_block; - __le64 ring_active_blocks; - __le64 ring_total_blocks; - __le64 ring_seq; -} __packed; - -/* - * We should be able to make the offset smaller if neither dirents nor - * data items use the full 64 bits. - */ -struct scoutfs_key { - __le64 inode; - u8 type; - __le64 offset; + struct scoutfs_btree_root btree_root; } __packed; #define SCOUTFS_ROOT_INO 1 -/* - * Currently we sort keys by the numeric value of the types, but that - * isn't necessary. We could have an arbitrary sort order. So we don't - * have to stress about cleverly allocating the types. - */ -#define SCOUTFS_INODE_KEY 1 -#define SCOUTFS_DIRENT_KEY 2 -#define SCOUTFS_DATA_KEY 3 - -struct scoutfs_ring_map_block { - struct scoutfs_block_header hdr; - __le32 nr_chunks; - __le64 blknos[0]; -} __packed; - -#define SCOUTFS_RING_MAP_BLOCKS \ - ((SCOUTFS_BLOCK_SIZE - sizeof(struct scoutfs_ring_map_block)) / \ - sizeof(__le64)) - -struct scoutfs_ring_entry { - u8 type; - __le16 len; -} __packed; - -/* - * Ring blocks are stored in chunks described by the ring map blocks. - * - * The manifest entries describe the position of a given log segment in - * the manifest. They're keyed by the block number so that we can - * record movement of a log segment in the manifest with one ring entry - * and we can record deletion with just the block number. - */ -struct scoutfs_ring_block { - struct scoutfs_block_header hdr; - __le16 nr_entries; -} __packed; - -enum { - SCOUTFS_RING_ADD_MANIFEST = 0, - SCOUTFS_RING_DEL_MANIFEST, - SCOUTFS_RING_BITMAP, -}; - -/* - * Including both keys might make the manifest too large. It might be - * better to only include one key and infer a block's range from the - * neighbour's key. The downside of that is that we assume that there - * isn't unused key space between blocks in a level. We might search - * blocks when we didn't need to. - */ -struct scoutfs_manifest_entry { - __le64 blkno; - __le64 seq; - __u8 level; - struct scoutfs_key first; - struct scoutfs_key last; -} __packed; - -#define SCOUTFS_MANIFESTS_PER_LEVEL 10 - -/* 2^22 * 10^13 > 2^64 */ -#define SCOUTFS_MAX_LEVEL 13 - -struct scoutfs_ring_bitmap { - __le32 offset; - __le64 bits[2]; -} __packed; - - -struct scoutfs_bloom_block { - struct scoutfs_block_header hdr; - __le64 bits[0]; -} __packed; - -#define SCOUTFS_BLOOM_BITS_PER_BLOCK \ - (((SCOUTFS_BLOCK_SIZE - sizeof(struct scoutfs_block_header)) / 8) * 64) - -/* - * Items in log segments are sorted in a skip list by their key. We - * have a rough limit of 64k items. - */ -#define SCOUTFS_SKIP_HEIGHT 16 -struct scoutfs_skip_root { - __le32 next[SCOUTFS_SKIP_HEIGHT]; -} __packed; - -/* - * An item block follows the bloom filter blocks at the start of a log - * segment. Its skip root references the item structs which then - * reference the item values in the rest of the block. The references - * are byte offsets from the start of the chunk. - */ -struct scoutfs_item_block { - struct scoutfs_block_header hdr; - struct scoutfs_key first; - struct scoutfs_key last; - struct scoutfs_skip_root skip_root; -} __packed; - -struct scoutfs_item { - struct scoutfs_key key; - __le32 offset; - __le16 len; - u8 skip_height; - __le32 skip_next[0]; -} __packed; - -/* - * Item size caps item file data item length so that they fit in checksummed - * 4k blocks with a bit of expansion room. - */ -#define SCOUTFS_MAX_ITEM_LEN \ - (SCOUTFS_BLOCK_SIZE - sizeof(struct scoutfs_block_header) - 32) - struct scoutfs_timespec { __le64 sec; __le32 nsec; diff --git a/utils/src/mkfs.c b/utils/src/mkfs.c index 5658d3d9..ac0c196e 100644 --- a/utils/src/mkfs.c +++ b/utils/src/mkfs.c @@ -17,7 +17,6 @@ #include "crc.h" #include "rand.h" #include "dev.h" -#include "bloom.h" #include "bitops.h" /* @@ -44,21 +43,13 @@ static int write_new_fs(char *path, int fd) { struct scoutfs_super_block *super; struct scoutfs_inode *inode; - struct scoutfs_ring_map_block *map; - struct scoutfs_ring_block *ring; - struct scoutfs_ring_entry *ent; - struct scoutfs_manifest_entry *mani; - struct scoutfs_ring_bitmap *bm; - struct scoutfs_item_block *iblk; - struct scoutfs_bloom_bits bits; - struct scoutfs_bloom_block *blm; - struct scoutfs_item *item; + struct scoutfs_btree_block *bt; + struct scoutfs_btree_item *item; struct scoutfs_key root_key; struct timeval tv; char uuid_str[37]; unsigned int i; u64 size; - u64 total_chunks; u64 blkno; void *buf; int ret; @@ -81,14 +72,12 @@ static int write_new_fs(char *path, int fd) goto out; } - total_chunks = size >> SCOUTFS_CHUNK_SHIFT; - root_key.inode = cpu_to_le64(SCOUTFS_ROOT_INO); root_key.type = SCOUTFS_INODE_KEY; root_key.offset = 0; - /* first chunk has super blocks, log segment chunk is next */ - blkno = 1 << SCOUTFS_CHUNK_BLOCK_SHIFT; + /* start with the block after the supers */ + blkno = SCOUTFS_SUPER_BLKNO + SCOUTFS_SUPER_NR; /* first initialize the super so we can use it to build structures */ memset(super, 0, SCOUTFS_BLOCK_SIZE); @@ -96,45 +85,21 @@ 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); - pseudo_random_bytes(super->bloom_salts, sizeof(super->bloom_salts)); - super->total_chunks = cpu_to_le64(total_chunks); - super->ring_map_seq = super->hdr.seq; - super->ring_first_block = cpu_to_le64(0); - super->ring_active_blocks = cpu_to_le64(1); - super->ring_total_blocks = cpu_to_le64(SCOUTFS_BLOCKS_PER_CHUNK); - super->ring_seq = super->hdr.seq; - /* - * There's only the root item so we check for its bloom bits as - * we write the bloom blocks. - */ - scoutfs_calc_bloom_bits(&bits, &root_key, super->bloom_salts); - for (i = 0; i < SCOUTFS_BLOOM_BLOCKS; i++) { - memset(buf, 0, SCOUTFS_BLOCK_SIZE); - blm = buf; - blm->hdr = super->hdr; - - scoutfs_set_bloom_bits(blm, i, &bits); - - ret = write_block(fd, blkno, &blm->hdr); - if (ret) - goto out; - blkno++; - } - - /* write a single log segment with the root inode item */ + /* write a btree leaf root inode item */ memset(buf, 0, SCOUTFS_BLOCK_SIZE); - iblk = buf; - iblk->hdr = super->hdr; - iblk->skip_root.next[0] = cpu_to_le32((SCOUTFS_BLOOM_BLOCKS << - SCOUTFS_BLOCK_SHIFT) + - sizeof(struct scoutfs_item_block)); - item = (void *)(iblk + 1); + bt = buf; + bt->hdr = super->hdr; + bt->nr_items = cpu_to_le16(1); + + item = (void *)(bt + 1); item->key = root_key; - item->offset = cpu_to_le32(le32_to_cpu(iblk->skip_root.next[0]) + - sizeof(struct scoutfs_item)); - item->len = cpu_to_le16(sizeof(struct scoutfs_inode)); - item->skip_height = 1; + item->tnode.parent = 0; + item->tnode.left = 0; + item->tnode.right = 0; + pseudo_random_bytes(&item->tnode.prio, sizeof(item->tnode.prio)); + item->val_len = cpu_to_le16(sizeof(struct scoutfs_inode)); + inode = (void *)(item + 1); inode->nlink = cpu_to_le32(2); inode->mode = cpu_to_le32(0755 | 0040000); @@ -145,52 +110,19 @@ static int write_new_fs(char *path, int fd) inode->mtime.sec = inode->atime.sec; inode->mtime.nsec = inode->atime.nsec; - ret = write_block(fd, blkno, &iblk->hdr); - if (ret) - goto out; - blkno = round_up(blkno, SCOUTFS_BLOCKS_PER_CHUNK); + bt->treap.off = cpu_to_le16((char *)&item->tnode - (char *)&bt->treap); + bt->total_free = cpu_to_le16(SCOUTFS_BLOCK_SIZE - + ((char *)(inode + 1) - (char *)bt)); + bt->tail_free = bt->total_free; - /* write the ring block whose manifest entry references the log block */ - memset(buf, 0, SCOUTFS_BLOCK_SIZE); - ring = buf; - ring->hdr = super->hdr; - ring->nr_entries = cpu_to_le16(2); - ent = (void *)(ring + 1); - ent->type = SCOUTFS_RING_ADD_MANIFEST; - ent->len = cpu_to_le16(sizeof(*mani)); - mani = (void *)(ent + 1); - mani->blkno = cpu_to_le64(blkno - SCOUTFS_BLOCKS_PER_CHUNK); - mani->seq = super->hdr.seq; - mani->level = 0; - mani->first = root_key; - mani->last = root_key; - ent = (void *)(mani + 1); - ent->type = SCOUTFS_RING_BITMAP; - ent->len = cpu_to_le16(sizeof(*bm)); - bm = (void *)(ent + 1); - memset(bm->bits, 0xff, sizeof(bm->bits)); - /* the first four chunks are allocated */ - bm->bits[0] = cpu_to_le64(~15ULL); - bm->bits[1] = cpu_to_le64(~0ULL); - - ret = write_block(fd, blkno, &ring->hdr); - if (ret) - goto out; - blkno += SCOUTFS_BLOCKS_PER_CHUNK; - - /* the ring has a single chunk for now */ - memset(buf, 0, SCOUTFS_BLOCK_SIZE); - map = buf; - map->hdr = super->hdr; - map->nr_chunks = cpu_to_le32(1); - map->blknos[0] = cpu_to_le64(blkno - SCOUTFS_BLOCKS_PER_CHUNK); - - ret = write_block(fd, blkno, &map->hdr); + ret = write_block(fd, blkno, &bt->hdr); if (ret) goto out; /* make sure the super references everything we just wrote */ - super->ring_map_blkno = cpu_to_le64(blkno); + super->btree_root.height = 1; + super->btree_root.ref.blkno = bt->hdr.blkno; + super->btree_root.ref.seq = bt->hdr.seq; /* write the two super blocks */ for (i = 0; i < SCOUTFS_SUPER_NR; i++) { @@ -210,12 +142,10 @@ static int write_new_fs(char *path, int fd) uuid_unparse(super->uuid, uuid_str); printf("Created scoutfs filesystem:\n" - " chunk bytes: %u\n" - " total chunks: %llu\n" + " block size: %u\n" " fsid: %llx\n" " uuid: %s\n", - SCOUTFS_CHUNK_SIZE, total_chunks, - le64_to_cpu(super->hdr.fsid), uuid_str); + SCOUTFS_BLOCK_SIZE, le64_to_cpu(super->hdr.fsid), uuid_str); ret = 0; out: diff --git a/utils/src/print.c b/utils/src/print.c index 9dc322ac..ca0275a3 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -42,91 +42,28 @@ static void *read_block(int fd, u64 blkno) return buf; } -static void *read_chunk(int fd, u64 blkno) -{ - ssize_t ret; - void *buf; - - buf = malloc(SCOUTFS_CHUNK_SIZE); - if (!buf) - return NULL; - - ret = pread(fd, buf, SCOUTFS_CHUNK_SIZE, blkno << SCOUTFS_BLOCK_SHIFT); - if (ret != SCOUTFS_CHUNK_SIZE) { - fprintf(stderr, "read blkno %llu returned %zd: %s (%d)\n", - blkno, ret, strerror(errno), errno); - free(buf); - buf = NULL; - } - - return buf; -} - -static void print_le32_list(int indent, __le32 *data, int nr) -{ - char *fmt; - int pos; - int len; - int i; - u32 d; - - printf("["); - - pos = indent; - for (i = 0; i < nr; i++) { - if (i + 1 < nr) - fmt = "%u, "; - else - fmt = "%u"; - - d = le32_to_cpu(data[i]); - len = snprintf(NULL, 0, fmt, d); - if (pos + len > 78) { - printf("\n%*c", indent, ' '); - pos = indent; - } - - printf(fmt, d); - pos += len; - } - - printf("]\n"); -} - static void print_block_header(struct scoutfs_block_header *hdr) { u32 crc = crc_block(hdr); char valid_str[40]; if (crc != le32_to_cpu(hdr->crc)) - sprintf(valid_str, "# != %08x", crc); + sprintf(valid_str, "(!= %08x) ", crc); else valid_str[0] = '\0'; - printf(" header:\n" - " crc: %08x %s\n" - " fsid: %llx\n" - " seq: %llu\n" - " blkno: %llu\n", + printf(" hdr: crc %08x %sfsid %llx seq %llu blkno %llu\n", le32_to_cpu(hdr->crc), valid_str, le64_to_cpu(hdr->fsid), le64_to_cpu(hdr->seq), le64_to_cpu(hdr->blkno)); } static void print_inode(struct scoutfs_inode *inode) { - printf(" inode:\n" - " size: %llu\n" - " blocks: %llu\n" - " nlink: %u\n" - " uid: %u\n" - " gid: %u\n" - " mode: 0%o\n" - " rdev: 0x%x\n" - " salt: 0x%x\n" - " max_dirent_hash_nr: %u\n" - " atime: %llu.%08u\n" - " ctime: %llu.%08u\n" - " mtime: %llu.%08u\n", + printf(" inode: size: %llu blocks: %llu nlink: %u\n" + " uid: %u gid: %u mode: 0%o rdev: 0x%x\n" + " salt: 0x%x max_dirent_hash_nr: %u\n" + " atime: %llu.%08u ctime: %llu.%08u\n" + " mtime: %llu.%08u\n", le64_to_cpu(inode->size), le64_to_cpu(inode->blocks), le32_to_cpu(inode->nlink), le32_to_cpu(inode->uid), le32_to_cpu(inode->gid), le32_to_cpu(inode->mode), @@ -150,271 +87,83 @@ static void print_dirent(struct scoutfs_dirent *dent, unsigned int val_len) name[i] = isprint(dent->name[i]) ? dent->name[i] : '.'; name[i] = '\0'; - printf(" dirent:\n" - " ino: %llu\n" - " type: %u\n" - " name: \"%.*s\"\n", + printf(" dirent: ino: %llu type: %u name: \"%.*s\"\n", le64_to_cpu(dent->ino), dent->type, i, name); } -static void print_item(struct scoutfs_item *item, void *val) +static void print_btree_item(unsigned int off, struct scoutfs_btree_item *item) { - printf(" item:\n" - " key: "SKF"\n" - " offset: %u\n" - " len: %u\n" - " skip_height: %u\n" - " skip_next[]: ", - SKA(&item->key), - le32_to_cpu(item->offset), - le16_to_cpu(item->len), - item->skip_height); - - print_le32_list(22, item->skip_next, item->skip_height); + printf(" item: key "SKF" val_len %u off %u tnode: parent %u left %u right %u " + "prio %x\n", + SKA(&item->key), le16_to_cpu(item->val_len), off, + le16_to_cpu(item->tnode.parent), + le16_to_cpu(item->tnode.left), + le16_to_cpu(item->tnode.right), + le32_to_cpu(item->tnode.prio)); switch(item->key.type) { case SCOUTFS_INODE_KEY: - print_inode(val); + print_inode((void *)item->val); break; case SCOUTFS_DIRENT_KEY: - print_dirent(val, le16_to_cpu(item->len)); + print_dirent((void *)item->val, le16_to_cpu(item->val_len)); break; } } -static int print_log_segment(int fd, u64 nr) -{ - struct scoutfs_item_block *iblk; - struct scoutfs_bloom_block *blm; - struct scoutfs_item *item; - char *buf; - char *val; - __le32 next; - int i; - - buf = read_chunk(fd, nr); - if (!buf) - return -ENOMEM; - - for (i = 0; i < SCOUTFS_BLOOM_BLOCKS; i++) { - - blm = (void *)(buf + (i << SCOUTFS_BLOCK_SHIFT)); - - printf("bloom block:\n"); - print_block_header(&blm->hdr); - } - - iblk = (void *)(buf + (SCOUTFS_BLOOM_BLOCKS << SCOUTFS_BLOCK_SHIFT)); - - printf("item block:\n"); - print_block_header(&iblk->hdr); - printf(" first: "SKF"\n" - " last: "SKF"\n" - " skip_root.next[]: ", - SKA(&iblk->first), SKA(&iblk->last)); - print_le32_list(23, iblk->skip_root.next, SCOUTFS_SKIP_HEIGHT); - - next = iblk->skip_root.next[0]; - while (next) { - item = (void *)(buf + le32_to_cpu(next)); - val = (void *)(buf + le32_to_cpu(item->offset)); - print_item(item, val); - next = item->skip_next[0]; - } - - free(buf); - - return 0; -} - -static int print_log_segments(int fd, __le64 *log_segs, u64 total_chunks) +static int print_btree_block(int fd, __le64 blkno, u8 level) { + struct scoutfs_btree_item *item; + struct scoutfs_btree_block *bt; + struct scoutfs_block_ref *ref; + unsigned int off; int ret = 0; int err; - s64 nr; - - while ((nr = find_first_le_bit(log_segs, total_chunks)) >= 0) { - clear_le_bit(log_segs, nr); - - err = print_log_segment(fd, nr << SCOUTFS_CHUNK_BLOCK_SHIFT); - if (!ret && err) - ret = err; - } - - return ret; -} - -static char *ent_type_str(u8 type) -{ - switch (type) { - case SCOUTFS_RING_ADD_MANIFEST: - return "ADD_MANIFEST"; - case SCOUTFS_RING_DEL_MANIFEST: - return "DEL_MANIFEST"; - case SCOUTFS_RING_BITMAP: - return "BITMAP"; - default: - return "(unknown)"; - } -} - -static void print_ring_entry(int fd, struct scoutfs_ring_entry *ent) -{ - struct scoutfs_manifest_entry *ment; - struct scoutfs_ring_bitmap *bm; - - printf(" entry:\n" - " type: %u # %s\n" - " len: %u\n", - ent->type, ent_type_str(ent->type), le16_to_cpu(ent->len)); - - switch(ent->type) { - case SCOUTFS_RING_ADD_MANIFEST: - ment = (void *)(ent + 1); - printf(" blkno: %llu\n" - " seq: %llu\n" - " level: %u\n" - " first: "SKF"\n" - " last: "SKF"\n", - le64_to_cpu(ment->blkno), le64_to_cpu(ment->seq), - ment->level, SKA(&ment->first), SKA(&ment->last)); - break; - case SCOUTFS_RING_DEL_MANIFEST: - ment = (void *)(ent + 1); - printf(" blkno: %llu\n" - " seq: %llu\n" - " level: %u\n" - " first: "SKF"\n" - " last: "SKF"\n", - le64_to_cpu(ment->blkno), le64_to_cpu(ment->seq), - ment->level, SKA(&ment->first), SKA(&ment->last)); - break; - case SCOUTFS_RING_BITMAP: - bm = (void *)(ent + 1); - printf(" offset: %u\n" - " bits: 0x%llx%llx\n", - le32_to_cpu(bm->offset), - le64_to_cpu(bm->bits[1]), le64_to_cpu(bm->bits[0])); - break; - } -} - -static void update_log_segs(struct scoutfs_ring_entry *ent, - __le64 *log_segs) -{ - struct scoutfs_manifest_entry *ment; - u64 bit; - - switch(ent->type) { - case SCOUTFS_RING_ADD_MANIFEST: - ment = (void *)(ent + 1); - bit = le64_to_cpu(ment->blkno) >> SCOUTFS_CHUNK_BLOCK_SHIFT; - set_le_bit(log_segs, bit); - break; - case SCOUTFS_RING_DEL_MANIFEST: - ment = (void *)(ent + 1); - bit = le64_to_cpu(ment->blkno) >> SCOUTFS_CHUNK_BLOCK_SHIFT; - clear_le_bit(log_segs, bit); - break; - } -} - -static int print_ring_block(int fd, u64 blkno, __le64 *log_segs) -{ - struct scoutfs_ring_block *ring; - struct scoutfs_ring_entry *ent; - size_t off; - int ret = 0; int i; - /* XXX just printing the first block for now */ - - ring = read_block(fd, blkno); - if (!ring) + bt = read_block(fd, le64_to_cpu(blkno)); + if (!bt) return -ENOMEM; - printf("ring block:\n"); - print_block_header(&ring->hdr); - printf(" nr_entries: %u\n", le16_to_cpu(ring->nr_entries)); + printf("btree blkno %llu\n", le64_to_cpu(blkno)); + print_block_header(&bt->hdr); + printf(" treap.off %u total_free %u tail_free %u nr_items %u\n", + le16_to_cpu(bt->treap.off), + le16_to_cpu(bt->total_free), + le16_to_cpu(bt->tail_free), + le16_to_cpu(bt->nr_items)); - off = sizeof(struct scoutfs_ring_block); - for (i = 0; i < le16_to_cpu(ring->nr_entries); i++) { - ent = (void *)((char *)ring + off); - - update_log_segs(ent, log_segs); - print_ring_entry(fd, ent); - - off += sizeof(struct scoutfs_ring_entry) + - le16_to_cpu(ent->len); - } - - free(ring); - return ret; -} - -/* - * Print all the active ring blocks that are referenced by the super - * and which were mapped by the map blocks that we printed. - */ -static int print_ring_blocks(int fd, struct scoutfs_super_block *super, - u64 *ring_blknos, __le64 *log_segs) -{ - u64 block; - u64 blkno; - u64 i; - int ret = 0; - int err; - - block = le64_to_cpu(super->ring_first_block); - - for (i = 0; i < le64_to_cpu(super->ring_active_blocks); i++) { - blkno = ring_blknos[block >> SCOUTFS_CHUNK_BLOCK_SHIFT] + - (block & SCOUTFS_CHUNK_BLOCK_MASK); - - err = print_ring_block(fd, blkno, log_segs); - if (err && !ret) - ret = err; - - if (++block == le64_to_cpu(super->ring_total_blocks)) - block = 0; - } - - return ret; -} - -/* - * print a chunk's worth of map blocks and stop if we hit a partial - * block. - */ -static int print_map_blocks(int fd, u64 blkno, u64 *ring_blknos) -{ - struct scoutfs_ring_map_block *map; - int r = 0; - int b; - int i; - - for (b = 0; SCOUTFS_BLOCKS_PER_CHUNK; b++) { - map = read_block(fd, blkno + b); - if (!map) - return -ENOMEM; - - printf("map block:\n"); - print_block_header(&map->hdr); - printf(" nr_chunks: %u\n", le32_to_cpu(map->nr_chunks)); - - printf(" blknos: "); - for (i = 0; i < le32_to_cpu(map->nr_chunks); i++, r++) { - printf(" %llu\n", le64_to_cpu(map->blknos[i])); - ring_blknos[r] = le64_to_cpu(map->blknos[i]); + /* XXX just print in offset order */ + item = (void *)(bt + 1); + for (i = 0; i < le16_to_cpu(bt->nr_items); i++) { + if (item->tnode.parent == cpu_to_le16(1)) { + i--; + } else { + off = (char *)&item->tnode - (char *)&bt->treap; + print_btree_item(off, item); } - free(map); - - if (i != SCOUTFS_RING_MAP_BLOCKS) - break; + item = (void *)&item->val[le16_to_cpu(item->val_len)]; } - return 0; + item = (void *)(bt + 1); + for (i = 0; level && i < le16_to_cpu(bt->nr_items); i++) { + if (item->tnode.parent == cpu_to_le16(1)) { + i--; + } else { + ref = (void *)item->val; + + err = print_btree_block(fd, ref->blkno, level - 1); + if (err && !ret) + ret = err; + } + + item = (void *)&item->val[le16_to_cpu(item->val_len)]; + } + + free(bt); + + return ret; } static int print_super_blocks(int fd) @@ -422,9 +171,6 @@ static int print_super_blocks(int fd) struct scoutfs_super_block *super; struct scoutfs_super_block recent = { .hdr.seq = 0 }; char uuid_str[37]; - __le64 *log_segs; - u64 *ring_blknos; - u64 total_chunks; int ret = 0; int err; int i; @@ -436,28 +182,14 @@ static int print_super_blocks(int fd) uuid_unparse(super->uuid, uuid_str); - printf("super:\n"); + printf("super blkno %llu\n", (u64)SCOUTFS_SUPER_BLKNO + i); print_block_header(&super->hdr); - printf(" id: %llx\n" - " uuid: %s\n" - " bloom_salts: ", - le64_to_cpu(super->id), - uuid_str); - print_le32_list(18, super->bloom_salts, SCOUTFS_BLOOM_SALTS); - printf(" total_chunks: %llu\n" - " ring_map_blkno: %llu\n" - " ring_map_seq: %llu\n" - " ring_first_block: %llu\n" - " ring_active_blocks: %llu\n" - " ring_total_blocks: %llu\n" - " ring_seq: %llu\n", - le64_to_cpu(super->total_chunks), - le64_to_cpu(super->ring_map_blkno), - le64_to_cpu(super->ring_map_seq), - le64_to_cpu(super->ring_first_block), - le64_to_cpu(super->ring_active_blocks), - le64_to_cpu(super->ring_total_blocks), - le64_to_cpu(super->ring_seq)); + printf(" id %llx uuid %s\n", + le64_to_cpu(super->id), uuid_str); + printf(" btree_root: height %u seq %llu blkno %llu\n", + super->btree_root.height, + le64_to_cpu(super->btree_root.ref.seq), + le64_to_cpu(super->btree_root.ref.blkno)); if (le64_to_cpu(super->hdr.seq) > le64_to_cpu(recent.hdr.seq)) memcpy(&recent, super, sizeof(recent)); @@ -466,37 +198,12 @@ static int print_super_blocks(int fd) } super = &recent; - total_chunks = le64_to_cpu(super->total_chunks); - /* - * Allocate a bitmap big enough to describe all the chunks and - * we can have at most a full chunk worth of map blocks. - */ - log_segs = calloc(1, (total_chunks + 63) / 8); - ring_blknos = calloc(1, SCOUTFS_CHUNK_SIZE); - if (!log_segs || !ring_blknos) { - ret = -ENOMEM; - goto out; - } - - err = print_map_blocks(fd, le64_to_cpu(super->ring_map_blkno), - ring_blknos); + 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; - - err = print_ring_blocks(fd, super, ring_blknos, log_segs); - if (err && !ret) - ret = err; - - err = print_log_segments(fd, log_segs, total_chunks); - if (err && !ret) - ret = err; - -out: - if (log_segs) - free(log_segs); - if (ring_blknos) - free(ring_blknos); return ret; } From 1235f04c4a4daec6f4456b3ad267f7567227c2a4 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Thu, 14 Apr 2016 12:59:37 -0700 Subject: [PATCH 024/235] Print parent block ref item values Signed-off-by: Zach Brown --- utils/src/print.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/utils/src/print.c b/utils/src/print.c index ca0275a3..3aa6277d 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -91,7 +91,14 @@ static void print_dirent(struct scoutfs_dirent *dent, unsigned int val_len) le64_to_cpu(dent->ino), dent->type, i, name); } -static void print_btree_item(unsigned int off, struct scoutfs_btree_item *item) +static void print_block_ref(struct scoutfs_block_ref *ref) +{ + printf(" ref: blkno %llu seq %llu\n", + le64_to_cpu(ref->blkno), le64_to_cpu(ref->seq)); +} + +static void print_btree_item(unsigned int off, struct scoutfs_btree_item *item, + u8 level) { printf(" item: key "SKF" val_len %u off %u tnode: parent %u left %u right %u " "prio %x\n", @@ -101,6 +108,11 @@ static void print_btree_item(unsigned int off, struct scoutfs_btree_item *item) le16_to_cpu(item->tnode.right), le32_to_cpu(item->tnode.prio)); + if (level) { + print_block_ref((void *)item->val); + return; + } + switch(item->key.type) { case SCOUTFS_INODE_KEY: print_inode((void *)item->val); @@ -140,7 +152,7 @@ static int print_btree_block(int fd, __le64 blkno, u8 level) i--; } else { off = (char *)&item->tnode - (char *)&bt->treap; - print_btree_item(off, item); + print_btree_item(off, item, level); } item = (void *)&item->val[le16_to_cpu(item->val_len)]; From 77c673f98400c26f37138e2d9ff74729abdadc3b Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Fri, 29 Apr 2016 21:34:44 -0700 Subject: [PATCH 025/235] Add mkfs and print support for buddy alloc Initialize the block count fields in the super block on mkfs and print out the buddy allocator fields and blocks. Signed-off-by: Zach Brown --- utils/src/format.h | 38 +++++++++++++++++++++++++++++ utils/src/mkfs.c | 37 ++++++++++++++++++++++++++-- utils/src/print.c | 60 +++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 130 insertions(+), 5 deletions(-) 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; } From 67ad29508d260259402d89060bb3194ba8b625d3 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Sun, 1 May 2016 09:11:52 -0700 Subject: [PATCH 026/235] Update for next_ino in super block Add support for storing the next allocated inode in the super block. Signed-off-by: Zach Brown --- utils/src/format.h | 1 + utils/src/mkfs.c | 1 + utils/src/print.c | 4 +++- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/utils/src/format.h b/utils/src/format.h index 6f80059e..e7bbcb59 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -149,6 +149,7 @@ struct scoutfs_super_block { struct scoutfs_block_header hdr; __le64 id; __u8 uuid[SCOUTFS_UUID_BYTES]; + __le64 next_ino; __le64 total_blocks; __le32 buddy_blocks; __le32 buddy_sweep_bit; diff --git a/utils/src/mkfs.c b/utils/src/mkfs.c index 116e8abf..af2a9661 100644 --- a/utils/src/mkfs.c +++ b/utils/src/mkfs.c @@ -114,6 +114,7 @@ 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->next_ino = cpu_to_le64(SCOUTFS_ROOT_INO + 1); super->total_blocks = cpu_to_le64(total_blocks); super->buddy_blocks = cpu_to_le32(buddy_blocks); diff --git a/utils/src/print.c b/utils/src/print.c index 36097cfe..23feb908 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -241,8 +241,10 @@ 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" + printf(" next_ino %llu total_blocks %llu buddy_blocks %u " + "buddy_sweep_bit %u\n" " buddy_head %llu buddy_tail %llu\n", + le64_to_cpu(super->next_ino), le64_to_cpu(super->total_blocks), le32_to_cpu(super->buddy_blocks), le32_to_cpu(super->buddy_sweep_bit), From 29c1f529f12ab37985a3138b697e82223958e914 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Mon, 2 May 2016 21:40:02 -0700 Subject: [PATCH 027/235] Get rid of max dirent collision nr in inode The slightly tweaked format that uses linear probing to mitigate dirent name hash collisions doesn't need a record of the greatest number of collisions in the dir inode. Signed-off-by: Zach Brown --- utils/src/format.h | 18 ++++++++++++------ utils/src/print.c | 3 +-- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index e7bbcb59..5deca747 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -182,7 +182,6 @@ struct scoutfs_inode { __le32 mode; __le32 rdev; __le32 salt; - __u8 max_dirent_hash_nr; struct scoutfs_timespec atime; struct scoutfs_timespec ctime; struct scoutfs_timespec mtime; @@ -201,12 +200,19 @@ struct scoutfs_dirent { } __packed; /* - * The max number of dirent hash values determines the overhead of - * lookups in very large directories. With 31bit offsets the number - * of entries stored before enospc tends to plateau around 200 million - * entries around 8 functions. That seems OK for now. + * Dirent items are stored at keys with the offset set to the hash of + * the name. Creation can find that hash values collide and will + * attempt to linearly probe this many following hash values looking for + * an unused value. + * + * In small directories this doesn't really matter because hash values + * will so very rarely collide. At around 50k items we start to see our + * first collisions. 16 slots is still pretty quick to scan in the + * btree and it gets us up into the hundreds of millions of entries + * before enospc is returned as we run out of hash values. */ -#define SCOUTFS_MAX_DENT_HASH_NR 8 +#define SCOUTFS_DIRENT_COLL_NR 16 + #define SCOUTFS_NAME_LEN 255 /* diff --git a/utils/src/print.c b/utils/src/print.c index 23feb908..7e51370e 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -61,14 +61,13 @@ static void print_inode(struct scoutfs_inode *inode) { printf(" inode: size: %llu blocks: %llu nlink: %u\n" " uid: %u gid: %u mode: 0%o rdev: 0x%x\n" - " salt: 0x%x max_dirent_hash_nr: %u\n" + " salt: 0x%x\n" " atime: %llu.%08u ctime: %llu.%08u\n" " mtime: %llu.%08u\n", le64_to_cpu(inode->size), le64_to_cpu(inode->blocks), le32_to_cpu(inode->nlink), le32_to_cpu(inode->uid), le32_to_cpu(inode->gid), le32_to_cpu(inode->mode), le32_to_cpu(inode->rdev), le32_to_cpu(inode->salt), - inode->max_dirent_hash_nr, le64_to_cpu(inode->atime.sec), le32_to_cpu(inode->atime.nsec), le64_to_cpu(inode->ctime.sec), From 54867b0f9cce8174fcd403a6dd8383c3f1849e8b Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Thu, 26 May 2016 21:58:14 -0700 Subject: [PATCH 028/235] Add support for printing kernel traces Add a 'trace' command which uses the debugfs file created by the scoutfs kernel module to read and print trace messages. Signed-off-by: Zach Brown --- utils/src/ioctl.h | 30 ++++++++ utils/src/sparse.h | 2 + utils/src/trace.c | 169 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 201 insertions(+) create mode 100644 utils/src/ioctl.h create mode 100644 utils/src/trace.c diff --git a/utils/src/ioctl.h b/utils/src/ioctl.h new file mode 100644 index 00000000..1accfdae --- /dev/null +++ b/utils/src/ioctl.h @@ -0,0 +1,30 @@ +#ifndef _SCOUTFS_IOCTL_H_ +#define _SCOUTFS_IOCTL_H_ + +/* XXX I have no idea how these are chosen. */ +#define SCOUTFS_IOCTL_MAGIC 's' + +struct scoutfs_ioctl_buf { + __u64 ptr; + __s32 len; +} __packed; + +/* + * Fills the buffer with a packed array of format strings. Trace records + * refer to the format strings in the buffer by their byte offset. + */ +#define SCOUTFS_IOC_GET_TRACE_FORMATS _IOW(SCOUTFS_IOCTL_MAGIC, 1, \ + struct scoutfs_ioctl_buf) + +struct scoutfs_trace_record { + __u16 format_off; + __u8 nr; + __u8 data[0]; +} __packed; +/* + * Fills the buffer with trace records. + */ +#define SCOUTFS_IOC_GET_TRACE_RECORDS _IOW(SCOUTFS_IOCTL_MAGIC, 2, \ + struct scoutfs_ioctl_buf) + +#endif diff --git a/utils/src/sparse.h b/utils/src/sparse.h index 63b66ca9..7842aca2 100644 --- a/utils/src/sparse.h +++ b/utils/src/sparse.h @@ -24,12 +24,14 @@ extern unsigned int __builtin_ia32_crc32qi(unsigned int, unsigned char); typedef unsigned char u8; typedef unsigned short u16; typedef unsigned int u32; +typedef int s32; typedef unsigned long long u64; typedef signed long long s64; typedef u8 __u8; typedef u16 __u16; typedef u32 __u32; +typedef s32 __s32; typedef u64 __u64; typedef u16 __bitwise __le16; diff --git a/utils/src/trace.c b/utils/src/trace.c new file mode 100644 index 00000000..ebff1df0 --- /dev/null +++ b/utils/src/trace.c @@ -0,0 +1,169 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sparse.h" +#include "util.h" +#include "ioctl.h" +#include "cmd.h" + +static int get_ibuf(int fd, int cmd, struct scoutfs_ioctl_buf *ibuf) +{ + int ret; + + ibuf->ptr = 0; + ret = 1024 * 1024; + do { + ibuf->len = ret; + if (ibuf->ptr) + free((void *)(intptr_t)ibuf->ptr); + ibuf->ptr = (intptr_t)malloc(ibuf->len); + if (!ibuf->ptr) { + ret = -errno; + fprintf(stderr, "allocate %d bytes failed: %s (%d)\n", + ibuf->len, strerror(errno), errno); + break; + } + + ret = ioctl(fd, cmd, ibuf); + if (ret < 0) { + ret = -errno; + fprintf(stderr, "ioctl cmd %u failed: %s (%d)\n", + cmd, strerror(errno), errno); + break; + } + } while (ret > ibuf->len); + + if (ret >= 0) { + ibuf->len = ret; + ret = 0; + } + + return ret; +} + +static int decode_u64_bytes(struct scoutfs_trace_record *rec, u64 *args) +{ + u8 *data; + int shift; + u64 val; + int i; + + data = rec->data; + for (i = 0; i < rec->nr; i++) { + val = 0; + shift = 0; + for (;;) { + val |= (u64)(*data & 127) << shift; + + if (!((*(data++)) & 128)) + break; + + shift += 7; + } + + args[i] = val; + } + + return data - rec->data; +} + +/* MY EYES */ +static void printf_nr_args(char *fmt, int nr, u64 *args) +{ + switch(nr) { + case 0: printf(fmt); break; + case 1: printf(fmt, args[0]); break; + case 2: printf(fmt, args[0], args[1]); break; + case 3: printf(fmt, args[0], args[1], args[2]); break; + case 4: printf(fmt, args[0], args[1], args[2], args[3]); break; + case 5: printf(fmt, args[0], args[1], args[2], args[3], args[4]); break; + case 6: printf(fmt, args[0], args[1], args[2], args[3], args[4], + args[5]); break; + case 7: printf(fmt, args[0], args[1], args[2], args[3], args[4], + args[5], args[6]); break; + case 8: printf(fmt, args[0], args[1], args[2], args[3], args[4], + args[5], args[6], args[7]); break; + case 9: printf(fmt, args[0], args[1], args[2], args[3], args[4], + args[5], args[6], args[7], args[8]); break; + case 10: printf(fmt, args[0], args[1], args[2], args[3], args[4], + args[5], args[6], args[7], args[8], args[9]); break; + case 11: printf(fmt, args[0], args[1], args[2], args[3], args[4], + args[5], args[6], args[7], args[8], args[9], + args[10]); break; + case 12: printf(fmt, args[0], args[1], args[2], args[3], args[4], + args[5], args[6], args[7], args[8], args[9], + args[10], args[11]); break; + case 13: printf(fmt, args[0], args[1], args[2], args[3], args[4], + args[5], args[6], args[7], args[8], args[9], + args[10], args[11], args[12]); break; + case 14: printf(fmt, args[0], args[1], args[2], args[3], args[4], + args[5], args[6], args[7], args[8], args[9], + args[10], args[11], args[12], args[13]); break; + case 15: printf(fmt, args[0], args[1], args[2], args[3], args[4], + args[5], args[6], args[7], args[8], args[9], + args[10], args[11], args[12], args[13], args[14]); break; + case 16: printf(fmt, args[0], args[1], args[2], args[3], args[4], + args[5], args[6], args[7], args[8], args[9], + args[10], args[11], args[12], args[13], args[14], + args[15]); break; + default: + printf("(too many args: fmt '%s' nr %d)\n", fmt, nr); + break; + } +} + +static int trace_cmd(int argc, char **argv) +{ + char *path = "/sys/kernel/debug/scoutfs/trace"; + struct scoutfs_ioctl_buf fmts = {0,}; + struct scoutfs_ioctl_buf recs = {0,}; + struct scoutfs_trace_record *rec; + u64 args[32]; /* absurdly huge */ + int off; + int ret; + int fd; + + fd = open(path, O_RDONLY); + if (fd < 0) { + ret = -errno; + fprintf(stderr, "failed to open '%s': %s (%d)\n", + path, strerror(errno), errno); + return ret; + } + + /* + * Read the formats and records. Our fd on the debugfs file + * should prevent the module from unloading which is the only + * way the per-module formats could change. + */ + ret = get_ibuf(fd, SCOUTFS_IOC_GET_TRACE_FORMATS, &fmts) ?: + get_ibuf(fd, SCOUTFS_IOC_GET_TRACE_RECORDS, &recs); + if (ret) + goto out; + + for (off = 0; off < recs.len; ) { + rec = (void *)(intptr_t)(recs.ptr + off); + off += sizeof(*rec) + decode_u64_bytes(rec, args); + + printf_nr_args((char *)fmts.ptr + rec->format_off, + rec->nr, args); + printf("\n"); + } + +out: + close(fd); + return ret; +}; + +static void __attribute__((constructor)) trace_ctor(void) +{ + cmd_register("trace", "", "print scoutfs kernel traces", + trace_cmd); +} From d774e5308ba2d290f8099388f69f64a2ba57ace2 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Sat, 28 May 2016 12:41:30 -0700 Subject: [PATCH 029/235] Add support for printing traces from files We can extract the formats and records from a crash dump and print them from the files. Signed-off-by: Zach Brown --- utils/src/trace.c | 98 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 80 insertions(+), 18 deletions(-) diff --git a/utils/src/trace.c b/utils/src/trace.c index ebff1df0..9ba9153f 100644 --- a/utils/src/trace.c +++ b/utils/src/trace.c @@ -13,7 +13,7 @@ #include "ioctl.h" #include "cmd.h" -static int get_ibuf(int fd, int cmd, struct scoutfs_ioctl_buf *ibuf) +static int ibuf_ioctl(int fd, int cmd, struct scoutfs_ioctl_buf *ibuf) { int ret; @@ -48,6 +48,59 @@ static int get_ibuf(int fd, int cmd, struct scoutfs_ioctl_buf *ibuf) return ret; } +static int ibuf_read(char *path, struct scoutfs_ioctl_buf *ibuf) +{ + struct stat st; + ssize_t bytes; + int fd; + int ret; + + if (stat(path, &st)) { + ret = -errno; + fprintf(stderr, "stat %s failed: %s (%d)\n", + path, strerror(errno), errno); + return ret; + } + + if (!S_ISREG(st.st_mode)) { + fprintf(stderr, "%s must be a regular file\n", path); + return -EINVAL; + } + + fd = open(path, O_RDONLY); + if (fd < 0) { + ret = -errno; + fprintf(stderr, "failed to open '%s': %s (%d)\n", + path, strerror(errno), errno); + return ret; + } + + ibuf->len = st.st_size; + + ibuf->ptr = (intptr_t)malloc(ibuf->len); + if (!ibuf->ptr) { + ret = -errno; + fprintf(stderr, "allocate %d bytes failed: %s (%d)\n", + ibuf->len, strerror(errno), errno); + return ret; + } + + bytes = read(fd, (void *)(intptr_t)ibuf->ptr, ibuf->len); + if (bytes != ibuf->len) { + if (bytes < 0) + ret = -errno; + else + ret = -EIO; + fprintf(stderr, "read %d bytes from %s returned %zd: %s (%d)\n", + ibuf->len, path, bytes, strerror(errno), errno); + } else { + ret = 0; + } + + close(fd); + return ret; +} + static int decode_u64_bytes(struct scoutfs_trace_record *rec, u64 *args) { u8 *data; @@ -130,21 +183,27 @@ static int trace_cmd(int argc, char **argv) int ret; int fd; - fd = open(path, O_RDONLY); - if (fd < 0) { - ret = -errno; - fprintf(stderr, "failed to open '%s': %s (%d)\n", - path, strerror(errno), errno); - return ret; - } + if (argc == 0) { + fd = open(path, O_RDONLY); + if (fd < 0) { + ret = -errno; + fprintf(stderr, "failed to open '%s': %s (%d)\n", + path, strerror(errno), errno); + return ret; + } - /* - * Read the formats and records. Our fd on the debugfs file - * should prevent the module from unloading which is the only - * way the per-module formats could change. - */ - ret = get_ibuf(fd, SCOUTFS_IOC_GET_TRACE_FORMATS, &fmts) ?: - get_ibuf(fd, SCOUTFS_IOC_GET_TRACE_RECORDS, &recs); + /* fd on debugfs file pins formats that live in module */ + ret = ibuf_ioctl(fd, SCOUTFS_IOC_GET_TRACE_FORMATS, &fmts) ?: + ibuf_ioctl(fd, SCOUTFS_IOC_GET_TRACE_RECORDS, &recs); + close(fd); + } else { + if (argc != 2) { + fprintf(stderr, "specify trace and record files\n"); + return -EINVAL; + } + ret = ibuf_read(argv[0], &fmts) ?: + ibuf_read(argv[1], &recs); + } if (ret) goto out; @@ -158,12 +217,15 @@ static int trace_cmd(int argc, char **argv) } out: - close(fd); + if (fmts.ptr) + free((void *)(intptr_t)fmts.ptr); + if (recs.ptr) + free((void *)(intptr_t)recs.ptr); return ret; }; static void __attribute__((constructor)) trace_ctor(void) { - cmd_register("trace", "", "print scoutfs kernel traces", - trace_cmd); + cmd_register("trace", "[fmt file] [record file]", + "print scoutfs kernel traces", trace_cmd); } From a069bdd9456f54b50e62c3ce0778185f029cd255 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Mon, 4 Jul 2016 11:02:07 -0700 Subject: [PATCH 030/235] Add format header updates for xattrs The kernel now has items and structs for xattrs. Signed-off-by: Zach Brown --- utils/src/format.h | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index 5deca747..5210e7cb 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -51,8 +51,9 @@ struct scoutfs_key { * have to stress about cleverly allocating the types. */ #define SCOUTFS_INODE_KEY 1 -#define SCOUTFS_DIRENT_KEY 2 -#define SCOUTFS_DATA_KEY 3 +#define SCOUTFS_XATTR_KEY 2 +#define SCOUTFS_DIRENT_KEY 3 +#define SCOUTFS_DATA_KEY 4 #define SCOUTFS_MAX_ITEM_LEN 2048 @@ -237,4 +238,14 @@ enum { SCOUTFS_DT_WHT, }; +#define SCOUTFS_MAX_XATTR_NAME_LEN 255 +#define SCOUTFS_MAX_XATTR_VALUE_LEN 255 +#define SCOUTFS_XATTR_HASH_MASK 7ULL + +struct scoutfs_xattr { + __u8 name_len; + __u8 value_len; + __u8 name[0]; +} __packed; + #endif From 54044508fa6dfef17e9952a3b6f530802eaf6e44 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Tue, 5 Jul 2016 17:49:13 -0400 Subject: [PATCH 031/235] Add inodes-since command The kernel now has an ioctl to give us inode numbers with their sequence number for every inode that's been modified since a given tree update sequence number. Update mkfs and print to the on-disk format changes and add a trivial inodes-since command which calls the ioctl and prints the results. Signed-off-by: Zach Brown --- utils/src/format.h | 5 +++ utils/src/ioctl.h | 19 ++++++++++ utils/src/mkfs.c | 1 + utils/src/print.c | 5 +-- utils/src/since.c | 89 ++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 117 insertions(+), 2 deletions(-) create mode 100644 utils/src/since.c diff --git a/utils/src/format.h b/utils/src/format.h index 5210e7cb..e3112b3e 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -91,9 +91,14 @@ struct scoutfs_btree_block { __le16 nr_items; } __packed; +/* + * The item sequence number is set to the dirty block's sequence number + * when the item is modified. It is not changed by splits or merges. + */ struct scoutfs_btree_item { struct scoutfs_key key; struct scoutfs_treap_node tnode; + __le64 seq; __le16 val_len; char val[0]; } __packed; diff --git a/utils/src/ioctl.h b/utils/src/ioctl.h index 1accfdae..7ea6e5f2 100644 --- a/utils/src/ioctl.h +++ b/utils/src/ioctl.h @@ -27,4 +27,23 @@ struct scoutfs_trace_record { #define SCOUTFS_IOC_GET_TRACE_RECORDS _IOW(SCOUTFS_IOCTL_MAGIC, 2, \ struct scoutfs_ioctl_buf) +struct scoutfs_ioctl_ino_seq { + __u64 ino; + __u64 seq; +} __packed; + +struct scoutfs_ioctl_inodes_since { + __u64 first_ino; + __u64 last_ino; + __u64 seq; + struct scoutfs_ioctl_buf results; +} __packed; + +/* + * Adds entries to the user's buffer for each inode whose sequence + * number is greater than or equal to the given seq. + */ +#define SCOUTFS_IOC_INODES_SINCE _IOW(SCOUTFS_IOCTL_MAGIC, 3, \ + struct scoutfs_ioctl_inodes_since) + #endif diff --git a/utils/src/mkfs.c b/utils/src/mkfs.c index af2a9661..48e6e1b0 100644 --- a/utils/src/mkfs.c +++ b/utils/src/mkfs.c @@ -125,6 +125,7 @@ static int write_new_fs(char *path, int fd) bt->nr_items = cpu_to_le16(1); item = (void *)(bt + 1); + item->seq = cpu_to_le64(1); item->key = root_key; item->tnode.parent = 0; item->tnode.left = 0; diff --git a/utils/src/print.c b/utils/src/print.c index 7e51370e..e8f8f61e 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -99,9 +99,10 @@ static void print_block_ref(struct scoutfs_block_ref *ref) static void print_btree_item(unsigned int off, struct scoutfs_btree_item *item, u8 level) { - printf(" item: key "SKF" val_len %u off %u tnode: parent %u left %u right %u " + printf(" item: key "SKF" seq %llu val_len %u off %u tnode: parent %u left %u right %u " "prio %x\n", - SKA(&item->key), le16_to_cpu(item->val_len), off, + SKA(&item->key), le64_to_cpu(item->seq), + le16_to_cpu(item->val_len), off, le16_to_cpu(item->tnode.parent), le16_to_cpu(item->tnode.left), le16_to_cpu(item->tnode.right), diff --git a/utils/src/since.c b/utils/src/since.c new file mode 100644 index 00000000..4fe22572 --- /dev/null +++ b/utils/src/since.c @@ -0,0 +1,89 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sparse.h" +#include "util.h" +#include "ioctl.h" +#include "cmd.h" + +static int since_cmd(int argc, char **argv) +{ + struct scoutfs_ioctl_inodes_since args; + struct scoutfs_ioctl_ino_seq *iseq; + int len = 4 * 1024 * 1024; + char *endptr; + u64 nrs[3]; + void *ptr; + int ret; + int fd; + u64 n; + int i; + + if (argc != 4) { + fprintf(stderr, "must specify seq and path\n"); + return -EINVAL; + } + + for (i = 0; i < array_size(nrs); i++) { + n = strtoull(argv[i], &endptr, 0); + if (*endptr != '\0' || + ((n == LLONG_MIN || n == LLONG_MAX) && errno == ERANGE)) { + fprintf(stderr, "error parsing 64bit value '%s'\n", + argv[i]); + return -EINVAL; + } + nrs[i] = n; + } + + fd = open(argv[3], O_RDONLY); + if (fd < 0) { + ret = -errno; + fprintf(stderr, "failed to open '%s': %s (%d)\n", + argv[3], strerror(errno), errno); + return ret; + } + + ptr = malloc(len); + if (!ptr) { + fprintf(stderr, "must specify seq and path\n"); + close(fd); + return -EINVAL; + } + + args.first_ino = nrs[0]; + args.last_ino = nrs[1]; + args.seq = nrs[2]; + args.results.ptr = (intptr_t)ptr; + args.results.len = len; + + ret = ioctl(fd, SCOUTFS_IOC_INODES_SINCE, &args); + if (ret < 0) { + ret = -errno; + fprintf(stderr, "inodes_since ioctl failed: %s (%d)\n", + strerror(errno), errno); + goto out; + } + + n = ret / sizeof(*iseq); + for (i = 0, iseq = ptr; i < n; i++, iseq++) + printf("ino %llu seq %llu\n", iseq->ino, iseq->seq); + +out: + free(ptr); + close(fd); + return ret; +}; + +static void __attribute__((constructor)) since_ctor(void) +{ + cmd_register("inodes-since", " ", + "print inodes modified since seq #", since_cmd); +} From fc37ece26be28d310f164d8eed222fc08b0c1fc9 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Fri, 22 Jul 2016 13:54:10 -0700 Subject: [PATCH 032/235] Remove homebrew tracing Happily, it turns out that there are crash extensions for extracting trace messages from crash dumps. That's good enough for us. Signed-off-by: Zach Brown --- utils/src/ioctl.h | 28 +----- utils/src/since.c | 4 +- utils/src/trace.c | 231 ---------------------------------------------- 3 files changed, 5 insertions(+), 258 deletions(-) delete mode 100644 utils/src/trace.c diff --git a/utils/src/ioctl.h b/utils/src/ioctl.h index 7ea6e5f2..009ef7d4 100644 --- a/utils/src/ioctl.h +++ b/utils/src/ioctl.h @@ -4,29 +4,6 @@ /* XXX I have no idea how these are chosen. */ #define SCOUTFS_IOCTL_MAGIC 's' -struct scoutfs_ioctl_buf { - __u64 ptr; - __s32 len; -} __packed; - -/* - * Fills the buffer with a packed array of format strings. Trace records - * refer to the format strings in the buffer by their byte offset. - */ -#define SCOUTFS_IOC_GET_TRACE_FORMATS _IOW(SCOUTFS_IOCTL_MAGIC, 1, \ - struct scoutfs_ioctl_buf) - -struct scoutfs_trace_record { - __u16 format_off; - __u8 nr; - __u8 data[0]; -} __packed; -/* - * Fills the buffer with trace records. - */ -#define SCOUTFS_IOC_GET_TRACE_RECORDS _IOW(SCOUTFS_IOCTL_MAGIC, 2, \ - struct scoutfs_ioctl_buf) - struct scoutfs_ioctl_ino_seq { __u64 ino; __u64 seq; @@ -36,14 +13,15 @@ struct scoutfs_ioctl_inodes_since { __u64 first_ino; __u64 last_ino; __u64 seq; - struct scoutfs_ioctl_buf results; + __u64 buf_ptr; + __u32 buf_len; } __packed; /* * Adds entries to the user's buffer for each inode whose sequence * number is greater than or equal to the given seq. */ -#define SCOUTFS_IOC_INODES_SINCE _IOW(SCOUTFS_IOCTL_MAGIC, 3, \ +#define SCOUTFS_IOC_INODES_SINCE _IOW(SCOUTFS_IOCTL_MAGIC, 1, \ struct scoutfs_ioctl_inodes_since) #endif diff --git a/utils/src/since.c b/utils/src/since.c index 4fe22572..9eb7a7c1 100644 --- a/utils/src/since.c +++ b/utils/src/since.c @@ -61,8 +61,8 @@ static int since_cmd(int argc, char **argv) args.first_ino = nrs[0]; args.last_ino = nrs[1]; args.seq = nrs[2]; - args.results.ptr = (intptr_t)ptr; - args.results.len = len; + args.buf_ptr = (intptr_t)ptr; + args.buf_len = len; ret = ioctl(fd, SCOUTFS_IOC_INODES_SINCE, &args); if (ret < 0) { diff --git a/utils/src/trace.c b/utils/src/trace.c deleted file mode 100644 index 9ba9153f..00000000 --- a/utils/src/trace.c +++ /dev/null @@ -1,231 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "sparse.h" -#include "util.h" -#include "ioctl.h" -#include "cmd.h" - -static int ibuf_ioctl(int fd, int cmd, struct scoutfs_ioctl_buf *ibuf) -{ - int ret; - - ibuf->ptr = 0; - ret = 1024 * 1024; - do { - ibuf->len = ret; - if (ibuf->ptr) - free((void *)(intptr_t)ibuf->ptr); - ibuf->ptr = (intptr_t)malloc(ibuf->len); - if (!ibuf->ptr) { - ret = -errno; - fprintf(stderr, "allocate %d bytes failed: %s (%d)\n", - ibuf->len, strerror(errno), errno); - break; - } - - ret = ioctl(fd, cmd, ibuf); - if (ret < 0) { - ret = -errno; - fprintf(stderr, "ioctl cmd %u failed: %s (%d)\n", - cmd, strerror(errno), errno); - break; - } - } while (ret > ibuf->len); - - if (ret >= 0) { - ibuf->len = ret; - ret = 0; - } - - return ret; -} - -static int ibuf_read(char *path, struct scoutfs_ioctl_buf *ibuf) -{ - struct stat st; - ssize_t bytes; - int fd; - int ret; - - if (stat(path, &st)) { - ret = -errno; - fprintf(stderr, "stat %s failed: %s (%d)\n", - path, strerror(errno), errno); - return ret; - } - - if (!S_ISREG(st.st_mode)) { - fprintf(stderr, "%s must be a regular file\n", path); - return -EINVAL; - } - - fd = open(path, O_RDONLY); - if (fd < 0) { - ret = -errno; - fprintf(stderr, "failed to open '%s': %s (%d)\n", - path, strerror(errno), errno); - return ret; - } - - ibuf->len = st.st_size; - - ibuf->ptr = (intptr_t)malloc(ibuf->len); - if (!ibuf->ptr) { - ret = -errno; - fprintf(stderr, "allocate %d bytes failed: %s (%d)\n", - ibuf->len, strerror(errno), errno); - return ret; - } - - bytes = read(fd, (void *)(intptr_t)ibuf->ptr, ibuf->len); - if (bytes != ibuf->len) { - if (bytes < 0) - ret = -errno; - else - ret = -EIO; - fprintf(stderr, "read %d bytes from %s returned %zd: %s (%d)\n", - ibuf->len, path, bytes, strerror(errno), errno); - } else { - ret = 0; - } - - close(fd); - return ret; -} - -static int decode_u64_bytes(struct scoutfs_trace_record *rec, u64 *args) -{ - u8 *data; - int shift; - u64 val; - int i; - - data = rec->data; - for (i = 0; i < rec->nr; i++) { - val = 0; - shift = 0; - for (;;) { - val |= (u64)(*data & 127) << shift; - - if (!((*(data++)) & 128)) - break; - - shift += 7; - } - - args[i] = val; - } - - return data - rec->data; -} - -/* MY EYES */ -static void printf_nr_args(char *fmt, int nr, u64 *args) -{ - switch(nr) { - case 0: printf(fmt); break; - case 1: printf(fmt, args[0]); break; - case 2: printf(fmt, args[0], args[1]); break; - case 3: printf(fmt, args[0], args[1], args[2]); break; - case 4: printf(fmt, args[0], args[1], args[2], args[3]); break; - case 5: printf(fmt, args[0], args[1], args[2], args[3], args[4]); break; - case 6: printf(fmt, args[0], args[1], args[2], args[3], args[4], - args[5]); break; - case 7: printf(fmt, args[0], args[1], args[2], args[3], args[4], - args[5], args[6]); break; - case 8: printf(fmt, args[0], args[1], args[2], args[3], args[4], - args[5], args[6], args[7]); break; - case 9: printf(fmt, args[0], args[1], args[2], args[3], args[4], - args[5], args[6], args[7], args[8]); break; - case 10: printf(fmt, args[0], args[1], args[2], args[3], args[4], - args[5], args[6], args[7], args[8], args[9]); break; - case 11: printf(fmt, args[0], args[1], args[2], args[3], args[4], - args[5], args[6], args[7], args[8], args[9], - args[10]); break; - case 12: printf(fmt, args[0], args[1], args[2], args[3], args[4], - args[5], args[6], args[7], args[8], args[9], - args[10], args[11]); break; - case 13: printf(fmt, args[0], args[1], args[2], args[3], args[4], - args[5], args[6], args[7], args[8], args[9], - args[10], args[11], args[12]); break; - case 14: printf(fmt, args[0], args[1], args[2], args[3], args[4], - args[5], args[6], args[7], args[8], args[9], - args[10], args[11], args[12], args[13]); break; - case 15: printf(fmt, args[0], args[1], args[2], args[3], args[4], - args[5], args[6], args[7], args[8], args[9], - args[10], args[11], args[12], args[13], args[14]); break; - case 16: printf(fmt, args[0], args[1], args[2], args[3], args[4], - args[5], args[6], args[7], args[8], args[9], - args[10], args[11], args[12], args[13], args[14], - args[15]); break; - default: - printf("(too many args: fmt '%s' nr %d)\n", fmt, nr); - break; - } -} - -static int trace_cmd(int argc, char **argv) -{ - char *path = "/sys/kernel/debug/scoutfs/trace"; - struct scoutfs_ioctl_buf fmts = {0,}; - struct scoutfs_ioctl_buf recs = {0,}; - struct scoutfs_trace_record *rec; - u64 args[32]; /* absurdly huge */ - int off; - int ret; - int fd; - - if (argc == 0) { - fd = open(path, O_RDONLY); - if (fd < 0) { - ret = -errno; - fprintf(stderr, "failed to open '%s': %s (%d)\n", - path, strerror(errno), errno); - return ret; - } - - /* fd on debugfs file pins formats that live in module */ - ret = ibuf_ioctl(fd, SCOUTFS_IOC_GET_TRACE_FORMATS, &fmts) ?: - ibuf_ioctl(fd, SCOUTFS_IOC_GET_TRACE_RECORDS, &recs); - close(fd); - } else { - if (argc != 2) { - fprintf(stderr, "specify trace and record files\n"); - return -EINVAL; - } - ret = ibuf_read(argv[0], &fmts) ?: - ibuf_read(argv[1], &recs); - } - if (ret) - goto out; - - for (off = 0; off < recs.len; ) { - rec = (void *)(intptr_t)(recs.ptr + off); - off += sizeof(*rec) + decode_u64_bytes(rec, args); - - printf_nr_args((char *)fmts.ptr + rec->format_off, - rec->nr, args); - printf("\n"); - } - -out: - if (fmts.ptr) - free((void *)(intptr_t)fmts.ptr); - if (recs.ptr) - free((void *)(intptr_t)recs.ptr); - return ret; -}; - -static void __attribute__((constructor)) trace_ctor(void) -{ - cmd_register("trace", "[fmt file] [record file]", - "print scoutfs kernel traces", trace_cmd); -} From 1cacc50de0a2bfa13e13b867e6fdfd3c6ba1544c Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Fri, 22 Jul 2016 14:57:12 -0700 Subject: [PATCH 033/235] Remove old unused lebitmap code Signed-off-by: Zach Brown --- utils/src/lebitmap.c | 38 -------------------------------------- utils/src/lebitmap.h | 11 ----------- utils/src/print.c | 1 - 3 files changed, 50 deletions(-) delete mode 100644 utils/src/lebitmap.c delete mode 100644 utils/src/lebitmap.h diff --git a/utils/src/lebitmap.c b/utils/src/lebitmap.c deleted file mode 100644 index e7848947..00000000 --- a/utils/src/lebitmap.c +++ /dev/null @@ -1,38 +0,0 @@ -#define _GNU_SOURCE /* ffsll */ -#include - -#include "lebitmap.h" - -void set_le_bit(__le64 *bits, u64 nr) -{ - bits += nr / 64; - - *bits = cpu_to_le64(le64_to_cpu(*bits) | (1ULL << (nr & 63))); -} - -void clear_le_bit(__le64 *bits, u64 nr) -{ - bits += nr / 64; - - *bits = cpu_to_le64(le64_to_cpu(*bits) & ~(1ULL << (nr & 63))); -} - -int test_le_bit(__le64 *bits, u64 nr) -{ - bits += nr / 64; - - return !!(le64_to_cpu(*bits) & (1ULL << (nr & 63))); -} - -/* returns -1 or nr */ -s64 find_first_le_bit(__le64 *bits, s64 count) -{ - long nr; - - for (nr = 0; count > 0; bits++, nr += 64, count -= 64) { - if (*bits) - return nr + ffsll(le64_to_cpu(*bits)) - 1; - } - - return -1; -} diff --git a/utils/src/lebitmap.h b/utils/src/lebitmap.h deleted file mode 100644 index 9e399d33..00000000 --- a/utils/src/lebitmap.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef _LEBITMAP_H_ -#define _LEBITMAP_H_ - -#include "sparse.h" - -void set_le_bit(__le64 *bits, u64 nr); -void clear_le_bit(__le64 *bits, u64 nr); -int test_le_bit(__le64 *bits, u64 nr); -s64 find_first_le_bit(__le64 *bits, s64 count); - -#endif diff --git a/utils/src/print.c b/utils/src/print.c index e8f8f61e..775b5e52 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -15,7 +15,6 @@ #include "format.h" #include "cmd.h" #include "crc.h" -#include "lebitmap.h" /* XXX maybe these go somewhere */ #define SKF "%llu.%u.%llu" From c48e08a378a265dd8ba3df8f7a62116244539421 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Mon, 25 Jul 2016 16:26:38 -0700 Subject: [PATCH 034/235] Add -fno-strict-aliasing We modify the same memory through pointers of different types all the live long day. Signed-off-by: Zach Brown --- utils/Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/utils/Makefile b/utils/Makefile index aad55eed..71db96e8 100644 --- a/utils/Makefile +++ b/utils/Makefile @@ -1,4 +1,5 @@ -CFLAGS := -Wall -O2 -Werror -D_FILE_OFFSET_BITS=64 -g -mrdrnd -msse4.2 +CFLAGS := -Wall -O2 -Werror -D_FILE_OFFSET_BITS=64 -g -mrdrnd -msse4.2 \ + -fno-strict-aliasing BIN := src/scoutfs OBJ := $(patsubst %.c,%.o,$(wildcard src/*.c)) From 99167f6d66e509389c51732b763abd03a8187e56 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Wed, 27 Jul 2016 13:50:22 -0700 Subject: [PATCH 035/235] Expand little endian bitops functions We had the start of functions that operated on little endian bitmaps. This adds more operations and uses __packed to support unaligned bitmaps on platforms where unaligned accesses are a problem. Signed-off-by: Zach Brown --- utils/src/bitops.h | 64 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 61 insertions(+), 3 deletions(-) diff --git a/utils/src/bitops.h b/utils/src/bitops.h index 5ac429fc..abfa3755 100644 --- a/utils/src/bitops.h +++ b/utils/src/bitops.h @@ -1,6 +1,13 @@ #ifndef _BITOPS_H_ #define _BITOPS_H_ +/* + * Implement little endian bitmaps in terms of native longs. __packed + * is used to avoid unaligned accesses. + */ + +typedef unsigned long * __packed ulong_ptr; + #define BITS_PER_LONG (sizeof(long) * 8) #if __BYTE_ORDER == __LITTLE_ENDIAN #define BITOP_LE_SWIZZLE 0 @@ -8,13 +15,64 @@ #define BITOP_LE_SWIZZLE ((BITS_PER_LONG-1) & ~0x7) #endif -static inline void set_bit_le(int nr, void *addr) +static inline ulong_ptr nr_word(int nr, ulong_ptr longs) { - unsigned long *longs = addr; + return &longs[nr / BITS_PER_LONG]; +} + +static inline unsigned long nr_mask(int nr) +{ + return 1UL << (nr % BITS_PER_LONG); +} + +static inline int test_bit(int nr, ulong_ptr longs) +{ + return !!(*nr_word(nr, longs) & nr_mask(nr)); +} + +static inline void set_bit(int nr, ulong_ptr longs) +{ + *nr_word(nr, longs) |= nr_mask(nr); +} + +static inline void clear_bit(int nr, ulong_ptr longs) +{ + *nr_word(nr, longs) &= ~nr_mask(nr); +} + +static inline int test_bit_le(int nr, void *addr) +{ + return test_bit(nr ^ BITOP_LE_SWIZZLE, addr); +} + +static inline int test_and_set_bit_le(int nr, void *addr) +{ + int ret; nr ^= BITOP_LE_SWIZZLE; + ret = test_bit(nr, addr); + set_bit(nr, addr); + return ret; +} - longs[nr / BITS_PER_LONG] |= 1UL << (nr & (BITS_PER_LONG - 1)); +static inline void set_bit_le(int nr, void *addr) +{ + set_bit(nr ^ BITOP_LE_SWIZZLE, addr); +} + +static inline void clear_bit_le(int nr, void *addr) +{ + clear_bit(nr ^ BITOP_LE_SWIZZLE, addr); +} + +static inline int test_and_clear_bit_le(int nr, void *addr) +{ + int ret; + + nr ^= BITOP_LE_SWIZZLE; + ret = test_bit(nr, addr); + clear_bit(nr, addr); + return ret; } #endif From 4b86256904f9a6cc599a0a165925fecbd92b793a Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Wed, 27 Jul 2016 13:54:05 -0700 Subject: [PATCH 036/235] Ignore sparse warning for builtin fpclassify Signed-off-by: Zach Brown --- utils/sparse.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/utils/sparse.sh b/utils/sparse.sh index 61c7bd33..93791617 100755 --- a/utils/sparse.sh +++ b/utils/sparse.sh @@ -19,6 +19,9 @@ RE="$RE|error: attribute '__leaf__': unknown attribute" # yes, sparse, that's the size of memseting a 4 meg buffer all right RE="$RE|warning: memset with byte count of 4194304" +# some sparse versions don't know about some builtins +RE="$RE|error: undefined identifier '__builtin_fpclassify'" + # # don't filter out 'too many errors' here, it can signify that # sparse doesn't understand something and is throwing a *ton* From 6a97aa3c9a182c1bcfdc465b7ab71a183183060b Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Mon, 25 Jul 2016 13:45:34 -0700 Subject: [PATCH 037/235] Add support for the radix buddy bitmaps Update mkfs and print to support the buddy allocator that's indexed by radix blocks. Signed-off-by: Zach Brown --- utils/src/format.h | 106 +++++++++++++++---------------- utils/src/mkfs.c | 153 +++++++++++++++++++++++++++++++++++++++------ utils/src/print.c | 125 +++++++++++++++++++++++++----------- utils/src/sparse.h | 5 ++ 4 files changed, 278 insertions(+), 111 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index e3112b3e..4c23d6e4 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -19,7 +19,8 @@ */ #define SCOUTFS_SUPER_BLKNO ((64 * 1024) >> SCOUTFS_BLOCK_SHIFT) #define SCOUTFS_SUPER_NR 2 -#define SCOUTFS_BUDDY_BLKNO (SCOUTFS_SUPER_BLKNO + SCOUTFS_SUPER_NR) +#define SCOUTFS_BUDDY_BM_BLKNO (SCOUTFS_SUPER_BLKNO + SCOUTFS_SUPER_NR) +#define SCOUTFS_BUDDY_BM_NR 2 /* * This header is found at the start of every block so that we can @@ -35,6 +36,52 @@ struct scoutfs_block_header { __le64 blkno; } __packed; +/* + * Block references include the sequence number so that we can detect + * readers racing with writers and so that we can tell that we don't + * need to follow a reference when traversing based on seqs. + */ +struct scoutfs_block_ref { + __le64 blkno; + __le64 seq; +} __packed; + +struct scoutfs_bitmap_block { + struct scoutfs_block_header hdr; + __le64 bits[0]; +} __packed; + +/* + * Track allocations from BLOCK_SIZE to (BLOCK_SIZE << ..._ORDERS). + */ +#define SCOUTFS_BUDDY_ORDERS 8 + +struct scoutfs_buddy_block { + struct scoutfs_block_header hdr; + __le32 order_counts[SCOUTFS_BUDDY_ORDERS]; + __le64 bits[0]; +} __packed; + +/* + * If we had log2(raw bits) orders we'd fully use all of the raw bits in + * the block. We're close enough that the amount of space wasted at the + * end (~1/256th of the block, ~64 bytes) isn't worth worrying about. + */ +#define SCOUTFS_BUDDY_ORDER0_BITS \ + (((SCOUTFS_BLOCK_SIZE - sizeof(struct scoutfs_buddy_block)) * 8) / 2) + +struct scoutfs_buddy_indirect { + struct scoutfs_block_header hdr; + struct scoutfs_buddy_slot { + __u8 free_orders; + struct scoutfs_block_ref ref; + } slots[0]; +} __packed; + +#define SCOUTFS_BUDDY_SLOTS \ + ((SCOUTFS_BLOCK_SIZE - sizeof(struct scoutfs_buddy_block)) / \ + sizeof(struct scoutfs_buddy_slot)) + /* * We should be able to make the offset smaller if neither dirents nor * data items use the full 64 bits. @@ -57,16 +104,6 @@ struct scoutfs_key { #define SCOUTFS_MAX_ITEM_LEN 2048 -/* - * Block references include the sequence number so that we can detect - * readers racing with writers and so that we can tell that we don't - * need to follow a reference when traversing based on seqs. - */ -struct scoutfs_block_ref { - __le64 blkno; - __le64 seq; -} __packed; - struct scoutfs_treap_root { __le16 off; } __packed; @@ -109,48 +146,6 @@ 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. - * - * The ring map blocks describe the chunks that make up the ring. - * - * The rest of the ring fields describe the state of the ring blocks - * that are stored in their chunks. The active portion of the ring - * describes the current state of the system and is replayed on mount. - */ struct scoutfs_super_block { struct scoutfs_block_header hdr; __le64 id; @@ -158,10 +153,9 @@ struct scoutfs_super_block { __le64 next_ino; __le64 total_blocks; __le32 buddy_blocks; - __le32 buddy_sweep_bit; - __le64 buddy_head; - __le64 buddy_tail; struct scoutfs_btree_root btree_root; + struct scoutfs_block_ref buddy_ind_ref; + struct scoutfs_block_ref buddy_bm_ref; } __packed; #define SCOUTFS_ROOT_INO 1 diff --git a/utils/src/mkfs.c b/utils/src/mkfs.c index 48e6e1b0..83ad7ef3 100644 --- a/utils/src/mkfs.c +++ b/utils/src/mkfs.c @@ -22,10 +22,13 @@ /* * Update the block's header and write it out. */ -static int write_block(int fd, u64 blkno, struct scoutfs_block_header *hdr) +static int write_block(int fd, u64 blkno, struct scoutfs_super_block *super, + struct scoutfs_block_header *hdr) { ssize_t ret; + if (super) + *hdr = super->hdr; hdr->blkno = cpu_to_le64(blkno); hdr->crc = cpu_to_le32(crc_block(hdr)); @@ -40,21 +43,84 @@ static int write_block(int fd, u64 blkno, struct scoutfs_block_header *hdr) } /* - * 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. + * Calculate the number of buddy blocks that are needed to manage + * allocation of a device with the given number of total blocks. + * + * We need a little bit of overhead to write each transaction's dirty + * buddy blocks to free space. We chose 16MB for now which is wild + * overkill and should be dependent on the max transaction size. */ 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); + return DIV_ROUND_UP(total_blocks, SCOUTFS_BUDDY_ORDER0_BITS) + + ((16 * 1024 * 1024) / SCOUTFS_BLOCK_SIZE); +} - /* XXX check u32 overflow? */ +static u32 first_blkno(struct scoutfs_super_block *super) +{ + return SCOUTFS_BUDDY_BM_BLKNO + SCOUTFS_BUDDY_BM_NR + + le32_to_cpu(super->buddy_blocks); +} - return round_up(blocks, 2); +/* the starting bit offset in the block bitmap of an order's bitmap */ +static int order_off(int order) +{ + if (order == 0) + return 0; + + return (2 * SCOUTFS_BUDDY_ORDER0_BITS) - + (SCOUTFS_BUDDY_ORDER0_BITS / (1 << (order - 1))); +} + +/* the bit offset in the block bitmap of an order's bit */ +static int order_nr(int order, int nr) +{ + return order_off(order) + nr; +} + +static int test_buddy_bit(struct scoutfs_buddy_block *bud, int order, int nr) +{ + return test_bit_le(order_nr(order, nr), bud->bits); +} + +static void set_buddy_bit(struct scoutfs_buddy_block *bud, int order, int nr) +{ + if (!test_and_set_bit_le(order_nr(order, nr), bud->bits)) + le32_add_cpu(&bud->order_counts[order], 1); +} + +static void clear_buddy_bit(struct scoutfs_buddy_block *bud, int order, int nr) +{ + if (test_and_clear_bit_le(order_nr(order, nr), bud->bits)) + le32_add_cpu(&bud->order_counts[order], -1); +} + +/* merge lower orders buddies as we free up to the highest */ +static void free_order_bit(struct scoutfs_buddy_block *bud, int order, int nr) +{ + int i; + + for (i = order; i < SCOUTFS_BUDDY_ORDERS - 1; i++) { + + if (!test_buddy_bit(bud, i, nr ^ 1)) + break; + + clear_buddy_bit(bud, i, nr ^ 1); + nr >>= 1; + } + + set_buddy_bit(bud, i, nr); +} + +static u8 calc_free_orders(struct scoutfs_buddy_block *bud) +{ + u8 free = 0; + int i; + + for (i = 0; i < SCOUTFS_BUDDY_ORDERS; i++) + free |= (!!bud->order_counts[i]) << i; + + return free; } static int write_new_fs(char *path, int fd) @@ -63,6 +129,9 @@ static int write_new_fs(char *path, int fd) struct scoutfs_inode *inode; struct scoutfs_btree_block *bt; struct scoutfs_btree_item *item; + struct scoutfs_buddy_block *bud; + struct scoutfs_buddy_indirect *ind; + struct scoutfs_bitmap_block *bm; struct scoutfs_key root_key; struct timeval tv; char uuid_str[37]; @@ -71,6 +140,7 @@ static int write_new_fs(char *path, int fd) u64 blkno; u64 total_blocks; u64 buddy_blocks; + u8 free_orders; void *buf; int ret; @@ -105,9 +175,6 @@ static int write_new_fs(char *path, int fd) root_key.type = SCOUTFS_INODE_KEY; root_key.offset = 0; - /* start with the block after the supers */ - blkno = SCOUTFS_SUPER_BLKNO + SCOUTFS_SUPER_NR; - /* first initialize the super so we can use it to build structures */ memset(super, 0, SCOUTFS_BLOCK_SIZE); pseudo_random_bytes(&super->hdr.fsid, sizeof(super->hdr.fsid)); @@ -118,10 +185,11 @@ static int write_new_fs(char *path, int fd) super->total_blocks = cpu_to_le64(total_blocks); super->buddy_blocks = cpu_to_le32(buddy_blocks); + blkno = first_blkno(super); + /* write a btree leaf root inode item */ memset(buf, 0, SCOUTFS_BLOCK_SIZE); bt = buf; - bt->hdr = super->hdr; bt->nr_items = cpu_to_le16(1); item = (void *)(bt + 1); @@ -148,19 +216,68 @@ static int write_new_fs(char *path, int fd) ((char *)(inode + 1) - (char *)bt)); bt->tail_free = bt->total_free; - ret = write_block(fd, blkno, &bt->hdr); + ret = write_block(fd, blkno, super, &bt->hdr); if (ret) goto out; - /* make sure the super references everything we just wrote */ + /* the super references the btree block */ super->btree_root.height = 1; super->btree_root.ref.blkno = bt->hdr.blkno; super->btree_root.ref.seq = bt->hdr.seq; + /* free all the blocks in the first buddy block after btree block */ + memset(buf, 0, SCOUTFS_BLOCK_SIZE); + bud = buf; + for (i = 1; i < min(total_blocks - first_blkno(super), + SCOUTFS_BUDDY_ORDER0_BITS); i++) + free_order_bit(bud, 0, i); + free_orders = calc_free_orders(bud); + + blkno = SCOUTFS_BUDDY_BM_BLKNO + SCOUTFS_BUDDY_BM_NR; + ret = write_block(fd, blkno, super, &bud->hdr); + if (ret) + goto out; + + /* an indirect buddy block references the buddy bitmap block */ + memset(buf, 0, SCOUTFS_BLOCK_SIZE); + ind = buf; + for (i = 0; i < SCOUTFS_BUDDY_SLOTS; i++) { + ind->slots[i].free_orders = 0; + ind->slots[i].ref = (struct scoutfs_block_ref){0,}; + } + ind->slots[0].free_orders = free_orders; + ind->slots[0].ref.seq = super->hdr.seq; + ind->slots[0].ref.blkno = cpu_to_le64(blkno); + + blkno++; + ret = write_block(fd, blkno, super, &ind->hdr); + if (ret) + goto out; + + /* the super references the buddy indirect block */ + super->buddy_ind_ref.blkno = ind->hdr.blkno; + super->buddy_ind_ref.seq = ind->hdr.seq; + + /* a bitmap block records the two used buddy blocks */ + memset(buf, 0, SCOUTFS_BLOCK_SIZE); + bm = buf; + memset(bm->bits, 0xff, SCOUTFS_BLOCK_SIZE - + offsetof(struct scoutfs_bitmap_block, bits)); + bm->bits[0] = cpu_to_le64(~0ULL << 2); /* two low order bits clear */ + + ret = write_block(fd, SCOUTFS_BUDDY_BM_BLKNO, super, &bm->hdr); + if (ret) + goto out; + + /* the super references the buddy bitmap block */ + super->buddy_bm_ref.blkno = bm->hdr.blkno; + super->buddy_bm_ref.seq = bm->hdr.seq; + /* write the two super blocks */ for (i = 0; i < SCOUTFS_SUPER_NR; i++) { super->hdr.seq = cpu_to_le64(i + 1); - ret = write_block(fd, SCOUTFS_SUPER_BLKNO + i, &super->hdr); + ret = write_block(fd, SCOUTFS_SUPER_BLKNO + i, NULL, + &super->hdr); if (ret) goto out; } diff --git a/utils/src/print.c b/utils/src/print.c index 775b5e52..8b99bb27 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -177,46 +177,90 @@ static int print_btree_block(int fd, __le64 blkno, u8 level) return ret; } +static int print_buddy_block(int fd, struct scoutfs_super_block *super, + u64 blkno) +{ + struct scoutfs_buddy_block *bud; + int i; + + bud = read_block(fd, blkno); + if (!bud) + return -ENOMEM; + + printf("buddy blkno %llu\n", blkno); + print_block_header(&bud->hdr); + printf(" order_counts:"); + for (i = 0; i < SCOUTFS_BUDDY_ORDERS; i++) + printf(" %u", le32_to_cpu(bud->order_counts[i])); + printf("\n"); + + free(bud); + + return 0; +} + static int print_buddy_blocks(int fd, struct scoutfs_super_block *super) { - struct scoutfs_buddy_chunk *chunk; - struct scoutfs_buddy_block *bb; + struct scoutfs_buddy_indirect *ind; + struct scoutfs_buddy_slot *slot; u64 blkno; - u64 blocks; - u64 head; - u64 tail; + int ret = 0; + int err; 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); + blkno = le64_to_cpu(super->buddy_ind_ref.blkno); + ind = read_block(fd, blkno); + if (!ind) + return -ENOMEM; - /* XXX make sure values are sane */ + printf("buddy indirect blkno %llu\n", blkno); + print_block_header(&ind->hdr); - for (; head < tail; head++) { + for (i = 0; i < SCOUTFS_BUDDY_SLOTS; i++) { + slot = &ind->slots[i]; - blkno = SCOUTFS_BUDDY_BLKNO + (head % blocks); - bb = read_block(fd, blkno); - if (!bb) - return -ENOMEM; + /* only print slots with non-zero fields */ + if (!slot->free_orders && !slot->ref.seq && !slot->ref.blkno) + continue; - 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); + printf(" slot[%u]: free_orders: %x ref: seq %llu blkno %llu\n", + i, slot->free_orders, le64_to_cpu(slot->ref.seq), + le64_to_cpu(slot->ref.blkno)); } + for (i = 0; i < SCOUTFS_BUDDY_SLOTS; i++) { + slot = &ind->slots[i]; + + if (!slot->free_orders && !slot->ref.seq && !slot->ref.blkno) + continue; + + err = print_buddy_block(fd, super, + le64_to_cpu(slot->ref.blkno)); + if (err && !ret) + ret = err; + } + + free(ind); + + return ret; +} + + +static int print_bitmap_block(int fd, struct scoutfs_super_block *super) +{ + struct scoutfs_bitmap_block *bm; + u64 blkno; + + blkno = le64_to_cpu(super->buddy_bm_ref.blkno); + bm = read_block(fd, blkno); + if (!bm) + return -ENOMEM; + + printf("bitmap blkno %llu\n", blkno); + print_block_header(&bm->hdr); + + free(bm); + return 0; } @@ -240,15 +284,16 @@ 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(" next_ino %llu total_blocks %llu buddy_blocks %u " - "buddy_sweep_bit %u\n" - " buddy_head %llu buddy_tail %llu\n", + printf(" next_ino %llu total_blocks %llu buddy_blocks %u\n", le64_to_cpu(super->next_ino), 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)); + le32_to_cpu(super->buddy_blocks)); + printf(" buddy_bm_ref: seq %llu blkno %llu\n", + le64_to_cpu(super->buddy_bm_ref.seq), + le64_to_cpu(super->buddy_bm_ref.blkno)); + printf(" buddy_ind_ref: seq %llu blkno %llu\n", + le64_to_cpu(super->buddy_ind_ref.seq), + le64_to_cpu(super->buddy_ind_ref.blkno)); printf(" btree_root: height %u seq %llu blkno %llu\n", super->btree_root.height, le64_to_cpu(super->btree_root.ref.seq), @@ -262,7 +307,13 @@ static int print_super_blocks(int fd) super = &recent; - ret = print_buddy_blocks(fd, super); + err = print_bitmap_block(fd, super); + if (err && !ret) + ret = err; + + err = print_buddy_blocks(fd, super); + if (err && !ret) + ret = err; if (super->btree_root.height) { err = print_btree_block(fd, super->btree_root.ref.blkno, diff --git a/utils/src/sparse.h b/utils/src/sparse.h index 7842aca2..24956150 100644 --- a/utils/src/sparse.h +++ b/utils/src/sparse.h @@ -104,4 +104,9 @@ __gen_functions(cast, be) #error "machine is neither BIG_ENDIAN nor LITTLE_ENDIAN" #endif +static inline void le32_add_cpu(__le32 *val, u32 delta) +{ + *val = cpu_to_le32(le32_to_cpu(*val) + delta); +} + #endif From 0af40547b5dfce077024bce830d4665a550b4edb Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Fri, 29 Jul 2016 13:56:21 -0700 Subject: [PATCH 038/235] Update to smaller block size We're going to try using a smaller fixed block size to reduce complexity in the file data extent code. Signed-off-by: Zach Brown --- utils/src/format.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index 4c23d6e4..4e9eeaad 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -6,7 +6,7 @@ /* super block id */ #define SCOUTFS_SUPER_ID 0x2e736674756f6373ULL /* "scoutfs." */ -#define SCOUTFS_BLOCK_SHIFT 14 +#define SCOUTFS_BLOCK_SHIFT 12 #define SCOUTFS_BLOCK_SIZE (1 << SCOUTFS_BLOCK_SHIFT) #define SCOUTFS_BLOCK_MASK (SCOUTFS_BLOCK_SIZE - 1) @@ -102,7 +102,7 @@ struct scoutfs_key { #define SCOUTFS_DIRENT_KEY 3 #define SCOUTFS_DATA_KEY 4 -#define SCOUTFS_MAX_ITEM_LEN 2048 +#define SCOUTFS_MAX_ITEM_LEN 512 struct scoutfs_treap_root { __le16 off; From 25e3b03d94813f4fefb9d7e82376ab1e811b5eea Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Tue, 2 Aug 2016 11:47:13 -0700 Subject: [PATCH 039/235] Add support for simpler btree block Update mkfs and print to the new simpler btree block format. Signed-off-by: Zach Brown --- utils/src/format.h | 29 +++++++++----------- utils/src/mkfs.c | 17 +++++------- utils/src/print.c | 67 +++++++++++++++++----------------------------- 3 files changed, 43 insertions(+), 70 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index 4e9eeaad..2a3605c4 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -104,28 +104,26 @@ struct scoutfs_key { #define SCOUTFS_MAX_ITEM_LEN 512 -struct scoutfs_treap_root { - __le16 off; -} __packed; - -struct scoutfs_treap_node { - __le16 parent; - __le16 left; - __le16 right; - __le32 prio; -} __packed; - struct scoutfs_btree_root { u8 height; struct scoutfs_block_ref ref; } __packed; +/* + * @free_end: records the byte offset of the first byte after the free + * space in the block between the header and the first item. New items + * are allocated by subtracting the space they need. + * + * @free_reclaim: records the number of bytes of free space amongst the + * items after free_end. If a block is compacted then this much new + * free space would be reclaimed. + */ struct scoutfs_btree_block { struct scoutfs_block_header hdr; - struct scoutfs_treap_root treap; - __le16 total_free; - __le16 tail_free; - __le16 nr_items; + __le16 free_end; + __le16 free_reclaim; + __u8 nr_items; + __le16 item_offs[0]; } __packed; /* @@ -134,7 +132,6 @@ struct scoutfs_btree_block { */ struct scoutfs_btree_item { struct scoutfs_key key; - struct scoutfs_treap_node tnode; __le64 seq; __le16 val_len; char val[0]; diff --git a/utils/src/mkfs.c b/utils/src/mkfs.c index 83ad7ef3..202e77a4 100644 --- a/utils/src/mkfs.c +++ b/utils/src/mkfs.c @@ -190,15 +190,15 @@ static int write_new_fs(char *path, int fd) /* write a btree leaf root inode item */ memset(buf, 0, SCOUTFS_BLOCK_SIZE); bt = buf; - bt->nr_items = cpu_to_le16(1); + bt->nr_items = 1; + bt->free_end = cpu_to_le16(SCOUTFS_BLOCK_SIZE - sizeof(*item) - + sizeof(*inode)); + bt->free_reclaim = 0; + bt->item_offs[0] = bt->free_end; - item = (void *)(bt + 1); + item = (void *)bt + le16_to_cpu(bt->free_end); item->seq = cpu_to_le64(1); item->key = root_key; - item->tnode.parent = 0; - item->tnode.left = 0; - item->tnode.right = 0; - pseudo_random_bytes(&item->tnode.prio, sizeof(item->tnode.prio)); item->val_len = cpu_to_le16(sizeof(struct scoutfs_inode)); inode = (void *)(item + 1); @@ -211,11 +211,6 @@ static int write_new_fs(char *path, int fd) inode->mtime.sec = inode->atime.sec; inode->mtime.nsec = inode->atime.nsec; - bt->treap.off = cpu_to_le16((char *)&item->tnode - (char *)&bt->treap); - bt->total_free = cpu_to_le16(SCOUTFS_BLOCK_SIZE - - ((char *)(inode + 1) - (char *)bt)); - bt->tail_free = bt->total_free; - ret = write_block(fd, blkno, super, &bt->hdr); if (ret) goto out; diff --git a/utils/src/print.c b/utils/src/print.c index 8b99bb27..1530d3e1 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -58,11 +58,11 @@ static void print_block_header(struct scoutfs_block_header *hdr) static void print_inode(struct scoutfs_inode *inode) { - printf(" inode: size: %llu blocks: %llu nlink: %u\n" - " uid: %u gid: %u mode: 0%o rdev: 0x%x\n" - " salt: 0x%x\n" - " atime: %llu.%08u ctime: %llu.%08u\n" - " mtime: %llu.%08u\n", + printf(" inode: size: %llu blocks: %llu nlink: %u\n" + " uid: %u gid: %u mode: 0%o rdev: 0x%x\n" + " salt: 0x%x\n" + " atime: %llu.%08u ctime: %llu.%08u\n" + " mtime: %llu.%08u\n", le64_to_cpu(inode->size), le64_to_cpu(inode->blocks), le32_to_cpu(inode->nlink), le32_to_cpu(inode->uid), le32_to_cpu(inode->gid), le32_to_cpu(inode->mode), @@ -95,17 +95,8 @@ static void print_block_ref(struct scoutfs_block_ref *ref) le64_to_cpu(ref->blkno), le64_to_cpu(ref->seq)); } -static void print_btree_item(unsigned int off, struct scoutfs_btree_item *item, - u8 level) +static void print_btree_val(struct scoutfs_btree_item *item, u8 level) { - printf(" item: key "SKF" seq %llu val_len %u off %u tnode: parent %u left %u right %u " - "prio %x\n", - SKA(&item->key), le64_to_cpu(item->seq), - le16_to_cpu(item->val_len), off, - le16_to_cpu(item->tnode.parent), - le16_to_cpu(item->tnode.left), - le16_to_cpu(item->tnode.right), - le32_to_cpu(item->tnode.prio)); if (level) { print_block_ref((void *)item->val); @@ -127,7 +118,6 @@ static int print_btree_block(int fd, __le64 blkno, u8 level) struct scoutfs_btree_item *item; struct scoutfs_btree_block *bt; struct scoutfs_block_ref *ref; - unsigned int off; int ret = 0; int err; int i; @@ -138,38 +128,29 @@ static int print_btree_block(int fd, __le64 blkno, u8 level) printf("btree blkno %llu\n", le64_to_cpu(blkno)); print_block_header(&bt->hdr); - printf(" treap.off %u total_free %u tail_free %u nr_items %u\n", - le16_to_cpu(bt->treap.off), - le16_to_cpu(bt->total_free), - le16_to_cpu(bt->tail_free), - le16_to_cpu(bt->nr_items)); + printf(" free_end %u free_reclaim %u nr_items %u\n", + le16_to_cpu(bt->free_end), + le16_to_cpu(bt->free_reclaim), + bt->nr_items); - /* XXX just print in offset order */ - item = (void *)(bt + 1); - for (i = 0; i < le16_to_cpu(bt->nr_items); i++) { - if (item->tnode.parent == cpu_to_le16(1)) { - i--; - } else { - off = (char *)&item->tnode - (char *)&bt->treap; - print_btree_item(off, item, level); - } + for (i = 0; i < bt->nr_items; i++) { + item = (void *)bt + le16_to_cpu(bt->item_offs[i]); - item = (void *)&item->val[le16_to_cpu(item->val_len)]; + printf(" [%u] off %u: key "SKF" seq %llu val_len %u\n", + i, le16_to_cpu(bt->item_offs[i]), + SKA(&item->key), le64_to_cpu(item->seq), + le16_to_cpu(item->val_len)); + + print_btree_val(item, level); } - item = (void *)(bt + 1); - for (i = 0; level && i < le16_to_cpu(bt->nr_items); i++) { - if (item->tnode.parent == cpu_to_le16(1)) { - i--; - } else { - ref = (void *)item->val; + for (i = 0; level && i < bt->nr_items; i++) { + item = (void *)bt + le16_to_cpu(bt->item_offs[i]); - err = print_btree_block(fd, ref->blkno, level - 1); - if (err && !ret) - ret = err; - } - - item = (void *)&item->val[le16_to_cpu(item->val_len)]; + ref = (void *)item->val; + err = print_btree_block(fd, ref->blkno, level - 1); + if (err && !ret) + ret = err; } free(bt); From be4a137479ce9a8e1136e7fa41cece20ff49fc2e Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Fri, 5 Aug 2016 14:53:42 -0700 Subject: [PATCH 040/235] Add support for printing block map items Signed-off-by: Zach Brown --- utils/src/format.h | 20 +++++++++++++++++++- utils/src/print.c | 14 ++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/utils/src/format.h b/utils/src/format.h index 2a3605c4..fbd09ddd 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -100,7 +100,7 @@ struct scoutfs_key { #define SCOUTFS_INODE_KEY 1 #define SCOUTFS_XATTR_KEY 2 #define SCOUTFS_DIRENT_KEY 3 -#define SCOUTFS_DATA_KEY 4 +#define SCOUTFS_BMAP_KEY 4 #define SCOUTFS_MAX_ITEM_LEN 512 @@ -244,4 +244,22 @@ struct scoutfs_xattr { __u8 name[0]; } __packed; +/* + * We use simple block map items to map a aligned fixed group of logical + * block offsets to physical blocks. We make them a decent size to + * reduce the item storage overhead per block referenced, but we don't + * want them so large that they start to take up an extraordinary amount + * of space for small files. 8 block items ranges from around 3% to .3% + * overhead for files that use only one or all of the blocks in the + * mapping item. + */ +#define SCOUTFS_BLOCK_MAP_SHIFT 3 +#define SCOUTFS_BLOCK_MAP_COUNT (1 << SCOUTFS_BLOCK_MAP_SHIFT) +#define SCOUTFS_BLOCK_MAP_MASK (SCOUTFS_BLOCK_MAP_COUNT - 1) + +struct scoutfs_block_map { + __le32 crc[SCOUTFS_BLOCK_MAP_COUNT]; + __le64 blkno[SCOUTFS_BLOCK_MAP_COUNT]; +}; + #endif diff --git a/utils/src/print.c b/utils/src/print.c index 1530d3e1..fca5837e 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -89,6 +89,17 @@ static void print_dirent(struct scoutfs_dirent *dent, unsigned int val_len) le64_to_cpu(dent->ino), dent->type, i, name); } +static void print_block_map(struct scoutfs_block_map *map) +{ + int i; + + printf(" bmap:"); + for (i = 0; i < SCOUTFS_BLOCK_MAP_COUNT; i++) + printf(" [%u] %llu", + i, le64_to_cpu(map->blkno[i])); + printf("\n"); +} + static void print_block_ref(struct scoutfs_block_ref *ref) { printf(" ref: blkno %llu seq %llu\n", @@ -110,6 +121,9 @@ static void print_btree_val(struct scoutfs_btree_item *item, u8 level) case SCOUTFS_DIRENT_KEY: print_dirent((void *)item->val, le16_to_cpu(item->val_len)); break; + case SCOUTFS_BMAP_KEY: + print_block_map((void *)item->val); + break; } } From 43619a245d1f1473c6cf00746d90e4a65163fef7 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Thu, 11 Aug 2016 14:34:10 -0700 Subject: [PATCH 041/235] Add inode-paths via link backrefs Add the inode-paths command which uses the ioctl to display all the paths that lead to the given inode. We add support for printing the new link backref items and inode and dirent fields. Signed-off-by: Zach Brown --- utils/src/format.h | 21 ++++++++-- utils/src/ino_paths.c | 92 +++++++++++++++++++++++++++++++++++++++++++ utils/src/ioctl.h | 13 ++++++ utils/src/print.c | 19 +++++++-- 4 files changed, 138 insertions(+), 7 deletions(-) create mode 100644 utils/src/ino_paths.c diff --git a/utils/src/format.h b/utils/src/format.h index fbd09ddd..a2137ca9 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -97,10 +97,11 @@ struct scoutfs_key { * isn't necessary. We could have an arbitrary sort order. So we don't * have to stress about cleverly allocating the types. */ -#define SCOUTFS_INODE_KEY 1 -#define SCOUTFS_XATTR_KEY 2 -#define SCOUTFS_DIRENT_KEY 3 -#define SCOUTFS_BMAP_KEY 4 +#define SCOUTFS_INODE_KEY 1 +#define SCOUTFS_XATTR_KEY 2 +#define SCOUTFS_DIRENT_KEY 3 +#define SCOUTFS_LINK_BACKREF_KEY 4 +#define SCOUTFS_BMAP_KEY 5 #define SCOUTFS_MAX_ITEM_LEN 512 @@ -173,6 +174,7 @@ struct scoutfs_timespec { struct scoutfs_inode { __le64 size; __le64 blocks; + __le64 link_counter; __le32 nlink; __le32 uid; __le32 gid; @@ -192,6 +194,7 @@ struct scoutfs_inode { */ struct scoutfs_dirent { __le64 ino; + __le64 counter; __u8 type; __u8 name[0]; } __packed; @@ -262,4 +265,14 @@ struct scoutfs_block_map { __le64 blkno[SCOUTFS_BLOCK_MAP_COUNT]; }; +/* + * link backrefs give us a way to find all the hard links that refer + * to a target inode. They're stored at an offset determined by an + * advancing counter in their inode. + */ +struct scoutfs_link_backref { + __le64 ino; + __le64 offset; +} __packed; + #endif diff --git a/utils/src/ino_paths.c b/utils/src/ino_paths.c new file mode 100644 index 00000000..17a087e5 --- /dev/null +++ b/utils/src/ino_paths.c @@ -0,0 +1,92 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sparse.h" +#include "util.h" +#include "ioctl.h" +#include "cmd.h" + +static int inode_paths_cmd(int argc, char **argv) +{ + struct scoutfs_ioctl_inode_paths args; + char *endptr; + void *ptr = NULL; + char *path; + u64 ino; + int len; + int ret; + int fd; + + if (argc != 2) { + fprintf(stderr, "must specify ino and path\n"); + return -EINVAL; + } + + ino = strtoull(argv[0], &endptr, 0); + if (*endptr != '\0' || + ((ino == LLONG_MIN || ino == LLONG_MAX) && errno == ERANGE)) { + fprintf(stderr, "error parsing inode number '%s'\n", + argv[0]); + return -EINVAL; + } + + fd = open(argv[1], O_RDONLY); + if (fd < 0) { + ret = -errno; + fprintf(stderr, "failed to open '%s': %s (%d)\n", + argv[1], strerror(errno), errno); + return ret; + } + + len = 16 * PATH_MAX; + do { + free(ptr); + ptr = malloc(len); + if (!ptr) { + fprintf(stderr, "couldn't allocate %d byte buffer\n", + len); + ret = -EINVAL; + goto out; + } + + args.ino = ino; + args.buf_ptr = (intptr_t)ptr; + args.buf_len = len; + + ret = ioctl(fd, SCOUTFS_IOC_INODE_PATHS, &args); + if (ret < 0 && errno != EOVERFLOW) { + ret = -errno; + fprintf(stderr, "inodes_since ioctl failed: %s (%d)\n", + strerror(errno), errno); + goto out; + } + + len *= 2; + + } while (ret < 0 && errno == EOVERFLOW); + + path = ptr; + while (*path) { + printf("%s\n", path); + path += strlen(path) + 1; + } + +out: + free(ptr); + close(fd); + return ret; +}; + +static void __attribute__((constructor)) since_ctor(void) +{ + cmd_register("inode-paths", " ", + "print paths that refer to inode #", inode_paths_cmd); +} diff --git a/utils/src/ioctl.h b/utils/src/ioctl.h index 009ef7d4..278d255f 100644 --- a/utils/src/ioctl.h +++ b/utils/src/ioctl.h @@ -24,4 +24,17 @@ struct scoutfs_ioctl_inodes_since { #define SCOUTFS_IOC_INODES_SINCE _IOW(SCOUTFS_IOCTL_MAGIC, 1, \ struct scoutfs_ioctl_inodes_since) +struct scoutfs_ioctl_inode_paths { + __u64 ino; + __u64 buf_ptr; + __u32 buf_len; +} __packed; + +/* + * Fills the callers buffer with all the paths from the root to the + * target inode. + */ +#define SCOUTFS_IOC_INODE_PATHS _IOW(SCOUTFS_IOCTL_MAGIC, 2, \ + struct scoutfs_ioctl_inode_paths) + #endif diff --git a/utils/src/print.c b/utils/src/print.c index fca5837e..b6399ca2 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -58,12 +58,13 @@ static void print_block_header(struct scoutfs_block_header *hdr) static void print_inode(struct scoutfs_inode *inode) { - printf(" inode: size: %llu blocks: %llu nlink: %u\n" + printf(" inode: size: %llu blocks: %llu lctr: %llu nlink: %u\n" " uid: %u gid: %u mode: 0%o rdev: 0x%x\n" " salt: 0x%x\n" " atime: %llu.%08u ctime: %llu.%08u\n" " mtime: %llu.%08u\n", le64_to_cpu(inode->size), le64_to_cpu(inode->blocks), + le64_to_cpu(inode->link_counter), le32_to_cpu(inode->nlink), le32_to_cpu(inode->uid), le32_to_cpu(inode->gid), le32_to_cpu(inode->mode), le32_to_cpu(inode->rdev), le32_to_cpu(inode->salt), @@ -85,8 +86,16 @@ static void print_dirent(struct scoutfs_dirent *dent, unsigned int val_len) name[i] = isprint(dent->name[i]) ? dent->name[i] : '.'; name[i] = '\0'; - printf(" dirent: ino: %llu type: %u name: \"%.*s\"\n", - le64_to_cpu(dent->ino), dent->type, i, name); + printf(" dirent: ino: %llu ctr: %llu type: %u name: \"%.*s\"\n", + le64_to_cpu(dent->ino), le64_to_cpu(dent->counter), + dent->type, i, name); +} + +static void print_link_backref(struct scoutfs_link_backref *lref, + unsigned int val_len) +{ + printf(" lref: ino: %llu offset: %llu\n", + le64_to_cpu(lref->ino), le64_to_cpu(lref->offset)); } static void print_block_map(struct scoutfs_block_map *map) @@ -121,6 +130,10 @@ static void print_btree_val(struct scoutfs_btree_item *item, u8 level) case SCOUTFS_DIRENT_KEY: print_dirent((void *)item->val, le16_to_cpu(item->val_len)); break; + case SCOUTFS_LINK_BACKREF_KEY: + print_link_backref((void *)item->val, + le16_to_cpu(item->val_len)); + break; case SCOUTFS_BMAP_KEY: print_block_map((void *)item->val); break; From c17a7036ed043097f585c35d96832cec867f17c0 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Mon, 22 Aug 2016 09:33:42 -0700 Subject: [PATCH 042/235] Add find xattr commands Add commands that use the find-xattr ioctls to show the inode numbers of inodes which probably contain xattrs matching the specified name or value. Signed-off-by: Zach Brown --- utils/src/find_xattr.c | 134 +++++++++++++++++++++++++++++++++++++++++ utils/src/format.h | 22 +++++-- utils/src/ioctl.h | 15 +++++ utils/src/print.c | 22 +++++++ 4 files changed, 187 insertions(+), 6 deletions(-) create mode 100644 utils/src/find_xattr.c diff --git a/utils/src/find_xattr.c b/utils/src/find_xattr.c new file mode 100644 index 00000000..9d489d07 --- /dev/null +++ b/utils/src/find_xattr.c @@ -0,0 +1,134 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sparse.h" +#include "util.h" +#include "ioctl.h" +#include "format.h" +#include "cmd.h" + +static int find_xattrs(bool find_name, int argc, char **argv) +{ + struct scoutfs_ioctl_find_xattr find; + char *endptr; + u64 first; + u64 last; + u64 *ino; + int ret; + int fd; + int ioc; + int count; + int i; + + if (find_name) + ioc = SCOUTFS_IOC_FIND_XATTR_NAME; + else + ioc = SCOUTFS_IOC_FIND_XATTR_VAL; + + if (argc != 4) { + fprintf(stderr, "must specify ino range, xattr str, and path\n"); + return -EINVAL; + } + + first = strtoull(argv[0], &endptr, 0); + if (*endptr != '\0' || + ((first == LLONG_MIN || first == LLONG_MAX) && errno == ERANGE)) { + fprintf(stderr, "error parsing inode number '%s'\n", + argv[0]); + return -EINVAL; + } + + last = strtoull(argv[1], &endptr, 0); + if (*endptr != '\0' || + ((last == LLONG_MIN || last == LLONG_MAX) && errno == ERANGE)) { + fprintf(stderr, "error parsing inode number '%s'\n", + argv[1]); + return -EINVAL; + } + + fd = open(argv[3], O_RDONLY); + if (fd < 0) { + ret = -errno; + fprintf(stderr, "failed to open '%s': %s (%d)\n", + argv[3], strerror(errno), errno); + return ret; + } + + count = 256; + ino = calloc(count, sizeof(*ino)); + if (!ino) { + fprintf(stderr, "couldn't allocate buffer for results\n"); + ret = -ENOMEM; + goto out; + } + + find.first_ino = first; + find.last_ino = last; + find.str_ptr = (unsigned long)argv[2]; + find.str_len = strlen(argv[2]); + find.ino_ptr = (unsigned long)ino; + find.ino_count = count; + + if (find.str_len > SCOUTFS_MAX_XATTR_LEN) { + fprintf(stderr, "xattr string len %u > %d\n", + find.str_len, SCOUTFS_MAX_XATTR_LEN); + ret = -EINVAL; + goto out; + } + + do { + ret = ioctl(fd, ioc, &find); + if (ret < 0) { + ret = -errno; + fprintf(stderr, "inodes_find_xattr ioctl failed: %s (%d)\n", + strerror(errno), errno); + goto out; + } + + for (i = 0; i < ret; i++) { + printf("%llu\n", ino[i]); + find.first_ino = ino[i] + 1; + + if (find.first_ino == 0) { + ret = 0; + break; + } + } + } while (ret > 0); + +out: + free(ino); + close(fd); + + return ret; +}; + +static int find_xattr_name(int argc, char **argv) +{ + return find_xattrs(true, argc, argv); +} + +static int find_xattr_val(int argc, char **argv) +{ + return find_xattrs(false, argc, argv); +} + +static void __attribute__((constructor)) find_xattr_ctor(void) +{ + cmd_register("find-xattr-name", " ", + "print inodes that might contain xattr name", + find_xattr_name); + cmd_register("find-xattr-value", " ", + "print inodes that might contain xattr value", + find_xattr_val); +} diff --git a/utils/src/format.h b/utils/src/format.h index a2137ca9..fbe60040 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -99,9 +99,11 @@ struct scoutfs_key { */ #define SCOUTFS_INODE_KEY 1 #define SCOUTFS_XATTR_KEY 2 -#define SCOUTFS_DIRENT_KEY 3 -#define SCOUTFS_LINK_BACKREF_KEY 4 -#define SCOUTFS_BMAP_KEY 5 +#define SCOUTFS_XATTR_NAME_HASH_KEY 3 +#define SCOUTFS_XATTR_VAL_HASH_KEY 4 +#define SCOUTFS_DIRENT_KEY 5 +#define SCOUTFS_LINK_BACKREF_KEY 6 +#define SCOUTFS_BMAP_KEY 7 #define SCOUTFS_MAX_ITEM_LEN 512 @@ -215,6 +217,15 @@ struct scoutfs_dirent { #define SCOUTFS_NAME_LEN 255 +/* + * This is arbitrarily limiting the max size of the single buffer + * that's needed in the inode_paths ioctl to return all the paths + * that link to an inode. The structures could easily support much + * more than this but then we'd need to grow a more thorough interface + * for iterating over referring paths. That sounds horrible. + */ +#define SCOUTFS_LINK_MAX 255 + /* * We only use 31 bits for readdir positions so that we don't confuse * old signed 32bit f_pos applications or those on the other side of @@ -237,9 +248,8 @@ enum { SCOUTFS_DT_WHT, }; -#define SCOUTFS_MAX_XATTR_NAME_LEN 255 -#define SCOUTFS_MAX_XATTR_VALUE_LEN 255 -#define SCOUTFS_XATTR_HASH_MASK 7ULL +#define SCOUTFS_MAX_XATTR_LEN 255 +#define SCOUTFS_XATTR_NAME_HASH_MASK 7ULL struct scoutfs_xattr { __u8 name_len; diff --git a/utils/src/ioctl.h b/utils/src/ioctl.h index 278d255f..a4991b69 100644 --- a/utils/src/ioctl.h +++ b/utils/src/ioctl.h @@ -37,4 +37,19 @@ struct scoutfs_ioctl_inode_paths { #define SCOUTFS_IOC_INODE_PATHS _IOW(SCOUTFS_IOCTL_MAGIC, 2, \ struct scoutfs_ioctl_inode_paths) +/* XXX might as well include a seq? 0 for current behaviour? */ +struct scoutfs_ioctl_find_xattr { + __u64 first_ino; + __u64 last_ino; + __u64 str_ptr; + __u32 str_len; + __u64 ino_ptr; + __u32 ino_count; +} __packed; + +#define SCOUTFS_IOC_FIND_XATTR_NAME _IOW(SCOUTFS_IOCTL_MAGIC, 3, \ + struct scoutfs_ioctl_find_xattr) +#define SCOUTFS_IOC_FIND_XATTR_VAL _IOW(SCOUTFS_IOCTL_MAGIC, 4, \ + struct scoutfs_ioctl_find_xattr) + #endif diff --git a/utils/src/print.c b/utils/src/print.c index b6399ca2..fb5a48a2 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -76,6 +76,22 @@ static void print_inode(struct scoutfs_inode *inode) le32_to_cpu(inode->mtime.nsec)); } +static void print_xattr(struct scoutfs_xattr *xat) +{ + /* XXX check lengths */ + + printf(" xattr: name %.*s val_len %u\n", + xat->name_len, xat->name, xat->value_len); +} + +static void print_xattr_val_hash(__le64 *refcount) +{ + /* XXX check lengths */ + + printf(" xattr_val_hash: refcount %llu\n", + le64_to_cpu(*refcount)); +} + static void print_dirent(struct scoutfs_dirent *dent, unsigned int val_len) { unsigned int name_len = val_len - sizeof(*dent); @@ -127,6 +143,12 @@ static void print_btree_val(struct scoutfs_btree_item *item, u8 level) case SCOUTFS_INODE_KEY: print_inode((void *)item->val); break; + case SCOUTFS_XATTR_KEY: + print_xattr((void *)item->val); + break; + case SCOUTFS_XATTR_VAL_HASH_KEY: + print_xattr_val_hash((void *)item->val); + break; case SCOUTFS_DIRENT_KEY: print_dirent((void *)item->val, le16_to_cpu(item->val_len)); break; From 2f91a9a735a8e4b9e1fbcc058b294f749c016053 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Tue, 23 Aug 2016 12:31:03 -0700 Subject: [PATCH 043/235] Make command listing less noisy It's still not great, but at least it's a little clearer. Signed-off-by: Zach Brown --- utils/src/cmd.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/utils/src/cmd.c b/utils/src/cmd.c index 2dc0cbd7..e723f859 100644 --- a/utils/src/cmd.c +++ b/utils/src/cmd.c @@ -45,13 +45,18 @@ static struct command *find_command(char *name) static void usage(void) { struct command *com; + int largest = 0; fprintf(stderr, "usage: scoutfs []\n" "Commands:\n"); + cmd_for_each(com) + largest = max(strlen(com->name), largest); + cmd_for_each(com) { - fprintf(stderr, " %8s %12s - %s\n", - com->name, com->opts, com->summary); + fprintf(stderr, " %*s %s\n %*s %s\n", + largest, com->name, com->opts, + largest, "", com->summary); } } From a89f6c10b111436726e4e60058bb5456e40933b5 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Tue, 23 Aug 2016 16:23:46 -0700 Subject: [PATCH 044/235] Add buddy indirect order totals The total counts of all the set order bits in all the child buddy blocks is needed for statfs. Signed-off-by: Zach Brown --- utils/src/format.h | 3 ++- utils/src/mkfs.c | 15 +++++++++------ utils/src/print.c | 4 ++++ 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index fbe60040..82fa436b 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -72,6 +72,7 @@ struct scoutfs_buddy_block { struct scoutfs_buddy_indirect { struct scoutfs_block_header hdr; + __le64 order_totals[SCOUTFS_BUDDY_ORDERS]; struct scoutfs_buddy_slot { __u8 free_orders; struct scoutfs_block_ref ref; @@ -79,7 +80,7 @@ struct scoutfs_buddy_indirect { } __packed; #define SCOUTFS_BUDDY_SLOTS \ - ((SCOUTFS_BLOCK_SIZE - sizeof(struct scoutfs_buddy_block)) / \ + ((SCOUTFS_BLOCK_SIZE - sizeof(struct scoutfs_buddy_indirect)) / \ sizeof(struct scoutfs_buddy_slot)) /* diff --git a/utils/src/mkfs.c b/utils/src/mkfs.c index 202e77a4..d133baca 100644 --- a/utils/src/mkfs.c +++ b/utils/src/mkfs.c @@ -140,15 +140,15 @@ static int write_new_fs(char *path, int fd) u64 blkno; u64 total_blocks; u64 buddy_blocks; - u8 free_orders; void *buf; int ret; gettimeofday(&tv, NULL); buf = malloc(SCOUTFS_BLOCK_SIZE); + bud = malloc(SCOUTFS_BLOCK_SIZE); super = malloc(SCOUTFS_BLOCK_SIZE); - if (!buf || !super) { + if (!buf || !bud || !super) { ret = -errno; fprintf(stderr, "failed to allocate a block: %s (%d)\n", strerror(errno), errno); @@ -221,12 +221,10 @@ static int write_new_fs(char *path, int fd) super->btree_root.ref.seq = bt->hdr.seq; /* free all the blocks in the first buddy block after btree block */ - memset(buf, 0, SCOUTFS_BLOCK_SIZE); - bud = buf; + memset(bud, 0, SCOUTFS_BLOCK_SIZE); for (i = 1; i < min(total_blocks - first_blkno(super), SCOUTFS_BUDDY_ORDER0_BITS); i++) free_order_bit(bud, 0, i); - free_orders = calc_free_orders(bud); blkno = SCOUTFS_BUDDY_BM_BLKNO + SCOUTFS_BUDDY_BM_NR; ret = write_block(fd, blkno, super, &bud->hdr); @@ -236,11 +234,14 @@ static int write_new_fs(char *path, int fd) /* an indirect buddy block references the buddy bitmap block */ memset(buf, 0, SCOUTFS_BLOCK_SIZE); ind = buf; + for (i = 0; i < SCOUTFS_BUDDY_ORDERS; i++) + ind->order_totals[i] = cpu_to_le64(le32_to_cpu( + bud->order_counts[i])); for (i = 0; i < SCOUTFS_BUDDY_SLOTS; i++) { ind->slots[i].free_orders = 0; ind->slots[i].ref = (struct scoutfs_block_ref){0,}; } - ind->slots[0].free_orders = free_orders; + ind->slots[0].free_orders = calc_free_orders(bud); ind->slots[0].ref.seq = super->hdr.seq; ind->slots[0].ref.blkno = cpu_to_le64(blkno); @@ -298,6 +299,8 @@ static int write_new_fs(char *path, int fd) out: if (super) free(super); + if (bud) + free(bud); if (buf) free(buf); return ret; diff --git a/utils/src/print.c b/utils/src/print.c index fb5a48a2..ed9e15db 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -245,6 +245,10 @@ static int print_buddy_blocks(int fd, struct scoutfs_super_block *super) printf("buddy indirect blkno %llu\n", blkno); print_block_header(&ind->hdr); + printf(" total_counts:"); + for (i = 0; i < SCOUTFS_BUDDY_ORDERS; i++) + printf(" %llu", le64_to_cpu(ind->order_totals[i])); + printf("\n"); for (i = 0; i < SCOUTFS_BUDDY_SLOTS; i++) { slot = &ind->slots[i]; From 86ffdf24a233cd844c0784b26475bccdab66495a Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Mon, 29 Aug 2016 10:23:44 -0700 Subject: [PATCH 045/235] Add symlink support Print out the raw symlink items. Signed-off-by: Zach Brown --- utils/src/format.h | 6 +++++- utils/src/print.c | 9 +++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/utils/src/format.h b/utils/src/format.h index 82fa436b..d2e542d9 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -104,7 +104,8 @@ struct scoutfs_key { #define SCOUTFS_XATTR_VAL_HASH_KEY 4 #define SCOUTFS_DIRENT_KEY 5 #define SCOUTFS_LINK_BACKREF_KEY 6 -#define SCOUTFS_BMAP_KEY 7 +#define SCOUTFS_SYMLINK_KEY 7 +#define SCOUTFS_BMAP_KEY 8 #define SCOUTFS_MAX_ITEM_LEN 512 @@ -191,6 +192,9 @@ struct scoutfs_inode { #define SCOUTFS_ROOT_INO 1 +/* like the block size, a reasonable min PATH_MAX across platforms */ +#define SCOUTFS_SYMLINK_MAX_SIZE 4096 + /* * Dirents are stored in items with an offset of the hash of their name. * Colliding names are packed into the value. diff --git a/utils/src/print.c b/utils/src/print.c index ed9e15db..e1a888bc 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -114,6 +114,12 @@ static void print_link_backref(struct scoutfs_link_backref *lref, le64_to_cpu(lref->ino), le64_to_cpu(lref->offset)); } +/* for now show the raw component items not the whole path */ +static void print_symlink(char *str, unsigned int val_len) +{ + printf(" symlink: %.*s\n", val_len, str); +} + static void print_block_map(struct scoutfs_block_map *map) { int i; @@ -156,6 +162,9 @@ static void print_btree_val(struct scoutfs_btree_item *item, u8 level) print_link_backref((void *)item->val, le16_to_cpu(item->val_len)); break; + case SCOUTFS_SYMLINK_KEY: + print_symlink((void *)item->val, le16_to_cpu(item->val_len)); + break; case SCOUTFS_BMAP_KEY: print_block_map((void *)item->val); break; From 4ccb80a8ec4320f4a2a0f34a401da00ec5c61140 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Thu, 8 Sep 2016 16:40:39 -0700 Subject: [PATCH 046/235] Initialize all the buddy slot free order fields Initialize the free_order field in all the slots of the buddy index block so that the kernel will try to allocate from them and will initialize and populate the first block. Signed-off-by: Zach Brown --- utils/src/mkfs.c | 34 ++++++++++++++++++++++++++++++++++ utils/src/print.c | 3 ++- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/utils/src/mkfs.c b/utils/src/mkfs.c index d133baca..920361be 100644 --- a/utils/src/mkfs.c +++ b/utils/src/mkfs.c @@ -123,6 +123,31 @@ static u8 calc_free_orders(struct scoutfs_buddy_block *bud) return free; } +/* + * Figure out the free orders for the slot that starts with the given + * blkno. The bits in the buddy bitmap are relative to the starting + * blkno and are aligned so the bits in the count of blocks in the slot + * reflect the presence of free orders in that slot. + */ +static u8 slot_free_orders(u64 sl_blkno, u64 total_blocks) +{ + u64 count; + u64 mask; + u8 free; + + if (sl_blkno >= total_blocks) + return 0; + + count = min(total_blocks - sl_blkno, SCOUTFS_BUDDY_ORDER0_BITS); + + mask = (1 << SCOUTFS_BUDDY_ORDERS) - 1; + free = count & mask; + if (count > mask) + free |= (mask + 1) >> 1; + + return free; +} + static int write_new_fs(char *path, int fd) { struct scoutfs_super_block *super; @@ -140,6 +165,7 @@ static int write_new_fs(char *path, int fd) u64 blkno; u64 total_blocks; u64 buddy_blocks; + u64 sl_blkno; void *buf; int ret; @@ -245,6 +271,14 @@ static int write_new_fs(char *path, int fd) ind->slots[0].ref.seq = super->hdr.seq; ind->slots[0].ref.blkno = cpu_to_le64(blkno); + /* initialize unpopulated slot bits so the kernel will use them */ + sl_blkno = first_blkno(super) + SCOUTFS_BUDDY_ORDER0_BITS; + for (i = 1; i < SCOUTFS_BUDDY_SLOTS; i++) { + ind->slots[i].free_orders = slot_free_orders(sl_blkno, + total_blocks); + sl_blkno += SCOUTFS_BUDDY_ORDER0_BITS; + } + blkno++; ret = write_block(fd, blkno, super, &ind->hdr); if (ret) diff --git a/utils/src/print.c b/utils/src/print.c index e1a888bc..29c18dea 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -274,7 +274,8 @@ static int print_buddy_blocks(int fd, struct scoutfs_super_block *super) for (i = 0; i < SCOUTFS_BUDDY_SLOTS; i++) { slot = &ind->slots[i]; - if (!slot->free_orders && !slot->ref.seq && !slot->ref.blkno) + /* only print populated buddy blocks */ + if (slot->ref.blkno == 0) continue; err = print_buddy_block(fd, super, From 0dff7f55a651025809270af5eff47617509a8a64 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Tue, 27 Sep 2016 09:47:50 -0700 Subject: [PATCH 047/235] Use openssl for pseudo random bytes The pseudo random byte wrapper function used the intel instructions so that it could deal with high call rates, like initializing random node priorities for a large treap. But this is obviously not remotely portable and has the annoying habit of tripping up versions of valgrind that haven't yet learned about these instructions. We don't actually have high bandwidth callers so let's back off and just let openssl take care of this for us. Signed-off-by: Zach Brown --- utils/Makefile | 4 ++-- utils/src/rand.c | 24 +++--------------------- 2 files changed, 5 insertions(+), 23 deletions(-) diff --git a/utils/Makefile b/utils/Makefile index 71db96e8..95cd9434 100644 --- a/utils/Makefile +++ b/utils/Makefile @@ -1,4 +1,4 @@ -CFLAGS := -Wall -O2 -Werror -D_FILE_OFFSET_BITS=64 -g -mrdrnd -msse4.2 \ +CFLAGS := -Wall -O2 -Werror -D_FILE_OFFSET_BITS=64 -g -msse4.2 \ -fno-strict-aliasing BIN := src/scoutfs @@ -21,7 +21,7 @@ endif $(BIN): $(OBJ) $(QU) [BIN $@] - $(VE)gcc -o $@ $^ -luuid -lm + $(VE)gcc -o $@ $^ -luuid -lm -lcrypto %.o %.d: %.c Makefile sparse.sh $(QU) [CC $<] diff --git a/utils/src/rand.c b/utils/src/rand.c index 6305d660..e5444810 100644 --- a/utils/src/rand.c +++ b/utils/src/rand.c @@ -4,27 +4,9 @@ #include "sparse.h" #include "util.h" +#include + void pseudo_random_bytes(void *data, unsigned int len) { - unsigned long long tmp; - unsigned long long *ll = data; - unsigned int sz = sizeof(*ll); - unsigned int unaligned; - - /* see if the initial buffer is unaligned */ - unaligned = min((unsigned long)data & (sz - 1), len); - if (unaligned) { - __builtin_ia32_rdrand64_step(&tmp); - memcpy(data, &tmp, unaligned); - data += unaligned; - len -= unaligned; - } - - for (ll = data; len >= sz; ll++, len -= sz) - __builtin_ia32_rdrand64_step(ll); - - if (len) { - __builtin_ia32_rdrand64_step(&tmp); - memcpy(ll, &tmp, len); - } + RAND_pseudo_bytes(data, len); } From e6222223c20b9656b40554202d8798882323d787 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Fri, 4 Nov 2016 14:08:11 -0700 Subject: [PATCH 048/235] Update format.h for kernel code helpers Update format.h for some format defines that have so far only been used by the kernel code. Signed-off-by: Zach Brown --- utils/src/format.h | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/utils/src/format.h b/utils/src/format.h index d2e542d9..fd0cbd62 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -22,6 +22,8 @@ #define SCOUTFS_BUDDY_BM_BLKNO (SCOUTFS_SUPER_BLKNO + SCOUTFS_SUPER_NR) #define SCOUTFS_BUDDY_BM_NR 2 +#define SCOUTFS_MAX_TRANS_BLOCKS (128 * 1024 * 1024 / SCOUTFS_BLOCK_SIZE) + /* * This header is found at the start of every block so that we can * verify that it's what we were looking for. The crc and padding @@ -146,6 +148,29 @@ struct scoutfs_btree_item { #define SCOUTFS_BTREE_FREE_LIMIT \ ((SCOUTFS_BLOCK_SIZE - sizeof(struct scoutfs_btree_block)) / 2) +/* XXX does this exist upstream somewhere? */ +#define member_sizeof(TYPE, MEMBER) (sizeof(((TYPE *)0)->MEMBER)) + +#define SCOUTFS_BTREE_MAX_ITEMS \ + ((SCOUTFS_BLOCK_SIZE - sizeof(struct scoutfs_btree_block)) / \ + (member_sizeof(struct scoutfs_btree_block, item_offs[0]) + \ + sizeof(struct scoutfs_btree_item))) + +/* + * We can calculate the max tree depth by calculating how many leaf + * blocks the tree could reference. The block device can only reference + * 2^64 bytes. The tallest parent tree has half full parent blocks. + * + * So we have the relation: + * + * ceil(max_items / 2) ^ (max_depth - 1) >= 2^64 / block_size + * + * and solve for depth: + * + * max_depth = log(ceil(max_items / 2), 2^64 / block_size) + 1 + */ +#define SCOUTFS_BTREE_MAX_DEPTH 10 + #define SCOUTFS_UUID_BYTES 16 struct scoutfs_super_block { From b436772376b530ef6c5a65c1d67ed21a99a01b39 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Thu, 27 Oct 2016 17:47:10 -0700 Subject: [PATCH 049/235] Add orphan key Printing the raw item is enough, it doesn't have a value to decode. Signed-off-by: Zach Brown --- utils/src/format.h | 1 + 1 file changed, 1 insertion(+) diff --git a/utils/src/format.h b/utils/src/format.h index fd0cbd62..7a02c26a 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -108,6 +108,7 @@ struct scoutfs_key { #define SCOUTFS_LINK_BACKREF_KEY 6 #define SCOUTFS_SYMLINK_KEY 7 #define SCOUTFS_BMAP_KEY 8 +#define SCOUTFS_ORPHAN_KEY 9 #define SCOUTFS_MAX_ITEM_LEN 512 From a901db2ff749018c01452f624a83170c7b186ef3 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Tue, 18 Oct 2016 11:53:29 -0700 Subject: [PATCH 050/235] Print seqs in bmap items The bmap items now have the sequence number that wrote each mapped block. Signed-off-by: Zach Brown --- utils/src/format.h | 2 +- utils/src/print.c | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index 7a02c26a..58fea85f 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -302,8 +302,8 @@ struct scoutfs_xattr { #define SCOUTFS_BLOCK_MAP_MASK (SCOUTFS_BLOCK_MAP_COUNT - 1) struct scoutfs_block_map { - __le32 crc[SCOUTFS_BLOCK_MAP_COUNT]; __le64 blkno[SCOUTFS_BLOCK_MAP_COUNT]; + __le64 seq[SCOUTFS_BLOCK_MAP_COUNT]; }; /* diff --git a/utils/src/print.c b/utils/src/print.c index 29c18dea..9877be90 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -126,8 +126,9 @@ static void print_block_map(struct scoutfs_block_map *map) printf(" bmap:"); for (i = 0; i < SCOUTFS_BLOCK_MAP_COUNT; i++) - printf(" [%u] %llu", - i, le64_to_cpu(map->blkno[i])); + printf(" [%u] %llu:%llu", + i, le64_to_cpu(map->blkno[i]), + le64_to_cpu(map->seq[i])); printf("\n"); } From 871db60fb2fe4129d97db479846eb3fe47c85b60 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Fri, 4 Nov 2016 14:03:58 -0700 Subject: [PATCH 051/235] Add U16_MAX Add a simple U16_MAX define for upcoming buddy changes. Signed-off-by: Zach Brown --- utils/src/sparse.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/utils/src/sparse.h b/utils/src/sparse.h index 24956150..58e54656 100644 --- a/utils/src/sparse.h +++ b/utils/src/sparse.h @@ -34,6 +34,8 @@ typedef u32 __u32; typedef s32 __s32; typedef u64 __u64; +#define U16_MAX ((u16)~0) + typedef u16 __bitwise __le16; typedef u16 __bitwise __be16; typedef u32 __bitwise __le32; From 40b9f19ec4d1cdbdd4b40bc43a5747ff00a261f8 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Fri, 4 Nov 2016 13:52:36 -0700 Subject: [PATCH 052/235] Add bitops.c for find_next_bit_le() The upcoming buddy changes are going to need a find_next_bit_le(). Signed-off-by: Zach Brown --- utils/src/bitops.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++ utils/src/bitops.h | 2 ++ 2 files changed, 62 insertions(+) create mode 100644 utils/src/bitops.c diff --git a/utils/src/bitops.c b/utils/src/bitops.c new file mode 100644 index 00000000..148051f4 --- /dev/null +++ b/utils/src/bitops.c @@ -0,0 +1,60 @@ +#include +#include +#include + +#include "sparse.h" +#include "util.h" +#include "bitops.h" + +#if (__SIZEOF_LONG__ == 8) +typedef __le64 lelong; +#define lelong_to_cpu le64_to_cpu + +#elif (__SIZEOF_LONG__ == 4) +typedef __le32 lelong; +#define lelong_to_cpu le32_to_cpu + +#else +#error "no sizeof long define?" +#endif + +/* + * I'd have used ffsl(), but defining _GNU_SOURCE caused build errors + * in glibc. The gcc builtin has the added bonus of returning 0 for the + * least significant bit instead of 1. + */ +#define ctzl __builtin_ctzl + +int find_next_bit_le(void *addr, long size, int start) +{ + lelong * __packed longs = addr; + unsigned long off = 0; + unsigned long masked; + + /* skip past whole longs before start */ + if (start >= BITS_PER_LONG) { + longs += start / BITS_PER_LONG; + off = start & ~(BITS_PER_LONG - 1); + start -= off; + } + + /* mask off low bits if start isn't aligned */ + if (start) { + masked = lelong_to_cpu(*longs) & ~((1 << (start)) - 1); + if (masked) + return min(ctzl(masked), size); + + off += BITS_PER_LONG; + longs++; + } + + /* then search remaining longs */ + while (off < size) { + if (*longs) + return min(off + ctzl(lelong_to_cpu(*longs)), size); + longs++; + off += BITS_PER_LONG; + } + + return size; +} diff --git a/utils/src/bitops.h b/utils/src/bitops.h index abfa3755..7f2add29 100644 --- a/utils/src/bitops.h +++ b/utils/src/bitops.h @@ -75,4 +75,6 @@ static inline int test_and_clear_bit_le(int nr, void *addr) return ret; } +int find_next_bit_le(void *addr, long size, int start); + #endif From cd0d045c9372a6845b85d14a1ed11acf1b3a84bc Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Fri, 28 Oct 2016 11:00:21 -0700 Subject: [PATCH 053/235] Add support for full radix buddy blocks Update mkfs and print for the full radix buddy allocators. mkfs has to calculate the number of blocks and the height of the tree and has to initialize the paths down the left and right side of the tree. Print needs to dump the new radix blockx and super block fields. Signed-off-by: Zach Brown --- utils/src/buddy.c | 61 +++++++++ utils/src/buddy.h | 16 +++ utils/src/format.h | 60 +++++---- utils/src/mkfs.c | 315 ++++++++++++++++++++++++++++++--------------- utils/src/print.c | 129 ++++++++----------- 5 files changed, 372 insertions(+), 209 deletions(-) create mode 100644 utils/src/buddy.c create mode 100644 utils/src/buddy.h diff --git a/utils/src/buddy.c b/utils/src/buddy.c new file mode 100644 index 00000000..1a0582d5 --- /dev/null +++ b/utils/src/buddy.c @@ -0,0 +1,61 @@ +#include +#include +#include + +#include "sparse.h" +#include "util.h" +#include "buddy.h" + +/* + * Figure out how many blocks the radix will need by starting with leaf + * blocks and dividing by the slot fanout until we have one block. cow + * updates require two copies of every block. + */ +static u64 calc_blocks(struct buddy_info *binf, u64 bits) +{ + u64 blocks = DIV_ROUND_UP(bits, SCOUTFS_BUDDY_ORDER0_BITS); + u64 tot = 0; + int level = 0; + int i; + + for (i = 0; i < SCOUTFS_BUDDY_MAX_HEIGHT; i++) + binf->blknos[i] = SCOUTFS_BUDDY_BLKNO; + + for (;;) { + for (i = level - 1; i >= 0; i--) + binf->blknos[i] += (blocks * 2); + tot += (blocks * 2); + + level++; + if (blocks == 1) + break; + blocks = DIV_ROUND_UP(blocks, SCOUTFS_BUDDY_SLOTS); + } + + binf->height = level; + + return tot; +} + +/* + * Figure out how many buddy blocks we'll need to allocate the rest of + * the blocks in the device. The first time through we find the size of + * the radix needed to describe the whole device, but that doesn't take + * the buddy block overhead into account. We iterate getting a more + * precise estimate each time. This only takes a few rounds to + * stabilize. + */ +void buddy_init(struct buddy_info *binf, u64 total_blocks) +{ + u64 blocks = SCOUTFS_BUDDY_BLKNO; + u64 was; + + while(1) { + was = blocks; + blocks = calc_blocks(binf, total_blocks - blocks); + if (blocks == was) + break; + } + + binf->buddy_blocks = blocks; +} diff --git a/utils/src/buddy.h b/utils/src/buddy.h new file mode 100644 index 00000000..6e70230d --- /dev/null +++ b/utils/src/buddy.h @@ -0,0 +1,16 @@ +#ifndef _BUDDY_H_ +#define _BUDDY_H_ + +#include "format.h" + +struct buddy_info { + u8 height; + u64 buddy_blocks; + + /* starting blkno in each level, including mirrors */ + u64 blknos[SCOUTFS_BUDDY_MAX_HEIGHT]; +}; + +void buddy_init(struct buddy_info *binf, u64 total_blocks); + +#endif diff --git a/utils/src/format.h b/utils/src/format.h index 58fea85f..fe83a346 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -19,8 +19,7 @@ */ #define SCOUTFS_SUPER_BLKNO ((64 * 1024) >> SCOUTFS_BLOCK_SHIFT) #define SCOUTFS_SUPER_NR 2 -#define SCOUTFS_BUDDY_BM_BLKNO (SCOUTFS_SUPER_BLKNO + SCOUTFS_SUPER_NR) -#define SCOUTFS_BUDDY_BM_NR 2 +#define SCOUTFS_BUDDY_BLKNO (SCOUTFS_SUPER_BLKNO + SCOUTFS_SUPER_NR) #define SCOUTFS_MAX_TRANS_BLOCKS (128 * 1024 * 1024 / SCOUTFS_BLOCK_SIZE) @@ -48,42 +47,49 @@ struct scoutfs_block_ref { __le64 seq; } __packed; -struct scoutfs_bitmap_block { - struct scoutfs_block_header hdr; - __le64 bits[0]; -} __packed; - /* - * Track allocations from BLOCK_SIZE to (BLOCK_SIZE << ..._ORDERS). + * If the block was full of bits the largest possible order would be + * the block size shift + 3 (BITS_PER_BYTE). But the header uses + * up some space and then the buddy bits mean two bits per block. + * Then +1 for this being the number, not the greatest order. */ -#define SCOUTFS_BUDDY_ORDERS 8 +#define SCOUTFS_BUDDY_ORDERS (SCOUTFS_BLOCK_SHIFT + 3 - 2 + 1) struct scoutfs_buddy_block { struct scoutfs_block_header hdr; - __le32 order_counts[SCOUTFS_BUDDY_ORDERS]; - __le64 bits[0]; + __le16 first_set[SCOUTFS_BUDDY_ORDERS]; + __u8 level; + __u8 __pad[3]; /* naturally align bits */ + union { + struct scoutfs_buddy_slot { + __le64 seq; + __le16 free_orders; + /* XXX seems like we could hide a bit somewhere */ + __u8 blkno_off; + } __packed slots[0]; + __le64 bits[0]; + } __packed; } __packed; /* - * If we had log2(raw bits) orders we'd fully use all of the raw bits in - * the block. We're close enough that the amount of space wasted at the - * end (~1/256th of the block, ~64 bytes) isn't worth worrying about. + * Each buddy leaf block references order 0 blocks with half of its + * bitmap. The other half of the bits are used for the higher order + * bits. */ #define SCOUTFS_BUDDY_ORDER0_BITS \ (((SCOUTFS_BLOCK_SIZE - sizeof(struct scoutfs_buddy_block)) * 8) / 2) -struct scoutfs_buddy_indirect { - struct scoutfs_block_header hdr; - __le64 order_totals[SCOUTFS_BUDDY_ORDERS]; - struct scoutfs_buddy_slot { - __u8 free_orders; - struct scoutfs_block_ref ref; - } slots[0]; +#define SCOUTFS_BUDDY_SLOTS \ + ((SCOUTFS_BLOCK_SIZE - sizeof(struct scoutfs_buddy_block)) / \ + sizeof(struct scoutfs_buddy_slot)) + +struct scoutfs_buddy_root { + struct scoutfs_buddy_slot slot; + __u8 height; } __packed; -#define SCOUTFS_BUDDY_SLOTS \ - ((SCOUTFS_BLOCK_SIZE - sizeof(struct scoutfs_buddy_indirect)) / \ - sizeof(struct scoutfs_buddy_slot)) +/* ((SCOUTFS_BUDDY_SLOTS^5) * SCOUTFS_BUDDY_ORDER0_BITS) > 2^52 */ +#define SCOUTFS_BUDDY_MAX_HEIGHT 6 /* * We should be able to make the offset smaller if neither dirents nor @@ -180,10 +186,10 @@ struct scoutfs_super_block { __u8 uuid[SCOUTFS_UUID_BYTES]; __le64 next_ino; __le64 total_blocks; - __le32 buddy_blocks; + __le64 free_blocks; + __le64 buddy_blocks; + struct scoutfs_buddy_root buddy_root; struct scoutfs_btree_root btree_root; - struct scoutfs_block_ref buddy_ind_ref; - struct scoutfs_block_ref buddy_bm_ref; } __packed; #define SCOUTFS_ROOT_INO 1 diff --git a/utils/src/mkfs.c b/utils/src/mkfs.c index 920361be..ac49cb74 100644 --- a/utils/src/mkfs.c +++ b/utils/src/mkfs.c @@ -18,6 +18,7 @@ #include "rand.h" #include "dev.h" #include "bitops.h" +#include "buddy.h" /* * Update the block's header and write it out. @@ -42,24 +43,14 @@ static int write_block(int fd, u64 blkno, struct scoutfs_super_block *super, return 0; } -/* - * Calculate the number of buddy blocks that are needed to manage - * allocation of a device with the given number of total blocks. - * - * We need a little bit of overhead to write each transaction's dirty - * buddy blocks to free space. We chose 16MB for now which is wild - * overkill and should be dependent on the max transaction size. - */ -static u32 calc_buddy_blocks(u64 total_blocks) +static u64 first_blkno(struct scoutfs_super_block *super) { - return DIV_ROUND_UP(total_blocks, SCOUTFS_BUDDY_ORDER0_BITS) + - ((16 * 1024 * 1024) / SCOUTFS_BLOCK_SIZE); + return SCOUTFS_BUDDY_BLKNO + le64_to_cpu(super->buddy_blocks); } -static u32 first_blkno(struct scoutfs_super_block *super) +static u64 last_blkno(struct scoutfs_super_block *super) { - return SCOUTFS_BUDDY_BM_BLKNO + SCOUTFS_BUDDY_BM_NR + - le32_to_cpu(super->buddy_blocks); + return le64_to_cpu(super->total_blocks) - 1; } /* the starting bit offset in the block bitmap of an order's bitmap */ @@ -78,6 +69,76 @@ static int order_nr(int order, int nr) return order_off(order) + nr; } +static void set_order_nr(struct scoutfs_buddy_block *bud, int order, u16 nr) +{ + u16 first = le16_to_cpu(bud->first_set[order]); + + if (nr <= first) + bud->first_set[order] = cpu_to_le16(nr); +} + +static void clear_order_nr(struct scoutfs_buddy_block *bud, int order, u16 nr) +{ + u16 first = le16_to_cpu(bud->first_set[order]); + int size; + int i; + + if (nr != first) + return; + + if (bud->level) { + for (i = nr + 1; i < SCOUTFS_BUDDY_SLOTS; i++) { + if (le16_to_cpu(bud->slots[i].free_orders) & + (1 << order)) + break; + } + if (i == SCOUTFS_BUDDY_SLOTS) + i = U16_MAX; + + } else { + size = order_off(order + 1); + i = find_next_bit_le(bud->bits, size, + order_nr(order, first) + 1); + if (i >= size) + i = U16_MAX; + else + i -= order_off(order); + } + + bud->first_set[order] = cpu_to_le16(i); +} + +#define for_each_changed_bit(nr, bit, old, new, tmp) \ + for (tmp = old ^ new; \ + tmp && (nr = ffs(tmp) - 1, bit = 1 << nr, 1); \ + tmp ^= bit) + +/* + * Set a slot's free_orders value and update first_set for each order + * that it changes. Returns true of the slot's free_orders was changed. + */ +static int set_slot_free_orders(struct scoutfs_buddy_block *bud, u16 sl, + u16 free_orders) +{ + u16 old = le16_to_cpu(bud->slots[sl].free_orders); + int order; + int tmp; + int bit; + + if (old == free_orders) + return 0; + + for_each_changed_bit(order, bit, old, free_orders, tmp) { + if (old & bit) + clear_order_nr(bud, order, sl); + else + set_order_nr(bud, order, sl); + } + + bud->slots[sl].free_orders = cpu_to_le16(free_orders); + return 1; +} + static int test_buddy_bit(struct scoutfs_buddy_block *bud, int order, int nr) { return test_bit_le(order_nr(order, nr), bud->bits); @@ -86,13 +147,13 @@ static int test_buddy_bit(struct scoutfs_buddy_block *bud, int order, int nr) static void set_buddy_bit(struct scoutfs_buddy_block *bud, int order, int nr) { if (!test_and_set_bit_le(order_nr(order, nr), bud->bits)) - le32_add_cpu(&bud->order_counts[order], 1); + set_order_nr(bud, order, nr); } static void clear_buddy_bit(struct scoutfs_buddy_block *bud, int order, int nr) { if (test_and_clear_bit_le(order_nr(order, nr), bud->bits)) - le32_add_cpu(&bud->order_counts[order], -1); + clear_order_nr(bud, order, nr); } /* merge lower orders buddies as we free up to the highest */ @@ -100,7 +161,7 @@ static void free_order_bit(struct scoutfs_buddy_block *bud, int order, int nr) { int i; - for (i = order; i < SCOUTFS_BUDDY_ORDERS - 1; i++) { + for (i = order; i < SCOUTFS_BUDDY_ORDERS - 2; i++) { if (!test_buddy_bit(bud, i, nr ^ 1)) break; @@ -112,40 +173,120 @@ static void free_order_bit(struct scoutfs_buddy_block *bud, int order, int nr) set_buddy_bit(bud, i, nr); } -static u8 calc_free_orders(struct scoutfs_buddy_block *bud) +static u16 calc_free_orders(struct scoutfs_buddy_block *bud) { - u8 free = 0; + u16 free = 0; int i; for (i = 0; i < SCOUTFS_BUDDY_ORDERS; i++) - free |= (!!bud->order_counts[i]) << i; + if (le16_to_cpu(bud->first_set[i]) != U16_MAX) + free |= 1 << i; return free; } -/* - * Figure out the free orders for the slot that starts with the given - * blkno. The bits in the buddy bitmap are relative to the starting - * blkno and are aligned so the bits in the count of blocks in the slot - * reflect the presence of free orders in that slot. - */ -static u8 slot_free_orders(u64 sl_blkno, u64 total_blocks) +static void init_buddy_block(struct scoutfs_buddy_block *bud, int level) { - u64 count; - u64 mask; - u8 free; + int i; - if (sl_blkno >= total_blocks) - return 0; + memset(bud, 0, SCOUTFS_BLOCK_SIZE); + for (i = 0; i < array_size(bud->first_set); i++) + bud->first_set[i] = cpu_to_le16(U16_MAX); + bud->level = level; +} - count = min(total_blocks - sl_blkno, SCOUTFS_BUDDY_ORDER0_BITS); +/* + * Write either the left-most or right-most buddy bitmap leaf in the + * allocator and then ascend writing parent blocks to the root. + * + * If we're writing the left leaf then blk is the first free blk. If + * we're writing the right leaf then blk is the last usable blk. + * + * If we're writing the left leaf then we don't actually write the root + * block. We record the free_orders for the first child block from the + * root block. When we write the right leaf we'll ascend into the root + * block and initialize the free_order of the first slot for the path to + * the left leaf. + * + * We initialize free_orders in all the unused slots so that the kernel + * can try to descend in to them when searching by size and will + * initialize new full blocks blocks. + */ +static int write_buddy_blocks(int fd, struct scoutfs_super_block *super, + struct buddy_info *binf, + struct scoutfs_buddy_block *bud, u64 blk, + int left, u16 *free_orders) +{ + u64 blkno; + int level; + int first; + int last; + int ret; + u16 free; + u16 full; + int sl; + int i; - mask = (1 << SCOUTFS_BUDDY_ORDERS) - 1; - free = count & mask; - if (count > mask) - free |= (mask + 1) >> 1; + if (left) { + first = blk; + last = SCOUTFS_BUDDY_ORDER0_BITS - 1; + } else { + first = 0; + last = min(blk % SCOUTFS_BUDDY_ORDER0_BITS, + SCOUTFS_BUDDY_ORDER0_BITS); + } - return free; + /* write the leaf block */ + level = 0; + init_buddy_block(bud, level); + for (i = first; i <= last; i++) + free_order_bit(bud, 0, i); + + blk = blk / SCOUTFS_BUDDY_ORDER0_BITS; + blkno = binf->blknos[level] + (blk * 2); + + ret = write_block(fd, blkno, super, &bud->hdr); + if (ret) + return ret; + + free = calc_free_orders(bud); + full = SCOUTFS_BUDDY_ORDER0_BITS; + + /* write parents, stopping before root if left */ + while (++level < (left ? binf->height - 1 : binf->height)) { + + sl = blk % SCOUTFS_BUDDY_SLOTS; + blk = blk / SCOUTFS_BUDDY_SLOTS; + blkno = binf->blknos[level] + (blk * 2); + + init_buddy_block(bud, level); + + /* set full until right spine, 0th in root from left */ + for (i = 0; i < sl; i++) + set_slot_free_orders(bud, i, full); + + if (!left && level == (binf->height - 1)) { + set_slot_free_orders(bud, 0, *free_orders); + bud->slots[0].seq = super->hdr.seq; + } + + set_slot_free_orders(bud, sl, free); + bud->slots[sl].seq = super->hdr.seq; + + /* init full slots in full parents down the left spine */ + for (i = sl; left && i < SCOUTFS_BUDDY_SLOTS; i++) + set_slot_free_orders(bud, i, full); + + ret = write_block(fd, blkno, super, &bud->hdr); + if (ret) + return ret; + + free = calc_free_orders(bud); + } + + *free_orders = free; + + return 0; } static int write_new_fs(char *path, int fd) @@ -154,27 +295,25 @@ static int write_new_fs(char *path, int fd) struct scoutfs_inode *inode; struct scoutfs_btree_block *bt; struct scoutfs_btree_item *item; - struct scoutfs_buddy_block *bud; - struct scoutfs_buddy_indirect *ind; - struct scoutfs_bitmap_block *bm; struct scoutfs_key root_key; + struct buddy_info binf; struct timeval tv; char uuid_str[37]; unsigned int i; + u64 limit; u64 size; u64 blkno; + u64 count; u64 total_blocks; - u64 buddy_blocks; - u64 sl_blkno; + u16 free_orders; void *buf; int ret; gettimeofday(&tv, NULL); buf = malloc(SCOUTFS_BLOCK_SIZE); - bud = malloc(SCOUTFS_BLOCK_SIZE); super = malloc(SCOUTFS_BLOCK_SIZE); - if (!buf || !bud || !super) { + if (!buf || !super) { ret = -errno; fprintf(stderr, "failed to allocate a block: %s (%d)\n", strerror(errno), errno); @@ -190,12 +329,8 @@ static int write_new_fs(char *path, int fd) /* 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); + + buddy_init(&binf, total_blocks); root_key.inode = cpu_to_le64(SCOUTFS_ROOT_INO); root_key.type = SCOUTFS_INODE_KEY; @@ -209,7 +344,16 @@ static int write_new_fs(char *path, int fd) uuid_generate(super->uuid); super->next_ino = cpu_to_le64(SCOUTFS_ROOT_INO + 1); super->total_blocks = cpu_to_le64(total_blocks); - super->buddy_blocks = cpu_to_le32(buddy_blocks); + super->buddy_blocks = cpu_to_le64(binf.buddy_blocks); + + /* require space for two leaf blocks for writing left/right paths */ + count = last_blkno(super) - first_blkno(super) + 1; + limit = (SCOUTFS_BUDDY_ORDER0_BITS * 2); + if (count < limit) { + fprintf(stderr, "%llu byte device only has room for %llu %u byte fs blocks, needs at least %llu fs blocks\n", + size, count, SCOUTFS_BLOCK_SIZE, limit); + goto out; + } blkno = first_blkno(super); @@ -240,68 +384,33 @@ static int write_new_fs(char *path, int fd) ret = write_block(fd, blkno, super, &bt->hdr); if (ret) goto out; + /* blkno is now first free */ + blkno++; /* the super references the btree block */ super->btree_root.height = 1; super->btree_root.ref.blkno = bt->hdr.blkno; super->btree_root.ref.seq = bt->hdr.seq; - /* free all the blocks in the first buddy block after btree block */ - memset(bud, 0, SCOUTFS_BLOCK_SIZE); - for (i = 1; i < min(total_blocks - first_blkno(super), - SCOUTFS_BUDDY_ORDER0_BITS); i++) - free_order_bit(bud, 0, i); + /* free_blocks reflects the fs blocks, not buddy blocks */ + super->free_blocks = cpu_to_le64(total_blocks - blkno); - blkno = SCOUTFS_BUDDY_BM_BLKNO + SCOUTFS_BUDDY_BM_NR; - ret = write_block(fd, blkno, super, &bud->hdr); + /* write left-most buddy block and all full parents, not root */ + ret = write_buddy_blocks(fd, super, &binf, buf, 0, 1, &free_orders); if (ret) goto out; - /* an indirect buddy block references the buddy bitmap block */ - memset(buf, 0, SCOUTFS_BLOCK_SIZE); - ind = buf; - for (i = 0; i < SCOUTFS_BUDDY_ORDERS; i++) - ind->order_totals[i] = cpu_to_le64(le32_to_cpu( - bud->order_counts[i])); - for (i = 0; i < SCOUTFS_BUDDY_SLOTS; i++) { - ind->slots[i].free_orders = 0; - ind->slots[i].ref = (struct scoutfs_block_ref){0,}; - } - ind->slots[0].free_orders = calc_free_orders(bud); - ind->slots[0].ref.seq = super->hdr.seq; - ind->slots[0].ref.blkno = cpu_to_le64(blkno); - - /* initialize unpopulated slot bits so the kernel will use them */ - sl_blkno = first_blkno(super) + SCOUTFS_BUDDY_ORDER0_BITS; - for (i = 1; i < SCOUTFS_BUDDY_SLOTS; i++) { - ind->slots[i].free_orders = slot_free_orders(sl_blkno, - total_blocks); - sl_blkno += SCOUTFS_BUDDY_ORDER0_BITS; - } - - blkno++; - ret = write_block(fd, blkno, super, &ind->hdr); + /* write right-most buddy and parents and the root */ + ret = write_buddy_blocks(fd, super, &binf, buf, + last_blkno(super) - first_blkno(super), + 0, &free_orders); if (ret) goto out; - /* the super references the buddy indirect block */ - super->buddy_ind_ref.blkno = ind->hdr.blkno; - super->buddy_ind_ref.seq = ind->hdr.seq; - - /* a bitmap block records the two used buddy blocks */ - memset(buf, 0, SCOUTFS_BLOCK_SIZE); - bm = buf; - memset(bm->bits, 0xff, SCOUTFS_BLOCK_SIZE - - offsetof(struct scoutfs_bitmap_block, bits)); - bm->bits[0] = cpu_to_le64(~0ULL << 2); /* two low order bits clear */ - - ret = write_block(fd, SCOUTFS_BUDDY_BM_BLKNO, super, &bm->hdr); - if (ret) - goto out; - - /* the super references the buddy bitmap block */ - super->buddy_bm_ref.blkno = bm->hdr.blkno; - super->buddy_bm_ref.seq = bm->hdr.seq; + /* the super references the buddy leaf block */ + super->buddy_root.height = binf.height; + super->buddy_root.slot.seq = super->hdr.seq; + super->buddy_root.slot.free_orders = cpu_to_le16(free_orders); /* write the two super blocks */ for (i = 0; i < SCOUTFS_SUPER_NR; i++) { @@ -326,15 +435,13 @@ static int write_new_fs(char *path, int fd) " buddy blocks: %llu\n" " fsid: %llx\n" " uuid: %s\n", - total_blocks, buddy_blocks, + total_blocks, le64_to_cpu(super->buddy_blocks), le64_to_cpu(super->hdr.fsid), uuid_str); ret = 0; out: if (super) free(super); - if (bud) - free(bud); if (buf) free(buf); return ret; diff --git a/utils/src/print.c b/utils/src/print.c index 9877be90..31ff1447 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -15,6 +15,7 @@ #include "format.h" #include "cmd.h" #include "crc.h" +#include "buddy.h" /* XXX maybe these go somewhere */ #define SKF "%llu.%u.%llu" @@ -217,98 +218,65 @@ static int print_btree_block(int fd, __le64 blkno, u8 level) return ret; } -static int print_buddy_block(int fd, struct scoutfs_super_block *super, - u64 blkno) +/* print populated buddy blocks */ +static int print_buddy_block(int fd, struct buddy_info *binf, + int level, u64 base, u8 off) { struct scoutfs_buddy_block *bud; + struct scoutfs_buddy_slot *slot; + int ret = 0; + u64 blkno; + u16 first; + int err; int i; + blkno = binf->blknos[level] + base + off; bud = read_block(fd, blkno); if (!bud) return -ENOMEM; printf("buddy blkno %llu\n", blkno); print_block_header(&bud->hdr); - printf(" order_counts:"); - for (i = 0; i < SCOUTFS_BUDDY_ORDERS; i++) - printf(" %u", le32_to_cpu(bud->order_counts[i])); + printf(" first_set:"); + for (i = 0; i < SCOUTFS_BUDDY_ORDERS; i++) { + first = le16_to_cpu(bud->first_set[i]); + if (first == U16_MAX) + printf(" -"); + else + printf(" %u", first); + } printf("\n"); + printf(" level: %u\n", bud->level); - free(bud); + for (i = 0; level && i < SCOUTFS_BUDDY_SLOTS; i++) { + slot = &bud->slots[i]; - return 0; -} - -static int print_buddy_blocks(int fd, struct scoutfs_super_block *super) -{ - struct scoutfs_buddy_indirect *ind; - struct scoutfs_buddy_slot *slot; - u64 blkno; - int ret = 0; - int err; - int i; - - blkno = le64_to_cpu(super->buddy_ind_ref.blkno); - ind = read_block(fd, blkno); - if (!ind) - return -ENOMEM; - - printf("buddy indirect blkno %llu\n", blkno); - print_block_header(&ind->hdr); - printf(" total_counts:"); - for (i = 0; i < SCOUTFS_BUDDY_ORDERS; i++) - printf(" %llu", le64_to_cpu(ind->order_totals[i])); - printf("\n"); - - for (i = 0; i < SCOUTFS_BUDDY_SLOTS; i++) { - slot = &ind->slots[i]; - - /* only print slots with non-zero fields */ - if (!slot->free_orders && !slot->ref.seq && !slot->ref.blkno) + if (slot->seq == 0) continue; - printf(" slot[%u]: free_orders: %x ref: seq %llu blkno %llu\n", - i, slot->free_orders, le64_to_cpu(slot->ref.seq), - le64_to_cpu(slot->ref.blkno)); + printf(" slots[%u]: seq %llu free_orders: %x blkno_off %u\n", + i, le64_to_cpu(slot->seq), + le16_to_cpu(slot->free_orders), slot->blkno_off); } - for (i = 0; i < SCOUTFS_BUDDY_SLOTS; i++) { - slot = &ind->slots[i]; + for (i = 0; level && i < SCOUTFS_BUDDY_SLOTS; i++) { + slot = &bud->slots[i]; - /* only print populated buddy blocks */ - if (slot->ref.blkno == 0) + if (slot->seq == 0) continue; - err = print_buddy_block(fd, super, - le64_to_cpu(slot->ref.blkno)); + err = print_buddy_block(fd, binf, level - 1, + (base * SCOUTFS_BUDDY_SLOTS) + (i * 2), + slot->blkno_off); if (err && !ret) ret = err; } - free(ind); + free(bud); return ret; } - -static int print_bitmap_block(int fd, struct scoutfs_super_block *super) -{ - struct scoutfs_bitmap_block *bm; - u64 blkno; - - blkno = le64_to_cpu(super->buddy_bm_ref.blkno); - bm = read_block(fd, blkno); - if (!bm) - return -ENOMEM; - - printf("bitmap blkno %llu\n", blkno); - print_block_header(&bm->hdr); - - free(bm); - - return 0; -} - static int print_super_blocks(int fd) { struct scoutfs_super_block *super; @@ -329,16 +297,17 @@ 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(" next_ino %llu total_blocks %llu buddy_blocks %u\n", + printf(" next_ino %llu total_blocks %llu buddy_blocks %llu\n" + " free_blocks %llu\n", le64_to_cpu(super->next_ino), le64_to_cpu(super->total_blocks), - le32_to_cpu(super->buddy_blocks)); - printf(" buddy_bm_ref: seq %llu blkno %llu\n", - le64_to_cpu(super->buddy_bm_ref.seq), - le64_to_cpu(super->buddy_bm_ref.blkno)); - printf(" buddy_ind_ref: seq %llu blkno %llu\n", - le64_to_cpu(super->buddy_ind_ref.seq), - le64_to_cpu(super->buddy_ind_ref.blkno)); + le64_to_cpu(super->buddy_blocks), + le64_to_cpu(super->free_blocks)); + printf(" buddy_root: height %u seq %llu free_orders %x blkno_off %u\n", + super->buddy_root.height, + le64_to_cpu(super->buddy_root.slot.seq), + le16_to_cpu(super->buddy_root.slot.free_orders), + super->buddy_root.slot.blkno_off); printf(" btree_root: height %u seq %llu blkno %llu\n", super->btree_root.height, le64_to_cpu(super->btree_root.ref.seq), @@ -352,13 +321,17 @@ static int print_super_blocks(int fd) super = &recent; - err = print_bitmap_block(fd, super); - if (err && !ret) - ret = err; - err = print_buddy_blocks(fd, super); - if (err && !ret) - ret = err; + if (super->buddy_root.height) { + struct buddy_info binf; + + buddy_init(&binf, le64_to_cpu(super->total_blocks)); + err = print_buddy_block(fd, &binf, + super->buddy_root.height - 1, 0, + super->buddy_root.slot.blkno_off); + if (err && !ret) + ret = err; + } if (super->btree_root.height) { err = print_btree_block(fd, super->btree_root.ref.blkno, From fb16af7b7d64addfd8abe1a213d0cc38888fc409 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Mon, 7 Nov 2016 14:42:36 -0800 Subject: [PATCH 054/235] btree nr_items is now a le16 The btree block now has a le16 nr_items field to make room for the number of items that larger blocks can hold. Signed-off-by: Zach Brown --- utils/src/format.h | 2 +- utils/src/mkfs.c | 2 +- utils/src/print.c | 11 ++++++----- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index fe83a346..685f1d53 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -136,7 +136,7 @@ struct scoutfs_btree_block { struct scoutfs_block_header hdr; __le16 free_end; __le16 free_reclaim; - __u8 nr_items; + __le16 nr_items; __le16 item_offs[0]; } __packed; diff --git a/utils/src/mkfs.c b/utils/src/mkfs.c index ac49cb74..c671d08d 100644 --- a/utils/src/mkfs.c +++ b/utils/src/mkfs.c @@ -360,7 +360,7 @@ static int write_new_fs(char *path, int fd) /* write a btree leaf root inode item */ memset(buf, 0, SCOUTFS_BLOCK_SIZE); bt = buf; - bt->nr_items = 1; + bt->nr_items = cpu_to_le16(1); bt->free_end = cpu_to_le16(SCOUTFS_BLOCK_SIZE - sizeof(*item) - sizeof(*inode)); bt->free_reclaim = 0; diff --git a/utils/src/print.c b/utils/src/print.c index 31ff1447..145d9f36 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -178,6 +178,7 @@ static int print_btree_block(int fd, __le64 blkno, u8 level) struct scoutfs_btree_item *item; struct scoutfs_btree_block *bt; struct scoutfs_block_ref *ref; + unsigned int nr; int ret = 0; int err; int i; @@ -186,14 +187,14 @@ static int print_btree_block(int fd, __le64 blkno, u8 level) if (!bt) return -ENOMEM; + nr = le16_to_cpu(bt->nr_items); + printf("btree blkno %llu\n", le64_to_cpu(blkno)); print_block_header(&bt->hdr); printf(" free_end %u free_reclaim %u nr_items %u\n", - le16_to_cpu(bt->free_end), - le16_to_cpu(bt->free_reclaim), - bt->nr_items); + le16_to_cpu(bt->free_end), le16_to_cpu(bt->free_reclaim), nr); - for (i = 0; i < bt->nr_items; i++) { + for (i = 0; i < nr; i++) { item = (void *)bt + le16_to_cpu(bt->item_offs[i]); printf(" [%u] off %u: key "SKF" seq %llu val_len %u\n", @@ -204,7 +205,7 @@ static int print_btree_block(int fd, __le64 blkno, u8 level) print_btree_val(item, level); } - for (i = 0; level && i < bt->nr_items; i++) { + for (i = 0; level && i < nr; i++) { item = (void *)bt + le16_to_cpu(bt->item_offs[i]); ref = (void *)item->val; From f1d8955303445604b11babd04644afcadea67a0e Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Mon, 14 Nov 2016 13:46:49 -0800 Subject: [PATCH 055/235] Support the ino_path ioctl We updated the inode_paths ioctl to return one path and use a counter cursor and renamed it to ino_path. Signed-off-by: Zach Brown --- utils/src/format.h | 9 ++--- utils/src/ino_path.c | 79 +++++++++++++++++++++++++++++++++++++ utils/src/ino_paths.c | 92 ------------------------------------------- utils/src/ioctl.h | 19 ++++----- 4 files changed, 92 insertions(+), 107 deletions(-) create mode 100644 utils/src/ino_path.c delete mode 100644 utils/src/ino_paths.c diff --git a/utils/src/format.h b/utils/src/format.h index 685f1d53..d88ed4d4 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -255,13 +255,10 @@ struct scoutfs_dirent { #define SCOUTFS_NAME_LEN 255 /* - * This is arbitrarily limiting the max size of the single buffer - * that's needed in the inode_paths ioctl to return all the paths - * that link to an inode. The structures could easily support much - * more than this but then we'd need to grow a more thorough interface - * for iterating over referring paths. That sounds horrible. + * Just to keep sloppy (int) apps from being confused. 2^31 is good + * enough for everybody. */ -#define SCOUTFS_LINK_MAX 255 +#define SCOUTFS_LINK_MAX (1 << 31) /* * We only use 31 bits for readdir positions so that we don't confuse diff --git a/utils/src/ino_path.c b/utils/src/ino_path.c new file mode 100644 index 00000000..69240a4f --- /dev/null +++ b/utils/src/ino_path.c @@ -0,0 +1,79 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sparse.h" +#include "util.h" +#include "ioctl.h" +#include "cmd.h" + +static int ino_path_cmd(int argc, char **argv) +{ + struct scoutfs_ioctl_ino_path args; + char *endptr = NULL; + char *path; + u64 ino; + int ret; + int fd; + + if (argc != 2) { + fprintf(stderr, "must specify ino and path\n"); + return -EINVAL; + } + + ino = strtoull(argv[0], &endptr, 0); + if (*endptr != '\0' || + ((ino == LLONG_MIN || ino == LLONG_MAX) && errno == ERANGE)) { + fprintf(stderr, "error parsing inode number '%s'\n", + argv[0]); + return -EINVAL; + } + + fd = open(argv[1], O_RDONLY); + if (fd < 0) { + ret = -errno; + fprintf(stderr, "failed to open '%s': %s (%d)\n", + argv[1], strerror(errno), errno); + return ret; + } + + path = malloc(PATH_MAX); + if (!path) { + fprintf(stderr, "couldn't allocate %d byte buffer\n", PATH_MAX); + ret = -ENOMEM; + goto out; + } + + args.ino = ino; + args.ctr = 0; + args.path_ptr = (intptr_t)path; + args.path_bytes = PATH_MAX; + do { + ret = ioctl(fd, SCOUTFS_IOC_INO_PATH, &args); + if (ret > 0) + printf("%s\n", path); + } while (ret > 0); + + if (ret < 0) { + ret = -errno; + fprintf(stderr, "inodes_since ioctl failed: %s (%d)\n", + strerror(errno), errno); + } +out: + free(path); + close(fd); + return ret; +}; + +static void __attribute__((constructor)) ino_path_ctor(void) +{ + cmd_register("ino-path", " ", + "print paths that refer to inode #", ino_path_cmd); +} diff --git a/utils/src/ino_paths.c b/utils/src/ino_paths.c deleted file mode 100644 index 17a087e5..00000000 --- a/utils/src/ino_paths.c +++ /dev/null @@ -1,92 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "sparse.h" -#include "util.h" -#include "ioctl.h" -#include "cmd.h" - -static int inode_paths_cmd(int argc, char **argv) -{ - struct scoutfs_ioctl_inode_paths args; - char *endptr; - void *ptr = NULL; - char *path; - u64 ino; - int len; - int ret; - int fd; - - if (argc != 2) { - fprintf(stderr, "must specify ino and path\n"); - return -EINVAL; - } - - ino = strtoull(argv[0], &endptr, 0); - if (*endptr != '\0' || - ((ino == LLONG_MIN || ino == LLONG_MAX) && errno == ERANGE)) { - fprintf(stderr, "error parsing inode number '%s'\n", - argv[0]); - return -EINVAL; - } - - fd = open(argv[1], O_RDONLY); - if (fd < 0) { - ret = -errno; - fprintf(stderr, "failed to open '%s': %s (%d)\n", - argv[1], strerror(errno), errno); - return ret; - } - - len = 16 * PATH_MAX; - do { - free(ptr); - ptr = malloc(len); - if (!ptr) { - fprintf(stderr, "couldn't allocate %d byte buffer\n", - len); - ret = -EINVAL; - goto out; - } - - args.ino = ino; - args.buf_ptr = (intptr_t)ptr; - args.buf_len = len; - - ret = ioctl(fd, SCOUTFS_IOC_INODE_PATHS, &args); - if (ret < 0 && errno != EOVERFLOW) { - ret = -errno; - fprintf(stderr, "inodes_since ioctl failed: %s (%d)\n", - strerror(errno), errno); - goto out; - } - - len *= 2; - - } while (ret < 0 && errno == EOVERFLOW); - - path = ptr; - while (*path) { - printf("%s\n", path); - path += strlen(path) + 1; - } - -out: - free(ptr); - close(fd); - return ret; -}; - -static void __attribute__((constructor)) since_ctor(void) -{ - cmd_register("inode-paths", " ", - "print paths that refer to inode #", inode_paths_cmd); -} diff --git a/utils/src/ioctl.h b/utils/src/ioctl.h index a4991b69..e5598103 100644 --- a/utils/src/ioctl.h +++ b/utils/src/ioctl.h @@ -24,18 +24,17 @@ struct scoutfs_ioctl_inodes_since { #define SCOUTFS_IOC_INODES_SINCE _IOW(SCOUTFS_IOCTL_MAGIC, 1, \ struct scoutfs_ioctl_inodes_since) -struct scoutfs_ioctl_inode_paths { +/* returns bytes of path buffer set starting at _off, including null */ +struct scoutfs_ioctl_ino_path { __u64 ino; - __u64 buf_ptr; - __u32 buf_len; + __u64 ctr; /* init to 0, set to next */ + __u64 path_ptr; + __u16 path_bytes; /* total buffer space, including null term */ } __packed; -/* - * Fills the callers buffer with all the paths from the root to the - * target inode. - */ -#define SCOUTFS_IOC_INODE_PATHS _IOW(SCOUTFS_IOCTL_MAGIC, 2, \ - struct scoutfs_ioctl_inode_paths) +/* Get a single path from the root to the given inode number */ +#define SCOUTFS_IOC_INO_PATH _IOW(SCOUTFS_IOCTL_MAGIC, 2, \ + struct scoutfs_ioctl_ino_path) /* XXX might as well include a seq? 0 for current behaviour? */ struct scoutfs_ioctl_find_xattr { @@ -52,4 +51,6 @@ struct scoutfs_ioctl_find_xattr { #define SCOUTFS_IOC_FIND_XATTR_VAL _IOW(SCOUTFS_IOCTL_MAGIC, 4, \ struct scoutfs_ioctl_find_xattr) +#define SCOUTFS_IOC_INODE_DATA_SINCE _IOW(SCOUTFS_IOCTL_MAGIC, 5, \ + struct scoutfs_ioctl_inodes_since) #endif From c2cfb0227fa1d95a102c405ed2cbcced53e40415 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Tue, 15 Nov 2016 15:51:29 -0800 Subject: [PATCH 056/235] Print the new inode data_version field We've added a data_version field to the inode for tracking changes to the file's data. Signed-off-by: Zach Brown --- utils/src/format.h | 5 +++++ utils/src/print.c | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/utils/src/format.h b/utils/src/format.h index d88ed4d4..c88462eb 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -200,6 +200,10 @@ struct scoutfs_timespec { } __packed; /* + * @data_version: incremented every time the contents of a file could + * have changed. It is exposed via an ioctl and is then provided as an + * argument to data functions to protect racing modification. + * * XXX * - otime? * - compat flags? @@ -211,6 +215,7 @@ struct scoutfs_inode { __le64 size; __le64 blocks; __le64 link_counter; + __le64 data_version; __le32 nlink; __le32 uid; __le32 gid; diff --git a/utils/src/print.c b/utils/src/print.c index 145d9f36..66f64fea 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -61,7 +61,7 @@ static void print_inode(struct scoutfs_inode *inode) { printf(" inode: size: %llu blocks: %llu lctr: %llu nlink: %u\n" " uid: %u gid: %u mode: 0%o rdev: 0x%x\n" - " salt: 0x%x\n" + " salt: 0x%x data_version %llu\n" " atime: %llu.%08u ctime: %llu.%08u\n" " mtime: %llu.%08u\n", le64_to_cpu(inode->size), le64_to_cpu(inode->blocks), @@ -69,6 +69,7 @@ static void print_inode(struct scoutfs_inode *inode) le32_to_cpu(inode->nlink), le32_to_cpu(inode->uid), le32_to_cpu(inode->gid), le32_to_cpu(inode->mode), le32_to_cpu(inode->rdev), le32_to_cpu(inode->salt), + le64_to_cpu(inode->data_version), le64_to_cpu(inode->atime.sec), le32_to_cpu(inode->atime.nsec), le64_to_cpu(inode->ctime.sec), From 22140c93d1ed9d1f8d375c0baa4daf52304c6fdc Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Tue, 15 Nov 2016 15:59:02 -0800 Subject: [PATCH 057/235] Print extents instead of bmap items Print the extent items now that we're not using bmap items any more. Signed-off-by: Zach Brown --- utils/src/format.h | 31 +++++++++---------------------- utils/src/print.c | 22 +++++++++++----------- 2 files changed, 20 insertions(+), 33 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index c88462eb..611cd7a2 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -113,7 +113,7 @@ struct scoutfs_key { #define SCOUTFS_DIRENT_KEY 5 #define SCOUTFS_LINK_BACKREF_KEY 6 #define SCOUTFS_SYMLINK_KEY 7 -#define SCOUTFS_BMAP_KEY 8 +#define SCOUTFS_EXTENT_KEY 8 #define SCOUTFS_ORPHAN_KEY 9 #define SCOUTFS_MAX_ITEM_LEN 512 @@ -259,11 +259,8 @@ struct scoutfs_dirent { #define SCOUTFS_NAME_LEN 255 -/* - * Just to keep sloppy (int) apps from being confused. 2^31 is good - * enough for everybody. - */ -#define SCOUTFS_LINK_MAX (1 << 31) +/* S32_MAX avoids the (int) sign bit and might avoid sloppy bugs */ +#define SCOUTFS_LINK_MAX S32_MAX /* * We only use 31 bits for readdir positions so that we don't confuse @@ -296,23 +293,13 @@ struct scoutfs_xattr { __u8 name[0]; } __packed; -/* - * We use simple block map items to map a aligned fixed group of logical - * block offsets to physical blocks. We make them a decent size to - * reduce the item storage overhead per block referenced, but we don't - * want them so large that they start to take up an extraordinary amount - * of space for small files. 8 block items ranges from around 3% to .3% - * overhead for files that use only one or all of the blocks in the - * mapping item. - */ -#define SCOUTFS_BLOCK_MAP_SHIFT 3 -#define SCOUTFS_BLOCK_MAP_COUNT (1 << SCOUTFS_BLOCK_MAP_SHIFT) -#define SCOUTFS_BLOCK_MAP_MASK (SCOUTFS_BLOCK_MAP_COUNT - 1) +struct scoutfs_file_extent { + __le64 blkno; + __le64 len; + __u8 flags; +} __packed; -struct scoutfs_block_map { - __le64 blkno[SCOUTFS_BLOCK_MAP_COUNT]; - __le64 seq[SCOUTFS_BLOCK_MAP_COUNT]; -}; +#define SCOUTFS_EXTENT_FLAG_OFFLINE (1 << 0) /* * link backrefs give us a way to find all the hard links that refer diff --git a/utils/src/print.c b/utils/src/print.c index 66f64fea..91655582 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -122,16 +122,16 @@ static void print_symlink(char *str, unsigned int val_len) printf(" symlink: %.*s\n", val_len, str); } -static void print_block_map(struct scoutfs_block_map *map) -{ - int i; +#define EXT_FLAG(f, flags, str) \ + (flags & f) ? str : "", (flags & (f - 1)) ? "|" : "" - printf(" bmap:"); - for (i = 0; i < SCOUTFS_BLOCK_MAP_COUNT; i++) - printf(" [%u] %llu:%llu", - i, le64_to_cpu(map->blkno[i]), - le64_to_cpu(map->seq[i])); - printf("\n"); +static void print_extent(struct scoutfs_key *key, + struct scoutfs_file_extent *ext) +{ + printf(" extent: (offest %llu) blkno %llu, len %llu flags %s%s\n", + le64_to_cpu(key->offset), le64_to_cpu(ext->blkno), + le64_to_cpu(ext->len), + EXT_FLAG(SCOUTFS_EXTENT_FLAG_OFFLINE, ext->flags, "OFF")); } static void print_block_ref(struct scoutfs_block_ref *ref) @@ -168,8 +168,8 @@ static void print_btree_val(struct scoutfs_btree_item *item, u8 level) case SCOUTFS_SYMLINK_KEY: print_symlink((void *)item->val, le16_to_cpu(item->val_len)); break; - case SCOUTFS_BMAP_KEY: - print_block_map((void *)item->val); + case SCOUTFS_EXTENT_KEY: + print_extent(&item->key, (void *)item->val); break; } } From 932b0776d1d498847ede3f6f6ff08afc91d1e5f6 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Tue, 15 Nov 2016 17:53:00 -0800 Subject: [PATCH 058/235] Add commands for working with offline data Add the data_version, stage, and release commands for working with offline extents of file data. Signed-off-by: Zach Brown --- utils/src/ioctl.h | 28 +++++ utils/src/stage_release.c | 233 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 261 insertions(+) create mode 100644 utils/src/stage_release.c diff --git a/utils/src/ioctl.h b/utils/src/ioctl.h index e5598103..75e11343 100644 --- a/utils/src/ioctl.h +++ b/utils/src/ioctl.h @@ -53,4 +53,32 @@ struct scoutfs_ioctl_find_xattr { #define SCOUTFS_IOC_INODE_DATA_SINCE _IOW(SCOUTFS_IOCTL_MAGIC, 5, \ struct scoutfs_ioctl_inodes_since) + +struct scoutfs_ioctl_data_version { + __u64 ino; + __u64 data_version; +} __packed; + +#define SCOUTFS_IOC_DATA_VERSION _IOW(SCOUTFS_IOCTL_MAGIC, 6, \ + struct scoutfs_ioctl_data_version) + +struct scoutfs_ioctl_release { + __u64 offset; + __u64 count; + __u64 data_version; +} __packed; + +#define SCOUTFS_IOC_RELEASE _IOW(SCOUTFS_IOCTL_MAGIC, 7, \ + struct scoutfs_ioctl_release) + +struct scoutfs_ioctl_stage { + __u64 data_version; + __u64 buf_ptr; + __u64 offset; + __s32 count; +} __packed; + +#define SCOUTFS_IOC_STAGE _IOW(SCOUTFS_IOCTL_MAGIC, 8, \ + struct scoutfs_ioctl_stage) + #endif diff --git a/utils/src/stage_release.c b/utils/src/stage_release.c new file mode 100644 index 00000000..581cb69f --- /dev/null +++ b/utils/src/stage_release.c @@ -0,0 +1,233 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sparse.h" +#include "util.h" +#include "ioctl.h" +#include "cmd.h" + +static int stage_cmd(int argc, char **argv) +{ + struct scoutfs_ioctl_stage args; + char *endptr = NULL; + char *buf = NULL; + int afd = -1; + int fd = -1; + u64 offset; + u64 count; + u64 vers; + int ret; + + if (argc != 5) { + fprintf(stderr, "must specify moar args\n"); + return -EINVAL; + } + + fd = open(argv[0], O_RDWR); + if (fd < 0) { + ret = -errno; + fprintf(stderr, "failed to open '%s': %s (%d)\n", + argv[0], strerror(errno), errno); + return ret; + } + + vers = strtoull(argv[1], &endptr, 0); + if (*endptr != '\0' || + ((vers == LLONG_MIN || vers == LLONG_MAX) && errno == ERANGE)) { + fprintf(stderr, "error parsing data version '%s'\n", + argv[1]); + ret = -EINVAL; + goto out; + } + + offset = strtoull(argv[2], &endptr, 0); + if (*endptr != '\0' || + ((offset == LLONG_MIN || offset == LLONG_MAX) && errno == ERANGE)) { + fprintf(stderr, "error parsing offset '%s'\n", + argv[2]); + ret = -EINVAL; + goto out; + } + + count = strtoull(argv[3], &endptr, 0); + if (*endptr != '\0' || + ((count == LLONG_MIN || count == LLONG_MAX) && errno == ERANGE)) { + fprintf(stderr, "error parsing count '%s'\n", + argv[3]); + ret = -EINVAL; + goto out; + } + + if (count > INT_MAX) { + fprintf(stderr, "count %llu too large, limited to %d\n", + count, INT_MAX); + ret = -EINVAL; + goto out; + } + + afd = open(argv[4], O_RDONLY); + if (afd < 0) { + ret = -errno; + fprintf(stderr, "failed to open '%s': %s (%d)\n", + argv[4], strerror(errno), errno); + goto out; + } + + buf = malloc(count); + if (!buf) { + fprintf(stderr, "couldn't allocate %llu byte buffer\n", + count); + ret = -ENOMEM; + goto out; + } + + ret = read(afd, buf, count); + if (ret < count) { + fprintf(stderr, "archive read returned %d, not %llu: error %s (%d)\n", + ret, count, strerror(errno), errno); + ret = -EIO; + goto out; + } + + args.data_version = vers; + args.buf_ptr = (unsigned long)buf; + args.offset = offset; + args.count = count; + + ret = ioctl(fd, SCOUTFS_IOC_STAGE, &args); + if (ret < count) { + fprintf(stderr, "stage returned %d, not %llu: error %s (%d)\n", + ret, count, strerror(errno), errno); + ret = -EIO; + } +out: + free(buf); + if (fd > -1) + close(fd); + if (afd > -1) + close(afd); + return ret; +}; + +static void __attribute__((constructor)) stage_ctor(void) +{ + cmd_register("stage", " ", + "write archive file contents to offline region", stage_cmd); +} + +static int release_cmd(int argc, char **argv) +{ + struct scoutfs_ioctl_release args; + char *endptr = NULL; + u64 offset; + u64 count; + u64 vers; + int ret; + int fd; + + if (argc != 4) { + fprintf(stderr, "must specify path, data version, offset, and count\n"); + return -EINVAL; + } + + fd = open(argv[0], O_RDWR); + if (fd < 0) { + ret = -errno; + fprintf(stderr, "failed to open '%s': %s (%d)\n", + argv[0], strerror(errno), errno); + return ret; + } + + vers = strtoull(argv[1], &endptr, 0); + if (*endptr != '\0' || + ((vers == LLONG_MIN || vers == LLONG_MAX) && errno == ERANGE)) { + fprintf(stderr, "error parsing data version '%s'\n", + argv[1]); + ret = -EINVAL; + goto out; + } + + offset = strtoull(argv[2], &endptr, 0); + if (*endptr != '\0' || + ((offset == LLONG_MIN || offset == LLONG_MAX) && errno == ERANGE)) { + fprintf(stderr, "error parsing starting offset '%s'\n", + argv[2]); + ret = -EINVAL; + goto out; + } + + count = strtoull(argv[3], &endptr, 0); + if (*endptr != '\0' || + ((count == LLONG_MIN || count == LLONG_MAX) && errno == ERANGE)) { + fprintf(stderr, "error parsing length '%s'\n", + argv[3]); + ret = -EINVAL; + goto out; + } + + args.offset = offset; + args.count = count; + args.data_version = vers; + + ret = ioctl(fd, SCOUTFS_IOC_RELEASE, &args); + if (ret < 0) { + ret = -errno; + fprintf(stderr, "release ioctl failed: %s (%d)\n", + strerror(errno), errno); + } +out: + close(fd); + return ret; +}; + +static void __attribute__((constructor)) release_ctor(void) +{ + cmd_register("release", " ", + "mark file region offline and free extents", release_cmd); +} + +static int data_version_cmd(int argc, char **argv) +{ + u64 vers; + int ret; + int fd; + + if (argc != 1) { + fprintf(stderr, "must specify path\n"); + return -EINVAL; + } + + fd = open(argv[0], O_RDWR); + if (fd < 0) { + ret = -errno; + fprintf(stderr, "failed to open '%s': %s (%d)\n", + argv[0], strerror(errno), errno); + return ret; + } + + ret = ioctl(fd, SCOUTFS_IOC_DATA_VERSION, &vers); + if (ret < 0) { + ret = -errno; + fprintf(stderr, "data version ioctl failed: %s (%d)\n", + strerror(errno), errno); + } else { + printf("%llu\n", vers); + } + + close(fd); + return ret; +}; + +static void __attribute__((constructor)) data_version_ctor(void) +{ + cmd_register("data_version", "", + "print the file's data version", data_version_cmd); +} From c3f122a5f188c6e91a6d4c3155ed1fccb1f02592 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Thu, 17 Nov 2016 15:03:22 -0800 Subject: [PATCH 059/235] Fix mkfs buddy initialization mkfs was starting setting free blk bits from 0 instead of from the blkno offset of the first free block. This resulted in the highest order above a used blkno being marked free. Freeing that blkno would set its lowest order blkno. Now that blkno can be allocated from two orders. That, eventually, can lead to blocks being doubly allocated and users trampling on each other. While auditing the code to chase this bug down I also noticed that write_buddy_blocks() was using a min() that makes no sense at all. Here 'blk' is inclusive, the modulo math works on its own. Signed-off-by: Zach Brown --- utils/src/mkfs.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/utils/src/mkfs.c b/utils/src/mkfs.c index c671d08d..7023b409 100644 --- a/utils/src/mkfs.c +++ b/utils/src/mkfs.c @@ -232,8 +232,7 @@ static int write_buddy_blocks(int fd, struct scoutfs_super_block *super, last = SCOUTFS_BUDDY_ORDER0_BITS - 1; } else { first = 0; - last = min(blk % SCOUTFS_BUDDY_ORDER0_BITS, - SCOUTFS_BUDDY_ORDER0_BITS); + last = blk % SCOUTFS_BUDDY_ORDER0_BITS; } /* write the leaf block */ @@ -396,7 +395,9 @@ static int write_new_fs(char *path, int fd) super->free_blocks = cpu_to_le64(total_blocks - blkno); /* write left-most buddy block and all full parents, not root */ - ret = write_buddy_blocks(fd, super, &binf, buf, 0, 1, &free_orders); + ret = write_buddy_blocks(fd, super, &binf, buf, + blkno - first_blkno(super), + 1, &free_orders); if (ret) goto out; From ec702b9bb3f241527bf760a4a251aa9111f68104 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Thu, 17 Nov 2016 15:45:05 -0800 Subject: [PATCH 060/235] Update the data_version ioctl to return the u64 We updated the code to use the new iteration of the data_version ioctl but we forgot to update the ioctl definition so it didn't actually work. Signed-off-by: Zach Brown --- utils/src/ioctl.h | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/utils/src/ioctl.h b/utils/src/ioctl.h index 75e11343..94963273 100644 --- a/utils/src/ioctl.h +++ b/utils/src/ioctl.h @@ -54,13 +54,7 @@ struct scoutfs_ioctl_find_xattr { #define SCOUTFS_IOC_INODE_DATA_SINCE _IOW(SCOUTFS_IOCTL_MAGIC, 5, \ struct scoutfs_ioctl_inodes_since) -struct scoutfs_ioctl_data_version { - __u64 ino; - __u64 data_version; -} __packed; - -#define SCOUTFS_IOC_DATA_VERSION _IOW(SCOUTFS_IOCTL_MAGIC, 6, \ - struct scoutfs_ioctl_data_version) +#define SCOUTFS_IOC_DATA_VERSION _IOW(SCOUTFS_IOCTL_MAGIC, 6, u64) struct scoutfs_ioctl_release { __u64 offset; From 41e3ca0f41d2188e1064301e080cfb03d4eee84b Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Thu, 17 Nov 2016 19:47:39 -0800 Subject: [PATCH 061/235] Consistently use __u8 in format.h Signed-off-by: Zach Brown --- utils/src/format.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index 611cd7a2..66c27847 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -97,7 +97,7 @@ struct scoutfs_buddy_root { */ struct scoutfs_key { __le64 inode; - u8 type; + __u8 type; __le64 offset; } __packed; @@ -119,7 +119,7 @@ struct scoutfs_key { #define SCOUTFS_MAX_ITEM_LEN 512 struct scoutfs_btree_root { - u8 height; + __u8 height; struct scoutfs_block_ref ref; } __packed; From 5fcf70b53e17ba8ec68f7c0e851e47f0ebd12ffd Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Thu, 17 Nov 2016 19:48:05 -0800 Subject: [PATCH 062/235] Catch up to kernel's scoutfs_extent Signed-off-by: Zach Brown --- utils/src/format.h | 2 +- utils/src/print.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index 66c27847..a30f348d 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -293,7 +293,7 @@ struct scoutfs_xattr { __u8 name[0]; } __packed; -struct scoutfs_file_extent { +struct scoutfs_extent { __le64 blkno; __le64 len; __u8 flags; diff --git a/utils/src/print.c b/utils/src/print.c index 91655582..3ee17643 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -126,7 +126,7 @@ static void print_symlink(char *str, unsigned int val_len) (flags & f) ? str : "", (flags & (f - 1)) ? "|" : "" static void print_extent(struct scoutfs_key *key, - struct scoutfs_file_extent *ext) + struct scoutfs_extent *ext) { printf(" extent: (offest %llu) blkno %llu, len %llu flags %s%s\n", le64_to_cpu(key->offset), le64_to_cpu(ext->blkno), From 9d3fe27929f12bdab9aa96a930a45c35dea72c8c Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Thu, 17 Nov 2016 20:42:49 -0800 Subject: [PATCH 063/235] Add data_since command Add a data_since command that operates just like inodes-since. Signed-off-by: Zach Brown --- utils/src/since.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/utils/src/since.c b/utils/src/since.c index 9eb7a7c1..d62db300 100644 --- a/utils/src/since.c +++ b/utils/src/since.c @@ -14,7 +14,7 @@ #include "ioctl.h" #include "cmd.h" -static int since_cmd(int argc, char **argv) +static int since_cmd(int argc, char **argv, unsigned long ioc) { struct scoutfs_ioctl_inodes_since args; struct scoutfs_ioctl_ino_seq *iseq; @@ -64,7 +64,7 @@ static int since_cmd(int argc, char **argv) args.buf_ptr = (intptr_t)ptr; args.buf_len = len; - ret = ioctl(fd, SCOUTFS_IOC_INODES_SINCE, &args); + ret = ioctl(fd, ioc, &args); if (ret < 0) { ret = -errno; fprintf(stderr, "inodes_since ioctl failed: %s (%d)\n", @@ -82,8 +82,20 @@ out: return ret; }; +static int inodes_since_cmd(int argc, char **argv) +{ + return since_cmd(argc, argv, SCOUTFS_IOC_INODES_SINCE); +} + +static int data_since_cmd(int argc, char **argv) +{ + return since_cmd(argc, argv, SCOUTFS_IOC_INODE_DATA_SINCE); +} + static void __attribute__((constructor)) since_ctor(void) { cmd_register("inodes-since", " ", - "print inodes modified since seq #", since_cmd); + "print inodes modified since seq #", inodes_since_cmd); + cmd_register("data-since", " ", + "print inodes with data blocks modified since seq #", data_since_cmd); } From c96b833a366e5e6b38eb46a9ca27183cf5997111 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Sat, 3 Dec 2016 12:17:47 -0800 Subject: [PATCH 064/235] mkfs LSM segment and ring stuctures Make a new file system by writing a root inode in a segment and storing a manifest entry in the ring that references the segment. Signed-off-by: Zach Brown --- utils/src/format.h | 96 ++++++++++- utils/src/mkfs.c | 409 +++++++++++---------------------------------- 2 files changed, 192 insertions(+), 313 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index a30f348d..dd4afe48 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -6,9 +6,23 @@ /* super block id */ #define SCOUTFS_SUPER_ID 0x2e736674756f6373ULL /* "scoutfs." */ +/* + * The super block and ring blocks are fixed 4k. + */ #define SCOUTFS_BLOCK_SHIFT 12 #define SCOUTFS_BLOCK_SIZE (1 << SCOUTFS_BLOCK_SHIFT) #define SCOUTFS_BLOCK_MASK (SCOUTFS_BLOCK_SIZE - 1) +#define SCOUTFS_BLOCKS_PER_PAGE (PAGE_SIZE / SCOUTFS_BLOCK_SIZE) + +/* + * FS data is stored in segments, for now they're fixed size. They'll + * be dynamic. + */ +#define SCOUTFS_SEGMENT_SHIFT 20 +#define SCOUTFS_SEGMENT_SIZE (1 << SCOUTFS_SEGMENT_SHIFT) +#define SCOUTFS_SEGMENT_MASK (SCOUTFS_SEGMENT_SIZE - 1) +#define SCOUTFS_SEGMENT_PAGES (SCOUTFS_SEGMENT_SIZE / PAGE_SIZE) +#define SCOUTFS_SEGMENT_BLOCKS (SCOUTFS_SEGMENT_SIZE / SCOUTFS_BLOCK_SIZE) #define SCOUTFS_PAGES_PER_BLOCK (SCOUTFS_BLOCK_SIZE / PAGE_SIZE) #define SCOUTFS_BLOCK_PAGE_ORDER (SCOUTFS_BLOCK_SHIFT - PAGE_SHIFT) @@ -23,6 +37,8 @@ #define SCOUTFS_MAX_TRANS_BLOCKS (128 * 1024 * 1024 / SCOUTFS_BLOCK_SIZE) +#define SCOUTFS_MAX_KEY_BYTES 255 + /* * This header is found at the start of every block so that we can * verify that it's what we were looking for. The crc and padding @@ -37,6 +53,67 @@ struct scoutfs_block_header { __le64 blkno; } __packed; +struct scoutfs_ring_entry_header { + __u8 type; + __le16 len; +} __packed; + +#define SCOUTFS_RING_ADD_MANIFEST 1 + +struct scoutfs_ring_add_manifest { + struct scoutfs_ring_entry_header eh; + __le64 segno; + __le64 seq; + __le16 first_key_len; + __le16 last_key_len; + __u8 level; + /* first and last key bytes */ +} __packed; + +/* + * This is absurdly huge. If there was only ever 1 item per segment and + * 2^64 items the tree could get this deep. + */ +#define SCOUTFS_MANIFEST_MAX_LEVEL 20 + +struct scoutfs_ring_block { + struct scoutfs_block_header hdr; + __le32 nr_entries; + struct scoutfs_ring_entry_header entries[0]; +} __packed; + +struct scoutfs_segment_item { + __le64 seq; + __le32 key_off; + __le32 val_off; + __le16 key_len; + __le16 val_len; +} __packed; + +/* + * Each large segment starts with a segment block that describes the + * rest of the blocks that make up the segment. + */ +struct scoutfs_segment_block { + __le32 crc; + __le32 _padding; + __le64 segno; + __le64 max_seq; + __le32 nr_items; + /* item array with gaps so they don't cross 4k blocks */ + /* packed keys */ + /* packed vals */ +} __packed; + +/* the first block in the segment has the header and items */ +#define SCOUTFS_SEGMENT_FIRST_BLOCK_ITEMS \ + ((SCOUTFS_BLOCK_SIZE - sizeof(struct scoutfs_segment_block)) / \ + sizeof(struct scoutfs_segment_item)) + +/* the rest of the header blocks are full of items */ +#define SCOUTFS_SEGMENT_ITEMS_PER_BLOCK \ + (SCOUTFS_BLOCK_SIZE / sizeof(struct scoutfs_segment_item)) + /* * Block references include the sequence number so that we can detect * readers racing with writers and so that we can tell that we don't @@ -97,7 +174,7 @@ struct scoutfs_buddy_root { */ struct scoutfs_key { __le64 inode; - __u8 type; + u8 type; __le64 offset; } __packed; @@ -118,8 +195,13 @@ struct scoutfs_key { #define SCOUTFS_MAX_ITEM_LEN 512 +struct scoutfs_inode_key { + __u8 type; + __be64 ino; +} __packed; + struct scoutfs_btree_root { - __u8 height; + u8 height; struct scoutfs_block_ref ref; } __packed; @@ -180,6 +262,11 @@ struct scoutfs_btree_item { #define SCOUTFS_UUID_BYTES 16 +/* + * The ring fields describe the statically allocated ring log. The + * head and tail indexes are logical 4k blocks offsets inside the ring. + * The head block should contain the seq. + */ struct scoutfs_super_block { struct scoutfs_block_header hdr; __le64 id; @@ -187,6 +274,11 @@ struct scoutfs_super_block { __le64 next_ino; __le64 total_blocks; __le64 free_blocks; + __le64 ring_blkno; + __le64 ring_blocks; + __le64 ring_head_index; + __le64 ring_tail_index; + __le64 ring_head_seq; __le64 buddy_blocks; struct scoutfs_buddy_root buddy_root; struct scoutfs_btree_root btree_root; diff --git a/utils/src/mkfs.c b/utils/src/mkfs.c index 7023b409..fb8fe80c 100644 --- a/utils/src/mkfs.c +++ b/utils/src/mkfs.c @@ -43,278 +43,70 @@ static int write_block(int fd, u64 blkno, struct scoutfs_super_block *super, return 0; } -static u64 first_blkno(struct scoutfs_super_block *super) -{ - return SCOUTFS_BUDDY_BLKNO + le64_to_cpu(super->buddy_blocks); -} - -static u64 last_blkno(struct scoutfs_super_block *super) -{ - return le64_to_cpu(super->total_blocks) - 1; -} - -/* the starting bit offset in the block bitmap of an order's bitmap */ -static int order_off(int order) -{ - if (order == 0) - return 0; - - return (2 * SCOUTFS_BUDDY_ORDER0_BITS) - - (SCOUTFS_BUDDY_ORDER0_BITS / (1 << (order - 1))); -} - -/* the bit offset in the block bitmap of an order's bit */ -static int order_nr(int order, int nr) -{ - return order_off(order) + nr; -} - -static void set_order_nr(struct scoutfs_buddy_block *bud, int order, u16 nr) -{ - u16 first = le16_to_cpu(bud->first_set[order]); - - if (nr <= first) - bud->first_set[order] = cpu_to_le16(nr); -} - -static void clear_order_nr(struct scoutfs_buddy_block *bud, int order, u16 nr) -{ - u16 first = le16_to_cpu(bud->first_set[order]); - int size; - int i; - - if (nr != first) - return; - - if (bud->level) { - for (i = nr + 1; i < SCOUTFS_BUDDY_SLOTS; i++) { - if (le16_to_cpu(bud->slots[i].free_orders) & - (1 << order)) - break; - } - if (i == SCOUTFS_BUDDY_SLOTS) - i = U16_MAX; - - } else { - size = order_off(order + 1); - i = find_next_bit_le(bud->bits, size, - order_nr(order, first) + 1); - if (i >= size) - i = U16_MAX; - else - i -= order_off(order); - } - - bud->first_set[order] = cpu_to_le16(i); -} - -#define for_each_changed_bit(nr, bit, old, new, tmp) \ - for (tmp = old ^ new; \ - tmp && (nr = ffs(tmp) - 1, bit = 1 << nr, 1); \ - tmp ^= bit) - /* - * Set a slot's free_orders value and update first_set for each order - * that it changes. Returns true of the slot's free_orders was changed. + * Figure out how many blocks the ring will need. This goes crazy + * with the variables to make the calculation clear. + * + * XXX just a place holder. The real calculation is more like: + * + * - max size add manifest entries for all segments + * - (some day) allocator entries for all segments + * - ring block header overhead + * - ring block unused tail space overhead + * */ -static int set_slot_free_orders(struct scoutfs_buddy_block *bud, u16 sl, - u16 free_orders) +static u64 calc_ring_blocks(u64 size) { - u16 old = le16_to_cpu(bud->slots[sl].free_orders); - int order; - int tmp; - int bit; + u64 first_seg_blocks; + u64 max_entry_bytes; + u64 total_bytes; + u64 blocks; + u64 segs; - if (old == free_orders) - return 0; + segs = size >> SCOUTFS_SEGMENT_SHIFT; + max_entry_bytes = sizeof(struct scoutfs_ring_add_manifest) + + (2 * SCOUTFS_MAX_KEY_BYTES); + total_bytes = (segs * max_entry_bytes) * 4; + blocks = DIV_ROUND_UP(total_bytes, SCOUTFS_BLOCK_SIZE); - for_each_changed_bit(order, bit, old, free_orders, tmp) { - if (old & bit) - clear_order_nr(bud, order, sl); - else - set_order_nr(bud, order, sl); - } + first_seg_blocks = SCOUTFS_SEGMENT_BLOCKS - + (SCOUTFS_SUPER_BLKNO + SCOUTFS_SUPER_NR); - bud->slots[sl].free_orders = cpu_to_le16(free_orders); - return 1; -} - -static int test_buddy_bit(struct scoutfs_buddy_block *bud, int order, int nr) -{ - return test_bit_le(order_nr(order, nr), bud->bits); -} - -static void set_buddy_bit(struct scoutfs_buddy_block *bud, int order, int nr) -{ - if (!test_and_set_bit_le(order_nr(order, nr), bud->bits)) - set_order_nr(bud, order, nr); -} - -static void clear_buddy_bit(struct scoutfs_buddy_block *bud, int order, int nr) -{ - if (test_and_clear_bit_le(order_nr(order, nr), bud->bits)) - clear_order_nr(bud, order, nr); -} - -/* merge lower orders buddies as we free up to the highest */ -static void free_order_bit(struct scoutfs_buddy_block *bud, int order, int nr) -{ - int i; - - for (i = order; i < SCOUTFS_BUDDY_ORDERS - 2; i++) { - - if (!test_buddy_bit(bud, i, nr ^ 1)) - break; - - clear_buddy_bit(bud, i, nr ^ 1); - nr >>= 1; - } - - set_buddy_bit(bud, i, nr); -} - -static u16 calc_free_orders(struct scoutfs_buddy_block *bud) -{ - u16 free = 0; - int i; - - for (i = 0; i < SCOUTFS_BUDDY_ORDERS; i++) - if (le16_to_cpu(bud->first_set[i]) != U16_MAX) - free |= 1 << i; - - return free; -} - -static void init_buddy_block(struct scoutfs_buddy_block *bud, int level) -{ - int i; - - memset(bud, 0, SCOUTFS_BLOCK_SIZE); - for (i = 0; i < array_size(bud->first_set); i++) - bud->first_set[i] = cpu_to_le16(U16_MAX); - bud->level = level; + return max(first_seg_blocks, blocks); } /* - * Write either the left-most or right-most buddy bitmap leaf in the - * allocator and then ascend writing parent blocks to the root. - * - * If we're writing the left leaf then blk is the first free blk. If - * we're writing the right leaf then blk is the last usable blk. - * - * If we're writing the left leaf then we don't actually write the root - * block. We record the free_orders for the first child block from the - * root block. When we write the right leaf we'll ascend into the root - * block and initialize the free_order of the first slot for the path to - * the left leaf. - * - * We initialize free_orders in all the unused slots so that the kernel - * can try to descend in to them when searching by size and will - * initialize new full blocks blocks. + * Make a new file system by writing: + * - super blocks + * - ring block with manifest entry + * - segment with root inode */ -static int write_buddy_blocks(int fd, struct scoutfs_super_block *super, - struct buddy_info *binf, - struct scoutfs_buddy_block *bud, u64 blk, - int left, u16 *free_orders) -{ - u64 blkno; - int level; - int first; - int last; - int ret; - u16 free; - u16 full; - int sl; - int i; - - if (left) { - first = blk; - last = SCOUTFS_BUDDY_ORDER0_BITS - 1; - } else { - first = 0; - last = blk % SCOUTFS_BUDDY_ORDER0_BITS; - } - - /* write the leaf block */ - level = 0; - init_buddy_block(bud, level); - for (i = first; i <= last; i++) - free_order_bit(bud, 0, i); - - blk = blk / SCOUTFS_BUDDY_ORDER0_BITS; - blkno = binf->blknos[level] + (blk * 2); - - ret = write_block(fd, blkno, super, &bud->hdr); - if (ret) - return ret; - - free = calc_free_orders(bud); - full = SCOUTFS_BUDDY_ORDER0_BITS; - - /* write parents, stopping before root if left */ - while (++level < (left ? binf->height - 1 : binf->height)) { - - sl = blk % SCOUTFS_BUDDY_SLOTS; - blk = blk / SCOUTFS_BUDDY_SLOTS; - blkno = binf->blknos[level] + (blk * 2); - - init_buddy_block(bud, level); - - /* set full until right spine, 0th in root from left */ - for (i = 0; i < sl; i++) - set_slot_free_orders(bud, i, full); - - if (!left && level == (binf->height - 1)) { - set_slot_free_orders(bud, 0, *free_orders); - bud->slots[0].seq = super->hdr.seq; - } - - set_slot_free_orders(bud, sl, free); - bud->slots[sl].seq = super->hdr.seq; - - /* init full slots in full parents down the left spine */ - for (i = sl; left && i < SCOUTFS_BUDDY_SLOTS; i++) - set_slot_free_orders(bud, i, full); - - ret = write_block(fd, blkno, super, &bud->hdr); - if (ret) - return ret; - - free = calc_free_orders(bud); - } - - *free_orders = free; - - return 0; -} - static int write_new_fs(char *path, int fd) { struct scoutfs_super_block *super; + struct scoutfs_inode_key *ikey; struct scoutfs_inode *inode; - struct scoutfs_btree_block *bt; - struct scoutfs_btree_item *item; - struct scoutfs_key root_key; - struct buddy_info binf; + struct scoutfs_segment_block *sblk; + struct scoutfs_ring_block *ring; + struct scoutfs_segment_item *item; + struct scoutfs_ring_add_manifest *am; struct timeval tv; char uuid_str[37]; unsigned int i; u64 limit; u64 size; - u64 blkno; - u64 count; u64 total_blocks; - u16 free_orders; - void *buf; + u64 ring_blocks; int ret; gettimeofday(&tv, NULL); - buf = malloc(SCOUTFS_BLOCK_SIZE); - super = malloc(SCOUTFS_BLOCK_SIZE); - if (!buf || !super) { + super = calloc(1, SCOUTFS_BLOCK_SIZE); + ring = calloc(1, SCOUTFS_BLOCK_SIZE); + sblk = calloc(1, SCOUTFS_SEGMENT_SIZE); + if (!super || !ring || !sblk) { ret = -errno; - fprintf(stderr, "failed to allocate a block: %s (%d)\n", + fprintf(stderr, "failed to allocate block mem: %s (%d)\n", strerror(errno), errno); goto out; } @@ -326,14 +118,16 @@ static int write_new_fs(char *path, int fd) goto out; } - /* the block limit is totally arbitrary */ + /* require space for one segment */ + limit = SCOUTFS_SEGMENT_SIZE * 2; + if (size < limit) { + fprintf(stderr, "%llu byte device too small for min %llu byte fs\n", + size, limit); + goto out; + } + total_blocks = size / SCOUTFS_BLOCK_SIZE; - - buddy_init(&binf, total_blocks); - - root_key.inode = cpu_to_le64(SCOUTFS_ROOT_INO); - root_key.type = SCOUTFS_INODE_KEY; - root_key.offset = 0; + ring_blocks = calc_ring_blocks(size); /* first initialize the super so we can use it to build structures */ memset(super, 0, SCOUTFS_BLOCK_SIZE); @@ -343,34 +137,28 @@ static int write_new_fs(char *path, int fd) uuid_generate(super->uuid); super->next_ino = cpu_to_le64(SCOUTFS_ROOT_INO + 1); super->total_blocks = cpu_to_le64(total_blocks); - super->buddy_blocks = cpu_to_le64(binf.buddy_blocks); + super->ring_blkno = cpu_to_le64(SCOUTFS_SUPER_BLKNO + 2); + super->ring_blocks = cpu_to_le64(ring_blocks); + super->ring_head_seq = cpu_to_le64(1); - /* require space for two leaf blocks for writing left/right paths */ - count = last_blkno(super) - first_blkno(super) + 1; - limit = (SCOUTFS_BUDDY_ORDER0_BITS * 2); - if (count < limit) { - fprintf(stderr, "%llu byte device only has room for %llu %u byte fs blocks, needs at least %llu fs blocks\n", - size, count, SCOUTFS_BLOCK_SIZE, limit); - goto out; - } + /* write seg with root inode */ + sblk->segno = cpu_to_le64(1); + sblk->max_seq = cpu_to_le64(1); + sblk->nr_items = cpu_to_le32(1); - blkno = first_blkno(super); + item = (void *)(sblk + 1); + ikey = (void *)(item + 1); + inode = (void *)(ikey + 1); - /* write a btree leaf root inode item */ - memset(buf, 0, SCOUTFS_BLOCK_SIZE); - bt = buf; - bt->nr_items = cpu_to_le16(1); - bt->free_end = cpu_to_le16(SCOUTFS_BLOCK_SIZE - sizeof(*item) - - sizeof(*inode)); - bt->free_reclaim = 0; - bt->item_offs[0] = bt->free_end; - - item = (void *)bt + le16_to_cpu(bt->free_end); item->seq = cpu_to_le64(1); - item->key = root_key; + item->key_off = cpu_to_le32((long)ikey - (long)sblk); + item->val_off = cpu_to_le32((long)inode - (long)sblk); + item->key_len = cpu_to_le16(sizeof(struct scoutfs_inode_key)); item->val_len = cpu_to_le16(sizeof(struct scoutfs_inode)); - inode = (void *)(item + 1); + ikey->type = SCOUTFS_INODE_KEY; + ikey->ino = cpu_to_be64(SCOUTFS_ROOT_INO); + inode->nlink = cpu_to_le32(2); inode->mode = cpu_to_le32(0755 | 0040000); inode->atime.sec = cpu_to_le64(tv.tv_sec); @@ -380,38 +168,35 @@ static int write_new_fs(char *path, int fd) inode->mtime.sec = inode->atime.sec; inode->mtime.nsec = inode->atime.nsec; - ret = write_block(fd, blkno, super, &bt->hdr); + ret = pwrite(fd, sblk, SCOUTFS_SEGMENT_SIZE, + 1 << SCOUTFS_SEGMENT_SHIFT); + if (ret != SCOUTFS_SEGMENT_SIZE) { + ret = -EIO; + goto out; + } + + /* write the ring block with the manifest entry pointing to seg */ + ring->nr_entries = cpu_to_le32(1); + + am = (void *)ring->entries; + am->eh.type = SCOUTFS_RING_ADD_MANIFEST; + am->eh.len = cpu_to_le16(sizeof(struct scoutfs_ring_add_manifest)); + am->segno = cpu_to_le64(1); + am->seq = cpu_to_le64(1); + am->first_key_len = cpu_to_le16(sizeof(struct scoutfs_inode_key)); + am->last_key_len = cpu_to_le16(sizeof(struct scoutfs_inode_key)); + am->level = 1; + ikey = (void *)(am + 1); + ikey->type = SCOUTFS_INODE_KEY; + ikey->ino = cpu_to_be64(SCOUTFS_ROOT_INO); + ikey = (void *)(ikey + 1); + ikey->type = SCOUTFS_INODE_KEY; + ikey->ino = cpu_to_be64(SCOUTFS_ROOT_INO); + + ret = write_block(fd, le64_to_cpu(super->ring_blkno), super, + &ring->hdr); if (ret) goto out; - /* blkno is now first free */ - blkno++; - - /* the super references the btree block */ - super->btree_root.height = 1; - super->btree_root.ref.blkno = bt->hdr.blkno; - super->btree_root.ref.seq = bt->hdr.seq; - - /* free_blocks reflects the fs blocks, not buddy blocks */ - super->free_blocks = cpu_to_le64(total_blocks - blkno); - - /* write left-most buddy block and all full parents, not root */ - ret = write_buddy_blocks(fd, super, &binf, buf, - blkno - first_blkno(super), - 1, &free_orders); - if (ret) - goto out; - - /* write right-most buddy and parents and the root */ - ret = write_buddy_blocks(fd, super, &binf, buf, - last_blkno(super) - first_blkno(super), - 0, &free_orders); - if (ret) - goto out; - - /* the super references the buddy leaf block */ - super->buddy_root.height = binf.height; - super->buddy_root.slot.seq = super->hdr.seq; - super->buddy_root.slot.free_orders = cpu_to_le16(free_orders); /* write the two super blocks */ for (i = 0; i < SCOUTFS_SUPER_NR; i++) { @@ -433,18 +218,20 @@ static int write_new_fs(char *path, int fd) printf("Created scoutfs filesystem:\n" " total blocks: %llu\n" - " buddy blocks: %llu\n" + " ring blocks: %llu\n" " fsid: %llx\n" " uuid: %s\n", - total_blocks, le64_to_cpu(super->buddy_blocks), - le64_to_cpu(super->hdr.fsid), uuid_str); + total_blocks, ring_blocks, le64_to_cpu(super->hdr.fsid), + uuid_str); ret = 0; out: if (super) free(super); - if (buf) - free(buf); + if (ring) + free(ring); + if (sblk) + free(sblk); return ret; } From eb4baa88f5790d408990a93abb2e9e678541cb80 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Sat, 3 Dec 2016 17:58:53 -0800 Subject: [PATCH 065/235] Print LSM structures Print segments and their items instead of btree blocks. Signed-off-by: Zach Brown --- utils/src/print.c | 301 ++++++++++++++++++++++++---------------------- 1 file changed, 160 insertions(+), 141 deletions(-) diff --git a/utils/src/print.c b/utils/src/print.c index 3ee17643..5baba80d 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -16,6 +16,7 @@ #include "cmd.h" #include "crc.h" #include "buddy.h" +#include "bitops.h" /* XXX maybe these go somewhere */ #define SKF "%llu.%u.%llu" @@ -42,6 +43,27 @@ static void *read_block(int fd, u64 blkno) return buf; } +static void *read_segment(int fd, u64 segno) +{ + ssize_t ret; + void *buf; + + buf = malloc(SCOUTFS_SEGMENT_SIZE); + if (!buf) + return NULL; + + ret = pread(fd, buf, SCOUTFS_SEGMENT_SIZE, + segno << SCOUTFS_SEGMENT_SHIFT); + if (ret != SCOUTFS_SEGMENT_SIZE) { + fprintf(stderr, "read segno %llu returned %zd: %s (%d)\n", + segno, ret, strerror(errno), errno); + free(buf); + buf = NULL; + } + + return buf; +} + static void print_block_header(struct scoutfs_block_header *hdr) { u32 crc = crc_block(hdr); @@ -57,13 +79,17 @@ static void print_block_header(struct scoutfs_block_header *hdr) le64_to_cpu(hdr->seq), le64_to_cpu(hdr->blkno)); } -static void print_inode(struct scoutfs_inode *inode) +static void print_inode(void *key, void *val) { - printf(" inode: size: %llu blocks: %llu lctr: %llu nlink: %u\n" - " uid: %u gid: %u mode: 0%o rdev: 0x%x\n" - " salt: 0x%x data_version %llu\n" - " atime: %llu.%08u ctime: %llu.%08u\n" - " mtime: %llu.%08u\n", + struct scoutfs_inode_key *ikey = key; + struct scoutfs_inode *inode = val; + + printf(" inode: ino %llu size %llu blocks %llu lctr %llu nlink %u\n" + " uid %u gid %u mode 0%o rdev 0x%x\n" + " salt 0x%x data_version %llu\n" + " atime %llu.%08u ctime %llu.%08u\n" + " mtime %llu.%08u\n", + be64_to_cpu(ikey->ino), le64_to_cpu(inode->size), le64_to_cpu(inode->blocks), le64_to_cpu(inode->link_counter), le32_to_cpu(inode->nlink), le32_to_cpu(inode->uid), @@ -78,6 +104,8 @@ static void print_inode(struct scoutfs_inode *inode) le32_to_cpu(inode->mtime.nsec)); } +#if 0 + static void print_xattr(struct scoutfs_xattr *xat) { /* XXX check lengths */ @@ -133,148 +161,149 @@ static void print_extent(struct scoutfs_key *key, le64_to_cpu(ext->len), EXT_FLAG(SCOUTFS_EXTENT_FLAG_OFFLINE, ext->flags, "OFF")); } +#endif -static void print_block_ref(struct scoutfs_block_ref *ref) +typedef void (*print_func_t)(void *key, void *val); + +static print_func_t printers[] = { + [SCOUTFS_INODE_KEY] = print_inode, +}; + +static void print_item(struct scoutfs_segment_block *sblk, + struct scoutfs_segment_item *item) { - printf(" ref: blkno %llu seq %llu\n", - le64_to_cpu(ref->blkno), le64_to_cpu(ref->seq)); + void *key = (char *)sblk + le32_to_cpu(item->key_off); + void *val = (char *)sblk + le32_to_cpu(item->val_off); + __u8 type = *(__u8 *)key; + + if (type < array_size(printers) && printers[type]) + printers[type](key, val); + else + printf(" unknown!\n"); } -static void print_btree_val(struct scoutfs_btree_item *item, u8 level) +static int print_segment(int fd, u64 segno) { - - if (level) { - print_block_ref((void *)item->val); - return; - } - - switch(item->key.type) { - case SCOUTFS_INODE_KEY: - print_inode((void *)item->val); - break; - case SCOUTFS_XATTR_KEY: - print_xattr((void *)item->val); - break; - case SCOUTFS_XATTR_VAL_HASH_KEY: - print_xattr_val_hash((void *)item->val); - break; - case SCOUTFS_DIRENT_KEY: - print_dirent((void *)item->val, le16_to_cpu(item->val_len)); - break; - case SCOUTFS_LINK_BACKREF_KEY: - print_link_backref((void *)item->val, - le16_to_cpu(item->val_len)); - break; - case SCOUTFS_SYMLINK_KEY: - print_symlink((void *)item->val, le16_to_cpu(item->val_len)); - break; - case SCOUTFS_EXTENT_KEY: - print_extent(&item->key, (void *)item->val); - break; - } -} - -static int print_btree_block(int fd, __le64 blkno, u8 level) -{ - struct scoutfs_btree_item *item; - struct scoutfs_btree_block *bt; - struct scoutfs_block_ref *ref; - unsigned int nr; - int ret = 0; - int err; + struct scoutfs_segment_block *sblk; + struct scoutfs_segment_item *item; int i; - bt = read_block(fd, le64_to_cpu(blkno)); - if (!bt) + sblk = read_segment(fd, segno); + if (!sblk) return -ENOMEM; - nr = le16_to_cpu(bt->nr_items); + printf("segment segno %llu\n", segno); +// print_block_header(&sblk->hdr); - printf("btree blkno %llu\n", le64_to_cpu(blkno)); - print_block_header(&bt->hdr); - printf(" free_end %u free_reclaim %u nr_items %u\n", - le16_to_cpu(bt->free_end), le16_to_cpu(bt->free_reclaim), nr); - - for (i = 0; i < nr; i++) { - item = (void *)bt + le16_to_cpu(bt->item_offs[i]); - - printf(" [%u] off %u: key "SKF" seq %llu val_len %u\n", - i, le16_to_cpu(bt->item_offs[i]), - SKA(&item->key), le64_to_cpu(item->seq), + item = (void *)(sblk + 1); + for (i = 0; i < le32_to_cpu(sblk->nr_items); i++) { + printf(" [%u]: seq %llu key_off %u val_off %u key_len %u " + "val_len %u\n", + i, + le64_to_cpu(item->seq), + le32_to_cpu(item->key_off), + le32_to_cpu(item->val_off), + le16_to_cpu(item->key_len), le16_to_cpu(item->val_len)); - print_btree_val(item, level); + print_item(sblk, item); + + /* XXX item has to skip holes at the end of blocks */ + item = (void *)(item + 1); } - for (i = 0; level && i < nr; i++) { - item = (void *)bt + le16_to_cpu(bt->item_offs[i]); + free(sblk); - ref = (void *)item->val; - err = print_btree_block(fd, ref->blkno, level - 1); + return 0; +} + +static int print_segments(int fd, unsigned long *seg_map, u64 total_segs) +{ + int ret = 0; + int i = 0; + int err; + + for (i = 0; + (i = find_next_bit_le(seg_map, total_segs, i)) < total_segs; + i++) { + + err = print_segment(fd, i); if (err && !ret) ret = err; + i++; } - free(bt); - return ret; } -/* print populated buddy blocks */ -static int print_buddy_block(int fd, struct buddy_info *binf, - int level, u64 base, u8 off) +static int print_ring_block(int fd, unsigned long *seg_map, u64 blkno) { - struct scoutfs_buddy_block *bud; - struct scoutfs_buddy_slot *slot; - int ret = 0; - u64 blkno; - u16 first; - int err; + struct scoutfs_ring_entry_header *eh; + struct scoutfs_ring_add_manifest *am; + struct scoutfs_ring_block *ring; + u32 off; int i; - blkno = binf->blknos[level] + base + off; - bud = read_block(fd, blkno); - if (!bud) + ring = read_block(fd, blkno); + if (!ring) return -ENOMEM; - printf("buddy blkno %llu\n", blkno); - print_block_header(&bud->hdr); - printf(" first_set:"); - for (i = 0; i < SCOUTFS_BUDDY_ORDERS; i++) { - first = le16_to_cpu(bud->first_set[i]); - if (first == U16_MAX) - printf(" -"); - else - printf(" %u", first); - } - printf("\n"); - printf(" level: %u\n", bud->level); + printf("ring blkno %llu\n", blkno); + print_block_header(&ring->hdr); - for (i = 0; level && i < SCOUTFS_BUDDY_SLOTS; i++) { - slot = &bud->slots[i]; + eh = ring->entries; + for (i = 0; i < le32_to_cpu(ring->nr_entries); i++) { + off = (char *)eh - (char *)ring; + printf(" [%u]: type %u len %u\n", + off, eh->type, le16_to_cpu(eh->len)); - if (slot->seq == 0) - continue; + switch(eh->type) { + case SCOUTFS_RING_ADD_MANIFEST: + am = (void *)eh; + printf(" add ment: segno %llu seq %llu " + "first_len %u last_len %u level %u\n", + le64_to_cpu(am->segno), + le64_to_cpu(am->seq), + le16_to_cpu(am->first_key_len), + le16_to_cpu(am->last_key_len), + am->level); - printf(" slots[%u]: seq %llu free_orders: %x blkno_off %u\n", - i, le64_to_cpu(slot->seq), - le16_to_cpu(slot->free_orders), slot->blkno_off); + /* XXX verify, 'int nr' limits segno precision */ + set_bit_le(le64_to_cpu(am->segno), seg_map); + break; + } } - for (i = 0; level && i < SCOUTFS_BUDDY_SLOTS; i++) { - slot = &bud->slots[i]; + free(ring); - if (slot->seq == 0) - continue; + return 0; +} - err = print_buddy_block(fd, binf, level - 1, - (base * SCOUTFS_BUDDY_SLOTS) + (i * 2), - slot->blkno_off); +static int print_ring_blocks(int fd, struct scoutfs_super_block *super, + unsigned long *seg_map) +{ + int ret = 0; + u64 blkno; + u16 index; + u16 tail; + int err; + + index = le64_to_cpu(super->ring_head_index); + tail = le64_to_cpu(super->ring_tail_index); + + for(;;) { + blkno = le64_to_cpu(super->ring_blkno) + index; + + err = print_ring_block(fd, seg_map, blkno); if (err && !ret) ret = err; - } - free(bud); + if (index == tail) + break; + + if (++index == le64_to_cpu(super->ring_blocks)) + index = 0; + }; return ret; } @@ -283,9 +312,11 @@ static int print_super_blocks(int fd) { struct scoutfs_super_block *super; struct scoutfs_super_block recent = { .hdr.seq = 0 }; + unsigned long *seg_map; char uuid_str[37]; + u64 total_segs; + u64 longs; int ret = 0; - int err; int i; for (i = 0; i < SCOUTFS_SUPER_NR; i++) { @@ -299,21 +330,16 @@ 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(" next_ino %llu total_blocks %llu buddy_blocks %llu\n" - " free_blocks %llu\n", + printf(" next_ino %llu total_blocks %llu free_blocks %llu\n" + " ring_blkno %llu ring_blocks %llu ring_head %llu\n" + " ring_tail %llu\n", le64_to_cpu(super->next_ino), le64_to_cpu(super->total_blocks), - le64_to_cpu(super->buddy_blocks), - le64_to_cpu(super->free_blocks)); - printf(" buddy_root: height %u seq %llu free_orders %x blkno_off %u\n", - super->buddy_root.height, - le64_to_cpu(super->buddy_root.slot.seq), - le16_to_cpu(super->buddy_root.slot.free_orders), - super->buddy_root.slot.blkno_off); - printf(" btree_root: height %u seq %llu blkno %llu\n", - super->btree_root.height, - le64_to_cpu(super->btree_root.ref.seq), - le64_to_cpu(super->btree_root.ref.blkno)); + le64_to_cpu(super->free_blocks), + le64_to_cpu(super->ring_blkno), + le64_to_cpu(super->ring_blocks), + le64_to_cpu(super->ring_head_index), + le64_to_cpu(super->ring_tail_index)); if (le64_to_cpu(super->hdr.seq) > le64_to_cpu(recent.hdr.seq)) memcpy(&recent, super, sizeof(recent)); @@ -323,24 +349,17 @@ static int print_super_blocks(int fd) super = &recent; + /* XXX :P */ + total_segs = le64_to_cpu(super->total_blocks) / SCOUTFS_SEGMENT_BLOCKS; + longs = DIV_ROUND_UP(total_segs, BITS_PER_LONG); + seg_map = calloc(longs, sizeof(unsigned long)); + if (!seg_map) + return -ENOMEM; - if (super->buddy_root.height) { - struct buddy_info binf; + ret = print_ring_blocks(fd, super, seg_map) ?: + print_segments(fd, seg_map, total_segs); - buddy_init(&binf, le64_to_cpu(super->total_blocks)); - err = print_buddy_block(fd, &binf, - super->buddy_root.height - 1, 0, - super->buddy_root.slot.blkno_off); - if (err && !ret) - ret = err; - } - - 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; - } + free(seg_map); return ret; } From 818e1496436d8d56f9b8fd6dd3680d3504f31ee1 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Wed, 7 Dec 2016 10:20:03 -0800 Subject: [PATCH 066/235] Update mkfs and print for lsm writing Adapt mkfs and print for the format changes made to support writing segments. Signed-off-by: Zach Brown --- utils/src/format.h | 80 ++++++++++++++++++++++++++++++++++------------ utils/src/item.c | 57 +++++++++++++++++++++++++++++++++ utils/src/item.h | 22 +++++++++++++ utils/src/mkfs.c | 51 ++++++++++++++++++++--------- utils/src/print.c | 63 +++++++++++++++++++++--------------- 5 files changed, 213 insertions(+), 60 deletions(-) create mode 100644 utils/src/item.c create mode 100644 utils/src/item.h diff --git a/utils/src/format.h b/utils/src/format.h index dd4afe48..eb19bd6a 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -58,7 +58,8 @@ struct scoutfs_ring_entry_header { __le16 len; } __packed; -#define SCOUTFS_RING_ADD_MANIFEST 1 +#define SCOUTFS_RING_ADD_MANIFEST 1 +#define SCOUTFS_RING_ADD_ALLOC 2 struct scoutfs_ring_add_manifest { struct scoutfs_ring_entry_header eh; @@ -70,26 +71,55 @@ struct scoutfs_ring_add_manifest { /* first and last key bytes */ } __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) + +/* + * The bits need to be aligned so that the host can use native long + * bitops on the bits in memory. + */ +struct scoutfs_ring_alloc_region { + struct scoutfs_ring_entry_header eh; + __le64 index; + __u8 pad[5]; + __le64 bits[SCOUTFS_ALLOC_REGION_BITS / 64]; +} __packed; + /* * This is absurdly huge. If there was only ever 1 item per segment and * 2^64 items the tree could get this deep. */ #define SCOUTFS_MANIFEST_MAX_LEVEL 20 +/* + * The packed entries in the block are terminated by a header with a 0 length. + */ struct scoutfs_ring_block { struct scoutfs_block_header hdr; - __le32 nr_entries; struct scoutfs_ring_entry_header entries[0]; } __packed; +/* + * We really want these to be a power of two size so that they're naturally + * aligned. This ensures that they won't cross page boundaries and we + * can use pointers to them in the page vecs that make up segments without + * funny business. + * + * We limit segment sizes to 8 megs (23 bits) and value lengths to 512 bytes + * (9 bits). The item offsets and lengths then take up 64 bits. + * + * We then operate on the items in on-stack nice native structs. + */ struct scoutfs_segment_item { __le64 seq; - __le32 key_off; - __le32 val_off; - __le16 key_len; - __le16 val_len; + __le32 key_off_len; + __le32 val_off_len; } __packed; +#define SCOUTFS_SEGMENT_ITEM_OFF_SHIFT 9 +#define SCOUTFS_SEGMENT_ITEM_LEN_MASK ((1 << SCOUTFS_SEGMENT_ITEM_OFF_SHIFT)-1) + /* * Each large segment starts with a segment block that describes the * rest of the blocks that make up the segment. @@ -100,20 +130,12 @@ struct scoutfs_segment_block { __le64 segno; __le64 max_seq; __le32 nr_items; - /* item array with gaps so they don't cross 4k blocks */ + __le32 _moar_pads; + struct scoutfs_segment_item items[0]; /* packed keys */ /* packed vals */ } __packed; -/* the first block in the segment has the header and items */ -#define SCOUTFS_SEGMENT_FIRST_BLOCK_ITEMS \ - ((SCOUTFS_BLOCK_SIZE - sizeof(struct scoutfs_segment_block)) / \ - sizeof(struct scoutfs_segment_item)) - -/* the rest of the header blocks are full of items */ -#define SCOUTFS_SEGMENT_ITEMS_PER_BLOCK \ - (SCOUTFS_BLOCK_SIZE / sizeof(struct scoutfs_segment_item)) - /* * Block references include the sequence number so that we can detect * readers racing with writers and so that we can tell that we don't @@ -188,18 +210,34 @@ struct scoutfs_key { #define SCOUTFS_XATTR_NAME_HASH_KEY 3 #define SCOUTFS_XATTR_VAL_HASH_KEY 4 #define SCOUTFS_DIRENT_KEY 5 -#define SCOUTFS_LINK_BACKREF_KEY 6 -#define SCOUTFS_SYMLINK_KEY 7 -#define SCOUTFS_EXTENT_KEY 8 -#define SCOUTFS_ORPHAN_KEY 9 +#define SCOUTFS_READDIR_KEY 6 +#define SCOUTFS_LINK_BACKREF_KEY 7 +#define SCOUTFS_SYMLINK_KEY 8 +#define SCOUTFS_EXTENT_KEY 9 +#define SCOUTFS_ORPHAN_KEY 10 #define SCOUTFS_MAX_ITEM_LEN 512 +/* value is struct scoutfs_inode */ struct scoutfs_inode_key { __u8 type; __be64 ino; } __packed; +/* value is struct scoutfs_dirent without the name */ +struct scoutfs_dirent_key { + __u8 type; + __be64 ino; + __u8 name[0]; +} __packed; + +/* value is struct scoutfs_dirent with the name */ +struct scoutfs_readdir_key { + __u8 type; + __be64 ino; + __be64 pos; +} __packed; + struct scoutfs_btree_root { u8 height; struct scoutfs_block_ref ref; @@ -272,6 +310,8 @@ struct scoutfs_super_block { __le64 id; __u8 uuid[SCOUTFS_UUID_BYTES]; __le64 next_ino; + __le64 alloc_uninit; + __le64 total_segs; __le64 total_blocks; __le64 free_blocks; __le64 ring_blkno; diff --git a/utils/src/item.c b/utils/src/item.c new file mode 100644 index 00000000..b6db8b15 --- /dev/null +++ b/utils/src/item.c @@ -0,0 +1,57 @@ +#include +#include + +#include "sparse.h" +#include "util.h" +#include "format.h" +#include "item.h" + +/* utils uses bit contiguous allocations */ +static void *off_ptr(struct scoutfs_segment_block *sblk, u32 off) +{ + return (char *)sblk + off; +} + +static u32 pos_off(struct scoutfs_segment_block *sblk, u32 pos) +{ + return offsetof(struct scoutfs_segment_block, items[pos]); +} + +static void *pos_ptr(struct scoutfs_segment_block *sblk, u32 pos) +{ + return off_ptr(sblk, pos_off(sblk, pos)); +} + +void load_item(struct scoutfs_segment_block *sblk, u32 pos, + struct native_item *item) +{ + struct scoutfs_segment_item *sitem = pos_ptr(sblk, pos); + u32 packed; + + item->seq = le64_to_cpu(sitem->seq); + + packed = le32_to_cpu(sitem->key_off_len); + item->key_off = packed >> SCOUTFS_SEGMENT_ITEM_OFF_SHIFT; + item->key_len = packed & SCOUTFS_SEGMENT_ITEM_LEN_MASK; + + packed = le32_to_cpu(sitem->val_off_len); + item->val_off = packed >> SCOUTFS_SEGMENT_ITEM_OFF_SHIFT; + item->val_len = packed & SCOUTFS_SEGMENT_ITEM_LEN_MASK; +} + +void store_item(struct scoutfs_segment_block *sblk, u32 pos, + struct native_item *item) +{ + struct scoutfs_segment_item *sitem = pos_ptr(sblk, pos); + u32 packed; + + sitem->seq = cpu_to_le64(item->seq); + + packed = (item->key_off << SCOUTFS_SEGMENT_ITEM_OFF_SHIFT) | + (item->key_len & SCOUTFS_SEGMENT_ITEM_LEN_MASK); + sitem->key_off_len = cpu_to_le32(packed); + + packed = (item->val_off << SCOUTFS_SEGMENT_ITEM_OFF_SHIFT) | + (item->val_len & SCOUTFS_SEGMENT_ITEM_LEN_MASK); + sitem->val_off_len = cpu_to_le32(packed); +} diff --git a/utils/src/item.h b/utils/src/item.h new file mode 100644 index 00000000..7c307eee --- /dev/null +++ b/utils/src/item.h @@ -0,0 +1,22 @@ +#ifndef _ITEM_H_ +#define _ITEM_H_ + +/* + * The persistent item fields that are stored in the segment are packed + * with funny precision. We translate those to and from a much more + * natural native representation of the fields. + */ +struct native_item { + u64 seq; + u32 key_off; + u32 val_off; + u16 key_len; + u16 val_len; +}; + +void load_item(struct scoutfs_segment_block *sblk, u32 pos, + struct native_item *item); +void store_item(struct scoutfs_segment_block *sblk, u32 pos, + struct native_item *item); + +#endif diff --git a/utils/src/mkfs.c b/utils/src/mkfs.c index fb8fe80c..dce26246 100644 --- a/utils/src/mkfs.c +++ b/utils/src/mkfs.c @@ -19,6 +19,7 @@ #include "dev.h" #include "bitops.h" #include "buddy.h" +#include "item.h" /* * Update the block's header and write it out. @@ -88,8 +89,9 @@ static int write_new_fs(char *path, int fd) struct scoutfs_inode *inode; struct scoutfs_segment_block *sblk; struct scoutfs_ring_block *ring; - struct scoutfs_segment_item *item; struct scoutfs_ring_add_manifest *am; + struct scoutfs_ring_alloc_region *reg; + struct native_item item; struct timeval tv; char uuid_str[37]; unsigned int i; @@ -97,6 +99,8 @@ static int write_new_fs(char *path, int fd) u64 size; u64 total_blocks; u64 ring_blocks; + u64 total_segs; + u64 first_segno; int ret; gettimeofday(&tv, NULL); @@ -127,6 +131,7 @@ static int write_new_fs(char *path, int fd) } total_blocks = size / SCOUTFS_BLOCK_SIZE; + total_segs = size / SCOUTFS_SEGMENT_SIZE; ring_blocks = calc_ring_blocks(size); /* first initialize the super so we can use it to build structures */ @@ -137,24 +142,30 @@ static int write_new_fs(char *path, int fd) uuid_generate(super->uuid); super->next_ino = cpu_to_le64(SCOUTFS_ROOT_INO + 1); super->total_blocks = cpu_to_le64(total_blocks); + super->total_segs = cpu_to_le64(total_segs); + super->alloc_uninit = cpu_to_le64(SCOUTFS_ALLOC_REGION_BITS); super->ring_blkno = cpu_to_le64(SCOUTFS_SUPER_BLKNO + 2); super->ring_blocks = cpu_to_le64(ring_blocks); super->ring_head_seq = cpu_to_le64(1); + first_segno = DIV_ROUND_UP(le64_to_cpu(super->ring_blkno) + + le64_to_cpu(super->ring_blocks), + SCOUTFS_SEGMENT_BLOCKS); + /* write seg with root inode */ - sblk->segno = cpu_to_le64(1); + sblk->segno = cpu_to_le64(first_segno); sblk->max_seq = cpu_to_le64(1); sblk->nr_items = cpu_to_le32(1); - item = (void *)(sblk + 1); - ikey = (void *)(item + 1); + ikey = (void *)&sblk->items[1]; inode = (void *)(ikey + 1); - item->seq = cpu_to_le64(1); - item->key_off = cpu_to_le32((long)ikey - (long)sblk); - item->val_off = cpu_to_le32((long)inode - (long)sblk); - item->key_len = cpu_to_le16(sizeof(struct scoutfs_inode_key)); - item->val_len = cpu_to_le16(sizeof(struct scoutfs_inode)); + item.seq = 1; + item.key_off = (long)ikey - (long)sblk; + item.val_off = (long)inode - (long)sblk; + item.key_len = sizeof(struct scoutfs_inode_key); + item.val_len = sizeof(struct scoutfs_inode); + store_item(sblk, 0, &item); ikey->type = SCOUTFS_INODE_KEY; ikey->ino = cpu_to_be64(SCOUTFS_ROOT_INO); @@ -169,19 +180,18 @@ static int write_new_fs(char *path, int fd) inode->mtime.nsec = inode->atime.nsec; ret = pwrite(fd, sblk, SCOUTFS_SEGMENT_SIZE, - 1 << SCOUTFS_SEGMENT_SHIFT); + first_segno << SCOUTFS_SEGMENT_SHIFT); if (ret != SCOUTFS_SEGMENT_SIZE) { ret = -EIO; goto out; } - /* write the ring block with the manifest entry pointing to seg */ - ring->nr_entries = cpu_to_le32(1); - + /* a single manifest entry points to the single segment */ am = (void *)ring->entries; am->eh.type = SCOUTFS_RING_ADD_MANIFEST; - am->eh.len = cpu_to_le16(sizeof(struct scoutfs_ring_add_manifest)); - am->segno = cpu_to_le64(1); + am->eh.len = cpu_to_le16(sizeof(struct scoutfs_ring_add_manifest) + + (2 * sizeof(struct scoutfs_inode_key))); + am->segno = sblk->segno; am->seq = cpu_to_le64(1); am->first_key_len = cpu_to_le16(sizeof(struct scoutfs_inode_key)); am->last_key_len = cpu_to_le16(sizeof(struct scoutfs_inode_key)); @@ -193,6 +203,17 @@ static int write_new_fs(char *path, int fd) ikey->type = SCOUTFS_INODE_KEY; ikey->ino = cpu_to_be64(SCOUTFS_ROOT_INO); + /* a single alloc region records the first two segs as allocated */ + reg = (void *)am + le16_to_cpu(am->eh.len); + reg->eh.type = SCOUTFS_RING_ADD_ALLOC; + reg->eh.len = cpu_to_le16(sizeof(struct scoutfs_ring_alloc_region)); + /* initial super, ring, and first seg are all allocated */ + memset(reg->bits, 0xff, sizeof(reg->bits)); + for (i = 0; i <= first_segno; i++) + clear_bit_le(i, reg->bits); + + /* block is already zeroed and so contains a 0 len terminating header */ + ret = write_block(fd, le64_to_cpu(super->ring_blkno), super, &ring->hdr); if (ret) diff --git a/utils/src/print.c b/utils/src/print.c index 5baba80d..ac562911 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -17,6 +17,7 @@ #include "crc.h" #include "buddy.h" #include "bitops.h" +#include "item.h" /* XXX maybe these go somewhere */ #define SKF "%llu.%u.%llu" @@ -169,12 +170,23 @@ static print_func_t printers[] = { [SCOUTFS_INODE_KEY] = print_inode, }; -static void print_item(struct scoutfs_segment_block *sblk, - struct scoutfs_segment_item *item) +static void print_item(struct scoutfs_segment_block *sblk, u32 pos) { - void *key = (char *)sblk + le32_to_cpu(item->key_off); - void *val = (char *)sblk + le32_to_cpu(item->val_off); - __u8 type = *(__u8 *)key; + struct native_item item; + void *key; + void *val; + __u8 type; + + load_item(sblk, pos, &item); + + key = (char *)sblk + item.key_off; + val = (char *)sblk + item.val_off; + type = *(__u8 *)key; + + printf(" [%u]: seq %llu key_off %u val_off %u key_len %u " + "val_len %u\n", + pos, item.seq, item.key_off, item.val_off, item.key_len, + item.val_len); if (type < array_size(printers) && printers[type]) printers[type](key, val); @@ -185,7 +197,6 @@ static void print_item(struct scoutfs_segment_block *sblk, static int print_segment(int fd, u64 segno) { struct scoutfs_segment_block *sblk; - struct scoutfs_segment_item *item; int i; sblk = read_segment(fd, segno); @@ -195,22 +206,8 @@ static int print_segment(int fd, u64 segno) printf("segment segno %llu\n", segno); // print_block_header(&sblk->hdr); - item = (void *)(sblk + 1); - for (i = 0; i < le32_to_cpu(sblk->nr_items); i++) { - printf(" [%u]: seq %llu key_off %u val_off %u key_len %u " - "val_len %u\n", - i, - le64_to_cpu(item->seq), - le32_to_cpu(item->key_off), - le32_to_cpu(item->val_off), - le16_to_cpu(item->key_len), - le16_to_cpu(item->val_len)); - - print_item(sblk, item); - - /* XXX item has to skip holes at the end of blocks */ - item = (void *)(item + 1); - } + for (i = 0; i < le32_to_cpu(sblk->nr_items); i++) + print_item(sblk, i); free(sblk); @@ -238,6 +235,7 @@ static int print_segments(int fd, unsigned long *seg_map, u64 total_segs) static int print_ring_block(int fd, unsigned long *seg_map, u64 blkno) { + struct scoutfs_ring_alloc_region *reg; struct scoutfs_ring_entry_header *eh; struct scoutfs_ring_add_manifest *am; struct scoutfs_ring_block *ring; @@ -252,12 +250,13 @@ static int print_ring_block(int fd, unsigned long *seg_map, u64 blkno) print_block_header(&ring->hdr); eh = ring->entries; - for (i = 0; i < le32_to_cpu(ring->nr_entries); i++) { + while (eh->len) { off = (char *)eh - (char *)ring; printf(" [%u]: type %u len %u\n", off, eh->type, le16_to_cpu(eh->len)); switch(eh->type) { + case SCOUTFS_RING_ADD_MANIFEST: am = (void *)eh; printf(" add ment: segno %llu seq %llu " @@ -271,7 +270,18 @@ static int print_ring_block(int fd, unsigned long *seg_map, u64 blkno) /* XXX verify, 'int nr' limits segno precision */ set_bit_le(le64_to_cpu(am->segno), seg_map); break; + + case SCOUTFS_RING_ADD_ALLOC: + reg = (void *)eh; + printf(" add alloc: index %llu bits", + le64_to_cpu(reg->index)); + for (i = 0; i < array_size(reg->bits); i++) + printf(" %016llx", le64_to_cpu(reg->bits[i])); + printf("\n"); + break; } + + eh = (void *)eh + le16_to_cpu(eh->len); } free(ring); @@ -330,16 +340,19 @@ 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); + /* XXX these are all in a crazy order */ printf(" next_ino %llu total_blocks %llu free_blocks %llu\n" " ring_blkno %llu ring_blocks %llu ring_head %llu\n" - " ring_tail %llu\n", + " ring_tail %llu alloc_uninit %llu total_segs %llu\n", le64_to_cpu(super->next_ino), le64_to_cpu(super->total_blocks), le64_to_cpu(super->free_blocks), le64_to_cpu(super->ring_blkno), le64_to_cpu(super->ring_blocks), le64_to_cpu(super->ring_head_index), - le64_to_cpu(super->ring_tail_index)); + le64_to_cpu(super->ring_tail_index), + le64_to_cpu(super->alloc_uninit), + le64_to_cpu(super->total_segs)); if (le64_to_cpu(super->hdr.seq) > le64_to_cpu(recent.hdr.seq)) memcpy(&recent, super, sizeof(recent)); From 7cd70ab2bb1bf0b45dd32502a0d48b34b53ad6dc Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Wed, 7 Dec 2016 11:43:02 -0800 Subject: [PATCH 067/235] Don't double increment segno when printing Signed-off-by: Zach Brown --- utils/src/print.c | 1 - 1 file changed, 1 deletion(-) diff --git a/utils/src/print.c b/utils/src/print.c index ac562911..da42658d 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -227,7 +227,6 @@ static int print_segments(int fd, unsigned long *seg_map, u64 total_segs) err = print_segment(fd, i); if (err && !ret) ret = err; - i++; } return ret; From 19b674cb38f26be16e770718b23cc7384fc7495e Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Wed, 7 Dec 2016 12:00:52 -0800 Subject: [PATCH 068/235] Print dirent and readdir items Signed-off-by: Zach Brown --- utils/src/print.c | 68 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 49 insertions(+), 19 deletions(-) diff --git a/utils/src/print.c b/utils/src/print.c index da42658d..d19b8aa9 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -80,7 +80,7 @@ static void print_block_header(struct scoutfs_block_header *hdr) le64_to_cpu(hdr->seq), le64_to_cpu(hdr->blkno)); } -static void print_inode(void *key, void *val) +static void print_inode(void *key, int key_len, void *val, int val_len) { struct scoutfs_inode_key *ikey = key; struct scoutfs_inode *inode = val; @@ -122,22 +122,49 @@ static void print_xattr_val_hash(__le64 *refcount) printf(" xattr_val_hash: refcount %llu\n", le64_to_cpu(*refcount)); } +#endif -static void print_dirent(struct scoutfs_dirent *dent, unsigned int val_len) +static u8 *global_printable_name(u8 *name, int name_len) { - unsigned int name_len = val_len - sizeof(*dent); - char name[SCOUTFS_NAME_LEN + 1]; + static u8 name_buf[SCOUTFS_NAME_LEN + 1]; int i; - for (i = 0; i < min(SCOUTFS_NAME_LEN, name_len); i++) - name[i] = isprint(dent->name[i]) ? dent->name[i] : '.'; - name[i] = '\0'; + name_len = min(SCOUTFS_NAME_LEN, name_len); + for (i = 0; i < name_len; i++) + name_buf[i] = isprint(name[i]) ? name[i] : '.'; + name_buf[i] = '\0'; - printf(" dirent: ino: %llu ctr: %llu type: %u name: \"%.*s\"\n", - le64_to_cpu(dent->ino), le64_to_cpu(dent->counter), - dent->type, i, name); + return name_buf; } +static void print_dirent(void *key, int key_len, void *val, int val_len) +{ + struct scoutfs_dirent_key *dkey = key; + struct scoutfs_dirent *dent = val; + unsigned int name_len = key_len - sizeof(*dkey); + u8 *name = global_printable_name(dkey->name, name_len); + + printf(" dirent: dir ino %llu type %u targ ino %llu\n" + " name %s\n", + be64_to_cpu(dkey->ino), dent->type, le64_to_cpu(dent->ino), + name); +} + +static void print_readdir(void *key, int key_len, void *val, int val_len) +{ + struct scoutfs_readdir_key *rkey = key; + struct scoutfs_dirent *dent = val; + unsigned int name_len = val_len - sizeof(*dent); + u8 *name = global_printable_name(dent->name, name_len); + + printf(" readdir: dir ino %llu pos %llu type %u targ ino %llu\n" + " name %s\n", + be64_to_cpu(rkey->ino), be64_to_cpu(rkey->pos), + dent->type, le64_to_cpu(dent->ino), + name); +} + +#if 0 static void print_link_backref(struct scoutfs_link_backref *lref, unsigned int val_len) { @@ -164,14 +191,17 @@ static void print_extent(struct scoutfs_key *key, } #endif -typedef void (*print_func_t)(void *key, void *val); +typedef void (*print_func_t)(void *key, int key_len, void *val, int val_len); static print_func_t printers[] = { [SCOUTFS_INODE_KEY] = print_inode, + [SCOUTFS_DIRENT_KEY] = print_dirent, + [SCOUTFS_READDIR_KEY] = print_readdir, }; static void print_item(struct scoutfs_segment_block *sblk, u32 pos) { + print_func_t printer; struct native_item item; void *key; void *val; @@ -183,15 +213,15 @@ static void print_item(struct scoutfs_segment_block *sblk, u32 pos) val = (char *)sblk + item.val_off; type = *(__u8 *)key; - printf(" [%u]: seq %llu key_off %u val_off %u key_len %u " - "val_len %u\n", - pos, item.seq, item.key_off, item.val_off, item.key_len, - item.val_len); + printer = type < array_size(printers) ? printers[type] : NULL; - if (type < array_size(printers) && printers[type]) - printers[type](key, val); - else - printf(" unknown!\n"); + printf(" [%u]: type %u seq %llu key_off %u val_off %u key_len %u " + "val_len %u%s\n", + pos, type, item.seq, item.key_off, item.val_off, item.key_len, + item.val_len, printer ? "" : " (unrecognized type)"); + + if (printer) + printer(key, item.key_len, val, item.val_len); } static int print_segment(int fd, u64 segno) From c3b6dd07633dfe5fed8e58e2a33ea3f4ddc642d1 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Thu, 8 Dec 2016 11:16:05 -0800 Subject: [PATCH 069/235] Describe ring log with index,nr Update mkfs and print to describe the ring blocks with a starting index and number of blocks instead of a head and tail index. Signed-off-by: Zach Brown --- utils/src/format.h | 6 +++--- utils/src/mkfs.c | 3 ++- utils/src/print.c | 24 ++++++++++++------------ 3 files changed, 17 insertions(+), 16 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index eb19bd6a..002ceaac 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -316,9 +316,9 @@ struct scoutfs_super_block { __le64 free_blocks; __le64 ring_blkno; __le64 ring_blocks; - __le64 ring_head_index; - __le64 ring_tail_index; - __le64 ring_head_seq; + __le64 ring_index; + __le64 ring_nr; + __le64 ring_seq; __le64 buddy_blocks; struct scoutfs_buddy_root buddy_root; struct scoutfs_btree_root btree_root; diff --git a/utils/src/mkfs.c b/utils/src/mkfs.c index dce26246..b477a7e2 100644 --- a/utils/src/mkfs.c +++ b/utils/src/mkfs.c @@ -146,7 +146,8 @@ static int write_new_fs(char *path, int fd) super->alloc_uninit = cpu_to_le64(SCOUTFS_ALLOC_REGION_BITS); super->ring_blkno = cpu_to_le64(SCOUTFS_SUPER_BLKNO + 2); super->ring_blocks = cpu_to_le64(ring_blocks); - super->ring_head_seq = cpu_to_le64(1); + super->ring_nr = cpu_to_le64(1); + super->ring_seq = cpu_to_le64(1); first_segno = DIV_ROUND_UP(le64_to_cpu(super->ring_blkno) + le64_to_cpu(super->ring_blocks), diff --git a/utils/src/print.c b/utils/src/print.c index d19b8aa9..bff7c592 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -323,25 +323,23 @@ static int print_ring_blocks(int fd, struct scoutfs_super_block *super, { int ret = 0; u64 blkno; - u16 index; - u16 tail; + u64 index; + u64 nr; int err; - index = le64_to_cpu(super->ring_head_index); - tail = le64_to_cpu(super->ring_tail_index); + index = le64_to_cpu(super->ring_index); + nr = le64_to_cpu(super->ring_nr); - for(;;) { + while (nr) { blkno = le64_to_cpu(super->ring_blkno) + index; err = print_ring_block(fd, seg_map, blkno); if (err && !ret) ret = err; - if (index == tail) - break; - if (++index == le64_to_cpu(super->ring_blocks)) index = 0; + nr--; }; return ret; @@ -371,15 +369,17 @@ static int print_super_blocks(int fd) le64_to_cpu(super->id), uuid_str); /* XXX these are all in a crazy order */ printf(" next_ino %llu total_blocks %llu free_blocks %llu\n" - " ring_blkno %llu ring_blocks %llu ring_head %llu\n" - " ring_tail %llu alloc_uninit %llu total_segs %llu\n", + " ring_blkno %llu ring_blocks %llu ring_index %llu\n" + " ring_nr %llu ring_seq %llu alloc_uninit %llu\n" + " total_segs %llu\n", le64_to_cpu(super->next_ino), le64_to_cpu(super->total_blocks), le64_to_cpu(super->free_blocks), le64_to_cpu(super->ring_blkno), le64_to_cpu(super->ring_blocks), - le64_to_cpu(super->ring_head_index), - le64_to_cpu(super->ring_tail_index), + le64_to_cpu(super->ring_index), + le64_to_cpu(super->ring_nr), + le64_to_cpu(super->ring_seq), le64_to_cpu(super->alloc_uninit), le64_to_cpu(super->total_segs)); From 7c4bc528c61187f340213562fe14cd776564271d Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Wed, 14 Dec 2016 13:53:46 -0800 Subject: [PATCH 070/235] Make sure manifests cover all keys Make sure that the manifest entries for a given level fully cover the possible key space. This helps item reading describe cached key ranges that extend around items. Signed-off-by: Zach Brown --- utils/src/format.h | 6 ++++-- utils/src/mkfs.c | 18 +++++++----------- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index 002ceaac..71027e90 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -37,8 +37,6 @@ #define SCOUTFS_MAX_TRANS_BLOCKS (128 * 1024 * 1024 / SCOUTFS_BLOCK_SIZE) -#define SCOUTFS_MAX_KEY_BYTES 255 - /* * This header is found at the start of every block so that we can * verify that it's what we were looking for. The crc and padding @@ -215,6 +213,7 @@ struct scoutfs_key { #define SCOUTFS_SYMLINK_KEY 8 #define SCOUTFS_EXTENT_KEY 9 #define SCOUTFS_ORPHAN_KEY 10 +#define SCOUTFS_MAX_UNUSED_KEY 255 #define SCOUTFS_MAX_ITEM_LEN 512 @@ -443,4 +442,7 @@ struct scoutfs_link_backref { __le64 offset; } __packed; +#define SCOUTFS_MAX_KEY_SIZE \ + offsetof(struct scoutfs_dirent_key, name[SCOUTFS_NAME_LEN]) + #endif diff --git a/utils/src/mkfs.c b/utils/src/mkfs.c index b477a7e2..1efc8a05 100644 --- a/utils/src/mkfs.c +++ b/utils/src/mkfs.c @@ -66,7 +66,7 @@ static u64 calc_ring_blocks(u64 size) segs = size >> SCOUTFS_SEGMENT_SHIFT; max_entry_bytes = sizeof(struct scoutfs_ring_add_manifest) + - (2 * SCOUTFS_MAX_KEY_BYTES); + (2 * SCOUTFS_MAX_KEY_SIZE); total_bytes = (segs * max_entry_bytes) * 4; blocks = DIV_ROUND_UP(total_bytes, SCOUTFS_BLOCK_SIZE); @@ -101,6 +101,7 @@ static int write_new_fs(char *path, int fd) u64 ring_blocks; u64 total_segs; u64 first_segno; + __u8 *type; int ret; gettimeofday(&tv, NULL); @@ -190,19 +191,14 @@ static int write_new_fs(char *path, int fd) /* a single manifest entry points to the single segment */ am = (void *)ring->entries; am->eh.type = SCOUTFS_RING_ADD_MANIFEST; - am->eh.len = cpu_to_le16(sizeof(struct scoutfs_ring_add_manifest) + - (2 * sizeof(struct scoutfs_inode_key))); + am->eh.len = cpu_to_le16(sizeof(struct scoutfs_ring_add_manifest) + 1); am->segno = sblk->segno; am->seq = cpu_to_le64(1); - am->first_key_len = cpu_to_le16(sizeof(struct scoutfs_inode_key)); - am->last_key_len = cpu_to_le16(sizeof(struct scoutfs_inode_key)); + am->first_key_len = 0; + am->last_key_len = cpu_to_le16(1); am->level = 1; - ikey = (void *)(am + 1); - ikey->type = SCOUTFS_INODE_KEY; - ikey->ino = cpu_to_be64(SCOUTFS_ROOT_INO); - ikey = (void *)(ikey + 1); - ikey->type = SCOUTFS_INODE_KEY; - ikey->ino = cpu_to_be64(SCOUTFS_ROOT_INO); + type = (void *)(am + 1); + *type = SCOUTFS_MAX_UNUSED_KEY; /* a single alloc region records the first two segs as allocated */ reg = (void *)am + le16_to_cpu(am->eh.len); From 484b34057ad7a1984cbbacf48623d34b3e775d29 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Fri, 30 Dec 2016 17:45:02 -0800 Subject: [PATCH 071/235] Update mkfs and print for treap ring Update mkfs and print now that the manifest and allocator are stored in treaps in the ring. Signed-off-by: Zach Brown --- utils/src/crc.c | 9 +++ utils/src/crc.h | 1 + utils/src/format.h | 82 +++++++++++++------- utils/src/mkfs.c | 134 ++++++++++++++++---------------- utils/src/print.c | 187 ++++++++++++++++++++++++++------------------- utils/src/util.h | 1 + 6 files changed, 243 insertions(+), 171 deletions(-) diff --git a/utils/src/crc.c b/utils/src/crc.c index 38640fbc..2932cb88 100644 --- a/utils/src/crc.c +++ b/utils/src/crc.c @@ -37,3 +37,12 @@ u32 crc_block(struct scoutfs_block_header *hdr) return crc32c(~0, (char *)hdr + sizeof(hdr->crc), SCOUTFS_BLOCK_SIZE - sizeof(hdr->crc)); } + +__le32 crc_node(struct scoutfs_treap_node *node) +{ + unsigned int skip = sizeof(node->crc); + unsigned int bytes = offsetof(struct scoutfs_treap_node, + data[le16_to_cpu(node->bytes)]); + + return cpu_to_le32(crc32c(~0, (char *)node + skip, bytes - skip)); +} diff --git a/utils/src/crc.h b/utils/src/crc.h index 6878bf2f..b584315d 100644 --- a/utils/src/crc.h +++ b/utils/src/crc.h @@ -8,5 +8,6 @@ u32 crc32c(u32 crc, const void *data, unsigned int len); u64 crc32c_64(u32 crc, const void *data, unsigned int len); u32 crc_block(struct scoutfs_block_header *hdr); +__le32 crc_node(struct scoutfs_treap_node *node); #endif diff --git a/utils/src/format.h b/utils/src/format.h index 71027e90..c611ea32 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -51,22 +51,63 @@ struct scoutfs_block_header { __le64 blkno; } __packed; -struct scoutfs_ring_entry_header { - __u8 type; - __le16 len; +struct scoutfs_treap_ref { + __le64 off; + __le64 gen; + __u8 aug_bits; } __packed; -#define SCOUTFS_RING_ADD_MANIFEST 1 -#define SCOUTFS_RING_ADD_ALLOC 2 +/* + * The lesser and greater bits are persistent on disk so that we can migrate + * nodes from the older half of the ring. + * + * The dirty bit is only used for in-memory nodes. + */ +#define SCOUTFS_TREAP_AUG_LESSER (1 << 0) +#define SCOUTFS_TREAP_AUG_GREATER (1 << 1) +#define SCOUTFS_TREAP_AUG_HALVES (SCOUTFS_TREAP_AUG_LESSER | \ + SCOUTFS_TREAP_AUG_GREATER) +#define SCOUTFS_TREAP_AUG_DIRTY (1 << 2) -struct scoutfs_ring_add_manifest { - struct scoutfs_ring_entry_header eh; +/* + * Treap nodes are stored at byte offset in the ring of blocks described + * by the super block. Each reference contains the off and gen that it + * will find in the node for verification. Each node has the header + * and data payload covered by a crc. + */ +struct scoutfs_treap_node { + __le32 crc; + __le64 off; + __le64 gen; + __le64 prio; + struct scoutfs_treap_ref left; + struct scoutfs_treap_ref right; + __le16 bytes; + u8 data[0]; +} __packed; + +struct scoutfs_treap_root { + struct scoutfs_treap_ref ref; +} __packed; + +/* + * This is absurdly huge. If there was only ever 1 item per segment and + * 2^64 items the tree could get this deep. + */ +#define SCOUTFS_MANIFEST_MAX_LEVEL 20 + +struct scoutfs_manifest { + struct scoutfs_treap_root root; + __le64 level_counts[SCOUTFS_MANIFEST_MAX_LEVEL]; +} __packed; + +struct scoutfs_manifest_entry { __le64 segno; __le64 seq; __le16 first_key_len; __le16 last_key_len; __u8 level; - /* first and last key bytes */ + __u8 keys[0]; } __packed; #define SCOUTFS_ALLOC_REGION_SHIFT 8 @@ -77,27 +118,11 @@ struct scoutfs_ring_add_manifest { * The bits need to be aligned so that the host can use native long * bitops on the bits in memory. */ -struct scoutfs_ring_alloc_region { - struct scoutfs_ring_entry_header eh; +struct scoutfs_alloc_region { __le64 index; - __u8 pad[5]; __le64 bits[SCOUTFS_ALLOC_REGION_BITS / 64]; } __packed; -/* - * This is absurdly huge. If there was only ever 1 item per segment and - * 2^64 items the tree could get this deep. - */ -#define SCOUTFS_MANIFEST_MAX_LEVEL 20 - -/* - * The packed entries in the block are terminated by a header with a 0 length. - */ -struct scoutfs_ring_block { - struct scoutfs_block_header hdr; - struct scoutfs_ring_entry_header entries[0]; -} __packed; - /* * We really want these to be a power of two size so that they're naturally * aligned. This ensures that they won't cross page boundaries and we @@ -315,12 +340,13 @@ struct scoutfs_super_block { __le64 free_blocks; __le64 ring_blkno; __le64 ring_blocks; - __le64 ring_index; - __le64 ring_nr; - __le64 ring_seq; + __le64 ring_tail_block; + __le64 ring_gen; __le64 buddy_blocks; struct scoutfs_buddy_root buddy_root; struct scoutfs_btree_root btree_root; + struct scoutfs_treap_root alloc_treap_root; + struct scoutfs_manifest manifest; } __packed; #define SCOUTFS_ROOT_INO 1 diff --git a/utils/src/mkfs.c b/utils/src/mkfs.c index 1efc8a05..0a10d5d3 100644 --- a/utils/src/mkfs.c +++ b/utils/src/mkfs.c @@ -21,20 +21,11 @@ #include "buddy.h" #include "item.h" -/* - * Update the block's header and write it out. - */ -static int write_block(int fd, u64 blkno, struct scoutfs_super_block *super, - struct scoutfs_block_header *hdr) +static int write_raw_block(int fd, u64 blkno, void *blk) { ssize_t ret; - if (super) - *hdr = super->hdr; - hdr->blkno = cpu_to_le64(blkno); - hdr->crc = cpu_to_le32(crc_block(hdr)); - - ret = pwrite(fd, hdr, SCOUTFS_BLOCK_SIZE, blkno << SCOUTFS_BLOCK_SHIFT); + ret = pwrite(fd, blk, SCOUTFS_BLOCK_SIZE, blkno << SCOUTFS_BLOCK_SHIFT); if (ret != SCOUTFS_BLOCK_SIZE) { fprintf(stderr, "write to blkno %llu returned %zd: %s (%d)\n", blkno, ret, strerror(errno), errno); @@ -45,41 +36,55 @@ static int write_block(int fd, u64 blkno, struct scoutfs_super_block *super, } /* - * Figure out how many blocks the ring will need. This goes crazy - * with the variables to make the calculation clear. - * - * XXX just a place holder. The real calculation is more like: - * - * - max size add manifest entries for all segments - * - (some day) allocator entries for all segments - * - ring block header overhead - * - ring block unused tail space overhead - * + * Update the block's header and write it out. */ -static u64 calc_ring_blocks(u64 size) +static int write_block(int fd, u64 blkno, struct scoutfs_super_block *super, + struct scoutfs_block_header *hdr) { - u64 first_seg_blocks; - u64 max_entry_bytes; - u64 total_bytes; - u64 blocks; - u64 segs; + if (super) + *hdr = super->hdr; + hdr->blkno = cpu_to_le64(blkno); + hdr->crc = cpu_to_le32(crc_block(hdr)); - segs = size >> SCOUTFS_SEGMENT_SHIFT; - max_entry_bytes = sizeof(struct scoutfs_ring_add_manifest) + - (2 * SCOUTFS_MAX_KEY_SIZE); - total_bytes = (segs * max_entry_bytes) * 4; - blocks = DIV_ROUND_UP(total_bytes, SCOUTFS_BLOCK_SIZE); + return write_raw_block(fd, blkno, hdr); +} - first_seg_blocks = SCOUTFS_SEGMENT_BLOCKS - - (SCOUTFS_SUPER_BLKNO + SCOUTFS_SUPER_NR); +/* + * Figure out how many blocks the ring will need. The ring has to hold: + * + * - manifest entries for every segment with largest keys + * - allocator regions for bits to reference every segment + * - empty space at the end of blocks so nodes don't cross blocks + * - double that to account for repeatedly duplicating entries + * - double that so we can migrate everything before wrapping + */ +static u64 calc_ring_blocks(u64 segs) +{ + u64 alloc_blocks; + u64 ment_blocks; + u64 block_bytes; + u64 node_bytes; + u64 regions; - return max(first_seg_blocks, blocks); + node_bytes = sizeof(struct scoutfs_treap_node) + + sizeof(struct scoutfs_manifest_entry) + + (2 * SCOUTFS_MAX_KEY_SIZE); + block_bytes = SCOUTFS_BLOCK_SIZE - (node_bytes - 1); + ment_blocks = DIV_ROUND_UP(segs * node_bytes, block_bytes); + + node_bytes = sizeof(struct scoutfs_treap_node) + + sizeof(struct scoutfs_alloc_region); + regions = DIV_ROUND_UP(segs, SCOUTFS_ALLOC_REGION_BITS); + block_bytes = SCOUTFS_BLOCK_SIZE - (node_bytes - 1); + alloc_blocks = DIV_ROUND_UP(regions * node_bytes, block_bytes); + + return ALIGN((ment_blocks + alloc_blocks) * 4, SCOUTFS_SEGMENT_BLOCKS); } /* * Make a new file system by writing: * - super blocks - * - ring block with manifest entry + * - ring block with manifest node * - segment with root inode */ static int write_new_fs(char *path, int fd) @@ -88,13 +93,12 @@ static int write_new_fs(char *path, int fd) struct scoutfs_inode_key *ikey; struct scoutfs_inode *inode; struct scoutfs_segment_block *sblk; - struct scoutfs_ring_block *ring; - struct scoutfs_ring_add_manifest *am; - struct scoutfs_ring_alloc_region *reg; + struct scoutfs_manifest_entry *ment; + struct scoutfs_treap_node *node; struct native_item item; struct timeval tv; char uuid_str[37]; - unsigned int i; + void *ring; u64 limit; u64 size; u64 total_blocks; @@ -103,6 +107,7 @@ static int write_new_fs(char *path, int fd) u64 first_segno; __u8 *type; int ret; + u64 i; gettimeofday(&tv, NULL); @@ -133,7 +138,7 @@ static int write_new_fs(char *path, int fd) total_blocks = size / SCOUTFS_BLOCK_SIZE; total_segs = size / SCOUTFS_SEGMENT_SIZE; - ring_blocks = calc_ring_blocks(size); + ring_blocks = calc_ring_blocks(total_segs); /* first initialize the super so we can use it to build structures */ memset(super, 0, SCOUTFS_BLOCK_SIZE); @@ -144,16 +149,18 @@ static int write_new_fs(char *path, int fd) super->next_ino = cpu_to_le64(SCOUTFS_ROOT_INO + 1); super->total_blocks = cpu_to_le64(total_blocks); super->total_segs = cpu_to_le64(total_segs); - super->alloc_uninit = cpu_to_le64(SCOUTFS_ALLOC_REGION_BITS); super->ring_blkno = cpu_to_le64(SCOUTFS_SUPER_BLKNO + 2); super->ring_blocks = cpu_to_le64(ring_blocks); - super->ring_nr = cpu_to_le64(1); - super->ring_seq = cpu_to_le64(1); + super->ring_tail_block = cpu_to_le64(1); + super->ring_gen = cpu_to_le64(1); first_segno = DIV_ROUND_UP(le64_to_cpu(super->ring_blkno) + le64_to_cpu(super->ring_blocks), SCOUTFS_SEGMENT_BLOCKS); + /* alloc from uninit, don't need regions yet */ + super->alloc_uninit = cpu_to_le64(first_segno + 1); + /* write seg with root inode */ sblk->segno = cpu_to_le64(first_segno); sblk->max_seq = cpu_to_le64(1); @@ -189,30 +196,29 @@ static int write_new_fs(char *path, int fd) } /* a single manifest entry points to the single segment */ - am = (void *)ring->entries; - am->eh.type = SCOUTFS_RING_ADD_MANIFEST; - am->eh.len = cpu_to_le16(sizeof(struct scoutfs_ring_add_manifest) + 1); - am->segno = sblk->segno; - am->seq = cpu_to_le64(1); - am->first_key_len = 0; - am->last_key_len = cpu_to_le16(1); - am->level = 1; - type = (void *)(am + 1); + node = ring; + node->off = cpu_to_le64((char *)node - (char *)ring); + node->gen = cpu_to_le64(1); + node->bytes = cpu_to_le16(sizeof(struct scoutfs_manifest_entry) + 1); + pseudo_random_bytes(&node->prio, sizeof(node->prio)); + + ment = (void *)node->data; + ment->segno = sblk->segno; + ment->seq = cpu_to_le64(1); + ment->first_key_len = 0; + ment->last_key_len = cpu_to_le16(1); + ment->level = 1; + type = (void *)ment->keys; *type = SCOUTFS_MAX_UNUSED_KEY; - /* a single alloc region records the first two segs as allocated */ - reg = (void *)am + le16_to_cpu(am->eh.len); - reg->eh.type = SCOUTFS_RING_ADD_ALLOC; - reg->eh.len = cpu_to_le16(sizeof(struct scoutfs_ring_alloc_region)); - /* initial super, ring, and first seg are all allocated */ - memset(reg->bits, 0xff, sizeof(reg->bits)); - for (i = 0; i <= first_segno; i++) - clear_bit_le(i, reg->bits); + node->crc = crc_node(node); - /* block is already zeroed and so contains a 0 len terminating header */ + super->manifest.root.ref.off = node->off; + super->manifest.root.ref.gen = node->gen; + super->manifest.root.ref.aug_bits = SCOUTFS_TREAP_AUG_LESSER; + super->manifest.level_counts[1] = cpu_to_le64(1); - ret = write_block(fd, le64_to_cpu(super->ring_blkno), super, - &ring->hdr); + ret = write_raw_block(fd, le64_to_cpu(super->ring_blkno), ring); if (ret) goto out; diff --git a/utils/src/print.c b/utils/src/print.c index bff7c592..14de06d7 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -262,87 +262,90 @@ static int print_segments(int fd, unsigned long *seg_map, u64 total_segs) return ret; } -static int print_ring_block(int fd, unsigned long *seg_map, u64 blkno) +enum { + TREAP_MANIFEST, + TREAP_ALLOC, +}; + +static void print_treap_ref(struct scoutfs_treap_ref *ref) { - struct scoutfs_ring_alloc_region *reg; - struct scoutfs_ring_entry_header *eh; - struct scoutfs_ring_add_manifest *am; - struct scoutfs_ring_block *ring; - u32 off; - int i; - - ring = read_block(fd, blkno); - if (!ring) - return -ENOMEM; - - printf("ring blkno %llu\n", blkno); - print_block_header(&ring->hdr); - - eh = ring->entries; - while (eh->len) { - off = (char *)eh - (char *)ring; - printf(" [%u]: type %u len %u\n", - off, eh->type, le16_to_cpu(eh->len)); - - switch(eh->type) { - - case SCOUTFS_RING_ADD_MANIFEST: - am = (void *)eh; - printf(" add ment: segno %llu seq %llu " - "first_len %u last_len %u level %u\n", - le64_to_cpu(am->segno), - le64_to_cpu(am->seq), - le16_to_cpu(am->first_key_len), - le16_to_cpu(am->last_key_len), - am->level); - - /* XXX verify, 'int nr' limits segno precision */ - set_bit_le(le64_to_cpu(am->segno), seg_map); - break; - - case SCOUTFS_RING_ADD_ALLOC: - reg = (void *)eh; - printf(" add alloc: index %llu bits", - le64_to_cpu(reg->index)); - for (i = 0; i < array_size(reg->bits); i++) - printf(" %016llx", le64_to_cpu(reg->bits[i])); - printf("\n"); - break; - } - - eh = (void *)eh + le16_to_cpu(eh->len); - } - - free(ring); - - return 0; + printf(" off %llu gen %llu aug_bits %x", + le64_to_cpu(ref->off), le64_to_cpu(ref->gen), + ref->aug_bits); } -static int print_ring_blocks(int fd, struct scoutfs_super_block *super, - unsigned long *seg_map) +static int print_treap_node(int fd, struct scoutfs_super_block *super, + unsigned treap, struct scoutfs_treap_ref *ref, + unsigned long *seg_map) { - int ret = 0; + struct scoutfs_manifest_entry *ment; + struct scoutfs_alloc_region *reg; + struct scoutfs_treap_node *tnode; + char valid_str[40]; + __le32 crc; u64 blkno; - u64 index; - u64 nr; - int err; + void *blk; + u64 off; + int i; - index = le64_to_cpu(super->ring_index); - nr = le64_to_cpu(super->ring_nr); + if (!ref->gen) + return 0; - while (nr) { - blkno = le64_to_cpu(super->ring_blkno) + index; + off = le64_to_cpu(ref->off); + blkno = le64_to_cpu(super->ring_blkno) + (off >> SCOUTFS_BLOCK_SHIFT); - err = print_ring_block(fd, seg_map, blkno); - if (err && !ret) - ret = err; + blk = read_block(fd, blkno); + if (!blk) + return -ENOMEM; - if (++index == le64_to_cpu(super->ring_blocks)) - index = 0; - nr--; - }; + tnode = blk + (off & SCOUTFS_BLOCK_MASK); - return ret; + crc = crc_node(tnode); + if (crc != tnode->crc) + sprintf(valid_str, "(!= %08x) ", le32_to_cpu(crc)); + else + valid_str[0] = '\0'; + + printf(" node: crc %08x %soff %llu gen %llu bytes %u prio %016llx\n" + " l:", + le32_to_cpu(tnode->crc), valid_str, le64_to_cpu(tnode->off), + le64_to_cpu(tnode->gen), le16_to_cpu(tnode->bytes), + le64_to_cpu(tnode->prio)); + print_treap_ref(&tnode->left); + printf(" r:"); + print_treap_ref(&tnode->right); + printf("\n"); + + switch(treap) { + case TREAP_MANIFEST: + ment = (void *)tnode->data; + printf(" ment: segno %llu seq %llu " + "first_len %u last_len %u level %u\n", + le64_to_cpu(ment->segno), + le64_to_cpu(ment->seq), + le16_to_cpu(ment->first_key_len), + le16_to_cpu(ment->last_key_len), + ment->level); + /* XXX verify, 'int nr' limits segno precision */ + set_bit_le(le64_to_cpu(ment->segno), seg_map); + break; + + case TREAP_ALLOC: + reg = (void *)tnode->data; + printf(" reg: index %llu bits", + le64_to_cpu(reg->index)); + for (i = 0; i < array_size(reg->bits); i++) + printf(" %016llx", le64_to_cpu(reg->bits[i])); + printf("\n"); + break; + } + + print_treap_node(fd, super, treap, &tnode->left, seg_map); + print_treap_node(fd, super, treap, &tnode->right, seg_map); + + free(blk); + + return 0; } static int print_super_blocks(int fd) @@ -351,10 +354,13 @@ static int print_super_blocks(int fd) struct scoutfs_super_block recent = { .hdr.seq = 0 }; unsigned long *seg_map; char uuid_str[37]; + __le64 *counts; u64 total_segs; u64 longs; int ret = 0; + int err; int i; + int j; for (i = 0; i < SCOUTFS_SUPER_NR; i++) { super = read_block(fd, SCOUTFS_SUPER_BLKNO + i); @@ -369,19 +375,31 @@ static int print_super_blocks(int fd) le64_to_cpu(super->id), uuid_str); /* XXX these are all in a crazy order */ printf(" next_ino %llu total_blocks %llu free_blocks %llu\n" - " ring_blkno %llu ring_blocks %llu ring_index %llu\n" - " ring_nr %llu ring_seq %llu alloc_uninit %llu\n" - " total_segs %llu\n", + " ring_blkno %llu ring_blocks %llu ring_tail_block %llu\n" + " ring_gen %llu alloc_uninit %llu total_segs %llu\n", le64_to_cpu(super->next_ino), le64_to_cpu(super->total_blocks), le64_to_cpu(super->free_blocks), le64_to_cpu(super->ring_blkno), le64_to_cpu(super->ring_blocks), - le64_to_cpu(super->ring_index), - le64_to_cpu(super->ring_nr), - le64_to_cpu(super->ring_seq), + le64_to_cpu(super->ring_tail_block), + le64_to_cpu(super->ring_gen), le64_to_cpu(super->alloc_uninit), le64_to_cpu(super->total_segs)); + printf(" alloc root:"); + print_treap_ref(&super->alloc_treap_root.ref); + printf("\n"); + printf(" manifest root:"); + print_treap_ref(&super->manifest.root.ref); + printf("\n"); + + printf(" level_counts:"); + counts = super->manifest.level_counts; + for (j = 0; j < SCOUTFS_MANIFEST_MAX_LEVEL; j++) { + if (le64_to_cpu(counts[j])) + printf(" %u: %llu", j, le64_to_cpu(counts[j])); + } + printf("\n"); if (le64_to_cpu(super->hdr.seq) > le64_to_cpu(recent.hdr.seq)) memcpy(&recent, super, sizeof(recent)); @@ -398,8 +416,19 @@ static int print_super_blocks(int fd) if (!seg_map) return -ENOMEM; - ret = print_ring_blocks(fd, super, seg_map) ?: - print_segments(fd, seg_map, total_segs); + printf("manifest treap:\n"); + ret = print_treap_node(fd, super, TREAP_MANIFEST, + &super->manifest.root.ref, seg_map); + + printf("alloc treap:\n"); + err = print_treap_node(fd, super, TREAP_ALLOC, + &super->alloc_treap_root.ref, NULL); + if (err && !ret) + ret = err; + + err = print_segments(fd, seg_map, total_segs); + if (err && !ret) + ret = err; free(seg_map); diff --git a/utils/src/util.h b/utils/src/util.h index daa28a98..ee6516e7 100644 --- a/utils/src/util.h +++ b/utils/src/util.h @@ -51,6 +51,7 @@ do { \ }) #define DIV_ROUND_UP(x, y) (((x) + (y) - 1) / (y)) +#define ALIGN(x, y) (((x) + (y) - 1) & ~((y) - 1)) #ifndef offsetof #define offsetof(type, memb) ((unsigned long)&((type *)0)->memb) From c2b47d84c1f3b6fa1629a815aaecd1c2661032c6 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Mon, 2 Jan 2017 09:21:31 -0800 Subject: [PATCH 072/235] Add next_seg_seq field to super Signed-off-by: Zach Brown --- utils/src/format.h | 3 ++- utils/src/mkfs.c | 3 ++- utils/src/print.c | 15 ++++++++++++--- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index c611ea32..6495d457 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -151,7 +151,7 @@ struct scoutfs_segment_block { __le32 crc; __le32 _padding; __le64 segno; - __le64 max_seq; + __le64 seq; __le32 nr_items; __le32 _moar_pads; struct scoutfs_segment_item items[0]; @@ -342,6 +342,7 @@ struct scoutfs_super_block { __le64 ring_blocks; __le64 ring_tail_block; __le64 ring_gen; + __le64 next_seg_seq; __le64 buddy_blocks; struct scoutfs_buddy_root buddy_root; struct scoutfs_btree_root btree_root; diff --git a/utils/src/mkfs.c b/utils/src/mkfs.c index 0a10d5d3..26560194 100644 --- a/utils/src/mkfs.c +++ b/utils/src/mkfs.c @@ -153,6 +153,7 @@ static int write_new_fs(char *path, int fd) super->ring_blocks = cpu_to_le64(ring_blocks); super->ring_tail_block = cpu_to_le64(1); super->ring_gen = cpu_to_le64(1); + super->next_seg_seq = cpu_to_le64(2); first_segno = DIV_ROUND_UP(le64_to_cpu(super->ring_blkno) + le64_to_cpu(super->ring_blocks), @@ -163,7 +164,7 @@ static int write_new_fs(char *path, int fd) /* write seg with root inode */ sblk->segno = cpu_to_le64(first_segno); - sblk->max_seq = cpu_to_le64(1); + sblk->seq = cpu_to_le64(1); sblk->nr_items = cpu_to_le32(1); ikey = (void *)&sblk->items[1]; diff --git a/utils/src/print.c b/utils/src/print.c index 14de06d7..8e756233 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -224,6 +224,13 @@ static void print_item(struct scoutfs_segment_block *sblk, u32 pos) printer(key, item.key_len, val, item.val_len); } +static void print_segment_block(struct scoutfs_segment_block *sblk) +{ + printf(" sblk: segno %llu seq %llu nr_items %u\n", + le64_to_cpu(sblk->segno), le64_to_cpu(sblk->seq), + le32_to_cpu(sblk->nr_items)); +} + static int print_segment(int fd, u64 segno) { struct scoutfs_segment_block *sblk; @@ -234,7 +241,7 @@ static int print_segment(int fd, u64 segno) return -ENOMEM; printf("segment segno %llu\n", segno); -// print_block_header(&sblk->hdr); + print_segment_block(sblk); for (i = 0; i < le32_to_cpu(sblk->nr_items); i++) print_item(sblk, i); @@ -376,7 +383,8 @@ static int print_super_blocks(int fd) /* XXX these are all in a crazy order */ printf(" next_ino %llu total_blocks %llu free_blocks %llu\n" " ring_blkno %llu ring_blocks %llu ring_tail_block %llu\n" - " ring_gen %llu alloc_uninit %llu total_segs %llu\n", + " ring_gen %llu alloc_uninit %llu total_segs %llu\n" + " next_seg_seq %llu\n", le64_to_cpu(super->next_ino), le64_to_cpu(super->total_blocks), le64_to_cpu(super->free_blocks), @@ -385,7 +393,8 @@ static int print_super_blocks(int fd) le64_to_cpu(super->ring_tail_block), le64_to_cpu(super->ring_gen), le64_to_cpu(super->alloc_uninit), - le64_to_cpu(super->total_segs)); + le64_to_cpu(super->total_segs), + le64_to_cpu(super->next_seg_seq)); printf(" alloc root:"); print_treap_ref(&super->alloc_treap_root.ref); printf("\n"); From 26a42669645589badff46d2f93c47c2feedb42ec Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Thu, 5 Jan 2017 17:44:49 -0800 Subject: [PATCH 073/235] Set manifest keys to precise segment keys We had changed the manifest keys to fully cover the space around the segments in the hopes that it'd let item reading easily find negative cached regions around items. But that makes compaction think that segments intersect with items when they really don't. We'd much rather avoid unnecessary compaction by having the manifest entries precisely reflect the keys in the segment. Item reading can do more work at run time to find the bounds of the key space that are around the edges of the segments it works with. Signed-off-by: Zach Brown --- utils/src/mkfs.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/utils/src/mkfs.c b/utils/src/mkfs.c index 26560194..555e193c 100644 --- a/utils/src/mkfs.c +++ b/utils/src/mkfs.c @@ -90,6 +90,7 @@ static u64 calc_ring_blocks(u64 segs) static int write_new_fs(char *path, int fd) { struct scoutfs_super_block *super; + struct scoutfs_inode_key root_ikey; struct scoutfs_inode_key *ikey; struct scoutfs_inode *inode; struct scoutfs_segment_block *sblk; @@ -105,7 +106,6 @@ static int write_new_fs(char *path, int fd) u64 ring_blocks; u64 total_segs; u64 first_segno; - __u8 *type; int ret; u64 i; @@ -167,6 +167,9 @@ static int write_new_fs(char *path, int fd) sblk->seq = cpu_to_le64(1); sblk->nr_items = cpu_to_le32(1); + root_ikey.type = SCOUTFS_INODE_KEY; + root_ikey.ino = cpu_to_be64(SCOUTFS_ROOT_INO); + ikey = (void *)&sblk->items[1]; inode = (void *)(ikey + 1); @@ -177,8 +180,7 @@ static int write_new_fs(char *path, int fd) item.val_len = sizeof(struct scoutfs_inode); store_item(sblk, 0, &item); - ikey->type = SCOUTFS_INODE_KEY; - ikey->ino = cpu_to_be64(SCOUTFS_ROOT_INO); + *ikey = root_ikey; inode->nlink = cpu_to_le32(2); inode->mode = cpu_to_le32(0755 | 0040000); @@ -200,17 +202,19 @@ static int write_new_fs(char *path, int fd) node = ring; node->off = cpu_to_le64((char *)node - (char *)ring); node->gen = cpu_to_le64(1); - node->bytes = cpu_to_le16(sizeof(struct scoutfs_manifest_entry) + 1); + node->bytes = cpu_to_le16(sizeof(struct scoutfs_manifest_entry) + + (2 * sizeof(struct scoutfs_inode_key))); pseudo_random_bytes(&node->prio, sizeof(node->prio)); ment = (void *)node->data; ment->segno = sblk->segno; ment->seq = cpu_to_le64(1); - ment->first_key_len = 0; - ment->last_key_len = cpu_to_le16(1); + ment->first_key_len = cpu_to_le16(sizeof(struct scoutfs_inode_key)); + ment->last_key_len = cpu_to_le16(sizeof(struct scoutfs_inode_key)); ment->level = 1; - type = (void *)ment->keys; - *type = SCOUTFS_MAX_UNUSED_KEY; + ikey = (void *)ment->keys; + ikey[0] = root_ikey; + ikey[1] = root_ikey; node->crc = crc_node(node); From 34c62824e5ad27ec911f6b7ceb80ac8f3c7f8f20 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Mon, 9 Jan 2017 16:21:13 -0800 Subject: [PATCH 074/235] Use a treap walker to print segments We were using a bitmap to record segments during manifest printing and then walking that bitmap to print segments. It's a little silly to have a second data structure record the referenced segments when we could just walk the manifest again to print the segments. So refactor node printing into a treap walker that calls a function for each node. Then we can have functions that print the node data structurs for each treap and then one that prints the segments that are referenced by manifest nodes. Signed-off-by: Zach Brown --- utils/src/print.c | 157 ++++++++++++++++++++-------------------------- 1 file changed, 68 insertions(+), 89 deletions(-) diff --git a/utils/src/print.c b/utils/src/print.c index 8e756233..f92c8af3 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -16,7 +16,6 @@ #include "cmd.h" #include "crc.h" #include "buddy.h" -#include "bitops.h" #include "item.h" /* XXX maybe these go somewhere */ @@ -231,8 +230,10 @@ static void print_segment_block(struct scoutfs_segment_block *sblk) le32_to_cpu(sblk->nr_items)); } -static int print_segment(int fd, u64 segno) +static int print_segment(int fd, struct scoutfs_treap_node *tnode) { + struct scoutfs_manifest_entry *ment = (void *)tnode->data; + u64 segno = le64_to_cpu(ment->segno); struct scoutfs_segment_block *sblk; int i; @@ -251,29 +252,6 @@ static int print_segment(int fd, u64 segno) return 0; } -static int print_segments(int fd, unsigned long *seg_map, u64 total_segs) -{ - int ret = 0; - int i = 0; - int err; - - for (i = 0; - (i = find_next_bit_le(seg_map, total_segs, i)) < total_segs; - i++) { - - err = print_segment(fd, i); - if (err && !ret) - ret = err; - } - - return ret; -} - -enum { - TREAP_MANIFEST, - TREAP_ALLOC, -}; - static void print_treap_ref(struct scoutfs_treap_ref *ref) { printf(" off %llu gen %llu aug_bits %x", @@ -281,31 +259,10 @@ static void print_treap_ref(struct scoutfs_treap_ref *ref) ref->aug_bits); } -static int print_treap_node(int fd, struct scoutfs_super_block *super, - unsigned treap, struct scoutfs_treap_ref *ref, - unsigned long *seg_map) +static void print_treap_node(struct scoutfs_treap_node *tnode) { - struct scoutfs_manifest_entry *ment; - struct scoutfs_alloc_region *reg; - struct scoutfs_treap_node *tnode; char valid_str[40]; __le32 crc; - u64 blkno; - void *blk; - u64 off; - int i; - - if (!ref->gen) - return 0; - - off = le64_to_cpu(ref->off); - blkno = le64_to_cpu(super->ring_blkno) + (off >> SCOUTFS_BLOCK_SHIFT); - - blk = read_block(fd, blkno); - if (!blk) - return -ENOMEM; - - tnode = blk + (off & SCOUTFS_BLOCK_MASK); crc = crc_node(tnode); if (crc != tnode->crc) @@ -322,48 +279,78 @@ static int print_treap_node(int fd, struct scoutfs_super_block *super, printf(" r:"); print_treap_ref(&tnode->right); printf("\n"); +} - switch(treap) { - case TREAP_MANIFEST: - ment = (void *)tnode->data; - printf(" ment: segno %llu seq %llu " - "first_len %u last_len %u level %u\n", - le64_to_cpu(ment->segno), - le64_to_cpu(ment->seq), - le16_to_cpu(ment->first_key_len), - le16_to_cpu(ment->last_key_len), - ment->level); - /* XXX verify, 'int nr' limits segno precision */ - set_bit_le(le64_to_cpu(ment->segno), seg_map); - break; +static int print_manifest_entry(int fd, struct scoutfs_treap_node *tnode) +{ + struct scoutfs_manifest_entry *ment = (void *)tnode->data; - case TREAP_ALLOC: - reg = (void *)tnode->data; - printf(" reg: index %llu bits", - le64_to_cpu(reg->index)); - for (i = 0; i < array_size(reg->bits); i++) - printf(" %016llx", le64_to_cpu(reg->bits[i])); - printf("\n"); - break; - } + print_treap_node(tnode); - print_treap_node(fd, super, treap, &tnode->left, seg_map); - print_treap_node(fd, super, treap, &tnode->right, seg_map); + printf(" ment: segno %llu seq %llu first_len %u last_len %u level %u\n", + le64_to_cpu(ment->segno), + le64_to_cpu(ment->seq), + le16_to_cpu(ment->first_key_len), + le16_to_cpu(ment->last_key_len), + ment->level); + + return 0; +} + +static int print_alloc_region(int fd, struct scoutfs_treap_node *tnode) +{ + struct scoutfs_alloc_region *reg = (void *)tnode->data; + int i; + + print_treap_node(tnode); + + printf(" reg: index %llu bits", le64_to_cpu(reg->index)); + for (i = 0; i < array_size(reg->bits); i++) + printf(" %016llx", le64_to_cpu(reg->bits[i])); + printf("\n"); + + return 0; +} + +typedef int (*tnode_func)(int fd, struct scoutfs_treap_node *tnode); + +static int walk_treap(int fd, struct scoutfs_super_block *super, + struct scoutfs_treap_ref *ref, tnode_func func) +{ + struct scoutfs_treap_node *tnode; + u64 blkno; + void *blk; + u64 off; + int ret; + + if (!ref->gen) + return 0; + + off = le64_to_cpu(ref->off); + blkno = le64_to_cpu(super->ring_blkno) + (off >> SCOUTFS_BLOCK_SHIFT); + + blk = read_block(fd, blkno); + if (!blk) + return -ENOMEM; + + tnode = blk + (off & SCOUTFS_BLOCK_MASK); + + ret = func(fd, tnode); + if (ret == 0) + ret = walk_treap(fd, super, &tnode->left, func) ?: + walk_treap(fd, super, &tnode->right, func); free(blk); - return 0; + return ret; } static int print_super_blocks(int fd) { struct scoutfs_super_block *super; struct scoutfs_super_block recent = { .hdr.seq = 0 }; - unsigned long *seg_map; char uuid_str[37]; __le64 *counts; - u64 total_segs; - u64 longs; int ret = 0; int err; int i; @@ -418,29 +405,21 @@ static int print_super_blocks(int fd) super = &recent; - /* XXX :P */ - total_segs = le64_to_cpu(super->total_blocks) / SCOUTFS_SEGMENT_BLOCKS; - longs = DIV_ROUND_UP(total_segs, BITS_PER_LONG); - seg_map = calloc(longs, sizeof(unsigned long)); - if (!seg_map) - return -ENOMEM; - printf("manifest treap:\n"); - ret = print_treap_node(fd, super, TREAP_MANIFEST, - &super->manifest.root.ref, seg_map); + ret = walk_treap(fd, super, &super->manifest.root.ref, + print_manifest_entry); printf("alloc treap:\n"); - err = print_treap_node(fd, super, TREAP_ALLOC, - &super->alloc_treap_root.ref, NULL); + err = walk_treap(fd, super, &super->alloc_treap_root.ref, + print_alloc_region); if (err && !ret) ret = err; - err = print_segments(fd, seg_map, total_segs); + err = walk_treap(fd, super, &super->manifest.root.ref, + print_segment); if (err && !ret) ret = err; - free(seg_map); - return ret; } From e81c256a22f3dc631ab11ca8380c5b2b8070ff56 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Mon, 9 Jan 2017 16:25:12 -0800 Subject: [PATCH 075/235] Remove the bitops helpers We don't have any use for the bitops today, we'll resurrect this in simpler form if it's needed again. Signed-off-by: Zach Brown --- utils/src/bitops.c | 60 ---------------------------------- utils/src/bitops.h | 80 ---------------------------------------------- utils/src/mkfs.c | 1 - 3 files changed, 141 deletions(-) delete mode 100644 utils/src/bitops.c delete mode 100644 utils/src/bitops.h diff --git a/utils/src/bitops.c b/utils/src/bitops.c deleted file mode 100644 index 148051f4..00000000 --- a/utils/src/bitops.c +++ /dev/null @@ -1,60 +0,0 @@ -#include -#include -#include - -#include "sparse.h" -#include "util.h" -#include "bitops.h" - -#if (__SIZEOF_LONG__ == 8) -typedef __le64 lelong; -#define lelong_to_cpu le64_to_cpu - -#elif (__SIZEOF_LONG__ == 4) -typedef __le32 lelong; -#define lelong_to_cpu le32_to_cpu - -#else -#error "no sizeof long define?" -#endif - -/* - * I'd have used ffsl(), but defining _GNU_SOURCE caused build errors - * in glibc. The gcc builtin has the added bonus of returning 0 for the - * least significant bit instead of 1. - */ -#define ctzl __builtin_ctzl - -int find_next_bit_le(void *addr, long size, int start) -{ - lelong * __packed longs = addr; - unsigned long off = 0; - unsigned long masked; - - /* skip past whole longs before start */ - if (start >= BITS_PER_LONG) { - longs += start / BITS_PER_LONG; - off = start & ~(BITS_PER_LONG - 1); - start -= off; - } - - /* mask off low bits if start isn't aligned */ - if (start) { - masked = lelong_to_cpu(*longs) & ~((1 << (start)) - 1); - if (masked) - return min(ctzl(masked), size); - - off += BITS_PER_LONG; - longs++; - } - - /* then search remaining longs */ - while (off < size) { - if (*longs) - return min(off + ctzl(lelong_to_cpu(*longs)), size); - longs++; - off += BITS_PER_LONG; - } - - return size; -} diff --git a/utils/src/bitops.h b/utils/src/bitops.h deleted file mode 100644 index 7f2add29..00000000 --- a/utils/src/bitops.h +++ /dev/null @@ -1,80 +0,0 @@ -#ifndef _BITOPS_H_ -#define _BITOPS_H_ - -/* - * Implement little endian bitmaps in terms of native longs. __packed - * is used to avoid unaligned accesses. - */ - -typedef unsigned long * __packed ulong_ptr; - -#define BITS_PER_LONG (sizeof(long) * 8) -#if __BYTE_ORDER == __LITTLE_ENDIAN -#define BITOP_LE_SWIZZLE 0 -#else -#define BITOP_LE_SWIZZLE ((BITS_PER_LONG-1) & ~0x7) -#endif - -static inline ulong_ptr nr_word(int nr, ulong_ptr longs) -{ - return &longs[nr / BITS_PER_LONG]; -} - -static inline unsigned long nr_mask(int nr) -{ - return 1UL << (nr % BITS_PER_LONG); -} - -static inline int test_bit(int nr, ulong_ptr longs) -{ - return !!(*nr_word(nr, longs) & nr_mask(nr)); -} - -static inline void set_bit(int nr, ulong_ptr longs) -{ - *nr_word(nr, longs) |= nr_mask(nr); -} - -static inline void clear_bit(int nr, ulong_ptr longs) -{ - *nr_word(nr, longs) &= ~nr_mask(nr); -} - -static inline int test_bit_le(int nr, void *addr) -{ - return test_bit(nr ^ BITOP_LE_SWIZZLE, addr); -} - -static inline int test_and_set_bit_le(int nr, void *addr) -{ - int ret; - - nr ^= BITOP_LE_SWIZZLE; - ret = test_bit(nr, addr); - set_bit(nr, addr); - return ret; -} - -static inline void set_bit_le(int nr, void *addr) -{ - set_bit(nr ^ BITOP_LE_SWIZZLE, addr); -} - -static inline void clear_bit_le(int nr, void *addr) -{ - clear_bit(nr ^ BITOP_LE_SWIZZLE, addr); -} - -static inline int test_and_clear_bit_le(int nr, void *addr) -{ - int ret; - - nr ^= BITOP_LE_SWIZZLE; - ret = test_bit(nr, addr); - clear_bit(nr, addr); - return ret; -} - -int find_next_bit_le(void *addr, long size, int start); - -#endif diff --git a/utils/src/mkfs.c b/utils/src/mkfs.c index 555e193c..e3dd1675 100644 --- a/utils/src/mkfs.c +++ b/utils/src/mkfs.c @@ -17,7 +17,6 @@ #include "crc.h" #include "rand.h" #include "dev.h" -#include "bitops.h" #include "buddy.h" #include "item.h" From c4f2563cc1f3b2bd44327b8aeed48eb2a12f47b3 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Mon, 23 Jan 2017 18:06:48 -0800 Subject: [PATCH 076/235] Update tools to new segment item layout The segment item struct used to have fiddly packed offsets and lengths. Now it's just normal fields so we can work with them directly and get rid of the native item indirection. Signed-off-by: Zach Brown --- utils/src/format.h | 18 +++++++-------- utils/src/item.c | 57 ---------------------------------------------- utils/src/item.h | 22 ------------------ utils/src/mkfs.c | 15 ++++++------ utils/src/print.c | 36 +++++++++++++++++++++-------- 5 files changed, 43 insertions(+), 105 deletions(-) delete mode 100644 utils/src/item.c delete mode 100644 utils/src/item.h diff --git a/utils/src/format.h b/utils/src/format.h index 6495d457..a2931169 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -96,6 +96,8 @@ struct scoutfs_treap_root { */ #define SCOUTFS_MANIFEST_MAX_LEVEL 20 +#define SCOUTFS_MANIFEST_FANOUT 10 + struct scoutfs_manifest { struct scoutfs_treap_root root; __le64 level_counts[SCOUTFS_MANIFEST_MAX_LEVEL]; @@ -128,20 +130,18 @@ struct scoutfs_alloc_region { * aligned. This ensures that they won't cross page boundaries and we * can use pointers to them in the page vecs that make up segments without * funny business. - * - * We limit segment sizes to 8 megs (23 bits) and value lengths to 512 bytes - * (9 bits). The item offsets and lengths then take up 64 bits. - * - * We then operate on the items in on-stack nice native structs. */ struct scoutfs_segment_item { __le64 seq; - __le32 key_off_len; - __le32 val_off_len; + __le32 key_off; + __le32 val_off; + __le16 key_len; + __le16 val_len; + __u8 padding[11]; + __u8 flags; } __packed; -#define SCOUTFS_SEGMENT_ITEM_OFF_SHIFT 9 -#define SCOUTFS_SEGMENT_ITEM_LEN_MASK ((1 << SCOUTFS_SEGMENT_ITEM_OFF_SHIFT)-1) +#define SCOUTFS_ITEM_FLAG_DELETION (1 << 0) /* * Each large segment starts with a segment block that describes the diff --git a/utils/src/item.c b/utils/src/item.c deleted file mode 100644 index b6db8b15..00000000 --- a/utils/src/item.c +++ /dev/null @@ -1,57 +0,0 @@ -#include -#include - -#include "sparse.h" -#include "util.h" -#include "format.h" -#include "item.h" - -/* utils uses bit contiguous allocations */ -static void *off_ptr(struct scoutfs_segment_block *sblk, u32 off) -{ - return (char *)sblk + off; -} - -static u32 pos_off(struct scoutfs_segment_block *sblk, u32 pos) -{ - return offsetof(struct scoutfs_segment_block, items[pos]); -} - -static void *pos_ptr(struct scoutfs_segment_block *sblk, u32 pos) -{ - return off_ptr(sblk, pos_off(sblk, pos)); -} - -void load_item(struct scoutfs_segment_block *sblk, u32 pos, - struct native_item *item) -{ - struct scoutfs_segment_item *sitem = pos_ptr(sblk, pos); - u32 packed; - - item->seq = le64_to_cpu(sitem->seq); - - packed = le32_to_cpu(sitem->key_off_len); - item->key_off = packed >> SCOUTFS_SEGMENT_ITEM_OFF_SHIFT; - item->key_len = packed & SCOUTFS_SEGMENT_ITEM_LEN_MASK; - - packed = le32_to_cpu(sitem->val_off_len); - item->val_off = packed >> SCOUTFS_SEGMENT_ITEM_OFF_SHIFT; - item->val_len = packed & SCOUTFS_SEGMENT_ITEM_LEN_MASK; -} - -void store_item(struct scoutfs_segment_block *sblk, u32 pos, - struct native_item *item) -{ - struct scoutfs_segment_item *sitem = pos_ptr(sblk, pos); - u32 packed; - - sitem->seq = cpu_to_le64(item->seq); - - packed = (item->key_off << SCOUTFS_SEGMENT_ITEM_OFF_SHIFT) | - (item->key_len & SCOUTFS_SEGMENT_ITEM_LEN_MASK); - sitem->key_off_len = cpu_to_le32(packed); - - packed = (item->val_off << SCOUTFS_SEGMENT_ITEM_OFF_SHIFT) | - (item->val_len & SCOUTFS_SEGMENT_ITEM_LEN_MASK); - sitem->val_off_len = cpu_to_le32(packed); -} diff --git a/utils/src/item.h b/utils/src/item.h deleted file mode 100644 index 7c307eee..00000000 --- a/utils/src/item.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef _ITEM_H_ -#define _ITEM_H_ - -/* - * The persistent item fields that are stored in the segment are packed - * with funny precision. We translate those to and from a much more - * natural native representation of the fields. - */ -struct native_item { - u64 seq; - u32 key_off; - u32 val_off; - u16 key_len; - u16 val_len; -}; - -void load_item(struct scoutfs_segment_block *sblk, u32 pos, - struct native_item *item); -void store_item(struct scoutfs_segment_block *sblk, u32 pos, - struct native_item *item); - -#endif diff --git a/utils/src/mkfs.c b/utils/src/mkfs.c index e3dd1675..db310b14 100644 --- a/utils/src/mkfs.c +++ b/utils/src/mkfs.c @@ -18,7 +18,6 @@ #include "rand.h" #include "dev.h" #include "buddy.h" -#include "item.h" static int write_raw_block(int fd, u64 blkno, void *blk) { @@ -95,7 +94,7 @@ static int write_new_fs(char *path, int fd) struct scoutfs_segment_block *sblk; struct scoutfs_manifest_entry *ment; struct scoutfs_treap_node *node; - struct native_item item; + struct scoutfs_segment_item *item; struct timeval tv; char uuid_str[37]; void *ring; @@ -169,15 +168,15 @@ static int write_new_fs(char *path, int fd) root_ikey.type = SCOUTFS_INODE_KEY; root_ikey.ino = cpu_to_be64(SCOUTFS_ROOT_INO); + item = &sblk->items[0]; ikey = (void *)&sblk->items[1]; inode = (void *)(ikey + 1); - item.seq = 1; - item.key_off = (long)ikey - (long)sblk; - item.val_off = (long)inode - (long)sblk; - item.key_len = sizeof(struct scoutfs_inode_key); - item.val_len = sizeof(struct scoutfs_inode); - store_item(sblk, 0, &item); + item->seq = cpu_to_le64(1); + item->key_off = cpu_to_le32((long)ikey - (long)sblk); + item->val_off = cpu_to_le32((long)inode - (long)sblk); + item->key_len = cpu_to_le16(sizeof(struct scoutfs_inode_key)); + item->val_len = cpu_to_le16(sizeof(struct scoutfs_inode)); *ikey = root_ikey; diff --git a/utils/src/print.c b/utils/src/print.c index f92c8af3..412cf3a7 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -16,7 +16,6 @@ #include "cmd.h" #include "crc.h" #include "buddy.h" -#include "item.h" /* XXX maybe these go somewhere */ #define SKF "%llu.%u.%llu" @@ -198,29 +197,48 @@ static print_func_t printers[] = { [SCOUTFS_READDIR_KEY] = print_readdir, }; +/* utils uses big contiguous allocations */ +static void *off_ptr(struct scoutfs_segment_block *sblk, u32 off) +{ + return (char *)sblk + off; +} + +static u32 pos_off(struct scoutfs_segment_block *sblk, u32 pos) +{ + return offsetof(struct scoutfs_segment_block, items[pos]); +} + +static void *pos_ptr(struct scoutfs_segment_block *sblk, u32 pos) +{ + return off_ptr(sblk, pos_off(sblk, pos)); +} + static void print_item(struct scoutfs_segment_block *sblk, u32 pos) { print_func_t printer; - struct native_item item; + struct scoutfs_segment_item *item; void *key; void *val; __u8 type; - load_item(sblk, pos, &item); + item = pos_ptr(sblk, pos); - key = (char *)sblk + item.key_off; - val = (char *)sblk + item.val_off; + key = (char *)sblk + le32_to_cpu(item->key_off); + val = (char *)sblk + le32_to_cpu(item->val_off); type = *(__u8 *)key; printer = type < array_size(printers) ? printers[type] : NULL; printf(" [%u]: type %u seq %llu key_off %u val_off %u key_len %u " - "val_len %u%s\n", - pos, type, item.seq, item.key_off, item.val_off, item.key_len, - item.val_len, printer ? "" : " (unrecognized type)"); + "val_len %u flags %x%s\n", + pos, type, le64_to_cpu(item->seq), le32_to_cpu(item->key_off), + le32_to_cpu(item->val_off), le16_to_cpu(item->key_len), + le16_to_cpu(item->val_len), item->flags, + printer ? "" : " (unrecognized type)"); if (printer) - printer(key, item.key_len, val, item.val_len); + printer(key, le16_to_cpu(item->key_len), + val, le16_to_cpu(item->val_len)); } static void print_segment_block(struct scoutfs_segment_block *sblk) From 38c8a4901f07a83dbc1136e9bbc5bb3f5ce68609 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Wed, 25 Jan 2017 11:16:21 -0800 Subject: [PATCH 077/235] Print orphan items Signed-off-by: Zach Brown --- utils/src/format.h | 6 ++++++ utils/src/print.c | 8 ++++++++ 2 files changed, 14 insertions(+) diff --git a/utils/src/format.h b/utils/src/format.h index a2931169..28125b6c 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -262,6 +262,12 @@ struct scoutfs_readdir_key { __be64 pos; } __packed; +/* no value */ +struct scoutfs_orphan_key { + __u8 type; + __be64 ino; +} __packed; + struct scoutfs_btree_root { u8 height; struct scoutfs_block_ref ref; diff --git a/utils/src/print.c b/utils/src/print.c index 412cf3a7..7c5b1321 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -103,6 +103,13 @@ static void print_inode(void *key, int key_len, void *val, int val_len) le32_to_cpu(inode->mtime.nsec)); } +static void print_orphan(void *key, int key_len, void *val, int val_len) +{ + struct scoutfs_orphan_key *okey = key; + + printf(" orphan: ino %llu\n", be64_to_cpu(okey->ino)); +} + #if 0 static void print_xattr(struct scoutfs_xattr *xat) @@ -193,6 +200,7 @@ typedef void (*print_func_t)(void *key, int key_len, void *val, int val_len); static print_func_t printers[] = { [SCOUTFS_INODE_KEY] = print_inode, + [SCOUTFS_ORPHAN_KEY] = print_orphan, [SCOUTFS_DIRENT_KEY] = print_dirent, [SCOUTFS_READDIR_KEY] = print_readdir, }; From 52291b2c755704acde718dae3a2d3eed418e1a7c Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Wed, 25 Jan 2017 11:17:30 -0800 Subject: [PATCH 078/235] Update format for readdir_pos We now track each parent dir's next readdir pos and the readdir pos of each dirent. Signed-off-by: Zach Brown --- utils/src/format.h | 3 +++ utils/src/mkfs.c | 3 ++- utils/src/print.c | 8 +++++--- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index 28125b6c..f2480fc3 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -380,6 +380,7 @@ struct scoutfs_inode { __le64 blocks; __le64 link_counter; __le64 data_version; + __le64 next_readdir_pos; __le32 nlink; __le32 uid; __le32 gid; @@ -403,6 +404,7 @@ struct scoutfs_inode { struct scoutfs_dirent { __le64 ino; __le64 counter; + __le64 readdir_pos; __u8 type; __u8 name[0]; } __packed; @@ -435,6 +437,7 @@ struct scoutfs_dirent { #define SCOUTFS_DIRENT_OFF_BITS 31 #define SCOUTFS_DIRENT_OFF_MASK ((1U << SCOUTFS_DIRENT_OFF_BITS) - 1) /* getdents returns next pos with an entry, no entry at (f_pos)~0 */ +#define SCOUTFS_DIRENT_FIRST_POS 2 #define SCOUTFS_DIRENT_LAST_POS (INT_MAX - 1) enum { diff --git a/utils/src/mkfs.c b/utils/src/mkfs.c index db310b14..57174c9f 100644 --- a/utils/src/mkfs.c +++ b/utils/src/mkfs.c @@ -180,7 +180,8 @@ static int write_new_fs(char *path, int fd) *ikey = root_ikey; - inode->nlink = cpu_to_le32(2); + inode->next_readdir_pos = cpu_to_le64(2); + inode->nlink = cpu_to_le32(SCOUTFS_DIRENT_FIRST_POS); inode->mode = cpu_to_le32(0755 | 0040000); inode->atime.sec = cpu_to_le64(tv.tv_sec); inode->atime.nsec = cpu_to_le32(tv.tv_usec * 1000); diff --git a/utils/src/print.c b/utils/src/print.c index 7c5b1321..30749431 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -85,7 +85,7 @@ static void print_inode(void *key, int key_len, void *val, int val_len) printf(" inode: ino %llu size %llu blocks %llu lctr %llu nlink %u\n" " uid %u gid %u mode 0%o rdev 0x%x\n" - " salt 0x%x data_version %llu\n" + " salt 0x%x next_readdir_pos %llu data_version %llu\n" " atime %llu.%08u ctime %llu.%08u\n" " mtime %llu.%08u\n", be64_to_cpu(ikey->ino), @@ -94,6 +94,7 @@ static void print_inode(void *key, int key_len, void *val, int val_len) le32_to_cpu(inode->nlink), le32_to_cpu(inode->uid), le32_to_cpu(inode->gid), le32_to_cpu(inode->mode), le32_to_cpu(inode->rdev), le32_to_cpu(inode->salt), + le64_to_cpu(inode->next_readdir_pos), le64_to_cpu(inode->data_version), le64_to_cpu(inode->atime.sec), le32_to_cpu(inode->atime.nsec), @@ -149,9 +150,10 @@ static void print_dirent(void *key, int key_len, void *val, int val_len) unsigned int name_len = key_len - sizeof(*dkey); u8 *name = global_printable_name(dkey->name, name_len); - printf(" dirent: dir ino %llu type %u targ ino %llu\n" + printf(" dirent: dir ino %llu type %u rdpos %llu targ ino %llu\n" " name %s\n", - be64_to_cpu(dkey->ino), dent->type, le64_to_cpu(dent->ino), + be64_to_cpu(dkey->ino), dent->type, + le64_to_cpu(dent->readdir_pos), le64_to_cpu(dent->ino), name); } From 44f8551fb6fca0bf5ba38cbcc6159ce7528deee8 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Wed, 1 Feb 2017 10:58:17 -0800 Subject: [PATCH 079/235] Print data items Signed-off-by: Zach Brown --- utils/src/format.h | 8 ++++++++ utils/src/print.c | 9 +++++++++ 2 files changed, 17 insertions(+) diff --git a/utils/src/format.h b/utils/src/format.h index f2480fc3..2ba9cbd8 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -238,6 +238,7 @@ struct scoutfs_key { #define SCOUTFS_SYMLINK_KEY 8 #define SCOUTFS_EXTENT_KEY 9 #define SCOUTFS_ORPHAN_KEY 10 +#define SCOUTFS_DATA_KEY 11 #define SCOUTFS_MAX_UNUSED_KEY 255 #define SCOUTFS_MAX_ITEM_LEN 512 @@ -268,6 +269,13 @@ struct scoutfs_orphan_key { __be64 ino; } __packed; +/* value is data payload bytes */ +struct scoutfs_data_key { + __u8 type; + __be64 ino; + __be64 block; +} __packed; + struct scoutfs_btree_root { u8 height; struct scoutfs_block_ref ref; diff --git a/utils/src/print.c b/utils/src/print.c index 30749431..9f486ccc 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -171,6 +171,14 @@ static void print_readdir(void *key, int key_len, void *val, int val_len) name); } +static void print_data(void *key, int key_len, void *val, int val_len) +{ + struct scoutfs_data_key *dat = key; + + printf(" data: ino %llu block %llu\n", + be64_to_cpu(dat->ino), be64_to_cpu(dat->block)); +} + #if 0 static void print_link_backref(struct scoutfs_link_backref *lref, unsigned int val_len) @@ -205,6 +213,7 @@ static print_func_t printers[] = { [SCOUTFS_ORPHAN_KEY] = print_orphan, [SCOUTFS_DIRENT_KEY] = print_dirent, [SCOUTFS_READDIR_KEY] = print_readdir, + [SCOUTFS_DATA_KEY] = print_data, }; /* utils uses big contiguous allocations */ From acda5a3bf108d48b5c3297b1c77a8ef5adce838b Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Wed, 1 Feb 2017 13:30:44 -0800 Subject: [PATCH 080/235] Add support for free_segs in super The allocator records the total number of free segments in the super block. Signed-off-by: Zach Brown --- utils/src/format.h | 3 +++ utils/src/mkfs.c | 1 + utils/src/print.c | 5 +++-- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index 2ba9cbd8..4a6b15bf 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -23,6 +23,8 @@ #define SCOUTFS_SEGMENT_MASK (SCOUTFS_SEGMENT_SIZE - 1) #define SCOUTFS_SEGMENT_PAGES (SCOUTFS_SEGMENT_SIZE / PAGE_SIZE) #define SCOUTFS_SEGMENT_BLOCKS (SCOUTFS_SEGMENT_SIZE / SCOUTFS_BLOCK_SIZE) +#define SCOUTFS_SEGMENT_BLOCK_SHIFT \ + (SCOUTFS_SEGMENT_SHIFT - SCOUTFS_BLOCK_SHIFT) #define SCOUTFS_PAGES_PER_BLOCK (SCOUTFS_BLOCK_SIZE / PAGE_SIZE) #define SCOUTFS_BLOCK_PAGE_ORDER (SCOUTFS_BLOCK_SHIFT - PAGE_SHIFT) @@ -350,6 +352,7 @@ struct scoutfs_super_block { __le64 next_ino; __le64 alloc_uninit; __le64 total_segs; + __le64 free_segs; __le64 total_blocks; __le64 free_blocks; __le64 ring_blkno; diff --git a/utils/src/mkfs.c b/utils/src/mkfs.c index 57174c9f..26f58ea3 100644 --- a/utils/src/mkfs.c +++ b/utils/src/mkfs.c @@ -159,6 +159,7 @@ static int write_new_fs(char *path, int fd) /* 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); diff --git a/utils/src/print.c b/utils/src/print.c index 9f486ccc..27243ca8 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -408,7 +408,7 @@ static int print_super_blocks(int fd) printf(" next_ino %llu total_blocks %llu free_blocks %llu\n" " ring_blkno %llu ring_blocks %llu ring_tail_block %llu\n" " ring_gen %llu alloc_uninit %llu total_segs %llu\n" - " next_seg_seq %llu\n", + " next_seg_seq %llu free_segs %llu\n", le64_to_cpu(super->next_ino), le64_to_cpu(super->total_blocks), le64_to_cpu(super->free_blocks), @@ -418,7 +418,8 @@ static int print_super_blocks(int fd) le64_to_cpu(super->ring_gen), le64_to_cpu(super->alloc_uninit), le64_to_cpu(super->total_segs), - le64_to_cpu(super->next_seg_seq)); + le64_to_cpu(super->next_seg_seq), + le64_to_cpu(super->free_segs)); printf(" alloc root:"); print_treap_ref(&super->alloc_treap_root.ref); printf("\n"); From 16da3c182a79b476a52ab113ab58e15cc9b1a0bf Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Fri, 3 Feb 2017 13:27:26 -0800 Subject: [PATCH 081/235] Add printing link backref items Signed-off-by: Zach Brown --- utils/src/format.h | 21 ++++++++++----------- utils/src/print.c | 14 +++++++++----- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index 4a6b15bf..0d5edcfc 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -265,6 +265,14 @@ struct scoutfs_readdir_key { __be64 pos; } __packed; +/* value is empty */ +struct scoutfs_link_backref_key { + __u8 type; + __be64 ino; + __be64 dir_ino; + __u8 name[0]; +} __packed; + /* no value */ struct scoutfs_orphan_key { __u8 type; @@ -479,17 +487,8 @@ struct scoutfs_extent { #define SCOUTFS_EXTENT_FLAG_OFFLINE (1 << 0) -/* - * link backrefs give us a way to find all the hard links that refer - * to a target inode. They're stored at an offset determined by an - * advancing counter in their inode. - */ -struct scoutfs_link_backref { - __le64 ino; - __le64 offset; -} __packed; - +/* ino_path can search for backref items with a null term */ #define SCOUTFS_MAX_KEY_SIZE \ - offsetof(struct scoutfs_dirent_key, name[SCOUTFS_NAME_LEN]) + offsetof(struct scoutfs_link_backref_key, name[SCOUTFS_NAME_LEN + 1]) #endif diff --git a/utils/src/print.c b/utils/src/print.c index 27243ca8..2f98717f 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -179,14 +179,17 @@ static void print_data(void *key, int key_len, void *val, int val_len) be64_to_cpu(dat->ino), be64_to_cpu(dat->block)); } -#if 0 -static void print_link_backref(struct scoutfs_link_backref *lref, - unsigned int val_len) +static void print_link_backref(void *key, int key_len, void *val, int val_len) { - printf(" lref: ino: %llu offset: %llu\n", - le64_to_cpu(lref->ino), le64_to_cpu(lref->offset)); + struct scoutfs_link_backref_key *lbkey = key; + unsigned int name_len = key_len - sizeof(*lbkey); + u8 *name = global_printable_name(lbkey->name, name_len); + + printf(" lbref: ino: %llu dir_ino %llu name %s\n", + be64_to_cpu(lbkey->ino), be64_to_cpu(lbkey->dir_ino), name); } +#if 0 /* for now show the raw component items not the whole path */ static void print_symlink(char *str, unsigned int val_len) { @@ -213,6 +216,7 @@ static print_func_t printers[] = { [SCOUTFS_ORPHAN_KEY] = print_orphan, [SCOUTFS_DIRENT_KEY] = print_dirent, [SCOUTFS_READDIR_KEY] = print_readdir, + [SCOUTFS_LINK_BACKREF_KEY] = print_link_backref, [SCOUTFS_DATA_KEY] = print_data, }; From 02993a2dd7fd7dda39ceac5ee52c447851bcadcd Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Fri, 3 Feb 2017 13:34:38 -0800 Subject: [PATCH 082/235] Update ino_path for the large cursor Previously we could iterate over backref items with a small u64. Now we need a larger opaque buffer. Signed-off-by: Zach Brown --- utils/src/ino_path.c | 15 +++++++++++-- utils/src/ioctl.h | 51 +++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 61 insertions(+), 5 deletions(-) diff --git a/utils/src/ino_path.c b/utils/src/ino_path.c index 69240a4f..69c50892 100644 --- a/utils/src/ino_path.c +++ b/utils/src/ino_path.c @@ -18,7 +18,8 @@ static int ino_path_cmd(int argc, char **argv) { struct scoutfs_ioctl_ino_path args; char *endptr = NULL; - char *path; + char *path = NULL; + char *curs = NULL; u64 ino; int ret; int fd; @@ -51,9 +52,18 @@ static int ino_path_cmd(int argc, char **argv) goto out; } + curs = calloc(1, SCOUTFS_IOC_INO_PATH_CURSOR_BYTES); + if (!curs) { + fprintf(stderr, "couldn't allocate %ld byte cursor\n", + SCOUTFS_IOC_INO_PATH_CURSOR_BYTES); + ret = -ENOMEM; + goto out; + } + args.ino = ino; - args.ctr = 0; + args.cursor_ptr = (intptr_t)curs; args.path_ptr = (intptr_t)path; + args.cursor_bytes = SCOUTFS_IOC_INO_PATH_CURSOR_BYTES; args.path_bytes = PATH_MAX; do { ret = ioctl(fd, SCOUTFS_IOC_INO_PATH, &args); @@ -68,6 +78,7 @@ static int ino_path_cmd(int argc, char **argv) } out: free(path); + free(curs); close(fd); return ret; }; diff --git a/utils/src/ioctl.h b/utils/src/ioctl.h index 94963273..5a047328 100644 --- a/utils/src/ioctl.h +++ b/utils/src/ioctl.h @@ -1,6 +1,8 @@ #ifndef _SCOUTFS_IOCTL_H_ #define _SCOUTFS_IOCTL_H_ +#include "format.h" + /* XXX I have no idea how these are chosen. */ #define SCOUTFS_IOCTL_MAGIC 's' @@ -24,18 +26,61 @@ struct scoutfs_ioctl_inodes_since { #define SCOUTFS_IOC_INODES_SINCE _IOW(SCOUTFS_IOCTL_MAGIC, 1, \ struct scoutfs_ioctl_inodes_since) -/* returns bytes of path buffer set starting at _off, including null */ +/* + * Fill the path buffer with the next path to the target inode. An + * iteration cursor is stored in the cursor buffer which advances + * through the paths to the inode at each call. + * + * @ino: The target ino that we're finding paths to. Constant across + * all the calls that make up an iteration over all the inode's paths. + * + * @cursor_ptr: A pointer to the buffer that will hold the iteration + * cursor. It must be initialized to 0 before iterating. Each call + * modifies it to skip past the result of that call. + * + * @cusur_bytes: The length of the cursor buffer. Must be + * SCOUTFS_IOC_INO_PATH_CURSOR_BYTES. + * + * @path_ptr: The buffer to store each found path. + * + * @path_bytes: The size of the buffer that will the found path + * including null termination. (PATH_MAX is a solid choice.) + * + * This only walks back through full hard links. None of the returned + * paths will reflect symlinks to components in the path. + * + * This doesn't ensure that the caller has permissions to traverse the + * returned paths to the inode. It requires CAP_DAC_READ_SEARCH which + * bypasses permissions checking. + * + * ENAMETOOLONG is returned when the next path found from the cursor + * doesn't fit in the path buffer. + * + * This call is not serialized with any modification (create, rename, + * unlink) of the path components. It will return all the paths that + * were stable both before and after the call. It may or may not return + * paths which are created or unlinked during the call. + * + * The number of bytes in the path, including the null terminator, are + * returned when a path is found. 0 is returned when there are no more + * paths to the link to the inode from the cursor. + */ struct scoutfs_ioctl_ino_path { __u64 ino; - __u64 ctr; /* init to 0, set to next */ + __u64 cursor_ptr; __u64 path_ptr; - __u16 path_bytes; /* total buffer space, including null term */ + __u16 cursor_bytes; + __u16 path_bytes; } __packed; +#define SCOUTFS_IOC_INO_PATH_CURSOR_BYTES \ + (sizeof(u64) + SCOUTFS_NAME_LEN + 1) + /* Get a single path from the root to the given inode number */ #define SCOUTFS_IOC_INO_PATH _IOW(SCOUTFS_IOCTL_MAGIC, 2, \ struct scoutfs_ioctl_ino_path) + /* XXX might as well include a seq? 0 for current behaviour? */ struct scoutfs_ioctl_find_xattr { __u64 first_ino; From 13b2d9bb882ab23a09206a09caf4232e132bf5e5 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Thu, 9 Feb 2017 15:41:48 -0800 Subject: [PATCH 083/235] Remove find_xattr commands We're no longer maintaining xattr backrefs. Signed-off-by: Zach Brown --- utils/src/find_xattr.c | 134 ----------------------------------------- utils/src/ioctl.h | 24 ++------ 2 files changed, 4 insertions(+), 154 deletions(-) delete mode 100644 utils/src/find_xattr.c diff --git a/utils/src/find_xattr.c b/utils/src/find_xattr.c deleted file mode 100644 index 9d489d07..00000000 --- a/utils/src/find_xattr.c +++ /dev/null @@ -1,134 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "sparse.h" -#include "util.h" -#include "ioctl.h" -#include "format.h" -#include "cmd.h" - -static int find_xattrs(bool find_name, int argc, char **argv) -{ - struct scoutfs_ioctl_find_xattr find; - char *endptr; - u64 first; - u64 last; - u64 *ino; - int ret; - int fd; - int ioc; - int count; - int i; - - if (find_name) - ioc = SCOUTFS_IOC_FIND_XATTR_NAME; - else - ioc = SCOUTFS_IOC_FIND_XATTR_VAL; - - if (argc != 4) { - fprintf(stderr, "must specify ino range, xattr str, and path\n"); - return -EINVAL; - } - - first = strtoull(argv[0], &endptr, 0); - if (*endptr != '\0' || - ((first == LLONG_MIN || first == LLONG_MAX) && errno == ERANGE)) { - fprintf(stderr, "error parsing inode number '%s'\n", - argv[0]); - return -EINVAL; - } - - last = strtoull(argv[1], &endptr, 0); - if (*endptr != '\0' || - ((last == LLONG_MIN || last == LLONG_MAX) && errno == ERANGE)) { - fprintf(stderr, "error parsing inode number '%s'\n", - argv[1]); - return -EINVAL; - } - - fd = open(argv[3], O_RDONLY); - if (fd < 0) { - ret = -errno; - fprintf(stderr, "failed to open '%s': %s (%d)\n", - argv[3], strerror(errno), errno); - return ret; - } - - count = 256; - ino = calloc(count, sizeof(*ino)); - if (!ino) { - fprintf(stderr, "couldn't allocate buffer for results\n"); - ret = -ENOMEM; - goto out; - } - - find.first_ino = first; - find.last_ino = last; - find.str_ptr = (unsigned long)argv[2]; - find.str_len = strlen(argv[2]); - find.ino_ptr = (unsigned long)ino; - find.ino_count = count; - - if (find.str_len > SCOUTFS_MAX_XATTR_LEN) { - fprintf(stderr, "xattr string len %u > %d\n", - find.str_len, SCOUTFS_MAX_XATTR_LEN); - ret = -EINVAL; - goto out; - } - - do { - ret = ioctl(fd, ioc, &find); - if (ret < 0) { - ret = -errno; - fprintf(stderr, "inodes_find_xattr ioctl failed: %s (%d)\n", - strerror(errno), errno); - goto out; - } - - for (i = 0; i < ret; i++) { - printf("%llu\n", ino[i]); - find.first_ino = ino[i] + 1; - - if (find.first_ino == 0) { - ret = 0; - break; - } - } - } while (ret > 0); - -out: - free(ino); - close(fd); - - return ret; -}; - -static int find_xattr_name(int argc, char **argv) -{ - return find_xattrs(true, argc, argv); -} - -static int find_xattr_val(int argc, char **argv) -{ - return find_xattrs(false, argc, argv); -} - -static void __attribute__((constructor)) find_xattr_ctor(void) -{ - cmd_register("find-xattr-name", " ", - "print inodes that might contain xattr name", - find_xattr_name); - cmd_register("find-xattr-value", " ", - "print inodes that might contain xattr value", - find_xattr_val); -} diff --git a/utils/src/ioctl.h b/utils/src/ioctl.h index 5a047328..190199fc 100644 --- a/utils/src/ioctl.h +++ b/utils/src/ioctl.h @@ -80,26 +80,10 @@ struct scoutfs_ioctl_ino_path { #define SCOUTFS_IOC_INO_PATH _IOW(SCOUTFS_IOCTL_MAGIC, 2, \ struct scoutfs_ioctl_ino_path) - -/* XXX might as well include a seq? 0 for current behaviour? */ -struct scoutfs_ioctl_find_xattr { - __u64 first_ino; - __u64 last_ino; - __u64 str_ptr; - __u32 str_len; - __u64 ino_ptr; - __u32 ino_count; -} __packed; - -#define SCOUTFS_IOC_FIND_XATTR_NAME _IOW(SCOUTFS_IOCTL_MAGIC, 3, \ - struct scoutfs_ioctl_find_xattr) -#define SCOUTFS_IOC_FIND_XATTR_VAL _IOW(SCOUTFS_IOCTL_MAGIC, 4, \ - struct scoutfs_ioctl_find_xattr) - -#define SCOUTFS_IOC_INODE_DATA_SINCE _IOW(SCOUTFS_IOCTL_MAGIC, 5, \ +#define SCOUTFS_IOC_INODE_DATA_SINCE _IOW(SCOUTFS_IOCTL_MAGIC, 3, \ struct scoutfs_ioctl_inodes_since) -#define SCOUTFS_IOC_DATA_VERSION _IOW(SCOUTFS_IOCTL_MAGIC, 6, u64) +#define SCOUTFS_IOC_DATA_VERSION _IOW(SCOUTFS_IOCTL_MAGIC, 4, u64) struct scoutfs_ioctl_release { __u64 offset; @@ -107,7 +91,7 @@ struct scoutfs_ioctl_release { __u64 data_version; } __packed; -#define SCOUTFS_IOC_RELEASE _IOW(SCOUTFS_IOCTL_MAGIC, 7, \ +#define SCOUTFS_IOC_RELEASE _IOW(SCOUTFS_IOCTL_MAGIC, 5, \ struct scoutfs_ioctl_release) struct scoutfs_ioctl_stage { @@ -117,7 +101,7 @@ struct scoutfs_ioctl_stage { __s32 count; } __packed; -#define SCOUTFS_IOC_STAGE _IOW(SCOUTFS_IOCTL_MAGIC, 8, \ +#define SCOUTFS_IOC_STAGE _IOW(SCOUTFS_IOCTL_MAGIC, 6, \ struct scoutfs_ioctl_stage) #endif From 77d0268cb28de75fd4410a4cb3df2e3297a0df1b Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Thu, 9 Feb 2017 15:47:02 -0800 Subject: [PATCH 084/235] Add printing xattrs For now we only print the xattr names, not the values. Signed-off-by: Zach Brown --- utils/src/format.h | 37 +++++++++++++++++++++++++------------ utils/src/print.c | 34 +++++++++++++++------------------- 2 files changed, 40 insertions(+), 31 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index 0d5edcfc..83c4d5d8 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -231,9 +231,7 @@ struct scoutfs_key { * have to stress about cleverly allocating the types. */ #define SCOUTFS_INODE_KEY 1 -#define SCOUTFS_XATTR_KEY 2 -#define SCOUTFS_XATTR_NAME_HASH_KEY 3 -#define SCOUTFS_XATTR_VAL_HASH_KEY 4 +#define SCOUTFS_XATTR_KEY 3 #define SCOUTFS_DIRENT_KEY 5 #define SCOUTFS_READDIR_KEY 6 #define SCOUTFS_LINK_BACKREF_KEY 7 @@ -286,6 +284,23 @@ struct scoutfs_data_key { __be64 block; } __packed; +/* value is each item's part of the full xattr value for the off/len */ +struct scoutfs_xattr_key { + __u8 type; + __be64 ino; + __u8 name[0]; +} __packed; + +struct scoutfs_xattr_key_footer { + __u8 null; + __u8 part; +} __packed; + +struct scoutfs_xattr_val_header { + __le16 part_len; + __u8 last_part; +} __packed; + struct scoutfs_btree_root { u8 height; struct scoutfs_block_ref ref; @@ -447,6 +462,13 @@ struct scoutfs_dirent { /* S32_MAX avoids the (int) sign bit and might avoid sloppy bugs */ #define SCOUTFS_LINK_MAX S32_MAX +#define SCOUTFS_XATTR_MAX_NAME_LEN 255 +#define SCOUTFS_XATTR_MAX_SIZE 65536 +#define SCOUTFS_XATTR_PART_SIZE \ + (SCOUTFS_BLOCK_SIZE - sizeof(struct scoutfs_xattr_val_header)) +#define SCOUTFS_XATTR_MAX_PARTS \ + DIV_ROUND_UP(SCOUTFS_XATTR_MAX_SIZE, SCOUTFS_XATTR_PART_SIZE) + /* * We only use 31 bits for readdir positions so that we don't confuse * old signed 32bit f_pos applications or those on the other side of @@ -470,15 +492,6 @@ enum { SCOUTFS_DT_WHT, }; -#define SCOUTFS_MAX_XATTR_LEN 255 -#define SCOUTFS_XATTR_NAME_HASH_MASK 7ULL - -struct scoutfs_xattr { - __u8 name_len; - __u8 value_len; - __u8 name[0]; -} __packed; - struct scoutfs_extent { __le64 blkno; __le64 len; diff --git a/utils/src/print.c b/utils/src/print.c index 2f98717f..ac8a5975 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -111,25 +111,6 @@ static void print_orphan(void *key, int key_len, void *val, int val_len) printf(" orphan: ino %llu\n", be64_to_cpu(okey->ino)); } -#if 0 - -static void print_xattr(struct scoutfs_xattr *xat) -{ - /* XXX check lengths */ - - printf(" xattr: name %.*s val_len %u\n", - xat->name_len, xat->name, xat->value_len); -} - -static void print_xattr_val_hash(__le64 *refcount) -{ - /* XXX check lengths */ - - printf(" xattr_val_hash: refcount %llu\n", - le64_to_cpu(*refcount)); -} -#endif - static u8 *global_printable_name(u8 *name, int name_len) { static u8 name_buf[SCOUTFS_NAME_LEN + 1]; @@ -143,6 +124,20 @@ static u8 *global_printable_name(u8 *name, int name_len) return name_buf; } +static void print_xattr(void *key, int key_len, void *val, int val_len) +{ + struct scoutfs_xattr_key *xkey = key; + struct scoutfs_xattr_key_footer *foot = key + key_len - sizeof(*foot); + struct scoutfs_xattr_val_header *vh = val; + unsigned int name_len = key_len - sizeof(*xkey) - sizeof(*foot); + u8 *name = global_printable_name(xkey->name, name_len); + + printf(" xattr: ino %llu part %u part_len %u last_part %u\n" + " name %s\n", + be64_to_cpu(xkey->ino), foot->part, le16_to_cpu(vh->part_len), + vh->last_part, name); +} + static void print_dirent(void *key, int key_len, void *val, int val_len) { struct scoutfs_dirent_key *dkey = key; @@ -213,6 +208,7 @@ typedef void (*print_func_t)(void *key, int key_len, void *val, int val_len); static print_func_t printers[] = { [SCOUTFS_INODE_KEY] = print_inode, + [SCOUTFS_XATTR_KEY] = print_xattr, [SCOUTFS_ORPHAN_KEY] = print_orphan, [SCOUTFS_DIRENT_KEY] = print_dirent, [SCOUTFS_READDIR_KEY] = print_readdir, From 2e2ee3b2f141569b7f5548890d1e06013515acc2 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Thu, 9 Feb 2017 16:25:37 -0800 Subject: [PATCH 085/235] Print symlink items Signed-off-by: Zach Brown --- utils/src/format.h | 6 ++++++ utils/src/print.c | 13 +++++++++---- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index 83c4d5d8..77945486 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -301,6 +301,12 @@ struct scoutfs_xattr_val_header { __u8 last_part; } __packed; +/* value is the null terminated target path */ +struct scoutfs_symlink_key { + __u8 type; + __be64 ino; +} __packed; + struct scoutfs_btree_root { u8 height; struct scoutfs_block_ref ref; diff --git a/utils/src/print.c b/utils/src/print.c index ac8a5975..f278ea99 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -184,13 +184,17 @@ static void print_link_backref(void *key, int key_len, void *val, int val_len) be64_to_cpu(lbkey->ino), be64_to_cpu(lbkey->dir_ino), name); } -#if 0 -/* for now show the raw component items not the whole path */ -static void print_symlink(char *str, unsigned int val_len) +static void print_symlink(void *key, int key_len, void *val, int val_len) { - printf(" symlink: %.*s\n", val_len, str); + struct scoutfs_symlink_key *skey = key; + u8 *name = global_printable_name(val, val_len - 1); + + printf(" symlink: ino %llu\n" + " target %s\n", + be64_to_cpu(skey->ino), name); } +#if 0 #define EXT_FLAG(f, flags, str) \ (flags & f) ? str : "", (flags & (f - 1)) ? "|" : "" @@ -212,6 +216,7 @@ static print_func_t printers[] = { [SCOUTFS_ORPHAN_KEY] = print_orphan, [SCOUTFS_DIRENT_KEY] = print_dirent, [SCOUTFS_READDIR_KEY] = print_readdir, + [SCOUTFS_SYMLINK_KEY] = print_symlink, [SCOUTFS_LINK_BACKREF_KEY] = print_link_backref, [SCOUTFS_DATA_KEY] = print_data, }; From a147239022b14a1c2bfa89736fe0f0f14223312d Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Fri, 10 Feb 2017 11:09:58 -0800 Subject: [PATCH 086/235] Remove dead block, btree, and buddy code Remove the last bits of the dead code from the old btree design. Signed-off-by: Zach Brown --- utils/src/buddy.c | 61 ---------------- utils/src/buddy.h | 16 ----- utils/src/format.h | 168 +-------------------------------------------- utils/src/mkfs.c | 8 +-- utils/src/print.c | 31 ++------- 5 files changed, 9 insertions(+), 275 deletions(-) delete mode 100644 utils/src/buddy.c delete mode 100644 utils/src/buddy.h diff --git a/utils/src/buddy.c b/utils/src/buddy.c deleted file mode 100644 index 1a0582d5..00000000 --- a/utils/src/buddy.c +++ /dev/null @@ -1,61 +0,0 @@ -#include -#include -#include - -#include "sparse.h" -#include "util.h" -#include "buddy.h" - -/* - * Figure out how many blocks the radix will need by starting with leaf - * blocks and dividing by the slot fanout until we have one block. cow - * updates require two copies of every block. - */ -static u64 calc_blocks(struct buddy_info *binf, u64 bits) -{ - u64 blocks = DIV_ROUND_UP(bits, SCOUTFS_BUDDY_ORDER0_BITS); - u64 tot = 0; - int level = 0; - int i; - - for (i = 0; i < SCOUTFS_BUDDY_MAX_HEIGHT; i++) - binf->blknos[i] = SCOUTFS_BUDDY_BLKNO; - - for (;;) { - for (i = level - 1; i >= 0; i--) - binf->blknos[i] += (blocks * 2); - tot += (blocks * 2); - - level++; - if (blocks == 1) - break; - blocks = DIV_ROUND_UP(blocks, SCOUTFS_BUDDY_SLOTS); - } - - binf->height = level; - - return tot; -} - -/* - * Figure out how many buddy blocks we'll need to allocate the rest of - * the blocks in the device. The first time through we find the size of - * the radix needed to describe the whole device, but that doesn't take - * the buddy block overhead into account. We iterate getting a more - * precise estimate each time. This only takes a few rounds to - * stabilize. - */ -void buddy_init(struct buddy_info *binf, u64 total_blocks) -{ - u64 blocks = SCOUTFS_BUDDY_BLKNO; - u64 was; - - while(1) { - was = blocks; - blocks = calc_blocks(binf, total_blocks - blocks); - if (blocks == was) - break; - } - - binf->buddy_blocks = blocks; -} diff --git a/utils/src/buddy.h b/utils/src/buddy.h deleted file mode 100644 index 6e70230d..00000000 --- a/utils/src/buddy.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef _BUDDY_H_ -#define _BUDDY_H_ - -#include "format.h" - -struct buddy_info { - u8 height; - u64 buddy_blocks; - - /* starting blkno in each level, including mirrors */ - u64 blknos[SCOUTFS_BUDDY_MAX_HEIGHT]; -}; - -void buddy_init(struct buddy_info *binf, u64 total_blocks); - -#endif diff --git a/utils/src/format.h b/utils/src/format.h index 77945486..b877d4d8 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -35,9 +35,6 @@ */ #define SCOUTFS_SUPER_BLKNO ((64 * 1024) >> SCOUTFS_BLOCK_SHIFT) #define SCOUTFS_SUPER_NR 2 -#define SCOUTFS_BUDDY_BLKNO (SCOUTFS_SUPER_BLKNO + SCOUTFS_SUPER_NR) - -#define SCOUTFS_MAX_TRANS_BLOCKS (128 * 1024 * 1024 / SCOUTFS_BLOCK_SIZE) /* * This header is found at the start of every block so that we can @@ -161,70 +158,6 @@ struct scoutfs_segment_block { /* packed vals */ } __packed; -/* - * Block references include the sequence number so that we can detect - * readers racing with writers and so that we can tell that we don't - * need to follow a reference when traversing based on seqs. - */ -struct scoutfs_block_ref { - __le64 blkno; - __le64 seq; -} __packed; - -/* - * If the block was full of bits the largest possible order would be - * the block size shift + 3 (BITS_PER_BYTE). But the header uses - * up some space and then the buddy bits mean two bits per block. - * Then +1 for this being the number, not the greatest order. - */ -#define SCOUTFS_BUDDY_ORDERS (SCOUTFS_BLOCK_SHIFT + 3 - 2 + 1) - -struct scoutfs_buddy_block { - struct scoutfs_block_header hdr; - __le16 first_set[SCOUTFS_BUDDY_ORDERS]; - __u8 level; - __u8 __pad[3]; /* naturally align bits */ - union { - struct scoutfs_buddy_slot { - __le64 seq; - __le16 free_orders; - /* XXX seems like we could hide a bit somewhere */ - __u8 blkno_off; - } __packed slots[0]; - __le64 bits[0]; - } __packed; -} __packed; - -/* - * Each buddy leaf block references order 0 blocks with half of its - * bitmap. The other half of the bits are used for the higher order - * bits. - */ -#define SCOUTFS_BUDDY_ORDER0_BITS \ - (((SCOUTFS_BLOCK_SIZE - sizeof(struct scoutfs_buddy_block)) * 8) / 2) - -#define SCOUTFS_BUDDY_SLOTS \ - ((SCOUTFS_BLOCK_SIZE - sizeof(struct scoutfs_buddy_block)) / \ - sizeof(struct scoutfs_buddy_slot)) - -struct scoutfs_buddy_root { - struct scoutfs_buddy_slot slot; - __u8 height; -} __packed; - -/* ((SCOUTFS_BUDDY_SLOTS^5) * SCOUTFS_BUDDY_ORDER0_BITS) > 2^52 */ -#define SCOUTFS_BUDDY_MAX_HEIGHT 6 - -/* - * We should be able to make the offset smaller if neither dirents nor - * data items use the full 64 bits. - */ -struct scoutfs_key { - __le64 inode; - u8 type; - __le64 offset; -} __packed; - /* * Currently we sort keys by the numeric value of the types, but that * isn't necessary. We could have an arbitrary sort order. So we don't @@ -241,8 +174,6 @@ struct scoutfs_key { #define SCOUTFS_DATA_KEY 11 #define SCOUTFS_MAX_UNUSED_KEY 255 -#define SCOUTFS_MAX_ITEM_LEN 512 - /* value is struct scoutfs_inode */ struct scoutfs_inode_key { __u8 type; @@ -307,66 +238,9 @@ struct scoutfs_symlink_key { __be64 ino; } __packed; -struct scoutfs_btree_root { - u8 height; - struct scoutfs_block_ref ref; -} __packed; - -/* - * @free_end: records the byte offset of the first byte after the free - * space in the block between the header and the first item. New items - * are allocated by subtracting the space they need. - * - * @free_reclaim: records the number of bytes of free space amongst the - * items after free_end. If a block is compacted then this much new - * free space would be reclaimed. - */ -struct scoutfs_btree_block { - struct scoutfs_block_header hdr; - __le16 free_end; - __le16 free_reclaim; - __le16 nr_items; - __le16 item_offs[0]; -} __packed; - -/* - * The item sequence number is set to the dirty block's sequence number - * when the item is modified. It is not changed by splits or merges. - */ -struct scoutfs_btree_item { - struct scoutfs_key key; - __le64 seq; - __le16 val_len; - char val[0]; -} __packed; - -/* Blocks are no more than half free. */ -#define SCOUTFS_BTREE_FREE_LIMIT \ - ((SCOUTFS_BLOCK_SIZE - sizeof(struct scoutfs_btree_block)) / 2) - /* XXX does this exist upstream somewhere? */ #define member_sizeof(TYPE, MEMBER) (sizeof(((TYPE *)0)->MEMBER)) -#define SCOUTFS_BTREE_MAX_ITEMS \ - ((SCOUTFS_BLOCK_SIZE - sizeof(struct scoutfs_btree_block)) / \ - (member_sizeof(struct scoutfs_btree_block, item_offs[0]) + \ - sizeof(struct scoutfs_btree_item))) - -/* - * We can calculate the max tree depth by calculating how many leaf - * blocks the tree could reference. The block device can only reference - * 2^64 bytes. The tallest parent tree has half full parent blocks. - * - * So we have the relation: - * - * ceil(max_items / 2) ^ (max_depth - 1) >= 2^64 / block_size - * - * and solve for depth: - * - * max_depth = log(ceil(max_items / 2), 2^64 / block_size) + 1 - */ -#define SCOUTFS_BTREE_MAX_DEPTH 10 - #define SCOUTFS_UUID_BYTES 16 /* @@ -382,16 +256,11 @@ struct scoutfs_super_block { __le64 alloc_uninit; __le64 total_segs; __le64 free_segs; - __le64 total_blocks; - __le64 free_blocks; __le64 ring_blkno; __le64 ring_blocks; __le64 ring_tail_block; __le64 ring_gen; __le64 next_seg_seq; - __le64 buddy_blocks; - struct scoutfs_buddy_root buddy_root; - struct scoutfs_btree_root btree_root; struct scoutfs_treap_root alloc_treap_root; struct scoutfs_manifest manifest; } __packed; @@ -418,7 +287,6 @@ struct scoutfs_timespec { struct scoutfs_inode { __le64 size; __le64 blocks; - __le64 link_counter; __le64 data_version; __le64 next_readdir_pos; __le32 nlink; @@ -426,7 +294,6 @@ struct scoutfs_inode { __le32 gid; __le32 mode; __le32 rdev; - __le32 salt; struct scoutfs_timespec atime; struct scoutfs_timespec ctime; struct scoutfs_timespec mtime; @@ -449,20 +316,6 @@ struct scoutfs_dirent { __u8 name[0]; } __packed; -/* - * Dirent items are stored at keys with the offset set to the hash of - * the name. Creation can find that hash values collide and will - * attempt to linearly probe this many following hash values looking for - * an unused value. - * - * In small directories this doesn't really matter because hash values - * will so very rarely collide. At around 50k items we start to see our - * first collisions. 16 slots is still pretty quick to scan in the - * btree and it gets us up into the hundreds of millions of entries - * before enospc is returned as we run out of hash values. - */ -#define SCOUTFS_DIRENT_COLL_NR 16 - #define SCOUTFS_NAME_LEN 255 /* S32_MAX avoids the (int) sign bit and might avoid sloppy bugs */ @@ -475,17 +328,10 @@ struct scoutfs_dirent { #define SCOUTFS_XATTR_MAX_PARTS \ DIV_ROUND_UP(SCOUTFS_XATTR_MAX_SIZE, SCOUTFS_XATTR_PART_SIZE) -/* - * We only use 31 bits for readdir positions so that we don't confuse - * old signed 32bit f_pos applications or those on the other side of - * network protocols that have limited readir positions. - */ - -#define SCOUTFS_DIRENT_OFF_BITS 31 -#define SCOUTFS_DIRENT_OFF_MASK ((1U << SCOUTFS_DIRENT_OFF_BITS) - 1) -/* getdents returns next pos with an entry, no entry at (f_pos)~0 */ +/* entries begin after . and .. */ #define SCOUTFS_DIRENT_FIRST_POS 2 -#define SCOUTFS_DIRENT_LAST_POS (INT_MAX - 1) +/* getdents returns next pos with an entry, no entry at (f_pos)~0 */ +#define SCOUTFS_DIRENT_LAST_POS (U64_MAX - 1) enum { SCOUTFS_DT_FIFO = 0, @@ -498,14 +344,6 @@ enum { SCOUTFS_DT_WHT, }; -struct scoutfs_extent { - __le64 blkno; - __le64 len; - __u8 flags; -} __packed; - -#define SCOUTFS_EXTENT_FLAG_OFFLINE (1 << 0) - /* ino_path can search for backref items with a null term */ #define SCOUTFS_MAX_KEY_SIZE \ offsetof(struct scoutfs_link_backref_key, name[SCOUTFS_NAME_LEN + 1]) diff --git a/utils/src/mkfs.c b/utils/src/mkfs.c index 26f58ea3..35d5ccf3 100644 --- a/utils/src/mkfs.c +++ b/utils/src/mkfs.c @@ -17,7 +17,6 @@ #include "crc.h" #include "rand.h" #include "dev.h" -#include "buddy.h" static int write_raw_block(int fd, u64 blkno, void *blk) { @@ -100,7 +99,6 @@ static int write_new_fs(char *path, int fd) void *ring; u64 limit; u64 size; - u64 total_blocks; u64 ring_blocks; u64 total_segs; u64 first_segno; @@ -134,7 +132,6 @@ static int write_new_fs(char *path, int fd) goto out; } - total_blocks = size / SCOUTFS_BLOCK_SIZE; total_segs = size / SCOUTFS_SEGMENT_SIZE; ring_blocks = calc_ring_blocks(total_segs); @@ -145,7 +142,6 @@ static int write_new_fs(char *path, int fd) super->id = cpu_to_le64(SCOUTFS_SUPER_ID); uuid_generate(super->uuid); super->next_ino = cpu_to_le64(SCOUTFS_ROOT_INO + 1); - super->total_blocks = cpu_to_le64(total_blocks); super->total_segs = cpu_to_le64(total_segs); super->ring_blkno = cpu_to_le64(SCOUTFS_SUPER_BLKNO + 2); super->ring_blocks = cpu_to_le64(ring_blocks); @@ -246,11 +242,11 @@ static int write_new_fs(char *path, int fd) uuid_unparse(super->uuid, uuid_str); printf("Created scoutfs filesystem:\n" - " total blocks: %llu\n" + " total segments: %llu\n" " ring blocks: %llu\n" " fsid: %llx\n" " uuid: %s\n", - total_blocks, ring_blocks, le64_to_cpu(super->hdr.fsid), + total_segs, ring_blocks, le64_to_cpu(super->hdr.fsid), uuid_str); ret = 0; diff --git a/utils/src/print.c b/utils/src/print.c index f278ea99..2665a516 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -15,12 +15,6 @@ #include "format.h" #include "cmd.h" #include "crc.h" -#include "buddy.h" - -/* XXX maybe these go somewhere */ -#define SKF "%llu.%u.%llu" -#define SKA(k) le64_to_cpu((k)->inode), (k)->type, \ - le64_to_cpu((k)->offset) static void *read_block(int fd, u64 blkno) { @@ -83,17 +77,16 @@ static void print_inode(void *key, int key_len, void *val, int val_len) struct scoutfs_inode_key *ikey = key; struct scoutfs_inode *inode = val; - printf(" inode: ino %llu size %llu blocks %llu lctr %llu nlink %u\n" + printf(" inode: ino %llu size %llu blocks %llu nlink %u\n" " uid %u gid %u mode 0%o rdev 0x%x\n" - " salt 0x%x next_readdir_pos %llu data_version %llu\n" + " next_readdir_pos %llu data_version %llu\n" " atime %llu.%08u ctime %llu.%08u\n" " mtime %llu.%08u\n", be64_to_cpu(ikey->ino), le64_to_cpu(inode->size), le64_to_cpu(inode->blocks), - le64_to_cpu(inode->link_counter), le32_to_cpu(inode->nlink), le32_to_cpu(inode->uid), le32_to_cpu(inode->gid), le32_to_cpu(inode->mode), - le32_to_cpu(inode->rdev), le32_to_cpu(inode->salt), + le32_to_cpu(inode->rdev), le64_to_cpu(inode->next_readdir_pos), le64_to_cpu(inode->data_version), le64_to_cpu(inode->atime.sec), @@ -194,20 +187,6 @@ static void print_symlink(void *key, int key_len, void *val, int val_len) be64_to_cpu(skey->ino), name); } -#if 0 -#define EXT_FLAG(f, flags, str) \ - (flags & f) ? str : "", (flags & (f - 1)) ? "|" : "" - -static void print_extent(struct scoutfs_key *key, - struct scoutfs_extent *ext) -{ - printf(" extent: (offest %llu) blkno %llu, len %llu flags %s%s\n", - le64_to_cpu(key->offset), le64_to_cpu(ext->blkno), - le64_to_cpu(ext->len), - EXT_FLAG(SCOUTFS_EXTENT_FLAG_OFFLINE, ext->flags, "OFF")); -} -#endif - typedef void (*print_func_t)(void *key, int key_len, void *val, int val_len); static print_func_t printers[] = { @@ -410,13 +389,11 @@ static int print_super_blocks(int fd) printf(" id %llx uuid %s\n", le64_to_cpu(super->id), uuid_str); /* XXX these are all in a crazy order */ - printf(" next_ino %llu total_blocks %llu free_blocks %llu\n" + printf(" next_ino %llu\n" " ring_blkno %llu ring_blocks %llu ring_tail_block %llu\n" " ring_gen %llu alloc_uninit %llu total_segs %llu\n" " next_seg_seq %llu free_segs %llu\n", le64_to_cpu(super->next_ino), - le64_to_cpu(super->total_blocks), - le64_to_cpu(super->free_blocks), le64_to_cpu(super->ring_blkno), le64_to_cpu(super->ring_blocks), le64_to_cpu(super->ring_tail_block), From f86ce74ffd4e4703a78fa7eb972e0bef40e78e16 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Tue, 11 Apr 2017 16:09:19 -0700 Subject: [PATCH 087/235] Add BITS_PER_LONG define Signed-off-by: Zach Brown --- utils/src/util.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/utils/src/util.h b/utils/src/util.h index ee6516e7..89ec4b4f 100644 --- a/utils/src/util.h +++ b/utils/src/util.h @@ -60,6 +60,8 @@ do { \ #define container_of(ptr, type, memb) \ ((type *)((void *)(ptr) - offsetof(type, memb))) +#define BITS_PER_LONG (sizeof(long) * 8) + /* * return -1,0,+1 based on the memcmp comparison of the minimum of their * two lengths. If their min shared bytes are equal but the lengths From bd54995599e62d668a11bb3684a97db5c912ae6c Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Tue, 11 Apr 2017 16:16:12 -0700 Subject: [PATCH 088/235] Add a simple native bitmap Nothing fancy at all. Signed-off-by: Zach Brown --- utils/src/bitmap.c | 57 ++++++++++++++++++++++++++++++++++++++++++++++ utils/src/bitmap.h | 9 ++++++++ 2 files changed, 66 insertions(+) create mode 100644 utils/src/bitmap.c create mode 100644 utils/src/bitmap.h diff --git a/utils/src/bitmap.c b/utils/src/bitmap.c new file mode 100644 index 00000000..5e9ab615 --- /dev/null +++ b/utils/src/bitmap.c @@ -0,0 +1,57 @@ +#define _GNU_SOURCE +#include +#include + +#include "sparse.h" +#include "util.h" +#include "bitmap.h" + +/* + * Just a quick simple native bitmap. + */ + +void set_bit(unsigned long *bits, u64 nr) +{ + bits[nr / BITS_PER_LONG] |= 1UL << (nr & (BITS_PER_LONG - 1)); +} + +void clear_bit(unsigned long *bits, u64 nr) +{ + bits[nr / BITS_PER_LONG] &= ~(1UL << (nr & (BITS_PER_LONG - 1))); +} + +u64 find_next_set_bit(unsigned long *map, u64 from, u64 total) +{ + unsigned long bits; + u64 base; + u64 nr; + int bit; + + base = from & ~((unsigned long)BITS_PER_LONG - 1); + map += from / BITS_PER_LONG; + + while (base < total) { + bits = *map; + + while (bits) { + bit = ffsl(bits) - 1; + nr = base + bit; + + if (nr >= from) + return min(nr, total); + + bits &= ~(1UL << bit); + } + + base += BITS_PER_LONG; + map++; + } + + return total; +} + +unsigned long *alloc_bits(u64 max) +{ + return calloc(DIV_ROUND_UP(max, BITS_PER_LONG), sizeof(unsigned long)); +} + diff --git a/utils/src/bitmap.h b/utils/src/bitmap.h new file mode 100644 index 00000000..993bad4e --- /dev/null +++ b/utils/src/bitmap.h @@ -0,0 +1,9 @@ +#ifndef _BITMAP_H_ +#define _BITMAP_H_ + +void set_bit(unsigned long *bits, u64 nr); +void clear_bit(unsigned long *bits, u64 nr); +u64 find_next_set_bit(unsigned long *start, u64 from, u64 total); +unsigned long *alloc_bits(u64 max); + +#endif From e09a21676257db1234ec448039e1f736853fda17 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Tue, 11 Apr 2017 16:09:52 -0700 Subject: [PATCH 089/235] Support simpler ring entries Add mkfs and print support for the simpler rings that the segment bitmap allocator and manifest are now using. Some other recent format header updates come along for the ride. Signed-off-by: Zach Brown --- utils/src/crc.c | 8 +- utils/src/crc.h | 2 +- utils/src/format.h | 97 +++++++++++++++--------- utils/src/mkfs.c | 162 +++++++++++++++++++++------------------ utils/src/print.c | 185 +++++++++++++++++++++++++-------------------- 5 files changed, 258 insertions(+), 196 deletions(-) diff --git a/utils/src/crc.c b/utils/src/crc.c index 2932cb88..8380cf37 100644 --- a/utils/src/crc.c +++ b/utils/src/crc.c @@ -38,11 +38,9 @@ u32 crc_block(struct scoutfs_block_header *hdr) SCOUTFS_BLOCK_SIZE - sizeof(hdr->crc)); } -__le32 crc_node(struct scoutfs_treap_node *node) +u32 crc_ring_block(struct scoutfs_ring_block *rblk) { - unsigned int skip = sizeof(node->crc); - unsigned int bytes = offsetof(struct scoutfs_treap_node, - data[le16_to_cpu(node->bytes)]); + unsigned long skip = (char *)(&rblk->crc + 1) - (char *)rblk; - return cpu_to_le32(crc32c(~0, (char *)node + skip, bytes - skip)); + return crc32c(~0, (char *)rblk + skip, SCOUTFS_BLOCK_SIZE - skip); } diff --git a/utils/src/crc.h b/utils/src/crc.h index b584315d..1006871e 100644 --- a/utils/src/crc.h +++ b/utils/src/crc.h @@ -8,6 +8,6 @@ u32 crc32c(u32 crc, const void *data, unsigned int len); u64 crc32c_64(u32 crc, const void *data, unsigned int len); u32 crc_block(struct scoutfs_block_header *hdr); -__le32 crc_node(struct scoutfs_treap_node *node); +u32 crc_ring_block(struct scoutfs_ring_block *rblk); #endif diff --git a/utils/src/format.h b/utils/src/format.h index b877d4d8..25cb3878 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -50,43 +50,30 @@ struct scoutfs_block_header { __le64 blkno; } __packed; -struct scoutfs_treap_ref { - __le64 off; - __le64 gen; - __u8 aug_bits; +struct scoutfs_ring_entry { + __le16 data_len; + __u8 flags; + __u8 data[0]; } __packed; -/* - * The lesser and greater bits are persistent on disk so that we can migrate - * nodes from the older half of the ring. - * - * The dirty bit is only used for in-memory nodes. - */ -#define SCOUTFS_TREAP_AUG_LESSER (1 << 0) -#define SCOUTFS_TREAP_AUG_GREATER (1 << 1) -#define SCOUTFS_TREAP_AUG_HALVES (SCOUTFS_TREAP_AUG_LESSER | \ - SCOUTFS_TREAP_AUG_GREATER) -#define SCOUTFS_TREAP_AUG_DIRTY (1 << 2) +#define SCOUTFS_RING_ENTRY_FLAG_DELETION (1 << 0) -/* - * Treap nodes are stored at byte offset in the ring of blocks described - * by the super block. Each reference contains the off and gen that it - * will find in the node for verification. Each node has the header - * and data payload covered by a crc. - */ -struct scoutfs_treap_node { +struct scoutfs_ring_block { __le32 crc; - __le64 off; - __le64 gen; - __le64 prio; - struct scoutfs_treap_ref left; - struct scoutfs_treap_ref right; - __le16 bytes; - u8 data[0]; + __le32 pad; + __le64 fsid; + __le64 seq; + __le64 block; + __le32 nr_entries; + struct scoutfs_ring_entry entries[0]; } __packed; -struct scoutfs_treap_root { - struct scoutfs_treap_ref ref; +struct scoutfs_ring_descriptor { + __le64 blkno; + __le64 total_blocks; + __le64 first_block; + __le64 first_seq; + __le64 nr_blocks; } __packed; /* @@ -98,7 +85,7 @@ struct scoutfs_treap_root { #define SCOUTFS_MANIFEST_FANOUT 10 struct scoutfs_manifest { - struct scoutfs_treap_root root; + struct scoutfs_ring_descriptor ring; __le64 level_counts[SCOUTFS_MANIFEST_MAX_LEVEL]; } __packed; @@ -172,7 +159,10 @@ struct scoutfs_segment_block { #define SCOUTFS_EXTENT_KEY 9 #define SCOUTFS_ORPHAN_KEY 10 #define SCOUTFS_DATA_KEY 11 -#define SCOUTFS_MAX_UNUSED_KEY 255 +/* not found in the fs */ +#define SCOUTFS_MAX_UNUSED_KEY 253 +#define SCOUTFS_NET_ADDR_KEY 254 +#define SCOUTFS_NET_LISTEN_KEY 255 /* value is struct scoutfs_inode */ struct scoutfs_inode_key { @@ -243,6 +233,7 @@ struct scoutfs_symlink_key { #define SCOUTFS_UUID_BYTES 16 + /* * The ring fields describe the statically allocated ring log. The * head and tail indexes are logical 4k blocks offsets inside the ring. @@ -261,7 +252,7 @@ struct scoutfs_super_block { __le64 ring_tail_block; __le64 ring_gen; __le64 next_seg_seq; - struct scoutfs_treap_root alloc_treap_root; + struct scoutfs_ring_descriptor alloc_ring; struct scoutfs_manifest manifest; } __packed; @@ -348,4 +339,42 @@ enum { #define SCOUTFS_MAX_KEY_SIZE \ offsetof(struct scoutfs_link_backref_key, name[SCOUTFS_NAME_LEN + 1]) +/* + * messages over the wire. + */ + +/* XXX ipv6 */ +struct scoutfs_inet_addr { + __le32 addr; + __le16 port; +} __packed; + +/* + * This header precedes and describes all network messages sent over + * sockets. The id is set by the request and sent in the reply. The + * type is strictly redundant in the reply because the id will find the + * send but we include it in both packets to make it easier to observe + * replies without having the id from their previous request. + */ +struct scoutfs_net_header { + __le64 id; + __le16 data_len; + __u8 type; + __u8 status; + __u8 data[0]; +}; + +enum { + /* sends and receives a struct scoutfs_timeval */ + SCOUTFS_NET_TRADE_TIME = 0, + SCOUTFS_NET_UNKNOWN, +}; + +enum { + SCOUTFS_NET_STATUS_REQUEST = 0, + SCOUTFS_NET_STATUS_SUCCESS, + SCOUTFS_NET_STATUS_ERROR, + SCOUTFS_NET_STATUS_UNKNOWN, +}; + #endif diff --git a/utils/src/mkfs.c b/utils/src/mkfs.c index 35d5ccf3..e416fc88 100644 --- a/utils/src/mkfs.c +++ b/utils/src/mkfs.c @@ -47,35 +47,23 @@ static int write_block(int fd, u64 blkno, struct scoutfs_super_block *super, } /* - * Figure out how many blocks the ring will need. The ring has to hold: - * - * - manifest entries for every segment with largest keys - * - allocator regions for bits to reference every segment - * - empty space at the end of blocks so nodes don't cross blocks - * - double that to account for repeatedly duplicating entries - * - double that so we can migrate everything before wrapping + * Figure out how many blocks a given ring will need given a max number + * of entries up to a given max size. We figure out how many blocks it + * could take to store these maximal entries given unused tail space and + * block header overheads. Then we (wastefully) multiply by three to + * ensure that the ring won't consume itself as it wraps. The caller + * aligns the ring size to a segment size depending on where it starts. */ -static u64 calc_ring_blocks(u64 segs) +static u64 calc_ring_blocks(u64 max_nr, u64 max_size) { - u64 alloc_blocks; - u64 ment_blocks; u64 block_bytes; - u64 node_bytes; - u64 regions; - node_bytes = sizeof(struct scoutfs_treap_node) + - sizeof(struct scoutfs_manifest_entry) + - (2 * SCOUTFS_MAX_KEY_SIZE); - block_bytes = SCOUTFS_BLOCK_SIZE - (node_bytes - 1); - ment_blocks = DIV_ROUND_UP(segs * node_bytes, block_bytes); + max_size += sizeof(struct scoutfs_ring_entry); - node_bytes = sizeof(struct scoutfs_treap_node) + - sizeof(struct scoutfs_alloc_region); - regions = DIV_ROUND_UP(segs, SCOUTFS_ALLOC_REGION_BITS); - block_bytes = SCOUTFS_BLOCK_SIZE - (node_bytes - 1); - alloc_blocks = DIV_ROUND_UP(regions * node_bytes, block_bytes); + block_bytes = SCOUTFS_BLOCK_SIZE - sizeof(struct scoutfs_ring_block) - + (max_size - 1); - return ALIGN((ment_blocks + alloc_blocks) * 4, SCOUTFS_SEGMENT_BLOCKS); + return DIV_ROUND_UP(max_nr * max_size, block_bytes) * 3; } /* @@ -92,11 +80,13 @@ static int write_new_fs(char *path, int fd) struct scoutfs_inode *inode; struct scoutfs_segment_block *sblk; struct scoutfs_manifest_entry *ment; - struct scoutfs_treap_node *node; + struct scoutfs_ring_descriptor *rdesc; + struct scoutfs_ring_block *rblk; + struct scoutfs_ring_entry *rent; struct scoutfs_segment_item *item; struct timeval tv; char uuid_str[37]; - void *ring; + u64 blkno; u64 limit; u64 size; u64 ring_blocks; @@ -108,9 +98,9 @@ static int write_new_fs(char *path, int fd) gettimeofday(&tv, NULL); super = calloc(1, SCOUTFS_BLOCK_SIZE); - ring = calloc(1, SCOUTFS_BLOCK_SIZE); + rblk = calloc(1, SCOUTFS_BLOCK_SIZE); sblk = calloc(1, SCOUTFS_SEGMENT_SIZE); - if (!super || !ring || !sblk) { + if (!super || !rblk || !sblk) { ret = -errno; fprintf(stderr, "failed to allocate block mem: %s (%d)\n", strerror(errno), errno); @@ -133,9 +123,12 @@ static int write_new_fs(char *path, int fd) } total_segs = size / SCOUTFS_SEGMENT_SIZE; - ring_blocks = calc_ring_blocks(total_segs); - /* first initialize the super so we can use it to build structures */ + /* segments and manifest entries all use single key */ + root_ikey.type = SCOUTFS_INODE_KEY; + root_ikey.ino = cpu_to_be64(SCOUTFS_ROOT_INO); + + /* partially initialize the super so we can use it to init others */ memset(super, 0, SCOUTFS_BLOCK_SIZE); pseudo_random_bytes(&super->hdr.fsid, sizeof(super->hdr.fsid)); super->hdr.seq = cpu_to_le64(1); @@ -143,15 +136,72 @@ static int write_new_fs(char *path, int fd) uuid_generate(super->uuid); super->next_ino = cpu_to_le64(SCOUTFS_ROOT_INO + 1); super->total_segs = cpu_to_le64(total_segs); - super->ring_blkno = cpu_to_le64(SCOUTFS_SUPER_BLKNO + 2); - super->ring_blocks = cpu_to_le64(ring_blocks); - super->ring_tail_block = cpu_to_le64(1); - super->ring_gen = cpu_to_le64(1); super->next_seg_seq = cpu_to_le64(2); - first_segno = DIV_ROUND_UP(le64_to_cpu(super->ring_blkno) + - le64_to_cpu(super->ring_blocks), - SCOUTFS_SEGMENT_BLOCKS); + /* start writing rings after the super */ + blkno = SCOUTFS_SUPER_BLKNO + SCOUTFS_SUPER_NR; + + /* allocator ring is empty, allocations start from super fields */ + ring_blocks = calc_ring_blocks(DIV_ROUND_UP(total_segs, + SCOUTFS_ALLOC_REGION_BITS), + sizeof(struct scoutfs_alloc_region)); + ring_blocks = round_up(blkno + ring_blocks, SCOUTFS_SEGMENT_BLOCKS) - + blkno; + + rdesc = &super->alloc_ring; + rdesc->blkno = cpu_to_le64(blkno); + rdesc->total_blocks = cpu_to_le64(ring_blocks); + rdesc->first_block = cpu_to_le64(0); + rdesc->first_seq = cpu_to_le64(0); + rdesc->nr_blocks = cpu_to_le64(0); + + blkno += ring_blocks; + + /* manifest ring has a block with an entry for the segment */ + ring_blocks = calc_ring_blocks(total_segs, + sizeof(struct scoutfs_manifest_entry) + + (2 * SCOUTFS_MAX_KEY_SIZE)); + ring_blocks = round_up(ring_blocks, SCOUTFS_SEGMENT_BLOCKS); + + /* first usable segno follows manifest ring */ + first_segno = (blkno + ring_blocks) / SCOUTFS_SEGMENT_BLOCKS; + + super->manifest.level_counts[1] = cpu_to_le64(1); + + rdesc = &super->manifest.ring; + rdesc->blkno = cpu_to_le64(blkno); + rdesc->total_blocks = cpu_to_le64(ring_blocks); + rdesc->first_seq = cpu_to_le64(1); + rdesc->nr_blocks = cpu_to_le64(1); + + memset(rblk, 0, SCOUTFS_BLOCK_SIZE); + rblk->pad = 0; + rblk->fsid = super->hdr.fsid; + rblk->seq = cpu_to_le64(1); + rblk->block = 0; + rblk->nr_entries = cpu_to_le32(1); + + rent = rblk->entries; + rent->flags = 0; + rent->data_len = cpu_to_le16(sizeof(struct scoutfs_manifest_entry) + + (2 * sizeof(struct scoutfs_inode_key))); + + ment = (void *)rent->data; + ment->segno = cpu_to_le64(first_segno); + ment->seq = cpu_to_le64(1); + ment->first_key_len = cpu_to_le16(sizeof(struct scoutfs_inode_key)); + ment->last_key_len = cpu_to_le16(sizeof(struct scoutfs_inode_key)); + ment->level = 1; + ikey = (void *)ment->keys; + ikey[0] = root_ikey; + ikey[1] = root_ikey; + + rblk->crc = cpu_to_le32(crc_ring_block(rblk)); + + ret = write_raw_block(fd, blkno, rblk); + if (ret) + goto out; + blkno += ring_blocks; /* alloc from uninit, don't need regions yet */ super->alloc_uninit = cpu_to_le64(first_segno + 1); @@ -162,9 +212,6 @@ static int write_new_fs(char *path, int fd) sblk->seq = cpu_to_le64(1); sblk->nr_items = cpu_to_le32(1); - root_ikey.type = SCOUTFS_INODE_KEY; - root_ikey.ino = cpu_to_be64(SCOUTFS_ROOT_INO); - item = &sblk->items[0]; ikey = (void *)&sblk->items[1]; inode = (void *)(ikey + 1); @@ -194,35 +241,6 @@ static int write_new_fs(char *path, int fd) goto out; } - /* a single manifest entry points to the single segment */ - node = ring; - node->off = cpu_to_le64((char *)node - (char *)ring); - node->gen = cpu_to_le64(1); - node->bytes = cpu_to_le16(sizeof(struct scoutfs_manifest_entry) + - (2 * sizeof(struct scoutfs_inode_key))); - pseudo_random_bytes(&node->prio, sizeof(node->prio)); - - ment = (void *)node->data; - ment->segno = sblk->segno; - ment->seq = cpu_to_le64(1); - ment->first_key_len = cpu_to_le16(sizeof(struct scoutfs_inode_key)); - ment->last_key_len = cpu_to_le16(sizeof(struct scoutfs_inode_key)); - ment->level = 1; - ikey = (void *)ment->keys; - ikey[0] = root_ikey; - ikey[1] = root_ikey; - - node->crc = crc_node(node); - - super->manifest.root.ref.off = node->off; - super->manifest.root.ref.gen = node->gen; - super->manifest.root.ref.aug_bits = SCOUTFS_TREAP_AUG_LESSER; - super->manifest.level_counts[1] = cpu_to_le64(1); - - ret = write_raw_block(fd, le64_to_cpu(super->ring_blkno), ring); - if (ret) - goto out; - /* write the two super blocks */ for (i = 0; i < SCOUTFS_SUPER_NR; i++) { super->hdr.seq = cpu_to_le64(i + 1); @@ -242,19 +260,17 @@ static int write_new_fs(char *path, int fd) uuid_unparse(super->uuid, uuid_str); printf("Created scoutfs filesystem:\n" - " total segments: %llu\n" - " ring blocks: %llu\n" " fsid: %llx\n" " uuid: %s\n", - total_segs, ring_blocks, le64_to_cpu(super->hdr.fsid), + le64_to_cpu(super->hdr.fsid), uuid_str); ret = 0; out: if (super) free(super); - if (ring) - free(ring); + if (rblk) + free(rblk); if (sblk) free(sblk); return ret; diff --git a/utils/src/print.c b/utils/src/print.c index 2665a516..b6b84fc6 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -13,6 +13,7 @@ #include "sparse.h" #include "util.h" #include "format.h" +#include "bitmap.h" #include "cmd.h" #include "crc.h" @@ -251,81 +252,69 @@ static void print_segment_block(struct scoutfs_segment_block *sblk) le32_to_cpu(sblk->nr_items)); } -static int print_segment(int fd, struct scoutfs_treap_node *tnode) +static int print_segments(int fd, unsigned long *seg_map, u64 total) { - struct scoutfs_manifest_entry *ment = (void *)tnode->data; - u64 segno = le64_to_cpu(ment->segno); struct scoutfs_segment_block *sblk; - int i; + u64 s; + u64 i; - sblk = read_segment(fd, segno); - if (!sblk) - return -ENOMEM; + for (s = 0; (s = find_next_set_bit(seg_map, s, total)) < total; s++) { + sblk = read_segment(fd, s); + if (!sblk) + return -ENOMEM; - printf("segment segno %llu\n", segno); - print_segment_block(sblk); + printf("segment segno %llu\n", s); + print_segment_block(sblk); - for (i = 0; i < le32_to_cpu(sblk->nr_items); i++) - print_item(sblk, i); + for (i = 0; i < le32_to_cpu(sblk->nr_items); i++) + print_item(sblk, i); - free(sblk); + free(sblk); + } return 0; } -static void print_treap_ref(struct scoutfs_treap_ref *ref) +static void print_ring_descriptor(struct scoutfs_ring_descriptor *rdesc, + char *which) { - printf(" off %llu gen %llu aug_bits %x", - le64_to_cpu(ref->off), le64_to_cpu(ref->gen), - ref->aug_bits); + printf(" %s ring:\n blkno %llu total_blocks %llu first_block %llu " + "first_seq %llu nr_blocks %llu\n", + which, le64_to_cpu(rdesc->blkno), + le64_to_cpu(rdesc->total_blocks), + le64_to_cpu(rdesc->first_block), + le64_to_cpu(rdesc->first_seq), + le64_to_cpu(rdesc->nr_blocks)); } -static void print_treap_node(struct scoutfs_treap_node *tnode) +static int print_manifest_entry(int fd, struct scoutfs_ring_entry *rent, + void *arg) { - char valid_str[40]; - __le32 crc; + struct scoutfs_manifest_entry *ment = (void *)rent->data; + unsigned long *seg_map = arg; - crc = crc_node(tnode); - if (crc != tnode->crc) - sprintf(valid_str, "(!= %08x) ", le32_to_cpu(crc)); - else - valid_str[0] = '\0'; - - printf(" node: crc %08x %soff %llu gen %llu bytes %u prio %016llx\n" - " l:", - le32_to_cpu(tnode->crc), valid_str, le64_to_cpu(tnode->off), - le64_to_cpu(tnode->gen), le16_to_cpu(tnode->bytes), - le64_to_cpu(tnode->prio)); - print_treap_ref(&tnode->left); - printf(" r:"); - print_treap_ref(&tnode->right); - printf("\n"); -} - -static int print_manifest_entry(int fd, struct scoutfs_treap_node *tnode) -{ - struct scoutfs_manifest_entry *ment = (void *)tnode->data; - - print_treap_node(tnode); - - printf(" ment: segno %llu seq %llu first_len %u last_len %u level %u\n", + printf(" segno %llu seq %llu first_len %u last_len %u level %u\n", le64_to_cpu(ment->segno), le64_to_cpu(ment->seq), le16_to_cpu(ment->first_key_len), le16_to_cpu(ment->last_key_len), ment->level); + if (rent->flags & SCOUTFS_RING_ENTRY_FLAG_DELETION) + clear_bit(seg_map, le64_to_cpu(ment->segno)); + else + set_bit(seg_map, le64_to_cpu(ment->segno)); + return 0; } -static int print_alloc_region(int fd, struct scoutfs_treap_node *tnode) +static int print_alloc_region(int fd, struct scoutfs_ring_entry *rent, + void *arg) { - struct scoutfs_alloc_region *reg = (void *)tnode->data; + struct scoutfs_alloc_region *reg = (void *)rent->data; int i; - print_treap_node(tnode); - - printf(" reg: index %llu bits", le64_to_cpu(reg->index)); + printf(" index %llu bits", le64_to_cpu(reg->index)); for (i = 0; i < array_size(reg->bits); i++) printf(" %016llx", le64_to_cpu(reg->bits[i])); printf("\n"); @@ -333,43 +322,68 @@ static int print_alloc_region(int fd, struct scoutfs_treap_node *tnode) return 0; } -typedef int (*tnode_func)(int fd, struct scoutfs_treap_node *tnode); +typedef int (*rent_func)(int fd, struct scoutfs_ring_entry *rent, void *arg); -static int walk_treap(int fd, struct scoutfs_super_block *super, - struct scoutfs_treap_ref *ref, tnode_func func) +static int print_ring(int fd, struct scoutfs_super_block *super, + char *which, struct scoutfs_ring_descriptor *rdesc, + rent_func func, void *arg) { - struct scoutfs_treap_node *tnode; + struct scoutfs_ring_block *rblk; + struct scoutfs_ring_entry *rent; + u64 block; u64 blkno; - void *blk; - u64 off; int ret; + u64 i; + u32 e; - if (!ref->gen) - return 0; + block = le64_to_cpu(rdesc->first_block); + for (i = 0; i < le64_to_cpu(rdesc->nr_blocks); i++) { + blkno = le64_to_cpu(rdesc->blkno) + block; - off = le64_to_cpu(ref->off); - blkno = le64_to_cpu(super->ring_blkno) + (off >> SCOUTFS_BLOCK_SHIFT); + rblk = read_block(fd, blkno); + if (!rblk) + return -ENOMEM; - blk = read_block(fd, blkno); - if (!blk) - return -ENOMEM; + printf("%s ring blkno %llu\n" + " crc %08x fsid %llx seq %llu block %llu " + "nr_entries %u\n", + which, blkno, le32_to_cpu(rblk->crc), + le64_to_cpu(rblk->fsid), + le64_to_cpu(rblk->seq), + le64_to_cpu(rblk->block), + le32_to_cpu(rblk->nr_entries)); - tnode = blk + (off & SCOUTFS_BLOCK_MASK); + rent = rblk->entries; + for (e = 0; e < le32_to_cpu(rblk->nr_entries); e++) { - ret = func(fd, tnode); - if (ret == 0) - ret = walk_treap(fd, super, &tnode->left, func) ?: - walk_treap(fd, super, &tnode->right, func); + printf(" entry [%u] off %lu data_len %u flags %x\n", + e, (char *)rent - (char *)rblk->entries, + le16_to_cpu(rent->data_len), rent->flags); - free(blk); + ret = func(fd, rent, arg); + if (ret) { + free(rblk); + return ret; + } - return ret; + rent = (void *)&rent->data[le16_to_cpu(rent->data_len)]; + } + + block++; + if (block == le64_to_cpu(rdesc->total_blocks)) + block = 0; + + free(rblk); + } + + return 0; } static int print_super_blocks(int fd) { struct scoutfs_super_block *super; struct scoutfs_super_block recent = { .hdr.seq = 0 }; + unsigned long *seg_map; char uuid_str[37]; __le64 *counts; int ret = 0; @@ -402,14 +416,11 @@ static int print_super_blocks(int fd) le64_to_cpu(super->total_segs), le64_to_cpu(super->next_seg_seq), le64_to_cpu(super->free_segs)); - printf(" alloc root:"); - print_treap_ref(&super->alloc_treap_root.ref); - printf("\n"); - printf(" manifest root:"); - print_treap_ref(&super->manifest.root.ref); - printf("\n"); - printf(" level_counts:"); + print_ring_descriptor(&super->alloc_ring, "alloc"); + print_ring_descriptor(&super->manifest.ring, "manifest"); + + printf(" level_counts:"); counts = super->manifest.level_counts; for (j = 0; j < SCOUTFS_MANIFEST_MAX_LEVEL; j++) { if (le64_to_cpu(counts[j])) @@ -425,21 +436,29 @@ static int print_super_blocks(int fd) super = &recent; - printf("manifest treap:\n"); - ret = walk_treap(fd, super, &super->manifest.root.ref, - print_manifest_entry); + seg_map = alloc_bits(le64_to_cpu(super->total_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); + return ret; + } - printf("alloc treap:\n"); - err = walk_treap(fd, super, &super->alloc_treap_root.ref, - print_alloc_region); + ret = print_ring(fd, super, "alloc", &super->alloc_ring, + print_alloc_region, NULL); + + err = print_ring(fd, super, "manifest", &super->manifest.ring, + print_manifest_entry, seg_map); if (err && !ret) ret = err; - err = walk_treap(fd, super, &super->manifest.root.ref, - print_segment); + err = print_segments(fd, seg_map, le64_to_cpu(super->total_segs)); if (err && !ret) ret = err; + free(seg_map); + return ret; } From 1c9a407059d0201983fa68ae02550505551beb9e Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Thu, 27 Apr 2017 12:33:52 -0700 Subject: [PATCH 090/235] scoutfs-utils: print extent items Signed-off-by: Zach Brown --- utils/src/format.h | 60 +++++++++++++++++++++++++++++++++++++++++----- utils/src/print.c | 55 +++++++++++++++++++++++++++++++++++------- 2 files changed, 100 insertions(+), 15 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index 25cb3878..a58d12b6 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -156,9 +156,10 @@ struct scoutfs_segment_block { #define SCOUTFS_READDIR_KEY 6 #define SCOUTFS_LINK_BACKREF_KEY 7 #define SCOUTFS_SYMLINK_KEY 8 -#define SCOUTFS_EXTENT_KEY 9 +#define SCOUTFS_FILE_EXTENT_KEY 9 #define SCOUTFS_ORPHAN_KEY 10 -#define SCOUTFS_DATA_KEY 11 +#define SCOUTFS_FREE_EXTENT_BLKNO_KEY 11 +#define SCOUTFS_FREE_EXTENT_BLOCKS_KEY 12 /* not found in the fs */ #define SCOUTFS_MAX_UNUSED_KEY 253 #define SCOUTFS_NET_ADDR_KEY 254 @@ -198,11 +199,28 @@ struct scoutfs_orphan_key { __be64 ino; } __packed; -/* value is data payload bytes */ -struct scoutfs_data_key { +/* no value */ +struct scoutfs_file_extent_key { __u8 type; __be64 ino; - __be64 block; + __be64 last_blk_off; + __be64 last_blkno; + __be64 blocks; +} __packed; + +/* no value */ +struct scoutfs_free_extent_blkno_key { + __u8 type; + __be64 node_id; + __be64 last_blkno; + __be64 blocks; +} __packed; + +struct scoutfs_free_extent_blocks_key { + __u8 type; + __be64 node_id; + __be64 blocks; + __be64 last_blkno; } __packed; /* value is each item's part of the full xattr value for the off/len */ @@ -362,11 +380,41 @@ struct scoutfs_net_header { __u8 type; __u8 status; __u8 data[0]; -}; +} __packed; + +/* + * When there's no more free inodes this will be sent with ino = ~0 and + * nr = 0. + */ +struct scoutfs_net_inode_alloc { + __le64 ino; + __le64 nr; +} __packed; + +struct scoutfs_net_key_range { + __le16 start_len; + __le16 end_len; + __u8 key_bytes[0]; +} __packed; + +struct scoutfs_net_manifest_entries { + __le16 nr; + struct scoutfs_manifest_entry ments[0]; +} __packed; + +struct scoutfs_net_segnos { + __le16 nr; + __le64 segnos[0]; +} __packed; enum { /* sends and receives a struct scoutfs_timeval */ SCOUTFS_NET_TRADE_TIME = 0, + SCOUTFS_NET_ALLOC_INODES, + SCOUTFS_NET_MANIFEST_RANGE_ENTRIES, + SCOUTFS_NET_ALLOC_SEGNO, + SCOUTFS_NET_RECORD_SEGMENT, + SCOUTFS_NET_BULK_ALLOC, SCOUTFS_NET_UNKNOWN, }; diff --git a/utils/src/print.c b/utils/src/print.c index b6b84fc6..29fa2f82 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -160,14 +160,6 @@ static void print_readdir(void *key, int key_len, void *val, int val_len) name); } -static void print_data(void *key, int key_len, void *val, int val_len) -{ - struct scoutfs_data_key *dat = key; - - printf(" data: ino %llu block %llu\n", - be64_to_cpu(dat->ino), be64_to_cpu(dat->block)); -} - static void print_link_backref(void *key, int key_len, void *val, int val_len) { struct scoutfs_link_backref_key *lbkey = key; @@ -188,6 +180,49 @@ static void print_symlink(void *key, int key_len, void *val, int val_len) be64_to_cpu(skey->ino), name); } +/* + * Just print the calculated starting blk_off/blkno, we can add a flag + * to print the raw values before the math if needed. + */ +static void print_file_extent(void *key, int key_len, void *val, int val_len) +{ + struct scoutfs_file_extent_key *fext = key; + u64 blocks = be64_to_cpu(fext->blocks); + u64 blk_off = be64_to_cpu(fext->last_blk_off) - blocks + 1; + u64 blkno = be64_to_cpu(fext->last_blkno) - blocks + 1; + + printf(" extent: ino %llu blk_off %llu blkno %llu blocks %llu\n", + be64_to_cpu(fext->ino), blk_off, blkno, blocks); +} + +static void print_free_extent(void *key, int key_len, void *val, int val_len) +{ + struct scoutfs_free_extent_blkno_key *blk = key; + struct scoutfs_free_extent_blocks_key *bks = key; + u64 last_blkno; + u64 node_id; + u64 blocks; + u64 blkno; + char *str; + + if (blk->type == SCOUTFS_FREE_EXTENT_BLKNO_KEY) { + str = "free (blkno)"; + node_id = be64_to_cpu(blk->node_id); + last_blkno = be64_to_cpu(blk->last_blkno); + blocks = be64_to_cpu(blk->blocks); + } else { + str = "free (blocks)"; + node_id = be64_to_cpu(bks->node_id); + last_blkno = be64_to_cpu(bks->last_blkno); + blocks = be64_to_cpu(bks->blocks); + } + + blkno = last_blkno - blocks + 1; + + printf(" %s: node_id %llx blkno %llu blocks %llu\n", + str, node_id, blkno, blocks); +} + typedef void (*print_func_t)(void *key, int key_len, void *val, int val_len); static print_func_t printers[] = { @@ -198,7 +233,9 @@ static print_func_t printers[] = { [SCOUTFS_READDIR_KEY] = print_readdir, [SCOUTFS_SYMLINK_KEY] = print_symlink, [SCOUTFS_LINK_BACKREF_KEY] = print_link_backref, - [SCOUTFS_DATA_KEY] = print_data, + [SCOUTFS_FILE_EXTENT_KEY] = print_file_extent, + [SCOUTFS_FREE_EXTENT_BLKNO_KEY] = print_free_extent, + [SCOUTFS_FREE_EXTENT_BLOCKS_KEY] = print_free_extent, }; /* utils uses big contiguous allocations */ From 4585d5715365c103998b4b08603b895a938930bb Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Mon, 8 May 2017 09:49:59 -0700 Subject: [PATCH 091/235] scoutfs-utils: only print recent super It's a bit confusing to always see both the old and current super block. Let's only print the first one. We could add an argument to print all of them. Signed-off-by: Zach Brown --- utils/src/print.c | 81 ++++++++++++++++++++++++++--------------------- 1 file changed, 45 insertions(+), 36 deletions(-) diff --git a/utils/src/print.c b/utils/src/print.c index 29fa2f82..d9e4cc21 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -416,63 +416,72 @@ static int print_ring(int fd, struct scoutfs_super_block *super, return 0; } +static void print_super_block(struct scoutfs_super_block *super, u64 blkno) +{ + char uuid_str[37]; + __le64 *counts; + int i; + + uuid_unparse(super->uuid, uuid_str); + + printf("super blkno %llu\n", blkno); + print_block_header(&super->hdr); + printf(" id %llx uuid %s\n", + le64_to_cpu(super->id), uuid_str); + /* XXX these are all in a crazy order */ + printf(" next_ino %llu\n" + " ring_blkno %llu ring_blocks %llu ring_tail_block %llu\n" + " ring_gen %llu alloc_uninit %llu total_segs %llu\n" + " next_seg_seq %llu free_segs %llu\n", + le64_to_cpu(super->next_ino), + le64_to_cpu(super->ring_blkno), + le64_to_cpu(super->ring_blocks), + le64_to_cpu(super->ring_tail_block), + le64_to_cpu(super->ring_gen), + le64_to_cpu(super->alloc_uninit), + le64_to_cpu(super->total_segs), + le64_to_cpu(super->next_seg_seq), + le64_to_cpu(super->free_segs)); + + print_ring_descriptor(&super->alloc_ring, "alloc"); + print_ring_descriptor(&super->manifest.ring, "manifest"); + + printf(" level_counts:"); + counts = super->manifest.level_counts; + for (i = 0; i < SCOUTFS_MANIFEST_MAX_LEVEL; i++) { + if (le64_to_cpu(counts[i])) + printf(" %u: %llu", i, le64_to_cpu(counts[i])); + } + printf("\n"); +} + static int print_super_blocks(int fd) { struct scoutfs_super_block *super; struct scoutfs_super_block recent = { .hdr.seq = 0 }; unsigned long *seg_map; - char uuid_str[37]; - __le64 *counts; int ret = 0; int err; int i; - int j; + int r = 0; for (i = 0; i < SCOUTFS_SUPER_NR; i++) { super = read_block(fd, SCOUTFS_SUPER_BLKNO + i); if (!super) return -ENOMEM; - uuid_unparse(super->uuid, uuid_str); - - printf("super blkno %llu\n", (u64)SCOUTFS_SUPER_BLKNO + i); - print_block_header(&super->hdr); - printf(" id %llx uuid %s\n", - le64_to_cpu(super->id), uuid_str); - /* XXX these are all in a crazy order */ - printf(" next_ino %llu\n" - " ring_blkno %llu ring_blocks %llu ring_tail_block %llu\n" - " ring_gen %llu alloc_uninit %llu total_segs %llu\n" - " next_seg_seq %llu free_segs %llu\n", - le64_to_cpu(super->next_ino), - le64_to_cpu(super->ring_blkno), - le64_to_cpu(super->ring_blocks), - le64_to_cpu(super->ring_tail_block), - le64_to_cpu(super->ring_gen), - le64_to_cpu(super->alloc_uninit), - le64_to_cpu(super->total_segs), - le64_to_cpu(super->next_seg_seq), - le64_to_cpu(super->free_segs)); - - print_ring_descriptor(&super->alloc_ring, "alloc"); - print_ring_descriptor(&super->manifest.ring, "manifest"); - - printf(" level_counts:"); - counts = super->manifest.level_counts; - for (j = 0; j < SCOUTFS_MANIFEST_MAX_LEVEL; j++) { - if (le64_to_cpu(counts[j])) - printf(" %u: %llu", j, le64_to_cpu(counts[j])); - } - printf("\n"); - - if (le64_to_cpu(super->hdr.seq) > le64_to_cpu(recent.hdr.seq)) + if (le64_to_cpu(super->hdr.seq) > le64_to_cpu(recent.hdr.seq)) { memcpy(&recent, super, sizeof(recent)); + r = i; + } free(super); } super = &recent; + print_super_block(super, SCOUTFS_SUPER_BLKNO + r); + seg_map = alloc_bits(le64_to_cpu(super->total_segs)); if (!seg_map) { ret = -ENOMEM; From a9cb464d49424f89d8c1171428cc055edc6742a8 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Mon, 15 May 2017 09:44:37 -0700 Subject: [PATCH 092/235] scoutfs-utils: rename __bitwise Recent kernel headers have leaked __bitwise into userspace. Rename our use of __bitwise in userspace sparse builds to avoid the collision. Signed-off-by: Zach Brown --- utils/src/sparse.h | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/utils/src/sparse.h b/utils/src/sparse.h index 58e54656..3777e471 100644 --- a/utils/src/sparse.h +++ b/utils/src/sparse.h @@ -7,8 +7,8 @@ #ifdef __CHECKER__ # undef __force # define __force __attribute__((force)) -# undef __bitwise -# define __bitwise __attribute__((bitwise)) +# undef __sp_biwise +# define __sp_biwise __attribute__((bitwise)) /* sparse seems to get confused by some builtins */ extern __builtin_ia32_rdrand64_step(unsigned long long *); extern unsigned int __builtin_ia32_crc32di(unsigned int, unsigned long long); @@ -18,7 +18,7 @@ extern unsigned int __builtin_ia32_crc32qi(unsigned int, unsigned char); #else # define __force -# define __bitwise +# define __sp_biwise #endif typedef unsigned char u8; @@ -36,12 +36,12 @@ typedef u64 __u64; #define U16_MAX ((u16)~0) -typedef u16 __bitwise __le16; -typedef u16 __bitwise __be16; -typedef u32 __bitwise __le32; -typedef u32 __bitwise __be32; -typedef u64 __bitwise __le64; -typedef u64 __bitwise __be64; +typedef u16 __sp_biwise __le16; +typedef u16 __sp_biwise __be16; +typedef u32 __sp_biwise __le32; +typedef u32 __sp_biwise __be32; +typedef u64 __sp_biwise __le64; +typedef u64 __sp_biwise __be64; static inline u16 ___swab16(u16 x) { From 9c00602051032aaa7ff7eab6955af8b0cb0a087c Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Mon, 15 May 2017 09:45:49 -0700 Subject: [PATCH 093/235] scoutfs-utils: print extent flags Signed-off-by: Zach Brown --- utils/src/format.h | 3 +++ utils/src/print.c | 6 ++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index a58d12b6..63cc07be 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -206,8 +206,11 @@ struct scoutfs_file_extent_key { __be64 last_blk_off; __be64 last_blkno; __be64 blocks; + __u8 flags; } __packed; +#define SCOUTFS_FILE_EXTENT_OFFLINE (1 << 0) + /* no value */ struct scoutfs_free_extent_blkno_key { __u8 type; diff --git a/utils/src/print.c b/utils/src/print.c index d9e4cc21..8921d182 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -191,8 +191,10 @@ static void print_file_extent(void *key, int key_len, void *val, int val_len) u64 blk_off = be64_to_cpu(fext->last_blk_off) - blocks + 1; u64 blkno = be64_to_cpu(fext->last_blkno) - blocks + 1; - printf(" extent: ino %llu blk_off %llu blkno %llu blocks %llu\n", - be64_to_cpu(fext->ino), blk_off, blkno, blocks); + printf(" extent: ino %llu blk_off %llu blkno %llu blocks %llu " + "flags %x (%c)\n", + be64_to_cpu(fext->ino), blk_off, blkno, blocks, fext->flags, + (fext->flags & SCOUTFS_FILE_EXTENT_OFFLINE) ? 'O' : '-'); } static void print_free_extent(void *key, int key_len, void *val, int val_len) From 9fc99a8c31965188fa702ee44218873ff9c3c5c1 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Mon, 15 May 2017 10:20:31 -0700 Subject: [PATCH 094/235] scoutfs-utils: add support for inode index items Add support for the inode index items which are replacing the seq walks from the old btree structures. We create the index items for the root inode, can print out the items, and add a commmand to walk the indices. Signed-off-by: Zach Brown --- utils/src/format.h | 15 ++++ utils/src/ino_path.c | 1 + utils/src/ioctl.h | 39 +++++----- utils/src/mkfs.c | 62 ++++++++++++---- utils/src/print.c | 12 +++ utils/src/since.c | 101 ------------------------- utils/src/walk_inodes.c | 158 ++++++++++++++++++++++++++++++++++++++++ 7 files changed, 256 insertions(+), 132 deletions(-) delete mode 100644 utils/src/since.c create mode 100644 utils/src/walk_inodes.c diff --git a/utils/src/format.h b/utils/src/format.h index 63cc07be..7e91afa1 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -160,6 +160,9 @@ struct scoutfs_segment_block { #define SCOUTFS_ORPHAN_KEY 10 #define SCOUTFS_FREE_EXTENT_BLKNO_KEY 11 #define SCOUTFS_FREE_EXTENT_BLOCKS_KEY 12 +#define SCOUTFS_INODE_INDEX_CTIME_KEY 13 +#define SCOUTFS_INODE_INDEX_MTIME_KEY 14 +#define SCOUTFS_INODE_INDEX_SIZE_KEY 15 /* not found in the fs */ #define SCOUTFS_MAX_UNUSED_KEY 253 #define SCOUTFS_NET_ADDR_KEY 254 @@ -249,6 +252,18 @@ struct scoutfs_symlink_key { __be64 ino; } __packed; +struct scoutfs_betimespec { + __be64 sec; + __be32 nsec; +} __packed; + +struct scoutfs_inode_index_key { + __u8 type; + __be64 major; + __be32 minor; + __be64 ino; +} __packed; + /* XXX does this exist upstream somewhere? */ #define member_sizeof(TYPE, MEMBER) (sizeof(((TYPE *)0)->MEMBER)) diff --git a/utils/src/ino_path.c b/utils/src/ino_path.c index 69c50892..36fe0535 100644 --- a/utils/src/ino_path.c +++ b/utils/src/ino_path.c @@ -11,6 +11,7 @@ #include "sparse.h" #include "util.h" +#include "format.h" #include "ioctl.h" #include "cmd.h" diff --git a/utils/src/ioctl.h b/utils/src/ioctl.h index 190199fc..864b9b8f 100644 --- a/utils/src/ioctl.h +++ b/utils/src/ioctl.h @@ -1,30 +1,36 @@ #ifndef _SCOUTFS_IOCTL_H_ #define _SCOUTFS_IOCTL_H_ -#include "format.h" - /* XXX I have no idea how these are chosen. */ #define SCOUTFS_IOCTL_MAGIC 's' -struct scoutfs_ioctl_ino_seq { +struct scoutfs_ioctl_walk_inodes_entry { + __u64 major; + __u32 minor; __u64 ino; - __u64 seq; } __packed; -struct scoutfs_ioctl_inodes_since { - __u64 first_ino; - __u64 last_ino; - __u64 seq; - __u64 buf_ptr; - __u32 buf_len; +struct scoutfs_ioctl_walk_inodes { + struct scoutfs_ioctl_walk_inodes_entry first; + struct scoutfs_ioctl_walk_inodes_entry last; + __u64 entries_ptr; + __u32 nr_entries; + __u8 index; } __packed; +enum { + SCOUTFS_IOC_WALK_INODES_CTIME = 0, + SCOUTFS_IOC_WALK_INODES_MTIME, + SCOUTFS_IOC_WALK_INODES_SIZE, + SCOUTFS_IOC_WALK_INODES_UNKNOWN, +}; + /* - * Adds entries to the user's buffer for each inode whose sequence - * number is greater than or equal to the given seq. + * Adds entries to the user's buffer for each inode that is found in the + * given index between the first and last positions. */ -#define SCOUTFS_IOC_INODES_SINCE _IOW(SCOUTFS_IOCTL_MAGIC, 1, \ - struct scoutfs_ioctl_inodes_since) +#define SCOUTFS_IOC_WALK_INODES _IOW(SCOUTFS_IOCTL_MAGIC, 1, \ + struct scoutfs_ioctl_walk_inodes) /* * Fill the path buffer with the next path to the target inode. An @@ -80,10 +86,7 @@ struct scoutfs_ioctl_ino_path { #define SCOUTFS_IOC_INO_PATH _IOW(SCOUTFS_IOCTL_MAGIC, 2, \ struct scoutfs_ioctl_ino_path) -#define SCOUTFS_IOC_INODE_DATA_SINCE _IOW(SCOUTFS_IOCTL_MAGIC, 3, \ - struct scoutfs_ioctl_inodes_since) - -#define SCOUTFS_IOC_DATA_VERSION _IOW(SCOUTFS_IOCTL_MAGIC, 4, u64) +#define SCOUTFS_IOC_DATA_VERSION _IOW(SCOUTFS_IOCTL_MAGIC, 4, __u64) struct scoutfs_ioctl_release { __u64 offset; diff --git a/utils/src/mkfs.c b/utils/src/mkfs.c index e416fc88..005723d8 100644 --- a/utils/src/mkfs.c +++ b/utils/src/mkfs.c @@ -75,8 +75,8 @@ static u64 calc_ring_blocks(u64 max_nr, u64 max_size) static int write_new_fs(char *path, int fd) { struct scoutfs_super_block *super; - struct scoutfs_inode_key root_ikey; struct scoutfs_inode_key *ikey; + struct scoutfs_inode_index_key *idx_key; struct scoutfs_inode *inode; struct scoutfs_segment_block *sblk; struct scoutfs_manifest_entry *ment; @@ -124,10 +124,6 @@ static int write_new_fs(char *path, int fd) total_segs = size / SCOUTFS_SEGMENT_SIZE; - /* segments and manifest entries all use single key */ - root_ikey.type = SCOUTFS_INODE_KEY; - root_ikey.ino = cpu_to_be64(SCOUTFS_ROOT_INO); - /* partially initialize the super so we can use it to init others */ memset(super, 0, SCOUTFS_BLOCK_SIZE); pseudo_random_bytes(&super->hdr.fsid, sizeof(super->hdr.fsid)); @@ -184,17 +180,23 @@ static int write_new_fs(char *path, int fd) rent = rblk->entries; rent->flags = 0; rent->data_len = cpu_to_le16(sizeof(struct scoutfs_manifest_entry) + - (2 * sizeof(struct scoutfs_inode_key))); + sizeof(struct scoutfs_inode_key) + + sizeof(struct scoutfs_inode_index_key)); ment = (void *)rent->data; ment->segno = cpu_to_le64(first_segno); ment->seq = cpu_to_le64(1); ment->first_key_len = cpu_to_le16(sizeof(struct scoutfs_inode_key)); - ment->last_key_len = cpu_to_le16(sizeof(struct scoutfs_inode_key)); + ment->last_key_len = cpu_to_le16(sizeof(struct scoutfs_inode_index_key)); ment->level = 1; ikey = (void *)ment->keys; - ikey[0] = root_ikey; - ikey[1] = root_ikey; + ikey->type = SCOUTFS_INODE_KEY; + ikey->ino = cpu_to_be64(SCOUTFS_ROOT_INO); + idx_key = (void *)(ikey + 1); + idx_key->type = SCOUTFS_INODE_INDEX_SIZE_KEY; + idx_key->major = cpu_to_be64(0); + idx_key->minor = 0; + idx_key->ino = cpu_to_be64(SCOUTFS_ROOT_INO); rblk->crc = cpu_to_le32(crc_ring_block(rblk)); @@ -210,11 +212,12 @@ static int write_new_fs(char *path, int fd) /* write seg with root inode */ sblk->segno = cpu_to_le64(first_segno); sblk->seq = cpu_to_le64(1); - sblk->nr_items = cpu_to_le32(1); + sblk->nr_items = cpu_to_le32(4); item = &sblk->items[0]; - ikey = (void *)&sblk->items[1]; - inode = (void *)(ikey + 1); + ikey = (void *)&sblk->items[4]; + inode = (void *)(ikey + 1) + + (3 * sizeof(struct scoutfs_inode_index_key)); item->seq = cpu_to_le64(1); item->key_off = cpu_to_le32((long)ikey - (long)sblk); @@ -222,7 +225,8 @@ static int write_new_fs(char *path, int fd) item->key_len = cpu_to_le16(sizeof(struct scoutfs_inode_key)); item->val_len = cpu_to_le16(sizeof(struct scoutfs_inode)); - *ikey = root_ikey; + ikey->type = SCOUTFS_INODE_KEY; + ikey->ino = cpu_to_be64(SCOUTFS_ROOT_INO); inode->next_readdir_pos = cpu_to_le64(2); inode->nlink = cpu_to_le32(SCOUTFS_DIRENT_FIRST_POS); @@ -234,6 +238,38 @@ static int write_new_fs(char *path, int fd) inode->mtime.sec = inode->atime.sec; inode->mtime.nsec = inode->atime.nsec; + item = (void *)(item + 1); + idx_key = (void *)(ikey + 1); + + /* write the root inode index keys */ + for (i = SCOUTFS_INODE_INDEX_CTIME_KEY; + i <= SCOUTFS_INODE_INDEX_SIZE_KEY; i++) { + + item->seq = cpu_to_le64(1); + item->key_off = cpu_to_le32((long)idx_key - (long)sblk); + item->val_off = 0; + item->key_len = cpu_to_le16(sizeof(*idx_key)); + item->val_len = 0; + + idx_key->type = i; + idx_key->ino = cpu_to_be64(SCOUTFS_ROOT_INO); + + switch(i) { + case SCOUTFS_INODE_INDEX_CTIME_KEY: + case SCOUTFS_INODE_INDEX_MTIME_KEY: + idx_key->major = cpu_to_be64(tv.tv_sec); + idx_key->minor = cpu_to_be32(tv.tv_usec * 1000); + break; + case SCOUTFS_INODE_INDEX_SIZE_KEY: + idx_key->major = cpu_to_be64(0); + idx_key->minor = 0; + break; + } + + item = (void *)(item + 1); + idx_key = (void *)(idx_key + 1); + } + ret = pwrite(fd, sblk, SCOUTFS_SEGMENT_SIZE, first_segno << SCOUTFS_SEGMENT_SHIFT); if (ret != SCOUTFS_SEGMENT_SIZE) { diff --git a/utils/src/print.c b/utils/src/print.c index 8921d182..9dd03192 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -225,6 +225,15 @@ static void print_free_extent(void *key, int key_len, void *val, int val_len) str, node_id, blkno, blocks); } +static void print_inode_index(void *key, int key_len, void *val, int val_len) +{ + struct scoutfs_inode_index_key *ikey = key; + + printf(" index: major %llu minor %u ino %llu\n", + be64_to_cpu(ikey->major), be32_to_cpu(ikey->minor), + be64_to_cpu(ikey->ino)); +} + typedef void (*print_func_t)(void *key, int key_len, void *val, int val_len); static print_func_t printers[] = { @@ -238,6 +247,9 @@ static print_func_t printers[] = { [SCOUTFS_FILE_EXTENT_KEY] = print_file_extent, [SCOUTFS_FREE_EXTENT_BLKNO_KEY] = print_free_extent, [SCOUTFS_FREE_EXTENT_BLOCKS_KEY] = print_free_extent, + [SCOUTFS_INODE_INDEX_CTIME_KEY] = print_inode_index, + [SCOUTFS_INODE_INDEX_MTIME_KEY] = print_inode_index, + [SCOUTFS_INODE_INDEX_SIZE_KEY] = print_inode_index, }; /* utils uses big contiguous allocations */ diff --git a/utils/src/since.c b/utils/src/since.c deleted file mode 100644 index d62db300..00000000 --- a/utils/src/since.c +++ /dev/null @@ -1,101 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "sparse.h" -#include "util.h" -#include "ioctl.h" -#include "cmd.h" - -static int since_cmd(int argc, char **argv, unsigned long ioc) -{ - struct scoutfs_ioctl_inodes_since args; - struct scoutfs_ioctl_ino_seq *iseq; - int len = 4 * 1024 * 1024; - char *endptr; - u64 nrs[3]; - void *ptr; - int ret; - int fd; - u64 n; - int i; - - if (argc != 4) { - fprintf(stderr, "must specify seq and path\n"); - return -EINVAL; - } - - for (i = 0; i < array_size(nrs); i++) { - n = strtoull(argv[i], &endptr, 0); - if (*endptr != '\0' || - ((n == LLONG_MIN || n == LLONG_MAX) && errno == ERANGE)) { - fprintf(stderr, "error parsing 64bit value '%s'\n", - argv[i]); - return -EINVAL; - } - nrs[i] = n; - } - - fd = open(argv[3], O_RDONLY); - if (fd < 0) { - ret = -errno; - fprintf(stderr, "failed to open '%s': %s (%d)\n", - argv[3], strerror(errno), errno); - return ret; - } - - ptr = malloc(len); - if (!ptr) { - fprintf(stderr, "must specify seq and path\n"); - close(fd); - return -EINVAL; - } - - args.first_ino = nrs[0]; - args.last_ino = nrs[1]; - args.seq = nrs[2]; - args.buf_ptr = (intptr_t)ptr; - args.buf_len = len; - - ret = ioctl(fd, ioc, &args); - if (ret < 0) { - ret = -errno; - fprintf(stderr, "inodes_since ioctl failed: %s (%d)\n", - strerror(errno), errno); - goto out; - } - - n = ret / sizeof(*iseq); - for (i = 0, iseq = ptr; i < n; i++, iseq++) - printf("ino %llu seq %llu\n", iseq->ino, iseq->seq); - -out: - free(ptr); - close(fd); - return ret; -}; - -static int inodes_since_cmd(int argc, char **argv) -{ - return since_cmd(argc, argv, SCOUTFS_IOC_INODES_SINCE); -} - -static int data_since_cmd(int argc, char **argv) -{ - return since_cmd(argc, argv, SCOUTFS_IOC_INODE_DATA_SINCE); -} - -static void __attribute__((constructor)) since_ctor(void) -{ - cmd_register("inodes-since", " ", - "print inodes modified since seq #", inodes_since_cmd); - cmd_register("data-since", " ", - "print inodes with data blocks modified since seq #", data_since_cmd); -} diff --git a/utils/src/walk_inodes.c b/utils/src/walk_inodes.c new file mode 100644 index 00000000..80ef6d6c --- /dev/null +++ b/utils/src/walk_inodes.c @@ -0,0 +1,158 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sparse.h" +#include "util.h" +#include "format.h" +#include "ioctl.h" +#include "cmd.h" + +/* + * Parse the command line specification of a walk inodes entry of the + * form "major.minor.ino". At least one value must be given, the rest + * default to 0. + */ +static int parse_walk_entry(struct scoutfs_ioctl_walk_inodes_entry *ent, + char *str) +{ + char *endptr; + char *c; + u64 ull; + u64 minor; + u64 *val; + + memset(ent, 0, sizeof(*ent)); + val = &ent->major; + + for (;;) { + c = index(str, '.'); + if (c) + *c = '\0'; + + endptr = NULL; + ull = strtoull(str, &endptr, 0); + if (*endptr != '\0' || + ((ull == LLONG_MIN || ull == LLONG_MAX) && + errno == ERANGE) || + (val == &minor && (*val < INT_MIN || *val > INT_MAX))) { + fprintf(stderr, "bad index pos at '%s'\n", str); + return -EINVAL; + } + + *val = ull; + + if (val == &ent->major) + val = &minor; + else if (val == &minor) + val = &ent->ino; + else + break; + + if (c) + str = c + 1; + else + break; + } + + ent->minor = minor; + return 0; +} + +static int walk_inodes_cmd(int argc, char **argv) +{ + struct scoutfs_ioctl_walk_inodes_entry ents[128]; + struct scoutfs_ioctl_walk_inodes walk; + u64 total = 0; + int ret; + int fd; + int i; + + if (argc != 4) { + fprintf(stderr, "must specify seq and path\n"); + return -EINVAL; + } + + if (!strcasecmp(argv[0], "size")) + walk.index = SCOUTFS_IOC_WALK_INODES_SIZE; + else if (!strcasecmp(argv[0], "ctime")) + walk.index = SCOUTFS_IOC_WALK_INODES_CTIME; + else if (!strcasecmp(argv[0], "mtime")) + walk.index = SCOUTFS_IOC_WALK_INODES_MTIME; + else { + fprintf(stderr, "unknown index '%s', try 'size', 'ctime, or " + "mtime'\n", argv[0]); + return -EINVAL; + } + + ret = parse_walk_entry(&walk.first, argv[1]); + if (ret) { + fprintf(stderr, "invalid first position '%s', try '1.2.3' or " + "'-1'\n", argv[1]); + return -EINVAL; + + } + + ret = parse_walk_entry(&walk.last, argv[2]); + if (ret) { + fprintf(stderr, "invalid last position '%s', try '1.2.3' or " + "'-1'\n", argv[2]); + return -EINVAL; + + } + + fd = open(argv[3], O_RDONLY); + if (fd < 0) { + ret = -errno; + fprintf(stderr, "failed to open '%s': %s (%d)\n", + argv[3], strerror(errno), errno); + return ret; + } + + walk.entries_ptr = (unsigned long)ents; + walk.nr_entries = array_size(ents); + + for (;;) { + ret = ioctl(fd, SCOUTFS_IOC_WALK_INODES, &walk); + if (ret < 0) { + ret = -errno; + fprintf(stderr, "walk_inodes ioctl failed: %s (%d)\n", + strerror(errno), errno); + break; + } else if (ret == 0) { + break; + } + + for (i = 0; i < ret; i++) { + if ((total + i) % 25 == 0) + printf("%-20s %-20s %-10s %-20s\n", + "#", "major", "minor", "ino"); + + printf("%-20llu %-20llu %-10u %-20llu\n", + total + i, ents[i].major, ents[i].minor, + ents[i].ino); + } + + total += i; + + walk.first = ents[i - 1]; + if (++walk.first.ino == 0 && ++walk.first.minor == 0) + walk.first.major++; + } + + close(fd); + return ret; +}; + +static void __attribute__((constructor)) walk_inodes_ctor(void) +{ + cmd_register("walk-inodes", " ", + "print range of indexed inodes", walk_inodes_cmd); +} From 08aaa5b430e9113dacac5c4a194f6ddd489336f1 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Tue, 16 May 2017 14:39:03 -0700 Subject: [PATCH 095/235] scoutfs-utils: add stat command The scoutfs stat command is modeled after stat(1) and uses the STAT_MORE ioctl. Signed-off-by: Zach Brown --- utils/src/ioctl.h | 42 +++++++++++++++++++++++++ utils/src/stage_release.c | 38 ----------------------- utils/src/stat.c | 65 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 107 insertions(+), 38 deletions(-) create mode 100644 utils/src/stat.c diff --git a/utils/src/ioctl.h b/utils/src/ioctl.h index 864b9b8f..822147d3 100644 --- a/utils/src/ioctl.h +++ b/utils/src/ioctl.h @@ -10,6 +10,27 @@ struct scoutfs_ioctl_walk_inodes_entry { __u64 ino; } __packed; +/* + * Walk inodes in an index that is sorted by one of their fields. + * + * Each index is built from generic index items that have major and + * minor values that are set to the field being indexed. In time + * indices, for example, major is seconds and minor is nanoseconds. + * + * @first The first index entry that can be returned. + * @last The last index entry that can be returned. + * @entries_ptr Pointer to emory containing buffer for entry results. + * @nr_entries The number of entries that can fit in the buffer. + * @index Which index to walk, enumerated in _WALK_INODES_ constants. + * + * To start iterating first can be memset to 0 and last to 0xff. Then + * after each set of results first can be set to the last entry returned + * and then the fields can be incremented in reverse sort order (ino < + * minor < major) as each increasingly significant value wraps around to + * 0. + * + * If first is greater than last then the walk will return 0 entries. + */ struct scoutfs_ioctl_walk_inodes { struct scoutfs_ioctl_walk_inodes_entry first; struct scoutfs_ioctl_walk_inodes_entry last; @@ -107,4 +128,25 @@ struct scoutfs_ioctl_stage { #define SCOUTFS_IOC_STAGE _IOW(SCOUTFS_IOCTL_MAGIC, 6, \ struct scoutfs_ioctl_stage) +/* + * Give the user inode fields that are not otherwise visible. statx() + * isn't always available and xattrs are relatively expensive. + * + * @valid_bytes stores the number of bytes that are valid in the + * structure. The caller sets this to the size of the struct that they + * understand. The kernel then fills and copies back the min of the + * size they and the user caller understand. The user can tell if a + * field is set if all of its bytes are within the valid_bytes that the + * kernel set on return. + * + * New fields are only added to the end of the struct. + */ +struct scoutfs_ioctl_stat_more { + __u64 valid_bytes; + __u64 data_version; +} __packed; + +#define SCOUTFS_IOC_STAT_MORE _IOW(SCOUTFS_IOCTL_MAGIC, 7, \ + struct scoutfs_ioctl_stat_more) + #endif diff --git a/utils/src/stage_release.c b/utils/src/stage_release.c index 581cb69f..61750b1a 100644 --- a/utils/src/stage_release.c +++ b/utils/src/stage_release.c @@ -193,41 +193,3 @@ static void __attribute__((constructor)) release_ctor(void) cmd_register("release", " ", "mark file region offline and free extents", release_cmd); } - -static int data_version_cmd(int argc, char **argv) -{ - u64 vers; - int ret; - int fd; - - if (argc != 1) { - fprintf(stderr, "must specify path\n"); - return -EINVAL; - } - - fd = open(argv[0], O_RDWR); - if (fd < 0) { - ret = -errno; - fprintf(stderr, "failed to open '%s': %s (%d)\n", - argv[0], strerror(errno), errno); - return ret; - } - - ret = ioctl(fd, SCOUTFS_IOC_DATA_VERSION, &vers); - if (ret < 0) { - ret = -errno; - fprintf(stderr, "data version ioctl failed: %s (%d)\n", - strerror(errno), errno); - } else { - printf("%llu\n", vers); - } - - close(fd); - return ret; -}; - -static void __attribute__((constructor)) data_version_ctor(void) -{ - cmd_register("data_version", "", - "print the file's data version", data_version_cmd); -} diff --git a/utils/src/stat.c b/utils/src/stat.c new file mode 100644 index 00000000..11d0972e --- /dev/null +++ b/utils/src/stat.c @@ -0,0 +1,65 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sparse.h" +#include "util.h" +#include "format.h" +#include "ioctl.h" +#include "cmd.h" + +static int stat_more_cmd(int argc, char **argv) +{ + struct scoutfs_ioctl_stat_more stm; + char *path; + int ret; + int fd; + int i; + + if (argc == 0) { + fprintf(stderr, "must specify at least one path argument\n"); + return -EINVAL; + } + + for (i = 0; i < argc; i++) { + path = argv[i]; + + fd = open(path, O_RDONLY); + if (fd < 0) { + ret = -errno; + fprintf(stderr, "failed to open '%s': %s (%d)\n", + path, strerror(errno), errno); + continue; + } + + memset(&stm, 0, sizeof(stm)); + stm.valid_bytes = sizeof(stm); + + ret = ioctl(fd, SCOUTFS_IOC_STAT_MORE, &stm); + if (ret < 0) { + ret = -errno; + fprintf(stderr, "stat_more ioctl failed on '%s': " + "%s (%d)\n", path, strerror(errno), errno); + } else { + printf(" File: '%s'\n" + " data_version: %-20llu\n", + path, stm.data_version); + } + + close(fd); + } + + return 0; +} + +static void __attribute__((constructor)) stat_more_ctor(void) +{ + cmd_register("stat", "", + "print scoutfs stat information for path", stat_more_cmd); +} From 228c5d8b4b84c6939495fd124d35d42e808831cc Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Fri, 19 May 2017 08:56:40 -0700 Subject: [PATCH 096/235] scoutfs-utils: support meta and data seqs Signed-off-by: Zach Brown --- utils/src/format.h | 19 ++++++++++++++++--- utils/src/ioctl.h | 4 ++++ utils/src/mkfs.c | 13 +++++++------ utils/src/print.c | 9 +++++++-- utils/src/stat.c | 4 +++- utils/src/walk_inodes.c | 4 ++++ 6 files changed, 41 insertions(+), 12 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index 7e91afa1..dd991c2d 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -163,6 +163,8 @@ struct scoutfs_segment_block { #define SCOUTFS_INODE_INDEX_CTIME_KEY 13 #define SCOUTFS_INODE_INDEX_MTIME_KEY 14 #define SCOUTFS_INODE_INDEX_SIZE_KEY 15 +#define SCOUTFS_INODE_INDEX_META_SEQ_KEY 16 +#define SCOUTFS_INODE_INDEX_DATA_SEQ_KEY 17 /* not found in the fs */ #define SCOUTFS_MAX_UNUSED_KEY 253 #define SCOUTFS_NET_ADDR_KEY 254 @@ -280,6 +282,7 @@ struct scoutfs_super_block { __le64 id; __u8 uuid[SCOUTFS_UUID_BYTES]; __le64 next_ino; + __le64 next_seq; __le64 alloc_uninit; __le64 total_segs; __le64 free_segs; @@ -300,6 +303,14 @@ struct scoutfs_timespec { } __packed; /* + * @meta_seq: advanced the first time an inode is updated in a given + * transaction. It can only advance again after the inode is written + * and a new transaction opens. + * + * @data_seq: advanced the first time a file's data (or size) is + * modified in a given transaction. It can only advance again after the + * file is written and a new transaction opens. + * * @data_version: incremented every time the contents of a file could * have changed. It is exposed via an ioctl and is then provided as an * argument to data functions to protect racing modification. @@ -314,6 +325,8 @@ struct scoutfs_timespec { struct scoutfs_inode { __le64 size; __le64 blocks; + __le64 meta_seq; + __le64 data_seq; __le64 data_version; __le64 next_readdir_pos; __le32 nlink; @@ -426,13 +439,13 @@ struct scoutfs_net_segnos { } __packed; enum { - /* sends and receives a struct scoutfs_timeval */ - SCOUTFS_NET_TRADE_TIME = 0, - SCOUTFS_NET_ALLOC_INODES, + SCOUTFS_NET_ALLOC_INODES = 0, SCOUTFS_NET_MANIFEST_RANGE_ENTRIES, SCOUTFS_NET_ALLOC_SEGNO, SCOUTFS_NET_RECORD_SEGMENT, SCOUTFS_NET_BULK_ALLOC, + SCOUTFS_NET_ADVANCE_SEQ, + SCOUTFS_NET_GET_LAST_SEQ, SCOUTFS_NET_UNKNOWN, }; diff --git a/utils/src/ioctl.h b/utils/src/ioctl.h index 822147d3..64fd525d 100644 --- a/utils/src/ioctl.h +++ b/utils/src/ioctl.h @@ -43,6 +43,8 @@ enum { SCOUTFS_IOC_WALK_INODES_CTIME = 0, SCOUTFS_IOC_WALK_INODES_MTIME, SCOUTFS_IOC_WALK_INODES_SIZE, + SCOUTFS_IOC_WALK_INODES_META_SEQ, + SCOUTFS_IOC_WALK_INODES_DATA_SEQ, SCOUTFS_IOC_WALK_INODES_UNKNOWN, }; @@ -143,6 +145,8 @@ struct scoutfs_ioctl_stage { */ struct scoutfs_ioctl_stat_more { __u64 valid_bytes; + __u64 meta_seq; + __u64 data_seq; __u64 data_version; } __packed; diff --git a/utils/src/mkfs.c b/utils/src/mkfs.c index 005723d8..31d719ae 100644 --- a/utils/src/mkfs.c +++ b/utils/src/mkfs.c @@ -131,6 +131,7 @@ static int write_new_fs(char *path, int fd) super->id = cpu_to_le64(SCOUTFS_SUPER_ID); 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->next_seg_seq = cpu_to_le64(2); @@ -193,7 +194,7 @@ static int write_new_fs(char *path, int fd) ikey->type = SCOUTFS_INODE_KEY; ikey->ino = cpu_to_be64(SCOUTFS_ROOT_INO); idx_key = (void *)(ikey + 1); - idx_key->type = SCOUTFS_INODE_INDEX_SIZE_KEY; + idx_key->type = SCOUTFS_INODE_INDEX_META_SEQ_KEY; idx_key->major = cpu_to_be64(0); idx_key->minor = 0; idx_key->ino = cpu_to_be64(SCOUTFS_ROOT_INO); @@ -212,12 +213,12 @@ static int write_new_fs(char *path, int fd) /* write seg with root inode */ sblk->segno = cpu_to_le64(first_segno); sblk->seq = cpu_to_le64(1); - sblk->nr_items = cpu_to_le32(4); + sblk->nr_items = cpu_to_le32(5); item = &sblk->items[0]; - ikey = (void *)&sblk->items[4]; + ikey = (void *)&sblk->items[5]; inode = (void *)(ikey + 1) + - (3 * sizeof(struct scoutfs_inode_index_key)); + (4 * sizeof(struct scoutfs_inode_index_key)); item->seq = cpu_to_le64(1); item->key_off = cpu_to_le32((long)ikey - (long)sblk); @@ -243,7 +244,7 @@ static int write_new_fs(char *path, int fd) /* write the root inode index keys */ for (i = SCOUTFS_INODE_INDEX_CTIME_KEY; - i <= SCOUTFS_INODE_INDEX_SIZE_KEY; i++) { + i <= SCOUTFS_INODE_INDEX_META_SEQ_KEY; i++) { item->seq = cpu_to_le64(1); item->key_off = cpu_to_le32((long)idx_key - (long)sblk); @@ -260,7 +261,7 @@ static int write_new_fs(char *path, int fd) idx_key->major = cpu_to_be64(tv.tv_sec); idx_key->minor = cpu_to_be32(tv.tv_usec * 1000); break; - case SCOUTFS_INODE_INDEX_SIZE_KEY: + default: idx_key->major = cpu_to_be64(0); idx_key->minor = 0; break; diff --git a/utils/src/print.c b/utils/src/print.c index 9dd03192..3fb5a313 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -80,7 +80,7 @@ static void print_inode(void *key, int key_len, void *val, int val_len) printf(" inode: ino %llu size %llu blocks %llu nlink %u\n" " uid %u gid %u mode 0%o rdev 0x%x\n" - " next_readdir_pos %llu data_version %llu\n" + " next_readdir_pos %llu meta_seq %llu data_seq %llu data_version %llu\n" " atime %llu.%08u ctime %llu.%08u\n" " mtime %llu.%08u\n", be64_to_cpu(ikey->ino), @@ -89,6 +89,8 @@ static void print_inode(void *key, int key_len, void *val, int val_len) le32_to_cpu(inode->gid), le32_to_cpu(inode->mode), le32_to_cpu(inode->rdev), le64_to_cpu(inode->next_readdir_pos), + le64_to_cpu(inode->meta_seq), + le64_to_cpu(inode->data_seq), le64_to_cpu(inode->data_version), le64_to_cpu(inode->atime.sec), le32_to_cpu(inode->atime.nsec), @@ -250,6 +252,8 @@ static print_func_t printers[] = { [SCOUTFS_INODE_INDEX_CTIME_KEY] = print_inode_index, [SCOUTFS_INODE_INDEX_MTIME_KEY] = print_inode_index, [SCOUTFS_INODE_INDEX_SIZE_KEY] = print_inode_index, + [SCOUTFS_INODE_INDEX_META_SEQ_KEY] = print_inode_index, + [SCOUTFS_INODE_INDEX_DATA_SEQ_KEY] = print_inode_index, }; /* utils uses big contiguous allocations */ @@ -443,11 +447,12 @@ static void print_super_block(struct scoutfs_super_block *super, u64 blkno) printf(" id %llx uuid %s\n", le64_to_cpu(super->id), uuid_str); /* XXX these are all in a crazy order */ - printf(" next_ino %llu\n" + printf(" next_ino %llu next_seq %llu\n" " ring_blkno %llu ring_blocks %llu ring_tail_block %llu\n" " ring_gen %llu alloc_uninit %llu total_segs %llu\n" " next_seg_seq %llu free_segs %llu\n", le64_to_cpu(super->next_ino), + le64_to_cpu(super->next_seq), le64_to_cpu(super->ring_blkno), le64_to_cpu(super->ring_blocks), le64_to_cpu(super->ring_tail_block), diff --git a/utils/src/stat.c b/utils/src/stat.c index 11d0972e..b584441b 100644 --- a/utils/src/stat.c +++ b/utils/src/stat.c @@ -48,8 +48,10 @@ static int stat_more_cmd(int argc, char **argv) "%s (%d)\n", path, strerror(errno), errno); } else { printf(" File: '%s'\n" + " meta_seq: %-20llu data_seq %-20llu" " data_version: %-20llu\n", - path, stm.data_version); + path, stm.meta_seq, stm.data_seq, + stm.data_version); } close(fd); diff --git a/utils/src/walk_inodes.c b/utils/src/walk_inodes.c index 80ef6d6c..5d06c4cd 100644 --- a/utils/src/walk_inodes.c +++ b/utils/src/walk_inodes.c @@ -86,6 +86,10 @@ static int walk_inodes_cmd(int argc, char **argv) walk.index = SCOUTFS_IOC_WALK_INODES_CTIME; else if (!strcasecmp(argv[0], "mtime")) walk.index = SCOUTFS_IOC_WALK_INODES_MTIME; + else if (!strcasecmp(argv[0], "meta_seq")) + walk.index = SCOUTFS_IOC_WALK_INODES_META_SEQ; + else if (!strcasecmp(argv[0], "data_seq")) + walk.index = SCOUTFS_IOC_WALK_INODES_DATA_SEQ; else { fprintf(stderr, "unknown index '%s', try 'size', 'ctime, or " "mtime'\n", argv[0]); From 51ae302d816bac18043920d9d28b83020e496eb0 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Fri, 9 Jun 2017 14:07:59 -0700 Subject: [PATCH 097/235] scoutfs-utils: add key printing Just lift the key printer from the kernel and use it to print item keys in segments and in manifest entries. Signed-off-by: Zach Brown --- utils/src/key.c | 148 ++++++++++++++++++++++++++++++++++++++++++++++ utils/src/key.h | 6 ++ utils/src/print.c | 10 ++++ 3 files changed, 164 insertions(+) create mode 100644 utils/src/key.c create mode 100644 utils/src/key.h diff --git a/utils/src/key.c b/utils/src/key.c new file mode 100644 index 00000000..1487f9e3 --- /dev/null +++ b/utils/src/key.c @@ -0,0 +1,148 @@ +#include +#include +#include +#include + +#include "sparse.h" +#include "util.h" +#include "format.h" +#include "key.h" + +/* + * This is mechanically derived from scoutfs_key_str() in the kernel: + * - s/key->data/key_data/ + * - s/key->key_len/key_len/ + * - s/return snprintf_null(buf, size, /return printf(/ + */ +int print_key(void *key_data, unsigned key_len) +{ + int len; + u8 type; + + if (key_data == NULL) + return printf("[NULL]"); + + if (key_len == 0) + return printf("[0 len]"); + + type = *(u8 *)key_data; + + switch(type) { + + case SCOUTFS_INODE_KEY: { + struct scoutfs_inode_key *ikey = key_data; + + if (key_len < sizeof(struct scoutfs_inode_key)) + break; + + return printf("ino.%llu", + be64_to_cpu(ikey->ino)); + } + + case SCOUTFS_XATTR_KEY: { + struct scoutfs_xattr_key *xkey = key_data; + + len = (int)key_len - offsetof(struct scoutfs_xattr_key, + name[1]); + if (len <= 0) + break; + + return printf("xat.%llu.%.*s", + be64_to_cpu(xkey->ino), len, xkey->name); + } + + case SCOUTFS_DIRENT_KEY: { + struct scoutfs_dirent_key *dkey = key_data; + + len = (int)key_len - sizeof(struct scoutfs_dirent_key); + if (len <= 0) + break; + + return printf("dnt.%llu.%.*s", + be64_to_cpu(dkey->ino), len, dkey->name); + } + + case SCOUTFS_READDIR_KEY: { + struct scoutfs_readdir_key *rkey = key_data; + + return printf("rdr.%llu.%llu", + be64_to_cpu(rkey->ino), + be64_to_cpu(rkey->pos)); + } + + case SCOUTFS_LINK_BACKREF_KEY: { + struct scoutfs_link_backref_key *lkey = key_data; + + len = (int)key_len - sizeof(*lkey); + if (len <= 0) + break; + + return printf("lbr.%llu.%llu.%.*s", + be64_to_cpu(lkey->ino), + be64_to_cpu(lkey->dir_ino), len, + lkey->name); + } + + case SCOUTFS_SYMLINK_KEY: { + struct scoutfs_symlink_key *skey = key_data; + + return printf("sym.%llu", + be64_to_cpu(skey->ino)); + } + + case SCOUTFS_FILE_EXTENT_KEY: { + struct scoutfs_file_extent_key *ekey = key_data; + + return printf("ext.%llu.%llu.%llu.%llu.%x", + be64_to_cpu(ekey->ino), + be64_to_cpu(ekey->last_blk_off), + be64_to_cpu(ekey->last_blkno), + be64_to_cpu(ekey->blocks), + ekey->flags); + } + + case SCOUTFS_ORPHAN_KEY: { + struct scoutfs_orphan_key *okey = key_data; + + return printf("orp.%llu", + be64_to_cpu(okey->ino)); + } + + case SCOUTFS_FREE_EXTENT_BLKNO_KEY: + case SCOUTFS_FREE_EXTENT_BLOCKS_KEY: { + struct scoutfs_free_extent_blkno_key *fkey = key_data; + + return printf("%s.%llu.%llu.%llu", + fkey->type == SCOUTFS_FREE_EXTENT_BLKNO_KEY ? "fel" : + "fes", + be64_to_cpu(fkey->node_id), + be64_to_cpu(fkey->last_blkno), + be64_to_cpu(fkey->blocks)); + } + + case SCOUTFS_INODE_INDEX_CTIME_KEY: + case SCOUTFS_INODE_INDEX_MTIME_KEY: + case SCOUTFS_INODE_INDEX_SIZE_KEY: + case SCOUTFS_INODE_INDEX_META_SEQ_KEY: + case SCOUTFS_INODE_INDEX_DATA_SEQ_KEY: { + struct scoutfs_inode_index_key *ikey = key_data; + + return printf("%s.%llu.%u.%llu", + ikey->type == SCOUTFS_INODE_INDEX_CTIME_KEY ? "ctm" : + ikey->type == SCOUTFS_INODE_INDEX_MTIME_KEY ? "mtm" : + ikey->type == SCOUTFS_INODE_INDEX_SIZE_KEY ? "siz" : + ikey->type == SCOUTFS_INODE_INDEX_META_SEQ_KEY ? "msq" : + ikey->type == SCOUTFS_INODE_INDEX_DATA_SEQ_KEY ? "dsq" : + "uii", be64_to_cpu(ikey->major), + be32_to_cpu(ikey->minor), + be64_to_cpu(ikey->ino)); + } + + default: + return printf("[unknown type %u len %u]", + type, key_len); + } + + return printf("[truncated type %u len %u]", + type, key_len); +} diff --git a/utils/src/key.h b/utils/src/key.h new file mode 100644 index 00000000..ebefbf9c --- /dev/null +++ b/utils/src/key.h @@ -0,0 +1,6 @@ +#ifndef _KEY_H_ +#define _KEY_H_ + +int print_key(void *key_data, unsigned key_len); + +#endif diff --git a/utils/src/print.c b/utils/src/print.c index 3fb5a313..7f2b5df6 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -16,6 +16,7 @@ #include "bitmap.h" #include "cmd.h" #include "crc.h" +#include "key.h" static void *read_block(int fd, u64 blkno) { @@ -294,6 +295,9 @@ static void print_item(struct scoutfs_segment_block *sblk, u32 pos) le32_to_cpu(item->val_off), le16_to_cpu(item->key_len), le16_to_cpu(item->val_len), item->flags, printer ? "" : " (unrecognized type)"); + printf(" key: "); + print_key(key, le16_to_cpu(item->key_len)); + printf("\n"); if (printer) printer(key, le16_to_cpu(item->key_len), @@ -354,6 +358,12 @@ static int print_manifest_entry(int fd, struct scoutfs_ring_entry *rent, le16_to_cpu(ment->first_key_len), le16_to_cpu(ment->last_key_len), ment->level); + printf(" first: "); + print_key(ment->keys, le16_to_cpu(ment->first_key_len)); + printf("\n last: "); + print_key(ment->keys + le16_to_cpu(ment->first_key_len), + le16_to_cpu(ment->last_key_len)); + printf("\n"); if (rent->flags & SCOUTFS_RING_ENTRY_FLAG_DELETION) clear_bit(seg_map, le64_to_cpu(ment->segno)); From c6eaccbf90f8ee59e264ca1c008de55a21dceae3 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Thu, 15 Jun 2017 22:09:01 -0700 Subject: [PATCH 098/235] scoutfs-utils: add item cache keys commands Add ioctls to get the keys for cached ranges and items and print them. Signed-off-by: Zach Brown --- utils/src/ioctl.h | 27 ++++++++ utils/src/item-cache-keys.c | 131 ++++++++++++++++++++++++++++++++++++ 2 files changed, 158 insertions(+) create mode 100644 utils/src/item-cache-keys.c diff --git a/utils/src/ioctl.h b/utils/src/ioctl.h index 64fd525d..447b4f8e 100644 --- a/utils/src/ioctl.h +++ b/utils/src/ioctl.h @@ -29,7 +29,18 @@ struct scoutfs_ioctl_walk_inodes_entry { * minor < major) as each increasingly significant value wraps around to * 0. * + * These indexes are not strictly consistent. The items that back these + * index entries aren't updated with cluster locks so they're not + * guaranteed to be visible the moment you read after writing. They're + * only visible when the transaction that updated them is synced. + * + * In addition, the seq indexes will only allow walking through sequence + * space that has been consistent. This prevents old dirty entries from + * becoming visible after newer stable entries are displayed. + * * If first is greater than last then the walk will return 0 entries. + * + * XXX invalidate before reading. */ struct scoutfs_ioctl_walk_inodes { struct scoutfs_ioctl_walk_inodes_entry first; @@ -153,4 +164,20 @@ struct scoutfs_ioctl_stat_more { #define SCOUTFS_IOC_STAT_MORE _IOW(SCOUTFS_IOCTL_MAGIC, 7, \ struct scoutfs_ioctl_stat_more) +struct scoutfs_ioctl_item_cache_keys { + __u64 key_ptr; + __u64 key_len; + __u64 buf_ptr; + __u64 buf_len; + __u8 which; +} __packed; + +enum { + SCOUTFS_IOC_ITEM_CACHE_KEYS_ITEMS = 0, + SCOUTFS_IOC_ITEM_CACHE_KEYS_RANGES, +}; + +#define SCOUTFS_IOC_ITEM_CACHE_KEYS _IOW(SCOUTFS_IOCTL_MAGIC, 8, \ + struct scoutfs_ioctl_item_cache_keys) + #endif diff --git a/utils/src/item-cache-keys.c b/utils/src/item-cache-keys.c new file mode 100644 index 00000000..765f77d8 --- /dev/null +++ b/utils/src/item-cache-keys.c @@ -0,0 +1,131 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sparse.h" +#include "util.h" +#include "format.h" +#include "ioctl.h" +#include "cmd.h" +#include "key.h" + +#define BUF_SIZE (64 * 1024) + +static int item_cache_keys(int argc, char **argv, int which) +{ + struct scoutfs_ioctl_item_cache_keys ick; + unsigned nr; + u16 key_len; + void *buf; + void *ptr; + int ret; + int fd; + + if (argc != 1) { + fprintf(stderr, "too many arguments, only scoutfs path needed"); + return -EINVAL; + } + + buf = malloc(BUF_SIZE); + if (!buf) { + ret = -errno; + fprintf(stderr, "failed to allocate buf: %s (%d)\n", + strerror(errno), errno); + return ret; + } + + fd = open(argv[0], O_RDONLY); + if (fd < 0) { + ret = -errno; + fprintf(stderr, "failed to open '%s': %s (%d)\n", + argv[0], strerror(errno), errno); + free(buf); + return ret; + } + + ick.buf_ptr = (unsigned long)buf; + ick.buf_len = BUF_SIZE; + ick.key_ptr = 0; + ick.key_len = 0; + ick.which = which; + + nr = 1; + for (;;) { + ret = ioctl(fd, SCOUTFS_IOC_ITEM_CACHE_KEYS, &ick); + if (ret < 0) { + ret = -errno; + fprintf(stderr, "walk_inodes ioctl failed: %s (%d)\n", + strerror(errno), errno); + break; + } else if (ret == 0) { + break; + } + + ptr = (void *)(unsigned long)ick.buf_ptr; + + while (ret) { + if (ret < sizeof(key_len)) { + fprintf(stderr, "truncated len: %d\n", ret); + ret = -EINVAL; + break; + } + + memcpy(&key_len, ptr, sizeof(key_len)); + ptr += sizeof(key_len); + ret -= sizeof(key_len); + + if (ret < key_len) { + fprintf(stderr, "key len %d < buffer %d\n", + key_len, ret); + ret = -EINVAL; + break; + } + + print_key(ptr, key_len); + if (which == SCOUTFS_IOC_ITEM_CACHE_KEYS_ITEMS || + (nr % 2) == 0) + printf("\n"); + else + printf(" - "); + + ick.key_ptr = (unsigned long)ptr; + ick.key_len = key_len; + + ptr += key_len; + ret -= key_len; + + nr++; + } + if (ret < 0) + break; + } + + close(fd); + free(buf); + return ret; +}; + +static int item_keys(int argc, char **argv) +{ + return item_cache_keys(argc, argv, SCOUTFS_IOC_ITEM_CACHE_KEYS_ITEMS); +} + +static int range_keys(int argc, char **argv) +{ + return item_cache_keys(argc, argv, SCOUTFS_IOC_ITEM_CACHE_KEYS_RANGES); +} + +static void __attribute__((constructor)) item_cache_key_ctor(void) +{ + cmd_register("item-cache-keys", "", + "print range of indexed inodes", item_keys); + cmd_register("item-cache-range-keys", "", + "print range of indexed inodes", range_keys); +} From 6ae8e9743f29409540c45beb1b6ce3223abf11c8 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Mon, 19 Jun 2017 15:23:17 -0700 Subject: [PATCH 099/235] scoutfs-utils: add support for skip list segments Signed-off-by: Zach Brown --- utils/src/format.h | 58 +++++++++++++++++++++++++++++++++---------- utils/src/mkfs.c | 35 +++++++++++++++----------- utils/src/print.c | 62 ++++++++++++++++++++++------------------------ 3 files changed, 96 insertions(+), 59 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index dd991c2d..1f994eec 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -112,19 +112,29 @@ struct scoutfs_alloc_region { } __packed; /* - * We really want these to be a power of two size so that they're naturally - * aligned. This ensures that they won't cross page boundaries and we - * can use pointers to them in the page vecs that make up segments without - * funny business. + * The max number of links defines the max number of entries that we can + * index in o(log n) and the static list head storage size in the + * segment block. We always pay the static storage cost, which is tiny, + * and we can look at the number of items to know the greatest number of + * links and skip most of the initial 0 links. + */ +#define SCOUTFS_MAX_SKIP_LINKS 32 + +/* + * Items are packed into segments and linked together in a skip list. + * Each item's header, links, key, and value are stored contiguously. + * They're not allowed to cross a block boundary. */ struct scoutfs_segment_item { - __le64 seq; - __le32 key_off; - __le32 val_off; __le16 key_len; __le16 val_len; - __u8 padding[11]; __u8 flags; + __u8 nr_links; + __le32 skip_links[0]; + /* + * u8 key_bytes[key_len] + * u8 val_bytes[val_len] + */ } __packed; #define SCOUTFS_ITEM_FLAG_DELETION (1 << 0) @@ -138,11 +148,11 @@ struct scoutfs_segment_block { __le32 _padding; __le64 segno; __le64 seq; + __le32 last_item_off; + __le32 total_bytes; __le32 nr_items; - __le32 _moar_pads; - struct scoutfs_segment_item items[0]; - /* packed keys */ - /* packed vals */ + __le32 skip_links[SCOUTFS_MAX_SKIP_LINKS]; + /* packed items */ } __packed; /* @@ -160,7 +170,7 @@ struct scoutfs_segment_block { #define SCOUTFS_ORPHAN_KEY 10 #define SCOUTFS_FREE_EXTENT_BLKNO_KEY 11 #define SCOUTFS_FREE_EXTENT_BLOCKS_KEY 12 -#define SCOUTFS_INODE_INDEX_CTIME_KEY 13 +#define SCOUTFS_INODE_INDEX_CTIME_KEY 13 /* don't forget first and last */ #define SCOUTFS_INODE_INDEX_MTIME_KEY 14 #define SCOUTFS_INODE_INDEX_SIZE_KEY 15 #define SCOUTFS_INODE_INDEX_META_SEQ_KEY 16 @@ -170,6 +180,11 @@ struct scoutfs_segment_block { #define SCOUTFS_NET_ADDR_KEY 254 #define SCOUTFS_NET_LISTEN_KEY 255 +#define SCOUTFS_INODE_INDEX_FIRST SCOUTFS_INODE_INDEX_CTIME_KEY +#define SCOUTFS_INODE_INDEX_LAST SCOUTFS_INODE_INDEX_DATA_SEQ_KEY +#define SCOUTFS_INODE_INDEX_NR \ + (SCOUTFS_INODE_INDEX_LAST - SCOUTFS_INODE_INDEX_FIRST + 1) + /* value is struct scoutfs_inode */ struct scoutfs_inode_key { __u8 type; @@ -388,6 +403,10 @@ enum { #define SCOUTFS_MAX_KEY_SIZE \ offsetof(struct scoutfs_link_backref_key, name[SCOUTFS_NAME_LEN + 1]) +/* largest single val are dirents, larger broken up into units of this */ +#define SCOUTFS_MAX_VAL_SIZE \ + offsetof(struct scoutfs_dirent, name[SCOUTFS_NAME_LEN]) + /* * messages over the wire. */ @@ -433,11 +452,24 @@ struct scoutfs_net_manifest_entries { struct scoutfs_manifest_entry ments[0]; } __packed; +/* XXX I dunno, totally made up */ +#define SCOUTFS_BULK_ALLOC_COUNT 32 + struct scoutfs_net_segnos { __le16 nr; __le64 segnos[0]; } __packed; +/* XXX eventually we'll have net compaction and will need agents to agree */ + +/* one upper segment and fanout lower segments */ +#define SCOUTFS_COMPACTION_MAX_INPUT (1 + SCOUTFS_MANIFEST_FANOUT) +/* sticky can add one, and so can item page alignment */ +#define SCOUTFS_COMPACTION_SLOP 2 +/* delete all inputs and insert all outputs (same goes for alloc|free segnos) */ +#define SCOUTFS_COMPACTION_MAX_UPDATE \ + (2 * (SCOUTFS_COMPACTION_MAX_INPUT + SCOUTFS_COMPACTION_SLOP)) + enum { SCOUTFS_NET_ALLOC_INODES = 0, SCOUTFS_NET_MANIFEST_RANGE_ENTRIES, diff --git a/utils/src/mkfs.c b/utils/src/mkfs.c index 31d719ae..07723de2 100644 --- a/utils/src/mkfs.c +++ b/utils/src/mkfs.c @@ -84,6 +84,7 @@ static int write_new_fs(char *path, int fd) struct scoutfs_ring_block *rblk; struct scoutfs_ring_entry *rent; struct scoutfs_segment_item *item; + __le32 *prev_link; struct timeval tv; char uuid_str[37]; u64 blkno; @@ -214,17 +215,15 @@ static int write_new_fs(char *path, int fd) sblk->segno = cpu_to_le64(first_segno); sblk->seq = cpu_to_le64(1); sblk->nr_items = cpu_to_le32(5); + prev_link = &sblk->skip_links[0]; - item = &sblk->items[0]; - ikey = (void *)&sblk->items[5]; - inode = (void *)(ikey + 1) + - (4 * sizeof(struct scoutfs_inode_index_key)); + item = (void *)(sblk + 1); + ikey = (void *)&item->skip_links[1]; + inode = (void *)ikey + sizeof(struct scoutfs_inode_key); - item->seq = cpu_to_le64(1); - item->key_off = cpu_to_le32((long)ikey - (long)sblk); - item->val_off = cpu_to_le32((long)inode - (long)sblk); item->key_len = cpu_to_le16(sizeof(struct scoutfs_inode_key)); item->val_len = cpu_to_le16(sizeof(struct scoutfs_inode)); + item->nr_links = 1; ikey->type = SCOUTFS_INODE_KEY; ikey->ino = cpu_to_be64(SCOUTFS_ROOT_INO); @@ -239,18 +238,19 @@ static int write_new_fs(char *path, int fd) inode->mtime.sec = inode->atime.sec; inode->mtime.nsec = inode->atime.nsec; - item = (void *)(item + 1); - idx_key = (void *)(ikey + 1); + *prev_link = cpu_to_le32((long)item -(long)sblk); + prev_link = &item->skip_links[0]; + + item = (void *)inode + sizeof(struct scoutfs_inode); + idx_key = (void *)&item->skip_links[1]; /* write the root inode index keys */ for (i = SCOUTFS_INODE_INDEX_CTIME_KEY; i <= SCOUTFS_INODE_INDEX_META_SEQ_KEY; i++) { - item->seq = cpu_to_le64(1); - item->key_off = cpu_to_le32((long)idx_key - (long)sblk); - item->val_off = 0; item->key_len = cpu_to_le16(sizeof(*idx_key)); item->val_len = 0; + item->nr_links = 1; idx_key->type = i; idx_key->ino = cpu_to_be64(SCOUTFS_ROOT_INO); @@ -267,10 +267,17 @@ static int write_new_fs(char *path, int fd) break; } - item = (void *)(item + 1); - idx_key = (void *)(idx_key + 1); + *prev_link = cpu_to_le32((long)item -(long)sblk); + prev_link = &item->skip_links[0]; + + sblk->last_item_off = cpu_to_le32((long)item - (long)sblk); + + item = (void *)(idx_key + 1); + idx_key = (void *)&item->skip_links[1]; } + sblk->total_bytes = cpu_to_le32((long)item - (long)sblk); + ret = pwrite(fd, sblk, SCOUTFS_SEGMENT_SIZE, first_segno << SCOUTFS_SEGMENT_SHIFT); if (ret != SCOUTFS_SEGMENT_SIZE) { diff --git a/utils/src/print.c b/utils/src/print.c index 7f2b5df6..72eb7f15 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -257,45 +257,29 @@ static print_func_t printers[] = { [SCOUTFS_INODE_INDEX_DATA_SEQ_KEY] = print_inode_index, }; -/* utils uses big contiguous allocations */ -static void *off_ptr(struct scoutfs_segment_block *sblk, u32 off) -{ - return (char *)sblk + off; -} - -static u32 pos_off(struct scoutfs_segment_block *sblk, u32 pos) -{ - return offsetof(struct scoutfs_segment_block, items[pos]); -} - -static void *pos_ptr(struct scoutfs_segment_block *sblk, u32 pos) -{ - return off_ptr(sblk, pos_off(sblk, pos)); -} - -static void print_item(struct scoutfs_segment_block *sblk, u32 pos) +static void print_item(struct scoutfs_segment_block *sblk, + struct scoutfs_segment_item *item, u32 which, u32 off) { print_func_t printer; - struct scoutfs_segment_item *item; void *key; void *val; __u8 type; + int i; - item = pos_ptr(sblk, pos); - - key = (char *)sblk + le32_to_cpu(item->key_off); - val = (char *)sblk + le32_to_cpu(item->val_off); + key = (char *)&item->skip_links[item->nr_links]; + val = (char *)key + le16_to_cpu(item->key_len); type = *(__u8 *)key; printer = type < array_size(printers) ? printers[type] : NULL; - printf(" [%u]: type %u seq %llu key_off %u val_off %u key_len %u " - "val_len %u flags %x%s\n", - pos, type, le64_to_cpu(item->seq), le32_to_cpu(item->key_off), - le32_to_cpu(item->val_off), le16_to_cpu(item->key_len), - le16_to_cpu(item->val_len), item->flags, - printer ? "" : " (unrecognized type)"); - printf(" key: "); + printf(" [%u]: type %u off %u key_len %u val_len %u nr_links %u flags %x%s\n", + which, type, off, le16_to_cpu(item->key_len), + le16_to_cpu(item->val_len), item->nr_links, + item->flags, printer ? "" : " (unrecognized type)"); + printf(" links:"); + for (i = 0; i < item->nr_links; i++) + printf(" %u", le32_to_cpu(item->skip_links[i])); + printf("\n key: "); print_key(key, le16_to_cpu(item->key_len)); printf("\n"); @@ -306,14 +290,24 @@ static void print_item(struct scoutfs_segment_block *sblk, u32 pos) static void print_segment_block(struct scoutfs_segment_block *sblk) { - printf(" sblk: segno %llu seq %llu nr_items %u\n", + int i; + + printf(" sblk: segno %llu seq %llu last_item_off %u total_bytes %u " + "nr_items %u\n", le64_to_cpu(sblk->segno), le64_to_cpu(sblk->seq), + le32_to_cpu(sblk->last_item_off), le32_to_cpu(sblk->total_bytes), le32_to_cpu(sblk->nr_items)); + printf(" links:"); + for (i = 0; sblk->skip_links[i]; i++) + printf(" %u", le32_to_cpu(sblk->skip_links[i])); + printf("\n"); } static int print_segments(int fd, unsigned long *seg_map, u64 total) { struct scoutfs_segment_block *sblk; + struct scoutfs_segment_item *item; + u32 off; u64 s; u64 i; @@ -325,8 +319,12 @@ static int print_segments(int fd, unsigned long *seg_map, u64 total) printf("segment segno %llu\n", s); print_segment_block(sblk); - for (i = 0; i < le32_to_cpu(sblk->nr_items); i++) - print_item(sblk, i); + off = le32_to_cpu(sblk->skip_links[0]); + for (i = 0; i < le32_to_cpu(sblk->nr_items); i++) { + item = (void *)sblk + off; + print_item(sblk, item, i, off); + off = le32_to_cpu(item->skip_links[0]); + } free(sblk); } From d78649e065c346b7fa4a0354fdb495051d6ee7e7 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Thu, 22 Jun 2017 11:26:00 -0700 Subject: [PATCH 100/235] scoutfs-utils: support symlink item with nr field Signed-off-by: Zach Brown --- utils/src/format.h | 5 ++++- utils/src/print.c | 12 +++++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index 1f994eec..a59ca4ca 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -263,12 +263,15 @@ struct scoutfs_xattr_val_header { __u8 last_part; } __packed; -/* value is the null terminated target path */ +/* size determines nr needed to store full target path in their values */ struct scoutfs_symlink_key { __u8 type; __be64 ino; + __u8 nr; } __packed; +#define SCOUTFS_SYMLINK_MAX_VAL_SIZE 200 + struct scoutfs_betimespec { __be64 sec; __be32 nsec; diff --git a/utils/src/print.c b/utils/src/print.c index 72eb7f15..2e2185af 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -176,11 +176,17 @@ static void print_link_backref(void *key, int key_len, void *val, int val_len) static void print_symlink(void *key, int key_len, void *val, int val_len) { struct scoutfs_symlink_key *skey = key; - u8 *name = global_printable_name(val, val_len - 1); + u8 *frag = val; + u8 *name; - printf(" symlink: ino %llu\n" + /* don't try to print null term */ + if (frag[val_len - 1] == '\0') + val_len--; + name = global_printable_name(frag, val_len); + + printf(" symlink: ino %llu nr %u\n" " target %s\n", - be64_to_cpu(skey->ino), name); + be64_to_cpu(skey->ino), skey->nr, name); } /* From 6c37e3dee05a00c0353dd355dae411a99462ab55 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Mon, 3 Jul 2017 09:50:11 -0700 Subject: [PATCH 101/235] scoutfs-utils: add btree ring storage Manifest entries and segment allocation bitmap regions are now stored in btree items instead of the ring log. This lets us work with them incrementally and share them between nodes. Signed-off-by: Zach Brown --- utils/src/crc.c | 12 +- utils/src/crc.h | 2 +- utils/src/format.h | 197 ++++++++++++++++++++-------- utils/src/mkfs.c | 231 ++++++++++++++++++++++----------- utils/src/print.c | 315 ++++++++++++++++++++++++++++----------------- 5 files changed, 504 insertions(+), 253 deletions(-) diff --git a/utils/src/crc.c b/utils/src/crc.c index 8380cf37..1d027a32 100644 --- a/utils/src/crc.c +++ b/utils/src/crc.c @@ -38,9 +38,15 @@ u32 crc_block(struct scoutfs_block_header *hdr) SCOUTFS_BLOCK_SIZE - sizeof(hdr->crc)); } -u32 crc_ring_block(struct scoutfs_ring_block *rblk) +u32 crc_btree_block(struct scoutfs_btree_block *bt) { - unsigned long skip = (char *)(&rblk->crc + 1) - (char *)rblk; + __le32 old; + u32 crc; - return crc32c(~0, (char *)rblk + skip, SCOUTFS_BLOCK_SIZE - skip); + old = bt->crc; + bt->crc = 0; + crc = crc32c(~0, bt, SCOUTFS_BLOCK_SIZE); + bt->crc = old; + + return crc; } diff --git a/utils/src/crc.h b/utils/src/crc.h index 1006871e..03ce2891 100644 --- a/utils/src/crc.h +++ b/utils/src/crc.h @@ -8,6 +8,6 @@ u32 crc32c(u32 crc, const void *data, unsigned int len); u64 crc32c_64(u32 crc, const void *data, unsigned int len); u32 crc_block(struct scoutfs_block_header *hdr); -u32 crc_ring_block(struct scoutfs_ring_block *rblk); +u32 crc_btree_block(struct scoutfs_btree_block *bt); #endif diff --git a/utils/src/format.h b/utils/src/format.h index a59ca4ca..b1bbdbbd 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -7,7 +7,7 @@ #define SCOUTFS_SUPER_ID 0x2e736674756f6373ULL /* "scoutfs." */ /* - * The super block and ring blocks are fixed 4k. + * The super block and btree blocks are fixed 4k. */ #define SCOUTFS_BLOCK_SHIFT 12 #define SCOUTFS_BLOCK_SIZE (1 << SCOUTFS_BLOCK_SHIFT) @@ -50,30 +50,97 @@ struct scoutfs_block_header { __le64 blkno; } __packed; -struct scoutfs_ring_entry { - __le16 data_len; - __u8 flags; +/* + * The largest possible btree has 2^64 bytes worth of segments with + * the largest possible keys in a pathologically sparse btree where + * all the nodes are half full. + */ + +/* + * Assert that we'll be able to represent all possible keys with 8 64bit + * primary sort values. + */ +#define SCOUTFS_BTREE_GREATEST_KEY_LEN 32 +/* level >0 segments can have a full key and some metadata */ +#define SCOUTFS_BTREE_MAX_KEY_LEN 320 +/* level 0 segments can have two full keys in the value :/ */ +#define SCOUTFS_BTREE_MAX_VAL_LEN 768 + +/* + * A 4EB test image measured a worst case height of 17. This is plenty + * generous. + */ +#define SCOUTFS_BTREE_MAX_HEIGHT 20 + +/* btree blocks (beyond the first) need to be at least half full */ +#define SCOUTFS_BTREE_FREE_LIMIT \ + ((SCOUTFS_BLOCK_SIZE - sizeof(struct scoutfs_btree_block)) / 2) + +#define SCOUTFS_BTREE_BITS 8 + +/* + * Btree items can have bits associated with them. Their parent items + * reflect all the bits that their child block contain. Thus searches + * can find items with bits set. + * + * @SCOUTFS_BTREE_BIT_HALF1: Tracks blocks found in the first half of + * the ring. It's used to migrate blocks from the old half of the ring + * into the current half as blocks are dirtied. It's not found in leaf + * items but is calculated based on the block number of referenced + * blocks. _HALF2 is identical but for the second half of the ring. + */ +enum { + SCOUTFS_BTREE_BIT_HALF1 = (1 << 0), + SCOUTFS_BTREE_BIT_HALF2 = (1 << 1), +}; + +#define SCOUTFS_BTREE_HALF_BITS \ + (SCOUTFS_BTREE_BIT_HALF1 | SCOUTFS_BTREE_BIT_HALF2) + +struct scoutfs_btree_ref { + __le64 blkno; + __le64 seq; +} __packed; + +/* + * A height of X means that the first block read will have level X-1 and + * the leaves will have level 0. + */ +struct scoutfs_btree_root { + struct scoutfs_btree_ref ref; + __u8 height; +} __packed; + +struct scoutfs_btree_item_header { + __le16 off; + __u8 bits; +} __packed; + +struct scoutfs_btree_item { + __le16 key_len; + __le16 val_len; __u8 data[0]; } __packed; -#define SCOUTFS_RING_ENTRY_FLAG_DELETION (1 << 0) - -struct scoutfs_ring_block { - __le32 crc; - __le32 pad; +struct scoutfs_btree_block { __le64 fsid; + __le64 blkno; __le64 seq; - __le64 block; - __le32 nr_entries; - struct scoutfs_ring_entry entries[0]; + __le32 crc; + __le32 _pad; + __le16 free_end; + __le16 free_reclaim; + __le16 nr_items; + __le16 bit_counts[SCOUTFS_BTREE_BITS]; + __u8 level; + struct scoutfs_btree_item_header item_hdrs[0]; } __packed; -struct scoutfs_ring_descriptor { - __le64 blkno; - __le64 total_blocks; - __le64 first_block; - __le64 first_seq; +struct scoutfs_btree_ring { + __le64 first_blkno; __le64 nr_blocks; + __le64 next_block; + __le64 next_seq; } __packed; /* @@ -85,16 +152,39 @@ struct scoutfs_ring_descriptor { #define SCOUTFS_MANIFEST_FANOUT 10 struct scoutfs_manifest { - struct scoutfs_ring_descriptor ring; + struct scoutfs_btree_root root; __le64 level_counts[SCOUTFS_MANIFEST_MAX_LEVEL]; } __packed; -struct scoutfs_manifest_entry { +/* + * Manifest entries are packed into btree keys and values in a very + * fiddly way so that we can sort them with memcmp first by level then + * by their position in the level. First comes the level. + * + * Level 0 segments are sorted by their seq so they don't have the first + * segment key in the manifest btree key. Both of their keys are in the + * value. + * + * Level 1 segments are sorted by their key before their seq so the + * btree header has the key and the seq is in the footer. Only their + * last key is in the value. + * + * We go to all this trouble so that we can communicate a version of the + * manifest with one btree root, have dense btree keys which are used as + * seperators in parent blocks, and don't duplicate the large keys in + * the manifest btree key and value. + */ + +struct scoutfs_manifest_btree_key { + __u8 level; + __u8 bkey[0]; +} __packed; + +struct scoutfs_manifest_btree_val { __le64 segno; __le64 seq; __le16 first_key_len; __le16 last_key_len; - __u8 level; __u8 keys[0]; } __packed; @@ -102,12 +192,12 @@ struct scoutfs_manifest_entry { #define SCOUTFS_ALLOC_REGION_BITS (1 << SCOUTFS_ALLOC_REGION_SHIFT) #define SCOUTFS_ALLOC_REGION_MASK (SCOUTFS_ALLOC_REGION_BITS - 1) -/* - * The bits need to be aligned so that the host can use native long - * bitops on the bits in memory. - */ -struct scoutfs_alloc_region { - __le64 index; +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]; } __packed; @@ -270,8 +360,6 @@ struct scoutfs_symlink_key { __u8 nr; } __packed; -#define SCOUTFS_SYMLINK_MAX_VAL_SIZE 200 - struct scoutfs_betimespec { __be64 sec; __be32 nsec; @@ -289,12 +377,14 @@ struct scoutfs_inode_index_key { #define SCOUTFS_UUID_BYTES 16 +/* XXX ipv6 */ +struct scoutfs_inet_addr { + __le32 addr; + __le16 port; +} __packed; + +#define SCOUTFS_DEFAULT_PORT 12345 -/* - * The ring fields describe the statically allocated ring log. The - * head and tail indexes are logical 4k blocks offsets inside the ring. - * The head block should contain the seq. - */ struct scoutfs_super_block { struct scoutfs_block_header hdr; __le64 id; @@ -304,13 +394,11 @@ struct scoutfs_super_block { __le64 alloc_uninit; __le64 total_segs; __le64 free_segs; - __le64 ring_blkno; - __le64 ring_blocks; - __le64 ring_tail_block; - __le64 ring_gen; + struct scoutfs_btree_ring bring; __le64 next_seg_seq; - struct scoutfs_ring_descriptor alloc_ring; + struct scoutfs_btree_root alloc_root; struct scoutfs_manifest manifest; + struct scoutfs_inet_addr server_addr; } __packed; #define SCOUTFS_ROOT_INO 1 @@ -379,13 +467,6 @@ struct scoutfs_dirent { /* S32_MAX avoids the (int) sign bit and might avoid sloppy bugs */ #define SCOUTFS_LINK_MAX S32_MAX -#define SCOUTFS_XATTR_MAX_NAME_LEN 255 -#define SCOUTFS_XATTR_MAX_SIZE 65536 -#define SCOUTFS_XATTR_PART_SIZE \ - (SCOUTFS_BLOCK_SIZE - sizeof(struct scoutfs_xattr_val_header)) -#define SCOUTFS_XATTR_MAX_PARTS \ - DIV_ROUND_UP(SCOUTFS_XATTR_MAX_SIZE, SCOUTFS_XATTR_PART_SIZE) - /* entries begin after . and .. */ #define SCOUTFS_DIRENT_FIRST_POS 2 /* getdents returns next pos with an entry, no entry at (f_pos)~0 */ @@ -410,16 +491,18 @@ enum { #define SCOUTFS_MAX_VAL_SIZE \ offsetof(struct scoutfs_dirent, name[SCOUTFS_NAME_LEN]) +#define SCOUTFS_XATTR_MAX_NAME_LEN 255 +#define SCOUTFS_XATTR_MAX_SIZE 65536 +#define SCOUTFS_XATTR_PART_SIZE \ + (SCOUTFS_MAX_VAL_SIZE - sizeof(struct scoutfs_xattr_val_header)) +#define SCOUTFS_XATTR_MAX_PARTS \ + DIV_ROUND_UP(SCOUTFS_XATTR_MAX_SIZE, SCOUTFS_XATTR_PART_SIZE) + + /* * messages over the wire. */ -/* XXX ipv6 */ -struct scoutfs_inet_addr { - __le32 addr; - __le16 port; -} __packed; - /* * This header precedes and describes all network messages sent over * sockets. The id is set by the request and sent in the reply. The @@ -450,9 +533,13 @@ struct scoutfs_net_key_range { __u8 key_bytes[0]; } __packed; -struct scoutfs_net_manifest_entries { - __le16 nr; - struct scoutfs_manifest_entry ments[0]; +struct scoutfs_net_manifest_entry { + __le64 segno; + __le64 seq; + __le16 first_key_len; + __le16 last_key_len; + __u8 level; + __u8 keys[0]; } __packed; /* XXX I dunno, totally made up */ @@ -475,12 +562,12 @@ struct scoutfs_net_segnos { enum { SCOUTFS_NET_ALLOC_INODES = 0, - SCOUTFS_NET_MANIFEST_RANGE_ENTRIES, 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, SCOUTFS_NET_UNKNOWN, }; diff --git a/utils/src/mkfs.c b/utils/src/mkfs.c index 07723de2..1388009e 100644 --- a/utils/src/mkfs.c +++ b/utils/src/mkfs.c @@ -47,30 +47,106 @@ static int write_block(int fd, u64 blkno, struct scoutfs_super_block *super, } /* - * Figure out how many blocks a given ring will need given a max number - * of entries up to a given max size. We figure out how many blocks it - * could take to store these maximal entries given unused tail space and - * block header overheads. Then we (wastefully) multiply by three to - * ensure that the ring won't consume itself as it wraps. The caller - * aligns the ring size to a segment size depending on where it starts. + * Calculate the greatest number of btree blocks that might be needed to + * store the given item population. At most all blocks will be half + * full. All keys will be the max size including parent items which + * determines the fanout. + * + * We will never hit this in practice. But some joker *could* fill a + * filesystem with empty files with enormous file names. */ -static u64 calc_ring_blocks(u64 max_nr, u64 max_size) +static u64 calc_btree_blocks(u64 nr, u64 max_key, u64 max_val) { - u64 block_bytes; + u64 item_bytes; + u64 fanout; + u64 block_items; + u64 leaf_blocks; + u64 level_blocks; + u64 total_blocks; - max_size += sizeof(struct scoutfs_ring_entry); + /* figure out the parent fanout for these silly huge possible items */ + item_bytes = sizeof(struct scoutfs_btree_item_header) + + sizeof(struct scoutfs_btree_item) + + max_key + sizeof(struct scoutfs_btree_ref); + fanout = (SCOUTFS_BLOCK_SIZE - SCOUTFS_BTREE_FREE_LIMIT) / item_bytes; - block_bytes = SCOUTFS_BLOCK_SIZE - sizeof(struct scoutfs_ring_block) - - (max_size - 1); + /* figure out how many items we have to store */ + item_bytes = sizeof(struct scoutfs_btree_item_header) + + sizeof(struct scoutfs_btree_item) + + max_key + max_val; + block_items = (SCOUTFS_BLOCK_SIZE - SCOUTFS_BTREE_FREE_LIMIT) / item_bytes; + leaf_blocks = DIV_ROUND_UP(nr, block_items); - return DIV_ROUND_UP(max_nr * max_size, block_bytes) * 3; + /* then calc total blocks as we grow to have enough blocks for items */ + level_blocks = 1; + total_blocks = level_blocks; + while (level_blocks < leaf_blocks) { + level_blocks *= fanout; + level_blocks = min(leaf_blocks, level_blocks); + total_blocks += level_blocks; + } + + return total_blocks; } +/* + * 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. + */ +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)); + + blocks += calc_btree_blocks(total_segs, + sizeof(struct scoutfs_manifest_btree_key) + + SCOUTFS_MAX_KEY_SIZE, + sizeof(struct scoutfs_manifest_btree_val) + + SCOUTFS_MAX_KEY_SIZE); + + return round_up(blocks * 4, SCOUTFS_SEGMENT_BLOCKS); +} + +static float size_flt(u64 nr, unsigned size) +{ + float x = (float)nr * (float)size; + + while (x >= 1024) + x /= 1024; + + return x; +} + +static char *size_str(u64 nr, unsigned size) +{ + float x = (float)nr * (float)size; + static char *suffixes[] = { + "B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB", + }; + int i = 0; + + while (x >= 1024) { + x /= 1024; + i++; + } + + return suffixes[i]; +} + +#define SIZE_FMT "%llu (%.2f %s)" +#define SIZE_ARGS(nr, sz) (nr), size_flt(nr, sz), size_str(nr, sz) + /* * Make a new file system by writing: * - super blocks - * - ring block with manifest node - * - segment with root inode + * - btree ring blocks with manifest and allocator btree blocks + * - segment with root inode items */ static int write_new_fs(char *path, int fd) { @@ -79,10 +155,10 @@ static int write_new_fs(char *path, int fd) struct scoutfs_inode_index_key *idx_key; struct scoutfs_inode *inode; struct scoutfs_segment_block *sblk; - struct scoutfs_manifest_entry *ment; - struct scoutfs_ring_descriptor *rdesc; - struct scoutfs_ring_block *rblk; - struct scoutfs_ring_entry *rent; + struct scoutfs_manifest_btree_key *mkey; + struct scoutfs_manifest_btree_val *mval; + struct scoutfs_btree_block *bt; + struct scoutfs_btree_item *btitem; struct scoutfs_segment_item *item; __le32 *prev_link; struct timeval tv; @@ -99,9 +175,9 @@ static int write_new_fs(char *path, int fd) gettimeofday(&tv, NULL); super = calloc(1, SCOUTFS_BLOCK_SIZE); - rblk = calloc(1, SCOUTFS_BLOCK_SIZE); + bt = calloc(1, SCOUTFS_BLOCK_SIZE); sblk = calloc(1, SCOUTFS_SEGMENT_SIZE); - if (!super || !rblk || !sblk) { + if (!super || !bt || !sblk) { ret = -errno; fprintf(stderr, "failed to allocate block mem: %s (%d)\n", strerror(errno), errno); @@ -136,73 +212,66 @@ static int write_new_fs(char *path, int fd) super->total_segs = cpu_to_le64(total_segs); super->next_seg_seq = cpu_to_le64(2); - /* start writing rings after the super */ - blkno = SCOUTFS_SUPER_BLKNO + SCOUTFS_SUPER_NR; - - /* allocator ring is empty, allocations start from super fields */ - ring_blocks = calc_ring_blocks(DIV_ROUND_UP(total_segs, - SCOUTFS_ALLOC_REGION_BITS), - sizeof(struct scoutfs_alloc_region)); - ring_blocks = round_up(blkno + ring_blocks, SCOUTFS_SEGMENT_BLOCKS) - - blkno; - - rdesc = &super->alloc_ring; - rdesc->blkno = cpu_to_le64(blkno); - rdesc->total_blocks = cpu_to_le64(ring_blocks); - rdesc->first_block = cpu_to_le64(0); - rdesc->first_seq = cpu_to_le64(0); - rdesc->nr_blocks = cpu_to_le64(0); - - blkno += ring_blocks; - - /* manifest ring has a block with an entry for the segment */ - ring_blocks = calc_ring_blocks(total_segs, - sizeof(struct scoutfs_manifest_entry) + - (2 * SCOUTFS_MAX_KEY_SIZE)); - ring_blocks = round_up(ring_blocks, SCOUTFS_SEGMENT_BLOCKS); - + /* align the btree ring to the segment after the supers */ + blkno = round_up(SCOUTFS_SUPER_BLKNO + SCOUTFS_SUPER_NR, + SCOUTFS_SEGMENT_BLOCKS); /* first usable segno follows manifest ring */ + ring_blocks = calc_btree_ring_blocks(total_segs); first_segno = (blkno + ring_blocks) / SCOUTFS_SEGMENT_BLOCKS; + 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_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; + + /* manifest btree has a block with an item for the segment */ + super->manifest.root.ref.blkno = cpu_to_le64(blkno); + super->manifest.root.ref.seq = cpu_to_le64(1); + super->manifest.root.height = 1; super->manifest.level_counts[1] = cpu_to_le64(1); - rdesc = &super->manifest.ring; - rdesc->blkno = cpu_to_le64(blkno); - rdesc->total_blocks = cpu_to_le64(ring_blocks); - rdesc->first_seq = cpu_to_le64(1); - rdesc->nr_blocks = cpu_to_le64(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(1); - memset(rblk, 0, SCOUTFS_BLOCK_SIZE); - rblk->pad = 0; - rblk->fsid = super->hdr.fsid; - rblk->seq = cpu_to_le64(1); - rblk->block = 0; - rblk->nr_entries = cpu_to_le32(1); + /* btree item allocated from the back of the block */ + idx_key = (void *)bt + SCOUTFS_BLOCK_SIZE - sizeof(*idx_key); + mval = (void *)idx_key - sizeof(*mval); + ikey = (void *)mval - sizeof(*ikey); + mkey = (void *)ikey - sizeof(*mkey); + btitem = (void *)mkey - sizeof(*btitem); - rent = rblk->entries; - rent->flags = 0; - rent->data_len = cpu_to_le16(sizeof(struct scoutfs_manifest_entry) + - sizeof(struct scoutfs_inode_key) + - sizeof(struct scoutfs_inode_index_key)); + bt->item_hdrs[0].off = cpu_to_le16((long)btitem - (long)bt); + bt->free_end = bt->item_hdrs[0].off; - ment = (void *)rent->data; - ment->segno = cpu_to_le64(first_segno); - ment->seq = cpu_to_le64(1); - ment->first_key_len = cpu_to_le16(sizeof(struct scoutfs_inode_key)); - ment->last_key_len = cpu_to_le16(sizeof(struct scoutfs_inode_index_key)); - ment->level = 1; - ikey = (void *)ment->keys; + btitem->key_len = cpu_to_le16(sizeof(struct scoutfs_manifest_btree_key) + + sizeof(struct scoutfs_inode_key)); + btitem->val_len = cpu_to_le16(sizeof(struct scoutfs_manifest_btree_val) + + sizeof(struct scoutfs_inode_index_key)); + + mkey->level = 1; ikey->type = SCOUTFS_INODE_KEY; ikey->ino = cpu_to_be64(SCOUTFS_ROOT_INO); - idx_key = (void *)(ikey + 1); + + mval->segno = cpu_to_le64(first_segno); + mval->seq = cpu_to_le64(1); + mval->first_key_len = cpu_to_le16(sizeof(struct scoutfs_inode_key)); + mval->last_key_len = cpu_to_le16(sizeof(struct scoutfs_inode_index_key)); idx_key->type = SCOUTFS_INODE_INDEX_META_SEQ_KEY; idx_key->major = cpu_to_be64(0); idx_key->minor = 0; idx_key->ino = cpu_to_be64(SCOUTFS_ROOT_INO); - rblk->crc = cpu_to_le32(crc_ring_block(rblk)); + bt->crc = cpu_to_le32(crc_btree_block(bt)); - ret = write_raw_block(fd, blkno, rblk); + ret = write_raw_block(fd, blkno, bt); if (ret) goto out; blkno += ring_blocks; @@ -304,17 +373,27 @@ static int write_new_fs(char *path, int fd) uuid_unparse(super->uuid, uuid_str); printf("Created scoutfs filesystem:\n" - " fsid: %llx\n" - " uuid: %s\n", + " device path: %s\n" + " fsid: %llx\n" + " uuid: %s\n" + " device bytes: "SIZE_FMT"\n" + " btree ring blocks: "SIZE_FMT"\n" + " usable segments: "SIZE_FMT"\n", + path, le64_to_cpu(super->hdr.fsid), - uuid_str); + uuid_str, + SIZE_ARGS(size, 1), + SIZE_ARGS(le64_to_cpu(super->bring.nr_blocks), + SCOUTFS_BLOCK_SIZE), + SIZE_ARGS(le64_to_cpu(super->free_segs) + 1, + SCOUTFS_SEGMENT_SIZE)); ret = 0; out: if (super) free(super); - if (rblk) - free(rblk); + if (bt) + free(bt); if (sblk) free(sblk); return ret; diff --git a/utils/src/print.c b/utils/src/print.c index 2e2185af..6462e161 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -338,116 +338,189 @@ static int print_segments(int fd, unsigned long *seg_map, u64 total) return 0; } -static void print_ring_descriptor(struct scoutfs_ring_descriptor *rdesc, - char *which) +static int print_manifest_entry(void *key, unsigned key_len, void *val, + unsigned val_len, void *arg) { - printf(" %s ring:\n blkno %llu total_blocks %llu first_block %llu " - "first_seq %llu nr_blocks %llu\n", - which, le64_to_cpu(rdesc->blkno), - le64_to_cpu(rdesc->total_blocks), - le64_to_cpu(rdesc->first_block), - le64_to_cpu(rdesc->first_seq), - le64_to_cpu(rdesc->nr_blocks)); -} - -static int print_manifest_entry(int fd, struct scoutfs_ring_entry *rent, - void *arg) -{ - struct scoutfs_manifest_entry *ment = (void *)rent->data; + struct scoutfs_manifest_btree_key *mkey = key; + struct scoutfs_manifest_btree_val *mval = val; unsigned long *seg_map = arg; + unsigned first_len; + unsigned last_len; + void *first; + void *last; + __be64 seq; - printf(" segno %llu seq %llu first_len %u last_len %u level %u\n", - le64_to_cpu(ment->segno), - le64_to_cpu(ment->seq), - le16_to_cpu(ment->first_key_len), - le16_to_cpu(ment->last_key_len), - ment->level); - printf(" first: "); - print_key(ment->keys, le16_to_cpu(ment->first_key_len)); - printf("\n last: "); - print_key(ment->keys + le16_to_cpu(ment->first_key_len), - le16_to_cpu(ment->last_key_len)); - printf("\n"); - - if (rent->flags & SCOUTFS_RING_ENTRY_FLAG_DELETION) - clear_bit(seg_map, le64_to_cpu(ment->segno)); - else - set_bit(seg_map, le64_to_cpu(ment->segno)); - - return 0; -} - -static int print_alloc_region(int fd, struct scoutfs_ring_entry *rent, - void *arg) -{ - struct scoutfs_alloc_region *reg = (void *)rent->data; - int i; - - printf(" index %llu bits", le64_to_cpu(reg->index)); - for (i = 0; i < array_size(reg->bits); i++) - printf(" %016llx", le64_to_cpu(reg->bits[i])); - printf("\n"); - - return 0; -} - -typedef int (*rent_func)(int fd, struct scoutfs_ring_entry *rent, void *arg); - -static int print_ring(int fd, struct scoutfs_super_block *super, - char *which, struct scoutfs_ring_descriptor *rdesc, - rent_func func, void *arg) -{ - struct scoutfs_ring_block *rblk; - struct scoutfs_ring_entry *rent; - u64 block; - u64 blkno; - int ret; - u64 i; - u32 e; - - block = le64_to_cpu(rdesc->first_block); - for (i = 0; i < le64_to_cpu(rdesc->nr_blocks); i++) { - blkno = le64_to_cpu(rdesc->blkno) + block; - - rblk = read_block(fd, blkno); - if (!rblk) - return -ENOMEM; - - printf("%s ring blkno %llu\n" - " crc %08x fsid %llx seq %llu block %llu " - "nr_entries %u\n", - which, blkno, le32_to_cpu(rblk->crc), - le64_to_cpu(rblk->fsid), - le64_to_cpu(rblk->seq), - le64_to_cpu(rblk->block), - le32_to_cpu(rblk->nr_entries)); - - rent = rblk->entries; - for (e = 0; e < le32_to_cpu(rblk->nr_entries); e++) { - - printf(" entry [%u] off %lu data_len %u flags %x\n", - e, (char *)rent - (char *)rblk->entries, - le16_to_cpu(rent->data_len), rent->flags); - - ret = func(fd, rent, arg); - if (ret) { - free(rblk); - return ret; - } - - rent = (void *)&rent->data[le16_to_cpu(rent->data_len)]; + /* parent items only have the key */ + if (val == NULL) { + if (mkey->level == 0) { + memcpy(&seq, mkey->bkey, sizeof(seq)); + printf(" level %u seq %llu\n", + mkey->level, be64_to_cpu(seq)); + } else { + printf(" level %u first ", mkey->level); + print_key(mkey->bkey, key_len - sizeof(mkey->level)); + printf("\n"); } - - block++; - if (block == le64_to_cpu(rdesc->total_blocks)) - block = 0; - - free(rblk); + return 0; } + /* leaf items print the whole entry */ + first_len = le16_to_cpu(mval->first_key_len); + last_len = le16_to_cpu(mval->last_key_len); + + if (mkey->level == 0) { + first = mval->keys; + last = mval->keys + first_len; + } else { + first = mkey->bkey; + last = mval->keys; + } + + printf(" level %u segno %llu seq %llu first_len %u last_len %u\n", + mkey->level, le64_to_cpu(mval->segno), le64_to_cpu(mval->seq), + first_len, last_len); + + printf(" first "); + print_key(first, first_len); + printf("\n last "); + print_key(last, last_len); + printf("\n"); + + set_bit(seg_map, le64_to_cpu(mval->segno)); + return 0; } +static int print_alloc_region(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; + + /* XXX check sizes */ + + printf(" index %llu bits", be64_to_cpu(reg_key->index)); + + 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"); + + return 0; +} + +typedef int (*print_item_func)(void *key, unsigned key_len, void *val, + unsigned val_len, void *arg); + +static int print_btree_ref(void *key, unsigned key_len, void *val, + unsigned val_len, print_item_func func, void *arg) +{ + struct scoutfs_btree_ref *ref = val; + + func(key, key_len, NULL, 0, arg); + printf(" ref blkno %llu seq %llu\n", + le64_to_cpu(ref->blkno), le64_to_cpu(ref->seq)); + + return 0; +} + +static int print_btree_block(int fd, struct scoutfs_super_block *super, + char *which, struct scoutfs_btree_ref *ref, + print_item_func func, void *arg, u8 level) +{ + struct scoutfs_btree_item *item; + struct scoutfs_btree_block *bt; + unsigned key_len; + unsigned val_len; + void *key; + void *val; + int ret; + int i; + + bt = read_block(fd, le64_to_cpu(ref->blkno)); + if (!bt) + return -ENOMEM; + + if (bt->level == level) { + printf("%s btree blkno %llu\n" + " fsid %llx blkno %llu seq %llu crc %08x \n" + " level %u free_end %u free_reclaim %u nr_items %u\n" + " bit_counts:", + which, le64_to_cpu(ref->blkno), + le64_to_cpu(bt->fsid), + le64_to_cpu(bt->blkno), + le64_to_cpu(bt->seq), + le32_to_cpu(bt->crc), + bt->level, + le16_to_cpu(bt->free_end), + le16_to_cpu(bt->free_reclaim), + le16_to_cpu(bt->nr_items)); + for (i = 0; i < array_size(bt->bit_counts); i++) { + if (bt->bit_counts[i]) + printf(" %u:%u", + i, le16_to_cpu(bt->bit_counts[i])); + } + printf("\n"); + } + + for (i = 0; i < le16_to_cpu(bt->nr_items); i++) { + item = (void *)bt + le16_to_cpu(bt->item_hdrs[i].off); + key_len = le16_to_cpu(item->key_len); + val_len = le16_to_cpu(item->val_len); + key = (void *)(item + 1); + val = (void *)key + key_len; + + if (level < bt->level) { + ref = val; + /* XXX check len */ + if (ref->blkno) { + ret = print_btree_block(fd, super, which, ref, + func, arg, level); + if (ret) + break; + } + continue; + } + + printf(" item [%u] off %u bits %02x key_len %u val_len %u\n", + i, le16_to_cpu(bt->item_hdrs[i].off), + bt->item_hdrs[i].bits, key_len, val_len); + + if (level) + print_btree_ref(key, key_len, val, val_len, func, arg); + else + func(key, key_len, val, val_len, arg); + } + + free(bt); + return 0; +} + +/* + * We print btrees by a breadth-first search. This way all the parent + * blocks are printed before the factor of fanout more numerous leaf + * blocks and their included items. + */ +static int print_btree(int fd, struct scoutfs_super_block *super, char *which, + struct scoutfs_btree_root *root, + print_item_func func, void *arg) +{ + int ret = 0; + int i; + + for (i = root->height - 1; i >= 0; i--) { + ret = print_btree_block(fd, super, which, &root->ref, + func, arg, i); + if (ret) + break; + } + + return ret; +} + static void print_super_block(struct scoutfs_super_block *super, u64 blkno) { char uuid_str[37]; @@ -460,24 +533,30 @@ static void print_super_block(struct scoutfs_super_block *super, u64 blkno) print_block_header(&super->hdr); printf(" id %llx uuid %s\n", le64_to_cpu(super->id), uuid_str); + /* XXX these are all in a crazy order */ - printf(" next_ino %llu next_seq %llu\n" - " ring_blkno %llu ring_blocks %llu ring_tail_block %llu\n" - " ring_gen %llu alloc_uninit %llu total_segs %llu\n" - " next_seg_seq %llu free_segs %llu\n", + printf(" next_ino %llu next_seq %llu next_seg_seq %llu\n" + " alloc_uninit %llu total_segs %llu free_segs %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\n" + " manifest btree root: height %u blkno %llu seq %llu\n", le64_to_cpu(super->next_ino), le64_to_cpu(super->next_seq), - le64_to_cpu(super->ring_blkno), - le64_to_cpu(super->ring_blocks), - le64_to_cpu(super->ring_tail_block), - le64_to_cpu(super->ring_gen), + le64_to_cpu(super->next_seg_seq), le64_to_cpu(super->alloc_uninit), le64_to_cpu(super->total_segs), - le64_to_cpu(super->next_seg_seq), - le64_to_cpu(super->free_segs)); - - print_ring_descriptor(&super->alloc_ring, "alloc"); - print_ring_descriptor(&super->manifest.ring, "manifest"); + le64_to_cpu(super->free_segs), + le64_to_cpu(super->bring.first_blkno), + le64_to_cpu(super->bring.nr_blocks), + le64_to_cpu(super->bring.next_block), + le64_to_cpu(super->bring.next_seq), + super->alloc_root.height, + le64_to_cpu(super->alloc_root.ref.blkno), + le64_to_cpu(super->alloc_root.ref.seq), + super->manifest.root.height, + le64_to_cpu(super->manifest.root.ref.blkno), + le64_to_cpu(super->manifest.root.ref.seq)); printf(" level_counts:"); counts = super->manifest.level_counts; @@ -524,11 +603,11 @@ static int print_super_blocks(int fd) return ret; } - ret = print_ring(fd, super, "alloc", &super->alloc_ring, - print_alloc_region, NULL); + ret = print_btree(fd, super, "alloc", &super->alloc_root, + print_alloc_region, NULL); - err = print_ring(fd, super, "manifest", &super->manifest.ring, - print_manifest_entry, seg_map); + err = print_btree(fd, super, "manifest", &super->manifest.root, + print_manifest_entry, seg_map); if (err && !ret) ret = err; From 7bbe49fde2afc21120e87ee29a47096dff9d6f32 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Thu, 13 Jul 2017 14:53:07 -0700 Subject: [PATCH 102/235] scoutfs-utils: sort keys by zones, then types Our item cache protocol is tied to holding DLM locks which cover a region of the item namespace. We want locks to cover all the data associated with an inode and other locks to cover the indexes. So we resort the items first by major (index, fs) then by inode type (inode, dirent, etc). Signed-off-by: Zach Brown --- utils/src/format.h | 125 +++++++++++++++++++++---------------- utils/src/key.c | 152 +++++++++++++++++++++++++++------------------ utils/src/mkfs.c | 108 ++++++++++++++++---------------- utils/src/print.c | 84 ++++++++++++++++++------- 4 files changed, 280 insertions(+), 189 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index b1bbdbbd..6251c8f8 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -50,12 +50,6 @@ struct scoutfs_block_header { __le64 blkno; } __packed; -/* - * The largest possible btree has 2^64 bytes worth of segments with - * the largest possible keys in a pathologically sparse btree where - * all the nodes are half full. - */ - /* * Assert that we'll be able to represent all possible keys with 8 64bit * primary sort values. @@ -94,9 +88,6 @@ enum { SCOUTFS_BTREE_BIT_HALF2 = (1 << 1), }; -#define SCOUTFS_BTREE_HALF_BITS \ - (SCOUTFS_BTREE_BIT_HALF1 | SCOUTFS_BTREE_BIT_HALF2) - struct scoutfs_btree_ref { __le64 blkno; __le64 seq; @@ -165,9 +156,8 @@ struct scoutfs_manifest { * segment key in the manifest btree key. Both of their keys are in the * value. * - * Level 1 segments are sorted by their key before their seq so the - * btree header has the key and the seq is in the footer. Only their - * last key is in the value. + * Level 1 segments are sorted by their first key so their last key is + * in the value. * * We go to all this trouble so that we can communicate a version of the * manifest with one btree root, have dense btree keys which are used as @@ -246,73 +236,78 @@ struct scoutfs_segment_block { } __packed; /* - * Currently we sort keys by the numeric value of the types, but that - * isn't necessary. We could have an arbitrary sort order. So we don't - * have to stress about cleverly allocating the types. + * Keys are first sorted by major key zones. */ -#define SCOUTFS_INODE_KEY 1 -#define SCOUTFS_XATTR_KEY 3 -#define SCOUTFS_DIRENT_KEY 5 -#define SCOUTFS_READDIR_KEY 6 -#define SCOUTFS_LINK_BACKREF_KEY 7 -#define SCOUTFS_SYMLINK_KEY 8 -#define SCOUTFS_FILE_EXTENT_KEY 9 -#define SCOUTFS_ORPHAN_KEY 10 -#define SCOUTFS_FREE_EXTENT_BLKNO_KEY 11 -#define SCOUTFS_FREE_EXTENT_BLOCKS_KEY 12 -#define SCOUTFS_INODE_INDEX_CTIME_KEY 13 /* don't forget first and last */ -#define SCOUTFS_INODE_INDEX_MTIME_KEY 14 -#define SCOUTFS_INODE_INDEX_SIZE_KEY 15 -#define SCOUTFS_INODE_INDEX_META_SEQ_KEY 16 -#define SCOUTFS_INODE_INDEX_DATA_SEQ_KEY 17 -/* not found in the fs */ -#define SCOUTFS_MAX_UNUSED_KEY 253 -#define SCOUTFS_NET_ADDR_KEY 254 -#define SCOUTFS_NET_LISTEN_KEY 255 +#define SCOUTFS_INODE_INDEX_ZONE 1 +#define SCOUTFS_NODE_ZONE 2 +#define SCOUTFS_FS_ZONE 3 + +/* inode index zone */ +#define SCOUTFS_INODE_INDEX_CTIME_TYPE 1 +#define SCOUTFS_INODE_INDEX_MTIME_TYPE 2 +#define SCOUTFS_INODE_INDEX_SIZE_TYPE 3 +#define SCOUTFS_INODE_INDEX_META_SEQ_TYPE 4 +#define SCOUTFS_INODE_INDEX_DATA_SEQ_TYPE 5 -#define SCOUTFS_INODE_INDEX_FIRST SCOUTFS_INODE_INDEX_CTIME_KEY -#define SCOUTFS_INODE_INDEX_LAST SCOUTFS_INODE_INDEX_DATA_SEQ_KEY #define SCOUTFS_INODE_INDEX_NR \ - (SCOUTFS_INODE_INDEX_LAST - SCOUTFS_INODE_INDEX_FIRST + 1) + (SCOUTFS_INODE_INDEX_DATA_SEQ_TYPE - SCOUTFS_INODE_INDEX_CTIME_TYPE + 1) + +/* node zone */ +#define SCOUTFS_FREE_EXTENT_BLKNO_TYPE 11 +#define SCOUTFS_FREE_EXTENT_BLOCKS_TYPE 12 + +/* fs zone */ +#define SCOUTFS_INODE_TYPE 1 +#define SCOUTFS_XATTR_TYPE 2 +#define SCOUTFS_DIRENT_TYPE 3 +#define SCOUTFS_READDIR_TYPE 4 +#define SCOUTFS_LINK_BACKREF_TYPE 5 +#define SCOUTFS_SYMLINK_TYPE 6 +#define SCOUTFS_FILE_EXTENT_TYPE 7 +#define SCOUTFS_ORPHAN_TYPE 8 + +/* XXX don't need these now that we have dlm lock spaces and resources */ +#define SCOUTFS_NET_ADDR_TYPE 254 +#define SCOUTFS_NET_LISTEN_TYPE 255 /* value is struct scoutfs_inode */ struct scoutfs_inode_key { - __u8 type; + __u8 zone; __be64 ino; + __u8 type; } __packed; /* value is struct scoutfs_dirent without the name */ struct scoutfs_dirent_key { - __u8 type; + __u8 zone; __be64 ino; + __u8 type; __u8 name[0]; } __packed; /* value is struct scoutfs_dirent with the name */ struct scoutfs_readdir_key { - __u8 type; + __u8 zone; __be64 ino; + __u8 type; __be64 pos; } __packed; /* value is empty */ struct scoutfs_link_backref_key { - __u8 type; + __u8 zone; __be64 ino; + __u8 type; __be64 dir_ino; __u8 name[0]; } __packed; -/* no value */ -struct scoutfs_orphan_key { - __u8 type; - __be64 ino; -} __packed; /* no value */ struct scoutfs_file_extent_key { - __u8 type; + __u8 zone; __be64 ino; + __u8 type; __be64 last_blk_off; __be64 last_blkno; __be64 blocks; @@ -323,23 +318,34 @@ struct scoutfs_file_extent_key { /* no value */ struct scoutfs_free_extent_blkno_key { - __u8 type; + __u8 zone; __be64 node_id; + __u8 type; __be64 last_blkno; __be64 blocks; } __packed; struct scoutfs_free_extent_blocks_key { - __u8 type; + __u8 zone; __be64 node_id; + __u8 type; __be64 blocks; __be64 last_blkno; } __packed; -/* value is each item's part of the full xattr value for the off/len */ -struct scoutfs_xattr_key { +/* no value */ +struct scoutfs_orphan_key { + __u8 zone; + __be64 node_id; __u8 type; __be64 ino; +} __packed; + +/* value is each item's part of the full xattr value for the off/len */ +struct scoutfs_xattr_key { + __u8 zone; + __be64 ino; + __u8 type; __u8 name[0]; } __packed; @@ -355,8 +361,9 @@ struct scoutfs_xattr_val_header { /* size determines nr needed to store full target path in their values */ struct scoutfs_symlink_key { - __u8 type; + __u8 zone; __be64 ino; + __u8 type; __u8 nr; } __packed; @@ -366,6 +373,7 @@ struct scoutfs_betimespec { } __packed; struct scoutfs_inode_index_key { + __u8 zone; __u8 type; __be64 major; __be32 minor; @@ -498,6 +506,19 @@ enum { #define SCOUTFS_XATTR_MAX_PARTS \ DIV_ROUND_UP(SCOUTFS_XATTR_MAX_SIZE, SCOUTFS_XATTR_PART_SIZE) +/* + * structures used by dlm + */ +struct scoutfs_lock_name { + __u8 zone; + __u8 type; + __le64 first; + __le64 second; +} __packed; + +#define SCOUTFS_LOCK_INODE_GROUP_NR 1024 +#define SCOUTFS_LOCK_INODE_GROUP_MASK (SCOUTFS_LOCK_INODE_GROUP_NR - 1) +#define SCOUTFS_LOCK_INODE_GROUP_OFFSET (~0ULL) /* * messages over the wire. diff --git a/utils/src/key.c b/utils/src/key.c index 1487f9e3..46bc1420 100644 --- a/utils/src/key.c +++ b/utils/src/key.c @@ -10,14 +10,16 @@ /* * This is mechanically derived from scoutfs_key_str() in the kernel: - * - s/key->data/key_data/ - * - s/key->key_len/key_len/ - * - s/return snprintf_null(buf, size, /return printf(/ + * - :.,$s/key->data/key_data/g + * - :.,$s/key->key_len/key_len/g + * - :.,$s/return snprintf_null(buf, size, /return printf(/g */ int print_key(void *key_data, unsigned key_len) { + struct scoutfs_inode_key *ikey; + u8 zone = 0; + u8 type = 0; int len; - u8 type; if (key_data == NULL) return printf("[NULL]"); @@ -25,21 +27,88 @@ int print_key(void *key_data, unsigned key_len) if (key_len == 0) return printf("[0 len]"); - type = *(u8 *)key_data; + zone = *(u8 *)key_data; - switch(type) { + /* handle smaller and unknown zones, fall through to fs types */ + switch(zone) { + case SCOUTFS_INODE_INDEX_ZONE: { + struct scoutfs_inode_index_key *ikey = key_data; + static char *type_strings[] = { + [SCOUTFS_INODE_INDEX_CTIME_TYPE] = "ctm", + [SCOUTFS_INODE_INDEX_MTIME_TYPE] = "mtm", + [SCOUTFS_INODE_INDEX_SIZE_TYPE] = "siz", + [SCOUTFS_INODE_INDEX_META_SEQ_TYPE] = "msq", + [SCOUTFS_INODE_INDEX_DATA_SEQ_TYPE] = "dsq", + }; - case SCOUTFS_INODE_KEY: { + if (key_len < sizeof(struct scoutfs_inode_index_key)) + break; + + if (type_strings[ikey->type]) + return printf("iin.%s.%llu.%u.%llu", + type_strings[ikey->type], + be64_to_cpu(ikey->major), + be32_to_cpu(ikey->minor), + be64_to_cpu(ikey->ino)); + else + return printf("[iin type %u?]", + ikey->type); + } + + /* node zone keys start with zone, node, type */ + case SCOUTFS_NODE_ZONE: { + struct scoutfs_free_extent_blkno_key *fkey = key_data; + + static char *type_strings[] = { + [SCOUTFS_FREE_EXTENT_BLKNO_TYPE] = "fno", + [SCOUTFS_FREE_EXTENT_BLOCKS_TYPE] = "fks", + }; + + switch(fkey->type) { + case SCOUTFS_ORPHAN_TYPE: { + struct scoutfs_orphan_key *okey = key_data; + + if (key_len < sizeof(struct scoutfs_orphan_key)) + break; + return printf("nod.%llu.orp.%llu", + be64_to_cpu(okey->node_id), + be64_to_cpu(okey->ino)); + } + + case SCOUTFS_FREE_EXTENT_BLKNO_TYPE: + case SCOUTFS_FREE_EXTENT_BLOCKS_TYPE: + return printf("nod.%llu.%s.%llu.%llu", + be64_to_cpu(fkey->node_id), + type_strings[fkey->type], + be64_to_cpu(fkey->last_blkno), + be64_to_cpu(fkey->blocks)); + default: + return printf("[nod type %u?]", + fkey->type); + } + } + + case SCOUTFS_FS_ZONE: + break; + + default: + return printf("[zone %u?]", zone); + } + + /* everything in the fs tree starts with zone, ino, type */ + ikey = key_data; + switch(ikey->type) { + case SCOUTFS_INODE_TYPE: { struct scoutfs_inode_key *ikey = key_data; if (key_len < sizeof(struct scoutfs_inode_key)) break; - return printf("ino.%llu", + return printf("fs.%llu.ino", be64_to_cpu(ikey->ino)); } - case SCOUTFS_XATTR_KEY: { + case SCOUTFS_XATTR_TYPE: { struct scoutfs_xattr_key *xkey = key_data; len = (int)key_len - offsetof(struct scoutfs_xattr_key, @@ -47,53 +116,53 @@ int print_key(void *key_data, unsigned key_len) if (len <= 0) break; - return printf("xat.%llu.%.*s", + return printf("fs.%llu.xat.%.*s", be64_to_cpu(xkey->ino), len, xkey->name); } - case SCOUTFS_DIRENT_KEY: { + case SCOUTFS_DIRENT_TYPE: { struct scoutfs_dirent_key *dkey = key_data; len = (int)key_len - sizeof(struct scoutfs_dirent_key); if (len <= 0) break; - return printf("dnt.%llu.%.*s", + return printf("fs.%llu.dnt.%.*s", be64_to_cpu(dkey->ino), len, dkey->name); } - case SCOUTFS_READDIR_KEY: { + case SCOUTFS_READDIR_TYPE: { struct scoutfs_readdir_key *rkey = key_data; - return printf("rdr.%llu.%llu", + return printf("fs.%llu.rdr.%llu", be64_to_cpu(rkey->ino), be64_to_cpu(rkey->pos)); } - case SCOUTFS_LINK_BACKREF_KEY: { + case SCOUTFS_LINK_BACKREF_TYPE: { struct scoutfs_link_backref_key *lkey = key_data; len = (int)key_len - sizeof(*lkey); if (len <= 0) break; - return printf("lbr.%llu.%llu.%.*s", + return printf("fs.%llu.lbr.%llu.%.*s", be64_to_cpu(lkey->ino), be64_to_cpu(lkey->dir_ino), len, lkey->name); } - case SCOUTFS_SYMLINK_KEY: { + case SCOUTFS_SYMLINK_TYPE: { struct scoutfs_symlink_key *skey = key_data; - return printf("sym.%llu", + return printf("fs.%llu.sym", be64_to_cpu(skey->ino)); } - case SCOUTFS_FILE_EXTENT_KEY: { + case SCOUTFS_FILE_EXTENT_TYPE: { struct scoutfs_file_extent_key *ekey = key_data; - return printf("ext.%llu.%llu.%llu.%llu.%x", + return printf("fs.%llu.ext.%llu.%llu.%llu.%x", be64_to_cpu(ekey->ino), be64_to_cpu(ekey->last_blk_off), be64_to_cpu(ekey->last_blkno), @@ -101,48 +170,11 @@ int print_key(void *key_data, unsigned key_len) ekey->flags); } - case SCOUTFS_ORPHAN_KEY: { - struct scoutfs_orphan_key *okey = key_data; - - return printf("orp.%llu", - be64_to_cpu(okey->ino)); - } - - case SCOUTFS_FREE_EXTENT_BLKNO_KEY: - case SCOUTFS_FREE_EXTENT_BLOCKS_KEY: { - struct scoutfs_free_extent_blkno_key *fkey = key_data; - - return printf("%s.%llu.%llu.%llu", - fkey->type == SCOUTFS_FREE_EXTENT_BLKNO_KEY ? "fel" : - "fes", - be64_to_cpu(fkey->node_id), - be64_to_cpu(fkey->last_blkno), - be64_to_cpu(fkey->blocks)); - } - - case SCOUTFS_INODE_INDEX_CTIME_KEY: - case SCOUTFS_INODE_INDEX_MTIME_KEY: - case SCOUTFS_INODE_INDEX_SIZE_KEY: - case SCOUTFS_INODE_INDEX_META_SEQ_KEY: - case SCOUTFS_INODE_INDEX_DATA_SEQ_KEY: { - struct scoutfs_inode_index_key *ikey = key_data; - - return printf("%s.%llu.%u.%llu", - ikey->type == SCOUTFS_INODE_INDEX_CTIME_KEY ? "ctm" : - ikey->type == SCOUTFS_INODE_INDEX_MTIME_KEY ? "mtm" : - ikey->type == SCOUTFS_INODE_INDEX_SIZE_KEY ? "siz" : - ikey->type == SCOUTFS_INODE_INDEX_META_SEQ_KEY ? "msq" : - ikey->type == SCOUTFS_INODE_INDEX_DATA_SEQ_KEY ? "dsq" : - "uii", be64_to_cpu(ikey->major), - be32_to_cpu(ikey->minor), - be64_to_cpu(ikey->ino)); - } - default: - return printf("[unknown type %u len %u]", - type, key_len); + return printf("[fs type %u?]", type); } - return printf("[truncated type %u len %u]", + return printf("[fs type %u trunc len %u]", type, key_len); + } diff --git a/utils/src/mkfs.c b/utils/src/mkfs.c index 1388009e..6182d199 100644 --- a/utils/src/mkfs.c +++ b/utils/src/mkfs.c @@ -242,32 +242,34 @@ static int write_new_fs(char *path, int fd) bt->nr_items = cpu_to_le16(1); /* btree item allocated from the back of the block */ - idx_key = (void *)bt + SCOUTFS_BLOCK_SIZE - sizeof(*idx_key); - mval = (void *)idx_key - sizeof(*mval); - ikey = (void *)mval - sizeof(*ikey); - mkey = (void *)ikey - sizeof(*mkey); + ikey = (void *)bt + SCOUTFS_BLOCK_SIZE - sizeof(*ikey); + mval = (void *)ikey - sizeof(*mval); + idx_key = (void *)mval - sizeof(*idx_key); + mkey = (void *)idx_key - sizeof(*mkey); btitem = (void *)mkey - 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(struct scoutfs_manifest_btree_key) + - sizeof(struct scoutfs_inode_key)); - btitem->val_len = cpu_to_le16(sizeof(struct scoutfs_manifest_btree_val) + sizeof(struct scoutfs_inode_index_key)); + btitem->val_len = cpu_to_le16(sizeof(struct scoutfs_manifest_btree_val) + + sizeof(struct scoutfs_inode_key)); mkey->level = 1; - ikey->type = SCOUTFS_INODE_KEY; - ikey->ino = cpu_to_be64(SCOUTFS_ROOT_INO); + idx_key->zone = SCOUTFS_INODE_INDEX_ZONE; + idx_key->type = SCOUTFS_INODE_INDEX_CTIME_TYPE; + idx_key->major = cpu_to_be64(tv.tv_sec); + idx_key->minor = cpu_to_be32(tv.tv_usec * 1000); + idx_key->ino = cpu_to_be64(SCOUTFS_ROOT_INO); mval->segno = cpu_to_le64(first_segno); mval->seq = cpu_to_le64(1); - mval->first_key_len = cpu_to_le16(sizeof(struct scoutfs_inode_key)); - mval->last_key_len = cpu_to_le16(sizeof(struct scoutfs_inode_index_key)); - idx_key->type = SCOUTFS_INODE_INDEX_META_SEQ_KEY; - idx_key->major = cpu_to_be64(0); - idx_key->minor = 0; - idx_key->ino = cpu_to_be64(SCOUTFS_ROOT_INO); + mval->first_key_len = cpu_to_le16(sizeof(struct scoutfs_inode_index_key)); + mval->last_key_len = cpu_to_le16(sizeof(struct scoutfs_inode_key)); + ikey->zone = SCOUTFS_FS_ZONE; + ikey->ino = cpu_to_be64(SCOUTFS_ROOT_INO); + ikey->type = SCOUTFS_INODE_TYPE; bt->crc = cpu_to_le32(crc_btree_block(bt)); @@ -287,6 +289,42 @@ static int write_new_fs(char *path, int fd) prev_link = &sblk->skip_links[0]; item = (void *)(sblk + 1); + *prev_link = cpu_to_le32((long)item -(long)sblk); + prev_link = &item->skip_links[0]; + + /* write the root inode index keys */ + for (i = SCOUTFS_INODE_INDEX_CTIME_TYPE; + i <= SCOUTFS_INODE_INDEX_META_SEQ_TYPE; i++) { + + item->key_len = cpu_to_le16(sizeof(*idx_key)); + item->val_len = 0; + item->nr_links = 1; + + idx_key = (void *)&item->skip_links[1]; + idx_key->zone = SCOUTFS_INODE_INDEX_ZONE; + idx_key->type = i; + idx_key->ino = cpu_to_be64(SCOUTFS_ROOT_INO); + + switch(i) { + case SCOUTFS_INODE_INDEX_CTIME_TYPE: + case SCOUTFS_INODE_INDEX_MTIME_TYPE: + idx_key->major = cpu_to_be64(tv.tv_sec); + idx_key->minor = cpu_to_be32(tv.tv_usec * 1000); + break; + default: + idx_key->major = cpu_to_be64(0); + idx_key->minor = 0; + break; + } + + + item = (void *)(idx_key + 1); + *prev_link = cpu_to_le32((long)item -(long)sblk); + prev_link = &item->skip_links[0]; + } + + sblk->last_item_off = cpu_to_le32((long)item - (long)sblk); + ikey = (void *)&item->skip_links[1]; inode = (void *)ikey + sizeof(struct scoutfs_inode_key); @@ -294,8 +332,9 @@ static int write_new_fs(char *path, int fd) item->val_len = cpu_to_le16(sizeof(struct scoutfs_inode)); item->nr_links = 1; - ikey->type = SCOUTFS_INODE_KEY; + ikey->zone = SCOUTFS_FS_ZONE; ikey->ino = cpu_to_be64(SCOUTFS_ROOT_INO); + ikey->type = SCOUTFS_INODE_TYPE; inode->next_readdir_pos = cpu_to_le64(2); inode->nlink = cpu_to_le32(SCOUTFS_DIRENT_FIRST_POS); @@ -307,44 +346,7 @@ static int write_new_fs(char *path, int fd) inode->mtime.sec = inode->atime.sec; inode->mtime.nsec = inode->atime.nsec; - *prev_link = cpu_to_le32((long)item -(long)sblk); - prev_link = &item->skip_links[0]; - - item = (void *)inode + sizeof(struct scoutfs_inode); - idx_key = (void *)&item->skip_links[1]; - - /* write the root inode index keys */ - for (i = SCOUTFS_INODE_INDEX_CTIME_KEY; - i <= SCOUTFS_INODE_INDEX_META_SEQ_KEY; i++) { - - item->key_len = cpu_to_le16(sizeof(*idx_key)); - item->val_len = 0; - item->nr_links = 1; - - idx_key->type = i; - idx_key->ino = cpu_to_be64(SCOUTFS_ROOT_INO); - - switch(i) { - case SCOUTFS_INODE_INDEX_CTIME_KEY: - case SCOUTFS_INODE_INDEX_MTIME_KEY: - idx_key->major = cpu_to_be64(tv.tv_sec); - idx_key->minor = cpu_to_be32(tv.tv_usec * 1000); - break; - default: - idx_key->major = cpu_to_be64(0); - idx_key->minor = 0; - break; - } - - *prev_link = cpu_to_le32((long)item -(long)sblk); - prev_link = &item->skip_links[0]; - - sblk->last_item_off = cpu_to_le32((long)item - (long)sblk); - - item = (void *)(idx_key + 1); - idx_key = (void *)&item->skip_links[1]; - } - + item = (void *)(inode + 1); sblk->total_bytes = cpu_to_le32((long)item - (long)sblk); ret = pwrite(fd, sblk, SCOUTFS_SEGMENT_SIZE, diff --git a/utils/src/print.c b/utils/src/print.c index 6462e161..3b3b4367 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -216,7 +216,7 @@ static void print_free_extent(void *key, int key_len, void *val, int val_len) u64 blkno; char *str; - if (blk->type == SCOUTFS_FREE_EXTENT_BLKNO_KEY) { + if (blk->type == SCOUTFS_FREE_EXTENT_BLKNO_TYPE) { str = "free (blkno)"; node_id = be64_to_cpu(blk->node_id); last_blkno = be64_to_cpu(blk->last_blkno); @@ -245,23 +245,58 @@ static void print_inode_index(void *key, int key_len, void *val, int val_len) typedef void (*print_func_t)(void *key, int key_len, void *val, int val_len); -static print_func_t printers[] = { - [SCOUTFS_INODE_KEY] = print_inode, - [SCOUTFS_XATTR_KEY] = print_xattr, - [SCOUTFS_ORPHAN_KEY] = print_orphan, - [SCOUTFS_DIRENT_KEY] = print_dirent, - [SCOUTFS_READDIR_KEY] = print_readdir, - [SCOUTFS_SYMLINK_KEY] = print_symlink, - [SCOUTFS_LINK_BACKREF_KEY] = print_link_backref, - [SCOUTFS_FILE_EXTENT_KEY] = print_file_extent, - [SCOUTFS_FREE_EXTENT_BLKNO_KEY] = print_free_extent, - [SCOUTFS_FREE_EXTENT_BLOCKS_KEY] = print_free_extent, - [SCOUTFS_INODE_INDEX_CTIME_KEY] = print_inode_index, - [SCOUTFS_INODE_INDEX_MTIME_KEY] = print_inode_index, - [SCOUTFS_INODE_INDEX_SIZE_KEY] = print_inode_index, - [SCOUTFS_INODE_INDEX_META_SEQ_KEY] = print_inode_index, - [SCOUTFS_INODE_INDEX_DATA_SEQ_KEY] = print_inode_index, -}; +static print_func_t find_printer(u8 zone, u8 type) +{ + if (zone == SCOUTFS_INODE_INDEX_ZONE && + type >= SCOUTFS_INODE_INDEX_CTIME_TYPE && + type <= SCOUTFS_INODE_INDEX_DATA_SEQ_TYPE) + return print_inode_index; + + if (zone == SCOUTFS_NODE_ZONE) { + if (type >= SCOUTFS_FREE_EXTENT_BLKNO_TYPE && + type <= SCOUTFS_FREE_EXTENT_BLOCKS_TYPE) + return print_free_extent; + if (type == SCOUTFS_ORPHAN_TYPE) + return print_orphan; + } + + if (zone == SCOUTFS_FS_ZONE) { + switch(type) { + case SCOUTFS_INODE_TYPE: return print_inode; + case SCOUTFS_XATTR_TYPE: return print_xattr; + case SCOUTFS_DIRENT_TYPE: return print_dirent; + case SCOUTFS_READDIR_TYPE: return print_readdir; + case SCOUTFS_SYMLINK_TYPE: return print_symlink; + case SCOUTFS_LINK_BACKREF_TYPE: return print_link_backref; + case SCOUTFS_FILE_EXTENT_TYPE: return print_file_extent; + } + } + + return NULL; +} + +static void find_zone_type(void *key, u8 *zone, u8 *type) +{ + struct scoutfs_inode_index_key *idx_key = key; + struct scoutfs_inode_key *ikey = key; + struct scoutfs_orphan_key *okey = key; + + *zone = *(u8 *)key; + + switch (*zone) { + case SCOUTFS_INODE_INDEX_ZONE: + *type = idx_key->type; + break; + case SCOUTFS_NODE_ZONE: + *type = okey->type; + break; + case SCOUTFS_FS_ZONE: + *type = ikey->type; + break; + default: + *type = 0; + } +} static void print_item(struct scoutfs_segment_block *sblk, struct scoutfs_segment_item *item, u32 which, u32 off) @@ -269,19 +304,20 @@ static void print_item(struct scoutfs_segment_block *sblk, print_func_t printer; void *key; void *val; - __u8 type; + u8 type; + u8 zone; int i; key = (char *)&item->skip_links[item->nr_links]; val = (char *)key + le16_to_cpu(item->key_len); - type = *(__u8 *)key; - printer = type < array_size(printers) ? printers[type] : NULL; + find_zone_type(key, &zone, &type); + printer = find_printer(zone, type); - printf(" [%u]: type %u off %u key_len %u val_len %u nr_links %u flags %x%s\n", - which, type, off, le16_to_cpu(item->key_len), + printf(" [%u]: off %u key_len %u val_len %u nr_links %u flags %x%s\n", + which, off, le16_to_cpu(item->key_len), le16_to_cpu(item->val_len), item->nr_links, - item->flags, printer ? "" : " (unrecognized type)"); + item->flags, printer ? "" : " (unrecognized zone+type)"); printf(" links:"); for (i = 0; i < item->nr_links; i++) printf(" %u", le32_to_cpu(item->skip_links[i])); From cf291e2483eb2209f9c808089d7d8f84427a2710 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Tue, 1 Aug 2017 10:43:14 -0700 Subject: [PATCH 103/235] scoutfs-utils: make release block granular Update messaging and the code to reflect that the release file region is specified in terms of 4K blocks. Signed-off-by: Zach Brown --- utils/src/ioctl.h | 24 +++++++++++++++++++++++- utils/src/stage_release.c | 12 ++++++------ 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/utils/src/ioctl.h b/utils/src/ioctl.h index 447b4f8e..b8e8aa40 100644 --- a/utils/src/ioctl.h +++ b/utils/src/ioctl.h @@ -122,8 +122,30 @@ struct scoutfs_ioctl_ino_path { #define SCOUTFS_IOC_DATA_VERSION _IOW(SCOUTFS_IOCTL_MAGIC, 4, __u64) +/* + * "Release" a contiguous range of logical blocks of file data. + * Released blocks are removed from the file system like truncation, but + * an offline record is left behind to trigger demand staging if the + * file is read. + * + * The starting block offset and number of blocks to release are in + * units 4KB blocks. + * + * The specified range can extend past i_size and can straddle sparse + * regions or blocks that are already offline. The only change it makes + * is to free and mark offline any existing blocks that intersect with + * the region. + * + * Returns 0 if the operation succeeds. If an error is returned then + * some partial region of the blocks in the region may have been marked + * offline. + * + * If the operation succeeds then inode metadata that reflects file data + * contents are not updated. This is intended to be transparent to the + * presentation of the data in the file. + */ struct scoutfs_ioctl_release { - __u64 offset; + __u64 block; __u64 count; __u64 data_version; } __packed; diff --git a/utils/src/stage_release.c b/utils/src/stage_release.c index 61750b1a..1efbaa7d 100644 --- a/utils/src/stage_release.c +++ b/utils/src/stage_release.c @@ -127,7 +127,7 @@ static int release_cmd(int argc, char **argv) { struct scoutfs_ioctl_release args; char *endptr = NULL; - u64 offset; + u64 block; u64 count; u64 vers; int ret; @@ -155,10 +155,10 @@ static int release_cmd(int argc, char **argv) goto out; } - offset = strtoull(argv[2], &endptr, 0); + block = strtoull(argv[2], &endptr, 0); if (*endptr != '\0' || - ((offset == LLONG_MIN || offset == LLONG_MAX) && errno == ERANGE)) { - fprintf(stderr, "error parsing starting offset '%s'\n", + ((block == LLONG_MIN || block == LLONG_MAX) && errno == ERANGE)) { + fprintf(stderr, "error parsing starting 4K block offset '%s'\n", argv[2]); ret = -EINVAL; goto out; @@ -173,7 +173,7 @@ static int release_cmd(int argc, char **argv) goto out; } - args.offset = offset; + args.block = block; args.count = count; args.data_version = vers; @@ -190,6 +190,6 @@ out: static void __attribute__((constructor)) release_ctor(void) { - cmd_register("release", " ", + cmd_register("release", " <4K block offset> ", "mark file region offline and free extents", release_cmd); } From 7684e7fcf69ddfbbeebdaac09b225121f68f0225 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Mon, 14 Aug 2017 09:22:14 -0700 Subject: [PATCH 104/235] scoutfs-utils: use exported types format.h and ioctl.h are copied from the kernel module. It had a habit of accidentally using types that aren't exported to userspace. It's since added build checks that enforce exported types. This copies the fixed use of exported types over for hopefully the last time. Signed-off-by: Zach Brown --- utils/src/format.h | 4 ++-- utils/src/ioctl.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index 6251c8f8..6c37816d 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -212,8 +212,8 @@ struct scoutfs_segment_item { __u8 nr_links; __le32 skip_links[0]; /* - * u8 key_bytes[key_len] - * u8 val_bytes[val_len] + * __u8 key_bytes[key_len] + * __u8 val_bytes[val_len] */ } __packed; diff --git a/utils/src/ioctl.h b/utils/src/ioctl.h index b8e8aa40..c1e7f6f9 100644 --- a/utils/src/ioctl.h +++ b/utils/src/ioctl.h @@ -114,7 +114,7 @@ struct scoutfs_ioctl_ino_path { } __packed; #define SCOUTFS_IOC_INO_PATH_CURSOR_BYTES \ - (sizeof(u64) + SCOUTFS_NAME_LEN + 1) + (sizeof(__u64) + SCOUTFS_NAME_LEN + 1) /* Get a single path from the root to the given inode number */ #define SCOUTFS_IOC_INO_PATH _IOW(SCOUTFS_IOCTL_MAGIC, 2, \ From 2c89ff3a07053b04d7350f1457c79de4d624750a Mon Sep 17 00:00:00 2001 From: Mark Fasheh Date: Tue, 22 Aug 2017 15:17:01 -0500 Subject: [PATCH 105/235] scoutfs-utils: remove inode ctime and mtime index items These were removed in the kernel, we no longer need them in userspace. Signed-off-by: Mark Fasheh --- utils/src/format.h | 4 +--- utils/src/ioctl.h | 4 +--- utils/src/key.c | 2 -- utils/src/mkfs.c | 26 ++++++++------------------ utils/src/print.c | 2 +- utils/src/walk_inodes.c | 4 ---- 6 files changed, 11 insertions(+), 31 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index 6c37816d..909c9894 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -243,14 +243,12 @@ struct scoutfs_segment_block { #define SCOUTFS_FS_ZONE 3 /* inode index zone */ -#define SCOUTFS_INODE_INDEX_CTIME_TYPE 1 -#define SCOUTFS_INODE_INDEX_MTIME_TYPE 2 #define SCOUTFS_INODE_INDEX_SIZE_TYPE 3 #define SCOUTFS_INODE_INDEX_META_SEQ_TYPE 4 #define SCOUTFS_INODE_INDEX_DATA_SEQ_TYPE 5 #define SCOUTFS_INODE_INDEX_NR \ - (SCOUTFS_INODE_INDEX_DATA_SEQ_TYPE - SCOUTFS_INODE_INDEX_CTIME_TYPE + 1) + (SCOUTFS_INODE_INDEX_DATA_SEQ_TYPE - SCOUTFS_INODE_INDEX_SIZE_TYPE + 1) /* node zone */ #define SCOUTFS_FREE_EXTENT_BLKNO_TYPE 11 diff --git a/utils/src/ioctl.h b/utils/src/ioctl.h index c1e7f6f9..e9a78db0 100644 --- a/utils/src/ioctl.h +++ b/utils/src/ioctl.h @@ -51,9 +51,7 @@ struct scoutfs_ioctl_walk_inodes { } __packed; enum { - SCOUTFS_IOC_WALK_INODES_CTIME = 0, - SCOUTFS_IOC_WALK_INODES_MTIME, - SCOUTFS_IOC_WALK_INODES_SIZE, + SCOUTFS_IOC_WALK_INODES_SIZE = 0, SCOUTFS_IOC_WALK_INODES_META_SEQ, SCOUTFS_IOC_WALK_INODES_DATA_SEQ, SCOUTFS_IOC_WALK_INODES_UNKNOWN, diff --git a/utils/src/key.c b/utils/src/key.c index 46bc1420..99730c75 100644 --- a/utils/src/key.c +++ b/utils/src/key.c @@ -34,8 +34,6 @@ int print_key(void *key_data, unsigned key_len) case SCOUTFS_INODE_INDEX_ZONE: { struct scoutfs_inode_index_key *ikey = key_data; static char *type_strings[] = { - [SCOUTFS_INODE_INDEX_CTIME_TYPE] = "ctm", - [SCOUTFS_INODE_INDEX_MTIME_TYPE] = "mtm", [SCOUTFS_INODE_INDEX_SIZE_TYPE] = "siz", [SCOUTFS_INODE_INDEX_META_SEQ_TYPE] = "msq", [SCOUTFS_INODE_INDEX_DATA_SEQ_TYPE] = "dsq", diff --git a/utils/src/mkfs.c b/utils/src/mkfs.c index 6182d199..943ffd8d 100644 --- a/utils/src/mkfs.c +++ b/utils/src/mkfs.c @@ -258,9 +258,9 @@ static int write_new_fs(char *path, int fd) mkey->level = 1; idx_key->zone = SCOUTFS_INODE_INDEX_ZONE; - idx_key->type = SCOUTFS_INODE_INDEX_CTIME_TYPE; - idx_key->major = cpu_to_be64(tv.tv_sec); - idx_key->minor = cpu_to_be32(tv.tv_usec * 1000); + idx_key->type = SCOUTFS_INODE_INDEX_SIZE_TYPE; + idx_key->major = 0; + idx_key->minor = 0; idx_key->ino = cpu_to_be64(SCOUTFS_ROOT_INO); mval->segno = cpu_to_le64(first_segno); @@ -285,7 +285,6 @@ static int write_new_fs(char *path, int fd) /* write seg with root inode */ sblk->segno = cpu_to_le64(first_segno); sblk->seq = cpu_to_le64(1); - sblk->nr_items = cpu_to_le32(5); prev_link = &sblk->skip_links[0]; item = (void *)(sblk + 1); @@ -293,30 +292,20 @@ static int write_new_fs(char *path, int fd) prev_link = &item->skip_links[0]; /* write the root inode index keys */ - for (i = SCOUTFS_INODE_INDEX_CTIME_TYPE; + for (i = SCOUTFS_INODE_INDEX_SIZE_TYPE; i <= SCOUTFS_INODE_INDEX_META_SEQ_TYPE; i++) { item->key_len = cpu_to_le16(sizeof(*idx_key)); item->val_len = 0; item->nr_links = 1; + le32_add_cpu(&sblk->nr_items, 1); idx_key = (void *)&item->skip_links[1]; idx_key->zone = SCOUTFS_INODE_INDEX_ZONE; idx_key->type = i; idx_key->ino = cpu_to_be64(SCOUTFS_ROOT_INO); - - switch(i) { - case SCOUTFS_INODE_INDEX_CTIME_TYPE: - case SCOUTFS_INODE_INDEX_MTIME_TYPE: - idx_key->major = cpu_to_be64(tv.tv_sec); - idx_key->minor = cpu_to_be32(tv.tv_usec * 1000); - break; - default: - idx_key->major = cpu_to_be64(0); - idx_key->minor = 0; - break; - } - + idx_key->major = 0; + idx_key->minor = 0; item = (void *)(idx_key + 1); *prev_link = cpu_to_le32((long)item -(long)sblk); @@ -331,6 +320,7 @@ static int write_new_fs(char *path, int fd) item->key_len = cpu_to_le16(sizeof(struct scoutfs_inode_key)); item->val_len = cpu_to_le16(sizeof(struct scoutfs_inode)); item->nr_links = 1; + le32_add_cpu(&sblk->nr_items, 1); ikey->zone = SCOUTFS_FS_ZONE; ikey->ino = cpu_to_be64(SCOUTFS_ROOT_INO); diff --git a/utils/src/print.c b/utils/src/print.c index 3b3b4367..08be009b 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -248,7 +248,7 @@ typedef void (*print_func_t)(void *key, int key_len, void *val, int val_len); static print_func_t find_printer(u8 zone, u8 type) { if (zone == SCOUTFS_INODE_INDEX_ZONE && - type >= SCOUTFS_INODE_INDEX_CTIME_TYPE && + type >= SCOUTFS_INODE_INDEX_SIZE_TYPE && type <= SCOUTFS_INODE_INDEX_DATA_SEQ_TYPE) return print_inode_index; diff --git a/utils/src/walk_inodes.c b/utils/src/walk_inodes.c index 5d06c4cd..045b6976 100644 --- a/utils/src/walk_inodes.c +++ b/utils/src/walk_inodes.c @@ -82,10 +82,6 @@ static int walk_inodes_cmd(int argc, char **argv) if (!strcasecmp(argv[0], "size")) walk.index = SCOUTFS_IOC_WALK_INODES_SIZE; - else if (!strcasecmp(argv[0], "ctime")) - walk.index = SCOUTFS_IOC_WALK_INODES_CTIME; - else if (!strcasecmp(argv[0], "mtime")) - walk.index = SCOUTFS_IOC_WALK_INODES_MTIME; else if (!strcasecmp(argv[0], "meta_seq")) walk.index = SCOUTFS_IOC_WALK_INODES_META_SEQ; else if (!strcasecmp(argv[0], "data_seq")) From affdaddc156a89172499a233cc510271c0b9acc5 Mon Sep 17 00:00:00 2001 From: Mark Fasheh Date: Thu, 31 Aug 2017 12:56:26 -0500 Subject: [PATCH 106/235] scoutfs-utils: zero minor variable in parse_walk_entry() Signed-off-by: Mark Fasheh --- utils/src/walk_inodes.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/src/walk_inodes.c b/utils/src/walk_inodes.c index 045b6976..91378ef3 100644 --- a/utils/src/walk_inodes.c +++ b/utils/src/walk_inodes.c @@ -26,7 +26,7 @@ static int parse_walk_entry(struct scoutfs_ioctl_walk_inodes_entry *ent, char *endptr; char *c; u64 ull; - u64 minor; + u64 minor = 0; u64 *val; memset(ent, 0, sizeof(*ent)); From 288a752f4246d59ccbe167fc57f14a1b4c1368d6 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Tue, 5 Sep 2017 09:57:26 -0700 Subject: [PATCH 107/235] scoutfs-utils: update key printing The kernel key printing code was refactored to more carefully print keys. Import this updated code by adding supporting functions around it so that we don't have to make edits to it and can easily update the import in the future. Signed-off-by: Zach Brown --- utils/src/format.h | 3 + utils/src/key.c | 521 +++++++++++++++++++++++++++++++-------------- utils/src/key.h | 2 +- 3 files changed, 363 insertions(+), 163 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index 909c9894..3c95f421 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -241,6 +241,7 @@ struct scoutfs_segment_block { #define SCOUTFS_INODE_INDEX_ZONE 1 #define SCOUTFS_NODE_ZONE 2 #define SCOUTFS_FS_ZONE 3 +#define SCOUTFS_MAX_ZONE 4 /* power of 2 is efficient */ /* inode index zone */ #define SCOUTFS_INODE_INDEX_SIZE_TYPE 3 @@ -264,6 +265,8 @@ struct scoutfs_segment_block { #define SCOUTFS_FILE_EXTENT_TYPE 7 #define SCOUTFS_ORPHAN_TYPE 8 +#define SCOUTFS_MAX_TYPE 16 /* power of 2 is efficient */ + /* XXX don't need these now that we have dlm lock spaces and resources */ #define SCOUTFS_NET_ADDR_TYPE 254 #define SCOUTFS_NET_LISTEN_TYPE 255 diff --git a/utils/src/key.c b/utils/src/key.c index 99730c75..beff97b2 100644 --- a/utils/src/key.c +++ b/utils/src/key.c @@ -2,6 +2,7 @@ #include #include #include +#include #include "sparse.h" #include "util.h" @@ -9,170 +10,366 @@ #include "key.h" /* - * This is mechanically derived from scoutfs_key_str() in the kernel: - * - :.,$s/key->data/key_data/g - * - :.,$s/key->key_len/key_len/g - * - :.,$s/return snprintf_null(buf, size, /return printf(/g + * To print keys we wrap the key snprintf code from the kernel with a + * few support functions. We need a few functions that the kernel has + * that we don't provide, then we implement our printing function by + * allocating a buffer for the formatted output then just printing it. + * + * To update the key printing code from the kernel we just need to make + * scoutfs_key_str_size() static and replace the snprintf call with the + * kernel's "%phN" format with the call to our replacement. + * + * This is not efficient but this isn't a performant path. */ -int print_key(void *key_data, unsigned key_len) + +#define min_t(t, a, b) min(a, b) + +struct scoutfs_key_buf { + void *data; + unsigned key_len; +}; + +/* + * like snprintf(buf, size, "%*phN", nr, bytes) in the kernel, but this + * is only called when there's room for the formatted output because + * we've already been through once with a 0 buffer to allocate a buffer + * for the output. + */ +static int snprintf_phN(char *buf, size_t size, unsigned nr, char *bytes) { - struct scoutfs_inode_key *ikey; - u8 zone = 0; - u8 type = 0; - int len; + int ret = 0; + int i; - if (key_data == NULL) - return printf("[NULL]"); - - if (key_len == 0) - return printf("[0 len]"); - - zone = *(u8 *)key_data; - - /* handle smaller and unknown zones, fall through to fs types */ - switch(zone) { - case SCOUTFS_INODE_INDEX_ZONE: { - struct scoutfs_inode_index_key *ikey = key_data; - static char *type_strings[] = { - [SCOUTFS_INODE_INDEX_SIZE_TYPE] = "siz", - [SCOUTFS_INODE_INDEX_META_SEQ_TYPE] = "msq", - [SCOUTFS_INODE_INDEX_DATA_SEQ_TYPE] = "dsq", - }; - - if (key_len < sizeof(struct scoutfs_inode_index_key)) - break; - - if (type_strings[ikey->type]) - return printf("iin.%s.%llu.%u.%llu", - type_strings[ikey->type], - be64_to_cpu(ikey->major), - be32_to_cpu(ikey->minor), - be64_to_cpu(ikey->ino)); - else - return printf("[iin type %u?]", - ikey->type); - } - - /* node zone keys start with zone, node, type */ - case SCOUTFS_NODE_ZONE: { - struct scoutfs_free_extent_blkno_key *fkey = key_data; - - static char *type_strings[] = { - [SCOUTFS_FREE_EXTENT_BLKNO_TYPE] = "fno", - [SCOUTFS_FREE_EXTENT_BLOCKS_TYPE] = "fks", - }; - - switch(fkey->type) { - case SCOUTFS_ORPHAN_TYPE: { - struct scoutfs_orphan_key *okey = key_data; - - if (key_len < sizeof(struct scoutfs_orphan_key)) - break; - return printf("nod.%llu.orp.%llu", - be64_to_cpu(okey->node_id), - be64_to_cpu(okey->ino)); - } - - case SCOUTFS_FREE_EXTENT_BLKNO_TYPE: - case SCOUTFS_FREE_EXTENT_BLOCKS_TYPE: - return printf("nod.%llu.%s.%llu.%llu", - be64_to_cpu(fkey->node_id), - type_strings[fkey->type], - be64_to_cpu(fkey->last_blkno), - be64_to_cpu(fkey->blocks)); - default: - return printf("[nod type %u?]", - fkey->type); - } - } - - case SCOUTFS_FS_ZONE: - break; - - default: - return printf("[zone %u?]", zone); - } - - /* everything in the fs tree starts with zone, ino, type */ - ikey = key_data; - switch(ikey->type) { - case SCOUTFS_INODE_TYPE: { - struct scoutfs_inode_key *ikey = key_data; - - if (key_len < sizeof(struct scoutfs_inode_key)) - break; - - return printf("fs.%llu.ino", - be64_to_cpu(ikey->ino)); - } - - case SCOUTFS_XATTR_TYPE: { - struct scoutfs_xattr_key *xkey = key_data; - - len = (int)key_len - offsetof(struct scoutfs_xattr_key, - name[1]); - if (len <= 0) - break; - - return printf("fs.%llu.xat.%.*s", - be64_to_cpu(xkey->ino), len, xkey->name); - } - - case SCOUTFS_DIRENT_TYPE: { - struct scoutfs_dirent_key *dkey = key_data; - - len = (int)key_len - sizeof(struct scoutfs_dirent_key); - if (len <= 0) - break; - - return printf("fs.%llu.dnt.%.*s", - be64_to_cpu(dkey->ino), len, dkey->name); - } - - case SCOUTFS_READDIR_TYPE: { - struct scoutfs_readdir_key *rkey = key_data; - - return printf("fs.%llu.rdr.%llu", - be64_to_cpu(rkey->ino), - be64_to_cpu(rkey->pos)); - } - - case SCOUTFS_LINK_BACKREF_TYPE: { - struct scoutfs_link_backref_key *lkey = key_data; - - len = (int)key_len - sizeof(*lkey); - if (len <= 0) - break; - - return printf("fs.%llu.lbr.%llu.%.*s", - be64_to_cpu(lkey->ino), - be64_to_cpu(lkey->dir_ino), len, - lkey->name); - } - - case SCOUTFS_SYMLINK_TYPE: { - struct scoutfs_symlink_key *skey = key_data; - - return printf("fs.%llu.sym", - be64_to_cpu(skey->ino)); - } - - case SCOUTFS_FILE_EXTENT_TYPE: { - struct scoutfs_file_extent_key *ekey = key_data; - - return printf("fs.%llu.ext.%llu.%llu.%llu.%x", - be64_to_cpu(ekey->ino), - be64_to_cpu(ekey->last_blk_off), - be64_to_cpu(ekey->last_blkno), - be64_to_cpu(ekey->blocks), - ekey->flags); - } - - default: - return printf("[fs type %u?]", type); - } - - return printf("[fs type %u trunc len %u]", - type, key_len); + for (i = 0; i < nr; i++) + ret += sprintf(buf + ret, "%02x", bytes[i]); + return ret; +} + +static char *memchr_inv(char *str, int c, size_t len) +{ + while (len--) { + if (*(str++) != c) + return str - 1; + } + + return NULL; +} + +static int scoutfs_key_str_size(char *buf, struct scoutfs_key_buf *key, + size_t size); + +void print_key(void *key_data, unsigned key_len) +{ + struct scoutfs_key_buf key = {.data = key_data, .key_len = key_len}; + char *buf; + int size; + + size = scoutfs_key_str_size(NULL, &key, 0); + if (size > 0) { + buf = malloc(size); + if (buf) { + size = scoutfs_key_str_size(buf, &key, size); + if (size > 0) + printf("%s", buf); + free(buf); + } + } +} + +/* ------ copied code follows --------- */ + +#define snprintf_null(buf, size, fmt, args...) \ + (snprintf((buf), (size), fmt, ##args) + 1) + +/* + * Store a formatted string representing the key in the buffer. The key + * must be at least min_len to store the data needed by the format at + * all. fmt_len is the length of data that's used by the format. These + * are different because we have badly designed keys with variable + * length data that isn't described by the key. It's assumed from the + * length of the key. Take dirents -- they need to at least have a + * dirent struct, but the name length is the rest of the key. + * + * (XXX And this goes horribly wrong when we pad out dirent keys to max + * len to increment at high precision. We'll never see these items used + * by real fs code, but temporary keys and range endpoints can be full + * precision and we can try and print them and get very confused. We + * need to rev the format to include explicit lengths.) + * + * If the format doesn't cover the entire key then we append more + * formatting to represent the trailing bytes: runs of zeros compresesd + * to _ and then hex output of non-zero bytes. + */ +static int snprintf_key(char *buf, size_t size, struct scoutfs_key_buf *key, + unsigned min_len, unsigned fmt_len, + const char *fmt, ...) + +{ + va_list args; + char *data; + char *end; + int left; + int part; + int ret; + int nr; + + if (key->key_len < min_len) + return snprintf_null(buf, size, "[trunc len %u < min %u]", + key->key_len, min_len); + + if (fmt_len == 0) + fmt_len = min_len; + + va_start(args, fmt); + ret = vsnprintf(buf, size, fmt, args); + va_end(args); + /* next formatting overwrites null */ + if (buf) { + buf += ret; + size -= min_t(int, size, ret); + } + + data = key->data + fmt_len; + left = key->key_len - fmt_len; + + while (left && (!buf || size > 1)) { + /* compress runs of zero bytes to _ */ + end = memchr_inv(data, 0, left); + nr = end ? end - data : left; + if (nr) { + if (buf) { + *(buf++) = '_'; + size--; + } + ret++; + data += nr; + left -= nr; + continue; + } + + /* + * hex print non-zero bytes. %ph is limited to 64 bytes + * and is buggy in that it still tries to print to buf + * past size. (so buf = null, size = 0 crashes instead + * of printing the length of the formatted string.) + */ + end = memchr(data, 0, left); + nr = end ? end - data : left; + nr = min(nr, 64); + + if (buf) + part = snprintf_phN(buf, size, nr, data); + else + part = nr * 2; + if (buf) { + buf += part; + size -= min_t(int, size, part); + } + ret += part; + + data += nr; + left -= nr; + } + + /* always store and include null */ + if (buf) + *buf = '\0'; + return ret + 1; +} + +typedef int (*key_printer_t)(char *buf, struct scoutfs_key_buf *key, + size_t size); + +static int pr_ino_idx(char *buf, struct scoutfs_key_buf *key, size_t size) +{ + static char *type_strings[] = { + [SCOUTFS_INODE_INDEX_SIZE_TYPE] = "siz", + [SCOUTFS_INODE_INDEX_META_SEQ_TYPE] = "msq", + [SCOUTFS_INODE_INDEX_DATA_SEQ_TYPE] = "dsq", + }; + struct scoutfs_inode_index_key *ikey = key->data; + + return snprintf_key(buf, size, key, + sizeof(struct scoutfs_inode_index_key), 0, + "iin.%s.%llu.%u.%llu", + type_strings[ikey->type], be64_to_cpu(ikey->major), + be32_to_cpu(ikey->minor), be64_to_cpu(ikey->ino)); +} + +static int pr_free_ext(char *buf, struct scoutfs_key_buf *key, size_t size) +{ + struct scoutfs_free_extent_blkno_key *fkey = key->data; + + static char *type_strings[] = { + [SCOUTFS_FREE_EXTENT_BLKNO_TYPE] = "fno", + [SCOUTFS_FREE_EXTENT_BLOCKS_TYPE] = "fks", + }; + + return snprintf_key(buf, size, key, + sizeof(struct scoutfs_free_extent_blkno_key), 0, + "nod.%llu.%s.%llu.%llu", + be64_to_cpu(fkey->node_id), + type_strings[fkey->type], + be64_to_cpu(fkey->last_blkno), + be64_to_cpu(fkey->blocks)); +} + +static int pr_orphan(char *buf, struct scoutfs_key_buf *key, size_t size) +{ + struct scoutfs_orphan_key *okey = key->data; + + return snprintf_key(buf, size, key, + sizeof(struct scoutfs_orphan_key), 0, + "nod.%llu.orp.%llu", + be64_to_cpu(okey->node_id), + be64_to_cpu(okey->ino)); +} + +static int pr_inode(char *buf, struct scoutfs_key_buf *key, size_t size) +{ + struct scoutfs_inode_key *ikey = key->data; + + return snprintf_key(buf, size, key, + sizeof(struct scoutfs_inode_key), 0, + "fs.%llu.ino", + be64_to_cpu(ikey->ino)); +} + +static int pr_xattr(char *buf, struct scoutfs_key_buf *key, size_t size) +{ + struct scoutfs_xattr_key *xkey = key->data; + int len = (int)key->key_len - + offsetof(struct scoutfs_xattr_key, name[1]); + + return snprintf_key(buf, size, key, + sizeof(struct scoutfs_xattr_key), key->key_len, + "fs.%llu.xat.%.*s", + be64_to_cpu(xkey->ino), len, xkey->name); +} + +static int pr_dirent(char *buf, struct scoutfs_key_buf *key, size_t size) +{ + struct scoutfs_dirent_key *dkey = key->data; + int len = (int)key->key_len - sizeof(struct scoutfs_dirent_key); + + return snprintf_key(buf, size, key, + sizeof(struct scoutfs_dirent_key), key->key_len, + "fs.%llu.dnt.%.*s", + be64_to_cpu(dkey->ino), len, dkey->name); +} + +static int pr_readdir(char *buf, struct scoutfs_key_buf *key, size_t size) +{ + struct scoutfs_readdir_key *rkey = key->data; + + return snprintf_key(buf, size, key, + sizeof(struct scoutfs_readdir_key), 0, + "fs.%llu.rdr.%llu", + be64_to_cpu(rkey->ino), be64_to_cpu(rkey->pos)); +} + +static int pr_link_backref(char *buf, struct scoutfs_key_buf *key, size_t size) +{ + struct scoutfs_link_backref_key *lkey = key->data; + int len = (int)key->key_len - sizeof(*lkey); + + return snprintf_key(buf, size, key, + sizeof(struct scoutfs_link_backref_key), + key->key_len, + "fs.%llu.lbr.%llu.%.*s", + be64_to_cpu(lkey->ino), be64_to_cpu(lkey->dir_ino), + len, lkey->name); +} + +static int pr_symlink(char *buf, struct scoutfs_key_buf *key, size_t size) +{ + struct scoutfs_symlink_key *skey = key->data; + + return snprintf_key(buf, size, key, + sizeof(struct scoutfs_symlink_key), 0, + "fs.%llu.sym", + be64_to_cpu(skey->ino)); +} + +static int pr_file_ext(char *buf, struct scoutfs_key_buf *key, size_t size) +{ + struct scoutfs_file_extent_key *ekey = key->data; + + return snprintf_key(buf, size, key, + sizeof(struct scoutfs_file_extent_key), 0, + "fs.%llu.ext.%llu.%llu.%llu.%x", + be64_to_cpu(ekey->ino), + be64_to_cpu(ekey->last_blk_off), + be64_to_cpu(ekey->last_blkno), + be64_to_cpu(ekey->blocks), + ekey->flags); +} + +const static key_printer_t key_printers[SCOUTFS_MAX_ZONE][SCOUTFS_MAX_TYPE] = { + [SCOUTFS_INODE_INDEX_ZONE][SCOUTFS_INODE_INDEX_SIZE_TYPE] = + pr_ino_idx, + [SCOUTFS_INODE_INDEX_ZONE][SCOUTFS_INODE_INDEX_META_SEQ_TYPE] = + pr_ino_idx, + [SCOUTFS_INODE_INDEX_ZONE][SCOUTFS_INODE_INDEX_DATA_SEQ_TYPE] = + pr_ino_idx, + [SCOUTFS_NODE_ZONE][SCOUTFS_FREE_EXTENT_BLKNO_TYPE] = pr_free_ext, + [SCOUTFS_NODE_ZONE][SCOUTFS_FREE_EXTENT_BLOCKS_TYPE] = pr_free_ext, + [SCOUTFS_NODE_ZONE][SCOUTFS_ORPHAN_TYPE] = pr_orphan, + [SCOUTFS_FS_ZONE][SCOUTFS_INODE_TYPE] = pr_inode, + [SCOUTFS_FS_ZONE][SCOUTFS_XATTR_TYPE] = pr_xattr, + [SCOUTFS_FS_ZONE][SCOUTFS_DIRENT_TYPE] = pr_dirent, + [SCOUTFS_FS_ZONE][SCOUTFS_READDIR_TYPE] = pr_readdir, + [SCOUTFS_FS_ZONE][SCOUTFS_LINK_BACKREF_TYPE] = pr_link_backref, + [SCOUTFS_FS_ZONE][SCOUTFS_SYMLINK_TYPE] = pr_symlink, + [SCOUTFS_FS_ZONE][SCOUTFS_FILE_EXTENT_TYPE] = pr_file_ext, +}; + +/* + * Write the null-terminated string that describes the key to the + * buffer. The bytes copied (including the null) is returned. A null + * buffer can be used to find the string size without writing anything. + * + * XXX nonprintable characters in the trace? + */ +static int scoutfs_key_str_size(char *buf, struct scoutfs_key_buf *key, + size_t size) +{ + u8 zone; + u8 type; + + if (key == NULL || key->data == NULL) + return snprintf_null(buf, size, "[NULL]"); + + /* always at least zone, some id, and type */ + if (key->key_len < (1 + 8 + 1)) + return snprintf_null(buf, size, "[trunc len %u]", key->key_len); + + zone = *(u8 *)key->data; + + /* + * each zone's keys always start with the same fields that let + * us deref any key to get the type. We chose a few representative + * keys from each zone to get the type. + */ + if (zone == SCOUTFS_INODE_INDEX_ZONE) { + struct scoutfs_inode_index_key *ikey = key->data; + type = ikey->type; + } else if (zone == SCOUTFS_NODE_ZONE) { + struct scoutfs_free_extent_blkno_key *fkey = key->data; + type = fkey->type; + } else if (zone == SCOUTFS_FS_ZONE) { + struct scoutfs_inode_key *ikey = key->data; + type = ikey->type; + } else { + type = 255; + } + + if (zone > SCOUTFS_MAX_ZONE || type > SCOUTFS_MAX_TYPE || + key_printers[zone][type] == NULL) { + return snprintf_null(buf, size, "[unk zone %u type %u]", + zone, type); + } + + return key_printers[zone][type](buf, key, size); } diff --git a/utils/src/key.h b/utils/src/key.h index ebefbf9c..bb51b80f 100644 --- a/utils/src/key.h +++ b/utils/src/key.h @@ -1,6 +1,6 @@ #ifndef _KEY_H_ #define _KEY_H_ -int print_key(void *key_data, unsigned key_len); +void print_key(void *key_data, unsigned key_len); #endif From 589e9d10b9461dd3a22b3056c4122c9a7edef91f Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Sun, 17 Sep 2017 11:15:57 -0700 Subject: [PATCH 108/235] scoutfs-utils: move to block mapping items Update the format header and print output for the mapping items and free bitmaps. Signed-off-by: Zach Brown --- utils/src/format.h | 98 ++++++++++++++++++++++++++++++++-------------- utils/src/key.c | 45 ++++++++++----------- utils/src/print.c | 60 +++++++++++----------------- 3 files changed, 111 insertions(+), 92 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index 3c95f421..b435bfb7 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -252,8 +252,8 @@ struct scoutfs_segment_block { (SCOUTFS_INODE_INDEX_DATA_SEQ_TYPE - SCOUTFS_INODE_INDEX_SIZE_TYPE + 1) /* node zone */ -#define SCOUTFS_FREE_EXTENT_BLKNO_TYPE 11 -#define SCOUTFS_FREE_EXTENT_BLOCKS_TYPE 12 +#define SCOUTFS_FREE_BITS_SEGNO_TYPE 1 +#define SCOUTFS_FREE_BITS_BLKNO_TYPE 2 /* fs zone */ #define SCOUTFS_INODE_TYPE 1 @@ -262,15 +262,11 @@ struct scoutfs_segment_block { #define SCOUTFS_READDIR_TYPE 4 #define SCOUTFS_LINK_BACKREF_TYPE 5 #define SCOUTFS_SYMLINK_TYPE 6 -#define SCOUTFS_FILE_EXTENT_TYPE 7 +#define SCOUTFS_BLOCK_MAPPING_TYPE 7 #define SCOUTFS_ORPHAN_TYPE 8 #define SCOUTFS_MAX_TYPE 16 /* power of 2 is efficient */ -/* XXX don't need these now that we have dlm lock spaces and resources */ -#define SCOUTFS_NET_ADDR_TYPE 254 -#define SCOUTFS_NET_LISTEN_TYPE 255 - /* value is struct scoutfs_inode */ struct scoutfs_inode_key { __u8 zone; @@ -303,38 +299,70 @@ struct scoutfs_link_backref_key { __u8 name[0]; } __packed; - -/* no value */ -struct scoutfs_file_extent_key { +/* key is bytes of encoded block mapping */ +struct scoutfs_block_mapping_key { __u8 zone; __be64 ino; __u8 type; - __be64 last_blk_off; - __be64 last_blkno; - __be64 blocks; - __u8 flags; + __be64 base; } __packed; -#define SCOUTFS_FILE_EXTENT_OFFLINE (1 << 0) +/* each mapping item describes a fixed number of blocks */ +#define SCOUTFS_BLOCK_MAPPING_SHIFT 6 +#define SCOUTFS_BLOCK_MAPPING_BLOCKS (1 << SCOUTFS_BLOCK_MAPPING_SHIFT) +#define SCOUTFS_BLOCK_MAPPING_MASK (SCOUTFS_BLOCK_MAPPING_BLOCKS - 1) -/* no value */ -struct scoutfs_free_extent_blkno_key { +/* + * The mapping item value is a byte stream that encodes the value of the + * mapped blocks. The first byte contains the last index that contains + * a mapped block in its low bits. The high bits contain the control + * bits for the first (and possibly only) mapped block. + * + * From then on we consume the control bits in the current control byte + * for each mapped block. Each block has two bits that describe the + * block: zero, incremental from previous block, delta encoded, and + * offline. If we run out of control bits then we consume the next byte + * in the stream for additional control bits. If we have a delta + * encoded block then we consume its encoded bytes from the byte stream. + */ + +#define SCOUTFS_BLOCK_ENC_ZERO 0 +#define SCOUTFS_BLOCK_ENC_INC 1 +#define SCOUTFS_BLOCK_ENC_DELTA 2 +#define SCOUTFS_BLOCK_ENC_OFFLINE 3 +#define SCOUTFS_BLOCK_ENC_MASK 3 + +#define SCOUTFS_ZIGZAG_MAX_BYTES (DIV_ROUND_UP(64, 7)) + +/* + * the largest block mapping has: nr byte, ctl bytes for all blocks, and + * worst case zigzag encodings for all blocks. + */ +#define SCOUTFS_BLOCK_MAPPING_MAX_BYTES \ + (1 + (SCOUTFS_BLOCK_MAPPING_BLOCKS / 4) + \ + (SCOUTFS_BLOCK_MAPPING_BLOCKS * SCOUTFS_ZIGZAG_MAX_BYTES)) + +/* free bit bitmaps contain a segment's worth of blocks */ +#define SCOUTFS_FREE_BITS_SHIFT \ + SCOUTFS_SEGMENT_BLOCK_SHIFT +#define SCOUTFS_FREE_BITS_BITS \ + (1 << SCOUTFS_FREE_BITS_SHIFT) +#define SCOUTFS_FREE_BITS_MASK \ + (SCOUTFS_FREE_BITS_BITS - 1) +#define SCOUTFS_FREE_BITS_U64S \ + DIV_ROUND_UP(SCOUTFS_FREE_BITS_BITS, 64) + +struct scoutfs_free_bits_key { __u8 zone; __be64 node_id; __u8 type; - __be64 last_blkno; - __be64 blocks; + __be64 base; } __packed; -struct scoutfs_free_extent_blocks_key { - __u8 zone; - __be64 node_id; - __u8 type; - __be64 blocks; - __be64 last_blkno; +struct scoutfs_free_bits { + __le64 bits[SCOUTFS_FREE_BITS_U64S]; } __packed; -/* no value */ struct scoutfs_orphan_key { __u8 zone; __be64 node_id; @@ -496,9 +524,7 @@ enum { #define SCOUTFS_MAX_KEY_SIZE \ offsetof(struct scoutfs_link_backref_key, name[SCOUTFS_NAME_LEN + 1]) -/* largest single val are dirents, larger broken up into units of this */ -#define SCOUTFS_MAX_VAL_SIZE \ - offsetof(struct scoutfs_dirent, name[SCOUTFS_NAME_LEN]) +#define SCOUTFS_MAX_VAL_SIZE SCOUTFS_BLOCK_MAPPING_MAX_BYTES #define SCOUTFS_XATTR_MAX_NAME_LEN 255 #define SCOUTFS_XATTR_MAX_SIZE 65536 @@ -510,7 +536,13 @@ enum { /* * structures used by dlm */ +#define SCOUTFS_LOCK_SCOPE_GLOBAL 1 +#define SCOUTFS_LOCK_SCOPE_FS_ITEMS 2 + +#define SCOUTFS_LOCK_TYPE_GLOBAL_RENAME 1 + struct scoutfs_lock_name { + __u8 scope; __u8 zone; __u8 type; __le64 first; @@ -572,6 +604,13 @@ struct scoutfs_net_segnos { __le64 segnos[0]; } __packed; +struct scoutfs_net_statfs { + __le64 total_segs; /* total segments in device */ + __le64 next_ino; /* next unused inode number */ + __le64 bfree; /* total free small blocks */ + __u8 uuid[SCOUTFS_UUID_BYTES]; /* logical volume uuid */ +} __packed; + /* XXX eventually we'll have net compaction and will need agents to agree */ /* one upper segment and fanout lower segments */ @@ -590,6 +629,7 @@ enum { SCOUTFS_NET_ADVANCE_SEQ, SCOUTFS_NET_GET_LAST_SEQ, SCOUTFS_NET_GET_MANIFEST_ROOT, + SCOUTFS_NET_STATFS, SCOUTFS_NET_UNKNOWN, }; diff --git a/utils/src/key.c b/utils/src/key.c index beff97b2..7588ea7f 100644 --- a/utils/src/key.c +++ b/utils/src/key.c @@ -197,22 +197,20 @@ static int pr_ino_idx(char *buf, struct scoutfs_key_buf *key, size_t size) be32_to_cpu(ikey->minor), be64_to_cpu(ikey->ino)); } -static int pr_free_ext(char *buf, struct scoutfs_key_buf *key, size_t size) +static int pr_free_bits(char *buf, struct scoutfs_key_buf *key, size_t size) { - struct scoutfs_free_extent_blkno_key *fkey = key->data; - static char *type_strings[] = { - [SCOUTFS_FREE_EXTENT_BLKNO_TYPE] = "fno", - [SCOUTFS_FREE_EXTENT_BLOCKS_TYPE] = "fks", + [SCOUTFS_FREE_BITS_SEGNO_TYPE] = "fsg", + [SCOUTFS_FREE_BITS_BLKNO_TYPE] = "fbk", }; + struct scoutfs_free_bits_key *frk = key->data; return snprintf_key(buf, size, key, - sizeof(struct scoutfs_free_extent_blkno_key), 0, - "nod.%llu.%s.%llu.%llu", - be64_to_cpu(fkey->node_id), - type_strings[fkey->type], - be64_to_cpu(fkey->last_blkno), - be64_to_cpu(fkey->blocks)); + sizeof(struct scoutfs_block_mapping_key), 0, + "nod.%llu.%s.%llu", + be64_to_cpu(frk->node_id), + type_strings[frk->type], + be64_to_cpu(frk->base)); } static int pr_orphan(char *buf, struct scoutfs_key_buf *key, size_t size) @@ -292,18 +290,15 @@ static int pr_symlink(char *buf, struct scoutfs_key_buf *key, size_t size) be64_to_cpu(skey->ino)); } -static int pr_file_ext(char *buf, struct scoutfs_key_buf *key, size_t size) +static int pr_block_mapping(char *buf, struct scoutfs_key_buf *key, size_t size) { - struct scoutfs_file_extent_key *ekey = key->data; + struct scoutfs_block_mapping_key *bmk = key->data; return snprintf_key(buf, size, key, - sizeof(struct scoutfs_file_extent_key), 0, - "fs.%llu.ext.%llu.%llu.%llu.%x", - be64_to_cpu(ekey->ino), - be64_to_cpu(ekey->last_blk_off), - be64_to_cpu(ekey->last_blkno), - be64_to_cpu(ekey->blocks), - ekey->flags); + sizeof(struct scoutfs_block_mapping_key), 0, + "fs.%llu.bmp.%llu", + be64_to_cpu(bmk->ino), + be64_to_cpu(bmk->base)); } const static key_printer_t key_printers[SCOUTFS_MAX_ZONE][SCOUTFS_MAX_TYPE] = { @@ -313,8 +308,8 @@ const static key_printer_t key_printers[SCOUTFS_MAX_ZONE][SCOUTFS_MAX_TYPE] = { pr_ino_idx, [SCOUTFS_INODE_INDEX_ZONE][SCOUTFS_INODE_INDEX_DATA_SEQ_TYPE] = pr_ino_idx, - [SCOUTFS_NODE_ZONE][SCOUTFS_FREE_EXTENT_BLKNO_TYPE] = pr_free_ext, - [SCOUTFS_NODE_ZONE][SCOUTFS_FREE_EXTENT_BLOCKS_TYPE] = pr_free_ext, + [SCOUTFS_NODE_ZONE][SCOUTFS_FREE_BITS_SEGNO_TYPE] = pr_free_bits, + [SCOUTFS_NODE_ZONE][SCOUTFS_FREE_BITS_BLKNO_TYPE] = pr_free_bits, [SCOUTFS_NODE_ZONE][SCOUTFS_ORPHAN_TYPE] = pr_orphan, [SCOUTFS_FS_ZONE][SCOUTFS_INODE_TYPE] = pr_inode, [SCOUTFS_FS_ZONE][SCOUTFS_XATTR_TYPE] = pr_xattr, @@ -322,7 +317,7 @@ const static key_printer_t key_printers[SCOUTFS_MAX_ZONE][SCOUTFS_MAX_TYPE] = { [SCOUTFS_FS_ZONE][SCOUTFS_READDIR_TYPE] = pr_readdir, [SCOUTFS_FS_ZONE][SCOUTFS_LINK_BACKREF_TYPE] = pr_link_backref, [SCOUTFS_FS_ZONE][SCOUTFS_SYMLINK_TYPE] = pr_symlink, - [SCOUTFS_FS_ZONE][SCOUTFS_FILE_EXTENT_TYPE] = pr_file_ext, + [SCOUTFS_FS_ZONE][SCOUTFS_BLOCK_MAPPING_TYPE] = pr_block_mapping, }; /* @@ -356,8 +351,8 @@ static int scoutfs_key_str_size(char *buf, struct scoutfs_key_buf *key, struct scoutfs_inode_index_key *ikey = key->data; type = ikey->type; } else if (zone == SCOUTFS_NODE_ZONE) { - struct scoutfs_free_extent_blkno_key *fkey = key->data; - type = fkey->type; + struct scoutfs_free_bits_key *fbk = key->data; + type = fbk->type; } else if (zone == SCOUTFS_FS_ZONE) { struct scoutfs_inode_key *ikey = key->data; type = ikey->type; diff --git a/utils/src/print.c b/utils/src/print.c index 08be009b..e5aba85b 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -190,48 +190,31 @@ static void print_symlink(void *key, int key_len, void *val, int val_len) } /* - * Just print the calculated starting blk_off/blkno, we can add a flag - * to print the raw values before the math if needed. + * XXX not decoding the bytes yet */ -static void print_file_extent(void *key, int key_len, void *val, int val_len) +static void print_block_mapping(void *key, int key_len, void *val, int val_len) { - struct scoutfs_file_extent_key *fext = key; - u64 blocks = be64_to_cpu(fext->blocks); - u64 blk_off = be64_to_cpu(fext->last_blk_off) - blocks + 1; - u64 blkno = be64_to_cpu(fext->last_blkno) - blocks + 1; + struct scoutfs_block_mapping_key *bmk = key; + u64 blk_off = be64_to_cpu(bmk->base) << SCOUTFS_BLOCK_MAPPING_SHIFT; + u8 nr = *((u8 *)val) & 63; - printf(" extent: ino %llu blk_off %llu blkno %llu blocks %llu " - "flags %x (%c)\n", - be64_to_cpu(fext->ino), blk_off, blkno, blocks, fext->flags, - (fext->flags & SCOUTFS_FILE_EXTENT_OFFLINE) ? 'O' : '-'); + printf(" block mapping: ino %llu blk_off %llu blocks %u\n", + be64_to_cpu(bmk->ino), blk_off, nr); } -static void print_free_extent(void *key, int key_len, void *val, int val_len) +static void print_free_bits(void *key, int key_len, void *val, int val_len) { - struct scoutfs_free_extent_blkno_key *blk = key; - struct scoutfs_free_extent_blocks_key *bks = key; - u64 last_blkno; - u64 node_id; - u64 blocks; - u64 blkno; - char *str; + struct scoutfs_free_bits_key *fbk = key; + struct scoutfs_free_bits *frb = val; + int i; - if (blk->type == SCOUTFS_FREE_EXTENT_BLKNO_TYPE) { - str = "free (blkno)"; - node_id = be64_to_cpu(blk->node_id); - last_blkno = be64_to_cpu(blk->last_blkno); - blocks = be64_to_cpu(blk->blocks); - } else { - str = "free (blocks)"; - node_id = be64_to_cpu(bks->node_id); - last_blkno = be64_to_cpu(bks->last_blkno); - blocks = be64_to_cpu(bks->blocks); - } + printf(" node_id %llx base %llu\n", + be64_to_cpu(fbk->node_id), be64_to_cpu(fbk->base)); - blkno = last_blkno - blocks + 1; - - printf(" %s: node_id %llx blkno %llu blocks %llu\n", - str, node_id, blkno, blocks); + printf(" bits:"); + for (i = 0; i < array_size(frb->bits); i++) + printf(" %016llx", le64_to_cpu(frb->bits[i])); + printf("\n"); } static void print_inode_index(void *key, int key_len, void *val, int val_len) @@ -253,9 +236,9 @@ static print_func_t find_printer(u8 zone, u8 type) return print_inode_index; if (zone == SCOUTFS_NODE_ZONE) { - if (type >= SCOUTFS_FREE_EXTENT_BLKNO_TYPE && - type <= SCOUTFS_FREE_EXTENT_BLOCKS_TYPE) - return print_free_extent; + if (type == SCOUTFS_FREE_BITS_SEGNO_TYPE || + type == SCOUTFS_FREE_BITS_BLKNO_TYPE) + return print_free_bits; if (type == SCOUTFS_ORPHAN_TYPE) return print_orphan; } @@ -268,7 +251,8 @@ static print_func_t find_printer(u8 zone, u8 type) case SCOUTFS_READDIR_TYPE: return print_readdir; case SCOUTFS_SYMLINK_TYPE: return print_symlink; case SCOUTFS_LINK_BACKREF_TYPE: return print_link_backref; - case SCOUTFS_FILE_EXTENT_TYPE: return print_file_extent; + case SCOUTFS_BLOCK_MAPPING_TYPE: + return print_block_mapping; } } From f02944bd73151444b1f1a56b4084ddbfcf4b777c Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Wed, 4 Oct 2017 11:10:03 -0700 Subject: [PATCH 109/235] scoutfs-utils: update inode index item types Signed-off-by: Zach Brown --- utils/src/format.h | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index b435bfb7..a4cd7515 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -244,12 +244,10 @@ struct scoutfs_segment_block { #define SCOUTFS_MAX_ZONE 4 /* power of 2 is efficient */ /* inode index zone */ -#define SCOUTFS_INODE_INDEX_SIZE_TYPE 3 -#define SCOUTFS_INODE_INDEX_META_SEQ_TYPE 4 -#define SCOUTFS_INODE_INDEX_DATA_SEQ_TYPE 5 - -#define SCOUTFS_INODE_INDEX_NR \ - (SCOUTFS_INODE_INDEX_DATA_SEQ_TYPE - SCOUTFS_INODE_INDEX_SIZE_TYPE + 1) +#define SCOUTFS_INODE_INDEX_SIZE_TYPE 1 +#define SCOUTFS_INODE_INDEX_META_SEQ_TYPE 2 +#define SCOUTFS_INODE_INDEX_DATA_SEQ_TYPE 3 +#define SCOUTFS_INODE_INDEX_NR 4 /* don't forget to update */ /* node zone */ #define SCOUTFS_FREE_BITS_SEGNO_TYPE 1 From 362fc0ab62cc73eebde1e9bba71fd9de5025a33b Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Wed, 11 Oct 2017 10:43:57 -0700 Subject: [PATCH 110/235] scoutfs-utils: update format.h The kernel format.h has built up some changes that the userspace utils don't use. We're about to start enforcing exact matching of the source files at run time so let's bring these back in sync. Signed-off-by: Zach Brown --- utils/src/format.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/utils/src/format.h b/utils/src/format.h index a4cd7515..3093150b 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -538,6 +538,7 @@ enum { #define SCOUTFS_LOCK_SCOPE_FS_ITEMS 2 #define SCOUTFS_LOCK_TYPE_GLOBAL_RENAME 1 +#define SCOUTFS_LOCK_TYPE_GLOBAL_SERVER 2 struct scoutfs_lock_name { __u8 scope; @@ -551,6 +552,8 @@ struct scoutfs_lock_name { #define SCOUTFS_LOCK_INODE_GROUP_MASK (SCOUTFS_LOCK_INODE_GROUP_NR - 1) #define SCOUTFS_LOCK_INODE_GROUP_OFFSET (~0ULL) +#define SCOUTFS_LOCK_SEQ_GROUP_MASK ((1ULL << 10) - 1) + /* * messages over the wire. */ From 34fc095392cda7be4ff57874789a8f0f581ae685 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Wed, 11 Oct 2017 10:52:39 -0700 Subject: [PATCH 111/235] scoutfs-utils: update btree ring calc Update the calculation of the largest number of btree blocks based on the format.h update that provides the min free space in parent blocks instead of the free limit for the entire block. Signed-off-by: Zach Brown --- utils/src/format.h | 17 ++++++++++++----- utils/src/mkfs.c | 6 ++++-- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index 3093150b..9bc496b3 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -60,16 +60,23 @@ struct scoutfs_block_header { /* level 0 segments can have two full keys in the value :/ */ #define SCOUTFS_BTREE_MAX_VAL_LEN 768 +/* + * The min number of free bytes we must leave in a parent as we descend + * to modify. This leaves enough free bytes to insert a possibly maximal + * sized key as a seperator for a child block. Fewer bytes then this + * and split/merge might try to insert a max child item in the parent + * that wouldn't fit. + */ +#define SCOUTFS_BTREE_PARENT_MIN_FREE_BYTES \ + (sizeof(struct scoutfs_btree_item_header) + \ + sizeof(struct scoutfs_btree_item) + SCOUTFS_BTREE_MAX_KEY_LEN +\ + sizeof(struct scoutfs_btree_ref)) + /* * A 4EB test image measured a worst case height of 17. This is plenty * generous. */ #define SCOUTFS_BTREE_MAX_HEIGHT 20 - -/* btree blocks (beyond the first) need to be at least half full */ -#define SCOUTFS_BTREE_FREE_LIMIT \ - ((SCOUTFS_BLOCK_SIZE - sizeof(struct scoutfs_btree_block)) / 2) - #define SCOUTFS_BTREE_BITS 8 /* diff --git a/utils/src/mkfs.c b/utils/src/mkfs.c index 943ffd8d..4a277c3b 100644 --- a/utils/src/mkfs.c +++ b/utils/src/mkfs.c @@ -68,13 +68,15 @@ static u64 calc_btree_blocks(u64 nr, u64 max_key, u64 max_val) item_bytes = sizeof(struct scoutfs_btree_item_header) + sizeof(struct scoutfs_btree_item) + max_key + sizeof(struct scoutfs_btree_ref); - fanout = (SCOUTFS_BLOCK_SIZE - SCOUTFS_BTREE_FREE_LIMIT) / item_bytes; + fanout = ((SCOUTFS_BLOCK_SIZE - sizeof(struct scoutfs_btree_block) - + SCOUTFS_BTREE_PARENT_MIN_FREE_BYTES) / 2) / item_bytes; /* figure out how many items we have to store */ item_bytes = sizeof(struct scoutfs_btree_item_header) + sizeof(struct scoutfs_btree_item) + max_key + max_val; - block_items = (SCOUTFS_BLOCK_SIZE - SCOUTFS_BTREE_FREE_LIMIT) / item_bytes; + block_items = ((SCOUTFS_BLOCK_SIZE - + sizeof(struct scoutfs_btree_block)) / 2) / item_bytes; leaf_blocks = DIV_ROUND_UP(nr, block_items); /* then calc total blocks as we grow to have enough blocks for items */ From b3d11925c7d961f4d631d6ebfa87dbc1d05535f1 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Wed, 11 Oct 2017 10:57:54 -0700 Subject: [PATCH 112/235] scoutfs-utils: add support for format_hash Calculate the hash of format.h and ioctl.h, put it in the super during mkfs, and print it out. Signed-off-by: Zach Brown --- utils/Makefile | 6 +++++- utils/src/format.h | 1 + utils/src/mkfs.c | 3 +++ utils/src/print.c | 6 ++++-- 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/utils/Makefile b/utils/Makefile index 95cd9434..39cd7202 100644 --- a/utils/Makefile +++ b/utils/Makefile @@ -1,5 +1,9 @@ +SCOUTFS_FORMAT_HASH := \ + $(shell cat src/format.h src/ioctl.h | md5sum | cut -b1-16) + CFLAGS := -Wall -O2 -Werror -D_FILE_OFFSET_BITS=64 -g -msse4.2 \ - -fno-strict-aliasing + -fno-strict-aliasing \ + -DSCOUTFS_FORMAT_HASH=0x$(SCOUTFS_FORMAT_HASH)LLU BIN := src/scoutfs OBJ := $(patsubst %.c,%.o,$(wildcard src/*.c)) diff --git a/utils/src/format.h b/utils/src/format.h index 9bc496b3..d9be949b 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -430,6 +430,7 @@ struct scoutfs_inet_addr { struct scoutfs_super_block { struct scoutfs_block_header hdr; __le64 id; + __le64 format_hash; __u8 uuid[SCOUTFS_UUID_BYTES]; __le64 next_ino; __le64 next_seq; diff --git a/utils/src/mkfs.c b/utils/src/mkfs.c index 4a277c3b..add474f7 100644 --- a/utils/src/mkfs.c +++ b/utils/src/mkfs.c @@ -208,6 +208,7 @@ static int write_new_fs(char *path, int fd) pseudo_random_bytes(&super->hdr.fsid, sizeof(super->hdr.fsid)); super->hdr.seq = cpu_to_le64(1); super->id = cpu_to_le64(SCOUTFS_SUPER_ID); + super->format_hash = cpu_to_le64(SCOUTFS_FORMAT_HASH); uuid_generate(super->uuid); super->next_ino = cpu_to_le64(SCOUTFS_ROOT_INO + 1); super->next_seq = cpu_to_le64(1); @@ -369,12 +370,14 @@ static int write_new_fs(char *path, int fd) printf("Created scoutfs filesystem:\n" " device path: %s\n" " fsid: %llx\n" + " format hash: %llx\n" " uuid: %s\n" " device bytes: "SIZE_FMT"\n" " btree ring blocks: "SIZE_FMT"\n" " usable segments: "SIZE_FMT"\n", path, le64_to_cpu(super->hdr.fsid), + le64_to_cpu(super->format_hash), uuid_str, SIZE_ARGS(size, 1), SIZE_ARGS(le64_to_cpu(super->bring.nr_blocks), diff --git a/utils/src/print.c b/utils/src/print.c index e5aba85b..14913067 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -551,8 +551,10 @@ static void print_super_block(struct scoutfs_super_block *super, u64 blkno) printf("super blkno %llu\n", blkno); print_block_header(&super->hdr); - printf(" id %llx uuid %s\n", - le64_to_cpu(super->id), uuid_str); + printf(" id %llx format_hash %llx\n" + " uuid %s\n", + le64_to_cpu(super->id), le64_to_cpu(super->format_hash), + uuid_str); /* XXX these are all in a crazy order */ printf(" next_ino %llu next_seq %llu next_seg_seq %llu\n" From 4ab22d8f09879f00aec032adc05a15fff7d3b9c4 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Wed, 11 Oct 2017 11:28:04 -0700 Subject: [PATCH 113/235] scoutfs-utils: update format for net greeting Signed-off-by: Zach Brown --- utils/src/format.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/utils/src/format.h b/utils/src/format.h index d9be949b..ffe5ce44 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -566,6 +566,11 @@ struct scoutfs_lock_name { * messages over the wire. */ +struct scoutfs_net_greeting { + __le64 fsid; + __le64 format_hash; +} __packed; + /* * This header precedes and describes all network messages sent over * sockets. The id is set by the request and sent in the reply. The From 0acab247e3fb18e1c2d4676db3d6aeccc9c5003d Mon Sep 17 00:00:00 2001 From: Mark Fasheh Date: Wed, 11 Oct 2017 16:06:33 -0500 Subject: [PATCH 114/235] scoutfs-utils: update scoutfs_inode definition We need the flags field from -kmod. Signed-off-by: Mark Fasheh --- utils/src/format.h | 3 +++ utils/src/print.c | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/utils/src/format.h b/utils/src/format.h index ffe5ce44..e30a1320 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -483,11 +483,14 @@ struct scoutfs_inode { __le32 gid; __le32 mode; __le32 rdev; + __le32 flags; struct scoutfs_timespec atime; struct scoutfs_timespec ctime; struct scoutfs_timespec mtime; } __packed; +#define SCOUTFS_INO_FLAG_TRUNCATE 0x1 + #define SCOUTFS_ROOT_INO 1 /* like the block size, a reasonable min PATH_MAX across platforms */ diff --git a/utils/src/print.c b/utils/src/print.c index 14913067..29338454 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -80,7 +80,7 @@ static void print_inode(void *key, int key_len, void *val, int val_len) struct scoutfs_inode *inode = val; printf(" inode: ino %llu size %llu blocks %llu nlink %u\n" - " uid %u gid %u mode 0%o rdev 0x%x\n" + " uid %u gid %u mode 0%o rdev 0x%x flags 0x%x\n" " next_readdir_pos %llu meta_seq %llu data_seq %llu data_version %llu\n" " atime %llu.%08u ctime %llu.%08u\n" " mtime %llu.%08u\n", @@ -89,6 +89,7 @@ static void print_inode(void *key, int key_len, void *val, int val_len) le32_to_cpu(inode->nlink), le32_to_cpu(inode->uid), le32_to_cpu(inode->gid), le32_to_cpu(inode->mode), le32_to_cpu(inode->rdev), + le32_to_cpu(inode->flags), le64_to_cpu(inode->next_readdir_pos), le64_to_cpu(inode->meta_seq), le64_to_cpu(inode->data_seq), From 80e0c4bd56925c77a8f19c8abe7c9ebc4038c957 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Tue, 24 Oct 2017 15:41:30 -0700 Subject: [PATCH 115/235] scoutfs-utils: add support for btree migration key Signed-off-by: Zach Brown --- utils/src/format.h | 5 +++++ utils/src/print.c | 8 +++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index e30a1320..3c78a8dd 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -103,10 +103,15 @@ struct scoutfs_btree_ref { /* * A height of X means that the first block read will have level X-1 and * the leaves will have level 0. + * + * The migration key is used to walk the tree finding old blocks to migrate + * into the current half of the ring. */ struct scoutfs_btree_root { struct scoutfs_btree_ref ref; __u8 height; + __le16 migration_key_len; + __u8 migration_key[SCOUTFS_BTREE_MAX_KEY_LEN]; } __packed; struct scoutfs_btree_item_header { diff --git a/utils/src/print.c b/utils/src/print.c index 29338454..ebf22dc9 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -562,8 +562,8 @@ static void print_super_block(struct scoutfs_super_block *super, u64 blkno) " alloc_uninit %llu total_segs %llu free_segs %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\n" - " manifest btree root: height %u blkno %llu seq %llu\n", + " alloc btree root: height %u blkno %llu seq %llu mig_len %u\n" + " manifest btree root: height %u blkno %llu seq %llu mig_len %u\n", le64_to_cpu(super->next_ino), le64_to_cpu(super->next_seq), le64_to_cpu(super->next_seg_seq), @@ -577,9 +577,11 @@ static void print_super_block(struct scoutfs_super_block *super, u64 blkno) super->alloc_root.height, le64_to_cpu(super->alloc_root.ref.blkno), le64_to_cpu(super->alloc_root.ref.seq), + le16_to_cpu(super->alloc_root.migration_key_len), super->manifest.root.height, le64_to_cpu(super->manifest.root.ref.blkno), - le64_to_cpu(super->manifest.root.ref.seq)); + le64_to_cpu(super->manifest.root.ref.seq), + le16_to_cpu(super->manifest.root.migration_key_len)); printf(" level_counts:"); counts = super->manifest.level_counts; From 0876fb31c6eeb1d30ee3526caf1e5975479fa635 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Tue, 24 Oct 2017 16:55:06 -0700 Subject: [PATCH 116/235] scoutfs-utils: remove btree item bit augmentation We no longer need the complexity of augmenting the btree to find items with bits set. Signed-off-by: Zach Brown --- utils/src/format.h | 19 ------------------- utils/src/print.c | 14 +++----------- 2 files changed, 3 insertions(+), 30 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index 3c78a8dd..c0e854d5 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -77,23 +77,6 @@ struct scoutfs_block_header { * generous. */ #define SCOUTFS_BTREE_MAX_HEIGHT 20 -#define SCOUTFS_BTREE_BITS 8 - -/* - * Btree items can have bits associated with them. Their parent items - * reflect all the bits that their child block contain. Thus searches - * can find items with bits set. - * - * @SCOUTFS_BTREE_BIT_HALF1: Tracks blocks found in the first half of - * the ring. It's used to migrate blocks from the old half of the ring - * into the current half as blocks are dirtied. It's not found in leaf - * items but is calculated based on the block number of referenced - * blocks. _HALF2 is identical but for the second half of the ring. - */ -enum { - SCOUTFS_BTREE_BIT_HALF1 = (1 << 0), - SCOUTFS_BTREE_BIT_HALF2 = (1 << 1), -}; struct scoutfs_btree_ref { __le64 blkno; @@ -116,7 +99,6 @@ struct scoutfs_btree_root { struct scoutfs_btree_item_header { __le16 off; - __u8 bits; } __packed; struct scoutfs_btree_item { @@ -134,7 +116,6 @@ struct scoutfs_btree_block { __le16 free_end; __le16 free_reclaim; __le16 nr_items; - __le16 bit_counts[SCOUTFS_BTREE_BITS]; __u8 level; struct scoutfs_btree_item_header item_hdrs[0]; } __packed; diff --git a/utils/src/print.c b/utils/src/print.c index ebf22dc9..3020ed79 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -468,8 +468,7 @@ static int print_btree_block(int fd, struct scoutfs_super_block *super, if (bt->level == level) { printf("%s btree blkno %llu\n" " fsid %llx blkno %llu seq %llu crc %08x \n" - " level %u free_end %u free_reclaim %u nr_items %u\n" - " bit_counts:", + " level %u free_end %u free_reclaim %u nr_items %u\n", which, le64_to_cpu(ref->blkno), le64_to_cpu(bt->fsid), le64_to_cpu(bt->blkno), @@ -479,12 +478,6 @@ static int print_btree_block(int fd, struct scoutfs_super_block *super, le16_to_cpu(bt->free_end), le16_to_cpu(bt->free_reclaim), le16_to_cpu(bt->nr_items)); - for (i = 0; i < array_size(bt->bit_counts); i++) { - if (bt->bit_counts[i]) - printf(" %u:%u", - i, le16_to_cpu(bt->bit_counts[i])); - } - printf("\n"); } for (i = 0; i < le16_to_cpu(bt->nr_items); i++) { @@ -506,9 +499,8 @@ static int print_btree_block(int fd, struct scoutfs_super_block *super, continue; } - printf(" item [%u] off %u bits %02x key_len %u val_len %u\n", - i, le16_to_cpu(bt->item_hdrs[i].off), - bt->item_hdrs[i].bits, key_len, val_len); + printf(" item [%u] off %u key_len %u val_len %u\n", + i, le16_to_cpu(bt->item_hdrs[i].off), key_len, val_len); if (level) print_btree_ref(key, key_len, val, val_len, func, arg); From 7df8b87128ac10602fa966582b17079f21b85f13 Mon Sep 17 00:00:00 2001 From: Mark Fasheh Date: Fri, 17 Nov 2017 22:24:28 -0600 Subject: [PATCH 117/235] scoutfs-utils: cmd_register - pass a parsing friendly argv We were chopping off the command string when passing the argument array into registered commands. getopt expects a program name as the first argument, so change cmd_execute() to only chop off the scoutfs program name now. Now we can parse command arguments in an easy and standard manner. This necessitates a small update of each commands usage of argv/argc. Signed-off-by: Mark Fasheh --- utils/src/cmd.c | 2 +- utils/src/ctrstat.c | 8 ++--- utils/src/ino_path.c | 10 +++--- utils/src/item-cache-keys.c | 6 ++-- utils/src/mkfs.c | 4 +-- utils/src/print.c | 4 +-- utils/src/stage_release.c | 64 ++++++++++++++++++------------------- utils/src/stat.c | 4 +-- utils/src/walk_inodes.c | 32 +++++++++---------- 9 files changed, 67 insertions(+), 67 deletions(-) diff --git a/utils/src/cmd.c b/utils/src/cmd.c index e723f859..92e799e5 100644 --- a/utils/src/cmd.c +++ b/utils/src/cmd.c @@ -76,7 +76,7 @@ int cmd_execute(int argc, char **argv) return 1; } - ret = com->func(argc - 2, argv + 2); + ret = com->func(argc - 1, argv + 1); if (ret < 0) { fprintf(stderr, "scoutfs: %s failed: %s (%d)\n", com->name, strerror(-ret), -ret); diff --git a/utils/src/ctrstat.c b/utils/src/ctrstat.c index cfad0740..1e30f090 100644 --- a/utils/src/ctrstat.c +++ b/utils/src/ctrstat.c @@ -129,16 +129,16 @@ static int ctrstat_cmd(int argc, char **argv) int iter; int ret; - if (argc > 1) { + if (argc > 2) { printf("scoutfs ctrstat: too many arguments\n"); return -EINVAL; } /* set the sleep duration */ - if (argc == 1) { - seconds = strtof(argv[0], NULL); + if (argc == 2) { + seconds = strtof(argv[1], NULL); if (fpclassify(seconds) != FP_NORMAL || seconds <= 0) { - printf("invalid sleep duration float: %s\n", argv[0]); + printf("invalid sleep duration float: %s\n", argv[1]); return -EINVAL; } } diff --git a/utils/src/ino_path.c b/utils/src/ino_path.c index 36fe0535..f23f3c7c 100644 --- a/utils/src/ino_path.c +++ b/utils/src/ino_path.c @@ -25,24 +25,24 @@ static int ino_path_cmd(int argc, char **argv) int ret; int fd; - if (argc != 2) { + if (argc != 3) { fprintf(stderr, "must specify ino and path\n"); return -EINVAL; } - ino = strtoull(argv[0], &endptr, 0); + ino = strtoull(argv[1], &endptr, 0); if (*endptr != '\0' || ((ino == LLONG_MIN || ino == LLONG_MAX) && errno == ERANGE)) { fprintf(stderr, "error parsing inode number '%s'\n", - argv[0]); + argv[1]); return -EINVAL; } - fd = open(argv[1], O_RDONLY); + fd = open(argv[2], O_RDONLY); if (fd < 0) { ret = -errno; fprintf(stderr, "failed to open '%s': %s (%d)\n", - argv[1], strerror(errno), errno); + argv[2], strerror(errno), errno); return ret; } diff --git a/utils/src/item-cache-keys.c b/utils/src/item-cache-keys.c index 765f77d8..704a8cc4 100644 --- a/utils/src/item-cache-keys.c +++ b/utils/src/item-cache-keys.c @@ -28,7 +28,7 @@ static int item_cache_keys(int argc, char **argv, int which) int ret; int fd; - if (argc != 1) { + if (argc != 2) { fprintf(stderr, "too many arguments, only scoutfs path needed"); return -EINVAL; } @@ -41,11 +41,11 @@ static int item_cache_keys(int argc, char **argv, int which) return ret; } - fd = open(argv[0], O_RDONLY); + fd = open(argv[1], O_RDONLY); if (fd < 0) { ret = -errno; fprintf(stderr, "failed to open '%s': %s (%d)\n", - argv[0], strerror(errno), errno); + argv[1], strerror(errno), errno); free(buf); return ret; } diff --git a/utils/src/mkfs.c b/utils/src/mkfs.c index add474f7..86543c10 100644 --- a/utils/src/mkfs.c +++ b/utils/src/mkfs.c @@ -398,11 +398,11 @@ out: static int mkfs_func(int argc, char *argv[]) { - char *path = argv[0]; + char *path = argv[1]; int ret; int fd; - if (argc != 1) { + if (argc != 2) { printf("scoutfs: mkfs: a single path argument is required\n"); return -EINVAL; } diff --git a/utils/src/print.c b/utils/src/print.c index 3020ed79..3007c063 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -643,11 +643,11 @@ static int print_cmd(int argc, char **argv) int ret; int fd; - if (argc != 1) { + if (argc != 2) { printf("scoutfs print: a single path argument is required\n"); return -EINVAL; } - path = argv[0]; + path = argv[1]; fd = open(path, O_RDONLY); if (fd < 0) { diff --git a/utils/src/stage_release.c b/utils/src/stage_release.c index 1efbaa7d..a7ee719d 100644 --- a/utils/src/stage_release.c +++ b/utils/src/stage_release.c @@ -26,42 +26,42 @@ static int stage_cmd(int argc, char **argv) u64 vers; int ret; - if (argc != 5) { + if (argc != 6) { fprintf(stderr, "must specify moar args\n"); return -EINVAL; } - fd = open(argv[0], O_RDWR); + fd = open(argv[1], O_RDWR); if (fd < 0) { ret = -errno; fprintf(stderr, "failed to open '%s': %s (%d)\n", - argv[0], strerror(errno), errno); + argv[1], strerror(errno), errno); return ret; } - vers = strtoull(argv[1], &endptr, 0); + vers = strtoull(argv[2], &endptr, 0); if (*endptr != '\0' || ((vers == LLONG_MIN || vers == LLONG_MAX) && errno == ERANGE)) { fprintf(stderr, "error parsing data version '%s'\n", - argv[1]); - ret = -EINVAL; - goto out; - } - - offset = strtoull(argv[2], &endptr, 0); - if (*endptr != '\0' || - ((offset == LLONG_MIN || offset == LLONG_MAX) && errno == ERANGE)) { - fprintf(stderr, "error parsing offset '%s'\n", argv[2]); ret = -EINVAL; goto out; } - count = strtoull(argv[3], &endptr, 0); + offset = strtoull(argv[3], &endptr, 0); + if (*endptr != '\0' || + ((offset == LLONG_MIN || offset == LLONG_MAX) && errno == ERANGE)) { + fprintf(stderr, "error parsing offset '%s'\n", + argv[3]); + ret = -EINVAL; + goto out; + } + + count = strtoull(argv[4], &endptr, 0); if (*endptr != '\0' || ((count == LLONG_MIN || count == LLONG_MAX) && errno == ERANGE)) { fprintf(stderr, "error parsing count '%s'\n", - argv[3]); + argv[4]); ret = -EINVAL; goto out; } @@ -73,11 +73,11 @@ static int stage_cmd(int argc, char **argv) goto out; } - afd = open(argv[4], O_RDONLY); + afd = open(argv[5], O_RDONLY); if (afd < 0) { ret = -errno; fprintf(stderr, "failed to open '%s': %s (%d)\n", - argv[4], strerror(errno), errno); + argv[5], strerror(errno), errno); goto out; } @@ -133,42 +133,42 @@ static int release_cmd(int argc, char **argv) int ret; int fd; - if (argc != 4) { + if (argc != 5) { fprintf(stderr, "must specify path, data version, offset, and count\n"); return -EINVAL; } - fd = open(argv[0], O_RDWR); + fd = open(argv[1], O_RDWR); if (fd < 0) { ret = -errno; fprintf(stderr, "failed to open '%s': %s (%d)\n", - argv[0], strerror(errno), errno); + argv[1], strerror(errno), errno); return ret; } - vers = strtoull(argv[1], &endptr, 0); + vers = strtoull(argv[2], &endptr, 0); if (*endptr != '\0' || ((vers == LLONG_MIN || vers == LLONG_MAX) && errno == ERANGE)) { fprintf(stderr, "error parsing data version '%s'\n", - argv[1]); - ret = -EINVAL; - goto out; - } - - block = strtoull(argv[2], &endptr, 0); - if (*endptr != '\0' || - ((block == LLONG_MIN || block == LLONG_MAX) && errno == ERANGE)) { - fprintf(stderr, "error parsing starting 4K block offset '%s'\n", argv[2]); ret = -EINVAL; goto out; } - count = strtoull(argv[3], &endptr, 0); + block = strtoull(argv[3], &endptr, 0); + if (*endptr != '\0' || + ((block == LLONG_MIN || block == LLONG_MAX) && errno == ERANGE)) { + fprintf(stderr, "error parsing starting 4K block offset '%s'\n", + argv[3]); + ret = -EINVAL; + goto out; + } + + count = strtoull(argv[4], &endptr, 0); if (*endptr != '\0' || ((count == LLONG_MIN || count == LLONG_MAX) && errno == ERANGE)) { fprintf(stderr, "error parsing length '%s'\n", - argv[3]); + argv[4]); ret = -EINVAL; goto out; } diff --git a/utils/src/stat.c b/utils/src/stat.c index b584441b..f9f4f569 100644 --- a/utils/src/stat.c +++ b/utils/src/stat.c @@ -22,12 +22,12 @@ static int stat_more_cmd(int argc, char **argv) int fd; int i; - if (argc == 0) { + if (argc == 1) { fprintf(stderr, "must specify at least one path argument\n"); return -EINVAL; } - for (i = 0; i < argc; i++) { + for (i = 1; i < argc; i++) { path = argv[i]; fd = open(path, O_RDONLY); diff --git a/utils/src/walk_inodes.c b/utils/src/walk_inodes.c index 91378ef3..c6b0bc99 100644 --- a/utils/src/walk_inodes.c +++ b/utils/src/walk_inodes.c @@ -75,44 +75,44 @@ static int walk_inodes_cmd(int argc, char **argv) int fd; int i; - if (argc != 4) { + if (argc != 5) { fprintf(stderr, "must specify seq and path\n"); return -EINVAL; } - if (!strcasecmp(argv[0], "size")) + if (!strcasecmp(argv[1], "size")) walk.index = SCOUTFS_IOC_WALK_INODES_SIZE; - else if (!strcasecmp(argv[0], "meta_seq")) + else if (!strcasecmp(argv[1], "meta_seq")) walk.index = SCOUTFS_IOC_WALK_INODES_META_SEQ; - else if (!strcasecmp(argv[0], "data_seq")) + else if (!strcasecmp(argv[1], "data_seq")) walk.index = SCOUTFS_IOC_WALK_INODES_DATA_SEQ; else { fprintf(stderr, "unknown index '%s', try 'size', 'ctime, or " - "mtime'\n", argv[0]); + "mtime'\n", argv[1]); return -EINVAL; } - ret = parse_walk_entry(&walk.first, argv[1]); + ret = parse_walk_entry(&walk.first, argv[2]); if (ret) { fprintf(stderr, "invalid first position '%s', try '1.2.3' or " - "'-1'\n", argv[1]); - return -EINVAL; - - } - - ret = parse_walk_entry(&walk.last, argv[2]); - if (ret) { - fprintf(stderr, "invalid last position '%s', try '1.2.3' or " "'-1'\n", argv[2]); return -EINVAL; } - fd = open(argv[3], O_RDONLY); + ret = parse_walk_entry(&walk.last, argv[3]); + if (ret) { + fprintf(stderr, "invalid last position '%s', try '1.2.3' or " + "'-1'\n", argv[3]); + return -EINVAL; + + } + + fd = open(argv[4], O_RDONLY); if (fd < 0) { ret = -errno; fprintf(stderr, "failed to open '%s': %s (%d)\n", - argv[3], strerror(errno), errno); + argv[4], strerror(errno), errno); return ret; } From 3ecc099589be6d25f7a55b4be81f88426871db15 Mon Sep 17 00:00:00 2001 From: Mark Fasheh Date: Thu, 16 Nov 2017 15:19:47 -0600 Subject: [PATCH 118/235] scoutfs-utils: add command to print locking state This command takes a device and dumps all dlmglue locks and their state to the console. It also computes some average lock wait times. We provide a couple of options: --lvbs=[yes|no] turns on or off printing of lvb data (default is off) --oneline provides a more concise per-lock printout. Signed-off-by: Mark Fasheh --- utils/src/dlmglue.h | 101 +++++++++++ utils/src/locks.c | 420 ++++++++++++++++++++++++++++++++++++++++++++ utils/src/print.c | 2 +- utils/src/util.h | 6 + 4 files changed, 528 insertions(+), 1 deletion(-) create mode 100644 utils/src/dlmglue.h create mode 100644 utils/src/locks.c diff --git a/utils/src/dlmglue.h b/utils/src/dlmglue.h new file mode 100644 index 00000000..985d65f9 --- /dev/null +++ b/utils/src/dlmglue.h @@ -0,0 +1,101 @@ +/* -*- mode: c; c-basic-offset: 8; -*- + * vim: noexpandtab sw=8 ts=8 sts=0: + * + * dlmglue.h + * + * dlmglue constants for userspace decoding + * + * Copyright (C) 2002, 2004 Oracle. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 021110-1307, USA. + */ + + +#ifndef DLMGLUE_H +#define DLMGLUE_H + +/* Max length of lockid name */ +#define OCFS2_LOCK_ID_MAX_LEN 32 + +#define DLM_LVB_LEN 64 + +enum ocfs2_ast_action { + OCFS2_AST_INVALID = 0, + OCFS2_AST_ATTACH, + OCFS2_AST_CONVERT, + OCFS2_AST_DOWNCONVERT, +}; + +/* actions for an unlockast function to take. */ +enum ocfs2_unlock_action { + OCFS2_UNLOCK_INVALID = 0, + OCFS2_UNLOCK_CANCEL_CONVERT, + OCFS2_UNLOCK_DROP_LOCK, +}; + +/* ocfs2_lock_res->l_flags flags. */ +#define OCFS2_LOCK_ATTACHED (0x00000001) /* we have initialized + * the lvb */ +#define OCFS2_LOCK_BUSY (0x00000002) /* we are currently in + * dlm_lock */ +#define OCFS2_LOCK_BLOCKED (0x00000004) /* blocked waiting to + * downconvert*/ +#define OCFS2_LOCK_LOCAL (0x00000008) /* newly created inode */ +#define OCFS2_LOCK_NEEDS_REFRESH (0x00000010) +#define OCFS2_LOCK_REFRESHING (0x00000020) +#define OCFS2_LOCK_INITIALIZED (0x00000040) /* track initialization + * for shutdown paths */ +#define OCFS2_LOCK_FREEING (0x00000080) /* help dlmglue track + * when to skip queueing + * a lock because it's + * about to be + * dropped. */ +#define OCFS2_LOCK_QUEUED (0x00000100) /* queued for downconvert */ +#define OCFS2_LOCK_NOCACHE (0x00000200) /* don't use a holder count */ +#define OCFS2_LOCK_PENDING (0x00000400) /* This lockres is pending a + call to dlm_lock. Only + exists with BUSY set. */ +#define OCFS2_LOCK_UPCONVERT_FINISHING (0x00000800) /* blocks the dc thread + * from downconverting + * before the upconvert + * has completed */ + +#define OCFS2_LOCK_NONBLOCK_FINISHED (0x00001000) /* NONBLOCK cluster + * lock has already + * returned, do not block + * dc thread from + * downconverting */ + +/* The cluster stack fields */ +#define OCFS2_STACK_LABEL_LEN 4 +#define OCFS2_CLUSTER_NAME_LEN 16 + +/* + * Return value from ->downconvert_worker functions. + * + * These control the precise actions of ocfs2_unblock_lock() + * and ocfs2_process_blocked_lock() + * + */ +enum ocfs2_unblock_action { + UNBLOCK_CONTINUE = 0, /* Continue downconvert */ + UNBLOCK_CONTINUE_POST = 1, /* Continue downconvert, fire + * ->post_unlock callback */ + UNBLOCK_STOP_POST = 2, /* Do not downconvert, fire + * ->post_unlock() callback. */ +}; + +#endif /* DLMGLUE_H */ diff --git a/utils/src/locks.c b/utils/src/locks.c new file mode 100644 index 00000000..b1f0113b --- /dev/null +++ b/utils/src/locks.c @@ -0,0 +1,420 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sparse.h" +#include "cmd.h" +#include "util.h" +#include "format.h" +#include "list.h" +#include "dlmglue.h" + +static int print_lvbs = 0; +static int oneline = 0; + +static char *level_str(int level) +{ + char *s; + + switch (level) { + case DLM_LOCK_IV: + s = "IV"; + break; + case DLM_LOCK_NL: + s = "NL"; + break; + case DLM_LOCK_CR: + s = "CR"; + break; + case DLM_LOCK_CW: + s = "CW"; + break; + case DLM_LOCK_PR: + s = "PR"; + break; + case DLM_LOCK_PW: + s = "PW"; + break; + case DLM_LOCK_EX: + s = "EX"; + break; + default: + s = "Unknown"; + } + + return s; +} + +static void print_flags(unsigned long flags, FILE *out) +{ + if (flags & OCFS2_LOCK_INITIALIZED ) + fprintf(out, " Initialized"); + + if (flags & OCFS2_LOCK_ATTACHED) + fprintf(out, " Attached"); + + if (flags & OCFS2_LOCK_BUSY) + fprintf(out, " Busy"); + + if (flags & OCFS2_LOCK_BLOCKED) + fprintf(out, " Blocked"); + + if (flags & OCFS2_LOCK_LOCAL) + fprintf(out, " Local"); + + if (flags & OCFS2_LOCK_NEEDS_REFRESH) + fprintf(out, " Needs Refresh"); + + if (flags & OCFS2_LOCK_REFRESHING) + fprintf(out, " Refreshing"); + + if (flags & OCFS2_LOCK_FREEING) + fprintf(out, " Freeing"); + + if (flags & OCFS2_LOCK_QUEUED) + fprintf(out, " Queued"); +} + +static char *action_str(unsigned int action) +{ + char *s; + + switch (action) { + case OCFS2_AST_INVALID: + s = "None"; + break; + case OCFS2_AST_ATTACH: + s = "Attach"; + break; + case OCFS2_AST_CONVERT: + s = "Convert"; + break; + case OCFS2_AST_DOWNCONVERT: + s = "Downconvert"; + break; + default: + s = "Unknown"; + } + + return s; +} + +static char *unlock_action_str(unsigned int unlock_action) +{ + char *s; + switch (unlock_action) { + case OCFS2_UNLOCK_INVALID: + s = "None"; + break; + case OCFS2_UNLOCK_CANCEL_CONVERT: + s = "Cancel Convert"; + break; + case OCFS2_UNLOCK_DROP_LOCK: + s = "Drop Lock"; + break; + default: + s = "Unknown"; + } + + return s; +} + +static void dump_raw_lvb(const char *lvb, FILE *out) +{ + int i; + + fprintf(out, "Raw LVB:\t"); + + for(i = 0; i < DLM_LVB_LEN; i++) { + fprintf(out, "%02hhx ", lvb[i]); + if (!((i+1) % 16) && i != (DLM_LVB_LEN-1)) + fprintf(out, "\n\t\t"); + } + fprintf(out, "\n"); +} + +static int end_line(FILE *f) +{ + int ret; + + do { + ret = fgetc(f); + if (ret == EOF) + return 1; + } while (ret != '\n'); + + return 0; +} + +/* the printing/scanning code here was modified from ocfs2-tools */ +static int print_fields(FILE *file, FILE *out) +{ + char id[OCFS2_LOCK_ID_MAX_LEN + 1]; + char lvb[DLM_LVB_LEN]; + int ret, i, level, requested, blocking; + unsigned long flags; + unsigned int action, unlock_action, cw, ro, ex, dummy; + const char *format; + unsigned long long num_prmode, num_exmode, num_cwmode; + unsigned int num_prmode_failed, num_exmode_failed, num_cwmode_failed; + unsigned long long total_prmode, total_exmode, total_cwmode; + unsigned long long avg_prmode = 0, avg_exmode = 0, avg_cwmode = 0; + unsigned int max_prmode, max_exmode, max_cwmode, num_refresh; + + ret = fscanf(file, "%s\t" + "%d\t" + "0x%lx\t" + "0x%x\t" + "0x%x\t" + "%u\t" + "%u\t" + "%d\t" + "%d\t", + id, + &level, + &flags, + &action, + &unlock_action, + &ro, + &ex, + &requested, + &blocking); + if (ret != 9) { + ret = -EINVAL; + goto out; + } + + format = "0x%x\t"; + for (i = 0; i < DLM_LVB_LEN; i++) { + ret = fscanf(file, format, &dummy); + if (ret != 1) { + ret = -EINVAL; + goto out; + } + + lvb[i] = (char) dummy; + } + + ret = fscanf(file, "%llu\t" + "%llu\t" + "%u\t" + "%u\t" + "%llu\t" + "%llu\t" + "%u\t" + "%u\t" + "%u\t" + "%u\t" + "%llu\t" + "%u\t" + "%llu\t" + "%u", + &num_prmode, + &num_exmode, + &num_prmode_failed, + &num_exmode_failed, + &total_prmode, + &total_exmode, + &max_prmode, + &max_exmode, + &num_refresh, + &cw, + &num_cwmode, + &num_cwmode_failed, + &total_cwmode, + &max_cwmode); + if (ret != 14) { + ret = -EINVAL; + goto out; + } + + if (oneline) { + fprintf(out, "%s mode %s flags", id, level_str(level)); + print_flags(flags, out); + fprintf(out, " cw/ro/ex %u/%u/%u act %s unlock %s req %s " + "block %s\n", cw, ro, ex, action_str(action), + unlock_action_str(unlock_action), level_str(requested), + level_str(blocking)); + ret = 1; + goto out; + } + + fprintf(out, "Lockres: %s Mode: %s\nFlags:", id, level_str(level)); + print_flags(flags, out); + fprintf(out, "\nCW Holders: %u RO Holders: %u EX Holders: %u\n", cw, + ro, ex); + fprintf(out, "Pending Action: %s Pending Unlock Action: %s\n", + action_str(action), unlock_action_str(unlock_action)); + fprintf(out, "Requested Mode: %s Blocking Mode: %s\n", + level_str(requested), level_str(blocking)); + + if (print_lvbs) + dump_raw_lvb(lvb, out); +#define NSEC_PER_USEC 1000 + + if (num_prmode) + avg_prmode = total_prmode/num_prmode; + + if (num_exmode) + avg_exmode = total_exmode/num_exmode; + + if (num_cwmode) + avg_cwmode = total_cwmode/num_cwmode; + + fprintf(out, "CW > Gets: %llu Fails: %u Waits Total: %lluus " + "Max: %uus Avg: %lluns\n", + num_cwmode, num_cwmode_failed, total_cwmode/NSEC_PER_USEC, + max_cwmode, avg_cwmode); + fprintf(out, "PR > Gets: %llu Fails: %u Waits Total: %lluus " + "Max: %uus Avg: %lluns\n", + num_prmode, num_prmode_failed, total_prmode/NSEC_PER_USEC, + max_prmode, avg_prmode); + fprintf(out, "EX > Gets: %llu Fails: %u Waits Total: %lluus " + "Max: %uus Avg: %lluns\n", + num_exmode, num_exmode_failed, total_exmode/NSEC_PER_USEC, + max_exmode, avg_exmode); + fprintf(out, "Disk Refreshes: %u\n", num_refresh); + + ret = 1; +out: + return ret; +} + +#define CURRENT_PROTO 4 +static void print_locks(int fd) +{ + FILE *file = fdopen(fd, "r"); + unsigned int version; + int ret; + + if (!file) + return; + + do { + /* + * Version is printed on every line (silly but easy to + * implement) + */ + ret = fscanf(file, "%x\t", &version); + if (ret != 1) + goto out; + + if (version > CURRENT_PROTO) { + fprintf(stdout, + "Lock debug proto is %u, but %u is the " + "highest I understand.\n", version, + CURRENT_PROTO); + goto out; + } + + ret = print_fields(file, stdout); + + /* Read to the end of the record here. Any new fields tagged + * onto the current format will be silently ignored. */ + } while (!end_line(file)); + +out: + fclose(file); +} + +static int get_fsid(int fd, u64 *fsid) +{ + struct scoutfs_super_block *super; + + super = read_block(fd, SCOUTFS_SUPER_BLKNO); + if (!super) + return -ENOMEM; + + *fsid = le64_to_cpu(super->hdr.fsid); + + return 0; +} + +static int locks_func(int argc, char *argv[]) +{ + char sysfs[PATH_MAX]; + char *path; + u64 fsid; + int ret; + int fd; + int c = 10000; + + static struct option long_ops[] = { + { "oneline", 0, NULL, 'o' }, + { "lvbs=", 1, NULL, 'L'}, + { NULL, 0, NULL, 0} + }; + + if (argc < 1) { + printf("scoutfs: locks: a device argument is required\n"); + return -EINVAL; + } + + while ((c = getopt_long(argc, argv, "l:", long_ops, NULL)) + != -1) { + switch (c) { + case 'o': + oneline = 1; + break; + case 'l': + case 'L': + if (strcasecmp(optarg, "yes") == 0) + print_lvbs = 1; + else if (strcasecmp(optarg, "no") == 0) + print_lvbs = 0; + break; + default: + return -EINVAL; + } + } + path = argv[optind]; + + /* XXX: Take mountpoint argument instead and turn that into a + * device for below */ + + fd = open(path, O_RDONLY); + if (fd < 0) { + ret = -errno; + fprintf(stderr, "failed to open '%s': %s (%d)\n", + path, strerror(errno), errno); + return ret; + } + + ret = get_fsid(fd, &fsid); + close(fd); + if (ret) + return ret; + + /* open sysfs file, print now */ + snprintf(sysfs, PATH_MAX, + "/sys/kernel/debug/scoutfs/%llx/locking_state", fsid); + + fd = open(sysfs, O_RDONLY); + if (fd < 0) { + ret = -errno; + fprintf(stderr, "failed to open '%s': %s (%d)\n", sysfs, + strerror(errno), errno); + return ret; + } + + print_locks(fd); + + close(fd); + + return 0; +} + +static void __attribute__((constructor)) locks_ctor(void) +{ + cmd_register("locks", "--lvbs=[yes|no] --oneline ", + "show file system locking state", locks_func); +} diff --git a/utils/src/print.c b/utils/src/print.c index 3007c063..6595c20c 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -18,7 +18,7 @@ #include "crc.h" #include "key.h" -static void *read_block(int fd, u64 blkno) +void *read_block(int fd, u64 blkno) { ssize_t ret; void *buf; diff --git a/utils/src/util.h b/utils/src/util.h index 89ec4b4f..1988d40b 100644 --- a/utils/src/util.h +++ b/utils/src/util.h @@ -5,6 +5,9 @@ #include #include +#include "sparse.h" + + /* * Generate build warnings if the condition is false but generate no * code at run time if it's true. @@ -75,4 +78,7 @@ static inline int memcmp_lens(const void *a, int a_len, return memcmp(a, b, len) ?: a_len - b_len; } +/* exported from print.c, we should probably just move it into a util.c */ +void *read_block(int fd, u64 blkno); + #endif From 33fa14b7305e0cb465d515f3a3e569db5e615c22 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Thu, 30 Nov 2017 11:18:38 -0800 Subject: [PATCH 119/235] scoutfs: remove SCOUTFS_LOCK_INODE_GROUP_OFFSET This is an unused artifact from a previous key format. Signed-off-by: Zach Brown --- utils/src/format.h | 1 - 1 file changed, 1 deletion(-) diff --git a/utils/src/format.h b/utils/src/format.h index c0e854d5..fc15a08a 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -547,7 +547,6 @@ struct scoutfs_lock_name { #define SCOUTFS_LOCK_INODE_GROUP_NR 1024 #define SCOUTFS_LOCK_INODE_GROUP_MASK (SCOUTFS_LOCK_INODE_GROUP_NR - 1) -#define SCOUTFS_LOCK_INODE_GROUP_OFFSET (~0ULL) #define SCOUTFS_LOCK_SEQ_GROUP_MASK ((1ULL << 10) - 1) From 7c30294e1bd574059da21ae85143b60ded3ed96a Mon Sep 17 00:00:00 2001 From: Mark Fasheh Date: Thu, 25 Jan 2018 13:29:24 -0800 Subject: [PATCH 120/235] scoutfs-utils: update format.h with file handle definition Signed-off-by: Mark Fasheh --- utils/src/format.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/utils/src/format.h b/utils/src/format.h index fc15a08a..03e49d7c 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -642,4 +642,16 @@ enum { SCOUTFS_NET_STATUS_UNKNOWN, }; +/* + * Scoutfs file handle structure - this can be copied out to userspace + * via open by handle or put on the wire from NFS. + */ +struct scoutfs_fid { + __le64 ino; + __le64 parent_ino; +} __packed; + +#define FILEID_SCOUTFS 0x81 +#define FILEID_SCOUTFS_WITH_PARENT 0x82 + #endif From 7d674fa4bfe635d70d4d7698a9ea60fc9e0f190d Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Mon, 29 Jan 2018 10:45:17 -0800 Subject: [PATCH 121/235] scoutfs-utils: remove size inode index items With the removal of the size index items we no longer have to print them or be able to walk the index. mkfs only needs to create a meta seq index item for the root inode. Signed-off-by: Zach Brown --- utils/src/format.h | 7 +++---- utils/src/ioctl.h | 3 +-- utils/src/key.c | 3 --- utils/src/mkfs.c | 33 ++++++++++++++------------------- utils/src/print.c | 2 +- utils/src/walk_inodes.c | 8 +++----- 6 files changed, 22 insertions(+), 34 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index 03e49d7c..90d1d4c7 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -237,10 +237,9 @@ struct scoutfs_segment_block { #define SCOUTFS_MAX_ZONE 4 /* power of 2 is efficient */ /* inode index zone */ -#define SCOUTFS_INODE_INDEX_SIZE_TYPE 1 -#define SCOUTFS_INODE_INDEX_META_SEQ_TYPE 2 -#define SCOUTFS_INODE_INDEX_DATA_SEQ_TYPE 3 -#define SCOUTFS_INODE_INDEX_NR 4 /* don't forget to update */ +#define SCOUTFS_INODE_INDEX_META_SEQ_TYPE 1 +#define SCOUTFS_INODE_INDEX_DATA_SEQ_TYPE 2 +#define SCOUTFS_INODE_INDEX_NR 3 /* don't forget to update */ /* node zone */ #define SCOUTFS_FREE_BITS_SEGNO_TYPE 1 diff --git a/utils/src/ioctl.h b/utils/src/ioctl.h index e9a78db0..34917a34 100644 --- a/utils/src/ioctl.h +++ b/utils/src/ioctl.h @@ -51,8 +51,7 @@ struct scoutfs_ioctl_walk_inodes { } __packed; enum { - SCOUTFS_IOC_WALK_INODES_SIZE = 0, - SCOUTFS_IOC_WALK_INODES_META_SEQ, + SCOUTFS_IOC_WALK_INODES_META_SEQ = 0, SCOUTFS_IOC_WALK_INODES_DATA_SEQ, SCOUTFS_IOC_WALK_INODES_UNKNOWN, }; diff --git a/utils/src/key.c b/utils/src/key.c index 7588ea7f..268525c2 100644 --- a/utils/src/key.c +++ b/utils/src/key.c @@ -184,7 +184,6 @@ typedef int (*key_printer_t)(char *buf, struct scoutfs_key_buf *key, static int pr_ino_idx(char *buf, struct scoutfs_key_buf *key, size_t size) { static char *type_strings[] = { - [SCOUTFS_INODE_INDEX_SIZE_TYPE] = "siz", [SCOUTFS_INODE_INDEX_META_SEQ_TYPE] = "msq", [SCOUTFS_INODE_INDEX_DATA_SEQ_TYPE] = "dsq", }; @@ -302,8 +301,6 @@ static int pr_block_mapping(char *buf, struct scoutfs_key_buf *key, size_t size) } const static key_printer_t key_printers[SCOUTFS_MAX_ZONE][SCOUTFS_MAX_TYPE] = { - [SCOUTFS_INODE_INDEX_ZONE][SCOUTFS_INODE_INDEX_SIZE_TYPE] = - pr_ino_idx, [SCOUTFS_INODE_INDEX_ZONE][SCOUTFS_INODE_INDEX_META_SEQ_TYPE] = pr_ino_idx, [SCOUTFS_INODE_INDEX_ZONE][SCOUTFS_INODE_INDEX_DATA_SEQ_TYPE] = diff --git a/utils/src/mkfs.c b/utils/src/mkfs.c index 86543c10..96497e47 100644 --- a/utils/src/mkfs.c +++ b/utils/src/mkfs.c @@ -261,7 +261,7 @@ static int write_new_fs(char *path, int fd) mkey->level = 1; idx_key->zone = SCOUTFS_INODE_INDEX_ZONE; - idx_key->type = SCOUTFS_INODE_INDEX_SIZE_TYPE; + idx_key->type = SCOUTFS_INODE_INDEX_META_SEQ_TYPE; idx_key->major = 0; idx_key->minor = 0; idx_key->ino = cpu_to_be64(SCOUTFS_ROOT_INO); @@ -294,26 +294,21 @@ static int write_new_fs(char *path, int fd) *prev_link = cpu_to_le32((long)item -(long)sblk); prev_link = &item->skip_links[0]; - /* write the root inode index keys */ - for (i = SCOUTFS_INODE_INDEX_SIZE_TYPE; - i <= SCOUTFS_INODE_INDEX_META_SEQ_TYPE; i++) { + item->key_len = cpu_to_le16(sizeof(*idx_key)); + item->val_len = 0; + item->nr_links = 1; + le32_add_cpu(&sblk->nr_items, 1); - item->key_len = cpu_to_le16(sizeof(*idx_key)); - item->val_len = 0; - item->nr_links = 1; - le32_add_cpu(&sblk->nr_items, 1); + idx_key = (void *)&item->skip_links[1]; + idx_key->zone = SCOUTFS_INODE_INDEX_ZONE; + idx_key->type = SCOUTFS_INODE_INDEX_META_SEQ_TYPE; + idx_key->ino = cpu_to_be64(SCOUTFS_ROOT_INO); + idx_key->major = 0; + idx_key->minor = 0; - idx_key = (void *)&item->skip_links[1]; - idx_key->zone = SCOUTFS_INODE_INDEX_ZONE; - idx_key->type = i; - idx_key->ino = cpu_to_be64(SCOUTFS_ROOT_INO); - idx_key->major = 0; - idx_key->minor = 0; - - item = (void *)(idx_key + 1); - *prev_link = cpu_to_le32((long)item -(long)sblk); - prev_link = &item->skip_links[0]; - } + item = (void *)(idx_key + 1); + *prev_link = cpu_to_le32((long)item -(long)sblk); + prev_link = &item->skip_links[0]; sblk->last_item_off = cpu_to_le32((long)item - (long)sblk); diff --git a/utils/src/print.c b/utils/src/print.c index 6595c20c..a22caaaa 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -232,7 +232,7 @@ typedef void (*print_func_t)(void *key, int key_len, void *val, int val_len); static print_func_t find_printer(u8 zone, u8 type) { if (zone == SCOUTFS_INODE_INDEX_ZONE && - type >= SCOUTFS_INODE_INDEX_SIZE_TYPE && + type >= SCOUTFS_INODE_INDEX_META_SEQ_TYPE && type <= SCOUTFS_INODE_INDEX_DATA_SEQ_TYPE) return print_inode_index; diff --git a/utils/src/walk_inodes.c b/utils/src/walk_inodes.c index c6b0bc99..6cf36d5a 100644 --- a/utils/src/walk_inodes.c +++ b/utils/src/walk_inodes.c @@ -80,15 +80,13 @@ static int walk_inodes_cmd(int argc, char **argv) return -EINVAL; } - if (!strcasecmp(argv[1], "size")) - walk.index = SCOUTFS_IOC_WALK_INODES_SIZE; - else if (!strcasecmp(argv[1], "meta_seq")) + if (!strcasecmp(argv[1], "meta_seq")) walk.index = SCOUTFS_IOC_WALK_INODES_META_SEQ; else if (!strcasecmp(argv[1], "data_seq")) walk.index = SCOUTFS_IOC_WALK_INODES_DATA_SEQ; else { - fprintf(stderr, "unknown index '%s', try 'size', 'ctime, or " - "mtime'\n", argv[1]); + fprintf(stderr, "unknown index '%s', try 'meta_seq' or " + "'data_seq'\n", argv[1]); return -EINVAL; } From e68a999ed5235f4a83c8e212f766824e1ca73740 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Mon, 12 Feb 2018 18:08:10 -0800 Subject: [PATCH 122/235] scoutfs-utils: remove locks command scoutfs now directly uses the kernel dlm subsystem and offsers a debugfs file with the current lock state. We don't need userspace to read and format the contents of a debugging file. Signed-off-by: Zach Brown --- utils/src/dlmglue.h | 101 ----------- utils/src/locks.c | 420 -------------------------------------------- utils/src/print.c | 2 +- utils/src/util.h | 6 - 4 files changed, 1 insertion(+), 528 deletions(-) delete mode 100644 utils/src/dlmglue.h delete mode 100644 utils/src/locks.c diff --git a/utils/src/dlmglue.h b/utils/src/dlmglue.h deleted file mode 100644 index 985d65f9..00000000 --- a/utils/src/dlmglue.h +++ /dev/null @@ -1,101 +0,0 @@ -/* -*- mode: c; c-basic-offset: 8; -*- - * vim: noexpandtab sw=8 ts=8 sts=0: - * - * dlmglue.h - * - * dlmglue constants for userspace decoding - * - * Copyright (C) 2002, 2004 Oracle. All rights reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 021110-1307, USA. - */ - - -#ifndef DLMGLUE_H -#define DLMGLUE_H - -/* Max length of lockid name */ -#define OCFS2_LOCK_ID_MAX_LEN 32 - -#define DLM_LVB_LEN 64 - -enum ocfs2_ast_action { - OCFS2_AST_INVALID = 0, - OCFS2_AST_ATTACH, - OCFS2_AST_CONVERT, - OCFS2_AST_DOWNCONVERT, -}; - -/* actions for an unlockast function to take. */ -enum ocfs2_unlock_action { - OCFS2_UNLOCK_INVALID = 0, - OCFS2_UNLOCK_CANCEL_CONVERT, - OCFS2_UNLOCK_DROP_LOCK, -}; - -/* ocfs2_lock_res->l_flags flags. */ -#define OCFS2_LOCK_ATTACHED (0x00000001) /* we have initialized - * the lvb */ -#define OCFS2_LOCK_BUSY (0x00000002) /* we are currently in - * dlm_lock */ -#define OCFS2_LOCK_BLOCKED (0x00000004) /* blocked waiting to - * downconvert*/ -#define OCFS2_LOCK_LOCAL (0x00000008) /* newly created inode */ -#define OCFS2_LOCK_NEEDS_REFRESH (0x00000010) -#define OCFS2_LOCK_REFRESHING (0x00000020) -#define OCFS2_LOCK_INITIALIZED (0x00000040) /* track initialization - * for shutdown paths */ -#define OCFS2_LOCK_FREEING (0x00000080) /* help dlmglue track - * when to skip queueing - * a lock because it's - * about to be - * dropped. */ -#define OCFS2_LOCK_QUEUED (0x00000100) /* queued for downconvert */ -#define OCFS2_LOCK_NOCACHE (0x00000200) /* don't use a holder count */ -#define OCFS2_LOCK_PENDING (0x00000400) /* This lockres is pending a - call to dlm_lock. Only - exists with BUSY set. */ -#define OCFS2_LOCK_UPCONVERT_FINISHING (0x00000800) /* blocks the dc thread - * from downconverting - * before the upconvert - * has completed */ - -#define OCFS2_LOCK_NONBLOCK_FINISHED (0x00001000) /* NONBLOCK cluster - * lock has already - * returned, do not block - * dc thread from - * downconverting */ - -/* The cluster stack fields */ -#define OCFS2_STACK_LABEL_LEN 4 -#define OCFS2_CLUSTER_NAME_LEN 16 - -/* - * Return value from ->downconvert_worker functions. - * - * These control the precise actions of ocfs2_unblock_lock() - * and ocfs2_process_blocked_lock() - * - */ -enum ocfs2_unblock_action { - UNBLOCK_CONTINUE = 0, /* Continue downconvert */ - UNBLOCK_CONTINUE_POST = 1, /* Continue downconvert, fire - * ->post_unlock callback */ - UNBLOCK_STOP_POST = 2, /* Do not downconvert, fire - * ->post_unlock() callback. */ -}; - -#endif /* DLMGLUE_H */ diff --git a/utils/src/locks.c b/utils/src/locks.c deleted file mode 100644 index b1f0113b..00000000 --- a/utils/src/locks.c +++ /dev/null @@ -1,420 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "sparse.h" -#include "cmd.h" -#include "util.h" -#include "format.h" -#include "list.h" -#include "dlmglue.h" - -static int print_lvbs = 0; -static int oneline = 0; - -static char *level_str(int level) -{ - char *s; - - switch (level) { - case DLM_LOCK_IV: - s = "IV"; - break; - case DLM_LOCK_NL: - s = "NL"; - break; - case DLM_LOCK_CR: - s = "CR"; - break; - case DLM_LOCK_CW: - s = "CW"; - break; - case DLM_LOCK_PR: - s = "PR"; - break; - case DLM_LOCK_PW: - s = "PW"; - break; - case DLM_LOCK_EX: - s = "EX"; - break; - default: - s = "Unknown"; - } - - return s; -} - -static void print_flags(unsigned long flags, FILE *out) -{ - if (flags & OCFS2_LOCK_INITIALIZED ) - fprintf(out, " Initialized"); - - if (flags & OCFS2_LOCK_ATTACHED) - fprintf(out, " Attached"); - - if (flags & OCFS2_LOCK_BUSY) - fprintf(out, " Busy"); - - if (flags & OCFS2_LOCK_BLOCKED) - fprintf(out, " Blocked"); - - if (flags & OCFS2_LOCK_LOCAL) - fprintf(out, " Local"); - - if (flags & OCFS2_LOCK_NEEDS_REFRESH) - fprintf(out, " Needs Refresh"); - - if (flags & OCFS2_LOCK_REFRESHING) - fprintf(out, " Refreshing"); - - if (flags & OCFS2_LOCK_FREEING) - fprintf(out, " Freeing"); - - if (flags & OCFS2_LOCK_QUEUED) - fprintf(out, " Queued"); -} - -static char *action_str(unsigned int action) -{ - char *s; - - switch (action) { - case OCFS2_AST_INVALID: - s = "None"; - break; - case OCFS2_AST_ATTACH: - s = "Attach"; - break; - case OCFS2_AST_CONVERT: - s = "Convert"; - break; - case OCFS2_AST_DOWNCONVERT: - s = "Downconvert"; - break; - default: - s = "Unknown"; - } - - return s; -} - -static char *unlock_action_str(unsigned int unlock_action) -{ - char *s; - switch (unlock_action) { - case OCFS2_UNLOCK_INVALID: - s = "None"; - break; - case OCFS2_UNLOCK_CANCEL_CONVERT: - s = "Cancel Convert"; - break; - case OCFS2_UNLOCK_DROP_LOCK: - s = "Drop Lock"; - break; - default: - s = "Unknown"; - } - - return s; -} - -static void dump_raw_lvb(const char *lvb, FILE *out) -{ - int i; - - fprintf(out, "Raw LVB:\t"); - - for(i = 0; i < DLM_LVB_LEN; i++) { - fprintf(out, "%02hhx ", lvb[i]); - if (!((i+1) % 16) && i != (DLM_LVB_LEN-1)) - fprintf(out, "\n\t\t"); - } - fprintf(out, "\n"); -} - -static int end_line(FILE *f) -{ - int ret; - - do { - ret = fgetc(f); - if (ret == EOF) - return 1; - } while (ret != '\n'); - - return 0; -} - -/* the printing/scanning code here was modified from ocfs2-tools */ -static int print_fields(FILE *file, FILE *out) -{ - char id[OCFS2_LOCK_ID_MAX_LEN + 1]; - char lvb[DLM_LVB_LEN]; - int ret, i, level, requested, blocking; - unsigned long flags; - unsigned int action, unlock_action, cw, ro, ex, dummy; - const char *format; - unsigned long long num_prmode, num_exmode, num_cwmode; - unsigned int num_prmode_failed, num_exmode_failed, num_cwmode_failed; - unsigned long long total_prmode, total_exmode, total_cwmode; - unsigned long long avg_prmode = 0, avg_exmode = 0, avg_cwmode = 0; - unsigned int max_prmode, max_exmode, max_cwmode, num_refresh; - - ret = fscanf(file, "%s\t" - "%d\t" - "0x%lx\t" - "0x%x\t" - "0x%x\t" - "%u\t" - "%u\t" - "%d\t" - "%d\t", - id, - &level, - &flags, - &action, - &unlock_action, - &ro, - &ex, - &requested, - &blocking); - if (ret != 9) { - ret = -EINVAL; - goto out; - } - - format = "0x%x\t"; - for (i = 0; i < DLM_LVB_LEN; i++) { - ret = fscanf(file, format, &dummy); - if (ret != 1) { - ret = -EINVAL; - goto out; - } - - lvb[i] = (char) dummy; - } - - ret = fscanf(file, "%llu\t" - "%llu\t" - "%u\t" - "%u\t" - "%llu\t" - "%llu\t" - "%u\t" - "%u\t" - "%u\t" - "%u\t" - "%llu\t" - "%u\t" - "%llu\t" - "%u", - &num_prmode, - &num_exmode, - &num_prmode_failed, - &num_exmode_failed, - &total_prmode, - &total_exmode, - &max_prmode, - &max_exmode, - &num_refresh, - &cw, - &num_cwmode, - &num_cwmode_failed, - &total_cwmode, - &max_cwmode); - if (ret != 14) { - ret = -EINVAL; - goto out; - } - - if (oneline) { - fprintf(out, "%s mode %s flags", id, level_str(level)); - print_flags(flags, out); - fprintf(out, " cw/ro/ex %u/%u/%u act %s unlock %s req %s " - "block %s\n", cw, ro, ex, action_str(action), - unlock_action_str(unlock_action), level_str(requested), - level_str(blocking)); - ret = 1; - goto out; - } - - fprintf(out, "Lockres: %s Mode: %s\nFlags:", id, level_str(level)); - print_flags(flags, out); - fprintf(out, "\nCW Holders: %u RO Holders: %u EX Holders: %u\n", cw, - ro, ex); - fprintf(out, "Pending Action: %s Pending Unlock Action: %s\n", - action_str(action), unlock_action_str(unlock_action)); - fprintf(out, "Requested Mode: %s Blocking Mode: %s\n", - level_str(requested), level_str(blocking)); - - if (print_lvbs) - dump_raw_lvb(lvb, out); -#define NSEC_PER_USEC 1000 - - if (num_prmode) - avg_prmode = total_prmode/num_prmode; - - if (num_exmode) - avg_exmode = total_exmode/num_exmode; - - if (num_cwmode) - avg_cwmode = total_cwmode/num_cwmode; - - fprintf(out, "CW > Gets: %llu Fails: %u Waits Total: %lluus " - "Max: %uus Avg: %lluns\n", - num_cwmode, num_cwmode_failed, total_cwmode/NSEC_PER_USEC, - max_cwmode, avg_cwmode); - fprintf(out, "PR > Gets: %llu Fails: %u Waits Total: %lluus " - "Max: %uus Avg: %lluns\n", - num_prmode, num_prmode_failed, total_prmode/NSEC_PER_USEC, - max_prmode, avg_prmode); - fprintf(out, "EX > Gets: %llu Fails: %u Waits Total: %lluus " - "Max: %uus Avg: %lluns\n", - num_exmode, num_exmode_failed, total_exmode/NSEC_PER_USEC, - max_exmode, avg_exmode); - fprintf(out, "Disk Refreshes: %u\n", num_refresh); - - ret = 1; -out: - return ret; -} - -#define CURRENT_PROTO 4 -static void print_locks(int fd) -{ - FILE *file = fdopen(fd, "r"); - unsigned int version; - int ret; - - if (!file) - return; - - do { - /* - * Version is printed on every line (silly but easy to - * implement) - */ - ret = fscanf(file, "%x\t", &version); - if (ret != 1) - goto out; - - if (version > CURRENT_PROTO) { - fprintf(stdout, - "Lock debug proto is %u, but %u is the " - "highest I understand.\n", version, - CURRENT_PROTO); - goto out; - } - - ret = print_fields(file, stdout); - - /* Read to the end of the record here. Any new fields tagged - * onto the current format will be silently ignored. */ - } while (!end_line(file)); - -out: - fclose(file); -} - -static int get_fsid(int fd, u64 *fsid) -{ - struct scoutfs_super_block *super; - - super = read_block(fd, SCOUTFS_SUPER_BLKNO); - if (!super) - return -ENOMEM; - - *fsid = le64_to_cpu(super->hdr.fsid); - - return 0; -} - -static int locks_func(int argc, char *argv[]) -{ - char sysfs[PATH_MAX]; - char *path; - u64 fsid; - int ret; - int fd; - int c = 10000; - - static struct option long_ops[] = { - { "oneline", 0, NULL, 'o' }, - { "lvbs=", 1, NULL, 'L'}, - { NULL, 0, NULL, 0} - }; - - if (argc < 1) { - printf("scoutfs: locks: a device argument is required\n"); - return -EINVAL; - } - - while ((c = getopt_long(argc, argv, "l:", long_ops, NULL)) - != -1) { - switch (c) { - case 'o': - oneline = 1; - break; - case 'l': - case 'L': - if (strcasecmp(optarg, "yes") == 0) - print_lvbs = 1; - else if (strcasecmp(optarg, "no") == 0) - print_lvbs = 0; - break; - default: - return -EINVAL; - } - } - path = argv[optind]; - - /* XXX: Take mountpoint argument instead and turn that into a - * device for below */ - - fd = open(path, O_RDONLY); - if (fd < 0) { - ret = -errno; - fprintf(stderr, "failed to open '%s': %s (%d)\n", - path, strerror(errno), errno); - return ret; - } - - ret = get_fsid(fd, &fsid); - close(fd); - if (ret) - return ret; - - /* open sysfs file, print now */ - snprintf(sysfs, PATH_MAX, - "/sys/kernel/debug/scoutfs/%llx/locking_state", fsid); - - fd = open(sysfs, O_RDONLY); - if (fd < 0) { - ret = -errno; - fprintf(stderr, "failed to open '%s': %s (%d)\n", sysfs, - strerror(errno), errno); - return ret; - } - - print_locks(fd); - - close(fd); - - return 0; -} - -static void __attribute__((constructor)) locks_ctor(void) -{ - cmd_register("locks", "--lvbs=[yes|no] --oneline ", - "show file system locking state", locks_func); -} diff --git a/utils/src/print.c b/utils/src/print.c index a22caaaa..4c340377 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -18,7 +18,7 @@ #include "crc.h" #include "key.h" -void *read_block(int fd, u64 blkno) +static void *read_block(int fd, u64 blkno) { ssize_t ret; void *buf; diff --git a/utils/src/util.h b/utils/src/util.h index 1988d40b..89ec4b4f 100644 --- a/utils/src/util.h +++ b/utils/src/util.h @@ -5,9 +5,6 @@ #include #include -#include "sparse.h" - - /* * Generate build warnings if the condition is false but generate no * code at run time if it's true. @@ -78,7 +75,4 @@ static inline int memcmp_lens(const void *a, int a_len, return memcmp(a, b, len) ?: a_len - b_len; } -/* exported from print.c, we should probably just move it into a util.c */ -void *read_block(int fd, u64 blkno); - #endif From d796fbf15e8a65fe50cbbfc995b6b99be6e68d05 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Fri, 2 Feb 2018 09:54:30 -0800 Subject: [PATCH 123/235] scoutfs: track online and offline blocks Signed-off-by: Zach Brown --- utils/src/format.h | 8 ++++++++ utils/src/ioctl.h | 2 ++ utils/src/stat.c | 7 +++++-- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index 90d1d4c7..bb8d674a 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -449,6 +449,12 @@ struct scoutfs_timespec { * have changed. It is exposed via an ioctl and is then provided as an * argument to data functions to protect racing modification. * + * @online_blocks: The number of fixed 4k blocks currently allocated and + * storing data in the volume. + * + * @offline_blocks: The number of fixed 4k blocks that could be made + * online by staging. + * * XXX * - otime? * - compat flags? @@ -462,6 +468,8 @@ struct scoutfs_inode { __le64 meta_seq; __le64 data_seq; __le64 data_version; + __le64 online_blocks; + __le64 offline_blocks; __le64 next_readdir_pos; __le32 nlink; __le32 uid; diff --git a/utils/src/ioctl.h b/utils/src/ioctl.h index 34917a34..33c94b95 100644 --- a/utils/src/ioctl.h +++ b/utils/src/ioctl.h @@ -178,6 +178,8 @@ struct scoutfs_ioctl_stat_more { __u64 meta_seq; __u64 data_seq; __u64 data_version; + __u64 online_blocks; + __u64 offline_blocks; } __packed; #define SCOUTFS_IOC_STAT_MORE _IOW(SCOUTFS_IOCTL_MAGIC, 7, \ diff --git a/utils/src/stat.c b/utils/src/stat.c index f9f4f569..8d320afc 100644 --- a/utils/src/stat.c +++ b/utils/src/stat.c @@ -49,9 +49,12 @@ static int stat_more_cmd(int argc, char **argv) } else { printf(" File: '%s'\n" " meta_seq: %-20llu data_seq %-20llu" - " data_version: %-20llu\n", + " data_version: %-20llu\n" + " online_blocks: %-20llu " + " offline_blocks: %-20llu\n", path, stm.meta_seq, stm.data_seq, - stm.data_version); + stm.data_version, stm.online_blocks, + stm.offline_blocks); } close(fd); From 2527b4906ed365a74f94b5a02bcdea38dfe14fbe Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Wed, 14 Feb 2018 13:45:37 -0800 Subject: [PATCH 124/235] scoutfs-utils: remove inode blocks field It's the sum of oneline and offline and is redundant. Signed-off-by: Zach Brown --- utils/src/format.h | 3 ++- utils/src/print.c | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index bb8d674a..99241394 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -13,6 +13,8 @@ #define SCOUTFS_BLOCK_SIZE (1 << SCOUTFS_BLOCK_SHIFT) #define SCOUTFS_BLOCK_MASK (SCOUTFS_BLOCK_SIZE - 1) #define SCOUTFS_BLOCKS_PER_PAGE (PAGE_SIZE / SCOUTFS_BLOCK_SIZE) +#define SCOUTFS_BLOCK_SECTOR_SHIFT (SCOUTFS_BLOCK_SHIFT - 9) +#define SCOUTFS_BLOCK_SECTORS (1 << SCOUTFS_BLOCK_SECTOR_SHIFT) /* * FS data is stored in segments, for now they're fixed size. They'll @@ -464,7 +466,6 @@ struct scoutfs_timespec { */ struct scoutfs_inode { __le64 size; - __le64 blocks; __le64 meta_seq; __le64 data_seq; __le64 data_version; diff --git a/utils/src/print.c b/utils/src/print.c index 4c340377..8cca921a 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -79,13 +79,13 @@ static void print_inode(void *key, int key_len, void *val, int val_len) struct scoutfs_inode_key *ikey = key; struct scoutfs_inode *inode = val; - printf(" inode: ino %llu size %llu blocks %llu nlink %u\n" + printf(" inode: ino %llu size %llu nlink %u\n" " uid %u gid %u mode 0%o rdev 0x%x flags 0x%x\n" " next_readdir_pos %llu meta_seq %llu data_seq %llu data_version %llu\n" " atime %llu.%08u ctime %llu.%08u\n" " mtime %llu.%08u\n", be64_to_cpu(ikey->ino), - le64_to_cpu(inode->size), le64_to_cpu(inode->blocks), + le64_to_cpu(inode->size), le32_to_cpu(inode->nlink), le32_to_cpu(inode->uid), le32_to_cpu(inode->gid), le32_to_cpu(inode->mode), le32_to_cpu(inode->rdev), From 02204c36fcddbf124a877a2bb8c1b6980f43113a Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Wed, 14 Feb 2018 11:35:49 -0800 Subject: [PATCH 125/235] scoutfs-utils: clean up 'stat' output The previous formatting was modeled after the free form 'stat' output and it's a real mess. Just make it a simple "name value" table. Signed-off-by: Zach Brown --- utils/src/stat.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/utils/src/stat.c b/utils/src/stat.c index 8d320afc..2def6cea 100644 --- a/utils/src/stat.c +++ b/utils/src/stat.c @@ -47,13 +47,17 @@ static int stat_more_cmd(int argc, char **argv) fprintf(stderr, "stat_more ioctl failed on '%s': " "%s (%d)\n", path, strerror(errno), errno); } else { - printf(" File: '%s'\n" - " meta_seq: %-20llu data_seq %-20llu" - " data_version: %-20llu\n" - " online_blocks: %-20llu " - " offline_blocks: %-20llu\n", - path, stm.meta_seq, stm.data_seq, - stm.data_version, stm.online_blocks, + printf("path %s\n" + "meta_seq %llu\n" + "data_seq %llu\n" + "data_version %llu\n" + "online_blocks %llu\n" + "offline_blocks %llu\n", + path, + stm.meta_seq, + stm.data_seq, + stm.data_version, + stm.online_blocks, stm.offline_blocks); } From ac1065014bf3cbeb5f9ef4a121efc2e133510748 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Wed, 14 Feb 2018 11:58:21 -0800 Subject: [PATCH 126/235] scoutfs-utils: add stat -s option Lots of tests run scout stat and parse a single value. Give them an option to have the only output be that value so they don't have to pull it out of the output. Signed-off-by: Zach Brown --- utils/src/stat.c | 83 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 69 insertions(+), 14 deletions(-) diff --git a/utils/src/stat.c b/utils/src/stat.c index 2def6cea..2ae1a91e 100644 --- a/utils/src/stat.c +++ b/utils/src/stat.c @@ -7,6 +7,8 @@ #include #include #include +#include +#include #include "sparse.h" #include "util.h" @@ -14,20 +16,75 @@ #include "ioctl.h" #include "cmd.h" +#define FIELD(f) { \ + .name = #f, \ + .offset = offsetof(struct scoutfs_ioctl_stat_more, f), \ +} + +static struct stat_more_field { + char *name; + size_t offset; +} fields[] = { + FIELD(meta_seq), + FIELD(data_seq), + FIELD(data_version), + FIELD(online_blocks), + FIELD(offline_blocks), + { NULL, } +}; + +#define for_each_field(f) \ + for (f = fields; f->name; f++) + +static struct option long_ops[] = { + { "single_field", 1, NULL, 's' }, + { NULL, 0, NULL, 0} +}; + static int stat_more_cmd(int argc, char **argv) { struct scoutfs_ioctl_stat_more stm; + struct stat_more_field *single = NULL; + struct stat_more_field *fi; + char *single_name = NULL; char *path; int ret; int fd; int i; + int c; - if (argc == 1) { + while ((c = getopt_long(argc, argv, "s:", long_ops, NULL)) != -1) { + switch (c) { + case 's': + single_name = strdup(optarg); + assert(single_name); + break; + case '?': + default: + return -EINVAL; + } + } + + if (single_name) { + for_each_field(fi) { + if (strcmp(fi->name, single_name) == 0) { + single = fi; + break; + } + } + if (!single) { + fprintf(stderr, "unknown stat_more field: '%s'\n", + single_name); + return -EINVAL; + } + } + + if (optind >= argc) { fprintf(stderr, "must specify at least one path argument\n"); return -EINVAL; } - for (i = 1; i < argc; i++) { + for (i = optind; i < argc; i++) { path = argv[i]; fd = open(path, O_RDONLY); @@ -46,19 +103,17 @@ static int stat_more_cmd(int argc, char **argv) ret = -errno; fprintf(stderr, "stat_more ioctl failed on '%s': " "%s (%d)\n", path, strerror(errno), errno); + + } else if (single) { + printf("%llu\n", + *(u64 *)((void *)&stm + single->offset)); + } else { - printf("path %s\n" - "meta_seq %llu\n" - "data_seq %llu\n" - "data_version %llu\n" - "online_blocks %llu\n" - "offline_blocks %llu\n", - path, - stm.meta_seq, - stm.data_seq, - stm.data_version, - stm.online_blocks, - stm.offline_blocks); + printf("%-17s %s\n", "path", path); + for_each_field(fi) { + printf("%-17s %llu\n", fi->name, + *(u64 *)((void *)&stm + fi->offset)); + } } close(fd); From 8119a56c9255e1c7d82a815fb09c734721226a75 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Wed, 14 Mar 2018 10:52:11 -0700 Subject: [PATCH 127/235] scoutfs-utils: update format for xattr cleanups xattr items are now stored at the hash of the name and have a header in the first part. Signed-off-by: Zach Brown --- utils/src/format.h | 34 +++++++++++++++++++--------------- utils/src/key.c | 8 ++++---- utils/src/print.c | 17 +++++++++-------- 3 files changed, 32 insertions(+), 27 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index 99241394..ff3e4872 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -362,22 +362,24 @@ struct scoutfs_orphan_key { __be64 ino; } __packed; -/* value is each item's part of the full xattr value for the off/len */ struct scoutfs_xattr_key { __u8 zone; __be64 ino; __u8 type; - __u8 name[0]; -} __packed; - -struct scoutfs_xattr_key_footer { - __u8 null; + __be32 name_hash; + __be64 id; __u8 part; } __packed; -struct scoutfs_xattr_val_header { - __le16 part_len; - __u8 last_part; +/* + * The first xattr part item has a header that describes the xattr. The + * name and value are then packed into the following bytes in the first + * part item and overflow into the values of the rest of the part items. + */ +struct scoutfs_xattr { + __u8 name_len; + __le16 val_len; + __u8 name[0]; } __packed; /* size determines nr needed to store full target path in their values */ @@ -472,6 +474,7 @@ struct scoutfs_inode { __le64 online_blocks; __le64 offline_blocks; __le64 next_readdir_pos; + __le64 next_xattr_id; __le32 nlink; __le32 uid; __le32 gid; @@ -529,12 +532,13 @@ enum { #define SCOUTFS_MAX_VAL_SIZE SCOUTFS_BLOCK_MAPPING_MAX_BYTES -#define SCOUTFS_XATTR_MAX_NAME_LEN 255 -#define SCOUTFS_XATTR_MAX_SIZE 65536 -#define SCOUTFS_XATTR_PART_SIZE \ - (SCOUTFS_MAX_VAL_SIZE - sizeof(struct scoutfs_xattr_val_header)) -#define SCOUTFS_XATTR_MAX_PARTS \ - DIV_ROUND_UP(SCOUTFS_XATTR_MAX_SIZE, SCOUTFS_XATTR_PART_SIZE) +#define SCOUTFS_XATTR_MAX_NAME_LEN 255 +#define SCOUTFS_XATTR_MAX_VAL_LEN 65535 +#define SCOUTFS_XATTR_MAX_PART_SIZE 512U + +#define SCOUTFS_XATTR_NR_PARTS(name_len, val_len) \ + DIV_ROUND_UP(sizeof(struct scoutfs_xattr) + name_len + val_len, \ + SCOUTFS_XATTR_MAX_PART_SIZE); /* * structures used by dlm diff --git a/utils/src/key.c b/utils/src/key.c index 268525c2..7aa14ad4 100644 --- a/utils/src/key.c +++ b/utils/src/key.c @@ -236,13 +236,13 @@ static int pr_inode(char *buf, struct scoutfs_key_buf *key, size_t size) static int pr_xattr(char *buf, struct scoutfs_key_buf *key, size_t size) { struct scoutfs_xattr_key *xkey = key->data; - int len = (int)key->key_len - - offsetof(struct scoutfs_xattr_key, name[1]); return snprintf_key(buf, size, key, sizeof(struct scoutfs_xattr_key), key->key_len, - "fs.%llu.xat.%.*s", - be64_to_cpu(xkey->ino), len, xkey->name); + "fs.%llu.xat.%08x.%llu.%u", + be64_to_cpu(xkey->ino), + be32_to_cpu(xkey->name_hash), + be64_to_cpu(xkey->id), xkey->part); } static int pr_dirent(char *buf, struct scoutfs_key_buf *key, size_t size) diff --git a/utils/src/print.c b/utils/src/print.c index 8cca921a..f5764949 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -125,15 +125,16 @@ static u8 *global_printable_name(u8 *name, int name_len) static void print_xattr(void *key, int key_len, void *val, int val_len) { struct scoutfs_xattr_key *xkey = key; - struct scoutfs_xattr_key_footer *foot = key + key_len - sizeof(*foot); - struct scoutfs_xattr_val_header *vh = val; - unsigned int name_len = key_len - sizeof(*xkey) - sizeof(*foot); - u8 *name = global_printable_name(xkey->name, name_len); + struct scoutfs_xattr *xat = val; - printf(" xattr: ino %llu part %u part_len %u last_part %u\n" - " name %s\n", - be64_to_cpu(xkey->ino), foot->part, le16_to_cpu(vh->part_len), - vh->last_part, name); + printf(" xattr: ino %llu name_hash %08x id %llu part %u\n", + be64_to_cpu(xkey->ino), be32_to_cpu(xkey->name_hash), + be64_to_cpu(xkey->id), xkey->part); + + if (xkey->part == 0) + printf(" name_len %u val_len %u name %s\n", + xat->name_len, le16_to_cpu(xat->val_len), + global_printable_name(xat->name, xat->name_len)); } static void print_dirent(void *key, int key_len, void *val, int val_len) From 787555158af55f1051e85d7aa29a8a4d1a24648c Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Tue, 3 Apr 2018 10:58:07 -0700 Subject: [PATCH 128/235] scoutfs-utils: builtin rand returns int When we added these externs to silence some spurious sparse warning we forgot to give them a return value. Signed-off-by: Zach Brown --- utils/src/sparse.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/src/sparse.h b/utils/src/sparse.h index 3777e471..34fbbbaa 100644 --- a/utils/src/sparse.h +++ b/utils/src/sparse.h @@ -10,7 +10,7 @@ # undef __sp_biwise # define __sp_biwise __attribute__((bitwise)) /* sparse seems to get confused by some builtins */ -extern __builtin_ia32_rdrand64_step(unsigned long long *); +extern int __builtin_ia32_rdrand64_step(unsigned long long *); extern unsigned int __builtin_ia32_crc32di(unsigned int, unsigned long long); extern unsigned int __builtin_ia32_crc32si(unsigned int, unsigned int); extern unsigned int __builtin_ia32_crc32hi(unsigned int, unsigned short); From 0770cc8c571abcfd03fcaa06b33aeb34fe0eef0e Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Wed, 3 Jan 2018 14:02:42 -0800 Subject: [PATCH 129/235] scoutfs-utils: support single dirent format Signed-off-by: Zach Brown --- utils/src/format.h | 37 ++++++++---------------- utils/src/ino_path.c | 54 +++++++++++++++++++--------------- utils/src/ioctl.h | 69 +++++++++++++++++++++++++++++--------------- utils/src/key.c | 38 +++++++----------------- utils/src/print.c | 34 ++++------------------ 5 files changed, 104 insertions(+), 128 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index ff3e4872..0509e647 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -266,29 +266,13 @@ struct scoutfs_inode_key { __u8 type; } __packed; -/* value is struct scoutfs_dirent without the name */ +/* value is struct scoutfs_dirent with the name */ struct scoutfs_dirent_key { __u8 zone; __be64 ino; __u8 type; - __u8 name[0]; -} __packed; - -/* value is struct scoutfs_dirent with the name */ -struct scoutfs_readdir_key { - __u8 zone; - __be64 ino; - __u8 type; - __be64 pos; -} __packed; - -/* value is empty */ -struct scoutfs_link_backref_key { - __u8 zone; - __be64 ino; - __u8 type; - __be64 dir_ino; - __u8 name[0]; + __be64 major; + __be64 minor; } __packed; /* key is bytes of encoded block mapping */ @@ -494,13 +478,17 @@ struct scoutfs_inode { #define SCOUTFS_SYMLINK_MAX_SIZE 4096 /* - * Dirents are stored in items with an offset of the hash of their name. - * Colliding names are packed into the value. + * Dirents are stored in multiple places to isolate contention when + * performing different operations: hashed by name for creation and + * lookup, at incrementing positions for readdir and resolving inodes to + * paths. Each entry has all the metadata needed to reference all the + * items (so an entry cached by lookup can be used to unlink all the + * items). */ struct scoutfs_dirent { __le64 ino; - __le64 counter; - __le64 readdir_pos; + __le64 hash; + __le64 pos; __u8 type; __u8 name[0]; } __packed; @@ -526,9 +514,8 @@ enum { SCOUTFS_DT_WHT, }; -/* ino_path can search for backref items with a null term */ #define SCOUTFS_MAX_KEY_SIZE \ - offsetof(struct scoutfs_link_backref_key, name[SCOUTFS_NAME_LEN + 1]) + sizeof(struct scoutfs_dirent_key) #define SCOUTFS_MAX_VAL_SIZE SCOUTFS_BLOCK_MAPPING_MAX_BYTES diff --git a/utils/src/ino_path.c b/utils/src/ino_path.c index f23f3c7c..91cbd184 100644 --- a/utils/src/ino_path.c +++ b/utils/src/ino_path.c @@ -18,9 +18,9 @@ static int ino_path_cmd(int argc, char **argv) { struct scoutfs_ioctl_ino_path args; + struct scoutfs_ioctl_ino_path_result *res; + unsigned int result_bytes; char *endptr = NULL; - char *path = NULL; - char *curs = NULL; u64 ino; int ret; int fd; @@ -38,6 +38,7 @@ static int ino_path_cmd(int argc, char **argv) return -EINVAL; } + fd = open(argv[2], O_RDONLY); if (fd < 0) { ret = -errno; @@ -46,31 +47,39 @@ static int ino_path_cmd(int argc, char **argv) return ret; } - path = malloc(PATH_MAX); - if (!path) { - fprintf(stderr, "couldn't allocate %d byte buffer\n", PATH_MAX); - ret = -ENOMEM; - goto out; - } - - curs = calloc(1, SCOUTFS_IOC_INO_PATH_CURSOR_BYTES); - if (!curs) { - fprintf(stderr, "couldn't allocate %ld byte cursor\n", - SCOUTFS_IOC_INO_PATH_CURSOR_BYTES); + result_bytes = offsetof(struct scoutfs_ioctl_ino_path_result, + path[PATH_MAX]); + res = malloc(result_bytes); + if (!res) { + fprintf(stderr, "couldn't allocate %u byte buffer\n", + result_bytes); ret = -ENOMEM; goto out; } args.ino = ino; - args.cursor_ptr = (intptr_t)curs; - args.path_ptr = (intptr_t)path; - args.cursor_bytes = SCOUTFS_IOC_INO_PATH_CURSOR_BYTES; - args.path_bytes = PATH_MAX; - do { + args.dir_ino = 0; + args.dir_pos = 0; + args.result_ptr = (intptr_t)res; + args.result_bytes = result_bytes; + for (;;) { ret = ioctl(fd, SCOUTFS_IOC_INO_PATH, &args); - if (ret > 0) - printf("%s\n", path); - } while (ret > 0); + if (ret < 0) { + ret = -errno; + if (ret == -ENOENT) + ret = 0; + break; + } + + printf("%.*s\n", res->path_bytes, res->path); + + args.dir_ino = res->dir_ino; + args.dir_pos = res->dir_pos; + if (++args.dir_pos == 0) { + if (++args.dir_ino == 0) + break; + } + } if (ret < 0) { ret = -errno; @@ -78,8 +87,7 @@ static int ino_path_cmd(int argc, char **argv) strerror(errno), errno); } out: - free(path); - free(curs); + free(res); close(fd); return ret; }; diff --git a/utils/src/ioctl.h b/utils/src/ioctl.h index 33c94b95..721f1cde 100644 --- a/utils/src/ioctl.h +++ b/utils/src/ioctl.h @@ -64,24 +64,36 @@ enum { struct scoutfs_ioctl_walk_inodes) /* - * Fill the path buffer with the next path to the target inode. An - * iteration cursor is stored in the cursor buffer which advances - * through the paths to the inode at each call. + * Fill the result buffer with the next absolute path to the target + * inode searching from a given position in a parent directory. * * @ino: The target ino that we're finding paths to. Constant across * all the calls that make up an iteration over all the inode's paths. * - * @cursor_ptr: A pointer to the buffer that will hold the iteration - * cursor. It must be initialized to 0 before iterating. Each call - * modifies it to skip past the result of that call. + * @dir_ino: The inode number of the directory containing the entry to + * our inode to search from. If this parent directory contains no more + * entries to our inode then we'll search through other parent directory + * inodes in inode order. * - * @cusur_bytes: The length of the cursor buffer. Must be - * SCOUTFS_IOC_INO_PATH_CURSOR_BYTES. + * @dir_pos: The position in the dir_ino parent directory of the entry + * to our inode to search from. If there is no entry at this position + * then we'll search through other entry positions in increasing order. + * If we exhaust the parent directory then we'll search through + * additional parent directories in inode order. * - * @path_ptr: The buffer to store each found path. + * @result_ptr: A pointer to the buffer where the result struct and + * absolute path will be stored. * - * @path_bytes: The size of the buffer that will the found path - * including null termination. (PATH_MAX is a solid choice.) + * @result_bytes: The size of the buffer that will contain the result + * struct and the null terminated absolute path name. + * + * To start iterating set the desired target inode, dir_ino to 0, + * dir_pos to 0, and set result_ptr and _bytes to a sufficiently large + * buffeer (sizeof(result) + PATH_MAX is a solid choice). + * + * After each returned result set the next search dir_ino and dir_pos to + * the returned dir_ino and dir_pos. Then increment the search dir_pos, + * and if it wrapped to 0, increment dir_ino. * * This only walks back through full hard links. None of the returned * paths will reflect symlinks to components in the path. @@ -90,28 +102,39 @@ enum { * returned paths to the inode. It requires CAP_DAC_READ_SEARCH which * bypasses permissions checking. * - * ENAMETOOLONG is returned when the next path found from the cursor - * doesn't fit in the path buffer. - * * This call is not serialized with any modification (create, rename, * unlink) of the path components. It will return all the paths that * were stable both before and after the call. It may or may not return * paths which are created or unlinked during the call. * - * The number of bytes in the path, including the null terminator, are - * returned when a path is found. 0 is returned when there are no more - * paths to the link to the inode from the cursor. + * On success 0 is returned and result struct is filled with the next + * absolute path. The path_bytes length of the path includes a null + * terminating byte. dir_ino and dir_pos refer to the position of the + * final component in its parent directory and can be advanced to search + * for the next terminal entry whose path is then built by walking up + * parent directories. + * + * ENOENT is returned when no paths are found. + * + * ENAMETOOLONG is returned when the result struct and path found + * doesn't fit in the result buffer. + * + * Many other errnos indicate hard failure to find the next path. */ struct scoutfs_ioctl_ino_path { __u64 ino; - __u64 cursor_ptr; - __u64 path_ptr; - __u16 cursor_bytes; - __u16 path_bytes; + __u64 dir_ino; + __u64 dir_pos; + __u64 result_ptr; + __u16 result_bytes; } __packed; -#define SCOUTFS_IOC_INO_PATH_CURSOR_BYTES \ - (sizeof(__u64) + SCOUTFS_NAME_LEN + 1) +struct scoutfs_ioctl_ino_path_result { + __u64 dir_ino; + __u64 dir_pos; + __u16 path_bytes; + __u8 path[0]; +} __packed; /* Get a single path from the root to the given inode number */ #define SCOUTFS_IOC_INO_PATH _IOW(SCOUTFS_IOCTL_MAGIC, 2, \ diff --git a/utils/src/key.c b/utils/src/key.c index 7aa14ad4..a32f4442 100644 --- a/utils/src/key.c +++ b/utils/src/key.c @@ -248,35 +248,17 @@ static int pr_xattr(char *buf, struct scoutfs_key_buf *key, size_t size) static int pr_dirent(char *buf, struct scoutfs_key_buf *key, size_t size) { struct scoutfs_dirent_key *dkey = key->data; - int len = (int)key->key_len - sizeof(struct scoutfs_dirent_key); + char *which = dkey->type == SCOUTFS_DIRENT_TYPE ? "dnt" : + dkey->type == SCOUTFS_READDIR_TYPE ? "rdr" : + dkey->type == SCOUTFS_LINK_BACKREF_TYPE ? "lbr" : + "unk"; return snprintf_key(buf, size, key, sizeof(struct scoutfs_dirent_key), key->key_len, - "fs.%llu.dnt.%.*s", - be64_to_cpu(dkey->ino), len, dkey->name); -} - -static int pr_readdir(char *buf, struct scoutfs_key_buf *key, size_t size) -{ - struct scoutfs_readdir_key *rkey = key->data; - - return snprintf_key(buf, size, key, - sizeof(struct scoutfs_readdir_key), 0, - "fs.%llu.rdr.%llu", - be64_to_cpu(rkey->ino), be64_to_cpu(rkey->pos)); -} - -static int pr_link_backref(char *buf, struct scoutfs_key_buf *key, size_t size) -{ - struct scoutfs_link_backref_key *lkey = key->data; - int len = (int)key->key_len - sizeof(*lkey); - - return snprintf_key(buf, size, key, - sizeof(struct scoutfs_link_backref_key), - key->key_len, - "fs.%llu.lbr.%llu.%.*s", - be64_to_cpu(lkey->ino), be64_to_cpu(lkey->dir_ino), - len, lkey->name); + "fs.%llu.%s.%llu.%llu", + be64_to_cpu(dkey->ino), which, + be64_to_cpu(dkey->major), + be64_to_cpu(dkey->minor)); } static int pr_symlink(char *buf, struct scoutfs_key_buf *key, size_t size) @@ -311,8 +293,8 @@ const static key_printer_t key_printers[SCOUTFS_MAX_ZONE][SCOUTFS_MAX_TYPE] = { [SCOUTFS_FS_ZONE][SCOUTFS_INODE_TYPE] = pr_inode, [SCOUTFS_FS_ZONE][SCOUTFS_XATTR_TYPE] = pr_xattr, [SCOUTFS_FS_ZONE][SCOUTFS_DIRENT_TYPE] = pr_dirent, - [SCOUTFS_FS_ZONE][SCOUTFS_READDIR_TYPE] = pr_readdir, - [SCOUTFS_FS_ZONE][SCOUTFS_LINK_BACKREF_TYPE] = pr_link_backref, + [SCOUTFS_FS_ZONE][SCOUTFS_READDIR_TYPE] = pr_dirent, + [SCOUTFS_FS_ZONE][SCOUTFS_LINK_BACKREF_TYPE] = pr_dirent, [SCOUTFS_FS_ZONE][SCOUTFS_SYMLINK_TYPE] = pr_symlink, [SCOUTFS_FS_ZONE][SCOUTFS_BLOCK_MAPPING_TYPE] = pr_block_mapping, }; diff --git a/utils/src/print.c b/utils/src/print.c index f5764949..a53963fa 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -141,40 +141,16 @@ static void print_dirent(void *key, int key_len, void *val, int val_len) { struct scoutfs_dirent_key *dkey = key; struct scoutfs_dirent *dent = val; - unsigned int name_len = key_len - sizeof(*dkey); - u8 *name = global_printable_name(dkey->name, name_len); - - printf(" dirent: dir ino %llu type %u rdpos %llu targ ino %llu\n" - " name %s\n", - be64_to_cpu(dkey->ino), dent->type, - le64_to_cpu(dent->readdir_pos), le64_to_cpu(dent->ino), - name); -} - -static void print_readdir(void *key, int key_len, void *val, int val_len) -{ - struct scoutfs_readdir_key *rkey = key; - struct scoutfs_dirent *dent = val; unsigned int name_len = val_len - sizeof(*dent); u8 *name = global_printable_name(dent->name, name_len); - printf(" readdir: dir ino %llu pos %llu type %u targ ino %llu\n" + printf(" dirent: dir %llu hash %016llx pos %llu type %u ino %llu\n" " name %s\n", - be64_to_cpu(rkey->ino), be64_to_cpu(rkey->pos), - dent->type, le64_to_cpu(dent->ino), + be64_to_cpu(dkey->ino), le64_to_cpu(dent->hash), + le64_to_cpu(dent->pos), dent->type, le64_to_cpu(dent->ino), name); } -static void print_link_backref(void *key, int key_len, void *val, int val_len) -{ - struct scoutfs_link_backref_key *lbkey = key; - unsigned int name_len = key_len - sizeof(*lbkey); - u8 *name = global_printable_name(lbkey->name, name_len); - - printf(" lbref: ino: %llu dir_ino %llu name %s\n", - be64_to_cpu(lbkey->ino), be64_to_cpu(lbkey->dir_ino), name); -} - static void print_symlink(void *key, int key_len, void *val, int val_len) { struct scoutfs_symlink_key *skey = key; @@ -250,9 +226,9 @@ static print_func_t find_printer(u8 zone, u8 type) case SCOUTFS_INODE_TYPE: return print_inode; case SCOUTFS_XATTR_TYPE: return print_xattr; case SCOUTFS_DIRENT_TYPE: return print_dirent; - case SCOUTFS_READDIR_TYPE: return print_readdir; + case SCOUTFS_READDIR_TYPE: return print_dirent; case SCOUTFS_SYMLINK_TYPE: return print_symlink; - case SCOUTFS_LINK_BACKREF_TYPE: return print_link_backref; + case SCOUTFS_LINK_BACKREF_TYPE: return print_dirent; case SCOUTFS_BLOCK_MAPPING_TYPE: return print_block_mapping; } From 65ce5c6ad5a37ccc05c77c0c2b3b590cea22e2da Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Mon, 19 Mar 2018 16:48:22 -0700 Subject: [PATCH 130/235] scoutfs-utils: clean up _MAX defines Signed-off-by: Zach Brown --- utils/src/sparse.h | 2 -- utils/src/util.h | 4 ++++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/utils/src/sparse.h b/utils/src/sparse.h index 34fbbbaa..22628018 100644 --- a/utils/src/sparse.h +++ b/utils/src/sparse.h @@ -34,8 +34,6 @@ typedef u32 __u32; typedef s32 __s32; typedef u64 __u64; -#define U16_MAX ((u16)~0) - typedef u16 __sp_biwise __le16; typedef u16 __sp_biwise __be16; typedef u32 __sp_biwise __le32; diff --git a/utils/src/util.h b/utils/src/util.h index 89ec4b4f..3ed72701 100644 --- a/utils/src/util.h +++ b/utils/src/util.h @@ -61,6 +61,10 @@ do { \ ((type *)((void *)(ptr) - offsetof(type, memb))) #define BITS_PER_LONG (sizeof(long) * 8) +#define U8_MAX ((u8)~0ULL) +#define U16_MAX ((u16)~0ULL) +#define U32_MAX ((u32)~0ULL) +#define U64_MAX ((u64)~0ULL) /* * return -1,0,+1 based on the memcmp comparison of the minimum of their From 837310e8e6c45a70aa53072d2424666c09045175 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Mon, 19 Mar 2018 16:48:55 -0700 Subject: [PATCH 131/235] scoutfs-utils: add le64_add_cpu Signed-off-by: Zach Brown --- utils/src/sparse.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/utils/src/sparse.h b/utils/src/sparse.h index 22628018..16fddc54 100644 --- a/utils/src/sparse.h +++ b/utils/src/sparse.h @@ -109,4 +109,14 @@ static inline void le32_add_cpu(__le32 *val, u32 delta) *val = cpu_to_le32(le32_to_cpu(*val) + delta); } +static inline void le64_add_cpu(__le64 *val, u64 delta) +{ + *val = cpu_to_le64(le64_to_cpu(*val) + delta); +} + +static inline void be64_add_cpu(__be64 *val, u64 delta) +{ + *val = cpu_to_be64(be64_to_cpu(*val) + delta); +} + #endif From 8e6c18a0fa30f3770be8a13d952552becabb4c6d Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Thu, 18 Jan 2018 14:19:15 -0800 Subject: [PATCH 132/235] scoutfs-utils: support small keys Make the changes to support the new small key struct. mkfs and print work with simpler keys, segment items, and manifest entries. The item cache keys ioctl now just needs to work with arrays of keys. Signed-off-by: Zach Brown --- utils/src/cmp.h | 23 +++ utils/src/endian_swap.h | 12 ++ utils/src/format.h | 176 ++++++++--------- utils/src/ioctl.h | 11 +- utils/src/item-cache-keys.c | 63 ++---- utils/src/key.c | 376 ++++-------------------------------- utils/src/key.h | 172 ++++++++++++++++- utils/src/mkfs.c | 69 +++---- utils/src/print.c | 159 +++++---------- utils/src/stage_release.c | 1 + 10 files changed, 422 insertions(+), 640 deletions(-) create mode 100644 utils/src/cmp.h create mode 100644 utils/src/endian_swap.h diff --git a/utils/src/cmp.h b/utils/src/cmp.h new file mode 100644 index 00000000..23c6d8a6 --- /dev/null +++ b/utils/src/cmp.h @@ -0,0 +1,23 @@ +#ifndef _SCOUTFS_CMP_H_ +#define _SCOUTFS_CMP_H_ + +/* + * A generic ternary comparison macro with strict type checking. + */ +#define scoutfs_cmp(a, b) \ +({ \ + __typeof__(a) _a = (a); \ + __typeof__(b) _b = (b); \ + int _ret; \ + \ + (void) (&_a == &_b); \ + _ret = _a < _b ? -1 : _a > _b ? 1 : 0; \ + _ret; \ +}) + +static inline int scoutfs_cmp_u64s(u64 a, u64 b) +{ + return a < b ? -1 : a > b ? 1 : 0; +} + +#endif diff --git a/utils/src/endian_swap.h b/utils/src/endian_swap.h new file mode 100644 index 00000000..e64d119e --- /dev/null +++ b/utils/src/endian_swap.h @@ -0,0 +1,12 @@ +#ifndef _SCOUTFS_ENDIAN_SWAP_H_ +#define _SCOUTFS_ENDIAN_SWAP_H_ + +#define le64_to_be64(x) cpu_to_be64(le64_to_cpu(x)) +#define le32_to_be32(x) cpu_to_be32(le32_to_cpu(x)) +#define le16_to_be16(x) cpu_to_be16(le16_to_cpu(x)) + +#define be64_to_le64(x) cpu_to_le64(be64_to_cpu(x)) +#define be32_to_le32(x) cpu_to_le32(be32_to_cpu(x)) +#define be16_to_le16(x) cpu_to_le16(be16_to_cpu(x)) + +#endif diff --git a/utils/src/format.h b/utils/src/format.h index 0509e647..27e467a5 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -52,6 +52,75 @@ struct scoutfs_block_header { __le64 blkno; } __packed; +/* + * scoutfs identifies all file system metadata items by a small key + * struct. + * + * Each item type maps their logical structures to the fixed fields in + * sort order. This lets us print keys without needing per-type + * formats. + * + * The keys are compared by considering the fields in struct order from + * most to least significant. They are considered a multi precision + * value when navigating the keys in ordered key space. We can + * increment them, subtract them from each other, etc. + */ +struct scoutfs_key { + __u8 sk_zone; + __le64 _sk_first; + __u8 sk_type; + __le64 _sk_second; + __le64 _sk_third; + __u8 _sk_fourth; +}__packed; + +/* inode index */ +#define skii_major _sk_second +#define skii_ino _sk_third + +/* node free bit map */ +#define skf_node_id _sk_first +#define skf_base _sk_second + +/* node orphan inode */ +#define sko_node_id _sk_first +#define sko_ino _sk_second + +/* inode */ +#define ski_ino _sk_first + +/* xattr parts */ +#define skx_ino _sk_first +#define skx_name_hash _sk_second +#define skx_id _sk_third +#define skx_part _sk_fourth + +/* directory entries */ +#define skd_ino _sk_first +#define skd_major _sk_second +#define skd_minor _sk_third + +/* symlink target */ +#define sks_ino _sk_first +#define sks_nr _sk_second + +/* file data mapping */ +#define skm_ino _sk_first +#define skm_base _sk_second + +/* + * The btree still uses memcmp() to compare keys. We should fix that + * before too long. + */ +struct scoutfs_key_be { + __u8 sk_zone; + __be64 _sk_first; + __u8 sk_type; + __be64 _sk_second; + __be64 _sk_third; + __u8 _sk_fourth; +}__packed; + /* * Assert that we'll be able to represent all possible keys with 8 64bit * primary sort values. @@ -143,34 +212,24 @@ struct scoutfs_manifest { } __packed; /* - * Manifest entries are packed into btree keys and values in a very - * fiddly way so that we can sort them with memcmp first by level then - * by their position in the level. First comes the level. + * Manifest entries are split across btree keys and values. Putting + * some entry fields in the value keeps the key smaller and increases + * the fanout of the btree which keeps the tree smaller and reduces + * block IO. * - * Level 0 segments are sorted by their seq so they don't have the first - * segment key in the manifest btree key. Both of their keys are in the - * value. - * - * Level 1 segments are sorted by their first key so their last key is - * in the value. - * - * We go to all this trouble so that we can communicate a version of the - * manifest with one btree root, have dense btree keys which are used as - * seperators in parent blocks, and don't duplicate the large keys in - * the manifest btree key and value. + * The key is made up of the level, first key, and seq. At level 0 + * segments can completely overlap and have identical key ranges but we + * avoid duplicate btree keys by including the unique seq. */ - struct scoutfs_manifest_btree_key { __u8 level; - __u8 bkey[0]; + struct scoutfs_key_be first_key; + __be64 seq; } __packed; struct scoutfs_manifest_btree_val { __le64 segno; - __le64 seq; - __le16 first_key_len; - __le16 last_key_len; - __u8 keys[0]; + struct scoutfs_key last_key; } __packed; #define SCOUTFS_ALLOC_REGION_SHIFT 8 @@ -201,15 +260,12 @@ struct scoutfs_alloc_region_btree_val { * They're not allowed to cross a block boundary. */ struct scoutfs_segment_item { - __le16 key_len; + struct scoutfs_key key; __le16 val_len; __u8 flags; __u8 nr_links; __le32 skip_links[0]; - /* - * __u8 key_bytes[key_len] - * __u8 val_bytes[val_len] - */ + /* __u8 val_bytes[val_len] */ } __packed; #define SCOUTFS_ITEM_FLAG_DELETION (1 << 0) @@ -259,30 +315,6 @@ struct scoutfs_segment_block { #define SCOUTFS_MAX_TYPE 16 /* power of 2 is efficient */ -/* value is struct scoutfs_inode */ -struct scoutfs_inode_key { - __u8 zone; - __be64 ino; - __u8 type; -} __packed; - -/* value is struct scoutfs_dirent with the name */ -struct scoutfs_dirent_key { - __u8 zone; - __be64 ino; - __u8 type; - __be64 major; - __be64 minor; -} __packed; - -/* key is bytes of encoded block mapping */ -struct scoutfs_block_mapping_key { - __u8 zone; - __be64 ino; - __u8 type; - __be64 base; -} __packed; - /* each mapping item describes a fixed number of blocks */ #define SCOUTFS_BLOCK_MAPPING_SHIFT 6 #define SCOUTFS_BLOCK_MAPPING_BLOCKS (1 << SCOUTFS_BLOCK_MAPPING_SHIFT) @@ -328,33 +360,10 @@ struct scoutfs_block_mapping_key { #define SCOUTFS_FREE_BITS_U64S \ DIV_ROUND_UP(SCOUTFS_FREE_BITS_BITS, 64) -struct scoutfs_free_bits_key { - __u8 zone; - __be64 node_id; - __u8 type; - __be64 base; -} __packed; - struct scoutfs_free_bits { __le64 bits[SCOUTFS_FREE_BITS_U64S]; } __packed; -struct scoutfs_orphan_key { - __u8 zone; - __be64 node_id; - __u8 type; - __be64 ino; -} __packed; - -struct scoutfs_xattr_key { - __u8 zone; - __be64 ino; - __u8 type; - __be32 name_hash; - __be64 id; - __u8 part; -} __packed; - /* * The first xattr part item has a header that describes the xattr. The * name and value are then packed into the following bytes in the first @@ -366,27 +375,11 @@ struct scoutfs_xattr { __u8 name[0]; } __packed; -/* size determines nr needed to store full target path in their values */ -struct scoutfs_symlink_key { - __u8 zone; - __be64 ino; - __u8 type; - __u8 nr; -} __packed; - struct scoutfs_betimespec { __be64 sec; __be32 nsec; } __packed; -struct scoutfs_inode_index_key { - __u8 zone; - __u8 type; - __be64 major; - __be32 minor; - __be64 ino; -} __packed; - /* XXX does this exist upstream somewhere? */ #define member_sizeof(TYPE, MEMBER) (sizeof(((TYPE *)0)->MEMBER)) @@ -514,9 +507,6 @@ enum { SCOUTFS_DT_WHT, }; -#define SCOUTFS_MAX_KEY_SIZE \ - sizeof(struct scoutfs_dirent_key) - #define SCOUTFS_MAX_VAL_SIZE SCOUTFS_BLOCK_MAPPING_MAX_BYTES #define SCOUTFS_XATTR_MAX_NAME_LEN 255 @@ -591,8 +581,8 @@ struct scoutfs_net_key_range { struct scoutfs_net_manifest_entry { __le64 segno; __le64 seq; - __le16 first_key_len; - __le16 last_key_len; + struct scoutfs_key first; + struct scoutfs_key last; __u8 level; __u8 keys[0]; } __packed; diff --git a/utils/src/ioctl.h b/utils/src/ioctl.h index 721f1cde..915a130b 100644 --- a/utils/src/ioctl.h +++ b/utils/src/ioctl.h @@ -208,11 +208,16 @@ struct scoutfs_ioctl_stat_more { #define SCOUTFS_IOC_STAT_MORE _IOW(SCOUTFS_IOCTL_MAGIC, 7, \ struct scoutfs_ioctl_stat_more) +/* + * Fills the buffer with either the keys for the cached items or the + * keys for the cached ranges found starting with the given key. The + * number of keys filled in the buffer is returned. When filling range + * keys the returned number will always be a multiple of two. + */ struct scoutfs_ioctl_item_cache_keys { - __u64 key_ptr; - __u64 key_len; + struct scoutfs_key key; __u64 buf_ptr; - __u64 buf_len; + __u16 buf_nr; __u8 which; } __packed; diff --git a/utils/src/item-cache-keys.c b/utils/src/item-cache-keys.c index 704a8cc4..68ca48c5 100644 --- a/utils/src/item-cache-keys.c +++ b/utils/src/item-cache-keys.c @@ -16,47 +16,32 @@ #include "cmd.h" #include "key.h" -#define BUF_SIZE (64 * 1024) - static int item_cache_keys(int argc, char **argv, int which) { struct scoutfs_ioctl_item_cache_keys ick; - unsigned nr; - u16 key_len; - void *buf; - void *ptr; + struct scoutfs_key keys[32]; int ret; int fd; + int i; if (argc != 2) { fprintf(stderr, "too many arguments, only scoutfs path needed"); return -EINVAL; } - buf = malloc(BUF_SIZE); - if (!buf) { - ret = -errno; - fprintf(stderr, "failed to allocate buf: %s (%d)\n", - strerror(errno), errno); - return ret; - } - fd = open(argv[1], O_RDONLY); if (fd < 0) { ret = -errno; fprintf(stderr, "failed to open '%s': %s (%d)\n", argv[1], strerror(errno), errno); - free(buf); return ret; } - ick.buf_ptr = (unsigned long)buf; - ick.buf_len = BUF_SIZE; - ick.key_ptr = 0; - ick.key_len = 0; + memset(&ick, 0, sizeof(ick)); + ick.buf_ptr = (unsigned long)keys; + ick.buf_nr = array_size(keys); ick.which = which; - nr = 1; for (;;) { ret = ioctl(fd, SCOUTFS_IOC_ITEM_CACHE_KEYS, &ick); if (ret < 0) { @@ -68,47 +53,21 @@ static int item_cache_keys(int argc, char **argv, int which) break; } - ptr = (void *)(unsigned long)ick.buf_ptr; + for (i = 0; i < ret; i++) { + printf(SK_FMT, SK_ARG(&keys[i])); - while (ret) { - if (ret < sizeof(key_len)) { - fprintf(stderr, "truncated len: %d\n", ret); - ret = -EINVAL; - break; - } - - memcpy(&key_len, ptr, sizeof(key_len)); - ptr += sizeof(key_len); - ret -= sizeof(key_len); - - if (ret < key_len) { - fprintf(stderr, "key len %d < buffer %d\n", - key_len, ret); - ret = -EINVAL; - break; - } - - print_key(ptr, key_len); if (which == SCOUTFS_IOC_ITEM_CACHE_KEYS_ITEMS || - (nr % 2) == 0) + (i & 1)) printf("\n"); else printf(" - "); - - ick.key_ptr = (unsigned long)ptr; - ick.key_len = key_len; - - ptr += key_len; - ret -= key_len; - - nr++; } - if (ret < 0) - break; + + ick.key = keys[i - 1]; + scoutfs_key_inc(&ick.key); } close(fd); - free(buf); return ret; }; diff --git a/utils/src/key.c b/utils/src/key.c index a32f4442..b19b6cd0 100644 --- a/utils/src/key.c +++ b/utils/src/key.c @@ -1,349 +1,55 @@ -#include +/* + * Copyright (C) 2018 Versity Software, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License v2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ #include -#include -#include -#include +#include +#include #include "sparse.h" #include "util.h" #include "format.h" #include "key.h" -/* - * To print keys we wrap the key snprintf code from the kernel with a - * few support functions. We need a few functions that the kernel has - * that we don't provide, then we implement our printing function by - * allocating a buffer for the formatted output then just printing it. - * - * To update the key printing code from the kernel we just need to make - * scoutfs_key_str_size() static and replace the snprintf call with the - * kernel's "%phN" format with the call to our replacement. - * - * This is not efficient but this isn't a performant path. - */ - -#define min_t(t, a, b) min(a, b) - -struct scoutfs_key_buf { - void *data; - unsigned key_len; +char *scoutfs_zone_strings[SCOUTFS_MAX_ZONE] = { + [SCOUTFS_INODE_INDEX_ZONE] = "ind", + [SCOUTFS_NODE_ZONE] = "nod", + [SCOUTFS_FS_ZONE] = "fs", }; -/* - * like snprintf(buf, size, "%*phN", nr, bytes) in the kernel, but this - * is only called when there's room for the formatted output because - * we've already been through once with a 0 buffer to allocate a buffer - * for the output. - */ -static int snprintf_phN(char *buf, size_t size, unsigned nr, char *bytes) +char *scoutfs_type_strings[SCOUTFS_MAX_ZONE][SCOUTFS_MAX_TYPE] = { + [SCOUTFS_INODE_INDEX_ZONE][SCOUTFS_INODE_INDEX_META_SEQ_TYPE] = "msq", + [SCOUTFS_INODE_INDEX_ZONE][SCOUTFS_INODE_INDEX_DATA_SEQ_TYPE] = "dsq", + [SCOUTFS_NODE_ZONE][SCOUTFS_FREE_BITS_SEGNO_TYPE] = "fsg", + [SCOUTFS_NODE_ZONE][SCOUTFS_FREE_BITS_BLKNO_TYPE] = "fbk", + [SCOUTFS_NODE_ZONE][SCOUTFS_ORPHAN_TYPE] = "orp", + [SCOUTFS_FS_ZONE][SCOUTFS_INODE_TYPE] = "ino", + [SCOUTFS_FS_ZONE][SCOUTFS_XATTR_TYPE] = "xat", + [SCOUTFS_FS_ZONE][SCOUTFS_DIRENT_TYPE] = "dnt", + [SCOUTFS_FS_ZONE][SCOUTFS_READDIR_TYPE] = "rdr", + [SCOUTFS_FS_ZONE][SCOUTFS_LINK_BACKREF_TYPE] = "lbr", + [SCOUTFS_FS_ZONE][SCOUTFS_SYMLINK_TYPE] = "sym", + [SCOUTFS_FS_ZONE][SCOUTFS_BLOCK_MAPPING_TYPE] = "bmp", +}; + +char scoutfs_unknown_u8_strings[U8_MAX][U8_STR_MAX]; + +static void __attribute__((constructor)) scoutfs_key_init(void) { - int ret = 0; + int ret; int i; - for (i = 0; i < nr; i++) - ret += sprintf(buf + ret, "%02x", bytes[i]); - - return ret; -} - -static char *memchr_inv(char *str, int c, size_t len) -{ - while (len--) { - if (*(str++) != c) - return str - 1; - } - - return NULL; -} - -static int scoutfs_key_str_size(char *buf, struct scoutfs_key_buf *key, - size_t size); - -void print_key(void *key_data, unsigned key_len) -{ - struct scoutfs_key_buf key = {.data = key_data, .key_len = key_len}; - char *buf; - int size; - - size = scoutfs_key_str_size(NULL, &key, 0); - if (size > 0) { - buf = malloc(size); - if (buf) { - size = scoutfs_key_str_size(buf, &key, size); - if (size > 0) - printf("%s", buf); - free(buf); - } + for (i = 0; i <= U8_MAX; i++) { + ret = snprintf(scoutfs_unknown_u8_strings[i], U8_STR_MAX, + "u%u", i); + assert(ret > 0 && ret < U8_STR_MAX); } } - -/* ------ copied code follows --------- */ - -#define snprintf_null(buf, size, fmt, args...) \ - (snprintf((buf), (size), fmt, ##args) + 1) - -/* - * Store a formatted string representing the key in the buffer. The key - * must be at least min_len to store the data needed by the format at - * all. fmt_len is the length of data that's used by the format. These - * are different because we have badly designed keys with variable - * length data that isn't described by the key. It's assumed from the - * length of the key. Take dirents -- they need to at least have a - * dirent struct, but the name length is the rest of the key. - * - * (XXX And this goes horribly wrong when we pad out dirent keys to max - * len to increment at high precision. We'll never see these items used - * by real fs code, but temporary keys and range endpoints can be full - * precision and we can try and print them and get very confused. We - * need to rev the format to include explicit lengths.) - * - * If the format doesn't cover the entire key then we append more - * formatting to represent the trailing bytes: runs of zeros compresesd - * to _ and then hex output of non-zero bytes. - */ -static int snprintf_key(char *buf, size_t size, struct scoutfs_key_buf *key, - unsigned min_len, unsigned fmt_len, - const char *fmt, ...) - -{ - va_list args; - char *data; - char *end; - int left; - int part; - int ret; - int nr; - - if (key->key_len < min_len) - return snprintf_null(buf, size, "[trunc len %u < min %u]", - key->key_len, min_len); - - if (fmt_len == 0) - fmt_len = min_len; - - va_start(args, fmt); - ret = vsnprintf(buf, size, fmt, args); - va_end(args); - /* next formatting overwrites null */ - if (buf) { - buf += ret; - size -= min_t(int, size, ret); - } - - data = key->data + fmt_len; - left = key->key_len - fmt_len; - - while (left && (!buf || size > 1)) { - /* compress runs of zero bytes to _ */ - end = memchr_inv(data, 0, left); - nr = end ? end - data : left; - if (nr) { - if (buf) { - *(buf++) = '_'; - size--; - } - ret++; - data += nr; - left -= nr; - continue; - } - - /* - * hex print non-zero bytes. %ph is limited to 64 bytes - * and is buggy in that it still tries to print to buf - * past size. (so buf = null, size = 0 crashes instead - * of printing the length of the formatted string.) - */ - end = memchr(data, 0, left); - nr = end ? end - data : left; - nr = min(nr, 64); - - if (buf) - part = snprintf_phN(buf, size, nr, data); - else - part = nr * 2; - if (buf) { - buf += part; - size -= min_t(int, size, part); - } - ret += part; - - data += nr; - left -= nr; - } - - /* always store and include null */ - if (buf) - *buf = '\0'; - return ret + 1; -} - -typedef int (*key_printer_t)(char *buf, struct scoutfs_key_buf *key, - size_t size); - -static int pr_ino_idx(char *buf, struct scoutfs_key_buf *key, size_t size) -{ - static char *type_strings[] = { - [SCOUTFS_INODE_INDEX_META_SEQ_TYPE] = "msq", - [SCOUTFS_INODE_INDEX_DATA_SEQ_TYPE] = "dsq", - }; - struct scoutfs_inode_index_key *ikey = key->data; - - return snprintf_key(buf, size, key, - sizeof(struct scoutfs_inode_index_key), 0, - "iin.%s.%llu.%u.%llu", - type_strings[ikey->type], be64_to_cpu(ikey->major), - be32_to_cpu(ikey->minor), be64_to_cpu(ikey->ino)); -} - -static int pr_free_bits(char *buf, struct scoutfs_key_buf *key, size_t size) -{ - static char *type_strings[] = { - [SCOUTFS_FREE_BITS_SEGNO_TYPE] = "fsg", - [SCOUTFS_FREE_BITS_BLKNO_TYPE] = "fbk", - }; - struct scoutfs_free_bits_key *frk = key->data; - - return snprintf_key(buf, size, key, - sizeof(struct scoutfs_block_mapping_key), 0, - "nod.%llu.%s.%llu", - be64_to_cpu(frk->node_id), - type_strings[frk->type], - be64_to_cpu(frk->base)); -} - -static int pr_orphan(char *buf, struct scoutfs_key_buf *key, size_t size) -{ - struct scoutfs_orphan_key *okey = key->data; - - return snprintf_key(buf, size, key, - sizeof(struct scoutfs_orphan_key), 0, - "nod.%llu.orp.%llu", - be64_to_cpu(okey->node_id), - be64_to_cpu(okey->ino)); -} - -static int pr_inode(char *buf, struct scoutfs_key_buf *key, size_t size) -{ - struct scoutfs_inode_key *ikey = key->data; - - return snprintf_key(buf, size, key, - sizeof(struct scoutfs_inode_key), 0, - "fs.%llu.ino", - be64_to_cpu(ikey->ino)); -} - -static int pr_xattr(char *buf, struct scoutfs_key_buf *key, size_t size) -{ - struct scoutfs_xattr_key *xkey = key->data; - - return snprintf_key(buf, size, key, - sizeof(struct scoutfs_xattr_key), key->key_len, - "fs.%llu.xat.%08x.%llu.%u", - be64_to_cpu(xkey->ino), - be32_to_cpu(xkey->name_hash), - be64_to_cpu(xkey->id), xkey->part); -} - -static int pr_dirent(char *buf, struct scoutfs_key_buf *key, size_t size) -{ - struct scoutfs_dirent_key *dkey = key->data; - char *which = dkey->type == SCOUTFS_DIRENT_TYPE ? "dnt" : - dkey->type == SCOUTFS_READDIR_TYPE ? "rdr" : - dkey->type == SCOUTFS_LINK_BACKREF_TYPE ? "lbr" : - "unk"; - - return snprintf_key(buf, size, key, - sizeof(struct scoutfs_dirent_key), key->key_len, - "fs.%llu.%s.%llu.%llu", - be64_to_cpu(dkey->ino), which, - be64_to_cpu(dkey->major), - be64_to_cpu(dkey->minor)); -} - -static int pr_symlink(char *buf, struct scoutfs_key_buf *key, size_t size) -{ - struct scoutfs_symlink_key *skey = key->data; - - return snprintf_key(buf, size, key, - sizeof(struct scoutfs_symlink_key), 0, - "fs.%llu.sym", - be64_to_cpu(skey->ino)); -} - -static int pr_block_mapping(char *buf, struct scoutfs_key_buf *key, size_t size) -{ - struct scoutfs_block_mapping_key *bmk = key->data; - - return snprintf_key(buf, size, key, - sizeof(struct scoutfs_block_mapping_key), 0, - "fs.%llu.bmp.%llu", - be64_to_cpu(bmk->ino), - be64_to_cpu(bmk->base)); -} - -const static key_printer_t key_printers[SCOUTFS_MAX_ZONE][SCOUTFS_MAX_TYPE] = { - [SCOUTFS_INODE_INDEX_ZONE][SCOUTFS_INODE_INDEX_META_SEQ_TYPE] = - pr_ino_idx, - [SCOUTFS_INODE_INDEX_ZONE][SCOUTFS_INODE_INDEX_DATA_SEQ_TYPE] = - pr_ino_idx, - [SCOUTFS_NODE_ZONE][SCOUTFS_FREE_BITS_SEGNO_TYPE] = pr_free_bits, - [SCOUTFS_NODE_ZONE][SCOUTFS_FREE_BITS_BLKNO_TYPE] = pr_free_bits, - [SCOUTFS_NODE_ZONE][SCOUTFS_ORPHAN_TYPE] = pr_orphan, - [SCOUTFS_FS_ZONE][SCOUTFS_INODE_TYPE] = pr_inode, - [SCOUTFS_FS_ZONE][SCOUTFS_XATTR_TYPE] = pr_xattr, - [SCOUTFS_FS_ZONE][SCOUTFS_DIRENT_TYPE] = pr_dirent, - [SCOUTFS_FS_ZONE][SCOUTFS_READDIR_TYPE] = pr_dirent, - [SCOUTFS_FS_ZONE][SCOUTFS_LINK_BACKREF_TYPE] = pr_dirent, - [SCOUTFS_FS_ZONE][SCOUTFS_SYMLINK_TYPE] = pr_symlink, - [SCOUTFS_FS_ZONE][SCOUTFS_BLOCK_MAPPING_TYPE] = pr_block_mapping, -}; - -/* - * Write the null-terminated string that describes the key to the - * buffer. The bytes copied (including the null) is returned. A null - * buffer can be used to find the string size without writing anything. - * - * XXX nonprintable characters in the trace? - */ -static int scoutfs_key_str_size(char *buf, struct scoutfs_key_buf *key, - size_t size) -{ - u8 zone; - u8 type; - - if (key == NULL || key->data == NULL) - return snprintf_null(buf, size, "[NULL]"); - - /* always at least zone, some id, and type */ - if (key->key_len < (1 + 8 + 1)) - return snprintf_null(buf, size, "[trunc len %u]", key->key_len); - - zone = *(u8 *)key->data; - - /* - * each zone's keys always start with the same fields that let - * us deref any key to get the type. We chose a few representative - * keys from each zone to get the type. - */ - if (zone == SCOUTFS_INODE_INDEX_ZONE) { - struct scoutfs_inode_index_key *ikey = key->data; - type = ikey->type; - } else if (zone == SCOUTFS_NODE_ZONE) { - struct scoutfs_free_bits_key *fbk = key->data; - type = fbk->type; - } else if (zone == SCOUTFS_FS_ZONE) { - struct scoutfs_inode_key *ikey = key->data; - type = ikey->type; - } else { - type = 255; - } - - if (zone > SCOUTFS_MAX_ZONE || type > SCOUTFS_MAX_TYPE || - key_printers[zone][type] == NULL) { - return snprintf_null(buf, size, "[unk zone %u type %u]", - zone, type); - } - - return key_printers[zone][type](buf, key, size); -} diff --git a/utils/src/key.h b/utils/src/key.h index bb51b80f..9401286f 100644 --- a/utils/src/key.h +++ b/utils/src/key.h @@ -1,6 +1,172 @@ -#ifndef _KEY_H_ -#define _KEY_H_ +#ifndef _SCOUTFS_KEY_H_ +#define _SCOUTFS_KEY_H_ -void print_key(void *key_data, unsigned key_len); +#include "sparse.h" +#include "util.h" +#include "format.h" +#include "cmp.h" +#include "endian_swap.h" + +extern char *scoutfs_zone_strings[SCOUTFS_MAX_ZONE]; +extern char *scoutfs_type_strings[SCOUTFS_MAX_ZONE][SCOUTFS_MAX_TYPE]; +#define U8_STR_MAX 5 /* u%3u'\0' */ +extern char scoutfs_unknown_u8_strings[U8_MAX][U8_STR_MAX]; + +static inline char *sk_zone_str(u8 zone) +{ + if (zone >= SCOUTFS_MAX_ZONE || scoutfs_zone_strings[zone] == NULL) + return scoutfs_unknown_u8_strings[zone]; + + return scoutfs_zone_strings[zone]; +} + +static inline char *sk_type_str(u8 zone, u8 type) +{ + if (zone >= SCOUTFS_MAX_ZONE || type >= SCOUTFS_MAX_TYPE || + scoutfs_type_strings[zone][type] == NULL) + return scoutfs_unknown_u8_strings[type]; + + return scoutfs_type_strings[zone][type]; +} + +#define SK_FMT "%s.%llu.%s.%llu.%llu.%u" +/* This does not support null keys */ +#define SK_ARG(key) sk_zone_str((key)->sk_zone), \ + le64_to_cpu((key)->_sk_first), \ + sk_type_str((key)->sk_zone, (key)->sk_type), \ + le64_to_cpu((key)->_sk_second), \ + le64_to_cpu((key)->_sk_third), \ + (key)->_sk_fourth + +static inline void scoutfs_key_set_zeros(struct scoutfs_key *key) +{ + key->sk_zone = 0; + key->_sk_first = 0; + key->sk_type = 0; + key->_sk_second = 0; + key->_sk_third = 0; + key->_sk_fourth = 0; +} + +static inline void scoutfs_key_copy_or_zeros(struct scoutfs_key *dst, + struct scoutfs_key *src) +{ + if (src) + *dst = *src; + else + scoutfs_key_set_zeros(dst); +} + +static inline void scoutfs_key_set_ones(struct scoutfs_key *key) +{ + key->sk_zone = U8_MAX; + key->_sk_first = cpu_to_le64(U64_MAX); + key->sk_type = U8_MAX; + key->_sk_second = cpu_to_le64(U64_MAX); + key->_sk_third = cpu_to_le64(U64_MAX); + key->_sk_fourth = U8_MAX; +} + +/* + * Return a -1/0/1 comparison of keys. + * + * It turns out that these ternary chains are consistently cheaper than + * other alternatives across keys that first differ in any of the + * values. Say maybe 20% faster than memcmp. + */ +static inline int scoutfs_key_compare(struct scoutfs_key *a, + struct scoutfs_key *b) +{ + return scoutfs_cmp(a->sk_zone, b->sk_zone) ?: + scoutfs_cmp(le64_to_cpu(a->_sk_first), le64_to_cpu(b->_sk_first)) ?: + scoutfs_cmp(a->sk_type, b->sk_type) ?: + scoutfs_cmp(le64_to_cpu(a->_sk_second), le64_to_cpu(b->_sk_second)) ?: + scoutfs_cmp(le64_to_cpu(a->_sk_third), le64_to_cpu(b->_sk_third)) ?: + scoutfs_cmp(a->_sk_fourth, b->_sk_fourth); +} + +/* + * Compare ranges of keys where overlapping is equality. Returns: + * -1: a_end < b_start + * 1: a_start > b_end + * else 0: ranges overlap + */ +static inline int scoutfs_key_compare_ranges(struct scoutfs_key *a_start, + struct scoutfs_key *a_end, + struct scoutfs_key *b_start, + struct scoutfs_key *b_end) +{ + return scoutfs_key_compare(a_end, b_start) < 0 ? -1 : + scoutfs_key_compare(a_start, b_end) > 0 ? 1 : + 0; +} + +static inline void scoutfs_key_inc(struct scoutfs_key *key) +{ + if (++key->_sk_fourth != 0) + return; + + le64_add_cpu(&key->_sk_third, 1); + if (key->_sk_third != 0) + return; + + le64_add_cpu(&key->_sk_second, 1); + if (key->_sk_second != 0) + return; + + if (++key->sk_type != 0) + return; + + le64_add_cpu(&key->_sk_first, 1); + if (key->_sk_first != 0) + return; + + key->sk_zone++; +} + +static inline void scoutfs_key_dec(struct scoutfs_key *key) +{ + if (--key->_sk_fourth != U8_MAX) + return; + + le64_add_cpu(&key->_sk_third, -1); + if (key->_sk_third != cpu_to_le64(U64_MAX)) + return; + + le64_add_cpu(&key->_sk_second, -1); + if (key->_sk_second != cpu_to_le64(U64_MAX)) + return; + + if (--key->sk_type != U8_MAX) + return; + + le64_add_cpu(&key->_sk_first, -1); + if (key->_sk_first != cpu_to_le64(U64_MAX)) + return; + + key->sk_zone--; +} + +static inline void scoutfs_key_to_be(struct scoutfs_key_be *be, + struct scoutfs_key *key) +{ + be->sk_zone = key->sk_zone; + be->_sk_first = le64_to_be64(key->_sk_first); + be->sk_type = key->sk_type; + be->_sk_second = le64_to_be64(key->_sk_second); + be->_sk_third = le64_to_be64(key->_sk_third); + be->_sk_fourth = key->_sk_fourth; +} + +static inline void scoutfs_key_from_be(struct scoutfs_key *key, + struct scoutfs_key_be *be) +{ + key->sk_zone = be->sk_zone; + key->_sk_first = be64_to_le64(be->_sk_first); + key->sk_type = be->sk_type; + key->_sk_second = be64_to_le64(be->_sk_second); + key->_sk_third = be64_to_le64(be->_sk_third); + key->_sk_fourth = be->_sk_fourth; +} #endif diff --git a/utils/src/mkfs.c b/utils/src/mkfs.c index 96497e47..4b357487 100644 --- a/utils/src/mkfs.c +++ b/utils/src/mkfs.c @@ -17,6 +17,7 @@ #include "crc.h" #include "rand.h" #include "dev.h" +#include "key.h" static int write_raw_block(int fd, u64 blkno, void *blk) { @@ -107,10 +108,8 @@ static u64 calc_btree_ring_blocks(u64 total_segs) sizeof(struct scoutfs_alloc_region_btree_val)); blocks += calc_btree_blocks(total_segs, - sizeof(struct scoutfs_manifest_btree_key) + - SCOUTFS_MAX_KEY_SIZE, - sizeof(struct scoutfs_manifest_btree_val) + - SCOUTFS_MAX_KEY_SIZE); + sizeof(struct scoutfs_manifest_btree_key), + sizeof(struct scoutfs_manifest_btree_val)); return round_up(blocks * 4, SCOUTFS_SEGMENT_BLOCKS); } @@ -153,8 +152,8 @@ static char *size_str(u64 nr, unsigned size) static int write_new_fs(char *path, int fd) { struct scoutfs_super_block *super; - struct scoutfs_inode_key *ikey; - struct scoutfs_inode_index_key *idx_key; + struct scoutfs_key *ino_key; + struct scoutfs_key *idx_key; struct scoutfs_inode *inode; struct scoutfs_segment_block *sblk; struct scoutfs_manifest_btree_key *mkey; @@ -162,6 +161,7 @@ static int write_new_fs(char *path, int fd) struct scoutfs_btree_block *bt; struct scoutfs_btree_item *btitem; struct scoutfs_segment_item *item; + struct scoutfs_key key; __le32 *prev_link; struct timeval tv; char uuid_str[37]; @@ -245,34 +245,29 @@ static int write_new_fs(char *path, int fd) bt->nr_items = cpu_to_le16(1); /* btree item allocated from the back of the block */ - ikey = (void *)bt + SCOUTFS_BLOCK_SIZE - sizeof(*ikey); - mval = (void *)ikey - sizeof(*mval); - idx_key = (void *)mval - sizeof(*idx_key); - mkey = (void *)idx_key - sizeof(*mkey); + mval = (void *)bt + SCOUTFS_BLOCK_SIZE - sizeof(*mval); + ino_key = &mval->last_key; + mkey = (void *)mval - sizeof(*mkey); btitem = (void *)mkey - 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(struct scoutfs_manifest_btree_key) + - sizeof(struct scoutfs_inode_index_key)); - btitem->val_len = cpu_to_le16(sizeof(struct scoutfs_manifest_btree_val) + - sizeof(struct scoutfs_inode_key)); + btitem->key_len = cpu_to_le16(sizeof(*mkey)); + btitem->val_len = cpu_to_le16(sizeof(*mval)); mkey->level = 1; - idx_key->zone = SCOUTFS_INODE_INDEX_ZONE; - idx_key->type = SCOUTFS_INODE_INDEX_META_SEQ_TYPE; - idx_key->major = 0; - idx_key->minor = 0; - idx_key->ino = cpu_to_be64(SCOUTFS_ROOT_INO); + mkey->seq = cpu_to_be64(1); + memset(&key, 0, sizeof(key)); + key.sk_zone = SCOUTFS_INODE_INDEX_ZONE; + key.sk_type = SCOUTFS_INODE_INDEX_META_SEQ_TYPE; + key.skii_ino = cpu_to_le64(SCOUTFS_ROOT_INO); + scoutfs_key_to_be(&mkey->first_key, &key); mval->segno = cpu_to_le64(first_segno); - mval->seq = cpu_to_le64(1); - mval->first_key_len = cpu_to_le16(sizeof(struct scoutfs_inode_index_key)); - mval->last_key_len = cpu_to_le16(sizeof(struct scoutfs_inode_key)); - ikey->zone = SCOUTFS_FS_ZONE; - ikey->ino = cpu_to_be64(SCOUTFS_ROOT_INO); - ikey->type = SCOUTFS_INODE_TYPE; + ino_key->sk_zone = SCOUTFS_FS_ZONE; + ino_key->ski_ino = cpu_to_le64(SCOUTFS_ROOT_INO); + ino_key->sk_type = SCOUTFS_INODE_TYPE; bt->crc = cpu_to_le32(crc_btree_block(bt)); @@ -294,35 +289,31 @@ static int write_new_fs(char *path, int fd) *prev_link = cpu_to_le32((long)item -(long)sblk); prev_link = &item->skip_links[0]; - item->key_len = cpu_to_le16(sizeof(*idx_key)); item->val_len = 0; item->nr_links = 1; le32_add_cpu(&sblk->nr_items, 1); - idx_key = (void *)&item->skip_links[1]; - idx_key->zone = SCOUTFS_INODE_INDEX_ZONE; - idx_key->type = SCOUTFS_INODE_INDEX_META_SEQ_TYPE; - idx_key->ino = cpu_to_be64(SCOUTFS_ROOT_INO); - idx_key->major = 0; - idx_key->minor = 0; + idx_key = &item->key; + idx_key->sk_zone = SCOUTFS_INODE_INDEX_ZONE; + idx_key->sk_type = SCOUTFS_INODE_INDEX_META_SEQ_TYPE; + idx_key->skii_ino = cpu_to_le64(SCOUTFS_ROOT_INO); - item = (void *)(idx_key + 1); + item = (void *)&item->skip_links[1]; *prev_link = cpu_to_le32((long)item -(long)sblk); prev_link = &item->skip_links[0]; sblk->last_item_off = cpu_to_le32((long)item - (long)sblk); - ikey = (void *)&item->skip_links[1]; - inode = (void *)ikey + sizeof(struct scoutfs_inode_key); + ino_key = (void *)&item->key; + inode = (void *)&item->skip_links[1]; - item->key_len = cpu_to_le16(sizeof(struct scoutfs_inode_key)); item->val_len = cpu_to_le16(sizeof(struct scoutfs_inode)); item->nr_links = 1; le32_add_cpu(&sblk->nr_items, 1); - ikey->zone = SCOUTFS_FS_ZONE; - ikey->ino = cpu_to_be64(SCOUTFS_ROOT_INO); - ikey->type = SCOUTFS_INODE_TYPE; + ino_key->sk_zone = SCOUTFS_FS_ZONE; + ino_key->ski_ino = cpu_to_le64(SCOUTFS_ROOT_INO); + ino_key->sk_type = SCOUTFS_INODE_TYPE; inode->next_readdir_pos = cpu_to_le64(2); inode->nlink = cpu_to_le32(SCOUTFS_DIRENT_FIRST_POS); diff --git a/utils/src/print.c b/utils/src/print.c index a53963fa..02e125e5 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -74,9 +74,8 @@ static void print_block_header(struct scoutfs_block_header *hdr) le64_to_cpu(hdr->seq), le64_to_cpu(hdr->blkno)); } -static void print_inode(void *key, int key_len, void *val, int val_len) +static void print_inode(struct scoutfs_key *key, void *val, int val_len) { - struct scoutfs_inode_key *ikey = key; struct scoutfs_inode *inode = val; printf(" inode: ino %llu size %llu nlink %u\n" @@ -84,7 +83,7 @@ static void print_inode(void *key, int key_len, void *val, int val_len) " next_readdir_pos %llu meta_seq %llu data_seq %llu data_version %llu\n" " atime %llu.%08u ctime %llu.%08u\n" " mtime %llu.%08u\n", - be64_to_cpu(ikey->ino), + le64_to_cpu(key->ski_ino), le64_to_cpu(inode->size), le32_to_cpu(inode->nlink), le32_to_cpu(inode->uid), le32_to_cpu(inode->gid), le32_to_cpu(inode->mode), @@ -102,11 +101,9 @@ static void print_inode(void *key, int key_len, void *val, int val_len) le32_to_cpu(inode->mtime.nsec)); } -static void print_orphan(void *key, int key_len, void *val, int val_len) +static void print_orphan(struct scoutfs_key *key, void *val, int val_len) { - struct scoutfs_orphan_key *okey = key; - - printf(" orphan: ino %llu\n", be64_to_cpu(okey->ino)); + printf(" orphan: ino %llu\n", le64_to_cpu(key->sko_ino)); } static u8 *global_printable_name(u8 *name, int name_len) @@ -122,38 +119,35 @@ static u8 *global_printable_name(u8 *name, int name_len) return name_buf; } -static void print_xattr(void *key, int key_len, void *val, int val_len) +static void print_xattr(struct scoutfs_key *key, void *val, int val_len) { - struct scoutfs_xattr_key *xkey = key; struct scoutfs_xattr *xat = val; printf(" xattr: ino %llu name_hash %08x id %llu part %u\n", - be64_to_cpu(xkey->ino), be32_to_cpu(xkey->name_hash), - be64_to_cpu(xkey->id), xkey->part); + le64_to_cpu(key->skx_ino), (u32)le64_to_cpu(key->skx_name_hash), + le64_to_cpu(key->skx_id), key->skx_part); - if (xkey->part == 0) + if (key->skx_part == 0) printf(" name_len %u val_len %u name %s\n", xat->name_len, le16_to_cpu(xat->val_len), global_printable_name(xat->name, xat->name_len)); } -static void print_dirent(void *key, int key_len, void *val, int val_len) +static void print_dirent(struct scoutfs_key *key, void *val, int val_len) { - struct scoutfs_dirent_key *dkey = key; struct scoutfs_dirent *dent = val; unsigned int name_len = val_len - sizeof(*dent); u8 *name = global_printable_name(dent->name, name_len); printf(" dirent: dir %llu hash %016llx pos %llu type %u ino %llu\n" " name %s\n", - be64_to_cpu(dkey->ino), le64_to_cpu(dent->hash), + le64_to_cpu(key->skd_ino), le64_to_cpu(dent->hash), le64_to_cpu(dent->pos), dent->type, le64_to_cpu(dent->ino), name); } -static void print_symlink(void *key, int key_len, void *val, int val_len) +static void print_symlink(struct scoutfs_key *key, void *val, int val_len) { - struct scoutfs_symlink_key *skey = key; u8 *frag = val; u8 *name; @@ -162,32 +156,30 @@ static void print_symlink(void *key, int key_len, void *val, int val_len) val_len--; name = global_printable_name(frag, val_len); - printf(" symlink: ino %llu nr %u\n" + printf(" symlink: ino %llu nr %llu\n" " target %s\n", - be64_to_cpu(skey->ino), skey->nr, name); + le64_to_cpu(key->sks_ino), le64_to_cpu(key->sks_nr), name); } /* * XXX not decoding the bytes yet */ -static void print_block_mapping(void *key, int key_len, void *val, int val_len) +static void print_block_mapping(struct scoutfs_key *key, void *val, int val_len) { - struct scoutfs_block_mapping_key *bmk = key; - u64 blk_off = be64_to_cpu(bmk->base) << SCOUTFS_BLOCK_MAPPING_SHIFT; + u64 blk_off = le64_to_cpu(key->skm_base) << SCOUTFS_BLOCK_MAPPING_SHIFT; u8 nr = *((u8 *)val) & 63; printf(" block mapping: ino %llu blk_off %llu blocks %u\n", - be64_to_cpu(bmk->ino), blk_off, nr); + le64_to_cpu(key->skm_ino), blk_off, nr); } -static void print_free_bits(void *key, int key_len, void *val, int val_len) +static void print_free_bits(struct scoutfs_key *key, void *val, int val_len) { - struct scoutfs_free_bits_key *fbk = key; struct scoutfs_free_bits *frb = val; int i; printf(" node_id %llx base %llu\n", - be64_to_cpu(fbk->node_id), be64_to_cpu(fbk->base)); + le64_to_cpu(key->skf_node_id), le64_to_cpu(key->skf_base)); printf(" bits:"); for (i = 0; i < array_size(frb->bits); i++) @@ -195,16 +187,13 @@ static void print_free_bits(void *key, int key_len, void *val, int val_len) printf("\n"); } -static void print_inode_index(void *key, int key_len, void *val, int val_len) +static void print_inode_index(struct scoutfs_key *key, void *val, int val_len) { - struct scoutfs_inode_index_key *ikey = key; - - printf(" index: major %llu minor %u ino %llu\n", - be64_to_cpu(ikey->major), be32_to_cpu(ikey->minor), - be64_to_cpu(ikey->ino)); + printf(" index: major %llu ino %llu\n", + le64_to_cpu(key->skii_major), le64_to_cpu(key->skii_ino)); } -typedef void (*print_func_t)(void *key, int key_len, void *val, int val_len); +typedef void (*print_func_t)(struct scoutfs_key *key, void *val, int val_len); static print_func_t find_printer(u8 zone, u8 type) { @@ -237,59 +226,28 @@ static print_func_t find_printer(u8 zone, u8 type) return NULL; } -static void find_zone_type(void *key, u8 *zone, u8 *type) -{ - struct scoutfs_inode_index_key *idx_key = key; - struct scoutfs_inode_key *ikey = key; - struct scoutfs_orphan_key *okey = key; - - *zone = *(u8 *)key; - - switch (*zone) { - case SCOUTFS_INODE_INDEX_ZONE: - *type = idx_key->type; - break; - case SCOUTFS_NODE_ZONE: - *type = okey->type; - break; - case SCOUTFS_FS_ZONE: - *type = ikey->type; - break; - default: - *type = 0; - } -} - static void print_item(struct scoutfs_segment_block *sblk, struct scoutfs_segment_item *item, u32 which, u32 off) { print_func_t printer; - void *key; void *val; - u8 type; - u8 zone; int i; - key = (char *)&item->skip_links[item->nr_links]; - val = (char *)key + le16_to_cpu(item->key_len); + val = (char *)&item->skip_links[item->nr_links]; - find_zone_type(key, &zone, &type); - printer = find_printer(zone, type); + printer = find_printer(item->key.sk_zone, item->key.sk_type); - printf(" [%u]: off %u key_len %u val_len %u nr_links %u flags %x%s\n", - which, off, le16_to_cpu(item->key_len), - le16_to_cpu(item->val_len), item->nr_links, + printf(" [%u]: key "SK_FMT" off %u val_len %u nr_links %u flags %x%s\n", + which, SK_ARG(&item->key), off, le16_to_cpu(item->val_len), + item->nr_links, item->flags, printer ? "" : " (unrecognized zone+type)"); printf(" links:"); for (i = 0; i < item->nr_links; i++) printf(" %u", le32_to_cpu(item->skip_links[i])); - printf("\n key: "); - print_key(key, le16_to_cpu(item->key_len)); printf("\n"); if (printer) - printer(key, le16_to_cpu(item->key_len), - val, le16_to_cpu(item->val_len)); + printer(&item->key, val, le16_to_cpu(item->val_len)); } static void print_segment_block(struct scoutfs_segment_block *sblk) @@ -341,51 +299,22 @@ static int print_manifest_entry(void *key, unsigned key_len, void *val, { struct scoutfs_manifest_btree_key *mkey = key; struct scoutfs_manifest_btree_val *mval = val; + struct scoutfs_key first; unsigned long *seg_map = arg; - unsigned first_len; - unsigned last_len; - void *first; - void *last; - __be64 seq; - /* parent items only have the key */ - if (val == NULL) { - if (mkey->level == 0) { - memcpy(&seq, mkey->bkey, sizeof(seq)); - printf(" level %u seq %llu\n", - mkey->level, be64_to_cpu(seq)); - } else { - printf(" level %u first ", mkey->level); - print_key(mkey->bkey, key_len - sizeof(mkey->level)); - printf("\n"); - } - return 0; + scoutfs_key_from_be(&first, &mkey->first_key); + + printf(" level %u first "SK_FMT" seq %llu\n", + mkey->level, SK_ARG(&first), be64_to_cpu(mkey->seq)); + + /* only items in leaf blocks have values */ + if (val) { + printf(" segno %llu last "SK_FMT"\n", + le64_to_cpu(mval->segno), SK_ARG(&mval->last_key)); + + set_bit(seg_map, le64_to_cpu(mval->segno)); } - /* leaf items print the whole entry */ - first_len = le16_to_cpu(mval->first_key_len); - last_len = le16_to_cpu(mval->last_key_len); - - if (mkey->level == 0) { - first = mval->keys; - last = mval->keys + first_len; - } else { - first = mkey->bkey; - last = mval->keys; - } - - printf(" level %u segno %llu seq %llu first_len %u last_len %u\n", - mkey->level, le64_to_cpu(mval->segno), le64_to_cpu(mval->seq), - first_len, last_len); - - printf(" first "); - print_key(first, first_len); - printf("\n last "); - print_key(last, last_len); - printf("\n"); - - set_bit(seg_map, le64_to_cpu(mval->segno)); - return 0; } @@ -514,7 +443,7 @@ static int print_btree(int fd, struct scoutfs_super_block *super, char *which, static void print_super_block(struct scoutfs_super_block *super, u64 blkno) { char uuid_str[37]; - __le64 *counts; + u64 count; int i; uuid_unparse(super->uuid, uuid_str); @@ -553,10 +482,10 @@ static void print_super_block(struct scoutfs_super_block *super, u64 blkno) le16_to_cpu(super->manifest.root.migration_key_len)); printf(" level_counts:"); - counts = super->manifest.level_counts; for (i = 0; i < SCOUTFS_MANIFEST_MAX_LEVEL; i++) { - if (le64_to_cpu(counts[i])) - printf(" %u: %llu", i, le64_to_cpu(counts[i])); + count = le64_to_cpu(super->manifest.level_counts[i]); + if (count) + printf(" %u: %llu", i, count); } printf("\n"); } diff --git a/utils/src/stage_release.c b/utils/src/stage_release.c index a7ee719d..293f012d 100644 --- a/utils/src/stage_release.c +++ b/utils/src/stage_release.c @@ -11,6 +11,7 @@ #include "sparse.h" #include "util.h" +#include "format.h" #include "ioctl.h" #include "cmd.h" From f275020baa9560630ae3297b26a71606f5b80c61 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Thu, 12 Apr 2018 12:11:07 -0700 Subject: [PATCH 133/235] scoutfs-utils: update btree constants Signed-off-by: Zach Brown --- utils/src/format.h | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index 27e467a5..b935d033 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -121,15 +121,9 @@ struct scoutfs_key_be { __u8 _sk_fourth; }__packed; -/* - * Assert that we'll be able to represent all possible keys with 8 64bit - * primary sort values. - */ -#define SCOUTFS_BTREE_GREATEST_KEY_LEN 32 -/* level >0 segments can have a full key and some metadata */ -#define SCOUTFS_BTREE_MAX_KEY_LEN 320 -/* level 0 segments can have two full keys in the value :/ */ -#define SCOUTFS_BTREE_MAX_VAL_LEN 768 +/* chose reasonable max key and value lens that have room for some u64s */ +#define SCOUTFS_BTREE_MAX_KEY_LEN 40 +#define SCOUTFS_BTREE_MAX_VAL_LEN 64 /* * The min number of free bytes we must leave in a parent as we descend @@ -143,6 +137,14 @@ struct scoutfs_key_be { sizeof(struct scoutfs_btree_item) + SCOUTFS_BTREE_MAX_KEY_LEN +\ sizeof(struct scoutfs_btree_ref)) +/* + * When debugging we can tune the splitting and merging thresholds to + * create much larger trees by having blocks with many fewer items. We + * implement this by pretending the blocks are tiny. They're still + * large enough for a handful of items. + */ +#define SCOUTFS_BTREE_TINY_BLOCK_SIZE 512 + /* * A 4EB test image measured a worst case height of 17. This is plenty * generous. From 37d5aae4d2447d00964baab88dd752cef30ee1dd Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Thu, 26 Apr 2018 14:31:38 -0700 Subject: [PATCH 134/235] scoutfs-utils: add corruption messages Update the format header and add a man page which describes the corruption messages that the kernel module can spit out. Signed-off-by: Zach Brown --- utils/man/scoutfs-corruption.7 | 169 +++++++++++++++++++++++++++++++++ utils/src/format.h | 17 ++++ 2 files changed, 186 insertions(+) create mode 100644 utils/man/scoutfs-corruption.7 diff --git a/utils/man/scoutfs-corruption.7 b/utils/man/scoutfs-corruption.7 new file mode 100644 index 00000000..99e797b3 --- /dev/null +++ b/utils/man/scoutfs-corruption.7 @@ -0,0 +1,169 @@ +.TH scoutfs-corruption 7 +.SH NAME +scoutfs-corruption \- corruption message details +.SH DESCRIPTION +If scoutfs detects corruption during operation it will output an error +message describing the corruption. This document gives details of the +corruption described by the messages. +.SH CORRUPTION MESSAGE IDENTIFIERS +.TP +.B SC_DIRENT_NAME_LEN +A directory entry with an invalid name length was found during lookup. + +Directory entries are stored in the values of metadata items. The item +value contains a small header and the full entry name. The length of +the entry name is calculated by substracting the size of the header from +the length of the item value. This corruption is detected if the length +of the calculated name length is invalid by being less than 1 or greater +than 255. + +.BR dir_ino " - inode number of directory that contains the item" +.br +.BR hash " - hash value of search name" +.br +.BR key " - identifies the item with the invalid name length" +.br +.BR len " - the invalid calculaged name length" +.sp +.TP +.B SC_DIRENT_READDIR_NAME_LEN +A directory entry with an invalid name length was found during readdir. + +This corruption is very similar to +.B SC_DIRENT_NAME_LEN +except that the corruption is discovered during readdir instead of +lookup. The readdir search key is formed from the file position instead +of from the hashed name as in lookup. The dirent structure stored in +the item value is the same. + +.BR dir_ino " - inode number of directory that contains the item" +.br +.BR pos " - the file position readdir was searching from" +.br +.BR key " - identifies the item with the invalid name length" +.br +.BR len " - the invalid calculaged name length" +.sp + +.TP +.B SC_DIRENT_BACKREF_NAME_LEN +A directory entry with an invalid name length was found while finding +entries that point to an inode. + +This corruption is very similar to +.B SC_DIRENT_NAME_LEN +except that the +corruption is discovered while finding entries that refer to a specific +inode. The search key is formed from the inode and position of the +referring entry instead of from the hashed name as in lookup. The +dirent structure stored in the item value is the same. + +.BR ino " - target inode number we're finding entries to" +.br +.BR dir_ino " - inode number of directory containing entries to search" +.br +.BR pos " - position in directory containing entries to search" +.br +.BR key " - identifies the item with the invalid name length" +.br +.BR len " - the invalid calculaged name length" +.sp + +.TP +.B SC_SYMLINK_INODE_SIZE +The items that contain a symlink target path weren't found. + +The target path of a symlink is stored in a series of metadata items. +The number of items can be calculated from the size of the path. While +trying to resolve a symlink one of the items wasn't found. + +.BR ino " - inode number of the symlink with the invalid size" +.br +.BR size " - the invalid size found in the inode" +.sp + +.TP +.B SC_SYMLINK_MISSING_ITEM +A symlink inode contained an invalid size. + +The i_size field of the inode that stores a symlink records the length +of the path of the symlink target. The path length can't be less than 1 +or greater than the max size which is around 4KiB. + +.BR ino " - inode number of the symlink with the invalid size" +.br +.BR size " - the length of the target path" +.sp + +.TP +.B SC_SYMLINK_NOT_NULL_TERM +A symlink target path wasn't null terminated. + +The target path stored in a symlink's metadata items wasn't null +terminated. + +.BR ino " - inode number of the symlink with the invalid size" +.br +.BR last " - the value of the final byte of the path" +.sp + +.TP +.B SC_BTREE_BLOCK_LEVEL +A btree block's header did not contain the expected level field. + +The btree root stores the height of the btree and each btree block +stores its level in the tree. During descent the level is loaded from +the root and decremented as each block is traveresed. This corruption +occurs when a btree block's level field didn't match the level that was +being calculated during descent. + +.BR root_height " - height of the tree in the root" +.br +.BR root_blkno " - block number of the first block in the root" +.br +.BR root_seq " - sequence number of the first block in the root" +.br +.BR blkno " - block number of the block with mismatched level" +.br +.BR seq " - sequence number of the block with mismatched level" +.br +.BR level " - level of the block with mismatched level" +.br +.BR expected " - expected level that was calculated during descent" +.sp + +.TP +.B SC_BTREE_NO_CHILD_REF +A btree parent block didn't have a child item for a key. + +Each child reference in a parent btree block contains the greatest key +that will be stored in the subtree rooted in the child. The child +references down the right side of the tree must have a key that is +greater than all possible keys. + +This corruption occurs during descent when the search key was greater +than the last child reference's key. + +.BR root_height " - height of the tree in the root" +.br +.BR root_blkno " - block number of the first block in the root" +.br +.BR root_seq " - sequence number of the first block in the root" +.br +.BR blkno " - block number of the block with mismatched level" +.br +.BR seq " - sequence number of the block with mismatched level" +.br +.BR level " - level of the block with mismatched level" +.br +.BR nr " - number of items in the parent block" +.br +.BR pos " - child item index that search found" +.br +.BR cmp " - comparison of search key and found" +.sp + +.SH AUTHORS +Zach Brown + + diff --git a/utils/src/format.h b/utils/src/format.h index b935d033..4b60a75c 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -645,4 +645,21 @@ struct scoutfs_fid { #define FILEID_SCOUTFS 0x81 #define FILEID_SCOUTFS_WITH_PARENT 0x82 +/* + * Identifiers for sources of corruption that can generate messages. + */ +enum { + SC_DIRENT_NAME_LEN = 0, + SC_DIRENT_BACKREF_NAME_LEN, + SC_DIRENT_READDIR_NAME_LEN, + SC_SYMLINK_INODE_SIZE, + SC_SYMLINK_MISSING_ITEM, + SC_SYMLINK_NOT_NULL_TERM, + SC_BTREE_BLOCK_LEVEL, + SC_BTREE_NO_CHILD_REF, + SC_NR_SOURCES, +}; + +#define SC_NR_LONGS DIV_ROUND_UP(SC_NR_SOURCES, BITS_PER_LONG) + #endif From f649edd65d6bc0fed162857a4bc6970eb4c63d7e Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Tue, 5 Jun 2018 15:00:24 -0700 Subject: [PATCH 135/235] scoutfs-utils: add block count corruption Signed-off-by: Zach Brown --- utils/src/format.h | 1 + 1 file changed, 1 insertion(+) diff --git a/utils/src/format.h b/utils/src/format.h index 4b60a75c..ee636150 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -657,6 +657,7 @@ enum { SC_SYMLINK_NOT_NULL_TERM, SC_BTREE_BLOCK_LEVEL, SC_BTREE_NO_CHILD_REF, + SC_INODE_BLOCK_COUNTS, SC_NR_SOURCES, }; From 35e4ab92f0ee4be8d79f4c6dc3579c42a99b5b91 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Fri, 6 Apr 2018 15:38:28 -0700 Subject: [PATCH 136/235] scoutfs-utils: support file and node free extents Add support for printing the items used to track file mapping extents and free extents. Signed-off-by: Zach Brown --- utils/src/format.h | 76 +++++++++++++--------------------------------- utils/src/key.c | 6 ++-- utils/src/print.c | 45 ++++++++++++++------------- 3 files changed, 46 insertions(+), 81 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index ee636150..c91b4a4b 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -15,6 +15,7 @@ #define SCOUTFS_BLOCKS_PER_PAGE (PAGE_SIZE / SCOUTFS_BLOCK_SIZE) #define SCOUTFS_BLOCK_SECTOR_SHIFT (SCOUTFS_BLOCK_SHIFT - 9) #define SCOUTFS_BLOCK_SECTORS (1 << SCOUTFS_BLOCK_SECTOR_SHIFT) +#define SCOUTFS_BLOCK_MAX (U64_MAX >> SCOUTFS_BLOCK_SHIFT) /* * FS data is stored in segments, for now they're fixed size. They'll @@ -78,9 +79,10 @@ struct scoutfs_key { #define skii_major _sk_second #define skii_ino _sk_third -/* node free bit map */ -#define skf_node_id _sk_first -#define skf_base _sk_second +/* node free extent */ +#define sknf_node_id _sk_first +#define sknf_major _sk_second +#define sknf_minor _sk_third /* node orphan inode */ #define sko_node_id _sk_first @@ -104,9 +106,9 @@ struct scoutfs_key { #define sks_ino _sk_first #define sks_nr _sk_second -/* file data mapping */ -#define skm_ino _sk_first -#define skm_base _sk_second +/* file extent */ +#define skfe_ino _sk_first +#define skfe_last _sk_second /* * The btree still uses memcmp() to compare keys. We should fix that @@ -302,8 +304,8 @@ struct scoutfs_segment_block { #define SCOUTFS_INODE_INDEX_NR 3 /* don't forget to update */ /* node zone */ -#define SCOUTFS_FREE_BITS_SEGNO_TYPE 1 -#define SCOUTFS_FREE_BITS_BLKNO_TYPE 2 +#define SCOUTFS_FREE_EXTENT_BLKNO_TYPE 1 +#define SCOUTFS_FREE_EXTENT_BLOCKS_TYPE 2 /* fs zone */ #define SCOUTFS_INODE_TYPE 1 @@ -312,60 +314,23 @@ struct scoutfs_segment_block { #define SCOUTFS_READDIR_TYPE 4 #define SCOUTFS_LINK_BACKREF_TYPE 5 #define SCOUTFS_SYMLINK_TYPE 6 -#define SCOUTFS_BLOCK_MAPPING_TYPE 7 +#define SCOUTFS_FILE_EXTENT_TYPE 7 #define SCOUTFS_ORPHAN_TYPE 8 #define SCOUTFS_MAX_TYPE 16 /* power of 2 is efficient */ -/* each mapping item describes a fixed number of blocks */ -#define SCOUTFS_BLOCK_MAPPING_SHIFT 6 -#define SCOUTFS_BLOCK_MAPPING_BLOCKS (1 << SCOUTFS_BLOCK_MAPPING_SHIFT) -#define SCOUTFS_BLOCK_MAPPING_MASK (SCOUTFS_BLOCK_MAPPING_BLOCKS - 1) - /* - * The mapping item value is a byte stream that encodes the value of the - * mapped blocks. The first byte contains the last index that contains - * a mapped block in its low bits. The high bits contain the control - * bits for the first (and possibly only) mapped block. - * - * From then on we consume the control bits in the current control byte - * for each mapped block. Each block has two bits that describe the - * block: zero, incremental from previous block, delta encoded, and - * offline. If we run out of control bits then we consume the next byte - * in the stream for additional control bits. If we have a delta - * encoded block then we consume its encoded bytes from the byte stream. + * File extents have more data than easily fits in the key so we move + * the non-indexed fields into the value. */ - -#define SCOUTFS_BLOCK_ENC_ZERO 0 -#define SCOUTFS_BLOCK_ENC_INC 1 -#define SCOUTFS_BLOCK_ENC_DELTA 2 -#define SCOUTFS_BLOCK_ENC_OFFLINE 3 -#define SCOUTFS_BLOCK_ENC_MASK 3 - -#define SCOUTFS_ZIGZAG_MAX_BYTES (DIV_ROUND_UP(64, 7)) - -/* - * the largest block mapping has: nr byte, ctl bytes for all blocks, and - * worst case zigzag encodings for all blocks. - */ -#define SCOUTFS_BLOCK_MAPPING_MAX_BYTES \ - (1 + (SCOUTFS_BLOCK_MAPPING_BLOCKS / 4) + \ - (SCOUTFS_BLOCK_MAPPING_BLOCKS * SCOUTFS_ZIGZAG_MAX_BYTES)) - -/* free bit bitmaps contain a segment's worth of blocks */ -#define SCOUTFS_FREE_BITS_SHIFT \ - SCOUTFS_SEGMENT_BLOCK_SHIFT -#define SCOUTFS_FREE_BITS_BITS \ - (1 << SCOUTFS_FREE_BITS_SHIFT) -#define SCOUTFS_FREE_BITS_MASK \ - (SCOUTFS_FREE_BITS_BITS - 1) -#define SCOUTFS_FREE_BITS_U64S \ - DIV_ROUND_UP(SCOUTFS_FREE_BITS_BITS, 64) - -struct scoutfs_free_bits { - __le64 bits[SCOUTFS_FREE_BITS_U64S]; +struct scoutfs_file_extent { + __le64 blkno; + __le64 len; + __u8 flags; } __packed; +#define SEF_OFFLINE 0x1 + /* * The first xattr part item has a header that describes the xattr. The * name and value are then packed into the following bytes in the first @@ -509,7 +474,6 @@ enum { SCOUTFS_DT_WHT, }; -#define SCOUTFS_MAX_VAL_SIZE SCOUTFS_BLOCK_MAPPING_MAX_BYTES #define SCOUTFS_XATTR_MAX_NAME_LEN 255 #define SCOUTFS_XATTR_MAX_VAL_LEN 65535 @@ -519,6 +483,8 @@ enum { DIV_ROUND_UP(sizeof(struct scoutfs_xattr) + name_len + val_len, \ SCOUTFS_XATTR_MAX_PART_SIZE); +#define SCOUTFS_MAX_VAL_SIZE SCOUTFS_XATTR_MAX_PART_SIZE + /* * structures used by dlm */ diff --git a/utils/src/key.c b/utils/src/key.c index b19b6cd0..2afc855f 100644 --- a/utils/src/key.c +++ b/utils/src/key.c @@ -28,8 +28,8 @@ char *scoutfs_zone_strings[SCOUTFS_MAX_ZONE] = { char *scoutfs_type_strings[SCOUTFS_MAX_ZONE][SCOUTFS_MAX_TYPE] = { [SCOUTFS_INODE_INDEX_ZONE][SCOUTFS_INODE_INDEX_META_SEQ_TYPE] = "msq", [SCOUTFS_INODE_INDEX_ZONE][SCOUTFS_INODE_INDEX_DATA_SEQ_TYPE] = "dsq", - [SCOUTFS_NODE_ZONE][SCOUTFS_FREE_BITS_SEGNO_TYPE] = "fsg", - [SCOUTFS_NODE_ZONE][SCOUTFS_FREE_BITS_BLKNO_TYPE] = "fbk", + [SCOUTFS_NODE_ZONE][SCOUTFS_FREE_EXTENT_BLKNO_TYPE] = "fbn", + [SCOUTFS_NODE_ZONE][SCOUTFS_FREE_EXTENT_BLOCKS_TYPE] = "fbs", [SCOUTFS_NODE_ZONE][SCOUTFS_ORPHAN_TYPE] = "orp", [SCOUTFS_FS_ZONE][SCOUTFS_INODE_TYPE] = "ino", [SCOUTFS_FS_ZONE][SCOUTFS_XATTR_TYPE] = "xat", @@ -37,7 +37,7 @@ char *scoutfs_type_strings[SCOUTFS_MAX_ZONE][SCOUTFS_MAX_TYPE] = { [SCOUTFS_FS_ZONE][SCOUTFS_READDIR_TYPE] = "rdr", [SCOUTFS_FS_ZONE][SCOUTFS_LINK_BACKREF_TYPE] = "lbr", [SCOUTFS_FS_ZONE][SCOUTFS_SYMLINK_TYPE] = "sym", - [SCOUTFS_FS_ZONE][SCOUTFS_BLOCK_MAPPING_TYPE] = "bmp", + [SCOUTFS_FS_ZONE][SCOUTFS_FILE_EXTENT_TYPE] = "fex", }; char scoutfs_unknown_u8_strings[U8_MAX][U8_STR_MAX]; diff --git a/utils/src/print.c b/utils/src/print.c index 02e125e5..10dc7122 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -161,30 +161,30 @@ static void print_symlink(struct scoutfs_key *key, void *val, int val_len) le64_to_cpu(key->sks_ino), le64_to_cpu(key->sks_nr), name); } -/* - * XXX not decoding the bytes yet - */ -static void print_block_mapping(struct scoutfs_key *key, void *val, int val_len) +static void print_file_extent(struct scoutfs_key *key, void *val, int val_len) { - u64 blk_off = le64_to_cpu(key->skm_base) << SCOUTFS_BLOCK_MAPPING_SHIFT; - u8 nr = *((u8 *)val) & 63; + struct scoutfs_file_extent *fex = val; + u64 iblock = le64_to_cpu(key->skfe_last) - le64_to_cpu(fex->len) + 1; - printf(" block mapping: ino %llu blk_off %llu blocks %u\n", - le64_to_cpu(key->skm_ino), blk_off, nr); + printf(" extent: ino %llu (last %llu) iblock %llu len %llu " + "blkno %llu flags 0x%x\n", + le64_to_cpu(key->skfe_ino), le64_to_cpu(key->skfe_last), + iblock, le64_to_cpu(fex->len), le64_to_cpu(fex->blkno), + fex->flags); } -static void print_free_bits(struct scoutfs_key *key, void *val, int val_len) +static void print_free_extent(struct scoutfs_key *key, void *val, int val_len) { - struct scoutfs_free_bits *frb = val; - int i; + u64 start = le64_to_cpu(key->sknf_major); + u64 len = le64_to_cpu(key->sknf_minor); + if (key->sk_type == SCOUTFS_FREE_EXTENT_BLOCKS_TYPE) + swap(start, len); + start -= (len - 1); - printf(" node_id %llx base %llu\n", - le64_to_cpu(key->skf_node_id), le64_to_cpu(key->skf_base)); - - printf(" bits:"); - for (i = 0; i < array_size(frb->bits); i++) - printf(" %016llx", le64_to_cpu(frb->bits[i])); - printf("\n"); + printf(" free extent: major %llu minor %llu (start %llu " + "len %llu)\n", + le64_to_cpu(key->sknf_major), le64_to_cpu(key->sknf_minor), + start, len); } static void print_inode_index(struct scoutfs_key *key, void *val, int val_len) @@ -203,9 +203,9 @@ static print_func_t find_printer(u8 zone, u8 type) return print_inode_index; if (zone == SCOUTFS_NODE_ZONE) { - if (type == SCOUTFS_FREE_BITS_SEGNO_TYPE || - type == SCOUTFS_FREE_BITS_BLKNO_TYPE) - return print_free_bits; + if (type == SCOUTFS_FREE_EXTENT_BLKNO_TYPE || + type == SCOUTFS_FREE_EXTENT_BLOCKS_TYPE) + return print_free_extent; if (type == SCOUTFS_ORPHAN_TYPE) return print_orphan; } @@ -218,8 +218,7 @@ static print_func_t find_printer(u8 zone, u8 type) case SCOUTFS_READDIR_TYPE: return print_dirent; case SCOUTFS_SYMLINK_TYPE: return print_symlink; case SCOUTFS_LINK_BACKREF_TYPE: return print_dirent; - case SCOUTFS_BLOCK_MAPPING_TYPE: - return print_block_mapping; + case SCOUTFS_FILE_EXTENT_TYPE: return print_file_extent; } } From 98d06c7a6b73b67c8fc40be6b8172b6990591463 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Mon, 16 Apr 2018 14:49:40 -0700 Subject: [PATCH 137/235] scoutfs-utils: mkfs requires 16 segments mkfs needs to make sure that a device is large enough for a file system. We had a tiny limit that almost certainly wouldn't have worked. Increase the limit to a still absurdly small but arguably possible 16 segments. Signed-off-by: Zach Brown --- utils/src/mkfs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/utils/src/mkfs.c b/utils/src/mkfs.c index 4b357487..f01b0e6c 100644 --- a/utils/src/mkfs.c +++ b/utils/src/mkfs.c @@ -193,8 +193,8 @@ static int write_new_fs(char *path, int fd) goto out; } - /* require space for one segment */ - limit = SCOUTFS_SEGMENT_SIZE * 2; + /* arbitrarily require space for a handful of segments */ + limit = SCOUTFS_SEGMENT_SIZE * 16; if (size < limit) { fprintf(stderr, "%llu byte device too small for min %llu byte fs\n", size, limit); From cfc8cb8800369a9de8a8c11b89372d361061fe58 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Mon, 16 Apr 2018 15:26:54 -0700 Subject: [PATCH 138/235] scoutfs-utils: support server extent allocation Signed-off-by: Zach Brown --- utils/src/format.h | 44 +++++++++------------- utils/src/mkfs.c | 94 +++++++++++++++++++++++++++++++++++----------- utils/src/print.c | 44 +++++++++++----------- 3 files changed, 114 insertions(+), 68 deletions(-) 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; From 59739e0057d2c0a5007794791ed8d071b9a5ed7d Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Mon, 16 Apr 2018 15:33:16 -0700 Subject: [PATCH 139/235] scoutfs-utils: remove sneaky tab in mkfs output We had a tab in the mkfs output that'd cause it to be misaligned. Signed-off-by: Zach Brown --- utils/src/mkfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/src/mkfs.c b/utils/src/mkfs.c index 69946449..8002fc6b 100644 --- a/utils/src/mkfs.c +++ b/utils/src/mkfs.c @@ -408,7 +408,7 @@ static int write_new_fs(char *path, int fd) " fsid: %llx\n" " format hash: %llx\n" " uuid: %s\n" - " device bytes: "SIZE_FMT"\n" + " device bytes: "SIZE_FMT"\n" " device blocks: "SIZE_FMT"\n" " btree ring blocks: "SIZE_FMT"\n" " free blocks: "SIZE_FMT"\n", From 3ab93baa554bea33eaecaa874a3fd217d4dc0688 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Thu, 19 Apr 2018 14:34:21 -0700 Subject: [PATCH 140/235] scoutfs-utils: update format for unwritten extents Signed-off-by: Zach Brown --- utils/src/format.h | 1 + 1 file changed, 1 insertion(+) diff --git a/utils/src/format.h b/utils/src/format.h index 86a29814..a4acbed9 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -330,6 +330,7 @@ struct scoutfs_file_extent { } __packed; #define SEF_OFFLINE 0x1 +#define SEF_UNWRITTEN 0x2 /* * The first xattr part item has a header that describes the xattr. The From 445ac621725f803fd0a540f53b710574d70e9a5f Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Tue, 22 May 2018 13:32:46 -0700 Subject: [PATCH 141/235] scoutfs-utils: add extent corruption sources Signed-off-by: Zach Brown --- utils/man/scoutfs-corruption.7 | 46 ++++++++++++++++++++++++++++++++++ utils/src/format.h | 5 ++++ 2 files changed, 51 insertions(+) diff --git a/utils/man/scoutfs-corruption.7 b/utils/man/scoutfs-corruption.7 index 99e797b3..32ea5a49 100644 --- a/utils/man/scoutfs-corruption.7 +++ b/utils/man/scoutfs-corruption.7 @@ -163,6 +163,52 @@ than the last child reference's key. .BR cmp " - comparison of search key and found" .sp +.TP +.B SC_EXTENT_ADD_CLEANUP, SC_EXTENT_REM_CLEANUP, SC_DATA_EXTENT_TRUNC_CLEANUP, SC_DATA_EXTENT_ALLOC_CLEANUP, SC_SERVER_EXTENT_CLEANUP + +Extents are used to track regions of blocks or files. The process of +modifying an extent creates and destroys intermediate extents, for +example as two disjoint extents are merged with a third that is created +between the two. If an error occurs during this process the +intermediate extents must be returned to the original state. If an +error occurs during this cleanup process then the resulting extents, +taken as a whole, can be inconsistent. + +They can describe overlapping regions. They can forget a region that was +previously described. The consequences of these inconsistencies depend +on the extent type. + +The +.I +_EXTENT_ +cases occur as core library code is modifying extents. It can happen on +behalf of both file data extents and free extents and while adding or +removing extents. + +The +.I +_DATA_EXTENT_ +cases occur in file mapping extents while either truncating (removing) +extents from a file or while allocating extents for a newly written +region of a file. + +The +.I +_SERVER_EXTENT_ +case occurs as the server is tracking free extents on behalf of all +nodes. + +Each corruption type message describes the extent and operation. + +.BR clean " - extent that was being cleaned up after an error" +.br +.BR ext " - primary extent that was being operated on before the error" +.br +.BR ret " - negative errno of the first error encountered" +.br +.BR op " - the operation the server was performing on the extent" +.sp + .SH AUTHORS Zach Brown diff --git a/utils/src/format.h b/utils/src/format.h index a4acbed9..f9a33b83 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -617,6 +617,11 @@ enum { SC_BTREE_BLOCK_LEVEL, SC_BTREE_NO_CHILD_REF, SC_INODE_BLOCK_COUNTS, + SC_EXTENT_ADD_CLEANUP, + SC_EXTENT_REM_CLEANUP, + SC_DATA_EXTENT_TRUNC_CLEANUP, + SC_DATA_EXTENT_ALLOC_CLEANUP, + SC_SERVER_EXTENT_CLEANUP, SC_NR_SOURCES, }; From 0a62ffbc2fa1917e29dbd751ff29be9e164e4c12 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Thu, 14 Jun 2018 14:10:28 -0700 Subject: [PATCH 142/235] scoutfs-utils: buffer staging The stage command was trivially implemented by allocating, reading, and staging the entire region in buffer. This is unreasonable for large file regions. Implement the stage command by having it read each portion of the region into a smaller buffer, starting with a meg. Signed-off-by: Zach Brown --- utils/src/stage_release.c | 54 ++++++++++++++++++++++++--------------- 1 file changed, 34 insertions(+), 20 deletions(-) diff --git a/utils/src/stage_release.c b/utils/src/stage_release.c index 293f012d..2e1ec342 100644 --- a/utils/src/stage_release.c +++ b/utils/src/stage_release.c @@ -18,6 +18,8 @@ static int stage_cmd(int argc, char **argv) { struct scoutfs_ioctl_stage args; + unsigned int buf_len = 1024 * 1024; + unsigned int bytes; char *endptr = NULL; char *buf = NULL; int afd = -1; @@ -82,33 +84,45 @@ static int stage_cmd(int argc, char **argv) goto out; } - buf = malloc(count); + buf = malloc(buf_len); if (!buf) { - fprintf(stderr, "couldn't allocate %llu byte buffer\n", - count); + fprintf(stderr, "couldn't allocate %u byte buffer\n", buf_len); ret = -ENOMEM; goto out; } - ret = read(afd, buf, count); - if (ret < count) { - fprintf(stderr, "archive read returned %d, not %llu: error %s (%d)\n", - ret, count, strerror(errno), errno); - ret = -EIO; - goto out; + while (count) { + + bytes = min(count, buf_len); + + ret = read(afd, buf, bytes); + if (ret <= 0) { + fprintf(stderr, "archive read returned %d: error %s (%d)\n", + ret, strerror(errno), errno); + ret = -EIO; + goto out; + } + + bytes = ret; + + args.data_version = vers; + args.buf_ptr = (unsigned long)buf; + args.offset = offset; + args.count = bytes; + + count -= bytes; + offset += bytes; + + ret = ioctl(fd, SCOUTFS_IOC_STAGE, &args); + if (ret != bytes) { + fprintf(stderr, "stage returned %d, not %u: error %s (%d)\n", + ret, bytes, strerror(errno), errno); + ret = -EIO; + goto out; + } } - args.data_version = vers; - args.buf_ptr = (unsigned long)buf; - args.offset = offset; - args.count = count; - - ret = ioctl(fd, SCOUTFS_IOC_STAGE, &args); - if (ret < count) { - fprintf(stderr, "stage returned %d, not %llu: error %s (%d)\n", - ret, count, strerror(errno), errno); - ret = -EIO; - } + ret = 0; out: free(buf); if (fd > -1) From 35b5f1f9c56ce08e7b330e7e4c02803bfa563dd5 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Fri, 15 Jun 2018 15:18:29 -0700 Subject: [PATCH 143/235] scoutfs-utils: add fallocate corruption source Signed-off-by: Zach Brown --- utils/man/scoutfs-corruption.7 | 9 +++++---- utils/src/format.h | 1 + 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/utils/man/scoutfs-corruption.7 b/utils/man/scoutfs-corruption.7 index 32ea5a49..980c1af1 100644 --- a/utils/man/scoutfs-corruption.7 +++ b/utils/man/scoutfs-corruption.7 @@ -164,7 +164,7 @@ than the last child reference's key. .sp .TP -.B SC_EXTENT_ADD_CLEANUP, SC_EXTENT_REM_CLEANUP, SC_DATA_EXTENT_TRUNC_CLEANUP, SC_DATA_EXTENT_ALLOC_CLEANUP, SC_SERVER_EXTENT_CLEANUP +.B SC_EXTENT_ADD_CLEANUP, SC_EXTENT_REM_CLEANUP, SC_DATA_EXTENT_TRUNC_CLEANUP, SC_DATA_EXTENT_ALLOC_CLEANUP, SC_DATA_EXTENT_FALLOCATE_CLEANUP, SC_SERVER_EXTENT_CLEANUP Extents are used to track regions of blocks or files. The process of modifying an extent creates and destroys intermediate extents, for @@ -188,9 +188,10 @@ removing extents. The .I _DATA_EXTENT_ -cases occur in file mapping extents while either truncating (removing) -extents from a file or while allocating extents for a newly written -region of a file. +cases occur in file mapping extents while truncating (removing) +extents from a file, while allocating extents for a newly written +region of a file, or while using fallocate to pre-allocate extents +to the file. The .I diff --git a/utils/src/format.h b/utils/src/format.h index f9a33b83..e3aa67d7 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -622,6 +622,7 @@ enum { SC_DATA_EXTENT_TRUNC_CLEANUP, SC_DATA_EXTENT_ALLOC_CLEANUP, SC_SERVER_EXTENT_CLEANUP, + SC_DATA_EXTENT_FALLOCATE_CLEANUP, SC_NR_SOURCES, }; From b96feaa5b0b5af80ba420f0a36c94fdde4d13ae5 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Wed, 20 Jun 2018 15:38:30 -0700 Subject: [PATCH 144/235] scoutfs-utils: add scoutfs_net_extent to format.h Signed-off-by: Zach Brown --- utils/src/format.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/utils/src/format.h b/utils/src/format.h index e3aa67d7..65c10b69 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -563,6 +563,11 @@ struct scoutfs_net_statfs { __u8 uuid[SCOUTFS_UUID_BYTES]; /* logical volume uuid */ } __packed; +struct scoutfs_net_extent { + __le64 start; + __le64 len; +} __packed; + /* XXX eventually we'll have net compaction and will need agents to agree */ /* one upper segment and fanout lower segments */ From 51a48fbbb6635670ac951071a5cf632831df3c6c Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Mon, 25 Jun 2018 16:18:09 -0700 Subject: [PATCH 145/235] scoutfs-utils: add TeX paper Add the start of a paper that documents the scoutfs design. Signed-off-by: Zach Brown --- utils/tex/.gitignore | 8 ++ utils/tex/Makefile | 33 ++++++ utils/tex/scoutfs.tex | 221 +++++++++++++++++++++++++++++++++++++++ utils/tex/usenix2019.sty | 97 +++++++++++++++++ utils/tex/usenix2019.tex | 219 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 578 insertions(+) create mode 100644 utils/tex/.gitignore create mode 100644 utils/tex/Makefile create mode 100644 utils/tex/scoutfs.tex create mode 100644 utils/tex/usenix2019.sty create mode 100644 utils/tex/usenix2019.tex diff --git a/utils/tex/.gitignore b/utils/tex/.gitignore new file mode 100644 index 00000000..853cdad5 --- /dev/null +++ b/utils/tex/.gitignore @@ -0,0 +1,8 @@ +missfont.log +*.fls +*.aux +*.d +*.d +*.fdb_latexmk +*.log +*.pdf diff --git a/utils/tex/Makefile b/utils/tex/Makefile new file mode 100644 index 00000000..35c59470 --- /dev/null +++ b/utils/tex/Makefile @@ -0,0 +1,33 @@ +# +# # dnf install latexmk texlive +# # make +# +# Tools +LATEXMK = latexmk +RM = rm -f + +# Project-specific settings +DOCNAME = scoutfs + +# Targets +all: doc +doc: pdf +pdf: $(DOCNAME).pdf + +# Rules +%.pdf: %.tex + $(LATEXMK) -pdf -M -MP -MF $*.d $* + +mostlyclean: + $(LATEXMK) -silent -c + $(RM) *.bbl + +clean: mostlyclean + $(LATEXMK) -silent -C + $(RM) *.run.xml *.synctex.gz + $(RM) *.d + +.PHONY: all clean doc mostlyclean pdf + +# Include auto-generated dependencies +-include *.d diff --git a/utils/tex/scoutfs.tex b/utils/tex/scoutfs.tex new file mode 100644 index 00000000..2c173be6 --- /dev/null +++ b/utils/tex/scoutfs.tex @@ -0,0 +1,221 @@ +% This was derived from the usenix templates, whose introductory +% comment is as follows: +% +% TEMPLATE for Usenix papers, specifically to meet requirements of +% USENIX '05 +% originally a template for producing IEEE-format articles using LaTeX. +% written by Matthew Ward, CS Department, Worcester Polytechnic Institute. +% adapted by David Beazley for his excellent SWIG paper in Proceedings, +% Tcl 96 +% turned into a smartass generic template by De Clarke, with thanks to +% both the above pioneers +% use at your own risk. Complaints to /dev/null. +% make it two column with no page numbering, default is 10 point + +% Munged by Fred Douglis 10/97 to separate +% the .sty file from the LaTeX source template, so that people can +% more easily include the .sty file into an existing document. Also +% changed to more closely follow the style guidelines as represented +% by the Word sample file. + +% Note that since 2010, USENIX does not require endnotes. If you want +% foot of page notes, don't include the endnotes package in the +% usepackage command, below. + +% This version uses the latex2e styles, not the very ancient 2.09 stuff. +\documentclass[letterpaper,twocolumn,10pt]{article} +\usepackage{usenix2019,epsfig} +\begin{document} + +%don't want date printed +\date{} + +%make title bold and 14 pt font (Latex default is non-bold, 16 pt) +\title{\Large \bf scoutfs : A Scalable Archival Filesystem} + +%for single author (just remove % characters) +\author{ +{\rm Zach Brown}\\ +Versity Software, Inc. +} + +\maketitle + +% Use the following at camera-ready time to suppress page numbers. +% Comment it out when you first submit the paper for review. +% \thispagestyle{empty} + +\section{Metadata Items} + +scoutfs stores filesystem metadata in items that are identified by a +key and contain a variable length value payload.\\ + +Every key uses a generic structure with a fixed number of fields. + +{\tt \small +\begin{verbatim} +struct scoutfs_key { + __u8 sk_zone; + __le64 _sk_first; + __u8 sk_type; + __le64 _sk_second; + __le64 _sk_third; + __u8 _sk_fourth; +}; +\end{verbatim} +} + +Using a shared key struct lets us sort all the metadata items in the +filesystem in one key space regardless of their form or function. The +generic keys are displayed, sorted, and computed (incrementing, finding +difference) without needing to know the specific fields of each item +type. + +Different structures are identified by their zone and type pair. They +then map their type's fields to the remaining generic fields to +determine the sorting of the item keys within their type. + +For example, when storing inodes we use the {\tt SCOUTFS\_FS\_ZONE} and +{\tt SCOUTFS\_INODE\_TYPE} and put the inode number in the first generic +key field. + +{\tt \small +\begin{verbatim} + #define ski_ino _sk_first +\end{verbatim} +} + +{\tt \small +\begin{verbatim} + key.sk_zone = SCOUTFS_FS_ZONE; + key.ski_ino = ino; + key.sk_type = SCOUTFS_INODE_TYPE; +\end{verbatim} +} + +Continuing this example, metadata that is associated with inodes also +use the {\tt SCOUTFS\_FS\_ZONE} and store the inode number in {\tt +\_sk\_first} but then have different type values. For example {\tt +SCOUTFS\_XATTR\_TYPE} or {\tt SCOUTFS\_SYMLINK\_TYPE}. When the items' +keys are sorted we end up with all the items for a given inode stored +near each other. + +\subsection{Directory Entries} + +A directory entry is stored in three different metadata items, each with +a different key and used for a different purpose. Each item shares the +same key format and directory entry value payload, however. + +The key stores the entry's directory inode number and major and minor +values associated with the type of directory entry being stored. + +{\tt \small +\begin{verbatim} + #define skd_ino _sk_first + #define skd_major _sk_second + #define skd_minor _sk_third +\end{verbatim} +} + +The value contains a directory entry struct with all the metadata +associated with a directory entry, including the full entry name. + +{\tt \small +\begin{verbatim} +struct scoutfs_dirent { + __le64 ino; + __le64 hash; + __le64 pos; + __u8 type; + __u8 name[0]; +}; +\end{verbatim} +} + +Each item contains a full copy of the item value. This duplicates +storage across each item type but also lets each operation be satisfied +by one item lookup. Once the item value is obtained its fields can be +used to construct the keys for each of the items associated with the +entry. + +\subsubsection{Directory Entry Lookup Items} + +{\tt \small +\begin{verbatim} + key.sk_zone = SCOUTFS_FS_ZONE; + key.skd_ino = dir_ino; + key.sk_type = SCOUTFS_DIRENT_TYPE; + key.skd_major = hash(entry_name); + key.skd_minor = dir_pos; +\end{verbatim} +} + +Lookup entries are stored in the parent directory at the hash of the +name of the entry. These entries are used to map names to inode numbers +during path traversal. + +The major key value is set to a 64bit hash of the file name. These hash +values can collide so the minor key value is set to the readdir position +in the directory of the entry. This readdir position is unique for +every entry and ensures that keys are unique when hash values collide. + +A name lookup is performed by iterating over all the keys with the major +that matches the hashed name. The full name in the dirent value struct +is compared to the search name. It will be very rare to have more than +one item with a given hash value. + +\subsubsection{Directory Entry Readdir Items} + +{\tt \small +\begin{verbatim} + key.sk_zone = SCOUTFS_FS_ZONE; + key.skd_ino = dir_ino; + key.sk_type = SCOUTFS_READDIR_TYPE; + key.skd_major = dir_pos; + key.skd_minor = 0; +\end{verbatim} +} + +Readdir entries are used to iterate over entries for the readdir() +call. By providing a unique 64bit {\tt dir\_pos} for each entry we avoid +having to track multiple entries for a given readdir position value. + +readdir() returns entries in {\tt dir\_pos} order which depends on entry +creation order and matches inode allocation order. Accessing the inodes +that are referenced by the entries returned from readdir() will result +in efficient forward iteration over the readdir and inode items, +assuming that files were simply created. + +Renaming files or creating hard links to existing files creates a new +entry but can't reassign the inode number and can result in mismatched +access patterns of the readdir entry items and the inode items. + +\subsubsection{Directory Entry Link Backref Items} + +{\tt \small +\begin{verbatim} + key.sk_zone = SCOUTFS_FS_ZONE; + key.skd_ino = target_ino; + key.sk_type = SCOUTFS_LINK_BACKREF_TYPE; + key.skd_major = dir_ino; + key.skd_minor = dir_pos; +\end{verbatim} +} + +Link backref entry items are stored with the target inode number and the +inode number and readdir position of the entry in its directory. +They're used to iterate over all the entries that refer to a given +inode. Full relative paths from the root directory to a target inode +can be constructed by walking up through each parent entry as its +discovered. + +Both inode numbers and readdir positions are allocated by strictly +increasing the next free number. Old inode numbers or readdir positions +are never reused. This means that resolving paths for existing inodes +will always walk keys that are strictly sorted less than the keys that +will be created as new files are created. This tends to isolate read +access patterns during backround archival policy processing from write +access patterns during new file creation and increases performance by +reducing contention. + +\end{document} diff --git a/utils/tex/usenix2019.sty b/utils/tex/usenix2019.sty new file mode 100644 index 00000000..dbab970c --- /dev/null +++ b/utils/tex/usenix2019.sty @@ -0,0 +1,97 @@ +% usenix.sty - to be used with latex2e for USENIX. +% To use this style file, look at the template usenix_template.tex +% +% $Id: usenix.sty,v 1.2 2005/02/16 22:30:47 maniatis Exp $ +% +% The following definitions are modifications of standard article.sty +% definitions, arranged to do a better job of matching the USENIX +% guidelines. +% It will automatically select two-column mode and the Times-Roman +% font. + +% +% USENIX papers are two-column. +% Times-Roman font is nice if you can get it (requires NFSS, +% which is in latex2e. + +\if@twocolumn\else\input twocolumn.sty\fi +\usepackage{mathptmx} % times roman, including math (where possible) + +% +% USENIX wants margins of: 0.75" sides, 1" bottom, and 1" top. +% 0.33" gutter between columns. +% Gives active areas of 7" x 9" +% +\setlength{\textheight}{9.0in} +\setlength{\columnsep}{0.33in} +\setlength{\textwidth}{7.00in} + +\setlength{\topmargin}{0.0in} + +\setlength{\headheight}{0.0in} + +\setlength{\headsep}{0.0in} + +\addtolength{\oddsidemargin}{-0.25in} +\addtolength{\evensidemargin}{-0.25in} + +% Usenix wants no page numbers for camera-ready papers, so that they can +% number them themselves. But submitted papers should have page numbers +% for the reviewers' convenience. +% +% +% \pagestyle{empty} + +% +% Usenix titles are in 14-point bold type, with no date, and with no +% change in the empty page headers. The whole author section is 12 point +% italic--- you must use {\rm } around the actual author names to get +% them in roman. +% +\def\maketitle{\par + \begingroup + \renewcommand\thefootnote{\fnsymbol{footnote}}% + \def\@makefnmark{\hbox to\z@{$\m@th^{\@thefnmark}$\hss}}% + \long\def\@makefntext##1{\parindent 1em\noindent + \hbox to1.8em{\hss$\m@th^{\@thefnmark}$}##1}% + \if@twocolumn + \twocolumn[\@maketitle]% + \else \newpage + \global\@topnum\z@ + \@maketitle \fi\@thanks + \endgroup + \setcounter{footnote}{0}% + \let\maketitle\relax + \let\@maketitle\relax + \gdef\@thanks{}\gdef\@author{}\gdef\@title{}\let\thanks\relax} + +\def\@maketitle{\newpage + \vbox to 2.5in{ + \vspace*{\fill} + \vskip 2em + \begin{center}% + {\Large\bf \@title \par}% + \vskip 0.375in minus 0.300in + {\large\it + \lineskip .5em + \begin{tabular}[t]{c}\@author + \end{tabular}\par}% + \end{center}% + \par + \vspace*{\fill} +% \vskip 1.5em + } +} + +% +% The abstract is preceded by a 12-pt bold centered heading +\def\abstract{\begin{center}% +{\large\bf \abstractname\vspace{-.5em}\vspace{\z@}}% +\end{center}} +\def\endabstract{} + +% +% Main section titles are 12-pt bold. Others can be same or smaller. +% +\def\section{\@startsection {section}{1}{\z@}{-3.5ex plus-1ex minus + -.2ex}{2.3ex plus.2ex}{\reset@font\large\bf}} diff --git a/utils/tex/usenix2019.tex b/utils/tex/usenix2019.tex new file mode 100644 index 00000000..b712783f --- /dev/null +++ b/utils/tex/usenix2019.tex @@ -0,0 +1,219 @@ +% TEMPLATE for Usenix papers, specifically to meet requirements of +% USENIX '05 +% originally a template for producing IEEE-format articles using LaTeX. +% written by Matthew Ward, CS Department, Worcester Polytechnic Institute. +% adapted by David Beazley for his excellent SWIG paper in Proceedings, +% Tcl 96 +% turned into a smartass generic template by De Clarke, with thanks to +% both the above pioneers +% use at your own risk. Complaints to /dev/null. +% make it two column with no page numbering, default is 10 point + +% Munged by Fred Douglis 10/97 to separate +% the .sty file from the LaTeX source template, so that people can +% more easily include the .sty file into an existing document. Also +% changed to more closely follow the style guidelines as represented +% by the Word sample file. + +% Note that since 2010, USENIX does not require endnotes. If you want +% foot of page notes, don't include the endnotes package in the +% usepackage command, below. + +% This version uses the latex2e styles, not the very ancient 2.09 stuff. +\documentclass[letterpaper,twocolumn,10pt]{article} +\usepackage{usenix2019,epsfig,endnotes} +\begin{document} + +%don't want date printed +\date{} + +%make title bold and 14 pt font (Latex default is non-bold, 16 pt) +\title{\Large \bf Wonderful : A Terrific Application and Fascinating Paper} + +%for single author (just remove % characters) +\author{ +{\rm Your N.\ Here}\\ +Your Institution +\and +{\rm Second Name}\\ +Second Institution +% copy the following lines to add more authors +% \and +% {\rm Name}\\ +%Name Institution +} % end author + +\maketitle + +% Use the following at camera-ready time to suppress page numbers. +% Comment it out when you first submit the paper for review. +\thispagestyle{empty} + + +\subsection*{Abstract} +Your Abstract Text Goes Here. Just a few facts. +Whet our appetites. + +\section{Introduction} + +A paragraph of text goes here. Lots of text. Plenty of interesting +text. \\ + +More fascinating text. Features\endnote{Remember to use endnotes, not footnotes!} galore, plethora of promises.\\ + +\section{This is Another Section} + +Some embedded literal typset code might +look like the following : + +{\tt \small +\begin{verbatim} +int wrap_fact(ClientData clientData, + Tcl_Interp *interp, + int argc, char *argv[]) { + int result; + int arg0; + if (argc != 2) { + interp->result = "wrong # args"; + return TCL_ERROR; + } + arg0 = atoi(argv[1]); + result = fact(arg0); + sprintf(interp->result,"%d",result); + return TCL_OK; +} +\end{verbatim} +} + +Now we're going to cite somebody. Watch for the cite tag. +Here it comes~\cite{Chaum1981,Diffie1976}. The tilde character (\~{}) +in the source means a non-breaking space. This way, your reference will +always be attached to the word that preceded it, instead of going to the +next line. + +\section{This Section has SubSections} +\subsection{First SubSection} + +Here's a typical figure reference. The figure is centered at the +top of the column. It's scaled. It's explicitly placed. You'll +have to tweak the numbers to get what you want.\\ + +% you can also use the wonderful epsfig package... +\begin{figure}[t] +\begin{center} +\begin{picture}(300,150)(0,200) +\put(-15,-30){\special{psfile = fig1.ps hscale = 50 vscale = 50}} +\end{picture}\\ +\end{center} +\caption{Wonderful Flowchart} +\end{figure} + +This text came after the figure, so we'll casually refer to Figure 1 +as we go on our merry way. + +\subsection{New Subsection} + +It can get tricky typesetting Tcl and C code in LaTeX because they share +a lot of mystical feelings about certain magic characters. You +will have to do a lot of escaping to typeset curly braces and percent +signs, for example, like this: +``The {\tt \%module} directive +sets the name of the initialization function. This is optional, but is +recommended if building a Tcl 7.5 module. +Everything inside the {\tt \%\{, \%\}} +block is copied directly into the output. allowing the inclusion of +header files and additional C code." \\ + +Sometimes you want to really call attention to a piece of text. You +can center it in the column like this: +\begin{center} +{\tt \_1008e614\_Vector\_p} +\end{center} +and people will really notice it.\\ + +\noindent +The noindent at the start of this paragraph makes it clear that it's +a continuation of the preceding text, not a new para in its own right. + + +Now this is an ingenious way to get a forced space. +{\tt Real~$*$} and {\tt double~$*$} are equivalent. + +Now here is another way to call attention to a line of code, but instead +of centering it, we noindent and bold it.\\ + +\noindent +{\bf \tt size\_t : fread ptr size nobj stream } \\ + +And here we have made an indented para like a definition tag (dt) +in HTML. You don't need a surrounding list macro pair. +\begin{itemize} +\item[] {\tt fread} reads from {\tt stream} into the array {\tt ptr} at +most {\tt nobj} objects of size {\tt size}. {\tt fread} returns +the number of objects read. +\end{itemize} +This concludes the definitions tag. + +\subsection{How to Build Your Paper} + +You have to run {\tt latex} once to prepare your references for +munging. Then run {\tt bibtex} to build your bibliography metadata. +Then run {\tt latex} twice to ensure all references have been resolved. +If your source file is called {\tt usenixTemplate.tex} and your {\tt + bibtex} file is called {\tt usenixTemplate.bib}, here's what you do: +{\tt \small +\begin{verbatim} +latex usenixTemplate +bibtex usenixTemplate +latex usenixTemplate +latex usenixTemplate +\end{verbatim} +} + + +\subsection{Last SubSection} + +Well, it's getting boring isn't it. This is the last subsection +before we wrap it up. + +\section{Acknowledgments} + +A polite author always includes acknowledgments. Thank everyone, +especially those who funded the work. + +\section{Availability} + +It's great when this section says that MyWonderfulApp is free software, +available via anonymous FTP from + +\begin{center} +{\tt ftp.site.dom/pub/myname/Wonderful}\\ +\end{center} + +Also, it's even greater when you can write that information is also +available on the Wonderful homepage at + +\begin{center} +{\tt http://www.site.dom/\~{}myname/SWIG} +\end{center} + +Now we get serious and fill in those references. Remember you will +have to run latex twice on the document in order to resolve those +cite tags you met earlier. This is where they get resolved. +We've preserved some real ones in addition to the template-speak. +After the bibliography you are DONE. + +{\footnotesize \bibliographystyle{acm} +\bibliography{../common/bibliography}} + + +\theendnotes + +\end{document} + + + + + + + From ea2ec838ec4d123ed6ccf1bf20767fe9adbb97e0 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Thu, 28 Jun 2018 15:24:22 -0700 Subject: [PATCH 146/235] scoutfs-utils: use one super and verify its crc Signed-off-by: Zach Brown --- utils/src/crc.c | 13 ------------- utils/src/crc.h | 1 - utils/src/format.h | 20 +++++++------------- utils/src/mkfs.c | 37 +++++++++++++++++-------------------- utils/src/print.c | 44 ++++++++++++++++---------------------------- 5 files changed, 40 insertions(+), 75 deletions(-) diff --git a/utils/src/crc.c b/utils/src/crc.c index 1d027a32..38640fbc 100644 --- a/utils/src/crc.c +++ b/utils/src/crc.c @@ -37,16 +37,3 @@ u32 crc_block(struct scoutfs_block_header *hdr) return crc32c(~0, (char *)hdr + sizeof(hdr->crc), SCOUTFS_BLOCK_SIZE - sizeof(hdr->crc)); } - -u32 crc_btree_block(struct scoutfs_btree_block *bt) -{ - __le32 old; - u32 crc; - - old = bt->crc; - bt->crc = 0; - crc = crc32c(~0, bt, SCOUTFS_BLOCK_SIZE); - bt->crc = old; - - return crc; -} diff --git a/utils/src/crc.h b/utils/src/crc.h index 03ce2891..6878bf2f 100644 --- a/utils/src/crc.h +++ b/utils/src/crc.h @@ -8,6 +8,5 @@ u32 crc32c(u32 crc, const void *data, unsigned int len); u64 crc32c_64(u32 crc, const void *data, unsigned int len); u32 crc_block(struct scoutfs_block_header *hdr); -u32 crc_btree_block(struct scoutfs_btree_block *bt); #endif diff --git a/utils/src/format.h b/utils/src/format.h index 65c10b69..9addabf5 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -33,17 +33,15 @@ #define SCOUTFS_BLOCK_PAGE_ORDER (SCOUTFS_BLOCK_SHIFT - PAGE_SHIFT) /* - * The super blocks leave some room at the start of the first block for - * platform structures like boot loaders. + * The super block leaves some room before the first block for platform + * structures like boot loaders. */ -#define SCOUTFS_SUPER_BLKNO ((64 * 1024) >> SCOUTFS_BLOCK_SHIFT) -#define SCOUTFS_SUPER_NR 2 +#define SCOUTFS_SUPER_BLKNO ((64ULL * 1024) >> SCOUTFS_BLOCK_SHIFT) /* - * This header is found at the start of every block so that we can - * verify that it's what we were looking for. The crc and padding - * starts the block so that its calculation operations on a nice 64bit - * aligned region. + * This header is stored at the start of btree blocks and the super + * block for verification. The crc is calculated by zeroing the crc and + * padding so the buffer is large and aligned. */ struct scoutfs_block_header { __le32 crc; @@ -183,11 +181,7 @@ struct scoutfs_btree_item { } __packed; struct scoutfs_btree_block { - __le64 fsid; - __le64 blkno; - __le64 seq; - __le32 crc; - __le32 _pad; + struct scoutfs_block_header hdr; __le16 free_end; __le16 free_reclaim; __le16 nr_items; diff --git a/utils/src/mkfs.c b/utils/src/mkfs.c index 8002fc6b..c0dfcbf5 100644 --- a/utils/src/mkfs.c +++ b/utils/src/mkfs.c @@ -184,7 +184,6 @@ static int write_new_fs(char *path, int fd) u64 free_start; u64 free_len; int ret; - u64 i; gettimeofday(&tv, NULL); @@ -228,9 +227,8 @@ static int write_new_fs(char *path, int fd) 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 */ - blkno = round_up(SCOUTFS_SUPER_BLKNO + SCOUTFS_SUPER_NR, - SCOUTFS_SEGMENT_BLOCKS); + /* align the btree ring to the segment after the super */ + blkno = round_up(SCOUTFS_SUPER_BLKNO + 1, SCOUTFS_SEGMENT_BLOCKS); /* first usable segno follows manifest ring */ ring_blocks = calc_btree_ring_blocks(total_segs); first_segno = (blkno + ring_blocks) / SCOUTFS_SEGMENT_BLOCKS; @@ -249,9 +247,9 @@ static int write_new_fs(char *path, int fd) 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->hdr.fsid = super->hdr.fsid; + bt->hdr.blkno = cpu_to_le64(blkno); + bt->hdr.seq = cpu_to_le64(1); bt->nr_items = cpu_to_le16(2); /* btree item allocated from the back of the block */ @@ -279,7 +277,8 @@ static int write_new_fs(char *path, int fd) 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)); + bt->hdr._pad = 0; + bt->hdr.crc = cpu_to_le32(crc_block(&bt->hdr)); ret = write_raw_block(fd, blkno, bt); if (ret) @@ -293,9 +292,9 @@ static int write_new_fs(char *path, int fd) super->manifest.level_counts[1] = cpu_to_le64(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->hdr.fsid = super->hdr.fsid; + bt->hdr.blkno = cpu_to_le64(blkno); + bt->hdr.seq = cpu_to_le64(1); bt->nr_items = cpu_to_le16(1); /* btree item allocated from the back of the block */ @@ -323,7 +322,8 @@ static int write_new_fs(char *path, int fd) ino_key->ski_ino = cpu_to_le64(SCOUTFS_ROOT_INO); ino_key->sk_type = SCOUTFS_INODE_TYPE; - bt->crc = cpu_to_le32(crc_btree_block(bt)); + bt->hdr._pad = 0; + bt->hdr.crc = cpu_to_le32(crc_block(&bt->hdr)); ret = write_raw_block(fd, blkno, bt); if (ret) @@ -385,14 +385,11 @@ static int write_new_fs(char *path, int fd) goto out; } - /* write the two super blocks */ - for (i = 0; i < SCOUTFS_SUPER_NR; i++) { - super->hdr.seq = cpu_to_le64(i + 1); - ret = write_block(fd, SCOUTFS_SUPER_BLKNO + i, NULL, - &super->hdr); - if (ret) - goto out; - } + /* write the super block */ + super->hdr.seq = cpu_to_le64(1); + ret = write_block(fd, SCOUTFS_SUPER_BLKNO, NULL, &super->hdr); + if (ret) + goto out; if (fsync(fd)) { ret = -errno; diff --git a/utils/src/print.c b/utils/src/print.c index 7df3f46b..5b67f564 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -373,13 +373,13 @@ static int print_btree_block(int fd, struct scoutfs_super_block *super, if (bt->level == level) { printf("%s btree blkno %llu\n" - " fsid %llx blkno %llu seq %llu crc %08x \n" + " crc %08x fsid %llx seq %llu blkno %llu \n" " level %u free_end %u free_reclaim %u nr_items %u\n", which, le64_to_cpu(ref->blkno), - le64_to_cpu(bt->fsid), - le64_to_cpu(bt->blkno), - le64_to_cpu(bt->seq), - le32_to_cpu(bt->crc), + le32_to_cpu(bt->hdr.crc), + le64_to_cpu(bt->hdr.fsid), + le64_to_cpu(bt->hdr.seq), + le64_to_cpu(bt->hdr.blkno), bt->level, le16_to_cpu(bt->free_end), le16_to_cpu(bt->free_reclaim), @@ -490,33 +490,19 @@ static void print_super_block(struct scoutfs_super_block *super, u64 blkno) printf("\n"); } -static int print_super_blocks(int fd) +static int print_volume(int fd) { - struct scoutfs_super_block *super; - struct scoutfs_super_block recent = { .hdr.seq = 0 }; - unsigned long *seg_map; + struct scoutfs_super_block *super = NULL; + unsigned long *seg_map = NULL; u64 nr_segs; int ret = 0; int err; - int i; - int r = 0; - for (i = 0; i < SCOUTFS_SUPER_NR; i++) { - super = read_block(fd, SCOUTFS_SUPER_BLKNO + i); - if (!super) - return -ENOMEM; + super = read_block(fd, SCOUTFS_SUPER_BLKNO); + if (!super) + return -ENOMEM; - if (le64_to_cpu(super->hdr.seq) > le64_to_cpu(recent.hdr.seq)) { - memcpy(&recent, super, sizeof(recent)); - r = i; - } - - free(super); - } - - super = &recent; - - print_super_block(super, SCOUTFS_SUPER_BLKNO + r); + print_super_block(super, SCOUTFS_SUPER_BLKNO); nr_segs = le64_to_cpu(super->total_blocks) / SCOUTFS_SEGMENT_BLOCKS; seg_map = alloc_bits(nr_segs); @@ -524,7 +510,7 @@ static int print_super_blocks(int fd) ret = -ENOMEM; fprintf(stderr, "failed to alloc %llu seg map: %s (%d)\n", nr_segs, strerror(errno), errno); - return ret; + goto out; } ret = print_btree(fd, super, "alloc", &super->alloc_root, @@ -539,6 +525,8 @@ static int print_super_blocks(int fd) if (err && !ret) ret = err; +out: + free(super); free(seg_map); return ret; @@ -564,7 +552,7 @@ static int print_cmd(int argc, char **argv) return ret; } - ret = print_super_blocks(fd); + ret = print_volume(fd); close(fd); return ret; }; From f368686b89005b4f5c186e5bb0b3ea06861effe4 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Tue, 3 Jul 2018 11:54:35 -0700 Subject: [PATCH 147/235] scoutfs-utils: add net free extents Signed-off-by: Zach Brown --- utils/src/format.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/utils/src/format.h b/utils/src/format.h index 9addabf5..e1dfc9a2 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -562,6 +562,20 @@ struct scoutfs_net_extent { __le64 len; } __packed; +struct scoutfs_net_extent_list { + __le64 nr; + struct { + __le64 start; + __le64 len; + } __packed extents[0]; +} __packed; + +#define SCOUTFS_NET_EXTENT_LIST_BYTES(nr) \ + offsetof(struct scoutfs_net_extent_list, extents[nr]) + +/* arbitrarily makes a nice ~1k extent list payload */ +#define SCOUTFS_NET_EXTENT_LIST_MAX_NR 64 + /* XXX eventually we'll have net compaction and will need agents to agree */ /* one upper segment and fanout lower segments */ @@ -575,6 +589,7 @@ struct scoutfs_net_extent { enum { SCOUTFS_NET_ALLOC_INODES = 0, SCOUTFS_NET_ALLOC_EXTENT, + SCOUTFS_NET_FREE_EXTENTS, SCOUTFS_NET_ALLOC_SEGNO, SCOUTFS_NET_RECORD_SEGMENT, SCOUTFS_NET_ADVANCE_SEQ, From c3ad8282a3f2ac229a7f722f788b5fc2a22fc4dd Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Mon, 23 Jul 2018 15:33:27 -0700 Subject: [PATCH 148/235] scoutfs-utils: update net format Signed-off-by: Zach Brown --- utils/src/format.h | 85 +++++++++++++++++++++++++++++++++------------- 1 file changed, 61 insertions(+), 24 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index e1dfc9a2..6f2d46f8 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -353,8 +353,6 @@ struct scoutfs_inet_addr { __le16 port; } __packed; -#define SCOUTFS_DEFAULT_PORT 12345 - struct scoutfs_super_block { struct scoutfs_block_header hdr; __le64 id; @@ -506,6 +504,10 @@ struct scoutfs_lock_name { * messages over the wire. */ +/* + * Greetings verify identity of communicating nodes. The sender + * sends their credentials and the receiver verifies them. + */ struct scoutfs_net_greeting { __le64 fsid; __le64 format_hash; @@ -517,15 +519,70 @@ struct scoutfs_net_greeting { * type is strictly redundant in the reply because the id will find the * send but we include it in both packets to make it easier to observe * replies without having the id from their previous request. + * + * Error is only set to a translated errno on response messages and + * data_len will be 0. */ struct scoutfs_net_header { __le64 id; __le16 data_len; - __u8 type; - __u8 status; + __u8 msg; + __u8 cmd; + __u8 error; __u8 data[0]; } __packed; +/* + * Greetings are the first messages sent down every newly established + * socket on the connection. Every other message gets a unique + * increasing id over the life time of the connection. + */ +#define SCOUTFS_NET_ID_GREETING 1 + +enum { + SCOUTFS_NET_MSG_REQUEST = 0, + SCOUTFS_NET_MSG_RESPONSE, + SCOUTFS_NET_MSG_ACK, + SCOUTFS_NET_MSG_UNKNOWN, +}; + +enum { + SCOUTFS_NET_CMD_GREETING = 0, + SCOUTFS_NET_CMD_ALLOC_INODES, + SCOUTFS_NET_CMD_ALLOC_EXTENT, + SCOUTFS_NET_CMD_FREE_EXTENTS, + SCOUTFS_NET_CMD_ALLOC_SEGNO, + SCOUTFS_NET_CMD_RECORD_SEGMENT, + SCOUTFS_NET_CMD_ADVANCE_SEQ, + SCOUTFS_NET_CMD_GET_LAST_SEQ, + SCOUTFS_NET_CMD_GET_MANIFEST_ROOT, + SCOUTFS_NET_CMD_STATFS, + SCOUTFS_NET_CMD_UNKNOWN, +}; + +/* + * Define a macro to evaluate another macro for each of the errnos we + * translate over the wire. This lets us keep our enum in sync with the + * mapping arrays to and from host errnos. + */ +#define EXPAND_EACH_NET_ERRNO \ + EXPAND_NET_ERRNO(ENOENT) \ + EXPAND_NET_ERRNO(ENOMEM) \ + EXPAND_NET_ERRNO(EIO) \ + EXPAND_NET_ERRNO(ENOSPC) \ + EXPAND_NET_ERRNO(EINVAL) + +#undef EXPAND_NET_ERRNO +#define EXPAND_NET_ERRNO(which) SCOUTFS_NET_ERR_##which, +enum { + SCOUTFS_NET_ERR_NONE = 0, + EXPAND_EACH_NET_ERRNO + SCOUTFS_NET_ERR_UNKNOWN, +}; + +/* arbitrarily chosen to be safely less than mss and allow 1k with header */ +#define SCOUTFS_NET_MAX_DATA_LEN 1100 + /* * When there's no more free inodes this will be sent with ino = ~0 and * nr = 0. @@ -586,26 +643,6 @@ struct scoutfs_net_extent_list { #define SCOUTFS_COMPACTION_MAX_UPDATE \ (2 * (SCOUTFS_COMPACTION_MAX_INPUT + SCOUTFS_COMPACTION_SLOP)) -enum { - SCOUTFS_NET_ALLOC_INODES = 0, - SCOUTFS_NET_ALLOC_EXTENT, - SCOUTFS_NET_FREE_EXTENTS, - SCOUTFS_NET_ALLOC_SEGNO, - SCOUTFS_NET_RECORD_SEGMENT, - SCOUTFS_NET_ADVANCE_SEQ, - SCOUTFS_NET_GET_LAST_SEQ, - SCOUTFS_NET_GET_MANIFEST_ROOT, - SCOUTFS_NET_STATFS, - SCOUTFS_NET_UNKNOWN, -}; - -enum { - SCOUTFS_NET_STATUS_REQUEST = 0, - SCOUTFS_NET_STATUS_SUCCESS, - SCOUTFS_NET_STATUS_ERROR, - SCOUTFS_NET_STATUS_UNKNOWN, -}; - /* * Scoutfs file handle structure - this can be copied out to userspace * via open by handle or put on the wire from NFS. From 7abf5c1e2ba038e0e8327a477271334269016676 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Thu, 16 Aug 2018 13:57:01 -0700 Subject: [PATCH 149/235] scoutfs-utils: calculate segment crc in mkfs Signed-off-by: Zach Brown --- utils/src/crc.c | 9 +++++++++ utils/src/crc.h | 1 + utils/src/format.h | 3 +++ utils/src/mkfs.c | 1 + 4 files changed, 14 insertions(+) diff --git a/utils/src/crc.c b/utils/src/crc.c index 38640fbc..714afa90 100644 --- a/utils/src/crc.c +++ b/utils/src/crc.c @@ -37,3 +37,12 @@ u32 crc_block(struct scoutfs_block_header *hdr) return crc32c(~0, (char *)hdr + sizeof(hdr->crc), SCOUTFS_BLOCK_SIZE - sizeof(hdr->crc)); } + +u32 crc_segment(struct scoutfs_segment_block *sblk) +{ + u32 off = offsetof(struct scoutfs_segment_block, _padding) + + sizeof(sblk->_padding); + + return crc32c(~0, (char *)sblk + off, + le32_to_cpu(sblk->total_bytes) - off); +} diff --git a/utils/src/crc.h b/utils/src/crc.h index 6878bf2f..a928bf0a 100644 --- a/utils/src/crc.h +++ b/utils/src/crc.h @@ -8,5 +8,6 @@ u32 crc32c(u32 crc, const void *data, unsigned int len); u64 crc32c_64(u32 crc, const void *data, unsigned int len); u32 crc_block(struct scoutfs_block_header *hdr); +u32 crc_segment(struct scoutfs_segment_block *seg); #endif diff --git a/utils/src/format.h b/utils/src/format.h index 6f2d46f8..5a01f57b 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -271,6 +271,9 @@ struct scoutfs_segment_item { /* * Each large segment starts with a segment block that describes the * rest of the blocks that make up the segment. + * + * The crc covers the initial total_bytes of the segment but starts + * after the padding. */ struct scoutfs_segment_block { __le32 crc; diff --git a/utils/src/mkfs.c b/utils/src/mkfs.c index c0dfcbf5..678114ef 100644 --- a/utils/src/mkfs.c +++ b/utils/src/mkfs.c @@ -377,6 +377,7 @@ static int write_new_fs(char *path, int fd) item = (void *)(inode + 1); sblk->total_bytes = cpu_to_le32((long)item - (long)sblk); + sblk->crc = cpu_to_le32(crc_segment(sblk)); ret = pwrite(fd, sblk, SCOUTFS_SEGMENT_SIZE, first_segno << SCOUTFS_SEGMENT_SHIFT); From 078d2f6073ba1347ab361fe28962558bae522652 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Mon, 30 Jul 2018 14:15:22 -0700 Subject: [PATCH 150/235] scoutfs-utils: update format for greeting node_id Signed-off-by: Zach Brown --- utils/src/format.h | 2 ++ utils/src/mkfs.c | 1 + utils/src/print.c | 2 ++ 3 files changed, 5 insertions(+) diff --git a/utils/src/format.h b/utils/src/format.h index 5a01f57b..95f732c9 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -368,6 +368,7 @@ struct scoutfs_super_block { __le64 alloc_cursor; struct scoutfs_btree_ring bring; __le64 next_seg_seq; + __le64 next_node_id; struct scoutfs_btree_root alloc_root; struct scoutfs_manifest manifest; struct scoutfs_inet_addr server_addr; @@ -514,6 +515,7 @@ struct scoutfs_lock_name { struct scoutfs_net_greeting { __le64 fsid; __le64 format_hash; + __le64 node_id; } __packed; /* diff --git a/utils/src/mkfs.c b/utils/src/mkfs.c index 678114ef..275a0347 100644 --- a/utils/src/mkfs.c +++ b/utils/src/mkfs.c @@ -226,6 +226,7 @@ static int write_new_fs(char *path, int fd) super->next_seq = cpu_to_le64(1); super->total_blocks = cpu_to_le64(total_blocks); super->next_seg_seq = cpu_to_le64(2); + super->next_node_id = cpu_to_le64(1); /* align the btree ring to the segment after the super */ blkno = round_up(SCOUTFS_SUPER_BLKNO + 1, SCOUTFS_SEGMENT_BLOCKS); diff --git a/utils/src/print.c b/utils/src/print.c index 5b67f564..95b7ff45 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -457,6 +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" + " next_node_id %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" @@ -465,6 +466,7 @@ 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->next_node_id), le64_to_cpu(super->total_blocks), le64_to_cpu(super->free_blocks), le64_to_cpu(super->alloc_cursor), From bf014a4c57083088fe4bbb4c85a8836f9815c2be Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Thu, 23 Aug 2018 14:02:36 -0700 Subject: [PATCH 151/235] scoutfs-utils: update format network requests We updated the format header when relaxing the restrictions on duplicate request message processing. Signed-off-by: Zach Brown --- utils/src/format.h | 26 ++++++-------------------- 1 file changed, 6 insertions(+), 20 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index 95f732c9..195bc3c7 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -520,36 +520,22 @@ struct scoutfs_net_greeting { /* * This header precedes and describes all network messages sent over - * sockets. The id is set by the request and sent in the reply. The - * type is strictly redundant in the reply because the id will find the - * send but we include it in both packets to make it easier to observe - * replies without having the id from their previous request. + * sockets. The id is set by the request and sent in the response. * - * Error is only set to a translated errno on response messages and - * data_len will be 0. + * Error is only set to a translated errno and will only be found in + * response messages. */ struct scoutfs_net_header { __le64 id; __le16 data_len; - __u8 msg; __u8 cmd; + __u8 flags; __u8 error; __u8 data[0]; } __packed; -/* - * Greetings are the first messages sent down every newly established - * socket on the connection. Every other message gets a unique - * increasing id over the life time of the connection. - */ -#define SCOUTFS_NET_ID_GREETING 1 - -enum { - SCOUTFS_NET_MSG_REQUEST = 0, - SCOUTFS_NET_MSG_RESPONSE, - SCOUTFS_NET_MSG_ACK, - SCOUTFS_NET_MSG_UNKNOWN, -}; +#define SCOUTFS_NET_FLAG_RESPONSE (1 << 0) +#define SCOUTFS_NET_FLAGS_UNKNOWN (U8_MAX << 1) enum { SCOUTFS_NET_CMD_GREETING = 0, From bbfa71361f6d62f36595c23458b7339b7ae7347b Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Wed, 8 Aug 2018 16:08:13 -0700 Subject: [PATCH 152/235] scoutfs-utils: compaction request format update Signed-off-by: Zach Brown --- utils/src/format.h | 53 ++++++++++++++++++++++++++++++++++++++-------- utils/src/mkfs.c | 1 + utils/src/print.c | 3 ++- 3 files changed, 47 insertions(+), 10 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index 195bc3c7..d7736a38 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -369,6 +369,7 @@ struct scoutfs_super_block { struct scoutfs_btree_ring bring; __le64 next_seg_seq; __le64 next_node_id; + __le64 next_compact_id; struct scoutfs_btree_root alloc_root; struct scoutfs_manifest manifest; struct scoutfs_inet_addr server_addr; @@ -548,6 +549,7 @@ enum { SCOUTFS_NET_CMD_GET_LAST_SEQ, SCOUTFS_NET_CMD_GET_MANIFEST_ROOT, SCOUTFS_NET_CMD_STATFS, + SCOUTFS_NET_CMD_COMPACT, SCOUTFS_NET_CMD_UNKNOWN, }; @@ -595,7 +597,6 @@ struct scoutfs_net_manifest_entry { struct scoutfs_key first; struct scoutfs_key last; __u8 level; - __u8 keys[0]; } __packed; struct scoutfs_net_statfs { @@ -624,15 +625,49 @@ struct scoutfs_net_extent_list { /* arbitrarily makes a nice ~1k extent list payload */ #define SCOUTFS_NET_EXTENT_LIST_MAX_NR 64 -/* XXX eventually we'll have net compaction and will need agents to agree */ - /* one upper segment and fanout lower segments */ -#define SCOUTFS_COMPACTION_MAX_INPUT (1 + SCOUTFS_MANIFEST_FANOUT) -/* sticky can add one, and so can item page alignment */ -#define SCOUTFS_COMPACTION_SLOP 2 -/* delete all inputs and insert all outputs (same goes for alloc|free segnos) */ -#define SCOUTFS_COMPACTION_MAX_UPDATE \ - (2 * (SCOUTFS_COMPACTION_MAX_INPUT + SCOUTFS_COMPACTION_SLOP)) +#define SCOUTFS_COMPACTION_MAX_INPUT (1 + SCOUTFS_MANIFEST_FANOUT) +/* sticky can split the input and item alignment padding can add a lower */ +#define SCOUTFS_COMPACTION_SEGNO_OVERHEAD 2 +#define SCOUTFS_COMPACTION_MAX_OUTPUT \ + (SCOUTFS_COMPACTION_MAX_INPUT + SCOUTFS_COMPACTION_SEGNO_OVERHEAD) + +/* + * A compact request is sent by the server to the client. It provides + * the input segments and enough allocated segnos to write the results. + * The id uniquely identifies this compaction request and is included in + * the response to clean up its allocated resources. + */ +struct scoutfs_net_compact_request { + __le64 id; + __u8 last_level; + __u8 flags; + __le64 segnos[SCOUTFS_COMPACTION_MAX_OUTPUT]; + struct scoutfs_net_manifest_entry ents[SCOUTFS_COMPACTION_MAX_INPUT]; +} __packed; + +/* + * A sticky compaction has more lower level segments that overlap with + * the end of the upper after the last lower level segment included in + * the compaction. Items left in the upper segment after the last lower + * need to be written to the upper level instead of the lower. The + * upper segment "sticks" in place instead of moving down to the lower + * level. + */ +#define SCOUTFS_NET_COMPACT_FLAG_STICKY (1 << 0) + +/* + * A compact response is sent by the client to the server. It describes + * the written output segments that need to be added to the manifest. + * The server compares the response to the request to free unused + * allocated segnos and input manifest entries. An empty response is + * valid and can happen if, say, the upper input segment completely + * deleted all the items in a single overlapping lower segment. + */ +struct scoutfs_net_compact_response { + __le64 id; + struct scoutfs_net_manifest_entry ents[SCOUTFS_COMPACTION_MAX_OUTPUT]; +} __packed; /* * Scoutfs file handle structure - this can be copied out to userspace diff --git a/utils/src/mkfs.c b/utils/src/mkfs.c index 275a0347..9f0aae48 100644 --- a/utils/src/mkfs.c +++ b/utils/src/mkfs.c @@ -227,6 +227,7 @@ static int write_new_fs(char *path, int fd) super->total_blocks = cpu_to_le64(total_blocks); super->next_seg_seq = cpu_to_le64(2); super->next_node_id = cpu_to_le64(1); + super->next_compact_id = cpu_to_le64(1); /* align the btree ring to the segment after the super */ blkno = round_up(SCOUTFS_SUPER_BLKNO + 1, SCOUTFS_SEGMENT_BLOCKS); diff --git a/utils/src/print.c b/utils/src/print.c index 95b7ff45..5115b84f 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -457,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" - " next_node_id %llu\n" + " next_node_id %llu next_compact_id %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" @@ -467,6 +467,7 @@ static void print_super_block(struct scoutfs_super_block *super, u64 blkno) le64_to_cpu(super->next_seq), le64_to_cpu(super->next_seg_seq), le64_to_cpu(super->next_node_id), + le64_to_cpu(super->next_compact_id), le64_to_cpu(super->total_blocks), le64_to_cpu(super->free_blocks), le64_to_cpu(super->alloc_cursor), From 92f22358a7ee6a6dee24a62af0fe09c7bcbb3f42 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Fri, 14 Sep 2018 13:51:09 -0700 Subject: [PATCH 153/235] scoutfs-utils: add rpm build make dist helpers Add make targets to build a spec file and tarball with a version based on a git tag. Signed-off-by: Zach Brown --- utils/.gitignore | 2 ++ utils/Makefile | 18 +++++++++- utils/scoutfs-utils.spec.in | 66 +++++++++++++++++++++++++++++++++++++ 3 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 utils/scoutfs-utils.spec.in diff --git a/utils/.gitignore b/utils/.gitignore index 0a68a563..3d9129b6 100644 --- a/utils/.gitignore +++ b/utils/.gitignore @@ -5,3 +5,5 @@ src/scoutfs .sparse* .mock.build* cscope.* +scoutfs-utils.spec +scoutfs-utils-*.tar diff --git a/utils/Makefile b/utils/Makefile index 39cd7202..978d8de1 100644 --- a/utils/Makefile +++ b/utils/Makefile @@ -33,6 +33,22 @@ $(BIN): $(OBJ) $(QU) [SP $<] $(VE)./sparse.sh -Wbitwise -D__CHECKER__ $(CFLAGS) $< -.PHONY: clean +.PHONY: .FORCE + +# - We use the git describe from tags to set up the RPM versioning +RPM_VERSION := $(shell git describe --long --tags | awk -F '-' '{gsub(/^v/,""); print $$1}') +RPM_GITHASH := $(shell git rev-parse --short HEAD) + +%.spec: %.spec.in .FORCE + sed -e 's/@@VERSION@@/$(RPM_VERSION)/g' \ + -e 's/@@GITHASH@@/$(RPM_GITHASH)/g' < $< > $@+ + mv $@+ $@ + +TARFILE = scoutfs-utils-$(RPM_VERSION).tar + +dist: $(RPM_DIR) scoutfs-utils.spec + git archive --format=tar --prefix scoutfs-utils-$(RPM_VERSION)/ HEAD^{tree} > $(TARFILE) + @ tar rf $(TARFILE) --transform="s@\(.*\)@scoutfs-utils-$(RPM_VERSION)/\1@" scoutfs-utils.spec + clean: @rm -f $(BIN) $(OBJ) $(DEPS) .sparse.* diff --git a/utils/scoutfs-utils.spec.in b/utils/scoutfs-utils.spec.in new file mode 100644 index 00000000..7c8bf445 --- /dev/null +++ b/utils/scoutfs-utils.spec.in @@ -0,0 +1,66 @@ +%define pkg_version @@VERSION@@ +%define pkg_git_hash @@GITHASH@@ +%define pkg_date %(date +%%Y%%m%%d) + +%{!?_release: %global _release 0.%{pkg_date}git%{pkg_git_hash}} + +Name: scoutfs-utils +Summary: scoutfs user space utilities +Version: %{pkg_version} +Release: %{_release}%{?dist} +License: GPLv2 +Group: System Environment/Base +URL: http://scoutfs.org/ + +BuildRequires: git +BuildRequires: gzip +BuildRequires: libuuid-devel +BuildRequires: openssl-devel + +#Requires: kmod-scoutfs = %{version} + +Source: scoutfs-utils-%{pkg_version}.tar + +# Disable the building of the debug package(s). +%define debug_package %{nil} + +%description +scoutfs - user space utilities + +%package -n scoutfs-devel +Summary: scoutfs devel headers +Version: %{pkg_version} +Release: %{_release}%{?dist} +License: GPLv2 +Group: Development/Libraries +URL: http://scoutfs.org/ + +%description -n scoutfs-devel +scoutfs - development headers + +%prep +%setup -q -n scoutfs-utils-%{pkg_version} + +%build +make +gzip man/*.7 + +%install +mkdir -p $RPM_BUILD_ROOT%{_mandir}/man7 +cp man/*.7.gz $RPM_BUILD_ROOT%{_mandir}/man7/. +install -m 755 -D src/scoutfs $RPM_BUILD_ROOT%{_sbindir}/scoutfs +install -m 644 -D src/ioctl.h $RPM_BUILD_ROOT%{_includedir}/scoutfs/ioctl.h +install -m 644 -D src/format.h $RPM_BUILD_ROOT%{_includedir}/scoutfs/format.h + +%files +%defattr(644,root,root,755) +%{_mandir}/man7/scoutfs-corruption.7.gz +%{_sbindir}/scoutfs + +%files -n scoutfs-devel +%defattr(644,root,root,755) +%{_includedir}/scoutfs + +%clean +rm -rf %{buildroot} + From 266b6d8bdd172a938f7dd1b46cf96ea733ae7bd8 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Fri, 14 Sep 2018 13:52:05 -0700 Subject: [PATCH 154/235] scoutfs-utils: add a README.md Add a markdown README for github. Signed-off-by: Zach Brown --- utils/README.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 utils/README.md diff --git a/utils/README.md b/utils/README.md new file mode 100644 index 00000000..4eb0650e --- /dev/null +++ b/utils/README.md @@ -0,0 +1,7 @@ +This repository contains the userspace software for the scoutfs +clustered filesystem. + +More context and instructions can be found on the https://scoutfs.org/ +community site or in the +[scoutfs-kmod-dev](https://github.com/versity/scoutfs-kmod-dev) git +repository which houses the scoutfs Linux kernel module. From f59dfe8b73ed2a7473bb6c9966188e3d9e2a0b66 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Fri, 14 Sep 2018 16:18:33 -0700 Subject: [PATCH 155/235] scoutfs-utils: make scoutfs binary executable The %defattr in the %files section was accidentally setting the installed scoutfs binary's mode to 644. Signed-off-by: Zach Brown --- utils/scoutfs-utils.spec.in | 1 + 1 file changed, 1 insertion(+) diff --git a/utils/scoutfs-utils.spec.in b/utils/scoutfs-utils.spec.in index 7c8bf445..81847d9d 100644 --- a/utils/scoutfs-utils.spec.in +++ b/utils/scoutfs-utils.spec.in @@ -55,6 +55,7 @@ install -m 644 -D src/format.h $RPM_BUILD_ROOT%{_includedir}/scoutfs/format.h %files %defattr(644,root,root,755) %{_mandir}/man7/scoutfs-corruption.7.gz +%defattr(755,root,root,755) %{_sbindir}/scoutfs %files -n scoutfs-devel From ea969a5dde2a54fb5c0d9e32968aeee401966c67 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Wed, 10 Oct 2018 11:09:48 -0700 Subject: [PATCH 156/235] scoutfs-utils: update format.h for quorum Signed-off-by: Zach Brown --- utils/src/format.h | 98 +++++++++++++++++++--- utils/src/mkfs.c | 199 ++++++++++++++++++++++++++++++++++++++++++++- utils/src/print.c | 86 +++++++++++++++++++- utils/src/rand.c | 2 +- 4 files changed, 366 insertions(+), 19 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index d7736a38..6d1bd222 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -38,6 +38,33 @@ */ #define SCOUTFS_SUPER_BLKNO ((64ULL * 1024) >> SCOUTFS_BLOCK_SHIFT) +/* + * A reasonably large region of aligned quorum blocks follow the super + * block. + */ +#define SCOUTFS_QUORUM_BLKNO ((128ULL * 1024) >> SCOUTFS_BLOCK_SHIFT) +#define SCOUTFS_QUORUM_BLOCKS ((128ULL * 1024) >> SCOUTFS_BLOCK_SHIFT) +#define SCOUTFS_QUORUM_MAX_SLOTS SCOUTFS_QUORUM_BLOCKS + +/* + * Base types used by other structures. + */ +struct scoutfs_timespec { + __le64 sec; + __le32 nsec; +} __packed; + +struct scoutfs_betimespec { + __be64 sec; + __be32 nsec; +} __packed; + +/* XXX ipv6 */ +struct scoutfs_inet_addr { + __le32 addr; + __le16 port; +} __packed; + /* * This header is stored at the start of btree blocks and the super * block for verification. The crc is calculated by zeroing the crc and @@ -340,22 +367,72 @@ struct scoutfs_xattr { __u8 name[0]; } __packed; -struct scoutfs_betimespec { - __be64 sec; - __be32 nsec; -} __packed; /* XXX does this exist upstream somewhere? */ #define member_sizeof(TYPE, MEMBER) (sizeof(((TYPE *)0)->MEMBER)) #define SCOUTFS_UUID_BYTES 16 +#define SCOUTFS_UNIQUE_NAME_MAX_BYTES 64 /* includes null */ -/* XXX ipv6 */ -struct scoutfs_inet_addr { - __le32 addr; - __le16 port; +/* + * During each quorum voting interval the fabric has to process 2 reads + * and a write for each voting mount. The only reason we limit the + * number of active quorum mounts is to limit the number of IOs per + * interval. We use a pretty conservative interval given that IOs will + * generally be faster than our constant and we'll have fewer active + * than the max. + */ +#define SCOUTFS_QUORUM_MAX_ACTIVE 7 +#define SCOUTFS_QUORUM_IO_LATENCY_MS 10 +#define SCOUTFS_QUORUM_INTERVAL_MS \ + (SCOUTFS_QUORUM_MAX_ACTIVE * 3 * SCOUTFS_QUORUM_IO_LATENCY_MS) + +/* + * Each mount that is found in the quorum config in the super block can + * write to quorum blocks indicating which mount they vote for as + * the leader. + * + * @config_gen: references the config gen in the super block + * @write_nr: incremented for every write, only 0 when never written + * @elected_nr: incremented when elected, 0 otherwise + * @vote_slot: the active config slot that the writer is voting for + */ +struct scoutfs_quorum_block { + __le64 fsid; + __le64 blkno; + __le64 config_gen; + __le64 write_nr; + __le64 elected_nr; + __le32 crc; + __u8 vote_slot; } __packed; +#define SCOUTFS_QUORUM_MAX_SLOTS SCOUTFS_QUORUM_BLOCKS + +/* + * Each quorum voter is described by a slot which corresponds to the + * block that the voter will write to. + * + * The stale flag is used to support config migration. A new + * configuration is written in free slots and the old configuration is + * marked stale. Stale slots can only be reclaimed once we have + * evidence that the named mount won't try and write to it by seeing it + * write to other slots or connect with the new gen. + */ +struct scoutfs_quorum_config { + __le64 gen; + struct scoutfs_quorum_slot { + __u8 name[SCOUTFS_UNIQUE_NAME_MAX_BYTES]; + struct scoutfs_inet_addr addr; + __u8 vote_priority; + __u8 flags; + } __packed slots[SCOUTFS_QUORUM_MAX_SLOTS]; +} __packed; + +#define SCOUTFS_QUORUM_SLOT_ACTIVE (1 << 0) +#define SCOUTFS_QUORUM_SLOT_STALE (1 << 1) +#define SCOUTFS_QUORUM_SLOT_FLAGS_UNKNOWN (U8_MAX << 2) + struct scoutfs_super_block { struct scoutfs_block_header hdr; __le64 id; @@ -373,14 +450,11 @@ struct scoutfs_super_block { struct scoutfs_btree_root alloc_root; struct scoutfs_manifest manifest; struct scoutfs_inet_addr server_addr; + struct scoutfs_quorum_config quorum_config; } __packed; #define SCOUTFS_ROOT_INO 1 -struct scoutfs_timespec { - __le64 sec; - __le32 nsec; -} __packed; /* * @meta_seq: advanced the first time an inode is updated in a given diff --git a/utils/src/mkfs.c b/utils/src/mkfs.c index 9f0aae48..de270550 100644 --- a/utils/src/mkfs.c +++ b/utils/src/mkfs.c @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -10,6 +11,11 @@ #include #include #include +#include +#include +#include +#include +#include #include "sparse.h" #include "cmd.h" @@ -157,7 +163,7 @@ static char *size_str(u64 nr, unsigned size) * - btree ring blocks with manifest and allocator btree blocks * - segment with root inode items */ -static int write_new_fs(char *path, int fd) +static int write_new_fs(char *path, int fd, struct scoutfs_quorum_config *conf) { struct scoutfs_super_block *super; struct scoutfs_key *ino_key; @@ -170,10 +176,13 @@ static int write_new_fs(char *path, int fd) struct scoutfs_btree_block *bt; struct scoutfs_btree_item *btitem; struct scoutfs_segment_item *item; + struct scoutfs_quorum_slot *slot; struct scoutfs_key key; + struct in_addr in; __le32 *prev_link; struct timeval tv; char uuid_str[37]; + void *zeros; u64 blkno; u64 limit; u64 size; @@ -184,13 +193,15 @@ static int write_new_fs(char *path, int fd) u64 free_start; u64 free_len; int ret; + int i; gettimeofday(&tv, NULL); super = calloc(1, SCOUTFS_BLOCK_SIZE); bt = calloc(1, SCOUTFS_BLOCK_SIZE); sblk = calloc(1, SCOUTFS_SEGMENT_SIZE); - if (!super || !bt || !sblk) { + zeros = calloc(1, SCOUTFS_SEGMENT_SIZE); + if (!super || !bt || !sblk || !zeros) { ret = -errno; fprintf(stderr, "failed to allocate block mem: %s (%d)\n", strerror(errno), errno); @@ -229,6 +240,9 @@ static int write_new_fs(char *path, int fd) super->next_node_id = cpu_to_le64(1); super->next_compact_id = cpu_to_le64(1); + super->quorum_config = *conf; + super->quorum_config.gen = cpu_to_le64(1); + /* align the btree ring to the segment after the super */ blkno = round_up(SCOUTFS_SUPER_BLKNO + 1, SCOUTFS_SEGMENT_BLOCKS); /* first usable segno follows manifest ring */ @@ -388,6 +402,16 @@ static int write_new_fs(char *path, int fd) goto out; } + /* zero out quorum blocks */ + for (i = 0; i < SCOUTFS_QUORUM_BLOCKS; i++) { + ret = write_raw_block(fd, SCOUTFS_QUORUM_BLKNO + i, zeros); + if (ret < 0) { + fprintf(stderr, "error zeroing quorum block: %s (%d)\n", + strerror(-errno), -errno); + goto out; + } + } + /* write the super block */ super->hdr.seq = cpu_to_le64(1); ret = write_block(fd, SCOUTFS_SUPER_BLKNO, NULL, &super->hdr); @@ -423,6 +447,19 @@ static int write_new_fs(char *path, int fd) SIZE_ARGS(le64_to_cpu(super->free_blocks), SCOUTFS_BLOCK_SIZE)); + printf(" quorum slots:\n"); + for (i = 0; i < array_size(super->quorum_config.slots); i++) { + slot = &super->quorum_config.slots[i]; + if (slot->flags == 0) + continue; + + in.s_addr = htonl(le32_to_cpu(slot->addr.addr)); + + printf(" [%2u]: name %s priority %u addr:port %s:%u\n", + i, slot->name, slot->vote_priority, + inet_ntoa(in), le16_to_cpu(slot->addr.port)); + } + ret = 0; out: if (super) @@ -431,20 +468,174 @@ out: free(bt); if (sblk) free(sblk); + if (zeros) + free(zeros); + return ret; +} + +static struct option long_ops[] = { + { "quorum_slot", 1, NULL, 'Q' }, + { NULL, 0, NULL, 0} +}; + +enum { NAME, PRIORITY, ADDR, PORT }; + +static int parse_quorum_slot(struct scoutfs_quorum_config *conf, char *arg) +{ + struct scoutfs_quorum_slot *slot; + struct scoutfs_quorum_slot *sl; + struct in_addr in; + unsigned long port; + int free_slot; + char *save; + char *tok; + char *dup; + char *s; + int ret; + int i; + + dup = strdup(arg); + if (!dup) { + printf("allocation failure while parsing quorum slot '%s'\n", + arg); + return -EINVAL; + } + + for (i = 0; i < array_size(conf->slots); i++) { + if (conf->slots[i].flags == 0) + break; + } + if (i == array_size(conf->slots)) { + printf("too many quorum slots provided\n"); + ret = -EINVAL; + goto out; + } + slot = &conf->slots[i]; + free_slot = i; + + slot->addr.port = cpu_to_le16(23853); /* randomly chosen */ + + for (save = NULL, s = dup, i = NAME; i <= PORT; i++, s = NULL) { + tok = strtok_r(s, ":", &save); + + if (tok == NULL) + break; + + /* assume flags and a default port */ + if (i == PORT && !isdigit(tok[0])) + i = PRIORITY; + + switch(i) { + case NAME: + if (strlen(tok) >= SCOUTFS_UNIQUE_NAME_MAX_BYTES) { + printf("quorum slot name too long: %s\n", tok); + return -EINVAL; + } + strcpy((char *)slot->name, tok); + break; + + case PRIORITY: + slot->vote_priority = strtoul(tok, NULL, 0); + if (slot->vote_priority > 255) { + printf("invalid quorum slot priority: %s\n", + tok); + ret = -EINVAL; + goto out; + } + break; + + case ADDR: + if (inet_aton(tok, &in) == 0) { + printf("invalid quorum slot address: %s\n", tok); + ret = -EINVAL; + goto out; + } + slot->addr.addr = cpu_to_le32(htonl(in.s_addr)); + break; + + case PORT: + port = strtoul(tok, NULL, 0); + if (port == 0 || port >= 65535) { + printf("invalid quorum slot port: %s\n", tok); + ret = -EINVAL; + goto out; + } + slot->addr.port = cpu_to_le16(port); + break; + + } + } + + if (slot->name[0] == '\0') { + printf("quorum slot must specify name: %s\n", arg); + ret = -EINVAL; + goto out; + } + + if (slot->addr.addr == 0) { + printf("quorum slot must specify address: %s\n", arg); + ret = -EINVAL; + goto out; + } + + for (i = 0; i < free_slot; i++) { + sl = &conf->slots[i]; + + if (strcmp((char *)slot->name, (char *)sl->name) == 0) { + printf("duplicate quorum slot name: %s\n", arg); + ret = -EINVAL; + goto out; + } + + if (memcmp(&slot->addr, &sl->addr, sizeof(slot->addr)) == 0) { + printf("duplicate quorum slot addr: %s\n", arg); + ret = -EINVAL; + goto out; + } + } + + slot->flags = SCOUTFS_QUORUM_SLOT_ACTIVE; + ret = 0; +out: + free(dup); return ret; } static int mkfs_func(int argc, char *argv[]) { + struct scoutfs_quorum_config conf = {0,}; + bool have_quorum = false; char *path = argv[1]; int ret; int fd; + int c; - if (argc != 2) { + while ((c = getopt_long(argc, argv, "Q:", long_ops, NULL)) != -1) { + switch (c) { + case 'Q': + ret = parse_quorum_slot(&conf, optarg); + if (ret) + return ret; + have_quorum = true; + break; + case '?': + default: + return -EINVAL; + } + } + + if (optind >= argc) { printf("scoutfs: mkfs: a single path argument is required\n"); return -EINVAL; } + path = argv[optind]; + + if (!have_quorum) { + printf("must configure quorum with --quorum_slot|-Q options\n"); + return -EINVAL; + } + fd = open(path, O_RDWR | O_EXCL); if (fd < 0) { ret = -errno; @@ -453,7 +644,7 @@ static int mkfs_func(int argc, char *argv[]) return ret; } - ret = write_new_fs(path, fd); + ret = write_new_fs(path, fd, &conf); close(fd); return ret; diff --git a/utils/src/print.c b/utils/src/print.c index 5115b84f..e21f3f84 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -9,6 +9,9 @@ #include #include #include +#include +#include +#include #include "sparse.h" #include "util.h" @@ -440,9 +443,68 @@ static int print_btree(int fd, struct scoutfs_super_block *super, char *which, return ret; } +static int print_quorum_blocks(int fd, struct scoutfs_super_block *super) +{ + struct scoutfs_quorum_block *blk; + u64 blkno; + int ret; + int i; + + for (i = 0; i < SCOUTFS_QUORUM_BLOCKS; i++) { + blkno = SCOUTFS_QUORUM_BLKNO + i; + blk = read_block(fd, blkno); + if (!blk) { + ret = -ENOMEM; + break; + } + + if (blk->fsid != 0 || blk->write_nr != 0) { + printf("quorum block blkno %llu\n" + " fsid %llx blkno %llu config_gen %llu crc 0x%08x\n" + " write_nr %llu elected_nr %llu vote_slot %u\n", + blkno, le64_to_cpu(blk->fsid), + le64_to_cpu(blk->blkno), + le64_to_cpu(blk->config_gen), + le32_to_cpu(blk->crc), + le64_to_cpu(blk->write_nr), + le64_to_cpu(blk->elected_nr), + blk->vote_slot); + } + + free(blk); + ret = 0; + } + + return ret; +} + +static void print_slot_flags(unsigned long flags) +{ + if (flags == 0) { + printf("-"); + return; + } + + while (flags) { + if (flags & SCOUTFS_QUORUM_SLOT_ACTIVE) { + printf("active"); + flags &= ~SCOUTFS_QUORUM_SLOT_ACTIVE; + + } else if (flags & SCOUTFS_QUORUM_SLOT_STALE) { + printf("stale"); + flags &= ~SCOUTFS_QUORUM_SLOT_STALE; + } + + if (flags) + printf(","); + } +} + static void print_super_block(struct scoutfs_super_block *super, u64 blkno) { + struct scoutfs_quorum_slot *slot; char uuid_str[37]; + struct in_addr in; u64 count; int i; @@ -491,6 +553,22 @@ static void print_super_block(struct scoutfs_super_block *super, u64 blkno) printf(" %u: %llu", i, count); } printf("\n"); + + printf(" quorum_config:\n gen: %llu\n", + le64_to_cpu(super->quorum_config.gen)); + for (i = 0; i < array_size(super->quorum_config.slots); i++) { + slot = &super->quorum_config.slots[i]; + if (slot->flags == 0) + continue; + + in.s_addr = htonl(le32_to_cpu(slot->addr.addr)); + + printf(" [%2u]: name %s priority %u addr %s:%u flags ", + i, slot->name, slot->vote_priority, inet_ntoa(in), + le16_to_cpu(slot->addr.port)); + print_slot_flags(slot->flags); + printf("\n"); + } } static int print_volume(int fd) @@ -516,8 +594,12 @@ static int print_volume(int fd) goto out; } - ret = print_btree(fd, super, "alloc", &super->alloc_root, - print_alloc_item, NULL); + ret = print_quorum_blocks(fd, super); + + err = print_btree(fd, super, "alloc", &super->alloc_root, + print_alloc_item, NULL); + if (err && !ret) + ret = err; err = print_btree(fd, super, "manifest", &super->manifest.root, print_manifest_entry, seg_map); diff --git a/utils/src/rand.c b/utils/src/rand.c index e5444810..8af89165 100644 --- a/utils/src/rand.c +++ b/utils/src/rand.c @@ -8,5 +8,5 @@ void pseudo_random_bytes(void *data, unsigned int len) { - RAND_pseudo_bytes(data, len); + RAND_bytes(data, len); } From 02d2edb46722d7b0a7c654598cd789270608c24e Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Tue, 26 Mar 2019 14:30:52 -0700 Subject: [PATCH 157/235] scoutfs-utils: remove super server_addr The server no longer stores the address to connect to in the super block. It's now stored in the quorum config and voting blocks. Signed-off-by: Zach Brown --- utils/src/format.h | 1 - 1 file changed, 1 deletion(-) diff --git a/utils/src/format.h b/utils/src/format.h index 6d1bd222..cbbd5e5f 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -449,7 +449,6 @@ struct scoutfs_super_block { __le64 next_compact_id; struct scoutfs_btree_root alloc_root; struct scoutfs_manifest manifest; - struct scoutfs_inet_addr server_addr; struct scoutfs_quorum_config quorum_config; } __packed; From dd117593da4e6d0a86719aaf64af08a96cea9de8 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Mon, 14 Jan 2019 14:51:49 -0800 Subject: [PATCH 158/235] scoutfs-utils: update format for locking service Update the format header to reflect that the kernel now uses a locking service instead of using an fs/dlm lockspace. Nothing in userspace uses locking. Signed-off-by: Zach Brown --- utils/src/format.h | 61 +++++++++++++++++++++++++++++++--------------- 1 file changed, 42 insertions(+), 19 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index cbbd5e5f..a8ebb465 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -320,7 +320,8 @@ struct scoutfs_segment_block { #define SCOUTFS_INODE_INDEX_ZONE 1 #define SCOUTFS_NODE_ZONE 2 #define SCOUTFS_FS_ZONE 3 -#define SCOUTFS_MAX_ZONE 4 /* power of 2 is efficient */ +#define SCOUTFS_LOCK_ZONE 4 +#define SCOUTFS_MAX_ZONE 8 /* power of 2 is efficient */ /* inode index zone */ #define SCOUTFS_INODE_INDEX_META_SEQ_TYPE 1 @@ -341,6 +342,9 @@ struct scoutfs_segment_block { #define SCOUTFS_FILE_EXTENT_TYPE 7 #define SCOUTFS_ORPHAN_TYPE 8 +/* lock zone, only ever found in lock ranges, never in persistent items */ +#define SCOUTFS_RENAME_TYPE 1 + #define SCOUTFS_MAX_TYPE 16 /* power of 2 is efficient */ /* @@ -556,26 +560,8 @@ enum { #define SCOUTFS_MAX_VAL_SIZE SCOUTFS_XATTR_MAX_PART_SIZE -/* - * structures used by dlm - */ -#define SCOUTFS_LOCK_SCOPE_GLOBAL 1 -#define SCOUTFS_LOCK_SCOPE_FS_ITEMS 2 - -#define SCOUTFS_LOCK_TYPE_GLOBAL_RENAME 1 -#define SCOUTFS_LOCK_TYPE_GLOBAL_SERVER 2 - -struct scoutfs_lock_name { - __u8 scope; - __u8 zone; - __u8 type; - __le64 first; - __le64 second; -} __packed; - #define SCOUTFS_LOCK_INODE_GROUP_NR 1024 #define SCOUTFS_LOCK_INODE_GROUP_MASK (SCOUTFS_LOCK_INODE_GROUP_NR - 1) - #define SCOUTFS_LOCK_SEQ_GROUP_MASK ((1ULL << 10) - 1) /* @@ -623,6 +609,7 @@ enum { SCOUTFS_NET_CMD_GET_MANIFEST_ROOT, SCOUTFS_NET_CMD_STATFS, SCOUTFS_NET_CMD_COMPACT, + SCOUTFS_NET_CMD_LOCK, SCOUTFS_NET_CMD_UNKNOWN, }; @@ -742,6 +729,42 @@ struct scoutfs_net_compact_response { struct scoutfs_net_manifest_entry ents[SCOUTFS_COMPACTION_MAX_OUTPUT]; } __packed; +struct scoutfs_net_lock { + struct scoutfs_key key; + __u8 old_mode; + __u8 new_mode; +} __packed; + +/* some enums for tracing */ +enum { + SLT_CLIENT, + SLT_SERVER, + SLT_GRANT, + SLT_INVALIDATE, + SLT_REQUEST, + SLT_RESPONSE, +}; + +/* + * Read and write locks operate as you'd expect. Multiple readers can + * hold read locks while writers are excluded. A single writer can hold + * a write lock which excludes other readers and writers. Writers can + * read while holding a write lock. + * + * Multiple writers can hold write only locks but they can not read, + * they can only generate dirty items. It's used when the system has + * other means of knowing that it's safe to overwrite items. + * + * The null mode provides no access and is used to destroy locks. + */ +enum { + SCOUTFS_LOCK_NULL = 0, + SCOUTFS_LOCK_READ, + SCOUTFS_LOCK_WRITE, + SCOUTFS_LOCK_WRITE_ONLY, + SCOUTFS_LOCK_INVALID, +}; + /* * Scoutfs file handle structure - this can be copied out to userspace * via open by handle or put on the wire from NFS. From 64bdda717c0e065212cf9b1eb68f0e7ad8ecb645 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Tue, 20 Nov 2018 15:28:26 -0800 Subject: [PATCH 159/235] scoutfs-utils: move super id to block hdr magic Move the magic value that identifies the super block into the block header and use it for btree blocks as well. Signed-off-by: Zach Brown --- utils/src/format.h | 12 +++++++----- utils/src/mkfs.c | 6 +++--- utils/src/print.c | 13 ++++++------- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index a8ebb465..e2bfecfa 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -3,8 +3,10 @@ /* statfs(2) f_type */ #define SCOUTFS_SUPER_MAGIC 0x554f4353 /* "SCOU" */ -/* super block id */ -#define SCOUTFS_SUPER_ID 0x2e736674756f6373ULL /* "scoutfs." */ + +/* block header magic values, chosen at random */ +#define SCOUTFS_BLOCK_MAGIC_SUPER 0x103c428b +#define SCOUTFS_BLOCK_MAGIC_BTREE 0xe597f96d /* * The super block and btree blocks are fixed 4k. @@ -67,12 +69,12 @@ struct scoutfs_inet_addr { /* * This header is stored at the start of btree blocks and the super - * block for verification. The crc is calculated by zeroing the crc and - * padding so the buffer is large and aligned. + * block for verification. The crc field is not included in the + * calculation of the crc. */ struct scoutfs_block_header { __le32 crc; - __le32 _pad; + __le32 magic; __le64 fsid; __le64 seq; __le64 blkno; diff --git a/utils/src/mkfs.c b/utils/src/mkfs.c index de270550..d7da103f 100644 --- a/utils/src/mkfs.c +++ b/utils/src/mkfs.c @@ -229,8 +229,8 @@ static int write_new_fs(char *path, int fd, struct scoutfs_quorum_config *conf) /* partially initialize the super so we can use it to init others */ memset(super, 0, SCOUTFS_BLOCK_SIZE); pseudo_random_bytes(&super->hdr.fsid, sizeof(super->hdr.fsid)); + super->hdr.magic = cpu_to_le32(SCOUTFS_BLOCK_MAGIC_SUPER); super->hdr.seq = cpu_to_le64(1); - super->id = cpu_to_le64(SCOUTFS_SUPER_ID); super->format_hash = cpu_to_le64(SCOUTFS_FORMAT_HASH); uuid_generate(super->uuid); super->next_ino = cpu_to_le64(SCOUTFS_ROOT_INO + 1); @@ -293,7 +293,7 @@ static int write_new_fs(char *path, int fd, struct scoutfs_quorum_config *conf) ebk->major = cpu_to_be64(free_len); ebk->minor = cpu_to_be64(free_start + free_len - 1); - bt->hdr._pad = 0; + bt->hdr.magic = cpu_to_le32(SCOUTFS_BLOCK_MAGIC_BTREE); bt->hdr.crc = cpu_to_le32(crc_block(&bt->hdr)); ret = write_raw_block(fd, blkno, bt); @@ -338,7 +338,7 @@ static int write_new_fs(char *path, int fd, struct scoutfs_quorum_config *conf) ino_key->ski_ino = cpu_to_le64(SCOUTFS_ROOT_INO); ino_key->sk_type = SCOUTFS_INODE_TYPE; - bt->hdr._pad = 0; + bt->hdr.magic = cpu_to_le32(SCOUTFS_BLOCK_MAGIC_BTREE); bt->hdr.crc = cpu_to_le32(crc_block(&bt->hdr)); ret = write_raw_block(fd, blkno, bt); diff --git a/utils/src/print.c b/utils/src/print.c index e21f3f84..e697b40b 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -72,9 +72,10 @@ static void print_block_header(struct scoutfs_block_header *hdr) else valid_str[0] = '\0'; - printf(" hdr: crc %08x %sfsid %llx seq %llu blkno %llu\n", - le32_to_cpu(hdr->crc), valid_str, le64_to_cpu(hdr->fsid), - le64_to_cpu(hdr->seq), le64_to_cpu(hdr->blkno)); + printf(" hdr: crc %08x %smagic %08x fsid %llx seq %llu blkno %llu\n", + le32_to_cpu(hdr->crc), valid_str, le32_to_cpu(hdr->magic), + le64_to_cpu(hdr->fsid), le64_to_cpu(hdr->blkno), + le64_to_cpu(hdr->seq)); } static void print_inode(struct scoutfs_key *key, void *val, int val_len) @@ -512,10 +513,8 @@ static void print_super_block(struct scoutfs_super_block *super, u64 blkno) printf("super blkno %llu\n", blkno); print_block_header(&super->hdr); - printf(" id %llx format_hash %llx\n" - " uuid %s\n", - le64_to_cpu(super->id), le64_to_cpu(super->format_hash), - uuid_str); + printf(" format_hash %llx uuid %s\n", + le64_to_cpu(super->format_hash), uuid_str); /* XXX these are all in a crazy order */ printf(" next_ino %llu next_seq %llu next_seg_seq %llu\n" From 4c611474e83f55a8061f6fe8f44f0d421b76d980 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Mon, 4 Feb 2019 14:29:45 -0800 Subject: [PATCH 160/235] scoutfs-utils: update for reliable messaging Signed-off-by: Zach Brown --- utils/src/format.h | 37 ++++++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index e2bfecfa..d46b2195 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -571,23 +571,53 @@ enum { */ /* - * Greetings verify identity of communicating nodes. The sender - * sends their credentials and the receiver verifies them. + * Greetings verify identity of communicating nodes. The sender sends + * their credentials and the receiver verifies them. + * + * @server_term: The raft term that elected the server. Initially 0 + * from the client, sent by the server, then sent by the client as it + * tries to reconnect. Used to identify a client reconnecting to a + * server that has timed out its connection. + * + * @node_id: The id of the client. Initially 0 from the client, + * assigned by the server, and sent by the client as it reconnects. + * Used by the server to identify reconnecting clients whose existing + * state must be dealt with. */ struct scoutfs_net_greeting { __le64 fsid; __le64 format_hash; + __le64 server_term; __le64 node_id; + __le64 flags; } __packed; +#define SCOUTFS_NET_GREETING_FLAG_FAREWELL (1 << 0) +#define SCOUTFS_NET_GREETING_FLAG_INVALID (~(__u64)0 << 1) + /* * This header precedes and describes all network messages sent over - * sockets. The id is set by the request and sent in the response. + * sockets. + * + * @seq: A sequence number that is increased for each message queued for + * send on the sender. The sender will never reorder messages in the + * send queue so this will always increase in recv on the receiver. The + * receiver can use this to drop messages that arrived twice after being + * resent across a newly connected socket for a given connection. + * + * @recv_seq: The sequence number of the last received message. The + * receiver is sending this to the sender in every message. The sender + * uses them to drop responses which have been delivered. + * + * @id: An increasing identifier that is set in each request. Responses + * specify the request that they're responding to. * * Error is only set to a translated errno and will only be found in * response messages. */ struct scoutfs_net_header { + __le64 seq; + __le64 recv_seq; __le64 id; __le16 data_len; __u8 cmd; @@ -612,6 +642,7 @@ enum { SCOUTFS_NET_CMD_STATFS, SCOUTFS_NET_CMD_COMPACT, SCOUTFS_NET_CMD_LOCK, + SCOUTFS_NET_CMD_FAREWELL, SCOUTFS_NET_CMD_UNKNOWN, }; From 3d64c46fcde45e22683516536ae23569f9a8ad6a Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Fri, 8 Feb 2019 13:28:38 -0800 Subject: [PATCH 161/235] scoutfs-utils: add lock clients btree Show the lock client btree entries in print. Signed-off-by: Zach Brown --- utils/src/format.h | 19 +++++++++++++++++++ utils/src/print.c | 20 ++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/utils/src/format.h b/utils/src/format.h index d46b2195..be2931dc 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -272,6 +272,14 @@ struct scoutfs_extent_btree_key { __be64 minor; } __packed; +/* + * The lock server keeps a persistent record of connected clients so that + * server failover knows who to wait for before resuming operations. + */ +struct scoutfs_lock_client_btree_key { + __be64 node_id; +} __packed; + /* * The max number of links defines the max number of entries that we can * index in o(log n) and the static list head storage size in the @@ -456,6 +464,7 @@ struct scoutfs_super_block { struct scoutfs_btree_root alloc_root; struct scoutfs_manifest manifest; struct scoutfs_quorum_config quorum_config; + struct scoutfs_btree_root lock_clients; } __packed; #define SCOUTFS_ROOT_INO 1 @@ -642,6 +651,7 @@ enum { SCOUTFS_NET_CMD_STATFS, SCOUTFS_NET_CMD_COMPACT, SCOUTFS_NET_CMD_LOCK, + SCOUTFS_NET_CMD_LOCK_RECOVER, SCOUTFS_NET_CMD_FAREWELL, SCOUTFS_NET_CMD_UNKNOWN, }; @@ -768,6 +778,15 @@ struct scoutfs_net_lock { __u8 new_mode; } __packed; +struct scoutfs_net_lock_recover { + __le16 nr; + struct scoutfs_net_lock locks[0]; +} __packed; + +#define SCOUTFS_NET_LOCK_MAX_RECOVER_NR \ + ((SCOUTFS_NET_MAX_DATA_LEN - sizeof(struct scoutfs_net_lock_recover)) /\ + sizeof(struct scoutfs_net_lock)) + /* some enums for tracing */ enum { SLT_CLIENT, diff --git a/utils/src/print.c b/utils/src/print.c index e697b40b..896cf83b 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -343,6 +343,16 @@ static int print_alloc_item(void *key, unsigned key_len, void *val, return 0; } +static int print_lock_clients_entry(void *key, unsigned key_len, void *val, + unsigned val_len, void *arg) +{ + struct scoutfs_lock_client_btree_key *cbk = key; + + printf(" node_ld %llu\n", be64_to_cpu(cbk->node_id)); + + return 0; +} + typedef int (*print_item_func)(void *key, unsigned key_len, void *val, unsigned val_len, void *arg); @@ -522,6 +532,7 @@ static void print_super_block(struct scoutfs_super_block *super, u64 blkno) " 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" + " lock_clients root: height %u blkno %llu seq %llu mig_len %u\n" " alloc btree root: height %u blkno %llu seq %llu mig_len %u\n" " manifest btree root: height %u blkno %llu seq %llu mig_len %u\n", le64_to_cpu(super->next_ino), @@ -536,6 +547,10 @@ static void print_super_block(struct scoutfs_super_block *super, u64 blkno) le64_to_cpu(super->bring.nr_blocks), le64_to_cpu(super->bring.next_block), le64_to_cpu(super->bring.next_seq), + super->lock_clients.height, + le64_to_cpu(super->lock_clients.ref.blkno), + le64_to_cpu(super->lock_clients.ref.seq), + le16_to_cpu(super->lock_clients.migration_key_len), super->alloc_root.height, le64_to_cpu(super->alloc_root.ref.blkno), le64_to_cpu(super->alloc_root.ref.seq), @@ -595,6 +610,11 @@ static int print_volume(int fd) ret = print_quorum_blocks(fd, super); + err = print_btree(fd, super, "lock_clients", &super->lock_clients, + print_lock_clients_entry, NULL); + if (err && !ret) + ret = err; + err = print_btree(fd, super, "alloc", &super->alloc_root, print_alloc_item, NULL); if (err && !ret) From 587760edb3e784ade098a89a8a6ebcaf9092a0b1 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Thu, 11 Apr 2019 13:02:14 -0700 Subject: [PATCH 162/235] scoutfs-utils: add clock sync id to messages Signed-off-by: Zach Brown --- utils/src/format.h | 1 + 1 file changed, 1 insertion(+) diff --git a/utils/src/format.h b/utils/src/format.h index be2931dc..cf73e6be 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -625,6 +625,7 @@ struct scoutfs_net_greeting { * response messages. */ struct scoutfs_net_header { + __le64 clock_sync_id; __le64 seq; __le64 recv_seq; __le64 id; From 3c9eeeb2efe36cc3b6716a4389bfe392a78fb94b Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Wed, 27 Feb 2019 10:11:04 -0800 Subject: [PATCH 163/235] scoutfs-utils: add transaction seq btree Signed-off-by: Zach Brown --- utils/src/format.h | 12 +++++++++++- utils/src/mkfs.c | 2 +- utils/src/print.c | 25 +++++++++++++++++++++++-- 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index cf73e6be..6918bd82 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -280,6 +280,15 @@ struct scoutfs_lock_client_btree_key { __be64 node_id; } __packed; +/* + * The server tracks transaction sequence numbers that clients have + * open. This limits results that can be returned from the seq indices. + */ +struct scoutfs_trans_seq_btree_key { + __be64 trans_seq; + __be64 node_id; +} __packed; + /* * The max number of links defines the max number of entries that we can * index in o(log n) and the static list head storage size in the @@ -453,7 +462,7 @@ struct scoutfs_super_block { __le64 format_hash; __u8 uuid[SCOUTFS_UUID_BYTES]; __le64 next_ino; - __le64 next_seq; + __le64 next_trans_seq; __le64 total_blocks; __le64 free_blocks; __le64 alloc_cursor; @@ -465,6 +474,7 @@ struct scoutfs_super_block { struct scoutfs_manifest manifest; struct scoutfs_quorum_config quorum_config; struct scoutfs_btree_root lock_clients; + struct scoutfs_btree_root trans_seqs; } __packed; #define SCOUTFS_ROOT_INO 1 diff --git a/utils/src/mkfs.c b/utils/src/mkfs.c index d7da103f..a9c61500 100644 --- a/utils/src/mkfs.c +++ b/utils/src/mkfs.c @@ -234,7 +234,7 @@ static int write_new_fs(char *path, int fd, struct scoutfs_quorum_config *conf) super->format_hash = cpu_to_le64(SCOUTFS_FORMAT_HASH); uuid_generate(super->uuid); super->next_ino = cpu_to_le64(SCOUTFS_ROOT_INO + 1); - super->next_seq = cpu_to_le64(1); + super->next_trans_seq = cpu_to_le64(1); super->total_blocks = cpu_to_le64(total_blocks); super->next_seg_seq = cpu_to_le64(2); super->next_node_id = cpu_to_le64(1); diff --git a/utils/src/print.c b/utils/src/print.c index 896cf83b..14aeac14 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -353,6 +353,17 @@ static int print_lock_clients_entry(void *key, unsigned key_len, void *val, return 0; } +static int print_trans_seqs_entry(void *key, unsigned key_len, void *val, + unsigned val_len, void *arg) +{ + struct scoutfs_trans_seq_btree_key *tsk = key; + + printf(" trans_seq %llu node_ld %llu\n", + be64_to_cpu(tsk->trans_seq), be64_to_cpu(tsk->node_id)); + + return 0; +} + typedef int (*print_item_func)(void *key, unsigned key_len, void *val, unsigned val_len, void *arg); @@ -527,16 +538,17 @@ static void print_super_block(struct scoutfs_super_block *super, u64 blkno) le64_to_cpu(super->format_hash), uuid_str); /* XXX these are all in a crazy order */ - printf(" next_ino %llu next_seq %llu next_seg_seq %llu\n" + printf(" next_ino %llu next_trans_seq %llu next_seg_seq %llu\n" " next_node_id %llu next_compact_id %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" " lock_clients root: height %u blkno %llu seq %llu mig_len %u\n" + " trans_seqs root: height %u blkno %llu seq %llu mig_len %u\n" " alloc btree root: height %u blkno %llu seq %llu mig_len %u\n" " manifest btree root: height %u blkno %llu seq %llu mig_len %u\n", le64_to_cpu(super->next_ino), - le64_to_cpu(super->next_seq), + le64_to_cpu(super->next_trans_seq), le64_to_cpu(super->next_seg_seq), le64_to_cpu(super->next_node_id), le64_to_cpu(super->next_compact_id), @@ -551,6 +563,10 @@ static void print_super_block(struct scoutfs_super_block *super, u64 blkno) le64_to_cpu(super->lock_clients.ref.blkno), le64_to_cpu(super->lock_clients.ref.seq), le16_to_cpu(super->lock_clients.migration_key_len), + super->trans_seqs.height, + le64_to_cpu(super->trans_seqs.ref.blkno), + le64_to_cpu(super->trans_seqs.ref.seq), + le16_to_cpu(super->trans_seqs.migration_key_len), super->alloc_root.height, le64_to_cpu(super->alloc_root.ref.blkno), le64_to_cpu(super->alloc_root.ref.seq), @@ -615,6 +631,11 @@ static int print_volume(int fd) if (err && !ret) ret = err; + err = print_btree(fd, super, "trans_seqs", &super->trans_seqs, + print_trans_seqs_entry, NULL); + if (err && !ret) + ret = err; + err = print_btree(fd, super, "alloc", &super->alloc_root, print_alloc_item, NULL); if (err && !ret) From 841fbc1b6648fd735d3feff37e17ac17b48f5b7c Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Tue, 5 Mar 2019 10:14:14 -0800 Subject: [PATCH 164/235] scoutfs-utils: add counters command Add a command to output the sysfs counters for a volume, with the option of generating a table that fits the terminal. Signed-off-by: Zach Brown --- utils/src/counters.c | 284 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 284 insertions(+) create mode 100644 utils/src/counters.c diff --git a/utils/src/counters.c b/utils/src/counters.c new file mode 100644 index 00000000..dfee51cc --- /dev/null +++ b/utils/src/counters.c @@ -0,0 +1,284 @@ +#define _XOPEN_SOURCE 700 /* openat */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "util.h" +#include "cmd.h" + +struct counter { + char *name; + char *val; + unsigned int name_wid; + unsigned int val_wid; +}; + +static int dots(char *name) +{ + return name[0] == '.' && + (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')); +} + +static int cmp_counter_names(const void *A, const void *B) +{ + const struct counter *a = A; + const struct counter *b = B; + + return strcmp(a->name, b->name); +} + +static int counters_cmd(int argc, char **argv) +{ + unsigned int *name_wid = NULL; + unsigned int *val_wid = NULL; + struct counter *ctrs = NULL; + struct counter *ctr; + char path[PATH_MAX + 1]; + unsigned int alloced = 0; + unsigned int min_rows; + unsigned int max_rows; + unsigned int rows = 0; + unsigned int cols = 0; + unsigned int nr = 0; + char *dir_arg = NULL; + struct dirent *dent; + bool table = false; + struct winsize ws; + DIR *dirp = NULL; + int dir_fd = -1; + char buf[25]; + int room; + int ret; + int fd; + int i; + int r; + int c; + + for (i = 1; i < argc; i++) { + if (strcmp(argv[i], "-t") == 0) + table = true; + else + dir_arg = argv[i]; + } + + ret = ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws); + if (ret < 0) + ret = ioctl(STDIN_FILENO, TIOCGWINSZ, &ws); + if (ret < 0) + table = false; + + if (dir_arg == NULL) { + printf("scoutfs counter-table: need sysfs scoutfs dir path (i.e. /sys/fs/scoutfs/$DEV)\n"); + return -EINVAL; + } + + ret = snprintf(path, PATH_MAX, "%s/counters", dir_arg); + if (ret < 1 || ret >= PATH_MAX) { + ret = -EINVAL; + fprintf(stderr, "invalid counter dir path '%s'\n", dir_arg); + goto out; + } + + dirp = opendir(path); + if (!dirp) { + ret = -errno; + fprintf(stderr, "failed to open sysfs counter dir '%s': %s (%d)\n", + path, strerror(errno), errno); + goto out; + } + + dir_fd = dup(dirfd(dirp)); + if (dir_fd < 0) { + ret = -errno; + fprintf(stderr, "couldn't dup fd for path '%s': %s (%d)\n", + path, strerror(errno), errno); + goto out; + } + + /* read all the counters */ + while ((dent = readdir(dirp))) { + if (dots(dent->d_name)) + continue; + if (nr == alloced) { + alloced += 100; + ctrs = realloc(ctrs, alloced * sizeof(*ctrs)); + name_wid = realloc(name_wid, alloced * sizeof(*name_wid)); + val_wid = realloc(val_wid, alloced * sizeof(*val_wid)); + if (!ctrs || !name_wid || !val_wid) { + fprintf(stderr, "counter array allocation error\n"); + ret = -ENOMEM; + goto out; + } + memset(&ctrs[nr], 0, (alloced - nr) * sizeof(*ctrs)); + } + + ctr = &ctrs[nr]; + + ctr->name = strdup(dent->d_name); + if (ctr->name == NULL) { + fprintf(stderr, "name string allocation error\n"); + ret = -ENOMEM; + goto out; + } + + fd = openat(dir_fd, ctr->name, O_RDONLY); + if (fd < 0) { + ret = -errno; + fprintf(stderr, "failed to open counter file '%s/%s': %s (%d)\n", + path, ctr->name, strerror(errno), errno); + goto out; + } + + ret = pread(fd, buf, sizeof(buf), 0); + close(fd); + + if (ret <= 1 || ret >= sizeof(buf) || buf[ret - 1] != '\n') { + fprintf(stderr, "counter file %s/%s read returned %d\n", + path, ctr->name, ret); + ret = -EIO; + goto out; + } + + buf[ret - 1] = '\0'; + ctr->val = strdup(buf); + if (ctr->val == NULL) { + fprintf(stderr, "value string allocation error\n"); + ret = -ENOMEM; + goto out; + } + + ctr->name_wid = strlen(ctr->name); + ctr->val_wid = strlen(ctr->val); + + name_wid[0] = max(ctr->name_wid, name_wid[0]); + val_wid[0] = max(ctr->val_wid, val_wid[0]); + + nr++; + } + closedir(dirp); + dirp = NULL; + close(dir_fd); + dir_fd = -1; + + /* huh, empty counter dir */ + if (nr == 0) { + ret = 0; + goto out; + } + + /* sort counters by name */ + qsort(ctrs, nr, sizeof(ctrs[0]), cmp_counter_names); + + /* + * If we're packing the counters into a table that fills the + * width of the terminal then there will be a smallest number of + * rows in the table that packs counters into columns that fill + * the width of the terminal. We perform a binary search for + * that smallest number of rows that doesn't fill too many + * columns. + * + * Unless we're not outputting a table, then we just spit out + * one column of counters and use the max field widths from the + * initial counter reads. + */ + if (table) { + min_rows = 1; + cols = ws.ws_col / (name_wid[0] + 1 + val_wid[0] + 2); + max_rows = nr / cols; + } else { + rows = nr; + cols = 1; + min_rows = nr + 1; + max_rows = nr - 1; + } + + while (min_rows <= max_rows) { + rows = min_rows + ((max_rows - min_rows) / 2); + i = 0; + room = ws.ws_col; + + /* + * Iterate over counters, storing the max field widths + * of each column, recording the column chars left in + * the terminal, stopping if we fill too many columns + * for the terminal. + */ + for (c = 0; i < nr && room >= 0; c++) { + name_wid[c] = 0; + val_wid[c] = 0; + + for (r = 0; r < rows && i < nr; r++, i++) { + ctr = &ctrs[i]; + + name_wid[c] = max(ctr->name_wid, name_wid[c]); + val_wid[c] = max(ctr->val_wid, val_wid[c]); + } + + cols = c + 1; + if (c > 0) + room -= 2; + room -= name_wid[c] + 1 + val_wid[c]; + } + + if (room < 0) { + /* need more rows if we ran out of cols */ + min_rows = rows + 1; + } else { + /* see if we can get away with fewer */ + if (max_rows == rows) + break; + max_rows = rows; + } + } + + /* finally output the columns in each row */ + for (r = 0; r < rows; r++) { + for (c = 0; c < cols; c++) { + i = (c * rows) + r; + if (i >= nr) + break; + ctr = &ctrs[i]; + + printf("%s%-*s %*s", + c > 0 ? " " : "", + name_wid[c], ctr->name, + val_wid[c], ctr->val); + } + printf("\n"); + } + + ret = 0; +out: + if (dirp) + closedir(dirp); + if (dir_fd >= 0) + close(dir_fd); + if (ctrs) { + for (i = 0; i < alloced; i++) { + free(ctrs[i].name); + free(ctrs[i].val); + } + free(ctrs); + } + free(name_wid); + free(val_wid); + + return ret; +}; + +static void __attribute__((constructor)) counters_ctor(void) +{ + cmd_register("counters", "[-t] ", + "show [tablular] counters for a given mounted volume", + counters_cmd); +} From a9b46213b3a5390574e1e4ea968e8a06e3ff1a20 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Wed, 6 Mar 2019 11:50:48 -0800 Subject: [PATCH 165/235] scoutfs-utils: remove ctrstat command Remove the ctrstat command. It was built back when we had a handful of counters. It's output format doesn't make much sense now that we have an absolute ton of counters. If we want fancy counter output in the future we'd add it to the counters command. Signed-off-by: Zach Brown --- utils/src/ctrstat.c | 248 -------------------------------------------- 1 file changed, 248 deletions(-) delete mode 100644 utils/src/ctrstat.c diff --git a/utils/src/ctrstat.c b/utils/src/ctrstat.c deleted file mode 100644 index 1e30f090..00000000 --- a/utils/src/ctrstat.c +++ /dev/null @@ -1,248 +0,0 @@ -#define _XOPEN_SOURCE 700 /* 600: floorf, strtof, 700: openat */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "util.h" -#include "cmd.h" -#include "list.h" - -#define SCOUTFS_SYSFS_PATH "/sys/fs/scoutfs" - -struct string_item { - struct list_head head; - int dir_fd; - char *str; - int len; -}; - -static int add_string(char *str, struct list_head *list) -{ - struct string_item *sitem; - int ret = -ENOMEM; - - sitem = malloc(sizeof(struct string_item)); - if (sitem) { - sitem->str = strdup(str); - if (sitem->str) { - sitem->dir_fd = -1; - list_add_tail(&sitem->head, list); - sitem->len = strlen(str); - ret = 0; - } - } - - if (ret) - fprintf(stderr, "failed to alloc mem for string '%s'\n", str); - - return ret; -} - -/* - * Iterate over all the mounted ids and use their open dirfds to open - * and read each counter. We have to open each time we want updated counters. - * We reflect the counter length in the column's label length. - */ -static int read_and_print_counters(struct list_head *labels, - struct list_head *id_list, int print) -{ - struct string_item *label; - struct string_item *id; - char buf[25]; - ssize_t bytes; - int ret = 0; - int fd; - - list_for_each_entry(id, id_list, head) { - list_for_each_entry(label, labels, head) { - /* id column */ - if (label->str[0] == '\0') { - label->len = max(label->len, id->len); - if (print) - printf("%*s ", label->len, id->str); - continue; - } - - /* have to open each time we want current counter :/ */ - fd = openat(id->dir_fd, label->str, O_RDONLY); - if (fd < 0) { - ret = -errno; - goto out; - } - - bytes = pread(fd, buf, sizeof(buf), 0); - close(fd); - - if (bytes <= 1 || bytes >= sizeof(buf) || - buf[bytes - 1] != '\n') { - fprintf(stderr, "counter file %s/%s read returned %zd\n", - id->str, label->str, bytes); - ret = -EIO; - goto out; - } - - label->len = max(label->len, bytes - 1); - - if (print) { - buf[bytes - 1] = '\0'; - printf("%*s ", label->len, buf); - } - } - if (print) - printf("\n"); - } - -out: - return ret; -} - -static int dots(char *name) -{ - return name[0] == '.' && - (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')); -} - -/* - * XXX deal with unmount ;) - */ -static int ctrstat_cmd(int argc, char **argv) -{ - struct string_item *label; - struct string_item *id; - LIST_HEAD(label_list); - char path[PATH_MAX]; - LIST_HEAD(id_list); - struct dirent *dent; - float seconds = 1.0; - struct timespec ts; - DIR *dirp; - int iter; - int ret; - - if (argc > 2) { - printf("scoutfs ctrstat: too many arguments\n"); - return -EINVAL; - } - - /* set the sleep duration */ - if (argc == 2) { - seconds = strtof(argv[1], NULL); - if (fpclassify(seconds) != FP_NORMAL || seconds <= 0) { - printf("invalid sleep duration float: %s\n", argv[1]); - return -EINVAL; - } - } - ts.tv_sec = (int)floorf(seconds); - ts.tv_nsec = (seconds - floorf(seconds)) * 1000000000; - - /* find all the mounted ids */ - dirp = opendir(SCOUTFS_SYSFS_PATH); - if (!dirp) { - ret = -errno; - fprintf(stderr, "failed to open '%s': %s (%d)\n", - SCOUTFS_SYSFS_PATH, strerror(errno), errno); - goto out; - } - while ((dent = readdir(dirp))) { - if (dots(dent->d_name)) - continue; - ret = add_string(dent->d_name, &id_list); - if (ret) - goto out; - - } - closedir(dirp); - dirp = NULL; - - /* add a dummy label for the id column */ - ret = add_string("", &label_list); - if (ret) - goto out; - - iter = 1; - list_for_each_entry(id, &id_list, head) { - snprintf(path, PATH_MAX, SCOUTFS_SYSFS_PATH"/%s/counters", - id->str); - - dirp = opendir(path); - if (!dirp) { - ret = -errno; - fprintf(stderr, "failed to open '%s': %s (%d)\n", - path, strerror(errno), errno); - goto out; - } - - /* hold a dir fd open for each id */ - id->dir_fd = dup(dirfd(dirp)); - if (id->dir_fd < 0) { - ret = -errno; - fprintf(stderr, "couldn't dup fd for '%s': %s (%d)\n", - path, strerror(errno), errno); - goto out; - } - - /* find all the counters, assume all ids have same */ - while (iter && (dent = readdir(dirp))) { - if (dots(dent->d_name)) - continue; - - ret = add_string(dent->d_name, &label_list); - if (ret) - goto out; - } - closedir(dirp); - dirp = NULL; - iter = 0; - } - - /* initial read pass to find the max lengths */ - ret = read_and_print_counters(&label_list, &id_list, 0); - if (ret) - goto out; - - for (iter = 0; ; iter++) { - /* print row of column labels */ - if (!(iter % 25)) { - list_for_each_entry(label, &label_list, head) - printf("%*s ", label->len, label->str); - printf("\n"); - } - - /* print each id and its stats */ - ret = read_and_print_counters(&label_list, &id_list, 1); - if (ret) - goto out; - - nanosleep(&ts, NULL); - } - ret = 0; -out: - if (dirp) - closedir(dirp); - - /* squish together and free all */ - list_splice(&label_list, &id_list); - list_for_each_entry_safe(id, label, &id_list, head) { - list_del_init(&id->head); - if (id->dir_fd >= 0) - close(id->dir_fd); - free(id); - } - return ret; -}; - -static void __attribute__((constructor)) ctrstat_ctor(void) -{ - cmd_register("ctrstat", "", "print counters over time", - ctrstat_cmd); -} From 514418421cef26f182fbf035d255b8bf87dd9791 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Mon, 8 Apr 2019 13:05:10 -0700 Subject: [PATCH 166/235] scoutfs-utils: add support for unmount_barrier Signed-off-by: Zach Brown --- utils/src/format.h | 28 +++++++++++++++++++++++++++- utils/src/print.c | 27 ++++++++++++++++++++++++++- 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index 6918bd82..b5ced5d4 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -48,6 +48,8 @@ #define SCOUTFS_QUORUM_BLOCKS ((128ULL * 1024) >> SCOUTFS_BLOCK_SHIFT) #define SCOUTFS_QUORUM_MAX_SLOTS SCOUTFS_QUORUM_BLOCKS +#define SCOUTFS_UNIQUE_NAME_MAX_BYTES 64 /* includes null */ + /* * Base types used by other structures. */ @@ -289,6 +291,18 @@ struct scoutfs_trans_seq_btree_key { __be64 node_id; } __packed; +/* + * The server keeps a persistent record of mounted clients. + */ +struct scoutfs_mounted_client_btree_key { + __be64 node_id; +} __packed; + +struct scoutfs_mounted_client_btree_val { + __u8 name[SCOUTFS_UNIQUE_NAME_MAX_BYTES]; +} __packed; + + /* * The max number of links defines the max number of entries that we can * index in o(log n) and the static list head storage size in the @@ -395,7 +409,6 @@ struct scoutfs_xattr { #define member_sizeof(TYPE, MEMBER) (sizeof(((TYPE *)0)->MEMBER)) #define SCOUTFS_UUID_BYTES 16 -#define SCOUTFS_UNIQUE_NAME_MAX_BYTES 64 /* includes null */ /* * During each quorum voting interval the fabric has to process 2 reads @@ -418,6 +431,7 @@ struct scoutfs_xattr { * @config_gen: references the config gen in the super block * @write_nr: incremented for every write, only 0 when never written * @elected_nr: incremented when elected, 0 otherwise + * @unmount_barrier: incremented by servers when all members have unmounted * @vote_slot: the active config slot that the writer is voting for */ struct scoutfs_quorum_block { @@ -426,6 +440,7 @@ struct scoutfs_quorum_block { __le64 config_gen; __le64 write_nr; __le64 elected_nr; + __le64 unmount_barrier; __le32 crc; __u8 vote_slot; } __packed; @@ -475,6 +490,7 @@ struct scoutfs_super_block { struct scoutfs_quorum_config quorum_config; struct scoutfs_btree_root lock_clients; struct scoutfs_btree_root trans_seqs; + struct scoutfs_btree_root mounted_clients; } __packed; #define SCOUTFS_ROOT_INO 1 @@ -593,20 +609,30 @@ enum { * Greetings verify identity of communicating nodes. The sender sends * their credentials and the receiver verifies them. * + * @name: The client sends its unique name to the server. + * * @server_term: The raft term that elected the server. Initially 0 * from the client, sent by the server, then sent by the client as it * tries to reconnect. Used to identify a client reconnecting to a * server that has timed out its connection. * + * @unmount_barrier: Incremented every time the remaining majority of + * quorum members all agree to leave. The server tells a quorum member + * the value that it's connecting under so that if the client sees the + * value increase in a quorum block it knows that the server has + * processed its farewell and can safely unmount. + * * @node_id: The id of the client. Initially 0 from the client, * assigned by the server, and sent by the client as it reconnects. * Used by the server to identify reconnecting clients whose existing * state must be dealt with. */ struct scoutfs_net_greeting { + __u8 name[SCOUTFS_UNIQUE_NAME_MAX_BYTES]; __le64 fsid; __le64 format_hash; __le64 server_term; + __le64 unmount_barrier; __le64 node_id; __le64 flags; } __packed; diff --git a/utils/src/print.c b/utils/src/print.c index 14aeac14..7f55c648 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -364,6 +364,19 @@ static int print_trans_seqs_entry(void *key, unsigned key_len, void *val, return 0; } +/* XXX should make sure that the val is null terminated */ +static int print_mounted_client_entry(void *key, unsigned key_len, void *val, + unsigned val_len, void *arg) +{ + struct scoutfs_mounted_client_btree_key *mck = key; + struct scoutfs_mounted_client_btree_val *mcv = val; + + printf(" node_id %llu name %s\n", + be64_to_cpu(mck->node_id), mcv->name); + + return 0; +} + typedef int (*print_item_func)(void *key, unsigned key_len, void *val, unsigned val_len, void *arg); @@ -483,13 +496,15 @@ static int print_quorum_blocks(int fd, struct scoutfs_super_block *super) if (blk->fsid != 0 || blk->write_nr != 0) { printf("quorum block blkno %llu\n" " fsid %llx blkno %llu config_gen %llu crc 0x%08x\n" - " write_nr %llu elected_nr %llu vote_slot %u\n", + " write_nr %llu elected_nr %llu " + "unmount_barrier %llu vote_slot %u\n", blkno, le64_to_cpu(blk->fsid), le64_to_cpu(blk->blkno), le64_to_cpu(blk->config_gen), le32_to_cpu(blk->crc), le64_to_cpu(blk->write_nr), le64_to_cpu(blk->elected_nr), + le64_to_cpu(blk->unmount_barrier), blk->vote_slot); } @@ -544,6 +559,7 @@ static void print_super_block(struct scoutfs_super_block *super, u64 blkno) " btree ring: first_blkno %llu nr_blocks %llu next_block %llu " "next_seq %llu\n" " lock_clients root: height %u blkno %llu seq %llu mig_len %u\n" + " mounted_clients root: height %u blkno %llu seq %llu mig_len %u\n" " trans_seqs root: height %u blkno %llu seq %llu mig_len %u\n" " alloc btree root: height %u blkno %llu seq %llu mig_len %u\n" " manifest btree root: height %u blkno %llu seq %llu mig_len %u\n", @@ -563,6 +579,10 @@ static void print_super_block(struct scoutfs_super_block *super, u64 blkno) le64_to_cpu(super->lock_clients.ref.blkno), le64_to_cpu(super->lock_clients.ref.seq), le16_to_cpu(super->lock_clients.migration_key_len), + super->mounted_clients.height, + le64_to_cpu(super->mounted_clients.ref.blkno), + le64_to_cpu(super->mounted_clients.ref.seq), + le16_to_cpu(super->mounted_clients.migration_key_len), super->trans_seqs.height, le64_to_cpu(super->trans_seqs.ref.blkno), le64_to_cpu(super->trans_seqs.ref.seq), @@ -631,6 +651,11 @@ static int print_volume(int fd) if (err && !ret) ret = err; + err = print_btree(fd, super, "mounted_clients", &super->mounted_clients, + print_mounted_client_entry, NULL); + if (err && !ret) + ret = err; + err = print_btree(fd, super, "trans_seqs", &super->trans_seqs, print_trans_seqs_entry, NULL); if (err && !ret) From 77bd0c20abfa32add7fe62ff93fe4a8a6dca493b Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Wed, 10 Apr 2019 14:54:45 -0700 Subject: [PATCH 167/235] scoutfs-utils: add flags to quorum block Signed-off-by: Zach Brown --- utils/src/format.h | 4 ++++ utils/src/print.c | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index b5ced5d4..936b0717 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -443,8 +443,12 @@ struct scoutfs_quorum_block { __le64 unmount_barrier; __le32 crc; __u8 vote_slot; + __u8 flags; } __packed; +#define SCOUTFS_QUORUM_BLOCK_FLAG_ELECTED (1 << 0) +#define SCOUTFS_QUORUM_BLOCK_FLAGS_UNKNOWN (U8_MAX << 1) + #define SCOUTFS_QUORUM_MAX_SLOTS SCOUTFS_QUORUM_BLOCKS /* diff --git a/utils/src/print.c b/utils/src/print.c index 7f55c648..161c7f33 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -497,7 +497,7 @@ static int print_quorum_blocks(int fd, struct scoutfs_super_block *super) printf("quorum block blkno %llu\n" " fsid %llx blkno %llu config_gen %llu crc 0x%08x\n" " write_nr %llu elected_nr %llu " - "unmount_barrier %llu vote_slot %u\n", + "unmount_barrier %llu vote_slot %u flags %02x\n", blkno, le64_to_cpu(blk->fsid), le64_to_cpu(blk->blkno), le64_to_cpu(blk->config_gen), @@ -505,7 +505,7 @@ static int print_quorum_blocks(int fd, struct scoutfs_super_block *super) le64_to_cpu(blk->write_nr), le64_to_cpu(blk->elected_nr), le64_to_cpu(blk->unmount_barrier), - blk->vote_slot); + blk->vote_slot, blk->flags); } free(blk); From 57da5fae4cfbf72f88caf727cb114a2c2875ec6b Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Fri, 19 Apr 2019 10:49:05 -0700 Subject: [PATCH 168/235] scoutfs-utils: add waiting ioctl command Add a quick command that lists the results of the new waiting ioctl. Signed-off-by: Zach Brown --- utils/src/format.h | 5 +- utils/src/ioctl.h | 24 ++++++++++ utils/src/waiting.c | 112 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 139 insertions(+), 2 deletions(-) create mode 100644 utils/src/waiting.c diff --git a/utils/src/format.h b/utils/src/format.h index 936b0717..9fcbc082 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -390,8 +390,9 @@ struct scoutfs_file_extent { __u8 flags; } __packed; -#define SEF_OFFLINE 0x1 -#define SEF_UNWRITTEN 0x2 +#define SEF_OFFLINE (1 << 0) +#define SEF_UNWRITTEN (1 << 1) +#define SEF_UNKNOWN (U8_MAX << 2) /* * The first xattr part item has a header that describes the xattr. The diff --git a/utils/src/ioctl.h b/utils/src/ioctl.h index 915a130b..1b592522 100644 --- a/utils/src/ioctl.h +++ b/utils/src/ioctl.h @@ -229,4 +229,28 @@ enum { #define SCOUTFS_IOC_ITEM_CACHE_KEYS _IOW(SCOUTFS_IOCTL_MAGIC, 8, \ struct scoutfs_ioctl_item_cache_keys) +struct scoutfs_ioctl_data_waiting_entry { + __u64 ino; + __u64 iblock; + __u8 op; +} __packed; + +#define SCOUTFS_IOC_DWO_READ (1 << 0) +#define SCOUTFS_IOC_DWO_WRITE (1 << 1) +#define SCOUTFS_IOC_DWO_CHANGE_SIZE (1 << 2) +#define SCOUTFS_IOC_DWO_UNKNOWN (U8_MAX << 3) + +struct scoutfs_ioctl_data_waiting { + __u64 flags; + __u64 after_ino; + __u64 after_iblock; + __u64 ents_ptr; + __u16 ents_nr; +} __packed; + +#define SCOUTFS_IOC_DATA_WAITING_FLAGS_UNKNOWN (U8_MAX << 0) + +#define SCOUTFS_IOC_DATA_WAITING _IOW(SCOUTFS_IOCTL_MAGIC, 9, \ + struct scoutfs_ioctl_data_waiting) + #endif diff --git a/utils/src/waiting.c b/utils/src/waiting.c new file mode 100644 index 00000000..6e916de0 --- /dev/null +++ b/utils/src/waiting.c @@ -0,0 +1,112 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sparse.h" +#include "util.h" +#include "format.h" +#include "ioctl.h" +#include "cmd.h" + +static int parse_u64(char *str, u64 *val_ret) +{ + unsigned long long ull; + char *endptr = NULL; + + ull = strtoull(str, &endptr, 0); + if (*endptr != '\0' || + ((ull == LLONG_MIN || ull == LLONG_MAX) && + errno == ERANGE)) { + fprintf(stderr, "invalid 64bit value: '%s'\n", str); + *val_ret = 0; + return -EINVAL; + } + + *val_ret = ull; + + return 0; +} + +#define OP_FMT "%s%s" + +/* + * Print the caller's string for the bit if it's set, and if it's set + * and there are more significant bits coming then we also print a + * separating comma. + */ +#define op_str(ops, bit, str) \ + (((ops) & (bit)) ? (str) : ""), \ + (((ops) & (bit)) && ((ops) & ~(((bit) << 1) - 1)) ? "," : "") + +static int waiting_cmd(int argc, char **argv) +{ + struct scoutfs_ioctl_data_waiting_entry dwe[16]; + struct scoutfs_ioctl_data_waiting idw; + int ret; + int fd; + int i; + + if (argc != 4) { + fprintf(stderr, "must specify ino, iblock, and path\n"); + return -EINVAL; + } + + ret = parse_u64(argv[1], &idw.after_ino) ?: + parse_u64(argv[2], &idw.after_iblock); + if (ret) + return ret; + + fd = open(argv[3], O_RDONLY); + if (fd < 0) { + ret = -errno; + fprintf(stderr, "failed to open '%s': %s (%d)\n", + argv[4], strerror(errno), errno); + return ret; + } + + idw.flags = 0; + idw.ents_ptr = (unsigned long)dwe; + idw.ents_nr = array_size(dwe); + + for (;;) { + ret = ioctl(fd, SCOUTFS_IOC_DATA_WAITING, &idw); + if (ret < 0) { + ret = -errno; + fprintf(stderr, "waiting ioctl failed: %s (%d)\n", + strerror(errno), errno); + break; + } else if (ret == 0) { + break; + } + + for (i = 0; i < ret; i++) + printf("ino %llu iblock %llu ops " + OP_FMT OP_FMT OP_FMT"\n", + dwe[i].ino, dwe[i].iblock, + op_str(dwe[i].op, SCOUTFS_IOC_DWO_READ, + "read"), + op_str(dwe[i].op, SCOUTFS_IOC_DWO_WRITE, + "write"), + op_str(dwe[i].op, SCOUTFS_IOC_DWO_CHANGE_SIZE, + "change_size")); + + idw.after_ino = dwe[i - 1].ino; + idw.after_iblock = dwe[i - 1].iblock; + } + + close(fd); + return ret; +}; + +static void __attribute__((constructor)) waiting_ctor(void) +{ + cmd_register("data-waiting", " ", + "print ops waiting for data blocks", waiting_cmd); +} From ffe15c2d82d9527a53dc32ab34739b8f339bda35 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Fri, 24 May 2019 10:37:37 -0700 Subject: [PATCH 169/235] scoutfs-utils: add string parsing functions We're starting to collect a few of these. Let's put them in one place. Signed-off-by: Zach Brown --- utils/src/parse.c | 71 +++++++++++++++++++++++++++++++++++++++++++++++ utils/src/parse.h | 10 +++++++ 2 files changed, 81 insertions(+) create mode 100644 utils/src/parse.c create mode 100644 utils/src/parse.h diff --git a/utils/src/parse.c b/utils/src/parse.c new file mode 100644 index 00000000..6e01b9d7 --- /dev/null +++ b/utils/src/parse.c @@ -0,0 +1,71 @@ +#include +#include +#include +#include +#include + +#include "sparse.h" +#include "util.h" +#include "format.h" + +#include "parse.h" + +int parse_u64(char *str, u64 *val_ret) +{ + unsigned long long ull; + char *endptr = NULL; + + ull = strtoull(str, &endptr, 0); + if (*endptr != '\0' || + ((ull == LLONG_MIN || ull == LLONG_MAX) && + errno == ERANGE)) { + fprintf(stderr, "invalid 64bit value: '%s'\n", str); + *val_ret = 0; + return -EINVAL; + } + + *val_ret = ull; + + return 0; +} + +int parse_u32(char *str, u32 *val_ret) +{ + u64 val; + int ret; + + ret = parse_u64(str, &val); + if (ret) + return ret; + + if (val > UINT_MAX) + return -EINVAL; + + *val_ret = val; + return 0; +} + +int parse_timespec(char *str, struct scoutfs_timespec *ts) +{ + unsigned long long sec; + unsigned int nsec; + int ret; + + memset(ts, 0, sizeof(struct scoutfs_timespec)); + + ret = sscanf(str, "%llu.%u", &sec, &nsec); + if (ret != 2) { + fprintf(stderr, "invalid timespec string: '%s'\n", str); + return -EINVAL; + } + + if (nsec > 1000000000) { + fprintf(stderr, "invalid timespec nsec value: '%s'\n", str); + return -EINVAL; + } + + ts->sec = cpu_to_le64(sec); + ts->nsec = cpu_to_le32(nsec); + + return 0; +} diff --git a/utils/src/parse.h b/utils/src/parse.h new file mode 100644 index 00000000..e1361853 --- /dev/null +++ b/utils/src/parse.h @@ -0,0 +1,10 @@ +#ifndef _PARSE_H_ +#define _PARSE_H_ + +struct scoutfs_timespec; + +int parse_u64(char *str, u64 *val_ret); +int parse_u32(char *str, u32 *val_ret); +int parse_timespec(char *str, struct scoutfs_timespec *ts); + +#endif From 336a6a155d33c6946d2bb7ac6ca567570405279b Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Fri, 24 May 2019 10:38:13 -0700 Subject: [PATCH 170/235] scoutfs-utils: add setattr more command Add a command that wraps the setattr_more ioctl. Signed-off-by: Zach Brown --- utils/src/ioctl.h | 18 ++++++++ utils/src/setattr.c | 108 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 126 insertions(+) create mode 100644 utils/src/setattr.c diff --git a/utils/src/ioctl.h b/utils/src/ioctl.h index 1b592522..e49116b4 100644 --- a/utils/src/ioctl.h +++ b/utils/src/ioctl.h @@ -253,4 +253,22 @@ struct scoutfs_ioctl_data_waiting { #define SCOUTFS_IOC_DATA_WAITING _IOW(SCOUTFS_IOCTL_MAGIC, 9, \ struct scoutfs_ioctl_data_waiting) +/* + * If i_size is set then data_version must be non-zero. If the offline + * flag is set then i_size must be set and a offline extent will be + * created from offset 0 to i_size. + */ +struct scoutfs_ioctl_setattr_more { + __u64 data_version; + __u64 i_size; + __u64 flags; + struct scoutfs_timespec ctime; +} __packed; + +#define SCOUTFS_IOC_SETATTR_MORE_OFFLINE (1 << 0) +#define SCOUTFS_IOC_SETATTR_MORE_UNKNOWN (U8_MAX << 1) + +#define SCOUTFS_IOC_SETATTR_MORE _IOW(SCOUTFS_IOCTL_MAGIC, 10, \ + struct scoutfs_ioctl_setattr_more) + #endif diff --git a/utils/src/setattr.c b/utils/src/setattr.c new file mode 100644 index 00000000..5c29f1f5 --- /dev/null +++ b/utils/src/setattr.c @@ -0,0 +1,108 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sparse.h" +#include "util.h" +#include "format.h" +#include "ioctl.h" +#include "parse.h" +#include "cmd.h" + +static struct option long_ops[] = { + { "ctime", 1, NULL, 'c' }, + { "data_version", 1, NULL, 'd' }, + { "file", 1, NULL, 'f' }, + { "offline", 0, NULL, 'o' }, + { "i_size", 1, NULL, 's' }, + { NULL, 0, NULL, 0} +}; + +static int setattr_more_cmd(int argc, char **argv) +{ + struct scoutfs_ioctl_setattr_more sm; + char *path = NULL; + int ret; + int fd = -1; + int c; + + memset(&sm, 0, sizeof(sm)); + + while ((c = getopt_long(argc, argv, "c:d:f:os:", long_ops, NULL)) != -1) { + switch (c) { + case 'c': + ret = parse_timespec(optarg, &sm.ctime); + if (ret) + goto out; + break; + case 'd': + ret = parse_u64(optarg, &sm.data_version); + if (ret) + goto out; + break; + case 'f': + path = strdup(optarg); + if (!path) { + fprintf(stderr, "path mem alloc failed\n"); + ret = -ENOMEM; + goto out; + } + break; + case 'o': + sm.flags |= SCOUTFS_IOC_SETATTR_MORE_OFFLINE; + break; + case 's': + ret = parse_u64(optarg, &sm.i_size); + if (ret) + goto out; + break; + case '?': + default: + ret = -EINVAL; + goto out; + } + } + + if (path == NULL) { + fprintf(stderr, "must specify -f path to file\n"); + ret = -EINVAL; + goto out; + } + + fd = open(path, O_WRONLY); + if (fd < 0) { + ret = -errno; + fprintf(stderr, "failed to open '%s': %s (%d)\n", + path, strerror(errno), errno); + goto out; + } + + ret = ioctl(fd, SCOUTFS_IOC_SETATTR_MORE, &sm); + if (ret < 0) { + ret = -errno; + fprintf(stderr, "setattr_more ioctl failed on '%s': " + "%s (%d)\n", path, strerror(errno), errno); + goto out; + } + + ret = 0; +out: + if (fd >= 0) + close(fd); + return ret; +} + +static void __attribute__((constructor)) setattr_more_ctor(void) +{ + cmd_register("setattr", "-c ctime -d data_version -o -s i_size -f ", + "set attributes on file with no data", + setattr_more_cmd); +} From da185b214bf40ec56c5e0afee7c6b8489579aa2f Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Wed, 29 May 2019 10:40:05 -0700 Subject: [PATCH 171/235] scoutfs: return non-zero status on error The error return conventions were confused, resulting in main exiting with success when command execution failed. Signed-off-by: Zach Brown --- utils/src/cmd.c | 3 ++- utils/src/cmd.h | 2 +- utils/src/main.c | 8 +------- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/utils/src/cmd.c b/utils/src/cmd.c index 92e799e5..607f12ec 100644 --- a/utils/src/cmd.c +++ b/utils/src/cmd.c @@ -60,7 +60,8 @@ static void usage(void) } } -int cmd_execute(int argc, char **argv) +/* this returns a positive unix return code on error for some reason */ +char cmd_execute(int argc, char **argv) { struct command *com = NULL; int ret; diff --git a/utils/src/cmd.h b/utils/src/cmd.h index 53515b36..084590e9 100644 --- a/utils/src/cmd.h +++ b/utils/src/cmd.h @@ -4,6 +4,6 @@ void cmd_register(char *name, char *opts, char *summary, int (*func)(int argc, char **argv)); -int cmd_execute(int argc, char **argv); +char cmd_execute(int argc, char **argv); #endif diff --git a/utils/src/main.c b/utils/src/main.c index 599babb0..369b4ece 100644 --- a/utils/src/main.c +++ b/utils/src/main.c @@ -10,15 +10,9 @@ int main(int argc, char **argv) { - int ret; - /* * XXX parse global options, env, configs, etc. */ - ret = cmd_execute(argc, argv); - if (ret < 0) - return 1; - - return 0; + return cmd_execute(argc, argv); } From 8d505668fe6fc01bbb49e040d5ea38d645b1a8df Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Thu, 30 May 2019 14:29:58 -0700 Subject: [PATCH 172/235] scoutfs-utils: add quorum block listening flag Signed-off-by: Zach Brown --- utils/src/format.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/utils/src/format.h b/utils/src/format.h index 9fcbc082..79317691 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -448,7 +448,8 @@ struct scoutfs_quorum_block { } __packed; #define SCOUTFS_QUORUM_BLOCK_FLAG_ELECTED (1 << 0) -#define SCOUTFS_QUORUM_BLOCK_FLAGS_UNKNOWN (U8_MAX << 1) +#define SCOUTFS_QUORUM_BLOCK_FLAG_LISTENING (1 << 1) +#define SCOUTFS_QUORUM_BLOCK_FLAGS_UNKNOWN (U8_MAX << 2) #define SCOUTFS_QUORUM_MAX_SLOTS SCOUTFS_QUORUM_BLOCKS From 674224d454952ab2a5ecb957ed70ac2f70ce4030 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Tue, 18 Jun 2019 16:52:29 -0700 Subject: [PATCH 173/235] scoutfs-utils: hidden and indexed xattrs Add support for the xattr tags which can hide or index xattrs by their name. We get an item that indexes inodes by the presence of an xattr, a listxattr_raw ioctl which can show hidden xattrs, and an ioctl that finds inodes which have an xattr. Signed-off-by: Zach Brown --- utils/src/find_xattrs.c | 117 ++++++++++++++++++++++++++++++ utils/src/format.h | 15 +++- utils/src/ioctl.h | 40 +++++++++++ utils/src/key.c | 2 + utils/src/listxattr_raw.c | 147 ++++++++++++++++++++++++++++++++++++++ utils/src/print.c | 11 +++ 6 files changed, 329 insertions(+), 3 deletions(-) create mode 100644 utils/src/find_xattrs.c create mode 100644 utils/src/listxattr_raw.c diff --git a/utils/src/find_xattrs.c b/utils/src/find_xattrs.c new file mode 100644 index 00000000..ab9da445 --- /dev/null +++ b/utils/src/find_xattrs.c @@ -0,0 +1,117 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sparse.h" +#include "util.h" +#include "format.h" +#include "ioctl.h" +#include "cmd.h" + +static struct option long_ops[] = { + { "name", 1, NULL, 'n' }, + { "file", 1, NULL, 'f' }, + { NULL, 0, NULL, 0} +}; + +static int find_xattrs_cmd(int argc, char **argv) +{ + struct scoutfs_ioctl_find_xattrs fx; + char *path = NULL; + char *name = NULL; + u64 inos[32]; + int fd = -1; + int ret; + int c; + int i; + + memset(&fx, 0, sizeof(fx)); + + while ((c = getopt_long(argc, argv, "f:n:", long_ops, NULL)) != -1) { + switch (c) { + case 'f': + path = strdup(optarg); + if (!path) { + fprintf(stderr, "path mem alloc failed\n"); + ret = -ENOMEM; + goto out; + } + break; + case 'n': + name = strdup(optarg); + if (!name) { + fprintf(stderr, "name mem alloc failed\n"); + ret = -ENOMEM; + goto out; + } + break; + case '?': + default: + ret = -EINVAL; + goto out; + } + } + + if (path == NULL) { + fprintf(stderr, "must specify -f path to file\n"); + ret = -EINVAL; + goto out; + } + + fd = open(path, O_RDONLY); + if (fd < 0) { + ret = -errno; + fprintf(stderr, "failed to open '%s': %s (%d)\n", + path, strerror(errno), errno); + goto out; + } + + fx.next_ino = 0; + fx.name_ptr = (unsigned long)name; + fx.inodes_ptr = (unsigned long)inos; + fx.name_bytes = strlen(name); + fx.nr_inodes = array_size(inos); + + for (;;) { + + ret = ioctl(fd, SCOUTFS_IOC_FIND_XATTRS, &fx); + if (ret == 0) + break; + if (ret < 0) { + ret = -errno; + fprintf(stderr, "find_xattrs ioctl failed: " + "%s (%d)\n", strerror(errno), errno); + goto out; + } + + for (i = 0; i < ret; i++) + printf("%llu\n", inos[i]); + + fx.next_ino = inos[ret - 1] + 1; + if (fx.next_ino == 0) + break; + } + + ret = 0; +out: + if (fd >= 0) + close(fd); + free(path); + free(name); + + return ret; +}; + +static void __attribute__((constructor)) find_xattrs_ctor(void) +{ + cmd_register("find-xattrs", "-n name -f ", + "print inode numbers of inodes which may have given xattr", + find_xattrs_cmd); +} diff --git a/utils/src/format.h b/utils/src/format.h index 79317691..19990161 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -108,6 +108,11 @@ struct scoutfs_key { #define skii_major _sk_second #define skii_ino _sk_third +/* xattr index */ +#define skxi_hash _sk_first +#define skxi_ino _sk_second +#define skxi_id _sk_third + /* node free extent */ #define sknf_node_id _sk_first #define sknf_major _sk_second @@ -351,9 +356,10 @@ struct scoutfs_segment_block { * Keys are first sorted by major key zones. */ #define SCOUTFS_INODE_INDEX_ZONE 1 -#define SCOUTFS_NODE_ZONE 2 -#define SCOUTFS_FS_ZONE 3 -#define SCOUTFS_LOCK_ZONE 4 +#define SCOUTFS_XATTR_INDEX_ZONE 2 +#define SCOUTFS_NODE_ZONE 3 +#define SCOUTFS_FS_ZONE 4 +#define SCOUTFS_LOCK_ZONE 5 #define SCOUTFS_MAX_ZONE 8 /* power of 2 is efficient */ /* inode index zone */ @@ -361,6 +367,9 @@ struct scoutfs_segment_block { #define SCOUTFS_INODE_INDEX_DATA_SEQ_TYPE 2 #define SCOUTFS_INODE_INDEX_NR 3 /* don't forget to update */ +/* xattr index zone */ +#define SCOUTFS_XATTR_INDEX_NAME_TYPE 1 + /* node zone (also used in server alloc btree) */ #define SCOUTFS_FREE_EXTENT_BLKNO_TYPE 1 #define SCOUTFS_FREE_EXTENT_BLOCKS_TYPE 2 diff --git a/utils/src/ioctl.h b/utils/src/ioctl.h index e49116b4..b9966c68 100644 --- a/utils/src/ioctl.h +++ b/utils/src/ioctl.h @@ -271,4 +271,44 @@ struct scoutfs_ioctl_setattr_more { #define SCOUTFS_IOC_SETATTR_MORE _IOW(SCOUTFS_IOCTL_MAGIC, 10, \ struct scoutfs_ioctl_setattr_more) +struct scoutfs_ioctl_listxattr_raw { + __u64 id_pos; + __u64 buf_ptr; + __u32 buf_bytes; + __u32 hash_pos; +} __packed; + +#define SCOUTFS_IOC_LISTXATTR_RAW _IOW(SCOUTFS_IOCTL_MAGIC, 11, \ + struct scoutfs_ioctl_listxattr_raw) + +/* + * Return the inode numbers of inodes which might contain the given + * named xattr. The inode may not have a set xattr with that name, the + * caller must check the returned inodes to see if they match. + * + * @next_ino: The next inode number that could be returned. Initialized + * to 0 when first searching and set to one past the last inode number + * returned to continue searching. + * @name_ptr: The address of the name of the xattr to search for. It does + * not need to be null terminated. + * @inodes_ptr: The address of the array of uint64_t inode numbers in which + * to store inode numbers that may contain the xattr. EFAULT may be returned + * if this address is not naturally aligned. + * @name_bytes: The number of non-null bytes found in the name at name_ptr. + * @nr_inodes: The number of elements in the array found at inodes_ptr. + * + * This requires the CAP_SYS_ADMIN capability and will return -EPERM if + * it's not granted. + */ +struct scoutfs_ioctl_find_xattrs { + __u64 next_ino; + __u64 name_ptr; + __u64 inodes_ptr; + __u16 name_bytes; + __u16 nr_inodes; +} __packed; + +#define SCOUTFS_IOC_FIND_XATTRS _IOW(SCOUTFS_IOCTL_MAGIC, 12, \ + struct scoutfs_ioctl_find_xattrs) + #endif diff --git a/utils/src/key.c b/utils/src/key.c index 2afc855f..32be964c 100644 --- a/utils/src/key.c +++ b/utils/src/key.c @@ -21,6 +21,7 @@ char *scoutfs_zone_strings[SCOUTFS_MAX_ZONE] = { [SCOUTFS_INODE_INDEX_ZONE] = "ind", + [SCOUTFS_XATTR_INDEX_ZONE] = "xnd", [SCOUTFS_NODE_ZONE] = "nod", [SCOUTFS_FS_ZONE] = "fs", }; @@ -28,6 +29,7 @@ char *scoutfs_zone_strings[SCOUTFS_MAX_ZONE] = { char *scoutfs_type_strings[SCOUTFS_MAX_ZONE][SCOUTFS_MAX_TYPE] = { [SCOUTFS_INODE_INDEX_ZONE][SCOUTFS_INODE_INDEX_META_SEQ_TYPE] = "msq", [SCOUTFS_INODE_INDEX_ZONE][SCOUTFS_INODE_INDEX_DATA_SEQ_TYPE] = "dsq", + [SCOUTFS_XATTR_INDEX_ZONE][SCOUTFS_XATTR_INDEX_NAME_TYPE] = "nam", [SCOUTFS_NODE_ZONE][SCOUTFS_FREE_EXTENT_BLKNO_TYPE] = "fbn", [SCOUTFS_NODE_ZONE][SCOUTFS_FREE_EXTENT_BLOCKS_TYPE] = "fbs", [SCOUTFS_NODE_ZONE][SCOUTFS_ORPHAN_TYPE] = "orp", diff --git a/utils/src/listxattr_raw.c b/utils/src/listxattr_raw.c new file mode 100644 index 00000000..f7b69c0d --- /dev/null +++ b/utils/src/listxattr_raw.c @@ -0,0 +1,147 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sparse.h" +#include "util.h" +#include "format.h" +#include "ioctl.h" +#include "cmd.h" + +static struct option long_ops[] = { + { "file", 1, NULL, 'f' }, + { NULL, 0, NULL, 0} +}; + +static int listxattr_raw_cmd(int argc, char **argv) +{ + struct scoutfs_ioctl_listxattr_raw lxr; + char *path = NULL; + char *buf = NULL; + char *name; + int fd = -1; + int bytes; + int len; + int ret; + int c; + int i; + + while ((c = getopt_long(argc, argv, "f:", long_ops, NULL)) != -1) { + switch (c) { + case 'f': + path = strdup(optarg); + if (!path) { + fprintf(stderr, "path mem alloc failed\n"); + ret = -ENOMEM; + goto out; + } + break; + case '?': + default: + ret = -EINVAL; + goto out; + } + } + + if (path == NULL) { + fprintf(stderr, "must specify -f path to file\n"); + ret = -EINVAL; + goto out; + } + + memset(&lxr, 0, sizeof(lxr)); + lxr.id_pos = 0; + lxr.hash_pos = 0; + lxr.buf_bytes = 256 * 1024; + + buf = malloc(lxr.buf_bytes); + if (!buf) { + fprintf(stderr, "xattr name buf alloc failed\n"); + return -ENOMEM; + } + lxr.buf_ptr = (unsigned long)buf; + + fd = open(path, O_RDONLY); + if (fd < 0) { + ret = -errno; + fprintf(stderr, "failed to open '%s': %s (%d)\n", + path, strerror(errno), errno); + goto out; + } + + for (;;) { + + ret = ioctl(fd, SCOUTFS_IOC_LISTXATTR_RAW, &lxr); + if (ret == 0) + break; + if (ret < 0) { + ret = -errno; + fprintf(stderr, "listxattr_raw ioctl failed: " + "%s (%d)\n", strerror(errno), errno); + goto out; + } + + bytes = ret; + + if (bytes > lxr.buf_bytes) { + fprintf(stderr, "listxattr_raw overflowed\n"); + ret = -EFAULT; + goto out; + } + if (buf[bytes - 1] != '\0') { + fprintf(stderr, "listxattr_raw didn't term\n"); + ret = -EINVAL; + goto out; + } + + name = buf; + + do { + len = strlen(name); + if (len == 0) { + fprintf(stderr, "listxattr_raw empty name\n"); + ret = -EINVAL; + goto out; + } + + if (len > SCOUTFS_XATTR_MAX_NAME_LEN) { + fprintf(stderr, "listxattr_raw long name\n"); + ret = -EINVAL; + goto out; + } + + for (i = 0; i < len; i++) { + if (!isprint(name[i])) + name[i] = '?'; + } + + printf("%s\n", name); + name += len + 1; + bytes -= len + 1; + + } while (bytes > 0); + } + + ret = 0; +out: + if (fd >= 0) + close(fd); + free(buf); + + return ret; +}; + +static void __attribute__((constructor)) listxattr_raw_ctor(void) +{ + cmd_register("listxattr-raw", "-f ", + "print only the names of all xattrs on the file", + listxattr_raw_cmd); +} diff --git a/utils/src/print.c b/utils/src/print.c index 161c7f33..dd9f7308 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -197,6 +197,13 @@ static void print_inode_index(struct scoutfs_key *key, void *val, int val_len) le64_to_cpu(key->skii_major), le64_to_cpu(key->skii_ino)); } +static void print_xattr_index(struct scoutfs_key *key, void *val, int val_len) +{ + printf(" xattr index: hash 0x%016llx ino %llu id %llu\n", + le64_to_cpu(key->skxi_hash), le64_to_cpu(key->skxi_ino), + le64_to_cpu(key->skxi_id)); +} + typedef void (*print_func_t)(struct scoutfs_key *key, void *val, int val_len); static print_func_t find_printer(u8 zone, u8 type) @@ -206,6 +213,10 @@ static print_func_t find_printer(u8 zone, u8 type) type <= SCOUTFS_INODE_INDEX_DATA_SEQ_TYPE) return print_inode_index; + if (zone == SCOUTFS_XATTR_INDEX_ZONE && + type >= SCOUTFS_XATTR_INDEX_NAME_TYPE) + return print_xattr_index; + if (zone == SCOUTFS_NODE_ZONE) { if (type == SCOUTFS_FREE_EXTENT_BLKNO_TYPE || type == SCOUTFS_FREE_EXTENT_BLOCKS_TYPE) From 8597fd0bfc02105fe73d67f393c031a3aa358c12 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Tue, 25 Jun 2019 12:42:59 -0700 Subject: [PATCH 174/235] scoutfs-utils: naturally align ioctl structs Use natuturally aligned and explicitly padded ioctl structs. Signed-off-by: Zach Brown --- utils/src/ioctl.h | 70 ++++++++++++++++++++++++++++--------- utils/src/item-cache-keys.c | 14 ++++---- utils/src/key.h | 18 ++++++++++ utils/src/parse.c | 8 ++--- utils/src/parse.h | 4 +-- utils/src/setattr.c | 6 +++- 6 files changed, 91 insertions(+), 29 deletions(-) diff --git a/utils/src/ioctl.h b/utils/src/ioctl.h index b9966c68..0397b634 100644 --- a/utils/src/ioctl.h +++ b/utils/src/ioctl.h @@ -1,14 +1,41 @@ #ifndef _SCOUTFS_IOCTL_H_ #define _SCOUTFS_IOCTL_H_ +/* + * We naturally align explicit width fields in the ioctl structs so that + * userspace doesn't need to deal with padding or unaligned packing and + * we don't have to deal with 32/64 compat. It makes it a little + * awkward to communicate persistent packed structs through the ioctls + * but that happens very rarely. An interesting special case are + * 0length arrays that follow the structs. We make those start at the + * next aligned offset of the struct to be safe. + * + * This is enforced by pahole scripting in external build environments. + */ + /* XXX I have no idea how these are chosen. */ #define SCOUTFS_IOCTL_MAGIC 's' +/* + * Packed scoutfs keys rarely cross the ioctl boundary so we have a + * translation struct. + */ +struct scoutfs_ioctl_key { + __le64 _sk_first; + __le64 _sk_second; + __le64 _sk_third; + __u8 _sk_fourth; + __u8 sk_type; + __u8 sk_zone; + __u8 _pad[5]; +}; + struct scoutfs_ioctl_walk_inodes_entry { __u64 major; - __u32 minor; __u64 ino; -} __packed; + __u32 minor; + __u8 _pad[4]; +}; /* * Walk inodes in an index that is sorted by one of their fields. @@ -48,7 +75,8 @@ struct scoutfs_ioctl_walk_inodes { __u64 entries_ptr; __u32 nr_entries; __u8 index; -} __packed; + __u8 _pad[11]; /* padded to align walk_inodes_entry total size */ +}; enum { SCOUTFS_IOC_WALK_INODES_META_SEQ = 0, @@ -127,14 +155,16 @@ struct scoutfs_ioctl_ino_path { __u64 dir_pos; __u64 result_ptr; __u16 result_bytes; -} __packed; + __u8 _pad[6]; +}; struct scoutfs_ioctl_ino_path_result { __u64 dir_ino; __u64 dir_pos; __u16 path_bytes; + __u8 _pad[6]; __u8 path[0]; -} __packed; +}; /* Get a single path from the root to the given inode number */ #define SCOUTFS_IOC_INO_PATH _IOW(SCOUTFS_IOCTL_MAGIC, 2, \ @@ -168,7 +198,7 @@ struct scoutfs_ioctl_release { __u64 block; __u64 count; __u64 data_version; -} __packed; +}; #define SCOUTFS_IOC_RELEASE _IOW(SCOUTFS_IOCTL_MAGIC, 5, \ struct scoutfs_ioctl_release) @@ -178,7 +208,8 @@ struct scoutfs_ioctl_stage { __u64 buf_ptr; __u64 offset; __s32 count; -} __packed; + __u32 _pad; +}; #define SCOUTFS_IOC_STAGE _IOW(SCOUTFS_IOCTL_MAGIC, 6, \ struct scoutfs_ioctl_stage) @@ -203,11 +234,12 @@ struct scoutfs_ioctl_stat_more { __u64 data_version; __u64 online_blocks; __u64 offline_blocks; -} __packed; +}; #define SCOUTFS_IOC_STAT_MORE _IOW(SCOUTFS_IOCTL_MAGIC, 7, \ struct scoutfs_ioctl_stat_more) + /* * Fills the buffer with either the keys for the cached items or the * keys for the cached ranges found starting with the given key. The @@ -215,11 +247,12 @@ struct scoutfs_ioctl_stat_more { * keys the returned number will always be a multiple of two. */ struct scoutfs_ioctl_item_cache_keys { - struct scoutfs_key key; + struct scoutfs_ioctl_key ikey; __u64 buf_ptr; __u16 buf_nr; __u8 which; -} __packed; + __u8 _pad[21]; /* padded to align _ioctl_key total size */ +}; enum { SCOUTFS_IOC_ITEM_CACHE_KEYS_ITEMS = 0, @@ -233,7 +266,8 @@ struct scoutfs_ioctl_data_waiting_entry { __u64 ino; __u64 iblock; __u8 op; -} __packed; + __u8 _pad[7]; +}; #define SCOUTFS_IOC_DWO_READ (1 << 0) #define SCOUTFS_IOC_DWO_WRITE (1 << 1) @@ -246,7 +280,8 @@ struct scoutfs_ioctl_data_waiting { __u64 after_iblock; __u64 ents_ptr; __u16 ents_nr; -} __packed; + __u8 _pad[6]; +}; #define SCOUTFS_IOC_DATA_WAITING_FLAGS_UNKNOWN (U8_MAX << 0) @@ -262,8 +297,10 @@ struct scoutfs_ioctl_setattr_more { __u64 data_version; __u64 i_size; __u64 flags; - struct scoutfs_timespec ctime; -} __packed; + __u64 ctime_sec; + __u32 ctime_nsec; + __u8 _pad[4]; +}; #define SCOUTFS_IOC_SETATTR_MORE_OFFLINE (1 << 0) #define SCOUTFS_IOC_SETATTR_MORE_UNKNOWN (U8_MAX << 1) @@ -276,7 +313,7 @@ struct scoutfs_ioctl_listxattr_raw { __u64 buf_ptr; __u32 buf_bytes; __u32 hash_pos; -} __packed; +}; #define SCOUTFS_IOC_LISTXATTR_RAW _IOW(SCOUTFS_IOCTL_MAGIC, 11, \ struct scoutfs_ioctl_listxattr_raw) @@ -306,7 +343,8 @@ struct scoutfs_ioctl_find_xattrs { __u64 inodes_ptr; __u16 name_bytes; __u16 nr_inodes; -} __packed; + __u8 _pad[4]; +}; #define SCOUTFS_IOC_FIND_XATTRS _IOW(SCOUTFS_IOCTL_MAGIC, 12, \ struct scoutfs_ioctl_find_xattrs) diff --git a/utils/src/item-cache-keys.c b/utils/src/item-cache-keys.c index 68ca48c5..a7577cb3 100644 --- a/utils/src/item-cache-keys.c +++ b/utils/src/item-cache-keys.c @@ -19,7 +19,8 @@ static int item_cache_keys(int argc, char **argv, int which) { struct scoutfs_ioctl_item_cache_keys ick; - struct scoutfs_key keys[32]; + struct scoutfs_ioctl_key ikeys[32]; + struct scoutfs_key key; int ret; int fd; int i; @@ -38,8 +39,8 @@ static int item_cache_keys(int argc, char **argv, int which) } memset(&ick, 0, sizeof(ick)); - ick.buf_ptr = (unsigned long)keys; - ick.buf_nr = array_size(keys); + ick.buf_ptr = (unsigned long)ikeys; + ick.buf_nr = array_size(ikeys); ick.which = which; for (;;) { @@ -54,7 +55,8 @@ static int item_cache_keys(int argc, char **argv, int which) } for (i = 0; i < ret; i++) { - printf(SK_FMT, SK_ARG(&keys[i])); + scoutfs_key_copy_types(&key, &ikeys[i]); + printf(SK_FMT, SK_ARG(&key)); if (which == SCOUTFS_IOC_ITEM_CACHE_KEYS_ITEMS || (i & 1)) @@ -63,8 +65,8 @@ static int item_cache_keys(int argc, char **argv, int which) printf(" - "); } - ick.key = keys[i - 1]; - scoutfs_key_inc(&ick.key); + scoutfs_key_inc(&key); + scoutfs_key_copy_types(&ick.ikey, &key); } close(fd); diff --git a/utils/src/key.h b/utils/src/key.h index 9401286f..5a1b580e 100644 --- a/utils/src/key.h +++ b/utils/src/key.h @@ -38,6 +38,24 @@ static inline char *sk_type_str(u8 zone, u8 type) le64_to_cpu((key)->_sk_third), \ (key)->_sk_fourth +/* + * copy fields between keys with the same fields but different types. + * The destination type might have internal padding so we zero it. + */ +#define scoutfs_key_copy_types(a, b) \ +do { \ + __typeof__(a) _to = (a); \ + __typeof__(b) _from = (b); \ + \ + memset(_to, 0, sizeof(*_to)); \ + _to->sk_zone = _from->sk_zone; \ + _to->_sk_first = _from->_sk_first; \ + _to->sk_type = _from->sk_type; \ + _to->_sk_second = _from->_sk_second; \ + _to->_sk_third = _from->_sk_third; \ + _to->_sk_fourth = _from->_sk_fourth; \ +} while (0) + static inline void scoutfs_key_set_zeros(struct scoutfs_key *key) { key->sk_zone = 0; diff --git a/utils/src/parse.c b/utils/src/parse.c index 6e01b9d7..302dc762 100644 --- a/utils/src/parse.c +++ b/utils/src/parse.c @@ -45,13 +45,13 @@ int parse_u32(char *str, u32 *val_ret) return 0; } -int parse_timespec(char *str, struct scoutfs_timespec *ts) +int parse_timespec(char *str, struct timespec *ts) { unsigned long long sec; unsigned int nsec; int ret; - memset(ts, 0, sizeof(struct scoutfs_timespec)); + memset(ts, 0, sizeof(struct timespec)); ret = sscanf(str, "%llu.%u", &sec, &nsec); if (ret != 2) { @@ -64,8 +64,8 @@ int parse_timespec(char *str, struct scoutfs_timespec *ts) return -EINVAL; } - ts->sec = cpu_to_le64(sec); - ts->nsec = cpu_to_le32(nsec); + ts->tv_sec = sec; + ts->tv_nsec = nsec; return 0; } diff --git a/utils/src/parse.h b/utils/src/parse.h index e1361853..ad25f879 100644 --- a/utils/src/parse.h +++ b/utils/src/parse.h @@ -1,10 +1,10 @@ #ifndef _PARSE_H_ #define _PARSE_H_ -struct scoutfs_timespec; +#include int parse_u64(char *str, u64 *val_ret); int parse_u32(char *str, u32 *val_ret); -int parse_timespec(char *str, struct scoutfs_timespec *ts); +int parse_timespec(char *str, struct timespec *ts); #endif diff --git a/utils/src/setattr.c b/utils/src/setattr.c index 5c29f1f5..e9ab3b34 100644 --- a/utils/src/setattr.c +++ b/utils/src/setattr.c @@ -29,6 +29,7 @@ static struct option long_ops[] = { static int setattr_more_cmd(int argc, char **argv) { struct scoutfs_ioctl_setattr_more sm; + struct timespec ctime; char *path = NULL; int ret; int fd = -1; @@ -39,7 +40,7 @@ static int setattr_more_cmd(int argc, char **argv) while ((c = getopt_long(argc, argv, "c:d:f:os:", long_ops, NULL)) != -1) { switch (c) { case 'c': - ret = parse_timespec(optarg, &sm.ctime); + ret = parse_timespec(optarg, &ctime); if (ret) goto out; break; @@ -85,6 +86,9 @@ static int setattr_more_cmd(int argc, char **argv) goto out; } + sm.ctime_sec = ctime.tv_sec; + sm.ctime_nsec = ctime.tv_nsec; + ret = ioctl(fd, SCOUTFS_IOC_SETATTR_MORE, &sm); if (ret < 0) { ret = -errno; From 9a087be46cd1a9c75e428b4281d41826231a7fdb Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Fri, 28 Jun 2019 09:57:16 -0700 Subject: [PATCH 175/235] scoutfs-utils: update ioctl _IO usage Signed-off-by: Zach Brown --- utils/src/ioctl.h | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/utils/src/ioctl.h b/utils/src/ioctl.h index 0397b634..4a62efe9 100644 --- a/utils/src/ioctl.h +++ b/utils/src/ioctl.h @@ -88,7 +88,7 @@ enum { * Adds entries to the user's buffer for each inode that is found in the * given index between the first and last positions. */ -#define SCOUTFS_IOC_WALK_INODES _IOW(SCOUTFS_IOCTL_MAGIC, 1, \ +#define SCOUTFS_IOC_WALK_INODES _IOR(SCOUTFS_IOCTL_MAGIC, 1, \ struct scoutfs_ioctl_walk_inodes) /* @@ -167,10 +167,8 @@ struct scoutfs_ioctl_ino_path_result { }; /* Get a single path from the root to the given inode number */ -#define SCOUTFS_IOC_INO_PATH _IOW(SCOUTFS_IOCTL_MAGIC, 2, \ - struct scoutfs_ioctl_ino_path) - -#define SCOUTFS_IOC_DATA_VERSION _IOW(SCOUTFS_IOCTL_MAGIC, 4, __u64) +#define SCOUTFS_IOC_INO_PATH _IOR(SCOUTFS_IOCTL_MAGIC, 2, \ + struct scoutfs_ioctl_ino_path) /* * "Release" a contiguous range of logical blocks of file data. @@ -200,8 +198,8 @@ struct scoutfs_ioctl_release { __u64 data_version; }; -#define SCOUTFS_IOC_RELEASE _IOW(SCOUTFS_IOCTL_MAGIC, 5, \ - struct scoutfs_ioctl_release) +#define SCOUTFS_IOC_RELEASE _IOW(SCOUTFS_IOCTL_MAGIC, 3, \ + struct scoutfs_ioctl_release) struct scoutfs_ioctl_stage { __u64 data_version; @@ -211,7 +209,7 @@ struct scoutfs_ioctl_stage { __u32 _pad; }; -#define SCOUTFS_IOC_STAGE _IOW(SCOUTFS_IOCTL_MAGIC, 6, \ +#define SCOUTFS_IOC_STAGE _IOW(SCOUTFS_IOCTL_MAGIC, 4, \ struct scoutfs_ioctl_stage) /* @@ -236,7 +234,7 @@ struct scoutfs_ioctl_stat_more { __u64 offline_blocks; }; -#define SCOUTFS_IOC_STAT_MORE _IOW(SCOUTFS_IOCTL_MAGIC, 7, \ +#define SCOUTFS_IOC_STAT_MORE _IOR(SCOUTFS_IOCTL_MAGIC, 5, \ struct scoutfs_ioctl_stat_more) @@ -259,7 +257,7 @@ enum { SCOUTFS_IOC_ITEM_CACHE_KEYS_RANGES, }; -#define SCOUTFS_IOC_ITEM_CACHE_KEYS _IOW(SCOUTFS_IOCTL_MAGIC, 8, \ +#define SCOUTFS_IOC_ITEM_CACHE_KEYS _IOR(SCOUTFS_IOCTL_MAGIC, 6, \ struct scoutfs_ioctl_item_cache_keys) struct scoutfs_ioctl_data_waiting_entry { @@ -285,7 +283,7 @@ struct scoutfs_ioctl_data_waiting { #define SCOUTFS_IOC_DATA_WAITING_FLAGS_UNKNOWN (U8_MAX << 0) -#define SCOUTFS_IOC_DATA_WAITING _IOW(SCOUTFS_IOCTL_MAGIC, 9, \ +#define SCOUTFS_IOC_DATA_WAITING _IOR(SCOUTFS_IOCTL_MAGIC, 7, \ struct scoutfs_ioctl_data_waiting) /* @@ -305,7 +303,7 @@ struct scoutfs_ioctl_setattr_more { #define SCOUTFS_IOC_SETATTR_MORE_OFFLINE (1 << 0) #define SCOUTFS_IOC_SETATTR_MORE_UNKNOWN (U8_MAX << 1) -#define SCOUTFS_IOC_SETATTR_MORE _IOW(SCOUTFS_IOCTL_MAGIC, 10, \ +#define SCOUTFS_IOC_SETATTR_MORE _IOW(SCOUTFS_IOCTL_MAGIC, 8, \ struct scoutfs_ioctl_setattr_more) struct scoutfs_ioctl_listxattr_raw { @@ -315,7 +313,7 @@ struct scoutfs_ioctl_listxattr_raw { __u32 hash_pos; }; -#define SCOUTFS_IOC_LISTXATTR_RAW _IOW(SCOUTFS_IOCTL_MAGIC, 11, \ +#define SCOUTFS_IOC_LISTXATTR_RAW _IOR(SCOUTFS_IOCTL_MAGIC, 9, \ struct scoutfs_ioctl_listxattr_raw) /* @@ -346,7 +344,7 @@ struct scoutfs_ioctl_find_xattrs { __u8 _pad[4]; }; -#define SCOUTFS_IOC_FIND_XATTRS _IOW(SCOUTFS_IOCTL_MAGIC, 12, \ - struct scoutfs_ioctl_find_xattrs) +#define SCOUTFS_IOC_FIND_XATTRS _IOR(SCOUTFS_IOCTL_MAGIC, 10, \ + struct scoutfs_ioctl_find_xattrs) #endif From adadd51815540df758314c84645dad4fbfa9586e Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Fri, 28 Jun 2019 10:12:28 -0700 Subject: [PATCH 176/235] scoutfs-utils: update for listxattr_hidden listxattr_raw was renamed to listxattr_hidden to more accurately describe the only reason that it exists. Signed-off-by: Zach Brown --- utils/src/ioctl.h | 6 +-- .../{listxattr_raw.c => listxattr_hidden.c} | 38 +++++++++---------- 2 files changed, 22 insertions(+), 22 deletions(-) rename utils/src/{listxattr_raw.c => listxattr_hidden.c} (69%) diff --git a/utils/src/ioctl.h b/utils/src/ioctl.h index 4a62efe9..5aa057c0 100644 --- a/utils/src/ioctl.h +++ b/utils/src/ioctl.h @@ -306,15 +306,15 @@ struct scoutfs_ioctl_setattr_more { #define SCOUTFS_IOC_SETATTR_MORE _IOW(SCOUTFS_IOCTL_MAGIC, 8, \ struct scoutfs_ioctl_setattr_more) -struct scoutfs_ioctl_listxattr_raw { +struct scoutfs_ioctl_listxattr_hidden { __u64 id_pos; __u64 buf_ptr; __u32 buf_bytes; __u32 hash_pos; }; -#define SCOUTFS_IOC_LISTXATTR_RAW _IOR(SCOUTFS_IOCTL_MAGIC, 9, \ - struct scoutfs_ioctl_listxattr_raw) +#define SCOUTFS_IOC_LISTXATTR_HIDDEN _IOR(SCOUTFS_IOCTL_MAGIC, 9, \ + struct scoutfs_ioctl_listxattr_hidden) /* * Return the inode numbers of inodes which might contain the given diff --git a/utils/src/listxattr_raw.c b/utils/src/listxattr_hidden.c similarity index 69% rename from utils/src/listxattr_raw.c rename to utils/src/listxattr_hidden.c index f7b69c0d..a98426aa 100644 --- a/utils/src/listxattr_raw.c +++ b/utils/src/listxattr_hidden.c @@ -21,9 +21,9 @@ static struct option long_ops[] = { { NULL, 0, NULL, 0} }; -static int listxattr_raw_cmd(int argc, char **argv) +static int listxattr_hidden_cmd(int argc, char **argv) { - struct scoutfs_ioctl_listxattr_raw lxr; + struct scoutfs_ioctl_listxattr_hidden lxh; char *path = NULL; char *buf = NULL; char *name; @@ -57,17 +57,17 @@ static int listxattr_raw_cmd(int argc, char **argv) goto out; } - memset(&lxr, 0, sizeof(lxr)); - lxr.id_pos = 0; - lxr.hash_pos = 0; - lxr.buf_bytes = 256 * 1024; + memset(&lxh, 0, sizeof(lxh)); + lxh.id_pos = 0; + lxh.hash_pos = 0; + lxh.buf_bytes = 256 * 1024; - buf = malloc(lxr.buf_bytes); + buf = malloc(lxh.buf_bytes); if (!buf) { fprintf(stderr, "xattr name buf alloc failed\n"); return -ENOMEM; } - lxr.buf_ptr = (unsigned long)buf; + lxh.buf_ptr = (unsigned long)buf; fd = open(path, O_RDONLY); if (fd < 0) { @@ -79,25 +79,25 @@ static int listxattr_raw_cmd(int argc, char **argv) for (;;) { - ret = ioctl(fd, SCOUTFS_IOC_LISTXATTR_RAW, &lxr); + ret = ioctl(fd, SCOUTFS_IOC_LISTXATTR_HIDDEN, &lxh); if (ret == 0) break; if (ret < 0) { ret = -errno; - fprintf(stderr, "listxattr_raw ioctl failed: " + fprintf(stderr, "listxattr_hidden ioctl failed: " "%s (%d)\n", strerror(errno), errno); goto out; } bytes = ret; - if (bytes > lxr.buf_bytes) { - fprintf(stderr, "listxattr_raw overflowed\n"); + if (bytes > lxh.buf_bytes) { + fprintf(stderr, "listxattr_hidden overflowed\n"); ret = -EFAULT; goto out; } if (buf[bytes - 1] != '\0') { - fprintf(stderr, "listxattr_raw didn't term\n"); + fprintf(stderr, "listxattr_hidden didn't term\n"); ret = -EINVAL; goto out; } @@ -107,13 +107,13 @@ static int listxattr_raw_cmd(int argc, char **argv) do { len = strlen(name); if (len == 0) { - fprintf(stderr, "listxattr_raw empty name\n"); + fprintf(stderr, "listxattr_hidden empty name\n"); ret = -EINVAL; goto out; } if (len > SCOUTFS_XATTR_MAX_NAME_LEN) { - fprintf(stderr, "listxattr_raw long name\n"); + fprintf(stderr, "listxattr_hidden long name\n"); ret = -EINVAL; goto out; } @@ -139,9 +139,9 @@ out: return ret; }; -static void __attribute__((constructor)) listxattr_raw_ctor(void) +static void __attribute__((constructor)) listxattr_hidden_ctor(void) { - cmd_register("listxattr-raw", "-f ", - "print only the names of all xattrs on the file", - listxattr_raw_cmd); + cmd_register("listxattr-hidden", "-f ", + "print the names of hidden xattrs on the file", + listxattr_hidden_cmd); } From 2dc611a433ea7984511e9400426f85c61f8c1487 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Mon, 24 Jun 2019 11:29:50 -0700 Subject: [PATCH 177/235] scoutfs-utils: update sysfs dir to use fr identity Signed-off-by: Zach Brown --- utils/src/counters.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/utils/src/counters.c b/utils/src/counters.c index dfee51cc..676226dc 100644 --- a/utils/src/counters.c +++ b/utils/src/counters.c @@ -78,7 +78,7 @@ static int counters_cmd(int argc, char **argv) table = false; if (dir_arg == NULL) { - printf("scoutfs counter-table: need sysfs scoutfs dir path (i.e. /sys/fs/scoutfs/$DEV)\n"); + printf("scoutfs counter-table: need mount sysfs dir (i.e. /sys/fs/scoutfs/$fr)\n"); return -EINVAL; } @@ -278,7 +278,7 @@ out: static void __attribute__((constructor)) counters_ctor(void) { - cmd_register("counters", "[-t] ", + cmd_register("counters", "[-t] ", "show [tablular] counters for a given mounted volume", counters_cmd); } From fc15b816b0e38463328eb5756284a661ffc92899 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Mon, 24 Jun 2019 11:41:00 -0700 Subject: [PATCH 178/235] scoutfs-utils: update format for rid Signed-off-by: Zach Brown --- utils/src/format.h | 12 ++++++------ utils/src/key.c | 8 ++++---- utils/src/print.c | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index 19990161..68396c19 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -114,12 +114,12 @@ struct scoutfs_key { #define skxi_id _sk_third /* node free extent */ -#define sknf_node_id _sk_first +#define sknf_rid _sk_first #define sknf_major _sk_second #define sknf_minor _sk_third /* node orphan inode */ -#define sko_node_id _sk_first +#define sko_rid _sk_first #define sko_ino _sk_second /* inode */ @@ -357,7 +357,7 @@ struct scoutfs_segment_block { */ #define SCOUTFS_INODE_INDEX_ZONE 1 #define SCOUTFS_XATTR_INDEX_ZONE 2 -#define SCOUTFS_NODE_ZONE 3 +#define SCOUTFS_RID_ZONE 3 #define SCOUTFS_FS_ZONE 4 #define SCOUTFS_LOCK_ZONE 5 #define SCOUTFS_MAX_ZONE 8 /* power of 2 is efficient */ @@ -370,9 +370,10 @@ struct scoutfs_segment_block { /* xattr index zone */ #define SCOUTFS_XATTR_INDEX_NAME_TYPE 1 -/* node zone (also used in server alloc btree) */ +/* rid zone (also used in server alloc btree) */ #define SCOUTFS_FREE_EXTENT_BLKNO_TYPE 1 #define SCOUTFS_FREE_EXTENT_BLOCKS_TYPE 2 +#define SCOUTFS_ORPHAN_TYPE 3 /* fs zone */ #define SCOUTFS_INODE_TYPE 1 @@ -382,12 +383,11 @@ struct scoutfs_segment_block { #define SCOUTFS_LINK_BACKREF_TYPE 5 #define SCOUTFS_SYMLINK_TYPE 6 #define SCOUTFS_FILE_EXTENT_TYPE 7 -#define SCOUTFS_ORPHAN_TYPE 8 /* lock zone, only ever found in lock ranges, never in persistent items */ #define SCOUTFS_RENAME_TYPE 1 -#define SCOUTFS_MAX_TYPE 16 /* power of 2 is efficient */ +#define SCOUTFS_MAX_TYPE 8 /* power of 2 is efficient */ /* * File extents have more data than easily fits in the key so we move diff --git a/utils/src/key.c b/utils/src/key.c index 32be964c..4f17201d 100644 --- a/utils/src/key.c +++ b/utils/src/key.c @@ -22,7 +22,7 @@ char *scoutfs_zone_strings[SCOUTFS_MAX_ZONE] = { [SCOUTFS_INODE_INDEX_ZONE] = "ind", [SCOUTFS_XATTR_INDEX_ZONE] = "xnd", - [SCOUTFS_NODE_ZONE] = "nod", + [SCOUTFS_RID_ZONE] = "rid", [SCOUTFS_FS_ZONE] = "fs", }; @@ -30,9 +30,9 @@ char *scoutfs_type_strings[SCOUTFS_MAX_ZONE][SCOUTFS_MAX_TYPE] = { [SCOUTFS_INODE_INDEX_ZONE][SCOUTFS_INODE_INDEX_META_SEQ_TYPE] = "msq", [SCOUTFS_INODE_INDEX_ZONE][SCOUTFS_INODE_INDEX_DATA_SEQ_TYPE] = "dsq", [SCOUTFS_XATTR_INDEX_ZONE][SCOUTFS_XATTR_INDEX_NAME_TYPE] = "nam", - [SCOUTFS_NODE_ZONE][SCOUTFS_FREE_EXTENT_BLKNO_TYPE] = "fbn", - [SCOUTFS_NODE_ZONE][SCOUTFS_FREE_EXTENT_BLOCKS_TYPE] = "fbs", - [SCOUTFS_NODE_ZONE][SCOUTFS_ORPHAN_TYPE] = "orp", + [SCOUTFS_RID_ZONE][SCOUTFS_FREE_EXTENT_BLKNO_TYPE] = "fbn", + [SCOUTFS_RID_ZONE][SCOUTFS_FREE_EXTENT_BLOCKS_TYPE] = "fbs", + [SCOUTFS_RID_ZONE][SCOUTFS_ORPHAN_TYPE] = "orp", [SCOUTFS_FS_ZONE][SCOUTFS_INODE_TYPE] = "ino", [SCOUTFS_FS_ZONE][SCOUTFS_XATTR_TYPE] = "xat", [SCOUTFS_FS_ZONE][SCOUTFS_DIRENT_TYPE] = "dnt", diff --git a/utils/src/print.c b/utils/src/print.c index dd9f7308..b41793c0 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -217,7 +217,7 @@ static print_func_t find_printer(u8 zone, u8 type) type >= SCOUTFS_XATTR_INDEX_NAME_TYPE) return print_xattr_index; - if (zone == SCOUTFS_NODE_ZONE) { + if (zone == SCOUTFS_RID_ZONE) { if (type == SCOUTFS_FREE_EXTENT_BLKNO_TYPE || type == SCOUTFS_FREE_EXTENT_BLOCKS_TYPE) return print_free_extent; From 3670a5b80dd31a119bf6d924f976df2f8fe42026 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Tue, 25 Jun 2019 10:03:07 -0700 Subject: [PATCH 179/235] scoutfs-utils: remove quorum slot config The format no longer has statically configured named slots. The only persistent config is the number of monts that must be voting to reach quorum. The quorum blocks now have a log of successfull elections. Signed-off-by: Zach Brown --- utils/src/format.h | 113 +++++++++++++---------------- utils/src/mkfs.c | 175 ++++++--------------------------------------- utils/src/print.c | 128 ++++++++++++++++++--------------- 3 files changed, 141 insertions(+), 275 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index 68396c19..c2f2fa15 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -42,11 +42,14 @@ /* * A reasonably large region of aligned quorum blocks follow the super - * block. + * block. Each voting cycle reads the entire region so we don't want it + * to be too enormous. 256K seems like a reasonably chunky single IO. + * The number of blocks in the region also determines the number of + * mounts that have a reasonable probability of not overwriting each + * other's random block locations. */ -#define SCOUTFS_QUORUM_BLKNO ((128ULL * 1024) >> SCOUTFS_BLOCK_SHIFT) -#define SCOUTFS_QUORUM_BLOCKS ((128ULL * 1024) >> SCOUTFS_BLOCK_SHIFT) -#define SCOUTFS_QUORUM_MAX_SLOTS SCOUTFS_QUORUM_BLOCKS +#define SCOUTFS_QUORUM_BLKNO ((256ULL * 1024) >> SCOUTFS_BLOCK_SHIFT) +#define SCOUTFS_QUORUM_BLOCKS ((256ULL * 1024) >> SCOUTFS_BLOCK_SHIFT) #define SCOUTFS_UNIQUE_NAME_MAX_BYTES 64 /* includes null */ @@ -304,9 +307,10 @@ struct scoutfs_mounted_client_btree_key { } __packed; struct scoutfs_mounted_client_btree_val { - __u8 name[SCOUTFS_UNIQUE_NAME_MAX_BYTES]; + __u8 flags; } __packed; +#define SCOUTFS_MOUNTED_CLIENT_VOTER (1 << 0) /* * The max number of links defines the max number of entries that we can @@ -421,70 +425,47 @@ struct scoutfs_xattr { #define SCOUTFS_UUID_BYTES 16 /* - * During each quorum voting interval the fabric has to process 2 reads - * and a write for each voting mount. The only reason we limit the - * number of active quorum mounts is to limit the number of IOs per - * interval. We use a pretty conservative interval given that IOs will - * generally be faster than our constant and we'll have fewer active - * than the max. + * Mounts read all the quorum blocks and write to one random quorum + * block during a cycle. The min cycle time limits the per-mount iop + * load during elections. The random cycle delay makes it less likely + * that mounts will read and write at the same time and miss each + * other's writes. An election only completes if a quorum of mounts + * vote for a leader before any of their elections timeout. This is + * made less likely by the probability that mounts will overwrite each + * others random block locations. The max quorum count limits that + * probability. 9 mounts only have a 55% chance of writing to unique 4k + * blocks in a 256k region. The election timeout is set to include + * enough cycles to usually complete the election. Once a leader is + * elected it spends a number of cycles writing out blocks with itself + * logged as a leader. This reduces the possibility that servers + * will have their log entries overwritten and not be fenced. */ -#define SCOUTFS_QUORUM_MAX_ACTIVE 7 -#define SCOUTFS_QUORUM_IO_LATENCY_MS 10 -#define SCOUTFS_QUORUM_INTERVAL_MS \ - (SCOUTFS_QUORUM_MAX_ACTIVE * 3 * SCOUTFS_QUORUM_IO_LATENCY_MS) +#define SCOUTFS_QUORUM_MAX_COUNT 9 +#define SCOUTFS_QUORUM_CYCLE_LO_MS 10 +#define SCOUTFS_QUORUM_CYCLE_HI_MS 20 +#define SCOUTFS_QUORUM_TERM_LO_MS 250 +#define SCOUTFS_QUORUM_TERM_HI_MS 500 +#define SCOUTFS_QUORUM_ELECTED_LOG_CYCLES 10 -/* - * Each mount that is found in the quorum config in the super block can - * write to quorum blocks indicating which mount they vote for as - * the leader. - * - * @config_gen: references the config gen in the super block - * @write_nr: incremented for every write, only 0 when never written - * @elected_nr: incremented when elected, 0 otherwise - * @unmount_barrier: incremented by servers when all members have unmounted - * @vote_slot: the active config slot that the writer is voting for - */ struct scoutfs_quorum_block { __le64 fsid; __le64 blkno; - __le64 config_gen; + __le64 term; __le64 write_nr; - __le64 elected_nr; - __le64 unmount_barrier; + __le64 voter_rid; + __le64 vote_for_rid; __le32 crc; - __u8 vote_slot; - __u8 flags; -} __packed; - -#define SCOUTFS_QUORUM_BLOCK_FLAG_ELECTED (1 << 0) -#define SCOUTFS_QUORUM_BLOCK_FLAG_LISTENING (1 << 1) -#define SCOUTFS_QUORUM_BLOCK_FLAGS_UNKNOWN (U8_MAX << 2) - -#define SCOUTFS_QUORUM_MAX_SLOTS SCOUTFS_QUORUM_BLOCKS - -/* - * Each quorum voter is described by a slot which corresponds to the - * block that the voter will write to. - * - * The stale flag is used to support config migration. A new - * configuration is written in free slots and the old configuration is - * marked stale. Stale slots can only be reclaimed once we have - * evidence that the named mount won't try and write to it by seeing it - * write to other slots or connect with the new gen. - */ -struct scoutfs_quorum_config { - __le64 gen; - struct scoutfs_quorum_slot { - __u8 name[SCOUTFS_UNIQUE_NAME_MAX_BYTES]; + __u8 log_nr; + struct scoutfs_quorum_log { + __le64 term; + __le64 rid; struct scoutfs_inet_addr addr; - __u8 vote_priority; - __u8 flags; - } __packed slots[SCOUTFS_QUORUM_MAX_SLOTS]; + } __packed log[0]; } __packed; -#define SCOUTFS_QUORUM_SLOT_ACTIVE (1 << 0) -#define SCOUTFS_QUORUM_SLOT_STALE (1 << 1) -#define SCOUTFS_QUORUM_SLOT_FLAGS_UNKNOWN (U8_MAX << 2) +#define SCOUTFS_QUORUM_LOG_MAX \ + ((SCOUTFS_BLOCK_SIZE - sizeof(struct scoutfs_quorum_block)) / \ + sizeof(struct scoutfs_quorum_log)) struct scoutfs_super_block { struct scoutfs_block_header hdr; @@ -500,9 +481,13 @@ struct scoutfs_super_block { __le64 next_seg_seq; __le64 next_node_id; __le64 next_compact_id; + __le64 quorum_fenced_term; + __le64 quorum_server_term; + __le64 unmount_barrier; + __u8 quorum_count; + struct scoutfs_inet_addr server_addr; struct scoutfs_btree_root alloc_root; struct scoutfs_manifest manifest; - struct scoutfs_quorum_config quorum_config; struct scoutfs_btree_root lock_clients; struct scoutfs_btree_root trans_seqs; struct scoutfs_btree_root mounted_clients; @@ -624,8 +609,6 @@ enum { * Greetings verify identity of communicating nodes. The sender sends * their credentials and the receiver verifies them. * - * @name: The client sends its unique name to the server. - * * @server_term: The raft term that elected the server. Initially 0 * from the client, sent by the server, then sent by the client as it * tries to reconnect. Used to identify a client reconnecting to a @@ -634,7 +617,7 @@ enum { * @unmount_barrier: Incremented every time the remaining majority of * quorum members all agree to leave. The server tells a quorum member * the value that it's connecting under so that if the client sees the - * value increase in a quorum block it knows that the server has + * value increase in the super block then it knows that the server has * processed its farewell and can safely unmount. * * @node_id: The id of the client. Initially 0 from the client, @@ -643,7 +626,6 @@ enum { * state must be dealt with. */ struct scoutfs_net_greeting { - __u8 name[SCOUTFS_UNIQUE_NAME_MAX_BYTES]; __le64 fsid; __le64 format_hash; __le64 server_term; @@ -653,7 +635,8 @@ struct scoutfs_net_greeting { } __packed; #define SCOUTFS_NET_GREETING_FLAG_FAREWELL (1 << 0) -#define SCOUTFS_NET_GREETING_FLAG_INVALID (~(__u64)0 << 1) +#define SCOUTFS_NET_GREETING_FLAG_VOTER (1 << 1) +#define SCOUTFS_NET_GREETING_FLAG_INVALID (~(__u64)0 << 2) /* * This header precedes and describes all network messages sent over diff --git a/utils/src/mkfs.c b/utils/src/mkfs.c index a9c61500..446f8714 100644 --- a/utils/src/mkfs.c +++ b/utils/src/mkfs.c @@ -163,7 +163,7 @@ static char *size_str(u64 nr, unsigned size) * - btree ring blocks with manifest and allocator btree blocks * - segment with root inode items */ -static int write_new_fs(char *path, int fd, struct scoutfs_quorum_config *conf) +static int write_new_fs(char *path, int fd, u8 quorum_count) { struct scoutfs_super_block *super; struct scoutfs_key *ino_key; @@ -176,9 +176,7 @@ static int write_new_fs(char *path, int fd, struct scoutfs_quorum_config *conf) struct scoutfs_btree_block *bt; struct scoutfs_btree_item *btitem; struct scoutfs_segment_item *item; - struct scoutfs_quorum_slot *slot; struct scoutfs_key key; - struct in_addr in; __le32 *prev_link; struct timeval tv; char uuid_str[37]; @@ -239,9 +237,7 @@ static int write_new_fs(char *path, int fd, struct scoutfs_quorum_config *conf) super->next_seg_seq = cpu_to_le64(2); super->next_node_id = cpu_to_le64(1); super->next_compact_id = cpu_to_le64(1); - - super->quorum_config = *conf; - super->quorum_config.gen = cpu_to_le64(1); + super->quorum_count = quorum_count; /* align the btree ring to the segment after the super */ blkno = round_up(SCOUTFS_SUPER_BLKNO + 1, SCOUTFS_SEGMENT_BLOCKS); @@ -435,7 +431,8 @@ static int write_new_fs(char *path, int fd, struct scoutfs_quorum_config *conf) " device bytes: "SIZE_FMT"\n" " device blocks: "SIZE_FMT"\n" " btree ring blocks: "SIZE_FMT"\n" - " free blocks: "SIZE_FMT"\n", + " free blocks: "SIZE_FMT"\n" + " quorum count: %u\n", path, le64_to_cpu(super->hdr.fsid), le64_to_cpu(super->format_hash), @@ -445,20 +442,8 @@ static int write_new_fs(char *path, int fd, struct scoutfs_quorum_config *conf) SIZE_ARGS(le64_to_cpu(super->bring.nr_blocks), SCOUTFS_BLOCK_SIZE), SIZE_ARGS(le64_to_cpu(super->free_blocks), - SCOUTFS_BLOCK_SIZE)); - - printf(" quorum slots:\n"); - for (i = 0; i < array_size(super->quorum_config.slots); i++) { - slot = &super->quorum_config.slots[i]; - if (slot->flags == 0) - continue; - - in.s_addr = htonl(le32_to_cpu(slot->addr.addr)); - - printf(" [%2u]: name %s priority %u addr:port %s:%u\n", - i, slot->name, slot->vote_priority, - inet_ntoa(in), le16_to_cpu(slot->addr.port)); - } + SCOUTFS_BLOCK_SIZE), + super->quorum_count); ret = 0; out: @@ -474,138 +459,16 @@ out: } static struct option long_ops[] = { - { "quorum_slot", 1, NULL, 'Q' }, + { "quorum_count", 1, NULL, 'Q' }, { NULL, 0, NULL, 0} }; -enum { NAME, PRIORITY, ADDR, PORT }; - -static int parse_quorum_slot(struct scoutfs_quorum_config *conf, char *arg) -{ - struct scoutfs_quorum_slot *slot; - struct scoutfs_quorum_slot *sl; - struct in_addr in; - unsigned long port; - int free_slot; - char *save; - char *tok; - char *dup; - char *s; - int ret; - int i; - - dup = strdup(arg); - if (!dup) { - printf("allocation failure while parsing quorum slot '%s'\n", - arg); - return -EINVAL; - } - - for (i = 0; i < array_size(conf->slots); i++) { - if (conf->slots[i].flags == 0) - break; - } - if (i == array_size(conf->slots)) { - printf("too many quorum slots provided\n"); - ret = -EINVAL; - goto out; - } - slot = &conf->slots[i]; - free_slot = i; - - slot->addr.port = cpu_to_le16(23853); /* randomly chosen */ - - for (save = NULL, s = dup, i = NAME; i <= PORT; i++, s = NULL) { - tok = strtok_r(s, ":", &save); - - if (tok == NULL) - break; - - /* assume flags and a default port */ - if (i == PORT && !isdigit(tok[0])) - i = PRIORITY; - - switch(i) { - case NAME: - if (strlen(tok) >= SCOUTFS_UNIQUE_NAME_MAX_BYTES) { - printf("quorum slot name too long: %s\n", tok); - return -EINVAL; - } - strcpy((char *)slot->name, tok); - break; - - case PRIORITY: - slot->vote_priority = strtoul(tok, NULL, 0); - if (slot->vote_priority > 255) { - printf("invalid quorum slot priority: %s\n", - tok); - ret = -EINVAL; - goto out; - } - break; - - case ADDR: - if (inet_aton(tok, &in) == 0) { - printf("invalid quorum slot address: %s\n", tok); - ret = -EINVAL; - goto out; - } - slot->addr.addr = cpu_to_le32(htonl(in.s_addr)); - break; - - case PORT: - port = strtoul(tok, NULL, 0); - if (port == 0 || port >= 65535) { - printf("invalid quorum slot port: %s\n", tok); - ret = -EINVAL; - goto out; - } - slot->addr.port = cpu_to_le16(port); - break; - - } - } - - if (slot->name[0] == '\0') { - printf("quorum slot must specify name: %s\n", arg); - ret = -EINVAL; - goto out; - } - - if (slot->addr.addr == 0) { - printf("quorum slot must specify address: %s\n", arg); - ret = -EINVAL; - goto out; - } - - for (i = 0; i < free_slot; i++) { - sl = &conf->slots[i]; - - if (strcmp((char *)slot->name, (char *)sl->name) == 0) { - printf("duplicate quorum slot name: %s\n", arg); - ret = -EINVAL; - goto out; - } - - if (memcmp(&slot->addr, &sl->addr, sizeof(slot->addr)) == 0) { - printf("duplicate quorum slot addr: %s\n", arg); - ret = -EINVAL; - goto out; - } - } - - slot->flags = SCOUTFS_QUORUM_SLOT_ACTIVE; - ret = 0; -out: - free(dup); - return ret; -} - static int mkfs_func(int argc, char *argv[]) { - struct scoutfs_quorum_config conf = {0,}; - bool have_quorum = false; + unsigned long long ull; char *path = argv[1]; + u8 quorum_count = 0; + char *end = NULL; int ret; int fd; int c; @@ -613,10 +476,14 @@ static int mkfs_func(int argc, char *argv[]) while ((c = getopt_long(argc, argv, "Q:", long_ops, NULL)) != -1) { switch (c) { case 'Q': - ret = parse_quorum_slot(&conf, optarg); - if (ret) - return ret; - have_quorum = true; + ull = strtoull(optarg, &end, 0); + if (*end != '\0' || ull == 0 || + ull > SCOUTFS_QUORUM_MAX_COUNT) { + printf("scoutfs: invalid quorum count '%s'\n", + optarg); + return -EINVAL; + } + quorum_count = ull; break; case '?': default: @@ -631,8 +498,8 @@ static int mkfs_func(int argc, char *argv[]) path = argv[optind]; - if (!have_quorum) { - printf("must configure quorum with --quorum_slot|-Q options\n"); + if (!quorum_count) { + printf("provide quorum count with --quorum_count|-Q option\n"); return -EINVAL; } @@ -644,7 +511,7 @@ static int mkfs_func(int argc, char *argv[]) return ret; } - ret = write_new_fs(path, fd, &conf); + ret = write_new_fs(path, fd, quorum_count); close(fd); return ret; diff --git a/utils/src/print.c b/utils/src/print.c index b41793c0..4f2cec39 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -382,8 +382,8 @@ static int print_mounted_client_entry(void *key, unsigned key_len, void *val, struct scoutfs_mounted_client_btree_key *mck = key; struct scoutfs_mounted_client_btree_val *mcv = val; - printf(" node_id %llu name %s\n", - be64_to_cpu(mck->node_id), mcv->name); + printf(" node_id %llu flags 0x%x\n", + be64_to_cpu(mck->node_id), mcv->flags); return 0; } @@ -489,70 +489,89 @@ static int print_btree(int fd, struct scoutfs_super_block *super, char *which, return ret; } +static char *alloc_addr_str(struct scoutfs_inet_addr *ia) +{ + struct in_addr addr; + char *quad; + char *str; + int len; + + memset(&addr, 0, sizeof(addr)); + addr.s_addr = htonl(le32_to_cpu(ia->addr)); + quad = inet_ntoa(addr); + if (quad == NULL) + return NULL; + + len = snprintf(NULL, 0, "%s:%u", quad, le16_to_cpu(ia->port)); + if (len < 1 || len > 22) + return NULL; + + len++; /* null */ + str = malloc(len); + if (!str) + return NULL; + + snprintf(str, len, "%s:%u", quad, le16_to_cpu(ia->port)); + return str; +} + static int print_quorum_blocks(int fd, struct scoutfs_super_block *super) { - struct scoutfs_quorum_block *blk; + struct scoutfs_quorum_block *blk = NULL; + char *log_addr = NULL; u64 blkno; int ret; int i; + int j; for (i = 0; i < SCOUTFS_QUORUM_BLOCKS; i++) { blkno = SCOUTFS_QUORUM_BLKNO + i; + free(blk); blk = read_block(fd, blkno); if (!blk) { ret = -ENOMEM; - break; + goto out; } - if (blk->fsid != 0 || blk->write_nr != 0) { + if (blk->voter_rid != 0) { printf("quorum block blkno %llu\n" - " fsid %llx blkno %llu config_gen %llu crc 0x%08x\n" - " write_nr %llu elected_nr %llu " - "unmount_barrier %llu vote_slot %u flags %02x\n", + " fsid %llx blkno %llu crc 0x%08x\n" + " term %llu write_nr %llu voter_rid %016llx " + "vote_for_rid %016llx\n" + " log_nr %u\n", blkno, le64_to_cpu(blk->fsid), - le64_to_cpu(blk->blkno), - le64_to_cpu(blk->config_gen), - le32_to_cpu(blk->crc), + le64_to_cpu(blk->blkno), le32_to_cpu(blk->crc), + le64_to_cpu(blk->term), le64_to_cpu(blk->write_nr), - le64_to_cpu(blk->elected_nr), - le64_to_cpu(blk->unmount_barrier), - blk->vote_slot, blk->flags); + le64_to_cpu(blk->voter_rid), + le64_to_cpu(blk->vote_for_rid), + blk->log_nr); + for (j = 0; j < blk->log_nr; j++) { + free(log_addr); + log_addr = alloc_addr_str(&blk->log[j].addr); + if (!log_addr) { + ret = -ENOMEM; + goto out; + } + printf(" [%u]: term %llu rid %llu addr %s\n", + j, le64_to_cpu(blk->log[j].term), + le64_to_cpu(blk->log[j].rid), + log_addr); + } } - - free(blk); - ret = 0; } + ret = 0; +out: + free(log_addr); + return ret; } -static void print_slot_flags(unsigned long flags) -{ - if (flags == 0) { - printf("-"); - return; - } - - while (flags) { - if (flags & SCOUTFS_QUORUM_SLOT_ACTIVE) { - printf("active"); - flags &= ~SCOUTFS_QUORUM_SLOT_ACTIVE; - - } else if (flags & SCOUTFS_QUORUM_SLOT_STALE) { - printf("stale"); - flags &= ~SCOUTFS_QUORUM_SLOT_STALE; - } - - if (flags) - printf(","); - } -} - static void print_super_block(struct scoutfs_super_block *super, u64 blkno) { - struct scoutfs_quorum_slot *slot; char uuid_str[37]; - struct in_addr in; + char *server_addr; u64 count; int i; @@ -563,10 +582,16 @@ static void print_super_block(struct scoutfs_super_block *super, u64 blkno) printf(" format_hash %llx uuid %s\n", le64_to_cpu(super->format_hash), uuid_str); + server_addr = alloc_addr_str(&super->server_addr); + if (!server_addr) + return; + /* XXX these are all in a crazy order */ printf(" next_ino %llu next_trans_seq %llu next_seg_seq %llu\n" " next_node_id %llu next_compact_id %llu\n" " total_blocks %llu free_blocks %llu alloc_cursor %llu\n" + " quorum_fenced_term %llu quorum_server_term %llu unmount_barrier %llu\n" + " quorum_count %u server_addr %s\n" " btree ring: first_blkno %llu nr_blocks %llu next_block %llu " "next_seq %llu\n" " lock_clients root: height %u blkno %llu seq %llu mig_len %u\n" @@ -582,6 +607,11 @@ static void print_super_block(struct scoutfs_super_block *super, u64 blkno) le64_to_cpu(super->total_blocks), le64_to_cpu(super->free_blocks), le64_to_cpu(super->alloc_cursor), + le64_to_cpu(super->quorum_fenced_term), + le64_to_cpu(super->quorum_server_term), + le64_to_cpu(super->unmount_barrier), + super->quorum_count, + server_addr, le64_to_cpu(super->bring.first_blkno), le64_to_cpu(super->bring.nr_blocks), le64_to_cpu(super->bring.next_block), @@ -615,21 +645,7 @@ static void print_super_block(struct scoutfs_super_block *super, u64 blkno) } printf("\n"); - printf(" quorum_config:\n gen: %llu\n", - le64_to_cpu(super->quorum_config.gen)); - for (i = 0; i < array_size(super->quorum_config.slots); i++) { - slot = &super->quorum_config.slots[i]; - if (slot->flags == 0) - continue; - - in.s_addr = htonl(le32_to_cpu(slot->addr.addr)); - - printf(" [%2u]: name %s priority %u addr %s:%u flags ", - i, slot->name, slot->vote_priority, inet_ntoa(in), - le16_to_cpu(slot->addr.port)); - print_slot_flags(slot->flags); - printf("\n"); - } + free(server_addr); } static int print_volume(int fd) From 7cd8738addf892c8499b363ca79c70457e5ffe70 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Wed, 10 Jul 2019 14:07:47 -0700 Subject: [PATCH 180/235] scoutfs-utils: net uses rid instead of node_id Now that networking is identifing clients by their rid some persistent structures are using that to store records of clients. Signed-off-by: Zach Brown --- utils/src/format.h | 22 +++++++++++----------- utils/src/mkfs.c | 1 - utils/src/print.c | 13 ++++++------- 3 files changed, 17 insertions(+), 19 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index c2f2fa15..2534fe16 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -287,7 +287,7 @@ struct scoutfs_extent_btree_key { * server failover knows who to wait for before resuming operations. */ struct scoutfs_lock_client_btree_key { - __be64 node_id; + __be64 rid; } __packed; /* @@ -296,14 +296,14 @@ struct scoutfs_lock_client_btree_key { */ struct scoutfs_trans_seq_btree_key { __be64 trans_seq; - __be64 node_id; + __be64 rid; } __packed; /* * The server keeps a persistent record of mounted clients. */ struct scoutfs_mounted_client_btree_key { - __be64 node_id; + __be64 rid; } __packed; struct scoutfs_mounted_client_btree_val { @@ -479,7 +479,6 @@ struct scoutfs_super_block { __le64 alloc_cursor; struct scoutfs_btree_ring bring; __le64 next_seg_seq; - __le64 next_node_id; __le64 next_compact_id; __le64 quorum_fenced_term; __le64 quorum_server_term; @@ -611,8 +610,9 @@ enum { * * @server_term: The raft term that elected the server. Initially 0 * from the client, sent by the server, then sent by the client as it - * tries to reconnect. Used to identify a client reconnecting to a - * server that has timed out its connection. + * tries to reconnect. Used to identify a client reconnecting to both + * the same serer after receiving a greeting response and to a new + * server after failover. * * @unmount_barrier: Incremented every time the remaining majority of * quorum members all agree to leave. The server tells a quorum member @@ -620,17 +620,17 @@ enum { * value increase in the super block then it knows that the server has * processed its farewell and can safely unmount. * - * @node_id: The id of the client. Initially 0 from the client, - * assigned by the server, and sent by the client as it reconnects. - * Used by the server to identify reconnecting clients whose existing - * state must be dealt with. + * @rid: The client's random id that was generated once as the mount + * started up. This identifies a specific remote mount across + * connections and servers. It's set to the client's rid in both the + * request and response for consistency. */ struct scoutfs_net_greeting { __le64 fsid; __le64 format_hash; __le64 server_term; __le64 unmount_barrier; - __le64 node_id; + __le64 rid; __le64 flags; } __packed; diff --git a/utils/src/mkfs.c b/utils/src/mkfs.c index 446f8714..7a28f9ef 100644 --- a/utils/src/mkfs.c +++ b/utils/src/mkfs.c @@ -235,7 +235,6 @@ static int write_new_fs(char *path, int fd, u8 quorum_count) super->next_trans_seq = cpu_to_le64(1); super->total_blocks = cpu_to_le64(total_blocks); super->next_seg_seq = cpu_to_le64(2); - super->next_node_id = cpu_to_le64(1); super->next_compact_id = cpu_to_le64(1); super->quorum_count = quorum_count; diff --git a/utils/src/print.c b/utils/src/print.c index 4f2cec39..5e714b09 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -359,7 +359,7 @@ static int print_lock_clients_entry(void *key, unsigned key_len, void *val, { struct scoutfs_lock_client_btree_key *cbk = key; - printf(" node_ld %llu\n", be64_to_cpu(cbk->node_id)); + printf(" rid %016llx\n", be64_to_cpu(cbk->rid)); return 0; } @@ -369,8 +369,8 @@ static int print_trans_seqs_entry(void *key, unsigned key_len, void *val, { struct scoutfs_trans_seq_btree_key *tsk = key; - printf(" trans_seq %llu node_ld %llu\n", - be64_to_cpu(tsk->trans_seq), be64_to_cpu(tsk->node_id)); + printf(" trans_seq %llu rid %016llx\n", + be64_to_cpu(tsk->trans_seq), be64_to_cpu(tsk->rid)); return 0; } @@ -382,8 +382,8 @@ static int print_mounted_client_entry(void *key, unsigned key_len, void *val, struct scoutfs_mounted_client_btree_key *mck = key; struct scoutfs_mounted_client_btree_val *mcv = val; - printf(" node_id %llu flags 0x%x\n", - be64_to_cpu(mck->node_id), mcv->flags); + printf(" rid %016llx flags 0x%x\n", + be64_to_cpu(mck->rid), mcv->flags); return 0; } @@ -588,7 +588,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_trans_seq %llu next_seg_seq %llu\n" - " next_node_id %llu next_compact_id %llu\n" + " next_compact_id %llu\n" " total_blocks %llu free_blocks %llu alloc_cursor %llu\n" " quorum_fenced_term %llu quorum_server_term %llu unmount_barrier %llu\n" " quorum_count %u server_addr %s\n" @@ -602,7 +602,6 @@ static void print_super_block(struct scoutfs_super_block *super, u64 blkno) le64_to_cpu(super->next_ino), le64_to_cpu(super->next_trans_seq), le64_to_cpu(super->next_seg_seq), - le64_to_cpu(super->next_node_id), le64_to_cpu(super->next_compact_id), le64_to_cpu(super->total_blocks), le64_to_cpu(super->free_blocks), From 70efa2f9059cfa440448907c2a52194ad7f0df93 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Fri, 19 Jul 2019 16:34:37 -0700 Subject: [PATCH 181/235] scoutfs-utils: add statfs wrapper Add a scoutfs command wrapper around the statfs_moe ioctl. It's the same as the stat_more ioctl but has different fields and a different ioctl. Signed-off-by: Zach Brown --- utils/src/ioctl.h | 22 +++++++ utils/src/stat.c | 144 ++++++++++++++++++++++++++++++++++++---------- 2 files changed, 136 insertions(+), 30 deletions(-) diff --git a/utils/src/ioctl.h b/utils/src/ioctl.h index 5aa057c0..5693668e 100644 --- a/utils/src/ioctl.h +++ b/utils/src/ioctl.h @@ -347,4 +347,26 @@ struct scoutfs_ioctl_find_xattrs { #define SCOUTFS_IOC_FIND_XATTRS _IOR(SCOUTFS_IOCTL_MAGIC, 10, \ struct scoutfs_ioctl_find_xattrs) +/* + * Give the user information about the filesystem. + * + * @valid_bytes stores the number of bytes that are valid in the + * structure. The caller sets this to the size of the struct that they + * understand. The kernel then fills and copies back the min of the + * size they and the user caller understand. The user can tell if a + * field is set if all of its bytes are within the valid_bytes that the + * kernel set on return. + * + * New fields are only added to the end of the struct. + */ +struct scoutfs_ioctl_statfs_more { + __u64 valid_bytes; + __u64 fsid; + __u64 rid; +} __packed; + +#define SCOUTFS_IOC_STATFS_MORE _IOR(SCOUTFS_IOCTL_MAGIC, 11, \ + struct scoutfs_ioctl_statfs_more) + + #endif diff --git a/utils/src/stat.c b/utils/src/stat.c index 2ae1a91e..9187feb2 100644 --- a/utils/src/stat.c +++ b/utils/src/stat.c @@ -16,43 +16,115 @@ #include "ioctl.h" #include "cmd.h" -#define FIELD(f) { \ - .name = #f, \ - .offset = offsetof(struct scoutfs_ioctl_stat_more, f), \ -} - -static struct stat_more_field { +struct stat_more_field { char *name; size_t offset; -} fields[] = { - FIELD(meta_seq), - FIELD(data_seq), - FIELD(data_version), - FIELD(online_blocks), - FIELD(offline_blocks), +}; + +#define FIELD(f, o) { \ + .name = #f, \ + .offset = o, \ +} + +#define INODE_FIELD_OFF(f) offsetof(struct scoutfs_ioctl_stat_more, f) +#define INODE_FIELD(f) FIELD(f, INODE_FIELD_OFF(f)) + +static struct stat_more_field inode_fields[] = { + INODE_FIELD(meta_seq), + INODE_FIELD(data_seq), + INODE_FIELD(data_version), + INODE_FIELD(online_blocks), + INODE_FIELD(offline_blocks), { NULL, } }; -#define for_each_field(f) \ +static void print_inode_field(void *st, size_t off) +{ + struct scoutfs_ioctl_stat_more *stm = st; + + switch(off) { + case INODE_FIELD_OFF(meta_seq): + printf("%llu", stm->meta_seq); + break; + case INODE_FIELD_OFF(data_seq): + printf("%llu", stm->data_seq); + break; + case INODE_FIELD_OFF(data_version): + printf("%llu", stm->data_version); + break; + case INODE_FIELD_OFF(online_blocks): + printf("%llu", stm->online_blocks); + break; + case INODE_FIELD_OFF(offline_blocks): + printf("%llu", stm->offline_blocks); + break; + }; +} + +#define FS_FIELD_OFF(f) offsetof(struct scoutfs_ioctl_statfs_more, f) +#define FS_FIELD(f) FIELD(f, FS_FIELD_OFF(f)) + +static struct stat_more_field fs_fields[] = { + FS_FIELD(fsid), + FS_FIELD(rid), + { NULL, } +}; + +static void print_fs_field(void *st, size_t off) +{ + struct scoutfs_ioctl_statfs_more *sfm = st; + + switch(off) { + case FS_FIELD_OFF(fsid): + printf("%016llx", sfm->fsid); + break; + case FS_FIELD_OFF(rid): + printf("%016llx", sfm->rid); + break; + }; +} + +#define for_each_field(f, fields) \ for (f = fields; f->name; f++) +typedef void (*print_field_t)(void *st, size_t off); + static struct option long_ops[] = { { "single_field", 1, NULL, 's' }, { NULL, 0, NULL, 0} }; -static int stat_more_cmd(int argc, char **argv) +static int do_stat(int argc, char **argv, int is_inode) { - struct scoutfs_ioctl_stat_more stm; + union { + struct scoutfs_ioctl_stat_more stm; + struct scoutfs_ioctl_statfs_more sfm; + } st; struct stat_more_field *single = NULL; + struct stat_more_field *fields; struct stat_more_field *fi; char *single_name = NULL; + print_field_t pr = NULL; char *path; + int cmd; int ret; int fd; int i; int c; + memset(&st, 0, sizeof(st)); + if (is_inode) { + cmd = SCOUTFS_IOC_STAT_MORE; + fields = inode_fields; + st.stm.valid_bytes = sizeof(struct scoutfs_ioctl_stat_more); + pr = print_inode_field; + } else { + cmd = SCOUTFS_IOC_STATFS_MORE; + fields = fs_fields; + st.sfm.valid_bytes = sizeof(struct scoutfs_ioctl_statfs_more); + pr = print_fs_field; + } + while ((c = getopt_long(argc, argv, "s:", long_ops, NULL)) != -1) { switch (c) { case 's': @@ -66,15 +138,14 @@ static int stat_more_cmd(int argc, char **argv) } if (single_name) { - for_each_field(fi) { + for_each_field(fi, fields) { if (strcmp(fi->name, single_name) == 0) { single = fi; break; } } if (!single) { - fprintf(stderr, "unknown stat_more field: '%s'\n", - single_name); + fprintf(stderr, "unknown field: '%s'\n", single_name); return -EINVAL; } } @@ -95,24 +166,21 @@ static int stat_more_cmd(int argc, char **argv) continue; } - memset(&stm, 0, sizeof(stm)); - stm.valid_bytes = sizeof(stm); - - ret = ioctl(fd, SCOUTFS_IOC_STAT_MORE, &stm); + ret = ioctl(fd, cmd, &st); if (ret < 0) { ret = -errno; - fprintf(stderr, "stat_more ioctl failed on '%s': " + fprintf(stderr, "ioctl failed on '%s': " "%s (%d)\n", path, strerror(errno), errno); } else if (single) { - printf("%llu\n", - *(u64 *)((void *)&stm + single->offset)); - + pr(&st, single->offset); + printf("\n"); } else { printf("%-17s %s\n", "path", path); - for_each_field(fi) { - printf("%-17s %llu\n", fi->name, - *(u64 *)((void *)&stm + fi->offset)); + for_each_field(fi, fields) { + printf("%-17s ", fi->name); + pr(&st, fi->offset); + printf("\n"); } } @@ -122,8 +190,24 @@ static int stat_more_cmd(int argc, char **argv) return 0; } +static int stat_more_cmd(int argc, char **argv) +{ + return do_stat(argc, argv, 1); +} + +static int statfs_more_cmd(int argc, char **argv) +{ + return do_stat(argc, argv, 0); +} + static void __attribute__((constructor)) stat_more_ctor(void) { cmd_register("stat", "", - "print scoutfs stat information for path", stat_more_cmd); + "show scoutfs inode information", stat_more_cmd); +} + +static void __attribute__((constructor)) statfs_more_ctor(void) +{ + cmd_register("statfs", "", + "show scoutfs file system information", statfs_more_cmd); } From 3776c18c66145a820719d54da6b9bdbff13b46d0 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Mon, 30 Sep 2019 11:26:36 -0700 Subject: [PATCH 182/235] scoutfs-utils: switch to btree forest Remove all the lsm code from mkfs and print, replacing it with the forest of btrees. Signed-off-by: Zach Brown --- utils/src/crc.c | 9 - utils/src/crc.h | 1 - utils/src/format.h | 237 ++++++++--------------- utils/src/ioctl.h | 32 +-- utils/src/item-cache-keys.c | 92 --------- utils/src/mkfs.c | 298 ++++++++++------------------ utils/src/print.c | 376 ++++++++++++++++++++++-------------- 7 files changed, 414 insertions(+), 631 deletions(-) delete mode 100644 utils/src/item-cache-keys.c diff --git a/utils/src/crc.c b/utils/src/crc.c index 714afa90..38640fbc 100644 --- a/utils/src/crc.c +++ b/utils/src/crc.c @@ -37,12 +37,3 @@ u32 crc_block(struct scoutfs_block_header *hdr) return crc32c(~0, (char *)hdr + sizeof(hdr->crc), SCOUTFS_BLOCK_SIZE - sizeof(hdr->crc)); } - -u32 crc_segment(struct scoutfs_segment_block *sblk) -{ - u32 off = offsetof(struct scoutfs_segment_block, _padding) + - sizeof(sblk->_padding); - - return crc32c(~0, (char *)sblk + off, - le32_to_cpu(sblk->total_bytes) - off); -} diff --git a/utils/src/crc.h b/utils/src/crc.h index a928bf0a..6878bf2f 100644 --- a/utils/src/crc.h +++ b/utils/src/crc.h @@ -8,6 +8,5 @@ u32 crc32c(u32 crc, const void *data, unsigned int len); u64 crc32c_64(u32 crc, const void *data, unsigned int len); u32 crc_block(struct scoutfs_block_header *hdr); -u32 crc_segment(struct scoutfs_segment_block *seg); #endif diff --git a/utils/src/format.h b/utils/src/format.h index 2534fe16..ad408c39 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -7,6 +7,7 @@ /* block header magic values, chosen at random */ #define SCOUTFS_BLOCK_MAGIC_SUPER 0x103c428b #define SCOUTFS_BLOCK_MAGIC_BTREE 0xe597f96d +#define SCOUTFS_BLOCK_MAGIC_BLOOM 0x31995604 /* * The super block and btree blocks are fixed 4k. @@ -19,18 +20,6 @@ #define SCOUTFS_BLOCK_SECTORS (1 << SCOUTFS_BLOCK_SECTOR_SHIFT) #define SCOUTFS_BLOCK_MAX (U64_MAX >> SCOUTFS_BLOCK_SHIFT) -/* - * FS data is stored in segments, for now they're fixed size. They'll - * be dynamic. - */ -#define SCOUTFS_SEGMENT_SHIFT 20 -#define SCOUTFS_SEGMENT_SIZE (1 << SCOUTFS_SEGMENT_SHIFT) -#define SCOUTFS_SEGMENT_MASK (SCOUTFS_SEGMENT_SIZE - 1) -#define SCOUTFS_SEGMENT_PAGES (SCOUTFS_SEGMENT_SIZE / PAGE_SIZE) -#define SCOUTFS_SEGMENT_BLOCKS (SCOUTFS_SEGMENT_SIZE / SCOUTFS_BLOCK_SIZE) -#define SCOUTFS_SEGMENT_BLOCK_SHIFT \ - (SCOUTFS_SEGMENT_SHIFT - SCOUTFS_BLOCK_SHIFT) - #define SCOUTFS_PAGES_PER_BLOCK (SCOUTFS_BLOCK_SIZE / PAGE_SIZE) #define SCOUTFS_BLOCK_PAGE_ORDER (SCOUTFS_BLOCK_SHIFT - PAGE_SHIFT) @@ -162,7 +151,7 @@ struct scoutfs_key_be { /* chose reasonable max key and value lens that have room for some u64s */ #define SCOUTFS_BTREE_MAX_KEY_LEN 40 -#define SCOUTFS_BTREE_MAX_VAL_LEN 64 +#define SCOUTFS_BTREE_MAX_VAL_LEN 256 /* * The min number of free bytes we must leave in a parent as we descend @@ -198,19 +187,14 @@ struct scoutfs_btree_ref { /* * A height of X means that the first block read will have level X-1 and * the leaves will have level 0. - * - * The migration key is used to walk the tree finding old blocks to migrate - * into the current half of the ring. */ struct scoutfs_btree_root { struct scoutfs_btree_ref ref; __u8 height; - __le16 migration_key_len; - __u8 migration_key[SCOUTFS_BTREE_MAX_KEY_LEN]; } __packed; struct scoutfs_btree_item_header { - __le16 off; + __le32 off; } __packed; struct scoutfs_btree_item { @@ -221,52 +205,32 @@ struct scoutfs_btree_item { struct scoutfs_btree_block { struct scoutfs_block_header hdr; - __le16 free_end; - __le16 free_reclaim; - __le16 nr_items; + __le32 free_end; + __le32 nr_items; __u8 level; struct scoutfs_btree_item_header item_hdrs[0]; } __packed; -struct scoutfs_btree_ring { - __le64 first_blkno; - __le64 nr_blocks; - __le64 next_block; - __le64 next_seq; -} __packed; - /* - * This is absurdly huge. If there was only ever 1 item per segment and - * 2^64 items the tree could get this deep. + * Free metadata blocks are tracked by block allocator items. */ -#define SCOUTFS_MANIFEST_MAX_LEVEL 20 - -#define SCOUTFS_MANIFEST_FANOUT 10 - -struct scoutfs_manifest { +struct scoutfs_balloc_root { struct scoutfs_btree_root root; - __le64 level_counts[SCOUTFS_MANIFEST_MAX_LEVEL]; + __le64 total_free; +} __packed; +struct scoutfs_balloc_item_key { + __be64 base; } __packed; -/* - * Manifest entries are split across btree keys and values. Putting - * some entry fields in the value keeps the key smaller and increases - * the fanout of the btree which keeps the tree smaller and reduces - * block IO. - * - * The key is made up of the level, first key, and seq. At level 0 - * segments can completely overlap and have identical key ranges but we - * avoid duplicate btree keys by including the unique seq. - */ -struct scoutfs_manifest_btree_key { - __u8 level; - struct scoutfs_key_be first_key; - __be64 seq; -} __packed; +#define SCOUTFS_BALLOC_ITEM_BYTES 256 +#define SCOUTFS_BALLOC_ITEM_U64S (SCOUTFS_BALLOC_ITEM_BYTES / \ + sizeof(__u64)) +#define SCOUTFS_BALLOC_ITEM_BITS (SCOUTFS_BALLOC_ITEM_BYTES * 8) +#define SCOUTFS_BALLOC_ITEM_BASE_SHIFT ilog2(SCOUTFS_BALLOC_ITEM_BITS) +#define SCOUTFS_BALLOC_ITEM_BIT_MASK (SCOUTFS_BALLOC_ITEM_BITS - 1) -struct scoutfs_manifest_btree_val { - __le64 segno; - struct scoutfs_key last_key; +struct scoutfs_balloc_item_val { + __le64 bits[SCOUTFS_BALLOC_ITEM_U64S]; } __packed; /* @@ -312,50 +276,61 @@ struct scoutfs_mounted_client_btree_val { #define SCOUTFS_MOUNTED_CLIENT_VOTER (1 << 0) -/* - * The max number of links defines the max number of entries that we can - * index in o(log n) and the static list head storage size in the - * segment block. We always pay the static storage cost, which is tiny, - * and we can look at the number of items to know the greatest number of - * links and skip most of the initial 0 links. - */ -#define SCOUTFS_MAX_SKIP_LINKS 32 +struct scoutfs_log_trees { + struct scoutfs_balloc_root alloc_root; + struct scoutfs_balloc_root free_root; + struct scoutfs_btree_root item_root; + struct scoutfs_btree_ref bloom_ref; + __le64 rid; + __le64 nr; +} __packed; -/* - * Items are packed into segments and linked together in a skip list. - * Each item's header, links, key, and value are stored contiguously. - * They're not allowed to cross a block boundary. - */ -struct scoutfs_segment_item { - struct scoutfs_key key; - __le16 val_len; +struct scoutfs_log_trees_key { + __be64 rid; + __be64 nr; +} __packed; + +struct scoutfs_log_trees_val { + struct scoutfs_balloc_root alloc_root; + struct scoutfs_balloc_root free_root; + struct scoutfs_btree_root item_root; + struct scoutfs_btree_ref bloom_ref; +} __packed; + +struct scoutfs_log_item_value { + __le64 vers; __u8 flags; - __u8 nr_links; - __le32 skip_links[0]; - /* __u8 val_bytes[val_len] */ + __u8 data[0]; } __packed; -#define SCOUTFS_ITEM_FLAG_DELETION (1 << 0) - /* - * Each large segment starts with a segment block that describes the - * rest of the blocks that make up the segment. - * - * The crc covers the initial total_bytes of the segment but starts - * after the padding. + * FS items are limited by the max btree value length with the log item + * value header. */ -struct scoutfs_segment_block { - __le32 crc; - __le32 _padding; - __le64 segno; - __le64 seq; - __le32 last_item_off; - __le32 total_bytes; - __le32 nr_items; - __le32 skip_links[SCOUTFS_MAX_SKIP_LINKS]; - /* packed items */ +#define SCOUTFS_MAX_VAL_SIZE \ + (SCOUTFS_BTREE_MAX_VAL_LEN - sizeof(struct scoutfs_log_item_value)) + +#define SCOUTFS_LOG_ITEM_FLAG_DELETION (1 << 0) + +struct scoutfs_bloom_block { + struct scoutfs_block_header hdr; + __le64 total_set; + __le64 bits[0]; } __packed; +/* + * Log trees include a tree of items that make up a fixed size bloom + * filter. Just a few megs worth of items lets us test for the presence + * of locks that cover billions of files with a .1% chance of false + * positives. The log trees should be finalized and merged long before + * the bloom filters fill up and start returning excessive false positives. + */ +#define SCOUTFS_FOREST_BLOOM_NRS 7 +#define SCOUTFS_FOREST_BLOOM_BITS \ + (((SCOUTFS_BLOCK_SIZE - sizeof(struct scoutfs_bloom_block)) / \ + member_sizeof(struct scoutfs_bloom_block, bits[0])) * \ + member_sizeof(struct scoutfs_bloom_block, bits[0]) * 8) \ + /* * Keys are first sorted by major key zones. */ @@ -475,18 +450,21 @@ struct scoutfs_super_block { __le64 next_ino; __le64 next_trans_seq; __le64 total_blocks; + __le64 next_uninit_free_block; + __le64 core_balloc_cursor; __le64 free_blocks; - __le64 alloc_cursor; - struct scoutfs_btree_ring bring; - __le64 next_seg_seq; - __le64 next_compact_id; + __le64 first_fs_blkno; + __le64 last_fs_blkno; __le64 quorum_fenced_term; __le64 quorum_server_term; __le64 unmount_barrier; __u8 quorum_count; struct scoutfs_inet_addr server_addr; + struct scoutfs_balloc_root core_balloc_alloc; + struct scoutfs_balloc_root core_balloc_free; struct scoutfs_btree_root alloc_root; - struct scoutfs_manifest manifest; + struct scoutfs_btree_root fs_root; + struct scoutfs_btree_root logs_root; struct scoutfs_btree_root lock_clients; struct scoutfs_btree_root trans_seqs; struct scoutfs_btree_root mounted_clients; @@ -594,8 +572,6 @@ enum { DIV_ROUND_UP(sizeof(struct scoutfs_xattr) + name_len + val_len, \ SCOUTFS_XATTR_MAX_PART_SIZE); -#define SCOUTFS_MAX_VAL_SIZE SCOUTFS_XATTR_MAX_PART_SIZE - #define SCOUTFS_LOCK_INODE_GROUP_NR 1024 #define SCOUTFS_LOCK_INODE_GROUP_MASK (SCOUTFS_LOCK_INODE_GROUP_NR - 1) #define SCOUTFS_LOCK_SEQ_GROUP_MASK ((1ULL << 10) - 1) @@ -678,13 +654,11 @@ enum { SCOUTFS_NET_CMD_ALLOC_INODES, SCOUTFS_NET_CMD_ALLOC_EXTENT, SCOUTFS_NET_CMD_FREE_EXTENTS, - SCOUTFS_NET_CMD_ALLOC_SEGNO, - SCOUTFS_NET_CMD_RECORD_SEGMENT, + SCOUTFS_NET_CMD_GET_LOG_TREES, + SCOUTFS_NET_CMD_COMMIT_LOG_TREES, SCOUTFS_NET_CMD_ADVANCE_SEQ, SCOUTFS_NET_CMD_GET_LAST_SEQ, - SCOUTFS_NET_CMD_GET_MANIFEST_ROOT, SCOUTFS_NET_CMD_STATFS, - SCOUTFS_NET_CMD_COMPACT, SCOUTFS_NET_CMD_LOCK, SCOUTFS_NET_CMD_LOCK_RECOVER, SCOUTFS_NET_CMD_FAREWELL, @@ -723,20 +697,6 @@ struct scoutfs_net_inode_alloc { __le64 nr; } __packed; -struct scoutfs_net_key_range { - __le16 start_len; - __le16 end_len; - __u8 key_bytes[0]; -} __packed; - -struct scoutfs_net_manifest_entry { - __le64 segno; - __le64 seq; - struct scoutfs_key first; - struct scoutfs_key last; - __u8 level; -} __packed; - struct scoutfs_net_statfs { __le64 total_blocks; /* total blocks in device */ __le64 next_ino; /* next unused inode number */ @@ -763,52 +723,9 @@ struct scoutfs_net_extent_list { /* arbitrarily makes a nice ~1k extent list payload */ #define SCOUTFS_NET_EXTENT_LIST_MAX_NR 64 -/* one upper segment and fanout lower segments */ -#define SCOUTFS_COMPACTION_MAX_INPUT (1 + SCOUTFS_MANIFEST_FANOUT) -/* sticky can split the input and item alignment padding can add a lower */ -#define SCOUTFS_COMPACTION_SEGNO_OVERHEAD 2 -#define SCOUTFS_COMPACTION_MAX_OUTPUT \ - (SCOUTFS_COMPACTION_MAX_INPUT + SCOUTFS_COMPACTION_SEGNO_OVERHEAD) - -/* - * A compact request is sent by the server to the client. It provides - * the input segments and enough allocated segnos to write the results. - * The id uniquely identifies this compaction request and is included in - * the response to clean up its allocated resources. - */ -struct scoutfs_net_compact_request { - __le64 id; - __u8 last_level; - __u8 flags; - __le64 segnos[SCOUTFS_COMPACTION_MAX_OUTPUT]; - struct scoutfs_net_manifest_entry ents[SCOUTFS_COMPACTION_MAX_INPUT]; -} __packed; - -/* - * A sticky compaction has more lower level segments that overlap with - * the end of the upper after the last lower level segment included in - * the compaction. Items left in the upper segment after the last lower - * need to be written to the upper level instead of the lower. The - * upper segment "sticks" in place instead of moving down to the lower - * level. - */ -#define SCOUTFS_NET_COMPACT_FLAG_STICKY (1 << 0) - -/* - * A compact response is sent by the client to the server. It describes - * the written output segments that need to be added to the manifest. - * The server compares the response to the request to free unused - * allocated segnos and input manifest entries. An empty response is - * valid and can happen if, say, the upper input segment completely - * deleted all the items in a single overlapping lower segment. - */ -struct scoutfs_net_compact_response { - __le64 id; - struct scoutfs_net_manifest_entry ents[SCOUTFS_COMPACTION_MAX_OUTPUT]; -} __packed; - struct scoutfs_net_lock { struct scoutfs_key key; + __le64 write_version; __u8 old_mode; __u8 new_mode; } __packed; diff --git a/utils/src/ioctl.h b/utils/src/ioctl.h index 5693668e..df0c1b54 100644 --- a/utils/src/ioctl.h +++ b/utils/src/ioctl.h @@ -238,28 +238,6 @@ struct scoutfs_ioctl_stat_more { struct scoutfs_ioctl_stat_more) -/* - * Fills the buffer with either the keys for the cached items or the - * keys for the cached ranges found starting with the given key. The - * number of keys filled in the buffer is returned. When filling range - * keys the returned number will always be a multiple of two. - */ -struct scoutfs_ioctl_item_cache_keys { - struct scoutfs_ioctl_key ikey; - __u64 buf_ptr; - __u16 buf_nr; - __u8 which; - __u8 _pad[21]; /* padded to align _ioctl_key total size */ -}; - -enum { - SCOUTFS_IOC_ITEM_CACHE_KEYS_ITEMS = 0, - SCOUTFS_IOC_ITEM_CACHE_KEYS_RANGES, -}; - -#define SCOUTFS_IOC_ITEM_CACHE_KEYS _IOR(SCOUTFS_IOCTL_MAGIC, 6, \ - struct scoutfs_ioctl_item_cache_keys) - struct scoutfs_ioctl_data_waiting_entry { __u64 ino; __u64 iblock; @@ -283,7 +261,7 @@ struct scoutfs_ioctl_data_waiting { #define SCOUTFS_IOC_DATA_WAITING_FLAGS_UNKNOWN (U8_MAX << 0) -#define SCOUTFS_IOC_DATA_WAITING _IOR(SCOUTFS_IOCTL_MAGIC, 7, \ +#define SCOUTFS_IOC_DATA_WAITING _IOR(SCOUTFS_IOCTL_MAGIC, 6, \ struct scoutfs_ioctl_data_waiting) /* @@ -303,7 +281,7 @@ struct scoutfs_ioctl_setattr_more { #define SCOUTFS_IOC_SETATTR_MORE_OFFLINE (1 << 0) #define SCOUTFS_IOC_SETATTR_MORE_UNKNOWN (U8_MAX << 1) -#define SCOUTFS_IOC_SETATTR_MORE _IOW(SCOUTFS_IOCTL_MAGIC, 8, \ +#define SCOUTFS_IOC_SETATTR_MORE _IOW(SCOUTFS_IOCTL_MAGIC, 7, \ struct scoutfs_ioctl_setattr_more) struct scoutfs_ioctl_listxattr_hidden { @@ -313,7 +291,7 @@ struct scoutfs_ioctl_listxattr_hidden { __u32 hash_pos; }; -#define SCOUTFS_IOC_LISTXATTR_HIDDEN _IOR(SCOUTFS_IOCTL_MAGIC, 9, \ +#define SCOUTFS_IOC_LISTXATTR_HIDDEN _IOR(SCOUTFS_IOCTL_MAGIC, 8, \ struct scoutfs_ioctl_listxattr_hidden) /* @@ -344,7 +322,7 @@ struct scoutfs_ioctl_find_xattrs { __u8 _pad[4]; }; -#define SCOUTFS_IOC_FIND_XATTRS _IOR(SCOUTFS_IOCTL_MAGIC, 10, \ +#define SCOUTFS_IOC_FIND_XATTRS _IOR(SCOUTFS_IOCTL_MAGIC, 9, \ struct scoutfs_ioctl_find_xattrs) /* @@ -365,7 +343,7 @@ struct scoutfs_ioctl_statfs_more { __u64 rid; } __packed; -#define SCOUTFS_IOC_STATFS_MORE _IOR(SCOUTFS_IOCTL_MAGIC, 11, \ +#define SCOUTFS_IOC_STATFS_MORE _IOR(SCOUTFS_IOCTL_MAGIC, 10, \ struct scoutfs_ioctl_statfs_more) diff --git a/utils/src/item-cache-keys.c b/utils/src/item-cache-keys.c deleted file mode 100644 index a7577cb3..00000000 --- a/utils/src/item-cache-keys.c +++ /dev/null @@ -1,92 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "sparse.h" -#include "util.h" -#include "format.h" -#include "ioctl.h" -#include "cmd.h" -#include "key.h" - -static int item_cache_keys(int argc, char **argv, int which) -{ - struct scoutfs_ioctl_item_cache_keys ick; - struct scoutfs_ioctl_key ikeys[32]; - struct scoutfs_key key; - int ret; - int fd; - int i; - - if (argc != 2) { - fprintf(stderr, "too many arguments, only scoutfs path needed"); - return -EINVAL; - } - - fd = open(argv[1], O_RDONLY); - if (fd < 0) { - ret = -errno; - fprintf(stderr, "failed to open '%s': %s (%d)\n", - argv[1], strerror(errno), errno); - return ret; - } - - memset(&ick, 0, sizeof(ick)); - ick.buf_ptr = (unsigned long)ikeys; - ick.buf_nr = array_size(ikeys); - ick.which = which; - - for (;;) { - ret = ioctl(fd, SCOUTFS_IOC_ITEM_CACHE_KEYS, &ick); - if (ret < 0) { - ret = -errno; - fprintf(stderr, "walk_inodes ioctl failed: %s (%d)\n", - strerror(errno), errno); - break; - } else if (ret == 0) { - break; - } - - for (i = 0; i < ret; i++) { - scoutfs_key_copy_types(&key, &ikeys[i]); - printf(SK_FMT, SK_ARG(&key)); - - if (which == SCOUTFS_IOC_ITEM_CACHE_KEYS_ITEMS || - (i & 1)) - printf("\n"); - else - printf(" - "); - } - - scoutfs_key_inc(&key); - scoutfs_key_copy_types(&ick.ikey, &key); - } - - close(fd); - return ret; -}; - -static int item_keys(int argc, char **argv) -{ - return item_cache_keys(argc, argv, SCOUTFS_IOC_ITEM_CACHE_KEYS_ITEMS); -} - -static int range_keys(int argc, char **argv) -{ - return item_cache_keys(argc, argv, SCOUTFS_IOC_ITEM_CACHE_KEYS_RANGES); -} - -static void __attribute__((constructor)) item_cache_key_ctor(void) -{ - cmd_register("item-cache-keys", "", - "print range of indexed inodes", item_keys); - cmd_register("item-cache-range-keys", "", - "print range of indexed inodes", range_keys); -} diff --git a/utils/src/mkfs.c b/utils/src/mkfs.c index 7a28f9ef..54d26abf 100644 --- a/utils/src/mkfs.c +++ b/utils/src/mkfs.c @@ -25,6 +25,7 @@ #include "rand.h" #include "dev.h" #include "key.h" +#include "bitops.h" static int write_raw_block(int fd, u64 blkno, void *blk) { @@ -54,80 +55,6 @@ static int write_block(int fd, u64 blkno, struct scoutfs_super_block *super, return write_raw_block(fd, blkno, hdr); } -/* - * Calculate the greatest number of btree blocks that might be needed to - * store the given item population. At most all blocks will be half - * full. All keys will be the max size including parent items which - * determines the fanout. - * - * We will never hit this in practice. But some joker *could* fill a - * filesystem with empty files with enormous file names. - */ -static u64 calc_btree_blocks(u64 nr, u64 max_key, u64 max_val) -{ - u64 item_bytes; - u64 fanout; - u64 block_items; - u64 leaf_blocks; - u64 level_blocks; - u64 total_blocks; - - /* figure out the parent fanout for these silly huge possible items */ - item_bytes = sizeof(struct scoutfs_btree_item_header) + - sizeof(struct scoutfs_btree_item) + - max_key + sizeof(struct scoutfs_btree_ref); - fanout = ((SCOUTFS_BLOCK_SIZE - sizeof(struct scoutfs_btree_block) - - SCOUTFS_BTREE_PARENT_MIN_FREE_BYTES) / 2) / item_bytes; - - /* figure out how many items we have to store */ - item_bytes = sizeof(struct scoutfs_btree_item_header) + - sizeof(struct scoutfs_btree_item) + - max_key + max_val; - block_items = ((SCOUTFS_BLOCK_SIZE - - sizeof(struct scoutfs_btree_block)) / 2) / item_bytes; - leaf_blocks = DIV_ROUND_UP(nr, block_items); - - /* then calc total blocks as we grow to have enough blocks for items */ - level_blocks = 1; - total_blocks = level_blocks; - while (level_blocks < leaf_blocks) { - level_blocks *= fanout; - level_blocks = min(leaf_blocks, level_blocks); - total_blocks += level_blocks; - } - - return total_blocks; -} - -/* - * Figure out how many btree ring blocks we'll need for all the btree - * 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; - - /* key is smaller for wider parent fanout */ - assert(sizeof(struct scoutfs_extent_btree_key) <= - sizeof(struct scoutfs_manifest_btree_key)); - - /* 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)); - - return round_up(blocks * 4, SCOUTFS_SEGMENT_BLOCKS); -} - static float size_flt(u64 nr, unsigned size) { float x = (float)nr * (float)size; @@ -166,28 +93,22 @@ static char *size_str(u64 nr, unsigned size) static int write_new_fs(char *path, int fd, u8 quorum_count) { struct scoutfs_super_block *super; - struct scoutfs_key *ino_key; - struct scoutfs_key *idx_key; + struct scoutfs_key_be *kbe; struct scoutfs_inode *inode; - 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; + struct scoutfs_balloc_item_key *bik; + struct scoutfs_balloc_item_val *biv; struct scoutfs_key key; - __le32 *prev_link; struct timeval tv; char uuid_str[37]; void *zeros; u64 blkno; u64 limit; u64 size; - u64 ring_blocks; - u64 total_segs; u64 total_blocks; - u64 first_segno; + u64 free_blkno; u64 free_start; u64 free_len; int ret; @@ -197,9 +118,8 @@ static int write_new_fs(char *path, int fd, u8 quorum_count) super = calloc(1, SCOUTFS_BLOCK_SIZE); bt = calloc(1, SCOUTFS_BLOCK_SIZE); - sblk = calloc(1, SCOUTFS_SEGMENT_SIZE); - zeros = calloc(1, SCOUTFS_SEGMENT_SIZE); - if (!super || !bt || !sblk || !zeros) { + zeros = calloc(1, SCOUTFS_BLOCK_SIZE); + if (!super || !bt || !zeros) { ret = -errno; fprintf(stderr, "failed to allocate block mem: %s (%d)\n", strerror(errno), errno); @@ -213,15 +133,14 @@ static int write_new_fs(char *path, int fd, u8 quorum_count) goto out; } - /* arbitrarily require space for a handful of segments */ - limit = SCOUTFS_SEGMENT_SIZE * 16; + /* arbitrarily require a reasonably large device */ + limit = 8ULL * (1024 * 1024 * 1024); if (size < limit) { fprintf(stderr, "%llu byte device too small for min %llu byte fs\n", size, limit); goto out; } - total_segs = size / SCOUTFS_SEGMENT_SIZE; total_blocks = size / SCOUTFS_BLOCK_SIZE; /* partially initialize the super so we can use it to init others */ @@ -234,25 +153,21 @@ static int write_new_fs(char *path, int fd, u8 quorum_count) super->next_ino = cpu_to_le64(SCOUTFS_ROOT_INO + 1); super->next_trans_seq = cpu_to_le64(1); super->total_blocks = cpu_to_le64(total_blocks); - super->next_seg_seq = cpu_to_le64(2); - super->next_compact_id = cpu_to_le64(1); super->quorum_count = quorum_count; - /* align the btree ring to the segment after the super */ - blkno = round_up(SCOUTFS_SUPER_BLKNO + 1, SCOUTFS_SEGMENT_BLOCKS); - /* 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); + /* metadata blocks start after the quorum blocks */ + free_blkno = SCOUTFS_QUORUM_BLKNO + SCOUTFS_QUORUM_BLOCKS; + + /* extents start after btree blocks */ + free_start = total_blocks - (total_blocks / 4); free_len = total_blocks - free_start; + /* fill out some alloc boundaries before using */ 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(2); - super->bring.next_seq = cpu_to_le64(2); - /* allocator btree has item with space after first segno */ + /* extent allocator btree indexes free data extent */ + blkno = free_blkno++; + super->alloc_root.ref.blkno = cpu_to_le64(blkno); super->alloc_root.ref.seq = cpu_to_le64(1); super->alloc_root.height = 1; @@ -261,14 +176,13 @@ static int write_new_fs(char *path, int fd, u8 quorum_count) bt->hdr.fsid = super->hdr.fsid; bt->hdr.blkno = cpu_to_le64(blkno); bt->hdr.seq = cpu_to_le64(1); - bt->nr_items = cpu_to_le16(2); + bt->nr_items = cpu_to_le32(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; + bt->item_hdrs[0].off = cpu_to_le32((long)btitem - (long)bt); btitem->key_len = cpu_to_le16(sizeof(*ebk)); btitem->val_len = cpu_to_le16(0); @@ -279,8 +193,7 @@ static int write_new_fs(char *path, int fd, u8 quorum_count) 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; + bt->item_hdrs[1].off = cpu_to_le32((long)btitem - (long)bt); btitem->key_len = cpu_to_le16(sizeof(*ebk)); btitem->val_len = cpu_to_le16(0); @@ -288,6 +201,8 @@ static int write_new_fs(char *path, int fd, u8 quorum_count) ebk->major = cpu_to_be64(free_len); ebk->minor = cpu_to_be64(free_start + free_len - 1); + bt->free_end = bt->item_hdrs[le32_to_cpu(bt->nr_items) - 1].off; + bt->hdr.magic = cpu_to_le32(SCOUTFS_BLOCK_MAGIC_BTREE); bt->hdr.crc = cpu_to_le32(crc_block(&bt->hdr)); @@ -296,85 +211,46 @@ static int write_new_fs(char *path, int fd, u8 quorum_count) goto out; blkno++; - /* manifest btree has a block with an item for the segment */ - super->manifest.root.ref.blkno = cpu_to_le64(blkno); - super->manifest.root.ref.seq = cpu_to_le64(1); - super->manifest.root.height = 1; - super->manifest.level_counts[1] = cpu_to_le64(1); + /* fs root starts with root inode and its index items */ + blkno = free_blkno++; + + super->fs_root.ref.blkno = cpu_to_le64(blkno); + super->fs_root.ref.seq = cpu_to_le64(1); + super->fs_root.height = 1; memset(bt, 0, SCOUTFS_BLOCK_SIZE); bt->hdr.fsid = super->hdr.fsid; bt->hdr.blkno = cpu_to_le64(blkno); bt->hdr.seq = cpu_to_le64(1); - bt->nr_items = cpu_to_le16(1); + bt->nr_items = cpu_to_le32(2); /* btree item allocated from the back of the block */ - mval = (void *)bt + SCOUTFS_BLOCK_SIZE - sizeof(*mval); - ino_key = &mval->last_key; - mkey = (void *)mval - sizeof(*mkey); - btitem = (void *)mkey - sizeof(*btitem); + kbe = (void *)bt + SCOUTFS_BLOCK_SIZE - sizeof(*kbe); + btitem = (void *)kbe - sizeof(*btitem); - bt->item_hdrs[0].off = cpu_to_le16((long)btitem - (long)bt); - bt->free_end = bt->item_hdrs[0].off; + bt->item_hdrs[0].off = cpu_to_le32((long)btitem - (long)bt); + btitem->key_len = cpu_to_le16(sizeof(*kbe)); + btitem->val_len = cpu_to_le16(0); - btitem->key_len = cpu_to_le16(sizeof(*mkey)); - btitem->val_len = cpu_to_le16(sizeof(*mval)); - - mkey->level = 1; - mkey->seq = cpu_to_be64(1); memset(&key, 0, sizeof(key)); key.sk_zone = SCOUTFS_INODE_INDEX_ZONE; key.sk_type = SCOUTFS_INODE_INDEX_META_SEQ_TYPE; key.skii_ino = cpu_to_le64(SCOUTFS_ROOT_INO); - scoutfs_key_to_be(&mkey->first_key, &key); + scoutfs_key_to_be(kbe, &key); - mval->segno = cpu_to_le64(first_segno); - ino_key->sk_zone = SCOUTFS_FS_ZONE; - ino_key->ski_ino = cpu_to_le64(SCOUTFS_ROOT_INO); - ino_key->sk_type = SCOUTFS_INODE_TYPE; + inode = (void *)btitem - sizeof(*inode); + kbe = (void *)inode - sizeof(*kbe); + btitem = (void *)kbe - sizeof(*btitem); - bt->hdr.magic = cpu_to_le32(SCOUTFS_BLOCK_MAGIC_BTREE); - bt->hdr.crc = cpu_to_le32(crc_block(&bt->hdr)); + bt->item_hdrs[1].off = cpu_to_le32((long)btitem - (long)bt); + btitem->key_len = cpu_to_le16(sizeof(*kbe)); + btitem->val_len = cpu_to_le16(sizeof(*inode)); - ret = write_raw_block(fd, blkno, bt); - if (ret) - goto out; - blkno += ring_blocks; - - /* write seg with root inode */ - sblk->segno = cpu_to_le64(first_segno); - sblk->seq = cpu_to_le64(1); - prev_link = &sblk->skip_links[0]; - - item = (void *)(sblk + 1); - *prev_link = cpu_to_le32((long)item -(long)sblk); - prev_link = &item->skip_links[0]; - - item->val_len = 0; - item->nr_links = 1; - le32_add_cpu(&sblk->nr_items, 1); - - idx_key = &item->key; - idx_key->sk_zone = SCOUTFS_INODE_INDEX_ZONE; - idx_key->sk_type = SCOUTFS_INODE_INDEX_META_SEQ_TYPE; - idx_key->skii_ino = cpu_to_le64(SCOUTFS_ROOT_INO); - - item = (void *)&item->skip_links[1]; - *prev_link = cpu_to_le32((long)item -(long)sblk); - prev_link = &item->skip_links[0]; - - sblk->last_item_off = cpu_to_le32((long)item - (long)sblk); - - ino_key = (void *)&item->key; - inode = (void *)&item->skip_links[1]; - - item->val_len = cpu_to_le16(sizeof(struct scoutfs_inode)); - item->nr_links = 1; - le32_add_cpu(&sblk->nr_items, 1); - - ino_key->sk_zone = SCOUTFS_FS_ZONE; - ino_key->ski_ino = cpu_to_le64(SCOUTFS_ROOT_INO); - ino_key->sk_type = SCOUTFS_INODE_TYPE; + memset(&key, 0, sizeof(key)); + key.sk_zone = SCOUTFS_FS_ZONE; + key.ski_ino = cpu_to_le64(SCOUTFS_ROOT_INO); + key.sk_type = SCOUTFS_INODE_TYPE; + scoutfs_key_to_be(kbe, &key); inode->next_readdir_pos = cpu_to_le64(2); inode->nlink = cpu_to_le32(SCOUTFS_DIRENT_FIRST_POS); @@ -386,16 +262,55 @@ static int write_new_fs(char *path, int fd, u8 quorum_count) inode->mtime.sec = inode->atime.sec; inode->mtime.nsec = inode->atime.nsec; - item = (void *)(inode + 1); - sblk->total_bytes = cpu_to_le32((long)item - (long)sblk); - sblk->crc = cpu_to_le32(crc_segment(sblk)); + bt->free_end = bt->item_hdrs[le32_to_cpu(bt->nr_items) - 1].off; - ret = pwrite(fd, sblk, SCOUTFS_SEGMENT_SIZE, - first_segno << SCOUTFS_SEGMENT_SHIFT); - if (ret != SCOUTFS_SEGMENT_SIZE) { - ret = -EIO; + bt->hdr.magic = cpu_to_le32(SCOUTFS_BLOCK_MAGIC_BTREE); + bt->hdr.crc = cpu_to_le32(crc_block(&bt->hdr)); + + ret = write_raw_block(fd, blkno, bt); + if (ret) + goto out; + + /* metadata block allocator has single item, server continues init */ + blkno = free_blkno++; + + super->core_balloc_alloc.root.ref.blkno = cpu_to_le64(blkno); + super->core_balloc_alloc.root.ref.seq = cpu_to_le64(1); + super->core_balloc_alloc.root.height = 1; + + /* XXX magic */ + + memset(bt, 0, SCOUTFS_BLOCK_SIZE); + bt->hdr.fsid = super->hdr.fsid; + bt->hdr.blkno = cpu_to_le64(blkno); + bt->hdr.seq = cpu_to_le64(1); + bt->nr_items = cpu_to_le32(1); + + /* btree item allocated from the back of the block */ + biv = (void *)bt + SCOUTFS_BLOCK_SIZE - sizeof(*biv); + bik = (void *)biv - sizeof(*bik); + btitem = (void *)bik - sizeof(*btitem); + + bt->item_hdrs[0].off = cpu_to_le32((long)btitem - (long)bt); + btitem->key_len = cpu_to_le16(sizeof(*bik)); + btitem->val_len = cpu_to_le16(sizeof(*biv)); + + bik->base = cpu_to_be64(0); /* XXX true? */ + + /* set all the bits past our final used blkno */ + super->core_balloc_free.total_free = + cpu_to_le64(SCOUTFS_BALLOC_ITEM_BITS - free_blkno); + for (i = free_blkno; i < SCOUTFS_BALLOC_ITEM_BITS; i++) + set_bit_le(i, &biv->bits); + + bt->free_end = bt->item_hdrs[le32_to_cpu(bt->nr_items) - 1].off; + + bt->hdr.magic = cpu_to_le32(SCOUTFS_BLOCK_MAGIC_BTREE); + bt->hdr.crc = cpu_to_le32(crc_block(&bt->hdr)); + + ret = write_raw_block(fd, blkno, bt); + if (ret) goto out; - } /* zero out quorum blocks */ for (i = 0; i < SCOUTFS_QUORUM_BLOCKS; i++) { @@ -407,6 +322,8 @@ static int write_new_fs(char *path, int fd, u8 quorum_count) } } + super->next_uninit_free_block = cpu_to_le64(SCOUTFS_BALLOC_ITEM_BITS); + /* write the super block */ super->hdr.seq = cpu_to_le64(1); ret = write_block(fd, SCOUTFS_SUPER_BLKNO, NULL, &super->hdr); @@ -423,22 +340,21 @@ static int write_new_fs(char *path, int fd, u8 quorum_count) uuid_unparse(super->uuid, uuid_str); printf("Created scoutfs filesystem:\n" - " device path: %s\n" - " fsid: %llx\n" - " format hash: %llx\n" - " uuid: %s\n" - " device bytes: "SIZE_FMT"\n" - " device blocks: "SIZE_FMT"\n" - " btree ring blocks: "SIZE_FMT"\n" - " free blocks: "SIZE_FMT"\n" - " quorum count: %u\n", + " device path: %s\n" + " fsid: %llx\n" + " format hash: %llx\n" + " uuid: %s\n" + " device blocks: "SIZE_FMT"\n" + " metadata blocks: "SIZE_FMT"\n" + " file extent blocks: "SIZE_FMT"\n" + " quorum count: %u\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), + SIZE_ARGS(le64_to_cpu(super->total_blocks) - + le64_to_cpu(super->free_blocks), SCOUTFS_BLOCK_SIZE), SIZE_ARGS(le64_to_cpu(super->free_blocks), SCOUTFS_BLOCK_SIZE), @@ -450,8 +366,6 @@ out: free(super); if (bt) free(bt); - if (sblk) - free(sblk); if (zeros) free(zeros); return ret; diff --git a/utils/src/print.c b/utils/src/print.c index 5e714b09..eac87402 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -41,27 +41,6 @@ static void *read_block(int fd, u64 blkno) return buf; } -static void *read_segment(int fd, u64 segno) -{ - ssize_t ret; - void *buf; - - buf = malloc(SCOUTFS_SEGMENT_SIZE); - if (!buf) - return NULL; - - ret = pread(fd, buf, SCOUTFS_SEGMENT_SIZE, - segno << SCOUTFS_SEGMENT_SHIFT); - if (ret != SCOUTFS_SEGMENT_SIZE) { - fprintf(stderr, "read segno %llu returned %zd: %s (%d)\n", - segno, ret, strerror(errno), errno); - free(buf); - buf = NULL; - } - - return buf; -} - static void print_block_header(struct scoutfs_block_header *hdr) { u32 crc = crc_block(hdr); @@ -240,93 +219,92 @@ static print_func_t find_printer(u8 zone, u8 type) return NULL; } -static void print_item(struct scoutfs_segment_block *sblk, - struct scoutfs_segment_item *item, u32 which, u32 off) +static int print_fs_item(void *key, unsigned key_len, void *val, + unsigned val_len, void *arg) { + struct scoutfs_key item_key; print_func_t printer; - void *val; - int i; - val = (char *)&item->skip_links[item->nr_links]; + scoutfs_key_from_be(&item_key, key); - printer = find_printer(item->key.sk_zone, item->key.sk_type); + printf(" "SK_FMT"\n", SK_ARG(&item_key)); - printf(" [%u]: key "SK_FMT" off %u val_len %u nr_links %u flags %x%s\n", - which, SK_ARG(&item->key), off, le16_to_cpu(item->val_len), - item->nr_links, - item->flags, printer ? "" : " (unrecognized zone+type)"); - printf(" links:"); - for (i = 0; i < item->nr_links; i++) - printf(" %u", le32_to_cpu(item->skip_links[i])); - printf("\n"); - - if (printer) - printer(&item->key, val, le16_to_cpu(item->val_len)); -} - -static void print_segment_block(struct scoutfs_segment_block *sblk) -{ - int i; - - printf(" sblk: segno %llu seq %llu last_item_off %u total_bytes %u " - "nr_items %u\n", - le64_to_cpu(sblk->segno), le64_to_cpu(sblk->seq), - le32_to_cpu(sblk->last_item_off), le32_to_cpu(sblk->total_bytes), - le32_to_cpu(sblk->nr_items)); - printf(" links:"); - for (i = 0; sblk->skip_links[i]; i++) - printf(" %u", le32_to_cpu(sblk->skip_links[i])); - printf("\n"); -} - -static int print_segments(int fd, unsigned long *seg_map, u64 total) -{ - struct scoutfs_segment_block *sblk; - struct scoutfs_segment_item *item; - u32 off; - u64 s; - u64 i; - - for (s = 0; (s = find_next_set_bit(seg_map, s, total)) < total; s++) { - sblk = read_segment(fd, s); - if (!sblk) - return -ENOMEM; - - printf("segment segno %llu\n", s); - print_segment_block(sblk); - - off = le32_to_cpu(sblk->skip_links[0]); - for (i = 0; i < le32_to_cpu(sblk->nr_items); i++) { - item = (void *)sblk + off; - print_item(sblk, item, i, off); - off = le32_to_cpu(item->skip_links[0]); - } - - free(sblk); + /* only items in leaf blocks have values */ + if (val) { + printer = find_printer(item_key.sk_zone, item_key.sk_type); + if (printer) + printer(&item_key, val, val_len); + else + printf(" (unknown zone %u type %u)\n", + item_key.sk_zone, item_key.sk_type); } return 0; } -static int print_manifest_entry(void *key, unsigned key_len, void *val, - unsigned val_len, void *arg) +/* same as fs item but with a small header in the value */ +static int print_logs_item(void *key, unsigned key_len, void *val, + unsigned val_len, void *arg) { - struct scoutfs_manifest_btree_key *mkey = key; - struct scoutfs_manifest_btree_val *mval = val; - struct scoutfs_key first; - unsigned long *seg_map = arg; + struct scoutfs_key item_key; + struct scoutfs_log_item_value *liv; + print_func_t printer; - scoutfs_key_from_be(&first, &mkey->first_key); + scoutfs_key_from_be(&item_key, key); - printf(" level %u first "SK_FMT" seq %llu\n", - mkey->level, SK_ARG(&first), be64_to_cpu(mkey->seq)); + printf(" "SK_FMT"\n", SK_ARG(&item_key)); /* only items in leaf blocks have values */ if (val) { - printf(" segno %llu last "SK_FMT"\n", - le64_to_cpu(mval->segno), SK_ARG(&mval->last_key)); + liv = val; + printf(" log_item_value: vers %llu flags %x\n", + le64_to_cpu(liv->vers), liv->flags); - set_bit(seg_map, le64_to_cpu(mval->segno)); + /* deletion items don't have values */ + if (!(liv->flags & SCOUTFS_LOG_ITEM_FLAG_DELETION)) { + printer = find_printer(item_key.sk_zone, + item_key.sk_type); + if (printer) + printer(&item_key, val + sizeof(*liv), + val_len - sizeof(*liv)); + else + printf(" (unknown zone %u type %u)\n", + item_key.sk_zone, item_key.sk_type); + } + } + + return 0; +} + +/* same as fs item but with a small header in the value */ +static int print_log_trees_item(void *key, unsigned key_len, void *val, + unsigned val_len, void *arg) +{ + struct scoutfs_log_trees_key *ltk = key; + struct scoutfs_log_trees_val *ltv = val; + + printf(" rid %llu nr %llu\n", + be64_to_cpu(ltk->rid), be64_to_cpu(ltk->nr)); + + /* only items in leaf blocks have values */ + if (val) { + printf(" alloc_root: total_free %llu root: height %u blkno %llu seq %llu\n" + " free_root: total_free %llu root: height %u blkno %llu seq %llu\n" + " item_root: height %u blkno %llu seq %llu\n" + " bloom_ref: blkno %llu seq %llu\n", + le64_to_cpu(ltv->alloc_root.total_free), + ltv->alloc_root.root.height, + le64_to_cpu(ltv->alloc_root.root.ref.blkno), + le64_to_cpu(ltv->alloc_root.root.ref.seq), + le64_to_cpu(ltv->free_root.total_free), + ltv->free_root.root.height, + le64_to_cpu(ltv->free_root.root.ref.blkno), + le64_to_cpu(ltv->free_root.root.ref.seq), + ltv->item_root.height, + le64_to_cpu(ltv->item_root.ref.blkno), + le64_to_cpu(ltv->item_root.ref.seq), + le64_to_cpu(ltv->bloom_ref.blkno), + le64_to_cpu(ltv->bloom_ref.seq)); } return 0; @@ -375,7 +353,18 @@ static int print_trans_seqs_entry(void *key, unsigned key_len, void *val, return 0; } -/* XXX should make sure that the val is null terminated */ +static int print_balloc_entry(void *key, unsigned key_len, void *val, + unsigned val_len, void *arg) +{ + struct scoutfs_balloc_item_key *bik = key; +// struct scoutfs_balloc_item_val *biv = val; + + printf(" base %llu\n", + be64_to_cpu(bik->base)); + + return 0; +} + static int print_mounted_client_entry(void *key, unsigned key_len, void *val, unsigned val_len, void *arg) { @@ -423,20 +412,19 @@ static int print_btree_block(int fd, struct scoutfs_super_block *super, if (bt->level == level) { printf("%s btree blkno %llu\n" " crc %08x fsid %llx seq %llu blkno %llu \n" - " level %u free_end %u free_reclaim %u nr_items %u\n", + " level %u free_end %u nr_items %u\n", which, le64_to_cpu(ref->blkno), le32_to_cpu(bt->hdr.crc), le64_to_cpu(bt->hdr.fsid), le64_to_cpu(bt->hdr.seq), le64_to_cpu(bt->hdr.blkno), bt->level, - le16_to_cpu(bt->free_end), - le16_to_cpu(bt->free_reclaim), - le16_to_cpu(bt->nr_items)); + le32_to_cpu(bt->free_end), + le32_to_cpu(bt->nr_items)); } - for (i = 0; i < le16_to_cpu(bt->nr_items); i++) { - item = (void *)bt + le16_to_cpu(bt->item_hdrs[i].off); + for (i = 0; i < le32_to_cpu(bt->nr_items); i++) { + item = (void *)bt + le32_to_cpu(bt->item_hdrs[i].off); key_len = le16_to_cpu(item->key_len); val_len = le16_to_cpu(item->val_len); key = (void *)(item + 1); @@ -455,7 +443,7 @@ static int print_btree_block(int fd, struct scoutfs_super_block *super, } printf(" item [%u] off %u key_len %u val_len %u\n", - i, le16_to_cpu(bt->item_hdrs[i].off), key_len, val_len); + i, le32_to_cpu(bt->item_hdrs[i].off), key_len, val_len); if (level) print_btree_ref(key, key_len, val, val_len, func, arg); @@ -489,6 +477,98 @@ static int print_btree(int fd, struct scoutfs_super_block *super, char *which, return ret; } +struct print_recursion_args { + struct scoutfs_super_block *super; + int fd; +}; + +/* same as fs item but with a small header in the value */ +static int print_log_trees_roots(void *key, unsigned key_len, void *val, + unsigned val_len, void *arg) +{ + struct scoutfs_log_trees_key *ltk = key; + struct scoutfs_log_trees_val *ltv = val; + struct print_recursion_args *pa = arg; + struct log_trees_roots { + char *fmt; + struct scoutfs_btree_root *root; + print_item_func func; + } roots[] = { + { "log_tree_rid:%llu_nr:%llu_alloc", + <v->alloc_root.root, + print_balloc_entry, + }, + { "log_tree_rid:%llu_nr:%llu_free", + <v->free_root.root, + print_balloc_entry, + }, + { "log_tree_rid:%llu_nr:%llu_item", + <v->item_root, + print_logs_item, + }, + }; + char which[100]; + int ret; + int err; + int i; + + /* XXX doesn't print the bloom block */ + + ret = 0; + for (i = 0; i < array_size(roots); i++) { + snprintf(which, sizeof(which) - 1, roots[i].fmt, + be64_to_cpu(ltk->rid), be64_to_cpu(ltk->nr)); + + err = print_btree(pa->fd, pa->super, which, roots[i].root, + roots[i].func, NULL); + if (err && !ret) + ret = err; + } + + return ret; +} + +static int print_btree_leaf_items(int fd, struct scoutfs_super_block *super, + struct scoutfs_btree_ref *ref, + print_item_func func, void *arg) +{ + struct scoutfs_btree_item *item; + struct scoutfs_btree_block *bt; + unsigned key_len; + unsigned val_len; + void *key; + void *val; + int ret; + int i; + + if (ref->blkno == 0) + return 0; + + bt = read_block(fd, le64_to_cpu(ref->blkno)); + if (!bt) + return -ENOMEM; + + for (i = 0; i < le32_to_cpu(bt->nr_items); i++) { + item = (void *)bt + le32_to_cpu(bt->item_hdrs[i].off); + key_len = le16_to_cpu(item->key_len); + val_len = le16_to_cpu(item->val_len); + key = (void *)(item + 1); + val = (void *)key + key_len; + + if (bt->level > 0) { + ret = print_btree_leaf_items(fd, super, val, func, arg); + if (ret) + break; + continue; + } else { + func(key, key_len, val, val_len, arg); + } + } + + free(bt); + return 0; +} + static char *alloc_addr_str(struct scoutfs_inet_addr *ia) { struct in_addr addr; @@ -572,8 +652,6 @@ static void print_super_block(struct scoutfs_super_block *super, u64 blkno) { char uuid_str[37]; char *server_addr; - u64 count; - int i; uuid_unparse(super->uuid, uuid_str); @@ -587,62 +665,52 @@ static void print_super_block(struct scoutfs_super_block *super, u64 blkno) return; /* XXX these are all in a crazy order */ - printf(" next_ino %llu next_trans_seq %llu next_seg_seq %llu\n" - " next_compact_id %llu\n" - " total_blocks %llu free_blocks %llu alloc_cursor %llu\n" + printf(" next_ino %llu next_trans_seq %llu\n" + " total_blocks %llu free_blocks %llu\n" + " next_uninit_free_block %llu core_balloc_blocks %llu\n" " quorum_fenced_term %llu quorum_server_term %llu unmount_barrier %llu\n" " quorum_count %u server_addr %s\n" - " btree ring: first_blkno %llu nr_blocks %llu next_block %llu " - "next_seq %llu\n" - " lock_clients root: height %u blkno %llu seq %llu mig_len %u\n" - " mounted_clients root: height %u blkno %llu seq %llu mig_len %u\n" - " trans_seqs root: height %u blkno %llu seq %llu mig_len %u\n" - " alloc btree root: height %u blkno %llu seq %llu mig_len %u\n" - " manifest btree root: height %u blkno %llu seq %llu mig_len %u\n", + " core_balloc_alloc: total_free %llu root: height %u blkno %llu seq %llu\n" + " core_balloc_free: total_free %llu root: height %u blkno %llu seq %llu\n" + " lock_clients root: height %u blkno %llu seq %llu\n" + " mounted_clients root: height %u blkno %llu seq %llu\n" + " trans_seqs root: height %u blkno %llu seq %llu\n" + " alloc btree root: height %u blkno %llu seq %llu\n" + " fs_root btree root: height %u blkno %llu seq %llu\n", le64_to_cpu(super->next_ino), le64_to_cpu(super->next_trans_seq), - le64_to_cpu(super->next_seg_seq), - le64_to_cpu(super->next_compact_id), le64_to_cpu(super->total_blocks), le64_to_cpu(super->free_blocks), - le64_to_cpu(super->alloc_cursor), + le64_to_cpu(super->next_uninit_free_block), + le64_to_cpu(super->core_balloc_cursor), le64_to_cpu(super->quorum_fenced_term), le64_to_cpu(super->quorum_server_term), le64_to_cpu(super->unmount_barrier), super->quorum_count, server_addr, - le64_to_cpu(super->bring.first_blkno), - le64_to_cpu(super->bring.nr_blocks), - le64_to_cpu(super->bring.next_block), - le64_to_cpu(super->bring.next_seq), + le64_to_cpu(super->core_balloc_alloc.total_free), + super->core_balloc_alloc.root.height, + le64_to_cpu(super->core_balloc_alloc.root.ref.blkno), + le64_to_cpu(super->core_balloc_alloc.root.ref.seq), + le64_to_cpu(super->core_balloc_free.total_free), + super->core_balloc_free.root.height, + le64_to_cpu(super->core_balloc_free.root.ref.blkno), + le64_to_cpu(super->core_balloc_free.root.ref.seq), super->lock_clients.height, le64_to_cpu(super->lock_clients.ref.blkno), le64_to_cpu(super->lock_clients.ref.seq), - le16_to_cpu(super->lock_clients.migration_key_len), super->mounted_clients.height, le64_to_cpu(super->mounted_clients.ref.blkno), le64_to_cpu(super->mounted_clients.ref.seq), - le16_to_cpu(super->mounted_clients.migration_key_len), super->trans_seqs.height, le64_to_cpu(super->trans_seqs.ref.blkno), le64_to_cpu(super->trans_seqs.ref.seq), - le16_to_cpu(super->trans_seqs.migration_key_len), super->alloc_root.height, le64_to_cpu(super->alloc_root.ref.blkno), le64_to_cpu(super->alloc_root.ref.seq), - le16_to_cpu(super->alloc_root.migration_key_len), - super->manifest.root.height, - le64_to_cpu(super->manifest.root.ref.blkno), - le64_to_cpu(super->manifest.root.ref.seq), - le16_to_cpu(super->manifest.root.migration_key_len)); - - printf(" level_counts:"); - for (i = 0; i < SCOUTFS_MANIFEST_MAX_LEVEL; i++) { - count = le64_to_cpu(super->manifest.level_counts[i]); - if (count) - printf(" %u: %llu", i, count); - } - printf("\n"); + super->fs_root.height, + le64_to_cpu(super->fs_root.ref.blkno), + le64_to_cpu(super->fs_root.ref.seq)); free(server_addr); } @@ -650,8 +718,7 @@ static void print_super_block(struct scoutfs_super_block *super, u64 blkno) static int print_volume(int fd) { struct scoutfs_super_block *super = NULL; - unsigned long *seg_map = NULL; - u64 nr_segs; + struct print_recursion_args pa; int ret = 0; int err; @@ -661,15 +728,6 @@ static int print_volume(int fd) print_super_block(super, SCOUTFS_SUPER_BLKNO); - 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", - nr_segs, strerror(errno), errno); - goto out; - } - ret = print_quorum_blocks(fd, super); err = print_btree(fd, super, "lock_clients", &super->lock_clients, @@ -687,23 +745,41 @@ static int print_volume(int fd) if (err && !ret) ret = err; + err = print_btree(fd, super, "core_balloc_alloc", + &super->core_balloc_alloc.root, + print_balloc_entry, NULL); + if (err && !ret) + ret = err; + + err = print_btree(fd, super, "core_balloc_free", + &super->core_balloc_free.root, + print_balloc_entry, NULL); + if (err && !ret) + ret = err; + err = print_btree(fd, super, "alloc", &super->alloc_root, print_alloc_item, NULL); if (err && !ret) ret = err; - err = print_btree(fd, super, "manifest", &super->manifest.root, - print_manifest_entry, seg_map); + err = print_btree(fd, super, "logs_root", &super->logs_root, + print_log_trees_item, NULL); if (err && !ret) ret = err; - err = print_segments(fd, seg_map, nr_segs); + pa.super = super; + pa.fd = fd; + err = print_btree_leaf_items(fd, super, &super->logs_root.ref, + print_log_trees_roots, &pa); + if (err && !ret) + ret = err; + + err = print_btree(fd, super, "fs_root", &super->fs_root, + print_fs_item, NULL); if (err && !ret) ret = err; -out: free(super); - free(seg_map); return ret; } From c87a9f3a07a627eaf409eabcb690a32165fe25a8 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Mon, 28 Oct 2019 14:24:47 -0700 Subject: [PATCH 183/235] scoutfs-utils: resurrect bitops We've had these in the past and we need them again for the block allocator item bitmaps. Signed-off-by: Zach Brown --- utils/src/bitops.h | 101 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 utils/src/bitops.h diff --git a/utils/src/bitops.h b/utils/src/bitops.h new file mode 100644 index 00000000..69605fed --- /dev/null +++ b/utils/src/bitops.h @@ -0,0 +1,101 @@ +#ifndef _BITOPS_H_ +#define _BITOPS_H_ + +#include "sparse.h" + +/* + * Implement little endian bitmaps in terms of native longs. __packed + * is used to avoid unaligned accesses. These are neither atomic nor + * particularly efficient. + */ + +#define BITS_PER_LONG (sizeof(long) * 8) +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define BITOP_LE_SWIZZLE 0 +#else +#define BITOP_LE_SWIZZLE ((BITS_PER_LONG-1) & ~0x7) +#endif + +static inline unsigned long get_nr_word(int nr, void *addr) +{ + unsigned long *longs = addr; + unsigned long ind = nr / BITS_PER_LONG; + unsigned long val; + + memcpy(&val, &longs[ind], sizeof(val)); + + return val; +} + +static inline void put_nr_word(int nr, void *addr, unsigned long val) +{ + unsigned long *longs = addr; + unsigned long ind = nr / BITS_PER_LONG; + + memcpy(&longs[ind], &val, sizeof(val)); +} + +static inline unsigned long nr_mask(int nr) +{ + return 1UL << (nr % BITS_PER_LONG); +} + +static inline int test_bit(int nr, void *addr) +{ + unsigned long val = get_nr_word(nr, addr); + + return !!(val & nr_mask(nr)); +} + +static inline void set_bit(int nr, void *addr) +{ + unsigned long val = get_nr_word(nr, addr); + + val |= nr_mask(nr); + put_nr_word(nr, addr, val); +} + +static inline void clear_bit(int nr, void *addr) +{ + unsigned long val = get_nr_word(nr, addr); + + val &= ~nr_mask(nr); + put_nr_word(nr, addr, val); +} + +static inline int test_bit_le(int nr, void *addr) +{ + return test_bit(nr ^ BITOP_LE_SWIZZLE, addr); +} + +static inline int test_and_set_bit_le(int nr, void *addr) +{ + int ret; + + nr ^= BITOP_LE_SWIZZLE; + ret = test_bit(nr, addr); + set_bit(nr, addr); + return ret; +} + +static inline void set_bit_le(int nr, void *addr) +{ + set_bit(nr ^ BITOP_LE_SWIZZLE, addr); +} + +static inline void clear_bit_le(int nr, void *addr) +{ + clear_bit(nr ^ BITOP_LE_SWIZZLE, addr); +} + +static inline int test_and_clear_bit_le(int nr, void *addr) +{ + int ret; + + nr ^= BITOP_LE_SWIZZLE; + ret = test_bit(nr, addr); + clear_bit(nr, addr); + return ret; +} + +#endif From e0a49c46a7f2346caee73c0ae1c78dce9938841e Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Tue, 10 Dec 2019 08:54:24 -0800 Subject: [PATCH 184/235] scoutfs-utils: add packed extents and bitmaps Signed-off-by: Zach Brown --- utils/src/format.h | 126 +++++++++++++++++++++++++++------------------ utils/src/key.c | 4 +- utils/src/mkfs.c | 90 +++++++++----------------------- utils/src/print.c | 126 ++++++++++++++++++++++++--------------------- 4 files changed, 168 insertions(+), 178 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index ad408c39..9b9fd06d 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -105,11 +105,6 @@ struct scoutfs_key { #define skxi_ino _sk_second #define skxi_id _sk_third -/* node free extent */ -#define sknf_rid _sk_first -#define sknf_major _sk_second -#define sknf_minor _sk_third - /* node orphan inode */ #define sko_rid _sk_first #define sko_ino _sk_second @@ -132,9 +127,10 @@ struct scoutfs_key { #define sks_ino _sk_first #define sks_nr _sk_second -/* file extent */ -#define skfe_ino _sk_first -#define skfe_last _sk_second +/* packed extents */ +#define skpe_ino _sk_first +#define skpe_base _sk_second +#define skpe_part _sk_fourth /* * The btree still uses memcmp() to compare keys. We should fix that @@ -149,9 +145,10 @@ struct scoutfs_key_be { __u8 _sk_fourth; }__packed; -/* chose reasonable max key and value lens that have room for some u64s */ +/* chose reasonable max key lens that have room for some u64s */ #define SCOUTFS_BTREE_MAX_KEY_LEN 40 -#define SCOUTFS_BTREE_MAX_VAL_LEN 256 +/* when we split we want to have multiple items on each side */ +#define SCOUTFS_BTREE_MAX_VAL_LEN (SCOUTFS_BLOCK_SIZE / 8) /* * The min number of free bytes we must leave in a parent as we descend @@ -234,18 +231,32 @@ struct scoutfs_balloc_item_val { } __packed; /* - * 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. + * Free data blocks are tracked in bitmaps stored in btree items. */ -struct scoutfs_extent_btree_key { +struct scoutfs_block_bitmap_key { __u8 type; - __be64 major; - __be64 minor; + __be64 base; } __packed; +#define SCOUTFS_BLOCK_BITMAP_BIG 0 +#define SCOUTFS_BLOCK_BITMAP_LITTLE 1 + +#define SCOUTFS_PACKED_BITMAP_WORDS 32 +#define SCOUTFS_PACKED_BITMAP_BITS (SCOUTFS_PACKED_BITMAP_WORDS * 64) +#define SCOUTFS_PACKED_BITMAP_MAX_BYTES \ + offsetof(struct scoutfs_packed_bitmap, \ + words[SCOUTFS_PACKED_BITMAP_WORDS]) + +#define SCOUTFS_BLOCK_BITMAP_BITS SCOUTFS_PACKED_BITMAP_BITS +#define SCOUTFS_BLOCK_BITMAP_BIT_MASK (SCOUTFS_PACKED_BITMAP_BITS - 1) +#define SCOUTFS_BLOCK_BITMAP_BASE_SHIFT (ilog2(SCOUTFS_PACKED_BITMAP_BITS)) + +struct scoutfs_packed_bitmap { + __le64 present; + __le64 set; + __le64 words[0]; +}; + /* * The lock server keeps a persistent record of connected clients so that * server failover knows who to wait for before resuming operations. @@ -276,11 +287,18 @@ struct scoutfs_mounted_client_btree_val { #define SCOUTFS_MOUNTED_CLIENT_VOTER (1 << 0) +/* + * XXX I imagine we should rename these now that they've evolved to track + * all the btrees that clients use during a transaction. It's not just + * about item logs, it's about clients making changes to trees. + */ struct scoutfs_log_trees { struct scoutfs_balloc_root alloc_root; struct scoutfs_balloc_root free_root; struct scoutfs_btree_root item_root; struct scoutfs_btree_ref bloom_ref; + struct scoutfs_balloc_root data_alloc; + struct scoutfs_balloc_root data_free; __le64 rid; __le64 nr; } __packed; @@ -295,6 +313,8 @@ struct scoutfs_log_trees_val { struct scoutfs_balloc_root free_root; struct scoutfs_btree_root item_root; struct scoutfs_btree_ref bloom_ref; + struct scoutfs_balloc_root data_alloc; + struct scoutfs_balloc_root data_free; } __packed; struct scoutfs_log_item_value { @@ -350,9 +370,7 @@ struct scoutfs_bloom_block { #define SCOUTFS_XATTR_INDEX_NAME_TYPE 1 /* rid zone (also used in server alloc btree) */ -#define SCOUTFS_FREE_EXTENT_BLKNO_TYPE 1 -#define SCOUTFS_FREE_EXTENT_BLOCKS_TYPE 2 -#define SCOUTFS_ORPHAN_TYPE 3 +#define SCOUTFS_ORPHAN_TYPE 1 /* fs zone */ #define SCOUTFS_INODE_TYPE 1 @@ -361,23 +379,45 @@ struct scoutfs_bloom_block { #define SCOUTFS_READDIR_TYPE 4 #define SCOUTFS_LINK_BACKREF_TYPE 5 #define SCOUTFS_SYMLINK_TYPE 6 -#define SCOUTFS_FILE_EXTENT_TYPE 7 +#define SCOUTFS_PACKED_EXTENT_TYPE 7 /* lock zone, only ever found in lock ranges, never in persistent items */ #define SCOUTFS_RENAME_TYPE 1 #define SCOUTFS_MAX_TYPE 8 /* power of 2 is efficient */ + /* - * File extents have more data than easily fits in the key so we move - * the non-indexed fields into the value. + * The extents that map blocks in a fixed-size logical region of a file + * are packed and stored in item values. The packed extents are + * contiguous so the starting logical block is implicit from the length + * of previous extents. Sparse regions are represented by 0 flags and + * blkno. The blkno of a packed extent is encoded as the zigzag (lsb is + * sign bit) difference from the last blkno of the previous extent. + * This guarantees that non-sparse extents must have a blkno delta of at + * least -1/1. High zero byte aren't stored. */ -struct scoutfs_file_extent { - __le64 blkno; - __le64 len; - __u8 flags; +struct scoutfs_packed_extent { + __le16 count; +#if defined(__LITTLE_ENDIAN_BITFIELD) + __u8 diff_bytes:4, + flags:3, + final:1; +#elif defined(__BIG_ENDIAN_BITFIELD) + __u8 final:1, + flags:3, + diff_bytes:4; +#else +#error "no {BIG,LITTLE}_ENDIAN_BITFIELD defined?" +#endif + __u8 le_blkno_diff[0]; } __packed; +#define SCOUTFS_PACKEXT_BLOCKS (8 * 1024 * 1024 / SCOUTFS_BLOCK_SIZE) +#define SCOUTFS_PACKEXT_BASE_SHIFT (ilog2(SCOUTFS_PACKEXT_BLOCKS)) +#define SCOUTFS_PACKEXT_BASE_MASK (~((__u64)SCOUTFS_PACKEXT_BLOCKS - 1)) +#define SCOUTFS_PACKEXT_MAX_BYTES SCOUTFS_MAX_VAL_SIZE + #define SEF_OFFLINE (1 << 0) #define SEF_UNWRITTEN (1 << 1) #define SEF_UNKNOWN (U8_MAX << 2) @@ -450,8 +490,12 @@ struct scoutfs_super_block { __le64 next_ino; __le64 next_trans_seq; __le64 total_blocks; - __le64 next_uninit_free_block; + __le64 next_uninit_meta_blkno; + __le64 last_uninit_meta_blkno; + __le64 next_uninit_data_blkno; + __le64 last_uninit_data_blkno; __le64 core_balloc_cursor; + __le64 core_data_alloc_cursor; __le64 free_blocks; __le64 first_fs_blkno; __le64 last_fs_blkno; @@ -462,7 +506,8 @@ struct scoutfs_super_block { struct scoutfs_inet_addr server_addr; struct scoutfs_balloc_root core_balloc_alloc; struct scoutfs_balloc_root core_balloc_free; - struct scoutfs_btree_root alloc_root; + struct scoutfs_balloc_root core_data_alloc; + struct scoutfs_balloc_root core_data_free; struct scoutfs_btree_root fs_root; struct scoutfs_btree_root logs_root; struct scoutfs_btree_root lock_clients; @@ -652,8 +697,6 @@ struct scoutfs_net_header { enum { SCOUTFS_NET_CMD_GREETING = 0, SCOUTFS_NET_CMD_ALLOC_INODES, - SCOUTFS_NET_CMD_ALLOC_EXTENT, - SCOUTFS_NET_CMD_FREE_EXTENTS, SCOUTFS_NET_CMD_GET_LOG_TREES, SCOUTFS_NET_CMD_COMMIT_LOG_TREES, SCOUTFS_NET_CMD_ADVANCE_SEQ, @@ -704,25 +747,6 @@ struct scoutfs_net_statfs { __u8 uuid[SCOUTFS_UUID_BYTES]; /* logical volume uuid */ } __packed; -struct scoutfs_net_extent { - __le64 start; - __le64 len; -} __packed; - -struct scoutfs_net_extent_list { - __le64 nr; - struct { - __le64 start; - __le64 len; - } __packed extents[0]; -} __packed; - -#define SCOUTFS_NET_EXTENT_LIST_BYTES(nr) \ - offsetof(struct scoutfs_net_extent_list, extents[nr]) - -/* arbitrarily makes a nice ~1k extent list payload */ -#define SCOUTFS_NET_EXTENT_LIST_MAX_NR 64 - struct scoutfs_net_lock { struct scoutfs_key key; __le64 write_version; diff --git a/utils/src/key.c b/utils/src/key.c index 4f17201d..e5c7a826 100644 --- a/utils/src/key.c +++ b/utils/src/key.c @@ -30,8 +30,6 @@ char *scoutfs_type_strings[SCOUTFS_MAX_ZONE][SCOUTFS_MAX_TYPE] = { [SCOUTFS_INODE_INDEX_ZONE][SCOUTFS_INODE_INDEX_META_SEQ_TYPE] = "msq", [SCOUTFS_INODE_INDEX_ZONE][SCOUTFS_INODE_INDEX_DATA_SEQ_TYPE] = "dsq", [SCOUTFS_XATTR_INDEX_ZONE][SCOUTFS_XATTR_INDEX_NAME_TYPE] = "nam", - [SCOUTFS_RID_ZONE][SCOUTFS_FREE_EXTENT_BLKNO_TYPE] = "fbn", - [SCOUTFS_RID_ZONE][SCOUTFS_FREE_EXTENT_BLOCKS_TYPE] = "fbs", [SCOUTFS_RID_ZONE][SCOUTFS_ORPHAN_TYPE] = "orp", [SCOUTFS_FS_ZONE][SCOUTFS_INODE_TYPE] = "ino", [SCOUTFS_FS_ZONE][SCOUTFS_XATTR_TYPE] = "xat", @@ -39,7 +37,7 @@ char *scoutfs_type_strings[SCOUTFS_MAX_ZONE][SCOUTFS_MAX_TYPE] = { [SCOUTFS_FS_ZONE][SCOUTFS_READDIR_TYPE] = "rdr", [SCOUTFS_FS_ZONE][SCOUTFS_LINK_BACKREF_TYPE] = "lbr", [SCOUTFS_FS_ZONE][SCOUTFS_SYMLINK_TYPE] = "sym", - [SCOUTFS_FS_ZONE][SCOUTFS_FILE_EXTENT_TYPE] = "fex", + [SCOUTFS_FS_ZONE][SCOUTFS_PACKED_EXTENT_TYPE] = "pex", }; char scoutfs_unknown_u8_strings[U8_MAX][U8_STR_MAX]; diff --git a/utils/src/mkfs.c b/utils/src/mkfs.c index 54d26abf..72fde3a0 100644 --- a/utils/src/mkfs.c +++ b/utils/src/mkfs.c @@ -95,7 +95,6 @@ static int write_new_fs(char *path, int fd, u8 quorum_count) struct scoutfs_super_block *super; struct scoutfs_key_be *kbe; struct scoutfs_inode *inode; - struct scoutfs_extent_btree_key *ebk; struct scoutfs_btree_block *bt; struct scoutfs_btree_item *btitem; struct scoutfs_balloc_item_key *bik; @@ -108,9 +107,10 @@ static int write_new_fs(char *path, int fd, u8 quorum_count) u64 limit; u64 size; u64 total_blocks; - u64 free_blkno; - u64 free_start; - u64 free_len; + u64 next_meta; + u64 last_meta; + u64 next_data; + u64 last_data; int ret; int i; @@ -156,63 +156,16 @@ static int write_new_fs(char *path, int fd, u8 quorum_count) super->quorum_count = quorum_count; /* metadata blocks start after the quorum blocks */ - free_blkno = SCOUTFS_QUORUM_BLKNO + SCOUTFS_QUORUM_BLOCKS; + next_meta = SCOUTFS_QUORUM_BLKNO + SCOUTFS_QUORUM_BLOCKS; - /* extents start after btree blocks */ - free_start = total_blocks - (total_blocks / 4); - free_len = total_blocks - free_start; - - /* fill out some alloc boundaries before using */ - super->free_blocks = cpu_to_le64(free_len); - - /* extent allocator btree indexes free data extent */ - blkno = free_blkno++; - - 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->hdr.fsid = super->hdr.fsid; - bt->hdr.blkno = cpu_to_le64(blkno); - bt->hdr.seq = cpu_to_le64(1); - bt->nr_items = cpu_to_le32(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_le32((long)btitem - (long)bt); - 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_le32((long)btitem - (long)bt); - 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->free_end = bt->item_hdrs[le32_to_cpu(bt->nr_items) - 1].off; - - bt->hdr.magic = cpu_to_le32(SCOUTFS_BLOCK_MAGIC_BTREE); - bt->hdr.crc = cpu_to_le32(crc_block(&bt->hdr)); - - ret = write_raw_block(fd, blkno, bt); - if (ret) - goto out; - blkno++; + /* data blocks are after metadata, we'll say 1:4 for now */ + next_data = round_up(next_meta + ((total_blocks - next_meta) / 5), + SCOUTFS_BLOCK_BITMAP_BITS); + last_meta = next_data - 1; + last_data = total_blocks - 1; /* fs root starts with root inode and its index items */ - blkno = free_blkno++; + blkno = next_meta++; super->fs_root.ref.blkno = cpu_to_le64(blkno); super->fs_root.ref.seq = cpu_to_le64(1); @@ -272,7 +225,7 @@ static int write_new_fs(char *path, int fd, u8 quorum_count) goto out; /* metadata block allocator has single item, server continues init */ - blkno = free_blkno++; + blkno = next_meta++; super->core_balloc_alloc.root.ref.blkno = cpu_to_le64(blkno); super->core_balloc_alloc.root.ref.seq = cpu_to_le64(1); @@ -299,9 +252,10 @@ static int write_new_fs(char *path, int fd, u8 quorum_count) /* set all the bits past our final used blkno */ super->core_balloc_free.total_free = - cpu_to_le64(SCOUTFS_BALLOC_ITEM_BITS - free_blkno); - for (i = free_blkno; i < SCOUTFS_BALLOC_ITEM_BITS; i++) + cpu_to_le64(SCOUTFS_BALLOC_ITEM_BITS - next_meta); + for (i = next_meta; i < SCOUTFS_BALLOC_ITEM_BITS; i++) set_bit_le(i, &biv->bits); + next_meta = i; bt->free_end = bt->item_hdrs[le32_to_cpu(bt->nr_items) - 1].off; @@ -322,7 +276,12 @@ static int write_new_fs(char *path, int fd, u8 quorum_count) } } - super->next_uninit_free_block = cpu_to_le64(SCOUTFS_BALLOC_ITEM_BITS); + /* fill out allocator fields now that we've written our blocks */ + super->next_uninit_meta_blkno = cpu_to_le64(next_meta); + super->last_uninit_meta_blkno = cpu_to_le64(last_meta); + super->next_uninit_data_blkno = cpu_to_le64(next_data); + super->last_uninit_data_blkno = cpu_to_le64(last_data); + super->free_blocks = cpu_to_le64(total_blocks - next_meta); /* write the super block */ super->hdr.seq = cpu_to_le64(1); @@ -346,17 +305,16 @@ static int write_new_fs(char *path, int fd, u8 quorum_count) " uuid: %s\n" " device blocks: "SIZE_FMT"\n" " metadata blocks: "SIZE_FMT"\n" - " file extent blocks: "SIZE_FMT"\n" + " data blocks: "SIZE_FMT"\n" " quorum count: %u\n", path, le64_to_cpu(super->hdr.fsid), le64_to_cpu(super->format_hash), uuid_str, SIZE_ARGS(total_blocks, SCOUTFS_BLOCK_SIZE), - SIZE_ARGS(le64_to_cpu(super->total_blocks) - - le64_to_cpu(super->free_blocks), + SIZE_ARGS(last_meta - next_meta + 1, SCOUTFS_BLOCK_SIZE), - SIZE_ARGS(le64_to_cpu(super->free_blocks), + SIZE_ARGS(last_data - next_data + 1, SCOUTFS_BLOCK_SIZE), super->quorum_count); diff --git a/utils/src/print.c b/utils/src/print.c index eac87402..dd2989c3 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -144,30 +144,14 @@ static void print_symlink(struct scoutfs_key *key, void *val, int val_len) le64_to_cpu(key->sks_ino), le64_to_cpu(key->sks_nr), name); } -static void print_file_extent(struct scoutfs_key *key, void *val, int val_len) +static void print_packed_extent(struct scoutfs_key *key, void *val, int val_len) { - struct scoutfs_file_extent *fex = val; - u64 iblock = le64_to_cpu(key->skfe_last) - le64_to_cpu(fex->len) + 1; + struct scoutfs_packed_extent *pe = val; - printf(" extent: ino %llu (last %llu) iblock %llu len %llu " - "blkno %llu flags 0x%x\n", - le64_to_cpu(key->skfe_ino), le64_to_cpu(key->skfe_last), - iblock, le64_to_cpu(fex->len), le64_to_cpu(fex->blkno), - fex->flags); -} - -static void print_free_extent(struct scoutfs_key *key, void *val, int val_len) -{ - u64 start = le64_to_cpu(key->sknf_major); - u64 len = le64_to_cpu(key->sknf_minor); - if (key->sk_type == SCOUTFS_FREE_EXTENT_BLOCKS_TYPE) - swap(start, len); - start -= (len - 1); - - printf(" free extent: major %llu minor %llu (start %llu " - "len %llu)\n", - le64_to_cpu(key->sknf_major), le64_to_cpu(key->sknf_minor), - start, len); + printf(" packed_extent: ino %llu base %llu part %u count %u diff_bytes %u flags 0x%x final %u\n", + le64_to_cpu(key->skpe_ino), le64_to_cpu(key->skpe_base), + key->skpe_part, le16_to_cpu(pe->count), pe->diff_bytes, + pe->flags, pe->final); } static void print_inode_index(struct scoutfs_key *key, void *val, int val_len) @@ -197,9 +181,6 @@ static print_func_t find_printer(u8 zone, u8 type) return print_xattr_index; if (zone == SCOUTFS_RID_ZONE) { - if (type == SCOUTFS_FREE_EXTENT_BLKNO_TYPE || - type == SCOUTFS_FREE_EXTENT_BLOCKS_TYPE) - return print_free_extent; if (type == SCOUTFS_ORPHAN_TYPE) return print_orphan; } @@ -212,7 +193,8 @@ static print_func_t find_printer(u8 zone, u8 type) case SCOUTFS_READDIR_TYPE: return print_dirent; case SCOUTFS_SYMLINK_TYPE: return print_symlink; case SCOUTFS_LINK_BACKREF_TYPE: return print_dirent; - case SCOUTFS_FILE_EXTENT_TYPE: return print_file_extent; + case SCOUTFS_PACKED_EXTENT_TYPE: + return print_packed_extent; } } @@ -291,7 +273,9 @@ static int print_log_trees_item(void *key, unsigned key_len, void *val, printf(" alloc_root: total_free %llu root: height %u blkno %llu seq %llu\n" " free_root: total_free %llu root: height %u blkno %llu seq %llu\n" " item_root: height %u blkno %llu seq %llu\n" - " bloom_ref: blkno %llu seq %llu\n", + " bloom_ref: blkno %llu seq %llu\n" + " data_alloc: total_free %llu root: height %u blkno %llu seq %llu\n" + " data_free: total_free %llu root: height %u blkno %llu seq %llu\n", le64_to_cpu(ltv->alloc_root.total_free), ltv->alloc_root.root.height, le64_to_cpu(ltv->alloc_root.root.ref.blkno), @@ -304,34 +288,20 @@ static int print_log_trees_item(void *key, unsigned key_len, void *val, le64_to_cpu(ltv->item_root.ref.blkno), le64_to_cpu(ltv->item_root.ref.seq), le64_to_cpu(ltv->bloom_ref.blkno), - le64_to_cpu(ltv->bloom_ref.seq)); + le64_to_cpu(ltv->bloom_ref.seq), + le64_to_cpu(ltv->data_alloc.total_free), + ltv->data_alloc.root.height, + le64_to_cpu(ltv->data_alloc.root.ref.blkno), + le64_to_cpu(ltv->data_alloc.root.ref.seq), + le64_to_cpu(ltv->data_free.total_free), + ltv->data_free.root.height, + le64_to_cpu(ltv->data_free.root.ref.blkno), + le64_to_cpu(ltv->data_free.root.ref.seq)); } return 0; } -static int print_alloc_item(void *key, unsigned key_len, void *val, - unsigned val_len, void *arg) -{ - struct scoutfs_extent_btree_key *ebk = key; - u64 start; - u64 len; - - /* XXX check sizes */ - - 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; - - 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; -} - static int print_lock_clients_entry(void *key, unsigned key_len, void *val, unsigned val_len, void *arg) { @@ -365,6 +335,19 @@ static int print_balloc_entry(void *key, unsigned key_len, void *val, return 0; } +static int print_bitmap_entry(void *key, unsigned key_len, void *val, + unsigned val_len, void *arg) +{ + struct scoutfs_block_bitmap_key *bbk = key; + struct scoutfs_packed_bitmap *pb = val; + + printf(" type %u base %llu present 0x%016llx set 0x%016llx\n", + bbk->type, be64_to_cpu(bbk->base), + le64_to_cpu(pb->present), le64_to_cpu(pb->set)); + + return 0; +} + static int print_mounted_client_entry(void *key, unsigned key_len, void *val, unsigned val_len, void *arg) { @@ -502,6 +485,14 @@ static int print_log_trees_roots(void *key, unsigned key_len, void *val, <v->free_root.root, print_balloc_entry, }, + { "log_tree_rid:%llu_nr:%llu_data_alloc", + <v->data_alloc.root, + print_bitmap_entry, + }, + { "log_tree_rid:%llu_nr:%llu_data_free", + <v->data_free.root, + print_bitmap_entry, + }, { "log_tree_rid:%llu_nr:%llu_item", <v->item_root, print_logs_item, @@ -667,22 +658,29 @@ 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_trans_seq %llu\n" " total_blocks %llu free_blocks %llu\n" - " next_uninit_free_block %llu core_balloc_blocks %llu\n" + " next_uninit_meta_blkno %llu last_uninit_meta_blkno %llu\n" + " next_uninit_data_blkno %llu last_uninit_data_blkno %llu\n" + " core_balloc_cursor %llu core_data_alloc_cursor %llu\n" " quorum_fenced_term %llu quorum_server_term %llu unmount_barrier %llu\n" " quorum_count %u server_addr %s\n" " core_balloc_alloc: total_free %llu root: height %u blkno %llu seq %llu\n" " core_balloc_free: total_free %llu root: height %u blkno %llu seq %llu\n" + " core_data_alloc: total_free %llu root: height %u blkno %llu seq %llu\n" + " core_data_free: total_free %llu root: height %u blkno %llu seq %llu\n" " lock_clients root: height %u blkno %llu seq %llu\n" " mounted_clients root: height %u blkno %llu seq %llu\n" " trans_seqs root: height %u blkno %llu seq %llu\n" - " alloc btree root: height %u blkno %llu seq %llu\n" " fs_root btree root: height %u blkno %llu seq %llu\n", le64_to_cpu(super->next_ino), le64_to_cpu(super->next_trans_seq), le64_to_cpu(super->total_blocks), le64_to_cpu(super->free_blocks), - le64_to_cpu(super->next_uninit_free_block), + le64_to_cpu(super->next_uninit_meta_blkno), + le64_to_cpu(super->last_uninit_meta_blkno), + le64_to_cpu(super->next_uninit_data_blkno), + le64_to_cpu(super->last_uninit_data_blkno), le64_to_cpu(super->core_balloc_cursor), + le64_to_cpu(super->core_data_alloc_cursor), le64_to_cpu(super->quorum_fenced_term), le64_to_cpu(super->quorum_server_term), le64_to_cpu(super->unmount_barrier), @@ -696,6 +694,14 @@ static void print_super_block(struct scoutfs_super_block *super, u64 blkno) super->core_balloc_free.root.height, le64_to_cpu(super->core_balloc_free.root.ref.blkno), le64_to_cpu(super->core_balloc_free.root.ref.seq), + le64_to_cpu(super->core_data_alloc.total_free), + super->core_data_alloc.root.height, + le64_to_cpu(super->core_data_alloc.root.ref.blkno), + le64_to_cpu(super->core_data_alloc.root.ref.seq), + le64_to_cpu(super->core_data_free.total_free), + super->core_data_free.root.height, + le64_to_cpu(super->core_data_free.root.ref.blkno), + le64_to_cpu(super->core_data_free.root.ref.seq), super->lock_clients.height, le64_to_cpu(super->lock_clients.ref.blkno), le64_to_cpu(super->lock_clients.ref.seq), @@ -705,9 +711,6 @@ static void print_super_block(struct scoutfs_super_block *super, u64 blkno) super->trans_seqs.height, le64_to_cpu(super->trans_seqs.ref.blkno), le64_to_cpu(super->trans_seqs.ref.seq), - super->alloc_root.height, - le64_to_cpu(super->alloc_root.ref.blkno), - le64_to_cpu(super->alloc_root.ref.seq), super->fs_root.height, le64_to_cpu(super->fs_root.ref.blkno), le64_to_cpu(super->fs_root.ref.seq)); @@ -757,8 +760,15 @@ static int print_volume(int fd) if (err && !ret) ret = err; - err = print_btree(fd, super, "alloc", &super->alloc_root, - print_alloc_item, NULL); + err = print_btree(fd, super, "core_data_alloc", + &super->core_data_alloc.root, + print_bitmap_entry, NULL); + if (err && !ret) + ret = err; + + err = print_btree(fd, super, "core_data_free", + &super->core_data_free.root, + print_bitmap_entry, NULL); if (err && !ret) ret = err; From 920fca752c1ec07f89a67a8c107bcc935a355b3e Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Mon, 16 Dec 2019 16:16:47 -0800 Subject: [PATCH 185/235] scoutfs-utils: have xattr use max val size Signed-off-by: Zach Brown --- utils/src/format.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index 9b9fd06d..d801c40d 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -611,11 +611,11 @@ enum { #define SCOUTFS_XATTR_MAX_NAME_LEN 255 #define SCOUTFS_XATTR_MAX_VAL_LEN 65535 -#define SCOUTFS_XATTR_MAX_PART_SIZE 512U +#define SCOUTFS_XATTR_MAX_PART_SIZE SCOUTFS_MAX_VAL_SIZE #define SCOUTFS_XATTR_NR_PARTS(name_len, val_len) \ DIV_ROUND_UP(sizeof(struct scoutfs_xattr) + name_len + val_len, \ - SCOUTFS_XATTR_MAX_PART_SIZE); + (unsigned int)SCOUTFS_XATTR_MAX_PART_SIZE) #define SCOUTFS_LOCK_INODE_GROUP_NR 1024 #define SCOUTFS_LOCK_INODE_GROUP_MASK (SCOUTFS_LOCK_INODE_GROUP_NR - 1) From 794277053f39d98f704ebe0588e6d7a1c1b1aa22 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Fri, 17 Jan 2020 15:46:33 -0800 Subject: [PATCH 186/235] scoutfs-utils: add a few more man pages Add an overview man page for scoutfs and add a manpage for the userspace utility and its commands. Signed-off-by: Zach Brown --- utils/man/scoutfs.5 | 48 ++++ utils/man/scoutfs.8 | 435 ++++++++++++++++++++++++++++++++++++ utils/scoutfs-utils.spec.in | 8 +- 3 files changed, 488 insertions(+), 3 deletions(-) create mode 100644 utils/man/scoutfs.5 create mode 100644 utils/man/scoutfs.8 diff --git a/utils/man/scoutfs.5 b/utils/man/scoutfs.5 new file mode 100644 index 00000000..9d45a3da --- /dev/null +++ b/utils/man/scoutfs.5 @@ -0,0 +1,48 @@ +.TH scoutfs 5 +.SH NAME +scoutfs \- overview and mount options for the scoutfs filesystem +.SH DESCRIPTION +A scoutfs filesystem is stored on a block device. Multiple mounts of +the filesystem are supported between hosts that share access to the +block device. A new filesystem is created with the +.B mkfs +command in the +.BR scoutfs (8) +utility. +.SH MOUNT OPTIONS +The following mount options are supported by scoutfs in addition to the +general mount options described in the +.BR mount (8) +manual page. +.TP +.B server_addr= +The server_addr option indicates that this mount will participate in +quorum election to try and run a server for all the mounts of its +filesystem. The option specifies the local TCP IPv4 address that the +mount's elected server will listen on for connections from all other +mounts of the filesystem. +.sp +The IPv4 address must be specified as a dotted quad, name resolution is +not supported. A specific port may be provided after a seperating +colon. If no port is specified then a random port will be chosen. The +address will be used for the lifetime of the mount and can not be +changed. The mount must be unmounted to specify a different address. +.sp +If server_addr is not specified then the mount will read the filesystem +until it sees the address of an elected server to connect to. +.SH FURTHER READING +A +.B scoutfs +filesystem can detect corruption at runtime. A catalog of kernel log +messages that indicate corruption can be found in +.BR scoutfs-corruption (8) +\&. + +.SH SEE ALSO +.BR scoutfs (8), +.BR scoutfs-corruption (7). + +.SH AUTHORS +Zach Brown + + diff --git a/utils/man/scoutfs.8 b/utils/man/scoutfs.8 new file mode 100644 index 00000000..4664f14a --- /dev/null +++ b/utils/man/scoutfs.8 @@ -0,0 +1,435 @@ +.TH scoutfs 8 +.SH NAME +scoutfs \- scoutfs management utility +.SH DESCRIPTION +The +.b +scoutfs +utility provides commands to manage a scoutfs filesystem. +.SH COMMANDS +.TP +.BI "counters [\-t\] " +.sp +Displays the counters and their values for a mounted scoutfs filesystem. +Each counter and its value are printed on a line to stdout with +sufficient spaces seperating the name and value to align the values +after +.RS 1.0i +.PD 0 +.TP +.sp +.B "\-t" +Format the counters into a table that fills the display instead of +printing one counter per line. The names and values are padded to +create columns that fill the current width of the terminal. +.TP +.B "sysfs topdir" +Specify the mount's sysfs directory in which to find the +.B counters/ +directory when then contains files for each counter. +The sysfs directory is typically +of the form +.I /sys/fs/scoutfs/f..r./ +\&. +.RE +.PD + +.TP +.BI "data-waiting " +.sp +Displays all the files and blocks for which there is a task blocked waiting on +offline data. +.sp +The results are sorted by the file's inode number and the +logical block offset that is being waited on. +.sp +Each line of output specifies a block in a file that has a task waiting +and is formatted as: +.I "ino iblock ops [str]" +\&. The ops string indicates blocked operations seperated by commas and can +include +.B read +for a read operation, +.B write +for a write operation, and +.B change_size +for a truncate or extending write. +.RS 1.0i +.PD 0 +.sp +.TP +.B "ino" +Start iterating over waiting tasks from the given inode number. +Specifying 0 will show all waiting tasks. +.TP +.B "iblock" +Start iterating over waiting tasks from the given logical block number +in the starting inode. Specifying 0 will show blocks in the first inode +and then continue to show all blocks with tasks waiting in all the +remaining inodes. +.TP +.B "path" +A path to any inode in the target filesystem, typically the root +directory. +.RE +.PD + +.TP +.BI "find-xattrs <\-n\ name> <\-f path>" +.sp +Displays the inode numbers of inodes in the filesystem which may have +an extended attribute with the given name. +.sp +The results may contain false positives. The returned inode numbers +should be checked to verify that the extended attribute is in fact +present on the inode. +.RS 1.0i +.PD 0 +.TP +.sp +.B "-n name" +Specifies the full name of the extended attribute to search for as +described in the +.BR xattr (7) +manual page. +.TP +.B "-f path" +Specifies the path to any inode in the filesystem to search. +.RE +.PD + +.TP +.BI "ino-path " +.sp +Displays all the paths to links to the given inode number. +.sp +All the relative paths from the root directory to each link of the +target inode are output, one result per line. Each output path is +guaranteed to have been a valid path to a link at some point in the +past. An individual path won't be corrupted by a rename that occurs +during the search. The set of paths can be modified while the search is +running. A rename of a parent directory of all the paths, for example, +can result in output where the parent directory name component changes +in the middle of outputting all the paths. +.RS 1.0i +.PD 0 +.sp +.TP +.B "ino" +The inode number of the target inode to resolve. +.TP +.B "path" +A path to any inode in the target filesystem, typically the root +directory. +.RE +.PD + +.TP +.BI "listxattr-hidden <\-f path>" +.sp +Displays all the extended attributes starting with the +.BR scoutfs. +prefix and which contain the +.BR hide. +tag +which makes them invisible to +.BR listxattr (2) +\&. +The names of each attribute are output, one name per line. Their order +is determined by internal indexing implementation details and should not +be relied on. +.RS 1.0i +.PD 0 +.TP +.sp +.B "-f path" +The path to the file whose extended attributes will be listed. The +user must have read permission to the inode. +.RE +.PD + +.TP +.BI "mkfs <\-Q nr> " +.sp +Initialize a new empty filesystem in the target device by writing empty +structures and a new superblock. +.sp +This +.B unconditionally destroys +the contents of the device, regardless of what it contains or who may be +using it. It simply writes new data structures into known offsets. +.B Be very careful that the device does not contain data and is not actively in use. +.RS 1.0i +.PD 0 +.TP +.sp +.B "-Q nr" +Specify the number of mounts needed to reach quorum and elect a mount +to start the server. Mounts of the device will hang until this many +mounts are operational and can elect a server amongst themselves. +.sp +Mounts with the +.B server_addr +mount option participate in quorum. The safest quorum number is the +smallest majority of an odd number of participating mounts. For +example, +two out of three total mounts. This ensures that there can only be one +set of mounts that can establish quorum. +.sp +Degenerate quorums are possible, for example by specifying half of an +even number of mounts or less than half of the mount count, down to even +just one mount establishing quorum. These minority quorums carry the +risk of multiple quorums being established concurrently. Each quorum's +elected servers race to fence each other and can have the unlikely +outcome of continually racing to fence each other resulting in a +persistent loss of service. +.TP +.B "path" +The path to the device whose contents will be unconditionally destroyed. +.RE +.PD + +.TP +.BI "print " +.sp +Prints out all of the metadata in the file system. This makes no effort +to ensure that the structures are consistent as they're traversed and +can present structures that seem corrupt as they change as they're +output. +.RS 1.0i +.PD 0 +.TP +.sp +.B "path" +The path to the device that contains the filesystem whose metadata will +be printed. The command reads from the buffer cache of the device which +may not reflect the current blocks in the filesystem that may have been +written through another host or device. The local device's cache can be +manually flused before printing, perhaps with the +.B \--flushbufs +command in the +.BR blockdev (8) +command. +.RE +.PD + +.TP +.BI "release <4KB block offset> <4KB block count>" +.sp +.B Release +the given logical block region of the file. That is, truncate away +any data blocks but leave behind offline data regions and do not change +the main inode metadata. Future attempts to read or write the block +region +will block until the region is restored by a +.B stage +write. This is used by userspace archive managers to store file data +in a remote archive tier. +.sp +This only works on regular files and with write permission. Releasing +regions that are already offline or are sparse, including past the end +of the file, silently succeed. +.RS 1.0i +.PD 0 +.TP +.sp +.B "path" +The path to the regular file whose region will be released. +.TP +.B "version" +The current data version of the contents of the file. This ensures +that a release operation is truncating the version of the data that it +expects. It can't throw away data that was newly written while it was +performing its release operation. An inode's data_version is read +by the SCOUTFS_IOC_STATFS_MORE +ioctl. +.TP +.B "4KB block offset" +The 64bit logical block offset of the start of the region in units of 4KB. +.TP +.B "4KB block count" +The 64bit length of the region to release in units of 4KB blocks. +.RE +.PD + +.TP +.BI "setattr <\-c ctime> <\-d data_version> -o <\-s i_size> <\-f path> +.sp +Set scoutfs specific metadata on a newly created inode without updating +other inode metadata. +.RS 1.0i +.PD 0 +.TP +.sp +.B "-c ctime" +Specify the inode's creation GMT timespec with 64bit seconds and 32bit +nanoseconds formatted as +.B sec.nsec +\&. +.TP +.B "-d data_version" +Specify the inode's data version. This can only be set on regular files whose +current data_version is 0. +.TP +.B "-o" +Create an offline region for all of the file's data up to the specified +file size. This can only be set on regular files whose data_version is +0 and i_size must also be specified. +.TP +.B "-s i_size" +Set the inode's i_size. This can only be set on regular files whose +data_version is 0. +.TP +.B "-f path" +The file whose metadata will be set. +.RE +.PD + +.TP +.BI "stage " +.sp +.B Stage +the contents of the file by reading a region of another archive file and writing it +into the file region without updating regular inode metadata. Any tasks +that are blocked by the offline region will proceed once it has been +staged. +.RS 1.0i +.PD 0 +.TP +.sp +.B "file" +The regular file whose contents will be staged. +.TP +.B "vers" +The data_version of the contents to be staged. It must match the +current data_version of the file. +.TP +.B "offset" +The starting byte offset of the region to write. This must be aligned +to 4KB blocks. +.TP +.B "count" +The length of the region to write in bytes. A length of 0 is a noop +and will immediately return success. The length must be a multiple +of 4KB blocks unless it is writing the final partial block in which +case it must end at i_size. +.TP +.B "archive file" +A file whose contents will be read and written as the staged region. +The start of the archive file will be used as the start of the region. +.RE +.PD + +.TP +.BI "stat [-s single] " +.sp +Display scoutfs metadata fields for the given inode. +.RS 1.0i +.PD 0 +.TP +.sp +.B "-s single" +Only ontput a single stat instead of all the stats with one stat per +line. The possible stat names are those given in the output. +.TP +.B "path" +The path to the file whose inode field will be output. +.sp +.TP +.RE +.PD +The fields are as follows: +.RS 1.0i +.PD 0 +.TP +.B "meta_seq" +The metadata change sequence. This changes each time the inode's metadata +is changed during a mount's transaction. +.TP +.B "data_seq" +The data change sequence. This changes each time the inode's data +is changed during a mount's transaction. +.TP +.B "data_version" +The data version changes every time any contents of the file changes, +including size changes. It can change many times during a syscall in a +transactions. +.TP +.B "online_blocks" +The number of 4Kb data blocks that contain data and can be read. +.TP +.B "online_blocks" +The number of 4Kb data blocks that are offline and would need to be +staged to be read. +.RE +.PD + +.TP +.BI "statfs [-s single] " +.sp +Display scoutfs metadata fields for a scoutfs filesystem. +.RS 1.0i +.PD 0 +.TP +.sp +.B "-s single" +Only ontput a single stat instead of all the stats with one stat per +line. The possible stat names are those given in the output. +.TP +.B "path" +The path to any inode in the filesystem. +.sp +.TP +.RE +.PD +The fields are as follows: +.RS 1.0i +.PD 0 +.TP +.B "fsid" +The unique 64bit filesystem identifier for this filesystem. +.TP +.B "rid" +The unique 64bit random identifier for this mount of the filesystem. +This is generated for every new mount of the file system. +.RE +.PD + +.TP +.BI "walk-inodes " +.sp +Walks an inode index in the file system and outputs the inode numbers +that are found within the first and last positions in the index. +.RS 1.0i +.PD 0 +.sp +.TP +.B "index" +Specifies the index to walk. The currently supported indices are +.B meta_seq +and +.B data_seq +\&. +.TP +.B "first" +The starting position of the index walk. +.I 0 +is the first possible position in every index. +.TP +.B "last" +The last position to include in the index walk. +.I \-1 +can be given as shorthand for the U64_MAX last possible position in +every index. +.TP +.B "path" +A path to any inode in the filesystem, typically the root directory. +.RE +.PD + +.SH SEE ALSO +.BR scoutfs (5), +.BR xattr (7). + +.SH AUTHORS +Zach Brown diff --git a/utils/scoutfs-utils.spec.in b/utils/scoutfs-utils.spec.in index 81847d9d..35219721 100644 --- a/utils/scoutfs-utils.spec.in +++ b/utils/scoutfs-utils.spec.in @@ -43,18 +43,20 @@ scoutfs - development headers %build make -gzip man/*.7 +gzip man/*.? %install -mkdir -p $RPM_BUILD_ROOT%{_mandir}/man7 +mkdir -p $RPM_BUILD_ROOT%{_mandir}/man{5,7,8} +cp man/*.5.gz $RPM_BUILD_ROOT%{_mandir}/man5/. cp man/*.7.gz $RPM_BUILD_ROOT%{_mandir}/man7/. +cp man/*.8.gz $RPM_BUILD_ROOT%{_mandir}/man8/. install -m 755 -D src/scoutfs $RPM_BUILD_ROOT%{_sbindir}/scoutfs install -m 644 -D src/ioctl.h $RPM_BUILD_ROOT%{_includedir}/scoutfs/ioctl.h install -m 644 -D src/format.h $RPM_BUILD_ROOT%{_includedir}/scoutfs/format.h %files %defattr(644,root,root,755) -%{_mandir}/man7/scoutfs-corruption.7.gz +%{_mandir}/man*/scoutfs*.gz %defattr(755,root,root,755) %{_sbindir}/scoutfs From 34c3d903d9853981e9847c70c9a95833156219e9 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Sun, 16 Feb 2020 17:06:44 -0800 Subject: [PATCH 187/235] scoutfs-utils: add round_down() and flsll() Add quick helpers for these two kernel functions. Signed-off-by: Zach Brown --- utils/src/util.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/utils/src/util.h b/utils/src/util.h index 3ed72701..b54fd147 100644 --- a/utils/src/util.h +++ b/utils/src/util.h @@ -49,6 +49,12 @@ do { \ \ ((a) + _b - 1) & ~(_b - 1); \ }) +#define round_down(a, b) \ +({ \ + __typeof__(a) _b = (b); \ + \ + ((a) & ~(_b - 1)); \ +}) #define DIV_ROUND_UP(x, y) (((x) + (y) - 1) / (y)) #define ALIGN(x, y) (((x) + (y) - 1) & ~((y) - 1)) @@ -66,6 +72,13 @@ do { \ #define U32_MAX ((u32)~0ULL) #define U64_MAX ((u64)~0ULL) +#define flsll(x) \ +({ \ + unsigned long long _x = (x); \ + \ + (_x == 0 ? 0 : 64 - __builtin_clzll(_x)); \ +}) + /* * return -1,0,+1 based on the memcmp comparison of the minimum of their * two lengths. If their min shared bytes are equal but the lengths From ff436db49b6db773292c258afa1295aaf63c0170 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Sun, 16 Feb 2020 17:32:54 -0800 Subject: [PATCH 188/235] scoutfs-utils: add support for radix alloc Add support for initializing radix allocator blocks that describe free space in mkfs and support for printing them out. Signed-off-by: Zach Brown --- utils/src/format.h | 126 +++++++++---------- utils/src/mkfs.c | 292 ++++++++++++++++++++++++++++++++++++--------- utils/src/print.c | 263 ++++++++++++++++++++-------------------- utils/src/radix.c | 106 ++++++++++++++++ utils/src/radix.h | 13 ++ 5 files changed, 542 insertions(+), 258 deletions(-) create mode 100644 utils/src/radix.c create mode 100644 utils/src/radix.h diff --git a/utils/src/format.h b/utils/src/format.h index d801c40d..6cfb7322 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -8,6 +8,7 @@ #define SCOUTFS_BLOCK_MAGIC_SUPER 0x103c428b #define SCOUTFS_BLOCK_MAGIC_BTREE 0xe597f96d #define SCOUTFS_BLOCK_MAGIC_BLOOM 0x31995604 +#define SCOUTFS_BLOCK_MAGIC_RADIX 0xebeb5e65 /* * The super block and btree blocks are fixed 4k. @@ -132,6 +133,43 @@ struct scoutfs_key { #define skpe_base _sk_second #define skpe_part _sk_fourth +struct scoutfs_radix_block { + struct scoutfs_block_header hdr; + __le32 sm_first; + __le32 lg_first; + union { + struct scoutfs_radix_ref { + __le64 blkno; + __le64 seq; + __le64 sm_total; + __le64 lg_total; + } __packed refs[0]; + __le64 bits[0]; + } __packed; +} __packed; + +struct scoutfs_radix_root { + __u8 height; + __le64 next_find_bit; + struct scoutfs_radix_ref ref; +} __packed; + +#define SCOUTFS_RADIX_REFS \ + ((SCOUTFS_BLOCK_SIZE - offsetof(struct scoutfs_radix_block, refs[0])) /\ + sizeof(struct scoutfs_radix_ref)) + +/* 8 meg regions with 4k data blocks */ +#define SCOUTFS_RADIX_LG_SHIFT 11 +#define SCOUTFS_RADIX_LG_BITS (1 << SCOUTFS_RADIX_LG_SHIFT) +#define SCOUTFS_RADIX_LG_MASK (SCOUTFS_RADIX_LG_BITS - 1) + +/* round block bits down to a multiple of large ranges */ +#define SCOUTFS_RADIX_BITS \ + (((SCOUTFS_BLOCK_SIZE - \ + offsetof(struct scoutfs_radix_block, bits[0])) * 8) & \ + ~(__u64)SCOUTFS_RADIX_LG_MASK) +#define SCOUTFS_RADIX_BITS_BYTES (SCOUTFS_RADIX_BITS / 8) + /* * The btree still uses memcmp() to compare keys. We should fix that * before too long. @@ -208,55 +246,6 @@ struct scoutfs_btree_block { struct scoutfs_btree_item_header item_hdrs[0]; } __packed; -/* - * Free metadata blocks are tracked by block allocator items. - */ -struct scoutfs_balloc_root { - struct scoutfs_btree_root root; - __le64 total_free; -} __packed; -struct scoutfs_balloc_item_key { - __be64 base; -} __packed; - -#define SCOUTFS_BALLOC_ITEM_BYTES 256 -#define SCOUTFS_BALLOC_ITEM_U64S (SCOUTFS_BALLOC_ITEM_BYTES / \ - sizeof(__u64)) -#define SCOUTFS_BALLOC_ITEM_BITS (SCOUTFS_BALLOC_ITEM_BYTES * 8) -#define SCOUTFS_BALLOC_ITEM_BASE_SHIFT ilog2(SCOUTFS_BALLOC_ITEM_BITS) -#define SCOUTFS_BALLOC_ITEM_BIT_MASK (SCOUTFS_BALLOC_ITEM_BITS - 1) - -struct scoutfs_balloc_item_val { - __le64 bits[SCOUTFS_BALLOC_ITEM_U64S]; -} __packed; - -/* - * Free data blocks are tracked in bitmaps stored in btree items. - */ -struct scoutfs_block_bitmap_key { - __u8 type; - __be64 base; -} __packed; - -#define SCOUTFS_BLOCK_BITMAP_BIG 0 -#define SCOUTFS_BLOCK_BITMAP_LITTLE 1 - -#define SCOUTFS_PACKED_BITMAP_WORDS 32 -#define SCOUTFS_PACKED_BITMAP_BITS (SCOUTFS_PACKED_BITMAP_WORDS * 64) -#define SCOUTFS_PACKED_BITMAP_MAX_BYTES \ - offsetof(struct scoutfs_packed_bitmap, \ - words[SCOUTFS_PACKED_BITMAP_WORDS]) - -#define SCOUTFS_BLOCK_BITMAP_BITS SCOUTFS_PACKED_BITMAP_BITS -#define SCOUTFS_BLOCK_BITMAP_BIT_MASK (SCOUTFS_PACKED_BITMAP_BITS - 1) -#define SCOUTFS_BLOCK_BITMAP_BASE_SHIFT (ilog2(SCOUTFS_PACKED_BITMAP_BITS)) - -struct scoutfs_packed_bitmap { - __le64 present; - __le64 set; - __le64 words[0]; -}; - /* * The lock server keeps a persistent record of connected clients so that * server failover knows who to wait for before resuming operations. @@ -293,12 +282,12 @@ struct scoutfs_mounted_client_btree_val { * about item logs, it's about clients making changes to trees. */ struct scoutfs_log_trees { - struct scoutfs_balloc_root alloc_root; - struct scoutfs_balloc_root free_root; + struct scoutfs_radix_root meta_avail; + struct scoutfs_radix_root meta_freed; struct scoutfs_btree_root item_root; struct scoutfs_btree_ref bloom_ref; - struct scoutfs_balloc_root data_alloc; - struct scoutfs_balloc_root data_free; + struct scoutfs_radix_root data_avail; + struct scoutfs_radix_root data_freed; __le64 rid; __le64 nr; } __packed; @@ -309,12 +298,12 @@ struct scoutfs_log_trees_key { } __packed; struct scoutfs_log_trees_val { - struct scoutfs_balloc_root alloc_root; - struct scoutfs_balloc_root free_root; + struct scoutfs_radix_root meta_avail; + struct scoutfs_radix_root meta_freed; struct scoutfs_btree_root item_root; struct scoutfs_btree_ref bloom_ref; - struct scoutfs_balloc_root data_alloc; - struct scoutfs_balloc_root data_free; + struct scoutfs_radix_root data_avail; + struct scoutfs_radix_root data_freed; } __packed; struct scoutfs_log_item_value { @@ -489,25 +478,22 @@ struct scoutfs_super_block { __u8 uuid[SCOUTFS_UUID_BYTES]; __le64 next_ino; __le64 next_trans_seq; - __le64 total_blocks; - __le64 next_uninit_meta_blkno; - __le64 last_uninit_meta_blkno; - __le64 next_uninit_data_blkno; - __le64 last_uninit_data_blkno; - __le64 core_balloc_cursor; - __le64 core_data_alloc_cursor; + __le64 total_meta_blocks; /* both static and dynamic */ + __le64 first_meta_blkno; /* first dynamically allocated */ + __le64 last_meta_blkno; + __le64 total_data_blocks; + __le64 first_data_blkno; + __le64 last_data_blkno; __le64 free_blocks; - __le64 first_fs_blkno; - __le64 last_fs_blkno; __le64 quorum_fenced_term; __le64 quorum_server_term; __le64 unmount_barrier; __u8 quorum_count; struct scoutfs_inet_addr server_addr; - struct scoutfs_balloc_root core_balloc_alloc; - struct scoutfs_balloc_root core_balloc_free; - struct scoutfs_balloc_root core_data_alloc; - struct scoutfs_balloc_root core_data_free; + struct scoutfs_radix_root core_meta_avail; + struct scoutfs_radix_root core_meta_freed; + struct scoutfs_radix_root core_data_avail; + struct scoutfs_radix_root core_data_freed; struct scoutfs_btree_root fs_root; struct scoutfs_btree_root logs_root; struct scoutfs_btree_root lock_clients; diff --git a/utils/src/mkfs.c b/utils/src/mkfs.c index 72fde3a0..63a97aec 100644 --- a/utils/src/mkfs.c +++ b/utils/src/mkfs.c @@ -26,6 +26,7 @@ #include "dev.h" #include "key.h" #include "bitops.h" +#include "radix.h" static int write_raw_block(int fd, u64 blkno, void *blk) { @@ -84,6 +85,199 @@ static char *size_str(u64 nr, unsigned size) #define SIZE_FMT "%llu (%.2f %s)" #define SIZE_ARGS(nr, sz) (nr), size_flt(nr, sz), size_str(nr, sz) +/* + * Update a reference to a block of references that has been modified. We + * walk all the references and rebuild the ref tracking. + */ +static void update_parent_ref(struct scoutfs_radix_ref *ref, + struct scoutfs_radix_block *rdx) +{ + int i; + + ref->sm_total = cpu_to_le64(0); + ref->lg_total = cpu_to_le64(0); + + rdx->sm_first = cpu_to_le32(SCOUTFS_RADIX_REFS); + rdx->lg_first = cpu_to_le32(SCOUTFS_RADIX_REFS); + + for (i = 0; i < SCOUTFS_RADIX_REFS; i++) { + if (le32_to_cpu(rdx->sm_first) == SCOUTFS_RADIX_REFS && + rdx->refs[i].sm_total != 0) + rdx->sm_first = cpu_to_le32(i); + if (le32_to_cpu(rdx->lg_first) == SCOUTFS_RADIX_REFS && + rdx->refs[i].lg_total != 0) + rdx->lg_first = cpu_to_le32(i); + + le64_add_cpu(&ref->sm_total, + le64_to_cpu(rdx->refs[i].sm_total)); + le64_add_cpu(&ref->lg_total, + le64_to_cpu(rdx->refs[i].lg_total)); + } +} + +/* + * Initialize all the blocks in a path to a leaf with the given blocks + * set. We know that we're being called to set all the bits in a region + * by setting the left and right partial leafs of the region. We first + * set the left and set full references down the left path, then we're + * called on the right and set full to the left and clear full refs past + * the right. + * + * The caller provides an array of block buffers and a starting block + * number to allocate blocks from and reference blocks within. It's the + * world's dumbest block cache. + */ +static void set_radix_path(struct scoutfs_super_block *super, int *inds, + struct scoutfs_radix_ref *ref, int level, bool left, + void **blocks, u64 blkno_base, u64 *next_blkno, + u64 first, u64 last) +{ + struct scoutfs_radix_block *rdx; + int lg_ind; + int lg_after; + u64 bno; + int ind; + int end; + int i; + + if (ref->blkno == 0) { + bno = (*next_blkno)++; + ref->blkno = cpu_to_le64(bno); + ref->seq = cpu_to_le64(1); + } + + rdx = blocks[le64_to_cpu(ref->blkno) - blkno_base]; + + if (level) { + ind = inds[level]; + + /* initialize empty parent blocks with empty refs */ + if (ref->sm_total == 0) { + for (i = 0; i < SCOUTFS_RADIX_REFS; i++) + radix_init_ref(&rdx->refs[i], level - 1, false); + } + + if (left) { + /* initialize full refs from left to end */ + for (i = ind + 1; i < SCOUTFS_RADIX_REFS; i++) + radix_init_ref(&rdx->refs[i], level - 1, true); + } else { + /* initialize full refs from start or left to right */ + for (i = le32_to_cpu(rdx->sm_first) != + SCOUTFS_RADIX_REFS ? + le32_to_cpu(rdx->sm_first) + 1 : 0; + i < ind; i++) + radix_init_ref(&rdx->refs[i], level - 1, true); + + /* wipe full refs from right (maybe including) to end */ + for (i = le64_to_cpu(rdx->refs[ind].blkno) == U64_MAX ? + ind : ind + 1; i < SCOUTFS_RADIX_REFS; i++) + radix_init_ref(&rdx->refs[i], level - 1, false); + } + + set_radix_path(super, inds, &rdx->refs[ind], level - 1, left, + blocks, blkno_base, next_blkno, first, last); + update_parent_ref(ref, rdx); + + } else { + ind = first - radix_calc_leaf_bit(first); + end = last - radix_calc_leaf_bit(last); + for (i = ind; i <= end; i++) + set_bit_le(i, rdx->bits); + + rdx->sm_first = cpu_to_le32(ind); + ref->sm_total = cpu_to_le64(end - ind + 1); + + lg_ind = round_up(ind, SCOUTFS_RADIX_LG_BITS); + lg_after = round_down(end + 1, SCOUTFS_RADIX_LG_BITS); + + if (lg_ind < SCOUTFS_RADIX_BITS) + rdx->lg_first = cpu_to_le32(lg_ind); + else + rdx->lg_first = cpu_to_le32(SCOUTFS_RADIX_BITS); + ref->lg_total = cpu_to_le64(lg_after - lg_ind); + } +} + +/* + * Initialize a new radix allocator with the region of bits set. We + * initialize and write populated blocks down the paths to the two ends + * of the interval and write full refs in between. + */ +static int write_radix_blocks(struct scoutfs_super_block *super, int fd, + struct scoutfs_radix_root *root, + u64 blkno, u64 first, u64 last) +{ + struct scoutfs_radix_block *rdx; + void **blocks; + u64 next_blkno; + u64 edge; + u8 height; + int alloced; + int used; + int *inds; + int ret; + int i; + + height = radix_height_from_last(last); + inds = alloca(sizeof(inds[0]) * height); + alloced = height * 2; + next_blkno = blkno; + + /* allocate all the blocks we might need */ + blocks = calloc(alloced, sizeof(*blocks)); + if (!blocks) + return -ENOMEM; + + for (i = 0; i < alloced; i++) { + blocks[i] = calloc(1, SCOUTFS_BLOCK_SIZE); + if (blocks[i] == NULL) { + ret = -ENOMEM; + goto out; + } + } + + /* initialize empty root ref */ + memset(root, 0, sizeof(struct scoutfs_radix_root)); + root->height = height; + radix_init_ref(&root->ref, height - 1, false); + + edge = radix_calc_leaf_bit(first) + SCOUTFS_RADIX_BITS - 1; + radix_calc_level_inds(inds, height, first); + set_radix_path(super, inds, &root->ref, root->height - 1, true, blocks, + blkno, &next_blkno, first, min(edge, last)); + + edge = radix_calc_leaf_bit(last); + radix_calc_level_inds(inds, height, last); + set_radix_path(super, inds, &root->ref, root->height - 1, false, blocks, + blkno, &next_blkno, max(first, edge), last); + + used = next_blkno - blkno; + + /* write out all the dirtied blocks */ + for (i = 0; i < used; i++) { + rdx = blocks[i]; + rdx->hdr.magic = cpu_to_le32(SCOUTFS_BLOCK_MAGIC_RADIX); + rdx->hdr.fsid = super->hdr.fsid; + rdx->hdr.seq = cpu_to_le64(1); + rdx->hdr.blkno = cpu_to_le64(blkno + i); + rdx->hdr.crc = cpu_to_le32(crc_block(&rdx->hdr)); + ret = write_raw_block(fd, blkno + i, rdx); + if (ret < 0) + goto out; + } + + ret = used; +out: + if (blocks) { + for (i = 0; i < alloced && blocks[i]; i++) + free(blocks[i]); + free(blocks); + } + + return ret; +} + /* * Make a new file system by writing: * - super blocks @@ -97,8 +291,6 @@ static int write_new_fs(char *path, int fd, u8 quorum_count) struct scoutfs_inode *inode; struct scoutfs_btree_block *bt; struct scoutfs_btree_item *btitem; - struct scoutfs_balloc_item_key *bik; - struct scoutfs_balloc_item_val *biv; struct scoutfs_key key; struct timeval tv; char uuid_str[37]; @@ -107,6 +299,7 @@ static int write_new_fs(char *path, int fd, u8 quorum_count) u64 limit; u64 size; u64 total_blocks; + u64 meta_alloc_blocks; u64 next_meta; u64 last_meta; u64 next_data; @@ -142,6 +335,13 @@ static int write_new_fs(char *path, int fd, u8 quorum_count) } total_blocks = size / SCOUTFS_BLOCK_SIZE; + /* metadata blocks start after the quorum blocks */ + next_meta = SCOUTFS_QUORUM_BLKNO + SCOUTFS_QUORUM_BLOCKS; + /* data blocks are after metadata, we'll say 1:4 for now */ + next_data = round_up(next_meta + ((total_blocks - next_meta) / 5), + SCOUTFS_RADIX_BITS); + last_meta = next_data - 1; + last_data = total_blocks - 1; /* partially initialize the super so we can use it to init others */ memset(super, 0, SCOUTFS_BLOCK_SIZE); @@ -152,18 +352,14 @@ static int write_new_fs(char *path, int fd, u8 quorum_count) uuid_generate(super->uuid); super->next_ino = cpu_to_le64(SCOUTFS_ROOT_INO + 1); super->next_trans_seq = cpu_to_le64(1); - super->total_blocks = cpu_to_le64(total_blocks); + super->total_meta_blocks = cpu_to_le64(last_meta + 1); + super->first_meta_blkno = cpu_to_le64(next_meta); + super->last_meta_blkno = cpu_to_le64(last_meta); + super->total_data_blocks = cpu_to_le64(last_data - next_data + 1); + super->first_data_blkno = cpu_to_le64(next_data); + super->last_data_blkno = cpu_to_le64(last_data); super->quorum_count = quorum_count; - /* metadata blocks start after the quorum blocks */ - next_meta = SCOUTFS_QUORUM_BLKNO + SCOUTFS_QUORUM_BLOCKS; - - /* data blocks are after metadata, we'll say 1:4 for now */ - next_data = round_up(next_meta + ((total_blocks - next_meta) / 5), - SCOUTFS_BLOCK_BITMAP_BITS); - last_meta = next_data - 1; - last_data = total_blocks - 1; - /* fs root starts with root inode and its index items */ blkno = next_meta++; @@ -224,47 +420,31 @@ static int write_new_fs(char *path, int fd, u8 quorum_count) if (ret) goto out; - /* metadata block allocator has single item, server continues init */ - blkno = next_meta++; - - super->core_balloc_alloc.root.ref.blkno = cpu_to_le64(blkno); - super->core_balloc_alloc.root.ref.seq = cpu_to_le64(1); - super->core_balloc_alloc.root.height = 1; - - /* XXX magic */ - - memset(bt, 0, SCOUTFS_BLOCK_SIZE); - bt->hdr.fsid = super->hdr.fsid; - bt->hdr.blkno = cpu_to_le64(blkno); - bt->hdr.seq = cpu_to_le64(1); - bt->nr_items = cpu_to_le32(1); - - /* btree item allocated from the back of the block */ - biv = (void *)bt + SCOUTFS_BLOCK_SIZE - sizeof(*biv); - bik = (void *)biv - sizeof(*bik); - btitem = (void *)bik - sizeof(*btitem); - - bt->item_hdrs[0].off = cpu_to_le32((long)btitem - (long)bt); - btitem->key_len = cpu_to_le16(sizeof(*bik)); - btitem->val_len = cpu_to_le16(sizeof(*biv)); - - bik->base = cpu_to_be64(0); /* XXX true? */ - - /* set all the bits past our final used blkno */ - super->core_balloc_free.total_free = - cpu_to_le64(SCOUTFS_BALLOC_ITEM_BITS - next_meta); - for (i = next_meta; i < SCOUTFS_BALLOC_ITEM_BITS; i++) - set_bit_le(i, &biv->bits); - next_meta = i; - - bt->free_end = bt->item_hdrs[le32_to_cpu(bt->nr_items) - 1].off; - - bt->hdr.magic = cpu_to_le32(SCOUTFS_BLOCK_MAGIC_BTREE); - bt->hdr.crc = cpu_to_le32(crc_block(&bt->hdr)); - - ret = write_raw_block(fd, blkno, bt); - if (ret) + /* write out radix allocator blocks for data */ + ret = write_radix_blocks(super, fd, &super->core_data_avail, next_meta, + next_data, last_data); + if (ret < 0) goto out; + next_meta += ret; + + super->core_data_freed.height = super->core_data_avail.height; + radix_init_ref(&super->core_data_freed.ref, 0, false); + + meta_alloc_blocks = radix_blocks_needed(next_meta, last_meta); + + /* + * Write out radix alloc blocks, knowing that the region we mark + * has to start after the blocks we store the allocator itself in. + */ + ret = write_radix_blocks(super, fd, &super->core_meta_avail, + next_meta, next_meta + meta_alloc_blocks, + last_meta); + if (ret < 0) + goto out; + next_meta += ret; + + super->core_meta_freed.height = super->core_meta_avail.height; + radix_init_ref(&super->core_meta_freed.ref, 0, false); /* zero out quorum blocks */ for (i = 0; i < SCOUTFS_QUORUM_BLOCKS; i++) { @@ -277,10 +457,6 @@ static int write_new_fs(char *path, int fd, u8 quorum_count) } /* fill out allocator fields now that we've written our blocks */ - super->next_uninit_meta_blkno = cpu_to_le64(next_meta); - super->last_uninit_meta_blkno = cpu_to_le64(last_meta); - super->next_uninit_data_blkno = cpu_to_le64(next_data); - super->last_uninit_data_blkno = cpu_to_le64(last_data); super->free_blocks = cpu_to_le64(total_blocks - next_meta); /* write the super block */ @@ -312,9 +488,9 @@ static int write_new_fs(char *path, int fd, u8 quorum_count) le64_to_cpu(super->format_hash), uuid_str, SIZE_ARGS(total_blocks, SCOUTFS_BLOCK_SIZE), - SIZE_ARGS(last_meta - next_meta + 1, + SIZE_ARGS(le64_to_cpu(super->total_meta_blocks), SCOUTFS_BLOCK_SIZE), - SIZE_ARGS(last_data - next_data + 1, + SIZE_ARGS(le64_to_cpu(super->total_data_blocks), SCOUTFS_BLOCK_SIZE), super->quorum_count); diff --git a/utils/src/print.c b/utils/src/print.c index dd2989c3..8a4a3e62 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -20,6 +20,7 @@ #include "cmd.h" #include "crc.h" #include "key.h" +#include "radix.h" static void *read_block(int fd, u64 blkno) { @@ -258,6 +259,18 @@ static int print_logs_item(void *key, unsigned key_len, void *val, return 0; } +#define RADREF_F \ + "blkno %llu seq %llu sm_total %llu lg_total %llu" +#define RADREF_A(ref) \ + le64_to_cpu((ref)->blkno), le64_to_cpu((ref)->seq), \ + le64_to_cpu((ref)->sm_total), le64_to_cpu((ref)->lg_total) + +#define RADROOT_F \ + "height %u next_find_bit %llu ref: "RADREF_F +#define RADROOT_A(root) \ + (root)->height, le64_to_cpu((root)->next_find_bit), \ + RADREF_A(&(root)->ref) + /* same as fs item but with a small header in the value */ static int print_log_trees_item(void *key, unsigned key_len, void *val, unsigned val_len, void *arg) @@ -270,33 +283,21 @@ static int print_log_trees_item(void *key, unsigned key_len, void *val, /* only items in leaf blocks have values */ if (val) { - printf(" alloc_root: total_free %llu root: height %u blkno %llu seq %llu\n" - " free_root: total_free %llu root: height %u blkno %llu seq %llu\n" + printf(" meta_avail: "RADROOT_F"\n" + " meta_freed: "RADROOT_F"\n" " item_root: height %u blkno %llu seq %llu\n" " bloom_ref: blkno %llu seq %llu\n" - " data_alloc: total_free %llu root: height %u blkno %llu seq %llu\n" - " data_free: total_free %llu root: height %u blkno %llu seq %llu\n", - le64_to_cpu(ltv->alloc_root.total_free), - ltv->alloc_root.root.height, - le64_to_cpu(ltv->alloc_root.root.ref.blkno), - le64_to_cpu(ltv->alloc_root.root.ref.seq), - le64_to_cpu(ltv->free_root.total_free), - ltv->free_root.root.height, - le64_to_cpu(ltv->free_root.root.ref.blkno), - le64_to_cpu(ltv->free_root.root.ref.seq), + " data_avail: "RADROOT_F"\n" + " data_freed: "RADROOT_F"\n", + RADROOT_A(<v->meta_avail), + RADROOT_A(<v->meta_freed), ltv->item_root.height, le64_to_cpu(ltv->item_root.ref.blkno), le64_to_cpu(ltv->item_root.ref.seq), le64_to_cpu(ltv->bloom_ref.blkno), le64_to_cpu(ltv->bloom_ref.seq), - le64_to_cpu(ltv->data_alloc.total_free), - ltv->data_alloc.root.height, - le64_to_cpu(ltv->data_alloc.root.ref.blkno), - le64_to_cpu(ltv->data_alloc.root.ref.seq), - le64_to_cpu(ltv->data_free.total_free), - ltv->data_free.root.height, - le64_to_cpu(ltv->data_free.root.ref.blkno), - le64_to_cpu(ltv->data_free.root.ref.seq)); + RADROOT_A(<v->data_avail), + RADROOT_A(<v->data_freed)); } return 0; @@ -323,31 +324,6 @@ static int print_trans_seqs_entry(void *key, unsigned key_len, void *val, return 0; } -static int print_balloc_entry(void *key, unsigned key_len, void *val, - unsigned val_len, void *arg) -{ - struct scoutfs_balloc_item_key *bik = key; -// struct scoutfs_balloc_item_val *biv = val; - - printf(" base %llu\n", - be64_to_cpu(bik->base)); - - return 0; -} - -static int print_bitmap_entry(void *key, unsigned key_len, void *val, - unsigned val_len, void *arg) -{ - struct scoutfs_block_bitmap_key *bbk = key; - struct scoutfs_packed_bitmap *pb = val; - - printf(" type %u base %llu present 0x%016llx set 0x%016llx\n", - bbk->type, be64_to_cpu(bbk->base), - le64_to_cpu(pb->present), le64_to_cpu(pb->set)); - - return 0; -} - static int print_mounted_client_entry(void *key, unsigned key_len, void *val, unsigned val_len, void *arg) { @@ -460,6 +436,71 @@ static int print_btree(int fd, struct scoutfs_super_block *super, char *which, return ret; } +static int print_radix_block(int fd, struct scoutfs_radix_ref *par, int level) +{ + struct scoutfs_radix_block *rdx; + u64 blkno; + int prev; + int ret; + int err; + int i; + + /* XXX not printing bitmap leaf blocks */ + blkno = le64_to_cpu(par->blkno); + if (blkno == 0 || blkno == U64_MAX || level == 0) + return 0; + + rdx = read_block(fd, le64_to_cpu(par->blkno)); + if (!rdx) { + ret = -ENOMEM; + goto out; + } + + printf("radix parent block blkno %llu\n", le64_to_cpu(par->blkno)); + print_block_header(&rdx->hdr); + printf(" sm_first %u lg_first %u\n", + le32_to_cpu(rdx->sm_first), le32_to_cpu(rdx->lg_first)); + + prev = 0; + for (i = 0; i < SCOUTFS_RADIX_REFS; i++) { + /* only skip if the next ref is identically full/empty */ + if ((le64_to_cpu(rdx->refs[i].blkno) == 0 || + le64_to_cpu(rdx->refs[i].blkno) == U64_MAX) && + (i + 1) < SCOUTFS_RADIX_REFS && + (le64_to_cpu(rdx->refs[i].blkno) == + le64_to_cpu(rdx->refs[i + 1].blkno))) { + prev++; + continue; + } + + if (prev) { + printf(" [%u - %u]: (%s): ", i - prev, i, + (le64_to_cpu(rdx->refs[i].blkno) == 0) ? "empty" : + "full"); + prev = 0; + } else { + printf(" [%u]: ", i); + } + + printf(RADREF_F"\n", RADREF_A(&rdx->refs[i])); + } + + ret = 0; + for (i = 0; i < SCOUTFS_RADIX_REFS; i++) { + if (le64_to_cpu(rdx->refs[i].blkno) != 0 && + le64_to_cpu(rdx->refs[i].blkno) != U64_MAX) { + err = print_radix_block(fd, &rdx->refs[i], level - 1); + if (err < 0 && ret == 0) + ret = err; + } + } + +out: + free(rdx); + + return ret; +} + struct print_recursion_args { struct scoutfs_super_block *super; int fd; @@ -469,52 +510,35 @@ struct print_recursion_args { static int print_log_trees_roots(void *key, unsigned key_len, void *val, unsigned val_len, void *arg) { - struct scoutfs_log_trees_key *ltk = key; +// struct scoutfs_log_trees_key *ltk = key; struct scoutfs_log_trees_val *ltv = val; struct print_recursion_args *pa = arg; - struct log_trees_roots { - char *fmt; - struct scoutfs_btree_root *root; - print_item_func func; - } roots[] = { - { "log_tree_rid:%llu_nr:%llu_alloc", - <v->alloc_root.root, - print_balloc_entry, - }, - { "log_tree_rid:%llu_nr:%llu_free", - <v->free_root.root, - print_balloc_entry, - }, - { "log_tree_rid:%llu_nr:%llu_data_alloc", - <v->data_alloc.root, - print_bitmap_entry, - }, - { "log_tree_rid:%llu_nr:%llu_data_free", - <v->data_free.root, - print_bitmap_entry, - }, - { "log_tree_rid:%llu_nr:%llu_item", - <v->item_root, - print_logs_item, - }, - }; - char which[100]; - int ret; + int ret = 0; int err; - int i; /* XXX doesn't print the bloom block */ - ret = 0; - for (i = 0; i < array_size(roots); i++) { - snprintf(which, sizeof(which) - 1, roots[i].fmt, - be64_to_cpu(ltk->rid), be64_to_cpu(ltk->nr)); + err = print_radix_block(pa->fd, <v->meta_avail.ref, + ltv->meta_avail.height - 1); + if (err && !ret) + ret = err; + err = print_radix_block(pa->fd, <v->meta_freed.ref, + ltv->meta_avail.height - 1); + if (err && !ret) + ret = err; + err = print_radix_block(pa->fd, <v->data_avail.ref, + ltv->data_avail.height - 1); + if (err && !ret) + ret = err; + err = print_radix_block(pa->fd, <v->meta_freed.ref, + ltv->data_avail.height - 1); + if (err && !ret) + ret = err; - err = print_btree(pa->fd, pa->super, which, roots[i].root, - roots[i].func, NULL); - if (err && !ret) - ret = err; - } + err = print_btree(pa->fd, pa->super, "", <v->item_root, + print_logs_item, NULL); + if (err && !ret) + ret = err; return ret; } @@ -657,51 +681,37 @@ 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_trans_seq %llu\n" - " total_blocks %llu free_blocks %llu\n" - " next_uninit_meta_blkno %llu last_uninit_meta_blkno %llu\n" - " next_uninit_data_blkno %llu last_uninit_data_blkno %llu\n" - " core_balloc_cursor %llu core_data_alloc_cursor %llu\n" + " total_meta_blocks %llu first_meta_blkno %llu last_meta_blkno %llu\n" + " total_data_blocks %llu first_data_blkno %llu last_data_blkno %llu\n" + " free_blocks %llu\n" " quorum_fenced_term %llu quorum_server_term %llu unmount_barrier %llu\n" " quorum_count %u server_addr %s\n" - " core_balloc_alloc: total_free %llu root: height %u blkno %llu seq %llu\n" - " core_balloc_free: total_free %llu root: height %u blkno %llu seq %llu\n" - " core_data_alloc: total_free %llu root: height %u blkno %llu seq %llu\n" - " core_data_free: total_free %llu root: height %u blkno %llu seq %llu\n" + " core_meta_avail: "RADROOT_F"\n" + " core_meta_freed: "RADROOT_F"\n" + " core_data_avail: "RADROOT_F"\n" + " core_data_freed: "RADROOT_F"\n" " lock_clients root: height %u blkno %llu seq %llu\n" " mounted_clients root: height %u blkno %llu seq %llu\n" " trans_seqs root: height %u blkno %llu seq %llu\n" " fs_root btree root: height %u blkno %llu seq %llu\n", le64_to_cpu(super->next_ino), le64_to_cpu(super->next_trans_seq), - le64_to_cpu(super->total_blocks), + le64_to_cpu(super->total_meta_blocks), + le64_to_cpu(super->first_meta_blkno), + le64_to_cpu(super->last_meta_blkno), + le64_to_cpu(super->total_data_blocks), + le64_to_cpu(super->first_data_blkno), + le64_to_cpu(super->last_data_blkno), le64_to_cpu(super->free_blocks), - le64_to_cpu(super->next_uninit_meta_blkno), - le64_to_cpu(super->last_uninit_meta_blkno), - le64_to_cpu(super->next_uninit_data_blkno), - le64_to_cpu(super->last_uninit_data_blkno), - le64_to_cpu(super->core_balloc_cursor), - le64_to_cpu(super->core_data_alloc_cursor), le64_to_cpu(super->quorum_fenced_term), le64_to_cpu(super->quorum_server_term), le64_to_cpu(super->unmount_barrier), super->quorum_count, server_addr, - le64_to_cpu(super->core_balloc_alloc.total_free), - super->core_balloc_alloc.root.height, - le64_to_cpu(super->core_balloc_alloc.root.ref.blkno), - le64_to_cpu(super->core_balloc_alloc.root.ref.seq), - le64_to_cpu(super->core_balloc_free.total_free), - super->core_balloc_free.root.height, - le64_to_cpu(super->core_balloc_free.root.ref.blkno), - le64_to_cpu(super->core_balloc_free.root.ref.seq), - le64_to_cpu(super->core_data_alloc.total_free), - super->core_data_alloc.root.height, - le64_to_cpu(super->core_data_alloc.root.ref.blkno), - le64_to_cpu(super->core_data_alloc.root.ref.seq), - le64_to_cpu(super->core_data_free.total_free), - super->core_data_free.root.height, - le64_to_cpu(super->core_data_free.root.ref.blkno), - le64_to_cpu(super->core_data_free.root.ref.seq), + RADROOT_A(&super->core_meta_avail), + RADROOT_A(&super->core_meta_freed), + RADROOT_A(&super->core_data_avail), + RADROOT_A(&super->core_data_freed), super->lock_clients.height, le64_to_cpu(super->lock_clients.ref.blkno), le64_to_cpu(super->lock_clients.ref.seq), @@ -748,27 +758,20 @@ static int print_volume(int fd) if (err && !ret) ret = err; - err = print_btree(fd, super, "core_balloc_alloc", - &super->core_balloc_alloc.root, - print_balloc_entry, NULL); + err = print_radix_block(fd, &super->core_meta_avail.ref, + super->core_meta_avail.height - 1); if (err && !ret) ret = err; - - err = print_btree(fd, super, "core_balloc_free", - &super->core_balloc_free.root, - print_balloc_entry, NULL); + err = print_radix_block(fd, &super->core_meta_freed.ref, + super->core_meta_freed.height - 1); if (err && !ret) ret = err; - - err = print_btree(fd, super, "core_data_alloc", - &super->core_data_alloc.root, - print_bitmap_entry, NULL); + err = print_radix_block(fd, &super->core_data_avail.ref, + super->core_data_avail.height - 1); if (err && !ret) ret = err; - - err = print_btree(fd, super, "core_data_free", - &super->core_data_free.root, - print_bitmap_entry, NULL); + err = print_radix_block(fd, &super->core_data_freed.ref, + super->core_data_freed.height - 1); if (err && !ret) ret = err; diff --git a/utils/src/radix.c b/utils/src/radix.c new file mode 100644 index 00000000..66a400a3 --- /dev/null +++ b/utils/src/radix.c @@ -0,0 +1,106 @@ +#include + +#include "sparse.h" +#include "util.h" +#include "format.h" +#include "radix.h" + +/* return the height of a tree needed to store the last bit */ +u8 radix_height_from_last(u64 last) +{ + u64 bit = SCOUTFS_RADIX_BITS - 1; + u64 mult = SCOUTFS_RADIX_BITS; + int i; + + for (i = 1; i <= U8_MAX; i++) { + if (bit >= last) + return i; + bit += (u64)(SCOUTFS_RADIX_REFS - 1) * mult; + mult *= SCOUTFS_RADIX_REFS; + } + + return U8_MAX; +} + +u64 radix_full_subtree_total(int level) +{ + u64 total = SCOUTFS_RADIX_BITS; + int i; + + for (i = 1; i <= level; i++) + total *= SCOUTFS_RADIX_REFS; + + return total; +} + +/* + * Initialize a reference to a block at the given level. + */ +void radix_init_ref(struct scoutfs_radix_ref *ref, int level, bool full) +{ + u64 tot; + + if (full) { + tot = radix_full_subtree_total(level); + + ref->blkno = cpu_to_le64(U64_MAX); + ref->seq = cpu_to_le64(0); + ref->sm_total = cpu_to_le64(tot); + ref->lg_total = cpu_to_le64(tot); + } else { + ref->blkno = cpu_to_le64(0); + ref->seq = cpu_to_le64(0); + ref->sm_total = cpu_to_le64(0); + ref->lg_total = cpu_to_le64(0); + } +} + +void radix_calc_level_inds(int *inds, u8 height, u64 bit) +{ + u32 ind; + int i; + + ind = bit % SCOUTFS_RADIX_BITS; + bit = bit / SCOUTFS_RADIX_BITS; + inds[0] = ind; + + for (i = 1; i < height; i++) { + ind = bit % SCOUTFS_RADIX_REFS; + bit = bit / SCOUTFS_RADIX_REFS; + inds[i] = ind; + } +} + +u64 radix_calc_leaf_bit(u64 bit) +{ + return bit - (bit % SCOUTFS_RADIX_BITS); +} + +/* + * The number of blocks needed to initialize a radix with left and right + * paths. The first time we find a level where the parent refs are at + * different indices determines where the paths diverge at lower levels. + * If the refs never diverge then the two paths traverse the same blocks + * and we just need blocks for the height of the tree. + */ +int radix_blocks_needed(u64 a, u64 b) +{ + u8 height = radix_height_from_last(b); + int *a_inds; + int *b_inds; + int i; + + a_inds = alloca(sizeof(a_inds[0] * height)); + b_inds = alloca(sizeof(b_inds[0] * height)); + + radix_calc_level_inds(a_inds, height, a); + radix_calc_level_inds(b_inds, height, b); + + for (i = height - 1; i > 0; i--) { + if (a_inds[i] != b_inds[i]) { + return (i * 2) + (height - i); + } + } + + return height; +} diff --git a/utils/src/radix.h b/utils/src/radix.h new file mode 100644 index 00000000..31f4db8c --- /dev/null +++ b/utils/src/radix.h @@ -0,0 +1,13 @@ +#ifndef _RADIX_H_ +#define _RADIX_H_ + +#include + +u8 radix_height_from_last(u64 last); +u64 radix_full_subtree_total(int level); +void radix_init_ref(struct scoutfs_radix_ref *ref, int level, bool full); +void radix_calc_level_inds(int *inds, u8 height, u64 bit); +u64 radix_calc_leaf_bit(u64 bit); +int radix_blocks_needed(u64 a, u64 b); + +#endif From 6b66e583f279743ba307e51e0c5bfb3fa3b9e5cb Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Fri, 21 Feb 2020 11:37:14 -0800 Subject: [PATCH 189/235] scoutfs-utils: fix printing block hdr fields The block header printing helper had the identifiers for the blkno and seq in the format string swapped. Signed-off-by: Zach Brown --- utils/src/print.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/src/print.c b/utils/src/print.c index 8a4a3e62..b755d65d 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -52,7 +52,7 @@ static void print_block_header(struct scoutfs_block_header *hdr) else valid_str[0] = '\0'; - printf(" hdr: crc %08x %smagic %08x fsid %llx seq %llu blkno %llu\n", + printf(" hdr: crc %08x %smagic %08x fsid %llx blkno %llu seq %llu\n", le32_to_cpu(hdr->crc), valid_str, le32_to_cpu(hdr->magic), le64_to_cpu(hdr->fsid), le64_to_cpu(hdr->blkno), le64_to_cpu(hdr->seq)); From ec782fff8dd7a1407e197c830fd400911583345b Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Sun, 23 Feb 2020 20:12:40 -0800 Subject: [PATCH 190/235] scoutfs-utils: meta and data free blocks The super block now tracks free metadata and data blocks in separate counters. Signed-off-by: Zach Brown --- utils/src/format.h | 3 ++- utils/src/mkfs.c | 3 ++- utils/src/print.c | 8 ++++---- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index 6cfb7322..42eecc90 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -481,10 +481,11 @@ struct scoutfs_super_block { __le64 total_meta_blocks; /* both static and dynamic */ __le64 first_meta_blkno; /* first dynamically allocated */ __le64 last_meta_blkno; + __le64 free_meta_blocks; __le64 total_data_blocks; __le64 first_data_blkno; __le64 last_data_blkno; - __le64 free_blocks; + __le64 free_data_blocks; __le64 quorum_fenced_term; __le64 quorum_server_term; __le64 unmount_barrier; diff --git a/utils/src/mkfs.c b/utils/src/mkfs.c index 63a97aec..ac3393f6 100644 --- a/utils/src/mkfs.c +++ b/utils/src/mkfs.c @@ -457,7 +457,8 @@ static int write_new_fs(char *path, int fd, u8 quorum_count) } /* fill out allocator fields now that we've written our blocks */ - super->free_blocks = cpu_to_le64(total_blocks - next_meta); + super->free_meta_blocks = cpu_to_le64(last_meta - next_meta + 1); + super->free_data_blocks = cpu_to_le64(last_data - next_data + 1); /* write the super block */ super->hdr.seq = cpu_to_le64(1); diff --git a/utils/src/print.c b/utils/src/print.c index b755d65d..29c3ff3b 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -681,9 +681,8 @@ 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_trans_seq %llu\n" - " total_meta_blocks %llu first_meta_blkno %llu last_meta_blkno %llu\n" - " total_data_blocks %llu first_data_blkno %llu last_data_blkno %llu\n" - " free_blocks %llu\n" + " total_meta_blocks %llu first_meta_blkno %llu last_meta_blkno %llu free_meta_blocks %llu\n" + " total_data_blocks %llu first_data_blkno %llu last_data_blkno %llu free_data_blocks %llu\n" " quorum_fenced_term %llu quorum_server_term %llu unmount_barrier %llu\n" " quorum_count %u server_addr %s\n" " core_meta_avail: "RADROOT_F"\n" @@ -699,10 +698,11 @@ static void print_super_block(struct scoutfs_super_block *super, u64 blkno) le64_to_cpu(super->total_meta_blocks), le64_to_cpu(super->first_meta_blkno), le64_to_cpu(super->last_meta_blkno), + le64_to_cpu(super->free_meta_blocks), le64_to_cpu(super->total_data_blocks), le64_to_cpu(super->first_data_blkno), le64_to_cpu(super->last_data_blkno), - le64_to_cpu(super->free_blocks), + le64_to_cpu(super->free_data_blocks), le64_to_cpu(super->quorum_fenced_term), le64_to_cpu(super->quorum_server_term), le64_to_cpu(super->unmount_barrier), From 53f29d3f2a8a2699d3e4a5a6cd0af79bbaf2bb3b Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Mon, 2 Mar 2020 11:44:23 -0800 Subject: [PATCH 191/235] scoutfs-utils: add ilog2() helper It's handy to use ilog2 in the format header for defining shifts based on values. Add a userspace helper that uses glibc's log2 functions. Signed-off-by: Zach Brown --- utils/src/util.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/utils/src/util.h b/utils/src/util.h index b54fd147..d7fa787e 100644 --- a/utils/src/util.h +++ b/utils/src/util.h @@ -4,6 +4,7 @@ #include #include #include +#include /* * Generate build warnings if the condition is false but generate no @@ -79,6 +80,11 @@ do { \ (_x == 0 ? 0 : 64 - __builtin_clzll(_x)); \ }) +#define ilog2(x) \ +({ \ + ((unsigned long)log2l((long double)x)); \ +}) + /* * return -1,0,+1 based on the memcmp comparison of the minimum of their * two lengths. If their min shared bytes are equal but the lengths From 91c64dfa2d9cf55324ffc8be638f1111b856638e Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Mon, 2 Mar 2020 11:20:12 -0800 Subject: [PATCH 192/235] scoutfs-utils: print packed extents Add support for printing the invidual extents stored in packed extent items. Signed-off-by: Zach Brown --- utils/src/print.c | 66 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 61 insertions(+), 5 deletions(-) diff --git a/utils/src/print.c b/utils/src/print.c index 29c3ff3b..01f0c284 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -147,12 +147,68 @@ static void print_symlink(struct scoutfs_key *key, void *val, int val_len) static void print_packed_extent(struct scoutfs_key *key, void *val, int val_len) { - struct scoutfs_packed_extent *pe = val; + struct scoutfs_packed_extent *pe; + __le64 led; + u64 iblock; + u64 blkno = 0; + u64 diff; + int off = 0; + int i = 0; - printf(" packed_extent: ino %llu base %llu part %u count %u diff_bytes %u flags 0x%x final %u\n", - le64_to_cpu(key->skpe_ino), le64_to_cpu(key->skpe_base), - key->skpe_part, le16_to_cpu(pe->count), pe->diff_bytes, - pe->flags, pe->final); + + /* + * Ugh, this is the only item that has state between items. It + * probably shouldn't. And I'm too lazy to plumb an arg through + * all the printers. + */ + static struct scoutfs_key next_key; + static u64 next_blkno; + + if (scoutfs_key_compare(key, &next_key) == 0) + blkno = next_blkno; + + iblock = le64_to_cpu(key->skpe_base) << SCOUTFS_PACKEXT_BASE_SHIFT; + + while (off < val_len) { + printf(" [%u] off %u: ibl %llu ", i, off, iblock); + + if (off + sizeof(struct scoutfs_packed_extent) > val_len) { + printf("(packed extent struct exceeds item)\n"); + return; + } + + pe = val + off; + printf("cnt %u dfb %u fl %x fin %u ", + le16_to_cpu(pe->count), pe->diff_bytes, pe->flags, + pe->final); + + off += sizeof(struct scoutfs_packed_extent); + + if (off + pe->diff_bytes > val_len) { + printf("(packed extent diff bytes exceeds item)\n"); + return; + } + + if (pe->diff_bytes) { + led = 0; + memcpy(&led, pe->le_blkno_diff, pe->diff_bytes); + diff = le64_to_cpu(led); + diff = (diff >> 1) ^ (-(diff & 1)); + blkno += diff; + printf("dif %lld blk %llu\n", (s64)diff, blkno); + blkno += le16_to_cpu(pe->count) - 1; + } else { + printf("(sparse)\n"); + } + + iblock += le16_to_cpu(pe->count); + off += pe->diff_bytes; + i++; + } + + next_blkno = blkno; + next_key = *key; + scoutfs_key_inc(&next_key); } static void print_inode_index(struct scoutfs_key *key, void *val, int val_len) From 3c7d1f3935b2352ec043f472d5560f23ea00aa47 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Wed, 4 Mar 2020 14:50:49 -0800 Subject: [PATCH 193/235] scoutfs-utils: quick forest bloom comment update Signed-off-by: Zach Brown --- utils/src/format.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index 42eecc90..f8b777c9 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -328,11 +328,11 @@ struct scoutfs_bloom_block { } __packed; /* - * Log trees include a tree of items that make up a fixed size bloom - * filter. Just a few megs worth of items lets us test for the presence - * of locks that cover billions of files with a .1% chance of false - * positives. The log trees should be finalized and merged long before - * the bloom filters fill up and start returning excessive false positives. + * Item log trees are accompanied by a block of bits that make up a + * bloom filter which indicate if the item log trees may contain items + * covered by a lock. The log trees should be finalized and merged long + * before the bloom filters fill up and start returning excessive false + * positives. */ #define SCOUTFS_FOREST_BLOOM_NRS 7 #define SCOUTFS_FOREST_BLOOM_BITS \ From 247e22f56ffecd916a1e59fd601fd5e639093aea Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Wed, 4 Mar 2020 14:51:07 -0800 Subject: [PATCH 194/235] scoutfs-utils: remove unused corruption sources Remove the definitions and descriptions of sources of corruption that are no longer identified by the kernel module. Signed-off-by: Zach Brown --- utils/man/scoutfs-corruption.7 | 47 ---------------------------------- utils/src/format.h | 6 ----- 2 files changed, 53 deletions(-) diff --git a/utils/man/scoutfs-corruption.7 b/utils/man/scoutfs-corruption.7 index 980c1af1..99e797b3 100644 --- a/utils/man/scoutfs-corruption.7 +++ b/utils/man/scoutfs-corruption.7 @@ -163,53 +163,6 @@ than the last child reference's key. .BR cmp " - comparison of search key and found" .sp -.TP -.B SC_EXTENT_ADD_CLEANUP, SC_EXTENT_REM_CLEANUP, SC_DATA_EXTENT_TRUNC_CLEANUP, SC_DATA_EXTENT_ALLOC_CLEANUP, SC_DATA_EXTENT_FALLOCATE_CLEANUP, SC_SERVER_EXTENT_CLEANUP - -Extents are used to track regions of blocks or files. The process of -modifying an extent creates and destroys intermediate extents, for -example as two disjoint extents are merged with a third that is created -between the two. If an error occurs during this process the -intermediate extents must be returned to the original state. If an -error occurs during this cleanup process then the resulting extents, -taken as a whole, can be inconsistent. - -They can describe overlapping regions. They can forget a region that was -previously described. The consequences of these inconsistencies depend -on the extent type. - -The -.I -_EXTENT_ -cases occur as core library code is modifying extents. It can happen on -behalf of both file data extents and free extents and while adding or -removing extents. - -The -.I -_DATA_EXTENT_ -cases occur in file mapping extents while truncating (removing) -extents from a file, while allocating extents for a newly written -region of a file, or while using fallocate to pre-allocate extents -to the file. - -The -.I -_SERVER_EXTENT_ -case occurs as the server is tracking free extents on behalf of all -nodes. - -Each corruption type message describes the extent and operation. - -.BR clean " - extent that was being cleaned up after an error" -.br -.BR ext " - primary extent that was being operated on before the error" -.br -.BR ret " - negative errno of the first error encountered" -.br -.BR op " - the operation the server was performing on the extent" -.sp - .SH AUTHORS Zach Brown diff --git a/utils/src/format.h b/utils/src/format.h index f8b777c9..2d295d20 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -805,12 +805,6 @@ enum { SC_BTREE_BLOCK_LEVEL, SC_BTREE_NO_CHILD_REF, SC_INODE_BLOCK_COUNTS, - SC_EXTENT_ADD_CLEANUP, - SC_EXTENT_REM_CLEANUP, - SC_DATA_EXTENT_TRUNC_CLEANUP, - SC_DATA_EXTENT_ALLOC_CLEANUP, - SC_SERVER_EXTENT_CLEANUP, - SC_DATA_EXTENT_FALLOCATE_CLEANUP, SC_NR_SOURCES, }; From 0a8faf3e942255f9b1b4431ac3f051a6a27215a2 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Tue, 26 May 2020 14:31:37 -0700 Subject: [PATCH 195/235] scoutfs-utils: add parse_s64() Signed-off-by: Zach Brown --- utils/src/parse.c | 19 +++++++++++++++++++ utils/src/parse.h | 1 + 2 files changed, 20 insertions(+) diff --git a/utils/src/parse.c b/utils/src/parse.c index 302dc762..720e3a25 100644 --- a/utils/src/parse.c +++ b/utils/src/parse.c @@ -29,6 +29,25 @@ int parse_u64(char *str, u64 *val_ret) return 0; } +int parse_s64(char *str, s64 *val_ret) +{ + long long ll; + char *endptr = NULL; + + ll = strtoll(str, &endptr, 0); + if (*endptr != '\0' || + ((ll == LLONG_MIN || ll == LLONG_MAX) && + errno == ERANGE)) { + fprintf(stderr, "invalid 64bit value: '%s'\n", str); + *val_ret = 0; + return -EINVAL; + } + + *val_ret = ll; + + return 0; +} + int parse_u32(char *str, u32 *val_ret) { u64 val; diff --git a/utils/src/parse.h b/utils/src/parse.h index ad25f879..a3ffc48d 100644 --- a/utils/src/parse.h +++ b/utils/src/parse.h @@ -4,6 +4,7 @@ #include int parse_u64(char *str, u64 *val_ret); +int parse_s64(char *str, s64 *val_ret); int parse_u32(char *str, u32 *val_ret); int parse_timespec(char *str, struct timespec *ts); From 79e235af6eba2289be4ab535daf3653b6dcb2c16 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Tue, 26 May 2020 14:32:55 -0700 Subject: [PATCH 196/235] scoutfs-utils: fix argv typo in error message The data_waiting ioctl used the wrong argv index when printing the path arg string that it failed to open. Signed-off-by: Zach Brown --- utils/src/waiting.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/src/waiting.c b/utils/src/waiting.c index 6e916de0..72f32b17 100644 --- a/utils/src/waiting.c +++ b/utils/src/waiting.c @@ -67,7 +67,7 @@ static int waiting_cmd(int argc, char **argv) if (fd < 0) { ret = -errno; fprintf(stderr, "failed to open '%s': %s (%d)\n", - argv[4], strerror(errno), errno); + argv[3], strerror(errno), errno); return ret; } From 74f85ff93da6848a30f097a306d87500a45a89bf Mon Sep 17 00:00:00 2001 From: Benjamin LaHaise Date: Wed, 20 May 2020 16:19:45 -0400 Subject: [PATCH 197/235] scoutfs: add data_wait_err for reporting errors Add support for reporting errors to data waiters via a new SCOUTFS_IOC_DATA_WAIT_ERR ioctl. This allows waiters to return an error to readers when staging fails. Signed-off-by: Benjamin LaHaise [zab: renamed to data_wait_err, took ino arg] Signed-off-by: Zach Brown --- utils/src/ioctl.h | 17 +++++++++ utils/src/sparse.h | 1 + utils/src/waiting.c | 90 ++++++++++++++++++++++++++++++++++++--------- 3 files changed, 90 insertions(+), 18 deletions(-) diff --git a/utils/src/ioctl.h b/utils/src/ioctl.h index df0c1b54..4b635f88 100644 --- a/utils/src/ioctl.h +++ b/utils/src/ioctl.h @@ -346,5 +346,22 @@ struct scoutfs_ioctl_statfs_more { #define SCOUTFS_IOC_STATFS_MORE _IOR(SCOUTFS_IOCTL_MAGIC, 10, \ struct scoutfs_ioctl_statfs_more) +/* + * Cause matching waiters to return an error. + * + * Find current waiters that match the inode, op, and block range to wake + * up and return an error. + */ +struct scoutfs_ioctl_data_wait_err { + __u64 ino; + __u64 data_version; + __u64 offset; + __u64 count; + __u64 op; + __s64 err; +}; + +#define SCOUTFS_IOC_DATA_WAIT_ERR _IOR(SCOUTFS_IOCTL_MAGIC, 11, \ + struct scoutfs_ioctl_data_wait_err) #endif diff --git a/utils/src/sparse.h b/utils/src/sparse.h index 16fddc54..6cd08345 100644 --- a/utils/src/sparse.h +++ b/utils/src/sparse.h @@ -33,6 +33,7 @@ typedef u16 __u16; typedef u32 __u32; typedef s32 __s32; typedef u64 __u64; +typedef s64 __s64; typedef u16 __sp_biwise __le16; typedef u16 __sp_biwise __be16; diff --git a/utils/src/waiting.c b/utils/src/waiting.c index 72f32b17..414a3c4c 100644 --- a/utils/src/waiting.c +++ b/utils/src/waiting.c @@ -14,25 +14,11 @@ #include "format.h" #include "ioctl.h" #include "cmd.h" +#include "parse.h" -static int parse_u64(char *str, u64 *val_ret) -{ - unsigned long long ull; - char *endptr = NULL; - - ull = strtoull(str, &endptr, 0); - if (*endptr != '\0' || - ((ull == LLONG_MIN || ull == LLONG_MAX) && - errno == ERANGE)) { - fprintf(stderr, "invalid 64bit value: '%s'\n", str); - *val_ret = 0; - return -EINVAL; - } - - *val_ret = ull; - - return 0; -} +#ifndef MAX_ERRNO +#define MAX_ERRNO 4095 +#endif #define OP_FMT "%s%s" @@ -110,3 +96,71 @@ static void __attribute__((constructor)) waiting_ctor(void) cmd_register("data-waiting", " ", "print ops waiting for data blocks", waiting_cmd); } + +static int data_wait_err_cmd(int argc, char **argv) +{ + struct scoutfs_ioctl_data_wait_err args; + int fd = -1; + int ret; + + memset(&args, 0, sizeof(args)); + + if (argc != 8) { + fprintf(stderr, "must specify path, ino, version, offset, count,op, and err\n"); + return -EINVAL; + } + + ret = parse_u64(argv[2], &args.ino) ?: + parse_u64(argv[3], &args.data_version) ?: + parse_u64(argv[4], &args.offset) ?: + parse_u64(argv[5], &args.count) ?: + parse_s64(argv[7], &args.err); + if (ret) + return ret; + + if ((args.err >= 0) || (args.err < -MAX_ERRNO)) { + fprintf(stderr, "err %lld invalid\n", args.err); + ret = -EINVAL; + goto out; + } + + if (!strcmp(argv[6], "read")) { + args.op = SCOUTFS_IOC_DWO_READ; + } else if (!strcmp(argv[6], "write")) { + args.op = SCOUTFS_IOC_DWO_WRITE; + } else if (!strcmp(argv[6], "change_size")) { + args.op = SCOUTFS_IOC_DWO_CHANGE_SIZE; + } else { + fprintf(stderr, "invalid data wait op: '%s'\n", argv[6]); + return -EINVAL; + } + + fd = open(argv[1], O_RDONLY); + if (fd < 0) { + ret = -errno; + fprintf(stderr, "failed to open '%s': %s (%d)\n", + argv[1], strerror(errno), errno); + return ret; + } + + ret = ioctl(fd, SCOUTFS_IOC_DATA_WAIT_ERR, &args); + if (ret < 0) { + fprintf(stderr, "data_wait_err returned %d: error %s (%d)\n", + ret, strerror(errno), errno); + ret = -EIO; + goto out; + } + printf("data_wait_err found %d waiters.\n", ret); + +out: + if (fd > -1) + close(fd); + return ret; +}; + +static void __attribute__((constructor)) data_wait_err_ctor(void) +{ + cmd_register("data-wait-err", " ", + "return error from matching waiters", + data_wait_err_cmd); +} From 4e546b2e7cba337c5de86c621506e7b327b62ac6 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Thu, 30 Apr 2020 12:04:22 -0700 Subject: [PATCH 198/235] scoutfs-utils: generate end_size_add_cpu() We had manually implemented a few of the functions to add values to specific endian types. Make a macro to generate the function and generate them for all the endian types we use. Signed-off-by: Zach Brown --- utils/src/sparse.h | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/utils/src/sparse.h b/utils/src/sparse.h index 6cd08345..567eaf2f 100644 --- a/utils/src/sparse.h +++ b/utils/src/sparse.h @@ -105,19 +105,17 @@ __gen_functions(cast, be) #error "machine is neither BIG_ENDIAN nor LITTLE_ENDIAN" #endif -static inline void le32_add_cpu(__le32 *val, u32 delta) -{ - *val = cpu_to_le32(le32_to_cpu(*val) + delta); +#define __gen_add_funcs(end, size) \ +static inline void end##size##_add_cpu(__##end##size *val, u##size delta) \ +{ \ + *val = cpu_to_##end##size(end##size##_to_cpu(*val) + delta); \ } -static inline void le64_add_cpu(__le64 *val, u64 delta) -{ - *val = cpu_to_le64(le64_to_cpu(*val) + delta); -} - -static inline void be64_add_cpu(__be64 *val, u64 delta) -{ - *val = cpu_to_be64(be64_to_cpu(*val) + delta); -} +__gen_add_funcs(le, 16) +__gen_add_funcs(le, 32) +__gen_add_funcs(le, 64) +__gen_add_funcs(be, 16) +__gen_add_funcs(be, 32) +__gen_add_funcs(be, 64) #endif From ac2d465b661f2f423f128bab59f8647498225b7a Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Wed, 29 Apr 2020 17:28:54 -0700 Subject: [PATCH 199/235] scoutfs-utils: print key zone and type numerically The kernel has long sinced moved away from symbolic printing of key cones and types, and it just removed the MAX values from the format header. Let's follow suit and get rid of the zone and type strings. Signed-off-by: Zach Brown --- utils/src/format.h | 4 ---- utils/src/key.c | 55 ---------------------------------------------- utils/src/key.h | 30 +++---------------------- 3 files changed, 3 insertions(+), 86 deletions(-) delete mode 100644 utils/src/key.c diff --git a/utils/src/format.h b/utils/src/format.h index 2d295d20..493a4ae3 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -348,7 +348,6 @@ struct scoutfs_bloom_block { #define SCOUTFS_RID_ZONE 3 #define SCOUTFS_FS_ZONE 4 #define SCOUTFS_LOCK_ZONE 5 -#define SCOUTFS_MAX_ZONE 8 /* power of 2 is efficient */ /* inode index zone */ #define SCOUTFS_INODE_INDEX_META_SEQ_TYPE 1 @@ -373,9 +372,6 @@ struct scoutfs_bloom_block { /* lock zone, only ever found in lock ranges, never in persistent items */ #define SCOUTFS_RENAME_TYPE 1 -#define SCOUTFS_MAX_TYPE 8 /* power of 2 is efficient */ - - /* * The extents that map blocks in a fixed-size logical region of a file * are packed and stored in item values. The packed extents are diff --git a/utils/src/key.c b/utils/src/key.c deleted file mode 100644 index e5c7a826..00000000 --- a/utils/src/key.c +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2018 Versity Software, Inc. All rights reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public - * License v2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - */ -#include -#include -#include - -#include "sparse.h" -#include "util.h" -#include "format.h" -#include "key.h" - -char *scoutfs_zone_strings[SCOUTFS_MAX_ZONE] = { - [SCOUTFS_INODE_INDEX_ZONE] = "ind", - [SCOUTFS_XATTR_INDEX_ZONE] = "xnd", - [SCOUTFS_RID_ZONE] = "rid", - [SCOUTFS_FS_ZONE] = "fs", -}; - -char *scoutfs_type_strings[SCOUTFS_MAX_ZONE][SCOUTFS_MAX_TYPE] = { - [SCOUTFS_INODE_INDEX_ZONE][SCOUTFS_INODE_INDEX_META_SEQ_TYPE] = "msq", - [SCOUTFS_INODE_INDEX_ZONE][SCOUTFS_INODE_INDEX_DATA_SEQ_TYPE] = "dsq", - [SCOUTFS_XATTR_INDEX_ZONE][SCOUTFS_XATTR_INDEX_NAME_TYPE] = "nam", - [SCOUTFS_RID_ZONE][SCOUTFS_ORPHAN_TYPE] = "orp", - [SCOUTFS_FS_ZONE][SCOUTFS_INODE_TYPE] = "ino", - [SCOUTFS_FS_ZONE][SCOUTFS_XATTR_TYPE] = "xat", - [SCOUTFS_FS_ZONE][SCOUTFS_DIRENT_TYPE] = "dnt", - [SCOUTFS_FS_ZONE][SCOUTFS_READDIR_TYPE] = "rdr", - [SCOUTFS_FS_ZONE][SCOUTFS_LINK_BACKREF_TYPE] = "lbr", - [SCOUTFS_FS_ZONE][SCOUTFS_SYMLINK_TYPE] = "sym", - [SCOUTFS_FS_ZONE][SCOUTFS_PACKED_EXTENT_TYPE] = "pex", -}; - -char scoutfs_unknown_u8_strings[U8_MAX][U8_STR_MAX]; - -static void __attribute__((constructor)) scoutfs_key_init(void) -{ - int ret; - int i; - - for (i = 0; i <= U8_MAX; i++) { - ret = snprintf(scoutfs_unknown_u8_strings[i], U8_STR_MAX, - "u%u", i); - assert(ret > 0 && ret < U8_STR_MAX); - } -} diff --git a/utils/src/key.h b/utils/src/key.h index 5a1b580e..49713f38 100644 --- a/utils/src/key.h +++ b/utils/src/key.h @@ -7,34 +7,10 @@ #include "cmp.h" #include "endian_swap.h" -extern char *scoutfs_zone_strings[SCOUTFS_MAX_ZONE]; -extern char *scoutfs_type_strings[SCOUTFS_MAX_ZONE][SCOUTFS_MAX_TYPE]; -#define U8_STR_MAX 5 /* u%3u'\0' */ -extern char scoutfs_unknown_u8_strings[U8_MAX][U8_STR_MAX]; - -static inline char *sk_zone_str(u8 zone) -{ - if (zone >= SCOUTFS_MAX_ZONE || scoutfs_zone_strings[zone] == NULL) - return scoutfs_unknown_u8_strings[zone]; - - return scoutfs_zone_strings[zone]; -} - -static inline char *sk_type_str(u8 zone, u8 type) -{ - if (zone >= SCOUTFS_MAX_ZONE || type >= SCOUTFS_MAX_TYPE || - scoutfs_type_strings[zone][type] == NULL) - return scoutfs_unknown_u8_strings[type]; - - return scoutfs_type_strings[zone][type]; -} - -#define SK_FMT "%s.%llu.%s.%llu.%llu.%u" +#define SK_FMT "%u.%llu.%u.%llu.%llu.%u" /* This does not support null keys */ -#define SK_ARG(key) sk_zone_str((key)->sk_zone), \ - le64_to_cpu((key)->_sk_first), \ - sk_type_str((key)->sk_zone, (key)->sk_type), \ - le64_to_cpu((key)->_sk_second), \ +#define SK_ARG(key) (key)->sk_zone, le64_to_cpu((key)->_sk_first), \ + (key)->sk_type, le64_to_cpu((key)->_sk_second), \ le64_to_cpu((key)->_sk_third), \ (key)->_sk_fourth From aa84f7c6014e82a8b442dd9b2da4ceada9c86e65 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Thu, 30 Apr 2020 09:45:15 -0700 Subject: [PATCH 200/235] scoutfs-utils: use scoutfs_key as btree key Track the kernel changes to use the scoutfs_key struct as the btree key instead of a big-endian binary blob. Signed-off-by: Zach Brown --- utils/src/format.h | 75 ++++++++++++++---------------------------- utils/src/key.h | 22 ------------- utils/src/mkfs.c | 31 ++++++++---------- utils/src/print.c | 81 ++++++++++++++++++---------------------------- 4 files changed, 69 insertions(+), 140 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index 493a4ae3..740a64fb 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -133,6 +133,20 @@ struct scoutfs_key { #define skpe_base _sk_second #define skpe_part _sk_fourth +/* log trees */ +#define sklt_rid _sk_first +#define sklt_nr _sk_second + +/* lock clients */ +#define sklc_rid _sk_first + +/* seqs */ +#define skts_trans_seq _sk_first +#define skts_rid _sk_second + +/* mounted clients */ +#define skmc_rid _sk_first + struct scoutfs_radix_block { struct scoutfs_block_header hdr; __le32 sm_first; @@ -170,34 +184,17 @@ struct scoutfs_radix_root { ~(__u64)SCOUTFS_RADIX_LG_MASK) #define SCOUTFS_RADIX_BITS_BYTES (SCOUTFS_RADIX_BITS / 8) -/* - * The btree still uses memcmp() to compare keys. We should fix that - * before too long. - */ -struct scoutfs_key_be { - __u8 sk_zone; - __be64 _sk_first; - __u8 sk_type; - __be64 _sk_second; - __be64 _sk_third; - __u8 _sk_fourth; -}__packed; - -/* chose reasonable max key lens that have room for some u64s */ -#define SCOUTFS_BTREE_MAX_KEY_LEN 40 /* when we split we want to have multiple items on each side */ #define SCOUTFS_BTREE_MAX_VAL_LEN (SCOUTFS_BLOCK_SIZE / 8) /* * The min number of free bytes we must leave in a parent as we descend - * to modify. This leaves enough free bytes to insert a possibly maximal - * sized key as a seperator for a child block. Fewer bytes then this - * and split/merge might try to insert a max child item in the parent - * that wouldn't fit. + * to modify. This guarantees enough free bytes in a parent to insert a + * new child reference item as a child block splits. */ #define SCOUTFS_BTREE_PARENT_MIN_FREE_BYTES \ (sizeof(struct scoutfs_btree_item_header) + \ - sizeof(struct scoutfs_btree_item) + SCOUTFS_BTREE_MAX_KEY_LEN +\ + sizeof(struct scoutfs_btree_item) + \ sizeof(struct scoutfs_btree_ref)) /* @@ -233,9 +230,9 @@ struct scoutfs_btree_item_header { } __packed; struct scoutfs_btree_item { - __le16 key_len; + struct scoutfs_key key; __le16 val_len; - __u8 data[0]; + __u8 val[0]; } __packed; struct scoutfs_btree_block { @@ -246,30 +243,6 @@ struct scoutfs_btree_block { struct scoutfs_btree_item_header item_hdrs[0]; } __packed; -/* - * The lock server keeps a persistent record of connected clients so that - * server failover knows who to wait for before resuming operations. - */ -struct scoutfs_lock_client_btree_key { - __be64 rid; -} __packed; - -/* - * The server tracks transaction sequence numbers that clients have - * open. This limits results that can be returned from the seq indices. - */ -struct scoutfs_trans_seq_btree_key { - __be64 trans_seq; - __be64 rid; -} __packed; - -/* - * The server keeps a persistent record of mounted clients. - */ -struct scoutfs_mounted_client_btree_key { - __be64 rid; -} __packed; - struct scoutfs_mounted_client_btree_val { __u8 flags; } __packed; @@ -292,11 +265,6 @@ struct scoutfs_log_trees { __le64 nr; } __packed; -struct scoutfs_log_trees_key { - __be64 rid; - __be64 nr; -} __packed; - struct scoutfs_log_trees_val { struct scoutfs_radix_root meta_avail; struct scoutfs_radix_root meta_freed; @@ -348,6 +316,11 @@ struct scoutfs_bloom_block { #define SCOUTFS_RID_ZONE 3 #define SCOUTFS_FS_ZONE 4 #define SCOUTFS_LOCK_ZONE 5 +/* Items only stored in server btrees */ +#define SCOUTFS_LOG_TREES_ZONE 6 +#define SCOUTFS_LOCK_CLIENTS_ZONE 7 +#define SCOUTFS_TRANS_SEQ_ZONE 8 +#define SCOUTFS_MOUNTED_CLIENT_ZONE 9 /* inode index zone */ #define SCOUTFS_INODE_INDEX_META_SEQ_TYPE 1 diff --git a/utils/src/key.h b/utils/src/key.h index 49713f38..7e3ad20d 100644 --- a/utils/src/key.h +++ b/utils/src/key.h @@ -141,26 +141,4 @@ static inline void scoutfs_key_dec(struct scoutfs_key *key) key->sk_zone--; } -static inline void scoutfs_key_to_be(struct scoutfs_key_be *be, - struct scoutfs_key *key) -{ - be->sk_zone = key->sk_zone; - be->_sk_first = le64_to_be64(key->_sk_first); - be->sk_type = key->sk_type; - be->_sk_second = le64_to_be64(key->_sk_second); - be->_sk_third = le64_to_be64(key->_sk_third); - be->_sk_fourth = key->_sk_fourth; -} - -static inline void scoutfs_key_from_be(struct scoutfs_key *key, - struct scoutfs_key_be *be) -{ - key->sk_zone = be->sk_zone; - key->_sk_first = be64_to_le64(be->_sk_first); - key->sk_type = be->sk_type; - key->_sk_second = be64_to_le64(be->_sk_second); - key->_sk_third = be64_to_le64(be->_sk_third); - key->_sk_fourth = be->_sk_fourth; -} - #endif diff --git a/utils/src/mkfs.c b/utils/src/mkfs.c index ac3393f6..8c06bb82 100644 --- a/utils/src/mkfs.c +++ b/utils/src/mkfs.c @@ -287,11 +287,10 @@ out: static int write_new_fs(char *path, int fd, u8 quorum_count) { struct scoutfs_super_block *super; - struct scoutfs_key_be *kbe; struct scoutfs_inode *inode; struct scoutfs_btree_block *bt; struct scoutfs_btree_item *btitem; - struct scoutfs_key key; + struct scoutfs_key *key; struct timeval tv; char uuid_str[37]; void *zeros; @@ -374,32 +373,28 @@ static int write_new_fs(char *path, int fd, u8 quorum_count) bt->nr_items = cpu_to_le32(2); /* btree item allocated from the back of the block */ - kbe = (void *)bt + SCOUTFS_BLOCK_SIZE - sizeof(*kbe); - btitem = (void *)kbe - sizeof(*btitem); + key = (void *)bt + SCOUTFS_BLOCK_SIZE - sizeof(*key); + btitem = (void *)key - sizeof(*btitem); bt->item_hdrs[0].off = cpu_to_le32((long)btitem - (long)bt); - btitem->key_len = cpu_to_le16(sizeof(*kbe)); btitem->val_len = cpu_to_le16(0); - memset(&key, 0, sizeof(key)); - key.sk_zone = SCOUTFS_INODE_INDEX_ZONE; - key.sk_type = SCOUTFS_INODE_INDEX_META_SEQ_TYPE; - key.skii_ino = cpu_to_le64(SCOUTFS_ROOT_INO); - scoutfs_key_to_be(kbe, &key); + memset(key, 0, sizeof(*key)); + key->sk_zone = SCOUTFS_INODE_INDEX_ZONE; + key->sk_type = SCOUTFS_INODE_INDEX_META_SEQ_TYPE; + key->skii_ino = cpu_to_le64(SCOUTFS_ROOT_INO); inode = (void *)btitem - sizeof(*inode); - kbe = (void *)inode - sizeof(*kbe); - btitem = (void *)kbe - sizeof(*btitem); + key = (void *)inode - sizeof(*key); + btitem = (void *)key - sizeof(*btitem); bt->item_hdrs[1].off = cpu_to_le32((long)btitem - (long)bt); - btitem->key_len = cpu_to_le16(sizeof(*kbe)); btitem->val_len = cpu_to_le16(sizeof(*inode)); - memset(&key, 0, sizeof(key)); - key.sk_zone = SCOUTFS_FS_ZONE; - key.ski_ino = cpu_to_le64(SCOUTFS_ROOT_INO); - key.sk_type = SCOUTFS_INODE_TYPE; - scoutfs_key_to_be(kbe, &key); + memset(key, 0, sizeof(*key)); + key->sk_zone = SCOUTFS_FS_ZONE; + key->ski_ino = cpu_to_le64(SCOUTFS_ROOT_INO); + key->sk_type = SCOUTFS_INODE_TYPE; inode->next_readdir_pos = cpu_to_le64(2); inode->nlink = cpu_to_le32(SCOUTFS_DIRENT_FIRST_POS); diff --git a/utils/src/print.c b/utils/src/print.c index 01f0c284..bf3a0801 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -258,40 +258,34 @@ static print_func_t find_printer(u8 zone, u8 type) return NULL; } -static int print_fs_item(void *key, unsigned key_len, void *val, +static int print_fs_item(struct scoutfs_key *key, void *val, unsigned val_len, void *arg) { - struct scoutfs_key item_key; print_func_t printer; - scoutfs_key_from_be(&item_key, key); - - printf(" "SK_FMT"\n", SK_ARG(&item_key)); + printf(" "SK_FMT"\n", SK_ARG(key)); /* only items in leaf blocks have values */ if (val) { - printer = find_printer(item_key.sk_zone, item_key.sk_type); + printer = find_printer(key->sk_zone, key->sk_type); if (printer) - printer(&item_key, val, val_len); + printer(key, val, val_len); else printf(" (unknown zone %u type %u)\n", - item_key.sk_zone, item_key.sk_type); + key->sk_zone, key->sk_type); } return 0; } /* same as fs item but with a small header in the value */ -static int print_logs_item(void *key, unsigned key_len, void *val, +static int print_logs_item(struct scoutfs_key *key, void *val, unsigned val_len, void *arg) { - struct scoutfs_key item_key; struct scoutfs_log_item_value *liv; print_func_t printer; - scoutfs_key_from_be(&item_key, key); - - printf(" "SK_FMT"\n", SK_ARG(&item_key)); + printf(" "SK_FMT"\n", SK_ARG(key)); /* only items in leaf blocks have values */ if (val) { @@ -301,14 +295,14 @@ static int print_logs_item(void *key, unsigned key_len, void *val, /* deletion items don't have values */ if (!(liv->flags & SCOUTFS_LOG_ITEM_FLAG_DELETION)) { - printer = find_printer(item_key.sk_zone, - item_key.sk_type); + printer = find_printer(key->sk_zone, + key->sk_type); if (printer) - printer(&item_key, val + sizeof(*liv), + printer(key, val + sizeof(*liv), val_len - sizeof(*liv)); else printf(" (unknown zone %u type %u)\n", - item_key.sk_zone, item_key.sk_type); + key->sk_zone, key->sk_type); } } @@ -328,14 +322,13 @@ static int print_logs_item(void *key, unsigned key_len, void *val, RADREF_A(&(root)->ref) /* same as fs item but with a small header in the value */ -static int print_log_trees_item(void *key, unsigned key_len, void *val, +static int print_log_trees_item(struct scoutfs_key *key, void *val, unsigned val_len, void *arg) { - struct scoutfs_log_trees_key *ltk = key; struct scoutfs_log_trees_val *ltv = val; printf(" rid %llu nr %llu\n", - be64_to_cpu(ltk->rid), be64_to_cpu(ltk->nr)); + le64_to_cpu(key->sklt_rid), le64_to_cpu(key->sklt_nr)); /* only items in leaf blocks have values */ if (val) { @@ -359,48 +352,43 @@ static int print_log_trees_item(void *key, unsigned key_len, void *val, return 0; } -static int print_lock_clients_entry(void *key, unsigned key_len, void *val, +static int print_lock_clients_entry(struct scoutfs_key *key, void *val, unsigned val_len, void *arg) { - struct scoutfs_lock_client_btree_key *cbk = key; - - printf(" rid %016llx\n", be64_to_cpu(cbk->rid)); + printf(" rid %016llx\n", le64_to_cpu(key->sklc_rid)); return 0; } -static int print_trans_seqs_entry(void *key, unsigned key_len, void *val, +static int print_trans_seqs_entry(struct scoutfs_key *key, void *val, unsigned val_len, void *arg) { - struct scoutfs_trans_seq_btree_key *tsk = key; - printf(" trans_seq %llu rid %016llx\n", - be64_to_cpu(tsk->trans_seq), be64_to_cpu(tsk->rid)); + le64_to_cpu(key->skts_trans_seq), le64_to_cpu(key->skts_rid)); return 0; } -static int print_mounted_client_entry(void *key, unsigned key_len, void *val, +static int print_mounted_client_entry(struct scoutfs_key *key, void *val, unsigned val_len, void *arg) { - struct scoutfs_mounted_client_btree_key *mck = key; struct scoutfs_mounted_client_btree_val *mcv = val; printf(" rid %016llx flags 0x%x\n", - be64_to_cpu(mck->rid), mcv->flags); + le64_to_cpu(key->skmc_rid), mcv->flags); return 0; } -typedef int (*print_item_func)(void *key, unsigned key_len, void *val, +typedef int (*print_item_func)(struct scoutfs_key *key, void *val, unsigned val_len, void *arg); -static int print_btree_ref(void *key, unsigned key_len, void *val, +static int print_btree_ref(struct scoutfs_key *key, void *val, unsigned val_len, print_item_func func, void *arg) { struct scoutfs_btree_ref *ref = val; - func(key, key_len, NULL, 0, arg); + func(key, NULL, 0, arg); printf(" ref blkno %llu seq %llu\n", le64_to_cpu(ref->blkno), le64_to_cpu(ref->seq)); @@ -413,9 +401,8 @@ static int print_btree_block(int fd, struct scoutfs_super_block *super, { struct scoutfs_btree_item *item; struct scoutfs_btree_block *bt; - unsigned key_len; + struct scoutfs_key *key; unsigned val_len; - void *key; void *val; int ret; int i; @@ -440,10 +427,9 @@ static int print_btree_block(int fd, struct scoutfs_super_block *super, for (i = 0; i < le32_to_cpu(bt->nr_items); i++) { item = (void *)bt + le32_to_cpu(bt->item_hdrs[i].off); - key_len = le16_to_cpu(item->key_len); val_len = le16_to_cpu(item->val_len); - key = (void *)(item + 1); - val = (void *)key + key_len; + key = &item->key; + val = item->val; if (level < bt->level) { ref = val; @@ -457,13 +443,13 @@ static int print_btree_block(int fd, struct scoutfs_super_block *super, continue; } - printf(" item [%u] off %u key_len %u val_len %u\n", - i, le32_to_cpu(bt->item_hdrs[i].off), key_len, val_len); + printf(" item [%u] off %u val_len %u\n", + i, le32_to_cpu(bt->item_hdrs[i].off), val_len); if (level) - print_btree_ref(key, key_len, val, val_len, func, arg); + print_btree_ref(key, val, val_len, func, arg); else - func(key, key_len, val, val_len, arg); + func(key, val, val_len, arg); } free(bt); @@ -563,10 +549,9 @@ struct print_recursion_args { }; /* same as fs item but with a small header in the value */ -static int print_log_trees_roots(void *key, unsigned key_len, void *val, +static int print_log_trees_roots(struct scoutfs_key *key, void *val, unsigned val_len, void *arg) { -// struct scoutfs_log_trees_key *ltk = key; struct scoutfs_log_trees_val *ltv = val; struct print_recursion_args *pa = arg; int ret = 0; @@ -605,7 +590,6 @@ static int print_btree_leaf_items(int fd, struct scoutfs_super_block *super, { struct scoutfs_btree_item *item; struct scoutfs_btree_block *bt; - unsigned key_len; unsigned val_len; void *key; void *val; @@ -621,10 +605,9 @@ static int print_btree_leaf_items(int fd, struct scoutfs_super_block *super, for (i = 0; i < le32_to_cpu(bt->nr_items); i++) { item = (void *)bt + le32_to_cpu(bt->item_hdrs[i].off); - key_len = le16_to_cpu(item->key_len); val_len = le16_to_cpu(item->val_len); key = (void *)(item + 1); - val = (void *)key + key_len; + val = (void *)(key + 1); if (bt->level > 0) { ret = print_btree_leaf_items(fd, super, val, func, arg); @@ -632,7 +615,7 @@ static int print_btree_leaf_items(int fd, struct scoutfs_super_block *super, break; continue; } else { - func(key, key_len, val, val_len, arg); + func(key, val, val_len, arg); } } From b86a1bebbb1229a4efb8c01f2ec4356f72e3c352 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Thu, 30 Apr 2020 12:00:15 -0700 Subject: [PATCH 201/235] scoutfs-utils: support btree avl and hash Update the internal structure of btree blocks to use the avl item index and hash table direct item lookup. Signed-off-by: Zach Brown --- utils/src/avl.c | 40 ++++++++++++++++++ utils/src/avl.h | 8 ++++ utils/src/format.h | 61 +++++++++++++++------------ utils/src/leaf_item_hash.c | 39 ++++++++++++++++++ utils/src/leaf_item_hash.h | 9 ++++ utils/src/mkfs.c | 48 ++++++++++++++++------ utils/src/print.c | 84 +++++++++++++++++++++++++++++++------- 7 files changed, 237 insertions(+), 52 deletions(-) create mode 100644 utils/src/avl.c create mode 100644 utils/src/avl.h create mode 100644 utils/src/leaf_item_hash.c create mode 100644 utils/src/leaf_item_hash.h diff --git a/utils/src/avl.c b/utils/src/avl.c new file mode 100644 index 00000000..5faf39e2 --- /dev/null +++ b/utils/src/avl.c @@ -0,0 +1,40 @@ +#include "sparse.h" +#include "util.h" +#include "format.h" +#include "avl.h" + +static struct scoutfs_avl_node *node_ptr(struct scoutfs_avl_root *root, + + __le16 off) +{ + return off ? (void *)root + le16_to_cpu(off) : NULL; +} + +struct scoutfs_avl_node *avl_first(struct scoutfs_avl_root *root) +{ + struct scoutfs_avl_node *node = node_ptr(root, root->node); + + while (node && node->left) + node = node_ptr(root, node->left); + + return node; +} + +struct scoutfs_avl_node *avl_next(struct scoutfs_avl_root *root, + struct scoutfs_avl_node *node) +{ + struct scoutfs_avl_node *parent; + + if (node->right) { + node = node_ptr(root, node->right); + while (node->left) + node = node_ptr(root, node->left); + return node; + } + + while ((parent = node_ptr(root, node->parent)) && + node == node_ptr(root, parent->right)) + node = parent; + + return parent; +} diff --git a/utils/src/avl.h b/utils/src/avl.h new file mode 100644 index 00000000..04c921fe --- /dev/null +++ b/utils/src/avl.h @@ -0,0 +1,8 @@ +#ifndef _AVL_H_ +#define _AVL_H_ + +struct scoutfs_avl_node *avl_first(struct scoutfs_avl_root *root); +struct scoutfs_avl_node *avl_next(struct scoutfs_avl_root *root, + struct scoutfs_avl_node *node); + +#endif diff --git a/utils/src/format.h b/utils/src/format.h index 740a64fb..822675e6 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -184,26 +184,22 @@ struct scoutfs_radix_root { ~(__u64)SCOUTFS_RADIX_LG_MASK) #define SCOUTFS_RADIX_BITS_BYTES (SCOUTFS_RADIX_BITS / 8) +struct scoutfs_avl_root { + __le16 node; +} __packed; + +struct scoutfs_avl_node { + __le16 parent; + __le16 left; + __le16 right; + __u8 height; +} __packed; + /* when we split we want to have multiple items on each side */ -#define SCOUTFS_BTREE_MAX_VAL_LEN (SCOUTFS_BLOCK_SIZE / 8) +#define SCOUTFS_BTREE_MAX_VAL_LEN 512 -/* - * The min number of free bytes we must leave in a parent as we descend - * to modify. This guarantees enough free bytes in a parent to insert a - * new child reference item as a child block splits. - */ -#define SCOUTFS_BTREE_PARENT_MIN_FREE_BYTES \ - (sizeof(struct scoutfs_btree_item_header) + \ - sizeof(struct scoutfs_btree_item) + \ - sizeof(struct scoutfs_btree_ref)) - -/* - * When debugging we can tune the splitting and merging thresholds to - * create much larger trees by having blocks with many fewer items. We - * implement this by pretending the blocks are tiny. They're still - * large enough for a handful of items. - */ -#define SCOUTFS_BTREE_TINY_BLOCK_SIZE 512 +/* each value ends with an offset which lets compaction iterate over values */ +#define SCOUTFS_BTREE_VAL_OWNER_BYTES sizeof(__le16) /* * A 4EB test image measured a worst case height of 17. This is plenty @@ -225,24 +221,37 @@ struct scoutfs_btree_root { __u8 height; } __packed; -struct scoutfs_btree_item_header { - __le32 off; -} __packed; - struct scoutfs_btree_item { + struct scoutfs_avl_node node; struct scoutfs_key key; + __le16 val_off; __le16 val_len; - __u8 val[0]; } __packed; struct scoutfs_btree_block { struct scoutfs_block_header hdr; - __le32 free_end; - __le32 nr_items; + struct scoutfs_avl_root item_root; + __le16 nr_items; + __le16 total_item_bytes; + __le16 mid_free_len; + __le16 last_free_off; + __le16 last_free_len; __u8 level; - struct scoutfs_btree_item_header item_hdrs[0]; + struct scoutfs_btree_item items[0]; + /* leaf blocks have a fixed size item offset hash table at the end */ } __packed; +/* + * Try to aim for a 75% load in a leaf full of items with no value. + * We'll almost never see this because most items have values and most + * blocks aren't full. + */ +#define SCOUTFS_BTREE_LEAF_ITEM_HASH_NR \ + ((SCOUTFS_BLOCK_SIZE - sizeof(struct scoutfs_btree_block)) / \ + (sizeof(struct scoutfs_btree_item) + (sizeof(__le16))) * 100 / 75) +#define SCOUTFS_BTREE_LEAF_ITEM_HASH_BYTES \ + (SCOUTFS_BTREE_LEAF_ITEM_HASH_NR * sizeof(__le16)) + struct scoutfs_mounted_client_btree_val { __u8 flags; } __packed; diff --git a/utils/src/leaf_item_hash.c b/utils/src/leaf_item_hash.c new file mode 100644 index 00000000..32dca525 --- /dev/null +++ b/utils/src/leaf_item_hash.c @@ -0,0 +1,39 @@ +#include "sparse.h" +#include "util.h" +#include "format.h" +#include "crc.h" +#include "leaf_item_hash.h" + +/* + * A minimal extraction of the leaf item hash from the kernel's btree. + */ + +int leaf_item_hash_ind(struct scoutfs_key *key) +{ + return crc32c(~0, key, sizeof(struct scoutfs_key)) % + SCOUTFS_BTREE_LEAF_ITEM_HASH_NR; +} + +__le16 *leaf_item_hash_buckets(struct scoutfs_btree_block *bt) +{ + return (void *)bt + SCOUTFS_BLOCK_SIZE - + SCOUTFS_BTREE_LEAF_ITEM_HASH_BYTES; +} + +void leaf_item_hash_insert(struct scoutfs_btree_block *bt, + struct scoutfs_key *key, __le16 off) +{ + __le16 *buckets = leaf_item_hash_buckets(bt); + int i; + + if (bt->level > 0) + return; + + for (i = leaf_item_hash_ind(key); + i < SCOUTFS_BTREE_LEAF_ITEM_HASH_NR; i++) { + if (buckets[i] == 0) { + buckets[i] = off; + return; + } + } +} diff --git a/utils/src/leaf_item_hash.h b/utils/src/leaf_item_hash.h new file mode 100644 index 00000000..0408bc0e --- /dev/null +++ b/utils/src/leaf_item_hash.h @@ -0,0 +1,9 @@ +#ifndef _LEAF_ITEM_HASH_H_ +#define _LEAF_ITEM_HASH_H_ + +int leaf_item_hash_ind(struct scoutfs_key *key); +__le16 *leaf_item_hash_buckets(struct scoutfs_btree_block *bt); +void leaf_item_hash_insert(struct scoutfs_btree_block *bt, + struct scoutfs_key *key, __le16 off); + +#endif diff --git a/utils/src/mkfs.c b/utils/src/mkfs.c index 8c06bb82..7b9650f4 100644 --- a/utils/src/mkfs.c +++ b/utils/src/mkfs.c @@ -27,6 +27,7 @@ #include "key.h" #include "bitops.h" #include "radix.h" +#include "leaf_item_hash.h" static int write_raw_block(int fd, u64 blkno, void *blk) { @@ -290,9 +291,11 @@ static int write_new_fs(char *path, int fd, u8 quorum_count) struct scoutfs_inode *inode; struct scoutfs_btree_block *bt; struct scoutfs_btree_item *btitem; + struct scoutfs_avl_node *par; struct scoutfs_key *key; struct timeval tv; char uuid_str[37]; + __le16 *own; void *zeros; u64 blkno; u64 limit; @@ -370,28 +373,43 @@ static int write_new_fs(char *path, int fd, u8 quorum_count) bt->hdr.fsid = super->hdr.fsid; bt->hdr.blkno = cpu_to_le64(blkno); bt->hdr.seq = cpu_to_le64(1); - bt->nr_items = cpu_to_le32(2); - /* btree item allocated from the back of the block */ - key = (void *)bt + SCOUTFS_BLOCK_SIZE - sizeof(*key); - btitem = (void *)key - sizeof(*btitem); + /* meta seq index for the root inode */ + btitem = &bt->items[le16_to_cpu(bt->nr_items)]; + le16_add_cpu(&bt->nr_items, 1); + key = &btitem->key; - bt->item_hdrs[0].off = cpu_to_le32((long)btitem - (long)bt); + bt->item_root.node = cpu_to_le16((void *)&btitem->node - + (void *)&bt->item_root); + btitem->node.height = 2; btitem->val_len = cpu_to_le16(0); - memset(key, 0, sizeof(*key)); key->sk_zone = SCOUTFS_INODE_INDEX_ZONE; key->sk_type = SCOUTFS_INODE_INDEX_META_SEQ_TYPE; key->skii_ino = cpu_to_le64(SCOUTFS_ROOT_INO); - inode = (void *)btitem - sizeof(*inode); - key = (void *)inode - sizeof(*key); - btitem = (void *)key - sizeof(*btitem); + leaf_item_hash_insert(bt, &btitem->key, + cpu_to_le16((void *)btitem - (void *)bt)); - bt->item_hdrs[1].off = cpu_to_le32((long)btitem - (long)bt); + /* root inode */ + par = &btitem->node; + btitem = &bt->items[le16_to_cpu(bt->nr_items)]; + le16_add_cpu(&bt->nr_items, 1); + key = &btitem->key; + own = (void *)bt + SCOUTFS_BLOCK_SIZE - + SCOUTFS_BTREE_LEAF_ITEM_HASH_BYTES - + SCOUTFS_BTREE_VAL_OWNER_BYTES; + inode = (void *)own - sizeof(*inode); + + par->right = cpu_to_le16((void *)&btitem->node - + (void *)&bt->item_root); + btitem->node.height = 1; + btitem->node.parent = cpu_to_le16((void *)par - (void *)&bt->item_root); + btitem->val_off = cpu_to_le16((void *)inode - (void *)bt); btitem->val_len = cpu_to_le16(sizeof(*inode)); + le16_add_cpu(&bt->total_item_bytes, le16_to_cpu(btitem->val_len) + + SCOUTFS_BTREE_VAL_OWNER_BYTES); - memset(key, 0, sizeof(*key)); key->sk_zone = SCOUTFS_FS_ZONE; key->ski_ino = cpu_to_le64(SCOUTFS_ROOT_INO); key->sk_type = SCOUTFS_INODE_TYPE; @@ -406,8 +424,14 @@ static int write_new_fs(char *path, int fd, u8 quorum_count) inode->mtime.sec = inode->atime.sec; inode->mtime.nsec = inode->atime.nsec; - bt->free_end = bt->item_hdrs[le32_to_cpu(bt->nr_items) - 1].off; + leaf_item_hash_insert(bt, &btitem->key, + cpu_to_le16((void *)btitem - (void *)bt)); + *own = cpu_to_le16((void *)btitem - (void *)bt); + le16_add_cpu(&bt->total_item_bytes, le16_to_cpu(bt->nr_items) * + sizeof(struct scoutfs_btree_item)); + bt->mid_free_len = cpu_to_le16((void *)inode - + (void *)&bt->items[le16_to_cpu(bt->nr_items)]); bt->hdr.magic = cpu_to_le32(SCOUTFS_BLOCK_MAGIC_BTREE); bt->hdr.crc = cpu_to_le32(crc_block(&bt->hdr)); diff --git a/utils/src/print.c b/utils/src/print.c index bf3a0801..4dff2c28 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -21,6 +21,8 @@ #include "crc.h" #include "key.h" #include "radix.h" +#include "avl.h" +#include "leaf_item_hash.h" static void *read_block(int fd, u64 blkno) { @@ -395,14 +397,48 @@ static int print_btree_ref(struct scoutfs_key *key, void *val, return 0; } +static void print_leaf_item_hash(struct scoutfs_btree_block *bt) +{ + __le16 *b; + int col; + int nr; + int i; + + /* print the leaf item hash */ + printf(" item hash: "); + col = 13; + + b = leaf_item_hash_buckets(bt); + nr = 0; + for (i = 0; i < SCOUTFS_BTREE_LEAF_ITEM_HASH_NR; i++) { + if (b[i] == 0) + continue; + + nr++; + col += snprintf(NULL, 0, "%u,%u ", i, le16_to_cpu(b[i])); + if (col >= 78) { + printf("\n "); + col = 3; + } + printf("%u,%u ", i, le16_to_cpu(b[i])); + } + if (col != 3) + printf("\n"); + printf(" (%u / %u populated, %u%% load)\n", + nr, (int)SCOUTFS_BTREE_LEAF_ITEM_HASH_NR, + nr * 100 / (int)SCOUTFS_BTREE_LEAF_ITEM_HASH_NR); +} + static int print_btree_block(int fd, struct scoutfs_super_block *super, char *which, struct scoutfs_btree_ref *ref, print_item_func func, void *arg, u8 level) { struct scoutfs_btree_item *item; + struct scoutfs_avl_node *node; struct scoutfs_btree_block *bt; struct scoutfs_key *key; - unsigned val_len; + unsigned int val_len; + unsigned int off; void *val; int ret; int i; @@ -414,22 +450,35 @@ static int print_btree_block(int fd, struct scoutfs_super_block *super, if (bt->level == level) { printf("%s btree blkno %llu\n" " crc %08x fsid %llx seq %llu blkno %llu \n" - " level %u free_end %u nr_items %u\n", + " total_item_bytes %u mid_free_len %u last_free_off %u " + "last_free_len %u\n" + " level %u nr_items %u item_root.node %u\n", which, le64_to_cpu(ref->blkno), le32_to_cpu(bt->hdr.crc), le64_to_cpu(bt->hdr.fsid), le64_to_cpu(bt->hdr.seq), le64_to_cpu(bt->hdr.blkno), + le16_to_cpu(bt->total_item_bytes), + le16_to_cpu(bt->mid_free_len), + le16_to_cpu(bt->last_free_off), + le16_to_cpu(bt->last_free_len), bt->level, - le32_to_cpu(bt->free_end), - le32_to_cpu(bt->nr_items)); + le16_to_cpu(bt->nr_items), + le16_to_cpu(bt->item_root.node)); + + if (bt->level == 0) + print_leaf_item_hash(bt); } - for (i = 0; i < le32_to_cpu(bt->nr_items); i++) { - item = (void *)bt + le32_to_cpu(bt->item_hdrs[i].off); + for (i = 0, node = avl_first(&bt->item_root); + node; + i++, node = avl_next(&bt->item_root, node)) { + + item = container_of(node, struct scoutfs_btree_item, node); + off = (void *)item - (void *)bt; val_len = le16_to_cpu(item->val_len); key = &item->key; - val = item->val; + val = (void *)bt + le16_to_cpu(item->val_off); if (level < bt->level) { ref = val; @@ -443,8 +492,12 @@ static int print_btree_block(int fd, struct scoutfs_super_block *super, continue; } - printf(" item [%u] off %u val_len %u\n", - i, le32_to_cpu(bt->item_hdrs[i].off), val_len); + printf(" [%u] off %u par %u l %u r %u h %u vo %u vl %u\n", + i, off, le16_to_cpu(item->node.parent), + le16_to_cpu(item->node.left), + le16_to_cpu(item->node.right), + item->node.height, le16_to_cpu(item->val_off), + val_len); if (level) print_btree_ref(key, val, val_len, func, arg); @@ -589,12 +642,12 @@ static int print_btree_leaf_items(int fd, struct scoutfs_super_block *super, print_item_func func, void *arg) { struct scoutfs_btree_item *item; + struct scoutfs_avl_node *node; struct scoutfs_btree_block *bt; unsigned val_len; void *key; void *val; int ret; - int i; if (ref->blkno == 0) return 0; @@ -603,11 +656,12 @@ static int print_btree_leaf_items(int fd, struct scoutfs_super_block *super, if (!bt) return -ENOMEM; - for (i = 0; i < le32_to_cpu(bt->nr_items); i++) { - item = (void *)bt + le32_to_cpu(bt->item_hdrs[i].off); + node = avl_first(&bt->item_root); + while (node) { + item = container_of(node, struct scoutfs_btree_item, node); val_len = le16_to_cpu(item->val_len); - key = (void *)(item + 1); - val = (void *)(key + 1); + key = &item->key; + val = (void *)bt + le16_to_cpu(item->val_off); if (bt->level > 0) { ret = print_btree_leaf_items(fd, super, val, func, arg); @@ -617,6 +671,8 @@ static int print_btree_leaf_items(int fd, struct scoutfs_super_block *super, } else { func(key, val, val_len, arg); } + + node = avl_next(&bt->item_root, node); } free(bt); From 39993d8b5f912240f3d15dd3c7b8b5a7bb37a88f Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Fri, 1 May 2020 11:59:00 -0700 Subject: [PATCH 202/235] scoutfs-utils: use larger metadata blocks Signed-off-by: Zach Brown --- utils/src/crc.c | 4 +- utils/src/crc.h | 2 +- utils/src/format.h | 73 ++++++++++++++++++++------------- utils/src/leaf_item_hash.c | 2 +- utils/src/mkfs.c | 82 ++++++++++++++++++++------------------ utils/src/print.c | 27 +++++++------ 6 files changed, 108 insertions(+), 82 deletions(-) diff --git a/utils/src/crc.c b/utils/src/crc.c index 38640fbc..0562e580 100644 --- a/utils/src/crc.c +++ b/utils/src/crc.c @@ -32,8 +32,8 @@ u64 crc32c_64(u32 crc, const void *data, unsigned int len) crc32c(~crc, data + len - half, half); } -u32 crc_block(struct scoutfs_block_header *hdr) +u32 crc_block(struct scoutfs_block_header *hdr, u32 size) { return crc32c(~0, (char *)hdr + sizeof(hdr->crc), - SCOUTFS_BLOCK_SIZE - sizeof(hdr->crc)); + size - sizeof(hdr->crc)); } diff --git a/utils/src/crc.h b/utils/src/crc.h index 6878bf2f..a6e85ae0 100644 --- a/utils/src/crc.h +++ b/utils/src/crc.h @@ -7,6 +7,6 @@ u32 crc32c(u32 crc, const void *data, unsigned int len); u64 crc32c_64(u32 crc, const void *data, unsigned int len); -u32 crc_block(struct scoutfs_block_header *hdr); +u32 crc_block(struct scoutfs_block_header *hdr, u32 size); #endif diff --git a/utils/src/format.h b/utils/src/format.h index 822675e6..6c667c97 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -11,24 +11,42 @@ #define SCOUTFS_BLOCK_MAGIC_RADIX 0xebeb5e65 /* - * The super block and btree blocks are fixed 4k. + * The super block, quorum block, and file data allocation granularity + * use the smaller 4KB block. */ -#define SCOUTFS_BLOCK_SHIFT 12 -#define SCOUTFS_BLOCK_SIZE (1 << SCOUTFS_BLOCK_SHIFT) -#define SCOUTFS_BLOCK_MASK (SCOUTFS_BLOCK_SIZE - 1) -#define SCOUTFS_BLOCKS_PER_PAGE (PAGE_SIZE / SCOUTFS_BLOCK_SIZE) -#define SCOUTFS_BLOCK_SECTOR_SHIFT (SCOUTFS_BLOCK_SHIFT - 9) -#define SCOUTFS_BLOCK_SECTORS (1 << SCOUTFS_BLOCK_SECTOR_SHIFT) -#define SCOUTFS_BLOCK_MAX (U64_MAX >> SCOUTFS_BLOCK_SHIFT) +#define SCOUTFS_BLOCK_SM_SHIFT 12 +#define SCOUTFS_BLOCK_SM_SIZE (1 << SCOUTFS_BLOCK_SM_SHIFT) +#define SCOUTFS_BLOCK_SM_MASK (SCOUTFS_BLOCK_SM_SIZE - 1) +#define SCOUTFS_BLOCK_SM_PER_PAGE (PAGE_SIZE / SCOUTFS_BLOCK_SM_SIZE) +#define SCOUTFS_BLOCK_SM_SECTOR_SHIFT (SCOUTFS_BLOCK_SM_SHIFT - 9) +#define SCOUTFS_BLOCK_SM_SECTORS (1 << SCOUTFS_BLOCK_SM_SECTOR_SHIFT) +#define SCOUTFS_BLOCK_SM_MAX (U64_MAX >> SCOUTFS_BLOCK_SM_SHIFT) +#define SCOUTFS_BLOCK_SM_PAGES_PER (SCOUTFS_BLOCK_SM_SIZE / PAGE_SIZE) +#define SCOUTFS_BLOCK_SM_PAGE_ORDER (SCOUTFS_BLOCK_SM_SHIFT - PAGE_SHIFT) + +/* + * The radix and btree structures, and the forest bloom block, use the + * larger 64KB metadata block size. + */ +#define SCOUTFS_BLOCK_LG_SHIFT 16 +#define SCOUTFS_BLOCK_LG_SIZE (1 << SCOUTFS_BLOCK_LG_SHIFT) +#define SCOUTFS_BLOCK_LG_MASK (SCOUTFS_BLOCK_LG_SIZE - 1) +#define SCOUTFS_BLOCK_LG_PER_PAGE (PAGE_SIZE / SCOUTFS_BLOCK_LG_SIZE) +#define SCOUTFS_BLOCK_LG_SECTOR_SHIFT (SCOUTFS_BLOCK_LG_SHIFT - 9) +#define SCOUTFS_BLOCK_LG_SECTORS (1 << SCOUTFS_BLOCK_LG_SECTOR_SHIFT) +#define SCOUTFS_BLOCK_LG_MAX (U64_MAX >> SCOUTFS_BLOCK_LG_SHIFT) +#define SCOUTFS_BLOCK_LG_PAGES_PER (SCOUTFS_BLOCK_LG_SIZE / PAGE_SIZE) +#define SCOUTFS_BLOCK_LG_PAGE_ORDER (SCOUTFS_BLOCK_LG_SHIFT - PAGE_SHIFT) + +#define SCOUTFS_BLOCK_SM_LG_SHIFT (SCOUTFS_BLOCK_LG_SHIFT - \ + SCOUTFS_BLOCK_SM_SHIFT) -#define SCOUTFS_PAGES_PER_BLOCK (SCOUTFS_BLOCK_SIZE / PAGE_SIZE) -#define SCOUTFS_BLOCK_PAGE_ORDER (SCOUTFS_BLOCK_SHIFT - PAGE_SHIFT) /* * The super block leaves some room before the first block for platform * structures like boot loaders. */ -#define SCOUTFS_SUPER_BLKNO ((64ULL * 1024) >> SCOUTFS_BLOCK_SHIFT) +#define SCOUTFS_SUPER_BLKNO ((64ULL * 1024) >> SCOUTFS_BLOCK_SM_SHIFT) /* * A reasonably large region of aligned quorum blocks follow the super @@ -38,8 +56,8 @@ * mounts that have a reasonable probability of not overwriting each * other's random block locations. */ -#define SCOUTFS_QUORUM_BLKNO ((256ULL * 1024) >> SCOUTFS_BLOCK_SHIFT) -#define SCOUTFS_QUORUM_BLOCKS ((256ULL * 1024) >> SCOUTFS_BLOCK_SHIFT) +#define SCOUTFS_QUORUM_BLKNO ((256ULL * 1024) >> SCOUTFS_BLOCK_SM_SHIFT) +#define SCOUTFS_QUORUM_BLOCKS ((256ULL * 1024) >> SCOUTFS_BLOCK_SM_SHIFT) #define SCOUTFS_UNIQUE_NAME_MAX_BYTES 64 /* includes null */ @@ -168,8 +186,9 @@ struct scoutfs_radix_root { struct scoutfs_radix_ref ref; } __packed; -#define SCOUTFS_RADIX_REFS \ - ((SCOUTFS_BLOCK_SIZE - offsetof(struct scoutfs_radix_block, refs[0])) /\ +#define SCOUTFS_RADIX_REFS \ + ((SCOUTFS_BLOCK_LG_SIZE - \ + offsetof(struct scoutfs_radix_block, refs[0])) / \ sizeof(struct scoutfs_radix_ref)) /* 8 meg regions with 4k data blocks */ @@ -178,8 +197,8 @@ struct scoutfs_radix_root { #define SCOUTFS_RADIX_LG_MASK (SCOUTFS_RADIX_LG_BITS - 1) /* round block bits down to a multiple of large ranges */ -#define SCOUTFS_RADIX_BITS \ - (((SCOUTFS_BLOCK_SIZE - \ +#define SCOUTFS_RADIX_BITS \ + (((SCOUTFS_BLOCK_LG_SIZE - \ offsetof(struct scoutfs_radix_block, bits[0])) * 8) & \ ~(__u64)SCOUTFS_RADIX_LG_MASK) #define SCOUTFS_RADIX_BITS_BYTES (SCOUTFS_RADIX_BITS / 8) @@ -247,7 +266,7 @@ struct scoutfs_btree_block { * blocks aren't full. */ #define SCOUTFS_BTREE_LEAF_ITEM_HASH_NR \ - ((SCOUTFS_BLOCK_SIZE - sizeof(struct scoutfs_btree_block)) / \ + ((SCOUTFS_BLOCK_LG_SIZE - sizeof(struct scoutfs_btree_block)) / \ (sizeof(struct scoutfs_btree_item) + (sizeof(__le16))) * 100 / 75) #define SCOUTFS_BTREE_LEAF_ITEM_HASH_BYTES \ (SCOUTFS_BTREE_LEAF_ITEM_HASH_NR * sizeof(__le16)) @@ -313,9 +332,9 @@ struct scoutfs_bloom_block { */ #define SCOUTFS_FOREST_BLOOM_NRS 7 #define SCOUTFS_FOREST_BLOOM_BITS \ - (((SCOUTFS_BLOCK_SIZE - sizeof(struct scoutfs_bloom_block)) / \ - member_sizeof(struct scoutfs_bloom_block, bits[0])) * \ - member_sizeof(struct scoutfs_bloom_block, bits[0]) * 8) \ + (((SCOUTFS_BLOCK_LG_SIZE - sizeof(struct scoutfs_bloom_block)) / \ + member_sizeof(struct scoutfs_bloom_block, bits[0])) * \ + member_sizeof(struct scoutfs_bloom_block, bits[0]) * 8) \ /* * Keys are first sorted by major key zones. @@ -380,10 +399,10 @@ struct scoutfs_packed_extent { __u8 le_blkno_diff[0]; } __packed; -#define SCOUTFS_PACKEXT_BLOCKS (8 * 1024 * 1024 / SCOUTFS_BLOCK_SIZE) -#define SCOUTFS_PACKEXT_BASE_SHIFT (ilog2(SCOUTFS_PACKEXT_BLOCKS)) -#define SCOUTFS_PACKEXT_BASE_MASK (~((__u64)SCOUTFS_PACKEXT_BLOCKS - 1)) -#define SCOUTFS_PACKEXT_MAX_BYTES SCOUTFS_MAX_VAL_SIZE +#define SCOUTFS_PACKEXT_BLOCKS (8 * 1024 * 1024 / SCOUTFS_BLOCK_SM_SIZE) +#define SCOUTFS_PACKEXT_BASE_SHIFT (ilog2(SCOUTFS_PACKEXT_BLOCKS)) +#define SCOUTFS_PACKEXT_BASE_MASK (~((__u64)SCOUTFS_PACKEXT_BLOCKS - 1)) +#define SCOUTFS_PACKEXT_MAX_BYTES SCOUTFS_MAX_VAL_SIZE #define SEF_OFFLINE (1 << 0) #define SEF_UNWRITTEN (1 << 1) @@ -445,8 +464,8 @@ struct scoutfs_quorum_block { } __packed log[0]; } __packed; -#define SCOUTFS_QUORUM_LOG_MAX \ - ((SCOUTFS_BLOCK_SIZE - sizeof(struct scoutfs_quorum_block)) / \ +#define SCOUTFS_QUORUM_LOG_MAX \ + ((SCOUTFS_BLOCK_SM_SIZE - sizeof(struct scoutfs_quorum_block)) / \ sizeof(struct scoutfs_quorum_log)) struct scoutfs_super_block { diff --git a/utils/src/leaf_item_hash.c b/utils/src/leaf_item_hash.c index 32dca525..4955b1b2 100644 --- a/utils/src/leaf_item_hash.c +++ b/utils/src/leaf_item_hash.c @@ -16,7 +16,7 @@ int leaf_item_hash_ind(struct scoutfs_key *key) __le16 *leaf_item_hash_buckets(struct scoutfs_btree_block *bt) { - return (void *)bt + SCOUTFS_BLOCK_SIZE - + return (void *)bt + SCOUTFS_BLOCK_LG_SIZE - SCOUTFS_BTREE_LEAF_ITEM_HASH_BYTES; } diff --git a/utils/src/mkfs.c b/utils/src/mkfs.c index 7b9650f4..1aa46700 100644 --- a/utils/src/mkfs.c +++ b/utils/src/mkfs.c @@ -29,12 +29,13 @@ #include "radix.h" #include "leaf_item_hash.h" -static int write_raw_block(int fd, u64 blkno, void *blk) +static int write_raw_block(int fd, u64 blkno, int shift, void *blk) { + size_t size = 1ULL << shift; ssize_t ret; - ret = pwrite(fd, blk, SCOUTFS_BLOCK_SIZE, blkno << SCOUTFS_BLOCK_SHIFT); - if (ret != SCOUTFS_BLOCK_SIZE) { + ret = pwrite(fd, blk, size, blkno << shift); + if (ret != size) { fprintf(stderr, "write to blkno %llu returned %zd: %s (%d)\n", blkno, ret, strerror(errno), errno); return -errno; @@ -46,15 +47,18 @@ static int write_raw_block(int fd, u64 blkno, void *blk) /* * Update the block's header and write it out. */ -static int write_block(int fd, u64 blkno, struct scoutfs_super_block *super, +static int write_block(int fd, u64 blkno, int shift, + struct scoutfs_super_block *super, struct scoutfs_block_header *hdr) { + size_t size = 1ULL << shift; + if (super) *hdr = super->hdr; hdr->blkno = cpu_to_le64(blkno); - hdr->crc = cpu_to_le32(crc_block(hdr)); + hdr->crc = cpu_to_le32(crc_block(hdr, size)); - return write_raw_block(fd, blkno, hdr); + return write_raw_block(fd, blkno, shift, hdr); } static float size_flt(u64 nr, unsigned size) @@ -231,7 +235,7 @@ static int write_radix_blocks(struct scoutfs_super_block *super, int fd, return -ENOMEM; for (i = 0; i < alloced; i++) { - blocks[i] = calloc(1, SCOUTFS_BLOCK_SIZE); + blocks[i] = calloc(1, SCOUTFS_BLOCK_LG_SIZE); if (blocks[i] == NULL) { ret = -ENOMEM; goto out; @@ -262,8 +266,10 @@ static int write_radix_blocks(struct scoutfs_super_block *super, int fd, rdx->hdr.fsid = super->hdr.fsid; rdx->hdr.seq = cpu_to_le64(1); rdx->hdr.blkno = cpu_to_le64(blkno + i); - rdx->hdr.crc = cpu_to_le32(crc_block(&rdx->hdr)); - ret = write_raw_block(fd, blkno + i, rdx); + rdx->hdr.crc = cpu_to_le32(crc_block(&rdx->hdr, + SCOUTFS_BLOCK_LG_SIZE)); + ret = write_raw_block(fd, blkno + i, SCOUTFS_BLOCK_LG_SHIFT, + rdx); if (ret < 0) goto out; } @@ -300,20 +306,19 @@ static int write_new_fs(char *path, int fd, u8 quorum_count) u64 blkno; u64 limit; u64 size; - u64 total_blocks; u64 meta_alloc_blocks; u64 next_meta; u64 last_meta; - u64 next_data; + u64 first_data; u64 last_data; int ret; int i; gettimeofday(&tv, NULL); - super = calloc(1, SCOUTFS_BLOCK_SIZE); - bt = calloc(1, SCOUTFS_BLOCK_SIZE); - zeros = calloc(1, SCOUTFS_BLOCK_SIZE); + super = calloc(1, SCOUTFS_BLOCK_SM_SIZE); + bt = calloc(1, SCOUTFS_BLOCK_LG_SIZE); + zeros = calloc(1, SCOUTFS_BLOCK_SM_SIZE); if (!super || !bt || !zeros) { ret = -errno; fprintf(stderr, "failed to allocate block mem: %s (%d)\n", @@ -336,17 +341,17 @@ static int write_new_fs(char *path, int fd, u8 quorum_count) goto out; } - total_blocks = size / SCOUTFS_BLOCK_SIZE; /* metadata blocks start after the quorum blocks */ - next_meta = SCOUTFS_QUORUM_BLKNO + SCOUTFS_QUORUM_BLOCKS; - /* data blocks are after metadata, we'll say 1:4 for now */ - next_data = round_up(next_meta + ((total_blocks - next_meta) / 5), - SCOUTFS_RADIX_BITS); - last_meta = next_data - 1; - last_data = total_blocks - 1; + next_meta = (SCOUTFS_QUORUM_BLKNO + SCOUTFS_QUORUM_BLOCKS) >> + SCOUTFS_BLOCK_SM_LG_SHIFT; + /* use about 1/5 of the device for metadata blocks */ + last_meta = next_meta + ((size / 5) >> SCOUTFS_BLOCK_LG_SHIFT); + /* The rest of the device is data blocks */ + first_data = (last_meta + 1) << SCOUTFS_BLOCK_SM_LG_SHIFT; + last_data = size >> SCOUTFS_BLOCK_SM_SHIFT; /* partially initialize the super so we can use it to init others */ - memset(super, 0, SCOUTFS_BLOCK_SIZE); + memset(super, 0, SCOUTFS_BLOCK_SM_SIZE); pseudo_random_bytes(&super->hdr.fsid, sizeof(super->hdr.fsid)); super->hdr.magic = cpu_to_le32(SCOUTFS_BLOCK_MAGIC_SUPER); super->hdr.seq = cpu_to_le64(1); @@ -357,8 +362,8 @@ static int write_new_fs(char *path, int fd, u8 quorum_count) super->total_meta_blocks = cpu_to_le64(last_meta + 1); super->first_meta_blkno = cpu_to_le64(next_meta); super->last_meta_blkno = cpu_to_le64(last_meta); - super->total_data_blocks = cpu_to_le64(last_data - next_data + 1); - super->first_data_blkno = cpu_to_le64(next_data); + super->total_data_blocks = cpu_to_le64(last_data - first_data + 1); + super->first_data_blkno = cpu_to_le64(first_data); super->last_data_blkno = cpu_to_le64(last_data); super->quorum_count = quorum_count; @@ -369,7 +374,7 @@ static int write_new_fs(char *path, int fd, u8 quorum_count) super->fs_root.ref.seq = cpu_to_le64(1); super->fs_root.height = 1; - memset(bt, 0, SCOUTFS_BLOCK_SIZE); + memset(bt, 0, SCOUTFS_BLOCK_LG_SIZE); bt->hdr.fsid = super->hdr.fsid; bt->hdr.blkno = cpu_to_le64(blkno); bt->hdr.seq = cpu_to_le64(1); @@ -396,7 +401,7 @@ static int write_new_fs(char *path, int fd, u8 quorum_count) btitem = &bt->items[le16_to_cpu(bt->nr_items)]; le16_add_cpu(&bt->nr_items, 1); key = &btitem->key; - own = (void *)bt + SCOUTFS_BLOCK_SIZE - + own = (void *)bt + SCOUTFS_BLOCK_LG_SIZE - SCOUTFS_BTREE_LEAF_ITEM_HASH_BYTES - SCOUTFS_BTREE_VAL_OWNER_BYTES; inode = (void *)own - sizeof(*inode); @@ -433,15 +438,16 @@ static int write_new_fs(char *path, int fd, u8 quorum_count) bt->mid_free_len = cpu_to_le16((void *)inode - (void *)&bt->items[le16_to_cpu(bt->nr_items)]); bt->hdr.magic = cpu_to_le32(SCOUTFS_BLOCK_MAGIC_BTREE); - bt->hdr.crc = cpu_to_le32(crc_block(&bt->hdr)); + bt->hdr.crc = cpu_to_le32(crc_block(&bt->hdr, + SCOUTFS_BLOCK_LG_SIZE)); - ret = write_raw_block(fd, blkno, bt); + ret = write_raw_block(fd, blkno, SCOUTFS_BLOCK_LG_SHIFT, bt); if (ret) goto out; /* write out radix allocator blocks for data */ ret = write_radix_blocks(super, fd, &super->core_data_avail, next_meta, - next_data, last_data); + first_data, last_data); if (ret < 0) goto out; next_meta += ret; @@ -467,7 +473,8 @@ static int write_new_fs(char *path, int fd, u8 quorum_count) /* zero out quorum blocks */ for (i = 0; i < SCOUTFS_QUORUM_BLOCKS; i++) { - ret = write_raw_block(fd, SCOUTFS_QUORUM_BLKNO + i, zeros); + ret = write_raw_block(fd, SCOUTFS_QUORUM_BLKNO + i, + SCOUTFS_BLOCK_SM_SHIFT, zeros); if (ret < 0) { fprintf(stderr, "error zeroing quorum block: %s (%d)\n", strerror(-errno), -errno); @@ -477,11 +484,12 @@ static int write_new_fs(char *path, int fd, u8 quorum_count) /* fill out allocator fields now that we've written our blocks */ super->free_meta_blocks = cpu_to_le64(last_meta - next_meta + 1); - super->free_data_blocks = cpu_to_le64(last_data - next_data + 1); + super->free_data_blocks = cpu_to_le64(last_data - first_data + 1); /* write the super block */ super->hdr.seq = cpu_to_le64(1); - ret = write_block(fd, SCOUTFS_SUPER_BLKNO, NULL, &super->hdr); + ret = write_block(fd, SCOUTFS_SUPER_BLKNO, SCOUTFS_BLOCK_SM_SHIFT, + NULL, &super->hdr); if (ret) goto out; @@ -499,19 +507,17 @@ static int write_new_fs(char *path, int fd, u8 quorum_count) " fsid: %llx\n" " format hash: %llx\n" " uuid: %s\n" - " device blocks: "SIZE_FMT"\n" - " metadata blocks: "SIZE_FMT"\n" - " data blocks: "SIZE_FMT"\n" + " 64KB metadata blocks: "SIZE_FMT"\n" + " 4KB data blocks: "SIZE_FMT"\n" " quorum count: %u\n", path, le64_to_cpu(super->hdr.fsid), le64_to_cpu(super->format_hash), uuid_str, - SIZE_ARGS(total_blocks, SCOUTFS_BLOCK_SIZE), SIZE_ARGS(le64_to_cpu(super->total_meta_blocks), - SCOUTFS_BLOCK_SIZE), + SCOUTFS_BLOCK_LG_SIZE), SIZE_ARGS(le64_to_cpu(super->total_data_blocks), - SCOUTFS_BLOCK_SIZE), + SCOUTFS_BLOCK_SM_SIZE), super->quorum_count); ret = 0; diff --git a/utils/src/print.c b/utils/src/print.c index 4dff2c28..9040611c 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -24,17 +24,18 @@ #include "avl.h" #include "leaf_item_hash.h" -static void *read_block(int fd, u64 blkno) +static void *read_block(int fd, u64 blkno, int shift) { + size_t size = 1ULL << shift; ssize_t ret; void *buf; - buf = malloc(SCOUTFS_BLOCK_SIZE); + buf = malloc(size); if (!buf) return NULL; - ret = pread(fd, buf, SCOUTFS_BLOCK_SIZE, blkno << SCOUTFS_BLOCK_SHIFT); - if (ret != SCOUTFS_BLOCK_SIZE) { + ret = pread(fd, buf, size, blkno << shift); + if (ret != size) { fprintf(stderr, "read blkno %llu returned %zd: %s (%d)\n", blkno, ret, strerror(errno), errno); free(buf); @@ -44,9 +45,9 @@ static void *read_block(int fd, u64 blkno) return buf; } -static void print_block_header(struct scoutfs_block_header *hdr) +static void print_block_header(struct scoutfs_block_header *hdr, int size) { - u32 crc = crc_block(hdr); + u32 crc = crc_block(hdr, size); char valid_str[40]; if (crc != le32_to_cpu(hdr->crc)) @@ -443,7 +444,7 @@ static int print_btree_block(int fd, struct scoutfs_super_block *super, int ret; int i; - bt = read_block(fd, le64_to_cpu(ref->blkno)); + bt = read_block(fd, le64_to_cpu(ref->blkno), SCOUTFS_BLOCK_LG_SHIFT); if (!bt) return -ENOMEM; @@ -545,14 +546,14 @@ static int print_radix_block(int fd, struct scoutfs_radix_ref *par, int level) if (blkno == 0 || blkno == U64_MAX || level == 0) return 0; - rdx = read_block(fd, le64_to_cpu(par->blkno)); + rdx = read_block(fd, le64_to_cpu(par->blkno) , SCOUTFS_BLOCK_LG_SHIFT); if (!rdx) { ret = -ENOMEM; goto out; } printf("radix parent block blkno %llu\n", le64_to_cpu(par->blkno)); - print_block_header(&rdx->hdr); + print_block_header(&rdx->hdr, SCOUTFS_BLOCK_LG_SIZE); printf(" sm_first %u lg_first %u\n", le32_to_cpu(rdx->sm_first), le32_to_cpu(rdx->lg_first)); @@ -652,7 +653,7 @@ static int print_btree_leaf_items(int fd, struct scoutfs_super_block *super, if (ref->blkno == 0) return 0; - bt = read_block(fd, le64_to_cpu(ref->blkno)); + bt = read_block(fd, le64_to_cpu(ref->blkno), SCOUTFS_BLOCK_LG_SHIFT); if (!bt) return -ENOMEM; @@ -717,7 +718,7 @@ static int print_quorum_blocks(int fd, struct scoutfs_super_block *super) for (i = 0; i < SCOUTFS_QUORUM_BLOCKS; i++) { blkno = SCOUTFS_QUORUM_BLKNO + i; free(blk); - blk = read_block(fd, blkno); + blk = read_block(fd, blkno, SCOUTFS_BLOCK_SM_SHIFT); if (!blk) { ret = -ENOMEM; goto out; @@ -766,7 +767,7 @@ static void print_super_block(struct scoutfs_super_block *super, u64 blkno) uuid_unparse(super->uuid, uuid_str); printf("super blkno %llu\n", blkno); - print_block_header(&super->hdr); + print_block_header(&super->hdr, SCOUTFS_BLOCK_SM_SIZE); printf(" format_hash %llx uuid %s\n", le64_to_cpu(super->format_hash), uuid_str); @@ -830,7 +831,7 @@ static int print_volume(int fd) int ret = 0; int err; - super = read_block(fd, SCOUTFS_SUPER_BLKNO); + super = read_block(fd, SCOUTFS_SUPER_BLKNO, SCOUTFS_BLOCK_SM_SHIFT); if (!super) return -ENOMEM; From ffc1e5aa86aef34afcaba04de2ad4469c0dc887d Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Thu, 7 May 2020 11:35:53 -0700 Subject: [PATCH 203/235] scoutfs-utils: update net root format Track the changes in the kernel to communicate btree roots over the network. Signed-off-by: Zach Brown --- utils/src/format.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/utils/src/format.h b/utils/src/format.h index 6c667c97..dc83c506 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -683,6 +683,7 @@ enum { SCOUTFS_NET_CMD_ALLOC_INODES, SCOUTFS_NET_CMD_GET_LOG_TREES, SCOUTFS_NET_CMD_COMMIT_LOG_TREES, + SCOUTFS_NET_CMD_GET_FS_ROOTS, SCOUTFS_NET_CMD_ADVANCE_SEQ, SCOUTFS_NET_CMD_GET_LAST_SEQ, SCOUTFS_NET_CMD_STATFS, @@ -731,6 +732,11 @@ struct scoutfs_net_statfs { __u8 uuid[SCOUTFS_UUID_BYTES]; /* logical volume uuid */ } __packed; +struct scoutfs_net_fs_roots { + struct scoutfs_btree_root fs_root; + struct scoutfs_btree_root logs_root; +} __packed; + struct scoutfs_net_lock { struct scoutfs_key key; __le64 write_version; @@ -738,6 +744,11 @@ struct scoutfs_net_lock { __u8 new_mode; } __packed; +struct scoutfs_net_lock_grant_response { + struct scoutfs_net_lock nl; + struct scoutfs_net_fs_roots nfr; +} __packed; + struct scoutfs_net_lock_recover { __le16 nr; struct scoutfs_net_lock locks[0]; From 5f0dbc5f85d48c72a00028251ecc4c5c8c7bdcfe Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Mon, 11 May 2020 16:57:00 -0700 Subject: [PATCH 204/235] scoutfs-utils: remove radix _first fields The recent cleanup of the radix allocator included removing tracking of the first set bits or references in blocks. Signed-off-by: Zach Brown --- utils/src/format.h | 2 -- utils/src/mkfs.c | 37 ++++++++++++------------------------- utils/src/print.c | 2 -- 3 files changed, 12 insertions(+), 29 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index dc83c506..8418a638 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -167,8 +167,6 @@ struct scoutfs_key { struct scoutfs_radix_block { struct scoutfs_block_header hdr; - __le32 sm_first; - __le32 lg_first; union { struct scoutfs_radix_ref { __le64 blkno; diff --git a/utils/src/mkfs.c b/utils/src/mkfs.c index 1aa46700..d6932b61 100644 --- a/utils/src/mkfs.c +++ b/utils/src/mkfs.c @@ -102,17 +102,7 @@ static void update_parent_ref(struct scoutfs_radix_ref *ref, ref->sm_total = cpu_to_le64(0); ref->lg_total = cpu_to_le64(0); - rdx->sm_first = cpu_to_le32(SCOUTFS_RADIX_REFS); - rdx->lg_first = cpu_to_le32(SCOUTFS_RADIX_REFS); - for (i = 0; i < SCOUTFS_RADIX_REFS; i++) { - if (le32_to_cpu(rdx->sm_first) == SCOUTFS_RADIX_REFS && - rdx->refs[i].sm_total != 0) - rdx->sm_first = cpu_to_le32(i); - if (le32_to_cpu(rdx->lg_first) == SCOUTFS_RADIX_REFS && - rdx->refs[i].lg_total != 0) - rdx->lg_first = cpu_to_le32(i); - le64_add_cpu(&ref->sm_total, le64_to_cpu(rdx->refs[i].sm_total)); le64_add_cpu(&ref->lg_total, @@ -138,6 +128,7 @@ static void set_radix_path(struct scoutfs_super_block *super, int *inds, u64 first, u64 last) { struct scoutfs_radix_block *rdx; + bool shared; int lg_ind; int lg_after; u64 bno; @@ -160,24 +151,24 @@ static void set_radix_path(struct scoutfs_super_block *super, int *inds, if (ref->sm_total == 0) { for (i = 0; i < SCOUTFS_RADIX_REFS; i++) radix_init_ref(&rdx->refs[i], level - 1, false); + shared = false; + } else { + shared = true; } if (left) { /* initialize full refs from left to end */ for (i = ind + 1; i < SCOUTFS_RADIX_REFS; i++) radix_init_ref(&rdx->refs[i], level - 1, true); - } else { - /* initialize full refs from start or left to right */ - for (i = le32_to_cpu(rdx->sm_first) != - SCOUTFS_RADIX_REFS ? - le32_to_cpu(rdx->sm_first) + 1 : 0; - i < ind; i++) - radix_init_ref(&rdx->refs[i], level - 1, true); - /* wipe full refs from right (maybe including) to end */ - for (i = le64_to_cpu(rdx->refs[ind].blkno) == U64_MAX ? - ind : ind + 1; i < SCOUTFS_RADIX_REFS; i++) + } else if (shared) { + /* wipe full refs including right to end */ + for (i = ind; i < SCOUTFS_RADIX_REFS; i++) radix_init_ref(&rdx->refs[i], level - 1, false); + } else { + /* initialize full refs from start to right */ + for (i = 0; i < ind - 1; i++) + radix_init_ref(&rdx->refs[i], level - 1, true); } set_radix_path(super, inds, &rdx->refs[ind], level - 1, left, @@ -185,21 +176,17 @@ static void set_radix_path(struct scoutfs_super_block *super, int *inds, update_parent_ref(ref, rdx); } else { + ind = first - radix_calc_leaf_bit(first); end = last - radix_calc_leaf_bit(last); for (i = ind; i <= end; i++) set_bit_le(i, rdx->bits); - rdx->sm_first = cpu_to_le32(ind); ref->sm_total = cpu_to_le64(end - ind + 1); lg_ind = round_up(ind, SCOUTFS_RADIX_LG_BITS); lg_after = round_down(end + 1, SCOUTFS_RADIX_LG_BITS); - if (lg_ind < SCOUTFS_RADIX_BITS) - rdx->lg_first = cpu_to_le32(lg_ind); - else - rdx->lg_first = cpu_to_le32(SCOUTFS_RADIX_BITS); ref->lg_total = cpu_to_le64(lg_after - lg_ind); } } diff --git a/utils/src/print.c b/utils/src/print.c index 9040611c..ecf4e862 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -554,8 +554,6 @@ static int print_radix_block(int fd, struct scoutfs_radix_ref *par, int level) printf("radix parent block blkno %llu\n", le64_to_cpu(par->blkno)); print_block_header(&rdx->hdr, SCOUTFS_BLOCK_LG_SIZE); - printf(" sm_first %u lg_first %u\n", - le32_to_cpu(rdx->sm_first), le32_to_cpu(rdx->lg_first)); prev = 0; for (i = 0; i < SCOUTFS_RADIX_REFS; i++) { From 9bb32b80037c351281f294ce97bfc9729e260f80 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Mon, 1 Jun 2020 16:18:59 -0700 Subject: [PATCH 205/235] scoutfs-utils: fix last data blkno The calculation of the last valid data blkno was off by one. It was calculating the total number of small blocks that fit in the device size. Signed-off-by: Zach Brown --- utils/src/mkfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/src/mkfs.c b/utils/src/mkfs.c index d6932b61..eddf01e5 100644 --- a/utils/src/mkfs.c +++ b/utils/src/mkfs.c @@ -335,7 +335,7 @@ static int write_new_fs(char *path, int fd, u8 quorum_count) last_meta = next_meta + ((size / 5) >> SCOUTFS_BLOCK_LG_SHIFT); /* The rest of the device is data blocks */ first_data = (last_meta + 1) << SCOUTFS_BLOCK_SM_LG_SHIFT; - last_data = size >> SCOUTFS_BLOCK_SM_SHIFT; + last_data = (size >> SCOUTFS_BLOCK_SM_SHIFT) - 1; /* partially initialize the super so we can use it to init others */ memset(super, 0, SCOUTFS_BLOCK_SM_SIZE); From 35d1ad1422d0c6c96b4313d2d3c77c500c4871f2 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Thu, 2 Jul 2020 15:15:17 -0700 Subject: [PATCH 206/235] scoutfs-utils: switch to using fnv1a for hashing Track the scoutfs module's switch to FNV1a for hashing. Signed-off-by: Zach Brown --- utils/src/format.h | 5 ++-- utils/src/hash.h | 49 ++++++++++++++++++++++++++++++++++++++ utils/src/leaf_item_hash.c | 4 ++-- 3 files changed, 54 insertions(+), 4 deletions(-) create mode 100644 utils/src/hash.h diff --git a/utils/src/format.h b/utils/src/format.h index 8418a638..ac05fecf 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -328,11 +328,12 @@ struct scoutfs_bloom_block { * before the bloom filters fill up and start returning excessive false * positives. */ -#define SCOUTFS_FOREST_BLOOM_NRS 7 +#define SCOUTFS_FOREST_BLOOM_NRS 3 #define SCOUTFS_FOREST_BLOOM_BITS \ (((SCOUTFS_BLOCK_LG_SIZE - sizeof(struct scoutfs_bloom_block)) / \ member_sizeof(struct scoutfs_bloom_block, bits[0])) * \ - member_sizeof(struct scoutfs_bloom_block, bits[0]) * 8) \ + member_sizeof(struct scoutfs_bloom_block, bits[0]) * 8) +#define SCOUTFS_FOREST_BLOOM_FUNC_BITS (SCOUTFS_BLOCK_LG_SHIFT + 3) /* * Keys are first sorted by major key zones. diff --git a/utils/src/hash.h b/utils/src/hash.h new file mode 100644 index 00000000..9b169877 --- /dev/null +++ b/utils/src/hash.h @@ -0,0 +1,49 @@ +#ifndef _SCOUTFS_HASH_H_ +#define _SCOUTFS_HASH_H_ + +/* + * We're using FNV1a for now. It's fine. Ish. + * + * The longer term plan is xxh3 but it looks like it'll take just a bit + * more time to be declared stable and then it needs to be ported to the + * kernel. + * + * - https://fastcompression.blogspot.com/2019/03/presenting-xxh3.html + * - https://github.com/Cyan4973/xxHash/releases/tag/v0.7.4 + */ + +static inline u32 fnv1a32(const void *data, unsigned int len) +{ + u32 hash = 0x811c9dc5; + + while (len--) { + hash ^= *(u8 *)(data++); + hash *= 0x01000193; + } + + return hash; +} + +static inline u64 fnv1a64(const void *data, unsigned int len) +{ + u64 hash = 0xcbf29ce484222325; + + while (len--) { + hash ^= *(u8 *)(data++); + hash *= 0x100000001b3; + } + + return hash; +} + +static inline u32 scoutfs_hash32(const void *data, unsigned int len) +{ + return fnv1a32(data, len); +} + +static inline u64 scoutfs_hash64(const void *data, unsigned int len) +{ + return fnv1a64(data, len); +} + +#endif diff --git a/utils/src/leaf_item_hash.c b/utils/src/leaf_item_hash.c index 4955b1b2..b2946d4b 100644 --- a/utils/src/leaf_item_hash.c +++ b/utils/src/leaf_item_hash.c @@ -1,7 +1,7 @@ #include "sparse.h" #include "util.h" #include "format.h" -#include "crc.h" +#include "hash.h" #include "leaf_item_hash.h" /* @@ -10,7 +10,7 @@ int leaf_item_hash_ind(struct scoutfs_key *key) { - return crc32c(~0, key, sizeof(struct scoutfs_key)) % + return scoutfs_hash32(key, sizeof(struct scoutfs_key)) % SCOUTFS_BTREE_LEAF_ITEM_HASH_NR; } From c0fdd37e5a79229aa4efbe5e714bbf1bb8216b4d Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Tue, 23 Jun 2020 12:41:47 -0700 Subject: [PATCH 207/235] scoutfs-utils: add get_unaligned helpers Signed-off-by: Zach Brown --- utils/src/util.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/utils/src/util.h b/utils/src/util.h index d7fa787e..637fdf5b 100644 --- a/utils/src/util.h +++ b/utils/src/util.h @@ -6,6 +6,8 @@ #include #include +#include "sparse.h" + /* * Generate build warnings if the condition is false but generate no * code at run time if it's true. @@ -85,6 +87,17 @@ do { \ ((unsigned long)log2l((long double)x)); \ }) +#define emit_get_unaligned_le(nr) \ +static inline __u##nr get_unaligned_le##nr(void *buf) \ +{ \ + __le##nr x; \ + memcpy(&x, buf, sizeof(x)); \ + return le##nr##_to_cpu(x); \ +} +emit_get_unaligned_le(16) +emit_get_unaligned_le(32) +emit_get_unaligned_le(64) + /* * return -1,0,+1 based on the memcmp comparison of the minimum of their * two lengths. If their min shared bytes are equal but the lengths From e85fc5b1a745480fd5e7e2a141dfddfbd98ad108 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Mon, 29 Jun 2020 10:41:34 -0700 Subject: [PATCH 208/235] scoutfs-utils: increase btree item value limit Signed-off-by: Zach Brown --- utils/src/format.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/src/format.h b/utils/src/format.h index ac05fecf..3d78dc8d 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -213,7 +213,7 @@ struct scoutfs_avl_node { } __packed; /* when we split we want to have multiple items on each side */ -#define SCOUTFS_BTREE_MAX_VAL_LEN 512 +#define SCOUTFS_BTREE_MAX_VAL_LEN 896 /* each value ends with an offset which lets compaction iterate over values */ #define SCOUTFS_BTREE_VAL_OWNER_BYTES sizeof(__le16) From e82cce36d9b5368be943aa5d55422869f8991f8b Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Tue, 30 Jun 2020 12:23:50 -0700 Subject: [PATCH 209/235] scoutfs-utils: rework get_fs_roots to get_roots Signed-off-by: Zach Brown --- utils/src/format.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index 3d78dc8d..66222e8e 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -682,7 +682,7 @@ enum { SCOUTFS_NET_CMD_ALLOC_INODES, SCOUTFS_NET_CMD_GET_LOG_TREES, SCOUTFS_NET_CMD_COMMIT_LOG_TREES, - SCOUTFS_NET_CMD_GET_FS_ROOTS, + SCOUTFS_NET_CMD_GET_ROOTS, SCOUTFS_NET_CMD_ADVANCE_SEQ, SCOUTFS_NET_CMD_GET_LAST_SEQ, SCOUTFS_NET_CMD_STATFS, @@ -731,7 +731,7 @@ struct scoutfs_net_statfs { __u8 uuid[SCOUTFS_UUID_BYTES]; /* logical volume uuid */ } __packed; -struct scoutfs_net_fs_roots { +struct scoutfs_net_roots { struct scoutfs_btree_root fs_root; struct scoutfs_btree_root logs_root; } __packed; @@ -745,7 +745,7 @@ struct scoutfs_net_lock { struct scoutfs_net_lock_grant_response { struct scoutfs_net_lock nl; - struct scoutfs_net_fs_roots nfr; + struct scoutfs_net_roots roots; } __packed; struct scoutfs_net_lock_recover { From f04a636229f663c28a17c02911328333326974e3 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Tue, 23 Jun 2020 09:51:50 -0700 Subject: [PATCH 210/235] scoutfs-utils: add support for srch Signed-off-by: Zach Brown --- utils/src/format.h | 101 ++++++++++++ utils/src/ioctl.h | 49 ++++-- utils/src/print.c | 165 ++++++++++++++++++- utils/src/{find_xattrs.c => search_xattrs.c} | 57 ++++--- utils/src/srch.c | 46 ++++++ utils/src/srch.h | 7 + 6 files changed, 390 insertions(+), 35 deletions(-) rename utils/src/{find_xattrs.c => search_xattrs.c} (58%) create mode 100644 utils/src/srch.c create mode 100644 utils/src/srch.h diff --git a/utils/src/format.h b/utils/src/format.h index 66222e8e..1e004fbe 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -9,6 +9,8 @@ #define SCOUTFS_BLOCK_MAGIC_BTREE 0xe597f96d #define SCOUTFS_BLOCK_MAGIC_BLOOM 0x31995604 #define SCOUTFS_BLOCK_MAGIC_RADIX 0xebeb5e65 +#define SCOUTFS_BLOCK_MAGIC_SRCH_BLOCK 0x897e4a7d +#define SCOUTFS_BLOCK_MAGIC_SRCH_PARENT 0xb23a2a05 /* * The super block, quorum block, and file data allocation granularity @@ -275,6 +277,93 @@ struct scoutfs_mounted_client_btree_val { #define SCOUTFS_MOUNTED_CLIENT_VOTER (1 << 0) +/* + * srch files are a contiguous run of blocks with compressed entries + * described by a dense parent radix. The files can be stored in + * log_tree items when the files contain unsorted entries written by + * mounts during their transactions. Sorted files of increasing size + * are kept in a btree off the super for searching and further + * compacting. + */ +struct scoutfs_srch_entry { + __le64 hash; + __le64 ino; + __le64 id; +} __packed; + +#define SCOUTFS_SRCH_ENTRY_MAX_BYTES (2 + (sizeof(__u64) * 3)) + +struct scoutfs_srch_ref { + __le64 blkno; + __le64 seq; +} __packed; + +struct scoutfs_srch_file { + struct scoutfs_srch_entry first; + struct scoutfs_srch_entry last; + __le64 blocks; + __le64 entries; + struct scoutfs_srch_ref ref; + __u8 height; +} __packed; + +struct scoutfs_srch_parent { + struct scoutfs_block_header hdr; + struct scoutfs_srch_ref refs[0]; +} __packed; + +#define SCOUTFS_SRCH_PARENT_REFS \ + ((SCOUTFS_BLOCK_LG_SIZE - \ + offsetof(struct scoutfs_srch_parent, refs)) / \ + sizeof(struct scoutfs_srch_ref)) + +struct scoutfs_srch_block { + struct scoutfs_block_header hdr; + struct scoutfs_srch_entry first; + struct scoutfs_srch_entry last; + struct scoutfs_srch_entry tail; + __le32 entry_nr; + __le32 entry_bytes; + __u8 entries[0]; +} __packed; + +/* + * Decoding loads final small deltas with full __u64 loads. Rather than + * check the size before each load we stop coding entries past the point + * where a full size entry could overflow the block. A final entry can + * start at this byte count and consume the rest of the block, though + * its unlikely. + */ +#define SCOUTFS_SRCH_BLOCK_SAFE_BYTES \ + (SCOUTFS_BLOCK_LG_SIZE - sizeof(struct scoutfs_srch_block) - \ + SCOUTFS_SRCH_ENTRY_MAX_BYTES) + +#define SCOUTFS_SRCH_LOG_BLOCK_LIMIT (1024 * 1024 / SCOUTFS_BLOCK_LG_SIZE) +#define SCOUTFS_SRCH_COMPACT_ORDER 3 +#define SCOUTFS_SRCH_COMPACT_NR (1 << SCOUTFS_SRCH_COMPACT_ORDER) + +struct scoutfs_srch_compact_input { + struct scoutfs_radix_root meta_avail; + struct scoutfs_radix_root meta_freed; + __le64 id; + __u8 nr; + __u8 flags; + struct scoutfs_srch_file sfl[SCOUTFS_SRCH_COMPACT_NR]; +} __packed; + +struct scoutfs_srch_compact_result { + struct scoutfs_radix_root meta_avail; + struct scoutfs_radix_root meta_freed; + __le64 id; + __u8 flags; + struct scoutfs_srch_file sfl; +} __packed; + +/* files are insorted logs */ +#define SCOUTFS_SRCH_COMPACT_FLAG_LOG (1 << 0) +/* compaction failed, release inputs */ +#define SCOUTFS_SRCH_COMPACT_FLAG_ERROR (1 << 1) + /* * XXX I imagine we should rename these now that they've evolved to track * all the btrees that clients use during a transaction. It's not just @@ -287,6 +376,7 @@ struct scoutfs_log_trees { struct scoutfs_btree_ref bloom_ref; struct scoutfs_radix_root data_avail; struct scoutfs_radix_root data_freed; + struct scoutfs_srch_file srch_file; __le64 rid; __le64 nr; } __packed; @@ -298,6 +388,7 @@ struct scoutfs_log_trees_val { struct scoutfs_btree_ref bloom_ref; struct scoutfs_radix_root data_avail; struct scoutfs_radix_root data_freed; + struct scoutfs_srch_file srch_file; } __packed; struct scoutfs_log_item_value { @@ -348,6 +439,7 @@ struct scoutfs_bloom_block { #define SCOUTFS_LOCK_CLIENTS_ZONE 7 #define SCOUTFS_TRANS_SEQ_ZONE 8 #define SCOUTFS_MOUNTED_CLIENT_ZONE 9 +#define SCOUTFS_SRCH_ZONE 10 /* inode index zone */ #define SCOUTFS_INODE_INDEX_META_SEQ_TYPE 1 @@ -372,6 +464,11 @@ struct scoutfs_bloom_block { /* lock zone, only ever found in lock ranges, never in persistent items */ #define SCOUTFS_RENAME_TYPE 1 +/* srch zone, only in server btrees */ +#define SCOUTFS_SRCH_LOG_TYPE 1 +#define SCOUTFS_SRCH_BLOCKS_TYPE 2 +#define SCOUTFS_SRCH_BUSY_TYPE 3 + /* * The extents that map blocks in a fixed-size logical region of a file * are packed and stored in item values. The packed extents are @@ -496,6 +593,7 @@ struct scoutfs_super_block { struct scoutfs_btree_root lock_clients; struct scoutfs_btree_root trans_seqs; struct scoutfs_btree_root mounted_clients; + struct scoutfs_btree_root srch_root; } __packed; #define SCOUTFS_ROOT_INO 1 @@ -688,6 +786,8 @@ enum { SCOUTFS_NET_CMD_STATFS, SCOUTFS_NET_CMD_LOCK, SCOUTFS_NET_CMD_LOCK_RECOVER, + SCOUTFS_NET_CMD_SRCH_GET_COMPACT, + SCOUTFS_NET_CMD_SRCH_COMMIT_COMPACT, SCOUTFS_NET_CMD_FAREWELL, SCOUTFS_NET_CMD_UNKNOWN, }; @@ -734,6 +834,7 @@ struct scoutfs_net_statfs { struct scoutfs_net_roots { struct scoutfs_btree_root fs_root; struct scoutfs_btree_root logs_root; + struct scoutfs_btree_root srch_root; } __packed; struct scoutfs_net_lock { diff --git a/utils/src/ioctl.h b/utils/src/ioctl.h index 4b635f88..2f861a4d 100644 --- a/utils/src/ioctl.h +++ b/utils/src/ioctl.h @@ -296,34 +296,57 @@ struct scoutfs_ioctl_listxattr_hidden { /* * Return the inode numbers of inodes which might contain the given - * named xattr. The inode may not have a set xattr with that name, the - * caller must check the returned inodes to see if they match. + * xattr. The inode may not have a set xattr with that name, the caller + * must check the returned inodes to see if they match. * * @next_ino: The next inode number that could be returned. Initialized * to 0 when first searching and set to one past the last inode number * returned to continue searching. - * @name_ptr: The address of the name of the xattr to search for. It does - * not need to be null terminated. - * @inodes_ptr: The address of the array of uint64_t inode numbers in which - * to store inode numbers that may contain the xattr. EFAULT may be returned - * if this address is not naturally aligned. - * @name_bytes: The number of non-null bytes found in the name at name_ptr. + * @last_ino: The last inode number that could be returned. U64_MAX to + * find all inodes. + * @name_ptr: The address of the name of the xattr to search for. It is + * not null terminated. + * @inodes_ptr: The address of the array of uint64_t inode numbers in + * which to store inode numbers that may contain the xattr. EFAULT may + * be returned if this address is not naturally aligned. + * @output_flags: Set as success is returned. If an error is returned + * then this field is undefined and should not be read. * @nr_inodes: The number of elements in the array found at inodes_ptr. + * @name_bytes: The number of non-null bytes found in the name at + * name_ptr. * * This requires the CAP_SYS_ADMIN capability and will return -EPERM if * it's not granted. + * + * The number of inode numbers stored in the inodes_ptr array is + * returned. If nr_inodes is 0 or last_ino is less than next_ino then 0 + * will be immediately returned. + * + * Partial progress can be returned if an error is hit or if nr_inodes + * was larger than the internal limit on the number of inodes returned + * in a search pass. The _END output flag is set if all the results + * including last_ino were searched in this pass. + * + * It's valuable to provide a large inodes array so that all the results + * can be found in one search pass and _END can be set. There are + * significant constant costs for performing each search pass. */ -struct scoutfs_ioctl_find_xattrs { +struct scoutfs_ioctl_search_xattrs { __u64 next_ino; + __u64 last_ino; __u64 name_ptr; __u64 inodes_ptr; + __u64 output_flags; + __u64 nr_inodes; __u16 name_bytes; - __u16 nr_inodes; - __u8 _pad[4]; + __u8 _pad[6]; }; -#define SCOUTFS_IOC_FIND_XATTRS _IOR(SCOUTFS_IOCTL_MAGIC, 9, \ - struct scoutfs_ioctl_find_xattrs) +/* set in output_flags if returned inodes reached last_ino */ +#define SCOUTFS_SEARCH_XATTRS_OFLAG_END (1ULL << 0) + +#define SCOUTFS_IOC_SEARCH_XATTRS _IOR(SCOUTFS_IOCTL_MAGIC, 9, \ + struct scoutfs_ioctl_search_xattrs) /* * Give the user information about the filesystem. diff --git a/utils/src/print.c b/utils/src/print.c index ecf4e862..74d03c34 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -22,6 +22,7 @@ #include "key.h" #include "radix.h" #include "avl.h" +#include "srch.h" #include "leaf_item_hash.h" static void *read_block(int fd, u64 blkno, int shift) @@ -324,6 +325,19 @@ static int print_logs_item(struct scoutfs_key *key, void *val, (root)->height, le64_to_cpu((root)->next_find_bit), \ RADREF_A(&(root)->ref) +#define SRE_FMT "%016llx.%llu.%llu" +#define SRE_A(sre) \ + le64_to_cpu((sre)->hash), le64_to_cpu((sre)->ino), \ + le64_to_cpu((sre)->id) + +#define SRF_FMT \ + "f "SRE_FMT" l "SRE_FMT" blks %llu ents %llu hei %u blkno %llu seq %016llx" +#define SRF_A(srf) \ + SRE_A(&(srf)->first), SRE_A(&(srf)->last), \ + le64_to_cpu((srf)->blocks), le64_to_cpu((srf)->entries), \ + (srf)->height, le64_to_cpu((srf)->ref.blkno), \ + le64_to_cpu((srf)->ref.seq) + /* same as fs item but with a small header in the value */ static int print_log_trees_item(struct scoutfs_key *key, void *val, unsigned val_len, void *arg) @@ -340,7 +354,8 @@ static int print_log_trees_item(struct scoutfs_key *key, void *val, " item_root: height %u blkno %llu seq %llu\n" " bloom_ref: blkno %llu seq %llu\n" " data_avail: "RADROOT_F"\n" - " data_freed: "RADROOT_F"\n", + " data_freed: "RADROOT_F"\n" + " srch_file: "SRF_FMT"\n", RADROOT_A(<v->meta_avail), RADROOT_A(<v->meta_freed), ltv->item_root.height, @@ -349,7 +364,37 @@ static int print_log_trees_item(struct scoutfs_key *key, void *val, le64_to_cpu(ltv->bloom_ref.blkno), le64_to_cpu(ltv->bloom_ref.seq), RADROOT_A(<v->data_avail), - RADROOT_A(<v->data_freed)); + RADROOT_A(<v->data_freed), + SRF_A(<v->srch_file)); + } + + return 0; +} + +static int print_srch_root_item(struct scoutfs_key *key, void *val, + unsigned val_len, void *arg) +{ + struct scoutfs_srch_file *sfl = val; + struct scoutfs_srch_compact_input *scin = val; + int i; + + printf(" "SK_FMT"\n", SK_ARG(key)); + + /* only items in leaf blocks have values */ + if (val) { + if (key->sk_type == SCOUTFS_SRCH_BUSY_TYPE) { + scin = val; + printf(" compact: nr_in %u in_flags 0x%x\n", + scin->nr, scin->flags); + for (i = 0; i < scin->nr; i++) { + sfl = &scin->sfl[i]; + printf(" [%u] "SRF_FMT"\n", + i, SRF_A(sfl)); + } + } else { + sfl = val; + printf(" "SRF_FMT"\n", SRF_A(sfl)); + } } return 0; @@ -595,6 +640,79 @@ out: return ret; } +static int print_srch_block(int fd, struct scoutfs_srch_ref *ref, int level) +{ + struct scoutfs_srch_parent *srp; + struct scoutfs_srch_block *srb; + struct scoutfs_srch_entry sre; + struct scoutfs_srch_entry prev; + u64 blkno; + int pos; + int ret; + int err; + int i; + + blkno = le64_to_cpu(ref->blkno); + if (blkno == 0) + return 0; + + srp = read_block(fd, blkno, SCOUTFS_BLOCK_LG_SHIFT); + if (!srp) { + ret = -ENOMEM; + goto out; + } + srb = (void *)srp; + + printf("srch %sblock blkno %llu\n", level ? "parent " : "", blkno); + print_block_header(&srp->hdr, SCOUTFS_BLOCK_LG_SIZE); + + for (i = 0; level > 0 && i < SCOUTFS_SRCH_PARENT_REFS; i++) { + if (le64_to_cpu(srp->refs[i].blkno) == 0) + continue; + printf(" [%u]: blkno %llu seq %llu\n", + i, le64_to_cpu(srp->refs[i].blkno), + le64_to_cpu(srp->refs[i].seq)); + } + + ret = 0; + for (i = 0; level > 0 && i < SCOUTFS_SRCH_PARENT_REFS; i++) { + if (le64_to_cpu(srp->refs[i].blkno) == 0) + continue; + err = print_srch_block(fd, &srp->refs[i], level - 1); + if (err < 0 && ret == 0) + ret = err; + } + + if (level > 0) + goto out; + + printf(" first "SRE_FMT" last "SRE_FMT" tail "SRE_FMT"\n" + " entry_nr %u entry_bytes %u\n", + SRE_A(&srb->first), SRE_A(&srb->last), SRE_A(&srb->tail), + le32_to_cpu(srb->entry_nr), le32_to_cpu(srb->entry_bytes)); + + memset(&prev, 0, sizeof(prev)); + pos = 0; + for (i = 0; level == 0 && i < le32_to_cpu(srb->entry_nr); i++) { + if (pos > SCOUTFS_SRCH_BLOCK_SAFE_BYTES) { + ret = EIO; + break; + } + + ret = srch_decode_entry(srb->entries + pos, &sre, &prev); + if (ret < 0) + break; + pos += ret; + prev = sre; + printf(" [%u]: (%u) "SRE_FMT"\n", i, ret, SRE_A(&sre)); + } + +out: + free(srp); + + return ret; +} + struct print_recursion_args { struct scoutfs_super_block *super; int fd; @@ -627,6 +745,10 @@ static int print_log_trees_roots(struct scoutfs_key *key, void *val, ltv->data_avail.height - 1); if (err && !ret) ret = err; + err = print_srch_block(pa->fd, <v->srch_file.ref, + ltv->srch_file.height - 1); + if (err && !ret) + ret = err; err = print_btree(pa->fd, pa->super, "", <v->item_root, print_logs_item, NULL); @@ -636,6 +758,33 @@ static int print_log_trees_roots(struct scoutfs_key *key, void *val, return ret; } +static int print_srch_root_files(struct scoutfs_key *key, void *val, + unsigned val_len, void *arg) +{ + struct print_recursion_args *pa = arg; + struct scoutfs_srch_compact_input *scin; + struct scoutfs_srch_file *sfl; + int ret = 0; + int i; + + if (key->sk_type == SCOUTFS_SRCH_BUSY_TYPE) { + scin = val; + for (i = 0; i < scin->nr; i++) { + sfl = &scin->sfl[i]; + ret = print_srch_block(pa->fd, &sfl->ref, + sfl->height - 1); + if (ret < 0) + break; + } + + } else { + sfl = val; + ret = print_srch_block(pa->fd, &sfl->ref, sfl->height - 1); + } + + return ret; +} + static int print_btree_leaf_items(int fd, struct scoutfs_super_block *super, struct scoutfs_btree_ref *ref, print_item_func func, void *arg) @@ -785,6 +934,7 @@ static void print_super_block(struct scoutfs_super_block *super, u64 blkno) " core_data_freed: "RADROOT_F"\n" " lock_clients root: height %u blkno %llu seq %llu\n" " mounted_clients root: height %u blkno %llu seq %llu\n" + " srch_root root: height %u blkno %llu seq %llu\n" " trans_seqs root: height %u blkno %llu seq %llu\n" " fs_root btree root: height %u blkno %llu seq %llu\n", le64_to_cpu(super->next_ino), @@ -812,6 +962,9 @@ static void print_super_block(struct scoutfs_super_block *super, u64 blkno) super->mounted_clients.height, le64_to_cpu(super->mounted_clients.ref.blkno), le64_to_cpu(super->mounted_clients.ref.seq), + super->srch_root.height, + le64_to_cpu(super->srch_root.ref.blkno), + le64_to_cpu(super->srch_root.ref.seq), super->trans_seqs.height, le64_to_cpu(super->trans_seqs.ref.blkno), le64_to_cpu(super->trans_seqs.ref.seq), @@ -869,6 +1022,10 @@ static int print_volume(int fd) if (err && !ret) ret = err; + err = print_btree(fd, super, "srch_root", &super->srch_root, + print_srch_root_item, NULL); + if (err && !ret) + ret = err; err = print_btree(fd, super, "logs_root", &super->logs_root, print_log_trees_item, NULL); if (err && !ret) @@ -876,6 +1033,10 @@ static int print_volume(int fd) pa.super = super; pa.fd = fd; + err = print_btree_leaf_items(fd, super, &super->srch_root.ref, + print_srch_root_files, &pa); + if (err && !ret) + ret = err; err = print_btree_leaf_items(fd, super, &super->logs_root.ref, print_log_trees_roots, &pa); if (err && !ret) diff --git a/utils/src/find_xattrs.c b/utils/src/search_xattrs.c similarity index 58% rename from utils/src/find_xattrs.c rename to utils/src/search_xattrs.c index ab9da445..4d7e86e8 100644 --- a/utils/src/find_xattrs.c +++ b/utils/src/search_xattrs.c @@ -21,18 +21,30 @@ static struct option long_ops[] = { { NULL, 0, NULL, 0} }; -static int find_xattrs_cmd(int argc, char **argv) +/* + * There are significant constant costs to each search call, we + * want to get the inodes in as few calls as possible. + */ +#define BATCH_SIZE 1000000 + +static int search_xattrs_cmd(int argc, char **argv) { - struct scoutfs_ioctl_find_xattrs fx; + struct scoutfs_ioctl_search_xattrs sx; char *path = NULL; char *name = NULL; - u64 inos[32]; + u64 *inos = NULL; int fd = -1; int ret; int c; int i; - memset(&fx, 0, sizeof(fx)); + memset(&sx, 0, sizeof(sx)); + inos = malloc(BATCH_SIZE * sizeof(inos[0])); + if (!inos) { + fprintf(stderr, "inos mem alloc failed\n"); + ret = -ENOMEM; + goto out; + } while ((c = getopt_long(argc, argv, "f:n:", long_ops, NULL)) != -1) { switch (c) { @@ -65,6 +77,12 @@ static int find_xattrs_cmd(int argc, char **argv) goto out; } + if (name == NULL) { + fprintf(stderr, "must specify -n xattr name to search for\n"); + ret = -EINVAL; + goto out; + } + fd = open(path, O_RDONLY); if (fd < 0) { ret = -errno; @@ -73,20 +91,20 @@ static int find_xattrs_cmd(int argc, char **argv) goto out; } - fx.next_ino = 0; - fx.name_ptr = (unsigned long)name; - fx.inodes_ptr = (unsigned long)inos; - fx.name_bytes = strlen(name); - fx.nr_inodes = array_size(inos); + sx.next_ino = 0; + sx.last_ino = U64_MAX; + sx.name_ptr = (unsigned long)name; + sx.inodes_ptr = (unsigned long)inos; + sx.name_bytes = strlen(name); + sx.nr_inodes = BATCH_SIZE; - for (;;) { - - ret = ioctl(fd, SCOUTFS_IOC_FIND_XATTRS, &fx); + do { + ret = ioctl(fd, SCOUTFS_IOC_SEARCH_XATTRS, &sx); if (ret == 0) break; if (ret < 0) { ret = -errno; - fprintf(stderr, "find_xattrs ioctl failed: " + fprintf(stderr, "search_xattrs ioctl failed: " "%s (%d)\n", strerror(errno), errno); goto out; } @@ -94,10 +112,8 @@ static int find_xattrs_cmd(int argc, char **argv) for (i = 0; i < ret; i++) printf("%llu\n", inos[i]); - fx.next_ino = inos[ret - 1] + 1; - if (fx.next_ino == 0) - break; - } + sx.next_ino = inos[ret - 1] + 1; + } while (!(sx.output_flags & SCOUTFS_SEARCH_XATTRS_OFLAG_END)); ret = 0; out: @@ -105,13 +121,14 @@ out: close(fd); free(path); free(name); + free(inos); return ret; }; -static void __attribute__((constructor)) find_xattrs_ctor(void) +static void __attribute__((constructor)) search_xattrs_ctor(void) { - cmd_register("find-xattrs", "-n name -f ", + cmd_register("search-xattrs", "-n name -f ", "print inode numbers of inodes which may have given xattr", - find_xattrs_cmd); + search_xattrs_cmd); } diff --git a/utils/src/srch.c b/utils/src/srch.c new file mode 100644 index 00000000..b58075f7 --- /dev/null +++ b/utils/src/srch.c @@ -0,0 +1,46 @@ +#include + +#include "sparse.h" +#include "util.h" +#include "format.h" +#include "srch.h" + +/* shifting by width is undefined :/ */ +#define BYTE_MASK(b) ((1ULL << (b << 3)) - 1) +static u64 byte_masks[] = { + 0, BYTE_MASK(1), BYTE_MASK(2), BYTE_MASK(3), + BYTE_MASK(4), BYTE_MASK(5), BYTE_MASK(6), BYTE_MASK(7), U64_MAX, +}; + +static u64 decode_u64(void *buf, int bytes) +{ + u64 val = get_unaligned_le64(buf) & byte_masks[bytes]; + + return (val >> 1) ^ (-(val & 1)); +} + +int srch_decode_entry(void *buf, struct scoutfs_srch_entry *sre, + struct scoutfs_srch_entry *prev) +{ + u64 diffs[3]; + u16 lengths; + int bytes; + int tot; + int i; + + lengths = get_unaligned_le16(buf); + tot = 2; + + for (i = 0; i < array_size(diffs); i++) { + bytes = min(8, lengths & 15); + diffs[i] = decode_u64(buf + tot, bytes); + tot += bytes; + lengths >>= 4; + } + + sre->hash = cpu_to_le64(le64_to_cpu(prev->hash) + diffs[0]); + sre->ino = cpu_to_le64(le64_to_cpu(prev->ino) + diffs[1]); + sre->id = cpu_to_le64(le64_to_cpu(prev->id) + diffs[2]); + + return tot; +} diff --git a/utils/src/srch.h b/utils/src/srch.h new file mode 100644 index 00000000..c44c52f9 --- /dev/null +++ b/utils/src/srch.h @@ -0,0 +1,7 @@ +#ifndef _SRCH_H_ +#define _SRCH_H_ + +int srch_decode_entry(void *buf, struct scoutfs_srch_entry *sre, + struct scoutfs_srch_entry *prev); + +#endif From 1e2dc6c1df209ad22ed54b5e00f7d3f7eeb0dfa0 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Thu, 16 Jul 2020 11:38:41 -0700 Subject: [PATCH 211/235] scoutfs-utils: add committed_seq to statfs_more Signed-off-by: Zach Brown --- utils/src/ioctl.h | 5 +++++ utils/src/stat.c | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/utils/src/ioctl.h b/utils/src/ioctl.h index 2f861a4d..1ef0aa36 100644 --- a/utils/src/ioctl.h +++ b/utils/src/ioctl.h @@ -358,12 +358,17 @@ struct scoutfs_ioctl_search_xattrs { * field is set if all of its bytes are within the valid_bytes that the * kernel set on return. * + * @committed_seq: All seqs up to and including this seq have been + * committed. Can be compared with meta_seq and data_seq from inodes in + * stat_more to discover if changes have been committed to disk. + * * New fields are only added to the end of the struct. */ struct scoutfs_ioctl_statfs_more { __u64 valid_bytes; __u64 fsid; __u64 rid; + __u64 committed_seq; } __packed; #define SCOUTFS_IOC_STATFS_MORE _IOR(SCOUTFS_IOCTL_MAGIC, 10, \ diff --git a/utils/src/stat.c b/utils/src/stat.c index 9187feb2..f2beb2ca 100644 --- a/utils/src/stat.c +++ b/utils/src/stat.c @@ -67,6 +67,7 @@ static void print_inode_field(void *st, size_t off) static struct stat_more_field fs_fields[] = { FS_FIELD(fsid), FS_FIELD(rid), + FS_FIELD(committed_seq), { NULL, } }; @@ -81,6 +82,9 @@ static void print_fs_field(void *st, size_t off) case FS_FIELD_OFF(rid): printf("%016llx", sfm->rid); break; + case FS_FIELD_OFF(committed_seq): + printf("%llu", sfm->committed_seq); + break; }; } From e2a919492ddd73a7dd0e98cb593d05cfef9c288c Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Thu, 30 Jul 2020 11:32:52 -0700 Subject: [PATCH 212/235] scoutfs-utils: remove unused xattr index items We're now using the .srch. xattr tags. Signed-off-by: Zach Brown --- utils/src/format.h | 15 +++------------ utils/src/print.c | 11 ----------- 2 files changed, 3 insertions(+), 23 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index 1e004fbe..cd33a7c6 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -121,11 +121,6 @@ struct scoutfs_key { #define skii_major _sk_second #define skii_ino _sk_third -/* xattr index */ -#define skxi_hash _sk_first -#define skxi_ino _sk_second -#define skxi_id _sk_third - /* node orphan inode */ #define sko_rid _sk_first #define sko_ino _sk_second @@ -430,10 +425,9 @@ struct scoutfs_bloom_block { * Keys are first sorted by major key zones. */ #define SCOUTFS_INODE_INDEX_ZONE 1 -#define SCOUTFS_XATTR_INDEX_ZONE 2 -#define SCOUTFS_RID_ZONE 3 -#define SCOUTFS_FS_ZONE 4 -#define SCOUTFS_LOCK_ZONE 5 +#define SCOUTFS_RID_ZONE 2 +#define SCOUTFS_FS_ZONE 3 +#define SCOUTFS_LOCK_ZONE 4 /* Items only stored in server btrees */ #define SCOUTFS_LOG_TREES_ZONE 6 #define SCOUTFS_LOCK_CLIENTS_ZONE 7 @@ -446,9 +440,6 @@ struct scoutfs_bloom_block { #define SCOUTFS_INODE_INDEX_DATA_SEQ_TYPE 2 #define SCOUTFS_INODE_INDEX_NR 3 /* don't forget to update */ -/* xattr index zone */ -#define SCOUTFS_XATTR_INDEX_NAME_TYPE 1 - /* rid zone (also used in server alloc btree) */ #define SCOUTFS_ORPHAN_TYPE 1 diff --git a/utils/src/print.c b/utils/src/print.c index 74d03c34..82784f82 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -221,13 +221,6 @@ static void print_inode_index(struct scoutfs_key *key, void *val, int val_len) le64_to_cpu(key->skii_major), le64_to_cpu(key->skii_ino)); } -static void print_xattr_index(struct scoutfs_key *key, void *val, int val_len) -{ - printf(" xattr index: hash 0x%016llx ino %llu id %llu\n", - le64_to_cpu(key->skxi_hash), le64_to_cpu(key->skxi_ino), - le64_to_cpu(key->skxi_id)); -} - typedef void (*print_func_t)(struct scoutfs_key *key, void *val, int val_len); static print_func_t find_printer(u8 zone, u8 type) @@ -237,10 +230,6 @@ static print_func_t find_printer(u8 zone, u8 type) type <= SCOUTFS_INODE_INDEX_DATA_SEQ_TYPE) return print_inode_index; - if (zone == SCOUTFS_XATTR_INDEX_ZONE && - type >= SCOUTFS_XATTR_INDEX_NAME_TYPE) - return print_xattr_index; - if (zone == SCOUTFS_RID_ZONE) { if (type == SCOUTFS_ORPHAN_TYPE) return print_orphan; From d87e2e01667343a542e025c2c3dc5c5da426f70c Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Fri, 18 Sep 2020 09:50:31 -0700 Subject: [PATCH 213/235] scoutfs-utils: add btree insertion for mkfs Use little helpers to insert items into new single block btrees for mkfs. We're about to insert a whole bunch more items. Signed-off-by: Zach Brown --- utils/src/avl.c | 8 ++++ utils/src/avl.h | 2 + utils/src/btree.c | 97 +++++++++++++++++++++++++++++++++++++++++++++++ utils/src/btree.h | 11 ++++++ utils/src/mkfs.c | 94 ++++++++++++--------------------------------- 5 files changed, 142 insertions(+), 70 deletions(-) create mode 100644 utils/src/btree.c create mode 100644 utils/src/btree.h diff --git a/utils/src/avl.c b/utils/src/avl.c index 5faf39e2..3d86c17f 100644 --- a/utils/src/avl.c +++ b/utils/src/avl.c @@ -10,6 +10,14 @@ static struct scoutfs_avl_node *node_ptr(struct scoutfs_avl_root *root, return off ? (void *)root + le16_to_cpu(off) : NULL; } +__le16 avl_node_off(struct scoutfs_avl_root *root, + struct scoutfs_avl_node *node) +{ + if (!node) + return 0; + return cpu_to_le16((void *)node - (void *)root); +} + struct scoutfs_avl_node *avl_first(struct scoutfs_avl_root *root) { struct scoutfs_avl_node *node = node_ptr(root, root->node); diff --git a/utils/src/avl.h b/utils/src/avl.h index 04c921fe..b72e8e3d 100644 --- a/utils/src/avl.h +++ b/utils/src/avl.h @@ -1,6 +1,8 @@ #ifndef _AVL_H_ #define _AVL_H_ +__le16 avl_node_off(struct scoutfs_avl_root *root, + struct scoutfs_avl_node *node); struct scoutfs_avl_node *avl_first(struct scoutfs_avl_root *root); struct scoutfs_avl_node *avl_next(struct scoutfs_avl_root *root, struct scoutfs_avl_node *node); diff --git a/utils/src/btree.c b/utils/src/btree.c new file mode 100644 index 00000000..5cec36bb --- /dev/null +++ b/utils/src/btree.c @@ -0,0 +1,97 @@ +#include + +#include "sparse.h" +#include "util.h" +#include "format.h" +#include "key.h" +#include "avl.h" +#include "leaf_item_hash.h" +#include "btree.h" + +static void init_block(struct scoutfs_btree_block *bt, int level) +{ + int free; + + free = SCOUTFS_BLOCK_LG_SIZE - sizeof(struct scoutfs_btree_block); + if (level == 0) + free -= SCOUTFS_BTREE_LEAF_ITEM_HASH_BYTES; + + bt->level = level; + bt->mid_free_len = cpu_to_le16(free); +} + +/* + * Point the root at the single leaf block that makes up a btree. + */ +void btree_init_root_single(struct scoutfs_btree_root *root, + struct scoutfs_btree_block *bt, + u64 blkno, u64 seq, __le64 fsid) +{ + root->ref.blkno = cpu_to_le64(blkno); + root->ref.seq = cpu_to_le64(1); + root->height = 1; + + memset(bt, 0, SCOUTFS_BLOCK_LG_SIZE); + bt->hdr.magic = cpu_to_le32(SCOUTFS_BLOCK_MAGIC_BTREE); + bt->hdr.fsid = fsid; + bt->hdr.blkno = cpu_to_le64(blkno); + bt->hdr.seq = cpu_to_le64(1); + + init_block(bt, 0); +} + +static void *alloc_val(struct scoutfs_btree_block *bt, int len) +{ + le16_add_cpu(&bt->mid_free_len, -len); + le16_add_cpu(&bt->total_item_bytes, len); + return (void *)bt + le16_to_cpu(bt->mid_free_len); +} + +/* + * Add a sorted item after all the items in the block. + * + * We simply implement the special case of a wildly imbalanced avl tree. + * Mkfs only ever inserts a handful of items and they'll be rebalanced + * over time. + */ +void btree_append_item(struct scoutfs_btree_block *bt, + struct scoutfs_key *key, void *val, int val_len) +{ + struct scoutfs_btree_item *item; + struct scoutfs_avl_node *prev; + __le16 *own_buf; + __le16 own; + void *val_buf; + + item = &bt->items[le16_to_cpu(bt->nr_items)]; + + if (bt->nr_items) { + assert(scoutfs_key_compare(key, &(item - 1)->key) > 0); + prev = &(item - 1)->node; + + item->node.height = prev->height++; + item->node.left = avl_node_off(&bt->item_root, prev); + prev->parent = avl_node_off(&bt->item_root, &item->node); + } + + bt->item_root.node = avl_node_off(&bt->item_root, &item->node); + le16_add_cpu(&bt->nr_items, 1); + le16_add_cpu(&bt->mid_free_len, + -(u16)sizeof(struct scoutfs_btree_item)); + le16_add_cpu(&bt->total_item_bytes, sizeof(struct scoutfs_btree_item)); + + item->key = *key; + leaf_item_hash_insert(bt, &item->key, + cpu_to_le16((void *)item - (void *)bt)); + if (val_len == 0) + return; + + own_buf = alloc_val(bt, SCOUTFS_BTREE_VAL_OWNER_BYTES); + own = cpu_to_le16((void *)item - (void *)bt); + memcpy(own_buf, &own, sizeof(own)); + + val_buf = alloc_val(bt, val_len); + item->val_off = cpu_to_le16((void *)val_buf - (void *)bt); + item->val_len = cpu_to_le16(val_len); + memcpy(val_buf, val, val_len); +} diff --git a/utils/src/btree.h b/utils/src/btree.h new file mode 100644 index 00000000..1f176a8b --- /dev/null +++ b/utils/src/btree.h @@ -0,0 +1,11 @@ +#ifndef _BTREE_H_ +#define _BTREE_H_ + +void btree_init_root_single(struct scoutfs_btree_root *root, + struct scoutfs_btree_block *bt, + u64 blkno, u64 seq, __le64 fsid); + +void btree_append_item(struct scoutfs_btree_block *bt, + struct scoutfs_key *key, void *val, int val_len); + +#endif diff --git a/utils/src/mkfs.c b/utils/src/mkfs.c index eddf01e5..373de609 100644 --- a/utils/src/mkfs.c +++ b/utils/src/mkfs.c @@ -27,6 +27,7 @@ #include "key.h" #include "bitops.h" #include "radix.h" +#include "btree.h" #include "leaf_item_hash.h" static int write_raw_block(int fd, u64 blkno, int shift, void *blk) @@ -281,14 +282,11 @@ out: static int write_new_fs(char *path, int fd, u8 quorum_count) { struct scoutfs_super_block *super; - struct scoutfs_inode *inode; + struct scoutfs_inode inode; struct scoutfs_btree_block *bt; - struct scoutfs_btree_item *btitem; - struct scoutfs_avl_node *par; - struct scoutfs_key *key; + struct scoutfs_key key; struct timeval tv; char uuid_str[37]; - __le16 *own; void *zeros; u64 blkno; u64 limit; @@ -356,75 +354,31 @@ static int write_new_fs(char *path, int fd, u8 quorum_count) /* fs root starts with root inode and its index items */ blkno = next_meta++; + btree_init_root_single(&super->fs_root, bt, blkno, 1, super->hdr.fsid); - super->fs_root.ref.blkno = cpu_to_le64(blkno); - super->fs_root.ref.seq = cpu_to_le64(1); - super->fs_root.height = 1; + memset(&key, 0, sizeof(key)); + key.sk_zone = SCOUTFS_INODE_INDEX_ZONE; + key.sk_type = SCOUTFS_INODE_INDEX_META_SEQ_TYPE; + key.skii_ino = cpu_to_le64(SCOUTFS_ROOT_INO); + btree_append_item(bt, &key, NULL, 0); - memset(bt, 0, SCOUTFS_BLOCK_LG_SIZE); - bt->hdr.fsid = super->hdr.fsid; - bt->hdr.blkno = cpu_to_le64(blkno); - bt->hdr.seq = cpu_to_le64(1); + memset(&key, 0, sizeof(key)); + key.sk_zone = SCOUTFS_FS_ZONE; + key.ski_ino = cpu_to_le64(SCOUTFS_ROOT_INO); + key.sk_type = SCOUTFS_INODE_TYPE; - /* meta seq index for the root inode */ - btitem = &bt->items[le16_to_cpu(bt->nr_items)]; - le16_add_cpu(&bt->nr_items, 1); - key = &btitem->key; + memset(&inode, 0, sizeof(inode)); + inode.next_readdir_pos = cpu_to_le64(2); + inode.nlink = cpu_to_le32(SCOUTFS_DIRENT_FIRST_POS); + inode.mode = cpu_to_le32(0755 | 0040000); + inode.atime.sec = cpu_to_le64(tv.tv_sec); + inode.atime.nsec = cpu_to_le32(tv.tv_usec * 1000); + inode.ctime.sec = inode.atime.sec; + inode.ctime.nsec = inode.atime.nsec; + inode.mtime.sec = inode.atime.sec; + inode.mtime.nsec = inode.atime.nsec; + btree_append_item(bt, &key, &inode, sizeof(inode)); - bt->item_root.node = cpu_to_le16((void *)&btitem->node - - (void *)&bt->item_root); - btitem->node.height = 2; - btitem->val_len = cpu_to_le16(0); - - key->sk_zone = SCOUTFS_INODE_INDEX_ZONE; - key->sk_type = SCOUTFS_INODE_INDEX_META_SEQ_TYPE; - key->skii_ino = cpu_to_le64(SCOUTFS_ROOT_INO); - - leaf_item_hash_insert(bt, &btitem->key, - cpu_to_le16((void *)btitem - (void *)bt)); - - /* root inode */ - par = &btitem->node; - btitem = &bt->items[le16_to_cpu(bt->nr_items)]; - le16_add_cpu(&bt->nr_items, 1); - key = &btitem->key; - own = (void *)bt + SCOUTFS_BLOCK_LG_SIZE - - SCOUTFS_BTREE_LEAF_ITEM_HASH_BYTES - - SCOUTFS_BTREE_VAL_OWNER_BYTES; - inode = (void *)own - sizeof(*inode); - - par->right = cpu_to_le16((void *)&btitem->node - - (void *)&bt->item_root); - btitem->node.height = 1; - btitem->node.parent = cpu_to_le16((void *)par - (void *)&bt->item_root); - btitem->val_off = cpu_to_le16((void *)inode - (void *)bt); - btitem->val_len = cpu_to_le16(sizeof(*inode)); - le16_add_cpu(&bt->total_item_bytes, le16_to_cpu(btitem->val_len) + - SCOUTFS_BTREE_VAL_OWNER_BYTES); - - key->sk_zone = SCOUTFS_FS_ZONE; - key->ski_ino = cpu_to_le64(SCOUTFS_ROOT_INO); - key->sk_type = SCOUTFS_INODE_TYPE; - - inode->next_readdir_pos = cpu_to_le64(2); - inode->nlink = cpu_to_le32(SCOUTFS_DIRENT_FIRST_POS); - inode->mode = cpu_to_le32(0755 | 0040000); - inode->atime.sec = cpu_to_le64(tv.tv_sec); - inode->atime.nsec = cpu_to_le32(tv.tv_usec * 1000); - inode->ctime.sec = inode->atime.sec; - inode->ctime.nsec = inode->atime.nsec; - inode->mtime.sec = inode->atime.sec; - inode->mtime.nsec = inode->atime.nsec; - - leaf_item_hash_insert(bt, &btitem->key, - cpu_to_le16((void *)btitem - (void *)bt)); - *own = cpu_to_le16((void *)btitem - (void *)bt); - - le16_add_cpu(&bt->total_item_bytes, le16_to_cpu(bt->nr_items) * - sizeof(struct scoutfs_btree_item)); - bt->mid_free_len = cpu_to_le16((void *)inode - - (void *)&bt->items[le16_to_cpu(bt->nr_items)]); - bt->hdr.magic = cpu_to_le32(SCOUTFS_BLOCK_MAGIC_BTREE); bt->hdr.crc = cpu_to_le32(crc_block(&bt->hdr, SCOUTFS_BLOCK_LG_SIZE)); From 23711f05f6fc689dd5d48370b0727dadacb75884 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Fri, 18 Sep 2020 15:06:37 -0700 Subject: [PATCH 214/235] scoutfs-utils: alloc and data uses full extents Signed-off-by: Zach Brown --- utils/src/format.h | 108 +++++++++++++--- utils/src/mkfs.c | 272 ++++++++++++--------------------------- utils/src/print.c | 313 +++++++++++++++++++++------------------------ utils/src/radix.c | 106 --------------- utils/src/radix.h | 13 -- 5 files changed, 315 insertions(+), 497 deletions(-) delete mode 100644 utils/src/radix.c delete mode 100644 utils/src/radix.h diff --git a/utils/src/format.h b/utils/src/format.h index cd33a7c6..d5a78ade 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -11,6 +11,7 @@ #define SCOUTFS_BLOCK_MAGIC_RADIX 0xebeb5e65 #define SCOUTFS_BLOCK_MAGIC_SRCH_BLOCK 0x897e4a7d #define SCOUTFS_BLOCK_MAGIC_SRCH_PARENT 0xb23a2a05 +#define SCOUTFS_BLOCK_MAGIC_ALLOC_LIST 0x8a93ac83 /* * The super block, quorum block, and file data allocation granularity @@ -143,10 +144,10 @@ struct scoutfs_key { #define sks_ino _sk_first #define sks_nr _sk_second -/* packed extents */ -#define skpe_ino _sk_first -#define skpe_base _sk_second -#define skpe_part _sk_fourth +/* data extents */ +#define skdx_ino _sk_first +#define skdx_end _sk_second +#define skdx_len _sk_third /* log trees */ #define sklt_rid _sk_first @@ -162,6 +163,13 @@ struct scoutfs_key { /* mounted clients */ #define skmc_rid _sk_first +/* free extents by blkno */ +#define skfb_end _sk_second +#define skfb_len _sk_third +/* free extents by len */ +#define skfl_neglen _sk_second +#define skfl_blkno _sk_third + struct scoutfs_radix_block { struct scoutfs_block_header hdr; union { @@ -266,6 +274,53 @@ struct scoutfs_btree_block { #define SCOUTFS_BTREE_LEAF_ITEM_HASH_BYTES \ (SCOUTFS_BTREE_LEAF_ITEM_HASH_NR * sizeof(__le16)) +struct scoutfs_alloc_list_ref { + __le64 blkno; + __le64 seq; +}__packed; + +/* + * first_nr tracks the nr of the first block in the list and is used for + * allocation sizing. total_nr is the sum of the nr of all the blocks in + * the list and is used for calculating total free block counts. + */ +struct scoutfs_alloc_list_head { + struct scoutfs_alloc_list_ref ref; + __le64 total_nr; + __le32 first_nr; +}__packed; + +/* + * While the main allocator uses extent items in btree blocks, metadata + * allocations for a single transaction are recorded in arrays in + * blocks. This limits the number of allocations and frees needed to + * cow and modify the structure. The blocks can be stored in a list + * which lets us create a persistent log of pending frees that are + * generated as we cow btree blocks to insert freed extents. + * + * The array floats in the block so that both adding and removing blknos + * only modifies an index. + */ +struct scoutfs_alloc_list_block { + struct scoutfs_block_header hdr; + struct scoutfs_alloc_list_ref next; + __le32 start; + __le32 nr; + __le64 blknos[0]; /* naturally aligned for sorting */ +}__packed; + +#define SCOUTFS_ALLOC_LIST_MAX_BLOCKS \ + ((SCOUTFS_BLOCK_LG_SIZE - sizeof(struct scoutfs_alloc_list_block)) / \ + (member_sizeof(struct scoutfs_alloc_list_block, blknos[0]))) + +/* + * These can safely be initialized to all-zeros. + */ +struct scoutfs_alloc_root { + __le64 total_len; + struct scoutfs_btree_root root; +}__packed; + struct scoutfs_mounted_client_btree_val { __u8 flags; } __packed; @@ -338,8 +393,8 @@ struct scoutfs_srch_block { #define SCOUTFS_SRCH_COMPACT_NR (1 << SCOUTFS_SRCH_COMPACT_ORDER) struct scoutfs_srch_compact_input { - struct scoutfs_radix_root meta_avail; - struct scoutfs_radix_root meta_freed; + struct scoutfs_alloc_list_head meta_avail; + struct scoutfs_alloc_list_head meta_freed; __le64 id; __u8 nr; __u8 flags; @@ -347,8 +402,8 @@ struct scoutfs_srch_compact_input { } __packed; struct scoutfs_srch_compact_result { - struct scoutfs_radix_root meta_avail; - struct scoutfs_radix_root meta_freed; + struct scoutfs_alloc_list_head meta_avail; + struct scoutfs_alloc_list_head meta_freed; __le64 id; __u8 flags; struct scoutfs_srch_file sfl; @@ -365,24 +420,24 @@ struct scoutfs_srch_compact_result { * about item logs, it's about clients making changes to trees. */ struct scoutfs_log_trees { - struct scoutfs_radix_root meta_avail; - struct scoutfs_radix_root meta_freed; + struct scoutfs_alloc_list_head meta_avail; + struct scoutfs_alloc_list_head meta_freed; struct scoutfs_btree_root item_root; struct scoutfs_btree_ref bloom_ref; - struct scoutfs_radix_root data_avail; - struct scoutfs_radix_root data_freed; + struct scoutfs_alloc_root data_avail; + struct scoutfs_alloc_root data_freed; struct scoutfs_srch_file srch_file; __le64 rid; __le64 nr; } __packed; struct scoutfs_log_trees_val { - struct scoutfs_radix_root meta_avail; - struct scoutfs_radix_root meta_freed; + struct scoutfs_alloc_list_head meta_avail; + struct scoutfs_alloc_list_head meta_freed; struct scoutfs_btree_root item_root; struct scoutfs_btree_ref bloom_ref; - struct scoutfs_radix_root data_avail; - struct scoutfs_radix_root data_freed; + struct scoutfs_alloc_root data_avail; + struct scoutfs_alloc_root data_freed; struct scoutfs_srch_file srch_file; } __packed; @@ -434,6 +489,7 @@ struct scoutfs_bloom_block { #define SCOUTFS_TRANS_SEQ_ZONE 8 #define SCOUTFS_MOUNTED_CLIENT_ZONE 9 #define SCOUTFS_SRCH_ZONE 10 +#define SCOUTFS_FREE_EXTENT_ZONE 11 /* inode index zone */ #define SCOUTFS_INODE_INDEX_META_SEQ_TYPE 1 @@ -450,7 +506,7 @@ struct scoutfs_bloom_block { #define SCOUTFS_READDIR_TYPE 4 #define SCOUTFS_LINK_BACKREF_TYPE 5 #define SCOUTFS_SYMLINK_TYPE 6 -#define SCOUTFS_PACKED_EXTENT_TYPE 7 +#define SCOUTFS_DATA_EXTENT_TYPE 7 /* lock zone, only ever found in lock ranges, never in persistent items */ #define SCOUTFS_RENAME_TYPE 1 @@ -460,6 +516,10 @@ struct scoutfs_bloom_block { #define SCOUTFS_SRCH_BLOCKS_TYPE 2 #define SCOUTFS_SRCH_BUSY_TYPE 3 +/* free extents in allocator btrees in client and server, by blkno or len */ +#define SCOUTFS_FREE_EXTENT_BLKNO_TYPE 1 +#define SCOUTFS_FREE_EXTENT_LEN_TYPE 2 + /* * The extents that map blocks in a fixed-size logical region of a file * are packed and stored in item values. The packed extents are @@ -491,6 +551,12 @@ struct scoutfs_packed_extent { #define SCOUTFS_PACKEXT_BASE_MASK (~((__u64)SCOUTFS_PACKEXT_BLOCKS - 1)) #define SCOUTFS_PACKEXT_MAX_BYTES SCOUTFS_MAX_VAL_SIZE +/* file data extents have start and len in key */ +struct scoutfs_data_extent_val { + __le64 blkno; + __u8 flags; +} __packed; + #define SEF_OFFLINE (1 << 0) #define SEF_UNWRITTEN (1 << 1) #define SEF_UNKNOWN (U8_MAX << 2) @@ -575,10 +641,10 @@ struct scoutfs_super_block { __le64 unmount_barrier; __u8 quorum_count; struct scoutfs_inet_addr server_addr; - struct scoutfs_radix_root core_meta_avail; - struct scoutfs_radix_root core_meta_freed; - struct scoutfs_radix_root core_data_avail; - struct scoutfs_radix_root core_data_freed; + struct scoutfs_alloc_root meta_alloc[2]; + struct scoutfs_alloc_root data_alloc; + struct scoutfs_alloc_list_head server_meta_avail[2]; + struct scoutfs_alloc_list_head server_meta_freed[2]; struct scoutfs_btree_root fs_root; struct scoutfs_btree_root logs_root; struct scoutfs_btree_root lock_clients; diff --git a/utils/src/mkfs.c b/utils/src/mkfs.c index 373de609..569e403f 100644 --- a/utils/src/mkfs.c +++ b/utils/src/mkfs.c @@ -26,7 +26,6 @@ #include "dev.h" #include "key.h" #include "bitops.h" -#include "radix.h" #include "btree.h" #include "leaf_item_hash.h" @@ -92,185 +91,39 @@ static char *size_str(u64 nr, unsigned size) #define SIZE_ARGS(nr, sz) (nr), size_flt(nr, sz), size_str(nr, sz) /* - * Update a reference to a block of references that has been modified. We - * walk all the references and rebuild the ref tracking. + * Write the single btree block that contains the blkno and len indexed + * items to store the given extent, and update the root to point to it. */ -static void update_parent_ref(struct scoutfs_radix_ref *ref, - struct scoutfs_radix_block *rdx) +static int write_alloc_root(struct scoutfs_super_block *super, int fd, + struct scoutfs_alloc_root *root, + struct scoutfs_btree_block *bt, + u64 blkno, u64 start, u64 len) { - int i; + struct scoutfs_key key; - ref->sm_total = cpu_to_le64(0); - ref->lg_total = cpu_to_le64(0); + btree_init_root_single(&root->root, bt, blkno, 1, super->hdr.fsid); + root->total_len = cpu_to_le64(len); - for (i = 0; i < SCOUTFS_RADIX_REFS; i++) { - le64_add_cpu(&ref->sm_total, - le64_to_cpu(rdx->refs[i].sm_total)); - le64_add_cpu(&ref->lg_total, - le64_to_cpu(rdx->refs[i].lg_total)); - } -} + memset(&key, 0, sizeof(key)); + key.sk_zone = SCOUTFS_FREE_EXTENT_ZONE; + key.sk_type = SCOUTFS_FREE_EXTENT_BLKNO_TYPE; + key.skii_ino = cpu_to_le64(SCOUTFS_ROOT_INO); + key.skfb_end = cpu_to_le64(start + len - 1); + key.skfb_len = cpu_to_le64(len); + btree_append_item(bt, &key, NULL, 0); -/* - * Initialize all the blocks in a path to a leaf with the given blocks - * set. We know that we're being called to set all the bits in a region - * by setting the left and right partial leafs of the region. We first - * set the left and set full references down the left path, then we're - * called on the right and set full to the left and clear full refs past - * the right. - * - * The caller provides an array of block buffers and a starting block - * number to allocate blocks from and reference blocks within. It's the - * world's dumbest block cache. - */ -static void set_radix_path(struct scoutfs_super_block *super, int *inds, - struct scoutfs_radix_ref *ref, int level, bool left, - void **blocks, u64 blkno_base, u64 *next_blkno, - u64 first, u64 last) -{ - struct scoutfs_radix_block *rdx; - bool shared; - int lg_ind; - int lg_after; - u64 bno; - int ind; - int end; - int i; + memset(&key, 0, sizeof(key)); + key.sk_zone = SCOUTFS_FREE_EXTENT_ZONE; + key.sk_type = SCOUTFS_FREE_EXTENT_LEN_TYPE; + key.skii_ino = cpu_to_le64(SCOUTFS_ROOT_INO); + key.skfl_neglen = cpu_to_le64(-len); + key.skfl_blkno = cpu_to_le64(start); + btree_append_item(bt, &key, NULL, 0); - if (ref->blkno == 0) { - bno = (*next_blkno)++; - ref->blkno = cpu_to_le64(bno); - ref->seq = cpu_to_le64(1); - } + bt->hdr.crc = cpu_to_le32(crc_block(&bt->hdr, + SCOUTFS_BLOCK_LG_SIZE)); - rdx = blocks[le64_to_cpu(ref->blkno) - blkno_base]; - - if (level) { - ind = inds[level]; - - /* initialize empty parent blocks with empty refs */ - if (ref->sm_total == 0) { - for (i = 0; i < SCOUTFS_RADIX_REFS; i++) - radix_init_ref(&rdx->refs[i], level - 1, false); - shared = false; - } else { - shared = true; - } - - if (left) { - /* initialize full refs from left to end */ - for (i = ind + 1; i < SCOUTFS_RADIX_REFS; i++) - radix_init_ref(&rdx->refs[i], level - 1, true); - - } else if (shared) { - /* wipe full refs including right to end */ - for (i = ind; i < SCOUTFS_RADIX_REFS; i++) - radix_init_ref(&rdx->refs[i], level - 1, false); - } else { - /* initialize full refs from start to right */ - for (i = 0; i < ind - 1; i++) - radix_init_ref(&rdx->refs[i], level - 1, true); - } - - set_radix_path(super, inds, &rdx->refs[ind], level - 1, left, - blocks, blkno_base, next_blkno, first, last); - update_parent_ref(ref, rdx); - - } else { - - ind = first - radix_calc_leaf_bit(first); - end = last - radix_calc_leaf_bit(last); - for (i = ind; i <= end; i++) - set_bit_le(i, rdx->bits); - - ref->sm_total = cpu_to_le64(end - ind + 1); - - lg_ind = round_up(ind, SCOUTFS_RADIX_LG_BITS); - lg_after = round_down(end + 1, SCOUTFS_RADIX_LG_BITS); - - ref->lg_total = cpu_to_le64(lg_after - lg_ind); - } -} - -/* - * Initialize a new radix allocator with the region of bits set. We - * initialize and write populated blocks down the paths to the two ends - * of the interval and write full refs in between. - */ -static int write_radix_blocks(struct scoutfs_super_block *super, int fd, - struct scoutfs_radix_root *root, - u64 blkno, u64 first, u64 last) -{ - struct scoutfs_radix_block *rdx; - void **blocks; - u64 next_blkno; - u64 edge; - u8 height; - int alloced; - int used; - int *inds; - int ret; - int i; - - height = radix_height_from_last(last); - inds = alloca(sizeof(inds[0]) * height); - alloced = height * 2; - next_blkno = blkno; - - /* allocate all the blocks we might need */ - blocks = calloc(alloced, sizeof(*blocks)); - if (!blocks) - return -ENOMEM; - - for (i = 0; i < alloced; i++) { - blocks[i] = calloc(1, SCOUTFS_BLOCK_LG_SIZE); - if (blocks[i] == NULL) { - ret = -ENOMEM; - goto out; - } - } - - /* initialize empty root ref */ - memset(root, 0, sizeof(struct scoutfs_radix_root)); - root->height = height; - radix_init_ref(&root->ref, height - 1, false); - - edge = radix_calc_leaf_bit(first) + SCOUTFS_RADIX_BITS - 1; - radix_calc_level_inds(inds, height, first); - set_radix_path(super, inds, &root->ref, root->height - 1, true, blocks, - blkno, &next_blkno, first, min(edge, last)); - - edge = radix_calc_leaf_bit(last); - radix_calc_level_inds(inds, height, last); - set_radix_path(super, inds, &root->ref, root->height - 1, false, blocks, - blkno, &next_blkno, max(first, edge), last); - - used = next_blkno - blkno; - - /* write out all the dirtied blocks */ - for (i = 0; i < used; i++) { - rdx = blocks[i]; - rdx->hdr.magic = cpu_to_le32(SCOUTFS_BLOCK_MAGIC_RADIX); - rdx->hdr.fsid = super->hdr.fsid; - rdx->hdr.seq = cpu_to_le64(1); - rdx->hdr.blkno = cpu_to_le64(blkno + i); - rdx->hdr.crc = cpu_to_le32(crc_block(&rdx->hdr, - SCOUTFS_BLOCK_LG_SIZE)); - ret = write_raw_block(fd, blkno + i, SCOUTFS_BLOCK_LG_SHIFT, - rdx); - if (ret < 0) - goto out; - } - - ret = used; -out: - if (blocks) { - for (i = 0; i < alloced && blocks[i]; i++) - free(blocks[i]); - free(blocks); - } - - return ret; + return write_raw_block(fd, blkno, SCOUTFS_BLOCK_LG_SHIFT, bt); } /* @@ -283,6 +136,7 @@ static int write_new_fs(char *path, int fd, u8 quorum_count) { struct scoutfs_super_block *super; struct scoutfs_inode inode; + struct scoutfs_alloc_list_block *lblk; struct scoutfs_btree_block *bt; struct scoutfs_key key; struct timeval tv; @@ -291,11 +145,12 @@ static int write_new_fs(char *path, int fd, u8 quorum_count) u64 blkno; u64 limit; u64 size; - u64 meta_alloc_blocks; u64 next_meta; u64 last_meta; u64 first_data; u64 last_data; + u64 meta_start; + u64 meta_len; int ret; int i; @@ -386,31 +241,62 @@ static int write_new_fs(char *path, int fd, u8 quorum_count) if (ret) goto out; - /* write out radix allocator blocks for data */ - ret = write_radix_blocks(super, fd, &super->core_data_avail, next_meta, - first_data, last_data); + /* fill an avail list block for the first server transaction */ + blkno = next_meta++; + lblk = (void *)bt; + memset(lblk, 0, SCOUTFS_BLOCK_LG_SIZE); + + lblk->hdr.magic = cpu_to_le32(SCOUTFS_BLOCK_MAGIC_ALLOC_LIST); + lblk->hdr.fsid = super->hdr.fsid; + lblk->hdr.blkno = cpu_to_le64(blkno); + lblk->hdr.seq = cpu_to_le64(1); + + meta_len = (64 * 1024 * 1024) >> SCOUTFS_BLOCK_LG_SHIFT; + for (i = 0; i < meta_len; i++) { + lblk->blknos[i] = cpu_to_le64(next_meta); + next_meta++; + } + lblk->nr = cpu_to_le32(i); + + super->server_meta_avail[0].ref.blkno = lblk->hdr.blkno; + super->server_meta_avail[0].ref.seq = lblk->hdr.seq; + super->server_meta_avail[0].total_nr = le32_to_le64(lblk->nr); + super->server_meta_avail[0].first_nr = lblk->nr; + + lblk->hdr.crc = cpu_to_le32(crc_block(&bt->hdr, SCOUTFS_BLOCK_LG_SIZE)); + ret = write_raw_block(fd, blkno, SCOUTFS_BLOCK_LG_SHIFT, lblk); + if (ret) + goto out; + + /* the data allocator has a single extent */ + blkno = next_meta++; + ret = write_alloc_root(super, fd, &super->data_alloc, bt, + blkno, first_data, + le64_to_cpu(super->total_data_blocks)); if (ret < 0) goto out; - next_meta += ret; - - super->core_data_freed.height = super->core_data_avail.height; - radix_init_ref(&super->core_data_freed.ref, 0, false); - - meta_alloc_blocks = radix_blocks_needed(next_meta, last_meta); /* - * Write out radix alloc blocks, knowing that the region we mark - * has to start after the blocks we store the allocator itself in. + * Initialize all the meta_alloc roots with an equal portion of + * the free metadata extents, excluding the blocks we're going + * to use for the allocators. */ - ret = write_radix_blocks(super, fd, &super->core_meta_avail, - next_meta, next_meta + meta_alloc_blocks, - last_meta); - if (ret < 0) - goto out; - next_meta += ret; + meta_start = next_meta + array_size(super->meta_alloc); + meta_len = DIV_ROUND_UP(last_meta - meta_start + 1, + array_size(super->meta_alloc)); - super->core_meta_freed.height = super->core_meta_avail.height; - radix_init_ref(&super->core_meta_freed.ref, 0, false); + /* each meta alloc root contains a portion of free metadata extents */ + for (i = 0; i < array_size(super->meta_alloc); i++) { + blkno = next_meta++; + ret = write_alloc_root(super, fd, &super->meta_alloc[i], bt, + blkno, meta_start, + min(meta_len, + last_meta - meta_start + 1)); + if (ret < 0) + goto out; + + meta_start += meta_len; + } /* zero out quorum blocks */ for (i = 0; i < SCOUTFS_QUORUM_BLOCKS; i++) { diff --git a/utils/src/print.c b/utils/src/print.c index 82784f82..cac546dc 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -20,7 +20,6 @@ #include "cmd.h" #include "crc.h" #include "key.h" -#include "radix.h" #include "avl.h" #include "srch.h" #include "leaf_item_hash.h" @@ -149,70 +148,17 @@ static void print_symlink(struct scoutfs_key *key, void *val, int val_len) le64_to_cpu(key->sks_ino), le64_to_cpu(key->sks_nr), name); } -static void print_packed_extent(struct scoutfs_key *key, void *val, int val_len) +static void print_data_extent(struct scoutfs_key *key, void *val, int val_len) { - struct scoutfs_packed_extent *pe; - __le64 led; + struct scoutfs_data_extent_val *dv = val; u64 iblock; - u64 blkno = 0; - u64 diff; - int off = 0; - int i = 0; + iblock = le64_to_cpu(key->skdx_end) - le64_to_cpu(key->skdx_len) + 1; - /* - * Ugh, this is the only item that has state between items. It - * probably shouldn't. And I'm too lazy to plumb an arg through - * all the printers. - */ - static struct scoutfs_key next_key; - static u64 next_blkno; - - if (scoutfs_key_compare(key, &next_key) == 0) - blkno = next_blkno; - - iblock = le64_to_cpu(key->skpe_base) << SCOUTFS_PACKEXT_BASE_SHIFT; - - while (off < val_len) { - printf(" [%u] off %u: ibl %llu ", i, off, iblock); - - if (off + sizeof(struct scoutfs_packed_extent) > val_len) { - printf("(packed extent struct exceeds item)\n"); - return; - } - - pe = val + off; - printf("cnt %u dfb %u fl %x fin %u ", - le16_to_cpu(pe->count), pe->diff_bytes, pe->flags, - pe->final); - - off += sizeof(struct scoutfs_packed_extent); - - if (off + pe->diff_bytes > val_len) { - printf("(packed extent diff bytes exceeds item)\n"); - return; - } - - if (pe->diff_bytes) { - led = 0; - memcpy(&led, pe->le_blkno_diff, pe->diff_bytes); - diff = le64_to_cpu(led); - diff = (diff >> 1) ^ (-(diff & 1)); - blkno += diff; - printf("dif %lld blk %llu\n", (s64)diff, blkno); - blkno += le16_to_cpu(pe->count) - 1; - } else { - printf("(sparse)\n"); - } - - iblock += le16_to_cpu(pe->count); - off += pe->diff_bytes; - i++; - } - - next_blkno = blkno; - next_key = *key; - scoutfs_key_inc(&next_key); + printf(" extent: ino %llu iblock %llu len %llu blkno %llu flags %x\n", + le64_to_cpu(key->skdx_ino), iblock, + le64_to_cpu(key->skdx_len), + le64_to_cpu(dv->blkno), dv->flags); } static void print_inode_index(struct scoutfs_key *key, void *val, int val_len) @@ -243,8 +189,7 @@ static print_func_t find_printer(u8 zone, u8 type) case SCOUTFS_READDIR_TYPE: return print_dirent; case SCOUTFS_SYMLINK_TYPE: return print_symlink; case SCOUTFS_LINK_BACKREF_TYPE: return print_dirent; - case SCOUTFS_PACKED_EXTENT_TYPE: - return print_packed_extent; + case SCOUTFS_DATA_EXTENT_TYPE: return print_data_extent; } } @@ -302,17 +247,31 @@ static int print_logs_item(struct scoutfs_key *key, void *val, return 0; } -#define RADREF_F \ - "blkno %llu seq %llu sm_total %llu lg_total %llu" -#define RADREF_A(ref) \ - le64_to_cpu((ref)->blkno), le64_to_cpu((ref)->seq), \ - le64_to_cpu((ref)->sm_total), le64_to_cpu((ref)->lg_total) +#define BTREF_F \ + "blkno %llu seq %llu" +#define BTREF_A(ref) \ + le64_to_cpu((ref)->blkno), le64_to_cpu((ref)->seq) -#define RADROOT_F \ - "height %u next_find_bit %llu ref: "RADREF_F -#define RADROOT_A(root) \ - (root)->height, le64_to_cpu((root)->next_find_bit), \ - RADREF_A(&(root)->ref) +#define BTROOT_F \ + BTREF_F" height %u" +#define BTROOT_A(root) \ + BTREF_A(&(root)->ref), (root)->height + +#define AL_REF_F \ + "blkno %llu seq %llu" +#define AL_REF_A(p) \ + le64_to_cpu((p)->blkno), le64_to_cpu((p)->seq) + +#define AL_HEAD_F \ + AL_REF_F" total_nr %llu first_nr %u" +#define AL_HEAD_A(p) \ + AL_REF_A(&(p)->ref), le64_to_cpu((p)->total_nr),\ + le32_to_cpu((p)->first_nr) + +#define ALCROOT_F \ + BTROOT_F" total_len %llu" +#define ALCROOT_A(ar) \ + BTROOT_A(&(ar)->root), le64_to_cpu((ar)->total_len) #define SRE_FMT "%016llx.%llu.%llu" #define SRE_A(sre) \ @@ -338,22 +297,22 @@ static int print_log_trees_item(struct scoutfs_key *key, void *val, /* only items in leaf blocks have values */ if (val) { - printf(" meta_avail: "RADROOT_F"\n" - " meta_freed: "RADROOT_F"\n" + printf(" meta_avail: "AL_HEAD_F"\n" + " meta_freed: "AL_HEAD_F"\n" " item_root: height %u blkno %llu seq %llu\n" " bloom_ref: blkno %llu seq %llu\n" - " data_avail: "RADROOT_F"\n" - " data_freed: "RADROOT_F"\n" + " data_avail: "ALCROOT_F"\n" + " data_freed: "ALCROOT_F"\n" " srch_file: "SRF_FMT"\n", - RADROOT_A(<v->meta_avail), - RADROOT_A(<v->meta_freed), + AL_HEAD_A(<v->meta_avail), + AL_HEAD_A(<v->meta_freed), ltv->item_root.height, le64_to_cpu(ltv->item_root.ref.blkno), le64_to_cpu(ltv->item_root.ref.seq), le64_to_cpu(ltv->bloom_ref.blkno), le64_to_cpu(ltv->bloom_ref.seq), - RADROOT_A(<v->data_avail), - RADROOT_A(<v->data_freed), + ALCROOT_A(<v->data_avail), + ALCROOT_A(<v->data_freed), SRF_A(<v->srch_file)); } @@ -417,6 +376,24 @@ static int print_mounted_client_entry(struct scoutfs_key *key, void *val, return 0; } +static int print_alloc_item(struct scoutfs_key *key, void *val, + unsigned val_len, void *arg) +{ + if (key->sk_type == SCOUTFS_FREE_EXTENT_BLKNO_TYPE) + printf(" free extent: blkno %llu len %llu end %llu\n", + le64_to_cpu(key->skfb_end) - + le64_to_cpu(key->skfb_len) + 1, + le64_to_cpu(key->skfb_len), + le64_to_cpu(key->skfb_end)); + else + printf(" free extent: blkno %llu len %llu neglen %lld\n", + le64_to_cpu(key->skfl_blkno), + -le64_to_cpu(key->skfl_neglen), + (long long)le64_to_cpu(key->skfl_neglen)); + + return 0; +} + typedef int (*print_item_func)(struct scoutfs_key *key, void *val, unsigned val_len, void *arg); @@ -566,67 +543,55 @@ static int print_btree(int fd, struct scoutfs_super_block *super, char *which, return ret; } -static int print_radix_block(int fd, struct scoutfs_radix_ref *par, int level) +static int print_alloc_list_block(int fd, char *str, + struct scoutfs_alloc_list_ref *ref) { - struct scoutfs_radix_block *rdx; + struct scoutfs_alloc_list_block *lblk; + struct scoutfs_alloc_list_ref next; u64 blkno; - int prev; - int ret; - int err; + u64 start; + u64 len; + int wid; int i; - /* XXX not printing bitmap leaf blocks */ - blkno = le64_to_cpu(par->blkno); - if (blkno == 0 || blkno == U64_MAX || level == 0) + blkno = le64_to_cpu(ref->blkno); + if (blkno == 0) return 0; - rdx = read_block(fd, le64_to_cpu(par->blkno) , SCOUTFS_BLOCK_LG_SHIFT); - if (!rdx) { - ret = -ENOMEM; - goto out; + lblk = read_block(fd, blkno, SCOUTFS_BLOCK_LG_SHIFT); + if (!lblk) + return -ENOMEM; + + printf("%s alloc_list_block blkno %llu\n", str, blkno); + print_block_header(&lblk->hdr, SCOUTFS_BLOCK_LG_SIZE); + printf(" next "AL_REF_F" start %u nr %u\n", + AL_REF_A(&lblk->next), le32_to_cpu(lblk->start), + le32_to_cpu(lblk->nr)); + + if (lblk->nr) { + wid = printf(" exts: "); + start = 0; + len = 0; + for (i = 0; i < le32_to_cpu(lblk->nr); i++) { + if (len == 0) + start = le64_to_cpu(lblk->blknos[i]); + len++; + + if (i == (le32_to_cpu(lblk->nr) - 1) || + start + len != le64_to_cpu(lblk->blknos[i + 1])) { + if (wid >= 72) + wid = printf("\n "); + + wid += printf("%llu,%llu ", start, len); + len = 0; + } + } + printf("\n"); } - printf("radix parent block blkno %llu\n", le64_to_cpu(par->blkno)); - print_block_header(&rdx->hdr, SCOUTFS_BLOCK_LG_SIZE); - - prev = 0; - for (i = 0; i < SCOUTFS_RADIX_REFS; i++) { - /* only skip if the next ref is identically full/empty */ - if ((le64_to_cpu(rdx->refs[i].blkno) == 0 || - le64_to_cpu(rdx->refs[i].blkno) == U64_MAX) && - (i + 1) < SCOUTFS_RADIX_REFS && - (le64_to_cpu(rdx->refs[i].blkno) == - le64_to_cpu(rdx->refs[i + 1].blkno))) { - prev++; - continue; - } - - if (prev) { - printf(" [%u - %u]: (%s): ", i - prev, i, - (le64_to_cpu(rdx->refs[i].blkno) == 0) ? "empty" : - "full"); - prev = 0; - } else { - printf(" [%u]: ", i); - } - - printf(RADREF_F"\n", RADREF_A(&rdx->refs[i])); - } - - ret = 0; - for (i = 0; i < SCOUTFS_RADIX_REFS; i++) { - if (le64_to_cpu(rdx->refs[i].blkno) != 0 && - le64_to_cpu(rdx->refs[i].blkno) != U64_MAX) { - err = print_radix_block(fd, &rdx->refs[i], level - 1); - if (err < 0 && ret == 0) - ret = err; - } - } - -out: - free(rdx); - - return ret; + next = lblk->next; + free(lblk); + return print_alloc_list_block(fd, str, &next); } static int print_srch_block(int fd, struct scoutfs_srch_ref *ref, int level) @@ -718,20 +683,20 @@ static int print_log_trees_roots(struct scoutfs_key *key, void *val, /* XXX doesn't print the bloom block */ - err = print_radix_block(pa->fd, <v->meta_avail.ref, - ltv->meta_avail.height - 1); + err = print_alloc_list_block(pa->fd, "ltv_meta_avail", + <v->meta_avail.ref); if (err && !ret) ret = err; - err = print_radix_block(pa->fd, <v->meta_freed.ref, - ltv->meta_avail.height - 1); + err = print_alloc_list_block(pa->fd, "ltv_meta_freed", + <v->meta_freed.ref); if (err && !ret) ret = err; - err = print_radix_block(pa->fd, <v->data_avail.ref, - ltv->data_avail.height - 1); + err = print_btree(pa->fd, pa->super, "data_avail", + <v->data_avail.root, print_alloc_item, NULL); if (err && !ret) ret = err; - err = print_radix_block(pa->fd, <v->meta_freed.ref, - ltv->data_avail.height - 1); + err = print_btree(pa->fd, pa->super, "data_freed", + <v->data_freed.root, print_alloc_item, NULL); if (err && !ret) ret = err; err = print_srch_block(pa->fd, <v->srch_file.ref, @@ -917,10 +882,13 @@ static void print_super_block(struct scoutfs_super_block *super, u64 blkno) " total_data_blocks %llu first_data_blkno %llu last_data_blkno %llu free_data_blocks %llu\n" " quorum_fenced_term %llu quorum_server_term %llu unmount_barrier %llu\n" " quorum_count %u server_addr %s\n" - " core_meta_avail: "RADROOT_F"\n" - " core_meta_freed: "RADROOT_F"\n" - " core_data_avail: "RADROOT_F"\n" - " core_data_freed: "RADROOT_F"\n" + " meta_alloc[0]: "ALCROOT_F"\n" + " meta_alloc[1]: "ALCROOT_F"\n" + " data_alloc: "ALCROOT_F"\n" + " server_meta_avail[0]: "AL_HEAD_F"\n" + " server_meta_avail[1]: "AL_HEAD_F"\n" + " server_meta_freed[0]: "AL_HEAD_F"\n" + " server_meta_freed[1]: "AL_HEAD_F"\n" " lock_clients root: height %u blkno %llu seq %llu\n" " mounted_clients root: height %u blkno %llu seq %llu\n" " srch_root root: height %u blkno %llu seq %llu\n" @@ -941,10 +909,13 @@ static void print_super_block(struct scoutfs_super_block *super, u64 blkno) le64_to_cpu(super->unmount_barrier), super->quorum_count, server_addr, - RADROOT_A(&super->core_meta_avail), - RADROOT_A(&super->core_meta_freed), - RADROOT_A(&super->core_data_avail), - RADROOT_A(&super->core_data_freed), + ALCROOT_A(&super->meta_alloc[0]), + ALCROOT_A(&super->meta_alloc[1]), + ALCROOT_A(&super->data_alloc), + AL_HEAD_A(&super->server_meta_avail[0]), + AL_HEAD_A(&super->server_meta_avail[1]), + AL_HEAD_A(&super->server_meta_freed[0]), + AL_HEAD_A(&super->server_meta_freed[1]), super->lock_clients.height, le64_to_cpu(super->lock_clients.ref.blkno), le64_to_cpu(super->lock_clients.ref.seq), @@ -968,8 +939,10 @@ static int print_volume(int fd) { struct scoutfs_super_block *super = NULL; struct print_recursion_args pa; + char str[80]; int ret = 0; int err; + int i; super = read_block(fd, SCOUTFS_SUPER_BLKNO, SCOUTFS_BLOCK_SM_SHIFT); if (!super) @@ -994,20 +967,32 @@ static int print_volume(int fd) if (err && !ret) ret = err; - err = print_radix_block(fd, &super->core_meta_avail.ref, - super->core_meta_avail.height - 1); - if (err && !ret) - ret = err; - err = print_radix_block(fd, &super->core_meta_freed.ref, - super->core_meta_freed.height - 1); - if (err && !ret) - ret = err; - err = print_radix_block(fd, &super->core_data_avail.ref, - super->core_data_avail.height - 1); - if (err && !ret) - ret = err; - err = print_radix_block(fd, &super->core_data_freed.ref, - super->core_data_freed.height - 1); + for (i = 0; i < array_size(super->server_meta_avail); i++) { + snprintf(str, sizeof(str), "server_meta_avail[%u]", i); + err = print_alloc_list_block(fd, str, + &super->server_meta_avail[i].ref); + if (err && !ret) + ret = err; + } + + for (i = 0; i < array_size(super->server_meta_freed); i++) { + snprintf(str, sizeof(str), "server_meta_freed[%u]", i); + err = print_alloc_list_block(fd, str, + &super->server_meta_freed[i].ref); + if (err && !ret) + ret = err; + } + + for (i = 0; i < array_size(super->meta_alloc); i++) { + snprintf(str, sizeof(str), "meta_alloc[%u]", i); + err = print_btree(fd, super, str, &super->meta_alloc[i].root, + print_alloc_item, NULL); + if (err && !ret) + ret = err; + } + + err = print_btree(fd, super, "data_alloc", &super->data_alloc.root, + print_alloc_item, NULL); if (err && !ret) ret = err; diff --git a/utils/src/radix.c b/utils/src/radix.c deleted file mode 100644 index 66a400a3..00000000 --- a/utils/src/radix.c +++ /dev/null @@ -1,106 +0,0 @@ -#include - -#include "sparse.h" -#include "util.h" -#include "format.h" -#include "radix.h" - -/* return the height of a tree needed to store the last bit */ -u8 radix_height_from_last(u64 last) -{ - u64 bit = SCOUTFS_RADIX_BITS - 1; - u64 mult = SCOUTFS_RADIX_BITS; - int i; - - for (i = 1; i <= U8_MAX; i++) { - if (bit >= last) - return i; - bit += (u64)(SCOUTFS_RADIX_REFS - 1) * mult; - mult *= SCOUTFS_RADIX_REFS; - } - - return U8_MAX; -} - -u64 radix_full_subtree_total(int level) -{ - u64 total = SCOUTFS_RADIX_BITS; - int i; - - for (i = 1; i <= level; i++) - total *= SCOUTFS_RADIX_REFS; - - return total; -} - -/* - * Initialize a reference to a block at the given level. - */ -void radix_init_ref(struct scoutfs_radix_ref *ref, int level, bool full) -{ - u64 tot; - - if (full) { - tot = radix_full_subtree_total(level); - - ref->blkno = cpu_to_le64(U64_MAX); - ref->seq = cpu_to_le64(0); - ref->sm_total = cpu_to_le64(tot); - ref->lg_total = cpu_to_le64(tot); - } else { - ref->blkno = cpu_to_le64(0); - ref->seq = cpu_to_le64(0); - ref->sm_total = cpu_to_le64(0); - ref->lg_total = cpu_to_le64(0); - } -} - -void radix_calc_level_inds(int *inds, u8 height, u64 bit) -{ - u32 ind; - int i; - - ind = bit % SCOUTFS_RADIX_BITS; - bit = bit / SCOUTFS_RADIX_BITS; - inds[0] = ind; - - for (i = 1; i < height; i++) { - ind = bit % SCOUTFS_RADIX_REFS; - bit = bit / SCOUTFS_RADIX_REFS; - inds[i] = ind; - } -} - -u64 radix_calc_leaf_bit(u64 bit) -{ - return bit - (bit % SCOUTFS_RADIX_BITS); -} - -/* - * The number of blocks needed to initialize a radix with left and right - * paths. The first time we find a level where the parent refs are at - * different indices determines where the paths diverge at lower levels. - * If the refs never diverge then the two paths traverse the same blocks - * and we just need blocks for the height of the tree. - */ -int radix_blocks_needed(u64 a, u64 b) -{ - u8 height = radix_height_from_last(b); - int *a_inds; - int *b_inds; - int i; - - a_inds = alloca(sizeof(a_inds[0] * height)); - b_inds = alloca(sizeof(b_inds[0] * height)); - - radix_calc_level_inds(a_inds, height, a); - radix_calc_level_inds(b_inds, height, b); - - for (i = height - 1; i > 0; i--) { - if (a_inds[i] != b_inds[i]) { - return (i * 2) + (height - i); - } - } - - return height; -} diff --git a/utils/src/radix.h b/utils/src/radix.h deleted file mode 100644 index 31f4db8c..00000000 --- a/utils/src/radix.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef _RADIX_H_ -#define _RADIX_H_ - -#include - -u8 radix_height_from_last(u64 last); -u64 radix_full_subtree_total(int level); -void radix_init_ref(struct scoutfs_radix_ref *ref, int level, bool full); -void radix_calc_level_inds(int *inds, u8 height, u64 bit); -u64 radix_calc_leaf_bit(u64 bit); -int radix_blocks_needed(u64 a, u64 b); - -#endif From e6385784f5875dff114f7b8c4828afffef142357 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Mon, 21 Sep 2020 14:29:16 -0700 Subject: [PATCH 215/235] scoutfs-utils: remove unused radix format Signed-off-by: Zach Brown --- utils/src/format.h | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index d5a78ade..428c94e6 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -8,7 +8,6 @@ #define SCOUTFS_BLOCK_MAGIC_SUPER 0x103c428b #define SCOUTFS_BLOCK_MAGIC_BTREE 0xe597f96d #define SCOUTFS_BLOCK_MAGIC_BLOOM 0x31995604 -#define SCOUTFS_BLOCK_MAGIC_RADIX 0xebeb5e65 #define SCOUTFS_BLOCK_MAGIC_SRCH_BLOCK 0x897e4a7d #define SCOUTFS_BLOCK_MAGIC_SRCH_PARENT 0xb23a2a05 #define SCOUTFS_BLOCK_MAGIC_ALLOC_LIST 0x8a93ac83 @@ -183,29 +182,6 @@ struct scoutfs_radix_block { } __packed; } __packed; -struct scoutfs_radix_root { - __u8 height; - __le64 next_find_bit; - struct scoutfs_radix_ref ref; -} __packed; - -#define SCOUTFS_RADIX_REFS \ - ((SCOUTFS_BLOCK_LG_SIZE - \ - offsetof(struct scoutfs_radix_block, refs[0])) / \ - sizeof(struct scoutfs_radix_ref)) - -/* 8 meg regions with 4k data blocks */ -#define SCOUTFS_RADIX_LG_SHIFT 11 -#define SCOUTFS_RADIX_LG_BITS (1 << SCOUTFS_RADIX_LG_SHIFT) -#define SCOUTFS_RADIX_LG_MASK (SCOUTFS_RADIX_LG_BITS - 1) - -/* round block bits down to a multiple of large ranges */ -#define SCOUTFS_RADIX_BITS \ - (((SCOUTFS_BLOCK_LG_SIZE - \ - offsetof(struct scoutfs_radix_block, bits[0])) * 8) & \ - ~(__u64)SCOUTFS_RADIX_LG_MASK) -#define SCOUTFS_RADIX_BITS_BYTES (SCOUTFS_RADIX_BITS / 8) - struct scoutfs_avl_root { __le16 node; } __packed; From fddfde62e6a03f2b0c7bff062b70735857d39488 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Wed, 30 Sep 2020 11:44:17 -0700 Subject: [PATCH 216/235] scoutfs-utils: add endian size swapping macros Signed-off-by: Zach Brown --- utils/src/endian_swap.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/utils/src/endian_swap.h b/utils/src/endian_swap.h index e64d119e..fae7d8df 100644 --- a/utils/src/endian_swap.h +++ b/utils/src/endian_swap.h @@ -9,4 +9,7 @@ #define be32_to_le32(x) cpu_to_le32(be32_to_cpu(x)) #define be16_to_le16(x) cpu_to_le16(be16_to_cpu(x)) +#define le16_to_le64(x) cpu_to_le64(le16_to_cpu(x)) +#define le32_to_le64(x) cpu_to_le64(le32_to_cpu(x)) + #endif From 36c426d555573c90bf6031c1fcebb0379075642e Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Mon, 12 Oct 2020 13:27:31 -0700 Subject: [PATCH 217/235] scoutfs-utils: add total m/d blocks to statfs Signed-off-by: Zach Brown --- utils/src/ioctl.h | 2 ++ utils/src/stat.c | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/utils/src/ioctl.h b/utils/src/ioctl.h index 1ef0aa36..c953be45 100644 --- a/utils/src/ioctl.h +++ b/utils/src/ioctl.h @@ -369,6 +369,8 @@ struct scoutfs_ioctl_statfs_more { __u64 fsid; __u64 rid; __u64 committed_seq; + __u64 total_meta_blocks; + __u64 total_data_blocks; } __packed; #define SCOUTFS_IOC_STATFS_MORE _IOR(SCOUTFS_IOCTL_MAGIC, 10, \ diff --git a/utils/src/stat.c b/utils/src/stat.c index f2beb2ca..ef9c6247 100644 --- a/utils/src/stat.c +++ b/utils/src/stat.c @@ -68,6 +68,8 @@ static struct stat_more_field fs_fields[] = { FS_FIELD(fsid), FS_FIELD(rid), FS_FIELD(committed_seq), + FS_FIELD(total_meta_blocks), + FS_FIELD(total_data_blocks), { NULL, } }; @@ -85,6 +87,12 @@ static void print_fs_field(void *st, size_t off) case FS_FIELD_OFF(committed_seq): printf("%llu", sfm->committed_seq); break; + case FS_FIELD_OFF(total_meta_blocks): + printf("%llu", sfm->total_meta_blocks); + break; + case FS_FIELD_OFF(total_data_blocks): + printf("%llu", sfm->total_data_blocks); + break; }; } From a19e1512771a17e28a8e0aa9e9a2f6bfce72c1c1 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Mon, 12 Oct 2020 13:43:32 -0700 Subject: [PATCH 218/235] scoutfs-utils: add fd which uses alloc_detail Add the df command which uses the new alloc_detail ioctl to show df for the metadata and data devices separately. Signed-off-by: Zach Brown --- utils/src/df.c | 126 +++++++++++++++++++++++++++++++++++++++++++++ utils/src/format.h | 14 +++-- utils/src/ioctl.h | 17 ++++++ 3 files changed, 149 insertions(+), 8 deletions(-) create mode 100644 utils/src/df.c diff --git a/utils/src/df.c b/utils/src/df.c new file mode 100644 index 00000000..86c1ae0f --- /dev/null +++ b/utils/src/df.c @@ -0,0 +1,126 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sparse.h" +#include "util.h" +#include "format.h" +#include "ioctl.h" +#include "cmd.h" + +#define COLS 8 + +static int df_cmd(int argc, char **argv) +{ + struct scoutfs_ioctl_alloc_detail ad; + struct scoutfs_ioctl_alloc_detail_entry *ade = NULL; + struct scoutfs_ioctl_statfs_more sfm; + char *title[COLS]; + u64 fields[COLS]; + int wid[COLS]; + u64 nr = 4096 / sizeof(*ade); + u64 meta_free = 0; + u64 data_free = 0; + int ret; + int fd; + int i; + + if (argc != 2) { + fprintf(stderr, "must specify path\n"); + return -EINVAL; + } + + fd = open(argv[1], O_RDONLY); + if (fd < 0) { + ret = -errno; + fprintf(stderr, "failed to open '%s': %s (%d)\n", + argv[1], strerror(errno), errno); + return ret; + } + + sfm.valid_bytes = sizeof(struct scoutfs_ioctl_statfs_more); + ret = ioctl(fd, SCOUTFS_IOC_STATFS_MORE, &sfm); + if (ret < 0) { + fprintf(stderr, "statfs_more returned %d: error %s (%d)\n", + ret, strerror(errno), errno); + ret = -EIO; + goto out; + } + + do { + free(ade); + ade = calloc(nr, sizeof(*ade)); + if (!ade) { + ret = -ENOMEM; + goto out; + } + + ad.entries_ptr = (intptr_t)ade; + ad.entries_nr = nr; + ret = ioctl(fd, SCOUTFS_IOC_ALLOC_DETAIL, &ad); + if (ret < 0 && errno == EOVERFLOW) + nr = nr + (nr >> 2); + } while (ret < 0 && errno == EOVERFLOW); + + if (ret < 0) { + fprintf(stderr, "alloc_detail returned %d: error %s (%d)\n", + ret, strerror(errno), errno); + ret = -EIO; + goto out; + } + + for (i = 0; i < ret; i++) { + if (ade[i].meta) + meta_free += ade[i].blocks; + else + data_free += ade[i].blocks; + } + + title[0] = "64K-Meta"; + title[1] = "Used"; + title[2] = "Avail"; + title[3] = "Use%"; + title[4] = "4K-Data"; + title[5] = "Used"; + title[6] = "Avail"; + title[7] = "Use%"; + + fields[0] = sfm.total_meta_blocks; + fields[1] = sfm.total_meta_blocks - meta_free; + fields[2] = meta_free; + fields[3] = fields[1] * 100 / fields[0]; + fields[4] = sfm.total_data_blocks; + fields[5] = sfm.total_data_blocks - data_free; + fields[6] = data_free; + fields[7] = fields[5] * 100 / fields[4]; + + for (i = 0; i < array_size(fields); i++) + wid[i] = max(snprintf(NULL, 0, "%s", title[i]), + snprintf(NULL, 0, "%llu", fields[i])); + + for (i = 0; i < array_size(fields); i++) + printf("%*s ", wid[i], title[i]); + printf("\n"); + for (i = 0; i < array_size(fields); i++) + wid[i] = printf("%*llu ", wid[i], fields[i]); + printf("\n"); + + ret = 0; +out: + free(ade); + return ret; +} + +static void __attribute__((constructor)) df_ctor(void) +{ + cmd_register("df", "", + "show metadata and data block usage", df_cmd); +} diff --git a/utils/src/format.h b/utils/src/format.h index 428c94e6..ee0e0d69 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -297,6 +297,12 @@ struct scoutfs_alloc_root { struct scoutfs_btree_root root; }__packed; +/* types of allocators, exposed to alloc_detail ioctl */ +#define SCOUTFS_ALLOC_OWNER_NONE 0 +#define SCOUTFS_ALLOC_OWNER_SERVER 1 +#define SCOUTFS_ALLOC_OWNER_MOUNT 2 +#define SCOUTFS_ALLOC_OWNER_SRCH 3 + struct scoutfs_mounted_client_btree_val { __u8 flags; } __packed; @@ -816,7 +822,6 @@ enum { SCOUTFS_NET_CMD_GET_ROOTS, SCOUTFS_NET_CMD_ADVANCE_SEQ, SCOUTFS_NET_CMD_GET_LAST_SEQ, - SCOUTFS_NET_CMD_STATFS, SCOUTFS_NET_CMD_LOCK, SCOUTFS_NET_CMD_LOCK_RECOVER, SCOUTFS_NET_CMD_SRCH_GET_COMPACT, @@ -857,13 +862,6 @@ struct scoutfs_net_inode_alloc { __le64 nr; } __packed; -struct scoutfs_net_statfs { - __le64 total_blocks; /* total blocks in device */ - __le64 next_ino; /* next unused inode number */ - __le64 bfree; /* free blocks */ - __u8 uuid[SCOUTFS_UUID_BYTES]; /* logical volume uuid */ -} __packed; - struct scoutfs_net_roots { struct scoutfs_btree_root fs_root; struct scoutfs_btree_root logs_root; diff --git a/utils/src/ioctl.h b/utils/src/ioctl.h index c953be45..f871d37e 100644 --- a/utils/src/ioctl.h +++ b/utils/src/ioctl.h @@ -394,4 +394,21 @@ struct scoutfs_ioctl_data_wait_err { #define SCOUTFS_IOC_DATA_WAIT_ERR _IOR(SCOUTFS_IOCTL_MAGIC, 11, \ struct scoutfs_ioctl_data_wait_err) + +#define SCOUTFS_IOC_ALLOC_DETAIL _IOR(SCOUTFS_IOCTL_MAGIC, 12, \ + struct scoutfs_ioctl_alloc_detail) + +struct scoutfs_ioctl_alloc_detail { + __u64 entries_ptr; + __u64 entries_nr; +}; + +struct scoutfs_ioctl_alloc_detail_entry { + __u64 id; + __u64 blocks; + __u8 type; + __u8 meta:1, + avail:1; +}; + #endif From 838e2934139018555e26c3f21ca59b9d87b304f3 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Fri, 16 Oct 2020 15:16:19 -0700 Subject: [PATCH 219/235] scoutfs-utils: update compaction item printing We now only use one srch file compaction struct and we store it in PENDING and BUSY key types. Signed-off-by: Zach Brown --- utils/src/format.h | 43 ++++++++++++++++++++++++++++--------------- utils/src/print.c | 35 ++++++++++++++++++++--------------- 2 files changed, 48 insertions(+), 30 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index ee0e0d69..6dafb382 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -374,27 +374,39 @@ struct scoutfs_srch_block { #define SCOUTFS_SRCH_COMPACT_ORDER 3 #define SCOUTFS_SRCH_COMPACT_NR (1 << SCOUTFS_SRCH_COMPACT_ORDER) -struct scoutfs_srch_compact_input { +/* + * A persistent record of a srch file compaction operation in progress. + * + * When compacting log files blk and pos aren't used. When compacting + * sorted files blk is the logical block number and pos is the byte + * offset of the next entry. When deleting files pos is the height of + * the level that we're deleting, and blk is the logical block offset of + * the next parent ref array index to descend through. + */ +struct scoutfs_srch_compact { struct scoutfs_alloc_list_head meta_avail; struct scoutfs_alloc_list_head meta_freed; __le64 id; __u8 nr; __u8 flags; - struct scoutfs_srch_file sfl[SCOUTFS_SRCH_COMPACT_NR]; + struct scoutfs_srch_file out; + struct scoutfs_srch_compact_input { + struct scoutfs_srch_file sfl; + __le64 blk; + __le64 pos; + } in[SCOUTFS_SRCH_COMPACT_NR] __packed; } __packed; -struct scoutfs_srch_compact_result { - struct scoutfs_alloc_list_head meta_avail; - struct scoutfs_alloc_list_head meta_freed; - __le64 id; - __u8 flags; - struct scoutfs_srch_file sfl; -} __packed; - -/* files are insorted logs */ -#define SCOUTFS_SRCH_COMPACT_FLAG_LOG (1 << 0) -/* compaction failed, release inputs */ -#define SCOUTFS_SRCH_COMPACT_FLAG_ERROR (1 << 1) +/* server -> client: combine input log file entries into output file */ +#define SCOUTFS_SRCH_COMPACT_FLAG_LOG (1 << 0) +/* server -> client: combine input sorted file entries into output file */ +#define SCOUTFS_SRCH_COMPACT_FLAG_SORTED (1 << 1) +/* server -> client: delete input files */ +#define SCOUTFS_SRCH_COMPACT_FLAG_DELETE (1 << 2) +/* client -> server: compaction phase (LOG,SORTED,DELETE) done */ +#define SCOUTFS_SRCH_COMPACT_FLAG_DONE (1 << 4) +/* client -> server: compaction failed */ +#define SCOUTFS_SRCH_COMPACT_FLAG_ERROR (1 << 5) /* * XXX I imagine we should rename these now that they've evolved to track @@ -496,7 +508,8 @@ struct scoutfs_bloom_block { /* srch zone, only in server btrees */ #define SCOUTFS_SRCH_LOG_TYPE 1 #define SCOUTFS_SRCH_BLOCKS_TYPE 2 -#define SCOUTFS_SRCH_BUSY_TYPE 3 +#define SCOUTFS_SRCH_PENDING_TYPE 3 +#define SCOUTFS_SRCH_BUSY_TYPE 4 /* free extents in allocator btrees in client and server, by blkno or len */ #define SCOUTFS_FREE_EXTENT_BLKNO_TYPE 1 diff --git a/utils/src/print.c b/utils/src/print.c index cac546dc..92a93049 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -322,22 +322,26 @@ static int print_log_trees_item(struct scoutfs_key *key, void *val, static int print_srch_root_item(struct scoutfs_key *key, void *val, unsigned val_len, void *arg) { - struct scoutfs_srch_file *sfl = val; - struct scoutfs_srch_compact_input *scin = val; + struct scoutfs_srch_compact *sc; + struct scoutfs_srch_file *sfl; int i; printf(" "SK_FMT"\n", SK_ARG(key)); /* only items in leaf blocks have values */ if (val) { - if (key->sk_type == SCOUTFS_SRCH_BUSY_TYPE) { - scin = val; - printf(" compact: nr_in %u in_flags 0x%x\n", - scin->nr, scin->flags); - for (i = 0; i < scin->nr; i++) { - sfl = &scin->sfl[i]; - printf(" [%u] "SRF_FMT"\n", - i, SRF_A(sfl)); + if (key->sk_type == SCOUTFS_SRCH_PENDING_TYPE || + key->sk_type == SCOUTFS_SRCH_BUSY_TYPE) { + sc = val; + printf(" compact %s: nr %u flags 0x%x\n", + key->sk_type == SCOUTFS_SRCH_PENDING_TYPE ? + "pending" : "busy", + sc->nr, sc->flags); + for (i = 0; i < sc->nr; i++) { + printf(" [%u] blk %llu pos %llu sfl "SRF_FMT"\n", + i, le64_to_cpu(sc->in[i].blk), + le64_to_cpu(sc->in[i].pos), + SRF_A(&sc->in[i].sfl)); } } else { sfl = val; @@ -716,15 +720,16 @@ static int print_srch_root_files(struct scoutfs_key *key, void *val, unsigned val_len, void *arg) { struct print_recursion_args *pa = arg; - struct scoutfs_srch_compact_input *scin; + struct scoutfs_srch_compact *sc; struct scoutfs_srch_file *sfl; int ret = 0; int i; - if (key->sk_type == SCOUTFS_SRCH_BUSY_TYPE) { - scin = val; - for (i = 0; i < scin->nr; i++) { - sfl = &scin->sfl[i]; + if (key->sk_type == SCOUTFS_SRCH_PENDING_TYPE || + key->sk_type == SCOUTFS_SRCH_BUSY_TYPE) { + sc = val; + for (i = 0; i < sc->nr; i++) { + sfl = &sc->in[i].sfl; ret = print_srch_block(pa->fd, &sfl->ref, sfl->height - 1); if (ret < 0) From 4ca0b3ff74f63024d5b6d1aeb654422c7d5c1f75 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Fri, 16 Oct 2020 15:21:07 -0700 Subject: [PATCH 220/235] scoutfs-utils: try compacting srch more frequently Signed-off-by: Zach Brown --- utils/src/format.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/src/format.h b/utils/src/format.h index 6dafb382..09092b2a 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -371,7 +371,7 @@ struct scoutfs_srch_block { SCOUTFS_SRCH_ENTRY_MAX_BYTES) #define SCOUTFS_SRCH_LOG_BLOCK_LIMIT (1024 * 1024 / SCOUTFS_BLOCK_LG_SIZE) -#define SCOUTFS_SRCH_COMPACT_ORDER 3 +#define SCOUTFS_SRCH_COMPACT_ORDER 2 #define SCOUTFS_SRCH_COMPACT_NR (1 << SCOUTFS_SRCH_COMPACT_ORDER) /* From b424208555831c4c74c6aae07b96844023ea2559 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Wed, 21 Oct 2020 13:34:27 -0700 Subject: [PATCH 221/235] scoutfs-utils: remove unused packed extents Signed-off-by: Zach Brown --- utils/src/format.h | 31 ------------------------------- 1 file changed, 31 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index 09092b2a..800d9fab 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -515,37 +515,6 @@ struct scoutfs_bloom_block { #define SCOUTFS_FREE_EXTENT_BLKNO_TYPE 1 #define SCOUTFS_FREE_EXTENT_LEN_TYPE 2 -/* - * The extents that map blocks in a fixed-size logical region of a file - * are packed and stored in item values. The packed extents are - * contiguous so the starting logical block is implicit from the length - * of previous extents. Sparse regions are represented by 0 flags and - * blkno. The blkno of a packed extent is encoded as the zigzag (lsb is - * sign bit) difference from the last blkno of the previous extent. - * This guarantees that non-sparse extents must have a blkno delta of at - * least -1/1. High zero byte aren't stored. - */ -struct scoutfs_packed_extent { - __le16 count; -#if defined(__LITTLE_ENDIAN_BITFIELD) - __u8 diff_bytes:4, - flags:3, - final:1; -#elif defined(__BIG_ENDIAN_BITFIELD) - __u8 final:1, - flags:3, - diff_bytes:4; -#else -#error "no {BIG,LITTLE}_ENDIAN_BITFIELD defined?" -#endif - __u8 le_blkno_diff[0]; -} __packed; - -#define SCOUTFS_PACKEXT_BLOCKS (8 * 1024 * 1024 / SCOUTFS_BLOCK_SM_SIZE) -#define SCOUTFS_PACKEXT_BASE_SHIFT (ilog2(SCOUTFS_PACKEXT_BLOCKS)) -#define SCOUTFS_PACKEXT_BASE_MASK (~((__u64)SCOUTFS_PACKEXT_BLOCKS - 1)) -#define SCOUTFS_PACKEXT_MAX_BYTES SCOUTFS_MAX_VAL_SIZE - /* file data extents have start and len in key */ struct scoutfs_data_extent_val { __le64 blkno; From 4bd86d1a005dfa71cf115f3d79e59ec0a062b397 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Fri, 23 Oct 2020 12:12:56 -0700 Subject: [PATCH 222/235] scoutfs-utils: return error for small device The check for a small device didn't return an error code because it was copied from error tests of ret for an error code. It has to generate one, do so. Signed-off-by: Zach Brown --- utils/src/mkfs.c | 1 + 1 file changed, 1 insertion(+) diff --git a/utils/src/mkfs.c b/utils/src/mkfs.c index 569e403f..71903a00 100644 --- a/utils/src/mkfs.c +++ b/utils/src/mkfs.c @@ -178,6 +178,7 @@ static int write_new_fs(char *path, int fd, u8 quorum_count) if (size < limit) { fprintf(stderr, "%llu byte device too small for min %llu byte fs\n", size, limit); + ret = -EINVAL; goto out; } From 669e7f733b6f521f3c395d13c62cd1c6de59a8a4 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Fri, 23 Oct 2020 12:21:37 -0700 Subject: [PATCH 223/235] scoutfs-utils: add -S to limit device size Add an option to mkfs to have it limit the size of the device that's used by mkfs. Signed-off-by: Zach Brown --- utils/man/scoutfs.8 | 5 +++++ utils/src/mkfs.c | 26 +++++++++++++++++++++++--- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/utils/man/scoutfs.8 b/utils/man/scoutfs.8 index 4664f14a..e397f724 100644 --- a/utils/man/scoutfs.8 +++ b/utils/man/scoutfs.8 @@ -184,6 +184,11 @@ elected servers race to fence each other and can have the unlikely outcome of continually racing to fence each other resulting in a persistent loss of service. .TP +.B "-S 4KB_blocks" +Limit the device size used by the filesystem to the given size in units +of 4KB blocks. It must be larger than the mkfs minimum size and fit +within the device. +.TP .B "path" The path to the device whose contents will be unconditionally destroyed. .RE diff --git a/utils/src/mkfs.c b/utils/src/mkfs.c index 71903a00..b03b33d5 100644 --- a/utils/src/mkfs.c +++ b/utils/src/mkfs.c @@ -21,6 +21,7 @@ #include "cmd.h" #include "util.h" #include "format.h" +#include "parse.h" #include "crc.h" #include "rand.h" #include "dev.h" @@ -132,7 +133,7 @@ static int write_alloc_root(struct scoutfs_super_block *super, int fd, * - btree ring blocks with manifest and allocator btree blocks * - segment with root inode items */ -static int write_new_fs(char *path, int fd, u8 quorum_count) +static int write_new_fs(char *path, int fd, u8 quorum_count, u64 dev_blocks) { struct scoutfs_super_block *super; struct scoutfs_inode inode; @@ -173,6 +174,16 @@ static int write_new_fs(char *path, int fd, u8 quorum_count) goto out; } + if (dev_blocks > 0 && size < (dev_blocks << SCOUTFS_BLOCK_SM_SHIFT)) { + fprintf(stderr, "device size limit %llu in 4KB blocks given with -S is greater than device byte size %llu\n", + dev_blocks, size); + ret = -EINVAL; + goto out; + } + + if (dev_blocks > 0 && size > (dev_blocks << SCOUTFS_BLOCK_SM_SHIFT)) + size = dev_blocks << SCOUTFS_BLOCK_SM_SHIFT; + /* arbitrarily require a reasonably large device */ limit = 8ULL * (1024 * 1024 * 1024); if (size < limit) { @@ -369,12 +380,13 @@ static int mkfs_func(int argc, char *argv[]) unsigned long long ull; char *path = argv[1]; u8 quorum_count = 0; + u64 dev_blocks = 0; char *end = NULL; int ret; int fd; int c; - while ((c = getopt_long(argc, argv, "Q:", long_ops, NULL)) != -1) { + while ((c = getopt_long(argc, argv, "Q:S:", long_ops, NULL)) != -1) { switch (c) { case 'Q': ull = strtoull(optarg, &end, 0); @@ -386,6 +398,14 @@ static int mkfs_func(int argc, char *argv[]) } quorum_count = ull; break; + case 'S': + ret = parse_u64(optarg, &dev_blocks); + if (ret < 0) { + printf("scoutfs: invalid device blocks count '%s'\n", + optarg); + return ret; + } + break; case '?': default: return -EINVAL; @@ -412,7 +432,7 @@ static int mkfs_func(int argc, char *argv[]) return ret; } - ret = write_new_fs(path, fd, quorum_count); + ret = write_new_fs(path, fd, quorum_count, dev_blocks); close(fd); return ret; From ea7c41d876b8295c957c47ff7be4a514118e636e Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Mon, 26 Oct 2020 10:51:06 -0700 Subject: [PATCH 224/235] scoutfs-utils: remove free_*_blocks super fields The kernel is no longer storing the total free space in all allocators in super block fields. Signed-off-by: Zach Brown --- utils/src/format.h | 2 -- utils/src/mkfs.c | 4 ---- utils/src/print.c | 6 ++---- 3 files changed, 2 insertions(+), 10 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index 800d9fab..34ebed1a 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -595,11 +595,9 @@ struct scoutfs_super_block { __le64 total_meta_blocks; /* both static and dynamic */ __le64 first_meta_blkno; /* first dynamically allocated */ __le64 last_meta_blkno; - __le64 free_meta_blocks; __le64 total_data_blocks; __le64 first_data_blkno; __le64 last_data_blkno; - __le64 free_data_blocks; __le64 quorum_fenced_term; __le64 quorum_server_term; __le64 unmount_barrier; diff --git a/utils/src/mkfs.c b/utils/src/mkfs.c index b03b33d5..b3225cab 100644 --- a/utils/src/mkfs.c +++ b/utils/src/mkfs.c @@ -321,10 +321,6 @@ static int write_new_fs(char *path, int fd, u8 quorum_count, u64 dev_blocks) } } - /* fill out allocator fields now that we've written our blocks */ - super->free_meta_blocks = cpu_to_le64(last_meta - next_meta + 1); - super->free_data_blocks = cpu_to_le64(last_data - first_data + 1); - /* write the super block */ super->hdr.seq = cpu_to_le64(1); ret = write_block(fd, SCOUTFS_SUPER_BLKNO, SCOUTFS_BLOCK_SM_SHIFT, diff --git a/utils/src/print.c b/utils/src/print.c index 92a93049..c8009cd0 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -883,8 +883,8 @@ 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_trans_seq %llu\n" - " total_meta_blocks %llu first_meta_blkno %llu last_meta_blkno %llu free_meta_blocks %llu\n" - " total_data_blocks %llu first_data_blkno %llu last_data_blkno %llu free_data_blocks %llu\n" + " total_meta_blocks %llu first_meta_blkno %llu last_meta_blkno %llu\n" + " total_data_blocks %llu first_data_blkno %llu last_data_blkno %llu\n" " quorum_fenced_term %llu quorum_server_term %llu unmount_barrier %llu\n" " quorum_count %u server_addr %s\n" " meta_alloc[0]: "ALCROOT_F"\n" @@ -904,11 +904,9 @@ static void print_super_block(struct scoutfs_super_block *super, u64 blkno) le64_to_cpu(super->total_meta_blocks), le64_to_cpu(super->first_meta_blkno), le64_to_cpu(super->last_meta_blkno), - le64_to_cpu(super->free_meta_blocks), le64_to_cpu(super->total_data_blocks), le64_to_cpu(super->first_data_blkno), le64_to_cpu(super->last_data_blkno), - le64_to_cpu(super->free_data_blocks), le64_to_cpu(super->quorum_fenced_term), le64_to_cpu(super->quorum_server_term), le64_to_cpu(super->unmount_barrier), From 6b1dd980f07a4a1169a99779e0e65b3bf148982c Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Tue, 13 Oct 2020 11:50:10 -0700 Subject: [PATCH 225/235] scoutfs-utils: remove btree item owner We no longer have an owner offset trailing btree item values. Signed-off-by: Zach Brown --- utils/src/btree.c | 6 ------ utils/src/format.h | 5 ----- utils/src/print.c | 5 +---- 3 files changed, 1 insertion(+), 15 deletions(-) diff --git a/utils/src/btree.c b/utils/src/btree.c index 5cec36bb..4008af63 100644 --- a/utils/src/btree.c +++ b/utils/src/btree.c @@ -59,8 +59,6 @@ void btree_append_item(struct scoutfs_btree_block *bt, { struct scoutfs_btree_item *item; struct scoutfs_avl_node *prev; - __le16 *own_buf; - __le16 own; void *val_buf; item = &bt->items[le16_to_cpu(bt->nr_items)]; @@ -86,10 +84,6 @@ void btree_append_item(struct scoutfs_btree_block *bt, if (val_len == 0) return; - own_buf = alloc_val(bt, SCOUTFS_BTREE_VAL_OWNER_BYTES); - own = cpu_to_le16((void *)item - (void *)bt); - memcpy(own_buf, &own, sizeof(own)); - val_buf = alloc_val(bt, val_len); item->val_off = cpu_to_le16((void *)val_buf - (void *)bt); item->val_len = cpu_to_le16(val_len); diff --git a/utils/src/format.h b/utils/src/format.h index 34ebed1a..0ccf78ca 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -196,9 +196,6 @@ struct scoutfs_avl_node { /* when we split we want to have multiple items on each side */ #define SCOUTFS_BTREE_MAX_VAL_LEN 896 -/* each value ends with an offset which lets compaction iterate over values */ -#define SCOUTFS_BTREE_VAL_OWNER_BYTES sizeof(__le16) - /* * A 4EB test image measured a worst case height of 17. This is plenty * generous. @@ -232,8 +229,6 @@ struct scoutfs_btree_block { __le16 nr_items; __le16 total_item_bytes; __le16 mid_free_len; - __le16 last_free_off; - __le16 last_free_len; __u8 level; struct scoutfs_btree_item items[0]; /* leaf blocks have a fixed size item offset hash table at the end */ diff --git a/utils/src/print.c b/utils/src/print.c index c8009cd0..077a7db7 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -466,8 +466,7 @@ static int print_btree_block(int fd, struct scoutfs_super_block *super, if (bt->level == level) { printf("%s btree blkno %llu\n" " crc %08x fsid %llx seq %llu blkno %llu \n" - " total_item_bytes %u mid_free_len %u last_free_off %u " - "last_free_len %u\n" + " total_item_bytes %u mid_free_len %u\n" " level %u nr_items %u item_root.node %u\n", which, le64_to_cpu(ref->blkno), le32_to_cpu(bt->hdr.crc), @@ -476,8 +475,6 @@ static int print_btree_block(int fd, struct scoutfs_super_block *super, le64_to_cpu(bt->hdr.blkno), le16_to_cpu(bt->total_item_bytes), le16_to_cpu(bt->mid_free_len), - le16_to_cpu(bt->last_free_off), - le16_to_cpu(bt->last_free_len), bt->level, le16_to_cpu(bt->nr_items), le16_to_cpu(bt->item_root.node)); From 8bd6646d9abe3ff6607033078a6b2964bc8b341d Mon Sep 17 00:00:00 2001 From: Andy Grover Date: Fri, 2 Oct 2020 12:08:07 -0700 Subject: [PATCH 226/235] scoutfs-utils: avoid redeclarations of __[be,le][16,32,64] in sparse.h dev.c includes linux/fs.h which includes linux/types.h, which defines these types, __be16 etc. These are also defined in sparse.h, but I don't think these are needed. Definitions in linux/types.h includes stuff to set attr(bitwise) if __CHECKER__ is defined, so we can remove __sp_biwise. Signed-off-by: Andy Grover --- utils/src/sparse.h | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/utils/src/sparse.h b/utils/src/sparse.h index 567eaf2f..012f27dc 100644 --- a/utils/src/sparse.h +++ b/utils/src/sparse.h @@ -3,12 +3,11 @@ #include #include +#include #ifdef __CHECKER__ # undef __force # define __force __attribute__((force)) -# undef __sp_biwise -# define __sp_biwise __attribute__((bitwise)) /* sparse seems to get confused by some builtins */ extern int __builtin_ia32_rdrand64_step(unsigned long long *); extern unsigned int __builtin_ia32_crc32di(unsigned int, unsigned long long); @@ -18,7 +17,6 @@ extern unsigned int __builtin_ia32_crc32qi(unsigned int, unsigned char); #else # define __force -# define __sp_biwise #endif typedef unsigned char u8; @@ -35,13 +33,6 @@ typedef s32 __s32; typedef u64 __u64; typedef s64 __s64; -typedef u16 __sp_biwise __le16; -typedef u16 __sp_biwise __be16; -typedef u32 __sp_biwise __le32; -typedef u32 __sp_biwise __be32; -typedef u64 __sp_biwise __le64; -typedef u64 __sp_biwise __be64; - static inline u16 ___swab16(u16 x) { return ((x & (u16)0x00ffU) << 8) | From e78ba2b427dba9ecca979cb8cdb06a79d03acae5 Mon Sep 17 00:00:00 2001 From: Andy Grover Date: Tue, 6 Oct 2020 14:40:33 -0700 Subject: [PATCH 227/235] scoutfs-utils: specify types for some long constants This avoids warnings on Centos 7 from gcc 4.8.5. --- utils/src/hash.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/utils/src/hash.h b/utils/src/hash.h index 9b169877..cb50b99c 100644 --- a/utils/src/hash.h +++ b/utils/src/hash.h @@ -26,11 +26,11 @@ static inline u32 fnv1a32(const void *data, unsigned int len) static inline u64 fnv1a64(const void *data, unsigned int len) { - u64 hash = 0xcbf29ce484222325; + u64 hash = 0xcbf29ce484222325ULL; while (len--) { hash ^= *(u8 *)(data++); - hash *= 0x100000001b3; + hash *= 0x100000001b3ULL; } return hash; From 6fea9f90c4b98ba94e0f9e9a6cecc2d4de31ecbc Mon Sep 17 00:00:00 2001 From: Andy Grover Date: Wed, 28 Oct 2020 10:35:40 -0700 Subject: [PATCH 228/235] scoutfs-utils: Sync latest headers with kernel code __packed no longer used. Signed-off-by: Andy Grover --- utils/src/format.h | 127 ++++++++++++++++++++++++++------------------- utils/src/ioctl.h | 4 +- 2 files changed, 76 insertions(+), 55 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index 0ccf78ca..941620b8 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -69,18 +69,15 @@ struct scoutfs_timespec { __le64 sec; __le32 nsec; -} __packed; - -struct scoutfs_betimespec { - __be64 sec; - __be32 nsec; -} __packed; + __u8 __pad[4]; +}; /* XXX ipv6 */ struct scoutfs_inet_addr { __le32 addr; __le16 port; -} __packed; + __u8 __pad[2]; +}; /* * This header is stored at the start of btree blocks and the super @@ -93,7 +90,7 @@ struct scoutfs_block_header { __le64 fsid; __le64 seq; __le64 blkno; -} __packed; +}; /* * scoutfs identifies all file system metadata items by a small key @@ -109,13 +106,14 @@ struct scoutfs_block_header { * increment them, subtract them from each other, etc. */ struct scoutfs_key { - __u8 sk_zone; __le64 _sk_first; - __u8 sk_type; __le64 _sk_second; __le64 _sk_third; __u8 _sk_fourth; -}__packed; + __u8 sk_zone; + __u8 sk_type; + __u8 __pad[5]; +}; /* inode index */ #define skii_major _sk_second @@ -177,21 +175,22 @@ struct scoutfs_radix_block { __le64 seq; __le64 sm_total; __le64 lg_total; - } __packed refs[0]; + } refs[0]; __le64 bits[0]; - } __packed; -} __packed; + }; +}; struct scoutfs_avl_root { __le16 node; -} __packed; +}; struct scoutfs_avl_node { __le16 parent; __le16 left; __le16 right; __u8 height; -} __packed; + __u8 __pad[1]; +}; /* when we split we want to have multiple items on each side */ #define SCOUTFS_BTREE_MAX_VAL_LEN 896 @@ -205,7 +204,7 @@ struct scoutfs_avl_node { struct scoutfs_btree_ref { __le64 blkno; __le64 seq; -} __packed; +}; /* * A height of X means that the first block read will have level X-1 and @@ -214,14 +213,16 @@ struct scoutfs_btree_ref { struct scoutfs_btree_root { struct scoutfs_btree_ref ref; __u8 height; -} __packed; + __u8 __pad[7]; +}; struct scoutfs_btree_item { struct scoutfs_avl_node node; struct scoutfs_key key; __le16 val_off; __le16 val_len; -} __packed; + __u8 __pad[4]; +}; struct scoutfs_btree_block { struct scoutfs_block_header hdr; @@ -230,25 +231,31 @@ struct scoutfs_btree_block { __le16 total_item_bytes; __le16 mid_free_len; __u8 level; + __u8 __pad[7]; struct scoutfs_btree_item items[0]; /* leaf blocks have a fixed size item offset hash table at the end */ -} __packed; +}; + +#define SCOUTFS_BTREE_VALUE_ALIGN 8 /* * Try to aim for a 75% load in a leaf full of items with no value. * We'll almost never see this because most items have values and most * blocks aren't full. */ -#define SCOUTFS_BTREE_LEAF_ITEM_HASH_NR \ +#define SCOUTFS_BTREE_LEAF_ITEM_HASH_NR_UNALIGNED \ ((SCOUTFS_BLOCK_LG_SIZE - sizeof(struct scoutfs_btree_block)) / \ (sizeof(struct scoutfs_btree_item) + (sizeof(__le16))) * 100 / 75) +#define SCOUTFS_BTREE_LEAF_ITEM_HASH_NR \ + (round_up(SCOUTFS_BTREE_LEAF_ITEM_HASH_NR_UNALIGNED, \ + SCOUTFS_BTREE_VALUE_ALIGN)) #define SCOUTFS_BTREE_LEAF_ITEM_HASH_BYTES \ (SCOUTFS_BTREE_LEAF_ITEM_HASH_NR * sizeof(__le16)) struct scoutfs_alloc_list_ref { __le64 blkno; __le64 seq; -}__packed; +}; /* * first_nr tracks the nr of the first block in the list and is used for @@ -259,7 +266,8 @@ struct scoutfs_alloc_list_head { struct scoutfs_alloc_list_ref ref; __le64 total_nr; __le32 first_nr; -}__packed; + __u8 __pad[4]; +}; /* * While the main allocator uses extent items in btree blocks, metadata @@ -278,7 +286,7 @@ struct scoutfs_alloc_list_block { __le32 start; __le32 nr; __le64 blknos[0]; /* naturally aligned for sorting */ -}__packed; +}; #define SCOUTFS_ALLOC_LIST_MAX_BLOCKS \ ((SCOUTFS_BLOCK_LG_SIZE - sizeof(struct scoutfs_alloc_list_block)) / \ @@ -290,7 +298,7 @@ struct scoutfs_alloc_list_block { struct scoutfs_alloc_root { __le64 total_len; struct scoutfs_btree_root root; -}__packed; +}; /* types of allocators, exposed to alloc_detail ioctl */ #define SCOUTFS_ALLOC_OWNER_NONE 0 @@ -300,7 +308,7 @@ struct scoutfs_alloc_root { struct scoutfs_mounted_client_btree_val { __u8 flags; -} __packed; +}; #define SCOUTFS_MOUNTED_CLIENT_VOTER (1 << 0) @@ -316,28 +324,29 @@ struct scoutfs_srch_entry { __le64 hash; __le64 ino; __le64 id; -} __packed; +}; #define SCOUTFS_SRCH_ENTRY_MAX_BYTES (2 + (sizeof(__u64) * 3)) struct scoutfs_srch_ref { __le64 blkno; __le64 seq; -} __packed; +}; struct scoutfs_srch_file { struct scoutfs_srch_entry first; struct scoutfs_srch_entry last; + struct scoutfs_srch_ref ref; __le64 blocks; __le64 entries; - struct scoutfs_srch_ref ref; __u8 height; -} __packed; + __u8 __pad[7]; +}; struct scoutfs_srch_parent { struct scoutfs_block_header hdr; struct scoutfs_srch_ref refs[0]; -} __packed; +}; #define SCOUTFS_SRCH_PARENT_REFS \ ((SCOUTFS_BLOCK_LG_SIZE - \ @@ -352,7 +361,7 @@ struct scoutfs_srch_block { __le32 entry_nr; __le32 entry_bytes; __u8 entries[0]; -} __packed; +}; /* * Decoding loads final small deltas with full __u64 loads. Rather than @@ -384,13 +393,14 @@ struct scoutfs_srch_compact { __le64 id; __u8 nr; __u8 flags; + __u8 __pad[6]; struct scoutfs_srch_file out; struct scoutfs_srch_compact_input { struct scoutfs_srch_file sfl; __le64 blk; __le64 pos; - } in[SCOUTFS_SRCH_COMPACT_NR] __packed; -} __packed; + } in[SCOUTFS_SRCH_COMPACT_NR]; +}; /* server -> client: combine input log file entries into output file */ #define SCOUTFS_SRCH_COMPACT_FLAG_LOG (1 << 0) @@ -418,7 +428,7 @@ struct scoutfs_log_trees { struct scoutfs_srch_file srch_file; __le64 rid; __le64 nr; -} __packed; +}; struct scoutfs_log_trees_val { struct scoutfs_alloc_list_head meta_avail; @@ -428,13 +438,14 @@ struct scoutfs_log_trees_val { struct scoutfs_alloc_root data_avail; struct scoutfs_alloc_root data_freed; struct scoutfs_srch_file srch_file; -} __packed; +}; struct scoutfs_log_item_value { __le64 vers; __u8 flags; + __u8 __pad[7]; __u8 data[0]; -} __packed; +}; /* * FS items are limited by the max btree value length with the log item @@ -449,7 +460,7 @@ struct scoutfs_bloom_block { struct scoutfs_block_header hdr; __le64 total_set; __le64 bits[0]; -} __packed; +}; /* * Item log trees are accompanied by a block of bits that make up a @@ -514,7 +525,8 @@ struct scoutfs_bloom_block { struct scoutfs_data_extent_val { __le64 blkno; __u8 flags; -} __packed; + __u8 __pad[7]; +}; #define SEF_OFFLINE (1 << 0) #define SEF_UNWRITTEN (1 << 1) @@ -526,10 +538,11 @@ struct scoutfs_data_extent_val { * part item and overflow into the values of the rest of the part items. */ struct scoutfs_xattr { - __u8 name_len; __le16 val_len; + __u8 name_len; + __u8 __pad[5]; __u8 name[0]; -} __packed; +}; /* XXX does this exist upstream somewhere? */ @@ -569,12 +582,13 @@ struct scoutfs_quorum_block { __le64 vote_for_rid; __le32 crc; __u8 log_nr; + __u8 __pad[3]; struct scoutfs_quorum_log { __le64 term; __le64 rid; struct scoutfs_inet_addr addr; - } __packed log[0]; -} __packed; + } log[0]; +}; #define SCOUTFS_QUORUM_LOG_MAX \ ((SCOUTFS_BLOCK_SM_SIZE - sizeof(struct scoutfs_quorum_block)) / \ @@ -597,6 +611,7 @@ struct scoutfs_super_block { __le64 quorum_server_term; __le64 unmount_barrier; __u8 quorum_count; + __u8 __pad[7]; struct scoutfs_inet_addr server_addr; struct scoutfs_alloc_root meta_alloc[2]; struct scoutfs_alloc_root data_alloc; @@ -608,7 +623,7 @@ struct scoutfs_super_block { struct scoutfs_btree_root trans_seqs; struct scoutfs_btree_root mounted_clients; struct scoutfs_btree_root srch_root; -} __packed; +}; #define SCOUTFS_ROOT_INO 1 @@ -657,7 +672,7 @@ struct scoutfs_inode { struct scoutfs_timespec atime; struct scoutfs_timespec ctime; struct scoutfs_timespec mtime; -} __packed; +}; #define SCOUTFS_INO_FLAG_TRUNCATE 0x1 @@ -679,8 +694,9 @@ struct scoutfs_dirent { __le64 hash; __le64 pos; __u8 type; + __u8 __pad[7]; __u8 name[0]; -} __packed; +}; #define SCOUTFS_NAME_LEN 255 @@ -748,7 +764,7 @@ struct scoutfs_net_greeting { __le64 unmount_barrier; __le64 rid; __le64 flags; -} __packed; +}; #define SCOUTFS_NET_GREETING_FLAG_FAREWELL (1 << 0) #define SCOUTFS_NET_GREETING_FLAG_VOTER (1 << 1) @@ -783,8 +799,9 @@ struct scoutfs_net_header { __u8 cmd; __u8 flags; __u8 error; + __u8 __pad[3]; __u8 data[0]; -} __packed; +}; #define SCOUTFS_NET_FLAG_RESPONSE (1 << 0) #define SCOUTFS_NET_FLAGS_UNKNOWN (U8_MAX << 1) @@ -835,30 +852,32 @@ enum { struct scoutfs_net_inode_alloc { __le64 ino; __le64 nr; -} __packed; +}; struct scoutfs_net_roots { struct scoutfs_btree_root fs_root; struct scoutfs_btree_root logs_root; struct scoutfs_btree_root srch_root; -} __packed; +}; struct scoutfs_net_lock { struct scoutfs_key key; __le64 write_version; __u8 old_mode; __u8 new_mode; -} __packed; + __u8 __pad[6]; +}; struct scoutfs_net_lock_grant_response { struct scoutfs_net_lock nl; struct scoutfs_net_roots roots; -} __packed; +}; struct scoutfs_net_lock_recover { __le16 nr; + __u8 __pad[6]; struct scoutfs_net_lock locks[0]; -} __packed; +}; #define SCOUTFS_NET_LOCK_MAX_RECOVER_NR \ ((SCOUTFS_NET_MAX_DATA_LEN - sizeof(struct scoutfs_net_lock_recover)) /\ @@ -901,7 +920,7 @@ enum { struct scoutfs_fid { __le64 ino; __le64 parent_ino; -} __packed; +}; #define FILEID_SCOUTFS 0x81 #define FILEID_SCOUTFS_WITH_PARENT 0x82 diff --git a/utils/src/ioctl.h b/utils/src/ioctl.h index f871d37e..8dc9d93c 100644 --- a/utils/src/ioctl.h +++ b/utils/src/ioctl.h @@ -371,7 +371,7 @@ struct scoutfs_ioctl_statfs_more { __u64 committed_seq; __u64 total_meta_blocks; __u64 total_data_blocks; -} __packed; +}; #define SCOUTFS_IOC_STATFS_MORE _IOR(SCOUTFS_IOCTL_MAGIC, 10, \ struct scoutfs_ioctl_statfs_more) @@ -409,6 +409,8 @@ struct scoutfs_ioctl_alloc_detail_entry { __u8 type; __u8 meta:1, avail:1; + __u8 __bit_pad:6; + __u8 __pad[6]; }; #endif From 57011826654f427acbf15a6cd35cee88679fc92c Mon Sep 17 00:00:00 2001 From: Andy Grover Date: Wed, 28 Oct 2020 10:46:18 -0700 Subject: [PATCH 229/235] scoutfs-utils: Enable -Wpadded The compiler will complain if it sees any padding. Fix a spot in print.c for this. Signed-off-by: Andy Grover --- utils/Makefile | 1 + utils/src/print.c | 1 + 2 files changed, 2 insertions(+) diff --git a/utils/Makefile b/utils/Makefile index 978d8de1..81386c83 100644 --- a/utils/Makefile +++ b/utils/Makefile @@ -2,6 +2,7 @@ SCOUTFS_FORMAT_HASH := \ $(shell cat src/format.h src/ioctl.h | md5sum | cut -b1-16) CFLAGS := -Wall -O2 -Werror -D_FILE_OFFSET_BITS=64 -g -msse4.2 \ + -Wpadded \ -fno-strict-aliasing \ -DSCOUTFS_FORMAT_HASH=0x$(SCOUTFS_FORMAT_HASH)LLU diff --git a/utils/src/print.c b/utils/src/print.c index 077a7db7..2f046861 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -671,6 +671,7 @@ out: struct print_recursion_args { struct scoutfs_super_block *super; int fd; + u8 __pad[4]; }; /* same as fs item but with a small header in the value */ From 42bf0980b6b94cd39a8b23b8b22473399cc43463 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Thu, 29 Oct 2020 11:32:28 -0700 Subject: [PATCH 230/235] scoutfs-utils: remove scoutfs_log_trees_val We're just using the one log_trees struct for both network messages and persistent btree item values. Signed-off-by: Zach Brown --- utils/src/format.h | 10 ---------- utils/src/print.c | 48 +++++++++++++++++++++++++--------------------- 2 files changed, 26 insertions(+), 32 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index 941620b8..d418828b 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -430,16 +430,6 @@ struct scoutfs_log_trees { __le64 nr; }; -struct scoutfs_log_trees_val { - struct scoutfs_alloc_list_head meta_avail; - struct scoutfs_alloc_list_head meta_freed; - struct scoutfs_btree_root item_root; - struct scoutfs_btree_ref bloom_ref; - struct scoutfs_alloc_root data_avail; - struct scoutfs_alloc_root data_freed; - struct scoutfs_srch_file srch_file; -}; - struct scoutfs_log_item_value { __le64 vers; __u8 flags; diff --git a/utils/src/print.c b/utils/src/print.c index 2f046861..08ad1e4c 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -290,7 +290,7 @@ static int print_logs_item(struct scoutfs_key *key, void *val, static int print_log_trees_item(struct scoutfs_key *key, void *val, unsigned val_len, void *arg) { - struct scoutfs_log_trees_val *ltv = val; + struct scoutfs_log_trees *lt = val; printf(" rid %llu nr %llu\n", le64_to_cpu(key->sklt_rid), le64_to_cpu(key->sklt_nr)); @@ -303,17 +303,21 @@ static int print_log_trees_item(struct scoutfs_key *key, void *val, " bloom_ref: blkno %llu seq %llu\n" " data_avail: "ALCROOT_F"\n" " data_freed: "ALCROOT_F"\n" - " srch_file: "SRF_FMT"\n", - AL_HEAD_A(<v->meta_avail), - AL_HEAD_A(<v->meta_freed), - ltv->item_root.height, - le64_to_cpu(ltv->item_root.ref.blkno), - le64_to_cpu(ltv->item_root.ref.seq), - le64_to_cpu(ltv->bloom_ref.blkno), - le64_to_cpu(ltv->bloom_ref.seq), - ALCROOT_A(<v->data_avail), - ALCROOT_A(<v->data_freed), - SRF_A(<v->srch_file)); + " srch_file: "SRF_FMT"\n" + " rid: %016llx\n" + " nr: %llu\n", + AL_HEAD_A(<->meta_avail), + AL_HEAD_A(<->meta_freed), + lt->item_root.height, + le64_to_cpu(lt->item_root.ref.blkno), + le64_to_cpu(lt->item_root.ref.seq), + le64_to_cpu(lt->bloom_ref.blkno), + le64_to_cpu(lt->bloom_ref.seq), + ALCROOT_A(<->data_avail), + ALCROOT_A(<->data_freed), + SRF_A(<->srch_file), + le64_to_cpu(lt->rid), + le64_to_cpu(lt->nr)); } return 0; @@ -678,35 +682,35 @@ struct print_recursion_args { static int print_log_trees_roots(struct scoutfs_key *key, void *val, unsigned val_len, void *arg) { - struct scoutfs_log_trees_val *ltv = val; + struct scoutfs_log_trees *lt = val; struct print_recursion_args *pa = arg; int ret = 0; int err; /* XXX doesn't print the bloom block */ - err = print_alloc_list_block(pa->fd, "ltv_meta_avail", - <v->meta_avail.ref); + err = print_alloc_list_block(pa->fd, "lt_meta_avail", + <->meta_avail.ref); if (err && !ret) ret = err; - err = print_alloc_list_block(pa->fd, "ltv_meta_freed", - <v->meta_freed.ref); + err = print_alloc_list_block(pa->fd, "lt_meta_freed", + <->meta_freed.ref); if (err && !ret) ret = err; err = print_btree(pa->fd, pa->super, "data_avail", - <v->data_avail.root, print_alloc_item, NULL); + <->data_avail.root, print_alloc_item, NULL); if (err && !ret) ret = err; err = print_btree(pa->fd, pa->super, "data_freed", - <v->data_freed.root, print_alloc_item, NULL); + <->data_freed.root, print_alloc_item, NULL); if (err && !ret) ret = err; - err = print_srch_block(pa->fd, <v->srch_file.ref, - ltv->srch_file.height - 1); + err = print_srch_block(pa->fd, <->srch_file.ref, + lt->srch_file.height - 1); if (err && !ret) ret = err; - err = print_btree(pa->fd, pa->super, "", <v->item_root, + err = print_btree(pa->fd, pa->super, "", <->item_root, print_logs_item, NULL); if (err && !ret) ret = err; From 66c63311313a4d8e4e1fc0fc12214610391d9731 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Thu, 29 Oct 2020 11:33:28 -0700 Subject: [PATCH 231/235] scoutfs-utils: add max item vers to log trees Add a field to the log_trees struct which records the greatest item version seen in items in the tree. Signed-off-by: Zach Brown --- utils/src/format.h | 1 + utils/src/print.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/utils/src/format.h b/utils/src/format.h index d418828b..7be325bc 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -426,6 +426,7 @@ struct scoutfs_log_trees { struct scoutfs_alloc_root data_avail; struct scoutfs_alloc_root data_freed; struct scoutfs_srch_file srch_file; + __le64 max_item_vers; __le64 rid; __le64 nr; }; diff --git a/utils/src/print.c b/utils/src/print.c index 08ad1e4c..280e8456 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -304,6 +304,7 @@ static int print_log_trees_item(struct scoutfs_key *key, void *val, " data_avail: "ALCROOT_F"\n" " data_freed: "ALCROOT_F"\n" " srch_file: "SRF_FMT"\n" + " max_item_vers: %llu\n" " rid: %016llx\n" " nr: %llu\n", AL_HEAD_A(<->meta_avail), @@ -316,6 +317,7 @@ static int print_log_trees_item(struct scoutfs_key *key, void *val, ALCROOT_A(<->data_avail), ALCROOT_A(<->data_freed), SRF_A(<->srch_file), + le64_to_cpu(lt->max_item_vers), le64_to_cpu(lt->rid), le64_to_cpu(lt->nr)); } From f46ab548a4f52b394181bce2fe6c733ea09b0d76 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Tue, 10 Nov 2020 13:39:21 -0800 Subject: [PATCH 232/235] scoutfs-utils: format df in two rows It was too tricky to pick out the difference between metadata and data usage in the previous format. This makes it much more clear which values are for either metadata or data. Signed-off-by: Zach Brown --- utils/src/df.c | 68 +++++++++++++++++++++++++++++--------------------- 1 file changed, 40 insertions(+), 28 deletions(-) diff --git a/utils/src/df.c b/utils/src/df.c index 86c1ae0f..6432b260 100644 --- a/utils/src/df.c +++ b/utils/src/df.c @@ -16,15 +16,16 @@ #include "ioctl.h" #include "cmd.h" -#define COLS 8 +#define ROWS 3 +#define COLS 7 +#define CHARS 20 static int df_cmd(int argc, char **argv) { struct scoutfs_ioctl_alloc_detail ad; struct scoutfs_ioctl_alloc_detail_entry *ade = NULL; struct scoutfs_ioctl_statfs_more sfm; - char *title[COLS]; - u64 fields[COLS]; + static char cells[ROWS][COLS][CHARS]; int wid[COLS]; u64 nr = 4096 / sizeof(*ade); u64 meta_free = 0; @@ -32,6 +33,8 @@ static int df_cmd(int argc, char **argv) int ret; int fd; int i; + int r; + int c; if (argc != 2) { fprintf(stderr, "must specify path\n"); @@ -84,34 +87,43 @@ static int df_cmd(int argc, char **argv) data_free += ade[i].blocks; } - title[0] = "64K-Meta"; - title[1] = "Used"; - title[2] = "Avail"; - title[3] = "Use%"; - title[4] = "4K-Data"; - title[5] = "Used"; - title[6] = "Avail"; - title[7] = "Use%"; + snprintf(cells[0][1], CHARS, "Type"); + snprintf(cells[0][2], CHARS, "Size"); + snprintf(cells[0][3], CHARS, "Total"); + snprintf(cells[0][4], CHARS, "Used"); + snprintf(cells[0][5], CHARS, "Free"); + snprintf(cells[0][6], CHARS, "Use%%"); - fields[0] = sfm.total_meta_blocks; - fields[1] = sfm.total_meta_blocks - meta_free; - fields[2] = meta_free; - fields[3] = fields[1] * 100 / fields[0]; - fields[4] = sfm.total_data_blocks; - fields[5] = sfm.total_data_blocks - data_free; - fields[6] = data_free; - fields[7] = fields[5] * 100 / fields[4]; + snprintf(cells[1][1], CHARS, "MetaData"); + snprintf(cells[1][2], CHARS, "64KB"); + snprintf(cells[1][3], CHARS, "%llu", sfm.total_meta_blocks); + snprintf(cells[1][4], CHARS, "%llu", sfm.total_meta_blocks - meta_free); + snprintf(cells[1][5], CHARS, "%llu", meta_free); + snprintf(cells[1][6], CHARS, "%llu", + ((sfm.total_meta_blocks - meta_free) * 100) / + sfm.total_meta_blocks); - for (i = 0; i < array_size(fields); i++) - wid[i] = max(snprintf(NULL, 0, "%s", title[i]), - snprintf(NULL, 0, "%llu", fields[i])); + snprintf(cells[2][1], CHARS, "Data"); + snprintf(cells[2][2], CHARS, "4KB"); + snprintf(cells[2][3], CHARS, "%llu", sfm.total_data_blocks); + snprintf(cells[2][4], CHARS, "%llu", sfm.total_data_blocks - data_free); + snprintf(cells[2][5], CHARS, "%llu", data_free); + snprintf(cells[2][6], CHARS, "%llu", + ((sfm.total_data_blocks - data_free) * 100) / + sfm.total_data_blocks); - for (i = 0; i < array_size(fields); i++) - printf("%*s ", wid[i], title[i]); - printf("\n"); - for (i = 0; i < array_size(fields); i++) - wid[i] = printf("%*llu ", wid[i], fields[i]); - printf("\n"); + for (r = 0; r < ROWS; r++) { + for (c = 0; c < COLS; c++) { + wid[c] = max(wid[c], strlen(cells[r][c])); + } + } + + for (r = 0; r < ROWS; r++) { + for (c = 0; c < COLS; c++) { + printf("%*s ", wid[c], cells[r][c]); + } + printf("\n"); + } ret = 0; out: From 8f72d166096236b775d3c2413f67aa8c41237812 Mon Sep 17 00:00:00 2001 From: Andy Grover Date: Tue, 20 Oct 2020 14:21:07 -0700 Subject: [PATCH 233/235] scoutfs-utils: Use separate block devices for metadata and data mkfs: Take two block devices as arguments. Write everything to metadata dev, and the superblock to the data dev. UUIDs match. Differentiate by checking a bit in a new "flags" field in the superblock. Refactor device_size() a little. Convert spaces to tabs. Move code to pretty-print sizes to dev.c so we can use it in error messages there, as well as in mkfs.c. print: Include flags in output. Add -D and -M options for setting max dev sizes Allow sizes to be specified using units like "K", "G" etc. Note: -D option replaces -S option, and uses above units rather than the number of 4k data blocks. Update man pages for cmdline changes. Signed-off-by: Andy Grover --- utils/man/scoutfs.5 | 8 ++- utils/man/scoutfs.8 | 42 +++++++---- utils/src/dev.c | 107 ++++++++++++++++++++------- utils/src/dev.h | 12 +++- utils/src/format.h | 9 +++ utils/src/mkfs.c | 171 ++++++++++++++++++++++---------------------- utils/src/parse.c | 64 +++++++++++++++++ utils/src/parse.h | 1 + utils/src/print.c | 1 + 9 files changed, 288 insertions(+), 127 deletions(-) diff --git a/utils/man/scoutfs.5 b/utils/man/scoutfs.5 index 9d45a3da..b85571cb 100644 --- a/utils/man/scoutfs.5 +++ b/utils/man/scoutfs.5 @@ -2,7 +2,7 @@ .SH NAME scoutfs \- overview and mount options for the scoutfs filesystem .SH DESCRIPTION -A scoutfs filesystem is stored on a block device. Multiple mounts of +A scoutfs filesystem is stored on two block devices. Multiple mounts of the filesystem are supported between hosts that share access to the block device. A new filesystem is created with the .B mkfs @@ -15,6 +15,12 @@ general mount options described in the .BR mount (8) manual page. .TP +.B metadev_path= +The metadev_path option specifies the path to the block device that +contains the filesystem's metadata. +.sp +This option is required. +.TP .B server_addr= The server_addr option indicates that this mount will participate in quorum election to try and run a server for all the mounts of its diff --git a/utils/man/scoutfs.8 b/utils/man/scoutfs.8 index e397f724..1e8721a2 100644 --- a/utils/man/scoutfs.8 +++ b/utils/man/scoutfs.8 @@ -149,23 +149,24 @@ user must have read permission to the inode. .PD .TP -.BI "mkfs <\-Q nr> " +.BI "mkfs <\-Q nr> [-M meta_size] [-D data_size]" .sp -Initialize a new empty filesystem in the target device by writing empty -structures and a new superblock. +Initialize a new empty filesystem in the target devices by writing empty +structures and a new superblock. Since ScoutFS uses separate block +devices for its metadata and data storage, both must be given. .sp This .B unconditionally destroys -the contents of the device, regardless of what it contains or who may be -using it. It simply writes new data structures into known offsets. -.B Be very careful that the device does not contain data and is not actively in use. +the contents of the devices, regardless of what they contain or who may be +using them. It simply writes new data structures into known offsets. +.B Be very careful that the devices do not contain data and are not actively in use. .RS 1.0i .PD 0 .TP .sp .B "-Q nr" Specify the number of mounts needed to reach quorum and elect a mount -to start the server. Mounts of the device will hang until this many +to start the server. Mounts of the filesystem will hang until this many mounts are operational and can elect a server amongst themselves. .sp Mounts with the @@ -184,13 +185,24 @@ elected servers race to fence each other and can have the unlikely outcome of continually racing to fence each other resulting in a persistent loss of service. .TP -.B "-S 4KB_blocks" -Limit the device size used by the filesystem to the given size in units -of 4KB blocks. It must be larger than the mkfs minimum size and fit -within the device. +.B "meta_dev_path" +The path to the device to be used for ScoutFS metadata. If possible, +use a faster block device for the metadata device. Its contents will be +unconditionally destroyed. .TP -.B "path" -The path to the device whose contents will be unconditionally destroyed. +.B "data_dev_path" +The path to the device to be used for ScoutFS file data. If possible, +use a larger block device for the data device. Its contents will be +unconditionally destroyed. +.TP +.B "-M meta_size" +Limit the space used by the filesystem on the metadata device to the +given size, rather than using the entire block device. Size is given as +an integer followed by a units digit: "K", "M", "G", "T", "P", to denote +kibibytes, mebibytes, etc. +.TP +.B "-D data_size" +Same as previous, but for limiting the size of the data device. .RE .PD @@ -206,11 +218,11 @@ output. .TP .sp .B "path" -The path to the device that contains the filesystem whose metadata will +The path to the metadata device for filesystem whose metadata will be printed. The command reads from the buffer cache of the device which may not reflect the current blocks in the filesystem that may have been written through another host or device. The local device's cache can be -manually flused before printing, perhaps with the +manually flushed before printing, perhaps with the .B \--flushbufs command in the .BR blockdev (8) diff --git a/utils/src/dev.c b/utils/src/dev.c index f1fdaada..303e6438 100644 --- a/utils/src/dev.c +++ b/utils/src/dev.c @@ -10,33 +10,92 @@ #include "sparse.h" #include "dev.h" -int device_size(char *path, int fd, u64 *size) +int device_size(char *path, int fd, + u64 min_size, u64 max_size, + char *use_type, u64 *size_ret) { - struct stat st; - int ret; + struct stat st; + u64 size; + char *target_type; + int ret; - if (fstat(fd, &st)) { - ret = -errno; - fprintf(stderr, "failed to stat '%s': %s (%d)\n", - path, strerror(errno), errno); - return ret; - } + if (fstat(fd, &st)) { + ret = -errno; + fprintf(stderr, "failed to stat '%s': %s (%d)\n", + path, strerror(errno), errno); + return ret; + } - if (S_ISREG(st.st_mode)) { - *size = st.st_size; - } else if (S_ISBLK(st.st_mode)) { - if (ioctl(fd, BLKGETSIZE64, size)) { - ret = -errno; - fprintf(stderr, "BLKGETSIZE64 failed '%s': %s (%d)\n", - path, strerror(errno), errno); - return ret; - } - } else { - fprintf(stderr, "path isn't regular or device file '%s'\n", - path); - return -EINVAL; - } + if (S_ISREG(st.st_mode)) { + size = st.st_size; + target_type = "file"; + } else if (S_ISBLK(st.st_mode)) { + if (ioctl(fd, BLKGETSIZE64, &size)) { + ret = -errno; + fprintf(stderr, "BLKGETSIZE64 failed '%s': %s (%d)\n", + path, strerror(errno), errno); + return ret; + } + target_type = "device"; + } else { + fprintf(stderr, "path isn't regular or device file '%s'\n", + path); + return -EINVAL; + } - return 0; + if (max_size) { + if (size > max_size) { + printf("Limiting use of "BASE_SIZE_FMT + " %s device to "BASE_SIZE_FMT"\n", + BASE_SIZE_ARGS(size), use_type, + BASE_SIZE_ARGS(max_size)); + size = max_size; + } else if (size < max_size) { + printf("Device size limit of "BASE_SIZE_FMT + " for %s device" + " is greater than "BASE_SIZE_FMT + " available, ignored.\n", + BASE_SIZE_ARGS(max_size), use_type, + BASE_SIZE_ARGS(size)); + } + } + + if (size < min_size) { + fprintf(stderr, + BASE_SIZE_FMT" %s too small for min " + BASE_SIZE_FMT" %s device\n", + BASE_SIZE_ARGS(size), target_type, + BASE_SIZE_ARGS(min_size), use_type); + return -EINVAL; + } + + *size_ret = size; + + return 0; } +float size_flt(u64 nr, unsigned size) +{ + float x = (float)nr * (float)size; + + while (x >= 1024) + x /= 1024; + + return x; +} + +char *size_str(u64 nr, unsigned size) +{ + float x = (float)nr * (float)size; + static char *suffixes[] = { + "B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB", + }; + int i = 0; + + while (x >= 1024) { + x /= 1024; + i++; + } + + return suffixes[i]; +} diff --git a/utils/src/dev.h b/utils/src/dev.h index 1fcf92fa..83dfffb4 100644 --- a/utils/src/dev.h +++ b/utils/src/dev.h @@ -1,6 +1,16 @@ #ifndef _DEV_H_ #define _DEV_H_ -int device_size(char *path, int fd, u64 *size); +#define BASE_SIZE_FMT "%.2f %s" +#define BASE_SIZE_ARGS(sz) size_flt(sz, 1), size_str(sz, 1) + +#define SIZE_FMT "%llu (%.2f %s)" +#define SIZE_ARGS(nr, sz) (nr), size_flt(nr, sz), size_str(nr, sz) + +int device_size(char *path, int fd, + u64 min_size, u64 max_size, + char *use_type, u64 *size_ret); +float size_flt(u64 nr, unsigned size); +char *size_str(u64 nr, unsigned size); #endif diff --git a/utils/src/format.h b/utils/src/format.h index 7be325bc..b38a55a8 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -61,6 +61,12 @@ #define SCOUTFS_QUORUM_BLKNO ((256ULL * 1024) >> SCOUTFS_BLOCK_SM_SHIFT) #define SCOUTFS_QUORUM_BLOCKS ((256ULL * 1024) >> SCOUTFS_BLOCK_SM_SHIFT) +/* + * Start data on the data device aligned as well. + */ +#define SCOUTFS_DATA_DEV_START_BLKNO ((256ULL * 1024) >> SCOUTFS_BLOCK_SM_SHIFT) + + #define SCOUTFS_UNIQUE_NAME_MAX_BYTES 64 /* includes null */ /* @@ -585,10 +591,13 @@ struct scoutfs_quorum_block { ((SCOUTFS_BLOCK_SM_SIZE - sizeof(struct scoutfs_quorum_block)) / \ sizeof(struct scoutfs_quorum_log)) +#define SCOUTFS_FLAG_IS_META_BDEV 0x01 + struct scoutfs_super_block { struct scoutfs_block_header hdr; __le64 id; __le64 format_hash; + __le64 flags; __u8 uuid[SCOUTFS_UUID_BYTES]; __le64 next_ino; __le64 next_trans_seq; diff --git a/utils/src/mkfs.c b/utils/src/mkfs.c index b3225cab..8d5e4dff 100644 --- a/utils/src/mkfs.c +++ b/utils/src/mkfs.c @@ -16,6 +16,7 @@ #include #include #include +#include #include "sparse.h" #include "cmd.h" @@ -62,35 +63,6 @@ static int write_block(int fd, u64 blkno, int shift, return write_raw_block(fd, blkno, shift, hdr); } -static float size_flt(u64 nr, unsigned size) -{ - float x = (float)nr * (float)size; - - while (x >= 1024) - x /= 1024; - - return x; -} - -static char *size_str(u64 nr, unsigned size) -{ - float x = (float)nr * (float)size; - static char *suffixes[] = { - "B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB", - }; - int i = 0; - - while (x >= 1024) { - x /= 1024; - i++; - } - - return suffixes[i]; -} - -#define SIZE_FMT "%llu (%.2f %s)" -#define SIZE_ARGS(nr, sz) (nr), size_flt(nr, sz), size_str(nr, sz) - /* * Write the single btree block that contains the blkno and len indexed * items to store the given extent, and update the root to point to it. @@ -132,8 +104,14 @@ static int write_alloc_root(struct scoutfs_super_block *super, int fd, * - super blocks * - btree ring blocks with manifest and allocator btree blocks * - segment with root inode items + * + * Superblock is written to both metadata and data devices, everything else is + * written only to the metadata device. */ -static int write_new_fs(char *path, int fd, u8 quorum_count, u64 dev_blocks) +static int write_new_fs(char *meta_path, char *data_path, + int meta_fd, int data_fd, + u8 quorum_count, + u64 max_meta_size, u64 max_data_size) { struct scoutfs_super_block *super; struct scoutfs_inode inode; @@ -144,8 +122,8 @@ static int write_new_fs(char *path, int fd, u8 quorum_count, u64 dev_blocks) char uuid_str[37]; void *zeros; u64 blkno; - u64 limit; - u64 size; + u64 meta_size; + u64 data_size; u64 next_meta; u64 last_meta; u64 first_data; @@ -167,40 +145,24 @@ static int write_new_fs(char *path, int fd, u8 quorum_count, u64 dev_blocks) goto out; } - ret = device_size(path, fd, &size); - if (ret) { - fprintf(stderr, "failed to stat '%s': %s (%d)\n", - path, strerror(errno), errno); + ret = device_size(meta_path, meta_fd, 2ULL * (1024 * 1024 * 1024), + max_meta_size, "meta", &meta_size); + if (ret) goto out; - } - if (dev_blocks > 0 && size < (dev_blocks << SCOUTFS_BLOCK_SM_SHIFT)) { - fprintf(stderr, "device size limit %llu in 4KB blocks given with -S is greater than device byte size %llu\n", - dev_blocks, size); - ret = -EINVAL; + ret = device_size(data_path, data_fd, 8ULL * (1024 * 1024 * 1024), + max_data_size, "data", &data_size); + if (ret) goto out; - } - - if (dev_blocks > 0 && size > (dev_blocks << SCOUTFS_BLOCK_SM_SHIFT)) - size = dev_blocks << SCOUTFS_BLOCK_SM_SHIFT; - - /* arbitrarily require a reasonably large device */ - limit = 8ULL * (1024 * 1024 * 1024); - if (size < limit) { - fprintf(stderr, "%llu byte device too small for min %llu byte fs\n", - size, limit); - ret = -EINVAL; - goto out; - } /* metadata blocks start after the quorum blocks */ next_meta = (SCOUTFS_QUORUM_BLKNO + SCOUTFS_QUORUM_BLOCKS) >> SCOUTFS_BLOCK_SM_LG_SHIFT; - /* use about 1/5 of the device for metadata blocks */ - last_meta = next_meta + ((size / 5) >> SCOUTFS_BLOCK_LG_SHIFT); - /* The rest of the device is data blocks */ - first_data = (last_meta + 1) << SCOUTFS_BLOCK_SM_LG_SHIFT; - last_data = (size >> SCOUTFS_BLOCK_SM_SHIFT) - 1; + /* rest of meta dev is available for metadata blocks */ + last_meta = (meta_size >> SCOUTFS_BLOCK_LG_SHIFT) - 1; + /* Data blocks go on the data dev */ + first_data = SCOUTFS_DATA_DEV_START_BLKNO; + last_data = (data_size >> SCOUTFS_BLOCK_SM_SHIFT) - 1; /* partially initialize the super so we can use it to init others */ memset(super, 0, SCOUTFS_BLOCK_SM_SIZE); @@ -249,7 +211,7 @@ static int write_new_fs(char *path, int fd, u8 quorum_count, u64 dev_blocks) bt->hdr.crc = cpu_to_le32(crc_block(&bt->hdr, SCOUTFS_BLOCK_LG_SIZE)); - ret = write_raw_block(fd, blkno, SCOUTFS_BLOCK_LG_SHIFT, bt); + ret = write_raw_block(meta_fd, blkno, SCOUTFS_BLOCK_LG_SHIFT, bt); if (ret) goto out; @@ -276,13 +238,13 @@ static int write_new_fs(char *path, int fd, u8 quorum_count, u64 dev_blocks) super->server_meta_avail[0].first_nr = lblk->nr; lblk->hdr.crc = cpu_to_le32(crc_block(&bt->hdr, SCOUTFS_BLOCK_LG_SIZE)); - ret = write_raw_block(fd, blkno, SCOUTFS_BLOCK_LG_SHIFT, lblk); + ret = write_raw_block(meta_fd, blkno, SCOUTFS_BLOCK_LG_SHIFT, lblk); if (ret) goto out; /* the data allocator has a single extent */ blkno = next_meta++; - ret = write_alloc_root(super, fd, &super->data_alloc, bt, + ret = write_alloc_root(super, meta_fd, &super->data_alloc, bt, blkno, first_data, le64_to_cpu(super->total_data_blocks)); if (ret < 0) @@ -300,7 +262,7 @@ static int write_new_fs(char *path, int fd, u8 quorum_count, u64 dev_blocks) /* each meta alloc root contains a portion of free metadata extents */ for (i = 0; i < array_size(super->meta_alloc); i++) { blkno = next_meta++; - ret = write_alloc_root(super, fd, &super->meta_alloc[i], bt, + ret = write_alloc_root(super, meta_fd, &super->meta_alloc[i], bt, blkno, meta_start, min(meta_len, last_meta - meta_start + 1)); @@ -312,7 +274,7 @@ static int write_new_fs(char *path, int fd, u8 quorum_count, u64 dev_blocks) /* zero out quorum blocks */ for (i = 0; i < SCOUTFS_QUORUM_BLOCKS; i++) { - ret = write_raw_block(fd, SCOUTFS_QUORUM_BLKNO + i, + ret = write_raw_block(meta_fd, SCOUTFS_QUORUM_BLKNO + i, SCOUTFS_BLOCK_SM_SHIFT, zeros); if (ret < 0) { fprintf(stderr, "error zeroing quorum block: %s (%d)\n", @@ -321,31 +283,46 @@ static int write_new_fs(char *path, int fd, u8 quorum_count, u64 dev_blocks) } } - /* write the super block */ + /* write the super block to data dev and meta dev*/ super->hdr.seq = cpu_to_le64(1); - ret = write_block(fd, SCOUTFS_SUPER_BLKNO, SCOUTFS_BLOCK_SM_SHIFT, + ret = write_block(data_fd, SCOUTFS_SUPER_BLKNO, SCOUTFS_BLOCK_SM_SHIFT, NULL, &super->hdr); if (ret) goto out; - if (fsync(fd)) { + if (fsync(data_fd)) { ret = -errno; fprintf(stderr, "failed to fsync '%s': %s (%d)\n", - path, strerror(errno), errno); + data_path, strerror(errno), errno); + goto out; + } + + super->flags |= cpu_to_le64(SCOUTFS_FLAG_IS_META_BDEV); + ret = write_block(meta_fd, SCOUTFS_SUPER_BLKNO, SCOUTFS_BLOCK_SM_SHIFT, + NULL, &super->hdr); + if (ret) + goto out; + + if (fsync(meta_fd)) { + ret = -errno; + fprintf(stderr, "failed to fsync '%s': %s (%d)\n", + meta_path, strerror(errno), errno); goto out; } uuid_unparse(super->uuid, uuid_str); printf("Created scoutfs filesystem:\n" - " device path: %s\n" + " meta device path: %s\n" + " data device path: %s\n" " fsid: %llx\n" " format hash: %llx\n" " uuid: %s\n" " 64KB metadata blocks: "SIZE_FMT"\n" " 4KB data blocks: "SIZE_FMT"\n" " quorum count: %u\n", - path, + meta_path, + data_path, le64_to_cpu(super->hdr.fsid), le64_to_cpu(super->format_hash), uuid_str, @@ -374,15 +351,18 @@ static struct option long_ops[] = { static int mkfs_func(int argc, char *argv[]) { unsigned long long ull; - char *path = argv[1]; u8 quorum_count = 0; - u64 dev_blocks = 0; + u64 max_data_size = 0; + u64 max_meta_size = 0; char *end = NULL; + char *meta_path; + char *data_path; + int meta_fd; + int data_fd; int ret; - int fd; int c; - while ((c = getopt_long(argc, argv, "Q:S:", long_ops, NULL)) != -1) { + while ((c = getopt_long(argc, argv, "Q:D:M:", long_ops, NULL)) != -1) { switch (c) { case 'Q': ull = strtoull(optarg, &end, 0); @@ -394,10 +374,18 @@ static int mkfs_func(int argc, char *argv[]) } quorum_count = ull; break; - case 'S': - ret = parse_u64(optarg, &dev_blocks); + case 'D': + ret = parse_human(optarg, &max_data_size); if (ret < 0) { - printf("scoutfs: invalid device blocks count '%s'\n", + printf("scoutfs: invalid data device size '%s'\n", + optarg); + return ret; + } + break; + case 'M': + ret = parse_human(optarg, &max_meta_size); + if (ret < 0) { + printf("scoutfs: invalid meta device size '%s'\n", optarg); return ret; } @@ -408,28 +396,39 @@ static int mkfs_func(int argc, char *argv[]) } } - if (optind >= argc) { - printf("scoutfs: mkfs: a single path argument is required\n"); + if (optind + 2 != argc) { + printf("scoutfs: mkfs: paths to metadata and data devices are required\n"); return -EINVAL; } - path = argv[optind]; + meta_path = argv[optind]; + data_path = argv[optind + 1]; if (!quorum_count) { printf("provide quorum count with --quorum_count|-Q option\n"); return -EINVAL; } - fd = open(path, O_RDWR | O_EXCL); - if (fd < 0) { + meta_fd = open(meta_path, O_RDWR | O_EXCL); + if (meta_fd < 0) { ret = -errno; - fprintf(stderr, "failed to open '%s': %s (%d)\n", - path, strerror(errno), errno); + fprintf(stderr, "failed to open metadata device '%s': %s (%d)\n", + meta_path, strerror(errno), errno); return ret; } - ret = write_new_fs(path, fd, quorum_count, dev_blocks); - close(fd); + data_fd = open(data_path, O_RDWR | O_EXCL); + if (data_fd < 0) { + ret = -errno; + fprintf(stderr, "failed to open data device '%s': %s (%d)\n", + data_path, strerror(errno), errno); + return ret; + } + + ret = write_new_fs(meta_path, data_path, meta_fd, data_fd, + quorum_count, max_meta_size, max_data_size); + close(meta_fd); + close(data_fd); return ret; } diff --git a/utils/src/parse.c b/utils/src/parse.c index 720e3a25..761129f9 100644 --- a/utils/src/parse.c +++ b/utils/src/parse.c @@ -10,6 +10,70 @@ #include "parse.h" +/* + * Convert size with multiplicative suffix to bytes. + * e.g. "40M", "10G", "4T" + * + * These are powers-of-two prefixes - K means 1024 not 1000. + * + * One can go pretty far with variations but keeping relatively simple for + * now: commas, decimals, and multichar suffixes not handled. + */ +int parse_human(char* str, u64 *val_ret) +{ + unsigned long long ull; + char *endptr = NULL; + int sh; + int ret = 0; + + ull = strtoull(str, &endptr, 0); + if (((ull == LLONG_MIN || ull == LLONG_MAX) && + errno == ERANGE)) { + fprintf(stderr, "invalid 64bit value: '%s'\n", str); + *val_ret = 0; + ret = -EINVAL; + goto error; + } + + switch (*endptr) { + case 'K': + sh = 10; + break; + case 'M': + sh = 20; + break; + case 'G': + sh = 30; + break; + case 'T': + sh = 40; + break; + case 'P': + sh = 50; + break; + case '\0': + sh = 0; + break; + default: + fprintf(stderr, "unknown suffix: '%s'\n", endptr); + ret = -ERANGE; + goto error; + } + + if (ull > (SIZE_MAX >> sh)) { + fprintf(stderr, "size too big: '%s'\n", str); + ret = -ERANGE; + goto error; + } + + ull <<= sh; + + *val_ret = ull; + +error: + return ret; +} + int parse_u64(char *str, u64 *val_ret) { unsigned long long ull; diff --git a/utils/src/parse.h b/utils/src/parse.h index a3ffc48d..0a0d9ac4 100644 --- a/utils/src/parse.h +++ b/utils/src/parse.h @@ -3,6 +3,7 @@ #include +int parse_human(char* str, u64 *val_ret); int parse_u64(char *str, u64 *val_ret); int parse_s64(char *str, s64 *val_ret); int parse_u32(char *str, u32 *val_ret); diff --git a/utils/src/print.c b/utils/src/print.c index 280e8456..7f713848 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -880,6 +880,7 @@ static void print_super_block(struct scoutfs_super_block *super, u64 blkno) print_block_header(&super->hdr, SCOUTFS_BLOCK_SM_SIZE); printf(" format_hash %llx uuid %s\n", le64_to_cpu(super->format_hash), uuid_str); + printf(" flags: 0x%016llx\n", super->flags); server_addr = alloc_addr_str(&super->server_addr); if (!server_addr) From 30668c1cdd5a44ad4b15ad22ec009100549b661a Mon Sep 17 00:00:00 2001 From: Andy Grover Date: Thu, 19 Nov 2020 12:17:56 -0800 Subject: [PATCH 234/235] scoutfs-utils: Fix df Not initializing wid[] can cause incorrect output. Also, we only need 6 columns if we reference the array from 0. Signed-off-by: Andy Grover --- utils/src/df.c | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/utils/src/df.c b/utils/src/df.c index 6432b260..96bec369 100644 --- a/utils/src/df.c +++ b/utils/src/df.c @@ -17,7 +17,7 @@ #include "cmd.h" #define ROWS 3 -#define COLS 7 +#define COLS 6 #define CHARS 20 static int df_cmd(int argc, char **argv) @@ -26,7 +26,7 @@ static int df_cmd(int argc, char **argv) struct scoutfs_ioctl_alloc_detail_entry *ade = NULL; struct scoutfs_ioctl_statfs_more sfm; static char cells[ROWS][COLS][CHARS]; - int wid[COLS]; + int wid[COLS] = {0}; u64 nr = 4096 / sizeof(*ade); u64 meta_free = 0; u64 data_free = 0; @@ -87,28 +87,28 @@ static int df_cmd(int argc, char **argv) data_free += ade[i].blocks; } - snprintf(cells[0][1], CHARS, "Type"); - snprintf(cells[0][2], CHARS, "Size"); - snprintf(cells[0][3], CHARS, "Total"); - snprintf(cells[0][4], CHARS, "Used"); - snprintf(cells[0][5], CHARS, "Free"); - snprintf(cells[0][6], CHARS, "Use%%"); + snprintf(cells[0][0], CHARS, "Type"); + snprintf(cells[0][1], CHARS, "Size"); + snprintf(cells[0][2], CHARS, "Total"); + snprintf(cells[0][3], CHARS, "Used"); + snprintf(cells[0][4], CHARS, "Free"); + snprintf(cells[0][5], CHARS, "Use%%"); - snprintf(cells[1][1], CHARS, "MetaData"); - snprintf(cells[1][2], CHARS, "64KB"); - snprintf(cells[1][3], CHARS, "%llu", sfm.total_meta_blocks); - snprintf(cells[1][4], CHARS, "%llu", sfm.total_meta_blocks - meta_free); - snprintf(cells[1][5], CHARS, "%llu", meta_free); - snprintf(cells[1][6], CHARS, "%llu", + snprintf(cells[1][0], CHARS, "MetaData"); + snprintf(cells[1][1], CHARS, "64KB"); + snprintf(cells[1][2], CHARS, "%llu", sfm.total_meta_blocks); + snprintf(cells[1][3], CHARS, "%llu", sfm.total_meta_blocks - meta_free); + snprintf(cells[1][4], CHARS, "%llu", meta_free); + snprintf(cells[1][5], CHARS, "%llu", ((sfm.total_meta_blocks - meta_free) * 100) / sfm.total_meta_blocks); - snprintf(cells[2][1], CHARS, "Data"); - snprintf(cells[2][2], CHARS, "4KB"); - snprintf(cells[2][3], CHARS, "%llu", sfm.total_data_blocks); - snprintf(cells[2][4], CHARS, "%llu", sfm.total_data_blocks - data_free); - snprintf(cells[2][5], CHARS, "%llu", data_free); - snprintf(cells[2][6], CHARS, "%llu", + snprintf(cells[2][0], CHARS, "Data"); + snprintf(cells[2][1], CHARS, "4KB"); + snprintf(cells[2][2], CHARS, "%llu", sfm.total_data_blocks); + snprintf(cells[2][3], CHARS, "%llu", sfm.total_data_blocks - data_free); + snprintf(cells[2][4], CHARS, "%llu", data_free); + snprintf(cells[2][5], CHARS, "%llu", ((sfm.total_data_blocks - data_free) * 100) / sfm.total_data_blocks); From 9a647a98f1ea4e73ab03a7050d75d11ce7f73dd2 Mon Sep 17 00:00:00 2001 From: Andy Grover Date: Mon, 9 Nov 2020 14:12:57 -0800 Subject: [PATCH 235/235] scoutfs-utils: Header changes to match kmod PR 41 Signed-off-by: Andy Grover --- utils/src/format.h | 12 ++++++------ utils/src/ioctl.h | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/utils/src/format.h b/utils/src/format.h index b38a55a8..033552bf 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -708,7 +708,7 @@ struct scoutfs_dirent { /* getdents returns next pos with an entry, no entry at (f_pos)~0 */ #define SCOUTFS_DIRENT_LAST_POS (U64_MAX - 1) -enum { +enum scoutfs_dentry_type { SCOUTFS_DT_FIFO = 0, SCOUTFS_DT_CHR, SCOUTFS_DT_DIR, @@ -806,7 +806,7 @@ struct scoutfs_net_header { #define SCOUTFS_NET_FLAG_RESPONSE (1 << 0) #define SCOUTFS_NET_FLAGS_UNKNOWN (U8_MAX << 1) -enum { +enum scoutfs_net_cmd { SCOUTFS_NET_CMD_GREETING = 0, SCOUTFS_NET_CMD_ALLOC_INODES, SCOUTFS_NET_CMD_GET_LOG_TREES, @@ -836,7 +836,7 @@ enum { #undef EXPAND_NET_ERRNO #define EXPAND_NET_ERRNO(which) SCOUTFS_NET_ERR_##which, -enum { +enum scoutfs_net_errors { SCOUTFS_NET_ERR_NONE = 0, EXPAND_EACH_NET_ERRNO SCOUTFS_NET_ERR_UNKNOWN, @@ -884,7 +884,7 @@ struct scoutfs_net_lock_recover { sizeof(struct scoutfs_net_lock)) /* some enums for tracing */ -enum { +enum scoutfs_lock_trace { SLT_CLIENT, SLT_SERVER, SLT_GRANT, @@ -905,7 +905,7 @@ enum { * * The null mode provides no access and is used to destroy locks. */ -enum { +enum scoutfs_lock_mode { SCOUTFS_LOCK_NULL = 0, SCOUTFS_LOCK_READ, SCOUTFS_LOCK_WRITE, @@ -928,7 +928,7 @@ struct scoutfs_fid { /* * Identifiers for sources of corruption that can generate messages. */ -enum { +enum scoutfs_corruption_sources { SC_DIRENT_NAME_LEN = 0, SC_DIRENT_BACKREF_NAME_LEN, SC_DIRENT_READDIR_NAME_LEN, diff --git a/utils/src/ioctl.h b/utils/src/ioctl.h index 8dc9d93c..a53626a0 100644 --- a/utils/src/ioctl.h +++ b/utils/src/ioctl.h @@ -78,7 +78,7 @@ struct scoutfs_ioctl_walk_inodes { __u8 _pad[11]; /* padded to align walk_inodes_entry total size */ }; -enum { +enum scoutfs_ino_walk_seq_type { SCOUTFS_IOC_WALK_INODES_META_SEQ = 0, SCOUTFS_IOC_WALK_INODES_DATA_SEQ, SCOUTFS_IOC_WALK_INODES_UNKNOWN,