Skip to content
This repository was archived by the owner on Dec 25, 2025. It is now read-only.

Commit 4b1116e

Browse files
committed
Add Augmented RB-Tree in VMA
1 parent f1fc590 commit 4b1116e

File tree

3 files changed

+220
-24
lines changed

3 files changed

+220
-24
lines changed

kernel/include/memory/vma.hpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ struct VmRegion {
1010
uintptr_t start;
1111
size_t size;
1212

13+
size_t gap;
14+
size_t subtree_max_gap;
15+
1316
uint8_t flags;
1417
CacheType cache;
1518

@@ -55,6 +58,7 @@ class VirtualMemoryAllocator {
5558

5659
VmRegion* find_node(uintptr_t start);
5760
uintptr_t find_hole(size_t size, size_t alignment);
61+
uintptr_t find_hole(VmRegion* node, size_t size, size_t alignment);
5862

5963
void insert_region(uintptr_t start, size_t size, uint8_t flags, CacheType cache);
6064
void insert_region_locked(uintptr_t start, size_t size, uint8_t flags, CacheType cache);
@@ -65,6 +69,11 @@ class VirtualMemoryAllocator {
6569
void insert_fixup(VmRegion* z);
6670
void delete_fixup(VmRegion* x);
6771

72+
void update_node_metadata(VmRegion* x);
73+
74+
VmRegion* predecessor(VmRegion* node);
75+
VmRegion* successor(VmRegion* node);
76+
6877
struct alignas(CACHE_LINE_SIZE) CpuCache {
6978
static constexpr int CAPACITY = 256;
7079
uintptr_t va_holes[CAPACITY];

kernel/src/hal/smp.cpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,6 @@ void CpuCoreManager::init(void* bsp_stack_top) {
7777
this->cores.push_back(data);
7878
}
7979

80-
LOG_DEBUG("Here");
81-
8280
for (size_t i = 0; i < this->cores.size(); ++i) {
8381
PerCpuData* core = this->cores[i];
8482
limine_mp_info* info = mp_request.response->cpus[i];

kernel/src/memory/vma.cpp

Lines changed: 211 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -229,37 +229,63 @@ VmRegion* VirtualMemoryAllocator::find_node(uintptr_t start) {
229229
}
230230

231231
uintptr_t VirtualMemoryAllocator::find_hole(size_t size, size_t alignment) {
232-
uintptr_t candidate = this->heap_base;
233-
VmRegion* stack[64];
234-
int top = 0;
235-
VmRegion* curr = this->root;
232+
// Try to find a hole inside the tree
233+
uintptr_t found = this->find_hole(this->root, size, alignment);
234+
if (found != 0) {
235+
return found;
236+
}
236237

237-
while (curr) {
238-
stack[top++] = curr;
239-
curr = curr->left;
238+
VmRegion* max = this->root;
239+
if (!max) {
240+
// Tree is empty, return aligned base
241+
return align_up(heap_base, alignment);
240242
}
241243

242-
while (top > 0) {
243-
curr = stack[--top];
244-
uintptr_t aligned = align_up(candidate, alignment);
244+
// Find the very last node.
245+
while (max->right) {
246+
max = max->right;
247+
}
245248

246-
if (aligned + size <= curr->start) {
247-
return aligned;
248-
}
249+
// Calculate valid address after the last node
250+
return align_up(max->end(), alignment);
251+
}
249252

250-
candidate = curr->end();
253+
uintptr_t VirtualMemoryAllocator::find_hole(VmRegion* node, size_t size, size_t alignment) {
254+
if (!node) {
255+
return 0;
256+
}
251257

252-
if (curr->right) {
253-
curr = curr->right;
258+
// If the largest anywhere in this subtree is smaller than requested size,
259+
// it is impossible to find a fit. Backtrack immediately.
260+
if (node->subtree_max_gap < size) {
261+
return 0;
262+
}
254263

255-
while (curr) {
256-
stack[top++] = curr;
257-
curr = curr->left;
258-
}
264+
// Prefer lower addresses. If the left subtree might have a gap,
265+
// look there.
266+
if (node->left && node->left->subtree_max_gap >= size) {
267+
uintptr_t left = this->find_hole(node->left, size, alignment);
268+
269+
if (left != 0) {
270+
return left;
259271
}
260272
}
261273

262-
return align_up(candidate, alignment);
274+
uintptr_t candidate = align_up(node->end(), alignment);
275+
size_t align_overhead = candidate - node->end();
276+
277+
// Ensure that the gap is physically large enoguht to hold (size + alignment adjustment)
278+
if (node->gap >= (size + align_overhead)) {
279+
return candidate;
280+
}
281+
282+
// We failed, try right.
283+
if (node->right && node->right->subtree_max_gap >= size) {
284+
return this->find_hole(node->right, size, alignment);
285+
}
286+
287+
// We failed, Mr. Stark.
288+
return 0;
263289
}
264290

265291
void VirtualMemoryAllocator::insert_region(uintptr_t start, size_t size, uint8_t flags,
@@ -276,6 +302,7 @@ void VirtualMemoryAllocator::insert_region_locked(uintptr_t start, size_t size,
276302
z->flags = flags;
277303
z->cache = cache;
278304
z->is_red = true;
305+
z->left = z->right = nullptr;
279306

280307
VmRegion* y = nullptr;
281308
VmRegion* x = this->root;
@@ -300,20 +327,81 @@ void VirtualMemoryAllocator::insert_region_locked(uintptr_t start, size_t size,
300327
y->right = z;
301328
}
302329

330+
// We need to find the node strictly before Z (predecssor) and
331+
// strictly after Z (successor) in address order to set the gaps
332+
// correctly.
333+
VmRegion* pred = this->predecessor(z);
334+
VmRegion* succ = this->successor(z);
335+
336+
if (succ) {
337+
// Distance to successor
338+
z->gap = succ->start - z->end();
339+
} else {
340+
// Z is the last node in memory. Gap is effectively infinite
341+
z->gap = static_cast<uintptr_t>(-1) - z->end();
342+
}
343+
344+
// Distance to Z
345+
if (pred) {
346+
pred->gap = z->start - pred->end();
347+
}
348+
349+
this->update_node_metadata(z);
350+
351+
// We must walk up from `z`. If we have a predessor, we must also walk up
352+
// from `pred` if `pred` is not an ancestor of `z`.
353+
VmRegion* iterator = z;
354+
while (iterator) {
355+
this->update_node_metadata(iterator);
356+
iterator = iterator->parent;
357+
}
358+
359+
if (pred) {
360+
iterator = pred;
361+
362+
while (iterator) {
363+
this->update_node_metadata(iterator);
364+
iterator = iterator->parent;
365+
}
366+
}
367+
368+
// Rebalance
303369
this->insert_fixup(z);
304370
}
305371

306372
void VirtualMemoryAllocator::delete_node_locked(VmRegion* z) {
373+
// Since we are about to remove the logical range z. We must
374+
// merge z's gap into its predessor's gap.
375+
// Now new Gap for Pred = (gap before z) + (z's size) + (gap after z)
376+
VmRegion* pred = this->predecessor(z);
377+
if (pred) {
378+
pred->gap += z->size + z->gap;
379+
}
380+
307381
VmRegion *x, *y;
308382
bool original_y_red = false;
309383

310384
if (!z->left || !z->right) {
311385
y = z;
312386
} else {
313387
y = z->right;
314-
while (y->left) y = y->left;
388+
389+
while (y->left) {
390+
y = y->left;
391+
}
392+
}
393+
394+
original_y_red = y->is_red;
395+
if (y->left) {
396+
x = y->left;
397+
} else {
398+
x = y->right;
315399
}
316400

401+
// Capture y's parent before we move pointers, so we know where
402+
// to start updating metadata
403+
VmRegion* update_start = y->parent;
404+
317405
original_y_red = y->is_red;
318406
if (y->left) {
319407
x = y->left;
@@ -338,12 +426,36 @@ void VirtualMemoryAllocator::delete_node_locked(VmRegion* z) {
338426
z->size = y->size;
339427
z->flags = y->flags;
340428
z->cache = y->cache;
429+
430+
// z now represents the region y used to hold.
431+
z->gap = y->gap;
341432
}
342433

343434
if (!original_y_red && x) {
344435
this->delete_fixup(x);
345436
}
346437

438+
// We modified the tree structure at y->parent. We must walk up
439+
// to root to recalculate `subtree_max_gap`.
440+
// If y != z, z is an ancestor of y, so this will also fix z's
441+
// metadata too.
442+
VmRegion* curr = update_start;
443+
while (curr) {
444+
this->update_node_metadata(curr);
445+
curr = curr->parent;
446+
}
447+
448+
// We modified `pred->gap` at the beginning. If `pred` was not an
449+
// ancestor of `update_start`, its subtree metadata is now stale.
450+
// Just walk up from pred too.
451+
if (pred) {
452+
curr = pred;
453+
while (curr) {
454+
this->update_node_metadata(curr);
455+
curr = curr->parent;
456+
}
457+
}
458+
347459
this->metadata_allocator.deallocate(y);
348460
}
349461

@@ -367,6 +479,9 @@ void VirtualMemoryAllocator::rotate_left(VmRegion* x) {
367479

368480
y->left = x;
369481
x->parent = y;
482+
483+
this->update_node_metadata(x);
484+
this->update_node_metadata(y);
370485
}
371486

372487
void VirtualMemoryAllocator::rotate_right(VmRegion* x) {
@@ -389,6 +504,9 @@ void VirtualMemoryAllocator::rotate_right(VmRegion* x) {
389504

390505
y->right = x;
391506
x->parent = y;
507+
508+
this->update_node_metadata(x);
509+
this->update_node_metadata(y);
392510
}
393511

394512
void VirtualMemoryAllocator::insert_fixup(VmRegion* z) {
@@ -512,4 +630,75 @@ void VirtualMemoryAllocator::delete_fixup(VmRegion* x) {
512630
x->is_red = false;
513631
}
514632
}
633+
634+
void VirtualMemoryAllocator::update_node_metadata(VmRegion* x) {
635+
if (!x) {
636+
return;
637+
}
638+
639+
size_t left_max = x->left ? x->left->subtree_max_gap : 0;
640+
size_t right_max = x->right ? x->right->subtree_max_gap : 0;
641+
642+
size_t curr_max = left_max;
643+
644+
if (right_max > curr_max) {
645+
curr_max = right_max;
646+
}
647+
648+
if (x->gap > curr_max) {
649+
curr_max = x->gap;
650+
}
651+
652+
x->subtree_max_gap = curr_max;
653+
}
654+
655+
VmRegion* VirtualMemoryAllocator::predecessor(VmRegion* node) {
656+
// If the left subtree is not null, the successor is the
657+
// rightmost (maximum) node in the left subtree.
658+
if (node->left) {
659+
node = node->left;
660+
661+
while (node->right) {
662+
node = node->right;
663+
}
664+
665+
return node;
666+
}
667+
668+
// If the left subtree is null, the successor is the lowest
669+
// ancestor for which 'x' is in the right subtree. We walk up
670+
// the tree until we are no longer a left child.
671+
VmRegion* p = node->parent;
672+
while (p && node == p->left) {
673+
node = p;
674+
p = p->parent;
675+
}
676+
677+
return p;
678+
}
679+
680+
VmRegion* VirtualMemoryAllocator::successor(VmRegion* node) {
681+
// If the right subtree is not null, the successor is the
682+
// leftmost (minimum) node in the right subtree.
683+
if (node->right) {
684+
node = node->right;
685+
686+
while (node->left) {
687+
node = node->left;
688+
}
689+
690+
return node;
691+
}
692+
693+
// If the right subtree is null, the successor is the lowest
694+
// ancestor for which 'x' is in the left subtree. We walk up
695+
// the tree until we are no longer a right child.
696+
VmRegion* p = node->parent;
697+
while (p && node == p->right) {
698+
node = p;
699+
p = p->parent;
700+
}
701+
702+
return p;
703+
}
515704
} // namespace kernel::memory

0 commit comments

Comments
 (0)