aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatt Redfearn <matt.redfearn@mips.com>2018-04-20 06:23:06 -0400
committerJames Hogan <jhogan@kernel.org>2018-05-15 10:53:44 -0400
commit84002c88599d6b537e54b003f763215be2075243 (patch)
treec413ffe5ba6951644deda48b824e151d9b46d68e
parent840a8b55effdc9a98b115f84b8bbb6a2f5d05226 (diff)
MIPS: perf: Fix perf with MT counting other threads
When perf is used in non-system mode, i.e. without specifying CPUs to count on, check_and_calc_range falls into the case when it sets M_TC_EN_ALL in the counter config_base. This has the impact of always counting for all of the threads in a core, even when the user has not requested it. For example this can be seen with a test program which executes 30002 instructions and 10000 branches running on one VPE and a busy load on the other VPE in the core. Without this commit, the expected count is not returned: taskset 4 dd if=/dev/zero of=/dev/null count=100000 & taskset 8 perf stat -e instructions:u,branches:u ./test_prog Performance counter stats for './test_prog': 103235 instructions:u 17015 branches:u In order to fix this, remove check_and_calc_range entirely and perform all of the logic in mipsxx_pmu_enable_event. Since mipsxx_pmu_enable_event now requires the range of the event, ensure that it is set by mipspmu_perf_event_encode in the same circumstances as before (i.e. #ifdef CONFIG_MIPS_MT_SMP && num_possible_cpus() > 1). The logic of mipsxx_pmu_enable_event now becomes: If the CPU is a BMIPS5000, then use the special vpe_id() implementation to select which VPE to count. If the counter has a range greater than a single VPE, i.e. it is a core-wide counter, then ensure that the counter is set up to count events from all TCs (though, since this is true by definition, is this necessary? Just enabling a core-wide counter in the per-VPE case appears experimentally to return the same counts. This is left in for now as the logic was present before). If the event is set up to count a particular CPU (i.e. system mode), then the VPE ID of that CPU is used for the counter. Otherwise, the event should be counted on the CPU scheduling this thread (this was the critical bit missing from the previous implementation) so the VPE ID of this CPU is used for the counter. With this commit, the same test as before returns the counts expected: taskset 4 dd if=/dev/zero of=/dev/null count=100000 & taskset 8 perf stat -e instructions:u,branches:u ./test_prog Performance counter stats for './test_prog': 30002 instructions:u 10000 branches:u Signed-off-by: Matt Redfearn <matt.redfearn@mips.com> Cc: Ralf Baechle <ralf@linux-mips.org> Cc: Florian Fainelli <f.fainelli@gmail.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Ingo Molnar <mingo@redhat.com> Cc: Arnaldo Carvalho de Melo <acme@kernel.org> Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com> Cc: Jiri Olsa <jolsa@redhat.com> Cc: Namhyung Kim <namhyung@kernel.org> Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/19138/ Signed-off-by: James Hogan <jhogan@kernel.org>
-rw-r--r--arch/mips/kernel/perf_event_mipsxx.c78
1 files changed, 39 insertions, 39 deletions
diff --git a/arch/mips/kernel/perf_event_mipsxx.c b/arch/mips/kernel/perf_event_mipsxx.c
index 11d1b2268fdd..413863508f6f 100644
--- a/arch/mips/kernel/perf_event_mipsxx.c
+++ b/arch/mips/kernel/perf_event_mipsxx.c
@@ -323,7 +323,11 @@ static int mipsxx_pmu_alloc_counter(struct cpu_hw_events *cpuc,
323 323
324static void mipsxx_pmu_enable_event(struct hw_perf_event *evt, int idx) 324static void mipsxx_pmu_enable_event(struct hw_perf_event *evt, int idx)
325{ 325{
326 struct perf_event *event = container_of(evt, struct perf_event, hw);
326 struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); 327 struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
328#ifdef CONFIG_MIPS_MT_SMP
329 unsigned int range = evt->event_base >> 24;
330#endif /* CONFIG_MIPS_MT_SMP */
327 331
328 WARN_ON(idx < 0 || idx >= mipspmu.num_counters); 332 WARN_ON(idx < 0 || idx >= mipspmu.num_counters);
329 333
@@ -331,11 +335,37 @@ static void mipsxx_pmu_enable_event(struct hw_perf_event *evt, int idx)
331 (evt->config_base & M_PERFCTL_CONFIG_MASK) | 335 (evt->config_base & M_PERFCTL_CONFIG_MASK) |
332 /* Make sure interrupt enabled. */ 336 /* Make sure interrupt enabled. */
333 MIPS_PERFCTRL_IE; 337 MIPS_PERFCTRL_IE;
334 if (IS_ENABLED(CONFIG_CPU_BMIPS5000)) 338
339#ifdef CONFIG_CPU_BMIPS5000
340 {
335 /* enable the counter for the calling thread */ 341 /* enable the counter for the calling thread */
336 cpuc->saved_ctrl[idx] |= 342 cpuc->saved_ctrl[idx] |=
337 (1 << (12 + vpe_id())) | BRCM_PERFCTRL_TC; 343 (1 << (12 + vpe_id())) | BRCM_PERFCTRL_TC;
344 }
345#else
346#ifdef CONFIG_MIPS_MT_SMP
347 if (range > V) {
348 /* The counter is processor wide. Set it up to count all TCs. */
349 pr_debug("Enabling perf counter for all TCs\n");
350 cpuc->saved_ctrl[idx] |= M_TC_EN_ALL;
351 } else
352#endif /* CONFIG_MIPS_MT_SMP */
353 {
354 unsigned int cpu, ctrl;
338 355
356 /*
357 * Set up the counter for a particular CPU when event->cpu is
358 * a valid CPU number. Otherwise set up the counter for the CPU
359 * scheduling this thread.
360 */
361 cpu = (event->cpu >= 0) ? event->cpu : smp_processor_id();
362
363 ctrl = M_PERFCTL_VPEID(cpu_vpe_id(&cpu_data[cpu]));
364 ctrl |= M_TC_EN_VPE;
365 cpuc->saved_ctrl[idx] |= ctrl;
366 pr_debug("Enabling perf counter for CPU%d\n", cpu);
367 }
368#endif /* CONFIG_CPU_BMIPS5000 */
339 /* 369 /*
340 * We do not actually let the counter run. Leave it until start(). 370 * We do not actually let the counter run. Leave it until start().
341 */ 371 */
@@ -649,13 +679,14 @@ static unsigned int mipspmu_perf_event_encode(const struct mips_perf_event *pev)
649 * event_id. 679 * event_id.
650 */ 680 */
651#ifdef CONFIG_MIPS_MT_SMP 681#ifdef CONFIG_MIPS_MT_SMP
652 return ((unsigned int)pev->range << 24) | 682 if (num_possible_cpus() > 1)
653 (pev->cntr_mask & 0xffff00) | 683 return ((unsigned int)pev->range << 24) |
654 (pev->event_id & 0xff); 684 (pev->cntr_mask & 0xffff00) |
655#else 685 (pev->event_id & 0xff);
656 return (pev->cntr_mask & 0xffff00) | 686 else
657 (pev->event_id & 0xff); 687#endif /* CONFIG_MIPS_MT_SMP */
658#endif 688 return ((pev->cntr_mask & 0xffff00) |
689 (pev->event_id & 0xff));
659} 690}
660 691
661static const struct mips_perf_event *mipspmu_map_general_event(int idx) 692static const struct mips_perf_event *mipspmu_map_general_event(int idx)
@@ -1259,33 +1290,6 @@ static const struct mips_perf_event xlp_cache_map
1259}, 1290},
1260}; 1291};
1261 1292
1262#ifdef CONFIG_MIPS_MT_SMP
1263static void check_and_calc_range(struct perf_event *event,
1264 const struct mips_perf_event *pev)
1265{
1266 struct hw_perf_event *hwc = &event->hw;
1267
1268 if (event->cpu >= 0) {
1269 if (pev->range > V) {
1270 /*
1271 * The user selected an event that is processor
1272 * wide, while expecting it to be VPE wide.
1273 */
1274 hwc->config_base |= M_TC_EN_ALL;
1275 } else {
1276 hwc->config_base |= M_PERFCTL_VPEID(vpe_id());
1277 hwc->config_base |= M_TC_EN_VPE;
1278 }
1279 } else
1280 hwc->config_base |= M_TC_EN_ALL;
1281}
1282#else
1283static void check_and_calc_range(struct perf_event *event,
1284 const struct mips_perf_event *pev)
1285{
1286}
1287#endif
1288
1289static int __hw_perf_event_init(struct perf_event *event) 1293static int __hw_perf_event_init(struct perf_event *event)
1290{ 1294{
1291 struct perf_event_attr *attr = &event->attr; 1295 struct perf_event_attr *attr = &event->attr;
@@ -1321,10 +1325,6 @@ static int __hw_perf_event_init(struct perf_event *event)
1321 */ 1325 */
1322 hwc->config_base = MIPS_PERFCTRL_IE; 1326 hwc->config_base = MIPS_PERFCTRL_IE;
1323 1327
1324 /* Calculate range bits and validate it. */
1325 if (num_possible_cpus() > 1)
1326 check_and_calc_range(event, pev);
1327
1328 hwc->event_base = mipspmu_perf_event_encode(pev); 1328 hwc->event_base = mipspmu_perf_event_encode(pev);
1329 if (PERF_TYPE_RAW == event->attr.type) 1329 if (PERF_TYPE_RAW == event->attr.type)
1330 mutex_unlock(&raw_event_mutex); 1330 mutex_unlock(&raw_event_mutex);