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