Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

docs/zh_CN: add riscv pmu.rst translation

This patch translates Documentation/riscv/pmu.rst into Chinese.

Signed-off-by: Yanteng Si <siyanteng@loongson.cn>
Reviewed-by: Alex Shi <alex.shi@linux.alibaba.com>
Link: https://lore.kernel.org/r/20210228191054.6048-4-siyanteng@loongson.cn
Signed-off-by: Jonathan Corbet <corbet@lwn.net>

authored by

Yanteng Si and committed by
Jonathan Corbet
b52e2a6e cb07e097

+233
+233
Documentation/translations/zh_CN/riscv/pmu.rst
··· 1 + .. include:: ../disclaimer-zh_CN.rst 2 + 3 + :Original: :doc:`../../../riscv/pmu` 4 + :Translator: Yanteng Si <siyanteng@loongson.cn> 5 + 6 + .. _cn_riscv_pmu: 7 + 8 + 9 + ======================== 10 + RISC-V平台上对PMUs的支持 11 + ======================== 12 + 13 + Alan Kao <alankao@andestech.com>, Mar 2018 14 + 15 + 简介 16 + ------------ 17 + 18 + 截止本文撰写时,在The RISC-V ISA Privileged Version 1.10中提到的 perf_event 19 + 相关特性如下: 20 + (详情请查阅手册) 21 + 22 + * [m|s]counteren 23 + * mcycle[h], cycle[h] 24 + * minstret[h], instret[h] 25 + * mhpeventx, mhpcounterx[h] 26 + 27 + 仅有以上这些功能,移植perf需要做很多工作,究其原因是缺少以下通用架构的性能 28 + 监测特性: 29 + 30 + * 启用/停用计数器 31 + 在我们这里,计数器一直在自由运行。 32 + * 计数器溢出引起的中断 33 + 规范中没有这种功能。 34 + * 中断指示器 35 + 不可能所有的计数器都有很多的中断端口,所以需要一个中断指示器让软件来判断 36 + 哪个计数器刚好溢出。 37 + * 写入计数器 38 + 由于内核不能修改计数器,所以会有一个SBI来支持这个功能[1]。 另外,一些厂商 39 + 考虑实现M-S-U型号机器的硬件扩展来直接写入计数器。 40 + 41 + 这篇文档旨在为开发者提供一个在内核中支持PMU的简要指南。下面的章节简要解释了 42 + perf' 机制和待办事项。 43 + 44 + 你可以在这里查看以前的讨论[1][2]。 另外,查看附录中的相关内核结构体可能会有 45 + 帮助。 46 + 47 + 48 + 1. 初始化 49 + --------- 50 + 51 + *riscv_pmu* 是一个类型为 *struct riscv_pmu* 的全局指针,它包含了根据perf内部 52 + 约定的各种方法和PMU-specific参数。人们应该声明这样的实例来代表PMU。 默认情况 53 + 下, *riscv_pmu* 指向一个常量结构体 *riscv_base_pmu* ,它对基准QEMU模型有非常 54 + 基础的支持。 55 + 56 + 57 + 然后他/她可以将实例的指针分配给 *riscv_pmu* ,这样就可以利用已经实现的最小逻 58 + 辑,或者创建他/她自己的 *riscv_init_platform_pmu* 实现。 59 + 60 + 换句话说,现有的 *riscv_base_pmu* 源只是提供了一个参考实现。 开发者可以灵活地 61 + 决定多少部分可用,在最极端的情况下,他们可以根据自己的需要定制每一个函数。 62 + 63 + 64 + 2. Event Initialization 65 + ----------------------- 66 + 67 + 当用户启动perf命令来监控一些事件时,首先会被用户空间的perf工具解释为多个 68 + *perf_event_open* 系统调用,然后进一步调用上一步分配的 *event_init* 成员函数 69 + 的主体。 在 *riscv_base_pmu* 的情况下,就是 *riscv_event_init* 。 70 + 71 + 该功能的主要目的是将用户提供的事件翻译成映射图,从而可以直接对HW-related的控 72 + 制寄存器或计数器进行操作。该翻译基于 *riscv_pmu* 中提供的映射和方法。 73 + 74 + 注意,有些功能也可以在这个阶段完成: 75 + 76 + (1) 中断设置,这个在下一节说; 77 + (2) 特限级设置(仅用户空间、仅内核空间、两者都有); 78 + (3) 析构函数设置。 通常应用 *riscv_destroy_event* 即可; 79 + (4) 对非采样事件的调整,这将被函数应用,如 *perf_adjust_period* ,通常如下:: 80 + 81 + if (!is_sampling_event(event)) { 82 + hwc->sample_period = x86_pmu.max_period; 83 + hwc->last_period = hwc->sample_period; 84 + local64_set(&hwc->period_left, hwc->sample_period); 85 + } 86 + 87 + 88 + 在 *riscv_base_pmu* 的情况下,目前只提供了(3)。 89 + 90 + 91 + 3. 中断 92 + ------- 93 + 94 + 3.1. 中断初始化 95 + 96 + 这种情况经常出现在 *event_init* 方案的开头。通常情况下,这应该是一个代码段,如:: 97 + 98 + int x86_reserve_hardware(void) 99 + { 100 + int err = 0; 101 + 102 + if (!atomic_inc_not_zero(&pmc_refcount)) { 103 + mutex_lock(&pmc_reserve_mutex); 104 + if (atomic_read(&pmc_refcount) == 0) { 105 + if (!reserve_pmc_hardware()) 106 + err = -EBUSY; 107 + else 108 + reserve_ds_buffers(); 109 + } 110 + if (!err) 111 + atomic_inc(&pmc_refcount); 112 + mutex_unlock(&pmc_reserve_mutex); 113 + } 114 + 115 + return err; 116 + } 117 + 118 + 而神奇的是 *reserve_pmc_hardware* ,它通常做原子操作,使实现的IRQ可以从某个全局函 119 + 数指针访问。 而 *release_pmc_hardware* 的作用正好相反,它用在上一节提到的事件分配 120 + 器中。 121 + 122 + (注:从所有架构的实现来看,*reserve/release* 对总是IRQ设置,所以 *pmc_hardware* 123 + 似乎有些误导。 它并不处理事件和物理计数器之间的绑定,这一点将在下一节介绍。) 124 + 125 + 3.2. IRQ结构体 126 + 127 + 基本上,一个IRQ运行以下伪代码:: 128 + 129 + for each hardware counter that triggered this overflow 130 + 131 + get the event of this counter 132 + 133 + // following two steps are defined as *read()*, 134 + // check the section Reading/Writing Counters for details. 135 + count the delta value since previous interrupt 136 + update the event->count (# event occurs) by adding delta, and 137 + event->hw.period_left by subtracting delta 138 + 139 + if the event overflows 140 + sample data 141 + set the counter appropriately for the next overflow 142 + 143 + if the event overflows again 144 + too frequently, throttle this event 145 + fi 146 + fi 147 + 148 + end for 149 + 150 + 然而截至目前,没有一个RISC-V的实现为perf设计了中断,所以具体的实现要在未来完成。 151 + 152 + 4. Reading/Writing 计数 153 + ----------------------- 154 + 155 + 它们看似差不多,但perf对待它们的态度却截然不同。 对于读,在 *struct pmu* 中有一个 156 + *read* 接口,但它的作用不仅仅是读。 根据上下文,*read* 函数不仅要读取计数器的内容 157 + (event->count),还要更新左周期到下一个中断(event->hw.period_left)。 158 + 159 + 但 perf 的核心不需要直接写计数器。 写计数器隐藏在以下两点的抽象化之后, 160 + 1) *pmu->start* ,从字面上看就是开始计数,所以必须把计数器设置成一个合适的值,以 161 + 便下一次中断; 162 + 2)在IRQ里面,应该把计数器设置成同样的合理值。 163 + 164 + 在RISC-V中,读操作不是问题,但写操作就需要费些力气了,因为S模式不允许写计数器。 165 + 166 + 167 + 5. add()/del()/start()/stop() 168 + ----------------------------- 169 + 170 + 基本思想: add()/del() 向PMU添加/删除事件,start()/stop() 启动/停止PMU中某个事件 171 + 的计数器。 所有这些函数都使用相同的参数: *struct perf_event *event* 和 *int flag* 。 172 + 173 + 把 perf 看作一个状态机,那么你会发现这些函数作为这些状态之间的状态转换过程。 174 + 定义了三种状态(event->hw.state): 175 + 176 + * PERF_HES_STOPPED: 计数停止 177 + * PERF_HES_UPTODATE: event->count是最新的 178 + * PERF_HES_ARCH: 依赖于体系结构的用法,。。。我们现在并不需要它。 179 + 180 + 这些状态转换的正常流程如下: 181 + 182 + * 用户启动一个 perf 事件,导致调用 *event_init* 。 183 + * 当被上下文切换进来的时候,*add* 会被 perf core 调用,并带有一个标志 PERF_EF_START, 184 + 也就是说事件被添加后应该被启动。 在这个阶段,如果有的话,一般事件会被绑定到一个物 185 + 理计数器上。当状态变为PERF_HES_STOPPED和PERF_HES_UPTODATE,因为现在已经停止了, 186 + (软件)事件计数不需要更新。 187 + 188 + - 然后调用 *start* ,并启用计数器。 189 + 通过PERF_EF_RELOAD标志,它向计数器写入一个适当的值(详细情况请参考上一节)。 190 + 如果标志不包含PERF_EF_RELOAD,则不会写入任何内容。 191 + 现在状态被重置为none,因为它既没有停止也没有更新(计数已经开始)。 192 + 193 + *当被上下文切换出来时被调用。 然后,它检查出PMU中的所有事件,并调用 *stop* 来更新它们 194 + 的计数。 195 + 196 + - *stop* 被 *del* 和perf核心调用,标志为PERF_EF_UPDATE,它经常以相同的逻辑和 *read* 197 + 共用同一个子程序。 198 + 状态又一次变为PERF_HES_STOPPED和PERF_HES_UPTODATE。 199 + 200 + - 这两对程序的生命周期: *add* 和 *del* 在任务切换时被反复调用;*start* 和 *stop* 在 201 + perf核心需要快速停止和启动时也会被调用,比如在调整中断周期时。 202 + 203 + 目前的实现已经足够了,将来可以很容易地扩展到功能。 204 + 205 + A. 相关结构体 206 + ------------- 207 + 208 + * struct pmu: include/linux/perf_event.h 209 + * struct riscv_pmu: arch/riscv/include/asm/perf_event.h 210 + 211 + 两个结构体都被设计为只读。 212 + 213 + *struct pmu* 定义了一些函数指针接口,它们大多以 *struct perf_event* 作为主参数,根据 214 + perf的内部状态机处理perf事件(详情请查看kernel/events/core.c)。 215 + 216 + *struct riscv_pmu* 定义了PMU的具体参数。 命名遵循所有其它架构的惯例。 217 + 218 + * struct perf_event: include/linux/perf_event.h 219 + * struct hw_perf_event 220 + 221 + 表示 perf 事件的通用结构体,以及硬件相关的细节。 222 + 223 + * struct riscv_hw_events: arch/riscv/include/asm/perf_event.h 224 + 225 + 保存事件状态的结构有两个固定成员。 226 + 事件的数量和事件的数组。 227 + 228 + 参考文献 229 + -------- 230 + 231 + [1] https://github.com/riscv/riscv-linux/pull/124 232 + 233 + [2] https://groups.google.com/a/groups.riscv.org/forum/#!topic/sw-dev/f19TmCNP6yA