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 <ma...@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 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/