diff options
author | kib <kib@FreeBSD.org> | 2015-02-25 16:12:56 +0000 |
---|---|---|
committer | kib <kib@FreeBSD.org> | 2015-02-25 16:12:56 +0000 |
commit | 2c8adee420d1fc852b5b84bc6acd1d0bf205b83f (patch) | |
tree | a71279b2126114fbf419f6ad617499beee5d06d8 /sys/kern | |
parent | 298cb69513578e15a5a8a0fd5dced4df2188db65 (diff) | |
download | FreeBSD-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.c | 35 |
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); |