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