xref: /kvmtool/virtio/vhost.c (revision 3b1cdcf9e78f7d36f0cca805c4172bba53779b69)
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