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

@@ -536,18 +536,20 @@ static void *
virtio_net_tx_thread(void *param)
{
struct virtio_net *net = param;
struct virtio_vq_info *vq;
struct virtio_vq_info *vq = &net->queues[VIRTIO_NET_TXQ];
int error;
vq = &net->queues[VIRTIO_NET_TXQ];
/*
* Let us wait till the tx queue pointers get initialised &
* first tx signaled
*/
pthread_mutex_lock(&net->tx_mtx);
error = pthread_cond_wait(&net->tx_cond, &net->tx_mtx);
assert(error == 0);
while (!net->closing && !vq_ring_ready(vq)) {
error = pthread_cond_wait(&net->tx_cond, &net->tx_mtx);
assert(error == 0);
}
if (net->closing) {
WPRINTF(("vtnet tx thread closing...\n"));
pthread_mutex_unlock(&net->tx_mtx);
@@ -556,6 +558,13 @@ virtio_net_tx_thread(void *param)
for (;;) {
/* note - tx mutex is locked here */
net->tx_in_progress = 0;
/*
* Checking the avail ring here serves two purposes:
* - avoid vring processing due to spurious wakeups
* - catch missing notifications before acquiring tx_mtx
*/
while (net->resetting || !vq_has_descs(vq)) {
vq_clear_used_ring_flags(&net->base, vq);
/* memory barrier */
@@ -563,7 +572,6 @@ virtio_net_tx_thread(void *param)
if (!net->resetting && vq_has_descs(vq))
break;
net->tx_in_progress = 0;
error = pthread_cond_wait(&net->tx_cond, &net->tx_mtx);
assert(error == 0);
if (net->closing) {
@@ -572,6 +580,7 @@ virtio_net_tx_thread(void *param)
return NULL;
}
}
vq->used->flags |= VRING_USED_F_NO_NOTIFY;
net->tx_in_progress = 1;
pthread_mutex_unlock(&net->tx_mtx);