xref: /kvmtool/virtio/balloon.c (revision 82d2f21ed7aed9aca8db7e89dadad4a890afb70c)
1*82d2f21eSSasha Levin #include "kvm/virtio-balloon.h"
2*82d2f21eSSasha Levin 
3*82d2f21eSSasha Levin #include "kvm/virtio-pci-dev.h"
4*82d2f21eSSasha Levin 
5*82d2f21eSSasha Levin #include "kvm/disk-image.h"
6*82d2f21eSSasha Levin #include "kvm/virtio.h"
7*82d2f21eSSasha Levin #include "kvm/ioport.h"
8*82d2f21eSSasha Levin #include "kvm/util.h"
9*82d2f21eSSasha Levin #include "kvm/kvm.h"
10*82d2f21eSSasha Levin #include "kvm/pci.h"
11*82d2f21eSSasha Levin #include "kvm/threadpool.h"
12*82d2f21eSSasha Levin #include "kvm/irq.h"
13*82d2f21eSSasha Levin #include "kvm/ioeventfd.h"
14*82d2f21eSSasha Levin 
15*82d2f21eSSasha Levin #include <linux/virtio_ring.h>
16*82d2f21eSSasha Levin #include <linux/virtio_balloon.h>
17*82d2f21eSSasha Levin 
18*82d2f21eSSasha Levin #include <linux/list.h>
19*82d2f21eSSasha Levin #include <fcntl.h>
20*82d2f21eSSasha Levin #include <sys/types.h>
21*82d2f21eSSasha Levin #include <sys/stat.h>
22*82d2f21eSSasha Levin #include <pthread.h>
23*82d2f21eSSasha Levin 
24*82d2f21eSSasha Levin #define NUM_VIRT_QUEUES		2
25*82d2f21eSSasha Levin #define VIRTIO_BLN_QUEUE_SIZE	128
26*82d2f21eSSasha Levin #define VIRTIO_BLN_INFLATE	0
27*82d2f21eSSasha Levin #define VIRTIO_BLN_DEFLATE	1
28*82d2f21eSSasha Levin 
29*82d2f21eSSasha Levin struct bln_dev {
30*82d2f21eSSasha Levin 	struct pci_device_header pci_hdr;
31*82d2f21eSSasha Levin 	struct list_head	list;
32*82d2f21eSSasha Levin 
33*82d2f21eSSasha Levin 	u16			base_addr;
34*82d2f21eSSasha Levin 	u8			status;
35*82d2f21eSSasha Levin 	u8			isr;
36*82d2f21eSSasha Levin 	u16			config_vector;
37*82d2f21eSSasha Levin 	u32			host_features;
38*82d2f21eSSasha Levin 
39*82d2f21eSSasha Levin 	/* virtio queue */
40*82d2f21eSSasha Levin 	u16			queue_selector;
41*82d2f21eSSasha Levin 	struct virt_queue	vqs[NUM_VIRT_QUEUES];
42*82d2f21eSSasha Levin 	void			*jobs[NUM_VIRT_QUEUES];
43*82d2f21eSSasha Levin 
44*82d2f21eSSasha Levin 	struct virtio_balloon_config config;
45*82d2f21eSSasha Levin };
46*82d2f21eSSasha Levin 
47*82d2f21eSSasha Levin static struct bln_dev bdev;
48*82d2f21eSSasha Levin extern struct kvm *kvm;
49*82d2f21eSSasha Levin 
50*82d2f21eSSasha Levin static bool virtio_bln_dev_in(void *data, unsigned long offset, int size, u32 count)
51*82d2f21eSSasha Levin {
52*82d2f21eSSasha Levin 	u8 *config_space = (u8 *) &bdev.config;
53*82d2f21eSSasha Levin 
54*82d2f21eSSasha Levin 	if (size != 1 || count != 1)
55*82d2f21eSSasha Levin 		return false;
56*82d2f21eSSasha Levin 
57*82d2f21eSSasha Levin 	ioport__write8(data, config_space[offset - VIRTIO_MSI_CONFIG_VECTOR]);
58*82d2f21eSSasha Levin 
59*82d2f21eSSasha Levin 	return true;
60*82d2f21eSSasha Levin }
61*82d2f21eSSasha Levin 
62*82d2f21eSSasha Levin static bool virtio_bln_dev_out(void *data, unsigned long offset, int size, u32 count)
63*82d2f21eSSasha Levin {
64*82d2f21eSSasha Levin 	u8 *config_space = (u8 *) &bdev.config;
65*82d2f21eSSasha Levin 
66*82d2f21eSSasha Levin 	if (size != 1 || count != 1)
67*82d2f21eSSasha Levin 		return false;
68*82d2f21eSSasha Levin 
69*82d2f21eSSasha Levin 	config_space[offset - VIRTIO_MSI_CONFIG_VECTOR] = *(u8 *)data;
70*82d2f21eSSasha Levin 
71*82d2f21eSSasha Levin 	return true;
72*82d2f21eSSasha Levin }
73*82d2f21eSSasha Levin 
74*82d2f21eSSasha Levin static bool virtio_bln_pci_io_in(struct ioport *ioport, struct kvm *kvm, u16 port, void *data, int size, u32 count)
75*82d2f21eSSasha Levin {
76*82d2f21eSSasha Levin 	unsigned long offset;
77*82d2f21eSSasha Levin 	bool ret = true;
78*82d2f21eSSasha Levin 
79*82d2f21eSSasha Levin 	offset = port - bdev.base_addr;
80*82d2f21eSSasha Levin 
81*82d2f21eSSasha Levin 	switch (offset) {
82*82d2f21eSSasha Levin 	case VIRTIO_PCI_HOST_FEATURES:
83*82d2f21eSSasha Levin 		ioport__write32(data, bdev.host_features);
84*82d2f21eSSasha Levin 		break;
85*82d2f21eSSasha Levin 	case VIRTIO_PCI_GUEST_FEATURES:
86*82d2f21eSSasha Levin 	case VIRTIO_PCI_QUEUE_SEL:
87*82d2f21eSSasha Levin 	case VIRTIO_PCI_QUEUE_NOTIFY:
88*82d2f21eSSasha Levin 		ret		= false;
89*82d2f21eSSasha Levin 		break;
90*82d2f21eSSasha Levin 	case VIRTIO_PCI_QUEUE_PFN:
91*82d2f21eSSasha Levin 		ioport__write32(data, bdev.vqs[bdev.queue_selector].pfn);
92*82d2f21eSSasha Levin 		break;
93*82d2f21eSSasha Levin 	case VIRTIO_PCI_QUEUE_NUM:
94*82d2f21eSSasha Levin 		ioport__write16(data, VIRTIO_BLN_QUEUE_SIZE);
95*82d2f21eSSasha Levin 		break;
96*82d2f21eSSasha Levin 	case VIRTIO_PCI_STATUS:
97*82d2f21eSSasha Levin 		ioport__write8(data, bdev.status);
98*82d2f21eSSasha Levin 		break;
99*82d2f21eSSasha Levin 	case VIRTIO_PCI_ISR:
100*82d2f21eSSasha Levin 		ioport__write8(data, bdev.isr);
101*82d2f21eSSasha Levin 		kvm__irq_line(kvm, bdev.pci_hdr.irq_line, VIRTIO_IRQ_LOW);
102*82d2f21eSSasha Levin 		bdev.isr = VIRTIO_IRQ_LOW;
103*82d2f21eSSasha Levin 		break;
104*82d2f21eSSasha Levin 	default:
105*82d2f21eSSasha Levin 		ret = virtio_bln_dev_in(data, offset, size, count);
106*82d2f21eSSasha Levin 		break;
107*82d2f21eSSasha Levin 	};
108*82d2f21eSSasha Levin 
109*82d2f21eSSasha Levin 	return ret;
110*82d2f21eSSasha Levin }
111*82d2f21eSSasha Levin 
112*82d2f21eSSasha Levin static bool virtio_bln_do_io_request(struct kvm *kvm, struct bln_dev *bdev, struct virt_queue *queue)
113*82d2f21eSSasha Levin {
114*82d2f21eSSasha Levin 	struct iovec iov[VIRTIO_BLN_QUEUE_SIZE];
115*82d2f21eSSasha Levin 	unsigned int len = 0;
116*82d2f21eSSasha Levin 	u16 out, in, head;
117*82d2f21eSSasha Levin 	u32 *ptrs, i;
118*82d2f21eSSasha Levin 
119*82d2f21eSSasha Levin 	head		= virt_queue__get_iov(queue, iov, &out, &in, kvm);
120*82d2f21eSSasha Levin 	ptrs		= iov[0].iov_base;
121*82d2f21eSSasha Levin 	len		= iov[0].iov_len / sizeof(u32);
122*82d2f21eSSasha Levin 
123*82d2f21eSSasha Levin 	for (i = 0 ; i < len ; i++) {
124*82d2f21eSSasha Levin 		void *guest_ptr;
125*82d2f21eSSasha Levin 
126*82d2f21eSSasha Levin 		guest_ptr = guest_flat_to_host(kvm, ptrs[i] << VIRTIO_BALLOON_PFN_SHIFT);
127*82d2f21eSSasha Levin 		if (queue == &bdev->vqs[VIRTIO_BLN_INFLATE]) {
128*82d2f21eSSasha Levin 			madvise(guest_ptr, 1 << VIRTIO_BALLOON_PFN_SHIFT, MADV_DONTNEED);
129*82d2f21eSSasha Levin 			bdev->config.actual++;
130*82d2f21eSSasha Levin 		} else {
131*82d2f21eSSasha Levin 			bdev->config.actual--;
132*82d2f21eSSasha Levin 		}
133*82d2f21eSSasha Levin 	}
134*82d2f21eSSasha Levin 
135*82d2f21eSSasha Levin 	virt_queue__set_used_elem(queue, head, len);
136*82d2f21eSSasha Levin 
137*82d2f21eSSasha Levin 	return true;
138*82d2f21eSSasha Levin }
139*82d2f21eSSasha Levin 
140*82d2f21eSSasha Levin static void virtio_bln_do_io(struct kvm *kvm, void *param)
141*82d2f21eSSasha Levin {
142*82d2f21eSSasha Levin 	struct virt_queue *vq = param;
143*82d2f21eSSasha Levin 
144*82d2f21eSSasha Levin 	while (virt_queue__available(vq)) {
145*82d2f21eSSasha Levin 		virtio_bln_do_io_request(kvm, &bdev, vq);
146*82d2f21eSSasha Levin 		virt_queue__trigger_irq(vq, bdev.pci_hdr.irq_line, &bdev.isr, kvm);
147*82d2f21eSSasha Levin 	}
148*82d2f21eSSasha Levin }
149*82d2f21eSSasha Levin 
150*82d2f21eSSasha Levin static void ioevent_callback(struct kvm *kvm, void *param)
151*82d2f21eSSasha Levin {
152*82d2f21eSSasha Levin 	thread_pool__do_job(param);
153*82d2f21eSSasha Levin }
154*82d2f21eSSasha Levin 
155*82d2f21eSSasha Levin static bool virtio_bln_pci_io_out(struct ioport *ioport, struct kvm *kvm, u16 port, void *data, int size, u32 count)
156*82d2f21eSSasha Levin {
157*82d2f21eSSasha Levin 	unsigned long offset;
158*82d2f21eSSasha Levin 	bool ret = true;
159*82d2f21eSSasha Levin 	struct ioevent ioevent;
160*82d2f21eSSasha Levin 
161*82d2f21eSSasha Levin 	offset = port - bdev.base_addr;
162*82d2f21eSSasha Levin 
163*82d2f21eSSasha Levin 	switch (offset) {
164*82d2f21eSSasha Levin 	case VIRTIO_MSI_QUEUE_VECTOR:
165*82d2f21eSSasha Levin 	case VIRTIO_PCI_GUEST_FEATURES:
166*82d2f21eSSasha Levin 		break;
167*82d2f21eSSasha Levin 	case VIRTIO_PCI_QUEUE_PFN: {
168*82d2f21eSSasha Levin 		struct virt_queue *queue;
169*82d2f21eSSasha Levin 		void *p;
170*82d2f21eSSasha Levin 
171*82d2f21eSSasha Levin 		queue			= &bdev.vqs[bdev.queue_selector];
172*82d2f21eSSasha Levin 		queue->pfn		= ioport__read32(data);
173*82d2f21eSSasha Levin 		p			= guest_pfn_to_host(kvm, queue->pfn);
174*82d2f21eSSasha Levin 
175*82d2f21eSSasha Levin 		vring_init(&queue->vring, VIRTIO_BLN_QUEUE_SIZE, p, VIRTIO_PCI_VRING_ALIGN);
176*82d2f21eSSasha Levin 
177*82d2f21eSSasha Levin 		bdev.jobs[bdev.queue_selector] = thread_pool__add_job(kvm, virtio_bln_do_io, queue);
178*82d2f21eSSasha Levin 
179*82d2f21eSSasha Levin 		ioevent = (struct ioevent) {
180*82d2f21eSSasha Levin 			.io_addr		= bdev.base_addr + VIRTIO_PCI_QUEUE_NOTIFY,
181*82d2f21eSSasha Levin 			.io_len			= sizeof(u16),
182*82d2f21eSSasha Levin 			.fn			= ioevent_callback,
183*82d2f21eSSasha Levin 			.fn_ptr			= bdev.jobs[bdev.queue_selector],
184*82d2f21eSSasha Levin 			.datamatch		= bdev.queue_selector,
185*82d2f21eSSasha Levin 			.fn_kvm			= kvm,
186*82d2f21eSSasha Levin 			.fd			= eventfd(0, 0),
187*82d2f21eSSasha Levin 		};
188*82d2f21eSSasha Levin 
189*82d2f21eSSasha Levin 		ioeventfd__add_event(&ioevent);
190*82d2f21eSSasha Levin 
191*82d2f21eSSasha Levin 		break;
192*82d2f21eSSasha Levin 	}
193*82d2f21eSSasha Levin 	case VIRTIO_PCI_QUEUE_SEL:
194*82d2f21eSSasha Levin 		bdev.queue_selector	= ioport__read16(data);
195*82d2f21eSSasha Levin 		break;
196*82d2f21eSSasha Levin 	case VIRTIO_PCI_QUEUE_NOTIFY: {
197*82d2f21eSSasha Levin 		u16 queue_index;
198*82d2f21eSSasha Levin 		queue_index		= ioport__read16(data);
199*82d2f21eSSasha Levin 		thread_pool__do_job(bdev.jobs[queue_index]);
200*82d2f21eSSasha Levin 		break;
201*82d2f21eSSasha Levin 	}
202*82d2f21eSSasha Levin 	case VIRTIO_PCI_STATUS:
203*82d2f21eSSasha Levin 		bdev.status		= ioport__read8(data);
204*82d2f21eSSasha Levin 		break;
205*82d2f21eSSasha Levin 	case VIRTIO_MSI_CONFIG_VECTOR:
206*82d2f21eSSasha Levin 		bdev.config_vector	= VIRTIO_MSI_NO_VECTOR;
207*82d2f21eSSasha Levin 		break;
208*82d2f21eSSasha Levin 	default:
209*82d2f21eSSasha Levin 		ret = virtio_bln_dev_out(data, offset, size, count);
210*82d2f21eSSasha Levin 		break;
211*82d2f21eSSasha Levin 	};
212*82d2f21eSSasha Levin 
213*82d2f21eSSasha Levin 	return ret;
214*82d2f21eSSasha Levin }
215*82d2f21eSSasha Levin 
216*82d2f21eSSasha Levin static struct ioport_operations virtio_bln_io_ops = {
217*82d2f21eSSasha Levin 	.io_in				= virtio_bln_pci_io_in,
218*82d2f21eSSasha Levin 	.io_out				= virtio_bln_pci_io_out,
219*82d2f21eSSasha Levin };
220*82d2f21eSSasha Levin 
221*82d2f21eSSasha Levin static void handle_sigmem(int sig)
222*82d2f21eSSasha Levin {
223*82d2f21eSSasha Levin 	if (sig == SIGKVMADDMEM)
224*82d2f21eSSasha Levin 		bdev.config.num_pages += 256;
225*82d2f21eSSasha Levin 	else
226*82d2f21eSSasha Levin 		bdev.config.num_pages -= 256;
227*82d2f21eSSasha Levin 
228*82d2f21eSSasha Levin 	/* Notify that the configuration space has changed */
229*82d2f21eSSasha Levin 	bdev.isr = VIRTIO_PCI_ISR_CONFIG;
230*82d2f21eSSasha Levin 	kvm__irq_line(kvm, bdev.pci_hdr.irq_line, 1);
231*82d2f21eSSasha Levin }
232*82d2f21eSSasha Levin 
233*82d2f21eSSasha Levin void virtio_bln__init(struct kvm *kvm)
234*82d2f21eSSasha Levin {
235*82d2f21eSSasha Levin 	u8 pin, line, dev;
236*82d2f21eSSasha Levin 	u16 bdev_base_addr;
237*82d2f21eSSasha Levin 
238*82d2f21eSSasha Levin 	signal(SIGKVMADDMEM, handle_sigmem);
239*82d2f21eSSasha Levin 	signal(SIGKVMDELMEM, handle_sigmem);
240*82d2f21eSSasha Levin 
241*82d2f21eSSasha Levin 	bdev_base_addr = ioport__register(IOPORT_EMPTY, &virtio_bln_io_ops, IOPORT_SIZE, &bdev);
242*82d2f21eSSasha Levin 
243*82d2f21eSSasha Levin 	bdev.pci_hdr = (struct pci_device_header) {
244*82d2f21eSSasha Levin 		.vendor_id		= PCI_VENDOR_ID_REDHAT_QUMRANET,
245*82d2f21eSSasha Levin 		.device_id		= PCI_DEVICE_ID_VIRTIO_BLN,
246*82d2f21eSSasha Levin 		.header_type		= PCI_HEADER_TYPE_NORMAL,
247*82d2f21eSSasha Levin 		.revision_id		= 0,
248*82d2f21eSSasha Levin 		.class			= 0x010000,
249*82d2f21eSSasha Levin 		.subsys_vendor_id	= PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET,
250*82d2f21eSSasha Levin 		.subsys_id		= VIRTIO_ID_BALLOON,
251*82d2f21eSSasha Levin 		.bar[0]			= bdev_base_addr | PCI_BASE_ADDRESS_SPACE_IO,
252*82d2f21eSSasha Levin 	};
253*82d2f21eSSasha Levin 
254*82d2f21eSSasha Levin 	bdev.base_addr = bdev_base_addr;
255*82d2f21eSSasha Levin 
256*82d2f21eSSasha Levin 	if (irq__register_device(VIRTIO_ID_RNG, &dev, &pin, &line) < 0)
257*82d2f21eSSasha Levin 		return;
258*82d2f21eSSasha Levin 
259*82d2f21eSSasha Levin 	bdev.pci_hdr.irq_pin	= pin;
260*82d2f21eSSasha Levin 	bdev.pci_hdr.irq_line	= line;
261*82d2f21eSSasha Levin 	bdev.host_features	= 0;
262*82d2f21eSSasha Levin 	memset(&bdev.config, 0, sizeof(struct virtio_balloon_config));
263*82d2f21eSSasha Levin 
264*82d2f21eSSasha Levin 	pci__register(&bdev.pci_hdr, dev);
265*82d2f21eSSasha Levin }
266