xref: /qemu/hw/virtio/vhost-shadow-virtqueue.c (revision dafb34c9926a38b3bfc4ab8f35a99ca1c706648c)
110857ec0SEugenio Pérez /*
210857ec0SEugenio Pérez  * vhost shadow virtqueue
310857ec0SEugenio Pérez  *
410857ec0SEugenio Pérez  * SPDX-FileCopyrightText: Red Hat, Inc. 2021
510857ec0SEugenio Pérez  * SPDX-FileContributor: Author: Eugenio Pérez <eperezma@redhat.com>
610857ec0SEugenio Pérez  *
710857ec0SEugenio Pérez  * SPDX-License-Identifier: GPL-2.0-or-later
810857ec0SEugenio Pérez  */
910857ec0SEugenio Pérez 
1010857ec0SEugenio Pérez #include "qemu/osdep.h"
1110857ec0SEugenio Pérez #include "hw/virtio/vhost-shadow-virtqueue.h"
1210857ec0SEugenio Pérez 
1310857ec0SEugenio Pérez #include "qemu/error-report.h"
144725a418SEugenio Pérez #include "qapi/error.h"
15dff4426fSEugenio Pérez #include "qemu/main-loop.h"
16dff4426fSEugenio Pérez #include "linux-headers/linux/vhost.h"
17dff4426fSEugenio Pérez 
18dff4426fSEugenio Pérez /**
194725a418SEugenio Pérez  * Validate the transport device features that both guests can use with the SVQ
204725a418SEugenio Pérez  * and SVQs can use with the device.
214725a418SEugenio Pérez  *
224725a418SEugenio Pérez  * @dev_features: The features
234725a418SEugenio Pérez  * @errp: Error pointer
244725a418SEugenio Pérez  */
254725a418SEugenio Pérez bool vhost_svq_valid_features(uint64_t features, Error **errp)
264725a418SEugenio Pérez {
274725a418SEugenio Pérez     bool ok = true;
284725a418SEugenio Pérez     uint64_t svq_features = features;
294725a418SEugenio Pérez 
304725a418SEugenio Pérez     for (uint64_t b = VIRTIO_TRANSPORT_F_START; b <= VIRTIO_TRANSPORT_F_END;
314725a418SEugenio Pérez          ++b) {
324725a418SEugenio Pérez         switch (b) {
334725a418SEugenio Pérez         case VIRTIO_F_ANY_LAYOUT:
344725a418SEugenio Pérez             continue;
354725a418SEugenio Pérez 
364725a418SEugenio Pérez         case VIRTIO_F_ACCESS_PLATFORM:
374725a418SEugenio Pérez             /* SVQ trust in the host's IOMMU to translate addresses */
384725a418SEugenio Pérez         case VIRTIO_F_VERSION_1:
394725a418SEugenio Pérez             /* SVQ trust that the guest vring is little endian */
404725a418SEugenio Pérez             if (!(svq_features & BIT_ULL(b))) {
414725a418SEugenio Pérez                 svq_features |= BIT_ULL(b);
424725a418SEugenio Pérez                 ok = false;
434725a418SEugenio Pérez             }
444725a418SEugenio Pérez             continue;
454725a418SEugenio Pérez 
464725a418SEugenio Pérez         default:
474725a418SEugenio Pérez             if (svq_features & BIT_ULL(b)) {
484725a418SEugenio Pérez                 svq_features &= ~BIT_ULL(b);
494725a418SEugenio Pérez                 ok = false;
504725a418SEugenio Pérez             }
514725a418SEugenio Pérez         }
524725a418SEugenio Pérez     }
534725a418SEugenio Pérez 
544725a418SEugenio Pérez     if (!ok) {
554725a418SEugenio Pérez         error_setg(errp, "SVQ Invalid device feature flags, offer: 0x%"PRIx64
564725a418SEugenio Pérez                          ", ok: 0x%"PRIx64, features, svq_features);
574725a418SEugenio Pérez     }
584725a418SEugenio Pérez     return ok;
594725a418SEugenio Pérez }
604725a418SEugenio Pérez 
614725a418SEugenio Pérez /**
62dff4426fSEugenio Pérez  * Forward guest notifications.
63dff4426fSEugenio Pérez  *
64dff4426fSEugenio Pérez  * @n: guest kick event notifier, the one that guest set to notify svq.
65dff4426fSEugenio Pérez  */
66dff4426fSEugenio Pérez static void vhost_handle_guest_kick(EventNotifier *n)
67dff4426fSEugenio Pérez {
68dff4426fSEugenio Pérez     VhostShadowVirtqueue *svq = container_of(n, VhostShadowVirtqueue, svq_kick);
69dff4426fSEugenio Pérez     event_notifier_test_and_clear(n);
70dff4426fSEugenio Pérez     event_notifier_set(&svq->hdev_kick);
71dff4426fSEugenio Pérez }
72dff4426fSEugenio Pérez 
73dff4426fSEugenio Pérez /**
74a8ac8858SEugenio Pérez  * Forward vhost notifications
75a8ac8858SEugenio Pérez  *
76a8ac8858SEugenio Pérez  * @n: hdev call event notifier, the one that device set to notify svq.
77a8ac8858SEugenio Pérez  */
78a8ac8858SEugenio Pérez static void vhost_svq_handle_call(EventNotifier *n)
79a8ac8858SEugenio Pérez {
80a8ac8858SEugenio Pérez     VhostShadowVirtqueue *svq = container_of(n, VhostShadowVirtqueue,
81a8ac8858SEugenio Pérez                                              hdev_call);
82a8ac8858SEugenio Pérez     event_notifier_test_and_clear(n);
83a8ac8858SEugenio Pérez     event_notifier_set(&svq->svq_call);
84a8ac8858SEugenio Pérez }
85a8ac8858SEugenio Pérez 
86a8ac8858SEugenio Pérez /**
87a8ac8858SEugenio Pérez  * Set the call notifier for the SVQ to call the guest
88a8ac8858SEugenio Pérez  *
89a8ac8858SEugenio Pérez  * @svq: Shadow virtqueue
90a8ac8858SEugenio Pérez  * @call_fd: call notifier
91a8ac8858SEugenio Pérez  *
92a8ac8858SEugenio Pérez  * Called on BQL context.
93a8ac8858SEugenio Pérez  */
94a8ac8858SEugenio Pérez void vhost_svq_set_svq_call_fd(VhostShadowVirtqueue *svq, int call_fd)
95a8ac8858SEugenio Pérez {
96a8ac8858SEugenio Pérez     if (call_fd == VHOST_FILE_UNBIND) {
97a8ac8858SEugenio Pérez         /*
98a8ac8858SEugenio Pérez          * Fail event_notifier_set if called handling device call.
99a8ac8858SEugenio Pérez          *
100a8ac8858SEugenio Pérez          * SVQ still needs device notifications, since it needs to keep
101a8ac8858SEugenio Pérez          * forwarding used buffers even with the unbind.
102a8ac8858SEugenio Pérez          */
103a8ac8858SEugenio Pérez         memset(&svq->svq_call, 0, sizeof(svq->svq_call));
104a8ac8858SEugenio Pérez     } else {
105a8ac8858SEugenio Pérez         event_notifier_init_fd(&svq->svq_call, call_fd);
106a8ac8858SEugenio Pérez     }
107a8ac8858SEugenio Pérez }
108a8ac8858SEugenio Pérez 
109a8ac8858SEugenio Pérez /**
110*dafb34c9SEugenio Pérez  * Get the shadow vq vring address.
111*dafb34c9SEugenio Pérez  * @svq: Shadow virtqueue
112*dafb34c9SEugenio Pérez  * @addr: Destination to store address
113*dafb34c9SEugenio Pérez  */
114*dafb34c9SEugenio Pérez void vhost_svq_get_vring_addr(const VhostShadowVirtqueue *svq,
115*dafb34c9SEugenio Pérez                               struct vhost_vring_addr *addr)
116*dafb34c9SEugenio Pérez {
117*dafb34c9SEugenio Pérez     addr->desc_user_addr = (uint64_t)(intptr_t)svq->vring.desc;
118*dafb34c9SEugenio Pérez     addr->avail_user_addr = (uint64_t)(intptr_t)svq->vring.avail;
119*dafb34c9SEugenio Pérez     addr->used_user_addr = (uint64_t)(intptr_t)svq->vring.used;
120*dafb34c9SEugenio Pérez }
121*dafb34c9SEugenio Pérez 
122*dafb34c9SEugenio Pérez size_t vhost_svq_driver_area_size(const VhostShadowVirtqueue *svq)
123*dafb34c9SEugenio Pérez {
124*dafb34c9SEugenio Pérez     size_t desc_size = sizeof(vring_desc_t) * svq->vring.num;
125*dafb34c9SEugenio Pérez     size_t avail_size = offsetof(vring_avail_t, ring) +
126*dafb34c9SEugenio Pérez                                              sizeof(uint16_t) * svq->vring.num;
127*dafb34c9SEugenio Pérez 
128*dafb34c9SEugenio Pérez     return ROUND_UP(desc_size + avail_size, qemu_real_host_page_size);
129*dafb34c9SEugenio Pérez }
130*dafb34c9SEugenio Pérez 
131*dafb34c9SEugenio Pérez size_t vhost_svq_device_area_size(const VhostShadowVirtqueue *svq)
132*dafb34c9SEugenio Pérez {
133*dafb34c9SEugenio Pérez     size_t used_size = offsetof(vring_used_t, ring) +
134*dafb34c9SEugenio Pérez                                     sizeof(vring_used_elem_t) * svq->vring.num;
135*dafb34c9SEugenio Pérez     return ROUND_UP(used_size, qemu_real_host_page_size);
136*dafb34c9SEugenio Pérez }
137*dafb34c9SEugenio Pérez 
138*dafb34c9SEugenio Pérez /**
139dff4426fSEugenio Pérez  * Set a new file descriptor for the guest to kick the SVQ and notify for avail
140dff4426fSEugenio Pérez  *
141dff4426fSEugenio Pérez  * @svq: The svq
142dff4426fSEugenio Pérez  * @svq_kick_fd: The svq kick fd
143dff4426fSEugenio Pérez  *
144dff4426fSEugenio Pérez  * Note that the SVQ will never close the old file descriptor.
145dff4426fSEugenio Pérez  */
146dff4426fSEugenio Pérez void vhost_svq_set_svq_kick_fd(VhostShadowVirtqueue *svq, int svq_kick_fd)
147dff4426fSEugenio Pérez {
148dff4426fSEugenio Pérez     EventNotifier *svq_kick = &svq->svq_kick;
149dff4426fSEugenio Pérez     bool poll_stop = VHOST_FILE_UNBIND != event_notifier_get_fd(svq_kick);
150dff4426fSEugenio Pérez     bool poll_start = svq_kick_fd != VHOST_FILE_UNBIND;
151dff4426fSEugenio Pérez 
152dff4426fSEugenio Pérez     if (poll_stop) {
153dff4426fSEugenio Pérez         event_notifier_set_handler(svq_kick, NULL);
154dff4426fSEugenio Pérez     }
155dff4426fSEugenio Pérez 
156dff4426fSEugenio Pérez     /*
157dff4426fSEugenio Pérez      * event_notifier_set_handler already checks for guest's notifications if
158dff4426fSEugenio Pérez      * they arrive at the new file descriptor in the switch, so there is no
159dff4426fSEugenio Pérez      * need to explicitly check for them.
160dff4426fSEugenio Pérez      */
161dff4426fSEugenio Pérez     if (poll_start) {
162dff4426fSEugenio Pérez         event_notifier_init_fd(svq_kick, svq_kick_fd);
163dff4426fSEugenio Pérez         event_notifier_set(svq_kick);
164dff4426fSEugenio Pérez         event_notifier_set_handler(svq_kick, vhost_handle_guest_kick);
165dff4426fSEugenio Pérez     }
166dff4426fSEugenio Pérez }
167dff4426fSEugenio Pérez 
168dff4426fSEugenio Pérez /**
169dff4426fSEugenio Pérez  * Stop the shadow virtqueue operation.
170dff4426fSEugenio Pérez  * @svq: Shadow Virtqueue
171dff4426fSEugenio Pérez  */
172dff4426fSEugenio Pérez void vhost_svq_stop(VhostShadowVirtqueue *svq)
173dff4426fSEugenio Pérez {
174dff4426fSEugenio Pérez     event_notifier_set_handler(&svq->svq_kick, NULL);
175dff4426fSEugenio Pérez }
17610857ec0SEugenio Pérez 
17710857ec0SEugenio Pérez /**
17810857ec0SEugenio Pérez  * Creates vhost shadow virtqueue, and instructs the vhost device to use the
17910857ec0SEugenio Pérez  * shadow methods and file descriptors.
18010857ec0SEugenio Pérez  *
18110857ec0SEugenio Pérez  * Returns the new virtqueue or NULL.
18210857ec0SEugenio Pérez  *
18310857ec0SEugenio Pérez  * In case of error, reason is reported through error_report.
18410857ec0SEugenio Pérez  */
18510857ec0SEugenio Pérez VhostShadowVirtqueue *vhost_svq_new(void)
18610857ec0SEugenio Pérez {
18710857ec0SEugenio Pérez     g_autofree VhostShadowVirtqueue *svq = g_new0(VhostShadowVirtqueue, 1);
18810857ec0SEugenio Pérez     int r;
18910857ec0SEugenio Pérez 
19010857ec0SEugenio Pérez     r = event_notifier_init(&svq->hdev_kick, 0);
19110857ec0SEugenio Pérez     if (r != 0) {
19210857ec0SEugenio Pérez         error_report("Couldn't create kick event notifier: %s (%d)",
19310857ec0SEugenio Pérez                      g_strerror(errno), errno);
19410857ec0SEugenio Pérez         goto err_init_hdev_kick;
19510857ec0SEugenio Pérez     }
19610857ec0SEugenio Pérez 
19710857ec0SEugenio Pérez     r = event_notifier_init(&svq->hdev_call, 0);
19810857ec0SEugenio Pérez     if (r != 0) {
19910857ec0SEugenio Pérez         error_report("Couldn't create call event notifier: %s (%d)",
20010857ec0SEugenio Pérez                      g_strerror(errno), errno);
20110857ec0SEugenio Pérez         goto err_init_hdev_call;
20210857ec0SEugenio Pérez     }
20310857ec0SEugenio Pérez 
204dff4426fSEugenio Pérez     event_notifier_init_fd(&svq->svq_kick, VHOST_FILE_UNBIND);
205a8ac8858SEugenio Pérez     event_notifier_set_handler(&svq->hdev_call, vhost_svq_handle_call);
20610857ec0SEugenio Pérez     return g_steal_pointer(&svq);
20710857ec0SEugenio Pérez 
20810857ec0SEugenio Pérez err_init_hdev_call:
20910857ec0SEugenio Pérez     event_notifier_cleanup(&svq->hdev_kick);
21010857ec0SEugenio Pérez 
21110857ec0SEugenio Pérez err_init_hdev_kick:
21210857ec0SEugenio Pérez     return NULL;
21310857ec0SEugenio Pérez }
21410857ec0SEugenio Pérez 
21510857ec0SEugenio Pérez /**
21610857ec0SEugenio Pérez  * Free the resources of the shadow virtqueue.
21710857ec0SEugenio Pérez  *
21810857ec0SEugenio Pérez  * @pvq: gpointer to SVQ so it can be used by autofree functions.
21910857ec0SEugenio Pérez  */
22010857ec0SEugenio Pérez void vhost_svq_free(gpointer pvq)
22110857ec0SEugenio Pérez {
22210857ec0SEugenio Pérez     VhostShadowVirtqueue *vq = pvq;
223dff4426fSEugenio Pérez     vhost_svq_stop(vq);
22410857ec0SEugenio Pérez     event_notifier_cleanup(&vq->hdev_kick);
225a8ac8858SEugenio Pérez     event_notifier_set_handler(&vq->hdev_call, NULL);
22610857ec0SEugenio Pérez     event_notifier_cleanup(&vq->hdev_call);
22710857ec0SEugenio Pérez     g_free(vq);
22810857ec0SEugenio Pérez }
229