AquilaOS documentation
How it works, and its code
Loading...
Searching...
No Matches
kvmem.c
Go to the documentation of this file.
1
19
20#include <core/system.h>
21#include <core/panic.h>
22#include <core/string.h>
23
24#include <mm/buddy.h>
25#include <mm/mm.h>
26#include <mm/pmap.h>
27#include <mm/vm.h>
28#include <mm/vmem.h>
29
31
32MALLOC_DEFINE(M_BUFFER, "buffer", "generic buffer");
33MALLOC_DEFINE(M_RINGBUF, "ring-buffer", "ringbuffer structure");
34MALLOC_DEFINE(M_QUEUE, "queue", "queue structure");
35MALLOC_DEFINE(M_QNODE, "queue-node", "queue node structure");
36MALLOC_DEFINE(M_HASHMAP, "hashmap", "hashmap structure");
37MALLOC_DEFINE(M_HASHMAP_NODE, "hashmap-node", "hashmap node structure");
38
39/* the guard page after each object */
40#define KVMEM_GUARD PAGE_SIZE
41
42static struct vmem kvmem;
43
44size_t kvmem_used; /* bytes of the objects' pages, for /proc/meminfo */
45size_t kvmem_obj_cnt; /* how many objects there are */
46
47void *slab_alloc(size_t size, struct malloc_type *type);
48int slab_owns(void *ptr);
49void slab_free(void *ptr);
50
55void kvmem_setup(void)
56{
57 /* enqueue() allocates the list's node, so M_QNODE can't be on the
58 * list before that node exists: it gets a placeholder first */
59 M_QNODE.qnode = (void *) 0xDEADBEEF;
60 M_QNODE.qnode = enqueue(malloc_types, &M_QNODE);
61
63 panic("kvmem: no memory for its arena");
64}
65
68static void kvmem_unmap(vaddr_t base, size_t size)
69{
70 for (vaddr_t va = base; va < base + size; va += PAGE_SIZE) {
72
73 if (paddr) {
75 mm_page_dealloc(paddr);
76 }
77 }
78}
79
82static void *kvmem_alloc(size_t size, struct malloc_type *type)
83{
85
87
88 if (!seg)
89 return NULL;
90
91 for (vaddr_t va = seg->base; va < seg->base + size; va += PAGE_SIZE) {
92 struct vm_page *vm_page = mm_page_alloc();
93
95 if (vm_page)
97
98 kvmem_unmap(seg->base, va - seg->base);
100 return NULL;
101 }
102 }
103
104 seg->type = type;
105 seg->bytes = size;
106
107 type->nr++;
108 type->total += size;
109 kvmem_used += size;
111
112 return (void *) seg->base;
113}
114
123void *kmalloc(size_t size, struct malloc_type *type, int flags)
124{
125 /* the small objects, most of them, from slabs */
126 void *obj = size <= SLAB_MAX ? slab_alloc(size, type) : kvmem_alloc(size, type);
127
128 if (!obj)
129 return NULL;
130
131 if (type->qnode == NULL)
132 type->qnode = enqueue(malloc_types, type);
133
134 if (flags & M_ZERO)
135 memset(obj, 0, size);
136
137 return obj;
138}
139
146void kfree(void *ptr)
147{
148 if (!ptr)
149 return;
150
151 if (slab_owns(ptr)) {
152 slab_free(ptr);
153 return;
154 }
155
156 struct vmem_seg *seg = vmem_find(&kvmem, (uintptr_t) ptr);
157
158 /* not the start of an object: a pointer into one, an object freed
159 * twice, or something kmalloc() never gave */
160 if (!seg) {
161 printk("kfree: %p isn't an object kmalloc() gave\n", ptr);
162 panic("kfree of something kmalloc() didn't give");
163 }
164
165 struct malloc_type *type = seg->type;
166
167 type->nr--;
168 type->total -= seg->bytes;
169 kvmem_used -= seg->bytes;
171
172 kvmem_unmap(seg->base, seg->bytes);
174}
175
176/*
177 * Self-checks, run when the kernel's command line asks for them (the
178 * kvmem.test and kvmem.guard arguments, which tests/kvmem uses).
179 */
180
181MALLOC_DEFINE(M_KVMEM_TEST, "kvmem-test", "kvmem self-test object");
182
184static size_t kvmem_segs(void)
185{
186 size_t n = 0;
187
188 for (struct vmem_seg *seg = kvmem.segs; seg; seg = seg->next)
189 ++n;
190
191 return n;
192}
193
194#define TEST_OBJS 48
195
202static const char *kvmem_selftest_run(void)
203{
204 static uint8_t *obj[TEST_OBJS];
205 static size_t size[TEST_OBJS];
206 size_t inuse = kvmem.inuse, segs = kvmem_segs(), objs = kvmem_obj_cnt;
207
208 for (int round = 0; round < 2; ++round) {
209 for (int i = 0; i < TEST_OBJS; ++i) {
210 if (obj[i])
211 continue;
212
213 size[i] = SLAB_MAX + 1 + (i * 1237 + round * 4093) % (6 * PAGE_SIZE);
214
215 if (!(obj[i] = kmalloc(size[i], &M_KVMEM_TEST, 0)))
216 return "an allocation failed";
217
218 if ((uintptr_t) obj[i] % PAGE_SIZE)
219 return "an object isn't on a page";
220
221 if (arch_page_get_mapping(kvm_space.pmap, (vaddr_t) obj[i] + PAGE_ROUND(size[i])))
222 return "a guard page is mapped";
223
224 memset(obj[i], i + 1, size[i]);
225 }
226
227 for (int i = 0; i < TEST_OBJS; ++i)
228 for (size_t b = 0; obj[i] && b < size[i]; b += 97)
229 if (obj[i][b] != (uint8_t) (i + 1))
230 return "an object was overwritten";
231
232 /* every third one, then every other: holes to fill and merge */
233 for (int i = round; i < TEST_OBJS; i += 3 - round) {
234 kfree(obj[i]);
235 obj[i] = NULL;
236 }
237 }
238
239 if (M_KVMEM_TEST.nr != (size_t) (kvmem_obj_cnt - objs))
240 return "the type's count is wrong";
241
242 /* the rest, from the last one */
243 for (int i = TEST_OBJS - 1; i >= 0; --i) {
244 kfree(obj[i]);
245 obj[i] = NULL;
246 }
247
248 if (M_KVMEM_TEST.nr || M_KVMEM_TEST.total)
249 return "the type still counts objects";
250
251 if (kvmem.inuse != inuse || kvmem_obj_cnt != objs)
252 return "the arena isn't as it was";
253
254 if (kvmem_segs() != segs)
255 return "free segments didn't merge";
256
257 return NULL;
258}
259
265{
266 const char *err = kvmem_selftest_run();
267
268 if (err)
269 printk("kvmem: self-test failed: %s\n", err);
270 else
271 printk("kvmem: self-test passed\n");
272}
273
279{
280 volatile char *buf = kmalloc(3000, &M_KVMEM_TEST, 0);
281 volatile char *past = buf + PAGE_ROUND(3000);
282
283 printk("kvmem: writing past %p's end, at %p\n", buf, past);
284 *past = 1;
285
286 printk("kvmem: the guard page didn't fault\n");
287}
#define PAGE_SIZE
Definition system.h:7
uint64_t paddr_t
Definition mm.h:39
#define ARCH_KVMEM_SIZE
Definition mm.h:16
#define ARCH_KVMEM_BASE
Definition mm.h:15
uint64_t vaddr_t
Definition mm.h:40
#define panic(s)
Definition panic.h:15
#define PAGE_ROUND(ptr)
Definition mm.h:10
struct vm_page * mm_page_alloc(void)
Allocates a free page of physical memory.
Definition mm.c:78
void mm_page_dealloc(paddr_t paddr)
Frees a page of physical memory, back to the zone it came from.
Definition mm.c:103
#define TEST_OBJS
Definition kvmem.c:194
void slab_free(void *ptr)
Frees an object of a slab.
Definition slab.c:199
static const char * kvmem_selftest_run(void)
Allocates objects of many sizes and checks that each is on pages of its own, that its guard page isn'...
Definition kvmem.c:202
static void kvmem_unmap(vaddr_t base, size_t size)
Unmaps the pages of an object at base, and gives them back to the buddy allocator.
Definition kvmem.c:68
static size_t kvmem_segs(void)
Returns how many segments, used and free, the arena has.
Definition kvmem.c:184
int slab_owns(void *ptr)
Says whether an object is a slab's, for kfree().
Definition slab.c:187
void * slab_alloc(size_t size, struct malloc_type *type)
Allocates a small object, from a slab of its malloc type and size class, making a new slab if none ha...
Definition slab.c:142
static struct vmem kvmem
Definition kvmem.c:42
struct queue * malloc_types
Definition kvmem.c:30
#define KVMEM_GUARD
Definition kvmem.c:40
size_t kvmem_used
Definition kvmem.c:44
static void * kvmem_alloc(size_t size, struct malloc_type *type)
Allocates an object of whole pages, with its guard page after it.
Definition kvmem.c:82
size_t kvmem_obj_cnt
Definition kvmem.c:45
#define MALLOC_DEFINE(type, _name, _desc)
Definition kvmem.h:30
#define SLAB_MAX
Definition kvmem.h:8
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
int pmap_add(struct pmap *pmap, vaddr_t va, paddr_t pa, uint32_t flags)
Maps a page in an address space, replacing what was mapped there.
Definition pmap.c:307
paddr_t arch_page_get_mapping(struct pmap *pmap, vaddr_t vaddr)
Finds the page mapped at a virtual address of an address space.
Definition pmap.c:431
void pmap_remove(struct pmap *pmap, vaddr_t sva, vaddr_t eva)
Unmaps the pages of a range of an address space, and flushes them from the TLBs that may have them.
Definition pmap.c:337
int printk(const char *fmt,...)
Writes a kernel message, to the message buffer and the consoles.
Definition printk.c:249
#define QUEUE_NEW()
Definition queue.h:40
#define NULL
Definition stddef.h:27
__SIZE_TYPE__ uintptr_t
Definition stddef.h:12
unsigned char uint8_t
Definition stddef.h:20
A malloc type: what an allocation is for, which /proc/kvmem counts by.
Definition kvmem.h:14
size_t total
Definition kvmem.h:18
size_t nr
Definition kvmem.h:17
struct qnode * qnode
Definition kvmem.h:19
A queue.
Definition queue.h:29
A received segment, with its header in host byte order.
Definition tcp_input.c:26
A page of physical memory (pages[] has one for every page of RAM).
Definition vm.h:150
paddr_t paddr
its physical address
Definition vm.h:151
struct pmap * pmap
its page tables (the architecture's)
Definition vm.h:27
A segment of an arena: a range that is either free or allocated.
Definition vmem.h:24
uintptr_t base
Definition vmem.h:25
size_t size
Definition vmem.h:26
An arena: a range, and its segments.
Definition vmem.h:41
struct vmem_seg * segs
Definition vmem.h:45
size_t size
Definition vmem.h:49
size_t inuse
Definition vmem.h:50
void kvmem_selftest(void)
Runs the allocator's self-test, for the kvmem.test kernel argument, and prints whether it passed.
Definition kvmem.c:264
void kvmem_setup(void)
Sets up the kernel's allocator at boot, once physical memory and the page tables are ready.
Definition kvmem.c:55
void kvmem_guardtest(void)
Writes one byte past the end of an object, for the kvmem.guard kernel argument: the guard page should...
Definition kvmem.c:278
struct vm_space kvm_space
Definition vmm.c:17
#define VM_KRW
kernel read/write
Definition vm.h:49
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
void vmem_free(struct vmem *vm, struct vmem_seg *seg)
Frees an allocated segment.
Definition vmem.c:223
struct vmem_seg * vmem_find(struct vmem *vm, uintptr_t base)
Finds the allocated segment that starts at an address.
Definition vmem.c:193