Merge remote-tracking branch 'remotes/qmp-unstable/queue/qmp' into staging

* remotes/qmp-unstable/queue/qmp:
  docs: add memory-hotplug.txt
  qemu-options.hx: improve -m description
  virtio-balloon: Add some trace events
  virtio-balloon: Fix balloon not working correctly when hotplug memory
  pc-dimm: add a function to calculate VM's current RAM size

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2015-03-08 12:47:13 +00:00
commit 9c31a8219a
7 changed files with 144 additions and 11 deletions

76
docs/memory-hotplug.txt Normal file
View File

@ -0,0 +1,76 @@
QEMU memory hotplug
===================
This document explains how to use the memory hotplug feature in QEMU,
which is present since v2.1.0.
Please, note that memory hotunplug is not supported yet. This means
that you're able to add memory, but you're not able to remove it.
Also, proper guest support is required for memory hotplug to work.
Basic RAM hotplug
-----------------
In order to be able to hotplug memory, QEMU has to be told how many
hotpluggable memory slots to create and what is the maximum amount of
memory the guest can grow. This is done at startup time by means of
the -m command-line option, which has the following format:
-m [size=]megs[,slots=n,maxmem=size]
Where,
- "megs" is the startup RAM. It is the RAM the guest will boot with
- "slots" is the number of hotpluggable memory slots
- "maxmem" is the maximum RAM size the guest can have
For example, the following command-line:
qemu [...] 1G,slots=3,maxmem=4G
Creates a guest with 1GB of memory and three hotpluggable memory slots.
The hotpluggable memory slots are empty when the guest is booted, so all
memory the guest will see after boot is 1GB. The maximum memory the
guest can reach is 4GB. This means that three additional gigabytes can be
hotplugged by using any combination of the available memory slots.
Two monitor commands are used to hotplug memory:
- "object_add": creates a memory backend object
- "device_add": creates a front-end pc-dimm device and inserts it
into the first empty slot
For example, the following commands add another 1GB to the guest
discussed earlier:
(qemu) object_add memory-backend-ram,id=mem1,size=1G
(qemu) device_add pc-dimm,id=dimm1,memdev=mem1
Using the file backend
----------------------
Besides basic RAM hotplug, QEMU also supports using files as a memory
backend. This is useful for using hugetlbfs in Linux, which provides
access to bigger page sizes.
For example, assuming that the host has 1GB hugepages available in
the /mnt/hugepages-1GB directory, a 1GB hugepage could be hotplugged
into the guest from the previous section with the following commands:
(qemu) object_add memory-backend-file,id=mem1,size=1G,mem-path=/mnt/hugepages-1GB
(qemu) device_add pc-dimm,id=dimm1,memdev=mem1
It's also possible to start a guest with memory cold-plugged into the
hotpluggable memory slots. This might seem counterintuitive at first,
but this allows for a lot of flexibility when using the file backend.
In the following command-line example, a 8GB guest is created where 6GB
comes from regular RAM, 1GB is a 1GB hugepage page and 256MB is from
2MB pages. Also, the guest has additional memory slots to hotplug more
2GB if needed:
qemu [...] -m 6GB,slots=4,maxmem=10G \
-object memory-backend-file,id=mem1,size=1G,mem-path=/mnt/hugepages-1G \
-device pc-dimm,id=dimm1,memdev=mem1 \
-object memory-backend-file,id=mem2,size=256M,mem-path=/mnt/hugepages-2MB \
-device pc-dimm,id=dimm2,memdev=mem2

View File

@ -100,6 +100,32 @@ int qmp_pc_dimm_device_list(Object *obj, void *opaque)
return 0; return 0;
} }
ram_addr_t get_current_ram_size(void)
{
MemoryDeviceInfoList *info_list = NULL;
MemoryDeviceInfoList **prev = &info_list;
MemoryDeviceInfoList *info;
ram_addr_t size = ram_size;
qmp_pc_dimm_device_list(qdev_get_machine(), &prev);
for (info = info_list; info; info = info->next) {
MemoryDeviceInfo *value = info->value;
if (value) {
switch (value->kind) {
case MEMORY_DEVICE_INFO_KIND_DIMM:
size += value->dimm->size;
break;
default:
break;
}
}
}
qapi_free_MemoryDeviceInfoList(info_list);
return size;
}
static int pc_dimm_slot2bitmap(Object *obj, void *opaque) static int pc_dimm_slot2bitmap(Object *obj, void *opaque)
{ {
unsigned long *bitmap = opaque; unsigned long *bitmap = opaque;

View File

@ -25,6 +25,7 @@
#include "exec/address-spaces.h" #include "exec/address-spaces.h"
#include "qapi/visitor.h" #include "qapi/visitor.h"
#include "qapi-event.h" #include "qapi-event.h"
#include "trace.h"
#if defined(__linux__) #if defined(__linux__)
#include <sys/mman.h> #include <sys/mman.h>
@ -222,6 +223,8 @@ static void virtio_balloon_handle_output(VirtIODevice *vdev, VirtQueue *vq)
if (!int128_nz(section.size) || !memory_region_is_ram(section.mr)) if (!int128_nz(section.size) || !memory_region_is_ram(section.mr))
continue; continue;
trace_virtio_balloon_handle_output(memory_region_name(section.mr),
pa);
/* Using memory_region_get_ram_ptr is bending the rules a bit, but /* Using memory_region_get_ram_ptr is bending the rules a bit, but
should be OK because we only want a single page. */ should be OK because we only want a single page. */
addr = section.offset_within_region; addr = section.offset_within_region;
@ -285,6 +288,7 @@ static void virtio_balloon_get_config(VirtIODevice *vdev, uint8_t *config_data)
config.num_pages = cpu_to_le32(dev->num_pages); config.num_pages = cpu_to_le32(dev->num_pages);
config.actual = cpu_to_le32(dev->actual); config.actual = cpu_to_le32(dev->actual);
trace_virtio_balloon_get_config(config.num_pages, config.actual);
memcpy(config_data, &config, sizeof(struct virtio_balloon_config)); memcpy(config_data, &config, sizeof(struct virtio_balloon_config));
} }
@ -294,13 +298,16 @@ static void virtio_balloon_set_config(VirtIODevice *vdev,
VirtIOBalloon *dev = VIRTIO_BALLOON(vdev); VirtIOBalloon *dev = VIRTIO_BALLOON(vdev);
struct virtio_balloon_config config; struct virtio_balloon_config config;
uint32_t oldactual = dev->actual; uint32_t oldactual = dev->actual;
ram_addr_t vm_ram_size = get_current_ram_size();
memcpy(&config, config_data, sizeof(struct virtio_balloon_config)); memcpy(&config, config_data, sizeof(struct virtio_balloon_config));
dev->actual = le32_to_cpu(config.actual); dev->actual = le32_to_cpu(config.actual);
if (dev->actual != oldactual) { if (dev->actual != oldactual) {
qapi_event_send_balloon_change(ram_size - qapi_event_send_balloon_change(vm_ram_size -
((ram_addr_t) dev->actual << VIRTIO_BALLOON_PFN_SHIFT), ((ram_addr_t) dev->actual << VIRTIO_BALLOON_PFN_SHIFT),
&error_abort); &error_abort);
} }
trace_virtio_balloon_set_config(dev->actual, oldactual);
} }
static uint32_t virtio_balloon_get_features(VirtIODevice *vdev, uint32_t f) static uint32_t virtio_balloon_get_features(VirtIODevice *vdev, uint32_t f)
@ -312,22 +319,24 @@ static uint32_t virtio_balloon_get_features(VirtIODevice *vdev, uint32_t f)
static void virtio_balloon_stat(void *opaque, BalloonInfo *info) static void virtio_balloon_stat(void *opaque, BalloonInfo *info)
{ {
VirtIOBalloon *dev = opaque; VirtIOBalloon *dev = opaque;
info->actual = ram_size - ((uint64_t) dev->actual << info->actual = get_current_ram_size() - ((uint64_t) dev->actual <<
VIRTIO_BALLOON_PFN_SHIFT); VIRTIO_BALLOON_PFN_SHIFT);
} }
static void virtio_balloon_to_target(void *opaque, ram_addr_t target) static void virtio_balloon_to_target(void *opaque, ram_addr_t target)
{ {
VirtIOBalloon *dev = VIRTIO_BALLOON(opaque); VirtIOBalloon *dev = VIRTIO_BALLOON(opaque);
VirtIODevice *vdev = VIRTIO_DEVICE(dev); VirtIODevice *vdev = VIRTIO_DEVICE(dev);
ram_addr_t vm_ram_size = get_current_ram_size();
if (target > ram_size) { if (target > vm_ram_size) {
target = ram_size; target = vm_ram_size;
} }
if (target) { if (target) {
dev->num_pages = (ram_size - target) >> VIRTIO_BALLOON_PFN_SHIFT; dev->num_pages = (vm_ram_size - target) >> VIRTIO_BALLOON_PFN_SHIFT;
virtio_notify_config(vdev); virtio_notify_config(vdev);
} }
trace_virtio_balloon_to_target(target, dev->num_pages);
} }
static void virtio_balloon_save(QEMUFile *f, void *opaque) static void virtio_balloon_save(QEMUFile *f, void *opaque)

View File

@ -52,6 +52,7 @@ typedef uintptr_t ram_addr_t;
#endif #endif
extern ram_addr_t ram_size; extern ram_addr_t ram_size;
ram_addr_t get_current_ram_size(void);
/* memory API */ /* memory API */

View File

@ -237,12 +237,24 @@ DEF("m", HAS_ARG, QEMU_OPTION_m,
"NOTE: Some architectures might enforce a specific granularity\n", "NOTE: Some architectures might enforce a specific granularity\n",
QEMU_ARCH_ALL) QEMU_ARCH_ALL)
STEXI STEXI
@item -m [size=]@var{megs} @item -m [size=]@var{megs}[,slots=n,maxmem=size]
@findex -m @findex -m
Set virtual RAM size to @var{megs} megabytes. Default is 128 MiB. Optionally, Sets guest startup RAM size to @var{megs} megabytes. Default is 128 MiB.
a suffix of ``M'' or ``G'' can be used to signify a value in megabytes or Optionally, a suffix of ``M'' or ``G'' can be used to signify a value in
gigabytes respectively. Optional pair @var{slots}, @var{maxmem} could be used megabytes or gigabytes respectively. Optional pair @var{slots}, @var{maxmem}
to set amount of hotluggable memory slots and possible maximum amount of memory. could be used to set amount of hotpluggable memory slots and maximum amount of
memory. Note that @var{maxmem} must be aligned to the page size.
For example, the following command-line sets the guest startup RAM size to
1GB, creates 3 slots to hotplug additional memory and sets the maximum
memory the guest can reach to 4GB:
@example
qemu-system-x86_64 -m 1G,slots=3,maxmem=4G
@end example
If @var{slots} and @var{maxmem} are not specified, memory hotplug won't
be enabled and the guest startup RAM will never increase.
ETEXI ETEXI
DEF("mem-path", HAS_ARG, QEMU_OPTION_mempath, DEF("mem-path", HAS_ARG, QEMU_OPTION_mempath,

View File

@ -5,3 +5,8 @@ int qmp_pc_dimm_device_list(Object *obj, void *opaque)
{ {
return 0; return 0;
} }
ram_addr_t get_current_ram_size(void)
{
return ram_size;
}

View File

@ -143,6 +143,10 @@ cpu_out(unsigned int addr, unsigned int val) "addr %#x value %u"
# balloon.c # balloon.c
# Since requests are raised via monitor, not many tracepoints are needed. # Since requests are raised via monitor, not many tracepoints are needed.
balloon_event(void *opaque, unsigned long addr) "opaque %p addr %lu" balloon_event(void *opaque, unsigned long addr) "opaque %p addr %lu"
virtio_balloon_handle_output(const char *name, uint64_t gpa) "setion name: %s gpa: %"PRIx64""
virtio_balloon_get_config(uint32_t num_pages, uint32_t acutal) "num_pages: %d acutal: %d"
virtio_balloon_set_config(uint32_t acutal, uint32_t oldacutal) "acutal: %d oldacutal: %d"
virtio_balloon_to_target(uint64_t target, uint32_t num_pages) "balloon target: %"PRIx64" num_pages: %d"
# hw/intc/apic_common.c # hw/intc/apic_common.c
cpu_set_apic_base(uint64_t val) "%016"PRIx64 cpu_set_apic_base(uint64_t val) "%016"PRIx64