[RFC PATCH 6/7]powerpc/powernv: add support to parse dt for nest pmu
From: Madhavan Srinivasan
Date: Wed Mar 11 2015 - 09:10:10 EST
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@0 <-- dt node for memory controller sub units
-mcs@1
-mcs@2
-mcs@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@xxxxxxxxxxxxxxxxxx>
---
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
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/