From 170a442702fc92692138f7d1d3e913a7e67dc134 Mon Sep 17 00:00:00 2001 From: Martin Pieuchot Date: Thu, 22 Sep 2016 12:55:25 +0000 Subject: Introduce a new 'softclock' thread that will be used to execute timeout callbacks needing a process context. The function timeout_set_proc(9) has to be used instead of timeout_set(9) when a timeout callback needs a process context. Note that if such a timeout is waiting, understand sleeping, for a non negligible amount of time it might delay other timeouts needing a process context. dlg@ agrees with this as a temporary solution. Manpage tweaks from jmc@ ok kettenis@, bluhm@, mikeb@ --- sys/kern/init_main.c | 6 +++- sys/kern/kern_timeout.c | 93 ++++++++++++++++++++++++++++++++++++++++++------- sys/sys/timeout.h | 4 ++- 3 files changed, 89 insertions(+), 14 deletions(-) (limited to 'sys') diff --git a/sys/kern/init_main.c b/sys/kern/init_main.c index f64a945a97c..f781069a92e 100644 --- a/sys/kern/init_main.c +++ b/sys/kern/init_main.c @@ -1,4 +1,4 @@ -/* $OpenBSD: init_main.c,v 1.258 2016/09/18 12:36:28 jasper Exp $ */ +/* $OpenBSD: init_main.c,v 1.259 2016/09/22 12:55:24 mpi Exp $ */ /* $NetBSD: init_main.c,v 1.84.4.1 1996/06/02 09:08:06 mrg Exp $ */ /* @@ -144,6 +144,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[]; @@ -336,6 +337,9 @@ main(void *framep) 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(); diff --git a/sys/kern/kern_timeout.c b/sys/kern/kern_timeout.c index b4ec8cdb4b2..6c6477e00a9 100644 --- a/sys/kern/kern_timeout.c +++ b/sys/kern/kern_timeout.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kern_timeout.c,v 1.48 2016/07/06 15:53:01 tedu Exp $ */ +/* $OpenBSD: kern_timeout.c,v 1.49 2016/09/22 12:55:24 mpi Exp $ */ /* * Copyright (c) 2001 Thomas Nordin * Copyright (c) 2000-2001 Artur Grabowski @@ -27,7 +27,7 @@ #include #include -#include +#include #include #include #include @@ -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_INITIALIZER(IPL_HIGH); #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,10 +151,17 @@ 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, NULL); +} + void timeout_set(struct timeout *new, void (*fn)(void *), void *arg) { @@ -159,6 +170,12 @@ timeout_set(struct timeout *new, void (*fn)(void *), void *arg) 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) @@ -333,39 +350,91 @@ timeout_hardclock_update(void) return (ret); } +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 *arg) +{ + if (kthread_create(softclock_thread, NULL, NULL, "softclock")) + panic("fork softclock"); +} - mtx_leave(&timeout_mutex); - fn(arg); - mtx_enter(&timeout_mutex); - } +void +softclock_thread(void *arg) +{ + CPU_INFO_ITERATOR cii; + struct cpu_info *ci; + struct timeout *to; + + KERNEL_ASSERT_LOCKED(); + + /* Be conservative for the moment */ + CPU_INFO_FOREACH(cii, ci) { + if (CPU_IS_PRIMARY(ci)) + break; + } + KASSERT(ci != NULL); + 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); } diff --git a/sys/sys/timeout.h b/sys/sys/timeout.h index 8f25af17e81..a00c62058a1 100644 --- a/sys/sys/timeout.h +++ b/sys/sys/timeout.h @@ -1,4 +1,4 @@ -/* $OpenBSD: timeout.h,v 1.25 2014/12/22 04:43:38 dlg Exp $ */ +/* $OpenBSD: timeout.h,v 1.26 2016/09/22 12:55:24 mpi Exp $ */ /* * Copyright (c) 2000-2001 Artur Grabowski * All rights reserved. @@ -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 *); -- cgit v1.2.3