dm: protect pthread_cond_wait() against spurious wakeups

Users of pthread_cond_wait() should take care of spurious wakeups and it
is usually used in conjunction with a predicate. Not doing so can result
in unintended behavior. For example:

virtio_net_tx_thread():
  entry -> pthread_cond_wait() -> spurious wakeup ->
  vq_clear_used_ring_flags() -> segfault (vq->used uninitialized)

tpm_crb_request_deliver():
  entry -> pthread_cond_wait() -> spurious wakeup ->
  swtpm_handle_request() called needlessly

virtio_rnd_get_entropy():
  entry -> pthread_cond_wait() -> spurious wakeup ->
  no avail ring processing ->
  virtio_rnd_notify() skips pthread_cond_signal() due to
  rnd->in_progress ->
  vq_endchains() called needlessly ->
  wait in pthread_cond_wait() indefinitely

Fix these uses of pthread_cond_wait() by using predicates.

The only use case without a clear predicate is the tx thread in
virtio-mei, because it works with two-dimensional linked lists.

v1 -> v2:
- fix bugs and comments
- reduce code redundancy

Tracked-On: #2763
Signed-off-by: Peter Fang <peter.fang@intel.com>
Reviewed-by: Shuo A Liu <shuo.a.liu@intel.com>
This commit is contained in:
Peter Fang
2019-03-17 02:36:26 -07:00
committed by wenlingz
parent e9261121b3
commit f412d52546
7 changed files with 118 additions and 72 deletions

View File

@@ -306,28 +306,37 @@ virtio_hdcp_talk_to_daemon(void *param)
for (;;) {
pthread_mutex_lock(&vhdcp->rx_mtx);
vhdcp->in_progress = 0;
ret = pthread_cond_wait(&vhdcp->rx_cond, &vhdcp->rx_mtx);
assert(ret == 0);
/*
* Checking the avail ring here serves two purposes:
* - avoid vring processing due to spurious wakeups
* - catch missing notifications before acquiring rx_mtx
*/
while (!vq_has_descs(rvq)) {
ret = pthread_cond_wait(&vhdcp->rx_cond, &vhdcp->rx_mtx);
assert(ret == 0);
}
vhdcp->in_progress = 1;
pthread_mutex_unlock(&vhdcp->rx_mtx);
while(vq_has_descs(rvq)) {
vq_getchain(rvq, &idx, &iov, 1, NULL);
do {
ret = vq_getchain(rvq, &idx, &iov, 1, NULL);
assert(ret > 0);
msg = (struct SocketData*)(iov.iov_base);
ret = performMessageTransaction(vhdcp->fd, *msg);
if (ret < 0)
{
if (ret < 0) {
close(vhdcp->fd);
vhdcp->fd = -1;
}
/* release this chain and handle more */
vq_relchain(rvq, idx, sizeof(struct SocketData));
}
} while (vq_has_descs(rvq));
/* at least one avail ring element has been processed */
vq_endchains(rvq, 1);
}
}