Skip to content

Commit 86696e6

Browse files
buslock, irq_stack, tdp_page_fault: bulk of updates
Signed-off-by: Leon <[email protected]>
1 parent ddde02d commit 86696e6

File tree

6 files changed

+467
-100
lines changed

6 files changed

+467
-100
lines changed

arch/intel/buslock.md

+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# Bus Lock
2+
3+
## Bus Lock 探测和处理
4+
### Split Lock 带来的问题
5+
* **Split lock** 是其操作数跨越两个高速缓存行的任何原子操作。由于操作数跨越两个缓存行并且操作必须是原子的,因此系统在 CPU 访问两个缓存行时锁定总线
6+
* **Bus lock**:获取 bus lock 是通过对写回 (WB) 内存的 split locked 访问或对非 WB 存储器的任何锁定的访问来实现的
7+
* 这通常比高速缓存行中的原子操作慢数千个周期
8+
* 它还会破坏其他内核的性能并使整个系统性能极大地降低
9+
### Split lock 的探测
10+
* Intel 处理器可能支持以下一种或两种硬件机制来检测 split lock 和 bus lock
11+
#### `#AC` 探测 Split Lock
12+
* 从 Tremont Atom 开始,CPU split lock 操作可能会在尝试拆分锁定操作时引发对齐检查 (`#AC`) 异常
13+
* Split lock 产生 `#AC` 异常由 `TEST_CTRL` MSR 的第 `29` 位,*split lock 探测位* 来启用
14+
#### `#DB` 探测 Bus Lock
15+
* 某些 CPU 具有在用户指令获取 bus lock 并 **执行后** 通过 `#DB` trap 通知内核的能力
16+
* 这允许内核终止应用程序或强制执行以达到节流的目的
17+
### 软件方面的处理
18+
* 内核 `#AC``#DB` 处理程序根据内核参数 `split_lock_detect` 处理 bus lock。以下是不同选项的摘要:
19+
20+
`split_lock_detect=` | split lock 引起的 `#AC` | bus lock 导致的 `#DB`
21+
---------------------|------------------------|-----------------------
22+
`off` | 什么都不做 | 什么都不做
23+
`warn`(缺省) | 当支持这两个功能时,每个任务警告一次内核 OOPs 并禁用未来的检查,在 `#AC` 中警告 | 每个任务警告一次并继续运行
24+
`fatal` | 当支持这两个功能时,内核 OOPs,并向用户态发送 `SIGBUS`,在 `#AC` 中产生 fatal 错误 | 向用户态发送 `SIGBUS`
25+
`ratelimit:N (0 < N <= 1000)` | 什么都不做 | 将系统范围内的 bus lock 速率限制为每秒 N 次总线锁定,并在总线锁定时发出警告
26+
27+
## Bus Lock 调试异常
28+
* `DB6` 寄存器 bit `11` 在 Bus Lock Trap 异常时由处理器清零
29+
* 当使用 `DEBUGCTL``IA32_DEBUGCTL` MSR `0x1D9`)的第 `2` 位启用 Bus Lock Trap 时,任何导致总线锁定的指令(主要是使用 `LOCK` 前缀在不可缓存内存上执行内存原子操作的指令)将清除 `DR6` 的第 `11` 位并导致 trap 类型为 `#DB` 的异常
30+
* **注意**:处理器不会以其他方式设置或清除此位
31+
* 为避免在辩别调试异常时出现混淆,软件调试异常处理程序应在返回被中断任务之前将此位设置为 `1`
32+
* 在不支持 Bus Lock Trap 异常的处理器上,`DR6` 的第 `11` 位是只读位,其作用与第 `10:4` 位相同,都是 `1`
33+
* CPU 使用 `CPUID.(EAX=7, ECX=0).ECX[24]` 枚举对该特性的支持,设置为 `1` 表示支持
34+
*`CPL > 0` 时,硬件仅生成用于 bus lock 检测的`#DB`,以避免在处理第一个 `#DB` 时来自多个 bus lock 的嵌套 `#DB`
35+
* *Breakpoint**bus lock* 都可以在同一指令中触发 `#DB` trap,处理它们的顺序由内核 `#DB` 处理程序选择
36+
*`/proc/cpuinfo` 中的 CPU feature 标志是 `bus_lock_detect`
37+
38+
* 启用 split lock 的调用路径
39+
```c
40+
early_identify_cpu()
41+
-> sld_setup()
42+
-> split_lock_setup()
43+
-> __split_lock_setup() //设置 split lock 的 CPU feature
44+
-> sld_state_setup() //得到 split_lock_detect= 的设置
45+
-> sld_state_show() //在启动时打印 split lock 和 bus lock 的支持情况
46+
```
47+
* 启用 bus lock 的调用路径
48+
```c
49+
identify_cpu()
50+
-> this_cpu->c_init(c)
51+
=> init_intel()
52+
-> split_lock_init()
53+
-> bus_lock_init()
54+
```
55+
56+
### Bus Lock 调试异常虚拟化的支持
57+
* VM exit 时设置 VMCS 的 guest-state area 中的 pending debug exception 字段的第 `11` 位,以指示总线锁定调试异常 pending 但未交付
58+
* 设置此位的 VM exit 也会设置该字段的第 `12` 位(VM exit 还设置第 `12` 位以指示至少遇到一个数据或 I/O 断点并在 `DR7` 中启用,或者发生与 RTM transactional regions 的高级调试相关的调试异常。)
59+
* 启用后,如果处理器检测到一个或多个 bus lock 是在 VMX non-root operation 执行期间导致的,则处理器会生成一个 VM exit, exit reason 为 `74`
60+
* 这种 VM exit 类似于陷阱,在执行获取总线锁的指令后交付
61+
* 如果此 VM exit 的交付被更高优先级的 VM exit 抢占,则 VMCS 中 exit reason 字段的第 `26` 位设置为 `1`
62+
* VMM 可以通过设置 secondary processor-based 执行控制的第 `30` 位,以在 VMX non-root operation 中获取的总线锁时发生 VM exit
63+
* 处理器通过设置 `IA32_VMX_PROCBASED_CTLS2` MSR 的第 `62` 位来枚举对此控制的的支持,设置为 `1` 表示支持
64+
65+
## References
66+
- [Bus lock detection and handling — The Linux Kernel documentation](https://docs.kernel.org/x86/buslock.html)
67+
- [x86 debug register - Wikipedia](https://en.wikipedia.org/wiki/X86_debug_register)
68+
- [Intel Instruction Set Extension Chapter 9](https://software.intel.com/content/dam/develop/public/us/en/documents/architecture-instruction-set-extensions-programming-reference.pdf)
69+
- [[PATCH v6 0_3] x86_bus_lock Enable bus lock detection - Fenghua Yu](https://lore.kernel.org/all/[email protected]/#r)

kernel/iommu.md

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# References
2+
- [Linux x86-64 IOMMU详解(一)——IOMMU简介](https://blog.csdn.net/qq_34719392/article/details/114834467)
3+
- [Linux x86-64 IOMMU详解(二)——SWIOTLB(软件 IOMMU)](https://blog.csdn.net/qq_34719392/article/details/114873284)
4+
- [Linux x86-64 IOMMU详解(三)——Intel IOMMU(硬件 IOMMU)的功能与基本原理](https://blog.csdn.net/qq_34719392/article/details/115374606)
5+
- [Linux x86-64 IOMMU详解(四)——启用Intel IOMMU 的配置](https://blog.csdn.net/qq_34719392/article/details/116153505)
6+
- [Linux x86-64 IOMMU详解(五)——Intel IOMMU 初始化流程](https://blog.csdn.net/qq_34719392/article/details/117563480)
7+
- [Linux x86-64 IOMMU详解(六)——Intel IOMMU 参与下的 DMA Coherent Mapping 流程](https://blog.csdn.net/qq_34719392/article/details/117699839)

kernel/irq_x86-64.md

+155-5
Original file line numberDiff line numberDiff line change
@@ -161,8 +161,8 @@ irq_entries_start
161161
* [[patch V2 00_13] x86_irq_64 Inline irq stack switching](https://lore.kernel.org/all/[email protected]/)
162162
* [x86/entry: Convert system vectors to irq stack macro](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=569dd8b4eb7ef666b467c41b8e8e4f2820d07f67)
163163
* 对于用户程序被中断或者已经有中断正在 per-CPU 的中断栈上被处理的情况,直接调用`__common_interrupt()`
164-
* 中断的是用户程序,用的应该是 trampoline stack
165-
* 已经有中断正在 per-CPU 中断栈上被处理,则使用 *当前内核栈*
164+
* 中断的是用户程序,第一站是 trampoline stack,但随即切换到进程内核栈上去处理中断
165+
* 已经有中断正在 per-CPU 中断栈上被处理,则使用 *当前内核栈*
166166

167167
## 异常栈
168168
* 对于无需进行 privilege-level 变化的情况,比如异常发生时 CPU 运行在内核态
@@ -174,8 +174,147 @@ irq_entries_start
174174
## Privilege-level 发生变化时的栈
175175
* 对于 privilege-level 变化的情况,比如异常或中断发生时 CPU 运行在用户态,handler 要使用的栈的 segment selector 和 stack pointer 是从当前执行任务的 TSS 中获得的。
176176
* 对于 x86-64 Linux 这个栈由`cpu_tss_rw.x86_tss.sp0`指示,也就是 CPU entry trampoline stack
177-
* 对于中断,内核会随后切换到中断栈上去处理中断
178-
* 对于异常,就接着在 trampoline stack 上去处理异常
177+
* 对于中断,内核会随后切换到中断栈上去处理中断(commit 569dd8b4eb7e 后有变化,见上面,也切换到进程内核栈上去处理中断)
178+
* 对于异常,随即切换到进程内核栈上去处理异常
179+
180+
```cpp
181+
/* Device interrupts common/spurious */
182+
DECLARE_IDTENTRY_IRQ(X86_TRAP_OTHER, common_interrupt);
183+
184+
#ifndef __ASSEMBLY__ //对于 C 代码包含该宏
185+
186+
#define DECLARE_IDTENTRY_IRQ(vector, func) \
187+
DECLARE_IDTENTRY_ERRORCODE(vector, func)
188+
189+
#define DECLARE_IDTENTRY_ERRORCODE(vector, func) \
190+
asmlinkage void asm_##func(void); \
191+
asmlinkage void xen_asm_##func(void); \
192+
__visible void func(struct pt_regs *regs, unsigned long error_code)
193+
194+
#else //对于汇编代码包含该宏
195+
/* Entries for common/spurious (device) interrupts */
196+
#define DECLARE_IDTENTRY_IRQ(vector, func) \
197+
idtentry_irq vector func
198+
#endif
199+
```
200+
* 中断处理函数的入口的汇编宏 `idtentry_irq`
201+
```c
202+
/*
203+
* Interrupt entry/exit.
204+
*
205+
+ The interrupt stubs push (vector) onto the stack, which is the error_code
206+
* position of idtentry exceptions, and jump to one of the two idtentry points
207+
* (common/spurious).
208+
*
209+
* common_interrupt is a hotpath, align it to a cache line
210+
*/
211+
.macro idtentry_irq vector cfunc
212+
.p2align CONFIG_X86_L1_CACHE_SHIFT
213+
idtentry \vector asm_\cfunc \cfunc has_error_code=1
214+
.endm
215+
216+
.macro idtentry vector asmsym cfunc has_error_code:req
217+
SYM_CODE_START(\asmsym)
218+
219+
.if \vector == X86_TRAP_BP //vector = X86_TRAP_OTHER,进不来
220+
/* #BP advances %rip to the next instruction */
221+
UNWIND_HINT_IRET_REGS offset=\has_error_code*8 signal=0
222+
.else
223+
UNWIND_HINT_IRET_REGS offset=\has_error_code*8
224+
.endif
225+
226+
ENDBR
227+
ASM_CLAC
228+
cld
229+
230+
.if \has_error_code == 0
231+
pushq $-1 /* ORIG_RAX: no syscall to restart */
232+
.endif
233+
234+
.if \vector == X86_TRAP_BP //vector = X86_TRAP_OTHER,进不来
235+
/*
236+
* If coming from kernel space, create a 6-word gap to allow the
237+
* int3 handler to emulate a call instruction.
238+
*/
239+
testb $3, CS-ORIG_RAX(%rsp)
240+
jnz .Lfrom_usermode_no_gap_\@
241+
.rept 6
242+
pushq 5*8(%rsp)
243+
.endr
244+
UNWIND_HINT_IRET_REGS offset=8
245+
.Lfrom_usermode_no_gap_\@:
246+
.endif
247+
248+
idtentry_body \cfunc \has_error_code
249+
250+
_ASM_NOKPROBE(\asmsym)
251+
SYM_CODE_END(\asmsym)
252+
.endm
253+
254+
/**
255+
* idtentry_body - Macro to emit code calling the C function
256+
* @cfunc: C function to be called
257+
* @has_error_code: Hardware pushed error code on stack
258+
*/
259+
.macro idtentry_body cfunc has_error_code:req
260+
261+
/*
262+
* Call error_entry() and switch to the task stack if from userspace.
263+
*
264+
* When in XENPV, it is already in the task stack, and it can't fault
265+
* for native_iret() nor native_load_gs_index() since XENPV uses its
266+
* own pvops for IRET and load_gs_index(). And it doesn't need to
267+
* switch the CR3. So it can skip invoking error_entry().
268+
*/
269+
ALTERNATIVE "call error_entry; movq %rax, %rsp", \
270+
"call xen_error_entry", X86_FEATURE_XENPV
271+
272+
ENCODE_FRAME_POINTER
273+
UNWIND_HINT_REGS
274+
275+
movq %rsp, %rdi /* pt_regs pointer into 1st argument*/
276+
277+
.if \has_error_code == 1
278+
movq ORIG_RAX(%rsp), %rsi /* get error code into 2nd argument*/
279+
movq $-1, ORIG_RAX(%rsp) /* no syscall to restart */
280+
.endif
281+
282+
call \cfunc //调用 common_interrupt
283+
284+
/* For some configurations \cfunc ends up being a noreturn. */
285+
REACHABLE
286+
287+
jmp error_return
288+
.endm
289+
```
290+
* `sync_regs()` 帮忙找到进程内核栈,真正地将栈切换到进程内核栈在 `call error_entry; movq %rax, %rsp`
291+
```cpp
292+
/*
293+
* Help handler running on a per-cpu (IST or entry trampoline) stack
294+
* to switch to the normal thread stack if the interrupted code was in
295+
* user mode. The actual stack switch is done in entry_64.S
296+
*/
297+
asmlinkage __visible noinstr struct pt_regs *sync_regs(struct pt_regs *eregs)
298+
{
299+
struct pt_regs *regs = (struct pt_regs *)this_cpu_read(pcpu_hot.top_of_stack) - 1;
300+
if (regs != eregs)
301+
*regs = *eregs;
302+
return regs;
303+
}
304+
```
305+
* `idtentry_body`里切换到进程内核栈,`sync_regs()` 的返回值即 `call error_entry` 的返回值,放在 `$rax`
306+
```cpp
307+
call error_entry
308+
PUSH_AND_CLEAR_REGS save_ret=1 //寄存器压栈
309+
testb $3, CS+8(%rsp) //中断/异常发生在内核态 or 用户态?
310+
jz .Lerror_kernelspace //内核态不走下面
311+
/* Put us onto the real thread stack. */
312+
jmp sync_regs //帮忙找到进程内核栈,该函数的返回就是 error_entry 的返回
313+
...
314+
.Lerror_kernelspace:
315+
...
316+
movq %rax, %rsp //sync_regs() 的返回值即 error_entry 的返回值,真正地将栈切换到进程内核栈
317+
```
179318

180319
> When the processor performs a call to the exception- or interrupt-handler procedure:
181320
> * If the handler procedure is going to be executed at a numerically lower privilege level, a stack switch occurs.
@@ -195,7 +334,7 @@ irq_entries_start
195334
>
196335
> -- SDM, Vol. 3A, 6.14.2 64-Bit Mode Stack Frame
197336
198-
* 为什么异常处理发生 oops 时看到的的栈有时是`0xfffffexxxxxxxxxx`有时是`0xffffc90000000000 ~ ffffe8ffffffffff`(对于 5 级页表是`0xffa0000000000000 ~ 0xffd1ffffffffffff`)?
337+
* 为什么异常处理发生 OOPs 时看到的的栈有时是`0xfffffexxxxxxxxxx`有时是`0xffffc90000000000 ~ ffffe8ffffffffff`(对于 5 级页表是`0xffa0000000000000 ~ 0xffd1ffffffffffff`)?
199338
* `0xfffffexxxxxxxxxx` 是 cpu_entry_area mapping 的范围,说明此时使用的是 CPU entry trampoline stack,异常发生时 CPU 在运行的是用户态的程序;
200339
* `0xffffc90000000000 ~ ffffe8ffffffffff` 是 vmalloc/ioremap space 的范围,说明当前内核启用了`CONFIG_VMAP_STACK`,进程内核栈是通过`vmalloc`分配的,异常发生时处于内核态;
201340
* 对于一些出错的极端情况,甚至有可能看到异常在使用中断栈或者 IST 栈。
@@ -380,6 +519,17 @@ struct cea_exception_stacks {
380519
};
381520
```
382521

522+
### 用户态发生 `#MC``#DB` 时的栈
523+
* 虽然 `#MC``#DB` 被 Linux 定义为使用 IST 栈,但发生在用户态时,会通过软件把它切换到进程内核栈上去处理
524+
```c
525+
# define DECLARE_IDTENTRY_MCE(vector, func) \
526+
idtentry_mce_db vector asm_##func func
527+
528+
# define DECLARE_IDTENTRY_DEBUG(vector, func) \
529+
idtentry_mce_db vector asm_##func func
530+
```
531+
* `idtentry_mce_db` 会判断,如果是发生在用户态,用的是汇编宏 `idtentry_body`,之前已经展示过了它是怎么切换栈的了
532+
383533
## CPU Entry Trampoline Stack
384534
* 出于安全的目的,trampoline stack 被引入以支持 x86 KAISER
385535
* https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=7f2590a110b837af5679d08fc25c6227c5a8c497

kernel/sched/sched_linux.md

+2
Original file line numberDiff line numberDiff line change
@@ -1016,3 +1016,5 @@ again:
10161016
* http://linuxperf.com/?p=42
10171017
* [TIF_NEED_RESCHED: Why Is It Needed](http://www.linuxinternals.org/blog/2016/03/20/tif-need-resched-why-is-it-needed/)
10181018
* [What Does an Idle CPU Do?](http://duartes.org/gustavo/blog/post/what-does-an-idle-cpu-do/)
1019+
- [LWN:Linux 新的 EEVDF 调度器!](https://mp.weixin.qq.com/s/MqAzzGU8JCV90wUUWUJbyQ)
1020+
- [An EEVDF CPU scheduler for Linux](https://lwn.net/Articles/925371/)

kernel/trace/ftrace.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -815,5 +815,7 @@ echo '-:dosysopen1' > /sys/kernel/debug/tracing/kprobe_events
815815
* [Debugging the kernel using Ftrace - part 1](https://lwn.net/Articles/365835/)
816816
* [Debugging the kernel using Ftrace - part 2](https://lwn.net/Articles/366796/)
817817
* [Secrets of the Ftrace function tracer](https://lwn.net/Articles/370423/)
818-
* [Kprobe-based Event Tracing — The Linux Kernel documentation](https://www.kernel.org/doc/html/latest/trace/kprobetrace.html)
818+
* [ftrace - Function Tracer — The Linux Kernel documentation](https://www.kernel.org/doc/html/latest/trace/ftrace.html)
819+
* [Event Tracing — The Linux Kernel documentation](https://www.kernel.org/doc/html/latest/trace/events.html)
819820
* [Kernel Probes (Kprobes) — The Linux Kernel documentation](https://www.kernel.org/doc/html/latest/trace/kprobes.html)
821+
* [Kprobe-based Event Tracing — The Linux Kernel documentation](https://www.kernel.org/doc/html/latest/trace/kprobetrace.html)

0 commit comments

Comments
 (0)