4730a8972b
Implement lock_spin()/unlock_spin() lock class methods, moving the assertion to _sleep() instead. Change assertions in callout(9) to allow spin locks for both regular and C_DIRECT_EXEC cases. In case of C_DIRECT_EXEC callouts spin locks are the only locks allowed actually. As the first use case allow taskqueue_enqueue_timeout() use on fast task queues. It actually becomes more efficient due to avoided extra context switches in callout(9) thanks to C_DIRECT_EXEC. MFC after: 2 weeks Reviewed by: hselasky Differential Revision: https://reviews.freebsd.org/D31778
914 lines
24 KiB
Groff
914 lines
24 KiB
Groff
.\" $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 September 1, 2021
|
|
.Dt CALLOUT 9
|
|
.Os
|
|
.Sh NAME
|
|
.Nm callout_active ,
|
|
.Nm callout_deactivate ,
|
|
.Nm callout_async_drain ,
|
|
.Nm callout_drain ,
|
|
.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_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
|
|
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 .
|