From dd46200256225b1d1f9eabe7aff3b163bbb08c9a Mon Sep 17 00:00:00 2001 From: Gleb Koval Date: Thu, 5 Dec 2024 21:46:49 +0000 Subject: [PATCH] feat: initial shared file page management and initialization --- src/threads/init.c | 2 + src/userprog/exception.c | 1 + src/userprog/pagedir.c | 13 +++- src/vm/page.c | 157 ++++++++++++++++++++++++++++++++++++++- src/vm/page.h | 27 ++++++- 5 files changed, 192 insertions(+), 8 deletions(-) diff --git a/src/threads/init.c b/src/threads/init.c index 69ac6af..c507d53 100644 --- a/src/threads/init.c +++ b/src/threads/init.c @@ -33,6 +33,7 @@ #endif #ifdef VM #include "vm/frame.h" +#include "vm/page.h" #include "devices/swap.h" #endif #ifdef FILESYS @@ -104,6 +105,7 @@ main (void) paging_init (); #ifdef VM frame_init (); + shared_file_pages_init (); #endif /* Segmentation. */ diff --git a/src/userprog/exception.c b/src/userprog/exception.c index dc1d48a..ac7bde8 100644 --- a/src/userprog/exception.c +++ b/src/userprog/exception.c @@ -292,6 +292,7 @@ fetch_page (void *upage, bool write) switch (page->type) { case PAGE_MMAP: case PAGE_FILE: + case PAGE_SHARED: success = page_load_file (page); if (success && page->type == PAGE_FILE) { diff --git a/src/userprog/pagedir.c b/src/userprog/pagedir.c index 886bb25..98bfb17 100644 --- a/src/userprog/pagedir.c +++ b/src/userprog/pagedir.c @@ -2,9 +2,12 @@ #include #include #include +#include "devices/swap.h" #include "threads/init.h" #include "threads/pte.h" #include "threads/palloc.h" +#include "vm/frame.h" +#include "vm/page.h" static uint32_t *active_pd (void); @@ -39,8 +42,14 @@ pagedir_destroy (uint32_t *pd) uint32_t *pte; for (pte = pt; pte < pt + PGSIZE / sizeof *pte; pte++) - if (*pte & PTE_P) - palloc_free_page (pte_get_page (*pte)); + { + if (page_is_shared_pte (pte)) + continue; + if (page_in_swap_pte (pte)) + swap_drop (page_get_swap_pte (pte)); + if (*pte & PTE_P) + frame_free (pte_get_page (*pte)); + } palloc_free_page (pt); } palloc_free_page (pd); diff --git a/src/vm/page.c b/src/vm/page.c index a21b9e8..f415e54 100644 --- a/src/vm/page.c +++ b/src/vm/page.c @@ -6,18 +6,32 @@ #include "threads/pte.h" #include "threads/malloc.h" #include "threads/palloc.h" +#include "threads/synch.h" #include "devices/swap.h" #include "userprog/process.h" #include "userprog/pagedir.h" #include "vm/frame.h" #define SWAP_FLAG_BIT 9 +#define SHARED_FLAG_BIT 10 #define ADDR_START_BIT 12 +struct hash shared_file_pages; +struct lock shared_file_pages_lock; + static unsigned page_hash (const struct hash_elem *e, void *aux UNUSED); static bool page_less (const struct hash_elem *a_, const struct hash_elem *b_, void *aux UNUSED); +static void page_flag_shared (struct thread *owner, void *upage, bool shared); +static unsigned shared_file_page_hash (const struct hash_elem *e, + void *aux UNUSED); +static bool shared_file_page_less (const struct hash_elem *a_, + const struct hash_elem *b_, + void *aux UNUSED); +static struct shared_file_page *shared_file_page_get (struct file *file, + void *upage); + /* Initialise a supplementary page table. */ bool init_pages (struct hash *pages) @@ -144,6 +158,44 @@ page_load_file (struct page_entry *page) frame_alloc should try to evict a page. If it is still NULL, the OS panics as this should not happen if eviction is working correctly. */ struct thread *t = thread_current (); + bool shareable = !page->writable && file_compare (page->file, t->exec_file); + if (shareable) + { + lock_acquire (&shared_file_pages_lock); + struct shared_file_page *sfp + = shared_file_page_get (page->file, page->upage); + if (sfp != NULL) + { + /* Frame exists, just install it. */ + if (sfp->frame != NULL) + if (!install_page (page->upage, sfp->frame, page->writable)) + { + lock_release (&shared_file_pages_lock); + return false; + } + /* Shared page is in swap. Load it. */ + else + { + void *frame = frame_alloc (PAL_USER, page->upage, t); + if (frame == NULL) + PANIC ( + "Could not allocate a frame to load page into memory."); + swap_in (frame, sfp->swap_slot); + + if (!install_page (page->upage, frame, false)) + { + frame_free (frame); + lock_release (&shared_file_pages_lock); + return false; + } + page_flag_shared (t, page->upage, true); + } + lock_release (&shared_file_pages_lock); + page->type = PAGE_SHARED; + return true; + } + } + void *frame = frame_alloc (PAL_USER, page->upage, t); pagedir_set_accessed (t->pagedir, page->upage, true); if (frame == NULL) @@ -152,6 +204,8 @@ page_load_file (struct page_entry *page) /* Map the page to the frame. */ if (!install_page (page->upage, frame, page->writable)) { + if (shareable) + lock_release (&shared_file_pages_lock); frame_free (frame); return false; } @@ -162,6 +216,8 @@ page_load_file (struct page_entry *page) file_seek (page->file, page->offset); if (file_read (page->file, frame, page->read_bytes) != (int) page->read_bytes) { + if (shareable) + lock_release (&shared_file_pages_lock); frame_free (frame); return false; } @@ -169,6 +225,27 @@ page_load_file (struct page_entry *page) /* Zero out the remaining bytes in the frame. */ memset (frame + page->read_bytes, 0, page->zero_bytes); + /* If file page is read-only, make it shared. */ + if (shareable) + { + struct shared_file_page *sfp = malloc (sizeof (struct shared_file_page)); + if (sfp == NULL) + { + lock_release (&shared_file_pages_lock); + frame_free (frame); + return false; + } + sfp->file = page->file; + sfp->upage = page->upage; + sfp->frame = frame; + sfp->swap_slot = 0; + sfp->ref_count = 1; + hash_insert (&shared_file_pages, &sfp->elem); + page_flag_shared (t, page->upage, true); + lock_release (&shared_file_pages_lock); + page->type = PAGE_SHARED; + } + /* Mark the page as loaded successfully. */ return true; } @@ -211,8 +288,14 @@ bool page_in_swap (struct thread *owner, void *upage) { uint32_t *pte = lookup_page (owner->pagedir, upage, false); - return pte != NULL && - (*pte & (1 << SWAP_FLAG_BIT)) != 0; + return page_in_swap_pte (pte); +} + +/* Returns true iff the page table entry is marked to be in the swap disk. */ +bool +page_in_swap_pte (uint32_t *pte) +{ + return pte != NULL && (*pte & (1 << SWAP_FLAG_BIT)) != 0; } /* Given that the page with user address 'upage' owned by 'owner' is flagged @@ -230,3 +313,73 @@ page_get_swap (struct thread *owner, void *upage) page_flag_swap (pte, false); return ((*pte & PTE_ADDR) >> ADDR_START_BIT); } + +/* Returns the swap slot stored in a PTE. */ +size_t +page_get_swap_pte (uint32_t *pte) +{ + ASSERT (pte != NULL); + ASSERT ((*pte & PTE_P) == 0); + return ((*pte & PTE_ADDR) >> ADDR_START_BIT); +} + +/* Flags the provided page table entry as representing a shared page. */ +static void +page_flag_shared (struct thread *owner, void *upage, bool shared) +{ + uint32_t *pte = lookup_page (owner->pagedir, upage, false); + ASSERT (pte != NULL); + + if (shared) + *pte |= (1 << SHARED_FLAG_BIT); + else + *pte &= ~(1 << SHARED_FLAG_BIT); +} + +/* Returns true iff the page with user address 'upage' owned by 'owner' + is flagged to be shared via the owner's page table. */ +bool +page_is_shared_pte (uint32_t *pte) +{ + return pte != NULL && (*pte & (1 << SHARED_FLAG_BIT)) != 0; +} + +/* Initializes the shared file pages hash table. */ +void +shared_file_pages_init () +{ + if (!hash_init (&shared_file_pages, shared_file_page_hash, + shared_file_page_less, NULL)) + PANIC ("Failed to initialize shared file pages hash table."); + lock_init (&shared_file_pages_lock); +} + +/* Hash function for shared file pages, used for storing entries in the + shared file pages table. */ +static unsigned +shared_file_page_hash (const struct hash_elem *e, void *aux UNUSED) +{ + struct shared_file_page *sfp = hash_entry (e, struct shared_file_page, elem); + void *bytes[2] = { sfp->upage, sfp->file }; + return hash_bytes (bytes, sizeof (bytes)); +} + +/* 'less_func' comparison function for shared file pages, used for comparing + the keys of the shared file pages table. */ +static bool +shared_file_page_less (const struct hash_elem *a_, const struct hash_elem *b_, + void *aux UNUSED) +{ + const struct shared_file_page *a + = hash_entry (a_, struct shared_file_page, elem); + const struct shared_file_page *b + = hash_entry (b_, struct shared_file_page, elem); + + return !file_compare (a->file, b->file) || a->upage < b->upage; +} + +static struct shared_file_page * +shared_file_page_get (struct file *file, void *upage) +{ + return NULL; +} diff --git a/src/vm/page.h b/src/vm/page.h index a96f89a..9e29e49 100644 --- a/src/vm/page.h +++ b/src/vm/page.h @@ -5,13 +5,16 @@ #include "threads/synch.h" #include "filesys/off_t.h" -enum page_type { +enum page_type +{ PAGE_FILE, PAGE_MMAP, - PAGE_EMPTY + PAGE_EMPTY, + PAGE_SHARED }; -struct page_entry { +struct page_entry +{ enum page_type type; /* Type of Data that should go into the page */ void *upage; /* Start Address of the User Page (Key of hash table). */ @@ -30,6 +33,17 @@ struct page_entry { struct hash_elem elem; /* An elem for the hash table. */ }; +struct shared_file_page +{ + struct file *file; + void *upage; + void *frame; + size_t swap_slot; + int ref_count; + + struct hash_elem elem; +}; + bool init_pages (struct hash *pages); struct page_entry *page_insert_swapped (void *upage, void* kpage, struct thread *owner); @@ -41,6 +55,11 @@ bool page_load_file (struct page_entry *page); void page_cleanup (struct hash_elem *e, void *aux); bool page_in_swap (struct thread *, void *); +bool page_in_swap_pte (uint32_t *pte); size_t page_get_swap (struct thread *owner, void *upage); +size_t page_get_swap_pte (uint32_t *pte); -#endif /* vm/frame.h */ +bool page_is_shared_pte (uint32_t *pte); +void shared_file_pages_init (); + +#endif