diff --git a/src/threads/thread.c b/src/threads/thread.c index 91a12b5..c2944cc 100644 --- a/src/threads/thread.c +++ b/src/threads/thread.c @@ -15,6 +15,7 @@ #include "threads/switch.h" #include "threads/synch.h" #include "threads/vaddr.h" +#include "vm/page.h" #ifdef USERPROG #include "userprog/process.h" #include "userprog/syscall.h" @@ -264,7 +265,8 @@ thread_create (const char *name, int priority, if (!hash_init (&t->open_files, fd_hash, fd_less, NULL) || !hash_init (&t->child_results, process_result_hash, - process_result_less, t)) + process_result_less, t) + || !hash_init (&t->pages, page_hash, page_less, NULL)) { palloc_free_page (t); free (t->result); diff --git a/src/userprog/exception.c b/src/userprog/exception.c index 7c4f2ad..ed6a6ba 100644 --- a/src/userprog/exception.c +++ b/src/userprog/exception.c @@ -5,13 +5,13 @@ #include "userprog/gdt.h" #include "threads/interrupt.h" #include "threads/thread.h" -#include "userprog/pagedir.h" #ifdef VM #include "vm/stackgrowth.h" #include "vm/frame.h" #include "vm/page.h" #include "devices/swap.h" #include "threads/vaddr.h" +#include "userprog/pagedir.h" #endif /* Number of page faults processed. */ @@ -19,6 +19,7 @@ static long long page_fault_cnt; static void kill (struct intr_frame *); static void page_fault (struct intr_frame *); +bool try_fetch_page (void *upage, bool write); /* Registers handlers for interrupts that can be caused by user programs. @@ -156,6 +157,7 @@ page_fault (struct intr_frame *f) #ifdef VM struct thread *t = thread_current (); + void *upage = pg_round_down (fault_addr); if (user) { if (not_present) @@ -167,7 +169,6 @@ page_fault (struct intr_frame *f) if (page_in_swap (t, fault_addr)) { size_t swap_slot = page_get_swap (t, fault_addr); - void *upage = pg_round_down (fault_addr); void *kpage = frame_alloc (0, upage, t); swap_in (kpage, swap_slot); @@ -192,6 +193,15 @@ page_fault (struct intr_frame *f) f->eax = 0xffffffff; return; } + + /* If the fault address is in a user page that is not present, then it might + just need to be lazily loaded. So, we check our SPT to see if the page + is expected to have data loaded in memory. */ + if (not_present && is_user_vaddr (upage) && upage != NULL) + { + if (try_fetch_page (upage, write)) + return; + } #endif /* To implement virtual memory, delete the rest of the function @@ -205,3 +215,32 @@ page_fault (struct intr_frame *f) kill (f); } +bool +try_fetch_page (void *upage, bool write) +{ + /* Check if the page is in the supplemental page table. That is, it is a page + that is expected to be in memory. */ + struct page_entry *page = page_get (upage); + if (page == NULL) + return false; + + /* An attempt to write to a non-writeable should fail. */ + if (write && !page->writable) + return false; + + /* Load the page into memory based on the type of data it is expecting. */ + bool success = false; + switch (page->type) { + case PAGE_EXECUTABLE: + success = page_load (page, page->writable); + break; + default: + return false; + } + + if (success && page->writable && + !pagedir_is_writable(thread_current()->pagedir, upage)) + pagedir_set_writable(thread_current()->pagedir, upage, true); + + return success; +} diff --git a/src/userprog/exception.h b/src/userprog/exception.h index f83e615..663db4b 100644 --- a/src/userprog/exception.h +++ b/src/userprog/exception.h @@ -1,6 +1,8 @@ #ifndef USERPROG_EXCEPTION_H #define USERPROG_EXCEPTION_H +#include + /* Page fault error code bits that describe the cause of the exception. */ #define PF_P 0x1 /* 0: not-present page. 1: access rights violation. */ #define PF_W 0x2 /* 0: read, 1: write. */ @@ -8,5 +10,7 @@ void exception_init (void); void exception_print_stats (void); +bool +try_fetch_page (void *upage, bool write); #endif /* userprog/exception.h */ diff --git a/src/userprog/process.c b/src/userprog/process.c index 85ebee5..f4a7439 100644 --- a/src/userprog/process.c +++ b/src/userprog/process.c @@ -24,6 +24,7 @@ #include "threads/vaddr.h" #include "threads/synch.h" #include "devices/timer.h" +#include "vm/page.h" #ifdef VM #include "vm/frame.h" #endif @@ -118,6 +119,7 @@ process_execute (const char *cmd) static void *get_usr_kpage (enum palloc_flags flags, void *upage); static void free_usr_kpage (void *kpage); +bool install_page (void *upage, void *kpage, bool writable); static bool process_init_stack (char *cmd_saveptr, void **esp, char *file_name); static void *push_to_stack (void **esp, void *data, size_t data_size); @@ -363,6 +365,7 @@ process_exit (void) /* Clean up all open files */ hash_destroy (&cur->open_files, fd_cleanup); + hash_destroy (&cur->pages, page_cleanup); /* Close the executable file, implicitly allowing it to be written to. */ if (cur->exec_file != NULL) @@ -620,7 +623,6 @@ load (const char *file_name, void (**eip) (void), void **esp) done: /* We arrive here whether the load is successful or not. */ - file_close (file); lock_release (&filesys_lock); return success; } @@ -688,58 +690,29 @@ validate_segment (const struct Elf32_Phdr *phdr, struct file *file) or disk read error occurs. */ static bool load_segment (struct file *file, off_t ofs, uint8_t *upage, - uint32_t read_bytes, uint32_t zero_bytes, bool writable) + uint32_t read_bytes, uint32_t zero_bytes, bool writable) { ASSERT ((read_bytes + zero_bytes) % PGSIZE == 0); ASSERT (pg_ofs (upage) == 0); ASSERT (ofs % PGSIZE == 0); - file_seek (file, ofs); - while (read_bytes > 0 || zero_bytes > 0) + while (read_bytes > 0 || zero_bytes > 0) { /* Calculate how to fill this page. We will read PAGE_READ_BYTES bytes from FILE and zero the final PAGE_ZERO_BYTES bytes. */ size_t page_read_bytes = read_bytes < PGSIZE ? read_bytes : PGSIZE; size_t page_zero_bytes = PGSIZE - page_read_bytes; - - /* Check if virtual page already allocated */ - struct thread *t = thread_current (); - uint8_t *kpage = pagedir_get_page (t->pagedir, upage); - - if (kpage == NULL){ - - /* Get a new page of memory. */ - kpage = get_usr_kpage (0, upage); - if (kpage == NULL){ - return false; - } - - /* Add the page to the process's address space. */ - if (!install_page (upage, kpage, writable)) - { - free_usr_kpage (kpage); - return false; - } - - } else { - - /* Check if writable flag for the page should be updated */ - if(writable && !pagedir_is_writable(t->pagedir, upage)){ - pagedir_set_writable(t->pagedir, upage, writable); - } - - } - /* Load data into the page. */ - if (file_read (file, kpage, page_read_bytes) != (int) page_read_bytes){ - return false; - } - memset (kpage + page_read_bytes, 0, page_zero_bytes); + /* Add the page metadata to the SPT to be lazy loaded later on */ + if (page_insert (file, ofs, upage, page_read_bytes, page_zero_bytes, + writable, PAGE_EXECUTABLE) == NULL) + return false; /* Advance. */ read_bytes -= page_read_bytes; zero_bytes -= page_zero_bytes; + ofs += PGSIZE; upage += PGSIZE; } return true; diff --git a/src/userprog/syscall.c b/src/userprog/syscall.c index 4bc34ca..2ccf3a2 100644 --- a/src/userprog/syscall.c +++ b/src/userprog/syscall.c @@ -536,4 +536,4 @@ put_user (uint8_t *udst, uint8_t byte) : "=&a"(error_code), "=m"(*udst) : "q"(byte)); return error_code != -1; -} \ No newline at end of file +} diff --git a/src/vm/frame.c b/src/vm/frame.c index 6d401b0..98339f8 100644 --- a/src/vm/frame.c +++ b/src/vm/frame.c @@ -260,4 +260,3 @@ lru_prev (struct list_elem *e) return list_prev (e); } - diff --git a/src/vm/page.c b/src/vm/page.c index 7133fca..79c176f 100644 --- a/src/vm/page.c +++ b/src/vm/page.c @@ -154,4 +154,3 @@ page_get_swap (struct thread *owner, void *upage) /* Masks the address bits and returns truncated value. */ return ((*pte & PTE_ADDR) >> ADDR_START_BIT); } -