xref: /qemu/hw/virtio/vhost-shadow-virtqueue.c (revision a8ac88585da1a3ae9c48bfd9e04430e895dc912d)
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