Routine kmsg processing
authorBarret Rhoden <brho@cs.berkeley.edu>
Tue, 23 Mar 2010 00:34:16 +0000 (17:34 -0700)
committerKevin Klues <klueska@cs.berkeley.edu>
Thu, 3 Nov 2011 00:35:40 +0000 (17:35 -0700)
Processes routine kernel messages in smp_idle().  Doesn't do it on
management cores yet (they call manager(), for now).

Documentation/kernel_messages.txt
kern/arch/i686/trap.c
kern/arch/sparc/trap.c
kern/include/trap.h
kern/src/smp.c

index 7ab3905..bfbde61 100644 (file)
@@ -67,6 +67,23 @@ Technically, an immediate message could not return, but only if the kernel code
 that was interrupted was not holding any locks, mucking with any invariants, or
 otherwise doing work that needed to be done.  Those cases seem rather rare.
 
+To retain some sort of sanity, the functions that do not return must adhere to
+some rules.  At some point they need to end in a place where they check routine
+messages or enable interrupts.  Returning to userspace will do this (interrupts
+are enabled).  __death will eventually call smp_idle(), which will check.  The
+idea behind this is that route messages will get processed once the kernel is
+able to (at a convenient place).
+
+Since some routine messages do not return by popping to userspace, we need to
+self-ipi to make sure the kernel regains control (this need might go away in the
+future).  Since we also want immediate messages to get processed before routine
+messages, and we want the system to be able to have a bunch of outstanding
+routine messages (though that is unlikely at this point), we briefly check
+for immed's inside process_routine_kmsg().  By only turning interrupts on for
+this means we avoid receiving excessive self_ipis for potentially not-returning
+routine messages.  Keep in mind that each one of those IPIs would be useless,
+since they will only run their functions when interrupting from userspace.
+
 Trickiness:
 --------------------------------
 If a function does not return, then the code might not check the list again, or
@@ -110,6 +127,13 @@ dynamically create the k_msgs (can pass them around easily, delay with them
 easily (alarms), and most importantly we can't deadlock by running out of room
 in a static buffer).
 
+When running our process_routine_kmsg()s, we could have made a userspace process
+that would get interrupted if there were any outstanding IPIs for routine
+messages.  We'd have to self_ipi, then switch to this process.  That kinda
+sucks, and would also mean that when we want to actually smp_idle, we'd have to
+be in userspace (and probably not cpu_halt()ing).  Making it possible to process
+the messages from within the kernel seemed much more flexible and better.
+
 Architecture Dependence:
 --------------------------------
 Some details will differ, based on architectural support.  For instance,
index 95db81c..d12a173 100644 (file)
@@ -482,3 +482,46 @@ void __kernel_message(struct trapframe *tf)
                }
        }
 }
+
+/* Runs any outstanding routine kernel messages from within the kernel.  Will
+ * make sure immediates still run first (or when they arrive, if processing a
+ * bunch of these messages).  This will disable interrupts, and restore them to
+ * whatever state you left them. */
+void process_routine_kmsg(void)
+{
+       per_cpu_info_t *myinfo = &per_cpu_info[core_id()];
+       kernel_message_t msg_cp, *k_msg;
+       int8_t irq_state = 0;
+
+       disable_irqsave(&irq_state);
+       while (1) {
+               /* normally, we want ints disabled, so we don't have an empty self-ipi
+                * for every routine message. (imagine a long list of routines).  But we
+                * do want immediates to run ahead of routines.  This enabling should
+                * work (might not in some shitty VMs).  Also note we can receive an
+                * extra self-ipi for routine messages before we turn off irqs again.
+                * Not a big deal, since we will process it right away. 
+                * TODO: consider calling __kernel_message() here. */
+               if (!STAILQ_EMPTY(&myinfo->immed_amsgs)) {
+                       enable_irq();
+                       cpu_relax();
+                       disable_irq();
+               }
+               k_msg = get_next_amsg(&myinfo->routine_amsgs,
+                                     &myinfo->routine_amsg_lock);
+               if (!k_msg) {
+                       enable_irqsave(&irq_state);
+                       return;
+               }
+               /* copy in, and then free, in case we don't return */
+               msg_cp = *k_msg;
+               kmem_cache_free(kernel_msg_cache, (void*)k_msg);
+               /* make sure an IPI is pending if we have more work */
+               if (!STAILQ_EMPTY(&myinfo->routine_amsgs) &&
+                      !ipi_is_pending(I_KERNEL_MSG))
+                       send_self_ipi(I_KERNEL_MSG);
+               /* Execute the kernel message */
+               assert(msg_cp.pc);
+               msg_cp.pc(0, msg_cp.srcid, msg_cp.arg0, msg_cp.arg1, msg_cp.arg2);
+       }
+}
index aa2937f..750521f 100644 (file)
@@ -202,6 +202,45 @@ void handle_ipi(trapframe_t* tf)
        env_pop_tf(tf);
 }
 
+/* Same as in x86.  Might be diff in the future if there is no way to check for
+ * immediate messages or there is the ability to selectively mask IPI vectors.*/
+void process_routine_kmsg(void)
+{
+       per_cpu_info_t *myinfo = &per_cpu_info[core_id()];
+       kernel_message_t msg_cp, *k_msg;
+       int8_t irq_state = 0;
+
+       disable_irqsave(&irq_state);
+       while (1) {
+               /* normally, we want ints disabled, so we don't have an empty self-ipi
+                * for every routine message. (imagine a long list of routines).  But we
+                * do want immediates to run ahead of routines.  This enabling should
+                * work (might not in some shitty VMs).  Also note we can receive an
+                * extra self-ipi for routine messages before we turn off irqs again.
+                * Not a big deal, since we will process it right away. */
+               if (!STAILQ_EMPTY(&myinfo->immed_amsgs)) {
+                       enable_irq();
+                       cpu_relax();
+                       disable_irq();
+               }
+               k_msg = get_next_amsg(&myinfo->routine_amsgs,
+                                     &myinfo->routine_amsg_lock);
+               if (!k_msg) {
+                       enable_irqsave(&irq_state);
+                       return;
+               }
+               /* copy in, and then free, in case we don't return */
+               msg_cp = *k_msg;
+               kmem_cache_free(kernel_msg_cache, (void*)k_msg);
+               /* make sure an IPI is pending if we have more work */
+               if (!STAILQ_EMPTY(&myinfo->routine_amsgs))
+                       send_ipi(core_id());
+               /* Execute the kernel message */
+               assert(msg_cp.pc);
+               msg_cp.pc(0, msg_cp.srcid, msg_cp.arg0, msg_cp.arg1, msg_cp.arg2);
+       }
+}
+
 void
 unhandled_trap(trapframe_t* state)
 {
index 802fdb7..5ab671b 100644 (file)
@@ -73,5 +73,6 @@ typedef struct kernel_message NTPTV(a0t) NTPTV(a1t) NTPTV(a2t) kernel_message_t;
 
 uint32_t send_kernel_message(uint32_t dst, amr_t pc, TV(a0t) arg0, TV(a1t) arg1,
                              TV(a2t) arg2, int type);
+void process_routine_kmsg(void);
 
 #endif /* ROS_KERN_TRAP_H */
index 19ce766..3d85e19 100644 (file)
@@ -43,7 +43,7 @@ void smp_idle(void)
        if (!management_core()) {
                enable_irq();
                while (1) {
-                       process_workqueue();
+                       process_routine_kmsg();
                        // consider races with work added after we started leaving the last func
                        cpu_halt();
                }