From b8865591d4d5680b4f766c25ca1db110320b4d15 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Fri, 31 Oct 2014 16:38:32 +0000 Subject: pc: kvm: check if KVM has free memory slots to avoid abort() When more memory devices are used than available KVM memory slots, QEMU crashes with: kvm_alloc_slot: no free slot available Aborted (core dumped) Fix this by checking that KVM has a free slot before attempting to map memory in guest address space. Signed-off-by: Igor Mammedov Acked-by: Paolo Bonzini Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/pc.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'hw/i386/pc.c') diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 1205db8..ce7b752 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1598,6 +1598,11 @@ static void pc_dimm_plug(HotplugHandler *hotplug_dev, goto out; } + if (kvm_enabled() && !kvm_has_free_slot(machine)) { + error_setg(&local_err, "hypervisor has no free memory slots left"); + goto out; + } + memory_region_add_subregion(&pcms->hotplug_memory, addr - pcms->hotplug_memory_base, mr); vmstate_register_ram(mr, dev); -- cgit v1.1 From 34dde13685ebc2c07923f32ad69e40b27c0e0bb4 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Fri, 31 Oct 2014 16:38:35 +0000 Subject: pc: make pc_dimm_plug() more readble split addr initialization from declaration so that later when new local vars are added property getter wouldn't drift off of error check. Signed-off-by: Igor Mammedov Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/pc.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'hw/i386/pc.c') diff --git a/hw/i386/pc.c b/hw/i386/pc.c index ce7b752..70ae3cf 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1556,8 +1556,9 @@ static void pc_dimm_plug(HotplugHandler *hotplug_dev, PCDIMMDevice *dimm = PC_DIMM(dev); PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm); MemoryRegion *mr = ddc->get_memory_region(dimm); - uint64_t addr = object_property_get_int(OBJECT(dimm), PC_DIMM_ADDR_PROP, - &local_err); + uint64_t addr; + + addr = object_property_get_int(OBJECT(dimm), PC_DIMM_ADDR_PROP, &local_err); if (local_err) { goto out; } -- cgit v1.1 From 92a37a04d6e034b73ea1ba4825ba4d5860f0a810 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Fri, 31 Oct 2014 16:38:36 +0000 Subject: pc: limit DIMM address and size to page aligned values When running in KVM mode, kvm_set_phys_mem() will silently fail if registered MemoryRegion address/size is not page aligned. Causing memory hotplug failure in guest. Mapping non aligned MemoryRegion in TCG mode 'works', but sane guest OS still expects page aligned memory module and fails to initialize it if it's not aligned. So do not allow non aligned (i.e. valid) address/size values for DIMM to avoid either KVM failure or guest issues caused by it. Signed-off-by: Igor Mammedov Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/pc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'hw/i386/pc.c') diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 70ae3cf..33928b9 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1556,6 +1556,7 @@ static void pc_dimm_plug(HotplugHandler *hotplug_dev, PCDIMMDevice *dimm = PC_DIMM(dev); PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm); MemoryRegion *mr = ddc->get_memory_region(dimm); + uint64_t align = TARGET_PAGE_SIZE; uint64_t addr; addr = object_property_get_int(OBJECT(dimm), PC_DIMM_ADDR_PROP, &local_err); @@ -1565,7 +1566,7 @@ static void pc_dimm_plug(HotplugHandler *hotplug_dev, addr = pc_dimm_get_free_addr(pcms->hotplug_memory_base, memory_region_size(&pcms->hotplug_memory), - !addr ? NULL : &addr, + !addr ? NULL : &addr, align, memory_region_size(mr), &local_err); if (local_err) { goto out; -- cgit v1.1 From 91aa70ab2a748e3a72004d1a729248221b7bb24a Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Fri, 31 Oct 2014 16:38:39 +0000 Subject: pc: align DIMM's address/size by backend's alignment value Performance wise it's better to align GVA by the backend's page size. Also do not allow to create DIMM device with suboptimal size (i.e. not aligned to backends page size) to aviod memory loss. Do above only for 2.2 and newer machine types to avoid breaking working configs with 2.1 machine type. Signed-off-by: Igor Mammedov Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/pc.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'hw/i386/pc.c') diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 33928b9..021ec44 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1564,6 +1564,10 @@ static void pc_dimm_plug(HotplugHandler *hotplug_dev, goto out; } + if (memory_region_get_alignment(mr) && pcms->enforce_aligned_dimm) { + align = memory_region_get_alignment(mr); + } + addr = pc_dimm_get_free_addr(pcms->hotplug_memory_base, memory_region_size(&pcms->hotplug_memory), !addr ? NULL : &addr, align, @@ -1732,6 +1736,13 @@ static void pc_machine_set_vmport(Object *obj, bool value, Error **errp) pcms->vmport = value; } +static bool pc_machine_get_aligned_dimm(Object *obj, Error **errp) +{ + PCMachineState *pcms = PC_MACHINE(obj); + + return pcms->enforce_aligned_dimm; +} + static void pc_machine_initfn(Object *obj) { PCMachineState *pcms = PC_MACHINE(obj); @@ -1744,11 +1755,17 @@ static void pc_machine_initfn(Object *obj) pc_machine_get_max_ram_below_4g, pc_machine_set_max_ram_below_4g, NULL, NULL, NULL); + pcms->vmport = !xen_enabled(); object_property_add_bool(obj, PC_MACHINE_VMPORT, pc_machine_get_vmport, pc_machine_set_vmport, NULL); + + pcms->enforce_aligned_dimm = true; + object_property_add_bool(obj, PC_MACHINE_ENFORCE_ALIGNED_DIMM, + pc_machine_get_aligned_dimm, + NULL, NULL); } static void pc_machine_class_init(ObjectClass *oc, void *data) -- cgit v1.1 From b03541fa7722d64a1c961a8467d778d7e086a933 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Fri, 31 Oct 2014 16:38:41 +0000 Subject: pc: explicitly check maxmem limit when adding DIMM Currently maxmem limit is not checked and depends on hotplug region container not being able to fit more RAM than maxmem. Do check explicitly so that it would be possible to change hotplug container size later to deal with fragmentation. Signed-off-by: Igor Mammedov Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/pc.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) (limited to 'hw/i386/pc.c') diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 021ec44..3d732cf 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1545,6 +1545,37 @@ void qemu_register_pc_machine(QEMUMachine *m) g_free(name); } +static int pc_dimm_count(Object *obj, void *opaque) +{ + int *count = opaque; + + if (object_dynamic_cast(obj, TYPE_PC_DIMM)) { + (*count)++; + } + + object_child_foreach(obj, pc_dimm_count, opaque); + return 0; +} + +static int pc_existing_dimms_capacity(Object *obj, void *opaque) +{ + Error *local_err = NULL; + uint64_t *size = opaque; + + if (object_dynamic_cast(obj, TYPE_PC_DIMM)) { + (*size) += object_property_get_int(obj, PC_DIMM_SIZE_PROP, &local_err); + + if (local_err) { + qerror_report_err(local_err); + error_free(local_err); + return 1; + } + } + + object_child_foreach(obj, pc_dimm_count, opaque); + return 0; +} + static void pc_dimm_plug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { @@ -1556,6 +1587,7 @@ static void pc_dimm_plug(HotplugHandler *hotplug_dev, PCDIMMDevice *dimm = PC_DIMM(dev); PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm); MemoryRegion *mr = ddc->get_memory_region(dimm); + uint64_t existing_dimms_capacity = 0; uint64_t align = TARGET_PAGE_SIZE; uint64_t addr; @@ -1576,6 +1608,19 @@ static void pc_dimm_plug(HotplugHandler *hotplug_dev, goto out; } + if (pc_existing_dimms_capacity(OBJECT(machine), &existing_dimms_capacity)) { + error_setg(&local_err, "failed to get total size of existing DIMMs"); + goto out; + } + + if (existing_dimms_capacity + memory_region_size(mr) > + machine->maxram_size - machine->ram_size) { + error_setg(&local_err, "not enough space, currently 0x%" PRIx64 + " in use of total 0x" RAM_ADDR_FMT, + existing_dimms_capacity, machine->maxram_size); + goto out; + } + object_property_set_int(OBJECT(dev), addr, PC_DIMM_ADDR_PROP, &local_err); if (local_err) { goto out; -- cgit v1.1 From 085f8e88ba73b7ff80298b0085f6e615d3b5c45f Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Fri, 31 Oct 2014 16:38:42 +0000 Subject: pc: count in 1Gb hugepage alignment when sizing hotplug-memory container if DIMMs with different size/alignment are interleaved in creation order, it could lead to hotplug-memory container fragmentation and following inability to use all RAM upto maxmem. For example: -m 4G,slots=3,maxmem=7G -object memory-backend-file,id=mem-1,size=256M,mem-path=/pagesize-2MB -device pc-dimm,id=mem1,memdev=mem-1 -object memory-backend-file,id=mem-2,size=1G,mem-path=/pagesize-1GB -device pc-dimm,id=mem2,memdev=mem-2 -object memory-backend-file,id=mem-3,size=256M,mem-path=/pagesize-2MB -device pc-dimm,id=mem3,memdev=mem-3 fragments hotplug-memory container and doesn't allow to use 1GB hugepage backend to consume remainig 1Gb. To ease managment factor count in max 1Gb alignment for each memory slot when sizing hotplug-memory region so that regadless of fragmentaion it would be possible to add max aligned DIMM. Signed-off-by: Igor Mammedov Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/pc.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'hw/i386/pc.c') diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 3d732cf..8be50a4 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1247,6 +1247,11 @@ FWCfgState *pc_memory_init(MachineState *machine, pcms->hotplug_memory_base = ROUND_UP(0x100000000ULL + above_4g_mem_size, 1ULL << 30); + if (pcms->enforce_aligned_dimm) { + /* size hotplug region assuming 1G page max alignment per slot */ + hotplug_mem_size += (1ULL << 30) * machine->ram_slots; + } + if ((pcms->hotplug_memory_base + hotplug_mem_size) < hotplug_mem_size) { error_report("unsupported amount of maximum memory: " RAM_ADDR_FMT, -- cgit v1.1