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

perf pmu: Lazily compute default config

The default config is computed during creation of the PMU and may do
things like scanning sysfs, when the PMU may just be used as part of
scanning. Change default_config to perf_event_attr_init_default, a
callback that is used when a default config needs initializing. This
avoids holding onto the memory for a perf_event_attr and copying.

On a tigerlake laptop running the pmu-scan benchmark:

Before:
Running 'internals/pmu-scan' benchmark:
Computing performance of sysfs PMU event scan for 100 times
Average core PMU scanning took: 28.780 usec (+- 0.503 usec)
Average PMU scanning took: 283.480 usec (+- 18.471 usec)
Number of openat syscalls: 30,227

After:
Running 'internals/pmu-scan' benchmark:
Computing performance of sysfs PMU event scan for 100 times
Average core PMU scanning took: 27.880 usec (+- 0.169 usec)
Average PMU scanning took: 245.260 usec (+- 15.758 usec)
Number of openat syscalls: 28,914

Over 3 runs it is a nearly 12% reduction in execution time and a 4.3%
of openat calls.

Signed-off-by: Ian Rogers <irogers@google.com>
Reviewed-by: Adrian Hunter <adrian.hunter@intel.com>
Cc: Ravi Bangoria <ravi.bangoria@amd.com>
Cc: James Clark <james.clark@arm.com>
Cc: Suzuki K Poulose <suzuki.poulose@arm.com>
Cc: Yang Jihong <yangjihong1@huawei.com>
Cc: Will Deacon <will@kernel.org>
Cc: Leo Yan <leo.yan@linaro.org>
Cc: Mike Leach <mike.leach@linaro.org>
Cc: Jing Zhang <renyu.zj@linux.alibaba.com>
Cc: Kajol Jain <kjain@linux.ibm.com>
Cc: Thomas Richter <tmricht@linux.ibm.com>
Cc: Kan Liang <kan.liang@linux.intel.com>
Cc: John Garry <john.g.garry@oracle.com>
Cc: linux-arm-kernel@lists.infradead.org
Cc: coresight@lists.linaro.org
Link: https://lore.kernel.org/r/20231012175645.1849503-8-irogers@google.com
Signed-off-by: Namhyung Kim <namhyung@kernel.org>

authored by

Ian Rogers and committed by
Namhyung Kim
0197da7a f20c15d1

+58 -62
+3 -10
tools/perf/arch/arm/util/cs-etm.c
··· 917 917 * (CFG_CHG and evsel__set_config_if_unset()). If no default is set then user 918 918 * changes aren't tracked. 919 919 */ 920 - struct perf_event_attr * 921 - cs_etm_get_default_config(struct perf_pmu *pmu __maybe_unused) 920 + void 921 + cs_etm_get_default_config(const struct perf_pmu *pmu __maybe_unused, 922 + struct perf_event_attr *attr) 922 923 { 923 - struct perf_event_attr *attr; 924 - 925 - attr = zalloc(sizeof(struct perf_event_attr)); 926 - if (!attr) 927 - return NULL; 928 - 929 924 attr->sample_period = 1; 930 - 931 - return attr; 932 925 }
+2 -2
tools/perf/arch/arm/util/pmu.c
··· 20 20 if (!strcmp(pmu->name, CORESIGHT_ETM_PMU_NAME)) { 21 21 /* add ETM default config here */ 22 22 pmu->selectable = true; 23 - pmu->default_config = cs_etm_get_default_config(pmu); 23 + pmu->perf_event_attr_init_default = cs_etm_get_default_config; 24 24 #if defined(__aarch64__) 25 25 } else if (strstarts(pmu->name, ARM_SPE_PMU_NAME)) { 26 26 pmu->selectable = true; 27 27 pmu->is_uncore = false; 28 - pmu->default_config = arm_spe_pmu_default_config(pmu); 28 + pmu->perf_event_attr_init_default = arm_spe_pmu_default_config; 29 29 } else if (strstarts(pmu->name, HISI_PTT_PMU_NAME)) { 30 30 pmu->selectable = true; 31 31 #endif
+23 -22
tools/perf/arch/arm64/util/arm-spe.c
··· 113 113 } 114 114 } 115 115 116 + static __u64 arm_spe_pmu__sample_period(const struct perf_pmu *arm_spe_pmu) 117 + { 118 + static __u64 sample_period; 119 + 120 + if (sample_period) 121 + return sample_period; 122 + 123 + /* 124 + * If kernel driver doesn't advertise a minimum, 125 + * use max allowable by PMSIDR_EL1.INTERVAL 126 + */ 127 + if (perf_pmu__scan_file(arm_spe_pmu, "caps/min_interval", "%llu", 128 + &sample_period) != 1) { 129 + pr_debug("arm_spe driver doesn't advertise a min. interval. Using 4096\n"); 130 + sample_period = 4096; 131 + } 132 + return sample_period; 133 + } 134 + 116 135 static int arm_spe_recording_options(struct auxtrace_record *itr, 117 136 struct evlist *evlist, 118 137 struct record_opts *opts) ··· 155 136 return -EINVAL; 156 137 } 157 138 evsel->core.attr.freq = 0; 158 - evsel->core.attr.sample_period = arm_spe_pmu->default_config->sample_period; 139 + evsel->core.attr.sample_period = arm_spe_pmu__sample_period(arm_spe_pmu); 159 140 evsel->needs_auxtrace_mmap = true; 160 141 arm_spe_evsel = evsel; 161 142 opts->full_auxtrace = true; ··· 514 495 return &sper->itr; 515 496 } 516 497 517 - struct perf_event_attr 518 - *arm_spe_pmu_default_config(struct perf_pmu *arm_spe_pmu) 498 + void 499 + arm_spe_pmu_default_config(const struct perf_pmu *arm_spe_pmu, struct perf_event_attr *attr) 519 500 { 520 - struct perf_event_attr *attr; 521 - 522 - attr = zalloc(sizeof(struct perf_event_attr)); 523 - if (!attr) { 524 - pr_err("arm_spe default config cannot allocate a perf_event_attr\n"); 525 - return NULL; 526 - } 527 - 528 - /* 529 - * If kernel driver doesn't advertise a minimum, 530 - * use max allowable by PMSIDR_EL1.INTERVAL 531 - */ 532 - if (perf_pmu__scan_file(arm_spe_pmu, "caps/min_interval", "%llu", 533 - &attr->sample_period) != 1) { 534 - pr_debug("arm_spe driver doesn't advertise a min. interval. Using 4096\n"); 535 - attr->sample_period = 4096; 536 - } 537 - 538 - return attr; 501 + attr->sample_period = arm_spe_pmu__sample_period(arm_spe_pmu); 539 502 }
+12 -13
tools/perf/arch/x86/util/intel-pt.c
··· 60 60 size_t priv_size; 61 61 }; 62 62 63 - static int intel_pt_parse_terms_with_default(struct perf_pmu *pmu, 63 + static int intel_pt_parse_terms_with_default(const struct perf_pmu *pmu, 64 64 const char *str, 65 65 u64 *config) 66 66 { ··· 84 84 return err; 85 85 } 86 86 87 - static int intel_pt_parse_terms(struct perf_pmu *pmu, const char *str, u64 *config) 87 + static int intel_pt_parse_terms(const struct perf_pmu *pmu, const char *str, u64 *config) 88 88 { 89 89 *config = 0; 90 90 return intel_pt_parse_terms_with_default(pmu, str, config); ··· 177 177 return pick; 178 178 } 179 179 180 - static u64 intel_pt_default_config(struct perf_pmu *intel_pt_pmu) 180 + static u64 intel_pt_default_config(const struct perf_pmu *intel_pt_pmu) 181 181 { 182 182 char buf[256]; 183 183 int mtc, mtc_periods = 0, mtc_period; ··· 256 256 return 0; 257 257 } 258 258 259 - struct perf_event_attr * 260 - intel_pt_pmu_default_config(struct perf_pmu *intel_pt_pmu) 259 + void intel_pt_pmu_default_config(const struct perf_pmu *intel_pt_pmu, 260 + struct perf_event_attr *attr) 261 261 { 262 - struct perf_event_attr *attr; 262 + static u64 config; 263 + static bool initialized; 263 264 264 - attr = zalloc(sizeof(struct perf_event_attr)); 265 - if (!attr) 266 - return NULL; 267 - 268 - attr->config = intel_pt_default_config(intel_pt_pmu); 269 - 270 - return attr; 265 + if (!initialized) { 266 + config = intel_pt_default_config(intel_pt_pmu); 267 + initialized = true; 268 + } 269 + attr->config = config; 271 270 } 272 271 273 272 static const char *intel_pt_find_filter(struct evlist *evlist,
+1 -1
tools/perf/arch/x86/util/pmu.c
··· 23 23 if (!strcmp(pmu->name, INTEL_PT_PMU_NAME)) { 24 24 pmu->auxtrace = true; 25 25 pmu->selectable = true; 26 - pmu->default_config = intel_pt_pmu_default_config(pmu); 26 + pmu->perf_event_attr_init_default = intel_pt_pmu_default_config; 27 27 } 28 28 if (!strcmp(pmu->name, INTEL_BTS_PMU_NAME)) { 29 29 pmu->auxtrace = true;
+3 -1
tools/perf/util/arm-spe.h
··· 27 27 int arm_spe_process_auxtrace_info(union perf_event *event, 28 28 struct perf_session *session); 29 29 30 - struct perf_event_attr *arm_spe_pmu_default_config(struct perf_pmu *arm_spe_pmu); 30 + void arm_spe_pmu_default_config(const struct perf_pmu *arm_spe_pmu, 31 + struct perf_event_attr *attr); 32 + 31 33 #endif
+1 -1
tools/perf/util/cs-etm.h
··· 242 242 243 243 int cs_etm__process_auxtrace_info(union perf_event *event, 244 244 struct perf_session *session); 245 - struct perf_event_attr *cs_etm_get_default_config(struct perf_pmu *pmu); 245 + void cs_etm_get_default_config(const struct perf_pmu *pmu, struct perf_event_attr *attr); 246 246 247 247 enum cs_etm_pid_fmt { 248 248 CS_ETM_PIDFMT_NONE,
+2 -1
tools/perf/util/intel-pt.h
··· 42 42 int intel_pt_process_auxtrace_info(union perf_event *event, 43 43 struct perf_session *session); 44 44 45 - struct perf_event_attr *intel_pt_pmu_default_config(struct perf_pmu *pmu); 45 + void intel_pt_pmu_default_config(const struct perf_pmu *intel_pt_pmu, 46 + struct perf_event_attr *attr); 46 47 47 48 #endif
+6 -6
tools/perf/util/parse-events.c
··· 1418 1418 } 1419 1419 fix_raw(&parsed_terms, pmu); 1420 1420 1421 - if (pmu->default_config) { 1422 - memcpy(&attr, pmu->default_config, sizeof(struct perf_event_attr)); 1423 - } else { 1424 - memset(&attr, 0, sizeof(attr)); 1425 - } 1421 + memset(&attr, 0, sizeof(attr)); 1422 + if (pmu->perf_event_attr_init_default) 1423 + pmu->perf_event_attr_init_default(pmu, &attr); 1424 + 1426 1425 attr.type = pmu->type; 1427 1426 1428 1427 if (list_empty(&parsed_terms.terms)) { ··· 1465 1466 * When using default config, record which bits of attr->config were 1466 1467 * changed by the user. 1467 1468 */ 1468 - if (pmu->default_config && get_config_chgs(pmu, &parsed_terms, &config_terms)) { 1469 + if (pmu->perf_event_attr_init_default && 1470 + get_config_chgs(pmu, &parsed_terms, &config_terms)) { 1469 1471 parse_events_terms__exit(&parsed_terms); 1470 1472 return -ENOMEM; 1471 1473 }
+1 -2
tools/perf/util/pmu.c
··· 1402 1402 struct parse_events_terms *head_terms, 1403 1403 struct parse_events_error *err) 1404 1404 { 1405 - bool zero = !!pmu->default_config; 1405 + bool zero = !!pmu->perf_event_attr_init_default; 1406 1406 1407 1407 return perf_pmu__config_terms(pmu, attr, head_terms, zero, err); 1408 1408 } ··· 2064 2064 2065 2065 perf_cpu_map__put(pmu->cpus); 2066 2066 2067 - zfree(&pmu->default_config); 2068 2067 zfree(&pmu->name); 2069 2068 zfree(&pmu->alias_name); 2070 2069 zfree(&pmu->id);
+4 -3
tools/perf/util/pmu.h
··· 92 92 */ 93 93 int max_precise; 94 94 /** 95 - * @default_config: Optional default perf_event_attr determined in 96 - * architecture specific code. 95 + * @perf_event_attr_init_default: Optional function to default 96 + * initialize PMU specific parts of the perf_event_attr. 97 97 */ 98 - struct perf_event_attr *default_config; 98 + void (*perf_event_attr_init_default)(const struct perf_pmu *pmu, 99 + struct perf_event_attr *attr); 99 100 /** 100 101 * @cpus: Empty or the contents of either of: 101 102 * <sysfs>/bus/event_source/devices/<name>/cpumask.