Author: kib
Date: Wed Feb 25 16:12:56 2015
New Revision: 279282
URL: https://svnweb.freebsd.org/changeset/base/279282

Log:
  When unlocking a contested PI pthread mutex, if the queue of waiters
  is empty, look up the umtx_pi and disown it if the current thread owns it.
  This can happen if a signal or timeout removed the last waiter from
  the queue, but there is still a thread in do_lock_pi() holding a reference
  on the umtx_pi.  The unlocking thread might not own the umtx_pi in this case,
  but if it does, it must disown it to keep the ownership consistent between
  the umtx_pi and the umutex.
  
  Submitted by: Eric van Gyzen <[email protected]>
        with advice from: Elliott Rabe and Jim Muchow, also at Dell Inc.
  Obtained from:        Dell Inc.
  PR:   198914

Modified:
  head/sys/kern/kern_umtx.c

Modified: head/sys/kern/kern_umtx.c
==============================================================================
--- head/sys/kern/kern_umtx.c   Wed Feb 25 13:58:43 2015        (r279281)
+++ head/sys/kern/kern_umtx.c   Wed Feb 25 16:12:56 2015        (r279282)
@@ -1445,6 +1445,19 @@ umtx_pi_setowner(struct umtx_pi *pi, str
        TAILQ_INSERT_TAIL(&uq_owner->uq_pi_contested, pi, pi_link);
 }
 
+
+/*
+ * Disown a PI mutex, and remove it from the owned list.
+ */
+static void
+umtx_pi_disown(struct umtx_pi *pi)
+{
+
+       mtx_assert(&umtx_lock, MA_OWNED);
+       TAILQ_REMOVE(&pi->pi_owner->td_umtxq->uq_pi_contested, pi, pi_link);
+       pi->pi_owner = NULL;
+}
+
 /*
  * Claim ownership of a PI mutex.
  */
@@ -1861,8 +1874,7 @@ do_unlock_pi(struct thread *td, struct u
                        return (EPERM);
                }
                uq_me = curthread->td_umtxq;
-               pi->pi_owner = NULL;
-               TAILQ_REMOVE(&uq_me->uq_pi_contested, pi, pi_link);
+               umtx_pi_disown(pi);
                /* get highest priority thread which is still sleeping. */
                uq_first = TAILQ_FIRST(&pi->pi_blocked);
                while (uq_first != NULL && 
@@ -1883,6 +1895,25 @@ do_unlock_pi(struct thread *td, struct u
                mtx_unlock_spin(&umtx_lock);
                if (uq_first)
                        umtxq_signal_thread(uq_first);
+       } else {
+               pi = umtx_pi_lookup(&key);
+               /*
+                * A umtx_pi can exist if a signal or timeout removed the
+                * last waiter from the umtxq, but there is still
+                * a thread in do_lock_pi() holding the umtx_pi.
+                */
+               if (pi != NULL) {
+                       /*
+                        * The umtx_pi can be unowned, such as when a thread
+                        * has just entered do_lock_pi(), allocated the
+                        * umtx_pi, and unlocked the umtxq.
+                        * If the current thread owns it, it must disown it.
+                        */
+                       mtx_lock_spin(&umtx_lock);
+                       if (pi->pi_owner == td)
+                               umtx_pi_disown(pi);
+                       mtx_unlock_spin(&umtx_lock);
+               }
        }
        umtxq_unlock(&key);
 
_______________________________________________
[email protected] mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "[email protected]"

Reply via email to