From 46311ac27fb45e8daecb0d2050645dbe5d8630a7 Mon Sep 17 00:00:00 2001 From: Matthew Dillon Date: Wed, 15 Mar 2006 07:58:37 +0000 Subject: [PATCH] Implement a VM load heuristic. sysctl vm.vm_load will return an indication of the load on the VM system in the range 0-1000. Implement a page allocation rate limit in vm_fault which is based on vm_load, and enabled via vm.vm_load_enable (default on). As the system becomes more and more memory bound, those processes whos page faults require a page allocation will start to allocate pages in smaller bursts and with greater and greater enforced delays, up to 1/10 of a second. Implement vm.vm_load_debug (for kernels with INVARIANTS), which outputs the burst calculations to the console when enabled. Increase the minimum guarenteed run time without swapping from 2 to 15 seconds. --- sys/vm/vm_extern.h | 3 ++- sys/vm/vm_fault.c | 55 ++++++++++++++++++++++++++++++++++++++++++++- sys/vm/vm_glue.c | 4 ++-- sys/vm/vm_map.h | 3 ++- sys/vm/vm_pageout.c | 29 +++++++++++++++++++++++- sys/vm/vm_pageout.h | 5 ++++- 6 files changed, 92 insertions(+), 7 deletions(-) diff --git a/sys/vm/vm_extern.h b/sys/vm/vm_extern.h index 786574054c..ed2f85edef 100644 --- a/sys/vm/vm_extern.h +++ b/sys/vm/vm_extern.h @@ -32,7 +32,7 @@ * * @(#)vm_extern.h 8.2 (Berkeley) 1/12/94 * $FreeBSD: src/sys/vm/vm_extern.h,v 1.46.2.3 2003/01/13 22:51:17 dillon Exp $ - * $DragonFly: src/sys/vm/vm_extern.h,v 1.14 2004/08/15 15:00:22 joerg Exp $ + * $DragonFly: src/sys/vm/vm_extern.h,v 1.15 2006/03/15 07:58:37 dillon Exp $ */ #ifndef _VM_EXTERN_H_ @@ -81,6 +81,7 @@ void vm_fault_copy_entry (vm_map_t, vm_map_t, vm_map_entry_t, vm_map_entry_t); void vm_fault_unwire (vm_map_t, vm_map_entry_t); int vm_fault_wire (vm_map_t, vm_map_entry_t, boolean_t); void vm_fork (struct proc *, struct proc *, int); +void vm_fault_ratecheck(void); void vm_waitproc (struct proc *); int vm_mmap (vm_map_t, vm_offset_t *, vm_size_t, vm_prot_t, vm_prot_t, int, void *, vm_ooffset_t); vm_offset_t vm_page_alloc_contig (vm_offset_t, vm_paddr_t, vm_paddr_t, vm_offset_t); diff --git a/sys/vm/vm_fault.c b/sys/vm/vm_fault.c index 840ae41c0c..9ec3c25b1b 100644 --- a/sys/vm/vm_fault.c +++ b/sys/vm/vm_fault.c @@ -67,7 +67,7 @@ * rights to redistribute these changes. * * $FreeBSD: src/sys/vm/vm_fault.c,v 1.108.2.8 2002/02/26 05:49:27 silby Exp $ - * $DragonFly: src/sys/vm/vm_fault.c,v 1.20 2005/11/14 18:50:15 dillon Exp $ + * $DragonFly: src/sys/vm/vm_fault.c,v 1.21 2006/03/15 07:58:37 dillon Exp $ */ /* @@ -76,6 +76,7 @@ #include #include +#include #include #include #include @@ -99,6 +100,7 @@ static int vm_fault_additional_pages (vm_page_t, int, int, vm_page_t *, int *); +static int vm_fault_ratelimit(struct vmspace *vmspace); #define VM_FAULT_READ_AHEAD 8 #define VM_FAULT_READ_BEHIND 7 @@ -195,6 +197,8 @@ vm_fault(vm_map_t map, vm_offset_t vaddr, vm_prot_t fault_type, int fault_flags) vm_page_t marray[VM_FAULT_READ]; int hardfault; int faultcount; + int limticks; + int didlimit = 0; struct faultstate fs; mycpu->gd_cnt.v_vm_faults++; @@ -378,6 +382,21 @@ RetryFault: return (KERN_PROTECTION_FAILURE); } + /* + * Ratelimit. + */ + if (didlimit == 0) { + limticks = + vm_fault_ratelimit(curproc->p_vmspace); + if (limticks) { + crit_exit(); + unlock_and_deallocate(&fs); + tsleep(curproc, 0, "vmrate", limticks); + didlimit = 1; + goto RetryFault; + } + } + /* * Allocate a new page for this object/offset pair. */ @@ -1026,6 +1045,40 @@ vm_fault_unwire(vm_map_t map, vm_map_entry_t entry) } } +/* + * Reduce the rate at which memory is allocated to a process based + * on the perceived load on the VM system. As the load increases + * the allocation burst rate goes down and the delay increases. + * + * Rate limiting does not apply when faulting active or inactive + * pages. When faulting 'cache' pages, rate limiting only applies + * if the system currently has a severe page deficit. + * + * XXX vm_pagesupply should be increased when a page is freed. + * + * We sleep up to 1/10 of a second. + */ +static int +vm_fault_ratelimit(struct vmspace *vmspace) +{ + if (vm_load_enable == 0) + return(0); + if (vmspace->vm_pagesupply > 0) { + --vmspace->vm_pagesupply; + return(0); + } +#ifdef INVARIANTS + if (vm_load_debug) { + printf("load %-4d give %d pgs, wait %d, pid %-5d (%s)\n", + vm_load, + (1000 - vm_load ) / 10, vm_load * hz / 10000, + curproc->p_pid, curproc->p_comm); + } +#endif + vmspace->vm_pagesupply = (1000 - vm_load) / 10; + return(vm_load * hz / 10000); +} + /* * Routine: * vm_fault_copy_entry diff --git a/sys/vm/vm_glue.c b/sys/vm/vm_glue.c index d7ab854004..5adb4b6df3 100644 --- a/sys/vm/vm_glue.c +++ b/sys/vm/vm_glue.c @@ -60,7 +60,7 @@ * rights to redistribute these changes. * * $FreeBSD: src/sys/vm/vm_glue.c,v 1.94.2.4 2003/01/13 22:51:17 dillon Exp $ - * $DragonFly: src/sys/vm/vm_glue.c,v 1.38 2005/11/22 08:41:06 dillon Exp $ + * $DragonFly: src/sys/vm/vm_glue.c,v 1.39 2006/03/15 07:58:37 dillon Exp $ */ #include "opt_vm.h" @@ -450,7 +450,7 @@ swapin_request(void) /* * Swap_idle_threshold1 is the guaranteed swapped in time for a process */ -static int swap_idle_threshold1 = 2; +static int swap_idle_threshold1 = 15; SYSCTL_INT(_vm, OID_AUTO, swap_idle_threshold1, CTLFLAG_RW, &swap_idle_threshold1, 0, ""); diff --git a/sys/vm/vm_map.h b/sys/vm/vm_map.h index 1df5c176aa..ea2e5104ce 100644 --- a/sys/vm/vm_map.h +++ b/sys/vm/vm_map.h @@ -62,7 +62,7 @@ * rights to redistribute these changes. * * $FreeBSD: src/sys/vm/vm_map.h,v 1.54.2.5 2003/01/13 22:51:17 dillon Exp $ - * $DragonFly: src/sys/vm/vm_map.h,v 1.17 2005/11/19 17:19:52 dillon Exp $ + * $DragonFly: src/sys/vm/vm_map.h,v 1.18 2006/03/15 07:58:37 dillon Exp $ */ /* @@ -231,6 +231,7 @@ struct vmspace { #define vm_endcopy vm_exitingcnt int vm_exitingcnt; /* several procsses zombied in exit1 */ int vm_upccount; /* number of registered upcalls */ + int vm_pagesupply; struct vmupcall *vm_upcalls; /* registered upcalls */ }; diff --git a/sys/vm/vm_pageout.c b/sys/vm/vm_pageout.c index 9c1e8adf4b..e0c864b0d3 100644 --- a/sys/vm/vm_pageout.c +++ b/sys/vm/vm_pageout.c @@ -66,7 +66,7 @@ * rights to redistribute these changes. * * $FreeBSD: src/sys/vm/vm_pageout.c,v 1.151.2.15 2002/12/29 18:21:04 dillon Exp $ - * $DragonFly: src/sys/vm/vm_pageout.c,v 1.17 2006/01/13 20:45:30 swildner Exp $ + * $DragonFly: src/sys/vm/vm_pageout.c,v 1.18 2006/03/15 07:58:37 dillon Exp $ */ /* @@ -195,6 +195,18 @@ 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_load; +SYSCTL_INT(_vm, OID_AUTO, vm_load, + CTLFLAG_RD, &vm_load, 0, "load on the VM system"); +int vm_load_enable = 1; +SYSCTL_INT(_vm, OID_AUTO, vm_load_enable, + CTLFLAG_RW, &vm_load_enable, 0, "enable vm_load rate limiting"); +#ifdef INVARIANTS +int vm_load_debug; +SYSCTL_INT(_vm, OID_AUTO, vm_load_debug, + CTLFLAG_RW, &vm_load_debug, 0, "debug vm_load"); +#endif + #define VM_PAGEOUT_PAGE_COUNT 16 int vm_pageout_page_count = VM_PAGEOUT_PAGE_COUNT; @@ -208,6 +220,21 @@ static void vm_req_vmdaemon (void); #endif static void vm_pageout_page_stats(void); +/* + * Update + */ +void +vm_fault_ratecheck(void) +{ + if (vm_pages_needed) { + if (vm_load < 1000) + ++vm_load; + } else { + if (vm_load > 0) + --vm_load; + } +} + /* * vm_pageout_clean: * diff --git a/sys/vm/vm_pageout.h b/sys/vm/vm_pageout.h index bc745dc501..0bd24810fa 100644 --- a/sys/vm/vm_pageout.h +++ b/sys/vm/vm_pageout.h @@ -62,7 +62,7 @@ * rights to redistribute these changes. * * $FreeBSD: src/sys/vm/vm_pageout.h,v 1.26.2.1 2002/02/26 05:49:28 silby Exp $ - * $DragonFly: src/sys/vm/vm_pageout.h,v 1.5 2004/05/20 22:42:25 dillon Exp $ + * $DragonFly: src/sys/vm/vm_pageout.h,v 1.6 2006/03/15 07:58:37 dillon Exp $ */ #ifndef _VM_VM_PAGEOUT_H_ @@ -80,6 +80,9 @@ extern int vm_page_max_wired; extern int vm_pages_needed; /* should be some "event" structure */ extern int vm_pageout_pages_needed; extern int vm_pageout_deficit; +extern int vm_load; +extern int vm_load_enable; +extern int vm_load_debug; #define VM_PAGEOUT_ASYNC 0 #define VM_PAGEOUT_SYNC 1 -- 2.41.0