Commit b015f6bc authored by Tyrel Datwyler's avatar Tyrel Datwyler Committed by Michael Ellerman
Browse files

powerpc/pseries: Add cpu DLPAR support for drc-info property



Older firmwares provided information about Dynamic Reconfig
Connectors (DRC) through several device tree properties, namely
ibm,drc-types, ibm,drc-indexes, ibm,drc-names, and
ibm,drc-power-domains. New firmwares have the ability to present this
same information in a much condensed format through a device tree
property called ibm,drc-info.

The existing cpu DLPAR hotplug code only understands the older DRC
property format when validating the drc-index of a cpu during a
hotplug add. This updates those code paths to use the ibm,drc-info
property, when present, instead for validation.

Signed-off-by: default avatarTyrel Datwyler <tyreld@linux.ibm.com>
Signed-off-by: default avatarMichael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/1573449697-5448-4-git-send-email-tyreld@linux.ibm.com
parent 775fa495
Loading
Loading
Loading
Loading
+112 −15
Original line number Diff line number Diff line
@@ -465,17 +465,67 @@ static bool dlpar_cpu_exists(struct device_node *parent, u32 drc_index)
	return found;
}

static bool drc_info_valid_index(struct device_node *parent, u32 drc_index)
{
	struct property *info;
	struct of_drc_info drc;
	const __be32 *value;
	u32 index;
	int count, i, j;

	info = of_find_property(parent, "ibm,drc-info", NULL);
	if (!info)
		return false;

	value = of_prop_next_u32(info, NULL, &count);

	/* First value of ibm,drc-info is number of drc-info records */
	if (value)
		value++;
	else
		return false;

	for (i = 0; i < count; i++) {
		if (of_read_drc_info_cell(&info, &value, &drc))
			return false;

		if (strncmp(drc.drc_type, "CPU", 3))
			break;

		if (drc_index > drc.last_drc_index)
			continue;

		index = drc.drc_index_start;
		for (j = 0; j < drc.num_sequential_elems; j++) {
			if (drc_index == index)
				return true;

			index += drc.sequential_inc;
		}
	}

	return false;
}

static bool valid_cpu_drc_index(struct device_node *parent, u32 drc_index)
{
	bool found = false;
	int rc, index;

	index = 0;
	if (of_find_property(parent, "ibm,drc-info", NULL))
		return drc_info_valid_index(parent, drc_index);

	/* 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.
	 */
	index = 1;
	while (!found) {
		u32 drc;

		rc = of_property_read_u32_index(parent, "ibm,drc-indexes",
						index++, &drc);

		if (rc)
			break;

@@ -718,19 +768,52 @@ static int dlpar_cpu_remove_by_count(u32 cpus_to_remove)
	return rc;
}

static int find_dlpar_cpus_to_add(u32 *cpu_drcs, u32 cpus_to_add)
static int find_drc_info_cpus_to_add(struct device_node *cpus,
				     struct property *info,
				     u32 *cpu_drcs, u32 cpus_to_add)
{
	struct device_node *parent;
	struct of_drc_info drc;
	const __be32 *value;
	u32 count, drc_index;
	int cpus_found = 0;
	int index, rc;
	int i, j;

	parent = of_find_node_by_path("/cpus");
	if (!parent) {
		pr_warn("Could not find CPU root node in device tree\n");
		kfree(cpu_drcs);
	if (!info)
		return -1;

	value = of_prop_next_u32(info, NULL, &count);
	if (value)
		value++;

	for (i = 0; i < count; i++) {
		of_read_drc_info_cell(&info, &value, &drc);
		if (strncmp(drc.drc_type, "CPU", 3))
			break;

		drc_index = drc.drc_index_start;
		for (j = 0; j < drc.num_sequential_elems; j++) {
			if (dlpar_cpu_exists(cpus, drc_index))
				continue;

			cpu_drcs[cpus_found++] = drc_index;

			if (cpus_found == cpus_to_add)
				return cpus_found;

			drc_index += drc.sequential_inc;
		}
	}

	return cpus_found;
}

static int find_drc_index_cpus_to_add(struct device_node *cpus,
				      u32 *cpu_drcs, u32 cpus_to_add)
{
	int cpus_found = 0;
	int index, rc;
	u32 drc_index;

	/* 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
@@ -738,25 +821,25 @@ static int find_dlpar_cpus_to_add(u32 *cpu_drcs, u32 cpus_to_add)
	 */
	index = 1;
	while (cpus_found < cpus_to_add) {
		u32 drc;
		rc = of_property_read_u32_index(cpus, "ibm,drc-indexes",
						index++, &drc_index);

		rc = of_property_read_u32_index(parent, "ibm,drc-indexes",
						index++, &drc);
		if (rc)
			break;

		if (dlpar_cpu_exists(parent, drc))
		if (dlpar_cpu_exists(cpus, drc_index))
			continue;

		cpu_drcs[cpus_found++] = drc;
		cpu_drcs[cpus_found++] = drc_index;
	}

	of_node_put(parent);
	return cpus_found;
}

static int dlpar_cpu_add_by_count(u32 cpus_to_add)
{
	struct device_node *parent;
	struct property *info;
	u32 *cpu_drcs;
	int cpus_added = 0;
	int cpus_found;
@@ -768,7 +851,21 @@ static int dlpar_cpu_add_by_count(u32 cpus_to_add)
	if (!cpu_drcs)
		return -EINVAL;

	cpus_found = find_dlpar_cpus_to_add(cpu_drcs, cpus_to_add);
	parent = of_find_node_by_path("/cpus");
	if (!parent) {
		pr_warn("Could not find CPU root node in device tree\n");
		kfree(cpu_drcs);
		return -1;
	}

	info = of_find_property(parent, "ibm,drc-info", NULL);
	if (info)
		cpus_found = find_drc_info_cpus_to_add(parent, info, cpu_drcs, cpus_to_add);
	else
		cpus_found = find_drc_index_cpus_to_add(parent, cpu_drcs, cpus_to_add);

	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);