aboutsummaryrefslogtreecommitdiff
path: root/hw/mem/memory-device.c
diff options
context:
space:
mode:
authorDavid Hildenbrand <david@redhat.com>2018-04-23 18:51:20 +0200
committerEduardo Habkost <ehabkost@redhat.com>2018-05-07 10:00:02 -0300
commitbb0831bdf45a61c83fa1def44ae391260ce2662d (patch)
treed195a8f86381c9ef931000428af1d07766809ca3 /hw/mem/memory-device.c
parentbd6c3e4a4975ee1e5cadbc1826af9bd0ca0954c2 (diff)
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/memory-device.c')
-rw-r--r--hw/mem/memory-device.c86
1 files changed, 86 insertions, 0 deletions
diff --git a/hw/mem/memory-device.c b/hw/mem/memory-device.c
index 6cbdaf99f3..a2cb85462f 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;