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
16867b15ccSJean-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
3682d2f21eSSasha Levin /* virtio queue */
3782d2f21eSSasha Levin struct virt_queue vqs[NUM_VIRT_QUEUES];
385cac5d9cSSasha Levin struct thread_pool__job jobs[NUM_VIRT_QUEUES];
3982d2f21eSSasha Levin
40bc10d2c1SSasha Levin struct virtio_balloon_stat stats[VIRTIO_BALLOON_S_NR];
41bc10d2c1SSasha Levin struct virtio_balloon_stat *cur_stat;
42bc10d2c1SSasha Levin u32 cur_stat_head;
43bc10d2c1SSasha Levin u16 stat_count;
44bc10d2c1SSasha Levin int stat_waitfd;
45bc10d2c1SSasha Levin
4682d2f21eSSasha Levin struct virtio_balloon_config config;
4782d2f21eSSasha Levin };
4882d2f21eSSasha Levin
4982d2f21eSSasha Levin static struct bln_dev bdev;
50312c62d1SSasha Levin static int compat_id = -1;
5182d2f21eSSasha Levin
virtio_bln_do_io_request(struct kvm * kvm,struct bln_dev * bdev,struct virt_queue * queue)5282d2f21eSSasha Levin static bool virtio_bln_do_io_request(struct kvm *kvm, struct bln_dev *bdev, struct virt_queue *queue)
5382d2f21eSSasha Levin {
5482d2f21eSSasha Levin struct iovec iov[VIRTIO_BLN_QUEUE_SIZE];
5582d2f21eSSasha Levin unsigned int len = 0;
5682d2f21eSSasha Levin u16 out, in, head;
5782d2f21eSSasha Levin u32 *ptrs, i;
58867b15ccSJean-Philippe Brucker u32 actual;
5982d2f21eSSasha Levin
6082d2f21eSSasha Levin head = virt_queue__get_iov(queue, iov, &out, &in, kvm);
6182d2f21eSSasha Levin ptrs = iov[0].iov_base;
6282d2f21eSSasha Levin len = iov[0].iov_len / sizeof(u32);
6382d2f21eSSasha Levin
64867b15ccSJean-Philippe Brucker actual = le32_to_cpu(bdev->config.actual);
6582d2f21eSSasha Levin for (i = 0 ; i < len ; i++) {
6682d2f21eSSasha Levin void *guest_ptr;
6782d2f21eSSasha Levin
68c8183442SKonstantin Khlebnikov guest_ptr = guest_flat_to_host(kvm, (u64)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);
71867b15ccSJean-Philippe Brucker actual++;
72bc10d2c1SSasha Levin } else if (queue == &bdev->vqs[VIRTIO_BLN_DEFLATE]) {
73867b15ccSJean-Philippe Brucker actual--;
7482d2f21eSSasha Levin }
7582d2f21eSSasha Levin }
76867b15ccSJean-Philippe Brucker bdev->config.actual = cpu_to_le32(actual);
7782d2f21eSSasha Levin
7882d2f21eSSasha Levin virt_queue__set_used_elem(queue, head, len);
7982d2f21eSSasha Levin
8082d2f21eSSasha Levin return true;
8182d2f21eSSasha Levin }
8282d2f21eSSasha Levin
virtio_bln_do_stat_request(struct kvm * kvm,struct bln_dev * bdev,struct virt_queue * queue)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
virtio_bln_do_io(struct kvm * kvm,void * param)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);
11902eca50cSAsias He bdev.vdev.ops->signal_vq(kvm, &bdev.vdev, 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);
12502eca50cSAsias He bdev.vdev.ops->signal_vq(kvm, &bdev.vdev, vq - bdev.vqs);
12682d2f21eSSasha Levin }
12782d2f21eSSasha Levin }
12882d2f21eSSasha Levin
virtio_bln__collect_stats(struct kvm * kvm)1294346fd8fSSasha Levin static int virtio_bln__collect_stats(struct kvm *kvm)
130bc10d2c1SSasha Levin {
1313a13530aSKeir Fraser struct virt_queue *vq = &bdev.vqs[VIRTIO_BLN_STATS];
132bc10d2c1SSasha Levin u64 tmp;
133bc10d2c1SSasha Levin
1343a13530aSKeir Fraser /* Exit if the queue is not set up. */
135609ee906SJean-Philippe Brucker if (!vq->enabled)
1363a13530aSKeir Fraser return -ENODEV;
1373a13530aSKeir Fraser
1383a13530aSKeir Fraser virt_queue__set_used_elem(vq, bdev.cur_stat_head,
139bc10d2c1SSasha Levin sizeof(struct virtio_balloon_stat));
14002eca50cSAsias He bdev.vdev.ops->signal_vq(kvm, &bdev.vdev, VIRTIO_BLN_STATS);
141bc10d2c1SSasha Levin
142bc10d2c1SSasha Levin if (read(bdev.stat_waitfd, &tmp, sizeof(tmp)) <= 0)
143bc10d2c1SSasha Levin return -EFAULT;
144bc10d2c1SSasha Levin
145bc10d2c1SSasha Levin return 0;
146bc10d2c1SSasha Levin }
147bc10d2c1SSasha Levin
virtio_bln__print_stats(struct kvm * kvm,int fd,u32 type,u32 len,u8 * msg)148e1063726SSasha Levin static void virtio_bln__print_stats(struct kvm *kvm, int fd, u32 type, u32 len, u8 *msg)
149bc10d2c1SSasha Levin {
150498746b9SSasha Levin int r;
151bc10d2c1SSasha Levin
152d0dc85a8SLai Jiangshan if (WARN_ON(type != KVM_IPC_STAT || len))
153d0dc85a8SLai Jiangshan return;
154d0dc85a8SLai Jiangshan
1554346fd8fSSasha Levin if (virtio_bln__collect_stats(kvm) < 0)
1564b1addaeSSasha Levin return;
157bc10d2c1SSasha Levin
158498746b9SSasha Levin r = write(fd, bdev.stats, sizeof(bdev.stats));
159498746b9SSasha Levin if (r < 0)
160498746b9SSasha Levin pr_warning("Failed sending memory stats");
161bc10d2c1SSasha Levin }
162bc10d2c1SSasha Levin
handle_mem(struct kvm * kvm,int fd,u32 type,u32 len,u8 * msg)163e1063726SSasha Levin static void handle_mem(struct kvm *kvm, int fd, u32 type, u32 len, u8 *msg)
16482d2f21eSSasha Levin {
16549669299SLai Jiangshan int mem;
166867b15ccSJean-Philippe Brucker u32 num_pages;
1674b1addaeSSasha Levin
16849669299SLai Jiangshan if (WARN_ON(type != KVM_IPC_BALLOON || len != sizeof(int)))
16949669299SLai Jiangshan return;
17049669299SLai Jiangshan
17149669299SLai Jiangshan mem = *(int *)msg;
172867b15ccSJean-Philippe Brucker num_pages = le32_to_cpu(bdev.config.num_pages);
173867b15ccSJean-Philippe Brucker
1744b1addaeSSasha Levin if (mem > 0) {
175867b15ccSJean-Philippe Brucker num_pages += 256 * mem;
1764b1addaeSSasha Levin } else if (mem < 0) {
177867b15ccSJean-Philippe Brucker if (num_pages < (u32)(256 * (-mem)))
17842bcd3eeSLiming Wang return;
17942bcd3eeSLiming Wang
180867b15ccSJean-Philippe Brucker num_pages += 256 * mem;
18142bcd3eeSLiming Wang }
18282d2f21eSSasha Levin
183867b15ccSJean-Philippe Brucker bdev.config.num_pages = cpu_to_le32(num_pages);
184867b15ccSJean-Philippe Brucker
18582d2f21eSSasha Levin /* Notify that the configuration space has changed */
18602eca50cSAsias He bdev.vdev.ops->signal_config(kvm, &bdev.vdev);
18799c74cf9SSasha Levin }
18899c74cf9SSasha Levin
get_config(struct kvm * kvm,void * dev)189c5ae742bSSasha Levin static u8 *get_config(struct kvm *kvm, void *dev)
19099c74cf9SSasha Levin {
19199c74cf9SSasha Levin struct bln_dev *bdev = dev;
19299c74cf9SSasha Levin
193c5ae742bSSasha Levin return ((u8 *)(&bdev->config));
19499c74cf9SSasha Levin }
19599c74cf9SSasha Levin
get_config_size(struct kvm * kvm,void * dev)196e4730284SMartin Radev static size_t get_config_size(struct kvm *kvm, void *dev)
197e4730284SMartin Radev {
198e4730284SMartin Radev struct bln_dev *bdev = dev;
199e4730284SMartin Radev
200e4730284SMartin Radev return sizeof(bdev->config);
201e4730284SMartin Radev }
202e4730284SMartin Radev
get_host_features(struct kvm * kvm,void * dev)2033c8f82b8SJean-Philippe Brucker static u64 get_host_features(struct kvm *kvm, void *dev)
20499c74cf9SSasha Levin {
20599c74cf9SSasha Levin return 1 << VIRTIO_BALLOON_F_STATS_VQ;
20699c74cf9SSasha Levin }
20799c74cf9SSasha Levin
init_vq(struct kvm * kvm,void * dev,u32 vq)208609ee906SJean-Philippe Brucker static int init_vq(struct kvm *kvm, void *dev, u32 vq)
20999c74cf9SSasha Levin {
21099c74cf9SSasha Levin struct bln_dev *bdev = dev;
21199c74cf9SSasha Levin struct virt_queue *queue;
21299c74cf9SSasha Levin
213312c62d1SSasha Levin compat__remove_message(compat_id);
21499c74cf9SSasha Levin
21599c74cf9SSasha Levin queue = &bdev->vqs[vq];
216fd41cde0SJean-Philippe Brucker
217609ee906SJean-Philippe Brucker virtio_init_device_vq(kvm, &bdev->vdev, queue, VIRTIO_BLN_QUEUE_SIZE);
21899c74cf9SSasha Levin
21999c74cf9SSasha Levin thread_pool__init_job(&bdev->jobs[vq], kvm, virtio_bln_do_io, queue);
22099c74cf9SSasha Levin
22199c74cf9SSasha Levin return 0;
22299c74cf9SSasha Levin }
22399c74cf9SSasha Levin
exit_vq(struct kvm * kvm,void * dev,u32 vq)224*74af1456SEduardo Bart static void exit_vq(struct kvm *kvm, void *dev, u32 vq)
225*74af1456SEduardo Bart {
226*74af1456SEduardo Bart struct bln_dev *bdev = dev;
227*74af1456SEduardo Bart
228*74af1456SEduardo Bart thread_pool__cancel_job(&bdev->jobs[vq]);
229*74af1456SEduardo Bart }
230*74af1456SEduardo Bart
notify_vq(struct kvm * kvm,void * dev,u32 vq)23199c74cf9SSasha Levin static int notify_vq(struct kvm *kvm, void *dev, u32 vq)
23299c74cf9SSasha Levin {
23399c74cf9SSasha Levin struct bln_dev *bdev = dev;
23499c74cf9SSasha Levin
23599c74cf9SSasha Levin thread_pool__do_job(&bdev->jobs[vq]);
23699c74cf9SSasha Levin
23799c74cf9SSasha Levin return 0;
23899c74cf9SSasha Levin }
23999c74cf9SSasha Levin
get_vq(struct kvm * kvm,void * dev,u32 vq)24053fbb17bSJean-Philippe Brucker static struct virt_queue *get_vq(struct kvm *kvm, void *dev, u32 vq)
24199c74cf9SSasha Levin {
24299c74cf9SSasha Levin struct bln_dev *bdev = dev;
24399c74cf9SSasha Levin
24453fbb17bSJean-Philippe Brucker return &bdev->vqs[vq];
24599c74cf9SSasha Levin }
24699c74cf9SSasha Levin
get_size_vq(struct kvm * kvm,void * dev,u32 vq)24799c74cf9SSasha Levin static int get_size_vq(struct kvm *kvm, void *dev, u32 vq)
24899c74cf9SSasha Levin {
24999c74cf9SSasha Levin return VIRTIO_BLN_QUEUE_SIZE;
25082d2f21eSSasha Levin }
25182d2f21eSSasha Levin
set_size_vq(struct kvm * kvm,void * dev,u32 vq,int size)25213cf07d9SWill Deacon static int set_size_vq(struct kvm *kvm, void *dev, u32 vq, int size)
25313cf07d9SWill Deacon {
25413cf07d9SWill Deacon /* FIXME: dynamic */
25513cf07d9SWill Deacon return size;
25613cf07d9SWill Deacon }
25713cf07d9SWill Deacon
get_vq_count(struct kvm * kvm,void * dev)25831e0eaccSMartin Radev static unsigned int get_vq_count(struct kvm *kvm, void *dev)
259b98ac591SJean-Philippe Brucker {
260b98ac591SJean-Philippe Brucker return NUM_VIRT_QUEUES;
261b98ac591SJean-Philippe Brucker }
262b98ac591SJean-Philippe Brucker
26315542babSAndre Przywara struct virtio_ops bln_dev_virtio_ops = {
26499c74cf9SSasha Levin .get_config = get_config,
265e4730284SMartin Radev .get_config_size = get_config_size,
26699c74cf9SSasha Levin .get_host_features = get_host_features,
26799c74cf9SSasha Levin .init_vq = init_vq,
268*74af1456SEduardo Bart .exit_vq = exit_vq,
26999c74cf9SSasha Levin .notify_vq = notify_vq,
27053fbb17bSJean-Philippe Brucker .get_vq = get_vq,
27199c74cf9SSasha Levin .get_size_vq = get_size_vq,
27213cf07d9SWill Deacon .set_size_vq = set_size_vq,
273b98ac591SJean-Philippe Brucker .get_vq_count = get_vq_count,
27499c74cf9SSasha Levin };
2754b2e0a7aSSasha Levin
virtio_bln__init(struct kvm * kvm)276d06db2fdSSasha Levin int virtio_bln__init(struct kvm *kvm)
2771c47ce69SSasha Levin {
278db927775SAlexandru Elisei int r;
279db927775SAlexandru Elisei
280d06db2fdSSasha Levin if (!kvm->cfg.balloon)
281d06db2fdSSasha Levin return 0;
282d06db2fdSSasha Levin
2831c47ce69SSasha Levin kvm_ipc__register_handler(KVM_IPC_BALLOON, handle_mem);
2841c47ce69SSasha Levin kvm_ipc__register_handler(KVM_IPC_STAT, virtio_bln__print_stats);
2851c47ce69SSasha Levin
2861c47ce69SSasha Levin bdev.stat_waitfd = eventfd(0, 0);
2871c47ce69SSasha Levin memset(&bdev.config, 0, sizeof(struct virtio_balloon_config));
2881c47ce69SSasha Levin
289db927775SAlexandru Elisei r = virtio_init(kvm, &bdev, &bdev.vdev, &bln_dev_virtio_ops,
2909b46ebc5SRajnesh Kanwal kvm->cfg.virtio_transport, PCI_DEVICE_ID_VIRTIO_BLN,
291ae06ce71SWill Deacon VIRTIO_ID_BALLOON, PCI_CLASS_BLN);
292db927775SAlexandru Elisei if (r < 0)
293db927775SAlexandru Elisei return r;
2941c47ce69SSasha Levin
295d278197dSAsias He if (compat_id == -1)
29652f34d2cSAsias He compat_id = virtio_compat_add_message("virtio-balloon", "CONFIG_VIRTIO_BALLOON");
297d06db2fdSSasha Levin
298d06db2fdSSasha Levin return 0;
299d06db2fdSSasha Levin }
30049a8afd1SSasha Levin virtio_dev_init(virtio_bln__init);
301d06db2fdSSasha Levin
virtio_bln__exit(struct kvm * kvm)302d06db2fdSSasha Levin int virtio_bln__exit(struct kvm *kvm)
303d06db2fdSSasha Levin {
304*74af1456SEduardo Bart virtio_exit(kvm, &bdev.vdev);
305*74af1456SEduardo Bart
306d06db2fdSSasha Levin return 0;
30782d2f21eSSasha Levin }
30849a8afd1SSasha Levin virtio_dev_exit(virtio_bln__exit);
309