Each hotplug-able memory slot is a SysBusDevice. All memslots are initially unpopulated. A hot-add operation for a particular memory slot 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. A hot-remove operation detaches and frees the MemoryRegion from system memory.
This is an early prototype and lacks proper qdev integration: a separate hotplug mechanism/side-channel is used and main system bus hotplug capability is ignored.
Signed-off-by: Vasilis Liaskovitis vasilis.liaskovitis@profitbricks.com --- hw/memslot.c | 195 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ hw/memslot.h | 44 +++++++++++++ 2 files changed, 239 insertions(+), 0 deletions(-) create mode 100644 hw/memslot.c create mode 100644 hw/memslot.h
diff --git a/hw/memslot.c b/hw/memslot.c new file mode 100644 index 0000000..b100824 --- /dev/null +++ b/hw/memslot.c @@ -0,0 +1,195 @@ +/* + * MemorySlot device for Memory Hotplug + * + * Copyright ProfitBricks GmbH 2012 + * 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 "qdev.h" +#include "memslot.h" +#include "../exec-memory.h" + +static DeviceState *memslot_hotplug_qdev; +static memslot_hotplug_fn memslot_hotplug; + +static Property memslot_properties[] = { + DEFINE_PROP_END_OF_LIST() +}; + +void memslot_populate(MemSlotState *s) +{ + char buf[32]; + MemoryRegion *new = NULL; + + sprintf(buf, "memslot%u", s->idx); + new = g_malloc(sizeof(MemoryRegion)); + memory_region_init_ram(new, buf, s->size); + vmstate_register_ram_global(new); + memory_region_add_subregion(get_system_memory(), s->start, new); + s->mr = new; + s->populated = 1; +} + +void memslot_depopulate(MemSlotState *s) +{ + assert(s); + if (s->populated) { + vmstate_unregister_ram(s->mr, NULL); + memory_region_del_subregion(get_system_memory(), s->mr); + memory_region_destroy(s->mr); + s->populated = 0; + s->mr = NULL; + } +} + +MemSlotState *memslot_create(char *id, target_phys_addr_t start, uint64_t size, + uint64_t node, uint32_t memslot_idx) +{ + DeviceState *dev; + MemSlotState *mdev; + + dev = sysbus_create_simple("memslot", -1, NULL); + dev->id = id; + + mdev = MEMSLOT(dev); + mdev->idx = memslot_idx; + mdev->start = start; + mdev->size = size; + mdev->node = node; + + return mdev; +} + +void memslot_register_hotplug(memslot_hotplug_fn hotplug, DeviceState *qdev) +{ + memslot_hotplug_qdev = qdev; + memslot_hotplug = hotplug; +} + +static MemSlotState *memslot_find(char *id) +{ + DeviceState *qdev; + qdev = qdev_find_recursive(sysbus_get_default(), id); + if (qdev) + return MEMSLOT(qdev); + return NULL; +} + +int memslot_do(Monitor *mon, const QDict *qdict) +{ + MemSlotState *slot = NULL; + + char *id = (char*) qdict_get_try_str(qdict, "id"); + if (!id) { + fprintf(stderr, "ERROR %s invalid id\n",__FUNCTION__); + return 1; + } + + slot = memslot_find(id); + + if (!slot) { + fprintf(stderr, "%s no slot %s found\n", __FUNCTION__, id); + return 1; + } + + char *action = (char*) qdict_get_try_str(qdict, "action"); + if (!action || (strcmp(action, "add") && strcmp(action, "delete"))) { + fprintf(stderr, "ERROR %s invalid action\n", __FUNCTION__); + return 1; + } + + if (!strcmp(action, "add")) { + if (slot->populated) { + fprintf(stderr, "ERROR %s slot %s already populated\n", + __FUNCTION__, id); + return 1; + } + memslot_populate(slot); + if (memslot_hotplug) + memslot_hotplug(memslot_hotplug_qdev, (SysBusDevice*)slot, 1); + } + else { + if (!slot->populated) { + fprintf(stderr, "ERROR %s slot %s is not populated\n", + __FUNCTION__, id); + return 1; + } + if (memslot_hotplug) + memslot_hotplug(memslot_hotplug_qdev, (SysBusDevice*)slot, 0); + } + return 0; +} + +MemSlotState *memslot_find_from_idx(uint32_t idx) +{ + Error *err = NULL; + DeviceState *dev; + MemSlotState *slot; + char *type; + BusState *bus = sysbus_get_default(); + QTAILQ_FOREACH(dev, &bus->children, sibling) { + type = object_property_get_str(OBJECT(dev), "type", &err); + if (err) { + error_free(err); + fprintf(stderr, "error getting device type\n"); + return NULL; + } + if (!strcmp(type, "memslot")) { + slot = MEMSLOT(dev); + if (slot->idx == idx) { + fprintf(stderr, "%s found slot with idx %u : %p\n", + __FUNCTION__, idx, slot); + return slot; + } + else + fprintf(stderr, "%s slot with idx %u != %u\n", __FUNCTION__, + slot->idx, idx); + } + } + return NULL; +} + +static int memslot_init(SysBusDevice *s) +{ + MemSlotState *slot; + slot = MEMSLOT(s); + slot->mr = NULL; + slot->populated = 0; + return 0; +} + +static void memslot_class_init(ObjectClass *klass, void *data) +{ + SysBusDeviceClass *sc = SYS_BUS_DEVICE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->props = memslot_properties; + sc->init = memslot_init; + memslot_hotplug = NULL; +} + +static TypeInfo memslot_info = { + .name = "memslot", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(MemSlotState), + .class_init = memslot_class_init, +}; + +static void memslot_register_types(void) +{ + type_register_static(&memslot_info); +} + +type_init(memslot_register_types) diff --git a/hw/memslot.h b/hw/memslot.h new file mode 100644 index 0000000..9412667 --- /dev/null +++ b/hw/memslot.h @@ -0,0 +1,44 @@ +#ifndef QEMU_MEM_H +#define QEMU_MEM_H + +#include "qemu-common.h" +#include "memory.h" +#include "sysbus.h" + +#define TYPE_MEMSLOT "memslot" +#define MEMSLOT(obj) \ + OBJECT_CHECK(MemSlotState, (obj), TYPE_MEMSLOT) +#define MEMSLOT_CLASS(klass) \ + OBJECT_CLASS_CHECK(MemSlotClass, (obj), TYPE_MEMSLOT) +#define MEMSLOT_GET_CLASS(obj) \ + OBJECT_GET_CLASS(MemSlotClass, (obj), TYPE_MEMSLOT) + +typedef struct MemSlotState { + SysBusDevice busdev; + uint32_t populated; /* 1 means device has been hotplugged. Default is 0. */ + uint32_t idx; /* index in memory hotplug register/bitmap */ + uint64_t start; /* starting physical address */ + uint64_t size; + uint32_t node; /* numa node proximity */ + MemoryRegion *mr; /* MemoryRegion for this slot. !NULL only if populated */ +} MemSlotState; + +typedef struct MemSlotClass +{ + SysBusDeviceClass parent_class; + void (*set)(MemSlotState *s, MemoryRegion *mem); +} MemSlotClass; + +/* mem.c */ + +typedef int (*memslot_hotplug_fn)(DeviceState *qdev, SysBusDevice *dev, int add); + +MemSlotState *memslot_create(char *id, target_phys_addr_t start, uint64_t size, + uint64_t node, uint32_t memslot_idx); +void memslot_populate(MemSlotState *s); +void memslot_depopulate(MemSlotState *s); +int memslot_do(Monitor *mon, const QDict *qdict); +MemSlotState *memslot_find_from_idx(uint32_t idx); +void memslot_register_hotplug(memslot_hotplug_fn hotplug, DeviceState *qdev); + +#endif