diff --git a/sys/sys/vmmeter.h b/sys/sys/vmmeter.h index d07eb27..94c24ba 100644 --- a/sys/sys/vmmeter.h +++ b/sys/sys/vmmeter.h @@ -194,9 +194,28 @@ vm_laundry_target(void) { return (vm_cnt.v_inactive_target - vm_cnt.v_inactive_count + + vm_paging_needed()); +} + +/* + * Return TRUE if we are in shortfall and must begin laundering dirty memory. + */ +static inline int +vm_laundering_needed(void) +{ + + return (vm_cnt.v_inactive_count < vm_cnt.v_inactive_target && vm_paging_target()); } +/* + * Obtain the value of global or a per-CPU counter. + */ +#define VM_METER_PCPU_CNT(member) \ + vm_meter_cnt(__offsetof(struct vmmeter, member), true) + +u_int vm_meter_cnt(int, bool); + #endif /* systemwide totals computed every five seconds */ diff --git a/sys/vm/vm_meter.c b/sys/vm/vm_meter.c index bb462d3..12730fb 100644 --- a/sys/vm/vm_meter.c +++ b/sys/vm/vm_meter.c @@ -224,8 +224,8 @@ vmtotal(SYSCTL_HANDLER_ARGS) } /* - * vcnt() - accumulate statistics from all cpus and the global cnt - * structure. + * vm_meter_cnt() - accumulate statistics from the global cnt structure and + * optionally all CPUs. * * The vmmeter structure is now per-cpu as well as global. Those * statistics which can be kept on a per-cpu basis (to avoid cache @@ -233,19 +233,32 @@ vmtotal(SYSCTL_HANDLER_ARGS) * statistics, such as v_free_reserved, are left in the global * structure. * - * (sysctl_oid *oidp, void *arg1, int arg2, struct sysctl_req *req) + * "offset" is the offset of the statistic in struct vmmeter. "pcpu" + * determines whether we will count per-CPU counters. */ -static int -vcnt(SYSCTL_HANDLER_ARGS) +u_int +vm_meter_cnt(int offset, bool pcpu) { - int count = *(int *)arg1; - int offset = (char *)arg1 - (char *)&vm_cnt; + struct pcpu *pcpup; + u_int count; int i; - CPU_FOREACH(i) { - struct pcpu *pcpu = pcpu_find(i); - count += *(int *)((char *)&pcpu->pc_cnt + offset); + count = *(u_int *)((char *)&vm_cnt + offset); + if (pcpu) { + CPU_FOREACH(i) { + pcpup = pcpu_find(i); + count += *(u_int *)((char *)&pcpup->pc_cnt + offset); + } } + return (count); +} + +static int +cnt_sysctl(SYSCTL_HANDLER_ARGS) +{ + u_int count; + + count = vm_meter_cnt((char *)arg1 - (char *)&vm_cnt, true); return (SYSCTL_OUT(req, &count, sizeof(int))); } @@ -261,8 +274,8 @@ SYSCTL_NODE(_vm_stats, OID_AUTO, misc, CTLFLAG_RW, 0, "VM meter misc stats"); #define VM_STATS(parent, var, descr) \ SYSCTL_PROC(parent, OID_AUTO, var, \ - CTLTYPE_UINT | CTLFLAG_RD | CTLFLAG_MPSAFE, &vm_cnt.var, 0, vcnt, \ - "IU", descr) + CTLTYPE_UINT | CTLFLAG_RD | CTLFLAG_MPSAFE, &vm_cnt.var, 0, \ + cnt_sysctl, "IU", descr) #define VM_STATS_VM(var, descr) VM_STATS(_vm_stats_vm, var, descr) #define VM_STATS_SYS(var, descr) VM_STATS(_vm_stats_sys, var, descr) diff --git a/sys/vm/vm_pageout.c b/sys/vm/vm_pageout.c index 6156b6d..1ce37fa 100644 --- a/sys/vm/vm_pageout.c +++ b/sys/vm/vm_pageout.c @@ -156,6 +156,7 @@ SYSINIT(vmdaemon, SI_SUB_KTHREAD_VM, SI_ORDER_FIRST, kproc_start, &vm_kp); #endif /* Sleep intervals for pagedaemon threads, in subdivisions of one second. */ +/* XXX these names are wrong */ #define VM_LAUNDER_INTERVAL 10 #define VM_INACT_SCAN_INTERVAL 2 @@ -232,21 +233,16 @@ SYSCTL_INT(_vm, OID_AUTO, act_scan_laundry_weight, CTLFLAG_RW, &act_scan_laundry_weight, 0, "weight given to clean vs. dirty pages in active queue scans"); -static u_int bkgrd_launder_ratio = 100; +static u_int bkgrd_launder_ratio = 50; SYSCTL_UINT(_vm, OID_AUTO, bkgrd_launder_ratio, CTLFLAG_RW, &bkgrd_launder_ratio, 0, - "ratio of inactive to laundry pages to trigger background laundering"); + "ratio of inact/laundry pages needed to trigger background laundering"); -static u_int bkgrd_launder_max = 32768; +static u_int bkgrd_launder_max = 4096; SYSCTL_UINT(_vm, OID_AUTO, bkgrd_launder_max, CTLFLAG_RW, &bkgrd_launder_max, 0, "maximum background laundering rate, in pages per second"); -static u_int bkgrd_launder_thresh; -SYSCTL_UINT(_vm, OID_AUTO, bkgrd_launder_thresh, - CTLFLAG_RW, &bkgrd_launder_thresh, 0, - "free page threshold below which background laundering may be started"); - #define VM_PAGEOUT_PAGE_COUNT 16 int vm_pageout_page_count = VM_PAGEOUT_PAGE_COUNT; @@ -1110,8 +1106,8 @@ vm_pageout_laundry_worker(void *arg) { struct vm_domain *domain; uint64_t ninact, nlaundry; - int cycle, tcycle, domidx, gen, launder, laundered; - int shortfall, prev_shortfall, target; + int cycle, tcycle, domidx, gen, launder; + int shortfall, prev_shortfall, target, wakeups; domidx = (uintptr_t)arg; domain = &vm_dom[domidx]; @@ -1123,10 +1119,6 @@ vm_pageout_laundry_worker(void *arg) shortfall = prev_shortfall = 0; target = 0; - if (bkgrd_launder_thresh == 0) - bkgrd_launder_thresh = max(vm_cnt.v_free_target / 2, - 3 * vm_pageout_wakeup_thresh / 2); - /* * Calls to these handlers are serialized by the swapconf lock. */ @@ -1139,34 +1131,35 @@ vm_pageout_laundry_worker(void *arg) * The pageout laundry worker is never done, so loop forever. */ for (;;) { - KASSERT(target >= 0, ("negative target %d", target)); launder = 0; - /* - * First determine whether we're in shortfall. If so, there's - * an impending need for clean pages. We attempt to launder the - * target within one pagedaemon sleep period. - */ shortfall = vm_laundry_target() + vm_pageout_deficit; - if (shortfall > 0) { + if (vm_laundering_needed()) { /* - * If the shortfall has grown since the last cycle or - * we're still in shortfall despite a previous - * laundering run, start a new run. + * If we're in shortfall and we haven't yet started a + * laundering cycle to get us out of it, begin a run. */ - if (shortfall > prev_shortfall || cycle == tcycle) { + if (prev_shortfall == 0) { target = shortfall; cycle = 0; tcycle = VM_LAUNDER_RATE; } - prev_shortfall = shortfall; - launder = target / (tcycle - (cycle % tcycle)); - goto launder; - } else { - if (prev_shortfall > 0) - /* We're out of shortfall; the target is met. */ - target = 0; - shortfall = prev_shortfall = 0; + prev_shortfall = min(shortfall, 0); + } + if (prev_shortfall > 0) { + /* + * We entered shortfall at some point in the past. If + * we have reached our target or the run is finished and + * we're not currently in shortfall, we have no + * immediate need launder pages. Otherwise keep + * laundering. + */ + if (shortfall <= 0 || cycle == tcycle) { + shortfall = prev_shortfall = target = 0; + } else { + launder = target / (tcycle - (cycle % tcycle)); + goto dolaundry; + } } /* @@ -1183,38 +1176,35 @@ vm_pageout_laundry_worker(void *arg) * pagedaemon has been woken up at least once since the previous * run. */ - if (target > 0 && cycle != tcycle) { + if (target > 0 && cycle < tcycle) { /* Continue an ongoing background run. */ launder = target / (tcycle - (cycle % tcycle)); - goto launder; + goto dolaundry; } ninact = vm_cnt.v_inactive_count; nlaundry = vm_cnt.v_laundry_count; + wakeups = vm_cnt.v_pdwakeups; if (ninact > 0 && - vm_cnt.v_pdwakeups != gen && - vm_cnt.v_free_count < bkgrd_launder_thresh && + wakeups != gen && nlaundry * bkgrd_launder_ratio >= ninact) { + gen = wakeups; + cycle = 0; tcycle = VM_LAUNDER_INTERVAL; - gen = vm_cnt.v_pdwakeups; if (nlaundry >= ninact) target = vm_cnt.v_free_target; else target = (nlaundry * vm_cnt.v_free_target << 16) / ninact >> 16; - target /= 2; - if (target > bkgrd_launder_max) - tcycle = target * VM_LAUNDER_INTERVAL / - bkgrd_launder_max; + target /= 10; + target = min(target, bkgrd_launder_max); launder = target / (tcycle - (cycle % tcycle)); } -launder: - if (launder > 0) { - laundered = vm_pageout_launder(domain, launder); - target -= min(laundered, target); - } +dolaundry: + if (launder > 0) + target -= vm_pageout_launder(domain, launder); tsleep(&vm_cnt.v_laundry_count, PVM, "laundr", hz / VM_LAUNDER_INTERVAL);