On Thu, Jun 24, 2021 at 06:51:07AM +0100, Jason McIntyre wrote: > On Wed, Jun 23, 2021 at 06:57:00PM -0500, Scott Cheloha wrote: > > Hi, > > > > I want to document kclock timeouts so others can use them. > > morning. reads fine, except one issue: > > [...] > > > +.Bl -tag -width kclock > > +.It Fa kclock > > +The timeout is scheduled against the given > > +.Fa kclock, > > you need a space between kclock and the comma
Huh, I'm a little surprised -Tlint doesn't flag that. Fixed. Are there any other mdoc(7)-type stylistic anachronisms we can fix up? Index: share/man/man9/timeout.9 =================================================================== RCS file: /cvs/src/share/man/man9/timeout.9,v retrieving revision 1.53 diff -u -p -r1.53 timeout.9 --- share/man/man9/timeout.9 11 May 2021 13:29:25 -0000 1.53 +++ share/man/man9/timeout.9 24 Jun 2021 22:37:38 -0000 @@ -44,7 +44,7 @@ .Nm timeout_triggered , .Nm TIMEOUT_INITIALIZER , .Nm TIMEOUT_INITIALIZER_FLAGS -.Nd execute a function after a specified period of time +.Nd execute a function in the future .Sh SYNOPSIS .In sys/types.h .In sys/timeout.h @@ -55,12 +55,13 @@ .Fa "struct timeout *to" .Fa "void (*fn)(void *)" .Fa "void *arg" +.Fa "int kclock" .Fa "int flags" .Fc .Ft void .Fn timeout_set_proc "struct timeout *to" "void (*fn)(void *)" "void *arg" .Ft int -.Fn timeout_add "struct timeout *to" "int ticks" +.Fn timeout_add "struct timeout *to" "int nticks" .Ft int .Fn timeout_del "struct timeout *to" .Ft int @@ -83,174 +84,218 @@ .Fn timeout_add_usec "struct timeout *to" "int usec" .Ft int .Fn timeout_add_nsec "struct timeout *to" "int nsec" -.Fn TIMEOUT_INITIALIZER "void (*fn)(void *)" "void *arg" -.Fn TIMEOUT_INITIALIZER_FLAGS "void (*fn)(void *)" "void *arg" "int flags" +.Ft int +.Fn timeout_in_nsec "struct timeout *to" "uint64_t nsecs" +.Ft int +.Fn timeout_at_ts "struct timeout *to" "const struct timespec *abs" +.Fo TIMEOUT_INITIALIZER +.Fa "void (*fn)(void *)" +.Fa "void *arg" +.Fc +.Fo TIMEOUT_INITIALIZER_FLAGS +.Fa "void (*fn)(void *)" +.Fa "void *arg" +.Fa "int kclock" +.Fa "int flags" +.Fc .Sh DESCRIPTION The .Nm timeout -API provides a mechanism to execute a function at a given time. -The granularity of the time is limited by the granularity of the -.Xr hardclock 9 -timer which executes -.Xr hz 9 -times a second. +API provides a mechanism to schedule a function for asynchronous +execution in the future. .Pp -It is the responsibility of the caller to provide these functions with -pre-allocated timeout structures. +All state is encapsulated in a caller-allocated timeout structure +.Pq hereafter, a Qo timeout Qc . +A timeout must be initialized before it may be used as input to other +functions in the API. .Pp The .Fn timeout_set -function prepares the timeout structure -.Fa to -to be used in future calls to -.Fn timeout_add -and -.Fn timeout_del . -The timeout will be prepared to call the function specified by the +function initializes the timeout +.Fa to . +When executed, +the timeout will call the function .Fa fn -argument with a -.Fa void * -argument given in the +with .Fa arg -argument. -Once initialized, the -.Fa to -structure can be used repeatedly in -.Fn timeout_add -and -.Fn timeout_del -and does not need to be reinitialized unless -the function called and/or its argument must change. +as its first parameter. +The timeout is implicitly scheduled against the +.Dv KCLOCK_NONE +clock and is not configured with any additional flags. .Pp The .Fn timeout_set_flags function is similar to -.Fn timeout_set -but it additionally accepts the bitwise OR of zero or more of the -following +.Fn timeout_set , +except that it takes two additional parameters: +.Bl -tag -width kclock +.It Fa kclock +The timeout is scheduled against the given +.Fa kclock , +which must be one of the following: +.Bl -tag -width KCLOCK_UPTIME +.It Dv KCLOCK_NONE +Low resolution tick-based clock. +The granularity of this clock is limited by the +.Xr hardclock 9 , +which executes roughly +.Xr hz 9 +times per second. +.It Dv KCLOCK_UPTIME +The uptime clock. +Counts the time elapsed since the system booted. +.El +.It Fa flags +The timeout's behavior may be configured with the bitwise OR of +zero or more of the following .Fa flags : -.Bl -tag -width TIMEOUT_PROC -offset indent +.Bl -tag -width TIMEOUT_PROC .It Dv TIMEOUT_PROC -Runs the timeout in a process context instead of the default +Execute the timeout in a process context instead of the default .Dv IPL_SOFTCLOCK interrupt context. .El +.El .Pp The .Fn timeout_set_proc -function is similar to +function is equivalent to +.Fn timeout_set , +except that the given timeout is configured with the +.Dv TIMEOUT_PROC +flag. +.Pp +A timeout may also be initialized statically. +The +.Fn TIMEOUT_INITIALIZER +macro is equivalent to the .Fn timeout_set -but it runs the timeout in a process context instead of the default -.Dv IPL_SOFTCLOCK -interrupt context. +function and the +.Fn TIMEOUT_INITIALIZER_FLAGS +macro is equivalent to the +.Fn timeout_set_flags +function. .Pp -The function -.Fn timeout_add -schedules the execution of the +The interfaces available for scheduling a timeout vary with the timeout's +.Fa kclock . +.Pp +.Dv KCLOCK_NONE +timeouts may be scheduled with the function +.Fn timeout_add , +which arms .Fa to -timeout in at least -.Fa ticks Ns /hz +for execution after +.Fa nticks +.Xr hardclock 9 +ticks have elapsed +.Pq see Xr hz 9 for details . +In practice, +.Fa nticks +ticks will usually elapse in slightly less than +.Fa nticks Ns /hz seconds. Negative values of -.Fa ticks +.Fa nticks are illegal. -If the value is -.Sq 0 -it will, in the current implementation, be treated as -.Sq 1 , -but in the future it might cause an immediate timeout. -The timeout in the +If +.Fa nticks +is zero it will be silently rounded up to one. +.Pp +For convenience, +.Dv KCLOCK_NONE +timeouts may also be scheduled with +.Fn timeout_add_sec , +.Fn timeout_add_msec , +.Fn timeout_add_usec , +.Fn timeout_add_nsec , +or +.Fn timeout_add_tv . +These wrapper functions convert their inputs to a count of ticks before +calling +.Fn timeout_add +to schedule the given timeout. +.Pp +.Dv KCLOCK_UPTIME +timeouts may be scheduled with +.Fn timeout_in_nsec , +which arms .Fa to -argument must be already initialized by -.Fn timeout_set , -.Fn timeout_set_flags , +to execute after at least +.Fa nsecs +nanoseconds have elapsed, +or with +.Fn timeout_at_ts , +which arms +.Fa to +to execute at or after the absolute time +.Fa abs +has elapsed on the system uptime clock. +.Pp +Once scheduled, +a timeout may not be reinitialized with +.Fn timeout_set or -.Fn timeout_set_proc -and may not be used in calls to -.Fn timeout_set , -.Fn timeout_set_flags , +.Fn timeout_set_flags +until it has executed or been cancelled with +.Fn timeout_del or -.Fn timeout_set_proc -until it has timed out or been removed with -.Fn timeout_del . -If the timeout in the -.Fa to -argument is already scheduled, the old execution time will be -replaced by the new one. +.Fn timeout_del_barrier . +.Pp +A pending timeout may be rescheduled without first cancelling it with +.Fn timeout_del +or +.Fn timeout_del_barrier . +The new expiration time will quietly supersede the original. .Pp The function .Fn timeout_del -will cancel the timeout in the argument +cancels any pending execution of .Fa to . -If the timeout has already executed or has never been added +If the timeout has already executed or was never scheduled the call will have no effect. .Pp +The .Fn timeout_del_barrier -is like -.Fn timeout_del -but it will wait until any current execution of the timeout has completed. +function is similar to +.Fn timeout_del , +except that it may block until any current execution of the timeout +.Fa to +has completed. .Pp +The .Fn timeout_barrier -ensures that any current execution of the timeout in the argument +function blocks until any current execution of the timeout .Fa to -has completed before returning. +has completed. .Pp The .Fn timeout_pending -macro can be used to check if a timeout is scheduled to run. +macro indicates whether the given timeout is scheduled for execution. +A timeout's pending status is cleared when it is executed or cancelled. .Pp The .Fn timeout_initialized -macro can be used to check if a timeout has been initialized. +macro indicates whether the given timeout has been initialized with +.Fn timeout_set +or +.Fn timeout_set_flags . +This macro must not be used unless the memory underlying +.Fa to +has been zeroed. .Pp The .Fn timeout_triggered -macro can be used to check if a timeout is running or has been run. -The -.Fn timeout_add -and -.Fn timeout_del -functions clear the triggered state for that timeout. -.Pp -When possible, use the -.Fn timeout_add_tv , -.Fn timeout_add_sec , -.Fn timeout_add_msec , -.Fn timeout_add_usec , -and -.Fn timeout_add_nsec -functions instead of -.Fn timeout_add . -Those functions add a timeout whilst converting the time specified -by the respective types. -They also defer the timeout handler for at least one tick if called -with a positive value. -.Pp -A timeout declaration can be initialised with the -.Fn TIMEOUT_INITIALIZER -macro. -The timeout will be prepared to call the function specified by the -.Fa fn -argument with the -.Fa void * -argument given in -.Fa arg . -.Pp -The -.Fn TIMEOUT_INITIALIZER_FLAGS -macro is similar to -.Fn TIMEOUT_INITIALIZER , -but it accepts additional flags. -See the -.Fn timeout_set_flags -function for details. +macro indicates whether the given timeout is executing or has finished +executing. +Rescheduling or cancelling a timeout clears its triggered status. .Sh CONTEXT .Fn timeout_set , .Fn timeout_set_flags , and .Fn timeout_set_proc -can be called during autoconf, from process context, or from interrupt -context. +can be called during autoconf, +from process context, +or from interrupt context. .Pp .Fn timeout_add , .Fn timeout_add_sec , @@ -258,54 +303,53 @@ context. .Fn timeout_add_nsec , .Fn timeout_add_usec , .Fn timeout_add_tv , +.Fn timeout_in_nsec , +.Fn timeout_at_ts , .Fn timeout_del , .Fn timeout_pending , .Fn timeout_initialized , +and .Fn timeout_triggered -can be called during autoconf, from process context, or from any -interrupt context at or below +can be called during autoconf, +from process context, +or from any interrupt context at or below .Dv IPL_CLOCK . .Pp +The .Fn timeout_barrier and .Fn timeout_del_barrier -can be called from process context. +functions may only be called from a process context. .Pp -When the timeout runs, the +When a timeout is executed, +the function .Fa fn -argument to -.Fn timeout_set -or -.Fn timeout_set_flags -will be called in an interrupt context at +given at initialization will be called from the .Dv IPL_SOFTCLOCK -or a process context if the +interrupt context, +or a process context if the timeout was configured with the .Dv TIMEOUT_PROC -flag was given at initialization. -The -.Fa fn -argument to -.Fn timeout_set_proc -will be called in a process context. +flag. .Sh RETURN VALUES .Fn timeout_add , .Fn timeout_add_sec , .Fn timeout_add_msec , .Fn timeout_add_nsec , .Fn timeout_add_usec , -and .Fn timeout_add_tv +.Fn timeout_in_nsec , +and +.Fn timeout_at_ts will return 1 if the timeout .Fa to -was added to the timeout schedule or 0 if it was already queued. +was newly scheduled or 0 if the timeout was already pending. .Pp .Fn timeout_del and .Fn timeout_del_barrier will return 1 if the timeout .Fa to -was removed from the pending timeout schedule or 0 if it was not -currently queued. +was pending or 0 otherwise. .Sh CODE REFERENCES These functions are implemented in the file .Pa sys/kern/kern_timeout.c . Index: sys/kern/kern_timeout.c =================================================================== RCS file: /cvs/src/sys/kern/kern_timeout.c,v retrieving revision 1.85 diff -u -p -r1.85 kern_timeout.c --- sys/kern/kern_timeout.c 19 Jun 2021 02:05:33 -0000 1.85 +++ sys/kern/kern_timeout.c 24 Jun 2021 22:37:39 -0000 @@ -252,7 +252,7 @@ timeout_proc_init(void) kthread_create_deferred(softclock_create_thread, NULL); } -static inline void +void _timeout_set(struct timeout *to, void (*fn)(void *), void *arg, int kclock, int flags) { @@ -269,9 +269,10 @@ timeout_set(struct timeout *new, void (* } void -timeout_set_flags(struct timeout *to, void (*fn)(void *), void *arg, int flags) +timeout_set_flags(struct timeout *to, void (*fn)(void *), void *arg, int kclock, + int flags) { - _timeout_set(to, fn, arg, KCLOCK_NONE, flags); + _timeout_set(to, fn, arg, kclock, flags); } void @@ -280,13 +281,6 @@ timeout_set_proc(struct timeout *new, vo _timeout_set(new, fn, arg, KCLOCK_NONE, TIMEOUT_PROC); } -void -timeout_set_kclock(struct timeout *to, void (*fn)(void *), void *arg, - int kclock, int flags) -{ - _timeout_set(to, fn, arg, kclock, flags | TIMEOUT_KCLOCK); -} - int timeout_add(struct timeout *new, int to_ticks) { @@ -294,7 +288,6 @@ timeout_add(struct timeout *new, int to_ int ret = 1; KASSERT(ISSET(new->to_flags, TIMEOUT_INITIALIZED)); - KASSERT(!ISSET(new->to_flags, TIMEOUT_KCLOCK)); KASSERT(new->to_kclock == KCLOCK_NONE); KASSERT(to_ticks >= 0); @@ -402,8 +395,8 @@ timeout_at_ts(struct timeout *to, const mtx_enter(&timeout_mutex); - KASSERT(ISSET(to->to_flags, TIMEOUT_INITIALIZED | TIMEOUT_KCLOCK)); - KASSERT(to->to_kclock != KCLOCK_NONE); + KASSERT(ISSET(to->to_flags, TIMEOUT_INITIALIZED)); + KASSERT(to->to_kclock == KCLOCK_UPTIME); old_abstime = to->to_abstime; to->to_abstime = *abstime; @@ -497,7 +490,8 @@ timeout_barrier(struct timeout *to) procflag = (to->to_flags & TIMEOUT_PROC); timeout_sync_order(procflag); - timeout_set_flags(&barrier, timeout_barrier_timeout, &c, procflag); + timeout_set_flags(&barrier, timeout_barrier_timeout, &c, KCLOCK_NONE, + procflag); barrier.to_process = curproc->p_p; cond_init(&c); @@ -535,7 +529,7 @@ timeout_bucket(const struct timeout *to) struct timespec diff, shifted_abstime; uint32_t level; - KASSERT(ISSET(to->to_flags, TIMEOUT_KCLOCK)); + KASSERT(to->to_kclock == KCLOCK_UPTIME); KASSERT(timespeccmp(&kc->kc_lastscan, &to->to_abstime, <)); timespecsub(&to->to_abstime, &kc->kc_lastscan, &diff); @@ -750,7 +744,7 @@ softclock(void *arg) CIRCQ_REMOVE(&to->to_list); if (to == first_new) new = 1; - if (ISSET(to->to_flags, TIMEOUT_KCLOCK)) + if (to->to_kclock != KCLOCK_NONE) softclock_process_kclock_timeout(to, new); else softclock_process_tick_timeout(to, new); @@ -915,7 +909,7 @@ db_show_timeout(struct timeout *to, stru else if (bucket == &timeout_proc) where = "thread"; else { - if (ISSET(to->to_flags, TIMEOUT_KCLOCK)) + if (to->to_kclock != KCLOCK_NONE) wheel = timeout_wheel_kc; else wheel = timeout_wheel; @@ -924,7 +918,7 @@ db_show_timeout(struct timeout *to, stru (bucket - wheel) / WHEELSIZE); where = buf; } - if (ISSET(to->to_flags, TIMEOUT_KCLOCK)) { + if (to->to_kclock != KCLOCK_NONE) { kc = &timeout_kclock[to->to_kclock]; timespecsub(&to->to_abstime, &kc->kc_lastscan, &remaining); db_printf("%20s %8s %7s 0x%0*lx %s\n", Index: sys/kern/kern_fork.c =================================================================== RCS file: /cvs/src/sys/kern/kern_fork.c,v retrieving revision 1.236 diff -u -p -r1.236 kern_fork.c --- sys/kern/kern_fork.c 19 Jun 2021 02:05:33 -0000 1.236 +++ sys/kern/kern_fork.c 24 Jun 2021 22:37:39 -0000 @@ -201,7 +201,7 @@ process_initialize(struct process *pr, s rw_init(&pr->ps_lock, "pslock"); mtx_init(&pr->ps_mtx, IPL_MPFLOOR); - timeout_set_kclock(&pr->ps_realit_to, realitexpire, pr, + timeout_set_flags(&pr->ps_realit_to, realitexpire, pr, KCLOCK_UPTIME, 0); timeout_set(&pr->ps_rucheck_to, rucheck, pr); } Index: sys/sys/timeout.h =================================================================== RCS file: /cvs/src/sys/sys/timeout.h,v retrieving revision 1.42 diff -u -p -r1.42 timeout.h --- sys/sys/timeout.h 19 Jun 2021 02:05:33 -0000 1.42 +++ sys/sys/timeout.h 24 Jun 2021 22:37:39 -0000 @@ -54,7 +54,6 @@ struct timeout { #define TIMEOUT_ONQUEUE 0x02 /* on any timeout queue */ #define TIMEOUT_INITIALIZED 0x04 /* initialized */ #define TIMEOUT_TRIGGERED 0x08 /* running or ran */ -#define TIMEOUT_KCLOCK 0x10 /* clock-based timeout */ struct timeoutstat { uint64_t tos_added; /* timeout_add*(9) calls */ @@ -98,18 +97,14 @@ int timeout_sysctl(void *, size_t *, voi .to_kclock = (_kclock) \ } -#define TIMEOUT_INITIALIZER_KCLOCK(_fn, _arg, _kclock, _flags) \ - __TIMEOUT_INITIALIZER((_fn), (_args), (_kclock), (_flags) | TIMEOUT_KCLOCK) - -#define TIMEOUT_INITIALIZER_FLAGS(_fn, _arg, _flags) \ - __TIMEOUT_INITIALIZER((_fn), (_args), KCLOCK_NONE, (_flags)) +#define TIMEOUT_INITIALIZER_FLAGS(_fn, _arg, _kclock, _flags) \ + __TIMEOUT_INITIALIZER((_fn), (_args), (_kclock), (_flags)) #define TIMEOUT_INITIALIZER(_f, _a) \ __TIMEOUT_INITIALIZER((_f), (_a), KCLOCK_NONE, 0) void timeout_set(struct timeout *, void (*)(void *), void *); -void timeout_set_flags(struct timeout *, void (*)(void *), void *, int); -void timeout_set_kclock(struct timeout *, void (*)(void *), void *, int, int); +void timeout_set_flags(struct timeout *, void (*)(void *), void *, int, int); void timeout_set_proc(struct timeout *, void (*)(void *), void *); int timeout_add(struct timeout *, int);