PKUOS - Pintos
Pintos source browser for PKU Operating System course
palloc.c
Go to the documentation of this file.
1#include "threads/palloc.h"
2#include <bitmap.h>
3#include <debug.h>
4#include <inttypes.h>
5#include <round.h>
6#include <stddef.h>
7#include <stdint.h>
8#include <stdio.h>
9#include <string.h>
10#include "threads/loader.h"
11#include "threads/synch.h"
12#include "threads/vaddr.h"
13
14/** Page allocator. Hands out memory in page-size (or
15 page-multiple) chunks. See malloc.h for an allocator that
16 hands out smaller chunks.
17
18 System memory is divided into two "pools" called the kernel
19 and user pools. The user pool is for user (virtual) memory
20 pages, the kernel pool for everything else. The idea here is
21 that the kernel needs to have memory for its own operations
22 even if user processes are swapping like mad.
23
24 By default, half of system RAM is given to the kernel pool and
25 half to the user pool. That should be huge overkill for the
26 kernel pool, but that's just fine for demonstration purposes. */
27
28/** A memory pool. */
29struct pool
30 {
31 struct lock lock; /**< Mutual exclusion. */
32 struct bitmap *used_map; /**< Bitmap of free pages. */
33 uint8_t *base; /**< Base of pool. */
34 };
35
36/** Two pools: one for kernel data, one for user pages. */
37static struct pool kernel_pool, user_pool;
38
39static void init_pool (struct pool *, void *base, size_t page_cnt,
40 const char *name);
41static bool page_from_pool (const struct pool *, void *page);
42
43/** Initializes the page allocator. At most USER_PAGE_LIMIT
44 pages are put into the user pool. */
45void
47{
48 /* Free memory starts at 1 MB and runs to the end of RAM. */
49 uint8_t *free_start = ptov (1024 * 1024);
50 uint8_t *free_end = ptov (init_ram_pages * PGSIZE);
51 size_t free_pages = (free_end - free_start) / PGSIZE;
52 size_t user_pages = free_pages / 2;
53 size_t kernel_pages;
54 if (user_pages > user_page_limit)
55 user_pages = user_page_limit;
56 kernel_pages = free_pages - user_pages;
57
58 /* Give half of memory to kernel, half to user. */
59 init_pool (&kernel_pool, free_start, kernel_pages, "kernel pool");
60 init_pool (&user_pool, free_start + kernel_pages * PGSIZE,
61 user_pages, "user pool");
62}
63
64/** Obtains and returns a group of PAGE_CNT contiguous free pages.
65 If PAL_USER is set, the pages are obtained from the user pool,
66 otherwise from the kernel pool. If PAL_ZERO is set in FLAGS,
67 then the pages are filled with zeros. If too few pages are
68 available, returns a null pointer, unless PAL_ASSERT is set in
69 FLAGS, in which case the kernel panics. */
70void *
71palloc_get_multiple (enum palloc_flags flags, size_t page_cnt)
72{
73 struct pool *pool = flags & PAL_USER ? &user_pool : &kernel_pool;
74 void *pages;
75 size_t page_idx;
76
77 if (page_cnt == 0)
78 return NULL;
79
81 page_idx = bitmap_scan_and_flip (pool->used_map, 0, page_cnt, false);
83
84 if (page_idx != BITMAP_ERROR)
85 pages = pool->base + PGSIZE * page_idx;
86 else
87 pages = NULL;
88
89 if (pages != NULL)
90 {
91 if (flags & PAL_ZERO)
92 memset (pages, 0, PGSIZE * page_cnt);
93 }
94 else
95 {
96 if (flags & PAL_ASSERT)
97 PANIC ("palloc_get: out of pages");
98 }
99
100 return pages;
101}
102
103/** Obtains a single free page and returns its kernel virtual
104 address.
105 If PAL_USER is set, the page is obtained from the user pool,
106 otherwise from the kernel pool. If PAL_ZERO is set in FLAGS,
107 then the page is filled with zeros. If no pages are
108 available, returns a null pointer, unless PAL_ASSERT is set in
109 FLAGS, in which case the kernel panics. */
110void *
112{
113 return palloc_get_multiple (flags, 1);
114}
115
116/** Frees the PAGE_CNT pages starting at PAGES. */
117void
118palloc_free_multiple (void *pages, size_t page_cnt)
119{
120 struct pool *pool;
121 size_t page_idx;
122
123 ASSERT (pg_ofs (pages) == 0);
124 if (pages == NULL || page_cnt == 0)
125 return;
126
127 if (page_from_pool (&kernel_pool, pages))
128 pool = &kernel_pool;
129 else if (page_from_pool (&user_pool, pages))
130 pool = &user_pool;
131 else
132 NOT_REACHED ();
133
134 page_idx = pg_no (pages) - pg_no (pool->base);
135
136#ifndef NDEBUG
137 memset (pages, 0xcc, PGSIZE * page_cnt);
138#endif
139
140 ASSERT (bitmap_all (pool->used_map, page_idx, page_cnt));
141 bitmap_set_multiple (pool->used_map, page_idx, page_cnt, false);
142}
143
144/** Frees the page at PAGE. */
145void
146palloc_free_page (void *page)
147{
148 palloc_free_multiple (page, 1);
149}
150
151/** Initializes pool P as starting at START and ending at END,
152 naming it NAME for debugging purposes. */
153static void
154init_pool (struct pool *p, void *base, size_t page_cnt, const char *name)
155{
156 /* We'll put the pool's used_map at its base.
157 Calculate the space needed for the bitmap
158 and subtract it from the pool's size. */
159 size_t bm_pages = DIV_ROUND_UP (bitmap_buf_size (page_cnt), PGSIZE);
160 if (bm_pages > page_cnt)
161 PANIC ("Not enough memory in %s for bitmap.", name);
162 page_cnt -= bm_pages;
163
164 printf ("%zu pages available in %s.\n", page_cnt, name);
165
166 /* Initialize the pool. */
167 lock_init (&p->lock);
168 p->used_map = bitmap_create_in_buf (page_cnt, base, bm_pages * PGSIZE);
169 p->base = base + bm_pages * PGSIZE;
170}
171
172/** Returns true if PAGE was allocated from POOL,
173 false otherwise. */
174static bool
175page_from_pool (const struct pool *pool, void *page)
176{
177 size_t page_no = pg_no (page);
178 size_t start_page = pg_no (pool->base);
179 size_t end_page = start_page + bitmap_size (pool->used_map);
180
181 return page_no >= start_page && page_no < end_page;
182}
size_t bitmap_size(const struct bitmap *b)
Bitmap size.
Definition: bitmap.c:136
void bitmap_set_multiple(struct bitmap *b, size_t start, size_t cnt, bool value)
Sets the CNT bits starting at START in B to VALUE.
Definition: bitmap.c:218
size_t bitmap_buf_size(size_t bit_cnt)
Returns the number of bytes required to accomodate a bitmap with BIT_CNT bits (for use with bitmap_cr...
Definition: bitmap.c:115
size_t bitmap_scan_and_flip(struct bitmap *b, size_t start, size_t cnt, bool value)
Finds the first group of CNT consecutive bits in B at or after START that are all set to VALUE,...
Definition: bitmap.c:320
bool bitmap_all(const struct bitmap *b, size_t start, size_t cnt)
Returns true if every bit in B between START and START + CNT, exclusive, is set to true,...
Definition: bitmap.c:284
struct bitmap * bitmap_create_in_buf(size_t bit_cnt, void *block, size_t block_size UNUSED)
Creates and returns a bitmap with BIT_CNT bits in the BLOCK_SIZE bytes of storage preallocated at BLO...
Definition: bitmap.c:100
#define BITMAP_ERROR
Finding set or unset bits.
Definition: bitmap.h:36
#define ASSERT(CONDITION)
This is outside the header guard so that debug.h may be included multiple times with different settin...
Definition: debug.h:31
#define NOT_REACHED()
lib/debug.h
Definition: debug.h:35
#define PANIC(...)
Halts the OS, printing the source file name, line number, and function name, plus a user-specific mes...
Definition: debug.h:14
static size_t user_page_limit
-ul: Maximum number of pages to put into palloc's user pool.
Definition: init.c:58
char * name[]
Definition: insult.c:47
int printf(const char *format,...)
Writes formatted output to the console.
Definition: stdio.c:79
uint32_t init_ram_pages
Amount of physical memory, in 4 kB pages.
void * palloc_get_page(enum palloc_flags flags)
Obtains a single free page and returns its kernel virtual address.
Definition: palloc.c:111
static bool page_from_pool(const struct pool *, void *page)
Returns true if PAGE was allocated from POOL, false otherwise.
Definition: palloc.c:175
void * palloc_get_multiple(enum palloc_flags flags, size_t page_cnt)
Obtains and returns a group of PAGE_CNT contiguous free pages.
Definition: palloc.c:71
static struct pool kernel_pool user_pool
Two pools: one for kernel data, one for user pages.
Definition: palloc.c:37
void palloc_init(size_t user_page_limit)
Initializes the page allocator.
Definition: palloc.c:46
void palloc_free_page(void *page)
Frees the page at PAGE.
Definition: palloc.c:146
static void init_pool(struct pool *, void *base, size_t page_cnt, const char *name)
Initializes pool P as starting at START and ending at END, naming it NAME for debugging purposes.
Definition: palloc.c:154
void palloc_free_multiple(void *pages, size_t page_cnt)
Frees the PAGE_CNT pages starting at PAGES.
Definition: palloc.c:118
palloc_flags
How to allocate pages.
Definition: palloc.h:8
@ PAL_ZERO
Zero page contents.
Definition: palloc.h:10
@ PAL_USER
User page.
Definition: palloc.h:11
@ PAL_ASSERT
Panic on failure.
Definition: palloc.h:9
#define DIV_ROUND_UP(X, STEP)
Yields X divided by STEP, rounded up.
Definition: round.h:10
#define NULL
Definition: stddef.h:4
unsigned char uint8_t
Definition: stdint.h:20
void * memset(void *dst_, int value, size_t size)
Sets the SIZE bytes in DST to VALUE.
Definition: string.c:279
From the outside, a bitmap is an array of bits.
Definition: bitmap.c:28
Lock.
Definition: synch.h:22
Page allocator.
Definition: palloc.c:30
struct bitmap * used_map
Bitmap of free pages.
Definition: palloc.c:32
uint8_t * base
Base of pool.
Definition: palloc.c:33
struct lock lock
Mutual exclusion.
Definition: palloc.c:31
void lock_release(struct lock *lock)
Releases LOCK, which must be owned by the current thread.
Definition: synch.c:229
void lock_init(struct lock *lock)
Initializes LOCK.
Definition: synch.c:176
void lock_acquire(struct lock *lock)
Acquires LOCK, sleeping until it becomes available if necessary.
Definition: synch.c:193
static uintptr_t pg_no(const void *va)
Virtual page number.
Definition: vaddr.h:29
#define PGSIZE
Bytes in a page.
Definition: vaddr.h:20
static unsigned pg_ofs(const void *va)
Offset within a page.
Definition: vaddr.h:24
static void * ptov(uintptr_t paddr)
Returns kernel virtual address at which physical address PADDR is mapped.
Definition: vaddr.h:72