scoutfs-utils: add support for srch

Signed-off-by: Zach Brown <zab@versity.com>
This commit is contained in:
Zach Brown
2020-08-26 14:39:28 -07:00
committed by Zach Brown
parent e82cce36d9
commit f04a636229
6 changed files with 390 additions and 35 deletions
+101
View File
@@ -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 {
+36 -13
View File
@@ -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.
+163 -2
View File
@@ -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(&ltv->meta_avail),
RADROOT_A(&ltv->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(&ltv->data_avail),
RADROOT_A(&ltv->data_freed));
RADROOT_A(&ltv->data_freed),
SRF_A(&ltv->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, &ltv->srch_file.ref,
ltv->srch_file.height - 1);
if (err && !ret)
ret = err;
err = print_btree(pa->fd, pa->super, "", &ltv->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)
@@ -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 <path>",
cmd_register("search-xattrs", "-n name -f <path>",
"print inode numbers of inodes which may have given xattr",
find_xattrs_cmd);
search_xattrs_cmd);
}
+46
View File
@@ -0,0 +1,46 @@
#include <errno.h>
#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;
}
+7
View File
@@ -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