arena: fix xalloc minaddr|maxaddr
Two problems:
- if you had a single segment that contained minaddr, we'd skip that
segment and start with the *next* one. Not only did this mean we
skipped a perfectly good segment that could have had the fix, but we
might not have a *next* segment, causing an OOM / failure.
The fix was to find the BT that contained minaddr, not just the strict
upper bound. This means we need to handle the case where BT contains
minaddr: hence try_start and try_size.
- We'd scan the list of all nodes starting from the upper bound (and now
equality), regardless of whether or not they are free. Yikes!
Signed-off-by: Barret Rhoden <brho@cs.berkeley.edu>
diff --git a/kern/src/arena.c b/kern/src/arena.c
index 4ebc089..ff53d92 100644
--- a/kern/src/arena.c
+++ b/kern/src/arena.c
@@ -882,17 +882,24 @@
* is not. */
}
-/* Helper. We want the first bt >= mindaddr, with prev < minaddr. */
+/* Helper. We want either the BT containing minaddr, or the closest to it
+ * (above). There might be no BTs containing it, above it, or below it. */
static bool __found_least_upper_btag(struct btag *bt, uintptr_t minaddr)
{
struct rb_node *prev;
+ struct btag *btp;
- if (bt->start < minaddr)
- return FALSE;
+ if (bt->start + bt->size <= minaddr)
+ return FALSE; /* We are strictly below */
+ if (bt->start <= minaddr)
+ return TRUE; /* We contain it */
prev = rb_prev(&bt->all_link);
if (!prev)
- return TRUE;
- if (container_of(prev, struct btag, all_link)->start < minaddr)
+ return TRUE; /* We are above, but no one else below */
+ /* We are above and not containing. If our prev is below min, then
+ * we're it. */
+ btp = container_of(prev, struct btag, all_link);
+ if (btp->start + btp->size <= minaddr)
return TRUE;
return FALSE;
}
@@ -904,9 +911,9 @@
{
struct rb_node *node = arena->all_segs.rb_node;
struct btag *bt;
- uintptr_t try;
+ uintptr_t try, try_start, try_size;
- /* Find the first bt >= minaddr */
+ /* Find the first BT containing, or >= minaddr */
while (node) {
bt = container_of(node, struct btag, all_link);
if (__found_least_upper_btag(bt, minaddr))
@@ -917,10 +924,20 @@
node = node->rb_right;
}
/* Now we're probably at the first start point (or there's no node).
- * Just scan from here. */
+ * Just scan from here. The first node could contain minaddr, so we
+ * need to round up. Also note that this is *all* segs, including
+ * non-free ones. */
for (/* node set */; node; node = rb_next(node)) {
bt = container_of(node, struct btag, all_link);
- try = __find_sufficient(bt->start, bt->size, size, np2sb, phase,
+ if (bt->status != BTAG_FREE)
+ continue;
+ try_start = bt->start;
+ try_size = bt->size;
+ if (bt->start < minaddr) {
+ try_start = minaddr;
+ try_size = bt->size - (minaddr - bt->start);
+ }
+ try = __find_sufficient(try_start, try_size, size, np2sb, phase,
nocross);
if (!try)
continue;