Rewrite palloc code so that it only touches the first 4 MB of RAM.
[pintos-anon] / src / threads / palloc.c
index 3e7c6a2a47f34679e0784a694871e7c1957be861..544dca935968c6eebdfde7986a33b41d20b8a7c2 100644 (file)
@@ -1,6 +1,7 @@
 #include "threads/palloc.h"
 #include <bitmap.h>
 #include <debug.h>
+#include <inttypes.h>
 #include <round.h>
 #include <stddef.h>
 #include <stdint.h>
 /* A memory pool. */
 struct pool
   {
-    struct lock lock;                   /* Mutual exclusion. */
-    struct bitmap *used_map;            /* Bitmap of free pages. */
-    uint8_t *start, *end;               /* Start and end of pool. */
+    size_t first_page;                  /* Page number of first page. */
+    size_t page_cnt;                    /* Number of pages. */
   };
 
+/* Tracks pages in use, with a lock protecting it. */
+static struct bitmap *used_map;
+static struct lock used_map_lock;
+
 /* Two pools: one for kernel data, one for user pages. */
-struct pool kernel_pool, user_pool;
+static struct pool kernel_pool, user_pool;
+
+/* Maximum number of pages to put in user pool. */
+size_t max_user_pages = SIZE_MAX;
 
-static void init_pool (struct pool *, void *start, void *end,
+static void init_pool (struct pool *pool, size_t first_page, size_t page_cnt,
                        const char *name);
 static bool page_from_pool (const struct pool *, void *page);
 
@@ -44,19 +51,37 @@ static bool page_from_pool (const struct pool *, void *page);
 void
 palloc_init (void) 
 {
-  /* End of the kernel as recorded by the linker.
-     See kernel.lds.S. */
-  extern char _end;
-
-  /* Free memory. */
-  uint8_t *free_start = pg_round_up (&_end);
-  uint8_t *free_end = ptov (ram_pages * PGSIZE);
-  size_t free_pages = (free_end - free_start) / PGSIZE;
-  uint8_t *free_middle = free_start + free_pages / 2 * PGSIZE;
-
-  /* Give half of memory to kernel, half to user. */
-  init_pool (&kernel_pool, free_start, free_middle, "kernel pool");
-  init_pool (&user_pool, free_middle, free_end, "user pool");
+  /* used_map from 1 MB as long as necessary. */
+  size_t bitmap_start = 1024;
+  size_t bitmap_pages = DIV_ROUND_UP (bitmap_needed_bytes (ram_pages), PGSIZE);
+
+  /* Free space from the bitmap to the end of RAM. */
+  size_t free_start = bitmap_start + bitmap_pages;
+  size_t free_pages = ram_pages - free_start;
+
+  /* Kernel and user get half of free space each.
+     User space can be limited by max_user_pages. */
+  size_t half_free = free_pages / 2;
+  size_t kernel_pages = half_free;
+  size_t user_pages = half_free < max_user_pages ? half_free : max_user_pages;
+
+  used_map = bitmap_create_preallocated (ram_pages,
+                                         ptov (bitmap_start * PGSIZE),
+                                         bitmap_pages * PGSIZE);
+  init_pool (&kernel_pool, free_start, kernel_pages, "kernel pool");
+  init_pool (&user_pool, free_start + kernel_pages, user_pages, "use pool");
+  lock_init (&used_map_lock, "used_map");
+}
+
+/* Initializes POOL to start (named NAME) at physical page number
+   FIRST_PAGE and continue for PAGE_CNT pages. */
+static void
+init_pool (struct pool *pool, size_t first_page, size_t page_cnt,
+           const char *name) 
+{
+  printf ("%zu pages available in %s.\n", page_cnt, name);
+  pool->first_page = first_page;
+  pool->page_cnt = page_cnt;
 }
 
 /* Obtains and returns a group of PAGE_CNT contiguous free pages.
@@ -68,18 +93,23 @@ palloc_init (void)
 void *
 palloc_get_multiple (enum palloc_flags flags, size_t page_cnt)
 {
-  struct pool *pool = flags & PAL_USER ? &user_pool : &kernel_pool;
+  struct pool *pool;
   void *pages;
   size_t page_idx;
 
   if (page_cnt == 0)
     return NULL;
 
-  lock_acquire (&pool->lock);
+  pool = flags & PAL_USER ? &user_pool : &kernel_pool;
+
+  lock_acquire (&used_map_lock);
+  page_idx = bitmap_scan_and_flip (used_map,
+                                   pool->first_page, pool->page_cnt,
+                                   false);
+  lock_release (&used_map_lock);
 
-  page_idx = bitmap_scan_and_flip (pool->used_map, 0, page_cnt, false);
   if (page_idx != BITMAP_ERROR)
-    pages = pool->start + PGSIZE * page_idx;
+    pages = ptov (PGSIZE * page_idx);
   else
     pages = NULL;
 
@@ -94,8 +124,6 @@ palloc_get_multiple (enum palloc_flags flags, size_t page_cnt)
         PANIC ("palloc_get: out of pages");
     }
 
-  lock_release (&pool->lock);
-  
   return pages;
 }
 
@@ -129,17 +157,14 @@ palloc_free_multiple (void *pages, size_t page_cnt)
   else
     NOT_REACHED ();
 
-  page_idx = pg_no (pages) - pg_no (pool->start);
-  ASSERT (pg_no (pages) + page_cnt <= pg_no (pool->end));
+  page_idx = vtop (pages) / PGSIZE;
 
 #ifndef NDEBUG
   memset (pages, 0xcc, PGSIZE * page_cnt);
 #endif
 
-  lock_acquire (&pool->lock);
-  ASSERT (bitmap_all (pool->used_map, page_idx, page_idx + page_cnt));
-  bitmap_set_multiple (pool->used_map, page_idx, page_idx + page_cnt, false);
-  lock_release (&pool->lock);
+  ASSERT (bitmap_all (used_map, page_idx, page_cnt));
+  bitmap_set_multiple (used_map, page_idx, page_cnt, false);
 }
 
 /* Frees the page at PAGE. */
@@ -149,36 +174,13 @@ palloc_free_page (void *page)
   palloc_free_multiple (page, 1);
 }
 
-/* Initializes pool P as starting at START and ending at END,
-   naming it NAME for debugging purposes. */
-static void
-init_pool (struct pool *p, void *start, void *end, const char *name) 
-{
-  size_t bitmap_size;
-  size_t page_cnt;
-
-  ASSERT (pg_ofs (start) == 0);
-  ASSERT (pg_ofs (end) == 0);
-  ASSERT (end > start);
-
-  page_cnt = pg_no (end) - pg_no (start);
-  printf ("%d kB allocated for %s.\n", (PGSIZE / 1024) * page_cnt, name);
-
-  lock_init (&p->lock, name);
-  bitmap_size = ROUND_UP (bitmap_needed_bytes (page_cnt), PGSIZE);
-  page_cnt -= bitmap_size / PGSIZE;
-  p->used_map = bitmap_create_preallocated (page_cnt, start, bitmap_size);
-  p->start = start + bitmap_size;
-  p->end = end;
-  ASSERT (p->end > p->start);
-}
-
 /* Returns true if PAGE was allocated from POOL,
    false otherwise. */
 static bool
-page_from_pool (const struct pool *pool, void *page_
+page_from_pool (const struct pool *pool, void *page) 
 {
-  uint8_t *page = page_;
+  size_t phys_page_no = vtop (page) / PGSIZE;
 
-  return page >= pool->start && page < pool->end;
+  return (phys_page_no >= pool->first_page
+          && phys_page_no < pool->first_page + pool->page_cnt);
 }