xref: /kvmtool/virtio/vsock.c (revision 3b1cdcf9e78f7d36f0cca805c4172bba53779b69)
1 #include "kvm/virtio-vsock.h"
2 #include "kvm/virtio-pci-dev.h"
3 #include "kvm/kvm.h"
4 #include "kvm/pci.h"
5 #include "kvm/ioeventfd.h"
6 #include "kvm/guest_compat.h"
7 #include "kvm/virtio-pci.h"
8 #include "kvm/virtio.h"
9 
10 #include <linux/byteorder.h>
11 #include <linux/kernel.h>
12 #include <linux/virtio_vsock.h>
13 #include <linux/vhost.h>
14 
15 #define VIRTIO_VSOCK_QUEUE_SIZE		128
16 
17 static LIST_HEAD(vdevs);
18 static int compat_id = -1;
19 
20 enum {
21 	VSOCK_VQ_RX     = 0, /* for host to guest data */
22 	VSOCK_VQ_TX     = 1, /* for guest to host data */
23 	VSOCK_VQ_EVENT  = 2,
24 	VSOCK_VQ_MAX    = 3,
25 };
26 
27 struct vsock_dev {
28 	struct virt_queue		vqs[VSOCK_VQ_MAX];
29 	struct virtio_vsock_config	config;
30 	u64				guest_cid;
31 	u32				features;
32 	int				vhost_fd;
33 	struct virtio_device		vdev;
34 	struct list_head		list;
35 	struct kvm			*kvm;
36 };
37 
get_config(struct kvm * kvm,void * dev)38 static u8 *get_config(struct kvm *kvm, void *dev)
39 {
40 	struct vsock_dev *vdev = dev;
41 
42 	return ((u8 *)(&vdev->config));
43 }
44 
get_config_size(struct kvm * kvm,void * dev)45 static size_t get_config_size(struct kvm *kvm, void *dev)
46 {
47 	struct vsock_dev *vdev = dev;
48 
49 	return sizeof(vdev->config);
50 }
51 
get_host_features(struct kvm * kvm,void * dev)52 static u64 get_host_features(struct kvm *kvm, void *dev)
53 {
54 	int r;
55 	u64 features;
56 	struct vsock_dev *vdev = dev;
57 
58 	r = ioctl(vdev->vhost_fd, VHOST_GET_FEATURES, &features);
59 	if (r != 0)
60 		die_perror("VHOST_GET_FEATURES failed");
61 
62 	return features &
63 		(1ULL << VIRTIO_RING_F_EVENT_IDX |
64 		 1ULL << VIRTIO_RING_F_INDIRECT_DESC);
65 }
66 
is_event_vq(u32 vq)67 static bool is_event_vq(u32 vq)
68 {
69 	return vq == VSOCK_VQ_EVENT;
70 }
71 
init_vq(struct kvm * kvm,void * dev,u32 vq)72 static int init_vq(struct kvm *kvm, void *dev, u32 vq)
73 {
74 	struct vsock_dev *vdev = dev;
75 	struct virt_queue *queue;
76 
77 	compat__remove_message(compat_id);
78 
79 	queue		= &vdev->vqs[vq];
80 	virtio_init_device_vq(kvm, &vdev->vdev, queue, VIRTIO_VSOCK_QUEUE_SIZE);
81 
82 	if (vdev->vhost_fd == -1 || is_event_vq(vq))
83 		return 0;
84 
85 	virtio_vhost_set_vring(kvm, vdev->vhost_fd, vq, queue);
86 	return 0;
87 }
88 
notify_vq_eventfd(struct kvm * kvm,void * dev,u32 vq,u32 efd)89 static void notify_vq_eventfd(struct kvm *kvm, void *dev, u32 vq, u32 efd)
90 {
91 	struct vsock_dev *vdev = dev;
92 
93 	if (vdev->vhost_fd == -1 || is_event_vq(vq))
94 		return;
95 
96 	virtio_vhost_set_vring_kick(kvm, vdev->vhost_fd, vq, efd);
97 }
98 
notify_status(struct kvm * kvm,void * dev,u32 status)99 static void notify_status(struct kvm *kvm, void *dev, u32 status)
100 {
101 	struct vsock_dev *vdev = dev;
102 	int r, start;
103 
104 	if (status & VIRTIO__STATUS_CONFIG)
105 		vdev->config.guest_cid = cpu_to_le64(vdev->guest_cid);
106 
107 	if (status & VIRTIO__STATUS_START) {
108 		start = 1;
109 
110 		r = virtio_vhost_set_features(vdev->vhost_fd,
111 					      vdev->vdev.features);
112 		if (r != 0)
113 			die_perror("VHOST_SET_FEATURES failed");
114 	} else if (status & VIRTIO__STATUS_STOP) {
115 		start = 0;
116 	} else {
117 		return;
118 	}
119 
120 	r = ioctl(vdev->vhost_fd, VHOST_VSOCK_SET_RUNNING, &start);
121 	if (r != 0)
122 		die("VHOST_VSOCK_SET_RUNNING failed %d", errno);
123 }
124 
notify_vq(struct kvm * kvm,void * dev,u32 vq)125 static int notify_vq(struct kvm *kvm, void *dev, u32 vq)
126 {
127 	return 0;
128 }
129 
get_vq(struct kvm * kvm,void * dev,u32 vq)130 static struct virt_queue *get_vq(struct kvm *kvm, void *dev, u32 vq)
131 {
132 	struct vsock_dev *vdev = dev;
133 
134 	return &vdev->vqs[vq];
135 }
136 
get_size_vq(struct kvm * kvm,void * dev,u32 vq)137 static int get_size_vq(struct kvm *kvm, void *dev, u32 vq)
138 {
139 	return VIRTIO_VSOCK_QUEUE_SIZE;
140 }
141 
set_size_vq(struct kvm * kvm,void * dev,u32 vq,int size)142 static int set_size_vq(struct kvm *kvm, void *dev, u32 vq, int size)
143 {
144 	return size;
145 }
146 
notify_vq_gsi(struct kvm * kvm,void * dev,u32 vq,u32 gsi)147 static void notify_vq_gsi(struct kvm *kvm, void *dev, u32 vq, u32 gsi)
148 {
149 	struct vsock_dev *vdev = dev;
150 
151 	if (vdev->vhost_fd == -1 || is_event_vq(vq))
152 		return;
153 
154 	virtio_vhost_set_vring_irqfd(kvm, gsi, &vdev->vqs[vq]);
155 }
156 
get_vq_count(struct kvm * kvm,void * dev)157 static unsigned int get_vq_count(struct kvm *kvm, void *dev)
158 {
159 	return VSOCK_VQ_MAX;
160 }
161 
162 static struct virtio_ops vsock_dev_virtio_ops = {
163 	.get_config		= get_config,
164 	.get_config_size	= get_config_size,
165 	.get_host_features	= get_host_features,
166 	.init_vq		= init_vq,
167 	.get_vq			= get_vq,
168 	.get_size_vq		= get_size_vq,
169 	.set_size_vq		= set_size_vq,
170 	.notify_vq_eventfd	= notify_vq_eventfd,
171 	.notify_status		= notify_status,
172 	.notify_vq_gsi		= notify_vq_gsi,
173 	.notify_vq		= notify_vq,
174 	.get_vq_count		= get_vq_count,
175 };
176 
virtio_vhost_vsock_init(struct kvm * kvm,struct vsock_dev * vdev)177 static void virtio_vhost_vsock_init(struct kvm *kvm, struct vsock_dev *vdev)
178 {
179 	int r;
180 
181 	vdev->vhost_fd = open("/dev/vhost-vsock", O_RDWR);
182 	if (vdev->vhost_fd < 0)
183 		die_perror("Failed opening vhost-vsock device");
184 
185 	virtio_vhost_init(kvm, vdev->vhost_fd);
186 
187 	r = ioctl(vdev->vhost_fd, VHOST_VSOCK_SET_GUEST_CID, &vdev->guest_cid);
188 	if (r != 0)
189 		die_perror("VHOST_VSOCK_SET_GUEST_CID failed");
190 
191 	vdev->vdev.use_vhost = true;
192 }
193 
virtio_vsock_init_one(struct kvm * kvm,u64 guest_cid)194 static int virtio_vsock_init_one(struct kvm *kvm, u64 guest_cid)
195 {
196 	struct vsock_dev *vdev;
197 	int r;
198 
199 	vdev = calloc(1, sizeof(struct vsock_dev));
200 	if (vdev == NULL)
201 		return -ENOMEM;
202 
203 	*vdev = (struct vsock_dev) {
204 		.guest_cid		= guest_cid,
205 		.vhost_fd		= -1,
206 		.kvm			= kvm,
207 	};
208 
209 	list_add_tail(&vdev->list, &vdevs);
210 
211 	r = virtio_init(kvm, vdev, &vdev->vdev, &vsock_dev_virtio_ops,
212 		    kvm->cfg.virtio_transport, PCI_DEVICE_ID_VIRTIO_VSOCK,
213 		    VIRTIO_ID_VSOCK, PCI_CLASS_VSOCK);
214 	if (r < 0)
215 	    return r;
216 
217 	virtio_vhost_vsock_init(kvm, vdev);
218 
219 	if (compat_id == -1)
220 		compat_id = virtio_compat_add_message("virtio-vsock", "CONFIG_VIRTIO_VSOCKETS");
221 
222 	return 0;
223 }
224 
virtio_vsock_exit_one(struct kvm * kvm,struct vsock_dev * vdev)225 static int virtio_vsock_exit_one(struct kvm *kvm, struct vsock_dev *vdev)
226 {
227 	list_del(&vdev->list);
228 	free(vdev);
229 
230 	return 0;
231 }
232 
virtio_vsock_init(struct kvm * kvm)233 int virtio_vsock_init(struct kvm *kvm)
234 {
235 	int r;
236 
237 	if (kvm->cfg.vsock_cid == 0)
238 		return 0;
239 
240 	r = virtio_vsock_init_one(kvm, kvm->cfg.vsock_cid);
241 	if (r < 0)
242 		goto cleanup;
243 
244 	return 0;
245 cleanup:
246 	return virtio_vsock_exit(kvm);
247 }
248 virtio_dev_init(virtio_vsock_init);
249 
virtio_vsock_exit(struct kvm * kvm)250 int virtio_vsock_exit(struct kvm *kvm)
251 {
252 	while (!list_empty(&vdevs)) {
253 		struct vsock_dev *vdev;
254 
255 		vdev = list_first_entry(&vdevs, struct vsock_dev, list);
256 		virtio_vsock_exit_one(kvm, vdev);
257 	}
258 
259 	return 0;
260 }
261 virtio_dev_exit(virtio_vsock_exit);
262