BarryServer : Git

All the code for all my projects
// BarryServer : Git / Nucleus / commit / 08afe80d1ca157b9cddb31ee48ab0e6e1823f559 / lib

// Related

Nucleus

Barry Object manager and heap in kernel library 08afe80 (3 years, 2 months ago)
diff --git a/lib/heap.c b/lib/heap.c
new file mode 100644
index 0000000..64e2d85
--- /dev/null
+++ b/lib/heap.c
@@ -0,0 +1,95 @@
+/*
+ * This file contains the functions related to the kernel's heap.  It uses a
+ * simple method of allocating and freeing blocks from a pool in the kernel's
+ * memory space.  This heap will be present in every virtual address space, so
+ * can be used to store any kernel data structures.
+ */
+
+#include <stddef.h>
+#include <stdint.h>
+#include <nucleus/kernel.h>
+#include <nucleus/lib.h>
+
+#define KHEAP_START 0x200000 /* 2MB */
+#define KHEAP_END   0x800000 /* 8MB */
+#define BLOCK_SIZE  16
+
+/* Structure for a memory block header */
+struct Header {
+	struct Header *next, *prev;
+	size_t size;
+	char magic[4];
+};
+
+/* Allocate a region of the heap */
+void *
+kmalloc(size_t size)
+{
+	size_t blockSize, gapSize;
+	uintptr_t blockEnd;
+	struct Header *prev, *head, *next = NULL;
+	head = prev = (void *) KHEAP_START;
+
+	/* Minimum allocation */
+	if (size % BLOCK_SIZE)
+		size += BLOCK_SIZE - (size % BLOCK_SIZE);
+
+	/* Block does not exist, create heap */
+	if (head->prev != head) {
+		head->prev = head;
+		head->next = NULL;
+		goto found;
+	}
+
+	/* Find gap */
+	while (head) {
+		next = head->next;
+		blockSize = sizeof(struct Header) + head->size;
+		blockEnd = (uintptr_t) head + blockSize;
+		if (next)
+			gapSize = (size_t) next - blockEnd;
+		else
+			gapSize = (size_t) KHEAP_END - blockEnd;
+		prev = head;
+
+		/* Fit in gap */
+		if (gapSize >= size + sizeof(struct Header)) {
+			head = (void *) blockEnd;
+			head->next = next;
+			head->prev = prev;
+			prev->next = head;
+			if (next)
+				next->prev = head;
+			goto found;
+		}
+
+		head = head->next;
+	}
+
+	panic("Kernel heap exhausted");
+
+found:
+	/* Found block */
+	head->size = size;
+	memcpy(head->magic, "HEAP", 4);
+	memset(head + 1, 0, size);
+	return (void *) (head + 1);
+}
+
+/* Free an allocated region of the heap */
+void
+kfree(void *addr)
+{
+	struct Header *prev, *head, *next;
+	head = (struct Header *) addr - 1;
+
+	ASSERT(!memcmp(head->magic, "HEAP", 4));
+	prev = head->prev;
+	next = head->next;
+	memset(head, 0, sizeof(struct Header));
+
+	if (prev != head)
+		prev->next = next;
+	if (next)
+		next->prev = prev;
+}
diff --git a/lib/object/list.c b/lib/object/list.c
new file mode 100644
index 0000000..6af7a72
--- /dev/null
+++ b/lib/object/list.c
@@ -0,0 +1,314 @@
+/*
+ * This file implements Object Lists.  Objects can be a part of multiple lists
+ * that can be managed automatically by the Object Manager.  This prevents
+ * subsystems having to implement their own object lists for sub-objects.  Each
+ * list is implemented as an Object itself, which allows the common object
+ * routines to be used on them.
+ */
+
+#include <stdarg.h>
+#include <nucleus/kernel.h>
+#include <nucleus/memory.h>
+#include <nucleus/object.h>
+
+/* Structure for a List Entry */
+struct ListEntry {
+	struct ListEntry *prev, *next;
+	Object *obj;
+};
+/* Structure for an Object List */
+struct ObjectList {
+	struct ListEntry *start, *end;
+	size_t entries;
+	ObjectType *type;
+	enum ListMode mode;
+	compare_callback_t compare;
+	Spinlock lock;
+};
+/* Iterator */
+struct Iterator {
+	ObjectList *list;
+	struct ListEntry *prev, *entry, *next;
+	off_t pos;
+};
+
+void init_lock(Spinlock *lock);
+void acquire(Spinlock *lock);
+void release(Spinlock *lock);
+
+/* Create an Object List */
+ObjectList *
+create_list(ObjectType *type, enum ListMode mode, ...)
+{
+	ObjectList *list = kmalloc(sizeof(ObjectList));
+	init_lock(&list->lock);
+	list->type = type;
+	list->mode = mode;
+
+	if (mode == LIST_ORDERED) {
+		va_list args;
+		va_start(args, mode);
+		list->compare = va_arg(args, compare_callback_t);
+		va_end(args);
+	}
+
+	return list;
+}
+
+/* Destroy an Object List */
+void
+destroy_list(ObjectList *list)
+{
+	while (list->entries > 0)
+		remove(list, list->start->obj);
+}
+
+/* Add an Object to a List */
+void
+add(ObjectList *list, void *addr)
+{
+	Object *obj = addr;
+	ASSERT(obj->magic == OBJECT_MAGIC);
+	if (list->type && obj->type != list->type)
+		return;
+
+	if (list->mode != LIST_LOCKLESS)
+		acquire(&list->lock);
+
+	struct ListEntry *entry, *next;
+	entry = kmalloc(sizeof(struct ListEntry));
+	entry->obj = get(obj);
+	list->entries++;
+
+	if (!list->start) {
+		/* Only item in list */
+		list->start = list->end = entry;
+	} else if (list->compare) {
+		/* Find next item */
+		for (next = list->start; next; next = next->next) {
+			if (list->compare(next->obj, obj) > 0)
+				break;
+		}
+		if (!next)
+			goto end;
+		/* Add in */
+		entry->prev = next->prev;
+		entry->next = next;
+		next->prev = entry;
+		if (entry->prev)
+			entry->prev->next = entry;
+		else
+			list->start = entry;
+	} else {
+end:
+		/* Add to end of list */
+		list->end->next = entry;
+		entry->prev = list->end;
+		list->end = entry;
+	}
+
+	if (list->mode != LIST_LOCKLESS)
+		release(&list->lock);
+}
+
+/* Remove an Object from a List */
+void
+remove(ObjectList *list, void *addr)
+{
+	Object *obj = addr;
+	ASSERT(obj->magic == OBJECT_MAGIC);
+	if (!list->start)
+		return;
+	if (list->type && obj->type != list->type)
+		return;
+	if (list->mode != LIST_LOCKLESS)
+		acquire(&list->lock);
+
+	/* Search for object */
+	struct ListEntry *entry;
+	for (entry = list->start;
+	     entry && entry->obj != obj;
+	     entry = entry->next);
+	if (!entry) {
+		release(&list->lock);
+		return;
+	}
+
+	/* Unlink list */
+	if (list->start == entry)
+		list->start = entry->next;
+	if (list->end == entry)
+		list->end = entry->prev;
+
+	/* Unlink neighbours */
+	if (entry->prev)
+		entry->prev->next = entry->next;
+	if (entry->next)
+		entry->next->prev = entry->prev;
+
+	/* Release resources */
+	if (__builtin_expect(!!(obj->usage), 1))
+		put(obj);
+	list->entries--;
+	kfree(entry);
+	if (list->mode != LIST_LOCKLESS)
+		release(&list->lock);
+}
+
+/* Pop the first Object in a List */
+void *
+pop_from_start(ObjectList *list)
+{
+	if (!list->start)
+		return NULL;
+	if (list->mode != LIST_LOCKLESS)
+		acquire(&list->lock);
+	Object *head = get(list->start->obj);
+	remove(list, head);
+	if (list->mode != LIST_LOCKLESS)
+		release(&list->lock);
+	return head;
+}
+
+/* Pop the last Object in a List */
+void *
+pop_from_end(ObjectList *list)
+{
+	if (!list->end)
+		return NULL;
+	if (list->mode != LIST_LOCKLESS)
+		acquire(&list->lock);
+	Object *tail = get(list->end->obj);
+	remove(list, tail);
+	if (list->mode != LIST_LOCKLESS)
+		release(&list->lock);
+	return tail;
+}
+
+/* Count the number of Objects in a List */
+size_t
+count(ObjectList *list)
+{
+	return list->entries;
+}
+
+/* Get the nth Object in a List */
+void *
+get_nth_item(ObjectList *list, off_t n)
+{
+	if (n > list->entries - 1)
+		return NULL;
+
+	struct ListEntry *entry;
+	if (n < (list->entries >> 1)) {
+		for (entry = list->start; entry; entry = entry->next)
+			if (n-- == 0)
+				break;
+	} else {
+		n = list->entries - 1 - n;
+		for (entry = list->end; entry; entry = entry->prev)
+			if (n-- == 0)
+				break;
+	}
+	return entry->obj;
+}
+
+/* Copy list */
+ObjectList *
+copy_list(ObjectList *list)
+{
+	ObjectList *newlist = create_list(list->type, list->mode,
+	                                  list->compare);
+	if (list->mode != LIST_LOCKLESS)
+		acquire(&list->lock);
+	struct ListEntry *entry;
+	for (entry = list->start; entry; entry = entry->next)
+		add(newlist, entry->obj);
+	if (list->mode != LIST_LOCKLESS)
+		release(&list->lock);
+	return newlist;
+}
+
+/* Concatenate one list onto the end of another */
+void
+concat_list(ObjectList *src, ObjectList *dest)
+{
+	if (src->mode != LIST_LOCKLESS)
+		acquire(&src->lock);
+	struct ListEntry *entry;
+	for (entry = src->start; entry; entry = entry->next)
+		add(dest, entry->obj);
+	if (src->mode != LIST_LOCKLESS)
+		release(&src->lock);
+}
+
+/* Iterate a List */
+Iterator *
+iterate(ObjectList *list)
+{
+	Iterator *i = kmalloc(sizeof(Iterator));
+	i->list = list;
+	return i;
+}
+
+/* Get first iteratable element */
+void *
+first(Iterator *iter)
+{
+	iter->entry = iter->list->start;
+	if (iter->entry) {
+		iter->next = iter->entry->next;
+		iter->pos = 0;
+		return iter->entry->obj;
+	}
+	return NULL;
+}
+
+/* Get last iteratable element */
+void *
+last(Iterator *iter)
+{
+	iter->entry = iter->list->end;
+	if (iter->entry) {
+		iter->prev = iter->entry->prev;
+		iter->pos = iter->list->entries - 1;
+		return iter->entry->obj;
+	}
+	return NULL;
+}
+
+/* Get next iteratable element */
+void *
+next(Iterator *iter)
+{
+	iter->entry = iter->next;
+	if (iter->entry) {
+		iter->prev = iter->entry->prev;
+		iter->next = iter->entry->next;
+		iter->pos++;
+		return iter->entry->obj;
+	}
+	return NULL;
+}
+
+/* Get previous iteratable element */
+void *
+prev(Iterator *iter)
+{
+	iter->entry = iter->prev;
+	if (iter->entry) {
+		iter->prev = iter->entry->prev;
+		iter->next = iter->entry->next;
+		iter->pos--;
+		return iter->entry->obj;
+	}
+	return NULL;
+}
+
+/* End iteration */
+int
+done_iterating(Iterator *iter)
+{
+	kfree(iter);
+	return 0;
+}
diff --git a/lib/object/lock.c b/lib/object/lock.c
new file mode 100644
index 0000000..d7a7d11
--- /dev/null
+++ b/lib/object/lock.c
@@ -0,0 +1,61 @@
+/*
+ * This file implements spinlocks.  It makes heavy use of GCC's atomic built-ins
+ * for syncronisation.  The spinlocks have some simple mechanisms for preventing
+ * deadlocks.  Each spinlock knowns which CPU/task is holding it, and can allow
+ * that CPU/task to acquire it multiple times and safely release it.
+ */
+
+#include <nucleus/kernel.h>
+#include <nucleus/object.h>
+#include <nucleus/task.h>
+
+/* Check if already holding */
+static int
+holding(Spinlock *lock)
+{
+	if (current)
+		return (lock->locked && lock->owner == current);
+	return (lock->locked && lock->cpu == (cpu->id + 1));
+}
+
+/* Initialise a lock */
+void
+init_lock(Spinlock *lock)
+{
+	lock->locked = 0;
+	lock->usage = 0;
+}
+
+/* Acquire a lock */
+void
+acquire(Spinlock *lock)
+{
+	/*
+	 * Reference count the lock so it can be safely acquired by the same
+	 * holder multiple times.  This stops a lock from deadlocking itself.
+	 */
+	__atomic_add_fetch(&lock->usage, 1, __ATOMIC_RELAXED);
+	if (holding(lock))
+		return;
+
+	while (__atomic_test_and_set(&lock->locked, __ATOMIC_ACQUIRE))
+		asm volatile("pause");
+
+	if (current)
+		lock->owner = current;
+	else
+		lock->cpu = cpu->id + 1;
+}
+
+/* Release a lock */
+void
+release(Spinlock *lock)
+{
+	ASSERT(holding(lock));
+	if (__atomic_sub_fetch(&lock->usage, 1, __ATOMIC_RELAXED))
+		return;
+	__atomic_clear(&lock->locked, __ATOMIC_RELEASE);
+
+	lock->owner = NULL;
+	lock->cpu = 0;
+}
diff --git a/lib/object/manager.c b/lib/object/manager.c
new file mode 100644
index 0000000..09d0b07
--- /dev/null
+++ b/lib/object/manager.c
@@ -0,0 +1,115 @@
+/*
+ * This is the Object Manager.  It implements the basic operations each object
+ * needs and leaves the rest up to the respective subsystem that implements that
+ * object.  The object manager is a resource manager which should help improve
+ * memory safety within the kernel.  It reference counts each object and
+ * controls their instantiation and deletion.
+ */
+
+#include <nucleus/kernel.h>
+#include <nucleus/memory.h>
+#include <nucleus/object.h>
+
+void init_lock(Spinlock *lock);
+void acquire(Spinlock *lock);
+void release(Spinlock *lock);
+
+/* Check the magic of an object */
+int
+check(void *addr)
+{
+	Object *obj = addr;
+	return (obj && obj->magic == OBJECT_MAGIC);
+}
+
+/* Obtain a reference to an object */
+void *
+get(void *addr)
+{
+	Object *obj = addr;
+	ASSERT(obj->magic == OBJECT_MAGIC);
+	__atomic_add_fetch(&obj->type->usage, 1, __ATOMIC_RELAXED);
+	__atomic_add_fetch(&obj->usage, 1, __ATOMIC_RELAXED);
+	return addr;
+}
+
+/* Release a reference to an object */
+void
+put(void *addr)
+{
+	Object *obj = addr;
+	ASSERT(obj->magic == OBJECT_MAGIC);
+	__atomic_sub_fetch(&obj->type->usage, 1, __ATOMIC_RELAXED);
+	if (__atomic_sub_fetch(&obj->usage, 1, __ATOMIC_RELAXED))
+		return;
+	if (obj->type->delete)
+		obj->type->delete(obj);
+	remove(obj->type->objects, obj);
+	obj->magic = 0;
+	if (obj->type->free)
+		obj->type->free(obj);
+	else
+		kfree(obj);
+}
+
+/* Create a new instance of an object */
+void *
+new(ObjectType *type)
+{
+	Object *obj;
+	if (type->alloc && type->free)
+		obj = type->alloc();
+	else
+		obj = kmalloc(type->size);
+	init_lock(&obj->lock);
+	obj->magic = OBJECT_MAGIC;
+	obj->type = type;
+	if (type->new)
+		type->new(obj);
+	if (!type->objects)
+		type->objects = create_list(type, LIST_NORMAL);
+	add(type->objects, obj);
+	/* No need to get() since it's in the global list */
+	return obj;
+}
+
+/* Copy an instance of an object */
+void *
+copy(void *addr)
+{
+	Object *parent = addr;
+	Object *child = NULL;
+	ASSERT(parent->magic == OBJECT_MAGIC);
+	if (parent->type->copy) {
+		child = new(parent->type);
+		parent->type->copy(parent, child);
+	}
+	return child;
+}
+
+/* Check how many times an object is referenced */
+refcount_t
+usage(void *addr)
+{
+	Object *obj = addr;
+	ASSERT(obj->magic == OBJECT_MAGIC);
+	return obj->usage;
+}
+
+/* Lock an object */
+void
+lock(void *addr)
+{
+	Object *obj = addr;
+	ASSERT(obj->magic == OBJECT_MAGIC);
+	acquire(&obj->lock);
+}
+
+/* Unlock an object */
+void
+unlock(void *addr)
+{
+	Object *obj = addr;
+	ASSERT(obj->magic == OBJECT_MAGIC);
+	release(&obj->lock);
+}