1 #include "kvm/irq.h"
2 #include "kvm/virtio.h"
3 #include "kvm/epoll.h"
4
5 #include <linux/kvm.h>
6 #include <linux/vhost.h>
7 #include <linux/list.h>
8
9 #include <sys/eventfd.h>
10
11 static struct kvm__epoll epoll;
12
virtio_vhost_signal_vq(struct kvm * kvm,struct epoll_event * ev)13 static void virtio_vhost_signal_vq(struct kvm *kvm, struct epoll_event *ev)
14 {
15 int r;
16 u64 tmp;
17 struct virt_queue *queue = ev->data.ptr;
18
19 if (read(queue->irqfd, &tmp, sizeof(tmp)) < 0)
20 pr_warning("%s: failed to read eventfd", __func__);
21
22 r = queue->vdev->ops->signal_vq(kvm, queue->vdev, queue->index);
23 if (r)
24 pr_warning("%s failed to signal virtqueue", __func__);
25 }
26
virtio_vhost_start_poll(struct kvm * kvm)27 static int virtio_vhost_start_poll(struct kvm *kvm)
28 {
29 if (epoll.fd)
30 return 0;
31
32 if (epoll__init(kvm, &epoll, "vhost-irq-worker",
33 virtio_vhost_signal_vq))
34 return -1;
35
36 return 0;
37 }
38
virtio_vhost_stop_poll(struct kvm * kvm)39 static int virtio_vhost_stop_poll(struct kvm *kvm)
40 {
41 if (epoll.fd)
42 epoll__exit(&epoll);
43 return 0;
44 }
45 base_exit(virtio_vhost_stop_poll);
46
virtio_vhost_init(struct kvm * kvm,int vhost_fd)47 void virtio_vhost_init(struct kvm *kvm, int vhost_fd)
48 {
49 struct kvm_mem_bank *bank;
50 struct vhost_memory *mem;
51 int i = 0, r;
52
53 r = virtio_vhost_start_poll(kvm);
54 if (r)
55 die("Unable to start vhost polling thread\n");
56
57 mem = calloc(1, sizeof(*mem) +
58 kvm->mem_slots * sizeof(struct vhost_memory_region));
59 if (mem == NULL)
60 die("Failed allocating memory for vhost memory map");
61
62 list_for_each_entry(bank, &kvm->mem_banks, list) {
63 mem->regions[i] = (struct vhost_memory_region) {
64 .guest_phys_addr = bank->guest_phys_addr,
65 .memory_size = bank->size,
66 .userspace_addr = (unsigned long)bank->host_addr,
67 };
68 i++;
69 }
70 mem->nregions = i;
71
72 r = ioctl(vhost_fd, VHOST_SET_OWNER);
73 if (r != 0)
74 die_perror("VHOST_SET_OWNER failed");
75
76 r = ioctl(vhost_fd, VHOST_SET_MEM_TABLE, mem);
77 if (r != 0)
78 die_perror("VHOST_SET_MEM_TABLE failed");
79
80 free(mem);
81 }
82
virtio_vhost_get_irqfd(struct virt_queue * queue)83 static int virtio_vhost_get_irqfd(struct virt_queue *queue)
84 {
85 if (!queue->irqfd) {
86 queue->irqfd = eventfd(0, 0);
87 if (queue->irqfd < 0)
88 die_perror("eventfd()");
89 }
90 return queue->irqfd;
91 }
92
virtio_vhost_set_vring(struct kvm * kvm,int vhost_fd,u32 index,struct virt_queue * queue)93 void virtio_vhost_set_vring(struct kvm *kvm, int vhost_fd, u32 index,
94 struct virt_queue *queue)
95 {
96 int r;
97 struct vhost_vring_addr addr = {
98 .index = index,
99 .desc_user_addr = (u64)(unsigned long)queue->vring.desc,
100 .avail_user_addr = (u64)(unsigned long)queue->vring.avail,
101 .used_user_addr = (u64)(unsigned long)queue->vring.used,
102 };
103 struct vhost_vring_state state = { .index = index };
104 struct vhost_vring_file file = {
105 .index = index,
106 .fd = virtio_vhost_get_irqfd(queue),
107 };
108 struct epoll_event event = {
109 .events = EPOLLIN,
110 .data.ptr = queue,
111 };
112
113 queue->index = index;
114
115 if (queue->endian != VIRTIO_ENDIAN_HOST)
116 die("VHOST requires the same endianness in guest and host");
117
118 state.num = queue->vring.num;
119 r = ioctl(vhost_fd, VHOST_SET_VRING_NUM, &state);
120 if (r < 0)
121 die_perror("VHOST_SET_VRING_NUM failed");
122
123 state.num = 0;
124 r = ioctl(vhost_fd, VHOST_SET_VRING_BASE, &state);
125 if (r < 0)
126 die_perror("VHOST_SET_VRING_BASE failed");
127
128 r = ioctl(vhost_fd, VHOST_SET_VRING_ADDR, &addr);
129 if (r < 0)
130 die_perror("VHOST_SET_VRING_ADDR failed");
131
132 r = ioctl(vhost_fd, VHOST_SET_VRING_CALL, &file);
133 if (r < 0)
134 die_perror("VHOST_SET_VRING_CALL failed");
135
136 r = epoll_ctl(epoll.fd, EPOLL_CTL_ADD, file.fd, &event);
137 if (r < 0)
138 die_perror("EPOLL_CTL_ADD vhost call fd");
139 }
140
virtio_vhost_set_vring_kick(struct kvm * kvm,int vhost_fd,u32 index,int event_fd)141 void virtio_vhost_set_vring_kick(struct kvm *kvm, int vhost_fd,
142 u32 index, int event_fd)
143 {
144 int r;
145 struct vhost_vring_file file = {
146 .index = index,
147 .fd = event_fd,
148 };
149
150 r = ioctl(vhost_fd, VHOST_SET_VRING_KICK, &file);
151 if (r < 0)
152 die_perror("VHOST_SET_VRING_KICK failed");
153 }
154
virtio_vhost_set_vring_irqfd(struct kvm * kvm,u32 gsi,struct virt_queue * queue)155 void virtio_vhost_set_vring_irqfd(struct kvm *kvm, u32 gsi,
156 struct virt_queue *queue)
157 {
158 int r;
159 int fd = virtio_vhost_get_irqfd(queue);
160
161 if (queue->gsi)
162 irq__del_irqfd(kvm, queue->gsi, fd);
163 else
164 /* Disconnect user polling thread */
165 epoll_ctl(epoll.fd, EPOLL_CTL_DEL, fd, NULL);
166
167 /* Connect the direct IRQFD route */
168 r = irq__add_irqfd(kvm, gsi, fd, -1);
169 if (r < 0)
170 die_perror("KVM_IRQFD failed");
171
172 queue->gsi = gsi;
173 }
174
virtio_vhost_reset_vring(struct kvm * kvm,int vhost_fd,u32 index,struct virt_queue * queue)175 void virtio_vhost_reset_vring(struct kvm *kvm, int vhost_fd, u32 index,
176 struct virt_queue *queue)
177
178 {
179 struct vhost_vring_file file = {
180 .index = index,
181 .fd = -1,
182 };
183
184 if (!queue->irqfd)
185 return;
186
187 if (queue->gsi) {
188 irq__del_irqfd(kvm, queue->gsi, queue->irqfd);
189 queue->gsi = 0;
190 }
191
192 epoll_ctl(epoll.fd, EPOLL_CTL_DEL, queue->irqfd, NULL);
193
194 if (ioctl(vhost_fd, VHOST_SET_VRING_CALL, &file))
195 perror("SET_VRING_CALL");
196 close(queue->irqfd);
197 queue->irqfd = 0;
198 }
199
virtio_vhost_set_features(int vhost_fd,u64 features)200 int virtio_vhost_set_features(int vhost_fd, u64 features)
201 {
202 /*
203 * vhost interprets VIRTIO_F_ACCESS_PLATFORM as meaning there is an
204 * iotlb. Since this is not the case for kvmtool, mask it.
205 */
206 u64 masked_feat = features & ~(1ULL << VIRTIO_F_ACCESS_PLATFORM);
207
208 return ioctl(vhost_fd, VHOST_SET_FEATURES, &masked_feat);
209 }
210