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