AquilaOS documentation
How it works, and its code
Loading...
Searching...
No Matches
buddy.c
Go to the documentation of this file.
1
12
13#include <core/system.h>
14#include <core/panic.h>
15#include <ds/bitmap.h>
16#include <ds/buddy.h>
17#include <mm/buddy.h>
18#include <boot/boot.h>
19
21static uintptr_t kstart = 0, kend = 0;
22
23/*
24 * At boot, what isn't free RAM (the kernel, the modules, firmware ranges)
25 * is set unusable a whole block of BUDDY_MAX_BS at a time, and its ranges
26 * are kept here. The first allocation gives back the pages of those
27 * blocks that no range covers (buddy_reserved_done()).
28 */
29#define RESERVED_MAX 64
30
31static struct {
34
36
37/* How many top-order blocks of each zone are RAM (set by buddy_setup()). */
39
42static inline void *alloc(size_t size, size_t align)
43{
44 extern void *mm_boot_alloc(size_t size, size_t align);
45 return mm_boot_alloc(size, align);
46}
47
50 [BUDDY_ZONE_DMA] = 0, /* 0 - 16 MiB */
51 [BUDDY_ZONE_NORMAL] = 0x1000000, /* 16 MiB - */
52 [BUDDY_ZONE_NR] = (uintptr_t) -1,
53};
54
60static size_t buddy_recursive_alloc(int zone, size_t order)
61{
62 if (order > BUDDY_MAX_ORDER)
63 return -1;
64
65 /* Check if there is a free bit in current order */
66 if (buddies[zone][order].usable) {
67 /* Search for a free bit in current order */
68 for (size_t i = buddies[zone][order].first_free_idx; i <= buddies[zone][order].bitmap.max_idx; ++i) {
69
70 /* If bit at i is not checked */
71 if (!bitmap_check(&buddies[zone][order].bitmap, i)) {
72
73 /* Mark the bit as used */
74 bitmap_set(&buddies[zone][order].bitmap, i);
75 buddies[zone][order].usable--;
76
77 /* Shift first_free_idx to search after child_idx */
78 buddies[zone][order].first_free_idx = i + 1;
79
80 return i;
81 }
82 }
83
84 return -1;
85 } else {
86 /* Search for a buddy in higher order to split */
87 size_t idx = buddy_recursive_alloc(zone, order + 1);
88
89 /* Could not find a free budy */
90 if (idx == (size_t) -1) return -1;
91
92 /* Select the left child of the selected bit */
93 size_t child_idx = idx << 1;
94
95 /* Mark the selected bit as used */
96 bitmap_set(&buddies[zone][order].bitmap, child_idx);
97
98 /* Mark it's buddy as free */
99 bitmap_clear(&buddies[zone][order].bitmap, BUDDY_IDX(child_idx));
100 buddies[zone][order].usable++;
101
102 /* Shift first_free_idx to search after child_idx */
103 buddies[zone][order].first_free_idx = child_idx + 1;
104
105 return child_idx;
106 }
107}
108
113static void buddy_recursive_free(int zone, size_t order, size_t idx)
114{
115 if (order > BUDDY_MAX_ORDER) return;
116 if (idx > buddies[zone][order].bitmap.max_idx) return;
117
118 /* Can't free an already free bit */
119 if (!bitmap_check(&buddies[zone][order].bitmap, idx)) return;
120
121 /* Check if buddy bit is free, then combine */
122 if (order < BUDDY_MAX_ORDER && !bitmap_check(&buddies[zone][order].bitmap, BUDDY_IDX(idx))) {
123 bitmap_set(&buddies[zone][order].bitmap, BUDDY_IDX(idx));
124 buddies[zone][order].usable--;
125
126 buddy_recursive_free(zone, order + 1, idx >> 1);
127 } else {
128 bitmap_clear(&buddies[zone][order].bitmap, idx);
129 buddies[zone][order].usable++;
130
131 /* Update first_free_idx */
132 if (buddies[zone][order].first_free_idx > idx)
133 buddies[zone][order].first_free_idx = idx;
134 }
135}
136
137static void buddy_reserved_done(void);
138
149paddr_t buddy_alloc(int zone, size_t _sz)
150{
151 if (_sz > BUDDY_MAX_BS)
152 panic("Cannot allocate buddy");
153
154 if (!reserved_done)
156
157 size_t sz = BUDDY_MIN_BS;
158
159 /* the smallest order whose blocks hold _sz bytes */
160 size_t order = 0;
161 for (; order <= BUDDY_MAX_ORDER; ++order) {
162 if (sz >= _sz)
163 break;
164 sz <<= 1;
165 }
166
167 size_t idx = buddy_recursive_alloc(zone, order);
168
169 if (idx == (size_t) -1)
170 return (paddr_t) -1;
171
172 k_used_mem += sz;
173 return buddy_zone_offset[zone] + (uintptr_t) (idx * (BUDDY_MIN_BS << order));
174}
175
183void buddy_free(int zone, paddr_t addr, size_t size)
184{
185 if (addr >= kstart && addr < kend)
186 panic("trying to free from kernel code");
187
188 size_t sz = BUDDY_MIN_BS;
189
190 /* the order buddy_alloc() took it from */
191 size_t order = 0;
192 for (; order < BUDDY_MAX_ORDER; ++order) {
193 if (sz >= size)
194 break;
195 sz <<= 1;
196 }
197
198 k_used_mem -= sz;
199
200 addr -= buddy_zone_offset[zone];
201 size_t idx = (addr / (BUDDY_MIN_BS << order));
202 buddy_recursive_free(zone, order, idx);
203}
204
212void buddy_set_unusable(paddr_t addr, size_t size)
213{
214 if (!size) return;
215
216 printk("buddy: set unusable: %p-%p\n", addr, addr+size-1);
217
218 if (reserved_done)
219 panic("buddy: memory set unusable after the first allocation");
220
222 panic("buddy: too many unusable ranges");
223
224 /* the range, widened to whole pages */
225 reserved[reserved_nr].start = addr & ~(paddr_t) (BUDDY_MIN_BS - 1);
226 reserved[reserved_nr].end = (addr + size + BUDDY_MIN_BS - 1) & ~(paddr_t) (BUDDY_MIN_BS - 1);
227 reserved_nr++;
228
229 /* the range's end (exclusive), clamped if it wraps around */
230 uintptr_t end = addr + size;
231 if (end < addr)
232 end = (uintptr_t) -1;
233
234 for (int zone = 0; zone < BUDDY_ZONE_NR; ++zone) {
235
236 uintptr_t zone_start = buddy_zone_offset[zone];
237 uintptr_t zone_end = buddy_zone_offset[zone+1];
238
239 if (end <= zone_start || addr >= zone_end)
240 continue;
241
242 struct buddy *buddy = &buddies[zone][BUDDY_MAX_ORDER];
243
244 uintptr_t zone_addr = MAX(addr, zone_start) - zone_start;
245 uintptr_t zone_size = MIN(end, zone_end) - zone_start - zone_addr;
246
247 /* the first and last (inclusive) blocks the range touches */
248 size_t start_idx = zone_addr / BUDDY_MAX_BS;
249 size_t end_idx = (zone_addr + zone_size - 1) / BUDDY_MAX_BS;
250
251 /* the range is past this zone's RAM */
252 if (start_idx > buddy->bitmap.max_idx)
253 continue;
254
255 if (end_idx > buddy->bitmap.max_idx)
256 end_idx = buddy->bitmap.max_idx;
257
258 for (size_t idx = start_idx; idx <= end_idx; ++idx) {
259 /* an overlapping range may have marked the block already */
260 if (!bitmap_check(&buddy->bitmap, idx)) {
261 bitmap_set(&buddy->bitmap, idx);
262 buddy->usable--;
263 }
264 }
265
266 size_t ffidx = buddy->first_free_idx;
267
268 if (ffidx >= start_idx && ffidx <= end_idx)
269 buddy->first_free_idx = end_idx + 1;
270 }
271}
272
274static int reserved_page(paddr_t page)
275{
276 for (int i = 0; i < reserved_nr; ++i)
277 if (page >= reserved[i].start && page < reserved[i].end)
278 return 1;
279
280 return 0;
281}
282
289static void buddy_reserved_done(void)
290{
291 size_t free = 0;
292
293 reserved_done = 1;
294
295 for (int zone = 0; zone < BUDDY_ZONE_NR; ++zone) {
296 struct buddy *top = &buddies[zone][BUDDY_MAX_ORDER];
297
298 for (size_t idx = 0; idx < zone_blocks[zone]; ++idx) {
299 if (!bitmap_check(&top->bitmap, idx)) {
300 free += BUDDY_MAX_BS;
301 continue;
302 }
303
304 paddr_t block = buddy_zone_offset[zone] + idx * BUDDY_MAX_BS;
305 size_t first = idx << BUDDY_MAX_ORDER;
306
307 for (size_t i = 0; i < (1U << BUDDY_MAX_ORDER); ++i) {
308 if (!reserved_page(block + i * BUDDY_MIN_BS)) {
309 buddy_recursive_free(zone, 0, first + i);
310 free += BUDDY_MIN_BS;
311 }
312 }
313 }
314 }
315
316 /* /proc/meminfo's MemFree is k_total_mem - k_used_mem */
317 k_used_mem = k_total_mem > free ? k_total_mem - free : 0;
318}
319
328int buddy_setup(size_t total_mem)
329{
330 printk("buddy: Setting up buddy allocator (total memory %p)\n", total_mem);
331
332 k_total_mem = total_mem;
333 k_used_mem = 0;
334
335 for (int zone = 0; zone < BUDDY_ZONE_NR; ++zone) {
336 size_t bits_cnt = 0;
337
338 uintptr_t zone_start = buddy_zone_offset[zone];
339 uintptr_t zone_end = MIN(buddy_zone_offset[zone+1], total_mem);
340
341 if (zone_end > zone_start)
342 bits_cnt = (zone_end - zone_start) / BUDDY_MAX_BS;
343
344 zone_blocks[zone] = bits_cnt;
345
346 if (!bits_cnt) {
347 /* no RAM in this zone: one block, marked used */
348 for (int i = BUDDY_MAX_ORDER; i >= 0; --i) {
349 buddies[zone][i].bitmap.map = alloc(bitmap_size(1), 4);
350 buddies[zone][i].bitmap.max_idx = 0;
351 bitmap_set(&buddies[zone][i].bitmap, 0);
352 buddies[zone][i].first_free_idx = -1;
353 buddies[zone][i].usable = 0;
354 }
355
356 continue;
357 }
358
359 for (int i = BUDDY_MAX_ORDER; i >= 0; --i) {
360 size_t bmsize = bitmap_size(bits_cnt);
361
362 buddies[zone][i].bitmap.map = alloc(bmsize, 4);
363 buddies[zone][i].bitmap.max_idx = bits_cnt - 1;
364
365 bits_cnt <<= 1;
366 }
367
368 /* the top order's blocks free, the smaller orders' all used */
372
373 for (int i = 0; i < BUDDY_MAX_ORDER; ++i) {
374 bitmap_set_range(&buddies[zone][i].bitmap, 0, buddies[zone][i].bitmap.max_idx);
375 buddies[zone][i].first_free_idx = -1;
376 buddies[zone][i].usable = 0;
377 }
378 }
379
380 /* the kernel's own memory (the linker script's symbols) */
381 extern char kernel_start;
382 extern char kernel_end;
383
384 kstart = (uintptr_t) &kernel_start;
386
388
389 extern struct boot *__kboot;
390
391 if (__kboot->symtab) {
394 }
395
396 if (__kboot->strtab) {
399 }
400
401 return 0;
402}
struct boot * __kboot
Definition init.c:19
uint64_t paddr_t
Definition mm.h:39
#define LMA(obj)
Definition mm.h:37
#define panic(s)
Definition panic.h:15
static void bitmap_set_range(struct bitmap *bitmap, size_t findex, size_t lindex)
Sets the bits from findex to lindex, inclusive.
Definition bitmap.h:60
static size_t bitmap_size(size_t n)
Returns the size in bytes of a bitmap of n bits.
Definition bitmap.h:36
static void bitmap_clear_range(struct bitmap *bitmap, size_t findex, size_t lindex)
Clears the bits from findex to lindex, inclusive.
Definition bitmap.h:89
static size_t zone_blocks[BUDDY_ZONE_NR]
Definition buddy.c:38
size_t k_total_mem
Definition buddy.c:20
static void buddy_reserved_done(void)
Frees the pages of the blocks set unusable at boot that no reserved range covers, at the first alloca...
Definition buddy.c:289
static uintptr_t kstart
Definition buddy.c:21
static size_t buddy_recursive_alloc(int zone, size_t order)
Takes a free block of order order from zone, splitting a bigger block if there is none.
Definition buddy.c:60
static int reserved_nr
Definition buddy.c:35
static int reserved_done
Definition buddy.c:35
static int reserved_page(paddr_t page)
Returns whether a range set unusable at boot covers page.
Definition buddy.c:274
#define RESERVED_MAX
Definition buddy.c:29
static void buddy_recursive_free(int zone, size_t order, size_t idx)
Frees the block at index idx of order order in zone, merging it with its buddy, and that block with i...
Definition buddy.c:113
static uintptr_t kend
Definition buddy.c:21
static struct @152050200367053112031232350014135337374054232355 reserved[RESERVED_MAX]
size_t k_used_mem
Definition buddy.c:20
struct buddy buddies[BUDDY_ZONE_NR][BUDDY_MAX_ORDER+1]
Definition buddy.c:48
static void * alloc(size_t size, size_t align)
Allocates memory for the bitmaps at boot, from what mm_setup() set aside: their size depends on how m...
Definition buddy.c:42
#define BUDDY_IDX(idx)
Definition buddy.h:17
struct elf32_shdr elf_shdr_t
Definition elf.h:223
char kernel_end
#define MIN(a, b)
Definition system.h:11
#define MAX(a, b)
Definition system.h:10
void buddy_free(int zone, paddr_t paddr, size_t size)
Frees a block that buddy_alloc() gave.
Definition buddy.c:183
#define BUDDY_ZONE_NR
Definition buddy.h:15
void buddy_set_unusable(paddr_t paddr, size_t size)
Marks a range of physical memory as not free, at boot, before the first allocation: the kernel,...
Definition buddy.c:212
#define BUDDY_MAX_BS
Definition buddy.h:12
#define BUDDY_ZONE_DMA
Definition buddy.h:16
#define BUDDY_MIN_BS
Definition buddy.h:11
paddr_t buddy_alloc(int zone, size_t size)
Allocates a block of physical memory from a zone, rounded up to a power of two pages.
Definition buddy.c:149
uintptr_t buddy_zone_offset[]
Definition buddy.c:49
#define BUDDY_ZONE_NORMAL
Definition buddy.h:17
int buddy_setup(size_t total_mem)
Sets up the allocator at boot: a bitmap for each zone and order, every top-order block of RAM free,...
Definition buddy.c:328
#define BUDDY_MAX_ORDER
Definition buddy.h:10
void * mm_boot_alloc(size_t size, size_t align)
Allocates boot memory, zeroed, before the buddy allocator is set up.
Definition mm.c:238
int printk(const char *fmt,...)
Writes a kernel message, to the message buffer and the consoles.
Definition printk.c:249
__SIZE_TYPE__ uintptr_t
Definition stddef.h:12
A bitmap: its blocks, and its last bit's index.
Definition bitmap.h:17
size_t max_idx
Definition bitmap.h:19
bitmap_t * map
Definition bitmap.h:18
Definition boot.h:35
elf_shdr_t * symtab
Definition boot.h:49
elf_shdr_t * strtab
Definition boot.h:52
elf_shdr_t * shdr
Definition boot.h:46
One order of a buddy allocator's zone (mm/buddy.c): a bitmap of its blocks, a set bit for a block tha...
Definition buddy.h:9
size_t usable
Definition buddy.h:11
struct bitmap bitmap
Definition buddy.h:12
size_t first_free_idx
Definition buddy.h:10
elf32_addr sh_addr
Definition elf.h:173
elf32_word sh_size
Definition elf.h:175