On Thu, Jun 24, 2021 at 05:39:56PM -0500, Scott Cheloha wrote:
> 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.
>
it did! you must have just missed it somehow.
> Fixed.
>
> Are there any other mdoc(7)-type stylistic anachronisms we can fix up?
>
everything else looked ok.
jmc
> 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);
>