blob: da97be00f9ebcfc3645c9c417939dda51077c6b5 [file] [log] [blame] [edit]
/* 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();
atomic_set(&tracker->need_to_run, FALSE); /* no internal HW mb */
/* 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);
wmb(); /* ensure the in_prog write comes after the run_again. */
atomic_set(&tracker->run_in_progress, FALSE); /* no internal HW mb */
/* in_prog write must come before run_again read */
wrmb();
} while (atomic_read(&tracker->need_to_run)); /* while there's more work*/
}