On a succesfull _EJ0 operation unmap the device from the guest by using the new qdev function qdev_unplug_complete, see: https://lists.gnu.org/archive/html/qemu-devel/2012-11/msg02699.html
The memory of the device should be freed when the last subsystem using it unmaps it, see the following two series: https://lists.gnu.org/archive/html/qemu-devel/2012-11/msg00728.html https://lists.gnu.org/archive/html/qemu-devel/2012-11/msg02697.html
Needs testing. Other subsystems (e.g. virtio-blk) may have to install new memorylisteners to complete pending I/O before device memory can be freed.
Signed-off-by: Vasilis Liaskovitis vasilis.liaskovitis@profitbricks.com --- hw/dimm.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ hw/dimm.h | 1 + 2 files changed, 52 insertions(+), 0 deletions(-)
diff --git a/hw/dimm.c b/hw/dimm.c index e79f23d..0b4e22d 100644 --- a/hw/dimm.c +++ b/hw/dimm.c @@ -120,6 +120,18 @@ static void dimm_populate(DimmDevice *s) s->mr = new; }
+static int dimm_depopulate(DeviceState *dev) +{ + DimmDevice *s = DIMM(dev); + assert(s); + vmstate_unregister_ram(s->mr, NULL); + memory_region_del_subregion(get_system_memory(), s->mr); + memory_region_destroy(s->mr); + s->populated = false; + s->mr = NULL; + return 0; +} + void dimm_config_create(char *id, uint64_t size, const char *bus, uint64_t node, uint32_t dimm_idx, uint32_t populated) { @@ -159,6 +171,11 @@ static void dimm_plug_device(DimmDevice *slot)
static int dimm_unplug_device(DeviceState *qdev) { + DimmBus *bus = DIMM_BUS(qdev_get_parent_bus(qdev)); + + if (bus->dimm_hotplug) { + bus->dimm_hotplug(bus->dimm_hotplug_qdev, DIMM(qdev), 0); + } return 1; }
@@ -186,6 +203,21 @@ static DimmDevice *dimm_find_from_name(DimmBus *bus, const char *name) return NULL; }
+static DimmDevice *dimm_find_from_idx(uint32_t idx) +{ + DimmDevice *slot; + DimmBus *bus; + + QLIST_FOREACH(bus, &memory_buses, next) { + QTAILQ_FOREACH(slot, &bus->dimmlist, nextdimm) { + if (slot->idx == idx) { + return slot; + } + } + } + return NULL; +} + void dimm_setup_fwcfg_layout(uint64_t *fw_cfg_slots) { DimmConfig *slot; @@ -275,6 +307,24 @@ static int dimm_init(DeviceState *s) return 0; }
+void dimm_notify(uint32_t idx, uint32_t event) +{ + DimmBus *bus; + DimmDevice *slot; + + slot = dimm_find_from_idx(idx); + assert(slot != NULL); + bus = DIMM_BUS(qdev_get_parent_bus(&slot->qdev)); + + switch (event) { + case DIMM_REMOVE_SUCCESS: + qdev_unplug_complete((DeviceState *)slot, NULL); + QTAILQ_REMOVE(&bus->dimmlist, slot, nextdimm); + break; + default: + break; + } +}
static void dimm_class_init(ObjectClass *klass, void *data) { @@ -283,6 +333,7 @@ static void dimm_class_init(ObjectClass *klass, void *data) dc->props = dimm_properties; dc->unplug = dimm_unplug_device; dc->init = dimm_init; + dc->exit = dimm_depopulate; dc->bus_type = TYPE_DIMM_BUS; }
diff --git a/hw/dimm.h b/hw/dimm.h index 5130b2c..86c7cd5 100644 --- a/hw/dimm.h +++ b/hw/dimm.h @@ -86,5 +86,6 @@ DimmBus *dimm_bus_create(Object *parent, const char *name, uint32_t max_dimms, void dimm_config_create(char *id, uint64_t size, const char *bus, uint64_t node, uint32_t dimm_idx, uint32_t populated); uint64_t get_hp_memory_total(void); +void dimm_notify(uint32_t idx, uint32_t event);
#endif