Implement skeleton for swapping frames into disk

This commit is contained in:
Themis Demetriades
2024-11-26 18:59:46 +00:00
parent ea2725f606
commit 1e6b90da0d
6 changed files with 106 additions and 25 deletions

View File

@@ -1,10 +1,14 @@
#include <debug.h>
#include <hash.h>
#include <list.h>
#include <string.h>
#include "frame.h"
#include "page.h"
#include "threads/malloc.h"
#include "threads/vaddr.h"
#include "threads/synch.h"
#include "devices/swap.h"
/* Hash table that maps every active frame's kernel virtual address
to its corresponding 'frame_metadata'.*/
@@ -22,13 +26,14 @@ struct list active_list;
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;
/* Protects access to the 'inactive' list. */
struct lock inactive_lock;
struct frame_metadata
{
void *frame; /* The kernel virtual address holding the frame. */
void *upage; /* The user virtual address pointing to the frame. */
struct thread *owner; /* Pointer to the thread that owns 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. */
@@ -40,6 +45,8 @@ struct frame_metadata
hash_hash_func frame_metadata_hash;
hash_less_func frame_metadata_less;
static struct frame_metadata *get_victim (void);
/* 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
@@ -51,7 +58,7 @@ frame_init (void)
list_init (&active_list);
list_init (&inactive_list);
lock_init (&inactive_head_lock);
lock_init (&inactive_lock);
}
/* Attempt to allocate a frame for a user process, either by direct
@@ -60,25 +67,42 @@ frame_init (void)
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)
frame_alloc (enum palloc_flags flags, void *upage, struct thread *owner)
{
flags |= PAL_USER;
void *frame = palloc_get_page (flags);
/* If a frame couldn't be allocated we must be out of main memory. Thus,
obtain a victim page to replace with our page, and swap the victim
into disk. */
if (frame == NULL)
{
/* TODO: Find victim page to replace, and swap it with this new page. */
return NULL;
/* TODO: Deal with race condition wherein a page may be evicted in one
thread while it's in the middle of being evicted in another. */
struct frame_metadata *victim = get_victim ();
if (victim == NULL)
return NULL;
size_t swap_slot = swap_out (victim->frame);
page_set_swap (victim->owner, victim->upage, swap_slot);
/* If zero flag is set, zero out the victim page. */
if (flags & PAL_ZERO)
memset (victim->frame, 0, PGSIZE);
frame = victim->frame;
}
struct frame_metadata *frame_metadata =
malloc (sizeof (struct frame_metadata));
frame_metadata->frame = frame;
frame_metadata->upage = upage;
frame_metadata->owner = owner;
/* Newly faulted pages begin at the head of the inactive list. */
lock_acquire (&inactive_head_lock);
lock_acquire (&inactive_lock);
list_push_front (&inactive_list, &frame_metadata->list_elem);
lock_release (&inactive_head_lock);
lock_release (&inactive_lock);
/* Finally, insert frame metadata within the frame table, with the key as its
allocated kernel address. */
@@ -109,6 +133,24 @@ frame_free (void *frame)
palloc_free_page (frame);
}
/* Obtain a pointer to the metadata of the frame we should evict next. */
static struct frame_metadata *
get_victim (void)
{
lock_acquire (&inactive_lock);
if (list_empty (&inactive_list))
{
return NULL;
}
else
{
struct list_elem *victim_elem = list_pop_back (&inactive_list);
lock_release (&inactive_lock);
return list_entry (victim_elem, struct frame_metadata, list_elem);
}
}
/* Hash function for frame metadata, used for storing entries in the
frame table. */
unsigned