fix: clear shared pages to avoid freeing them

This commit is contained in:
2024-12-04 11:03:35 +00:00
parent 6218b5e80d
commit 10bfffc4b0
3 changed files with 48 additions and 26 deletions

View File

@@ -187,15 +187,11 @@ try_fetch_page (void *upage, bool write)
bool success = false; bool success = false;
switch (page->type) { switch (page->type) {
case PAGE_EXECUTABLE: case PAGE_EXECUTABLE:
success = page_load (page, write); success = page_load (page);
break; break;
default: default:
return false; return false;
} }
if (success && write &&
!pagedir_is_writable(thread_current()->pagedir, upage))
pagedir_set_writable(thread_current()->pagedir, upage, true);
return success; return success;
} }

View File

@@ -5,6 +5,7 @@
#include "threads/malloc.h" #include "threads/malloc.h"
#include "threads/palloc.h" #include "threads/palloc.h"
#include "threads/vaddr.h" #include "threads/vaddr.h"
#include "userprog/pagedir.h"
#include "userprog/process.h" #include "userprog/process.h"
#include "vm/frame.h" #include "vm/frame.h"
@@ -41,18 +42,28 @@ struct page_entry *
page_insert (struct file *file, off_t ofs, void *upage, uint32_t read_bytes, page_insert (struct file *file, off_t ofs, void *upage, uint32_t read_bytes,
uint32_t zero_bytes, bool writable, enum page_type type) uint32_t zero_bytes, bool writable, enum page_type type)
{ {
/* If page exists, just update it. */
struct page_entry *existing = page_get (upage);
if (existing != NULL)
{
existing->read_bytes = read_bytes;
existing->zero_bytes = zero_bytes;
existing->writable = writable;
return existing;
}
/* Otherwise allocate a new one. */
struct page_entry *page = malloc(sizeof (struct page_entry)); struct page_entry *page = malloc(sizeof (struct page_entry));
if (page == NULL) if (page == NULL)
return NULL; return NULL;
page->file = file; page->file = file;
page->offset = ofs; page->offset = ofs;
page->upage = upage; page->upage = upage;
page->read_bytes = read_bytes; page->read_bytes = read_bytes;
page->zero_bytes = zero_bytes; page->zero_bytes = zero_bytes;
page->writable = writable; page->writable = writable;
page->shared = false;
page->type = type; page->type = type;
hash_insert (&thread_current ()->pages, &page->elem); hash_insert (&thread_current ()->pages, &page->elem);
return page; return page;
} }
@@ -75,20 +86,23 @@ page_get (void *upage)
} }
bool bool
page_load (struct page_entry *page, bool writable) page_load (struct page_entry *page)
{ {
/* If the page is read-only, we want to check if it is a shared page already /* If the page is read-only, we want to check if it is a shared page already
loaded into memory. If it is, we can just map the page to the frame. */ loaded into memory. If it is, we can just map the page to the frame. */
if (!page->writable && !writable) if (!page->writable)
{ {
lock_acquire (&shared_files_lock);
struct shared_page_entry *shared_page = struct shared_page_entry *shared_page =
shared_page_get (page->file, page->upage); shared_page_get (page->file, page->upage);
/* Mark page as shared and install the shared frame. */
if (shared_page != NULL) if (shared_page != NULL)
{ {
/* Map the page to the shared frame for this read-only portion. */ lock_release (&shared_files_lock);
if (!install_page (page->upage, shared_page->frame, false)) if (!install_page (page->upage, shared_page->frame, false))
return false; return false;
page->shared = true;
return true; return true;
} }
} }
@@ -100,22 +114,20 @@ page_load (struct page_entry *page, bool writable)
if (frame == NULL) if (frame == NULL)
PANIC ("Could not allocate a frame to load page into memory."); PANIC ("Could not allocate a frame to load page into memory.");
/* Ensure page is not marked as shared while it doesn't exist in the
shared_files table, to avoid memory leaks. */
page->shared = false;
/* Map the page to the frame. */ /* Map the page to the frame. */
if (!install_page (page->upage, frame, page->writable)) if (!install_page (page->upage, frame, page->writable))
{ goto fail;
frame_free (frame);
return false;
}
/* Move the file pointer to the correct location in the file. Then, read the /* Move the file pointer to the correct location in the file. Then, read the
data from the file into the frame. Checks that we were able to read the data from the file into the frame. Checks that we were able to read the
expected number of bytes. */ expected number of bytes. */
file_seek (page->file, page->offset); file_seek (page->file, page->offset);
if (file_read (page->file, frame, page->read_bytes) != (int) page->read_bytes) if (file_read (page->file, frame, page->read_bytes) != (int) page->read_bytes)
{ goto fail;
frame_free (frame);
return false;
}
/* Zero out the remaining bytes in the frame. */ /* Zero out the remaining bytes in the frame. */
memset (frame + page->read_bytes, 0, page->zero_bytes); memset (frame + page->read_bytes, 0, page->zero_bytes);
@@ -123,15 +135,22 @@ page_load (struct page_entry *page, bool writable)
/* If the page is read-only, we need to add it to the shared pages table. */ /* If the page is read-only, we need to add it to the shared pages table. */
if (!page->writable) if (!page->writable)
{ {
if (shared_page_insert (page->file, page->upage, frame) == NULL) struct shared_page_entry *shared_page
{ = shared_page_insert (page->file, page->upage, frame);
frame_free (frame); if (shared_page == NULL)
return false; goto fail;
} lock_release (&shared_files_lock);
page->shared = true;
} }
/* Mark the page as loaded successfully. */ /* Mark the page as loaded successfully. */
return true; return true;
fail:
if (!page->writable)
lock_release (&shared_files_lock);
frame_free (frame);
return false;
} }
/* Function to clean up a page_entry. Given the elem of that page_entry, frees /* Function to clean up a page_entry. Given the elem of that page_entry, frees
@@ -139,7 +158,13 @@ page_load (struct page_entry *page, bool writable)
void void
page_cleanup (struct hash_elem *e, void *aux UNUSED) page_cleanup (struct hash_elem *e, void *aux UNUSED)
{ {
free (hash_entry (e, struct page_entry, elem)); struct page_entry *page = hash_entry (e, struct page_entry, elem);
/* If page is shared then mark it as not present to avoid it being freed. */
uint32_t *pd = thread_current ()->pagedir;
if (pd != NULL && page->shared)
pagedir_clear_page (pd, page->upage);
free (page);
} }
/* Hashing function needed for the shared_file table. Returns a hash for an /* Hashing function needed for the shared_file table. Returns a hash for an

View File

@@ -23,6 +23,7 @@ struct page_entry {
uint32_t read_bytes; /* Number of bytes to read within the page. */ uint32_t read_bytes; /* Number of bytes to read within the page. */
uint32_t zero_bytes; /* Number of bytes to zero within the page. */ uint32_t zero_bytes; /* Number of bytes to zero within the page. */
bool writable; /* Flag for whether this page is writable or not. */ bool writable; /* Flag for whether this page is writable or not. */
bool shared; /* Flag for whether this page is shared or not. */
struct hash_elem elem; /* An elem for the hash table. */ struct hash_elem elem; /* An elem for the hash table. */
}; };
@@ -49,7 +50,7 @@ struct page_entry *page_insert (struct file *file, off_t ofs, void *upage,
uint32_t read_bytes, uint32_t zero_bytes, uint32_t read_bytes, uint32_t zero_bytes,
bool writable, enum page_type type); bool writable, enum page_type type);
struct page_entry *page_get (void *upage); struct page_entry *page_get (void *upage);
bool page_load (struct page_entry *page, bool writable); bool page_load (struct page_entry *page);
void page_cleanup (struct hash_elem *e, void *aux); void page_cleanup (struct hash_elem *e, void *aux);
unsigned shared_file_hash (const struct hash_elem *e, void *aux); unsigned shared_file_hash (const struct hash_elem *e, void *aux);