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