01 · QUESTION
무엇을 확인할 것인가
PTE를 memory에서 바꾼 뒤 remote CPU가 old translation을 더 이상 사용하지 않는 시점은 언제인가?
initiator는 PTE write를 publish하고 mm를 실행할 수 있는 CPU mask를 얻어 local invalidate와 remote request를 보낸다. page-table page free는 모든 대상 CPU의 invalidate completion 뒤에만 가능하다.
range와 full-mm 선택, freed_tables 여부, huge mapping stride와 lazy CPU 제외가 shootdown 비용과 correctness를 결정한다.
Tshoot = Tpublish + max(Tipi_delivery + Tremote_flush) + Tack. CPU 수보다 실제 mm cpumask 크기와 interrupt-off 구간이 tail latency를 지배한다.02 · CONTRACT
공통 계약과 architecture 구현
| architecture | 핵심 mechanism | 실패 형태 | 확인할 상태 |
|---|---|---|---|
| arm64 | TLBI ASIDE1/VAAE1 계열과 DSB/ISB sequence | TLBI만 실행하고 completion barrier를 빼면 page free 뒤 remote walker가 old PA를 사용한다. | TLBI operand의 ASID/VA, range scale, DSB domain과 mm cpumask를 확인한다. |
| x86-64 | INVLPG, INVPCID 또는 CR3 reload와 TLB generation | CPU가 mm를 막 load하는 순간 대상 mask에서 빠지면 stale PTE를 무기한 사용한다. | start/end, stride_shift, freed_tables, new_tlb_gen, INVPCID capability와 target cpumask를 본다. |
| RISC-V | local/remote SFENCE.VMA와 SBI RFENCE 또는 IPI | SBI/firmware가 잘못된 hart mask나 ASID를 받으면 일부 hart에만 stale translation이 남는다. | start/end, page size, ASID, hart mask, SBI return과 remote completion을 확인한다. |
03 · DIAGRAMS
세 그림으로 먼저 읽기
arm64
- mechanism
- TLBI ASIDE1/VAAE1 계열과 DSB/ISB sequence
- state
- range TLBI 지원 여부와 granule에 따라 한 명령 또는 반복 invalidate를 선택한다. inner-shareable TLBI와 DSB가 다른 PE의 completion을 보장한다.
- checkpoint
- TLBI operand의 ASID/VA, range scale, DSB domain과 mm cpumask를 확인한다.
x86-64
- mechanism
- INVLPG, INVPCID 또는 CR3 reload와 TLB generation
- state
- range page 수, ceiling과 freed_tables에 따라 per-page INVLPG와 full context flush를 고른다. remote CPU는 flush info의 new_tlb_gen을 적용한다.
- checkpoint
- start/end, stride_shift, freed_tables, new_tlb_gen, INVPCID capability와 target cpumask를 본다.
RISC-V
- mechanism
- local/remote SFENCE.VMA와 SBI RFENCE 또는 IPI
- state
- ASID와 range 크기에 따라 local fence, IPI 기반 remote fence 또는 SBI remote sfence를 선택한다. hardware range invalidate가 제한적이면 page별 또는 전체 fence로 확대한다.
- checkpoint
- start/end, page size, ASID, hart mask, SBI return과 remote completion을 확인한다.
04 · SOURCE
Linux 6.18.37 원본 코드와 줄별 설명
소스 위치를 고정된 숫자로 복사하지 않고 Linux v6.18.37 tree에서 함수 선언을 다시 찾아 발췌했습니다. 아래 코드와 각 줄의 설명은 1:1로 대응합니다.
arm64 · Linux 6.18.37
TLBI ASIDE1/VAAE1 계열과 DSB/ISB sequence
range TLBI 지원 여부와 granule에 따라 한 명령 또는 반복 invalidate를 선택한다. inner-shareable TLBI와 DSB가 다른 PE의 completion을 보장한다.
원본 코드: arch/arm64/include/asm/tlbflush.h:364-420
364static inline void flush_tlb_all(void)
365{
366 dsb(ishst);
367 __tlbi(vmalle1is);
368 __tlbi_sync_s1ish_kernel();
369 isb();
370}
371
372static inline void flush_tlb_mm(struct mm_struct *mm)
373{
374 unsigned long asid;
375
376 dsb(ishst);
377 asid = __TLBI_VADDR(0, ASID(mm));
378 __tlbi(aside1is, asid);
379 __tlbi_user(aside1is, asid);
380 __tlbi_sync_s1ish(mm);
381 mmu_notifier_arch_invalidate_secondary_tlbs(mm, 0, -1UL);
382}
383
384static inline void __flush_tlb_page_nosync(struct mm_struct *mm,
385 unsigned long uaddr)
386{
387 unsigned long addr;
388
389 dsb(ishst);
390 addr = __TLBI_VADDR(uaddr, ASID(mm));
391 __tlbi(vale1is, addr);
392 __tlbi_user(vale1is, addr);
393 mmu_notifier_arch_invalidate_secondary_tlbs(mm, uaddr & PAGE_MASK,
394 (uaddr & PAGE_MASK) + PAGE_SIZE);
395}
396
397static inline void flush_tlb_page_nosync(struct vm_area_struct *vma,
398 unsigned long uaddr)
399{
400 return __flush_tlb_page_nosync(vma->vm_mm, uaddr);
401}
402
403static inline void flush_tlb_page(struct vm_area_struct *vma,
404 unsigned long uaddr)
405{
406 flush_tlb_page_nosync(vma, uaddr);
407 __tlbi_sync_s1ish(vma->vm_mm);
408}
409
410static inline bool arch_tlbbatch_should_defer(struct mm_struct *mm)
411{
412 return true;
413}
414
415/*
416 * To support TLB batched flush for multiple pages unmapping, we only send
417 * the TLBI for each page in arch_tlbbatch_add_pending() and wait for the
418 * completion at the end in arch_tlbbatch_flush(). Since we've already issued
419 * TLBI for each page so only a DSB is needed to synchronise its effect on the
420 * other CPUs.라인 바이 라인 주석
빈 줄과 전처리 경계도 생략하지 않았습니다. 원본의 57개 줄에 각각 설명을 붙였습니다.
static inline void flush_tlb_all(void)이 함수의 진입 계약이 시작된다. arm64에서 caller context, argument ownership과 반환 시 보장할 architecture state를 먼저 적는다.
{C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
dsb(ishst);memory, translation 또는 instruction-fetch 관찰 순서를 확정하는 architecture 명령이다. 앞에서 publish한 상태와 뒤에서 재사용하는 상태의 경계를 이 줄에 둔다.
__tlbi(vmalle1is);helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
__tlbi_sync_s1ish_kernel();helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
isb();memory, translation 또는 instruction-fetch 관찰 순서를 확정하는 architecture 명령이다. 앞에서 publish한 상태와 뒤에서 재사용하는 상태의 경계를 이 줄에 둔다.
}C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
(blank)빈 줄은 arm64 TLB shootdown: TLBI, INVLPG/INVPCID와 SFENCE.VMA 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
static inline void flush_tlb_mm(struct mm_struct *mm)이 함수의 진입 계약이 시작된다. arm64에서 caller context, argument ownership과 반환 시 보장할 architecture state를 먼저 적는다.
{C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
unsigned long asid;선언 또는 macro 확장 일부다. type의 폭과 signedness, per-CPU/task/object 중 어느 수명을 따르는 값인지 확인한다.
(blank)빈 줄은 arm64 TLB shootdown: TLBI, INVLPG/INVPCID와 SFENCE.VMA 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
dsb(ishst);memory, translation 또는 instruction-fetch 관찰 순서를 확정하는 architecture 명령이다. 앞에서 publish한 상태와 뒤에서 재사용하는 상태의 경계를 이 줄에 둔다.
asid = __TLBI_VADDR(0, ASID(mm));helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
__tlbi(aside1is, asid);inner-shareable domain에서 해당 ASID의 stage-1 translation을 무효화한다.
__tlbi_user(aside1is, asid);helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
__tlbi_sync_s1ish(mm);helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
mmu_notifier_arch_invalidate_secondary_tlbs(mm, 0, -1UL);helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
}C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
(blank)빈 줄은 arm64 TLB shootdown: TLBI, INVLPG/INVPCID와 SFENCE.VMA 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
static inline void __flush_tlb_page_nosync(struct mm_struct *mm,이 줄이 arm64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. TLB shootdown: TLBI, INVLPG/INVPCID와 SFENCE.VMA의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.
unsigned long uaddr)이 줄이 arm64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. TLB shootdown: TLBI, INVLPG/INVPCID와 SFENCE.VMA의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.
{C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
unsigned long addr;선언 또는 macro 확장 일부다. type의 폭과 signedness, per-CPU/task/object 중 어느 수명을 따르는 값인지 확인한다.
(blank)빈 줄은 arm64 TLB shootdown: TLBI, INVLPG/INVPCID와 SFENCE.VMA 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
dsb(ishst);memory, translation 또는 instruction-fetch 관찰 순서를 확정하는 architecture 명령이다. 앞에서 publish한 상태와 뒤에서 재사용하는 상태의 경계를 이 줄에 둔다.
addr = __TLBI_VADDR(uaddr, ASID(mm));helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
__tlbi(vale1is, addr);helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
__tlbi_user(vale1is, addr);helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
mmu_notifier_arch_invalidate_secondary_tlbs(mm, uaddr & PAGE_MASK,이 줄이 arm64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. TLB shootdown: TLBI, INVLPG/INVPCID와 SFENCE.VMA의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.
(uaddr & PAGE_MASK) + PAGE_SIZE);이 줄이 arm64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. TLB shootdown: TLBI, INVLPG/INVPCID와 SFENCE.VMA의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.
}C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
(blank)빈 줄은 arm64 TLB shootdown: TLBI, INVLPG/INVPCID와 SFENCE.VMA 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
static inline void flush_tlb_page_nosync(struct vm_area_struct *vma,이 줄이 arm64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. TLB shootdown: TLBI, INVLPG/INVPCID와 SFENCE.VMA의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.
unsigned long uaddr)이 줄이 arm64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. TLB shootdown: TLBI, INVLPG/INVPCID와 SFENCE.VMA의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.
{C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
return __flush_tlb_page_nosync(vma->vm_mm, uaddr);이 함수가 TLB shootdown: TLBI, INVLPG/INVPCID와 SFENCE.VMA 단계의 결과 또는 오류를 상위 계층에 전달한다. 반환 전에 lock, interrupt state, reference와 hardware active state가 정리됐는지 확인한다.
}C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
(blank)빈 줄은 arm64 TLB shootdown: TLBI, INVLPG/INVPCID와 SFENCE.VMA 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
static inline void flush_tlb_page(struct vm_area_struct *vma,이 줄이 arm64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. TLB shootdown: TLBI, INVLPG/INVPCID와 SFENCE.VMA의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.
unsigned long uaddr)이 줄이 arm64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. TLB shootdown: TLBI, INVLPG/INVPCID와 SFENCE.VMA의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.
{C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
flush_tlb_page_nosync(vma, uaddr);helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
__tlbi_sync_s1ish(vma->vm_mm);helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
}C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
(blank)빈 줄은 arm64 TLB shootdown: TLBI, INVLPG/INVPCID와 SFENCE.VMA 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
static inline bool arch_tlbbatch_should_defer(struct mm_struct *mm)이 함수의 진입 계약이 시작된다. arm64에서 caller context, argument ownership과 반환 시 보장할 architecture state를 먼저 적는다.
{C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
return true;이 함수가 TLB shootdown: TLBI, INVLPG/INVPCID와 SFENCE.VMA 단계의 결과 또는 오류를 상위 계층에 전달한다. 반환 전에 lock, interrupt state, reference와 hardware active state가 정리됐는지 확인한다.
}C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
(blank)빈 줄은 arm64 TLB shootdown: TLBI, INVLPG/INVPCID와 SFENCE.VMA 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
/*Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* To support TLB batched flush for multiple pages unmapping, we only sendLinux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* the TLBI for each page in arch_tlbbatch_add_pending() and wait for theLinux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* completion at the end in arch_tlbbatch_flush(). Since we've already issuedLinux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* TLBI for each page so only a DSB is needed to synchronise its effect on theLinux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* other CPUs.Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
x86-64 · Linux 6.18.37
INVLPG, INVPCID 또는 CR3 reload와 TLB generation
range page 수, ceiling과 freed_tables에 따라 per-page INVLPG와 full context flush를 고른다. remote CPU는 flush info의 new_tlb_gen을 적용한다.
원본 코드: arch/x86/mm/tlb.c:1440-1520
1440{
1441#ifdef CONFIG_DEBUG_VM
1442 /* Complete reentrancy prevention checks */
1443 barrier();
1444 this_cpu_dec(flush_tlb_info_idx);
1445#endif
1446}
1447
1448void flush_tlb_mm_range(struct mm_struct *mm, unsigned long start,
1449 unsigned long end, unsigned int stride_shift,
1450 bool freed_tables)
1451{
1452 struct flush_tlb_info *info;
1453 int cpu = get_cpu();
1454 u64 new_tlb_gen;
1455
1456 /* This is also a barrier that synchronizes with switch_mm(). */
1457 new_tlb_gen = inc_mm_tlb_gen(mm);
1458
1459 info = get_flush_tlb_info(mm, start, end, stride_shift, freed_tables,
1460 new_tlb_gen);
1461
1462 /*
1463 * flush_tlb_multi() is not optimized for the common case in which only
1464 * a local TLB flush is needed. Optimize this use-case by calling
1465 * flush_tlb_func_local() directly in this case.
1466 */
1467 if (mm_global_asid(mm)) {
1468 broadcast_tlb_flush(info);
1469 } else if (cpumask_any_but(mm_cpumask(mm), cpu) < nr_cpu_ids) {
1470 info->trim_cpumask = should_trim_cpumask(mm);
1471 flush_tlb_multi(mm_cpumask(mm), info);
1472 consider_global_asid(mm);
1473 } else if (mm == this_cpu_read(cpu_tlbstate.loaded_mm)) {
1474 lockdep_assert_irqs_enabled();
1475 local_irq_disable();
1476 flush_tlb_func(info);
1477 local_irq_enable();
1478 }
1479
1480 put_flush_tlb_info();
1481 put_cpu();
1482 mmu_notifier_arch_invalidate_secondary_tlbs(mm, start, end);
1483}
1484
1485static void do_flush_tlb_all(void *info)
1486{
1487 count_vm_tlb_event(NR_TLB_REMOTE_FLUSH_RECEIVED);
1488 __flush_tlb_all();
1489}
1490
1491void flush_tlb_all(void)
1492{
1493 count_vm_tlb_event(NR_TLB_REMOTE_FLUSH);
1494
1495 /* First try (faster) hardware-assisted TLB invalidation. */
1496 if (cpu_feature_enabled(X86_FEATURE_INVLPGB))
1497 invlpgb_flush_all();
1498 else
1499 /* Fall back to the IPI-based invalidation. */
1500 on_each_cpu(do_flush_tlb_all, NULL, 1);
1501}
1502
1503/* Flush an arbitrarily large range of memory with INVLPGB. */
1504static void invlpgb_kernel_range_flush(struct flush_tlb_info *info)
1505{
1506 unsigned long addr, nr;
1507
1508 for (addr = info->start; addr < info->end; addr += nr << PAGE_SHIFT) {
1509 nr = (info->end - addr) >> PAGE_SHIFT;
1510
1511 /*
1512 * INVLPGB has a limit on the size of ranges it can
1513 * flush. Break up large flushes.
1514 */
1515 nr = clamp_val(nr, 1, invlpgb_count_max);
1516
1517 invlpgb_flush_addr_nosync(addr, nr);
1518 }
1519 __tlbsync();
1520}라인 바이 라인 주석
빈 줄과 전처리 경계도 생략하지 않았습니다. 원본의 81개 줄에 각각 설명을 붙였습니다.
{C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
#ifdef CONFIG_DEBUG_VMKconfig와 compiler feature에 따라 최종 object에 남는 경로가 달라지는 전처리 경계다. 대상 .config와 disassembly로 실제 선택을 확인한다.
/* Complete reentrancy prevention checks */Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
barrier();helper 또는 architecture operation을 실행한다. x86-64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
this_cpu_dec(flush_tlb_info_idx);helper 또는 architecture operation을 실행한다. x86-64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
#endifKconfig와 compiler feature에 따라 최종 object에 남는 경로가 달라지는 전처리 경계다. 대상 .config와 disassembly로 실제 선택을 확인한다.
}C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
(blank)빈 줄은 x86-64 TLB shootdown: TLBI, INVLPG/INVPCID와 SFENCE.VMA 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
void flush_tlb_mm_range(struct mm_struct *mm, unsigned long start,이 줄이 x86-64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. TLB shootdown: TLBI, INVLPG/INVPCID와 SFENCE.VMA의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.
unsigned long end, unsigned int stride_shift,이 줄이 x86-64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. TLB shootdown: TLBI, INVLPG/INVPCID와 SFENCE.VMA의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.
bool freed_tables)이 줄이 x86-64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. TLB shootdown: TLBI, INVLPG/INVPCID와 SFENCE.VMA의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.
{C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
struct flush_tlb_info *info;선언 또는 macro 확장 일부다. type의 폭과 signedness, per-CPU/task/object 중 어느 수명을 따르는 값인지 확인한다.
int cpu = get_cpu();helper 또는 architecture operation을 실행한다. x86-64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
u64 new_tlb_gen;선언 또는 macro 확장 일부다. type의 폭과 signedness, per-CPU/task/object 중 어느 수명을 따르는 값인지 확인한다.
(blank)빈 줄은 x86-64 TLB shootdown: TLBI, INVLPG/INVPCID와 SFENCE.VMA 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
/* This is also a barrier that synchronizes with switch_mm(). */Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
new_tlb_gen = inc_mm_tlb_gen(mm);PTE 변경 세대를 올려 switch path와 remote flush가 stale generation을 탐지하게 한다.
(blank)빈 줄은 x86-64 TLB shootdown: TLBI, INVLPG/INVPCID와 SFENCE.VMA 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
info = get_flush_tlb_info(mm, start, end, stride_shift, freed_tables,계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.
new_tlb_gen);이 줄이 x86-64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. TLB shootdown: TLBI, INVLPG/INVPCID와 SFENCE.VMA의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.
(blank)빈 줄은 x86-64 TLB shootdown: TLBI, INVLPG/INVPCID와 SFENCE.VMA 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
/*Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* flush_tlb_multi() is not optimized for the common case in which only선택한 CPU mask에 shootdown callback을 보내고 completion을 기다리는 경계다.
* a local TLB flush is needed. Optimize this use-case by callingLinux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* flush_tlb_func_local() directly in this case.Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
*/Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
if (mm_global_asid(mm)) {이 조건이 x86-64 fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.
broadcast_tlb_flush(info);helper 또는 architecture operation을 실행한다. x86-64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
} else if (cpumask_any_but(mm_cpumask(mm), cpu) < nr_cpu_ids) {이 줄이 x86-64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. TLB shootdown: TLBI, INVLPG/INVPCID와 SFENCE.VMA의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.
info->trim_cpumask = should_trim_cpumask(mm);helper 또는 architecture operation을 실행한다. x86-64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
flush_tlb_multi(mm_cpumask(mm), info);선택한 CPU mask에 shootdown callback을 보내고 completion을 기다리는 경계다.
consider_global_asid(mm);helper 또는 architecture operation을 실행한다. x86-64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
} else if (mm == this_cpu_read(cpu_tlbstate.loaded_mm)) {이 줄이 x86-64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. TLB shootdown: TLBI, INVLPG/INVPCID와 SFENCE.VMA의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.
lockdep_assert_irqs_enabled();helper 또는 architecture operation을 실행한다. x86-64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
local_irq_disable();helper 또는 architecture operation을 실행한다. x86-64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
flush_tlb_func(info);helper 또는 architecture operation을 실행한다. x86-64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
local_irq_enable();helper 또는 architecture operation을 실행한다. x86-64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
}C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
(blank)빈 줄은 x86-64 TLB shootdown: TLBI, INVLPG/INVPCID와 SFENCE.VMA 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
put_flush_tlb_info();helper 또는 architecture operation을 실행한다. x86-64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
put_cpu();helper 또는 architecture operation을 실행한다. x86-64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
mmu_notifier_arch_invalidate_secondary_tlbs(mm, start, end);helper 또는 architecture operation을 실행한다. x86-64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
}C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
(blank)빈 줄은 x86-64 TLB shootdown: TLBI, INVLPG/INVPCID와 SFENCE.VMA 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
static void do_flush_tlb_all(void *info)이 함수의 진입 계약이 시작된다. x86-64에서 caller context, argument ownership과 반환 시 보장할 architecture state를 먼저 적는다.
{C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
count_vm_tlb_event(NR_TLB_REMOTE_FLUSH_RECEIVED);helper 또는 architecture operation을 실행한다. x86-64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
__flush_tlb_all();helper 또는 architecture operation을 실행한다. x86-64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
}C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
(blank)빈 줄은 x86-64 TLB shootdown: TLBI, INVLPG/INVPCID와 SFENCE.VMA 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
void flush_tlb_all(void)이 함수의 진입 계약이 시작된다. x86-64에서 caller context, argument ownership과 반환 시 보장할 architecture state를 먼저 적는다.
{C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
count_vm_tlb_event(NR_TLB_REMOTE_FLUSH);helper 또는 architecture operation을 실행한다. x86-64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
(blank)빈 줄은 x86-64 TLB shootdown: TLBI, INVLPG/INVPCID와 SFENCE.VMA 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
/* First try (faster) hardware-assisted TLB invalidation. */Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
if (cpu_feature_enabled(X86_FEATURE_INVLPGB))이 조건이 x86-64 fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.
invlpgb_flush_all();helper 또는 architecture operation을 실행한다. x86-64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
else앞 조건이 성립하지 않았을 때의 대체 경로다. fast path와 같은 ownership, ordering과 반환 계약을 제공해야 한다.
/* Fall back to the IPI-based invalidation. */Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
on_each_cpu(do_flush_tlb_all, NULL, 1);helper 또는 architecture operation을 실행한다. x86-64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
}C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
(blank)빈 줄은 x86-64 TLB shootdown: TLBI, INVLPG/INVPCID와 SFENCE.VMA 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
/* Flush an arbitrarily large range of memory with INVLPGB. */Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
static void invlpgb_kernel_range_flush(struct flush_tlb_info *info)이 함수의 진입 계약이 시작된다. x86-64에서 caller context, argument ownership과 반환 시 보장할 architecture state를 먼저 적는다.
{C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
unsigned long addr, nr;이 줄이 x86-64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. TLB shootdown: TLBI, INVLPG/INVPCID와 SFENCE.VMA의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.
(blank)빈 줄은 x86-64 TLB shootdown: TLBI, INVLPG/INVPCID와 SFENCE.VMA 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
for (addr = info->start; addr < info->end; addr += nr << PAGE_SHIFT) {range, CPU mask, relocation 또는 descriptor를 반복 처리한다. 반복 상한과 중간 실패 때 이미 처리한 항목을 되돌리는 경로를 함께 본다.
nr = (info->end - addr) >> PAGE_SHIFT;계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.
(blank)빈 줄은 x86-64 TLB shootdown: TLBI, INVLPG/INVPCID와 SFENCE.VMA 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
/*Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* INVLPGB has a limit on the size of ranges it canLinux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* flush. Break up large flushes.Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
*/Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
nr = clamp_val(nr, 1, invlpgb_count_max);helper 또는 architecture operation을 실행한다. x86-64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
(blank)빈 줄은 x86-64 TLB shootdown: TLBI, INVLPG/INVPCID와 SFENCE.VMA 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
invlpgb_flush_addr_nosync(addr, nr);helper 또는 architecture operation을 실행한다. x86-64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
}C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
__tlbsync();helper 또는 architecture operation을 실행한다. x86-64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
}C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
RISC-V · Linux 6.18.37
local/remote SFENCE.VMA와 SBI RFENCE 또는 IPI
ASID와 range 크기에 따라 local fence, IPI 기반 remote fence 또는 SBI remote sfence를 선택한다. hardware range invalidate가 제한적이면 page별 또는 전체 fence로 확대한다.
원본 코드: arch/riscv/mm/tlbflush.c:149-213
149 mmu_notifier_arch_invalidate_secondary_tlbs(mm, start, start + size);
150}
151
152void flush_tlb_mm(struct mm_struct *mm)
153{
154 __flush_tlb_range(mm, mm_cpumask(mm), 0, FLUSH_TLB_MAX_SIZE, PAGE_SIZE);
155}
156
157void flush_tlb_mm_range(struct mm_struct *mm,
158 unsigned long start, unsigned long end,
159 unsigned int page_size)
160{
161 __flush_tlb_range(mm, mm_cpumask(mm), start, end - start, page_size);
162}
163
164void flush_tlb_page(struct vm_area_struct *vma, unsigned long addr)
165{
166 __flush_tlb_range(vma->vm_mm, mm_cpumask(vma->vm_mm),
167 addr, PAGE_SIZE, PAGE_SIZE);
168}
169
170void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
171 unsigned long end)
172{
173 unsigned long stride_size;
174
175 if (!is_vm_hugetlb_page(vma)) {
176 stride_size = PAGE_SIZE;
177 } else {
178 stride_size = huge_page_size(hstate_vma(vma));
179
180 /*
181 * As stated in the privileged specification, every PTE in a
182 * NAPOT region must be invalidated, so reset the stride in that
183 * case.
184 */
185 if (has_svnapot()) {
186 if (stride_size >= PGDIR_SIZE)
187 stride_size = PGDIR_SIZE;
188 else if (stride_size >= P4D_SIZE)
189 stride_size = P4D_SIZE;
190 else if (stride_size >= PUD_SIZE)
191 stride_size = PUD_SIZE;
192 else if (stride_size >= PMD_SIZE)
193 stride_size = PMD_SIZE;
194 else
195 stride_size = PAGE_SIZE;
196 }
197 }
198
199 __flush_tlb_range(vma->vm_mm, mm_cpumask(vma->vm_mm),
200 start, end - start, stride_size);
201}
202
203void flush_tlb_kernel_range(unsigned long start, unsigned long end)
204{
205 __flush_tlb_range(NULL, cpu_online_mask,
206 start, end - start, PAGE_SIZE);
207}
208
209#ifdef CONFIG_TRANSPARENT_HUGEPAGE
210void flush_pmd_tlb_range(struct vm_area_struct *vma, unsigned long start,
211 unsigned long end)
212{
213 __flush_tlb_range(vma->vm_mm, mm_cpumask(vma->vm_mm),라인 바이 라인 주석
빈 줄과 전처리 경계도 생략하지 않았습니다. 원본의 65개 줄에 각각 설명을 붙였습니다.
mmu_notifier_arch_invalidate_secondary_tlbs(mm, start, start + size);helper 또는 architecture operation을 실행한다. RISC-V에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
}C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
(blank)빈 줄은 RISC-V TLB shootdown: TLBI, INVLPG/INVPCID와 SFENCE.VMA 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
void flush_tlb_mm(struct mm_struct *mm)이 함수의 진입 계약이 시작된다. RISC-V에서 caller context, argument ownership과 반환 시 보장할 architecture state를 먼저 적는다.
{C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
__flush_tlb_range(mm, mm_cpumask(mm), 0, FLUSH_TLB_MAX_SIZE, PAGE_SIZE);helper 또는 architecture operation을 실행한다. RISC-V에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
}C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
(blank)빈 줄은 RISC-V TLB shootdown: TLBI, INVLPG/INVPCID와 SFENCE.VMA 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
void flush_tlb_mm_range(struct mm_struct *mm,이 줄이 RISC-V의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. TLB shootdown: TLBI, INVLPG/INVPCID와 SFENCE.VMA의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.
unsigned long start, unsigned long end,이 줄이 RISC-V의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. TLB shootdown: TLBI, INVLPG/INVPCID와 SFENCE.VMA의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.
unsigned int page_size)이 줄이 RISC-V의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. TLB shootdown: TLBI, INVLPG/INVPCID와 SFENCE.VMA의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.
{C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
__flush_tlb_range(mm, mm_cpumask(mm), start, end - start, page_size);helper 또는 architecture operation을 실행한다. RISC-V에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
}C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
(blank)빈 줄은 RISC-V TLB shootdown: TLBI, INVLPG/INVPCID와 SFENCE.VMA 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
void flush_tlb_page(struct vm_area_struct *vma, unsigned long addr)이 함수의 진입 계약이 시작된다. RISC-V에서 caller context, argument ownership과 반환 시 보장할 architecture state를 먼저 적는다.
{C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
__flush_tlb_range(vma->vm_mm, mm_cpumask(vma->vm_mm),이 줄이 RISC-V의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. TLB shootdown: TLBI, INVLPG/INVPCID와 SFENCE.VMA의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.
addr, PAGE_SIZE, PAGE_SIZE);이 줄이 RISC-V의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. TLB shootdown: TLBI, INVLPG/INVPCID와 SFENCE.VMA의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.
}C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
(blank)빈 줄은 RISC-V TLB shootdown: TLBI, INVLPG/INVPCID와 SFENCE.VMA 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,이 줄이 RISC-V의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. TLB shootdown: TLBI, INVLPG/INVPCID와 SFENCE.VMA의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.
unsigned long end)이 줄이 RISC-V의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. TLB shootdown: TLBI, INVLPG/INVPCID와 SFENCE.VMA의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.
{C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
unsigned long stride_size;선언 또는 macro 확장 일부다. type의 폭과 signedness, per-CPU/task/object 중 어느 수명을 따르는 값인지 확인한다.
(blank)빈 줄은 RISC-V TLB shootdown: TLBI, INVLPG/INVPCID와 SFENCE.VMA 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
if (!is_vm_hugetlb_page(vma)) {이 조건이 RISC-V fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.
stride_size = PAGE_SIZE;계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.
} else {이 줄이 RISC-V의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. TLB shootdown: TLBI, INVLPG/INVPCID와 SFENCE.VMA의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.
stride_size = huge_page_size(hstate_vma(vma));helper 또는 architecture operation을 실행한다. RISC-V에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
(blank)빈 줄은 RISC-V TLB shootdown: TLBI, INVLPG/INVPCID와 SFENCE.VMA 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
/*Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* As stated in the privileged specification, every PTE in aLinux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* NAPOT region must be invalidated, so reset the stride in thatLinux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* case.Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
*/Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
if (has_svnapot()) {이 조건이 RISC-V fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.
if (stride_size >= PGDIR_SIZE)이 조건이 RISC-V fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.
stride_size = PGDIR_SIZE;계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.
else if (stride_size >= P4D_SIZE)이 조건이 RISC-V fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.
stride_size = P4D_SIZE;계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.
else if (stride_size >= PUD_SIZE)이 조건이 RISC-V fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.
stride_size = PUD_SIZE;계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.
else if (stride_size >= PMD_SIZE)이 조건이 RISC-V fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.
stride_size = PMD_SIZE;계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.
else앞 조건이 성립하지 않았을 때의 대체 경로다. fast path와 같은 ownership, ordering과 반환 계약을 제공해야 한다.
stride_size = PAGE_SIZE;계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.
}C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
}C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
(blank)빈 줄은 RISC-V TLB shootdown: TLBI, INVLPG/INVPCID와 SFENCE.VMA 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
__flush_tlb_range(vma->vm_mm, mm_cpumask(vma->vm_mm),이 줄이 RISC-V의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. TLB shootdown: TLBI, INVLPG/INVPCID와 SFENCE.VMA의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.
start, end - start, stride_size);이 줄이 RISC-V의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. TLB shootdown: TLBI, INVLPG/INVPCID와 SFENCE.VMA의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.
}C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
(blank)빈 줄은 RISC-V TLB shootdown: TLBI, INVLPG/INVPCID와 SFENCE.VMA 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
void flush_tlb_kernel_range(unsigned long start, unsigned long end)이 함수의 진입 계약이 시작된다. RISC-V에서 caller context, argument ownership과 반환 시 보장할 architecture state를 먼저 적는다.
{C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
__flush_tlb_range(NULL, cpu_online_mask,이 줄이 RISC-V의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. TLB shootdown: TLBI, INVLPG/INVPCID와 SFENCE.VMA의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.
start, end - start, PAGE_SIZE);이 줄이 RISC-V의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. TLB shootdown: TLBI, INVLPG/INVPCID와 SFENCE.VMA의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.
}C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
(blank)빈 줄은 RISC-V TLB shootdown: TLBI, INVLPG/INVPCID와 SFENCE.VMA 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
#ifdef CONFIG_TRANSPARENT_HUGEPAGEKconfig와 compiler feature에 따라 최종 object에 남는 경로가 달라지는 전처리 경계다. 대상 .config와 disassembly로 실제 선택을 확인한다.
void flush_pmd_tlb_range(struct vm_area_struct *vma, unsigned long start,이 줄이 RISC-V의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. TLB shootdown: TLBI, INVLPG/INVPCID와 SFENCE.VMA의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.
unsigned long end)이 줄이 RISC-V의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. TLB shootdown: TLBI, INVLPG/INVPCID와 SFENCE.VMA의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.
{C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
__flush_tlb_range(vma->vm_mm, mm_cpumask(vma->vm_mm),이 줄이 RISC-V의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. TLB shootdown: TLBI, INVLPG/INVPCID와 SFENCE.VMA의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.
05 · WORKED EXAMPLE
숫자로 검산하기
4KiB range flush와 full flush 선택
256KiB 범위, 4KiB page, mm 사용 CPU 6개, per-page invalidate 80ns, IPI 왕복 2us라고 가정한다.
- pages256KiB / 4KiB = 64개 translation이다.
- local costpage별이면 64x80ns=5.12us로 full-context flush 비용과 비교한다.
- remote cost6개 CPU에 병렬 IPI를 보내도 완료 시간은 가장 늦은 CPU의 2us+flush 시간이다.
- free boundary모든 ack 뒤에만 unmapped physical page와 table page를 allocator에 반환한다.
결론threshold는 page 수만이 아니라 CPU mask 크기, PCID/ASID 유지 이점과 remote interrupt latency에 따라 달라진다.
06 · DEEP DIVE
경계별 상세 분석
공통 kernel core와 architecture hook의 경계
initiator는 PTE write를 publish하고 mm를 실행할 수 있는 CPU mask를 얻어 local invalidate와 remote request를 보낸다. page-table page free는 모든 대상 CPU의 invalidate completion 뒤에만 가능하다.
range와 full-mm 선택, freed_tables 여부, huge mapping stride와 lazy CPU 제외가 shootdown 비용과 correctness를 결정한다.
arm64: TLBI ASIDE1/VAAE1 계열과 DSB/ISB sequence
range TLBI 지원 여부와 granule에 따라 한 명령 또는 반복 invalidate를 선택한다. inner-shareable TLBI와 DSB가 다른 PE의 completion을 보장한다.
PTE store 뒤 DSB, TLBI, completion DSB와 필요한 ISB 순서를 지킨다. 디버깅할 때는 TLBI operand의 ASID/VA, range scale, DSB domain과 mm cpumask를 확인한다.
x86-64: INVLPG, INVPCID 또는 CR3 reload와 TLB generation
range page 수, ceiling과 freed_tables에 따라 per-page INVLPG와 full context flush를 고른다. remote CPU는 flush info의 new_tlb_gen을 적용한다.
switch_mm의 loaded_mm/tlb_gen publication과 shootdown side의 mm_cpumask read가 ordering pair를 이룬다. 디버깅할 때는 start/end, stride_shift, freed_tables, new_tlb_gen, INVPCID capability와 target cpumask를 본다.
RISC-V: local/remote SFENCE.VMA와 SBI RFENCE 또는 IPI
ASID와 range 크기에 따라 local fence, IPI 기반 remote fence 또는 SBI remote sfence를 선택한다. hardware range invalidate가 제한적이면 page별 또는 전체 fence로 확대한다.
PTE store의 visibility와 remote fence request ordering이 보장되어야 receiver가 새 PTE를 읽는다. 디버깅할 때는 start/end, page size, ASID, hart mask, SBI return과 remote completion을 확인한다.
객체 수명과 소유권을 먼저 고정한다
invalidated physical page와 page-table page는 remote TLB entry가 사라지기 전까지 재사용하면 안 된다. mmu_gather가 이 deferred free를 관리한다.
주소나 register 값이 맞는지만 확인하면 stale state를 놓친다. producer, publication, consumer와 폐기 지점을 같은 표에 기록한다.
latency upper bound는 hardware instruction 하나가 아니다
Tshoot = Tpublish + max(Tipi_delivery + Tremote_flush) + Tack. CPU 수보다 실제 mm cpumask 크기와 interrupt-off 구간이 tail latency를 지배한다.
평균값 외에 interrupt-off 구간, remote CPU 응답, firmware 호출과 retry 횟수를 분리해야 최악 지연의 원인을 찾을 수 있다.
07 · FAILURE
실패를 어떤 증거로 나눌 것인가
| 분류 | 관찰되는 결과 | 첫 확인값 |
|---|---|---|
| arm64 | TLBI만 실행하고 completion barrier를 빼면 page free 뒤 remote walker가 old PA를 사용한다. | TLBI operand의 ASID/VA, range scale, DSB domain과 mm cpumask를 확인한다. |
| x86-64 | CPU가 mm를 막 load하는 순간 대상 mask에서 빠지면 stale PTE를 무기한 사용한다. | start/end, stride_shift, freed_tables, new_tlb_gen, INVPCID capability와 target cpumask를 본다. |
| RISC-V | SBI/firmware가 잘못된 hart mask나 ASID를 받으면 일부 hart에만 stale translation이 남는다. | start/end, page size, ASID, hart mask, SBI return과 remote completion을 확인한다. |
08 · LAB
재현과 계측 절차
tlb:tlb_flush와 IPI trace를 함께 수집해 initiator와 가장 늦은 remote CPU를 찾는다.- CPU 하나에서 interrupt를 길게 막아 shootdown upper bound가 어떻게 늘어나는지 측정한다.
- 동일한 workload에서 세 architecture의 tracepoint 이름, CPU 번호, PC, stack pointer와 address-space identifier를 같은 열로 기록한다.
- 소스만 읽고 끝내지 않고 최종
vmlinux의objdump -dr,readelf -SW결과로 선택된 alternative와 section 배치를 확인한다.
09 · REFERENCES
원문 좌표
- arm64arch/arm64/include/asm/tlbflush.h:364-420
- x86-64arch/x86/mm/tlb.c:1440-1520
- RISC-Varch/riscv/mm/tlbflush.c:149-213
Linux kernel source: GPL-2.0-only. 이 글의 코드 발췌는 Linux v6.18.37 원문을 기준으로 하며, 분석 문장은 해당 코드의 실행 조건과 상태 경계를 설명합니다.