AquilaOS documentation
How it works, and its code
Loading...
Searching...
No Matches
vmem.c
Go to the documentation of this file.
1
4
5#include <core/system.h>
6#include <core/panic.h>
7#include <core/string.h>
8#include <mm/vmem.h>
9
10MALLOC_DEFINE(M_VMEM_SEG, "vmem-seg", "vmem segment");
11
13static int log2_floor(size_t n)
14{
15 return (int) (sizeof(size_t) * 8 - 1) - __builtin_clzl(n);
16}
17
19static int log2_ceil(size_t n)
20{
21 int l = log2_floor(n);
22 return ((size_t) 1 << l) == n ? l : l + 1;
23}
24
26static struct vmem_seg **free_list(struct vmem *vm, struct vmem_seg *seg)
27{
28 return &vm->free[log2_floor(seg->size / vm->quantum)];
29}
30
32static struct vmem_seg **hash_chain(struct vmem *vm, uintptr_t base)
33{
34 return &vm->hash[(base / vm->quantum) % VMEM_HASH];
35}
36
39static void list_insert(struct vmem_seg **list, struct vmem_seg *seg)
40{
41 seg->lprev = NULL;
42 seg->lnext = *list;
43
44 if (*list)
45 (*list)->lprev = seg;
46
47 *list = seg;
48}
49
51static void seg_list_remove(struct vmem_seg **list, struct vmem_seg *seg)
52{
53 if (seg->lprev)
54 seg->lprev->lnext = seg->lnext;
55 else
56 *list = seg->lnext;
57
58 if (seg->lnext)
59 seg->lnext->lprev = seg->lprev;
60
61 seg->lprev = seg->lnext = NULL;
62}
63
66static void seg_link(struct vmem *vm, struct vmem_seg *prev, struct vmem_seg *seg)
67{
68 seg->prev = prev;
69 seg->next = prev ? prev->next : vm->segs;
70
71 if (seg->next)
72 seg->next->prev = seg;
73
74 if (prev)
75 prev->next = seg;
76 else
77 vm->segs = seg;
78}
79
81static void seg_unlink(struct vmem *vm, struct vmem_seg *seg)
82{
83 if (seg->prev)
84 seg->prev->next = seg->next;
85 else
86 vm->segs = seg->next;
87
88 if (seg->next)
89 seg->next->prev = seg->prev;
90}
91
104int vmem_init(struct vmem *vm, const char *name, uintptr_t base, size_t size, size_t quantum)
105{
106 memset(vm, 0, sizeof(*vm));
107 vm->name = name;
108 vm->quantum = quantum;
109 vm->size = size;
110
111 struct vmem_seg *seg = kmalloc(sizeof(*seg), &M_VMEM_SEG, M_ZERO);
112
113 if (!seg)
114 return -ENOMEM;
115
116 seg->base = base;
117 seg->size = size - size % quantum;
118 seg->free = 1;
119
120 seg_link(vm, NULL, seg);
122 return 0;
123}
124
134struct vmem_seg *vmem_alloc(struct vmem *vm, size_t size)
135{
136 size_t n = (size + vm->quantum - 1) / vm->quantum;
137 struct vmem_seg *seg = NULL;
138
139 if (!n)
140 n = 1;
141
142 /* instant fit: every segment on list ceil(log2 n) or above is big
143 * enough, so the first one found will do */
144 for (size_t i = log2_ceil(n); i < VMEM_LISTS && !seg; ++i)
145 seg = vm->free[i];
146
147 /* otherwise, a segment on list floor(log2 n) may be big enough */
148 if (!seg) {
149 for (struct vmem_seg *s = vm->free[log2_floor(n)]; s && !seg; s = s->lnext)
150 if (s->size >= n * vm->quantum)
151 seg = s;
152 }
153
154 if (!seg)
155 return NULL;
156
157 size = n * vm->quantum;
158
159 /* the rest of the segment stays free, as a segment of its own */
160 if (seg->size > size) {
161 struct vmem_seg *rest = kmalloc(sizeof(*rest), &M_VMEM_SEG, M_ZERO);
162
163 if (!rest)
164 return NULL;
165
167
168 rest->base = seg->base + size;
169 rest->size = seg->size - size;
170 rest->free = 1;
171 seg->size = size;
172
173 seg_link(vm, seg, rest);
174 list_insert(free_list(vm, rest), rest);
175 } else {
177 }
178
179 seg->free = 0;
180 list_insert(hash_chain(vm, seg->base), seg);
181 vm->inuse += seg->size;
182
183 return seg;
184}
185
194{
195 for (struct vmem_seg *seg = *hash_chain(vm, base); seg; seg = seg->lnext)
196 if (seg->base == base)
197 return seg;
198
199 return NULL;
200}
201
204static void seg_merge(struct vmem *vm, struct vmem_seg *a, struct vmem_seg *b)
205{
206 seg_list_remove(free_list(vm, a), a);
207 seg_list_remove(free_list(vm, b), b);
208
209 a->size += b->size;
210 seg_unlink(vm, b);
211 kfree(b);
212
213 list_insert(free_list(vm, a), a);
214}
215
223void vmem_free(struct vmem *vm, struct vmem_seg *seg)
224{
225 if (seg->free)
226 panic("vmem_free: a free segment");
227
228 seg_list_remove(hash_chain(vm, seg->base), seg);
229 vm->inuse -= seg->size;
230
231 seg->free = 1;
232 seg->type = NULL;
233 seg->bytes = 0;
235
236 /* merge it with its free neighbours */
237 if (seg->next && seg->next->free)
238 seg_merge(vm, seg, seg->next);
239
240 if (seg->prev && seg->prev->free)
241 seg_merge(vm, seg->prev, seg);
242}
#define panic(s)
Definition panic.h:15
#define MALLOC_DEFINE(type, _name, _desc)
Definition kvmem.h:30
void * kmalloc(size_t size, struct malloc_type *type, int flags)
Allocates memory for one of the kernel's structures.
Definition kvmem.c:123
#define M_ZERO
Definition kvmem.h:26
void kfree(void *)
Frees an object that kmalloc() gave.
Definition kvmem.c:146
void * memset(void *dst, int c, size_t n)
Sets n bytes to c.
Definition libc.c:23
#define NULL
Definition stddef.h:27
__SIZE_TYPE__ uintptr_t
Definition stddef.h:12
__SIZE_TYPE__ size_t
Definition stddef.h:7
A list: its first and last nodes, and how many there are.
Definition list.h:37
A received segment, with its header in host byte order.
Definition tcp_input.c:26
A segment of an arena: a range that is either free or allocated.
Definition vmem.h:24
uintptr_t base
Definition vmem.h:25
int free
Definition vmem.h:27
size_t size
Definition vmem.h:26
struct vmem_seg * prev
Definition vmem.h:29
struct vmem_seg * next
Definition vmem.h:29
An arena: a range, and its segments.
Definition vmem.h:41
struct vmem_seg * free[VMEM_LISTS]
Definition vmem.h:46
struct vmem_seg * segs
Definition vmem.h:45
size_t size
Definition vmem.h:49
const char * name
Definition vmem.h:42
struct vmem_seg * hash[VMEM_HASH]
Definition vmem.h:47
size_t quantum
Definition vmem.h:43
size_t inuse
Definition vmem.h:50
static int log2_ceil(size_t n)
Returns log2(n) rounded up; n must not be 0.
Definition vmem.c:19
static struct vmem_seg ** free_list(struct vmem *vm, struct vmem_seg *seg)
Returns the free list that a free segment belongs on, by its size.
Definition vmem.c:26
static int log2_floor(size_t n)
Returns log2(n) rounded down; n must not be 0.
Definition vmem.c:13
static void seg_link(struct vmem *vm, struct vmem_seg *prev, struct vmem_seg *seg)
Links seg into the arena's list of segments, in address order, right after prev (NULL: as the first).
Definition vmem.c:66
static struct vmem_seg ** hash_chain(struct vmem *vm, uintptr_t base)
Returns the hash chain of the allocated segments that start at base.
Definition vmem.c:32
static void seg_merge(struct vmem *vm, struct vmem_seg *a, struct vmem_seg *b)
Merges the free segment b into the free segment a, which comes right before it.
Definition vmem.c:204
static void seg_unlink(struct vmem *vm, struct vmem_seg *seg)
Takes seg off the arena's list of segments.
Definition vmem.c:81
static void list_insert(struct vmem_seg **list, struct vmem_seg *seg)
Puts a segment at the head of a free list or a hash chain, through its lprev and lnext.
Definition vmem.c:39
static void seg_list_remove(struct vmem_seg **list, struct vmem_seg *seg)
Takes a segment off the free list or hash chain it is on.
Definition vmem.c:51
int vmem_init(struct vmem *vm, const char *name, uintptr_t base, size_t size, size_t quantum)
Sets up an arena: the range [base, base + size), all free.
Definition vmem.c:104
struct vmem_seg * vmem_alloc(struct vmem *vm, size_t size)
Allocates a range from an arena.
Definition vmem.c:134
#define VMEM_LISTS
Definition vmem.h:37
void vmem_free(struct vmem *vm, struct vmem_seg *seg)
Frees an allocated segment.
Definition vmem.c:223
#define VMEM_HASH
Definition vmem.h:38
struct vmem_seg * vmem_find(struct vmem *vm, uintptr_t base)
Finds the allocated segment that starts at an address.
Definition vmem.c:193