summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Braun <rbraun@sceen.net>2016-12-27 19:51:30 +0100
committerRichard Braun <rbraun@sceen.net>2016-12-27 19:51:30 +0100
commit941d462425fb2692fd9ffea1ab03e927697fcfb0 (patch)
tree7f18f1b5c1f3f7ec5fc410b3e08310d893ad0e32
parenta926bb3a5bbd9cd5db6f1435d32312a5499ed62d (diff)
VM: really fix pageout of external objects backed by the default pager
Commit eb07428ffb0009085fcd01dd1b79d9953af8e0ad does fix pageout of external objects backed by the default pager, but the way it's done has a vicious side effect: because they're considered external, the pageout daemon can keep evicting them even though the external pagers haven't released them, unlike internal pages which must all be released before the pageout daemon can make progress. This can lead to a situation where too many pages become wired, the default pager cannot allocate memory to process new requests, and the pageout daemon cannot recycle any more page, causing a panic. This change makes the pageout daemon use the same strategy for both internal pages and external pages sent to the default pager: use the laundry bit and wait for all laundry pages to be released, thereby completely synchronizing the pageout daemon and the default pager. * vm/vm_page.c (vm_page_can_move): Allow external laundry pages to be moved. (vm_page_seg_evict): Don't alter the `external_laundry' bit, merely disable double paging for external pages sent to the default pager. * vm/vm_pageout.c: Include vm/memory_object.h. (vm_pageout_setup): Don't check whether the `external_laundry' bit is set, but handle external pages sent to the default pager the same as internal pages.
-rw-r--r--vm/vm_page.c22
-rw-r--r--vm/vm_pageout.c13
2 files changed, 10 insertions, 25 deletions
diff --git a/vm/vm_page.c b/vm/vm_page.c
index a7dab114..9a7fa275 100644
--- a/vm/vm_page.c
+++ b/vm/vm_page.c
@@ -313,7 +313,6 @@ vm_page_can_move(const struct vm_page *page)
*/
return !page->busy
- && !page->external_laundry
&& !page->wanted
&& !page->absent
&& page->object->alive;
@@ -1157,26 +1156,19 @@ restart:
* fault could occur, during which the map would be locked.
* This fault would cause a new paging request to the default
* pager. Receiving that request would deadlock when trying to
- * lock the map again. Instead, the page isn't double paged.
- * The external_laundry bit is set to indicate this situation
- * to vm_pageout_setup.
+ * lock the map again. Instead, the page isn't double paged
+ * and vm_pageout_setup wires the page down, trusting the
+ * default pager as for internal pages.
*/
- assert(!page->laundry && !page->external_laundry);
+ assert(!page->laundry);
assert(!(double_paging && page->external));
- if (object->internal) {
+ if (object->internal || !alloc_paused ||
+ memory_manager_default_port(object->pager)) {
double_paging = FALSE;
} else {
- if (memory_manager_default_port(object->pager)) {
- double_paging = FALSE;
- page->external_laundry = TRUE;
- } else if (!alloc_paused) {
- double_paging = FALSE;
- } else {
- double_paging = TRUE;
- page->laundry = TRUE;
- }
+ double_paging = page->laundry = TRUE;
}
out:
diff --git a/vm/vm_pageout.c b/vm/vm_pageout.c
index 62a27f1a..575a9f5d 100644
--- a/vm/vm_pageout.c
+++ b/vm/vm_pageout.c
@@ -47,6 +47,7 @@
#include <kern/task.h>
#include <kern/thread.h>
#include <kern/printf.h>
+#include <vm/memory_object.h>
#include <vm/pmap.h>
#include <vm/vm_map.h>
#include <vm/vm_object.h>
@@ -253,21 +254,13 @@ vm_pageout_setup(
assert(!old_object->internal);
m->laundry = FALSE;
- } else if (old_object->internal) {
+ } else if (old_object->internal ||
+ memory_manager_default_port(old_object->pager)) {
m->laundry = TRUE;
vm_page_laundry_count++;
vm_page_wire(m);
} else {
- /*
- * The caller is telling us that this page belongs
- * to an external object managed by the default pager.
- * Wire it to avoid a deadlock on the default pager map.
- */
- if (m->external_laundry) {
- vm_page_wire(m);
- }
-
m->external_laundry = TRUE;
/*