Reduces irq_window in guest by only injecting an interrupt if guest has handled all buffers we used so far.
Signed-off-by: Michael S. Tsirkin <m...@redhat.com> --- This is the qemu part of the story. hw/vhost_net.c | 6 ++++++ hw/virtio.c | 15 +++++++++++++++ hw/virtio.h | 4 ++++ 3 files changed, 25 insertions(+), 0 deletions(-) diff --git a/hw/vhost_net.c b/hw/vhost_net.c index 2e292ee..d77c9b2 100644 --- a/hw/vhost_net.c +++ b/hw/vhost_net.c @@ -51,6 +51,9 @@ unsigned vhost_net_get_features(struct vhost_net *net, unsigned features) if (!(net->dev.features & (1 << VIRTIO_RING_F_INDIRECT_DESC))) { features &= ~(1 << VIRTIO_RING_F_INDIRECT_DESC); } + if (!(net->dev.features & (1 << VIRTIO_RING_F_PUBLISH_USED))) { + features &= ~(1 << VIRTIO_RING_F_PUBLISH_USED); + } features &= ~(1 << VIRTIO_NET_F_MRG_RXBUF); return features; } @@ -64,6 +67,9 @@ void vhost_net_ack_features(struct vhost_net *net, unsigned features) if (features & (1 << VIRTIO_RING_F_INDIRECT_DESC)) { net->dev.acked_features |= (1 << VIRTIO_RING_F_INDIRECT_DESC); } + if (features & (1 << VIRTIO_RING_F_PUBLISH_USED)) { + net->dev.acked_features |= (1 << VIRTIO_RING_F_PUBLISH_USED); + } } static int vhost_net_get_fd(VLANClientState *backend) diff --git a/hw/virtio.c b/hw/virtio.c index e7657ae..5d686f0 100644 --- a/hw/virtio.c +++ b/hw/virtio.c @@ -71,6 +71,7 @@ struct VirtQueue target_phys_addr_t pa; uint16_t last_avail_idx; int inuse; + int num_notify; uint16_t vector; void (*handle_output)(VirtIODevice *vdev, VirtQueue *vq); VirtIODevice *vdev; @@ -139,6 +140,11 @@ static inline uint16_t vring_avail_ring(VirtQueue *vq, int i) return lduw_phys(pa); } +static inline uint16_t vring_last_used_idx(VirtQueue *vq) +{ + return vring_avail_ring(vq, vq->vring.num); +} + static inline void vring_used_ring_id(VirtQueue *vq, int i, uint32_t val) { target_phys_addr_t pa; @@ -234,6 +240,7 @@ void virtqueue_flush(VirtQueue *vq, unsigned int count) wmb(); vring_used_idx_increment(vq, count); vq->inuse -= count; + vq->num_notify += count; } void virtqueue_push(VirtQueue *vq, const VirtQueueElement *elem, @@ -603,6 +610,14 @@ void virtio_irq(VirtQueue *vq) void virtio_notify(VirtIODevice *vdev, VirtQueue *vq) { + /* Do not notify if guest did not yet see the last update. */ + if ((vdev->guest_features & (1 << VIRTIO_RING_F_PUBLISH_USED)) && + vring_last_used_idx(vq) != + (uint16_t)(vring_used_idx(vq) + vq->num_notify)) + return; + + vq->num_notify = 0; + /* Always notify when queue is empty (when feature acknowledge) */ if ((vring_avail_flags(vq) & VRING_AVAIL_F_NO_INTERRUPT) && (!(vdev->guest_features & (1 << VIRTIO_F_NOTIFY_ON_EMPTY)) || diff --git a/hw/virtio.h b/hw/virtio.h index f885f1b..4bdfded 100644 --- a/hw/virtio.h +++ b/hw/virtio.h @@ -43,6 +43,8 @@ #define VIRTIO_F_NOTIFY_ON_EMPTY 24 /* We support indirect buffer descriptors */ #define VIRTIO_RING_F_INDIRECT_DESC 28 +/* The Guest publishes last-seen used index at the end of the avail ring. */ +#define VIRTIO_RING_F_PUBLISH_USED 29 /* A guest should never accept this. It implies negotiation is broken. */ #define VIRTIO_F_BAD_FEATURE 30 @@ -189,6 +191,8 @@ VirtIODevice *virtio_balloon_init(DeviceState *dev); void virtio_net_exit(VirtIODevice *vdev); #define DEFINE_VIRTIO_COMMON_FEATURES(_state, _field) \ + DEFINE_PROP_BIT("publish_used", _state, _field, \ + VIRTIO_RING_F_PUBLISH_USED, true), \ DEFINE_PROP_BIT("indirect_desc", _state, _field, \ VIRTIO_RING_F_INDIRECT_DESC, true) -- 1.7.1.12.g42b7f