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