Fixes event_q "get" interfaces to work with UCQs
[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         struct procdata *pd = (struct procdata*)UDATA;
24         return &pd->vcore_preempt_data[vcoreid].ev_mbox;
25 }
26
27 /* Posts a message to the mbox, subject to flags.  Feel free to send 0 for the
28  * flags if you don't want to give them the option of EVENT_NOMSG (which is what
29  * we do when sending an indirection event).  Make sure that if mbox is a user
30  * pointer, that you've checked it *and* have that processes address space
31  * loaded.  This can get called with a KVA for mbox. */
32 static void post_ev_msg(struct event_mbox *mbox, struct event_msg *msg,
33                         int ev_flags)
34 {
35         struct proc *p = current;
36         printd("[kernel] Sending event type %d to mbox %08p\n", msg->ev_type, mbox);
37         /* Sanity check */
38         assert(p);
39         /* If they just want a bit (NOMSG), just set the bit */
40         if (ev_flags & EVENT_NOMSG) {
41                 SET_BITMASK_BIT_ATOMIC(mbox->ev_bitmap, msg->ev_type);
42         } else {
43                 send_ucq_msg(&mbox->ev_msgs, p, msg);
44         }
45 }
46
47 /* Send an event to ev_q, based on the parameters in ev_q's flag.  We don't
48  * accept null ev_qs, since the caller ought to be checking before bothering to
49  * make a msg and send it to the event_q.  Vcoreid is who the kernel thinks the
50  * message ought to go to (for IPIs).  Appropriate for things like
51  * EV_PREEMPT_PENDING, where we tell the affected vcore.  To have the message go
52  * where the kernel suggests, set EVENT_VCORE_APPRO(priate). */
53 void send_event(struct proc *p, struct event_queue *ev_q, struct event_msg *msg,
54                 uint32_t vcoreid)
55 {
56         struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
57         struct proc *old_proc = pcpui->cur_proc;        /* uncounted ref */
58         struct event_mbox *ev_mbox = 0, *vcore_mbox;
59         struct event_msg local_msg = {0};
60         assert(p);
61         printd("[kernel] sending msg to proc %08p, ev_q %08p\n", p, ev_q);
62         if (!ev_q) {
63                 warn("[kernel] Null ev_q - kernel code should check before sending!");
64                 return;
65         }
66         if (!is_user_rwaddr(ev_q, sizeof(struct event_queue))) {
67                 /* Ought to kill them, just warn for now */
68                 warn("[kernel] Illegal addr for ev_q");
69                 return;
70         }
71         /* ev_q is a user pointer, so we need to make sure we're in the right
72          * address space */
73         if (old_proc != p) {
74                 /* Technically, we're storing a ref here, but our current ref on p is
75                  * sufficient (so long as we don't decref below) */
76                 pcpui->cur_proc = p;
77                 lcr3(p->env_cr3);
78         }
79         /* Get the mbox and vcoreid */
80         /* If we're going with APPRO, we use the kernel's suggested vcore's ev_mbox.
81          * vcoreid is already what the kernel suggests. */
82         if (ev_q->ev_flags & EVENT_VCORE_APPRO) {
83                 ev_mbox = get_proc_ev_mbox(vcoreid);
84         } else {        /* common case */
85                 ev_mbox = ev_q->ev_mbox;
86                 vcoreid = ev_q->ev_vcore;
87         }
88         /* Check on the style, which could affect our mbox selection.  Other styles
89          * would go here (or in similar functions we call to).  Important thing is
90          * we come out knowing which vcore to send to in the event of an IPI, and we
91          * know what mbox to post to. */
92         if (ev_q->ev_flags & EVENT_ROUNDROBIN) {
93                 /* Pick a vcore, and if we don't have a mbox yet, pick that vcore's
94                  * default mbox.  Assuming ev_vcore was the previous one used.  Note
95                  * that round-robin overrides the passed-in vcoreid. */
96                 vcoreid = (ev_q->ev_vcore + 1) % p->procinfo->num_vcores;
97                 ev_q->ev_vcore = vcoreid;
98                 if (!ev_mbox)
99                         ev_mbox = get_proc_ev_mbox(vcoreid);
100         }
101         /* At this point, we ought to have the right mbox to send the msg to, and
102          * which vcore to send an IPI to (if we send one).  The mbox could be the
103          * vcore's vcpd ev_mbox. */
104         if (!ev_mbox) {
105                 /* this is a process error */
106                 warn("[kernel] ought to have an mbox by now!");
107                 goto out;
108         }
109         /* Even if we're using an mbox in procdata (VCPD), we want a user pointer */
110         if (!is_user_rwaddr(ev_mbox, sizeof(struct event_mbox))) {
111                 /* Ought to kill them, just warn for now */
112                 warn("[kernel] Illegal addr for ev_mbox");
113                 goto out;
114         }
115         /* We used to support no msgs, but quit being lazy and send a 'msg'.  If the
116          * ev_q is a NOMSG, we won't actually memcpy or anything, it'll just be a
117          * vehicle for sending the ev_type. */
118         assert(msg);
119         post_ev_msg(ev_mbox, msg, ev_q->ev_flags);
120         /* Optional IPIs */
121         if (ev_q->ev_flags & EVENT_IPI) {
122                 /* if the mbox we sent to isn't the default one, we need to send the
123                  * vcore an ev_q indirection event */
124                 vcore_mbox = get_proc_ev_mbox(vcoreid);
125                 if (ev_mbox != vcore_mbox) {
126                         /* it is tempting to send_kernel_event(), using the ev_q for that
127                          * event, but that is inappropriate here, since we are sending to a
128                          * specific vcore */
129                         local_msg.ev_type = EV_EVENT;
130                         local_msg.ev_arg3 = ev_q;
131                         post_ev_msg(vcore_mbox, &local_msg, 0);
132                 }
133                 proc_notify(p, vcoreid);
134         }
135         /* Fall through */
136 out:
137         /* Return to the old address space.  We switched to p in the first place if
138          * it wasn't the same as the original current (old_proc). */
139         if (old_proc != p) {
140                 pcpui->cur_proc = old_proc;
141                 if (old_proc)
142                         lcr3(old_proc->env_cr3);
143                 else
144                         lcr3(boot_cr3);
145         }
146 }
147
148 /* Send an event for the kernel event ev_num.  These are the "one sided" kernel
149  * initiated events, that require a lookup of the ev_q in procdata.  This is
150  * roughly equivalent to the old "proc_notify()" */
151 void send_kernel_event(struct proc *p, struct event_msg *msg, uint32_t vcoreid)
152 {
153         uint16_t ev_num = msg->ev_type;
154         assert(ev_num < MAX_NR_EVENT);          /* events start at 0 */
155         struct event_queue *ev_q = p->procdata->kernel_evts[ev_num];
156         if (ev_q)
157                 send_event(p, ev_q, msg, vcoreid);
158 }
159
160 /* Writes the msg to the vcpd/default mbox of the vcore.  Needs to load current,
161  * but doesn't need to care about what the process wants.  Note this isn't
162  * commonly used - just the monitor and sys_self_notify(). */
163 void post_vcore_event(struct proc *p, struct event_msg *msg, uint32_t vcoreid)
164 {
165         struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
166         struct proc *old_proc = pcpui->cur_proc;        /* uncounted ref */
167         /* Need to set p as current to post the event */
168         if (old_proc != p) {
169                 pcpui->cur_proc = p;
170                 lcr3(p->env_cr3);
171         }
172         /* *ev_mbox is the user address of the vcpd mbox */
173         post_ev_msg(get_proc_ev_mbox(vcoreid), msg, 0); /* no chance for a NOMSG */
174         /* Unload the address space, if applicable */
175         if (old_proc != p) {
176                 pcpui->cur_proc = old_proc;
177                 if (old_proc)
178                         lcr3(old_proc->env_cr3);
179                 else
180                         lcr3(boot_cr3);
181         }
182 }