尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Linux PowerPC PMU 事件分支(EBB)详解:基于 perf_events 的用户态性能监控中断机制

Linux PowerPC PMU 事件分支(EBB)详解:基于 perf_events 的用户态性能监控中断机制 Linux PowerPC PMU 事件分支EBB详解基于 perf_events 的用户态性能监控中断机制【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux导读Event Based BranchesEBB事件分支是 Power 处理器提供的一项硬件特性当指定事件如 PMU 计数器溢出发生时硬件直接跳转到用户空间预先指定的地址执行处理代码全程无需陷入内核。本文以 Linux 内核源码树中 Documentation/arch/powerpc/pmu-ebb.rst 为主线结合 arch/powerpc/perf/core-book3s.c 的 EBB 实现与 tools/testing/selftests/powerpc/pmu/ebb/ 测试套件系统讲解如何通过 Linuxperf_eventsAPI 配置 Power PMU 生成 EBB包括事件创建约束、启用判定、处理器编写规范以及 fork 语义并深入剖析内核层的调度与上下文切换实现。什么是 EBB硬件直接分支到用户态地址EBBEvent Based Branches允许硬件在特定事件发生时直接分支到用户空间指定的地址执行代码全程无需陷入内核。文档明确说明其完整规范定义于 Power ISA v2.07。当前仓库的 arch/powerpc/include/asm/reg.h 中定义了三组与 EBB 直接相关的特殊寄存器#define SPRN_EBBHR 804 /* Event based branch handler register */ #define SPRN_EBBRR 805 /* Event based branch return register */ #define SPRN_BESCR 806 /* Branch event status and control register */ #define BESCR_GE 0x8000000000000000ULL /* Global Enable */EBBHREBB 处理程序的入口地址Handler Register由用户态通过mtspr(SPRN_EBBHR, entry)写入。EBBRREBB 返回地址寄存器Return Register事件发生后硬件会把被打断的指令地址保存在这里处理程序用rfebb指令返回。BESCR分支事件状态与控制寄存器Branch Event Status and Control Register其 bit 0 为全局使能位BESCR_GEGlobal Enable。此外还有BESCR_PMEPMU EBB 使能与BESCR_PMEOPMU EBB 溢出状态等位详见 selftests 中的使用。需要 EBB 配置的事件类型之一是PMU 异常当性能计数器溢出时硬件不是进入内核中断处理而是把控制权直接交给用户态注册的 EBB 处理程序。这正是本文主题——通过 Linuxperf_eventsAPI 为 Power PMU 配置 EBB。术语约定EBB 事件文档约定全文所称的 EBB eventEBB 事件指的就是一个在attr.config中设置了 EBB 标志的struct perf_event。硬件 PMU 上所有可配置的事件类型都可以作为 EBB 事件。内核侧对该标志的判定位于 arch/powerpc/perf/core-book3s.c 的is_ebb_event()static bool is_ebb_event(struct perf_event *event) { /* * This could be a per-PMU callback, but wed rather avoid the cost. We * check that the PMU supports EBB, meaning those that dont can still * use bit 63 of the event code for something else if they wish. */ return (ppmu-flags PPMU_ARCH_207S) ((event-attr.config PERF_EVENT_CONFIG_EBB_SHIFT) 1); }判定有两个前提其一是当前 PMU 必须具备 Power ISA 2.07ARCH 207S能力标志其二是事件编码的 bit 63 被置位。UAPI 头文件 arch/powerpc/include/uapi/asm/perf_event.h 给出了该位的确切定义/* * We use bit 63 of perf_event_attr.config as a flag to request EBB. */ #define PERF_EVENT_CONFIG_EBB_SHIFT 63值得注意的是内核特意把PMU 是否支持 EBB与bit 63 的语义分开处理不支持 EBB 的 PMU 仍可自由使用 bit 63 编码其它含义互不冲突。设计约束EBB 只适合自我监控文档在 Background 一节阐述了 EBB 的几个根本性设计约束EBB 投递给当前运行进程当 PMU EBB 发生时它被投递给当前正在运行的进程因此 EBB 只能被程序用于自我监控self-monitoring。跨进程附加事件是可能的perf_events特性允许在标准权限检查下为其它进程创建事件EBB 事件也不例外。但是如果目标进程自己没有通过mtspr(BESCR)启用 EBBEBB 永远不会被投递。因此理论上可以出现这样的场景进程 A 为自己启用 EBB 但不配置任何事件之后进程 B 把 EBB 事件附加到进程 A 上从而向 A 投递 EBB。文档坦言尚不清楚这种做法是否真的有用Its not clear if this is actually useful这属于 API 能力的自然延伸而非推荐用法。PMU 全局独占一旦 PMU 被配置为 EBB 模式所有 PMU 中断都投递给用户进程。这意味着只要有一个 EBB 事件被调度到 PMU 上就不能再配置任何非 EBB 事件——EBB 事件无法与常规perf命令或任何其它 perf 事件并发运行。反过来在使用了 EBB 的进程上运行perf命令是安全的内核一般会正常调度 EBB 事件而perf会收到它的事件无法运行的通知。独占与优先级pinned exclusive 的互斥实现EBB 事件与常规事件之间的互斥是通过perf_events既有的pinned钉住与exclusive独占属性实现的EBB 事件优先于其它事件被调度除非对方也是 pinned 事件若一个 EBB 事件与一个常规事件都是 pinned则先启用的那个被调度成功另一个进入错误状态error state。这层语义在内核端由ebb_event_check()arch/powerpc/perf/core-book3s.c强制校验并在启用 EBB 事件一节进一步说明其可观测行为。创建 EBB 事件严格的属性约束要请求一个事件按 EBB 方式计数需满足文档列出的以下规则1. 事件编码 bit 63 置位e-attr.config | (1ull 63);这正是 selftests 中 ebb.c 的event_ebb_init()的实现与 UAPI 的PERF_EVENT_CONFIG_EBB_SHIFT完全对应。2. 必须设置 pinned 与 exclusiveEBB 事件必须以 pinned 和 exclusive 属性创建。若创建的是事件组只有组领导者leader可以设置这两个属性。selftests 中 ebb.c 的event_leader_ebb_init()展示了完整初始化方式void event_leader_ebb_init(struct event *e) { event_ebb_init(e); e-attr.exclusive 1; e-attr.pinned 1; }3. 禁止设置的属性EBB 事件不得设置以下任一属性禁止属性含义原因inherit子进程继承事件EBB 不跨 fork 继承见后文且会破坏任务绑定语义sample_period采样周期EBB 由硬件直接投递无需内核采样freq频率模式同上内核无法介入频率计算enable_on_execexec 时自动启用EBB 使能必须由用户态显式控制内核在ebb_event_check()中逐项校验这些规则static int ebb_event_check(struct perf_event *event) { struct perf_event *leader event-group_leader; /* Event and group leader must agree on EBB */ if (is_ebb_event(leader) ! is_ebb_event(event)) return -EINVAL; if (is_ebb_event(event)) { if (!(event-attach_state PERF_ATTACH_TASK)) return -EINVAL; if (!leader-attr.pinned || !leader-attr.exclusive) return -EINVAL; if (event-attr.freq || event-attr.inherit || event-attr.sample_type || event-attr.sample_period || event-attr.enable_on_exec) return -EINVAL; } return 0; }可见校验共四条组内 EBB 一致性、必须挂到任务PERF_ATTACH_TASK、组长必须 pinnedexclusive、禁止上述属性。任何一条不满足都返回-EINVAL。event_attributes_test.c测试程序即围绕这些约束展开正反用例验证见 event_attributes_test.c。4. 必须附加到任务EBB 事件必须附加到一个任务上通过向perf_event_open()传入pid值指定通常传0表示当前任务。这与PERF_ATTACH_TASK的校验一致——没有任务上下文EBB 无处投递。5. 组内事件必须统一事件组内所有成员必须就 EBB 达成一致要么全部请求 EBB要么全部不请求。内核在ebb_event_check()中通过比较is_ebb_event(leader)与is_ebb_event(event)强制这一点。值得补充的是从该函数可见非领导成员可以有自己的config但组的 pinned/exclusive 属性只看 leader因此一个事件组中只有 leader 的 config bit 63 决定组属性是否合法。6. 必须指定 PMCEBB 事件必须指明要计数所在的 PMCPerformance Monitor Counter这样用户态才能可靠地判断事件被调度到哪一个 PMC 上从而在 EBB 处理程序中读取正确的SPRN_PMCn寄存器。selftests 通过ebb_enable_pmc_counting(pmc)记录到ebb_state.pmc_enable[]与write_pmc()/read_pmc()ebb.c体现这一约定。启用 EBB 事件ioctl / prctl 与 read() 判定事件成功 open 之后需要启用它。文档给出两种途径ioctl() 接口对事件 fd 调用PERF_EVENT_IOC_ENABLEprctl() 接口通过PR_TASK_PERF_EVENTS_ENABLE启用任务的全部事件。selftests 中 ebb.c 的event_enable()走 ioctl 路径而其调用序列展示了完整的启用流程FAIL_IF(event_open(event)); ebb_enable_pmc_counting(1); setup_ebb_handler(standard_ebb_callee); ebb_global_enable(); FAIL_IF(event_enable(event)); if (event_read(event)) { /* ... EBB 未能调度通知父进程错误 ... */ return 2; }这里的关键是文档强调的语义由于perf_eventsAPI 的设计启用事件并不保证它已被调度到 PMU 上。要确认 EBB 事件确实被调度必须对事件执行一次read()。若read()返回 EOF说明事件没有被调度EBB 并未启用。event_read()正是包装了对事件 fd 的read()调用。这一行为的根源是 EBB 事件同时具备 pinned 与 exclusive 属性EBB 事件被启用时会强制把所有非 pinned 事件赶下 PMU此时启用成功但如果 PMU 上已经有一个 pinned 事件那么本次启用不成功——于是read()返回 EOF调用方据此得知 EBB 未生效。一个完整的 EBB 使能序列来自 selftestsebb.c 的ebb_global_enable()与setup_ebb_handler()展示了用户在事件创建/启用之外必须完成的硬件准备工作void ebb_global_enable(void) { /* Enable EBBs globally and PMU EBBs */ mtspr(SPRN_BESCR, 0x8000000100000000ull); mb(); }写入BESCR的值0x8000000100000000含义清晰最高位即BESCR_GE全局使能bit 32 即 PMU EBB 使能位BESCR_PME。同时用户态还必须先写好 EBBHRvoid setup_ebb_handler(void (*callee)(void)) { ... /* Ensure ebb_user_func is set before we set the handler */ mb(); mtspr(SPRN_EBBHR, entry); /* Make sure the handler is set before we return */ mb(); }两个mb()内存屏障分别保证用户回调先于 handler 地址写入和handler 地址写入先于后续操作可见避免竞态。这些步骤对应文档 Background 中目标进程通过mtspr(BESCR)启用 EBB的硬件侧描述。读取与关闭 EBB 事件读取可以对 EBB 事件执行read()但结果是毫无意义的。因为中断被直接投递给用户进程内核无法统计该事件只会返回一个垃圾值junk value。read()在此的唯一用途就是上一节所述的调度判定EOF 表示未调度。关闭用完后像常规事件一样close()即可。如果这是最后一个 EBB 事件PMU 将被解除 EBB 配置之后不再投递任何 PMU EBB。selftests 中的 close_clears_pmcc_test.c 专门验证了关闭事件后用户态对 PMC 的读/写权限MMCR0[PMCC]被正确回收防止权限残留。EBB 处理程序以中断处理器的风格编写用户态代码EBB 处理程序本质上是普通用户空间代码但必须以中断处理程序的风格编写进入 handler 时所有寄存器都是活的possibly live在调用任何其它代码之前必须先把它们保存起来。具体怎么保存由程序自行决定对 C 程序而言一个相对简单的方案是在栈上建立一个中断帧interrupt frame并保存寄存器。仓库中的 ebb_handler.S 给出了完整的参考实现其框架极具教学价值栈布局设计注释中绘制的示意图在用户栈上依次分配 Red zone / ABI Gap、VSR0–VSR63 与 VSCR/FSCR、GPR r0–r31 及 XER/CTR/LR/CCR 保存区、调用者帧总计#define SAVE_AREA ((NR_GPR NR_SPR) * 8 (NR_VSR * 16)) #define CALLER_FRAME 112 #define STACK_FRAME (ABIGAP SAVE_AREA CALLER_FRAME)入口处理stdu r1,-STACK_FRAME(r1)压栈建帧然后依次保存 GPRSAVE_GPR(n)、XER/CTR/LR/CCR再通过stxvd2x保存全部 64 个 VSR 向量寄存器与 FSCR/VSCR 状态位。之后故意用lis n,0xaaaa破坏TRASH除 r1、r13 外的所有 GPR——这验证了 EBB handler 不能依赖任何调用者寄存器的存活值是所有寄存器都可能是活的的直接体现。调用用户回调恢复 TOC 指针RESTORE_TOC后调用 C 侧钩子ebb_hook而 ebb.c 中void ebb_hook(void) { if (ebb_user_func) ebb_user_func(); }将控制权转交给用户注册的standard_ebb_callee之类的回调。返回路径按相反顺序恢复 FSCR、VSCR、全部 VSR、XER/CTR/LR/CCR、GPR最后addi r1,r1,STACK_FRAME撤销栈帧并以特殊指令返回#define RFEBB .long 0x4c0009240x4c000924即rfebbReturn From Event Based Branch指令编码。它从 EBBRR 恢复被打断的指令流这正是 EBB 与普通中断最大的不同——由硬件完成上下文切换的返回无需内核参与。standard_ebb_calleeebb.c展示了处理逻辑的典型写法读SPRN_BESCR检查BESCR_PMEOPMU EBB 溢出位无溢出则记为 spurious伪中断否则递增ebb_count、读取 MMCR0、遍历所有启用的 PMC 用count_pmc()累加计数值最后调用reset_ebb()复位void reset_ebb_with_clear_mask(unsigned long mmcr0_clear_mask) { u64 val; /* 2) clear MMCR0[PMAO] - docs say BESCR[PMEO] should do this */ /* 3) set MMCR0[PMAE] - docs say BESCR[PME] should do this */ val mfspr(SPRN_MMCR0); mtspr(SPRN_MMCR0, (val ~mmcr0_clear_mask) | MMCR0_PMAE); /* 4) clear BESCR[PMEO] */ mtspr(SPRN_BESCRR, BESCR_PMEO); /* 5) set BESCR[PME] */ mtspr(SPRN_BESCRS, BESCR_PME); /* 6) rfebb 1 - done in our caller */ }这个序列恰好对应硬件手册对 EBB handler 退出前的复位要求清除MMCR0[PMAO]/FC冻结标志、重新置位MMCR0[PMAE]、清BESCR[PMEO]、置BESCR[PME]随后由汇编层执行rfebb。fork 语义EBB 不跨进程继承文档明确两条 fork 规则事件不继承EBB 事件不随fork()继承。子进程若想使用 EBB必须为自己重新打开事件。寄存器状态清零BESCR/EBBHR/EBBRR中的 EBB 状态在fork()时被清除。selftests 中 fork_cleanup_test.c 与 ebb_on_child_test.c 分别验证了fork 后子进程 EBB 状态被清理与子进程自愿重新初始化 EBB两条路径ebb_on_willing_child_test.c 则测试了配合协作的子进程场景。内核调度实现pinned/exclusive 如何在 PMU 上落地文档描述的互斥语义在 arch/powerpc/perf/core-book3s.c 中有完整的调度期实现。首次挂入时的used_ebb标记——ebb_event_add()static void ebb_event_add(struct perf_event *event) { if (!is_ebb_event(event) || current-thread.used_ebb) return; /* * IFF this is the first time weve added an EBB event, set * PMXE in the user MMCR0 so we can detect when its cleared by * userspace. We need this so that we can context switch while * userspace is in the EBB handler (where PMXE is 0). */ current-thread.used_ebb 1; current-thread.mmcr0 | MMCR0_PMXE; }首次添加 EBB 事件时内核在用户态可见的 MMCR0 中置位MMCR0_PMXE性能监控异常使能目的是在用户态处于 EBB handlerPMXE 为 0时也能正确完成上下文切换。调度切入ebb_switch_in()——当 PMU 以 EBB 模式切入时向 MMCR0 追加多个用户态可访问位reg.h 定义/* Enable EBB and read/write to all 6 PMCs and BHRB for userspace */ mmcr0 | MMCR0_EBE | MMCR0_BHRBA | MMCR0_PMCC_U6;MMCR0_EBE0x00100000Event based branch enableEBB 主开关MMCR0_BHRBA0x00200000允许用户态访问 BHRB分支历史记录缓冲MMCR0_PMCC_U60x00080000PMC1–6 对用户态可读写。随后把用户态保存的 SIAR/SIER/SDAR 与 MMCR2 合并写回mtspr并为 POWER10/12 等 PPMU_ARCH_31 平台追加 MMCR3/SIER2/SIER3。注释明确合并语义用户 MMCR2 只能置位冻结计数器而不能清位若任务需要自行清位解冻计数器则不应在事件上设置exclude_xxx而应由用户态全权管理 MMCR2。调度切出ebb_switch_out()——当MMCR0_EBE置位时把 SIAR/SIER/SDAR、MMCR0仅保留MMCR0_USER_MASK FC|PMXE|PMAO位、MMCR2 等保存回current-thread供下次切回时恢复#define MMCR0_USER_MASK (MMCR0_FC | MMCR0_PMXE | MMCR0_PMAO) #define MMCR2_USER_MASK 0x4020100804020000UL /* (FC1P|FC2P|FC3P|FC4P|FC5P|FC6P) */ #define SIER_USER_MASK 0x7fffffUL这些掩码表明EBB 模式下用户态只对 MMCR0/MMCR2/SIER 的用户相关子集拥有可见性内核与用户态各自维护自己的那部分状态上下文切换时按位合并——这是 EBB 能在零内核介入下工作的关键机制。POWER8E 硬件缺陷补偿pmao_restore_workaround——该函数处理 POWER8E 的一个已知 PMU 上下文切换缺陷计数器溢出后 PMXE 被清、MMCR0 置 FC/PMAO但 PMU 异常可能延迟到达若在此之前发生上下文切换异常会丢失。解决方式是检测PMAO置位而PMAO_SYNC未置位的状态EBB 场景还要求BESCR_GE置位通过让 PMC6 逼近溢出制造一个新的待决异常待返回用户态时再投递。这一实现细节表明EBB 在真实硬件上的可靠性依赖内核与硬件的协同补偿并非简单的全硬件路径。测试验证selftests 中的 EBB 覆盖矩阵内核为 EBB 提供了完整的自测套件位于 tools/testing/selftests/powerpc/pmu/ebb/Makefile 定义了全部 25 个测试程序覆盖了本文讨论的几乎所有语义测试程序验证要点reg_access_test/regs_access_pmccext_test用户态对 EBB 相关 SPR 与 PMC 的读写访问event_attributes_test创建 EBB 事件的属性约束pinned/exclusive、禁用属性等正反用例cycles_test最基本的 EBB 周期计数cycles_with_freeze_test与计数器冻结MMCR0[FC]交互cycles_with_mmcr2_test用户态 MMCR2 冻结位语义pmc56_overflow_testPMC5/6 溢出路径ebb_vs_cpu_event_test/cpu_event_vs_ebb_testEBB 与 CPU 级事件的互斥cpu_event_pinned_vs_ebb_testEBB 与 pinned CPU 事件同时请求时的仲裁先启用者胜task_event_vs_ebb_test/task_event_pinned_vs_ebb_testEBB 与任务级事件的互斥multi_counter_test多 PMC 同时计数multi_ebb_procs_test多进程 EBBpmae_handling_testPMAE/PMAO 处理close_clears_pmcc_testclose 后回收用户态 PMC 权限instruction_count_test指令计数事件fork_cleanup_testfork 后 EBB 状态清理ebb_on_child_test/ebb_on_willing_child_test子进程 EBB 场景back_to_back_ebbs_test连续 EBB 触发lost_exception_test异常丢失场景no_handler_test未注册 handler 的行为测试统一以 64 位编译CFLAGS -m64并显式关闭 PIE-no-pie因为 ebb_handler.S 中的汇编代码与绝对地址处理依赖非 PIE 布局。运行这些测试前可通过ebb_is_supported()检查PPC_FEATURE2_EBBhwcap2 位见 ebb.c确认硬件支持 EBB——EBB 至少要求 POWER8 及以后的处理器。这一 hwcap 检查与内核侧PPMU_ARCH_207S能力标志is_ebb_event互为印证文档所述的 Power ISA v2.07 特性在软件栈中由这两个地方共同把关。小结EBB 的完整工作链路综合文档与源码一条 PMU EBB 的完整生命周期可归纳为用户态初始化设置attr.configbit 63PERF_EVENT_CONFIG_EBB_SHIFTleader 设置pinnedexclusive组内统一 EBB 意愿指定 PMC通过perf_event_open()pid当前任务创建任务级事件——内核ebb_event_check()校验全部约束用户态硬件准备mtspr(SPRN_EBBHR, handler)注册处理程序mtspr(SPRN_BESCR, GE|PME)全局使能启用与确认ioctl/prctl 启用事件后read()返回非 EOF 才表明事件已调度上 PMUpinned/exclusive 互斥仲裁事件触发PMC 溢出时硬件直接分支到 EBBHR 指向的用户态地址处理程序如 ebb_handler.S 的框架保存全部寄存器、读取 BESCR/PMC、复位 MMCR0/BESCR 后以rfebb返回内核兜底调度切入/切出时通过ebb_switch_in()/ebb_switch_out()按位合并用户态与内核态的 MMCR0/MMCR2/SIER 状态并以pmao_restore_workaround()补偿 POWER8E 硬件缺陷清理close()最后一个 EBB 事件后 PMU 退出 EBB 模式fork()不会继承任何 EBB 状态子进程需重新初始化。EBB 的价值在于把性能监控中断的投递路径从内核中断 → 采样 → 信号/通知缩短为硬件 → 用户态 handler为低延迟、高精度的用户态自监控例如运行时剖析器、硬件事件驱动的自适应优化提供了 Linux 下的官方支持路径。对开发者而言pmu-ebb.rst 是 API 层面的权威指南而 core-book3s.c 与 selftests 套件则分别提供了实现级与可运行示例级的完整参照。【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表