perf mem record: Use the IBS swfilt filter when available

IBS events with exclude_{user,kernel} bits, as used for per-thread
recording when kernel samples are not allowed, are rejected on hardware
without the privilege filter, so per-thread 'perf mem record' fails on
AMD:

  $ perf mem record -o /dev/null -- true
  Failure to open event 'ibs_op/ldlat=0/u' on PMU 'ibs_op' which will be removed.

Kernel v6.14 added swfilt, a software privilege filter exposed as the
'swfilt' format term, making those events usable per-thread.  Give the
ibs_op memory events extra tables with the term, selected in
perf_pmu__arch_init() when the PMU exposes it, keeping the names that
need system wide mode otherwise; the knowledge that IBS needs this
stays in the arch code.

Suggested-by: Namhyung Kim <namhyung@kernel.org>
Suggested-by: Ravi Bangoria <ravi.bangoria@amd.com>
Reviewed-by: Namhyung Kim <namhyung@kernel.org>
Reviewed-by: Ravi Bangoria <ravi.bangoria@amd.com>
Assisted-by: LLM
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
Arnaldo Carvalho de Melo 2026-09-24 16:27:57 -03:00
parent 010147f87a
commit 528b1475f9
4 changed files with 31 additions and 4 deletions

View File

@ -21,14 +21,31 @@ struct perf_mem_event perf_mem_events_intel_aux[PERF_MEM_EVENTS__MAX] = {
E(NULL, NULL, NULL, false, 0),
};
/*
* IBS events with exclude_{user,kernel} bits are rejected on hardware
* without the privilege filter unless swfilt is used: the extra tables
* carry the term, selected when the PMU exposes it.
*/
struct perf_mem_event perf_mem_events_amd[PERF_MEM_EVENTS__MAX] = {
E(NULL, NULL, NULL, false, 0),
E(NULL, NULL, NULL, false, 0),
E("mem-ldst", "%s//", NULL, false, 0),
};
struct perf_mem_event perf_mem_events_amd_swfilt[PERF_MEM_EVENTS__MAX] = {
E(NULL, NULL, NULL, false, 0),
E(NULL, NULL, NULL, false, 0),
E("mem-ldst", "%s/swfilt=1/", NULL, false, 0),
};
struct perf_mem_event perf_mem_events_amd_ldlat[PERF_MEM_EVENTS__MAX] = {
E(NULL, NULL, NULL, false, 0),
E(NULL, NULL, NULL, false, 0),
E("mem-ldst", "%s/ldlat=%u/", NULL, true, 0),
};
struct perf_mem_event perf_mem_events_amd_ldlat_swfilt[PERF_MEM_EVENTS__MAX] = {
E(NULL, NULL, NULL, false, 0),
E(NULL, NULL, NULL, false, 0),
E("mem-ldst", "%s/ldlat=%u,swfilt=1/", NULL, true, 0),
};

View File

@ -6,6 +6,8 @@ extern struct perf_mem_event perf_mem_events_intel[PERF_MEM_EVENTS__MAX];
extern struct perf_mem_event perf_mem_events_intel_aux[PERF_MEM_EVENTS__MAX];
extern struct perf_mem_event perf_mem_events_amd[PERF_MEM_EVENTS__MAX];
extern struct perf_mem_event perf_mem_events_amd_swfilt[PERF_MEM_EVENTS__MAX];
extern struct perf_mem_event perf_mem_events_amd_ldlat[PERF_MEM_EVENTS__MAX];
extern struct perf_mem_event perf_mem_events_amd_ldlat_swfilt[PERF_MEM_EVENTS__MAX];
#endif /* _X86_MEM_EVENTS_H */

View File

@ -333,6 +333,7 @@ static void uncore_cha_imc_adjust_cpumask_for_snc(struct perf_pmu *pmu, bool cha
void perf_pmu__arch_init(struct perf_pmu *pmu)
{
struct perf_pmu_caps *ldlat_cap;
bool swfilt_format;
if (!strcmp(pmu->name, INTEL_PT_PMU_NAME)) {
pmu->auxtrace = true;
@ -348,7 +349,10 @@ void perf_pmu__arch_init(struct perf_pmu *pmu)
if (strcmp(pmu->name, "ibs_op"))
return;
pmu->mem_events = perf_mem_events_amd;
swfilt_format = perf_pmu__has_format(pmu, "swfilt");
pmu->mem_events = swfilt_format ?
perf_mem_events_amd_swfilt :
perf_mem_events_amd;
if (!perf_pmu__caps_parse(pmu))
return;
@ -358,7 +362,9 @@ void perf_pmu__arch_init(struct perf_pmu *pmu)
return;
perf_mem_events__loads_ldlat = 0;
pmu->mem_events = perf_mem_events_amd_ldlat;
pmu->mem_events = swfilt_format ?
perf_mem_events_amd_ldlat_swfilt :
perf_mem_events_amd_ldlat;
} else {
if (pmu->is_core) {
if (perf_pmu__have_event(pmu, "mem-loads-aux"))

View File

@ -64,15 +64,17 @@ if (($is_amd >= 1)); then
# --ldlat on AMD:
# o Zen4 and earlier uarch does not support ldlat
# o Even on supported platforms, it's disabled (--ldlat=0) by default.
# o Kernels with the swfilt term add it even when ldlat is not
# supported, so only check ldlat when the term is present.
ldlat=${BASH_REMATCH[1]}
if [[ -n $ldlat ]]; then
if [[ $ldlat == *ldlat=* ]]; then
if ! [[ "$ldlat" =~ ldlat=0 ]]; then
echo "ERROR: ldlat not initialized to 0?"
exit 1
fi
mem_events="$(perf mem record -v --ldlat=150 -e list 2>&1)"
if ! [[ "$mem_events" =~ ^mem-ldst.*ibs_op/ldlat=150/.*available ]]; then
if ! [[ "$mem_events" =~ ^mem-ldst.*ibs_op/ldlat=150[,/].*available ]]; then
echo "ERROR: --ldlat not honored?"
exit 1
fi