Merge branch 'master' of ssh://scm.millennium.berkeley.edu/project/cs/radlab/src...
[akaros.git] / kern / src / page_alloc.c
1 /* Copyright (c) 2009 The Regents of the University  of California. 
2  * See the COPYRIGHT files at the top of this source tree for full 
3  * license information.
4  * 
5  * Kevin Klues <klueska@cs.berkeley.edu>    
6  */
7
8 #ifdef __SHARC__
9 #pragma nosharc
10 #endif
11
12 #include <sys/queue.h>
13 #include <page_alloc.h>
14 #include <pmap.h>
15 #include <string.h>
16
17 /**
18  * @brief Clear a Page structure.
19  *
20  * The result has null links and 0 refcount.
21  * Note that the corresponding physical page is NOT initialized!
22  */
23 static void page_clear(page_t *SAFE page)
24 {
25         memset(page, 0, sizeof(page_t));
26 }
27
28 error_t page_alloc_from_color_range(page_t** page,  
29                                     uint16_t base_color,
30                                     uint16_t range) {
31
32         // Find first available color with pages available
33     //  in the proper range
34         int i = base_color;
35         spin_lock_irqsave(&colored_page_free_list_lock);
36         for(i; i<(base_color+range); i++) {
37                 if(!LIST_EMPTY(&colored_page_free_list[i]))
38                         break;
39         }
40         // Alocate a page from that color
41         if(i < (base_color+range)) {
42                 *page = LIST_FIRST(&colored_page_free_list[i]);
43                 LIST_REMOVE(*page, page_link);
44                 page_clear(*page);
45                 spin_unlock_irqsave(&colored_page_free_list_lock);
46                 return ESUCCESS;
47         }
48         spin_unlock_irqsave(&colored_page_free_list_lock);
49         return -ENOMEM;
50 }
51
52 /**
53  * @brief Allocates a physical page from a pool of unused physical memory
54  *
55  * Does NOT set the contents of the physical page to zero -
56  * the caller must do that if necessary.
57  *
58  * @param[out] page  set to point to the Page struct
59  *                   of the newly allocated page
60  *
61  * @return ESUCCESS on success
62  * @return -ENOMEM  otherwise
63  */
64 error_t page_alloc(page_t** page) 
65 {
66         return page_alloc_from_color_range(page, 0, llc_num_colors);
67 }
68
69 /*
70  * This macro defines multiple functions of the form:
71  * error_t _cache##_page_alloc(page_t** page, size_t color)
72  *
73  * Each of these functions operates on a different level of 
74  * of the cache heirarchy, and allocates a physical page
75  * from the list of pages corresponding to the supplied 
76  * color for the given cache.  
77  * 
78  * Does NOT set the contents of the physical page to zero -
79  * the caller must do that if necessary.
80  *
81  * color       -- the color from which to allocate a page
82  * *page       -- is set to point to the Page struct 
83  *                of the newly allocated page
84  *
85  * RETURNS 
86  *   ESUCCESS  -- on success
87  *   -ENOMEM   -- otherwise 
88  *
89  * error_t _cache##_page_alloc(page_t** page, size_t color)
90  * {
91  *       if(!LIST_EMPTY(&(_cache##_cache_colored_page_list)[(color)])) {
92  *        *(page) = LIST_FIRST(&(_cache##_cache_colored_page_list)[(color)]);
93  *               LIST_REMOVE(*page, global_link);
94  *               REMOVE_CACHE_COLORING_PAGE_FROM_FREE_LISTS(page);
95  *               page_clear(*page);
96  *               return ESUCCESS;
97  *       }
98  *       return -ENOMEM;
99  * }
100  */
101 error_t l1_page_alloc(page_t** page, size_t color)
102 {
103         if(available_caches.l1)
104         {
105                 uint16_t range = llc_num_colors / get_cache_num_page_colors(&l1);
106                 uint16_t base_color = color*range;
107                 return page_alloc_from_color_range(page, base_color, range);
108         }
109         return -ENOCACHE;
110 }
111
112 error_t l2_page_alloc(page_t** page, size_t color)
113 {
114         if(available_caches.l2)
115         {
116                 uint16_t range = llc_num_colors / get_cache_num_page_colors(&l2);
117                 uint16_t base_color = color*range;
118                 return page_alloc_from_color_range(page, base_color, range);
119         }
120         return -ENOCACHE;
121 }
122
123 error_t l3_page_alloc(page_t** page, size_t color)
124 {
125         if(available_caches.l3)
126         {
127                 uint16_t range = llc_num_colors / get_cache_num_page_colors(&l3);
128                 uint16_t base_color = color*range;
129                 return page_alloc_from_color_range(page, base_color, range);
130         }
131         return -ENOCACHE;
132 }
133
134 /*
135  * Allocates a specific physical page.
136  * Does NOT set the contents of the physical page to zero -
137  * the caller must do that if necessary.
138  *
139  * ppn         -- the page number to allocate
140  * *page       -- is set to point to the Page struct 
141  *                of the newly allocated page
142  *
143  * RETURNS 
144  *   ESUCCESS  -- on success
145  *   -ENOMEM   -- otherwise 
146  */
147 error_t page_alloc_specific(page_t** page, size_t ppn)
148 {
149         spin_lock_irqsave(&colored_page_free_list_lock);
150         page_t* sp_page = ppn2page(ppn);
151         if( sp_page->page_ref != 0 )
152                 return -ENOMEM;
153         *page = sp_page;
154         LIST_REMOVE(*page, page_link);
155         spin_unlock_irqsave(&colored_page_free_list_lock);
156
157         page_clear(*page);
158         return 0;
159 }
160
161 /*
162  * Return a page to the free list.
163  * (This function should only be called when pp->page_ref reaches 0.)
164  */
165 error_t page_free(page_t* page) 
166 {
167         //TODO: Put a lock around this
168         page_clear(page);
169         cache_t* llc = available_caches.llc;
170
171         spin_lock_irqsave(&colored_page_free_list_lock);
172         LIST_INSERT_HEAD(
173            &(colored_page_free_list[get_page_color(page2ppn(page), llc)]),
174            page,
175            page_link
176         );
177         spin_unlock_irqsave(&colored_page_free_list_lock);
178
179         return ESUCCESS;
180 }
181
182 /*
183  * Check if a page with the given pyhysical page # is free
184  */
185 int page_is_free(size_t ppn) {
186         page_t* page = ppn2page(ppn);
187         if( page->page_ref == 0 )
188                 return TRUE;
189         return FALSE;
190 }
191
192 /*
193  * Increment the reference count on a page
194  */
195 void page_incref(page_t *page)
196 {
197         page->page_ref++;
198 }
199
200 /*
201  * Decrement the reference count on a page,
202  * freeing it if there are no more refs.
203  */
204 void page_decref(page_t *page)
205 {
206         if (--page->page_ref == 0)
207                 page_free(page);
208 }
209
210 /*
211  * Set the reference count on a page to a specific value
212  */
213 void page_setref(page_t *page, size_t val)
214 {
215         page->page_ref = val;
216 }
217
218 /*
219  * Get the reference count on a page
220  */
221 size_t page_getref(page_t *page)
222 {
223         return page->page_ref;
224 }
225