qemu with hax to log dma reads & writes jcs.org/2018/11/12/vfio

virtio: notify virtqueue via host notifier when available

Host notifiers are used in several cases:
1. Traditional ioeventfd where virtqueue notifications are handled in
the main loop thread.
2. IOThreads (aio_handle_output) where virtqueue notifications are
handled in an IOThread AioContext.
3. vhost where virtqueue notifications are handled by kernel vhost or
a vhost-user device backend.

Most virtqueue notifications from the guest use the ioeventfd mechanism,
but there are corner cases where QEMU code calls virtio_queue_notify().
This currently honors the host notifier for the IOThreads
aio_handle_output case, but not for the vhost case. The result is that
vhost does not receive virtqueue notifications from QEMU when
virtio_queue_notify() is called.

This patch extends virtio_queue_notify() to set the host notifier
whenever it is enabled instead of calling the vq->(aio_)handle_output()
function directly. We track the host notifier state for each virtqueue
separately since some devices may use it only for certain virtqueues.

This fixes the vhost case although it does add a trip through the
eventfd for the traditional ioeventfd case. I don't think it's worth
adding a fast path for the traditional ioeventfd case because calling
virtio_queue_notify() is rare when ioeventfd is enabled.

Reported-by: Felipe Franciosi <felipe@nutanix.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Message-Id: <20191105140946.165584-1-stefanha@redhat.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>

authored by

Stefan Hajnoczi and committed by
Michael S. Tsirkin
fcccb271 977aff10

+13 -1
+4
hw/virtio/virtio-bus.c
··· 288 288 k->ioeventfd_assign(proxy, notifier, n, false); 289 289 } 290 290 291 + if (r == 0) { 292 + virtio_queue_set_host_notifier_enabled(vq, assign); 293 + } 294 + 291 295 return r; 292 296 } 293 297
+8 -1
hw/virtio/virtio.c
··· 128 128 VirtIODevice *vdev; 129 129 EventNotifier guest_notifier; 130 130 EventNotifier host_notifier; 131 + bool host_notifier_enabled; 131 132 QLIST_ENTRY(VirtQueue) node; 132 133 }; 133 134 ··· 2271 2272 } 2272 2273 2273 2274 trace_virtio_queue_notify(vdev, vq - vdev->vq, vq); 2274 - if (vq->handle_aio_output) { 2275 + if (vq->host_notifier_enabled) { 2275 2276 event_notifier_set(&vq->host_notifier); 2276 2277 } else if (vq->handle_output) { 2277 2278 vq->handle_output(vdev, vq); ··· 3145 3146 vdev->vq[i].vector = VIRTIO_NO_VECTOR; 3146 3147 vdev->vq[i].vdev = vdev; 3147 3148 vdev->vq[i].queue_index = i; 3149 + vdev->vq[i].host_notifier_enabled = false; 3148 3150 } 3149 3151 3150 3152 vdev->name = name; ··· 3434 3436 EventNotifier *virtio_queue_get_host_notifier(VirtQueue *vq) 3435 3437 { 3436 3438 return &vq->host_notifier; 3439 + } 3440 + 3441 + void virtio_queue_set_host_notifier_enabled(VirtQueue *vq, bool enabled) 3442 + { 3443 + vq->host_notifier_enabled = enabled; 3437 3444 } 3438 3445 3439 3446 int virtio_queue_set_host_notifier_mr(VirtIODevice *vdev, int n,
+1
include/hw/virtio/virtio.h
··· 312 312 void virtio_device_release_ioeventfd(VirtIODevice *vdev); 313 313 bool virtio_device_ioeventfd_enabled(VirtIODevice *vdev); 314 314 EventNotifier *virtio_queue_get_host_notifier(VirtQueue *vq); 315 + void virtio_queue_set_host_notifier_enabled(VirtQueue *vq, bool enabled); 315 316 void virtio_queue_host_notifier_read(EventNotifier *n); 316 317 void virtio_queue_aio_set_host_notifier_handler(VirtQueue *vq, AioContext *ctx, 317 318 VirtIOHandleAIOOutput handle_output);