This patch adds support to QEMU for Rusty's recently introduce virtio balloon
driver. The user-facing portions of this are the introduction of a "balloon"
and "info balloon" command in the monitor.
Right now madvise() is commented out since it causes host panics. Ballooning
is still functional though--the host just doesn't reclaim the memory
immediately.
Signed-off-by: Anthony Liguori <[EMAIL PROTECTED]>
diff --git a/qemu/Makefile.target b/qemu/Makefile.target
index d0cde54..c0205d7 100644
--- a/qemu/Makefile.target
+++ b/qemu/Makefile.target
@@ -574,7 +574,7 @@ OBJS += e1000.o
OBJS+= hypercall.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/qemu/hw/pc.c b/qemu/hw/pc.c
index ef180ec..9cba6fc 100644
--- a/qemu/hw/pc.c
+++ b/qemu/hw/pc.c
@@ -1113,6 +1113,9 @@ static void pc_init1(ram_addr_t ram_size, int
vga_ram_size,
}
}
+ if (pci_enabled)
+ virtio_balloon_init(pci_bus, 0x1AF4, 0x1002);
+
if (extboot_drive != -1) {
DriveInfo *info = &drives_table[extboot_drive];
int cyls, heads, secs;
diff --git a/qemu/hw/pc.h b/qemu/hw/pc.h
index d109569..706881b 100644
--- a/qemu/hw/pc.h
+++ b/qemu/hw/pc.h
@@ -152,6 +152,9 @@ void virtio_net_poll(void);
void *virtio_blk_init(PCIBus *bus, uint16_t vendor, uint16_t device,
BlockDriverState *bs);
+/* virtio-balloon.h */
+void *virtio_balloon_init(PCIBus *bus, uint16_t vendor, uint16_t device);
+
/* extboot.c */
void extboot_init(BlockDriverState *bs, int cmd);
diff --git a/qemu/hw/virtio-balloon.c b/qemu/hw/virtio-balloon.c
new file mode 100644
index 0000000..d7b61d0
--- /dev/null
+++ b/qemu/hw/virtio-balloon.c
@@ -0,0 +1,145 @@
+/*
+ * 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 "virtio.h"
+#include "block.h"
+#include "pc.h"
+#include "balloon.h"
+#include "sysemu.h"
+
+#include <sys/mman.h>
+
+/* from Linux's linux/virtio_blk.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;
+};
+
+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 virtio_balloon_handle_output(VirtIODevice *vdev, VirtQueue *vq)
+{
+ VirtIOBalloon *s = to_virtio_balloon(vdev);
+ VirtQueueElement elem;
+ unsigned int count;
+
+ while ((count = virtqueue_pop(vq, &elem)) != 0) {
+ int i;
+ unsigned int wlen = 0;
+
+ for (i = 0; i < elem.out_num; i++) {
+ int flags;
+ uint32_t *pfns = elem.out_sg[i].iov_base;
+ unsigned int n_pfns = elem.out_sg[i].iov_len / 4;
+ int j;
+
+ for (j = 0; j < n_pfns; j++) {
+ if (vq == s->dvq) /* deflate */
+ flags = MADV_WILLNEED;
+ else /* inflate */
+ flags = MADV_DONTNEED;
+
+#if 0
+ /* can't use this until we have mmu notifier support */
+ madvise(phys_ram_base + (pfns[j] << TARGET_PAGE_BITS),
+ TARGET_PAGE_SIZE, flags);
+#endif
+ }
+
+ wlen += elem.out_sg[i].iov_len;
+ }
+
+ virtqueue_push(vq, &elem, wlen);
+ 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) {
+ 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, uint16_t vendor, uint16_t device)
+{
+ VirtIOBalloon *s;
+
+ s = (VirtIOBalloon *)virtio_init_pci(bus, "virtio-balloon",
+ vendor, device,
+ 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/qemu/hw/virtio-blk.c b/qemu/hw/virtio-blk.c
index 301b5a1..c8c3233 100644
--- a/qemu/hw/virtio-blk.c
+++ b/qemu/hw/virtio-blk.c
@@ -153,7 +153,7 @@ void *virtio_blk_init(PCIBus *bus, uint16_t vendor,
uint16_t device,
0x01, 0x80, 0x00,
16, sizeof(VirtIOBlock));
- s->vdev.update_config = virtio_blk_update_config;
+ s->vdev.get_config = virtio_blk_update_config;
s->vdev.get_features = virtio_blk_get_features;
s->bs = bs;
diff --git a/qemu/hw/virtio-net.c b/qemu/hw/virtio-net.c
index 86f9e5a..8c1f953 100644
--- a/qemu/hw/virtio-net.c
+++ b/qemu/hw/virtio-net.c
@@ -288,7 +288,7 @@ void *virtio_net_init(PCIBus *bus, NICInfo *nd, int devfn)
0x02, 0x00, 0x00,
6, sizeof(VirtIONet));
- n->vdev.update_config = virtio_net_update_config;
+ n->vdev.get_config = virtio_net_update_config;
n->vdev.get_features = virtio_net_get_features;
n->rx_vq = virtio_add_queue(&n->vdev, 512, virtio_net_handle_rx);
n->tx_vq = virtio_add_queue(&n->vdev, 128, virtio_net_handle_tx);
diff --git a/qemu/hw/virtio.c b/qemu/hw/virtio.c
index 634f869..78d679f 100644
--- a/qemu/hw/virtio.c
+++ b/qemu/hw/virtio.c
@@ -307,6 +307,9 @@ static void virtio_config_writeb(void *opaque, uint32_t
addr, uint32_t data)
return;
memcpy(vdev->config + addr, &val, sizeof(val));
+
+ if (vdev->set_config)
+ vdev->set_config(vdev, vdev->config);
}
static void virtio_config_writew(void *opaque, uint32_t addr, uint32_t data)
@@ -319,6 +322,9 @@ static void virtio_config_writew(void *opaque, uint32_t
addr, uint32_t data)
return;
memcpy(vdev->config + addr, &val, sizeof(val));
+
+ if (vdev->set_config)
+ vdev->set_config(vdev, vdev->config);
}
static void virtio_config_writel(void *opaque, uint32_t addr, uint32_t data)
@@ -331,6 +337,9 @@ static void virtio_config_writel(void *opaque, uint32_t
addr, uint32_t data)
return;
memcpy(vdev->config + addr, &val, sizeof(val));
+
+ if (vdev->set_config)
+ vdev->set_config(vdev, vdev->config);
}
static void virtio_map(PCIDevice *pci_dev, int region_num,
@@ -359,7 +368,7 @@ static void virtio_map(PCIDevice *pci_dev, int region_num,
register_ioport_read(addr + 20, vdev->config_len, 4,
virtio_config_readl, vdev);
- vdev->update_config(vdev, vdev->config);
+ vdev->get_config(vdev, vdev->config);
}
}
@@ -383,6 +392,14 @@ VirtQueue *virtio_add_queue(VirtIODevice *vdev, int
queue_size,
return &vdev->vq[i];
}
+void virtio_notify_config(VirtIODevice *vdev)
+{
+ /* make sure we have the latest config */
+ vdev->get_config(vdev, vdev->config);
+ vdev->isr = 3;
+ virtio_update_irq(vdev);
+}
+
void virtio_notify(VirtIODevice *vdev, VirtQueue *vq)
{
/* Always notify when queue is empty */
diff --git a/qemu/hw/virtio.h b/qemu/hw/virtio.h
index dee97ba..ed8bebd 100644
--- a/qemu/hw/virtio.h
+++ b/qemu/hw/virtio.h
@@ -118,7 +118,8 @@ struct VirtIODevice
void *config;
uint32_t (*get_features)(VirtIODevice *vdev);
void (*set_features)(VirtIODevice *vdev, uint32_t val);
- void (*update_config)(VirtIODevice *vdev, uint8_t *config);
+ void (*get_config)(VirtIODevice *vdev, uint8_t *config);
+ void (*set_config)(VirtIODevice *vdev, const uint8_t *config);
VirtQueue vq[VIRTIO_PCI_QUEUE_MAX];
};
@@ -140,4 +141,6 @@ int virtqueue_pop(VirtQueue *vq, VirtQueueElement *elem);
void virtio_notify(VirtIODevice *vdev, VirtQueue *vq);
+void virtio_notify_config(VirtIODevice *vdev);
+
#endif
diff --git a/qemu/monitor.c b/qemu/monitor.c
index e8022c8..fb16566 100644
--- a/qemu/monitor.c
+++ b/qemu/monitor.c
@@ -35,6 +35,7 @@
#include "audio/audio.h"
#include "disas.h"
#include "migration.h"
+#include "balloon.h"
#include <dirent.h>
#include "qemu-kvm.h"
@@ -1261,6 +1262,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" },
@@ -1338,6 +1356,8 @@ static term_cmd_t term_cmds[] = {
"", "cancel the current VM migration" },
{ "migrate_set_speed", "s", do_migrate_set_speed,
"value", "set maximum speed (in bytes) for migrations" },
+ { "balloon", "i", do_balloon,
+ "target", "request VM to change it's memory allocation (in MB)" },
{ NULL, NULL, },
};
@@ -1400,6 +1420,8 @@ static term_cmd_t info_cmds[] = {
#endif
{ "migration", "", do_info_migration,
"", "show migration information" },
+ { "balloon", "", do_info_balloon,
+ "", "show balloon information" },
{ NULL, NULL, },
};
diff --git a/qemu/vl.c b/qemu/vl.c
index c9ed3f0..141cc98 100644
--- a/qemu/vl.c
+++ b/qemu/vl.c
@@ -38,6 +38,7 @@
#include "block.h"
#include "audio/audio.h"
#include "migration.h"
+#include "balloon.h"
#include "qemu-kvm.h"
#include <unistd.h>
@@ -513,6 +514,31 @@ void hw_error(const char *fmt, ...)
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 */
-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft
Defy all challenges. Microsoft(R) Visual Studio 2008.
http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/
_______________________________________________
kvm-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/kvm-devel