Extract the calculation of the callout fire time into the new function

callout_when(9).  See the man page update for the description of the
intended use.

Tested by:	pho
Reviewed by:	jhb, bjk (man page updates)
Sponsored by:	The FreeBSD Foundation
MFC after:	1 month
X-Differential revision:	https://reviews.freebsd.org/D7137
This commit is contained in:
Konstantin Belousov 2016-07-28 08:57:01 +00:00
parent ad90086e85
commit a9e182e895
4 changed files with 177 additions and 59 deletions

View File

@ -1766,6 +1766,7 @@ MLINKS+=timeout.9 callout.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 cred_update_thread.9 \
ucred.9 crcopy.9 \

View File

@ -29,7 +29,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd July 4, 2016
.Dd July 27, 2016
.Dt TIMEOUT 9
.Os
.Sh NAME
@ -56,6 +56,7 @@
.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
@ -91,20 +92,48 @@ struct callout_handle handle = CALLOUT_HANDLE_INITIALIZER(&handle);
.Ft int
.Fn callout_reset "struct callout *c" "int ticks" "timeout_t *func" "void *arg"
.Ft int
.Fn callout_reset_curcpu "struct callout *c" "int ticks" "timeout_t *func" \
"void *arg"
.Fo callout_reset_curcpu
.Fa "struct callout *c"
.Fa "int ticks"
.Fa "timeout_t *func"
.Fa "void *arg"
.Fc
.Ft int
.Fn callout_reset_on "struct callout *c" "int ticks" "timeout_t *func" \
"void *arg" "int cpu"
.Fo callout_reset_on
.Fa "struct callout *c"
.Fa "int ticks"
.Fa "timeout_t *func"
.Fa "void *arg"
.Fa "int cpu"
.Fc
.Ft int
.Fn callout_reset_sbt "struct callout *c" "sbintime_t sbt" \
"sbintime_t pr" "timeout_t *func" "void *arg" "int flags"
.Fo callout_reset_sbt
.Fa "struct callout *c"
.Fa "sbintime_t sbt"
.Fa "sbintime_t pr"
.Fa "timeout_t *func"
.Fa "void *arg"
.Fa "int flags"
.Fc
.Ft int
.Fn callout_reset_sbt_curcpu "struct callout *c" "sbintime_t sbt" \
"sbintime_t pr" "timeout_t *func" "void *arg" "int flags"
.Fo callout_reset_sbt_curcpu
.Fa "struct callout *c"
.Fa "sbintime_t sbt"
.Fa "sbintime_t pr"
.Fa "timeout_t *func"
.Fa "void *arg"
.Fa "int flags"
.Fc
.Ft int
.Fn callout_reset_sbt_on "struct callout *c" "sbintime_t sbt" \
"sbintime_t pr" "timeout_t *func" "void *arg" "int cpu" "int flags"
.Fo callout_reset_sbt_on
.Fa "struct callout *c"
.Fa "sbintime_t sbt"
.Fa "sbintime_t pr"
.Fa "timeout_t *func"
.Fa "void *arg"
.Fa "int cpu"
.Fa "int flags"
.Fc
.Ft int
.Fn callout_schedule "struct callout *c" "int ticks"
.Ft int
@ -112,16 +141,37 @@ struct callout_handle handle = CALLOUT_HANDLE_INITIALIZER(&handle);
.Ft int
.Fn callout_schedule_on "struct callout *c" "int ticks" "int cpu"
.Ft int
.Fn callout_schedule_sbt "struct callout *c" "sbintime_t sbt" \
"sbintime_t pr" "int flags"
.Fo callout_schedule_sbt
.Fa "struct callout *c"
.Fa "sbintime_t sbt"
.Fa "sbintime_t pr"
.Fa "int flags"
.Fc
.Ft int
.Fn callout_schedule_sbt_curcpu "struct callout *c" "sbintime_t sbt" \
"sbintime_t pr" "int flags"
.Fo callout_schedule_sbt_curcpu
.Fa "struct callout *c"
.Fa "sbintime_t sbt"
.Fa "sbintime_t pr"
.Fa "int flags"
.Fc
.Ft int
.Fn callout_schedule_sbt_on "struct callout *c" "sbintime_t sbt" \
"sbintime_t pr" "int cpu" "int flags"
.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
@ -387,6 +437,26 @@ 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
@ -503,6 +573,39 @@ 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.

View File

@ -945,45 +945,20 @@ callout_handle_init(struct callout_handle *handle)
handle->callout = NULL;
}
/*
* New interface; clients allocate their own callout structures.
*
* callout_reset() - establish or change a timeout
* callout_stop() - disestablish a timeout
* callout_init() - initialize a callout structure so that it can
* safely be passed to callout_reset() and callout_stop()
*
* <sys/callout.h> defines three convenience macros:
*
* callout_active() - returns truth if callout has not been stopped,
* drained, or deactivated since the last time the callout was
* reset.
* callout_pending() - returns truth if callout is still waiting for timeout
* callout_deactivate() - marks the callout as having been serviced
*/
int
callout_reset_sbt_on(struct callout *c, sbintime_t sbt, sbintime_t precision,
void (*ftn)(void *), void *arg, int cpu, int flags)
void
callout_when(sbintime_t sbt, sbintime_t precision, int flags,
sbintime_t *res, sbintime_t *prec_res)
{
sbintime_t to_sbt, pr;
struct callout_cpu *cc;
int cancelled, direct;
int ignore_cpu=0;
sbintime_t to_sbt, to_pr;
cancelled = 0;
if (cpu == -1) {
ignore_cpu = 1;
} else if ((cpu >= MAXCPU) ||
((CC_CPU(cpu))->cc_inited == 0)) {
/* Invalid CPU spec */
panic("Invalid CPU in callout %d", cpu);
if ((flags & (C_ABSOLUTE | C_PRECALC)) != 0) {
*res = sbt;
*prec_res = precision;
return;
}
if (flags & C_ABSOLUTE) {
to_sbt = sbt;
} else {
if ((flags & C_HARDCLOCK) && (sbt < tick_sbt))
if ((flags & C_HARDCLOCK) != 0 && sbt < tick_sbt)
sbt = tick_sbt;
if ((flags & C_HARDCLOCK) ||
if ((flags & C_HARDCLOCK) != 0 ||
#ifdef NO_EVENTTIMERS
sbt >= sbt_timethreshold) {
to_sbt = getsbinuptime();
@ -1014,11 +989,47 @@ callout_reset_sbt_on(struct callout *c, sbintime_t sbt, sbintime_t precision,
to_sbt = SBT_MAX;
else
to_sbt += sbt;
pr = ((C_PRELGET(flags) < 0) ? sbt >> tc_precexp :
*res = to_sbt;
to_pr = ((C_PRELGET(flags) < 0) ? sbt >> tc_precexp :
sbt >> C_PRELGET(flags));
if (pr > precision)
precision = pr;
*prec_res = to_pr > precision ? to_pr : precision;
}
/*
* New interface; clients allocate their own callout structures.
*
* callout_reset() - establish or change a timeout
* callout_stop() - disestablish a timeout
* callout_init() - initialize a callout structure so that it can
* safely be passed to callout_reset() and callout_stop()
*
* <sys/callout.h> defines three convenience macros:
*
* callout_active() - returns truth if callout has not been stopped,
* drained, or deactivated since the last time the callout was
* reset.
* callout_pending() - returns truth if callout is still waiting for timeout
* callout_deactivate() - marks the callout as having been serviced
*/
int
callout_reset_sbt_on(struct callout *c, sbintime_t sbt, sbintime_t prec,
void (*ftn)(void *), void *arg, int cpu, int flags)
{
sbintime_t to_sbt, precision;
struct callout_cpu *cc;
int cancelled, direct;
int ignore_cpu=0;
cancelled = 0;
if (cpu == -1) {
ignore_cpu = 1;
} else if ((cpu >= MAXCPU) ||
((CC_CPU(cpu))->cc_inited == 0)) {
/* Invalid CPU spec */
panic("Invalid CPU in callout %d", cpu);
}
callout_when(sbt, prec, flags, &to_sbt, &precision);
/*
* This flag used to be added by callout_cc_add, but the
* first time you call this we could end up with the

View File

@ -57,6 +57,7 @@
#define C_PRELGET(x) (int)((((x) >> 1) & C_PRELRANGE) - 1)
#define C_HARDCLOCK 0x0100 /* align to hardclock() calls */
#define C_ABSOLUTE 0x0200 /* event time is absolute. */
#define C_PRECALC 0x0400 /* event time is pre-calculated. */
struct callout_handle {
struct callout *callout;
@ -129,6 +130,8 @@ int _callout_stop_safe(struct callout *, int, void (*)(void *));
void callout_process(sbintime_t now);
#define callout_async_drain(c, d) \
_callout_stop_safe(c, 0, d)
void callout_when(sbintime_t sbt, sbintime_t precision, int flags,
sbintime_t *sbt_res, sbintime_t *prec_res);
#endif
#endif /* _SYS_CALLOUT_H_ */