@@ -33,9 +33,19 @@
#include "qmp-commands.h"
#include "qapi/qmp/qerror.h"
#include "qapi/qmp/qjson.h"
+#include "exec/ram_addr.h"
+#include "migration/vmstate.h"
+
+#define BALLOON_BMAP_NAME "balloon.bmap"
+#define BALLOON_BMAP_SIZE(nr) (nr / (((1UL << balloon_bitmap_pfn_shift) * \
+ sizeof(unsigned long)) - 1))
static QEMUBalloonEvent *balloon_event_fn;
static QEMUBalloonStatus *balloon_stat_fn;
+static QemuMutex balloon_bmap_mutex;
+static MemoryRegion *bmap_mr;
+static unsigned long *bmap;
+static unsigned int balloon_bitmap_pfn_shift;
static void *balloon_opaque;
static bool balloon_inhibited;
@@ -49,6 +59,16 @@ void qemu_balloon_inhibit(bool state)
balloon_inhibited = state;
}
+void qemu_mutex_lock_balloon_bitmap(void)
+{
+ qemu_mutex_lock(&balloon_bmap_mutex);
+}
+
+void qemu_mutex_unlock_balloon_bitmap(void)
+{
+ qemu_mutex_unlock(&balloon_bmap_mutex);
+}
+
static bool have_balloon(Error **errp)
{
if (kvm_enabled() && !kvm_has_sync_mmu()) {
@@ -65,7 +85,8 @@ static bool have_balloon(Error **errp)
}
int qemu_add_balloon_handler(QEMUBalloonEvent *event_func,
- QEMUBalloonStatus *stat_func, void *opaque)
+ QEMUBalloonStatus *stat_func,
+ void *opaque, int balloon_pfn_shift)
{
if (balloon_event_fn || balloon_stat_fn || balloon_opaque) {
/* We're already registered one balloon handler. How many can
@@ -75,7 +96,18 @@ int qemu_add_balloon_handler(QEMUBalloonEvent *event_func,
}
balloon_event_fn = event_func;
balloon_stat_fn = stat_func;
+ balloon_bitmap_pfn_shift = balloon_pfn_shift;
balloon_opaque = opaque;
+
+ qemu_mutex_init(&balloon_bmap_mutex);
+ bmap_mr = g_new(MemoryRegion, 1);
+ memory_region_init_resizeable_ram(bmap_mr, NULL, BALLOON_BMAP_NAME,
+ BALLOON_BMAP_SIZE((last_ram_offset())),
+ BALLOON_BMAP_SIZE((last_ram_offset())),
+ NULL, &error_fatal);
+ vmstate_register_ram_global(bmap_mr);
+ bmap = memory_region_get_ram_ptr(bmap_mr);
+ bitmap_clear(bmap, 0, (last_ram_offset() >> balloon_bitmap_pfn_shift));
return 0;
}
@@ -84,6 +116,8 @@ void qemu_remove_balloon_handler(void *opaque)
if (balloon_opaque != opaque) {
return;
}
+ object_unref(OBJECT(bmap_mr));
+ bmap = NULL;
balloon_event_fn = NULL;
balloon_stat_fn = NULL;
balloon_opaque = NULL;
@@ -116,3 +150,58 @@ void qmp_balloon(int64_t target, Error **errp)
trace_balloon_event(balloon_opaque, target);
balloon_event_fn(balloon_opaque, target);
}
+
+/* Should be called with balloon bitmap mutex lock held */
+void qemu_balloon_bitmap_update(ram_addr_t addr, int deflate)
+{
+ unsigned long offset = 0;
+
+ if (!bmap) {
+ return;
+ }
+ offset = (addr >> balloon_bitmap_pfn_shift);
+ if (deflate == 0) {
+ set_bit(offset, bmap);
+ } else {
+ clear_bit(offset, bmap);
+ }
+}
+
+/* Handle Ram hotplug case, only called in case old < new */
+void qemu_balloon_bitmap_extend(RAMBlock *new_block,
+ ram_addr_t old, ram_addr_t new)
+{
+ RAMBlock *block = NULL;
+ unsigned long *old_bitmap;
+
+ if (!bmap || !new_block || (new_block->mr &&
+ (strcmp(new_block->mr->name, BALLOON_BMAP_NAME) == 0))) {
+ return;
+ }
+
+ block = qemu_ram_block_by_name(BALLOON_BMAP_NAME);
+ if (BALLOON_BMAP_SIZE((last_ram_offset())) <= block->used_length) {
+ /* Current bitmap already considers new size */
+ return;
+ }
+
+ old = old >> balloon_bitmap_pfn_shift;
+ new = new >> balloon_bitmap_pfn_shift;
+
+ old_bitmap = bitmap_new(old);
+ bitmap_clear(old_bitmap, 0, old);
+ qemu_mutex_lock_balloon_bitmap();
+ bitmap_copy(old_bitmap, bmap, old);
+ object_unref(OBJECT(bmap_mr));
+ memory_region_init_resizeable_ram(bmap_mr, NULL, BALLOON_BMAP_NAME,
+ BALLOON_BMAP_SIZE((last_ram_offset())),
+ BALLOON_BMAP_SIZE((last_ram_offset())),
+ NULL, &error_fatal);
+
+ vmstate_register_ram_global(bmap_mr);
+ bmap = memory_region_get_ram_ptr(bmap_mr);
+ bitmap_clear(bmap, 0, new);
+ bitmap_copy(bmap, old_bitmap, old);
+ qemu_mutex_unlock_balloon_bitmap();
+ g_free(old_bitmap);
+}
@@ -44,6 +44,7 @@
#else /* !CONFIG_USER_ONLY */
#include "sysemu/xen-mapcache.h"
#include "trace.h"
+#include "sysemu/balloon.h"
#endif
#include "exec/cpu-all.h"
#include "qemu/rcu_queue.h"
@@ -1636,6 +1637,11 @@ static void ram_block_add(RAMBlock *new_block, Error **errp)
smp_wmb();
ram_list.version++;
qemu_mutex_unlock_ramlist();
+ if (new_ram_size > old_ram_size) {
+ qemu_balloon_bitmap_extend(new_block,
+ (old_ram_size << TARGET_PAGE_BITS),
+ (new_ram_size << TARGET_PAGE_BITS));
+ }
cpu_physical_memory_set_dirty_range(new_block->offset,
new_block->used_length,
@@ -216,17 +216,21 @@ static void virtio_balloon_handle_output(VirtIODevice *vdev, VirtQueue *vq)
VirtQueueElement *elem;
MemoryRegionSection section;
+ qemu_mutex_lock_balloon_bitmap();
for (;;) {
size_t offset = 0;
uint32_t pfn;
elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
if (!elem) {
+ qemu_mutex_unlock_balloon_bitmap();
return;
}
while (iov_to_buf(elem->out_sg, elem->out_num, offset, &pfn, 4) == 4) {
ram_addr_t pa;
ram_addr_t addr;
+ void *user_addr;
+ ram_addr_t ram_addr, ram_addr_offset;
int p = virtio_ldl_p(vdev, &pfn);
pa = (ram_addr_t) p << VIRTIO_BALLOON_PFN_SHIFT;
@@ -244,6 +248,15 @@ static void virtio_balloon_handle_output(VirtIODevice *vdev, VirtQueue *vq)
addr = section.offset_within_region;
balloon_page(memory_region_get_ram_ptr(section.mr) + addr,
!!(vq == s->dvq));
+ user_addr = memory_region_get_ram_ptr(section.mr) + addr;
+ qemu_ram_block_from_host(user_addr, true, &ram_addr,
+ &ram_addr_offset);
+ if (TARGET_PAGE_BITS > VIRTIO_BALLOON_PFN_SHIFT) {
+ /* ram_addr will be TARGET_PAGE_BITS alligned, add offset */
+ ram_addr = ram_addr + ((unsigned long)user_addr &
+ ((1UL << TARGET_PAGE_BITS) - 1));
+ }
+ qemu_balloon_bitmap_update(ram_addr, !!(vq == s->dvq));
memory_region_unref(section.mr);
}
@@ -251,6 +264,7 @@ static void virtio_balloon_handle_output(VirtIODevice *vdev, VirtQueue *vq)
virtio_notify(vdev, vq);
g_free(elem);
}
+ qemu_mutex_unlock_balloon_bitmap();
}
static void virtio_balloon_receive_stats(VirtIODevice *vdev, VirtQueue *vq)
@@ -445,7 +459,8 @@ static void virtio_balloon_device_realize(DeviceState *dev, Error **errp)
sizeof(struct virtio_balloon_config));
ret = qemu_add_balloon_handler(virtio_balloon_to_target,
- virtio_balloon_stat, s);
+ virtio_balloon_stat,
+ s, VIRTIO_BALLOON_PFN_SHIFT);
if (ret < 0) {
error_setg(errp, "Only one balloon device is supported");
@@ -20,9 +20,15 @@ typedef void (QEMUBalloonEvent)(void *opaque, ram_addr_t target);
typedef void (QEMUBalloonStatus)(void *opaque, BalloonInfo *info);
int qemu_add_balloon_handler(QEMUBalloonEvent *event_func,
- QEMUBalloonStatus *stat_func, void *opaque);
+ QEMUBalloonStatus *stat_func,
+ void *opaque, int balloon_pfn_shift);
void qemu_remove_balloon_handler(void *opaque);
bool qemu_balloon_is_inhibited(void);
void qemu_balloon_inhibit(bool state);
+void qemu_mutex_lock_balloon_bitmap(void);
+void qemu_mutex_unlock_balloon_bitmap(void);
+void qemu_balloon_bitmap_update(ram_addr_t addr, int deflate);
+void qemu_balloon_bitmap_extend(RAMBlock *new_block,
+ ram_addr_t old, ram_addr_t new);
#endif
The ramblock for balloon bitmap is initialized as part of virtio-balloon device realize itself. The bitmap represents entire guest ram memory till last_ram_offset(). The bit in the balloon bitmap represents a page of size (1UL << VIRTIO_BALLOON_PFN_SHIFT). Guest ram pages returned by virtio-balloon driver will be represented by 1 in the bitmap. The bitmap is also resized in case of more RAM is hotplugged. Signed-off-by: Jitendra Kolhe <jitendra.kolhe@hpe.com> --- balloon.c | 91 +++++++++++++++++++++++++++++++++++++++++++++- exec.c | 6 +++ hw/virtio/virtio-balloon.c | 17 ++++++++- include/sysemu/balloon.h | 8 +++- 4 files changed, 119 insertions(+), 3 deletions(-)