Kernel alarms can run in IRQ or RKM context
authorBarret Rhoden <brho@cs.berkeley.edu>
Tue, 3 Jun 2014 22:35:02 +0000 (15:35 -0700)
committerBarret Rhoden <brho@cs.berkeley.edu>
Tue, 3 Jun 2014 23:31:51 +0000 (16:31 -0700)
Some alarms want to run in IRQ context, and some want to run as RKMs.  We can
only have one tchain, which manages the timer IRQ, and the tchain is the tool
to manage that, regardless of IRQ vs. RKM context.

Alarm handlers that run in IRQ context will have access to the HW TF.

Check out fbee73f5ee for when we switched it from IRQ -> RKM in the first
place.  There are a few alarms that want to be RKMs, due to locking issues.
You can't send_event() from IRQ context, for instance...

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

index 5496d9c..752775f 100644 (file)
  * stack of the thread you're about to block on. */
 struct alarm_waiter {
        uint64_t                                        wake_up_time;   /* ugh, this is a TSC for now */
  * stack of the thread you're about to block on. */
 struct alarm_waiter {
        uint64_t                                        wake_up_time;   /* ugh, this is a TSC for now */
-       void (*func) (struct alarm_waiter *waiter);     /* either this, or a kthread */
-       struct semaphore                        sem;                    /* kthread will sleep on this */
+       union {
+               void (*func) (struct alarm_waiter *waiter);
+               void (*func_irq) (struct alarm_waiter *waiter,
+                                 struct hw_trapframe *hw_tf);
+               struct semaphore                        sem;            /* kthread will sleep on this */
+       };
        void                                            *data;
        TAILQ_ENTRY(alarm_waiter)       next;
        bool                                            on_tchain;
        void                                            *data;
        TAILQ_ENTRY(alarm_waiter)       next;
        bool                                            on_tchain;
+       bool                                            irq_ok;
+       bool                                            has_func;
 };
 TAILQ_HEAD(awaiters_tailq, alarm_waiter);              /* ideally not a LL */
 
 };
 TAILQ_HEAD(awaiters_tailq, alarm_waiter);              /* ideally not a LL */
 
@@ -86,6 +92,9 @@ void init_timer_chain(struct timer_chain *tchain,
 /* For fresh alarm waiters.  func == 0 for kthreads */
 void init_awaiter(struct alarm_waiter *waiter,
                   void (*func) (struct alarm_waiter *));
 /* For fresh alarm waiters.  func == 0 for kthreads */
 void init_awaiter(struct alarm_waiter *waiter,
                   void (*func) (struct alarm_waiter *));
+void init_awaiter_irq(struct alarm_waiter *waiter,
+                      void (*func_irq) (struct alarm_waiter *awaiter,
+                                        struct hw_trapframe *hw_tf));
 /* Sets the time an awaiter goes off */
 void set_awaiter_abs(struct alarm_waiter *waiter, uint64_t abs_time);
 void set_awaiter_rel(struct alarm_waiter *waiter, uint64_t usleep);
 /* Sets the time an awaiter goes off */
 void set_awaiter_abs(struct alarm_waiter *waiter, uint64_t abs_time);
 void set_awaiter_rel(struct alarm_waiter *waiter, uint64_t usleep);
@@ -98,8 +107,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);
                      uint64_t abs_time);
 /* Blocks on the alarm waiter */
 int sleep_on_awaiter(struct alarm_waiter *waiter);
-/* Interrupt handlers needs to RKM this.  Don't call it directly. */
-void __trigger_tchain(uint32_t srcid, long a0, long a1, long a2);
+/* Interrupt handlers need to call this.  Don't call it directly. */
+void __trigger_tchain(struct timer_chain *tchain, struct hw_trapframe *hw_tf);
 /* How to set a specific alarm: the per-cpu timer interrupt */
 void set_pcpu_alarm_interrupt(uint64_t time, struct timer_chain *tchain);
 
 /* How to set a specific alarm: the per-cpu timer interrupt */
 void set_pcpu_alarm_interrupt(uint64_t time, struct timer_chain *tchain);
 
index 3e89699..f58dbe5 100644 (file)
@@ -47,14 +47,31 @@ void init_timer_chain(struct timer_chain *tchain,
 
 /* Initializes a new awaiter.  Pass 0 for the function if you want it to be a
  * kthread-alarm, and sleep on it after you set the alarm later. */
 
 /* Initializes a new awaiter.  Pass 0 for the function if you want it to be a
  * kthread-alarm, and sleep on it after you set the alarm later. */
-void init_awaiter(struct alarm_waiter *waiter,
-                  void (*func) (struct alarm_waiter *awaiter))
+static void __init_awaiter(struct alarm_waiter *waiter)
 {
        waiter->wake_up_time = ALARM_POISON_TIME;
 {
        waiter->wake_up_time = ALARM_POISON_TIME;
-       waiter->func = func;
-       if (!func)
-               sem_init_irqsave(&waiter->sem, 0);
        waiter->on_tchain = FALSE;
        waiter->on_tchain = FALSE;
+       if (!waiter->has_func)
+               sem_init_irqsave(&waiter->sem, 0);
+}
+
+void init_awaiter(struct alarm_waiter *waiter,
+                  void (*func) (struct alarm_waiter *awaiter))
+{
+       waiter->irq_ok = FALSE;
+       waiter->has_func = func ? TRUE : FALSE;
+       waiter->func = func;                    /* if !func, this is a harmless zeroing */
+       __init_awaiter(waiter);
+}
+
+void init_awaiter_irq(struct alarm_waiter *waiter,
+                      void (*func_irq) (struct alarm_waiter *awaiter,
+                                        struct hw_trapframe *hw_tf))
+{
+       waiter->irq_ok = TRUE;
+       waiter->has_func = func_irq ? TRUE : FALSE;
+       waiter->func_irq = func_irq;    /* if !func, this is a harmless zeroing */
+       __init_awaiter(waiter);
 }
 
 /* Give this the absolute time.  For now, abs_time is the TSC time that you want
 }
 
 /* Give this the absolute time.  For now, abs_time is the TSC time that you want
@@ -104,26 +121,41 @@ static void reset_tchain_interrupt(struct timer_chain *tchain)
        }
 }
 
        }
 }
 
+static void __run_awaiter(uint32_t srcid, long a0, long a1, long a2)
+{
+       struct alarm_waiter *waiter = (struct alarm_waiter*)a0;
+       waiter->func(waiter);
+}
+
 /* When an awaiter's time has come, this gets called.  If it was a kthread, it
  * will wake up.  o/w, it will call the func ptr stored in the awaiter. */
 /* When an awaiter's time has come, this gets called.  If it was a kthread, it
  * will wake up.  o/w, it will call the func ptr stored in the awaiter. */
-static void wake_awaiter(struct alarm_waiter *waiter)
+static void wake_awaiter(struct alarm_waiter *waiter,
+                         struct hw_trapframe *hw_tf)
 {
 {
-       if (waiter->func)
-               waiter->func(waiter);
-       else
+       if (waiter->has_func) {
+               if (waiter->irq_ok)
+                       waiter->func_irq(waiter, hw_tf);
+               else
+                       send_kernel_message(core_id(), __run_awaiter, (long)waiter,
+                                           0, 0, KMSG_ROUTINE);
+       } else {
                sem_up(&waiter->sem); /* IRQs are disabled, can call sem_up directly */
                sem_up(&waiter->sem); /* IRQs are disabled, can call sem_up directly */
+       }
 }
 
 /* This is called when an interrupt triggers a tchain, and needs to wake up
  * everyone whose time is up.  Called from IRQ context. */
 }
 
 /* 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(uint32_t srcid, long a0, long a1, long a2)
+void __trigger_tchain(struct timer_chain *tchain, struct hw_trapframe *hw_tf)
 {
 {
-       struct timer_chain *tchain = (struct timer_chain*)a0;
        struct alarm_waiter *i, *temp;
        uint64_t now = read_tsc();
        bool changed_list = FALSE;
        struct alarm_waiter *i, *temp;
        uint64_t now = read_tsc();
        bool changed_list = FALSE;
-       assert(!irq_is_enabled());
-       spin_lock(&tchain->lock);
+       /* why do we disable irqs here?  the lock is irqsave, but we (think we) know
+        * the timer IRQ for this tchain won't fire again.  disabling irqs is nice
+        * for the lock debugger.  i don't want to disable the debugger completely,
+        * and we can't make the debugger ignore irq context code either in the
+        * general case.  it might be nice for handlers to have IRQs disabled too.*/
+       spin_lock_irqsave(&tchain->lock);
        TAILQ_FOREACH_SAFE(i, &tchain->waiters, next, temp) {
                printd("Trying to wake up %p who is due at %llu and now is %llu\n",
                       i, i->wake_up_time, now);
        TAILQ_FOREACH_SAFE(i, &tchain->waiters, next, temp) {
                printd("Trying to wake up %p who is due at %llu and now is %llu\n",
                       i, i->wake_up_time, now);
@@ -136,7 +168,7 @@ void __trigger_tchain(uint32_t srcid, long a0, long a1, long a2)
                        /* Don't touch the waiter after waking it, since it could be in use
                         * on another core (and the waiter can be clobbered as the kthread
                         * unwinds its stack).  Or it could be kfreed */
                        /* Don't touch the waiter after waking it, since it could be in use
                         * on another core (and the waiter can be clobbered as the kthread
                         * unwinds its stack).  Or it could be kfreed */
-                       wake_awaiter(i);
+                       wake_awaiter(i, hw_tf);
                } else {
                        break;
                }
                } else {
                        break;
                }
@@ -146,7 +178,7 @@ void __trigger_tchain(uint32_t srcid, long a0, long a1, long a2)
        }
        /* Need to reset the interrupt no matter what */
        reset_tchain_interrupt(tchain);
        }
        /* Need to reset the interrupt no matter what */
        reset_tchain_interrupt(tchain);
-       spin_unlock(&tchain->lock);
+       spin_unlock_irqsave(&tchain->lock);
 }
 
 /* Helper, inserts the waiter into the tchain, returning TRUE if we still need
 }
 
 /* Helper, inserts the waiter into the tchain, returning TRUE if we still need
@@ -280,7 +312,7 @@ void reset_alarm_abs(struct timer_chain *tchain, struct alarm_waiter *waiter,
 int sleep_on_awaiter(struct alarm_waiter *waiter)
 {
        int8_t irq_state = 0;
 int sleep_on_awaiter(struct alarm_waiter *waiter)
 {
        int8_t irq_state = 0;
-       if (waiter->func)
+       if (waiter->has_func)
                panic("Tried blocking on a waiter %p with a func %p!", waiter,
                      waiter->func);
        /* Put the kthread to sleep.  TODO: This can fail (or at least it will be
                panic("Tried blocking on a waiter %p with a func %p!", waiter,
                      waiter->func);
        /* Put the kthread to sleep.  TODO: This can fail (or at least it will be
@@ -299,7 +331,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
  * 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 sends an RKM to __trigger_tchain(tchain)
+ *     - Make sure the interrupt handler calls __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
  *
  *     - 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 a195873..da1582f 100644 (file)
@@ -61,11 +61,7 @@ void timer_interrupt(struct hw_trapframe *hw_tf, void *data)
        else
                oprofile_add_userpc(get_hwtf_pc(hw_tf));
 
        else
                oprofile_add_userpc(get_hwtf_pc(hw_tf));
 
-       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);
+       __trigger_tchain(&per_cpu_info[core_id()].tchain, hw_tf);
 }
 
 /* We can overflow/wraparound when we multiply up, but we have to divide last,
 }
 
 /* We can overflow/wraparound when we multiply up, but we have to divide last,