Linux 内核 HiSilicon PCIe PMU 驱动全解:用 perf 精确监控 Hip09 的 PCIe 带宽、时延与总线占用

发布时间:2026/9/10 14:45:34
Linux 内核 HiSilicon PCIe PMU 驱动全解:用 perf 精确监控 Hip09 的 PCIe 带宽、时延与总线占用 Linux 内核 HiSilicon PCIe PMU 驱动全解用 perf 精确监控 Hip09 的 PCIe 带宽、时延与总线占用【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linuxHiSilicon华为海思Hip09 芯片为每个 PCIe Core 内置了一个 PCIe Performance Monitoring UnitPMU可通过 Linux perf 事件子系统采集 Root Port 及其下游所有 Endpoint 的带宽bandwidth、时延latency、总线利用率bus utilization和缓冲区占用buffer occupancy数据。本文基于内核文档 hisi-pcie-pmu.rst 和驱动实现 hisi_pcie_pmu.c 展开完整覆盖 PMU 的 sysfs 接口、16 个内置 perf 事件、4 类过滤选项port/bdf、trig、thr、len_mode以及事件分组的底层原理读完后可在基于 Hip09 的 ARM64 平台上直接上手做 PCIe 性能调优。1. PMU 硬件模型与驱动注册1.1 每个 PCIe Core 一个 PMU硬件上每个 PCIe Core 配有 1 个 PMU负责监控该 Core 的多个 Root Port 以及这些 Root Port 下游的所有 Endpoint 设备。PMU 本身以一个 PCI RCiEPRoot Complex Integrated Endpoint设备挂在系统总线上驱动通过 PCI 设备表精确匹配static const struct pci_device_id hisi_pcie_pmu_ids[] { { PCI_DEVICE(PCI_VENDOR_ID_HUAWEI, 0xa12d) }, { 0, } };见 hisi_pcie_pmu.c只匹配 vendor 为 HUAWEI、device ID 为0xa12d的设备因此该驱动实际只在 Hip09 一类的 HiSilicon 平台上生效。probe 流程中驱动使能设备后映射 BAR2pcim_iomap_regions(pdev, BIT(2), DRV_NAME)见 hisi_pcie_pmu.c作为 PMU 寄存器基址随后分配 PMU 结构、注册 MSI 中断、注册 CPU 热插拔回调最后调用perf_pmu_register()把 PMU 挂入 perf 框架见 hisi_pcie_init_pmu。内核配置项为CONFIG_HISI_PCIE_PMUtristate HiSilicon PCIE PERF PMU依赖PCI ARM64模块编译规则在 Makefile帮助文本见 Kconfig。1.2 sysfs 暴露的 PMU 名称与属性PMU 的注册名由两个寄存器字段拼成sicl-idPCIe 交换单元编号与core-idPCIe Core 编号两者从HISI_PCIE_REG_INFO偏移0xfe8的高低 16 位解析而来最终生成hisi_pciesicl_corecore形式例如hisi_pcie0_core0name devm_kasprintf(pdev-dev, GFP_KERNEL, hisi_pcie%u_core%u, sicl_id, core_id);见 hisi_pcie_alloc_pmu。文档中给出的示例名hisi_pcie0_core0即对应 sicl-id0、core-id0 的实例。perf 框架在/sys/bus/event_source/devices/hisi_pciesicl_corecore/下自动生成该 PMU 的属性组驱动通过attr_groups声明了 5 组见 hisi_pcie_pmu_attr_groups属性作用驱动实现format/描述perf_event_attr中config事件、config1过滤项、config2port/bdf各字段位宽format 属性表events/描述全部已文档化事件perf list的枚举来源events 属性表identifier读取HISI_PCIE_REG_VERSION0xfe4标识 PMU 硬件版本identifier_showbus/bdf_min/bdf_max该 PMU 监控的 Root Port 的总线号及 BDF 范围[bdf_min, bdf_max]取自HISI_PCIE_REG_BDF0xfe0bus/bdf_min/bdf_maxcpumaskPMU 当前绑定的计数 CPUcpumask_show其中format组完整定义了 perf 事件描述与硬件寄存器之间的映射关系event config:0-16 thr_len config1:0-3 thr_mode config1:4 trig_len config1:5-8 trig_mode config1:9 len_mode config1:10-11 port config2:0-15 bdf config2:16-31即perf stat -e hisi_pcie0_core0/rx_mwr_latency,port0x1/这类命令行中事件名经events/目录解析进config低 16 位port写进config2低 16 位trig_len/thr_len等写进config1对应位段。2. 寄存器组织与计数器原理PMU 的寄存器以 8 字节步距HISI_PCIE_REG_STEP排布 8 个独立计数通道每个通道拥有一组同名寄存器寄存器偏移宽度用途HISI_PCIE_GLOBAL_CTRL0x0032-bit全局使能0x01开、0关HISI_PCIE_EVENT_CTRL0x01064-bit事件码 全部过滤条件 事件使能/清 0HISI_PCIE_CNT0x09064-bit硬件计数器latency/flux 侧HISI_PCIE_EXT_CNT0x11064-bit扩展计数器cnt/time 侧HISI_PCIE_INT_STAT0x15032-bit溢出中断状态HISI_PCIE_INT_MASK0x15432-bit溢出中断屏蔽HISI_PCIE_REG_BDF0xfe032-bit监控 Root Port 的 bdf_min/bdf_maxHISI_PCIE_REG_VERSION0xfe432-bit硬件版本identifierHISI_PCIE_REG_INFO0xfe832-bitsicl-id / core-id见 寄存器定义。2.1 双计数器配对为什么相关事件必须同组带宽、时延、总线利用率、缓冲区占用这类指标都是两个量相除的结果。例如 RX 内存写时延计数器累加总延时周期数delay cycles扩展计数器累加 RX 写 TLP 报文数两者相除才得到平均时延。驱动用事件索引的bit 16区分两类计数器见 EXT_COUNTER_IS_USED 的注释bit160走HISI_PCIE_CNT偏移 0x090bit161走HISI_PCIE_EXT_CNT偏移 0x110低 16 位是真实事件索引直接写入HISI_PCIE_EVENT_CTRL的HISI_PCIE_EVENT_Mbit 0-15因此rx_mwr_cnt的事件 ID 是0x10010而非0x00010——bit16 置位只是标记它占用扩展计数器真实事件码仍是0x0010。正因为这两个计数器必须同时开始、同时结束统计才能算出正确比值文档明确要求相关事件应放进同一个 perf 事件组。判断两个事件是否相关有两种方式按事件名时延事件形如xxx_latency/xxx_cnt带宽事件形如xxx_flux/xxx_time按事件类型编码形如event0xXXXX与event0x1XXXX即 bit16 相差的那一对。2.2 16 个内置事件全表驱动以PMU_EVENT_ATTR_ID在 sysfsevents/目录发布以下 16 个事件见 事件属性表perf list的枚举即来自这里事件名事件 ID方向/类型配对对象rx_mwr_latency0x0010RX 内存写时延延时周期rx_mwr_cntrx_mwr_cnt0x10010RX 内存写报文数rx_mwr_latencyrx_mrd_latency0x0210RX 内存读时延rx_mrd_cntrx_mrd_cnt0x10210RX 内存读报文数rx_mrd_latencytx_mrd_latency0x0011TX 内存读时延tx_mrd_cnttx_mrd_cnt0x10011TX 内存读报文数tx_mrd_latencyrx_mwr_flux0x0104RX 内存写流量字节rx_mwr_timerx_mwr_time0x10104RX 内存写有效传输时间rx_mwr_fluxrx_mrd_flux0x0804RX 内存读流量rx_mrd_timerx_mrd_time0x10804RX 内存读有效传输时间rx_mrd_fluxrx_cpl_flux0x2004RX Completion 流量rx_cpl_timerx_cpl_time0x12004RX Completion 有效传输时间rx_cpl_fluxtx_mwr_flux0x0105TX 内存写流量tx_mwr_timetx_mwr_time0x10105TX 内存写有效传输时间tx_mwr_fluxtx_mrd_flux0x0405TX 内存读流量tx_mrd_timetx_mrd_time0x10405TX 内存读有效传输时间tx_mrd_fluxtx_cpl_flux0x1005TX Completion 流量tx_cpl_timetx_cpl_time0x11005TX Completion 有效传输时间tx_cpl_flux从 ID 的 bit 16 可以看出每一对里latency/flux侧落在普通计数器、cnt/time侧落在扩展计数器与 2.1 节的编码规则一致。带宽 flux/time平均时延 latency/cnt。2.3 计数、溢出与计数 CPU每个 64-bit 计数器初始值设为BIT_ULL(63)HISI_PCIE_INIT_VAL即只保留 63 位计数空间计满翻转时硬件置位HISI_PCIE_INT_STAT触发 MSI 中断。中断处理函数 hisi_pcie_pmu_irq 遍历 8 个通道清状态、累加event-count并重新装载初值hisi_pcie_pmu_set_period。由于event-count用补差方式累加delta (new_cnt - prev_cnt) GENMASK_64(63,0)见 hisi_pcie_pmu_event_update63-bit 翻转不会导致计数错误。PCIe PMU 不是 per-CPU 的但 perf 框架要求事件挂在某个 CPU 上因此驱动注册了 CPU 热插拔回调CPU 上线时用cpumask_local_spread()在本 NUMA 节点选一个计数 CPU并把 MSI 中断亲和性钉死在该 CPUhisi_pcie_pmu_online_cpu若该 CPU 下线则迁移上下文到同节点其它在线 CPUhisi_pcie_pmu_offline_cpu。sysfs 中的cpumask文件可以随时查看当前绑定情况。3. 快速上手perf list 与 perf stat3.1 列出可用事件$# perf list hisi_pcie0_core0/rx_mwr_latency/ [kernel PMU event] hisi_pcie0_core0/rx_mwr_cnt/ [kernel PMU event]3.2 基本统计不推荐单独使用$# perf stat -e hisi_pcie0_core0/rx_mwr_latency,port0xffff/ $# perf stat -e hisi_pcie0_core0/rx_mwr_cnt,port0xffff/注意这两条分别运行会得到两个独立的时间窗口latency / cnt的比值没有意义。相关事件必须放进同一事件组同时统计$# perf stat -e {hisi_pcie0_core0/rx_mwr_latency,port0xffff/,hisi_pcie0_core0/rx_mwr_cnt,port0xffff/}花括号{...}是 perf 的事件组语法组内事件被强制同时启动/停止。驱动在软件层也做了保障hisi_pcie_pmu_get_event_idx()见 源码会让配置完全相同的相关事件复用同一个硬件通道索引而 hisi_pcie_pmu_validate_event_group 则校验组内不同配置的事件数不超过 8 个计数器HISI_PCIE_MAX_COUNTERS超过即拒绝调度。3.3 能力限制当前驱动不支持 samplingperf record不可用也不支持 attach 到任务-p/-t用法无效。对应到 hisi_pcie_pmu_event_init 中的硬拒绝/* Sampling is not supported. */ if (is_sampling_event(event) || event-attach_state PERF_ATTACH_TASK) return -EOPNOTSUPP;此外 PMU 注册时带有PERF_PMU_CAP_NO_EXCLUDE能力位task_ctx_nr perf_invalid_context即事件只能以系统级global方式运行这与监控 Root Port 全部下游流量的语义相符。4. 过滤选项详解过滤条件全部编码在perf_event_attr的config1/config2中由 HISI_PCIE_PMU_FILTER_ATTR 宏 展开的一组位域提取函数读取。PMU 只统计目标 Root Port 下游流量因此过滤分为两大类目标过滤port/bdf与统计条件过滤trig/thr/len_mode。4.1 目标过滤port 与 bdfport16 位位图选中的 bit 对应一个 Root Portbit 位置按该口在 Core 内的通道划分。AP 层事件可以多选端口TL/DL 层事件只能单选。例如目标 Root Port 是0000:00:00.0x8 口bit0时port0x10000:00:04.0x4 口bit8时port0x100两口都监控则port0x101$# perf stat -e hisi_pcie0_core0/rx_mwr_latency,port0x1/ sleep 5bdf仅可用于带宽flux类事件按 BDF 选中目标 Endpoint计数器只统计该 EP 请求产生的流量。例如bdf0x3900表示目标 EP 的 BDF 是0000:39:00.0$# perf stat -e hisi_pcie0_core0/rx_mrd_flux,bdf0x3900/ sleep 5二者的互斥规则源码见 hisi_pcie_pmu_get_event_ctrl_val二者必须至少设置一个同时设置时只有port生效port非零时走HISI_PCIE_TARGET_M直接填位图port为零时才置HISI_PCIE_TARGET_EN并把bdf写入目标字段。port未设置或显式置 0默认时bdf过滤器生效——因为bdf0本身就是合法值0000:00:00.0。若port未设置且bdf指定的设备不在本 PMU 监控范围其上游 Root Port 不在[bdf_min, bdf_max]内hisi_pcie_pmu_valid_filter()会直接拒绝事件返回-EINVAL校验逻辑见 hisi_pcie_pmu_valid_requester_id通过pci_get_domain_bus_and_slot()找到目标设备再用pcie_find_root_port()上溯到 Root Port最后判断其 BDF 是否落在 PMU 的监控区间。4.2 触发过滤Trigger filter仅用于带宽事件。首次出现 TLP 长度大于/小于触发条件时统计才开始。trig_len指定条件值含义为 2^N DWtrig_mode指定比较方向trig_mode0TLP 长度条件时开始统计trig_mode1TLP 长度条件时开始统计。例如trig_len4即条件为 2^4 DW64 字节trig_mode0时统计大 TLP 触发后的带宽$# perf stat -e hisi_pcie0_core0/rx_mrd_flux,port0xffff,trig_len0x4,trig_mode1/ sleep 5驱动侧的约束trig_len超过 10 会被hisi_pcie_pmu_valid_filter()拒绝HISI_PCIE_TRIG_MAX_VAL 10trig_len非零时才写入HISI_PCIE_TRIG_Mbit 56-59、HISI_PCIE_TRIG_MODE_Mbit 53并置HISI_PCIE_TRIG_ENbit 52。4.3 阈值过滤Threshold filter同样仅用于带宽事件。计数器只在 TLP 长度落入指定范围时累计thr_mode0TLP 长度≥阈值时计数thr_mode1TLP 长度阈值时计数。thr_len4即阈值为 2^4 DW$# perf stat -e hisi_pcie0_core0/rx_mrd_flux,port0xffff,thr_len0x4,thr_mode1/ sleep 5对应硬件位段HISI_PCIE_THR_Mbit 28-31、HISI_PCIE_THR_MODE_Mbit 27与HISI_PCIE_THR_ENbit 26上限同样是 10HISI_PCIE_THR_MAX_VAL。4.4 TLP 长度口径过滤len_mode统计带宽时流量可以取 TLP 的不同部分。len_mode为 2 位字段取值语义取值含义0b00保留行为未定义勿用0b011只统计 TLPpayload带宽0b102只统计 TLPheader带宽0b113同时统计 header 与 payload未显式指定时默认2b11头负载即真实线上流量口径。驱动中的默认值常量HISI_PCIE_LEN_M_DEFAULT 3ULL印证了这一点见 hisi_pcie_pmu.c。示例len_mode2只数 TLP 头带宽len_mode1只数负载带宽$# perf stat -e hisi_pcie0_core0/rx_mrd_flux,port0xffff,len_mode0x1/ sleep 54.5 过滤参数汇总表参数perf 属性字段位宽适用事件取值说明portconfig2:0-1516-bit 位图全部bit 对应 Root PortAP 层事件可多选TL/DL 层事件单选bdfconfig2:16-3116-bit BDF仅带宽事件目标 Endpoint 的 BDF如0x3900 0000:39:00.0trig_lenconfig1:5-84-bit≤10仅带宽事件触发条件2^N DWtrig_modeconfig1:91-bit仅带宽事件0: 条件开始统计1: 条件thr_lenconfig1:0-34-bit≤10仅带宽事件阈值2^N DWthr_modeconfig1:41-bit仅带宽事件0: ≥ 阈值计数1: 阈值len_modeconfig1:10-112-bit带宽事件1payload2header3两者默认eventconfig:0-1617-bit全部事件码bit16 决定 CNT 还是 EXT_CNT5. 事件生命周期从 perf stat 到寄存器以perf stat -e {hisi_pcie0_core0/rx_mwr_latency,port0xffff/,hisi_pcie0_core0/rx_mwr_cnt,port0xffff/}为例perf 框架依次调用驱动实现的回调event_inithisi_pcie_pmu_event_init校验 type、拒绝 sampling/attach-task按bit16决定hwc-event_base指向HISI_PCIE_CNT0x090还是HISI_PCIE_EXT_CNT0x110做过滤合法性与事件组校验并把event-cpu钉到 PMU 的计数 CPU。addhisi_pcie_pmu_add分配通道索引hwc-idx0-7空闲通道先写HISI_PCIE_RESET_CNTbit22再写HISI_PCIE_INIT_SETbit34复位。pmu_enable只要还有空通道即向HISI_PCIE_GLOBAL_CTRL写HISI_PCIE_GLOBAL_EN开启整个 PMU。starthisi_pcie_pmu_start核心一步是把事件与全部过滤条件打包成 64-bit 的EVENT_CTRL值写入对应通道事件码bit0-15、目标 port/bdfbit36-52 TARGET_EN bit32、trigbit52-59、thrbit26-31、len_modebit34-35缺省补 3然后置HISI_PCIE_EVENT_ENbit20使能计数、解中断屏蔽、把计数器装载为BIT(63)。stop/del读回计数器累加差值hisi_pcie_pmu_event_update清事件使能、写HISI_PCIE_INIT_SET清除EVENT_CTRL配置。pmu_disable全部事件移除后向HISI_PCIE_GLOBAL_CTRL写 0 关闭 PMU。这套流程与文档描述的相关事件需同时起停完全吻合——组内所有事件在同一个调度上下文被统一 add/start硬件上共享同一时钟与触发窗口。6. 适用前提与排查要点平台前提依赖CONFIG_HISI_PCIE_PMUARM64 PCI且仅匹配 HUAWEI0xa12d的 RCiEP 设备非 HiSilicon Hip09 类平台不会出现hisi_pcie*_core*事件源。先确认监控范围再过滤用 sysfs 的bus、bdf_min、bdf_max确认 PMU 管辖的 Root Port 区间再决定port位图或bdf目标bdf 越界的事件会被内核直接拒绝-EINVAL而不是静默统计错误流量。必须成组统计比值型指标时延 xxx_latency / xxx_cnt带宽 xxx_flux / xxx_time两个事件必须写进同一{}事件组。不要尝试 perf record 或按任务统计PCIe PMU 只提供全局聚合计数驱动显式返回-EOPNOTSUPP。硬件版本核对读identifier文件HISI_PCIE_REG_VERSION可在多代平台间区分 PMU 硬件版本。7. 小结本文梳理了 Linux 内核中 HiSilicon PCIe PMU 的完整用法与实现驱动 hisi_pcie_pmu.c 把 Hip09 PCIe Core 的 8 通道双计数器硬件抽象成 16 个成对的 perf 事件通过format//events/sysfs 接口与port、bdf、trig_*、thr_*、len_mode五组过滤参数对接 perf 命令行带宽与时延类比值指标必须使用事件组保证同时起停采样与 per-task attach 则明确不支持。对运行在 Hip09 上的存储、HPC 或高性能网络负载这套机制提供了不侵入业务、精确到单个 Root Port / 单个 Endpoint 的 PCIe 性能观测手段。官方文档见 Documentation/admin-guide/perf/hisi-pcie-pmu.rst同系列还有 HiSilicon SoC uncore PMU 文档 与 HNS3 PMU 文档 可对照参考。【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考