#include "opt_vm.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/proc.h>
#include <sys/kthread.h>
#include <sys/resourcevar.h>
#include <sys/signalvar.h>
#include <sys/vnode.h>
#include <sys/malloc.h>
#include <sys/vmmeter.h>
#include <sys/conf.h>
#include <sys/sysctl.h>
#include <vm/vm.h>
#include <vm/vm_param.h>
#include <sys/lock.h>
#include <vm/vm_object.h>
#include <vm/vm_page.h>
#include <vm/vm_map.h>
#include <vm/vm_pageout.h>
#include <vm/vm_pager.h>
#include <vm/swap_pager.h>
#include <vm/vm_extern.h>
#include <sys/spinlock2.h>
#include <vm/vm_page2.h>
struct markers {
struct vm_page hold;
struct vm_page stat;
struct vm_page pact;
};
static int vm_pageout_page(vm_page_t m, long *max_launderp,
long *vnodes_skippedp, struct vnode **vpfailedp,
int pass, int vmflush_flags, long *counts);
static int vm_pageout_clean_helper (vm_page_t, int);
static void vm_pageout_free_page_calc (vm_size_t count);
static void vm_pageout_page_free(vm_page_t m) ;
__read_frequently struct thread *emergpager;
__read_frequently struct thread *pagethread;
static int sequence_emerg_pager;
#if !defined(NO_SWAPPING)
static void vm_daemon (void);
static struct thread *vmthread;
static struct kproc_desc vm_kp = {
"vmdaemon",
vm_daemon,
&vmthread
};
SYSINIT(vmdaemon, SI_SUB_KTHREAD_VM, SI_ORDER_FIRST, kproc_start, &vm_kp);
#endif
__read_mostly int vm_pages_needed = 0;
__read_mostly int vm_pageout_deficit = 0;
__read_mostly int vm_pageout_pages_needed = 0;
__read_mostly int vm_page_free_hysteresis = 16;
__read_mostly static time_t vm_pagedaemon_uptime;
#if !defined(NO_SWAPPING)
static int vm_daemon_needed;
#endif
__read_mostly static int vm_queue_idle_perc = 20;
__read_mostly static int vm_max_launder = 0;
__read_mostly static int vm_emerg_launder = 100;
__read_mostly static int vm_pageout_stats_actcmp = 0;
__read_mostly static int vm_pageout_stats_inamin = 16;
__read_mostly static int vm_pageout_stats_inalim = 4096;
__read_mostly static int vm_pageout_stats_scan = 0;
__read_mostly static int vm_pageout_stats_ticks = 0;
__read_mostly static int vm_pageout_algorithm = 0;
__read_mostly static int defer_swap_pageouts = 0;
__read_mostly static int disable_swap_pageouts = 0;
__read_mostly static u_int vm_anonmem_decline = ACT_DECLINE;
__read_mostly static u_int vm_filemem_decline = ACT_DECLINE * 2;
__read_mostly static int vm_pageout_debug;
__read_mostly static long vm_pageout_stats_rsecs = 300;
#if defined(NO_SWAPPING)
__read_mostly static int vm_swap_enabled=0;
#else
__read_mostly static int vm_swap_enabled=1;
#endif
__read_mostly int vm_pageout_memuse_mode=2;
__read_mostly int vm_pageout_allow_active=1;
SYSCTL_UINT(_vm, VM_PAGEOUT_ALGORITHM, anonmem_decline,
CTLFLAG_RW, &vm_anonmem_decline, 0, "active->inactive anon memory");
SYSCTL_INT(_vm, VM_PAGEOUT_ALGORITHM, filemem_decline,
CTLFLAG_RW, &vm_filemem_decline, 0, "active->inactive file cache");
SYSCTL_INT(_vm, OID_AUTO, page_free_hysteresis,
CTLFLAG_RW, &vm_page_free_hysteresis, 0,
"Free more pages than the minimum required");
SYSCTL_INT(_vm, OID_AUTO, queue_idle_perc,
CTLFLAG_RW, &vm_queue_idle_perc, 0, "page stats stop point, percent");
SYSCTL_INT(_vm, OID_AUTO, max_launder,
CTLFLAG_RW, &vm_max_launder, 0, "Limit dirty flushes in pageout");
SYSCTL_INT(_vm, OID_AUTO, emerg_launder,
CTLFLAG_RW, &vm_emerg_launder, 0, "Emergency pager minimum");
SYSCTL_INT(_vm, OID_AUTO, pageout_stats_actcmp,
CTLFLAG_RW, &vm_pageout_stats_actcmp, 0,
"Current dynamic act_count comparator");
SYSCTL_INT(_vm, OID_AUTO, pageout_stats_inamin,
CTLFLAG_RW, &vm_pageout_stats_inamin, 0,
"min out of lim tests must match");
SYSCTL_INT(_vm, OID_AUTO, pageout_stats_inalim,
CTLFLAG_RW, &vm_pageout_stats_inalim, 0,
"min out of lim tests must match");
SYSCTL_INT(_vm, OID_AUTO, pageout_stats_ticks,
CTLFLAG_RW, &vm_pageout_stats_ticks, 0,
"Interval for partial stats scan");
SYSCTL_INT(_vm, OID_AUTO, pageout_stats_scan,
CTLFLAG_RW, &vm_pageout_stats_scan, 0,
"hold/ACT scan count per interval");
SYSCTL_LONG(_vm, OID_AUTO, pageout_stats_rsecs,
CTLFLAG_RW, &vm_pageout_stats_rsecs, 0,
"min out of lim tests must match");
SYSCTL_INT(_vm, OID_AUTO, pageout_memuse_mode,
CTLFLAG_RW, &vm_pageout_memuse_mode, 0, "memoryuse resource mode");
SYSCTL_INT(_vm, OID_AUTO, pageout_allow_active,
CTLFLAG_RW, &vm_pageout_allow_active, 0, "allow inactive+active");
SYSCTL_INT(_vm, OID_AUTO, pageout_debug,
CTLFLAG_RW, &vm_pageout_debug, 0, "debug pageout pages (count)");
#if defined(NO_SWAPPING)
SYSCTL_INT(_vm, VM_SWAPPING_ENABLED, swap_enabled,
CTLFLAG_RD, &vm_swap_enabled, 0, "");
#else
SYSCTL_INT(_vm, VM_SWAPPING_ENABLED, swap_enabled,
CTLFLAG_RW, &vm_swap_enabled, 0, "Enable entire process swapout");
#endif
SYSCTL_INT(_vm, OID_AUTO, defer_swapspace_pageouts,
CTLFLAG_RW, &defer_swap_pageouts, 0, "Give preference to dirty pages in mem");
SYSCTL_INT(_vm, OID_AUTO, disable_swapspace_pageouts,
CTLFLAG_RW, &disable_swap_pageouts, 0, "Disallow swapout of dirty pages");
static int pageout_lock_miss;
SYSCTL_INT(_vm, OID_AUTO, pageout_lock_miss,
CTLFLAG_RD, &pageout_lock_miss, 0, "vget() lock misses during pageout");
int vm_page_max_wired;
static MALLOC_DEFINE(M_PAGEOUT, "pageout", "Pageout structures");
#if !defined(NO_SWAPPING)
static void vm_req_vmdaemon (void);
#endif
#define MAXSCAN_DIVIDER 10
#define VM_CACHE_SCAN_MIN 16
#define VM_CACHE_SCAN_NOM (VM_CACHE_SCAN_MIN * 4)
static __inline long
PQAVERAGE(long n)
{
long avg;
if (n >= 0) {
avg = ((n + (PQ_L2_SIZE - 1)) / (PQ_L2_SIZE / 2) + 1);
} else {
avg = ((n - (PQ_L2_SIZE - 1)) / (PQ_L2_SIZE / 2) - 1);
}
return avg;
}
static int
vm_pageout_clean_helper(vm_page_t m, int vmflush_flags)
{
vm_object_t object;
vm_page_t mc[BLIST_MAX_ALLOC];
int error;
int ib, is, page_base;
vm_pindex_t pindex = m->pindex;
object = m->object;
if (m->hold_count != 0 || (m->flags & PG_UNQUEUED)) {
vm_page_wakeup(m);
return 0;
}
page_base = pindex % BLIST_MAX_ALLOC;
mc[page_base] = m;
ib = page_base - 1;
is = page_base + 1;
vm_object_hold(object);
while (ib >= 0) {
vm_page_t p;
p = vm_page_lookup_busy_try(object, pindex - page_base + ib,
TRUE, &error);
if (error || p == NULL)
break;
if ((p->queue - p->pc) == PQ_CACHE ||
(p->flags & PG_UNQUEUED)) {
vm_page_wakeup(p);
break;
}
vm_page_test_dirty(p);
if (((p->dirty & p->valid) == 0 &&
(p->flags & PG_NEED_COMMIT) == 0) ||
p->wire_count != 0 ||
p->hold_count != 0) {
vm_page_wakeup(p);
break;
}
if (p->queue - p->pc != PQ_INACTIVE) {
if (p->queue - p->pc != PQ_ACTIVE ||
(vmflush_flags & OBJPC_ALLOW_ACTIVE) == 0) {
vm_page_wakeup(p);
break;
}
}
if (m->flags & PG_WINATCFLS)
vm_page_flag_set(p, PG_WINATCFLS);
else
vm_page_flag_clear(p, PG_WINATCFLS);
p->act_count = m->act_count;
mc[ib] = p;
--ib;
}
++ib;
while (is < BLIST_MAX_ALLOC &&
pindex - page_base + is < object->size) {
vm_page_t p;
p = vm_page_lookup_busy_try(object, pindex - page_base + is,
TRUE, &error);
if (error || p == NULL)
break;
if (((p->queue - p->pc) == PQ_CACHE) ||
(p->flags & PG_UNQUEUED)) {
vm_page_wakeup(p);
break;
}
vm_page_test_dirty(p);
if (((p->dirty & p->valid) == 0 &&
(p->flags & PG_NEED_COMMIT) == 0) ||
p->wire_count != 0 ||
p->hold_count != 0) {
vm_page_wakeup(p);
break;
}
if (p->queue - p->pc != PQ_INACTIVE) {
if (p->queue - p->pc != PQ_ACTIVE ||
(vmflush_flags & OBJPC_ALLOW_ACTIVE) == 0) {
vm_page_wakeup(p);
break;
}
}
if (m->flags & PG_WINATCFLS)
vm_page_flag_set(p, PG_WINATCFLS);
else
vm_page_flag_clear(p, PG_WINATCFLS);
p->act_count = m->act_count;
mc[is] = p;
++is;
}
vm_object_drop(object);
return vm_pageout_flush(&mc[ib], is - ib, vmflush_flags);
}
int
vm_pageout_flush(vm_page_t *mc, int count, int vmflush_flags)
{
vm_object_t object;
int pageout_status[count];
int numpagedout = 0;
int i;
for (i = 0; i < count; i++) {
KASSERT(mc[i]->valid == VM_PAGE_BITS_ALL,
("vm_pageout_flush page %p index %d/%d: partially "
"invalid page", mc[i], i, count));
vm_page_io_start(mc[i]);
}
for (i = 0; i < count; i++) {
if (vmflush_flags & OBJPC_TRY_TO_CACHE)
vm_page_protect(mc[i], VM_PROT_NONE);
else
vm_page_protect(mc[i], VM_PROT_READ);
vm_page_wakeup(mc[i]);
}
object = mc[0]->object;
vm_object_pip_add(object, count);
vm_pager_put_pages(object, mc, count,
(vmflush_flags |
((object == kernel_object) ? OBJPC_SYNC : 0)),
pageout_status);
for (i = 0; i < count; i++) {
vm_page_t mt = mc[i];
switch (pageout_status[i]) {
case VM_PAGER_OK:
numpagedout++;
break;
case VM_PAGER_PEND:
numpagedout++;
break;
case VM_PAGER_BAD:
vm_page_busy_wait(mt, FALSE, "pgbad");
pmap_clear_modify(mt);
vm_page_undirty(mt);
vm_page_wakeup(mt);
break;
case VM_PAGER_ERROR:
case VM_PAGER_FAIL:
break;
case VM_PAGER_AGAIN:
break;
}
if (pageout_status[i] != VM_PAGER_PEND) {
vm_page_busy_wait(mt, FALSE, "pgouw");
vm_page_io_finish(mt);
if (vmflush_flags & OBJPC_TRY_TO_CACHE) {
vm_page_try_to_cache(mt);
} else if (vm_paging_severe()) {
vm_page_deactivate(mt);
vm_page_wakeup(mt);
} else {
vm_page_wakeup(mt);
}
vm_object_pip_wakeup(object);
}
}
return numpagedout;
}
#if !defined(NO_SWAPPING)
static
int
vm_pageout_mdp_callback(struct pmap_pgscan_info *info, vm_offset_t va,
vm_page_t p)
{
int actcount;
int cleanit = 0;
KKASSERT((p->flags & PG_MARKER) == 0);
if (pmap_resident_tlnw_count(info->pmap) <= info->limit) {
vm_page_wakeup(p);
return(-1);
}
mycpu->gd_cnt.v_pdpages++;
if (p->wire_count || p->hold_count || (p->flags & PG_UNQUEUED)) {
vm_page_wakeup(p);
goto done;
}
++info->actioncount;
actcount = pmap_ts_referenced(p);
if (actcount) {
vm_page_flag_set(p, PG_REFERENCED);
} else if (p->flags & PG_REFERENCED) {
actcount = 1;
}
if (actcount) {
if (p->queue - p->pc != PQ_ACTIVE) {
vm_page_and_queue_spin_lock(p);
if (p->queue - p->pc != PQ_ACTIVE) {
vm_page_and_queue_spin_unlock(p);
vm_page_activate(p);
} else {
vm_page_and_queue_spin_unlock(p);
}
} else {
p->act_count += actcount;
if (p->act_count > ACT_MAX)
p->act_count = ACT_MAX;
}
vm_page_flag_clear(p, PG_REFERENCED);
vm_page_wakeup(p);
goto done;
}
pmap_remove_specific(info->pmap, p);
if ((p->flags & PG_MAPPED) == 0 ||
(pmap_mapped_sync(p) & PG_MAPPED) == 0) {
if (p->queue - p->pc == PQ_ACTIVE) {
vm_page_deactivate(p);
}
if (p->queue - p->pc == PQ_INACTIVE) {
cleanit = 1;
}
}
if (cleanit) {
long max_launder = 0x7FFF;
long vnodes_skipped = 0;
long counts[4] = { 0, 0, 0, 0 };
int vmflush_flags;
struct vnode *vpfailed = NULL;
info->offset = va;
if (vm_pageout_memuse_mode >= 2) {
vmflush_flags = OBJPC_TRY_TO_CACHE |
OBJPC_ALLOW_ACTIVE;
if (swap_user_async == 0)
vmflush_flags |= OBJPC_SYNC;
vm_page_flag_set(p, PG_WINATCFLS);
info->cleancount +=
vm_pageout_page(p, &max_launder,
&vnodes_skipped,
&vpfailed, 1, vmflush_flags,
counts);
} else {
vm_page_wakeup(p);
++info->cleancount;
}
} else {
vm_page_wakeup(p);
}
done:
lwkt_user_yield();
return 0;
}
void
vm_pageout_map_deactivate_pages(vm_map_t map, vm_pindex_t limit)
{
vm_offset_t pgout_offset;
struct pmap_pgscan_info info;
int retries = 3;
pgout_offset = map->pgout_offset;
again:
#if 0
kprintf("%016jx ", pgout_offset);
#endif
if (pgout_offset < VM_MIN_USER_ADDRESS)
pgout_offset = VM_MIN_USER_ADDRESS;
if (pgout_offset >= VM_MAX_USER_ADDRESS)
pgout_offset = 0;
info.pmap = vm_map_pmap(map);
info.limit = limit;
info.beg_addr = pgout_offset;
info.end_addr = VM_MAX_USER_ADDRESS;
info.callback = vm_pageout_mdp_callback;
info.cleancount = 0;
info.actioncount = 0;
info.busycount = 0;
pmap_pgscan(&info);
pgout_offset = info.offset;
#if 0
kprintf("%016jx %08lx %08lx\n", pgout_offset,
info.cleancount, info.actioncount);
#endif
if (pgout_offset != VM_MAX_USER_ADDRESS &&
pmap_resident_tlnw_count(vm_map_pmap(map)) > limit) {
goto again;
} else if (retries &&
pmap_resident_tlnw_count(vm_map_pmap(map)) > limit) {
--retries;
goto again;
}
map->pgout_offset = pgout_offset;
}
#endif
static void
vm_pageout_page_free(vm_page_t m)
{
vm_page_protect(m, VM_PROT_NONE);
vm_page_free(m);
}
struct vm_pageout_scan_info {
struct proc *bigproc;
vm_offset_t bigsize;
};
static int vm_pageout_scan_callback(struct proc *p, void *data);
static int
vm_pageout_scan_inactive(int pass, int q, long avail_shortage,
long *vnodes_skipped, long *counts)
{
vm_page_t m;
struct vm_page marker;
struct vnode *vpfailed;
long maxscan;
long delta = 0;
long max_launder;
int isep;
int vmflush_flags;
isep = (curthread == emergpager);
max_launder = (long)vm_max_launder / PQ_L2_SIZE;
if (pass)
max_launder *= 2;
max_launder = (max_launder + MAXSCAN_DIVIDER - 1) / MAXSCAN_DIVIDER;
if (max_launder <= 1)
max_launder = 1;
if (max_launder >= vm_max_launder / 16)
max_launder = vm_max_launder / 16 + 1;
bzero(&marker, sizeof(marker));
marker.flags = PG_FICTITIOUS | PG_MARKER;
marker.busy_count = PBUSY_LOCKED;
marker.queue = PQ_INACTIVE + q;
marker.pc = q;
marker.wire_count = 1;
vpfailed = NULL;
vm_page_queues_spin_lock(PQ_INACTIVE + q);
TAILQ_INSERT_HEAD(&vm_page_queues[PQ_INACTIVE + q].pl, &marker, pageq);
maxscan = (vm_page_queues[PQ_INACTIVE + q].lcnt + MAXSCAN_DIVIDER - 1) /
MAXSCAN_DIVIDER + 1;
while ((m = TAILQ_NEXT(&marker, pageq)) != NULL &&
maxscan-- > 0 && avail_shortage - delta > 0)
{
int count;
KKASSERT(m->queue == PQ_INACTIVE + q);
TAILQ_REMOVE(&vm_page_queues[PQ_INACTIVE + q].pl,
&marker, pageq);
TAILQ_INSERT_AFTER(&vm_page_queues[PQ_INACTIVE + q].pl, m,
&marker, pageq);
mycpu->gd_cnt.v_pdpages++;
if (m->flags & PG_MARKER)
continue;
if (vm_page_busy_try(m, TRUE))
continue;
KKASSERT(m->queue == PQ_INACTIVE + q);
vm_page_queues_spin_unlock(PQ_INACTIVE + q);
if (isep && m->object) {
struct vnode *vp;
switch(m->object->type) {
case OBJT_DEFAULT:
case OBJT_SWAP:
break;
case OBJT_VNODE:
vp = m->object->handle;
if (vp && vp->v_type == VCHR &&
vp->v_rdev && vp->v_rdev->si_ops &&
(vp->v_rdev->si_ops->head.flags &
D_NOEMERGPGR) == 0) {
break;
}
default:
vm_page_wakeup(m);
vm_page_queues_spin_lock(PQ_INACTIVE + q);
lwkt_yield();
continue;
}
}
if (vm_pageout_memuse_mode >= 3)
vm_page_flag_set(m, PG_WINATCFLS);
vmflush_flags = 0;
if (vm_pageout_allow_active)
vmflush_flags |= OBJPC_ALLOW_ACTIVE;
if (m->flags & PG_WINATCFLS)
vmflush_flags |= OBJPC_TRY_TO_CACHE;
count = vm_pageout_page(m, &max_launder, vnodes_skipped,
&vpfailed, pass, vmflush_flags, counts);
delta += count;
lwkt_yield();
vm_page_queues_spin_lock(PQ_INACTIVE + q);
if (!vm_paging_target2()) {
if (delta < avail_shortage)
delta = avail_shortage;
break;
}
}
TAILQ_REMOVE(&vm_page_queues[PQ_INACTIVE + q].pl, &marker, pageq);
vm_page_queues_spin_unlock(PQ_INACTIVE + q);
return (delta);
}
static int
vm_pageout_page(vm_page_t m, long *max_launderp, long *vnodes_skippedp,
struct vnode **vpfailedp, int pass, int vmflush_flags,
long *counts)
{
vm_object_t object;
int actcount;
int count = 0;
if (m->wire_count) {
vm_page_unqueue_nowakeup(m);
vm_page_wakeup(m);
return 0;
}
if (m->hold_count) {
vm_page_and_queue_spin_lock(m);
if (m->queue - m->pc == PQ_INACTIVE) {
TAILQ_REMOVE(
&vm_page_queues[m->queue].pl, m, pageq);
TAILQ_INSERT_TAIL(
&vm_page_queues[m->queue].pl, m, pageq);
}
vm_page_and_queue_spin_unlock(m);
vm_page_wakeup(m);
return 0;
}
if (m->object == NULL || m->object->ref_count == 0) {
vm_page_flag_clear(m, PG_REFERENCED);
pmap_clear_reference(m);
} else if (((m->flags & PG_REFERENCED) == 0) &&
(actcount = pmap_ts_referenced(m))) {
++counts[3];
vm_page_activate(m);
m->act_count += (actcount + ACT_ADVANCE);
vm_page_wakeup(m);
return 0;
}
if ((m->flags & PG_REFERENCED) != 0) {
vm_page_flag_clear(m, PG_REFERENCED);
actcount = pmap_ts_referenced(m);
vm_page_activate(m);
m->act_count += (actcount + ACT_ADVANCE + 1);
vm_page_wakeup(m);
++counts[3];
return 0;
}
if (m->dirty == 0) {
vm_page_test_dirty(m);
} else {
vm_page_dirty(m);
}
if (m->valid == 0 && (m->flags & PG_NEED_COMMIT) == 0) {
vm_pageout_page_free(m);
mycpu->gd_cnt.v_dfree++;
++count;
++counts[1];
} else if (m->dirty == 0 && (m->flags & PG_NEED_COMMIT) == 0) {
vm_page_cache(m);
++count;
++counts[1];
} else if ((m->flags & PG_WINATCFLS) == 0 && pass == 0) {
++counts[2];
vm_page_flag_set(m, PG_WINATCFLS);
vm_page_and_queue_spin_lock(m);
if (m->queue - m->pc == PQ_INACTIVE) {
TAILQ_REMOVE(
&vm_page_queues[m->queue].pl, m, pageq);
TAILQ_INSERT_TAIL(
&vm_page_queues[m->queue].pl, m, pageq);
}
vm_page_and_queue_spin_unlock(m);
vm_page_wakeup(m);
} else if (*max_launderp > 0) {
int swap_pageouts_ok;
struct vnode *vp = NULL;
if ((m->flags & PG_WINATCFLS) == 0)
vm_page_flag_set(m, PG_WINATCFLS);
swap_pageouts_ok = 0;
object = m->object;
if (object &&
(object->type != OBJT_SWAP) &&
(object->type != OBJT_DEFAULT)) {
swap_pageouts_ok = 1;
} else {
swap_pageouts_ok = !(defer_swap_pageouts ||
disable_swap_pageouts);
swap_pageouts_ok |= (!disable_swap_pageouts &&
defer_swap_pageouts &&
vm_paging_min());
}
if (!swap_pageouts_ok ||
(object == NULL) ||
(object->flags & OBJ_DEAD)) {
vm_page_and_queue_spin_lock(m);
if (m->queue - m->pc == PQ_INACTIVE) {
TAILQ_REMOVE(
&vm_page_queues[m->queue].pl,
m, pageq);
TAILQ_INSERT_TAIL(
&vm_page_queues[m->queue].pl,
m, pageq);
}
vm_page_and_queue_spin_unlock(m);
vm_page_wakeup(m);
return 0;
}
if (object->type == OBJT_VNODE) {
int flags;
vp = object->handle;
flags = LK_EXCLUSIVE;
if (vp == *vpfailedp)
flags |= LK_NOWAIT;
else
flags |= LK_TIMELOCK;
vm_page_hold(m);
vm_page_wakeup(m);
if (vget(vp, flags) != 0) {
*vpfailedp = vp;
++pageout_lock_miss;
if (object->flags & OBJ_MIGHTBEDIRTY)
++*vnodes_skippedp;
vm_page_unhold(m);
return 0;
}
if (m->queue - m->pc != PQ_INACTIVE ||
m->object != object ||
object->handle != vp) {
if (object->flags & OBJ_MIGHTBEDIRTY)
++*vnodes_skippedp;
vput(vp);
vm_page_unhold(m);
return 0;
}
if (vm_page_busy_try(m, TRUE)) {
vput(vp);
vm_page_unhold(m);
return 0;
}
vm_page_unhold(m);
if (m->wire_count) {
vm_page_unqueue_nowakeup(m);
vput(vp);
vm_page_wakeup(m);
return 0;
}
if (m->hold_count) {
rebusy_failed:
vm_page_and_queue_spin_lock(m);
if (m->queue - m->pc == PQ_INACTIVE) {
TAILQ_REMOVE(&vm_page_queues[m->queue].pl, m, pageq);
TAILQ_INSERT_TAIL(&vm_page_queues[m->queue].pl, m, pageq);
}
vm_page_and_queue_spin_unlock(m);
if (object->flags & OBJ_MIGHTBEDIRTY)
++*vnodes_skippedp;
vm_page_wakeup(m);
vput(vp);
return 0;
}
if (m->queue - m->pc != PQ_INACTIVE ||
m->object != object ||
object->handle != vp) {
kprintf("vm_pageout_page: "
"rebusy %p failed(A)\n",
m);
goto rebusy_failed;
}
if (m->valid == 0 && (m->flags & PG_NEED_COMMIT) == 0) {
kprintf("vm_pageout_page: "
"rebusy %p failed(B)\n",
m);
goto rebusy_failed;
}
if (m->dirty == 0 && (m->flags & PG_NEED_COMMIT) == 0) {
kprintf("vm_pageout_page: "
"rebusy %p failed(C)\n",
m);
goto rebusy_failed;
}
}
count = vm_pageout_clean_helper(m, vmflush_flags);
counts[0] += count;
*max_launderp -= count;
if (vp != NULL)
vput(vp);
} else {
vm_page_wakeup(m);
}
return count;
}
static int
vm_pageout_scan_active(int pass, int q,
long avail_shortage, long inactive_shortage,
struct vm_page *marker,
long *recycle_countp)
{
vm_page_t m;
int actcount;
long delta = 0;
long maxscan;
int isep;
isep = (curthread == emergpager);
vm_page_queues_spin_lock(PQ_ACTIVE + q);
maxscan = (vm_page_queues[PQ_ACTIVE + q].lcnt + MAXSCAN_DIVIDER - 1) /
MAXSCAN_DIVIDER + 1;
while ((m = TAILQ_NEXT(marker, pageq)) != NULL &&
maxscan-- > 0 && (avail_shortage - delta > 0 ||
inactive_shortage > 0))
{
KKASSERT(m->queue == PQ_ACTIVE + q);
TAILQ_REMOVE(&vm_page_queues[PQ_ACTIVE + q].pl,
marker, pageq);
TAILQ_INSERT_AFTER(&vm_page_queues[PQ_ACTIVE + q].pl, m,
marker, pageq);
if (m->flags & PG_MARKER)
continue;
if (vm_page_busy_try(m, TRUE))
continue;
KKASSERT(m->queue == PQ_ACTIVE + q);
vm_page_queues_spin_unlock(PQ_ACTIVE + q);
#if 0
if (m->hold_count) {
vm_page_and_queue_spin_lock(m);
if (m->queue - m->pc == PQ_ACTIVE) {
TAILQ_REMOVE(
&vm_page_queues[PQ_ACTIVE + q].pl,
m, pageq);
TAILQ_INSERT_TAIL(
&vm_page_queues[PQ_ACTIVE + q].pl,
m, pageq);
}
vm_page_and_queue_spin_unlock(m);
vm_page_wakeup(m);
goto next;
}
#endif
if (m->wire_count) {
vm_page_unqueue_nowakeup(m);
vm_page_wakeup(m);
goto next;
}
if (isep && m->object && m->object->type == OBJT_VNODE) {
#if 0
vm_page_and_queue_spin_lock(m);
if (m->queue - m->pc == PQ_ACTIVE) {
TAILQ_REMOVE(
&vm_page_queues[PQ_ACTIVE + q].pl,
m, pageq);
TAILQ_INSERT_TAIL(
&vm_page_queues[PQ_ACTIVE + q].pl,
m, pageq);
}
vm_page_and_queue_spin_unlock(m);
#endif
vm_page_wakeup(m);
goto next;
}
mycpu->gd_cnt.v_pdpages++;
actcount = 0;
if (m->object && m->object->ref_count != 0) {
if (m->flags & PG_REFERENCED)
++actcount;
actcount += pmap_ts_referenced(m);
if (actcount) {
m->act_count += ACT_ADVANCE + actcount;
if (m->act_count > ACT_MAX)
m->act_count = ACT_MAX;
}
}
vm_page_flag_clear(m, PG_REFERENCED);
if (actcount && m->object && m->object->ref_count != 0) {
#if 0
vm_page_and_queue_spin_lock(m);
if (m->queue - m->pc == PQ_ACTIVE) {
TAILQ_REMOVE(
&vm_page_queues[PQ_ACTIVE + q].pl,
m, pageq);
TAILQ_INSERT_TAIL(
&vm_page_queues[PQ_ACTIVE + q].pl,
m, pageq);
}
vm_page_and_queue_spin_unlock(m);
#endif
vm_page_wakeup(m);
} else {
switch((m->object ? m->object->type : OBJT_DEFAULT)) {
case OBJT_DEFAULT:
case OBJT_SWAP:
m->act_count -= min(m->act_count,
vm_anonmem_decline);
break;
default:
m->act_count -= min(m->act_count,
vm_filemem_decline);
break;
}
if (vm_pageout_algorithm ||
(m->object == NULL) ||
(m->object && (m->object->ref_count == 0)) ||
m->act_count < pass + 1
) {
--inactive_shortage;
if (avail_shortage - delta > 0 ||
(m->object && (m->object->ref_count == 0)))
{
if (avail_shortage - delta > 0)
++*recycle_countp;
vm_page_protect(m, VM_PROT_NONE);
if (m->dirty == 0 &&
(m->flags & PG_NEED_COMMIT) == 0 &&
avail_shortage - delta > 0) {
vm_page_cache(m);
} else {
vm_page_deactivate(m);
vm_page_wakeup(m);
}
} else {
vm_page_deactivate(m);
vm_page_wakeup(m);
}
++delta;
} else {
#if 0
vm_page_and_queue_spin_lock(m);
if (m->queue - m->pc == PQ_ACTIVE) {
TAILQ_REMOVE(
&vm_page_queues[PQ_ACTIVE + q].pl,
m, pageq);
TAILQ_INSERT_TAIL(
&vm_page_queues[PQ_ACTIVE + q].pl,
m, pageq);
}
vm_page_and_queue_spin_unlock(m);
#endif
vm_page_wakeup(m);
}
}
next:
lwkt_yield();
vm_page_queues_spin_lock(PQ_ACTIVE + q);
}
if (m == NULL) {
TAILQ_REMOVE(&vm_page_queues[PQ_ACTIVE + q].pl,
marker, pageq);
TAILQ_INSERT_HEAD(&vm_page_queues[PQ_ACTIVE + q].pl,
marker, pageq);
}
vm_page_queues_spin_unlock(PQ_ACTIVE + q);
return (delta);
}
static void
vm_pageout_scan_cache(long avail_shortage, int pass,
long vnodes_skipped, long recycle_count)
{
static int lastkillticks;
struct vm_pageout_scan_info info;
vm_page_t m;
int isep;
isep = (curthread == emergpager);
while ((vmstats.v_free_count < vmstats.v_free_target ||
vmstats.v_free_count < vmstats.v_free_min) &&
vmstats.v_cache_count > VM_CACHE_SCAN_MIN)
{
static int cache_rover[2] = { 0, PQ_L2_MASK / 2 };
if (((cache_rover[0] ^ cache_rover[1]) & PQ_L2_MASK) == 0)
goto next_rover;
m = vm_page_list_find(PQ_CACHE, cache_rover[isep] & PQ_L2_MASK);
if (m == NULL)
break;
if (vm_page_busy_try(m, TRUE)) {
vm_page_deactivate_locked(m);
vm_page_spin_unlock(m);
continue;
}
vm_page_spin_unlock(m);
pagedaemon_wakeup();
lwkt_yield();
if ((m->flags & PG_MAPPED) || (m->valid & m->dirty)) {
kprintf("WARNING! page race during find/busy: %p "
"queue == %d dirty=%02x\n",
m, m->queue - m->pc, m->dirty);
}
if ((m->flags & (PG_UNQUEUED | PG_NEED_COMMIT | PG_MAPPED)) ||
m->hold_count ||
m->wire_count ||
(m->valid & m->dirty))
{
vm_page_deactivate(m);
vm_page_wakeup(m);
continue;
}
pmap_mapped_sync(m);
KKASSERT((m->flags & PG_MAPPED) == 0);
KKASSERT(m->dirty == 0);
vm_pageout_page_free(m);
mycpu->gd_cnt.v_dfree++;
next_rover:
if (isep)
cache_rover[1] -= PQ_PRIME2;
else
cache_rover[0] += PQ_PRIME2;
}
if (vm_paging_target1()) {
if (vnodes_skipped && vm_paging_min())
speedup_syncer(NULL);
#if !defined(NO_SWAPPING)
if (vm_swap_enabled && vm_paging_target1())
vm_req_vmdaemon();
#endif
}
if (swap_pager_almost_full &&
pass > 0 &&
isep == 0 &&
(vm_paging_min_dnc(recycle_count) || avail_shortage > 0)) {
kprintf("Warning: system low on memory+swap "
"shortage %ld for %d ticks!\n",
avail_shortage, ticks - swap_fail_ticks);
if (bootverbose) {
kprintf("Metrics: spaf=%d spf=%d pass=%d "
"availshrt=%ld tgt=%d/%d inacshrt=%ld "
"last=%u\n",
swap_pager_almost_full,
swap_pager_full,
pass,
avail_shortage,
vm_paging_target1(),
vm_paging_target2(),
vm_paging_target2_count(),
(unsigned int)(ticks - lastkillticks));
}
}
if (swap_pager_full &&
pass > 1 &&
isep == 0 &&
avail_shortage > 0 &&
vm_paging_target1() &&
(unsigned int)(ticks - lastkillticks) >= hz)
{
lastkillticks = ticks;
info.bigproc = NULL;
info.bigsize = 0;
allproc_scan(vm_pageout_scan_callback, &info, 0);
if (info.bigproc != NULL) {
kprintf("Try to kill process %d %s\n",
info.bigproc->p_pid, info.bigproc->p_comm);
info.bigproc->p_nice = PRIO_MIN;
info.bigproc->p_usched->resetpriority(
FIRST_LWP_IN_PROC(info.bigproc));
atomic_set_int(&info.bigproc->p_flags, P_LOWMEMKILL);
killproc(info.bigproc, "out of swap space");
wakeup(&vmstats.v_free_count);
PRELE(info.bigproc);
}
}
}
static int
vm_pageout_scan_callback(struct proc *p, void *data)
{
struct vm_pageout_scan_info *info = data;
vm_offset_t size;
if ((p->p_flags & P_SYSTEM) || (p->p_pid == 1) ||
((p->p_pid < 48) && (vm_swap_size != 0))) {
return (0);
}
lwkt_gettoken(&p->p_token);
if (p->p_stat != SACTIVE && p->p_stat != SSTOP && p->p_stat != SCORE) {
lwkt_reltoken(&p->p_token);
return (0);
}
size = vmspace_anonymous_count(p->p_vmspace) +
vmspace_swap_count(p->p_vmspace);
if (info->bigsize < size) {
if (info->bigproc)
PRELE(info->bigproc);
PHOLD(p);
info->bigproc = p;
info->bigsize = size;
}
lwkt_reltoken(&p->p_token);
lwkt_yield();
return(0);
}
static void
vm_pageout_scan_hold(int q, struct vm_page *marker)
{
vm_page_t m;
long pcount;
pcount = vm_page_queues[PQ_HOLD + q].lcnt;
if (pcount > vm_pageout_stats_scan)
pcount = vm_pageout_stats_scan;
vm_page_queues_spin_lock(PQ_HOLD + q);
while ((m = TAILQ_NEXT(marker, pageq)) != NULL &&
pcount-- > 0)
{
KKASSERT(m->queue == PQ_HOLD + q);
TAILQ_REMOVE(&vm_page_queues[PQ_HOLD + q].pl, marker, pageq);
TAILQ_INSERT_AFTER(&vm_page_queues[PQ_HOLD + q].pl, m,
marker, pageq);
if (m->flags & PG_MARKER)
continue;
if (m->hold_count)
break;
kprintf("DEBUG: pageout HOLD->FREE %p\n", m);
vm_page_hold(m);
vm_page_queues_spin_unlock(PQ_HOLD + q);
vm_page_unhold(m);
vm_page_queues_spin_lock(PQ_HOLD + q);
}
if (m == NULL) {
TAILQ_REMOVE(&vm_page_queues[PQ_HOLD + q].pl,
marker, pageq);
TAILQ_INSERT_HEAD(&vm_page_queues[PQ_HOLD + q].pl,
marker, pageq);
}
vm_page_queues_spin_unlock(PQ_HOLD + q);
}
static void
vm_pageout_page_stats(int q, struct vm_page *marker, long *counterp)
{
struct vpgqueues *pq = &vm_page_queues[PQ_ACTIVE + q];
vm_page_t m;
long pcount;
if (vmstats.v_active_count < vmstats.v_free_count +
vmstats.v_cache_count +
vmstats.v_inactive_count)
{
return;
}
if (vm_pages_needed == 0 && !vm_paging_inactive()) {
if (time_uptime - vm_pagedaemon_uptime > vm_pageout_stats_rsecs)
return;
}
if (vm_pageout_debug) {
static time_t save_time;
if (save_time != time_uptime) {
save_time = time_uptime;
kprintf("DEACTIVATE Q=%4d N=%ld\n",
q, vm_paging_inactive_count());
}
}
pcount = pq->lcnt;
if (pcount > vm_pageout_stats_scan)
pcount = vm_pageout_stats_scan;
vm_page_queues_spin_lock(PQ_ACTIVE + q);
while ((m = TAILQ_NEXT(marker, pageq)) != NULL &&
pcount-- > 0)
{
int actcount;
KKASSERT(m->queue == PQ_ACTIVE + q);
TAILQ_REMOVE(&pq->pl, marker, pageq);
TAILQ_INSERT_AFTER(&pq->pl, m, marker, pageq);
if (m->flags & PG_MARKER)
continue;
++counterp[0];
if (vm_page_busy_try(m, TRUE)) {
continue;
}
KKASSERT(m->queue == PQ_ACTIVE + q);
vm_page_queues_spin_unlock(PQ_ACTIVE + q);
if (m->wire_count) {
vm_page_unqueue_nowakeup(m);
vm_page_wakeup(m);
goto next;
}
if (m->hold_count || m->wire_count) {
vm_page_wakeup(m);
goto next;
}
actcount = 0;
if (m->flags & PG_REFERENCED) {
vm_page_flag_clear(m, PG_REFERENCED);
actcount += 1;
}
actcount += pmap_ts_referenced(m);
if (actcount) {
m->act_count += ACT_ADVANCE + actcount;
if (m->act_count > ACT_MAX)
m->act_count = ACT_MAX;
#if 0
vm_page_and_queue_spin_lock(m);
if (m->queue - m->pc == PQ_ACTIVE) {
TAILQ_REMOVE(&pq->pl, m, pageq);
TAILQ_INSERT_TAIL(&pq->pl, m, pageq);
}
vm_page_and_queue_spin_unlock(m);
#endif
vm_page_wakeup(m);
goto next;
}
if (m->act_count == 0) {
if (vm_paging_inactive()) {
vm_page_protect(m, VM_PROT_NONE);
vm_page_deactivate(m);
}
++counterp[1];
} else {
m->act_count -= min(m->act_count, ACT_DECLINE);
#if 0
vm_page_and_queue_spin_lock(m);
if (m->queue - m->pc == PQ_ACTIVE) {
TAILQ_REMOVE(&pq->pl, m, pageq);
TAILQ_INSERT_TAIL(&pq->pl, m, pageq);
}
vm_page_and_queue_spin_unlock(m);
#endif
if (m->act_count < vm_pageout_stats_actcmp) {
if (vm_paging_inactive()) {
vm_page_protect(m, VM_PROT_NONE);
vm_page_deactivate(m);
}
++counterp[1];
}
}
vm_page_wakeup(m);
next:
vm_page_queues_spin_lock(PQ_ACTIVE + q);
}
if (m == NULL) {
TAILQ_REMOVE(&pq->pl, marker, pageq);
TAILQ_INSERT_HEAD(&pq->pl, marker, pageq);
}
vm_page_queues_spin_unlock(PQ_ACTIVE + q);
if (counterp[0] > vm_pageout_stats_inalim) {
if (counterp[1] < vm_pageout_stats_inamin) {
if (vm_pageout_stats_actcmp < ACT_MAX * 3 / 4)
++vm_pageout_stats_actcmp;
} else {
if (vm_pageout_stats_actcmp > 0)
--vm_pageout_stats_actcmp;
}
counterp[0] = 0;
counterp[1] = 0;
}
}
static void
vm_pageout_free_page_calc(vm_size_t count)
{
vmstats.v_free_min = 64 + vmstats.v_page_count / 200;
vmstats.v_free_reserved = vmstats.v_free_min * 4 / 8 + 7;
vmstats.v_free_severe = vmstats.v_free_min * 4 / 8 + 0;
vmstats.v_pageout_free_min = vmstats.v_free_min * 2 / 8 + 7;
vmstats.v_interrupt_free_min = vmstats.v_free_min * 1 / 8 + 7;
}
static void
vm_pageout_thread(void)
{
int pass;
int q;
int q1iterator = 0;
int q2iterator = 0;
int q3iterator = 0;
int isep;
enum { PAGING_IDLE, PAGING_TARGET1, PAGING_TARGET2 } state;
struct markers *markers;
long scounter[2] = { 0, 0 };
time_t warn_time;
curthread->td_flags |= TDF_SYSTHREAD;
state = PAGING_IDLE;
markers = kmalloc(sizeof(*markers) * PQ_L2_SIZE,
M_PAGEOUT, M_WAITOK | M_ZERO);
for (q = 0; q < PQ_L2_SIZE; ++q) {
struct markers *mark = &markers[q];
mark->hold.flags = PG_FICTITIOUS | PG_MARKER;
mark->hold.busy_count = PBUSY_LOCKED;
mark->hold.queue = PQ_HOLD + q;
mark->hold.pc = PQ_HOLD + q;
mark->hold.wire_count = 1;
vm_page_queues_spin_lock(PQ_HOLD + q);
TAILQ_INSERT_HEAD(&vm_page_queues[PQ_HOLD + q].pl,
&mark->hold, pageq);
vm_page_queues_spin_unlock(PQ_HOLD + q);
mark->stat.flags = PG_FICTITIOUS | PG_MARKER;
mark->stat.busy_count = PBUSY_LOCKED;
mark->stat.queue = PQ_ACTIVE + q;
mark->stat.pc = PQ_ACTIVE + q;
mark->stat.wire_count = 1;
vm_page_queues_spin_lock(PQ_ACTIVE + q);
TAILQ_INSERT_HEAD(&vm_page_queues[PQ_ACTIVE + q].pl,
&mark->stat, pageq);
vm_page_queues_spin_unlock(PQ_ACTIVE + q);
mark->pact.flags = PG_FICTITIOUS | PG_MARKER;
mark->pact.busy_count = PBUSY_LOCKED;
mark->pact.queue = PQ_ACTIVE + q;
mark->pact.pc = PQ_ACTIVE + q;
mark->pact.wire_count = 1;
vm_page_queues_spin_lock(PQ_ACTIVE + q);
TAILQ_INSERT_HEAD(&vm_page_queues[PQ_ACTIVE + q].pl,
&mark->pact, pageq);
vm_page_queues_spin_unlock(PQ_ACTIVE + q);
}
isep = 0;
if (curthread == emergpager) {
isep = 1;
goto skip_setup;
}
if (vm_max_launder == 0)
vm_max_launder = physmem / 256 + 16;
vm_pageout_free_page_calc(vmstats.v_page_count);
vmstats.v_free_target = vmstats.v_free_min * 2;
vmstats.v_paging_wait = vmstats.v_free_min * 2;
vmstats.v_paging_start = vmstats.v_free_min * 3;
vmstats.v_paging_target1 = vmstats.v_free_min * 4;
vmstats.v_paging_target2 = vmstats.v_free_min * 5;
vmstats.v_inactive_target = vmstats.v_free_count / 16;
if (vm_page_max_wired == 0)
vm_page_max_wired = vmstats.v_free_count / 3;
if (vm_pageout_stats_scan == 0) {
vm_pageout_stats_scan = vmstats.v_free_count / PQ_L2_SIZE / 16;
if (vm_pageout_stats_scan < 16)
vm_pageout_stats_scan = 16;
}
if (vm_pageout_stats_ticks == 0)
vm_pageout_stats_ticks = hz / 10;
vm_pagedaemon_uptime = time_uptime;
swap_pager_swap_init();
atomic_swap_int(&sequence_emerg_pager, 1);
wakeup(&sequence_emerg_pager);
skip_setup:
if (isep) {
while (sequence_emerg_pager == 0)
tsleep(&sequence_emerg_pager, 0, "pstartup", hz);
}
pass = 0;
warn_time = time_uptime;
while (TRUE) {
int error;
long avail_shortage;
long inactive_shortage;
long vnodes_skipped = 0;
long recycle_count = 0;
long tmp;
if (pass > 0x7FFF0000)
pass = 0x70000000;
if (isep) {
if (vm_pages_needed)
tsleep(&vm_pagedaemon_uptime, 0, "psleep", hz);
else
tsleep(&vm_pagedaemon_uptime, 0, "psleep", hz*10);
if (vm_pages_needed == 0) {
pass = 0;
continue;
}
if ((int)(time_uptime - vm_pagedaemon_uptime) < 2) {
pass = 0;
continue;
}
} else {
vm_pageout_scan_hold(q3iterator & PQ_L2_MASK,
&markers[q3iterator & PQ_L2_MASK].hold);
vm_pageout_page_stats(q3iterator & PQ_L2_MASK,
&markers[q3iterator & PQ_L2_MASK].stat,
scounter);
++q3iterator;
if (vm_pages_needed == 0) {
error = tsleep(&vm_pages_needed,
0, "psleep",
vm_pageout_stats_ticks);
if (error &&
vm_paging_start(0) == 0 &&
vm_pages_needed == 0)
{
continue;
}
vm_pagedaemon_uptime = time_uptime;
vm_pages_needed = 1;
state = PAGING_TARGET1;
wakeup(&vm_pagedaemon_uptime);
}
}
mycpu->gd_cnt.v_pdwakeups++;
vmstats_rollup();
if (isep == 0)
vm_pagedaemon_uptime = time_uptime;
if (state == PAGING_TARGET1) {
avail_shortage = vm_paging_target1_count() +
vm_pageout_deficit;
} else {
avail_shortage = vm_paging_target2_count() +
vm_pageout_deficit;
}
vm_pageout_deficit = 0;
if (avail_shortage > 0) {
long delta = 0;
long counts[4] = { 0, 0, 0, 0 };
long use = avail_shortage;
int qq;
if (vm_pageout_debug) {
static time_t save_time3;
if (save_time3 != time_uptime) {
save_time3 = time_uptime;
kprintf("scan_inactive "
"pass %d isep=%d\n",
pass, isep);
}
}
if (state == PAGING_TARGET2 &&
use > vmstats.v_inactive_target / 10)
{
use = vmstats.v_inactive_target / 10 + 1;
}
qq = q1iterator;
for (q = 0; q < PQ_L2_SIZE; ++q) {
delta += vm_pageout_scan_inactive(
pass / MAXSCAN_DIVIDER,
qq & PQ_L2_MASK,
PQAVERAGE(use),
&vnodes_skipped, counts);
if (isep)
--qq;
else
++qq;
if (avail_shortage - delta <= 0)
break;
if (!vm_paging_target2() &&
!vm_paging_min_dnc(vm_page_free_hysteresis)) {
avail_shortage = 0;
break;
}
}
if (vm_pageout_debug) {
static time_t save_time2;
if (save_time2 != time_uptime) {
save_time2 = time_uptime;
kprintf("flsh %ld cln %ld "
"lru2 %ld react %ld "
"delta %ld\n",
counts[0], counts[1],
counts[2], counts[3],
delta);
}
}
avail_shortage -= delta;
q1iterator = qq;
}
vmstats_rollup();
if (isep == 0)
vm_pagedaemon_uptime = time_uptime;
inactive_shortage = vmstats.v_inactive_target -
vmstats.v_inactive_count;
if (avail_shortage > 0) {
tmp = avail_shortage * 2;
if (tmp > vmstats.v_inactive_target / 10)
tmp = vmstats.v_inactive_target / 10;
inactive_shortage += tmp;
}
if (isep == 0 && inactive_shortage > 0) {
pmap_collect();
}
if (isep && inactive_shortage < vm_emerg_launder)
inactive_shortage = vm_emerg_launder;
if ( inactive_shortage > 0) {
long delta = 0;
int qq;
qq = q2iterator;
for (q = 0; q < PQ_L2_SIZE; ++q) {
delta += vm_pageout_scan_active(
pass / MAXSCAN_DIVIDER,
qq & PQ_L2_MASK,
PQAVERAGE(avail_shortage),
PQAVERAGE(inactive_shortage),
&markers[qq & PQ_L2_MASK].pact,
&recycle_count);
if (isep)
--qq;
else
++qq;
if (inactive_shortage - delta <= 0 &&
avail_shortage - delta <= 0) {
break;
}
if (vmstats.v_inactive_count >
vmstats.v_inactive_target)
{
inactive_shortage = 0;
break;
}
}
inactive_shortage -= delta;
avail_shortage -= delta;
q2iterator = qq;
}
vmstats_rollup();
if (isep == 0)
vm_pagedaemon_uptime = time_uptime;
vm_pageout_scan_cache(avail_shortage, pass / MAXSCAN_DIVIDER,
vnodes_skipped, recycle_count);
if (avail_shortage > 0) {
if (pass == 0)
warn_time = time_uptime;
++pass;
if (isep == 0 && time_uptime - warn_time >= 60) {
kprintf("pagedaemon: WARNING! Continuous "
"paging for %ld minutes\n",
(time_uptime - warn_time ) / 60);
warn_time = time_uptime;
}
if (vm_pages_needed) {
if (swap_pager_full) {
tsleep(&vm_pages_needed, 0, "pdelay",
hz / 5);
}
}
} else {
pass = 0;
if (vm_paging_start(0) ||
vm_paging_min_dnc(vm_page_free_hysteresis))
{
state = PAGING_TARGET1;
vm_pages_needed = 2;
if (!vm_paging_wait())
wakeup(&vmstats.v_free_count);
} else if (vm_pages_needed) {
if (vm_paging_target1()) {
state = PAGING_TARGET1;
vm_pages_needed = 2;
} else if (vm_paging_target2()) {
state = PAGING_TARGET2;
vm_pages_needed = 2;
} else {
vm_pages_needed = 0;
}
wakeup(&vmstats.v_free_count);
}
}
}
}
static struct kproc_desc pg1_kp = {
"pagedaemon",
vm_pageout_thread,
&pagethread
};
SYSINIT(pagedaemon, SI_SUB_KTHREAD_PAGE, SI_ORDER_FIRST, kproc_start, &pg1_kp);
static struct kproc_desc pg2_kp = {
"emergpager",
vm_pageout_thread,
&emergpager
};
SYSINIT(emergpager, SI_SUB_KTHREAD_PAGE, SI_ORDER_ANY, kproc_start, &pg2_kp);
void
pagedaemon_wakeup(void)
{
if (vm_paging_start(0) && curthread != pagethread) {
if (vm_pages_needed <= 1) {
vm_pages_needed = 1;
wakeup(&vm_pages_needed);
} else if (vm_paging_min()) {
++vm_pages_needed;
}
}
}
#if !defined(NO_SWAPPING)
static void
vm_req_vmdaemon(void)
{
static int lastrun = 0;
if ((ticks > (lastrun + hz)) || (ticks < lastrun)) {
wakeup(&vm_daemon_needed);
lastrun = ticks;
}
}
static int vm_daemon_callback(struct proc *p, void *data __unused);
static void
vm_daemon(void)
{
while (TRUE) {
tsleep(&vm_daemon_needed, 0, "psleep", 0);
allproc_scan(vm_daemon_callback, NULL, 0);
}
}
static int
vm_daemon_callback(struct proc *p, void *data __unused)
{
struct vmspace *vm;
vm_pindex_t limit, size;
lwkt_gettoken(&p->p_token);
if (p->p_flags & (P_SYSTEM | P_WEXIT)) {
lwkt_reltoken(&p->p_token);
return (0);
}
if (p->p_stat != SACTIVE && p->p_stat != SSTOP && p->p_stat != SCORE) {
lwkt_reltoken(&p->p_token);
return (0);
}
limit = OFF_TO_IDX(qmin(p->p_rlimit[RLIMIT_RSS].rlim_cur,
p->p_rlimit[RLIMIT_RSS].rlim_max));
vm = p->p_vmspace;
vmspace_hold(vm);
size = pmap_resident_tlnw_count(&vm->vm_pmap);
if (limit >= 0 && size > 4096 &&
size - 4096 >= limit && vm_pageout_memuse_mode >= 1) {
vm_pageout_map_deactivate_pages(&vm->vm_map, limit);
}
vmspace_drop(vm);
lwkt_reltoken(&p->p_token);
return (0);
}
#endif