From c4cb5c0651710b2d4a776bb84c77bdf9fd2404b6 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Tue, 24 Jul 2018 16:26:10 -0700 Subject: [PATCH] scoutfs: add trivial seq file wrapper Add a seq file wrapper which lets callers track objects easily. Signed-off-by: Zach Brown --- kmod/src/Makefile | 2 +- kmod/src/tseq.c | 224 ++++++++++++++++++++++++++++++++++++++++++++++ kmod/src/tseq.h | 32 +++++++ 3 files changed, 257 insertions(+), 1 deletion(-) create mode 100644 kmod/src/tseq.c create mode 100644 kmod/src/tseq.h diff --git a/kmod/src/Makefile b/kmod/src/Makefile index d4d0db75..0cf70986 100644 --- a/kmod/src/Makefile +++ b/kmod/src/Makefile @@ -9,7 +9,7 @@ scoutfs-y += bio.o block.o btree.o client.o compact.o counters.o data.o dir.o \ export.o extents.o file.o inode.o ioctl.o item.o lock.o \ manifest.o msg.o net.o options.o per_task.o seg.o server.o \ scoutfs_trace.o sort_priv.o super.o sysfs.o trans.o \ - triggers.o xattr.o + triggers.o tseq.o xattr.o # # The raw types aren't available in userspace headers. Make sure all diff --git a/kmod/src/tseq.c b/kmod/src/tseq.c new file mode 100644 index 00000000..781d00f3 --- /dev/null +++ b/kmod/src/tseq.c @@ -0,0 +1,224 @@ +/* + * 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 "tseq.h" + +/* + * This trivial seq file wrapper takes care of the details of displaying + * a set of objects in seq file output. We use an augmented rbtree to + * add new objects at the next free file position. The caller takes + * care of the object life times, only debugfs file creation can fail. + */ + +static loff_t tseq_node_total(struct rb_node *node) +{ + struct scoutfs_tseq_entry *ent; + + if (node == NULL) + return 0; + + ent = rb_entry(node, struct scoutfs_tseq_entry, node); + return ent->total; +} + +static struct scoutfs_tseq_entry *tseq_rb_next(struct scoutfs_tseq_entry *ent) +{ + struct rb_node *node = rb_next(&ent->node); + + if (node == NULL) + return NULL; + + return rb_entry(node, struct scoutfs_tseq_entry, node); +} + +static loff_t tseq_compute_total(struct scoutfs_tseq_entry *ent) +{ + return 1 + tseq_node_total(ent->node.rb_left) + + tseq_node_total(ent->node.rb_right); +} + +RB_DECLARE_CALLBACKS(static, tseq_rb_callbacks, struct scoutfs_tseq_entry, + node, loff_t, total, tseq_compute_total) + +void scoutfs_tseq_tree_init(struct scoutfs_tseq_tree *tree, + scoutfs_tseq_show_t show) +{ + spin_lock_init(&tree->lock); + tree->root = RB_ROOT; + tree->show = show; +} + +/* + * Descend towards the leaf node that should be the parent for inserting + * a new entry. + * + * We use the augmented subtree totals to see when a left subtree has + * fewer entries than the current entry's pos which tells us that there + * is a lesser free pos. + * + * If there isn't a lesser free pos then we descend to the right and set + * the minimum possible pos to the pos after the entry we're traversing. + */ +void scoutfs_tseq_add(struct scoutfs_tseq_tree *tree, + struct scoutfs_tseq_entry *ins) +{ + struct scoutfs_tseq_entry *ent; + struct rb_node *parent; + struct rb_node **node; + loff_t min_pos; + + spin_lock(&tree->lock); + + node = &tree->root.rb_node; + parent = NULL; + min_pos = 0; + + while (*node) { + parent = *node; + ent = rb_entry(*node, struct scoutfs_tseq_entry, node); + + ent->total++; + + if (min_pos + tseq_node_total(ent->node.rb_left) < ent->pos) { + node = &ent->node.rb_left; + } else { + min_pos = ent->pos + 1; + node = &ent->node.rb_right; + } + } + + ins->pos = min_pos; + ins->total = 1; + rb_link_node(&ins->node, parent, node); + rb_insert_augmented(&ins->node, &tree->root, &tseq_rb_callbacks); + + spin_unlock(&tree->lock); +} + +static struct scoutfs_tseq_entry *tseq_pos_next(struct scoutfs_tseq_tree *tree, + loff_t pos) +{ + struct scoutfs_tseq_entry *next; + struct scoutfs_tseq_entry *ent; + struct rb_node *node; + + assert_spin_locked(&tree->lock); + + node = tree->root.rb_node; + next = NULL; + + while (node) { + ent = rb_entry(node, struct scoutfs_tseq_entry, node); + + if (pos < ent->pos) { + next = ent; + node = ent->node.rb_left; + } else if (pos > ent->pos) { + node = ent->node.rb_right; + } else { + return ent; + } + } + + return next; +} + +void scoutfs_tseq_del(struct scoutfs_tseq_tree *tree, + struct scoutfs_tseq_entry *ent) +{ + spin_lock(&tree->lock); + rb_erase_augmented(&ent->node, &tree->root, &tseq_rb_callbacks); + RB_CLEAR_NODE(&ent->node); + spin_unlock(&tree->lock); +} + +/* _stop is always called no matter what start returns */ +static void *scoutfs_tseq_seq_start(struct seq_file *m, loff_t *pos) + __acquires(tree->lock) +{ + struct scoutfs_tseq_tree *tree = m->private; + + spin_lock(&tree->lock); + + return tseq_pos_next(tree, *pos); +} + +static void *scoutfs_tseq_seq_next(struct seq_file *m, void *v, loff_t *pos) +{ + struct scoutfs_tseq_entry *ent = v; + + ent = tseq_rb_next(ent); + if (ent) + *pos = ent->pos; + return ent; +} + +static void scoutfs_tseq_seq_stop(struct seq_file *m, void *v) + __releases(tree->lock) +{ + struct scoutfs_tseq_tree *tree = m->private; + + spin_unlock(&tree->lock); +} + +static int scoutfs_tseq_seq_show(struct seq_file *m, void *v) +{ + struct scoutfs_tseq_tree *tree = m->private; + struct scoutfs_tseq_entry *ent = v; + + tree->show(m, ent); + return 0; +} + +static const struct seq_operations scoutfs_tseq_seq_ops = { + .start = scoutfs_tseq_seq_start, + .next = scoutfs_tseq_seq_next, + .stop = scoutfs_tseq_seq_stop, + .show = scoutfs_tseq_seq_show, +}; + +static int scoutfs_tseq_open(struct inode *inode, struct file *file) +{ + struct seq_file *m; + int ret; + + ret = seq_open(file, &scoutfs_tseq_seq_ops); + if (ret == 0) { + m = file->private_data; + m->private = inode->i_private; + } + return ret; +} + +static const struct file_operations scoutfs_tseq_fops = { + .open = scoutfs_tseq_open, + .release = seq_release, + .read = seq_read, + .llseek = seq_lseek, +}; + +/* + * This doesn't create any additional state so the returned dentry + * can be destroyed with the usual debugfs file calls. + */ +struct dentry *scoutfs_tseq_create(const char *name, struct dentry *parent, + struct scoutfs_tseq_tree *tree) +{ + return debugfs_create_file(name, S_IFREG|S_IRUSR, parent, tree, + &scoutfs_tseq_fops); +} diff --git a/kmod/src/tseq.h b/kmod/src/tseq.h new file mode 100644 index 00000000..d9b05a9e --- /dev/null +++ b/kmod/src/tseq.h @@ -0,0 +1,32 @@ +#ifndef _SCOUTFS_TSEQ_H_ +#define _SCOUTFS_TSEQ_H_ + +#include + +struct scoutfs_tseq_entry; +typedef void (*scoutfs_tseq_show_t)(struct seq_file *m, + struct scoutfs_tseq_entry *ent); + +struct scoutfs_tseq_tree { + spinlock_t lock; + struct rb_root root; + scoutfs_tseq_show_t show; +}; + +struct scoutfs_tseq_entry { + struct rb_node node; + loff_t pos; + loff_t total; +}; + +void scoutfs_tseq_tree_init(struct scoutfs_tseq_tree *tree, + scoutfs_tseq_show_t show); +void scoutfs_tseq_add(struct scoutfs_tseq_tree *tree, + struct scoutfs_tseq_entry *ent); +void scoutfs_tseq_del(struct scoutfs_tseq_tree *tree, + struct scoutfs_tseq_entry *ent); + +struct dentry *scoutfs_tseq_create(const char *name, struct dentry *parent, + struct scoutfs_tseq_tree *tree); + +#endif