diff mbox

[v3,4/5] hw/phb3: add host sync notifier to trigger creset/CAPP disable on kexec

Message ID c3c2f61fcc512d8b1906374d8a0fabbd264281f6.1485502358.git-series.andrew.donnellan@au1.ibm.com
State Accepted
Headers show

Commit Message

Andrew Donnellan Jan. 27, 2017, 7:33 a.m. UTC
To support kexec in Linux, we need to trigger a creset to disable CAPP mode
on each PHB that has been attached to a CAPP.

Add a host sync notifier, phb3_host_sync_reset(), that will be triggered by
the opal_sync_host_reboot() call that Linux makes when "shutting down" a
powernv system (this includes bringing the system down to prepare it for
kexec). This notifier will trigger a creset only on PHBs that need it, and
will poll regularly until the creset completes.

This approach is somewhat hacky, as it's somewhat of an abuse of the host
sync notifier system (IMHO), but it seems the most obvious way to
ensure that the reset/CAPP disable occurs that will work with old kernel
versions and not require additional support on the kernel side.

Suggested-by: Stewart Smith <stewart@linux.vnet.ibm.com>
Signed-off-by: Andrew Donnellan <andrew.donnellan@au1.ibm.com>

---

v2->v3:
* Correctly terminate polling when error is returned (thanks Gavin)

v1->v2:
* Add explanatory comment about use of host sync notifier (suggested by
Fred)
---
 hw/phb3.c | 40 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 40 insertions(+)
diff mbox

Patch

diff --git a/hw/phb3.c b/hw/phb3.c
index 053fe40..1ab4e8f 100644
--- a/hw/phb3.c
+++ b/hw/phb3.c
@@ -4623,6 +4623,44 @@  static bool phb3_calculate_windows(struct phb3 *p)
 	return true;
 }
 
+/*
+ * Trigger a creset to disable CAPI mode on kernel shutdown.
+ *
+ * This helper is called repeatedly by the host sync notifier mechanism, which
+ * relies on the kernel to regularly poll the OPAL_SYNC_HOST_REBOOT call as it
+ * shuts down.
+ *
+ * This is a somewhat hacky abuse of the host sync notifier mechanism, but the
+ * alternatives require a new API call which won't work for older kernels.
+ */
+static bool phb3_host_sync_reset(void *data)
+{
+	struct phb3 *p = (struct phb3 *)data;
+	struct pci_slot *slot = p->phb.slot;
+	struct proc_chip *chip = get_chip(p->chip_id);
+	int64_t rc;
+
+	switch (slot->state) {
+	case PHB3_SLOT_NORMAL:
+		lock(&capi_lock);
+		rc = (chip->capp_phb3_attached_mask & (1 << p->index)) ?
+			OPAL_PHB_CAPI_MODE_CAPI :
+			OPAL_PHB_CAPI_MODE_PCIE;
+		unlock(&capi_lock);
+
+		if (rc == OPAL_PHB_CAPI_MODE_PCIE)
+			return true;
+
+		PHBINF(p, "PHB in CAPI mode, resetting\n");
+		p->flags &= ~PHB3_CAPP_RECOVERY;
+		phb3_creset(slot);
+		return false;
+	default:
+		rc = slot->ops.poll(slot);
+		return rc <= OPAL_SUCCESS;
+	}
+}
+
 static void phb3_create(struct dt_node *np)
 {
 	const struct dt_property *prop;
@@ -4756,6 +4794,8 @@  static void phb3_create(struct dt_node *np)
 	/* Load capp microcode into capp unit */
 	capp_load_ucode(p);
 
+	opal_add_host_sync_notifier(phb3_host_sync_reset, p);
+
 	/* Platform additional setup */
 	if (platform.pci_setup_phb)
 		platform.pci_setup_phb(&p->phb, p->index);