xref: /kvmtool/virtio/balloon.c (revision 99c74cf9e2653308840d42d40ba61dd49543b2ba)
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"
1082d2f21eSSasha Levin #include "kvm/ioeventfd.h"
114b2e0a7aSSasha Levin #include "kvm/guest_compat.h"
12*99c74cf9SSasha Levin #include "kvm/virtio-pci.h"
1382d2f21eSSasha Levin 
1482d2f21eSSasha Levin #include <linux/virtio_ring.h>
1582d2f21eSSasha Levin #include <linux/virtio_balloon.h>
1682d2f21eSSasha Levin 
17*99c74cf9SSasha 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>
22*99c74cf9SSasha Levin #include <sys/mman.h>
2382d2f21eSSasha Levin #include <pthread.h>
2482d2f21eSSasha Levin 
25bc10d2c1SSasha Levin #define NUM_VIRT_QUEUES		3
2682d2f21eSSasha Levin #define VIRTIO_BLN_QUEUE_SIZE	128
2782d2f21eSSasha Levin #define VIRTIO_BLN_INFLATE	0
2882d2f21eSSasha Levin #define VIRTIO_BLN_DEFLATE	1
29bc10d2c1SSasha Levin #define VIRTIO_BLN_STATS	2
3082d2f21eSSasha Levin 
3182d2f21eSSasha Levin struct bln_dev {
3282d2f21eSSasha Levin 	struct list_head	list;
33*99c74cf9SSasha Levin 	struct virtio_pci	vpci;
3482d2f21eSSasha Levin 
35*99c74cf9SSasha Levin 	u32			features;
3682d2f21eSSasha Levin 
3782d2f21eSSasha Levin 	/* virtio queue */
3882d2f21eSSasha Levin 	struct virt_queue	vqs[NUM_VIRT_QUEUES];
395cac5d9cSSasha Levin 	struct thread_pool__job	jobs[NUM_VIRT_QUEUES];
4082d2f21eSSasha Levin 
41bc10d2c1SSasha Levin 	struct virtio_balloon_stat stats[VIRTIO_BALLOON_S_NR];
42bc10d2c1SSasha Levin 	struct virtio_balloon_stat *cur_stat;
43bc10d2c1SSasha Levin 	u32			cur_stat_head;
44bc10d2c1SSasha Levin 	u16			stat_count;
45bc10d2c1SSasha Levin 	int			stat_waitfd;
46bc10d2c1SSasha Levin 
474b2e0a7aSSasha Levin 	int			compat_id;
4882d2f21eSSasha Levin 	struct virtio_balloon_config config;
4982d2f21eSSasha Levin };
5082d2f21eSSasha Levin 
5182d2f21eSSasha Levin static struct bln_dev bdev;
5282d2f21eSSasha Levin extern struct kvm *kvm;
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;
6082d2f21eSSasha Levin 
6182d2f21eSSasha Levin 	head		= virt_queue__get_iov(queue, iov, &out, &in, kvm);
6282d2f21eSSasha Levin 	ptrs		= iov[0].iov_base;
6382d2f21eSSasha Levin 	len		= iov[0].iov_len / sizeof(u32);
6482d2f21eSSasha Levin 
6582d2f21eSSasha Levin 	for (i = 0 ; i < len ; i++) {
6682d2f21eSSasha Levin 		void *guest_ptr;
6782d2f21eSSasha Levin 
6882d2f21eSSasha Levin 		guest_ptr = guest_flat_to_host(kvm, ptrs[i] << VIRTIO_BALLOON_PFN_SHIFT);
6982d2f21eSSasha Levin 		if (queue == &bdev->vqs[VIRTIO_BLN_INFLATE]) {
7082d2f21eSSasha Levin 			madvise(guest_ptr, 1 << VIRTIO_BALLOON_PFN_SHIFT, MADV_DONTNEED);
7182d2f21eSSasha Levin 			bdev->config.actual++;
72bc10d2c1SSasha Levin 		} else if (queue == &bdev->vqs[VIRTIO_BLN_DEFLATE]) {
7382d2f21eSSasha Levin 			bdev->config.actual--;
7482d2f21eSSasha Levin 		}
7582d2f21eSSasha Levin 	}
7682d2f21eSSasha Levin 
7782d2f21eSSasha Levin 	virt_queue__set_used_elem(queue, head, len);
7882d2f21eSSasha Levin 
7982d2f21eSSasha Levin 	return true;
8082d2f21eSSasha Levin }
8182d2f21eSSasha Levin 
82bc10d2c1SSasha Levin static bool virtio_bln_do_stat_request(struct kvm *kvm, struct bln_dev *bdev, struct virt_queue *queue)
83bc10d2c1SSasha Levin {
84bc10d2c1SSasha Levin 	struct iovec iov[VIRTIO_BLN_QUEUE_SIZE];
85bc10d2c1SSasha Levin 	u16 out, in, head;
86bc10d2c1SSasha Levin 	struct virtio_balloon_stat *stat;
87bc10d2c1SSasha Levin 	u64 wait_val = 1;
88bc10d2c1SSasha Levin 
89bc10d2c1SSasha Levin 	head = virt_queue__get_iov(queue, iov, &out, &in, kvm);
90bc10d2c1SSasha Levin 	stat = iov[0].iov_base;
91bc10d2c1SSasha Levin 
92bc10d2c1SSasha Levin 	/* Initial empty stat buffer */
93bc10d2c1SSasha Levin 	if (bdev->cur_stat == NULL) {
94bc10d2c1SSasha Levin 		bdev->cur_stat = stat;
95bc10d2c1SSasha Levin 		bdev->cur_stat_head = head;
96bc10d2c1SSasha Levin 
97bc10d2c1SSasha Levin 		return true;
98bc10d2c1SSasha Levin 	}
99bc10d2c1SSasha Levin 
100bc10d2c1SSasha Levin 	memcpy(bdev->stats, stat, iov[0].iov_len);
101bc10d2c1SSasha Levin 
102bc10d2c1SSasha Levin 	bdev->stat_count = iov[0].iov_len / sizeof(struct virtio_balloon_stat);
103bc10d2c1SSasha Levin 	bdev->cur_stat = stat;
104bc10d2c1SSasha Levin 	bdev->cur_stat_head = head;
105bc10d2c1SSasha Levin 
106bc10d2c1SSasha Levin 	if (write(bdev->stat_waitfd, &wait_val, sizeof(wait_val)) <= 0)
107bc10d2c1SSasha Levin 		return -EFAULT;
108bc10d2c1SSasha Levin 
109bc10d2c1SSasha Levin 	return 1;
110bc10d2c1SSasha Levin }
111bc10d2c1SSasha Levin 
11282d2f21eSSasha Levin static void virtio_bln_do_io(struct kvm *kvm, void *param)
11382d2f21eSSasha Levin {
11482d2f21eSSasha Levin 	struct virt_queue *vq = param;
11582d2f21eSSasha Levin 
116bc10d2c1SSasha Levin 	if (vq == &bdev.vqs[VIRTIO_BLN_STATS]) {
117bc10d2c1SSasha Levin 		virtio_bln_do_stat_request(kvm, &bdev, vq);
118*99c74cf9SSasha Levin 		virtio_pci__signal_vq(kvm, &bdev.vpci, VIRTIO_BLN_STATS);
119bc10d2c1SSasha Levin 		return;
120bc10d2c1SSasha Levin 	}
121bc10d2c1SSasha Levin 
12282d2f21eSSasha Levin 	while (virt_queue__available(vq)) {
12382d2f21eSSasha Levin 		virtio_bln_do_io_request(kvm, &bdev, vq);
124*99c74cf9SSasha Levin 		virtio_pci__signal_vq(kvm, &bdev.vpci, vq - bdev.vqs);
12582d2f21eSSasha Levin 	}
12682d2f21eSSasha Levin }
12782d2f21eSSasha Levin 
12882d2f21eSSasha Levin static void ioevent_callback(struct kvm *kvm, void *param)
12982d2f21eSSasha Levin {
13082d2f21eSSasha Levin 	thread_pool__do_job(param);
13182d2f21eSSasha Levin }
13282d2f21eSSasha Levin 
133bc10d2c1SSasha Levin static int virtio_bln__collect_stats(void)
134bc10d2c1SSasha Levin {
135bc10d2c1SSasha Levin 	u64 tmp;
136bc10d2c1SSasha Levin 
137bc10d2c1SSasha Levin 	virt_queue__set_used_elem(&bdev.vqs[VIRTIO_BLN_STATS], bdev.cur_stat_head,
138bc10d2c1SSasha Levin 				  sizeof(struct virtio_balloon_stat));
139*99c74cf9SSasha Levin 	virtio_pci__signal_vq(kvm, &bdev.vpci, VIRTIO_BLN_STATS);
140bc10d2c1SSasha Levin 
141bc10d2c1SSasha Levin 	if (read(bdev.stat_waitfd, &tmp, sizeof(tmp)) <= 0)
142bc10d2c1SSasha Levin 		return -EFAULT;
143bc10d2c1SSasha Levin 
144bc10d2c1SSasha Levin 	return 0;
145bc10d2c1SSasha Levin }
146bc10d2c1SSasha Levin 
147bc10d2c1SSasha Levin static int virtio_bln__print_stats(void)
148bc10d2c1SSasha Levin {
149bc10d2c1SSasha Levin 	u16 i;
150bc10d2c1SSasha Levin 
151bc10d2c1SSasha Levin 	if (virtio_bln__collect_stats() < 0)
152bc10d2c1SSasha Levin 		return -EFAULT;
153bc10d2c1SSasha Levin 
154bc10d2c1SSasha Levin 	printf("\n\n\t*** Guest memory statistics ***\n\n");
155bc10d2c1SSasha Levin 	for (i = 0; i < bdev.stat_count; i++) {
156bc10d2c1SSasha Levin 		switch (bdev.stats[i].tag) {
157bc10d2c1SSasha Levin 		case VIRTIO_BALLOON_S_SWAP_IN:
158bc10d2c1SSasha Levin 			printf("The amount of memory that has been swapped in (in bytes):");
159bc10d2c1SSasha Levin 			break;
160bc10d2c1SSasha Levin 		case VIRTIO_BALLOON_S_SWAP_OUT:
161bc10d2c1SSasha Levin 			printf("The amount of memory that has been swapped out to disk (in bytes):");
162bc10d2c1SSasha Levin 			break;
163bc10d2c1SSasha Levin 		case VIRTIO_BALLOON_S_MAJFLT:
164bc10d2c1SSasha Levin 			printf("The number of major page faults that have occurred:");
165bc10d2c1SSasha Levin 			break;
166bc10d2c1SSasha Levin 		case VIRTIO_BALLOON_S_MINFLT:
167bc10d2c1SSasha Levin 			printf("The number of minor page faults that have occurred:");
168bc10d2c1SSasha Levin 			break;
169bc10d2c1SSasha Levin 		case VIRTIO_BALLOON_S_MEMFREE:
170bc10d2c1SSasha Levin 			printf("The amount of memory not being used for any purpose (in bytes):");
171bc10d2c1SSasha Levin 			break;
172bc10d2c1SSasha Levin 		case VIRTIO_BALLOON_S_MEMTOT:
173bc10d2c1SSasha Levin 			printf("The total amount of memory available (in bytes):");
174bc10d2c1SSasha Levin 			break;
175bc10d2c1SSasha Levin 		}
176bc10d2c1SSasha Levin 		printf("%llu\n", bdev.stats[i].val);
177bc10d2c1SSasha Levin 	}
178bc10d2c1SSasha Levin 	printf("\n");
179bc10d2c1SSasha Levin 
180bc10d2c1SSasha Levin 	return 0;
181bc10d2c1SSasha Levin }
182bc10d2c1SSasha Levin 
18382d2f21eSSasha Levin static void handle_sigmem(int sig)
18482d2f21eSSasha Levin {
18542bcd3eeSLiming Wang 	if (sig == SIGKVMADDMEM) {
18682d2f21eSSasha Levin 		bdev.config.num_pages += 256;
187bc10d2c1SSasha Levin 	} else if (sig == SIGKVMDELMEM) {
18842bcd3eeSLiming Wang 		if (bdev.config.num_pages < 256)
18942bcd3eeSLiming Wang 			return;
19042bcd3eeSLiming Wang 
19182d2f21eSSasha Levin 		bdev.config.num_pages -= 256;
192bc10d2c1SSasha Levin 	} else if (sig == SIGKVMMEMSTAT) {
193bc10d2c1SSasha Levin 		virtio_bln__print_stats();
194bc10d2c1SSasha Levin 
195bc10d2c1SSasha Levin 		return;
19642bcd3eeSLiming Wang 	}
19782d2f21eSSasha Levin 
19882d2f21eSSasha Levin 	/* Notify that the configuration space has changed */
199*99c74cf9SSasha Levin 	virtio_pci__signal_config(kvm, &bdev.vpci);
200*99c74cf9SSasha Levin }
201*99c74cf9SSasha Levin 
202*99c74cf9SSasha Levin static void set_config(struct kvm *kvm, void *dev, u8 data, u32 offset)
203*99c74cf9SSasha Levin {
204*99c74cf9SSasha Levin 	struct bln_dev *bdev = dev;
205*99c74cf9SSasha Levin 
206*99c74cf9SSasha Levin 	((u8 *)(&bdev->config))[offset] = data;
207*99c74cf9SSasha Levin }
208*99c74cf9SSasha Levin 
209*99c74cf9SSasha Levin static u8 get_config(struct kvm *kvm, void *dev, u32 offset)
210*99c74cf9SSasha Levin {
211*99c74cf9SSasha Levin 	struct bln_dev *bdev = dev;
212*99c74cf9SSasha Levin 
213*99c74cf9SSasha Levin 	return ((u8 *)(&bdev->config))[offset];
214*99c74cf9SSasha Levin }
215*99c74cf9SSasha Levin 
216*99c74cf9SSasha Levin static u32 get_host_features(struct kvm *kvm, void *dev)
217*99c74cf9SSasha Levin {
218*99c74cf9SSasha Levin 	return 1 << VIRTIO_BALLOON_F_STATS_VQ;
219*99c74cf9SSasha Levin }
220*99c74cf9SSasha Levin 
221*99c74cf9SSasha Levin static void set_guest_features(struct kvm *kvm, void *dev, u32 features)
222*99c74cf9SSasha Levin {
223*99c74cf9SSasha Levin 	struct bln_dev *bdev = dev;
224*99c74cf9SSasha Levin 
225*99c74cf9SSasha Levin 	bdev->features = features;
226*99c74cf9SSasha Levin }
227*99c74cf9SSasha Levin 
228*99c74cf9SSasha Levin static int init_vq(struct kvm *kvm, void *dev, u32 vq, u32 pfn)
229*99c74cf9SSasha Levin {
230*99c74cf9SSasha Levin 	struct bln_dev *bdev = dev;
231*99c74cf9SSasha Levin 	struct virt_queue *queue;
232*99c74cf9SSasha Levin 	void *p;
233*99c74cf9SSasha Levin 	struct ioevent ioevent;
234*99c74cf9SSasha Levin 
235*99c74cf9SSasha Levin 	compat__remove_message(bdev->compat_id);
236*99c74cf9SSasha Levin 
237*99c74cf9SSasha Levin 	queue			= &bdev->vqs[vq];
238*99c74cf9SSasha Levin 	queue->pfn		= pfn;
239*99c74cf9SSasha Levin 	p			= guest_pfn_to_host(kvm, queue->pfn);
240*99c74cf9SSasha Levin 
241*99c74cf9SSasha Levin 	thread_pool__init_job(&bdev->jobs[vq], kvm, virtio_bln_do_io, queue);
242*99c74cf9SSasha Levin 	vring_init(&queue->vring, VIRTIO_BLN_QUEUE_SIZE, p, VIRTIO_PCI_VRING_ALIGN);
243*99c74cf9SSasha Levin 
244*99c74cf9SSasha Levin 	ioevent = (struct ioevent) {
245*99c74cf9SSasha Levin 		.io_addr	= bdev->vpci.base_addr + VIRTIO_PCI_QUEUE_NOTIFY,
246*99c74cf9SSasha Levin 		.io_len		= sizeof(u16),
247*99c74cf9SSasha Levin 		.fn		= ioevent_callback,
248*99c74cf9SSasha Levin 		.fn_ptr		= &bdev->jobs[vq],
249*99c74cf9SSasha Levin 		.datamatch	= vq,
250*99c74cf9SSasha Levin 		.fn_kvm		= kvm,
251*99c74cf9SSasha Levin 		.fd		= eventfd(0, 0),
252*99c74cf9SSasha Levin 	};
253*99c74cf9SSasha Levin 
254*99c74cf9SSasha Levin 	ioeventfd__add_event(&ioevent);
255*99c74cf9SSasha Levin 
256*99c74cf9SSasha Levin 	return 0;
257*99c74cf9SSasha Levin }
258*99c74cf9SSasha Levin 
259*99c74cf9SSasha Levin static int notify_vq(struct kvm *kvm, void *dev, u32 vq)
260*99c74cf9SSasha Levin {
261*99c74cf9SSasha Levin 	struct bln_dev *bdev = dev;
262*99c74cf9SSasha Levin 
263*99c74cf9SSasha Levin 	thread_pool__do_job(&bdev->jobs[vq]);
264*99c74cf9SSasha Levin 
265*99c74cf9SSasha Levin 	return 0;
266*99c74cf9SSasha Levin }
267*99c74cf9SSasha Levin 
268*99c74cf9SSasha Levin static int get_pfn_vq(struct kvm *kvm, void *dev, u32 vq)
269*99c74cf9SSasha Levin {
270*99c74cf9SSasha Levin 	struct bln_dev *bdev = dev;
271*99c74cf9SSasha Levin 
272*99c74cf9SSasha Levin 	return bdev->vqs[vq].pfn;
273*99c74cf9SSasha Levin }
274*99c74cf9SSasha Levin 
275*99c74cf9SSasha Levin static int get_size_vq(struct kvm *kvm, void *dev, u32 vq)
276*99c74cf9SSasha Levin {
277*99c74cf9SSasha Levin 	return VIRTIO_BLN_QUEUE_SIZE;
27882d2f21eSSasha Levin }
27982d2f21eSSasha Levin 
28082d2f21eSSasha Levin void virtio_bln__init(struct kvm *kvm)
28182d2f21eSSasha Levin {
28282d2f21eSSasha Levin 	signal(SIGKVMADDMEM, handle_sigmem);
28382d2f21eSSasha Levin 	signal(SIGKVMDELMEM, handle_sigmem);
284bc10d2c1SSasha Levin 	signal(SIGKVMMEMSTAT, handle_sigmem);
28582d2f21eSSasha Levin 
286bc10d2c1SSasha Levin 	bdev.stat_waitfd	= eventfd(0, 0);
28782d2f21eSSasha Levin 	memset(&bdev.config, 0, sizeof(struct virtio_balloon_config));
28882d2f21eSSasha Levin 
289*99c74cf9SSasha Levin 	virtio_pci__init(kvm, &bdev.vpci, &bdev, PCI_DEVICE_ID_VIRTIO_BLN, VIRTIO_ID_BALLOON);
290*99c74cf9SSasha Levin 	bdev.vpci.ops = (struct virtio_pci_ops) {
291*99c74cf9SSasha Levin 		.set_config		= set_config,
292*99c74cf9SSasha Levin 		.get_config		= get_config,
293*99c74cf9SSasha Levin 		.get_host_features	= get_host_features,
294*99c74cf9SSasha Levin 		.set_guest_features	= set_guest_features,
295*99c74cf9SSasha Levin 		.init_vq		= init_vq,
296*99c74cf9SSasha Levin 		.notify_vq		= notify_vq,
297*99c74cf9SSasha Levin 		.get_pfn_vq		= get_pfn_vq,
298*99c74cf9SSasha Levin 		.get_size_vq		= get_size_vq,
299*99c74cf9SSasha Levin 	};
3004b2e0a7aSSasha Levin 
3014b2e0a7aSSasha Levin 	bdev.compat_id = compat__add_message("virtio-balloon device was not detected",
3024b2e0a7aSSasha Levin 						"While you have requested a virtio-balloon device, "
3034b2e0a7aSSasha Levin 						"the guest kernel didn't seem to detect it.\n"
3044b2e0a7aSSasha Levin 						"Please make sure that the kernel was compiled"
3054b2e0a7aSSasha Levin 						"with CONFIG_VIRTIO_BALLOON.");
30682d2f21eSSasha Levin }
307