Orion
Barry Importing existing Orion kernel d41a53c (2 years, 4 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); +}