Patchwork [RFC,v4,23/30] dimm: add hot-remove capability

login
register
mail settings
Submitter Vasilis Liaskovitis
Date Dec. 18, 2012, 12:41 p.m.
Message ID <1355834518-17989-24-git-send-email-vasilis.liaskovitis@profitbricks.com>
Download mbox | patch
Permalink /patch/207101/
State New
Headers show

Comments

Vasilis Liaskovitis - Dec. 18, 2012, 12:41 p.m.
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(-)

Patch

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