qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH v5 04/14] Implement dimm device abstraction


From: Hu Tao
Subject: [Qemu-devel] [PATCH v5 04/14] Implement dimm device abstraction
Date: Wed, 26 Jun 2013 17:13:27 +0800

Each hotplug-able memory slot is a DimmDevice. All DimmDevices are attached
to a new bus called DimmBus. This bus is introduced so that we no longer
depend on hotplug-capability of main system bus (the main bus does not allow
hotplugging). The DimmBus should be attached to a chipset Device (i440fx in case
of the pc)

A hot-add operation for a particular dimm:
- creates a new DimmDevice and attaches it to the DimmBus
- creates a new MemoryRegion of the given physical address offset, size and
node proximity, and attaches it to main system memory as a sub_region.

Hotplug operations are done through normal device_add commands.
Also add properties to DimmDevice.

TODO/FIXME: Dimm configurations are specified at qemu startup at the command
line. Dimms with "populated=off" are actually not created, but their config
is saved in the corresponding bus. If user does not specify the correct bus
for a dimm device during device_add and there are multiple memory buses in the
machine (e.g. right now membus.0 and membus.pv are created in this pachset) the
first bus found in the device tree that matches the device type is picked. This
bus may be the wrong one, and the dimm config is not retrieved, thus creating a
useless dimm device. A solution would be to delete the new dimm if the dimm
config was not found (see commented qmp_device_del in dimm_init) but this does
not look clean. This design needs to be fixed somehow to avoid similar problems
if multiple memory buses are present in the same machine.

Signed-off-by: Vasilis Liaskovitis <address@hidden>
Signed-off-by: Hu Tao <address@hidden>
---
 default-configs/x86_64-softmmu.mak |   1 +
 hw/Makefile.objs                   |   1 +
 hw/mem-hotplug/Makefile.objs       |   1 +
 hw/mem-hotplug/dimm.c              | 241 +++++++++++++++++++++++++++++++++++++
 include/hw/mem-hotplug/dimm.h      |  77 ++++++++++++
 5 files changed, 321 insertions(+)
 create mode 100644 hw/mem-hotplug/Makefile.objs
 create mode 100644 hw/mem-hotplug/dimm.c
 create mode 100644 include/hw/mem-hotplug/dimm.h

diff --git a/default-configs/x86_64-softmmu.mak 
b/default-configs/x86_64-softmmu.mak
index 599b630..5348800 100644
--- a/default-configs/x86_64-softmmu.mak
+++ b/default-configs/x86_64-softmmu.mak
@@ -46,3 +46,4 @@ CONFIG_APIC=y
 CONFIG_IOAPIC=y
 CONFIG_ICC_BUS=y
 CONFIG_PVPANIC=y
+CONFIG_MEM_HOTPLUG=y
diff --git a/hw/Makefile.objs b/hw/Makefile.objs
index 0243d6a..6d3dc73 100644
--- a/hw/Makefile.objs
+++ b/hw/Makefile.objs
@@ -27,6 +27,7 @@ devices-dirs-$(CONFIG_SOFTMMU) += usb/
 devices-dirs-$(CONFIG_VIRTIO) += virtio/
 devices-dirs-$(CONFIG_SOFTMMU) += watchdog/
 devices-dirs-$(CONFIG_SOFTMMU) += xen/
+devices-dirs-$(CONFIG_MEM_HOTPLUG) += mem-hotplug/
 devices-dirs-y += core/
 common-obj-y += $(devices-dirs-y)
 obj-y += $(devices-dirs-y)
diff --git a/hw/mem-hotplug/Makefile.objs b/hw/mem-hotplug/Makefile.objs
new file mode 100644
index 0000000..7563ef5
--- /dev/null
+++ b/hw/mem-hotplug/Makefile.objs
@@ -0,0 +1 @@
+common-obj-$(CONFIG_MEM_HOTPLUG) += dimm.o
diff --git a/hw/mem-hotplug/dimm.c b/hw/mem-hotplug/dimm.c
new file mode 100644
index 0000000..09cfc4b
--- /dev/null
+++ b/hw/mem-hotplug/dimm.c
@@ -0,0 +1,241 @@
+/*
+ * Dimm device for Memory Hotplug
+ *
+ * Copyright ProfitBricks GmbH 2013
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>
+ */
+
+#include "trace.h"
+#include "hw/qdev.h"
+#include "hw/mem-hotplug/dimm.h"
+#include <time.h>
+#include "qmp-commands.h"
+
+/* the following list is used to hold dimm config info before machine
+ * is initialized. After machine init, the list is not used anymore.*/
+static DimmConfiglist dimmconfig_list =
+       QTAILQ_HEAD_INITIALIZER(dimmconfig_list);
+
+/* the list of memory buses */
+static QLIST_HEAD(, DimmBus) memory_buses;
+
+static void dimmbus_dev_print(Monitor *mon, DeviceState *dev, int indent);
+static char *dimmbus_get_fw_dev_path(DeviceState *dev);
+
+static Property dimm_properties[] = {
+    DEFINE_PROP_UINT64("start", DimmDevice, start, 0),
+    DEFINE_PROP_SIZE("size", DimmDevice, size, DEFAULT_DIMMSIZE),
+    DEFINE_PROP_UINT32("node", DimmDevice, node, 0),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void dimmbus_dev_print(Monitor *mon, DeviceState *dev, int indent)
+{
+}
+
+static char *dimmbus_get_fw_dev_path(DeviceState *dev)
+{
+    char path[40];
+
+    snprintf(path, sizeof(path), "%s", qdev_fw_name(dev));
+    return strdup(path);
+}
+
+static void dimm_bus_class_init(ObjectClass *klass, void *data)
+{
+    BusClass *k = BUS_CLASS(klass);
+
+    k->print_dev = dimmbus_dev_print;
+    k->get_fw_dev_path = dimmbus_get_fw_dev_path;
+}
+
+static void dimm_bus_initfn(Object *obj)
+{
+    DimmBus *bus = DIMM_BUS(obj);
+    QTAILQ_INIT(&bus->dimmconfig_list);
+    QTAILQ_INIT(&bus->dimmlist);
+}
+
+static const TypeInfo dimm_bus_info = {
+    .name = TYPE_DIMM_BUS,
+    .parent = TYPE_BUS,
+    .instance_size = sizeof(DimmBus),
+    .instance_init = dimm_bus_initfn,
+    .class_init = dimm_bus_class_init,
+};
+
+DimmBus *dimm_bus_create(Object *parent, const char *name, uint32_t max_dimms,
+                         dimm_calcoffset_fn pmc_set_offset)
+{
+    DimmBus *memory_bus;
+    DimmConfig *dimm_cfg, *next_cfg;
+    uint32_t num_dimms = 0;
+
+    memory_bus = g_malloc0(dimm_bus_info.instance_size);
+    memory_bus->qbus.name = name ? g_strdup(name) : "membus.0";
+    qbus_create_inplace(&memory_bus->qbus, TYPE_DIMM_BUS, DEVICE(parent),
+                        name);
+
+    QTAILQ_FOREACH_SAFE(dimm_cfg, &dimmconfig_list, nextdimmcfg, next_cfg) {
+        if (!strcmp(memory_bus->qbus.name, dimm_cfg->bus_name)) {
+            if (max_dimms && (num_dimms == max_dimms)) {
+                fprintf(stderr, "Bus %s can only accept %u number of DIMMs\n",
+                        name, max_dimms);
+            }
+            QTAILQ_REMOVE(&dimmconfig_list, dimm_cfg, nextdimmcfg);
+            QTAILQ_INSERT_TAIL(&memory_bus->dimmconfig_list, dimm_cfg,
+                               nextdimmcfg);
+
+            dimm_cfg->start = pmc_set_offset(DEVICE(parent), dimm_cfg->size);
+            num_dimms++;
+        }
+    }
+    QLIST_INSERT_HEAD(&memory_buses, memory_bus, next);
+    return memory_bus;
+}
+
+static void dimm_populate(DimmDevice *s)
+{
+    DeviceState *dev = (DeviceState *)s;
+    MemoryRegion *new = NULL;
+
+    new = g_malloc(sizeof(MemoryRegion));
+    memory_region_init_ram(new, dev->id, s->size);
+    vmstate_register_ram_global(new);
+    memory_region_add_subregion(get_system_memory(), s->start, new);
+    s->mr = new;
+}
+
+void dimm_config_create(char *id, uint64_t size, const char *bus, uint64_t 
node,
+                        uint32_t dimm_idx)
+{
+    DimmConfig *dimm_cfg;
+    dimm_cfg = (DimmConfig *) g_malloc0(sizeof(DimmConfig));
+    dimm_cfg->name = strdup(id);
+    dimm_cfg->bus_name = strdup(bus);
+    dimm_cfg->idx = dimm_idx;
+    dimm_cfg->start = 0;
+    dimm_cfg->size = size;
+    dimm_cfg->node = node;
+
+    QTAILQ_INSERT_TAIL(&dimmconfig_list, dimm_cfg, nextdimmcfg);
+}
+
+void dimm_bus_hotplug(dimm_hotplug_fn hotplug, DeviceState *qdev)
+{
+    DimmBus *bus;
+    QLIST_FOREACH(bus, &memory_buses, next) {
+        assert(bus);
+        bus->qbus.allow_hotplug = 1;
+        bus->dimm_hotplug_qdev = qdev;
+        bus->dimm_hotplug = hotplug;
+    }
+}
+
+static void dimm_plug_device(DimmDevice *slot)
+{
+    DimmBus *bus = DIMM_BUS(qdev_get_parent_bus(&slot->qdev));
+
+    dimm_populate(slot);
+    if (bus->dimm_hotplug) {
+        bus->dimm_hotplug(bus->dimm_hotplug_qdev, slot, 1);
+    }
+}
+
+static int dimm_unplug_device(DeviceState *qdev)
+{
+    return 1;
+}
+
+static DimmConfig *dimmcfg_find_from_name(DimmBus *bus, const char *name)
+{
+    DimmConfig *slot;
+
+    QTAILQ_FOREACH(slot, &bus->dimmconfig_list, nextdimmcfg) {
+        if (!strcmp(slot->name, name)) {
+            return slot;
+        }
+    }
+    return NULL;
+}
+
+void dimm_setup_fwcfg_layout(uint64_t *fw_cfg_slots)
+{
+    DimmConfig *slot;
+    DimmBus *bus;
+
+    QLIST_FOREACH(bus, &memory_buses, next) {
+        QTAILQ_FOREACH(slot, &bus->dimmconfig_list, nextdimmcfg) {
+            assert(slot->start);
+            fw_cfg_slots[3 * slot->idx] = cpu_to_le64(slot->start);
+            fw_cfg_slots[3 * slot->idx + 1] = cpu_to_le64(slot->size);
+            fw_cfg_slots[3 * slot->idx + 2] = cpu_to_le64(slot->node);
+        }
+    }
+}
+
+static void dimm_realize(DeviceState *s, Error **errp)
+{
+    DimmBus *bus = DIMM_BUS(qdev_get_parent_bus(s));
+    DimmDevice *slot;
+    DimmConfig *slotcfg;
+
+    slot = DIMM(s);
+    slot->mr = NULL;
+
+    slotcfg = dimmcfg_find_from_name(bus, s->id);
+
+    if (!slotcfg) {
+        error_setg(errp,
+                   "FIXME no config for slot %s found, dimm should be 
deleted\n",
+                   s->id);
+        /* qmp_device_del(s->id, NULL); */
+        return;
+    }
+
+    slot->idx = slotcfg->idx;
+    assert(slotcfg->start);
+    slot->start = slotcfg->start;
+    slot->size = slotcfg->size;
+    slot->node = slotcfg->node;
+
+    QTAILQ_INSERT_TAIL(&bus->dimmlist, slot, nextdimm);
+    dimm_plug_device(slot);
+}
+
+
+static void dimm_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->props = dimm_properties;
+    dc->unplug = dimm_unplug_device;
+    dc->realize = dimm_realize;
+    dc->bus_type = TYPE_DIMM_BUS;
+}
+
+static TypeInfo dimm_info = {
+    .name          = TYPE_DIMM,
+    .parent        = TYPE_DEVICE,
+    .instance_size = sizeof(DimmDevice),
+    .class_init    = dimm_class_init,
+};
+
+static void dimm_register_types(void)
+{
+    type_register_static(&dimm_bus_info);
+    type_register_static(&dimm_info);
+}
+
+type_init(dimm_register_types)
diff --git a/include/hw/mem-hotplug/dimm.h b/include/hw/mem-hotplug/dimm.h
new file mode 100644
index 0000000..8625ae6
--- /dev/null
+++ b/include/hw/mem-hotplug/dimm.h
@@ -0,0 +1,77 @@
+#ifndef QEMU_DIMM_H
+#define QEMU_DIMM_H
+
+#include "qemu-common.h"
+#include "exec/memory.h"
+#include "exec/address-spaces.h"
+#include "hw/sysbus.h"
+#include "qapi-types.h"
+#include "qemu-common.h"
+#define MAX_DIMMS 255
+#define DIMM_BITMAP_BYTES ((MAX_DIMMS + 7) / 8)
+#define DEFAULT_DIMMSIZE (1024*1024*1024)
+
+typedef enum {
+    DIMM_REMOVE_SUCCESS = 0,
+    DIMM_REMOVE_FAIL = 1,
+    DIMM_ADD_SUCCESS = 2,
+    DIMM_ADD_FAIL = 3
+} dimm_hp_result_code;
+
+#define TYPE_DIMM "dimm"
+#define DIMM(obj) \
+    OBJECT_CHECK(DimmDevice, (obj), TYPE_DIMM)
+
+typedef struct DimmDevice DimmDevice;
+typedef QTAILQ_HEAD(DimmConfiglist, DimmConfig) DimmConfiglist;
+
+struct DimmDevice {
+    DeviceState qdev;
+    uint32_t idx; /* index in memory hotplug register/bitmap */
+    ram_addr_t start; /* starting physical address */
+    ram_addr_t size;
+    uint32_t node; /* numa node proximity */
+    MemoryRegion *mr; /* MemoryRegion for this slot. !NULL only if populated */
+    QTAILQ_ENTRY(DimmDevice) nextdimm;
+};
+
+typedef struct DimmConfig {
+    const char *name;
+    uint32_t idx; /* index in linear memory hotplug bitmap */
+    const char *bus_name;
+    ram_addr_t start; /* starting physical address */
+    ram_addr_t size;
+    uint32_t node; /* numa node proximity */
+    QTAILQ_ENTRY(DimmConfig) nextdimmcfg;
+} DimmConfig;
+
+typedef int (*dimm_hotplug_fn)(DeviceState *qdev, DimmDevice *dev, int add);
+typedef hwaddr(*dimm_calcoffset_fn)(DeviceState *dev, uint64_t size);
+
+#define TYPE_DIMM_BUS "dimmbus"
+#define DIMM_BUS(obj) OBJECT_CHECK(DimmBus, (obj), TYPE_DIMM_BUS)
+
+typedef struct DimmBus {
+    BusState qbus;
+    DeviceState *dimm_hotplug_qdev;
+    dimm_hotplug_fn dimm_hotplug;
+    DimmConfiglist dimmconfig_list;
+    QTAILQ_HEAD(Dimmlist, DimmDevice) dimmlist;
+    QLIST_ENTRY(DimmBus) next;
+} DimmBus;
+
+struct dimm_hp_result {
+    const char *dimmname;
+    dimm_hp_result_code ret;
+    QTAILQ_ENTRY(dimm_hp_result) next;
+};
+
+void dimm_bus_hotplug(dimm_hotplug_fn hotplug, DeviceState *qdev);
+void dimm_setup_fwcfg_layout(uint64_t *fw_cfg_slots);
+int dimm_add(char *id);
+DimmBus *dimm_bus_create(Object *parent, const char *name, uint32_t max_dimms,
+    dimm_calcoffset_fn pmc_set_offset);
+void dimm_config_create(char *id, uint64_t size, const char *bus, uint64_t 
node,
+        uint32_t dimm_idx);
+
+#endif
-- 
1.8.3.1




reply via email to

[Prev in Thread] Current Thread [Next in Thread]