On Mon, 16 Mar 2015 16:58:18 +0800 Zhu Guihua <zhugh.f...@cn.fujitsu.com> wrote:
> This patch adds a new bit to memory hotplug IO port indicating that actually bit was added in 2/6 where is_removing had been added. > EJ0 has been evaluated by guest OS. And call pc-dimm unplug cb to do > the real removal. > > Signed-off-by: Zhu Guihua <zhugh.f...@cn.fujitsu.com> > --- > docs/specs/acpi_mem_hotplug.txt | 11 +++++++++-- > hw/acpi/memory_hotplug.c | 21 +++++++++++++++++++-- > hw/core/qdev.c | 2 +- > hw/i386/acpi-build.c | 9 +++++++++ > hw/i386/acpi-dsdt-mem-hotplug.dsl | 10 ++++++++++ > include/hw/acpi/pc-hotplug.h | 2 ++ > include/hw/qdev-core.h | 1 + > trace-events | 1 + > 8 files changed, 52 insertions(+), 5 deletions(-) > > diff --git a/docs/specs/acpi_mem_hotplug.txt b/docs/specs/acpi_mem_hotplug.txt > index 1290994..85cd4b8 100644 > --- a/docs/specs/acpi_mem_hotplug.txt > +++ b/docs/specs/acpi_mem_hotplug.txt > @@ -19,7 +19,9 @@ Memory hot-plug interface (IO port 0xa00-0xa17, 1-4 byte > access): > 1: Device insert event, used to distinguish device for which > no device check event to OSPM was issued. > It's valid only when bit 1 is set. > - 2-7: reserved and should be ignored by OSPM > + 2: Device remove event, used to distinguish device for which > + no device check event to OSPM was issued. > + 3-7: reserved and should be ignored by OSPM > [0x15-0x17] reserved > > write access: > @@ -35,7 +37,12 @@ Memory hot-plug interface (IO port 0xa00-0xa17, 1-4 byte > access): > 1: if set to 1 clears device insert event, set by OSPM > after it has emitted device check event for the > selected memory device > - 2-7: reserved, OSPM must clear them before writing to register > + 2: if set to 1 clears device remove event, set by OSPM > + after it has emitted device check event for the > + selected memory device. if guest fails to eject device, it > + should send OST event about it and forget about device > + removal. > + 3-7: reserved, OSPM must clear them before writing to register > > Selecting memory device slot beyond present range has no effect on platform: > - write accesses to memory hot-plug registers not documented above are > diff --git a/hw/acpi/memory_hotplug.c b/hw/acpi/memory_hotplug.c > index 687b2f1..d6b8c89 100644 > --- a/hw/acpi/memory_hotplug.c > +++ b/hw/acpi/memory_hotplug.c > @@ -2,6 +2,7 @@ > #include "hw/acpi/pc-hotplug.h" > #include "hw/mem/pc-dimm.h" > #include "hw/boards.h" > +#include "hw/qdev-core.h" > #include "trace.h" > #include "qapi-event.h" > > @@ -91,6 +92,8 @@ static void acpi_memory_hotplug_write(void *opaque, hwaddr > addr, uint64_t data, > MemHotplugState *mem_st = opaque; > MemStatus *mdev; > ACPIOSTInfo *info; > + DeviceState *dev = NULL; > + HotplugHandler *hotplug_ctrl = NULL; > > if (!mem_st->dev_count) { > return; > @@ -122,19 +125,33 @@ static void acpi_memory_hotplug_write(void *opaque, > hwaddr addr, uint64_t data, > mdev = &mem_st->devs[mem_st->selector]; > mdev->ost_status = data; > trace_mhp_acpi_write_ost_status(mem_st->selector, mdev->ost_status); > - /* TODO: implement memory removal on guest signal */ > > info = acpi_memory_device_status(mem_st->selector, mdev); > qapi_event_send_acpi_device_ost(info, &error_abort); > qapi_free_ACPIOSTInfo(info); > break; > - case 0x14: > + case 0x14: /* set is_* fields */ > 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) { /* request removal of device */ fix comment to match docs above. > + mdev->is_removing = false; > + trace_mhp_acpi_clear_remove_evt(mem_st->selector); just clear event here and don't do removal part as it doesn't match documentation you've written above regarding this field. It would be better to move is_removing handling from here to 2/6 + related ASL code from DSDT which should clear it after sending device check. > + /* > + * QEMU memory hot unplug is an asynchronous procedure. QEMU > first > + * calls pc-dimm unplug request cb to send a SCI to guest. When > the > + * guest OS finished handling the SCI, it evaluates ACPI EJ0, and > + * QEMU calls pc-dimm unplug cb to remove memory device. > + */ something like this comment, should be in acpi_mem_hotplug.txt not here. There is 'is_enabled' field, which is 1 if device is present, we can use it for triggering actual ejecting in QEMU from EJ0(), something like: } else if (data & 1) { /* eject device */ > + dev = DEVICE(mdev->dimm); potential NULL dereference, dimm could be NULL if guest does eject twice or does eject of empty slot. Perhaps add check before accessing dimm. if(!mdev->is_enabled) { trace_..._ejecting_invalid_slot(...) break; } > + hotplug_ctrl = qdev_get_hotplug_handler(dev); > + /* Call pc-dimm unplug cb. */ > + hotplug_handler_unplug(hotplug_ctrl, dev, NULL); It's not that we can do anything about error at this point but instead of forgetting it silently at least log error in trace, the best would be in addition to that send QMP event to notify mgmt about it. (sending QMP event could be a separate patch) > } > break; > + default: > + break; > } > > } > diff --git a/hw/core/qdev.c b/hw/core/qdev.c > index 6be5866..4676ffb 100644 > --- a/hw/core/qdev.c > +++ b/hw/core/qdev.c > @@ -273,7 +273,7 @@ void qdev_set_legacy_instance_id(DeviceState *dev, int > alias_id, > dev->alias_required_for_version = required_for_version; > } > > -static HotplugHandler *qdev_get_hotplug_handler(DeviceState *dev) > +HotplugHandler *qdev_get_hotplug_handler(DeviceState *dev) > { > HotplugHandler *hotplug_ctrl = NULL; > > diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c > index d0a5c85..1ba6102 100644 > --- a/hw/i386/acpi-build.c > +++ b/hw/i386/acpi-build.c > @@ -929,6 +929,9 @@ build_ssdt(GArray *table_data, GArray *linker, > aml_append(field, > /*(read) 1 if has a insert event. (write) 1 to clear event */ > aml_named_field(stringify(MEMORY_SLOT_INSERT_EVENT), 1)); > + aml_append(field, > + /*(read) 1 if has a remove event. (write) 1 to clear event */ > + aml_named_field(stringify(MEMORY_SLOT_REMOVE_EVENT), 1)); > aml_append(scope, field); > > field = aml_field(stringify(MEMORY_HOTPLUG_IO_REGION), > aml_dword_acc); > @@ -972,6 +975,12 @@ build_ssdt(GArray *table_data, GArray *linker, > ))); > aml_append(dev, method); > > + method = aml_method("_EJ0", 1); > + s = BASEPATH stringify(MEMORY_SLOT_EJECT_METHOD); > + aml_append(method, aml_return(aml_call2(s, aml_name("_UID"), > + aml_arg(0)))); > + aml_append(dev, method); > + > aml_append(sb_scope, dev); > } > > diff --git a/hw/i386/acpi-dsdt-mem-hotplug.dsl > b/hw/i386/acpi-dsdt-mem-hotplug.dsl > index 1e9ec39..ef847e2 100644 > --- a/hw/i386/acpi-dsdt-mem-hotplug.dsl > +++ b/hw/i386/acpi-dsdt-mem-hotplug.dsl > @@ -29,6 +29,7 @@ > External(MEMORY_SLOT_PROXIMITY, FieldUnitObj) // read only > External(MEMORY_SLOT_ENABLED, FieldUnitObj) // 1 if enabled, > read only > External(MEMORY_SLOT_INSERT_EVENT, FieldUnitObj) // (read) 1 if > has a insert event. (write) 1 to clear event > + External(MEMORY_SLOT_REMOVE_EVENT, FieldUnitObj) // (read) 1 if > has a remove event. (write) 1 to clear event > External(MEMORY_SLOT_SLECTOR, FieldUnitObj) // DIMM selector, > write only > External(MEMORY_SLOT_OST_EVENT, FieldUnitObj) // _OST event > code, write only > External(MEMORY_SLOT_OST_STATUS, FieldUnitObj) // _OST status > code, write only > @@ -55,6 +56,8 @@ > If (LEqual(MEMORY_SLOT_INSERT_EVENT, One)) { // Memory > device needs check > MEMORY_SLOT_NOTIFY_METHOD(Local0, 1) > Store(1, MEMORY_SLOT_INSERT_EVENT) > + } Elseif (LEqual(MEMORY_SLOT_REMOVE_EVENT, One)) { // > Ejection request > + MEMORY_SLOT_NOTIFY_METHOD(Local0, 3) clear removing field here. > } > // TODO: handle memory eject request > Add(Local0, One, Local0) // goto next DIMM > @@ -156,5 +159,12 @@ > Store(Arg2, MEMORY_SLOT_OST_STATUS) > Release(MEMORY_SLOT_LOCK) > } > + > + Method(MEMORY_SLOT_EJECT_METHOD, 2) { > + Acquire(MEMORY_SLOT_LOCK, 0xFFFF) > + Store(ToInteger(Arg0), MEMORY_SLOT_SLECTOR) // select DIMM > + Store(One, MEMORY_SLOT_REMOVE_EVENT) redo it using enabled field. Otherwise it could cause guest/QEMU crash: - if 1st memory was asked to be removed - then OSPM processes it but has not called _EJ0 yet leaving is_removed set - then QEMU adds/removes another DIMM triggering slots scan which would issue second Notify(remove) since is_removed is still set - as result it will cause failure in OSPM or in QEMU if OSPM issues double EJ0() > + Release(MEMORY_SLOT_LOCK) > + } > } // Device() > } // Scope() > diff --git a/include/hw/acpi/pc-hotplug.h b/include/hw/acpi/pc-hotplug.h > index efa6ed7..680810b 100644 > --- a/include/hw/acpi/pc-hotplug.h > +++ b/include/hw/acpi/pc-hotplug.h > @@ -43,6 +43,7 @@ > #define MEMORY_SLOT_PROXIMITY MPX > #define MEMORY_SLOT_ENABLED MES > #define MEMORY_SLOT_INSERT_EVENT MINS > +#define MEMORY_SLOT_REMOVE_EVENT MRMV > #define MEMORY_SLOT_SLECTOR MSEL > #define MEMORY_SLOT_OST_EVENT MOEV > #define MEMORY_SLOT_OST_STATUS MOSC > @@ -51,6 +52,7 @@ > #define MEMORY_SLOT_CRS_METHOD MCRS > #define MEMORY_SLOT_OST_METHOD MOST > #define MEMORY_SLOT_PROXIMITY_METHOD MPXM > +#define MEMORY_SLOT_EJECT_METHOD MEJ0 > #define MEMORY_SLOT_NOTIFY_METHOD MTFY > #define MEMORY_SLOT_SCAN_METHOD MSCN > > diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h > index 4e673f9..5b7acf1 100644 > --- a/include/hw/qdev-core.h > +++ b/include/hw/qdev-core.h > @@ -266,6 +266,7 @@ int qdev_init(DeviceState *dev) QEMU_WARN_UNUSED_RESULT; > void qdev_init_nofail(DeviceState *dev); > void qdev_set_legacy_instance_id(DeviceState *dev, int alias_id, > int required_for_version); > +HotplugHandler *qdev_get_hotplug_handler(DeviceState *dev); > void qdev_unplug(DeviceState *dev, Error **errp); > void qdev_simple_device_unplug_cb(HotplugHandler *hotplug_dev, > DeviceState *dev, Error **errp); > diff --git a/trace-events b/trace-events > index 30eba92..e552355 100644 > --- a/trace-events > +++ b/trace-events > @@ -1572,6 +1572,7 @@ mhp_acpi_write_slot(uint32_t slot) "set active slot: > 0x%"PRIx32 > mhp_acpi_write_ost_ev(uint32_t slot, uint32_t ev) "slot[0x%"PRIx32"] OST > EVENT: 0x%"PRIx32 > mhp_acpi_write_ost_status(uint32_t slot, uint32_t st) "slot[0x%"PRIx32"] OST > STATUS: 0x%"PRIx32 > mhp_acpi_clear_insert_evt(uint32_t slot) "slot[0x%"PRIx32"] clear insert > event" > +mhp_acpi_clear_remove_evt(uint32_t slot) "slot[0x%"PRIx32"] clear remove > event" > > # hw/i386/pc.c > mhp_pc_dimm_assigned_slot(int slot) "0x%d"