xref: /kvmtool/virtio/balloon.c (revision 1c47ce695a0c853fcca0dccf558c7f2d62d23715)
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"
11*1c47ce69SSasha Levin #include "kvm/virtio-trans.h"
124b1addaeSSasha Levin #include "kvm/kvm-ipc.h"
1382d2f21eSSasha Levin 
1482d2f21eSSasha Levin #include <linux/virtio_ring.h>
1582d2f21eSSasha Levin #include <linux/virtio_balloon.h>
1682d2f21eSSasha Levin 
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;
34*1c47ce69SSasha Levin 	struct virtio_trans	vtrans;
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;
5282d2f21eSSasha Levin extern struct kvm *kvm;
53312c62d1SSasha Levin static int compat_id = -1;
5482d2f21eSSasha Levin 
5582d2f21eSSasha Levin static bool virtio_bln_do_io_request(struct kvm *kvm, struct bln_dev *bdev, struct virt_queue *queue)
5682d2f21eSSasha Levin {
5782d2f21eSSasha Levin 	struct iovec iov[VIRTIO_BLN_QUEUE_SIZE];
5882d2f21eSSasha Levin 	unsigned int len = 0;
5982d2f21eSSasha Levin 	u16 out, in, head;
6082d2f21eSSasha Levin 	u32 *ptrs, i;
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 
6682d2f21eSSasha Levin 	for (i = 0 ; i < len ; i++) {
6782d2f21eSSasha Levin 		void *guest_ptr;
6882d2f21eSSasha Levin 
6982d2f21eSSasha Levin 		guest_ptr = guest_flat_to_host(kvm, ptrs[i] << VIRTIO_BALLOON_PFN_SHIFT);
7082d2f21eSSasha Levin 		if (queue == &bdev->vqs[VIRTIO_BLN_INFLATE]) {
7182d2f21eSSasha Levin 			madvise(guest_ptr, 1 << VIRTIO_BALLOON_PFN_SHIFT, MADV_DONTNEED);
7282d2f21eSSasha Levin 			bdev->config.actual++;
73bc10d2c1SSasha Levin 		} else if (queue == &bdev->vqs[VIRTIO_BLN_DEFLATE]) {
7482d2f21eSSasha Levin 			bdev->config.actual--;
7582d2f21eSSasha Levin 		}
7682d2f21eSSasha Levin 	}
7782d2f21eSSasha Levin 
7882d2f21eSSasha Levin 	virt_queue__set_used_elem(queue, head, len);
7982d2f21eSSasha Levin 
8082d2f21eSSasha Levin 	return true;
8182d2f21eSSasha Levin }
8282d2f21eSSasha Levin 
83bc10d2c1SSasha Levin static bool virtio_bln_do_stat_request(struct kvm *kvm, struct bln_dev *bdev, struct virt_queue *queue)
84bc10d2c1SSasha Levin {
85bc10d2c1SSasha Levin 	struct iovec iov[VIRTIO_BLN_QUEUE_SIZE];
86bc10d2c1SSasha Levin 	u16 out, in, head;
87bc10d2c1SSasha Levin 	struct virtio_balloon_stat *stat;
88bc10d2c1SSasha Levin 	u64 wait_val = 1;
89bc10d2c1SSasha Levin 
90bc10d2c1SSasha Levin 	head = virt_queue__get_iov(queue, iov, &out, &in, kvm);
91bc10d2c1SSasha Levin 	stat = iov[0].iov_base;
92bc10d2c1SSasha Levin 
93bc10d2c1SSasha Levin 	/* Initial empty stat buffer */
94bc10d2c1SSasha Levin 	if (bdev->cur_stat == NULL) {
95bc10d2c1SSasha Levin 		bdev->cur_stat = stat;
96bc10d2c1SSasha Levin 		bdev->cur_stat_head = head;
97bc10d2c1SSasha Levin 
98bc10d2c1SSasha Levin 		return true;
99bc10d2c1SSasha Levin 	}
100bc10d2c1SSasha Levin 
101bc10d2c1SSasha Levin 	memcpy(bdev->stats, stat, iov[0].iov_len);
102bc10d2c1SSasha Levin 
103bc10d2c1SSasha Levin 	bdev->stat_count = iov[0].iov_len / sizeof(struct virtio_balloon_stat);
104bc10d2c1SSasha Levin 	bdev->cur_stat = stat;
105bc10d2c1SSasha Levin 	bdev->cur_stat_head = head;
106bc10d2c1SSasha Levin 
107bc10d2c1SSasha Levin 	if (write(bdev->stat_waitfd, &wait_val, sizeof(wait_val)) <= 0)
108bc10d2c1SSasha Levin 		return -EFAULT;
109bc10d2c1SSasha Levin 
110bc10d2c1SSasha Levin 	return 1;
111bc10d2c1SSasha Levin }
112bc10d2c1SSasha Levin 
11382d2f21eSSasha Levin static void virtio_bln_do_io(struct kvm *kvm, void *param)
11482d2f21eSSasha Levin {
11582d2f21eSSasha Levin 	struct virt_queue *vq = param;
11682d2f21eSSasha Levin 
117bc10d2c1SSasha Levin 	if (vq == &bdev.vqs[VIRTIO_BLN_STATS]) {
118bc10d2c1SSasha Levin 		virtio_bln_do_stat_request(kvm, &bdev, vq);
119*1c47ce69SSasha Levin 		bdev.vtrans.trans_ops->signal_vq(kvm, &bdev.vtrans, VIRTIO_BLN_STATS);
120bc10d2c1SSasha Levin 		return;
121bc10d2c1SSasha Levin 	}
122bc10d2c1SSasha Levin 
12382d2f21eSSasha Levin 	while (virt_queue__available(vq)) {
12482d2f21eSSasha Levin 		virtio_bln_do_io_request(kvm, &bdev, vq);
125*1c47ce69SSasha Levin 		bdev.vtrans.trans_ops->signal_vq(kvm, &bdev.vtrans, vq - bdev.vqs);
12682d2f21eSSasha Levin 	}
12782d2f21eSSasha Levin }
12882d2f21eSSasha Levin 
129bc10d2c1SSasha Levin static int virtio_bln__collect_stats(void)
130bc10d2c1SSasha Levin {
131bc10d2c1SSasha Levin 	u64 tmp;
132bc10d2c1SSasha Levin 
133bc10d2c1SSasha Levin 	virt_queue__set_used_elem(&bdev.vqs[VIRTIO_BLN_STATS], bdev.cur_stat_head,
134bc10d2c1SSasha Levin 				  sizeof(struct virtio_balloon_stat));
135*1c47ce69SSasha Levin 	bdev.vtrans.trans_ops->signal_vq(kvm, &bdev.vtrans, VIRTIO_BLN_STATS);
136bc10d2c1SSasha Levin 
137bc10d2c1SSasha Levin 	if (read(bdev.stat_waitfd, &tmp, sizeof(tmp)) <= 0)
138bc10d2c1SSasha Levin 		return -EFAULT;
139bc10d2c1SSasha Levin 
140bc10d2c1SSasha Levin 	return 0;
141bc10d2c1SSasha Levin }
142bc10d2c1SSasha Levin 
1434b1addaeSSasha Levin static void virtio_bln__print_stats(int fd, u32 type, u32 len, u8 *msg)
144bc10d2c1SSasha Levin {
145498746b9SSasha Levin 	int r;
146bc10d2c1SSasha Levin 
147bc10d2c1SSasha Levin 	if (virtio_bln__collect_stats() < 0)
1484b1addaeSSasha Levin 		return;
149bc10d2c1SSasha Levin 
150498746b9SSasha Levin 	r = write(fd, bdev.stats, sizeof(bdev.stats));
151498746b9SSasha Levin 	if (r < 0)
152498746b9SSasha Levin 		pr_warning("Failed sending memory stats");
153bc10d2c1SSasha Levin }
154bc10d2c1SSasha Levin 
1554b1addaeSSasha Levin static void handle_mem(int fd, u32 type, u32 len, u8 *msg)
15682d2f21eSSasha Levin {
1574b1addaeSSasha Levin 	int mem = *(int *)msg;
1584b1addaeSSasha Levin 
1594b1addaeSSasha Levin 	if (mem > 0) {
1604b1addaeSSasha Levin 		bdev.config.num_pages += 256 * mem;
1614b1addaeSSasha Levin 	} else if (mem < 0) {
1624b1addaeSSasha Levin 		if (bdev.config.num_pages < (u32)(256 * (-mem)))
16342bcd3eeSLiming Wang 			return;
16442bcd3eeSLiming Wang 
1654b1addaeSSasha Levin 		bdev.config.num_pages += 256 * mem;
16642bcd3eeSLiming Wang 	}
16782d2f21eSSasha Levin 
16882d2f21eSSasha Levin 	/* Notify that the configuration space has changed */
169*1c47ce69SSasha Levin 	bdev.vtrans.trans_ops->signal_config(kvm, &bdev.vtrans);
17099c74cf9SSasha Levin }
17199c74cf9SSasha Levin 
17299c74cf9SSasha Levin static void set_config(struct kvm *kvm, void *dev, u8 data, u32 offset)
17399c74cf9SSasha Levin {
17499c74cf9SSasha Levin 	struct bln_dev *bdev = dev;
17599c74cf9SSasha Levin 
17699c74cf9SSasha Levin 	((u8 *)(&bdev->config))[offset] = data;
17799c74cf9SSasha Levin }
17899c74cf9SSasha Levin 
17999c74cf9SSasha Levin static u8 get_config(struct kvm *kvm, void *dev, u32 offset)
18099c74cf9SSasha Levin {
18199c74cf9SSasha Levin 	struct bln_dev *bdev = dev;
18299c74cf9SSasha Levin 
18399c74cf9SSasha Levin 	return ((u8 *)(&bdev->config))[offset];
18499c74cf9SSasha Levin }
18599c74cf9SSasha Levin 
18699c74cf9SSasha Levin static u32 get_host_features(struct kvm *kvm, void *dev)
18799c74cf9SSasha Levin {
18899c74cf9SSasha Levin 	return 1 << VIRTIO_BALLOON_F_STATS_VQ;
18999c74cf9SSasha Levin }
19099c74cf9SSasha Levin 
19199c74cf9SSasha Levin static void set_guest_features(struct kvm *kvm, void *dev, u32 features)
19299c74cf9SSasha Levin {
19399c74cf9SSasha Levin 	struct bln_dev *bdev = dev;
19499c74cf9SSasha Levin 
19599c74cf9SSasha Levin 	bdev->features = features;
19699c74cf9SSasha Levin }
19799c74cf9SSasha Levin 
19899c74cf9SSasha Levin static int init_vq(struct kvm *kvm, void *dev, u32 vq, u32 pfn)
19999c74cf9SSasha Levin {
20099c74cf9SSasha Levin 	struct bln_dev *bdev = dev;
20199c74cf9SSasha Levin 	struct virt_queue *queue;
20299c74cf9SSasha Levin 	void *p;
20399c74cf9SSasha Levin 
204312c62d1SSasha Levin 	compat__remove_message(compat_id);
20599c74cf9SSasha Levin 
20699c74cf9SSasha Levin 	queue			= &bdev->vqs[vq];
20799c74cf9SSasha Levin 	queue->pfn		= pfn;
20899c74cf9SSasha Levin 	p			= guest_pfn_to_host(kvm, queue->pfn);
20999c74cf9SSasha Levin 
21099c74cf9SSasha Levin 	thread_pool__init_job(&bdev->jobs[vq], kvm, virtio_bln_do_io, queue);
21199c74cf9SSasha Levin 	vring_init(&queue->vring, VIRTIO_BLN_QUEUE_SIZE, p, VIRTIO_PCI_VRING_ALIGN);
21299c74cf9SSasha Levin 
21399c74cf9SSasha Levin 	return 0;
21499c74cf9SSasha Levin }
21599c74cf9SSasha Levin 
21699c74cf9SSasha Levin static int notify_vq(struct kvm *kvm, void *dev, u32 vq)
21799c74cf9SSasha Levin {
21899c74cf9SSasha Levin 	struct bln_dev *bdev = dev;
21999c74cf9SSasha Levin 
22099c74cf9SSasha Levin 	thread_pool__do_job(&bdev->jobs[vq]);
22199c74cf9SSasha Levin 
22299c74cf9SSasha Levin 	return 0;
22399c74cf9SSasha Levin }
22499c74cf9SSasha Levin 
22599c74cf9SSasha Levin static int get_pfn_vq(struct kvm *kvm, void *dev, u32 vq)
22699c74cf9SSasha Levin {
22799c74cf9SSasha Levin 	struct bln_dev *bdev = dev;
22899c74cf9SSasha Levin 
22999c74cf9SSasha Levin 	return bdev->vqs[vq].pfn;
23099c74cf9SSasha Levin }
23199c74cf9SSasha Levin 
23299c74cf9SSasha Levin static int get_size_vq(struct kvm *kvm, void *dev, u32 vq)
23399c74cf9SSasha Levin {
23499c74cf9SSasha Levin 	return VIRTIO_BLN_QUEUE_SIZE;
23582d2f21eSSasha Levin }
23682d2f21eSSasha Levin 
237*1c47ce69SSasha Levin struct virtio_ops bln_dev_virtio_ops = (struct virtio_ops) {
23899c74cf9SSasha Levin 	.set_config		= set_config,
23999c74cf9SSasha Levin 	.get_config		= get_config,
24099c74cf9SSasha Levin 	.get_host_features	= get_host_features,
24199c74cf9SSasha Levin 	.set_guest_features	= set_guest_features,
24299c74cf9SSasha Levin 	.init_vq		= init_vq,
24399c74cf9SSasha Levin 	.notify_vq		= notify_vq,
24499c74cf9SSasha Levin 	.get_pfn_vq		= get_pfn_vq,
24599c74cf9SSasha Levin 	.get_size_vq		= get_size_vq,
24699c74cf9SSasha Levin };
2474b2e0a7aSSasha Levin 
248*1c47ce69SSasha Levin void virtio_bln__init(struct kvm *kvm)
249*1c47ce69SSasha Levin {
250*1c47ce69SSasha Levin 	kvm_ipc__register_handler(KVM_IPC_BALLOON, handle_mem);
251*1c47ce69SSasha Levin 	kvm_ipc__register_handler(KVM_IPC_STAT, virtio_bln__print_stats);
252*1c47ce69SSasha Levin 
253*1c47ce69SSasha Levin 	bdev.stat_waitfd	= eventfd(0, 0);
254*1c47ce69SSasha Levin 	memset(&bdev.config, 0, sizeof(struct virtio_balloon_config));
255*1c47ce69SSasha Levin 
256*1c47ce69SSasha Levin 	virtio_trans_init(&bdev.vtrans, VIRTIO_PCI);
257*1c47ce69SSasha Levin 	bdev.vtrans.trans_ops->init(kvm, &bdev.vtrans, &bdev, PCI_DEVICE_ID_VIRTIO_BLN,
258*1c47ce69SSasha Levin 					VIRTIO_ID_BALLOON, PCI_CLASS_BLN);
259*1c47ce69SSasha Levin 	bdev.vtrans.virtio_ops = &bln_dev_virtio_ops;
260*1c47ce69SSasha Levin 
261312c62d1SSasha Levin 	if (compat_id != -1)
262312c62d1SSasha Levin 		compat_id = compat__add_message("virtio-balloon device was not detected",
2634b2e0a7aSSasha Levin 						"While you have requested a virtio-balloon device, "
264fc835ab3SSasha Levin 						"the guest kernel did not initialize it.\n"
265fc835ab3SSasha Levin 						"Please make sure that the guest kernel was "
266fc835ab3SSasha Levin 						"compiled with CONFIG_VIRTIO_BALLOON=y enabled "
267fc835ab3SSasha Levin 						"in its .config");
26882d2f21eSSasha Levin }
269