Ensures IRQs are enabled when proc_destroy()ing
[akaros.git] / kern / src / alarm.c
index 9b6809f..b4a00ed 100644 (file)
 #include <smp.h>
 
 /* Helper, resets the earliest/latest times, based on the elements of the list.
- * If the list is empty, any new waiters will be earlier and later than the
- * current (which is none). */
+ * If the list is empty, we set the times to be the 12345 poison time.  Since
+ * the list is empty, the alarm shouldn't be going off. */
 static void reset_tchain_times(struct timer_chain *tchain)
 {
        if (TAILQ_EMPTY(&tchain->waiters)) {
-               tchain->earliest_time = (uint64_t)-1;
-               tchain->latest_time = 0;
+               tchain->earliest_time = ALARM_POISON_TIME;
+               tchain->latest_time = ALARM_POISON_TIME;
        } else {
                tchain->earliest_time = TAILQ_FIRST(&tchain->waiters)->wake_up_time;
                tchain->latest_time =
@@ -49,7 +49,7 @@ void init_timer_chain(struct timer_chain *tchain,
 void init_awaiter(struct alarm_waiter *waiter,
                   void (*func) (struct alarm_waiter *awaiter))
 {
-       waiter->wake_up_time = (uint64_t)-1;
+       waiter->wake_up_time = ALARM_POISON_TIME;
        waiter->func = func;
        if (!func)
                init_sem(&waiter->sem, 0);
@@ -66,8 +66,22 @@ void set_awaiter_abs(struct alarm_waiter *waiter, uint64_t abs_time)
  * use than dealing with the TSC. */
 void set_awaiter_rel(struct alarm_waiter *waiter, uint64_t usleep)
 {
-       uint64_t now = read_tsc();
-       set_awaiter_abs(waiter, now + usleep * (system_timing.tsc_freq / 1000000));
+       uint64_t now, then;
+       now = read_tsc();
+       then = now + usec2tsc(usleep);
+       /* This will go off if we wrap-around the TSC.  It'll never happen for legit
+        * values, but this might catch some bugs with large usleeps. */
+       assert(now < then);
+       set_awaiter_abs(waiter, then);
+}
+
+/* Increment the timer that was already set, so that it goes off usleep usec
+ * from the previous tick.  This is different than 'rel' in that it doesn't care
+ * about when 'now' is. */
+void set_awaiter_inc(struct alarm_waiter *waiter, uint64_t usleep)
+{
+       assert(waiter->wake_up_time != ALARM_POISON_TIME);
+       waiter->wake_up_time += usec2tsc(usleep);
 }
 
 /* Helper, makes sure the interrupt is turned on at the right time.  Most of the
@@ -81,6 +95,7 @@ static void reset_tchain_interrupt(struct timer_chain *tchain)
                tchain->set_interrupt(0, tchain);
        } else {
                /* Make sure it is on and set to the earliest time */
+               assert(tchain->earliest_time != ALARM_POISON_TIME);
                /* TODO: check for times in the past or very close to now */
                printd("Turning alarm on for %llu\n", tchain->earliest_time);
                tchain->set_interrupt(tchain->earliest_time, tchain);
@@ -96,10 +111,12 @@ static void wake_awaiter(struct alarm_waiter *waiter)
        } else {
                /* Might encaps this */
                struct kthread *sleeper;
-               sleeper = __up_sem(&waiter->sem);
+               sleeper = __up_sem(&waiter->sem, TRUE);
                if (sleeper)
                        kthread_runnable(sleeper);
-               assert(TAILQ_EMPTY(&waiter->sem.waiters));
+               /* Don't touch the sleeper or waiter after making the kthread runnable,
+                * since it could be in use on another core (and the waiter can be
+                * clobbered as the kthread unwinds its stack). */
        }
 }
 
@@ -118,6 +135,9 @@ void trigger_tchain(struct timer_chain *tchain)
                if (i->wake_up_time <= now) {
                        changed_list = TRUE;
                        TAILQ_REMOVE(&tchain->waiters, i, next);
+                       /* 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);
                } else {
                        break;
@@ -136,44 +156,50 @@ void trigger_tchain(struct timer_chain *tchain)
 void set_alarm(struct timer_chain *tchain, struct alarm_waiter *waiter)
 {
        struct alarm_waiter *i, *temp;
-       bool reset_int = FALSE;
        int8_t irq_state = 0;
-       bool inserted = FALSE;
 
+       /* This will fail if you don't set a time */
+       assert(waiter->wake_up_time != ALARM_POISON_TIME);
        disable_irqsave(&irq_state);
-       /* Set the tchains upper and lower bounds, possibly needing a change to the
-        * interrupt time. */
+       /* Either the list is empty, or not. */
+       if (TAILQ_EMPTY(&tchain->waiters)) {
+               tchain->earliest_time = waiter->wake_up_time;
+               tchain->latest_time = waiter->wake_up_time;
+               TAILQ_INSERT_HEAD(&tchain->waiters, waiter, next);
+               /* Need to turn on the timer interrupt later */
+               goto reset_out;
+       }
+       /* If not, either we're first, last, or in the middle.  Reset the interrupt
+        * and adjust the tchain's times accordingly. */
        if (waiter->wake_up_time < tchain->earliest_time) {
                tchain->earliest_time = waiter->wake_up_time;
-               /* later on, set the core time to go off at the new, earliest time */
-               reset_int = TRUE;
+               TAILQ_INSERT_HEAD(&tchain->waiters, waiter, next);
+               /* Changed the first entry; we'll need to reset the interrupt later */
+               goto reset_out;
        }
-       if (waiter->wake_up_time > tchain->latest_time) {
+       /* If there is a tie for last, the newer one will really go last.  We need
+        * to handle equality here since the loop later won't catch it. */
+       if (waiter->wake_up_time >= tchain->latest_time) {
                tchain->latest_time = waiter->wake_up_time;
                /* Proactively put it at the end if we know we're last */
                TAILQ_INSERT_TAIL(&tchain->waiters, waiter, next);
-               inserted = TRUE;
+               goto no_reset_out;
        }
        /* Insert before the first one you are earlier than.  This won't scale well
         * (TODO) if we have a lot of inserts.  The proactive insert_tail up above
         * will help a bit. */
-       if (!inserted) {
-               TAILQ_FOREACH_SAFE(i, &tchain->waiters, next, temp) {
-                       if (waiter->wake_up_time < i->wake_up_time) {
-                               TAILQ_INSERT_BEFORE(i, waiter, next);
-                               inserted = TRUE;
-                               break;
-                       }
+       TAILQ_FOREACH_SAFE(i, &tchain->waiters, next, temp) {
+               if (waiter->wake_up_time < i->wake_up_time) {
+                       TAILQ_INSERT_BEFORE(i, waiter, next);
+                       goto no_reset_out;
                }
        }
-       /* Still not in?  The list ought to be empty. */
-       if (!inserted) {
-               assert(TAILQ_EMPTY(&tchain->waiters));
-               TAILQ_INSERT_HEAD(&tchain->waiters, waiter, next);
-       }
-       if (reset_int)
-               reset_tchain_interrupt(tchain);
+       panic("Could not find a spot for awaiter %08p\n", waiter);
+reset_out:
+       reset_tchain_interrupt(tchain);
+no_reset_out:
        enable_irqsave(&irq_state);
+       /* TODO: could put some debug stuff here */
 }
 
 /* Removes waiter from the tchain before it goes off. 
@@ -190,12 +216,12 @@ void unset_alarm(struct timer_chain *tchain, struct alarm_waiter *waiter)
         * the first and/or last element of the chain. */
        if (TAILQ_FIRST(&tchain->waiters) == waiter) {
                temp = TAILQ_NEXT(waiter, next);
-               tchain->earliest_time = (temp) ? temp->wake_up_time : (uint64_t)-1;
+               tchain->earliest_time = (temp) ? temp->wake_up_time : ALARM_POISON_TIME;
                reset_int = TRUE;               /* we'll need to reset the timer later */
        }
        if (TAILQ_LAST(&tchain->waiters, awaiters_tailq) == waiter) {
                temp = TAILQ_PREV(waiter, awaiters_tailq, next);
-               tchain->latest_time = (temp) ? temp->wake_up_time : 0;
+               tchain->latest_time = (temp) ? temp->wake_up_time : ALARM_POISON_TIME;
        }
        TAILQ_REMOVE(&tchain->waiters, waiter, next);
        if (reset_int)
@@ -240,7 +266,8 @@ void set_pcpu_alarm_interrupt(uint64_t time, struct timer_chain *tchain)
                if (time <= now)
                        rel_usec = 1;
                else
-                       rel_usec = ((time - now) / (system_timing.tsc_freq / 1000000));
+                       rel_usec = tsc2usec(time - now);
+               rel_usec = MAX(rel_usec, 1);
                printd("Setting alarm for %llu, it is now %llu, rel_time %llu "
                       "tchain %08p\n", time, now, rel_usec, pcpui_tchain);
                /* Note that sparc doesn't honor the one-shot setting, so you might get
@@ -255,6 +282,8 @@ void set_pcpu_alarm_interrupt(uint64_t time, struct timer_chain *tchain)
 }
 
 /* Debug helpers */
+
+/* Disable irqs before calling this, or otherwise protect yourself. */
 void print_chain(struct timer_chain *tchain)
 {
        struct alarm_waiter *i;
@@ -263,6 +292,24 @@ void print_chain(struct timer_chain *tchain)
               tchain->earliest_time,
               tchain->latest_time);
        TAILQ_FOREACH(i, &tchain->waiters, next) {
-               printk("\tWaiter %08p, time: %llu\n", i, i->wake_up_time);
+               struct kthread *kthread = TAILQ_FIRST(&i->sem.waiters);
+               printk("\tWaiter %08p, time: %llu, kthread: %08p (%08p)\n", i,
+                      i->wake_up_time, kthread, (kthread ? kthread->proc : 0));
+
        }
 }
+
+/* Prints all chains, rather verbosely */
+void print_pcpu_chains(void)
+{
+       struct timer_chain *pcpu_chain;
+       int8_t irq_state = 0;
+       printk("PCPU Chains:  It is now %llu\n", read_tsc());
+
+       disable_irqsave(&irq_state);
+       for (int i = 0; i < num_cpus; i++) {
+               pcpu_chain = &per_cpu_info[i].tchain;
+               print_chain(pcpu_chain);
+       }
+       enable_irqsave(&irq_state);
+}