summaryrefslogtreecommitdiffstats
path: root/sys/vm/vm_pageout.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/vm/vm_pageout.c')
-rw-r--r--sys/vm/vm_pageout.c112
1 files changed, 94 insertions, 18 deletions
diff --git a/sys/vm/vm_pageout.c b/sys/vm/vm_pageout.c
index 2cc738d..156d11a 100644
--- a/sys/vm/vm_pageout.c
+++ b/sys/vm/vm_pageout.c
@@ -121,7 +121,8 @@ static void vm_pageout(void);
static void vm_pageout_init(void);
static int vm_pageout_clean(vm_page_t);
static void vm_pageout_scan(struct vm_domain *vmd, int pass);
-static void vm_pageout_mightbe_oom(struct vm_domain *vmd, int pass);
+static void vm_pageout_mightbe_oom(struct vm_domain *vmd, int page_shortage,
+ int starting_page_shortage);
SYSINIT(pagedaemon_init, SI_SUB_KTHREAD_PAGE, SI_ORDER_FIRST, vm_pageout_init,
NULL);
@@ -158,6 +159,7 @@ int vm_pages_needed; /* Event on which pageout daemon sleeps */
int vm_pageout_deficit; /* Estimated number of pages deficit */
int vm_pageout_pages_needed; /* flag saying that the pageout daemon needs pages */
int vm_pageout_wakeup_thresh;
+static int vm_pageout_oom_seq = 12;
#if !defined(NO_SWAPPING)
static int vm_pageout_req_swapout; /* XXX */
@@ -217,6 +219,10 @@ static int pageout_lock_miss;
SYSCTL_INT(_vm, OID_AUTO, pageout_lock_miss,
CTLFLAG_RD, &pageout_lock_miss, 0, "vget() lock misses during pageout");
+SYSCTL_INT(_vm, OID_AUTO, pageout_oom_seq,
+ CTLFLAG_RW, &vm_pageout_oom_seq, 0,
+ "back-to-back calls to oom detector to start OOM");
+
#define VM_PAGEOUT_PAGE_COUNT 16
int vm_pageout_page_count = VM_PAGEOUT_PAGE_COUNT;
@@ -941,7 +947,7 @@ vm_pageout_scan(struct vm_domain *vmd, int pass)
long min_scan;
int act_delta, addl_page_shortage, deficit, maxscan, page_shortage;
int vnodes_skipped = 0;
- int maxlaunder, scan_tick, scanned;
+ int maxlaunder, scan_tick, scanned, starting_page_shortage;
int lockmode;
boolean_t queues_locked;
@@ -981,6 +987,7 @@ vm_pageout_scan(struct vm_domain *vmd, int pass)
page_shortage = vm_paging_target() + deficit;
} else
page_shortage = deficit = 0;
+ starting_page_shortage = page_shortage;
/*
* maxlaunder limits the number of dirty pages we flush per scan.
@@ -1358,6 +1365,12 @@ relock_queues:
(void)speedup_syncer();
/*
+ * If the inactive queue scan fails repeatedly to meet its
+ * target, kill the largest process.
+ */
+ vm_pageout_mightbe_oom(vmd, page_shortage, starting_page_shortage);
+
+ /*
* Compute the number of pages we want to try to move from the
* active queue to the inactive queue.
*/
@@ -1469,15 +1482,6 @@ relock_queues:
}
}
#endif
-
- /*
- * If we are critically low on one of RAM or swap and low on
- * the other, kill the largest process. However, we avoid
- * doing this on the first pass in order to give ourselves a
- * chance to flush out dirty vnode-backed pages and to allow
- * active pages to be moved to the inactive queue and reclaimed.
- */
- vm_pageout_mightbe_oom(vmd, pass);
}
static int vm_pageout_oom_vote;
@@ -1488,12 +1492,17 @@ static int vm_pageout_oom_vote;
* failed to reach free target is premature.
*/
static void
-vm_pageout_mightbe_oom(struct vm_domain *vmd, int pass)
+vm_pageout_mightbe_oom(struct vm_domain *vmd, int page_shortage,
+ int starting_page_shortage)
{
int old_vote;
- if (pass <= 1 || !((swap_pager_avail < 64 && vm_page_count_min()) ||
- (swap_pager_full && vm_paging_target() > 0))) {
+ if (starting_page_shortage <= 0 || starting_page_shortage !=
+ page_shortage)
+ vmd->vmd_oom_seq = 0;
+ else
+ vmd->vmd_oom_seq++;
+ if (vmd->vmd_oom_seq < vm_pageout_oom_seq) {
if (vmd->vmd_oom) {
vmd->vmd_oom = FALSE;
atomic_subtract_int(&vm_pageout_oom_vote, 1);
@@ -1501,6 +1510,12 @@ vm_pageout_mightbe_oom(struct vm_domain *vmd, int pass)
return;
}
+ /*
+ * Do not follow the call sequence until OOM condition is
+ * cleared.
+ */
+ vmd->vmd_oom_seq = 0;
+
if (vmd->vmd_oom)
return;
@@ -1526,6 +1541,65 @@ vm_pageout_mightbe_oom(struct vm_domain *vmd, int pass)
atomic_subtract_int(&vm_pageout_oom_vote, 1);
}
+/*
+ * The OOM killer is the page daemon's action of last resort when
+ * memory allocation requests have been stalled for a prolonged period
+ * of time because it cannot reclaim memory. This function computes
+ * the approximate number of physical pages that could be reclaimed if
+ * the specified address space is destroyed.
+ *
+ * Private, anonymous memory owned by the address space is the
+ * principal resource that we expect to recover after an OOM kill.
+ * Since the physical pages mapped by the address space's COW entries
+ * are typically shared pages, they are unlikely to be released and so
+ * they are not counted.
+ *
+ * To get to the point where the page daemon runs the OOM killer, its
+ * efforts to write-back vnode-backed pages may have stalled. This
+ * could be caused by a memory allocation deadlock in the write path
+ * that might be resolved by an OOM kill. Therefore, physical pages
+ * belonging to vnode-backed objects are counted, because they might
+ * be freed without being written out first if the address space holds
+ * the last reference to an unlinked vnode.
+ *
+ * Similarly, physical pages belonging to OBJT_PHYS objects are
+ * counted because the address space might hold the last reference to
+ * the object.
+ */
+static long
+vm_pageout_oom_pagecount(struct vmspace *vmspace)
+{
+ vm_map_t map;
+ vm_map_entry_t entry;
+ vm_object_t obj;
+ long res;
+
+ map = &vmspace->vm_map;
+ KASSERT(!map->system_map, ("system map"));
+ sx_assert(&map->lock, SA_LOCKED);
+ res = 0;
+ for (entry = map->header.next; entry != &map->header;
+ entry = entry->next) {
+ if ((entry->eflags & MAP_ENTRY_IS_SUB_MAP) != 0)
+ continue;
+ obj = entry->object.vm_object;
+ if (obj == NULL)
+ continue;
+ if ((entry->eflags & MAP_ENTRY_NEEDS_COPY) != 0 &&
+ obj->ref_count != 1)
+ continue;
+ switch (obj->type) {
+ case OBJT_DEFAULT:
+ case OBJT_SWAP:
+ case OBJT_PHYS:
+ case OBJT_VNODE:
+ res += obj->resident_page_count;
+ break;
+ }
+ }
+ return (res);
+}
+
void
vm_pageout_oom(int shortage)
{
@@ -1570,7 +1644,8 @@ vm_pageout_oom(int shortage)
if (!TD_ON_RUNQ(td) &&
!TD_IS_RUNNING(td) &&
!TD_IS_SLEEPING(td) &&
- !TD_IS_SUSPENDED(td)) {
+ !TD_IS_SUSPENDED(td) &&
+ !TD_IS_SWAPPED(td)) {
thread_unlock(td);
breakout = 1;
break;
@@ -1598,12 +1673,13 @@ vm_pageout_oom(int shortage)
}
PROC_UNLOCK(p);
size = vmspace_swap_count(vm);
- vm_map_unlock_read(&vm->vm_map);
if (shortage == VM_OOM_MEM)
- size += vmspace_resident_count(vm);
+ size += vm_pageout_oom_pagecount(vm);
+ vm_map_unlock_read(&vm->vm_map);
vmspace_free(vm);
+
/*
- * if the this process is bigger than the biggest one
+ * If this process is bigger than the biggest one,
* remember it.
*/
if (size > bigsize) {
OpenPOWER on IntegriCloud