MCS locks properly disable thread migration
authorBarret Rhoden <brho@cs.berkeley.edu>
Mon, 12 Sep 2011 21:52:17 +0000 (14:52 -0700)
committerKevin Klues <klueska@cs.berkeley.edu>
Thu, 3 Nov 2011 00:36:06 +0000 (17:36 -0700)
Any time you disable notifs, you need to disable uthread migration too.
The reason for this is that we can't atomically disable interrupts on a
vcore, so we need to make sure we're not moved around in the instant
after we read vcoreid and before we actually disable notifs.  x86's
'cli' doesn't have this issue.  =)

I put an assert in disable_notifs() to catch any future callers that
don't do this.

Also note that disabling/enabling notifs isn't the only reason to
disable migration; we want to disable migration for code that requires
staying on a vcore.  We don't necessarily want to turn it off an on at
exactly the same call sites as enable/disable notifs.  Check
uthread_yield() for an example (note it doesn't even enable_notifs()
directly).

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

index 1c481f8..4d460f7 100644 (file)
@@ -43,13 +43,14 @@ static inline int vcore_id(void);
 static inline bool in_vcore_context(void);
 static inline bool in_multi_mode(void);
 static inline void __enable_notifs(uint32_t vcoreid);
-static inline void disable_notifs(uint32_t vcoreid);
+static inline void __disable_notifs(uint32_t vcoreid);
 static inline bool notif_is_enabled(uint32_t vcoreid);
 int vcore_init(void);
 int vcore_request(size_t k);
 void vcore_yield(bool preempt_pending);
 void clear_notif_pending(uint32_t vcoreid);
 void enable_notifs(uint32_t vcoreid);
+void disable_notifs(uint32_t vcoreid);
 void vcore_idle(void);
 
 /* Static inlines */
@@ -84,7 +85,7 @@ static inline void __enable_notifs(uint32_t vcoreid)
        __procdata.vcore_preempt_data[vcoreid].notif_enabled = TRUE;
 }
 
-static inline void disable_notifs(uint32_t vcoreid)
+static inline void __disable_notifs(uint32_t vcoreid)
 {
        __procdata.vcore_preempt_data[vcoreid].notif_enabled = FALSE;
 }
index dcfbd80..a8fbbe7 100644 (file)
@@ -3,6 +3,7 @@
 #include <arch/atomic.h>
 #include <string.h>
 #include <stdlib.h>
+#include <uthread.h>
 
 // MCS locks
 void mcs_lock_init(struct mcs_lock *lock)
@@ -54,16 +55,30 @@ void mcs_lock_unlock(struct mcs_lock *lock, struct mcs_lock_qnode *qnode)
  * (when switching into the TLS, etc). */
 void mcs_lock_notifsafe(struct mcs_lock *lock, struct mcs_lock_qnode *qnode)
 {
-       if (!in_vcore_context())
+       if (!in_vcore_context()) {
+               if (current_uthread)
+                       current_uthread->flags |= UTHREAD_DONT_MIGRATE;
+               wmb();
                disable_notifs(vcore_id());
+               wmb();
+               if (current_uthread)
+                       current_uthread->flags &= ~UTHREAD_DONT_MIGRATE;
+       }
        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() && num_vcores() > 0)
+       if (!in_vcore_context() && in_multi_mode()) {
+               if (current_uthread)
+                       current_uthread->flags |= UTHREAD_DONT_MIGRATE;
+               wmb();
                enable_notifs(vcore_id());
+               wmb();
+               if (current_uthread)
+                       current_uthread->flags &= ~UTHREAD_DONT_MIGRATE;
+       }
 }
 
 // MCS dissemination barrier!
index b11f53e..434774b 100644 (file)
@@ -237,6 +237,15 @@ void enable_notifs(uint32_t vcoreid)
                sys_self_notify(vcoreid, EV_NONE, 0);
 }
 
+/* Helper to disable notifs.  It simply checks to make sure we disabled uthread
+ * migration, which is a common mistake. */
+void disable_notifs(uint32_t vcoreid)
+{
+       if (!in_vcore_context() && current_uthread)
+               assert(current_uthread->flags & UTHREAD_DONT_MIGRATE);
+       __disable_notifs(vcoreid);
+}
+
 /* Like smp_idle(), this will put the core in a state that it can only be woken
  * up by an IPI.  In the future, we may halt or something. */
 void __attribute__((noreturn)) vcore_idle(void)