[patch 1/3] QEMU support for virtio balloon driver

Marcelo Tosatti
2008-03-12T19:17:21Z

[-- Attachment #1: qemu-balloon --]
[-- Type: text/plain, Size: 12508 bytes --]

From: Anthony Liguori <aliguori@us.ibm.com>

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.

Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>

Index: kvm-userspace.balloon/qemu/Makefile.target
===================================================================
--- kvm-userspace.balloon.orig/qemu/Makefile.target
+++ kvm-userspace.balloon/qemu/Makefile.target
@@ -577,7 +577,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
Index: kvm-userspace.balloon/qemu/balloon.h
===================================================================
--- /dev/null
+++ kvm-userspace.balloon/qemu/balloon.h
@@ -0,0 +1,14 @@
+#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
Index: kvm-userspace.balloon/qemu/hw/pc.c
===================================================================
--- kvm-userspace.balloon.orig/qemu/hw/pc.c
+++ kvm-userspace.balloon/qemu/hw/pc.c
@@ -1120,6 +1120,9 @@ static void pc_init1(ram_addr_t 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;
Index: kvm-userspace.balloon/qemu/hw/pc.h
===================================================================
--- kvm-userspace.balloon.orig/qemu/hw/pc.h
+++ kvm-userspace.balloon/qemu/hw/pc.h
@@ -153,6 +153,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);
Index: kvm-userspace.balloon/qemu/hw/virtio-balloon.c
===================================================================
--- /dev/null
+++ kvm-userspace.balloon/qemu/hw/virtio-balloon.c
@@ -0,0 +1,145 @@
+/*
+ * 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 "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;
+}
Index: kvm-userspace.balloon/qemu/hw/virtio-blk.c
===================================================================
--- kvm-userspace.balloon.orig/qemu/hw/virtio-blk.c
+++ kvm-userspace.balloon/qemu/hw/virtio-blk.c
@@ -153,7 +153,7 @@ void *virtio_blk_init(PCIBus *bus, uint1
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;

Index: kvm-userspace.balloon/qemu/hw/virtio-net.c
===================================================================
--- kvm-userspace.balloon.orig/qemu/hw/virtio-net.c
+++ kvm-userspace.balloon/qemu/hw/virtio-net.c
@@ -288,7 +288,7 @@ void *virtio_net_init(PCIBus *bus, NICIn
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);
Index: kvm-userspace.balloon/qemu/hw/virtio.c
===================================================================
--- kvm-userspace.balloon.orig/qemu/hw/virtio.c
+++ kvm-userspace.balloon/qemu/hw/virtio.c
@@ -307,6 +307,9 @@ static void virtio_config_writeb(void *o
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 *o
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 *o
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_de
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
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 */
Index: kvm-userspace.balloon/qemu/hw/virtio.h
===================================================================
--- kvm-userspace.balloon.orig/qemu/hw/virtio.h
+++ kvm-userspace.balloon/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, VirtQue

void virtio_notify(VirtIODevice *vdev, VirtQueue *vq);

+void virtio_notify_config(VirtIODevice *vdev);
+
#endif
Index: kvm-userspace.balloon/qemu/monitor.c
===================================================================
--- kvm-userspace.balloon.orig/qemu/monitor.c
+++ kvm-userspace.balloon/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"
@@ -1281,6 +1282,23 @@ static void do_wav_capture (const char *
}
#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" },
@@ -1359,6 +1377,8 @@ static term_cmd_t term_cmds[] = {
{ "migrate_set_speed", "s", do_migrate_set_speed,
"value", "set maximum speed (in bytes) for migrations" },
{ "cpu_set", "is", do_cpu_set_nr, "cpu [online|offline]", "change cpu state" },
+ { "balloon", "i", do_balloon,
+ "target", "request VM to change it's memory allocation (in MB)" },
{ NULL, NULL, },
};

@@ -1421,6 +1441,8 @@ static term_cmd_t info_cmds[] = {
#endif
{ "migration", "", do_info_migration,
"", "show migration information" },
+ { "balloon", "", do_info_balloon,
+ "", "show balloon information" },
{ NULL, NULL, },
};

Index: kvm-userspace.balloon/qemu/vl.c
===================================================================
--- kvm-userspace.balloon.orig/qemu/vl.c
+++ kvm-userspace.balloon/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 */