xref: /kvmtool/virtio/balloon.c (revision 867b15ccd7dae9ba7a174f97d4fe76e90a79d957)
182d2f21eSSasha Levin #include "kvm/virtio-balloon.h"
282d2f21eSSasha Levin 
382d2f21eSSasha Levin #include "kvm/virtio-pci-dev.h"
482d2f21eSSasha Levin 
582d2f21eSSasha Levin #include "kvm/virtio.h"
682d2f21eSSasha Levin #include "kvm/util.h"
782d2f21eSSasha Levin #include "kvm/kvm.h"
882d2f21eSSasha Levin #include "kvm/pci.h"
982d2f21eSSasha Levin #include "kvm/threadpool.h"
104b2e0a7aSSasha Levin #include "kvm/guest_compat.h"
114b1addaeSSasha Levin #include "kvm/kvm-ipc.h"
1282d2f21eSSasha Levin 
1382d2f21eSSasha Levin #include <linux/virtio_ring.h>
1482d2f21eSSasha Levin #include <linux/virtio_balloon.h>
1582d2f21eSSasha Levin 
16*867b15ccSJean-Philippe Brucker #include <linux/byteorder.h>
1799c74cf9SSasha Levin #include <linux/kernel.h>
1882d2f21eSSasha Levin #include <linux/list.h>
1982d2f21eSSasha Levin #include <fcntl.h>
2082d2f21eSSasha Levin #include <sys/types.h>
2182d2f21eSSasha Levin #include <sys/stat.h>
2299c74cf9SSasha Levin #include <sys/mman.h>
2382d2f21eSSasha Levin #include <pthread.h>
241599d724SSasha Levin #include <sys/eventfd.h>
2582d2f21eSSasha Levin 
26bc10d2c1SSasha Levin #define NUM_VIRT_QUEUES		3
2782d2f21eSSasha Levin #define VIRTIO_BLN_QUEUE_SIZE	128
2882d2f21eSSasha Levin #define VIRTIO_BLN_INFLATE	0
2982d2f21eSSasha Levin #define VIRTIO_BLN_DEFLATE	1
30bc10d2c1SSasha Levin #define VIRTIO_BLN_STATS	2
3182d2f21eSSasha Levin 
3282d2f21eSSasha Levin struct bln_dev {
3382d2f21eSSasha Levin 	struct list_head	list;
3402eca50cSAsias He 	struct virtio_device	vdev;
3582d2f21eSSasha Levin 
3699c74cf9SSasha Levin 	u32			features;
3782d2f21eSSasha Levin 
3882d2f21eSSasha Levin 	/* virtio queue */
3982d2f21eSSasha Levin 	struct virt_queue	vqs[NUM_VIRT_QUEUES];
405cac5d9cSSasha Levin 	struct thread_pool__job	jobs[NUM_VIRT_QUEUES];
4182d2f21eSSasha Levin 
42bc10d2c1SSasha Levin 	struct virtio_balloon_stat stats[VIRTIO_BALLOON_S_NR];
43bc10d2c1SSasha Levin 	struct virtio_balloon_stat *cur_stat;
44bc10d2c1SSasha Levin 	u32			cur_stat_head;
45bc10d2c1SSasha Levin 	u16			stat_count;
46bc10d2c1SSasha Levin 	int			stat_waitfd;
47bc10d2c1SSasha Levin 
4882d2f21eSSasha Levin 	struct virtio_balloon_config config;
4982d2f21eSSasha Levin };
5082d2f21eSSasha Levin 
5182d2f21eSSasha Levin static struct bln_dev bdev;
52312c62d1SSasha Levin static int compat_id = -1;
5382d2f21eSSasha Levin 
5482d2f21eSSasha Levin static bool virtio_bln_do_io_request(struct kvm *kvm, struct bln_dev *bdev, struct virt_queue *queue)
5582d2f21eSSasha Levin {
5682d2f21eSSasha Levin 	struct iovec iov[VIRTIO_BLN_QUEUE_SIZE];
5782d2f21eSSasha Levin 	unsigned int len = 0;
5882d2f21eSSasha Levin 	u16 out, in, head;
5982d2f21eSSasha Levin 	u32 *ptrs, i;
60*867b15ccSJean-Philippe Brucker 	u32 actual;
6182d2f21eSSasha Levin 
6282d2f21eSSasha Levin 	head	= virt_queue__get_iov(queue, iov, &out, &in, kvm);
6382d2f21eSSasha Levin 	ptrs	= iov[0].iov_base;
6482d2f21eSSasha Levin 	len	= iov[0].iov_len / sizeof(u32);
6582d2f21eSSasha Levin 
66*867b15ccSJean-Philippe Brucker 	actual = le32_to_cpu(bdev->config.actual);
6782d2f21eSSasha Levin 	for (i = 0 ; i < len ; i++) {
6882d2f21eSSasha Levin 		void *guest_ptr;
6982d2f21eSSasha Levin 
70c8183442SKonstantin Khlebnikov 		guest_ptr = guest_flat_to_host(kvm, (u64)ptrs[i] << VIRTIO_BALLOON_PFN_SHIFT);
7182d2f21eSSasha Levin 		if (queue == &bdev->vqs[VIRTIO_BLN_INFLATE]) {
7282d2f21eSSasha Levin 			madvise(guest_ptr, 1 << VIRTIO_BALLOON_PFN_SHIFT, MADV_DONTNEED);
73*867b15ccSJean-Philippe Brucker 			actual++;
74bc10d2c1SSasha Levin 		} else if (queue == &bdev->vqs[VIRTIO_BLN_DEFLATE]) {
75*867b15ccSJean-Philippe Brucker 			actual--;
7682d2f21eSSasha Levin 		}
7782d2f21eSSasha Levin 	}
78*867b15ccSJean-Philippe Brucker 	bdev->config.actual = cpu_to_le32(actual);
7982d2f21eSSasha Levin 
8082d2f21eSSasha Levin 	virt_queue__set_used_elem(queue, head, len);
8182d2f21eSSasha Levin 
8282d2f21eSSasha Levin 	return true;
8382d2f21eSSasha Levin }
8482d2f21eSSasha Levin 
85bc10d2c1SSasha Levin static bool virtio_bln_do_stat_request(struct kvm *kvm, struct bln_dev *bdev, struct virt_queue *queue)
86bc10d2c1SSasha Levin {
87bc10d2c1SSasha Levin 	struct iovec iov[VIRTIO_BLN_QUEUE_SIZE];
88bc10d2c1SSasha Levin 	u16 out, in, head;
89bc10d2c1SSasha Levin 	struct virtio_balloon_stat *stat;
90bc10d2c1SSasha Levin 	u64 wait_val = 1;
91bc10d2c1SSasha Levin 
92bc10d2c1SSasha Levin 	head = virt_queue__get_iov(queue, iov, &out, &in, kvm);
93bc10d2c1SSasha Levin 	stat = iov[0].iov_base;
94bc10d2c1SSasha Levin 
95bc10d2c1SSasha Levin 	/* Initial empty stat buffer */
96bc10d2c1SSasha Levin 	if (bdev->cur_stat == NULL) {
97bc10d2c1SSasha Levin 		bdev->cur_stat = stat;
98bc10d2c1SSasha Levin 		bdev->cur_stat_head = head;
99bc10d2c1SSasha Levin 
100bc10d2c1SSasha Levin 		return true;
101bc10d2c1SSasha Levin 	}
102bc10d2c1SSasha Levin 
103bc10d2c1SSasha Levin 	memcpy(bdev->stats, stat, iov[0].iov_len);
104bc10d2c1SSasha Levin 
105bc10d2c1SSasha Levin 	bdev->stat_count = iov[0].iov_len / sizeof(struct virtio_balloon_stat);
106bc10d2c1SSasha Levin 	bdev->cur_stat = stat;
107bc10d2c1SSasha Levin 	bdev->cur_stat_head = head;
108bc10d2c1SSasha Levin 
109bc10d2c1SSasha Levin 	if (write(bdev->stat_waitfd, &wait_val, sizeof(wait_val)) <= 0)
110bc10d2c1SSasha Levin 		return -EFAULT;
111bc10d2c1SSasha Levin 
112bc10d2c1SSasha Levin 	return 1;
113bc10d2c1SSasha Levin }
114bc10d2c1SSasha Levin 
11582d2f21eSSasha Levin static void virtio_bln_do_io(struct kvm *kvm, void *param)
11682d2f21eSSasha Levin {
11782d2f21eSSasha Levin 	struct virt_queue *vq = param;
11882d2f21eSSasha Levin 
119bc10d2c1SSasha Levin 	if (vq == &bdev.vqs[VIRTIO_BLN_STATS]) {
120bc10d2c1SSasha Levin 		virtio_bln_do_stat_request(kvm, &bdev, vq);
12102eca50cSAsias He 		bdev.vdev.ops->signal_vq(kvm, &bdev.vdev, VIRTIO_BLN_STATS);
122bc10d2c1SSasha Levin 		return;
123bc10d2c1SSasha Levin 	}
124bc10d2c1SSasha Levin 
12582d2f21eSSasha Levin 	while (virt_queue__available(vq)) {
12682d2f21eSSasha Levin 		virtio_bln_do_io_request(kvm, &bdev, vq);
12702eca50cSAsias He 		bdev.vdev.ops->signal_vq(kvm, &bdev.vdev, vq - bdev.vqs);
12882d2f21eSSasha Levin 	}
12982d2f21eSSasha Levin }
13082d2f21eSSasha Levin 
1314346fd8fSSasha Levin static int virtio_bln__collect_stats(struct kvm *kvm)
132bc10d2c1SSasha Levin {
1333a13530aSKeir Fraser 	struct virt_queue *vq = &bdev.vqs[VIRTIO_BLN_STATS];
134bc10d2c1SSasha Levin 	u64 tmp;
135bc10d2c1SSasha Levin 
1363a13530aSKeir Fraser 	/* Exit if the queue is not set up. */
137609ee906SJean-Philippe Brucker 	if (!vq->enabled)
1383a13530aSKeir Fraser 		return -ENODEV;
1393a13530aSKeir Fraser 
1403a13530aSKeir Fraser 	virt_queue__set_used_elem(vq, bdev.cur_stat_head,
141bc10d2c1SSasha Levin 				  sizeof(struct virtio_balloon_stat));
14202eca50cSAsias He 	bdev.vdev.ops->signal_vq(kvm, &bdev.vdev, VIRTIO_BLN_STATS);
143bc10d2c1SSasha Levin 
144bc10d2c1SSasha Levin 	if (read(bdev.stat_waitfd, &tmp, sizeof(tmp)) <= 0)
145bc10d2c1SSasha Levin 		return -EFAULT;
146bc10d2c1SSasha Levin 
147bc10d2c1SSasha Levin 	return 0;
148bc10d2c1SSasha Levin }
149bc10d2c1SSasha Levin 
150e1063726SSasha Levin static void virtio_bln__print_stats(struct kvm *kvm, int fd, u32 type, u32 len, u8 *msg)
151bc10d2c1SSasha Levin {
152498746b9SSasha Levin 	int r;
153bc10d2c1SSasha Levin 
154d0dc85a8SLai Jiangshan 	if (WARN_ON(type != KVM_IPC_STAT || len))
155d0dc85a8SLai Jiangshan 		return;
156d0dc85a8SLai Jiangshan 
1574346fd8fSSasha Levin 	if (virtio_bln__collect_stats(kvm) < 0)
1584b1addaeSSasha Levin 		return;
159bc10d2c1SSasha Levin 
160498746b9SSasha Levin 	r = write(fd, bdev.stats, sizeof(bdev.stats));
161498746b9SSasha Levin 	if (r < 0)
162498746b9SSasha Levin 		pr_warning("Failed sending memory stats");
163bc10d2c1SSasha Levin }
164bc10d2c1SSasha Levin 
165e1063726SSasha Levin static void handle_mem(struct kvm *kvm, int fd, u32 type, u32 len, u8 *msg)
16682d2f21eSSasha Levin {
16749669299SLai Jiangshan 	int mem;
168*867b15ccSJean-Philippe Brucker 	u32 num_pages;
1694b1addaeSSasha Levin 
17049669299SLai Jiangshan 	if (WARN_ON(type != KVM_IPC_BALLOON || len != sizeof(int)))
17149669299SLai Jiangshan 		return;
17249669299SLai Jiangshan 
17349669299SLai Jiangshan 	mem = *(int *)msg;
174*867b15ccSJean-Philippe Brucker 	num_pages = le32_to_cpu(bdev.config.num_pages);
175*867b15ccSJean-Philippe Brucker 
1764b1addaeSSasha Levin 	if (mem > 0) {
177*867b15ccSJean-Philippe Brucker 		num_pages += 256 * mem;
1784b1addaeSSasha Levin 	} else if (mem < 0) {
179*867b15ccSJean-Philippe Brucker 		if (num_pages < (u32)(256 * (-mem)))
18042bcd3eeSLiming Wang 			return;
18142bcd3eeSLiming Wang 
182*867b15ccSJean-Philippe Brucker 		num_pages += 256 * mem;
18342bcd3eeSLiming Wang 	}
18482d2f21eSSasha Levin 
185*867b15ccSJean-Philippe Brucker 	bdev.config.num_pages = cpu_to_le32(num_pages);
186*867b15ccSJean-Philippe Brucker 
18782d2f21eSSasha Levin 	/* Notify that the configuration space has changed */
18802eca50cSAsias He 	bdev.vdev.ops->signal_config(kvm, &bdev.vdev);
18999c74cf9SSasha Levin }
19099c74cf9SSasha Levin 
191c5ae742bSSasha Levin static u8 *get_config(struct kvm *kvm, void *dev)
19299c74cf9SSasha Levin {
19399c74cf9SSasha Levin 	struct bln_dev *bdev = dev;
19499c74cf9SSasha Levin 
195c5ae742bSSasha Levin 	return ((u8 *)(&bdev->config));
19699c74cf9SSasha Levin }
19799c74cf9SSasha Levin 
198e4730284SMartin Radev static size_t get_config_size(struct kvm *kvm, void *dev)
199e4730284SMartin Radev {
200e4730284SMartin Radev 	struct bln_dev *bdev = dev;
201e4730284SMartin Radev 
202e4730284SMartin Radev 	return sizeof(bdev->config);
203e4730284SMartin Radev }
204e4730284SMartin Radev 
20599c74cf9SSasha Levin static u32 get_host_features(struct kvm *kvm, void *dev)
20699c74cf9SSasha Levin {
20799c74cf9SSasha Levin 	return 1 << VIRTIO_BALLOON_F_STATS_VQ;
20899c74cf9SSasha Levin }
20999c74cf9SSasha Levin 
21099c74cf9SSasha Levin static void set_guest_features(struct kvm *kvm, void *dev, u32 features)
21199c74cf9SSasha Levin {
21299c74cf9SSasha Levin 	struct bln_dev *bdev = dev;
21399c74cf9SSasha Levin 
21499c74cf9SSasha Levin 	bdev->features = features;
21599c74cf9SSasha Levin }
21699c74cf9SSasha Levin 
21795242e44SJean-Philippe Brucker static void notify_status(struct kvm *kvm, void *dev, u32 status)
21895242e44SJean-Philippe Brucker {
21995242e44SJean-Philippe Brucker }
22095242e44SJean-Philippe Brucker 
221609ee906SJean-Philippe Brucker static int init_vq(struct kvm *kvm, void *dev, u32 vq)
22299c74cf9SSasha Levin {
22399c74cf9SSasha Levin 	struct bln_dev *bdev = dev;
22499c74cf9SSasha Levin 	struct virt_queue *queue;
22599c74cf9SSasha Levin 
226312c62d1SSasha Levin 	compat__remove_message(compat_id);
22799c74cf9SSasha Levin 
22899c74cf9SSasha Levin 	queue		= &bdev->vqs[vq];
229fd41cde0SJean-Philippe Brucker 
230609ee906SJean-Philippe Brucker 	virtio_init_device_vq(kvm, &bdev->vdev, queue, VIRTIO_BLN_QUEUE_SIZE);
23199c74cf9SSasha Levin 
23299c74cf9SSasha Levin 	thread_pool__init_job(&bdev->jobs[vq], kvm, virtio_bln_do_io, queue);
23399c74cf9SSasha Levin 
23499c74cf9SSasha Levin 	return 0;
23599c74cf9SSasha Levin }
23699c74cf9SSasha Levin 
23799c74cf9SSasha Levin static int notify_vq(struct kvm *kvm, void *dev, u32 vq)
23899c74cf9SSasha Levin {
23999c74cf9SSasha Levin 	struct bln_dev *bdev = dev;
24099c74cf9SSasha Levin 
24199c74cf9SSasha Levin 	thread_pool__do_job(&bdev->jobs[vq]);
24299c74cf9SSasha Levin 
24399c74cf9SSasha Levin 	return 0;
24499c74cf9SSasha Levin }
24599c74cf9SSasha Levin 
24653fbb17bSJean-Philippe Brucker static struct virt_queue *get_vq(struct kvm *kvm, void *dev, u32 vq)
24799c74cf9SSasha Levin {
24899c74cf9SSasha Levin 	struct bln_dev *bdev = dev;
24999c74cf9SSasha Levin 
25053fbb17bSJean-Philippe Brucker 	return &bdev->vqs[vq];
25199c74cf9SSasha Levin }
25299c74cf9SSasha Levin 
25399c74cf9SSasha Levin static int get_size_vq(struct kvm *kvm, void *dev, u32 vq)
25499c74cf9SSasha Levin {
25599c74cf9SSasha Levin 	return VIRTIO_BLN_QUEUE_SIZE;
25682d2f21eSSasha Levin }
25782d2f21eSSasha Levin 
25813cf07d9SWill Deacon static int set_size_vq(struct kvm *kvm, void *dev, u32 vq, int size)
25913cf07d9SWill Deacon {
26013cf07d9SWill Deacon 	/* FIXME: dynamic */
26113cf07d9SWill Deacon 	return size;
26213cf07d9SWill Deacon }
26313cf07d9SWill Deacon 
26431e0eaccSMartin Radev static unsigned int get_vq_count(struct kvm *kvm, void *dev)
265b98ac591SJean-Philippe Brucker {
266b98ac591SJean-Philippe Brucker 	return NUM_VIRT_QUEUES;
267b98ac591SJean-Philippe Brucker }
268b98ac591SJean-Philippe Brucker 
26915542babSAndre Przywara struct virtio_ops bln_dev_virtio_ops = {
27099c74cf9SSasha Levin 	.get_config		= get_config,
271e4730284SMartin Radev 	.get_config_size	= get_config_size,
27299c74cf9SSasha Levin 	.get_host_features	= get_host_features,
27399c74cf9SSasha Levin 	.set_guest_features	= set_guest_features,
27499c74cf9SSasha Levin 	.init_vq		= init_vq,
27595242e44SJean-Philippe Brucker 	.notify_status		= notify_status,
27699c74cf9SSasha Levin 	.notify_vq		= notify_vq,
27753fbb17bSJean-Philippe Brucker 	.get_vq			= get_vq,
27899c74cf9SSasha Levin 	.get_size_vq		= get_size_vq,
27913cf07d9SWill Deacon 	.set_size_vq            = set_size_vq,
280b98ac591SJean-Philippe Brucker 	.get_vq_count		= get_vq_count,
28199c74cf9SSasha Levin };
2824b2e0a7aSSasha Levin 
283d06db2fdSSasha Levin int virtio_bln__init(struct kvm *kvm)
2841c47ce69SSasha Levin {
285db927775SAlexandru Elisei 	int r;
286db927775SAlexandru Elisei 
287d06db2fdSSasha Levin 	if (!kvm->cfg.balloon)
288d06db2fdSSasha Levin 		return 0;
289d06db2fdSSasha Levin 
2901c47ce69SSasha Levin 	kvm_ipc__register_handler(KVM_IPC_BALLOON, handle_mem);
2911c47ce69SSasha Levin 	kvm_ipc__register_handler(KVM_IPC_STAT, virtio_bln__print_stats);
2921c47ce69SSasha Levin 
2931c47ce69SSasha Levin 	bdev.stat_waitfd	= eventfd(0, 0);
2941c47ce69SSasha Levin 	memset(&bdev.config, 0, sizeof(struct virtio_balloon_config));
2951c47ce69SSasha Levin 
296db927775SAlexandru Elisei 	r = virtio_init(kvm, &bdev, &bdev.vdev, &bln_dev_virtio_ops,
297d97dadecSWill Deacon 			VIRTIO_DEFAULT_TRANS(kvm), PCI_DEVICE_ID_VIRTIO_BLN,
298ae06ce71SWill Deacon 			VIRTIO_ID_BALLOON, PCI_CLASS_BLN);
299db927775SAlexandru Elisei 	if (r < 0)
300db927775SAlexandru Elisei 		return r;
3011c47ce69SSasha Levin 
302d278197dSAsias He 	if (compat_id == -1)
30352f34d2cSAsias He 		compat_id = virtio_compat_add_message("virtio-balloon", "CONFIG_VIRTIO_BALLOON");
304d06db2fdSSasha Levin 
305d06db2fdSSasha Levin 	return 0;
306d06db2fdSSasha Levin }
30749a8afd1SSasha Levin virtio_dev_init(virtio_bln__init);
308d06db2fdSSasha Levin 
309d06db2fdSSasha Levin int virtio_bln__exit(struct kvm *kvm)
310d06db2fdSSasha Levin {
311d06db2fdSSasha Levin 	return 0;
31282d2f21eSSasha Levin }
31349a8afd1SSasha Levin virtio_dev_exit(virtio_bln__exit);
314