diff options
author | Kent Overstreet <kent.overstreet@linux.dev> | 2023-10-24 21:46:58 +0300 |
---|---|---|
committer | Kent Overstreet <kent.overstreet@linux.dev> | 2023-10-31 04:48:22 +0300 |
commit | ee526b88caaa4b4182144bf2576af2c3b1e9c759 (patch) | |
tree | d25c1a6b69d86c8a7ed518334dbcc13a842b37e4 /include/linux/closure.h | |
parent | 2bce6368c46b835a133f7f4946eea9c4513828dd (diff) | |
download | linux-ee526b88caaa4b4182144bf2576af2c3b1e9c759.tar.xz |
closures: Fix race in closure_sync()
As pointed out by Linus, closure_sync() was racy; we could skip blocking
immediately after a get() and a put(), but then that would skip any
barrier corresponding to the other thread's put() barrier.
To fix this, always do the full __closure_sync() sequence whenever any
get() has happened and the closure might have been used by other
threads.
Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
Diffstat (limited to 'include/linux/closure.h')
-rw-r--r-- | include/linux/closure.h | 10 |
1 files changed, 9 insertions, 1 deletions
diff --git a/include/linux/closure.h b/include/linux/closure.h index bdab17050bc8..de7bb47d8a46 100644 --- a/include/linux/closure.h +++ b/include/linux/closure.h @@ -154,6 +154,7 @@ struct closure { struct closure *parent; atomic_t remaining; + bool closure_get_happened; #ifdef CONFIG_DEBUG_CLOSURES #define CLOSURE_MAGIC_DEAD 0xc054dead @@ -185,7 +186,11 @@ static inline unsigned closure_nr_remaining(struct closure *cl) */ static inline void closure_sync(struct closure *cl) { - if (closure_nr_remaining(cl) != 1) +#ifdef CONFIG_DEBUG_CLOSURES + BUG_ON(closure_nr_remaining(cl) != 1 && !cl->closure_get_happened); +#endif + + if (cl->closure_get_happened) __closure_sync(cl); } @@ -257,6 +262,8 @@ static inline void closure_queue(struct closure *cl) */ static inline void closure_get(struct closure *cl) { + cl->closure_get_happened = true; + #ifdef CONFIG_DEBUG_CLOSURES BUG_ON((atomic_inc_return(&cl->remaining) & CLOSURE_REMAINING_MASK) <= 1); @@ -279,6 +286,7 @@ static inline void closure_init(struct closure *cl, struct closure *parent) closure_get(parent); atomic_set(&cl->remaining, CLOSURE_REMAINING_INITIALIZER); + cl->closure_get_happened = false; closure_debug_create(cl); closure_set_ip(cl); |