Clunky adaptive mutexes
authorBarret Rhoden <brho@cs.berkeley.edu>
Tue, 9 Dec 2014 23:04:13 +0000 (15:04 -0800)
committerBarret Rhoden <brho@cs.berkeley.edu>
Wed, 10 Dec 2014 06:55:08 +0000 (22:55 -0800)
Semaphores will spin a bit before sleeping.  The amount of time is up to you
(CONFIG).

Note that you can't always profile the time spent spinning, since we can't
enable IRQs blindly in sem_down().  Even if we still have IRQs disabled when we
trydown(), we'd still deadlock since CVs are implemented with semaphores.

On a similar note, CVs and thus rendezes are implemented under the hood with
semaphores.  It's not clear to me that spinning is the right approach here.

Additionally, Akaros is a non-preemptive kernel, in that kernel code won't be
preempted for other routine kernel code.  If a sem is upped in a routine kernel
message (RKM), then spinning will be harmful if the unblocking RKM is sent to
the spinning core.

Kconfig
kern/src/kthread.c

diff --git a/Kconfig b/Kconfig
index ecb9ff1..9d9dc6f 100644 (file)
--- a/Kconfig
+++ b/Kconfig
@@ -147,6 +147,21 @@ config SEMAPHORE_DEBUG
                semaphore was downed, and provides a linked list of all semaphores that
                have waiters.  This will slow down all semaphore ups and downs.
 
+config SEM_SPINWAIT
+       bool "Semaphore spinwaiting"
+       default n
+       help
+               Turns on semaphore spinwaiting.  In lieu of intelligent Adaptive
+               Mutexes, busy semaphores will just spin for a while before fully
+               sleeping.
+
+config SEM_SPINWAIT_NR_LOOPS
+       int "Number of polls before sleeping"
+       depends on SEM_SPINWAIT
+       default 100
+       help
+               How many times to poll a busy semaphore before going to sleep.
+
 config RESET_STACKS
        bool "Reset Stacks"
        default y
index 87fc961..8150f8a 100644 (file)
@@ -260,6 +260,9 @@ void sem_init_irqsave(struct semaphore *sem, int signals)
 bool sem_trydown(struct semaphore *sem)
 {
        bool ret = FALSE;
+       /* lockless peek */
+       if (sem->nr_signals <= 0)
+               return ret;
        spin_lock(&sem->lock);
        if (sem->nr_signals > 0) {
                sem->nr_signals--;
@@ -288,8 +291,16 @@ void sem_down(struct semaphore *sem)
        assert(pcpui->cur_kthread);
        /* Try to down the semaphore.  If there is a signal there, we can skip all
         * of the sleep prep and just return. */
+#ifdef CONFIG_SEM_SPINWAIT
+       for (int i = 0; i < CONFIG_SEM_SPINWAIT_NR_LOOPS; i++) {
+               if (sem_trydown(sem))
+                       goto block_return_path;
+               cpu_relax();
+       }
+#else
        if (sem_trydown(sem))
                goto block_return_path;
+#endif
        /* We're probably going to sleep, so get ready.  We'll check again later. */
        kthread = pcpui->cur_kthread;
        /* We need to have a spare slot for restart, so we also use it when