Index: share/man/man9/Makefile =================================================================== --- share/man/man9/Makefile +++ share/man/man9/Makefile @@ -1694,6 +1694,7 @@ timeout.9 callout_active.9 \ timeout.9 callout_deactivate.9 \ timeout.9 callout_drain.9 \ + timeout.9 callout_drain_async.9 \ timeout.9 callout_handle_init.9 \ timeout.9 callout_init.9 \ timeout.9 callout_init_mtx.9 \ Index: share/man/man9/timeout.9 =================================================================== --- share/man/man9/timeout.9 +++ share/man/man9/timeout.9 @@ -36,6 +36,7 @@ .Nm callout_active , .Nm callout_deactivate , .Nm callout_drain , +.Nm callout_drain_async , .Nm callout_handle_init , .Nm callout_init , .Nm callout_init_mtx , @@ -70,6 +71,8 @@ .Fn callout_deactivate "struct callout *c" .Ft int .Fn callout_drain "struct callout *c" +.Ft int +.Fn callout_drain_async "struct callout *c" "callout_func_t *fn" "void *arg" .Ft void .Fn callout_handle_init "struct callout_handle *handle" .Bd -literal @@ -264,6 +267,24 @@ .Fn callout_drain returns. .Pp +The function +.Fn callout_drain_async +is non-blocking and works the same as the +.Fn callout_stop +function. +When this function returns zero, do not call it again until the callback function given by +.Fa fn +has been called with argument +.Fa arg . +Only one of +.Fn callout_drain +or +.Fn callout_drain_async +should be called at a time to drain a callout. +If this function returns non-zero, it is safe to free the callout structure pointed to by the +.Fa c +argument immediately. +.Pp The .Fn callout_reset and Index: sys/kern/kern_timeout.c =================================================================== --- sys/kern/kern_timeout.c +++ sys/kern/kern_timeout.c @@ -136,6 +136,8 @@ */ struct cc_exec { struct callout *cc_curr; + callout_func_t *cc_drain_fn; + void *cc_drain_arg; #ifdef SMP void (*ce_migration_func)(void *); void *ce_migration_arg; @@ -170,6 +172,8 @@ #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_fn(cc, dir) cc->cc_exec_entity[dir].cc_drain_fn +#define cc_exec_drain_arg(cc, dir) cc->cc_exec_entity[dir].cc_drain_arg #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 +683,7 @@ cc_exec_curr(cc, direct) = c; cc_exec_cancel(cc, direct) = false; + cc_exec_drain_fn(cc, direct) = NULL; CC_UNLOCK(cc); if (c_lock != NULL) { class->lc_lock(c_lock, lock_status); @@ -744,6 +749,11 @@ CC_LOCK(cc); KASSERT(cc_exec_curr(cc, direct) == c, ("mishandled cc_curr")); cc_exec_curr(cc, direct) = NULL; + if (cc_exec_drain_fn(cc, direct) != NULL) { + CC_UNLOCK(cc); + cc_exec_drain_fn(cc, direct)(cc_exec_drain_arg(cc, direct)); + CC_LOCK(cc); + } if (cc_exec_waiting(cc, direct)) { /* * There is someone waiting for the @@ -1145,6 +1155,41 @@ } int +callout_drain_async(struct callout *c, callout_func_t *func, void *arg) +{ + struct callout_cpu *cc; + int retval; + int direct; + + /* + * NOTE: The return value of "callout_stop()" cannot be used + * in this context, because it does not reliably tell if the + * callout is currently executing or not. + */ + + /* stop callout */ + (void) callout_stop(c); + + /* check if callback is being called */ + cc = callout_lock(c); + if (c->c_iflags & CALLOUT_DIRECT) { + direct = 1; + } else { + direct = 0; + } + retval = (cc_exec_curr(cc, direct) == c); + + if (retval) { + /* install drain callback function and argument */ + cc_exec_drain_fn(cc, direct) = func; + cc_exec_drain_arg(cc, direct) = arg; + } + CC_UNLOCK(cc); + + return (!retval); +} + +int _callout_stop_safe(struct callout *c, int safe) { struct callout_cpu *cc, *old_cc; Index: sys/sys/_callout.h =================================================================== --- sys/sys/_callout.h +++ sys/sys/_callout.h @@ -46,6 +46,8 @@ SLIST_HEAD(callout_slist, callout); TAILQ_HEAD(callout_tailq, callout); +typedef void callout_func_t(void *); + struct callout { union { LIST_ENTRY(callout) le; Index: sys/sys/callout.h =================================================================== --- sys/sys/callout.h +++ sys/sys/callout.h @@ -82,6 +82,7 @@ #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) +int callout_drain_async(struct callout *, callout_func_t *, void *); void callout_init(struct callout *, int); void _callout_init_lock(struct callout *, struct lock_object *, int); #define callout_init_mtx(c, mtx, flags) \