详解:perf 驱动的 CXL 内存与协议事件观测)
Linux CXL 性能监控单元CPMU详解perf 驱动的 CXL 内存与协议事件观测【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux本文基于 Linux 内核的 CXL Performance Monitoring Unit (CPMU) 文档 及其对应的内核实现讲解 CXL 3.0 规范中 CPMU 的设备模型、sysfs 事件接口、perf 工具链用法与底层驱动原理。读完后你将能够定位系统中已注册的 CXL PMU 设备、用perf list/perf stat采集 CXL 协议与 DRAM 层事件并理解计数器分配、事件掩码与过滤机制在源码中的实现方式。1. CPMUCXL 规范定义的标准性能监控单元CXL 3.0 规范在 13.2 节Performance Monitoring中定义了 CXL Performance Monitoring UnitCPMU。按照文档描述CXL 组件——例如 Root Port、Switch Upstream Port、End Point——都可以携带任意数量的 CPMU 实例且其能力完全可以通过设备自身发现fully discoverable。规范为所有 CXL 协议报文类型定义了事件并额外提供了一组 CXL 设备上常被统计的事件例如 DRAM 事件。这与 CPU PMU 的思路一致perf 子系统提供一个统一框架各类硬件 PMU 通过标准接口接入。CPMU 驱动在内核中的注册点见 cxl_pmu_probe()其中通过info-pmu (struct pmu) { ... }填入了event_init、add、start、stop、read等回调把 CPMU 挂入 perf 框架。2. CXL 总线上的 PMU 设备模型CPMU 驱动在 CXL 总线上注册一个名为pmu_memX.Y的 perf PMU它代表 memX 的第 Y 个 CPMU。设备节点位于/sys/bus/cxl/device/pmu_memX.Y对应的 perf PMU 注册在/sys/bus/event_sources/devices/cxl_pmu_memX.Y文档同时提醒与其他 CXL 总线设备一样这里的 id 本身没有特定含义具体到哪个 CXL 设备需要通过 CXL 总线上该设备的父设备关系device parent来建立。从源码看这一设备模型由两部分实现devm_cxl_pmu_add()在 CXL 总线上创建cxl_pmu类型的设备。当类型为CXL_PMU_MEMDEV时设备名格式化为pmu_mem%d.%dassoc_id 与 index并把 CXL 父设备作为dev-parent这正是文档中通过父设备定位具体 CXL 设备说法的出处。struct cxl_pmu仅保存设备节点、寄存器基址base、assoc_id、index与类型。寄存器块大小由#define CXL_PMU_REGMAP_SIZE 0xe00定义注释标注其来源于 CXL 3.0 规范的 Table 8-32。CPMU 的寄存器块映射发生在 cxl_map_pmu_regs()它把寄存器组的物理地址通过devm_cxl_iomap_block()映射为长度为CXL_PMU_REGMAP_SIZE的内核虚拟地址区间。3. sysfs 接口format 与 events 目录PMU 驱动通过 sysfs 描述可用事件与过滤选项format 目录描述perf_event_attr结构中config事件 vendor id、group id 和 mask、config1threshold、filter enables和config2filter 参数三个字段的位布局events 目录描述所有有文档定义的事件即perf list中看到的事件。驱动中的 format 属性由 cxl_pmu_format_attr[] 逐项声明位域划分如下属性字段位置含义maskconfig:0-31事件掩码可组合多位gidconfig:32-47事件组 IDvidconfig:48-63事件 Vendor IDthresholdconfig1:0-15计数阈值invertconfig1:16计数取反edgeconfig1:17边沿计数hdm_filter_enconfig1:18使能 HDM decoder 过滤hdmconfig2:0-15HDM decoder 过滤值crb_filter_enconfig1:19使能 Channel/Rank/Bank 过滤crbconfig2:32-63CRB 过滤值值得注意的是过滤属性并非无条件可见cxl_pmu_format_is_visible() 会根据 CPMU 能力寄存器解析出的filter_hdm/filter_crb标志对不支持的过滤器隐藏相应 sysfs 属性。events 目录中的每个事件文件输出configvid:gid:mask组合值由 CXL_PMU_EVENT_ATTR 宏 将 vid/gid/mask 打包为 64 位config值而 cxl_pmu_event_is_visible() 会先查询固定计数器、再查询可配置计数器的事件能力列表决定某个事件在perf list中是否出现。也就是说perf list里能看到的事件取决于硬件上报的 Event Capabilities而非一份静态清单。4. 事件粒度细粒度事件与多 bit 掩码组合perf list中显示的事件是最细粒度fine-grained的事件——每个事件只置位事件掩码中的一个 bit。文档强调更通用的事件可以通过在config中设置多个掩码 bit 来启用例如用一个计数器统计所有 Device to Host 读请求d2h_req_rdcurrd2h_req_rdownd2h_req_rdsharedd2h_req_rdanyd2h_req_rdownnodata这些事件的定义见 cxl_pmu_event_attrs[]按 CXL 规范的报文类型组织为若干组Group ID 定义在 CXL_PMU_GID_* 宏Clock TicksGID 0x0000clock_ticksD2H Request / Response如d2h_req_rdshared、d2h_rsp_rspvhitvH2D Request / Response如h2d_req_snpcur、h2d_rsp_goM2S / S2M 内存层报文CXL.mem 协议如m2s_req_memrd、m2s_rwd_memwr、s2m_bisnp_cur、s2m_drs_memdataCache 数据cachedata_d2h_data、cachedata_h2d_dataDDR / 队列 / 重试 / 限流如ddr_act、ddr_casrd、ddr_refresh、rd_queue_occ、retry_event_trig_by_ecc、thermal_throttle_event等。在可配置计数器上请求掩码必须是该事件组 Supported Events 的子集见 cxl_pmu_find_config_counter_ev_cap() 中的if (msk ~pmu_ev-msk) continue;而固定功能计数器要求掩码精确匹配cxl_pmu_find_fixed_counter_ev_cap() 中msk pmu_ev-msk。5. 实操示例perf list 与 perf stat文档给出了如下用法示例$# perf list cxl_pmu_mem0.0/clock_ticks/ [Kernel PMU event] cxl_pmu_mem0.0/d2h_req_rdshared/ [Kernel PMU event] cxl_pmu_mem0.0/h2d_req_snpcur/ [Kernel PMU event] cxl_pmu_mem0.0/h2d_req_snpdata/ [Kernel PMU event] cxl_pmu_mem0.0/h2d_req_snpinv/ [Kernel PMU event] ----------------------------------------------------------- $# perf stat -a -e cxl_pmu_mem0.0/clock_ticks/ -e cxl_pmu_mem0.0/d2h_req_rdshared/此外硬件厂商还可能实现厂商自定义事件此时可以直接指定 vid/gid/mask$# perf stat -a -e cxl_pmu_mem0.0/vidVID,gidGID,maskMASK/使用perf stat -a统计时驱动侧的调用链为cxl_pmu_event_init()校验并分配计数器索引→cxl_pmu_event_add()分配计数器、置位used_counter_bm→cxl_pmu_event_start()写 filter 寄存器、计数器配置寄存器清 0 计数器后使能读取时由perf_event框架调用cxl_pmu_read()回读计数器差值累加到event-count。6. 限制不支持采样与任务级统计文档明确说明该驱动不支持采样sampling因此perf record不可用由于只支持系统级system-wide计数把事件挂到某个 task 上也不支持。这一点在源码中有直接对应。cxl_pmu_event_init() 开头即拒绝采样和任务附加if (is_sampling_event(event) || event-attach_state PERF_ATTACH_TASK) return -EOPNOTSUPP;同时 PMU 声明了.task_ctx_nr perf_invalid_context与PERF_PMU_CAP_NO_EXCLUDE能力并在事件初始化中强制event-cpu info-on_cpu——所有事件都绑定到 PMU 探测时选定的那个 CPU该 CPU 由 sysfs 的cpumask属性 对外展示。7. 能力发现能力寄存器、计数器类型与中断CPMU 的可发现性体现在驱动探测阶段对硬件寄存器的解析。cxl_pmu_parse_caps() 从能力寄存器CXL_PMU_CAP_REG偏移 0x0读出计数器数量NUM_COUNTERS 1与计数器位宽事件能力寄存器数量NUM_EVN_CAP_REG_SUP 1过滤器能力位HDM decoder 过滤、Channel/Rank/Bank 过滤MSI 向量号MSI_N仅在INT位置位时有效。其中有一项硬性前提能力寄存器必须同时置位WRITEABLE_WHEN_FROZEN与FREEZE否则驱动报Counters not writable while frozen并拒绝加载。计数器分两类由每个 Counter Configuration 寄存器偏移0x200 8*n的低 2 位类型字段区分固定功能计数器FIXED_FUN其事件组索引与事件掩码由寄存器常量给出驱动据此建立event_caps_fixed链表事件掩码取自计数器配置寄存器本身可配置计数器CONFIGURABLE遍历未被固定计数器占用的 Event Capabilities 寄存器偏移0x100 8*n建立event_caps_configurable链表事件掩码取自能力寄存器的 Supported Events Bitmask。固定计数器占用的事件能力寄存器不能再被可配置计数器使用这一约束在解析时通过fixed_counter_event_cap_bm位图维护。计数器与事件的分配逻辑见 cxl_pmu_get_event_idx()先查固定计数器精确匹配未命中再从空闲的可配置计数器中取一个。中断方面CPMU 溢出通过 MSI 上报。cxl_pmu_probe() 中用pci_irq_vector(pdev, info-irq)取向量并以IRQF_SHARED | IRQF_NO_THREAD注册 cxl_pmu_irq()。中断处理程序读取CXL_PMU_OVERFLOW_REG偏移 0x10识别溢出位图对每个溢出计数器调用__cxl_pmu_read(event, true)累加差值并补偿一次计数器位宽大小的回绕最后写回溢出寄存器做确认。读计数时的差值计算在 __cxl_pmu_read()用local64_cmpxchg循环更新prev_count按计数器位宽对差值取模溢出时补加一个2^width。过滤寄存器方面cxl_pmu_event_start() 在启动事件时先写 Filter 配置寄存器Filter ID 0 为 HDM decoder 过滤Filter ID 1 为 CRB 过滤值为 0xFFFFFFFF 表示不过滤再更新计数器配置寄存器置位INT_ON_OVRFLW、FREEZE_ON_OVRFLW、ENABLE按需设置EDGE/INVERT/THRESHOLD对可配置计数器额外写入事件组索引与事件掩码最后清零计数值开始统计。文档中提到的 threshold 语义在此也有注释说明CXL 3.0 规范要求每时钟只计 1 次的事件把 threshold 设为 1设为 0 时实际上会按原始值计数。此外cxl_pmu_event_init() 对 CRB 过滤做了较严格的校验仅 CXL Vendor ID 的事件组可用事件组限定为 DDR、Queue Occupancy、Queue Residency、Retry 四类且事件掩码只能置 1 位——因为多事件同时计数时过滤行为未定义。8. 小结与适用前提综合文档与源码Linux 内核的 CPMU 支持可以概括为硬件前提CXL 3.0及后续版本新增的 DDR 扩展、队列、重试、限流事件组兼容的 CXL 组件CPMU 能力寄存器声明FREEZE与WRITEABLE_WHEN_FROZEN且提供 MSI 中断用户侧接口/sys/bus/cxl/device/pmu_memX.Y设备、/sys/bus/event_sources/devices/cxl_pmu_memX.YPMU以及其下format/events/cpumask目录使用方式perf list发现事件、perf stat -a采集系统级计数多 bit 掩码可实现聚合统计vid...,gid...,mask...语法支持厂商自定义事件能力边界不支持采样perf record不可用、不支持任务级附加事件统一绑定到 PMU 所在 CPU。相关源码入口PMU 核心驱动 drivers/perf/cxl_pmu.c、总线侧设备创建 drivers/cxl/core/pmu.c、寄存器块定义 drivers/cxl/pmu.h 与 cxl_map_pmu_regs()。【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考