news 2026/9/10 14:45:31

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Linux 内核 HiSilicon PCIe PMU 驱动全解:用 perf 精确监控 Hip09 的 PCIe 带宽、时延与总线占用

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

【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux

HiSilicon(华为海思)Hip09 芯片为每个 PCIe Core 内置了一个 PCIe Performance Monitoring Unit(PMU),可通过 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 RCiEP(Root 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 流程中驱动使能设备后,映射 BAR2(pcim_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-id>(PCIe 交换单元编号)与<core-id>(PCIe Core 编号),两者从HISI_PCIE_REG_INFO(偏移0xfe8)的高低 16 位解析而来,最终生成hisi_pcie<sicl>_core<core>形式,例如hisi_pcie0_core0

name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "hisi_pcie%u_core%u", sicl_id, core_id);

见 hisi_pcie_alloc_pmu。文档中给出的示例名hisi_pcie0_core0即对应 sicl-id=0、core-id=0 的实例。

perf 框架在/sys/bus/event_source/devices/hisi_pcie<sicl>_core<core>/下自动生成该 PMU 的属性组,驱动通过attr_groups声明了 5 组(见 hisi_pcie_pmu_attr_groups):

属性作用驱动实现
format/描述perf_event_attrconfig(事件)、config1(过滤项)、config2(port/bdf)各字段位宽format 属性表
events/描述全部已文档化事件,perf list的枚举来源events 属性表
identifier读取HISI_PCIE_REG_VERSION0xfe4),标识 PMU 硬件版本identifier_show
bus/bdf_min/bdf_max该 PMU 监控的 Root Port 的总线号及 BDF 范围[bdf_min, bdf_max],取自HISI_PCIE_REG_BDF0xfe0bus/bdf_min/bdf_max
cpumaskPMU 当前绑定的"计数 CPU"cpumask_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,port=0x1/这类命令行中,事件名经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事件码 + 全部过滤条件 + 事件使能/清 0
HISI_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_max
HISI_PCIE_REG_VERSION0xfe432-bit硬件版本(identifier)
HISI_PCIE_REG_INFO0xfe832-bitsicl-id / core-id

见 寄存器定义。

2.1 双计数器配对:为什么"相关事件"必须同组

带宽、时延、总线利用率、缓冲区占用这类指标都是"两个量相除"的结果。例如 RX 内存写时延:计数器累加总延时周期数(delay cycles),扩展计数器累加 RX 写 TLP 报文数,两者相除才得到平均时延。驱动用事件索引的bit 16区分两类计数器(见 EXT_COUNTER_IS_USED 的注释):

  • bit16=0:走HISI_PCIE_CNT(偏移 0x090)
  • bit16=1:走HISI_PCIE_EXT_CNT(偏移 0x110)
  • 低 16 位是"真实"事件索引,直接写入HISI_PCIE_EVENT_CTRLHISI_PCIE_EVENT_M(bit 0-15)

因此rx_mwr_cnt的事件 ID 是0x10010而非0x00010——bit16 置位只是标记它占用扩展计数器,真实事件码仍是0x0010

正因为这两个计数器必须同时开始、同时结束统计才能算出正确比值,文档明确要求:相关事件应放进同一个 perf 事件组。判断两个事件是否"相关"有两种方式:

  1. 按事件名:时延事件形如xxx_latency/xxx_cnt,带宽事件形如xxx_flux/xxx_time
  2. 按事件类型编码:形如event=0xXXXXevent=0x1XXXX(即 bit16 相差的那一对)。

2.2 16 个内置事件全表

驱动以PMU_EVENT_ATTR_ID在 sysfsevents/目录发布以下 16 个事件(见 事件属性表),perf list的枚举即来自这里:

事件名事件 ID方向/类型配对对象
rx_mwr_latency0x0010RX 内存写时延(延时周期)rx_mwr_cnt
rx_mwr_cnt0x10010RX 内存写报文数rx_mwr_latency
rx_mrd_latency0x0210RX 内存读时延rx_mrd_cnt
rx_mrd_cnt0x10210RX 内存读报文数rx_mrd_latency
tx_mrd_latency0x0011TX 内存读时延tx_mrd_cnt
tx_mrd_cnt0x10011TX 内存读报文数tx_mrd_latency
rx_mwr_flux0x0104RX 内存写流量(字节)rx_mwr_time
rx_mwr_time0x10104RX 内存写有效传输时间rx_mwr_flux
rx_mrd_flux0x0804RX 内存读流量rx_mrd_time
rx_mrd_time0x10804RX 内存读有效传输时间rx_mrd_flux
rx_cpl_flux0x2004RX Completion 流量rx_cpl_time
rx_cpl_time0x12004RX Completion 有效传输时间rx_cpl_flux
tx_mwr_flux0x0105TX 内存写流量tx_mwr_time
tx_mwr_time0x10105TX 内存写有效传输时间tx_mwr_flux
tx_mrd_flux0x0405TX 内存读流量tx_mrd_time
tx_mrd_time0x10405TX 内存读有效传输时间tx_mrd_flux
tx_cpl_flux0x1005TX Completion 流量tx_cpl_time
tx_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_update),63-bit 翻转不会导致计数错误。

PCIe PMU 不是 per-CPU 的,但 perf 框架要求事件挂在某个 CPU 上,因此驱动注册了 CPU 热插拔回调:CPU 上线时用cpumask_local_spread()在本 NUMA 节点选一个"计数 CPU",并把 MSI 中断亲和性钉死在该 CPU(hisi_pcie_pmu_online_cpu);若该 CPU 下线则迁移上下文到同节点其它在线 CPU(hisi_pcie_pmu_offline_cpu)。sysfs 中的cpumask文件可以随时查看当前绑定情况。

3. 快速上手:perf list 与 perf stat

3.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,port=0xffff/ $# perf stat -e hisi_pcie0_core0/rx_mwr_cnt,port=0xffff/

注意:这两条分别运行会得到两个独立的时间窗口latency / cnt的比值没有意义。相关事件必须放进同一事件组同时统计:

$# perf stat -e "{hisi_pcie0_core0/rx_mwr_latency,port=0xffff/,hisi_pcie0_core0/rx_mwr_cnt,port=0xffff/}"

花括号{...}是 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_attrconfig1/config2中,由 HISI_PCIE_PMU_FILTER_ATTR 宏 展开的一组位域提取函数读取。PMU 只统计"目标 Root Port 下游流量",因此过滤分为两大类:目标过滤(port/bdf)与统计条件过滤(trig/thr/len_mode)。

4.1 目标过滤:port 与 bdf

port:16 位位图,选中的 bit 对应一个 Root Port(bit 位置按该口在 Core 内的通道划分)。AP 层事件可以多选端口,TL/DL 层事件只能单选。例如目标 Root Port 是0000:00:00.0(x8 口,bit0)时port=0x10000:00:04.0(x4 口,bit8)时port=0x100;两口都监控则port=0x101

$# perf stat -e hisi_pcie0_core0/rx_mwr_latency,port=0x1/ sleep 5

bdf:仅可用于带宽(flux)类事件,按 BDF 选中目标 Endpoint,计数器只统计该 EP 请求产生的流量。例如bdf=0x3900表示目标 EP 的 BDF 是0000:39:00.0

$# perf stat -e hisi_pcie0_core0/rx_mrd_flux,bdf=0x3900/ sleep 5

二者的互斥规则(源码见 hisi_pcie_pmu_get_event_ctrl_val):

  • 二者必须至少设置一个;同时设置时只有port生效(port非零时走HISI_PCIE_TARGET_M直接填位图,port为零时才置HISI_PCIE_TARGET_EN并把bdf写入目标字段)。
  • port未设置或显式置 0(默认)时,bdf过滤器生效——因为bdf=0本身就是合法值(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 DW),trig_mode指定比较方向:

  • trig_mode=0:TLP 长度>条件时开始统计;
  • trig_mode=1:TLP 长度<条件时开始统计。

例如trig_len=4即条件为 2^4 DW(64 字节),trig_mode=0时统计"大 TLP 触发后的带宽":

$# perf stat -e hisi_pcie0_core0/rx_mrd_flux,port=0xffff,trig_len=0x4,trig_mode=1/ sleep 5

驱动侧的约束:trig_len超过 10 会被hisi_pcie_pmu_valid_filter()拒绝(HISI_PCIE_TRIG_MAX_VAL = 10);trig_len非零时才写入HISI_PCIE_TRIG_M(bit 56-59)、HISI_PCIE_TRIG_MODE_M(bit 53)并置HISI_PCIE_TRIG_EN(bit 52)。

4.3 阈值过滤(Threshold filter)

同样仅用于带宽事件。计数器只在 TLP 长度落入指定范围时累计:

  • thr_mode=0:TLP 长度阈值时计数;
  • thr_mode=1:TLP 长度<阈值时计数。

thr_len=4即阈值为 2^4 DW:

$# perf stat -e hisi_pcie0_core0/rx_mrd_flux,port=0xffff,thr_len=0x4,thr_mode=1/ sleep 5

对应硬件位段HISI_PCIE_THR_M(bit 28-31)、HISI_PCIE_THR_MODE_M(bit 27)与HISI_PCIE_THR_EN(bit 26),上限同样是 10(HISI_PCIE_THR_MAX_VAL)。

4.4 TLP 长度口径过滤(len_mode)

统计带宽时,"流量"可以取 TLP 的不同部分。len_mode为 2 位字段,取值语义:

取值含义
0b00保留(行为未定义,勿用)
0b01(1)只统计 TLPpayload带宽
0b10(2)只统计 TLPheader带宽
0b11(3)同时统计 header 与 payload

未显式指定时默认2'b11(头+负载,即真实线上流量口径)。驱动中的默认值常量HISI_PCIE_LEN_M_DEFAULT = 3ULL印证了这一点(见 hisi_pcie_pmu.c)。示例:len_mode=2只数 TLP 头带宽,len_mode=1只数负载带宽:

$# perf stat -e hisi_pcie0_core0/rx_mrd_flux,port=0xffff,len_mode=0x1/ sleep 5

4.5 过滤参数汇总表

参数perf 属性字段位宽适用事件取值说明
portconfig2:0-1516-bit 位图全部bit 对应 Root Port;AP 层事件可多选,TL/DL 层事件单选
bdfconfig2:16-3116-bit BDF仅带宽事件目标 Endpoint 的 BDF,如0x3900= 0000:39:00.0
trig_lenconfig1:5-84-bit,≤10仅带宽事件触发条件,2^N DW
trig_modeconfig1:91-bit仅带宽事件0: > 条件开始统计;1: < 条件
thr_lenconfig1:0-34-bit,≤10仅带宽事件阈值,2^N DW
thr_modeconfig1:41-bit仅带宽事件0: ≥ 阈值计数;1: < 阈值
len_modeconfig1:10-112-bit带宽事件1=payload,2=header,3=两者(默认)
eventconfig:0-1617-bit全部事件码,bit16 决定 CNT 还是 EXT_CNT

5. 事件生命周期:从 perf stat 到寄存器

perf stat -e "{hisi_pcie0_core0/rx_mwr_latency,port=0xffff/,hisi_pcie0_core0/rx_mwr_cnt,port=0xffff/}"为例,perf 框架依次调用驱动实现的回调:

  1. event_init(hisi_pcie_pmu_event_init):校验 type、拒绝 sampling/attach-task,按bit16决定hwc->event_base指向HISI_PCIE_CNT(0x090)还是HISI_PCIE_EXT_CNT(0x110),做过滤合法性与事件组校验,并把event->cpu钉到 PMU 的"计数 CPU"。
  2. add(hisi_pcie_pmu_add):分配通道索引hwc->idx(0-7);空闲通道先写HISI_PCIE_RESET_CNT(bit22)再写HISI_PCIE_INIT_SET(bit34)复位。
  3. pmu_enable:只要还有空通道即向HISI_PCIE_GLOBAL_CTRLHISI_PCIE_GLOBAL_EN,开启整个 PMU。
  4. start(hisi_pcie_pmu_start):核心一步是把事件与全部过滤条件打包成 64-bit 的EVENT_CTRL值写入对应通道:事件码(bit0-15)、目标 port/bdf(bit36-52 + TARGET_EN bit32)、trig(bit52-59)、thr(bit26-31)、len_mode(bit34-35,缺省补 3);然后置HISI_PCIE_EVENT_EN(bit20)使能计数、解中断屏蔽、把计数器装载为BIT(63)
  5. stop/del:读回计数器累加差值(hisi_pcie_pmu_event_update),清事件使能、写HISI_PCIE_INIT_SET清除EVENT_CTRL配置。
  6. pmu_disable:全部事件移除后,向HISI_PCIE_GLOBAL_CTRL写 0 关闭 PMU。

这套流程与文档描述的"相关事件需同时起停"完全吻合——组内所有事件在同一个调度上下文被统一 add/start,硬件上共享同一时钟与触发窗口。

6. 适用前提与排查要点

  • 平台前提:依赖CONFIG_HISI_PCIE_PMUARM64 + PCI),且仅匹配 HUAWEI0xa12d的 RCiEP 设备;非 HiSilicon Hip09 类平台不会出现hisi_pcie*_core*事件源。
  • 先确认监控范围再过滤:用 sysfs 的busbdf_minbdf_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 接口与portbdftrig_*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),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/10 14:44:09

分布式卡尔曼滤波在工业物联网中的应用与优化

1. 离散时间线性系统分布式滤波概述在工业物联网和智能传感网络快速发展的当下&#xff0c;多传感器协同状态估计已成为自动化控制领域的关键技术。传统集中式卡尔曼滤波虽然理论完备&#xff0c;但在大规模网络中存在单点故障风险、通信负担过重等固有缺陷。我们团队在近三年的…

作者头像 李华
网站建设 2026/9/10 14:43:08

Hadoop电影推荐系统:从伪分布式搭建到ALS矩阵分解实战

简介&#xff1a;本资源是一个基于Hadoop分布式框架实现的电影推荐系统完整工程&#xff0c;面向大数据初学者、Java开发人员及推荐系统实践者&#xff0c;解决海量用户行为数据下的个性化推荐建模与并行计算落地问题。压缩包共1117个文件&#xff0c;以379个PHP和169个HTML文件…

作者头像 李华
网站建设 2026/9/10 14:41:47

Android学生信息管理系统毕业设计实战指南

简介&#xff1a;这是一套面向计算机专业本科生的Android毕业设计实战源码&#xff0c;基于Java语言与SQLite本地数据库实现完整的学生信息管理功能&#xff0c;适用于课程设计、毕设选题及移动端开发入门实践。资源包含115个文件&#xff0c;涵盖34个XML布局文件&#xff08;定…

作者头像 李华
网站建设 2026/9/10 14:41:10

MapReduce核心原理与实战:从分而治之到HDFS集成

1. 从“一脸懵”到“真看懂”&#xff1a;MapReduce到底是什么我到现在还记得第一次翻开Hadoop源码、看到MapReduce两个单词时的感受——名字高端、文档晦涩、示例代码看完一遍脑子还是空的。后来在真实项目里把一个几千万行的日志清洗任务跑通、调优、稳定上线之后&#xff0c…

作者头像 李华