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
get_config(struct kvm * kvm,void * dev)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
get_config_size(struct kvm * kvm,void * dev)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
get_host_features(struct kvm * kvm,void * dev)523c8f82b8SJean-Philippe Brucker static u64 get_host_features(struct kvm *kvm, void *dev)
53117d6495STianjia Zhang {
54cf8358d3SJean-Philippe Brucker int r;
55cf8358d3SJean-Philippe Brucker u64 features;
56cf8358d3SJean-Philippe Brucker struct vsock_dev *vdev = dev;
57cf8358d3SJean-Philippe Brucker
58cf8358d3SJean-Philippe Brucker r = ioctl(vdev->vhost_fd, VHOST_GET_FEATURES, &features);
59cf8358d3SJean-Philippe Brucker if (r != 0)
60cf8358d3SJean-Philippe Brucker die_perror("VHOST_GET_FEATURES failed");
61cf8358d3SJean-Philippe Brucker
62cf8358d3SJean-Philippe Brucker return features &
63cf8358d3SJean-Philippe Brucker (1ULL << VIRTIO_RING_F_EVENT_IDX |
64cf8358d3SJean-Philippe Brucker 1ULL << VIRTIO_RING_F_INDIRECT_DESC);
65117d6495STianjia Zhang }
66117d6495STianjia Zhang
is_event_vq(u32 vq)67117d6495STianjia Zhang static bool is_event_vq(u32 vq)
68117d6495STianjia Zhang {
69117d6495STianjia Zhang return vq == VSOCK_VQ_EVENT;
70117d6495STianjia Zhang }
71117d6495STianjia Zhang
init_vq(struct kvm * kvm,void * dev,u32 vq)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
notify_vq_eventfd(struct kvm * kvm,void * dev,u32 vq,u32 efd)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
notify_status(struct kvm * kvm,void * dev,u32 status)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
107cf8358d3SJean-Philippe Brucker if (status & VIRTIO__STATUS_START) {
108a8e397bbSJean-Philippe Brucker start = 1;
109cf8358d3SJean-Philippe Brucker
110*3b1cdcf9SJean-Philippe Brucker r = virtio_vhost_set_features(vdev->vhost_fd,
111*3b1cdcf9SJean-Philippe Brucker vdev->vdev.features);
112cf8358d3SJean-Philippe Brucker if (r != 0)
113cf8358d3SJean-Philippe Brucker die_perror("VHOST_SET_FEATURES failed");
114cf8358d3SJean-Philippe Brucker } else if (status & VIRTIO__STATUS_STOP) {
115a8e397bbSJean-Philippe Brucker start = 0;
116cf8358d3SJean-Philippe Brucker } else {
117117d6495STianjia Zhang return;
118cf8358d3SJean-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
notify_vq(struct kvm * kvm,void * dev,u32 vq)125117d6495STianjia Zhang static int notify_vq(struct kvm *kvm, void *dev, u32 vq)
126117d6495STianjia Zhang {
127117d6495STianjia Zhang return 0;
128117d6495STianjia Zhang }
129117d6495STianjia Zhang
get_vq(struct kvm * kvm,void * dev,u32 vq)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
get_size_vq(struct kvm * kvm,void * dev,u32 vq)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
set_size_vq(struct kvm * kvm,void * dev,u32 vq,int size)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
notify_vq_gsi(struct kvm * kvm,void * dev,u32 vq,u32 gsi)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
15446aaf3b8SJean-Philippe Brucker virtio_vhost_set_vring_irqfd(kvm, gsi, &vdev->vqs[vq]);
155117d6495STianjia Zhang }
156117d6495STianjia Zhang
get_vq_count(struct kvm * kvm,void * dev)15731e0eaccSMartin Radev static unsigned int get_vq_count(struct kvm *kvm, void *dev)
158117d6495STianjia Zhang {
159117d6495STianjia Zhang return VSOCK_VQ_MAX;
160117d6495STianjia Zhang }
161117d6495STianjia Zhang
162117d6495STianjia Zhang static struct virtio_ops vsock_dev_virtio_ops = {
163117d6495STianjia Zhang .get_config = get_config,
164e4730284SMartin Radev .get_config_size = get_config_size,
165117d6495STianjia Zhang .get_host_features = get_host_features,
166117d6495STianjia Zhang .init_vq = init_vq,
167117d6495STianjia Zhang .get_vq = get_vq,
168117d6495STianjia Zhang .get_size_vq = get_size_vq,
169117d6495STianjia Zhang .set_size_vq = set_size_vq,
170117d6495STianjia Zhang .notify_vq_eventfd = notify_vq_eventfd,
171117d6495STianjia Zhang .notify_status = notify_status,
172117d6495STianjia Zhang .notify_vq_gsi = notify_vq_gsi,
173117d6495STianjia Zhang .notify_vq = notify_vq,
174117d6495STianjia Zhang .get_vq_count = get_vq_count,
175117d6495STianjia Zhang };
176117d6495STianjia Zhang
virtio_vhost_vsock_init(struct kvm * kvm,struct vsock_dev * vdev)177117d6495STianjia Zhang static void virtio_vhost_vsock_init(struct kvm *kvm, struct vsock_dev *vdev)
178117d6495STianjia Zhang {
179f84ab9ebSJean-Philippe Brucker int r;
180117d6495STianjia Zhang
181117d6495STianjia Zhang vdev->vhost_fd = open("/dev/vhost-vsock", O_RDWR);
182117d6495STianjia Zhang if (vdev->vhost_fd < 0)
183117d6495STianjia Zhang die_perror("Failed opening vhost-vsock device");
184117d6495STianjia Zhang
185f84ab9ebSJean-Philippe Brucker virtio_vhost_init(kvm, vdev->vhost_fd);
186117d6495STianjia Zhang
187867b15ccSJean-Philippe Brucker r = ioctl(vdev->vhost_fd, VHOST_VSOCK_SET_GUEST_CID, &vdev->guest_cid);
188117d6495STianjia Zhang if (r != 0)
189117d6495STianjia Zhang die_perror("VHOST_VSOCK_SET_GUEST_CID failed");
190117d6495STianjia Zhang
191117d6495STianjia Zhang vdev->vdev.use_vhost = true;
192117d6495STianjia Zhang }
193117d6495STianjia Zhang
virtio_vsock_init_one(struct kvm * kvm,u64 guest_cid)194117d6495STianjia Zhang static int virtio_vsock_init_one(struct kvm *kvm, u64 guest_cid)
195117d6495STianjia Zhang {
196117d6495STianjia Zhang struct vsock_dev *vdev;
197117d6495STianjia Zhang int r;
198117d6495STianjia Zhang
199117d6495STianjia Zhang vdev = calloc(1, sizeof(struct vsock_dev));
200117d6495STianjia Zhang if (vdev == NULL)
201117d6495STianjia Zhang return -ENOMEM;
202117d6495STianjia Zhang
203117d6495STianjia Zhang *vdev = (struct vsock_dev) {
204117d6495STianjia Zhang .guest_cid = guest_cid,
205117d6495STianjia Zhang .vhost_fd = -1,
206117d6495STianjia Zhang .kvm = kvm,
207117d6495STianjia Zhang };
208117d6495STianjia Zhang
209117d6495STianjia Zhang list_add_tail(&vdev->list, &vdevs);
210117d6495STianjia Zhang
211117d6495STianjia Zhang r = virtio_init(kvm, vdev, &vdev->vdev, &vsock_dev_virtio_ops,
2129b46ebc5SRajnesh Kanwal kvm->cfg.virtio_transport, PCI_DEVICE_ID_VIRTIO_VSOCK,
213117d6495STianjia Zhang VIRTIO_ID_VSOCK, PCI_CLASS_VSOCK);
214117d6495STianjia Zhang if (r < 0)
215117d6495STianjia Zhang return r;
216117d6495STianjia Zhang
217117d6495STianjia Zhang virtio_vhost_vsock_init(kvm, vdev);
218117d6495STianjia Zhang
219117d6495STianjia Zhang if (compat_id == -1)
22033e026a7SJean-Philippe Brucker compat_id = virtio_compat_add_message("virtio-vsock", "CONFIG_VIRTIO_VSOCKETS");
221117d6495STianjia Zhang
222117d6495STianjia Zhang return 0;
223117d6495STianjia Zhang }
224117d6495STianjia Zhang
virtio_vsock_exit_one(struct kvm * kvm,struct vsock_dev * vdev)225117d6495STianjia Zhang static int virtio_vsock_exit_one(struct kvm *kvm, struct vsock_dev *vdev)
226117d6495STianjia Zhang {
227117d6495STianjia Zhang list_del(&vdev->list);
228117d6495STianjia Zhang free(vdev);
229117d6495STianjia Zhang
230117d6495STianjia Zhang return 0;
231117d6495STianjia Zhang }
232117d6495STianjia Zhang
virtio_vsock_init(struct kvm * kvm)233117d6495STianjia Zhang int virtio_vsock_init(struct kvm *kvm)
234117d6495STianjia Zhang {
235117d6495STianjia Zhang int r;
236117d6495STianjia Zhang
237117d6495STianjia Zhang if (kvm->cfg.vsock_cid == 0)
238117d6495STianjia Zhang return 0;
239117d6495STianjia Zhang
240117d6495STianjia Zhang r = virtio_vsock_init_one(kvm, kvm->cfg.vsock_cid);
241117d6495STianjia Zhang if (r < 0)
242117d6495STianjia Zhang goto cleanup;
243117d6495STianjia Zhang
244117d6495STianjia Zhang return 0;
245117d6495STianjia Zhang cleanup:
246117d6495STianjia Zhang return virtio_vsock_exit(kvm);
247117d6495STianjia Zhang }
248117d6495STianjia Zhang virtio_dev_init(virtio_vsock_init);
249117d6495STianjia Zhang
virtio_vsock_exit(struct kvm * kvm)250117d6495STianjia Zhang int virtio_vsock_exit(struct kvm *kvm)
251117d6495STianjia Zhang {
252117d6495STianjia Zhang while (!list_empty(&vdevs)) {
253117d6495STianjia Zhang struct vsock_dev *vdev;
254117d6495STianjia Zhang
255117d6495STianjia Zhang vdev = list_first_entry(&vdevs, struct vsock_dev, list);
256117d6495STianjia Zhang virtio_vsock_exit_one(kvm, vdev);
257117d6495STianjia Zhang }
258117d6495STianjia Zhang
259117d6495STianjia Zhang return 0;
260117d6495STianjia Zhang }
261117d6495STianjia Zhang virtio_dev_exit(virtio_vsock_exit);
262