Checks for non-VCPD mboxs and INDIRs
[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 */
75 static void send_indir_to_vcore(struct event_queue *ev_q, uint32_t vcoreid)
76 {
77         struct event_msg local_msg = {0};
78         local_msg.ev_type = EV_EVENT;
79         local_msg.ev_arg3 = ev_q;
80         post_ev_msg(get_proc_ev_mbox(vcoreid), &local_msg, 0);
81 }
82
83 /* Helper that alerts a vcore, by IPI and/or INDIR, that it needs to check the
84  * ev_q.  Handles FALLBACK and other tricky things.  Returns which vcore was
85  * alerted.  The only caller of this is send_event(), and this makes it a little
86  * clearer/easier.
87  *
88  * One of the goals of FALLBACK (and this func) is to allow processes to yield
89  * cores without fear of losing messages (INDIR messages, btw (aka, non-vcore
90  * business)).
91  *
92  * The plan for dealing with FALLBACK is that we get a good vcoreid (can recv
93  * messages), then do the IPI/INDIRs, and then check to make sure the vcore is
94  * still good.  If the vcore is no longer available, we find another.  Userspace
95  * will make sure to turn off the can_recv_msg flag (and then check for messages
96  * again) before yielding.
97  *
98  * I don't particularly care if the vcore is offline or not for INDIRs.  There
99  * is a small window when a vcore is offline but can receive messages AND that
100  * another vcore is online.  This would only happen when a vcore doesn't respond
101  * to a preemption.  This would NOT happen when the entire process was preempted
102  * (which is when I would want to send to the initial offline vcore anyway).  In
103  * short, if can_recv is set, I'll send it there, and let userspace handle the
104  * rare "unresponsive" preemption.  There are a lot of legit reasons why a vcore
105  * would be offline (or preempt_pending) and have can_recv set.
106  *
107  * IPIs don't matter as much.  We'll send them to the (fallback) vcore, but
108  * never send them to an offline vcore.  If we lose a race and try to IPI an
109  * offline core, proc_notify can handle it.  I do the checks here to avoid some
110  * future pain (for now). */
111 static uint32_t alert_vcore(struct proc *p, struct event_queue *ev_q,
112                             uint32_t vcoreid)
113 {
114         int num_loops = 0;
115         /* Don't care about FALLBACK, just send and be done with it */
116         if (!ev_q->ev_flags & EVENT_FALLBACK) {
117                 if (ev_q->ev_flags & EVENT_INDIR)
118                         send_indir_to_vcore(ev_q, vcoreid);
119                 /* Don't bother with the IPI if the vcore is offline */
120                 if ((ev_q->ev_flags & EVENT_IPI) && vcore_is_mapped(p, vcoreid))
121                         proc_notify(p, vcoreid);
122                 return vcoreid;
123         }
124         /* If we're here, we care about FALLBACK.  Loop, trying vcores til we don't
125          * lose the race.  It's a user bug (which we'll comment on in a helper) if
126          * there are no vcores willing to rcv a message. */
127         do {
128                 /* Sanity check.  Should never happen, unless we're buggy */
129                 if (num_loops++ > MAX_NUM_CPUS)
130                         warn("Having a hard time finding an online vcore");
131                 /* Preemptively try to get a 'good' vcoreid.  The vcore might actually
132                  * be offline. */
133                 if (!can_alert_vcore(p, vcoreid)) {
134                         vcoreid = 0;    /* start the search from 0, more likely to be on */
135                         vcoreid = find_alertable_vcore(p, vcoreid);
136                 }
137                 /* If we're here, we think the vcore can recv the INDIR */
138                 if (ev_q->ev_flags & EVENT_INDIR)
139                         send_indir_to_vcore(ev_q, vcoreid);
140                 /* Only send the IPI if it is also online (optimization) */
141                 if ((ev_q->ev_flags & EVENT_IPI) && vcore_is_mapped(p, vcoreid))
142                         proc_notify(p, vcoreid);
143                 wmb();
144                 /* If the vcore now can't receive the message, we probably lost the
145                  * race, so let's loop and try with another.  Some vcore is getting
146                  * spurious messages, but those are not incorrect (just slows things a
147                  * bit if we lost the race). */
148         } while (!can_alert_vcore(p, vcoreid));
149         return vcoreid;
150 }
151
152 /* Send an event to ev_q, based on the parameters in ev_q's flag.  We don't
153  * accept null ev_qs, since the caller ought to be checking before bothering to
154  * make a msg and send it to the event_q.  Vcoreid is who the kernel thinks the
155  * message ought to go to (for IPIs).  Appropriate for things like
156  * EV_PREEMPT_PENDING, where we tell the affected vcore.  To have the message go
157  * where the kernel suggests, set EVENT_VCORE_APPRO(priate). */
158 void send_event(struct proc *p, struct event_queue *ev_q, struct event_msg *msg,
159                 uint32_t vcoreid)
160 {
161         struct proc *old_proc;
162         struct event_mbox *ev_mbox = 0;
163         assert(p);
164         printd("[kernel] sending msg to proc %08p, ev_q %08p\n", p, ev_q);
165         if (!ev_q) {
166                 warn("[kernel] Null ev_q - kernel code should check before sending!");
167                 return;
168         }
169         if (!is_user_rwaddr(ev_q, sizeof(struct event_queue))) {
170                 /* Ought to kill them, just warn for now */
171                 warn("[kernel] Illegal addr for ev_q");
172                 return;
173         }
174         /* ev_q is a user pointer, so we need to make sure we're in the right
175          * address space */
176         old_proc = switch_to(p);
177         /* Get the mbox and vcoreid */
178         /* If we're going with APPRO, we use the kernel's suggested vcore's ev_mbox.
179          * vcoreid is already what the kernel suggests. */
180         if (ev_q->ev_flags & EVENT_VCORE_APPRO) {
181                 ev_mbox = get_proc_ev_mbox(vcoreid);
182         } else {        /* common case */
183                 ev_mbox = ev_q->ev_mbox;
184                 vcoreid = ev_q->ev_vcore;
185         }
186         /* Check on the style, which could affect our mbox selection.  Other styles
187          * would go here (or in similar functions we call to).  Important thing is
188          * we come out knowing which vcore to send to in the event of an IPI/INDIR,
189          * and we know what mbox to post to. */
190         if (ev_q->ev_flags & EVENT_ROUNDROBIN) {
191                 /* Pick a vcore, and if we don't have a mbox yet, pick that vcore's
192                  * default mbox.  Assuming ev_vcore was the previous one used.  Note
193                  * that round-robin overrides the passed-in vcoreid. */
194                 vcoreid = (ev_q->ev_vcore + 1) % p->procinfo->num_vcores;
195                 ev_q->ev_vcore = vcoreid;
196                 /* Note that the style of not having a specific ev_mbox may go away.  I
197                  * can't think of legitimate uses of this for now, since things that are
198                  * RR probably are non-vcore-business, and thus inappropriate for a VCPD
199                  * ev_mbox. */
200                 if (!ev_mbox)
201                         ev_mbox = get_proc_ev_mbox(vcoreid);
202         }
203         /* At this point, we ought to have the right mbox to send the msg to, and
204          * which vcore to send an IPI to (if we send one).  The mbox could be the
205          * vcore's vcpd ev_mbox.  The vcoreid only matters for IPIs and INDIRs. */
206         if (!ev_mbox) {
207                 /* this is a process error */
208                 warn("[kernel] ought to have an mbox by now!");
209                 goto out;
210         }
211         /* Even if we're using an mbox in procdata (VCPD), we want a user pointer */
212         if (!is_user_rwaddr(ev_mbox, sizeof(struct event_mbox))) {
213                 /* Ought to kill them, just warn for now */
214                 warn("[kernel] Illegal addr for ev_mbox");
215                 goto out;
216         }
217         /* We used to support no msgs, but quit being lazy and send a 'msg'.  If the
218          * ev_q is a NOMSG, we won't actually memcpy or anything, it'll just be a
219          * vehicle for sending the ev_type. */
220         assert(msg);
221         post_ev_msg(ev_mbox, msg, ev_q->ev_flags);
222         /* Help out userspace a bit by checking for a potentially confusing bug */
223         if ((ev_mbox == get_proc_ev_mbox(vcoreid)) &&
224             (ev_q->ev_flags & EVENT_INDIR))
225                 printk("[kernel] User-bug: ev_q has an INDIR with a VCPD ev_mbox!\n");
226         /* Prod/alert a vcore with an IPI or INDIR, if desired */
227         if ((ev_q->ev_flags & (EVENT_IPI | EVENT_INDIR)))
228                 alert_vcore(p, ev_q, vcoreid);
229         /* TODO: If the whole proc is offline, this is where we can check and make
230          * it runnable (if we want).  Alternatively, we can do this only if they
231          * asked for IPIs or INDIRs. */
232
233         /* Fall through */
234 out:
235         /* Return to the old address space. */
236         switch_back(p, old_proc);
237 }
238
239 /* Send an event for the kernel event ev_num.  These are the "one sided" kernel
240  * initiated events, that require a lookup of the ev_q in procdata.  This is
241  * roughly equivalent to the old "proc_notify()" */
242 void send_kernel_event(struct proc *p, struct event_msg *msg, uint32_t vcoreid)
243 {
244         uint16_t ev_num = msg->ev_type;
245         assert(ev_num < MAX_NR_EVENT);          /* events start at 0 */
246         struct event_queue *ev_q = p->procdata->kernel_evts[ev_num];
247         if (ev_q)
248                 send_event(p, ev_q, msg, vcoreid);
249 }
250
251 /* Writes the msg to the vcpd/default mbox of the vcore.  Needs to load current,
252  * but doesn't need to care about what the process wants.  Note this isn't
253  * commonly used - just the monitor and sys_self_notify(). */
254 void post_vcore_event(struct proc *p, struct event_msg *msg, uint32_t vcoreid)
255 {
256         /* Need to set p as current to post the event */
257         struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
258         struct proc *old_proc = switch_to(p);
259         /* *ev_mbox is the user address of the vcpd mbox */
260         post_ev_msg(get_proc_ev_mbox(vcoreid), msg, 0); /* no chance for a NOMSG */
261         switch_back(p, old_proc);
262 }