summaryrefslogtreecommitdiffstats
path: root/sys/kern
diff options
context:
space:
mode:
authorkib <kib@FreeBSD.org>2015-02-25 16:12:56 +0000
committerkib <kib@FreeBSD.org>2015-02-25 16:12:56 +0000
commit2c8adee420d1fc852b5b84bc6acd1d0bf205b83f (patch)
treea71279b2126114fbf419f6ad617499beee5d06d8 /sys/kern
parent298cb69513578e15a5a8a0fd5dced4df2188db65 (diff)
downloadFreeBSD-src-2c8adee420d1fc852b5b84bc6acd1d0bf205b83f.zip
FreeBSD-src-2c8adee420d1fc852b5b84bc6acd1d0bf205b83f.tar.gz
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 <eric_van_gyzen@dell.com> with advice from: Elliott Rabe and Jim Muchow, also at Dell Inc. Obtained from: Dell Inc. PR: 198914
Diffstat (limited to 'sys/kern')
-rw-r--r--sys/kern/kern_umtx.c35
1 files changed, 33 insertions, 2 deletions
diff --git a/sys/kern/kern_umtx.c b/sys/kern/kern_umtx.c
index 317e05b..75aa1d8 100644
--- a/sys/kern/kern_umtx.c
+++ b/sys/kern/kern_umtx.c
@@ -1445,6 +1445,19 @@ umtx_pi_setowner(struct umtx_pi *pi, struct thread *owner)
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 umutex *m, uint32_t flags)
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 umutex *m, uint32_t flags)
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);
OpenPOWER on IntegriCloud