IBS support has been enhanced with two new features in upcoming uarch:
1. DataSrc extension and 2. L3 Miss Filtering capability. Both are
indicated by CPUID_Fn8000001B_EAX bit 11.
DataSrc extension provides additional data source details for tagged
load/store operations. Add support for these new bits in perf report/
script raw-dump.
IBS L3 miss filtering works by tagging an instruction on IBS counter
overflow and generating an NMI if the tagged instruction causes an L3
miss. Samples without an L3 miss are discarded and counter is reset
with random value (between 1-15 for fetch pmu and 1-127 for op pmu).
This helps in reducing sampling overhead when user is interested only
in such samples. One of the use case of such filtered samples is to
feed data to page-migration daemon in tiered memory systems.
Add support for L3 miss filtering in IBS driver via new pmu attribute
"l3missonly". Example usage:
# perf record -a -e ibs_op/l3missonly=1/ --raw-samples sleep 5
# perf report -D
Some important points to keep in mind while using L3 miss filtering:
1. Hw internally reset sampling period when tagged instruction does
not cause L3 miss. But there is no way to reconstruct aggregated
sampling period when this happens.
2. L3 miss is not the actual event being counted. Rather, IBS will
count fetch, cycles or uOps depending on the configuration. Thus
sampling period have no direct connection to L3 misses.
1st causes sampling period skew. Thus, I've added warning message at
perf record:
# perf record -c 10000 -C 0 -e ibs_op/l3missonly=1/
WARNING: Hw internally resets sampling period when L3 Miss Filtering is enabled
and tagged operation does not cause L3 Miss. This causes sampling period skew.
User can configure smaller sampling period to get more samples while
using l3missonly.
v1: https://lore.kernel.org/r/[email protected]
v1->v2:
- patch 1 and 2 are new. 1st patch passes on return value of pmu init
functions. 2nd patch refactors pmu attribute code by using
->is_visible() callback.
- Patch 3 and 4 now also uses ->is_visible() callback for pmu format
and capability attributes respectively.
- Other minor improvements suggested by Robert
Ravi Bangoria (8):
perf/amd/ibs: Cascade pmu init functions' return value
perf/amd/ibs: Use ->is_visible callback for dynamic attributes
perf/amd/ibs: Add support for L3 miss filtering
perf/amd/ibs: Advertise zen4_ibs_extensions as pmu capability
attribute
perf record ibs: Warn about sampling period skew
perf header: Parse non-cpu pmu capabilities
perf script ibs: Support new IBS bits in raw trace dump
perf ibs: Fix comment
arch/x86/events/amd/ibs.c | 191 +++++++++++++---
arch/x86/include/asm/amd-ibs.h | 18 +-
arch/x86/include/asm/perf_event.h | 3 +
tools/arch/x86/include/asm/amd-ibs.h | 18 +-
.../Documentation/perf.data-file-format.txt | 18 ++
tools/perf/arch/x86/util/evsel.c | 34 +++
tools/perf/util/amd-sample-raw.c | 68 +++++-
tools/perf/util/env.c | 48 +++-
tools/perf/util/env.h | 11 +
tools/perf/util/evsel.c | 7 +
tools/perf/util/evsel.h | 1 +
tools/perf/util/header.c | 211 ++++++++++++++++++
tools/perf/util/header.h | 1 +
tools/perf/util/pmu.c | 15 +-
tools/perf/util/pmu.h | 2 +
15 files changed, 586 insertions(+), 60 deletions(-)
--
2.27.0
PMU driver can advertise certain feature via capability attribute('caps'
sysfs directory) which can be consumed by userspace tools like perf. Add
zen4_ibs_extensions capability attribute for IBS pmus. This attribute
will be enabled when CPUID_Fn8000001B_EAX[11] is set.
With patch on Zen4:
$ ls /sys/bus/event_source/devices/ibs_op/caps
zen4_ibs_extensions
Signed-off-by: Ravi Bangoria <[email protected]>
---
arch/x86/events/amd/ibs.c | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)
diff --git a/arch/x86/events/amd/ibs.c b/arch/x86/events/amd/ibs.c
index 52d2eb9ff19a..12b0fd4a0328 100644
--- a/arch/x86/events/amd/ibs.c
+++ b/arch/x86/events/amd/ibs.c
@@ -527,8 +527,14 @@ static struct attribute_group empty_format_group = {
.attrs = attrs_empty,
};
+static struct attribute_group empty_caps_group = {
+ .name = "caps",
+ .attrs = attrs_empty,
+};
+
static const struct attribute_group *empty_attr_groups[] = {
&empty_format_group,
+ &empty_caps_group,
NULL,
};
@@ -536,6 +542,7 @@ PMU_FORMAT_ATTR(rand_en, "config:57");
PMU_FORMAT_ATTR(cnt_ctl, "config:19");
PMU_EVENT_ATTR_STRING(l3missonly, fetch_l3missonly, "config:59");
PMU_EVENT_ATTR_STRING(l3missonly, op_l3missonly, "config:16");
+PMU_EVENT_ATTR_STRING(zen4_ibs_extensions, zen4_ibs_extensions, "1");
static umode_t
zen4_ibs_extensions_is_visible(struct kobject *kobj, struct attribute *attr, int i)
@@ -553,6 +560,11 @@ static struct attribute *fetch_l3missonly_attrs[] = {
NULL,
};
+static struct attribute *zen4_ibs_extensions_attrs[] = {
+ &zen4_ibs_extensions.attr.attr,
+ NULL,
+};
+
static struct attribute_group group_rand_en = {
.name = "format",
.attrs = rand_en_attrs,
@@ -564,13 +576,21 @@ static struct attribute_group group_fetch_l3missonly = {
.is_visible = zen4_ibs_extensions_is_visible,
};
+static struct attribute_group group_zen4_ibs_extensions = {
+ .name = "caps",
+ .attrs = zen4_ibs_extensions_attrs,
+ .is_visible = zen4_ibs_extensions_is_visible,
+};
+
static const struct attribute_group *fetch_attr_groups[] = {
&group_rand_en,
+ &empty_caps_group,
NULL,
};
static const struct attribute_group *fetch_attr_update[] = {
&group_fetch_l3missonly,
+ &group_zen4_ibs_extensions,
NULL,
};
@@ -605,6 +625,7 @@ static struct attribute_group group_op_l3missonly = {
static const struct attribute_group *op_attr_update[] = {
&group_cnt_ctl,
&group_op_l3missonly,
+ &group_zen4_ibs_extensions,
NULL,
};
--
2.27.0
s/IBS Op Data 2/IBS Op Data 1/ for MSR 0xc0011035.
Signed-off-by: Ravi Bangoria <[email protected]>
---
arch/x86/include/asm/amd-ibs.h | 2 +-
tools/arch/x86/include/asm/amd-ibs.h | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/arch/x86/include/asm/amd-ibs.h b/arch/x86/include/asm/amd-ibs.h
index b40b2d4ea2ac..f3eb098d63d4 100644
--- a/arch/x86/include/asm/amd-ibs.h
+++ b/arch/x86/include/asm/amd-ibs.h
@@ -52,7 +52,7 @@ union ibs_op_ctl {
};
};
-/* MSR 0xc0011035: IBS Op Data 2 */
+/* MSR 0xc0011035: IBS Op Data 1 */
union ibs_op_data {
__u64 val;
struct {
diff --git a/tools/arch/x86/include/asm/amd-ibs.h b/tools/arch/x86/include/asm/amd-ibs.h
index 21e01cf6162e..9a3312e12e2e 100644
--- a/tools/arch/x86/include/asm/amd-ibs.h
+++ b/tools/arch/x86/include/asm/amd-ibs.h
@@ -52,7 +52,7 @@ union ibs_op_ctl {
};
};
-/* MSR 0xc0011035: IBS Op Data 2 */
+/* MSR 0xc0011035: IBS Op Data 1 */
union ibs_op_data {
__u64 val;
struct {
--
2.27.0
IBS support has been enhanced with two new features in upcoming uarch:
1. DataSrc extension and 2. L3 miss filtering. Additional set of bits
has been introduced in IBS registers to exploit these features.
Interpret those bits while doing perf report/script raw dump.
IBS op pmu ex:
$ sudo ./perf record -c 130 -a -e ibs_op/l3missonly=1/ --raw-samples
$ sudo ./perf report -D
...
ibs_op_ctl: 0000004500070008 MaxCnt 128 L3MissOnly 1 En 1
Val 1 CntCtl 0=cycles CurCnt 69
ibs_op_data: 0000000000710002 CompToRetCtr 2 TagToRetCtr 113
BrnRet 0 RipInvalid 0 BrnFuse 0 Microcode 0
ibs_op_data2: 0000000000000002 CacheHitSt 0=M-state RmtNode 0
DataSrc 2=A peer cache in a near CCX
ibs_op_data3: 000000681d1700a1 LdOp 1 StOp 0 DcL1TlbMiss 0
DcL2TlbMiss 0 DcL1TlbHit2M 0 DcL1TlbHit1G 1 DcL2TlbHit2M 0
DcMiss 1 DcMisAcc 0 DcWcMemAcc 0 DcUcMemAcc 0 DcLockedOp 0
DcMissNoMabAlloc 1 DcLinAddrValid 1 DcPhyAddrValid 1
DcL2TlbHit1G 0 L2Miss 1 SwPf 0 OpMemWidth 8 bytes
OpDcMissOpenMemReqs 7 DcMissLat 104 TlbRefillLat 0
IBS Fetch pmu ex:
$ sudo ./perf record -c 130 -a -e ibs_fetch/l3missonly=1/ --raw-samples
$ sudo ./perf report -D
...
ibs_fetch_ctl: 3c1f00c700080008 MaxCnt 128 Cnt 128 Lat 199
En 1 Val 1 Comp 1 IcMiss 1 PhyAddrValid 1 L1TlbPgSz 4KB
L1TlbMiss 0 L2TlbMiss 0 RandEn 0 L2Miss 1 L3MissOnly 1
FetchOcMiss 1 FetchL3Miss 1
With the DataSrc extensions, the source of data can be decoded among:
- Local L3 or other L1/L2 in CCX.
- A peer cache in a near CCX.
- Data returned from DRAM.
- A peer cache in a far CCX.
- DRAM address map with "long latency" bit set.
- Data returned from MMIO/Config/PCI/APIC.
- Extension Memory (S-Link, GenZ, etc - identified by the CS target
and/or address map at DF's choice).
- Peer Agent Memory.
Signed-off-by: Ravi Bangoria <[email protected]>
---
arch/x86/include/asm/amd-ibs.h | 16 ++++---
tools/arch/x86/include/asm/amd-ibs.h | 16 ++++---
tools/perf/util/amd-sample-raw.c | 68 ++++++++++++++++++++++++----
3 files changed, 80 insertions(+), 20 deletions(-)
diff --git a/arch/x86/include/asm/amd-ibs.h b/arch/x86/include/asm/amd-ibs.h
index 46e1df45efc0..b40b2d4ea2ac 100644
--- a/arch/x86/include/asm/amd-ibs.h
+++ b/arch/x86/include/asm/amd-ibs.h
@@ -29,7 +29,10 @@ union ibs_fetch_ctl {
rand_en:1, /* 57: random tagging enable */
fetch_l2_miss:1,/* 58: L2 miss for sampled fetch
* (needs IbsFetchComp) */
- reserved:5; /* 59-63: reserved */
+ l3_miss_only:1, /* 59: Collect L3 miss samples only */
+ fetch_oc_miss:1,/* 60: Op cache miss for the sampled fetch */
+ fetch_l3_miss:1,/* 61: L3 cache miss for the sampled fetch */
+ reserved:2; /* 62-63: reserved */
};
};
@@ -38,14 +41,14 @@ union ibs_op_ctl {
__u64 val;
struct {
__u64 opmaxcnt:16, /* 0-15: periodic op max. count */
- reserved0:1, /* 16: reserved */
+ l3_miss_only:1, /* 16: Collect L3 miss samples only */
op_en:1, /* 17: op sampling enable */
op_val:1, /* 18: op sample valid */
cnt_ctl:1, /* 19: periodic op counter control */
opmaxcnt_ext:7, /* 20-26: upper 7 bits of periodic op maximum count */
- reserved1:5, /* 27-31: reserved */
+ reserved0:5, /* 27-31: reserved */
opcurcnt:27, /* 32-58: periodic op counter current count */
- reserved2:5; /* 59-63: reserved */
+ reserved1:5; /* 59-63: reserved */
};
};
@@ -71,11 +74,12 @@ union ibs_op_data {
union ibs_op_data2 {
__u64 val;
struct {
- __u64 data_src:3, /* 0-2: data source */
+ __u64 data_src_lo:3, /* 0-2: data source low */
reserved0:1, /* 3: reserved */
rmt_node:1, /* 4: destination node */
cache_hit_st:1, /* 5: cache hit state */
- reserved1:57; /* 5-63: reserved */
+ data_src_hi:2, /* 6-7: data source high */
+ reserved1:56; /* 8-63: reserved */
};
};
diff --git a/tools/arch/x86/include/asm/amd-ibs.h b/tools/arch/x86/include/asm/amd-ibs.h
index 174e7d83fcbd..21e01cf6162e 100644
--- a/tools/arch/x86/include/asm/amd-ibs.h
+++ b/tools/arch/x86/include/asm/amd-ibs.h
@@ -29,7 +29,10 @@ union ibs_fetch_ctl {
rand_en:1, /* 57: random tagging enable */
fetch_l2_miss:1,/* 58: L2 miss for sampled fetch
* (needs IbsFetchComp) */
- reserved:5; /* 59-63: reserved */
+ l3_miss_only:1, /* 59: Collect L3 miss samples only */
+ fetch_oc_miss:1,/* 60: Op cache miss for the sampled fetch */
+ fetch_l3_miss:1,/* 61: L3 cache miss for the sampled fetch */
+ reserved:2; /* 62-63: reserved */
};
};
@@ -38,14 +41,14 @@ union ibs_op_ctl {
__u64 val;
struct {
__u64 opmaxcnt:16, /* 0-15: periodic op max. count */
- reserved0:1, /* 16: reserved */
+ l3_miss_only:1, /* 16: Collect L3 miss samples only */
op_en:1, /* 17: op sampling enable */
op_val:1, /* 18: op sample valid */
cnt_ctl:1, /* 19: periodic op counter control */
opmaxcnt_ext:7, /* 20-26: upper 7 bits of periodic op maximum count */
- reserved1:5, /* 27-31: reserved */
+ reserved0:5, /* 27-31: reserved */
opcurcnt:27, /* 32-58: periodic op counter current count */
- reserved2:5; /* 59-63: reserved */
+ reserved1:5; /* 59-63: reserved */
};
};
@@ -71,11 +74,12 @@ union ibs_op_data {
union ibs_op_data2 {
__u64 val;
struct {
- __u64 data_src:3, /* 0-2: data source */
+ __u64 data_src_lo:3, /* 0-2: data source low */
reserved0:1, /* 3: reserved */
rmt_node:1, /* 4: destination node */
cache_hit_st:1, /* 5: cache hit state */
- reserved1:57; /* 5-63: reserved */
+ data_src_hi:2, /* 6-7: data source high */
+ reserved1:56; /* 8-63: reserved */
};
};
diff --git a/tools/perf/util/amd-sample-raw.c b/tools/perf/util/amd-sample-raw.c
index d19d765195c5..63303f583bc0 100644
--- a/tools/perf/util/amd-sample-raw.c
+++ b/tools/perf/util/amd-sample-raw.c
@@ -18,6 +18,7 @@
#include "pmu-events/pmu-events.h"
static u32 cpu_family, cpu_model, ibs_fetch_type, ibs_op_type;
+static bool zen4_ibs_extensions;
static void pr_ibs_fetch_ctl(union ibs_fetch_ctl reg)
{
@@ -39,6 +40,7 @@ static void pr_ibs_fetch_ctl(union ibs_fetch_ctl reg)
};
const char *ic_miss_str = NULL;
const char *l1tlb_pgsz_str = NULL;
+ char l3_miss_str[sizeof(" L3MissOnly _ FetchOcMiss _ FetchL3Miss _")] = "";
if (cpu_family == 0x19 && cpu_model < 0x10) {
/*
@@ -53,12 +55,19 @@ static void pr_ibs_fetch_ctl(union ibs_fetch_ctl reg)
ic_miss_str = ic_miss_strs[reg.ic_miss];
}
+ if (zen4_ibs_extensions) {
+ snprintf(l3_miss_str, sizeof(l3_miss_str),
+ " L3MissOnly %d FetchOcMiss %d FetchL3Miss %d",
+ reg.l3_miss_only, reg.fetch_oc_miss, reg.fetch_l3_miss);
+ }
+
printf("ibs_fetch_ctl:\t%016llx MaxCnt %7d Cnt %7d Lat %5d En %d Val %d Comp %d%s "
- "PhyAddrValid %d%s L1TlbMiss %d L2TlbMiss %d RandEn %d%s\n",
+ "PhyAddrValid %d%s L1TlbMiss %d L2TlbMiss %d RandEn %d%s%s\n",
reg.val, reg.fetch_maxcnt << 4, reg.fetch_cnt << 4, reg.fetch_lat,
reg.fetch_en, reg.fetch_val, reg.fetch_comp, ic_miss_str ? : "",
reg.phy_addr_valid, l1tlb_pgsz_str ? : "", reg.l1tlb_miss, reg.l2tlb_miss,
- reg.rand_en, reg.fetch_comp ? (reg.fetch_l2_miss ? " L2Miss 1" : " L2Miss 0") : "");
+ reg.rand_en, reg.fetch_comp ? (reg.fetch_l2_miss ? " L2Miss 1" : " L2Miss 0") : "",
+ l3_miss_str);
}
static void pr_ic_ibs_extd_ctl(union ic_ibs_extd_ctl reg)
@@ -68,9 +77,15 @@ static void pr_ic_ibs_extd_ctl(union ic_ibs_extd_ctl reg)
static void pr_ibs_op_ctl(union ibs_op_ctl reg)
{
- printf("ibs_op_ctl:\t%016llx MaxCnt %9d En %d Val %d CntCtl %d=%s CurCnt %9d\n",
- reg.val, ((reg.opmaxcnt_ext << 16) | reg.opmaxcnt) << 4, reg.op_en, reg.op_val,
- reg.cnt_ctl, reg.cnt_ctl ? "uOps" : "cycles", reg.opcurcnt);
+ char l3_miss_only[sizeof(" L3MissOnly _")] = "";
+
+ if (zen4_ibs_extensions)
+ snprintf(l3_miss_only, sizeof(l3_miss_only), " L3MissOnly %d", reg.l3_miss_only);
+
+ printf("ibs_op_ctl:\t%016llx MaxCnt %9d%s En %d Val %d CntCtl %d=%s CurCnt %9d\n",
+ reg.val, ((reg.opmaxcnt_ext << 16) | reg.opmaxcnt) << 4, l3_miss_only,
+ reg.op_en, reg.op_val, reg.cnt_ctl,
+ reg.cnt_ctl ? "uOps" : "cycles", reg.opcurcnt);
}
static void pr_ibs_op_data(union ibs_op_data reg)
@@ -84,7 +99,34 @@ static void pr_ibs_op_data(union ibs_op_data reg)
reg.op_brn_ret, reg.op_rip_invalid, reg.op_brn_fuse, reg.op_microcode);
}
-static void pr_ibs_op_data2(union ibs_op_data2 reg)
+static void pr_ibs_op_data2_extended(union ibs_op_data2 reg)
+{
+ static const char * const data_src_str[] = {
+ "",
+ " DataSrc 1=Local L3 or other L1/L2 in CCX",
+ " DataSrc 2=A peer cache in a near CCX",
+ " DataSrc 3=Data returned from DRAM",
+ " DataSrc 4=(reserved)",
+ " DataSrc 5=A peer cache in a far CCX",
+ " DataSrc 6=DRAM address map with \"long latency\" bit set",
+ " DataSrc 7=Data returned from MMIO/Config/PCI/APIC",
+ " DataSrc 8=Extension Memory (S-Link, GenZ, etc)",
+ " DataSrc 9=(reserved)",
+ " DataSrc 10=(reserved)",
+ " DataSrc 11=(reserved)",
+ " DataSrc 12=Peer Agent Memory",
+ /* 13 to 31 are reserved. Avoid printing them. */
+ };
+ int data_src = (reg.data_src_hi << 3) | reg.data_src_lo;
+
+ printf("ibs_op_data2:\t%016llx %sRmtNode %d%s\n", reg.val,
+ (data_src == 1 || data_src == 2 || data_src == 5) ?
+ (reg.cache_hit_st ? "CacheHitSt 1=O-State " : "CacheHitSt 0=M-state ") : "",
+ reg.rmt_node,
+ data_src < (int)ARRAY_SIZE(data_src_str) ? data_src_str[data_src] : "");
+}
+
+static void pr_ibs_op_data2_default(union ibs_op_data2 reg)
{
static const char * const data_src_str[] = {
"",
@@ -98,9 +140,16 @@ static void pr_ibs_op_data2(union ibs_op_data2 reg)
};
printf("ibs_op_data2:\t%016llx %sRmtNode %d%s\n", reg.val,
- reg.data_src == 2 ? (reg.cache_hit_st ? "CacheHitSt 1=O-State "
+ reg.data_src_lo == 2 ? (reg.cache_hit_st ? "CacheHitSt 1=O-State "
: "CacheHitSt 0=M-state ") : "",
- reg.rmt_node, data_src_str[reg.data_src]);
+ reg.rmt_node, data_src_str[reg.data_src_lo]);
+}
+
+static void pr_ibs_op_data2(union ibs_op_data2 reg)
+{
+ if (zen4_ibs_extensions)
+ return pr_ibs_op_data2_extended(reg);
+ pr_ibs_op_data2_default(reg);
}
static void pr_ibs_op_data3(union ibs_op_data3 reg)
@@ -279,6 +328,9 @@ bool evlist__has_amd_ibs(struct evlist *evlist)
pmu_mapping += strlen(pmu_mapping) + 1 /* '\0' */;
}
+ if (perf_env__find_pmu_cap(env, 0, "ibs_op", "zen4_ibs_extensions"))
+ zen4_ibs_extensions = 1;
+
if (ibs_fetch_type || ibs_op_type) {
if (!cpu_family)
parse_cpuid(env);
--
2.27.0
Pmus advertise their capabilities via sysfs attribute files but
perf tool currently parses only core(cpu) pmu capabilities. Add
support for parsing non-cpu pmu capabilities.
Signed-off-by: Ravi Bangoria <[email protected]>
---
.../Documentation/perf.data-file-format.txt | 18 ++
tools/perf/util/env.c | 48 +++-
tools/perf/util/env.h | 11 +
tools/perf/util/header.c | 211 ++++++++++++++++++
tools/perf/util/header.h | 1 +
tools/perf/util/pmu.c | 15 +-
tools/perf/util/pmu.h | 2 +
7 files changed, 301 insertions(+), 5 deletions(-)
diff --git a/tools/perf/Documentation/perf.data-file-format.txt b/tools/perf/Documentation/perf.data-file-format.txt
index f56d0e0fbff6..dea3acb36558 100644
--- a/tools/perf/Documentation/perf.data-file-format.txt
+++ b/tools/perf/Documentation/perf.data-file-format.txt
@@ -435,6 +435,24 @@ struct {
} [nr_pmu];
};
+ HEADER_PMU_CAPS = 32,
+
+ List of pmu capabilities (except cpu pmu which is already
+ covered by HEADER_CPU_PMU_CAPS)
+
+struct {
+ u32 nr_pmus;
+ struct {
+ u8 core_type; /* For hybrid topology */
+ char pmu_name[];
+ u16 nr_caps;
+ struct {
+ char name[];
+ char value[];
+ } [nr_caps];
+ } [nr_pmus];
+};
+
other bits are reserved and should ignored for now
HEADER_FEAT_BITS = 256,
diff --git a/tools/perf/util/env.c b/tools/perf/util/env.c
index 579e44c59914..928633f07086 100644
--- a/tools/perf/util/env.c
+++ b/tools/perf/util/env.c
@@ -179,7 +179,7 @@ static void perf_env__purge_bpf(struct perf_env *env __maybe_unused)
void perf_env__exit(struct perf_env *env)
{
- int i;
+ int i, j;
perf_env__purge_bpf(env);
perf_env__purge_cgroups(env);
@@ -222,6 +222,14 @@ void perf_env__exit(struct perf_env *env)
zfree(&env->hybrid_cpc_nodes[i].pmu_name);
}
zfree(&env->hybrid_cpc_nodes);
+
+ for (i = 0; i < env->nr_pmus_with_caps; i++) {
+ zfree(&env->env_pmu_caps[i].pmu_name);
+ for (j = 0; j < env->env_pmu_caps[i].nr_caps; j++)
+ zfree(&env->env_pmu_caps[i].pmu_caps[j]);
+ zfree(&env->env_pmu_caps[i].pmu_caps);
+ }
+ zfree(&env->env_pmu_caps);
}
void perf_env__init(struct perf_env *env)
@@ -527,3 +535,41 @@ int perf_env__numa_node(struct perf_env *env, struct perf_cpu cpu)
return cpu.cpu >= 0 && cpu.cpu < env->nr_numa_map ? env->numa_map[cpu.cpu] : -1;
}
+
+char *perf_env__find_pmu_cap(struct perf_env *env, u8 core_type,
+ const char *pmu_name, const char *cap)
+{
+ struct env_pmu_caps *env_pmu_caps = env->env_pmu_caps;
+ char *cap_eq;
+ int cap_size;
+ char **ptr;
+ int i, j;
+
+ if (!pmu_name || !cap)
+ return NULL;
+
+ cap_size = strlen(cap);
+ cap_eq = zalloc(cap_size + 2);
+ if (!cap_eq)
+ return NULL;
+
+ memcpy(cap_eq, cap, cap_size);
+ cap_eq[cap_size] = '=';
+
+ for (i = 0; i < env->nr_pmus_with_caps; i++) {
+ if (env_pmu_caps[i].core_type != core_type ||
+ strcmp(env_pmu_caps[i].pmu_name, pmu_name))
+ continue;
+
+ ptr = env_pmu_caps[i].pmu_caps;
+
+ for (j = 0; j < env_pmu_caps[i].nr_caps; j++) {
+ if (!strncmp(ptr[j], cap_eq, cap_size + 1)) {
+ free(cap_eq);
+ return &ptr[j][cap_size + 1];
+ }
+ }
+ }
+ free(cap_eq);
+ return NULL;
+}
diff --git a/tools/perf/util/env.h b/tools/perf/util/env.h
index a3541f98e1fc..2b767f4ae6e0 100644
--- a/tools/perf/util/env.h
+++ b/tools/perf/util/env.h
@@ -50,6 +50,13 @@ struct hybrid_cpc_node {
char *pmu_name;
};
+struct env_pmu_caps {
+ u8 core_type;
+ char *pmu_name;
+ u16 nr_caps;
+ char **pmu_caps;
+};
+
struct perf_env {
char *hostname;
char *os_release;
@@ -75,6 +82,7 @@ struct perf_env {
int nr_cpu_pmu_caps;
int nr_hybrid_nodes;
int nr_hybrid_cpc_nodes;
+ int nr_pmus_with_caps;
char *cmdline;
const char **cmdline_argv;
char *sibling_cores;
@@ -95,6 +103,7 @@ struct perf_env {
unsigned long long memory_bsize;
struct hybrid_node *hybrid_nodes;
struct hybrid_cpc_node *hybrid_cpc_nodes;
+ struct env_pmu_caps *env_pmu_caps;
#ifdef HAVE_LIBBPF_SUPPORT
/*
* bpf_info_lock protects bpf rbtrees. This is needed because the
@@ -172,4 +181,6 @@ bool perf_env__insert_btf(struct perf_env *env, struct btf_node *btf_node);
struct btf_node *perf_env__find_btf(struct perf_env *env, __u32 btf_id);
int perf_env__numa_node(struct perf_env *env, struct perf_cpu cpu);
+char *perf_env__find_pmu_cap(struct perf_env *env, u8 core_type,
+ const char *pmu_name, const char *cap);
#endif /* __PERF_ENV_H */
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
index a27132e5a5ef..23d89dbfcd96 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -217,6 +217,19 @@ static int __do_read(struct feat_fd *ff, void *addr, ssize_t size)
return __do_read_buf(ff, addr, size);
}
+static int do_read_u16(struct feat_fd *ff, u16 *addr)
+{
+ int ret;
+
+ ret = __do_read(ff, addr, sizeof(*addr));
+ if (ret)
+ return ret;
+
+ if (ff->ph->needs_swap)
+ *addr = bswap_16(*addr);
+ return 0;
+}
+
static int do_read_u32(struct feat_fd *ff, u32 *addr)
{
int ret;
@@ -1580,6 +1593,77 @@ static int write_hybrid_cpu_pmu_caps(struct feat_fd *ff,
return 0;
}
+/*
+ * File format:
+ *
+ * struct {
+ * u32 nr_pmus;
+ * struct {
+ * u8 core_type;
+ * char pmu_name[];
+ * u16 nr_caps;
+ * struct {
+ * char name[];
+ * char value[];
+ * } [nr_caps];
+ * } [nr_pmus];
+ * };
+ */
+static int write_pmu_caps(struct feat_fd *ff, struct evlist *evlist __maybe_unused)
+{
+ struct perf_pmu_caps *caps = NULL;
+ struct perf_pmu *pmu = NULL;
+ u8 core_type = 0;
+ u32 nr_pmus = 0;
+ int ret;
+
+ while ((pmu = perf_pmu__scan(pmu))) {
+ if (!pmu->name || !strncmp(pmu->name, "cpu", 3) ||
+ perf_pmu__caps_parse(pmu) <= 0)
+ continue;
+ nr_pmus++;
+ }
+
+ ret = do_write(ff, &nr_pmus, sizeof(nr_pmus));
+ if (ret < 0)
+ return ret;
+
+ if (!nr_pmus)
+ return 0;
+
+ while ((pmu = perf_pmu__scan(pmu))) {
+ if (!pmu->name || !strncmp(pmu->name, "cpu", 3) || !pmu->nr_caps)
+ continue;
+
+ /*
+ * Currently core_type is always set to 0. But it can be
+ * used in future for hybrid topology pmus.
+ */
+ ret = do_write(ff, &core_type, sizeof(core_type));
+ if (ret < 0)
+ return ret;
+
+ ret = do_write_string(ff, pmu->name);
+ if (ret < 0)
+ return ret;
+
+ ret = do_write(ff, &pmu->nr_caps, sizeof(pmu->nr_caps));
+ if (ret < 0)
+ return ret;
+
+ list_for_each_entry(caps, &pmu->caps, list) {
+ ret = do_write_string(ff, caps->name);
+ if (ret < 0)
+ return ret;
+
+ ret = do_write_string(ff, caps->value);
+ if (ret < 0)
+ return ret;
+ }
+ }
+ return 0;
+}
+
static void print_hostname(struct feat_fd *ff, FILE *fp)
{
fprintf(fp, "# hostname : %s\n", ff->ph->env.hostname);
@@ -2209,6 +2293,31 @@ static void print_mem_topology(struct feat_fd *ff, FILE *fp)
}
}
+static void print_pmu_caps(struct feat_fd *ff, FILE *fp)
+{
+ struct env_pmu_caps *env_pmu_caps = ff->ph->env.env_pmu_caps;
+ int nr_pmus_with_caps = ff->ph->env.nr_pmus_with_caps;
+ const char *delimiter = "";
+ char **ptr;
+ int i, j;
+
+ if (!nr_pmus_with_caps)
+ return;
+
+ for (i = 0; i < nr_pmus_with_caps; i++) {
+ fprintf(fp, "# %s pmu capabilities: ", env_pmu_caps[i].pmu_name);
+
+ ptr = env_pmu_caps[i].pmu_caps;
+
+ delimiter = "";
+ for (j = 0; j < env_pmu_caps[i].nr_caps; j++) {
+ fprintf(fp, "%s%s", delimiter, ptr[j]);
+ delimiter = ", ";
+ }
+ fprintf(fp, "\n");
+ }
+}
+
static int __event_process_build_id(struct perf_record_header_build_id *bev,
char *filename,
struct perf_session *session)
@@ -3319,6 +3428,107 @@ static int process_hybrid_cpu_pmu_caps(struct feat_fd *ff,
return ret;
}
+static int __process_pmu_caps(struct feat_fd *ff, struct env_pmu_caps *env_pmu_caps)
+{
+ u16 nr_caps = env_pmu_caps->nr_caps;
+ int name_size, value_size;
+ char *name, *value, *ptr;
+ u16 i;
+
+ env_pmu_caps->pmu_caps = zalloc(sizeof(char *) * nr_caps);
+ if (!env_pmu_caps->pmu_caps)
+ return -1;
+
+ for (i = 0; i < nr_caps; i++) {
+ name = do_read_string(ff);
+ if (!name)
+ goto error;
+
+ value = do_read_string(ff);
+ if (!value)
+ goto free_name;
+
+ name_size = strlen(name);
+ value_size = strlen(value);
+ ptr = zalloc(sizeof(char) * (name_size + value_size + 2));
+ if (!ptr)
+ goto free_value;
+
+ memcpy(ptr, name, name_size);
+ ptr[name_size] = '=';
+ memcpy(ptr + name_size + 1, value, value_size);
+ env_pmu_caps->pmu_caps[i] = ptr;
+
+ free(value);
+ free(name);
+ }
+ return 0;
+
+free_value:
+ free(value);
+free_name:
+ free(name);
+error:
+ for (; i > 0; i--)
+ free(env_pmu_caps->pmu_caps[i - 1]);
+ free(env_pmu_caps->pmu_caps);
+ return -1;
+}
+
+static int process_pmu_caps(struct feat_fd *ff, void *data __maybe_unused)
+{
+ struct env_pmu_caps *env_pmu_caps;
+ u32 nr_pmus;
+ u32 i;
+ u16 j;
+
+ ff->ph->env.nr_pmus_with_caps = 0;
+ ff->ph->env.env_pmu_caps = NULL;
+
+ if (do_read_u32(ff, &nr_pmus))
+ return -1;
+
+ if (!nr_pmus)
+ return 0;
+
+ env_pmu_caps = zalloc(sizeof(struct env_pmu_caps) * nr_pmus);
+ if (!env_pmu_caps)
+ return -ENOMEM;
+
+ for (i = 0; i < nr_pmus; i++) {
+ if (__do_read(ff, &env_pmu_caps[i].core_type, sizeof(env_pmu_caps[i].core_type)))
+ goto error;
+
+ env_pmu_caps[i].pmu_name = do_read_string(ff);
+ if (!env_pmu_caps[i].pmu_name)
+ goto error;
+
+ if (do_read_u16(ff, &env_pmu_caps[i].nr_caps))
+ goto free_pmu_name;
+
+ if (!__process_pmu_caps(ff, &env_pmu_caps[i]))
+ continue;
+
+free_pmu_name:
+ free(env_pmu_caps[i].pmu_name);
+ goto error;
+ }
+
+ ff->ph->env.nr_pmus_with_caps = nr_pmus;
+ ff->ph->env.env_pmu_caps = env_pmu_caps;
+ return 0;
+
+error:
+ for (; i > 0; i--) {
+ free(env_pmu_caps[i - 1].pmu_name);
+ for (j = 0; j < env_pmu_caps[i - 1].nr_caps; j++)
+ free(env_pmu_caps[i - 1].pmu_caps[j]);
+ free(env_pmu_caps[i - 1].pmu_caps);
+ }
+ free(env_pmu_caps);
+ return -1;
+}
+
#define FEAT_OPR(n, func, __full_only) \
[HEADER_##n] = { \
.name = __stringify(n), \
@@ -3382,6 +3592,7 @@ const struct perf_header_feature_ops feat_ops[HEADER_LAST_FEATURE] = {
FEAT_OPR(CLOCK_DATA, clock_data, false),
FEAT_OPN(HYBRID_TOPOLOGY, hybrid_topology, true),
FEAT_OPR(HYBRID_CPU_PMU_CAPS, hybrid_cpu_pmu_caps, false),
+ FEAT_OPR(PMU_CAPS, pmu_caps, false),
};
struct header_print_data {
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h
index 0eb4bc29a5a4..e9a067bb8b9e 100644
--- a/tools/perf/util/header.h
+++ b/tools/perf/util/header.h
@@ -47,6 +47,7 @@ enum {
HEADER_CLOCK_DATA,
HEADER_HYBRID_TOPOLOGY,
HEADER_HYBRID_CPU_PMU_CAPS,
+ HEADER_PMU_CAPS,
HEADER_LAST_FEATURE,
HEADER_FEAT_BITS = 256,
};
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
index 9a1c7e63e663..8d599acb7569 100644
--- a/tools/perf/util/pmu.c
+++ b/tools/perf/util/pmu.c
@@ -1890,16 +1890,22 @@ int perf_pmu__caps_parse(struct perf_pmu *pmu)
const char *sysfs = sysfs__mountpoint();
DIR *caps_dir;
struct dirent *evt_ent;
- int nr_caps = 0;
+
+ if (pmu->caps_initialized)
+ return pmu->nr_caps;
if (!sysfs)
return -1;
+ pmu->nr_caps = 0;
+
snprintf(caps_path, PATH_MAX,
"%s" EVENT_SOURCE_DEVICE_PATH "%s/caps", sysfs, pmu->name);
- if (stat(caps_path, &st) < 0)
+ if (stat(caps_path, &st) < 0) {
+ pmu->caps_initialized = true;
return 0; /* no error if caps does not exist */
+ }
caps_dir = opendir(caps_path);
if (!caps_dir)
@@ -1926,13 +1932,14 @@ int perf_pmu__caps_parse(struct perf_pmu *pmu)
continue;
}
- nr_caps++;
+ pmu->nr_caps++;
fclose(file);
}
closedir(caps_dir);
- return nr_caps;
+ pmu->caps_initialized = true;
+ return pmu->nr_caps;
}
void perf_pmu__warn_invalid_config(struct perf_pmu *pmu, __u64 config,
diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h
index 541889fa9f9c..593005e68bea 100644
--- a/tools/perf/util/pmu.h
+++ b/tools/perf/util/pmu.h
@@ -46,6 +46,8 @@ struct perf_pmu {
struct perf_cpu_map *cpus;
struct list_head format; /* HEAD struct perf_pmu_format -> list */
struct list_head aliases; /* HEAD struct perf_pmu_alias -> list */
+ bool caps_initialized;
+ u16 nr_caps;
struct list_head caps; /* HEAD struct perf_pmu_caps -> list */
struct list_head list; /* ELEM */
struct list_head hybrid_list;
--
2.27.0
The following commit has been merged into the perf/core branch of tip:
Commit-ID: 838de1d843fc9b6161e0e1c6308a8c027d08606d
Gitweb: https://git.kernel.org/tip/838de1d843fc9b6161e0e1c6308a8c027d08606d
Author: Ravi Bangoria <[email protected]>
AuthorDate: Mon, 09 May 2022 10:19:10 +05:30
Committer: Peter Zijlstra <[email protected]>
CommitterDate: Wed, 11 May 2022 16:27:10 +02:00
perf/amd/ibs: Advertise zen4_ibs_extensions as pmu capability attribute
PMU driver can advertise certain feature via capability attribute('caps'
sysfs directory) which can be consumed by userspace tools like perf. Add
zen4_ibs_extensions capability attribute for IBS pmus. This attribute
will be enabled when CPUID_Fn8000001B_EAX[11] is set.
With patch on Zen4:
$ ls /sys/bus/event_source/devices/ibs_op/caps
zen4_ibs_extensions
Signed-off-by: Ravi Bangoria <[email protected]>
Signed-off-by: Peter Zijlstra (Intel) <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
---
arch/x86/events/amd/ibs.c | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)
diff --git a/arch/x86/events/amd/ibs.c b/arch/x86/events/amd/ibs.c
index 2dc8b7e..c251bc4 100644
--- a/arch/x86/events/amd/ibs.c
+++ b/arch/x86/events/amd/ibs.c
@@ -537,8 +537,14 @@ static struct attribute_group empty_format_group = {
.attrs = attrs_empty,
};
+static struct attribute_group empty_caps_group = {
+ .name = "caps",
+ .attrs = attrs_empty,
+};
+
static const struct attribute_group *empty_attr_groups[] = {
&empty_format_group,
+ &empty_caps_group,
NULL,
};
@@ -546,6 +552,7 @@ PMU_FORMAT_ATTR(rand_en, "config:57");
PMU_FORMAT_ATTR(cnt_ctl, "config:19");
PMU_EVENT_ATTR_STRING(l3missonly, fetch_l3missonly, "config:59");
PMU_EVENT_ATTR_STRING(l3missonly, op_l3missonly, "config:16");
+PMU_EVENT_ATTR_STRING(zen4_ibs_extensions, zen4_ibs_extensions, "1");
static umode_t
zen4_ibs_extensions_is_visible(struct kobject *kobj, struct attribute *attr, int i)
@@ -563,6 +570,11 @@ static struct attribute *fetch_l3missonly_attrs[] = {
NULL,
};
+static struct attribute *zen4_ibs_extensions_attrs[] = {
+ &zen4_ibs_extensions.attr.attr,
+ NULL,
+};
+
static struct attribute_group group_rand_en = {
.name = "format",
.attrs = rand_en_attrs,
@@ -574,13 +586,21 @@ static struct attribute_group group_fetch_l3missonly = {
.is_visible = zen4_ibs_extensions_is_visible,
};
+static struct attribute_group group_zen4_ibs_extensions = {
+ .name = "caps",
+ .attrs = zen4_ibs_extensions_attrs,
+ .is_visible = zen4_ibs_extensions_is_visible,
+};
+
static const struct attribute_group *fetch_attr_groups[] = {
&group_rand_en,
+ &empty_caps_group,
NULL,
};
static const struct attribute_group *fetch_attr_update[] = {
&group_fetch_l3missonly,
+ &group_zen4_ibs_extensions,
NULL,
};
@@ -615,6 +635,7 @@ static struct attribute_group group_op_l3missonly = {
static const struct attribute_group *op_attr_update[] = {
&group_cnt_ctl,
&group_op_l3missonly,
+ &group_zen4_ibs_extensions,
NULL,
};
The following commit has been merged into the perf/core branch of tip:
Commit-ID: 9cb23f598c641c1dcbe18defd219cdc439bc94a8
Gitweb: https://git.kernel.org/tip/9cb23f598c641c1dcbe18defd219cdc439bc94a8
Author: Ravi Bangoria <[email protected]>
AuthorDate: Mon, 09 May 2022 10:19:14 +05:30
Committer: Peter Zijlstra <[email protected]>
CommitterDate: Wed, 11 May 2022 16:27:10 +02:00
perf/ibs: Fix comment
s/IBS Op Data 2/IBS Op Data 1/ for MSR 0xc0011035.
Signed-off-by: Ravi Bangoria <[email protected]>
Signed-off-by: Peter Zijlstra (Intel) <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
---
arch/x86/include/asm/amd-ibs.h | 2 +-
tools/arch/x86/include/asm/amd-ibs.h | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/arch/x86/include/asm/amd-ibs.h b/arch/x86/include/asm/amd-ibs.h
index 46e1df4..aabdbb5 100644
--- a/arch/x86/include/asm/amd-ibs.h
+++ b/arch/x86/include/asm/amd-ibs.h
@@ -49,7 +49,7 @@ union ibs_op_ctl {
};
};
-/* MSR 0xc0011035: IBS Op Data 2 */
+/* MSR 0xc0011035: IBS Op Data 1 */
union ibs_op_data {
__u64 val;
struct {
diff --git a/tools/arch/x86/include/asm/amd-ibs.h b/tools/arch/x86/include/asm/amd-ibs.h
index 174e7d8..765e9e7 100644
--- a/tools/arch/x86/include/asm/amd-ibs.h
+++ b/tools/arch/x86/include/asm/amd-ibs.h
@@ -49,7 +49,7 @@ union ibs_op_ctl {
};
};
-/* MSR 0xc0011035: IBS Op Data 2 */
+/* MSR 0xc0011035: IBS Op Data 1 */
union ibs_op_data {
__u64 val;
struct {
On 09-May-22 10:19 AM, Ravi Bangoria wrote:
> Pmus advertise their capabilities via sysfs attribute files but
> perf tool currently parses only core(cpu) pmu capabilities. Add
> support for parsing non-cpu pmu capabilities.
Arnaldo, Jiri,
Does tool side patches looks good to you? Please consider pulling them.
Thanks,
Ravi
On 16-May-22 6:59 PM, Arnaldo Carvalho de Melo wrote:
> Em Mon, May 09, 2022 at 10:19:13AM +0530, Ravi Bangoria escreveu:
>>
>> Signed-off-by: Ravi Bangoria <[email protected]>
>> ---
>> arch/x86/include/asm/amd-ibs.h | 16 ++++---
>> tools/arch/x86/include/asm/amd-ibs.h | 16 ++++---
>> tools/perf/util/amd-sample-raw.c | 68 ++++++++++++++++++++++++----
>> 3 files changed, 80 insertions(+), 20 deletions(-)
>
> Please separate the tooling part on a separate patch.
Sure.
Thanks,
Ravi
On 16-May-22 6:58 PM, Arnaldo Carvalho de Melo wrote:
> Em Mon, May 09, 2022 at 10:19:12AM +0530, Ravi Bangoria escreveu:
>> Pmus advertise their capabilities via sysfs attribute files but
>> perf tool currently parses only core(cpu) pmu capabilities. Add
>> support for parsing non-cpu pmu capabilities.
>>
>> Signed-off-by: Ravi Bangoria <[email protected]>
>> ---
>> .../Documentation/perf.data-file-format.txt | 18 ++
>> tools/perf/util/env.c | 48 +++-
>> tools/perf/util/env.h | 11 +
>> tools/perf/util/header.c | 211 ++++++++++++++++++
>> tools/perf/util/header.h | 1 +
>> tools/perf/util/pmu.c | 15 +-
>> tools/perf/util/pmu.h | 2 +
>> 7 files changed, 301 insertions(+), 5 deletions(-)
>>
>> diff --git a/tools/perf/Documentation/perf.data-file-format.txt b/tools/perf/Documentation/perf.data-file-format.txt
>> index f56d0e0fbff6..dea3acb36558 100644
>> --- a/tools/perf/Documentation/perf.data-file-format.txt
>> +++ b/tools/perf/Documentation/perf.data-file-format.txt
>> @@ -435,6 +435,24 @@ struct {
>> } [nr_pmu];
>> };
>>
>> + HEADER_PMU_CAPS = 32,
>> +
>> + List of pmu capabilities (except cpu pmu which is already
>> + covered by HEADER_CPU_PMU_CAPS)
>> +
>> +struct {
>> + u32 nr_pmus;
>> + struct {
>> + u8 core_type; /* For hybrid topology */
>
> Humm, I'd say use u32 here and..
>
>> + char pmu_name[];
>> + u16 nr_caps;
>
> Here, no need to save space here, I guess.
Yeah I know it's not a biggie but fwiw I thoughtfully allocated space.
256 types should be more than enough for core_type. Similarly no real
pmu will have more than 65,536 capabilities :)
Anyway, will convert them to u32.
Thanks,
Ravi
Em Mon, May 09, 2022 at 10:19:13AM +0530, Ravi Bangoria escreveu:
>
> Signed-off-by: Ravi Bangoria <[email protected]>
> ---
> arch/x86/include/asm/amd-ibs.h | 16 ++++---
> tools/arch/x86/include/asm/amd-ibs.h | 16 ++++---
> tools/perf/util/amd-sample-raw.c | 68 ++++++++++++++++++++++++----
> 3 files changed, 80 insertions(+), 20 deletions(-)
Please separate the tooling part on a separate patch.
- Arnaldo
Em Mon, May 09, 2022 at 10:19:12AM +0530, Ravi Bangoria escreveu:
> Pmus advertise their capabilities via sysfs attribute files but
> perf tool currently parses only core(cpu) pmu capabilities. Add
> support for parsing non-cpu pmu capabilities.
>
> Signed-off-by: Ravi Bangoria <[email protected]>
> ---
> .../Documentation/perf.data-file-format.txt | 18 ++
> tools/perf/util/env.c | 48 +++-
> tools/perf/util/env.h | 11 +
> tools/perf/util/header.c | 211 ++++++++++++++++++
> tools/perf/util/header.h | 1 +
> tools/perf/util/pmu.c | 15 +-
> tools/perf/util/pmu.h | 2 +
> 7 files changed, 301 insertions(+), 5 deletions(-)
>
> diff --git a/tools/perf/Documentation/perf.data-file-format.txt b/tools/perf/Documentation/perf.data-file-format.txt
> index f56d0e0fbff6..dea3acb36558 100644
> --- a/tools/perf/Documentation/perf.data-file-format.txt
> +++ b/tools/perf/Documentation/perf.data-file-format.txt
> @@ -435,6 +435,24 @@ struct {
> } [nr_pmu];
> };
>
> + HEADER_PMU_CAPS = 32,
> +
> + List of pmu capabilities (except cpu pmu which is already
> + covered by HEADER_CPU_PMU_CAPS)
> +
> +struct {
> + u32 nr_pmus;
> + struct {
> + u8 core_type; /* For hybrid topology */
Humm, I'd say use u32 here and..
> + char pmu_name[];
> + u16 nr_caps;
Here, no need to save space here, I guess.
> + struct {
> + char name[];
> + char value[];
> + } [nr_caps];
> + } [nr_pmus];
> +};
> +
> other bits are reserved and should ignored for now
> HEADER_FEAT_BITS = 256,
>
> diff --git a/tools/perf/util/env.c b/tools/perf/util/env.c
> index 579e44c59914..928633f07086 100644
> --- a/tools/perf/util/env.c
> +++ b/tools/perf/util/env.c
> @@ -179,7 +179,7 @@ static void perf_env__purge_bpf(struct perf_env *env __maybe_unused)
>
> void perf_env__exit(struct perf_env *env)
> {
> - int i;
> + int i, j;
>
> perf_env__purge_bpf(env);
> perf_env__purge_cgroups(env);
> @@ -222,6 +222,14 @@ void perf_env__exit(struct perf_env *env)
> zfree(&env->hybrid_cpc_nodes[i].pmu_name);
> }
> zfree(&env->hybrid_cpc_nodes);
> +
> + for (i = 0; i < env->nr_pmus_with_caps; i++) {
> + zfree(&env->env_pmu_caps[i].pmu_name);
> + for (j = 0; j < env->env_pmu_caps[i].nr_caps; j++)
> + zfree(&env->env_pmu_caps[i].pmu_caps[j]);
> + zfree(&env->env_pmu_caps[i].pmu_caps);
> + }
> + zfree(&env->env_pmu_caps);
> }
>
> void perf_env__init(struct perf_env *env)
> @@ -527,3 +535,41 @@ int perf_env__numa_node(struct perf_env *env, struct perf_cpu cpu)
>
> return cpu.cpu >= 0 && cpu.cpu < env->nr_numa_map ? env->numa_map[cpu.cpu] : -1;
> }
> +
> +char *perf_env__find_pmu_cap(struct perf_env *env, u8 core_type,
> + const char *pmu_name, const char *cap)
> +{
> + struct env_pmu_caps *env_pmu_caps = env->env_pmu_caps;
> + char *cap_eq;
> + int cap_size;
> + char **ptr;
> + int i, j;
> +
> + if (!pmu_name || !cap)
> + return NULL;
> +
> + cap_size = strlen(cap);
> + cap_eq = zalloc(cap_size + 2);
> + if (!cap_eq)
> + return NULL;
> +
> + memcpy(cap_eq, cap, cap_size);
> + cap_eq[cap_size] = '=';
> +
> + for (i = 0; i < env->nr_pmus_with_caps; i++) {
> + if (env_pmu_caps[i].core_type != core_type ||
> + strcmp(env_pmu_caps[i].pmu_name, pmu_name))
> + continue;
> +
> + ptr = env_pmu_caps[i].pmu_caps;
> +
> + for (j = 0; j < env_pmu_caps[i].nr_caps; j++) {
> + if (!strncmp(ptr[j], cap_eq, cap_size + 1)) {
> + free(cap_eq);
> + return &ptr[j][cap_size + 1];
> + }
> + }
> + }
> + free(cap_eq);
> + return NULL;
> +}
> diff --git a/tools/perf/util/env.h b/tools/perf/util/env.h
> index a3541f98e1fc..2b767f4ae6e0 100644
> --- a/tools/perf/util/env.h
> +++ b/tools/perf/util/env.h
> @@ -50,6 +50,13 @@ struct hybrid_cpc_node {
> char *pmu_name;
> };
>
> +struct env_pmu_caps {
> + u8 core_type;
> + char *pmu_name;
> + u16 nr_caps;
> + char **pmu_caps;
> +};
> +
> struct perf_env {
> char *hostname;
> char *os_release;
> @@ -75,6 +82,7 @@ struct perf_env {
> int nr_cpu_pmu_caps;
> int nr_hybrid_nodes;
> int nr_hybrid_cpc_nodes;
> + int nr_pmus_with_caps;
> char *cmdline;
> const char **cmdline_argv;
> char *sibling_cores;
> @@ -95,6 +103,7 @@ struct perf_env {
> unsigned long long memory_bsize;
> struct hybrid_node *hybrid_nodes;
> struct hybrid_cpc_node *hybrid_cpc_nodes;
> + struct env_pmu_caps *env_pmu_caps;
> #ifdef HAVE_LIBBPF_SUPPORT
> /*
> * bpf_info_lock protects bpf rbtrees. This is needed because the
> @@ -172,4 +181,6 @@ bool perf_env__insert_btf(struct perf_env *env, struct btf_node *btf_node);
> struct btf_node *perf_env__find_btf(struct perf_env *env, __u32 btf_id);
>
> int perf_env__numa_node(struct perf_env *env, struct perf_cpu cpu);
> +char *perf_env__find_pmu_cap(struct perf_env *env, u8 core_type,
> + const char *pmu_name, const char *cap);
> #endif /* __PERF_ENV_H */
> diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
> index a27132e5a5ef..23d89dbfcd96 100644
> --- a/tools/perf/util/header.c
> +++ b/tools/perf/util/header.c
> @@ -217,6 +217,19 @@ static int __do_read(struct feat_fd *ff, void *addr, ssize_t size)
> return __do_read_buf(ff, addr, size);
> }
>
> +static int do_read_u16(struct feat_fd *ff, u16 *addr)
> +{
> + int ret;
> +
> + ret = __do_read(ff, addr, sizeof(*addr));
> + if (ret)
> + return ret;
> +
> + if (ff->ph->needs_swap)
> + *addr = bswap_16(*addr);
> + return 0;
> +}
> +
You will not need the do_read_u16, use do_read_u32.
Then change the other parts to the u32 type.
- Arnaldo
> static int do_read_u32(struct feat_fd *ff, u32 *addr)
> {
> int ret;
> @@ -1580,6 +1593,77 @@ static int write_hybrid_cpu_pmu_caps(struct feat_fd *ff,
> return 0;
> }
>
> +/*
> + * File format:
> + *
> + * struct {
> + * u32 nr_pmus;
> + * struct {
> + * u8 core_type;
> + * char pmu_name[];
> + * u16 nr_caps;
Update here.
> + * struct {
> + * char name[];
> + * char value[];
> + * } [nr_caps];
> + * } [nr_pmus];
> + * };
> + */
> +static int write_pmu_caps(struct feat_fd *ff, struct evlist *evlist __maybe_unused)
> +{
> + struct perf_pmu_caps *caps = NULL;
> + struct perf_pmu *pmu = NULL;
> + u8 core_type = 0;
> + u32 nr_pmus = 0;
> + int ret;
> +
> + while ((pmu = perf_pmu__scan(pmu))) {
> + if (!pmu->name || !strncmp(pmu->name, "cpu", 3) ||
> + perf_pmu__caps_parse(pmu) <= 0)
> + continue;
> + nr_pmus++;
> + }
> +
> + ret = do_write(ff, &nr_pmus, sizeof(nr_pmus));
> + if (ret < 0)
> + return ret;
> +
> + if (!nr_pmus)
> + return 0;
> +
> + while ((pmu = perf_pmu__scan(pmu))) {
> + if (!pmu->name || !strncmp(pmu->name, "cpu", 3) || !pmu->nr_caps)
> + continue;
> +
> + /*
> + * Currently core_type is always set to 0. But it can be
> + * used in future for hybrid topology pmus.
> + */
> + ret = do_write(ff, &core_type, sizeof(core_type));
> + if (ret < 0)
> + return ret;
> +
> + ret = do_write_string(ff, pmu->name);
> + if (ret < 0)
> + return ret;
> +
> + ret = do_write(ff, &pmu->nr_caps, sizeof(pmu->nr_caps));
> + if (ret < 0)
> + return ret;
> +
> + list_for_each_entry(caps, &pmu->caps, list) {
> + ret = do_write_string(ff, caps->name);
> + if (ret < 0)
> + return ret;
> +
> + ret = do_write_string(ff, caps->value);
> + if (ret < 0)
> + return ret;
> + }
> + }
> + return 0;
> +}
> +
> static void print_hostname(struct feat_fd *ff, FILE *fp)
> {
> fprintf(fp, "# hostname : %s\n", ff->ph->env.hostname);
> @@ -2209,6 +2293,31 @@ static void print_mem_topology(struct feat_fd *ff, FILE *fp)
> }
> }
>
> +static void print_pmu_caps(struct feat_fd *ff, FILE *fp)
> +{
> + struct env_pmu_caps *env_pmu_caps = ff->ph->env.env_pmu_caps;
> + int nr_pmus_with_caps = ff->ph->env.nr_pmus_with_caps;
> + const char *delimiter = "";
> + char **ptr;
> + int i, j;
> +
> + if (!nr_pmus_with_caps)
> + return;
> +
> + for (i = 0; i < nr_pmus_with_caps; i++) {
> + fprintf(fp, "# %s pmu capabilities: ", env_pmu_caps[i].pmu_name);
> +
> + ptr = env_pmu_caps[i].pmu_caps;
> +
> + delimiter = "";
> + for (j = 0; j < env_pmu_caps[i].nr_caps; j++) {
> + fprintf(fp, "%s%s", delimiter, ptr[j]);
> + delimiter = ", ";
> + }
> + fprintf(fp, "\n");
> + }
> +}
> +
> static int __event_process_build_id(struct perf_record_header_build_id *bev,
> char *filename,
> struct perf_session *session)
> @@ -3319,6 +3428,107 @@ static int process_hybrid_cpu_pmu_caps(struct feat_fd *ff,
> return ret;
> }
>
> +static int __process_pmu_caps(struct feat_fd *ff, struct env_pmu_caps *env_pmu_caps)
> +{
> + u16 nr_caps = env_pmu_caps->nr_caps;
> + int name_size, value_size;
> + char *name, *value, *ptr;
> + u16 i;
> +
> + env_pmu_caps->pmu_caps = zalloc(sizeof(char *) * nr_caps);
> + if (!env_pmu_caps->pmu_caps)
> + return -1;
> +
> + for (i = 0; i < nr_caps; i++) {
> + name = do_read_string(ff);
> + if (!name)
> + goto error;
> +
> + value = do_read_string(ff);
> + if (!value)
> + goto free_name;
> +
> + name_size = strlen(name);
> + value_size = strlen(value);
> + ptr = zalloc(sizeof(char) * (name_size + value_size + 2));
> + if (!ptr)
> + goto free_value;
> +
> + memcpy(ptr, name, name_size);
> + ptr[name_size] = '=';
> + memcpy(ptr + name_size + 1, value, value_size);
> + env_pmu_caps->pmu_caps[i] = ptr;
> +
> + free(value);
> + free(name);
> + }
> + return 0;
> +
> +free_value:
> + free(value);
> +free_name:
> + free(name);
> +error:
> + for (; i > 0; i--)
> + free(env_pmu_caps->pmu_caps[i - 1]);
> + free(env_pmu_caps->pmu_caps);
> + return -1;
> +}
> +
> +static int process_pmu_caps(struct feat_fd *ff, void *data __maybe_unused)
> +{
> + struct env_pmu_caps *env_pmu_caps;
> + u32 nr_pmus;
> + u32 i;
> + u16 j;
> +
> + ff->ph->env.nr_pmus_with_caps = 0;
> + ff->ph->env.env_pmu_caps = NULL;
> +
> + if (do_read_u32(ff, &nr_pmus))
> + return -1;
> +
> + if (!nr_pmus)
> + return 0;
> +
> + env_pmu_caps = zalloc(sizeof(struct env_pmu_caps) * nr_pmus);
> + if (!env_pmu_caps)
> + return -ENOMEM;
> +
> + for (i = 0; i < nr_pmus; i++) {
> + if (__do_read(ff, &env_pmu_caps[i].core_type, sizeof(env_pmu_caps[i].core_type)))
> + goto error;
> +
> + env_pmu_caps[i].pmu_name = do_read_string(ff);
> + if (!env_pmu_caps[i].pmu_name)
> + goto error;
> +
> + if (do_read_u16(ff, &env_pmu_caps[i].nr_caps))
> + goto free_pmu_name;
> +
> + if (!__process_pmu_caps(ff, &env_pmu_caps[i]))
> + continue;
> +
> +free_pmu_name:
> + free(env_pmu_caps[i].pmu_name);
> + goto error;
> + }
> +
> + ff->ph->env.nr_pmus_with_caps = nr_pmus;
> + ff->ph->env.env_pmu_caps = env_pmu_caps;
> + return 0;
> +
> +error:
> + for (; i > 0; i--) {
> + free(env_pmu_caps[i - 1].pmu_name);
> + for (j = 0; j < env_pmu_caps[i - 1].nr_caps; j++)
> + free(env_pmu_caps[i - 1].pmu_caps[j]);
> + free(env_pmu_caps[i - 1].pmu_caps);
> + }
> + free(env_pmu_caps);
> + return -1;
> +}
> +
> #define FEAT_OPR(n, func, __full_only) \
> [HEADER_##n] = { \
> .name = __stringify(n), \
> @@ -3382,6 +3592,7 @@ const struct perf_header_feature_ops feat_ops[HEADER_LAST_FEATURE] = {
> FEAT_OPR(CLOCK_DATA, clock_data, false),
> FEAT_OPN(HYBRID_TOPOLOGY, hybrid_topology, true),
> FEAT_OPR(HYBRID_CPU_PMU_CAPS, hybrid_cpu_pmu_caps, false),
> + FEAT_OPR(PMU_CAPS, pmu_caps, false),
> };
>
> struct header_print_data {
> diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h
> index 0eb4bc29a5a4..e9a067bb8b9e 100644
> --- a/tools/perf/util/header.h
> +++ b/tools/perf/util/header.h
> @@ -47,6 +47,7 @@ enum {
> HEADER_CLOCK_DATA,
> HEADER_HYBRID_TOPOLOGY,
> HEADER_HYBRID_CPU_PMU_CAPS,
> + HEADER_PMU_CAPS,
> HEADER_LAST_FEATURE,
> HEADER_FEAT_BITS = 256,
> };
> diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
> index 9a1c7e63e663..8d599acb7569 100644
> --- a/tools/perf/util/pmu.c
> +++ b/tools/perf/util/pmu.c
> @@ -1890,16 +1890,22 @@ int perf_pmu__caps_parse(struct perf_pmu *pmu)
> const char *sysfs = sysfs__mountpoint();
> DIR *caps_dir;
> struct dirent *evt_ent;
> - int nr_caps = 0;
> +
> + if (pmu->caps_initialized)
> + return pmu->nr_caps;
>
> if (!sysfs)
> return -1;
>
> + pmu->nr_caps = 0;
> +
> snprintf(caps_path, PATH_MAX,
> "%s" EVENT_SOURCE_DEVICE_PATH "%s/caps", sysfs, pmu->name);
>
> - if (stat(caps_path, &st) < 0)
> + if (stat(caps_path, &st) < 0) {
> + pmu->caps_initialized = true;
> return 0; /* no error if caps does not exist */
> + }
>
> caps_dir = opendir(caps_path);
> if (!caps_dir)
> @@ -1926,13 +1932,14 @@ int perf_pmu__caps_parse(struct perf_pmu *pmu)
> continue;
> }
>
> - nr_caps++;
> + pmu->nr_caps++;
> fclose(file);
> }
>
> closedir(caps_dir);
>
> - return nr_caps;
> + pmu->caps_initialized = true;
> + return pmu->nr_caps;
> }
>
> void perf_pmu__warn_invalid_config(struct perf_pmu *pmu, __u64 config,
> diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h
> index 541889fa9f9c..593005e68bea 100644
> --- a/tools/perf/util/pmu.h
> +++ b/tools/perf/util/pmu.h
> @@ -46,6 +46,8 @@ struct perf_pmu {
> struct perf_cpu_map *cpus;
> struct list_head format; /* HEAD struct perf_pmu_format -> list */
> struct list_head aliases; /* HEAD struct perf_pmu_alias -> list */
> + bool caps_initialized;
> + u16 nr_caps;
> struct list_head caps; /* HEAD struct perf_pmu_caps -> list */
> struct list_head list; /* ELEM */
> struct list_head hybrid_list;
> --
> 2.27.0
--
- Arnaldo
Em Mon, May 16, 2022 at 09:45:44AM +0530, Ravi Bangoria escreveu:
>
> On 09-May-22 10:19 AM, Ravi Bangoria wrote:
> > Pmus advertise their capabilities via sysfs attribute files but
> > perf tool currently parses only core(cpu) pmu capabilities. Add
> > support for parsing non-cpu pmu capabilities.
>
> Arnaldo, Jiri,
>
> Does tool side patches looks good to you? Please consider pulling them.
So the kernel part was merged, ok, will look into it.
- Arnaldo