@@ -221,6 +221,32 @@ static void treap_insert(struct vma *vma, enum which_treap which) {
221221 }
222222}
223223
224+ /**
225+ * Removes a VMA from a treap.
226+ */
227+ static void treap_remove (struct vma * vma , enum which_treap which ) {
228+ struct treap_head * node = & vma -> treaps [which ];
229+ assert (node -> priority );
230+
231+ if (!node -> children [0 ] && !node -> children [1 ]) {
232+ // We're a leaf; we can simply remove ourselves without needing to
233+ // rebalance.
234+ if (node -> parent ) {
235+ // We're not the root.
236+ assert (node -> parent -> treaps [which ].children [node -> which_child ] == vma );
237+ node -> parent -> treaps [which ].children [node -> which_child ] = nullptr ;
238+ } else {
239+ // We're the root.
240+ assert (vma -> allocator -> roots [which ] == vma );
241+ vma -> allocator -> roots [which ] = nullptr ;
242+ }
243+ } else {
244+ TODO ();
245+ }
246+
247+ treap_init (node , node -> value );
248+ }
249+
224250/**
225251 * Computes the bounds of a VMA.
226252 */
@@ -315,3 +341,94 @@ void vma_allocator_print(struct vma_allocator *allocator) {
315341 is_vma_free (vma ) ? "FREE" : "USED" );
316342 }
317343}
344+
345+ static struct vma * vma_find (struct vma_allocator * allocator , uaddr addr ) {
346+ struct vma * vma = allocator -> roots [BY_ADDR ];
347+ while (vma ) {
348+ uaddr start , end ;
349+ vma_bounds (vma , & start , & end );
350+ if (addr < start )
351+ vma = vma -> treaps [BY_ADDR ].children [0 ];
352+ else if (end <= addr )
353+ vma = vma -> treaps [BY_ADDR ].children [1 ];
354+ else
355+ break ;
356+ }
357+ return vma ;
358+ }
359+
360+ struct vma * vma_alloc_by_addr (struct vma_allocator * allocator , uaddr lo ,
361+ uaddr hi ) {
362+ assert ((lo & 0xfff ) == 0 && (hi & 0xfff ) == 0 );
363+ assert (lo < hi );
364+
365+ struct vma * vma = vma_find (allocator , lo );
366+ if (!vma )
367+ return nullptr ;
368+
369+ // If the VMA isn't free, we can't allocate.
370+ if (!is_vma_free (vma ))
371+ return nullptr ;
372+
373+ // If the same VMA doesn't contain the highest address, there must be another
374+ // allocation inside the range.
375+ uaddr vma_lo , vma_hi ;
376+ vma_bounds (vma , & vma_lo , & vma_hi );
377+ if (vma_hi < hi )
378+ return nullptr ;
379+
380+ // Now, we want to split the VMA we found to contain the allocation.
381+ //
382+ // The simplest thing to do is to remove the VMA from the treaps, split it,
383+ // and reinsert the split-out parts. We keep it in the list, so that we can
384+ // avoid re-traversing it later.
385+ //
386+ // Before we do, we allocate memory for the new VMAs to be stored, so we don't
387+ // have to "undo" work if allocation fails later.
388+ struct vma * new_vma_lo = nullptr , * new_vma_hi = nullptr ;
389+ if (vma_lo != lo )
390+ if (!(new_vma_lo = alloc (sizeof (struct vma ))))
391+ goto fail ;
392+ if (vma_hi != hi )
393+ if (!(new_vma_hi = alloc (sizeof (struct vma ))))
394+ goto fail ;
395+
396+ // Save a pointer to the previous VMA (or the allocator), so we can insert all
397+ // the VMAs into the list without searching.
398+ struct list_head * prev = vma -> list_head .prev ;
399+
400+ // Remove the VMA. This breaks invariants (the allocator may now be empty),
401+ // but we restore them later.
402+ list_remove (& vma -> list_head );
403+ treap_remove (vma , BY_ADDR );
404+ if (is_vma_free (vma ))
405+ treap_remove (vma , BY_SIZE );
406+
407+ // Initialize the VMAs and insert them into the list and treaps.
408+ if (new_vma_hi ) {
409+ vma_init (allocator , hi , vma_hi , new_vma_hi );
410+ list_unshift (prev , & new_vma_hi -> list_head );
411+ treap_insert (new_vma_hi , BY_ADDR );
412+ treap_insert (new_vma_hi , BY_SIZE );
413+ }
414+ vma_init (allocator , lo , hi , vma );
415+ list_unshift (prev , & vma -> list_head );
416+ treap_insert (vma , BY_ADDR );
417+ if (new_vma_lo ) {
418+ vma_init (allocator , vma_lo , lo , new_vma_lo );
419+ list_unshift (prev , & new_vma_lo -> list_head );
420+ treap_insert (new_vma_lo , BY_ADDR );
421+ treap_insert (new_vma_lo , BY_SIZE );
422+ }
423+
424+ // Mark our VMA as used.
425+ vma -> treaps [BY_SIZE ].priority = 0 ;
426+
427+ // Return the VMA.
428+ return vma ;
429+
430+ fail :
431+ free (new_vma_lo );
432+ free (new_vma_hi );
433+ return nullptr ;
434+ }
0 commit comments