mirror of https://github.com/xemu-project/xemu.git
pc-dimm: add busy address check and address auto-allocation
- if 'addr' property is not specified on -device/device_add command, treat the default value as request for assigning PCDIMMDevice to the first free memory region. - if 'addr' is provided with -device/device_add command, attempt to use it or fail command if it's already occupied or falls inside of an existing PCDIMMDevice memory region. Note: GCompareFunc(a, b) used by g_slist_insert_sorted() returns 'gint', however it might be too small to fit difference between 2 addresses. So use 128bit to calculate the difference and normalize result to -1/0/1 return values. Signed-off-by: Igor Mammedov <imammedo@redhat.com> Signed-off-by: Tang Chen <tangchen@cn.fujitsu.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> Tested-by: Andrey Korolyov <andrey@xdel.ru> MST: commit log tweaks
This commit is contained in:
parent
95bee274fd
commit
0b31257116
|
@ -1558,6 +1558,15 @@ static void pc_dimm_plug(HotplugHandler *hotplug_dev,
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addr = pc_dimm_get_free_addr(pcms->hotplug_memory_base,
|
||||||
|
memory_region_size(&pcms->hotplug_memory),
|
||||||
|
!addr ? NULL : &addr,
|
||||||
|
memory_region_size(mr), &local_err);
|
||||||
|
if (local_err) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
object_property_set_int(OBJECT(dev), addr, PC_DIMM_ADDR_PROP, &local_err);
|
||||||
|
|
||||||
memory_region_add_subregion(&pcms->hotplug_memory,
|
memory_region_add_subregion(&pcms->hotplug_memory,
|
||||||
addr - pcms->hotplug_memory_base, mr);
|
addr - pcms->hotplug_memory_base, mr);
|
||||||
vmstate_register_ram(mr, dev);
|
vmstate_register_ram(mr, dev);
|
||||||
|
|
|
@ -21,6 +21,88 @@
|
||||||
#include "hw/mem/pc-dimm.h"
|
#include "hw/mem/pc-dimm.h"
|
||||||
#include "qemu/config-file.h"
|
#include "qemu/config-file.h"
|
||||||
#include "qapi/visitor.h"
|
#include "qapi/visitor.h"
|
||||||
|
#include "qemu/range.h"
|
||||||
|
|
||||||
|
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 size,
|
||||||
|
Error **errp)
|
||||||
|
{
|
||||||
|
GSList *list = NULL, *item;
|
||||||
|
uint64_t new_addr, ret = 0;
|
||||||
|
uint64_t address_space_end = address_space_start + address_space_size;
|
||||||
|
|
||||||
|
assert(address_space_end > address_space_size);
|
||||||
|
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_int(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 = dimm->addr + dimm_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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[] = {
|
static Property pc_dimm_properties[] = {
|
||||||
DEFINE_PROP_UINT64(PC_DIMM_ADDR_PROP, PCDIMMDevice, addr, 0),
|
DEFINE_PROP_UINT64(PC_DIMM_ADDR_PROP, PCDIMMDevice, addr, 0),
|
||||||
|
|
|
@ -70,4 +70,8 @@ typedef struct PCDIMMDeviceClass {
|
||||||
MemoryRegion *(*get_memory_region)(PCDIMMDevice *dimm);
|
MemoryRegion *(*get_memory_region)(PCDIMMDevice *dimm);
|
||||||
} PCDIMMDeviceClass;
|
} PCDIMMDeviceClass;
|
||||||
|
|
||||||
|
uint64_t pc_dimm_get_free_addr(uint64_t address_space_start,
|
||||||
|
uint64_t address_space_size,
|
||||||
|
uint64_t *hint, uint64_t size,
|
||||||
|
Error **errp);
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue