From e11b2ef0c606ab516a4344aeea1dbba22cb1fe5d Mon Sep 17 00:00:00 2001 From: manuel Date: Thu, 21 Jun 2012 16:47:23 +0200 Subject: initial implementation of memory mapped files --- Makefile.build | 3 +- userprog/process.c | 18 ++++++++- userprog/process.h | 2 + userprog/syscall.c | 62 ++++++++++++++++++++++++++-- vm/mmap.c | 116 +++++++++++++++++++++++++++++++++++++++++++++++++++++ vm/mmap.h | 31 ++++++++++++++ vm/page.c | 43 ++++---------------- vm/page.h | 27 +++++-------- 8 files changed, 241 insertions(+), 61 deletions(-) create mode 100644 vm/mmap.c create mode 100644 vm/mmap.h diff --git a/Makefile.build b/Makefile.build index e68e721..4854124 100644 --- a/Makefile.build +++ b/Makefile.build @@ -62,7 +62,8 @@ userprog_SRC += userprog/gdt.c # GDT initialization. userprog_SRC += userprog/tss.c # TSS management. # No virtual memory code yet. -vm_SRC = vm/page.c +vm_SRC = vm/page.c +vm_SRC += vm/mmap.c # Filesystem code. filesys_SRC = filesys/filesys.c # Filesystem core. diff --git a/userprog/process.c b/userprog/process.c index 2771e76..f399038 100644 --- a/userprog/process.c +++ b/userprog/process.c @@ -130,6 +130,8 @@ start_process (void *aux) thread->process = process; page_table_init (&thread->page_table); + if (!mmap_table_init (&process->mmap_table)) + goto signal; /* Initialize interrupt frame and load executable. */ memset (&if_, 0, sizeof if_); @@ -149,6 +151,14 @@ start_process (void *aux) /* If process startup failed, quit. */ if (thread->process == NULL) { if (process != NULL) { + //TODO: free mmap table + page table? + + /* close/free memory mapped files */ + //mmap_table_free (&process->mmap_table); + + /* free page table */ + //page_table_free (&thread->page_table); + if (process->fd_table.fds != NULL) palloc_free_page (process->fd_table.fds); palloc_free_page (process); @@ -232,6 +242,9 @@ process_exit (void) lock_release (&filesys_lock); } + /* close/free memory mapped files */ + mmap_table_free (&proc->mmap_table); + int fd; for (fd = 2; fd <= proc->fd_table.fd_max; fd++) { process_close_file (fd); @@ -254,6 +267,7 @@ process_exit (void) pagedir_destroy (pd); } + /* free page table */ page_table_free (&thread->page_table); /* Destroy the process structure if the parent is not alive @@ -560,8 +574,8 @@ load_segment (struct file *file, off_t ofs, uint8_t *upage, size_t page_zero_bytes = PGSIZE - page_read_bytes; /* add segment to page table for on demand loading */ - if (!page_table_insert_segment (file, ofs, upage, page_read_bytes, - page_zero_bytes, writable)) + if (!page_table_insert_segment (&thread_current ()->page_table, file, ofs, + upage, page_read_bytes, writable)) return false; /* Advance. */ diff --git a/userprog/process.h b/userprog/process.h index 5fcd80e..bac9a60 100644 --- a/userprog/process.h +++ b/userprog/process.h @@ -2,6 +2,7 @@ #define USERPROG_PROCESS_H #include "threads/thread.h" +#include "vm/mmap.h" #define STACK_SIZE (1 << 23) /* 8MB maximum stack size */ @@ -27,6 +28,7 @@ struct process { /* files */ struct file *executable; /* Loaded executable, if any. */ struct fd_table fd_table; /* File descriptor table */ + struct mmap_table mmap_table; /* Memory mapped files table */ /* Owned by syscall.c */ void* syscall_buffer; diff --git a/userprog/syscall.c b/userprog/syscall.c index 7f8c397..14cbaab 100644 --- a/userprog/syscall.c +++ b/userprog/syscall.c @@ -14,7 +14,8 @@ #include "userprog/pagedir.h" #include "userprog/process.h" #include "userprog/syscall.h" -#include "lib/user/syscall.h" +#include "vm/mmap.h" +#include "vm/page.h" #define STACK_SLOT_SIZE sizeof(int) @@ -226,8 +227,8 @@ syscall_handler (struct intr_frame *f) goto fail; } syscall_sp = sp; - result = fp (sp, &segfault); - if (segfault) + result = fp (sp, &segfault); + if (segfault) goto fail; f->eax = result; return; @@ -574,11 +575,64 @@ syscall_close (void *sp, bool *segfault) static int syscall_mmap (void *sp, bool *segfault) { - return 0; + int fd; + void *addr, *offset; + struct file *file, *file2; + off_t len; + + /* get arguments */ + if (! copy_from_user (&fd, STACK_ADDR (sp,1)) || + ! copy_from_user (&addr, STACK_ADDR (sp, 2))) { + *segfault = true; + return -1; + } + + /* address must be valid and page-aligned */ + if (addr == NULL || pg_ofs (addr)) + return -1; + + /* file descriptors 0 and 1 are reserved */ + if (fd == 0 || fd == 1) + return -1; + + /* get file */ + file = process_get_file (fd); + if (file == NULL) + return -1; + + /* file length must be positiv */ + len = file_length (file); + if (len <= 0) + return -1; + + /* check if the pages don't overlap any existing pages */ + offset = addr; + while(offset < addr + len) + { + if (page_table_fetch (&thread_current ()->page_table, offset)) + return -1; + offset += PGSIZE; + } + + process_lock_filesys (); + file2 = file_reopen (file); + process_unlock_filesys (); + + return (file2 == NULL) ? -1 : + mmap_table_insert (&process_current()->mmap_table, addr, file2, len); } static int syscall_munmap (void *sp, bool *segfault) { + mapid_t mapping; + + /* get arguments */ + if (! copy_from_user (&mapping, STACK_ADDR (sp,1))) { + *segfault = true; + return 0; + } + + mmap_table_remove (&process_current()->mmap_table, mapping); return 0; } diff --git a/vm/mmap.c b/vm/mmap.c new file mode 100644 index 0000000..9e7ae47 --- /dev/null +++ b/vm/mmap.c @@ -0,0 +1,116 @@ +#include +#include "filesys/file.h" +#include "threads/thread.h" +#include "threads/vaddr.h" +#include "threads/malloc.h" +#include "threads/palloc.h" +#include "vm/mmap.h" +#include "vm/page.h" + +static struct mmap_table_entry *mmap_table_get (struct mmap_table *table, + mapid_t mapid); + +/* initialize memory mapped files table */ +bool +mmap_table_init (struct mmap_table *table) +{ + table->ids = palloc_get_page (PAL_ZERO); + if (table->ids == NULL) + return false; + table->id_cap = PGSIZE / sizeof (table->ids[0]); + table->id_free = 0; + table->id_max = -1; + return true; +} + +/* frees the content of mmap table */ +void +mmap_table_free (struct mmap_table *table) +{ + //TODO + palloc_free_page (table->ids); +} + +/* inserts a new mmap entry in the mmap table + returns -1 if entry is invalid or already in the table */ +mapid_t +mmap_table_insert (struct mmap_table *table, void *addr, struct file *file, + off_t len) +{ + off_t ofs = 0; + size_t page_read_bytes; + struct mmap_table_entry *mme; + mapid_t mapid; + + /* no more entries left */ + if (table->id_free >= table->id_cap) + return -1; + + mme = malloc (sizeof *mme); + if (mme == NULL) + return -1; + + mapid = table->id_free++; + mme->addr = addr; + mme->file = file; + table->ids[mapid] = mme; + + /* create needed pages in page table */ + while (len > 0) + { + page_read_bytes = (len < PGSIZE) ? len : PGSIZE; + + if (!page_table_insert_segment (&thread_current ()->page_table, file, ofs, + addr, page_read_bytes, true)) + return -1; + + /* Advance. */ + len -= PGSIZE; + ofs += page_read_bytes; + addr += PGSIZE; + } + + /* update index of free/max mapid index */ + if (mapid > table->id_max) + table->id_max = mapid; + while (table->ids[table->id_free] != NULL) + { + table->id_free++; + if (table->id_free >= table->id_cap) + break; + } + + return mapid; +} + +static struct mmap_table_entry * +mmap_table_get (struct mmap_table *table, mapid_t mapid) +{ + if (mapid < 0 || mapid >= table->id_cap || !table->ids[mapid]) + return NULL; + return table->ids[mapid]; +} + +bool +mmap_table_remove (struct mmap_table *table, mapid_t mapid) +{ + struct mmap_table_entry *mme = mmap_table_get(table, mapid); + if (mme == NULL) + return false; + + table->ids[mapid] = NULL; + + //TODO + + /* update index of free/max file descriptor index */ + if (mapid < table->id_free) + table->id_free = mapid; + while (table->ids[table->id_max] == NULL) + { + table->id_max--; + if (table->id_max < 0) + break; + } + + return true; +} diff --git a/vm/mmap.h b/vm/mmap.h new file mode 100644 index 0000000..bee364e --- /dev/null +++ b/vm/mmap.h @@ -0,0 +1,31 @@ +#ifndef VM_MMAP_H +#define VM_MMAP_H + +#include +#include "filesys/off_t.h" +#include "lib/user/syscall.h" + +/* we use the same structure as fd_table thus the capacity is fixed to + 1024 (PGSIZE/4) */ +struct mmap_table +{ + struct mmap_table_entry **ids; + int id_free; /* lowest-index free */ + int id_max; /* highest-index used */ + int id_cap; /* mmap table capacity */ +}; + +/* a single entry in the mmap table */ +struct mmap_table_entry +{ + void *addr; + struct file *file; +}; + +bool mmap_table_init (struct mmap_table *table); +void mmap_table_free (struct mmap_table *table); +mapid_t mmap_table_insert (struct mmap_table *table, void *addr, + struct file *file, off_t len); +bool mmap_table_remove (struct mmap_table *table, mapid_t mapping); + +#endif diff --git a/vm/page.c b/vm/page.c index 336353e..2853497 100644 --- a/vm/page.c +++ b/vm/page.c @@ -2,6 +2,7 @@ #include "filesys/file.h" #include "userprog/process.h" #include "threads/thread.h" +#include "threads/vaddr.h" #include "threads/malloc.h" #include "threads/palloc.h" #include "vm/page.h" @@ -11,8 +12,6 @@ static bool page_table_cmp_less (const struct hash_elem *a, const struct hash_el void *aux UNUSED); static void page_table_entry_free (struct hash_elem *e, void *aux UNUSED); static bool page_table_insert (struct hash *ht, struct page_table_entry *pte); -static bool page_load_segment (struct page_table_entry *pte); -static bool page_load_mmf (struct page_table_entry *pte); /* initialize page table structure */ void @@ -69,23 +68,20 @@ page_table_insert (struct hash *ht, struct page_table_entry *pte) /* inserts a new entry of type segment into the page table returns false if entry is invalid or already in the table */ bool -page_table_insert_segment (struct file *file, off_t ofs, uint8_t *upage, - uint32_t read_bytes, uint32_t zero_bytes, bool writable) +page_table_insert_segment (struct hash *ht, struct file *file, off_t ofs, + uint8_t *upage, uint32_t read_bytes, bool writable) { struct page_table_entry *pte = malloc (sizeof *pte); if (pte == NULL) return false; pte->upage = upage; - pte->type = PAGE_SEGMENT; pte->loaded = false; pte->segment.file = file; pte->segment.ofs = ofs; pte->segment.read_bytes = read_bytes; - pte->segment.zero_bytes = zero_bytes; pte->segment.writable = writable; - - return page_table_insert (&thread_current ()->page_table, pte); + return page_table_insert (ht, pte); } /* fetch an entry from the page table. returns NULL if not found */ @@ -105,28 +101,11 @@ page_table_fetch (struct hash *ht, void *upage) bool page_load (struct page_table_entry *pte) { + struct segment *data; if (pte->loaded) return true; - switch (pte->type) - { - case PAGE_SEGMENT: - return page_load_segment (pte); - case PAGE_MEMORY_MAPPED_FILE: - return page_load_mmf (pte); - default: - ASSERT (false); - break; - } - return false; -} - -/* load the segment data page */ -static bool -page_load_segment (struct page_table_entry *pte) -{ - struct segment *data = &pte->segment; - + data = &pte->segment; file_seek (data->file, data->ofs); /* Get a page of memory. */ @@ -141,7 +120,7 @@ page_load_segment (struct page_table_entry *pte) palloc_free_page (kpage); return false; } - memset (kpage + data->read_bytes, 0, data->zero_bytes); + memset (kpage + data->read_bytes, 0, PGSIZE - data->read_bytes); /* Add the page to the process's address space. */ if (!process_install_page (pte->upage, kpage, data->writable)) @@ -153,11 +132,3 @@ page_load_segment (struct page_table_entry *pte) pte->loaded = true; return true; } - -/* load the memory mappged file page */ -static bool -page_load_mmf (struct page_table_entry *pte) -{ - //TODO: implement - return false; -} diff --git a/vm/page.h b/vm/page.h index 9cea048..da1398c 100644 --- a/vm/page.h +++ b/vm/page.h @@ -10,24 +10,15 @@ struct page_table_entry { void *upage; /* virtual address of page */ bool loaded; /* indicates if page is loaded */ - enum - { - PAGE_SEGMENT, - PAGE_MEMORY_MAPPED_FILE, - } type; /* type of page */ - union + /* structure needed for lazy loading of data segments and mmapped files */ + struct segment { - /* structure needed for lazy loading of data segments */ - struct segment - { - struct file *file; - off_t ofs; - uint32_t read_bytes; - uint32_t zero_bytes; - bool writable; - } segment; - }; + struct file *file; + off_t ofs; + uint32_t read_bytes; + bool writable; + } segment; struct hash_elem elem; /* Hash element. */ }; @@ -35,8 +26,8 @@ struct page_table_entry void page_table_init (struct hash *ht); void page_table_free (struct hash *ht); struct page_table_entry *page_table_fetch (struct hash *ht, void *upage); -bool page_table_insert_segment (struct file *file, off_t ofs, uint8_t *upage, - uint32_t read_bytes, uint32_t zero_bytes, bool writable); +bool page_table_insert_segment (struct hash *ht, struct file *file, off_t ofs, + uint8_t *upage, uint32_t read_bytes, bool writable); bool page_load (struct page_table_entry *pte); #endif -- cgit v1.2.3