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
This commit is contained in:
Konstantin Belousov 2015-02-25 16:12:56 +00:00
parent 9c0f6aa762
commit cc876d2c5c
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=279282

View File

@ -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);