qemu-devel
[Top][All Lists]
Advanced

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

[Qemu-devel] [RFC PATCH] add memory hotunplug support


From: Zhu Guihua
Subject: [Qemu-devel] [RFC PATCH] add memory hotunplug support
Date: Thu, 17 Jul 2014 13:32:16 +0800

From: Hu Tao <address@hidden>

This patch is to solve a problem that when you add a hot-pluggable memory,
you can't remove the memory.

Its approach is to set GPE status bit by qemu, then generate SCI to notify
guest os. Guest os checks device status, and free memory resource if possible, 
then generate OST. Finally, qemu handles OST events to free dimm device. 

1. based on branch mst/pci 
   url = http://git.kernel.org/pub/scm/virt/kvm/mst/qemu.git

2. known problems
   1) When you try to delete a pc-dimm twice, guest will generate some errors.
   2) If you create a pc-dimm whose id is d9, then you excute 'device_del d10',
      the pc-dimm will be deleted successfully. If you change the id property 
      of device_del to others, the device_del operation will also be excuted 
      correctly, and the pc-dimm d9 will be deleted.

Signed-off-by: Hu Tao <address@hidden>
Signed-off-by: Zhu Guihua <address@hidden>
---
 hw/acpi/memory_hotplug.c         | 74 +++++++++++++++++++++++++++++++++++++++-
 hw/acpi/piix4.c                  |  2 ++
 hw/core/qdev.c                   |  9 +++++
 hw/i386/pc.c                     | 31 +++++++++++++++++
 hw/i386/ssdt-mem.dsl             |  8 +++++
 hw/i386/ssdt-misc.dsl            | 13 ++++++-
 include/hw/acpi/memory_hotplug.h |  3 ++
 include/qom/object.h             |  1 +
 qdev-monitor.c                   | 25 +++++++++++++-
 qom/object.c                     |  2 +-
 10 files changed, 164 insertions(+), 4 deletions(-)

diff --git a/hw/acpi/memory_hotplug.c b/hw/acpi/memory_hotplug.c
index ed39241..b43b2b4 100644
--- a/hw/acpi/memory_hotplug.c
+++ b/hw/acpi/memory_hotplug.c
@@ -75,12 +75,14 @@ static uint64_t acpi_memory_hotplug_read(void *opaque, 
hwaddr addr,
     case 0x14: /* pack and return is_* fields */
         val |= mdev->is_enabled   ? 1 : 0;
         val |= mdev->is_inserting ? 2 : 0;
+        val |= mdev->is_removing ? 4 : 0;
         trace_mhp_acpi_read_flags(mem_st->selector, val);
         break;
     default:
         val = ~0;
         break;
     }
+
     return val;
 }
 
@@ -126,17 +128,57 @@ static void acpi_memory_hotplug_write(void *opaque, 
hwaddr addr, uint64_t data,
         info = acpi_memory_device_status(mem_st->selector, mdev);
         qapi_event_send_acpi_device_ost(info, &error_abort);
         qapi_free_ACPIOSTInfo(info);
+        switch (mdev->ost_event) {
+        case 0x03: /* EJECT */
+            switch (mdev->ost_status) {
+            case 0x0: /* SUCCESS */
+                object_unparent(OBJECT(mdev->dimm));
+                mdev->is_removing = false;
+                mdev->dimm = NULL;
+                break;
+            case 0x1: /* FAILURE */
+            case 0x2: /* UNRECOGNIZED NOTIFY */
+            case 0x80: /* EJECT NOT SUPPORTED */
+            case 0x81: /* DEVICE IN USE */
+            case 0x82: /* DEVICE BUSY */
+            case 0x83: /* EJECT_DEPENDENCY_BUSY */
+                mdev->is_removing = false;
+                mdev->is_enabled = true;
+                break;
+            case 0x84: /* EJECTION IN PROGRESS */
+                break;
+            default:
+                break;
+            }
+            break;
+        case 0x103: /* OSPM EJECT */
+            switch (mdev->ost_status) {
+            case 0x0: /* SUCCESS */
+                object_unparent(OBJECT(mdev->dimm));
+                mdev->is_removing = false;
+                mdev->dimm = NULL;
+                break;
+            case 0x84: /* EJECTION IN PROGRESS */
+                mdev->is_enabled = false;
+                mdev->is_removing = true;
+                break;
+            default:
+                break;
+            }
+        }
         break;
     case 0x14:
         mdev = &mem_st->devs[mem_st->selector];
         if (data & 2) { /* clear insert event */
             mdev->is_inserting  = false;
             trace_mhp_acpi_clear_insert_evt(mem_st->selector);
+        } else if (data & 4) { /* MRMV */
+            mdev->is_enabled = false;
         }
         break;
     }
-
 }
+
 static const MemoryRegionOps acpi_memory_hotplug_ops = {
     .read = acpi_memory_hotplug_read,
     .write = acpi_memory_hotplug_write,
@@ -195,6 +237,36 @@ void acpi_memory_plug_cb(ACPIREGS *ar, qemu_irq irq, 
MemHotplugState *mem_st,
     return;
 }
 
+void acpi_memory_unplug_cb(ACPIREGS *ar, qemu_irq irq, MemHotplugState *mem_st,
+                           DeviceState *dev, Error **errp)
+{
+    MemStatus *mdev;
+    Error *local_err = NULL;
+    int slot = object_property_get_int(OBJECT(dev), "slot", &local_err);
+
+    if (local_err) {
+        error_propagate(errp, local_err);
+        return;
+    }
+
+    if (slot >= mem_st->dev_count) {
+        char *dev_path = object_get_canonical_path(OBJECT(dev));
+        error_setg(errp, "acpi_memory_plug_cb: "
+                   "device [%s] returned invalid memory slot[%d]",
+                    dev_path, slot);
+        g_free(dev_path);
+        return;
+    }
+
+    mdev = &mem_st->devs[slot];
+    mdev->is_removing = true;
+
+    /* do ACPI magic */
+    ar->gpe.sts[0] |= ACPI_MEMORY_HOTPLUG_STATUS;
+    acpi_update_sci(ar, irq);
+    return;
+}
+
 static const VMStateDescription vmstate_memhp_sts = {
     .name = "memory hotplug device state",
     .version_id = 1,
diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c
index b72b34e..37d593a 100644
--- a/hw/acpi/piix4.c
+++ b/hw/acpi/piix4.c
@@ -362,6 +362,8 @@ static void piix4_device_unplug_cb(HotplugHandler 
*hotplug_dev,
     if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) {
         acpi_pcihp_device_unplug_cb(&s->ar, s->irq, &s->acpi_pci_hotplug, dev,
                                     errp);
+    } else if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
+        acpi_memory_unplug_cb(&s->ar, s->irq, &s->acpi_memory_hotplug, dev, 
errp);
     } else {
         error_setg(errp, "acpi: device unplug request for not supported device"
                    " type: %s", object_get_typename(OBJECT(dev)));
diff --git a/hw/core/qdev.c b/hw/core/qdev.c
index da1ba48..7990edf 100644
--- a/hw/core/qdev.c
+++ b/hw/core/qdev.c
@@ -228,6 +228,15 @@ void qdev_unplug(DeviceState *dev, Error **errp)
 
     if (dev->parent_bus && dev->parent_bus->hotplug_handler) {
         hotplug_handler_unplug(dev->parent_bus->hotplug_handler, dev, errp);
+    } else if (*errp == NULL) {
+            HotplugHandler *hotplug_ctrl;
+            MachineState *machine = MACHINE(qdev_get_machine());
+            MachineClass *mc = MACHINE_GET_CLASS(machine);
+
+            hotplug_ctrl = mc->get_hotplug_handler(machine ,dev);
+            if (hotplug_ctrl) {
+                hotplug_handler_unplug(hotplug_ctrl, dev, errp);
+            }
     } else {
         assert(dc->unplug != NULL);
         if (dc->unplug(dev) < 0) { /* legacy handler */
diff --git a/hw/i386/pc.c b/hw/i386/pc.c
index 2cf22b1..3537601 100644
--- a/hw/i386/pc.c
+++ b/hw/i386/pc.c
@@ -61,6 +61,8 @@
 #include "hw/mem/pc-dimm.h"
 #include "trace.h"
 #include "qapi/visitor.h"
+#include "hw/acpi/piix4.h"
+#include "hw/i386/ich9.h"
 
 /* debug PC/ISA interrupts */
 //#define DEBUG_IRQ
@@ -1612,6 +1614,26 @@ out:
     error_propagate(errp, local_err);
 }
 
+static void pc_dimm_unplug(HotplugHandler *hotplug_dev,
+                           DeviceState *dev, Error **errp)
+{
+    Object *acpi_dev;
+    HotplugHandlerClass *hhc;
+    Error *local_err = NULL;
+
+    acpi_dev = (acpi_dev = piix4_pm_find()) ? acpi_dev : ich9_lpc_find();
+    if (!acpi_dev) {
+        error_setg(&local_err,
+                   "memory hotplug is not enabled: missing acpi device");
+        return;
+    }
+
+    hhc = HOTPLUG_HANDLER_GET_CLASS(acpi_dev);
+    hhc->unplug(HOTPLUG_HANDLER(acpi_dev), dev, &local_err);
+
+    error_propagate(errp, local_err);
+}
+
 static void pc_machine_device_plug_cb(HotplugHandler *hotplug_dev,
                                       DeviceState *dev, Error **errp)
 {
@@ -1620,6 +1642,14 @@ static void pc_machine_device_plug_cb(HotplugHandler 
*hotplug_dev,
     }
 }
 
+static void pc_machine_device_unplug_cb(HotplugHandler *hotplug_dev,
+                                        DeviceState *dev, Error **errp)
+{
+    if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
+        pc_dimm_unplug(hotplug_dev, dev, errp);
+    }
+}
+
 static HotplugHandler *pc_get_hotpug_handler(MachineState *machine,
                                              DeviceState *dev)
 {
@@ -1706,6 +1736,7 @@ static void pc_machine_class_init(ObjectClass *oc, void 
*data)
     pcmc->get_hotplug_handler = mc->get_hotplug_handler;
     mc->get_hotplug_handler = pc_get_hotpug_handler;
     hc->plug = pc_machine_device_plug_cb;
+    hc->unplug = pc_machine_device_unplug_cb;
 }
 
 static const TypeInfo pc_machine_info = {
diff --git a/hw/i386/ssdt-mem.dsl b/hw/i386/ssdt-mem.dsl
index 8e17bd1..bf20ee4 100644
--- a/hw/i386/ssdt-mem.dsl
+++ b/hw/i386/ssdt-mem.dsl
@@ -43,6 +43,11 @@ DefinitionBlock ("ssdt-mem.aml", "SSDT", 0x02, "BXPC", 
"CSSDT", 0x1)
     External(\_SB.PCI0.MEMORY_HOPTLUG_DEVICE.MEMORY_SLOT_STATUS_METHOD, 
MethodObj)
     External(\_SB.PCI0.MEMORY_HOPTLUG_DEVICE.MEMORY_SLOT_OST_METHOD, MethodObj)
     External(\_SB.PCI0.MEMORY_HOPTLUG_DEVICE.MEMORY_SLOT_PROXIMITY_METHOD, 
MethodObj)
+    External(\_SB.MHPD.MCRS, MethodObj)
+    External(\_SB.MHPD.MRST, MethodObj)
+    External(\_SB.MHPD.MOST, MethodObj)
+    External(\_SB.MHPD.MPXM, MethodObj)
+    External(\_SB.MHPD.MDEJ, MethodObj)
 
     Scope(\_SB) {
 /*  v------------------ DO NOT EDIT ------------------v */
@@ -72,6 +77,9 @@ DefinitionBlock ("ssdt-mem.aml", "SSDT", 0x02, "BXPC", 
"CSSDT", 0x1)
             Method(_OST, 3) {
                 \_SB.PCI0.MEMORY_HOPTLUG_DEVICE.MEMORY_SLOT_OST_METHOD(_UID, 
Arg0, Arg1, Arg2)
             }
+            Method(_EJ0, 1) {
+                \_SB.MHPD.MDEJ(_UID, Arg0)
+            }
         }
     }
 }
diff --git a/hw/i386/ssdt-misc.dsl b/hw/i386/ssdt-misc.dsl
index d329b8b..38e9bed 100644
--- a/hw/i386/ssdt-misc.dsl
+++ b/hw/i386/ssdt-misc.dsl
@@ -156,6 +156,9 @@ DefinitionBlock ("ssdt-misc.aml", "SSDT", 0x01, "BXPC", 
"BXSSDTSUSP", 0x1)
                 Offset(20),
                 MEMORY_SLOT_ENABLED,  1, // 1 if enabled, read only
                 MEMORY_SLOT_INSERT_EVENT, 1, // (read) 1 if has a insert 
event. (write) 1 to clear event
+                //MES,  1, // 1 if DIMM enabled used by _STA, read only
+                //MINS, 1, // (read) 1 if DIMM has a insert event. (write) 1 
after MTFY() to clear event
+                MRMV, 1, // 1 if DIMM has a remove request, read only
             }
 
             Mutex (MEMORY_SLOT_LOCK, 0)
@@ -178,7 +181,9 @@ DefinitionBlock ("ssdt-misc.aml", "SSDT", 0x01, "BXPC", 
"BXSSDTSUSP", 0x1)
                         MEMORY_SLOT_NOTIFY_METHOD(Local0, 1)
                         Store(1, MEMORY_SLOT_INSERT_EVENT)
                     }
-                    // TODO: handle memory eject request
+                    If (LEqual(MRMV, One)) { // Ejection request
+                        MTFY(Local0, 3)
+                    }
                     Add(Local0, One, Local0) // goto next DIMM
                 }
                 Release(MEMORY_SLOT_LOCK)
@@ -278,6 +283,12 @@ DefinitionBlock ("ssdt-misc.aml", "SSDT", 0x01, "BXPC", 
"BXSSDTSUSP", 0x1)
                 Store(Arg2, MEMORY_SLOT_OST_STATUS)
                 Release(MEMORY_SLOT_LOCK)
             }
+            Method(MDEJ, 2) {
+                Acquire(MLCK, 0xFFFF)
+                Store(ToInteger(Arg0), MSEL) // select DIMM
+                Store(One, MRMV)
+                Release(MLCK)
+            }
         } // Device()
     } // Scope()
 }
diff --git a/include/hw/acpi/memory_hotplug.h b/include/hw/acpi/memory_hotplug.h
index 7bbf8a0..fe41268 100644
--- a/include/hw/acpi/memory_hotplug.h
+++ b/include/hw/acpi/memory_hotplug.h
@@ -11,6 +11,7 @@ typedef struct MemStatus {
     DeviceState *dimm;
     bool is_enabled;
     bool is_inserting;
+    bool is_removing;
     uint32_t ost_event;
     uint32_t ost_status;
 } MemStatus;
@@ -28,6 +29,8 @@ void acpi_memory_hotplug_init(MemoryRegion *as, Object *owner,
 
 void acpi_memory_plug_cb(ACPIREGS *ar, qemu_irq irq, MemHotplugState *mem_st,
                          DeviceState *dev, Error **errp);
+void acpi_memory_unplug_cb(ACPIREGS *ar, qemu_irq irq, MemHotplugState *mem_st,
+                           DeviceState *dev, Error **errp);
 
 extern const VMStateDescription vmstate_memory_hotplug;
 #define VMSTATE_MEMORY_HOTPLUG(memhp, state) \
diff --git a/include/qom/object.h b/include/qom/object.h
index 8a05a81..a352beb 100644
--- a/include/qom/object.h
+++ b/include/qom/object.h
@@ -1300,5 +1300,6 @@ int object_child_foreach(Object *obj, int (*fn)(Object 
*child, void *opaque),
  */
 Object *container_get(Object *root, const char *path);
 
+bool object_property_is_child(ObjectProperty *prop);
 
 #endif
diff --git a/qdev-monitor.c b/qdev-monitor.c
index f87f3d8..168806f 100644
--- a/qdev-monitor.c
+++ b/qdev-monitor.c
@@ -24,6 +24,7 @@
 #include "qmp-commands.h"
 #include "sysemu/arch_init.h"
 #include "qemu/config-file.h"
+#include "qom/object.h"
 
 /*
  * Aliases were a bad idea from the start.  Let's keep them
@@ -689,12 +690,34 @@ int do_device_add(Monitor *mon, const QDict *qdict, 
QObject **ret_data)
     return 0;
 }
 
+static DeviceState *find_peripheral_device(const char *id)
+{
+    Object *peripheral = qdev_get_peripheral();
+    DeviceState *dev = NULL;
+    ObjectProperty *prop;
+
+
+    QTAILQ_FOREACH(prop, &peripheral->properties, node) {
+        if (object_property_is_child(prop)) {
+            dev = DEVICE(prop->opaque);
+            if (!strcmp(dev->id, id)) {
+                return dev;
+            }
+        }
+    }
+
+    return dev;
+}
+
 void qmp_device_del(const char *id, Error **errp)
 {
     DeviceState *dev;
 
     dev = qdev_find_recursive(sysbus_get_default(), id);
-    if (NULL == dev) {
+    if (dev == NULL) {
+        dev = find_peripheral_device(id);
+    }
+    if (dev == NULL) {
         error_set(errp, QERR_DEVICE_NOT_FOUND, id);
         return;
     }
diff --git a/qom/object.c b/qom/object.c
index 0e8267b..1183e5d 100644
--- a/qom/object.c
+++ b/qom/object.c
@@ -351,7 +351,7 @@ void object_initialize(void *data, size_t size, const char 
*typename)
     object_initialize_with_type(data, size, type);
 }
 
-static inline bool object_property_is_child(ObjectProperty *prop)
+bool object_property_is_child(ObjectProperty *prop)
 {
     return strstart(prop->type, "child<", NULL);
 }
-- 
1.9.3




reply via email to

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