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

perf: Skip and warn on unknown format 'configN' attrs

If the kernel exposes a new perf_event_attr field in a format attr, perf
will return an error stating the specified PMU can't be found. For
example, a format attr with 'config3:0-63' causes an error as config3 is
unknown to perf. This causes a compatibility issue between a newer
kernel with older perf tool.

Before this change with a kernel adding 'config3' I get:

$ perf record -e arm_spe// -- true
event syntax error: 'arm_spe//'
\___ Cannot find PMU `arm_spe'. Missing kernel support?
Run 'perf list' for a list of valid events

Usage: perf record [<options>] [<command>]
or: perf record [<options>] -- <command> [<options>]

-e, --event <event> event selector. use 'perf list' to list
available events

After this change, I get:

$ perf record -e arm_spe// -- true
WARNING: 'arm_spe_0' format 'inv_event_filter' requires 'perf_event_attr::config3' which is not supported by this version of perf!
[ perf record: Woken up 2 times to write data ]
[ perf record: Captured and wrote 0.091 MB perf.data ]

To support unknown configN formats, rework the YACC implementation to
pass any config[0-9]+ format to perf_pmu__new_format() to handle with a
warning.

Reviewed-by: Namhyung Kim <namhyung@kernel.org>
Signed-off-by: Rob Herring <robh@kernel.org>
Tested-by: Leo Yan <leo.yan@linaro.org>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: James Clark <james.clark@arm.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: stable@vger.kernel.org
Link: https://lore.kernel.org/r/20220914-arm-perf-tool-spe1-2-v2-v4-1-83c098e6212e@kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

authored by

Rob Herring and committed by
Arnaldo Carvalho de Melo
e552b7be 0cef141e

+26 -13
+3
tools/perf/util/parse-events.c
··· 246 246 struct perf_cpu_map *cpus = pmu ? perf_cpu_map__get(pmu->cpus) : 247 247 cpu_list ? perf_cpu_map__new(cpu_list) : NULL; 248 248 249 + if (pmu) 250 + perf_pmu__warn_invalid_formats(pmu); 251 + 249 252 if (pmu && attr->type == PERF_TYPE_RAW) 250 253 perf_pmu__warn_invalid_config(pmu, attr->config, name); 251 254
+17
tools/perf/util/pmu.c
··· 1005 1005 return NULL; 1006 1006 } 1007 1007 1008 + void perf_pmu__warn_invalid_formats(struct perf_pmu *pmu) 1009 + { 1010 + struct perf_pmu_format *format; 1011 + 1012 + /* fake pmu doesn't have format list */ 1013 + if (pmu == &perf_pmu__fake) 1014 + return; 1015 + 1016 + list_for_each_entry(format, &pmu->format, list) 1017 + if (format->value >= PERF_PMU_FORMAT_VALUE_CONFIG_END) { 1018 + pr_warning("WARNING: '%s' format '%s' requires 'perf_event_attr::config%d'" 1019 + "which is not supported by this version of perf!\n", 1020 + pmu->name, format->name, format->value); 1021 + return; 1022 + } 1023 + } 1024 + 1008 1025 static struct perf_pmu *pmu_find(const char *name) 1009 1026 { 1010 1027 struct perf_pmu *pmu;
+2
tools/perf/util/pmu.h
··· 17 17 PERF_PMU_FORMAT_VALUE_CONFIG, 18 18 PERF_PMU_FORMAT_VALUE_CONFIG1, 19 19 PERF_PMU_FORMAT_VALUE_CONFIG2, 20 + PERF_PMU_FORMAT_VALUE_CONFIG_END, 20 21 }; 21 22 22 23 #define PERF_PMU_FORMAT_BITS 64 ··· 140 139 141 140 void perf_pmu__warn_invalid_config(struct perf_pmu *pmu, __u64 config, 142 141 const char *name); 142 + void perf_pmu__warn_invalid_formats(struct perf_pmu *pmu); 143 143 144 144 bool perf_pmu__has_hybrid(void); 145 145 int perf_pmu__match(char *pattern, char *name, char *tok);
-2
tools/perf/util/pmu.l
··· 27 27 28 28 {num_dec} { return value(10); } 29 29 config { return PP_CONFIG; } 30 - config1 { return PP_CONFIG1; } 31 - config2 { return PP_CONFIG2; } 32 30 - { return '-'; } 33 31 : { return ':'; } 34 32 , { return ','; }
+4 -11
tools/perf/util/pmu.y
··· 18 18 19 19 %} 20 20 21 - %token PP_CONFIG PP_CONFIG1 PP_CONFIG2 21 + %token PP_CONFIG 22 22 %token PP_VALUE PP_ERROR 23 23 %type <num> PP_VALUE 24 24 %type <bits> bit_term ··· 45 45 $3)); 46 46 } 47 47 | 48 - PP_CONFIG1 ':' bits 48 + PP_CONFIG PP_VALUE ':' bits 49 49 { 50 50 ABORT_ON(perf_pmu__new_format(format, name, 51 - PERF_PMU_FORMAT_VALUE_CONFIG1, 52 - $3)); 53 - } 54 - | 55 - PP_CONFIG2 ':' bits 56 - { 57 - ABORT_ON(perf_pmu__new_format(format, name, 58 - PERF_PMU_FORMAT_VALUE_CONFIG2, 59 - $3)); 51 + $2, 52 + $4)); 60 53 } 61 54 62 55 bits: