Changes semaphore API
authorBarret Rhoden <brho@cs.berkeley.edu>
Fri, 9 Nov 2012 15:10:20 +0000 (07:10 -0800)
committerBarret Rhoden <brho@cs.berkeley.edu>
Fri, 9 Nov 2012 15:10:20 +0000 (07:10 -0800)
There now is an irqsave API, same as CVs.  All semaphore users need to
change and use irqsave if they will be called from irq context.  This
also renames sleep_on() to sem_down().  There's no awesome way to name
it, other than to know what it means to down a semaphore.

Also, we now only disable irqs when blocking a kthread for the smallest
amount of time (unless its irqsave): when we need to atomically sleep
and change to a new thread (stack).

kern/include/kthread.h
kern/src/alarm.c
kern/src/blockdev.c
kern/src/console.c
kern/src/ext2fs.c
kern/src/kthread.c
kern/src/page_alloc.c
kern/src/testing.c

index 576f692..43d899f 100644 (file)
@@ -43,78 +43,29 @@ struct cond_var {
        unsigned long                           nr_waiters;
 };
 
-/* This doesn't have to be inline, but it doesn't matter for now */
-static inline void init_sem(struct semaphore *sem, int signals)
-{
-       TAILQ_INIT(&sem->waiters);
-       sem->nr_signals = signals;
-       spinlock_init(&sem->lock);
-}
-
-/* Down and up for the semaphore are a little more low-level than usual, since
- * they are meant to be called by functions that manage the sleeping of a
- * kthread.  For instance, __down_sem() always returns right away.  For now,
- * these are just examples, since the actual usage will probably need lower
- * access. */
-
-/* Down : decrement, if it was 0 or less, we need to sleep.  Returns false if
- * the kthread did not need to sleep (the signal was already there). */
-static inline bool __down_sem(struct semaphore *sem, struct kthread *kthread)
-{
-       /* Don't actually use this, this is an example of how to build a sem */
-       assert(0);
-       bool retval = FALSE;
-       spin_lock_irqsave(&sem->lock);
-       if (sem->nr_signals-- <= 0) {
-               /* Need to sleep */
-               retval = TRUE;
-               TAILQ_INSERT_TAIL(&sem->waiters, kthread, link);
-       }
-       spin_unlock_irqsave(&sem->lock);
-       return retval;
-}
-
-/* Ups the semaphore.  If it was < 0, we need to wake up someone, which is the
- * return value.  If you think there should be at most one, set exactly_one. */
-static inline struct kthread *__up_sem(struct semaphore *sem, bool exactly_one)
-{
-       struct kthread *kthread = 0;
-       spin_lock_irqsave(&sem->lock);
-       if (sem->nr_signals++ < 0) {
-               /* could do something with 'priority' here */
-               kthread = TAILQ_FIRST(&sem->waiters);
-               TAILQ_REMOVE(&sem->waiters, kthread, link);
-               if (exactly_one)
-                       assert(TAILQ_EMPTY(&sem->waiters));
-       } else {
-               assert(TAILQ_EMPTY(&sem->waiters));
-       }
-       spin_unlock_irqsave(&sem->lock);
-       return kthread;
-}
-
 void kthread_init(void);
-void sleep_on(struct semaphore *sem);
 void restart_kthread(struct kthread *kthread);
 void kthread_runnable(struct kthread *kthread);
-/* Kmsg handler to launch/run a kthread.  This must be a routine message, since
- * it does not return. */
-void __launch_kthread(struct trapframe *tf, uint32_t srcid, long a0, long a1,
-                         long a2);
 void kthread_yield(void);
 
+void sem_init(struct semaphore *sem, int signals);
+void sem_down(struct semaphore *sem);
+bool sem_up(struct semaphore *sem);
+void sem_down_irqsave(struct semaphore *sem, int8_t *irq_state);
+bool sem_up_irqsave(struct semaphore *sem, int8_t *irq_state);
+
 void cv_init(struct cond_var *cv);
 void cv_lock(struct cond_var *cv);
 void cv_unlock(struct cond_var *cv);
-void cv_lock_irqsave(struct cond_var *cv, int8_t *state);
-void cv_unlock_irqsave(struct cond_var *cv, int8_t *state);
+void cv_lock_irqsave(struct cond_var *cv, int8_t *irq_state);
+void cv_unlock_irqsave(struct cond_var *cv, int8_t *irq_state);
 void cv_wait_and_unlock(struct cond_var *cv);  /* does not mess with irqs */
 void cv_wait(struct cond_var *cv);
 void __cv_signal(struct cond_var *cv);
 void __cv_broadcast(struct cond_var *cv);
 void cv_signal(struct cond_var *cv);
 void cv_broadcast(struct cond_var *cv);
-void cv_signal_irqsave(struct cond_var *cv, int8_t *state);
-void cv_broadcast_irqsave(struct cond_var *cv, int8_t *state);
+void cv_signal_irqsave(struct cond_var *cv, int8_t *irq_state);
+void cv_broadcast_irqsave(struct cond_var *cv, int8_t *irq_state);
 
 #endif /* ROS_KERN_KTHREAD_H */
index b4a00ed..889ca0f 100644 (file)
@@ -52,7 +52,7 @@ void init_awaiter(struct alarm_waiter *waiter,
        waiter->wake_up_time = ALARM_POISON_TIME;
        waiter->func = func;
        if (!func)
-               init_sem(&waiter->sem, 0);
+               sem_init(&waiter->sem, 0);
 }
 
 /* Give this the absolute time.  For now, abs_time is the TSC time that you want
@@ -106,18 +106,11 @@ 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)
 {
-       if (waiter->func) {
+       int8_t irq_state = 0;
+       if (waiter->func)
                waiter->func(waiter);
-       } else {
-               /* Might encaps this */
-               struct kthread *sleeper;
-               sleeper = __up_sem(&waiter->sem, TRUE);
-               if (sleeper)
-                       kthread_runnable(sleeper);
-               /* 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). */
-       }
+       else
+               sem_up_irqsave(&waiter->sem, &irq_state);
 }
 
 /* This is called when an interrupt triggers a tchain, and needs to wake up
@@ -233,12 +226,13 @@ void unset_alarm(struct timer_chain *tchain, struct alarm_waiter *waiter)
  * (process limit, etc).  Don't call it on a waiter that is an event-handler. */
 int sleep_on_awaiter(struct alarm_waiter *waiter)
 {
+       int8_t irq_state = 0;
        if (waiter->func)
                panic("Tried blocking on a waiter %08p with a func %08p!", waiter,
                      waiter->func);
        /* Put the kthread to sleep.  TODO: This can fail (or at least it will be
         * able to in the future) and we'll need to handle that. */
-       sleep_on(&waiter->sem);
+       sem_down_irqsave(&waiter->sem, &irq_state);
        return 0;
 }
 
index 3a40b8a..879e062 100644 (file)
@@ -138,14 +138,12 @@ int bdev_submit_request(struct block_device *bdev, struct block_request *breq)
 /* Helper method, unblocks someone blocked on sleep_on_breq(). */
 void generic_breq_done(struct block_request *breq)
 {
+       int8_t irq_state = 0;
 #ifdef __i386__        /* Sparc can't restart kthreads yet */
-       struct kthread *sleeper = __up_sem(&breq->sem, TRUE);
-       if (!sleeper) {
+       if (!sem_up_irqsave(&breq->sem, &irq_state)) {
                /* This shouldn't happen anymore.  Let brho know if it does. */
                warn("[kernel] no one waiting on breq %08p", breq);
-               return;
        }
-       kthread_runnable(sleeper);
 #else
        breq->data = (void*)1;
 #endif
@@ -156,11 +154,12 @@ void generic_breq_done(struct block_request *breq)
  * for real block devices (that don't fake things with timer interrupts). */
 void sleep_on_breq(struct block_request *breq)
 {
+       int8_t irq_state = 0;
        /* Since printk takes a while, this may make you lose the race */
        printd("Sleeping on breq %08p\n", breq);
        assert(irq_is_enabled());
 #ifdef __i386__
-       sleep_on(&breq->sem);
+       sem_down_irqsave(&breq->sem, &irq_state);
 #else
        /* Sparc can't block yet (TODO).  This only works if the completion happened
         * first (for now) */
@@ -263,7 +262,7 @@ found:
        breq->flags = BREQ_READ;
        breq->callback = generic_breq_done;
        breq->data = 0;
-       init_sem(&breq->sem, 0);
+       sem_init(&breq->sem, 0);
        breq->bhs = breq->local_bhs;
        breq->bhs[0] = bh;
        breq->nr_bhs = 1;
index 20d7d3e..56b92df 100644 (file)
@@ -16,7 +16,7 @@ void kb_buf_init(struct kb_buffer *kb)
        kb->prod_idx = 0;
        kb->cons_idx = 0;
        spinlock_init(&kb->buf_lock);
-       init_sem(&kb->buf_sem, 0);
+       sem_init(&kb->buf_sem, 0);
        /* no need to memset the buffer - we only read something that is written */
 }
 
@@ -26,7 +26,6 @@ void kb_add_to_buf(struct kb_buffer *kb, char c)
 {
        /* make sure we're a power of 2 */
        static_assert(KB_BUF_SIZE == __RD32(KB_BUF_SIZE));
-       struct kthread *sleeper;
        bool was_empty = FALSE;
        spin_lock(&kb->buf_lock);
        if (!__ring_full(KB_BUF_SIZE, kb->prod_idx, kb->cons_idx)) {
@@ -43,11 +42,8 @@ void kb_add_to_buf(struct kb_buffer *kb, char c)
         * blockers: if there are any items in the buffer, either the sem is upped,
         * or there is an active consumer.  consumers immediately down (to become an
         * active consumer). */
-       if (was_empty) {
-               sleeper = __up_sem(&kb->buf_sem, FALSE);
-               if (sleeper)
-                       kthread_runnable(sleeper);
-       }
+       if (was_empty)
+               sem_up(&kb->buf_sem);
        /* also note that multiple readers on the console/serial are going to fight
         * for input and it is going to get interleaved - broader issue */
 }
@@ -55,7 +51,6 @@ void kb_add_to_buf(struct kb_buffer *kb, char c)
 /* Will read cnt chars from the KB buf into dst.  Will block until complete. */
 void kb_get_from_buf(struct kb_buffer *kb, char *dst, size_t cnt)
 {
-       struct kthread *sleeper;
        unsigned int dst_idx = 0; /* aka, amt copied so far */
        bool need_an_up = FALSE;
 
@@ -65,7 +60,7 @@ void kb_get_from_buf(struct kb_buffer *kb, char *dst, size_t cnt)
        while (dst_idx < cnt) {
                /* this will return immediately if some data is already there, o/w we
                 * block til there is some activity */
-               sleep_on(&kb->buf_sem);
+               sem_down(&kb->buf_sem);
                spin_lock(&kb->buf_lock);
                /* under the current scheme, we should only have one active consumer at
                 * a time, so if we woke up, the ring must not be empty. */
@@ -84,11 +79,8 @@ void kb_get_from_buf(struct kb_buffer *kb, char *dst, size_t cnt)
        }
        /* Remember: if the buf is non empty, there is either an active consumer or
         * the sem is upped. */
-       if (need_an_up) {
-               sleeper = __up_sem(&kb->buf_sem, FALSE);
-               if (sleeper)
-                       kthread_runnable(sleeper);
-       }
+       if (need_an_up)
+               sem_up(&kb->buf_sem);
 }
 
 /* Kernel messages associated with the console.  Arch-specific interrupt
index c81a223..bac92c4 100644 (file)
@@ -715,7 +715,7 @@ int ext2_readpage(struct page_map *pm, struct page *page)
        breq->flags = BREQ_READ;
        breq->callback = generic_breq_done;
        breq->data = 0;
-       init_sem(&breq->sem, 0);
+       sem_init(&breq->sem, 0);
        breq->bhs = breq->local_bhs;
        breq->nr_bhs = 0;
        /* Pack the BH pointers in the block request */
index 12cfdd2..3d3005f 100644 (file)
@@ -20,20 +20,159 @@ void kthread_init(void)
                                           __alignof__(struct kthread), 0, 0, 0);
 }
 
+/* Starts kthread on the calling core.  This does not return, and will handle
+ * the details of cleaning up whatever is currently running (freeing its stack,
+ * etc).  Pairs with sem_down(). */
+void restart_kthread(struct kthread *kthread)
+{
+       struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
+       uintptr_t current_stacktop;
+       /* Avoid messy complications.  The kthread will enable_irqsave() when it
+        * comes back up. */
+       disable_irq();
+       /* Free any spare, since we need ours to become the spare (since we can't
+        * free our current kthread *before* popping it, nor can we free the current
+        * stack until we pop to the kthread's stack). */
+       if (pcpui->spare) {
+               /* assumes the stack is a page, and that stacktop is somewhere in
+                * (pg_bottom, pg_bottom + PGSIZE].  Normally, it ought to be pg_bottom
+                * + PGSIZE (on x86).  kva2page can take any kva, not just a page
+                * aligned addr. */
+               page_decref(kva2page((void*)pcpui->spare->stacktop - 1));
+               kmem_cache_free(kthread_kcache, pcpui->spare);
+       }
+       current_stacktop = get_stack_top();
+       /* When a kthread runs, its stack is the default kernel stack */
+       set_stack_top(kthread->stacktop);
+#ifdef __CONFIG_KTHREAD_POISON__
+       /* TODO: KTHR-STACK */
+       /* Assert and switch to cur stack not in use, kthr stack in use */
+       uintptr_t *cur_stack_poison, *kth_stack_poison;
+       cur_stack_poison = (uintptr_t*)ROUNDDOWN(current_stacktop - 1, PGSIZE);
+       assert(*cur_stack_poison == 0xdeadbeef);
+       *cur_stack_poison = 0;
+       kth_stack_poison = (uintptr_t*)ROUNDDOWN(kthread->stacktop - 1, PGSIZE);
+       assert(!*kth_stack_poison);
+       *kth_stack_poison = 0xdeadbeef;
+#endif /* __CONFIG_KTHREAD_POISON__ */
+       /* Set the spare stuff (current kthread, current (not kthread) stacktop) */
+       pcpui->spare = kthread;
+       kthread->stacktop = current_stacktop;
+       /* Only change current if we need to (the kthread was in process context) */
+       if (kthread->proc) {
+               /* Load our page tables before potentially decreffing cur_proc */
+               lcr3(kthread->proc->env_cr3);
+               /* Might have to clear out an existing current.  If they need to be set
+                * later (like in restartcore), it'll be done on demand. */
+               if (pcpui->cur_proc)
+                       proc_decref(pcpui->cur_proc);
+               /* We also transfer our counted ref from kthread->proc to cur_proc */
+               pcpui->cur_proc = kthread->proc;
+       }
+       /* Tell the core which syscall we are running (if any) */
+       assert(!pcpui->cur_sysc);       /* catch bugs, prev user should clear */
+       pcpui->cur_sysc = kthread->sysc;
+       /* Finally, restart our thread */
+       pop_kernel_tf(&kthread->context);
+}
+
+/* Kmsg handler to launch/run a kthread.  This must be a routine message, since
+ * it does not return.  */
+static void __launch_kthread(struct trapframe *tf, uint32_t srcid, long a0,
+                             long a1, long a2)
+{
+       struct kthread *kthread = (struct kthread*)a0;
+       struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
+       struct proc *cur_proc = pcpui->cur_proc;
+       
+       if (pcpui->owning_proc && pcpui->owning_proc != kthread->proc) {
+               /* Some process should be running here that is not the same as the
+                * kthread.  This means the _M is getting interrupted or otherwise
+                * delayed.  If we want to do something other than run it (like send the
+                * kmsg to another pcore, or ship the context from here to somewhere
+                * else/deschedule it (like for an _S)), do it here. */
+               cmb();  /* do nothing/placeholder */
+               #if 0
+               /* example of something to do (wrap up and schedule an _S).  Note this
+                * might not work perfectly, but is just an example.  One thing to be
+                * careful of is that spin_lock() can't be called if __launch isn't
+                * ROUTINE (which it is right now). */
+               if (pcpui->owning_proc->state == PROC_RUNNING_S) {
+                       spin_lock(&pcpui->owning_proc->proc_lock);
+                       /* Wrap up / yield the _S proc */
+                       __proc_set_state(pcpui->owning_proc, PROC_WAITING);
+                       __proc_save_context_s(pcpui->owning_proc, current_tf);
+                       spin_unlock(&pcpui->owning_proc->proc_lock);
+                       proc_wakeup(p);
+                       abandon_core();
+                       /* prob need to clear the owning proc?  this is some old shit, so
+                        * don't just uncomment it. */
+               }
+               #endif
+       }
+       /* o/w, just run the kthread.  any trapframes that are supposed to run or
+        * were interrupted will run whenever the kthread smp_idles() or otherwise
+        * finishes. */
+       restart_kthread(kthread);
+       assert(0);
+}
+
+/* Call this when a kthread becomes runnable/unblocked.  We don't do anything
+ * particularly smart yet, but when we do, we can put it here. */
+void kthread_runnable(struct kthread *kthread)
+{
+       uint32_t dst = core_id();
+       #if 0
+       /* turn this block on if you want to test migrating non-core0 kthreads */
+       switch (dst) {
+               case 0:
+                       break;
+               case 7:
+                       dst = 2;
+                       break;
+               default:
+                       dst++;
+       }
+       #endif
+       /* For lack of anything better, send it to ourselves. (TODO: KSCHED) */
+       send_kernel_message(dst, __launch_kthread, (long)kthread, 0, 0,
+                           KMSG_ROUTINE);
+}
+
+/* Stop the current kthread.  It'll get woken up next time we run routine kmsgs,
+ * after all existing kmsgs are processed. */
+void kthread_yield(void)
+{
+       struct semaphore local_sem, *sem = &local_sem;
+       void __wake_me_up(struct trapframe *tf, uint32_t srcid, long a0, long a1,
+                         long a2)
+       {
+               assert(sem_up(sem));
+       }
+       sem_init(sem, 0);
+       send_kernel_message(core_id(), __wake_me_up, 0, 0, 0, KMSG_ROUTINE);
+       sem_down(sem);
+}
+
+/* Semaphores, using kthreads directly */
+void sem_init(struct semaphore *sem, int signals)
+{
+       TAILQ_INIT(&sem->waiters);
+       sem->nr_signals = signals;
+       spinlock_init(&sem->lock);
+}
+
 /* This downs the semaphore and suspends the current kernel context on its
  * waitqueue if there are no pending signals.  Note that the case where the
  * signal is already there is not optimized. */
-void sleep_on(struct semaphore *sem)
+void sem_down(struct semaphore *sem)
 {
        volatile bool blocking = TRUE;  /* signal to short circuit when restarting*/
        struct kthread *kthread;
        struct page *page;                              /* assumption here that stacks are PGSIZE */
        register uintptr_t new_stacktop;
-       int8_t irq_state = 0;
        struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
 
-       /* interrupts would be messy here */
-       disable_irqsave(&irq_state);
        /* Make sure we aren't holding any locks (only works if SPINLOCK_DEBUG) */
        assert(!pcpui->lock_depth);
        /* Try to down the semaphore.  If there is a signal there, we can skip all
@@ -42,10 +181,10 @@ void sleep_on(struct semaphore *sem)
        if (sem->nr_signals > 0) {
                sem->nr_signals--;
                spin_unlock(&sem->lock);
-               goto block_return_path_np;
+               goto block_return_path;
        }
-       /* we're probably going to sleep, so get ready.  We'll check again later. */
        spin_unlock(&sem->lock);
+       /* We're probably going to sleep, so get ready.  We'll check again later. */
        /* Try to get the spare first.  If there is one, we'll use it (o/w, we'll
         * get a fresh kthread.  Why we need this is more clear when we try to
         * restart kthreads.  Having them also ought to cut down on contention.
@@ -99,17 +238,25 @@ void sleep_on(struct semaphore *sem)
        /* Down the semaphore.  We need this to be inline.  If we're sleeping, once
         * we unlock the kthread could be started up again and can return and start
         * trashing this function's stack, hence the weird control flow. */
-       spin_lock(&sem->lock);  /* no need for irqsave, since we disabled ints */
-       if (sem->nr_signals-- <= 0)
+       spin_lock(&sem->lock);
+       if (sem->nr_signals-- <= 0) {
                TAILQ_INSERT_TAIL(&sem->waiters, kthread, link);
-       else                                                            /* we didn't sleep */
+               /* At this point, we know we'll sleep and change stacks later.  Once we
+                * unlock, we could have the kthread restarted (possibly on another
+                * core), so we need to disable irqs until we are on our new stack.
+                * Otherwise, if we take an IRQ, we'll be using our stack while another
+                * core is using it (restarted kthread).  Basically, disabling irqs
+                * allows us to atomically unlock and 'yield'. */
+               disable_irq();
+       } else {                                                        /* we didn't sleep */
                goto unwind_sleep_prep;
+       }
        spin_unlock(&sem->lock);
        /* Switch to the core's default stack.  After this, don't use local
         * variables.  TODO: we shouldn't be using new_stacktop either, can't always
         * trust the register keyword (AFAIK). */
        set_stack_pointer(new_stacktop);
-       smp_idle();
+       smp_idle();                                                     /* reenables irqs eventually */
        /* smp_idle never returns */
        assert(0);
 unwind_sleep_prep:
@@ -135,151 +282,56 @@ unwind_sleep_prep:
 #endif /* __CONFIG_KTHREAD_POISON__ */
 block_return_path:
        printd("[kernel] Returning from being 'blocked'! at %llu\n", read_tsc());
-block_return_path_np:
-       enable_irqsave(&irq_state);
        return;
 }
 
-/* Starts kthread on the calling core.  This does not return, and will handle
- * the details of cleaning up whatever is currently running (freeing its stack,
- * etc). */
-void restart_kthread(struct kthread *kthread)
+/* Ups the semaphore.  If it was < 0, we need to wake up someone, which we do.
+ * Returns TRUE if we woke someone, FALSE o/w (used for debugging in some
+ * places).  If we need more control, we can implement a version of the old
+ * __up_sem() again.  */
+bool sem_up(struct semaphore *sem)
 {
-       struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
-       uintptr_t current_stacktop;
-       /* Avoid messy complications.  The kthread will enable_irqsave() when it
-        * comes back up. */
-       disable_irq();
-       /* Free any spare, since we need ours to become the spare (since we can't
-        * free our current kthread *before* popping it, nor can we free the current
-        * stack until we pop to the kthread's stack). */
-       if (pcpui->spare) {
-               /* assumes the stack is a page, and that stacktop is somewhere in
-                * (pg_bottom, pg_bottom + PGSIZE].  Normally, it ought to be pg_bottom
-                * + PGSIZE (on x86).  kva2page can take any kva, not just a page
-                * aligned addr. */
-               page_decref(kva2page((void*)pcpui->spare->stacktop - 1));
-               kmem_cache_free(kthread_kcache, pcpui->spare);
-       }
-       current_stacktop = get_stack_top();
-       /* When a kthread runs, its stack is the default kernel stack */
-       set_stack_top(kthread->stacktop);
-#ifdef __CONFIG_KTHREAD_POISON__
-       /* TODO: KTHR-STACK */
-       /* Assert and switch to cur stack not in use, kthr stack in use */
-       uintptr_t *cur_stack_poison, *kth_stack_poison;
-       cur_stack_poison = (uintptr_t*)ROUNDDOWN(current_stacktop - 1, PGSIZE);
-       assert(*cur_stack_poison == 0xdeadbeef);
-       *cur_stack_poison = 0;
-       kth_stack_poison = (uintptr_t*)ROUNDDOWN(kthread->stacktop - 1, PGSIZE);
-       assert(!*kth_stack_poison);
-       *kth_stack_poison = 0xdeadbeef;
-#endif /* __CONFIG_KTHREAD_POISON__ */
-       /* Set the spare stuff (current kthread, current (not kthread) stacktop) */
-       pcpui->spare = kthread;
-       kthread->stacktop = current_stacktop;
-       /* Only change current if we need to (the kthread was in process context) */
-       if (kthread->proc) {
-               /* Load our page tables before potentially decreffing cur_proc */
-               lcr3(kthread->proc->env_cr3);
-               /* Might have to clear out an existing current.  If they need to be set
-                * later (like in restartcore), it'll be done on demand. */
-               if (pcpui->cur_proc)
-                       proc_decref(pcpui->cur_proc);
-               /* We also transfer our counted ref from kthread->proc to cur_proc */
-               pcpui->cur_proc = kthread->proc;
+       struct kthread *kthread = 0;
+       spin_lock(&sem->lock);
+       if (sem->nr_signals++ < 0) {
+               assert(!TAILQ_EMPTY(&sem->waiters));
+               /* could do something with 'priority' here */
+               kthread = TAILQ_FIRST(&sem->waiters);
+               TAILQ_REMOVE(&sem->waiters, kthread, link);
+       } else {
+               assert(TAILQ_EMPTY(&sem->waiters));
        }
-       /* Tell the core which syscall we are running (if any) */
-       assert(!pcpui->cur_sysc);       /* catch bugs, prev user should clear */
-       pcpui->cur_sysc = kthread->sysc;
-       /* Finally, restart our thread */
-       pop_kernel_tf(&kthread->context);
-}
-
-/* Call this when a kthread becomes runnable/unblocked.  We don't do anything
- * particularly smart yet, but when we do, we can put it here. */
-void kthread_runnable(struct kthread *kthread)
-{
-       uint32_t dst = core_id();
-       #if 0
-       /* turn this block on if you want to test migrating non-core0 kthreads */
-       switch (dst) {
-               case 0:
-                       break;
-               case 7:
-                       dst = 2;
-                       break;
-               default:
-                       dst++;
+       spin_unlock(&sem->lock);
+       /* Note that once we call kthread_runnable(), we cannot touch the sem again.
+        * Some sems are on stacks.  The caller can touch sem, if it knows about the
+        * memory/usage of the sem.  Likewise, we can't touch the kthread either. */
+       if (kthread) {
+               kthread_runnable(kthread);
+               return TRUE;
        }
-       #endif
-       /* For lack of anything better, send it to ourselves. (TODO: KSCHED) */
-       send_kernel_message(dst, __launch_kthread, (long)kthread, 0, 0,
-                           KMSG_ROUTINE);
+       return FALSE;
 }
 
-/* Kmsg handler to launch/run a kthread.  This must be a routine message, since
- * it does not return.  */
-void __launch_kthread(struct trapframe *tf, uint32_t srcid, long a0, long a1,
-                         long a2)
+void sem_down_irqsave(struct semaphore *sem, int8_t *irq_state)
 {
-       struct kthread *kthread = (struct kthread*)a0;
-       struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
-       struct proc *cur_proc = pcpui->cur_proc;
-       
-       if (pcpui->owning_proc && pcpui->owning_proc != kthread->proc) {
-               /* Some process should be running here that is not the same as the
-                * kthread.  This means the _M is getting interrupted or otherwise
-                * delayed.  If we want to do something other than run it (like send the
-                * kmsg to another pcore, or ship the context from here to somewhere
-                * else/deschedule it (like for an _S)), do it here. */
-               cmb();  /* do nothing/placeholder */
-               #if 0
-               /* example of something to do (wrap up and schedule an _S).  Note this
-                * might not work perfectly, but is just an example.  One thing to be
-                * careful of is that spin_lock() can't be called if __launch isn't
-                * ROUTINE (which it is right now). */
-               if (pcpui->owning_proc->state == PROC_RUNNING_S) {
-                       spin_lock(&pcpui->owning_proc->proc_lock);
-                       /* Wrap up / yield the _S proc */
-                       __proc_set_state(pcpui->owning_proc, PROC_WAITING);
-                       __proc_save_context_s(pcpui->owning_proc, current_tf);
-                       spin_unlock(&pcpui->owning_proc->proc_lock);
-                       proc_wakeup(p);
-                       abandon_core();
-                       /* prob need to clear the owning proc?  this is some old shit, so
-                        * don't just uncomment it. */
-               }
-               #endif
-       }
-       /* o/w, just run the kthread.  any trapframes that are supposed to run or
-        * were interrupted will run whenever the kthread smp_idles() or otherwise
-        * finishes. */
-       restart_kthread(kthread);
-       assert(0);
+       disable_irqsave(irq_state);
+       sem_down(sem);
+       enable_irqsave(irq_state);
 }
 
-/* Stop the current kthread.  It'll get woken up next time we run routine kmsgs,
- * after all existing kmsgs are processed. */
-void kthread_yield(void)
+bool sem_up_irqsave(struct semaphore *sem, int8_t *irq_state)
 {
-       struct semaphore local_sem, *sem = &local_sem;
-       void __wake_me_up(struct trapframe *tf, uint32_t srcid, long a0, long a1,
-                         long a2)
-       {
-               struct kthread *me = __up_sem(sem, TRUE);
-               assert(me);
-               kthread_runnable(me);
-       }
-       init_sem(sem, 0);
-       send_kernel_message(core_id(), __wake_me_up, 0, 0, 0, KMSG_ROUTINE);
-       sleep_on(sem);
+       bool retval;
+       disable_irqsave(irq_state);
+       retval = sem_up(sem);
+       enable_irqsave(irq_state);
+       return retval;
 }
 
 /* Condition variables, using semaphores and kthreads */
 void cv_init(struct cond_var *cv)
 {
-       init_sem(&cv->sem, 0);
+       sem_init(&cv->sem, 0);
        spinlock_init(&cv->lock);
        cv->nr_waiters = 0;
 }
@@ -331,7 +383,7 @@ void cv_wait_and_unlock(struct cond_var *cv)
               core_id(), nr_sem_waiters(&cv->sem), cv->nr_waiters);
        /* Atomically sleeps and 'unlocks' the next kthread from its busy loop (the
         * one right above this), when it changes the sems nr_signals/waiters. */
-       sleep_on(&cv->sem);
+       sem_down(&cv->sem);
 }
 
 /* Comes in locked.  Note cv_lock does not disable irqs. */
@@ -345,12 +397,13 @@ void cv_wait(struct cond_var *cv)
 static void sem_wake_one(struct semaphore *sem)
 {
        struct kthread *kthread;
-       spin_lock_irqsave(&sem->lock);
+       /* these locks will be irqsaved if the CV is irqsave (only need the one) */
+       spin_lock(&sem->lock);
        assert(sem->nr_signals < 0);
        sem->nr_signals++;
        kthread = TAILQ_FIRST(&sem->waiters);
        TAILQ_REMOVE(&sem->waiters, kthread, link);
-       spin_unlock_irqsave(&sem->lock);
+       spin_unlock(&sem->lock);
        kthread_runnable(kthread);
 }
 
index 9ed659c..00653e8 100644 (file)
@@ -50,7 +50,7 @@ static void __page_init(struct page *page)
 {
        memset(page, 0, sizeof(page_t));
        page_setref(page, 1);
-       init_sem(&page->pg_sem, 0);
+       sem_init(&page->pg_sem, 0);
 }
 
 #define __PAGE_ALLOC_FROM_RANGE_GENERIC(page, base_color, range, predicate) \
@@ -301,7 +301,7 @@ void page_setref(page_t *page, size_t val)
 void lock_page(struct page *page)
 {
        /* when this returns, we have are the ones to have locked the page */
-       sleep_on(&page->pg_sem);
+       sem_down(&page->pg_sem);
        assert(!(page->pg_flags & PG_LOCKED));
        page->pg_flags |= PG_LOCKED;
 }
@@ -309,12 +309,9 @@ void lock_page(struct page *page)
 /* Unlocks the page, and wakes up whoever is waiting on the lock */
 void unlock_page(struct page *page)
 {
-       struct kthread *sleeper;
        page->pg_flags &= ~PG_LOCKED;
-       sleeper = __up_sem(&page->pg_sem, FALSE);
-       if (sleeper) {
+       if (sem_up(&page->pg_sem)) {
                printk("Unexpected sleeper on a page!");        /* til we test this */
-               kthread_runnable(sleeper);
        }
 }
 
index 9ad9038..10ae441 100644 (file)
@@ -1318,20 +1318,16 @@ void test_kthreads(void)
                         long a2)
        {
                struct semaphore *sem = (struct semaphore*)a0;
-               struct kthread *kthread;
                printk("[kmsg] Upping the sem to start the kthread, stacktop is %08p\n",
                       get_stack_top());
-               kthread = __up_sem(sem, FALSE);
-               if (!kthread) {
+               if (!sem_up(sem)) {
                        printk("[kmsg] Crap, the sem didn't have a kthread waiting!\n");
                        return;
                }
-               printk("[kmsg] Restarting the kthread...\n");
-               restart_kthread(kthread);
-               panic("[kmsg] Damnit...");
+               printk("Kthread will restart when we handle the __launch RKM\n");
        }
        struct semaphore sem;
-       init_sem(&sem, 1);              /* set to 1 to test the unwind */
+       sem_init(&sem, 1);              /* set to 1 to test the unwind */
        printk("We're a kthread!  Stacktop is %08p.  Testing suspend, etc...\n",
               get_stack_top());
        /* So we have something that will wake us up.  Routine messages won't get
@@ -1341,10 +1337,10 @@ void test_kthreads(void)
        /* Actually block (or try to) */
        /* This one shouldn't block - but will test the unwind (if 1 above) */
        printk("About to sleep, but should unwind (signal beat us)\n");
-       sleep_on(&sem);
+       sem_down(&sem);
        /* This one is for real, yo.  Run and tell that. */
        printk("About to sleep for real\n");
-       sleep_on(&sem);
+       sem_down(&sem);
        printk("Kthread restarted!, Stacktop is %08p.\n", get_stack_top());
 }