summaryrefslogtreecommitdiffstats
path: root/hw/ppc
diff options
context:
space:
mode:
authorPeter Maydell2021-03-12 12:30:55 +0100
committerPeter Maydell2021-03-12 12:30:55 +0100
commit1941858448e76f83eb00614c4f34ac29e9a8e792 (patch)
tree4d3bafbd64a5b337d56beb28a1fcd28bd9cc3001 /hw/ppc
parentMerge remote-tracking branch 'remotes/vivier2/tags/trivial-branch-for-6.0-pul... (diff)
parentspapr.c: send QAPI event when memory hotunplug fails (diff)
downloadqemu-1941858448e76f83eb00614c4f34ac29e9a8e792.tar.gz
qemu-1941858448e76f83eb00614c4f34ac29e9a8e792.tar.xz
qemu-1941858448e76f83eb00614c4f34ac29e9a8e792.zip
Merge remote-tracking branch 'remotes/dg-gitlab/tags/ppc-for-6.0-20210310' into staging
ppc patch queue for 2021-03-10 Next batch of patches for the ppc target and machine types. Includes: * Several cleanups for sm501 from Peter Maydell * An update to the SLOF guest firmware * Improved handling of hotplug failures in spapr, associated cleanups to the hotplug handling code * Several etsec fixes and cleanups from Bin Meng * Assorted other fixes and cleanups # gpg: Signature made Wed 10 Mar 2021 04:08:53 GMT # gpg: using RSA key 75F46586AE61A66CC44E87DC6C38CACA20D9B392 # gpg: Good signature from "David Gibson <david@gibson.dropbear.id.au>" [full] # gpg: aka "David Gibson (Red Hat) <dgibson@redhat.com>" [full] # gpg: aka "David Gibson (ozlabs.org) <dgibson@ozlabs.org>" [full] # gpg: aka "David Gibson (kernel.org) <dwg@kernel.org>" [unknown] # Primary key fingerprint: 75F4 6586 AE61 A66C C44E 87DC 6C38 CACA 20D9 B392 * remotes/dg-gitlab/tags/ppc-for-6.0-20210310: spapr.c: send QAPI event when memory hotunplug fails spapr.c: remove duplicated assert in spapr_memory_unplug_request() target/ppc: fix icount support on Book-e vms accessing SPRs qemu_timer.c: add timer_deadline_ms() helper spapr_pci.c: add 'unplug already in progress' message for PCI unplug spapr.c: add 'unplug already in progress' message for PHB unplug hw/ppc: e500: Add missing <ranges> in the eTSEC node hw/net: fsl_etsec: Fix build error when HEX_DUMP is on spapr_drc.c: use DRC reconfiguration to cleanup DIMM unplug state spapr_drc.c: add hotunplug timeout for CPUs spapr_drc.c: introduce unplug_timeout_timer target/ppc: Fix bcdsub. emulation when result overflows docs/system: Extend PPC section spapr: rename spapr_drc_detach() to spapr_drc_unplug_request() spapr_drc.c: use spapr_drc_release() in isolate_physical/set_unusable pseries: Update SLOF firmware image spapr_drc.c: do not call spapr_drc_detach() in drc_isolate_logical() hw/display/sm501: Inline template header into C file hw/display/sm501: Expand out macros in template header hw/display/sm501: Remove dead code for non-32-bit RGB surfaces Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'hw/ppc')
-rw-r--r--hw/ppc/e500.c1
-rw-r--r--hw/ppc/spapr.c67
-rw-r--r--hw/ppc/spapr_drc.c110
-rw-r--r--hw/ppc/spapr_pci.c8
-rw-r--r--hw/ppc/trace-events2
5 files changed, 150 insertions, 38 deletions
diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c
index 01517a6c6c..1d94485ac8 100644
--- a/hw/ppc/e500.c
+++ b/hw/ppc/e500.c
@@ -231,6 +231,7 @@ static int create_devtree_etsec(SysBusDevice *sbdev, PlatformDevtreeData *data)
assert(irq2 >= 0);
qemu_fdt_add_subnode(fdt, node);
+ qemu_fdt_setprop(fdt, node, "ranges", NULL, 0);
qemu_fdt_setprop_string(fdt, node, "device_type", "network");
qemu_fdt_setprop_string(fdt, node, "compatible", "fsl,etsec2");
qemu_fdt_setprop_string(fdt, node, "model", "eTSEC");
diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c
index 85fe65f894..d56418ca29 100644
--- a/hw/ppc/spapr.c
+++ b/hw/ppc/spapr.c
@@ -28,6 +28,7 @@
#include "qemu-common.h"
#include "qemu/datadir.h"
#include "qapi/error.h"
+#include "qapi/qapi-events-machine.h"
#include "qapi/visitor.h"
#include "sysemu/sysemu.h"
#include "sysemu/hostmem.h"
@@ -3575,6 +3576,57 @@ static SpaprDimmState *spapr_recover_pending_dimm_state(SpaprMachineState *ms,
return spapr_pending_dimm_unplugs_add(ms, avail_lmbs, dimm);
}
+void spapr_memory_unplug_rollback(SpaprMachineState *spapr, DeviceState *dev)
+{
+ SpaprDimmState *ds;
+ PCDIMMDevice *dimm;
+ SpaprDrc *drc;
+ uint32_t nr_lmbs;
+ uint64_t size, addr_start, addr;
+ g_autofree char *qapi_error = NULL;
+ int i;
+
+ if (!dev) {
+ return;
+ }
+
+ dimm = PC_DIMM(dev);
+ ds = spapr_pending_dimm_unplugs_find(spapr, dimm);
+
+ /*
+ * 'ds == NULL' would mean that the DIMM doesn't have a pending
+ * unplug state, but one of its DRC is marked as unplug_requested.
+ * This is bad and weird enough to g_assert() out.
+ */
+ g_assert(ds);
+
+ spapr_pending_dimm_unplugs_remove(spapr, ds);
+
+ size = memory_device_get_region_size(MEMORY_DEVICE(dimm), &error_abort);
+ nr_lmbs = size / SPAPR_MEMORY_BLOCK_SIZE;
+
+ addr_start = object_property_get_uint(OBJECT(dimm), PC_DIMM_ADDR_PROP,
+ &error_abort);
+
+ addr = addr_start;
+ for (i = 0; i < nr_lmbs; i++) {
+ drc = spapr_drc_by_id(TYPE_SPAPR_DRC_LMB,
+ addr / SPAPR_MEMORY_BLOCK_SIZE);
+ g_assert(drc);
+
+ drc->unplug_requested = false;
+ addr += SPAPR_MEMORY_BLOCK_SIZE;
+ }
+
+ /*
+ * Tell QAPI that something happened and the memory
+ * hotunplug wasn't successful.
+ */
+ qapi_error = g_strdup_printf("Memory hotunplug rejected by the guest "
+ "for device %s", dev->id);
+ qapi_event_send_mem_unplug_error(dev->id, qapi_error);
+}
+
/* Callback to be called during DRC release. */
void spapr_lmb_release(DeviceState *dev)
{
@@ -3654,13 +3706,12 @@ static void spapr_memory_unplug_request(HotplugHandler *hotplug_dev,
addr / SPAPR_MEMORY_BLOCK_SIZE);
g_assert(drc);
- spapr_drc_detach(drc);
+ spapr_drc_unplug_request(drc);
addr += SPAPR_MEMORY_BLOCK_SIZE;
}
drc = spapr_drc_by_id(TYPE_SPAPR_DRC_LMB,
addr_start / SPAPR_MEMORY_BLOCK_SIZE);
- g_assert(drc);
spapr_hotplug_req_remove_by_count_indexed(SPAPR_DR_CONNECTOR_TYPE_LMB,
nr_lmbs, spapr_drc_index(drc));
}
@@ -3722,8 +3773,12 @@ void spapr_core_unplug_request(HotplugHandler *hotplug_dev, DeviceState *dev,
g_assert(drc);
if (!spapr_drc_unplug_requested(drc)) {
- spapr_drc_detach(drc);
+ spapr_drc_unplug_request(drc);
spapr_hotplug_req_remove_by_index(drc);
+ } else {
+ error_setg(errp, "core-id %d unplug is still pending, %d seconds "
+ "timeout remaining",
+ cc->core_id, spapr_drc_unplug_timeout_remaining_sec(drc));
}
}
@@ -3985,8 +4040,12 @@ static void spapr_phb_unplug_request(HotplugHandler *hotplug_dev,
assert(drc);
if (!spapr_drc_unplug_requested(drc)) {
- spapr_drc_detach(drc);
+ spapr_drc_unplug_request(drc);
spapr_hotplug_req_remove_by_index(drc);
+ } else {
+ error_setg(errp,
+ "PCI Host Bridge unplug already in progress for device %s",
+ dev->id);
}
}
diff --git a/hw/ppc/spapr_drc.c b/hw/ppc/spapr_drc.c
index 8571d5bafe..8a71b03800 100644
--- a/hw/ppc/spapr_drc.c
+++ b/hw/ppc/spapr_drc.c
@@ -50,6 +50,22 @@ uint32_t spapr_drc_index(SpaprDrc *drc)
| (drc->id & DRC_INDEX_ID_MASK);
}
+static void spapr_drc_release(SpaprDrc *drc)
+{
+ SpaprDrcClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
+
+ drck->release(drc->dev);
+
+ drc->unplug_requested = false;
+ timer_del(drc->unplug_timeout_timer);
+
+ g_free(drc->fdt);
+ drc->fdt = NULL;
+ drc->fdt_start_offset = 0;
+ object_property_del(OBJECT(drc), "device");
+ drc->dev = NULL;
+}
+
static uint32_t drc_isolate_physical(SpaprDrc *drc)
{
switch (drc->state) {
@@ -68,7 +84,7 @@ static uint32_t drc_isolate_physical(SpaprDrc *drc)
if (drc->unplug_requested) {
uint32_t drc_index = spapr_drc_index(drc);
trace_spapr_drc_set_isolation_state_finalizing(drc_index);
- spapr_drc_detach(drc);
+ spapr_drc_release(drc);
}
return RTAS_OUT_SUCCESS;
@@ -132,19 +148,6 @@ static uint32_t drc_isolate_logical(SpaprDrc *drc)
drc->state = SPAPR_DRC_STATE_LOGICAL_AVAILABLE;
- /* if we're awaiting release, but still in an unconfigured state,
- * it's likely the guest is still in the process of configuring
- * the device and is transitioning the devices to an ISOLATED
- * state as a part of that process. so we only complete the
- * removal when this transition happens for a device in a
- * configured state, as suggested by the state diagram from PAPR+
- * 2.7, 13.4
- */
- if (drc->unplug_requested) {
- uint32_t drc_index = spapr_drc_index(drc);
- trace_spapr_drc_set_isolation_state_finalizing(drc_index);
- spapr_drc_detach(drc);
- }
return RTAS_OUT_SUCCESS;
}
@@ -222,7 +225,7 @@ static uint32_t drc_set_unusable(SpaprDrc *drc)
if (drc->unplug_requested) {
uint32_t drc_index = spapr_drc_index(drc);
trace_spapr_drc_set_allocation_state_finalizing(drc_index);
- spapr_drc_detach(drc);
+ spapr_drc_release(drc);
}
return RTAS_OUT_SUCCESS;
@@ -369,6 +372,17 @@ static void prop_get_fdt(Object *obj, Visitor *v, const char *name,
} while (fdt_depth != 0);
}
+static void spapr_drc_start_unplug_timeout_timer(SpaprDrc *drc)
+{
+ SpaprDrcClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
+
+ if (drck->unplug_timeout_seconds != 0) {
+ timer_mod(drc->unplug_timeout_timer,
+ qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) +
+ drck->unplug_timeout_seconds * 1000);
+ }
+}
+
void spapr_drc_attach(SpaprDrc *drc, DeviceState *d)
{
trace_spapr_drc_attach(spapr_drc_index(drc));
@@ -385,30 +399,18 @@ void spapr_drc_attach(SpaprDrc *drc, DeviceState *d)
NULL, 0);
}
-static void spapr_drc_release(SpaprDrc *drc)
-{
- SpaprDrcClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
-
- drck->release(drc->dev);
-
- drc->unplug_requested = false;
- g_free(drc->fdt);
- drc->fdt = NULL;
- drc->fdt_start_offset = 0;
- object_property_del(OBJECT(drc), "device");
- drc->dev = NULL;
-}
-
-void spapr_drc_detach(SpaprDrc *drc)
+void spapr_drc_unplug_request(SpaprDrc *drc)
{
SpaprDrcClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
- trace_spapr_drc_detach(spapr_drc_index(drc));
+ trace_spapr_drc_unplug_request(spapr_drc_index(drc));
g_assert(drc->dev);
drc->unplug_requested = true;
+ spapr_drc_start_unplug_timeout_timer(drc);
+
if (drc->state != drck->empty_state) {
trace_spapr_drc_awaiting_quiesce(spapr_drc_index(drc));
return;
@@ -417,6 +419,15 @@ void spapr_drc_detach(SpaprDrc *drc)
spapr_drc_release(drc);
}
+int spapr_drc_unplug_timeout_remaining_sec(SpaprDrc *drc)
+{
+ if (drc->unplug_requested) {
+ return timer_deadline_ms(drc->unplug_timeout_timer) / 1000;
+ }
+
+ return 0;
+}
+
bool spapr_drc_reset(SpaprDrc *drc)
{
SpaprDrcClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
@@ -488,11 +499,23 @@ static bool spapr_drc_needed(void *opaque)
spapr_drc_unplug_requested(drc);
}
+static int spapr_drc_post_load(void *opaque, int version_id)
+{
+ SpaprDrc *drc = opaque;
+
+ if (drc->unplug_requested) {
+ spapr_drc_start_unplug_timeout_timer(drc);
+ }
+
+ return 0;
+}
+
static const VMStateDescription vmstate_spapr_drc = {
.name = "spapr_drc",
.version_id = 1,
.minimum_version_id = 1,
.needed = spapr_drc_needed,
+ .post_load = spapr_drc_post_load,
.fields = (VMStateField []) {
VMSTATE_UINT32(state, SpaprDrc),
VMSTATE_END_OF_LIST()
@@ -503,6 +526,15 @@ static const VMStateDescription vmstate_spapr_drc = {
}
};
+static void drc_unplug_timeout_cb(void *opaque)
+{
+ SpaprDrc *drc = opaque;
+
+ if (drc->unplug_requested) {
+ drc->unplug_requested = false;
+ }
+}
+
static void drc_realize(DeviceState *d, Error **errp)
{
SpaprDrc *drc = SPAPR_DR_CONNECTOR(d);
@@ -525,6 +557,11 @@ static void drc_realize(DeviceState *d, Error **errp)
object_property_add_alias(root_container, link_name,
drc->owner, child_name);
g_free(link_name);
+
+ drc->unplug_timeout_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL,
+ drc_unplug_timeout_cb,
+ drc);
+
vmstate_register(VMSTATE_IF(drc), spapr_drc_index(drc), &vmstate_spapr_drc,
drc);
trace_spapr_drc_realize_complete(spapr_drc_index(drc));
@@ -542,6 +579,7 @@ static void drc_unrealize(DeviceState *d)
name = g_strdup_printf("%x", spapr_drc_index(drc));
object_property_del(root_container, name);
g_free(name);
+ timer_free(drc->unplug_timeout_timer);
}
SpaprDrc *spapr_dr_connector_new(Object *owner, const char *type,
@@ -683,6 +721,7 @@ static void spapr_drc_cpu_class_init(ObjectClass *k, void *data)
drck->drc_name_prefix = "CPU ";
drck->release = spapr_core_release;
drck->dt_populate = spapr_core_dt_populate;
+ drck->unplug_timeout_seconds = 15;
}
static void spapr_drc_pci_class_init(ObjectClass *k, void *data)
@@ -1190,6 +1229,15 @@ static void rtas_ibm_configure_connector(PowerPCCPU *cpu,
drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
+ /*
+ * This indicates that the kernel is reconfiguring a LMB due to
+ * a failed hotunplug. Rollback the DIMM unplug process.
+ */
+ if (spapr_drc_type(drc) == SPAPR_DR_CONNECTOR_TYPE_LMB &&
+ drc->unplug_requested) {
+ spapr_memory_unplug_rollback(spapr, drc->dev);
+ }
+
if (!drc->fdt) {
void *fdt;
int fdt_size;
diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c
index f1c7479816..feba18cb12 100644
--- a/hw/ppc/spapr_pci.c
+++ b/hw/ppc/spapr_pci.c
@@ -1723,12 +1723,12 @@ static void spapr_pci_unplug_request(HotplugHandler *plug_handler,
* functions, even if their unplug weren't requested
* beforehand.
*/
- spapr_drc_detach(func_drc);
+ spapr_drc_unplug_request(func_drc);
}
}
}
- spapr_drc_detach(drc);
+ spapr_drc_unplug_request(drc);
/* if this isn't func 0, defer unplug event. otherwise signal removal
* for all present functions
@@ -1743,6 +1743,10 @@ static void spapr_pci_unplug_request(HotplugHandler *plug_handler,
}
}
}
+ } else {
+ error_setg(errp,
+ "PCI device unplug already in progress for device %s",
+ drc->dev->id);
}
}
diff --git a/hw/ppc/trace-events b/hw/ppc/trace-events
index 1e91984526..b4bbfbb013 100644
--- a/hw/ppc/trace-events
+++ b/hw/ppc/trace-events
@@ -50,7 +50,7 @@ spapr_drc_set_allocation_state(uint32_t index, int state) "drc: 0x%"PRIx32", sta
spapr_drc_set_allocation_state_finalizing(uint32_t index) "drc: 0x%"PRIx32
spapr_drc_set_configured(uint32_t index) "drc: 0x%"PRIx32
spapr_drc_attach(uint32_t index) "drc: 0x%"PRIx32
-spapr_drc_detach(uint32_t index) "drc: 0x%"PRIx32
+spapr_drc_unplug_request(uint32_t index) "drc: 0x%"PRIx32
spapr_drc_awaiting_quiesce(uint32_t index) "drc: 0x%"PRIx32
spapr_drc_reset(uint32_t index) "drc: 0x%"PRIx32
spapr_drc_realize(uint32_t index) "drc: 0x%"PRIx32