proc_yield() will return if you have an event
[akaros.git] / kern / src / event.c
1 /* Copyright (c) 2011 The Regents of the University of California
2  * Barret Rhoden <brho@cs.berkeley.edu>
3  * See LICENSE for details.
4  *
5  * Kernel utility functions for sending events and notifications (IPIs) to
6  * processes. */
7
8 #include <ucq.h>
9 #include <bitmask.h>
10 #include <event.h>
11 #include <atomic.h>
12 #include <process.h>
13 #include <smp.h>
14 #include <umem.h>
15 #include <stdio.h>
16 #include <assert.h>
17 #include <pmap.h>
18
19 /* Note this returns the user address of the mbox, not the KVA.  You'll need
20  * current loaded to access this, and it will work for any process. */
21 static struct event_mbox *get_proc_ev_mbox(uint32_t vcoreid)
22 {
23         return &__procdata.vcore_preempt_data[vcoreid].ev_mbox;
24 }
25
26 /* Posts a message to the mbox, subject to flags.  Feel free to send 0 for the
27  * flags if you don't want to give them the option of EVENT_NOMSG (which is what
28  * we do when sending an indirection event).  Make sure that if mbox is a user
29  * pointer, that you've checked it *and* have that processes address space
30  * loaded.  This can get called with a KVA for mbox. */
31 static void post_ev_msg(struct event_mbox *mbox, struct event_msg *msg,
32                         int ev_flags)
33 {
34         struct proc *p = current;
35         printd("[kernel] Sending event type %d to mbox %08p\n", msg->ev_type, mbox);
36         /* Sanity check */
37         assert(p);
38         /* If they just want a bit (NOMSG), just set the bit */
39         if (ev_flags & EVENT_NOMSG) {
40                 SET_BITMASK_BIT_ATOMIC(mbox->ev_bitmap, msg->ev_type);
41         } else {
42                 send_ucq_msg(&mbox->ev_msgs, p, msg);
43         }
44 }
45
46 /* Can we alert the vcore?  (Will it check its messages).  Note this checks
47  * procdata via the user pointer. */
48 static bool can_alert_vcore(struct proc *p, uint32_t vcoreid)
49 {
50         struct preempt_data *vcpd = &__procdata.vcore_preempt_data[vcoreid];
51         return vcpd->can_rcv_msg;
52 }
53
54 /* Scans the vcoremap, looking for an alertable vcore (returing that vcoreid).
55  * If this fails, it's userspace's fault, so we'll complain loudly.  
56  *
57  * It is possible for a vcore to yield and toggle this flag off before we post
58  * the indir, which is why we have that loop in alert_vcore().
59  *
60  * Note this checks procdata via the user pointer. */
61 uint32_t find_alertable_vcore(struct proc *p, uint32_t start_loc)
62 {
63         struct procinfo *pi = p->procinfo;
64         for (uint32_t i = start_loc; i < pi->max_vcores; i++) {
65                 if (can_alert_vcore(p, i)) {
66                         return i;
67                 }
68         }
69         /* if we're here, the program is likely fucked.  buggy at least */
70         printk("[kernel] no vcores can recv messages!  (user bug)\n");
71         return 0;       /* vcore 0 is the most likely to come back online */
72 }
73
74 /* Helper to send an indir, called from a couple places.  Note this uses a
75  * userspace address for the VCPD (though not a user's pointer). */
76 static void send_indir_to_vcore(struct event_queue *ev_q, uint32_t vcoreid)
77 {
78         struct preempt_data *vcpd = &__procdata.vcore_preempt_data[vcoreid];
79         struct event_msg local_msg = {0};
80         local_msg.ev_type = EV_EVENT;
81         local_msg.ev_arg3 = ev_q;
82         post_ev_msg(get_proc_ev_mbox(vcoreid), &local_msg, 0);
83         /* Set notif pending, so userspace doesn't miss the INDIR while yielding */
84         wmb();
85         vcpd->notif_pending = TRUE;
86 }
87
88 /* Helper that alerts a vcore, by IPI and/or INDIR, that it needs to check the
89  * ev_q.  Handles FALLBACK and other tricky things.  Returns which vcore was
90  * alerted.  The only caller of this is send_event(), and this makes it a little
91  * clearer/easier.
92  *
93  * One of the goals of FALLBACK (and this func) is to allow processes to yield
94  * cores without fear of losing messages (INDIR messages, btw (aka, non-vcore
95  * business)).
96  *
97  * The plan for dealing with FALLBACK is that we get a good vcoreid (can recv
98  * messages), then do the IPI/INDIRs, and then check to make sure the vcore is
99  * still good.  If the vcore is no longer available, we find another.  Userspace
100  * will make sure to turn off the can_recv_msg flag (and then check for messages
101  * again) before yielding.
102  *
103  * I don't particularly care if the vcore is offline or not for INDIRs.  There
104  * is a small window when a vcore is offline but can receive messages AND that
105  * another vcore is online.  This would only happen when a vcore doesn't respond
106  * to a preemption.  This would NOT happen when the entire process was preempted
107  * (which is when I would want to send to the initial offline vcore anyway).  In
108  * short, if can_recv is set, I'll send it there, and let userspace handle the
109  * rare "unresponsive" preemption.  There are a lot of legit reasons why a vcore
110  * would be offline (or preempt_pending) and have can_recv set.
111  *
112  * IPIs don't matter as much.  We'll send them to the (fallback) vcore, but
113  * never send them to an offline vcore.  If we lose a race and try to IPI an
114  * offline core, proc_notify can handle it.  I do the checks here to avoid some
115  * future pain (for now). */
116 static uint32_t alert_vcore(struct proc *p, struct event_queue *ev_q,
117                             uint32_t vcoreid)
118 {
119         int num_loops = 0;
120         /* If an alert is already pending, just return */
121         if (ev_q->ev_alert_pending)
122                 return vcoreid;
123         /* We'll eventually get an INDIR through, so don't send any more til
124          * userspace toggles this.  Regardless of other writers to this flag, we
125          * eventually send an alert that causes userspace to turn throttling off
126          * again (before handling all of the ev_q's events).
127          *
128          * This will also squelch IPIs, since there's no reason to send the IPI if
129          * the INDIR is still un-acknowledged.  The vcore is either in vcore
130          * context, attempting to deal with the INDIR, or offline.  This statement
131          * is probably true. */
132         if (ev_q->ev_flags & EVENT_INDIR) {
133                 ev_q->ev_alert_pending = TRUE;
134         }
135         /* Don't care about FALLBACK, just send and be done with it */
136         if (!ev_q->ev_flags & EVENT_FALLBACK) {
137                 if (ev_q->ev_flags & EVENT_INDIR)
138                         send_indir_to_vcore(ev_q, vcoreid);
139                 /* Don't bother with the IPI if the vcore is offline */
140                 if ((ev_q->ev_flags & EVENT_IPI) && vcore_is_mapped(p, vcoreid))
141                         proc_notify(p, vcoreid);
142                 return vcoreid;
143         }
144         /* If we're here, we care about FALLBACK.  Loop, trying vcores til we don't
145          * lose the race.  It's a user bug (which we'll comment on in a helper) if
146          * there are no vcores willing to rcv a message. */
147         do {
148                 /* Sanity check.  Should never happen, unless we're buggy */
149                 if (num_loops++ > MAX_NUM_CPUS)
150                         warn("Having a hard time finding an online vcore");
151                 /* Preemptively try to get a 'good' vcoreid.  The vcore might actually
152                  * be offline. */
153                 if (!can_alert_vcore(p, vcoreid)) {
154                         vcoreid = 0;    /* start the search from 0, more likely to be on */
155                         vcoreid = find_alertable_vcore(p, vcoreid);
156                 }
157                 /* If we're here, we think the vcore can recv the INDIR */
158                 if (ev_q->ev_flags & EVENT_INDIR)
159                         send_indir_to_vcore(ev_q, vcoreid);
160                 /* Only send the IPI if it is also online (optimization) */
161                 if ((ev_q->ev_flags & EVENT_IPI) && vcore_is_mapped(p, vcoreid))
162                         proc_notify(p, vcoreid);
163                 wmb();
164                 /* If the vcore now can't receive the message, we probably lost the
165                  * race, so let's loop and try with another.  Some vcore is getting
166                  * spurious messages, but those are not incorrect (just slows things a
167                  * bit if we lost the race). */
168         } while (!can_alert_vcore(p, vcoreid));
169         return vcoreid;
170 }
171
172 /* Send an event to ev_q, based on the parameters in ev_q's flag.  We don't
173  * accept null ev_qs, since the caller ought to be checking before bothering to
174  * make a msg and send it to the event_q.  Vcoreid is who the kernel thinks the
175  * message ought to go to (for IPIs).  Appropriate for things like
176  * EV_PREEMPT_PENDING, where we tell the affected vcore.  To have the message go
177  * where the kernel suggests, set EVENT_VCORE_APPRO(priate). */
178 void send_event(struct proc *p, struct event_queue *ev_q, struct event_msg *msg,
179                 uint32_t vcoreid)
180 {
181         struct proc *old_proc;
182         struct event_mbox *ev_mbox = 0;
183         assert(p);
184         printd("[kernel] sending msg to proc %08p, ev_q %08p\n", p, ev_q);
185         if (!ev_q) {
186                 warn("[kernel] Null ev_q - kernel code should check before sending!");
187                 return;
188         }
189         if (!is_user_rwaddr(ev_q, sizeof(struct event_queue))) {
190                 /* Ought to kill them, just warn for now */
191                 warn("[kernel] Illegal addr for ev_q");
192                 return;
193         }
194         /* ev_q is a user pointer, so we need to make sure we're in the right
195          * address space */
196         old_proc = switch_to(p);
197         /* Get the mbox and vcoreid */
198         /* If we're going with APPRO, we use the kernel's suggested vcore's ev_mbox.
199          * vcoreid is already what the kernel suggests. */
200         if (ev_q->ev_flags & EVENT_VCORE_APPRO) {
201                 ev_mbox = get_proc_ev_mbox(vcoreid);
202         } else {        /* common case */
203                 ev_mbox = ev_q->ev_mbox;
204                 vcoreid = ev_q->ev_vcore;
205         }
206         /* Check on the style, which could affect our mbox selection.  Other styles
207          * would go here (or in similar functions we call to).  Important thing is
208          * we come out knowing which vcore to send to in the event of an IPI/INDIR,
209          * and we know what mbox to post to. */
210         if (ev_q->ev_flags & EVENT_ROUNDROBIN) {
211                 /* Pick a vcore, and if we don't have a mbox yet, pick that vcore's
212                  * default mbox.  Assuming ev_vcore was the previous one used.  Note
213                  * that round-robin overrides the passed-in vcoreid. */
214                 vcoreid = (ev_q->ev_vcore + 1) % p->procinfo->num_vcores;
215                 ev_q->ev_vcore = vcoreid;
216                 /* Note that the style of not having a specific ev_mbox may go away.  I
217                  * can't think of legitimate uses of this for now, since things that are
218                  * RR probably are non-vcore-business, and thus inappropriate for a VCPD
219                  * ev_mbox. */
220                 if (!ev_mbox)
221                         ev_mbox = get_proc_ev_mbox(vcoreid);
222         }
223         /* At this point, we ought to have the right mbox to send the msg to, and
224          * which vcore to send an IPI to (if we send one).  The mbox could be the
225          * vcore's vcpd ev_mbox.  The vcoreid only matters for IPIs and INDIRs. */
226         if (!ev_mbox) {
227                 /* this is a process error */
228                 warn("[kernel] ought to have an mbox by now!");
229                 goto out;
230         }
231         /* Even if we're using an mbox in procdata (VCPD), we want a user pointer */
232         if (!is_user_rwaddr(ev_mbox, sizeof(struct event_mbox))) {
233                 /* Ought to kill them, just warn for now */
234                 warn("[kernel] Illegal addr for ev_mbox");
235                 goto out;
236         }
237         /* We used to support no msgs, but quit being lazy and send a 'msg'.  If the
238          * ev_q is a NOMSG, we won't actually memcpy or anything, it'll just be a
239          * vehicle for sending the ev_type. */
240         assert(msg);
241         post_ev_msg(ev_mbox, msg, ev_q->ev_flags);
242         /* Help out userspace a bit by checking for a potentially confusing bug */
243         if ((ev_mbox == get_proc_ev_mbox(vcoreid)) &&
244             (ev_q->ev_flags & EVENT_INDIR))
245                 printk("[kernel] User-bug: ev_q has an INDIR with a VCPD ev_mbox!\n");
246         /* Prod/alert a vcore with an IPI or INDIR, if desired */
247         if ((ev_q->ev_flags & (EVENT_IPI | EVENT_INDIR)))
248                 alert_vcore(p, ev_q, vcoreid);
249         /* TODO: If the whole proc is offline, this is where we can check and make
250          * it runnable (if we want).  Alternatively, we can do this only if they
251          * asked for IPIs or INDIRs. */
252
253         /* Fall through */
254 out:
255         /* Return to the old address space. */
256         switch_back(p, old_proc);
257 }
258
259 /* Send an event for the kernel event ev_num.  These are the "one sided" kernel
260  * initiated events, that require a lookup of the ev_q in procdata.  This is
261  * roughly equivalent to the old "proc_notify()" */
262 void send_kernel_event(struct proc *p, struct event_msg *msg, uint32_t vcoreid)
263 {
264         uint16_t ev_num = msg->ev_type;
265         assert(ev_num < MAX_NR_EVENT);          /* events start at 0 */
266         struct event_queue *ev_q = p->procdata->kernel_evts[ev_num];
267         if (ev_q)
268                 send_event(p, ev_q, msg, vcoreid);
269 }
270
271 /* Writes the msg to the vcpd/default mbox of the vcore.  Needs to load current,
272  * but doesn't need to care about what the process wants.  Note this isn't
273  * commonly used - just the monitor and sys_self_notify(). */
274 void post_vcore_event(struct proc *p, struct event_msg *msg, uint32_t vcoreid)
275 {
276         /* Need to set p as current to post the event */
277         struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
278         struct proc *old_proc = switch_to(p);
279         /* *ev_mbox is the user address of the vcpd mbox */
280         post_ev_msg(get_proc_ev_mbox(vcoreid), msg, 0); /* no chance for a NOMSG */
281         switch_back(p, old_proc);
282 }