Fixes tchain corruption
authorBarret Rhoden <brho@cs.berkeley.edu>
Fri, 22 Nov 2013 01:55:34 +0000 (17:55 -0800)
committerBarret Rhoden <brho@cs.berkeley.edu>
Thu, 16 Jan 2014 21:07:51 +0000 (13:07 -0800)
If you have multiple unsets of the same waiter, you would corrupt the
list.  I actually had this happen, though I'm not sure how.  The ksched
alarm was getting accidentally ripped off this list, causing all
processes to hang.  My hunch is that ping was setting and cancelling
alarms frequently, and perhaps userspace submitted concurrent requests
to unset its main alarm.

kern/src/alarm.c

index 7bee5f7..0b481ef 100644 (file)
@@ -108,8 +108,6 @@ static void reset_tchain_interrupt(struct timer_chain *tchain)
  * will wake up.  o/w, it will call the func ptr stored in the awaiter. */
 static void wake_awaiter(struct alarm_waiter *waiter)
 {
-       waiter->on_tchain = FALSE;
-       cmb();  /* enforce the on_tchain write before the handlers */
        if (waiter->func)
                waiter->func(waiter);
        else
@@ -132,7 +130,9 @@ void __trigger_tchain(uint32_t srcid, long a0, long a1, long a2)
                /* 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 */
@@ -157,6 +157,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 +233,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;
 }