diff options
author | David Hildenbrand <david@redhat.com> | 2018-04-23 18:51:20 +0200 |
---|---|---|
committer | Eduardo Habkost <ehabkost@redhat.com> | 2018-05-07 10:00:02 -0300 |
commit | bb0831bdf45a61c83fa1def44ae391260ce2662d (patch) | |
tree | d195a8f86381c9ef931000428af1d07766809ca3 /hw/mem | |
parent | bd6c3e4a4975ee1e5cadbc1826af9bd0ca0954c2 (diff) | |
download | qemu-bb0831bdf45a61c83fa1def44ae391260ce2662d.zip qemu-bb0831bdf45a61c83fa1def44ae391260ce2662d.tar.gz qemu-bb0831bdf45a61c83fa1def44ae391260ce2662d.tar.bz2 |
pc-dimm: factor out address search into MemoryDevice code
This mainly moves code, but does a handfull of optimizations:
- We pass the machine instead of the address space properties
- We check the hinted address directly and handle fragmented memory
better
- We make the search independent of pc-dimm
Signed-off-by: David Hildenbrand <david@redhat.com>
Message-Id: <20180423165126.15441-6-david@redhat.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
Diffstat (limited to 'hw/mem')
-rw-r--r-- | hw/mem/memory-device.c | 86 | ||||
-rw-r--r-- | hw/mem/pc-dimm.c | 109 |
2 files changed, 88 insertions, 107 deletions
diff --git a/hw/mem/memory-device.c b/hw/mem/memory-device.c index 6cbdaf9..a2cb854 100644 --- a/hw/mem/memory-device.c +++ b/hw/mem/memory-device.c @@ -48,6 +48,92 @@ static int memory_device_build_list(Object *obj, void *opaque) return 0; } +uint64_t memory_device_get_free_addr(MachineState *ms, const uint64_t *hint, + uint64_t align, uint64_t size, + Error **errp) +{ + uint64_t address_space_start, address_space_end; + GSList *list = NULL, *item; + uint64_t new_addr = 0; + + if (!ms->device_memory) { + error_setg(errp, "memory devices (e.g. for memory hotplug) are not " + "supported by the machine"); + return 0; + } + + if (!memory_region_size(&ms->device_memory->mr)) { + error_setg(errp, "memory devices (e.g. for memory hotplug) are not " + "enabled, please specify the maxmem option"); + return 0; + } + address_space_start = ms->device_memory->base; + address_space_end = address_space_start + + memory_region_size(&ms->device_memory->mr); + g_assert(QEMU_ALIGN_UP(address_space_start, align) == address_space_start); + g_assert(address_space_end >= address_space_start); + + if (hint && QEMU_ALIGN_UP(*hint, align) != *hint) { + error_setg(errp, "address must be aligned to 0x%" PRIx64 " bytes", + align); + return 0; + } + + if (QEMU_ALIGN_UP(size, align) != size) { + error_setg(errp, "backend memory size must be multiple of 0x%" + PRIx64, align); + return 0; + } + + if (hint) { + new_addr = *hint; + if (new_addr < address_space_start) { + error_setg(errp, "can't add memory [0x%" PRIx64 ":0x%" PRIx64 + "] at 0x%" PRIx64, new_addr, size, address_space_start); + return 0; + } else if ((new_addr + size) > address_space_end) { + error_setg(errp, "can't add memory [0x%" PRIx64 ":0x%" PRIx64 + "] beyond 0x%" PRIx64, new_addr, size, + address_space_end); + return 0; + } + } else { + new_addr = address_space_start; + } + + /* find address range that will fit new memory device */ + object_child_foreach(OBJECT(ms), memory_device_build_list, &list); + for (item = list; item; item = g_slist_next(item)) { + const MemoryDeviceState *md = item->data; + const MemoryDeviceClass *mdc = MEMORY_DEVICE_GET_CLASS(OBJECT(md)); + uint64_t md_size, md_addr; + + md_addr = mdc->get_addr(md); + md_size = mdc->get_region_size(md); + if (*errp) { + goto out; + } + + if (ranges_overlap(md_addr, md_size, new_addr, size)) { + if (hint) { + const DeviceState *d = DEVICE(md); + error_setg(errp, "address range conflicts with '%s'", d->id); + goto out; + } + new_addr = QEMU_ALIGN_UP(md_addr + md_size, align); + } + } + + if (new_addr + size > address_space_end) { + error_setg(errp, "could not find position in guest address space for " + "memory device - memory fragmented due to alignments"); + goto out; + } +out: + g_slist_free(list); + return new_addr; +} + MemoryDeviceInfoList *qmp_memory_device_list(void) { GSList *devices = NULL, *item; diff --git a/hw/mem/pc-dimm.c b/hw/mem/pc-dimm.c index 37b8be8..9b70174 100644 --- a/hw/mem/pc-dimm.c +++ b/hw/mem/pc-dimm.c @@ -23,9 +23,7 @@ #include "hw/mem/nvdimm.h" #include "hw/mem/memory-device.h" #include "qapi/error.h" -#include "qemu/config-file.h" #include "qapi/visitor.h" -#include "qemu/range.h" #include "sysemu/numa.h" #include "sysemu/kvm.h" #include "trace.h" @@ -60,10 +58,8 @@ void pc_dimm_memory_plug(DeviceState *dev, MachineState *machine, goto out; } - addr = pc_dimm_get_free_addr(hpms->base, - memory_region_size(&hpms->mr), - !addr ? NULL : &addr, align, - memory_region_size(mr), &local_err); + addr = memory_device_get_free_addr(machine, !addr ? NULL : &addr, align, + memory_region_size(mr), &local_err); if (local_err) { goto out; } @@ -211,107 +207,6 @@ out: return slot; } -static gint pc_dimm_addr_sort(gconstpointer a, gconstpointer b) -{ - PCDIMMDevice *x = PC_DIMM(a); - PCDIMMDevice *y = PC_DIMM(b); - Int128 diff = int128_sub(int128_make64(x->addr), int128_make64(y->addr)); - - if (int128_lt(diff, int128_zero())) { - return -1; - } else if (int128_gt(diff, int128_zero())) { - return 1; - } - return 0; -} - -static int pc_dimm_built_list(Object *obj, void *opaque) -{ - GSList **list = opaque; - - if (object_dynamic_cast(obj, TYPE_PC_DIMM)) { - DeviceState *dev = DEVICE(obj); - if (dev->realized) { /* only realized DIMMs matter */ - *list = g_slist_insert_sorted(*list, dev, pc_dimm_addr_sort); - } - } - - object_child_foreach(obj, pc_dimm_built_list, opaque); - return 0; -} - -uint64_t pc_dimm_get_free_addr(uint64_t address_space_start, - uint64_t address_space_size, - uint64_t *hint, uint64_t align, uint64_t size, - Error **errp) -{ - GSList *list = NULL, *item; - uint64_t new_addr, ret = 0; - uint64_t address_space_end = address_space_start + address_space_size; - - g_assert(QEMU_ALIGN_UP(address_space_start, align) == address_space_start); - - if (!address_space_size) { - error_setg(errp, "memory hotplug is not enabled, " - "please add maxmem option"); - goto out; - } - - if (hint && QEMU_ALIGN_UP(*hint, align) != *hint) { - error_setg(errp, "address must be aligned to 0x%" PRIx64 " bytes", - align); - goto out; - } - - if (QEMU_ALIGN_UP(size, align) != size) { - error_setg(errp, "backend memory size must be multiple of 0x%" - PRIx64, align); - goto out; - } - - assert(address_space_end > address_space_start); - object_child_foreach(qdev_get_machine(), pc_dimm_built_list, &list); - - if (hint) { - new_addr = *hint; - } else { - new_addr = address_space_start; - } - - /* find address range that will fit new DIMM */ - for (item = list; item; item = g_slist_next(item)) { - PCDIMMDevice *dimm = item->data; - uint64_t dimm_size = object_property_get_uint(OBJECT(dimm), - PC_DIMM_SIZE_PROP, - errp); - if (errp && *errp) { - goto out; - } - - if (ranges_overlap(dimm->addr, dimm_size, new_addr, size)) { - if (hint) { - DeviceState *d = DEVICE(dimm); - error_setg(errp, "address range conflicts with '%s'", d->id); - goto out; - } - new_addr = QEMU_ALIGN_UP(dimm->addr + dimm_size, align); - } - } - ret = new_addr; - - if (new_addr < address_space_start) { - error_setg(errp, "can't add memory [0x%" PRIx64 ":0x%" PRIx64 - "] at 0x%" PRIx64, new_addr, size, address_space_start); - } else if ((new_addr + size) > address_space_end) { - error_setg(errp, "can't add memory [0x%" PRIx64 ":0x%" PRIx64 - "] beyond 0x%" PRIx64, new_addr, size, address_space_end); - } - -out: - g_slist_free(list); - return ret; -} - static Property pc_dimm_properties[] = { DEFINE_PROP_UINT64(PC_DIMM_ADDR_PROP, PCDIMMDevice, addr, 0), DEFINE_PROP_UINT32(PC_DIMM_NODE_PROP, PCDIMMDevice, node, 0), |