Improve cake allocator's memory utilisation (#43)
[lunaix-os.git] / lunaix-os / includes / lunaix / mm / page.h
1 #ifndef __LUNAIX_PAGE_H
2 #define __LUNAIX_PAGE_H
3
4 #include <lunaix/mm/pmm.h>
5 #include <lunaix/mm/vmm.h>
6 #include <lunaix/mm/vmtlb.h>
7
8 #include <klibc/string.h>
9
10 /**
11  * @brief A leaflet represent a bunch 4k ppage
12  *        as single multi-ordered page, as such
13  *        big page can seen as an unfolded version
14  *        of these small 4k ppages hence the name.
15  *        It is introduced to solve the issue that
16  *        is discovered during refactoring - It is 
17  *        jolly unclear whether the ppage is a head, 
18  *        tail, or even worse, the middle one, when
19  *        passing around between functions.
20  *        This concept is surprisingly similar to
21  *        Linux's struct folio (I swear to the 
22  *        Almighty Princess of the Sun, Celestia, 
23  *        that I don't quite understand what folio 
24  *        is until I've wrote the conceptually same 
25  *        thing)
26  * 
27  */
28 struct leaflet
29 {
30     struct ppage lead_page;
31 };
32
33 static inline struct leaflet*
34 get_leaflet(struct ppage* page)
35 {
36     return (struct leaflet*)leading_page(page);
37 }
38
39 static inline struct ppage*
40 get_ppage(struct leaflet* leaflet)
41 {
42     return (struct ppage*)leaflet;
43 }
44
45 static inline struct leaflet*
46 alloc_leaflet(int order)
47 {
48     return (struct leaflet*)pmm_alloc_napot_type(POOL_UNIFIED, order, 0);
49 }
50
51 static inline struct leaflet*
52 alloc_leaflet_pinned(int order)
53 {
54     return (struct leaflet*)pmm_alloc_napot_type(POOL_UNIFIED, order, PP_FGLOCKED);
55 }
56
57 static inline void
58 leaflet_borrow(struct leaflet* leaflet)
59 {
60     struct ppage* const page = get_ppage(leaflet);
61     assert(page->refs);
62     if (reserved_page(page)) {
63         return;
64     }
65     
66     page->refs++;
67 }
68
69 static inline void
70 leaflet_return(struct leaflet* leaflet)
71 {
72     struct ppage* const page = get_ppage(leaflet);
73     assert(page->refs);
74     pmm_free_one(page, 0);
75 }
76
77 static inline unsigned int
78 leaflet_refcount(struct leaflet* leaflet)
79 {
80     return get_ppage(leaflet)->refs;
81 }
82
83 static inline int
84 leaflet_order(struct leaflet* leaflet)
85 {
86     return ppage_order(get_ppage(leaflet));
87 }
88
89 static inline int
90 leaflet_size(struct leaflet* leaflet)
91 {
92     return PAGE_SIZE << leaflet_order(leaflet);
93 }
94
95 static inline int
96 leaflet_nfold(struct leaflet* leaflet)
97 {
98     return 1 << leaflet_order(leaflet);
99 }
100
101 static inline struct leaflet*
102 ppfn_leaflet(pfn_t ppfn)
103 {
104     return get_leaflet(ppage(ppfn));
105 }
106
107 static inline struct leaflet*
108 pte_leaflet(pte_t pte)
109 {
110     struct ppage* ppfn = ppage(pfn(pte_paddr(pte)));
111     return get_leaflet(ppfn);
112 }
113
114 static inline struct leaflet*
115 pte_leaflet_aligned(pte_t pte)
116 {
117     struct ppage* ppfn = ppage(pfn(pte_paddr(pte)));
118     struct leaflet* _l = get_leaflet(ppfn);
119
120     assert((ptr_t)_l == (ptr_t)ppfn);
121     return _l;
122 }
123
124 static inline pfn_t
125 leaflet_ppfn(struct leaflet* leaflet)
126 {
127     return ppfn(get_ppage(leaflet));
128 }
129
130 static inline ptr_t
131 leaflet_addr(struct leaflet* leaflet)
132 {
133     return page_addr(ppfn(get_ppage(leaflet)));
134 }
135
136 static inline void
137 unpin_leaflet(struct leaflet* leaflet)
138 {
139     change_page_type(get_ppage(leaflet), 0);
140 }
141
142 static inline void
143 pin_leaflet(struct leaflet* leaflet)
144 {
145     change_page_type(get_ppage(leaflet), PP_FGLOCKED);
146 }
147
148 /**
149  * @brief Map a leaflet
150  * 
151  * @param ptep 
152  * @param leaflet 
153  * @return pages folded into that leaflet
154  */
155 static inline size_t
156 ptep_map_leaflet(pte_t* ptep, pte_t pte, struct leaflet* leaflet)
157 {
158     // We do not support huge leaflet yet
159     assert(leaflet_order(leaflet) < LEVEL_SHIFT);
160
161     pte = pte_setppfn(pte, leaflet_ppfn(leaflet));
162     pte = pte_mkloaded(pte);
163
164     int n = leaflet_nfold(leaflet);
165     vmm_set_ptes_contig(ptep, pte, LFT_SIZE, n);
166
167     return n;
168 }
169
170 /**
171  * @brief Unmap a leaflet
172  * 
173  * @param ptep 
174  * @param leaflet 
175  * @return pages folded into that leaflet
176  */
177 static inline size_t
178 ptep_unmap_leaflet(pte_t* ptep, struct leaflet* leaflet)
179 {
180     // We do not support huge leaflet yet
181     assert(leaflet_order(leaflet) < LEVEL_SHIFT);
182
183     int n = leaflet_nfold(leaflet);
184     vmm_unset_ptes(ptep, n);
185
186     return n;
187 }
188
189 static inline ptr_t
190 leaflet_mount(struct leaflet* leaflet)
191 {
192     pte_t* ptep = mkptep_va(VMS_SELF, PG_MOUNT_VAR);    
193     ptep_map_leaflet(ptep, mkpte_prot(KERNEL_DATA), leaflet);
194
195     tlb_flush_kernel_ranged(PG_MOUNT_VAR, leaflet_nfold(leaflet));
196
197     return PG_MOUNT_VAR;
198 }
199
200 static inline void
201 leaflet_unmount(struct leaflet* leaflet)
202 {
203     pte_t* ptep = mkptep_va(VMS_SELF, PG_MOUNT_VAR);    
204     vmm_unset_ptes(ptep, leaflet_nfold(leaflet));
205
206     tlb_flush_kernel_ranged(PG_MOUNT_VAR, leaflet_nfold(leaflet));
207 }
208
209 static inline void
210 leaflet_fill(struct leaflet* leaflet, unsigned int val)
211 {
212     ptr_t mnt;
213     
214     mnt = leaflet_mount(leaflet);
215     memset((void*)mnt, val, leaflet_size(leaflet));
216     leaflet_unmount(leaflet);
217 }
218
219 static inline void
220 leaflet_wipe(struct leaflet* leaflet)
221 {
222     leaflet_fill(leaflet, 0);
223 }
224
225 /**
226  * @brief Duplicate the leaflet
227  *
228  * @return Duplication of given leaflet
229  *
230  */
231 struct leaflet*
232 dup_leaflet(struct leaflet* leaflet);
233
234
235 /**
236  * @brief Maps a number of contiguous ptes in kernel 
237  *        address space
238  * 
239  * @param pte the pte to be mapped
240  * @param lvl_size size of the page pointed by the given pte
241  * @param n number of ptes
242  * @return ptr_t 
243  */
244 ptr_t
245 vmap_ptes_at(pte_t pte, size_t lvl_size, int n);
246
247 /**
248  * @brief Maps a number of contiguous ptes in kernel 
249  *        address space (leaf page size)
250  * 
251  * @param pte the pte to be mapped
252  * @param n number of ptes
253  * @return ptr_t 
254  */
255 static inline ptr_t
256 vmap_leaf_ptes(pte_t pte, int n)
257 {
258     return vmap_ptes_at(pte, LFT_SIZE, n);
259 }
260
261 /**
262  * @brief Maps a contiguous range of physical address 
263  *        into kernel address space (leaf page size)
264  * 
265  * @param paddr start of the physical address range
266  * @param size size of the physical range
267  * @param prot default protection to be applied
268  * @return ptr_t 
269  */
270 static inline ptr_t
271 vmap(struct leaflet* leaflet, pte_attr_t prot)
272 {
273     pte_t _pte = mkpte(page_addr(leaflet_ppfn(leaflet)), prot);
274     return vmap_ptes_at(_pte, LFT_SIZE, leaflet_nfold(leaflet));
275 }
276
277 void
278 vunmap(ptr_t ptr, struct leaflet* leaflet);
279
280 static inline ptr_t
281 vmap_range(pfn_t start, size_t npages, pte_attr_t prot)
282 {
283     pte_t _pte = mkpte(page_addr(start), prot);
284     return vmap_ptes_at(_pte, LFT_SIZE, npages);
285 }
286
287 static inline void
288 vunmap_range(pfn_t start, size_t npages)
289 {
290     pte_t* ptep = mkptep_va(VMS_SELF, start);
291     vmm_set_ptes_contig(ptep, null_pte, LFT_SIZE, npages);
292 }
293
294
295 /**
296  * @brief Allocate a page in kernel space.
297  * 
298  * @param ptep 
299  * @param pte 
300  * @param order 
301  * @return pte_t 
302  */
303 pte_t 
304 alloc_kpage_at(pte_t* ptep, pte_t pte, int order);
305
306 static inline void*
307 vmalloc_page(int order)
308 {
309     struct leaflet* leaf = alloc_leaflet(0);
310     if (!leaf) {
311         return NULL;
312     }
313
314     return (void*)vmap(leaf, KERNEL_DATA);
315 }
316
317 static inline void
318 vmfree(void* ptr)
319 {
320     struct leaflet* leaf = ppfn_leaflet(pfn((ptr_t)ptr));
321     leaflet_return(leaf);
322 }
323
324 #endif /* __LUNAIX_PAGE_H */