01 · QUESTION
무엇을 확인할 것인가
dma_map_single()이 반환한 주소는 physical address와 언제 같고, CPU와 device가 같은 buffer를 동시에 cacheable하게 접근하지 못하도록 어떤 상태 전환이 필요한가?
DMA API는 CPU virtual buffer, physical page, device-visible DMA address와 IOMMU IOVA를 분리한다. coherent allocation은 동시 관찰 계약을 제공하고 streaming mapping은 map/sync/unmap으로 ownership을 전환한다.
driver의 buffer lifetime, architecture cache maintenance, IOMMU page-table update와 device descriptor publication을 각각 구분한다. mapping 성공 뒤에도 descriptor write barrier와 device doorbell ordering이 필요하다.
02 · CONTRACT
공통 계약과 architecture 구현
| architecture | 핵심 mechanism | 실패 형태 | 확인할 상태 |
|---|---|---|---|
| arm64 | dma-direct/SMMU와 explicit cache maintenance | direction을 반대로 지정하거나 partial cache line ownership을 공유하면 CPU dirty data 손실 또는 stale device data 관찰이 생긴다. | device dma_coherent 속성, DMA direction, cache line 정렬, StreamID, IOVA/PA, SMMU STE/CD와 IOTLB sync를 본다. |
| x86-64 | 기본 cache-coherent DMA와 VT-d/AMD IOMMU | 64-bit device에 잘못된 32-bit mask, IOMMU group 격리 실패 또는 unmap 없는 장기 pin은 DMA fault와 memory pressure를 만든다. | dma_mask/coherent_dma_mask, IOVA, BDF requester ID, IOMMU domain, SWIOTLB slot과 fault record를 확인한다. |
| RISC-V | dma-noncoherent cache block operation과 RISC-V IOMMU | cache block size 오인, unaligned buffer의 이웃 data 손상 또는 cache maintenance extension이 없는 platform의 잘못된 direct mapping이 data corruption을 만든다. | riscv_cbom_block_size, dma-noncoherent flag, direction, IOVA/PA, device ID와 IOMMU fault queue를 본다. |
03 · DIAGRAMS
세 그림으로 먼저 읽기
arm64
- mechanism
- dma-direct/SMMU와 explicit cache maintenance
- state
- platform가 dma-coherent면 normal cacheable mapping을 공유하지만 non-coherent device에서는 map-to-device 전 clean, map-from-device 전후 invalidate가 필요하다. SMMUv3는 StreamID로 context를 고르고 IOVA를 PA로 변환한다.
- checkpoint
- device dma_coherent 속성, DMA direction, cache line 정렬, StreamID, IOVA/PA, SMMU STE/CD와 IOTLB sync를 본다.
x86-64
- mechanism
- 기본 cache-coherent DMA와 VT-d/AMD IOMMU
- state
- 일반 PC의 WB memory는 CPU cache와 PCIe DMA가 hardware coherency를 유지해 streaming map에 명시적 cache clean이 보통 없다. dma mask를 넘는 주소는 SWIOTLB bounce를 쓰고 IOMMU가 requester ID별 IOVA translation과 interrupt remapping을 제공한다.
- checkpoint
- dma_mask/coherent_dma_mask, IOVA, BDF requester ID, IOMMU domain, SWIOTLB slot과 fault record를 확인한다.
RISC-V
- mechanism
- dma-noncoherent cache block operation과 RISC-V IOMMU
- state
- platform coherency가 보장되지 않으면 Zicbom cache-block clean/flush/inval 또는 platform callback으로 streaming mapping을 동기화한다. IOMMU가 있는 시스템은 device ID/process ID context로 IOVA를 변환하지만 구현과 firmware description을 함께 확인해야 한다.
- checkpoint
- riscv_cbom_block_size, dma-noncoherent flag, direction, IOVA/PA, device ID와 IOMMU fault queue를 본다.
04 · SOURCE
Linux 6.18.37 원본 코드와 줄별 설명
소스 위치를 고정된 숫자로 복사하지 않고 Linux v6.18.37 tree에서 함수 선언을 다시 찾아 발췌했습니다. 아래 코드와 각 줄의 설명은 1:1로 대응합니다.
arm64 · Linux 6.18.37
dma-direct/SMMU와 explicit cache maintenance
platform가 dma-coherent면 normal cacheable mapping을 공유하지만 non-coherent device에서는 map-to-device 전 clean, map-from-device 전후 invalidate가 필요하다. SMMUv3는 StreamID로 context를 고르고 IOVA를 PA로 변환한다.
원본 코드: arch/arm64/mm/dma-mapping.c:9-55
9#include <linux/dma-map-ops.h>
10#include <xen/xen.h>
11
12#include <asm/cacheflush.h>
13#include <asm/xen/xen-ops.h>
14
15void arch_sync_dma_for_device(phys_addr_t paddr, size_t size,
16 enum dma_data_direction dir)
17{
18 unsigned long start = (unsigned long)phys_to_virt(paddr);
19
20 dcache_clean_poc(start, start + size);
21}
22
23void arch_sync_dma_for_cpu(phys_addr_t paddr, size_t size,
24 enum dma_data_direction dir)
25{
26 unsigned long start = (unsigned long)phys_to_virt(paddr);
27
28 if (dir == DMA_TO_DEVICE)
29 return;
30
31 dcache_inval_poc(start, start + size);
32}
33
34void arch_dma_prep_coherent(struct page *page, size_t size)
35{
36 unsigned long start = (unsigned long)page_address(page);
37
38 dcache_clean_poc(start, start + size);
39}
40
41void arch_setup_dma_ops(struct device *dev, bool coherent)
42{
43 int cls = cache_line_size_of_cpu();
44
45 WARN_TAINT(!coherent && cls > ARCH_DMA_MINALIGN,
46 TAINT_CPU_OUT_OF_SPEC,
47 "%s %s: ARCH_DMA_MINALIGN smaller than CTR_EL0.CWG (%d < %d)",
48 dev_driver_string(dev), dev_name(dev),
49 ARCH_DMA_MINALIGN, cls);
50
51 dev->dma_coherent = coherent;
52
53 xen_setup_dma_ops(dev);
54}
55 라인 바이 라인 주석
빈 줄과 전처리 경계도 생략하지 않았습니다. 원본의 47개 줄에 각각 설명을 붙였습니다.
#include <linux/dma-map-ops.h>compile-time 이름, constant 또는 architecture helper를 가져오는 줄이다. macro라면 최종 instruction과 memory-order 의미까지 펼쳐서 확인한다.
#include <xen/xen.h>compile-time 이름, constant 또는 architecture helper를 가져오는 줄이다. macro라면 최종 instruction과 memory-order 의미까지 펼쳐서 확인한다.
(blank)빈 줄은 arm64 DMA, cache coherency와 IOMMU translation 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
#include <asm/cacheflush.h>compile-time 이름, constant 또는 architecture helper를 가져오는 줄이다. macro라면 최종 instruction과 memory-order 의미까지 펼쳐서 확인한다.
#include <asm/xen/xen-ops.h>compile-time 이름, constant 또는 architecture helper를 가져오는 줄이다. macro라면 최종 instruction과 memory-order 의미까지 펼쳐서 확인한다.
(blank)빈 줄은 arm64 DMA, cache coherency와 IOMMU translation 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
void arch_sync_dma_for_device(phys_addr_t paddr, size_t size,이 줄이 arm64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. DMA, cache coherency와 IOMMU translation의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.
enum dma_data_direction dir)이 줄이 arm64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. DMA, cache coherency와 IOMMU translation의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.
{C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
unsigned long start = (unsigned long)phys_to_virt(paddr);helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
(blank)빈 줄은 arm64 DMA, cache coherency와 IOMMU translation 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
dcache_clean_poc(start, start + size);helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
}C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
(blank)빈 줄은 arm64 DMA, cache coherency와 IOMMU translation 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
void arch_sync_dma_for_cpu(phys_addr_t paddr, size_t size,이 줄이 arm64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. DMA, cache coherency와 IOMMU translation의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.
enum dma_data_direction dir)이 줄이 arm64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. DMA, cache coherency와 IOMMU translation의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.
{C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
unsigned long start = (unsigned long)phys_to_virt(paddr);helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
(blank)빈 줄은 arm64 DMA, cache coherency와 IOMMU translation 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
if (dir == DMA_TO_DEVICE)이 조건이 arm64 fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.
return;이 함수가 DMA, cache coherency와 IOMMU translation 단계의 결과 또는 오류를 상위 계층에 전달한다. 반환 전에 lock, interrupt state, reference와 hardware active state가 정리됐는지 확인한다.
(blank)빈 줄은 arm64 DMA, cache coherency와 IOMMU translation 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
dcache_inval_poc(start, start + size);helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
}C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
(blank)빈 줄은 arm64 DMA, cache coherency와 IOMMU translation 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
void arch_dma_prep_coherent(struct page *page, size_t size)이 함수의 진입 계약이 시작된다. arm64에서 caller context, argument ownership과 반환 시 보장할 architecture state를 먼저 적는다.
{C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
unsigned long start = (unsigned long)page_address(page);helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
(blank)빈 줄은 arm64 DMA, cache coherency와 IOMMU translation 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
dcache_clean_poc(start, start + size);helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
}C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
(blank)빈 줄은 arm64 DMA, cache coherency와 IOMMU translation 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
void arch_setup_dma_ops(struct device *dev, bool coherent)이 함수의 진입 계약이 시작된다. arm64에서 caller context, argument ownership과 반환 시 보장할 architecture state를 먼저 적는다.
{C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
int cls = cache_line_size_of_cpu();helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
(blank)빈 줄은 arm64 DMA, cache coherency와 IOMMU translation 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
WARN_TAINT(!coherent && cls > ARCH_DMA_MINALIGN,불가능해야 하는 상태 또는 복구 가능한 오류를 외부에 드러내는 줄이다. 직전 register/object 값을 함께 남겨 재현 가능한 failure signature를 만든다.
TAINT_CPU_OUT_OF_SPEC,선언 또는 macro 확장 일부다. type의 폭과 signedness, per-CPU/task/object 중 어느 수명을 따르는 값인지 확인한다.
"%s %s: ARCH_DMA_MINALIGN smaller than CTR_EL0.CWG (%d < %d)",이 줄이 arm64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. DMA, cache coherency와 IOMMU translation의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.
dev_driver_string(dev), dev_name(dev),이 줄이 arm64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. DMA, cache coherency와 IOMMU translation의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.
ARCH_DMA_MINALIGN, cls);이 줄이 arm64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. DMA, cache coherency와 IOMMU translation의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.
(blank)빈 줄은 arm64 DMA, cache coherency와 IOMMU translation 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
dev->dma_coherent = coherent;계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.
(blank)빈 줄은 arm64 DMA, cache coherency와 IOMMU translation 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
xen_setup_dma_ops(dev);helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
}C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
(blank)빈 줄은 arm64 DMA, cache coherency와 IOMMU translation 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
x86-64 · Linux 6.18.37
기본 cache-coherent DMA와 VT-d/AMD IOMMU
일반 PC의 WB memory는 CPU cache와 PCIe DMA가 hardware coherency를 유지해 streaming map에 명시적 cache clean이 보통 없다. dma mask를 넘는 주소는 SWIOTLB bounce를 쓰고 IOMMU가 requester ID별 IOVA translation과 interrupt remapping을 제공한다.
원본 코드: arch/x86/kernel/pci-dma.c:92-140
92#else
93static inline void __init pci_xen_swiotlb_init(void)
94{
95}
96#endif /* CONFIG_SWIOTLB_XEN */
97
98void __init pci_iommu_alloc(void)
99{
100 if (xen_pv_domain()) {
101 pci_xen_swiotlb_init();
102 return;
103 }
104 pci_swiotlb_detect();
105 gart_iommu_hole_init();
106 amd_iommu_detect();
107 detect_intel_iommu();
108 swiotlb_init(x86_swiotlb_enable, x86_swiotlb_flags);
109}
110
111static __init int iommu_setup(char *p)
112{
113 iommu_merge = 1;
114
115 if (!p)
116 return -EINVAL;
117
118 while (*p) {
119 if (!strncmp(p, "off", 3))
120 no_iommu = 1;
121 /* gart_parse_options has more force support */
122 if (!strncmp(p, "force", 5))
123 force_iommu = 1;
124 if (!strncmp(p, "noforce", 7)) {
125 iommu_merge = 0;
126 force_iommu = 0;
127 }
128
129 if (!strncmp(p, "biomerge", 8)) {
130 iommu_merge = 1;
131 force_iommu = 1;
132 }
133 if (!strncmp(p, "panic", 5))
134 panic_on_overflow = 1;
135 if (!strncmp(p, "nopanic", 7))
136 panic_on_overflow = 0;
137 if (!strncmp(p, "merge", 5)) {
138 iommu_merge = 1;
139 force_iommu = 1;
140 }라인 바이 라인 주석
빈 줄과 전처리 경계도 생략하지 않았습니다. 원본의 49개 줄에 각각 설명을 붙였습니다.
#elseKconfig와 compiler feature에 따라 최종 object에 남는 경로가 달라지는 전처리 경계다. 대상 .config와 disassembly로 실제 선택을 확인한다.
static inline void __init pci_xen_swiotlb_init(void)이 함수의 진입 계약이 시작된다. x86-64에서 caller context, argument ownership과 반환 시 보장할 architecture state를 먼저 적는다.
{C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
}C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
#endif /* CONFIG_SWIOTLB_XEN */Kconfig와 compiler feature에 따라 최종 object에 남는 경로가 달라지는 전처리 경계다. 대상 .config와 disassembly로 실제 선택을 확인한다.
(blank)빈 줄은 x86-64 DMA, cache coherency와 IOMMU translation 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
void __init pci_iommu_alloc(void)이 함수의 진입 계약이 시작된다. x86-64에서 caller context, argument ownership과 반환 시 보장할 architecture state를 먼저 적는다.
{C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
if (xen_pv_domain()) {이 조건이 x86-64 fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.
pci_xen_swiotlb_init();helper 또는 architecture operation을 실행한다. x86-64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
return;이 함수가 DMA, cache coherency와 IOMMU translation 단계의 결과 또는 오류를 상위 계층에 전달한다. 반환 전에 lock, interrupt state, reference와 hardware active state가 정리됐는지 확인한다.
}C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
pci_swiotlb_detect();helper 또는 architecture operation을 실행한다. x86-64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
gart_iommu_hole_init();helper 또는 architecture operation을 실행한다. x86-64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
amd_iommu_detect();helper 또는 architecture operation을 실행한다. x86-64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
detect_intel_iommu();helper 또는 architecture operation을 실행한다. x86-64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
swiotlb_init(x86_swiotlb_enable, x86_swiotlb_flags);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 DMA, cache coherency와 IOMMU translation 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
static __init int iommu_setup(char *p)이 함수의 진입 계약이 시작된다. x86-64에서 caller context, argument ownership과 반환 시 보장할 architecture state를 먼저 적는다.
{C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
iommu_merge = 1;계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.
(blank)빈 줄은 x86-64 DMA, cache coherency와 IOMMU translation 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
if (!p)이 조건이 x86-64 fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.
return -EINVAL;이 함수가 DMA, cache coherency와 IOMMU translation 단계의 결과 또는 오류를 상위 계층에 전달한다. 반환 전에 lock, interrupt state, reference와 hardware active state가 정리됐는지 확인한다.
(blank)빈 줄은 x86-64 DMA, cache coherency와 IOMMU translation 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
while (*p) {range, CPU mask, relocation 또는 descriptor를 반복 처리한다. 반복 상한과 중간 실패 때 이미 처리한 항목을 되돌리는 경로를 함께 본다.
if (!strncmp(p, "off", 3))이 조건이 x86-64 fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.
no_iommu = 1;계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.
/* gart_parse_options has more force support */Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
if (!strncmp(p, "force", 5))이 조건이 x86-64 fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.
force_iommu = 1;계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.
if (!strncmp(p, "noforce", 7)) {이 조건이 x86-64 fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.
iommu_merge = 0;계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.
force_iommu = 0;계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.
}C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
(blank)빈 줄은 x86-64 DMA, cache coherency와 IOMMU translation 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
if (!strncmp(p, "biomerge", 8)) {이 조건이 x86-64 fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.
iommu_merge = 1;계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.
force_iommu = 1;계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.
}C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
if (!strncmp(p, "panic", 5))이 조건이 x86-64 fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.
panic_on_overflow = 1;불가능해야 하는 상태 또는 복구 가능한 오류를 외부에 드러내는 줄이다. 직전 register/object 값을 함께 남겨 재현 가능한 failure signature를 만든다.
if (!strncmp(p, "nopanic", 7))이 조건이 x86-64 fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.
panic_on_overflow = 0;불가능해야 하는 상태 또는 복구 가능한 오류를 외부에 드러내는 줄이다. 직전 register/object 값을 함께 남겨 재현 가능한 failure signature를 만든다.
if (!strncmp(p, "merge", 5)) {이 조건이 x86-64 fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.
iommu_merge = 1;계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.
force_iommu = 1;계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.
}C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
RISC-V · Linux 6.18.37
dma-noncoherent cache block operation과 RISC-V IOMMU
platform coherency가 보장되지 않으면 Zicbom cache-block clean/flush/inval 또는 platform callback으로 streaming mapping을 동기화한다. IOMMU가 있는 시스템은 device ID/process ID context로 IOVA를 변환하지만 구현과 firmware description을 함께 확인해야 한다.
원본 코드: arch/riscv/mm/dma-noncoherent.c:63-117
63
64static inline bool arch_sync_dma_cpu_needs_post_dma_flush(void)
65{
66 return true;
67}
68
69void arch_sync_dma_for_device(phys_addr_t paddr, size_t size,
70 enum dma_data_direction dir)
71{
72 switch (dir) {
73 case DMA_TO_DEVICE:
74 arch_dma_cache_wback(paddr, size);
75 break;
76
77 case DMA_FROM_DEVICE:
78 if (!arch_sync_dma_clean_before_fromdevice()) {
79 arch_dma_cache_inv(paddr, size);
80 break;
81 }
82 fallthrough;
83
84 case DMA_BIDIRECTIONAL:
85 /* Skip the invalidate here if it's done later */
86 if (IS_ENABLED(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU) &&
87 arch_sync_dma_cpu_needs_post_dma_flush())
88 arch_dma_cache_wback(paddr, size);
89 else
90 arch_dma_cache_wback_inv(paddr, size);
91 break;
92
93 default:
94 break;
95 }
96}
97
98void arch_sync_dma_for_cpu(phys_addr_t paddr, size_t size,
99 enum dma_data_direction dir)
100{
101 switch (dir) {
102 case DMA_TO_DEVICE:
103 break;
104
105 case DMA_FROM_DEVICE:
106 case DMA_BIDIRECTIONAL:
107 /* FROM_DEVICE invalidate needed if speculative CPU prefetch only */
108 if (arch_sync_dma_cpu_needs_post_dma_flush())
109 arch_dma_cache_inv(paddr, size);
110 break;
111
112 default:
113 break;
114 }
115}
116
117void arch_dma_prep_coherent(struct page *page, size_t size)라인 바이 라인 주석
빈 줄과 전처리 경계도 생략하지 않았습니다. 원본의 55개 줄에 각각 설명을 붙였습니다.
(blank)빈 줄은 RISC-V DMA, cache coherency와 IOMMU translation 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
static inline bool arch_sync_dma_cpu_needs_post_dma_flush(void)이 함수의 진입 계약이 시작된다. RISC-V에서 caller context, argument ownership과 반환 시 보장할 architecture state를 먼저 적는다.
{C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
return true;이 함수가 DMA, cache coherency와 IOMMU translation 단계의 결과 또는 오류를 상위 계층에 전달한다. 반환 전에 lock, interrupt state, reference와 hardware active state가 정리됐는지 확인한다.
}C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
(blank)빈 줄은 RISC-V DMA, cache coherency와 IOMMU translation 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
void arch_sync_dma_for_device(phys_addr_t paddr, size_t size,이 줄이 RISC-V의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. DMA, cache coherency와 IOMMU translation의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.
enum dma_data_direction dir)이 줄이 RISC-V의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. DMA, cache coherency와 IOMMU translation의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.
{C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
switch (dir) {hardware reason, flag 또는 operation 종류를 개별 처리 경로로 분기한다. 각 case가 공통 cleanup과 completion 지점에 도달하는지 확인한다.
case DMA_TO_DEVICE:hardware reason, flag 또는 operation 종류를 개별 처리 경로로 분기한다. 각 case가 공통 cleanup과 completion 지점에 도달하는지 확인한다.
arch_dma_cache_wback(paddr, size);helper 또는 architecture operation을 실행한다. RISC-V에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
break;정상 직선 경로를 벗어나 cleanup, retry 또는 다음 항목으로 이동한다. 이동 대상에서 해제하는 resource와 현재까지 획득한 ownership을 맞춘다.
(blank)빈 줄은 RISC-V DMA, cache coherency와 IOMMU translation 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
case DMA_FROM_DEVICE:hardware reason, flag 또는 operation 종류를 개별 처리 경로로 분기한다. 각 case가 공통 cleanup과 completion 지점에 도달하는지 확인한다.
if (!arch_sync_dma_clean_before_fromdevice()) {이 조건이 RISC-V fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.
arch_dma_cache_inv(paddr, size);helper 또는 architecture operation을 실행한다. RISC-V에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
break;정상 직선 경로를 벗어나 cleanup, retry 또는 다음 항목으로 이동한다. 이동 대상에서 해제하는 resource와 현재까지 획득한 ownership을 맞춘다.
}C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
fallthrough;선언 또는 macro 확장 일부다. type의 폭과 signedness, per-CPU/task/object 중 어느 수명을 따르는 값인지 확인한다.
(blank)빈 줄은 RISC-V DMA, cache coherency와 IOMMU translation 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
case DMA_BIDIRECTIONAL:hardware reason, flag 또는 operation 종류를 개별 처리 경로로 분기한다. 각 case가 공통 cleanup과 completion 지점에 도달하는지 확인한다.
/* Skip the invalidate here if it's done later */Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
if (IS_ENABLED(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU) &&이 조건이 RISC-V fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.
arch_sync_dma_cpu_needs_post_dma_flush())이 함수의 진입 계약이 시작된다. RISC-V에서 caller context, argument ownership과 반환 시 보장할 architecture state를 먼저 적는다.
arch_dma_cache_wback(paddr, size);helper 또는 architecture operation을 실행한다. RISC-V에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
else앞 조건이 성립하지 않았을 때의 대체 경로다. fast path와 같은 ownership, ordering과 반환 계약을 제공해야 한다.
arch_dma_cache_wback_inv(paddr, size);helper 또는 architecture operation을 실행한다. RISC-V에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
break;정상 직선 경로를 벗어나 cleanup, retry 또는 다음 항목으로 이동한다. 이동 대상에서 해제하는 resource와 현재까지 획득한 ownership을 맞춘다.
(blank)빈 줄은 RISC-V DMA, cache coherency와 IOMMU translation 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
default:분기 label이다. 이 위치로 들어오는 모든 선행 경로가 같은 register, stack, lock과 interrupt 상태를 만족하는지 비교한다.
break;정상 직선 경로를 벗어나 cleanup, retry 또는 다음 항목으로 이동한다. 이동 대상에서 해제하는 resource와 현재까지 획득한 ownership을 맞춘다.
}C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
}C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
(blank)빈 줄은 RISC-V DMA, cache coherency와 IOMMU translation 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
void arch_sync_dma_for_cpu(phys_addr_t paddr, size_t size,이 줄이 RISC-V의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. DMA, cache coherency와 IOMMU translation의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.
enum dma_data_direction dir)이 줄이 RISC-V의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. DMA, cache coherency와 IOMMU translation의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.
{C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
switch (dir) {hardware reason, flag 또는 operation 종류를 개별 처리 경로로 분기한다. 각 case가 공통 cleanup과 completion 지점에 도달하는지 확인한다.
case DMA_TO_DEVICE:hardware reason, flag 또는 operation 종류를 개별 처리 경로로 분기한다. 각 case가 공통 cleanup과 completion 지점에 도달하는지 확인한다.
break;정상 직선 경로를 벗어나 cleanup, retry 또는 다음 항목으로 이동한다. 이동 대상에서 해제하는 resource와 현재까지 획득한 ownership을 맞춘다.
(blank)빈 줄은 RISC-V DMA, cache coherency와 IOMMU translation 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
case DMA_FROM_DEVICE:hardware reason, flag 또는 operation 종류를 개별 처리 경로로 분기한다. 각 case가 공통 cleanup과 completion 지점에 도달하는지 확인한다.
case DMA_BIDIRECTIONAL:hardware reason, flag 또는 operation 종류를 개별 처리 경로로 분기한다. 각 case가 공통 cleanup과 completion 지점에 도달하는지 확인한다.
/* FROM_DEVICE invalidate needed if speculative CPU prefetch only */Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
if (arch_sync_dma_cpu_needs_post_dma_flush())이 조건이 RISC-V fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.
arch_dma_cache_inv(paddr, size);helper 또는 architecture operation을 실행한다. RISC-V에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
break;정상 직선 경로를 벗어나 cleanup, retry 또는 다음 항목으로 이동한다. 이동 대상에서 해제하는 resource와 현재까지 획득한 ownership을 맞춘다.
(blank)빈 줄은 RISC-V DMA, cache coherency와 IOMMU translation 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
default:분기 label이다. 이 위치로 들어오는 모든 선행 경로가 같은 register, stack, lock과 interrupt 상태를 만족하는지 비교한다.
break;정상 직선 경로를 벗어나 cleanup, retry 또는 다음 항목으로 이동한다. 이동 대상에서 해제하는 resource와 현재까지 획득한 ownership을 맞춘다.
}C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
}C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
(blank)빈 줄은 RISC-V DMA, cache coherency와 IOMMU translation 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
void arch_dma_prep_coherent(struct page *page, size_t size)이 함수의 진입 계약이 시작된다. RISC-V에서 caller context, argument ownership과 반환 시 보장할 architecture state를 먼저 적는다.
05 · WORKED EXAMPLE
숫자로 검산하기
64KiB RX buffer의 ownership 전환
non-coherent CPU cache line 64B, device가 64KiB packet area를 채우고 IOMMU page는 4KiB라고 가정한다.
- map16개 IOMMU PTE가 IOVA range를 backing page에 연결하며 direction은 DMA_FROM_DEVICE다.
- device ownsdescriptor를 publish한 뒤 CPU는 buffer payload를 읽거나 쓰지 않는다.
- completedevice completion 뒤 CPU가 64KiB/64B = 1024 cache line을 invalidate하고 packet을 읽는다.
- recycleCPU가 metadata를 수정했다면 필요한 clean을 수행하고 descriptor barrier 뒤 다시 device에 넘긴다.
결론DMA address 계산, IOMMU translation과 cache ownership은 서로 다른 문제다. 셋을 한 단계로 표현하면 corruption 원인을 찾기 어렵다.
06 · DEEP DIVE
경계별 상세 분석
공통 kernel core와 architecture hook의 경계
DMA API는 CPU virtual buffer, physical page, device-visible DMA address와 IOMMU IOVA를 분리한다. coherent allocation은 동시 관찰 계약을 제공하고 streaming mapping은 map/sync/unmap으로 ownership을 전환한다.
driver의 buffer lifetime, architecture cache maintenance, IOMMU page-table update와 device descriptor publication을 각각 구분한다. mapping 성공 뒤에도 descriptor write barrier와 device doorbell ordering이 필요하다.
arm64: dma-direct/SMMU와 explicit cache maintenance
platform가 dma-coherent면 normal cacheable mapping을 공유하지만 non-coherent device에서는 map-to-device 전 clean, map-from-device 전후 invalidate가 필요하다. SMMUv3는 StreamID로 context를 고르고 IOVA를 PA로 변환한다.
CPU cache operation completion, descriptor store barrier와 SMMU command queue sync가 device fetch보다 앞서야 한다. 디버깅할 때는 device dma_coherent 속성, DMA direction, cache line 정렬, StreamID, IOVA/PA, SMMU STE/CD와 IOTLB sync를 본다.
x86-64: 기본 cache-coherent DMA와 VT-d/AMD IOMMU
일반 PC의 WB memory는 CPU cache와 PCIe DMA가 hardware coherency를 유지해 streaming map에 명시적 cache clean이 보통 없다. dma mask를 넘는 주소는 SWIOTLB bounce를 쓰고 IOMMU가 requester ID별 IOVA translation과 interrupt remapping을 제공한다.
coherent memory라도 descriptor/data store가 device MMIO doorbell보다 먼저 보이도록 dma_wmb가 필요하며 posted MMIO completion은 별도 readback을 요구할 수 있다. 디버깅할 때는 dma_mask/coherent_dma_mask, IOVA, BDF requester ID, IOMMU domain, SWIOTLB slot과 fault record를 확인한다.
RISC-V: dma-noncoherent cache block operation과 RISC-V IOMMU
platform coherency가 보장되지 않으면 Zicbom cache-block clean/flush/inval 또는 platform callback으로 streaming mapping을 동기화한다. IOMMU가 있는 시스템은 device ID/process ID context로 IOVA를 변환하지만 구현과 firmware description을 함께 확인해야 한다.
CBO operation 전후 fence와 device descriptor barrier가 CPU cache ownership과 DMA 시작 순서를 만든다. 디버깅할 때는 riscv_cbom_block_size, dma-noncoherent flag, direction, IOVA/PA, device ID와 IOMMU fault queue를 본다.
객체 수명과 소유권을 먼저 고정한다
buffer page와 IOVA는 device DMA completion과 IOMMU invalidation이 끝날 때까지 재사용할 수 없다. unmap 전에 device를 멈추거나 completion을 확인하지 않으면 새 owner memory를 이전 device가 덮는다.
주소나 register 값이 맞는지만 확인하면 stale state를 놓친다. producer, publication, consumer와 폐기 지점을 같은 표에 기록한다.
latency upper bound는 hardware instruction 하나가 아니다
direct DMA는 cache clean/invalidate byte 수가, IOMMU DMA는 IOVA allocation과 page-table map, IOTLB invalidation이 지배한다. bounce buffer는 추가 copy와 제한된 pool 대기를 만든다.
평균값 외에 interrupt-off 구간, remote CPU 응답, firmware 호출과 retry 횟수를 분리해야 최악 지연의 원인을 찾을 수 있다.
07 · FAILURE
실패를 어떤 증거로 나눌 것인가
| 분류 | 관찰되는 결과 | 첫 확인값 |
|---|---|---|
| arm64 | direction을 반대로 지정하거나 partial cache line ownership을 공유하면 CPU dirty data 손실 또는 stale device data 관찰이 생긴다. | device dma_coherent 속성, DMA direction, cache line 정렬, StreamID, IOVA/PA, SMMU STE/CD와 IOTLB sync를 본다. |
| x86-64 | 64-bit device에 잘못된 32-bit mask, IOMMU group 격리 실패 또는 unmap 없는 장기 pin은 DMA fault와 memory pressure를 만든다. | dma_mask/coherent_dma_mask, IOVA, BDF requester ID, IOMMU domain, SWIOTLB slot과 fault record를 확인한다. |
| RISC-V | cache block size 오인, unaligned buffer의 이웃 data 손상 또는 cache maintenance extension이 없는 platform의 잘못된 direct mapping이 data corruption을 만든다. | riscv_cbom_block_size, dma-noncoherent flag, direction, IOVA/PA, device ID와 IOMMU fault queue를 본다. |
08 · LAB
재현과 계측 절차
- 같은 driver를 IOMMU passthrough와 translated mode에서 실행해 map/unmap latency와 IOTLB miss를 비교한다.
- non-coherent QEMU/platform에서 sync 호출 하나를 의도적으로 빼 stale line이 어느 방향에서 나타나는지 확인한다.
- 동일한 workload에서 세 architecture의 tracepoint 이름, CPU 번호, PC, stack pointer와 address-space identifier를 같은 열로 기록한다.
- 소스만 읽고 끝내지 않고 최종
vmlinux의objdump -dr,readelf -SW결과로 선택된 alternative와 section 배치를 확인한다.
09 · REFERENCES
원문 좌표
- arm64arch/arm64/mm/dma-mapping.c:9-55
- x86-64arch/x86/kernel/pci-dma.c:92-140
- RISC-Varch/riscv/mm/dma-noncoherent.c:63-117
Linux kernel source: GPL-2.0-only. 이 글의 코드 발췌는 Linux v6.18.37 원문을 기준으로 하며, 분석 문장은 해당 코드의 실행 조건과 상태 경계를 설명합니다.