Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#133] Implement memory mapped files #134

Merged
merged 1 commit into from
May 20, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/threads/thread.c
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,7 @@ init_thread (struct thread *t, const char *name, int priority)
#ifdef USERPROG
list_init (&(&t->process)->child_list);
list_init (&(&t->process)->file_list);
list_init (&(&t->process)->mmap_list);
#endif
}

Expand Down
53 changes: 53 additions & 0 deletions src/userprog/process.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
#include <string.h>
#include "userprog/gdt.h"
#include "userprog/pagedir.h"
#ifdef VM
#include "userprog/syscall.h"
#endif
#include "userprog/tss.h"
#include "filesys/directory.h"
#include "filesys/file.h"
Expand All @@ -24,6 +27,9 @@
#endif

#define FD_MIN 2 /* Min value for file descriptors. */
#ifdef VM
#define MAPID_MIN 0 /* Min value for memory mapped identifiers. */
#endif

/* Structure for arguments. */
struct arguments
Expand Down Expand Up @@ -132,6 +138,9 @@ start_process (void *arguments)
}
curr->exec_file = exec_file;
curr->fd_next = FD_MIN;
#ifdef VM
curr->mapid_next = MAPID_MIN;
#endif

/* Free resources. */
palloc_free_page (args->argv[0]);
Expand Down Expand Up @@ -206,6 +215,14 @@ process_exit (void)
file_close (pfe->file);
free (pfe);
}
#ifdef VM
for (e = list_begin (&proc->mmap_list); e != list_end (&proc->mmap_list);)
{
struct process_mmap *mmap = list_entry (e, struct process_mmap, elem);
e = list_next (e);
unmap_mmap_item (mmap);
}
#endif

struct thread *curr = thread_current ();
struct suppl_pt *pt;
Expand Down Expand Up @@ -307,6 +324,42 @@ process_set_file (struct file *file)
/* Return the file descriptor. */
return pfe->fd;
}

#ifdef VM
/* Returns a process' memory mapped file by its identifier. */
struct process_mmap *
process_get_mmap (mapid_t id)
{
struct list *list = &process_current ()->mmap_list;
struct list_elem *e;
for (e = list_begin (list); e != list_end (list); e = list_next (e))
{
struct process_mmap *mmap = list_entry (e, struct process_mmap, elem);
if (mmap->id == id)
return mmap;
}
return NULL;
}

/* Sets the memory mapped file information into
the current process and returns its identifier. */
mapid_t
process_set_mmap (struct file *file, void *addr, size_t size)
{
struct process_mmap *mmap = malloc (sizeof (struct process_mmap));
if (mmap == NULL)
return MAP_FAILED;

struct process *curr = process_current ();
mmap->id = curr->mapid_next++;
mmap->file = file;
mmap->addr = addr;
mmap->size = size;
list_push_back (&curr->mmap_list, &mmap->elem);

return mmap->id;
}
#endif

/* We load ELF binaries. The following definitions are taken
from the ELF specification, [ELF1], more-or-less verbatim. */
Expand Down
34 changes: 31 additions & 3 deletions src/userprog/process.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@
typedef int pid_t;
#define PID_ERROR ((pid_t) -1) /* Error value for pid_t. */

#ifdef VM
/* Map region identifier. */
typedef int mapid_t;
#define MAP_FAILED ((mapid_t) -1) /* Error value for mapid_t. */
#endif

/* Process status flags. */
#define PROCESS_LOADING 0 /* Process is loading. */
#define PROCESS_RUNNING 1 /* Process is running. */
Expand All @@ -21,8 +27,14 @@ struct process
struct file *exec_file; /* Process executable file. */
struct list child_list; /* List of child processes. */
struct list file_list; /* List of files in use. */
#ifdef VM
struct list mmap_list; /* List of memory mapped file. */
#endif
struct process_info *info; /* Process information for its parent. */
int fd_next; /* File descriptor tracker. */
#ifdef VM
mapid_t mapid_next; /* Mapping identifier tracker. */
#endif
};

/* An user process information for its parent process. */
Expand All @@ -44,13 +56,29 @@ struct process_file
struct list_elem elem; /* List element. */
};

#ifdef VM
/* A memory mapped file information by some process. */
struct process_mmap
{
mapid_t id; /* Mapping identifier. */
struct file *file; /* Memory mapped file. */
void *addr; /* Mapped address. */
size_t size; /* File size. */
struct list_elem elem; /* List element. */
};
#endif

pid_t process_execute (const char *file_name);
int process_wait (pid_t pid);
int process_wait (pid_t);
void process_exit (void);
void process_activate (void);
struct process *process_current (void);
struct process_info *process_find_child (pid_t pid);
struct process_info *process_find_child (pid_t);
struct file *process_get_file (int fd);
int process_set_file (struct file *file);
int process_set_file (struct file *);
#ifdef VM
struct process_mmap *process_get_mmap (mapid_t);
mapid_t process_set_mmap (struct file *, void *addr, size_t);
#endif

#endif /* userprog/process.h */
143 changes: 142 additions & 1 deletion src/userprog/syscall.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#include "userprog/syscall.h"
#include <debug.h>
#include <stdio.h>
#include <syscall-nr.h>
#include <debug.h>
#include "devices/input.h"
#include "filesys/file.h"
#include "filesys/filesys.h"
Expand All @@ -12,6 +12,11 @@
#include "threads/thread.h"
#include "threads/vaddr.h"
#include "userprog/process.h"
#ifdef VM
#include "vm/frame.h"
#include "vm/page.h"
#include "vm/swap.h"
#endif

/* A Lock for mutual exclusion between system calls. */
static struct lock filesys_lock;
Expand All @@ -36,6 +41,10 @@ static int syscall_write (int fd, void *buffer, unsigned size);
static void syscall_seek (int fd, unsigned position);
static unsigned syscall_tell (int fd);
static void syscall_close (int fd);
#ifdef VM
static mapid_t syscall_mmap (int fd, void *addr);
static void syscall_munmap (mapid_t mapping);
#endif

void
syscall_init (void)
Expand Down Expand Up @@ -98,9 +107,19 @@ syscall_handler (struct intr_frame *f UNUSED)
case SYS_CLOSE:
syscall_close ((int) get_word (esp + 1));
break;
#ifdef VM
case SYS_MMAP:
f->eax = syscall_mmap ((int) get_word (esp + 1),
(void *) get_word (esp + 2));
break;
case SYS_MUNMAP:
syscall_munmap ((mapid_t) get_word (esp + 1));
break;
#endif
default:
/* Undefined system calls. */
thread_exit ();
NOT_REACHED ();
}
}

Expand Down Expand Up @@ -378,6 +397,128 @@ syscall_close (int fd)
lock_release (&filesys_lock);
}

#ifdef VM
/* Maps the file open as FD into the process’s virtual address
space. */
static mapid_t
syscall_mmap (int fd, void *addr)
{
off_t fault_ofs = -1;
struct file *f = NULL;

/* Check the validity. */
lock_acquire (&filesys_lock);
if (addr == NULL || !is_user_vaddr (addr) || pg_ofs (addr) != 0)
goto fail;

/* Get the file. */
f = process_get_file (fd);
if (f == NULL)
goto fail;

/* Reopen the file. */
size_t size;
f = file_reopen (f);
if (f == NULL || (size = file_length (f)) == 0)
goto fail;

/* Install a new page. */
off_t ofs;
for (ofs = 0; (size_t) ofs < size; ofs += PGSIZE)
{
size_t read_bytes = (size_t) ofs + PGSIZE < size ? PGSIZE : size - ofs;
size_t zero_bytes = PGSIZE - read_bytes;
if (!suppl_pt_set_file (addr + ofs, f, ofs, read_bytes, zero_bytes, true))
{
fault_ofs = ofs;
goto fail;
}
}
fault_ofs = size;

/* Create a new mmap item. */
mapid_t id = process_set_mmap (f, addr, size);
if (id == MAP_FAILED)
goto fail;

/* Return mapping id. */
lock_release (&filesys_lock);
return id;

fail:
for (ofs = 0; ofs < fault_ofs; ofs += PGSIZE)
suppl_pt_clear_page (addr + ofs);
file_close (f);
lock_release (&filesys_lock);
return MAP_FAILED;
}

/* Unmaps the mapping designated by mapping, which must be
a mapping ID returned by a previous call to mmap by
the same process that has not yet been unmapped. */
static void
syscall_munmap (mapid_t mapping)
{
struct process_mmap *mmap = process_get_mmap (mapping);
if (mmap == NULL)
return;
unmap_mmap_item (mmap);
}

/* Unmaps the mapping, which must be a mapping ID returned by
a previous call to mmap by the same process that has not yet
been unmapped. */
void
unmap_mmap_item (struct process_mmap *mmap)
{
ASSERT (pg_ofs (mmap->addr) == 0);

lock_acquire (&filesys_lock);

/* Write back to the file. */
off_t ofs;
for (ofs = 0; (size_t) ofs < mmap->size; ofs += PGSIZE)
{
void *addr = mmap->addr + ofs;
struct suppl_pte *pte = suppl_pt_get_page (addr);
if (pte == NULL)
continue;

/* If page is loaded now. */
if (pte->kpage != NULL)
{
if (suppl_pt_update_dirty (pte))
file_write_at (mmap->file, pte->kpage, PGSIZE, ofs);
frame_remove (pte->kpage);
palloc_free_page (pte->kpage);
}
/* If page is on swap disk. */
else if (pte->type == PAGE_SWAP)
{
if (suppl_pt_update_dirty (pte))
{
void *kpage = palloc_get_page (0);
swap_in (kpage, pte->swap_index);
file_write_at (mmap->file, pte->kpage, PGSIZE, ofs);
palloc_free_page (kpage);
}
else
swap_remove (pte->swap_index);
}

/* Free resources. */
pagedir_clear_page (pte->pagedir, pte->upage);
hash_delete (&thread_current ()->suppl_pt->hash, &pte->elem);
}

/* Free resources. */
list_remove (&mmap->elem);
file_close (mmap->file);
free (mmap);
lock_release (&filesys_lock);
}
#endif

/* Reads a byte at user virtual address UADDR.
Returns the byte value if successful, -1 if a segfault
occured or UADDR is not in the user space. */
Expand Down
4 changes: 4 additions & 0 deletions src/userprog/syscall.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
#ifndef USERPROG_SYSCALL_H
#define USERPROG_SYSCALL_H

#include "process.h"

void syscall_init (void);
void syscall_exit (int);

void unmap_mmap_item (struct process_mmap *);

#endif /* userprog/syscall.h */
9 changes: 6 additions & 3 deletions src/vm/page.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@

static hash_hash_func suppl_pt_hash;
static hash_less_func suppl_pt_less;
static hash_action_func suppl_pt_free_pte;

/* Creates and returns a new supplemental page table. */
struct suppl_pt *
Expand Down Expand Up @@ -41,6 +40,8 @@ suppl_pt_destroy (struct suppl_pt *pt)
bool
suppl_pt_set_zero (void *upage)
{
if (suppl_pt_get_page (upage) != NULL)
return false;
struct suppl_pte *pte = malloc (sizeof (struct suppl_pte));
if (pte == NULL)
return false;
Expand All @@ -64,6 +65,8 @@ bool
suppl_pt_set_file (void *upage, struct file *file, off_t ofs,
uint32_t read_bytes, uint32_t zero_bytes, bool writable)
{
if (suppl_pt_get_page (upage) != NULL)
return false;
struct suppl_pte *pte = malloc (sizeof (struct suppl_pte));
if (pte == NULL)
return false;
Expand Down Expand Up @@ -160,9 +163,9 @@ void
suppl_pt_clear_page (void *upage)
{
struct suppl_pte *pte = suppl_pt_get_page (upage);
pagedir_clear_page (pte->pagedir, upage);
if (pte == NULL)
return;
pagedir_clear_page (pte->pagedir, upage);
suppl_pt_free_pte (&pte->elem, thread_current ()->suppl_pt);
}

Expand Down Expand Up @@ -216,7 +219,7 @@ suppl_pt_less (const struct hash_elem *e1, const struct hash_elem *e2,
/* Frees a supplemental page table entry.
This also removes the frame table entry if supplemental page
PT is given, but not frees allocated page. */
static void
void
suppl_pt_free_pte (struct hash_elem *e, void *pt)
{
struct suppl_pte *pte = hash_entry (e, struct suppl_pte, elem);
Expand Down
Loading