@@ -229,37 +229,63 @@ VmRegion* VirtualMemoryAllocator::find_node(uintptr_t start) {
229229}
230230
231231uintptr_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
265291void 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
306372void 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
372487void 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
394512void 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