Compare commits

...

17 Commits

Author SHA1 Message Date
sBubshait
61f6374006 Update gitlab CI to only run the tests associated with this feature (mmapped-files). 2024-12-04 22:06:09 +00:00
sBubshait
26a2d40325 Implement implicitly unmapping all mmapped files when a process exits. Refactor to reduce duplication 2024-12-04 22:00:59 +00:00
sBubshait
806d6bc19e Refactor: Move destroying mmap data into process_exit instead of thread 2024-12-04 21:59:38 +00:00
sBubshait
ecbb4e74a5 Implement the unmap system call, writing back to the file if a page is dirty before removing from SPT 2024-12-04 19:07:28 +00:00
sBubshait
02b79d1934 Update mmap to add temporarily page_set_swap until swap is implemented 2024-12-04 18:13:07 +00:00
sBubshait
857cae3578 Update mmap to add a get helper function to find a mmap entry from its mapping 2024-12-04 18:08:05 +00:00
sBubshait
941e1e067a Update SPT page entry to change type from EXECUTABLE to PAGE_FILE to capture mmaps in addition to executables 2024-12-04 17:51:30 +00:00
sBubshait
ad6e4b4059 Implement syscall_mmap to validate and then map all file data into a user address in memory 2024-12-04 17:42:53 +00:00
sBubshait
72fa0c1bbb Fix Bug: Initialise the mmap table for the newly created thread rather than the current thread 2024-12-04 17:41:14 +00:00
sBubshait
67f16cb2a6 Update syscall.c to allow mmap and unmap system calls through helper handler functions for each 2024-12-04 15:31:53 +00:00
sBubshait
6b0f708d8f Update mmap to add an insert helper function to allocate and add new mmap entries to the hash table 2024-12-04 15:26:00 +00:00
sBubshait
6e838aa06a Fix Bug in thread.c: Only initialise and destroy mmap files table if VM is defined 2024-12-04 15:24:11 +00:00
sBubshait
a2f46f3b72 Add a mmap destroy function to cleanup all mmap hash table entries upon thread exit 2024-12-04 15:14:02 +00:00
sBubshait
1ce09a49a1 Add helper functions to initialise the memory-mapped files table and counter 2024-12-04 15:08:43 +00:00
sBubshait
b3042b5aa6 Update thread structure to add mmap files table and a counter for mappings of the thread 2024-12-04 14:56:52 +00:00
sBubshait
85aabd86cd Update gitlab ci file to include mmap tests in the automated testing pipeline 2024-12-04 14:55:28 +00:00
sBubshait
acc768e177 Add mmap module in vm defining mmap_entry structure and some helper functions 2024-12-04 13:01:01 +00:00
11 changed files with 275 additions and 24 deletions

View File

@@ -32,9 +32,10 @@ test_userprog:
extends: .pintos_tests
variables:
DIR: userprog
IGNORE: (tests/userprog/no-vm/multi-oom)
test_vm:
extends: .pintos_tests
variables:
DIR: vm
IGNORE: (tests/vm/pt-grow-stack|tests/vm/pt-grow-pusha|tests/vm/pt-big-stk-obj|tests/vm/pt-overflowstk|tests/vm/pt-write-code2|tests/vm/pt-grow-stk-sc|tests/vm/page-linear|tests/vm/page-parallel|tests/vm/page-merge-seq|tests/vm/page-merge-par|tests/vm/page-merge-stk|tests/vm/page-merge-mm|tests/vm/mmap-read|tests/vm/mmap-close|tests/vm/mmap-overlap|tests/vm/mmap-twice|tests/vm/mmap-write|tests/vm/mmap-exit|tests/vm/mmap-shuffle|tests/vm/mmap-clean|tests/vm/mmap-inherit|tests/vm/mmap-misalign|tests/vm/mmap-null|tests/vm/mmap-over-code|tests/vm/mmap-over-data|tests/vm/mmap-over-stk|tests/vm/mmap-remove)
IGNORE: (tests/vm/pt-grow-stack|tests/vm/pt-grow-pusha|tests/vm/pt-big-stk-obj|tests/vm/pt-overflowstk|tests/vm/pt-write-code2|tests/vm/pt-grow-stk-sc|tests/vm/page-linear|tests/vm/page-parallel|tests/vm/page-merge-seq|tests/vm/page-merge-par|tests/vm/page-merge-stk|tests/vm/page-merge-mm|tests/vm/mmap-over-stk)

View File

@@ -64,6 +64,7 @@ userprog_SRC += userprog/tss.c # TSS management.
# Virtual memory code.
vm_SRC += vm/frame.c # Frame table manager.
vm_SRC += vm/page.c # Page table manager.
vm_SRC += vm/mmap.c # Memory-mapped files.
vm_SRC += devices/swap.c # Swap block manager.
# Filesystem code.

View File

@@ -15,10 +15,13 @@
#include "threads/switch.h"
#include "threads/synch.h"
#include "threads/vaddr.h"
#include "vm/page.h"
#ifdef USERPROG
#include "userprog/process.h"
#include "userprog/syscall.h"
#include "vm/page.h"
#endif
#ifdef VM
#include "vm/mmap.h"
#endif
/* Random value for struct thread's `magic' member.
@@ -274,6 +277,10 @@ thread_create (const char *name, int priority,
}
#endif
#ifdef VM
mmap_init (t);
#endif
/* Prepare thread for first run by initializing its stack.
Do this atomically so intermediate values for the 'stack'
member cannot be observed. */

View File

@@ -137,6 +137,10 @@ struct thread
struct hash pages; /* Table of open user pages. */
/* Memory mapped files for user virtual memory. */
struct hash mmap_files; /* List of memory mapped files. */
unsigned int mmap_counter; /* Counter for memory mapped files. */
#ifdef USERPROG
/* Owned by userprog/process.c. */
uint32_t *pagedir; /* Page directory. */

View File

@@ -194,7 +194,7 @@ try_fetch_page (void *upage, bool write)
/* Load the page into memory based on the type of data it is expecting. */
bool success = false;
switch (page->type) {
case PAGE_EXECUTABLE:
case PAGE_FILE:
success = page_load (page, page->writable);
break;
default:

View File

@@ -25,6 +25,7 @@
#include "threads/synch.h"
#include "devices/timer.h"
#include "vm/page.h"
#include "vm/mmap.h"
#ifdef VM
#include "vm/frame.h"
#endif
@@ -363,6 +364,9 @@ process_exit (void)
struct thread *cur = thread_current ();
uint32_t *pd;
/* Unmap all memory mapped files */
mmap_destroy ();
/* Clean up all open files */
hash_destroy (&cur->open_files, fd_cleanup);
hash_destroy (&cur->pages, page_cleanup);
@@ -706,7 +710,7 @@ load_segment (struct file *file, off_t ofs, uint8_t *upage,
/* Add the page metadata to the SPT to be lazy loaded later on */
if (page_insert (file, ofs, upage, page_read_bytes, page_zero_bytes,
writable, PAGE_EXECUTABLE) == NULL)
writable, PAGE_FILE) == NULL)
return false;
/* Advance. */

View File

@@ -11,11 +11,14 @@
#include "threads/synch.h"
#include "userprog/process.h"
#include "userprog/pagedir.h"
#include "vm/page.h"
#include "vm/mmap.h"
#include <stdio.h>
#include <syscall-nr.h>
#define MAX_SYSCALL_ARGS 3
#define EXIT_FAILURE -1
#define MMAP_FAILURE -1
struct open_file
{
@@ -45,6 +48,8 @@ static int syscall_write (int fd, const void *buffer, unsigned size);
static void syscall_seek (int fd, unsigned position);
static unsigned syscall_tell (int fd);
static void syscall_close (int fd);
static mapid_t syscall_mmap (int fd, void *addr);
static void syscall_munmap (mapid_t mapping);
static struct open_file *fd_get_file (int fd);
static void validate_user_pointer (const void *start, size_t size, bool write);
@@ -74,6 +79,8 @@ static const struct syscall_arguments syscall_lookup[] =
[SYS_SEEK] = {(syscall_function) syscall_seek, 2},
[SYS_TELL] = {(syscall_function) syscall_tell, 1},
[SYS_CLOSE] = {(syscall_function) syscall_close, 1},
[SYS_MMAP] = {(syscall_function) syscall_mmap, 2},
[SYS_MUNMAP] = {(syscall_function) syscall_munmap, 1}
};
/* The number of syscall functions (i.e, number of elements) within the
@@ -394,6 +401,79 @@ syscall_close (int fd)
}
}
/* Handles the syscall for memory mapping a file. */
static mapid_t
syscall_mmap (int fd, void *addr)
{
/* Ensure the FD is for a file in the filesystem (not STDIN or STDOUT). */
if (fd == STDOUT_FILENO || fd == STDIN_FILENO)
return MMAP_FAILURE;
/* Validate that there is a file associated with the given FD. */
struct open_file *file_info = fd_get_file (fd);
if (file_info == NULL)
return MMAP_FAILURE;
/* Ensure that the address is page-aligned and it's neither NULL nor zero. */
if (addr == 0 || addr == NULL || pg_ofs (addr) != 0)
return MMAP_FAILURE;
/* Reopen the file to obtain a separate and independent reference to the file
for the mapping. */
struct file *file = file_reopen (file_info->file);
if (file == NULL)
return MMAP_FAILURE;
/* Get the size of the file. Mmap fails if the file is empty. */
off_t file_size = file_length (file);
if (file_size == 0)
return MMAP_FAILURE;
/* Check and ensure that there is enough space in the user virtual memory to
hold the entire file. */
for (off_t ofs = 0; ofs < file_size; ofs += PGSIZE)
{
if (page_get (addr + ofs) != NULL)
return MMAP_FAILURE;
}
/* Map the file data into the user virtual memory starting from addr. */
for (off_t ofs = 0; ofs < file_size; ofs += PGSIZE)
{
off_t read_bytes = file_size - ofs < PGSIZE ? file_size - ofs : PGSIZE;
off_t zero_bytes = PGSIZE - read_bytes;
if (page_insert (file, ofs, addr + ofs, read_bytes, zero_bytes, true,
PAGE_FILE) == NULL)
return MMAP_FAILURE;
}
/* Create a new mapping for the file. */
struct mmap_entry *mmap = mmap_insert (file, addr);
if (mmap == NULL)
return MMAP_FAILURE;
return mmap->mapping;
}
/* Handles the syscall for unmapping a memory mapped file.
Pre: mapping is a valid mapping identifier returned by mmap syscall. */
static void
syscall_munmap (mapid_t mapping)
{
/* Get the mmap entry from the mapping identifier. */
struct mmap_entry *mmap = mmap_get (mapping);
/* Delete the mmap entry from the hash table. */
hash_delete (&thread_current ()->mmap_files, &mmap->elem);
/* Unmap the mmap entry: free the pages and write back to the file if
necessary. NOTE. freeing and cleaning up is also handled by mmap_unmap. */
mmap_unmap (mmap);
}
/* Hashing function needed for the open_file table. Returns a hash for an entry,
based on its FD. */
unsigned

146
src/vm/mmap.c Normal file
View File

@@ -0,0 +1,146 @@
#include "mmap.h"
#include "page.h"
#include "threads/vaddr.h"
#include "threads/malloc.h"
#include "userprog/syscall.h"
#include "userprog/pagedir.h"
#include <stdio.h>
static unsigned mmap_hash (const struct hash_elem *e, void *aux);
static bool mmap_less (const struct hash_elem *a_, const struct hash_elem *b_,
void *aux);
static void mmap_cleanup(struct hash_elem *e, void *aux);
/* Initializes the mmap table for the given thread, setting the mmap counter to
0 and initializing the hash table. */
bool
mmap_init (struct thread *t)
{
t->mmap_counter = 0;
return hash_init (&t->mmap_files, mmap_hash, mmap_less, NULL);
}
struct mmap_entry *
mmap_get (mapid_t mapping)
{
struct mmap_entry fake_mmap_entry;
fake_mmap_entry.mapping = mapping;
struct hash_elem *e
= hash_find (&thread_current ()->mmap_files, &fake_mmap_entry.elem);
if (e == NULL)
return NULL;
return hash_entry (e, struct mmap_entry, elem);
}
/* Inserts a new mmap entry into the mmap table for the current thread. Upage
is the start address of the file data in the user VM. */
struct mmap_entry *
mmap_insert (struct file *file, void *upage)
{
if (file == NULL || upage == NULL)
return NULL;
struct mmap_entry *mmap = malloc (sizeof (struct mmap_entry));
if (mmap == NULL)
return NULL;
mmap->mapping = thread_current ()->mmap_counter++;
mmap->file = file;
mmap->upage = upage;
hash_insert (&thread_current ()->mmap_files, &mmap->elem);
return mmap;
}
/* Unmaps the given mmap entry from the current thread's mmap table. */
void
mmap_unmap (struct mmap_entry *mmap)
{
if (mmap == NULL)
return;
/* Free all the pages associated with the mapping, writing back to the file
if necessary. */
off_t length = file_length (mmap->file);
for (off_t ofs = 0; ofs < length; ofs += PGSIZE)
{
void *upage = mmap->upage + ofs;
/* Get the SPT page entry for this page. */
struct page_entry *page = page_get(upage);
if (page == NULL)
continue;
/* Write the page back to the file if it is dirty. */
if (pagedir_is_dirty (thread_current ()->pagedir, upage))
{
lock_acquire (&filesys_lock);
file_write_at (mmap->file, upage, page->read_bytes, ofs);
lock_release (&filesys_lock);
}
/* Remove the page from the supplemental page table. */
hash_delete (&thread_current ()->pages, &page->elem);
}
file_close (mmap->file);
free (mmap);
}
/* Destroys the mmap table for the current thread. Frees all the memory
allocated for the mmap entries. */
void
mmap_destroy (void)
{
hash_destroy (&thread_current ()->mmap_files, mmap_cleanup);
}
/* A hash function for the mmap table. Returns a hash for an entry, based on its
mapping. */
static unsigned
mmap_hash (const struct hash_elem *e, void *aux UNUSED)
{
return hash_entry (e, struct mmap_entry, elem)->mapping;
}
/* A comparator function for the mmap table. Compares two entries based on their
mappings. */
static bool
mmap_less (const struct hash_elem *a_, const struct hash_elem *b_,
void *aux UNUSED)
{
const struct mmap_entry *a = hash_entry (a_, struct mmap_entry, elem);
const struct mmap_entry *b = hash_entry (b_, struct mmap_entry, elem);
return a->mapping < b->mapping;
}
/* Cleans up the mmap table for the current thread. Implicitly unmaps the mmap
entry, freeing pages and writing back to the file if necessary. */
static void
mmap_cleanup (struct hash_elem *e, void *aux UNUSED)
{
struct mmap_entry *mmap = hash_entry (e, struct mmap_entry, elem);
mmap_unmap (mmap);
}
/* Updates the 'owner' thread's page table entry for virtual address 'upage'
to have a present bit of 0 and stores the specified swap slot value in the
entry for later retrieval from disk. */
void
page_set_swap (struct thread *owner, void *upage, size_t swap_slot)
{
}
/* Given that the page with user address 'upage' owned by 'owner' is flagged
to be in the swap disk via the owner's page table, returns its stored
swap slot. Otherwise panics the kernel. */
size_t
page_get_swap (struct thread *owner, void *upage)
{
return 0;
}

27
src/vm/mmap.h Normal file
View File

@@ -0,0 +1,27 @@
#ifndef VM_MMAP_H
#define VM_MMAP_H
#include <hash.h>
#include "threads/thread.h"
#include "filesys/file.h"
/* A mapping identifier type. */
typedef unsigned mapid_t;
/* A structure to represent a memory mapped file. */
struct mmap_entry {
mapid_t mapping; /* The mapping identifier of the mapped file. */
struct file *file; /* A pointer to the file that is being mapped. */
void *upage; /* The start address of the file data in the user VM. */
struct hash_elem elem; /* An elem for the hash table. */
};
bool mmap_init (struct thread *t);
struct mmap_entry *mmap_get (mapid_t mapping);
struct mmap_entry *mmap_insert (struct file *file, void *upage);
void mmap_unmap (struct mmap_entry *mmap);
void mmap_umap_all (void);
void mmap_destroy (void);
#endif /* vm/mmap.h */

View File

@@ -59,7 +59,6 @@ page_get (void *upage)
struct hash_elem *e
= hash_find (&thread_current ()->pages, &fake_page_entry.elem);
if (e == NULL)
return NULL;
@@ -107,21 +106,3 @@ page_cleanup (struct hash_elem *e, void *aux UNUSED)
{
free (hash_entry (e, struct page_entry, elem));
}
/* Updates the 'owner' thread's page table entry for virtual address 'upage'
to have a present bit of 0 and stores the specified swap slot value in the
entry for later retrieval from disk. */
void
page_set_swap (struct thread *owner, void *upage, size_t swap_slot)
{
}
/* Given that the page with user address 'upage' owned by 'owner' is flagged
to be in the swap disk via the owner's page table, returns its stored
swap slot. Otherwise panics the kernel. */
size_t
page_get_swap (struct thread *owner, void *upage)
{
}

View File

@@ -5,7 +5,7 @@
#include "filesys/off_t.h"
enum page_type {
PAGE_EXECUTABLE,
PAGE_FILE,
PAGE_EMPTY
};