this introduces a global gc task that loops over all the pools looking for pages that havent been used for a very long time so they can be freed.
this is the simplest way of doing this without introducing per pool timeouts/tasks which in turn could introduce races with pool_destroy, or more shared global state that gets touched on pool_get/put ops. im adding the timeout in src/sys/kern/init_main.c cos having pool_init do it causes faults when a pool is created before the timeout subsystem is set up. i have tested this on sparc64, amd64, and hppa, and it seems to do the advertised job. Index: subr_pool.c =================================================================== RCS file: /cvs/src/sys/kern/subr_pool.c,v retrieving revision 1.173 diff -u -p -r1.173 subr_pool.c --- subr_pool.c 22 Dec 2014 00:33:40 -0000 1.173 +++ subr_pool.c 22 Dec 2014 04:40:29 -0000 @@ -40,6 +40,8 @@ #include <sys/syslog.h> #include <sys/rwlock.h> #include <sys/sysctl.h> +#include <sys/task.h> +#include <sys/timeout.h> #include <uvm/uvm_extern.h> @@ -160,6 +162,12 @@ void pool_print1(struct pool *, const c #define pool_sleep(pl) msleep(pl, &pl->pr_mtx, PSWP, pl->pr_wchan, 0) +/* stale page garbage collectors */ +void pool_gc_sched(void *); +struct timeout pool_gc_tick = TIMEOUT_INITIALIZER(pool_gc_sched, NULL); +void pool_gc_pages(void *, void *); +struct task pool_gc_task = TASK_INITIALIZER(pool_gc_pages, NULL, NULL); + static inline int phtree_compare(struct pool_item_header *a, struct pool_item_header *b) { @@ -1337,6 +1345,46 @@ done: rw_exit_read(&pool_lock); return (rv); +} + +void +pool_gc_sched(void *x) +{ + task_add(systq, &pool_gc_task); +} + +void +pool_gc_pages(void *x, void *y) +{ + extern int ticks; + struct pool *pp; + struct pool_item_header *ph, *freeph; + int s; + + rw_enter_read(&pool_lock); + s = splvm(); + SIMPLEQ_FOREACH(pp, &pool_head, pr_poollist) { + if (!mtx_enter_try(&pp->pr_mtx)) + continue; + + /* is it time to free a page? */ + if (pp->pr_nidle > pp->pr_minpages && + (ph = TAILQ_FIRST(&pp->pr_emptypages)) != NULL && + (ticks - ph->ph_tick) > (hz * 8)) { + freeph = ph; + pool_p_remove(pp, freeph); + } else + freeph = NULL; + + mtx_leave(&pp->pr_mtx); + + if (freeph != NULL) + pool_p_free(pp, freeph); + } + splx(s); /* XXX */ + rw_exit_read(&pool_lock); + + timeout_add_sec(&pool_gc_tick, 1); } /* Index: init_main.c =================================================================== RCS file: /cvs/src/sys/kern/init_main.c,v retrieving revision 1.229 diff -u -p -r1.229 init_main.c --- init_main.c 17 Dec 2014 06:58:11 -0000 1.229 +++ init_main.c 22 Dec 2014 04:40:29 -0000 @@ -123,6 +123,8 @@ extern struct timeout setperf_to; void setperf_auto(void *); #endif +extern struct timeout pool_gc_tick; + int cmask = CMASK; extern struct user *proc0paddr; @@ -556,6 +558,11 @@ main(void *framep) #ifndef SMALL_KERNEL timeout_set(&setperf_to, setperf_auto, NULL); #endif + + /* + * Start the idle pool page garbage collector + */ + timeout_add_sec(&pool_gc_tick, 1); /* * proc0: nothing to do, back to sleep