1 /*
2  * vhost shadow virtqueue
3  *
4  * SPDX-FileCopyrightText: Red Hat, Inc. 2021
5  * SPDX-FileContributor: Author: Eugenio Pérez <eperezma@redhat.com>
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  */
9 
10 #ifndef VHOST_SHADOW_VIRTQUEUE_H
11 #define VHOST_SHADOW_VIRTQUEUE_H
12 
13 #include "qemu/event_notifier.h"
14 #include "hw/virtio/virtio.h"
15 #include "standard-headers/linux/vhost_types.h"
16 #include "hw/virtio/vhost-iova-tree.h"
17 
18 typedef struct SVQDescState {
19     VirtQueueElement *elem;
20 
21     /*
22      * Number of descriptors exposed to the device. May or may not match
23      * guest's
24      */
25     unsigned int ndescs;
26 } SVQDescState;
27 
28 typedef struct VhostShadowVirtqueue VhostShadowVirtqueue;
29 
30 /**
31  * Callback to handle an avail buffer.
32  *
33  * @svq:  Shadow virtqueue
34  * @elem:  Element placed in the queue by the guest
35  * @vq_callback_opaque:  Opaque
36  *
37  * Returns 0 if the vq is running as expected.
38  *
39  * Note that ownership of elem is transferred to the callback.
40  */
41 typedef int (*VirtQueueAvailCallback)(VhostShadowVirtqueue *svq,
42                                       VirtQueueElement *elem,
43                                       void *vq_callback_opaque);
44 
45 typedef struct VhostShadowVirtqueueOps {
46     VirtQueueAvailCallback avail_handler;
47 } VhostShadowVirtqueueOps;
48 
49 /* Shadow virtqueue to relay notifications */
50 typedef struct VhostShadowVirtqueue {
51     /* Shadow vring */
52     struct vring vring;
53 
54     /* Shadow kick notifier, sent to vhost */
55     EventNotifier hdev_kick;
56     /* Shadow call notifier, sent to vhost */
57     EventNotifier hdev_call;
58 
59     /*
60      * Borrowed virtqueue's guest to host notifier. To borrow it in this event
61      * notifier allows to recover the VhostShadowVirtqueue from the event loop
62      * easily. If we use the VirtQueue's one, we don't have an easy way to
63      * retrieve VhostShadowVirtqueue.
64      *
65      * So shadow virtqueue must not clean it, or we would lose VirtQueue one.
66      */
67     EventNotifier svq_kick;
68 
69     /* Guest's call notifier, where the SVQ calls guest. */
70     EventNotifier svq_call;
71 
72     /* Virtio queue shadowing */
73     VirtQueue *vq;
74 
75     /* Virtio device */
76     VirtIODevice *vdev;
77 
78     /* IOVA mapping */
79     VhostIOVATree *iova_tree;
80 
81     /* SVQ vring descriptors state */
82     SVQDescState *desc_state;
83 
84     /* Next VirtQueue element that guest made available */
85     VirtQueueElement *next_guest_avail_elem;
86 
87     /*
88      * Backup next field for each descriptor so we can recover securely, not
89      * needing to trust the device access.
90      */
91     uint16_t *desc_next;
92 
93     /* Caller callbacks */
94     const VhostShadowVirtqueueOps *ops;
95 
96     /* Caller callbacks opaque */
97     void *ops_opaque;
98 
99     /* Next head to expose to the device */
100     uint16_t shadow_avail_idx;
101 
102     /* Next free descriptor */
103     uint16_t free_head;
104 
105     /* Last seen used idx */
106     uint16_t shadow_used_idx;
107 
108     /* Next head to consume from the device */
109     uint16_t last_used_idx;
110 
111     /* Size of SVQ vring free descriptors */
112     uint16_t num_free;
113 } VhostShadowVirtqueue;
114 
115 bool vhost_svq_valid_features(uint64_t features, Error **errp);
116 
117 void vhost_svq_push_elem(VhostShadowVirtqueue *svq,
118                          const VirtQueueElement *elem, uint32_t len);
119 int vhost_svq_add(VhostShadowVirtqueue *svq, const struct iovec *out_sg,
120                   size_t out_num, const struct iovec *in_sg, size_t in_num,
121                   VirtQueueElement *elem);
122 size_t vhost_svq_poll(VhostShadowVirtqueue *svq, size_t num);
123 
124 void vhost_svq_set_svq_kick_fd(VhostShadowVirtqueue *svq, int svq_kick_fd);
125 void vhost_svq_set_svq_call_fd(VhostShadowVirtqueue *svq, int call_fd);
126 void vhost_svq_get_vring_addr(const VhostShadowVirtqueue *svq,
127                               struct vhost_vring_addr *addr);
128 size_t vhost_svq_driver_area_size(const VhostShadowVirtqueue *svq);
129 size_t vhost_svq_device_area_size(const VhostShadowVirtqueue *svq);
130 
131 void vhost_svq_start(VhostShadowVirtqueue *svq, VirtIODevice *vdev,
132                      VirtQueue *vq, VhostIOVATree *iova_tree);
133 void vhost_svq_stop(VhostShadowVirtqueue *svq);
134 
135 VhostShadowVirtqueue *vhost_svq_new(const VhostShadowVirtqueueOps *ops,
136                                     void *ops_opaque);
137 
138 void vhost_svq_free(gpointer vq);
139 G_DEFINE_AUTOPTR_CLEANUP_FUNC(VhostShadowVirtqueue, vhost_svq_free);
140 
141 #endif
142