| /* 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)); |
| } |