Linux 内核 Qualcomm Centriq L2 Cache PMU 驱动解析:perf 事件编码、sysfs 接口与 Kryo 间接寄存器访问机制
【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux
Linux 内核为高通(Qualcomm)Centriq SoC 提供了一颗专门的 L2 缓存性能监控单元(PMU)驱动,位于 drivers/perf/qcom_l2_pmu.c,官方使用文档为 Documentation/admin-guide/perf/qcom_l2_pmu.rst。本文以该文档为核心,逐条展开其描述的事件编码方案、sysfs 接口与 perf 用法,并结合驱动源码深入剖析“逻辑聚合 PMU + 物理集群 PMU”的架构设计、Kryo L2 间接寄存器访问机制、计数器溢出处理与 CPU 热插拔管理流程,帮助读者在 Centriq 平台上正确配置和采集 L2 缓存性能数据。
1. 驱动定位与适用平台
官方文档开门见山:该驱动支持 Qualcomm Technologies Centriq SoC 中的 L2 缓存集群(L2 cache clusters)。Centriq 处理器存在多个物理 L2 缓存集群,每个集群拥有自己独立的 PMU 硬件,且每个集群关联一个或多个 CPU。
编译层面的适用前提可以从内核配置中得到印证,见 drivers/perf/Kconfig:
config QCOM_L2_PMU bool "Qualcomm L2-cache PMU" depends on ARCH_QCOM && ARM64 && ACPI select QCOM_KRYO_L2_ACCESSORS即该驱动仅在满足ARCH_QCOM && ARM64 && ACPI时可启用——它是一个面向 ARM64 高通平台、依赖 ACPI 固件(而非设备树)来描述硬件拓扑的平台驱动。构建规则见 drivers/perf/Makefile 中的obj-$(CONFIG_QCOM_L2_PMU) += qcom_l2_pmu.o。驱动还强制选择了QCOM_KRYO_L2_ACCESSORS选项(构建规则位于 drivers/soc/qcom/Makefile),这个模块提供了访问 L2 缓存寄存器所需的基础设施(见第 5 节)。
驱动以平台驱动方式注册,ACPI 匹配表中标注的设备 ID 为QCOM8130,见 drivers/perf/qcom_l2_pmu.c:
static const struct acpi_device_id l2_cache_pmu_acpi_match[] = { { "QCOM8130", }, { } };2. 逻辑聚合架构:一颗“虚拟” PMU 汇聚所有物理 PMU
文档指出的关键设计是:“There is one logical L2 PMU exposed, which aggregates the results from the physical PMUs.”(对外只暴露一个逻辑 L2 PMU,它聚合各个物理 PMU 的统计结果)。这一设计直接体现在源码的两个核心结构中:
struct l2cache_pmu(逻辑聚合 PMU)实现 perf 框架要求的通用struct pmu回调,并管理所有硬件 PMU:
/* drivers/perf/qcom_l2_pmu.c */ struct l2cache_pmu { struct hlist_node node; u32 num_pmus; /* 物理 PMU 数量 */ struct pmu pmu; /* 注册到 perf 框架的 pmu 对象 */ int num_counters; /* 计数器数量(含周期计数器) */ cpumask_t cpumask; /* 各集群负责 CPU 的并集,即 sysfs cpumask */ struct platform_device *pdev; struct cluster_pmu * __percpu *pmu_cluster; /* 每 CPU 到所属集群的映射 */ struct list_head clusters; };struct cluster_pmu则代表一颗硬件 PMU,即一个 L2 缓存集群:
struct cluster_pmu { struct list_head next; struct perf_event *events[MAX_L2_CTRS]; /* 本集群正在使用的计数器事件 */ struct l2cache_pmu *l2cache_pmu; DECLARE_BITMAP(used_counters, MAX_L2_CTRS); /* 计数器占用位图 */ DECLARE_BITMAP(used_groups, L2_EVT_GROUP_MAX + 1); /* 事件组占用位图 */ int irq; /* 该集群的溢出中断 */ int cluster_id; /* 固件(ACPI UID)声明的集群编号 */ int on_cpu; /* 负责处理本集群所有 PMU 事件的 CPU */ cpumask_t cluster_cpus; /* 本集群关联的所有 CPU */ spinlock_t pmu_lock; };其中MAX_L2_CTRS定义为 9(见 drivers/perf/qcom_l2_pmu.c),即每颗硬件 PMU 最多管理 9 个计数器。
由于逻辑 PMU 聚合了多个物理 PMU,文档特别提到:驱动提供一个cpumasksysfs 属性,包含“每个集群一个负责 CPU”的面罩(mask),这些 CPU 负责处理各自集群上的所有 PMU 事件。对应源码中,cpumask属性由l2_cache_pmu_cpumask_show实现(drivers/perf/qcom_l2_pmu.c),其值在各集群的on_cpu被确定时(见第 7 节热插拔流程)逐步写入l2cache_pmu->cpumask。用户空间可用它了解事件实际在哪些 CPU 上落点。
3. 事件编码方案:0xCCG 二维数组与列排他
这是官方文档中最核心的技术内容。文档把 L2 事件解释为一个二维数组:每一“列”是一组(group)事件,共 8 组;同一组内同时只能有一个事件生效;同一组中指定了多个事件时,发生冲突的事件无法被同时计数。事件的编码形式为0xCCG:其中CC是两位十六进制数,表示事件代码(数组的行);G表示事件组(列),取值 0–7。此外还有一个周期计数器事件,编码为0xFE,独立于上述编码方案之外。
源码中的位运算宏与文档完全对应(drivers/perf/qcom_l2_pmu.c):
#define L2_EVT_MASK 0x00000FFF /* 合法 config 值域:低 12 位 */ #define L2_EVT_CODE_MASK 0x00000FF0 #define L2_EVT_GRP_MASK 0x0000000F #define L2_EVT_CODE_SHIFT 4 /* 代码占 bit4-11 */ #define L2_EVT_GRP_SHIFT 0 /* 组号占 bit0-3 */ #define L2_EVT_CODE(event) (((event) & L2_EVT_CODE_MASK) >> L2_EVT_CODE_SHIFT) #define L2_EVT_GROUP(event) (((event) & L2_EVT_GRP_MASK) >> L2_EVT_GRP_SHIFT) #define L2_EVT_GROUP_MAX 7由宏可精确读出编码布局:低 4 位(bit0-3)是组号 G,bit4-11 是事件代码 CC,bit12 及以上必须为 0,整体合法值域为0x000-0xFFF(L2_EVT_MASK = 0x00000FFF),唯一的方案外编码是周期计数器的0xFE(L2CYCLE_CTR_RAW_CODE,drivers/perf/qcom_l2_pmu.c)。l2_cache_event_init在事件初始化时据此做合法性校验:既不属于 8 组以内、又超出掩码范围的 config(且不是0xFE)会被拒绝并返回-EINVAL(drivers/perf/qcom_l2_pmu.c)。
3.1 同组冲突的检测(列排他)
文档所说的“列冲突”在源码中被落实为两道检查。第一道在l2_cache_event_init中:同一组(group leader 或任一同组 sibling)内出现第二个同组事件时,直接报 “Column exclusion: conflicting events” 并返回-EINVAL(drivers/perf/qcom_l2_pmu.c):
for_each_sibling_event(sibling, event->group_leader) { if ((sibling != event) && !is_software_event(sibling) && (L2_EVT_GROUP(sibling->attr.config) == L2_EVT_GROUP(event->attr.config))) { dev_dbg_ratelimited(&l2cache_pmu->pdev->dev, "Column exclusion: conflicting events %llx %llx\n", ...); return -EINVAL; } }第二道在计数器分配函数l2_cache_get_event_idx中:通过cluster->used_groups位图检查该事件的列(组)是否已被其他非同组事件占用,占用则返回-EAGAIN,让 perf 框架稍后重试或报错(drivers/perf/qcom_l2_pmu.c)。计数器分配本身也通过used_counters位图查找空闲计数器,全部用尽时同样返回-EAGAIN。
3.2 命名事件及其原始编码
驱动在 sysfs 的events目录下导出了 11 个命名事件,其原始编码(raw code)由源码中的事件宏定义(drivers/perf/qcom_l2_pmu.c)和事件属性表(drivers/perf/qcom_l2_pmu.c)共同确定,整理如下:
| sysfs 事件名 | 原始编码 | 编码结构(CCG) |
|---|---|---|
cycles | 0xFE | 方案外的周期计数器 |
dcache-ops | 0x400 | CC=0x04, G=0 |
icache-ops | 0x401 | CC=0x04, G=1 |
tlbi | 0x402 | CC=0x04, G=2 |
barriers | 0x403 | CC=0x04, G=3 |
total-reads | 0x405 | CC=0x04, G=5 |
total-writes | 0x406 | CC=0x04, G=6 |
total-requests | 0x407 | CC=0x04, G=7 |
ldrex | 0x420 | CC=0x04, G=2 |
strex | 0x421 | CC=0x04, G=3 |
clrex | 0x422 | CC=0x04, G=4 |
这张表揭示了编码方案的语义:例如0x400至0x407这 8 个事件共享同一事件代码0x04,仅组号 G 不同,恰好铺满 8 列;而icache-ops(G=1)与strex(G=3)、tlbi(G=2)与ldrex(G=2)等分属不同行但可能落入同一组,因此它们彼此互斥、不能同时计数。dcache-ops、icache-ops、tlbi、barriers、total-reads、total-writes、total-requests以及独占一组语义的原子指令计数(ldrex/strex/clrex)覆盖了 L2 缓存的操作面:缓存操作、TLB 维护、屏障、读写流量以及独占式(exclusive)原子指令序列。
4. sysfs 接口:format、events 与 cpumask
文档说明:“The driver provides a description of its available events and configuration options in sysfs, see /sys/bus/event_source/devices/l2cache_0. The 'format' directory describes the format of the events.”(驱动在 sysfs 中提供可用事件与配置选项的说明,format目录描述事件格式。)
PMU 注册名在 probe 中固定为l2cache_0(注释说明后缀是实例号,为将来多 socket 场景预留),因此 sysfs 节点位于/sys/bus/event_source/devices/l2cache_0/,见 drivers/perf/qcom_l2_pmu.c:
l2cache_pmu->pmu = (struct pmu) { /* suffix is instance id for future use with multiple sockets */ .name = "l2cache_0", ... .attr_groups = l2_cache_pmu_attr_grps, .capabilities = PERF_PMU_CAP_NO_EXCLUDE, };注意capabilities中的PERF_PMU_CAP_NO_EXCLUDE标志:该 PMU 不支持按用户态/内核态(exclude_user/exclude_kernel)过滤事件,这也是它不支持 per-task 会话的配套约束。
format目录的三个属性由PMU_FORMAT_ATTR定义(drivers/perf/qcom_l2_pmu.c):
/* CCG format for perf RAW codes. */ PMU_FORMAT_ATTR(l2_code, "config:4-11"); PMU_FORMAT_ATTR(l2_group, "config:0-3"); PMU_FORMAT_ATTR(event, "config:0-11");l2_code:config 的 bit4-11,即事件代码 CC(行号);l2_group:config 的 bit0-3,即事件组 G(列号);event:完整的 bit0-11,即整个 12 位事件字段。
events目录下的每个文件(对应第 3.2 节表格中的事件名)由l2cache_pmu_event_show以event=0x%02llx形式输出其原始编码,用户可直接从 sysfs 查到命名事件对应的 RAW code(drivers/perf/qcom_l2_pmu.c)。cpumask属性则如第 2 节所述,输出各集群负责 CPU 构成的掩码。
5. 硬件访问层:Kryo L2 间接寄存器机制
L2 缓存 PMU 的寄存器不在普通 MMIO 空间,而是通过 ARMv8 系统寄存器以“间接访问”方式读写。驱动全部寄存器操作都经由kryo_l2_set_indirect_reg/kryo_l2_get_indirect_reg两个函数完成,其实现在 drivers/soc/qcom/kryo-l2-accessors.c:
#define L2CPUSRSELR_EL1 sys_reg(3, 3, 15, 0, 6) #define L2CPUSRDR_EL1 sys_reg(3, 3, 15, 0, 7) void kryo_l2_set_indirect_reg(u64 reg, u64 val) { ... raw_spin_lock_irqsave(&l2_access_lock, flags); write_sysreg_s(reg, L2CPUSRSELR_EL1); /* 先写选择寄存器(间接地址) */ isb(); write_sysreg_s(val, L2CPUSRDR_EL1); /* 再写数据寄存器 */ isb(); raw_spin_unlock_irqrestore(&l2_access_lock, flags); }访问协议是:先把目标寄存器编号写入选择寄存器L2CPUSRSELR_EL1(OP=3, CRn=3, CRm=15, op2=6),执行 ISB 屏障保证系统寄存器访问的顺序性,然后把数据写入数据寄存器L2CPUSRDR_EL1(op2=7);读取则反过来,先选地址再从数据寄存器读值。整个序列由一个全局自旋锁l2_access_lock保护,保证“选地址-传数据”两步不被并发访问打断,避免两个 PMU 用户互相覆盖选择寄存器。这解释了为什么 drivers/perf/qcom_l2_pmu.c 中的l2_cache_pmu_enable注释强调每个事件都关联一个 CPU、只在对应 CPU 上使能计数器。
驱动使用的寄存器映射集中在 drivers/perf/qcom_l2_pmu.c 的一组宏定义中:
| 寄存器 | 偏移 | 用途 |
|---|---|---|
L2PMCR | 0x400 | PMU 控制寄存器:复位、全局计数器使能;bit11-15 存放事件计数器数量 |
L2PMCNTENCLR/L2PMCNTENSET | 0x403/0x404 | 计数器使能清除/置位 |
L2PMINTENCLR/L2PMINTENSET | 0x405/0x406 | 计数器溢出中断使能清除/置位 |
L2PMOVSCLR/L2PMOVSSET | 0x407/0x408 | 溢出状态位清除/置位(置位寄存器实为只读状态位) |
L2PMCCNTCR | 0x409 | 周期计数器控制 |
L2PMCCNTR | 0x40A | 周期计数器计数值(64 位) |
L2PMCCNTSR | 0x40C | 周期计数器状态 |
L2PMRESR | 0x410 | 事件代码解析寄存器:每 8 位存一个组的事件代码 |
IA_L2PMXEVCNTCR_BASE | 0x420 | 通用计数器 i 的控制寄存器基址 |
IA_L2PMXEVCNTR_BASE | 0x421 | 通用计数器 i 的计数值基址 |
IA_L2PMXEVFILTER_BASE | 0x423 | 通用计数器 i 的事件过滤器基址 |
IA_L2PMXEVTYPER_BASE | 0x424 | 通用计数器 i 的事件类型(组号)基址 |
每号计数器的寄存器地址由reg_idx(reg, i)宏按IA_L2_REG_OFFSET(0x10)步进计算(drivers/perf/qcom_l2_pmu.c)。
5.1 事件如何编程进硬件
l2_cache_event_start展示了单个事件从 config 值到硬件配置的完整链路(drivers/perf/qcom_l2_pmu.c):
- 先调用
l2_cache_cluster_set_period预装载计数器重载值; - 周期计数器(config 为
0xFE):只写L2PMCCNTCR为 0; - 普通事件:把
L2_EVT_CODE(config)(CC)与L2_EVT_GROUP(config)(G)分别编程——cluster_pmu_set_evcntcr(idx, 0)、cluster_pmu_set_evtyper(idx, event_group)(组号写入 EVT YPER 寄存器的 bit3 起的位置,见L2EVTYPER_REG_SHIFT 3),并通过cluster_pmu_set_resr把事件代码 CC 写入L2PMRESR中该组对应的 8 位字段(L2PMRESR_GROUP_BITS = 8); cluster_pmu_set_evfilter_sys_mode(idx)写事件过滤器,组合了SUFILTER_ALL | ORGFILTER_IDINDEP | ORGFILTER_ALL三个位(drivers/perf/qcom_l2_pmu.c),源码注释说明这是关闭按来源 CPU 过滤,让本集群内所有 CPU、所有子单元以及 ID 无关的事件都能被该计数器捕获——即每个物理 PMU 统计的是整个集群的聚合流量;- 最后使能该计数器的溢出中断并置位计数器使能。
第 3 步中cluster_pmu_set_resr在修改L2PMRESR前会获取cluster->pmu_lock自旋锁并执行读-改-写,同时置位L2PMRESR_EN(bit63),保证多组事件代码并发写入时不互相破坏。
5.2 周期计数器的特殊地位
get_num_counters从L2PMCR的 bit11-15 读取事件计数器数量,再加 1 计入周期计数器(drivers/perf/qcom_l2_pmu.c)。probe 时周期计数器固定占用最后一个计数器索引:l2_cycle_ctr_idx = num_counters - 1,且l2_counter_present_mask把低 N-1 个计数器位与 bit31(L2CYCLE_CTR_BIT)一起置位——说明硬件层面周期计数器虽逻辑上位于最高位,其溢出状态却映射在溢出状态寄存器的 bit31(drivers/perf/qcom_l2_pmu.c)。idx_to_reg_bit宏负责把计数器索引换算成使能/溢出位图对应的位。
6. 计数、周期与溢出处理
L2 PMU 是纯统计型 PMU:文档明确“The driver does not support sampling, therefore 'perf record' will not work. Per-task perf sessions are not supported.”(驱动不支持采样,perf record无效;不支持 per-task 会话)。源码中这两处限制都在l2_cache_event_init里落地:hwc->sample_period非零时返回 “Sampling not supported”,event->cpu < 0(per-task)时返回 “Per-task mode not supported”(drivers/perf/qcom_l2_pmu.c)。
统计的正确性依赖溢出中断。l2_cache_handle_irq(drivers/perf/qcom_l2_pmu.c)的流程是:
cluster_pmu_getreset_ovsr读取L2PMOVSSET(溢出状态)并立刻写L2PMOVSCLR复位;- 与
l2_counter_present_mask做与运算判断是否真为本 PMU 的溢出,否则返回IRQ_NONE; - 遍历所有已分配计数器,对溢出的每一个:
l2_cache_event_update读取硬件计数值累加进event->count,随后l2_cache_cluster_set_period重新预装载。
周期(period)重载值体现了防回绕设计(drivers/perf/qcom_l2_pmu.c):周期被限制为计数器最大值的一半——周期计数器预装载BIT_63,32 位通用计数器预装载BIT_31。源码注释解释:即便出现极端的中断延迟,计数器也“希望”不会回绕超过其初始值,从而保证累加的 delta 始终正确。
l2_cache_event_update中还有与位宽相关的细节(drivers/perf/qcom_l2_pmu.c):周期计数器是 64 位,其余计数器都是 32 位;对 32 位计数器,两次读值之差必须按 32 位截断(delta &= 0xffffffff),否则跨硬件重载周期后的差值会算错。该函数用local64_read/local64_cmpxchg无锁循环保证prev_count更新与硬件读数的原子配对,适配perf stat等可能并发读同一事件的场景。
计数器使能/停用的最小操作集封装为四个内联函数(使能/禁用计数器、使能/禁用其溢出中断),分别对应L2PMCNTENSET/CLR与L2PMINTENSET/CLR寄存器加idx_to_reg_bit换算出的位(drivers/perf/qcom_l2_pmu.c)。
7. CPU 与集群的关联:热插拔驱动的全生命周期
逻辑聚合 PMU 与“每集群一个负责 CPU”的模型,要求驱动深度参与 CPU 热插拔。驱动在device_initcall中通过cpuhp_setup_state_multi注册了CPUHP_AP_PERF_ARM_QCOM_L2_ONLINE状态,分别挂接l2cache_pmu_online_cpu与l2cache_pmu_offline_cpu(drivers/perf/qcom_l2_pmu.c)。
上线(online)流程(drivers/perf/qcom_l2_pmu.c):
- 若该 CPU 尚未关联过集群,调用
l2_cache_associate_cpu_with_cluster:读取MPIDR寄存器,假定单线程核心的集群号在 MPIDR 的 AFF1 位段、多线程核心在 AFF2 位段(源码注释提醒,若固件布局变化此逻辑需更新),据此从集群链表中匹配cluster_id,把 CPU 记入cluster->cluster_cpus并建立 per-CPU 的pmu_cluster映射; - 若该集群已有别的 CPU 在负责(
on_cpu != -1),本 CPU 直接返回; - 否则本 CPU 成为该集群的负责 CPU:写入
on_cpu、加入逻辑 PMU 的cpumask(即第 4 节 sysfs 属性的内容来源)、调用cluster_pmu_reset(依次写L2PMCR复位值、清使能/中断/溢出位,drivers/perf/qcom_l2_pmu.c)把硬件置入已知状态,然后把中断亲和性设置到该 CPU 并enable_irq。
下线(offline)流程(drivers/perf/qcom_l2_pmu.c):
- 若本 CPU 不是负责 CPU,无需处理;
- 放弃所有权:清
cpumask位、on_cpu = -1; - 用
cpumask_any_and_but在本集群仍在线的其他 CPU 中选一个接替者;若没有(集群所有 CPU 都下线),直接disable_irq挂起中断; - 若有接替者,调用
perf_pmu_migrate_context把 perf 事件上下文迁移过去,并更新中断亲和性。
probe 阶段的集群枚举:驱动遍历平台设备的子设备(ACPI 子节点),从每个子节点的 ACPI UID 解析出固件声明的cluster_id,取得其中断并注册l2_cache_handle_irq(带IRQF_NOBALANCING | IRQF_NO_THREAD | IRQF_NO_AUTOEN标志,初始不自动使能,等集群负责 CPU 确定后再打开);每个成功解析的子设备计入num_pmus。probe 结尾注册热插拔状态、以名字l2cache_0调perf_pmu_register完成 PMU 注册,并打印 “Registered L2 cache PMU using %d HW PMUs”(drivers/perf/qcom_l2_pmu.c)。
8. 实操:用 perf 采集 L2 缓存事件
文档给出了两条perf stat示例命令,结合第 3 节的编码方案可以精确解读:
perf stat -e l2cache_0/config=0x001/,l2cache_0/config=0x042/ -a sleep 1config=0x001:CC=0x00,G=1 —— 事件代码 0x00 在第 1 组的事件;config=0x042:CC=0x04,G=2 —— 事件代码 0x04 在第 2 组的事件;- 两者组号不同(G=1 与 G=2),不违反列排他约束,可以放入同一会话同时计数;
-a表示全系统统计。
perf stat -e l2cache_0/config=0xfe/ -C 2 sleep 1config=0xfe即方案外的周期计数器事件(对应 sysfs 命名事件l2cache_0/cycles);-C 2把统计绑定到 CPU 2。由于驱动不支持 per-task 模式且同一集群的事件会被强制迁移到该集群的负责 CPU 上(l2_cache_event_init结尾event->cpu = cluster->on_cpu,drivers/perf/qcom_l2_pmu.c),指定 CPU 的实际意义是选定其所属的 L2 集群。
基于以上机制,实操时还应注意:
- 命名事件可以直接使用:既然 sysfs 的
events/目录暴露了dcache-ops、icache-ops、total-reads等命名事件,命令可以写成perf stat -e l2cache_0/dcache-ops/ -a sleep 1,等价于对应的 RAW code; - 同组事件必须错峰统计:例如
dcache-ops(G=0)与任何其他 G=0 的事件不能同组并发;icache-ops(G=1)与strex(G=3)互斥但可与dcache-ops同跑。规划事件清单时,按 G 值分桶即可预判冲突; - 每次最多 8 个普通事件并发:8 组各占一列,计数器总数上限为
MAX_L2_CTRS(9,含周期计数器),超出后l2_cache_get_event_idx返回-EAGAIN; perf record与--task不可用:采样和 per-task 会话均返回-EOPNOTSUPP,只能做perf stat类聚合统计;- 结果归属:每个物理 PMU 的过滤器已放开到“全部子单元 + 全部来源 CPU”,因此某个集群 PMU 上的计数反映的是整个 L2 集群的合计行为,逻辑 PMU 再把各集群结果聚合呈现。
9. 关键结论速览
- 一颗逻辑 PMU 聚合多颗物理 PMU:
/sys/bus/event_source/devices/l2cache_0/是唯一入口,cpumask属性列出各集群的负责 CPU; - 事件编码 0xCCG:bit0-3 为组号(0–7),bit4-11 为事件代码,
0xFE为独立的周期计数器;同组事件互斥,源码以used_groups位图与组内 sibling 双重检查强制执行; - 寄存器经 Kryo 间接访问协议:选择寄存器 + 数据寄存器两次系统寄存器写/读,配合 ISB 与全局自旋锁,封装为
kryo_l2_*_indirect_reg; - 纯统计、不可采样:
perf stat可用,perf record与 per-task 会话被驱动显式拒绝;溢出中断 + 半量程周期重载 + 32 位差值截断共同保证统计精度; - 与 CPU 生命周期深度绑定:MPIDR 亲和级别确定 CPU 与集群的归属,负责 CPU 下线时 perf 上下文自动迁移,集群整体下线时中断挂起。
以上机制的完整实现见 drivers/perf/qcom_l2_pmu.c,间接寄存器访问层见 drivers/soc/qcom/kryo-l2-accessors.c 及其头文件 include/soc/qcom/kryo-l2-accessors.h,行为说明的权威来源是 Documentation/admin-guide/perf/qcom_l2_pmu.rst。在 Centriq 平台上做 L2 缓存性能剖析时,建议先读format/与events/两个 sysfs 目录确认编码与可用事件,再按“每列至多一事件”的原则组织perf stat事件清单。
【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考