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

virtio: don't enable notifications during polling

Virtqueue notifications are not necessary during polling, so we disable
them. This allows the guest driver to avoid MMIO vmexits.
Unfortunately the virtio-blk and virtio-scsi handler functions re-enable
notifications, defeating this optimization.

Fix virtio-blk and virtio-scsi emulation so they leave notifications
disabled. The key thing to remember for correctness is that polling
always checks one last time after ending its loop, therefore it's safe
to lose the race when re-enabling notifications at the end of polling.

There is a measurable performance improvement of 5-10% with the null-co
block driver. Real-life storage configurations will see a smaller
improvement because the MMIO vmexit overhead contributes less to
latency.

Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Message-Id: <20191209210957.65087-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
d0435bc5 6620801f

+21 -10
+7 -2
hw/block/virtio-blk.c
··· 764 764 { 765 765 VirtIOBlockReq *req; 766 766 MultiReqBuffer mrb = {}; 767 + bool suppress_notifications = virtio_queue_get_notification(vq); 767 768 bool progress = false; 768 769 769 770 aio_context_acquire(blk_get_aio_context(s->blk)); 770 771 blk_io_plug(s->blk); 771 772 772 773 do { 773 - virtio_queue_set_notification(vq, 0); 774 + if (suppress_notifications) { 775 + virtio_queue_set_notification(vq, 0); 776 + } 774 777 775 778 while ((req = virtio_blk_get_request(s, vq))) { 776 779 progress = true; ··· 781 784 } 782 785 } 783 786 784 - virtio_queue_set_notification(vq, 1); 787 + if (suppress_notifications) { 788 + virtio_queue_set_notification(vq, 1); 789 + } 785 790 } while (!virtio_queue_empty(vq)); 786 791 787 792 if (mrb.num_reqs) {
+7 -2
hw/scsi/virtio-scsi.c
··· 597 597 { 598 598 VirtIOSCSIReq *req, *next; 599 599 int ret = 0; 600 + bool suppress_notifications = virtio_queue_get_notification(vq); 600 601 bool progress = false; 601 602 602 603 QTAILQ_HEAD(, VirtIOSCSIReq) reqs = QTAILQ_HEAD_INITIALIZER(reqs); 603 604 604 605 do { 605 - virtio_queue_set_notification(vq, 0); 606 + if (suppress_notifications) { 607 + virtio_queue_set_notification(vq, 0); 608 + } 606 609 607 610 while ((req = virtio_scsi_pop_req(s, vq))) { 608 611 progress = true; ··· 622 625 } 623 626 } 624 627 625 - virtio_queue_set_notification(vq, 1); 628 + if (suppress_notifications) { 629 + virtio_queue_set_notification(vq, 1); 630 + } 626 631 } while (ret != -EINVAL && !virtio_queue_empty(vq)); 627 632 628 633 QTAILQ_FOREACH_SAFE(req, &reqs, next, next) {
+6 -6
hw/virtio/virtio.c
··· 432 432 } 433 433 } 434 434 435 + bool virtio_queue_get_notification(VirtQueue *vq) 436 + { 437 + return vq->notification; 438 + } 439 + 435 440 void virtio_queue_set_notification(VirtQueue *vq, int enable) 436 441 { 437 442 vq->notification = enable; ··· 3410 3415 { 3411 3416 EventNotifier *n = opaque; 3412 3417 VirtQueue *vq = container_of(n, VirtQueue, host_notifier); 3413 - bool progress; 3414 3418 3415 3419 if (!vq->vring.desc || virtio_queue_empty(vq)) { 3416 3420 return false; 3417 3421 } 3418 3422 3419 - progress = virtio_queue_notify_aio_vq(vq); 3420 - 3421 - /* In case the handler function re-enabled notifications */ 3422 - virtio_queue_set_notification(vq, 0); 3423 - return progress; 3423 + return virtio_queue_notify_aio_vq(vq); 3424 3424 } 3425 3425 3426 3426 static void virtio_queue_host_notifier_aio_poll_end(EventNotifier *n)
+1
include/hw/virtio/virtio.h
··· 228 228 229 229 void virtio_notify_config(VirtIODevice *vdev); 230 230 231 + bool virtio_queue_get_notification(VirtQueue *vq); 231 232 void virtio_queue_set_notification(VirtQueue *vq, int enable); 232 233 233 234 int virtio_queue_ready(VirtQueue *vq);