mirror of
https://github.com/projectacrn/acrn-hypervisor.git
synced 2026-06-08 18:14:53 +00:00
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:
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user