BCQ touch-ups (XCC)
[akaros.git] / kern / src / alarm.c
index 7bee5f7..81891ba 100644 (file)
@@ -19,6 +19,7 @@
 #include <alarm.h>
 #include <stdio.h>
 #include <smp.h>
+#include <kmalloc.h>
 
 /* Helper, resets the earliest/latest times, based on the elements of the list.
  * If the list is empty, we set the times to be the 12345 poison time.  Since
@@ -47,14 +48,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. */
-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->func = func;
-       if (!func)
-               sem_init_irqsave(&waiter->sem, 0);
        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
@@ -104,39 +122,54 @@ 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. */
-static void wake_awaiter(struct alarm_waiter *waiter)
+static void wake_awaiter(struct alarm_waiter *waiter,
+                         struct hw_trapframe *hw_tf)
 {
-       waiter->on_tchain = FALSE;
-       cmb();  /* enforce the on_tchain write before the handlers */
-       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 */
+       }
 }
 
 /* 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;
-       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);
                /* TODO: Could also do something in cases where we're close to now */
                if (i->wake_up_time <= now) {
                        changed_list = TRUE;
+                       i->on_tchain = FALSE;
                        TAILQ_REMOVE(&tchain->waiters, i, next);
+                       cmb();  /* enforce waking after removal */
                        /* 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;
                }
@@ -146,7 +179,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);
-       spin_unlock(&tchain->lock);
+       spin_unlock_irqsave(&tchain->lock);
 }
 
 /* Helper, inserts the waiter into the tchain, returning TRUE if we still need
@@ -157,6 +190,7 @@ static bool __insert_awaiter(struct timer_chain *tchain,
        struct alarm_waiter *i, *temp;
        /* This will fail if you don't set a time */
        assert(waiter->wake_up_time != ALARM_POISON_TIME);
+       assert(!waiter->on_tchain);
        waiter->on_tchain = TRUE;
        /* Either the list is empty, or not. */
        if (TAILQ_EMPTY(&tchain->waiters)) {
@@ -232,6 +266,7 @@ static bool __remove_awaiter(struct timer_chain *tchain,
                tchain->latest_time = (temp) ? temp->wake_up_time : ALARM_POISON_TIME;
        }
        TAILQ_REMOVE(&tchain->waiters, waiter, next);
+       waiter->on_tchain = FALSE;
        return reset_int;
 }
 
@@ -278,7 +313,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;
-       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
@@ -297,7 +332,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 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
  *
@@ -320,7 +355,7 @@ void set_pcpu_alarm_interrupt(uint64_t time, struct timer_chain *tchain)
                /* TODO: using the LAPIC vector is a bit ghetto, since that's x86.  But
                 * RISCV ignores the vector field, and we don't have a global IRQ vector
                 * namespace or anything. */
-               send_ipi(rem_pcpui - &per_cpu_info[0], LAPIC_TIMER_DEFAULT_VECTOR);
+               send_ipi(rem_pcpui - &per_cpu_info[0], IdtLAPIC_TIMER);
                return;
        }
        if (time) {
@@ -352,6 +387,18 @@ void print_chain(struct timer_chain *tchain)
               tchain->earliest_time,
               tchain->latest_time);
        TAILQ_FOREACH(i, &tchain->waiters, next) {
+               if (i->has_func) {
+                       uintptr_t f;
+                       if (i->irq_ok)
+                               f = (uintptr_t)i->func_irq;
+                       else
+                               f = (uintptr_t)i->func;
+                       char *f_name = get_fn_name(f);
+                       printk("\tWaiter %p, time %llu, func %p (%s)\n", i,
+                              i->wake_up_time, f, f_name);
+                       kfree(f_name);
+                       continue;
+               }
                struct kthread *kthread = TAILQ_FIRST(&i->sem.waiters);
                printk("\tWaiter %p, time: %llu, kthread: %p (%p) %s\n", i,
                       i->wake_up_time, kthread, (kthread ? kthread->proc : 0),