aboutsummaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorJulien Charbon <jch@FreeBSD.org>2015-08-18 10:15:09 +0000
committerJulien Charbon <jch@FreeBSD.org>2015-08-18 10:15:09 +0000
commita1e6f8ff2772f9166da566708f5f21748f61146c (patch)
treec94c3946bce57fe6354c1f69864c130d703c370d /sys
parent5a060174cd66576b36eba503e131862949ad2085 (diff)
downloadsrc-a1e6f8ff2772f9166da566708f5f21748f61146c.tar.gz
src-a1e6f8ff2772f9166da566708f5f21748f61146c.zip
callout_stop() should return 0 (fail) when the callout is currently
being serviced and indeed unstoppable. A scenario to reproduce this case is: - the callout is being serviced and at same time, - callout_reset() is called on this callout that sets the CALLOUT_PENDING flag and at same time, - callout_stop() is called on this callout and returns 1 (success) even if the callout is indeed currently running and unstoppable. This issue was caught up while making r284245 (D2763) workaround, and was discussed at BSDCan 2015. Once applied the r284245 workaround is not needed anymore and will be reverted. Differential Revision: https://reviews.freebsd.org/D3078 Reviewed by: jhb Sponsored by: Verisign, Inc.
Notes
Notes: svn path=/head/; revision=286880
Diffstat (limited to 'sys')
-rw-r--r--sys/kern/kern_timeout.c11
1 files changed, 9 insertions, 2 deletions
diff --git a/sys/kern/kern_timeout.c b/sys/kern/kern_timeout.c
index 01da596643d3..21f478982125 100644
--- a/sys/kern/kern_timeout.c
+++ b/sys/kern/kern_timeout.c
@@ -1150,7 +1150,7 @@ _callout_stop_safe(struct callout *c, int safe)
struct callout_cpu *cc, *old_cc;
struct lock_class *class;
int direct, sq_locked, use_lock;
- int not_on_a_list;
+ int not_on_a_list, not_running;
if (safe)
WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, c->c_lock,
@@ -1378,8 +1378,15 @@ again:
}
}
callout_cc_del(c, cc);
+
+ /*
+ * If we are asked to stop a callout which is currently in progress
+ * and indeed impossible to stop then return 0.
+ */
+ not_running = !(cc_exec_curr(cc, direct) == c);
+
CC_UNLOCK(cc);
- return (1);
+ return (not_running);
}
void