[RFC PATCH 6/7]powerpc/powernv: add support to parse dt for nest pmu

Madhavan Srinivasan maddy at linux.vnet.ibm.com
Thu Mar 12 00:07:12 AEDT 2015


Nest counter unit informations are passed on as part of device-tree
to kernel. Here is the Device-tree layout.

DT root folder  /:
 /
 |
 -uncore@<chip-id>
   |
   -phb  <--dt node for pcie host bridge (uncore pmu type)
   -...
   -mcs  <--dt node for memory controller (uncore pmu type)
     |
     -mcs at 0   <-- dt node for memory controller sub units
     -mcs at 1
     -mcs at 2
     -mcs at 3
      |
      - mcs_read                <-- Sample event file
      - unit.mcs_read.unit      <-- Sample event unit file.
      - scale.mcs_read.scale    <-- Sample event scale file

Patch implements generic function to creates type structure and populate 
events and event related information from DT. For each type structure, 
new pmu is registered.

Signed-off-by: Madhavan Srinivasan <maddy at linux.vnet.ibm.com>
---
 arch/powerpc/perf/uncore_pmu_p8.c | 240 +++++++++++++++++++++++++++++++++++++-
 1 file changed, 238 insertions(+), 2 deletions(-)

diff --git a/arch/powerpc/perf/uncore_pmu_p8.c b/arch/powerpc/perf/uncore_pmu_p8.c
index 411c077..6a347d6 100644
--- a/arch/powerpc/perf/uncore_pmu_p8.c
+++ b/arch/powerpc/perf/uncore_pmu_p8.c
@@ -153,8 +153,246 @@ struct pmu p8_uncore_pmu = {
 	.read		= p8_uncore_perf_event_update,
 };
 
+/*
+ * Populate event name and string in attribute
+ */
+struct attribute *dev_str_attr(char *name, char *str)
+{
+	struct perf_pmu_events_attr *attr;
+
+	attr = kzalloc(sizeof(*attr), GFP_KERNEL);
+
+	attr->event_str = (const char *)str;
+	attr->attr.attr.name = name;
+	attr->attr.attr.mode = 0444;
+	attr->attr.show = perf_event_sysfs_show;
+
+	return &attr->attr.attr;
+}
+
+int update_uncore_unit_event_group(
+		struct ppc64_uncore_type *ptr)
+{
+	struct attribute_group *attr_group;
+	struct attribute **attrs;
+	struct ppc64_uncore_type_events *ev_arry = ptr->event_arry;
+	char *ev_val;
+	int i, j;
+
+	/*
+	 * Count the total # of events for this uncore pmu
+	 */
+	for (j = 0; ev_arry[0].ev_offset[j] != 0; j++)
+		;
+
+	attr_group = kzalloc(sizeof(struct attribute *) * (j) +
+		sizeof(*attr_group), GFP_KERNEL);
+	if (!attr_group)
+		return -ENOMEM;
+
+	attrs = (struct attribute **)(attr_group + 1);
+	attr_group->name = "events";
+	attr_group->attrs = attrs;
+
+	for (i = 0; i < j; i++) {
+		if (ev_arry[0].ev_offset[i] < 0) {
+			attrs[i] = dev_str_attr((char *)ev_arry[0].ev_name[i], (char *)ev_arry[0].ev_value[i]);
+		} else {
+			ev_val = kzalloc(MAX_PMU_NAME_LEN, GFP_KERNEL);
+			if (!ev_val)
+				return -ENOMEM;
+			sprintf((char *)ev_val, "event=0x%02x", (i+1));
+			attrs[i] = dev_str_attr((char *)ev_arry[0].ev_name[i], ev_val);
+		}
+	}
+
+	ptr->events_group = attr_group;
+	return 0;
+}
+
+/*
+ * Parse the child node and identify events associated
+ * with this pmu.
+ */
+static int populate_events(struct device_node *dev,
+			struct ppc64_uncore_type *ptr, int idx)
+{
+	struct ppc64_uncore_type_events *ev_arry = ptr->event_arry;
+	struct property *pp;
+	int ev_idx = 0, len = 0;
+	const char *start, *end, *out_str;
+	char *buf;
+	const __be32 *lval;
+	u32 val;
+
+	/*
+	 * Loop through each property
+	 */
+	for_each_property_of_node(dev, pp) {
+		start = pp->name;
+		end = start + strlen(start);
+		len = strlen(start);
+
+		/* Skip these, we dont need it */
+		if (!strcmp(pp->name, "name") ||
+		    !strcmp(pp->name, "phandle") ||
+		    !strcmp(pp->name, "ibm,dev-id") ||
+		    !strcmp(pp->name, "linux,phandle"))
+			continue;
+
+		buf = kzalloc(MAX_PMU_NAME_LEN, GFP_KERNEL);
+		if (!buf)
+			return -ENOMEM;
+
+		/*
+		 * Event may have corresponding unit and scale
+		 * information. Incase of unit and scale, populate
+		 * the offset value to negative.
+		 */
+		if (strncmp(pp->name, "unit.", 5) == 0) {
+			of_property_read_string(dev, pp->name, &out_str);
+			start += 5;
+			len = strlen(start);
+			strncpy(buf, start, strlen(start));
+			of_property_read_string(dev, pp->value, &out_str);
+
+			ev_arry[idx].ev_offset[ev_idx] = -1;
+			ev_arry[idx].ev_name[ev_idx] = buf;
+			ev_arry[idx].ev_value[ev_idx] = out_str;
+			ev_idx++;
+			continue;
+		}
+
+		if (strncmp(pp->name, "scale.", 6) == 0) {
+			of_property_read_string(dev, pp->name, &out_str);
+			start += 6;
+			len = strlen(start);
+			strncpy(buf, start, strlen(start));
+			of_property_read_string(dev, pp->value, &out_str);
+
+			ev_arry[idx].ev_offset[ev_idx] = -1;
+			ev_arry[idx].ev_name[ev_idx] = buf;
+			ev_arry[idx].ev_value[ev_idx] = out_str;
+			ev_idx++;
+			continue;
+		}
+
+		/*
+		 * Event support by this pmu.
+		 */
+		lval = of_get_property(dev, pp->name, NULL);
+		val = (uint32_t)be32_to_cpup(lval);
+		strncpy(buf, start, len);
+		ev_arry[idx].ev_offset[ev_idx] = val;
+		ev_arry[idx].ev_name[ev_idx] = buf;
+		ev_arry[idx].ev_value[ev_idx] = "\0";
+		ev_idx++;
+	}
+	return 0;
+}
+
+static int uncore_unit_parse(struct device_node *parent,
+				struct ppc64_uncore_type *ptr)
+{
+	struct device_node *child;
+	const __be32 *unit_idx;
+	int idx;
+
+	for_each_available_child_of_node(parent, child) {
+		unit_idx = of_get_property(child, "ibm,dev-id", NULL);
+		idx = (uint32_t)be32_to_cpup(unit_idx);
+		if (populate_events(child, ptr, idx))
+			return -1;
+	}
+
+	if (update_uncore_unit_event_group(ptr))
+		return -1;
+
+	return 0;
+}
+
+static int uncore_create_types(struct device_node *parent)
+{
+	struct device_node *dev_type;
+	struct ppc64_uncore_type *uncore_type;
+	const __be32 *uncore_type_units;
+	int idx = 0, i;
+
+	for_each_child_of_node(parent, dev_type) {
+		uncore_type = (struct ppc64_uncore_type *)
+				kzalloc(sizeof(struct ppc64_uncore_type),
+					GFP_KERNEL);
+		if (!uncore_type)
+			goto fail;
+
+		/*
+		 * Populate the uncore pmu type name and
+		 * # of sub units.
+		 */
+		uncore_type->name = of_get_property(dev_type,
+					"name", NULL);
+		uncore_type_units = of_get_property(dev_type,
+					"sub_units", NULL);
+		uncore_type->num_boxes = be32_to_cpup(uncore_type_units);
+
+		if (uncore_unit_parse(dev_type, uncore_type))
+			goto fail;
+
+		uncore_type->format_group = &p8_uncore_format_group;
+		uncore_type->pmu = &p8_uncore_pmu;
+
+		p8_uncore[idx++] = uncore_type;
+	}
+
+	return 0;
+
+fail:
+	for (i = 0; i < idx; i++)
+		kfree(p8_uncore[i]);
+	return -1;
+}
+
 static int uncore_init(void)
 {
+	struct device_node *dev;
+	const __be32 *gcid;
+	const __be64 *chip_ima_reg;
+	const __be32 *chip_ima_size;
+	int idx = 0;
+
+	dev = of_find_node_with_property(NULL, "ibm,uncore");
+	if (!dev)
+		return -EINVAL;
+
+	for_each_node_with_property(dev, "ibm,uncore") {
+		gcid = of_get_property(dev, "ibm,chip-id", NULL);
+		chip_ima_reg = of_get_property(dev, "reg", NULL);
+		chip_ima_size = of_get_property(dev, "size", NULL);
+		if ((!gcid) || (!chip_ima_reg) || (!chip_ima_size)) {
+			pr_err("%s: device %s missing property \n",
+				__func__, dev->full_name);
+			return -EINVAL;
+		}
+
+		/*
+		 * Get the chip id, populate
+		 * address range/size and translate it.
+		 */
+		idx = (uint32_t)be32_to_cpup(gcid);
+		uncore_per_chip[idx].chip_id = (uint32_t)be32_to_cpup(gcid);
+		uncore_per_chip[idx].preg_base = be64_to_cpup(chip_ima_reg);
+		uncore_per_chip[idx].size = be32_to_cpup(chip_ima_size);
+		uncore_per_chip[idx].vreg_base = (u64)ioremap(
+			(phys_addr_t)uncore_per_chip[idx].preg_base,
+				uncore_per_chip[idx].size);
+
+		/*
+		 * looks for uncore pmu types supported here.
+		 */
+		if (uncore_create_types(dev))
+			return -EINVAL;
+	}
+
 	return 0;
 }
 
@@ -163,5 +401,3 @@ int uncore_p8_init(void)
 	ppc64_uncore = p8_uncore;
 	return uncore_init();
 }
-
-
-- 
1.9.1



More information about the Linuxppc-dev mailing list