Revamped event interfaces (XCC)
authorBarret Rhoden <brho@cs.berkeley.edu>
Thu, 17 Feb 2011 20:23:51 +0000 (12:23 -0800)
committerKevin Klues <klueska@cs.berkeley.edu>
Thu, 3 Nov 2011 00:35:57 +0000 (17:35 -0700)
Rebuild your XCC, or at least update the kernel headers.

The event_queue explains how a process would like to receive an event.
This replaces the old notification stuff.  Notification now refers to
'active notification', aka IPIs to userspace.  Check the documentation
for more info.  Test apps show how to use it, for now.

22 files changed:
Documentation/async_events.txt [new file with mode: 0644]
kern/include/event.h [new file with mode: 0644]
kern/include/process.h
kern/include/ros/event.h [new file with mode: 0644]
kern/include/ros/notification.h [deleted file]
kern/include/ros/procdata.h
kern/include/umem.h
kern/src/Makefrag
kern/src/event.c [new file with mode: 0644]
kern/src/monitor.c
kern/src/process.c
kern/src/syscall.c
kern/src/umem.c
tests/eth_audio.c
tests/mhello.c
tests/msr_dumb_while.c
tests/msr_get_cores.c
tests/msr_get_singlecore.c
tests/msr_nice_while.c
user/include/parlib.h
user/parlib/pthread.c
user/parlib/syscall.c

diff --git a/Documentation/async_events.txt b/Documentation/async_events.txt
new file mode 100644 (file)
index 0000000..ffc148b
--- /dev/null
@@ -0,0 +1,352 @@
+async_events.txt
+Barret Rhoden
+
+1. Overview
+2. Async Syscalls and I/O
+3. Event Delivery / Notification
+4. Misc Things That Aren't Sorted Completely:
+
+1. Overview
+====================
+1.1 Event Handling / Notifications / Async IO Issues:
+------------------------------------------------------------------
+Basically, syscalls use the ROS event delivery mechanisms, redefined and
+described below.  Syscalls use the event delivery just like any other
+subsystem would that wants to deliver messages to a process.  The only other
+example we have right now are the "kernel notifications", which are the
+one-sided, kernel-initiated messages that the kernel sends to a process.
+
+Overall, there are several analogies from how vcores work to how the OS
+handles interrupts.  This is a result of trying to make vcores run like
+virtual multiprocessors, in control of their resources and aware of the lower
+levels of the system.  This analogy has guided much of how the vcore layer
+works.  Whenever we have issues with the 2-lsched, realize the amount of
+control they want means using solutions that the OS must do too.
+
+Note that there is some pointer chasing going on, though we try to keep it to
+a minimum.  Any time the kernel chases a pointer, it needs to make sure it is
+in the R/W section of userspace, though it doesn't need to check if the page
+is present.  There's more info in the Page Fault sections of the
+documentation.  (Briefly, if the kernel PFs on a user address, it will either
+block and handle the PF, or if the address was unmapped, it will kill the
+process).
+
+1.2 Some Definitions:
+---------------------------------------
+ev_q, event_queue, event_q: all terms used interchangeably with each other.
+They are the endpoint for communicating messages to a process, encapsulating
+the method of delivery (such as IPI or not) with where to save the message.
+
+Vcore context: the execution context of the virtual core on the "trampoline"
+stack.  All executions start from the top of this stack, and no stack state is
+saved between vcore_entry() calls.  All executions on here are non-blocking,
+notifications (IPIs) are disabled, and there is a specific TLS loaded.  Vcore
+context is used for running the second level scheduler (2LS), swapping between
+threads, and handling notifications.  It is analagous to "interrupt context"
+in the OS.  Any functions called from here should be brief.  Any memory
+touched must be pinned.  In Lithe terms, vcore context might be called the
+Hart / hard thread.  People often wonder if they can run out of vcore context
+directly.  Technically, you can, but you lose the ability to take any fault
+(page fault) or to get IPIs for notification.  In essence, you lose control,
+analgous to running an application in the kernel with preemption/interrupts
+disabled.  See the process documentation for more info.
+
+2LS: is the second level scheduler/framework.  This code executes in vcore
+context, and is Lithe / plugs in to Lithe (eventually).  Often used
+interchangeably with "vcore context", usually when I want to emphasize the
+scheduling nature of the code.
+
+VCPD: "virtual core preemption data".  In procdata, there is an array of
+struct preempt_data, one per vcore.  This is the default location to look for
+all things related to the management of vcores, such as its event_mbox (queue
+of incoming messages/notifications/events).  Both the kernel and the vcore
+code know to look here for a variety of things.
+
+Notif_table: This is a list of event_q*s that correspond to certain
+unexpected/"one-sided" events the kernel sends to the process.  It is similar
+to an IRQ table in the kernel.  Each event_q tells the kernel how the process
+wants to be told about the specific event type.
+
+Notifications: used to be a generic event, but now used in terms of the verb
+'notify' (do_notify()).  In older docs, passive notification is just writing a
+message somewhere.  Active notification is an IPI delivered to a vcore.  I use
+that term interchangeably with an IPI, and usually you can tell by context
+that I'm talking about an IPI going to a process (and not just the kernel).
+The details of it make it more complicated than just an IPI, but it's
+analagous.  I've start referring to notification as the IPI, and "passive
+notification" as just events, though older documentation has both meanings.
+
+BCQ: "bounded concurrent queue".  It is a fixed size array of messages
+(structs of notification events, or whatever).  It is non-blocking, supporting
+multiple producers and consumers, where the producers do not trust the
+consumers.  It is the primary mechanism for the kernel delivering message
+payloads into a process's address space.  Note that producers don't trust each
+other either (in the event of weirdness, the producers give up and say the
+buffer is full).  This means that a process can produce for one of its ev_qs
+(which is what they need to do to send message to itself).
+
+2. Async Syscalls and I/O
+====================
+The syscall struct is the contract for work with the kernel, including async
+I/O.  Lots of current OS async packages use epoll or other polling systems.
+Note the distinction between Polling and Async I/O.  Polling is about finding
+out if a call will block.  It is primarily used for sockets and pipes.  It
+does relatively nothing for disk I/O, which requires a separate async I/O
+system.  By having all syscalls be async, we can make polling a bit easier and
+more unified with the generic event code that we use for all syscalls.
+
+For instance, we can have a sys_poll syscall, which is async just like any
+other syscall.  The call can be a "one shot / non-blocking", like the current
+systems polling code, or it can also notify on change (not requiring future
+polls) via the event_q mechanisms.  If you don't want to be IPId, you can
+"poll" the syscall struct - not requiring another kernel crossing/syscall.
+
+Note that we do not tie syscalls and polling to FDs.  We do events on
+syscalls, which can be used to check FDs.  I think a bunch of polling cases
+will not be needed once we have async syscalls, but for those that remain,
+we'll have sys_poll() (or whatever).
+
+To receive an event on a syscall completion or status change, just fill in the
+event_q pointer.  If it is 0, the kernel will assume you poll the actual
+syscall struct.
+
+       struct syscall {
+               current stuff                   /* arguments, retvals */
+               struct ev_queue *               /* struct used for messaging, including IPIs*/
+               void *                                  /* used by 2LS, usually a struct u_thread * */
+       }
+
+One issue with async syscalls is that there can be too many outstanding IOs
+(normally sync calls provide feedback / don't allow you to over-request).
+Eventually, processes can exhaust kernel memory (the kthreads, specifically).
+We need a way to limit the kthreads per proc, etc.  Shouldn't be a big deal.
+
+Normally, we talk about changing the flag in a syscall to SC_DONE.  Async
+syscalls can be SC_PROGRESS (new stuff happened on it), which can trigger a
+notification event.  Some calls, like AIO or bulk accept, exist for a while
+and slowly get filled in / completed.  In the future, we'll also want a way to
+abort the in-progress syscalls (possibly any syscall!).
+
+3. Event Delivery / Notification
+====================
+3.1 Basics
+----------------------------------------------
+The mbox (mailbox) is where the actual messages go, or the overflow of a
+message is tracked.
+
+       struct ev_mbox {
+               bcq of notif_events     /* bounded buffer, multi-consumer/producer */
+               overflow_count
+               msg_bitmap
+       }
+       struct ev_queue {                       /* aka, event_q, ev_q, etc. */
+               struct ev_mbox * 
+               void handler(struct event_q *)
+               vcore_to_be_told
+               flags                                   /* IPI_WANTED, RR, 2L-handle-it, etc */
+       }
+       struct ev_queue_big {
+               struct ev_mbox *                /* pointing to the internal storage */
+               vcore_to_be_told
+               flags                                   /* IPI_WANTED, RR, 2L-handle-it, etc */
+               struct ev_mbox { }              /* never access this directly */
+       }
+
+The purpose of the big one is to simply embed some storage.  Still, only
+access the mbox via the pointer.  The big one can be casted (and stored as)
+the regular, so long as you know to dealloc a big one (free() knows, custom
+styles or slabs would need some help).
+
+The ev_mbox says where to put the actual message, and the flags handle things
+such as whether or not an IPI is wanted.
+
+Using pointers for the ev_q like this allows multiple event queues to use the
+same mbox.  For example, we could use the vcpd queue for both kernel-generated
+events as well as async syscall responses.  The notification table is actually
+a bunch of ev_qs, many of which could be pointing to the same vcore/vcpd-mbox,
+albeit with different flags.
+
+3.2 Kernel Notification Using Event Queues
+----------------------------------------------
+The notif_tbl/notif_methods (kernel-generated 'one-sided' events) is just an
+array of struct ev_queue*s.  Handling a notification is like any other time
+when we want to send an event.  Follow a pointer, send a message, etc.  As
+with all ev_qs, ev_mbox* points to where you want the message for the event,
+which usually is the vcpd's mbox.  If the ev_q pointer is 0, then we know the
+process doesn't want the event (equivalent to the older 'NOTIF_WANTED' flag).
+Theoretically, we can send kernel notifs to user threads.  While it isn't
+clear that anyone will ever want this, it is possible (barring other issues),
+since they are just events.
+
+Also note the flag EVENT_VCORE_APPRO.  Processes should set this for certain
+types of events where they want the kernel to send the event/IPI to the
+'appropriate' vcore.  For example, when sending a message about a preemption
+coming in, it makes sense for the kernel to send it to the vcore that is going
+to get preempted, but the application could choose to ignore the notification.
+
+There are also interfaces in the kernel to put a message in an ev_mbox
+regardless of the process's wishes (post_vcore_event()), and can send an IPI
+at any time (proc_notify()).
+
+3.3 IPIs and Indirection Events
+----------------------------------------------
+When an ev_q calls for an IPI, the kernel finds out what vcore the process
+wants.  The code already sent the message to the ev_q's mbox.  If the vcore's
+vcpd mbox is the same as the ev_q's mbox (pointer check), then just send the
+IPI.  If it is different, we need to put a message in the vcpd's mbox telling
+them "ev_q*", so the vcore knows why it got an IPI.  This level of indirection
+is only necessary when the ev_q requests an IPI and it is not the vcore using
+its vcpd mbox.  The vcore needs to know why it received an IPI.  The IPI
+(active notifcation) is merely a prodding, and the vcore needs a known place
+to look for why it was woken up.  This is a little different when dealing with
+non-specific IPIs (like Round-Robin).
+
+If the vcore gets an indirection message, it will be of type NE_EVENT (or
+whatever), with an ev_q* as the payload.
+
+In the event there are issues with this, we can introduce a flag that says we
+don't need a separate notif_event explaining the IPI: prodding the vcore was
+enough.  Either way, we can deliver event messages directly to the vcore's
+mbox / bcq.
+
+There's a slight race on changing the mbox* and the vcore number within the
+event_q.  The message could have gone to the wrong (old) vcore, but not the
+IPI.  Not a big deal - IPIs can be spurious, and the other vcore will
+eventually get it.  The real way around this is create a new ev_q and change
+the pointer (thus atomically changing the entire ev_q's contents), though this
+can be a bit tricky if you have multiple places pointing to the same ev_q
+(can't change them all at once).
+
+If you want to receive an event when a syscall completes or has a change in
+status, simply allocate an event_q, and point the syscall at it.  syscall:
+ev_q* -> "vcore for IPI, syscall message in the ev_q mbox", etc.  You can also
+point it to an existing ev_q.
+
+3.4 Application-specific Event Handling
+---------------------------------------
+So what happens when the vcore/2LS isn't handling an event queue, but has been
+"told" about it?  This "telling" is in the form of an IPI.  The vcore was
+prodded, but is not supposed to handle the event.  This is actually what
+happens now in Linux when you send signals for AIO.  It's all about who (which
+thread, in their world) is being interrupted to process the work in an
+application specific way.  The app sets the handler, with the option to have a
+thread spawned (instead of a sighandler), etc.
+
+This is not exactly the same as the case above where the ev_mbox* pointed to
+the vcore's default mbox.  That issue was just about avoiding extra messages
+(and messages in weird orders).  A vcore won't handle an ev_q if the
+message/contents of the queue aren't meant for the vcore/2LS.  For example, a
+thread can want to run its own handler, perhaps because it performs its own
+asynchronous I/O (compared to relying on the 2LS to schedule synchronous
+blocking u_threads).
+
+There are a couple ways to handle this.  Ultimately, the application is
+supposed to handle the event.  If it asked for an IPI, it is because something
+ought to be done, which really means running a handler.  If the application
+sets EV_THREAD in the ev_q's flags, the 2LS ought to spawn a thread to run the
+ev_q's handler.  If EV_JUSTHANDLEIT is set, the vcore will execute the handler
+itself.  Careful with this, since the only memory it touches must be pinned,
+the function must not block (this is only true for the handlers called
+directly out of vcore context), and it should return quickly.
+
+Note that in either case, vcore-written code (library code) does not look at
+the contents of the notification event.  Also Note the handler takes the whole
+event_queue, and not a specific message.  It is more flexible, can handle
+multiple specific events, and doesn't require the vcore code to dequeue and
+event and either pass by value or allocate more memory.
+
+Continuing the analogy between vcores getting IPIs and the OS getting HW
+interrupts, what goes on in vcore context is like what goes on in interrupt
+context, and the threaded handler is like running a threaded interrupt handler
+(in Linux).  In the ROS world, it is like having the interrupt handler kick
+off a kernel message to defer the work out of interrupt context.
+
+If neither of the application-specific handling flags are set, the vcore will
+respond to the IPI by attempting to handle the event on its own (lookup table
+based on the type of event (like "syscall complete")).  If you didn't want the
+vcore to handle it, then you shouldn't have asked for an IPI.  Those flags are
+the means by which the vcore can distinguish between its event_qs and the
+applications.  It does not make sense otherwise to send the vcore an IPI and
+an event_q, but not tell give the code the info it needs to handle it.
+
+In the future, we might have the ability to block a u_thread on an event_q, so
+we'll have other EV_ flags to express this, and probably a void*.  This may
+end up being redudant, since u_threads will be able to block on syscalls (and
+not necessarily IPIs sent to vcores).
+
+As a side note, a vcore can turn off the IPI wanted flag at any time.  For
+instance, when it spawns a thread to handle an ev_q, the vcore can turn off
+IPI wanted on that event_q, and the thread handler can turn it back on when it
+is done processing and wants to be re-IPId.  The reason for this is to avoid
+taking future IPIs (once we leave vcore context, IPIs are enabled) to let us
+know about an event for which a handler is already running.
+
+3.5 Overflowed/Missed Messages in the VCPD 
+---------------------------------------
+All event_q's requesting IPIs ought to register with the 2LS.  This is for
+recovering in case the vcpd's mbox overflowed, and the vcore knows it missed a
+NE_EVENT type message.  At that point, it would have to check all of its
+IPI-based queues.  To do so, it could check to see if the mbox has any
+messages, though in all likelihood, we'll just act as if there was a message
+on each of the queues (all such handlers should be able to handle spurious
+IPIs anyways).  This is analagous to how the OS's block drivers don't solely
+rely on receiving an interrupt (they deal with it via timeouts).  Any user
+code requiring an IPI must do this.  Any code that runs better due to getting
+the IPI ought to do this.
+
+We could imagine having a thread spawned to handle an ev_q, and the vcore
+never has to touch the ev_q (which might make it easier for memory
+allocation).  This isn't a great idea, but I'll still explain it.  In the
+notif_ev message sent to the vcore, it has the event_q*.  We could also send a
+flag with the same info as in the event_q's flags, and also send the handler.
+The problem with this is that it isn't resilient to failure.  If there was a
+message overflow, it would have the check the event_q (which was registered
+before) anyway, and could potentially page fault there.  Also the kernel would
+have faulted on it (and read it in) back when it tried to read those values.
+It's somewhat moot, since we're going to have an allocator that pins event_qs.
+
+3.6 Round-Robin or Other IPI-delivery styles
+---------------------------------------
+In the same way that the IOAPIC can deliver interrupts to a group of cores,
+round-robinning between them, so can we imagine processes wanting to
+distribute the IPI/active notification of events across its vcores.  This is
+only meaningful is the NOTIF_IPI_WANTED flag is set.
+
+Eventually we'll support this, via a flag in the event_q.  When
+NE_ROUND_ROBIN, or whatever, is set a couple things will happen.  First, the
+vcore field will be used in a "delivery-specific" manner.  In the case of RR,
+it will probably be the most recent destination.  Perhaps it will be a bitmask
+of vcores available to receive.  More important is the event_mbox*.  If it is
+set, then the event message will be sent there.  Whichever vcore gets selected
+will receive an IPI, and its vcpd mbox will get a NE_EVENT message.  If the
+event_mbox* is 0, then the actual message will get delivered to the vcore's
+vcpd mbox (the default location).
+
+3.7 Event_q-less Notifications
+---------------------------------------
+Some events needs to be delivered directly to the vcore, regardless of any
+event_qs.  This happens currently when we bypass the notification table (e.g.,
+sys_self_notify(), preemptions, etc).  These notifs will just use the vcore's
+default mbox.  In essence, the ev_q is being generated/sent with the call.
+The implied/fake ev_q points to the vcpd's mbox, with the given vcore set, and
+with IPI_WANTED set.  It is tempting to make those functions take a
+dynamically generated ev_q, though more likely we'll just use the lower level
+functions in the kernel, much like the Round Robin set will need to do.  No
+need to force things to fit just for the sake of using a 'solution'.  We want
+tools to make solutions, not packaged solutions.
+
+4. Misc Things That Aren't Sorted Completely:
+====================
+4.1 What about short handlers?
+---------------------------------------
+Once we sort the other issues, we can ask for them via a flag in the event_q,
+and run the handler in the event_q struct.
+
+4.2 What about blocking on a syscall?
+---------------------------------------
+The current plan is to set a flag, and let the kernel go from there.  The
+kernel knows which process it is, since that info is saved in the kthread that
+blocked.  One issue is that the process could muck with that flag and then go
+to sleep forever.  To deal with that, maybe we'd have a long running timer to
+reap those.  Arguably, it's like having a process while(1).  You can screw
+yourself, etc.  Killing the process would still work.
diff --git a/kern/include/event.h b/kern/include/event.h
new file mode 100644 (file)
index 0000000..fae1d95
--- /dev/null
@@ -0,0 +1,19 @@
+/* Copyright (c) 2011 The Regents of the University of California
+ * Barret Rhoden <brho@cs.berkeley.edu>
+ * See LICENSE for details.
+ *
+ * Kernel utility functions for sending events and notifications (IPIs) to
+ * processes. */
+
+#ifndef ROS_KERN_EVENT_H
+#define ROS_KERN_EVENT_H
+
+#include <ros/event.h>
+#include <process.h>
+
+void send_event(struct proc *p, struct event_queue *ev_q, struct event_msg *msg,
+                uint32_t vcoreid);
+void send_kernel_event(struct proc *p, struct event_msg *msg, uint32_t vcoreid);
+void post_vcore_event(struct proc *p, struct event_msg *msg, uint32_t vcoreid);
+
+#endif /* ROS_KERN_EVENT_H */
index f193217..21f661d 100644 (file)
@@ -9,7 +9,7 @@
 #define ROS_KERN_PROCESS_H
 
 #include <ros/common.h>
-#include <ros/notification.h>
+#include <ros/event.h>
 #include <trap.h>
 #include <atomic.h>
 #include <kref.h>
@@ -81,9 +81,7 @@ void proc_restartcore(void);
 void proc_destroy(struct proc *SAFE p);
 void __proc_yield_s(struct proc *p, struct trapframe *tf);
 void proc_yield(struct proc *SAFE p, bool being_nice);
-void do_notify(struct proc *p, uint32_t vcoreid, unsigned int notif,
-               struct notif_event *ne);
-void proc_notify(struct proc *p, unsigned int notif, struct notif_event *ne);
+void proc_notify(struct proc *p, uint32_t vcoreid);
 
 /* Exposed for sys_getvcoreid(), til it's unnecessary */
 uint32_t proc_get_vcoreid(struct proc *SAFE p, uint32_t pcoreid);
@@ -152,4 +150,4 @@ void print_idlecoremap(void);
 void print_allpids(void);
 void print_proc_info(pid_t pid);
 
-#endif // !ROS_KERN_PROCESS_H
+#endif /* !ROS_KERN_PROCESS_H */
diff --git a/kern/include/ros/event.h b/kern/include/ros/event.h
new file mode 100644 (file)
index 0000000..56ef5ce
--- /dev/null
@@ -0,0 +1,108 @@
+/* Copyright (c) 2010-2011 The Regents of the University of California
+ * Barret Rhoden <brho@cs.berkeley.edu>
+ * See LICENSE for details.
+ *
+ * Kernel interface for event/notification delivery and preemption. */
+
+#ifndef ROS_INC_EVENT_H
+#define ROS_INC_EVENT_H
+
+#include <ros/common.h>
+#include <ros/atomic.h>
+#include <ros/bcq_struct.h>
+#include <ros/arch/trapframe.h>
+
+/* Event Delivery Flags. */
+#define EVENT_IPI                              0x001   /* IPI the core */
+#define EVENT_NOMSG                            0x002   /* just send the bit, not the msg */
+#define EVENT_ROUNDROBIN               0x004   /* pick a vcore, RR style */
+#define EVENT_VCORE_APPRO              0x008   /* send to where the kernel wants */
+
+/* Event Message Types */
+#define EV_NONE                                         0
+#define EV_PREEMPT_PENDING              1
+#define EV_GANG_PREMPT_PENDING  2
+#define EV_VCORE_REVOKE                         3
+#define EV_GANG_RETURN                  4
+#define EV_USER_IPI                             5
+#define EV_PAGE_FAULT                   6
+#define EV_ALARM                                7
+#define EV_EVENT                                8
+#define EV_FREE_APPLE_PIE               9
+#define NR_EVENT_TYPES                 10 /* keep me last */
+
+/* Will probably have dynamic notifications later */
+#define MAX_NR_DYN_EVENT               25
+#define MAX_NR_EVENT                   (NR_EVENT_TYPES + MAX_NR_DYN_EVENT)
+
+/* Want to keep this small and generic, but add items as you need them.  One
+ * item some will need is an expiration time, which ought to be put in the 64
+ * bit arg.  Will need tweaking / thought as we come up with events.  These are
+ * what get put on the per-core queue in procdata. */
+struct event_msg {
+       uint16_t                                        ev_type;
+       uint16_t                                        ev_arg1;
+       uint32_t                                        ev_arg2;
+       void                                            *ev_arg3;
+       uint64_t                                        ev_arg4;
+};
+
+/* Random numbers */
+#define NR_BCQ_EVENTS 10               /* nr events in a bcq */
+#define NR_BCQ_EV_LOOPS 4              /* how many loops to try to enqueue */
+
+DEFINE_BCQ_TYPES(event_msg, struct event_msg, NR_BCQ_EVENTS);
+
+/* Structure for storing / receiving event messages.  An overflow causes the
+ * bit of the event to get set in the bitmap.  You can also have just the bit
+ * sent (and no message). */
+struct event_mbox {
+       struct event_msg_bcq            ev_msgs;
+       uint8_t                                         ev_bitmap[(MAX_NR_EVENT - 1) / 8 + 1];
+       unsigned int                            ev_overflows;
+};
+
+/* The kernel sends messages to this structure, which describes how and where
+ * to receive messages, including optional IPIs. */
+struct event_queue {
+       struct event_mbox                       *ev_mbox;
+       int                                                     ev_flags;
+       uint32_t                                        ev_vcore;
+       void                                            (*ev_handler)(struct event_queue *);
+};
+
+/* Big version, contains storage space for the ev_mbox.  Never access the
+ * internal mbox directly. */
+struct event_queue_big {
+       struct event_mbox                       *ev_mbox;
+       int                                                     ev_flags;
+       uint32_t                                        ev_vcore;
+       void                                            (*ev_handler)(struct event_queue *);
+       struct event_mbox                       ev_imbox;
+};
+
+/* Per-core data about preemptions and notifications */
+struct preempt_data {
+       struct user_trapframe           preempt_tf;
+       struct ancillary_state          preempt_anc;
+       struct user_trapframe           notif_tf;
+       uintptr_t                                       transition_stack;       /* advertised by the user */
+       bool                                            notif_enabled;          /* vcore willing to recv */
+       bool                                            notif_pending;          /* notif k_msg on the way */
+       seq_ctr_t                                       preempt_tf_valid;
+       struct event_mbox                       ev_mbox;
+};
+
+/* Structs for different types of events that need parameters. */
+// TODO: think about this a bit.  And don't want to make them til we need them.
+
+/* Example: want the vcoreid of what was lost. */
+struct ev_vcore_revoke {
+       uint16_t                                        type;
+       uint16_t                                        pad1;
+       uint32_t                                        vcoreid;
+       void                                            *pad3;
+       uint64_t                                        pad4;
+};
+
+#endif /* ROS_INC_EVENT_H */
diff --git a/kern/include/ros/notification.h b/kern/include/ros/notification.h
deleted file mode 100644 (file)
index ff55cb7..0000000
+++ /dev/null
@@ -1,87 +0,0 @@
-/* Copyright (c) 2010 The Regents of the University of California
- * Barret Rhoden <brho@cs.berkeley.edu>
- * See LICENSE for details.
- *
- * Kernel interface for notification delivery and preemption. */
-
-#ifndef ROS_INC_NOTIFICATION_H
-#define ROS_INC_NOTIFICATION_H
-
-#include <ros/common.h>
-#include <ros/atomic.h>
-#include <ros/bcq_struct.h>
-#include <ros/arch/trapframe.h>
-
-/* How/If a process wants to be notified about an event */
-struct notif_method {
-       uint32_t                                vcoreid;        /* which vcore to notify */
-       int                                             flags;
-};
-
-/* Notification Flags.  vcore0 stuff might be implemented. */
-#define NOTIF_WANTED                   0x001   /* wanted, process-wide */
-#define NOTIF_IPI                              0x002   /* IPI the core */
-#define NOTIF_MSG                              0x004   /* send a message (notif event) */
-#define NOTIF_VCORE0_IPI               0x008   /* fall back to vcore0 for an IPI */
-#define NOTIF_VCORE0_EVENT             0x010   /* fall back to vcore0 for an event */
-
-/* Notification Event Types */
-#define NE_NONE                                         0
-#define NE_PREEMPT_PENDING              1
-#define NE_GANG_PREMPT_PENDING  2
-#define NE_VCORE_REVOKE                         3
-#define NE_GANG_RETURN                  4
-#define NE_USER_IPI                             5
-#define NE_PAGE_FAULT                   6
-#define NE_ALARM                                7
-#define NE_FREE_APPLE_PIE               8
-#define NE_ETC_ETC_ETC                  9
-#define NR_NOTIF_TYPES                 10 /* keep me last */
-
-/* Will probably have dynamic notifications later */
-#define MAX_NR_DYN_NOTIF               25
-#define MAX_NR_NOTIF                   (NR_NOTIF_TYPES + MAX_NR_DYN_NOTIF)
-
-/* Want to keep this small and generic, but add items as you need them.  One
- * item some will need is an expiration time, which ought to be put in the 64
- * bit arg.  Will need tweaking / thought as we come up with events.  These are
- * what get put on the per-core queue in procdata. */
-struct notif_event {
-       uint16_t                                ne_type;
-       uint16_t                                ne_arg1;
-       uint32_t                                ne_arg2;
-       uint64_t                                ne_arg3;
-       uint64_t                                ne_arg4;
-};
-
-#define NR_PERCORE_EVENTS 10 // whatever
-
-DEFINE_BCQ_TYPES(notif_evt, struct notif_event, NR_PERCORE_EVENTS);
-
-/* Per-core data about preemptions and notifications */
-struct preempt_data {
-       struct user_trapframe   preempt_tf;
-       struct ancillary_state  preempt_anc;
-       struct user_trapframe   notif_tf;
-       uintptr_t                               transition_stack;       /* advertised by the user */
-       bool                                    notif_enabled;          /* vcore is willing to receive*/
-       bool                                    notif_pending;          /* notif k_msg on the way */
-       seq_ctr_t                               preempt_tf_valid;
-       uint8_t                                 notif_bmask[(MAX_NR_NOTIF - 1) / 8 + 1];
-       struct notif_evt_bcq    notif_evts;
-       unsigned int                    event_overflows;
-};
-
-/* Structs for different types of events that need parameters. */
-// TODO: think about this a bit.  And don't want to make them til we need them.
-
-/* Example: want the vcoreid of what was lost. */
-struct ne_vcore_revoke {
-       uint16_t                                ne_type;
-       uint16_t                                ne_pad1;
-       uint32_t                                ne_vcoreid;
-       uint64_t                                ne_pad3;
-       uint64_t                                ne_pad4;
-};
-
-#endif /* ROS_INC_NOTIFICATION_H */
index 2d92cf6..de898b2 100644 (file)
@@ -10,7 +10,7 @@
 #include <ros/arch/arch.h>
 #include <ros/common.h>
 #include <ros/procinfo.h>
-#include <ros/notification.h>
+#include <ros/event.h>
 
 typedef struct procdata {
        syscall_sring_t                 syscallring;
@@ -22,7 +22,7 @@ typedef struct procdata {
 #endif
        /* glibc relies on stuff above this point.  if you change it, you need to
         * rebuild glibc. */
-       struct notif_method             notif_methods[MAX_NR_NOTIF];
+       struct event_queue              *kernel_evts[MAX_NR_EVENT];
        /* Long range, would like these to be mapped in lazily, as the vcores are
         * requested.  Sharing MAX_NUM_CPUS is a bit weird too. */
        struct preempt_data             vcore_preempt_data[MAX_NUM_CPUS];
index f76172b..f6b4265 100644 (file)
@@ -8,6 +8,11 @@
 #include <ros/common.h>
 #include <process.h>
 
+/* Is this a valid user pointer for read/write?  It doesn't care if the address
+ * is paged out or even an unmapped region: simply if it is in part of the
+ * address space that could be RW user */
+static inline bool is_user_rwaddr(void *addr);
+
 /* Can they use the area in the manner of perm? */
 void *user_mem_check(struct proc *p, const void *DANGEROUS va, size_t len,
                      int perm);
@@ -37,3 +42,10 @@ void user_memdup_free(struct proc *p, void *va);
 char *user_strdup(struct proc *p, const char *u_string, size_t strlen);
 char *user_strdup_errno(struct proc *p, const char *u_string, size_t strlen);
 void *kmalloc_errno(int len);
+bool uva_is_kva(struct proc *p, void *uva, void *kva);
+
+/* UTOP is defined as virtual address below which a process can write */
+static inline bool is_user_rwaddr(void *addr)
+{
+       return ((uintptr_t)addr < UTOP) ? TRUE : FALSE;
+}
index 17c3f99..d6da11e 100644 (file)
@@ -51,6 +51,7 @@ KERN_SRCFILES := $(KERN_ARCH_SRCFILES) \
                  $(KERN_SRC_DIR)/kthread.c \
                  $(KERN_SRC_DIR)/eth_audio.c \
                  $(KERN_SRC_DIR)/net.c \
+                 $(KERN_SRC_DIR)/event.c \
                  $(KERN_SRC_DIR)/arsc.c
 
 # Only build files if they exist.
diff --git a/kern/src/event.c b/kern/src/event.c
new file mode 100644 (file)
index 0000000..805c633
--- /dev/null
@@ -0,0 +1,160 @@
+/* Copyright (c) 2011 The Regents of the University of California
+ * Barret Rhoden <brho@cs.berkeley.edu>
+ * See LICENSE for details.
+ *
+ * Kernel utility functions for sending events and notifications (IPIs) to
+ * processes. */
+
+#include <ros/bcq.h>
+#include <arch/bitmask.h>
+#include <event.h>
+#include <atomic.h>
+#include <process.h>
+#include <smp.h>
+#include <umem.h>
+#include <stdio.h>
+#include <assert.h>
+
+/* Note this returns the KVA of the mbox, not the user one. */
+static struct event_mbox *get_proc_ev_mbox(struct proc *p, uint32_t vcoreid)
+{
+       return &p->procdata->vcore_preempt_data[vcoreid].ev_mbox;
+}
+
+/* Posts a message to the mbox, subject to flags.  Feel free to send 0 for the
+ * flags if you don't want to give them the option of EVENT_NOMSG (which is what
+ * we do when sending an indirection event).  Make sure that if mbox is a user
+ * pointer, that you've checked it *and* have that processes address space
+ * loaded.  This can get called with a KVA for mbox. */
+static void post_ev_msg(struct event_mbox *mbox, struct event_msg *msg,
+                        int ev_flags)
+{
+       printd("Sending event type %d\n", msg->ev_type);
+       /* Sanity check */
+       if (is_user_rwaddr(mbox))
+               assert(current);
+       /* If they just want a bit (NOMSG), just set the bit */
+       if (ev_flags & EVENT_NOMSG) {
+               SET_BITMASK_BIT_ATOMIC(mbox->ev_bitmap, msg->ev_type);
+       } else {
+               /* Enqueue returns 0 on success.  On failure, set a bit. */
+               if (bcq_enqueue(&mbox->ev_msgs, msg, NR_BCQ_EVENTS, NR_BCQ_EV_LOOPS)) {
+                       atomic_inc((atomic_t)&mbox->ev_overflows); // careful here
+                       SET_BITMASK_BIT_ATOMIC(mbox->ev_bitmap, msg->ev_type);
+                       /* Catch "lots" of overflows that aren't acknowledged */
+                       if (mbox->ev_overflows > 10000)
+                               warn("proc %d has way too many overflows", current->pid);
+               }
+       }
+}
+
+/* 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
+ * message ought to go to (for IPIs).  Appropriate for things like
+ * EV_PREEMPT_PENDING, where we tell the affected vcore.  To have the message go
+ * where the kernel suggests, set EVENT_VCORE_APPRO(priate). */
+void send_event(struct proc *p, struct event_queue *ev_q, struct event_msg *msg,
+                uint32_t vcoreid)
+{
+       struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
+       struct proc *old_proc = 0;
+       struct event_mbox *ev_mbox, *vcore_mbox;
+       struct event_msg local_msg = {0};
+       if (!ev_q) {
+               warn("[kernel] Null ev_q - kernel code should check before sending!");
+               return;
+       }
+       if (!is_user_rwaddr(ev_q)) {
+               /* Ought to kill them, just warn for now */
+               warn("[kernel] Illegal addr for ev_q");
+               return;
+       }
+       /* ev_q can be a user pointer (not in procdata), so we need to make sure
+        * we're in the right address space */
+       if (pcpui->cur_proc != p) {
+               old_proc = pcpui->cur_proc;
+               /* Technically, we're storing a ref here, but our current ref on p is
+                * sufficient (so long as we don't decref below) */
+               pcpui->cur_proc = p;
+               lcr3(p->env_cr3);
+       }
+       /* If we don't follow the kernel's advice, do what the process wants */
+       if (!(ev_q->ev_flags & EVENT_VCORE_APPRO))
+               vcoreid = ev_q->ev_vcore;
+       /* Post the event.  Need to get the initial mbox (might be 0). */
+       ev_mbox = ev_q->ev_mbox;
+       /* 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, and we
+        * know what mbox to post to. */
+       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. */
+               vcoreid = (ev_q->ev_vcore + 1) % p->procinfo->num_vcores;
+               ev_q->ev_vcore = vcoreid;
+               if (!ev_mbox)
+                       ev_mbox = get_proc_ev_mbox(p, vcoreid);
+       }
+       /* 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). */
+       if (!ev_mbox) {
+               /* this is a process error */
+               warn("[kernel] ought to have an mbox by now!");
+               goto out;
+       }
+       if (!is_user_rwaddr(ev_mbox)) {
+               /* Ought to kill them, just warn for now */
+               warn("[kernel] Illegal addr for ev_mbox");
+               goto out;
+       }
+       /* We used to support no msgs, but quit being lazy and send a msg */
+       assert(msg);
+       post_ev_msg(ev_mbox, msg, ev_q->ev_flags);
+       /* Optional IPIs */
+       if (ev_q->ev_flags & EVENT_IPI) {
+               /* if the mbox we sent to isn't the default one, we need to send the
+                * vcore an ev_q indirection event */
+               vcore_mbox = get_proc_ev_mbox(p, vcoreid);
+               if (!uva_is_kva(p, ev_mbox, vcore_mbox)) {
+                       /* it is tempting to send_kernel_event(), using the ev_q for that
+                        * event, but that is inappropriate here, since we are sending to a
+                        * specific vcore */
+                       local_msg.ev_type = EV_EVENT;
+                       local_msg.ev_arg3 = ev_q;
+                       post_ev_msg(vcore_mbox, &local_msg, 0);
+               }
+               proc_notify(p, vcoreid);
+       }
+out:
+       /* Return to the old address space.  We switched to p in the first place if
+        * it wasn't the same as the original current (old_proc). */
+       if (old_proc != p) {
+               pcpui->cur_proc = old_proc;
+               if (old_proc)
+                       lcr3(old_proc->env_cr3);
+       }
+}
+
+/* Send an event for the kernel event ev_num.  These are the "one sided" kernel
+ * initiated events, that require a lookup of the ev_q in procdata.  This is
+ * roughly equivalent to the old "proc_notify()" */
+void send_kernel_event(struct proc *p, struct event_msg *msg, uint32_t vcoreid)
+{
+       uint16_t ev_num = msg->ev_type;
+       assert(ev_num < MAX_NR_EVENT);          /* events start at 0 */
+       struct event_queue *ev_q = p->procdata->kernel_evts[ev_num];
+       if (ev_q)
+               send_event(p, ev_q, msg, vcoreid);
+}
+
+/* Writes the msg to the vcpd/default mbox of the vcore.  Doesn't need to check
+ * for current, or care about what the process wants. */
+void post_vcore_event(struct proc *p, struct event_msg *msg, uint32_t vcoreid)
+{
+       struct event_mbox *vcore_mbox;
+       /* kernel address of the vcpd mbox */
+       vcore_mbox = get_proc_ev_mbox(p, vcoreid);
+       post_ev_msg(vcore_mbox, msg, 0);                /* no chance for a NOMSG either */
+}
index 3523032..4ede378 100644 (file)
@@ -25,6 +25,7 @@
 #include <syscall.h>
 #include <kmalloc.h>
 #include <elf.h>
+#include <event.h>
 
 #include <ros/timer.h>
 #include <ros/memlayout.h>
@@ -435,11 +436,12 @@ int mon_kfunc(int argc, char *NTS *NT COUNT(argc) argv, trapframe_t *tf)
        return 0;
 }
 
+/* Sending a vcoreid forces an event and an IPI/notification */
 int mon_notify(int argc, char *NTS *NT COUNT(argc) argv, trapframe_t *tf)
 {
        struct proc *p;
-       unsigned int num;
        uint32_t vcoreid;
+       struct event_msg msg = {0};
 
        if (argc < 3) {
                printk("Usage: notify PID NUM [VCOREID]\n");
@@ -450,12 +452,14 @@ int mon_notify(int argc, char *NTS *NT COUNT(argc) argv, trapframe_t *tf)
                printk("No such proc\n");
                return 1;
        }
-       num = strtol(argv[2], 0, 0);
+       msg.ev_type = strtol(argv[2], 0, 0);
        if (argc == 4) {
                vcoreid = strtol(argv[3], 0, 0);
-               do_notify(p, vcoreid, num, 0);
+               post_vcore_event(p, &msg, vcoreid);
+               proc_notify(p, vcoreid);
        } else {
-               proc_notify(p, num, 0);
+               /* o/w, try and do what they want */
+               send_kernel_event(p, &msg, 0);
        }
        kref_put(&p->kref);
        return 0;
index 81fb6e6..8319da0 100644 (file)
@@ -7,6 +7,7 @@
 #endif
 
 #include <ros/bcq.h>
+#include <event.h>
 #include <arch/arch.h>
 #include <arch/bitmask.h>
 #include <process.h>
@@ -836,42 +837,26 @@ void proc_yield(struct proc *SAFE p, bool being_nice)
        smp_idle();
 }
 
-/* If you expect to notify yourself, cleanup state and process_routine_kmsg() */
-void do_notify(struct proc *p, uint32_t vcoreid, unsigned int notif,
-               struct notif_event *ne)
+/* Sends a notification (aka active notification, aka IPI) to p's vcore.  We
+ * only send a notification if one isn't already pending and they are enabled.
+ * There's a bunch of weird cases with this, and how pending / enabled are
+ * signals between the user and kernel - check the documentation.
+ *
+ * If you expect to notify yourself, cleanup state and process_routine_kmsg() */
+void proc_notify(struct proc *p, uint32_t vcoreid)
 {
-       printd("sending notif %d to proc %p\n", notif, p);
-       assert(notif < MAX_NR_NOTIF);
-       if (ne)
-               assert(notif == ne->ne_type);
-
-       struct notif_method *nm = &p->procdata->notif_methods[notif];
        struct preempt_data *vcpd = &p->procdata->vcore_preempt_data[vcoreid];
-
-       printd("nm = %p, vcpd = %p\n", nm, vcpd);
-       /* enqueue notif message or toggle bits */
-       if (ne && nm->flags & NOTIF_MSG) {
-               if (bcq_enqueue(&vcpd->notif_evts, ne, NR_PERCORE_EVENTS, 4)) {
-                       atomic_inc((atomic_t)&vcpd->event_overflows); // careful here
-                       SET_BITMASK_BIT_ATOMIC(vcpd->notif_bmask, notif);
-               }
-       } else {
-               SET_BITMASK_BIT_ATOMIC(vcpd->notif_bmask, notif);
-       }
-
-       /* Active notification */
        /* TODO: Currently, there is a race for notif_pending, and multiple senders
         * can send an IPI.  Worst thing is that the process gets interrupted
         * briefly and the kernel immediately returns back once it realizes notifs
         * are masked.  To fix it, we'll need atomic_swapb() (right answer), or not
         * use a bool. (wrong answer). */
-       if (nm->flags & NOTIF_IPI && !vcpd->notif_pending) {
+       if (!vcpd->notif_pending) {
                vcpd->notif_pending = TRUE;
                if (vcpd->notif_enabled) {
                        /* GIANT WARNING: we aren't using the proc-lock to protect the
                         * vcoremap.  We want to be able to use this from interrupt context,
-                        * and don't want the proc_lock to be an irqsave.
-                        */
+                        * and don't want the proc_lock to be an irqsave. */
                        if ((p->state & PROC_RUNNING_M) && // TODO: (VC#) (_S state)
                                      (p->procinfo->vcoremap[vcoreid].valid)) {
                                printd("[kernel] sending notif to vcore %d\n", vcoreid);
@@ -884,28 +869,6 @@ void do_notify(struct proc *p, uint32_t vcoreid, unsigned int notif,
        }
 }
 
-/* Sends notification number notif to proc p.  Meant for generic notifications /
- * reference implementation.  do_notify does the real work.  This one mostly
- * just determines where the notif should be sent, other checks, etc.
- * Specifically, it handles the parameters of notif_methods.  If you happen to
- * notify yourself, make sure you process routine kmsgs. */
-void proc_notify(struct proc *p, unsigned int notif, struct notif_event *ne)
-{
-       assert(notif < MAX_NR_NOTIF); // notifs start at 0
-       struct notif_method *nm = &p->procdata->notif_methods[notif];
-       struct notif_event local_ne;
-
-       /* Caller can opt to not send an NE, in which case we use the notif */
-       if (!ne) {
-               ne = &local_ne;
-               ne->ne_type = notif;
-       }
-
-       if (!(nm->flags & NOTIF_WANTED))
-               return;
-       do_notify(p, nm->vcoreid, ne->ne_type, ne);
-}
-
 /************************  Preemption Functions  ******************************
  * Don't rely on these much - I'll be sure to change them up a bit.
  *
@@ -926,13 +889,16 @@ void proc_notify(struct proc *p, unsigned int notif, struct notif_event *ne)
  * about locking, do it before calling.  Takes a vcoreid! */
 void __proc_preempt_warn(struct proc *p, uint32_t vcoreid, uint64_t when)
 {
+       struct event_msg local_msg = {0};
        /* danger with doing this unlocked: preempt_pending is set, but never 0'd,
         * since it is unmapped and not dealt with (TODO)*/
        p->procinfo->vcoremap[vcoreid].preempt_pending = when;
-       /* notify, if they want to hear about this event.  regardless of how they
-        * want it, we can send this as a bit.  Subject to change. */
-       if (p->procdata->notif_methods[NE_PREEMPT_PENDING].flags | NOTIF_WANTED)
-               do_notify(p, vcoreid, NE_PREEMPT_PENDING, 0);
+
+       /* Send the event (which internally checks to see how they want it) */
+       local_msg.ev_type = EV_PREEMPT_PENDING;
+       local_msg.ev_arg1 = vcoreid;
+       send_kernel_event(p, &local_msg, vcoreid);
+
        /* TODO: consider putting in some lookup place for the alarm to find it.
         * til then, it'll have to scan the vcoremap (O(n) instead of O(m)) */
 }
index 7bfd677..17e3684 100644 (file)
@@ -5,7 +5,6 @@
 #endif
 
 #include <ros/common.h>
-#include <ros/notification.h>
 #include <arch/types.h>
 #include <arch/arch.h>
 #include <arch/mmu.h>
@@ -34,6 +33,7 @@
 #include <devfs.h>
 #include <smp.h>
 #include <arsc_server.h>
+#include <event.h>
 
 
 #ifdef __CONFIG_NETWORKING__
@@ -620,14 +620,13 @@ static int sys_resource_req(struct proc *p, int type, unsigned int amt_wanted,
        return retval;
 }
 
-/* Will notify the target on the given vcore, if the caller controls the target.
- * Will honor the target's wanted/vcoreid.  u_ne can be NULL. */
-static int sys_notify(struct proc *p, int target_pid, unsigned int notif,
-                      struct notif_event *u_ne)
+/* Untested.  Will notify the target on the given vcore, if the caller controls
+ * the target.  Will honor the target's wanted/vcoreid.  u_ne can be NULL. */
+static int sys_notify(struct proc *p, int target_pid, unsigned int ev_type,
+                      struct event_msg *u_msg)
 {
-       struct notif_event local_ne;
+       struct event_msg local_msg = {0};
        struct proc *target = pid2proc(target_pid);
-
        if (!target) {
                set_errno(EBADPROC);
                return -1;
@@ -637,40 +636,38 @@ static int sys_notify(struct proc *p, int target_pid, unsigned int notif,
                set_errno(EPERM);
                return -1;
        }
-       /* if the user provided a notif_event, copy it in and use that */
-       if (u_ne) {
-               if (memcpy_from_user(p, &local_ne, u_ne, sizeof(struct notif_event))) {
+       /* if the user provided an ev_msg, copy it in and use that */
+       if (u_msg) {
+               if (memcpy_from_user(p, &local_msg, u_msg, sizeof(struct event_msg))) {
                        kref_put(&target->kref);
                        set_errno(EINVAL);
                        return -1;
                }
-               proc_notify(target, local_ne.ne_type, &local_ne);
-       } else {
-               proc_notify(target, notif, 0);
        }
+       send_kernel_event(target, &local_msg, 0);
        kref_put(&target->kref);
        return 0;
 }
 
 /* Will notify the calling process on the given vcore, independently of WANTED
  * or advertised vcoreid.  If you change the parameters, change pop_ros_tf() */
-static int sys_self_notify(struct proc *p, uint32_t vcoreid, unsigned int notif,
-                           struct notif_event *u_ne)
+static int sys_self_notify(struct proc *p, uint32_t vcoreid,
+                           unsigned int ev_type, struct event_msg *u_msg)
 {
-       struct notif_event local_ne;
+       struct event_msg local_msg = {0};
 
-       printd("[kernel] received self notify for vcoreid %d, notif %d, ne %08p\n",
-              vcoreid, notif, u_ne);
-       /* if the user provided a notif_event, copy it in and use that */
-       if (u_ne) {
-               if (memcpy_from_user(p, &local_ne, u_ne, sizeof(struct notif_event))) {
+       printd("[kernel] received self notify for vcoreid %d, type %d, msg %08p\n",
+              vcoreid, ev_type, u_msg);
+       /* if the user provided an ev_msg, copy it in and use that */
+       if (u_msg) {
+               if (memcpy_from_user(p, &local_msg, u_msg, sizeof(struct event_msg))) {
                        set_errno(EINVAL);
                        return -1;
                }
-               do_notify(p, vcoreid, local_ne.ne_type, &local_ne);
-       } else {
-               do_notify(p, vcoreid, notif, 0);
        }
+       /* this will post a message and IPI, regardless of wants/needs/debutantes.*/
+       post_vcore_event(p, &local_msg, vcoreid);
+       proc_notify(p, vcoreid);
        return 0;
 }
 
index ab00b74..0f2fd82 100644 (file)
@@ -322,3 +322,20 @@ void *kmalloc_errno(int len)
                set_errno(ENOMEM);
        return kva;
 }
+
+/* Returns true if uva and kva both resolve to the same phys addr.  If uva is
+ * unmapped, it will return FALSE.  This is probably what you want, since after
+ * all uva isn't kva. */
+bool uva_is_kva(struct proc *p, void *uva, void *kva)
+{
+       struct page *u_page;
+       assert(kva);                            /* catch bugs */
+       /* Check offsets first */
+       if (PGOFF(uva) != PGOFF(kva))
+               return FALSE;
+       /* Check to see if it is the same physical page */
+       u_page = page_lookup(p->env_pgdir, uva, 0);
+       if (!u_page)
+               return FALSE;
+       return (kva2page(kva) == u_page) ? TRUE : FALSE;
+}
index 0c5fae3..57f1ca0 100644 (file)
@@ -1,6 +1,6 @@
 #include <ros/resource.h>
 #include <ros/procdata.h>
-#include <ros/notification.h>
+#include <ros/event.h>
 #include <ros/bcq.h>
 #include <parlib.h>
 #include <vcore.h>
@@ -52,17 +52,13 @@ int main()
        if (vcore_init())
                printf("vcore_init() failed, we're fucked!\n");
 
-       /* tell the kernel where and how we want to receive notifications */
-       struct notif_method *nm;
-       /* ETHAUD Turn off all notifs */
-       for (int i = 0; i < MAX_NR_NOTIF; i++) {
-               nm = &__procdata.notif_methods[i];
-               nm->flags = 0;
-       }
        /* ETHAUD Turn on Free apple pie (which is the network packet) */
-       nm = &__procdata.notif_methods[NE_FREE_APPLE_PIE];
-       nm->flags |= NOTIF_WANTED | NOTIF_MSG | NOTIF_IPI;
-       nm->vcoreid = 0;        /* get it on vcore 0 */
+       struct event_queue *ev_q = malloc(sizeof(struct event_queue));
+       ev_q->ev_mbox = &__procdata.vcore_preempt_data[0].ev_mbox;
+       ev_q->ev_flags = EVENT_IPI;
+       ev_q->ev_vcore = 0;     
+       ev_q->ev_handler = 0;
+       __procdata.kernel_evts[EV_FREE_APPLE_PIE] = ev_q;
 
        /* Need to save this somewhere that you can find it again when restarting
         * core0 */
@@ -106,23 +102,18 @@ void vcore_entry(void)
        uint32_t vcoreid = vcore_id();
        static bool first_time = TRUE;
 
-/* begin: stuff userspace needs to do to handle notifications */
+/* begin: stuff userspace needs to do to handle events/notifications */
 
        struct vcore *vc = &__procinfo.vcoremap[vcoreid];
        struct preempt_data *vcpd;
        vcpd = &__procdata.vcore_preempt_data[vcoreid];
        
-       /* here is how you receive a notif_event */
-       struct notif_event ne = {0};
-       bcq_dequeue(&vcpd->notif_evts, &ne, NR_PERCORE_EVENTS);
-       /* it might be in bitmask form too: */
-       //printf("and the bitmask looks like: ");
-       //PRINT_BITMASK(__procdata.vcore_preempt_data[vcoreid].notif_bmask, MAX_NR_NOTIF);
-       /* can see how many messages had to be sent as bits */
-       //printf("Number of event overflows: %d\n", vcpd->event_overflows);
+       /* here is how you receive an event message (ought to check overflow) */
+       struct event_msg ev_msg = {0};
+       bcq_dequeue(&vcpd->ev_mbox.ev_msgs, &ev_msg, NR_BCQ_EVENTS);
 
        /* ETHAUD app: process the packet if we got a notif */
-       if (ne.ne_type == NE_FREE_APPLE_PIE)
+       if (ev_msg.ev_type == EV_FREE_APPLE_PIE)
                process_packet();
 
        if (vc->preempt_pending) {
index a31ef2d..765a7bf 100644 (file)
@@ -2,7 +2,7 @@
 #include <ros/mman.h>
 #include <ros/resource.h>
 #include <ros/procdata.h>
-#include <ros/notification.h>
+#include <ros/event.h>
 #include <ros/bcq.h>
 #include <arch/arch.h>
 #include <rstdio.h>
@@ -20,6 +20,8 @@ mcs_barrier_t b;
 __thread int temp;
 void *core0_tls = 0;
 
+struct event_queue_big *indirect_q;
+
 int main(int argc, char** argv)
 {
        uint32_t vcoreid;
@@ -27,17 +29,32 @@ int main(int argc, char** argv)
 
        mcs_barrier_init(&b, max_vcores());
 
+       /* prep indirect ev_q (TODO PIN).  Note we grab a big one */
+       indirect_q = malloc(sizeof(struct event_queue_big));
+       indirect_q->ev_mbox = &indirect_q->ev_imbox;
+       indirect_q->ev_flags = EVENT_IPI;
+       indirect_q->ev_vcore = 1;                       /* IPI core 1 */
+       indirect_q->ev_handler = 0;
+       printf("Registering %08p for event type %d\n", indirect_q,
+              EV_FREE_APPLE_PIE);
+       __procdata.kernel_evts[EV_FREE_APPLE_PIE] = (struct event_queue*)indirect_q;
+
 /* begin: stuff userspace needs to do before switching to multi-mode */
        if (vcore_init())
                printf("vcore_init() failed, we're fucked!\n");
 
-       /* tell the kernel where and how we want to receive notifications */
-       struct notif_method *nm;
-       for (int i = 0; i < MAX_NR_NOTIF; i++) {
-               nm = &__procdata.notif_methods[i];
-               nm->flags |= NOTIF_WANTED | NOTIF_MSG | NOTIF_IPI;
-               nm->vcoreid = i % 2; // vcore0 or 1, keepin' it fresh.
-       }
+       /* Tell the kernel where and how we want to receive events.  This is just an
+        * example of what to do to have a notification turned on.  We're turning on
+        * USER_IPIs, posting events to vcore 0's vcpd, and telling the kernel to
+        * send to vcore 0.
+        * TODO: (PIN) this ev_q needs to be pinned */
+       struct event_queue *ev_q = malloc(sizeof(struct event_queue));
+       ev_q->ev_mbox = &__procdata.vcore_preempt_data[0].ev_mbox;
+       ev_q->ev_flags = EVENT_IPI;     /* we want an IPI */
+       ev_q->ev_vcore = 0;                     /* IPI core 0 */
+       ev_q->ev_handler = 0;
+       /* Now tell the kernel about it */
+       __procdata.kernel_evts[EV_USER_IPI] = ev_q;
 
        /* Need to save this somewhere that you can find it again when restarting
         * core0 */
@@ -66,19 +83,17 @@ int main(int argc, char** argv)
                printf("This is vcore0, right after vcore_request, retval=%d\n", retval);
        }
 
-#if 0
        /* test notifying my vcore2 */
        udelay(5000000);
        printf("Vcore 0 self-notifying vcore 2 with notif 4!\n");
-       struct notif_event ne;
-       ne.ne_type = 4;
-       sys_self_notify(2, 4, &ne);
+       struct event_msg msg;
+       msg.ev_type = 4;
+       sys_self_notify(2, 4, &msg);
        udelay(5000000);
        printf("Vcore 0 notifying itself with notif 3!\n");
-       ne.ne_type = 3;
-       sys_notify(sys_getpid(), 3, &ne);
+       msg.ev_type = 3;
+       sys_notify(sys_getpid(), 3, &msg);
        udelay(1000000);
-#endif
 
        /* test loop for restarting a notif_tf */
        if (vcoreid == 0) {
@@ -110,17 +125,28 @@ void vcore_entry(void)
        struct preempt_data *vcpd;
        vcpd = &__procdata.vcore_preempt_data[vcoreid];
        
-       /* here is how you receive a notif_event */
-       struct notif_event ne = {0};
-       bcq_dequeue(&vcpd->notif_evts, &ne, NR_PERCORE_EVENTS);
+       /* here is how you receive an event (ought to check for overflow, etc) */
+       struct event_msg ev_msg = {0};
+       bcq_dequeue(&vcpd->ev_mbox.ev_msgs, &ev_msg, NR_BCQ_EVENTS);
        printf("the queue is on vcore %d and has a ne with type %d\n", vcoreid,
-              ne.ne_type);
+              ev_msg.ev_type);
        /* it might be in bitmask form too: */
        //printf("and the bitmask looks like: ");
        //PRINT_BITMASK(__procdata.vcore_preempt_data[vcoreid].notif_bmask, MAX_NR_NOTIF);
        /* can see how many messages had to be sent as bits */
-       printf("Number of event overflows: %d\n", vcpd->event_overflows);
-
+       printf("Number of event overflows: %d\n", vcpd->ev_mbox.ev_overflows);
+
+       /* How we handle indirection events: */
+       struct event_queue_big *ev_q;
+       struct event_msg indir_msg = {0};
+       if (ev_msg.ev_type == EV_EVENT) {
+               ev_q = ev_msg.ev_arg3;  /* convention */
+               printf("Detected EV_EVENT, ev_q is %08p (%08p)\n", ev_q, indirect_q);
+               bcq_dequeue(&ev_q->ev_mbox->ev_msgs, &indir_msg, NR_BCQ_EVENTS);
+               printf("Message of type: %d (%d)\n", indir_msg.ev_type,
+                      EV_FREE_APPLE_PIE);
+       }
+       /* how we tell a preemption is pending (regardless of notif/events) */
        if (vc->preempt_pending) {
                printf("Oh crap, vcore %d is being preempted!  Yielding\n", vcoreid);
                sys_yield(TRUE);
@@ -164,6 +190,6 @@ void vcore_entry(void)
        vcore_request(1);
        //mcs_barrier_wait(&b,vcore_id());
        udelay(vcoreid * 10000000);
-       exit(0);
+       //exit(0);
        while(1);
 }
index f47a7a3..2fdfc06 100644 (file)
@@ -16,6 +16,15 @@ int main(int argc, char** argv)
        struct preempt_data *vcpd = &__procdata.vcore_preempt_data[0];
        vcpd->notif_enabled = TRUE;
 
+       /* Get EV_ALARM on vcore 1, with IPI.
+        * TODO: (PIN) this ev_q needs to be pinned */
+       struct event_queue *ev_q = malloc(sizeof(struct event_queue));
+       ev_q->ev_mbox = &__procdata.vcore_preempt_data[1].ev_mbox;
+       ev_q->ev_flags = EVENT_IPI;
+       ev_q->ev_vcore = 1;
+       ev_q->ev_handler = 0;
+       __procdata.kernel_evts[EV_ALARM] = ev_q;
+
        vcore_request(max_vcores());
 
        /* should never make it here */
@@ -27,13 +36,9 @@ void vcore_entry(void)
        struct preempt_data *vcpd = &__procdata.vcore_preempt_data[0];
        vcpd->notif_enabled = TRUE;
 
-       struct notif_method *nm = &__procdata.notif_methods[NE_ALARM];
-       nm->flags = NOTIF_WANTED | NOTIF_MSG | NOTIF_IPI;
-       nm->vcoreid = 1;
-
-       struct notif_event ne = {0};
-       bcq_dequeue(&vcpd->notif_evts, &ne, NR_PERCORE_EVENTS);
-       if (ne.ne_type == NE_ALARM)
+       struct event_msg ev_msg = {0};
+       bcq_dequeue(&vcpd->ev_mbox.ev_msgs, &ev_msg, NR_BCQ_EVENTS);
+       if (ev_msg.ev_type == EV_ALARM)
                printf("[T]:009:E:%llu\n", read_tsc());
        while(1);
 }
index c5cf629..d6fd37a 100644 (file)
@@ -15,7 +15,6 @@
 #include <ros/mman.h>
 #include <ros/resource.h>
 #include <ros/procdata.h>
-#include <ros/notification.h>
 #include <ros/bcq.h>
 #include <arch/arch.h>
 #include <rstdio.h>
@@ -43,6 +42,7 @@ int main(int argc, char** argv)
 /* begin: stuff userspace needs to do before switching to multi-mode */
        if (vcore_init())
                printf("vcore_init() failed, we're fucked!\n");
+       #if 0
        /* tell the kernel where and how we want to receive notifications */
        struct notif_method *nm;
        for (int i = 0; i < MAX_NR_NOTIF; i++) {
@@ -50,6 +50,7 @@ int main(int argc, char** argv)
                nm->flags |= NOTIF_WANTED | NOTIF_MSG | NOTIF_IPI;
                nm->vcoreid = i % 2; // vcore0 or 1, keepin' it fresh.
        }
+       #endif
        /* Need to save this somewhere that you can find it again when restarting
         * core0 */
        core0_tls = get_tls_desc(0);
index 6fb5060..0c74dfe 100644 (file)
@@ -7,7 +7,6 @@
 #include <ros/mman.h>
 #include <ros/resource.h>
 #include <ros/procdata.h>
-#include <ros/notification.h>
 #include <ros/bcq.h>
 #include <arch/arch.h>
 #include <rstdio.h>
@@ -36,6 +35,7 @@ int main(int argc, char** argv)
 /* begin: stuff userspace needs to do before switching to multi-mode */
        if (vcore_init())
                printf("vcore_init() failed, we're fucked!\n");
+       #if 0
        /* tell the kernel where and how we want to receive notifications */
        struct notif_method *nm;
        for (int i = 0; i < MAX_NR_NOTIF; i++) {
@@ -43,6 +43,7 @@ int main(int argc, char** argv)
                nm->flags |= NOTIF_WANTED | NOTIF_MSG | NOTIF_IPI;
                nm->vcoreid = i % 2; // vcore0 or 1, keepin' it fresh.
        }
+       #endif
        /* Need to save this somewhere that you can find it again when restarting
         * core0 */
        core0_tls = get_tls_desc(0);
index 0556f29..fc91bcd 100644 (file)
@@ -4,7 +4,7 @@
  * userthread.  The pthread code will nicely yield if it detects an incoming
  * preemption. */
 
-#include <ros/notification.h>
+#include <ros/event.h>
 #include <stdlib.h>
 #include <vcore.h>
 #include <pthread.h>
@@ -19,10 +19,15 @@ int main(int argc, char** argv)
 {
        pthread_t *my_threads = malloc(sizeof(pthread_t) * max_vcores());
 
-       /* set up to receive the PREEMPT_PENDING notif */
-       struct notif_method *nm;
-       nm = &__procdata.notif_methods[NE_PREEMPT_PENDING];
-       nm->flags |= NOTIF_WANTED | NOTIF_IPI;
+       /* set up to receive the PREEMPT_PENDING event.  EVENT_VCORE_APPRO tells the
+        * kernel to send the msg to whichever vcore is appropriate. 
+        * TODO: (PIN) this ev_q needs to be pinned */
+       struct event_queue *ev_q = malloc(sizeof(struct event_queue));
+       ev_q->ev_mbox = &__procdata.vcore_preempt_data[0].ev_mbox;
+       ev_q->ev_flags = EVENT_IPI | EVENT_NOMSG | EVENT_VCORE_APPRO;
+       ev_q->ev_vcore = 0;
+       ev_q->ev_handler = 0;
+       __procdata.kernel_evts[EV_PREEMPT_PENDING] = ev_q;
 
        /* actually only need one less, since the _S will be pthread 0 */
        for (int i = 0; i < max_vcores() - 1; i++)
index aab0181..72b4290 100644 (file)
@@ -44,9 +44,9 @@ void        sys_reboot();
 int         gettimeofday(struct timeval* tp, void* tzp);
 void *COUNT(length) sys_mmap(void *SNT addr, size_t length, int prot, int flags,
                              int fd, size_t offset);
-int         sys_notify(int pid, unsigned int notif, struct notif_event *ne);
-int         sys_self_notify(uint32_t vcoreid, unsigned int notif,
-                            struct notif_event *ne);
+int         sys_notify(int pid, unsigned int ev_type, struct event_msg *u_msg);
+int         sys_self_notify(uint32_t vcoreid, unsigned int ev_type,
+                            struct event_msg *u_msg);
 int         sys_halt_core(unsigned int usec);
 
 /* ARSC */
index 7f461c4..291965e 100644 (file)
@@ -8,7 +8,7 @@
 #include <rstdio.h>
 #include <errno.h>
 #include <parlib.h>
-#include <ros/notification.h>
+#include <ros/event.h>
 #include <arch/atomic.h>
 #include <arch/arch.h>
 #include <sys/queue.h>
@@ -35,13 +35,19 @@ void _pthread_init()
        
        assert(vcore_id() == 0);
 
-       /* tell the kernel where and how we want to receive notifications */
-       struct notif_method *nm;
-       for (int i = 0; i < MAX_NR_NOTIF; i++) {
-               nm = &__procdata.notif_methods[i];
-               nm->flags |= NOTIF_WANTED | NOTIF_MSG | NOTIF_IPI;
-               nm->vcoreid = i % 2; // vcore0 or 1, keepin' it fresh.
-       }
+       /* Tell the kernel where and how we want to receive events.  This is just an
+        * example of what to do to have a notification turned on.  We're turning on
+        * USER_IPIs, posting events to vcore 0's vcpd, and telling the kernel to
+        * send to vcore 0.
+        * TODO: (PIN) this ev_q needs to be pinned */
+       struct event_queue *ev_q = malloc(sizeof(struct event_queue));
+       ev_q->ev_mbox = &__procdata.vcore_preempt_data[0].ev_mbox;
+       ev_q->ev_flags = EVENT_IPI;     /* we want an IPI */
+       ev_q->ev_vcore = 0;                     /* IPI core 0 */
+       ev_q->ev_handler = 0;
+       /* Now tell the kernel about it */
+       __procdata.kernel_evts[EV_USER_IPI] = ev_q;
+
        /* don't forget to enable notifs on vcore0.  if you don't, the kernel will
         * restart your _S with notifs disabled, which is a path to confusion. */
        struct preempt_data *vcpd = &__procdata.vcore_preempt_data[0];
index ab54b0f..6004fd0 100644 (file)
@@ -112,15 +112,15 @@ void *CT(length) sys_mmap(void *SNT addr, size_t length, int prot, int flags,
        return (void*)ros_syscall(SYS_mmap, addr, length, prot, flags, fd, offset);
 }
 
-int sys_notify(int pid, unsigned int notif, struct notif_event *ne)
+int sys_notify(int pid, unsigned int ev_type, struct event_msg *u_msg)
 {
-       return ros_syscall(SYS_notify, pid, notif, ne, 0, 0, 0);
+       return ros_syscall(SYS_notify, pid, ev_type, u_msg, 0, 0, 0);
 }
 
-int sys_self_notify(uint32_t vcoreid, unsigned int notif,
-                    struct notif_event *ne)
+int sys_self_notify(uint32_t vcoreid, unsigned int ev_type,
+                    struct event_msg *u_msg)
 {
-       return ros_syscall(SYS_self_notify, vcoreid, notif, ne, 0, 0, 0);
+       return ros_syscall(SYS_self_notify, vcoreid, ev_type, u_msg, 0, 0, 0);
 }
 
 int sys_halt_core(unsigned int usec)