feat: implement frame table without thread safety
This commit is contained in:
137
src/vm/frame.c
Normal file
137
src/vm/frame.c
Normal 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
10
src/vm/frame.h
Normal 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 */
|
||||
Reference in New Issue
Block a user