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" 14dff4426fSEugenio Pérez #include "qemu/main-loop.h" 15dff4426fSEugenio Pérez #include "linux-headers/linux/vhost.h" 16dff4426fSEugenio Pérez 17dff4426fSEugenio Pérez /** 18dff4426fSEugenio Pérez * Forward guest notifications. 19dff4426fSEugenio Pérez * 20dff4426fSEugenio Pérez * @n: guest kick event notifier, the one that guest set to notify svq. 21dff4426fSEugenio Pérez */ 22dff4426fSEugenio Pérez static void vhost_handle_guest_kick(EventNotifier *n) 23dff4426fSEugenio Pérez { 24dff4426fSEugenio Pérez VhostShadowVirtqueue *svq = container_of(n, VhostShadowVirtqueue, svq_kick); 25dff4426fSEugenio Pérez event_notifier_test_and_clear(n); 26dff4426fSEugenio Pérez event_notifier_set(&svq->hdev_kick); 27dff4426fSEugenio Pérez } 28dff4426fSEugenio Pérez 29dff4426fSEugenio Pérez /** 30*a8ac8858SEugenio Pérez * Forward vhost notifications 31*a8ac8858SEugenio Pérez * 32*a8ac8858SEugenio Pérez * @n: hdev call event notifier, the one that device set to notify svq. 33*a8ac8858SEugenio Pérez */ 34*a8ac8858SEugenio Pérez static void vhost_svq_handle_call(EventNotifier *n) 35*a8ac8858SEugenio Pérez { 36*a8ac8858SEugenio Pérez VhostShadowVirtqueue *svq = container_of(n, VhostShadowVirtqueue, 37*a8ac8858SEugenio Pérez hdev_call); 38*a8ac8858SEugenio Pérez event_notifier_test_and_clear(n); 39*a8ac8858SEugenio Pérez event_notifier_set(&svq->svq_call); 40*a8ac8858SEugenio Pérez } 41*a8ac8858SEugenio Pérez 42*a8ac8858SEugenio Pérez /** 43*a8ac8858SEugenio Pérez * Set the call notifier for the SVQ to call the guest 44*a8ac8858SEugenio Pérez * 45*a8ac8858SEugenio Pérez * @svq: Shadow virtqueue 46*a8ac8858SEugenio Pérez * @call_fd: call notifier 47*a8ac8858SEugenio Pérez * 48*a8ac8858SEugenio Pérez * Called on BQL context. 49*a8ac8858SEugenio Pérez */ 50*a8ac8858SEugenio Pérez void vhost_svq_set_svq_call_fd(VhostShadowVirtqueue *svq, int call_fd) 51*a8ac8858SEugenio Pérez { 52*a8ac8858SEugenio Pérez if (call_fd == VHOST_FILE_UNBIND) { 53*a8ac8858SEugenio Pérez /* 54*a8ac8858SEugenio Pérez * Fail event_notifier_set if called handling device call. 55*a8ac8858SEugenio Pérez * 56*a8ac8858SEugenio Pérez * SVQ still needs device notifications, since it needs to keep 57*a8ac8858SEugenio Pérez * forwarding used buffers even with the unbind. 58*a8ac8858SEugenio Pérez */ 59*a8ac8858SEugenio Pérez memset(&svq->svq_call, 0, sizeof(svq->svq_call)); 60*a8ac8858SEugenio Pérez } else { 61*a8ac8858SEugenio Pérez event_notifier_init_fd(&svq->svq_call, call_fd); 62*a8ac8858SEugenio Pérez } 63*a8ac8858SEugenio Pérez } 64*a8ac8858SEugenio Pérez 65*a8ac8858SEugenio Pérez /** 66dff4426fSEugenio Pérez * Set a new file descriptor for the guest to kick the SVQ and notify for avail 67dff4426fSEugenio Pérez * 68dff4426fSEugenio Pérez * @svq: The svq 69dff4426fSEugenio Pérez * @svq_kick_fd: The svq kick fd 70dff4426fSEugenio Pérez * 71dff4426fSEugenio Pérez * Note that the SVQ will never close the old file descriptor. 72dff4426fSEugenio Pérez */ 73dff4426fSEugenio Pérez void vhost_svq_set_svq_kick_fd(VhostShadowVirtqueue *svq, int svq_kick_fd) 74dff4426fSEugenio Pérez { 75dff4426fSEugenio Pérez EventNotifier *svq_kick = &svq->svq_kick; 76dff4426fSEugenio Pérez bool poll_stop = VHOST_FILE_UNBIND != event_notifier_get_fd(svq_kick); 77dff4426fSEugenio Pérez bool poll_start = svq_kick_fd != VHOST_FILE_UNBIND; 78dff4426fSEugenio Pérez 79dff4426fSEugenio Pérez if (poll_stop) { 80dff4426fSEugenio Pérez event_notifier_set_handler(svq_kick, NULL); 81dff4426fSEugenio Pérez } 82dff4426fSEugenio Pérez 83dff4426fSEugenio Pérez /* 84dff4426fSEugenio Pérez * event_notifier_set_handler already checks for guest's notifications if 85dff4426fSEugenio Pérez * they arrive at the new file descriptor in the switch, so there is no 86dff4426fSEugenio Pérez * need to explicitly check for them. 87dff4426fSEugenio Pérez */ 88dff4426fSEugenio Pérez if (poll_start) { 89dff4426fSEugenio Pérez event_notifier_init_fd(svq_kick, svq_kick_fd); 90dff4426fSEugenio Pérez event_notifier_set(svq_kick); 91dff4426fSEugenio Pérez event_notifier_set_handler(svq_kick, vhost_handle_guest_kick); 92dff4426fSEugenio Pérez } 93dff4426fSEugenio Pérez } 94dff4426fSEugenio Pérez 95dff4426fSEugenio Pérez /** 96dff4426fSEugenio Pérez * Stop the shadow virtqueue operation. 97dff4426fSEugenio Pérez * @svq: Shadow Virtqueue 98dff4426fSEugenio Pérez */ 99dff4426fSEugenio Pérez void vhost_svq_stop(VhostShadowVirtqueue *svq) 100dff4426fSEugenio Pérez { 101dff4426fSEugenio Pérez event_notifier_set_handler(&svq->svq_kick, NULL); 102dff4426fSEugenio Pérez } 10310857ec0SEugenio Pérez 10410857ec0SEugenio Pérez /** 10510857ec0SEugenio Pérez * Creates vhost shadow virtqueue, and instructs the vhost device to use the 10610857ec0SEugenio Pérez * shadow methods and file descriptors. 10710857ec0SEugenio Pérez * 10810857ec0SEugenio Pérez * Returns the new virtqueue or NULL. 10910857ec0SEugenio Pérez * 11010857ec0SEugenio Pérez * In case of error, reason is reported through error_report. 11110857ec0SEugenio Pérez */ 11210857ec0SEugenio Pérez VhostShadowVirtqueue *vhost_svq_new(void) 11310857ec0SEugenio Pérez { 11410857ec0SEugenio Pérez g_autofree VhostShadowVirtqueue *svq = g_new0(VhostShadowVirtqueue, 1); 11510857ec0SEugenio Pérez int r; 11610857ec0SEugenio Pérez 11710857ec0SEugenio Pérez r = event_notifier_init(&svq->hdev_kick, 0); 11810857ec0SEugenio Pérez if (r != 0) { 11910857ec0SEugenio Pérez error_report("Couldn't create kick event notifier: %s (%d)", 12010857ec0SEugenio Pérez g_strerror(errno), errno); 12110857ec0SEugenio Pérez goto err_init_hdev_kick; 12210857ec0SEugenio Pérez } 12310857ec0SEugenio Pérez 12410857ec0SEugenio Pérez r = event_notifier_init(&svq->hdev_call, 0); 12510857ec0SEugenio Pérez if (r != 0) { 12610857ec0SEugenio Pérez error_report("Couldn't create call event notifier: %s (%d)", 12710857ec0SEugenio Pérez g_strerror(errno), errno); 12810857ec0SEugenio Pérez goto err_init_hdev_call; 12910857ec0SEugenio Pérez } 13010857ec0SEugenio Pérez 131dff4426fSEugenio Pérez event_notifier_init_fd(&svq->svq_kick, VHOST_FILE_UNBIND); 132*a8ac8858SEugenio Pérez event_notifier_set_handler(&svq->hdev_call, vhost_svq_handle_call); 13310857ec0SEugenio Pérez return g_steal_pointer(&svq); 13410857ec0SEugenio Pérez 13510857ec0SEugenio Pérez err_init_hdev_call: 13610857ec0SEugenio Pérez event_notifier_cleanup(&svq->hdev_kick); 13710857ec0SEugenio Pérez 13810857ec0SEugenio Pérez err_init_hdev_kick: 13910857ec0SEugenio Pérez return NULL; 14010857ec0SEugenio Pérez } 14110857ec0SEugenio Pérez 14210857ec0SEugenio Pérez /** 14310857ec0SEugenio Pérez * Free the resources of the shadow virtqueue. 14410857ec0SEugenio Pérez * 14510857ec0SEugenio Pérez * @pvq: gpointer to SVQ so it can be used by autofree functions. 14610857ec0SEugenio Pérez */ 14710857ec0SEugenio Pérez void vhost_svq_free(gpointer pvq) 14810857ec0SEugenio Pérez { 14910857ec0SEugenio Pérez VhostShadowVirtqueue *vq = pvq; 150dff4426fSEugenio Pérez vhost_svq_stop(vq); 15110857ec0SEugenio Pérez event_notifier_cleanup(&vq->hdev_kick); 152*a8ac8858SEugenio Pérez event_notifier_set_handler(&vq->hdev_call, NULL); 15310857ec0SEugenio Pérez event_notifier_cleanup(&vq->hdev_call); 15410857ec0SEugenio Pérez g_free(vq); 15510857ec0SEugenio Pérez } 156