xref: /kvmtool/virtio/vsock.c (revision 31e0eacca520f60ac02dfaaaeaeddfcc132095c0)
1117d6495STianjia Zhang #include "kvm/virtio-vsock.h"
2117d6495STianjia Zhang #include "kvm/virtio-pci-dev.h"
3117d6495STianjia Zhang #include "kvm/kvm.h"
4117d6495STianjia Zhang #include "kvm/pci.h"
5117d6495STianjia Zhang #include "kvm/ioeventfd.h"
6117d6495STianjia Zhang #include "kvm/guest_compat.h"
7117d6495STianjia Zhang #include "kvm/virtio-pci.h"
8117d6495STianjia Zhang #include "kvm/virtio.h"
9117d6495STianjia Zhang 
10117d6495STianjia Zhang #include <linux/kernel.h>
11117d6495STianjia Zhang #include <linux/virtio_vsock.h>
12117d6495STianjia Zhang #include <linux/vhost.h>
13117d6495STianjia Zhang 
14117d6495STianjia Zhang #define VIRTIO_VSOCK_QUEUE_SIZE		128
15117d6495STianjia Zhang 
16117d6495STianjia Zhang static LIST_HEAD(vdevs);
17117d6495STianjia Zhang static int compat_id = -1;
18117d6495STianjia Zhang 
19117d6495STianjia Zhang enum {
20117d6495STianjia Zhang 	VSOCK_VQ_RX     = 0, /* for host to guest data */
21117d6495STianjia Zhang 	VSOCK_VQ_TX     = 1, /* for guest to host data */
22117d6495STianjia Zhang 	VSOCK_VQ_EVENT  = 2,
23117d6495STianjia Zhang 	VSOCK_VQ_MAX    = 3,
24117d6495STianjia Zhang };
25117d6495STianjia Zhang 
26117d6495STianjia Zhang struct vsock_dev {
27117d6495STianjia Zhang 	struct virt_queue		vqs[VSOCK_VQ_MAX];
28117d6495STianjia Zhang 	struct virtio_vsock_config	config;
29117d6495STianjia Zhang 	u32				features;
30117d6495STianjia Zhang 	int				vhost_fd;
31117d6495STianjia Zhang 	struct virtio_device		vdev;
32117d6495STianjia Zhang 	struct list_head		list;
33117d6495STianjia Zhang 	struct kvm			*kvm;
34117d6495STianjia Zhang 	bool				started;
35117d6495STianjia Zhang };
36117d6495STianjia Zhang 
37117d6495STianjia Zhang static u8 *get_config(struct kvm *kvm, void *dev)
38117d6495STianjia Zhang {
39117d6495STianjia Zhang 	struct vsock_dev *vdev = dev;
40117d6495STianjia Zhang 
41117d6495STianjia Zhang 	return ((u8 *)(&vdev->config));
42117d6495STianjia Zhang }
43117d6495STianjia Zhang 
44e4730284SMartin Radev static size_t get_config_size(struct kvm *kvm, void *dev)
45e4730284SMartin Radev {
46e4730284SMartin Radev 	struct vsock_dev *vdev = dev;
47e4730284SMartin Radev 
48e4730284SMartin Radev 	return sizeof(vdev->config);
49e4730284SMartin Radev }
50e4730284SMartin Radev 
51117d6495STianjia Zhang static u32 get_host_features(struct kvm *kvm, void *dev)
52117d6495STianjia Zhang {
53117d6495STianjia Zhang 	return 1UL << VIRTIO_RING_F_EVENT_IDX
54117d6495STianjia Zhang 		| 1UL << VIRTIO_RING_F_INDIRECT_DESC;
55117d6495STianjia Zhang }
56117d6495STianjia Zhang 
57117d6495STianjia Zhang static void set_guest_features(struct kvm *kvm, void *dev, u32 features)
58117d6495STianjia Zhang {
59117d6495STianjia Zhang 	struct vsock_dev *vdev = dev;
60117d6495STianjia Zhang 
61117d6495STianjia Zhang 	vdev->features = features;
62117d6495STianjia Zhang }
63117d6495STianjia Zhang 
64117d6495STianjia Zhang static bool is_event_vq(u32 vq)
65117d6495STianjia Zhang {
66117d6495STianjia Zhang 	return vq == VSOCK_VQ_EVENT;
67117d6495STianjia Zhang }
68117d6495STianjia Zhang 
69117d6495STianjia Zhang static int init_vq(struct kvm *kvm, void *dev, u32 vq, u32 page_size, u32 align,
70117d6495STianjia Zhang 		   u32 pfn)
71117d6495STianjia Zhang {
72117d6495STianjia Zhang 	struct vhost_vring_state state = { .index = vq };
73117d6495STianjia Zhang 	struct vhost_vring_addr addr;
74117d6495STianjia Zhang 	struct vsock_dev *vdev = dev;
75117d6495STianjia Zhang 	struct virt_queue *queue;
76117d6495STianjia Zhang 	void *p;
77117d6495STianjia Zhang 	int r;
78117d6495STianjia Zhang 
79117d6495STianjia Zhang 	compat__remove_message(compat_id);
80117d6495STianjia Zhang 
81117d6495STianjia Zhang 	queue		= &vdev->vqs[vq];
82117d6495STianjia Zhang 	queue->pfn	= pfn;
83117d6495STianjia Zhang 	p		= virtio_get_vq(kvm, queue->pfn, page_size);
84117d6495STianjia Zhang 
85117d6495STianjia Zhang 	vring_init(&queue->vring, VIRTIO_VSOCK_QUEUE_SIZE, p, align);
86117d6495STianjia Zhang 	virtio_init_device_vq(&vdev->vdev, queue);
87117d6495STianjia Zhang 
88117d6495STianjia Zhang 	if (vdev->vhost_fd == -1)
89117d6495STianjia Zhang 		return 0;
90117d6495STianjia Zhang 
91117d6495STianjia Zhang 	if (is_event_vq(vq))
92117d6495STianjia Zhang 		return 0;
93117d6495STianjia Zhang 
94117d6495STianjia Zhang 	state.num = queue->vring.num;
95117d6495STianjia Zhang 	r = ioctl(vdev->vhost_fd, VHOST_SET_VRING_NUM, &state);
96117d6495STianjia Zhang 	if (r < 0)
97117d6495STianjia Zhang 		die_perror("VHOST_SET_VRING_NUM failed");
98117d6495STianjia Zhang 
99117d6495STianjia Zhang 	state.num = 0;
100117d6495STianjia Zhang 	r = ioctl(vdev->vhost_fd, VHOST_SET_VRING_BASE, &state);
101117d6495STianjia Zhang 	if (r < 0)
102117d6495STianjia Zhang 		die_perror("VHOST_SET_VRING_BASE failed");
103117d6495STianjia Zhang 
104117d6495STianjia Zhang 	addr = (struct vhost_vring_addr) {
105117d6495STianjia Zhang 		.index = vq,
106117d6495STianjia Zhang 		.desc_user_addr = (u64)(unsigned long)queue->vring.desc,
107117d6495STianjia Zhang 		.avail_user_addr = (u64)(unsigned long)queue->vring.avail,
108117d6495STianjia Zhang 		.used_user_addr = (u64)(unsigned long)queue->vring.used,
109117d6495STianjia Zhang 	};
110117d6495STianjia Zhang 
111117d6495STianjia Zhang 	r = ioctl(vdev->vhost_fd, VHOST_SET_VRING_ADDR, &addr);
112117d6495STianjia Zhang 	if (r < 0)
113117d6495STianjia Zhang 		die_perror("VHOST_SET_VRING_ADDR failed");
114117d6495STianjia Zhang 
115117d6495STianjia Zhang 	return 0;
116117d6495STianjia Zhang }
117117d6495STianjia Zhang 
118117d6495STianjia Zhang static void notify_vq_eventfd(struct kvm *kvm, void *dev, u32 vq, u32 efd)
119117d6495STianjia Zhang {
120117d6495STianjia Zhang 	struct vsock_dev *vdev = dev;
121117d6495STianjia Zhang 	struct vhost_vring_file file = {
122117d6495STianjia Zhang 		.index	= vq,
123117d6495STianjia Zhang 		.fd	= efd,
124117d6495STianjia Zhang 	};
125117d6495STianjia Zhang 	int r;
126117d6495STianjia Zhang 
127117d6495STianjia Zhang 	if (is_event_vq(vq))
128117d6495STianjia Zhang 		return;
129117d6495STianjia Zhang 
130117d6495STianjia Zhang 	if (vdev->vhost_fd == -1)
131117d6495STianjia Zhang 		return;
132117d6495STianjia Zhang 
133117d6495STianjia Zhang 	r = ioctl(vdev->vhost_fd, VHOST_SET_VRING_KICK, &file);
134117d6495STianjia Zhang 	if (r < 0)
135117d6495STianjia Zhang 		die_perror("VHOST_SET_VRING_KICK failed");
136117d6495STianjia Zhang }
137117d6495STianjia Zhang 
138117d6495STianjia Zhang static void notify_status(struct kvm *kvm, void *dev, u32 status)
139117d6495STianjia Zhang {
140117d6495STianjia Zhang 	struct vsock_dev *vdev = dev;
141117d6495STianjia Zhang 	int r, start;
142117d6495STianjia Zhang 
143117d6495STianjia Zhang 	start = !!(status & VIRTIO_CONFIG_S_DRIVER_OK);
144117d6495STianjia Zhang 	if (vdev->started == start)
145117d6495STianjia Zhang 		return;
146117d6495STianjia Zhang 
147117d6495STianjia Zhang 	r = ioctl(vdev->vhost_fd, VHOST_VSOCK_SET_RUNNING, &start);
148117d6495STianjia Zhang 	if (r != 0)
149117d6495STianjia Zhang 		die("VHOST_VSOCK_SET_RUNNING failed %d", errno);
150117d6495STianjia Zhang 
151117d6495STianjia Zhang 	vdev->started = start;
152117d6495STianjia Zhang }
153117d6495STianjia Zhang 
154117d6495STianjia Zhang static int notify_vq(struct kvm *kvm, void *dev, u32 vq)
155117d6495STianjia Zhang {
156117d6495STianjia Zhang 	return 0;
157117d6495STianjia Zhang }
158117d6495STianjia Zhang 
159117d6495STianjia Zhang static struct virt_queue *get_vq(struct kvm *kvm, void *dev, u32 vq)
160117d6495STianjia Zhang {
161117d6495STianjia Zhang 	struct vsock_dev *vdev = dev;
162117d6495STianjia Zhang 
163117d6495STianjia Zhang 	return &vdev->vqs[vq];
164117d6495STianjia Zhang }
165117d6495STianjia Zhang 
166117d6495STianjia Zhang static int get_size_vq(struct kvm *kvm, void *dev, u32 vq)
167117d6495STianjia Zhang {
168117d6495STianjia Zhang 	return VIRTIO_VSOCK_QUEUE_SIZE;
169117d6495STianjia Zhang }
170117d6495STianjia Zhang 
171117d6495STianjia Zhang static int set_size_vq(struct kvm *kvm, void *dev, u32 vq, int size)
172117d6495STianjia Zhang {
173117d6495STianjia Zhang 	return size;
174117d6495STianjia Zhang }
175117d6495STianjia Zhang 
176117d6495STianjia Zhang static void notify_vq_gsi(struct kvm *kvm, void *dev, u32 vq, u32 gsi)
177117d6495STianjia Zhang {
178117d6495STianjia Zhang 	struct vhost_vring_file file;
179117d6495STianjia Zhang 	struct vsock_dev *vdev = dev;
180117d6495STianjia Zhang 	struct kvm_irqfd irq;
181117d6495STianjia Zhang 	int r;
182117d6495STianjia Zhang 
183117d6495STianjia Zhang 	if (vdev->vhost_fd == -1)
184117d6495STianjia Zhang 		return;
185117d6495STianjia Zhang 
186117d6495STianjia Zhang 	if (is_event_vq(vq))
187117d6495STianjia Zhang 		return;
188117d6495STianjia Zhang 
189117d6495STianjia Zhang 	irq = (struct kvm_irqfd) {
190117d6495STianjia Zhang 		.gsi	= gsi,
191117d6495STianjia Zhang 		.fd	= eventfd(0, 0),
192117d6495STianjia Zhang 	};
193117d6495STianjia Zhang 	file = (struct vhost_vring_file) {
194117d6495STianjia Zhang 		.index	= vq,
195117d6495STianjia Zhang 		.fd	= irq.fd,
196117d6495STianjia Zhang 	};
197117d6495STianjia Zhang 
198117d6495STianjia Zhang 	r = ioctl(kvm->vm_fd, KVM_IRQFD, &irq);
199117d6495STianjia Zhang 	if (r < 0)
200117d6495STianjia Zhang 		die_perror("KVM_IRQFD failed");
201117d6495STianjia Zhang 
202117d6495STianjia Zhang 	r = ioctl(vdev->vhost_fd, VHOST_SET_VRING_CALL, &file);
203117d6495STianjia Zhang 	if (r < 0)
204117d6495STianjia Zhang 		die_perror("VHOST_SET_VRING_CALL failed");
205117d6495STianjia Zhang }
206117d6495STianjia Zhang 
207*31e0eaccSMartin Radev static unsigned int get_vq_count(struct kvm *kvm, void *dev)
208117d6495STianjia Zhang {
209117d6495STianjia Zhang 	return VSOCK_VQ_MAX;
210117d6495STianjia Zhang }
211117d6495STianjia Zhang 
212117d6495STianjia Zhang static struct virtio_ops vsock_dev_virtio_ops = {
213117d6495STianjia Zhang 	.get_config		= get_config,
214e4730284SMartin Radev 	.get_config_size	= get_config_size,
215117d6495STianjia Zhang 	.get_host_features	= get_host_features,
216117d6495STianjia Zhang 	.set_guest_features	= set_guest_features,
217117d6495STianjia Zhang 	.init_vq		= init_vq,
218117d6495STianjia Zhang 	.get_vq			= get_vq,
219117d6495STianjia Zhang 	.get_size_vq		= get_size_vq,
220117d6495STianjia Zhang 	.set_size_vq		= set_size_vq,
221117d6495STianjia Zhang 	.notify_vq_eventfd	= notify_vq_eventfd,
222117d6495STianjia Zhang 	.notify_status		= notify_status,
223117d6495STianjia Zhang 	.notify_vq_gsi		= notify_vq_gsi,
224117d6495STianjia Zhang 	.notify_vq		= notify_vq,
225117d6495STianjia Zhang 	.get_vq_count		= get_vq_count,
226117d6495STianjia Zhang };
227117d6495STianjia Zhang 
228117d6495STianjia Zhang static void virtio_vhost_vsock_init(struct kvm *kvm, struct vsock_dev *vdev)
229117d6495STianjia Zhang {
230117d6495STianjia Zhang 	struct kvm_mem_bank *bank;
231117d6495STianjia Zhang 	struct vhost_memory *mem;
232117d6495STianjia Zhang 	u64 features;
233117d6495STianjia Zhang 	int r, i;
234117d6495STianjia Zhang 
235117d6495STianjia Zhang 	vdev->vhost_fd = open("/dev/vhost-vsock", O_RDWR);
236117d6495STianjia Zhang 	if (vdev->vhost_fd < 0)
237117d6495STianjia Zhang 		die_perror("Failed opening vhost-vsock device");
238117d6495STianjia Zhang 
239117d6495STianjia Zhang 	mem = calloc(1, sizeof(*mem) + sizeof(struct vhost_memory_region));
240117d6495STianjia Zhang 	if (mem == NULL)
241117d6495STianjia Zhang 		die("Failed allocating memory for vhost memory map");
242117d6495STianjia Zhang 
243117d6495STianjia Zhang 	i = 0;
244117d6495STianjia Zhang 	list_for_each_entry(bank, &kvm->mem_banks, list) {
245117d6495STianjia Zhang 		mem->regions[i] = (struct vhost_memory_region) {
246117d6495STianjia Zhang 			.guest_phys_addr = bank->guest_phys_addr,
247117d6495STianjia Zhang 			.memory_size	 = bank->size,
248117d6495STianjia Zhang 			.userspace_addr	 = (unsigned long)bank->host_addr,
249117d6495STianjia Zhang 		};
250117d6495STianjia Zhang 		i++;
251117d6495STianjia Zhang 	}
252117d6495STianjia Zhang 	mem->nregions = i;
253117d6495STianjia Zhang 
254117d6495STianjia Zhang 	r = ioctl(vdev->vhost_fd, VHOST_SET_OWNER);
255117d6495STianjia Zhang 	if (r != 0)
256117d6495STianjia Zhang 		die_perror("VHOST_SET_OWNER failed");
257117d6495STianjia Zhang 
258117d6495STianjia Zhang 	r = ioctl(vdev->vhost_fd, VHOST_SET_MEM_TABLE, mem);
259117d6495STianjia Zhang 	if (r != 0)
260117d6495STianjia Zhang 		die_perror("VHOST_SET_MEM_TABLE failed");
261117d6495STianjia Zhang 
262117d6495STianjia Zhang 	r = ioctl(vdev->vhost_fd, VHOST_GET_FEATURES, &features);
263117d6495STianjia Zhang 	if (r != 0)
264117d6495STianjia Zhang 		die_perror("VHOST_GET_FEATURES failed");
265117d6495STianjia Zhang 
266117d6495STianjia Zhang 	r = ioctl(vdev->vhost_fd, VHOST_SET_FEATURES, &features);
267117d6495STianjia Zhang 	if (r != 0)
268117d6495STianjia Zhang 		die_perror("VHOST_SET_FEATURES failed");
269117d6495STianjia Zhang 
270117d6495STianjia Zhang 	r = ioctl(vdev->vhost_fd, VHOST_VSOCK_SET_GUEST_CID, &vdev->config.guest_cid);
271117d6495STianjia Zhang 	if (r != 0)
272117d6495STianjia Zhang 		die_perror("VHOST_VSOCK_SET_GUEST_CID failed");
273117d6495STianjia Zhang 
274117d6495STianjia Zhang 	vdev->vdev.use_vhost = true;
275117d6495STianjia Zhang 
276117d6495STianjia Zhang 	free(mem);
277117d6495STianjia Zhang }
278117d6495STianjia Zhang 
279117d6495STianjia Zhang static int virtio_vsock_init_one(struct kvm *kvm, u64 guest_cid)
280117d6495STianjia Zhang {
281117d6495STianjia Zhang 	struct vsock_dev *vdev;
282117d6495STianjia Zhang 	int r;
283117d6495STianjia Zhang 
284117d6495STianjia Zhang 	vdev = calloc(1, sizeof(struct vsock_dev));
285117d6495STianjia Zhang 	if (vdev == NULL)
286117d6495STianjia Zhang 		return -ENOMEM;
287117d6495STianjia Zhang 
288117d6495STianjia Zhang 	*vdev = (struct vsock_dev) {
289117d6495STianjia Zhang 		.config	= (struct virtio_vsock_config) {
290117d6495STianjia Zhang 			.guest_cid	= guest_cid,
291117d6495STianjia Zhang 		},
292117d6495STianjia Zhang 		.vhost_fd		= -1,
293117d6495STianjia Zhang 		.kvm			= kvm,
294117d6495STianjia Zhang 	};
295117d6495STianjia Zhang 
296117d6495STianjia Zhang 	list_add_tail(&vdev->list, &vdevs);
297117d6495STianjia Zhang 
298117d6495STianjia Zhang 	r = virtio_init(kvm, vdev, &vdev->vdev, &vsock_dev_virtio_ops,
299117d6495STianjia Zhang 		    VIRTIO_DEFAULT_TRANS(kvm), PCI_DEVICE_ID_VIRTIO_VSOCK,
300117d6495STianjia Zhang 		    VIRTIO_ID_VSOCK, PCI_CLASS_VSOCK);
301117d6495STianjia Zhang 	if (r < 0)
302117d6495STianjia Zhang 	    return r;
303117d6495STianjia Zhang 
304117d6495STianjia Zhang 	virtio_vhost_vsock_init(kvm, vdev);
305117d6495STianjia Zhang 
306117d6495STianjia Zhang 	if (compat_id == -1)
307117d6495STianjia Zhang 		compat_id = virtio_compat_add_message("virtio-vsock", "CONFIG_VIRTIO_VSOCK");
308117d6495STianjia Zhang 
309117d6495STianjia Zhang 	return 0;
310117d6495STianjia Zhang }
311117d6495STianjia Zhang 
312117d6495STianjia Zhang static int virtio_vsock_exit_one(struct kvm *kvm, struct vsock_dev *vdev)
313117d6495STianjia Zhang {
314117d6495STianjia Zhang 	list_del(&vdev->list);
315117d6495STianjia Zhang 	free(vdev);
316117d6495STianjia Zhang 
317117d6495STianjia Zhang 	return 0;
318117d6495STianjia Zhang }
319117d6495STianjia Zhang 
320117d6495STianjia Zhang int virtio_vsock_init(struct kvm *kvm)
321117d6495STianjia Zhang {
322117d6495STianjia Zhang 	int r;
323117d6495STianjia Zhang 
324117d6495STianjia Zhang 	if (kvm->cfg.vsock_cid == 0)
325117d6495STianjia Zhang 		return 0;
326117d6495STianjia Zhang 
327117d6495STianjia Zhang 	r = virtio_vsock_init_one(kvm, kvm->cfg.vsock_cid);
328117d6495STianjia Zhang 	if (r < 0)
329117d6495STianjia Zhang 		goto cleanup;
330117d6495STianjia Zhang 
331117d6495STianjia Zhang 	return 0;
332117d6495STianjia Zhang cleanup:
333117d6495STianjia Zhang 	return virtio_vsock_exit(kvm);
334117d6495STianjia Zhang }
335117d6495STianjia Zhang virtio_dev_init(virtio_vsock_init);
336117d6495STianjia Zhang 
337117d6495STianjia Zhang int virtio_vsock_exit(struct kvm *kvm)
338117d6495STianjia Zhang {
339117d6495STianjia Zhang 	while (!list_empty(&vdevs)) {
340117d6495STianjia Zhang 		struct vsock_dev *vdev;
341117d6495STianjia Zhang 
342117d6495STianjia Zhang 		vdev = list_first_entry(&vdevs, struct vsock_dev, list);
343117d6495STianjia Zhang 		virtio_vsock_exit_one(kvm, vdev);
344117d6495STianjia Zhang 	}
345117d6495STianjia Zhang 
346117d6495STianjia Zhang 	return 0;
347117d6495STianjia Zhang }
348117d6495STianjia Zhang virtio_dev_exit(virtio_vsock_exit);
349