closures: closure_sync_timeout()

Add a new variant of closure_sync_timeout() that takes a timeout.

Note that when this returns -ETIME the closure will still be waiting on
something, i.e. it's not safe to return if you've got a stack allocated
closure.

Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
This commit is contained in:
Kent Overstreet 2024-05-03 14:43:54 -04:00
parent 9a768ab75b
commit 4c5b7294de
2 changed files with 49 additions and 0 deletions

View file

@ -194,6 +194,18 @@ static inline void closure_sync(struct closure *cl)
__closure_sync(cl);
}
int __closure_sync_timeout(struct closure *cl, unsigned long timeout);
static inline int closure_sync_timeout(struct closure *cl, unsigned long timeout)
{
#ifdef CONFIG_DEBUG_CLOSURES
BUG_ON(closure_nr_remaining(cl) != 1 && !cl->closure_get_happened);
#endif
return cl->closure_get_happened
? __closure_sync_timeout(cl, timeout)
: 0;
}
#ifdef CONFIG_DEBUG_CLOSURES
void closure_debug_create(struct closure *cl);

View file

@ -139,6 +139,43 @@ void __sched __closure_sync(struct closure *cl)
}
EXPORT_SYMBOL(__closure_sync);
int __sched __closure_sync_timeout(struct closure *cl, unsigned long timeout)
{
struct closure_syncer s = { .task = current };
int ret = 0;
cl->s = &s;
continue_at(cl, closure_sync_fn, NULL);
while (1) {
set_current_state(TASK_UNINTERRUPTIBLE);
if (s.done)
break;
if (!timeout) {
/*
* Carefully undo the continue_at() - but only if it
* hasn't completed, i.e. the final closure_put() hasn't
* happened yet:
*/
unsigned old, new, v = atomic_read(&cl->remaining);
do {
old = v;
if (!old || (old & CLOSURE_RUNNING))
goto success;
new = old + CLOSURE_REMAINING_INITIALIZER;
} while ((v = atomic_cmpxchg(&cl->remaining, old, new)) != old);
ret = -ETIME;
}
timeout = schedule_timeout(timeout);
}
success:
__set_current_state(TASK_RUNNING);
return ret;
}
EXPORT_SYMBOL(__closure_sync_timeout);
#ifdef CONFIG_DEBUG_CLOSURES
static LIST_HEAD(closure_list);