xref: /kvmtool/virtio/vhost.c (revision 3b1cdcf9e78f7d36f0cca805c4172bba53779b69)
1029cd2bbSJean-Philippe Brucker #include "kvm/irq.h"
2745221e5SJean-Philippe Brucker #include "kvm/virtio.h"
346aaf3b8SJean-Philippe Brucker #include "kvm/epoll.h"
4745221e5SJean-Philippe Brucker 
5f84ab9ebSJean-Philippe Brucker #include <linux/kvm.h>
6f84ab9ebSJean-Philippe Brucker #include <linux/vhost.h>
7f84ab9ebSJean-Philippe Brucker #include <linux/list.h>
8f84ab9ebSJean-Philippe Brucker 
9029cd2bbSJean-Philippe Brucker #include <sys/eventfd.h>
10029cd2bbSJean-Philippe Brucker 
1146aaf3b8SJean-Philippe Brucker static struct kvm__epoll epoll;
1246aaf3b8SJean-Philippe Brucker 
virtio_vhost_signal_vq(struct kvm * kvm,struct epoll_event * ev)1346aaf3b8SJean-Philippe Brucker static void virtio_vhost_signal_vq(struct kvm *kvm, struct epoll_event *ev)
1446aaf3b8SJean-Philippe Brucker {
1546aaf3b8SJean-Philippe Brucker 	int r;
1646aaf3b8SJean-Philippe Brucker 	u64 tmp;
1746aaf3b8SJean-Philippe Brucker 	struct virt_queue *queue = ev->data.ptr;
1846aaf3b8SJean-Philippe Brucker 
1946aaf3b8SJean-Philippe Brucker 	if (read(queue->irqfd, &tmp, sizeof(tmp)) < 0)
2046aaf3b8SJean-Philippe Brucker 		pr_warning("%s: failed to read eventfd", __func__);
2146aaf3b8SJean-Philippe Brucker 
2246aaf3b8SJean-Philippe Brucker 	r = queue->vdev->ops->signal_vq(kvm, queue->vdev, queue->index);
2346aaf3b8SJean-Philippe Brucker 	if (r)
2446aaf3b8SJean-Philippe Brucker 		pr_warning("%s failed to signal virtqueue", __func__);
2546aaf3b8SJean-Philippe Brucker }
2646aaf3b8SJean-Philippe Brucker 
virtio_vhost_start_poll(struct kvm * kvm)2746aaf3b8SJean-Philippe Brucker static int virtio_vhost_start_poll(struct kvm *kvm)
2846aaf3b8SJean-Philippe Brucker {
2946aaf3b8SJean-Philippe Brucker 	if (epoll.fd)
3046aaf3b8SJean-Philippe Brucker 		return 0;
3146aaf3b8SJean-Philippe Brucker 
3246aaf3b8SJean-Philippe Brucker 	if (epoll__init(kvm, &epoll, "vhost-irq-worker",
3346aaf3b8SJean-Philippe Brucker 			virtio_vhost_signal_vq))
3446aaf3b8SJean-Philippe Brucker 		return -1;
3546aaf3b8SJean-Philippe Brucker 
3646aaf3b8SJean-Philippe Brucker 	return 0;
3746aaf3b8SJean-Philippe Brucker }
3846aaf3b8SJean-Philippe Brucker 
virtio_vhost_stop_poll(struct kvm * kvm)3946aaf3b8SJean-Philippe Brucker static int virtio_vhost_stop_poll(struct kvm *kvm)
4046aaf3b8SJean-Philippe Brucker {
4146aaf3b8SJean-Philippe Brucker 	if (epoll.fd)
4246aaf3b8SJean-Philippe Brucker 		epoll__exit(&epoll);
4346aaf3b8SJean-Philippe Brucker 	return 0;
4446aaf3b8SJean-Philippe Brucker }
4546aaf3b8SJean-Philippe Brucker base_exit(virtio_vhost_stop_poll);
4646aaf3b8SJean-Philippe Brucker 
virtio_vhost_init(struct kvm * kvm,int vhost_fd)47f84ab9ebSJean-Philippe Brucker void virtio_vhost_init(struct kvm *kvm, int vhost_fd)
48f84ab9ebSJean-Philippe Brucker {
49f84ab9ebSJean-Philippe Brucker 	struct kvm_mem_bank *bank;
50f84ab9ebSJean-Philippe Brucker 	struct vhost_memory *mem;
51f84ab9ebSJean-Philippe Brucker 	int i = 0, r;
52f84ab9ebSJean-Philippe Brucker 
5346aaf3b8SJean-Philippe Brucker 	r = virtio_vhost_start_poll(kvm);
5446aaf3b8SJean-Philippe Brucker 	if (r)
5546aaf3b8SJean-Philippe Brucker 		die("Unable to start vhost polling thread\n");
5646aaf3b8SJean-Philippe Brucker 
57f84ab9ebSJean-Philippe Brucker 	mem = calloc(1, sizeof(*mem) +
58f84ab9ebSJean-Philippe Brucker 		     kvm->mem_slots * sizeof(struct vhost_memory_region));
59f84ab9ebSJean-Philippe Brucker 	if (mem == NULL)
60f84ab9ebSJean-Philippe Brucker 		die("Failed allocating memory for vhost memory map");
61f84ab9ebSJean-Philippe Brucker 
62f84ab9ebSJean-Philippe Brucker 	list_for_each_entry(bank, &kvm->mem_banks, list) {
63f84ab9ebSJean-Philippe Brucker 		mem->regions[i] = (struct vhost_memory_region) {
64f84ab9ebSJean-Philippe Brucker 			.guest_phys_addr = bank->guest_phys_addr,
65f84ab9ebSJean-Philippe Brucker 			.memory_size	 = bank->size,
66f84ab9ebSJean-Philippe Brucker 			.userspace_addr	 = (unsigned long)bank->host_addr,
67f84ab9ebSJean-Philippe Brucker 		};
68f84ab9ebSJean-Philippe Brucker 		i++;
69f84ab9ebSJean-Philippe Brucker 	}
70f84ab9ebSJean-Philippe Brucker 	mem->nregions = i;
71f84ab9ebSJean-Philippe Brucker 
72f84ab9ebSJean-Philippe Brucker 	r = ioctl(vhost_fd, VHOST_SET_OWNER);
73f84ab9ebSJean-Philippe Brucker 	if (r != 0)
74f84ab9ebSJean-Philippe Brucker 		die_perror("VHOST_SET_OWNER failed");
75f84ab9ebSJean-Philippe Brucker 
76f84ab9ebSJean-Philippe Brucker 	r = ioctl(vhost_fd, VHOST_SET_MEM_TABLE, mem);
77f84ab9ebSJean-Philippe Brucker 	if (r != 0)
78f84ab9ebSJean-Philippe Brucker 		die_perror("VHOST_SET_MEM_TABLE failed");
79f84ab9ebSJean-Philippe Brucker 
80f84ab9ebSJean-Philippe Brucker 	free(mem);
81f84ab9ebSJean-Philippe Brucker }
82745221e5SJean-Philippe Brucker 
virtio_vhost_get_irqfd(struct virt_queue * queue)8346aaf3b8SJean-Philippe Brucker static int virtio_vhost_get_irqfd(struct virt_queue *queue)
8446aaf3b8SJean-Philippe Brucker {
8546aaf3b8SJean-Philippe Brucker 	if (!queue->irqfd) {
8646aaf3b8SJean-Philippe Brucker 		queue->irqfd = eventfd(0, 0);
8746aaf3b8SJean-Philippe Brucker 		if (queue->irqfd < 0)
8846aaf3b8SJean-Philippe Brucker 			die_perror("eventfd()");
8946aaf3b8SJean-Philippe Brucker 	}
9046aaf3b8SJean-Philippe Brucker 	return queue->irqfd;
9146aaf3b8SJean-Philippe Brucker }
9246aaf3b8SJean-Philippe Brucker 
virtio_vhost_set_vring(struct kvm * kvm,int vhost_fd,u32 index,struct virt_queue * queue)93745221e5SJean-Philippe Brucker void virtio_vhost_set_vring(struct kvm *kvm, int vhost_fd, u32 index,
94745221e5SJean-Philippe Brucker 			    struct virt_queue *queue)
95745221e5SJean-Philippe Brucker {
96745221e5SJean-Philippe Brucker 	int r;
97745221e5SJean-Philippe Brucker 	struct vhost_vring_addr addr = {
98745221e5SJean-Philippe Brucker 		.index = index,
99745221e5SJean-Philippe Brucker 		.desc_user_addr = (u64)(unsigned long)queue->vring.desc,
100745221e5SJean-Philippe Brucker 		.avail_user_addr = (u64)(unsigned long)queue->vring.avail,
101745221e5SJean-Philippe Brucker 		.used_user_addr = (u64)(unsigned long)queue->vring.used,
102745221e5SJean-Philippe Brucker 	};
103745221e5SJean-Philippe Brucker 	struct vhost_vring_state state = { .index = index };
10446aaf3b8SJean-Philippe Brucker 	struct vhost_vring_file file = {
10546aaf3b8SJean-Philippe Brucker 		.index	= index,
10646aaf3b8SJean-Philippe Brucker 		.fd	= virtio_vhost_get_irqfd(queue),
10746aaf3b8SJean-Philippe Brucker 	};
10846aaf3b8SJean-Philippe Brucker 	struct epoll_event event = {
10946aaf3b8SJean-Philippe Brucker 		.events = EPOLLIN,
11046aaf3b8SJean-Philippe Brucker 		.data.ptr = queue,
11146aaf3b8SJean-Philippe Brucker 	};
11246aaf3b8SJean-Philippe Brucker 
11346aaf3b8SJean-Philippe Brucker 	queue->index = index;
114745221e5SJean-Philippe Brucker 
115745221e5SJean-Philippe Brucker 	if (queue->endian != VIRTIO_ENDIAN_HOST)
116745221e5SJean-Philippe Brucker 		die("VHOST requires the same endianness in guest and host");
117745221e5SJean-Philippe Brucker 
118745221e5SJean-Philippe Brucker 	state.num = queue->vring.num;
119745221e5SJean-Philippe Brucker 	r = ioctl(vhost_fd, VHOST_SET_VRING_NUM, &state);
120745221e5SJean-Philippe Brucker 	if (r < 0)
121745221e5SJean-Philippe Brucker 		die_perror("VHOST_SET_VRING_NUM failed");
122745221e5SJean-Philippe Brucker 
123745221e5SJean-Philippe Brucker 	state.num = 0;
124745221e5SJean-Philippe Brucker 	r = ioctl(vhost_fd, VHOST_SET_VRING_BASE, &state);
125745221e5SJean-Philippe Brucker 	if (r < 0)
126745221e5SJean-Philippe Brucker 		die_perror("VHOST_SET_VRING_BASE failed");
127745221e5SJean-Philippe Brucker 
128745221e5SJean-Philippe Brucker 	r = ioctl(vhost_fd, VHOST_SET_VRING_ADDR, &addr);
129745221e5SJean-Philippe Brucker 	if (r < 0)
130745221e5SJean-Philippe Brucker 		die_perror("VHOST_SET_VRING_ADDR failed");
13146aaf3b8SJean-Philippe Brucker 
13246aaf3b8SJean-Philippe Brucker 	r = ioctl(vhost_fd, VHOST_SET_VRING_CALL, &file);
13346aaf3b8SJean-Philippe Brucker 	if (r < 0)
13446aaf3b8SJean-Philippe Brucker 		die_perror("VHOST_SET_VRING_CALL failed");
13546aaf3b8SJean-Philippe Brucker 
13646aaf3b8SJean-Philippe Brucker 	r = epoll_ctl(epoll.fd, EPOLL_CTL_ADD, file.fd, &event);
13746aaf3b8SJean-Philippe Brucker 	if (r < 0)
13846aaf3b8SJean-Philippe Brucker 		die_perror("EPOLL_CTL_ADD vhost call fd");
139745221e5SJean-Philippe Brucker }
140676c0c8aSJean-Philippe Brucker 
virtio_vhost_set_vring_kick(struct kvm * kvm,int vhost_fd,u32 index,int event_fd)141676c0c8aSJean-Philippe Brucker void virtio_vhost_set_vring_kick(struct kvm *kvm, int vhost_fd,
142676c0c8aSJean-Philippe Brucker 				 u32 index, int event_fd)
143676c0c8aSJean-Philippe Brucker {
144676c0c8aSJean-Philippe Brucker 	int r;
145676c0c8aSJean-Philippe Brucker 	struct vhost_vring_file file = {
146676c0c8aSJean-Philippe Brucker 		.index	= index,
147676c0c8aSJean-Philippe Brucker 		.fd	= event_fd,
148676c0c8aSJean-Philippe Brucker 	};
149676c0c8aSJean-Philippe Brucker 
150676c0c8aSJean-Philippe Brucker 	r = ioctl(vhost_fd, VHOST_SET_VRING_KICK, &file);
151676c0c8aSJean-Philippe Brucker 	if (r < 0)
152676c0c8aSJean-Philippe Brucker 		die_perror("VHOST_SET_VRING_KICK failed");
153676c0c8aSJean-Philippe Brucker }
154029cd2bbSJean-Philippe Brucker 
virtio_vhost_set_vring_irqfd(struct kvm * kvm,u32 gsi,struct virt_queue * queue)15546aaf3b8SJean-Philippe Brucker void virtio_vhost_set_vring_irqfd(struct kvm *kvm, u32 gsi,
15646aaf3b8SJean-Philippe Brucker 				  struct virt_queue *queue)
157029cd2bbSJean-Philippe Brucker {
158029cd2bbSJean-Philippe Brucker 	int r;
15946aaf3b8SJean-Philippe Brucker 	int fd = virtio_vhost_get_irqfd(queue);
160029cd2bbSJean-Philippe Brucker 
16146aaf3b8SJean-Philippe Brucker 	if (queue->gsi)
16246aaf3b8SJean-Philippe Brucker 		irq__del_irqfd(kvm, queue->gsi, fd);
16346aaf3b8SJean-Philippe Brucker 	else
16446aaf3b8SJean-Philippe Brucker 		/* Disconnect user polling thread */
16546aaf3b8SJean-Philippe Brucker 		epoll_ctl(epoll.fd, EPOLL_CTL_DEL, fd, NULL);
16646aaf3b8SJean-Philippe Brucker 
16746aaf3b8SJean-Philippe Brucker 	/* Connect the direct IRQFD route */
16846aaf3b8SJean-Philippe Brucker 	r = irq__add_irqfd(kvm, gsi, fd, -1);
169029cd2bbSJean-Philippe Brucker 	if (r < 0)
170029cd2bbSJean-Philippe Brucker 		die_perror("KVM_IRQFD failed");
171029cd2bbSJean-Philippe Brucker 
172029cd2bbSJean-Philippe Brucker 	queue->gsi = gsi;
173029cd2bbSJean-Philippe Brucker }
174029cd2bbSJean-Philippe Brucker 
virtio_vhost_reset_vring(struct kvm * kvm,int vhost_fd,u32 index,struct virt_queue * queue)175029cd2bbSJean-Philippe Brucker void virtio_vhost_reset_vring(struct kvm *kvm, int vhost_fd, u32 index,
176029cd2bbSJean-Philippe Brucker 			      struct virt_queue *queue)
177029cd2bbSJean-Philippe Brucker 
178029cd2bbSJean-Philippe Brucker {
17946aaf3b8SJean-Philippe Brucker 	struct vhost_vring_file file = {
18046aaf3b8SJean-Philippe Brucker 		.index	= index,
18146aaf3b8SJean-Philippe Brucker 		.fd	= -1,
18246aaf3b8SJean-Philippe Brucker 	};
18346aaf3b8SJean-Philippe Brucker 
18446aaf3b8SJean-Philippe Brucker 	if (!queue->irqfd)
18546aaf3b8SJean-Philippe Brucker 		return;
18646aaf3b8SJean-Philippe Brucker 
187029cd2bbSJean-Philippe Brucker 	if (queue->gsi) {
188029cd2bbSJean-Philippe Brucker 		irq__del_irqfd(kvm, queue->gsi, queue->irqfd);
18946aaf3b8SJean-Philippe Brucker 		queue->gsi = 0;
190029cd2bbSJean-Philippe Brucker 	}
19146aaf3b8SJean-Philippe Brucker 
19246aaf3b8SJean-Philippe Brucker 	epoll_ctl(epoll.fd, EPOLL_CTL_DEL, queue->irqfd, NULL);
19346aaf3b8SJean-Philippe Brucker 
19446aaf3b8SJean-Philippe Brucker 	if (ioctl(vhost_fd, VHOST_SET_VRING_CALL, &file))
19546aaf3b8SJean-Philippe Brucker 		perror("SET_VRING_CALL");
19646aaf3b8SJean-Philippe Brucker 	close(queue->irqfd);
19746aaf3b8SJean-Philippe Brucker 	queue->irqfd = 0;
198029cd2bbSJean-Philippe Brucker }
199*3b1cdcf9SJean-Philippe Brucker 
virtio_vhost_set_features(int vhost_fd,u64 features)200*3b1cdcf9SJean-Philippe Brucker int virtio_vhost_set_features(int vhost_fd, u64 features)
201*3b1cdcf9SJean-Philippe Brucker {
202*3b1cdcf9SJean-Philippe Brucker 	/*
203*3b1cdcf9SJean-Philippe Brucker 	 * vhost interprets VIRTIO_F_ACCESS_PLATFORM as meaning there is an
204*3b1cdcf9SJean-Philippe Brucker 	 * iotlb. Since this is not the case for kvmtool, mask it.
205*3b1cdcf9SJean-Philippe Brucker 	 */
206*3b1cdcf9SJean-Philippe Brucker 	u64 masked_feat = features & ~(1ULL << VIRTIO_F_ACCESS_PLATFORM);
207*3b1cdcf9SJean-Philippe Brucker 
208*3b1cdcf9SJean-Philippe Brucker 	return ioctl(vhost_fd, VHOST_SET_FEATURES, &masked_feat);
209*3b1cdcf9SJean-Philippe Brucker }
210