From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1756998AbcBWD5J (ORCPT ); Mon, 22 Feb 2016 22:57:09 -0500 Received: from e28smtp05.in.ibm.com ([125.16.236.5]:44845 "EHLO e28smtp05.in.ibm.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1756896AbcBWD5G (ORCPT ); Mon, 22 Feb 2016 22:57:06 -0500 X-IBM-Helo: d28relay01.in.ibm.com X-IBM-MailFrom: maddy@linux.vnet.ibm.com X-IBM-RcptTo: linux-kernel@vger.kernel.org From: Madhavan Srinivasan To: linux-kernel@vger.kernel.org Cc: Madhavan Srinivasan , Michael Ellerman , Benjamin Herrenschmidt , Paul Mackerras , Anton Blanchard , Daniel Axtens , Stephane Eranian , Sukadev Bhattiprolu Subject: [PATCH v8 4/7] powerpc/powernv: detect supported nest units and its events Date: Tue, 23 Feb 2016 09:16:35 +0530 Message-Id: <1456199198-11056-5-git-send-email-maddy@linux.vnet.ibm.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1456199198-11056-1-git-send-email-maddy@linux.vnet.ibm.com> References: <1456199198-11056-1-git-send-email-maddy@linux.vnet.ibm.com> X-TM-AS-MML: disable x-cbid: 16022303-0017-0000-0000-00000A326CE8 Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Parse device tree to detect nest units. Traverse through each nest unit folder to find supported events and corresponding unit/scale files (if any). The nest unit event file from Device Tree will contain the offset in the reserved memory region to get the counter data for a given event. Kernel code uses this offset as event configuration value. Device tree parser code also looks for scale/unit in the file name and passes on the file as an event attr for perf tool to use in the post processing. Cc: Michael Ellerman Cc: Benjamin Herrenschmidt Cc: Paul Mackerras Cc: Anton Blanchard Cc: Daniel Axtens Cc: Stephane Eranian Cc: Sukadev Bhattiprolu Signed-off-by: Madhavan Srinivasan --- arch/powerpc/platforms/powernv/opal-nest.c | 190 ++++++++++++++++++++++++++++- 1 file changed, 189 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/platforms/powernv/opal-nest.c b/arch/powerpc/platforms/powernv/opal-nest.c index 2ca879fdf159..548a8c0236e4 100644 --- a/arch/powerpc/platforms/powernv/opal-nest.c +++ b/arch/powerpc/platforms/powernv/opal-nest.c @@ -31,13 +31,194 @@ #include extern struct perchip_nest_info nest_perchip_info[NEST_MAX_CHIPS]; +extern struct nest_pmu *per_nest_pmu_arr[NEST_MAX_PMUS]; + +static int nest_event_info(char *name, struct nest_ima_events *nest_events) +{ + char *buf; + + /* memory for content */ + buf = kzalloc(NEST_MAX_PMU_NAME_LEN, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + nest_events->ev_name = name; + nest_events->ev_value = buf; + return 0; +} + +static int nest_event_info_str(struct property *pp, char *name, + struct nest_ima_events *nest_events) +{ + if (nest_event_info(name, nest_events)) + return -ENOMEM; + + if (!pp->value || (strnlen(pp->value, pp->length) == pp->length) || + (pp->length > NEST_MAX_PMU_NAME_LEN)) + return -EINVAL; + + strncpy(nest_events->ev_value, (const char *)pp->value, pp->length); + return 0; +} + +static int nest_event_info_val(char *name, u32 val, + struct nest_ima_events *nest_events) +{ + if (nest_event_info(name, nest_events)) + return -ENOMEM; + + sprintf(nest_events->ev_value, "event=0x%x", val); + return 0; +} + +static int nest_events_node_parser(struct device_node *dev, + struct nest_ima_events *nest_events) +{ + struct property *name, *pp, *id; + char *buf, *start, *ev_name; + u32 val; + int idx = 0, ret; + + if (!dev) + return -EINVAL; + + /* + * Loop through each property + */ + name = of_find_property(dev, "name", NULL); + if (!name) { + printk(KERN_INFO "No property by name\n"); + return -1; + } + + if (!name->value || + (strnlen(name->value, name->length) == name->length) || + (name->length > NEST_MAX_PMU_NAME_LEN)) + return -EINVAL; + + ev_name = kzalloc(NEST_MAX_PMU_NAME_LEN, GFP_KERNEL); + if (!ev_name) + return -ENOMEM; + + /* Now that we got the event name, look for id */ + id = of_find_property(dev, "id", NULL); + if (!id) { + strncpy(ev_name, name->value, (int)strlen(name->value)); + printk(KERN_INFO "No property by id = %s\n", ev_name); + } else { + if (!id->value || + (strnlen(id->value, id->length) == id->length) || + (id->length > NEST_MAX_PMU_NAME_LEN)) + return -EINVAL; + + of_property_read_u32(dev, id->name, &val); + sprintf(ev_name, "%s_%x", (char *)name->value, val); + } + + for_each_property_of_node(dev, pp) { + start = pp->name; + + /* Skip these, we don't need it */ + if (!strcmp(pp->name, "phandle") || + !strcmp(pp->name, "linux,phandle") || + !strcmp(pp->name, "name")) + continue; + + if (strncmp(pp->name, "reg", 3) == 0) { + of_property_read_u32(dev, pp->name, &val); + ret = nest_event_info_val(ev_name, val, &nest_events[idx]); + idx++; + } else if (strncmp(pp->name, "unit", 4) == 0) { + buf = kzalloc(NEST_MAX_PMU_NAME_LEN, GFP_KERNEL); + if (!buf) + return -ENOMEM; + sprintf(buf,"%s.unit", ev_name); + ret = nest_event_info_str(pp, buf, &nest_events[idx]); + idx++; + } else if (strncmp(pp->name, "scale", 5) == 0) { + buf = kzalloc(NEST_MAX_PMU_NAME_LEN, GFP_KERNEL); + if (!buf) + return -ENOMEM; + sprintf(buf,"%s.scale", ev_name); + ret = nest_event_info_str(pp, buf, &nest_events[idx]); + idx++; + } + + if (ret) + return ret; + } + + return idx; +} + +static int nest_pmu_create(struct device_node *parent, int pmu_index) +{ + struct device_node *ev_node; + struct nest_ima_events *nest_events; + struct nest_pmu *pmu_ptr; + struct property *pp; + char *buf; + int idx = 0, ret; + + if (!parent) + return -EINVAL; + + /* memory for nest pmus */ + pmu_ptr = kzalloc(sizeof(struct nest_pmu), GFP_KERNEL); + if (!pmu_ptr) + return -ENOMEM; + + /* Needed for hotplug/migration */ + per_nest_pmu_arr[pmu_index] = pmu_ptr; + + /* memory for nest pmu events */ + nest_events = kzalloc((sizeof(struct nest_ima_events) * + NEST_MAX_EVENTS_SUPPORTED), GFP_KERNEL); + if (!nest_events) + return -ENOMEM; + + pp = of_find_property(parent, "name", NULL); + if (!pp) { + printk(KERN_INFO "No property by name\n"); + return -1; + } + + if (!pp->value || + (strnlen(pp->value, pp->length) == pp->length) || + (pp->length > NEST_MAX_PMU_NAME_LEN)) + return -EINVAL; + + buf = kzalloc(NEST_MAX_PMU_NAME_LEN, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + /* Save the name to register it later */ + sprintf(buf, "nest_%s", (char *)pp->value); + pmu_ptr->pmu.name = (char *)buf; + + /* Loop through event nodes */ + for_each_child_of_node(parent, ev_node) { + ret = nest_events_node_parser(ev_node, &nest_events[idx]); + if (ret < 0) + return -1; + + /* + * nest_event_node_parser will return number of + * event entried created for this. This could include + * event scale and unit files also. + */ + idx += ret; + } + + return 0; +} static int opal_nest_counters_probe(struct platform_device *pdev) { struct device_node *child, *parent; struct perchip_nest_info *pcni; u32 idx, range[4], pages; - int rc=0, i=0; + int rc=0, i=0, pmu_count=0; if (!pdev || !pdev->dev.of_node) return -ENODEV; @@ -75,6 +256,13 @@ static int opal_nest_counters_probe(struct platform_device *pdev) } while( i < (pcni->size/PAGE_SIZE)); } + parent = of_get_next_child(pdev->dev.of_node, NULL); + for_each_child_of_node(parent, child) { + rc = nest_pmu_create(child, pmu_count++); + if (rc) + return rc; + } + return rc; } -- 1.9.1