diff --git a/pintos/include/threads/thread.h b/pintos/include/threads/thread.h index d863fa6..5572589 100644 --- a/pintos/include/threads/thread.h +++ b/pintos/include/threads/thread.h @@ -99,6 +99,8 @@ struct thread { struct list donor_list; struct list_elem donor_elem; + uintptr_t user_rsp; + /* Shared between thread.c and synch.c. */ struct list_elem elem; /* List element. */ struct list_elem allelem; diff --git a/pintos/include/vm/vm.h b/pintos/include/vm/vm.h index 3d0da9e..b9ea8e2 100644 --- a/pintos/include/vm/vm.h +++ b/pintos/include/vm/vm.h @@ -64,10 +64,12 @@ struct page { }; }; -/* The representation of "frame" */ struct frame { void *kva; struct page *page; + + struct list_elem frame_elem; + struct thread *owner; }; /* The function table for page operations. diff --git a/pintos/userprog/syscall.c b/pintos/userprog/syscall.c index a6e7e15..e04983f 100644 --- a/pintos/userprog/syscall.c +++ b/pintos/userprog/syscall.c @@ -74,6 +74,7 @@ void syscall_init(void) { /* The main system call interface */ void syscall_handler(struct intr_frame* f) { thread_current()->tf = *f; + thread_current()->user_rsp = f->rsp; uint64_t arg1 = f->R.rdi, arg2 = f->R.rsi, arg3 = f->R.rdx, arg4 = f->R.r10, arg5 = f->R.r8; switch (f->R.rax) { case SYS_HALT: diff --git a/pintos/vm/anon.c b/pintos/vm/anon.c index 75a24c3..ac1d69a 100644 --- a/pintos/vm/anon.c +++ b/pintos/vm/anon.c @@ -5,6 +5,7 @@ /* DO NOT MODIFY BELOW LINE */ static struct disk *swap_disk; +static struct bitmap *swap_table; static bool anon_swap_in (struct page *page, void *kva); static bool anon_swap_out (struct page *page); static void anon_destroy (struct page *page); @@ -17,32 +18,66 @@ static const struct page_operations anon_ops = { .type = VM_ANON, }; -/* Initialize the data for anonymous pages */ + void vm_anon_init (void) { swap_disk = disk_get (1, 1); + if (swap_disk == NULL) { + return; + } + + swap_table = bitmap_create(disk_size(swap_disk)/8); } /* Initialize the file mapping */ bool anon_initializer (struct page *page, enum vm_type type, void *kva) { struct anon_page *anon_page; + page->operations = &anon_ops; - // anon_page = &page->anon; - // anon_page->swap_idx = BITMAP_ERROR; - return true; + anon_page = &page->anon; + anon_page->swap_idx = -1; + return true; } -/* Swap in the page by read contents from the swap disk. */ -static bool -anon_swap_in (struct page *page, void *kva) { +static bool anon_swap_in (struct page *page, void *kva) { + size_t disk_index; + ASSERT (page != NULL); ASSERT (page->frame != NULL); + + disk_index = page->anon.swap_idx; + if (disk_index == -1) { + return false; + } + + + for (int i = 0; i < 8; i++) { + disk_read(swap_disk, disk_index * 8 + i, page->frame->kva + DISK_SECTOR_SIZE * i); + } + + bitmap_set (swap_table, disk_index, false); + page->anon.swap_idx = -1; + return true; } -/* Swap out the page by writing contents to the swap disk. */ -static bool -anon_swap_out (struct page *page) { +static bool anon_swap_out (struct page *page) { + size_t disk_index; + ASSERT (page != NULL); + + disk_index = bitmap_scan_and_flip(swap_table, 0, 1, false); + if (disk_index == -1) { + return false; + } + + for (int i = 0; i < 8; i++) { + disk_write(swap_disk, disk_index * 8 + i, page->frame->kva + DISK_SECTOR_SIZE * i); + } + + page->anon.swap_idx = disk_index; + pml4_clear_page(thread_current()->pml4, page->va); + page->frame = NULL; + return true; } diff --git a/pintos/vm/file.c b/pintos/vm/file.c index 01d368e..7f82379 100644 --- a/pintos/vm/file.c +++ b/pintos/vm/file.c @@ -82,7 +82,20 @@ static bool file_backed_swap_out (struct page *page) { /* Destory the file backed page. PAGE will be freed by the caller. */ static void file_backed_destroy (struct page *page) { - file_backed_swap_out (page); + struct file_page *file_page = &page->file; + struct thread *t = thread_current(); + + if (page->frame) { + if (pml4_is_dirty(t->pml4, page->va)) { + lock_acquire(&file_lock); + file_write_at(file_page->file, page->frame->kva, file_page->read_bytes, file_page->ofs); + lock_release(&file_lock); + } + pml4_clear_page(t->pml4, page->va); + palloc_free_page(page->frame->kva); + free(page->frame); + page->frame = NULL; + } } void *do_mmap (void *addr, size_t length, int writable, struct file *file, off_t offset) { diff --git a/pintos/vm/vm.c b/pintos/vm/vm.c index c6c07b8..92fbe5e 100644 --- a/pintos/vm/vm.c +++ b/pintos/vm/vm.c @@ -12,10 +12,19 @@ #include "vm/file.h" /* Initializes the virtual memory subsystem by invoking each subsystem's * intialize codes. */ +struct list frame_table; +struct lock frame_lock; +struct list_elem *clock_elem; + void vm_init (void) { vm_anon_init (); vm_file_init (); + + list_init(&frame_table); + lock_init(&frame_lock); + clock_elem = NULL; + #ifdef EFILESYS /* For project 4 */ pagecache_init (); #endif @@ -133,22 +142,68 @@ bool spt_remove_page (struct supplemental_page_table *spt, struct page *page) { return false; } -/* Get the struct frame, that will be evicted. */ -static struct frame * -vm_get_victim (void) { - struct frame *victim = NULL; - /* TODO: The policy for eviction is up to you. */ +static struct frame * vm_get_victim (void) { + struct frame *victim = NULL; + struct list_elem *e = clock_elem; + struct frame *frame; + + if (list_empty(&frame_table)) { + return NULL; + } + + if (e == NULL || e == list_end(&frame_table)) { + e = list_begin(&frame_table); + } + + while (true) { + + if (e == list_end(&frame_table)) + e = list_begin(&frame_table); + + frame = list_entry(e, struct frame, frame_elem); + + if (frame->page == NULL) { + e = list_next(e); + continue; + } + + if (pml4_is_accessed(frame->owner->pml4, frame->page->va)) { + pml4_set_accessed(frame->owner->pml4, frame->page->va, false); + + } else { + victim = frame; + clock_elem = list_next(e); + + if (clock_elem == list_end(&frame_table)) + clock_elem = list_begin(&frame_table); + + break; + } + + e = list_next(e); + } - return victim; + return victim; } -/* Evict one page and return the corresponding frame. - * Return NULL on error.*/ -static struct frame * -vm_evict_frame (void) { - struct frame *victim UNUSED = vm_get_victim (); - /* TODO: swap out the victim and return the evicted frame. */ +static struct frame * vm_evict_frame (void) { + + lock_acquire(&frame_lock); + struct frame *victim = vm_get_victim (); + + + if (victim == NULL) { + lock_release(&frame_lock); + return NULL; + } + + if (swap_out(victim->page)) { + victim->page = NULL; + lock_release(&frame_lock); + return victim; + } + lock_release(&frame_lock); return NULL; } @@ -161,9 +216,10 @@ static struct frame * vm_get_frame (void) { void *kva; kva = palloc_get_page(PAL_USER); - if (kva == NULL) { - PANIC("todo"); - } + if (kva == NULL) { + frame = vm_evict_frame(); + return frame; + } frame = malloc(sizeof(struct frame)); if (frame == NULL) { @@ -174,6 +230,10 @@ static struct frame * vm_get_frame (void) { frame->kva = kva; frame->page = NULL; + lock_acquire(&frame_lock); + list_push_back(&frame_table, &frame->frame_elem); + lock_release(&frame_lock); + ASSERT (frame != NULL); ASSERT (frame->page == NULL); return frame; @@ -208,8 +268,18 @@ bool vm_try_handle_fault (struct intr_frame *f, void *addr, bool user, bool writ page_addr = pg_round_down (addr); page = spt_find_page (spt, page_addr); + if (page == NULL) { + + uintptr_t rsp; + + if (user) { + rsp = f->rsp; + } else { + rsp = thread_current()->user_rsp; + } + if (!should_grow_stack (f, addr, user) || !vm_stack_growth (page_addr)) return false; @@ -256,8 +326,14 @@ static bool vm_do_claim_page (struct page *page) { frame = vm_get_frame (); frame->page = page; page->frame = frame; + frame->owner = thread_current(); if (!pml4_set_page(thread_current()->pml4, page->va, frame->kva, page->writable)){ + + lock_acquire(&frame_lock); + list_remove(&frame->frame_elem); + lock_release(&frame_lock); + palloc_free_page(frame->kva); free(frame); page->frame = NULL; @@ -424,7 +500,7 @@ static bool should_grow_stack (struct intr_frame *f, void *addr, bool user) { uint8_t *rsp = NULL; uint8_t *fault_addr = addr; - rsp = user ? (uint8_t *) f->rsp : (uint8_t *) thread_current ()->tf.rsp; + rsp = user ? (uint8_t *) f->rsp : (uint8_t *) thread_current ()->user_rsp; if (fault_addr >= (uint8_t *) USER_STACK) return false;