Linux 内核 APM X-Gene SoC PMU(xgene-pmu)驱动完全指南:从 sysfs 事件到 perf 实战
【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux
本指南基于 Linux 内核源码树中的 Documentation/admin-guide/perf/xgene-pmu.rst 编写,全面讲解 APM X-Gene SoC 上由xgene-pmu驱动管理的系统级 Performance Monitoring Unit(PMU):L3 缓存、I/O 桥、内存控制器桥与内存控制器四类设备。读完本文,你将掌握如何在 X-Gene 平台上用perf工具枚举 SoC 事件、配置 agent 过滤、执行perf stat系统级计数,并理解驱动在寄存器与 perf 子系统之间的底层实现机制。
X-Gene SoC PMU 概述:四类系统设备 PMU
X-Gene SoC PMU 由多个相互独立的系统设备 PMU 组成,包括L3 cache(L3C)、I/O bridge(IOB)、memory controller bridge(MCB)和memory controller(MC)。这些 PMU 设备采用与 ARM 核 PMU 类似的松散架构模型,并共享同一个顶层中断与状态 CSR(Control/Status Register)区域——这一点在驱动代码中体现为公共的 PCPPMU 寄存器组(PCPPMU_INTSTATUS_REG/PCPPMU_INTMASK_REG,见 drivers/perf/xgene_pmu.c)。
从驱动的角度看,xgene-pmu是一个平台设备驱动,它注册的是一组独立的 perf PMU(而非单个 PMU),每个 PMU 对应一个xgene_pmu_dev实例,命名规则为l3c%d、iob%d、iob_slow%d、mcb%d、mc%d(见 xgene_pmu_dev_name())。
驱动支持三个硬件版本,由枚举PCP_PMU_V1 / PCP_PMU_V2 / PCP_PMU_V3区分(drivers/perf/xgene_pmu.c):
| 版本 | 计数器宽度 | 每 PMU 计数器数 | 事件 ID 位宽(示例) | 匹配方式 |
|---|---|---|---|---|
| V1 | 32 位 | 1 | L3C: config 0-7 | DTapm,xgene-pmu/ ACPIAPMC0D5B |
| V2 | 32 位 | 4 | L3C: config 0-7 | DTapm,xgene-pmu-v2/ ACPIAPMC0D5C |
| V3 | 64 位 | 4 | L3C: config 0-39 | ACPIAPMC0D83 |
版本差异在驱动中由两套 ops 表(xgene_pmu_ops与xgene_pmu_v3_ops)实现,其中 V3 使用 64 位计数器读写(两个 32 位寄存器拼接,并带进位一致性重读保护),且不再支持 agent 掩码寄存器(write_agentmsk/write_agent1msk为空实现),见 drivers/perf/xgene_pmu.c 与 drivers/perf/xgene_pmu.c。
PMU(perf)驱动与 sysfs 事件发现
xgene-pmu驱动为上述每类 PMU 注册独立的 perf 驱动,每个驱动的可用事件与配置选项通过 sysfs 暴露,目录为:
/sys/bus/event_source/devices/<l3cX/iobX/mcbX/mcX>/目录下主要包含三类属性组(对应驱动中的 attribute group):
format目录:描述perf_event_attr结构中config(事件 ID)与config1(agent ID)字段的位格式;events目录:提供所有受支持事件类型的配置模板,可直接用于 perf 工具;cpumask属性:包含一个 CPU ID,所有 PMU 事件由该 CPU 统一处理。
例如l3c0/bank-fifo-full/等价于l3c0/config=0x0b/。在驱动中,事件模板由XGENE_PMU_EVENT_ATTR宏生成(drivers/perf/xgene_pmu.c),events目录下的每个属性实际输出config=0x%llx形式的字符串(xgene_pmu_event_show()),perf 工具读取后即自动展开为原始配置。
format 目录:config 与 config1 的位布局
驱动为 V1/V2 定义了 4 组 format 属性(drivers/perf/xgene_pmu.c):
| PMU | format 属性 | 字段布局 |
|---|---|---|
| l3c | l3c_eventid | config:0-7 |
| l3c | l3c_agentid | config1:0-9 |
| iob | iob_eventid | config:0-7 |
| iob | iob_agentid | config1:0-63 |
| mcb | mcb_eventid | config:0-5 |
| mcb | mcb_agentid | config1:0-9 |
| mc | mc_eventid | config:0-28 |
可见:config字段存放事件 ID(各 PMU 位宽不同,如 MCB 只支持 0-5 位,即 0x00~0x05 的事件号);config1字段存放 agent 掩码,其中 IOB 使用完整的 64 位(由config1的低 32 位与高 32 位两个寄存器PMU_PMAMR0/PMU_PMAMR1承载,见 drivers/perf/xgene_pmu.c)。V3 的 format 则只暴露config事件 ID 字段,位宽显著扩大(如 L3C 为config:0-39,MC 为config:0-44),见 drivers/perf/xgene_pmu.c。
events 目录:V1/V2 事件表
以下是 V1/V2 各 PMU 在events目录下暴露的完整事件清单(对应 drivers/perf/xgene_pmu.c 中的事件数组):
l3c(L3 cache)事件:
| 事件名 | config 值 | 含义 |
|---|---|---|
cycle-count | 0x00 | 周期计数 |
cycle-count-div-64 | 0x01 | 周期计数(除以 64) |
read-hit | 0x02 | 读命中 |
read-miss | 0x03 | 读未命中 |
write-need-replacement | 0x06 | 写操作需要替换 |
write-not-need-replacement | 0x07 | 写操作不需要替换 |
tq-full | 0x08 | 事务队列满 |
ackq-full | 0x09 | ACK 队列满 |
wdb-full | 0x0a | 写数据缓冲满 |
bank-fifo-full | 0x0b | bank FIFO 满 |
odb-full | 0x0c | 出站数据缓冲满 |
wbq-full | 0x0d | 写回队列满 |
bank-conflict-fifo-issue | 0x0e | bank 冲突 FIFO 发射 |
bank-fifo-issue | 0x0f | bank FIFO 发射 |
iob(I/O bridge)事件:cycle-count(0x00)、cycle-count-div-64(0x01)、axi0-read(0x02)、axi0-read-partial(0x03)、axi1-read(0x04)、axi1-read-partial(0x05)、csw-read-block(0x06)、csw-read-partial(0x07)、axi0-write(0x10)、axi0-write-partial(0x11)、axi1-write(0x13)、axi1-write-partial(0x14)、csw-inbound-dirty(0x16)。
mcb(内存控制器桥)事件:cycle-count(0x00)、cycle-count-div-64(0x01)、csw-read(0x02)、csw-write-request(0x03)、mcb-csw-stall(0x04)、cancel-read-gack(0x05)。
mc(内存控制器)事件:覆盖命令发送(act-cmd-sent、pre-cmd-sent、rd-cmd-sent、rda-cmd-sent、wr-cmd-sent、wra-cmd-sent、pde-cmd-sent、sre-cmd-sent、prea-cmd-sent、ref-cmd-sent、rd-rda-cmd-sent、wr-wra-cmd-sent)、冲突(in-rd-collision、in-wr-collision、collision-queue-not-empty、collision-queue-full)、MCU 请求(mcu-request、mcu-rd-request、mcu-hp-rd-request、mcu-wr-request、mcu-rd-proceed-all、mcu-rd-proceed-cancel、mcu-rd-response、mcu-rd-proceed-speculative-all、mcu-rd-proceed-speculative-cancel、mcu-wr-proceed-all、mcu-wr-proceed-cancel),config 值从 0x00 到 0x1c。
V3 的事件表更为庞大,例如 L3C V3 提供 40 个事件(含index-flush-eviction、clean-eviction、dirty-eviction、bloomfilter-clearing、generation-flip、vcc-droop-detected等),IOB 分为 fast(49 个,含 snoop、barrier 相关)与 slow(9 个)两套,MCB V3 有 36 个事件,MC V3 有 45 个事件(含各种 hazard:raw-hazard、war-hazard、waw-hazard、rar-hazard以及rank-active、rank-idle、rank-pd、rank-sref等),完整清单见 drivers/perf/xgene_pmu.c。
agent ID:按数据通路来源过滤事件
X-Gene SoC 的绝大多数 PMU 都有特定的agent ID 列表,用于监控特定数据通路的性能。例如,L3 cache 的 agent 可以是某个具体 CPU 或某个 I/O 桥。每个 PMU 都有一组 2 个寄存器(PMU_PMAMR0/PMU_PMAMR1,偏移 0xA00 / 0xA04)用于掩码请求来源的 agent:
- 若与 agent 编号对应的位被置 1,则仅当事件由该 agent 的请求引发时才被计数;
- 每个 agent ID 位与
config1字段中的对应位是反向映射的; - 默认情况下事件对所有 agent 请求计数(
config1 = 0x0)。
也就是说,config1中的某个位为 0 表示"该 agent 被计入",为 1 表示"该 agent 被排除"。在驱动实现中,xgene_perf_enable_event()将config1取反后写入掩码寄存器:write_agentmsk(pmu_dev, ~GET_AGENTID(event))(drivers/perf/xgene_pmu.c),与文档描述的"inversely mapped"完全一致;IOB 额外将config1的高 32 位写入第二个掩码寄存器PMU_PMAMR1。各 PMU 具体支持哪些 agent,需查阅 APM X-Gene 用户手册(User Manual)。
cpumask:单 CPU 承载全部事件
由于 SoC 系统级 PMU 的计数器是所有核共享的,perf 核心的诸多操作(如事件轮转)都运行在单一 CPU 上下文上。为避免 off-core PMU 事件被分配到不同 CPU 导致的问题,驱动强制将所有事件绑定到cpumask属性指定的唯一 CPU上(xgene_perf_event_init() 中将event->cpu重写为cpumask_first(&pmu_dev->parent->cpu))。
cpumask属性本身由cpumask_show()输出(drivers/perf/xgene_pmu.c)。该 CPU 的选择由 CPU 热插拔回调维护:在 CPU 上线时选定一个"读者 CPU",并将溢出中断的亲和性(irq_set_affinity)一并绑定到该 CPU;当该 CPU 下线时,通过perf_pmu_migrate_context()把所有 PMU 的 perf 上下文迁移到另一个在线 CPU,见 drivers/perf/xgene_pmu.c。因此用户在使用时无需手动指定-C,驱动会自动使用 cpumask 指定的 CPU。
perf 工具实战:枚举与计数
在内核开启CONFIG_ARM_APM_XGENE_PMU(drivers/perf下的xgene-pmu驱动,平台驱动见 drivers/perf/xgene_pmu.c)后,即可用 perf 工具访问这些 PMU。
1. 枚举可用事件:
/ # perf list | grep -e l3c -e iob -e mcb -e mc l3c0/ackq-full/ [Kernel PMU event] <...> mcb1/mcb-csw-stall/ [Kernel PMU event]perf list输出中的事件名直接来自 sysfsevents目录,与驱动源码中的XGENE_PMU_EVENT_ATTR定义一一对应。
2. 系统级计数统计(1 秒):
/ # perf stat -a -e l3c0/read-miss/,mcb1/csw-write-request/ sleep 1-a表示系统级(all CPUs)模式,这是必须的——SoC PMU 计数器跨核共享,不支持 per-task 会话。
3. 结合 agent 过滤:
/ # perf stat -a -e l3c0/read-miss,config1=0xfffffffffffffffe/ sleep 1config1=0xfffffffffffffffe表示仅统计来自 agent 0(bit 0 为 0)的读未命中,屏蔽其余所有 agent 的请求。从驱动的反向掩码逻辑可以理解:~0xfffffffffffffffe = 0x1,写入PMU_PMAMR0后只有 bit 0 使能。
4. 事件组使用:驱动要求同组事件必须来自同一 PMU(软件事件除外),混用不同 PMU 的事件组会被xgene_perf_event_init()拒绝(drivers/perf/xgene_pmu.c)。
驱动不支持的能力与原因
原文档明确指出驱动存在以下限制,结合源码可以进一步解释其根因:
- 不支持采样(sampling):
perf record无法工作。在xgene_perf_event_init()中,is_sampling_event(event)直接返回-EINVAL(drivers/perf/xgene_pmu.c); - 不支持 per-task(不带
-a)会话:PERF_ATTACH_TASK同样被拒绝,且 PMU 注册时task_ctx_nr = perf_invalid_context(drivers/perf/xgene_pmu.c); - 另外 PMU 的 capabilities 为
PERF_PMU_CAP_NO_EXCLUDE,即不支持排除用户态/内核态(exclude_user/exclude_kernel)等过滤。
这些限制的根源在于:SoC 系统 PMU 计数器是跨所有核共享的全局硬件资源,无法按进程隔离,也不具备按指令地址采样的能力。事件初始化时若传入event->cpu < 0(即未绑定具体 CPU)也会返回-EINVAL。
中断处理与计数器溢出
顶层中断处理由xgene_pmu_isr()完成:读取共享 PCPPMU 中断状态寄存器,按版本解析 MCU/MCB/L3C/IOB 中断源,再分别遍历各 PMU 列表调用_xgene_pmu_isr()(drivers/perf/xgene_pmu.c)。
在_xgene_pmu_isr()中(drivers/perf/xgene_pmu.c):
- 停止计数器(写
PMU_PMCR的 E 位); - 读取溢出状态寄存器
PMU_PMOVSR(V3 用PMU_PMOVSSET)并只取PMU_OVERFLOW_MASK(0xF)低 4 位; - 按版本清除中断标志(V1 写 0x0,V2 回写 pmovsr,V3 写
PMU_PMOVSCLR); - 对每个溢出的计数器调用
xgene_perf_event_update()累加计数,再调用xgene_perf_event_set_period()重设周期; - 重新启动计数器。
32 位计数器以2^31为编程周期(1ULL << 31),以应对极端中断延迟下计数器被追上的风险;64 位计数器(V3)则不预期溢出(drivers/perf/xgene_pmu.c)。计数增量计算使用(new - prev) & max_period的环绕减法(drivers/perf/xgene_pmu.c)。
设备树(DT)与 ACPI 支持
驱动同时支持 Flattened Device Tree(FDT)与 ACPI 两种固件接口:
- 设备树:
of_match_table匹配apm,xgene-pmu(V1)与apm,xgene-pmu-v2(V2),完整绑定说明见 Documentation/devicetree/bindings/perf/apm,xgene-pmu.yaml。父节点需要regmap-csw、regmap-mcba、regmap-mcbb三个 syscon phandle,用于探测活动的 MCB/MC 数量;子节点按 compatible(apm,xgene-pmu-l3c/-iob/-mcb/-mc)枚举,MCB/MC 子节点用enable-bit-index指定其在活动掩码中的位号(如 4 个 MC 子节点分别用 0~3),未指定时默认为 0("总是使能",见 drivers/perf/xgene_pmu.c); - ACPI:匹配
APMC0D5B(V1)、APMC0D5C(V2)、APMC0D83(V3)三个 PNP ID,子设备通过acpi_walk_namespace递归枚举,类型由APMC0D5D~APMC0D60、APMC0D84~APMC0D88映射(drivers/perf/xgene_pmu.c)。
驱动在 probe 阶段会读取 CSW(Crossbar Switch)与 MCBA/MCBB 寄存器来判断实际活动的 MCB 数量(单/双 MCB)与 MC 数量(MCBADDRMR_DUALMCU_MODE_MASK位判断 dual-MCU 模式),从而只注册硬件实际存在的 PMU 设备(drivers/perf/xgene_pmu.c);V3 下 L3C 最多 8 个(掩码 0xFF),MC 数量则由 CSW 路由位推断。这也是为什么不同机器上/sys/bus/event_source/devices/下出现的l3cX/mcbX/mcX数量会不同。
小结
X-Gene SoC PMU 通过xgene-pmu驱动将 L3C、IOB、MCB、MC 四类系统设备以标准 perf PMU 的形式接入 Linux perf 子系统。使用时只需三步:用perf list枚举事件、用perf stat -a做系统级计数、需要时通过config1按 agent 过滤数据通路。其底层实现——共享中断 CSR、反向 agent 掩码、单 CPU 亲和性绑定、32/64 位计数器溢出处理——体现了 off-core 系统 PMU 与核内 PMU 在设计上的典型差异,值得嵌入式与服务器性能工程师深入参考。
【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考