← Documents Documentation/arch/x86/tlb.rst GitHub 원문 ↗

Linux 6.18.37 · Architecture

The TLB

x86 TLB의 global flush와 INVLPG 선택 기준, tunable 및 refill 측정을 설명합니다.

Source pathDocumentation/arch/x86/tlb.rst
Source versionLinux v6.18.37
TranslationDUJINLABS 전문 번역 + 해설

요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.

1. 요약·해설

원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.

요약과 해설

tlb.rst:1-83

x86 TLB invalidation은 빠르지만 다른 entry까지 버리는 full flush와, 정밀하지만 page마다 instruction 비용을 내는 `INVLPG` 사이의 선택입니다. kernel은 flush 범위, TLB content·size, microarchitecture를 완전히 알 수 없어 workload에 맞는 trade-off를 사용합니다.

`tlb_single_page_flush_ceiling`을 낮추면 global flush를 더 자주 선택합니다. `trace_tlb_flush()`와 `perf stat`의 page-walk counter로 `INVLPG` 비용과 이후 TLB refill 비용의 균형을 측정할 수 있습니다.

2. 영어 원문 전체

번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.

원문 전체 펼치기
1 .. SPDX-License-Identifier: GPL-2.0
2
3 =======
4 The TLB
5 =======
6
7 When the kernel unmaps or modified the attributes of a range of
8 memory, it has two choices:
9
10 1. Flush the entire TLB with a two-instruction sequence. This is
11 a quick operation, but it causes collateral damage: TLB entries
12 from areas other than the one we are trying to flush will be
13 destroyed and must be refilled later, at some cost.
14 2. Use the invlpg instruction to invalidate a single page at a
15 time. This could potentially cost many more instructions, but
16 it is a much more precise operation, causing no collateral
17 damage to other TLB entries.
18
19 Which method to do depends on a few things:
20
21 1. The size of the flush being performed. A flush of the entire
22 address space is obviously better performed by flushing the
23 entire TLB than doing 2^48/PAGE_SIZE individual flushes.
24 2. The contents of the TLB. If the TLB is empty, then there will
25 be no collateral damage caused by doing the global flush, and
26 all of the individual flush will have ended up being wasted
27 work.
28 3. The size of the TLB. The larger the TLB, the more collateral
29 damage we do with a full flush. So, the larger the TLB, the
30 more attractive an individual flush looks. Data and
31 instructions have separate TLBs, as do different page sizes.
32 4. The microarchitecture. The TLB has become a multi-level
33 cache on modern CPUs, and the global flushes have become more
34 expensive relative to single-page flushes.
35
36 There is obviously no way the kernel can know all these things,
37 especially the contents of the TLB during a given flush. The
38 sizes of the flush will vary greatly depending on the workload as
39 well. There is essentially no "right" point to choose.
40
41 You may be doing too many individual invalidations if you see the
42 invlpg instruction (or instructions _near_ it) show up high in
43 profiles. If you believe that individual invalidations being
44 called too often, you can lower the tunable::
45
46 /sys/kernel/debug/x86/tlb_single_page_flush_ceiling
47
48 This will cause us to do the global flush for more cases.
49 Lowering it to 0 will disable the use of the individual flushes.
50 Setting it to 1 is a very conservative setting and it should
51 never need to be 0 under normal circumstances.
52
53 Despite the fact that a single individual flush on x86 is
54 guaranteed to flush a full 2MB [1]_, hugetlbfs always uses the full
55 flushes. THP is treated exactly the same as normal memory.
56
57 You might see invlpg inside of flush_tlb_mm_range() show up in
58 profiles, or you can use the trace_tlb_flush() tracepoints. to
59 determine how long the flush operations are taking.
60
61 Essentially, you are balancing the cycles you spend doing invlpg
62 with the cycles that you spend refilling the TLB later.
63
64 You can measure how expensive TLB refills are by using
65 performance counters and 'perf stat', like this::
66
67 perf stat -e
68 cpu/event=0x8,umask=0x84,name=dtlb_load_misses_walk_duration/,
69 cpu/event=0x8,umask=0x82,name=dtlb_load_misses_walk_completed/,
70 cpu/event=0x49,umask=0x4,name=dtlb_store_misses_walk_duration/,
71 cpu/event=0x49,umask=0x2,name=dtlb_store_misses_walk_completed/,
72 cpu/event=0x85,umask=0x4,name=itlb_misses_walk_duration/,
73 cpu/event=0x85,umask=0x2,name=itlb_misses_walk_completed/
74
75 That works on an IvyBridge-era CPU (i5-3320M). Different CPUs
76 may have differently-named counters, but they should at least
77 be there in some form. You can use pmu-tools 'ocperf list'
78 (https://github.com/andikleen/pmu-tools) to find the right
79 counters for a given CPU.
80
81 .. [1] A footnote in Intel's SDM "4.10.4.2 Recommended Invalidation"
82 says: "One execution of INVLPG is sufficient even for a page
83 with size greater than 4 KBytes."
84

3. 한국어 전문 번역

영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.

TLB flush 방식 두 가지

1-18

이 문서는 `SPDX-License-Identifier: GPL-2.0`으로 배포됩니다. kernel이 memory range의 mapping을 해제하거나 attribute를 변경할 때 선택할 수 있는 TLB 처리 방식은 두 가지입니다.

  • 두 instruction sequence로 전체 TLB를 flush합니다. 빠른 operation이지만 대상이 아닌 영역의 TLB entry까지 파괴하는 collateral damage가 생기며, 이후 비용을 들여 다시 채워야 합니다.
  • `INVLPG` instruction으로 page를 하나씩 invalidate합니다. 훨씬 많은 instruction이 필요할 수 있지만 정밀하게 대상만 처리하므로 다른 TLB entry에 collateral damage를 주지 않습니다.

flush 방식을 고르는 요인

19-40

어느 방법을 사용할지는 다음 요인에 따라 달라집니다.

  • flush할 범위의 크기입니다. 전체 address space를 flush한다면 `2^48/PAGE_SIZE`번의 개별 flush보다 전체 TLB flush가 명백히 낫습니다.
  • 현재 TLB content입니다. TLB가 비어 있다면 global flush가 collateral damage를 만들지 않고, 개별 flush에 들인 작업은 모두 낭비가 됩니다.
  • TLB 크기입니다. TLB가 클수록 full flush의 collateral damage가 커져 individual flush가 더 매력적입니다. data와 instruction은 서로 다른 TLB를 사용하고 page size별 TLB도 구분됩니다.
  • microarchitecture입니다. 현대 CPU에서 TLB는 multi-level cache가 되었고 global flush는 single-page flush에 비해 더 비싸졌습니다.

kernel이 주어진 flush 시점의 TLB content를 비롯한 모든 요인을 알 방법은 없습니다. workload에 따라 flush size도 크게 달라지므로 본질적으로 하나의 `올바른` 선택 지점은 존재하지 않습니다.

single-page flush 조정과 관찰

41-63

profile 상위에 `INVLPG` instruction이나 그 주변 instruction이 나타난다면 individual invalidation을 너무 많이 수행하고 있을 수 있습니다. 개별 invalidation 호출이 지나치게 잦다고 판단하면 다음 tunable 값을 낮출 수 있습니다.

/sys/kernel/debug/x86/tlb_single_page_flush_ceiling

값을 낮추면 더 많은 경우에 global flush를 사용합니다. 0으로 낮추면 individual flush 사용을 비활성화합니다. 1은 매우 보수적인 설정이며 정상 상황에서는 0까지 낮출 필요가 없어야 합니다.

x86의 single individual flush 한 번이 full 2MB를 flush한다고 보장되지만, `hugetlbfs`는 항상 full flush를 사용합니다. THP는 normal memory와 완전히 동일하게 취급합니다.

profile에서 `flush_tlb_mm_range()` 안의 `INVLPG`를 확인하거나 `trace_tlb_flush()` tracepoint로 flush operation에 걸린 시간을 측정할 수 있습니다.

결국 `INVLPG` 실행에 쓰는 cycle과 이후 TLB를 다시 채우는 데 쓰는 cycle 사이의 균형을 맞추는 문제입니다.

perf로 TLB refill 비용 측정

64-83

performance counter와 `perf stat`을 사용하면 TLB refill 비용을 다음과 같이 측정할 수 있습니다.

perf stat -e
  cpu/event=0x8,umask=0x84,name=dtlb_load_misses_walk_duration/,
  cpu/event=0x8,umask=0x82,name=dtlb_load_misses_walk_completed/,
  cpu/event=0x49,umask=0x4,name=dtlb_store_misses_walk_duration/,
  cpu/event=0x49,umask=0x2,name=dtlb_store_misses_walk_completed/,
  cpu/event=0x85,umask=0x4,name=itlb_misses_walk_duration/,
  cpu/event=0x85,umask=0x2,name=itlb_misses_walk_completed/

이 event 조합은 IvyBridge 세대 CPU인 `i5-3320M`에서 동작합니다. 다른 CPU에서는 counter 이름이 다를 수 있지만 어떤 형태로든 해당 counter를 제공해야 합니다.

특정 CPU에 맞는 counter는 pmu-tools의 `ocperf list`와 https://github.com/andikleen/pmu-tools 에서 찾을 수 있습니다.

Intel SDM `4.10.4.2 Recommended Invalidation`의 footnote에 따르면 page size가 4 KBytes보다 크더라도 `INVLPG` 한 번이면 충분합니다.