Linux 内核 HiSilicon SoC Uncore PMU 实战指南:L3C/HHA/DDRC 的 perf 事件采集与过滤配置
【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux
HiSilicon SoC 内部的 L3 缓存(L3C)、Hydra Home Agent(HHA)与 DDR 控制器(DDRC)等系统级设备各自内置独立的 uncore 性能监控单元(PMU)。本文基于内核文档 Documentation/admin-guide/perf/hisi-pmu.rst 与驱动源码 drivers/perf/hisilicon/,完整讲解 HiSilicon uncore PMU 的 SoC 拓扑、sysfs 事件发现机制、perf list/perf stat的使用方法,以及 PMU v2(identifier0x30)与 v3(identifier0x40)新增的tt_core、tt_req、datasrc_cfg、ext、srcid/tgtid等过滤参数,帮助你在 HiSilicon 平台上对 NoC、缓存与内存子系统做精细的性能剖析。
HiSilicon SoC 拓扑:SCCL、CCL 与 uncore 设备
理解 uncore PMU 之前,先要理解 HiSilicon SoC 的层次化封装结构。根据官方文档的描述:
- HiSilicon SoC 芯片集成了多种相互独立的系统设备 PMU,例如 L3 缓存(L3C)、Hydra Home Agent(HHA)和 DDRC。这些 PMU 彼此独立,各自拥有硬件逻辑来采集统计与性能信息;
- SoC 封装了多个 CPU die 与 IO die。每个 CPU 簇(CPU Cluster,CCL)由 4 个 CPU 核心组成,共享一片 L3 缓存;
- 每个 CPU die 被称为 Super CPU Cluster(SCCL),由 6 个 CCL 构成;
- 每个 SCCL 配备 2 个 HHA(编号 0–1)和 4 个 DDRC(编号 0–3)。
PMU v2 之后的 SoC 进一步封装了 I/O die:I/O die 被称为 Super I/O Cluster(SICL),包含多个 I/O Cluster(ICL)。SoC 中每个 CCL/ICL 都有唯一的 11 位 ID,其中高 6 位是 SCCL-ID,低 5 位是 CCL/ICL-ID。文档列出了 I/O die 上各 ICL 类型的编码:
| 低 5 位编码 | ICL 类型 |
|---|---|
5'b00000 | I/O_MGMT_ICL |
5'b00001 | Network_ICL |
5'b00011 | HAC_ICL |
5'b10000 | PCIe_ICL |
这套拓扑在驱动中由struct hisi_pmu_topology表达,见 hisi_uncore_pmu.h:其中sccl_id/sicl_id/scl_id三者共用一个 union(SCCL 与 SICL 是平行的,一个 PMU 不可能同时位于两者之上),ccl_id表示 CPU 簇,index_id表示同位置多个同类 PMU 的模块编号,sub_id则是 PMU 的子模块编号——文档注释里明确举例:DDRC PMU v2 因为每个 DDRC 内含多个 DMC 而使用了sub_id。任何无法定位的拓扑字段取值为-1。
拓扑 ID 的来源在 hisi_uncore_pmu.c 的hisi_uncore_pmu_init_topology():驱动从设备固件属性hisilicon,scl-id、hisilicon,ccl-id、hisilicon,idx-id、hisilicon,sub-id中读取。而对于 CPU 侧,hisi_read_sccl_and_ccl_id()(hisi_uncore_pmu.c)则从MPIDR_EL1的 affinity 层级解析出 SCCL/CCL ID,并按 CPU 型号区分三种编码:
- TSV110 的 MT 版本:SCCL 取
Aff2[7:3],CCL 取Aff2[2:0]; - 其他 MT 部件:SCCL 取
Aff3[7:0],CCL 取Aff2[7:0]; - 非 MT 部件:SCCL 取
Aff2[7:0],CCL 取Aff1[7:0]。
驱动构建与 sysfs 事件发现
编译依赖
uncore PMU 驱动族的构建开关在 drivers/perf/hisilicon/Kconfig:HISI_PMU为 tristate 选项,依赖ARM64 && ACPI,即该套驱动仅面向 ARM64 且使用 ACPI 的 HiSilicon 系统。Makefile 显示CONFIG_HISI_PMU会编译 9 个目标文件:hisi_uncore_pmu.o(公共框架)以及hisi_uncore_l3c_pmu.o、hisi_uncore_hha_pmu.o、hisi_uncore_ddrc_pmu.o、hisi_uncore_sllc_pmu.o、hisi_uncore_pa_pmu.o、hisi_uncore_cpa_pmu.o、hisi_uncore_uc_pmu.o、hisi_uncore_noc_pmu.o、hisi_uncore_mn_pmu.o等具体设备 PMU。此外还有独立的HISI_PCIE_PMU与HNS3_PMU选项,分别面向 PCIe 与 HNS3 网卡的 RCiEP 设备,与本文的 uncore PMU 主题无关。
每个模块注册为独立 PMU
文档指出:每个 L3C、HHA、DDRC 都会以独立的 PMU 身份向 perf 注册,PMU 名称在事件列表中形如hisi_sccl<sccl-id>_module<index-id>,其中sccl-id是 SCCL 标识,index-id是模块索引。例如:
hisi_sccl3_l3c0/rd_hit_cpipe:SCCL 3 上第 0 号 L3C 的READ_HIT_CPIPE事件;hisi_sccl1_hha0/rx_operations:SCCL 1 上第 0 号 HHA 的RX_OPERATIONS事件。
对应地,设备节点位于:
/sys/bus/event_source/devices/hisi_sccl{X}_<l3c{Y}/hha{Y}/ddrc{Y}>以 L3C 驱动为例,hisi_uncore_l3c_pmu.c 的 probe 函数会按拓扑生成名称:有sub_id时格式为hisi_sccl%d_l3c%d_%d(v3 命名),否则为hisi_sccl%d_l3c%d,随后调用perf_pmu_register()完成注册。事件与过滤参数则通过 sysfs 的events/与format/属性组暴露,perf list就是读取这些属性列出的事件清单。每个 PMU 的 sysfs 属性组都由公共的hisi_pmu_cpumask_attr_group、hisi_pmu_identifier_group与各版本的 format/events 组拼成,例如 L3C 的组装见 hisi_uncore_l3c_pmu.c。
cpumask 与 associated_cpus
文档说明驱动还提供两个额外的 sysfs 属性:
cpumask:显示用于统计该 uncore PMU 事件的 CPU 核心 ID。它是给 perf 等用户态工具的提示(hint),通常只包含associated_cpus中的某一个关联 CPU;associated_cpus:显示与该 PMU 关联的 CPU 集合。
源码印证了这一点。hisi_uncore_pmu.c 中cpumask属性只输出单个on_cpu,associated_cpus输出hisi_pmu->associated_cpus位图。on_cpu的选择逻辑在hisi_uncore_pmu_online_cpu()(hisi_uncore_pmu.c):当某个与 PMU 拓扑关联的 CPU 上线时,驱动把该 CPU 记入associated_cpus,并将其设为on_cpu,同时把 PMU 溢出中断的 affinity 绑到同一 CPU;若 PMU 没有关联 CPU(例如位于 SICL 上),则用cpumask_local_spread()按 NUMA 节点就近选取。当on_cpu离线时,hisi_uncore_pmu_offline_cpu()(hisi_uncore_pmu.c)会通过perf_pmu_migrate_context()把 PMU 上下文迁移到新的关联 CPU 并更新中断亲和性。此外 hisi_uncore_pmu.c 在event_init里会强制执行event->cpu = hisi_pmu->on_cpu——同一个 PMU 上的所有事件被约束在同一个 CPU 上计数,这与 uncore 计数器在 die 内共享的硬件特性一致。
基础用法:perf list 与 perf stat
文档给出的标准用法如下。先用perf list查看 uncore 事件:
$# perf list hisi_sccl3_l3c0/rd_hit_cpipe/ [kernel PMU event] ------------------------------------------ hisi_sccl3_l3c0/wr_hit_cpipe/ [kernel PMU event] ------------------------------------------ hisi_sccl1_l3c0/rd_hit_cpipe/ [kernel PMU event] ------------------------------------------ hisi_sccl1_l3c0/wr_hit_cpipe/ [kernel PMU event] ------------------------------------------再对指定 PMU 事件做整机(-a)统计,既可以用事件名,也可以用原始事件码:
$# perf stat -a -e hisi_sccl3_l3c0/rd_hit_cpipe/ sleep 5 $# perf stat -a -e hisi_sccl3_l3c0/config=0x02/ sleep 5两条命令等价:从源码的事件表 hisi_uncore_l3c_pmu.c 可以确认 L3C PMU v1 注册的事件包括rd_cpipe(0x00)、wr_cpipe(0x01)、rd_hit_cpipe(0x02)、wr_hit_cpipe(0x03)、victim_num(0x04)、rd_spipe(0x20)、wr_spipe(0x21)、rd_hit_spipe(0x22)、wr_hit_spipe(0x23)、back_invalid(0x29)、retry_cpu(0x40)、retry_ring(0x41)、prefetch_drop(0x42)——config=0x02正是rd_hit_cpipe。
PMU v2(identifier 0x30):新增过滤能力
文档指出 PMU v2 的 identifier 为0x30(驱动中定义为 hisi_uncore_pmu.h 的HISI_PMU_V2 0x30),拓扑与 v1 相同,但硬件增加了一组过滤功能,都通过 perf 事件的 config1/config2 字段下发。
1. tt_core:按核/线程过滤 L3C 计数
L3C PMU 支持在簇内按 core/thread 过滤,以位图形式指定:
$# perf stat -a -e hisi_sccl3_l3c0/config=0x02,tt_core=0x3/ sleep 5上例只统计该簇中 core/thread 0 和 1 产生的操作。注意文档的告诫:不要使用tt_core_deprecated指定核/线程过滤,该选项仅为向后兼容保留,且只支持 8 位,可能覆盖不全共享该 L3C 的所有核/线程。
源码中这两者的位域定义见 hisi_uncore_l3c_pmu.c 的 v2 format 组:
event→config:0-7;tt_core_deprecated→config1:0-7;tt_req→config1:8-10;datasrc_cfg→config1:11-15;datasrc_skt→config1:16;tt_core→config2:0-15。
tt_core从 8 位扩展到 16 位以覆盖共享 L3 的全部 CPU。驱动在hisi_l3c_pmu_get_tt_core()(hisi_uncore_l3c_pmu.c)中优先取config2的新字段,为空时回退到旧字段;而check_filter()(hisi_uncore_l3c_pmu.c)同时拒绝了两者同时设置的非法组合。命中过滤后,hisi_l3c_pmu_config_core_tracetag()会把位图写入L3C_CORE_CTRL寄存器并置位L3C_PERF_CTRL的核计数使能与 tracetag 核使能(hisi_uncore_l3c_pmu.c)。
2. tt_req:按读/写/原子操作类型过滤(Tracetag)
tt_req允许只统计读、写或原子操作,缺省为统计全部操作。tt_req是 3 位字段,编码如下:
| 编码 | 含义 |
|---|---|
3'b100(0x4) | 读操作 |
3'b101(0x5) | 写操作 |
3'b110(0x6) | 原子 store 操作 |
3'b111(0x7) | 原子非 store 操作 |
| 其他 | 保留 |
示例:
$# perf stat -a -e hisi_sccl3_l3c0/config=0x02,tt_req=0x4/ sleep 5上例只统计该簇内的读操作。实现上,hisi_l3c_pmu_config_req_tracetag()(hisi_uncore_l3c_pmu.c)把tt_req左移 7 位写入L3C_TRACETAG_CTRL的请求类型域并置请求使能位,再在L3C_PERF_CTRL中打开L3C_TRACETAG_EN使能位;停止时由hisi_l3c_pmu_clear_req_tracetag()对称清除。
3. datasrc:确认数据来源
datasrc用于判断数据从哪里来,是 5 位编码。文档列出的重要编码:
| 编码 | 数据来源 |
|---|---|
5'b00001 | 本 die 的 L3C |
5'b01000 | 跨 die(cross-die)的 L3C |
5'b01001 | 另一个 socket 中的 L3C |
5'b01110 | 本地 DDR |
5'b01111 | 跨 die 的 DDR |
5'b10000 | 跨 socket 的 DDR |
主要用途是帮助定位数据源相对 CPU 核心的远近。多芯片场景下使用datasrc_cfg过滤时,perf 命令中还需要配置datasrc_skt:
$# perf stat -a -e hisi_sccl3_l3c0/config=0xb9,datasrc_cfg=0xE/, \ hisi_sccl3_l3c0/config=0xb9,datasrc_cfg=0xF/ sleep 5源码侧,hisi_l3c_pmu_config_ds()(hisi_uncore_l3c_pmu.c)将 5 位datasrc_cfg写入每 4 个计数器共享的 8 位数据源类型寄存器(前 4 个计数器用L3C_DATSRC_TYPE0,后 4 个用L3C_DATSRC_TYPE1);若设置了datasrc_skt,则置位L3C_DATSRC_CTRL的L3C_DATSRC_SKT_EN。
4. uring_channel:按 uring 通道过滤 UC PMU 事件
UC PMU 的事件0x47~0x59支持按 tx 请求 uring channel 过滤,该字段为 2 位:
| 编码 | 含义 |
|---|---|
2'b11 | 统计发送到 uring_ext(MATA)通道的事件 |
2'b01 | 与2'b11相同 |
2'b10 | 统计发送到 uring(非 MATA)通道的事件 |
2'b00 | 缺省值,统计发送到 uring 与 uring_ext 两个通道的事件 |
5. ch:按 NoC 事务通道过滤
NoC PMU 支持用该选项过滤特定事务通道的计数,当前支持的通道:
| 编码 | 通道 |
|---|---|
3'b010 | Request 通道 |
3'b100 | Snoop 通道 |
3'b110 | Response 通道 |
3'b111 | Data 通道 |
6. tt_en:只统计带 tracetag 的事务
若设置tt_en,NoC PMU 只统计设置了 tracetag 的事务。tracetag 的详细机制即上文 tt_req/tt_core 部分所述。
PMU v3(identifier 0x40):子 PMU 划分与 ext 选项
PMU v3 的 identifier 为0x40。为了获得更细粒度的跟踪能力,v3 把部分 uncore PMU 拆分为若干 part,每个 part 拥有专属 PMU,这些 PMU 合起来覆盖某个 uncore 设备的全部事件监控职责。sysfs 命名格式随之变化:
/sys/bus/event_source/devices/hisi_sccl{X}_<l3c{Y}_{Z}/ddrc{Y}_{Z}/noc{Y}_{Z}>其中Z是 sub-id,表示同一硬件设备不同 part 对应的 PMU。不同 sub-id 的 PMU 用法基本一致;特别地,L3C PMU 提供了ext选项以探索更细粒度的 L3C 统计。驱动把ext作为向硬件下发 perf 命令时的“终止提示”,取值约束为:
ext=0:缺省值,可以直接使用事件名;ext=1与ext=2:必须使用事件码,不支持事件名。
文档给出的示例:
$# perf stat -a -e hisi_sccl0_l3c1_0/rd_spipe/ sleep 5或:
$# perf stat -a -e hisi_sccl0_l3c1_0/event=0x1,ext=1/ sleep 5其中hisi_sccl0_l3c1_0定位的是 SCCL 0 上 L3 缓存 1 的 pipe0。第一条命令因为隐含ext=0,定位到 L3C 的第一个 part;第二条命令则对 L3C 的另一个 part 以事件0x1发起计数。
源码与文档完全对应:hisi_get_ext()从config:16-17提取 ext 值(hisi_uncore_l3c_pmu.c 及 v3 format 组)。在hisi_l3c_pmu_get_event_idx()(hisi_uncore_l3c_pmu.c)中,普通事件与扩展事件分别在不同的计数器位段中分配索引,并把对应的基地址(base或ext_base[ext - 1])保存到event->hw.event_base——因为普通事件和扩展事件位于不同的地址空间。probe 阶段(hisi_uncore_l3c_pmu.c)若检测到硬件支持 ext,则按中断数量推算 ext 区域数并累加计数器总数,因此 v3 的 L3C 最多可监视 2~3 倍于单区域数量的并发事件。
按 CCL/ICL ID 过滤:srcid/tgtid
v3 下用户还可通过srcid_cmd & srcid_msk配置只统计来自特定 CCL/ICL 的数据,通过tgtid_cmd & tgtid_msk统计发往特定 CCL/ICL 的数据。注意掩码语义:srcid_msk/tgtid_msk中置 1 的位表示匹配时不检查该位。若上述所有过滤选项都未启用,PMU 按缺省值工作——不区分过滤条件与 ID 信息,直接返回 PMU 计数器中的总计数。
功能限制:不支持采样与 per-task 绑定
文档明确:当前驱动不支持采样,因此perf record不可用;由于事件全部属于 uncore,attach 到具体 task 也不被支持。源码在 hisi_uncore_pmu.c 的hisi_uncore_pmu_event_init()中给出了硬性校验:
- 采样事件或带
PERF_ATTACH_TASK的事件直接返回-EOPNOTSUPP,注释解释了原因:计数器为 CPU die(SCCL)内所有核共享,且 uncore 计数器不隶属于任何 CPU,无法支持 per-task 模式; event->cpu < 0(未指定 CPU)返回-EINVAL;- 事件组内的硬件事件数不得超过 PMU 可用计数器数(
hisi_validate_event_group()); - 事件码超过该 PMU 的
check_event上限返回-EINVAL。例如 L3C v1 上限为0x59(L3C_V1_NR_EVENTS),v2/v3 为0xFF。
另外,从驱动结构看,公共头文件定义了HISI_MAX_COUNTERS为0x18(24),即单个 PMU 事件最多可挂 24 个硬件计数器;L3C 每个区域固定 8 个计数器(L3C_NR_COUNTERS)。计数精度方面,L3C v1 为 48 位计数器,v2/v3 为 64 位,这由各版本的hisi_pmu_dev_info中的counter_bits字段声明(hisi_uncore_l3c_pmu.c)。公共框架的hisi_uncore_pmu_set_event_period()(hisi_uncore_pmu.c)会把回绕点设为2^(counter_bits - 1),为极端情况下的中断延迟留出余量。
小结与深入路径
- 设备发现:
/sys/bus/event_source/devices/hisi_sccl*_{l3c,hha,ddrc}*目录下的events/、format/、cpumask、associated_cpus、identifier属性是理解每个 PMU 能力的权威入口,perf list是其用户态映射; - 事件统计:优先
perf stat -a -e <pmu>/<event>/ sleep N,需要精细过滤时按 v2/v3 的tt_core、tt_req、datasrc_cfg/datasrc_skt、ch、tt_en、ext、srcid/tgtid组合; - 使用事件名最稳妥(
ext != 0除外);原始config=0xNN编码需与format/属性及驱动中的事件表核对; - 由于不支持采样与 per-task 绑定,本套 PMU 适合做系统级(
-a)带宽、命中率、延迟来源类的统计性剖析,而非逐样本的火焰图分析; - 源码入口:公共框架 hisi_uncore_pmu.c 与 hisi_uncore_pmu.h,L3C 实现 hisi_uncore_l3c_pmu.c,HHA/DDRC 等其余模块见 drivers/perf/hisilicon/;L3C 的 ACPI 设备 ID 为 HISI0213(v1)、HISI0214(v2)、HISI0215(v3),可用于在 ACPI 系统中确认硬件版本(hisi_uncore_l3c_pmu.c);
- 文档最后提示:如需特定 SoC 上 PMU 设备的完整事件清单及其信息,建议联系驱动维护者获取。
【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考