Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

perf pmus: Allow just core PMU scanning

Scanning all PMUs is expensive as all PMUs sysfs entries are loaded,
benchmarking shows more than 4x the cost:

```
$ perf bench internals pmu-scan -i 1000
Computing performance of sysfs PMU event scan for 1000 times
Average core PMU scanning took: 989.231 usec (+- 1.535 usec)
Average PMU scanning took: 4309.425 usec (+- 74.322 usec)
```

Add new perf_pmus__scan_core routine that scans just core
PMUs. Replace perf_pmus__scan calls with perf_pmus__scan_core when
non-core PMUs are being ignored.

Reviewed-by: Kan Liang <kan.liang@linux.intel.com>
Signed-off-by: Ian Rogers <irogers@google.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Ali Saidi <alisaidi@amazon.com>
Cc: Athira Rajeev <atrajeev@linux.vnet.ibm.com>
Cc: Dmitrii Dolgov <9erthalion6@gmail.com>
Cc: Huacai Chen <chenhuacai@kernel.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: James Clark <james.clark@arm.com>
Cc: Jing Zhang <renyu.zj@linux.alibaba.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: John Garry <john.g.garry@oracle.com>
Cc: Kajol Jain <kjain@linux.ibm.com>
Cc: Kang Minchul <tegongkang@gmail.com>
Cc: Leo Yan <leo.yan@linaro.org>
Cc: Madhavan Srinivasan <maddy@linux.ibm.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Mike Leach <mike.leach@linaro.org>
Cc: Ming Wang <wangming01@loongson.cn>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Ravi Bangoria <ravi.bangoria@amd.com>
Cc: Rob Herring <robh@kernel.org>
Cc: Sandipan Das <sandipan.das@amd.com>
Cc: Sean Christopherson <seanjc@google.com>
Cc: Suzuki Poulouse <suzuki.poulose@arm.com>
Cc: Thomas Richter <tmricht@linux.ibm.com>
Cc: Will Deacon <will@kernel.org>
Cc: Xing Zhengjun <zhengjun.xing@linux.intel.com>
Cc: coresight@lists.linaro.org
Cc: linux-arm-kernel@lists.infradead.org
Link: https://lore.kernel.org/r/20230527072210.2900565-30-irogers@google.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

authored by

Ian Rogers and committed by
Arnaldo Carvalho de Melo
9d6a1df9 15c57a80

+73 -94
+1 -4
tools/perf/arch/arm64/util/pmu.c
··· 11 11 { 12 12 struct perf_pmu *pmu = NULL; 13 13 14 - while ((pmu = perf_pmus__scan(pmu))) { 15 - if (!is_pmu_core(pmu->name)) 16 - continue; 17 - 14 + while ((pmu = perf_pmus__scan_core(pmu))) { 18 15 /* 19 16 * The cpumap should cover all CPUs. Otherwise, some CPUs may 20 17 * not support some events or have different event IDs.
+1 -4
tools/perf/arch/x86/util/evlist.c
··· 33 33 continue; 34 34 } 35 35 36 - while ((pmu = perf_pmus__scan(pmu)) != NULL) { 36 + while ((pmu = perf_pmus__scan_core(pmu)) != NULL) { 37 37 struct perf_cpu_map *cpus; 38 38 struct evsel *evsel; 39 - 40 - if (!pmu->is_core) 41 - continue; 42 39 43 40 evsel = evsel__new(attrs + i); 44 41 if (evsel == NULL)
+3 -5
tools/perf/arch/x86/util/perf_regs.c
··· 300 300 * The same register set is supported among different hybrid PMUs. 301 301 * Only check the first available one. 302 302 */ 303 - while ((pmu = perf_pmus__scan(pmu)) != NULL) { 304 - if (pmu->is_core) { 305 - type = pmu->type; 306 - break; 307 - } 303 + while ((pmu = perf_pmus__scan_core(pmu)) != NULL) { 304 + type = pmu->type; 305 + break; 308 306 } 309 307 attr.config |= type << PERF_PMU_TYPE_SHIFT; 310 308 }
+27 -19
tools/perf/bench/pmu-scan.c
··· 22 22 int nr_aliases; 23 23 int nr_formats; 24 24 int nr_caps; 25 + bool is_core; 25 26 }; 26 27 27 28 static const struct option options[] = { ··· 54 53 r = results + nr_pmus; 55 54 56 55 r->name = strdup(pmu->name); 56 + r->is_core = pmu->is_core; 57 57 r->nr_caps = pmu->nr_caps; 58 58 59 59 r->nr_aliases = 0; ··· 74 72 return 0; 75 73 } 76 74 77 - static int check_result(void) 75 + static int check_result(bool core_only) 78 76 { 79 77 struct pmu_scan_result *r; 80 78 struct perf_pmu *pmu; ··· 83 81 84 82 for (int i = 0; i < nr_pmus; i++) { 85 83 r = &results[i]; 84 + if (core_only && !r->is_core) 85 + continue; 86 + 86 87 pmu = perf_pmus__find(r->name); 87 88 if (pmu == NULL) { 88 89 pr_err("Cannot find PMU %s\n", r->name); ··· 135 130 struct timeval start, end, diff; 136 131 double time_average, time_stddev; 137 132 u64 runtime_us; 138 - unsigned int i; 139 133 int ret; 140 134 141 135 init_stats(&stats); ··· 146 142 return -1; 147 143 } 148 144 149 - for (i = 0; i < iterations; i++) { 150 - gettimeofday(&start, NULL); 151 - perf_pmus__scan(NULL); 152 - gettimeofday(&end, NULL); 145 + for (int j = 0; j < 2; j++) { 146 + bool core_only = (j == 0); 153 147 154 - timersub(&end, &start, &diff); 155 - runtime_us = diff.tv_sec * USEC_PER_SEC + diff.tv_usec; 156 - update_stats(&stats, runtime_us); 148 + for (unsigned int i = 0; i < iterations; i++) { 149 + gettimeofday(&start, NULL); 150 + if (core_only) 151 + perf_pmus__scan_core(NULL); 152 + else 153 + perf_pmus__scan(NULL); 154 + gettimeofday(&end, NULL); 155 + timersub(&end, &start, &diff); 156 + runtime_us = diff.tv_sec * USEC_PER_SEC + diff.tv_usec; 157 + update_stats(&stats, runtime_us); 157 158 158 - ret = check_result(); 159 - perf_pmus__destroy(); 160 - if (ret < 0) 161 - break; 159 + ret = check_result(core_only); 160 + perf_pmus__destroy(); 161 + if (ret < 0) 162 + break; 163 + } 164 + time_average = avg_stats(&stats); 165 + time_stddev = stddev_stats(&stats); 166 + pr_info(" Average%s PMU scanning took: %.3f usec (+- %.3f usec)\n", 167 + core_only ? " core" : "", time_average, time_stddev); 162 168 } 163 - 164 - time_average = avg_stats(&stats); 165 - time_stddev = stddev_stats(&stats); 166 - pr_info(" Average PMU scanning took: %.3f usec (+- %.3f usec)\n", 167 - time_average, time_stddev); 168 - 169 169 delete_result(); 170 170 return 0; 171 171 }
+1 -4
tools/perf/tests/pmu-events.c
··· 709 709 struct perf_pmu *pmu = NULL; 710 710 unsigned long i; 711 711 712 - while ((pmu = perf_pmus__scan(pmu)) != NULL) { 712 + while ((pmu = perf_pmus__scan_core(pmu)) != NULL) { 713 713 int count = 0; 714 - 715 - if (!is_pmu_core(pmu->name)) 716 - continue; 717 714 718 715 if (list_empty(&pmu->format)) { 719 716 pr_debug2("skipping testing core PMU %s\n", pmu->name);
+4 -8
tools/perf/util/cputopo.c
··· 477 477 if (!perf_pmus__has_hybrid()) 478 478 return NULL; 479 479 480 - while ((pmu = perf_pmus__scan(pmu)) != NULL) { 481 - if (pmu->is_core) 482 - nr++; 483 - } 480 + while ((pmu = perf_pmus__scan_core(pmu)) != NULL) 481 + nr++; 482 + 484 483 if (nr == 0) 485 484 return NULL; 486 485 ··· 488 489 return NULL; 489 490 490 491 tp->nr = nr; 491 - while ((pmu = perf_pmus__scan(pmu)) != NULL) { 492 - if (!pmu->is_core) 493 - continue; 494 - 492 + while ((pmu = perf_pmus__scan_core(pmu)) != NULL) { 495 493 if (load_hybrid_node(&tp->nodes[i], pmu)) { 496 494 hybrid_topology__delete(tp); 497 495 return NULL;
+1 -4
tools/perf/util/header.c
··· 1607 1607 */ 1608 1608 if (perf_pmus__has_hybrid()) { 1609 1609 pmu = NULL; 1610 - while ((pmu = perf_pmus__scan(pmu))) { 1611 - if (!pmu->is_core) 1612 - continue; 1613 - 1610 + while ((pmu = perf_pmus__scan_core(pmu))) { 1614 1611 ret = __write_pmu_caps(ff, pmu, true); 1615 1612 if (ret < 0) 1616 1613 return ret;
+3 -11
tools/perf/util/mem-events.c
··· 136 136 } else { 137 137 struct perf_pmu *pmu = NULL; 138 138 139 - while ((pmu = perf_pmus__scan(pmu)) != NULL) { 140 - if (!pmu->is_core) 141 - continue; 142 - 139 + while ((pmu = perf_pmus__scan_core(pmu)) != NULL) { 143 140 scnprintf(sysfs_name, sizeof(sysfs_name), 144 141 e->sysfs_name, pmu->name); 145 142 e->supported |= perf_mem_event__supported(mnt, sysfs_name); ··· 173 176 char sysfs_name[100]; 174 177 struct perf_pmu *pmu = NULL; 175 178 176 - while ((pmu = perf_pmus__scan(pmu)) != NULL) { 177 - if (!pmu->is_core) 178 - continue; 179 - 179 + while ((pmu = perf_pmus__scan_core(pmu)) != NULL) { 180 180 scnprintf(sysfs_name, sizeof(sysfs_name), e->sysfs_name, 181 181 pmu->name); 182 182 if (!perf_mem_event__supported(mnt, sysfs_name)) { ··· 211 217 return -1; 212 218 } 213 219 214 - while ((pmu = perf_pmus__scan(pmu)) != NULL) { 215 - if (!pmu->is_core) 216 - continue; 220 + while ((pmu = perf_pmus__scan_core(pmu)) != NULL) { 217 221 rec_argv[i++] = "-e"; 218 222 s = perf_mem_events__name(j, pmu->name); 219 223 if (s) {
+4 -9
tools/perf/util/parse-events.c
··· 453 453 const char *config_name = get_config_name(head_config); 454 454 const char *metric_id = get_config_metric_id(head_config); 455 455 456 - while ((pmu = perf_pmus__scan(pmu)) != NULL) { 456 + /* Legacy cache events are only supported by core PMUs. */ 457 + while ((pmu = perf_pmus__scan_core(pmu)) != NULL) { 457 458 LIST_HEAD(config_terms); 458 459 struct perf_event_attr attr; 459 460 int ret; 460 - 461 - /* Skip unsupported PMUs. */ 462 - if (!perf_pmu__supports_legacy_cache(pmu)) 463 - continue; 464 461 465 462 if (parse_events__filter_pmu(parse_state, pmu)) 466 463 continue; ··· 1478 1481 return __parse_events_add_numeric(parse_state, list, /*pmu=*/NULL, 1479 1482 type, config, head_config); 1480 1483 1481 - while ((pmu = perf_pmus__scan(pmu)) != NULL) { 1484 + /* Wildcards on numeric values are only supported by core PMUs. */ 1485 + while ((pmu = perf_pmus__scan_core(pmu)) != NULL) { 1482 1486 int ret; 1483 - 1484 - if (!perf_pmu__supports_wildcard_numeric(pmu)) 1485 - continue; 1486 1487 1487 1488 if (parse_events__filter_pmu(parse_state, pmu)) 1488 1489 continue;
-10
tools/perf/util/pmu.c
··· 1427 1427 return pmu->is_core; 1428 1428 } 1429 1429 1430 - bool perf_pmu__supports_wildcard_numeric(const struct perf_pmu *pmu) 1431 - { 1432 - return pmu->is_core; 1433 - } 1434 - 1435 1430 bool perf_pmu__auto_merge_stats(const struct perf_pmu *pmu) 1436 1431 { 1437 1432 return !is_pmu_hybrid(pmu->name); 1438 - } 1439 - 1440 - bool perf_pmu__is_mem_pmu(const struct perf_pmu *pmu) 1441 - { 1442 - return pmu->is_core; 1443 1433 } 1444 1434 1445 1435 bool perf_pmu__have_event(const struct perf_pmu *pmu, const char *name)
-2
tools/perf/util/pmu.h
··· 223 223 bool is_pmu_core(const char *name); 224 224 bool is_pmu_hybrid(const char *name); 225 225 bool perf_pmu__supports_legacy_cache(const struct perf_pmu *pmu); 226 - bool perf_pmu__supports_wildcard_numeric(const struct perf_pmu *pmu); 227 226 bool perf_pmu__auto_merge_stats(const struct perf_pmu *pmu); 228 - bool perf_pmu__is_mem_pmu(const struct perf_pmu *pmu); 229 227 bool perf_pmu__have_event(const struct perf_pmu *pmu, const char *name); 230 228 231 229 FILE *perf_pmu__open_file(struct perf_pmu *pmu, const char *name);
+22 -8
tools/perf/util/pmus.c
··· 87 87 } 88 88 89 89 /* Add all pmus in sysfs to pmu list: */ 90 - static void pmu_read_sysfs(void) 90 + static void pmu_read_sysfs(bool core_only) 91 91 { 92 92 int fd; 93 93 DIR *dir; ··· 103 103 104 104 while ((dent = readdir(dir))) { 105 105 if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, "..")) 106 + continue; 107 + if (core_only && !is_pmu_core(dent->d_name)) 106 108 continue; 107 109 /* add to static LIST_HEAD(core_pmus) or LIST_HEAD(other_pmus): */ 108 110 perf_pmu__find2(fd, dent->d_name); ··· 137 135 bool use_core_pmus = !pmu || pmu->is_core; 138 136 139 137 if (!pmu) { 140 - pmu_read_sysfs(); 138 + pmu_read_sysfs(/*core_only=*/false); 141 139 pmu = list_prepare_entry(pmu, &core_pmus, list); 142 140 } 143 141 if (use_core_pmus) { ··· 149 147 } 150 148 list_for_each_entry_continue(pmu, &other_pmus, list) 151 149 return pmu; 150 + return NULL; 151 + } 152 + 153 + struct perf_pmu *perf_pmus__scan_core(struct perf_pmu *pmu) 154 + { 155 + if (!pmu) { 156 + pmu_read_sysfs(/*core_only=*/true); 157 + pmu = list_prepare_entry(pmu, &core_pmus, list); 158 + } 159 + list_for_each_entry_continue(pmu, &core_pmus, list) 160 + return pmu; 161 + 152 162 return NULL; 153 163 } 154 164 ··· 190 176 struct perf_pmu *pmu = NULL; 191 177 int count = 0; 192 178 193 - while ((pmu = perf_pmus__scan(pmu)) != NULL) { 194 - if (perf_pmu__is_mem_pmu(pmu)) 195 - count++; 196 - } 179 + /* All core PMUs are for mem events. */ 180 + while ((pmu = perf_pmus__scan_core(pmu)) != NULL) 181 + count++; 182 + 197 183 return count; 198 184 } 199 185 ··· 435 421 if (!hybrid_scanned) { 436 422 struct perf_pmu *pmu = NULL; 437 423 438 - while ((pmu = perf_pmus__scan(pmu)) != NULL) { 439 - if (pmu->is_core && is_pmu_hybrid(pmu->name)) { 424 + while ((pmu = perf_pmus__scan_core(pmu)) != NULL) { 425 + if (is_pmu_hybrid(pmu->name)) { 440 426 has_hybrid = true; 441 427 break; 442 428 }
+1
tools/perf/util/pmus.h
··· 11 11 struct perf_pmu *perf_pmus__find_by_type(unsigned int type); 12 12 13 13 struct perf_pmu *perf_pmus__scan(struct perf_pmu *pmu); 14 + struct perf_pmu *perf_pmus__scan_core(struct perf_pmu *pmu); 14 15 15 16 const struct perf_pmu *perf_pmus__pmu_for_pmu_filter(const char *str); 16 17
+5 -6
tools/perf/util/print-events.c
··· 272 272 struct perf_pmu *pmu = NULL; 273 273 const char *event_type_descriptor = event_type_descriptors[PERF_TYPE_HW_CACHE]; 274 274 275 - while ((pmu = perf_pmus__scan(pmu)) != NULL) { 276 - /* 277 - * Skip uncore PMUs for performance. PERF_TYPE_HW_CACHE type 278 - * attributes can accept software PMUs in the extended type, so 279 - * also skip. 280 - */ 275 + /* 276 + * Only print core PMUs, skipping uncore for performance and 277 + * PERF_TYPE_SOFTWARE that can succeed in opening legacy cache evenst. 278 + */ 279 + while ((pmu = perf_pmus__scan_core(pmu)) != NULL) { 281 280 if (pmu->is_uncore || pmu->type == PERF_TYPE_SOFTWARE) 282 281 continue; 283 282