BarryServer : Git

All the code for all my projects
// BarryServer : Git / Orion / blob / 1628fcfdfdf2978ed9ccac96ee7d13bb3dc43a01 / vfs / cache.c

// Related

Orion

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