Page MenuHomeFreeBSD

D4076.id9992.diff
No OneTemporary

D4076.id9992.diff

Index: share/man/man9/timeout.9
===================================================================
--- share/man/man9/timeout.9
+++ share/man/man9/timeout.9
@@ -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 ,
@@ -247,6 +248,25 @@
then that lock must be held when this function is called.
.Pp
The function
+.Fn callout_async_drain
+is identical to
+.Fn callout_stop
+except that it will only return zero if the
+callout cannot be stopped. 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. This function MUST NOT be called on a
+stopped timeout. This function MUST NOT also be called while holding any
+locks on which the callout might block, or deadlock will result. 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.
+.Pp
+The function
.Fn callout_drain
is identical to
.Fn callout_stop
Index: sys/kern/kern_timeout.c
===================================================================
--- sys/kern/kern_timeout.c
+++ sys/kern/kern_timeout.c
@@ -136,6 +136,7 @@
*/
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 @@
#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 @@
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 @@
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 @@
}
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,15 +1237,20 @@
* 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);
+ KASSERT(drain == NULL,
+ ("async drain not allowed on stopped/expired timer"));
CC_UNLOCK(cc);
if (sq_locked)
sleepq_release(&cc_exec_waiting(cc, direct));
@@ -1298,7 +1315,7 @@
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
@@ -1305,7 +1322,9 @@
* 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 +1370,17 @@
#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);
Index: sys/sys/callout.h
===================================================================
--- sys/sys/callout.h
+++ sys/sys/callout.h
@@ -81,7 +81,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)
+#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_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_ */

File Metadata

Mime Type
text/plain
Expires
Sat, Nov 22, 8:40 PM (5 h, 19 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
25952533
Default Alt Text
D4076.id9992.diff (5 KB)

Event Timeline