This patch implements the virtio balloon driver backend. A user can interact with the balloon driver using a newly introduce monitor command 'balloon'.
Ballooning is used to request the guest to stop using a certain portion of its memory. The guest notifies the host of this memory so the host can immediately reallocate it. Ballooning is implemented within QEMU via the madvise() system call. This is for Linux hosts only ATM but it should be easy enough to add the right code for other hosts. If you balloon down sufficiently, you can see the resident memory of the QEMU instance decrease when using this driver. Signed-off-by: Anthony Liguori <[EMAIL PROTECTED]> diff --git a/Makefile.target b/Makefile.target index f9fe660..86a0bf5 100644 --- a/Makefile.target +++ b/Makefile.target @@ -535,7 +535,7 @@ OBJS += rtl8139.o OBJS += e1000.o # virtio devices -OBJS += virtio.o virtio-net.o virtio-blk.o +OBJS += virtio.o virtio-net.o virtio-blk.o virtio-balloon.o ifeq ($(TARGET_BASE_ARCH), i386) # Hardware support diff --git a/balloon.h b/balloon.h new file mode 100644 index 0000000..60b4a5d --- /dev/null +++ b/balloon.h @@ -0,0 +1,27 @@ +/* + * Balloon + * + * Copyright IBM, Corp. 2008 + * + * Authors: + * Anthony Liguori <[EMAIL PROTECTED]> + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + */ + +#ifndef _QEMU_BALLOON_H +#define _QEMU_BALLOON_H + +#include "cpu-defs.h" + +typedef ram_addr_t (QEMUBalloonEvent)(void *opaque, ram_addr_t target); + +void qemu_add_balloon_handler(QEMUBalloonEvent *func, void *opaque); + +void qemu_balloon(ram_addr_t target); + +ram_addr_t qemu_balloon_status(void); + +#endif diff --git a/hw/pc.c b/hw/pc.c index 2da9413..8d3401a 100644 --- a/hw/pc.c +++ b/hw/pc.c @@ -1023,6 +1023,8 @@ static void pc_init1(int ram_size, int vga_ram_size, } } + if (pci_enabled) + virtio_balloon_init(pci_bus); } static void pc_init_pci(int ram_size, int vga_ram_size, diff --git a/hw/pc.h b/hw/pc.h index c828cda..67583f7 100644 --- a/hw/pc.h +++ b/hw/pc.h @@ -146,4 +146,7 @@ void isa_ne2000_init(int base, qemu_irq irq, NICInfo *nd); /* virtio-blk.c */ void *virtio_blk_init(PCIBus *bus, BlockDriverState *bs); +/* virtio-balloon.h */ +void *virtio_balloon_init(PCIBus *bus); + #endif diff --git a/hw/virtio-balloon.c b/hw/virtio-balloon.c new file mode 100644 index 0000000..b37eb04 --- /dev/null +++ b/hw/virtio-balloon.c @@ -0,0 +1,137 @@ +/* + * Virtio Block Device + * + * Copyright IBM, Corp. 2008 + * + * Authors: + * Anthony Liguori <[EMAIL PROTECTED]> + * + * 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" + +#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__) + madvise(addr, TARGET_PAGE_SIZE, deflate ? MADV_WILLNEED : MADV_DONTNEED); +#endif +} + +static void virtio_balloon_handle_output(VirtIODevice *vdev, VirtQueue *vq) +{ + VirtIOBalloon *s = to_virtio_balloon(vdev); + VirtQueueElement *elem; + + while ((elem = virtqueue_pop(vq)) != NULL) { + size_t offset = 0; + uint32_t pfn; + + while (memcpy_from_iovector(&pfn, offset, 4, elem->virt_out) == 4) { + ram_addr_t phys, pa; + + pa = (ram_addr_t)ldl_p(&pfn) << TARGET_PAGE_BITS; + + phys = cpu_get_physical_page_desc(pa); + + /* can't balloon non-RAM pages */ + if ((phys & ~TARGET_PAGE_MASK) != IO_MEM_RAM) + continue; + + balloon_page(phys_ram_base + (phys & TARGET_PAGE_MASK), + !!(vq == s->dvq)); + + offset += 4; + } + + 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 = dev->num_pages; + config.actual = 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) >> TARGET_PAGE_BITS; + virtio_notify_config(&dev->vdev); + } + + return ram_size - (dev->actual << TARGET_PAGE_BITS); +} + +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)); + + 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); + + return &s->vdev; +} diff --git a/hw/virtio-balloon.h b/hw/virtio-balloon.h new file mode 100644 index 0000000..27d6985 --- /dev/null +++ b/hw/virtio-balloon.h @@ -0,0 +1,34 @@ +/* + * Virtio Support + * + * Copyright IBM, Corp. 2007-2008 + * + * Authors: + * Anthony Liguori <[EMAIL PROTECTED]> + * Rusty Russell <[EMAIL PROTECTED]> + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + */ + +#ifndef _QEMU_VIRTIO_BALLOON_H +#define _QEMU_VIRTIO_BALLOON_H + +/* from Linux's linux/virtio_balloon.h */ + +/* The ID for virtio_balloon */ +#define VIRTIO_ID_BALLOON 5 + +/* The feature bitmap for virtio balloon */ +#define VIRTIO_BALLOON_F_MUST_TELL_HOST 0 /* Tell before reclaiming pages */ + +struct virtio_balloon_config +{ + /* Number of pages host wants Guest to give up. */ + uint32_t num_pages; + /* Number of pages we've actually got in balloon. */ + uint32_t actual; +}; + +#endif diff --git a/monitor.c b/monitor.c index 025025b..7f4c096 100644 --- a/monitor.c +++ b/monitor.c @@ -34,6 +34,7 @@ #include "block.h" #include "audio/audio.h" #include "disas.h" +#include "balloon.h" #include <dirent.h> #ifdef CONFIG_PROFILER @@ -1257,6 +1258,23 @@ static void do_wav_capture (const char *path, } #endif +static void do_balloon(int value) +{ + ram_addr_t target = value; + qemu_balloon(target << 20); +} + +static void do_info_balloon(void) +{ + ram_addr_t actual; + + actual = qemu_balloon_status(); + if (actual == 0) + term_printf("Ballooning not activated in VM\n"); + else + term_printf("balloon: actual=%d\n", (int)(actual >> 20)); +} + static term_cmd_t term_cmds[] = { { "help|?", "s?", do_help, "[cmd]", "show the help" }, @@ -1328,6 +1346,8 @@ static term_cmd_t term_cmds[] = { "capture index", "stop capture" }, { "memsave", "lis", do_memory_save, "addr size file", "save to disk virtual memory dump starting at 'addr' of size 'size'", }, + { "balloon", "i", do_balloon, + "target", "request VM to change it's memory allocation (in MB)" }, { NULL, NULL, }, }; @@ -1388,6 +1408,8 @@ static term_cmd_t info_cmds[] = { { "slirp", "", do_info_slirp, "", "show SLIRP statistics", }, #endif + { "balloon", "", do_info_balloon, + "", "show balloon information" }, { NULL, NULL, }, }; diff --git a/vl.c b/vl.c index 9b614e9..22ec24f 100644 --- a/vl.c +++ b/vl.c @@ -37,6 +37,7 @@ #include "qemu-char.h" #include "block.h" #include "audio/audio.h" +#include "balloon.h" #include <unistd.h> #include <fcntl.h> @@ -482,6 +483,31 @@ void hw_error(const char *fmt, ...) va_end(ap); abort(); } + +/***************/ +/* ballooning */ + +static QEMUBalloonEvent *qemu_balloon_event; +void *qemu_balloon_event_opaque; + +void qemu_add_balloon_handler(QEMUBalloonEvent *func, void *opaque) +{ + qemu_balloon_event = func; + qemu_balloon_event_opaque = opaque; +} + +void qemu_balloon(ram_addr_t target) +{ + if (qemu_balloon_event) + qemu_balloon_event(qemu_balloon_event_opaque, target); +} + +ram_addr_t qemu_balloon_status(void) +{ + if (qemu_balloon_event) + return qemu_balloon_event(qemu_balloon_event_opaque, 0); + return 0; +} /***********************************************************/ /* keyboard/mouse */ ------------------------------------------------------------------------- Check out the new SourceForge.net Marketplace. It's the best place to buy or sell services for just about anything Open Source. http://ad.doubleclick.net/clk;164216239;13503038;w?http://sf.net/marketplace _______________________________________________ kvm-devel mailing list kvm-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/kvm-devel