diff options
Diffstat (limited to 'hw/virtio-balloon.c')
-rw-r--r-- | hw/virtio-balloon.c | 194 |
1 files changed, 194 insertions, 0 deletions
diff --git a/hw/virtio-balloon.c b/hw/virtio-balloon.c new file mode 100644 index 0000000..e668436 --- /dev/null +++ b/hw/virtio-balloon.c @@ -0,0 +1,194 @@ +/* + * Virtio Block Device + * + * Copyright IBM, Corp. 2008 + * + * Authors: + * Anthony Liguori <aliguori@us.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + */ + +#include "qemu-common.h" +#include "virtio.h" +#include "pc.h" +#include "sysemu.h" +#include "cpu.h" +#include "balloon.h" +#include "virtio-balloon.h" +#include "kvm.h" + +#if defined(__linux__) +#include <sys/mman.h> +#endif + +typedef struct VirtIOBalloon +{ + VirtIODevice vdev; + VirtQueue *ivq, *dvq; + uint32_t num_pages; + uint32_t actual; +} VirtIOBalloon; + +static VirtIOBalloon *to_virtio_balloon(VirtIODevice *vdev) +{ + return (VirtIOBalloon *)vdev; +} + +static void balloon_page(void *addr, int deflate) +{ +#if defined(__linux__) + if (!kvm_enabled() || kvm_has_sync_mmu()) + madvise(addr, TARGET_PAGE_SIZE, + deflate ? MADV_WILLNEED : MADV_DONTNEED); +#endif +} + +/* FIXME: once we do a virtio refactoring, this will get subsumed into common + * code */ +static size_t memcpy_from_iovector(void *data, size_t offset, size_t size, + struct iovec *iov, int iovlen) +{ + int i; + uint8_t *ptr = data; + size_t iov_off = 0; + size_t data_off = 0; + + for (i = 0; i < iovlen && size; i++) { + if (offset < (iov_off + iov[i].iov_len)) { + size_t len = MIN((iov_off + iov[i].iov_len) - offset , size); + + memcpy(ptr + data_off, iov[i].iov_base + (offset - iov_off), len); + + data_off += len; + offset += len; + size -= len; + } + + iov_off += iov[i].iov_len; + } + + return data_off; +} + +static void virtio_balloon_handle_output(VirtIODevice *vdev, VirtQueue *vq) +{ + VirtIOBalloon *s = to_virtio_balloon(vdev); + VirtQueueElement elem; + + while (virtqueue_pop(vq, &elem)) { + size_t offset = 0; + uint32_t pfn; + + while (memcpy_from_iovector(&pfn, offset, 4, + elem.out_sg, elem.out_num) == 4) { + ram_addr_t pa; + ram_addr_t addr; + + pa = (ram_addr_t)ldl_p(&pfn) << VIRTIO_BALLOON_PFN_SHIFT; + offset += 4; + + addr = cpu_get_physical_page_desc(pa); + if ((addr & ~TARGET_PAGE_MASK) != IO_MEM_RAM) + continue; + + balloon_page(phys_ram_base + addr, !!(vq == s->dvq)); + } + + virtqueue_push(vq, &elem, offset); + virtio_notify(vdev, vq); + } +} + +static void virtio_balloon_get_config(VirtIODevice *vdev, uint8_t *config_data) +{ + VirtIOBalloon *dev = to_virtio_balloon(vdev); + struct virtio_balloon_config config; + + config.num_pages = cpu_to_le32(dev->num_pages); + config.actual = cpu_to_le32(dev->actual); + + memcpy(config_data, &config, 8); +} + +static void virtio_balloon_set_config(VirtIODevice *vdev, + const uint8_t *config_data) +{ + VirtIOBalloon *dev = to_virtio_balloon(vdev); + struct virtio_balloon_config config; + memcpy(&config, config_data, 8); + dev->actual = config.actual; +} + +static uint32_t virtio_balloon_get_features(VirtIODevice *vdev) +{ + return 0; +} + +static ram_addr_t virtio_balloon_to_target(void *opaque, ram_addr_t target) +{ + VirtIOBalloon *dev = opaque; + + if (target > ram_size) + target = ram_size; + + if (target) { + dev->num_pages = (ram_size - target) >> VIRTIO_BALLOON_PFN_SHIFT; + virtio_notify_config(&dev->vdev); + } + + return ram_size - (dev->actual << VIRTIO_BALLOON_PFN_SHIFT); +} + +static void virtio_balloon_save(QEMUFile *f, void *opaque) +{ + VirtIOBalloon *s = opaque; + + virtio_save(&s->vdev, f); + + qemu_put_be32(f, s->num_pages); + qemu_put_be32(f, s->actual); +} + +static int virtio_balloon_load(QEMUFile *f, void *opaque, int version_id) +{ + VirtIOBalloon *s = opaque; + + if (version_id != 1) + return -EINVAL; + + virtio_load(&s->vdev, f); + + s->num_pages = qemu_get_be32(f); + s->actual = qemu_get_be32(f); + + return 0; +} + +void *virtio_balloon_init(PCIBus *bus) +{ + VirtIOBalloon *s; + + s = (VirtIOBalloon *)virtio_init_pci(bus, "virtio-balloon", + 6900, 0x1002, + 0, VIRTIO_ID_BALLOON, + 0x05, 0x00, 0x00, + 8, sizeof(VirtIOBalloon)); + if (s == NULL) + return NULL; + + s->vdev.get_config = virtio_balloon_get_config; + s->vdev.set_config = virtio_balloon_set_config; + s->vdev.get_features = virtio_balloon_get_features; + + s->ivq = virtio_add_queue(&s->vdev, 128, virtio_balloon_handle_output); + s->dvq = virtio_add_queue(&s->vdev, 128, virtio_balloon_handle_output); + + qemu_add_balloon_handler(virtio_balloon_to_target, s); + + register_savevm("virtio-balloon", -1, 1, virtio_balloon_save, virtio_balloon_load, s); + + return &s->vdev; +} |