a39efc5a7d735e602de09686b4bbba7fc9edd73a
[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 <ros/bcq.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 KVA of the mbox, not the user one. */
20 static struct event_mbox *get_proc_ev_mbox(struct proc *p, uint32_t vcoreid)
21 {
22         return &p->procdata->vcore_preempt_data[vcoreid].ev_mbox;
23 }
24
25 /* Posts a message to the mbox, subject to flags.  Feel free to send 0 for the
26  * flags if you don't want to give them the option of EVENT_NOMSG (which is what
27  * we do when sending an indirection event).  Make sure that if mbox is a user
28  * pointer, that you've checked it *and* have that processes address space
29  * loaded.  This can get called with a KVA for mbox. */
30 static void post_ev_msg(struct event_mbox *mbox, struct event_msg *msg,
31                         int ev_flags)
32 {
33         printd("Sending event type %d\n", msg->ev_type);
34         /* Sanity check */
35         if (is_user_rwaddr(mbox))
36                 assert(current);
37         /* If they just want a bit (NOMSG), just set the bit */
38         if (ev_flags & EVENT_NOMSG) {
39                 SET_BITMASK_BIT_ATOMIC(mbox->ev_bitmap, msg->ev_type);
40         } else {
41                 /* Enqueue returns 0 on success.  On failure, set a bit. */
42                 if (bcq_enqueue(&mbox->ev_msgs, msg, NR_BCQ_EVENTS, NR_BCQ_EV_LOOPS)) {
43                         atomic_inc((atomic_t)&mbox->ev_overflows); // careful here
44                         SET_BITMASK_BIT_ATOMIC(mbox->ev_bitmap, msg->ev_type);
45                         /* Catch "lots" of overflows that aren't acknowledged */
46                         if (mbox->ev_overflows > 10000)
47                                 warn("proc %d has way too many overflows", current->pid);
48                 }
49         }
50 }
51
52 /* Send an event to ev_q, based on the parameters in ev_q's flag.  We don't
53  * accept null ev_qs, since the caller ought to be checking before bothering to
54  * make a msg and send it to the event_q.  Vcoreid is who the kernel thinks the
55  * message ought to go to (for IPIs).  Appropriate for things like
56  * EV_PREEMPT_PENDING, where we tell the affected vcore.  To have the message go
57  * where the kernel suggests, set EVENT_VCORE_APPRO(priate). */
58 void send_event(struct proc *p, struct event_queue *ev_q, struct event_msg *msg,
59                 uint32_t vcoreid)
60 {
61         struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
62         struct proc *old_proc = pcpui->cur_proc;        /* uncounted ref */
63         struct event_mbox *ev_mbox, *vcore_mbox;
64         struct event_msg local_msg = {0};
65         assert(p);
66         if (!ev_q) {
67                 warn("[kernel] Null ev_q - kernel code should check before sending!");
68                 return;
69         }
70         if (!is_user_rwaddr(ev_q)) {
71                 /* Ought to kill them, just warn for now */
72                 warn("[kernel] Illegal addr for ev_q");
73                 return;
74         }
75         /* ev_q can be a user pointer (not in procdata), so we need to make sure
76          * we're in the right address space */
77         if (old_proc != p) {
78                 /* Technically, we're storing a ref here, but our current ref on p is
79                  * sufficient (so long as we don't decref below) */
80                 pcpui->cur_proc = p;
81                 lcr3(p->env_cr3);
82         }
83         /* If we don't follow the kernel's advice, do what the process wants */
84         if (!(ev_q->ev_flags & EVENT_VCORE_APPRO))
85                 vcoreid = ev_q->ev_vcore;
86         /* Post the event.  Need to get the initial mbox (might be 0). */
87         ev_mbox = ev_q->ev_mbox;
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(p, 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). */
103         if (!ev_mbox) {
104                 /* this is a process error */
105                 warn("[kernel] ought to have an mbox by now!");
106                 goto out;
107         }
108         if (!is_user_rwaddr(ev_mbox)) {
109                 /* Ought to kill them, just warn for now */
110                 warn("[kernel] Illegal addr for ev_mbox");
111                 goto out;
112         }
113         /* We used to support no msgs, but quit being lazy and send a msg */
114         assert(msg);
115         post_ev_msg(ev_mbox, msg, ev_q->ev_flags);
116         /* Optional IPIs */
117         if (ev_q->ev_flags & EVENT_IPI) {
118                 /* if the mbox we sent to isn't the default one, we need to send the
119                  * vcore an ev_q indirection event */
120                 vcore_mbox = get_proc_ev_mbox(p, vcoreid);
121                 if (!uva_is_kva(p, ev_mbox, vcore_mbox)) {
122                         /* it is tempting to send_kernel_event(), using the ev_q for that
123                          * event, but that is inappropriate here, since we are sending to a
124                          * specific vcore */
125                         local_msg.ev_type = EV_EVENT;
126                         local_msg.ev_arg3 = ev_q;
127                         post_ev_msg(vcore_mbox, &local_msg, 0);
128                 }
129                 proc_notify(p, vcoreid);
130         }
131 out:
132         /* Return to the old address space.  We switched to p in the first place if
133          * it wasn't the same as the original current (old_proc). */
134         if (old_proc != p) {
135                 pcpui->cur_proc = old_proc;
136                 if (old_proc)
137                         lcr3(old_proc->env_cr3);
138                 else
139                         lcr3(boot_cr3);
140         }
141 }
142
143 /* Send an event for the kernel event ev_num.  These are the "one sided" kernel
144  * initiated events, that require a lookup of the ev_q in procdata.  This is
145  * roughly equivalent to the old "proc_notify()" */
146 void send_kernel_event(struct proc *p, struct event_msg *msg, uint32_t vcoreid)
147 {
148         uint16_t ev_num = msg->ev_type;
149         assert(ev_num < MAX_NR_EVENT);          /* events start at 0 */
150         struct event_queue *ev_q = p->procdata->kernel_evts[ev_num];
151         if (ev_q)
152                 send_event(p, ev_q, msg, vcoreid);
153 }
154
155 /* Writes the msg to the vcpd/default mbox of the vcore.  Doesn't need to check
156  * for current, or care about what the process wants. */
157 void post_vcore_event(struct proc *p, struct event_msg *msg, uint32_t vcoreid)
158 {
159         struct event_mbox *vcore_mbox;
160         /* kernel address of the vcpd mbox */
161         vcore_mbox = get_proc_ev_mbox(p, vcoreid);
162         post_ev_msg(vcore_mbox, msg, 0);                /* no chance for a NOMSG either */
163 }