Fixes set_alarm() for alarm handlers
authorBarret Rhoden <brho@cs.berkeley.edu>
Sun, 20 Oct 2013 17:05:48 +0000 (10:05 -0700)
committerBarret Rhoden <brho@cs.berkeley.edu>
Thu, 16 Jan 2014 19:12:19 +0000 (11:12 -0800)
set_alarm() grabs the tchain lock.  In alarm handlers, currently the
lock is already held while executing the handlers.

Instead, use __set_alarm() when the tchain lock is already held.

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

index ac6c0f8..d1b9e07 100644 (file)
@@ -33,7 +33,8 @@
  *     set_alarm(tchain, waiter);
  * If you want the HANDLER to run again, do this at the end of it:
  *     set_awaiter_rel(waiter, USEC);
  *     set_alarm(tchain, waiter);
  * If you want the HANDLER to run again, do this at the end of it:
  *     set_awaiter_rel(waiter, USEC);
- *     set_alarm(tchain, waiter);
+ *     __set_alarm(tchain, waiter);
+ * Do not call set_alarm() from within an alarm handler; you'll deadlock.
  * Don't forget to manage your memory at some (safe) point:
  *     kfree(waiter);
  * In the future, we might have a slab for these.  You can get it from wherever
  * Don't forget to manage your memory at some (safe) point:
  *     kfree(waiter);
  * In the future, we might have a slab for these.  You can get it from wherever
@@ -90,6 +91,7 @@ void set_awaiter_abs(struct alarm_waiter *waiter, uint64_t abs_time);
 void set_awaiter_rel(struct alarm_waiter *waiter, uint64_t usleep);
 void set_awaiter_inc(struct alarm_waiter *waiter, uint64_t usleep);
 /* Arms/disarms the alarm */
 void set_awaiter_rel(struct alarm_waiter *waiter, uint64_t usleep);
 void set_awaiter_inc(struct alarm_waiter *waiter, uint64_t usleep);
 /* Arms/disarms the alarm */
+void __set_alarm(struct timer_chain *tchain, struct alarm_waiter *waiter);
 void set_alarm(struct timer_chain *tchain, struct alarm_waiter *waiter);
 bool unset_alarm(struct timer_chain *tchain, struct alarm_waiter *waiter);
 /* Blocks on the alarm waiter */
 void set_alarm(struct timer_chain *tchain, struct alarm_waiter *waiter);
 bool unset_alarm(struct timer_chain *tchain, struct alarm_waiter *waiter);
 /* Blocks on the alarm waiter */
index 9a59a82..cf1fd70 100644 (file)
@@ -149,14 +149,13 @@ void trigger_tchain(struct timer_chain *tchain)
 }
 
 /* Sets the alarm.  If it is a kthread-style alarm (func == 0), sleep on it
 }
 
 /* Sets the alarm.  If it is a kthread-style alarm (func == 0), sleep on it
- * later.  Hold the lock, if applicable.  If this is a per-core tchain, the
- * interrupt-disabling ought to suffice. */
-void set_alarm(struct timer_chain *tchain, struct alarm_waiter *waiter)
+ * later.  This version assumes you have the lock held.  That only makes sense
+ * from alarm handlers, which are called with this lock held from IRQ context */
+void __set_alarm(struct timer_chain *tchain, struct alarm_waiter *waiter)
 {
        struct alarm_waiter *i, *temp;
        /* This will fail if you don't set a time */
        assert(waiter->wake_up_time != ALARM_POISON_TIME);
 {
        struct alarm_waiter *i, *temp;
        /* This will fail if you don't set a time */
        assert(waiter->wake_up_time != ALARM_POISON_TIME);
-       spin_lock_irqsave(&tchain->lock);
        /* has_fired tells us if it is on the tchain or not */
        waiter->has_fired = FALSE;
        /* Either the list is empty, or not. */
        /* has_fired tells us if it is on the tchain or not */
        waiter->has_fired = FALSE;
        /* Either the list is empty, or not. */
@@ -196,10 +195,19 @@ void set_alarm(struct timer_chain *tchain, struct alarm_waiter *waiter)
 reset_out:
        reset_tchain_interrupt(tchain);
 no_reset_out:
 reset_out:
        reset_tchain_interrupt(tchain);
 no_reset_out:
-       spin_unlock_irqsave(&tchain->lock);
+       ;
        /* TODO: could put some debug stuff here */
 }
 
        /* TODO: could put some debug stuff here */
 }
 
+/* Sets the alarm.  Don't call this from an alarm handler, since you already
+ * have the lock held.  Call __set_alarm() instead. */
+void set_alarm(struct timer_chain *tchain, struct alarm_waiter *waiter)
+{
+       spin_lock_irqsave(&tchain->lock);
+       __set_alarm(tchain, waiter);
+       spin_unlock_irqsave(&tchain->lock);
+}
+
 /* Removes waiter from the tchain before it goes off.  Returns TRUE if we
  * disarmed before the alarm went off, FALSE if it already fired. */
 bool unset_alarm(struct timer_chain *tchain, struct alarm_waiter *waiter)
 /* Removes waiter from the tchain before it goes off.  Returns TRUE if we
  * disarmed before the alarm went off, FALSE if it already fired. */
 bool unset_alarm(struct timer_chain *tchain, struct alarm_waiter *waiter)