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

Reply via email to