Index: head/ObsoleteFiles.inc =================================================================== --- head/ObsoleteFiles.inc +++ head/ObsoleteFiles.inc @@ -36,6 +36,9 @@ # xargs -n1 | sort | uniq -d; # done +# 20191213: remove timeout(9) +OLD_FILES+=usr/share/man/man9/timeout.9.gz +OLD_FILES+=usr/share/man/man9/untimeout.9.gz # 20191128: Removal of trm(4) OLD_FILES+=usr/share/man/man4/trm.4.gz # 20191121: Removal of sio(4) Index: head/share/man/man9/Makefile =================================================================== --- head/share/man/man9/Makefile +++ head/share/man/man9/Makefile @@ -56,6 +56,7 @@ BUS_SETUP_INTR.9 \ bus_space.9 \ byteorder.9 \ + callout.9 \ casuword.9 \ cd.9 \ cnv.9 \ @@ -315,7 +316,6 @@ tcp_functions.9 \ thread_exit.9 \ time.9 \ - timeout.9 \ tvtohz.9 \ ucred.9 \ uidinfo.9 \ @@ -766,6 +766,30 @@ byteorder.9 le64dec.9 \ byteorder.9 le64enc.9 \ byteorder.9 le64toh.9 +MLINKS+=callout.9 callout_active.9 \ + callout.9 callout_async_drain.9 \ + callout.9 callout_deactivate.9 \ + callout.9 callout_drain.9 \ + callout.9 callout_handle_init.9 \ + callout.9 callout_init.9 \ + callout.9 callout_init_mtx.9 \ + callout.9 callout_init_rm.9 \ + callout.9 callout_init_rw.9 \ + callout.9 callout_pending.9 \ + callout.9 callout_reset.9 \ + callout.9 callout_reset_curcpu.9 \ + callout.9 callout_reset_on.9 \ + callout.9 callout_reset_sbt.9 \ + callout.9 callout_reset_sbt_curcpu.9 \ + callout.9 callout_reset_sbt_on.9 \ + callout.9 callout_schedule.9 \ + callout.9 callout_schedule_curcpu.9 \ + callout.9 callout_schedule_on.9 \ + callout.9 callout_schedule_sbt.9 \ + callout.9 callout_schedule_sbt_curcpu.9 \ + callout.9 callout_schedule_sbt_on.9 \ + callout.9 callout_stop.9 \ + callout.9 callout_when.9 MLINKS+=cnv.9 cnvlist.9 \ cnv.9 cnvlist_free_binary.9 \ cnv.9 cnvlist_free_bool.9 \ @@ -2097,32 +2121,6 @@ MLINKS+=time.9 boottime.9 \ time.9 time_second.9 \ time.9 time_uptime.9 -MLINKS+=timeout.9 callout.9 \ - timeout.9 callout_active.9 \ - timeout.9 callout_async_drain.9 \ - timeout.9 callout_deactivate.9 \ - timeout.9 callout_drain.9 \ - timeout.9 callout_handle_init.9 \ - timeout.9 callout_init.9 \ - timeout.9 callout_init_mtx.9 \ - timeout.9 callout_init_rm.9 \ - timeout.9 callout_init_rw.9 \ - timeout.9 callout_pending.9 \ - timeout.9 callout_reset.9 \ - timeout.9 callout_reset_curcpu.9 \ - timeout.9 callout_reset_on.9 \ - timeout.9 callout_reset_sbt.9 \ - timeout.9 callout_reset_sbt_curcpu.9 \ - timeout.9 callout_reset_sbt_on.9 \ - timeout.9 callout_schedule.9 \ - timeout.9 callout_schedule_curcpu.9 \ - timeout.9 callout_schedule_on.9 \ - timeout.9 callout_schedule_sbt.9 \ - timeout.9 callout_schedule_sbt_curcpu.9 \ - timeout.9 callout_schedule_sbt_on.9 \ - timeout.9 callout_stop.9 \ - timeout.9 callout_when.9 \ - timeout.9 untimeout.9 MLINKS+=ucred.9 crcopy.9 \ ucred.9 crcopysafe.9 \ ucred.9 crdup.9 \ Index: head/share/man/man9/callout.9 =================================================================== --- head/share/man/man9/callout.9 +++ head/share/man/man9/callout.9 @@ -0,0 +1,922 @@ +.\" $NetBSD: timeout.9,v 1.2 1996/06/23 22:32:34 pk Exp $ +.\" +.\" Copyright (c) 1996 The NetBSD Foundation, Inc. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to The NetBSD Foundation +.\" by Paul Kranenburg. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS +.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE +.\" LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +.\" POSSIBILITY OF SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd December 13, 2019 +.Dt CALLOUT 9 +.Os +.Sh NAME +.Nm callout_active , +.Nm callout_deactivate , +.Nm callout_async_drain , +.Nm callout_drain , +.Nm callout_handle_init , +.Nm callout_init , +.Nm callout_init_mtx , +.Nm callout_init_rm , +.Nm callout_init_rw , +.Nm callout_pending , +.Nm callout_reset , +.Nm callout_reset_curcpu , +.Nm callout_reset_on , +.Nm callout_reset_sbt , +.Nm callout_reset_sbt_curcpu , +.Nm callout_reset_sbt_on , +.Nm callout_schedule , +.Nm callout_schedule_curcpu , +.Nm callout_schedule_on , +.Nm callout_schedule_sbt , +.Nm callout_schedule_sbt_curcpu , +.Nm callout_schedule_sbt_on , +.Nm callout_stop , +.Nm callout_when +.Nd execute a function after a specified length of time +.Sh SYNOPSIS +.In sys/types.h +.In sys/callout.h +.Bd -literal +typedef void callout_func_t (void *); +.Ed +.Ft int +.Fn callout_active "struct callout *c" +.Ft void +.Fn callout_deactivate "struct callout *c" +.Ft int +.Fn callout_async_drain "struct callout *c" "callout_func_t *drain" +.Ft int +.Fn callout_drain "struct callout *c" +.Ft void +.Fn callout_handle_init "struct callout_handle *handle" +.Bd -literal +struct callout_handle handle = CALLOUT_HANDLE_INITIALIZER(&handle); +.Ed +.Ft void +.Fn callout_init "struct callout *c" "int mpsafe" +.Ft void +.Fn callout_init_mtx "struct callout *c" "struct mtx *mtx" "int flags" +.Ft void +.Fn callout_init_rm "struct callout *c" "struct rmlock *rm" "int flags" +.Ft void +.Fn callout_init_rw "struct callout *c" "struct rwlock *rw" "int flags" +.Ft int +.Fn callout_pending "struct callout *c" +.Ft int +.Fo callout_reset +.Fa "struct callout *c" +.Fa "int ticks" +.Fa "callout_func_t *func" +.Fa "void *arg" +.Fc +.Ft int +.Fo callout_reset_curcpu +.Fa "struct callout *c" +.Fa "int ticks" +.Fa "callout_func_t *func" +.Fa "void *arg" +.Fc +.Ft int +.Fo callout_reset_on +.Fa "struct callout *c" +.Fa "int ticks" +.Fa "callout_func_t *func" +.Fa "void *arg" +.Fa "int cpu" +.Fc +.Ft int +.Fo callout_reset_sbt +.Fa "struct callout *c" +.Fa "sbintime_t sbt" +.Fa "sbintime_t pr" +.Fa "callout_func_t *func" +.Fa "void *arg" +.Fa "int flags" +.Fc +.Ft int +.Fo callout_reset_sbt_curcpu +.Fa "struct callout *c" +.Fa "sbintime_t sbt" +.Fa "sbintime_t pr" +.Fa "callout_func_t *func" +.Fa "void *arg" +.Fa "int flags" +.Fc +.Ft int +.Fo callout_reset_sbt_on +.Fa "struct callout *c" +.Fa "sbintime_t sbt" +.Fa "sbintime_t pr" +.Fa "callout_func_t *func" +.Fa "void *arg" +.Fa "int cpu" +.Fa "int flags" +.Fc +.Ft int +.Fn callout_schedule "struct callout *c" "int ticks" +.Ft int +.Fn callout_schedule_curcpu "struct callout *c" "int ticks" +.Ft int +.Fn callout_schedule_on "struct callout *c" "int ticks" "int cpu" +.Ft int +.Fo callout_schedule_sbt +.Fa "struct callout *c" +.Fa "sbintime_t sbt" +.Fa "sbintime_t pr" +.Fa "int flags" +.Fc +.Ft int +.Fo callout_schedule_sbt_curcpu +.Fa "struct callout *c" +.Fa "sbintime_t sbt" +.Fa "sbintime_t pr" +.Fa "int flags" +.Fc +.Ft int +.Fo callout_schedule_sbt_on +.Fa "struct callout *c" +.Fa "sbintime_t sbt" +.Fa "sbintime_t pr" +.Fa "int cpu" +.Fa "int flags" +.Fc +.Ft int +.Fn callout_stop "struct callout *c" +.Ft sbintime_t +.Fo callout_when +.Fa "sbintime_t sbt" +.Fa "sbintime_t precision" +.Fa "int flags" +.Fa "sbintime_t *sbt_res" +.Fa "sbintime_t *precision_res" +.Fc +.Sh DESCRIPTION +The +.Nm callout +API is used to schedule a call to an arbitrary function at a specific +time in the future. +Consumers of this API are required to allocate a callout structure +.Pq struct callout +for each pending function invocation. +This structure stores state about the pending function invocation including +the function to be called and the time at which the function should be invoked. +Pending function calls can be cancelled or rescheduled to a different time. +In addition, +a callout structure may be reused to schedule a new function call after a +scheduled call is completed. +.Pp +Callouts only provide a single-shot mode. +If a consumer requires a periodic timer, +it must explicitly reschedule each function call. +This is normally done by rescheduling the subsequent call within the called +function. +.Pp +Callout functions must not sleep. +They may not acquire sleepable locks, +wait on condition variables, +perform blocking allocation requests, +or invoke any other action that might sleep. +.Pp +Each callout structure must be initialized by +.Fn callout_init , +.Fn callout_init_mtx , +.Fn callout_init_rm , +or +.Fn callout_init_rw +before it is passed to any of the other callout functions. +The +.Fn callout_init +function initializes a callout structure in +.Fa c +that is not associated with a specific lock. +If the +.Fa mpsafe +argument is zero, +the callout structure is not considered to be +.Dq multi-processor safe ; +and the Giant lock will be acquired before calling the callout function +and released when the callout function returns. +.Pp +The +.Fn callout_init_mtx , +.Fn callout_init_rm , +and +.Fn callout_init_rw +functions initialize a callout structure in +.Fa c +that is associated with a specific lock. +The lock is specified by the +.Fa mtx , +.Fa rm , +or +.Fa rw +parameter. +The associated lock must be held while stopping or rescheduling the +callout. +The callout subsystem acquires the associated lock before calling the +callout function and releases it after the function returns. +If the callout was cancelled while the callout subsystem waited for the +associated lock, +the callout function is not called, +and the associated lock is released. +This ensures that stopping or rescheduling the callout will abort any +previously scheduled invocation. +.Pp +Only regular mutexes may be used with +.Fn callout_init_mtx ; +spin mutexes are not supported. +A sleepable read-mostly lock +.Po +one initialized with the +.Dv RM_SLEEPABLE +flag +.Pc +may not be used with +.Fn callout_init_rm . +Similarly, other sleepable lock types such as +.Xr sx 9 +and +.Xr lockmgr 9 +cannot be used with callouts because sleeping is not permitted in +the callout subsystem. +.Pp +These +.Fa flags +may be specified for +.Fn callout_init_mtx , +.Fn callout_init_rm , +or +.Fn callout_init_rw : +.Bl -tag -width ".Dv CALLOUT_RETURNUNLOCKED" +.It Dv CALLOUT_RETURNUNLOCKED +The callout function will release the associated lock itself, +so the callout subsystem should not attempt to unlock it +after the callout function returns. +.It Dv CALLOUT_SHAREDLOCK +The lock is only acquired in read mode when running the callout handler. +This flag is ignored by +.Fn callout_init_mtx . +.El +.Pp +The function +.Fn callout_stop +cancels a callout +.Fa c +if it is currently pending. +If the callout is pending and successfully stopped, then +.Fn callout_stop +returns a value of one. +If the callout is not set, or +has already been serviced, then +negative one is returned. +If the callout is currently being serviced and cannot be stopped, +then zero will be returned. +If the callout is currently being serviced and cannot be stopped, and at the +same time a next invocation of the same callout is also scheduled, then +.Fn callout_stop +unschedules the next run and returns zero. +If the callout has an associated lock, +then that lock must be held when this function is called. +.Pp +The function +.Fn callout_async_drain +is identical to +.Fn callout_stop +with one difference. +When +.Fn callout_async_drain +returns zero it will arrange for the function +.Fa drain +to be called using the same argument given to the +.Fn callout_reset +function. +.Fn callout_async_drain +If the callout has an associated lock, +then that lock must be held when this function is called. +Note that when stopping multiple callouts that use the same lock it is possible +to get multiple return's of zero and multiple calls to the +.Fa drain +function, depending upon which CPU's the callouts are running. +The +.Fa drain +function itself is called from the context of the completing callout +i.e. softclock or hardclock, just like a callout itself. +.Pp +The function +.Fn callout_drain +is identical to +.Fn callout_stop +except that it will wait for the callout +.Fa c +to complete if it is already in progress. +This function MUST NOT be called while holding any +locks on which the callout might block, or deadlock will result. +Note that if the callout subsystem has already begun processing this +callout, then the callout function may be invoked before +.Fn callout_drain +returns. +However, the callout subsystem does guarantee that the callout will be +fully stopped before +.Fn callout_drain +returns. +.Pp +The +.Fn callout_reset +and +.Fn callout_schedule +function families schedule a future function invocation for callout +.Fa c . +If +.Fa c +already has a pending callout, +it is cancelled before the new invocation is scheduled. +These functions return a value of one if a pending callout was cancelled +and zero if there was no pending callout. +If the callout has an associated lock, +then that lock must be held when any of these functions are called. +.Pp +The time at which the callout function will be invoked is determined by +either the +.Fa ticks +argument or the +.Fa sbt , +.Fa pr , +and +.Fa flags +arguments. +When +.Fa ticks +is used, +the callout is scheduled to execute after +.Fa ticks Ns No /hz +seconds. +Non-positive values of +.Fa ticks +are silently converted to the value +.Sq 1 . +.Pp +The +.Fa sbt , +.Fa pr , +and +.Fa flags +arguments provide more control over the scheduled time including +support for higher resolution times, +specifying the precision of the scheduled time, +and setting an absolute deadline instead of a relative timeout. +The callout is scheduled to execute in a time window which begins at +the time specified in +.Fa sbt +and extends for the amount of time specified in +.Fa pr . +If +.Fa sbt +specifies a time in the past, +the window is adjusted to start at the current time. +A non-zero value for +.Fa pr +allows the callout subsystem to coalesce callouts scheduled close to each +other into fewer timer interrupts, +reducing processing overhead and power consumption. +These +.Fa flags +may be specified to adjust the interpretation of +.Fa sbt +and +.Fa pr : +.Bl -tag -width ".Dv C_DIRECT_EXEC" +.It Dv C_ABSOLUTE +Handle the +.Fa sbt +argument as an absolute time since boot. +By default, +.Fa sbt +is treated as a relative amount of time, +similar to +.Fa ticks . +.It Dv C_DIRECT_EXEC +Run the handler directly from hardware interrupt context instead of from the +softclock thread. +This reduces latency and overhead, but puts more constraints on the callout +function. +Callout functions run in this context may use only spin mutexes for locking +and should be as small as possible because they run with absolute priority. +.It Fn C_PREL +Specifies relative event time precision as binary logarithm of time interval +divided by acceptable time deviation: 1 -- 1/2, 2 -- 1/4, etc. +Note that the larger of +.Fa pr +or this value is used as the length of the time window. +Smaller values +.Pq which result in larger time intervals +allow the callout subsystem to aggregate more events in one timer interrupt. +.It Dv C_PRECALC +The +.Fa sbt +argument specifies the absolute time at which the callout should be run, +and the +.Fa pr +argument specifies the requested precision, which will not be +adjusted during the scheduling process. +The +.Fa sbt +and +.Fa pr +values should be calculated by an earlier call to +.Fn callout_when +which uses the user-supplied +.Fa sbt , +.Fa pr , +and +.Fa flags +values. +.It Dv C_HARDCLOCK +Align the timeouts to +.Fn hardclock +calls if possible. +.El +.Pp +The +.Fn callout_reset +functions accept a +.Fa func +argument which identifies the function to be called when the time expires. +It must be a pointer to a function that takes a single +.Fa void * +argument. +Upon invocation, +.Fa func +will receive +.Fa arg +as its only argument. +The +.Fn callout_schedule +functions reuse the +.Fa func +and +.Fa arg +arguments from the previous callout. +Note that one of the +.Fn callout_reset +functions must always be called to initialize +.Fa func +and +.Fa arg +before one of the +.Fn callout_schedule +functions can be used. +.Pp +The callout subsystem provides a softclock thread for each CPU in the system. +Callouts are assigned to a single CPU and are executed by the softclock thread +for that CPU. +Initially, +callouts are assigned to CPU 0. +The +.Fn callout_reset_on , +.Fn callout_reset_sbt_on , +.Fn callout_schedule_on +and +.Fn callout_schedule_sbt_on +functions assign the callout to CPU +.Fa cpu . +The +.Fn callout_reset_curcpu , +.Fn callout_reset_sbt_curpu , +.Fn callout_schedule_curcpu +and +.Fn callout_schedule_sbt_curcpu +functions assign the callout to the current CPU. +The +.Fn callout_reset , +.Fn callout_reset_sbt , +.Fn callout_schedule +and +.Fn callout_schedule_sbt +functions schedule the callout to execute in the softclock thread of the CPU +to which it is currently assigned. +.Pp +Softclock threads are not pinned to their respective CPUs by default. +The softclock thread for CPU 0 can be pinned to CPU 0 by setting the +.Va kern.pin_default_swi +loader tunable to a non-zero value. +Softclock threads for CPUs other than zero can be pinned to their +respective CPUs by setting the +.Va kern.pin_pcpu_swi +loader tunable to a non-zero value. +.Pp +The macros +.Fn callout_pending , +.Fn callout_active +and +.Fn callout_deactivate +provide access to the current state of the callout. +The +.Fn callout_pending +macro checks whether a callout is +.Em pending ; +a callout is considered +.Em pending +when a timeout has been set but the time has not yet arrived. +Note that once the timeout time arrives and the callout subsystem +starts to process this callout, +.Fn callout_pending +will return +.Dv FALSE +even though the callout function may not have finished +.Pq or even begun +executing. +The +.Fn callout_active +macro checks whether a callout is marked as +.Em active , +and the +.Fn callout_deactivate +macro clears the callout's +.Em active +flag. +The callout subsystem marks a callout as +.Em active +when a timeout is set and it clears the +.Em active +flag in +.Fn callout_stop +and +.Fn callout_drain , +but it +.Em does not +clear it when a callout expires normally via the execution of the +callout function. +.Pp +The +.Fn callout_when +function may be used to pre-calculate the absolute time at which the +timeout should be run and the precision of the scheduled run time +according to the required time +.Fa sbt , +precision +.Fa precision , +and additional adjustments requested by the +.Fa flags +argument. +Flags accepted by the +.Fn callout_when +function are the same as flags for the +.Fn callout_reset +function. +The resulting time is assigned to the variable pointed to by the +.Fa sbt_res +argument, and the resulting precision is assigned to +.Fa *precision_res . +When passing the results to +.Fa callout_reset , +add the +.Va C_PRECALC +flag to +.Fa flags , +to avoid incorrect re-adjustment. +The function is intended for situations where precise time of the callout +run should be known in advance, since +trying to read this time from the callout structure itself after a +.Fn callout_reset +call is racy. +.Ss "Avoiding Race Conditions" +The callout subsystem invokes callout functions from its own thread +context. +Without some kind of synchronization, +it is possible that a callout +function will be invoked concurrently with an attempt to stop or reset +the callout by another thread. +In particular, since callout functions typically acquire a lock as +their first action, the callout function may have already been invoked, +but is blocked waiting for that lock at the time that another thread +tries to reset or stop the callout. +.Pp +There are three main techniques for addressing these +synchronization concerns. +The first approach is preferred as it is the simplest: +.Bl -enum -offset indent +.It +Callouts can be associated with a specific lock when they are initialized +by +.Fn callout_init_mtx , +.Fn callout_init_rm , +or +.Fn callout_init_rw . +When a callout is associated with a lock, +the callout subsystem acquires the lock before the callout function is +invoked. +This allows the callout subsystem to transparently handle races between +callout cancellation, +scheduling, +and execution. +Note that the associated lock must be acquired before calling +.Fn callout_stop +or one of the +.Fn callout_reset +or +.Fn callout_schedule +functions to provide this safety. +.Pp +A callout initialized via +.Fn callout_init +with +.Fa mpsafe +set to zero is implicitly associated with the +.Va Giant +mutex. +If +.Va Giant +is held when cancelling or rescheduling the callout, +then its use will prevent races with the callout function. +.It +The return value from +.Fn callout_stop +.Po +or the +.Fn callout_reset +and +.Fn callout_schedule +function families +.Pc +indicates whether or not the callout was removed. +If it is known that the callout was set and the callout function has +not yet executed, then a return value of +.Dv FALSE +indicates that the callout function is about to be called. +For example: +.Bd -literal -offset indent +if (sc->sc_flags & SCFLG_CALLOUT_RUNNING) { + if (callout_stop(&sc->sc_callout)) { + sc->sc_flags &= ~SCFLG_CALLOUT_RUNNING; + /* successfully stopped */ + } else { + /* + * callout has expired and callout + * function is about to be executed + */ + } +} +.Ed +.It +The +.Fn callout_pending , +.Fn callout_active +and +.Fn callout_deactivate +macros can be used together to work around the race conditions. +When a callout's timeout is set, the callout subsystem marks the +callout as both +.Em active +and +.Em pending . +When the timeout time arrives, the callout subsystem begins processing +the callout by first clearing the +.Em pending +flag. +It then invokes the callout function without changing the +.Em active +flag, and does not clear the +.Em active +flag even after the callout function returns. +The mechanism described here requires the callout function itself to +clear the +.Em active +flag using the +.Fn callout_deactivate +macro. +The +.Fn callout_stop +and +.Fn callout_drain +functions always clear both the +.Em active +and +.Em pending +flags before returning. +.Pp +The callout function should first check the +.Em pending +flag and return without action if +.Fn callout_pending +returns +.Dv TRUE . +This indicates that the callout was rescheduled using +.Fn callout_reset +just before the callout function was invoked. +If +.Fn callout_active +returns +.Dv FALSE +then the callout function should also return without action. +This indicates that the callout has been stopped. +Finally, the callout function should call +.Fn callout_deactivate +to clear the +.Em active +flag. +For example: +.Bd -literal -offset indent +mtx_lock(&sc->sc_mtx); +if (callout_pending(&sc->sc_callout)) { + /* callout was reset */ + mtx_unlock(&sc->sc_mtx); + return; +} +if (!callout_active(&sc->sc_callout)) { + /* callout was stopped */ + mtx_unlock(&sc->sc_mtx); + return; +} +callout_deactivate(&sc->sc_callout); +/* rest of callout function */ +.Ed +.Pp +Together with appropriate synchronization, such as the mutex used above, +this approach permits the +.Fn callout_stop +and +.Fn callout_reset +functions to be used at any time without races. +For example: +.Bd -literal -offset indent +mtx_lock(&sc->sc_mtx); +callout_stop(&sc->sc_callout); +/* The callout is effectively stopped now. */ +.Ed +.Pp +If the callout is still pending then these functions operate normally, +but if processing of the callout has already begun then the tests in +the callout function cause it to return without further action. +Synchronization between the callout function and other code ensures that +stopping or resetting the callout will never be attempted while the +callout function is past the +.Fn callout_deactivate +call. +.Pp +The above technique additionally ensures that the +.Em active +flag always reflects whether the callout is effectively enabled or +disabled. +If +.Fn callout_active +returns false, then the callout is effectively disabled, since even if +the callout subsystem is actually just about to invoke the callout +function, the callout function will return without action. +.El +.Pp +There is one final race condition that must be considered when a +callout is being stopped for the last time. +In this case it may not be safe to let the callout function itself +detect that the callout was stopped, since it may need to access +data objects that have already been destroyed or recycled. +To ensure that the callout is completely finished, a call to +.Fn callout_drain +should be used. +In particular, +a callout should always be drained prior to destroying its associated lock +or releasing the storage for the callout structure. +.Sh RETURN VALUES +The +.Fn callout_active +macro returns the state of a callout's +.Em active +flag. +.Pp +The +.Fn callout_pending +macro returns the state of a callout's +.Em pending +flag. +.Pp +The +.Fn callout_reset +and +.Fn callout_schedule +function families return a value of one if the callout was pending before the new +function invocation was scheduled. +.Pp +The +.Fn callout_stop +and +.Fn callout_drain +functions return a value of one if the callout was still pending when it was +called, a zero if the callout could not be stopped and a negative one is it +was either not running or has already completed. +.Sh HISTORY +.Fx +initially used the long standing +.Bx +linked list +callout mechanism which offered O(n) insertion and removal running time +but did not generate or require handles for untimeout operations. +.Pp +.Fx 3.0 +introduced a new set of timeout and untimeout routines from +.Nx +based on the work of +.An Adam M. Costello +and +.An George Varghese , +published in a technical report entitled +.%T "Redesigning the BSD Callout and Timer Facilities" +and modified for inclusion in +.Fx +by +.An Justin T. Gibbs . +The original work on the data structures used in that implementation +was published by +.An G. Varghese +and +.An A. Lauck +in the paper +.%T "Hashed and Hierarchical Timing Wheels: Data Structures for the Efficient Implementation of a Timer Facility" +in the +.%B "Proceedings of the 11th ACM Annual Symposium on Operating Systems Principles" . +.Pp +.Fx 3.3 +introduced the first implementations of +.Fn callout_init , +.Fn callout_reset , +and +.Fn callout_stop +which permitted callers to allocate dedicated storage for callouts. +This ensured that a callout would always fire unlike +.Fn timeout +which would silently fail if it was unable to allocate a callout. +.Pp +.Fx 5.0 +permitted callout handlers to be tagged as MPSAFE via +.Fn callout_init . +.Pp +.Fx 5.3 +introduced +.Fn callout_drain . +.Pp +.Fx 6.0 +introduced +.Fn callout_init_mtx . +.Pp +.Fx 8.0 +introduced per-CPU callout wheels, +.Fn callout_init_rw , +and +.Fn callout_schedule . +.Pp +.Fx 9.0 +changed the underlying timer interrupts used to drive callouts to prefer +one-shot event timers instead of a periodic timer interrupt. +.Pp +.Fx 10.0 +switched the callout wheel to support tickless operation. +These changes introduced +.Vt sbintime_t +and the +.Fn callout_reset_sbt* +family of functions. +.Fx 10.0 also added +.Dv C_DIRECT_EXEC +and +.Fn callout_init_rm . +.Pp +.Fx 10.2 +introduced the +.Fn callout_schedule_sbt* +family of functions. +.Pp +.Fx 11.0 +introduced +.Fn callout_async_drain . +.Fx 11.1 +introduced +.Fn callout_when . +.Fx 13.0 +removed +.Vt timeout_t , +.Fn timeout , +and +.Fn untimeout . Index: head/share/man/man9/timeout.9 =================================================================== --- head/share/man/man9/timeout.9 +++ head/share/man/man9/timeout.9 @@ -1,961 +0,0 @@ -.\" $NetBSD: timeout.9,v 1.2 1996/06/23 22:32:34 pk Exp $ -.\" -.\" Copyright (c) 1996 The NetBSD Foundation, Inc. -.\" All rights reserved. -.\" -.\" This code is derived from software contributed to The NetBSD Foundation -.\" by Paul Kranenburg. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" -.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS -.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED -.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE -.\" LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -.\" POSSIBILITY OF SUCH DAMAGE. -.\" -.\" $FreeBSD$ -.\" -.Dd December 10, 2019 -.Dt TIMEOUT 9 -.Os -.Sh NAME -.Nm callout_active , -.Nm callout_deactivate , -.Nm callout_async_drain , -.Nm callout_drain , -.Nm callout_handle_init , -.Nm callout_init , -.Nm callout_init_mtx , -.Nm callout_init_rm , -.Nm callout_init_rw , -.Nm callout_pending , -.Nm callout_reset , -.Nm callout_reset_curcpu , -.Nm callout_reset_on , -.Nm callout_reset_sbt , -.Nm callout_reset_sbt_curcpu , -.Nm callout_reset_sbt_on , -.Nm callout_schedule , -.Nm callout_schedule_curcpu , -.Nm callout_schedule_on , -.Nm callout_schedule_sbt , -.Nm callout_schedule_sbt_curcpu , -.Nm callout_schedule_sbt_on , -.Nm callout_stop , -.Nm callout_when , -.Nm timeout , -.Nm untimeout -.Nd execute a function after a specified length of time -.Sh SYNOPSIS -.In sys/types.h -.In sys/callout.h -.In sys/systm.h -.Bd -literal -typedef void callout_func_t (void *); -typedef void timeout_t (void *); -.Ed -.Ft int -.Fn callout_active "struct callout *c" -.Ft void -.Fn callout_deactivate "struct callout *c" -.Ft int -.Fn callout_async_drain "struct callout *c" "callout_func_t *drain" -.Ft int -.Fn callout_drain "struct callout *c" -.Ft void -.Fn callout_handle_init "struct callout_handle *handle" -.Bd -literal -struct callout_handle handle = CALLOUT_HANDLE_INITIALIZER(&handle); -.Ed -.Ft void -.Fn callout_init "struct callout *c" "int mpsafe" -.Ft void -.Fn callout_init_mtx "struct callout *c" "struct mtx *mtx" "int flags" -.Ft void -.Fn callout_init_rm "struct callout *c" "struct rmlock *rm" "int flags" -.Ft void -.Fn callout_init_rw "struct callout *c" "struct rwlock *rw" "int flags" -.Ft int -.Fn callout_pending "struct callout *c" -.Ft int -.Fo callout_reset -.Fa "struct callout *c" -.Fa "int ticks" -.Fa "callout_func_t *func" -.Fa "void *arg" -.Fc -.Ft int -.Fo callout_reset_curcpu -.Fa "struct callout *c" -.Fa "int ticks" -.Fa "callout_func_t *func" -.Fa "void *arg" -.Fc -.Ft int -.Fo callout_reset_on -.Fa "struct callout *c" -.Fa "int ticks" -.Fa "callout_func_t *func" -.Fa "void *arg" -.Fa "int cpu" -.Fc -.Ft int -.Fo callout_reset_sbt -.Fa "struct callout *c" -.Fa "sbintime_t sbt" -.Fa "sbintime_t pr" -.Fa "callout_func_t *func" -.Fa "void *arg" -.Fa "int flags" -.Fc -.Ft int -.Fo callout_reset_sbt_curcpu -.Fa "struct callout *c" -.Fa "sbintime_t sbt" -.Fa "sbintime_t pr" -.Fa "callout_func_t *func" -.Fa "void *arg" -.Fa "int flags" -.Fc -.Ft int -.Fo callout_reset_sbt_on -.Fa "struct callout *c" -.Fa "sbintime_t sbt" -.Fa "sbintime_t pr" -.Fa "callout_func_t *func" -.Fa "void *arg" -.Fa "int cpu" -.Fa "int flags" -.Fc -.Ft int -.Fn callout_schedule "struct callout *c" "int ticks" -.Ft int -.Fn callout_schedule_curcpu "struct callout *c" "int ticks" -.Ft int -.Fn callout_schedule_on "struct callout *c" "int ticks" "int cpu" -.Ft int -.Fo callout_schedule_sbt -.Fa "struct callout *c" -.Fa "sbintime_t sbt" -.Fa "sbintime_t pr" -.Fa "int flags" -.Fc -.Ft int -.Fo callout_schedule_sbt_curcpu -.Fa "struct callout *c" -.Fa "sbintime_t sbt" -.Fa "sbintime_t pr" -.Fa "int flags" -.Fc -.Ft int -.Fo callout_schedule_sbt_on -.Fa "struct callout *c" -.Fa "sbintime_t sbt" -.Fa "sbintime_t pr" -.Fa "int cpu" -.Fa "int flags" -.Fc -.Ft int -.Fn callout_stop "struct callout *c" -.Ft sbintime_t -.Fo callout_when -.Fa "sbintime_t sbt" -.Fa "sbintime_t precision" -.Fa "int flags" -.Fa "sbintime_t *sbt_res" -.Fa "sbintime_t *precision_res" -.Fc -.Ft struct callout_handle -.Fn timeout "timeout_t *func" "void *arg" "int ticks" -.Ft void -.Fn untimeout "timeout_t *func" "void *arg" "struct callout_handle handle" -.Sh DESCRIPTION -The -.Nm callout -API is used to schedule a call to an arbitrary function at a specific -time in the future. -Consumers of this API are required to allocate a callout structure -.Pq struct callout -for each pending function invocation. -This structure stores state about the pending function invocation including -the function to be called and the time at which the function should be invoked. -Pending function calls can be cancelled or rescheduled to a different time. -In addition, -a callout structure may be reused to schedule a new function call after a -scheduled call is completed. -.Pp -Callouts only provide a single-shot mode. -If a consumer requires a periodic timer, -it must explicitly reschedule each function call. -This is normally done by rescheduling the subsequent call within the called -function. -.Pp -Callout functions must not sleep. -They may not acquire sleepable locks, -wait on condition variables, -perform blocking allocation requests, -or invoke any other action that might sleep. -.Pp -Each callout structure must be initialized by -.Fn callout_init , -.Fn callout_init_mtx , -.Fn callout_init_rm , -or -.Fn callout_init_rw -before it is passed to any of the other callout functions. -The -.Fn callout_init -function initializes a callout structure in -.Fa c -that is not associated with a specific lock. -If the -.Fa mpsafe -argument is zero, -the callout structure is not considered to be -.Dq multi-processor safe ; -and the Giant lock will be acquired before calling the callout function -and released when the callout function returns. -.Pp -The -.Fn callout_init_mtx , -.Fn callout_init_rm , -and -.Fn callout_init_rw -functions initialize a callout structure in -.Fa c -that is associated with a specific lock. -The lock is specified by the -.Fa mtx , -.Fa rm , -or -.Fa rw -parameter. -The associated lock must be held while stopping or rescheduling the -callout. -The callout subsystem acquires the associated lock before calling the -callout function and releases it after the function returns. -If the callout was cancelled while the callout subsystem waited for the -associated lock, -the callout function is not called, -and the associated lock is released. -This ensures that stopping or rescheduling the callout will abort any -previously scheduled invocation. -.Pp -Only regular mutexes may be used with -.Fn callout_init_mtx ; -spin mutexes are not supported. -A sleepable read-mostly lock -.Po -one initialized with the -.Dv RM_SLEEPABLE -flag -.Pc -may not be used with -.Fn callout_init_rm . -Similarly, other sleepable lock types such as -.Xr sx 9 -and -.Xr lockmgr 9 -cannot be used with callouts because sleeping is not permitted in -the callout subsystem. -.Pp -These -.Fa flags -may be specified for -.Fn callout_init_mtx , -.Fn callout_init_rm , -or -.Fn callout_init_rw : -.Bl -tag -width ".Dv CALLOUT_RETURNUNLOCKED" -.It Dv CALLOUT_RETURNUNLOCKED -The callout function will release the associated lock itself, -so the callout subsystem should not attempt to unlock it -after the callout function returns. -.It Dv CALLOUT_SHAREDLOCK -The lock is only acquired in read mode when running the callout handler. -This flag is ignored by -.Fn callout_init_mtx . -.El -.Pp -The function -.Fn callout_stop -cancels a callout -.Fa c -if it is currently pending. -If the callout is pending and successfully stopped, then -.Fn callout_stop -returns a value of one. -If the callout is not set, or -has already been serviced, then -negative one is returned. -If the callout is currently being serviced and cannot be stopped, -then zero will be returned. -If the callout is currently being serviced and cannot be stopped, and at the -same time a next invocation of the same callout is also scheduled, then -.Fn callout_stop -unschedules the next run and returns zero. -If the callout has an associated lock, -then that lock must be held when this function is called. -.Pp -The function -.Fn callout_async_drain -is identical to -.Fn callout_stop -with one difference. -When -.Fn callout_async_drain -returns zero it will arrange for the function -.Fa drain -to be called using the same argument given to the -.Fn callout_reset -function. -.Fn callout_async_drain -If the callout has an associated lock, -then that lock must be held when this function is called. -Note that when stopping multiple callouts that use the same lock it is possible -to get multiple return's of zero and multiple calls to the -.Fa drain -function, depending upon which CPU's the callouts are running. -The -.Fa drain -function itself is called from the context of the completing callout -i.e. softclock or hardclock, just like a callout itself. -.Pp -The function -.Fn callout_drain -is identical to -.Fn callout_stop -except that it will wait for the callout -.Fa c -to complete if it is already in progress. -This function MUST NOT be called while holding any -locks on which the callout might block, or deadlock will result. -Note that if the callout subsystem has already begun processing this -callout, then the callout function may be invoked before -.Fn callout_drain -returns. -However, the callout subsystem does guarantee that the callout will be -fully stopped before -.Fn callout_drain -returns. -.Pp -The -.Fn callout_reset -and -.Fn callout_schedule -function families schedule a future function invocation for callout -.Fa c . -If -.Fa c -already has a pending callout, -it is cancelled before the new invocation is scheduled. -These functions return a value of one if a pending callout was cancelled -and zero if there was no pending callout. -If the callout has an associated lock, -then that lock must be held when any of these functions are called. -.Pp -The time at which the callout function will be invoked is determined by -either the -.Fa ticks -argument or the -.Fa sbt , -.Fa pr , -and -.Fa flags -arguments. -When -.Fa ticks -is used, -the callout is scheduled to execute after -.Fa ticks Ns No /hz -seconds. -Non-positive values of -.Fa ticks -are silently converted to the value -.Sq 1 . -.Pp -The -.Fa sbt , -.Fa pr , -and -.Fa flags -arguments provide more control over the scheduled time including -support for higher resolution times, -specifying the precision of the scheduled time, -and setting an absolute deadline instead of a relative timeout. -The callout is scheduled to execute in a time window which begins at -the time specified in -.Fa sbt -and extends for the amount of time specified in -.Fa pr . -If -.Fa sbt -specifies a time in the past, -the window is adjusted to start at the current time. -A non-zero value for -.Fa pr -allows the callout subsystem to coalesce callouts scheduled close to each -other into fewer timer interrupts, -reducing processing overhead and power consumption. -These -.Fa flags -may be specified to adjust the interpretation of -.Fa sbt -and -.Fa pr : -.Bl -tag -width ".Dv C_DIRECT_EXEC" -.It Dv C_ABSOLUTE -Handle the -.Fa sbt -argument as an absolute time since boot. -By default, -.Fa sbt -is treated as a relative amount of time, -similar to -.Fa ticks . -.It Dv C_DIRECT_EXEC -Run the handler directly from hardware interrupt context instead of from the -softclock thread. -This reduces latency and overhead, but puts more constraints on the callout -function. -Callout functions run in this context may use only spin mutexes for locking -and should be as small as possible because they run with absolute priority. -.It Fn C_PREL -Specifies relative event time precision as binary logarithm of time interval -divided by acceptable time deviation: 1 -- 1/2, 2 -- 1/4, etc. -Note that the larger of -.Fa pr -or this value is used as the length of the time window. -Smaller values -.Pq which result in larger time intervals -allow the callout subsystem to aggregate more events in one timer interrupt. -.It Dv C_PRECALC -The -.Fa sbt -argument specifies the absolute time at which the callout should be run, -and the -.Fa pr -argument specifies the requested precision, which will not be -adjusted during the scheduling process. -The -.Fa sbt -and -.Fa pr -values should be calculated by an earlier call to -.Fn callout_when -which uses the user-supplied -.Fa sbt , -.Fa pr , -and -.Fa flags -values. -.It Dv C_HARDCLOCK -Align the timeouts to -.Fn hardclock -calls if possible. -.El -.Pp -The -.Fn callout_reset -functions accept a -.Fa func -argument which identifies the function to be called when the time expires. -It must be a pointer to a function that takes a single -.Fa void * -argument. -Upon invocation, -.Fa func -will receive -.Fa arg -as its only argument. -The -.Fn callout_schedule -functions reuse the -.Fa func -and -.Fa arg -arguments from the previous callout. -Note that one of the -.Fn callout_reset -functions must always be called to initialize -.Fa func -and -.Fa arg -before one of the -.Fn callout_schedule -functions can be used. -.Pp -The callout subsystem provides a softclock thread for each CPU in the system. -Callouts are assigned to a single CPU and are executed by the softclock thread -for that CPU. -Initially, -callouts are assigned to CPU 0. -The -.Fn callout_reset_on , -.Fn callout_reset_sbt_on , -.Fn callout_schedule_on -and -.Fn callout_schedule_sbt_on -functions assign the callout to CPU -.Fa cpu . -The -.Fn callout_reset_curcpu , -.Fn callout_reset_sbt_curpu , -.Fn callout_schedule_curcpu -and -.Fn callout_schedule_sbt_curcpu -functions assign the callout to the current CPU. -The -.Fn callout_reset , -.Fn callout_reset_sbt , -.Fn callout_schedule -and -.Fn callout_schedule_sbt -functions schedule the callout to execute in the softclock thread of the CPU -to which it is currently assigned. -.Pp -Softclock threads are not pinned to their respective CPUs by default. -The softclock thread for CPU 0 can be pinned to CPU 0 by setting the -.Va kern.pin_default_swi -loader tunable to a non-zero value. -Softclock threads for CPUs other than zero can be pinned to their -respective CPUs by setting the -.Va kern.pin_pcpu_swi -loader tunable to a non-zero value. -.Pp -The macros -.Fn callout_pending , -.Fn callout_active -and -.Fn callout_deactivate -provide access to the current state of the callout. -The -.Fn callout_pending -macro checks whether a callout is -.Em pending ; -a callout is considered -.Em pending -when a timeout has been set but the time has not yet arrived. -Note that once the timeout time arrives and the callout subsystem -starts to process this callout, -.Fn callout_pending -will return -.Dv FALSE -even though the callout function may not have finished -.Pq or even begun -executing. -The -.Fn callout_active -macro checks whether a callout is marked as -.Em active , -and the -.Fn callout_deactivate -macro clears the callout's -.Em active -flag. -The callout subsystem marks a callout as -.Em active -when a timeout is set and it clears the -.Em active -flag in -.Fn callout_stop -and -.Fn callout_drain , -but it -.Em does not -clear it when a callout expires normally via the execution of the -callout function. -.Pp -The -.Fn callout_when -function may be used to pre-calculate the absolute time at which the -timeout should be run and the precision of the scheduled run time -according to the required time -.Fa sbt , -precision -.Fa precision , -and additional adjustments requested by the -.Fa flags -argument. -Flags accepted by the -.Fn callout_when -function are the same as flags for the -.Fn callout_reset -function. -The resulting time is assigned to the variable pointed to by the -.Fa sbt_res -argument, and the resulting precision is assigned to -.Fa *precision_res . -When passing the results to -.Fa callout_reset , -add the -.Va C_PRECALC -flag to -.Fa flags , -to avoid incorrect re-adjustment. -The function is intended for situations where precise time of the callout -run should be known in advance, since -trying to read this time from the callout structure itself after a -.Fn callout_reset -call is racy. -.Ss "Avoiding Race Conditions" -The callout subsystem invokes callout functions from its own thread -context. -Without some kind of synchronization, -it is possible that a callout -function will be invoked concurrently with an attempt to stop or reset -the callout by another thread. -In particular, since callout functions typically acquire a lock as -their first action, the callout function may have already been invoked, -but is blocked waiting for that lock at the time that another thread -tries to reset or stop the callout. -.Pp -There are three main techniques for addressing these -synchronization concerns. -The first approach is preferred as it is the simplest: -.Bl -enum -offset indent -.It -Callouts can be associated with a specific lock when they are initialized -by -.Fn callout_init_mtx , -.Fn callout_init_rm , -or -.Fn callout_init_rw . -When a callout is associated with a lock, -the callout subsystem acquires the lock before the callout function is -invoked. -This allows the callout subsystem to transparently handle races between -callout cancellation, -scheduling, -and execution. -Note that the associated lock must be acquired before calling -.Fn callout_stop -or one of the -.Fn callout_reset -or -.Fn callout_schedule -functions to provide this safety. -.Pp -A callout initialized via -.Fn callout_init -with -.Fa mpsafe -set to zero is implicitly associated with the -.Va Giant -mutex. -If -.Va Giant -is held when cancelling or rescheduling the callout, -then its use will prevent races with the callout function. -.It -The return value from -.Fn callout_stop -.Po -or the -.Fn callout_reset -and -.Fn callout_schedule -function families -.Pc -indicates whether or not the callout was removed. -If it is known that the callout was set and the callout function has -not yet executed, then a return value of -.Dv FALSE -indicates that the callout function is about to be called. -For example: -.Bd -literal -offset indent -if (sc->sc_flags & SCFLG_CALLOUT_RUNNING) { - if (callout_stop(&sc->sc_callout)) { - sc->sc_flags &= ~SCFLG_CALLOUT_RUNNING; - /* successfully stopped */ - } else { - /* - * callout has expired and callout - * function is about to be executed - */ - } -} -.Ed -.It -The -.Fn callout_pending , -.Fn callout_active -and -.Fn callout_deactivate -macros can be used together to work around the race conditions. -When a callout's timeout is set, the callout subsystem marks the -callout as both -.Em active -and -.Em pending . -When the timeout time arrives, the callout subsystem begins processing -the callout by first clearing the -.Em pending -flag. -It then invokes the callout function without changing the -.Em active -flag, and does not clear the -.Em active -flag even after the callout function returns. -The mechanism described here requires the callout function itself to -clear the -.Em active -flag using the -.Fn callout_deactivate -macro. -The -.Fn callout_stop -and -.Fn callout_drain -functions always clear both the -.Em active -and -.Em pending -flags before returning. -.Pp -The callout function should first check the -.Em pending -flag and return without action if -.Fn callout_pending -returns -.Dv TRUE . -This indicates that the callout was rescheduled using -.Fn callout_reset -just before the callout function was invoked. -If -.Fn callout_active -returns -.Dv FALSE -then the callout function should also return without action. -This indicates that the callout has been stopped. -Finally, the callout function should call -.Fn callout_deactivate -to clear the -.Em active -flag. -For example: -.Bd -literal -offset indent -mtx_lock(&sc->sc_mtx); -if (callout_pending(&sc->sc_callout)) { - /* callout was reset */ - mtx_unlock(&sc->sc_mtx); - return; -} -if (!callout_active(&sc->sc_callout)) { - /* callout was stopped */ - mtx_unlock(&sc->sc_mtx); - return; -} -callout_deactivate(&sc->sc_callout); -/* rest of callout function */ -.Ed -.Pp -Together with appropriate synchronization, such as the mutex used above, -this approach permits the -.Fn callout_stop -and -.Fn callout_reset -functions to be used at any time without races. -For example: -.Bd -literal -offset indent -mtx_lock(&sc->sc_mtx); -callout_stop(&sc->sc_callout); -/* The callout is effectively stopped now. */ -.Ed -.Pp -If the callout is still pending then these functions operate normally, -but if processing of the callout has already begun then the tests in -the callout function cause it to return without further action. -Synchronization between the callout function and other code ensures that -stopping or resetting the callout will never be attempted while the -callout function is past the -.Fn callout_deactivate -call. -.Pp -The above technique additionally ensures that the -.Em active -flag always reflects whether the callout is effectively enabled or -disabled. -If -.Fn callout_active -returns false, then the callout is effectively disabled, since even if -the callout subsystem is actually just about to invoke the callout -function, the callout function will return without action. -.El -.Pp -There is one final race condition that must be considered when a -callout is being stopped for the last time. -In this case it may not be safe to let the callout function itself -detect that the callout was stopped, since it may need to access -data objects that have already been destroyed or recycled. -To ensure that the callout is completely finished, a call to -.Fn callout_drain -should be used. -In particular, -a callout should always be drained prior to destroying its associated lock -or releasing the storage for the callout structure. -.Sh LEGACY API -.Bf Sy -The functions below are a legacy API that will be removed in a future release. -New code should not use these routines. -.Ef -.Pp -The function -.Fn timeout -schedules a call to the function given by the argument -.Fa func -to take place after -.Fa ticks Ns No /hz -seconds. -Non-positive values of -.Fa ticks -are silently converted to the value -.Sq 1 . -.Fa func -should be a pointer to a function that takes a -.Fa void * -argument. -Upon invocation, -.Fa func -will receive -.Fa arg -as its only argument. -The return value from -.Fn timeout -is a -.Ft struct callout_handle -which can be used in conjunction with the -.Fn untimeout -function to request that a scheduled timeout be canceled. -.Pp -The function -.Fn callout_handle_init -can be used to initialize a handle to a state which will cause -any calls to -.Fn untimeout -with that handle to return with no side -effects. -.Pp -Assigning a callout handle the value of -.Fn CALLOUT_HANDLE_INITIALIZER -performs the same function as -.Fn callout_handle_init -and is provided for use on statically declared or global callout handles. -.Pp -The function -.Fn untimeout -cancels the timeout associated with -.Fa handle -using the -.Fa func -and -.Fa arg -arguments to validate the handle. -If the handle does not correspond to a timeout with -the function -.Fa func -taking the argument -.Fa arg -no action is taken. -.Fa handle -must be initialized by a previous call to -.Fn timeout , -.Fn callout_handle_init , -or assigned the value of -.Fn CALLOUT_HANDLE_INITIALIZER "&handle" -before being passed to -.Fn untimeout . -The behavior of calling -.Fn untimeout -with an uninitialized handle -is undefined. -.Pp -As handles are recycled by the system, it is possible (although unlikely) -that a handle from one invocation of -.Fn timeout -may match the handle of another invocation of -.Fn timeout -if both calls used the same function pointer and argument, and the first -timeout is expired or canceled before the second call. -The timeout facility offers O(1) running time for -.Fn timeout -and -.Fn untimeout . -Timeouts are executed from -.Fn softclock -with the -.Va Giant -lock held. -Thus they are protected from re-entrancy. -.Sh RETURN VALUES -The -.Fn callout_active -macro returns the state of a callout's -.Em active -flag. -.Pp -The -.Fn callout_pending -macro returns the state of a callout's -.Em pending -flag. -.Pp -The -.Fn callout_reset -and -.Fn callout_schedule -function families return a value of one if the callout was pending before the new -function invocation was scheduled. -.Pp -The -.Fn callout_stop -and -.Fn callout_drain -functions return a value of one if the callout was still pending when it was -called, a zero if the callout could not be stopped and a negative one is it -was either not running or has already completed. -The -.Fn timeout -function returns a -.Ft struct callout_handle -that can be passed to -.Fn untimeout . -.Sh HISTORY -The current timeout and untimeout routines are based on the work of -.An Adam M. Costello -and -.An George Varghese , -published in a technical report entitled -.%T "Redesigning the BSD Callout and Timer Facilities" -and modified slightly for inclusion in -.Fx -by -.An Justin T. Gibbs . -The original work on the data structures used in this implementation -was published by -.An G. Varghese -and -.An A. Lauck -in the paper -.%T "Hashed and Hierarchical Timing Wheels: Data Structures for the Efficient Implementation of a Timer Facility" -in the -.%B "Proceedings of the 11th ACM Annual Symposium on Operating Systems Principles" . -The current implementation replaces the long standing -.Bx -linked list -callout mechanism which offered O(n) insertion and removal running time -but did not generate or require handles for untimeout operations. Index: head/sys/kern/kern_intr.c =================================================================== --- head/sys/kern/kern_intr.c +++ head/sys/kern/kern_intr.c @@ -84,7 +84,6 @@ uintptr_t event; }; -struct intr_event *clk_intr_event; struct intr_event *tty_intr_event; void *vm_ih; struct proc *intrproc; Index: head/sys/kern/kern_timeout.c =================================================================== --- head/sys/kern/kern_timeout.c +++ head/sys/kern/kern_timeout.c @@ -167,10 +167,8 @@ struct mtx_padalign cc_lock; struct cc_exec cc_exec_entity[2]; struct callout *cc_next; - struct callout *cc_callout; struct callout_list *cc_callwheel; struct callout_tailq cc_expireq; - struct callout_slist cc_callfree; sbintime_t cc_firstevent; sbintime_t cc_lastscan; void *cc_cookie; @@ -210,7 +208,7 @@ #define CC_UNLOCK(cc) mtx_unlock_spin(&(cc)->cc_lock) #define CC_LOCK_ASSERT(cc) mtx_assert(&(cc)->cc_lock, MA_OWNED) -static int __read_mostly timeout_cpu; +static int __read_mostly cc_default_cpu; static void callout_cpu_init(struct callout_cpu *cc, int cpu); static void softclock_call_cc(struct callout *c, struct callout_cpu *cc, @@ -279,6 +277,7 @@ callout_callwheel_init(void *dummy) { struct callout_cpu *cc; + int cpu; /* * Calculate the size of the callout wheel and the preallocated @@ -304,16 +303,14 @@ TUNABLE_INT_FETCH("kern.pin_pcpu_swi", &pin_pcpu_swi); /* - * Only BSP handles timeout(9) and receives a preallocation. - * - * XXX: Once all timeout(9) consumers are converted this can - * be removed. + * Initialize callout wheels. The software interrupt threads + * are created later. */ - timeout_cpu = PCPU_GET(cpuid); - cc = CC_CPU(timeout_cpu); - cc->cc_callout = malloc(ncallout * sizeof(struct callout), - M_CALLOUT, M_WAITOK); - callout_cpu_init(cc, timeout_cpu); + cc_default_cpu = PCPU_GET(cpuid); + CPU_FOREACH(cpu) { + cc = CC_CPU(cpu); + callout_cpu_init(cc, cpu); + } } SYSINIT(callwheel_init, SI_SUB_CPU, SI_ORDER_ANY, callout_callwheel_init, NULL); @@ -323,11 +320,9 @@ static void callout_cpu_init(struct callout_cpu *cc, int cpu) { - struct callout *c; int i; mtx_init(&cc->cc_lock, "callout", NULL, MTX_SPIN | MTX_RECURSE); - SLIST_INIT(&cc->cc_callfree); cc->cc_inited = 1; cc->cc_callwheel = malloc_domainset(sizeof(struct callout_list) * callwheelsize, M_CALLOUT, @@ -342,14 +337,6 @@ snprintf(cc->cc_ktr_event_name, sizeof(cc->cc_ktr_event_name), "callwheel cpu %d", cpu); #endif - if (cc->cc_callout == NULL) /* Only BSP handles timeout(9) */ - return; - for (i = 0; i < ncallout; i++) { - c = &cc->cc_callout[i]; - callout_init(c, 0); - c->c_iflags = CALLOUT_LOCAL_ALLOC; - SLIST_INSERT_HEAD(&cc->cc_callfree, c, c_links.sle); - } } #ifdef SMP @@ -383,50 +370,35 @@ #endif /* - * Start standard softclock thread. + * Start softclock threads. */ static void start_softclock(void *dummy) { struct callout_cpu *cc; char name[MAXCOMLEN]; -#ifdef SMP int cpu; + bool pin_swi; struct intr_event *ie; -#endif - cc = CC_CPU(timeout_cpu); - snprintf(name, sizeof(name), "clock (%d)", timeout_cpu); - if (swi_add(&clk_intr_event, name, softclock, cc, SWI_CLOCK, - INTR_MPSAFE, &cc->cc_cookie)) - panic("died while creating standard software ithreads"); - if (pin_default_swi && - (intr_event_bind(clk_intr_event, timeout_cpu) != 0)) { - printf("%s: timeout clock couldn't be pinned to cpu %d\n", - __func__, - timeout_cpu); - } - -#ifdef SMP CPU_FOREACH(cpu) { - if (cpu == timeout_cpu) - continue; cc = CC_CPU(cpu); - cc->cc_callout = NULL; /* Only BSP handles timeout(9). */ - callout_cpu_init(cc, cpu); snprintf(name, sizeof(name), "clock (%d)", cpu); ie = NULL; if (swi_add(&ie, name, softclock, cc, SWI_CLOCK, INTR_MPSAFE, &cc->cc_cookie)) panic("died while creating standard software ithreads"); - if (pin_pcpu_swi && (intr_event_bind(ie, cpu) != 0)) { - printf("%s: per-cpu clock couldn't be pinned to " - "cpu %d\n", + if (cpu == cc_default_cpu) + pin_swi = pin_default_swi; + else + pin_swi = pin_pcpu_swi; + if (pin_swi && (intr_event_bind(ie, cpu) != 0)) { + printf("%s: %s clock couldn't be pinned to cpu %d\n", __func__, + cpu == cc_default_cpu ? "default" : "per-cpu", cpu); } } -#endif } SYSINIT(start_softclock, SI_SUB_SOFTINTR, SI_ORDER_FIRST, start_softclock, NULL); @@ -639,16 +611,6 @@ } static void -callout_cc_del(struct callout *c, struct callout_cpu *cc) -{ - - if ((c->c_iflags & CALLOUT_LOCAL_ALLOC) == 0) - return; - c->c_func = NULL; - SLIST_INSERT_HEAD(&cc->cc_callfree, c, c_links.sle); -} - -static void softclock_call_cc(struct callout *c, struct callout_cpu *cc, #ifdef CALLOUT_PROFILING int *mpcalls, int *lockcalls, int *gcalls, @@ -692,10 +654,7 @@ c_func = c->c_func; c_arg = c->c_arg; c_iflags = c->c_iflags; - if (c->c_iflags & CALLOUT_LOCAL_ALLOC) - c->c_iflags = CALLOUT_LOCAL_ALLOC; - else - c->c_iflags &= ~CALLOUT_PENDING; + c->c_iflags &= ~CALLOUT_PENDING; cc_exec_curr(cc, direct) = c; cc_exec_last_func(cc, direct) = c_func; @@ -795,8 +754,6 @@ wakeup(&cc_exec_waiting(cc, direct)); CC_LOCK(cc); } else if (cc_cce_migrating(cc, direct)) { - KASSERT((c_iflags & CALLOUT_LOCAL_ALLOC) == 0, - ("Migrating legacy callout %p", c)); #ifdef SMP /* * If the callout was scheduled for @@ -819,7 +776,6 @@ CTR3(KTR_CALLOUT, "deferred cancelled %p func %p arg %p", c, new_func, new_arg); - callout_cc_del(c, cc); return; } c->c_iflags &= ~CALLOUT_DFRMIGRATION; @@ -834,19 +790,6 @@ panic("migration should not happen"); #endif } - /* - * If the current callout is locally allocated (from - * timeout(9)) then put it on the freelist. - * - * Note: we need to check the cached copy of c_iflags because - * if it was not local, then it's not safe to deref the - * callout pointer. - */ - KASSERT((c_iflags & CALLOUT_LOCAL_ALLOC) == 0 || - c->c_iflags == CALLOUT_LOCAL_ALLOC, - ("corrupted callout")); - if (c_iflags & CALLOUT_LOCAL_ALLOC) - callout_cc_del(c, cc); } /* @@ -896,70 +839,7 @@ CC_UNLOCK(cc); } -/* - * timeout -- - * Execute a function after a specified length of time. - * - * untimeout -- - * Cancel previous timeout function call. - * - * callout_handle_init -- - * Initialize a handle so that using it with untimeout is benign. - * - * See AT&T BCI Driver Reference Manual for specification. This - * implementation differs from that one in that although an - * identification value is returned from timeout, the original - * arguments to timeout as well as the identifier are used to - * identify entries for untimeout. - */ -struct callout_handle -timeout(timeout_t *ftn, void *arg, int to_ticks) -{ - struct callout_cpu *cc; - struct callout *new; - struct callout_handle handle; - - cc = CC_CPU(timeout_cpu); - CC_LOCK(cc); - /* Fill in the next free callout structure. */ - new = SLIST_FIRST(&cc->cc_callfree); - if (new == NULL) - /* XXX Attempt to malloc first */ - panic("timeout table full"); - SLIST_REMOVE_HEAD(&cc->cc_callfree, c_links.sle); - callout_reset(new, to_ticks, ftn, arg); - handle.callout = new; - CC_UNLOCK(cc); - - return (handle); -} - void -untimeout(timeout_t *ftn, void *arg, struct callout_handle handle) -{ - struct callout_cpu *cc; - - /* - * Check for a handle that was initialized - * by callout_handle_init, but never used - * for a real timeout. - */ - if (handle.callout == NULL) - return; - - cc = callout_lock(handle.callout); - if (handle.callout->c_func == ftn && handle.callout->c_arg == arg) - callout_stop(handle.callout); - CC_UNLOCK(cc); -} - -void -callout_handle_init(struct callout_handle *handle) -{ - handle->callout = NULL; -} - -void callout_when(sbintime_t sbt, sbintime_t precision, int flags, sbintime_t *res, sbintime_t *prec_res) { @@ -1060,12 +940,9 @@ ("%s: direct callout %p has lock", __func__, c)); cc = callout_lock(c); /* - * Don't allow migration of pre-allocated callouts lest they - * become unbalanced or handle the case where the user does - * not care. + * Don't allow migration if the user does not care. */ - if ((c->c_iflags & CALLOUT_LOCAL_ALLOC) || - ignore_cpu) { + if (ignore_cpu) { cpu = c->c_cpu; } @@ -1435,7 +1312,6 @@ TAILQ_REMOVE(&cc->cc_expireq, c, c_links.tqe); } } - callout_cc_del(c, cc); CC_UNLOCK(cc); return (cancelled); } @@ -1451,7 +1327,7 @@ c->c_lock = &Giant.lock_object; c->c_iflags = 0; } - c->c_cpu = timeout_cpu; + c->c_cpu = cc_default_cpu; } void @@ -1467,7 +1343,7 @@ (LC_SPINLOCK | LC_SLEEPABLE)), ("%s: invalid lock class", __func__)); c->c_iflags = flags & (CALLOUT_RETURNUNLOCKED | CALLOUT_SHAREDLOCK); - c->c_cpu = timeout_cpu; + c->c_cpu = cc_default_cpu; } #ifdef APM_FIXUP_CALLTODO @@ -1559,9 +1435,7 @@ sbintime_t maxpr, maxt, medpr, medt, now, spr, st, t; int ct[64], cpr[64], ccpbk[32]; int error, val, i, count, tcum, pcum, maxc, c, medc; -#ifdef SMP int cpu; -#endif val = 0; error = sysctl_handle_int(oidp, &val, 0, req); @@ -1573,12 +1447,8 @@ bzero(ct, sizeof(ct)); bzero(cpr, sizeof(cpr)); now = sbinuptime(); -#ifdef SMP CPU_FOREACH(cpu) { cc = CC_CPU(cpu); -#else - cc = CC_CPU(timeout_cpu); -#endif CC_LOCK(cc); for (i = 0; i < callwheelsize; i++) { sc = &cc->cc_callwheel[i]; @@ -1603,9 +1473,7 @@ count += c; } CC_UNLOCK(cc); -#ifdef SMP } -#endif for (i = 0, tcum = 0; i < 64 && tcum < count / 2; i++) tcum += ct[i]; Index: head/sys/netgraph/ng_base.c =================================================================== --- head/sys/netgraph/ng_base.c +++ head/sys/netgraph/ng_base.c @@ -3801,7 +3801,7 @@ return (0); } -/* A special modified version of untimeout() */ +/* A special modified version of callout_stop() */ int ng_uncallout(struct callout *c, node_p node) { Index: head/sys/sys/interrupt.h =================================================================== --- head/sys/sys/interrupt.h +++ head/sys/sys/interrupt.h @@ -151,7 +151,6 @@ struct proc; extern struct intr_event *tty_intr_event; -extern struct intr_event *clk_intr_event; extern void *vm_ih; /* Counts and names for statistics (defined in MD code). */ Index: head/sys/sys/param.h =================================================================== --- head/sys/sys/param.h +++ head/sys/sys/param.h @@ -60,7 +60,7 @@ * in the range 5 to 9. */ #undef __FreeBSD_version -#define __FreeBSD_version 1300066 /* Master, propagated to newvers */ +#define __FreeBSD_version 1300067 /* Master, propagated to newvers */ /* * __FreeBSD_kernel__ indicates that this system uses the kernel of FreeBSD, Index: head/sys/sys/systm.h =================================================================== --- head/sys/sys/systm.h +++ head/sys/sys/systm.h @@ -466,15 +466,6 @@ void kern_reboot(int) __dead2; void shutdown_nice(int); -/* Timeouts */ -typedef void timeout_t(void *); /* timeout function type */ -#define CALLOUT_HANDLE_INITIALIZER(handle) \ - { NULL } - -void callout_handle_init(struct callout_handle *); -struct callout_handle timeout(timeout_t *, void *, int); -void untimeout(timeout_t *, void *, struct callout_handle); - /* Stubs for obsolete functions that used to be for interrupt management */ static __inline intrmask_t splbio(void) { return 0; } static __inline intrmask_t splcam(void) { return 0; }