Fix a bug that causes uvm_pmr_get1page() to fail for allocations that
authorkettenis <kettenis@openbsd.org>
Sat, 20 Jun 2015 20:20:08 +0000 (20:20 +0000)
committerkettenis <kettenis@openbsd.org>
Sat, 20 Jun 2015 20:20:08 +0000 (20:20 +0000)
specify an address constraint even when free pages that meet the constraint
are still available.  This happens because the old code was using the root
of the size tree as a starting point for a search down the address tree.
This meant only part of the address tree was searched, and that part could
very well not contain any of the pages that met the constraint.  Instead,
always walk the address tree from its root if the list of single pages is
empty and the root of the size tree doesn't meet our constraints.

From Visa Hankala.

ok deraadt@

sys/uvm/uvm_pmemrange.c

index 6bf6a75..a3bd809 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: uvm_pmemrange.c,v 1.44 2014/11/13 00:47:44 tedu Exp $ */
+/*     $OpenBSD: uvm_pmemrange.c,v 1.45 2015/06/20 20:20:08 kettenis Exp $     */
 
 /*
  * Copyright (c) 2009, 2010 Ariane van der Steldt <ariane@stack.nl>
@@ -1708,11 +1708,35 @@ uvm_pmr_get1page(psize_t count, int memtype_init, struct pglist *result,
                                found = TAILQ_NEXT(found, pageq);
 
                        if (found == NULL) {
-                               found = RB_ROOT(&pmr->size[memtype]);
-                               /* Size tree gives pg[1] instead of pg[0] */
+                               /*
+                                * Check if the size tree contains a range
+                                * that intersects with the boundaries. As the
+                                * allocation is for any page, try the smallest
+                                * range so that large ranges are preserved for
+                                * more constrained cases. Only one entry is
+                                * checked here, to avoid a brute-force search.
+                                *
+                                * Note that a size tree gives pg[1] instead of
+                                * pg[0].
+                                */
+                               found = RB_MIN(uvm_pmr_size,
+                                   &pmr->size[memtype]);
                                if (found != NULL) {
                                        found--;
-
+                                       if (!PMR_INTERSECTS_WITH(
+                                           atop(VM_PAGE_TO_PHYS(found)),
+                                           atop(VM_PAGE_TO_PHYS(found)) +
+                                           found->fpgsz, start, end))
+                                               found = NULL;
+                               }
+                       }
+                       if (found == NULL) {
+                               /*
+                                * Try address-guided search to meet the page
+                                * number constraints.
+                                */
+                               found = RB_ROOT(&pmr->addr);
+                               if (found != NULL) {
                                        found = uvm_pmr_rootupdate(pmr, found,
                                            start, end, memtype);
                                }