Set errno when only errstr was called
[akaros.git] / kern / src / event.c
index 7589f6a..314c706 100644 (file)
 #include <stdio.h>
 #include <assert.h>
 #include <pmap.h>
+#include <schedule.h>
+
+/* Userspace could give us a vcoreid that causes us to compute a vcpd that is
+ * outside procdata.  If we hit UWLIM, then we've gone farther than we should.
+ * We check the vcoreid, instead of the resulting address, to avoid issues like
+ * address wrap-around. */
+static bool vcoreid_is_safe(uint32_t vcoreid)
+{
+       /* MAX_NUM_VCORES == MAX_NUM_CPUS (check procinfo/procdata) */
+       return vcoreid < MAX_NUM_CPUS;
+}
 
 /* Note these three helpers return the user address of the mbox, not the KVA.
  * Load current to access this, and it will work for any process. */
@@ -41,7 +52,7 @@ static struct event_mbox *get_vcpd_mbox(uint32_t vcoreid, int ev_flags)
 static bool can_msg_vcore(uint32_t vcoreid)
 {
        struct preempt_data *vcpd = &__procdata.vcore_preempt_data[vcoreid];
-       return vcpd->can_rcv_msg;
+       return atomic_read(&vcpd->flags) & VC_CAN_RCV_MSG;
 }
 
 /* Says a vcore can be messaged.  Only call this once you are sure this is true
@@ -49,7 +60,7 @@ static bool can_msg_vcore(uint32_t vcoreid)
 static void set_vcore_msgable(uint32_t vcoreid)
 {
        struct preempt_data *vcpd = &__procdata.vcore_preempt_data[vcoreid];
-       vcpd->can_rcv_msg = TRUE;
+       atomic_or(&vcpd->flags, VC_CAN_RCV_MSG);
 }
 
 /* Posts a message to the mbox, subject to flags.  Feel free to send 0 for the
@@ -60,7 +71,7 @@ static void set_vcore_msgable(uint32_t vcoreid)
 static void post_ev_msg(struct proc *p, struct event_mbox *mbox,
                         struct event_msg *msg, int ev_flags)
 {
-       printd("[kernel] Sending event type %d to mbox %08p\n", msg->ev_type, mbox);
+       printd("[kernel] Sending event type %d to mbox %p\n", msg->ev_type, mbox);
        /* Sanity check */
        assert(p);
        /* If they just want a bit (NOMSG), just set the bit */
@@ -93,6 +104,7 @@ static void post_vc_msg(struct proc *p, uint32_t vcoreid,
  * sure it is mapped (slight optimization) */
 static void try_notify(struct proc *p, uint32_t vcoreid, int ev_flags)
 {
+       /* Note this is an unlocked-peek at the vcoremap */
        if ((ev_flags & EVENT_IPI) && vcore_is_mapped(p, vcoreid))
                proc_notify(p, vcoreid);
 }
@@ -106,7 +118,7 @@ static void spam_vcore(struct proc *p, uint32_t vcoreid,
        try_notify(p, vcoreid, ev_flags);
 }
 
-/* Attempts to message a vcore that may or may not have 'can_rcv_msg' set.  If
+/* Attempts to message a vcore that may or may not have VC_CAN_RCV_MSG set.  If
  * so, we'll post the message and the message will eventually get dealt with
  * (when the vcore runs or when it is preempte-recovered). */
 static bool try_spam_vcore(struct proc *p, uint32_t vcoreid,
@@ -158,7 +170,7 @@ static bool spam_list_member(struct vcore_tailq *list, struct proc *p,
                 * will either be turned on later, or have a preempt message sent about
                 * their demise.
                 *
-                * We race on list membership (and not exclusively 'can_rcv_msg', so
+                * We race on list membership (and not exclusively VC_CAN_RCV_MSG, so
                 * that when it fails we can get a new vcore to try (or know WHP there
                 * are none). */
                vc_first = TAILQ_FIRST(list);
@@ -185,7 +197,7 @@ static bool spam_list_member(struct vcore_tailq *list, struct proc *p,
  * this must be able to handle spurious reads, since more than one vcore is
  * likely to get the message and handle it.
  *
- * We try the desired vcore, using 'can_rcv_msg'.  Failing that, we'll search
+ * We try the desired vcore, using VC_CAN_RCV_MSG.  Failing that, we'll search
  * the online and then the bulk_preempted lists.  These lists serve as a way to
  * find likely messageable vcores.  spam_list_member() helps us with them,
  * failing if anything seems to go wrong.  At which point we just lock and try
@@ -228,11 +240,8 @@ static void spam_public_msg(struct proc *p, struct event_msg *ev_msg,
                         * right as another vcore was yielding, and the preempted
                         * message was sent after the last vcore yielded (which caused
                         * us to be WAITING */
-                       if (p->state == PROC_WAITING) {
-                               spin_lock(&p->proc_lock);
-                               __proc_wakeup(p);       /* internally, this double-checks WAITING */
-                               spin_unlock(&p->proc_lock);
-                       }
+                       if (p->state == PROC_WAITING)
+                               proc_wakeup(p); /* internally, this double-checks WAITING */
                        return;
                }
        }
@@ -276,9 +285,9 @@ ultimate_fallback:
        /* The first event to catch the process with no online/bp vcores will need
         * to wake it up.  (We could be RUNNABLE_M here if another event already woke
         * us.) and we didn't get lucky with the penultimate fallback.
-        * __proc_wakeup() will check for WAITING. */
-       __proc_wakeup(p);
+        * proc_wakeup (and __proc_wakeup()) will check for WAITING. */
        spin_unlock(&p->proc_lock);
+       proc_wakeup(p);
        return;
 }
 
@@ -322,21 +331,6 @@ static void send_indir(struct proc *p, struct event_queue *ev_q,
        spam_public_msg(p, &local_msg, vcoreid, ev_q->ev_flags & EVENT_SPAM_FLAGS);
 }
 
-/* Helper that alerts a vcore, by IPI and/or INDIR, that it needs to check the
- * ev_q.  send_indir() eventually Handles FALLBACK and other tricky things. 
- * alerted. */
-static void alert_vcore(struct proc *p, struct event_queue *ev_q,
-                        uint32_t vcoreid)
-{
-       /* INDIR will also call try_notify (IPI) later */
-       if (ev_q->ev_flags & EVENT_INDIR) {
-               send_indir(p, ev_q, vcoreid);
-       } else {
-               /* they may want an IPI despite not wanting an INDIR */
-               try_notify(p, vcoreid, ev_q->ev_flags);
-       }
-}
-
 /* Send an event to ev_q, based on the parameters in ev_q's flag.  We don't
  * accept null ev_qs, since the caller ought to be checking before bothering to
  * make a msg and send it to the event_q.  Vcoreid is who the kernel thinks the
@@ -349,58 +343,82 @@ void send_event(struct proc *p, struct event_queue *ev_q, struct event_msg *msg,
        struct proc *old_proc;
        struct event_mbox *ev_mbox = 0;
        assert(p);
-       printd("[kernel] sending msg to proc %08p, ev_q %08p\n", p, ev_q);
+       printd("[kernel] sending msg to proc %p, ev_q %p\n", p, ev_q);
        if (!ev_q) {
                warn("[kernel] Null ev_q - kernel code should check before sending!");
                return;
        }
        if (!is_user_rwaddr(ev_q, sizeof(struct event_queue))) {
                /* Ought to kill them, just warn for now */
-               warn("[kernel] Illegal addr for ev_q");
+               printk("[kernel] Illegal addr for ev_q\n");
+               return;
+       }
+       /* This should be caught by "future technology" that can tell when the
+        * kernel PFs on the user's behalf.  For now, we catch common userspace bugs
+        * (had this happen a few times). */
+       if (!PTE_ADDR(ev_q)) {
+               printk("[kernel] Bad addr %p for ev_q\n", ev_q);
                return;
        }
        /* ev_q is a user pointer, so we need to make sure we're in the right
         * address space */
        old_proc = switch_to(p);
-       /* Get the mbox and vcoreid */
-       /* If we're going with APPRO, we use the kernel's suggested vcore's ev_mbox.
-        * vcoreid is already what the kernel suggests. */
-       if (ev_q->ev_flags & EVENT_VCORE_APPRO) {
-               /* flags determine if it's private (like a preempt pending) or not */
-               ev_mbox = get_vcpd_mbox(vcoreid, ev_q->ev_flags);
-       } else {        /* common case */
-               ev_mbox = ev_q->ev_mbox;
-               vcoreid = ev_q->ev_vcore;
+       /* If we're an _S, just spam vcore0, and wake up if necessary. */
+       if (!__proc_is_mcp(p)) {
+               spam_vcore(p, 0, msg, ev_q->ev_flags);
+               wrmb(); /* don't let the notif_pending write pass the state read */
+               /* using the same pattern as in spam_public (which can have multiple
+                * unblock callbacks */
+               if (p->state == PROC_WAITING)
+                       proc_wakeup(p);
+               goto out;
        }
-       /* Check on the style, which could affect our mbox selection.  Other styles
-        * would go here (or in similar functions we call to).  Important thing is
-        * we come out knowing which vcore to send to in the event of an IPI/INDIR,
-        * and we know what mbox to post to. */
+       /* Get the vcoreid that we'll message (if appropriate).  For INDIR and
+        * SPAMMING, this is the first choice of a vcore, but other vcores might get
+        * it.  Common case is !APPRO and !ROUNDROBIN.  Note we are clobbering the
+        * vcoreid parameter. */
+       if (!(ev_q->ev_flags & EVENT_VCORE_APPRO))
+               vcoreid = ev_q->ev_vcore;       /* use the ev_q's vcoreid */
+       /* Note that RR overwrites APPRO */
        if (ev_q->ev_flags & EVENT_ROUNDROBIN) {
-               /* Pick a vcore, and if we don't have a mbox yet, pick that vcore's
-                * default mbox.  Assuming ev_vcore was the previous one used.  Note
-                * that round-robin overrides the passed-in vcoreid. */
+               /* Pick a vcore, round-robin style.  Assuming ev_vcore was the previous
+                * one used.  Note that round-robin overrides the passed-in vcoreid.
+                * Also note this may be 'wrong' if num_vcores changes. */
                vcoreid = (ev_q->ev_vcore + 1) % p->procinfo->num_vcores;
                ev_q->ev_vcore = vcoreid;
-               /* Note that the style of not having a specific ev_mbox may go away.  I
-                * can't think of legitimate uses of this for now, since things that are
-                * RR probably are non-vcore-business, and thus inappropriate for a VCPD
-                * ev_mbox. */
-               if (!ev_mbox)
-                       ev_mbox = get_vcpd_mbox(vcoreid, ev_q->ev_flags);
        }
+       if (!vcoreid_is_safe(vcoreid)) {
+               /* Ought to kill them, just warn for now */
+               printk("[kernel] Vcoreid %d unsafe! (too big?)\n", vcoreid);
+               goto out;
+       }
+       /* If we're a SPAM_PUBLIC, they just want us to spam the message.  Note we
+        * don't care about the mbox, since it'll go to VCPD public mboxes, and
+        * we'll prefer to send it to whatever vcoreid we determined at this point
+        * (via APPRO or whatever). */
+       if (ev_q->ev_flags & EVENT_SPAM_PUBLIC) {
+               spam_public_msg(p, msg, vcoreid, ev_q->ev_flags & EVENT_SPAM_FLAGS);
+               goto out;
+       }
+       /* We aren't spamming and we know the default vcore, and now we need to
+        * figure out which mbox to use.  If they provided an mbox, we'll use it.
+        * If not, we'll use a VCPD mbox (public or private, depending on the
+        * flags). */
+       ev_mbox = ev_q->ev_mbox;
+       if (!ev_mbox)
+               ev_mbox = get_vcpd_mbox(vcoreid, ev_q->ev_flags);
        /* At this point, we ought to have the right mbox to send the msg to, and
-        * which vcore to send an IPI to (if we send one).  The mbox could be the
-        * vcore's vcpd ev_mbox.  The vcoreid only matters for IPIs and INDIRs. */
+        * which vcore to alert (IPI/INDIR) (if applicable).  The mbox could be the
+        * vcore's vcpd ev_mbox. */
        if (!ev_mbox) {
-               /* this is a process error */
+               /* This shouldn't happen any more, this is more for sanity's sake */
                warn("[kernel] ought to have an mbox by now!");
                goto out;
        }
        /* Even if we're using an mbox in procdata (VCPD), we want a user pointer */
        if (!is_user_rwaddr(ev_mbox, sizeof(struct event_mbox))) {
                /* Ought to kill them, just warn for now */
-               warn("[kernel] Illegal addr for ev_mbox");
+               printk("[kernel] Illegal addr for ev_mbox\n");
                goto out;
        }
        /* We used to support no msgs, but quit being lazy and send a 'msg'.  If the
@@ -408,14 +426,15 @@ void send_event(struct proc *p, struct event_queue *ev_q, struct event_msg *msg,
         * vehicle for sending the ev_type. */
        assert(msg);
        post_ev_msg(p, ev_mbox, msg, ev_q->ev_flags);
-       wmb();  /* ensure ev_msg write is before alert_vcore() */
-       /* Help out userspace a bit by checking for a potentially confusing bug */
-       if ((ev_mbox == get_vcpd_mbox_pub(vcoreid)) &&
-           (ev_q->ev_flags & EVENT_INDIR))
-               printk("[kernel] User-bug: ev_q has an INDIR with a VCPD ev_mbox!\n");
-       /* Prod/alert a vcore with an IPI or INDIR, if desired */
-       if ((ev_q->ev_flags & (EVENT_IPI | EVENT_INDIR)))
-               alert_vcore(p, ev_q, vcoreid);
+       wmb();  /* ensure ev_msg write is before alerting the vcore */
+       /* Prod/alert a vcore with an IPI or INDIR, if desired.  INDIR will also
+        * call try_notify (IPI) later */
+       if (ev_q->ev_flags & EVENT_INDIR) {
+               send_indir(p, ev_q, vcoreid);
+       } else {
+               /* they may want an IPI despite not wanting an INDIR */
+               try_notify(p, vcoreid, ev_q->ev_flags);
+       }
        /* Fall through */
 out:
        /* Return to the old address space. */
@@ -454,3 +473,13 @@ void post_vcore_event(struct proc *p, struct event_msg *msg, uint32_t vcoreid,
        post_vc_msg(p, vcoreid, get_vcpd_mbox(vcoreid, ev_flags), msg, ev_flags);
        switch_back(p, old_proc);
 }
+
+/* Attempts to send a posix signal to the process.  If they do not have an ev_q
+ * registered for EV_POSIX_SIGNAL, then nothing will happen. */
+void send_posix_signal(struct proc *p, int sig_nr)
+{
+       struct event_msg local_msg = {0};
+       local_msg.ev_type = EV_POSIX_SIGNAL;
+       local_msg.ev_arg1 = sig_nr;
+       send_kernel_event(p, &local_msg, 0);
+}