aboutsummaryrefslogtreecommitdiff
path: root/src/kernel/user/pager.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/kernel/user/pager.c')
-rw-r--r--src/kernel/user/pager.c185
1 files changed, 163 insertions, 22 deletions
diff --git a/src/kernel/user/pager.c b/src/kernel/user/pager.c
index c892599..f03e45b 100644
--- a/src/kernel/user/pager.c
+++ b/src/kernel/user/pager.c
@@ -15,12 +15,14 @@
static void swap_page_in(pager_t *p, size_t offset, size_t len);
static void swap_page_out(pager_t *p, size_t offset, size_t len);
static void swap_page_release(pager_t *p, size_t offset, size_t len);
+static bool swap_resize(pager_t *p, size_t new_size);
static pager_ops_t swap_pager_ops = {
.page_in = swap_page_in,
.page_commit = 0,
.page_out = swap_page_out,
.page_release = swap_page_release,
+ .resize = swap_resize,
};
pager_t *new_swap_pager(size_t size) {
@@ -44,17 +46,15 @@ error:
void swap_page_in(pager_t *p, size_t offset, size_t len) {
ASSERT(PAGE_ALIGNED(offset));
- size_t npages = PAGE_ALIGN_UP(len) / PAGE_SIZE;
- void *region = region_alloc(PAGE_SIZE, "Page zero area", 0);
+ void *region = region_alloc(PAGE_SIZE, "Page zeroing area", 0);
if (region == 0) return;
- for (size_t i = 0; i < npages; i++) {
- size_t page = offset + (i * PAGE_SIZE);
+ for (size_t page = offset; page < offset + len; page += PAGE_SIZE) {
if (hashtbl_find(p->pages, (void*)page) == 0) {
uint32_t frame = frame_alloc(1);
if (frame != 0) {
- pd_map_page(region, frame, true);
+ if (!pd_map_page(region, frame, true)) PANIC("TODO");;
memset(region, 0, PAGE_SIZE);
pd_unmap_page(region);
}
@@ -73,10 +73,8 @@ void swap_page_out(pager_t *p, size_t offset, size_t len) {
void swap_page_release(pager_t *p, size_t offset, size_t len) {
ASSERT(PAGE_ALIGNED(offset));
- size_t npages = PAGE_ALIGN_UP(len) / PAGE_SIZE;
- for (size_t i = 0; i < npages; i++) {
- size_t page = offset + (i * PAGE_SIZE);
+ for (size_t page = offset; page < offset + len; page += PAGE_SIZE) {
uint32_t frame = (uint32_t)hashtbl_find(p->pages, (void*)page) >> ENT_FRAME_SHIFT;
if (frame != 0) {
hashtbl_remove(p->pages, (void*)page);
@@ -85,6 +83,79 @@ void swap_page_release(pager_t *p, size_t offset, size_t len) {
}
}
+bool swap_resize(pager_t *p, size_t new_size) {
+ // later : remove unused pages in swap file
+
+ swap_page_release(p, PAGE_ALIGN_UP(new_size), p->size - PAGE_ALIGN_UP(new_size));
+
+ size_t last_page = PAGE_ALIGN_DOWN(new_size);
+
+ if (!PAGE_ALIGNED(new_size) && hashtbl_find(p->pages, (void*)last_page) != 0) {
+ void *region = region_alloc(PAGE_SIZE, "Page zeroing area", 0);
+ if (!region) PANIC("TODO");
+
+ uint32_t frame = (uint32_t)hashtbl_find(p->pages, (void*)last_page) >> ENT_FRAME_SHIFT;
+ ASSERT(frame != 0);
+
+ if (!pd_map_page(region, frame, true)) PANIC("TODO");
+
+ size_t b0 = new_size - last_page;
+ memset(region + b0, 0, PAGE_SIZE - b0);
+
+ pd_unmap_page(region);
+
+ region_free(region);
+ }
+
+ return true;
+}
+
+// ============ //
+// DEVICE PAGER //
+// ============ //
+
+static void device_page_in(pager_t *p, size_t offset, size_t len);
+
+static pager_ops_t device_pager_ops = {
+ .page_in = device_page_in,
+ .page_commit = 0,
+ .page_out = 0,
+ .page_release = 0,
+ .resize = 0,
+};
+
+pager_t *new_device_page(size_t size, size_t phys_offset) {
+ ASSERT(PAGE_ALIGNED(phys_offset));
+
+ pager_t *p = (pager_t*)malloc(sizeof(pager_t));
+ if (p == 0) return 0;
+
+ p->pages = create_hashtbl(id_key_eq_fun, id_hash_fun, 0);
+ if (p->pages == 0) goto error;
+
+ p->ops = &device_pager_ops;
+ p->size = size;
+ p->lock = MUTEX_UNLOCKED;
+ p->maps = 0;
+ p->device_pager.phys_offset = phys_offset;
+
+ return p;
+
+error:
+ if (p) free(p);
+ return 0;
+}
+
+void device_page_in(pager_t *p, size_t offset, size_t len) {
+ ASSERT(PAGE_ALIGNED(offset));
+
+ for (size_t page = offset; page < offset + len; page += PAGE_SIZE) {
+ if (hashtbl_find(p->pages, (void*)page) == 0) {
+ hashtbl_add(p->pages, (void*)page, (void*)(p->device_pager.phys_offset + page));
+ }
+ }
+}
+
// ======================= //
// GENERIC PAGER FUNCTIONS //
// ======================= //
@@ -92,23 +163,33 @@ void swap_page_release(pager_t *p, size_t offset, size_t len) {
void delete_pager(pager_t *p) {
ASSERT(p->maps == 0);
- pager_commit_dirty(p);
-
mutex_lock(&p->lock);
- p->ops->page_release(p, 0, p->size);
+ if (p->ops->page_commit) p->ops->page_commit(p, 0, p->size);
+ if (p->ops->page_release) p->ops->page_release(p, 0, p->size);
+
delete_hashtbl(p->pages);
free(p);
}
+bool pager_resize(pager_t *p, size_t newsize) {
+ if (!p->ops->resize) return false;
+
+ mutex_lock(&p->lock);
+
+ bool ret = p->ops->resize(p, newsize);
+
+ mutex_unlock(&p->lock);
+
+ return ret;
+}
+
void pager_access(pager_t *p, size_t offset, size_t len, bool accessed, bool dirty) {
mutex_lock(&p->lock);
ASSERT(PAGE_ALIGNED(offset));
- size_t npages = PAGE_ALIGN_UP(len) / PAGE_SIZE;
- for (size_t i = 0; i < npages; i++) {
- size_t page = offset + (i * PAGE_SIZE);
+ for (size_t page = offset; page < offset + len; page += PAGE_SIZE) {
uint32_t v = (uint32_t)hashtbl_find(p->pages, (void*)page);
if (v != 0) {
hashtbl_change(p->pages, (void*)page,
@@ -124,14 +205,7 @@ void pager_commit_dirty(pager_t *p) {
mutex_lock(&p->lock);
- for (size_t i = 0; i < p->size; i += PAGE_SIZE) {
- uint32_t v = (uint32_t)hashtbl_find(p->pages, (void*)i);
- if (v & PAGE_DIRTY) {
- p->ops->page_commit(p, i, PAGE_SIZE);
- hashtbl_change(p->pages, (void*)i, (void*)(v & ~PAGE_DIRTY));
- }
-
- }
+ p->ops->page_commit(p, 0, p->size);
mutex_unlock(&p->lock);
}
@@ -153,6 +227,59 @@ int pager_get_frame(pager_t *p, size_t offset) {
return ret;
}
+static size_t pager_do_rw(pager_t *p, size_t offset, size_t len, char* buf, bool write) {
+ size_t ret = 0;
+ void* region = 0;
+
+ mutex_lock(&p->lock);
+
+ if (offset >= p->size) return 0;
+ if (offset + len > p->size) len = p->size - offset;
+
+ size_t first_page = PAGE_ALIGN_DOWN(offset);
+ size_t region_len = offset + len - first_page;
+
+ region = region_alloc(PAGE_ALIGN_UP(region_len), "Temporary pager read/write zone", 0);
+ if (region == 0) goto end_read;
+
+ p->ops->page_in(p, first_page, region_len);
+ for (size_t i = first_page; i < first_page + region_len; i += PAGE_SIZE) {
+ uint32_t frame = (uint32_t)hashtbl_find(p->pages, (void*)i) >> ENT_FRAME_SHIFT;
+ if (frame == 0) goto end_read;
+ if (!pd_map_page(region + i - first_page, frame, write)) goto end_read;
+ }
+
+ if (write) {
+ memcpy(region + offset - first_page, buf, len);
+ for (size_t page = first_page; page < first_page+ region_len; page += PAGE_SIZE) {
+ uint32_t v = (uint32_t)hashtbl_find(p->pages, (void*)page);
+ ASSERT(v != 0);
+ hashtbl_change(p->pages, (void*)page, (void*)(v | PAGE_ACCESSED));
+ }
+ } else {
+ memcpy(buf, region + offset - first_page, len);
+ }
+ ret = len;
+
+end_read:
+ if (region) {
+ for (void* x = region; x < region + region_len; x += PAGE_SIZE) {
+ if (pd_get_frame(x) != 0) pd_unmap_page(x);
+ }
+ region_free(region);
+ }
+ mutex_unlock(&p->lock);
+ return ret;
+}
+
+size_t pager_read(pager_t *p, size_t offset, size_t len, char* buf) {
+ return pager_do_rw(p, offset, len, buf, false);
+}
+
+size_t pager_write(pager_t *p, size_t offset, size_t len, const char* buf) {
+ return pager_do_rw(p, offset, len, (char*)buf, true);
+}
+
void pager_add_map(pager_t *p, user_region_t *reg) {
mutex_lock(&p->lock);
@@ -180,4 +307,18 @@ void pager_rm_map(pager_t *p, user_region_t *reg) {
mutex_unlock(&p->lock);
}
+// ---- For use by FS drivers
+
+size_t fs_read_from_pager(fs_handle_t *f, size_t offset, size_t len, char* buf) {
+ ASSERT(f->node->pager != 0);
+
+ return pager_read(f->node->pager, offset, len, buf);
+}
+size_t fs_write_to_pager(fs_handle_t *f, size_t offset, size_t len, const char* buf) {
+ ASSERT(f->node->pager != 0);
+ if (offset + len > f->node->pager->size) pager_resize(f->node->pager, offset + len);
+
+ return pager_write(f->node->pager, offset, len, buf);
+}
+
/* vim: set ts=4 sw=4 tw=0 noet :*/