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,6 +945,56 @@ 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)
{
sbintime_t to_sbt, to_pr;
if ((flags & (C_ABSOLUTE | C_PRECALC)) != 0) {
*res = sbt;
*prec_res = precision;
return;
}
if ((flags & C_HARDCLOCK) != 0 && sbt < tick_sbt)
sbt = tick_sbt;
if ((flags & C_HARDCLOCK) != 0 ||
#ifdef NO_EVENTTIMERS
sbt >= sbt_timethreshold) {
to_sbt = getsbinuptime();
/* Add safety belt for the case of hz > 1000. */
to_sbt += tc_tick_sbt - tick_sbt;
#else
sbt >= sbt_tickthreshold) {
/*
* Obtain the time of the last hardclock() call on
* this CPU directly from the kern_clocksource.c.
* This value is per-CPU, but it is equal for all
* active ones.
*/
#ifdef __LP64__
to_sbt = DPCPU_GET(hardclocktime);
#else
spinlock_enter();
to_sbt = DPCPU_GET(hardclocktime);
spinlock_exit();
#endif
#endif
if ((flags & C_HARDCLOCK) == 0)
to_sbt += tick_sbt;
} else
to_sbt = sbinuptime();
if (SBT_MAX - to_sbt < sbt)
to_sbt = SBT_MAX;
else
to_sbt += sbt;
*res = to_sbt;
to_pr = ((C_PRELGET(flags) < 0) ? sbt >> tc_precexp :
sbt >> C_PRELGET(flags));
*prec_res = to_pr > precision ? to_pr : precision;
}
/*
* New interface; clients allocate their own callout structures.
*
@ -962,10 +1012,10 @@ callout_handle_init(struct callout_handle *handle)
* callout_deactivate() - marks the callout as having been serviced
*/
int
callout_reset_sbt_on(struct callout *c, sbintime_t sbt, sbintime_t precision,
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, pr;
sbintime_t to_sbt, precision;
struct callout_cpu *cc;
int cancelled, direct;
int ignore_cpu=0;
@ -978,47 +1028,8 @@ callout_reset_sbt_on(struct callout *c, sbintime_t sbt, sbintime_t precision,
/* Invalid CPU spec */
panic("Invalid CPU in callout %d", cpu);
}
if (flags & C_ABSOLUTE) {
to_sbt = sbt;
} else {
if ((flags & C_HARDCLOCK) && (sbt < tick_sbt))
sbt = tick_sbt;
if ((flags & C_HARDCLOCK) ||
#ifdef NO_EVENTTIMERS
sbt >= sbt_timethreshold) {
to_sbt = getsbinuptime();
callout_when(sbt, prec, flags, &to_sbt, &precision);
/* Add safety belt for the case of hz > 1000. */
to_sbt += tc_tick_sbt - tick_sbt;
#else
sbt >= sbt_tickthreshold) {
/*
* Obtain the time of the last hardclock() call on
* this CPU directly from the kern_clocksource.c.
* This value is per-CPU, but it is equal for all
* active ones.
*/
#ifdef __LP64__
to_sbt = DPCPU_GET(hardclocktime);
#else
spinlock_enter();
to_sbt = DPCPU_GET(hardclocktime);
spinlock_exit();
#endif
#endif
if ((flags & C_HARDCLOCK) == 0)
to_sbt += tick_sbt;
} else
to_sbt = sbinuptime();
if (SBT_MAX - to_sbt < sbt)
to_sbt = SBT_MAX;
else
to_sbt += sbt;
pr = ((C_PRELGET(flags) < 0) ? sbt >> tc_precexp :
sbt >> C_PRELGET(flags));
if (pr > precision)
precision = pr;
}
/*
* 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_ */