From 4c1dd505a6a68172c019bf20ae797f4fbf6b05c2 Mon Sep 17 00:00:00 2001 From: Nadav Har'El <nyh@cloudius-systems.com> Date: Wed, 10 Jul 2013 22:04:44 +0300 Subject: [PATCH] rewrite virtio_driver::wait_for_queue In my memcached tests (with mc_benchmark as the driver), I saw virtio_driver::wait_for_queue appears to have some bug or race condition - in some cases it hangs on waiting for the rx queue - and simply never returns. I can't say I understand what the bug in this code is, however. Instead, I just wrote it from scratch in a different way, which I think is much clearer - and this code no longer exhibits this bug. I can't put my finger on why my new version is more correct than the old one - or even just difference... Dor, maybe you can find a difference? But it definitely behaves differently. --- drivers/virtio.cc | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/drivers/virtio.cc b/drivers/virtio.cc index ad6178f6c..5741fb20e 100644 --- a/drivers/virtio.cc +++ b/drivers/virtio.cc @@ -192,24 +192,23 @@ vring* virtio_driver::get_virt_queue(unsigned idx) void virtio_driver::wait_for_queue(vring* queue, bool (vring::*pred)() const) { - sched::thread::wait_until([queue,pred] { - bool have_elements = (queue->*pred)(); - if (!have_elements) { - queue->enable_interrupts(); - - // we must check that the ring is not empty *after* - // we enable interrupts to avoid a race where a packet - // may have been delivered between queue->used_ring_not_empty() - // and queue->enable_interrupts() above - have_elements = (queue->*pred)(); - if (have_elements) { - queue->disable_interrupts(); - } - } - - trace_virtio_wait_for_queue(queue, have_elements); - return have_elements; + if ((queue->*pred)()) { + return; + } + queue->enable_interrupts(); + // It is possible that after checking pred() and before enabling + // interrupts, the delivered us a packet, for which we will not get an + // interrupt. So we much check again. + if ((queue->*pred)()) { + queue->disable_interrupts(); + return; + } + sched::thread::wait_until([&] { + // Need to re-enable interrupts as the interrupt disabled them. + queue->enable_interrupts(); + return (queue->*pred)(); }); + queue->disable_interrupts(); } u32 virtio_driver::get_device_features(void) -- GitLab