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