pc-dimm: factor out MemoryDevice interface
On the qmp level, we already have the concept of memory devices:
"query-memory-devices"
Right now, we only support NVDIMM and PCDIMM.
We want to map other devices later into the address space of the guest.
Such device could e.g. be virtio devices. These devices will have a
guest memory range assigned but won't be exposed via e.g. ACPI. We want
to make them look like memory device, but not glued to pc-dimm.
Especially, it will not always be possible to have TYPE_PC_DIMM as a parent
class (e.g. virtio devices). Let's use an interface instead. As a first
part, convert handling of
- qmp_pc_dimm_device_list
- get_plugged_memory_size
to our new model. plug/unplug stuff etc. will follow later.
A memory device will have to provide the following functions:
- get_addr(): Necessary, as the property "addr" can e.g. not be used for
virtio devices (already defined).
- get_plugged_size(): The amount this device offers to the guest as of
now.
- get_region_size(): Because this can later on be bigger than the
plugged size.
- fill_device_info(): Fill MemoryDeviceInfo, e.g. for qmp.
Reviewed-by: David Gibson <david@gibson.dropbear.id.au>
Signed-off-by: David Hildenbrand <david@redhat.com>
Message-Id: <20180423165126.15441-2-david@redhat.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
diff --git a/hw/mem/memory-device.c b/hw/mem/memory-device.c
new file mode 100644
index 0000000..6cbdaf9
--- /dev/null
+++ b/hw/mem/memory-device.c
@@ -0,0 +1,120 @@
+/*
+ * Memory Device Interface
+ *
+ * Copyright ProfitBricks GmbH 2012
+ * Copyright (C) 2014 Red Hat Inc
+ * Copyright (c) 2018 Red Hat Inc
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/mem/memory-device.h"
+#include "hw/qdev.h"
+#include "qapi/error.h"
+#include "hw/boards.h"
+#include "qemu/range.h"
+
+static gint memory_device_addr_sort(gconstpointer a, gconstpointer b)
+{
+ const MemoryDeviceState *md_a = MEMORY_DEVICE(a);
+ const MemoryDeviceState *md_b = MEMORY_DEVICE(b);
+ const MemoryDeviceClass *mdc_a = MEMORY_DEVICE_GET_CLASS(a);
+ const MemoryDeviceClass *mdc_b = MEMORY_DEVICE_GET_CLASS(b);
+ const uint64_t addr_a = mdc_a->get_addr(md_a);
+ const uint64_t addr_b = mdc_b->get_addr(md_b);
+
+ if (addr_a > addr_b) {
+ return 1;
+ } else if (addr_a < addr_b) {
+ return -1;
+ }
+ return 0;
+}
+
+static int memory_device_build_list(Object *obj, void *opaque)
+{
+ GSList **list = opaque;
+
+ if (object_dynamic_cast(obj, TYPE_MEMORY_DEVICE)) {
+ DeviceState *dev = DEVICE(obj);
+ if (dev->realized) { /* only realized memory devices matter */
+ *list = g_slist_insert_sorted(*list, dev, memory_device_addr_sort);
+ }
+ }
+
+ object_child_foreach(obj, memory_device_build_list, opaque);
+ return 0;
+}
+
+MemoryDeviceInfoList *qmp_memory_device_list(void)
+{
+ GSList *devices = NULL, *item;
+ MemoryDeviceInfoList *list = NULL, *prev = NULL;
+
+ object_child_foreach(qdev_get_machine(), memory_device_build_list,
+ &devices);
+
+ for (item = devices; item; item = g_slist_next(item)) {
+ const MemoryDeviceState *md = MEMORY_DEVICE(item->data);
+ const MemoryDeviceClass *mdc = MEMORY_DEVICE_GET_CLASS(item->data);
+ MemoryDeviceInfoList *elem = g_new0(MemoryDeviceInfoList, 1);
+ MemoryDeviceInfo *info = g_new0(MemoryDeviceInfo, 1);
+
+ mdc->fill_device_info(md, info);
+
+ elem->value = info;
+ elem->next = NULL;
+ if (prev) {
+ prev->next = elem;
+ } else {
+ list = elem;
+ }
+ prev = elem;
+ }
+
+ g_slist_free(devices);
+
+ return list;
+}
+
+static int memory_device_plugged_size(Object *obj, void *opaque)
+{
+ uint64_t *size = opaque;
+
+ if (object_dynamic_cast(obj, TYPE_MEMORY_DEVICE)) {
+ const DeviceState *dev = DEVICE(obj);
+ const MemoryDeviceState *md = MEMORY_DEVICE(obj);
+ const MemoryDeviceClass *mdc = MEMORY_DEVICE_GET_CLASS(obj);
+
+ if (dev->realized) {
+ *size += mdc->get_plugged_size(md);
+ }
+ }
+
+ object_child_foreach(obj, memory_device_plugged_size, opaque);
+ return 0;
+}
+
+uint64_t get_plugged_memory_size(void)
+{
+ uint64_t size = 0;
+
+ memory_device_plugged_size(qdev_get_machine(), &size);
+
+ return size;
+}
+
+static const TypeInfo memory_device_info = {
+ .name = TYPE_MEMORY_DEVICE,
+ .parent = TYPE_INTERFACE,
+ .class_size = sizeof(MemoryDeviceClass),
+};
+
+static void memory_device_register_types(void)
+{
+ type_register_static(&memory_device_info);
+}
+
+type_init(memory_device_register_types)