diff options
Diffstat (limited to 'sys/kern')
-rw-r--r-- | sys/kern/kern_sysctl.c | 1 | ||||
-rw-r--r-- | sys/kern/vfs_init.c | 3 | ||||
-rw-r--r-- | sys/kern/vfs_vcache.c | 226 |
3 files changed, 230 insertions, 0 deletions
diff --git a/sys/kern/kern_sysctl.c b/sys/kern/kern_sysctl.c index a1bd91a..6334d06 100644 --- a/sys/kern/kern_sysctl.c +++ b/sys/kern/kern_sysctl.c @@ -52,6 +52,7 @@ static struct sysctl_entry common_optab[] = { [KERN_OSTYPE] = { KERN_OSTYPE, SYSCTL_OPTYPE_STR_RO, hyra }, [KERN_OSRELEASE] = { KERN_OSRELEASE, SYSCTL_OPTYPE_STR_RO, &osrelease }, [KERN_VERSION] = { KERN_VERSION, SYSCTL_OPTYPE_STR_RO, &hyra_version }, + [KERN_VCACHE_TYPE] = { KERN_VCACHE_TYPE, SYSCTL_OPTYPE_STR, NULL } }; static int diff --git a/sys/kern/vfs_init.c b/sys/kern/vfs_init.c index ab18d09..e563df0 100644 --- a/sys/kern/vfs_init.c +++ b/sys/kern/vfs_init.c @@ -56,6 +56,9 @@ vfs_init(void) vfsops->init(fs); } } + + /* Use global vcache by default */ + vfs_vcache_migrate(VCACHE_TYPE_GLOBAL); } struct fs_info * diff --git a/sys/kern/vfs_vcache.c b/sys/kern/vfs_vcache.c new file mode 100644 index 0000000..dc140f7 --- /dev/null +++ b/sys/kern/vfs_vcache.c @@ -0,0 +1,226 @@ +/* + * Copyright (c) 2023-2024 Ian Marco Moffett and the Osmora Team. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Hyra nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/vnode.h> +#include <sys/errno.h> +#include <sys/types.h> +#include <sys/cdefs.h> +#include <sys/sysctl.h> +#include <sys/syslog.h> +#include <sys/panic.h> +#include <sys/spinlock.h> +#include <vm/dynalloc.h> +#include <string.h> + +#define VCACHE_SIZE 64 + +#define pr_trace(fmt, ...) kprintf("vcache: " fmt, ##__VA_ARGS__) + +struct vcache { + TAILQ_HEAD(vcache_head, vnode) q; + ssize_t size; /* In entries (-1 not set up) */ +} vcache = { .size = -1 }; + +/* + * Our vcache will be here if our caching type is + * global. + */ +static int vcache_type = VCACHE_TYPE_NONE; +__cacheline_aligned static struct spinlock vcache_lock; + +/* + * Pull a vnode from the head of the global + * vcache. Returns NULL if none are found. + * + * XXX: Caller must acquire vcache_lock. + */ +static struct vnode * +vcache_global_pull(void) +{ + struct vnode *vp; + + if (vcache.size <= 0) { + return NULL; + } + + vp = TAILQ_FIRST(&vcache.q); + TAILQ_REMOVE(&vcache.q, vp, vcache_link); + --vcache.size; + return vp; +} + +/* + * Add a new entry to the global vcache + * + * XXX: Caller must acquire vcache_lock. + * @vp: New vnode to add. + */ +static int +vcache_global_add(struct vnode *vp) +{ + struct vnode *tmp; + + /* + * Do some checks on the vcache size, should be >= 0 + * but if it is -1 then we just need to initialize the + * queue. However, if it is less than -1... Then shit, + * good luck debugging I suppose. + * + * The global vcache naturally behaves as an LRU cache. + * If we need more space, the tail of the queue is evicted. + */ + if (vcache.size < 0) { + TAILQ_INIT(&vcache.q); + vcache.size = 0; + } else if (vcache.size < -1) { + panic("vcache_global_add: Bad vcache size, catching fire\n"); + } else if (vcache.size == VCACHE_SIZE) { + /* Evict the tail */ + tmp = TAILQ_LAST(&vcache.q, vcache_head); + TAILQ_REMOVE(&vcache.q, tmp, vcache_link); + dynfree(tmp); + --vcache.size; + } + + TAILQ_INSERT_TAIL(&vcache.q, vp, vcache_link); + ++vcache.size; + return 0; +} + +/* + * Migrate the vnode cache (vcache) from one mode + * (e.g., global, proc, none) to another. This transition + * is done without an extreme performance impact through a + * process called lazy vcache migration (LZVM). For example, + * if we update the vcache type to be "proc" from an initial + * type of "global", the global vcache is made read-only until + * all entries are eventually invalidated naturally. In other + * words, both the global vcache and per-process vcaches will + * be checked during the migration process, however once the + * global vcache becomes empty it will no longer be checked. + */ +int +vfs_vcache_migrate(int newtype) +{ + char *sysctl_val; + struct sysctl_args args; + int retval, name = KERN_VCACHE_TYPE; + + switch (newtype) { + case VCACHE_TYPE_NONE: + sysctl_val = "none"; + break; + case VCACHE_TYPE_PROC: + sysctl_val = "proc"; + break; + case VCACHE_TYPE_GLOBAL: + sysctl_val = "global"; + break; + default: + return -EINVAL; + } + + /* Prepare the sysctl args */ + args.name = &name; + args.nlen = 1; + args.oldp = NULL; + args.oldlenp = NULL; + args.newp = sysctl_val; + args.newlen = strlen(sysctl_val); + + if ((retval = sysctl(&args)) != 0) { + return retval; + } + + vcache_type = newtype; + return 0; +} + +/* + * Add a vnode to vcache. + * + * @vp: Pointer of vnode to add. + */ +int +vfs_vcache_enter(struct vnode *vp) +{ + int retval = 0; + + switch (vcache_type) { + case VCACHE_TYPE_NONE: + break; + case VCACHE_TYPE_PROC: + /* TODO */ + pr_trace("warn: proc vcache not supported, using global...\n"); + vcache_type = VCACHE_TYPE_GLOBAL; + /* - FALL THROUGH - */ + case VCACHE_TYPE_GLOBAL: + spinlock_acquire(&vcache_lock); + retval = vcache_global_add(vp); + spinlock_release(&vcache_lock); + break; + default: + pr_trace("warn: Bad vcache type, falling back to none\n"); + vcache_type = VCACHE_TYPE_NONE; + break; + } + + return retval; +} + +/* + * Pull a random vnode from the vcache to + * recycle. + */ +struct vnode * +vfs_recycle_vnode(void) +{ + struct vnode *vp = NULL; + + switch (vcache_type) { + case VCACHE_TYPE_NONE: + break; + case VCACHE_TYPE_PROC: + /* TODO */ + pr_trace("warn: proc vcache not supported, using global...\n"); + vcache_type = VCACHE_TYPE_GLOBAL; + /* - FALL THROUGH - */ + case VCACHE_TYPE_GLOBAL: + spinlock_acquire(&vcache_lock); + vp = vcache_global_pull(); + spinlock_release(&vcache_lock); + break; + default: + pr_trace("warn: Bad vcache type, falling back to none\n"); + vcache_type = VCACHE_TYPE_NONE; + break; + } + + return vp; +} |