MCS locks have a "notification-safe" variant
authorBarret Rhoden <brho@cs.berkeley.edu>
Sat, 5 Mar 2011 01:41:18 +0000 (17:41 -0800)
committerKevin Klues <klueska@cs.berkeley.edu>
Thu, 3 Nov 2011 00:36:00 +0000 (17:36 -0700)
Any lock that is grabbed from vcore context needs to use
mcs_lock_notifsafe(), which is analagous to using irqsave in the kernel
on its spin_locks.

user/parlib/include/mcs.h
user/parlib/include/vcore.h
user/parlib/mcs.c
user/parlib/uthread.c
user/parlib/vcore.c
user/pthread/pthread.c

index 7d40af0..9016302 100644 (file)
@@ -46,6 +46,9 @@ void mcs_lock_init(struct mcs_lock *lock);
  * in a thread control block, etc. */
 void mcs_lock_lock(struct mcs_lock *lock, struct mcs_lock_qnode *qnode);
 void mcs_lock_unlock(struct mcs_lock *lock, struct mcs_lock_qnode *qnode);
+/* If you lock the lock from vcore context, you must use these. */
+void mcs_lock_notifsafe(struct mcs_lock *lock, struct mcs_lock_qnode *qnode);
+void mcs_unlock_notifsafe(struct mcs_lock *lock, struct mcs_lock_qnode *qnode);
 
 #ifdef __cplusplus
 }
index 0efa970..12e8f73 100644 (file)
@@ -41,12 +41,13 @@ static inline size_t max_vcores(void);
 static inline size_t num_vcores(void);
 static inline int vcore_id(void);
 static inline bool in_vcore_context(void);
-static inline void enable_notifs(uint32_t vcoreid);
+static inline void __enable_notifs(uint32_t vcoreid);
 static inline void disable_notifs(uint32_t vcoreid);
 int vcore_init(void);
 int vcore_request(size_t k);
 void vcore_yield(void);
 void clear_notif_pending(uint32_t vcoreid);
+void enable_notifs(uint32_t vcoreid);
 
 /* Static inlines */
 static inline size_t max_vcores(void)
@@ -69,7 +70,8 @@ static inline bool in_vcore_context(void)
        return __vcore_context;
 }
 
-static inline void enable_notifs(uint32_t vcoreid)
+/* Only call this if you know what you are doing. */
+static inline void __enable_notifs(uint32_t vcoreid)
 {
        __procdata.vcore_preempt_data[vcoreid].notif_enabled = TRUE;
 }
index 3fb73d7..f74262c 100644 (file)
@@ -48,6 +48,24 @@ void mcs_lock_unlock(struct mcs_lock *lock, struct mcs_lock_qnode *qnode)
                qnode->next->locked = 0;
 }
 
+/* We don't bother saving the state, like we do with irqsave, since we can use
+ * whether or not we are in vcore context to determine that.  This means you
+ * shouldn't call this from those moments when you fake being in vcore context
+ * (when switching into the TLS, etc). */
+void mcs_lock_notifsafe(struct mcs_lock *lock, struct mcs_lock_qnode *qnode)
+{
+       if (!in_vcore_context())
+               disable_notifs(vcore_id());
+       mcs_lock_lock(lock, qnode);
+}
+
+void mcs_unlock_notifsafe(struct mcs_lock *lock, struct mcs_lock_qnode *qnode)
+{
+       mcs_lock_unlock(lock, qnode);
+       if (!in_vcore_context())
+               enable_notifs(vcore_id());
+}
+
 // MCS dissemination barrier!
 int mcs_barrier_init(mcs_barrier_t* b, size_t np)
 {
index a2b474f..1c9a5ec 100644 (file)
@@ -40,7 +40,7 @@ static int uthread_init(void)
        assert(!in_vcore_context());
        /* don't forget to enable notifs on vcore0.  if you don't, the kernel will
         * restart your _S with notifs disabled, which is a path to confusion. */
-       enable_notifs(0);
+       __enable_notifs(0);
        /* Get ourselves into _M mode */
        while (num_vcores() < 1) {
                vcore_request(1);
index 07de9c1..906c891 100644 (file)
@@ -141,7 +141,7 @@ int vcore_request(size_t k)
 
        // TODO: could do this function without a lock once we 
        // have atomic fetch and add in user space
-       mcs_lock_lock(&_vcore_lock, &local_qn);
+       mcs_lock_notifsafe(&_vcore_lock, &local_qn);
 
        size_t vcores_wanted = num_vcores() + k;
        if(k < 0 || vcores_wanted > max_vcores())
@@ -156,10 +156,13 @@ int vcore_request(size_t k)
                        goto fail; // errno set by the call that failed
                _max_vcores_ever_wanted++;
        }
+       /* Ugly hack, but we need to be able to transition to _M mode */
+       if (num_vcores() == 0)
+               __enable_notifs(vcore_id());
        ret = sys_resource_req(RES_CORES, vcores_wanted, 1, 0);
 
 fail:
-       mcs_lock_unlock(&_vcore_lock, &local_qn);
+       mcs_unlock_notifsafe(&_vcore_lock, &local_qn);
        return ret;
 }
 
@@ -182,3 +185,12 @@ void clear_notif_pending(uint32_t vcoreid)
                __procdata.vcore_preempt_data[vcoreid].notif_pending = 0;
        } while (handle_events(vcoreid));
 }
+
+/* Enables notifs, and deals with missed notifs by self notifying.  This should
+ * be rare, so the syscall overhead isn't a big deal. */
+void enable_notifs(uint32_t vcoreid)
+{
+       __enable_notifs(vcoreid);
+       if (__procdata.vcore_preempt_data[vcoreid].notif_pending)
+               sys_self_notify(vcoreid, EV_NONE, 0);
+}
index 0b782fd..1d31349 100644 (file)
@@ -76,10 +76,10 @@ struct uthread *pth_init(void)
        assert(t->id == 0);
 
        /* Put the new pthread on the active queue */
-       mcs_lock_lock(&queue_lock, &local_qn);
+       mcs_lock_notifsafe(&queue_lock, &local_qn);
        threads_active++;
        TAILQ_INSERT_TAIL(&active_queue, t, next);
-       mcs_lock_unlock(&queue_lock, &local_qn);
+       mcs_unlock_notifsafe(&queue_lock, &local_qn);
        return (struct uthread*)t;
 }
 
@@ -95,7 +95,7 @@ void __attribute__((noreturn)) pth_sched_entry(void)
        /* no one currently running, so lets get someone from the ready queue */
        struct pthread_tcb *new_thread = NULL;
        struct mcs_lock_qnode local_qn = {0};
-       mcs_lock_lock(&queue_lock, &local_qn);
+       mcs_lock_notifsafe(&queue_lock, &local_qn);
        new_thread = TAILQ_FIRST(&ready_queue);
        if (new_thread) {
                TAILQ_REMOVE(&ready_queue, new_thread, next);
@@ -103,7 +103,7 @@ void __attribute__((noreturn)) pth_sched_entry(void)
                threads_active++;
                threads_ready--;
        }
-       mcs_lock_unlock(&queue_lock, &local_qn);
+       mcs_unlock_notifsafe(&queue_lock, &local_qn);
        /* Instead of yielding, you could spin, turn off the core, set an alarm,
         * whatever.  You want some logic to decide this.  Uthread code wil have
         * helpers for this (like how we provide run_uthread()) */
@@ -157,10 +157,10 @@ void pth_thread_runnable(struct uthread *uthread)
        struct mcs_lock_qnode local_qn = {0};
        /* Insert the newly created thread into the ready queue of threads.
         * It will be removed from this queue later when vcore_entry() comes up */
-       mcs_lock_lock(&queue_lock, &local_qn);
+       mcs_lock_notifsafe(&queue_lock, &local_qn);
        TAILQ_INSERT_TAIL(&ready_queue, pthread, next);
        threads_ready++;
-       mcs_lock_unlock(&queue_lock, &local_qn);
+       mcs_unlock_notifsafe(&queue_lock, &local_qn);
 }
 
 /* The calling thread is yielding.  Do what you need to do to restart (like put
@@ -173,12 +173,12 @@ void pth_thread_yield(struct uthread *uthread)
        /* Take from the active list, and put on the ready list (tail).  Don't do
         * this until we are done completely with the thread, since it can be
         * restarted somewhere else. */
-       mcs_lock_lock(&queue_lock, &local_qn);
+       mcs_lock_notifsafe(&queue_lock, &local_qn);
        threads_active--;
        TAILQ_REMOVE(&active_queue, pthread, next);
        threads_ready++;
        TAILQ_INSERT_TAIL(&ready_queue, pthread, next);
-       mcs_lock_unlock(&queue_lock, &local_qn);
+       mcs_unlock_notifsafe(&queue_lock, &local_qn);
 }
 
 /* Thread is exiting, do your 2LS specific stuff.  You're in vcore context.
@@ -188,10 +188,10 @@ void pth_thread_exit(struct uthread *uthread)
        struct pthread_tcb *pthread = (struct pthread_tcb*)uthread;
        struct mcs_lock_qnode local_qn = {0};
        /* Remove from the active runqueue */
-       mcs_lock_lock(&queue_lock, &local_qn);
+       mcs_lock_notifsafe(&queue_lock, &local_qn);
        threads_active--;
        TAILQ_REMOVE(&active_queue, pthread, next);
-       mcs_lock_unlock(&queue_lock, &local_qn);
+       mcs_unlock_notifsafe(&queue_lock, &local_qn);
        /* Cleanup, mirroring pth_thread_create() */
        __pthread_free_stack(pthread);
        /* TODO: race on detach state */