The uvm_pmr_get1page() function can fail even when there are suitable
free pages available. If there is no hit in the list of single pages in
a pmemrange, the function continues the lookup in the address tree.
The function takes the root of a size-sorted tree as the starting point
for the address tree search. Because the chosen root node is likely to
cover only a part of the whole address tree, the tree lookup might be
incomplete. This seems to be a mistake that was made while refactoring
uvm_pmemrange.c between revisions 1.8 and 1.10.

The patch below changes the logic to always use the address tree root.
I have tested it on amd64, i386 and octeon.

The issue appears to get masked easily in a running system because
there tend to be enough pages in circulation. I happened to notice it
while I was playing with an EdgeRouter Lite to get the full 512 MiB of
RAM usable. After having removed the #if 0's, in file
arch/octeon/octeon/machdep.c, that disable the second 256 MiB, the
system kept on hanging during the early steps of the init process. An
execve() call tried to allocate DMA'ble pages of memory, and this is
where uvm_pmr_get1page() failed. Due to bad luck, there always were an
address tree lookup that happened to process only the part of the tree
that did not meet the address constraints. The process ended up waiting
for the page daemon, and nothing was to happen since there were no
other process to release a suitable page.

With the patch, and the #if 0's removed, the EdgeRouter Lite has run
stably with the full memory available and completed several make build
cycles successfully.


Index: uvm/uvm_pmemrange.c
===================================================================
RCS file: src/sys/uvm/uvm_pmemrange.c,v
retrieving revision 1.44
diff -u -p -r1.44 uvm_pmemrange.c
--- uvm/uvm_pmemrange.c 13 Nov 2014 00:47:44 -0000      1.44
+++ uvm/uvm_pmemrange.c 16 Jun 2015 10:36:53 -0000
@@ -1708,11 +1708,8 @@ uvm_pmr_get1page(psize_t count, int memt
                                found = TAILQ_NEXT(found, pageq);
 
                        if (found == NULL) {
-                               found = RB_ROOT(&pmr->size[memtype]);
-                               /* Size tree gives pg[1] instead of pg[0] */
+                               found = RB_ROOT(&pmr->addr);
                                if (found != NULL) {
-                                       found--;
-
                                        found = uvm_pmr_rootupdate(pmr, found,
                                            start, end, memtype);
                                }

Reply via email to