Orion
Barry Importing existing Orion kernel d41a53c (3 years, 2 months ago)
diff --git a/vfs/cache.c b/vfs/cache.c
new file mode 100644
index 0000000..54a7495
--- /dev/null
+++ b/vfs/cache.c
@@ -0,0 +1,307 @@
+/*
+ * This file handles the VFS cache. This includes the VFS tree cache and all
+ * the functions for managing the associated structures. One such structure
+ * is the Inode's list of Directory Entries. Every DirEntry has a name, but
+ * also has a hash. This is for fast comparisons - when iterating the list to
+ * search for a name, first check if the hash matches. This saves a lot of time
+ * rather than actually comparing the names. Entries with hash collisions are
+ * placed adjacent in the list.
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include "vfs.h"
+#include "inode.h"
+#include "cache.h"
+#include "../mem/heap.h"
+#include "../mem/paging.h"
+#include "../mem/frame.h"
+#include "../task/task.h"
+
+/* Hash a file name */
+static uint32_t
+name_hash(const char *name)
+{
+ uint32_t digest = 5381;
+ int c;
+ while (c = *name++)
+ digest = ((digest << 5) + digest) ^ c; /* digest*33 ^ c */
+ return digest;
+}
+
+/* Reap any unused branches of the cache tree */
+void
+cache_reaper(void)
+{
+ /*
+ * The Cache Reaper attempts to release all the directory entries
+ * belonging to the root of the current task's file system context.
+ * This will in theory release all the associated inodes. If those
+ * inodes are only held in the cache (not in use, and therefore not
+ * referenced by anything else), they'll be freed and will in turn have
+ * their directory entries released. This recursive process will prune
+ * most of the VFS cache, leaving only in-use inodes.
+ */
+ if (!current)
+ return;
+ if (!current->fs)
+ return;
+ if (!current->fs->root)
+ return;
+ entry_clean(current->fs->root);
+ current->fs->root->dirEntries = NULL;
+}
+
+/* Get a DirEntry */
+DirEntry *
+entry_get(DirEntry *entry)
+{
+ entry->usage++;
+ return entry;
+}
+
+/* Put a DirEntry */
+void
+entry_put(DirEntry *entry)
+{
+ if (--entry->usage)
+ return;
+ if (entry->inode)
+ inode_put(entry->inode);
+ kfree(entry);
+}
+
+/* Search for a Directory Entry in a list by name */
+DirEntry *
+entry_find(Inode *inode, const char *name)
+{
+ if (!inode)
+ return NULL;
+ acquire(&inode->lock);
+ uint32_t search = name_hash(name);
+ DirEntry *item = inode->dirEntries;
+ /* Look for matching hash */
+ while (item) {
+ if (item->hash == search)
+ break;
+ item = item->next;
+ }
+ if (!item) {
+ release(&inode->lock);
+ return NULL;
+ }
+ /* Check all collisions */
+ while (item && item->hash == search) {
+ if (!strcmp(item->name, name))
+ break;
+ item = item->next;
+ }
+ release(&inode->lock);
+ if (!item || item->hash != search)
+ return NULL;
+ return item;
+}
+
+/* Add a Directory Entry to a list */
+void
+entry_add(Inode *inode, DirEntry *insert)
+{
+ if (!inode)
+ return;
+ acquire(&inode->lock);
+ insert->hash = name_hash(insert->name);
+ if (!inode->dirEntries) {
+ inode->dirEntries = entry_get(insert);
+ release(&inode->lock);
+ return;
+ }
+
+ /* Try to find a collision */
+ DirEntry *collision = inode->dirEntries;
+ while (collision->next) {
+ if (collision->hash == insert->hash)
+ break;
+ collision = collision->next;
+ }
+ /* Add into list */
+ insert->next = collision->next;
+ collision->next = entry_get(insert);
+ release(&inode->lock);
+}
+
+/* Remove a Directory Entry from a list */
+void
+entry_remove(Inode *inode, const char *name)
+{
+ if (!inode)
+ return;
+ acquire(&inode->lock);
+ uint32_t search = name_hash(name);
+ DirEntry *item = inode->dirEntries, *prev = NULL;
+ /* Look for matching hash */
+ while (item) {
+ if (item->hash == search)
+ break;
+ prev = item;
+ item = item->next;
+ }
+ if (!item) {
+ release(&inode->lock);
+ return;
+ }
+ /* Check all collisions */
+ while (item && item->hash == search) {
+ if (!strcmp(item->name, name))
+ break;
+ prev = item;
+ item = item->next;
+ }
+ if (!item || item->hash != search) {
+ release(&inode->lock);
+ return;
+ }
+
+ /* Link over item */
+ if (prev)
+ prev->next = item->next;
+ else
+ inode->dirEntries = item;
+ entry_put(item);
+ release(&inode->lock);
+}
+
+/* Clean a list of Directory Entries */
+void
+entry_clean(Inode *inode)
+{
+ if (!inode)
+ return;
+ if (!inode->dirEntries)
+ return;
+ acquire(&inode->lock);
+ DirEntry *de, *den;
+ for (de = inode->dirEntries; de; de = den) {
+ den = de->next;
+ entry_put(de);
+ }
+ release(&inode->lock);
+}
+
+/* Get a Page */
+Page *
+page_get(Page *page)
+{
+ page->usage++;
+ return page;
+}
+
+/* Put a Page */
+void
+page_put(Page *page)
+{
+ if (--page->usage)
+ return;
+ free_frame(PG_ADDR(page->frame));
+ kfree(page);
+}
+
+/* Find a Page by offset */
+Page *
+page_find(Inode *inode, off_t offset)
+{
+ if (!inode)
+ return NULL;
+ acquire(&inode->lock);
+ PageCache *cache;
+ for (cache = inode->pages; cache; cache = cache->next)
+ if (cache->page->offset == PG_ADDR(offset))
+ break;
+ release(&inode->lock);
+ if (!cache)
+ return NULL;
+ return cache->page;
+}
+
+/* Add a page to a list */
+void
+page_add(Inode *inode, Page *page)
+{
+ if (!inode)
+ return;
+ acquire(&inode->lock);
+ PageCache *cache;
+ if (!inode->pages) {
+ inode->pages = kmalloc(sizeof(PageCache));
+ cache = inode->pages;
+ } else {
+ for (cache = inode->pages; cache->next; cache = cache->next);
+ cache->next = kmalloc(sizeof(PageCache));
+ cache = cache->next;
+ }
+ cache->page = page_get(page);
+ release(&inode->lock);
+}
+
+/* Create and add a Page to a list */
+Page *
+page_create(Inode *inode, page_t frame, off_t offset)
+{
+ if (!inode)
+ return NULL;
+ acquire(&inode->lock);
+ Page *prev, *page = kmalloc(sizeof(Page));
+ page->frame = PG_ADDR(frame);
+ page->offset = PG_ADDR(offset);
+ page_add(inode, page);
+ release(&inode->lock);
+ return page;
+}
+
+/* Remove a Page from a list */
+void
+page_remove(Inode *inode, Page *page)
+{
+ if (!inode)
+ return;
+ if (!inode->pages)
+ return;
+ acquire(&inode->lock);
+
+ PageCache *cache, *item;
+ if (inode->pages->page == page) {
+ item = inode->pages;
+ inode->pages = item->next;
+ } else {
+ for (cache = inode->pages;
+ cache->next; cache = cache->next)
+ if (cache->next->page == page)
+ break;
+ if (!cache->next) {
+ release(&inode->lock);
+ return;
+ }
+ item = cache->next;
+ cache->next = item->next;
+ }
+ page_put(page);
+ kfree(item);
+ release(&inode->lock);
+}
+
+/* Clean a list of Pages */
+void
+page_clean(Inode *inode)
+{
+ if (!inode)
+ return;
+ if (!inode->pages)
+ return;
+ acquire(&inode->lock);
+ PageCache *c, *cn;
+ for (c = inode->pages; c; c = cn) {
+ page_put(c->page);
+ cn = c->next;
+ kfree(c);
+ }
+ release(&inode->lock);
+}