parlib: Catch illegal block attempts
authorBarret Rhoden <brho@cs.berkeley.edu>
Mon, 24 Apr 2017 15:50:29 +0000 (11:50 -0400)
committerBarret Rhoden <brho@cs.berkeley.edu>
Wed, 3 May 2017 16:13:02 +0000 (12:13 -0400)
Without the assert, we can have some code that shouldn't block grab mutexes
and whatnot.  They could have blocked, but they didn't, and then we only
catch the bug in the constested case - rarer and harder to debug.

Now, we'll immediately panic if we try to do something wrong.

Signed-off-by: Barret Rhoden <brho@cs.berkeley.edu>
user/parlib/include/parlib/uthread.h
user/parlib/mutex.c
user/parlib/uthread.c

index 34aa6e7..a5672d6 100644 (file)
@@ -172,6 +172,7 @@ void uthread_paused(struct uthread *uthread);
 bool __check_preempt_pending(uint32_t vcoreid);        /* careful: check the code */
 void uth_disable_notifs(void);
 void uth_enable_notifs(void);
+void assert_can_block(void);
 
 /* Helpers, which the 2LS can call */
 void __block_uthread_on_async_sysc(struct uthread *uth);
index 314be10..d2a9c8e 100644 (file)
@@ -118,6 +118,7 @@ bool uth_semaphore_timed_down(uth_semaphore_t *sem,
        struct alarm_waiter waiter[1];
        struct timeout_blob blob[1];
 
+       assert_can_block();
        parlib_run_once(&sem->once_ctl, __uth_semaphore_init, sem);
        spin_pdr_lock(&sem->lock);
        if (sem->count > 0) {
@@ -154,6 +155,7 @@ bool uth_semaphore_trydown(uth_semaphore_t *sem)
 {
        bool ret = FALSE;
 
+       assert_can_block();
        parlib_run_once(&sem->once_ctl, __uth_semaphore_init, sem);
        spin_pdr_lock(&sem->lock);
        if (sem->count > 0) {
@@ -285,9 +287,8 @@ void uth_recurse_mutex_free(uth_recurse_mutex_t *r_mtx)
 bool uth_recurse_mutex_timed_lock(uth_recurse_mutex_t *r_mtx,
                                   const struct timespec *abs_timeout)
 {
+       assert_can_block();
        parlib_run_once(&r_mtx->once_ctl, __uth_recurse_mutex_init, r_mtx);
-       assert(!in_vcore_context());
-       assert(current_uthread);
        /* We don't have to worry about races on current_uthread or count.  They are
         * only written by the initial lockholder, and this check will only be true
         * for the initial lockholder, which cannot concurrently call this function
@@ -316,9 +317,8 @@ bool uth_recurse_mutex_trylock(uth_recurse_mutex_t *r_mtx)
 {
        bool ret;
 
+       assert_can_block();
        parlib_run_once(&r_mtx->once_ctl, __uth_recurse_mutex_init, r_mtx);
-       assert(!in_vcore_context());
-       assert(current_uthread);
        if (r_mtx->lockholder == current_uthread) {
                r_mtx->count++;
                return TRUE;
@@ -472,6 +472,7 @@ bool uth_cond_var_timed_wait(uth_cond_var_t *cv, uth_mutex_t *mtx,
        struct timeout_blob blob[1];
        bool ret = TRUE;
 
+       assert_can_block();
        parlib_run_once(&cv->once_ctl, __uth_cond_var_init, cv);
        link.cv = cv;
        link.mtx = mtx;
@@ -613,6 +614,7 @@ static void __rwlock_rd_cb(struct uthread *uth, void *arg)
 
 void uth_rwlock_rdlock(uth_rwlock_t *rwl)
 {
+       assert_can_block();
        parlib_run_once(&rwl->once_ctl, __uth_rwlock_init, rwl);
        spin_pdr_lock(&rwl->lock);
        /* Readers always make progress when there is no writer */
@@ -628,6 +630,7 @@ bool uth_rwlock_try_rdlock(uth_rwlock_t *rwl)
 {
        bool ret = FALSE;
 
+       assert_can_block();
        parlib_run_once(&rwl->once_ctl, __uth_rwlock_init, rwl);
        spin_pdr_lock(&rwl->lock);
        if (!rwl->has_writer) {
@@ -648,6 +651,7 @@ static void __rwlock_wr_cb(struct uthread *uth, void *arg)
 
 void uth_rwlock_wrlock(uth_rwlock_t *rwl)
 {
+       assert_can_block();
        parlib_run_once(&rwl->once_ctl, __uth_rwlock_init, rwl);
        spin_pdr_lock(&rwl->lock);
        /* Writers require total mutual exclusion - no writers or readers */
@@ -663,6 +667,7 @@ bool uth_rwlock_try_wrlock(uth_rwlock_t *rwl)
 {
        bool ret = FALSE;
 
+       assert_can_block();
        parlib_run_once(&rwl->once_ctl, __uth_rwlock_init, rwl);
        spin_pdr_lock(&rwl->lock);
        if (!rwl->has_writer && !rwl->nr_readers) {
index 0bc800b..0a339c4 100644 (file)
@@ -912,6 +912,22 @@ void uth_enable_notifs(void)
        }
 }
 
+void assert_can_block(void)
+{
+       if (in_vcore_context())
+               panic("Vcore context tried to block!");
+       if (!current_uthread) {
+               /* Pre-parlib SCPs can do whatever. */
+               if (atomic_read(&vcpd_of(0)->flags) & VC_SCP_NOVCCTX)
+                       return;
+               panic("No current_uthread and tried to block!");
+       }
+       if (current_uthread->notif_disabled_depth)
+               panic("Uthread tried to block with notifs disabled!");
+       if (current_uthread->flags & UTHREAD_DONT_MIGRATE)
+               panic("Uthread tried to block with DONT_MIGRATE!");
+}
+
 /* Helper: returns TRUE if it succeeded in starting the uth stealing process. */
 static bool start_uth_stealing(struct preempt_data *vcpd)
 {