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

@@ -304,27 +304,38 @@ virtio_rnd_get_entropy(void *param)
struct virtio_vq_info *vq = &rnd->vq;
struct iovec iov;
uint16_t idx;
int len, error;
int ret;
ssize_t len;
for (;;) {
pthread_mutex_lock(&rnd->rx_mtx);
rnd->in_progress = 0;
error = pthread_cond_wait(&rnd->rx_cond, &rnd->rx_mtx);
assert(error == 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(vq)) {
ret = pthread_cond_wait(&rnd->rx_cond, &rnd->rx_mtx);
assert(ret == 0);
}
rnd->in_progress = 1;
pthread_mutex_unlock(&rnd->rx_mtx);
while(vq_has_descs(vq)) {
vq_getchain(vq, &idx, &iov, 1, NULL);
do {
ret = vq_getchain(vq, &idx, &iov, 1, NULL);
assert(ret > 0);
len = read(rnd->fd, iov.iov_base, iov.iov_len);
assert(len > 0);
/* release this chain and handle more */
vq_relchain(vq, idx, len);
}
} while (vq_has_descs(vq));
/* at least one avail ring element has been processed */
vq_endchains(vq, 1);
}
}