From: Ben Pfaff Date: Mon, 27 Sep 2004 23:49:29 +0000 (+0000) Subject: Update. X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=8a8db36c5a9ca4d8a8c7323105328beb57a6ae75;p=pintos-anon Update. --- diff --git a/solutions/README b/solutions/README new file mode 100644 index 0000000..8a83af0 --- /dev/null +++ b/solutions/README @@ -0,0 +1,7 @@ +Sample solutions. + +* The solutions for p1-2 and p2 are okay. + +* The solution for p3 is terrible. For example, there's no locking at + all. I just wrote it to make sure that the project was possible + given the tools that we gave the students. diff --git a/solutions/p1-2.patch b/solutions/p1-2.patch index dbe4434..a5e743f 100644 --- a/solutions/p1-2.patch +++ b/solutions/p1-2.patch @@ -1,9 +1,6 @@ -Only in pintos/src/threads: .#init.c -Only in pintos/src/threads: build -Only in pintos/src/threads: init.c~ -diff -urp pintos.orig/src/threads/synch.c pintos/src/threads/synch.c +diff -X pat -urpN pintos.orig/src/threads/synch.c pintos/src/threads/synch.c --- pintos.orig/src/threads/synch.c 2004-09-19 21:29:53.000000000 -0700 -+++ pintos/src/threads/synch.c 2004-09-20 22:38:56.000000000 -0700 ++++ pintos/src/threads/synch.c 2004-09-27 16:50:14.000000000 -0700 @@ -330,3 +330,35 @@ cond_name (const struct condition *cond) return cond->name; @@ -40,10 +37,9 @@ diff -urp pintos.orig/src/threads/synch.c pintos/src/threads/synch.c + } + lock_release (&latch->monitor_lock); +} -Only in pintos/src/threads: synch.c.orig -diff -urp pintos.orig/src/threads/synch.h pintos/src/threads/synch.h +diff -X pat -urpN pintos.orig/src/threads/synch.h pintos/src/threads/synch.h --- pintos.orig/src/threads/synch.h 2004-09-19 21:29:53.000000000 -0700 -+++ pintos/src/threads/synch.h 2004-09-20 22:38:56.000000000 -0700 ++++ pintos/src/threads/synch.h 2004-09-27 16:50:14.000000000 -0700 @@ -44,4 +44,16 @@ void cond_signal (struct condition *, st void cond_broadcast (struct condition *, struct lock *); const char *cond_name (const struct condition *); @@ -61,11 +57,10 @@ diff -urp pintos.orig/src/threads/synch.h pintos/src/threads/synch.h +void latch_release (struct latch *); + #endif /* threads/synch.h */ -Only in pintos/src/threads: synch.h.orig -diff -urp pintos.orig/src/threads/thread.c pintos/src/threads/thread.c ---- pintos.orig/src/threads/thread.c 2004-09-20 19:32:31.000000000 -0700 -+++ pintos/src/threads/thread.c 2004-09-20 22:52:43.000000000 -0700 -@@ -76,6 +76,7 @@ thread_init (void) +diff -X pat -urpN pintos.orig/src/threads/thread.c pintos/src/threads/thread.c +--- pintos.orig/src/threads/thread.c 2004-09-26 14:15:17.000000000 -0700 ++++ pintos/src/threads/thread.c 2004-09-27 16:51:03.000000000 -0700 +@@ -80,6 +80,7 @@ thread_init (void) init_thread (initial_thread, "main", PRI_DEFAULT); initial_thread->status = THREAD_RUNNING; initial_thread->tid = allocate_tid (); @@ -73,7 +68,7 @@ diff -urp pintos.orig/src/threads/thread.c pintos/src/threads/thread.c } /* Starts preemptive thread scheduling by enabling interrupts. -@@ -120,6 +121,7 @@ thread_create (const char *name, int pri +@@ -148,6 +149,7 @@ thread_create (const char *name, int pri /* Initialize thread. */ init_thread (t, name, priority); tid = t->tid = allocate_tid (); @@ -81,7 +76,7 @@ diff -urp pintos.orig/src/threads/thread.c pintos/src/threads/thread.c /* Stack frame for kernel_thread(). */ kf = alloc_frame (t, sizeof *kf); -@@ -196,12 +198,30 @@ thread_tid (void) +@@ -224,16 +226,34 @@ thread_tid (void) void thread_exit (void) { @@ -90,6 +85,10 @@ diff -urp pintos.orig/src/threads/thread.c pintos/src/threads/thread.c + ASSERT (!intr_context ()); + #ifdef USERPROG + process_exit (); + #endif + + /* Notify our parent that we're dying. */ + latch_release (&t->ready_to_die); + @@ -113,7 +112,7 @@ diff -urp pintos.orig/src/threads/thread.c pintos/src/threads/thread.c schedule (); NOT_REACHED (); } -@@ -238,6 +258,22 @@ thread_block (void) +@@ -270,6 +290,22 @@ thread_block (void) thread_current ()->status = THREAD_BLOCKED; schedule (); } @@ -136,7 +135,7 @@ diff -urp pintos.orig/src/threads/thread.c pintos/src/threads/thread.c /* Idle thread. Executes when no other thread is ready to run. */ static void -@@ -303,6 +339,9 @@ init_thread (struct thread *t, const cha +@@ -335,6 +371,9 @@ init_thread (struct thread *t, const cha strlcpy (t->name, name, sizeof t->name); t->stack = (uint8_t *) t + PGSIZE; t->priority = priority; @@ -146,13 +145,9 @@ diff -urp pintos.orig/src/threads/thread.c pintos/src/threads/thread.c t->magic = THREAD_MAGIC; } -Only in pintos/src/threads: thread.c.orig -Only in pintos/src/threads: thread.c.rej -Only in pintos/src/threads: thread.c.rej~ -Only in pintos/src/threads: thread.c~ -diff -urp pintos.orig/src/threads/thread.h pintos/src/threads/thread.h ---- pintos.orig/src/threads/thread.h 2004-09-20 15:29:18.000000000 -0700 -+++ pintos/src/threads/thread.h 2004-09-20 22:36:55.000000000 -0700 +diff -X pat -urpN pintos.orig/src/threads/thread.h pintos/src/threads/thread.h +--- pintos.orig/src/threads/thread.h 2004-09-26 14:15:17.000000000 -0700 ++++ pintos/src/threads/thread.h 2004-09-27 16:50:14.000000000 -0700 @@ -4,6 +4,7 @@ #include #include @@ -174,5 +169,3 @@ diff -urp pintos.orig/src/threads/thread.h pintos/src/threads/thread.h /* Shared between thread.c and synch.c. */ list_elem elem; /* List element. */ -Only in pintos/src/threads: thread.h.orig -Only in pintos/src/threads: thread.h~ diff --git a/solutions/p2.patch b/solutions/p2.patch index 8ff1be7..68331bd 100644 --- a/solutions/p2.patch +++ b/solutions/p2.patch @@ -1,16 +1,15 @@ -diff -urp pintos.orig/src/constants.h pintos/src/constants.h +diff -urpN pintos.orig/src/constants.h pintos/src/constants.h --- pintos.orig/src/constants.h 2004-09-21 17:26:39.000000000 -0700 -+++ pintos/src/constants.h 2004-09-22 00:34:01.000000000 -0700 ++++ pintos/src/constants.h 2004-09-27 16:41:17.000000000 -0700 @@ -8,4 +8,4 @@ /*#define MACRONAME 1 */ /* Uncomment if if you've implemented thread_join(). */ -/*#define THREAD_JOIN_IMPLEMENTED 1*/ +#define THREAD_JOIN_IMPLEMENTED 1 -Only in pintos/src: constants.h~ -diff -urp pintos.orig/src/threads/synch.c pintos/src/threads/synch.c +diff -urpN pintos.orig/src/threads/synch.c pintos/src/threads/synch.c --- pintos.orig/src/threads/synch.c 2004-09-19 21:29:53.000000000 -0700 -+++ pintos/src/threads/synch.c 2004-09-22 00:30:05.000000000 -0700 ++++ pintos/src/threads/synch.c 2004-09-27 16:41:17.000000000 -0700 @@ -330,3 +330,35 @@ cond_name (const struct condition *cond) return cond->name; @@ -47,9 +46,9 @@ diff -urp pintos.orig/src/threads/synch.c pintos/src/threads/synch.c + } + lock_release (&latch->monitor_lock); +} -diff -urp pintos.orig/src/threads/synch.h pintos/src/threads/synch.h +diff -urpN pintos.orig/src/threads/synch.h pintos/src/threads/synch.h --- pintos.orig/src/threads/synch.h 2004-09-19 21:29:53.000000000 -0700 -+++ pintos/src/threads/synch.h 2004-09-22 00:30:05.000000000 -0700 ++++ pintos/src/threads/synch.h 2004-09-27 16:41:17.000000000 -0700 @@ -44,4 +44,16 @@ void cond_signal (struct condition *, st void cond_broadcast (struct condition *, struct lock *); const char *cond_name (const struct condition *); @@ -67,9 +66,9 @@ diff -urp pintos.orig/src/threads/synch.h pintos/src/threads/synch.h +void latch_release (struct latch *); + #endif /* threads/synch.h */ -diff -urp pintos.orig/src/threads/thread.c pintos/src/threads/thread.c ---- pintos.orig/src/threads/thread.c 2004-09-21 22:42:17.000000000 -0700 -+++ pintos/src/threads/thread.c 2004-09-22 00:32:39.000000000 -0700 +diff -urpN pintos.orig/src/threads/thread.c pintos/src/threads/thread.c +--- pintos.orig/src/threads/thread.c 2004-09-26 14:15:17.000000000 -0700 ++++ pintos/src/threads/thread.c 2004-09-27 16:41:17.000000000 -0700 @@ -13,6 +13,7 @@ #include "threads/synch.h" #ifdef USERPROG @@ -78,7 +77,7 @@ diff -urp pintos.orig/src/threads/thread.c pintos/src/threads/thread.c #endif /* Random value for struct thread's `magic' member. -@@ -75,6 +76,7 @@ thread_init (void) +@@ -80,6 +81,7 @@ thread_init (void) init_thread (initial_thread, "main", PRI_DEFAULT); initial_thread->status = THREAD_RUNNING; initial_thread->tid = allocate_tid (); @@ -86,7 +85,7 @@ diff -urp pintos.orig/src/threads/thread.c pintos/src/threads/thread.c } /* Starts preemptive thread scheduling by enabling interrupts. -@@ -119,6 +121,7 @@ thread_create (const char *name, int pri +@@ -148,6 +150,7 @@ thread_create (const char *name, int pri /* Initialize thread. */ init_thread (t, name, priority); tid = t->tid = allocate_tid (); @@ -94,7 +93,7 @@ diff -urp pintos.orig/src/threads/thread.c pintos/src/threads/thread.c /* Stack frame for kernel_thread(). */ kf = alloc_frame (t, sizeof *kf); -@@ -195,16 +198,36 @@ thread_tid (void) +@@ -224,16 +227,36 @@ thread_tid (void) void thread_exit (void) { @@ -132,7 +131,7 @@ diff -urp pintos.orig/src/threads/thread.c pintos/src/threads/thread.c schedule (); NOT_REACHED (); } -@@ -241,6 +264,26 @@ thread_block (void) +@@ -270,6 +293,26 @@ thread_block (void) thread_current ()->status = THREAD_BLOCKED; schedule (); } @@ -159,7 +158,7 @@ diff -urp pintos.orig/src/threads/thread.c pintos/src/threads/thread.c /* Idle thread. Executes when no other thread is ready to run. */ static void -@@ -306,6 +349,12 @@ init_thread (struct thread *t, const cha +@@ -335,6 +378,12 @@ init_thread (struct thread *t, const cha strlcpy (t->name, name, sizeof t->name); t->stack = (uint8_t *) t + PGSIZE; t->priority = priority; @@ -172,12 +171,9 @@ diff -urp pintos.orig/src/threads/thread.c pintos/src/threads/thread.c t->magic = THREAD_MAGIC; } -Only in pintos/src/threads: thread.c.orig -Only in pintos/src/threads: thread.c.rej -Only in pintos/src/threads: thread.c~ -diff -urp pintos.orig/src/threads/thread.h pintos/src/threads/thread.h ---- pintos.orig/src/threads/thread.h 2004-09-20 15:29:18.000000000 -0700 -+++ pintos/src/threads/thread.h 2004-09-22 00:30:05.000000000 -0700 +diff -urpN pintos.orig/src/threads/thread.h pintos/src/threads/thread.h +--- pintos.orig/src/threads/thread.h 2004-09-26 14:15:17.000000000 -0700 ++++ pintos/src/threads/thread.h 2004-09-27 16:41:17.000000000 -0700 @@ -4,6 +4,7 @@ #include #include @@ -210,7 +206,7 @@ diff -urp pintos.orig/src/threads/thread.h pintos/src/threads/thread.h #endif /* Owned by thread.c */ -@@ -117,7 +129,7 @@ void thread_yield (void); +@@ -119,7 +131,7 @@ void thread_yield (void); void thread_block (void); /* This function will be implemented in problem 1-2. */ @@ -219,11 +215,10 @@ diff -urp pintos.orig/src/threads/thread.h pintos/src/threads/thread.h /* These functions will be implemented in problem 1-3. */ void thread_set_priority (int); -Only in pintos/src/userprog: build -diff -urp pintos.orig/src/userprog/exception.c pintos/src/userprog/exception.c ---- pintos.orig/src/userprog/exception.c 2004-09-20 12:06:58.000000000 -0700 -+++ pintos/src/userprog/exception.c 2004-09-22 00:30:05.000000000 -0700 -@@ -134,6 +134,13 @@ page_fault (struct intr_frame *f) +diff -urpN pintos.orig/src/userprog/exception.c pintos/src/userprog/exception.c +--- pintos.orig/src/userprog/exception.c 2004-09-26 14:15:17.000000000 -0700 ++++ pintos/src/userprog/exception.c 2004-09-27 16:41:17.000000000 -0700 +@@ -147,6 +147,13 @@ page_fault (struct intr_frame *f) write = (f->error_code & PF_W) != 0; user = (f->error_code & PF_U) != 0; @@ -237,25 +232,9 @@ diff -urp pintos.orig/src/userprog/exception.c pintos/src/userprog/exception.c /* To implement virtual memory, delete the rest of the function body, and replace it with code that brings in the page to which fault_addr refers. */ -diff -urp pintos.orig/src/userprog/pagedir.c pintos/src/userprog/pagedir.c ---- pintos.orig/src/userprog/pagedir.c 2004-09-21 22:42:17.000000000 -0700 -+++ pintos/src/userprog/pagedir.c 2004-09-22 00:32:23.000000000 -0700 -@@ -99,7 +99,7 @@ pagedir_set_page (uint32_t *pd, void *up - ASSERT (pg_ofs (upage) == 0); - ASSERT (pg_ofs (kpage) == 0); - ASSERT (upage < PHYS_BASE); -- ASSERT (lookup_page (pd, upage, false) == NULL); -+ ASSERT (pagedir_get_page (pd, upage) == NULL); - - pte = lookup_page (pd, upage, true); - if (pte != NULL) -Only in pintos/src/userprog: pagedir.c.orig -Only in pintos/src/userprog: pagedir.c.rej -Only in pintos/src/userprog: pagedir.c.rej~ -Only in pintos/src/userprog: pagedir.c~ -diff -urp pintos.orig/src/userprog/process.c pintos/src/userprog/process.c ---- pintos.orig/src/userprog/process.c 2004-09-21 22:42:17.000000000 -0700 -+++ pintos/src/userprog/process.c 2004-09-22 00:30:05.000000000 -0700 +diff -urpN pintos.orig/src/userprog/process.c pintos/src/userprog/process.c +--- pintos.orig/src/userprog/process.c 2004-09-22 17:58:29.000000000 -0700 ++++ pintos/src/userprog/process.c 2004-09-27 16:41:17.000000000 -0700 @@ -182,7 +182,7 @@ struct Elf32_Phdr #define PF_R 4 /* Readable. */ @@ -391,7 +370,7 @@ diff -urp pintos.orig/src/userprog/process.c pintos/src/userprog/process.c uint8_t *kpage; bool success = false; @@ -382,9 +462,9 @@ setup_stack (void **esp) - kpage = palloc_get (PAL_USER | PAL_ZERO); + kpage = palloc_get_page (PAL_USER | PAL_ZERO); if (kpage != NULL) { - success = install_page (((uint8_t *) PHYS_BASE) - PGSIZE, kpage); @@ -401,12 +380,11 @@ diff -urp pintos.orig/src/userprog/process.c pintos/src/userprog/process.c + if (install_page (upage, kpage)) + success = init_cmdline (kpage, upage, cmdline, esp); else - palloc_free (kpage); + palloc_free_page (kpage); } -Only in pintos/src/userprog: process.c.orig -diff -urp pintos.orig/src/userprog/syscall.c pintos/src/userprog/syscall.c ---- pintos.orig/src/userprog/syscall.c 2004-09-05 22:19:19.000000000 -0700 -+++ pintos/src/userprog/syscall.c 2004-09-22 00:30:05.000000000 -0700 +diff -urpN pintos.orig/src/userprog/syscall.c pintos/src/userprog/syscall.c +--- pintos.orig/src/userprog/syscall.c 2004-09-26 14:15:17.000000000 -0700 ++++ pintos/src/userprog/syscall.c 2004-09-27 16:43:00.000000000 -0700 @@ -1,20 +1,429 @@ #include "userprog/syscall.h" #include @@ -423,9 +401,10 @@ diff -urp pintos.orig/src/userprog/syscall.c pintos/src/userprog/syscall.c +#include "threads/mmu.h" +#include "threads/palloc.h" #include "threads/thread.h" - +- ++ +typedef int syscall_function (int, int, int); -+ ++ +static int sys_halt (void); +static int sys_exit (int status); +static int sys_exec (const char *ufile); @@ -439,13 +418,13 @@ diff -urp pintos.orig/src/userprog/syscall.c pintos/src/userprog/syscall.c +static int sys_seek (int handle, unsigned position); +static int sys_tell (int handle); +static int sys_close (int handle); -+ ++ +struct syscall + { + size_t arg_cnt; + syscall_function *func; + }; -+ ++ +struct syscall syscall_table[] = + { + {0, (syscall_function *) sys_halt}, @@ -463,85 +442,88 @@ diff -urp pintos.orig/src/userprog/syscall.c pintos/src/userprog/syscall.c + {1, (syscall_function *) sys_close}, + }; +static const int syscall_cnt = sizeof syscall_table / sizeof *syscall_table; -+ ++ static void syscall_handler (struct intr_frame *); +- +static void copy_in (void *, const void *, size_t); -+ ++ +static struct lock fs_lock; - ++ void syscall_init (void) { intr_register (0x30, 3, INTR_ON, syscall_handler, "syscall"); + lock_init (&fs_lock, "fs"); } - +- ++ static void - syscall_handler (struct intr_frame *f) +-syscall_handler (struct intr_frame *f UNUSED) ++syscall_handler (struct intr_frame *f) { - printf ("system call!\n"); + struct syscall *s; + int call_nr; + int args[3]; -+ ++ + copy_in (&call_nr, f->esp, sizeof call_nr); + if (call_nr < 0 || call_nr >= syscall_cnt) + { + printf ("bad syscall number %d\n", call_nr); + thread_exit (); + } -+ ++ + s = syscall_table + call_nr; + ASSERT (s->arg_cnt <= sizeof args / sizeof *args); + memset (args, 0, sizeof args); + copy_in (args, (uint32_t *) f->esp + 1, sizeof *args * s->arg_cnt); + f->eax = s->func (args[0], args[1], args[2]); +} -+ ++ +static bool +verify_user (const void *uaddr) +{ + return pagedir_get_page (thread_current ()->pagedir, uaddr) != NULL; +} -+ ++ +static inline bool get_user (uint8_t *dst, const uint8_t *usrc) { + int eax; + asm ("movl $1f, %%eax; movb %2, %%al; movb %%al, %0; 1:" + : "=m" (*dst), "=&a" (eax) : "m" (*usrc)); + return eax != 0; +} -+ ++ +static inline bool put_user (uint8_t *udst, uint8_t byte) { + int eax; + asm ("movl $1f, %%eax; movb %b2, %0; 1:" + : "=m" (*udst), "=&a" (eax) : "r" (byte)); + return eax != 0; +} -+ ++ +static void +copy_in (void *dst_, const void *usrc_, size_t size) +{ + uint8_t *dst = dst_; + const uint8_t *usrc = usrc_; -+ ++ + for (; size > 0; size--, dst++, usrc++) + if (usrc >= (uint8_t *) PHYS_BASE || !get_user (dst, usrc)) + thread_exit (); +} -+ ++ +static char * +copy_in_string (const char *us) +{ + char *ks; + size_t length; -+ -+ ks = palloc_get (0); ++ ++ ks = palloc_get_page (0); + if (ks == NULL) + { + printf ("copy_in_string: out of memory\n"); + thread_exit (); + } -+ ++ + for (length = 0; length < PGSIZE; length++) + { + if (us >= (char *) PHYS_BASE || !get_user (ks + length, us++)) @@ -549,22 +531,22 @@ diff -urp pintos.orig/src/userprog/syscall.c pintos/src/userprog/syscall.c + printf ("bad user reference (%p)\n", us + length); + thread_exit (); + } -+ ++ + if (ks[length] == '\0') + return ks; + } -+ ++ + printf ("copy_in_string: string too long\n"); -+ palloc_free (ks); ++ palloc_free_page (ks); + thread_exit (); +} -+ ++ +static int +sys_halt (void) +{ + power_off (); +} -+ ++ +static int +sys_exit (int ret_code) +{ @@ -572,76 +554,76 @@ diff -urp pintos.orig/src/userprog/syscall.c pintos/src/userprog/syscall.c + thread_exit (); + NOT_REACHED (); +} -+ ++ +static int +sys_exec (const char *ufile) +{ + tid_t tid; + char *kfile = copy_in_string (ufile); -+ ++ + lock_acquire (&fs_lock); + tid = process_execute (kfile); + lock_release (&fs_lock); -+ -+ palloc_free (kfile); -+ ++ ++ palloc_free_page (kfile); ++ + return tid; +} -+ ++ +static int +sys_join (tid_t child) +{ + return thread_join (child); +} -+ ++ +static int +sys_create (const char *ufile, unsigned initial_size) +{ + char *kfile = copy_in_string (ufile); + bool ok; -+ ++ + lock_acquire (&fs_lock); + ok = filesys_create (kfile, initial_size); + lock_release (&fs_lock); -+ -+ palloc_free (kfile); -+ ++ ++ palloc_free_page (kfile); ++ + return ok; +} -+ ++ +static int +sys_remove (const char *ufile) +{ + char *kfile = copy_in_string (ufile); + bool ok; -+ ++ + lock_acquire (&fs_lock); + ok = filesys_remove (kfile); + lock_release (&fs_lock); -+ -+ palloc_free (kfile); -+ ++ ++ palloc_free_page (kfile); ++ + return ok; +} -+ ++ +struct fildes + { + list_elem elem; + struct file *file; + int handle; + }; -+ ++ +static int +sys_open (const char *ufile) +{ + char *kfile = copy_in_string (ufile); + struct fildes *fd; + int handle = -1; -+ ++ + fd = malloc (sizeof *fd); + if (fd == NULL) + goto exit; -+ ++ + lock_acquire (&fs_lock); + fd->file = filesys_open (kfile); + if (fd->file != NULL) @@ -653,18 +635,18 @@ diff -urp pintos.orig/src/userprog/syscall.c pintos/src/userprog/syscall.c + else + free (fd); + lock_release (&fs_lock); -+ ++ + exit: -+ palloc_free (kfile); ++ palloc_free_page (kfile); + return handle; +} -+ ++ +static struct fildes * +lookup_fd (int handle) +{ + struct thread *cur = thread_current (); + list_elem *e; -+ ++ + for (e = list_begin (&cur->fds); e != list_end (&cur->fds); + e = list_next (e)) + { @@ -672,31 +654,31 @@ diff -urp pintos.orig/src/userprog/syscall.c pintos/src/userprog/syscall.c + if (fd->handle == handle) + return fd; + } -+ ++ + printf ("no handle %d\n", handle); thread_exit (); } -+ ++ +static int +sys_filesize (int handle) +{ + struct fildes *fd = lookup_fd (handle); + int size; -+ ++ + lock_acquire (&fs_lock); + size = file_length (fd->file); + lock_release (&fs_lock); -+ ++ + return size; +} -+ ++ +static int +sys_read (int handle, void *udst_, unsigned size) +{ + uint8_t *udst = udst_; + struct fildes *fd; + int bytes_read = 0; -+ ++ + if (handle == STDIN_FILENO) + { + for (bytes_read = 0; (size_t) bytes_read < size; bytes_read++) @@ -704,7 +686,7 @@ diff -urp pintos.orig/src/userprog/syscall.c pintos/src/userprog/syscall.c + thread_exit (); + return bytes_read; + } -+ ++ + lock_acquire (&fs_lock); + fd = lookup_fd (handle); + while (size > 0) @@ -712,13 +694,13 @@ diff -urp pintos.orig/src/userprog/syscall.c pintos/src/userprog/syscall.c + size_t page_left = PGSIZE - pg_ofs (udst); + size_t read_amt = size < page_left ? size : page_left; + off_t retval; -+ ++ + if (!verify_user (udst)) + { + lock_release (&fs_lock); + thread_exit (); + } -+ ++ + retval = file_read (fd->file, udst, read_amt); + if (retval < 0) + { @@ -726,26 +708,26 @@ diff -urp pintos.orig/src/userprog/syscall.c pintos/src/userprog/syscall.c + bytes_read = -1; + break; + } -+ ++ + bytes_read += retval; + if (retval != (off_t) read_amt) + break; -+ ++ + udst += retval; + size -= retval; + } + lock_release (&fs_lock); -+ ++ + return bytes_read; +} -+ ++ +static int +sys_write (int handle, void *usrc_, unsigned size) +{ + uint8_t *usrc = usrc_; + struct fildes *fd = NULL; + int bytes_written = 0; -+ ++ + lock_acquire (&fs_lock); + if (handle != STDOUT_FILENO) + fd = lookup_fd (handle); @@ -754,13 +736,13 @@ diff -urp pintos.orig/src/userprog/syscall.c pintos/src/userprog/syscall.c + size_t page_left = PGSIZE - pg_ofs (usrc); + size_t write_amt = size < page_left ? size : page_left; + off_t retval; -+ ++ + if (!verify_user (usrc)) + { + lock_release (&fs_lock); + thread_exit (); + } -+ ++ + if (handle == STDOUT_FILENO) + { + putbuf (usrc, write_amt); @@ -774,44 +756,44 @@ diff -urp pintos.orig/src/userprog/syscall.c pintos/src/userprog/syscall.c + bytes_written = -1; + break; + } -+ ++ + bytes_written += retval; + if (retval != (off_t) write_amt) + break; -+ ++ + usrc += retval; + size -= retval; + } + lock_release (&fs_lock); -+ ++ + return bytes_written; +} -+ ++ +static int +sys_seek (int handle, unsigned position) +{ + struct fildes *fd = lookup_fd (handle); -+ ++ + lock_acquire (&fs_lock); + file_seek (fd->file, position); + lock_release (&fs_lock); -+ ++ + return 0; +} -+ ++ +static int +sys_tell (int handle) +{ + struct fildes *fd = lookup_fd (handle); + unsigned position; -+ ++ + lock_acquire (&fs_lock); + position = file_tell (fd->file); + lock_release (&fs_lock); -+ ++ + return position; +} -+ ++ +static int +sys_close (int handle) +{ @@ -823,13 +805,13 @@ diff -urp pintos.orig/src/userprog/syscall.c pintos/src/userprog/syscall.c + free (fd); + return 0; +} -+ ++ +void +syscall_exit (void) +{ + struct thread *cur = thread_current (); + list_elem *e, *next; -+ ++ + for (e = list_begin (&cur->fds); e != list_end (&cur->fds); e = next) + { + struct fildes *fd = list_entry (e, struct fildes, elem); @@ -838,9 +820,9 @@ diff -urp pintos.orig/src/userprog/syscall.c pintos/src/userprog/syscall.c + free (fd); + } +} -diff -urp pintos.orig/src/userprog/syscall.h pintos/src/userprog/syscall.h +diff -urpN pintos.orig/src/userprog/syscall.h pintos/src/userprog/syscall.h --- pintos.orig/src/userprog/syscall.h 2004-09-05 22:38:45.000000000 -0700 -+++ pintos/src/userprog/syscall.h 2004-09-22 00:30:05.000000000 -0700 ++++ pintos/src/userprog/syscall.h 2004-09-27 16:41:17.000000000 -0700 @@ -2,5 +2,6 @@ #define USERPROG_SYSCALL_H diff --git a/solutions/p3.patch b/solutions/p3.patch new file mode 100644 index 0000000..078d12a --- /dev/null +++ b/solutions/p3.patch @@ -0,0 +1,1424 @@ +diff -urpN pintos.orig/src/Makefile.build pintos/src/Makefile.build +--- pintos.orig/src/Makefile.build 2004-09-20 20:26:41.000000000 -0700 ++++ pintos/src/Makefile.build 2004-09-27 13:29:43.000000000 -0700 +@@ -51,8 +51,9 @@ userprog_SRC += userprog/syscall.c # Sys + userprog_SRC += userprog/gdt.c # GDT initialization. + userprog_SRC += userprog/tss.c # TSS management. + +-# No virtual memory code yet. +-#vm_SRC = vm/filename.c # Some file. ++# Virtual memory code. ++vm_SRC = vm/pageframe.c # Page frame management. ++vm_SRC += vm/swap.c # Swap file management. + + # Filesystem code. + filesys_SRC = filesys/filesys.c # Filesystem core. +diff -urpN pintos.orig/src/constants.h pintos/src/constants.h +--- pintos.orig/src/constants.h 2004-09-21 17:26:39.000000000 -0700 ++++ pintos/src/constants.h 2004-09-27 13:29:43.000000000 -0700 +@@ -8,4 +8,4 @@ + /*#define MACRONAME 1 */ + + /* Uncomment if if you've implemented thread_join(). */ +-/*#define THREAD_JOIN_IMPLEMENTED 1*/ ++#define THREAD_JOIN_IMPLEMENTED 1 +diff -urpN pintos.orig/src/threads/init.c pintos/src/threads/init.c +--- pintos.orig/src/threads/init.c 2004-09-26 14:15:17.000000000 -0700 ++++ pintos/src/threads/init.c 2004-09-27 16:08:03.000000000 -0700 +@@ -21,12 +21,15 @@ + #include "threads/test.h" + #include "threads/thread.h" + #ifdef USERPROG ++#include "userprog/pagedir.h" + #include "userprog/process.h" + #include "userprog/exception.h" + #include "userprog/gdt.h" + #include "userprog/syscall.h" + #include "userprog/tss.h" + #endif ++#include "vm/pageframe.h" ++#include "vm/swap.h" + #ifdef FILESYS + #include "devices/disk.h" + #include "filesys/filesys.h" +@@ -78,6 +81,7 @@ main (void) + /* Initialize memory system, segments, paging. */ + palloc_init (); + paging_init (); ++ pageframe_init (); + #ifdef USERPROG + tss_init (); + gdt_init (); +@@ -105,6 +109,7 @@ main (void) + disk_init (); + filesys_init (format_filesys); + fsutil_run (); ++ swap_init (); + #endif + + printf ("Boot complete.\n"); +diff -urpN pintos.orig/src/threads/palloc.c pintos/src/threads/palloc.c +diff -urpN pintos.orig/src/threads/palloc.h pintos/src/threads/palloc.h +diff -urpN pintos.orig/src/threads/synch.c pintos/src/threads/synch.c +--- pintos.orig/src/threads/synch.c 2004-09-19 21:29:53.000000000 -0700 ++++ pintos/src/threads/synch.c 2004-09-27 13:29:43.000000000 -0700 +@@ -330,3 +330,35 @@ cond_name (const struct condition *cond) + + return cond->name; + } ++ ++void ++latch_init (struct latch *latch, const char *name) ++{ ++ latch->released = false; ++ lock_init (&latch->monitor_lock, name); ++ cond_init (&latch->rel_cond, name); ++} ++ ++void ++latch_acquire (struct latch *latch) ++{ ++ lock_acquire (&latch->monitor_lock); ++ if (!latch->released) ++ { ++ cond_wait (&latch->rel_cond, &latch->monitor_lock); ++ ASSERT (latch->released); ++ } ++ lock_release (&latch->monitor_lock); ++} ++ ++void ++latch_release (struct latch *latch) ++{ ++ lock_acquire (&latch->monitor_lock); ++ if (!latch->released) ++ { ++ latch->released = true; ++ cond_signal (&latch->rel_cond, &latch->monitor_lock); ++ } ++ lock_release (&latch->monitor_lock); ++} +diff -urpN pintos.orig/src/threads/synch.h pintos/src/threads/synch.h +--- pintos.orig/src/threads/synch.h 2004-09-19 21:29:53.000000000 -0700 ++++ pintos/src/threads/synch.h 2004-09-27 13:29:43.000000000 -0700 +@@ -44,4 +44,16 @@ void cond_signal (struct condition *, st + void cond_broadcast (struct condition *, struct lock *); + const char *cond_name (const struct condition *); + ++/* Latch. */ ++struct latch ++ { ++ bool released; /* Released yet? */ ++ struct lock monitor_lock; /* Monitor lock. */ ++ struct condition rel_cond; /* Signaled when released. */ ++ }; ++ ++void latch_init (struct latch *, const char *); ++void latch_acquire (struct latch *); ++void latch_release (struct latch *); ++ + #endif /* threads/synch.h */ +diff -urpN pintos.orig/src/threads/thread.c pintos/src/threads/thread.c +--- pintos.orig/src/threads/thread.c 2004-09-26 14:15:17.000000000 -0700 ++++ pintos/src/threads/thread.c 2004-09-27 13:29:43.000000000 -0700 +@@ -13,6 +13,7 @@ + #include "threads/synch.h" + #ifdef USERPROG + #include "userprog/process.h" ++#include "userprog/syscall.h" + #endif + + /* Random value for struct thread's `magic' member. +@@ -80,6 +81,7 @@ thread_init (void) + init_thread (initial_thread, "main", PRI_DEFAULT); + initial_thread->status = THREAD_RUNNING; + initial_thread->tid = allocate_tid (); ++ sema_up (&initial_thread->can_die); + } + + /* Starts preemptive thread scheduling by enabling interrupts. +@@ -148,6 +150,7 @@ thread_create (const char *name, int pri + /* Initialize thread. */ + init_thread (t, name, priority); + tid = t->tid = allocate_tid (); ++ list_push_back (&thread_current ()->children, &t->children_elem); + + /* Stack frame for kernel_thread(). */ + kf = alloc_frame (t, sizeof *kf); +@@ -224,16 +227,36 @@ thread_tid (void) + void + thread_exit (void) + { ++ struct thread *t = thread_current (); ++ list_elem *e, *next; ++ + ASSERT (!intr_context ()); + + #ifdef USERPROG + process_exit (); + #endif ++ syscall_exit (); ++ ++ /* Notify our parent that we're dying. */ ++ latch_release (&t->ready_to_die); ++ ++ /* Notify our children that they can die. */ ++ for (e = list_begin (&t->children); e != list_end (&t->children); ++ e = next) ++ { ++ struct thread *child = list_entry (e, struct thread, children_elem); ++ next = list_next (e); ++ list_remove (e); ++ sema_up (&child->can_die); ++ } ++ ++ /* Wait until our parent is ready for us to die. */ ++ sema_down (&t->can_die); + + /* Just set our status to dying and schedule another process. + We will be destroyed during the call to schedule_tail(). */ + intr_disable (); +- thread_current ()->status = THREAD_DYING; ++ t->status = THREAD_DYING; + schedule (); + NOT_REACHED (); + } +@@ -270,6 +293,26 @@ thread_block (void) + thread_current ()->status = THREAD_BLOCKED; + schedule (); + } ++ ++/* Waits for thread with tid CHILD_TID to die. */ ++int ++thread_join (tid_t child_tid) ++{ ++ struct thread *cur = thread_current (); ++ list_elem *e; ++ ++ for (e = list_begin (&cur->children); e != list_end (&cur->children); ) ++ { ++ struct thread *child = list_entry (e, struct thread, children_elem); ++ e = list_next (e); ++ if (child->tid == child_tid) ++ { ++ latch_acquire (&child->ready_to_die); ++ return child->ret_code; ++ } ++ } ++ return -1; ++} + + /* Idle thread. Executes when no other thread is ready to run. */ + static void +@@ -335,6 +378,12 @@ init_thread (struct thread *t, const cha + strlcpy (t->name, name, sizeof t->name); + t->stack = (uint8_t *) t + PGSIZE; + t->priority = priority; ++ latch_init (&t->ready_to_die, "ready-to-die"); ++ sema_init (&t->can_die, 0, "can-die"); ++ list_init (&t->children); ++ t->ret_code = -1; ++ list_init (&t->fds); ++ t->next_handle = 2; + t->magic = THREAD_MAGIC; + } + +diff -urpN pintos.orig/src/threads/thread.h pintos/src/threads/thread.h +--- pintos.orig/src/threads/thread.h 2004-09-26 14:15:17.000000000 -0700 ++++ pintos/src/threads/thread.h 2004-09-27 13:29:43.000000000 -0700 +@@ -2,8 +2,10 @@ + #define THREADS_THREAD_H + + #include ++#include + #include + #include ++#include "threads/synch.h" + + /* States in a thread's life cycle. */ + enum thread_status +@@ -89,12 +91,24 @@ struct thread + uint8_t *stack; /* Saved stack pointer. */ + int priority; /* Priority. */ + ++ /* Members for implementing thread_join(). */ ++ struct latch ready_to_die; /* Release when thread about to die. */ ++ struct semaphore can_die; /* Up when thread allowed to die. */ ++ struct list children; /* List of child threads. */ ++ list_elem children_elem; /* Element of `children' list. */ ++ int ret_code; /* Return status. */ ++ + /* Shared between thread.c and synch.c. */ + list_elem elem; /* List element. */ + + #ifdef USERPROG + /* Owned by userprog/process.c. */ + uint32_t *pagedir; /* Page directory. */ ++ struct hash pages; /* Hash of `struct user_page's. */ ++ ++ /* Owned by syscall.c. */ ++ struct list fds; /* List of file descriptors. */ ++ int next_handle; /* Next handle value. */ + #endif + + /* Owned by thread.c */ +@@ -119,7 +133,7 @@ void thread_yield (void); + void thread_block (void); + + /* This function will be implemented in problem 1-2. */ +-void thread_join (tid_t); ++int thread_join (tid_t); + + /* These functions will be implemented in problem 1-3. */ + void thread_set_priority (int); +diff -urpN pintos.orig/src/userprog/exception.c pintos/src/userprog/exception.c +--- pintos.orig/src/userprog/exception.c 2004-09-26 14:15:17.000000000 -0700 ++++ pintos/src/userprog/exception.c 2004-09-27 13:29:44.000000000 -0700 +@@ -1,9 +1,16 @@ + #include "userprog/exception.h" + #include + #include ++#include + #include "userprog/gdt.h" ++#include "userprog/pagedir.h" ++#include "userprog/process.h" ++#include "filesys/file.h" + #include "threads/interrupt.h" ++#include "threads/mmu.h" + #include "threads/thread.h" ++#include "vm/pageframe.h" ++#include "vm/swap.h" + + /* Number of page faults processed. */ + static long long page_fault_cnt; +@@ -124,10 +131,13 @@ kill (struct intr_frame *f) + static void + page_fault (struct intr_frame *f) + { ++ struct thread *t; + bool not_present; /* True: not-present page, false: writing r/o page. */ + bool write; /* True: access was write, false: access was read. */ + bool user; /* True: access by user, false: access by kernel. */ + void *fault_addr; /* Fault address. */ ++ struct user_page tmp_up, *up; ++ hash_elem *e; + + /* Obtain faulting address, the virtual address that was + accessed to cause the fault. It may point to code or to +@@ -147,14 +157,62 @@ page_fault (struct intr_frame *f) + write = (f->error_code & PF_W) != 0; + user = (f->error_code & PF_U) != 0; + +- /* To implement virtual memory, delete the rest of the function +- body, and replace it with code that brings in the page to +- which fault_addr refers. */ +- printf ("Page fault at %p: %s error %s page in %s context.\n", +- fault_addr, +- not_present ? "not present" : "rights violation", +- write ? "writing" : "reading", +- user ? "user" : "kernel"); +- kill (f); ++ if (!not_present) ++ goto bad_access; ++ ++ t = thread_current (); ++ if (t->pagedir == NULL) ++ goto bad_access; ++ ++ //printf ("fault %p (page=%p)\n", fault_addr, pg_round_down (fault_addr)); ++ tmp_up.upage = pg_round_down (fault_addr); ++ e = hash_find (&t->pages, &tmp_up.elem); ++ if (e == NULL) ++ { ++ printf ("no user_page for %p\n", fault_addr); ++ goto bad_access; ++ } ++ up = hash_entry (e, struct user_page, elem); ++ ++ if (up->frame == NULL) ++ { ++ if (!pageframe_allocate (up)) ++ { ++ printf ("virtual memory exhausted, killing process\n"); ++ if (!user) ++ goto bad_access; ++ thread_exit (); ++ } ++ if (up->file != NULL) ++ { ++ off_t amt = file_read_at (up->file, ++ up->frame->kpage, up->file_size, ++ up->file_ofs); ++ ASSERT (amt == (off_t) up->file_size); ++ memset (up->frame->kpage + up->file_size, 0, PGSIZE - up->file_size); ++ } ++ else if (up->swap_page != SIZE_MAX) ++ swap_read (up); ++ else ++ memset (up->frame->kpage, 0, PGSIZE); ++ } ++ pagedir_set_page (t->pagedir, up->upage, up->frame->kpage, true); ++ return; ++ ++ bad_access: ++ if (user || fault_addr > PHYS_BASE) ++ { ++ printf ("Page fault at %p: %s error %s page in %s context.\n", ++ fault_addr, ++ not_present ? "not present" : "rights violation", ++ write ? "writing" : "reading", ++ user ? "user" : "kernel"); ++ kill (f); ++ } ++ else ++ { ++ f->eip = (void (*) (void)) f->eax; ++ f->eax = 0; ++ } + } + +diff -urpN pintos.orig/src/userprog/process.c pintos/src/userprog/process.c +--- pintos.orig/src/userprog/process.c 2004-09-22 17:58:29.000000000 -0700 ++++ pintos/src/userprog/process.c 2004-09-27 14:43:09.000000000 -0700 +@@ -7,15 +7,18 @@ + #include "userprog/gdt.h" + #include "userprog/pagedir.h" + #include "userprog/tss.h" ++#include "vm/pageframe.h" + #include "filesys/directory.h" + #include "filesys/file.h" + #include "filesys/filesys.h" + #include "threads/flags.h" + #include "threads/init.h" + #include "threads/interrupt.h" ++#include "threads/malloc.h" + #include "threads/mmu.h" + #include "threads/palloc.h" + #include "threads/thread.h" ++#include "vm/swap.h" + + static thread_func execute_thread NO_RETURN; + static bool load (const char *cmdline, void (**eip) (void), void **esp); +@@ -100,6 +103,10 @@ process_exit (void) + cur->pagedir = NULL; + pagedir_activate (NULL); + pagedir_destroy (pd); ++ ++ /* We maintain the invariant that `hash' is initialized iff ++ `pd != NULL'. */ ++ hash_destroy (&cur->pages); + } + } + +@@ -182,7 +189,10 @@ struct Elf32_Phdr + #define PF_R 4 /* Readable. */ + + static bool load_segment (struct file *, const struct Elf32_Phdr *); +-static bool setup_stack (void **esp); ++static bool setup_stack (const char *cmdline, void **esp); ++static unsigned user_page_hash (const hash_elem *, void *); ++static bool user_page_less (const hash_elem *, const hash_elem *, void *); ++static struct user_page *make_user_page (void *upage); + + /* Aborts loading an executable, with an error message. */ + #define LOAD_ERROR(MSG) \ +@@ -198,19 +208,35 @@ static bool setup_stack (void **esp); + and its initial stack pointer into *ESP. + Returns true if successful, false otherwise. */ + bool +-load (const char *filename, void (**eip) (void), void **esp) ++load (const char *cmdline, void (**eip) (void), void **esp) + { + struct thread *t = thread_current (); ++ char filename[NAME_MAX + 2]; + struct Elf32_Ehdr ehdr; + struct file *file = NULL; + off_t file_ofs; + bool success = false; ++ char *cp; + int i; + ++ /* Create hash of user pages. */ ++ hash_init (&t->pages, user_page_hash, user_page_less, NULL); ++ + /* Allocate page directory. */ + t->pagedir = pagedir_create (); +- if (t->pagedir == NULL) +- LOAD_ERROR (("page directory allocation failed")); ++ if (t->pagedir == NULL) ++ { ++ hash_destroy (&t->pages); ++ LOAD_ERROR (("page directory allocation failed")); ++ } ++ ++ /* Extract filename from command line. */ ++ while (*cmdline == ' ') ++ cmdline++; ++ strlcpy (filename, cmdline, sizeof filename); ++ cp = strchr (filename, ' '); ++ if (cp != NULL) ++ *cp = '\0'; + + /* Open executable file. */ + file = filesys_open (filename); +@@ -269,8 +295,23 @@ load (const char *filename, void (**eip) + } + + /* Set up stack. */ +- if (!setup_stack (esp)) ++ if (!setup_stack (cmdline, esp)) + goto done; ++ ++#if 0 ++ { ++ struct hash_iterator i; ++ ++ hash_first (&i, &thread_current ()->pages); ++ while (hash_next (&i)) ++ { ++ struct user_page *up = hash_entry (hash_cur (&i), ++ struct user_page, elem); ++ printf ("%08x ", up->upage); ++ } ++ printf ("\n"); ++ } ++#endif + + /* Start address. */ + *eip = (void (*) (void)) ehdr.e_entry; +@@ -279,14 +320,12 @@ load (const char *filename, void (**eip) + + done: + /* We arrive here whether the load is successful or not. */ +- file_close (file); ++ //file_close (file); // FIXME + return success; + } + + /* load() helpers. */ + +-static bool install_page (void *upage, void *kpage); +- + /* Loads the segment described by PHDR from FILE into user + address space. Return true if successful, false otherwise. */ + static bool +@@ -296,6 +335,7 @@ load_segment (struct file *file, const s + uint8_t *upage; /* Iterator from start to end. */ + off_t filesz_left; /* Bytes left of file data (as opposed to + zero-initialized bytes). */ ++ off_t file_ofs; + + /* Is this a read-only segment? Not currently used, so it's + commented out. You'll want to use it when implementing VM +@@ -340,70 +380,206 @@ load_segment (struct file *file, const s + + /* Load the segment page-by-page into memory. */ + filesz_left = phdr->p_filesz + (phdr->p_vaddr & PGMASK); +- file_seek (file, ROUND_DOWN (phdr->p_offset, PGSIZE)); ++ file_ofs = ROUND_DOWN (phdr->p_offset, PGSIZE); + for (upage = start; upage < (uint8_t *) end; upage += PGSIZE) + { + /* We want to read min(PGSIZE, filesz_left) bytes from the + file into the page and zero the rest. */ + size_t read_bytes = filesz_left >= PGSIZE ? PGSIZE : filesz_left; +- size_t zero_bytes = PGSIZE - read_bytes; +- uint8_t *kpage = palloc_get_page (PAL_USER); +- if (kpage == NULL) ++ struct user_page *up = make_user_page (upage); ++ if (up == NULL) + return false; + +- /* Do the reading and zeroing. */ +- if (file_read (file, kpage, read_bytes) != (int) read_bytes) ++ if (read_bytes > 0) + { +- palloc_free_page (kpage); +- return false; ++ /* Map page. */ ++ up->file = file; ++ up->file_ofs = file_ofs; ++ up->file_size = read_bytes; ++ file_ofs += read_bytes; + } +- memset (kpage + read_bytes, 0, zero_bytes); +- filesz_left -= read_bytes; +- +- /* Add the page to the process's address space. */ +- if (!install_page (upage, kpage)) ++ else + { +- palloc_free_page (kpage); +- return false; ++ /* Page is all zeros. Nothing to do. */ + } ++ filesz_left -= read_bytes; + } ++ ++ return true; ++} ++ ++static void ++reverse (int argc, char **argv) ++{ ++ for (; argc > 1; argc -= 2, argv++) ++ { ++ char *tmp = argv[0]; ++ argv[0] = argv[argc - 1]; ++ argv[argc - 1] = tmp; ++ } ++} ++static void * ++push (uint8_t *kpage, size_t *ofs, const void *buf, size_t size) ++{ ++ size_t padsize = ROUND_UP (size, sizeof (uint32_t)); ++ if (*ofs < padsize) ++ return NULL; ++ ++ *ofs -= padsize; ++ memcpy (kpage + *ofs + (padsize - size), buf, size); ++ return kpage + *ofs + (padsize - size); ++} ++ ++static bool ++init_cmdline (uint8_t *kpage, uint8_t *upage, const char *cmdline, ++ void **esp) ++{ ++ size_t ofs = PGSIZE; ++ char *const null = NULL; ++ char *cmdline_copy; ++ char *karg, *saveptr; ++ int argc; ++ char **argv; ++ ++ /* Push command line string. */ ++ cmdline_copy = push (kpage, &ofs, cmdline, strlen (cmdline) + 1); ++ if (cmdline_copy == NULL) ++ return false; ++ ++ if (push (kpage, &ofs, &null, sizeof null) == NULL) ++ return false; ++ ++ /* Parse command line into arguments ++ and push them in reverse order. */ ++ argc = 0; ++ for (karg = strtok_r (cmdline_copy, " ", &saveptr); karg != NULL; ++ karg = strtok_r (NULL, " ", &saveptr)) ++ { ++ char *uarg = upage + (karg - (char *) kpage); ++ if (push (kpage, &ofs, &uarg, sizeof uarg) == NULL) ++ return false; ++ argc++; ++ } ++ ++ /* Reverse the order of the command line arguments. */ ++ argv = (char **) (upage + ofs); ++ reverse (argc, (char **) (kpage + ofs)); ++ ++ /* Push argv, argc, "return address". */ ++ if (push (kpage, &ofs, &argv, sizeof argv) == NULL ++ || push (kpage, &ofs, &argc, sizeof argc) == NULL ++ || push (kpage, &ofs, &null, sizeof null) == NULL) ++ return false; ++ ++ /* Set initial stack pointer. */ ++ *esp = upage + ofs; + + return true; + } + +-/* Create a minimal stack by mapping a zeroed page at the top of +- user virtual memory. */ ++/* Create a minimal stack for T by mapping a page at the ++ top of user virtual memory. Fills in the page using CMDLINE ++ and sets *ESP to the stack pointer. */ + static bool +-setup_stack (void **esp) ++setup_stack (const char *cmdline, void **esp) + { +- uint8_t *kpage; +- bool success = false; ++ struct user_page *up = make_user_page ((uint8_t *) PHYS_BASE - PGSIZE); ++ return (up != NULL ++ && pageframe_allocate (up) ++ && init_cmdline (up->frame->kpage, up->upage, cmdline, esp)); ++} ++ ++static unsigned ++user_page_hash (const hash_elem *e, void *aux UNUSED) ++{ ++ struct user_page *up = hash_entry (e, struct user_page, elem); ++ return hash_bytes (&up->upage, sizeof up->upage); ++} + +- kpage = palloc_get_page (PAL_USER | PAL_ZERO); +- if (kpage != NULL) ++static bool ++user_page_less (const hash_elem *a_, const hash_elem *b_, void *aux UNUSED) ++{ ++ struct user_page *a = hash_entry (a_, struct user_page, elem); ++ struct user_page *b = hash_entry (b_, struct user_page, elem); ++ ++ return a->upage < b->upage; ++} ++ ++static struct user_page * ++make_user_page (void *upage) ++{ ++ struct user_page *up; ++ ++ up = malloc (sizeof *up); ++ if (up != NULL) + { +- success = install_page (((uint8_t *) PHYS_BASE) - PGSIZE, kpage); +- if (success) +- *esp = PHYS_BASE; ++ memset (up, 0, sizeof *up); ++ up->swap_page = SIZE_MAX; ++ ++ up->upage = upage; ++ if (hash_insert (&thread_current ()->pages, &up->elem) != NULL) ++ { ++ free (up); ++ up = NULL; ++ } ++#if 0 + else +- palloc_free_page (kpage); ++ printf ("make_user_page(%p) okay\n", upage); ++#endif + } +- else +- printf ("failed to allocate process stack\n"); + +- return success; ++ return up; + } + +-/* Adds a mapping from user virtual address UPAGE to kernel +- virtual address KPAGE to the page table. Fails if UPAGE is +- already mapped or if memory allocation fails. */ +-static bool +-install_page (void *upage, void *kpage) ++static void ++dump_page (struct user_page *up) + { +- struct thread *t = thread_current (); ++ off_t amt; + +- /* Verify that there's not already a page at that virtual +- address, then map our page there. */ +- return (pagedir_get_page (t->pagedir, upage) == NULL +- && pagedir_set_page (t->pagedir, upage, kpage, true)); ++ ASSERT (up->file != NULL); ++ up->file_size = PGSIZE; ++ amt = file_write_at (up->file, up->frame->kpage, ++ up->file_size, up->file_ofs); ++ ASSERT (amt == (off_t) up->file_size); + } ++ ++bool ++process_evict_page (struct thread *t, struct user_page *up) ++{ ++ ASSERT (up->frame != NULL); ++ ++ if (pagedir_test_accessed (t->pagedir, up->upage)) ++ { ++ pagedir_clear_accessed (t->pagedir, up->upage); ++ return false; ++ } ++ ++ if (up->file == NULL) ++ { ++ if (!swap_write (up)) ++ return false; ++ } ++ else if (pagedir_test_dirty (t->pagedir, up->upage)) ++ { ++ /* Need to write out. */ ++ if (up->private) ++ { ++ up->file = NULL; // FIXME ++ up->private = false; ++ if (!swap_write (up)) ++ return false; ++ } ++ ++ dump_page (up); ++ } ++ else ++ { ++ /* Already on disk, not dirty. ++ Nothing to do. */ ++ } ++ ++ pagedir_clear_page (t->pagedir, up->upage); ++ pageframe_free (up->frame); ++ return true; ++} ++ +diff -urpN pintos.orig/src/userprog/process.h pintos/src/userprog/process.h +--- pintos.orig/src/userprog/process.h 2004-09-21 22:42:17.000000000 -0700 ++++ pintos/src/userprog/process.h 2004-09-27 14:43:13.000000000 -0700 +@@ -2,9 +2,32 @@ + #define USERPROG_PROCESS_H + + #include "threads/thread.h" ++#include "filesys/off_t.h" ++ ++struct user_page ++ { ++ hash_elem elem; ++ void *upage; /* Virtual address of mapping. */ ++ ++ /* If FRAME is nonnull, the page is in memory. ++ If FILE is nonnull, the page is on disk. ++ If both are null, the page is all zeroes. ++ If both are nonnull, the page is in memory and backed by a ++ file mapping (not the swap file). */ ++ struct page_frame *frame; ++ size_t swap_page; ++ struct file *file; ++ off_t file_ofs; ++ size_t file_size; /* Number of bytes on disk, <= PGSIZE. */ ++ ++ bool dirty : 1; ++ bool accessed : 1; ++ bool private : 1; /* Write dirty pages to swap or to FILE? */ ++ }; + + tid_t process_execute (const char *filename); + void process_exit (void); + void process_activate (void); ++bool process_evict_page (struct thread *, struct user_page *); + + #endif /* userprog/process.h */ +diff -urpN pintos.orig/src/userprog/syscall.c pintos/src/userprog/syscall.c +--- pintos.orig/src/userprog/syscall.c 2004-09-26 14:15:17.000000000 -0700 ++++ pintos/src/userprog/syscall.c 2004-09-27 14:42:01.000000000 -0700 +@@ -1,20 +1,429 @@ + #include "userprog/syscall.h" + #include ++#include + #include ++#include "threads/init.h" + #include "threads/interrupt.h" ++#include "threads/malloc.h" ++#include "threads/mmu.h" ++#include "threads/palloc.h" + #include "threads/thread.h" ++#include "userprog/pagedir.h" ++#include "userprog/process.h" ++#include "filesys/filesys.h" ++#include "filesys/file.h" ++#include "devices/kbd.h" ++ ++typedef int syscall_function (int, int, int); ++ ++static int sys_halt (void); ++static int sys_exit (int status); ++static int sys_exec (const char *ufile); ++static int sys_join (tid_t); ++static int sys_create (const char *ufile, unsigned initial_size); ++static int sys_remove (const char *ufile); ++static int sys_open (const char *ufile); ++static int sys_filesize (int handle); ++static int sys_read (int handle, void *udst_, unsigned size); ++static int sys_write (int handle, void *usrc_, unsigned size); ++static int sys_seek (int handle, unsigned position); ++static int sys_tell (int handle); ++static int sys_close (int handle); ++ ++struct syscall ++ { ++ size_t arg_cnt; ++ syscall_function *func; ++ }; ++ ++struct syscall syscall_table[] = ++ { ++ {0, (syscall_function *) sys_halt}, ++ {1, (syscall_function *) sys_exit}, ++ {1, (syscall_function *) sys_exec}, ++ {1, (syscall_function *) sys_join}, ++ {2, (syscall_function *) sys_create}, ++ {1, (syscall_function *) sys_remove}, ++ {1, (syscall_function *) sys_open}, ++ {1, (syscall_function *) sys_filesize}, ++ {3, (syscall_function *) sys_read}, ++ {3, (syscall_function *) sys_write}, ++ {2, (syscall_function *) sys_seek}, ++ {1, (syscall_function *) sys_tell}, ++ {1, (syscall_function *) sys_close}, ++ }; ++static const int syscall_cnt = sizeof syscall_table / sizeof *syscall_table; + + static void syscall_handler (struct intr_frame *); ++static void copy_in (void *, const void *, size_t); ++ ++static struct lock fs_lock; + + void + syscall_init (void) + { + intr_register (0x30, 3, INTR_ON, syscall_handler, "syscall"); ++ lock_init (&fs_lock, "fs"); + } + + static void +-syscall_handler (struct intr_frame *f UNUSED) ++syscall_handler (struct intr_frame *f) ++{ ++ struct syscall *s; ++ int call_nr; ++ int args[3]; ++ ++ copy_in (&call_nr, f->esp, sizeof call_nr); ++ if (call_nr < 0 || call_nr >= syscall_cnt) ++ { ++ printf ("bad syscall number %d\n", call_nr); ++ thread_exit (); ++ } ++ ++ s = syscall_table + call_nr; ++ ASSERT (s->arg_cnt <= sizeof args / sizeof *args); ++ memset (args, 0, sizeof args); ++ copy_in (args, (uint32_t *) f->esp + 1, sizeof *args * s->arg_cnt); ++ f->eax = s->func (args[0], args[1], args[2]); ++} ++ ++static bool ++verify_user (const void *uaddr) ++{ ++ return pagedir_get_page (thread_current ()->pagedir, uaddr) != NULL; ++} ++ ++static inline bool get_user (uint8_t *dst, const uint8_t *usrc) { ++ int eax; ++ asm ("movl $1f, %%eax; movb %2, %%al; movb %%al, %0; 1:" ++ : "=m" (*dst), "=&a" (eax) : "m" (*usrc)); ++ return eax != 0; ++} ++ ++static inline bool put_user (uint8_t *udst, uint8_t byte) { ++ int eax; ++ asm ("movl $1f, %%eax; movb %b2, %0; 1:" ++ : "=m" (*udst), "=&a" (eax) : "r" (byte)); ++ return eax != 0; ++} ++ ++static void ++copy_in (void *dst_, const void *usrc_, size_t size) ++{ ++ uint8_t *dst = dst_; ++ const uint8_t *usrc = usrc_; ++ ++ for (; size > 0; size--, dst++, usrc++) ++ if (usrc >= (uint8_t *) PHYS_BASE || !get_user (dst, usrc)) ++ thread_exit (); ++} ++ ++static char * ++copy_in_string (const char *us) ++{ ++ char *ks; ++ size_t length; ++ ++ ks = palloc_get_page (0); ++ if (ks == NULL) ++ { ++ printf ("copy_in_string: out of memory\n"); ++ thread_exit (); ++ } ++ ++ for (length = 0; length < PGSIZE; length++) ++ { ++ if (us >= (char *) PHYS_BASE || !get_user (ks + length, us++)) ++ { ++ printf ("bad user reference (%p)\n", us + length); ++ thread_exit (); ++ } ++ ++ if (ks[length] == '\0') ++ return ks; ++ } ++ ++ printf ("copy_in_string: string too long\n"); ++ palloc_free_page (ks); ++ thread_exit (); ++} ++ ++static int ++sys_halt (void) ++{ ++ power_off (); ++} ++ ++static int ++sys_exit (int ret_code) + { +- printf ("system call!\n"); ++ thread_current ()->ret_code = ret_code; + thread_exit (); ++ NOT_REACHED (); ++} ++ ++static int ++sys_exec (const char *ufile) ++{ ++ tid_t tid; ++ char *kfile = copy_in_string (ufile); ++ ++ lock_acquire (&fs_lock); ++ tid = process_execute (kfile); ++ lock_release (&fs_lock); ++ ++ palloc_free_page (kfile); ++ ++ return tid; ++} ++ ++static int ++sys_join (tid_t child) ++{ ++ return thread_join (child); ++} ++ ++static int ++sys_create (const char *ufile, unsigned initial_size) ++{ ++ char *kfile = copy_in_string (ufile); ++ bool ok; ++ ++ lock_acquire (&fs_lock); ++ ok = filesys_create (kfile, initial_size); ++ lock_release (&fs_lock); ++ ++ palloc_free_page (kfile); ++ ++ return ok; ++} ++ ++static int ++sys_remove (const char *ufile) ++{ ++ char *kfile = copy_in_string (ufile); ++ bool ok; ++ ++ lock_acquire (&fs_lock); ++ ok = filesys_remove (kfile); ++ lock_release (&fs_lock); ++ ++ palloc_free_page (kfile); ++ ++ return ok; ++} ++ ++struct fildes ++ { ++ list_elem elem; ++ struct file *file; ++ int handle; ++ }; ++ ++static int ++sys_open (const char *ufile) ++{ ++ char *kfile = copy_in_string (ufile); ++ struct fildes *fd; ++ int handle = -1; ++ ++ fd = malloc (sizeof *fd); ++ if (fd == NULL) ++ goto exit; ++ ++ lock_acquire (&fs_lock); ++ fd->file = filesys_open (kfile); ++ if (fd->file != NULL) ++ { ++ struct thread *cur = thread_current (); ++ handle = fd->handle = cur->next_handle++; ++ list_push_front (&cur->fds, &fd->elem); ++ } ++ else ++ free (fd); ++ lock_release (&fs_lock); ++ ++ exit: ++ palloc_free_page (kfile); ++ return handle; ++} ++ ++static struct fildes * ++lookup_fd (int handle) ++{ ++ struct thread *cur = thread_current (); ++ list_elem *e; ++ ++ for (e = list_begin (&cur->fds); e != list_end (&cur->fds); ++ e = list_next (e)) ++ { ++ struct fildes *fd = list_entry (e, struct fildes, elem); ++ if (fd->handle == handle) ++ return fd; ++ } ++ ++ printf ("no handle %d\n", handle); ++thread_exit (); ++} ++ ++static int ++sys_filesize (int handle) ++{ ++ struct fildes *fd = lookup_fd (handle); ++ int size; ++ ++ lock_acquire (&fs_lock); ++ size = file_length (fd->file); ++ lock_release (&fs_lock); ++ ++ return size; ++} ++ ++static int ++sys_read (int handle, void *udst_, unsigned size) ++{ ++ uint8_t *udst = udst_; ++ struct fildes *fd; ++ int bytes_read = 0; ++ ++ if (handle == STDIN_FILENO) ++ { ++ for (bytes_read = 0; (size_t) bytes_read < size; bytes_read++) ++ if (udst >= (uint8_t *) PHYS_BASE || !put_user (udst++, kbd_getc ())) ++ thread_exit (); ++ return bytes_read; ++ } ++ ++ lock_acquire (&fs_lock); ++ fd = lookup_fd (handle); ++ while (size > 0) ++ { ++ size_t page_left = PGSIZE - pg_ofs (udst); ++ size_t read_amt = size < page_left ? size : page_left; ++ off_t retval; ++ ++ if (!verify_user (udst)) ++ { ++ lock_release (&fs_lock); ++ thread_exit (); ++ } ++ ++ retval = file_read (fd->file, udst, read_amt); ++ if (retval < 0) ++ { ++ if (bytes_read == 0) ++ bytes_read = -1; ++ break; ++ } ++ ++ bytes_read += retval; ++ if (retval != (off_t) read_amt) ++ break; ++ ++ udst += retval; ++ size -= retval; ++ } ++ lock_release (&fs_lock); ++ ++ return bytes_read; ++} ++ ++static int ++sys_write (int handle, void *usrc_, unsigned size) ++{ ++ uint8_t *usrc = usrc_; ++ struct fildes *fd = NULL; ++ int bytes_written = 0; ++ ++ lock_acquire (&fs_lock); ++ if (handle != STDOUT_FILENO) ++ fd = lookup_fd (handle); ++ while (size > 0) ++ { ++ size_t page_left = PGSIZE - pg_ofs (usrc); ++ size_t write_amt = size < page_left ? size : page_left; ++ off_t retval; ++ ++ if (!verify_user (usrc)) ++ { ++ lock_release (&fs_lock); ++ thread_exit (); ++ } ++ ++ if (handle == STDOUT_FILENO) ++ { ++ putbuf (usrc, write_amt); ++ retval = write_amt; ++ } ++ else ++ retval = file_write (fd->file, usrc, write_amt); ++ if (retval < 0) ++ { ++ if (bytes_written == 0) ++ bytes_written = -1; ++ break; ++ } ++ ++ bytes_written += retval; ++ if (retval != (off_t) write_amt) ++ break; ++ ++ usrc += retval; ++ size -= retval; ++ } ++ lock_release (&fs_lock); ++ ++ return bytes_written; ++} ++ ++static int ++sys_seek (int handle, unsigned position) ++{ ++ struct fildes *fd = lookup_fd (handle); ++ ++ lock_acquire (&fs_lock); ++ file_seek (fd->file, position); ++ lock_release (&fs_lock); ++ ++ return 0; ++} ++ ++static int ++sys_tell (int handle) ++{ ++ struct fildes *fd = lookup_fd (handle); ++ unsigned position; ++ ++ lock_acquire (&fs_lock); ++ position = file_tell (fd->file); ++ lock_release (&fs_lock); ++ ++ return position; ++} ++ ++static int ++sys_close (int handle) ++{ ++ struct fildes *fd = lookup_fd (handle); ++ lock_acquire (&fs_lock); ++ file_close (fd->file); ++ lock_release (&fs_lock); ++ list_remove (&fd->elem); ++ free (fd); ++ return 0; ++} ++ ++void ++syscall_exit (void) ++{ ++ struct thread *cur = thread_current (); ++ list_elem *e, *next; ++ ++ for (e = list_begin (&cur->fds); e != list_end (&cur->fds); e = next) ++ { ++ struct fildes *fd = list_entry (e, struct fildes, elem); ++ next = list_next (e); ++ file_close (fd->file); ++ free (fd); ++ } + } +diff -urpN pintos.orig/src/userprog/syscall.h pintos/src/userprog/syscall.h +--- pintos.orig/src/userprog/syscall.h 2004-09-05 22:38:45.000000000 -0700 ++++ pintos/src/userprog/syscall.h 2004-09-27 13:29:44.000000000 -0700 +@@ -2,5 +2,6 @@ + #define USERPROG_SYSCALL_H + + void syscall_init (void); ++void syscall_exit (void); + + #endif /* userprog/syscall.h */ +diff -urpN pintos.orig/src/vm/pageframe.c pintos/src/vm/pageframe.c +--- pintos.orig/src/vm/pageframe.c 1969-12-31 16:00:00.000000000 -0800 ++++ pintos/src/vm/pageframe.c 2004-09-27 13:29:44.000000000 -0700 +@@ -0,0 +1,75 @@ ++#include "vm/pageframe.h" ++#include ++#include "threads/init.h" ++#include "threads/malloc.h" ++#include "threads/mmu.h" ++#include "threads/palloc.h" ++#include "userprog/process.h" ++ ++static struct page_frame *frames; ++static size_t frame_cnt; ++ ++static struct page_frame *next_frame; ++ ++static inline bool ++in_use (const struct page_frame *pf) ++{ ++ ASSERT ((pf->owner != NULL) == (pf->user_page != NULL)); ++ return pf->owner != NULL; ++} ++ ++void ++pageframe_init (void) ++{ ++ uint8_t *kpage; ++ ++ frame_cnt = ram_pages; ++ frames = calloc (sizeof *frames, frame_cnt); ++ if (frames == NULL) ++ PANIC ("can't allocate page frames"); ++ ++ while ((kpage = palloc_get_page (PAL_USER)) != NULL) ++ { ++ struct page_frame *pf = frames + (vtop (kpage) >> PGBITS); ++ pf->kpage = kpage; ++ } ++ ++ next_frame = frames; ++} ++ ++bool ++pageframe_allocate (struct user_page *up) ++{ ++ struct page_frame *pf; ++ size_t loops; ++ ++ ASSERT (up->frame == NULL); ++ ++ loops = 0; ++ do ++ { ++ pf = next_frame++; ++ if (next_frame >= frames + frame_cnt) ++ next_frame = frames; ++ if (loops++ > 2 * frame_cnt) ++ return false; ++ } ++ while (pf->kpage == NULL ++ || (in_use (pf) && !process_evict_page (pf->owner, pf->user_page))); ++ ++ ASSERT (!in_use (pf)); ++ pf->owner = thread_current (); ++ pf->user_page = up; ++ up->frame = pf; ++ return true; ++} ++ ++void ++pageframe_free (struct page_frame *pf) ++{ ++ ASSERT (in_use (pf)); ++ ++ pf->owner = NULL; ++ pf->user_page->frame = NULL; ++ pf->user_page = NULL; ++} +diff -urpN pintos.orig/src/vm/pageframe.h pintos/src/vm/pageframe.h +--- pintos.orig/src/vm/pageframe.h 1969-12-31 16:00:00.000000000 -0800 ++++ pintos/src/vm/pageframe.h 2004-09-27 13:29:44.000000000 -0700 +@@ -0,0 +1,17 @@ ++#ifndef VM_PAGEFRAME_H ++#define VM_PAGEFRAME_H 1 ++ ++#include ++ ++struct page_frame ++ { ++ void *kpage; ++ struct thread *owner; ++ struct user_page *user_page; ++ }; ++ ++void pageframe_init (void); ++bool pageframe_allocate (struct user_page *); ++void pageframe_free (struct page_frame *); ++ ++#endif /* vm/pageframe.h */ +diff -urpN pintos.orig/src/vm/swap.c pintos/src/vm/swap.c +--- pintos.orig/src/vm/swap.c 1969-12-31 16:00:00.000000000 -0800 ++++ pintos/src/vm/swap.c 2004-09-27 13:29:44.000000000 -0700 +@@ -0,0 +1,66 @@ ++#include "vm/swap.h" ++#include ++#include ++#include "vm/pageframe.h" ++#include "threads/mmu.h" ++#include "filesys/file.h" ++#include "filesys/filesys.h" ++#include "userprog/process.h" ++ ++static size_t swap_pages; ++static struct disk *swap_disk; ++static struct bitmap *used_pages; ++ ++void ++swap_init (void) ++{ ++ swap_disk = disk_get (1, 1); ++ if (swap_disk == NULL) ++ PANIC ("no swap disk"); ++ swap_pages = disk_size (swap_disk) / (PGSIZE / DISK_SECTOR_SIZE); ++ printf ("swap disk has room for %zu pages\n", swap_pages); ++ ++ used_pages = bitmap_create (swap_pages); ++ if (used_pages == NULL) ++ PANIC ("couldn't create swap bitmap"); ++} ++ ++bool ++swap_write (struct user_page *up) ++{ ++ size_t page; ++ disk_sector_t sector; ++ int i; ++ ++ ASSERT (up->frame != NULL); ++ ASSERT (up->file == NULL); ++ ++ page = bitmap_scan_and_flip (used_pages, 0, 1, false); ++ if (page == BITMAP_ERROR) ++ return false; ++ ++ up->swap_page = page; ++ sector = (disk_sector_t) page * (PGSIZE / DISK_SECTOR_SIZE); ++ for (i = 0; i < PGSIZE / DISK_SECTOR_SIZE; i++) ++ disk_write (swap_disk, sector++, up->frame->kpage + i * DISK_SECTOR_SIZE); ++ ++ return true; ++} ++ ++void ++swap_read (struct user_page *up) ++{ ++ disk_sector_t sector; ++ int i; ++ ++ ASSERT (up->frame != NULL); ++ ++ ASSERT (bitmap_test (used_pages, up->swap_page)); ++ bitmap_reset (used_pages, up->swap_page); ++ ++ sector = (disk_sector_t) up->swap_page * (PGSIZE / DISK_SECTOR_SIZE); ++ for (i = 0; i < PGSIZE / DISK_SECTOR_SIZE; i++) ++ disk_read (swap_disk, sector++, up->frame->kpage + i * DISK_SECTOR_SIZE); ++ ++ up->swap_page = SIZE_MAX; ++} +diff -urpN pintos.orig/src/vm/swap.h pintos/src/vm/swap.h +--- pintos.orig/src/vm/swap.h 1969-12-31 16:00:00.000000000 -0800 ++++ pintos/src/vm/swap.h 2004-09-27 13:29:44.000000000 -0700 +@@ -0,0 +1,11 @@ ++#ifndef VM_SWAP_H ++#define VM_SWAP_H 1 ++ ++#include ++ ++struct user_page; ++void swap_init (void); ++bool swap_write (struct user_page *); ++void swap_read (struct user_page *); ++ ++#endif /* vm/swap.h */