Kernel alarm handling moved to RKM context
authorBarret Rhoden <brho@cs.berkeley.edu>
Mon, 18 Nov 2013 23:07:32 +0000 (15:07 -0800)
committerBarret Rhoden <brho@cs.berkeley.edu>
Thu, 16 Jan 2014 19:45:59 +0000 (11:45 -0800)
Instead of running handlers from IRQ context, we shift them to RKM
context.  Alternatively, we could only do this for the awaiter handlers
(and not the semaphores), but it's probably not a big deal.

The reason for this so that we can easily send_event from an alarm
handler.  Right now, you can't send_event from IRQ context since it
might trigger a proc wakeup (grab the proclock, scheduler callbacks,
etc), all of which are not irqsave.

While I could change all of those, I'd rather cut down on the amount of
things in IRQ context (both for interrupt latency as well as for code
safety - RKMs can block, etc, though a block will delay all alarms under
the current code).  Keep in mind that send_event() can also trigger an
mmap call.  If we ever want blockable alarms, we can easily put that in
(IRQ handler wakes sems, executes handlers marked "IRQOK", and launched
an RKM otherwise).

kern/include/alarm.h
kern/src/alarm.c
kern/src/schedule.c
kern/src/time.c

index 8f52320..5496d9c 100644 (file)
@@ -98,8 +98,8 @@ void reset_alarm_abs(struct timer_chain *tchain, struct alarm_waiter *waiter,
                      uint64_t abs_time);
 /* Blocks on the alarm waiter */
 int sleep_on_awaiter(struct alarm_waiter *waiter);
-/* Interrupt handlers needs to call this.  Don't call it directly. */
-void trigger_tchain(struct timer_chain *tchain);
+/* Interrupt handlers needs to RKM this.  Don't call it directly. */
+void __trigger_tchain(uint32_t srcid, long a0, long a1, long a2);
 /* How to set a specific alarm: the per-cpu timer interrupt */
 void set_pcpu_alarm_interrupt(uint64_t time, struct timer_chain *tchain);
 
index 4baf2a9..c125702 100644 (file)
@@ -118,8 +118,9 @@ static void wake_awaiter(struct alarm_waiter *waiter)
 
 /* This is called when an interrupt triggers a tchain, and needs to wake up
  * everyone whose time is up.  Called from IRQ context. */
-void trigger_tchain(struct timer_chain *tchain)
+void __trigger_tchain(uint32_t srcid, long a0, long a1, long a2)
 {
+       struct timer_chain *tchain = (struct timer_chain*)a0;
        struct alarm_waiter *i, *temp;
        uint64_t now = read_tsc();
        bool changed_list = FALSE;
@@ -296,7 +297,7 @@ int sleep_on_awaiter(struct alarm_waiter *waiter)
  * if time is 0.   Any function like this needs to do a few things:
  *     - Make sure the interrupt is on and will go off when we want
  *     - Make sure the interrupt source can find tchain
- *     - Make sure the interrupt handler calls trigger_tchain(tchain)
+ *     - Make sure the interrupt handler sends an RKM to __trigger_tchain(tchain)
  *     - Make sure you don't clobber an old tchain here (a bug) 
  * This implies the function knows how to find its timer source/void
  *
index 14b4013..473aabf 100644 (file)
@@ -122,6 +122,8 @@ static void __ksched_tick(uint32_t srcid, long a0, long a1, long a2)
  * interrupt context). */
 static void __kalarm(struct alarm_waiter *waiter)
 {
+       /* Not necessary when alarms are running in RKM context (check
+        * timer_interrupt()) */
        send_kernel_message(core_id(), __ksched_tick, 0, 0, 0, KMSG_ROUTINE);
 }
 
index df30404..cd886ef 100644 (file)
@@ -56,8 +56,11 @@ void udelay_sched(uint64_t usec)
  * you want something to happen in the future, set an alarm. */
 void timer_interrupt(struct hw_trapframe *hw_tf, void *data)
 {
-       struct timer_chain *pcpui_tchain = &per_cpu_info[core_id()].tchain;
-       trigger_tchain(pcpui_tchain);
+       int coreid = core_id();
+       /* run the alarms out of RKM context, so that event delivery works nicely
+        * (keeps the proc lock and ksched lock non-irqsave) */
+       send_kernel_message(coreid, __trigger_tchain,
+                           (long)&per_cpu_info[coreid].tchain, 0, 0, KMSG_ROUTINE);
 }
 
 /* We can overflow/wraparound when we multiply up, but we have to divide last,