summaryrefslogtreecommitdiff
path: root/vm/vm_object.c
diff options
context:
space:
mode:
authorRichard Braun <rbraun@sceen.net>2017-07-02 12:07:46 +0200
committerRichard Braun <rbraun@sceen.net>2017-07-02 14:37:55 +0200
commit62142bef38dee8cc8878290a8775da2ae8c1d86d (patch)
tree799d1538ccde20ed9298931752416f7d5eab8533 /vm/vm_object.c
parenta822e80fe2b15cb93d01388b2d2a9f944ca72372 (diff)
vm/vm_object: new module
Start a very simple VM object implementation for page tracking only. The locking protocol is still not well defined, especially for pages. The only purpose of the current code is to allow the kernel virtual memory interface to release pages on physical mapping creation errors.
Diffstat (limited to 'vm/vm_object.c')
-rw-r--r--vm/vm_object.c142
1 files changed, 142 insertions, 0 deletions
diff --git a/vm/vm_object.c b/vm/vm_object.c
new file mode 100644
index 00000000..874b26f5
--- /dev/null
+++ b/vm/vm_object.c
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2017 Richard Braun.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ * This implementation is based on the paper "A lockless pagecache in Linux"
+ * by Nick Piggin. It allows looking up pages without contention on VM objects.
+ */
+
+#include <assert.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include <kern/init.h>
+#include <kern/llsync.h>
+#include <kern/mutex.h>
+#include <kern/rdxtree.h>
+#include <vm/vm_object.h>
+#include <vm/vm_page.h>
+#include <machine/page.h>
+
+void __init
+vm_object_setup(void)
+{
+}
+
+void __init
+vm_object_init(struct vm_object *object, uint64_t size)
+{
+ assert(vm_page_aligned(size));
+
+ mutex_init(&object->lock);
+ rdxtree_init(&object->pages, 0);
+ object->size = size;
+ object->nr_pages = 0;
+}
+
+int
+vm_object_insert(struct vm_object *object, struct vm_page *page,
+ uint64_t offset)
+{
+ int error;
+
+ assert(vm_page_aligned(offset));
+
+ /*
+ * The page may have no references. Add one before publishing
+ * so that concurrent lookups succeed.
+ */
+ vm_page_ref(page);
+
+ mutex_lock(&object->lock);
+
+ if (offset >= object->size) {
+ error = ERROR_INVAL;
+ goto error;
+ }
+
+ error = rdxtree_insert(&object->pages, vm_page_atop(offset), page);
+
+ if (error) {
+ goto error;
+ }
+
+ vm_page_link(page, object, offset);
+ object->nr_pages++;
+ assert(object->nr_pages != 0);
+
+ mutex_unlock(&object->lock);
+
+ return 0;
+
+error:
+ mutex_unlock(&object->lock);
+
+ vm_page_unref(page);
+
+ return error;
+}
+
+void
+vm_object_remove(struct vm_object *object, uint64_t start, uint64_t end)
+{
+ struct vm_page *page;
+ uint64_t offset;
+
+ assert(vm_page_aligned(start));
+ assert(vm_page_aligned(end));
+ assert(start <= end);
+
+ mutex_lock(&object->lock);
+
+ for (offset = start; offset < end; offset += PAGE_SIZE) {
+ page = rdxtree_remove(&object->pages, vm_page_atop(offset));
+
+ if (page == NULL) {
+ continue;
+ }
+
+ vm_page_unlink(page);
+ vm_page_unref(page);
+ assert(object->nr_pages != 0);
+ object->nr_pages--;
+ }
+
+ mutex_unlock(&object->lock);
+}
+
+struct vm_page *
+vm_object_lookup(struct vm_object *object, uint64_t offset)
+{
+ struct vm_page *page;
+ int error;
+
+ llsync_read_enter();
+
+ do {
+ page = rdxtree_lookup(&object->pages, vm_page_atop(offset));
+
+ if (page == NULL) {
+ break;
+ }
+
+ error = vm_page_tryref(page);
+ } while (error);
+
+ llsync_read_exit();
+
+ return page;
+}