From: Marco Schlumpp Date: Mon, 20 Feb 2023 13:57:00 +0000 (+0100) Subject: plat/virtio: Implement support for event index notification suppression X-Git-Url: http://xenbits.xensource.com/gitweb?a=commitdiff_plain;h=973e8a7e13aca29cb0ea2f630330b27a44dd4e31;p=unikraft%2Funikraft.git plat/virtio: Implement support for event index notification suppression This allows the drivers/device to specify the other side should send a notification. This is required for firecracker which does not support the original notification suppression flag. Signed-off-by: Marco Schlumpp --- diff --git a/plat/drivers/include/virtio/virtio_ring.h b/plat/drivers/include/virtio/virtio_ring.h index 9f65594a7..40fa38db7 100644 --- a/plat/drivers/include/virtio/virtio_ring.h +++ b/plat/drivers/include/virtio/virtio_ring.h @@ -157,7 +157,7 @@ struct vring { * versa. They are at the end for backwards compatibility. */ #define vring_used_event(vr) ((vr)->avail->ring[(vr)->num]) -#define vring_avail_event(vr) (*(__virtio16 *)&(vr)->used->ring[(vr)->num]) +#define vring_avail_event(vr) (*(__virtio_le16 *)&(vr)->used->ring[(vr)->num]) static inline void vring_init(struct vring *vr, unsigned int num, uint8_t *p, unsigned long align) @@ -167,7 +167,8 @@ static inline void vring_init(struct vring *vr, unsigned int num, uint8_t *p, vr->avail = (struct vring_avail *) (p + num * sizeof(struct vring_desc)); vr->used = (void *) - (((unsigned long) &vr->avail->ring[num] + align - 1) & ~(align - 1)); + (((unsigned long) &vr->avail->ring[num] + sizeof(uint16_t) + + align - 1) & ~(align - 1)); } static inline unsigned int vring_size(unsigned int num, unsigned long align) @@ -186,7 +187,8 @@ static inline unsigned int vring_size(unsigned int num, unsigned long align) static inline int vring_need_event(__u16 event_idx, __u16 new_idx, __u16 old_idx) { - return (new_idx - event_idx - 1) < (new_idx - old_idx); + return (uint16_t)(new_idx - event_idx - 1) < + (uint16_t)(new_idx - old_idx); } #ifdef __cplusplus diff --git a/plat/drivers/include/virtio/virtqueue.h b/plat/drivers/include/virtio/virtqueue.h index 1daf659f6..4220d8b4b 100644 --- a/plat/drivers/include/virtio/virtqueue.h +++ b/plat/drivers/include/virtio/virtqueue.h @@ -66,6 +66,8 @@ struct virtqueue { virtqueue_callback_t vq_callback; /* Next entry of the queue */ UK_TAILQ_ENTRY(struct virtqueue) next; + /* EVENT_IDX notification suppression is used */ + __u8 uses_event_idx; /* Private data structure used by the driver of the queue */ void *priv; }; diff --git a/plat/drivers/virtio/virtio_net.c b/plat/drivers/virtio/virtio_net.c index ab93f79a1..3bc3f1c30 100644 --- a/plat/drivers/virtio/virtio_net.c +++ b/plat/drivers/virtio/virtio_net.c @@ -928,6 +928,16 @@ static int virtio_netdev_feature_negotiate(struct uk_netdev *n) VIRTIO_FEATURE_SET(drv_features, VIRTIO_NET_F_GUEST_CSUM); } + /** + * Use index based event supression when it's available. + * This allows a more fine-grained control when the hypervisor should + * notify the guest. Some hypervisors such as firecracker also do not + * support the original flag. + */ + if (VIRTIO_FEATURE_HAS(host_features, VIRTIO_F_EVENT_IDX)) { + VIRTIO_FEATURE_SET(drv_features, VIRTIO_F_EVENT_IDX); + } + /** * TCP Segmentation Offload * NOTE: This enables sending and receiving of packets marked with diff --git a/plat/drivers/virtio/virtio_ring.c b/plat/drivers/virtio/virtio_ring.c index d0883b216..5f748b542 100644 --- a/plat/drivers/virtio/virtio_ring.c +++ b/plat/drivers/virtio/virtio_ring.c @@ -43,6 +43,7 @@ #include #include #include +#include #ifdef CONFIG_LIBUKVMEM #include #include @@ -101,6 +102,12 @@ void virtqueue_intr_disable(struct virtqueue *vq) UK_ASSERT(vq); vrq = to_virtqueue_vring(vq); + + if (vq->uses_event_idx) { + vring_used_event(&vrq->vring) = + vrq->last_used_desc_idx - vrq->vring.num - 1; + return; + } vrq->vring.avail->flags |= (VRING_AVAIL_F_NO_INTERRUPT); } @@ -114,24 +121,30 @@ int virtqueue_intr_enable(struct virtqueue *vq) vrq = to_virtqueue_vring(vq); /* Check if there are no more packets enabled */ if (!virtqueue_hasdata(vq)) { - if (vrq->vring.avail->flags & VRING_AVAIL_F_NO_INTERRUPT) { + if (vq->uses_event_idx) { + /* TODO: Add a parameter to delay interrupts. (For + * example for TXQ interrupts) + */ + vring_used_event(&vrq->vring) = vrq->last_used_desc_idx + 0; + } else { vrq->vring.avail->flags &= (~VRING_AVAIL_F_NO_INTERRUPT); - /** - * We enabled the interrupts. We ensure it using the - * memory barrier and check if there are any further - * data available in the queue. The check for data - * after enabling the interrupt is to make sure we do - * not miss any interrupt while transitioning to enable - * interrupt. This is inline with the requirement from - * virtio specification section 3.2.2 - */ - mb(); - /* Check if there are further descriptors */ - if (virtqueue_hasdata(vq)) { - virtqueue_intr_disable(vq); - rc = 1; - } + } + + /** + * We enabled the interrupts. We ensure it using the + * memory barrier and check if there are any further + * data available in the queue. The check for data + * after enabling the interrupt is to make sure we do + * not miss any interrupt while transitioning to enable + * interrupt. This is inline with the requirement from + * virtio specification section 3.2.2 + */ + mb(); + /* Check if there are further descriptors */ + if (virtqueue_hasdata(vq)) { + virtqueue_intr_disable(vq); + rc = 1; } } else { /** @@ -186,9 +199,16 @@ static inline void virtqueue_detach_desc(struct virtqueue_vring *vrq, int virtqueue_notify_enabled(struct virtqueue *vq) { struct virtqueue_vring *vrq; + uint16_t old, new; UK_ASSERT(vq); vrq = to_virtqueue_vring(vq); + if (vq->uses_event_idx) { + new = vrq->vring.avail->idx; + old = new - 1; /* FIXME: Use actual written count */ + + return vring_need_event(vring_avail_event(&vrq->vring), new, old); + } return ((vrq->vring.used->flags & VRING_USED_F_NO_NOTIFY) == 0); } @@ -233,10 +253,9 @@ __u64 virtqueue_feature_negotiate(__u64 feature_set) { __u64 feature = (1ULL << VIRTIO_TRANSPORT_F_START) - 1; - /** - * Currently out vring driver does not support any ring feature. We will - * add support to transport feature in the future. - */ + /* Allow event index feature */ + feature |= 1ULL << VIRTIO_F_EVENT_IDX; + feature &= feature_set; return feature; } @@ -450,6 +469,7 @@ struct virtqueue *virtqueue_create(__u16 queue_id, __u16 nr_descs, __u16 align, vq->vdev = vdev; vq->vq_callback = callback; vq->vq_notify_host = notify; + vq->uses_event_idx = VIRTIO_FEATURE_HAS(vdev->features, VIRTIO_F_EVENT_IDX); return vq; err_freevq: