From a9a2ea9125f89347e0aa038a136ebd43e6b251b4 Mon Sep 17 00:00:00 2001 From: Alex Auvolat Date: Mon, 9 Mar 2015 13:35:45 +0100 Subject: Add pager interface ; implement basic functionnality to replace private maps. --- src/kernel/Makefile | 2 +- src/kernel/include/pager.h | 73 +++++++++++++++++ src/kernel/include/process.h | 15 ++-- src/kernel/include/sys.h | 7 +- src/kernel/include/vfs.h | 4 + src/kernel/user/pager.c | 183 +++++++++++++++++++++++++++++++++++++++++++ src/kernel/user/process.c | 93 +++++++++++----------- 7 files changed, 321 insertions(+), 56 deletions(-) create mode 100644 src/kernel/include/pager.h create mode 100644 src/kernel/user/pager.c (limited to 'src') diff --git a/src/kernel/Makefile b/src/kernel/Makefile index f7cee23..1b01f13 100644 --- a/src/kernel/Makefile +++ b/src/kernel/Makefile @@ -3,7 +3,7 @@ OBJ = core/loader.o core/dbglog.o \ core/gdt.o core/idt.o core/interrupt.o core/context_switch.o core/thread.o \ core/frame.o core/paging.o core/freemem.o core/region.o core/kmalloc.o \ core/worker.o core/prng.o \ - user/vfs.o user/nullfs.o user/process.o user/elf.o user/syscall.o user/ipc.o \ + user/vfs.o user/nullfs.o user/process.o user/elf.o user/syscall.o user/ipc.o user/pager.o \ dev/pci.o dev/pciide.o \ fs/iso9660.o diff --git a/src/kernel/include/pager.h b/src/kernel/include/pager.h new file mode 100644 index 0000000..5491d10 --- /dev/null +++ b/src/kernel/include/pager.h @@ -0,0 +1,73 @@ +#pragma once + +#include +#include + +#include + +#define PAGE_PINNED 0x01 +#define PAGE_ACCESSED 0x02 +#define PAGE_DIRTY 0x04 + +typedef struct pager pager_t; +typedef struct user_region user_region_t; +typedef struct fs_node fs_node_t; + +typedef struct { + size_t (*read)(fs_node_t* data, size_t offset, size_t len, char* ptr); + size_t (*write)(fs_node_t* data, size_t offset, size_t len, const char* ptr); + bool (*resize)(fs_node_t* data, size_t new_size); +} vfs_pager_ops_t; + +typedef struct { + void (*page_in)(pager_t *p, size_t offset, size_t len); // allocates the pages + void (*page_commit)(pager_t *p, size_t offset, size_t len); // writes pages back to storage (if applicable) + void (*page_out)(pager_t *p, size_t offset, size_t len); // frees the pages (no need to write back) + void (*page_release)(pager_t *p, size_t offset, size_t len); // called on delete/resize +} pager_ops_t; + +typedef struct pager { + pager_ops_t *ops; + + union { + struct { + fs_node_t* vfs_data; + vfs_pager_ops_t *vfs_ops; + } vfs_pager; + struct { + size_t phys_offset; + } device_pager; + }; + + size_t size; + + hashtbl_t *pages; + mutex_t lock; + + user_region_t *maps; +} pager_t; + +pager_t* new_swap_pager(size_t size); +pager_t* new_vfs_pager(size_t size, fs_node_t* vfs_node, vfs_pager_ops_t *vfs_ops); +pager_t* new_dev_pager(size_t size, size_t phys_offset); + +void delete_pager(pager_t *p); + +bool pager_resize(pager_t *p, size_t newsize); + +// void pager_pin_region(pager_t *pager, size_t offset, size_t len); // implement later +// void pager_unpin_region(pager_t *pager, size_t offset, size_t len); + +void pager_access(pager_t *p, size_t offset, size_t len, bool accessed, bool dirty); +void pager_commit_dirty(pager_t *p); + +int pager_get_frame(pager_t *p, size_t offset); + +size_t pager_read(pager_t *p, size_t offset, size_t len, char* buf); +size_t pager_write(pager_t *p, size_t offset, size_t len, const char* buf); + +void pager_add_map(pager_t *p, user_region_t *reg); +void pager_rm_map(pager_t *p, user_region_t *reg); + + +/* vim: set ts=4 sw=4 tw=0 noet :*/ diff --git a/src/kernel/include/process.h b/src/kernel/include/process.h index 8dfa274..8cdfe85 100644 --- a/src/kernel/include/process.h +++ b/src/kernel/include/process.h @@ -15,9 +15,9 @@ #include #include +#include -#include - +#include // common header for mmaps #include // common header defining process statuses #define USERSTACK_ADDR 0xB8000000 @@ -31,12 +31,15 @@ typedef struct user_region { void* addr; size_t size; - int mode; + fs_handle_t *file; + pager_t *pager; + size_t offset; + bool own_pager; - fs_handle_t *file; // null if not mmaped-file - size_t file_offset; + int16_t mode; - struct user_region *next; + struct user_region *next_in_proc; + struct user_region *next_for_pager; } user_region_t; typedef struct process { diff --git a/src/kernel/include/sys.h b/src/kernel/include/sys.h index 944df96..61bfc59 100644 --- a/src/kernel/include/sys.h +++ b/src/kernel/include/sys.h @@ -75,9 +75,10 @@ static inline void invlpg(void* addr) { #define PAGE_SIZE 0x1000 #define PAGE_MASK 0xFFFFF000 -#define PAGE_ALIGN_DOWN(x) (((size_t)x) & PAGE_MASK) -#define PAGE_ALIGN_UP(x) ((((size_t)x)&(~PAGE_MASK)) == 0 ? ((size_t)x) : (((size_t)x) & PAGE_MASK) + PAGE_SIZE) -#define PAGE_ID(x) (((size_t)x) / PAGE_SIZE) +#define PAGE_ALIGNED(x) ((((size_t)(x)) & (~PAGE_MASK)) == 0) +#define PAGE_ALIGN_DOWN(x) (((size_t)(x)) & PAGE_MASK) +#define PAGE_ALIGN_UP(x) ((((size_t)(x))&(~PAGE_MASK)) == 0 ? ((size_t)x) : (((size_t)x) & PAGE_MASK) + PAGE_SIZE) +#define PAGE_ID(x) (((size_t)(x)) / PAGE_SIZE) #define PAGE_SHIFT 12 #define PT_SHIFT 10 // PAGE_SHIFT + PT_SHIFT + PT_SHIFT = 32 diff --git a/src/kernel/include/vfs.h b/src/kernel/include/vfs.h index cc6d11c..cdd9e8d 100644 --- a/src/kernel/include/vfs.h +++ b/src/kernel/include/vfs.h @@ -8,6 +8,8 @@ #include // common header +#include + // How to use : // - When using a filesystem : never call the operations in fs_*_ops_t directly, use // the functions defined bellow in section "public functions"; @@ -108,6 +110,8 @@ typedef struct fs_node { // These fields are filled by the FS's specific walk() code fs_node_ops_t *ops; fs_node_ptr data; + + pager_t *pager; } fs_node_t; // ------------------------------------------- diff --git a/src/kernel/user/pager.c b/src/kernel/user/pager.c new file mode 100644 index 0000000..c892599 --- /dev/null +++ b/src/kernel/user/pager.c @@ -0,0 +1,183 @@ +#include + +#include +#include +#include +#include + +#define ENT_FRAME_SHIFT 12 + + +// ========== // +// SWAP PAGER // +// ========== // + +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 pager_ops_t swap_pager_ops = { + .page_in = swap_page_in, + .page_commit = 0, + .page_out = swap_page_out, + .page_release = swap_page_release, +}; + +pager_t *new_swap_pager(size_t size) { + 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 = &swap_pager_ops; + p->size = size; + p->lock = MUTEX_UNLOCKED; + p->maps = 0; + + return p; + +error: + if (p) free(p); + return 0; +} + +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); + if (region == 0) return; + + for (size_t i = 0; i < npages; i++) { + size_t page = offset + (i * 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); + memset(region, 0, PAGE_SIZE); + pd_unmap_page(region); + } + if (!hashtbl_add(p->pages, (void*)page, (void*)(frame << ENT_FRAME_SHIFT))) { + frame_free(frame, 1); + } + } + } + + region_free(region); +} + +void swap_page_out(pager_t *p, size_t offset, size_t len) { + // later : save to swap file... +} + +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); + uint32_t frame = (uint32_t)hashtbl_find(p->pages, (void*)page) >> ENT_FRAME_SHIFT; + if (frame != 0) { + hashtbl_remove(p->pages, (void*)page); + frame_free(frame, 1); + } + } +} + +// ======================= // +// GENERIC PAGER FUNCTIONS // +// ======================= // + +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); + delete_hashtbl(p->pages); + free(p); +} + +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); + uint32_t v = (uint32_t)hashtbl_find(p->pages, (void*)page); + if (v != 0) { + hashtbl_change(p->pages, (void*)page, + (void*)(v | (accessed ? PAGE_ACCESSED : 0) | (dirty ? PAGE_DIRTY : 0))); + } + } + + mutex_unlock(&p->lock); +} + +void pager_commit_dirty(pager_t *p) { + if (!p->ops->page_commit) return; + + 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)); + } + + } + + mutex_unlock(&p->lock); +} + +int pager_get_frame(pager_t *p, size_t offset) { + mutex_lock(&p->lock); + + ASSERT(PAGE_ALIGNED(offset)); + + uint32_t v = (uint32_t)hashtbl_find(p->pages, (void*)offset); + if (v == 0) { + p->ops->page_in(p, offset, PAGE_SIZE); + } + + uint32_t ret = (uint32_t)hashtbl_find(p->pages, (void*)offset) >> ENT_FRAME_SHIFT; + + mutex_unlock(&p->lock); + + return ret; +} + +void pager_add_map(pager_t *p, user_region_t *reg) { + mutex_lock(&p->lock); + + reg->next_for_pager = p->maps; + p->maps = reg; + + mutex_unlock(&p->lock); +} + +void pager_rm_map(pager_t *p, user_region_t *reg) { + mutex_lock(&p->lock); + + if (p->maps == reg) { + p->maps = reg->next_for_pager; + } else { + for (user_region_t *it = p->maps; it->next_for_pager != 0; it = it->next_for_pager) { + if (it->next_for_pager == reg) { + it->next_for_pager = reg->next_for_pager; + break; + } + } + } + reg->next_for_pager = 0; + + mutex_unlock(&p->lock); +} + +/* vim: set ts=4 sw=4 tw=0 noet :*/ diff --git a/src/kernel/user/process.c b/src/kernel/user/process.c index 2812fc6..1b3013b 100644 --- a/src/kernel/user/process.c +++ b/src/kernel/user/process.c @@ -489,32 +489,38 @@ bool mmap(process_t *proc, void* addr, size_t size, int mode) { if ((uint32_t)addr & (~PAGE_MASK)) return false; + if (addr >= (void*)K_HIGHHALF_ADDR || addr + size > (void*)K_HIGHHALF_ADDR + || addr + size <= addr || size == 0) return false; + user_region_t *r = (user_region_t*)malloc(sizeof(user_region_t)); if (r == 0) return false; + r->proc = proc; r->addr = addr; - r->size = PAGE_ALIGN_UP(size); + r->size = size; - if (r->addr >= (void*)K_HIGHHALF_ADDR || r->addr + r->size > (void*)K_HIGHHALF_ADDR - || r->addr + r->size <= r->addr || r->size == 0) { - free(r); - return false; - } + r->own_pager = true; + r->pager = new_swap_pager(size); + if (r->pager == 0) goto error; + r->offset = 0; bool add_ok = btree_add(proc->regions_idx, r->addr, r); - if (!add_ok) { - free(r); - return false; - } + if (!add_ok) goto error; r->mode = mode; r->file = 0; - r->file_offset = 0; - r->next = proc->regions; + r->next_in_proc = proc->regions; proc->regions = r; + pager_add_map(r->pager, r); + return true; + +error: + if (r && r->pager) delete_pager(r->pager); + if (r) free(r); + return false; } bool mmap_file(process_t *proc, fs_handle_t *h, size_t offset, void* addr, size_t size, int mode) { @@ -527,34 +533,38 @@ bool mmap_file(process_t *proc, fs_handle_t *h, size_t offset, void* addr, size_ if (!(fmode & FM_MMAP) || !(fmode & FM_READ)) return false; if ((mode & MM_WRITE) && !(fmode & FM_WRITE)) return false; + ASSERT(h->node->pager != 0); + + if (addr >= (void*)K_HIGHHALF_ADDR || addr + size > (void*)K_HIGHHALF_ADDR + || addr + size <= addr || size == 0) return false; + user_region_t *r = (user_region_t*)malloc(sizeof(user_region_t)); if (r == 0) return false; r->addr = addr; - r->size = PAGE_ALIGN_UP(size); - - if (r->addr >= (void*)K_HIGHHALF_ADDR || r->addr + r->size > (void*)K_HIGHHALF_ADDR - || r->addr + r->size <= r->addr || r->size == 0) { - free(r); - return false; - } + r->size = size; bool add_ok = btree_add(proc->regions_idx, r->addr, r); - if (!add_ok) { - free(r); - return false; - } + if (!add_ok) goto error; ref_file(h); + r->file = h; r->mode = mode; - r->file = h; - r->file_offset = offset; + r->offset = offset; + r->pager = h->node->pager; + r->own_pager = false; - r->next = proc->regions; + pager_add_map(r->pager, r); + + r->next_in_proc = proc->regions; proc->regions = r; return true; + +error: + if (r) free(r); + return false; } bool mchmap(process_t *proc, void* addr, int mode) { @@ -592,11 +602,11 @@ bool munmap(process_t *proc, void* addr) { if (r == 0) return false; if (proc->regions == r) { - proc->regions = r->next; + proc->regions = r->next_in_proc; } else { - for (user_region_t *it = proc->regions; it != 0; it = it->next) { - if (it->next == r) { - it->next = r->next; + for (user_region_t *it = proc->regions; it != 0; it = it->next_in_proc) { + if (it->next_in_proc == r) { + it->next_in_proc = r->next_in_proc; break; } } @@ -609,22 +619,20 @@ bool munmap(process_t *proc, void* addr) { switch_pagedir(proc->pd); for (void* it = r->addr; it < r->addr + r->size; it += PAGE_SIZE) { uint32_t ent = pd_get_entry(it); - uint32_t frame = pd_get_frame(it); if (ent & PTE_PRESENT) { - if ((ent & PTE_DIRTY) && (r->mode & MM_WRITE) && r->file != 0) { - // TODO COMMIT PAGE!! - } + pager_access(r->pager, + it - r->addr + r->offset, PAGE_SIZE, + (ent & PTE_ACCESSED), (ent & PTE_DIRTY)); pd_unmap_page(it); - if (r->file != 0) { - // frame is owned by process - frame_free(frame, 1); - } } } switch_pagedir(save_pd); + pager_rm_map(r->pager, r); + if (r->file != 0) unref_file(r->file); + if (r->own_pager) delete_pager(r->pager); free(r); @@ -663,12 +671,7 @@ static void proc_usermem_pf(void* p, registers_t *regs, void* addr) { uint32_t frame; do { - if (r->file == 0) { - frame = frame_alloc(1); - } else { - PANIC("Not implemented mmap (AWFUL TODO)"); - // Here we must get the page from the cache - } + frame = pager_get_frame(r->pager, addr - r->addr + r->offset); if (frame == 0) { free_some_memory(); } @@ -677,8 +680,6 @@ static void proc_usermem_pf(void* p, registers_t *regs, void* addr) { while(!pd_map_page(addr, frame, (r->mode & MM_WRITE) != 0)) { free_some_memory(); } - - if (r->file == 0) memset(addr, 0, PAGE_SIZE); // zero out } void probe_for_read(const void* addr, size_t len) { -- cgit v1.2.3