feat: implement frame table without thread safety

This commit is contained in:
Themis Demetriades
2024-11-26 15:17:11 +00:00
parent 605050e38d
commit ea2725f606
5 changed files with 189 additions and 5 deletions

View File

@@ -62,6 +62,7 @@ userprog_SRC += userprog/gdt.c # GDT initialization.
userprog_SRC += userprog/tss.c # TSS management. userprog_SRC += userprog/tss.c # TSS management.
# Virtual memory code. # Virtual memory code.
vm_SRC += vm/frame.c # Frame table manager.
vm_SRC += devices/swap.c # Swap block manager. vm_SRC += devices/swap.c # Swap block manager.
#vm_SRC = vm/file.c # Some other file. #vm_SRC = vm/file.c # Some other file.

View File

@@ -32,6 +32,7 @@
#include "tests/threads/tests.h" #include "tests/threads/tests.h"
#endif #endif
#ifdef VM #ifdef VM
#include "vm/frame.h"
#include "devices/swap.h" #include "devices/swap.h"
#endif #endif
#ifdef FILESYS #ifdef FILESYS
@@ -101,6 +102,9 @@ main (void)
palloc_init (user_page_limit); palloc_init (user_page_limit);
malloc_init (); malloc_init ();
paging_init (); paging_init ();
#ifdef VM
frame_init ();
#endif
/* Segmentation. */ /* Segmentation. */
#ifdef USERPROG #ifdef USERPROG

View File

@@ -24,6 +24,9 @@
#include "threads/vaddr.h" #include "threads/vaddr.h"
#include "threads/synch.h" #include "threads/synch.h"
#include "devices/timer.h" #include "devices/timer.h"
#ifdef VM
#include "vm/frame.h"
#endif
/* Defines the native number of bytes processed by the processor /* Defines the native number of bytes processed by the processor
(for the purposes of alignment). */ (for the purposes of alignment). */
@@ -113,7 +116,10 @@ process_execute (const char *cmd)
return tid; return tid;
} }
static void *get_usr_kpage (enum palloc_flags flags);
static void free_usr_kpage (void *kpage);
static bool install_page (void *upage, void *kpage, bool writable); static bool install_page (void *upage, void *kpage, bool writable);
static bool process_init_stack (char *cmd_saveptr, void **esp, char *file_name); 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); static void *push_to_stack (void **esp, void *data, size_t data_size);
#define push_var_to_stack(esp, var) (push_to_stack (esp, &var, sizeof (var))) #define push_var_to_stack(esp, var) (push_to_stack (esp, &var, sizeof (var)))
@@ -253,7 +259,7 @@ process_init_stack (char *cmd_saveptr, void **esp, char *file_name)
/* Allocate the pages and map them to the user process. */ /* Allocate the pages and map them to the user process. */
for (int i = 1; i < pages_needed + 1; i++) for (int i = 1; i < pages_needed + 1; i++)
{ {
uint8_t *kpage = palloc_get_page (PAL_USER | PAL_ZERO); uint8_t *kpage = get_usr_kpage (PAL_ZERO);
if (!install_page (((uint8_t *) PHYS_BASE) - PGSIZE * (i + 1), if (!install_page (((uint8_t *) PHYS_BASE) - PGSIZE * (i + 1),
kpage, true)) kpage, true))
return false; return false;
@@ -704,7 +710,7 @@ load_segment (struct file *file, off_t ofs, uint8_t *upage,
if (kpage == NULL){ if (kpage == NULL){
/* Get a new page of memory. */ /* Get a new page of memory. */
kpage = palloc_get_page (PAL_USER); kpage = get_usr_kpage (0);
if (kpage == NULL){ if (kpage == NULL){
return false; return false;
} }
@@ -712,7 +718,7 @@ load_segment (struct file *file, off_t ofs, uint8_t *upage,
/* Add the page to the process's address space. */ /* Add the page to the process's address space. */
if (!install_page (upage, kpage, writable)) if (!install_page (upage, kpage, writable))
{ {
palloc_free_page (kpage); free_usr_kpage (kpage);
return false; return false;
} }
@@ -747,18 +753,44 @@ setup_stack (void **esp)
uint8_t *kpage; uint8_t *kpage;
bool success = false; bool success = false;
kpage = palloc_get_page (PAL_USER | PAL_ZERO); kpage = get_usr_kpage (PAL_ZERO);
if (kpage != NULL) if (kpage != NULL)
{ {
success = install_page (((uint8_t *) PHYS_BASE) - PGSIZE, kpage, true); success = install_page (((uint8_t *) PHYS_BASE) - PGSIZE, kpage, true);
if (success) if (success)
*esp = PHYS_BASE; *esp = PHYS_BASE;
else else
palloc_free_page (kpage); free_usr_kpage (kpage);
} }
return success; return success;
} }
/* Claims a page from the user pool and returns its kernel address,
updating the frame table if VM is enabled. */
static void *
get_usr_kpage (enum palloc_flags flags)
{
void *page;
#ifdef VM
page = frame_alloc (flags);
#else
page = palloc_get_page (flags | PAL_USER);
#endif
return page;
}
/* Frees a page belonging to a user process given its kernel address,
updating the frame table if VM is enabled. */
static void
free_usr_kpage (void *kpage)
{
#ifdef VM
frame_free (kpage);
#else
palloc_free_page (kpage);
#endif
}
/* Adds a mapping from user virtual address UPAGE to kernel /* Adds a mapping from user virtual address UPAGE to kernel
virtual address KPAGE to the page table. virtual address KPAGE to the page table.
If WRITABLE is true, the user process may modify the page; If WRITABLE is true, the user process may modify the page;

137
src/vm/frame.c Normal file
View File

@@ -0,0 +1,137 @@
#include <debug.h>
#include <hash.h>
#include <list.h>
#include "frame.h"
#include "threads/malloc.h"
#include "threads/synch.h"
/* Hash table that maps every active frame's kernel virtual address
to its corresponding 'frame_metadata'.*/
struct hash frame_table;
/* Linked list of frame_metadata whose pages are predicted to currently
be in the working set of a process. They are not considered for
eviction, but are considered for demotion to the 'inactive' list. */
struct list active_list;
/* Linked list of frame_metadata whose pages are predicted to leave the
working set of their processes soon, so are considered for eviction.
Pages are considered for eviction from the tail end, and are initially
demoted to 'inactive' at the head. */
struct list inactive_list;
/* Synchronisation variables. */
/* Ensures mutual exclusion to accessing the 'head' and first element of
'inactive_list', which is accessed every time a frame is allocated. */
struct lock inactive_head_lock;
struct frame_metadata
{
void *frame; /* The kernel virtual address holding the frame. */
struct hash_elem hash_elem; /* Tracks the position of the frame metadata
within 'frame_table', whose key is the
kernel virtual address of the frame. */
struct list_elem list_elem; /* Tracks the position of the frame metadata
in either the 'active' or 'inactive' list,
so a victim can be chosen for eviction. */
};
hash_hash_func frame_metadata_hash;
hash_less_func frame_metadata_less;
/* Initialize the frame system by initializing the frame (hash) table with
the frame_metadata hashing and comparison functions, as well as initializing
the active & inactive lists. Also initializes the system's synchronisation
primitives. */
void
frame_init (void)
{
hash_init (&frame_table, frame_metadata_hash, frame_metadata_less, NULL);
list_init (&active_list);
list_init (&inactive_list);
lock_init (&inactive_head_lock);
}
/* Attempt to allocate a frame for a user process, either by direct
allocation of a user page if there is sufficient RAM, or by
evicting a currently active page if memory allocated for user
processes is fulled and storing it in swap. If swap is full in
the former case, panic the kernel. */
void *
frame_alloc (enum palloc_flags flags)
{
flags |= PAL_USER;
void *frame = palloc_get_page (flags);
if (frame == NULL)
{
/* TODO: Find victim page to replace, and swap it with this new page. */
return NULL;
}
struct frame_metadata *frame_metadata =
malloc (sizeof (struct frame_metadata));
frame_metadata->frame = frame;
/* Newly faulted pages begin at the head of the inactive list. */
lock_acquire (&inactive_head_lock);
list_push_front (&inactive_list, &frame_metadata->list_elem);
lock_release (&inactive_head_lock);
/* Finally, insert frame metadata within the frame table, with the key as its
allocated kernel address. */
hash_replace (&frame_table, &frame_metadata->hash_elem);
return frame;
}
/* Attempt to deallocate a frame for a user process by removing it from the
frame table as well as active/inactive list, and freeing the underlying
page memory. Panics if the frame isn't active in memory. */
void
frame_free (void *frame)
{
struct frame_metadata key_metadata;
key_metadata.frame = frame;
struct hash_elem *e =
hash_delete (&frame_table, &key_metadata.hash_elem);
if (e == NULL) PANIC ("Attempted to free a frame without a corresponding "
"kernel address!\n");
struct frame_metadata *frame_metadata =
hash_entry (e, struct frame_metadata, hash_elem);
list_remove (&frame_metadata->list_elem);
free (frame_metadata);
palloc_free_page (frame);
}
/* Hash function for frame metadata, used for storing entries in the
frame table. */
unsigned
frame_metadata_hash (const struct hash_elem *e, void *aux UNUSED)
{
struct frame_metadata *frame_metadata =
hash_entry (e, struct frame_metadata, hash_elem);
return hash_bytes (&frame_metadata->frame, sizeof (frame_metadata->frame));
}
/* 'less_func' comparison function for frame metadata, used for comparing
the keys of the frame table. Returns true iff the kernel virtual address
of the first frame is less than that of the second frame. */
bool
frame_metadata_less (const struct hash_elem *a_, const struct hash_elem *b_,
void *aux UNUSED)
{
struct frame_metadata *a =
hash_entry (a_, struct frame_metadata, hash_elem);
struct frame_metadata *b =
hash_entry (b_, struct frame_metadata, hash_elem);
return a->frame < b->frame;
}

10
src/vm/frame.h Normal file
View File

@@ -0,0 +1,10 @@
#ifndef VM_FRAME_H
#define VM_FRAME_H
#include "threads/palloc.h"
void frame_init (void);
void *frame_alloc (enum palloc_flags);
void frame_free (void *frame);
#endif /* vm/frame.h */