| 1 | /* |
| 2 | * vhost shadow virtqueue |
| 3 | * |
| 4 | * SPDX-FileCopyrightText: Red Hat, Inc. 2021 |
| 5 | * SPDX-FileContributor: Author: Eugenio Pérez <eperezma@redhat.com> |
| 6 | * |
| 7 | * SPDX-License-Identifier: GPL-2.0-or-later |
| 8 | */ |
| 9 | |
| 10 | #ifndef VHOST_SHADOW_VIRTQUEUE_H |
| 11 | #define VHOST_SHADOW_VIRTQUEUE_H |
| 12 | |
| 13 | #include "qemu/event_notifier.h" |
| 14 | #include "hw/virtio/virtio.h" |
| 15 | #include "standard-headers/linux/vhost_types.h" |
| 16 | #include "hw/virtio/vhost-iova-tree.h" |
| 17 | |
| 18 | typedef struct SVQDescState { |
| 19 | VirtQueueElement *elem; |
| 20 | |
| 21 | /* |
| 22 | * Number of descriptors exposed to the device. May or may not match |
| 23 | * guest's |
| 24 | */ |
| 25 | unsigned int ndescs; |
| 26 | |
| 27 | union { |
| 28 | /* |
| 29 | * Total length of the available buffer that is writable by the device. |
| 30 | * Only used in packed vq. |
| 31 | */ |
| 32 | uint32_t in_bytes; |
| 33 | |
| 34 | /* |
| 35 | * Backup next field for each descriptor so we can recover securely, not |
| 36 | * needing to trust the device access. Only used in split vq. |
| 37 | */ |
| 38 | uint16_t next; |
| 39 | }; |
| 40 | } SVQDescState; |
| 41 | |
| 42 | typedef struct VhostShadowVirtqueue VhostShadowVirtqueue; |
| 43 | |
| 44 | /** |
| 45 | * Callback to handle an avail buffer. |
| 46 | * |
| 47 | * @svq: Shadow virtqueue |
| 48 | * @elem: Element placed in the queue by the guest |
| 49 | * @vq_callback_opaque: Opaque |
| 50 | * |
| 51 | * Returns 0 if the vq is running as expected. |
| 52 | * |
| 53 | * Note that ownership of elem is transferred to the callback. |
| 54 | */ |
| 55 | typedef int (*VirtQueueAvailCallback)(VhostShadowVirtqueue *svq, |
| 56 | VirtQueueElement *elem, |
| 57 | void *vq_callback_opaque); |
| 58 | |
| 59 | typedef struct VhostShadowVirtqueueOps { |
| 60 | VirtQueueAvailCallback avail_handler; |
| 61 | } VhostShadowVirtqueueOps; |
| 62 | |
| 63 | /* Shadow virtqueue to relay notifications */ |
| 64 | typedef struct VhostShadowVirtqueue { |
| 65 | /* Shadow vring */ |
| 66 | struct vring vring; |
| 67 | |
| 68 | /* Shadow kick notifier, sent to vhost */ |
| 69 | EventNotifier hdev_kick; |
| 70 | /* Shadow call notifier, sent to vhost */ |
| 71 | EventNotifier hdev_call; |
| 72 | |
| 73 | /* |
| 74 | * Borrowed virtqueue's guest to host notifier. To borrow it in this event |
| 75 | * notifier allows to recover the VhostShadowVirtqueue from the event loop |
| 76 | * easily. If we use the VirtQueue's one, we don't have an easy way to |
| 77 | * retrieve VhostShadowVirtqueue. |
| 78 | * |
| 79 | * So shadow virtqueue must not clean it, or we would lose VirtQueue one. |
| 80 | */ |
| 81 | EventNotifier svq_kick; |
| 82 | |
| 83 | /* Guest's call notifier, where the SVQ calls guest. */ |
| 84 | EventNotifier svq_call; |
| 85 | |
| 86 | /* Virtio queue shadowing */ |
| 87 | VirtQueue *vq; |
| 88 | |
| 89 | /* Virtio device */ |
| 90 | VirtIODevice *vdev; |
| 91 | |
| 92 | /* IOVA mapping */ |
| 93 | VhostIOVATree *iova_tree; |
| 94 | |
| 95 | /* SVQ vring descriptors state */ |
| 96 | SVQDescState *desc_state; |
| 97 | |
| 98 | /* Next VirtQueue element that guest made available */ |
| 99 | VirtQueueElement *next_guest_avail_elem; |
| 100 | |
| 101 | /* Caller callbacks */ |
| 102 | const VhostShadowVirtqueueOps *ops; |
| 103 | |
| 104 | /* Caller callbacks opaque */ |
| 105 | void *ops_opaque; |
| 106 | |
| 107 | /* Next head to expose to the device */ |
| 108 | uint16_t shadow_avail_idx; |
| 109 | |
| 110 | /* |
| 111 | * Next free descriptor. |
| 112 | * |
| 113 | * Without IN_ORDER free_head is used as a linked list head, and |
| 114 | * desc_next[id] is the next element. |
| 115 | * With IN_ORDER free_head is the next available buffer index. |
| 116 | */ |
| 117 | uint16_t free_head; |
| 118 | |
| 119 | /* |
| 120 | * Last used element of the processing batch of used descriptors if |
| 121 | * IN_ORDER. |
| 122 | * If SVQ is not processing a batch of descriptors id is set to UINT_MAX. |
| 123 | */ |
| 124 | vring_used_elem_t batch_last; |
| 125 | |
| 126 | /* Last used id if IN_ORDER and split vq */ |
| 127 | uint16_t last_used; |
| 128 | |
| 129 | /* Last seen used idx */ |
| 130 | uint16_t shadow_used_idx; |
| 131 | |
| 132 | /* Next head to consume from the device */ |
| 133 | uint16_t last_used_idx; |
| 134 | |
| 135 | /* Size of SVQ vring free descriptors */ |
| 136 | uint16_t num_free; |
| 137 | } VhostShadowVirtqueue; |
| 138 | |
| 139 | bool vhost_svq_valid_features(uint64_t features, Error **errp); |
| 140 | |
| 141 | uint16_t vhost_svq_available_slots(const VhostShadowVirtqueue *svq); |
| 142 | void vhost_svq_push_elem(VhostShadowVirtqueue *svq, |
| 143 | const VirtQueueElement *elem, uint32_t len); |
| 144 | int vhost_svq_add(VhostShadowVirtqueue *svq, const struct iovec *out_sg, |
| 145 | size_t out_num, const hwaddr *out_addr, |
| 146 | const struct iovec *in_sg, size_t in_num, |
| 147 | const hwaddr *in_addr, VirtQueueElement *elem); |
| 148 | size_t vhost_svq_poll(VhostShadowVirtqueue *svq, size_t num); |
| 149 | |
| 150 | void vhost_svq_set_svq_kick_fd(VhostShadowVirtqueue *svq, int svq_kick_fd); |
| 151 | void vhost_svq_set_svq_call_fd(VhostShadowVirtqueue *svq, int call_fd); |
| 152 | void vhost_svq_get_vring_addr(const VhostShadowVirtqueue *svq, |
| 153 | struct vhost_vring_addr *addr); |
| 154 | size_t vhost_svq_driver_area_size(const VhostShadowVirtqueue *svq); |
| 155 | size_t vhost_svq_device_area_size(const VhostShadowVirtqueue *svq); |
| 156 | |
| 157 | void vhost_svq_start(VhostShadowVirtqueue *svq, VirtIODevice *vdev, |
| 158 | VirtQueue *vq, VhostIOVATree *iova_tree); |
| 159 | void vhost_svq_stop(VhostShadowVirtqueue *svq); |
| 160 | |
| 161 | VhostShadowVirtqueue *vhost_svq_new(const VhostShadowVirtqueueOps *ops, |
| 162 | void *ops_opaque); |
| 163 | |
| 164 | void vhost_svq_free(gpointer vq); |
| 165 | G_DEFINE_AUTOPTR_CLEANUP_FUNC(VhostShadowVirtqueue, vhost_svq_free); |
| 166 | |
| 167 | #endif |