blob: ba70b44dead46e9fce5f92886b90516b0f6fc535 [file] [log] [blame]
/* Copyright (c) 2012 The Regents of the University of California
* Barret Rhoden <brho@cs.berkeley.edu>
* See LICENSE for details.
*
* Post work and poke synchronization. This is a wait-free way to make sure
* some code is run, usually by the calling core, but potentially by any core.
* Under contention, everyone just posts work, and one core will carry out the
* work. Callers post work (the meaning of which is particular to their
* subsystem), then call this function. The function is not run concurrently
* with itself.
*
* As far as uthreads, vcores, and preemption go, poking is safe in uthread
* context and if preemptions occur. However, a uthread running the poke
* function that gets preempted could delay the execution of the poke
* indefinitely. In general, post-and-poke does not provide any guarantee about
* *when* the poke finally occurs. If delays of this sort are a problem, then
* run poke() from vcore context.
*
* Adapted from the kernel's implementation. */
#include <parlib/poke.h>
#include <parlib/arch/atomic.h>
#include <parlib/assert.h>
/* This is the 'post (work) and poke' style of sync. We make sure the poke
* tracker's function runs. Once this returns, the func either has run or is
* currently running (in case someone else is running now). We won't wait or
* spin or anything, and it is safe to call this recursively (deeper in the
* call-graph).
*
* It's up to the caller to somehow post its work. We'll also pass arg to the
* func, ONLY IF the caller is the one to execute it - so there's no guarantee
* the func(specific_arg) combo will actually run. It's more for info
* purposes/optimizations/etc. If no one uses it, I'll get rid of it. */
void poke(struct poke_tracker *tracker, void *arg)
{
atomic_set(&tracker->need_to_run, TRUE);
/* will need to repeatedly do it if someone keeps posting work */
do {
/* want an wrmb() btw posting work/need_to_run and in_progress.
* the swap provides the HW mb. just need a cmb, which we do in
* the loop to cover the iterations (even though i can't imagine
* the compiler reordering the check it needed to do for the
* branch).. */
cmb();
/* poke / make sure someone does it. if we get a TRUE (1) back,
* someone is already running and will deal with the posted
* work. (probably on their next loop). if we got a 0 back, we
* won the race and have the 'lock'. */
if (atomic_swap(&tracker->run_in_progress, TRUE))
return;
/* if we're here, then we're the one who needs to run the func.
* */
/* clear the 'need to run', since we're running it now. new
* users will set it again. this write needs to be wmb()'d
* after in_progress. the swap provided the HW mb(). */
cmb();
/* no internal HW mb */
atomic_set(&tracker->need_to_run, FALSE);
/* run the actual function. the poke sync makes sure only one
* caller is in that func at a time. */
assert(tracker->func);
tracker->func(arg);
/* ensure the in_prog write comes after the run_again. */
wmb();
/* no internal HW mb */
atomic_set(&tracker->run_in_progress, FALSE);
/* in_prog write must come before run_again read */
wrmb();
} while (atomic_read(&tracker->need_to_run));
}