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