After discussing with a few people about a new "timed task" API I came to the conclusion that mixing timeouts and tasks will result in:
- always including a 'struct timeout' in a 'struct task', or the other the way around or - introducing a new data structure, hence API. Since I'd like to keep the change as small as possible when converting existing timeout_set(9), neither option seem a good fit. So I decided to add a new kernel thread, curiously named "softclock", that will offer his stack to the poor timeout handlers that need one. With this approach, converting a timeout is just a matter of doing: s/timeout_set/timeout_set_proc/ Diff below includes the conversions I need for the "netlock". I'm waiting for feedbacks and a better name to document the new function. Comments? Index: net/if_pflow.c =================================================================== RCS file: /cvs/src/sys/net/if_pflow.c,v retrieving revision 1.61 diff -u -p -r1.61 if_pflow.c --- net/if_pflow.c 29 Apr 2016 08:55:03 -0000 1.61 +++ net/if_pflow.c 15 Sep 2016 14:19:10 -0000 @@ -548,15 +548,16 @@ pflow_init_timeouts(struct pflow_softc * if (timeout_initialized(&sc->sc_tmo_tmpl)) timeout_del(&sc->sc_tmo_tmpl); if (!timeout_initialized(&sc->sc_tmo)) - timeout_set(&sc->sc_tmo, pflow_timeout, sc); + timeout_set_proc(&sc->sc_tmo, pflow_timeout, sc); break; case PFLOW_PROTO_10: if (!timeout_initialized(&sc->sc_tmo_tmpl)) - timeout_set(&sc->sc_tmo_tmpl, pflow_timeout_tmpl, sc); + timeout_set_proc(&sc->sc_tmo_tmpl, pflow_timeout_tmpl, + sc); if (!timeout_initialized(&sc->sc_tmo)) - timeout_set(&sc->sc_tmo, pflow_timeout, sc); + timeout_set_proc(&sc->sc_tmo, pflow_timeout, sc); if (!timeout_initialized(&sc->sc_tmo6)) - timeout_set(&sc->sc_tmo6, pflow_timeout6, sc); + timeout_set_proc(&sc->sc_tmo6, pflow_timeout6, sc); timeout_add_sec(&sc->sc_tmo_tmpl, PFLOW_TMPL_TIMEOUT); break; Index: net/if_pfsync.c =================================================================== RCS file: /cvs/src/sys/net/if_pfsync.c,v retrieving revision 1.231 diff -u -p -r1.231 if_pfsync.c --- net/if_pfsync.c 15 Sep 2016 02:00:18 -0000 1.231 +++ net/if_pfsync.c 15 Sep 2016 14:19:10 -0000 @@ -328,9 +328,9 @@ pfsync_clone_create(struct if_clone *ifc IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); ifp->if_hdrlen = sizeof(struct pfsync_header); ifp->if_mtu = ETHERMTU; - timeout_set(&sc->sc_tmo, pfsync_timeout, sc); - timeout_set(&sc->sc_bulk_tmo, pfsync_bulk_update, sc); - timeout_set(&sc->sc_bulkfail_tmo, pfsync_bulk_fail, sc); + timeout_set_proc(&sc->sc_tmo, pfsync_timeout, sc); + timeout_set_proc(&sc->sc_bulk_tmo, pfsync_bulk_update, sc); + timeout_set_proc(&sc->sc_bulkfail_tmo, pfsync_bulk_fail, sc); if_attach(ifp); if_alloc_sadl(ifp); @@ -1723,7 +1723,7 @@ pfsync_defer(struct pf_state *st, struct sc->sc_deferred++; TAILQ_INSERT_TAIL(&sc->sc_deferrals, pd, pd_entry); - timeout_set(&pd->pd_tmo, pfsync_defer_tmo, pd); + timeout_set_proc(&pd->pd_tmo, pfsync_defer_tmo, pd); timeout_add_msec(&pd->pd_tmo, 20); schednetisr(NETISR_PFSYNC); Index: netinet/ip_carp.c =================================================================== RCS file: /cvs/src/sys/netinet/ip_carp.c,v retrieving revision 1.293 diff -u -p -r1.293 ip_carp.c --- netinet/ip_carp.c 25 Jul 2016 16:44:04 -0000 1.293 +++ netinet/ip_carp.c 15 Sep 2016 14:19:11 -0000 @@ -831,9 +831,9 @@ carp_new_vhost(struct carp_softc *sc, in vhe->vhid = vhid; vhe->advskew = advskew; vhe->state = INIT; - timeout_set(&vhe->ad_tmo, carp_send_ad, vhe); - timeout_set(&vhe->md_tmo, carp_master_down, vhe); - timeout_set(&vhe->md6_tmo, carp_master_down, vhe); + timeout_set_proc(&vhe->ad_tmo, carp_send_ad, vhe); + timeout_set_proc(&vhe->md_tmo, carp_master_down, vhe); + timeout_set_proc(&vhe->md6_tmo, carp_master_down, vhe); KERNEL_ASSERT_LOCKED(); /* touching carp_vhosts */ Index: netinet/tcp_timer.h =================================================================== RCS file: /cvs/src/sys/netinet/tcp_timer.h,v retrieving revision 1.13 diff -u -p -r1.13 tcp_timer.h --- netinet/tcp_timer.h 6 Jul 2011 23:44:20 -0000 1.13 +++ netinet/tcp_timer.h 15 Sep 2016 14:19:11 -0000 @@ -116,7 +116,7 @@ const char *tcptimers[] = * Init, arm, disarm, and test TCP timers. */ #define TCP_TIMER_INIT(tp, timer) \ - timeout_set(&(tp)->t_timer[(timer)], tcp_timer_funcs[(timer)], tp) + timeout_set_proc(&(tp)->t_timer[(timer)], tcp_timer_funcs[(timer)], tp) #define TCP_TIMER_ARM(tp, timer, nticks) \ timeout_add(&(tp)->t_timer[(timer)], (nticks) * (hz / PR_SLOWHZ)) Index: netinet/tcp_var.h =================================================================== RCS file: /cvs/src/sys/netinet/tcp_var.h,v retrieving revision 1.115 diff -u -p -r1.115 tcp_var.h --- netinet/tcp_var.h 20 Jul 2016 19:57:53 -0000 1.115 +++ netinet/tcp_var.h 15 Sep 2016 14:19:11 -0000 @@ -217,7 +217,7 @@ extern int tcp_delack_ticks; void tcp_delack(void *); #define TCP_INIT_DELACK(tp) \ - timeout_set(&(tp)->t_delack_to, tcp_delack, tp) + timeout_set_proc(&(tp)->t_delack_to, tcp_delack, tp) #define TCP_RESTART_DELACK(tp) \ timeout_add(&(tp)->t_delack_to, tcp_delack_ticks) Index: kern/init_main.c =================================================================== RCS file: /cvs/src/sys/kern/init_main.c,v retrieving revision 1.257 diff -u -p -r1.257 init_main.c --- kern/init_main.c 4 Sep 2016 09:22:29 -0000 1.257 +++ kern/init_main.c 15 Sep 2016 14:19:10 -0000 @@ -143,6 +143,7 @@ void prof_init(void); void init_exec(void); void kqueue_init(void); void taskq_init(void); +void timeout_proc_init(void); void pool_gc_pages(void *); extern char sigcode[], esigcode[], sigcoderet[]; @@ -334,6 +335,9 @@ main(void *framep) sleep_queue_init(); sched_init_cpu(curcpu()); p->p_cpu->ci_randseed = (arc4random() & 0x7fffffff) + 1; + + /* Initialize timeouts in process context. */ + timeout_proc_init(); /* Initialize task queues */ taskq_init(); Index: kern/kern_timeout.c =================================================================== RCS file: /cvs/src/sys/kern/kern_timeout.c,v retrieving revision 1.48 diff -u -p -r1.48 kern_timeout.c --- kern/kern_timeout.c 6 Jul 2016 15:53:01 -0000 1.48 +++ kern/kern_timeout.c 15 Sep 2016 14:19:10 -0000 @@ -27,7 +27,7 @@ #include <sys/param.h> #include <sys/systm.h> -#include <sys/lock.h> +#include <sys/kthread.h> #include <sys/timeout.h> #include <sys/mutex.h> #include <sys/kernel.h> @@ -54,6 +54,7 @@ struct circq timeout_wheel[BUCKETS]; /* Queues of timeouts */ struct circq timeout_todo; /* Worklist */ +struct circq timeout_proc; /* Due timeouts needing proc. context */ #define MASKWHEEL(wheel, time) (((time) >> ((wheel)*WHEELBITS)) & WHEELMASK) @@ -127,6 +128,9 @@ struct mutex timeout_mutex = MUTEX_INITI #define CIRCQ_EMPTY(elem) (CIRCQ_FIRST(elem) == (elem)) +void softclock_thread(void *); +void softclock_create_thread(void *); + /* * Some of the "math" in here is a bit tricky. * @@ -147,11 +151,18 @@ timeout_startup(void) int b; CIRCQ_INIT(&timeout_todo); + CIRCQ_INIT(&timeout_proc); for (b = 0; b < nitems(timeout_wheel); b++) CIRCQ_INIT(&timeout_wheel[b]); } void +timeout_proc_init(void) +{ + kthread_create_deferred(softclock_create_thread, curcpu()); +} + +void timeout_set(struct timeout *new, void (*fn)(void *), void *arg) { new->to_func = fn; @@ -159,6 +170,12 @@ timeout_set(struct timeout *new, void (* new->to_flags = TIMEOUT_INITIALIZED; } +void +timeout_set_proc(struct timeout *new, void (*fn)(void *), void *arg) +{ + timeout_set(new, fn, arg); + new->to_flags |= TIMEOUT_NEEDPROCCTX; +} int timeout_add(struct timeout *new, int to_ticks) @@ -334,38 +351,84 @@ timeout_hardclock_update(void) } void +timeout_run(struct timeout *to) +{ + void (*fn)(void *); + void *arg; + + MUTEX_ASSERT_LOCKED(&timeout_mutex); + + to->to_flags &= ~TIMEOUT_ONQUEUE; + to->to_flags |= TIMEOUT_TRIGGERED; + + fn = to->to_func; + arg = to->to_arg; + + mtx_leave(&timeout_mutex); + fn(arg); + mtx_enter(&timeout_mutex); +} + +void softclock(void *arg) { int delta; struct circq *bucket; struct timeout *to; - void (*fn)(void *); mtx_enter(&timeout_mutex); while (!CIRCQ_EMPTY(&timeout_todo)) { to = timeout_from_circq(CIRCQ_FIRST(&timeout_todo)); CIRCQ_REMOVE(&to->to_list); - /* If due run it, otherwise insert it into the right bucket. */ + /* + * If due run it or defer execution to the thread, + * otherwise insert it into the right bucket. + */ delta = to->to_time - ticks; if (delta > 0) { bucket = &BUCKET(delta, to->to_time); CIRCQ_INSERT(&to->to_list, bucket); + } else if (to->to_flags & TIMEOUT_NEEDPROCCTX) { + CIRCQ_INSERT(&to->to_list, &timeout_proc); + wakeup(&timeout_proc); } else { #ifdef DEBUG if (delta < 0) printf("timeout delayed %d\n", delta); #endif - to->to_flags &= ~TIMEOUT_ONQUEUE; - to->to_flags |= TIMEOUT_TRIGGERED; + timeout_run(to); + } + } + mtx_leave(&timeout_mutex); +} - fn = to->to_func; - arg = to->to_arg; +void +softclock_create_thread(void *xci) +{ + if (kthread_create(softclock_thread, xci, NULL, "softclock")) + panic("fork softclock"); +} - mtx_leave(&timeout_mutex); - fn(arg); - mtx_enter(&timeout_mutex); - } +void +softclock_thread(void *xci) +{ + struct cpu_info *ci = xci; + struct timeout *to; + + KERNEL_ASSERT_LOCKED(); + + /* Be conservative for the moment. */ + sched_peg_curproc(ci); + + mtx_enter(&timeout_mutex); + for (;;) { + while (CIRCQ_EMPTY(&timeout_proc)) + msleep(&timeout_proc, &timeout_mutex, PSWP, "bored", 0); + + to = timeout_from_circq(CIRCQ_FIRST(&timeout_proc)); + CIRCQ_REMOVE(&to->to_list); + timeout_run(to); } mtx_leave(&timeout_mutex); } Index: sys/timeout.h =================================================================== RCS file: /cvs/src/sys/sys/timeout.h,v retrieving revision 1.25 diff -u -p -r1.25 timeout.h --- sys/timeout.h 22 Dec 2014 04:43:38 -0000 1.25 +++ sys/timeout.h 15 Sep 2016 14:19:11 -0000 @@ -67,6 +67,7 @@ struct timeout { /* * flags in the to_flags field. */ +#define TIMEOUT_NEEDPROCCTX 1 /* timeout needs a process context */ #define TIMEOUT_ONQUEUE 2 /* timeout is on the todo queue */ #define TIMEOUT_INITIALIZED 4 /* timeout is initialized */ #define TIMEOUT_TRIGGERED 8 /* timeout is running or ran */ @@ -88,6 +89,7 @@ struct timeout { struct bintime; void timeout_set(struct timeout *, void (*)(void *), void *); +void timeout_set_proc(struct timeout *, void (*)(void *), void *); int timeout_add(struct timeout *, int); int timeout_add_tv(struct timeout *, const struct timeval *); int timeout_add_ts(struct timeout *, const struct timespec *);