Skip to content

Commit 06efa5f

Browse files
author
Kent Overstreet
committed
closures: closure_get_not_zero(), closure_return_sync()
Provide new primitives for solving a lifetime issue with bcachefs btree_trans objects. closure_sync_return(): like closure_sync(), wait synchronously for any outstanding gets. like closure_return, the closure is considered "finished" and the ref left at 0. closure_get_not_zero(): get a ref on a closure if it's alive, i.e. the ref is not zero. Signed-off-by: Kent Overstreet <[email protected]>
1 parent 18e9284 commit 06efa5f

File tree

2 files changed

+69
-6
lines changed

2 files changed

+69
-6
lines changed

include/linux/closure.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,21 @@ static inline void closure_get(struct closure *cl)
284284
#endif
285285
}
286286

287+
/**
288+
* closure_get_not_zero
289+
*/
290+
static inline bool closure_get_not_zero(struct closure *cl)
291+
{
292+
unsigned old = atomic_read(&cl->remaining);
293+
do {
294+
if (!(old & CLOSURE_REMAINING_MASK))
295+
return false;
296+
297+
} while (!atomic_try_cmpxchg_acquire(&cl->remaining, &old, old + 1));
298+
299+
return true;
300+
}
301+
287302
/**
288303
* closure_init - Initialize a closure, setting the refcount to 1
289304
* @cl: closure to initialize
@@ -310,6 +325,12 @@ static inline void closure_init_stack(struct closure *cl)
310325
atomic_set(&cl->remaining, CLOSURE_REMAINING_INITIALIZER);
311326
}
312327

328+
static inline void closure_init_stack_release(struct closure *cl)
329+
{
330+
memset(cl, 0, sizeof(struct closure));
331+
atomic_set_release(&cl->remaining, CLOSURE_REMAINING_INITIALIZER);
332+
}
333+
313334
/**
314335
* closure_wake_up - wake up all closures on a wait list,
315336
* with memory barrier
@@ -355,6 +376,8 @@ do { \
355376
*/
356377
#define closure_return(_cl) continue_at((_cl), NULL, NULL)
357378

379+
void closure_return_sync(struct closure *cl);
380+
358381
/**
359382
* continue_at_nobarrier - jump to another function without barrier
360383
*

lib/closure.c

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
#include <linux/seq_file.h>
1414
#include <linux/sched/debug.h>
1515

16-
static inline void closure_put_after_sub(struct closure *cl, int flags)
16+
static inline void closure_put_after_sub_checks(int flags)
1717
{
1818
int r = flags & CLOSURE_REMAINING_MASK;
1919

@@ -22,12 +22,17 @@ static inline void closure_put_after_sub(struct closure *cl, int flags)
2222
flags & CLOSURE_GUARD_MASK, (unsigned) __fls(r)))
2323
r &= ~CLOSURE_GUARD_MASK;
2424

25-
if (!r) {
26-
smp_acquire__after_ctrl_dep();
25+
WARN(!r && (flags & ~CLOSURE_DESTRUCTOR),
26+
"closure ref hit 0 with incorrect flags set: %x (%u)",
27+
flags & ~CLOSURE_DESTRUCTOR, (unsigned) __fls(flags));
28+
}
29+
30+
static inline void closure_put_after_sub(struct closure *cl, int flags)
31+
{
32+
closure_put_after_sub_checks(flags);
2733

28-
WARN(flags & ~CLOSURE_DESTRUCTOR,
29-
"closure ref hit 0 with incorrect flags set: %x (%u)",
30-
flags & ~CLOSURE_DESTRUCTOR, (unsigned) __fls(flags));
34+
if (!(flags & CLOSURE_REMAINING_MASK)) {
35+
smp_acquire__after_ctrl_dep();
3136

3237
cl->closure_get_happened = false;
3338

@@ -145,6 +150,41 @@ void __sched __closure_sync(struct closure *cl)
145150
}
146151
EXPORT_SYMBOL(__closure_sync);
147152

153+
/*
154+
* closure_return_sync - finish running a closure, synchronously (i.e. waiting
155+
* for outstanding get()s to finish) and returning once closure refcount is 0.
156+
*
157+
* Unlike closure_sync() this doesn't reinit the ref to 1; subsequent
158+
* closure_get_not_zero() calls waill fail.
159+
*/
160+
void __sched closure_return_sync(struct closure *cl)
161+
{
162+
struct closure_syncer s = { .task = current };
163+
164+
cl->s = &s;
165+
set_closure_fn(cl, closure_sync_fn, NULL);
166+
167+
unsigned flags = atomic_sub_return_release(1 + CLOSURE_RUNNING - CLOSURE_DESTRUCTOR,
168+
&cl->remaining);
169+
170+
closure_put_after_sub_checks(flags);
171+
172+
if (unlikely(flags & CLOSURE_REMAINING_MASK)) {
173+
while (1) {
174+
set_current_state(TASK_UNINTERRUPTIBLE);
175+
if (s.done)
176+
break;
177+
schedule();
178+
}
179+
180+
__set_current_state(TASK_RUNNING);
181+
}
182+
183+
if (cl->parent)
184+
closure_put(cl->parent);
185+
}
186+
EXPORT_SYMBOL(closure_return_sync);
187+
148188
int __sched __closure_sync_timeout(struct closure *cl, unsigned long timeout)
149189
{
150190
struct closure_syncer s = { .task = current };

0 commit comments

Comments
 (0)