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