From patchwork Thu Mar 16 07:35:04 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: maddy X-Patchwork-Id: 739664 Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3vkLK03NFyz9rxw for ; Thu, 16 Mar 2017 18:51:48 +1100 (AEDT) Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) by lists.ozlabs.org (Postfix) with ESMTP id 3vkLK02XlGzDrHL for ; Thu, 16 Mar 2017 18:51:48 +1100 (AEDT) X-Original-To: linuxppc-dev@lists.ozlabs.org Delivered-To: linuxppc-dev@lists.ozlabs.org Received: from mx0a-001b2d01.pphosted.com (mx0b-001b2d01.pphosted.com [148.163.158.5]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 3vkL0R0GYszDqYy for ; Thu, 16 Mar 2017 18:37:26 +1100 (AEDT) Received: from pps.filterd (m0098417.ppops.net [127.0.0.1]) by mx0a-001b2d01.pphosted.com (8.16.0.20/8.16.0.20) with SMTP id v2G7SdHl105278 for ; Thu, 16 Mar 2017 03:37:18 -0400 Received: from e23smtp02.au.ibm.com (e23smtp02.au.ibm.com [202.81.31.144]) by mx0a-001b2d01.pphosted.com with ESMTP id 297bu78meu-1 (version=TLSv1.2 cipher=AES256-SHA bits=256 verify=NOT) for ; Thu, 16 Mar 2017 03:37:18 -0400 Received: from localhost by e23smtp02.au.ibm.com with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted for from ; Thu, 16 Mar 2017 17:37:15 +1000 Received: from d23relay10.au.ibm.com (202.81.31.229) by e23smtp02.au.ibm.com (202.81.31.208) with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted; Thu, 16 Mar 2017 17:37:13 +1000 Received: from d23av04.au.ibm.com (d23av04.au.ibm.com [9.190.235.139]) by d23relay10.au.ibm.com (8.14.9/8.14.9/NCO v10.0) with ESMTP id v2G7b53l53149764 for ; Thu, 16 Mar 2017 18:37:13 +1100 Received: from d23av04.au.ibm.com (localhost [127.0.0.1]) by d23av04.au.ibm.com (8.14.4/8.14.4/NCO v10.0 AVout) with ESMTP id v2G7adUR010665 for ; Thu, 16 Mar 2017 18:36:40 +1100 Received: from SrihariSrinidhi.in.ibm.com ([9.77.193.245]) by d23av04.au.ibm.com (8.14.4/8.14.4/NCO v10.0 AVin) with ESMTP id v2G7ZTYk008432; Thu, 16 Mar 2017 18:36:34 +1100 From: Madhavan Srinivasan To: mpe@ellerman.id.au Subject: [PATCH v5 10/13] powerpc/perf: Thread IMC PMU functions Date: Thu, 16 Mar 2017 13:05:04 +0530 X-Mailer: git-send-email 2.7.4 In-Reply-To: <1489649707-8021-1-git-send-email-maddy@linux.vnet.ibm.com> References: <1489649707-8021-1-git-send-email-maddy@linux.vnet.ibm.com> X-TM-AS-MML: disable x-cbid: 17031607-0004-0000-0000-000001EEA28B X-IBM-AV-DETECTION: SAVI=unused REMOTE=unused XFE=unused x-cbparentid: 17031607-0005-0000-0000-000009DE8AA0 Message-Id: <1489649707-8021-11-git-send-email-maddy@linux.vnet.ibm.com> X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10432:, , definitions=2017-03-16_06:, , signatures=0 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 spamscore=0 suspectscore=3 malwarescore=0 phishscore=0 adultscore=0 bulkscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1702020001 definitions=main-1703160061 X-BeenThere: linuxppc-dev@lists.ozlabs.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: Linux on PowerPC Developers Mail List List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Stewart Smith , "Gautham R . Shenoy" , Michael Neuling , Madhavan Srinivasan , linuxppc-dev@lists.ozlabs.org, Stephane Eranian , linux-kernel@vger.kernel.org, Anju T Sudhakar , Paul Mackerras , Anton Blanchard , Sukadev Bhattiprolu , Hemant Kumar , Daniel Axtens Errors-To: linuxppc-dev-bounces+patchwork-incoming=ozlabs.org@lists.ozlabs.org Sender: "Linuxppc-dev" From: Hemant Kumar This patch adds the PMU functions required for event initialization, read, update, add, del etc. for thread IMC PMU. Thread IMC PMUs are used for per-task monitoring. These PMUs don't need any hotplugging support. For each CPU, a page of memory is allocated and is kept static i.e., these pages will exist till the machine shuts down. The base address of this page is assigned to the ldbar of that cpu. As soon as we do that, the thread IMC counters start running for that cpu and the data of these counters are assigned to the page allocated. But we use this for per-task monitoring. Whenever we start monitoring a task, the event is added is onto the task. At that point, we read the initial value of the event. Whenever, we stop monitoring the task, the final value is taken and the difference is the event data. Now, a task can move to a different cpu. Suppose a task X is moving from cpu A to cpu B. When the task is scheduled out of A, we get an event_del for A, and hence, the event data is updated. And, we stop updating the X's event data. As soon as X moves on to B, event_add is called for B, and we again update the event_data. And this is how it keeps on updating the event data even when the task is scheduled on to different cpus. Cc: Gautham R. Shenoy Cc: Balbir Singh Cc: Benjamin Herrenschmidt Cc: Paul Mackerras Cc: Anton Blanchard Cc: Sukadev Bhattiprolu Cc: Michael Neuling Cc: Stewart Smith Cc: Daniel Axtens Cc: Stephane Eranian Cc: Anju T Sudhakar Signed-off-by: Hemant Kumar Signed-off-by: Madhavan Srinivasan --- arch/powerpc/include/asm/imc-pmu.h | 4 + arch/powerpc/perf/imc-pmu.c | 161 ++++++++++++++++++++++++++++++++++++- 2 files changed, 164 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/include/asm/imc-pmu.h b/arch/powerpc/include/asm/imc-pmu.h index f2b4f122e6c2..8b7141ba2f2b 100644 --- a/arch/powerpc/include/asm/imc-pmu.h +++ b/arch/powerpc/include/asm/imc-pmu.h @@ -22,6 +22,7 @@ #define IMC_MAX_PMUS 32 #define IMC_MAX_PMU_NAME_LEN 256 #define IMC_MAX_CORES 256 +#define IMC_MAX_CPUS 2048 #define NEST_IMC_ENGINE_START 1 #define NEST_IMC_ENGINE_STOP 0 @@ -34,6 +35,9 @@ #define IMC_DTB_CORE_COMPAT "ibm,imc-counters-core" #define IMC_DTB_THREAD_COMPAT "ibm,imc-counters-thread" +#define THREAD_IMC_LDBAR_MASK 0x0003ffffffffe000 +#define THREAD_IMC_ENABLE 0x8000000000000000 + /* * Structure to hold per chip specific memory address * information for nest pmus. Nest Counter data are exported diff --git a/arch/powerpc/perf/imc-pmu.c b/arch/powerpc/perf/imc-pmu.c index 32eea6941e95..6fc1fbc0067c 100644 --- a/arch/powerpc/perf/imc-pmu.c +++ b/arch/powerpc/perf/imc-pmu.c @@ -23,6 +23,9 @@ static u64 per_core_pdbar_add[IMC_MAX_CHIPS][IMC_MAX_CORES]; static cpumask_t core_imc_cpumask; struct imc_pmu *core_imc_pmu; +/* Maintains base address for all the cpus */ +static u64 per_cpu_add[IMC_MAX_CPUS]; + /* Needed for sanity check */ extern u64 nest_max_offset; extern u64 core_max_offset; @@ -443,6 +446,56 @@ static int core_imc_event_init(struct perf_event *event) return 0; } +static int thread_imc_event_init(struct perf_event *event) +{ + struct task_struct *target; + + if (event->attr.type != event->pmu->type) + return -ENOENT; + + /* Sampling not supported */ + if (event->hw.sample_period) + return -EINVAL; + + event->hw.idx = -1; + + /* Sanity check for config (event offset) */ + if (event->attr.config > thread_max_offset) + return -EINVAL; + + target = event->hw.target; + + if (!target) + return -EINVAL; + + event->pmu->task_ctx_nr = perf_sw_context; + return 0; +} + +static void thread_imc_read_counter(struct perf_event *event) +{ + u64 *addr, data; + int cpu_id = smp_processor_id(); + + addr = (u64 *)(per_cpu_add[cpu_id] + event->attr.config); + data = __be64_to_cpu(*addr); + local64_set(&event->hw.prev_count, data); +} + +static void thread_imc_perf_event_update(struct perf_event *event) +{ + u64 counter_prev, counter_new, final_count, *addr; + int cpu_id = smp_processor_id(); + + addr = (u64 *)(per_cpu_add[cpu_id] + event->attr.config); + 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 imc_read_counter(struct perf_event *event) { u64 *addr, data; @@ -483,6 +536,53 @@ static int imc_event_add(struct perf_event *event, int flags) return 0; } +static void thread_imc_event_start(struct perf_event *event, int flags) +{ + thread_imc_read_counter(event); +} + +static void thread_imc_event_stop(struct perf_event *event, int flags) +{ + thread_imc_perf_event_update(event); +} + +static void thread_imc_event_del(struct perf_event *event, int flags) +{ + thread_imc_perf_event_update(event); +} + +static int thread_imc_event_add(struct perf_event *event, int flags) +{ + thread_imc_event_start(event, flags); + + return 0; +} + +static void thread_imc_pmu_start_txn(struct pmu *pmu, + unsigned int txn_flags) +{ + if (txn_flags & ~PERF_PMU_TXN_ADD) + return; + perf_pmu_disable(pmu); +} + +static void thread_imc_pmu_cancel_txn(struct pmu *pmu) +{ + perf_pmu_enable(pmu); +} + +static int thread_imc_pmu_commit_txn(struct pmu *pmu) +{ + perf_pmu_enable(pmu); + return 0; +} + +static void thread_imc_pmu_sched_task(struct perf_event_context *ctx, + bool sched_in) +{ + return; +} + /* update_pmu_ops : Populate the appropriate operations for "pmu" */ static int update_pmu_ops(struct imc_pmu *pmu) { @@ -492,17 +592,31 @@ static int update_pmu_ops(struct imc_pmu *pmu) pmu->pmu.task_ctx_nr = perf_invalid_context; if (pmu->domain == IMC_DOMAIN_NEST) { pmu->pmu.event_init = nest_imc_event_init; + pmu->attr_groups[2] = &imc_pmu_cpumask_attr_group; } else if (pmu->domain == IMC_DOMAIN_CORE) { pmu->pmu.event_init = core_imc_event_init; + pmu->attr_groups[2] = &imc_pmu_cpumask_attr_group; } + pmu->pmu.add = imc_event_add; pmu->pmu.del = imc_event_stop; pmu->pmu.start = imc_event_start; pmu->pmu.stop = imc_event_stop; pmu->pmu.read = imc_perf_event_update; pmu->attr_groups[1] = &imc_format_group; - pmu->attr_groups[2] = &imc_pmu_cpumask_attr_group; pmu->pmu.attr_groups = pmu->attr_groups; + if (pmu->domain == IMC_DOMAIN_THREAD) { + pmu->pmu.event_init = thread_imc_event_init; + pmu->pmu.start = thread_imc_event_start; + pmu->pmu.add = thread_imc_event_add; + pmu->pmu.del = thread_imc_event_del; + pmu->pmu.stop = thread_imc_event_stop; + pmu->pmu.read = thread_imc_perf_event_update; + pmu->pmu.start_txn = thread_imc_pmu_start_txn; + pmu->pmu.cancel_txn = thread_imc_pmu_cancel_txn; + pmu->pmu.commit_txn = thread_imc_pmu_commit_txn; + pmu->pmu.sched_task = thread_imc_pmu_sched_task; + } return 0; } @@ -558,6 +672,44 @@ static int update_events_in_group(struct imc_events *events, return 0; } +static void cleanup_thread_imc_memory(void *dummy) +{ + int cpu_id = smp_processor_id(); + u64 addr = per_cpu_add[cpu_id]; + + /* Only if the address is non-zero, shall we free it */ + if (addr) + free_pages(addr, 0); +} + +static void cleanup_all_thread_imc_memory(void) +{ + on_each_cpu(cleanup_thread_imc_memory, NULL, 1); +} + +/* + * Allocates a page of memory for each of the online cpus, and, writes the + * physical base address of that page to the LDBAR for that cpu. This starts + * the thread IMC counters. + */ +static void thread_imc_mem_alloc(void *dummy) +{ + u64 ldbar_addr, ldbar_value; + int cpu_id = smp_processor_id(); + + per_cpu_add[cpu_id] = (u64)__get_free_pages(GFP_KERNEL | __GFP_ZERO, + 0); + ldbar_addr = (u64)virt_to_phys((void *)per_cpu_add[cpu_id]); + ldbar_value = (ldbar_addr & (u64)THREAD_IMC_LDBAR_MASK) | + (u64)THREAD_IMC_ENABLE; + mtspr(SPRN_LDBAR, ldbar_value); +} + +void thread_imc_cpu_init(void) +{ + on_each_cpu(thread_imc_mem_alloc, NULL, 1); +} + /* * init_imc_pmu : Setup the IMC pmu device in "pmu_ptr" and its events * "events". @@ -581,6 +733,9 @@ int init_imc_pmu(struct imc_events *events, int idx, if (ret) return ret; break; + case IMC_DOMAIN_THREAD: + thread_imc_cpu_init(); + break; default: return -1; /* Unknown domain */ } @@ -612,5 +767,9 @@ int init_imc_pmu(struct imc_events *events, int idx, if (pmu_ptr->domain == IMC_DOMAIN_CORE) cleanup_all_core_imc_memory(); + /* For thread_imc, we have allocated memory, we need to free it */ + if (pmu_ptr->domain == IMC_DOMAIN_THREAD) + cleanup_all_thread_imc_memory(); + return ret; }