WIP-pop-3000
[akaros.git] / kern / src / rendez.c
1 /* Copyright (c) 2013 The Regents of the University of California
2  * Barret Rhoden <brho@cs.berkeley.edu>
3  * See LICENSE for details.
4  *
5  * Plan9 style Rendezvous (http://plan9.bell-labs.com/sys/doc/sleep.html)
6  *
7  * We implement it with CVs, and it can handle multiple sleepers/wakers. */
8
9 #include <rendez.h>
10 #include <kthread.h>
11 #include <alarm.h>
12 #include <assert.h>
13 #include <smp.h>
14 #include <err.h>
15
16 void rendez_init(struct rendez *rv)
17 {
18         cv_init_irqsave(&rv->cv);
19 }
20
21 // XXX can we have multiple sleepers on a rendez with different functions?
22 //              if not, we can have the func (+ arg?) in the object
23 //              waker can check the func too, to avoid spurious wakeups
24 //
25 //              if so, we could still store the func with some lookup-elem and still
26 //              have the waker run the funcs.
27 void rendez_sleep(struct rendez *rv, int (*cond)(void*), void *arg)
28 {
29         int8_t irq_state = 0;
30         struct cv_lookup_elm cle;
31         /* Do a quick check before registering and sleeping.  this is the 'check,
32          * signal, check again' pattern, where the first check is an optimization.
33          * Many rendezes will already be satisfied, so we want to avoid excessive
34          * locking associated with reg/dereg. */
35         cv_lock_irqsave(&rv->cv, &irq_state);
36         if (cond(arg)) {
37                 cv_unlock_irqsave(&rv->cv, &irq_state);
38                 return;
39         }
40         __reg_abortable_cv(&cle, &rv->cv);
41         /* Mesa-style semantics, which is definitely what you want.  See the
42          * discussion at the end of the URL above. */
43         while (!cond(arg)) {
44                 /* it's okay if we miss the ABORT flag; we hold the cv lock, so an
45                  * aborter's broadcast is waiting until we unlock. */
46                 if (should_abort(&cle)) {
47                         cv_unlock_irqsave(&rv->cv, &irq_state);
48                         dereg_abortable_cv(&cle);
49                         error(EINTR, "syscall aborted");
50                 }
51                 cv_wait(&rv->cv);
52                 cpu_relax();
53         }
54         cv_unlock_irqsave(&rv->cv, &irq_state);
55         dereg_abortable_cv(&cle);
56 }
57
58 /* Force a wakeup of all waiters on the rv, including non-timeout users.  For
59  * those, they will just wake up, see the condition is still false (probably)
60  * and go back to sleep. */
61 static void rendez_alarm_handler(struct alarm_waiter *awaiter,
62                                  struct hw_trapframe *hw_tf)
63 {
64         struct rendez *rv = (struct rendez*)awaiter->data;
65
66         rendez_wakeup(rv);
67 }
68
69 /* Like sleep, but it will timeout in 'usec' microseconds. */
70 void rendez_sleep_timeout(struct rendez *rv, int (*cond)(void*), void *arg,
71                           uint64_t usec)
72 {
73         int8_t irq_state = 0;
74         struct alarm_waiter awaiter;
75         struct cv_lookup_elm cle;
76         struct timer_chain *pcpui_tchain = &per_cpu_info[core_id()].tchain;
77
78         if (!usec)
79                 return;
80         /* Doing this cond check early, but then unlocking again.  Mostly just to
81          * avoid weird issues with the CV lock and the alarm tchain lock. */
82         cv_lock_irqsave(&rv->cv, &irq_state);
83         if (cond(arg)) {
84                 cv_unlock_irqsave(&rv->cv, &irq_state);
85                 return;
86         }
87         cv_unlock_irqsave(&rv->cv, &irq_state);
88         /* The handler will call rendez_wake, but won't mess with the condition
89          * state.  It's enough to break us out of cv_wait() to see .on_tchain.
90          * Since all we're doing is poking a rendez, we might as well just do it
91          * from IRQ ctx instead of mucking with an extra RKM.  It also avoids issues
92          * with unset_alarm blocking. */
93         init_awaiter_irq(&awaiter, rendez_alarm_handler);
94         awaiter.data = rv;
95         set_awaiter_rel(&awaiter, usec);
96         /* Set our alarm on this cpu's tchain.  Note that when we sleep in cv_wait,
97          * we could be migrated, and later on we could be unsetting the alarm
98          * remotely. */
99         set_alarm(pcpui_tchain, &awaiter);
100         cv_lock_irqsave(&rv->cv, &irq_state);
101         __reg_abortable_cv(&cle, &rv->cv);
102         /* We could wake early for a few reasons.  Legit wakeups after a changed
103          * condition (and we should exit), other alarms with different timeouts (and
104          * we should go back to sleep), etc.  Note it is possible for our alarm to
105          * fire immediately upon setting it: before we even cv_lock. */
106         while (!cond(arg) && awaiter.on_tchain) {
107                 if (should_abort(&cle)) {
108                         cv_unlock_irqsave(&rv->cv, &irq_state);
109                         unset_alarm(pcpui_tchain, &awaiter);
110                         dereg_abortable_cv(&cle);
111                         error(EINTR, "syscall aborted");
112                 }
113                 cv_wait(&rv->cv);
114                 cpu_relax();
115         }
116         cv_unlock_irqsave(&rv->cv, &irq_state);
117         dereg_abortable_cv(&cle);
118         /* Turn off our alarm.  If it already fired, this is a no-op.  Note this
119          * could be cross-core. */
120         unset_alarm(pcpui_tchain, &awaiter);
121 }
122
123 /* plan9 rendez returned a pointer to the proc woken up.  we return "true" if we
124  * woke someone up. */
125 bool rendez_wakeup(struct rendez *rv)
126 {
127         int8_t irq_state = 0;
128         bool ret;
129         /* The plan9 style "one sleeper, one waker" could get by with a signal here.
130          * But we want to make sure all potential waiters are woken up. */
131         cv_lock_irqsave(&rv->cv, &irq_state);
132         ret = rv->cv.nr_waiters ? TRUE : FALSE;
133         __cv_broadcast(&rv->cv);
134         cv_unlock_irqsave(&rv->cv, &irq_state);
135         return ret;
136 }