8a3814f0431c98cb3a13d4512ed2ae529d2d5a0b
[akaros.git] / kern / src / page_alloc.c
1 /* Copyright (c) 2009, 2010 The Regents of the University of California.
2  * Copyright (c) 2016 Google Inc
3  * See LICENSE for details.
4  *
5  * Barret Rhoden <brho@cs.berkeley.edu>
6  * Kevin Klues <klueska@cs.berkeley.edu> */
7
8 #include <page_alloc.h>
9 #include <pmap.h>
10 #include <kmalloc.h>
11 #include <arena.h>
12
13 /* Helper, allocates a free page. */
14 static struct page *get_a_free_page(void)
15 {
16         void *addr;
17
18         addr = kpages_alloc(PGSIZE, MEM_ATOMIC);
19         if (!addr)
20                 return NULL;
21         return kva2page(addr);
22 }
23
24 /**
25  * @brief Allocates a physical page from a pool of unused physical memory.
26  *
27  * Zeroes the page.
28  *
29  * @param[out] page  set to point to the Page struct
30  *                   of the newly allocated page
31  *
32  * @return ESUCCESS on success
33  * @return -ENOMEM  otherwise
34  */
35 error_t upage_alloc(struct proc *p, page_t **page, bool zero)
36 {
37         struct page *pg = get_a_free_page();
38
39         if (!pg)
40                 return -ENOMEM;
41         *page = pg;
42         if (zero)
43                 memset(page2kva(*page), 0, PGSIZE);
44         return 0;
45 }
46
47 error_t kpage_alloc(page_t **page)
48 {
49         struct page *pg = get_a_free_page();
50
51         if (!pg)
52                 return -ENOMEM;
53         *page = pg;
54         return 0;
55 }
56
57 /* Helper: allocates a refcounted page of memory for the kernel's use and
58  * returns the kernel address (kernbase), or 0 on error. */
59 void *kpage_alloc_addr(void)
60 {
61         struct page *pg = get_a_free_page();
62
63         if (!pg)
64                 return 0;
65         return page2kva(pg);
66 }
67
68 void *kpage_zalloc_addr(void)
69 {
70         void *retval = kpage_alloc_addr();
71         if (retval)
72                 memset(retval, 0, PGSIZE);
73         return retval;
74 }
75
76 /* Helper function for allocating from the kpages_arena.  This may be useful
77  * later since we might send the caller to a different NUMA domain. */
78 void *kpages_alloc(size_t size, int flags)
79 {
80         return arena_alloc(kpages_arena, size, flags);
81 }
82
83 void *kpages_zalloc(size_t size, int flags)
84 {
85         void *ret = arena_alloc(kpages_arena, size, flags);
86
87         if (!ret)
88                 return NULL;
89         memset(ret, 0, size);
90         return ret;
91 }
92
93 void kpages_free(void *addr, size_t size)
94 {
95         arena_free(kpages_arena, addr, size);
96 }
97
98 void *get_cont_pages(size_t order, int flags)
99 {
100         return kpages_alloc(PGSIZE << order, flags);
101 }
102
103 void free_cont_pages(void *buf, size_t order)
104 {
105         kpages_free(buf, PGSIZE << order);
106 }
107
108 /* Frees the page */
109 void page_decref(page_t *page)
110 {
111         kpages_free(page2kva(page), PGSIZE);
112 }
113
114 /* Attempts to get a lock on the page for IO operations.  If it is already
115  * locked, it will block the kthread until it is unlocked.  Note that this is
116  * really a "sleep on some event", not necessarily the IO, but it is "the page
117  * is ready". */
118 void lock_page(struct page *page)
119 {
120         /* when this returns, we have are the ones to have locked the page */
121         sem_down(&page->pg_sem);
122         assert(!(atomic_read(&page->pg_flags) & PG_LOCKED));
123         atomic_or(&page->pg_flags, PG_LOCKED);
124 }
125
126 /* Unlocks the page, and wakes up whoever is waiting on the lock */
127 void unlock_page(struct page *page)
128 {
129         atomic_and(&page->pg_flags, ~PG_LOCKED);
130         sem_up(&page->pg_sem);
131 }