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" 14*4725a418SEugenio 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 /** 19*4725a418SEugenio Pérez * Validate the transport device features that both guests can use with the SVQ 20*4725a418SEugenio Pérez * and SVQs can use with the device. 21*4725a418SEugenio Pérez * 22*4725a418SEugenio Pérez * @dev_features: The features 23*4725a418SEugenio Pérez * @errp: Error pointer 24*4725a418SEugenio Pérez */ 25*4725a418SEugenio Pérez bool vhost_svq_valid_features(uint64_t features, Error **errp) 26*4725a418SEugenio Pérez { 27*4725a418SEugenio Pérez bool ok = true; 28*4725a418SEugenio Pérez uint64_t svq_features = features; 29*4725a418SEugenio Pérez 30*4725a418SEugenio Pérez for (uint64_t b = VIRTIO_TRANSPORT_F_START; b <= VIRTIO_TRANSPORT_F_END; 31*4725a418SEugenio Pérez ++b) { 32*4725a418SEugenio Pérez switch (b) { 33*4725a418SEugenio Pérez case VIRTIO_F_ANY_LAYOUT: 34*4725a418SEugenio Pérez continue; 35*4725a418SEugenio Pérez 36*4725a418SEugenio Pérez case VIRTIO_F_ACCESS_PLATFORM: 37*4725a418SEugenio Pérez /* SVQ trust in the host's IOMMU to translate addresses */ 38*4725a418SEugenio Pérez case VIRTIO_F_VERSION_1: 39*4725a418SEugenio Pérez /* SVQ trust that the guest vring is little endian */ 40*4725a418SEugenio Pérez if (!(svq_features & BIT_ULL(b))) { 41*4725a418SEugenio Pérez svq_features |= BIT_ULL(b); 42*4725a418SEugenio Pérez ok = false; 43*4725a418SEugenio Pérez } 44*4725a418SEugenio Pérez continue; 45*4725a418SEugenio Pérez 46*4725a418SEugenio Pérez default: 47*4725a418SEugenio Pérez if (svq_features & BIT_ULL(b)) { 48*4725a418SEugenio Pérez svq_features &= ~BIT_ULL(b); 49*4725a418SEugenio Pérez ok = false; 50*4725a418SEugenio Pérez } 51*4725a418SEugenio Pérez } 52*4725a418SEugenio Pérez } 53*4725a418SEugenio Pérez 54*4725a418SEugenio Pérez if (!ok) { 55*4725a418SEugenio Pérez error_setg(errp, "SVQ Invalid device feature flags, offer: 0x%"PRIx64 56*4725a418SEugenio Pérez ", ok: 0x%"PRIx64, features, svq_features); 57*4725a418SEugenio Pérez } 58*4725a418SEugenio Pérez return ok; 59*4725a418SEugenio Pérez } 60*4725a418SEugenio Pérez 61*4725a418SEugenio 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 /** 110dff4426fSEugenio Pérez * Set a new file descriptor for the guest to kick the SVQ and notify for avail 111dff4426fSEugenio Pérez * 112dff4426fSEugenio Pérez * @svq: The svq 113dff4426fSEugenio Pérez * @svq_kick_fd: The svq kick fd 114dff4426fSEugenio Pérez * 115dff4426fSEugenio Pérez * Note that the SVQ will never close the old file descriptor. 116dff4426fSEugenio Pérez */ 117dff4426fSEugenio Pérez void vhost_svq_set_svq_kick_fd(VhostShadowVirtqueue *svq, int svq_kick_fd) 118dff4426fSEugenio Pérez { 119dff4426fSEugenio Pérez EventNotifier *svq_kick = &svq->svq_kick; 120dff4426fSEugenio Pérez bool poll_stop = VHOST_FILE_UNBIND != event_notifier_get_fd(svq_kick); 121dff4426fSEugenio Pérez bool poll_start = svq_kick_fd != VHOST_FILE_UNBIND; 122dff4426fSEugenio Pérez 123dff4426fSEugenio Pérez if (poll_stop) { 124dff4426fSEugenio Pérez event_notifier_set_handler(svq_kick, NULL); 125dff4426fSEugenio Pérez } 126dff4426fSEugenio Pérez 127dff4426fSEugenio Pérez /* 128dff4426fSEugenio Pérez * event_notifier_set_handler already checks for guest's notifications if 129dff4426fSEugenio Pérez * they arrive at the new file descriptor in the switch, so there is no 130dff4426fSEugenio Pérez * need to explicitly check for them. 131dff4426fSEugenio Pérez */ 132dff4426fSEugenio Pérez if (poll_start) { 133dff4426fSEugenio Pérez event_notifier_init_fd(svq_kick, svq_kick_fd); 134dff4426fSEugenio Pérez event_notifier_set(svq_kick); 135dff4426fSEugenio Pérez event_notifier_set_handler(svq_kick, vhost_handle_guest_kick); 136dff4426fSEugenio Pérez } 137dff4426fSEugenio Pérez } 138dff4426fSEugenio Pérez 139dff4426fSEugenio Pérez /** 140dff4426fSEugenio Pérez * Stop the shadow virtqueue operation. 141dff4426fSEugenio Pérez * @svq: Shadow Virtqueue 142dff4426fSEugenio Pérez */ 143dff4426fSEugenio Pérez void vhost_svq_stop(VhostShadowVirtqueue *svq) 144dff4426fSEugenio Pérez { 145dff4426fSEugenio Pérez event_notifier_set_handler(&svq->svq_kick, NULL); 146dff4426fSEugenio Pérez } 14710857ec0SEugenio Pérez 14810857ec0SEugenio Pérez /** 14910857ec0SEugenio Pérez * Creates vhost shadow virtqueue, and instructs the vhost device to use the 15010857ec0SEugenio Pérez * shadow methods and file descriptors. 15110857ec0SEugenio Pérez * 15210857ec0SEugenio Pérez * Returns the new virtqueue or NULL. 15310857ec0SEugenio Pérez * 15410857ec0SEugenio Pérez * In case of error, reason is reported through error_report. 15510857ec0SEugenio Pérez */ 15610857ec0SEugenio Pérez VhostShadowVirtqueue *vhost_svq_new(void) 15710857ec0SEugenio Pérez { 15810857ec0SEugenio Pérez g_autofree VhostShadowVirtqueue *svq = g_new0(VhostShadowVirtqueue, 1); 15910857ec0SEugenio Pérez int r; 16010857ec0SEugenio Pérez 16110857ec0SEugenio Pérez r = event_notifier_init(&svq->hdev_kick, 0); 16210857ec0SEugenio Pérez if (r != 0) { 16310857ec0SEugenio Pérez error_report("Couldn't create kick event notifier: %s (%d)", 16410857ec0SEugenio Pérez g_strerror(errno), errno); 16510857ec0SEugenio Pérez goto err_init_hdev_kick; 16610857ec0SEugenio Pérez } 16710857ec0SEugenio Pérez 16810857ec0SEugenio Pérez r = event_notifier_init(&svq->hdev_call, 0); 16910857ec0SEugenio Pérez if (r != 0) { 17010857ec0SEugenio Pérez error_report("Couldn't create call event notifier: %s (%d)", 17110857ec0SEugenio Pérez g_strerror(errno), errno); 17210857ec0SEugenio Pérez goto err_init_hdev_call; 17310857ec0SEugenio Pérez } 17410857ec0SEugenio Pérez 175dff4426fSEugenio Pérez event_notifier_init_fd(&svq->svq_kick, VHOST_FILE_UNBIND); 176a8ac8858SEugenio Pérez event_notifier_set_handler(&svq->hdev_call, vhost_svq_handle_call); 17710857ec0SEugenio Pérez return g_steal_pointer(&svq); 17810857ec0SEugenio Pérez 17910857ec0SEugenio Pérez err_init_hdev_call: 18010857ec0SEugenio Pérez event_notifier_cleanup(&svq->hdev_kick); 18110857ec0SEugenio Pérez 18210857ec0SEugenio Pérez err_init_hdev_kick: 18310857ec0SEugenio Pérez return NULL; 18410857ec0SEugenio Pérez } 18510857ec0SEugenio Pérez 18610857ec0SEugenio Pérez /** 18710857ec0SEugenio Pérez * Free the resources of the shadow virtqueue. 18810857ec0SEugenio Pérez * 18910857ec0SEugenio Pérez * @pvq: gpointer to SVQ so it can be used by autofree functions. 19010857ec0SEugenio Pérez */ 19110857ec0SEugenio Pérez void vhost_svq_free(gpointer pvq) 19210857ec0SEugenio Pérez { 19310857ec0SEugenio Pérez VhostShadowVirtqueue *vq = pvq; 194dff4426fSEugenio Pérez vhost_svq_stop(vq); 19510857ec0SEugenio Pérez event_notifier_cleanup(&vq->hdev_kick); 196a8ac8858SEugenio Pérez event_notifier_set_handler(&vq->hdev_call, NULL); 19710857ec0SEugenio Pérez event_notifier_cleanup(&vq->hdev_call); 19810857ec0SEugenio Pérez g_free(vq); 19910857ec0SEugenio Pérez } 200