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