xref: /kvmtool/virtio/vsock.c (revision cf8358d34f3157e1a891ad00a9959870b65a1861)
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 
10867b15ccSJean-Philippe Brucker #include <linux/byteorder.h>
11117d6495STianjia Zhang #include <linux/kernel.h>
12117d6495STianjia Zhang #include <linux/virtio_vsock.h>
13117d6495STianjia Zhang #include <linux/vhost.h>
14117d6495STianjia Zhang 
15117d6495STianjia Zhang #define VIRTIO_VSOCK_QUEUE_SIZE		128
16117d6495STianjia Zhang 
17117d6495STianjia Zhang static LIST_HEAD(vdevs);
18117d6495STianjia Zhang static int compat_id = -1;
19117d6495STianjia Zhang 
20117d6495STianjia Zhang enum {
21117d6495STianjia Zhang 	VSOCK_VQ_RX     = 0, /* for host to guest data */
22117d6495STianjia Zhang 	VSOCK_VQ_TX     = 1, /* for guest to host data */
23117d6495STianjia Zhang 	VSOCK_VQ_EVENT  = 2,
24117d6495STianjia Zhang 	VSOCK_VQ_MAX    = 3,
25117d6495STianjia Zhang };
26117d6495STianjia Zhang 
27117d6495STianjia Zhang struct vsock_dev {
28117d6495STianjia Zhang 	struct virt_queue		vqs[VSOCK_VQ_MAX];
29117d6495STianjia Zhang 	struct virtio_vsock_config	config;
30867b15ccSJean-Philippe Brucker 	u64				guest_cid;
31117d6495STianjia Zhang 	u32				features;
32117d6495STianjia Zhang 	int				vhost_fd;
33117d6495STianjia Zhang 	struct virtio_device		vdev;
34117d6495STianjia Zhang 	struct list_head		list;
35117d6495STianjia Zhang 	struct kvm			*kvm;
36117d6495STianjia Zhang };
37117d6495STianjia Zhang 
38117d6495STianjia Zhang static u8 *get_config(struct kvm *kvm, void *dev)
39117d6495STianjia Zhang {
40117d6495STianjia Zhang 	struct vsock_dev *vdev = dev;
41117d6495STianjia Zhang 
42117d6495STianjia Zhang 	return ((u8 *)(&vdev->config));
43117d6495STianjia Zhang }
44117d6495STianjia Zhang 
45e4730284SMartin Radev static size_t get_config_size(struct kvm *kvm, void *dev)
46e4730284SMartin Radev {
47e4730284SMartin Radev 	struct vsock_dev *vdev = dev;
48e4730284SMartin Radev 
49e4730284SMartin Radev 	return sizeof(vdev->config);
50e4730284SMartin Radev }
51e4730284SMartin Radev 
523c8f82b8SJean-Philippe Brucker static u64 get_host_features(struct kvm *kvm, void *dev)
53117d6495STianjia Zhang {
54*cf8358d3SJean-Philippe Brucker 	int r;
55*cf8358d3SJean-Philippe Brucker 	u64 features;
56*cf8358d3SJean-Philippe Brucker 	struct vsock_dev *vdev = dev;
57*cf8358d3SJean-Philippe Brucker 
58*cf8358d3SJean-Philippe Brucker 	r = ioctl(vdev->vhost_fd, VHOST_GET_FEATURES, &features);
59*cf8358d3SJean-Philippe Brucker 	if (r != 0)
60*cf8358d3SJean-Philippe Brucker 		die_perror("VHOST_GET_FEATURES failed");
61*cf8358d3SJean-Philippe Brucker 
62*cf8358d3SJean-Philippe Brucker 	return features &
63*cf8358d3SJean-Philippe Brucker 		(1ULL << VIRTIO_RING_F_EVENT_IDX |
64*cf8358d3SJean-Philippe Brucker 		 1ULL << VIRTIO_RING_F_INDIRECT_DESC);
65117d6495STianjia Zhang }
66117d6495STianjia Zhang 
67117d6495STianjia Zhang static bool is_event_vq(u32 vq)
68117d6495STianjia Zhang {
69117d6495STianjia Zhang 	return vq == VSOCK_VQ_EVENT;
70117d6495STianjia Zhang }
71117d6495STianjia Zhang 
72609ee906SJean-Philippe Brucker static int init_vq(struct kvm *kvm, void *dev, u32 vq)
73117d6495STianjia Zhang {
74117d6495STianjia Zhang 	struct vsock_dev *vdev = dev;
75117d6495STianjia Zhang 	struct virt_queue *queue;
76117d6495STianjia Zhang 
77117d6495STianjia Zhang 	compat__remove_message(compat_id);
78117d6495STianjia Zhang 
79117d6495STianjia Zhang 	queue		= &vdev->vqs[vq];
80609ee906SJean-Philippe Brucker 	virtio_init_device_vq(kvm, &vdev->vdev, queue, VIRTIO_VSOCK_QUEUE_SIZE);
81117d6495STianjia Zhang 
82745221e5SJean-Philippe Brucker 	if (vdev->vhost_fd == -1 || is_event_vq(vq))
83117d6495STianjia Zhang 		return 0;
84117d6495STianjia Zhang 
85745221e5SJean-Philippe Brucker 	virtio_vhost_set_vring(kvm, vdev->vhost_fd, vq, queue);
86117d6495STianjia Zhang 	return 0;
87117d6495STianjia Zhang }
88117d6495STianjia Zhang 
89117d6495STianjia Zhang static void notify_vq_eventfd(struct kvm *kvm, void *dev, u32 vq, u32 efd)
90117d6495STianjia Zhang {
91117d6495STianjia Zhang 	struct vsock_dev *vdev = dev;
92117d6495STianjia Zhang 
93676c0c8aSJean-Philippe Brucker 	if (vdev->vhost_fd == -1 || is_event_vq(vq))
94117d6495STianjia Zhang 		return;
95117d6495STianjia Zhang 
96676c0c8aSJean-Philippe Brucker 	virtio_vhost_set_vring_kick(kvm, vdev->vhost_fd, vq, efd);
97117d6495STianjia Zhang }
98117d6495STianjia Zhang 
99117d6495STianjia Zhang static void notify_status(struct kvm *kvm, void *dev, u32 status)
100117d6495STianjia Zhang {
101117d6495STianjia Zhang 	struct vsock_dev *vdev = dev;
102117d6495STianjia Zhang 	int r, start;
103117d6495STianjia Zhang 
104867b15ccSJean-Philippe Brucker 	if (status & VIRTIO__STATUS_CONFIG)
105867b15ccSJean-Philippe Brucker 		vdev->config.guest_cid = cpu_to_le64(vdev->guest_cid);
106867b15ccSJean-Philippe Brucker 
107*cf8358d3SJean-Philippe Brucker 	if (status & VIRTIO__STATUS_START) {
108a8e397bbSJean-Philippe Brucker 		start = 1;
109*cf8358d3SJean-Philippe Brucker 
110*cf8358d3SJean-Philippe Brucker 		r = ioctl(vdev->vhost_fd, VHOST_SET_FEATURES,
111*cf8358d3SJean-Philippe Brucker 			  &vdev->vdev.features);
112*cf8358d3SJean-Philippe Brucker 		if (r != 0)
113*cf8358d3SJean-Philippe Brucker 			die_perror("VHOST_SET_FEATURES failed");
114*cf8358d3SJean-Philippe Brucker 	} else if (status & VIRTIO__STATUS_STOP) {
115a8e397bbSJean-Philippe Brucker 		start = 0;
116*cf8358d3SJean-Philippe Brucker 	} else {
117117d6495STianjia Zhang 		return;
118*cf8358d3SJean-Philippe Brucker 	}
119117d6495STianjia Zhang 
120117d6495STianjia Zhang 	r = ioctl(vdev->vhost_fd, VHOST_VSOCK_SET_RUNNING, &start);
121117d6495STianjia Zhang 	if (r != 0)
122117d6495STianjia Zhang 		die("VHOST_VSOCK_SET_RUNNING failed %d", errno);
123117d6495STianjia Zhang }
124117d6495STianjia Zhang 
125117d6495STianjia Zhang static int notify_vq(struct kvm *kvm, void *dev, u32 vq)
126117d6495STianjia Zhang {
127117d6495STianjia Zhang 	return 0;
128117d6495STianjia Zhang }
129117d6495STianjia Zhang 
130117d6495STianjia Zhang static struct virt_queue *get_vq(struct kvm *kvm, void *dev, u32 vq)
131117d6495STianjia Zhang {
132117d6495STianjia Zhang 	struct vsock_dev *vdev = dev;
133117d6495STianjia Zhang 
134117d6495STianjia Zhang 	return &vdev->vqs[vq];
135117d6495STianjia Zhang }
136117d6495STianjia Zhang 
137117d6495STianjia Zhang static int get_size_vq(struct kvm *kvm, void *dev, u32 vq)
138117d6495STianjia Zhang {
139117d6495STianjia Zhang 	return VIRTIO_VSOCK_QUEUE_SIZE;
140117d6495STianjia Zhang }
141117d6495STianjia Zhang 
142117d6495STianjia Zhang static int set_size_vq(struct kvm *kvm, void *dev, u32 vq, int size)
143117d6495STianjia Zhang {
144117d6495STianjia Zhang 	return size;
145117d6495STianjia Zhang }
146117d6495STianjia Zhang 
147117d6495STianjia Zhang static void notify_vq_gsi(struct kvm *kvm, void *dev, u32 vq, u32 gsi)
148117d6495STianjia Zhang {
149117d6495STianjia Zhang 	struct vsock_dev *vdev = dev;
150117d6495STianjia Zhang 
151029cd2bbSJean-Philippe Brucker 	if (vdev->vhost_fd == -1 || is_event_vq(vq))
152117d6495STianjia Zhang 		return;
153117d6495STianjia Zhang 
154029cd2bbSJean-Philippe Brucker 	virtio_vhost_set_vring_call(kvm, vdev->vhost_fd, vq, gsi,
155029cd2bbSJean-Philippe Brucker 				    &vdev->vqs[vq]);
156117d6495STianjia Zhang }
157117d6495STianjia Zhang 
15831e0eaccSMartin Radev static unsigned int get_vq_count(struct kvm *kvm, void *dev)
159117d6495STianjia Zhang {
160117d6495STianjia Zhang 	return VSOCK_VQ_MAX;
161117d6495STianjia Zhang }
162117d6495STianjia Zhang 
163117d6495STianjia Zhang static struct virtio_ops vsock_dev_virtio_ops = {
164117d6495STianjia Zhang 	.get_config		= get_config,
165e4730284SMartin Radev 	.get_config_size	= get_config_size,
166117d6495STianjia Zhang 	.get_host_features	= get_host_features,
167117d6495STianjia Zhang 	.init_vq		= init_vq,
168117d6495STianjia Zhang 	.get_vq			= get_vq,
169117d6495STianjia Zhang 	.get_size_vq		= get_size_vq,
170117d6495STianjia Zhang 	.set_size_vq		= set_size_vq,
171117d6495STianjia Zhang 	.notify_vq_eventfd	= notify_vq_eventfd,
172117d6495STianjia Zhang 	.notify_status		= notify_status,
173117d6495STianjia Zhang 	.notify_vq_gsi		= notify_vq_gsi,
174117d6495STianjia Zhang 	.notify_vq		= notify_vq,
175117d6495STianjia Zhang 	.get_vq_count		= get_vq_count,
176117d6495STianjia Zhang };
177117d6495STianjia Zhang 
178117d6495STianjia Zhang static void virtio_vhost_vsock_init(struct kvm *kvm, struct vsock_dev *vdev)
179117d6495STianjia Zhang {
180f84ab9ebSJean-Philippe Brucker 	int r;
181117d6495STianjia Zhang 
182117d6495STianjia Zhang 	vdev->vhost_fd = open("/dev/vhost-vsock", O_RDWR);
183117d6495STianjia Zhang 	if (vdev->vhost_fd < 0)
184117d6495STianjia Zhang 		die_perror("Failed opening vhost-vsock device");
185117d6495STianjia Zhang 
186f84ab9ebSJean-Philippe Brucker 	virtio_vhost_init(kvm, vdev->vhost_fd);
187117d6495STianjia Zhang 
188867b15ccSJean-Philippe Brucker 	r = ioctl(vdev->vhost_fd, VHOST_VSOCK_SET_GUEST_CID, &vdev->guest_cid);
189117d6495STianjia Zhang 	if (r != 0)
190117d6495STianjia Zhang 		die_perror("VHOST_VSOCK_SET_GUEST_CID failed");
191117d6495STianjia Zhang 
192117d6495STianjia Zhang 	vdev->vdev.use_vhost = true;
193117d6495STianjia Zhang }
194117d6495STianjia Zhang 
195117d6495STianjia Zhang static int virtio_vsock_init_one(struct kvm *kvm, u64 guest_cid)
196117d6495STianjia Zhang {
197117d6495STianjia Zhang 	struct vsock_dev *vdev;
198117d6495STianjia Zhang 	int r;
199117d6495STianjia Zhang 
200117d6495STianjia Zhang 	vdev = calloc(1, sizeof(struct vsock_dev));
201117d6495STianjia Zhang 	if (vdev == NULL)
202117d6495STianjia Zhang 		return -ENOMEM;
203117d6495STianjia Zhang 
204117d6495STianjia Zhang 	*vdev = (struct vsock_dev) {
205117d6495STianjia Zhang 		.guest_cid		= guest_cid,
206117d6495STianjia Zhang 		.vhost_fd		= -1,
207117d6495STianjia Zhang 		.kvm			= kvm,
208117d6495STianjia Zhang 	};
209117d6495STianjia Zhang 
210117d6495STianjia Zhang 	list_add_tail(&vdev->list, &vdevs);
211117d6495STianjia Zhang 
212117d6495STianjia Zhang 	r = virtio_init(kvm, vdev, &vdev->vdev, &vsock_dev_virtio_ops,
2139b46ebc5SRajnesh Kanwal 		    kvm->cfg.virtio_transport, PCI_DEVICE_ID_VIRTIO_VSOCK,
214117d6495STianjia Zhang 		    VIRTIO_ID_VSOCK, PCI_CLASS_VSOCK);
215117d6495STianjia Zhang 	if (r < 0)
216117d6495STianjia Zhang 	    return r;
217117d6495STianjia Zhang 
218117d6495STianjia Zhang 	virtio_vhost_vsock_init(kvm, vdev);
219117d6495STianjia Zhang 
220117d6495STianjia Zhang 	if (compat_id == -1)
221117d6495STianjia Zhang 		compat_id = virtio_compat_add_message("virtio-vsock", "CONFIG_VIRTIO_VSOCK");
222117d6495STianjia Zhang 
223117d6495STianjia Zhang 	return 0;
224117d6495STianjia Zhang }
225117d6495STianjia Zhang 
226117d6495STianjia Zhang static int virtio_vsock_exit_one(struct kvm *kvm, struct vsock_dev *vdev)
227117d6495STianjia Zhang {
228117d6495STianjia Zhang 	list_del(&vdev->list);
229117d6495STianjia Zhang 	free(vdev);
230117d6495STianjia Zhang 
231117d6495STianjia Zhang 	return 0;
232117d6495STianjia Zhang }
233117d6495STianjia Zhang 
234117d6495STianjia Zhang int virtio_vsock_init(struct kvm *kvm)
235117d6495STianjia Zhang {
236117d6495STianjia Zhang 	int r;
237117d6495STianjia Zhang 
238117d6495STianjia Zhang 	if (kvm->cfg.vsock_cid == 0)
239117d6495STianjia Zhang 		return 0;
240117d6495STianjia Zhang 
241117d6495STianjia Zhang 	r = virtio_vsock_init_one(kvm, kvm->cfg.vsock_cid);
242117d6495STianjia Zhang 	if (r < 0)
243117d6495STianjia Zhang 		goto cleanup;
244117d6495STianjia Zhang 
245117d6495STianjia Zhang 	return 0;
246117d6495STianjia Zhang cleanup:
247117d6495STianjia Zhang 	return virtio_vsock_exit(kvm);
248117d6495STianjia Zhang }
249117d6495STianjia Zhang virtio_dev_init(virtio_vsock_init);
250117d6495STianjia Zhang 
251117d6495STianjia Zhang int virtio_vsock_exit(struct kvm *kvm)
252117d6495STianjia Zhang {
253117d6495STianjia Zhang 	while (!list_empty(&vdevs)) {
254117d6495STianjia Zhang 		struct vsock_dev *vdev;
255117d6495STianjia Zhang 
256117d6495STianjia Zhang 		vdev = list_first_entry(&vdevs, struct vsock_dev, list);
257117d6495STianjia Zhang 		virtio_vsock_exit_one(kvm, vdev);
258117d6495STianjia Zhang 	}
259117d6495STianjia Zhang 
260117d6495STianjia Zhang 	return 0;
261117d6495STianjia Zhang }
262117d6495STianjia Zhang virtio_dev_exit(virtio_vsock_exit);
263