Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754743AbcKCHXd (ORCPT ); Thu, 3 Nov 2016 03:23:33 -0400 Received: from mx0a-001b2d01.pphosted.com ([148.163.156.1]:44184 "EHLO mx0a-001b2d01.pphosted.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754355AbcKCHXZ (ORCPT ); Thu, 3 Nov 2016 03:23:25 -0400 From: Hemant Kumar To: linux-kernel@vger.kernel.org Cc: maddy@linux.vnet.ibm.com, mpe@ellerman.id.au, benh@kernel.crashing.org, paulus@samba.org, anton@samba.org, sukadev@linux.vnet.ibm.com, mikey@neuling.org, stewart@linux.vnet.ibm.com, eranian@google.com, Hemant Kumar Subject: [PATCH 5/6] powerpc/perf: Generic ima pmu event functions Date: Thu, 3 Nov 2016 12:52:34 +0530 X-Mailer: git-send-email 2.7.4 In-Reply-To: <1478157755-14636-1-git-send-email-hemant@linux.vnet.ibm.com> References: <1478157755-14636-1-git-send-email-hemant@linux.vnet.ibm.com> X-TM-AS-MML: disable X-Content-Scanned: Fidelis XPS MAILER x-cbid: 16110307-0004-0000-0000-000001AEB55D X-IBM-AV-DETECTION: SAVI=unused REMOTE=unused XFE=unused x-cbparentid: 16110307-0005-0000-0000-00000901AD10 Message-Id: <1478157755-14636-6-git-send-email-hemant@linux.vnet.ibm.com> X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10432:,, definitions=2016-11-03_02:,, signatures=0 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 spamscore=0 suspectscore=4 malwarescore=0 phishscore=0 adultscore=0 bulkscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1609300000 definitions=main-1611030137 Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 8210 Lines: 268 Since, the IMA counters' data are periodically fed to a memory location, the functions to read/update, start/stop, add/del can be generic and can be used by all IMA PMU units. This patch adds a set of generic ima pmu related event functions to be used by each ima pmu unit. Add code to setup format attribute and to register ima pmus. Add an event_init function for nest_ima events. Cc: Madhavan Srinivasan Cc: Michael Ellerman Cc: Benjamin Herrenschmidt Cc: Paul Mackerras Cc: Anton Blanchard Cc: Sukadev Bhattiprolu Cc: Michael Neuling Cc: Stewart Smith Cc: Stephane Eranian Signed-off-by: Hemant Kumar --- arch/powerpc/include/asm/ima-pmu.h | 2 + arch/powerpc/perf/ima-pmu.c | 122 ++++++++++++++++++++++++++++++ arch/powerpc/platforms/powernv/opal-ima.c | 37 +++++++-- 3 files changed, 154 insertions(+), 7 deletions(-) diff --git a/arch/powerpc/include/asm/ima-pmu.h b/arch/powerpc/include/asm/ima-pmu.h index 0ed8886..f0d95bb 100644 --- a/arch/powerpc/include/asm/ima-pmu.h +++ b/arch/powerpc/include/asm/ima-pmu.h @@ -70,4 +70,6 @@ struct ima_pmu { #define UNKNOWN_DOMAIN -1 +int ima_get_domain(struct device_node *pmu_dev); + #endif /* PPC_POWERNV_IMA_PMU_DEF_H */ diff --git a/arch/powerpc/perf/ima-pmu.c b/arch/powerpc/perf/ima-pmu.c index 50d2226..9948636 100644 --- a/arch/powerpc/perf/ima-pmu.c +++ b/arch/powerpc/perf/ima-pmu.c @@ -17,6 +17,117 @@ struct perchip_nest_info nest_perchip_info[IMA_MAX_CHIPS]; struct ima_pmu *per_nest_pmu_arr[IMA_MAX_PMUS]; +/* Needed for sanity check */ +extern u64 nest_max_offset; + +PMU_FORMAT_ATTR(event, "config:0-20"); +static struct attribute *ima_format_attrs[] = { + &format_attr_event.attr, + NULL, +}; + +static struct attribute_group ima_format_group = { + .name = "format", + .attrs = ima_format_attrs, +}; + +static int nest_ima_event_init(struct perf_event *event) +{ + int chip_id; + u32 config = event->attr.config; + struct perchip_nest_info *pcni; + + if (event->attr.type != event->pmu->type) + return -ENOENT; + + /* Sampling not supported */ + if (event->hw.sample_period) + return -EINVAL; + + /* unsupported modes and filters */ + if (event->attr.exclude_user || + event->attr.exclude_kernel || + event->attr.exclude_hv || + event->attr.exclude_idle || + event->attr.exclude_host || + event->attr.exclude_guest) + return -EINVAL; + + if (event->cpu < 0) + return -EINVAL; + + /* Sanity check for config (event offset) */ + if (config > nest_max_offset) + return -EINVAL; + + chip_id = topology_physical_package_id(event->cpu); + pcni = &nest_perchip_info[chip_id]; + event->hw.event_base = pcni->vbase[config/PAGE_SIZE] + + (config & ~PAGE_MASK); + + return 0; +} + +static void ima_read_counter(struct perf_event *event) +{ + u64 *addr, data; + + addr = (u64 *)event->hw.event_base; + data = __be64_to_cpu(*addr); + local64_set(&event->hw.prev_count, data); +} + +static void ima_perf_event_update(struct perf_event *event) +{ + u64 counter_prev, counter_new, final_count, *addr; + + addr = (u64 *)event->hw.event_base; + counter_prev = local64_read(&event->hw.prev_count); + counter_new = __be64_to_cpu(*addr); + final_count = counter_new - counter_prev; + + local64_set(&event->hw.prev_count, counter_new); + local64_add(final_count, &event->count); +} + +static void ima_event_start(struct perf_event *event, int flags) +{ + ima_read_counter(event); +} + +static void ima_event_stop(struct perf_event *event, int flags) +{ + if (flags & PERF_EF_UPDATE) + ima_perf_event_update(event); +} + +static int ima_event_add(struct perf_event *event, int flags) +{ + if (flags & PERF_EF_START) + ima_event_start(event, flags); + + return 0; +} + +/* update_pmu_ops : Populate the appropriate operations for "pmu" */ +static int update_pmu_ops(struct ima_pmu *pmu) +{ + if (!pmu) + return -EINVAL; + + pmu->pmu.task_ctx_nr = perf_invalid_context; + pmu->pmu.event_init = nest_ima_event_init; + pmu->pmu.add = ima_event_add; + pmu->pmu.del = ima_event_stop; + pmu->pmu.start = ima_event_start; + pmu->pmu.stop = ima_event_stop; + pmu->pmu.read = ima_perf_event_update; + pmu->attr_groups[1] = &ima_format_group; + pmu->pmu.attr_groups = pmu->attr_groups; + + return 0; +} + /* dev_str_attr : Populate event "name" and string "str" in attribute */ static struct attribute *dev_str_attr(const char *name, const char *str) { @@ -83,6 +194,17 @@ int init_ima_pmu(struct ima_events *events, int idx, if (ret) goto err_free; + ret = update_pmu_ops(pmu_ptr); + if (ret) + goto err_free; + + ret = perf_pmu_register(&pmu_ptr->pmu, pmu_ptr->pmu.name, -1); + if (ret) + goto err_free; + + pr_info("%s performance monitor hardware support registered\n", + pmu_ptr->pmu.name); + return 0; err_free: diff --git a/arch/powerpc/platforms/powernv/opal-ima.c b/arch/powerpc/platforms/powernv/opal-ima.c index a3e8b86..2e8ce3b 100644 --- a/arch/powerpc/platforms/powernv/opal-ima.c +++ b/arch/powerpc/platforms/powernv/opal-ima.c @@ -34,6 +34,8 @@ extern struct perchip_nest_info nest_perchip_info[IMA_MAX_CHIPS]; extern struct ima_pmu *per_nest_pmu_arr[IMA_MAX_PMUS]; +u64 nest_max_offset; + extern int init_ima_pmu(struct ima_events *events, int idx, struct ima_pmu *pmu_ptr); @@ -68,8 +70,25 @@ static int ima_event_info_str(struct property *pp, char *name, return 0; } +/* + * Updates the maximum offset for an event in the pmu with domain + * "pmu_domain". Right now, only nest domain is supported. + */ +static void update_max_value(u32 value, int pmu_domain) +{ + switch (pmu_domain) { + case IMA_DOMAIN_NEST: + if (nest_max_offset < value) + nest_max_offset = value; + break; + default: + /* Unknown domain, return */ + return; + } +} + static int ima_event_info_val(char *name, u32 val, - struct ima_events *events) + struct ima_events *events, int pmu_domain) { int ret; @@ -77,6 +96,7 @@ static int ima_event_info_val(char *name, u32 val, if (ret) return ret; sprintf(events->ev_value, "event=0x%x", val); + update_max_value(val, pmu_domain); return 0; } @@ -89,7 +109,7 @@ static int ima_event_info_val(char *name, u32 val, * Also, parse the "scale" and "unit" properties, if any. */ static int ima_events_node_parser(struct device_node *dev, - struct ima_events *events) + struct ima_events *events, int pmu_domain) { struct property *name, *pp; char *buf, *ev_name; @@ -131,7 +151,8 @@ static int ima_events_node_parser(struct device_node *dev, */ if (strncmp(pp->name, "reg", 3) == 0) { of_property_read_u32(dev, pp->name, &val); - ret = ima_event_info_val(ev_name, val, &events[idx]); + ret = ima_event_info_val(ev_name, val, &events[idx], + pmu_domain); if (ret) { kfree(events[idx].ev_name); kfree(events[idx].ev_value); @@ -218,9 +239,10 @@ static void ima_free_events(struct ima_events *events, int nr_entries) /* * ima_pmu_create : Takes the parent device which is the pmu unit and a * pmu_index as the inputs. - * Allocates memory for the pmu, sets up its domain (NEST or CORE), and - * allocates memory for the events supported by this pmu. Assigns a name for - * the pmu. Calls ima_events_node_parser() to setup the individual events. + * Allocates memory for the pmu, sets up its domain (right now, only NEST), + * and allocates memory for the events supported by this pmu. Assigns a name + * for the pmu. Calls ima_events_node_parser() to setup the individual + * events. * If everything goes fine, it calls, init_ima_pmu() to setup the pmu device * and register it. */ @@ -289,7 +311,8 @@ static int ima_pmu_create(struct device_node *parent, int pmu_index) /* Loop through event nodes */ for_each_child_of_node(parent, ev_node) { - ret = ima_events_node_parser(ev_node, &events[idx]); + ret = ima_events_node_parser(ev_node, &events[idx], + pmu_ptr->domain); if (ret < 0) { /* Unable to parse this event */ if (ret == -ENOMEM) -- 2.7.4