diff --git a/drivers/virtio.cc b/drivers/virtio.cc
index ad6178f6cecee13678b75f0779c531d424d2dfca..5741fb20ebc674da650eceedd0a70a2ab706d999 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)