Skip to contentSkip to conversation

powerpc/pseries: Move CPU DLPAR into the kernel

PATCH Superseded Nathan Fontenot 2 participants 11 messages Oct 27, 2015 – Dec 1, 2015 PowerPC

v2 of 3 Superseded Latest is v3 →

lore
Share & tools
lore
#0 lore raw

v2 cover powerpc/pseries: Move CPU DLPAR into the kernel

To better support CPU hotplug in PowerKVM and PowerVM environments, the handling of CPU dlpar should be done entirely in the kernel. This will allow a common entry point to be used for PowerVM and PowerKVM CPU dlpar requests.

For both environments the entry point is the same one introduced in a previous patch set that moved memory hotplug into the kernel. This entry point accepts a rtas hotplug event which is either constructed when using the /sys/kernel/dlpar interface or is passed to the kernel when handling a ras epow interrupt.

Note: The patches are based on top of two previous patches I sent out;

[PATCH] powerpc/pseries: Verify CPU doesn't exist before adding
https://lists.ozlabs.org/pipermail/linuxppc-dev/2015-October/135550.html

[PATCH v2] powerpc/pseries: Correct string length in pseries_of_derive_parent()
https://lists.ozlabs.org/pipermail/linuxppc-dev/2015-October/135667.html

-Nathan

Patch 1/6:
- Consolidate cpu hotplug code from pseries/dlpar.c to pseries/hotplug-cpu.c

Patch 2/6:
- Factor out common code pieces for both environments

Patch 3/6:
- Update cpu dlpar error recovery

Patch 4/6:
- Add cpu hotplug remove capability

Patch 5/6:
- Add cpu hotplug add capability

Patch 6/6:
- Enable sysfs interface for cpu hotplug

 dlpar.c       |  232 ----------------------
 hotplug-cpu.c |  596 +++++++++++++++++++++++++++++++++++++++++++++++++++++-----
 pseries.h     |    9 
 3 files changed, 568 insertions(+), 269 deletions(-)
#1 lore raw

PATCH v2 1/6 powerpc/pseries: Consolidate CPU hotplug code to hotplug-cpu.c

No functional changes, this patch is simply a move of the cpu hotplug code from pseries/dlpar.c to pseries/hotplug-cpu.c. This is in an effort to consolidate all of the cpu hotplug code in a common place.

---
 arch/powerpc/platforms/pseries/dlpar.c       |  226 --------------------------
 arch/powerpc/platforms/pseries/hotplug-cpu.c |  218 +++++++++++++++++++++++++
 2 files changed, 219 insertions(+), 225 deletions(-)
arch/powerpc/platforms/pseries/dlpar.c +1 -225
index fe6320d..438fdbd 100644
@@ -20,7 +20,6 @@
 #include <linux/of.h>
 
 #include "of_helpers.h"
-#include "offline_states.h"
 #include "pseries.h"
 
 #include <asm/prom.h>
@@ -338,220 +337,6 @@ int dlpar_release_drc(u32 drc_index)
 	return 0;
 }
 
-#ifdef CONFIG_ARCH_CPU_PROBE_RELEASE
-
-static int dlpar_online_cpu(struct device_node *dn)
-{
-	int rc = 0;
-	unsigned int cpu;
-	int len, nthreads, i;
-	const __be32 *intserv;
-	u32 thread;
-
-	intserv = of_get_property(dn, "ibm,ppc-interrupt-server#s", &len);
-	if (!intserv)
-		return -EINVAL;
-
-	nthreads = len / sizeof(u32);
-
-	cpu_maps_update_begin();
-	for (i = 0; i < nthreads; i++) {
-		thread = be32_to_cpu(intserv[i]);
-		for_each_present_cpu(cpu) {
-			if (get_hard_smp_processor_id(cpu) != thread)
-				continue;
-			BUG_ON(get_cpu_current_state(cpu)
-					!= CPU_STATE_OFFLINE);
-			cpu_maps_update_done();
-			rc = device_online(get_cpu_device(cpu));
-			if (rc)
-				goto out;
-			cpu_maps_update_begin();
-
-			break;
-		}
-		if (cpu == num_possible_cpus())
-			printk(KERN_WARNING "Could not find cpu to online "
-			       "with physical id 0x%x\n", thread);
-	}
-	cpu_maps_update_done();
-
-out:
-	return rc;
-
-}
-
-static bool dlpar_cpu_exists(struct device_node *parent, u32 drc_index)
-{
-	struct device_node *child = NULL;
-	u32 my_drc_index;
-	bool found;
-	int rc;
-
-	/* Assume cpu doesn't exist */
-	found = false;
-
-	for_each_child_of_node(parent, child) {
-		rc = of_property_read_u32(child, "ibm,my-drc-index",
-					  &my_drc_index);
-		if (rc)
-			continue;
-
-		if (my_drc_index == drc_index) {
-			of_node_put(child);
-			found = true;
-			break;
-		}
-	}
-
-	return found;
-}
-
-static ssize_t dlpar_cpu_probe(const char *buf, size_t count)
-{
-	struct device_node *dn, *parent;
-	u32 drc_index;
-	int rc;
-
-	rc = kstrtou32(buf, 0, &drc_index);
-	if (rc)
-		return -EINVAL;
-
-	parent = of_find_node_by_path("/cpus");
-	if (!parent)
-		return -ENODEV;
-
-	if (dlpar_cpu_exists(parent, drc_index)) {
-		of_node_put(parent);
-		printk(KERN_WARNING "CPU with drc index %x already exists\n",
-		       drc_index);
-		return -EINVAL;
-	}
-
-	rc = dlpar_acquire_drc(drc_index);
-	if (rc) {
-		of_node_put(parent);
-		return -EINVAL;
-	}
-
-	dn = dlpar_configure_connector(cpu_to_be32(drc_index), parent);
-	of_node_put(parent);
-	if (!dn) {
-		dlpar_release_drc(drc_index);
-		return -EINVAL;
-	}
-
-	rc = dlpar_attach_node(dn);
-	if (rc) {
-		dlpar_release_drc(drc_index);
-		dlpar_free_cc_nodes(dn);
-		return rc;
-	}
-
-	rc = dlpar_online_cpu(dn);
-	if (rc)
-		return rc;
-
-	return count;
-}
-
-static int dlpar_offline_cpu(struct device_node *dn)
-{
-	int rc = 0;
-	unsigned int cpu;
-	int len, nthreads, i;
-	const __be32 *intserv;
-	u32 thread;
-
-	intserv = of_get_property(dn, "ibm,ppc-interrupt-server#s", &len);
-	if (!intserv)
-		return -EINVAL;
-
-	nthreads = len / sizeof(u32);
-
-	cpu_maps_update_begin();
-	for (i = 0; i < nthreads; i++) {
-		thread = be32_to_cpu(intserv[i]);
-		for_each_present_cpu(cpu) {
-			if (get_hard_smp_processor_id(cpu) != thread)
-				continue;
-
-			if (get_cpu_current_state(cpu) == CPU_STATE_OFFLINE)
-				break;
-
-			if (get_cpu_current_state(cpu) == CPU_STATE_ONLINE) {
-				set_preferred_offline_state(cpu, CPU_STATE_OFFLINE);
-				cpu_maps_update_done();
-				rc = device_offline(get_cpu_device(cpu));
-				if (rc)
-					goto out;
-				cpu_maps_update_begin();
-				break;
-
-			}
-
-			/*
-			 * The cpu is in CPU_STATE_INACTIVE.
-			 * Upgrade it's state to CPU_STATE_OFFLINE.
-			 */
-			set_preferred_offline_state(cpu, CPU_STATE_OFFLINE);
-			BUG_ON(plpar_hcall_norets(H_PROD, thread)
-								!= H_SUCCESS);
-			__cpu_die(cpu);
-			break;
-		}
-		if (cpu == num_possible_cpus())
-			printk(KERN_WARNING "Could not find cpu to offline "
-			       "with physical id 0x%x\n", thread);
-	}
-	cpu_maps_update_done();
-
-out:
-	return rc;
-
-}
-
-static ssize_t dlpar_cpu_release(const char *buf, size_t count)
-{
-	struct device_node *dn;
-	u32 drc_index;
-	int rc;
-
-	dn = of_find_node_by_path(buf);
-	if (!dn)
-		return -EINVAL;
-
-	rc = of_property_read_u32(dn, "ibm,my-drc-index", &drc_index);
-	if (rc) {
-		of_node_put(dn);
-		return -EINVAL;
-	}
-
-	rc = dlpar_offline_cpu(dn);
-	if (rc) {
-		of_node_put(dn);
-		return -EINVAL;
-	}
-
-	rc = dlpar_release_drc(drc_index);
-	if (rc) {
-		of_node_put(dn);
-		return rc;
-	}
-
-	rc = dlpar_detach_node(dn);
-	if (rc) {
-		dlpar_acquire_drc(drc_index);
-		return rc;
-	}
-
-	of_node_put(dn);
-
-	return count;
-}
-
-#endif /* CONFIG_ARCH_CPU_PROBE_RELEASE */
-
 static int handle_dlpar_errorlog(struct pseries_hp_errorlog *hp_elog)
 {
 	int rc;
@@ -659,16 +444,7 @@ static CLASS_ATTR(dlpar, S_IWUSR, NULL, dlpar_store);
 
 static int __init pseries_dlpar_init(void)
 {
-	int rc;
-
-#ifdef CONFIG_ARCH_CPU_PROBE_RELEASE
-	ppc_md.cpu_probe = dlpar_cpu_probe;
-	ppc_md.cpu_release = dlpar_cpu_release;
-#endif /* CONFIG_ARCH_CPU_PROBE_RELEASE */
-
-	rc = sysfs_create_file(kernel_kobj, &class_attr_dlpar.attr);
-
-	return rc;
+	return sysfs_create_file(kernel_kobj, &class_attr_dlpar.attr);
 }
 machine_device_initcall(pseries, pseries_dlpar_init);
 
arch/powerpc/platforms/pseries/hotplug-cpu.c +218 -0

236 lines not on this page · raw message

#2 lore raw

PATCH v2 2/6 powerpc/pseries: Factor out common cpu hotplug code

In reply to Nathan Fontenot

Re-factor the cpu hotplug code to support doing cpu hotplug completely in the kernel and using the existing sysfs probe/release interfaces. This patch pulls out pieces of existing cpu hotplug code into common routines, dlpar_cpu_add() and dlpar_cpu_remove(), to be used by both interfaces. There are no functional changes introduced.

---
 arch/powerpc/platforms/pseries/hotplug-cpu.c |   70 ++++++++++++++------------
 1 file changed, 39 insertions(+), 31 deletions(-)
arch/powerpc/platforms/pseries/hotplug-cpu.c +39 -31
index 10c2942..d6cb184 100644
@@ -340,8 +340,6 @@ static void pseries_remove_processor(struct device_node *np)
 	cpu_maps_update_done();
 }
 
-#ifdef CONFIG_ARCH_CPU_PROBE_RELEASE
-
 static int dlpar_online_cpu(struct device_node *dn)
 {
 	int rc = 0;
@@ -409,16 +407,11 @@ static bool dlpar_cpu_exists(struct device_node *parent, u32 drc_index)
 	return found;
 }
 
-static ssize_t dlpar_cpu_probe(const char *buf, size_t count)
+static ssize_t dlpar_cpu_add(u32 drc_index)
 {
 	struct device_node *dn, *parent;
-	u32 drc_index;
 	int rc;
 
-	rc = kstrtou32(buf, 0, &drc_index);
-	if (rc)
-		return -EINVAL;
-
 	parent = of_find_node_by_path("/cpus");
 	if (!parent)
 		return -ENODEV;
@@ -449,10 +442,7 @@ static ssize_t dlpar_cpu_probe(const char *buf, size_t count)
 	}
 
 	rc = dlpar_online_cpu(dn);
-	if (rc)
-		return rc;
-
-	return count;
+	return rc;
 }
 
 static int dlpar_offline_cpu(struct device_node *dn)
@@ -511,6 +501,41 @@ out:
 
 }
 
+static ssize_t dlpar_cpu_remove(struct device_node *dn, u32 drc_index)
+{
+	int rc;
+
+	rc = dlpar_offline_cpu(dn);
+	if (rc)
+		return -EINVAL;
+
+	rc = dlpar_release_drc(drc_index);
+	if (rc)
+		return rc;
+
+	rc = dlpar_detach_node(dn);
+	if (rc)
+		dlpar_acquire_drc(drc_index);
+
+	return rc;
+}
+
+#ifdef CONFIG_ARCH_CPU_PROBE_RELEASE
+
+static ssize_t dlpar_cpu_probe(const char *buf, size_t count)
+{
+	u32 drc_index;
+	int rc;
+
+	rc = kstrtou32(buf, 0, &drc_index);
+	if (rc)
+		return -EINVAL;
+
+	rc = dlpar_cpu_add(drc_index);
+
+	return rc ? rc : count;
+}
+
 static ssize_t dlpar_cpu_release(const char *buf, size_t count)
 {
 	struct device_node *dn;
@@ -527,27 +552,10 @@ static ssize_t dlpar_cpu_release(const char *buf, size_t count)
 		return -EINVAL;
 	}
 
-	rc = dlpar_offline_cpu(dn);
-	if (rc) {
-		of_node_put(dn);
-		return -EINVAL;
-	}
-
-	rc = dlpar_release_drc(drc_index);
-	if (rc) {
-		of_node_put(dn);
-		return rc;
-	}
-
-	rc = dlpar_detach_node(dn);
-	if (rc) {
-		dlpar_acquire_drc(drc_index);
-		return rc;
-	}
-
+	rc = dlpar_cpu_remove(dn, drc_index);
 	of_node_put(dn);
 
-	return count;
+	return rc ? rc : count;
 }
 
 #endif /* CONFIG_ARCH_CPU_PROBE_RELEASE */
#3 lore raw

PATCH v2 3/6 powerpc/pseries: Update CPU hotplug error recovery

In reply to Nathan Fontenot

Update the cpu dlpar add/remove paths to do better error recovery when a failure occurs during the add/remove operation.

---

Updates for v2:
- Corrected some pr_* statements moving pr_info to pr_debug staements and pr_debug to pr_warn statements.

 arch/powerpc/platforms/pseries/hotplug-cpu.c |   76 ++++++++++++++++++++++----
 1 file changed, 63 insertions(+), 13 deletions(-)
arch/powerpc/platforms/pseries/hotplug-cpu.c +63 -13
index d6cb184..f080e81 100644
@@ -18,6 +18,8 @@
  *      2 of the License, or (at your option) any later version.
  */
 
+#define pr_fmt(fmt)     "pseries-hotplug-cpu: " fmt
+
 #include <linux/kernel.h>
 #include <linux/interrupt.h>
 #include <linux/delay.h>
@@ -410,38 +412,67 @@ static bool dlpar_cpu_exists(struct device_node *parent, u32 drc_index)
 static ssize_t dlpar_cpu_add(u32 drc_index)
 {
 	struct device_node *dn, *parent;
-	int rc;
+	int rc, saved_rc;
+
+	pr_debug("Attempting to add CPU, drc index: %x\n", drc_index);
 
 	parent = of_find_node_by_path("/cpus");
-	if (!parent)
+	if (!parent) {
+		pr_warn("Failed to find CPU root node \"/cpus\"\n");
 		return -ENODEV;
+	}
 
 	if (dlpar_cpu_exists(parent, drc_index)) {
 		of_node_put(parent);
-		printk(KERN_WARNING "CPU with drc index %x already exists\n",
-		       drc_index);
+		pr_warn("CPU with drc index %x already exists\n", drc_index);
 		return -EINVAL;
 	}
 
 	rc = dlpar_acquire_drc(drc_index);
 	if (rc) {
+		pr_warn("Failed to acquire DRC, rc: %d, drc index: %x\n",
+			rc, drc_index);
 		of_node_put(parent);
 		return -EINVAL;
 	}
 
 	dn = dlpar_configure_connector(cpu_to_be32(drc_index), parent);
 	of_node_put(parent);
-	if (!dn)
+	if (!dn) {
+		pr_warn("Failed call to configure-connector, drc index: %x\n",
+			drc_index);
+		dlpar_release_drc(drc_index);
 		return -EINVAL;
+	}
 
 	rc = dlpar_attach_node(dn);
 	if (rc) {
-		dlpar_release_drc(drc_index);
-		dlpar_free_cc_nodes(dn);
-		return rc;
+		saved_rc = rc;
+		pr_warn("Failed to attach node %s, rc: %d, drc index: %x\n",
+			dn->name, rc, drc_index);
+
+		rc = dlpar_release_drc(drc_index);
+		if (!rc)
+			dlpar_free_cc_nodes(dn);
+
+		return saved_rc;
 	}
 
 	rc = dlpar_online_cpu(dn);
+	if (rc) {
+		saved_rc = rc;
+		pr_warn("Failed to online cpu %s, rc: %d, drc index: %x\n",
+			dn->name, rc, drc_index);
+
+		rc = dlpar_detach_node(dn);
+		if (!rc)
+			dlpar_release_drc(drc_index);
+
+		return saved_rc;
+	}
+
+	pr_debug("Successfully added CPU %s, drc index: %x\n", dn->name,
+		 drc_index);
 	return rc;
 }
 
@@ -505,19 +536,38 @@ static ssize_t dlpar_cpu_remove(struct device_node *dn, u32 drc_index)
 {
 	int rc;
 
+	pr_debug("Attemping to remove CPU %s, drc index: %x\n",
+		 dn->name, drc_index);
+
 	rc = dlpar_offline_cpu(dn);
-	if (rc)
+	if (rc) {
+		pr_warn("Failed to offline CPU %s, rc: %d\n", dn->name, rc);
 		return -EINVAL;
+	}
 
 	rc = dlpar_release_drc(drc_index);
-	if (rc)
+	if (rc) {
+		pr_warn("Failed to release drc (%x) for CPU %s, rc: %d\n",
+			drc_index, dn->name, rc);
+		dlpar_online_cpu(dn);
 		return rc;
+	}
 
 	rc = dlpar_detach_node(dn);
-	if (rc)
-		dlpar_acquire_drc(drc_index);
+	if (rc) {
+		int saved_rc = rc;
 
-	return rc;
+		pr_warn("Failed to detach CPU %s, rc: %d", dn->name, rc);
+
+		rc = dlpar_acquire_drc(drc_index);
+		if (!rc)
+			dlpar_online_cpu(dn);
+
+		return saved_rc;
+	}
+
+	pr_debug("Successfully removed CPU, drc index: %x\n", drc_index);
+	return 0;
 }
 
 #ifdef CONFIG_ARCH_CPU_PROBE_RELEASE
#4 lore raw

PATCH v2 4/6 powerpc/pseries: Add CPU dlpar remove functionality

In reply to Nathan Fontenot

Add the ability to dlpar remove CPUs via hotplug rtas events, either by specifying the drc-index of the CPU to remove or providing a count of cpus to remove.

To remove multiple cpus in a single request we create a list of possible DR (Dynamic Reconfiguration) cpus and their drc indexes that can be removed. We can then traverse the list remove each cpu and easily clean up in any cases of failure.

---

Updates for v2:
- use for_each_node_by_type() instead of for_each_child_of_node
- updated how the list of cpus to remove is generated
- change to remove all cpus requested or none at all
- put function declarations under CONFIG_HOTPLUG_CPU

 arch/powerpc/platforms/pseries/hotplug-cpu.c |  138 ++++++++++++++++++++++++++
 arch/powerpc/platforms/pseries/pseries.h     |    9 ++
 2 files changed, 147 insertions(+)
arch/powerpc/platforms/pseries/hotplug-cpu.c +138 -0
index f080e81..635f0ba 100644
@@ -26,6 +26,7 @@
 #include <linux/sched.h>	/* for idle_task_exit */
 #include <linux/cpu.h>
 #include <linux/of.h>
+#include <linux/slab.h>
 #include <asm/prom.h>
 #include <asm/rtas.h>
 #include <asm/firmware.h>
@@ -570,6 +571,143 @@ static ssize_t dlpar_cpu_remove(struct device_node *dn, u32 drc_index)
 	return 0;
 }
 
+static struct device_node *cpu_drc_index_to_dn(u32 drc_index)
+{
+	struct device_node *dn;
+	u32 my_index;
+	int rc;
+
+	for_each_node_by_type(dn, "cpu") {
+		rc = of_property_read_u32(dn, "ibm,my-drc-index", &my_index);
+		if (rc)
+			continue;
+
+		if (my_index == drc_index)
+			break;
+	}
+
+	return dn;
+}
+
+static int dlpar_cpu_remove_by_index(u32 drc_index)
+{
+	struct device_node *dn;
+	int rc;
+
+	dn = cpu_drc_index_to_dn(drc_index);
+	if (!dn)
+		return -ENODEV;
+
+	rc = dlpar_cpu_remove(dn, drc_index);
+	of_node_put(dn);
+	return rc;
+}
+
+static u32 *dlpar_cpus_to_remove(int cpus_to_remove)
+{
+	struct device_node *dn;
+	u32 *cpu_drcs;
+	int cpus_found = 0;
+	int i, rc;
+
+	cpu_drcs = kcalloc(cpus_to_remove, sizeof(*cpu_drcs), GFP_KERNEL);
+	if (!cpu_drcs)
+		return NULL;
+
+	i = 0;
+	for_each_node_by_type(dn, "cpu") {
+		cpus_found++;
+
+		if (cpus_found > cpus_to_remove) {
+			of_node_put(dn);
+			break;
+		}
+
+		rc = of_property_read_u32(dn, "ibm,my-drc-index",
+					  &cpu_drcs[i++]);
+		if (rc) {
+			of_node_put(dn);
+			break;
+		}
+	}
+
+	/* We want to find cpus_to_remove + 1 CPUs to ensure we do not
+	 * remove the last CPU.
+	 */
+	if (cpus_found <= cpus_to_remove) {
+		pr_warn("Failed to find enough CPUs (%d of %d) to remove\n",
+			cpus_found, cpus_to_remove);
+		kfree(cpu_drcs);
+		cpu_drcs = NULL;
+	}
+
+	return cpu_drcs;
+}
+
+static int dlpar_cpu_remove_by_count(u32 cpus_to_remove)
+{
+	u32 *cpu_drcs;
+	int cpus_removed = 0;
+	int i, rc;
+
+	pr_debug("Attempting to hot-remove %d CPUs\n", cpus_to_remove);
+
+	cpu_drcs = dlpar_cpus_to_remove(cpus_to_remove);
+	if (!cpu_drcs)
+		return -EINVAL;
+
+	for (i = 0; i < cpus_to_remove; i++) {
+		rc = dlpar_cpu_remove_by_index(cpu_drcs[i]);
+		if (rc)
+			break;
+
+		cpus_removed++;
+	}
+
+	if (cpus_removed != cpus_to_remove) {
+		pr_warn("CPU hot-remove failed, adding back removed CPUs\n");
+
+		for (i = 0; i < cpus_removed; i++)
+			dlpar_cpu_add(cpu_drcs[i]);
+
+		rc = -EINVAL;
+	} else {
+		rc = 0;
+	}
+
+	kfree(cpu_drcs);
+	return rc;
+}
+
+int dlpar_cpu(struct pseries_hp_errorlog *hp_elog)
+{
+	u32 count, drc_index;
+	int rc;
+
+	count = hp_elog->_drc_u.drc_count;
+	drc_index = hp_elog->_drc_u.drc_index;
+
+	lock_device_hotplug();
+
+	switch (hp_elog->action) {
+	case PSERIES_HP_ELOG_ACTION_REMOVE:
+		if (hp_elog->id_type == PSERIES_HP_ELOG_ID_DRC_COUNT)
+			rc = dlpar_cpu_remove_by_count(count);
+		else if (hp_elog->id_type == PSERIES_HP_ELOG_ID_DRC_INDEX)
+			rc = dlpar_cpu_remove_by_index(drc_index);
+		else
+			rc = -EINVAL;
+		break;
+	default:
+		pr_err("Invalid action (%d) specified\n", hp_elog->action);
+		rc = -EINVAL;
+		break;
+	}
+
+	unlock_device_hotplug();
+	return rc;
+}
+
 #ifdef CONFIG_ARCH_CPU_PROBE_RELEASE
 
 static ssize_t dlpar_cpu_probe(const char *buf, size_t count)
arch/powerpc/platforms/pseries/pseries.h +9 -0
index 8411c27..7aa83f0 100644
@@ -73,6 +73,15 @@ static inline int dlpar_memory(struct pseries_hp_errorlog *hp_elog)
 }
 #endif
 
+#ifdef CONFIG_HOTPLUG_CPU
+int dlpar_cpu(struct pseries_hp_errorlog *hp_elog);
+#else
+static inline int dlpar_cpu(struct pseries_hp_errorlog *hp_elog)
+{
+	return -EOPNOTSUPP;
+}
+#endif
+
 /* PCI root bridge prepare function override for pseries */
 struct pci_host_bridge;
 int pseries_root_bridge_prepare(struct pci_host_bridge *bridge);
#5 lore raw

Re: [PATCH v2 4/6] powerpc/pseries: Add CPU dlpar remove functionality

Quoted 31 lines · Nathan Fontenot

Add the ability to dlpar remove CPUs via hotplug rtas events, either by specifying the drc-index of the CPU to remove or providing a count of cpus to remove.

arch/powerpc/platforms/pseries/hotplug-cpu.c +19 -0
index f080e81..635f0ba 100644
@@ -570,6 +571,143 @@ static ssize_t dlpar_cpu_remove(struct device_node *dn, u32 drc_index)
 	return 0;
 }
 
+static struct device_node *cpu_drc_index_to_dn(u32 drc_index)
+{
+	struct device_node *dn;
+	u32 my_index;
+	int rc;
+
+	for_each_node_by_type(dn, "cpu") {
+		rc = of_property_read_u32(dn, "ibm,my-drc-index", &my_index);
+		if (rc)
+			continue;
+
+		if (my_index == drc_index)
+			break;
+	}
+
+	return dn;
+}
+
+static int dlpar_cpu_remove_by_index(u32 drc_index)

arch/powerpc/platforms/pseries/hotplug-cpu.c static ssize_t dlpar_cpu_remove(struct device_node *dn, u32 drc_index) · line 602

Quoted 10 lines
arch/powerpc/platforms/pseries/hotplug-cpu.c +10 -0
+{
+	struct device_node *dn;
+	int rc;
+
+	dn = cpu_drc_index_to_dn(drc_index);
+	if (!dn)
+		return -ENODEV;
+
+	rc = dlpar_cpu_remove(dn, drc_index);
+	of_node_put(dn);

Does dlpar_cpu_remove() work when you still hold a reference to the dn?

It looks like dlpar_detach_node() does an of_node_put() also.

Quoted 18 lines
arch/powerpc/platforms/pseries/hotplug-cpu.c +18 -0
+	return rc;
+}
+
+static u32 *dlpar_cpus_to_remove(int cpus_to_remove)
+{
+	struct device_node *dn;
+	u32 *cpu_drcs;
+	int cpus_found = 0;
+	int i, rc;
+
+	cpu_drcs = kcalloc(cpus_to_remove, sizeof(*cpu_drcs), GFP_KERNEL);
+	if (!cpu_drcs)
+		return NULL;
+
+	i = 0;
+	for_each_node_by_type(dn, "cpu") {
+		cpus_found++;
+

arch/powerpc/platforms/pseries/hotplug-cpu.c static ssize_t dlpar_cpu_remove(struct device_node *dn, u32 drc_index) · line 630

Quoted 10 lines
arch/powerpc/platforms/pseries/hotplug-cpu.c +10 -0
+		if (cpus_found > cpus_to_remove) {
+			of_node_put(dn);
+			break;
+		}
+
+		rc = of_property_read_u32(dn, "ibm,my-drc-index",
+					  &cpu_drcs[i++]);
+		if (rc) {
+			of_node_put(dn);
+			break;

I'm not sure about the logic here with cpus_found and i. If you break here cpus_found will be 1, but you found zero, which seems odd.

If instead you delayed the increment of i:

		rc = of_property_read_u32(dn, "ibm,my-drc-index",
					  &cpu_drcs[i]);
		if (rc) {
			of_node_put(dn);
			break;
		}

		i++;
	}

Then i would equal the number of cpus found at all times. If you need to count one more in the if below you can just do that there.

arch/powerpc/platforms/pseries/hotplug-cpu.c static ssize_t dlpar_cpu_remove(struct device_node *dn, u32 drc_index) · line 642

Quoted 10 lines
arch/powerpc/platforms/pseries/hotplug-cpu.c +10 -0
+
+	/* We want to find cpus_to_remove + 1 CPUs to ensure we do not
+	 * remove the last CPU.
+	 */
+	if (cpus_found <= cpus_to_remove) {
+		pr_warn("Failed to find enough CPUs (%d of %d) to remove\n",
+			cpus_found, cpus_to_remove);
+		kfree(cpu_drcs);
+		cpu_drcs = NULL;
+	}

On my two cpu system when I do "cpu remove count 1" this always says:

pseries-hotplug-cpu: Failed to find enough CPUs (1 of 1) to remove

Which confuses me.

I suspect that's because I actually have one cpu *node*, which appears to Linux as two cpus (due to SMT). So I think it's working as expected, but it's not very clear from a user's perspective.

cheers

#6 lore raw

Re: [PATCH v2 4/6] powerpc/pseries: Add CPU dlpar remove functionality

Quoted 37 lines · Michael Ellerman
Quoted 35 lines · Nathan Fontenot

Add the ability to dlpar remove CPUs via hotplug rtas events, either by specifying the drc-index of the CPU to remove or providing a count of cpus to remove.

arch/powerpc/platforms/pseries/hotplug-cpu.c +23 -0
index f080e81..635f0ba 100644
@@ -570,6 +571,143 @@ static ssize_t dlpar_cpu_remove(struct device_node *dn, u32 drc_index)
 	return 0;
 }
 
+static struct device_node *cpu_drc_index_to_dn(u32 drc_index)
+{
+	struct device_node *dn;
+	u32 my_index;
+	int rc;
+
+	for_each_node_by_type(dn, "cpu") {
+		rc = of_property_read_u32(dn, "ibm,my-drc-index", &my_index);
+		if (rc)
+			continue;
+
+		if (my_index == drc_index)
+			break;
+	}
+
+	return dn;
+}
+
+static int dlpar_cpu_remove_by_index(u32 drc_index)
+{
+	struct device_node *dn;
+	int rc;
+
Quoted 8 lines

arch/powerpc/platforms/pseries/hotplug-cpu.c static ssize_t dlpar_cpu_remove(struct device_node *dn, u32 drc_index) · line 602

Quoted 6 lines
arch/powerpc/platforms/pseries/hotplug-cpu.c +6 -0
+	dn = cpu_drc_index_to_dn(drc_index);
+	if (!dn)
+		return -ENODEV;
+
+	rc = dlpar_cpu_remove(dn, drc_index);
+	of_node_put(dn);

Does dlpar_cpu_remove() work when you still hold a reference to the dn?

Yes, this works while holding the dn reference here.

Quoted 2 lines

It looks like dlpar_detach_node() does an of_node_put() also.

Correct. The of_node_put() in dlpar_detach_node() is there to do a put from the initial node creation, without this the reference count would never go to zero and the node would not be released.

Quoted 24 lines
Quoted 24 lines
arch/powerpc/platforms/pseries/hotplug-cpu.c +23 -0
+	return rc;
+}
+
+static u32 *dlpar_cpus_to_remove(int cpus_to_remove)
+{
+	struct device_node *dn;
+	u32 *cpu_drcs;
+	int cpus_found = 0;
+	int i, rc;
+
+	cpu_drcs = kcalloc(cpus_to_remove, sizeof(*cpu_drcs), GFP_KERNEL);
+	if (!cpu_drcs)
+		return NULL;
+
+	i = 0;
+	for_each_node_by_type(dn, "cpu") {
+		cpus_found++;
+
+		if (cpus_found > cpus_to_remove) {
+			of_node_put(dn);
+			break;
+		}
+
Quoted 9 lines

arch/powerpc/platforms/pseries/hotplug-cpu.c static ssize_t dlpar_cpu_remove(struct device_node *dn, u32 drc_index) · line 630

Quoted 5 lines
arch/powerpc/platforms/pseries/hotplug-cpu.c +5 -0
+		rc = of_property_read_u32(dn, "ibm,my-drc-index",
+					  &cpu_drcs[i++]);
+		if (rc) {
+			of_node_put(dn);
+			break;

I'm not sure about the logic here with cpus_found and i. If you break here cpus_found will be 1, but you found zero, which seems odd.

Agreed, it is a bit odd. The cpus_found var is meant to count the number of cpus we find when looping through for_each_node_by_type() whereas i is just meant to be an index into the cpu_cars array so we can save the drc-index of the cpus we find. The variable i is just to index the array, nothing more.

Perhaps instead of having i to use as an index into the cpu_drcs array I could just use [cpus_found - 1] to index cpu_drcs and get rid of i. Not sure if that makes the code any easier to read.

Quoted 28 lines

If instead you delayed the increment of i:

		rc = of_property_read_u32(dn, "ibm,my-drc-index",
					  &cpu_drcs[i]);
		if (rc) {
			of_node_put(dn);
			break;
		}

		i++;
	}

Then i would equal the number of cpus found at all times. If you need to count one more in the if below you can just do that there.

arch/powerpc/platforms/pseries/hotplug-cpu.c static ssize_t dlpar_cpu_remove(struct device_node *dn, u32 drc_index) · line 642

Quoted 10 lines
arch/powerpc/platforms/pseries/hotplug-cpu.c +10 -0
+
+	/* We want to find cpus_to_remove + 1 CPUs to ensure we do not
+	 * remove the last CPU.
+	 */
+	if (cpus_found <= cpus_to_remove) {
+		pr_warn("Failed to find enough CPUs (%d of %d) to remove\n",
+			cpus_found, cpus_to_remove);
+		kfree(cpu_drcs);
+		cpu_drcs = NULL;
+	}

On my two cpu system when I do "cpu remove count 1" this always says:

Quoted 8 lines

pseries-hotplug-cpu: Failed to find enough CPUs (1 of 1) to remove

Which confuses me.

I suspect that's because I actually have one cpu *node*, which appears to Linux as two cpus (due to SMT). So I think it's working as expected, but it's not very clear from a user's perspective.

You are correct. For Power CPU DLPAR works on a node basis. If there is only one node it will not remove the last CPU.

I should update the cpus_found to check to print a message if the remove request would entail removing the last CPU and that the request is being failed because of that.

Thanks for the feedback,
-Nathan

#7 lore raw

PATCH v2 5/6 powerpc/pseries: Add CPU dlpar add functionality

In reply to Nathan Fontenot

Add the ability to hotplug add cpus via rtas hotplug events by either specifying the drc index of the CPU to add, or providing a count of the number of CPUs to add.

---
 arch/powerpc/platforms/pseries/hotplug-cpu.c |   94 ++++++++++++++++++++++++++
 1 file changed, 94 insertions(+)
arch/powerpc/platforms/pseries/hotplug-cpu.c +94 -0
index 635f0ba..8ec52c0 100644
@@ -679,6 +679,92 @@ static int dlpar_cpu_remove_by_count(u32 cpus_to_remove)
 	return rc;
 }
 
+static u32 *dlpar_cpus_to_add(u32 cpus_to_add)
+{
+	struct device_node *parent;
+	u32 *cpu_drcs;
+	int cpus_found = 0;
+	int i, index, rc;
+
+	cpu_drcs = kcalloc(cpus_to_add, sizeof(*cpu_drcs), GFP_KERNEL);
+	if (!cpu_drcs)
+		return NULL;
+
+	parent = of_find_node_by_path("/cpus");
+	if (!parent) {
+		kfree(cpu_drcs);
+		return NULL;
+	}
+
+	/* Search the ibm,drc-indexes array for possible CPU drcs to
+	 * add. Note that the format of the ibm,drc-indexes array is
+	 * the number of entries in the array followed by the array
+	 * of drc values so we start looking at index = 1.
+	 */
+	i = 0;
+	index = 1;
+	while (cpus_found < cpus_to_add) {
+		u32 drc;
+
+		rc = of_property_read_u32_index(parent, "ibm,drc-indexes",
+						index++, &drc);
+		if (rc)
+			break;
+
+		if (dlpar_cpu_exists(parent, drc))
+			continue;
+
+		cpu_drcs[i++] = drc;
+		cpus_found++;
+	}
+
+	of_node_put(parent);
+
+	if (cpus_found < cpus_to_add) {
+		pr_warn("Failed to find enough CPUs (%d of %d) to add\n",
+			cpus_found, cpus_to_add);
+		kfree(cpu_drcs);
+		cpu_drcs = NULL;
+	}
+
+	return cpu_drcs;
+}
+
+static int dlpar_cpu_add_by_count(u32 cpus_to_add)
+{
+	u32 *cpu_drcs;
+	int cpus_added = 0;
+	int i, rc;
+
+	pr_debug("Attempting to hot-add %d CPUs\n", cpus_to_add);
+
+	cpu_drcs = dlpar_cpus_to_add(cpus_to_add);
+	if (!cpu_drcs)
+		return -EINVAL;
+
+	for (i = 0; i < cpus_to_add; i++) {
+		rc = dlpar_cpu_add(cpu_drcs[i]);
+		if (rc)
+			break;
+
+		cpus_added++;
+	}
+
+	if (cpus_added < cpus_to_add) {
+		pr_warn("CPU hot-add failed, removing any added CPUs\n");
+
+		for (i = 0; i < cpus_added; i++)
+			dlpar_cpu_remove_by_index(cpu_drcs[i]);
+
+		rc = -EINVAL;
+	} else {
+		rc = 0;
+	}
+
+	kfree(cpu_drcs);
+	return rc;
+}
+
 int dlpar_cpu(struct pseries_hp_errorlog *hp_elog)
 {
 	u32 count, drc_index;
@@ -698,6 +784,14 @@ int dlpar_cpu(struct pseries_hp_errorlog *hp_elog)
 		else
 			rc = -EINVAL;
 		break;
+	case PSERIES_HP_ELOG_ACTION_ADD:
+		if (hp_elog->id_type == PSERIES_HP_ELOG_ID_DRC_COUNT)
+			rc = dlpar_cpu_add_by_count(count);
+		else if (hp_elog->id_type == PSERIES_HP_ELOG_ID_DRC_INDEX)
+			rc = dlpar_cpu_add(drc_index);
+		else
+			rc = -EINVAL;
+		break;
 	default:
 		pr_err("Invalid action (%d) specified\n", hp_elog->action);
 		rc = -EINVAL;
#8 lore raw

Re: [PATCH v2 5/6] powerpc/pseries: Add CPU dlpar add functionality

Quoted 3 lines · Nathan Fontenot

Add the ability to hotplug add cpus via rtas hotplug events by either specifying the drc index of the CPU to add, or providing a count of the number of CPUs to add.

So I just tried running this on my system, without doing anything on the HMC.

I have:

  # lsprop /proc/device-tree/cpus/ibm,drc-indexes 
  /proc/device-tree/cpus/ibm,drc-indexes
  		 00000002 10000000 10000002

So I think that says I have two cpus.

The first one is already added:

/proc/device-tree/cpus/PowerPC,POWER6@0/ibm,my-drc-index
10000000 (268435456)

But can I add the second one?

  # echo "cpu add count 1" > dlpar 
  RTAS: event: 34, Type: Platform Error, Severity: 2
  pseries-hotplug-cpu: Failed to acquire DRC, rc: -5, drc index: 10000002
  pseries-hotplug-cpu: CPU hot-add failed, removing any added CPUs
  -bash: echo: write error: Invalid argument

Seems not. But I'm not clear why?

Adding by index doesn't work either:

  # echo "cpu add index 10000002" > dlpar
  pseries-hotplug-cpu: Failed to acquire DRC, rc: -1, drc index: 989682
  -bash: echo: write error: Invalid argument

That's a little confusing, drc index is hex obviously.

[root@p6-10-P5-E0 kernel]# echo "cpu add index 0x10000002" > dlpar
RTAS: event: 35, Type: Platform Error, Severity: 2
pseries-hotplug-cpu: Failed to acquire DRC, rc: -5, drc index: 10000002 -bash: echo: write error: Invalid argument

So that's probably all PEBKAC, but if we can make it more intutive that would be good.

cheers

#9 lore raw

Re: [PATCH v2 5/6] powerpc/pseries: Add CPU dlpar add functionality

Quoted 23 lines · Michael Ellerman
Quoted 3 lines · Nathan Fontenot

Add the ability to hotplug add cpus via rtas hotplug events by either specifying the drc index of the CPU to add, or providing a count of the number of CPUs to add.

So I just tried running this on my system, without doing anything on the HMC.

I have:

  # lsprop /proc/device-tree/cpus/ibm,drc-indexes 
  /proc/device-tree/cpus/ibm,drc-indexes
  		 00000002 10000000 10000002

So I think that says I have two cpus.

The first one is already added:

/proc/device-tree/cpus/PowerPC,POWER6@0/ibm,my-drc-index
10000000 (268435456)

But can I add the second one?

Quoted 8 lines
  # echo "cpu add count 1" > dlpar 
  RTAS: event: 34, Type: Platform Error, Severity: 2
  pseries-hotplug-cpu: Failed to acquire DRC, rc: -5, drc index: 10000002
  pseries-hotplug-cpu: CPU hot-add failed, removing any added CPUs
  -bash: echo: write error: Invalid argument

Seems not. But I'm not clear why?

The add request is failing because rtas call to acquire the resource is failing. I have seen this on many systems lately. Previously one could add and remove CPUs (and memory) by running the drmgr command on an LPAR manually and everything would work. It seems there are now systems (not sure if its a firmware or HMC update) that will only allow the rtas call to acquire the resource to succeed when the request is initiated from the HMC.

I can provide a patch for drmgr that uses the new sysfs interface so you can drive the dlpar operations from the hmc.

Quoted 9 lines

Adding by index doesn't work either:

  # echo "cpu add index 10000002" > dlpar
  pseries-hotplug-cpu: Failed to acquire DRC, rc: -1, drc index: 989682
  -bash: echo: write error: Invalid argument

That's a little confusing, drc index is hex obviously.

Quoted 7 lines

[root@p6-10-P5-E0 kernel]# echo "cpu add index 0x10000002" > dlpar
RTAS: event: 35, Type: Platform Error, Severity: 2
pseries-hotplug-cpu: Failed to acquire DRC, rc: -5, drc index: 10000002 -bash: echo: write error: Invalid argument

So that's probably all PEBKAC, but if we can make it more intutive that would be good.

I wrote the interface to take the drc index value in the format specified, so without the 0x it assumes decimal. Should be a better error message in this case.

-Nathan

#10 lore raw

PATCH v2 6/6 powerpc/pseries: Enable kernel CPU dlpar from sysfs

In reply to Nathan Fontenot

Enable new kernel cpu hotplug functionality by allowing cpu dlpar requests to be initiated from sysfs.

---
 arch/powerpc/platforms/pseries/dlpar.c |    6 ++++++
 1 file changed, 6 insertions(+)
arch/powerpc/platforms/pseries/dlpar.c +6 -0
index 438fdbd..2b93ae8 100644
@@ -356,6 +356,9 @@ static int handle_dlpar_errorlog(struct pseries_hp_errorlog *hp_elog)
 	case PSERIES_HP_ELOG_RESOURCE_MEM:
 		rc = dlpar_memory(hp_elog);
 		break;
+	case PSERIES_HP_ELOG_RESOURCE_CPU:
+		rc = dlpar_cpu(hp_elog);
+		break;
 	default:
 		pr_warn_ratelimited("Invalid resource (%d) specified\n",
 				    hp_elog->resource);
@@ -385,6 +388,9 @@ static ssize_t dlpar_store(struct class *class, struct class_attribute *attr,
 	if (!strncmp(arg, "memory", 6)) {
 		hp_elog->resource = PSERIES_HP_ELOG_RESOURCE_MEM;
 		arg += strlen("memory ");
+	} else if (!strncmp(arg, "cpu", 3)) {
+		hp_elog->resource = PSERIES_HP_ELOG_RESOURCE_CPU;
+		arg += strlen("cpu ");
 	} else {
 		pr_err("Invalid resource specified: \"%s\"\n", buf);
 		rc = -EINVAL;