aboutsummaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2021-03-12 11:30:55 +0000
committerPeter Maydell <peter.maydell@linaro.org>2021-03-12 11:30:55 +0000
commit1941858448e76f83eb00614c4f34ac29e9a8e792 (patch)
tree4d3bafbd64a5b337d56beb28a1fcd28bd9cc3001 /hw
parent6f34661b6c97a37a5efc27d31c037ddeda4547e2 (diff)
parenteb7f80fd26d73e7e1af105431da58971b1dba517 (diff)
downloadqemu-1941858448e76f83eb00614c4f34ac29e9a8e792.zip
qemu-1941858448e76f83eb00614c4f34ac29e9a8e792.tar.gz
qemu-1941858448e76f83eb00614c4f34ac29e9a8e792.tar.bz2
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')
-rw-r--r--hw/display/sm501.c160
-rw-r--r--hw/display/sm501_template.h131
-rw-r--r--hw/net/fsl_etsec/etsec.c1
-rw-r--r--hw/net/fsl_etsec/rings.c1
-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
9 files changed, 232 insertions, 249 deletions
diff --git a/hw/display/sm501.c b/hw/display/sm501.c
index 8966b69..8789722 100644
--- a/hw/display/sm501.c
+++ b/hw/display/sm501.c
@@ -1558,86 +1558,85 @@ typedef void draw_hwc_line_func(uint8_t *d, const uint8_t *s,
int width, const uint8_t *palette,
int c_x, int c_y);
-#define DEPTH 8
-#include "sm501_template.h"
-
-#define DEPTH 15
-#include "sm501_template.h"
-
-#define BGR_FORMAT
-#define DEPTH 15
-#include "sm501_template.h"
-
-#define DEPTH 16
-#include "sm501_template.h"
-
-#define BGR_FORMAT
-#define DEPTH 16
-#include "sm501_template.h"
-
-#define DEPTH 32
-#include "sm501_template.h"
-
-#define BGR_FORMAT
-#define DEPTH 32
-#include "sm501_template.h"
-
-static draw_line_func *draw_line8_funcs[] = {
- draw_line8_8,
- draw_line8_15,
- draw_line8_16,
- draw_line8_32,
- draw_line8_32bgr,
- draw_line8_15bgr,
- draw_line8_16bgr,
-};
-
-static draw_line_func *draw_line16_funcs[] = {
- draw_line16_8,
- draw_line16_15,
- draw_line16_16,
- draw_line16_32,
- draw_line16_32bgr,
- draw_line16_15bgr,
- draw_line16_16bgr,
-};
+static void draw_line8_32(uint8_t *d, const uint8_t *s, int width,
+ const uint32_t *pal)
+{
+ uint8_t v, r, g, b;
+ do {
+ v = ldub_p(s);
+ r = (pal[v] >> 16) & 0xff;
+ g = (pal[v] >> 8) & 0xff;
+ b = (pal[v] >> 0) & 0xff;
+ *(uint32_t *)d = rgb_to_pixel32(r, g, b);
+ s++;
+ d += 4;
+ } while (--width != 0);
+}
-static draw_line_func *draw_line32_funcs[] = {
- draw_line32_8,
- draw_line32_15,
- draw_line32_16,
- draw_line32_32,
- draw_line32_32bgr,
- draw_line32_15bgr,
- draw_line32_16bgr,
-};
+static void draw_line16_32(uint8_t *d, const uint8_t *s, int width,
+ const uint32_t *pal)
+{
+ uint16_t rgb565;
+ uint8_t r, g, b;
+
+ do {
+ rgb565 = lduw_le_p(s);
+ r = (rgb565 >> 8) & 0xf8;
+ g = (rgb565 >> 3) & 0xfc;
+ b = (rgb565 << 3) & 0xf8;
+ *(uint32_t *)d = rgb_to_pixel32(r, g, b);
+ s += 2;
+ d += 4;
+ } while (--width != 0);
+}
-static draw_hwc_line_func *draw_hwc_line_funcs[] = {
- draw_hwc_line_8,
- draw_hwc_line_15,
- draw_hwc_line_16,
- draw_hwc_line_32,
- draw_hwc_line_32bgr,
- draw_hwc_line_15bgr,
- draw_hwc_line_16bgr,
-};
+static void draw_line32_32(uint8_t *d, const uint8_t *s, int width,
+ const uint32_t *pal)
+{
+ uint8_t r, g, b;
+
+ do {
+ r = s[2];
+ g = s[1];
+ b = s[0];
+ *(uint32_t *)d = rgb_to_pixel32(r, g, b);
+ s += 4;
+ d += 4;
+ } while (--width != 0);
+}
-static inline int get_depth_index(DisplaySurface *surface)
+/**
+ * Draw hardware cursor image on the given line.
+ */
+static void draw_hwc_line_32(uint8_t *d, const uint8_t *s, int width,
+ const uint8_t *palette, int c_x, int c_y)
{
- switch (surface_bits_per_pixel(surface)) {
- default:
- case 8:
- return 0;
- case 15:
- return 1;
- case 16:
- return 2;
- case 32:
- if (is_surface_bgr(surface)) {
- return 4;
- } else {
- return 3;
+ int i;
+ uint8_t r, g, b, v, bitset = 0;
+
+ /* get cursor position */
+ assert(0 <= c_y && c_y < SM501_HWC_HEIGHT);
+ s += SM501_HWC_WIDTH * c_y / 4; /* 4 pixels per byte */
+ d += c_x * 4;
+
+ for (i = 0; i < SM501_HWC_WIDTH && c_x + i < width; i++) {
+ /* get pixel value */
+ if (i % 4 == 0) {
+ bitset = ldub_p(s);
+ s++;
}
+ v = bitset & 3;
+ bitset >>= 2;
+
+ /* write pixel */
+ if (v) {
+ v--;
+ r = palette[v * 3 + 0];
+ g = palette[v * 3 + 1];
+ b = palette[v * 3 + 2];
+ *(uint32_t *)d = rgb_to_pixel32(r, g, b);
+ }
+ d += 4;
}
}
@@ -1652,7 +1651,6 @@ static void sm501_update_display(void *opaque)
int height = get_height(s, crt);
int src_bpp = get_bpp(s, crt);
int dst_bpp = surface_bytes_per_pixel(surface);
- int dst_depth_index = get_depth_index(surface);
draw_line_func *draw_line = NULL;
draw_hwc_line_func *draw_hwc_line = NULL;
int full_update = 0;
@@ -1662,6 +1660,8 @@ static void sm501_update_display(void *opaque)
uint8_t hwc_palette[3 * 3];
uint8_t *hwc_src = NULL;
+ assert(dst_bpp == 4); /* Output is always 32-bit RGB */
+
if (!((crt ? s->dc_crt_control : s->dc_panel_control)
& SM501_DC_CRT_CONTROL_ENABLE)) {
return;
@@ -1674,13 +1674,13 @@ static void sm501_update_display(void *opaque)
/* choose draw_line function */
switch (src_bpp) {
case 1:
- draw_line = draw_line8_funcs[dst_depth_index];
+ draw_line = draw_line8_32;
break;
case 2:
- draw_line = draw_line16_funcs[dst_depth_index];
+ draw_line = draw_line16_32;
break;
case 4:
- draw_line = draw_line32_funcs[dst_depth_index];
+ draw_line = draw_line32_32;
break;
default:
qemu_log_mask(LOG_GUEST_ERROR, "sm501: update display"
@@ -1691,7 +1691,7 @@ static void sm501_update_display(void *opaque)
/* set up to draw hardware cursor */
if (is_hwc_enabled(s, crt)) {
/* choose cursor draw line function */
- draw_hwc_line = draw_hwc_line_funcs[dst_depth_index];
+ draw_hwc_line = draw_hwc_line_32;
hwc_src = get_hwc_address(s, crt);
c_x = get_hwc_x(s, crt);
c_y = get_hwc_y(s, crt);
diff --git a/hw/display/sm501_template.h b/hw/display/sm501_template.h
deleted file mode 100644
index a60abad..0000000
--- a/hw/display/sm501_template.h
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * Pixel drawing function templates for QEMU SM501 Device
- *
- * Copyright (c) 2008 Shin-ichiro KAWASAKI
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-#if DEPTH == 8
-#define BPP 1
-#define PIXEL_TYPE uint8_t
-#elif DEPTH == 15 || DEPTH == 16
-#define BPP 2
-#define PIXEL_TYPE uint16_t
-#elif DEPTH == 32
-#define BPP 4
-#define PIXEL_TYPE uint32_t
-#else
-#error unsupport depth
-#endif
-
-#ifdef BGR_FORMAT
-#define PIXEL_NAME glue(DEPTH, bgr)
-#else
-#define PIXEL_NAME DEPTH
-#endif /* BGR_FORMAT */
-
-
-static void glue(draw_line8_, PIXEL_NAME)(
- uint8_t *d, const uint8_t *s, int width, const uint32_t *pal)
-{
- uint8_t v, r, g, b;
- do {
- v = ldub_p(s);
- r = (pal[v] >> 16) & 0xff;
- g = (pal[v] >> 8) & 0xff;
- b = (pal[v] >> 0) & 0xff;
- *(PIXEL_TYPE *)d = glue(rgb_to_pixel, PIXEL_NAME)(r, g, b);
- s++;
- d += BPP;
- } while (--width != 0);
-}
-
-static void glue(draw_line16_, PIXEL_NAME)(
- uint8_t *d, const uint8_t *s, int width, const uint32_t *pal)
-{
- uint16_t rgb565;
- uint8_t r, g, b;
-
- do {
- rgb565 = lduw_le_p(s);
- r = (rgb565 >> 8) & 0xf8;
- g = (rgb565 >> 3) & 0xfc;
- b = (rgb565 << 3) & 0xf8;
- *(PIXEL_TYPE *)d = glue(rgb_to_pixel, PIXEL_NAME)(r, g, b);
- s += 2;
- d += BPP;
- } while (--width != 0);
-}
-
-static void glue(draw_line32_, PIXEL_NAME)(
- uint8_t *d, const uint8_t *s, int width, const uint32_t *pal)
-{
- uint8_t r, g, b;
-
- do {
- r = s[2];
- g = s[1];
- b = s[0];
- *(PIXEL_TYPE *)d = glue(rgb_to_pixel, PIXEL_NAME)(r, g, b);
- s += 4;
- d += BPP;
- } while (--width != 0);
-}
-
-/**
- * Draw hardware cursor image on the given line.
- */
-static void glue(draw_hwc_line_, PIXEL_NAME)(uint8_t *d, const uint8_t *s,
- int width, const uint8_t *palette, int c_x, int c_y)
-{
- int i;
- uint8_t r, g, b, v, bitset = 0;
-
- /* get cursor position */
- assert(0 <= c_y && c_y < SM501_HWC_HEIGHT);
- s += SM501_HWC_WIDTH * c_y / 4; /* 4 pixels per byte */
- d += c_x * BPP;
-
- for (i = 0; i < SM501_HWC_WIDTH && c_x + i < width; i++) {
- /* get pixel value */
- if (i % 4 == 0) {
- bitset = ldub_p(s);
- s++;
- }
- v = bitset & 3;
- bitset >>= 2;
-
- /* write pixel */
- if (v) {
- v--;
- r = palette[v * 3 + 0];
- g = palette[v * 3 + 1];
- b = palette[v * 3 + 2];
- *(PIXEL_TYPE *)d = glue(rgb_to_pixel, PIXEL_NAME)(r, g, b);
- }
- d += BPP;
- }
-}
-
-#undef DEPTH
-#undef BPP
-#undef PIXEL_TYPE
-#undef PIXEL_NAME
-#undef BGR_FORMAT
diff --git a/hw/net/fsl_etsec/etsec.c b/hw/net/fsl_etsec/etsec.c
index 93886bb..bd9d62b 100644
--- a/hw/net/fsl_etsec/etsec.c
+++ b/hw/net/fsl_etsec/etsec.c
@@ -27,6 +27,7 @@
*/
#include "qemu/osdep.h"
+#include "qemu-common.h"
#include "hw/sysbus.h"
#include "hw/irq.h"
#include "hw/ptimer.h"
diff --git a/hw/net/fsl_etsec/rings.c b/hw/net/fsl_etsec/rings.c
index fe055d3..d6be0d7 100644
--- a/hw/net/fsl_etsec/rings.c
+++ b/hw/net/fsl_etsec/rings.c
@@ -22,6 +22,7 @@
* THE SOFTWARE.
*/
#include "qemu/osdep.h"
+#include "qemu-common.h"
#include "net/checksum.h"
#include "qemu/log.h"
#include "etsec.h"
diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c
index 01517a6..1d94485 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 85fe65f..d56418c 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 8571d5b..8a71b03 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 f1c7479..feba18c 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 1e91984..b4bbfbb 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