From 140972727f77ae91085be934ca5376a66676a7c9 Mon Sep 17 00:00:00 2001 From: Ian Moffett Date: Wed, 13 Nov 2024 21:45:04 -0500 Subject: kernel: vfs: Add vnode cache implementation Signed-off-by: Ian Moffett --- sys/include/sys/sysctl.h | 1 + sys/include/sys/vnode.h | 15 ++++ sys/kern/kern_sysctl.c | 1 + sys/kern/vfs_init.c | 3 + sys/kern/vfs_vcache.c | 226 +++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 246 insertions(+) create mode 100644 sys/kern/vfs_vcache.c diff --git a/sys/include/sys/sysctl.h b/sys/include/sys/sysctl.h index 0f1df26..9623137 100644 --- a/sys/include/sys/sysctl.h +++ b/sys/include/sys/sysctl.h @@ -39,6 +39,7 @@ #define KERN_OSTYPE 0 #define KERN_OSRELEASE 1 #define KERN_VERSION 2 +#define KERN_VCACHE_TYPE 3 /* * Option types (i.e., int, string, etc) for diff --git a/sys/include/sys/vnode.h b/sys/include/sys/vnode.h index 3f3b011..33d5b17 100644 --- a/sys/include/sys/vnode.h +++ b/sys/include/sys/vnode.h @@ -31,6 +31,7 @@ #define _SYS_VNODE_H_ #include +#include #include #include @@ -44,10 +45,18 @@ struct vnode { void *data; const struct vops *vops; uint32_t refcount; + TAILQ_ENTRY(vnode) vcache_link; }; #define vfs_vref(VP) (atomic_inc_int(&(VP)->refcount)) +/* vcache types */ +#if defined(_KERNEL) +#define VCACHE_TYPE_NONE 0 +#define VCACHE_TYPE_PROC 1 +#define VCACHE_TYPE_GLOBAL 2 +#endif /* KERNEL */ + /* Vnode type flags */ #define VNON 0x00 /* Uninitialized */ #define VREG 0x01 /* Regular file */ @@ -86,6 +95,12 @@ struct vops { extern struct vnode *g_root_vnode; +int vfs_vcache_type(void); +int vfs_vcache_migrate(int newtype); + +int vfs_vcache_enter(struct vnode *vp); +struct vnode *vfs_recycle_vnode(void); + int vfs_alloc_vnode(struct vnode **res, int type); int vfs_release_vnode(struct vnode *vp); 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; +} -- cgit v1.2.3