diff --git a/articles/20220415-riscv-tracepoint-jump-label-part3-static-branch.md b/articles/20220415-riscv-tracepoint-jump-label-part3-static-branch.md index 4e29e6677165664dc953e26dcc301ea08f7852f5..e14b037a679c597d9db1acd38cadcaa96f05b4ab 100644 --- a/articles/20220415-riscv-tracepoint-jump-label-part3-static-branch.md +++ b/articles/20220415-riscv-tracepoint-jump-label-part3-static-branch.md @@ -1,5 +1,7 @@ +> Corrector: [TinyCorrect](https://gitee.com/tinylab/tinycorrect) v0.1-rc2 - [spaces header toc codeblock tables urls]
> Author: Falcon
> Date: 2022/04/15
+> Revisor: Falcon
> Project: [RISC-V Linux 内核剖析](https://gitee.com/tinylab/riscv-linux)
> Sponsor: PLCT Lab, ISCAS @@ -104,13 +106,13 @@ label: 从该系列第 2 篇的 “指令长度” 一节可以看到,由于基础指令级(RV32I/RV64I)的编码长度为 32 位,`JUMP_LABEL_NOP_SIZE` 被定义成了 4 字节(32/8)。 -`.option` 部分为汇编伪指令,用于限定 `.option push` 和 `.option pop` 之间的链接时代码优化。其中 `.option norvc` 禁用压缩指令(“C”扩展指令集,16位编码,占2字节),确保动态启停时刚好有 4 字节的 `nop` 和 `JAL offset` 写入空间。而 `.option norelax` 是阻止 Linker Relaxation,从 [RISC-V Assembly Programmer's Manual: .option](https://github.com/riscv-non-isa/riscv-asm-manual/blob/master/riscv-asm.md#.option) 的描述和 [All Aboard, Part 3: Linker Relaxation in the RISC-V Toolchain](https://www.sifive.com/blog/all-aboard-part-3-linker-relaxation-in-riscv-toolchain) 举的例子来看,主要是防止链接时编译器做进一步的指令精简,比如长跳转变为短跳转,虽然看上去这里的 `nop` 和 `jal zero,%l[label]` 都已经没有优化空间。 +`.option` 部分为汇编伪指令,用于限定 `.option push` 和 `.option pop` 之间的链接时代码优化。其中 `.option norvc` 禁用压缩指令(“C”扩展指令集,16位编码,占2字节),确保动态启停时刚好有 4 字节的 `nop` 和 `JAL offset` 写入空间。而 `.option norelax` 是阻止 Linker Relaxation,从 [RISC-V Assembly Programmer's Manual: .option][003] 的描述和 [All Aboard, Part 3: Linker Relaxation in the RISC-V Toolchain][007] 举的例子来看,主要是防止链接时编译器做进一步的指令精简,比如长跳转变为短跳转,虽然看上去这里的 `nop` 和 `jal zero,%l[label]` 都已经没有优化空间。 `.align RISCV_LGPTR` 用于 Data Section 的对齐要求,而 `RISCV_PTR` 用于设定数据空间大小,具体定义见 `arch/riscv/include/asm/asm.h`,这里不做进一步介绍。 接下来重点关注从 `.pushsection` 开始的几条汇编语句: -首先,`.pushsection __jump_table, \"aw\"` 和 `.popsection` 把中间的内容放入名为 `__jump_table` 的 ELF DATA Section,可参考 [Binutils Section](https://sourceware.org/binutils/docs/as/Section.html). +首先,`.pushsection __jump_table, \"aw\"` 和 `.popsection` 把中间的内容放入名为 `__jump_table` 的 ELF DATA Section,可参考 [Binutils Section][005]. 其次,在对齐指令之后,一次存入了三个数据,即上节提到的 `struct jump_entry` 数据结构。可以看到,所有的数据都被表示为相对当前链接地址(`.`)的偏移(offset),其中: @@ -211,7 +213,7 @@ struct static_key { : : "i"(&((char *)key)[branch]) : : label); ``` -先来看这一段,`%0`,在 [arm64](https://gitee.com/mirrors/gcc/blob/master/gcc/config/aarch64/aarch64.cc) 上也写作 `%c0`,表示存放后面的不带符号的整形立即数。 +先来看这一段,`%0`,在 [arm64][001] 上也写作 `%c0`,表示存放后面的不带符号的整形立即数。 而后面的 `&((char *)key)[branch]` 非常关键,它首先把本来指向 `struct static_key` 的 `&key` 强转为 `char *` 的数组,而 `branch` 作为下标,那么当 `branch` 为 `false` 时,这里取到的地址就是 `struct static_key *key`,如果 `branch` 为 `true` 时,则取到的地址为 `&((char *)key)[1]`,即 `&(char *)key + 1`,大体如下图: @@ -267,7 +269,6 @@ int static_key_count(struct static_key *key) } EXPORT_SYMBOL_GPL(static_key_count); - // include/linux/jump_label.h:414 #define static_key_enabled(x) \ @@ -302,12 +303,12 @@ enum jump_label_type { 也就是 `Jump Type` 是 `enabled` 和 `branch` 的异或结果: -id | enabled | branch | Jump Type ----|------------|---------|------------- - 1 | 0 | 0 | 0 // JUMP_LABEL_NOP - 2 | 0 | 1 | 1 // JUMP_LABEL_JMP - 3 | 1 | 0 | 1 // JUMP_LABEL_JMP - 4 | 1 | 1 | 0 // JUMP_LABEL_NOP +| id | enabled | branch | Jump Type | +|----|---------|--------|---------------------| +| 1 | 0 | 0 | 0 // JUMP_LABEL_NOP | +| 2 | 0 | 1 | 1 // JUMP_LABEL_JMP | +| 3 | 1 | 0 | 1 // JUMP_LABEL_JMP | +| 4 | 1 | 1 | 0 // JUMP_LABEL_NOP | 考虑初始为 `false` 的情况,当 `enabled` 为 0 时,初始为 `JUMP_LABEL_NOP`,此时调用位置被替换为 `static_key_false(key)`: @@ -521,7 +522,7 @@ void arch_jump_label_transform(struct jump_entry *entry, // 从 __jump_table ## Tracepoint 如何使用 Jump Label -### 以 `sched_wait_task` Tracepoint 为例 +### 以 sched_wait_task Tracepoint 为例 先从用户态来看看内核提供的 Tracing events 接口: @@ -562,7 +563,7 @@ $ cat /sys/kernel/debug/tracing/events/sched/sched_wait_task/enable 默认为 0,可写入 1 开启。当然,也可以通过 `tracing/set_event` 来进行设置,该 event 的用法这里不做进一步介绍,请参考资料 `Documentation/trace/events.rst`。 -### `sched_wait_task` Tracepoint 加在什么位置 +### sched_wait_task Tracepoint 加在什么位置 内核中通过 `trace_sched_wait_task()` 函数来获取正在等待进入 unschedule 的进程,该 Tracepoint 被加在 `wait_task_inactive()`: @@ -579,7 +580,7 @@ unsigned long wait_task_inactive(struct task_struct *p, unsigned int match_state } ``` -### `trace_sched_wait_task()` 跟踪函数如何定义 +### trace_sched_wait_task() 跟踪函数如何定义 这部分的宏定义非常复杂,套了很多层,理解起来非常困难,大家感兴趣可以参考 `Documentation/trace/tracepoints.rst`。 @@ -628,7 +629,7 @@ static inline void trace_##name(proto) \ .key = STATIC_KEY_INIT_FALSE, ``` -### 如何实现 `sched_wait_task` Tracepoint 的启停 +### 如何实现 sched_wait_task Tracepoint 的启停 本文前面已经介绍了启停接口:`static_key_enable()` 和 `static_key_disable()`,Tracepoint 就是通过它们实现启用和停止的。 @@ -792,6 +793,7 @@ kernel/sched/stats.h:#define schedstat_val_or_zero(var) ((schedstat_enabled()) kernel/sched/stats.h: if (schedstat_enabled()) kernel/sched/stats.h:# define schedstat_enabled() 0 ``` + 根据第 1 部分测试的数据以及 `Documentation/staging/static-keys.rst` 中的性能数据分析,可以预期这一块会对整体性能有一定的提升效果。 接下来是最终调用启停接口的位置,有两处,一处是通过内核参数传递 `schedstats=enable|disable`,另外一处是 `/proc/sys/kernel/sched_schedstats` 接口。 @@ -868,10 +870,18 @@ patch_text_nosync(addr, &insn, sizeof(insn)); ## 参考资料 -* [Jump Label](https://lwn.net/Articles/412072/) -* [Tracepoint](https://www.kernel.org/doc/html/latest/core-api/tracepoint.html) -* [RISC-V Assembly Programmer's Manual](https://github.com/riscv-non-isa/riscv-asm-manual/blob/master/riscv-asm.md) -* [All Aboard, Part 3: Linker Relaxation in the RISC-V Toolchain](https://www.sifive.com/blog/all-aboard-part-3-linker-relaxation-in-riscv-toolchain) +* [Jump Label][004] +* [Tracepoint][006] +* [RISC-V Assembly Programmer's Manual][002] +* [All Aboard, Part 3: Linker Relaxation in the RISC-V Toolchain][007] * Documentation/staging/static-keys.rst * Documentation/trace/events.rst * Documentation/trace/tracepoints.rst + +[001]: https://gitee.com/mirrors/gcc/blob/master/gcc/config/aarch64/aarch64.cc +[002]: https://github.com/riscv-non-isa/riscv-asm-manual/blob/master/riscv-asm.md +[003]: https://github.com/riscv-non-isa/riscv-asm-manual/blob/master/riscv-asm.md#.option +[004]: https://lwn.net/Articles/412072/ +[005]: https://sourceware.org/binutils/docs/as/Section.html +[006]: https://www.kernel.org/doc/html/latest/core-api/tracepoint.html +[007]: https://www.sifive.com/blog/all-aboard-part-3-linker-relaxation-in-riscv-toolchain