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;