Add new async_drain to the callout system. This is so-far not used but
should be used by TCP for sure in its cleanup of the IN-PCB (will be coming shortly). Sponsored by: Netflix Inc. Differential Revision: https://reviews.freebsd.org/D4076
This commit is contained in:
parent
f52e50aef4
commit
18b4fd62e0
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=290664
@ -35,6 +35,7 @@
|
||||
.Sh NAME
|
||||
.Nm callout_active ,
|
||||
.Nm callout_deactivate ,
|
||||
.Nm callout_async_drain ,
|
||||
.Nm callout_drain ,
|
||||
.Nm callout_handle_init ,
|
||||
.Nm callout_init ,
|
||||
@ -69,6 +70,8 @@ typedef void timeout_t (void *);
|
||||
.Ft void
|
||||
.Fn callout_deactivate "struct callout *c"
|
||||
.Ft int
|
||||
.Fn callout_async_drain "struct callout *c" "timeout_t *drain"
|
||||
.Ft int
|
||||
.Fn callout_drain "struct callout *c"
|
||||
.Ft void
|
||||
.Fn callout_handle_init "struct callout_handle *handle"
|
||||
@ -236,17 +239,42 @@ The function
|
||||
cancels a callout
|
||||
.Fa c
|
||||
if it is currently pending.
|
||||
If the callout is pending, then
|
||||
If the callout is pending and successfuly stopped, then
|
||||
.Fn callout_stop
|
||||
returns a non-zero value.
|
||||
If the callout is not set,
|
||||
has already been serviced,
|
||||
or is currently being serviced,
|
||||
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 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.
|
||||
p
|
||||
.Pp
|
||||
The function
|
||||
.Fn callout_drain
|
||||
is identical to
|
||||
.Fn callout_stop
|
||||
|
@ -136,6 +136,7 @@ u_int callwheelsize, callwheelmask;
|
||||
*/
|
||||
struct cc_exec {
|
||||
struct callout *cc_curr;
|
||||
void (*cc_drain)(void *);
|
||||
#ifdef SMP
|
||||
void (*ce_migration_func)(void *);
|
||||
void *ce_migration_arg;
|
||||
@ -170,6 +171,7 @@ struct callout_cpu {
|
||||
#define callout_migrating(c) ((c)->c_iflags & CALLOUT_DFRMIGRATION)
|
||||
|
||||
#define cc_exec_curr(cc, dir) cc->cc_exec_entity[dir].cc_curr
|
||||
#define cc_exec_drain(cc, dir) cc->cc_exec_entity[dir].cc_drain
|
||||
#define cc_exec_next(cc) cc->cc_next
|
||||
#define cc_exec_cancel(cc, dir) cc->cc_exec_entity[dir].cc_cancel
|
||||
#define cc_exec_waiting(cc, dir) cc->cc_exec_entity[dir].cc_waiting
|
||||
@ -679,6 +681,7 @@ softclock_call_cc(struct callout *c, struct callout_cpu *cc,
|
||||
|
||||
cc_exec_curr(cc, direct) = c;
|
||||
cc_exec_cancel(cc, direct) = false;
|
||||
cc_exec_drain(cc, direct) = NULL;
|
||||
CC_UNLOCK(cc);
|
||||
if (c_lock != NULL) {
|
||||
class->lc_lock(c_lock, lock_status);
|
||||
@ -744,6 +747,15 @@ softclock_call_cc(struct callout *c, struct callout_cpu *cc,
|
||||
CC_LOCK(cc);
|
||||
KASSERT(cc_exec_curr(cc, direct) == c, ("mishandled cc_curr"));
|
||||
cc_exec_curr(cc, direct) = NULL;
|
||||
if (cc_exec_drain(cc, direct)) {
|
||||
void (*drain)(void *);
|
||||
|
||||
drain = cc_exec_drain(cc, direct);
|
||||
cc_exec_drain(cc, direct) = NULL;
|
||||
CC_UNLOCK(cc);
|
||||
drain(c_arg);
|
||||
CC_LOCK(cc);
|
||||
}
|
||||
if (cc_exec_waiting(cc, direct)) {
|
||||
/*
|
||||
* There is someone waiting for the
|
||||
@ -1145,7 +1157,7 @@ callout_schedule(struct callout *c, int to_ticks)
|
||||
}
|
||||
|
||||
int
|
||||
_callout_stop_safe(struct callout *c, int safe)
|
||||
_callout_stop_safe(struct callout *c, int safe, void (*drain)(void *))
|
||||
{
|
||||
struct callout_cpu *cc, *old_cc;
|
||||
struct lock_class *class;
|
||||
@ -1225,19 +1237,22 @@ _callout_stop_safe(struct callout *c, int safe)
|
||||
* stop it by other means however.
|
||||
*/
|
||||
if (!(c->c_iflags & CALLOUT_PENDING)) {
|
||||
c->c_flags &= ~CALLOUT_ACTIVE;
|
||||
|
||||
/*
|
||||
* If it wasn't on the queue and it isn't the current
|
||||
* callout, then we can't stop it, so just bail.
|
||||
* It probably has already been run (if locking
|
||||
* is properly done). You could get here if the caller
|
||||
* calls stop twice in a row for example. The second
|
||||
* call would fall here without CALLOUT_ACTIVE set.
|
||||
*/
|
||||
c->c_flags &= ~CALLOUT_ACTIVE;
|
||||
if (cc_exec_curr(cc, direct) != c) {
|
||||
CTR3(KTR_CALLOUT, "failed to stop %p func %p arg %p",
|
||||
c, c->c_func, c->c_arg);
|
||||
CC_UNLOCK(cc);
|
||||
if (sq_locked)
|
||||
sleepq_release(&cc_exec_waiting(cc, direct));
|
||||
return (0);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (safe) {
|
||||
@ -1298,14 +1313,16 @@ _callout_stop_safe(struct callout *c, int safe)
|
||||
CC_LOCK(cc);
|
||||
}
|
||||
} else if (use_lock &&
|
||||
!cc_exec_cancel(cc, direct)) {
|
||||
!cc_exec_cancel(cc, direct) && (drain == NULL)) {
|
||||
|
||||
/*
|
||||
* The current callout is waiting for its
|
||||
* lock which we hold. Cancel the callout
|
||||
* and return. After our caller drops the
|
||||
* lock, the callout will be skipped in
|
||||
* softclock().
|
||||
* softclock(). This *only* works with a
|
||||
* callout_stop() *not* callout_drain() or
|
||||
* callout_async_drain().
|
||||
*/
|
||||
cc_exec_cancel(cc, direct) = true;
|
||||
CTR3(KTR_CALLOUT, "cancelled %p func %p arg %p",
|
||||
@ -1351,11 +1368,17 @@ _callout_stop_safe(struct callout *c, int safe)
|
||||
#endif
|
||||
CTR3(KTR_CALLOUT, "postponing stop %p func %p arg %p",
|
||||
c, c->c_func, c->c_arg);
|
||||
if (drain) {
|
||||
cc_exec_drain(cc, direct) = drain;
|
||||
}
|
||||
CC_UNLOCK(cc);
|
||||
return (0);
|
||||
}
|
||||
CTR3(KTR_CALLOUT, "failed to stop %p func %p arg %p",
|
||||
c, c->c_func, c->c_arg);
|
||||
if (drain) {
|
||||
cc_exec_drain(cc, direct) = drain;
|
||||
}
|
||||
CC_UNLOCK(cc);
|
||||
KASSERT(!sq_locked, ("sleepqueue chain still locked"));
|
||||
return (0);
|
||||
|
@ -81,7 +81,7 @@ struct callout_handle {
|
||||
*/
|
||||
#define callout_active(c) ((c)->c_flags & CALLOUT_ACTIVE)
|
||||
#define callout_deactivate(c) ((c)->c_flags &= ~CALLOUT_ACTIVE)
|
||||
#define callout_drain(c) _callout_stop_safe(c, 1)
|
||||
#define callout_drain(c) _callout_stop_safe(c, 1, NULL)
|
||||
void callout_init(struct callout *, int);
|
||||
void _callout_init_lock(struct callout *, struct lock_object *, int);
|
||||
#define callout_init_mtx(c, mtx, flags) \
|
||||
@ -119,10 +119,11 @@ int callout_schedule(struct callout *, int);
|
||||
int callout_schedule_on(struct callout *, int, int);
|
||||
#define callout_schedule_curcpu(c, on_tick) \
|
||||
callout_schedule_on((c), (on_tick), PCPU_GET(cpuid))
|
||||
#define callout_stop(c) _callout_stop_safe(c, 0)
|
||||
int _callout_stop_safe(struct callout *, int);
|
||||
#define callout_stop(c) _callout_stop_safe(c, 0, NULL)
|
||||
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)
|
||||
#endif
|
||||
|
||||
#endif /* _SYS_CALLOUT_H_ */
|
||||
|
Loading…
Reference in New Issue
Block a user