refactor: 문서 개선 중

This commit is contained in:
donghyeon-ka
2026-09-21 14:30:55 +09:00
parent c93cdea150
commit 805a18f486
1497 changed files with 525837 additions and 59152 deletions
+4 -2
View File
@@ -37,6 +37,8 @@
| `final/` | SSOT — 이 프로젝트에 대해 아는 것 전부 |
| `tech-log-studio/` | SSOT 에서 뽑아 쓴 글. 정본은 `tech-log-tree.json` |
## 반입하는 동안에만 있는 것
## source/ 정책 — durable import snapshot
`source/` — 밖에서 가져온 원본. `final/` 내용을 담으면 지운다.
`source/` 는 이 프로젝트에서는 임시 작업 재료가 아니다. 제5~6부의 실험대 문서는 `9465582b5d1630eb4ae7c4e078021486919bf6b6` 을 HEAD 로 둔 **미커밋 working tree**에서 반입됐고, 반입한 바이트와 정확히 같은 commit은 찾지 못했다. 따라서 `source/` 시점의 바이트를 고정하는 durable import snapshot 역할을 한다.
`final/` 이 내용을 설명한다고 해서 `source/` 를 지우지 않는다. exact snapshot을 다른 durable artifact로 옮기고 hash까지 고정한 경우에만 제거할 수 있다. `source/.source-revision`의 commit은 **import 당시 HEAD**이지 반입 바이트의 exact revision이라는 뜻이 아니다.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -14,11 +14,11 @@
"long_description": "위에서 아래로 읽는다. 맨 위 Guest Userspace 구역에 PostgreSQL / Keycloak 이 있고 write() 가 게스트 커널로 내려간다. Guest Kernel 구역 안에서 VFS 가 먼저 받고, ext4 / XFS 가 파일을 block 공간에 배치하고, Guest Page Cache 가 그 내용을 dirty page 로 받아 두었다가 writeback 으로 Guest Block Layer 에 내려보낸다. Guest Block Layer 는 그것을 WRITE 나 FLUSH 요청으로 바꿔 /dev/vda 에 걸고, virtio-blk Frontend 가 그 요청을 virtqueue 에 게시한다. virtqueue 아래의 VM Boundary 부터는 QEMU 와 호스트 계층이라 이 그림에서 잘랐다. 완료가 같은 virtqueue 로 되돌아오는 역방향은 SSOT 의 다른 절이 대는 사실이라 이 그림에 넣지 않았고 본문 문단이 맡는다.",
"source_context": {
"document": "docs/virtualization/final/document.md",
"document_sha256": "181e2b3cc8a45bae81e7e8193d026937c4d586ae4495d3b2c323eb7e6abcfadd",
"document_sha256": "8c4ecc64c8cea9a4450ed7131fdd9cb2048dc092b66cd969f6346ed77887c210",
"anchor": {
"kind": "heading",
"value": "128. 전체 구조",
"line": 5804
"line": 5817
}
},
"composition": {
@@ -38,8 +38,8 @@
"description": "게스트 사용자 공간. 저장장치를 직접 다루지 않는다.",
"evidence": [
{
"start_line": 5807,
"end_line": 5809
"start_line": 5820,
"end_line": 5822
}
],
"assumption": false
@@ -51,8 +51,8 @@
"description": "게스트 커널 공간. VM Boundary 위쪽 계층이 전부 여기 있다.",
"evidence": [
{
"start_line": 5815,
"end_line": 5833
"start_line": 5828,
"end_line": 5846
}
],
"assumption": false
@@ -69,16 +69,16 @@
"description": "파일 연산을 요청하는 게스트 프로세스.",
"evidence": [
{
"start_line": 5807,
"end_line": 5809
"start_line": 5820,
"end_line": 5822
},
{
"start_line": 5885,
"end_line": 5885
"start_line": 5898,
"end_line": 5898
},
{
"start_line": 5811,
"end_line": 5813
"start_line": 5824,
"end_line": 5826
}
],
"assumption": false,
@@ -97,12 +97,12 @@
"description": "열린 파일이 어느 filesystem 구현에 속하는지 보고 연산을 넘기는 공통 계층.",
"evidence": [
{
"start_line": 5815,
"end_line": 5817
"start_line": 5828,
"end_line": 5830
},
{
"start_line": 5905,
"end_line": 5905
"start_line": 5918,
"end_line": 5918
}
],
"assumption": false
@@ -117,8 +117,8 @@
"description": "파일과 디렉터리를 block 공간에 배치하는 게스트 filesystem.",
"evidence": [
{
"start_line": 5819,
"end_line": 5819
"start_line": 5832,
"end_line": 5832
}
],
"assumption": false
@@ -133,8 +133,8 @@
"description": "게스트 RAM 에 있는 cache. write() 가 성공해도 데이터는 아직 여기까지만 확정된다.",
"evidence": [
{
"start_line": 5821,
"end_line": 5821
"start_line": 5834,
"end_line": 5834
}
],
"assumption": false
@@ -149,8 +149,8 @@
"description": "파일 세계의 요청을 block device 세계의 요청으로 바꾸는 계층.",
"evidence": [
{
"start_line": 5825,
"end_line": 5825
"start_line": 5838,
"end_line": 5838
}
],
"assumption": false
@@ -165,12 +165,12 @@
"description": "게스트 Linux 가 하나의 block device 로 인식하는 이름.",
"evidence": [
{
"start_line": 5829,
"end_line": 5829
"start_line": 5842,
"end_line": 5842
},
{
"start_line": 5879,
"end_line": 5879
"start_line": 5892,
"end_line": 5892
}
],
"assumption": false
@@ -185,12 +185,12 @@
"description": "그 가상 block device 를 제어하는 게스트 커널 driver.",
"evidence": [
{
"start_line": 5831,
"end_line": 5831
"start_line": 5844,
"end_line": 5844
},
{
"start_line": 5789,
"end_line": 5789
"start_line": 5802,
"end_line": 5802
}
],
"assumption": false
@@ -206,8 +206,8 @@
"description": "요청이 실리는 자리. 바로 아래가 VM Boundary 다.",
"evidence": [
{
"start_line": 5833,
"end_line": 5835
"start_line": 5846,
"end_line": 5848
}
],
"assumption": false
@@ -224,12 +224,12 @@
"order": 1,
"evidence": [
{
"start_line": 5811,
"end_line": 5813
"start_line": 5824,
"end_line": 5826
},
{
"start_line": 5890,
"end_line": 5890
"start_line": 5903,
"end_line": 5903
}
],
"assumption": false
@@ -244,8 +244,8 @@
"order": 2,
"evidence": [
{
"start_line": 5817,
"end_line": 5819
"start_line": 5830,
"end_line": 5832
}
],
"assumption": false
@@ -260,8 +260,8 @@
"order": 3,
"evidence": [
{
"start_line": 5819,
"end_line": 5821
"start_line": 5832,
"end_line": 5834
}
],
"assumption": false
@@ -276,8 +276,8 @@
"order": 4,
"evidence": [
{
"start_line": 5823,
"end_line": 5825
"start_line": 5836,
"end_line": 5838
}
],
"assumption": false
@@ -292,8 +292,8 @@
"order": 5,
"evidence": [
{
"start_line": 5827,
"end_line": 5829
"start_line": 5840,
"end_line": 5842
}
],
"assumption": false
@@ -308,8 +308,8 @@
"order": 6,
"evidence": [
{
"start_line": 5829,
"end_line": 5831
"start_line": 5842,
"end_line": 5844
}
],
"assumption": false
@@ -324,8 +324,8 @@
"order": 7,
"evidence": [
{
"start_line": 5831,
"end_line": 5833
"start_line": 5844,
"end_line": 5846
}
],
"assumption": false
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -4,7 +4,7 @@
"title": "Guest 의 메모리 접근 한 번은 이름이 두 번 바뀌며 네 계층을 지난다",
"question": "Guest 프로세스가 읽은 주소는 어느 계층을 차례로 지나 어떤 물리 메모리에 닿는가",
"type": "data-flow",
"direction": "TB",
"direction": "LR",
"audience": [
"가상 머신에 준 RAM 이 Host RAM 에서 무엇을 뜻하는지 가르려는 사람",
"KVM/QEMU 메모리 경로를 처음 읽는 사람"
@@ -14,11 +14,11 @@
"long_description": "위에서 아래로 읽는다. 맨 위 Guest 구역 안에 Keycloak · PostgreSQL 이 있고 그 접근은 GVA 로 Guest TLB 에 들어간다. TLB 에 변환이 없으면 TLB Miss 로 Guest Page Table 을 조회하고 거기서 GVA 가 GPA 가 된다. 그 GPA 는 VM Boundary 를 넘어 Guest 구역 밖의 EPT 로 가고 EPT 가 GPA 를 HPA 로 바꾼다. HPA 는 Host RAM 구역의 Host Physical Memory 에 닿고, 그 physical page 는 맨 아래에서 NUMA Node 0 또는 NUMA Node 1 가운데 한쪽에 놓인다. 상자 안 작은 글씨는 그 단계가 어느 계층인지와 무엇을 무엇으로 바꾸는지를 적은 것이다. 어느 단계에서 접근이 완료되지 못할 때 무엇이 일어나는지와 QEMU 가 이 경로를 어떻게 준비하는지는 본문의 문단과 표가 맡는다.",
"source_context": {
"document": "docs/virtualization/final/document.md",
"document_sha256": "181e2b3cc8a45bae81e7e8193d026937c4d586ae4495d3b2c323eb7e6abcfadd",
"document_sha256": "8c4ecc64c8cea9a4450ed7131fdd9cb2048dc092b66cd969f6346ed77887c210",
"anchor": {
"kind": "heading",
"value": "87. 최종 기준 그림",
"line": 4029
"line": 4042
}
},
"composition": {
@@ -38,8 +38,8 @@
"description": "VM Boundary 안쪽. Guest Userspace 와 Guest Kernel 이 여기 있고 GVA 가 GPA 가 되는 첫 번째 변환이 여기서 끝난다.",
"evidence": [
{
"start_line": 4034,
"end_line": 4053
"start_line": 4047,
"end_line": 4066
}
],
"assumption": false
@@ -51,8 +51,8 @@
"description": "변환이 끝난 주소가 닿는 실제 서버 메모리.",
"evidence": [
{
"start_line": 4069,
"end_line": 4079
"start_line": 4082,
"end_line": 4092
}
],
"assumption": false
@@ -69,8 +69,8 @@
"description": "Guest 안에서 메모리를 읽는 프로세스. 여기서 GVA 가 출발한다.",
"evidence": [
{
"start_line": 4034,
"end_line": 4038
"start_line": 4047,
"end_line": 4051
}
],
"assumption": false,
@@ -88,8 +88,8 @@
"description": "최근 변환 결과를 담아 두는 CPU 안의 캐시.",
"evidence": [
{
"start_line": 4043,
"end_line": 4045
"start_line": 4056,
"end_line": 4058
}
],
"assumption": false,
@@ -107,8 +107,8 @@
"description": "GVA 를 GPA 로 바꾸는 첫 번째 변환 표.",
"evidence": [
{
"start_line": 4048,
"end_line": 4053
"start_line": 4061,
"end_line": 4066
}
],
"assumption": false,
@@ -127,8 +127,8 @@
"description": "VM Boundary 바깥에서 GPA 를 HPA 로 바꾸는 두 번째 변환 표. KVM 과 CPU 가 맡는다.",
"evidence": [
{
"start_line": 4055,
"end_line": 4067
"start_line": 4068,
"end_line": 4080
}
],
"assumption": false,
@@ -147,8 +147,8 @@
"description": "HPA 가 가리키는 실제 서버 메모리.",
"evidence": [
{
"start_line": 4069,
"end_line": 4072
"start_line": 4082,
"end_line": 4085
}
],
"assumption": false
@@ -163,8 +163,8 @@
"description": "physical page 가 놓일 수 있는 한쪽 NUMA node.",
"evidence": [
{
"start_line": 4073,
"end_line": 4075
"start_line": 4086,
"end_line": 4088
}
],
"assumption": false
@@ -179,8 +179,8 @@
"description": "physical page 가 놓일 수 있는 다른 쪽 NUMA node.",
"evidence": [
{
"start_line": 4073,
"end_line": 4075
"start_line": 4086,
"end_line": 4088
}
],
"assumption": false
@@ -196,8 +196,8 @@
"style": "solid",
"evidence": [
{
"start_line": 4036,
"end_line": 4043
"start_line": 4049,
"end_line": 4056
}
],
"assumption": false
@@ -211,8 +211,8 @@
"style": "solid",
"evidence": [
{
"start_line": 4043,
"end_line": 4048
"start_line": 4056,
"end_line": 4061
}
],
"assumption": false
@@ -226,8 +226,8 @@
"style": "solid",
"evidence": [
{
"start_line": 4053,
"end_line": 4062
"start_line": 4066,
"end_line": 4075
}
],
"assumption": false
@@ -241,8 +241,8 @@
"style": "solid",
"evidence": [
{
"start_line": 4062,
"end_line": 4071
"start_line": 4075,
"end_line": 4084
}
],
"assumption": false
@@ -256,8 +256,8 @@
"style": "solid",
"evidence": [
{
"start_line": 4071,
"end_line": 4075
"start_line": 4084,
"end_line": 4088
}
],
"assumption": false
@@ -271,8 +271,8 @@
"style": "solid",
"evidence": [
{
"start_line": 4071,
"end_line": 4075
"start_line": 4084,
"end_line": 4088
}
],
"assumption": false
@@ -280,6 +280,6 @@
],
"legend": [],
"metadata": {
"rationale": "data-flow 로 고른 이유는 이 절이 답하는 물음이 「주소 하나가 어디를 지나 어디에 닿는가」이기 때문이다. LR 로 세우면 캔버스가 1889px 에 종횡비 5.37 이라 문서 폭에서 글자가 읽히지 않아 TB 로 세웠다. Guest Userspace · Guest Kernel · KVM / CPU · Host RAM 넷을 모두 group 으로 두면 group 이름이 그 경계를 넘는 엣지 라벨에 먹혀 「Guest Kerne」·「KVM / CP」로 잘렸다. 그래서 경계는 VM Boundary 를 사이에 둔 Guest 와 Host RAM 둘만 상자로 두고, 나머지 두 계층 이름은 그 자리 노드의 details 로 내렸다. SSOT 가 같은 절에 그린 관리 경로(virsh · libvirt · QEMU · KVM)와 자원 압박 경로는 다른 물음이라 이 그림에 지 않았다. §87 이 EPT 옆에 적어 둔 「EPT Violation 가능」과 Guest Page Table 옆의 「Guest #PF 가능」도 뺐다 — 이 기록의 본문은 정상 경로를 세우고, 세 계층의 fault 는 짝이 되는 기록이 맡는다. 마지막 Physical RAM 상자는 Host Physical Memory 와 뜻이 겹쳐 빼고 NUMA node 둘을 끝으로 두었다."
"rationale": "data-flow 로 고른 이유는 이 절이 답하는 물음이 「주소 하나가 어디를 지나 어디에 닿는가」이기 때문이다. 현재 renderer에서는 LR이 Guest → EPT → Host RAM 변환 사슬과 마지막 NUMA 분기를 겹침 없이 읽게 한다. Guest Userspace · Guest Kernel · KVM / CPU · Host RAM 넷을 모두 group 으로 두면 경계 라벨이 엣지와 충돌하므로, VM Boundary 를 사이에 둔 Guest 와 Host RAM 둘만 상자로 두고 나머지 계층은 node details 로 남겼다. SSOT 의 관리 경로와 fault 경로는 다른 물음이라 이 그림에 지 않다."
}
}
}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,237 @@
{
"version": "1.1",
"id": "memory-pressure-reclaim-swap-oom",
"title": "Host memory pressure가 storage와 OOM으로 갈라지는 경로",
"question": "Host memory pressure가 생겼을 때 reclaim 이후 어떤 경로가 swap I/O와 storage contention으로 이어지고, 확보 실패는 어디서 OOM으로 이어지는가",
"type": "data-flow",
"direction": "LR",
"audience": [
"VM host 메모리 압박을 추적하는 개발자",
"swap과 OOM의 발생 계층을 구분하려는 운영자"
],
"summary": "Host memory pressure는 reclaim으로 시작한다. clean file-backed page는 버렸다가 필요할 때 storage에서 다시 읽을 수 있고 anonymous page는 Host swap I/O로 이어질 수 있다. reclaim으로도 메모리를 확보하지 못하면 Host OOM 경로가 열린다.",
"alt": "Host memory pressure가 reclaim에서 clean page 재읽기, anonymous page의 Host swap과 storage contention, 메모리 확보 실패에 따른 Host OOM으로 갈라지는 흐름도. QEMU가 OOM victim이 될 때 VM 전체가 중단될 수 있다는 결과는 본문에서 설명한다.",
"long_description": "왼쪽에서 Host memory pressure가 reclaim으로 들어간다. reclaim 이후 clean file-backed page는 버렸다가 필요할 때 storage에서 다시 읽는 경로, anonymous page는 Host swap I/O를 통해 physical storage와 경쟁하는 경로, 필요한 메모리를 확보하지 못하면 Host OOM으로 가는 경로로 갈린다. storage contention이 application latency로 이어지는 결과와 Host OOM이 QEMU를 victim으로 고를 때 VM 전체가 중단될 수 있다는 결과는 본문에서 이어 설명한다.",
"source_context": {
"document": "docs/virtualization/final/document.md",
"document_sha256": "8c4ecc64c8cea9a4450ed7131fdd9cb2048dc092b66cd969f6346ed77887c210",
"anchor": {
"kind": "heading",
"value": "제2부 — 메모리 가상화",
"line": 1670
}
},
"composition": {
"profile": "component-flow",
"diagram_only": true,
"reference_ids": [
"payment-event-flow"
],
"rationale": "압박 하나가 reclaim 뒤 여러 자원 경로로 갈라지고 다시 storage contention과 OOM 결과로 이어지는 흐름이 핵심이다.",
"focus_node": "reclaim"
},
"groups": [],
"nodes": [
{
"id": "pressure",
"label": "Host memory pressure",
"kind": "resource",
"role": "source",
"description": "실제 Host RAM 수요가 available physical memory에 접근한 상태.",
"evidence": [
{
"start_line": 2818,
"end_line": 2831
}
],
"assumption": false
},
{
"id": "reclaim",
"label": "Reclaim",
"kind": "component",
"role": "service",
"emphasis": "primary",
"description": "Linux가 회수 가능한 page/cache를 처리하는 첫 대응.",
"evidence": [
{
"start_line": 2818,
"end_line": 2831
}
],
"assumption": false
},
{
"id": "clean-page",
"label": "clean file-backed page",
"kind": "data",
"role": "service",
"details": [
"discard · reread"
],
"description": "원본이 storage에 있어 RAM에서 제거하고 나중에 다시 읽을 수 있다.",
"evidence": [
{
"start_line": 2834,
"end_line": 2848
}
],
"assumption": false
},
{
"id": "host-swap",
"label": "Host swap I/O",
"kind": "storage",
"role": "store",
"details": [
"anonymous page"
],
"description": "anonymous page의 backing이 필요할 때 host swap I/O가 생길 수 있다.",
"evidence": [
{
"start_line": 2850,
"end_line": 2852
},
{
"start_line": 2856,
"end_line": 2886
}
],
"assumption": false
},
{
"id": "storage",
"label": "Storage contention",
"kind": "storage",
"role": "store",
"shape": "database",
"details": [
"Host swap",
"DB I/O",
"writeback"
],
"description": "여러 I/O가 한 physical device에 몰려 contention을 만든다.",
"evidence": [
{
"start_line": 2938,
"end_line": 2965
}
],
"assumption": false
},
{
"id": "host-oom",
"label": "Host OOM",
"kind": "result",
"role": "service",
"description": "reclaim 등으로도 필요한 memory 확보에 실패했을 때 Host kernel에서 발생할 수 있다.",
"evidence": [
{
"start_line": 3213,
"end_line": 3231
},
{
"start_line": 3249,
"end_line": 3265
}
],
"assumption": false
}
],
"edges": [
{
"id": "pressure-reclaim",
"from": "pressure",
"to": "reclaim",
"label": "pressure",
"kind": "control",
"evidence": [
{
"start_line": 2818,
"end_line": 2831
}
],
"assumption": false
},
{
"id": "reclaim-clean",
"from": "reclaim",
"to": "clean-page",
"label": "file-backed",
"kind": "control",
"evidence": [
{
"start_line": 2834,
"end_line": 2848
}
],
"assumption": false
},
{
"id": "clean-storage",
"from": "clean-page",
"to": "storage",
"label": "reread",
"kind": "data",
"style": "dashed",
"evidence": [
{
"start_line": 2834,
"end_line": 2848
}
],
"assumption": false
},
{
"id": "reclaim-swap",
"from": "reclaim",
"to": "host-swap",
"label": "anonymous",
"kind": "data",
"evidence": [
{
"start_line": 2850,
"end_line": 2852
}
],
"assumption": false
},
{
"id": "swap-storage",
"from": "host-swap",
"to": "storage",
"label": "swap",
"kind": "data",
"evidence": [
{
"start_line": 2871,
"end_line": 2886
},
{
"start_line": 2940,
"end_line": 2946
}
],
"assumption": false
},
{
"id": "reclaim-oom",
"from": "reclaim",
"to": "host-oom",
"label": "확보 실패",
"kind": "control",
"style": "dashed",
"evidence": [
{
"start_line": 3213,
"end_line": 3231
}
],
"assumption": false
}
],
"legend": [],
"metadata": {
"rationale": "분기와 재합류를 모두 넣어 자동 배치가 교차하던 이전 안을 줄였다. 그림은 Host pressure가 reclaim 뒤 clean-page·Host swap·Host OOM으로 갈리는 구조까지만 보이고, application latency와 QEMU victim 결과는 본문에 남긴다."
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -14,7 +14,7 @@
"long_description": "왼쪽에서 오른쪽으로 읽는다. 왼쪽 끝이 밖에서 친 curl 이 만든 inbound packet 이고 forward 훅으로 들어간다. 첫 점선 상자가 DNAT 파일에 둔 base 체인이다. priority filter - 10 이라 먼저 돌고, 그 안의 ct state new accept 가 이 packet 을 통과시킨다. 다음 점선 상자가 libvirt 가 만든 ip libvirt_network 테이블의 guest_input 체인이다. 앞 체인의 accept 는 평가를 끝내지 않으므로 같은 packet 이 이 체인으로 이어진다. 그 안의 상자 둘은 같은 체인의 맨 앞과 맨 끝이다. 구멍을 넣기 전에는 맨 앞이 비어 있어 packet 이 ct state established,related accept 에 걸리지 못한 채 체인 끝 reject 에 닿았고 connection refused 로 끝났다. 그 reject 규칙의 카운터가 4 패킷 240 바이트다. insert 로 구멍을 넣으면 그 규칙이 맨 앞에 서서 같은 packet 을 먼저 받아 192.168.122.10 의 엣지 nginx 로 보낸다. 실선이 구멍을 넣은 뒤의 경로이고 점선이 넣기 전의 경로다. 이 그림은 그 규칙이 libvirt 네트워크를 다시 세우면 사라진다는 것은 말하지 않는다.",
"source_context": {
"document": "docs/virtualization/final/document.md",
"document_sha256": "60d902c7aed218ba637b48603a3bd0e6b193fea59c9de97ffdb8e05d5087aedd",
"document_sha256": "8c4ecc64c8cea9a4450ed7131fdd9cb2048dc092b66cd969f6346ed77887c210",
"anchor": {
"kind": "heading",
"value": "180. nftables 는 앞 체인의 `accept` 로 뒤 체인의 `reject` 를 막지 못한다",
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -14,11 +14,11 @@
"long_description": "참여자는 왼쪽부터 QEMU vCPU Thread, VM1 Memory Backing, Node 0 CPU, Node 0 RAM, Node 1 RAM 다섯이다. 메시지는 위에서 아래로 읽는다. 1번에서 vCPU thread 가 Node 0 CPU 에 pinning 되고, 2번에서 VM1 의 memory backing 이 Node 1 에 놓인다. 그 배치에서 3번의 memory 접근은 Node 1 RAM 으로 가고 이것이 remote access 다. Node 0 과 Node 1 사이에는 interconnect 가 있어 이 접근만 그 구간을 건넌다. 4번과 5번은 그 뒤에 일어나는 다른 사건이 아니라 배치를 고친 구성이다. memory placement 와 binding 까지 Node 0 으로 맞추면 같은 접근이 Node 0 RAM 으로 가고 local access 가 된다. 접근 비용이 실제로 얼마나 차이 나는지, 이 Host 가 몇 개의 node 를 가졌는지는 그림이 말하지 않는다.",
"source_context": {
"document": "docs/virtualization/final/document.md",
"document_sha256": "181e2b3cc8a45bae81e7e8193d026937c4d586ae4495d3b2c323eb7e6abcfadd",
"document_sha256": "8c4ecc64c8cea9a4450ed7131fdd9cb2048dc092b66cd969f6346ed77887c210",
"anchor": {
"kind": "heading",
"value": "75. vCPU와 NUMA의 연결",
"line": 3313
"line": 3326
}
},
"composition": {
@@ -45,8 +45,8 @@
"description": "Guest vCPU 가 Host 에서 갖는 실체. Host scheduler 가 이것을 logical CPU 에 올린다.",
"evidence": [
{
"start_line": 3315,
"end_line": 3324
"start_line": 3328,
"end_line": 3337
}
],
"assumption": false
@@ -63,16 +63,16 @@
"description": "그 가상 머신의 Host physical backing page 가 놓이는 자리.",
"evidence": [
{
"start_line": 3327,
"end_line": 3327
"start_line": 3340,
"end_line": 3340
},
{
"start_line": 3352,
"end_line": 3354
"start_line": 3365,
"end_line": 3367
},
{
"start_line": 3381,
"end_line": 3382
"start_line": 3394,
"end_line": 3395
}
],
"assumption": false
@@ -90,8 +90,8 @@
"description": "vCPU thread 가 실행되는 logical CPU. NUMA Node 0 에 속한다.",
"evidence": [
{
"start_line": 3322,
"end_line": 3330
"start_line": 3335,
"end_line": 3343
}
],
"assumption": false
@@ -108,12 +108,12 @@
"description": "Node 0 CPU 와 같은 node 의 RAM.",
"evidence": [
{
"start_line": 3288,
"end_line": 3296
"start_line": 3301,
"end_line": 3309
},
{
"start_line": 3373,
"end_line": 3382
"start_line": 3386,
"end_line": 3395
}
],
"assumption": false
@@ -130,12 +130,12 @@
"description": "다른 node 의 RAM. Node 0 CPU 가 읽으면 interconnect 를 건넌다.",
"evidence": [
{
"start_line": 3299,
"end_line": 3306
"start_line": 3312,
"end_line": 3319
},
{
"start_line": 3334,
"end_line": 3334
"start_line": 3347,
"end_line": 3347
}
],
"assumption": false
@@ -152,12 +152,12 @@
"style": "solid",
"evidence": [
{
"start_line": 3322,
"end_line": 3324
"start_line": 3335,
"end_line": 3337
},
{
"start_line": 3347,
"end_line": 3350
"start_line": 3360,
"end_line": 3363
}
],
"assumption": false
@@ -172,12 +172,12 @@
"style": "solid",
"evidence": [
{
"start_line": 3327,
"end_line": 3327
"start_line": 3340,
"end_line": 3340
},
{
"start_line": 3352,
"end_line": 3354
"start_line": 3365,
"end_line": 3367
}
],
"assumption": false
@@ -192,12 +192,12 @@
"style": "dashed",
"evidence": [
{
"start_line": 3329,
"end_line": 3339
"start_line": 3342,
"end_line": 3352
},
{
"start_line": 3356,
"end_line": 3357
"start_line": 3369,
"end_line": 3370
}
],
"assumption": false
@@ -212,12 +212,12 @@
"style": "solid",
"evidence": [
{
"start_line": 3361,
"end_line": 3366
"start_line": 3374,
"end_line": 3379
},
{
"start_line": 3381,
"end_line": 3382
"start_line": 3394,
"end_line": 3395
}
],
"assumption": false
@@ -232,12 +232,12 @@
"style": "solid",
"evidence": [
{
"start_line": 3288,
"end_line": 3296
"start_line": 3301,
"end_line": 3309
},
{
"start_line": 3373,
"end_line": 3382
"start_line": 3386,
"end_line": 3395
}
],
"assumption": false
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -4,7 +4,7 @@
"title": "control/setup path 는 QEMU 를 지나고 data path 는 backend 에 따라 QEMU 를 지나기도 안 지나기도 한다",
"question": "설정하는 경로와 packet 이 흐르는 경로는 어디에서 갈라지고, 그 두 갈래 가운데 어느 쪽이 QEMU 사용자 공간을 지나는가",
"type": "network",
"direction": "TB",
"direction": "LR",
"audience": [
"가상 머신 안의 요청이 느릴 때 계층을 가르려는 사람",
"vhost-net 이 무엇을 우회하는지 확인하려는 사람"
@@ -14,11 +14,11 @@
"long_description": "왼쪽에서 오른쪽으로 읽는다. 위쪽 줄이 control/setup path 다. virsh 에서 libvirt 와 QEMU 를 지나 virtio-net Device Model 에 닿고, 그 device model 이 virtual NIC 생성과 feature negotiation, virtqueue 설정, vhost-net backend 설정을 맡는다. 아래쪽 줄이 data path 다. Physical NIC 에서 Physical NIC Driver 와 Linux Bridge, TAP 까지 온다. 두 줄은 네 번째 열 다음에서 만난다. device model 은 점선으로 backend 둘을 설정하고, TAP 은 실선으로 그 둘 가운데 실제로 쓰는 쪽에 packet 을 넘긴다. vhost-net 을 쓰면 packet 은 Host Kernel 안에서 처리돼 QEMU 사용자 공간을 지나지 않고, QEMU virtio backend 를 쓰면 같은 packet 이 QEMU 사용자 공간을 지난다. 어느 쪽이든 virtqueue 에서 만나 Guest 로 올라간다. 이 Host 가 둘 중 어느 backend 를 쓰고 있는지는 그림이 말하지 않는다.",
"source_context": {
"document": "docs/virtualization/final/document.md",
"document_sha256": "181e2b3cc8a45bae81e7e8193d026937c4d586ae4495d3b2c323eb7e6abcfadd",
"document_sha256": "8c4ecc64c8cea9a4450ed7131fdd9cb2048dc092b66cd969f6346ed77887c210",
"anchor": {
"kind": "heading",
"value": "125. 최종 기준 구조",
"line": 5682
"line": 5695
}
},
"composition": {
@@ -44,12 +44,12 @@
"description": "VM/network 관리 CLI. packet datapath 에는 직접 참여하지 않는다.",
"evidence": [
{
"start_line": 5662,
"end_line": 5662
"start_line": 5675,
"end_line": 5675
},
{
"start_line": 5687,
"end_line": 5689
"start_line": 5700,
"end_line": 5702
}
],
"assumption": false
@@ -63,12 +63,12 @@
"description": "VM lifecycle 과 NIC/network configuration 을 관리한다.",
"evidence": [
{
"start_line": 5663,
"end_line": 5663
"start_line": 5676,
"end_line": 5676
},
{
"start_line": 5691,
"end_line": 5691
"start_line": 5704,
"end_line": 5704
}
],
"assumption": false
@@ -85,12 +85,12 @@
"description": "실제로 실행되는 프로세스. 이 안에 virtio-net device model 이 있다.",
"evidence": [
{
"start_line": 5666,
"end_line": 5666
"start_line": 5679,
"end_line": 5679
},
{
"start_line": 5693,
"end_line": 5693
"start_line": 5706,
"end_line": 5706
}
],
"assumption": false
@@ -111,12 +111,12 @@
"description": "control/setup path 가 닿는 자리. 장치를 만들고 backend 를 연결한다.",
"evidence": [
{
"start_line": 5666,
"end_line": 5666
"start_line": 5679,
"end_line": 5679
},
{
"start_line": 5695,
"end_line": 5700
"start_line": 5708,
"end_line": 5713
}
],
"assumption": false
@@ -130,8 +130,8 @@
"description": "밖에서 온 packet 이 처음 닿는 하드웨어.",
"evidence": [
{
"start_line": 5706,
"end_line": 5708
"start_line": 5719,
"end_line": 5721
}
],
"assumption": false
@@ -145,12 +145,12 @@
"description": "그 하드웨어를 제어하는 Host kernel driver.",
"evidence": [
{
"start_line": 5674,
"end_line": 5674
"start_line": 5687,
"end_line": 5687
},
{
"start_line": 5710,
"end_line": 5710
"start_line": 5723,
"end_line": 5723
}
],
"assumption": false
@@ -164,12 +164,12 @@
"description": "L2 software switch 와 L3 routing, 주소 변환이 놓이는 자리.",
"evidence": [
{
"start_line": 5672,
"end_line": 5673
"start_line": 5685,
"end_line": 5686
},
{
"start_line": 5712,
"end_line": 5712
"start_line": 5725,
"end_line": 5725
}
],
"assumption": false
@@ -186,12 +186,12 @@
"description": "VM 의 Ethernet frame 과 Host Linux networking 을 잇는 접점.",
"evidence": [
{
"start_line": 5671,
"end_line": 5671
"start_line": 5684,
"end_line": 5684
},
{
"start_line": 5714,
"end_line": 5714
"start_line": 5727,
"end_line": 5727
}
],
"assumption": false
@@ -208,12 +208,12 @@
"description": "반복되는 packet datapath 를 Host kernel 에서 처리하는 backend.",
"evidence": [
{
"start_line": 5669,
"end_line": 5669
"start_line": 5682,
"end_line": 5682
},
{
"start_line": 5716,
"end_line": 5716
"start_line": 5729,
"end_line": 5729
}
],
"assumption": false
@@ -230,12 +230,12 @@
"description": "vhost-net 을 쓰지 않을 때 datapath backend 를 맡는 쪽.",
"evidence": [
{
"start_line": 5668,
"end_line": 5668
"start_line": 5681,
"end_line": 5681
},
{
"start_line": 5742,
"end_line": 5742
"start_line": 5755,
"end_line": 5755
}
],
"assumption": false
@@ -252,12 +252,12 @@
"description": "Guest 와 Host backend 가 I/O buffer 를 주고받는 descriptor 기반 공유 큐.",
"evidence": [
{
"start_line": 5667,
"end_line": 5667
"start_line": 5680,
"end_line": 5680
},
{
"start_line": 5718,
"end_line": 5718
"start_line": 5731,
"end_line": 5731
}
],
"assumption": false
@@ -277,12 +277,12 @@
"description": "packet 이 올라가 애플리케이션 socket 에 닿는 쪽.",
"evidence": [
{
"start_line": 5664,
"end_line": 5665
"start_line": 5677,
"end_line": 5678
},
{
"start_line": 5720,
"end_line": 5726
"start_line": 5733,
"end_line": 5739
}
],
"assumption": false
@@ -298,12 +298,12 @@
"style": "solid",
"evidence": [
{
"start_line": 5662,
"end_line": 5663
"start_line": 5675,
"end_line": 5676
},
{
"start_line": 5684,
"end_line": 5691
"start_line": 5697,
"end_line": 5704
}
],
"assumption": false
@@ -312,17 +312,17 @@
"id": "libvirt-to-qemu",
"from": "libvirt",
"to": "qemu",
"label": "NIC/network configuration",
"label": "NIC config",
"kind": "control",
"style": "solid",
"evidence": [
{
"start_line": 5663,
"end_line": 5663
"start_line": 5676,
"end_line": 5676
},
{
"start_line": 5691,
"end_line": 5693
"start_line": 5704,
"end_line": 5706
}
],
"assumption": false
@@ -336,12 +336,12 @@
"style": "solid",
"evidence": [
{
"start_line": 5666,
"end_line": 5666
"start_line": 5679,
"end_line": 5679
},
{
"start_line": 5693,
"end_line": 5695
"start_line": 5706,
"end_line": 5708
}
],
"assumption": false
@@ -355,12 +355,12 @@
"style": "solid",
"evidence": [
{
"start_line": 5671,
"end_line": 5671
"start_line": 5684,
"end_line": 5684
},
{
"start_line": 5708,
"end_line": 5710
"start_line": 5721,
"end_line": 5723
}
],
"assumption": false
@@ -374,12 +374,12 @@
"style": "solid",
"evidence": [
{
"start_line": 5672,
"end_line": 5672
"start_line": 5685,
"end_line": 5685
},
{
"start_line": 5710,
"end_line": 5712
"start_line": 5723,
"end_line": 5725
}
],
"assumption": false
@@ -393,12 +393,12 @@
"style": "solid",
"evidence": [
{
"start_line": 5671,
"end_line": 5671
"start_line": 5684,
"end_line": 5684
},
{
"start_line": 5712,
"end_line": 5714
"start_line": 5725,
"end_line": 5727
}
],
"assumption": false
@@ -412,12 +412,12 @@
"style": "dashed",
"evidence": [
{
"start_line": 5700,
"end_line": 5700
"start_line": 5713,
"end_line": 5713
},
{
"start_line": 5716,
"end_line": 5716
"start_line": 5729,
"end_line": 5729
}
],
"assumption": false
@@ -431,12 +431,12 @@
"style": "dashed",
"evidence": [
{
"start_line": 5668,
"end_line": 5668
"start_line": 5681,
"end_line": 5681
},
{
"start_line": 5700,
"end_line": 5700
"start_line": 5713,
"end_line": 5713
}
],
"assumption": false
@@ -450,12 +450,12 @@
"style": "solid",
"evidence": [
{
"start_line": 5703,
"end_line": 5703
"start_line": 5716,
"end_line": 5716
},
{
"start_line": 5714,
"end_line": 5716
"start_line": 5727,
"end_line": 5729
}
],
"assumption": false
@@ -469,12 +469,12 @@
"style": "solid",
"evidence": [
{
"start_line": 5729,
"end_line": 5729
"start_line": 5742,
"end_line": 5742
},
{
"start_line": 5740,
"end_line": 5742
"start_line": 5753,
"end_line": 5755
}
],
"assumption": false
@@ -488,12 +488,12 @@
"style": "solid",
"evidence": [
{
"start_line": 5703,
"end_line": 5703
"start_line": 5716,
"end_line": 5716
},
{
"start_line": 5716,
"end_line": 5718
"start_line": 5729,
"end_line": 5731
}
],
"assumption": false
@@ -507,12 +507,12 @@
"style": "solid",
"evidence": [
{
"start_line": 5729,
"end_line": 5729
"start_line": 5742,
"end_line": 5742
},
{
"start_line": 5742,
"end_line": 5744
"start_line": 5755,
"end_line": 5757
}
],
"assumption": false
@@ -521,17 +521,17 @@
"id": "virtqueue-to-guest",
"from": "virtqueue",
"to": "guest",
"label": "virtio-net Frontend Driver",
"label": "frontend",
"kind": "data",
"style": "solid",
"evidence": [
{
"start_line": 5664,
"end_line": 5664
"start_line": 5677,
"end_line": 5677
},
{
"start_line": 5718,
"end_line": 5726
"start_line": 5731,
"end_line": 5739
}
],
"assumption": false
@@ -539,6 +539,6 @@
],
"legend": [],
"metadata": {
"rationale": "network 으로 고른 이유는 이 절이 답하는 물음이 「어느 경로가 어느 계층을 지나는가」이기 때문이다. 점선은 설정이고 실선은 packet 이다 — 색이 아니라 선 모양으로 갈랐다. Internet/Client 와 Socket, Keycloak 은 노드로 세우지 않고 양 끝 노드의 details 로 내렸다. §125 가 적은 세 직선을 그대로 세 장으로 옮기면 SSOT 와 같은 그림이 되고, 이 글이 필요로 하는 것은 그 셋이 어디를 공유하는가다. LR 로 그렸더니 캔버스가 2249px 가 되어 TB 로 바꿨다. 갈래 구간의 엇갈리는 두 이음에 라벨을 다 붙였더니 라벨끼리 겹치고(497px²) 하나가 device model 상자에 먹혀(316px²) check-figure-overlap 이 걸렸다. 그 구간에서는 vhost-net 쪽 두 이음에만 이름을 남기고 QEMU backend 쪽 두 이음은 선 모양(점선=설정·실선=packet)과 도착 상자 이름으로 읽게 두었다."
"rationale": "이 절이 세운 것은 control/setup path와 두 data path가 device model·backend·virtqueue에서 갈라지고 다시 만나는 구조다. component-flow와 LR를 사용하면 이 관계를 상자 충돌 없이 보존한다. NIC config는 NIC/network configuration의 축약이고 frontend는 도착 노드의 virtio-net Frontend Driver를 반복하지 않기 위한 edge 축약이다. backend 선택이라는 핵심 의미와 QEMU userspace 우회 여부는 node/edge 구조로 그대로 남는다."
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -4,7 +4,7 @@
"title": "세 fault 는 변환 사슬의 서로 다른 단계에서 갈라져 나가 서로 다른 계층이 받는다",
"question": "Guest Page Fault · EPT Violation · Host Page Fault 는 각각 어느 단계에서 갈라지고 누가 받는가",
"type": "flow",
"direction": "TB",
"direction": "LR",
"audience": [
"VM 안의 메모리 지연을 계층별로 가르려는 사람",
"fault 지표를 어느 쪽부터 볼지 정하려는 사람"
@@ -14,11 +14,11 @@
"long_description": "위에서 아래로 읽는다. 맨 위 왼쪽에 Guest Page Table 이 있고 거기서 갈래가 둘이다. 하나는 Guest Page Fault 로 Guest Kernel 에 빠지고, 다른 하나는 GPA 를 들고 아래 EPT 로 내려간다. EPT 에서도 갈래가 나뉘어 EPT Violation 은 VM Exit 을 거쳐 KVM 으로 간다. 맨 위 오른쪽에는 QEMU 의 Guest RAM Backing 이 따로 있고 그쪽 갈래인 Host Page Fault 는 Host Kernel 로 간다. 받는 자리의 이름이 그대로 계층 이름이다 — Guest Kernel · KVM · Host Kernel. QEMU 갈래가 EPT 갈래와 선으로 이어져 있지 않은 것은 근거 문서가 둘을 잇는 문장을 두지 않았기 때문이다. 다섯 원인이 무엇인지와 두 사건을 항목별로 견주는 것은 본문의 표가 맡는다.",
"source_context": {
"document": "docs/virtualization/final/document.md",
"document_sha256": "181e2b3cc8a45bae81e7e8193d026937c4d586ae4495d3b2c323eb7e6abcfadd",
"document_sha256": "8c4ecc64c8cea9a4450ed7131fdd9cb2048dc092b66cd969f6346ed77887c210",
"anchor": {
"kind": "heading",
"value": "48. Guest Page Fault와 EPT Violation 비교",
"line": 2480
"line": 2493
}
},
"composition": {
@@ -44,12 +44,12 @@
"description": "첫 번째 translation 단계. 여기서 접근을 완료할 수 없으면 Guest Page Fault 가 난다.",
"evidence": [
{
"start_line": 2443,
"end_line": 2445
"start_line": 2456,
"end_line": 2458
},
{
"start_line": 2484,
"end_line": 2485
"start_line": 2497,
"end_line": 2498
}
],
"assumption": false
@@ -63,12 +63,12 @@
"description": "Guest Page Fault 를 받는 계층.",
"evidence": [
{
"start_line": 2487,
"end_line": 2487
"start_line": 2500,
"end_line": 2500
},
{
"start_line": 2493,
"end_line": 2494
"start_line": 2506,
"end_line": 2507
}
],
"assumption": false
@@ -86,12 +86,12 @@
"description": "두 번째 translation 단계. Guest translation 이 성공한 뒤 여기서 막히면 EPT Violation 이 난다.",
"evidence": [
{
"start_line": 2465,
"end_line": 2469
"start_line": 2478,
"end_line": 2482
},
{
"start_line": 2484,
"end_line": 2485
"start_line": 2497,
"end_line": 2498
}
],
"assumption": false
@@ -105,12 +105,12 @@
"description": "EPT Violation 을 VM Exit 뒤에 받는 계층.",
"evidence": [
{
"start_line": 2471,
"end_line": 2473
"start_line": 2484,
"end_line": 2486
},
{
"start_line": 2487,
"end_line": 2487
"start_line": 2500,
"end_line": 2500
}
],
"assumption": false
@@ -127,12 +127,12 @@
"description": "Host 의 일반 userspace 메모리로 관리되는 자리. demand allocation 이나 reclaim, swap 때문에 여기서 fault 가 난다.",
"evidence": [
{
"start_line": 2504,
"end_line": 2504
"start_line": 2517,
"end_line": 2517
},
{
"start_line": 2514,
"end_line": 2519
"start_line": 2527,
"end_line": 2532
}
],
"assumption": false
@@ -146,8 +146,8 @@
"description": "Host Page Fault 를 받아 필요한 Host page 를 처리하는 계층.",
"evidence": [
{
"start_line": 2521,
"end_line": 2525
"start_line": 2534,
"end_line": 2538
}
],
"assumption": false
@@ -158,17 +158,17 @@
"id": "guest-fault-branch",
"from": "guest-page-table",
"to": "guest-kernel",
"label": "Guest Page Fault",
"label": "Guest #PF",
"kind": "event",
"style": "solid",
"evidence": [
{
"start_line": 2484,
"end_line": 2487
"start_line": 2497,
"end_line": 2500
},
{
"start_line": 2493,
"end_line": 2494
"start_line": 2506,
"end_line": 2507
}
],
"assumption": false
@@ -182,8 +182,8 @@
"style": "solid",
"evidence": [
{
"start_line": 2461,
"end_line": 2467
"start_line": 2474,
"end_line": 2480
}
],
"assumption": false
@@ -197,12 +197,12 @@
"style": "solid",
"evidence": [
{
"start_line": 2467,
"end_line": 2473
"start_line": 2480,
"end_line": 2486
},
{
"start_line": 2487,
"end_line": 2487
"start_line": 2500,
"end_line": 2500
}
],
"assumption": false
@@ -211,13 +211,13 @@
"id": "host-fault-branch",
"from": "qemu-backing",
"to": "host-kernel",
"label": "Host Page Fault",
"label": "Host #PF",
"kind": "event",
"style": "solid",
"evidence": [
{
"start_line": 2517,
"end_line": 2525
"start_line": 2530,
"end_line": 2538
}
],
"assumption": false
@@ -225,6 +225,6 @@
],
"legend": [],
"metadata": {
"rationale": "flow 로 고른 이유는 이 절이 답하는 물음이 「어느 단계에서 갈라져 누구에게 가는가」이기 때문이다. 세 fault 를 나란히 늘어놓으면 그것은 본문에 이미 있는 비교표와 같은 것이 되므로, 갈래가 사슬의 어느 자리에서 나오는지를 자리로 보이게 세웠다. EPT 에서 나온 갈래와 QEMU backing 에서 나온 갈래를 선으로 잇지 않은 것은 근거 문서가 EPT 가 얻은 HPA 와 QEMU backing 의 Host physical address 를 같은 것이라고 적지 않았기 때문이다. 없는 문장을 선으로 만들지 않았다. 처음에는 Guest 와 Host 를 group 상자로 두었는데, 레이아웃이 노드를 group 별로 모으지 않아 Host 상자가 Guest 노드까지 감싸 버렸다. 자리의 이름(Guest Kernel · KVM · Host Kernel)이 이미 계층을 말하므로 group 을 빼고 이름에 맡겼다."
"rationale": "flow 로 고른 이유는 이 절이 답하는 물음이 「어느 단계에서 갈라져 누구에게 가는가」이기 때문이다. 현재 renderer에서는 LR가 변환 사슬과 세 fault branch를 겹침 없이 보인다. Guest #PF 와 Host #PF 는 본문에서 설명하는 Guest Page Fault·Host Page Fault의 축약 라벨이고 의미를 줄이지 않는다. EPT Violation은 VM Exit까지 포함해 그대로 둔다. 관계는 선으로 만들지 않는다."
}
}
}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,217 @@
{
"version": "1.1",
"id": "qcow2-mapping-clusters",
"title": "qcow2 L1·L2 매핑과 backing fallback",
"question": "게스트 오프셋은 qcow2 파일의 데이터 클러스터로 어떻게 매핑되고, L2 항목이 0일 때 왜 backing file이나 zero-fill로 갈리는가",
"type": "data-flow",
"direction": "LR",
"audience": [
"qcow2 overlay 내부를 이해하려는 개발자",
"virtual size와 실제 disk size 차이를 이해하려는 운영자"
],
"summary": "게스트 오프셋의 상위 비트가 L1, 중간 비트가 L2, 하위 비트가 클러스터 내부 위치를 고른다. L2 항목에 값이 있으면 현재 qcow2의 데이터 클러스터를 읽고, 0이면 backing file 유무에 따라 같은 위치를 읽거나 0으로 채운 클러스터를 돌려준다.",
"alt": "게스트 오프셋이 L1과 L2 표를 지나 현재 qcow2 데이터 클러스터로 가거나, L2 항목이 0일 때 backing file 또는 zero-fill로 갈라지는 qcow2 매핑도.",
"long_description": "게스트가 읽으려는 위치는 L1 표와 L2 표를 통해 파일 안 데이터 클러스터 오프셋으로 변환된다. 매핑된 값이 있으면 현재 qcow2의 데이터 클러스터를 읽는다. 값이 0이고 backing file이 있으면 base.qcow2의 같은 가상 위치를 읽고, backing file이 없으면 0으로 채운 64KB를 반환한다. backing file 경로는 qcow2 헤더의 문자열이므로 오버레이만 옮기면 부팅할 수 없다는 제한도 그림 끝에 반영한다.",
"source_context": {
"document": "docs/virtualization/final/document.md",
"document_sha256": "8c4ecc64c8cea9a4450ed7131fdd9cb2048dc092b66cd969f6346ed77887c210",
"anchor": {
"kind": "heading",
"value": "231. qcow2 파일 내부는 어떻게 생겼나 — 매핑표가 전부다",
"line": 14221
}
},
"composition": {
"profile": "component-flow",
"diagram_only": true,
"reference_ids": [
"payment-event-flow"
],
"rationale": "매핑의 핵심은 한 위치가 L1/L2를 지나 세 결과 중 하나로 흐르는 관계다.",
"focus_node": "l2"
},
"groups": [],
"nodes": [
{
"id": "guest-offset",
"label": "Guest offset",
"kind": "data",
"role": "source",
"details": [
"virtual disk position"
],
"description": "게스트가 읽으려는 가상 디스크 위치.",
"evidence": [
{
"start_line": 14317,
"end_line": 14325
}
],
"assumption": false
},
{
"id": "l1",
"label": "L1 table",
"kind": "component",
"role": "service",
"details": [
"상위 비트"
],
"description": "필요한 L2 표가 있는 클러스터 위치를 찾는다.",
"evidence": [
{
"start_line": 14312,
"end_line": 14337
}
],
"assumption": false
},
{
"id": "l2",
"label": "L2 entry",
"kind": "component",
"role": "service",
"emphasis": "primary",
"details": [
"중간 13비트"
],
"description": "현재 qcow2 데이터 클러스터 오프셋을 가지거나 0일 수 있다.",
"evidence": [
{
"start_line": 14320,
"end_line": 14347
}
],
"assumption": false
},
{
"id": "local-cluster",
"label": "현재 qcow2 data cluster",
"kind": "store",
"role": "store",
"shape": "database",
"description": "L2 값이 있을 때 같은 qcow2 파일 안에서 읽는 데이터 클러스터.",
"evidence": [
{
"start_line": 14267,
"end_line": 14272
},
{
"start_line": 14343,
"end_line": 14345
}
],
"assumption": false
},
{
"id": "backing",
"label": "base.qcow2",
"kind": "store",
"role": "store",
"shape": "database",
"details": [
"backing file"
],
"description": "L2 항목이 0이고 backing file이 있을 때 같은 가상 위치를 읽는 바닥 파일.",
"evidence": [
{
"start_line": 14343,
"end_line": 14354
}
],
"assumption": false
},
{
"id": "zero-fill",
"label": "zero-filled 64KB",
"kind": "result",
"role": "sink",
"description": "L2 항목이 0이고 backing file도 없을 때 만들어 반환하는 0 데이터.",
"evidence": [
{
"start_line": 14343,
"end_line": 14347
}
],
"assumption": false
}
],
"edges": [
{
"id": "offset-l1",
"from": "guest-offset",
"to": "l1",
"label": "상위 비트",
"kind": "data",
"evidence": [
{
"start_line": 14317,
"end_line": 14324
}
],
"assumption": false
},
{
"id": "l1-l2",
"from": "l1",
"to": "l2",
"label": "L2 cluster",
"kind": "data",
"evidence": [
{
"start_line": 14320,
"end_line": 14337
}
],
"assumption": false
},
{
"id": "l2-local",
"from": "l2",
"to": "local-cluster",
"label": "mapped",
"kind": "data",
"evidence": [
{
"start_line": 14343,
"end_line": 14345
}
],
"assumption": false
},
{
"id": "l2-backing",
"from": "l2",
"to": "backing",
"label": "0 + backing",
"kind": "data",
"style": "dashed",
"evidence": [
{
"start_line": 14343,
"end_line": 14350
}
],
"assumption": false
},
{
"id": "l2-zero",
"from": "l2",
"to": "zero-fill",
"label": "0 + no backing",
"kind": "result",
"style": "dashed",
"evidence": [
{
"start_line": 14343,
"end_line": 14347
}
],
"assumption": false
}
],
"legend": [],
"metadata": {
"rationale": "refcount·압축·스냅샷은 같은 파일 형식의 다른 질문이라 제외하고, 읽기 매핑과 backing fallback에만 집중했다."
}
}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,224 @@
{
"version": "1.1",
"id": "qemu-block-backend-forms",
"title": "virtqueue 뒤 QEMU block backend의 세 형태",
"question": "Guest /dev/vda 요청이 VM boundary를 넘은 뒤 QEMU에서 qcow2, RAW, Host block device 중 어느 backend로 이어지는가",
"type": "architecture",
"direction": "LR",
"audience": [
"virtio-blk와 QEMU block backend 경계를 이해하려는 개발자",
"Guest disk 이름만으로 Host backend를 단정하지 않으려는 운영자"
],
"summary": "Guest /dev/vda의 I/O는 virtio-blk·virtqueue를 지나 Host userspace의 QEMU block backend로 넘어간다. backend는 qcow2 file, RAW file, Host block device 세 형태일 수 있다. 파일 backend는 Host filesystem을 경유하고, Host block device는 파일이 아닌 장치를 backend로 쓴다.",
"alt": "Guest의 /dev/vda가 virtio-blk와 virtqueue를 지나 QEMU block backend에 도착한 뒤 qcow2 파일, RAW 파일, Host block device 세 갈래로 나뉘는 구조도. 그 아래 Host block stack은 별도 Concept이 맡아 이 그림에서는 다시 합치지 않는다.",
"long_description": "왼쪽에서 Guest /dev/vda 요청이 virtio-blk와 virtqueue를 지나 VM boundary를 넘는다. QEMU block backend에서 qcow2 file, RAW file, Host block device 세 갈래로 나뉜다. qcow2와 RAW 노드에는 Host filesystem을 경유한다는 차이를, Host block device에는 파일 backend가 아니라는 차이를 적었다. 세 경로 아래의 Host block stack은 별도 Concept이 맡으므로 이 그림에서는 다시 합치지 않는다.",
"source_context": {
"document": "docs/virtualization/final/document.md",
"document_sha256": "8c4ecc64c8cea9a4450ed7131fdd9cb2048dc092b66cd969f6346ed77887c210",
"anchor": {
"kind": "heading",
"value": "제4부 — 스토리지 가상화",
"line": 5791
}
},
"composition": {
"profile": "component-flow",
"diagram_only": true,
"reference_ids": [
"payment-event-flow"
],
"rationale": "핵심은 Guest disk 요청이 QEMU block backend를 지나 세 형태로 갈라지는 방향성이다. zone 상자보다 branch가 읽히는 것이 중요해 component-flow를 쓴다.",
"focus_node": "backend"
},
"groups": [],
"nodes": [
{
"id": "vda",
"label": "/dev/vda",
"kind": "storage",
"role": "source",
"description": "Guest가 보는 virtual disk.",
"evidence": [
{
"start_line": 6355,
"end_line": 6363
}
],
"assumption": false
},
{
"id": "virtio-queue",
"label": "virtio-blk · virtqueue",
"kind": "component",
"role": "service",
"description": "Guest /dev/vda 요청을 virtio-blk가 받아 virtqueue에 싣는 경로를 한 노드로 묶었다.",
"evidence": [
{
"start_line": 6355,
"end_line": 6364
}
],
"assumption": false,
"details": [
"Guest",
"VM boundary 직전"
]
},
{
"id": "backend",
"label": "QEMU block backend",
"kind": "service",
"role": "service",
"emphasis": "primary",
"description": "Guest virtual I/O를 실제 Host backend에 연결한다.",
"evidence": [
{
"start_line": 6367,
"end_line": 6387
}
],
"assumption": false
},
{
"id": "qcow2",
"label": "qcow2 file",
"kind": "storage",
"role": "service",
"details": [
"file backend",
"mapping · metadata",
"→ Host filesystem"
],
"description": "sparse/COW 기능을 가진 file backend.",
"evidence": [
{
"start_line": 6419,
"end_line": 6460
},
{
"start_line": 6512,
"end_line": 6547
}
],
"assumption": false
},
{
"id": "raw",
"label": "RAW file",
"kind": "storage",
"role": "service",
"description": "상대적으로 단순한 offset 대응을 쓰는 file backend.",
"evidence": [
{
"start_line": 6512,
"end_line": 6547
}
],
"assumption": false,
"details": [
"file backend",
"offset 대응",
"→ Host filesystem"
]
},
{
"id": "block-device",
"label": "Host block device",
"kind": "storage",
"role": "service",
"details": [
"/dev/...",
"file 없음",
"→ Host block stack"
],
"description": "QEMU가 Host block device를 직접 backend로 사용할 수 있는 형태.",
"evidence": [
{
"start_line": 6551,
"end_line": 6575
}
],
"assumption": false
}
],
"edges": [
{
"id": "vda-virtio",
"from": "vda",
"to": "virtio-queue",
"label": "block I/O",
"kind": "request",
"evidence": [
{
"start_line": 6355,
"end_line": 6363
}
],
"assumption": false
},
{
"id": "vq-backend",
"from": "virtio-queue",
"to": "backend",
"label": "VM boundary",
"kind": "request",
"evidence": [
{
"start_line": 6362,
"end_line": 6374
}
],
"assumption": false
},
{
"id": "backend-qcow2",
"from": "backend",
"to": "qcow2",
"label": "qcow2 backend",
"kind": "data",
"evidence": [
{
"start_line": 6369,
"end_line": 6379
},
{
"start_line": 6419,
"end_line": 6460
}
],
"assumption": false
},
{
"id": "backend-raw",
"from": "backend",
"to": "raw",
"label": "RAW backend",
"kind": "data",
"evidence": [
{
"start_line": 6512,
"end_line": 6547
}
],
"assumption": false
},
{
"id": "backend-block",
"from": "backend",
"to": "block-device",
"label": "block-device backend",
"kind": "data",
"evidence": [
{
"start_line": 6551,
"end_line": 6575
}
],
"assumption": false
}
],
"legend": [],
"metadata": {
"rationale": "이 그림의 물음은 QEMU backend 세 형태의 경계 차이다. 세 경로가 결국 Host block stack으로 내려가는 것은 별도 Concept이 맡으므로, 재합류 노드는 빼고 각 backend detail에 다음 계층을 적었다."
}
}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,230 @@
{
"version": "1.1",
"id": "two-l7-hops-entry-recursion",
"title": "L7 두 홉과 진입점 이중화 재귀",
"question": "요청은 왜 nginx와 Traefik이라는 두 L7 홉을 지나며, nginx를 두 대로 늘리면 왜 다시 selector가 필요한가",
"type": "architecture",
"direction": "LR",
"audience": [
"실험대의 nginx와 Traefik 역할을 구분하려는 개발자",
"진입점 이중화가 왜 새 SPOF를 만드는지 이해하려는 운영자"
],
"summary": "정상 요청은 브라우저에서 edge nginx, Traefik, Pod 순으로 흐르고 두 L7 홉은 서로 다른 라우팅 범위를 맡는다. edge nginx를 두 대로 늘리면 어느 nginx를 고를 selector가 필요해지고 그 selector가 다시 단일 장애점이 된다.",
"alt": "브라우저에서 edge nginx와 Traefik을 거쳐 Pod로 가는 두 L7 홉과, edge nginx를 두 대로 늘릴 때 앞단 selector가 새 단일 장애점이 되는 재귀를 함께 보여 주는 흐름도.",
"long_description": "왼쪽의 정상 경로는 브라우저가 TLS를 edge nginx에서 종료하고 고정 노드 IP로 전달한 뒤, Traefik이 동적으로 Pod를 고르는 흐름이다. 아래쪽 이중화 경로는 nginx-1과 nginx-2 앞에 selector를 두면 어느 nginx로 갈지 정할 수 있지만 selector 자체가 새 SPOF가 되어 같은 질문이 반복된다는 점을 보여 준다. VRRP·DNS·BGP·클라우드 LB 같은 재귀 종료 방식의 상세 비교는 본문 표에 남기고 그림에는 넣지 않는다.",
"source_context": {
"document": "docs/virtualization/final/document.md",
"document_sha256": "8c4ecc64c8cea9a4450ed7131fdd9cb2048dc092b66cd969f6346ed77887c210",
"anchor": {
"kind": "heading",
"value": "261. 진입점 자체가 죽으면 — 로드밸런서의 재귀 문제",
"line": 15796
}
},
"composition": {
"profile": "component-flow",
"diagram_only": true,
"reference_ids": [
"payment-event-flow"
],
"rationale": "핵심은 요청과 선택 경로의 방향성이다. 비교표보다 엣지를 지웠을 때 의미가 더 크게 사라지므로 component-flow로 그린다.",
"focus_node": "edge-nginx"
},
"groups": [],
"nodes": [
{
"id": "browser",
"label": "Browser",
"kind": "actor",
"role": "source",
"shape": "actor",
"description": "외부 요청의 시작점.",
"evidence": [
{
"start_line": 15860,
"end_line": 15864
}
],
"assumption": false
},
{
"id": "edge-nginx",
"label": "edge nginx",
"kind": "service",
"role": "service",
"emphasis": "primary",
"details": [
"L7",
"TLS 종료",
"고정 노드 선택"
],
"description": "클러스터 밖 진입점. TLS와 인증서, forwarded header, 어느 노드로 보낼지를 맡는다.",
"evidence": [
{
"start_line": 15860,
"end_line": 15882
}
],
"assumption": false
},
{
"id": "traefik",
"label": "Traefik",
"kind": "service",
"role": "service",
"details": [
"L7",
"Ingress",
"동적 Pod 선택"
],
"description": "클러스터 안에서 Service/Ingress 상태를 따라 어느 Pod로 보낼지 정한다.",
"evidence": [
{
"start_line": 15872,
"end_line": 15882
}
],
"assumption": false
},
{
"id": "pod",
"label": "Pod",
"kind": "runtime",
"role": "sink",
"description": "Traefik이 선택한 실제 워크로드.",
"evidence": [
{
"start_line": 15860,
"end_line": 15864
}
],
"assumption": false
},
{
"id": "selector",
"label": "앞단 selector",
"kind": "service",
"role": "service",
"details": [
"edge redundancy"
],
"description": "nginx를 여러 머신으로 이중화했을 때 어느 nginx로 갈지 정하는 새 앞단.",
"evidence": [
{
"start_line": 15941,
"end_line": 15945
}
],
"assumption": false
},
{
"id": "nginx-pair",
"label": "nginx-1 · nginx-2",
"kind": "service",
"role": "service",
"details": [
"서로 다른 머신"
],
"description": "진짜 이중화를 위해 머신을 늘린 edge nginx 쌍.",
"evidence": [
{
"start_line": 15934,
"end_line": 15944
}
],
"assumption": false
},
{
"id": "new-spof",
"label": "selector가 새 SPOF",
"kind": "result",
"role": "sink",
"description": "selector를 하나 더 두면 같은 단일 장애점 질문이 한 단계 앞으로 이동한다.",
"evidence": [
{
"start_line": 15941,
"end_line": 15952
}
],
"assumption": false
}
],
"edges": [
{
"id": "browser-edge",
"from": "browser",
"to": "edge-nginx",
"label": "HTTPS",
"kind": "request",
"evidence": [
{
"start_line": 15860,
"end_line": 15864
}
],
"assumption": false
},
{
"id": "edge-traefik",
"from": "edge-nginx",
"to": "traefik",
"label": "노드 선택 · HTTP",
"kind": "request",
"evidence": [
{
"start_line": 15872,
"end_line": 15882
}
],
"assumption": false
},
{
"id": "traefik-pod",
"from": "traefik",
"to": "pod",
"label": "Ingress routing",
"kind": "request",
"evidence": [
{
"start_line": 15860,
"end_line": 15882
}
],
"assumption": false
},
{
"id": "selector-pair",
"from": "selector",
"to": "nginx-pair",
"label": "nginx 선택",
"kind": "request",
"style": "dashed",
"evidence": [
{
"start_line": 15941,
"end_line": 15945
}
],
"assumption": false
},
{
"id": "selector-spof",
"from": "selector",
"to": "new-spof",
"label": "단일 selector",
"kind": "result",
"style": "dashed",
"evidence": [
{
"start_line": 15941,
"end_line": 15952
}
],
"assumption": false
}
],
"legend": [],
"metadata": {
"rationale": "두 L7 홉의 역할 차이와 진입점 이중화 재귀만 남겼다. VRRP의 상세 동작은 별도 본문 설명이므로 그림에서 제외한다."
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -14,11 +14,11 @@
"long_description": "참여자는 왼쪽부터 Guest kernel 의 virtio-balloon Driver, virtqueue, QEMU virtio-balloon Device, Host Memory Management 네 개다. 메시지는 위에서 아래로 읽는다. 1번은 Host Memory Management 가 QEMU 쪽 device 에 balloon target 조정을 보내는 것이고, 2번에서 그 요청이 virtqueue 로 내려가면서 VM Boundary 를 넘고, 3번에서 Guest kernel 의 driver 에 balloon target 이 닿는다. 여기서 방향이 바뀐다. 4번에서 driver 가 확보한 Guest page 를 virtqueue 에 올리고, 5번에서 그것이 다시 VM Boundary 를 넘어 QEMU 쪽 device 로 가고, 6번에서 device 가 Host Memory Management 에 회수 가능 backing 으로 넘긴다. target 을 올릴 때와 내릴 때 Guest 가 쓸 수 있는 memory 가 어느 쪽으로 움직이는지는 본문의 표가 맡는다.",
"source_context": {
"document": "docs/virtualization/final/document.md",
"document_sha256": "181e2b3cc8a45bae81e7e8193d026937c4d586ae4495d3b2c323eb7e6abcfadd",
"document_sha256": "8c4ecc64c8cea9a4450ed7131fdd9cb2048dc092b66cd969f6346ed77887c210",
"anchor": {
"kind": "heading",
"value": "65. virtio-balloon 구조",
"line": 3008
"line": 3021
}
},
"composition": {
@@ -45,12 +45,12 @@
"description": "Guest kernel 쪽 balloon driver. target 을 받아 Guest page 를 확보한다.",
"evidence": [
{
"start_line": 3013,
"end_line": 3015
"start_line": 3026,
"end_line": 3028
},
{
"start_line": 3046,
"end_line": 3046
"start_line": 3059,
"end_line": 3059
}
],
"assumption": false
@@ -64,8 +64,8 @@
"description": "Guest 와 QEMU 가 balloon 요청을 주고받는 공유 큐. VM Boundary 바로 안쪽이다.",
"evidence": [
{
"start_line": 3017,
"end_line": 3017
"start_line": 3030,
"end_line": 3030
}
],
"assumption": false
@@ -79,12 +79,12 @@
"description": "Host 쪽 balloon device. 경계를 넘어온 내용을 Host memory management 로 넘긴다.",
"evidence": [
{
"start_line": 3022,
"end_line": 3022
"start_line": 3035,
"end_line": 3035
},
{
"start_line": 3037,
"end_line": 3041
"start_line": 3050,
"end_line": 3054
}
],
"assumption": false
@@ -98,12 +98,12 @@
"description": "balloon target 을 정하는 쪽이자 회수 기회가 생기는 자리.",
"evidence": [
{
"start_line": 3025,
"end_line": 3025
"start_line": 3038,
"end_line": 3038
},
{
"start_line": 3034,
"end_line": 3037
"start_line": 3047,
"end_line": 3050
}
],
"assumption": false
@@ -120,8 +120,8 @@
"style": "solid",
"evidence": [
{
"start_line": 3034,
"end_line": 3041
"start_line": 3047,
"end_line": 3054
}
],
"assumption": false
@@ -136,12 +136,12 @@
"style": "solid",
"evidence": [
{
"start_line": 3017,
"end_line": 3022
"start_line": 3030,
"end_line": 3035
},
{
"start_line": 3041,
"end_line": 3046
"start_line": 3054,
"end_line": 3059
}
],
"assumption": false
@@ -156,12 +156,12 @@
"style": "solid",
"evidence": [
{
"start_line": 3013,
"end_line": 3017
"start_line": 3026,
"end_line": 3030
},
{
"start_line": 3043,
"end_line": 3046
"start_line": 3056,
"end_line": 3059
}
],
"assumption": false
@@ -176,8 +176,8 @@
"style": "solid",
"evidence": [
{
"start_line": 3046,
"end_line": 3050
"start_line": 3059,
"end_line": 3063
}
],
"assumption": false
@@ -192,8 +192,8 @@
"style": "solid",
"evidence": [
{
"start_line": 3017,
"end_line": 3025
"start_line": 3030,
"end_line": 3038
}
],
"assumption": false
@@ -208,12 +208,12 @@
"style": "solid",
"evidence": [
{
"start_line": 3022,
"end_line": 3028
"start_line": 3035,
"end_line": 3041
},
{
"start_line": 3077,
"end_line": 3079
"start_line": 3090,
"end_line": 3092
}
],
"assumption": false
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -14,11 +14,11 @@
"long_description": "왼쪽에서 오른쪽으로 읽는다. 왼쪽 끝에 Guest 가 있고 VM Exit 이 KVM 으로 제어권을 넘긴다. KVM 은 Exit Reason 을 확인해 두 갈래 중 하나를 고른다. 커널 안에서 처리할 수 있는 Exit 은 Kernel 쪽 KVM 커널 처리로 가고, userspace 처리가 필요한 Exit 만 KVM_RUN 이 반환되면서 Userspace 쪽 QEMU 로 간다. QEMU 는 필요한 장치 동작을 처리한 뒤 KVM_RUN 을 다시 호출한다. 두 갈래는 오른쪽 끝에서 다시 만나며 어느 쪽이든 VM Entry 를 지나 Guest 실행 재개로 돌아온다. 무엇이 Exit 을 부르는지와 Exit 이 잦을 때 무엇을 함께 보는지는 본문의 표와 문단이 맡는다.",
"source_context": {
"document": "docs/virtualization/final/document.md",
"document_sha256": "181e2b3cc8a45bae81e7e8193d026937c4d586ae4495d3b2c323eb7e6abcfadd",
"document_sha256": "8c4ecc64c8cea9a4450ed7131fdd9cb2048dc092b66cd969f6346ed77887c210",
"anchor": {
"kind": "heading",
"value": "9. VM Exit 이후 처리",
"line": 493
"line": 506
}
},
"composition": {
@@ -41,8 +41,8 @@
"description": "VM Exit 이 나기 직전까지 실행 중이던 Guest.",
"evidence": [
{
"start_line": 498,
"end_line": 501
"start_line": 511,
"end_line": 514
}
],
"assumption": false
@@ -60,12 +60,12 @@
"description": "VM Exit 을 받아 Exit Reason 을 확인하고 갈래를 고르는 자리.",
"evidence": [
{
"start_line": 495,
"end_line": 495
"start_line": 508,
"end_line": 508
},
{
"start_line": 502,
"end_line": 504
"start_line": 515,
"end_line": 517
}
],
"assumption": false
@@ -79,12 +79,12 @@
"description": "커널 안에서 끝낼 수 있는 Exit 을 처리하는 갈래. QEMU 까지 올라가지 않는다.",
"evidence": [
{
"start_line": 508,
"end_line": 510
"start_line": 521,
"end_line": 523
},
{
"start_line": 531,
"end_line": 531
"start_line": 544,
"end_line": 544
}
],
"assumption": false,
@@ -106,12 +106,12 @@
"description": "userspace 처리가 필요한 Exit 에서만 개입하는 갈래.",
"evidence": [
{
"start_line": 510,
"end_line": 516
"start_line": 523,
"end_line": 529
},
{
"start_line": 533,
"end_line": 533
"start_line": 546,
"end_line": 546
}
],
"assumption": false
@@ -125,12 +125,12 @@
"description": "두 갈래가 다시 만나는 자리. VM Exit 은 VM 종료가 아니라 여기로 돌아온다.",
"evidence": [
{
"start_line": 521,
"end_line": 524
"start_line": 534,
"end_line": 537
},
{
"start_line": 529,
"end_line": 529
"start_line": 542,
"end_line": 542
}
],
"assumption": false
@@ -146,12 +146,12 @@
"style": "solid",
"evidence": [
{
"start_line": 495,
"end_line": 495
"start_line": 508,
"end_line": 508
},
{
"start_line": 498,
"end_line": 502
"start_line": 511,
"end_line": 515
}
],
"assumption": false
@@ -165,12 +165,12 @@
"style": "solid",
"evidence": [
{
"start_line": 504,
"end_line": 510
"start_line": 517,
"end_line": 523
},
{
"start_line": 531,
"end_line": 531
"start_line": 544,
"end_line": 544
}
],
"assumption": false
@@ -184,12 +184,12 @@
"style": "solid",
"evidence": [
{
"start_line": 508,
"end_line": 512
"start_line": 521,
"end_line": 525
},
{
"start_line": 533,
"end_line": 533
"start_line": 546,
"end_line": 546
}
],
"assumption": false
@@ -203,12 +203,12 @@
"style": "solid",
"evidence": [
{
"start_line": 518,
"end_line": 524
"start_line": 531,
"end_line": 537
},
{
"start_line": 531,
"end_line": 531
"start_line": 544,
"end_line": 544
}
],
"assumption": false
@@ -222,12 +222,12 @@
"style": "solid",
"evidence": [
{
"start_line": 514,
"end_line": 524
"start_line": 527,
"end_line": 537
},
{
"start_line": 533,
"end_line": 533
"start_line": 546,
"end_line": 546
}
],
"assumption": false
@@ -237,4 +237,4 @@
"metadata": {
"rationale": "flow 로 고른 이유는 이 절이 답하는 물음이 「제어권이 어디까지 갔다가 어떻게 돌아오는가」이기 때문이다. Kernel·Userspace 를 경계 상자로 그리면 두 상자가 같은 열에서 겹쳐(check-figure-overlap 15750px²) 어느 노드가 어느 경계인지 오히려 흐려져 노드의 details 로 내렸다. 무엇이 Exit 을 부르는지(§8)와 Exit 이 잦을 때 무엇을 함께 보는지는 본문 표가 이미 맡고 있어 그림에 넣지 않았다."
}
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -4,7 +4,7 @@
"title": "가상 머신 둘과 호스트 프로세스가 하나의 NVMe 로 모인다",
"question": "서로 다른 곳에서 시작한 I/O 는 어디에서 하나로 모여 device 로 나가는가",
"type": "flow",
"direction": "TB",
"direction": "LR",
"audience": [
"가상 머신 위에서 본 저장소 지연의 원인을 가르려는 사람",
"Storage Contention 과 CPU Contention 을 나눠 재려는 사람"
@@ -14,11 +14,11 @@
"long_description": "위쪽에 네 출발점이 나란히 있다. VM1 QEMU 는 WRITE X 와 READ Y, WRITE Z 를 내고 VM2 QEMU 는 READ A 와 WRITE B 를 내며, 그 옆에 호스트의 Nginx 와 다른 프로세스가 있다. 네 갈래가 모두 가운데의 Host Block Layer 하나로 모인다. Host Block Layer 는 그 I/O 가 가상 머신 안에서 시작했는지 호스트 프로세스에서 시작했는지를 본질적으로 구분해 처리하는 계층이 아니라 들어온 것을 모두 Host block request 로 다루고, 그 요청들을 queue 에서 관리해 맨 아래 NVMe 로 dispatch 한다. 여기서 생기는 경쟁이 Storage Contention 이고 CPU 실행 시간을 두고 벌어지는 CPU Contention 과는 다투는 자원이 다르다는 것은 본문 문단이 맡는다.",
"source_context": {
"document": "docs/virtualization/final/document.md",
"document_sha256": "181e2b3cc8a45bae81e7e8193d026937c4d586ae4495d3b2c323eb7e6abcfadd",
"document_sha256": "8c4ecc64c8cea9a4450ed7131fdd9cb2048dc092b66cd969f6346ed77887c210",
"anchor": {
"kind": "heading",
"value": "159. 여러 VM이 하나의 NVMe를 공유하면",
"line": 7026
"line": 7039
}
},
"composition": {
@@ -46,12 +46,12 @@
"description": "가상 머신 하나의 QEMU 프로세스. 게스트의 I/O 가 여기서 호스트 파일 I/O 가 된다.",
"evidence": [
{
"start_line": 7029,
"end_line": 7029
"start_line": 7042,
"end_line": 7042
},
{
"start_line": 7041,
"end_line": 7044
"start_line": 7054,
"end_line": 7057
}
],
"assumption": false
@@ -69,12 +69,12 @@
"description": "다른 가상 머신의 QEMU 프로세스.",
"evidence": [
{
"start_line": 7031,
"end_line": 7031
"start_line": 7044,
"end_line": 7044
},
{
"start_line": 7046,
"end_line": 7048
"start_line": 7059,
"end_line": 7061
}
],
"assumption": false
@@ -88,8 +88,8 @@
"description": "호스트에서 직접 도는 프로세스.",
"evidence": [
{
"start_line": 7033,
"end_line": 7033
"start_line": 7046,
"end_line": 7046
}
],
"assumption": false
@@ -103,8 +103,8 @@
"description": "그 밖의 호스트 프로세스.",
"evidence": [
{
"start_line": 7035,
"end_line": 7038
"start_line": 7048,
"end_line": 7051
}
],
"assumption": false
@@ -123,20 +123,20 @@
"description": "어디서 왔는지 구분하지 않고 모두 Host block request 로 다루는 계층. queue 에서 관리해 device 로 내보낸다.",
"evidence": [
{
"start_line": 7031,
"end_line": 7031
"start_line": 7044,
"end_line": 7044
},
{
"start_line": 7017,
"end_line": 7022
"start_line": 7030,
"end_line": 7035
},
{
"start_line": 7054,
"end_line": 7054
"start_line": 7067,
"end_line": 7067
},
{
"start_line": 7058,
"end_line": 7060
"start_line": 7071,
"end_line": 7073
}
],
"assumption": false
@@ -150,16 +150,16 @@
"description": "네 갈래가 결국 함께 쓰는 하나의 물리 device.",
"evidence": [
{
"start_line": 7031,
"end_line": 7031
"start_line": 7044,
"end_line": 7044
},
{
"start_line": 7019,
"end_line": 7019
"start_line": 7032,
"end_line": 7032
},
{
"start_line": 7066,
"end_line": 7069
"start_line": 7079,
"end_line": 7082
}
],
"assumption": false
@@ -176,8 +176,8 @@
"order": 1,
"evidence": [
{
"start_line": 7029,
"end_line": 7031
"start_line": 7042,
"end_line": 7044
}
],
"assumption": false
@@ -192,8 +192,8 @@
"order": 2,
"evidence": [
{
"start_line": 7031,
"end_line": 7031
"start_line": 7044,
"end_line": 7044
}
],
"assumption": false
@@ -208,8 +208,8 @@
"order": 3,
"evidence": [
{
"start_line": 7031,
"end_line": 7033
"start_line": 7044,
"end_line": 7046
}
],
"assumption": false
@@ -224,8 +224,8 @@
"order": 4,
"evidence": [
{
"start_line": 7031,
"end_line": 7035
"start_line": 7044,
"end_line": 7048
}
],
"assumption": false
@@ -240,12 +240,12 @@
"order": 5,
"evidence": [
{
"start_line": 7031,
"end_line": 7031
"start_line": 7044,
"end_line": 7044
},
{
"start_line": 7054,
"end_line": 7054
"start_line": 7067,
"end_line": 7067
}
],
"assumption": false
@@ -253,6 +253,6 @@
],
"legend": [],
"metadata": {
"rationale": "component-flow 를 고른 이유는 이 절이 답하는 물음이 「여러 곳에서 온 요청이 어디에서 하나가 되는가」이기 때문이고, 그 수렴은 자리로만 보인다. 네 출발점의 화살표에는 라벨을 달지 않았다 — 넷이 같은 Host block request」라는 것이 이 그림의 논지인데 같은 라벨을 네 번 겹쳐 놓으면 라벨끼리 먹힌다. 그래서 그 이름은 모이는 노드의 details 로 올렸다. blk-mq 도 노드가 아니라 details 다 — §160 이 그리는 것은 CPU 넷이 자기 queue 를 갖는 다른 수렴이라 이 그림의 네 출발점과 같은 축에 놓을 수 없다. I/O Scheduler 와 NVMe Driver 는 §161 과 §164 가 대는 사실인데 이 앵커의 준비된 문맥(§158~§160) 밖이라 넣지 않았다. §159 의 「Host Process READ C」도 붙이지 않았다 — 그 프로세스가 Nginx 인지 Host 기타인지 SSOT 가 말하지 않는다."
"rationale": "component-flow 를 고른 이유는 이 절이 답하는 물음이 「여러 곳에서 온 요청이 어디에서 하나가 되는가」이기 때문이다. 현재 renderer에서는 LR로 두어 네 출발점이 Host Block Layer 로 수렴하는 선을 상자 충돌 없이 보이게 한다. 네 출발점의 화살표에는 같은 Host block request 라벨을 반복하지 않고 그 의미를 모이는 노드의 details 로 올렸다. blk-mq 와 I/O Scheduler 의 세부는 다른 절이 맡는다."
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -14,11 +14,11 @@
"long_description": "위에서 아래로 읽는다. 맨 위 PostgreSQL 이 fsync() 를 부르고 그 요구가 Guest Filesystem 으로 내려간다. Guest Block Layer 에서 FLUSH 등의 요청으로 바뀌고, virtio-blk 를 지나 VM 경계를 넘어 QEMU / Backend 로 간다. 거기서 Host Storage Stack 을 지나 맨 아래 Physical Storage 까지 그 의미가 전달되어야 완료가 성립한다. 여섯 번 손이 바뀌는 동안 한 곳이라도 그 의미를 지키지 않으면 게스트가 받은 완료 응답이 거짓이 된다. write() 완료와 writeback 완료, fsync/flush 완료, 전원 장애에도 안전한 durability 가 서로 다르다는 구분은 본문의 코드블록이 맡는다.",
"source_context": {
"document": "docs/virtualization/final/document.md",
"document_sha256": "181e2b3cc8a45bae81e7e8193d026937c4d586ae4495d3b2c323eb7e6abcfadd",
"document_sha256": "8c4ecc64c8cea9a4450ed7131fdd9cb2048dc092b66cd969f6346ed77887c210",
"anchor": {
"kind": "heading",
"value": "150. `fsync()`가 필요한 이유",
"line": 6723
"line": 6736
}
},
"composition": {
@@ -45,12 +45,12 @@
"description": "필요한 시점에 fsync() 를 불러 영속성 경계까지 반영을 요청하는 게스트 프로세스.",
"evidence": [
{
"start_line": 6742,
"end_line": 6742
"start_line": 6755,
"end_line": 6755
},
{
"start_line": 6726,
"end_line": 6734
"start_line": 6739,
"end_line": 6747
}
],
"assumption": false
@@ -64,8 +64,8 @@
"description": "fsync() 요구를 처음 받는 게스트 파일시스템.",
"evidence": [
{
"start_line": 6746,
"end_line": 6746
"start_line": 6759,
"end_line": 6759
}
],
"assumption": false
@@ -79,8 +79,8 @@
"description": "요구를 FLUSH 등의 block 요청으로 바꾸는 계층.",
"evidence": [
{
"start_line": 6749,
"end_line": 6751
"start_line": 6762,
"end_line": 6764
}
],
"assumption": false
@@ -94,8 +94,8 @@
"description": "그 요청을 VM 경계 너머로 옮기는 게스트 driver.",
"evidence": [
{
"start_line": 6753,
"end_line": 6753
"start_line": 6766,
"end_line": 6766
}
],
"assumption": false
@@ -109,12 +109,12 @@
"description": "요청을 받아 호스트 쪽 파일 I/O 나 block I/O 로 잇는 자리.",
"evidence": [
{
"start_line": 6756,
"end_line": 6756
"start_line": 6769,
"end_line": 6769
},
{
"start_line": 6686,
"end_line": 6686
"start_line": 6699,
"end_line": 6699
}
],
"assumption": false
@@ -128,12 +128,12 @@
"description": "호스트 Page Cache 와 파일시스템, Block Layer 가 놓인 구간.",
"evidence": [
{
"start_line": 6759,
"end_line": 6759
"start_line": 6772,
"end_line": 6772
},
{
"start_line": 6688,
"end_line": 6692
"start_line": 6701,
"end_line": 6705
}
],
"assumption": false
@@ -148,12 +148,12 @@
"description": "요구가 여기까지 전달되어야 정전 이후 생존을 말할 수 있다.",
"evidence": [
{
"start_line": 6762,
"end_line": 6765
"start_line": 6775,
"end_line": 6778
},
{
"start_line": 6729,
"end_line": 6729
"start_line": 6742,
"end_line": 6742
}
],
"assumption": false
@@ -170,8 +170,8 @@
"order": 1,
"evidence": [
{
"start_line": 6744,
"end_line": 6746
"start_line": 6757,
"end_line": 6759
}
],
"assumption": false
@@ -186,8 +186,8 @@
"order": 2,
"evidence": [
{
"start_line": 6746,
"end_line": 6749
"start_line": 6759,
"end_line": 6762
}
],
"assumption": false
@@ -202,8 +202,8 @@
"order": 3,
"evidence": [
{
"start_line": 6751,
"end_line": 6753
"start_line": 6764,
"end_line": 6766
}
],
"assumption": false
@@ -218,8 +218,8 @@
"order": 4,
"evidence": [
{
"start_line": 6753,
"end_line": 6756
"start_line": 6766,
"end_line": 6769
}
],
"assumption": false
@@ -234,8 +234,8 @@
"order": 5,
"evidence": [
{
"start_line": 6756,
"end_line": 6759
"start_line": 6769,
"end_line": 6772
}
],
"assumption": false
@@ -250,8 +250,8 @@
"order": 6,
"evidence": [
{
"start_line": 6759,
"end_line": 6765
"start_line": 6772,
"end_line": 6778
}
],
"assumption": false
@@ -10,23 +10,23 @@
## Elements and evidence
- **Boundary: Guest Userspace** (boundary): 게스트 사용자 공간. 저장장치를 직접 다루지 않는다. Evidence: L5807L5809.
- **Boundary: Guest Kernel** (boundary): 게스트 커널 공간. VM Boundary 위쪽 계층이 전부 여기 있다. Evidence: L5815L5833.
- **PostgreSQL / Keycloak** (service): 파일 연산을 요청하는 게스트 프로세스. Evidence: L5807L5809, L5885L5885, L5811L5813.
- **VFS** (service): 열린 파일이 어느 filesystem 구현에 속하는지 보고 연산을 넘기는 공통 계층. Evidence: L5815L5817, L5905L5905.
- **ext4 / XFS** (service): 파일과 디렉터리를 block 공간에 배치하는 게스트 filesystem. Evidence: L5819L5819.
- **Guest Page Cache** (store): 게스트 RAM 에 있는 cache. write() 가 성공해도 데이터는 아직 여기까지만 확정된다. Evidence: L5821L5821.
- **Guest Block Layer** (service): 파일 세계의 요청을 block device 세계의 요청으로 바꾸는 계층. Evidence: L5825L5825.
- **/dev/vda** (device): 게스트 Linux 가 하나의 block device 로 인식하는 이름. Evidence: L5829L5829, L5879L5879.
- **virtio-blk Frontend** (service): 그 가상 block device 를 제어하는 게스트 커널 driver. Evidence: L5831L5831, L5789L5789.
- **virtqueue** (queue): 요청이 실리는 자리. 바로 아래가 VM Boundary 다. Evidence: L5833L5835.
- **Boundary: Guest Userspace** (boundary): 게스트 사용자 공간. 저장장치를 직접 다루지 않는다. Evidence: L5820L5822.
- **Boundary: Guest Kernel** (boundary): 게스트 커널 공간. VM Boundary 위쪽 계층이 전부 여기 있다. Evidence: L5828L5846.
- **PostgreSQL / Keycloak** (service): 파일 연산을 요청하는 게스트 프로세스. Evidence: L5820L5822, L5898L5898, L5824L5826.
- **VFS** (service): 열린 파일이 어느 filesystem 구현에 속하는지 보고 연산을 넘기는 공통 계층. Evidence: L5828L5830, L5918L5918.
- **ext4 / XFS** (service): 파일과 디렉터리를 block 공간에 배치하는 게스트 filesystem. Evidence: L5832L5832.
- **Guest Page Cache** (store): 게스트 RAM 에 있는 cache. write() 가 성공해도 데이터는 아직 여기까지만 확정된다. Evidence: L5834L5834.
- **Guest Block Layer** (service): 파일 세계의 요청을 block device 세계의 요청으로 바꾸는 계층. Evidence: L5838L5838.
- **/dev/vda** (device): 게스트 Linux 가 하나의 block device 로 인식하는 이름. Evidence: L5842L5842, L5892L5892.
- **virtio-blk Frontend** (service): 그 가상 block device 를 제어하는 게스트 커널 driver. Evidence: L5844L5844, L5802L5802.
- **virtqueue** (queue): 요청이 실리는 자리. 바로 아래가 VM Boundary 다. Evidence: L5846L5848.
## Relationships
- **PostgreSQL / Keycloak → VFS:** data. Evidence: L5811L5813, L5890L5890.
- **VFS → ext4 / XFS:** data. Evidence: L5817L5819.
- **ext4 / XFS → Guest Page Cache:** data. Evidence: L5819L5821.
- **Guest Page Cache → Guest Block Layer:** writeback. Evidence: L5823L5825.
- **Guest Block Layer → /dev/vda:** WRITE / FLUSH. Evidence: L5827L5829.
- **/dev/vda → virtio-blk Frontend:** data. Evidence: L5829L5831.
- **virtio-blk Frontend → virtqueue:** Virtio block request. Evidence: L5831L5833.
- **PostgreSQL / Keycloak → VFS:** data. Evidence: L5824L5826, L5903L5903.
- **VFS → ext4 / XFS:** data. Evidence: L5830L5832.
- **ext4 / XFS → Guest Page Cache:** data. Evidence: L5832L5834.
- **Guest Page Cache → Guest Block Layer:** writeback. Evidence: L5836L5838.
- **Guest Block Layer → /dev/vda:** WRITE / FLUSH. Evidence: L5840L5842.
- **/dev/vda → virtio-blk Frontend:** data. Evidence: L5842L5844.
- **virtio-blk Frontend → virtqueue:** Virtio block request. Evidence: L5844L5846.
@@ -0,0 +1,30 @@
digraph techviz {
graph [rankdir=TB, splines=ortho, nodesep=0.55, ranksep=0.85];
node [fontname=Helvetica, fontsize=11, margin="0.18,0.12", style="rounded,filled", fillcolor=white, color="#2d4357", penwidth=1.5];
edge [fontname=Helvetica, fontsize=10, color="#364b5f", penwidth=1.4, arrowsize=0.75];
subgraph cluster_0 {
label="Guest Userspace";
style="rounded,dashed";
color="#66788a";
n0 [label="PostgreSQL / Keycloak", shape=box, style="rounded,filled"];
}
subgraph cluster_1 {
label="Guest Kernel";
style="rounded,dashed";
color="#66788a";
n1 [label="VFS", shape=box, style="rounded,filled"];
n2 [label="ext4 / XFS", shape=box, style="rounded,filled"];
n3 [label="Guest Page Cache", shape=box, style="rounded,filled"];
n4 [label="Guest Block Layer", shape=box, style="rounded,filled"];
n5 [label="/dev/vda", shape=box, style="rounded,filled"];
n6 [label="virtio-blk Frontend", shape=box, style="rounded,filled"];
n7 [label="virtqueue", shape=oval, style="rounded,filled"];
}
n0 -> n1 [label="", style=solid];
n1 -> n2 [label="", style=solid];
n2 -> n3 [label="", style=solid];
n3 -> n4 [label="writeback", style=solid];
n4 -> n5 [label="WRITE / FLUSH", style=solid];
n5 -> n6 [label="", style=solid];
n6 -> n7 [label="Virtio block request", style=solid];
}
@@ -1,73 +1,73 @@
<?xml version="1.0" encoding="UTF-8"?>
<mxfile host="app.diagrams.net" modified="2026-07-23T00:00:00.000Z" agent="techviz-harness" version="24.7.17" type="device">
<diagram id="guest-block-io-to-virtqueue" name="게스트의 write() 가 virtqueue 에 실리기까지">
<mxGraphModel dx="680" dy="1360" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1360" math="0" shadow="0">
<mxGraphModel dx="680" dy="2501" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="2501" math="0" shadow="0">
<root>
<mxCell id="0"/>
<mxCell id="1" parent="0"/>
<mxCell id="g_guest-userspace" value="Guest Userspace" style="swimlane;html=1;rounded=1;startSize=30;horizontal=1;dashed=1;strokeWidth=1.5;fillColor=#f7f9fb;strokeColor=#66788a;fontStyle=1;fontSize=13;" vertex="1" parent="1">
<mxGeometry x="40.0" y="35.0" width="241.0" height="160.0" as="geometry"/>
<mxGeometry x="35.0" y="99.0" width="241.0" height="160.0" as="geometry"/>
</mxCell>
<mxCell id="g_guest-kernel" value="Guest Kernel" style="swimlane;html=1;rounded=1;startSize=30;horizontal=1;dashed=1;strokeWidth=1.5;fillColor=#f7f9fb;strokeColor=#66788a;fontStyle=1;fontSize=13;" vertex="1" parent="1">
<mxGeometry x="47.0" y="219.0" width="227.0" height="1096.0" as="geometry"/>
<mxGeometry x="47.0" y="440.0" width="227.0" height="2016.0" as="geometry"/>
</mxCell>
<mxCell id="n_app" value="PostgreSQL / Keycloak&lt;br/&gt;read / write&lt;br/&gt;fsync / sync" tooltip="파일 연산을 요청하는 게스트 프로세스. | Evidence: L5807-L5809, L5885-L5885, L5811-L5813" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="70.0" y="81.0" width="181.0" height="88.0" as="geometry"/>
<mxCell id="n_app" value="PostgreSQL / Keycloak&lt;br/&gt;read / write&lt;br/&gt;fsync / sync" tooltip="파일 연산을 요청하는 게스트 프로세스. | Evidence: L5820-L5822, L5898-L5898, L5824-L5826" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="65.0" y="145.0" width="181.0" height="88.0" as="geometry"/>
</mxCell>
<mxCell id="n_vfs" value="VFS" tooltip="열린 파일이 어느 filesystem 구현에 속하는지 보고 연산을 넘기는 공통 계층. | Evidence: L5815-L5817, L5905-L5905" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="85.5" y="265.0" width="150.0" height="64.0" as="geometry"/>
<mxCell id="n_vfs" value="VFS" tooltip="열린 파일이 어느 filesystem 구현에 속하는지 보고 연산을 넘기는 공통 계층. | Evidence: L5828-L5830, L5918-L5918" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="77.0" y="486.0" width="150.0" height="64.0" as="geometry"/>
</mxCell>
<mxCell id="n_fs" value="ext4 / XFS" tooltip="파일과 디렉터리를 block 공간에 배치하는 게스트 filesystem. | Evidence: L5819-L5819" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="85.5" y="425.0" width="150.0" height="64.0" as="geometry"/>
<mxCell id="n_fs" value="ext4 / XFS" tooltip="파일과 디렉터리를 block 공간에 배치하는 게스트 filesystem. | Evidence: L5832-L5832" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="77.0" y="796.0" width="150.0" height="64.0" as="geometry"/>
</mxCell>
<mxCell id="n_page-cache" value="Guest Page Cache" tooltip="게스트 RAM 에 있는 cache. write() 가 성공해도 데이터는 아직 여기까지만 확정된다. | Evidence: L5821-L5821" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="85.5" y="585.0" width="150.0" height="64.0" as="geometry"/>
<mxCell id="n_page-cache" value="Guest Page Cache" tooltip="게스트 RAM 에 있는 cache. write() 가 성공해도 데이터는 아직 여기까지만 확정된다. | Evidence: L5834-L5834" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="77.0" y="1106.0" width="150.0" height="64.0" as="geometry"/>
</mxCell>
<mxCell id="n_block-layer" value="Guest Block Layer" tooltip="파일 세계의 요청을 block device 세계의 요청으로 바꾸는 계층. | Evidence: L5825-L5825" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="84.0" y="745.0" width="153.0" height="64.0" as="geometry"/>
<mxCell id="n_block-layer" value="Guest Block Layer" tooltip="파일 세계의 요청을 block device 세계의 요청으로 바꾸는 계층. | Evidence: L5838-L5838" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="77.0" y="1416.0" width="153.0" height="64.0" as="geometry"/>
</mxCell>
<mxCell id="n_vda" value="/dev/vda" tooltip="게스트 Linux 가 하나의 block device 로 인식하는 이름. | Evidence: L5829-L5829, L5879-L5879" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="85.5" y="905.0" width="150.0" height="64.0" as="geometry"/>
<mxCell id="n_vda" value="/dev/vda" tooltip="게스트 Linux 가 하나의 block device 로 인식하는 이름. | Evidence: L5842-L5842, L5892-L5892" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="77.0" y="1729.0" width="150.0" height="64.0" as="geometry"/>
</mxCell>
<mxCell id="n_virtio-blk" value="virtio-blk Frontend" tooltip="그 가상 block device 를 제어하는 게스트 커널 driver. | Evidence: L5831-L5831, L5789-L5789" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="77.0" y="1065.0" width="167.0" height="64.0" as="geometry"/>
<mxCell id="n_virtio-blk" value="virtio-blk Frontend" tooltip="그 가상 block device 를 제어하는 게스트 커널 driver. | Evidence: L5844-L5844, L5802-L5802" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="77.0" y="2039.0" width="167.0" height="64.0" as="geometry"/>
</mxCell>
<mxCell id="n_virtqueue" value="virtqueue" tooltip="요청이 실리는 자리. 바로 아래가 VM Boundary 다. | Evidence: L5833-L5835" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;strokeColor=#2563eb;strokeWidth=2;" vertex="1" parent="1">
<mxGeometry x="85.5" y="1225.0" width="150.0" height="64.0" as="geometry"/>
<mxCell id="n_virtqueue" value="virtqueue" tooltip="요청이 실리는 자리. 바로 아래가 VM Boundary 다. | Evidence: L5846-L5848" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;strokeColor=#2563eb;strokeWidth=2;" vertex="1" parent="1">
<mxGeometry x="77.0" y="2366.0" width="150.0" height="64.0" as="geometry"/>
</mxCell>
<mxCell id="e_app-to-vfs" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_app" target="n_vfs">
<mxGeometry relative="1" as="geometry">
<mxPoint x="184.5" y="217.0" as="offset"/>
<mxPoint x="153.8" y="331.5" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="e_vfs-to-fs" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_vfs" target="n_fs">
<mxGeometry relative="1" as="geometry">
<mxPoint x="184.5" y="377.0" as="offset"/>
<mxPoint x="176.0" y="673.0" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="e_fs-to-page-cache" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_fs" target="n_page-cache">
<mxGeometry relative="1" as="geometry">
<mxPoint x="184.5" y="537.0" as="offset"/>
<mxPoint x="176.0" y="983.0" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="e_page-cache-to-block-layer" value="writeback" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_page-cache" target="n_block-layer">
<mxGeometry relative="1" as="geometry">
<mxPoint x="184.5" y="697.0" as="offset"/>
<mxPoint x="152.8" y="1265.0" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="e_block-layer-to-vda" value="WRITE / FLUSH" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_block-layer" target="n_vda">
<mxGeometry relative="1" as="geometry">
<mxPoint x="184.5" y="857.0" as="offset"/>
<mxPoint x="152.8" y="1576.5" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="e_vda-to-virtio-blk" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_vda" target="n_virtio-blk">
<mxGeometry relative="1" as="geometry">
<mxPoint x="184.5" y="1017.0" as="offset"/>
<mxPoint x="156.2" y="1888.0" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="e_virtio-blk-to-virtqueue" value="Virtio block request" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_virtio-blk" target="n_virtqueue">
<mxGeometry relative="1" as="geometry">
<mxPoint x="184.5" y="1177.0" as="offset"/>
<mxPoint x="156.2" y="2206.5" as="offset"/>
</mxGeometry>
</mxCell>
</root>
@@ -6,8 +6,8 @@
{
"id": "group-guest-userspace",
"type": "rectangle",
"x": 40.0,
"y": 35.0,
"x": 35.0,
"y": 99.0,
"width": 241.0,
"height": 160.0,
"angle": 0,
@@ -36,8 +36,8 @@
{
"id": "group-label-guest-userspace",
"type": "text",
"x": 56.0,
"y": 41.0,
"x": 51.0,
"y": 105.0,
"width": 135,
"height": 24,
"angle": 0,
@@ -76,9 +76,9 @@
"id": "group-guest-kernel",
"type": "rectangle",
"x": 47.0,
"y": 219.0,
"y": 440.0,
"width": 227.0,
"height": 1096.0,
"height": 2016.0,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "#f8f9fa",
@@ -106,7 +106,7 @@
"id": "group-label-guest-kernel",
"type": "text",
"x": 63.0,
"y": 225.0,
"y": 446.0,
"width": 108,
"height": 24,
"angle": 0,
@@ -144,10 +144,10 @@
{
"id": "edge-app-to-vfs",
"type": "arrow",
"x": 160.5,
"y": 169.0,
"width": 0.0,
"height": 96.0,
"x": 152.0,
"y": 233.0,
"width": 3.5,
"height": 253.0,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
@@ -170,20 +170,20 @@
"locked": false,
"points": [
[
0.0,
3.5,
0.0
],
[
0.0,
48.0
3.5,
126.5
],
[
0.0,
48.0
126.5
],
[
0.0,
96.0
253.0
]
],
"lastCommittedPoint": null,
@@ -204,10 +204,10 @@
{
"id": "edge-vfs-to-fs",
"type": "arrow",
"x": 160.5,
"y": 329.0,
"x": 152.0,
"y": 550.0,
"width": 0.0,
"height": 96.0,
"height": 246.0,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
@@ -235,15 +235,15 @@
],
[
0.0,
48.0
123.0
],
[
0.0,
48.0
123.0
],
[
0.0,
96.0
246.0
]
],
"lastCommittedPoint": null,
@@ -264,10 +264,10 @@
{
"id": "edge-fs-to-page-cache",
"type": "arrow",
"x": 160.5,
"y": 489.0,
"x": 152.0,
"y": 860.0,
"width": 0.0,
"height": 96.0,
"height": 246.0,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
@@ -295,15 +295,15 @@
],
[
0.0,
48.0
123.0
],
[
0.0,
48.0
123.0
],
[
0.0,
96.0
246.0
]
],
"lastCommittedPoint": null,
@@ -324,10 +324,10 @@
{
"id": "edge-page-cache-to-block-layer",
"type": "arrow",
"x": 160.5,
"y": 649.0,
"width": 0.0,
"height": 96.0,
"x": 152.0,
"y": 1170.0,
"width": 1.5,
"height": 246.0,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
@@ -355,15 +355,15 @@
],
[
0.0,
48.0
123.0
],
[
0.0,
48.0
1.5,
123.0
],
[
0.0,
96.0
1.5,
246.0
]
],
"lastCommittedPoint": null,
@@ -384,8 +384,8 @@
{
"id": "edge-label-page-cache-to-block-layer",
"type": "text",
"x": 139.5,
"y": 685.0,
"x": 107.75,
"y": 1253.0,
"width": 90,
"height": 24,
"angle": 0,
@@ -423,10 +423,10 @@
{
"id": "edge-block-layer-to-vda",
"type": "arrow",
"x": 160.5,
"y": 809.0,
"width": 0.0,
"height": 96.0,
"x": 152.0,
"y": 1480.0,
"width": 1.5,
"height": 249.0,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
@@ -449,20 +449,20 @@
"locked": false,
"points": [
[
0.0,
1.5,
0.0
],
[
0.0,
48.0
1.5,
124.5
],
[
0.0,
48.0
124.5
],
[
0.0,
96.0
249.0
]
],
"lastCommittedPoint": null,
@@ -483,8 +483,8 @@
{
"id": "edge-label-block-layer-to-vda",
"type": "text",
"x": 132.5,
"y": 845.0,
"x": 100.75,
"y": 1564.5,
"width": 104,
"height": 24,
"angle": 0,
@@ -522,10 +522,10 @@
{
"id": "edge-vda-to-virtio-blk",
"type": "arrow",
"x": 160.5,
"y": 969.0,
"width": 0.0,
"height": 96.0,
"x": 152.0,
"y": 1793.0,
"width": 8.5,
"height": 246.0,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
@@ -553,15 +553,15 @@
],
[
0.0,
48.0
123.0
],
[
0.0,
48.0
8.5,
123.0
],
[
0.0,
96.0
8.5,
246.0
]
],
"lastCommittedPoint": null,
@@ -582,10 +582,10 @@
{
"id": "edge-virtio-blk-to-virtqueue",
"type": "arrow",
"x": 160.5,
"y": 1129.0,
"width": 0.0,
"height": 96.0,
"x": 152.0,
"y": 2103.0,
"width": 8.5,
"height": 263.0,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
@@ -608,20 +608,20 @@
"locked": false,
"points": [
[
0.0,
8.5,
0.0
],
[
0.0,
48.0
8.5,
131.5
],
[
0.0,
48.0
131.5
],
[
0.0,
96.0
263.0
]
],
"lastCommittedPoint": null,
@@ -642,8 +642,8 @@
{
"id": "edge-label-virtio-blk-to-virtqueue",
"type": "text",
"x": 104.5,
"y": 1165.0,
"x": 76.25,
"y": 2194.5,
"width": 160,
"height": 24,
"angle": 0,
@@ -681,8 +681,8 @@
{
"id": "node-app",
"type": "rectangle",
"x": 70.0,
"y": 81.0,
"x": 65.0,
"y": 145.0,
"width": 181.0,
"height": 88.0,
"angle": 0,
@@ -711,8 +711,8 @@
{
"id": "node-label-app",
"type": "text",
"x": 80.0,
"y": 91.0,
"x": 75.0,
"y": 155.0,
"width": 161.0,
"height": 68.0,
"angle": 0,
@@ -750,8 +750,8 @@
{
"id": "node-vfs",
"type": "rectangle",
"x": 85.5,
"y": 265.0,
"x": 77.0,
"y": 486.0,
"width": 150.0,
"height": 64.0,
"angle": 0,
@@ -780,8 +780,8 @@
{
"id": "node-label-vfs",
"type": "text",
"x": 95.5,
"y": 275.0,
"x": 87.0,
"y": 496.0,
"width": 130.0,
"height": 44.0,
"angle": 0,
@@ -819,8 +819,8 @@
{
"id": "node-fs",
"type": "rectangle",
"x": 85.5,
"y": 425.0,
"x": 77.0,
"y": 796.0,
"width": 150.0,
"height": 64.0,
"angle": 0,
@@ -849,8 +849,8 @@
{
"id": "node-label-fs",
"type": "text",
"x": 95.5,
"y": 435.0,
"x": 87.0,
"y": 806.0,
"width": 130.0,
"height": 44.0,
"angle": 0,
@@ -888,8 +888,8 @@
{
"id": "node-page-cache",
"type": "rectangle",
"x": 85.5,
"y": 585.0,
"x": 77.0,
"y": 1106.0,
"width": 150.0,
"height": 64.0,
"angle": 0,
@@ -918,8 +918,8 @@
{
"id": "node-label-page-cache",
"type": "text",
"x": 95.5,
"y": 595.0,
"x": 87.0,
"y": 1116.0,
"width": 130.0,
"height": 44.0,
"angle": 0,
@@ -957,8 +957,8 @@
{
"id": "node-block-layer",
"type": "rectangle",
"x": 84.0,
"y": 745.0,
"x": 77.0,
"y": 1416.0,
"width": 153.0,
"height": 64.0,
"angle": 0,
@@ -987,8 +987,8 @@
{
"id": "node-label-block-layer",
"type": "text",
"x": 94.0,
"y": 755.0,
"x": 87.0,
"y": 1426.0,
"width": 133.0,
"height": 44.0,
"angle": 0,
@@ -1026,8 +1026,8 @@
{
"id": "node-vda",
"type": "rectangle",
"x": 85.5,
"y": 905.0,
"x": 77.0,
"y": 1729.0,
"width": 150.0,
"height": 64.0,
"angle": 0,
@@ -1056,8 +1056,8 @@
{
"id": "node-label-vda",
"type": "text",
"x": 95.5,
"y": 915.0,
"x": 87.0,
"y": 1739.0,
"width": 130.0,
"height": 44.0,
"angle": 0,
@@ -1096,7 +1096,7 @@
"id": "node-virtio-blk",
"type": "rectangle",
"x": 77.0,
"y": 1065.0,
"y": 2039.0,
"width": 167.0,
"height": 64.0,
"angle": 0,
@@ -1126,7 +1126,7 @@
"id": "node-label-virtio-blk",
"type": "text",
"x": 87.0,
"y": 1075.0,
"y": 2049.0,
"width": 147.0,
"height": 44.0,
"angle": 0,
@@ -1164,8 +1164,8 @@
{
"id": "node-virtqueue",
"type": "rectangle",
"x": 85.5,
"y": 1225.0,
"x": 77.0,
"y": 2366.0,
"width": 150.0,
"height": 64.0,
"angle": 0,
@@ -1194,8 +1194,8 @@
{
"id": "node-label-virtqueue",
"type": "text",
"x": 95.5,
"y": 1235.0,
"x": 87.0,
"y": 2376.0,
"width": 130.0,
"height": 44.0,
"angle": 0,
@@ -2,30 +2,41 @@
"harness_version": "0.2.0",
"spec_id": "guest-block-io-to-virtqueue",
"spec_version": "1.1",
"spec_sha256": "f41eedadcb8a6841cf981bea9e0a9e50a3df836df3cad528b4245a8d42e83915",
"spec_sha256": "3c2c2a9e053d25d2277226299d48db325ea1d1af836f6bacf4c23ea3672c38f4",
"source_context": {
"document": "docs/virtualization/final/document.md",
"document_sha256": "181e2b3cc8a45bae81e7e8193d026937c4d586ae4495d3b2c323eb7e6abcfadd",
"document_sha256": "8c4ecc64c8cea9a4450ed7131fdd9cb2048dc092b66cd969f6346ed77887c210",
"anchor": {
"kind": "heading",
"value": "128. 전체 구조",
"line": 5804
"line": 5817
}
},
"outputs": [
"guest-block-io-to-virtqueue.svg",
"guest-block-io-to-virtqueue.drawio",
"guest-block-io-to-virtqueue.mmd",
"guest-block-io-to-virtqueue.d2",
"guest-block-io-to-virtqueue.dot",
"guest-block-io-to-virtqueue.drawio",
"guest-block-io-to-virtqueue.excalidraw",
"guest-block-io-to-virtqueue.alt.md"
],
"lint_issue_count": 5,
"lint_issue_count": 6,
"assumption_count": 0,
"assumptions_allowed": false,
"composition_profile": "component-flow",
"reference_ids": [
"payment-event-flow"
],
"diagram_only": true
"diagram_only": true,
"source_spec_file_sha256": "bdb3a2b7f3434ecf75e7dceca28611002766e42aabd96a9c8f521c29049466d9",
"output_sha256": {
"guest-block-io-to-virtqueue.svg": "5b7be8f61cc3a04b3b490184f7fe4b1cb2b6f62e48fb6d1dd116b4a8253fdc4a",
"guest-block-io-to-virtqueue.mmd": "cf6e5a7a029e614d0e0f1f295663f9f733e9fbeeee002b16c545129257f7565d",
"guest-block-io-to-virtqueue.d2": "92c96a0b016a4b2b8f3d03ca528574f3c75156588ff66610c47bd7b938181740",
"guest-block-io-to-virtqueue.dot": "3f3605120200df313b5476cb4a3b2121aa5b8b3c05052e9e822403b5275f7077",
"guest-block-io-to-virtqueue.drawio": "c649f476bed58b768777cbac3d773a5a3ba3d7cfa95a9bb3e598defb670c94b9",
"guest-block-io-to-virtqueue.excalidraw": "84f0416128f3a3ac1ceca2a0a2e3cb5b0ebbb74bf522c47ae2c0b558c2fe24c8",
"guest-block-io-to-virtqueue.alt.md": "0d45d2ce74a7e2245e046f950fa0bbdd4e037bdb095444e95f14e762d8f4bc53"
}
}
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="680" height="1360" viewBox="0 0 680 1360" role="img" aria-labelledby="diagram-title diagram-description">
<svg xmlns="http://www.w3.org/2000/svg" width="680" height="2501" viewBox="0 0 680 2501" role="img" aria-labelledby="diagram-title diagram-description">
<title id="diagram-title">게스트의 write() 가 virtqueue 에 실리기까지</title>
<desc id="diagram-description">위에서 아래로 읽는다. 맨 위 Guest Userspace 구역에 PostgreSQL / Keycloak 이 있고 write() 가 게스트 커널로 내려간다. Guest Kernel 구역 안에서 VFS 가 먼저 받고, ext4 / XFS 가 파일을 block 공간에 배치하고, Guest Page Cache 가 그 내용을 dirty page 로 받아 두었다가 writeback 으로 Guest Block Layer 에 내려보낸다. Guest Block Layer 는 그것을 WRITE 나 FLUSH 요청으로 바꿔 /dev/vda 에 걸고, virtio-blk Frontend 가 그 요청을 virtqueue 에 게시한다. virtqueue 아래의 VM Boundary 부터는 QEMU 와 호스트 계층이라 이 그림에서 잘랐다. 완료가 같은 virtqueue 로 되돌아오는 역방향은 SSOT 의 다른 절이 대는 사실이라 이 그림에 넣지 않았고 본문 문단이 맡는다.</desc>
<metadata>{&quot;techviz&quot;:{&quot;spec_version&quot;:&quot;1.1&quot;,&quot;id&quot;:&quot;guest-block-io-to-virtqueue&quot;,&quot;profile&quot;:&quot;component-flow&quot;},&quot;source_context&quot;:{&quot;document&quot;:&quot;docs/virtualization/final/document.md&quot;,&quot;document_sha256&quot;:&quot;181e2b3cc8a45bae81e7e8193d026937c4d586ae4495d3b2c323eb7e6abcfadd&quot;,&quot;anchor&quot;:{&quot;kind&quot;:&quot;heading&quot;,&quot;value&quot;:&quot;128. 전체 구조&quot;,&quot;line&quot;:5804}},&quot;evidence_policy&quot;:&quot;Each factual element cites source lines or is marked assumption.&quot;,&quot;diagram_only&quot;:true}</metadata>
<metadata>{&quot;techviz&quot;:{&quot;spec_version&quot;:&quot;1.1&quot;,&quot;id&quot;:&quot;guest-block-io-to-virtqueue&quot;,&quot;profile&quot;:&quot;component-flow&quot;},&quot;source_context&quot;:{&quot;document&quot;:&quot;docs/virtualization/final/document.md&quot;,&quot;document_sha256&quot;:&quot;8c4ecc64c8cea9a4450ed7131fdd9cb2048dc092b66cd969f6346ed77887c210&quot;,&quot;anchor&quot;:{&quot;kind&quot;:&quot;heading&quot;,&quot;value&quot;:&quot;128. 전체 구조&quot;,&quot;line&quot;:5817}},&quot;evidence_policy&quot;:&quot;Each factual element cites source lines or is marked assumption.&quot;,&quot;diagram_only&quot;:true}</metadata>
<defs>
<marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M 0 0 L 10 5 L 0 10 z" />
@@ -49,59 +49,59 @@
.timeline-detail { font-size: 11px; fill: #4b5563; text-anchor: middle; }
</style>
</defs>
<rect class="canvas" width="680" height="1360" />
<rect class="group-box" x="40.0" y="35.0" width="241.0" height="160.0" rx="8" />
<rect class="group-label-bg" x="54.0" y="25.0" width="127.0" height="22" />
<text class="group-label" x="64.0" y="40.0">Guest Userspace</text>
<rect class="group-box" x="47.0" y="219.0" width="227.0" height="1096.0" rx="8" />
<rect class="group-label-bg" x="61.0" y="209.0" width="106.0" height="22" />
<text class="group-label" x="71.0" y="224.0">Guest Kernel</text>
<polyline class="edge kind-data style-solid emphasis-normal" points="160.5,169.0 160.5,217.0 160.5,217.0 160.5,265.0" data-evidence="5811-5813,5890-5890" />
<polyline class="edge kind-data style-solid emphasis-normal" points="160.5,329.0 160.5,377.0 160.5,377.0 160.5,425.0" data-evidence="5817-5819" />
<polyline class="edge kind-data style-solid emphasis-normal" points="160.5,489.0 160.5,537.0 160.5,537.0 160.5,585.0" data-evidence="5819-5821" />
<polyline class="edge kind-data style-solid emphasis-normal" points="160.5,649.0 160.5,697.0 160.5,697.0 160.5,745.0" data-evidence="5823-5825" />
<rect class="edge-label-bg" x="145.3" y="683.0" width="78.3" height="22" rx="3" />
<text class="edge-label" x="184.5" y="698.0">writeback</text>
<polyline class="edge kind-data style-solid emphasis-normal" points="160.5,809.0 160.5,857.0 160.5,857.0 160.5,905.0" data-evidence="5827-5829" />
<rect class="edge-label-bg" x="131.9" y="843.0" width="105.1" height="22" rx="3" />
<text class="edge-label" x="184.5" y="858.0">WRITE / FLUSH</text>
<polyline class="edge kind-data style-solid emphasis-normal" points="160.5,969.0 160.5,1017.0 160.5,1017.0 160.5,1065.0" data-evidence="5829-5831" />
<polyline class="edge kind-data style-solid emphasis-normal" points="160.5,1129.0 160.5,1177.0 160.5,1177.0 160.5,1225.0" data-evidence="5831-5833" />
<rect class="edge-label-bg" x="108.5" y="1163.0" width="152.0" height="22" rx="3" />
<text class="edge-label" x="184.5" y="1178.0">Virtio block request</text>
<rect class="canvas" width="680" height="2501" />
<rect class="group-box" x="35.0" y="99.0" width="241.0" height="160.0" rx="8" />
<rect class="group-label-bg" x="49.0" y="89.0" width="127.0" height="22" />
<text class="group-label" x="59.0" y="104.0">Guest Userspace</text>
<rect class="group-box" x="47.0" y="440.0" width="227.0" height="2016.0" rx="8" />
<rect class="group-label-bg" x="61.0" y="430.0" width="106.0" height="22" />
<text class="group-label" x="71.0" y="445.0">Guest Kernel</text>
<polyline class="edge kind-data style-solid emphasis-normal" points="155.5,233.0 155.5,359.5 152.0,359.5 152.0,486.0" data-evidence="5824-5826,5903-5903" />
<polyline class="edge kind-data style-solid emphasis-normal" points="152.0,550.0 152.0,673.0 152.0,673.0 152.0,796.0" data-evidence="5830-5832" />
<polyline class="edge kind-data style-solid emphasis-normal" points="152.0,860.0 152.0,983.0 152.0,983.0 152.0,1106.0" data-evidence="5832-5834" />
<polyline class="edge kind-data style-solid emphasis-normal" points="152.0,1170.0 152.0,1293.0 153.5,1293.0 153.5,1416.0" data-evidence="5836-5838" />
<rect class="edge-label-bg" x="113.6" y="1251.0" width="78.3" height="22" rx="3" />
<text class="edge-label" x="152.8" y="1266.0">writeback</text>
<polyline class="edge kind-data style-solid emphasis-normal" points="153.5,1480.0 153.5,1604.5 152.0,1604.5 152.0,1729.0" data-evidence="5840-5842" />
<rect class="edge-label-bg" x="100.2" y="1562.5" width="105.1" height="22" rx="3" />
<text class="edge-label" x="152.8" y="1577.5">WRITE / FLUSH</text>
<polyline class="edge kind-data style-solid emphasis-normal" points="152.0,1793.0 152.0,1916.0 160.5,1916.0 160.5,2039.0" data-evidence="5842-5844" />
<polyline class="edge kind-data style-solid emphasis-normal" points="160.5,2103.0 160.5,2234.5 152.0,2234.5 152.0,2366.0" data-evidence="5844-5846" />
<rect class="edge-label-bg" x="80.2" y="2192.5" width="152.0" height="22" rx="3" />
<text class="edge-label" x="156.2" y="2207.5">Virtio block request</text>
<g id="node-app">
<rect class="node-shape kind-service emphasis-normal role-source" data-evidence="5807-5809,5885-5885,5811-5813" x="70.0" y="81.0" width="181.0" height="88.0" rx="7" />
<text class="node-label" x="160.5" y="108.0">PostgreSQL / Keycloak</text>
<line class="node-detail-divider" x1="84.0" y1="129.0" x2="237.0" y2="129.0" />
<text class="node-detail" x="86.0" y="146.0">read / write</text>
<text class="node-detail" x="86.0" y="162.0">fsync / sync</text>
<rect class="node-shape kind-service emphasis-normal role-source" data-evidence="5820-5822,5898-5898,5824-5826" x="65.0" y="145.0" width="181.0" height="88.0" rx="7" />
<text class="node-label" x="155.5" y="172.0">PostgreSQL / Keycloak</text>
<line class="node-detail-divider" x1="79.0" y1="193.0" x2="232.0" y2="193.0" />
<text class="node-detail" x="81.0" y="210.0">read / write</text>
<text class="node-detail" x="81.0" y="226.0">fsync / sync</text>
</g>
<g id="node-vfs">
<rect class="node-shape kind-service emphasis-normal role-service" data-evidence="5815-5817,5905-5905" x="85.5" y="265.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="160.5" y="295.0">VFS</text>
<rect class="node-shape kind-service emphasis-normal role-service" data-evidence="5828-5830,5918-5918" x="77.0" y="486.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="152.0" y="516.0">VFS</text>
</g>
<g id="node-fs">
<rect class="node-shape kind-service emphasis-normal role-service" data-evidence="5819-5819" x="85.5" y="425.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="160.5" y="455.0">ext4 / XFS</text>
<rect class="node-shape kind-service emphasis-normal role-service" data-evidence="5832-5832" x="77.0" y="796.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="152.0" y="826.0">ext4 / XFS</text>
</g>
<g id="node-page-cache">
<rect class="node-shape kind-store emphasis-normal role-store" data-evidence="5821-5821" x="85.5" y="585.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="160.5" y="615.0">Guest Page Cache</text>
<rect class="node-shape kind-store emphasis-normal role-store" data-evidence="5834-5834" x="77.0" y="1106.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="152.0" y="1136.0">Guest Page Cache</text>
</g>
<g id="node-block-layer">
<rect class="node-shape kind-service emphasis-normal role-service" data-evidence="5825-5825" x="84.0" y="745.0" width="153.0" height="64.0" rx="7" />
<text class="node-label" x="160.5" y="775.0">Guest Block Layer</text>
<rect class="node-shape kind-service emphasis-normal role-service" data-evidence="5838-5838" x="77.0" y="1416.0" width="153.0" height="64.0" rx="7" />
<text class="node-label" x="153.5" y="1446.0">Guest Block Layer</text>
</g>
<g id="node-vda">
<rect class="node-shape kind-device emphasis-normal role-service" data-evidence="5829-5829,5879-5879" x="85.5" y="905.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="160.5" y="935.0">/dev/vda</text>
<rect class="node-shape kind-device emphasis-normal role-service" data-evidence="5842-5842,5892-5892" x="77.0" y="1729.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="152.0" y="1759.0">/dev/vda</text>
</g>
<g id="node-virtio-blk">
<rect class="node-shape kind-service emphasis-normal role-service" data-evidence="5831-5831,5789-5789" x="77.0" y="1065.0" width="167.0" height="64.0" rx="7" />
<text class="node-label" x="160.5" y="1095.0">virtio-blk Frontend</text>
<rect class="node-shape kind-service emphasis-normal role-service" data-evidence="5844-5844,5802-5802" x="77.0" y="2039.0" width="167.0" height="64.0" rx="7" />
<text class="node-label" x="160.5" y="2069.0">virtio-blk Frontend</text>
</g>
<g id="node-virtqueue">
<rect class="node-shape kind-queue emphasis-primary role-queue" data-evidence="5833-5835" x="85.5" y="1225.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="160.5" y="1255.0">virtqueue</text>
<rect class="node-shape kind-queue emphasis-primary role-queue" data-evidence="5846-5848" x="77.0" y="2366.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="152.0" y="2396.0">virtqueue</text>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 8.9 KiB

After

Width:  |  Height:  |  Size: 8.9 KiB

@@ -10,21 +10,21 @@ Guest 구역 안에서 Keycloak · PostgreSQL 의 GVA 가 Guest TLB 와 Guest Pa
## Elements and evidence
- **Boundary: Guest** (boundary): VM Boundary 안쪽. Guest Userspace 와 Guest Kernel 이 여기 있고 GVA 가 GPA 가 되는 첫 번째 변환이 여기서 끝난다. Evidence: L4034L4053.
- **Boundary: Host RAM** (boundary): 변환이 끝난 주소가 닿는 실제 서버 메모리. Evidence: L4069L4079.
- **Keycloak · PostgreSQL** (actor): Guest 안에서 메모리를 읽는 프로세스. 여기서 GVA 가 출발한다. Evidence: L4034L4038.
- **Guest TLB** (component): 최근 변환 결과를 담아 두는 CPU 안의 캐시. Evidence: L4043L4045.
- **Guest Page Table** (component): GVA 를 GPA 로 바꾸는 첫 번째 변환 표. Evidence: L4048L4053.
- **EPT** (component): VM Boundary 바깥에서 GPA 를 HPA 로 바꾸는 두 번째 변환 표. KVM 과 CPU 가 맡는다. Evidence: L4055L4067.
- **Host Physical Memory** (store): HPA 가 가리키는 실제 서버 메모리. Evidence: L4069L4072.
- **NUMA Node 0** (store): physical page 가 놓일 수 있는 한쪽 NUMA node. Evidence: L4073L4075.
- **NUMA Node 1** (store): physical page 가 놓일 수 있는 다른 쪽 NUMA node. Evidence: L4073L4075.
- **Boundary: Guest** (boundary): VM Boundary 안쪽. Guest Userspace 와 Guest Kernel 이 여기 있고 GVA 가 GPA 가 되는 첫 번째 변환이 여기서 끝난다. Evidence: L4047L4066.
- **Boundary: Host RAM** (boundary): 변환이 끝난 주소가 닿는 실제 서버 메모리. Evidence: L4082L4092.
- **Keycloak · PostgreSQL** (actor): Guest 안에서 메모리를 읽는 프로세스. 여기서 GVA 가 출발한다. Evidence: L4047L4051.
- **Guest TLB** (component): 최근 변환 결과를 담아 두는 CPU 안의 캐시. Evidence: L4056L4058.
- **Guest Page Table** (component): GVA 를 GPA 로 바꾸는 첫 번째 변환 표. Evidence: L4061L4066.
- **EPT** (component): VM Boundary 바깥에서 GPA 를 HPA 로 바꾸는 두 번째 변환 표. KVM 과 CPU 가 맡는다. Evidence: L4068L4080.
- **Host Physical Memory** (store): HPA 가 가리키는 실제 서버 메모리. Evidence: L4082L4085.
- **NUMA Node 0** (store): physical page 가 놓일 수 있는 한쪽 NUMA node. Evidence: L4086L4088.
- **NUMA Node 1** (store): physical page 가 놓일 수 있는 다른 쪽 NUMA node. Evidence: L4086L4088.
## Relationships
- **EPT → Host Physical Memory:** HPA. Evidence: L4062L4071.
- **Host Physical Memory → NUMA Node 0:** Physical Page. Evidence: L4071L4075.
- **Host Physical Memory → NUMA Node 1:** Physical Page. Evidence: L4071L4075.
- **Guest Page Table → EPT:** VM Boundary. Evidence: L4053L4062.
- **Keycloak · PostgreSQL → Guest TLB:** GVA. Evidence: L4036L4043.
- **Guest TLB → Guest Page Table:** TLB Miss. Evidence: L4043L4048.
- **EPT → Host Physical Memory:** HPA. Evidence: L4075L4084.
- **Host Physical Memory → NUMA Node 0:** Physical Page. Evidence: L4084L4088.
- **Host Physical Memory → NUMA Node 1:** Physical Page. Evidence: L4084L4088.
- **Guest Page Table → EPT:** VM Boundary. Evidence: L4066L4075.
- **Keycloak · PostgreSQL → Guest TLB:** GVA. Evidence: L4049L4056.
- **Guest TLB → Guest Page Table:** TLB Miss. Evidence: L4056L4061.
@@ -1,6 +1,6 @@
# Guest 의 메모리 접근 한 번은 이름이 두 번 바뀌며 네 계층을 지난다
# Question: Guest 프로세스가 읽은 주소는 어느 계층을 차례로 지나 어떤 물리 메모리에 닿는가
direction: down
direction: right
g0: "Guest" {
n0: "Keycloak · PostgreSQL" {
shape: person
@@ -0,0 +1,28 @@
digraph techviz {
graph [rankdir=LR, splines=ortho, nodesep=0.55, ranksep=0.85];
node [fontname=Helvetica, fontsize=11, margin="0.18,0.12", style="rounded,filled", fillcolor=white, color="#2d4357", penwidth=1.5];
edge [fontname=Helvetica, fontsize=10, color="#364b5f", penwidth=1.4, arrowsize=0.75];
subgraph cluster_0 {
label="Guest";
style="rounded,dashed";
color="#66788a";
n0 [label="Keycloak · PostgreSQL", shape=box, style="rounded,dashed,filled"];
n1 [label="Guest TLB", shape=box, style="rounded,filled"];
n2 [label="Guest Page Table", shape=box, style="rounded,filled"];
}
subgraph cluster_1 {
label="Host RAM";
style="rounded,dashed";
color="#66788a";
n4 [label="Host Physical Memory", shape=box, style="rounded,filled"];
n5 [label="NUMA Node 0", shape=box, style="rounded,filled"];
n6 [label="NUMA Node 1", shape=box, style="rounded,filled"];
}
n3 [label="EPT", shape=box, style="rounded,filled"];
n0 -> n1 [label="GVA", style=solid];
n1 -> n2 [label="TLB Miss", style=solid];
n2 -> n3 [label="VM Boundary", style=solid];
n3 -> n4 [label="HPA", style=solid];
n4 -> n5 [label="Physical Page", style=solid];
n4 -> n6 [label="Physical Page", style=solid];
}
@@ -1,65 +1,65 @@
<?xml version="1.0" encoding="UTF-8"?>
<mxfile host="app.diagrams.net" modified="2026-07-23T00:00:00.000Z" agent="techviz-harness" version="24.7.17" type="device">
<diagram id="guest-memory-address-translation-path" name="Guest 의 메모리 접근 한 번은 이름이 두 번 바뀌며 네 계층을 지난다">
<mxGraphModel dx="680" dy="1078" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
<mxGraphModel dx="1900" dy="352" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1900" pageHeight="1169" math="0" shadow="0">
<root>
<mxCell id="0"/>
<mxCell id="1" parent="0"/>
<mxCell id="g_guest" value="Guest" style="swimlane;html=1;rounded=1;startSize=30;horizontal=1;dashed=1;strokeWidth=1.5;fillColor=#f7f9fb;strokeColor=#66788a;fontStyle=1;fontSize=13;" vertex="1" parent="1">
<mxGeometry x="129.5" y="35.0" width="241.0" height="494.0" as="geometry"/>
<mxGeometry x="40.0" y="91.0" width="861.0" height="160.0" as="geometry"/>
</mxCell>
<mxCell id="g_host-ram" value="Host RAM" style="swimlane;html=1;rounded=1;startSize=30;horizontal=1;dashed=1;strokeWidth=1.5;fillColor=#f7f9fb;strokeColor=#66788a;fontStyle=1;fontSize=13;" vertex="1" parent="1">
<mxGeometry x="40.0" y="737.0" width="420.0" height="296.0" as="geometry"/>
<mxGeometry x="1311.0" y="35.0" width="544.0" height="272.0" as="geometry"/>
</mxCell>
<mxCell id="n_guest-process" value="Keycloak · PostgreSQL&lt;br/&gt;Guest Userspace" tooltip="Guest 안에서 메모리를 읽는 프로세스. 여기서 GVA 가 출발한다. | Evidence: L4034-L4038" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="159.5" y="81.0" width="181.0" height="71.0" as="geometry"/>
<mxCell id="n_guest-process" value="Keycloak · PostgreSQL&lt;br/&gt;Guest Userspace" tooltip="Guest 안에서 메모리를 읽는 프로세스. 여기서 GVA 가 출발한다. | Evidence: L4047-L4051" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="70.0" y="145.5" width="181.0" height="71.0" as="geometry"/>
</mxCell>
<mxCell id="n_guest-tlb" value="Guest TLB&lt;br/&gt;Guest Kernel" tooltip="최근 변환 결과를 담아 두는 CPU 안의 캐시. | Evidence: L4043-L4045" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="175.0" y="248.0" width="150.0" height="71.0" as="geometry"/>
<mxCell id="n_guest-tlb" value="Guest TLB&lt;br/&gt;Guest Kernel" tooltip="최근 변환 결과를 담아 두는 CPU 안의 캐시. | Evidence: L4056-L4058" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="411.0" y="145.5" width="150.0" height="71.0" as="geometry"/>
</mxCell>
<mxCell id="n_guest-page-table" value="Guest Page Table&lt;br/&gt;Guest Kernel&lt;br/&gt;GVA → GPA" tooltip="GVA 를 GPA 로 바꾸는 첫 번째 변환 표. | Evidence: L4048-L4053" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="175.0" y="415.0" width="150.0" height="88.0" as="geometry"/>
<mxCell id="n_guest-page-table" value="Guest Page Table&lt;br/&gt;Guest Kernel&lt;br/&gt;GVA → GPA" tooltip="GVA 를 GPA 로 바꾸는 첫 번째 변환 표. | Evidence: L4061-L4066" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="721.0" y="137.0" width="150.0" height="88.0" as="geometry"/>
</mxCell>
<mxCell id="n_ept" value="EPT&lt;br/&gt;KVM / CPU&lt;br/&gt;GPA → HPA" tooltip="VM Boundary 바깥에서 GPA 를 HPA 로 바꾸는 두 번째 변환 표. KVM 과 CPU 가 맡는다. | Evidence: L4055-L4067" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;strokeColor=#2563eb;strokeWidth=2;" vertex="1" parent="1">
<mxGeometry x="175.0" y="599.0" width="150.0" height="88.0" as="geometry"/>
<mxCell id="n_ept" value="EPT&lt;br/&gt;KVM / CPU&lt;br/&gt;GPA → HPA" tooltip="VM Boundary 바깥에서 GPA 를 HPA 로 바꾸는 두 번째 변환 표. KVM 과 CPU 가 맡는다. | Evidence: L4068-L4080" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;strokeColor=#2563eb;strokeWidth=2;" vertex="1" parent="1">
<mxGeometry x="1031.0" y="137.0" width="150.0" height="88.0" as="geometry"/>
</mxCell>
<mxCell id="n_host-physical-memory" value="Host Physical Memory" tooltip="HPA 가 가리키는 실제 서버 메모리. | Evidence: L4069-L4072" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="163.0" y="783.0" width="174.0" height="64.0" as="geometry"/>
<mxCell id="n_host-physical-memory" value="Host Physical Memory" tooltip="HPA 가 가리키는 실제 서버 메모리. | Evidence: L4082-L4085" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="1341.0" y="149.0" width="174.0" height="64.0" as="geometry"/>
</mxCell>
<mxCell id="n_numa-node-0" value="NUMA Node 0" tooltip="physical page 가 놓일 수 있는 한쪽 NUMA node. | Evidence: L4073-L4075" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="70.0" y="943.0" width="150.0" height="64.0" as="geometry"/>
<mxCell id="n_numa-node-0" value="NUMA Node 0" tooltip="physical page 가 놓일 수 있는 한쪽 NUMA node. | Evidence: L4086-L4088" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="1675.0" y="81.0" width="150.0" height="64.0" as="geometry"/>
</mxCell>
<mxCell id="n_numa-node-1" value="NUMA Node 1" tooltip="physical page 가 놓일 수 있는 다른 쪽 NUMA node. | Evidence: L4073-L4075" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="280.0" y="943.0" width="150.0" height="64.0" as="geometry"/>
<mxCell id="n_numa-node-1" value="NUMA Node 1" tooltip="physical page 가 놓일 수 있는 다른 쪽 NUMA node. | Evidence: L4086-L4088" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="1675.0" y="217.0" width="150.0" height="64.0" as="geometry"/>
</mxCell>
<mxCell id="e_ept-to-host-memory" value="HPA" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_ept" target="n_host-physical-memory">
<mxGeometry relative="1" as="geometry">
<mxPoint x="274.0" y="735.0" as="offset"/>
<mxPoint x="1261.0" y="153.0" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="e_host-memory-to-node-0" value="Physical Page" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_host-physical-memory" target="n_numa-node-0">
<mxGeometry relative="1" as="geometry">
<mxPoint x="193.0" y="867.0" as="offset"/>
<mxPoint x="1619.0" y="142.5" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="e_host-memory-to-node-1" value="Physical Page" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_host-physical-memory" target="n_numa-node-1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="307.0" y="867.0" as="offset"/>
<mxPoint x="1619.0" y="219.5" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="e_page-table-to-ept" value="VM Boundary" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_guest-page-table" target="n_ept">
<mxGeometry relative="1" as="geometry">
<mxPoint x="274.0" y="551.0" as="offset"/>
<mxPoint x="951.0" y="153.0" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="e_process-to-tlb" value="GVA" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_guest-process" target="n_guest-tlb">
<mxGeometry relative="1" as="geometry">
<mxPoint x="274.0" y="200.0" as="offset"/>
<mxPoint x="331.0" y="153.0" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="e_tlb-to-page-table" value="TLB Miss" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_guest-tlb" target="n_guest-page-table">
<mxGeometry relative="1" as="geometry">
<mxPoint x="274.0" y="367.0" as="offset"/>
<mxPoint x="641.0" y="153.0" as="offset"/>
</mxGeometry>
</mxCell>
</root>
@@ -6,10 +6,10 @@
{
"id": "group-guest",
"type": "rectangle",
"x": 129.5,
"y": 35.0,
"width": 241.0,
"height": 494.0,
"x": 40.0,
"y": 91.0,
"width": 861.0,
"height": 160.0,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "#f8f9fa",
@@ -36,8 +36,8 @@
{
"id": "group-label-guest",
"type": "text",
"x": 145.5,
"y": 41.0,
"x": 56.0,
"y": 97.0,
"width": 100,
"height": 24,
"angle": 0,
@@ -75,10 +75,10 @@
{
"id": "group-host-ram",
"type": "rectangle",
"x": 40.0,
"y": 737.0,
"width": 420.0,
"height": 296.0,
"x": 1311.0,
"y": 35.0,
"width": 544.0,
"height": 272.0,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "#f8f9fa",
@@ -105,8 +105,8 @@
{
"id": "group-label-host-ram",
"type": "text",
"x": 56.0,
"y": 743.0,
"x": 1327.0,
"y": 41.0,
"width": 100,
"height": 24,
"angle": 0,
@@ -144,10 +144,10 @@
{
"id": "edge-ept-to-host-memory",
"type": "arrow",
"x": 250.0,
"y": 687.0,
"width": 0.0,
"height": 96.0,
"x": 1181.0,
"y": 181.0,
"width": 160.0,
"height": 0.0,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
@@ -174,16 +174,16 @@
0.0
],
[
0.0,
48.0
80.0,
0.0
],
[
0.0,
48.0
80.0,
0.0
],
[
0.0,
96.0
160.0,
0.0
]
],
"lastCommittedPoint": null,
@@ -204,8 +204,8 @@
{
"id": "edge-label-ept-to-host-memory",
"type": "text",
"x": 229.0,
"y": 723.0,
"x": 1216.0,
"y": 141.0,
"width": 90,
"height": 24,
"angle": 0,
@@ -243,10 +243,10 @@
{
"id": "edge-host-memory-to-node-0",
"type": "arrow",
"x": 145.0,
"y": 847.0,
"width": 96.0,
"height": 96.0,
"x": 1515.0,
"y": 113.0,
"width": 160.0,
"height": 59.0,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
@@ -269,20 +269,20 @@
"locked": false,
"points": [
[
96.0,
0.0,
59.0
],
[
80.0,
59.0
],
[
80.0,
0.0
],
[
96.0,
48.0
],
[
0.0,
48.0
],
[
0.0,
96.0
160.0,
0.0
]
],
"lastCommittedPoint": null,
@@ -303,8 +303,8 @@
{
"id": "edge-label-host-memory-to-node-0",
"type": "text",
"x": 141.0,
"y": 855.0,
"x": 1567.0,
"y": 130.5,
"width": 104,
"height": 24,
"angle": 0,
@@ -342,10 +342,10 @@
{
"id": "edge-host-memory-to-node-1",
"type": "arrow",
"x": 259.0,
"y": 847.0,
"width": 96.0,
"height": 96.0,
"x": 1515.0,
"y": 190.0,
"width": 160.0,
"height": 59.0,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
@@ -372,16 +372,16 @@
0.0
],
[
0.0,
48.0
80.0,
0.0
],
[
96.0,
48.0
80.0,
59.0
],
[
96.0,
96.0
160.0,
59.0
]
],
"lastCommittedPoint": null,
@@ -402,8 +402,8 @@
{
"id": "edge-label-host-memory-to-node-1",
"type": "text",
"x": 255.0,
"y": 855.0,
"x": 1567.0,
"y": 207.5,
"width": 104,
"height": 24,
"angle": 0,
@@ -441,10 +441,10 @@
{
"id": "edge-page-table-to-ept",
"type": "arrow",
"x": 250.0,
"y": 503.0,
"width": 0.0,
"height": 96.0,
"x": 871.0,
"y": 181.0,
"width": 160.0,
"height": 0.0,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
@@ -471,16 +471,16 @@
0.0
],
[
0.0,
48.0
80.0,
0.0
],
[
0.0,
48.0
80.0,
0.0
],
[
0.0,
96.0
160.0,
0.0
]
],
"lastCommittedPoint": null,
@@ -501,8 +501,8 @@
{
"id": "edge-label-page-table-to-ept",
"type": "text",
"x": 229.0,
"y": 539.0,
"x": 906.0,
"y": 141.0,
"width": 90,
"height": 24,
"angle": 0,
@@ -540,10 +540,10 @@
{
"id": "edge-process-to-tlb",
"type": "arrow",
"x": 250.0,
"y": 152.0,
"width": 0.0,
"height": 96.0,
"x": 251.0,
"y": 181.0,
"width": 160.0,
"height": 0.0,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
@@ -570,16 +570,16 @@
0.0
],
[
0.0,
48.0
80.0,
0.0
],
[
0.0,
48.0
80.0,
0.0
],
[
0.0,
96.0
160.0,
0.0
]
],
"lastCommittedPoint": null,
@@ -600,8 +600,8 @@
{
"id": "edge-label-process-to-tlb",
"type": "text",
"x": 229.0,
"y": 188.0,
"x": 286.0,
"y": 141.0,
"width": 90,
"height": 24,
"angle": 0,
@@ -639,10 +639,10 @@
{
"id": "edge-tlb-to-page-table",
"type": "arrow",
"x": 250.0,
"y": 319.0,
"width": 0.0,
"height": 96.0,
"x": 561.0,
"y": 181.0,
"width": 160.0,
"height": 0.0,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
@@ -669,16 +669,16 @@
0.0
],
[
0.0,
48.0
80.0,
0.0
],
[
0.0,
48.0
80.0,
0.0
],
[
0.0,
96.0
160.0,
0.0
]
],
"lastCommittedPoint": null,
@@ -699,8 +699,8 @@
{
"id": "edge-label-tlb-to-page-table",
"type": "text",
"x": 229.0,
"y": 355.0,
"x": 596.0,
"y": 141.0,
"width": 90,
"height": 24,
"angle": 0,
@@ -738,8 +738,8 @@
{
"id": "node-guest-process",
"type": "rectangle",
"x": 159.5,
"y": 81.0,
"x": 70.0,
"y": 145.5,
"width": 181.0,
"height": 71.0,
"angle": 0,
@@ -768,8 +768,8 @@
{
"id": "node-label-guest-process",
"type": "text",
"x": 169.5,
"y": 91.0,
"x": 80.0,
"y": 155.5,
"width": 161.0,
"height": 51.0,
"angle": 0,
@@ -807,8 +807,8 @@
{
"id": "node-guest-tlb",
"type": "rectangle",
"x": 175.0,
"y": 248.0,
"x": 411.0,
"y": 145.5,
"width": 150.0,
"height": 71.0,
"angle": 0,
@@ -837,8 +837,8 @@
{
"id": "node-label-guest-tlb",
"type": "text",
"x": 185.0,
"y": 258.0,
"x": 421.0,
"y": 155.5,
"width": 130.0,
"height": 51.0,
"angle": 0,
@@ -876,8 +876,8 @@
{
"id": "node-guest-page-table",
"type": "rectangle",
"x": 175.0,
"y": 415.0,
"x": 721.0,
"y": 137.0,
"width": 150.0,
"height": 88.0,
"angle": 0,
@@ -906,8 +906,8 @@
{
"id": "node-label-guest-page-table",
"type": "text",
"x": 185.0,
"y": 425.0,
"x": 731.0,
"y": 147.0,
"width": 130.0,
"height": 68.0,
"angle": 0,
@@ -945,8 +945,8 @@
{
"id": "node-ept",
"type": "rectangle",
"x": 175.0,
"y": 599.0,
"x": 1031.0,
"y": 137.0,
"width": 150.0,
"height": 88.0,
"angle": 0,
@@ -975,8 +975,8 @@
{
"id": "node-label-ept",
"type": "text",
"x": 185.0,
"y": 609.0,
"x": 1041.0,
"y": 147.0,
"width": 130.0,
"height": 68.0,
"angle": 0,
@@ -1014,8 +1014,8 @@
{
"id": "node-host-physical-memory",
"type": "rectangle",
"x": 163.0,
"y": 783.0,
"x": 1341.0,
"y": 149.0,
"width": 174.0,
"height": 64.0,
"angle": 0,
@@ -1044,8 +1044,8 @@
{
"id": "node-label-host-physical-memory",
"type": "text",
"x": 173.0,
"y": 793.0,
"x": 1351.0,
"y": 159.0,
"width": 154.0,
"height": 44.0,
"angle": 0,
@@ -1083,8 +1083,8 @@
{
"id": "node-numa-node-0",
"type": "rectangle",
"x": 70.0,
"y": 943.0,
"x": 1675.0,
"y": 81.0,
"width": 150.0,
"height": 64.0,
"angle": 0,
@@ -1113,8 +1113,8 @@
{
"id": "node-label-numa-node-0",
"type": "text",
"x": 80.0,
"y": 953.0,
"x": 1685.0,
"y": 91.0,
"width": 130.0,
"height": 44.0,
"angle": 0,
@@ -1152,8 +1152,8 @@
{
"id": "node-numa-node-1",
"type": "rectangle",
"x": 280.0,
"y": 943.0,
"x": 1675.0,
"y": 217.0,
"width": 150.0,
"height": 64.0,
"angle": 0,
@@ -1182,8 +1182,8 @@
{
"id": "node-label-numa-node-1",
"type": "text",
"x": 290.0,
"y": 953.0,
"x": 1685.0,
"y": 227.0,
"width": 130.0,
"height": 44.0,
"angle": 0,
@@ -2,30 +2,41 @@
"harness_version": "0.2.0",
"spec_id": "guest-memory-address-translation-path",
"spec_version": "1.1",
"spec_sha256": "9db86cb8c5f81b1c5886877e1efd3ae4d4174d839fb29cffd5f8ae102508f2f3",
"spec_sha256": "e30f27d5ec8b8a67d37f29129f6d6988f5713e779480a2be0ae5417d8d3c143b",
"source_context": {
"document": "docs/virtualization/final/document.md",
"document_sha256": "181e2b3cc8a45bae81e7e8193d026937c4d586ae4495d3b2c323eb7e6abcfadd",
"document_sha256": "8c4ecc64c8cea9a4450ed7131fdd9cb2048dc092b66cd969f6346ed77887c210",
"anchor": {
"kind": "heading",
"value": "87. 최종 기준 그림",
"line": 4029
"line": 4042
}
},
"outputs": [
"guest-memory-address-translation-path.svg",
"guest-memory-address-translation-path.drawio",
"guest-memory-address-translation-path.mmd",
"guest-memory-address-translation-path.d2",
"guest-memory-address-translation-path.dot",
"guest-memory-address-translation-path.drawio",
"guest-memory-address-translation-path.excalidraw",
"guest-memory-address-translation-path.alt.md"
],
"lint_issue_count": 0,
"lint_issue_count": 2,
"assumption_count": 0,
"assumptions_allowed": false,
"composition_profile": "component-flow",
"reference_ids": [
"payment-event-flow"
],
"diagram_only": true
"diagram_only": true,
"source_spec_file_sha256": "f61398e1b5a1282f1904e0674b0bc1ecf6eccea307053bd176c40eff3580d445",
"output_sha256": {
"guest-memory-address-translation-path.svg": "713f7a4c829230fb80d915946f08f7af1b190336ad8b27222885f8e010099c35",
"guest-memory-address-translation-path.mmd": "84e22ae6bfaf94daaf62c61270b00aba3160c87a6084b9033a77adac7e3efc90",
"guest-memory-address-translation-path.d2": "060efab06f41aa65df54cb1f19ad2fb0a9e05c4fadfb896cfc6cac5a260571e2",
"guest-memory-address-translation-path.dot": "e3e795e40a234ff708981b04c15d4924eaa8cd71779b81c24633b52bfecd9ba0",
"guest-memory-address-translation-path.drawio": "584d77a986cf771141d4246570b52f4590a79e840831ad2924585494ecd2f5b6",
"guest-memory-address-translation-path.excalidraw": "dcc857856e9a2cf2abde5812668c1987710f8655230a4a8e8299ffa74298a6ff",
"guest-memory-address-translation-path.alt.md": "ea2406106c239086672848f73b69658275fff87d34399e9837f570173c0d7a26"
}
}
@@ -1,6 +1,6 @@
%% Guest 의 메모리 접근 한 번은 이름이 두 번 바뀌며 네 계층을 지난다
%% question: Guest 프로세스가 읽은 주소는 어느 계층을 차례로 지나 어떤 물리 메모리에 닿는가
flowchart TB
flowchart LR
subgraph g_guest["Guest"]
n0(["Keycloak · PostgreSQL"])
n1["Guest TLB"]
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="680" height="1078" viewBox="0 0 680 1078" role="img" aria-labelledby="diagram-title diagram-description">
<svg xmlns="http://www.w3.org/2000/svg" width="1900" height="352" viewBox="0 0 1900 352" role="img" aria-labelledby="diagram-title diagram-description">
<title id="diagram-title">Guest 의 메모리 접근 한 번은 이름이 두 번 바뀌며 네 계층을 지난다</title>
<desc id="diagram-description">위에서 아래로 읽는다. 맨 위 Guest 구역 안에 Keycloak · PostgreSQL 이 있고 그 접근은 GVA 로 Guest TLB 에 들어간다. TLB 에 변환이 없으면 TLB Miss 로 Guest Page Table 을 조회하고 거기서 GVA 가 GPA 가 된다. 그 GPA 는 VM Boundary 를 넘어 Guest 구역 밖의 EPT 로 가고 EPT 가 GPA 를 HPA 로 바꾼다. HPA 는 Host RAM 구역의 Host Physical Memory 에 닿고, 그 physical page 는 맨 아래에서 NUMA Node 0 또는 NUMA Node 1 가운데 한쪽에 놓인다. 상자 안 작은 글씨는 그 단계가 어느 계층인지와 무엇을 무엇으로 바꾸는지를 적은 것이다. 어느 단계에서 접근이 완료되지 못할 때 무엇이 일어나는지와 QEMU 가 이 경로를 어떻게 준비하는지는 본문의 문단과 표가 맡는다.</desc>
<metadata>{&quot;techviz&quot;:{&quot;spec_version&quot;:&quot;1.1&quot;,&quot;id&quot;:&quot;guest-memory-address-translation-path&quot;,&quot;profile&quot;:&quot;component-flow&quot;},&quot;source_context&quot;:{&quot;document&quot;:&quot;docs/virtualization/final/document.md&quot;,&quot;document_sha256&quot;:&quot;181e2b3cc8a45bae81e7e8193d026937c4d586ae4495d3b2c323eb7e6abcfadd&quot;,&quot;anchor&quot;:{&quot;kind&quot;:&quot;heading&quot;,&quot;value&quot;:&quot;87. 최종 기준 그림&quot;,&quot;line&quot;:4029}},&quot;evidence_policy&quot;:&quot;Each factual element cites source lines or is marked assumption.&quot;,&quot;diagram_only&quot;:true}</metadata>
<metadata>{&quot;techviz&quot;:{&quot;spec_version&quot;:&quot;1.1&quot;,&quot;id&quot;:&quot;guest-memory-address-translation-path&quot;,&quot;profile&quot;:&quot;component-flow&quot;},&quot;source_context&quot;:{&quot;document&quot;:&quot;docs/virtualization/final/document.md&quot;,&quot;document_sha256&quot;:&quot;8c4ecc64c8cea9a4450ed7131fdd9cb2048dc092b66cd969f6346ed77887c210&quot;,&quot;anchor&quot;:{&quot;kind&quot;:&quot;heading&quot;,&quot;value&quot;:&quot;87. 최종 기준 그림&quot;,&quot;line&quot;:4042}},&quot;evidence_policy&quot;:&quot;Each factual element cites source lines or is marked assumption.&quot;,&quot;diagram_only&quot;:true}</metadata>
<defs>
<marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M 0 0 L 10 5 L 0 10 z" />
@@ -49,67 +49,67 @@
.timeline-detail { font-size: 11px; fill: #4b5563; text-anchor: middle; }
</style>
</defs>
<rect class="canvas" width="680" height="1078" />
<rect class="group-box" x="129.5" y="35.0" width="241.0" height="494.0" rx="8" />
<rect class="group-label-bg" x="143.5" y="25.0" width="90.0" height="22" />
<text class="group-label" x="153.5" y="40.0">Guest</text>
<rect class="group-box" x="40.0" y="737.0" width="420.0" height="296.0" rx="8" />
<rect class="group-label-bg" x="54.0" y="727.0" width="90.0" height="22" />
<text class="group-label" x="64.0" y="742.0">Host RAM</text>
<polyline class="edge kind-data style-solid emphasis-normal" points="250.0,687.0 250.0,735.0 250.0,735.0 250.0,783.0" data-evidence="4062-4071" />
<rect class="edge-label-bg" x="252.0" y="721.0" width="44.0" height="22" rx="3" />
<text class="edge-label" x="274.0" y="736.0">HPA</text>
<polyline class="edge kind-data style-solid emphasis-normal" points="241.0,847.0 241.0,895.0 145.0,895.0 145.0,943.0" data-evidence="4071-4075" />
<rect class="edge-label-bg" x="140.4" y="853.0" width="105.1" height="22" rx="3" />
<text class="edge-label" x="193.0" y="868.0">Physical Page</text>
<polyline class="edge kind-data style-solid emphasis-normal" points="259.0,847.0 259.0,895.0 355.0,895.0 355.0,943.0" data-evidence="4071-4075" />
<rect class="edge-label-bg" x="254.4" y="853.0" width="105.1" height="22" rx="3" />
<text class="edge-label" x="307.0" y="868.0">Physical Page</text>
<polyline class="edge kind-control style-solid emphasis-normal" points="250.0,503.0 250.0,551.0 250.0,551.0 250.0,599.0" data-evidence="4053-4062" />
<rect class="edge-label-bg" x="228.2" y="537.0" width="91.7" height="22" rx="3" />
<text class="edge-label" x="274.0" y="552.0">VM Boundary</text>
<polyline class="edge kind-data style-solid emphasis-normal" points="250.0,152.0 250.0,200.0 250.0,200.0 250.0,248.0" data-evidence="4036-4043" />
<rect class="edge-label-bg" x="252.0" y="186.0" width="44.0" height="22" rx="3" />
<text class="edge-label" x="274.0" y="201.0">GVA</text>
<polyline class="edge kind-control style-solid emphasis-normal" points="250.0,319.0 250.0,367.0 250.0,367.0 250.0,415.0" data-evidence="4043-4048" />
<rect class="edge-label-bg" x="238.2" y="353.0" width="71.6" height="22" rx="3" />
<text class="edge-label" x="274.0" y="368.0">TLB Miss</text>
<rect class="canvas" width="1900" height="352" />
<rect class="group-box" x="40.0" y="91.0" width="861.0" height="160.0" rx="8" />
<rect class="group-label-bg" x="54.0" y="81.0" width="90.0" height="22" />
<text class="group-label" x="64.0" y="96.0">Guest</text>
<rect class="group-box" x="1311.0" y="35.0" width="544.0" height="272.0" rx="8" />
<rect class="group-label-bg" x="1325.0" y="25.0" width="90.0" height="22" />
<text class="group-label" x="1335.0" y="40.0">Host RAM</text>
<polyline class="edge kind-data style-solid emphasis-normal" points="1181.0,181.0 1261.0,181.0 1261.0,181.0 1341.0,181.0" data-evidence="4075-4084" />
<rect class="edge-label-bg" x="1239.0" y="139.0" width="44.0" height="22" rx="3" />
<text class="edge-label" x="1261.0" y="154.0">HPA</text>
<polyline class="edge kind-data style-solid emphasis-normal" points="1515.0,172.0 1595.0,172.0 1595.0,113.0 1675.0,113.0" data-evidence="4084-4088" />
<rect class="edge-label-bg" x="1566.5" y="128.5" width="105.1" height="22" rx="3" />
<text class="edge-label" x="1619.0" y="143.5">Physical Page</text>
<polyline class="edge kind-data style-solid emphasis-normal" points="1515.0,190.0 1595.0,190.0 1595.0,249.0 1675.0,249.0" data-evidence="4084-4088" />
<rect class="edge-label-bg" x="1566.5" y="205.5" width="105.1" height="22" rx="3" />
<text class="edge-label" x="1619.0" y="220.5">Physical Page</text>
<polyline class="edge kind-control style-solid emphasis-normal" points="871.0,181.0 951.0,181.0 951.0,181.0 1031.0,181.0" data-evidence="4066-4075" />
<rect class="edge-label-bg" x="905.1" y="139.0" width="91.7" height="22" rx="3" />
<text class="edge-label" x="951.0" y="154.0">VM Boundary</text>
<polyline class="edge kind-data style-solid emphasis-normal" points="251.0,181.0 331.0,181.0 331.0,181.0 411.0,181.0" data-evidence="4049-4056" />
<rect class="edge-label-bg" x="309.0" y="139.0" width="44.0" height="22" rx="3" />
<text class="edge-label" x="331.0" y="154.0">GVA</text>
<polyline class="edge kind-control style-solid emphasis-normal" points="561.0,181.0 641.0,181.0 641.0,181.0 721.0,181.0" data-evidence="4056-4061" />
<rect class="edge-label-bg" x="605.2" y="139.0" width="71.6" height="22" rx="3" />
<text class="edge-label" x="641.0" y="154.0">TLB Miss</text>
<g id="node-guest-process">
<rect class="node-shape kind-actor emphasis-normal role-source" data-evidence="4034-4038" x="159.5" y="81.0" width="181.0" height="71.0" rx="7" />
<text class="node-label" x="250.0" y="108.0">Keycloak · PostgreSQL</text>
<line class="node-detail-divider" x1="173.5" y1="129.0" x2="326.5" y2="129.0" />
<text class="node-detail" x="175.5" y="146.0">Guest Userspace</text>
<rect class="node-shape kind-actor emphasis-normal role-source" data-evidence="4047-4051" x="70.0" y="145.5" width="181.0" height="71.0" rx="7" />
<text class="node-label" x="160.5" y="172.5">Keycloak · PostgreSQL</text>
<line class="node-detail-divider" x1="84.0" y1="193.5" x2="237.0" y2="193.5" />
<text class="node-detail" x="86.0" y="210.5">Guest Userspace</text>
</g>
<g id="node-guest-tlb">
<rect class="node-shape kind-component emphasis-normal role-store" data-evidence="4043-4045" x="175.0" y="248.0" width="150.0" height="71.0" rx="7" />
<text class="node-label" x="250.0" y="275.0">Guest TLB</text>
<line class="node-detail-divider" x1="189.0" y1="296.0" x2="311.0" y2="296.0" />
<text class="node-detail" x="191.0" y="313.0">Guest Kernel</text>
<rect class="node-shape kind-component emphasis-normal role-store" data-evidence="4056-4058" x="411.0" y="145.5" width="150.0" height="71.0" rx="7" />
<text class="node-label" x="486.0" y="172.5">Guest TLB</text>
<line class="node-detail-divider" x1="425.0" y1="193.5" x2="547.0" y2="193.5" />
<text class="node-detail" x="427.0" y="210.5">Guest Kernel</text>
</g>
<g id="node-guest-page-table">
<rect class="node-shape kind-component emphasis-normal role-service" data-evidence="4048-4053" x="175.0" y="415.0" width="150.0" height="88.0" rx="7" />
<text class="node-label" x="250.0" y="442.0">Guest Page Table</text>
<line class="node-detail-divider" x1="189.0" y1="463.0" x2="311.0" y2="463.0" />
<text class="node-detail" x="191.0" y="480.0">Guest Kernel</text>
<text class="node-detail" x="191.0" y="496.0">GVA → GPA</text>
<rect class="node-shape kind-component emphasis-normal role-service" data-evidence="4061-4066" x="721.0" y="137.0" width="150.0" height="88.0" rx="7" />
<text class="node-label" x="796.0" y="164.0">Guest Page Table</text>
<line class="node-detail-divider" x1="735.0" y1="185.0" x2="857.0" y2="185.0" />
<text class="node-detail" x="737.0" y="202.0">Guest Kernel</text>
<text class="node-detail" x="737.0" y="218.0">GVA → GPA</text>
</g>
<g id="node-ept">
<rect class="node-shape kind-component emphasis-primary role-service" data-evidence="4055-4067" x="175.0" y="599.0" width="150.0" height="88.0" rx="7" />
<text class="node-label" x="250.0" y="626.0">EPT</text>
<line class="node-detail-divider" x1="189.0" y1="647.0" x2="311.0" y2="647.0" />
<text class="node-detail" x="191.0" y="664.0">KVM / CPU</text>
<text class="node-detail" x="191.0" y="680.0">GPA → HPA</text>
<rect class="node-shape kind-component emphasis-primary role-service" data-evidence="4068-4080" x="1031.0" y="137.0" width="150.0" height="88.0" rx="7" />
<text class="node-label" x="1106.0" y="164.0">EPT</text>
<line class="node-detail-divider" x1="1045.0" y1="185.0" x2="1167.0" y2="185.0" />
<text class="node-detail" x="1047.0" y="202.0">KVM / CPU</text>
<text class="node-detail" x="1047.0" y="218.0">GPA → HPA</text>
</g>
<g id="node-host-physical-memory">
<rect class="node-shape kind-store emphasis-normal role-store" data-evidence="4069-4072" x="163.0" y="783.0" width="174.0" height="64.0" rx="7" />
<text class="node-label" x="250.0" y="813.0">Host Physical Memory</text>
<rect class="node-shape kind-store emphasis-normal role-store" data-evidence="4082-4085" x="1341.0" y="149.0" width="174.0" height="64.0" rx="7" />
<text class="node-label" x="1428.0" y="179.0">Host Physical Memory</text>
</g>
<g id="node-numa-node-0">
<rect class="node-shape kind-store emphasis-normal role-sink" data-evidence="4073-4075" x="70.0" y="943.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="145.0" y="973.0">NUMA Node 0</text>
<rect class="node-shape kind-store emphasis-normal role-sink" data-evidence="4086-4088" x="1675.0" y="81.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="1750.0" y="111.0">NUMA Node 0</text>
</g>
<g id="node-numa-node-1">
<rect class="node-shape kind-store emphasis-normal role-sink" data-evidence="4073-4075" x="280.0" y="943.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="355.0" y="973.0">NUMA Node 1</text>
<rect class="node-shape kind-store emphasis-normal role-sink" data-evidence="4086-4088" x="1675.0" y="217.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="1750.0" y="247.0">NUMA Node 1</text>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 9.4 KiB

After

Width:  |  Height:  |  Size: 9.4 KiB

@@ -0,0 +1,27 @@
# Host memory pressure가 storage와 OOM으로 갈라지는 경로
## Alternative text
Host memory pressure가 reclaim에서 clean page 재읽기, anonymous page의 Host swap과 storage contention, 메모리 확보 실패에 따른 Host OOM으로 갈라지는 흐름도. QEMU가 OOM victim이 될 때 VM 전체가 중단될 수 있다는 결과는 본문에서 설명한다.
## Long description
왼쪽에서 Host memory pressure가 reclaim으로 들어간다. reclaim 이후 clean file-backed page는 버렸다가 필요할 때 storage에서 다시 읽는 경로, anonymous page는 Host swap I/O를 통해 physical storage와 경쟁하는 경로, 필요한 메모리를 확보하지 못하면 Host OOM으로 가는 경로로 갈린다. storage contention이 application latency로 이어지는 결과와 Host OOM이 QEMU를 victim으로 고를 때 VM 전체가 중단될 수 있다는 결과는 본문에서 이어 설명한다.
## Elements and evidence
- **Host memory pressure** (resource): 실제 Host RAM 수요가 available physical memory에 접근한 상태. Evidence: L2818L2831.
- **Reclaim** (component): Linux가 회수 가능한 page/cache를 처리하는 첫 대응. Evidence: L2818L2831.
- **clean file-backed page** (data): 원본이 storage에 있어 RAM에서 제거하고 나중에 다시 읽을 수 있다. Evidence: L2834L2848.
- **Host swap I/O** (storage): anonymous page의 backing이 필요할 때 host swap I/O가 생길 수 있다. Evidence: L2850L2852, L2856L2886.
- **Storage contention** (storage): 여러 I/O가 한 physical device에 몰려 contention을 만든다. Evidence: L2938L2965.
- **Host OOM** (result): reclaim 등으로도 필요한 memory 확보에 실패했을 때 Host kernel에서 발생할 수 있다. Evidence: L3213L3231, L3249L3265.
## Relationships
- **clean file-backed page → Storage contention:** reread. Evidence: L2834L2848.
- **Host memory pressure → Reclaim:** pressure. Evidence: L2818L2831.
- **Reclaim → clean file-backed page:** file-backed. Evidence: L2834L2848.
- **Reclaim → Host OOM:** 확보 실패. Evidence: L3213L3231.
- **Reclaim → Host swap I/O:** anonymous. Evidence: L2850L2852.
- **Host swap I/O → Storage contention:** swap. Evidence: L2871L2886, L2940L2946.
@@ -0,0 +1,27 @@
# Host memory pressure가 storage와 OOM으로 갈라지는 경로
# Question: Host memory pressure가 생겼을 때 reclaim 이후 어떤 경로가 swap I/O와 storage contention으로 이어지고, 확보 실패는 어디서 OOM으로 이어지는가
direction: right
n0: "Host memory pressure" {
shape: rectangle
}
n1: "Reclaim" {
shape: rectangle
}
n2: "clean file-backed page" {
shape: rectangle
}
n3: "Host swap I/O" {
shape: cylinder
}
n4: "Storage contention" {
shape: cylinder
}
n5: "Host OOM" {
shape: rectangle
}
n0 -> n1: "pressure"
n1 -> n2: "file-backed"
n2 -> n4: "reread"
n1 -> n3: "anonymous"
n3 -> n4: "swap"
n1 -> n5: "확보 실패"
@@ -0,0 +1,17 @@
digraph techviz {
graph [rankdir=LR, splines=ortho, nodesep=0.55, ranksep=0.85];
node [fontname=Helvetica, fontsize=11, margin="0.18,0.12", style="rounded,filled", fillcolor=white, color="#2d4357", penwidth=1.5];
edge [fontname=Helvetica, fontsize=10, color="#364b5f", penwidth=1.4, arrowsize=0.75];
n0 [label="Host memory pressure", shape=box, style="rounded,filled"];
n1 [label="Reclaim", shape=box, style="rounded,filled"];
n2 [label="clean file-backed page", shape=box, style="rounded,filled"];
n3 [label="Host swap I/O", shape=cylinder, style="rounded,filled"];
n4 [label="Storage contention", shape=cylinder, style="rounded,filled"];
n5 [label="Host OOM", shape=box, style="rounded,filled"];
n0 -> n1 [label="pressure", style=solid];
n1 -> n2 [label="file-backed", style=solid];
n2 -> n4 [label="reread", style=solid];
n1 -> n3 [label="anonymous", style=solid];
n3 -> n4 [label="swap", style=solid];
n1 -> n5 [label="확보 실패", style=solid];
}
@@ -0,0 +1,59 @@
<?xml version="1.0" encoding="UTF-8"?>
<mxfile host="app.diagrams.net" modified="2026-07-23T00:00:00.000Z" agent="techviz-harness" version="24.7.17" type="device">
<diagram id="memory-pressure-reclaim-swap-oom" name="Host memory pressure가 storage와 OOM으로 갈라지는 경로">
<mxGraphModel dx="1313" dy="455" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1313" pageHeight="1169" math="0" shadow="0">
<root>
<mxCell id="0"/>
<mxCell id="1" parent="0"/>
<mxCell id="n_pressure" value="Host memory pressure" tooltip="실제 Host RAM 수요가 available physical memory에 접근한 상태. | Evidence: L2818-L2831" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;shape=document;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="70.0" y="180.0" width="220.0" height="110.0" as="geometry"/>
</mxCell>
<mxCell id="n_reclaim" value="Reclaim" tooltip="Linux가 회수 가능한 page/cache를 처리하는 첫 대응. | Evidence: L2818-L2831" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;strokeColor=#2563eb;strokeWidth=2;" vertex="1" parent="1">
<mxGeometry x="450.0" y="203.0" width="150.0" height="64.0" as="geometry"/>
</mxCell>
<mxCell id="n_clean-page" value="clean file-backed page&lt;br/&gt;discard · reread" tooltip="원본이 storage에 있어 RAM에서 제거하고 나중에 다시 읽을 수 있다. | Evidence: L2834-L2848" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="760.0" y="60.0" width="188.0" height="71.0" as="geometry"/>
</mxCell>
<mxCell id="n_host-oom" value="Host OOM" tooltip="reclaim 등으로도 필요한 memory 확보에 실패했을 때 Host kernel에서 발생할 수 있다. | Evidence: L3213-L3231, L3249-L3265" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="779.0" y="203.0" width="150.0" height="64.0" as="geometry"/>
</mxCell>
<mxCell id="n_host-swap" value="Host swap I/O&lt;br/&gt;anonymous page" tooltip="anonymous page의 backing이 필요할 때 host swap I/O가 생길 수 있다. | Evidence: L2850-L2852, L2856-L2886" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;shape=cylinder3;boundedLbl=1;backgroundOutline=1;fillColor=#eef6fb;" vertex="1" parent="1">
<mxGeometry x="779.0" y="339.0" width="150.0" height="71.0" as="geometry"/>
</mxCell>
<mxCell id="n_storage" value="Storage contention&lt;br/&gt;Host swap&lt;br/&gt;DB I/O&lt;br/&gt;writeback" tooltip="여러 I/O가 한 physical device에 몰려 contention을 만든다. | Evidence: L2938-L2965" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;shape=cylinder3;boundedLbl=1;backgroundOutline=1;fillColor=#eef6fb;" vertex="1" parent="1">
<mxGeometry x="1108.0" y="182.5" width="160.0" height="105.0" as="geometry"/>
</mxCell>
<mxCell id="e_clean-storage" value="reread" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_clean-page" target="n_storage">
<mxGeometry relative="1" as="geometry">
<mxPoint x="1052.0" y="160.8" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="e_pressure-reclaim" value="pressure" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_pressure" target="n_reclaim">
<mxGeometry relative="1" as="geometry">
<mxPoint x="370.0" y="207.0" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="e_reclaim-clean" value="file-backed" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_reclaim" target="n_clean-page">
<mxGeometry relative="1" as="geometry">
<mxPoint x="704.0" y="156.2" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="e_reclaim-oom" value="확보 실패" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_reclaim" target="n_host-oom">
<mxGeometry relative="1" as="geometry">
<mxPoint x="689.5" y="207.0" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="e_reclaim-swap" value="anonymous" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_reclaim" target="n_host-swap">
<mxGeometry relative="1" as="geometry">
<mxPoint x="713.5" y="313.8" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="e_swap-storage" value="swap" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_host-swap" target="n_storage">
<mxGeometry relative="1" as="geometry">
<mxPoint x="1042.5" y="309.2" as="offset"/>
</mxGeometry>
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>
@@ -0,0 +1,42 @@
{
"harness_version": "0.2.0",
"spec_id": "memory-pressure-reclaim-swap-oom",
"spec_version": "1.1",
"spec_sha256": "5861e49daf088f0cd326d7fd970908676dca01d984c0f7a0feb1350ad820999b",
"source_context": {
"document": "docs/virtualization/final/document.md",
"document_sha256": "8c4ecc64c8cea9a4450ed7131fdd9cb2048dc092b66cd969f6346ed77887c210",
"anchor": {
"kind": "heading",
"value": "제2부 — 메모리 가상화",
"line": 1670
}
},
"outputs": [
"memory-pressure-reclaim-swap-oom.svg",
"memory-pressure-reclaim-swap-oom.mmd",
"memory-pressure-reclaim-swap-oom.d2",
"memory-pressure-reclaim-swap-oom.dot",
"memory-pressure-reclaim-swap-oom.drawio",
"memory-pressure-reclaim-swap-oom.excalidraw",
"memory-pressure-reclaim-swap-oom.alt.md"
],
"lint_issue_count": 0,
"assumption_count": 0,
"assumptions_allowed": false,
"composition_profile": "component-flow",
"reference_ids": [
"payment-event-flow"
],
"diagram_only": true,
"source_spec_file_sha256": "ba06d73804fd520863c0cde6749d9b0d64f690e2fc38846e4d798982248feeca",
"output_sha256": {
"memory-pressure-reclaim-swap-oom.svg": "efcd3add0e205f71dcc42a8b9195dbfedc4db250a45f468f3708522f99f859fd",
"memory-pressure-reclaim-swap-oom.mmd": "b2963febc42ddaf0067c1d4e154470cd87c062b6604dd55e8ef3ea2a396d8c40",
"memory-pressure-reclaim-swap-oom.d2": "a00a8d24f0bf8967f3248026846562ea03492cdbd9ac89a09f9242864a58d6c2",
"memory-pressure-reclaim-swap-oom.dot": "c3d5a50508ffb776c4ad6540535308c80a24dcd61800fba7b661fe4cf9520cac",
"memory-pressure-reclaim-swap-oom.drawio": "9e56388949d8012cf7638667f94ac64178eb90a0d552c4b86cf0fbdb7681180c",
"memory-pressure-reclaim-swap-oom.excalidraw": "77f8e646292609d6da44308fed8398e9cc1706c1d8379f3a204278f9a4211ece",
"memory-pressure-reclaim-swap-oom.alt.md": "88d0c06a087ca774e9ec4e45c0d2c98207b4cf65935272e3fd497c8aef3033f4"
}
}
@@ -0,0 +1,15 @@
%% Host memory pressure가 storage와 OOM으로 갈라지는 경로
%% question: Host memory pressure가 생겼을 때 reclaim 이후 어떤 경로가 swap I/O와 storage contention으로 이어지고, 확보 실패는 어디서 OOM으로 이어지는가
flowchart LR
n0["Host memory pressure"]
n1["Reclaim"]
n2["clean file-backed page"]
n3[("Host swap I/O")]
n4[("Storage contention")]
n5["Host OOM"]
n0 -->|"pressure"| n1
n1 -->|"file-backed"| n2
n2 -->|"reread"| n4
n1 -->|"anonymous"| n3
n3 -->|"swap"| n4
n1 -->|"확보 실패"| n5
@@ -0,0 +1,103 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="1313" height="455" viewBox="0 0 1313 455" role="img" aria-labelledby="diagram-title diagram-description">
<title id="diagram-title">Host memory pressure가 storage와 OOM으로 갈라지는 경로</title>
<desc id="diagram-description">왼쪽에서 Host memory pressure가 reclaim으로 들어간다. reclaim 이후 clean file-backed page는 버렸다가 필요할 때 storage에서 다시 읽는 경로, anonymous page는 Host swap I/O를 통해 physical storage와 경쟁하는 경로, 필요한 메모리를 확보하지 못하면 Host OOM으로 가는 경로로 갈린다. storage contention이 application latency로 이어지는 결과와 Host OOM이 QEMU를 victim으로 고를 때 VM 전체가 중단될 수 있다는 결과는 본문에서 이어 설명한다.</desc>
<metadata>{&quot;techviz&quot;:{&quot;spec_version&quot;:&quot;1.1&quot;,&quot;id&quot;:&quot;memory-pressure-reclaim-swap-oom&quot;,&quot;profile&quot;:&quot;component-flow&quot;},&quot;source_context&quot;:{&quot;document&quot;:&quot;docs/virtualization/final/document.md&quot;,&quot;document_sha256&quot;:&quot;8c4ecc64c8cea9a4450ed7131fdd9cb2048dc092b66cd969f6346ed77887c210&quot;,&quot;anchor&quot;:{&quot;kind&quot;:&quot;heading&quot;,&quot;value&quot;:&quot;제2부 — 메모리 가상화&quot;,&quot;line&quot;:1670}},&quot;evidence_policy&quot;:&quot;Each factual element cites source lines or is marked assumption.&quot;,&quot;diagram_only&quot;:true}</metadata>
<defs>
<marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M 0 0 L 10 5 L 0 10 z" />
</marker>
<style>
:root { color-scheme: light; }
text { font-family: Inter, Pretendard, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #111827; }
.canvas { fill: #ffffff; }
.group-box { fill: #ffffff; stroke: #9ca3af; stroke-width: 1.4; stroke-dasharray: 7 5; }
.group-label-bg { fill: #ffffff; }
.group-label { font-size: 13px; font-weight: 650; fill: #374151; }
.edge { fill: none; stroke: #374151; stroke-width: 1.8; stroke-linejoin: round; stroke-linecap: round; marker-end: url(#arrow); }
.edge.style-dashed, .edge.semantic-dashed, .edge.assumption { stroke-dasharray: 7 5; }
.edge.style-dotted { stroke-dasharray: 2 5; }
.edge.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.edge.emphasis-muted { stroke: #9ca3af; }
.edge.emphasis-warning, .edge.kind-failure, .edge.kind-error { stroke: #dc2626; stroke-width: 2.2; }
.edge-label-bg { fill: #ffffff; }
.edge-label { font-size: 12px; font-weight: 560; text-anchor: middle; }
.node-shape { fill: #ffffff; stroke: #4b5563; stroke-width: 1.7; }
.node-shape.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.node-shape.emphasis-muted { stroke: #9ca3af; fill: #f9fafb; }
.node-shape.emphasis-warning { stroke: #d97706; stroke-width: 2; fill: #fffdf5; }
.node-shape.kind-database, .node-shape.kind-datastore, .node-shape.kind-storage { fill: #f8fafc; }
.node-shape.kind-queue, .node-shape.kind-event, .node-shape.kind-topic { fill: #fafafa; }
.node-shape.assumption { stroke-dasharray: 4 4; }
.storage-bottom, .controller-divider { fill: none; stroke: #4b5563; stroke-width: 1.4; }
.controller-led { fill: #4b5563; }
.actor-symbol { fill: none; stroke: #4b5563; stroke-width: 1.8; stroke-linecap: round; }
.actor-symbol.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.node-label { font-size: 14px; font-weight: 650; text-anchor: middle; }
.node-role { font-size: 10px; letter-spacing: 0.04em; text-anchor: middle; fill: #6b7280; }
.node-detail-divider { stroke: #d1d5db; stroke-width: 1; }
.node-detail { font-size: 11px; fill: #374151; }
.assumption-badge { font-size: 9px; font-weight: 700; fill: #92400e; }
.failure-mark { stroke: #dc2626; stroke-width: 4; stroke-linecap: round; }
.lifeline { stroke: #9ca3af; stroke-width: 1.2; stroke-dasharray: 5 5; }
.timeline-axis { stroke: #374151; stroke-width: 1.8; marker-end: url(#arrow); }
.timeline-stem { stroke: #6b7280; stroke-width: 1.3; }
.timeline-marker { fill: #ffffff; stroke: #374151; stroke-width: 1.7; }
.timeline-marker.primary { fill: #2563eb; stroke: #2563eb; }
.timeline-marker.warning { fill: #dc2626; stroke: #dc2626; }
.timeline-label { font-size: 13px; font-weight: 650; text-anchor: middle; }
.timeline-detail { font-size: 11px; fill: #4b5563; text-anchor: middle; }
</style>
</defs>
<rect class="canvas" width="1313" height="455" />
<polyline class="edge kind-data style-dashed emphasis-normal" points="948.0,95.5 1028.0,95.5 1028.0,226.0 1108.0,226.0" data-evidence="2834-2848" />
<rect class="edge-label-bg" x="1022.9" y="146.8" width="58.2" height="22" rx="3" />
<text class="edge-label" x="1052.0" y="161.8">reread</text>
<polyline class="edge kind-control style-solid emphasis-normal" points="290.0,235.0 370.0,235.0 370.0,235.0 450.0,235.0" data-evidence="2818-2831" />
<rect class="edge-label-bg" x="334.2" y="193.0" width="71.6" height="22" rx="3" />
<text class="edge-label" x="370.0" y="208.0">pressure</text>
<polyline class="edge kind-control style-solid emphasis-normal" points="600.0,217.0 680.0,217.0 680.0,95.5 760.0,95.5" data-evidence="2834-2848" />
<rect class="edge-label-bg" x="658.1" y="142.2" width="91.7" height="22" rx="3" />
<text class="edge-label" x="704.0" y="157.2">file-backed</text>
<polyline class="edge kind-control style-dashed emphasis-normal" points="600.0,235.0 689.5,235.0 689.5,235.0 779.0,235.0" data-evidence="3213-3231" />
<rect class="edge-label-bg" x="663.8" y="193.0" width="51.5" height="22" rx="3" />
<text class="edge-label" x="689.5" y="208.0">확보 실패</text>
<polyline class="edge kind-data style-solid emphasis-normal" points="600.0,253.0 689.5,253.0 689.5,374.5 779.0,374.5" data-evidence="2850-2852" />
<rect class="edge-label-bg" x="674.4" y="299.8" width="78.3" height="22" rx="3" />
<text class="edge-label" x="713.5" y="314.8">anonymous</text>
<polyline class="edge kind-data style-solid emphasis-normal" points="929.0,374.5 1018.5,374.5 1018.5,244.0 1108.0,244.0" data-evidence="2871-2886,2940-2946" />
<rect class="edge-label-bg" x="1020.1" y="295.2" width="44.8" height="22" rx="3" />
<text class="edge-label" x="1042.5" y="310.2">swap</text>
<g id="node-pressure">
<path class="node-shape kind-resource emphasis-normal role-source" data-evidence="2818-2831" d="M 70.0 180.0 H 259.2 L 290.0 210.8 V 290.0 H 70.0 Z M 259.2 180.0 V 210.8 H 290.0" />
<text class="node-label" x="180.0" y="233.0">Host memory pressure</text>
</g>
<g id="node-reclaim">
<rect class="node-shape kind-component emphasis-primary role-service" data-evidence="2818-2831" x="450.0" y="203.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="525.0" y="233.0">Reclaim</text>
</g>
<g id="node-clean-page">
<rect class="node-shape kind-data emphasis-normal role-service" data-evidence="2834-2848" x="760.0" y="60.0" width="188.0" height="71.0" rx="7" />
<text class="node-label" x="854.0" y="87.0">clean file-backed page</text>
<line class="node-detail-divider" x1="774.0" y1="108.0" x2="934.0" y2="108.0" />
<text class="node-detail" x="776.0" y="125.0">discard · reread</text>
</g>
<g id="node-host-oom">
<rect class="node-shape kind-result emphasis-normal role-service" data-evidence="3213-3231,3249-3265" x="779.0" y="203.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="854.0" y="233.0">Host OOM</text>
</g>
<g id="node-host-swap">
<rect class="node-shape kind-storage emphasis-normal role-store" data-evidence="2850-2852,2856-2886" x="779.0" y="350.8" width="150.0" height="47.3" /><ellipse class="node-shape kind-storage emphasis-normal role-store" cx="854.0" cy="350.8" rx="75.0" ry="11.8" /><path class="storage-bottom" d="M 779.0 398.2 A 75.0 11.8 0 0 0 929.0 398.2" />
<text class="node-label" x="854.0" y="366.0">Host swap I/O</text>
<line class="node-detail-divider" x1="793.0" y1="387.0" x2="915.0" y2="387.0" />
<text class="node-detail" x="795.0" y="404.0">anonymous page</text>
</g>
<g id="node-storage">
<rect class="node-shape kind-storage emphasis-normal role-store" data-evidence="2938-2965" x="1108.0" y="195.5" width="160.0" height="79.0" /><ellipse class="node-shape kind-storage emphasis-normal role-store" cx="1188.0" cy="195.5" rx="80.0" ry="13.0" /><path class="storage-bottom" d="M 1108.0 274.5 A 80.0 13.0 0 0 0 1268.0 274.5" />
<text class="node-label" x="1188.0" y="209.5">Storage contention</text>
<line class="node-detail-divider" x1="1122.0" y1="230.5" x2="1254.0" y2="230.5" />
<text class="node-detail" x="1124.0" y="247.5">Host swap</text>
<text class="node-detail" x="1124.0" y="263.5">DB I/O</text>
<text class="node-detail" x="1124.0" y="279.5">writeback</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 8.7 KiB

@@ -0,0 +1,26 @@
digraph techviz {
graph [rankdir=LR, splines=ortho, nodesep=0.55, ranksep=0.85];
node [fontname=Helvetica, fontsize=11, margin="0.18,0.12", style="rounded,filled", fillcolor=white, color="#2d4357", penwidth=1.5];
edge [fontname=Helvetica, fontsize=10, color="#364b5f", penwidth=1.4, arrowsize=0.75];
subgraph cluster_0 {
label="forward · priority filter - 10";
style="rounded,dashed";
color="#66788a";
n1 [label="ct state new accept", shape=box, style="rounded,filled"];
}
subgraph cluster_1 {
label="ip libvirt_network · guest_input";
style="rounded,dashed";
color="#66788a";
n2 [label="inserted accept", shape=box, style="rounded,filled"];
n3 [label="reject", shape=box, style="rounded,filled"];
}
n0 [label="inbound packet", shape=box, style="rounded,filled"];
n4 [label="edge nginx", shape=box, style="rounded,filled"];
n5 [label="connection refused", shape=box, style="rounded,filled"];
n0 -> n1 [label="forward hook", style=solid];
n1 -> n2 [label="after insert", style=solid];
n1 -> n3 [label="before insert", style=solid];
n2 -> n4 [label="accept", style=solid];
n3 -> n5 [label="reject", style=solid];
}
@@ -2,10 +2,10 @@
"harness_version": "0.2.0",
"spec_id": "nftables-forward-hook-chain-order",
"spec_version": "1.1",
"spec_sha256": "7c412e3a6278b75551ce56f35693dd236d0b3bf0962c2788a87703123f009f9f",
"spec_sha256": "a035a69d70e850cb32cb89258e9912fdcd4533a8d312be789b040a12b51b1225",
"source_context": {
"document": "docs/virtualization/final/document.md",
"document_sha256": "60d902c7aed218ba637b48603a3bd0e6b193fea59c9de97ffdb8e05d5087aedd",
"document_sha256": "8c4ecc64c8cea9a4450ed7131fdd9cb2048dc092b66cd969f6346ed77887c210",
"anchor": {
"kind": "heading",
"value": "180. nftables 는 앞 체인의 `accept` 로 뒤 체인의 `reject` 를 막지 못한다",
@@ -14,9 +14,10 @@
},
"outputs": [
"nftables-forward-hook-chain-order.svg",
"nftables-forward-hook-chain-order.drawio",
"nftables-forward-hook-chain-order.mmd",
"nftables-forward-hook-chain-order.d2",
"nftables-forward-hook-chain-order.dot",
"nftables-forward-hook-chain-order.drawio",
"nftables-forward-hook-chain-order.excalidraw",
"nftables-forward-hook-chain-order.alt.md"
],
@@ -27,5 +28,15 @@
"reference_ids": [
"payment-event-flow"
],
"diagram_only": true
"diagram_only": true,
"source_spec_file_sha256": "6ced898c845eb65e6dccc403fcadfbf82d3805f7fe089c75241132621201298e",
"output_sha256": {
"nftables-forward-hook-chain-order.svg": "f5cf1414171d34677a6e2e430b4269a8687a5968f50488f2257ddc3d3be808db",
"nftables-forward-hook-chain-order.mmd": "a523ee8885b46bc40937b8992f9ebf65d273eaf378f980af1026bbf2cda84e83",
"nftables-forward-hook-chain-order.d2": "117662bdc7955bb01ceb6a36136ee2d5d6da727301b5c0f2c24c75fc71454c87",
"nftables-forward-hook-chain-order.dot": "e71e3d8dc4a0e7417e4ef8514b79ec52099609c50bf895d31888e3ed4add8166",
"nftables-forward-hook-chain-order.drawio": "9a60b8116a117e18d83eb6ba78048f3400578dd62adb3247aa88f3d5bffb0e98",
"nftables-forward-hook-chain-order.excalidraw": "efa24031fb83393e57aeebd824f3e839c97c5b55f61ae1922c6933a4f1e709b7",
"nftables-forward-hook-chain-order.alt.md": "aa81f31c4403feadabb90846086ab5f29b821fd82712fcb275d8e8c0b92e17b2"
}
}
@@ -2,7 +2,7 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1298" height="434" viewBox="0 0 1298 434" role="img" aria-labelledby="diagram-title diagram-description">
<title id="diagram-title">같은 forward 훅에 붙은 base 체인 둘이 우선순위 순으로 이어서 평가되고, 앞 체인의 accept 는 뒤 체인의 reject 를 막지 못한다</title>
<desc id="diagram-description">왼쪽에서 오른쪽으로 읽는다. 왼쪽 끝이 밖에서 친 curl 이 만든 inbound packet 이고 forward 훅으로 들어간다. 첫 점선 상자가 DNAT 파일에 둔 base 체인이다. priority filter - 10 이라 먼저 돌고, 그 안의 ct state new accept 가 이 packet 을 통과시킨다. 다음 점선 상자가 libvirt 가 만든 ip libvirt_network 테이블의 guest_input 체인이다. 앞 체인의 accept 는 평가를 끝내지 않으므로 같은 packet 이 이 체인으로 이어진다. 그 안의 상자 둘은 같은 체인의 맨 앞과 맨 끝이다. 구멍을 넣기 전에는 맨 앞이 비어 있어 packet 이 ct state established,related accept 에 걸리지 못한 채 체인 끝 reject 에 닿았고 connection refused 로 끝났다. 그 reject 규칙의 카운터가 4 패킷 240 바이트다. insert 로 구멍을 넣으면 그 규칙이 맨 앞에 서서 같은 packet 을 먼저 받아 192.168.122.10 의 엣지 nginx 로 보낸다. 실선이 구멍을 넣은 뒤의 경로이고 점선이 넣기 전의 경로다. 이 그림은 그 규칙이 libvirt 네트워크를 다시 세우면 사라진다는 것은 말하지 않는다.</desc>
<metadata>{&quot;techviz&quot;:{&quot;spec_version&quot;:&quot;1.1&quot;,&quot;id&quot;:&quot;nftables-forward-hook-chain-order&quot;,&quot;profile&quot;:&quot;component-flow&quot;},&quot;source_context&quot;:{&quot;document&quot;:&quot;docs/virtualization/final/document.md&quot;,&quot;document_sha256&quot;:&quot;60d902c7aed218ba637b48603a3bd0e6b193fea59c9de97ffdb8e05d5087aedd&quot;,&quot;anchor&quot;:{&quot;kind&quot;:&quot;heading&quot;,&quot;value&quot;:&quot;180. nftables 는 앞 체인의 `accept` 로 뒤 체인의 `reject` 를 막지 못한다&quot;,&quot;line&quot;:7805}},&quot;evidence_policy&quot;:&quot;Each factual element cites source lines or is marked assumption.&quot;,&quot;diagram_only&quot;:true}</metadata>
<metadata>{&quot;techviz&quot;:{&quot;spec_version&quot;:&quot;1.1&quot;,&quot;id&quot;:&quot;nftables-forward-hook-chain-order&quot;,&quot;profile&quot;:&quot;component-flow&quot;},&quot;source_context&quot;:{&quot;document&quot;:&quot;docs/virtualization/final/document.md&quot;,&quot;document_sha256&quot;:&quot;8c4ecc64c8cea9a4450ed7131fdd9cb2048dc092b66cd969f6346ed77887c210&quot;,&quot;anchor&quot;:{&quot;kind&quot;:&quot;heading&quot;,&quot;value&quot;:&quot;180. nftables 는 앞 체인의 `accept` 로 뒤 체인의 `reject` 를 막지 못한다&quot;,&quot;line&quot;:7805}},&quot;evidence_policy&quot;:&quot;Each factual element cites source lines or is marked assumption.&quot;,&quot;diagram_only&quot;:true}</metadata>
<defs>
<marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M 0 0 L 10 5 L 0 10 z" />

Before

Width:  |  Height:  |  Size: 9.7 KiB

After

Width:  |  Height:  |  Size: 9.7 KiB

@@ -10,16 +10,16 @@ QEMU vCPU Thread, VM1 Memory Backing, Node 0 CPU, Node 0 RAM, Node 1 RAM 다섯
## Elements and evidence
- **QEMU vCPU Thread** (process): Guest vCPU 가 Host 에서 갖는 실체. Host scheduler 가 이것을 logical CPU 에 올린다. Evidence: L3315L3324.
- **VM1 Memory Backing** (memory): 그 가상 머신의 Host physical backing page 가 놓이는 자리. Evidence: L3327L3327, L3352L3354, L3381L3382.
- **Node 0 CPU** (compute): vCPU thread 가 실행되는 logical CPU. NUMA Node 0 에 속한다. Evidence: L3322L3330.
- **Node 0 RAM** (memory): Node 0 CPU 와 같은 node 의 RAM. Evidence: L3288L3296, L3373L3382.
- **Node 1 RAM** (memory): 다른 node 의 RAM. Node 0 CPU 가 읽으면 interconnect 를 건넌다. Evidence: L3299L3306, L3334L3334.
- **QEMU vCPU Thread** (process): Guest vCPU 가 Host 에서 갖는 실체. Host scheduler 가 이것을 logical CPU 에 올린다. Evidence: L3328L3337.
- **VM1 Memory Backing** (memory): 그 가상 머신의 Host physical backing page 가 놓이는 자리. Evidence: L3340L3340, L3365L3367, L3394L3395.
- **Node 0 CPU** (compute): vCPU thread 가 실행되는 logical CPU. NUMA Node 0 에 속한다. Evidence: L3335L3343.
- **Node 0 RAM** (memory): Node 0 CPU 와 같은 node 의 RAM. Evidence: L3301L3309, L3386L3395.
- **Node 1 RAM** (memory): 다른 node 의 RAM. Node 0 CPU 가 읽으면 interconnect 를 건넌다. Evidence: L3312L3319, L3347L3347.
## Relationships
- **QEMU vCPU Thread → Node 0 CPU:** vCPU Pinning. Evidence: L3322L3324, L3347L3350.
- **VM1 Memory Backing → Node 1 RAM:** Memory Placement. Evidence: L3327L3327, L3352L3354.
- **Node 0 CPU → Node 1 RAM:** Remote Access. Evidence: L3329L3339, L3356L3357.
- **VM1 Memory Backing → Node 0 RAM:** Memory Binding. Evidence: L3361L3366, L3381L3382.
- **Node 0 CPU → Node 0 RAM:** Local Access. Evidence: L3288L3296, L3373L3382.
- **QEMU vCPU Thread → Node 0 CPU:** vCPU Pinning. Evidence: L3335L3337, L3360L3363.
- **VM1 Memory Backing → Node 1 RAM:** Memory Placement. Evidence: L3340L3340, L3365L3367.
- **Node 0 CPU → Node 1 RAM:** Remote Access. Evidence: L3342L3352, L3369L3370.
- **VM1 Memory Backing → Node 0 RAM:** Memory Binding. Evidence: L3374L3379, L3394L3395.
- **Node 0 CPU → Node 0 RAM:** Local Access. Evidence: L3301L3309, L3386L3395.
@@ -0,0 +1,15 @@
digraph techviz {
graph [rankdir=LR, splines=ortho, nodesep=0.55, ranksep=0.85];
node [fontname=Helvetica, fontsize=11, margin="0.18,0.12", style="rounded,filled", fillcolor=white, color="#2d4357", penwidth=1.5];
edge [fontname=Helvetica, fontsize=10, color="#364b5f", penwidth=1.4, arrowsize=0.75];
n0 [label="QEMU vCPU Thread", shape=box, style="rounded,filled"];
n1 [label="VM1 Memory Backing", shape=box, style="rounded,filled"];
n2 [label="Node 0 CPU", shape=box, style="rounded,filled"];
n3 [label="Node 0 RAM", shape=box, style="rounded,filled"];
n4 [label="Node 1 RAM", shape=box, style="rounded,filled"];
n0 -> n2 [label="vCPU Pinning", style=solid];
n1 -> n4 [label="Memory Placement", style=solid];
n2 -> n4 [label="Remote Access", style=solid];
n1 -> n3 [label="Memory Binding", style=solid];
n2 -> n3 [label="Local Access", style=solid];
}
@@ -5,19 +5,19 @@
<root>
<mxCell id="0"/>
<mxCell id="1" parent="0"/>
<mxCell id="n_qemu-vcpu-thread" value="QEMU vCPU Thread&lt;br/&gt;Guest vCPU" tooltip="Guest vCPU 가 Host 에서 갖는 실체. Host scheduler 가 이것을 logical CPU 에 올린다. | Evidence: L3315-L3324" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;strokeColor=#2563eb;strokeWidth=2;" vertex="1" parent="1">
<mxCell id="n_qemu-vcpu-thread" value="QEMU vCPU Thread&lt;br/&gt;Guest vCPU" tooltip="Guest vCPU 가 Host 에서 갖는 실체. Host scheduler 가 이것을 logical CPU 에 올린다. | Evidence: L3328-L3337" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;strokeColor=#2563eb;strokeWidth=2;" vertex="1" parent="1">
<mxGeometry x="45.0" y="35.0" width="150.0" height="71.0" as="geometry"/>
</mxCell>
<mxCell id="n_vm-memory-backing" value="VM1 Memory Backing&lt;br/&gt;VM1 RAM" tooltip="그 가상 머신의 Host physical backing page 가 놓이는 자리. | Evidence: L3327-L3327, L3352-L3354, L3381-L3382" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxCell id="n_vm-memory-backing" value="VM1 Memory Backing&lt;br/&gt;VM1 RAM" tooltip="그 가상 머신의 Host physical backing page 가 놓이는 자리. | Evidence: L3340-L3340, L3365-L3367, L3394-L3395" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="255.0" y="35.0" width="160.0" height="71.0" as="geometry"/>
</mxCell>
<mxCell id="n_node0-cpu" value="Node 0 CPU&lt;br/&gt;Host Logical CPU" tooltip="vCPU thread 가 실행되는 logical CPU. NUMA Node 0 에 속한다. | Evidence: L3322-L3330" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;strokeColor=#2563eb;strokeWidth=2;" vertex="1" parent="1">
<mxCell id="n_node0-cpu" value="Node 0 CPU&lt;br/&gt;Host Logical CPU" tooltip="vCPU thread 가 실행되는 logical CPU. NUMA Node 0 에 속한다. | Evidence: L3335-L3343" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;strokeColor=#2563eb;strokeWidth=2;" vertex="1" parent="1">
<mxGeometry x="465.0" y="35.0" width="150.0" height="71.0" as="geometry"/>
</mxCell>
<mxCell id="n_node0-ram" value="Node 0 RAM&lt;br/&gt;Local Memory" tooltip="Node 0 CPU 와 같은 node 의 RAM. | Evidence: L3288-L3296, L3373-L3382" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxCell id="n_node0-ram" value="Node 0 RAM&lt;br/&gt;Local Memory" tooltip="Node 0 CPU 와 같은 node 의 RAM. | Evidence: L3301-L3309, L3386-L3395" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="675.0" y="35.0" width="150.0" height="71.0" as="geometry"/>
</mxCell>
<mxCell id="n_node1-ram" value="Node 1 RAM&lt;br/&gt;Remote Memory" tooltip="다른 node 의 RAM. Node 0 CPU 가 읽으면 interconnect 를 건넌다. | Evidence: L3299-L3306, L3334-L3334" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxCell id="n_node1-ram" value="Node 1 RAM&lt;br/&gt;Remote Memory" tooltip="다른 node 의 RAM. Node 0 CPU 가 읽으면 interconnect 를 건넌다. | Evidence: L3312-L3319, L3347-L3347" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="885.0" y="35.0" width="150.0" height="71.0" as="geometry"/>
</mxCell>
<mxCell id="e_vcpu-pinning" value="vCPU Pinning" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_qemu-vcpu-thread" target="n_node0-cpu">
@@ -2,21 +2,22 @@
"harness_version": "0.2.0",
"spec_id": "numa-vcpu-and-memory-placement",
"spec_version": "1.1",
"spec_sha256": "afeb9cbc8d7063a1945cabb916eadab7d248a252fce57e433355e6c679090bd5",
"spec_sha256": "49d394cecc2f97b5dd1c569cc040275a11abb76b563dda8edde59f497d21c7d9",
"source_context": {
"document": "docs/virtualization/final/document.md",
"document_sha256": "181e2b3cc8a45bae81e7e8193d026937c4d586ae4495d3b2c323eb7e6abcfadd",
"document_sha256": "8c4ecc64c8cea9a4450ed7131fdd9cb2048dc092b66cd969f6346ed77887c210",
"anchor": {
"kind": "heading",
"value": "75. vCPU와 NUMA의 연결",
"line": 3313
"line": 3326
}
},
"outputs": [
"numa-vcpu-and-memory-placement.svg",
"numa-vcpu-and-memory-placement.drawio",
"numa-vcpu-and-memory-placement.mmd",
"numa-vcpu-and-memory-placement.d2",
"numa-vcpu-and-memory-placement.dot",
"numa-vcpu-and-memory-placement.drawio",
"numa-vcpu-and-memory-placement.excalidraw",
"numa-vcpu-and-memory-placement.alt.md"
],
@@ -27,5 +28,15 @@
"reference_ids": [
"payment-approval-sequence"
],
"diagram_only": true
"diagram_only": true,
"source_spec_file_sha256": "1db7bff99e8493c392fb04c43ee74f13b4bcf0c0d2685f036bcdd74bbd6c00c4",
"output_sha256": {
"numa-vcpu-and-memory-placement.svg": "3720f7bf05faaf6e542fd41cb08d62afad368a2aa55ee6d960a1372b0d766da2",
"numa-vcpu-and-memory-placement.mmd": "5b1860bb9a59f0f7848a7789e8ccd0c8471b5560113bbf44c09a9d1672494bc0",
"numa-vcpu-and-memory-placement.d2": "a061122778afc79df6d07608d99ca98f625e73411a8dceeba163cd2315a3b215",
"numa-vcpu-and-memory-placement.dot": "63025126420fadbf9ddf5686ff7e07aab5ec7cd226349d812d36fb47046f2e0f",
"numa-vcpu-and-memory-placement.drawio": "24c0328f9bba296c4bdeed687a72d80789d893d3c4d803567d10611f84057059",
"numa-vcpu-and-memory-placement.excalidraw": "c8115da91d5a884bb8d256b01477ec548ece6db09fc1c3ac58a609d9ab9b0834",
"numa-vcpu-and-memory-placement.alt.md": "a8db12c5add65f655d32a0f5bb00b01c6c873503ca57b52ff4814f215dd8afdb"
}
}
@@ -2,7 +2,7 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1080" height="480" viewBox="0 0 1080 480" role="img" aria-labelledby="diagram-title diagram-description">
<title id="diagram-title">vCPU 를 Node 0 에 묶어도 backing 이 Node 1 에 있으면 접근은 remote 로 남는다</title>
<desc id="diagram-description">참여자는 왼쪽부터 QEMU vCPU Thread, VM1 Memory Backing, Node 0 CPU, Node 0 RAM, Node 1 RAM 다섯이다. 메시지는 위에서 아래로 읽는다. 1번에서 vCPU thread 가 Node 0 CPU 에 pinning 되고, 2번에서 VM1 의 memory backing 이 Node 1 에 놓인다. 그 배치에서 3번의 memory 접근은 Node 1 RAM 으로 가고 이것이 remote access 다. Node 0 과 Node 1 사이에는 interconnect 가 있어 이 접근만 그 구간을 건넌다. 4번과 5번은 그 뒤에 일어나는 다른 사건이 아니라 배치를 고친 구성이다. memory placement 와 binding 까지 Node 0 으로 맞추면 같은 접근이 Node 0 RAM 으로 가고 local access 가 된다. 접근 비용이 실제로 얼마나 차이 나는지, 이 Host 가 몇 개의 node 를 가졌는지는 그림이 말하지 않는다.</desc>
<metadata>{&quot;techviz&quot;:{&quot;spec_version&quot;:&quot;1.1&quot;,&quot;id&quot;:&quot;numa-vcpu-and-memory-placement&quot;,&quot;profile&quot;:&quot;sequence&quot;},&quot;source_context&quot;:{&quot;document&quot;:&quot;docs/virtualization/final/document.md&quot;,&quot;document_sha256&quot;:&quot;181e2b3cc8a45bae81e7e8193d026937c4d586ae4495d3b2c323eb7e6abcfadd&quot;,&quot;anchor&quot;:{&quot;kind&quot;:&quot;heading&quot;,&quot;value&quot;:&quot;75. vCPU와 NUMA의 연결&quot;,&quot;line&quot;:3313}},&quot;evidence_policy&quot;:&quot;Each factual element cites source lines or is marked assumption.&quot;,&quot;diagram_only&quot;:true}</metadata>
<metadata>{&quot;techviz&quot;:{&quot;spec_version&quot;:&quot;1.1&quot;,&quot;id&quot;:&quot;numa-vcpu-and-memory-placement&quot;,&quot;profile&quot;:&quot;sequence&quot;},&quot;source_context&quot;:{&quot;document&quot;:&quot;docs/virtualization/final/document.md&quot;,&quot;document_sha256&quot;:&quot;8c4ecc64c8cea9a4450ed7131fdd9cb2048dc092b66cd969f6346ed77887c210&quot;,&quot;anchor&quot;:{&quot;kind&quot;:&quot;heading&quot;,&quot;value&quot;:&quot;75. vCPU와 NUMA의 연결&quot;,&quot;line&quot;:3326}},&quot;evidence_policy&quot;:&quot;Each factual element cites source lines or is marked assumption.&quot;,&quot;diagram_only&quot;:true}</metadata>
<defs>
<marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M 0 0 L 10 5 L 0 10 z" />
@@ -50,44 +50,44 @@
</style>
</defs>
<rect class="canvas" width="1080" height="480" />
<rect class="node-shape kind-process emphasis-primary role-participant" data-evidence="3315-3324" x="45.0" y="35.0" width="150.0" height="71.0" rx="7" />
<rect class="node-shape kind-process emphasis-primary role-participant" data-evidence="3328-3337" x="45.0" y="35.0" width="150.0" height="71.0" rx="7" />
<text class="node-label" x="120.0" y="62.0">QEMU vCPU Thread</text>
<line class="node-detail-divider" x1="59.0" y1="83.0" x2="181.0" y2="83.0" />
<text class="node-detail" x="61.0" y="100.0">Guest vCPU</text>
<line class="lifeline" x1="120.0" y1="106.0" x2="120.0" y2="450.0" />
<rect class="node-shape kind-memory emphasis-normal role-participant" data-evidence="3327-3327,3352-3354,3381-3382" x="255.0" y="35.0" width="160.0" height="71.0" rx="7" />
<rect class="node-shape kind-memory emphasis-normal role-participant" data-evidence="3340-3340,3365-3367,3394-3395" x="255.0" y="35.0" width="160.0" height="71.0" rx="7" />
<text class="node-label" x="335.0" y="62.0">VM1 Memory Backing</text>
<line class="node-detail-divider" x1="269.0" y1="83.0" x2="401.0" y2="83.0" />
<text class="node-detail" x="271.0" y="100.0">VM1 RAM</text>
<line class="lifeline" x1="335.0" y1="106.0" x2="335.0" y2="450.0" />
<rect class="node-shape kind-compute emphasis-primary role-participant" data-evidence="3322-3330" x="465.0" y="35.0" width="150.0" height="71.0" rx="7" />
<rect class="node-shape kind-compute emphasis-primary role-participant" data-evidence="3335-3343" x="465.0" y="35.0" width="150.0" height="71.0" rx="7" />
<text class="node-label" x="540.0" y="62.0">Node 0 CPU</text>
<line class="node-detail-divider" x1="479.0" y1="83.0" x2="601.0" y2="83.0" />
<text class="node-detail" x="481.0" y="100.0">Host Logical CPU</text>
<line class="lifeline" x1="540.0" y1="106.0" x2="540.0" y2="450.0" />
<rect class="node-shape kind-memory emphasis-normal role-participant" data-evidence="3288-3296,3373-3382" x="675.0" y="35.0" width="150.0" height="71.0" rx="7" />
<rect class="node-shape kind-memory emphasis-normal role-participant" data-evidence="3301-3309,3386-3395" x="675.0" y="35.0" width="150.0" height="71.0" rx="7" />
<text class="node-label" x="750.0" y="62.0">Node 0 RAM</text>
<line class="node-detail-divider" x1="689.0" y1="83.0" x2="811.0" y2="83.0" />
<text class="node-detail" x="691.0" y="100.0">Local Memory</text>
<line class="lifeline" x1="750.0" y1="106.0" x2="750.0" y2="450.0" />
<rect class="node-shape kind-memory emphasis-normal role-participant" data-evidence="3299-3306,3334-3334" x="885.0" y="35.0" width="150.0" height="71.0" rx="7" />
<rect class="node-shape kind-memory emphasis-normal role-participant" data-evidence="3312-3319,3347-3347" x="885.0" y="35.0" width="150.0" height="71.0" rx="7" />
<text class="node-label" x="960.0" y="62.0">Node 1 RAM</text>
<line class="node-detail-divider" x1="899.0" y1="83.0" x2="1021.0" y2="83.0" />
<text class="node-detail" x="901.0" y="100.0">Remote Memory</text>
<line class="lifeline" x1="960.0" y1="106.0" x2="960.0" y2="450.0" />
<polyline class="edge kind-control style-solid emphasis-normal" points="120.0,140.0 540.0,140.0" data-evidence="3322-3324,3347-3350" />
<polyline class="edge kind-control style-solid emphasis-normal" points="120.0,140.0 540.0,140.0" data-evidence="3335-3337,3360-3363" />
<rect class="edge-label-bg" x="270.8" y="114.0" width="118.5" height="22" rx="3" />
<text class="edge-label" x="330.0" y="129.0">1. vCPU Pinning</text>
<polyline class="edge kind-control style-solid emphasis-normal" points="335.0,202.0 960.0,202.0" data-evidence="3327-3327,3352-3354" />
<polyline class="edge kind-control style-solid emphasis-normal" points="335.0,202.0 960.0,202.0" data-evidence="3340-3340,3365-3367" />
<rect class="edge-label-bg" x="574.9" y="176.0" width="145.3" height="22" rx="3" />
<text class="edge-label" x="647.5" y="191.0">2. Memory Placement</text>
<polyline class="edge kind-read style-dashed emphasis-normal" points="540.0,264.0 960.0,264.0" data-evidence="3329-3339,3356-3357" />
<polyline class="edge kind-read style-dashed emphasis-normal" points="540.0,264.0 960.0,264.0" data-evidence="3342-3352,3369-3370" />
<rect class="edge-label-bg" x="687.4" y="238.0" width="125.2" height="22" rx="3" />
<text class="edge-label" x="750.0" y="253.0">3. Remote Access</text>
<polyline class="edge kind-control style-solid emphasis-normal" points="335.0,326.0 750.0,326.0" data-evidence="3361-3366,3381-3382" />
<polyline class="edge kind-control style-solid emphasis-normal" points="335.0,326.0 750.0,326.0" data-evidence="3374-3379,3394-3395" />
<rect class="edge-label-bg" x="476.6" y="300.0" width="131.9" height="22" rx="3" />
<text class="edge-label" x="542.5" y="315.0">4. Memory Binding</text>
<polyline class="edge kind-read style-solid emphasis-normal" points="540.0,388.0 750.0,388.0" data-evidence="3288-3296,3373-3382" />
<polyline class="edge kind-read style-solid emphasis-normal" points="540.0,388.0 750.0,388.0" data-evidence="3301-3309,3386-3395" />
<rect class="edge-label-bg" x="585.8" y="362.0" width="118.5" height="22" rx="3" />
<text class="edge-label" x="645.0" y="377.0">5. Local Access</text>
</svg>

Before

Width:  |  Height:  |  Size: 8.4 KiB

After

Width:  |  Height:  |  Size: 8.4 KiB

@@ -10,34 +10,34 @@
## Elements and evidence
- **virsh** (cli): VM/network 관리 CLI. packet datapath 에는 직접 참여하지 않는다. Evidence: L5662L5662, L5687L5689.
- **libvirt** (service): VM lifecycle 과 NIC/network configuration 을 관리한다. Evidence: L5663L5663, L5691L5691.
- **QEMU** (process): 실제로 실행되는 프로세스. 이 안에 virtio-net device model 이 있다. Evidence: L5666L5666, L5693L5693.
- **virtio-net Device Model** (service): control/setup path 가 닿는 자리. 장치를 만들고 backend 를 연결한다. Evidence: L5666L5666, L5695L5700.
- **Physical NIC** (hardware): 밖에서 온 packet 이 처음 닿는 하드웨어. Evidence: L5706L5708.
- **Physical NIC Driver** (driver): 그 하드웨어를 제어하는 Host kernel driver. Evidence: L5674L5674, L5710L5710.
- **virsh** (cli): VM/network 관리 CLI. packet datapath 에는 직접 참여하지 않는다. Evidence: L5675L5675, L5700L5702.
- **libvirt** (service): VM lifecycle 과 NIC/network configuration 을 관리한다. Evidence: L5676L5676, L5704L5704.
- **QEMU** (process): 실제로 실행되는 프로세스. 이 안에 virtio-net device model 이 있다. Evidence: L5679L5679, L5706L5706.
- **virtio-net Device Model** (service): control/setup path 가 닿는 자리. 장치를 만들고 backend 를 연결한다. Evidence: L5679L5679, L5708L5713.
- **Physical NIC** (hardware): 밖에서 온 packet 이 처음 닿는 하드웨어. Evidence: L5719L5721.
- **Physical NIC Driver** (driver): 그 하드웨어를 제어하는 Host kernel driver. Evidence: L5687L5687, L5723L5723.
- **Linux Bridge /
Routing / NAT** (network): L2 software switch 와 L3 routing, 주소 변환이 놓이는 자리. Evidence: L5672L5673, L5712L5712.
- **TAP** (network): VM 의 Ethernet frame 과 Host Linux networking 을 잇는 접점. Evidence: L5671L5671, L5714L5714.
- **vhost-net** (kernel): 반복되는 packet datapath 를 Host kernel 에서 처리하는 backend. Evidence: L5669L5669, L5716L5716.
- **QEMU virtio backend** (process): vhost-net 을 쓰지 않을 때 datapath backend 를 맡는 쪽. Evidence: L5668L5668, L5742L5742.
- **virtqueue** (queue): Guest 와 Host backend 가 I/O buffer 를 주고받는 descriptor 기반 공유 큐. Evidence: L5667L5667, L5718L5718.
- **Guest** (service): packet 이 올라가 애플리케이션 socket 에 닿는 쪽. Evidence: L5664L5665, L5720L5726.
Routing / NAT** (network): L2 software switch 와 L3 routing, 주소 변환이 놓이는 자리. Evidence: L5685L5686, L5725L5725.
- **TAP** (network): VM 의 Ethernet frame 과 Host Linux networking 을 잇는 접점. Evidence: L5684L5684, L5727L5727.
- **vhost-net** (kernel): 반복되는 packet datapath 를 Host kernel 에서 처리하는 backend. Evidence: L5682L5682, L5729L5729.
- **QEMU virtio backend** (process): vhost-net 을 쓰지 않을 때 datapath backend 를 맡는 쪽. Evidence: L5681L5681, L5755L5755.
- **virtqueue** (queue): Guest 와 Host backend 가 I/O buffer 를 주고받는 descriptor 기반 공유 큐. Evidence: L5680L5680, L5731L5731.
- **Guest** (service): packet 이 올라가 애플리케이션 socket 에 닿는 쪽. Evidence: L5677L5678, L5733L5739.
## Relationships
- **Linux Bridge /
Routing / NAT → TAP:** Ethernet Frame. Evidence: L5671L5671, L5712L5714.
Routing / NAT → TAP:** Ethernet Frame. Evidence: L5684L5684, L5725L5727.
- **Physical NIC Driver → Linux Bridge /
Routing / NAT:** L2 forwarding. Evidence: L5672L5672, L5710L5712.
- **libvirt → QEMU:** NIC/network configuration. Evidence: L5663L5663, L5691L5693.
- **Physical NIC → Physical NIC Driver:** Ethernet Frame. Evidence: L5671L5671, L5708L5710.
- **QEMU virtio backend → virtqueue:** Data Path. Evidence: L5729L5729, L5742L5744.
- **QEMU → virtio-net Device Model:** QEMU process. Evidence: L5666L5666, L5693L5695.
- **virtio-net Device Model → QEMU virtio backend:** control. Evidence: L5668L5668, L5700L5700.
- **virtio-net Device Model → vhost-net:** vhost-net backend 설정. Evidence: L5700L5700, L5716L5716.
- **TAP → QEMU virtio backend:** data. Evidence: L5729L5729, L5740L5742.
- **TAP → vhost-net:** vhost-net 사용. Evidence: L5703L5703, L5714L5716.
- **vhost-net → virtqueue:** Data Path. Evidence: L5703L5703, L5716L5718.
- **virsh → libvirt:** Control / Setup. Evidence: L5662L5663, L5684L5691.
- **virtqueue → Guest:** virtio-net Frontend Driver. Evidence: L5664L5664, L5718L5726.
Routing / NAT:** L2 forwarding. Evidence: L5685L5685, L5723L5725.
- **libvirt → QEMU:** NIC config. Evidence: L5676L5676, L5704L5706.
- **Physical NIC → Physical NIC Driver:** Ethernet Frame. Evidence: L5684L5684, L5721L5723.
- **QEMU virtio backend → virtqueue:** Data Path. Evidence: L5742L5742, L5755L5757.
- **QEMU → virtio-net Device Model:** QEMU process. Evidence: L5679L5679, L5706L5708.
- **virtio-net Device Model → QEMU virtio backend:** control. Evidence: L5681L5681, L5713L5713.
- **virtio-net Device Model → vhost-net:** vhost-net backend 설정. Evidence: L5713L5713, L5729L5729.
- **TAP → QEMU virtio backend:** data. Evidence: L5742L5742, L5753L5755.
- **TAP → vhost-net:** vhost-net 사용. Evidence: L5716L5716, L5727L5729.
- **vhost-net → virtqueue:** Data Path. Evidence: L5716L5716, L5729L5731.
- **virsh → libvirt:** Control / Setup. Evidence: L5675L5676, L5697L5704.
- **virtqueue → Guest:** frontend. Evidence: L5677L5677, L5731L5739.
@@ -1,6 +1,6 @@
# control/setup path 는 QEMU 를 지나고 data path 는 backend 에 따라 QEMU 를 지나기도 안 지나기도 한다
# Question: 설정하는 경로와 packet 이 흐르는 경로는 어디에서 갈라지고, 그 두 갈래 가운데 어느 쪽이 QEMU 사용자 공간을 지나는가
direction: down
direction: right
n0: "virsh" {
shape: rectangle
}
@@ -38,7 +38,7 @@ n11: "Guest" {
shape: rectangle
}
n0 -> n1: "Control / Setup"
n1 -> n2: "NIC/network configuration"
n1 -> n2: "NIC config"
n2 -> n3: "QEMU process"
n4 -> n5: "Ethernet Frame"
n5 -> n6: "L2 forwarding"
@@ -49,4 +49,4 @@ n7 -> n8: "vhost-net 사용"
n7 -> n9
n8 -> n10: "Data Path"
n9 -> n10: "Data Path"
n10 -> n11: "virtio-net Frontend Driver"
n10 -> n11: "frontend"
@@ -0,0 +1,30 @@
digraph techviz {
graph [rankdir=LR, splines=ortho, nodesep=0.55, ranksep=0.85];
node [fontname=Helvetica, fontsize=11, margin="0.18,0.12", style="rounded,filled", fillcolor=white, color="#2d4357", penwidth=1.5];
edge [fontname=Helvetica, fontsize=10, color="#364b5f", penwidth=1.4, arrowsize=0.75];
n0 [label="virsh", shape=box, style="rounded,filled"];
n1 [label="libvirt", shape=box, style="rounded,filled"];
n2 [label="QEMU", shape=box, style="rounded,filled"];
n3 [label="virtio-net Device Model", shape=box, style="rounded,filled"];
n4 [label="Physical NIC", shape=box, style="rounded,filled"];
n5 [label="Physical NIC Driver", shape=box, style="rounded,filled"];
n6 [label="Linux Bridge /\nRouting / NAT", shape=box, style="rounded,filled"];
n7 [label="TAP", shape=box, style="rounded,filled"];
n8 [label="vhost-net", shape=box, style="rounded,filled"];
n9 [label="QEMU virtio backend", shape=box, style="rounded,filled"];
n10 [label="virtqueue", shape=oval, style="rounded,filled"];
n11 [label="Guest", shape=box, style="rounded,filled"];
n0 -> n1 [label="Control / Setup", style=solid];
n1 -> n2 [label="NIC config", style=solid];
n2 -> n3 [label="QEMU process", style=solid];
n4 -> n5 [label="Ethernet Frame", style=solid];
n5 -> n6 [label="L2 forwarding", style=solid];
n6 -> n7 [label="Ethernet Frame", style=solid];
n3 -> n8 [label="vhost-net backend 설정", style=solid];
n3 -> n9 [label="", style=solid];
n7 -> n8 [label="vhost-net 사용", style=solid];
n7 -> n9 [label="", style=solid];
n8 -> n10 [label="Data Path", style=solid];
n9 -> n10 [label="Data Path", style=solid];
n10 -> n11 [label="frontend", style=solid];
}
@@ -1,110 +1,110 @@
<?xml version="1.0" encoding="UTF-8"?>
<mxfile host="app.diagrams.net" modified="2026-07-23T00:00:00.000Z" agent="techviz-harness" version="24.7.17" type="device">
<diagram id="packet-control-and-data-paths" name="control/setup path 는 QEMU 를 지나고 data path 는 backend 에 따라 QEMU 를 지나기도 안 지나기도 한다">
<mxGraphModel dx="680" dy="1296" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1296" math="0" shadow="0">
<mxGraphModel dx="2249" dy="390" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="2249" pageHeight="1169" math="0" shadow="0">
<root>
<mxCell id="0"/>
<mxCell id="1" parent="0"/>
<mxCell id="n_virsh" value="virsh&lt;br/&gt;User" tooltip="VM/network 관리 CLI. packet datapath 에는 직접 참여하지 않는다. | Evidence: L5662-L5662, L5687-L5689" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="90.5" y="60.0" width="150.0" height="71.0" as="geometry"/>
<mxCell id="n_virsh" value="virsh&lt;br/&gt;User" tooltip="VM/network 관리 CLI. packet datapath 에는 직접 참여하지 않는다. | Evidence: L5675-L5675, L5700-L5702" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="70.0" y="99.0" width="150.0" height="71.0" as="geometry"/>
</mxCell>
<mxCell id="n_physical-nic" value="Physical NIC" tooltip="밖에서 온 packet 이 처음 닿는 하드웨어. | Evidence: L5706-L5708" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="300.5" y="63.5" width="150.0" height="64.0" as="geometry"/>
<mxCell id="n_physical-nic" value="Physical NIC" tooltip="밖에서 온 packet 이 처음 닿는 하드웨어. | Evidence: L5719-L5721" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="70.0" y="242.0" width="150.0" height="64.0" as="geometry"/>
</mxCell>
<mxCell id="n_libvirt" value="libvirt" tooltip="VM lifecycle 과 NIC/network configuration 을 관리한다. | Evidence: L5663-L5663, L5691-L5691" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="82.0" y="227.0" width="150.0" height="64.0" as="geometry"/>
<mxCell id="n_libvirt" value="libvirt" tooltip="VM lifecycle 과 NIC/network configuration 을 관리한다. | Evidence: L5676-L5676, L5704-L5704" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="388.5" y="102.5" width="150.0" height="64.0" as="geometry"/>
</mxCell>
<mxCell id="n_physical-nic-driver" value="Physical NIC Driver" tooltip="그 하드웨어를 제어하는 Host kernel driver. | Evidence: L5674-L5674, L5710-L5710" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="292.0" y="227.0" width="167.0" height="64.0" as="geometry"/>
<mxCell id="n_physical-nic-driver" value="Physical NIC Driver" tooltip="그 하드웨어를 제어하는 Host kernel driver. | Evidence: L5687-L5687, L5723-L5723" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="380.0" y="238.5" width="167.0" height="64.0" as="geometry"/>
</mxCell>
<mxCell id="n_qemu" value="QEMU&lt;br/&gt;Host Userspace" tooltip="실제로 실행되는 프로세스. 이 안에 virtio-net device model 이 있다. | Evidence: L5666-L5666, L5693-L5693" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="90.5" y="388.5" width="150.0" height="71.0" as="geometry"/>
<mxCell id="n_qemu" value="QEMU&lt;br/&gt;Host Userspace" tooltip="실제로 실행되는 프로세스. 이 안에 virtio-net device model 이 있다. | Evidence: L5679-L5679, L5706-L5706" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="707.0" y="94.0" width="150.0" height="71.0" as="geometry"/>
</mxCell>
<mxCell id="n_linux-bridge" value="Linux Bridge /
Routing / NAT" tooltip="L2 software switch 와 L3 routing, 주소 변환이 놓이는 자리. | Evidence: L5672-L5673, L5712-L5712" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="300.5" y="387.0" width="150.0" height="74.0" as="geometry"/>
Routing / NAT" tooltip="L2 software switch 와 L3 routing, 주소 변환이 놓이는 자리. | Evidence: L5685-L5686, L5725-L5725" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="707.0" y="237.0" width="150.0" height="74.0" as="geometry"/>
</mxCell>
<mxCell id="n_virtio-net-device-model" value="virtio-net Device Model&lt;br/&gt;virtual NIC 생성&lt;br/&gt;feature negotiation&lt;br/&gt;virtqueue 설정&lt;br/&gt;vhost-net backend 설정" tooltip="control/setup path 가 닿는 자리. 장치를 만들고 backend 를 연결한다. | Evidence: L5666-L5666, L5695-L5700" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;strokeColor=#2563eb;strokeWidth=2;" vertex="1" parent="1">
<mxGeometry x="70.0" y="557.0" width="174.0" height="142.0" as="geometry"/>
<mxCell id="n_virtio-net-device-model" value="virtio-net Device Model&lt;br/&gt;virtual NIC 생성&lt;br/&gt;feature negotiation&lt;br/&gt;virtqueue 설정&lt;br/&gt;vhost-net backend 설정" tooltip="control/setup path 가 닿는 자리. 장치를 만들고 backend 를 연결한다. | Evidence: L5679-L5679, L5708-L5713" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;strokeColor=#2563eb;strokeWidth=2;" vertex="1" parent="1">
<mxGeometry x="1017.0" y="60.0" width="174.0" height="142.0" as="geometry"/>
</mxCell>
<mxCell id="n_tap" value="TAP&lt;br/&gt;Host-side interface" tooltip="VM 의 Ethernet frame 과 Host Linux networking 을 잇는 접점. | Evidence: L5671-L5671, L5714-L5714" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="304.0" y="592.5" width="167.0" height="71.0" as="geometry"/>
<mxCell id="n_tap" value="TAP&lt;br/&gt;Host-side interface" tooltip="VM 의 Ethernet frame 과 Host Linux networking 을 잇는 접점. | Evidence: L5684-L5684, L5727-L5727" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="1020.5" y="274.0" width="167.0" height="71.0" as="geometry"/>
</mxCell>
<mxCell id="n_qemu-virtio-backend" value="QEMU virtio backend&lt;br/&gt;QEMU Userspace" tooltip="vhost-net 을 쓰지 않을 때 datapath backend 를 맡는 쪽. | Evidence: L5668-L5668, L5742-L5742" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="82.0" y="795.0" width="167.0" height="71.0" as="geometry"/>
<mxCell id="n_qemu-virtio-backend" value="QEMU virtio backend&lt;br/&gt;QEMU Userspace" tooltip="vhost-net 을 쓰지 않을 때 datapath backend 를 맡는 쪽. | Evidence: L5681-L5681, L5755-L5755" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="1351.0" y="95.5" width="167.0" height="71.0" as="geometry"/>
</mxCell>
<mxCell id="n_vhost-net" value="vhost-net&lt;br/&gt;Host Kernel" tooltip="반복되는 packet datapath 를 Host kernel 에서 처리하는 backend. | Evidence: L5669-L5669, L5716-L5716" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="309.0" y="795.0" width="150.0" height="71.0" as="geometry"/>
<mxCell id="n_vhost-net" value="vhost-net&lt;br/&gt;Host Kernel" tooltip="반복되는 packet datapath 를 Host kernel 에서 처리하는 backend. | Evidence: L5682-L5682, L5729-L5729" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="1359.5" y="238.5" width="150.0" height="71.0" as="geometry"/>
</mxCell>
<mxCell id="n_virtqueue" value="virtqueue&lt;br/&gt;shared queue" tooltip="Guest 와 Host backend 가 I/O buffer 를 주고받는 descriptor 기반 공유 큐. | Evidence: L5667-L5667, L5718-L5718" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="195.5" y="962.0" width="150.0" height="71.0" as="geometry"/>
<mxCell id="n_virtqueue" value="virtqueue&lt;br/&gt;shared queue" tooltip="Guest 와 Host backend 가 I/O buffer 를 주고받는 descriptor 기반 공유 큐. | Evidence: L5680-L5680, L5731-L5731" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="1678.0" y="167.0" width="150.0" height="71.0" as="geometry"/>
</mxCell>
<mxCell id="n_guest" value="Guest&lt;br/&gt;virtio-net Frontend Driver&lt;br/&gt;Guest TCP/IP Stack&lt;br/&gt;Socket&lt;br/&gt;Keycloak" tooltip="packet 이 올라가 애플리케이션 socket 에 닿는 쪽. | Evidence: L5664-L5665, L5720-L5726" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="162.5" y="1129.0" width="216.0" height="122.0" as="geometry"/>
<mxCell id="n_guest" value="Guest&lt;br/&gt;virtio-net Frontend Driver&lt;br/&gt;Guest TCP/IP Stack&lt;br/&gt;Socket&lt;br/&gt;Keycloak" tooltip="packet 이 올라가 애플리케이션 socket 에 닿는 쪽. | Evidence: L5677-L5678, L5733-L5739" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="1988.0" y="141.5" width="216.0" height="122.0" as="geometry"/>
</mxCell>
<mxCell id="e_bridge-to-tap" value="Ethernet Frame" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_linux-bridge" target="n_tap">
<mxGeometry relative="1" as="geometry">
<mxPoint x="381.5" y="498.8" as="offset"/>
<mxPoint x="962.8" y="291.8" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="e_driver-to-bridge" value="L2 forwarding" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_physical-nic-driver" target="n_linux-bridge">
<mxGeometry relative="1" as="geometry">
<mxPoint x="399.5" y="339.0" as="offset"/>
<mxPoint x="651.0" y="272.2" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="e_libvirt-to-qemu" value="NIC/network configuration" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_libvirt" target="n_qemu">
<mxCell id="e_libvirt-to-qemu" value="NIC config" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_libvirt" target="n_qemu">
<mxGeometry relative="1" as="geometry">
<mxPoint x="161.2" y="311.8" as="offset"/>
<mxPoint x="646.8" y="132.0" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="e_nic-to-driver" value="Ethernet Frame" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_physical-nic" target="n_physical-nic-driver">
<mxGeometry relative="1" as="geometry">
<mxPoint x="399.5" y="177.2" as="offset"/>
<mxPoint x="324.0" y="272.2" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="e_qemu-backend-to-virtqueue" value="Data Path" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_qemu-virtio-backend" target="n_virtqueue">
<mxGeometry relative="1" as="geometry">
<mxPoint x="213.5" y="886.0" as="offset"/>
<mxPoint x="1622.0" y="162.2" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="e_qemu-to-device-model" value="QEMU process" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_qemu" target="n_virtio-net-device-model">
<mxGeometry relative="1" as="geometry">
<mxPoint x="161.2" y="480.2" as="offset"/>
<mxPoint x="961.0" y="130.2" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="e_setup-qemu-backend" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_virtio-net-device-model" target="n_qemu-virtio-backend">
<mxGeometry relative="1" as="geometry">
<mxPoint x="152.2" y="719.0" as="offset"/>
<mxPoint x="1271.0" y="94.0" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="e_setup-vhost-backend" value="vhost-net backend 설정" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_virtio-net-device-model" target="n_vhost-net">
<mxGeometry relative="1" as="geometry">
<mxPoint x="270.5" y="719.0" as="offset"/>
<mxPoint x="1299.2" y="202.5" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="e_tap-to-qemu-backend" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_tap" target="n_qemu-virtio-backend">
<mxGeometry relative="1" as="geometry">
<mxPoint x="276.5" y="701.2" as="offset"/>
<mxPoint x="1293.2" y="220.2" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="e_tap-to-vhost" value="vhost-net 사용" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_tap" target="n_vhost-net">
<mxGeometry relative="1" as="geometry">
<mxPoint x="394.8" y="701.2" as="offset"/>
<mxPoint x="1297.5" y="300.8" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="e_vhost-to-virtqueue" value="Data Path" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_vhost-net" target="n_virtqueue">
<mxGeometry relative="1" as="geometry">
<mxPoint x="331.8" y="886.0" as="offset"/>
<mxPoint x="1617.8" y="242.8" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="e_virsh-to-libvirt" value="Control / Setup" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_virsh" target="n_libvirt">
<mxGeometry relative="1" as="geometry">
<mxPoint x="161.2" y="151.0" as="offset"/>
<mxPoint x="304.2" y="106.5" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="e_virtqueue-to-guest" value="virtio-net Frontend Driver" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_virtqueue" target="n_guest">
<mxCell id="e_virtqueue-to-guest" value="frontend" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_virtqueue" target="n_guest">
<mxGeometry relative="1" as="geometry">
<mxPoint x="294.5" y="1081.0" as="offset"/>
<mxPoint x="1908.0" y="174.5" as="offset"/>
</mxGeometry>
</mxCell>
</root>
@@ -6,10 +6,10 @@
{
"id": "edge-bridge-to-tap",
"type": "arrow",
"x": 375.5,
"y": 461.0,
"width": 12.0,
"height": 131.5,
"x": 857.0,
"y": 274.0,
"width": 163.5,
"height": 35.5,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
@@ -36,16 +36,16 @@
0.0
],
[
0.0,
65.75
81.75,
0.0
],
[
12.0,
65.75
81.75,
35.5
],
[
12.0,
131.5
163.5,
35.5
]
],
"lastCommittedPoint": null,
@@ -66,8 +66,8 @@
{
"id": "edge-label-bridge-to-tap",
"type": "text",
"x": 325.5,
"y": 486.75,
"x": 906.75,
"y": 279.75,
"width": 112,
"height": 24,
"angle": 0,
@@ -105,10 +105,10 @@
{
"id": "edge-driver-to-bridge",
"type": "arrow",
"x": 375.5,
"y": 291.0,
"width": 0.0,
"height": 96.0,
"x": 547.0,
"y": 270.5,
"width": 160.0,
"height": 3.5,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
@@ -135,16 +135,16 @@
0.0
],
[
0.0,
48.0
80.0,
0.0
],
[
0.0,
48.0
80.0,
3.5
],
[
0.0,
96.0
160.0,
3.5
]
],
"lastCommittedPoint": null,
@@ -165,8 +165,8 @@
{
"id": "edge-label-driver-to-bridge",
"type": "text",
"x": 347.5,
"y": 327.0,
"x": 599.0,
"y": 260.25,
"width": 104,
"height": 24,
"angle": 0,
@@ -204,10 +204,10 @@
{
"id": "edge-libvirt-to-qemu",
"type": "arrow",
"x": 157.0,
"y": 291.0,
"width": 8.5,
"height": 97.5,
"x": 538.5,
"y": 129.5,
"width": 168.5,
"height": 5.0,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
@@ -231,19 +231,19 @@
"points": [
[
0.0,
5.0
],
[
84.25,
5.0
],
[
84.25,
0.0
],
[
0.0,
48.75
],
[
8.5,
48.75
],
[
8.5,
97.5
168.5,
0.0
]
],
"lastCommittedPoint": null,
@@ -264,9 +264,9 @@
{
"id": "edge-label-libvirt-to-qemu",
"type": "text",
"x": 61.25,
"y": 299.75,
"width": 200,
"x": 601.75,
"y": 120.0,
"width": 90,
"height": 24,
"angle": 0,
"strokeColor": "#1e1e1e",
@@ -292,21 +292,21 @@
"locked": false,
"fontSize": 13,
"fontFamily": 5,
"text": "NIC/network configuration",
"text": "NIC config",
"textAlign": "center",
"verticalAlign": "middle",
"containerId": null,
"originalText": "NIC/network configuration",
"originalText": "NIC config",
"autoResize": true,
"lineHeight": 1.25
},
{
"id": "edge-nic-to-driver",
"type": "arrow",
"x": 375.5,
"y": 127.5,
"width": 0.0,
"height": 99.5,
"x": 220.0,
"y": 270.5,
"width": 160.0,
"height": 3.5,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
@@ -330,19 +330,19 @@
"points": [
[
0.0,
3.5
],
[
80.0,
3.5
],
[
80.0,
0.0
],
[
0.0,
49.75
],
[
0.0,
49.75
],
[
0.0,
99.5
160.0,
0.0
]
],
"lastCommittedPoint": null,
@@ -363,8 +363,8 @@
{
"id": "edge-label-nic-to-driver",
"type": "text",
"x": 343.5,
"y": 165.25,
"x": 268.0,
"y": 260.25,
"width": 112,
"height": 24,
"angle": 0,
@@ -402,10 +402,10 @@
{
"id": "edge-qemu-backend-to-virtqueue",
"type": "arrow",
"x": 165.5,
"y": 866.0,
"width": 96.0,
"height": 96.0,
"x": 1518.0,
"y": 131.0,
"width": 160.0,
"height": 62.5,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
@@ -432,16 +432,16 @@
0.0
],
[
0.0,
48.0
80.0,
0.0
],
[
96.0,
48.0
80.0,
62.5
],
[
96.0,
96.0
160.0,
62.5
]
],
"lastCommittedPoint": null,
@@ -462,8 +462,8 @@
{
"id": "edge-label-qemu-backend-to-virtqueue",
"type": "text",
"x": 168.5,
"y": 874.0,
"x": 1577.0,
"y": 150.25,
"width": 90,
"height": 24,
"angle": 0,
@@ -501,10 +501,10 @@
{
"id": "edge-qemu-to-device-model",
"type": "arrow",
"x": 157.0,
"y": 459.5,
"width": 8.5,
"height": 97.5,
"x": 857.0,
"y": 129.5,
"width": 160.0,
"height": 1.5,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
@@ -527,20 +527,20 @@
"locked": false,
"points": [
[
8.5,
0.0,
0.0
],
[
8.5,
48.75
80.0,
0.0
],
[
0.0,
48.75
80.0,
1.5
],
[
0.0,
97.5
160.0,
1.5
]
],
"lastCommittedPoint": null,
@@ -561,8 +561,8 @@
{
"id": "edge-label-qemu-to-device-model",
"type": "text",
"x": 113.25,
"y": 468.25,
"x": 913.0,
"y": 118.25,
"width": 96,
"height": 24,
"angle": 0,
@@ -600,10 +600,10 @@
{
"id": "edge-setup-qemu-backend",
"type": "arrow",
"x": 148.0,
"y": 699.0,
"width": 8.5,
"height": 96.0,
"x": 1191.0,
"y": 122.0,
"width": 160.0,
"height": 0.0,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
@@ -630,16 +630,16 @@
0.0
],
[
0.0,
48.0
80.0,
0.0
],
[
8.5,
48.0
80.0,
0.0
],
[
8.5,
96.0
160.0,
0.0
]
],
"lastCommittedPoint": null,
@@ -660,10 +660,10 @@
{
"id": "edge-setup-vhost-backend",
"type": "arrow",
"x": 166.0,
"y": 699.0,
"width": 209.0,
"height": 96.0,
"x": 1191.0,
"y": 140.0,
"width": 168.5,
"height": 125.0,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
@@ -690,16 +690,16 @@
0.0
],
[
0.0,
48.0
84.25,
0.0
],
[
209.0,
48.0
84.25,
125.0
],
[
209.0,
96.0
168.5,
125.0
]
],
"lastCommittedPoint": null,
@@ -720,8 +720,8 @@
{
"id": "edge-label-setup-vhost-backend",
"type": "text",
"x": 190.5,
"y": 707.0,
"x": 1219.25,
"y": 190.5,
"width": 160,
"height": 24,
"angle": 0,
@@ -759,10 +759,10 @@
{
"id": "edge-tap-to-qemu-backend",
"type": "arrow",
"x": 174.5,
"y": 663.5,
"width": 204.0,
"height": 131.5,
"x": 1187.5,
"y": 140.0,
"width": 163.5,
"height": 160.5,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
@@ -785,20 +785,20 @@
"locked": false,
"points": [
[
204.0,
0.0,
160.5
],
[
81.75,
160.5
],
[
81.75,
0.0
],
[
204.0,
65.75
],
[
0.0,
65.75
],
[
0.0,
131.5
163.5,
0.0
]
],
"lastCommittedPoint": null,
@@ -819,10 +819,10 @@
{
"id": "edge-tap-to-vhost",
"type": "arrow",
"x": 393.0,
"y": 663.5,
"width": 3.5,
"height": 131.5,
"x": 1187.5,
"y": 283.0,
"width": 172.0,
"height": 35.5,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
@@ -845,20 +845,20 @@
"locked": false,
"points": [
[
3.5,
0.0,
35.5
],
[
86.0,
35.5
],
[
86.0,
0.0
],
[
3.5,
65.75
],
[
0.0,
65.75
],
[
0.0,
131.5
172.0,
0.0
]
],
"lastCommittedPoint": null,
@@ -879,8 +879,8 @@
{
"id": "edge-label-tap-to-vhost",
"type": "text",
"x": 346.75,
"y": 689.25,
"x": 1249.5,
"y": 288.75,
"width": 96,
"height": 24,
"angle": 0,
@@ -918,10 +918,10 @@
{
"id": "edge-vhost-to-virtqueue",
"type": "arrow",
"x": 279.5,
"y": 866.0,
"width": 104.5,
"height": 96.0,
"x": 1509.5,
"y": 211.5,
"width": 168.5,
"height": 62.5,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
@@ -944,20 +944,20 @@
"locked": false,
"points": [
[
104.5,
0.0,
62.5
],
[
84.25,
62.5
],
[
84.25,
0.0
],
[
104.5,
48.0
],
[
0.0,
48.0
],
[
0.0,
96.0
168.5,
0.0
]
],
"lastCommittedPoint": null,
@@ -978,8 +978,8 @@
{
"id": "edge-label-vhost-to-virtqueue",
"type": "text",
"x": 286.75,
"y": 874.0,
"x": 1572.75,
"y": 230.75,
"width": 90,
"height": 24,
"angle": 0,
@@ -1017,10 +1017,10 @@
{
"id": "edge-virsh-to-libvirt",
"type": "arrow",
"x": 157.0,
"y": 131.0,
"width": 8.5,
"height": 96.0,
"x": 220.0,
"y": 134.5,
"width": 168.5,
"height": 0.0,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
@@ -1043,20 +1043,20 @@
"locked": false,
"points": [
[
8.5,
0.0,
0.0
],
[
8.5,
48.0
84.25,
0.0
],
[
0.0,
48.0
84.25,
0.0
],
[
0.0,
96.0
168.5,
0.0
]
],
"lastCommittedPoint": null,
@@ -1077,8 +1077,8 @@
{
"id": "edge-label-virsh-to-libvirt",
"type": "text",
"x": 101.25,
"y": 139.0,
"x": 244.25,
"y": 94.5,
"width": 120,
"height": 24,
"angle": 0,
@@ -1116,10 +1116,10 @@
{
"id": "edge-virtqueue-to-guest",
"type": "arrow",
"x": 270.5,
"y": 1033.0,
"width": 0.0,
"height": 96.0,
"x": 1828.0,
"y": 202.5,
"width": 160.0,
"height": 0.0,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
@@ -1146,16 +1146,16 @@
0.0
],
[
0.0,
48.0
80.0,
0.0
],
[
0.0,
48.0
80.0,
0.0
],
[
0.0,
96.0
160.0,
0.0
]
],
"lastCommittedPoint": null,
@@ -1176,9 +1176,9 @@
{
"id": "edge-label-virtqueue-to-guest",
"type": "text",
"x": 190.5,
"y": 1069.0,
"width": 208,
"x": 1863.0,
"y": 162.5,
"width": 90,
"height": 24,
"angle": 0,
"strokeColor": "#1e1e1e",
@@ -1204,19 +1204,19 @@
"locked": false,
"fontSize": 13,
"fontFamily": 5,
"text": "virtio-net Frontend Driver",
"text": "frontend",
"textAlign": "center",
"verticalAlign": "middle",
"containerId": null,
"originalText": "virtio-net Frontend Driver",
"originalText": "frontend",
"autoResize": true,
"lineHeight": 1.25
},
{
"id": "node-virsh",
"type": "rectangle",
"x": 90.5,
"y": 60.0,
"x": 70.0,
"y": 99.0,
"width": 150.0,
"height": 71.0,
"angle": 0,
@@ -1245,8 +1245,8 @@
{
"id": "node-label-virsh",
"type": "text",
"x": 100.5,
"y": 70.0,
"x": 80.0,
"y": 109.0,
"width": 130.0,
"height": 51.0,
"angle": 0,
@@ -1284,8 +1284,8 @@
{
"id": "node-physical-nic",
"type": "rectangle",
"x": 300.5,
"y": 63.5,
"x": 70.0,
"y": 242.0,
"width": 150.0,
"height": 64.0,
"angle": 0,
@@ -1314,8 +1314,8 @@
{
"id": "node-label-physical-nic",
"type": "text",
"x": 310.5,
"y": 73.5,
"x": 80.0,
"y": 252.0,
"width": 130.0,
"height": 44.0,
"angle": 0,
@@ -1353,8 +1353,8 @@
{
"id": "node-libvirt",
"type": "rectangle",
"x": 82.0,
"y": 227.0,
"x": 388.5,
"y": 102.5,
"width": 150.0,
"height": 64.0,
"angle": 0,
@@ -1383,8 +1383,8 @@
{
"id": "node-label-libvirt",
"type": "text",
"x": 92.0,
"y": 237.0,
"x": 398.5,
"y": 112.5,
"width": 130.0,
"height": 44.0,
"angle": 0,
@@ -1422,8 +1422,8 @@
{
"id": "node-physical-nic-driver",
"type": "rectangle",
"x": 292.0,
"y": 227.0,
"x": 380.0,
"y": 238.5,
"width": 167.0,
"height": 64.0,
"angle": 0,
@@ -1452,8 +1452,8 @@
{
"id": "node-label-physical-nic-driver",
"type": "text",
"x": 302.0,
"y": 237.0,
"x": 390.0,
"y": 248.5,
"width": 147.0,
"height": 44.0,
"angle": 0,
@@ -1491,8 +1491,8 @@
{
"id": "node-qemu",
"type": "rectangle",
"x": 90.5,
"y": 388.5,
"x": 707.0,
"y": 94.0,
"width": 150.0,
"height": 71.0,
"angle": 0,
@@ -1521,8 +1521,8 @@
{
"id": "node-label-qemu",
"type": "text",
"x": 100.5,
"y": 398.5,
"x": 717.0,
"y": 104.0,
"width": 130.0,
"height": 51.0,
"angle": 0,
@@ -1560,8 +1560,8 @@
{
"id": "node-linux-bridge",
"type": "rectangle",
"x": 300.5,
"y": 387.0,
"x": 707.0,
"y": 237.0,
"width": 150.0,
"height": 74.0,
"angle": 0,
@@ -1590,8 +1590,8 @@
{
"id": "node-label-linux-bridge",
"type": "text",
"x": 310.5,
"y": 397.0,
"x": 717.0,
"y": 247.0,
"width": 130.0,
"height": 54.0,
"angle": 0,
@@ -1629,8 +1629,8 @@
{
"id": "node-virtio-net-device-model",
"type": "rectangle",
"x": 70.0,
"y": 557.0,
"x": 1017.0,
"y": 60.0,
"width": 174.0,
"height": 142.0,
"angle": 0,
@@ -1659,8 +1659,8 @@
{
"id": "node-label-virtio-net-device-model",
"type": "text",
"x": 80.0,
"y": 567.0,
"x": 1027.0,
"y": 70.0,
"width": 154.0,
"height": 122.0,
"angle": 0,
@@ -1698,8 +1698,8 @@
{
"id": "node-tap",
"type": "rectangle",
"x": 304.0,
"y": 592.5,
"x": 1020.5,
"y": 274.0,
"width": 167.0,
"height": 71.0,
"angle": 0,
@@ -1728,8 +1728,8 @@
{
"id": "node-label-tap",
"type": "text",
"x": 314.0,
"y": 602.5,
"x": 1030.5,
"y": 284.0,
"width": 147.0,
"height": 51.0,
"angle": 0,
@@ -1767,8 +1767,8 @@
{
"id": "node-qemu-virtio-backend",
"type": "rectangle",
"x": 82.0,
"y": 795.0,
"x": 1351.0,
"y": 95.5,
"width": 167.0,
"height": 71.0,
"angle": 0,
@@ -1797,8 +1797,8 @@
{
"id": "node-label-qemu-virtio-backend",
"type": "text",
"x": 92.0,
"y": 805.0,
"x": 1361.0,
"y": 105.5,
"width": 147.0,
"height": 51.0,
"angle": 0,
@@ -1836,8 +1836,8 @@
{
"id": "node-vhost-net",
"type": "rectangle",
"x": 309.0,
"y": 795.0,
"x": 1359.5,
"y": 238.5,
"width": 150.0,
"height": 71.0,
"angle": 0,
@@ -1866,8 +1866,8 @@
{
"id": "node-label-vhost-net",
"type": "text",
"x": 319.0,
"y": 805.0,
"x": 1369.5,
"y": 248.5,
"width": 130.0,
"height": 51.0,
"angle": 0,
@@ -1905,8 +1905,8 @@
{
"id": "node-virtqueue",
"type": "rectangle",
"x": 195.5,
"y": 962.0,
"x": 1678.0,
"y": 167.0,
"width": 150.0,
"height": 71.0,
"angle": 0,
@@ -1935,8 +1935,8 @@
{
"id": "node-label-virtqueue",
"type": "text",
"x": 205.5,
"y": 972.0,
"x": 1688.0,
"y": 177.0,
"width": 130.0,
"height": 51.0,
"angle": 0,
@@ -1974,8 +1974,8 @@
{
"id": "node-guest",
"type": "rectangle",
"x": 162.5,
"y": 1129.0,
"x": 1988.0,
"y": 141.5,
"width": 216.0,
"height": 122.0,
"angle": 0,
@@ -2004,8 +2004,8 @@
{
"id": "node-label-guest",
"type": "text",
"x": 172.5,
"y": 1139.0,
"x": 1998.0,
"y": 151.5,
"width": 196.0,
"height": 102.0,
"angle": 0,
@@ -2,21 +2,22 @@
"harness_version": "0.2.0",
"spec_id": "packet-control-and-data-paths",
"spec_version": "1.1",
"spec_sha256": "fa2d5e22352fa6cf975665cd431a236deaa4ae88ea0eb8461bb1b3c8b0b03dc6",
"spec_sha256": "5ad8e6e91ced885c14673ed8b75153b054376278d256fa39f26aba1df9ed947b",
"source_context": {
"document": "docs/virtualization/final/document.md",
"document_sha256": "181e2b3cc8a45bae81e7e8193d026937c4d586ae4495d3b2c323eb7e6abcfadd",
"document_sha256": "8c4ecc64c8cea9a4450ed7131fdd9cb2048dc092b66cd969f6346ed77887c210",
"anchor": {
"kind": "heading",
"value": "125. 최종 기준 구조",
"line": 5682
"line": 5695
}
},
"outputs": [
"packet-control-and-data-paths.svg",
"packet-control-and-data-paths.drawio",
"packet-control-and-data-paths.mmd",
"packet-control-and-data-paths.d2",
"packet-control-and-data-paths.dot",
"packet-control-and-data-paths.drawio",
"packet-control-and-data-paths.excalidraw",
"packet-control-and-data-paths.alt.md"
],
@@ -27,5 +28,15 @@
"reference_ids": [
"payment-event-flow"
],
"diagram_only": true
"diagram_only": true,
"source_spec_file_sha256": "cf22eb6977c1e3e1d493881d30604c345489c58bce67e8b76e854f4117ba2788",
"output_sha256": {
"packet-control-and-data-paths.svg": "82845b88520456296032041122e937ae087fb0521dfcdd510d589f1cbe12eb70",
"packet-control-and-data-paths.mmd": "53a570ae187c9f35848eab3df4401d73ebd5be465a05067fb231ff36358a68ed",
"packet-control-and-data-paths.d2": "316908b56f29c6b508d133c8c5b6baa9dfe6aa5348540f8877fd798480a21039",
"packet-control-and-data-paths.dot": "32fbc68beb83b176eef7e12b966cb8eb6e589881dc6d6a4aeb77434a0c0bc396",
"packet-control-and-data-paths.drawio": "ee81cf96da5e4d6b70c8dc35ad0f77c043d493157e9be49608e7ccf5b6aba2a7",
"packet-control-and-data-paths.excalidraw": "dec21227b4a33abfdcb8087f417933dd3f00c3ead46890d505bfb05a8b847c3b",
"packet-control-and-data-paths.alt.md": "c21d6531f69bc6e02826e1306d327920108cf6b010ac94588011a92387f16125"
}
}
@@ -1,6 +1,6 @@
%% control/setup path 는 QEMU 를 지나고 data path 는 backend 에 따라 QEMU 를 지나기도 안 지나기도 한다
%% question: 설정하는 경로와 packet 이 흐르는 경로는 어디에서 갈라지고, 그 두 갈래 가운데 어느 쪽이 QEMU 사용자 공간을 지나는가
flowchart TB
flowchart LR
n0["virsh"]
n1["libvirt"]
n2["QEMU"]
@@ -14,7 +14,7 @@ flowchart TB
n10(["virtqueue"])
n11["Guest"]
n0 -->|"Control / Setup"| n1
n1 -->|"NIC/network configuration"| n2
n1 -->|"NIC config"| n2
n2 -->|"QEMU process"| n3
n4 -->|"Ethernet Frame"| n5
n5 -->|"L2 forwarding"| n6
@@ -25,4 +25,4 @@ flowchart TB
n7 --> n9
n8 -->|"Data Path"| n10
n9 -->|"Data Path"| n10
n10 -->|"virtio-net Frontend Driver"| n11
n10 -->|"frontend"| n11
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="680" height="1296" viewBox="0 0 680 1296" role="img" aria-labelledby="diagram-title diagram-description">
<svg xmlns="http://www.w3.org/2000/svg" width="2249" height="390" viewBox="0 0 2249 390" role="img" aria-labelledby="diagram-title diagram-description">
<title id="diagram-title">control/setup path 는 QEMU 를 지나고 data path 는 backend 에 따라 QEMU 를 지나기도 안 지나기도 한다</title>
<desc id="diagram-description">왼쪽에서 오른쪽으로 읽는다. 위쪽 줄이 control/setup path 다. virsh 에서 libvirt 와 QEMU 를 지나 virtio-net Device Model 에 닿고, 그 device model 이 virtual NIC 생성과 feature negotiation, virtqueue 설정, vhost-net backend 설정을 맡는다. 아래쪽 줄이 data path 다. Physical NIC 에서 Physical NIC Driver 와 Linux Bridge, TAP 까지 온다. 두 줄은 네 번째 열 다음에서 만난다. device model 은 점선으로 backend 둘을 설정하고, TAP 은 실선으로 그 둘 가운데 실제로 쓰는 쪽에 packet 을 넘긴다. vhost-net 을 쓰면 packet 은 Host Kernel 안에서 처리돼 QEMU 사용자 공간을 지나지 않고, QEMU virtio backend 를 쓰면 같은 packet 이 QEMU 사용자 공간을 지난다. 어느 쪽이든 virtqueue 에서 만나 Guest 로 올라간다. 이 Host 가 둘 중 어느 backend 를 쓰고 있는지는 그림이 말하지 않는다.</desc>
<metadata>{&quot;techviz&quot;:{&quot;spec_version&quot;:&quot;1.1&quot;,&quot;id&quot;:&quot;packet-control-and-data-paths&quot;,&quot;profile&quot;:&quot;component-flow&quot;},&quot;source_context&quot;:{&quot;document&quot;:&quot;docs/virtualization/final/document.md&quot;,&quot;document_sha256&quot;:&quot;181e2b3cc8a45bae81e7e8193d026937c4d586ae4495d3b2c323eb7e6abcfadd&quot;,&quot;anchor&quot;:{&quot;kind&quot;:&quot;heading&quot;,&quot;value&quot;:&quot;125. 최종 기준 구조&quot;,&quot;line&quot;:5682}},&quot;evidence_policy&quot;:&quot;Each factual element cites source lines or is marked assumption.&quot;,&quot;diagram_only&quot;:true}</metadata>
<metadata>{&quot;techviz&quot;:{&quot;spec_version&quot;:&quot;1.1&quot;,&quot;id&quot;:&quot;packet-control-and-data-paths&quot;,&quot;profile&quot;:&quot;component-flow&quot;},&quot;source_context&quot;:{&quot;document&quot;:&quot;docs/virtualization/final/document.md&quot;,&quot;document_sha256&quot;:&quot;8c4ecc64c8cea9a4450ed7131fdd9cb2048dc092b66cd969f6346ed77887c210&quot;,&quot;anchor&quot;:{&quot;kind&quot;:&quot;heading&quot;,&quot;value&quot;:&quot;125. 최종 기준 구조&quot;,&quot;line&quot;:5695}},&quot;evidence_policy&quot;:&quot;Each factual element cites source lines or is marked assumption.&quot;,&quot;diagram_only&quot;:true}</metadata>
<defs>
<marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M 0 0 L 10 5 L 0 10 z" />
@@ -49,112 +49,112 @@
.timeline-detail { font-size: 11px; fill: #4b5563; text-anchor: middle; }
</style>
</defs>
<rect class="canvas" width="680" height="1296" />
<polyline class="edge kind-data style-solid emphasis-normal" points="375.5,461.0 375.5,526.8 387.5,526.8 387.5,592.5" data-evidence="5671-5671,5712-5714" />
<rect class="edge-label-bg" x="325.6" y="484.8" width="111.8" height="22" rx="3" />
<text class="edge-label" x="381.5" y="499.8">Ethernet Frame</text>
<polyline class="edge kind-data style-solid emphasis-normal" points="375.5,291.0 375.5,339.0 375.5,339.0 375.5,387.0" data-evidence="5672-5672,5710-5712" />
<rect class="edge-label-bg" x="346.9" y="325.0" width="105.1" height="22" rx="3" />
<text class="edge-label" x="399.5" y="340.0">L2 forwarding</text>
<polyline class="edge kind-control style-solid emphasis-normal" points="157.0,291.0 157.0,339.8 165.5,339.8 165.5,388.5" data-evidence="5663-5663,5691-5693" />
<rect class="edge-label-bg" x="68.5" y="297.8" width="185.5" height="22" rx="3" />
<text class="edge-label" x="161.2" y="312.8">NIC/network configuration</text>
<polyline class="edge kind-data style-solid emphasis-normal" points="375.5,127.5 375.5,177.2 375.5,177.2 375.5,227.0" data-evidence="5671-5671,5708-5710" />
<rect class="edge-label-bg" x="343.6" y="163.2" width="111.8" height="22" rx="3" />
<text class="edge-label" x="399.5" y="178.2">Ethernet Frame</text>
<polyline class="edge kind-data style-solid emphasis-normal" points="165.5,866.0 165.5,914.0 261.5,914.0 261.5,962.0" data-evidence="5729-5729,5742-5744" />
<rect class="edge-label-bg" x="174.3" y="872.0" width="78.3" height="22" rx="3" />
<text class="edge-label" x="213.5" y="887.0">Data Path</text>
<polyline class="edge kind-control style-solid emphasis-normal" points="165.5,459.5 165.5,508.2 157.0,508.2 157.0,557.0" data-evidence="5666-5666,5693-5695" />
<rect class="edge-label-bg" x="112.0" y="466.2" width="98.4" height="22" rx="3" />
<text class="edge-label" x="161.2" y="481.2">QEMU process</text>
<polyline class="edge kind-control style-dashed emphasis-normal" points="148.0,699.0 148.0,747.0 156.5,747.0 156.5,795.0" data-evidence="5668-5668,5700-5700" />
<polyline class="edge kind-control style-dashed emphasis-normal" points="166.0,699.0 166.0,747.0 375.0,747.0 375.0,795.0" data-evidence="5700-5700,5716-5716" />
<rect class="edge-label-bg" x="194.5" y="705.0" width="152.0" height="22" rx="3" />
<text class="edge-label" x="270.5" y="720.0">vhost-net backend 설정</text>
<polyline class="edge kind-data style-solid emphasis-normal" points="378.5,663.5 378.5,729.2 174.5,729.2 174.5,795.0" data-evidence="5729-5729,5740-5742" />
<polyline class="edge kind-data style-solid emphasis-normal" points="396.5,663.5 396.5,729.2 393.0,729.2 393.0,795.0" data-evidence="5703-5703,5714-5716" />
<rect class="edge-label-bg" x="345.6" y="687.2" width="98.4" height="22" rx="3" />
<text class="edge-label" x="394.8" y="702.2">vhost-net 사용</text>
<polyline class="edge kind-data style-solid emphasis-normal" points="384.0,866.0 384.0,914.0 279.5,914.0 279.5,962.0" data-evidence="5703-5703,5716-5718" />
<rect class="edge-label-bg" x="292.6" y="872.0" width="78.3" height="22" rx="3" />
<text class="edge-label" x="331.8" y="887.0">Data Path</text>
<polyline class="edge kind-control style-solid emphasis-normal" points="165.5,131.0 165.5,179.0 157.0,179.0 157.0,227.0" data-evidence="5662-5663,5684-5691" />
<rect class="edge-label-bg" x="102.0" y="137.0" width="118.5" height="22" rx="3" />
<text class="edge-label" x="161.2" y="152.0">Control / Setup</text>
<polyline class="edge kind-data style-solid emphasis-normal" points="270.5,1033.0 270.5,1081.0 270.5,1081.0 270.5,1129.0" data-evidence="5664-5664,5718-5726" />
<rect class="edge-label-bg" x="198.4" y="1067.0" width="192.2" height="22" rx="3" />
<text class="edge-label" x="294.5" y="1082.0">virtio-net Frontend Driver</text>
<rect class="canvas" width="2249" height="390" />
<polyline class="edge kind-data style-solid emphasis-normal" points="857.0,274.0 938.8,274.0 938.8,309.5 1020.5,309.5" data-evidence="5684-5684,5725-5727" />
<rect class="edge-label-bg" x="906.9" y="277.8" width="111.8" height="22" rx="3" />
<text class="edge-label" x="962.8" y="292.8">Ethernet Frame</text>
<polyline class="edge kind-data style-solid emphasis-normal" points="547.0,270.5 627.0,270.5 627.0,274.0 707.0,274.0" data-evidence="5685-5685,5723-5725" />
<rect class="edge-label-bg" x="598.5" y="258.2" width="105.1" height="22" rx="3" />
<text class="edge-label" x="651.0" y="273.2">L2 forwarding</text>
<polyline class="edge kind-control style-solid emphasis-normal" points="538.5,134.5 622.8,134.5 622.8,129.5 707.0,129.5" data-evidence="5676-5676,5704-5706" />
<rect class="edge-label-bg" x="604.2" y="118.0" width="85.0" height="22" rx="3" />
<text class="edge-label" x="646.8" y="133.0">NIC config</text>
<polyline class="edge kind-data style-solid emphasis-normal" points="220.0,274.0 300.0,274.0 300.0,270.5 380.0,270.5" data-evidence="5684-5684,5721-5723" />
<rect class="edge-label-bg" x="268.1" y="258.2" width="111.8" height="22" rx="3" />
<text class="edge-label" x="324.0" y="273.2">Ethernet Frame</text>
<polyline class="edge kind-data style-solid emphasis-normal" points="1518.0,131.0 1598.0,131.0 1598.0,193.5 1678.0,193.5" data-evidence="5742-5742,5755-5757" />
<rect class="edge-label-bg" x="1582.8" y="148.2" width="78.3" height="22" rx="3" />
<text class="edge-label" x="1622.0" y="163.2">Data Path</text>
<polyline class="edge kind-control style-solid emphasis-normal" points="857.0,129.5 937.0,129.5 937.0,131.0 1017.0,131.0" data-evidence="5679-5679,5706-5708" />
<rect class="edge-label-bg" x="911.8" y="116.2" width="98.4" height="22" rx="3" />
<text class="edge-label" x="961.0" y="131.2">QEMU process</text>
<polyline class="edge kind-control style-dashed emphasis-normal" points="1191.0,122.0 1271.0,122.0 1271.0,122.0 1351.0,122.0" data-evidence="5681-5681,5713-5713" />
<polyline class="edge kind-control style-dashed emphasis-normal" points="1191.0,140.0 1275.2,140.0 1275.2,265.0 1359.5,265.0" data-evidence="5713-5713,5729-5729" />
<rect class="edge-label-bg" x="1223.2" y="188.5" width="152.0" height="22" rx="3" />
<text class="edge-label" x="1299.2" y="203.5">vhost-net backend 설정</text>
<polyline class="edge kind-data style-solid emphasis-normal" points="1187.5,300.5 1269.2,300.5 1269.2,140.0 1351.0,140.0" data-evidence="5742-5742,5753-5755" />
<polyline class="edge kind-data style-solid emphasis-normal" points="1187.5,318.5 1273.5,318.5 1273.5,283.0 1359.5,283.0" data-evidence="5716-5716,5727-5729" />
<rect class="edge-label-bg" x="1248.3" y="286.8" width="98.4" height="22" rx="3" />
<text class="edge-label" x="1297.5" y="301.8">vhost-net 사용</text>
<polyline class="edge kind-data style-solid emphasis-normal" points="1509.5,274.0 1593.8,274.0 1593.8,211.5 1678.0,211.5" data-evidence="5716-5716,5729-5731" />
<rect class="edge-label-bg" x="1578.6" y="228.8" width="78.3" height="22" rx="3" />
<text class="edge-label" x="1617.8" y="243.8">Data Path</text>
<polyline class="edge kind-control style-solid emphasis-normal" points="220.0,134.5 304.2,134.5 304.2,134.5 388.5,134.5" data-evidence="5675-5676,5697-5704" />
<rect class="edge-label-bg" x="245.0" y="92.5" width="118.5" height="22" rx="3" />
<text class="edge-label" x="304.2" y="107.5">Control / Setup</text>
<polyline class="edge kind-data style-solid emphasis-normal" points="1828.0,202.5 1908.0,202.5 1908.0,202.5 1988.0,202.5" data-evidence="5677-5677,5731-5739" />
<rect class="edge-label-bg" x="1872.2" y="160.5" width="71.6" height="22" rx="3" />
<text class="edge-label" x="1908.0" y="175.5">frontend</text>
<g id="node-virsh">
<rect class="node-shape kind-cli emphasis-normal role-actor" data-evidence="5662-5662,5687-5689" x="90.5" y="60.0" width="150.0" height="71.0" rx="7" />
<text class="node-label" x="165.5" y="87.0">virsh</text>
<line class="node-detail-divider" x1="104.5" y1="108.0" x2="226.5" y2="108.0" />
<text class="node-detail" x="106.5" y="125.0">User</text>
<rect class="node-shape kind-cli emphasis-normal role-actor" data-evidence="5675-5675,5700-5702" x="70.0" y="99.0" width="150.0" height="71.0" rx="7" />
<text class="node-label" x="145.0" y="126.0">virsh</text>
<line class="node-detail-divider" x1="84.0" y1="147.0" x2="206.0" y2="147.0" />
<text class="node-detail" x="86.0" y="164.0">User</text>
</g>
<g id="node-physical-nic">
<rect class="node-shape kind-hardware emphasis-normal role-source" data-evidence="5706-5708" x="300.5" y="63.5" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="375.5" y="93.5">Physical NIC</text>
<rect class="node-shape kind-hardware emphasis-normal role-source" data-evidence="5719-5721" x="70.0" y="242.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="145.0" y="272.0">Physical NIC</text>
</g>
<g id="node-libvirt">
<rect class="node-shape kind-service emphasis-normal role-service" data-evidence="5663-5663,5691-5691" x="82.0" y="227.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="157.0" y="257.0">libvirt</text>
<rect class="node-shape kind-service emphasis-normal role-service" data-evidence="5676-5676,5704-5704" x="388.5" y="102.5" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="463.5" y="132.5">libvirt</text>
</g>
<g id="node-physical-nic-driver">
<rect class="node-shape kind-driver emphasis-normal role-service" data-evidence="5674-5674,5710-5710" x="292.0" y="227.0" width="167.0" height="64.0" rx="7" />
<text class="node-label" x="375.5" y="257.0">Physical NIC Driver</text>
<rect class="node-shape kind-driver emphasis-normal role-service" data-evidence="5687-5687,5723-5723" x="380.0" y="238.5" width="167.0" height="64.0" rx="7" />
<text class="node-label" x="463.5" y="268.5">Physical NIC Driver</text>
</g>
<g id="node-qemu">
<rect class="node-shape kind-process emphasis-normal role-service" data-evidence="5666-5666,5693-5693" x="90.5" y="388.5" width="150.0" height="71.0" rx="7" />
<text class="node-label" x="165.5" y="415.5">QEMU</text>
<line class="node-detail-divider" x1="104.5" y1="436.5" x2="226.5" y2="436.5" />
<text class="node-detail" x="106.5" y="453.5">Host Userspace</text>
<rect class="node-shape kind-process emphasis-normal role-service" data-evidence="5679-5679,5706-5706" x="707.0" y="94.0" width="150.0" height="71.0" rx="7" />
<text class="node-label" x="782.0" y="121.0">QEMU</text>
<line class="node-detail-divider" x1="721.0" y1="142.0" x2="843.0" y2="142.0" />
<text class="node-detail" x="723.0" y="159.0">Host Userspace</text>
</g>
<g id="node-linux-bridge">
<rect class="node-shape kind-network emphasis-normal role-service" data-evidence="5672-5673,5712-5712" x="300.5" y="387.0" width="150.0" height="74.0" rx="7" />
<text class="node-label" x="375.5" y="414.0">Linux Bridge /</text>
<text class="node-label" x="375.5" y="432.0">Routing / NAT</text>
<rect class="node-shape kind-network emphasis-normal role-service" data-evidence="5685-5686,5725-5725" x="707.0" y="237.0" width="150.0" height="74.0" rx="7" />
<text class="node-label" x="782.0" y="264.0">Linux Bridge /</text>
<text class="node-label" x="782.0" y="282.0">Routing / NAT</text>
</g>
<g id="node-virtio-net-device-model">
<rect class="node-shape kind-service emphasis-primary role-service" data-evidence="5666-5666,5695-5700" x="70.0" y="557.0" width="174.0" height="142.0" rx="7" />
<text class="node-label" x="157.0" y="585.0">virtio-net Device</text>
<text class="node-label" x="157.0" y="603.0">Model</text>
<line class="node-detail-divider" x1="84.0" y1="624.0" x2="230.0" y2="624.0" />
<text class="node-detail" x="86.0" y="641.0">virtual NIC 생성</text>
<text class="node-detail" x="86.0" y="657.0">feature negotiation</text>
<text class="node-detail" x="86.0" y="673.0">virtqueue 설정</text>
<text class="node-detail" x="86.0" y="689.0">vhost-net backend 설정</text>
<rect class="node-shape kind-service emphasis-primary role-service" data-evidence="5679-5679,5708-5713" x="1017.0" y="60.0" width="174.0" height="142.0" rx="7" />
<text class="node-label" x="1104.0" y="88.0">virtio-net Device</text>
<text class="node-label" x="1104.0" y="106.0">Model</text>
<line class="node-detail-divider" x1="1031.0" y1="127.0" x2="1177.0" y2="127.0" />
<text class="node-detail" x="1033.0" y="144.0">virtual NIC 생성</text>
<text class="node-detail" x="1033.0" y="160.0">feature negotiation</text>
<text class="node-detail" x="1033.0" y="176.0">virtqueue 설정</text>
<text class="node-detail" x="1033.0" y="192.0">vhost-net backend 설정</text>
</g>
<g id="node-tap">
<rect class="node-shape kind-network emphasis-normal role-service" data-evidence="5671-5671,5714-5714" x="304.0" y="592.5" width="167.0" height="71.0" rx="7" />
<text class="node-label" x="387.5" y="619.5">TAP</text>
<line class="node-detail-divider" x1="318.0" y1="640.5" x2="457.0" y2="640.5" />
<text class="node-detail" x="320.0" y="657.5">Host-side interface</text>
<rect class="node-shape kind-network emphasis-normal role-service" data-evidence="5684-5684,5727-5727" x="1020.5" y="274.0" width="167.0" height="71.0" rx="7" />
<text class="node-label" x="1104.0" y="301.0">TAP</text>
<line class="node-detail-divider" x1="1034.5" y1="322.0" x2="1173.5" y2="322.0" />
<text class="node-detail" x="1036.5" y="339.0">Host-side interface</text>
</g>
<g id="node-qemu-virtio-backend">
<rect class="node-shape kind-process emphasis-normal role-service" data-evidence="5668-5668,5742-5742" x="82.0" y="795.0" width="167.0" height="71.0" rx="7" />
<text class="node-label" x="165.5" y="822.0">QEMU virtio backend</text>
<line class="node-detail-divider" x1="96.0" y1="843.0" x2="235.0" y2="843.0" />
<text class="node-detail" x="98.0" y="860.0">QEMU Userspace</text>
<rect class="node-shape kind-process emphasis-normal role-service" data-evidence="5681-5681,5755-5755" x="1351.0" y="95.5" width="167.0" height="71.0" rx="7" />
<text class="node-label" x="1434.5" y="122.5">QEMU virtio backend</text>
<line class="node-detail-divider" x1="1365.0" y1="143.5" x2="1504.0" y2="143.5" />
<text class="node-detail" x="1367.0" y="160.5">QEMU Userspace</text>
</g>
<g id="node-vhost-net">
<rect class="node-shape kind-kernel emphasis-normal role-service" data-evidence="5669-5669,5716-5716" x="309.0" y="795.0" width="150.0" height="71.0" rx="7" />
<text class="node-label" x="384.0" y="822.0">vhost-net</text>
<line class="node-detail-divider" x1="323.0" y1="843.0" x2="445.0" y2="843.0" />
<text class="node-detail" x="325.0" y="860.0">Host Kernel</text>
<rect class="node-shape kind-kernel emphasis-normal role-service" data-evidence="5682-5682,5729-5729" x="1359.5" y="238.5" width="150.0" height="71.0" rx="7" />
<text class="node-label" x="1434.5" y="265.5">vhost-net</text>
<line class="node-detail-divider" x1="1373.5" y1="286.5" x2="1495.5" y2="286.5" />
<text class="node-detail" x="1375.5" y="303.5">Host Kernel</text>
</g>
<g id="node-virtqueue">
<rect class="node-shape kind-queue emphasis-normal role-queue" data-evidence="5667-5667,5718-5718" x="195.5" y="962.0" width="150.0" height="71.0" rx="7" />
<text class="node-label" x="270.5" y="989.0">virtqueue</text>
<line class="node-detail-divider" x1="209.5" y1="1010.0" x2="331.5" y2="1010.0" />
<text class="node-detail" x="211.5" y="1027.0">shared queue</text>
<rect class="node-shape kind-queue emphasis-normal role-queue" data-evidence="5680-5680,5731-5731" x="1678.0" y="167.0" width="150.0" height="71.0" rx="7" />
<text class="node-label" x="1753.0" y="194.0">virtqueue</text>
<line class="node-detail-divider" x1="1692.0" y1="215.0" x2="1814.0" y2="215.0" />
<text class="node-detail" x="1694.0" y="232.0">shared queue</text>
</g>
<g id="node-guest">
<rect class="node-shape kind-service emphasis-normal role-sink" data-evidence="5664-5665,5720-5726" x="162.5" y="1129.0" width="216.0" height="122.0" rx="7" />
<text class="node-label" x="270.5" y="1156.0">Guest</text>
<line class="node-detail-divider" x1="176.5" y1="1177.0" x2="364.5" y2="1177.0" />
<text class="node-detail" x="178.5" y="1194.0">virtio-net Frontend Driver</text>
<text class="node-detail" x="178.5" y="1210.0">Guest TCP/IP Stack</text>
<text class="node-detail" x="178.5" y="1226.0">Socket</text>
<text class="node-detail" x="178.5" y="1242.0">Keycloak</text>
<rect class="node-shape kind-service emphasis-normal role-sink" data-evidence="5677-5678,5733-5739" x="1988.0" y="141.5" width="216.0" height="122.0" rx="7" />
<text class="node-label" x="2096.0" y="168.5">Guest</text>
<line class="node-detail-divider" x1="2002.0" y1="189.5" x2="2190.0" y2="189.5" />
<text class="node-detail" x="2004.0" y="206.5">virtio-net Frontend Driver</text>
<text class="node-detail" x="2004.0" y="222.5">Guest TCP/IP Stack</text>
<text class="node-detail" x="2004.0" y="238.5">Socket</text>
<text class="node-detail" x="2004.0" y="254.5">Keycloak</text>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

@@ -10,16 +10,16 @@ Guest Page Table 과 EPT, QEMU 의 Guest RAM Backing 에서 각각 fault 가 갈
## Elements and evidence
- **Guest Page Table** (component): 첫 번째 translation 단계. 여기서 접근을 완료할 수 없으면 Guest Page Fault 가 난다. Evidence: L2443L2445, L2484L2485.
- **Guest Kernel** (service): Guest Page Fault 를 받는 계층. Evidence: L2487L2487, L2493L2494.
- **EPT** (component): 두 번째 translation 단계. Guest translation 이 성공한 뒤 여기서 막히면 EPT Violation 이 난다. Evidence: L2465L2469, L2484L2485.
- **KVM** (service): EPT Violation 을 VM Exit 뒤에 받는 계층. Evidence: L2471L2473, L2487L2487.
- **QEMU · Guest RAM Backing** (store): Host 의 일반 userspace 메모리로 관리되는 자리. demand allocation 이나 reclaim, swap 때문에 여기서 fault 가 난다. Evidence: L2504L2504, L2514L2519.
- **Host Kernel** (service): Host Page Fault 를 받아 필요한 Host page 를 처리하는 계층. Evidence: L2521L2525.
- **Guest Page Table** (component): 첫 번째 translation 단계. 여기서 접근을 완료할 수 없으면 Guest Page Fault 가 난다. Evidence: L2456L2458, L2497L2498.
- **Guest Kernel** (service): Guest Page Fault 를 받는 계층. Evidence: L2500L2500, L2506L2507.
- **EPT** (component): 두 번째 translation 단계. Guest translation 이 성공한 뒤 여기서 막히면 EPT Violation 이 난다. Evidence: L2478L2482, L2497L2498.
- **KVM** (service): EPT Violation 을 VM Exit 뒤에 받는 계층. Evidence: L2484L2486, L2500L2500.
- **QEMU · Guest RAM Backing** (store): Host 의 일반 userspace 메모리로 관리되는 자리. demand allocation 이나 reclaim, swap 때문에 여기서 fault 가 난다. Evidence: L2517L2517, L2527L2532.
- **Host Kernel** (service): Host Page Fault 를 받아 필요한 Host page 를 처리하는 계층. Evidence: L2534L2538.
## Relationships
- **EPT → KVM:** EPT Violation · VM Exit. Evidence: L2467L2473, L2487L2487.
- **Guest Page Table → Guest Kernel:** Guest Page Fault. Evidence: L2484L2487, L2493L2494.
- **QEMU · Guest RAM Backing → Host Kernel:** Host Page Fault. Evidence: L2517L2525.
- **Guest Page Table → EPT:** GPA. Evidence: L2461L2467.
- **EPT → KVM:** EPT Violation · VM Exit. Evidence: L2480L2486, L2500L2500.
- **Guest Page Table → Guest Kernel:** Guest #PF. Evidence: L2497L2500, L2506L2507.
- **QEMU · Guest RAM Backing → Host Kernel:** Host #PF. Evidence: L2530L2538.
- **Guest Page Table → EPT:** GPA. Evidence: L2474L2480.
@@ -1,6 +1,6 @@
# 세 fault 는 변환 사슬의 서로 다른 단계에서 갈라져 나가 서로 다른 계층이 받는다
# Question: Guest Page Fault · EPT Violation · Host Page Fault 는 각각 어느 단계에서 갈라지고 누가 받는가
direction: down
direction: right
n0: "Guest Page Table" {
shape: rectangle
}
@@ -19,7 +19,7 @@ n4: "QEMU · Guest RAM Backing" {
n5: "Host Kernel" {
shape: rectangle
}
n0 -> n1: "Guest Page Fault" { style.stroke-dash: 4 }
n0 -> n1: "Guest #PF" { style.stroke-dash: 4 }
n0 -> n2: "GPA"
n2 -> n3: "EPT Violation · VM Exit" { style.stroke-dash: 4 }
n4 -> n5: "Host Page Fault" { style.stroke-dash: 4 }
n4 -> n5: "Host #PF" { style.stroke-dash: 4 }
@@ -0,0 +1,15 @@
digraph techviz {
graph [rankdir=LR, splines=ortho, nodesep=0.55, ranksep=0.85];
node [fontname=Helvetica, fontsize=11, margin="0.18,0.12", style="rounded,filled", fillcolor=white, color="#2d4357", penwidth=1.5];
edge [fontname=Helvetica, fontsize=10, color="#364b5f", penwidth=1.4, arrowsize=0.75];
n0 [label="Guest Page Table", shape=box, style="rounded,filled"];
n1 [label="Guest Kernel", shape=box, style="rounded,filled"];
n2 [label="EPT", shape=box, style="rounded,filled"];
n3 [label="KVM", shape=box, style="rounded,filled"];
n4 [label="QEMU · Guest RAM Backing", shape=box, style="rounded,filled"];
n5 [label="Host Kernel", shape=box, style="rounded,filled"];
n0 -> n1 [label="Guest #PF", style=dashed];
n0 -> n2 [label="GPA", style=solid];
n2 -> n3 [label="EPT Violation · VM Exit", style=dashed];
n4 -> n5 [label="Host #PF", style=dashed];
}
@@ -1,46 +1,46 @@
<?xml version="1.0" encoding="UTF-8"?>
<mxfile host="app.diagrams.net" modified="2026-07-23T00:00:00.000Z" agent="techviz-harness" version="24.7.17" type="device">
<diagram id="page-fault-layers-in-a-vm" name="세 fault 는 변환 사슬의 서로 다른 단계에서 갈라져 나가 서로 다른 계층이 받는다">
<mxGraphModel dx="685" dy="523" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
<mxGraphModel dx="902" dy="448" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="902" pageHeight="1169" math="0" shadow="0">
<root>
<mxCell id="0"/>
<mxCell id="1" parent="0"/>
<mxCell id="n_guest-page-table" value="Guest Page Table&lt;br/&gt;GVA → GPA" tooltip="첫 번째 translation 단계. 여기서 접근을 완료할 수 없으면 Guest Page Fault 가 난다. | Evidence: L2443-L2445, L2484-L2485" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="166.5" y="70.0" width="150.0" height="71.0" as="geometry"/>
<mxCell id="n_guest-page-table" value="Guest Page Table&lt;br/&gt;GVA → GPA" tooltip="첫 번째 translation 단계. 여기서 접근을 완료할 수 없으면 Guest Page Fault 가 난다. | Evidence: L2456-L2458, L2497-L2498" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="78.5" y="114.5" width="150.0" height="71.0" as="geometry"/>
</mxCell>
<mxCell id="n_qemu-backing" value="QEMU · Guest RAM Backing&lt;br/&gt;Host Virtual Memory" tooltip="Host 의 일반 userspace 메모리로 관리되는 자리. demand allocation 이나 reclaim, swap 때문에 여기서 fault 가 난다. | Evidence: L2504-L2504, L2514-L2519" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="376.5" y="60.0" width="167.0" height="91.0" as="geometry"/>
<mxCell id="n_qemu-backing" value="QEMU · Guest RAM Backing&lt;br/&gt;Host Virtual Memory" tooltip="Host 의 일반 userspace 메모리로 관리되는 자리. demand allocation 이나 reclaim, swap 때문에 여기서 fault 가 난다. | Evidence: L2517-L2517, L2527-L2532" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="70.0" y="257.5" width="167.0" height="91.0" as="geometry"/>
</mxCell>
<mxCell id="n_ept" value="EPT&lt;br/&gt;GPA → HPA" tooltip="두 번째 translation 단계. Guest translation 이 성공한 뒤 여기서 막히면 EPT Violation 이 난다. | Evidence: L2465-L2469, L2484-L2485" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;strokeColor=#2563eb;strokeWidth=2;" vertex="1" parent="1">
<mxGeometry x="70.0" y="247.0" width="150.0" height="71.0" as="geometry"/>
<mxCell id="n_ept" value="EPT&lt;br/&gt;GPA → HPA" tooltip="두 번째 translation 단계. Guest translation 이 성공한 뒤 여기서 막히면 EPT Violation 이 난다. | Evidence: L2478-L2482, L2497-L2498" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;strokeColor=#2563eb;strokeWidth=2;" vertex="1" parent="1">
<mxGeometry x="397.0" y="60.0" width="150.0" height="71.0" as="geometry"/>
</mxCell>
<mxCell id="n_guest-kernel" value="Guest Kernel" tooltip="Guest Page Fault 를 받는 계층. | Evidence: L2487-L2487, L2493-L2494" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="280.0" y="250.5" width="150.0" height="64.0" as="geometry"/>
<mxCell id="n_guest-kernel" value="Guest Kernel" tooltip="Guest Page Fault 를 받는 계층. | Evidence: L2500-L2500, L2506-L2507" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="397.0" y="203.0" width="150.0" height="64.0" as="geometry"/>
</mxCell>
<mxCell id="n_host-kernel" value="Host Kernel" tooltip="Host Page Fault 를 받아 필요한 Host page 를 처리하는 계층. | Evidence: L2521-L2525" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="490.0" y="250.5" width="150.0" height="64.0" as="geometry"/>
<mxCell id="n_host-kernel" value="Host Kernel" tooltip="Host Page Fault 를 받아 필요한 Host page 를 처리하는 계층. | Evidence: L2534-L2538" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="397.0" y="339.0" width="150.0" height="64.0" as="geometry"/>
</mxCell>
<mxCell id="n_kvm" value="KVM" tooltip="EPT Violation 을 VM Exit 뒤에 받는 계층. | Evidence: L2471-L2473, L2487-L2487" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="280.0" y="414.0" width="150.0" height="64.0" as="geometry"/>
<mxCell id="n_kvm" value="KVM" tooltip="EPT Violation 을 VM Exit 뒤에 받는 계층. | Evidence: L2484-L2486, L2500-L2500" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="707.0" y="199.5" width="150.0" height="64.0" as="geometry"/>
</mxCell>
<mxCell id="e_ept-violation-branch" value="EPT Violation · VM Exit" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;dashed=1;" edge="1" parent="1" source="n_ept" target="n_kvm">
<mxGeometry relative="1" as="geometry">
<mxPoint x="250.0" y="338.0" as="offset"/>
<mxPoint x="651.0" y="163.5" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="e_guest-fault-branch" value="Guest Page Fault" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;dashed=1;" edge="1" parent="1" source="n_guest-page-table" target="n_guest-kernel">
<mxCell id="e_guest-fault-branch" value="Guest #PF" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;dashed=1;" edge="1" parent="1" source="n_guest-page-table" target="n_guest-kernel">
<mxGeometry relative="1" as="geometry">
<mxPoint x="302.8" y="167.8" as="offset"/>
<mxPoint x="336.8" y="197.0" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="e_host-fault-branch" value="Host Page Fault" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;dashed=1;" edge="1" parent="1" source="n_qemu-backing" target="n_host-kernel">
<mxCell id="e_host-fault-branch" value="Host #PF" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;dashed=1;" edge="1" parent="1" source="n_qemu-backing" target="n_host-kernel">
<mxGeometry relative="1" as="geometry">
<mxPoint x="512.5" y="172.8" as="offset"/>
<mxPoint x="341.0" y="337.0" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="e_translation-continues" value="GPA" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_guest-page-table" target="n_ept">
<mxGeometry relative="1" as="geometry">
<mxPoint x="188.8" y="166.0" as="offset"/>
<mxPoint x="336.8" y="118.2" as="offset"/>
</mxGeometry>
</mxCell>
</root>
@@ -6,10 +6,10 @@
{
"id": "edge-ept-violation-branch",
"type": "arrow",
"x": 145.0,
"y": 318.0,
"width": 210.0,
"height": 96.0,
"x": 547.0,
"y": 95.5,
"width": 160.0,
"height": 136.0,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
@@ -36,16 +36,16 @@
0.0
],
[
0.0,
48.0
80.0,
0.0
],
[
210.0,
48.0
80.0,
136.0
],
[
210.0,
96.0
160.0,
136.0
]
],
"lastCommittedPoint": null,
@@ -66,8 +66,8 @@
{
"id": "edge-label-ept-violation-branch",
"type": "text",
"x": 158.0,
"y": 326.0,
"x": 559.0,
"y": 151.5,
"width": 184,
"height": 24,
"angle": 0,
@@ -105,10 +105,10 @@
{
"id": "edge-guest-fault-branch",
"type": "arrow",
"x": 250.5,
"y": 141.0,
"width": 104.5,
"height": 109.5,
"x": 228.5,
"y": 159.0,
"width": 168.5,
"height": 76.0,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
@@ -135,16 +135,16 @@
0.0
],
[
0.0,
54.75
84.25,
0.0
],
[
104.5,
54.75
84.25,
76.0
],
[
104.5,
109.5
168.5,
76.0
]
],
"lastCommittedPoint": null,
@@ -165,9 +165,9 @@
{
"id": "edge-label-guest-fault-branch",
"type": "text",
"x": 238.75,
"y": 155.75,
"width": 128,
"x": 291.75,
"y": 185.0,
"width": 90,
"height": 24,
"angle": 0,
"strokeColor": "#1e1e1e",
@@ -193,21 +193,21 @@
"locked": false,
"fontSize": 13,
"fontFamily": 5,
"text": "Guest Page Fault",
"text": "Guest #PF",
"textAlign": "center",
"verticalAlign": "middle",
"containerId": null,
"originalText": "Guest Page Fault",
"originalText": "Guest #PF",
"autoResize": true,
"lineHeight": 1.25
},
{
"id": "edge-host-fault-branch",
"type": "arrow",
"x": 460.0,
"y": 151.0,
"width": 105.0,
"height": 99.5,
"x": 237.0,
"y": 303.0,
"width": 160.0,
"height": 68.0,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
@@ -234,16 +234,16 @@
0.0
],
[
0.0,
49.75
80.0,
0.0
],
[
105.0,
49.75
80.0,
68.0
],
[
105.0,
99.5
160.0,
68.0
]
],
"lastCommittedPoint": null,
@@ -264,9 +264,9 @@
{
"id": "edge-label-host-fault-branch",
"type": "text",
"x": 452.5,
"y": 160.75,
"width": 120,
"x": 296.0,
"y": 325.0,
"width": 90,
"height": 24,
"angle": 0,
"strokeColor": "#1e1e1e",
@@ -292,21 +292,21 @@
"locked": false,
"fontSize": 13,
"fontFamily": 5,
"text": "Host Page Fault",
"text": "Host #PF",
"textAlign": "center",
"verticalAlign": "middle",
"containerId": null,
"originalText": "Host Page Fault",
"originalText": "Host #PF",
"autoResize": true,
"lineHeight": 1.25
},
{
"id": "edge-translation-continues",
"type": "arrow",
"x": 145.0,
"y": 141.0,
"width": 87.5,
"height": 106.0,
"x": 228.5,
"y": 95.5,
"width": 168.5,
"height": 45.5,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
@@ -329,20 +329,20 @@
"locked": false,
"points": [
[
87.5,
0.0,
45.5
],
[
84.25,
45.5
],
[
84.25,
0.0
],
[
87.5,
53.0
],
[
0.0,
53.0
],
[
0.0,
106.0
168.5,
0.0
]
],
"lastCommittedPoint": null,
@@ -363,8 +363,8 @@
{
"id": "edge-label-translation-continues",
"type": "text",
"x": 143.75,
"y": 154.0,
"x": 291.75,
"y": 106.25,
"width": 90,
"height": 24,
"angle": 0,
@@ -402,8 +402,8 @@
{
"id": "node-guest-page-table",
"type": "rectangle",
"x": 166.5,
"y": 70.0,
"x": 78.5,
"y": 114.5,
"width": 150.0,
"height": 71.0,
"angle": 0,
@@ -432,8 +432,8 @@
{
"id": "node-label-guest-page-table",
"type": "text",
"x": 176.5,
"y": 80.0,
"x": 88.5,
"y": 124.5,
"width": 130.0,
"height": 51.0,
"angle": 0,
@@ -471,8 +471,8 @@
{
"id": "node-qemu-backing",
"type": "rectangle",
"x": 376.5,
"y": 60.0,
"x": 70.0,
"y": 257.5,
"width": 167.0,
"height": 91.0,
"angle": 0,
@@ -501,8 +501,8 @@
{
"id": "node-label-qemu-backing",
"type": "text",
"x": 386.5,
"y": 70.0,
"x": 80.0,
"y": 267.5,
"width": 147.0,
"height": 71.0,
"angle": 0,
@@ -540,8 +540,8 @@
{
"id": "node-ept",
"type": "rectangle",
"x": 70.0,
"y": 247.0,
"x": 397.0,
"y": 60.0,
"width": 150.0,
"height": 71.0,
"angle": 0,
@@ -570,8 +570,8 @@
{
"id": "node-label-ept",
"type": "text",
"x": 80.0,
"y": 257.0,
"x": 407.0,
"y": 70.0,
"width": 130.0,
"height": 51.0,
"angle": 0,
@@ -609,8 +609,8 @@
{
"id": "node-guest-kernel",
"type": "rectangle",
"x": 280.0,
"y": 250.5,
"x": 397.0,
"y": 203.0,
"width": 150.0,
"height": 64.0,
"angle": 0,
@@ -639,8 +639,8 @@
{
"id": "node-label-guest-kernel",
"type": "text",
"x": 290.0,
"y": 260.5,
"x": 407.0,
"y": 213.0,
"width": 130.0,
"height": 44.0,
"angle": 0,
@@ -678,8 +678,8 @@
{
"id": "node-host-kernel",
"type": "rectangle",
"x": 490.0,
"y": 250.5,
"x": 397.0,
"y": 339.0,
"width": 150.0,
"height": 64.0,
"angle": 0,
@@ -708,8 +708,8 @@
{
"id": "node-label-host-kernel",
"type": "text",
"x": 500.0,
"y": 260.5,
"x": 407.0,
"y": 349.0,
"width": 130.0,
"height": 44.0,
"angle": 0,
@@ -747,8 +747,8 @@
{
"id": "node-kvm",
"type": "rectangle",
"x": 280.0,
"y": 414.0,
"x": 707.0,
"y": 199.5,
"width": 150.0,
"height": 64.0,
"angle": 0,
@@ -777,8 +777,8 @@
{
"id": "node-label-kvm",
"type": "text",
"x": 290.0,
"y": 424.0,
"x": 717.0,
"y": 209.5,
"width": 130.0,
"height": 44.0,
"angle": 0,
@@ -2,21 +2,22 @@
"harness_version": "0.2.0",
"spec_id": "page-fault-layers-in-a-vm",
"spec_version": "1.1",
"spec_sha256": "1db9b8029b08c8d4c61b3f01119f121b394b7d0efb9818cb63793d65b858d667",
"spec_sha256": "25421b9911ae384dcdb8d986608e8b727aea1345c48d19210e608b4d9ebbd026",
"source_context": {
"document": "docs/virtualization/final/document.md",
"document_sha256": "181e2b3cc8a45bae81e7e8193d026937c4d586ae4495d3b2c323eb7e6abcfadd",
"document_sha256": "8c4ecc64c8cea9a4450ed7131fdd9cb2048dc092b66cd969f6346ed77887c210",
"anchor": {
"kind": "heading",
"value": "48. Guest Page Fault와 EPT Violation 비교",
"line": 2480
"line": 2493
}
},
"outputs": [
"page-fault-layers-in-a-vm.svg",
"page-fault-layers-in-a-vm.drawio",
"page-fault-layers-in-a-vm.mmd",
"page-fault-layers-in-a-vm.d2",
"page-fault-layers-in-a-vm.dot",
"page-fault-layers-in-a-vm.drawio",
"page-fault-layers-in-a-vm.excalidraw",
"page-fault-layers-in-a-vm.alt.md"
],
@@ -27,5 +28,15 @@
"reference_ids": [
"payment-event-flow"
],
"diagram_only": true
"diagram_only": true,
"source_spec_file_sha256": "403c7a8d1ab23e8e8a21ff940845a1a99bbf97b95535c2598ce37b4ddfb34851",
"output_sha256": {
"page-fault-layers-in-a-vm.svg": "ddd294b5abeceb1ad8a7df1e8b14568ca5b4ac4f6aa94267e99b281fc2c0305e",
"page-fault-layers-in-a-vm.mmd": "613bc7653e5896cd279d8a4a0cf497f08abd800433b42722f223a80dbfd4b8d4",
"page-fault-layers-in-a-vm.d2": "5ea7f0d37257545af1b8f7930e5a3cc28559d2b7dee034f7bc39d848faa3f36e",
"page-fault-layers-in-a-vm.dot": "75410f40e76c82baf0f1d4e52453e7714c4b5cee25efb75c476db31afef78f90",
"page-fault-layers-in-a-vm.drawio": "70e07a8e69ad81634599b05cf24cd7b083f819799c1084c5e48a6c225ad29702",
"page-fault-layers-in-a-vm.excalidraw": "a817ec26ba3159c9dff9276556375f72846b65a34c2098ea6fe05eeaf20657c9",
"page-fault-layers-in-a-vm.alt.md": "4d703b128f2c6fdf3b17393433e349330b6c392cf1b056a240e349a850a2527e"
}
}
@@ -1,13 +1,13 @@
%% 세 fault 는 변환 사슬의 서로 다른 단계에서 갈라져 나가 서로 다른 계층이 받는다
%% question: Guest Page Fault · EPT Violation · Host Page Fault 는 각각 어느 단계에서 갈라지고 누가 받는가
flowchart TB
flowchart LR
n0["Guest Page Table"]
n1["Guest Kernel"]
n2["EPT"]
n3["KVM"]
n4["QEMU · Guest RAM Backing"]
n5["Host Kernel"]
n0 -.->|"Guest Page Fault"| n1
n0 -.->|"Guest #PF"| n1
n0 -->|"GPA"| n2
n2 -.->|"EPT Violation · VM Exit"| n3
n4 -.->|"Host Page Fault"| n5
n4 -.->|"Host #PF"| n5
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="685" height="523" viewBox="0 0 685 523" role="img" aria-labelledby="diagram-title diagram-description">
<svg xmlns="http://www.w3.org/2000/svg" width="902" height="448" viewBox="0 0 902 448" role="img" aria-labelledby="diagram-title diagram-description">
<title id="diagram-title">세 fault 는 변환 사슬의 서로 다른 단계에서 갈라져 나가 서로 다른 계층이 받는다</title>
<desc id="diagram-description">위에서 아래로 읽는다. 맨 위 왼쪽에 Guest Page Table 이 있고 거기서 갈래가 둘이다. 하나는 Guest Page Fault 로 Guest Kernel 에 빠지고, 다른 하나는 GPA 를 들고 아래 EPT 로 내려간다. EPT 에서도 갈래가 나뉘어 EPT Violation 은 VM Exit 을 거쳐 KVM 으로 간다. 맨 위 오른쪽에는 QEMU 의 Guest RAM Backing 이 따로 있고 그쪽 갈래인 Host Page Fault 는 Host Kernel 로 간다. 받는 자리의 이름이 그대로 계층 이름이다 — Guest Kernel · KVM · Host Kernel. QEMU 갈래가 EPT 갈래와 선으로 이어져 있지 않은 것은 근거 문서가 둘을 잇는 문장을 두지 않았기 때문이다. 다섯 원인이 무엇인지와 두 사건을 항목별로 견주는 것은 본문의 표가 맡는다.</desc>
<metadata>{&quot;techviz&quot;:{&quot;spec_version&quot;:&quot;1.1&quot;,&quot;id&quot;:&quot;page-fault-layers-in-a-vm&quot;,&quot;profile&quot;:&quot;component-flow&quot;},&quot;source_context&quot;:{&quot;document&quot;:&quot;docs/virtualization/final/document.md&quot;,&quot;document_sha256&quot;:&quot;181e2b3cc8a45bae81e7e8193d026937c4d586ae4495d3b2c323eb7e6abcfadd&quot;,&quot;anchor&quot;:{&quot;kind&quot;:&quot;heading&quot;,&quot;value&quot;:&quot;48. Guest Page Fault와 EPT Violation 비교&quot;,&quot;line&quot;:2480}},&quot;evidence_policy&quot;:&quot;Each factual element cites source lines or is marked assumption.&quot;,&quot;diagram_only&quot;:true}</metadata>
<metadata>{&quot;techviz&quot;:{&quot;spec_version&quot;:&quot;1.1&quot;,&quot;id&quot;:&quot;page-fault-layers-in-a-vm&quot;,&quot;profile&quot;:&quot;component-flow&quot;},&quot;source_context&quot;:{&quot;document&quot;:&quot;docs/virtualization/final/document.md&quot;,&quot;document_sha256&quot;:&quot;8c4ecc64c8cea9a4450ed7131fdd9cb2048dc092b66cd969f6346ed77887c210&quot;,&quot;anchor&quot;:{&quot;kind&quot;:&quot;heading&quot;,&quot;value&quot;:&quot;48. Guest Page Fault와 EPT Violation 비교&quot;,&quot;line&quot;:2493}},&quot;evidence_policy&quot;:&quot;Each factual element cites source lines or is marked assumption.&quot;,&quot;diagram_only&quot;:true}</metadata>
<defs>
<marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M 0 0 L 10 5 L 0 10 z" />
@@ -49,48 +49,48 @@
.timeline-detail { font-size: 11px; fill: #4b5563; text-anchor: middle; }
</style>
</defs>
<rect class="canvas" width="685" height="523" />
<polyline class="edge kind-event style-solid emphasis-normal semantic-dashed" points="145.0,318.0 145.0,366.0 355.0,366.0 355.0,414.0" data-evidence="2467-2473,2487-2487" />
<rect class="edge-label-bg" x="163.9" y="324.0" width="172.1" height="22" rx="3" />
<text class="edge-label" x="250.0" y="339.0">EPT Violation · VM Exit</text>
<polyline class="edge kind-event style-solid emphasis-normal semantic-dashed" points="250.5,141.0 250.5,195.8 355.0,195.8 355.0,250.5" data-evidence="2484-2487,2493-2494" />
<rect class="edge-label-bg" x="240.2" y="153.8" width="125.2" height="22" rx="3" />
<text class="edge-label" x="302.8" y="168.8">Guest Page Fault</text>
<polyline class="edge kind-event style-solid emphasis-normal semantic-dashed" points="460.0,151.0 460.0,200.8 565.0,200.8 565.0,250.5" data-evidence="2517-2525" />
<rect class="edge-label-bg" x="453.2" y="158.8" width="118.5" height="22" rx="3" />
<text class="edge-label" x="512.5" y="173.8">Host Page Fault</text>
<polyline class="edge kind-data style-solid emphasis-normal" points="232.5,141.0 232.5,194.0 145.0,194.0 145.0,247.0" data-evidence="2461-2467" />
<rect class="edge-label-bg" x="166.8" y="152.0" width="44.0" height="22" rx="3" />
<text class="edge-label" x="188.8" y="167.0">GPA</text>
<rect class="canvas" width="902" height="448" />
<polyline class="edge kind-event style-solid emphasis-normal semantic-dashed" points="547.0,95.5 627.0,95.5 627.0,231.5 707.0,231.5" data-evidence="2480-2486,2500-2500" />
<rect class="edge-label-bg" x="565.0" y="149.5" width="172.1" height="22" rx="3" />
<text class="edge-label" x="651.0" y="164.5">EPT Violation · VM Exit</text>
<polyline class="edge kind-event style-solid emphasis-normal semantic-dashed" points="228.5,159.0 312.8,159.0 312.8,235.0 397.0,235.0" data-evidence="2497-2500,2506-2507" />
<rect class="edge-label-bg" x="297.6" y="183.0" width="78.3" height="22" rx="3" />
<text class="edge-label" x="336.8" y="198.0">Guest #PF</text>
<polyline class="edge kind-event style-solid emphasis-normal semantic-dashed" points="237.0,303.0 317.0,303.0 317.0,371.0 397.0,371.0" data-evidence="2530-2538" />
<rect class="edge-label-bg" x="305.2" y="323.0" width="71.6" height="22" rx="3" />
<text class="edge-label" x="341.0" y="338.0">Host #PF</text>
<polyline class="edge kind-data style-solid emphasis-normal" points="228.5,141.0 312.8,141.0 312.8,95.5 397.0,95.5" data-evidence="2474-2480" />
<rect class="edge-label-bg" x="314.8" y="104.2" width="44.0" height="22" rx="3" />
<text class="edge-label" x="336.8" y="119.2">GPA</text>
<g id="node-guest-page-table">
<rect class="node-shape kind-component emphasis-normal role-source" data-evidence="2443-2445,2484-2485" x="166.5" y="70.0" width="150.0" height="71.0" rx="7" />
<text class="node-label" x="241.5" y="97.0">Guest Page Table</text>
<line class="node-detail-divider" x1="180.5" y1="118.0" x2="302.5" y2="118.0" />
<text class="node-detail" x="182.5" y="135.0">GVA → GPA</text>
<rect class="node-shape kind-component emphasis-normal role-source" data-evidence="2456-2458,2497-2498" x="78.5" y="114.5" width="150.0" height="71.0" rx="7" />
<text class="node-label" x="153.5" y="141.5">Guest Page Table</text>
<line class="node-detail-divider" x1="92.5" y1="162.5" x2="214.5" y2="162.5" />
<text class="node-detail" x="94.5" y="179.5">GVA → GPA</text>
</g>
<g id="node-qemu-backing">
<rect class="node-shape kind-store emphasis-normal role-source" data-evidence="2504-2504,2514-2519" x="376.5" y="60.0" width="167.0" height="91.0" rx="7" />
<text class="node-label" x="460.0" y="87.0">QEMU · Guest RAM</text>
<text class="node-label" x="460.0" y="105.0">Backing</text>
<line class="node-detail-divider" x1="390.5" y1="126.0" x2="529.5" y2="126.0" />
<text class="node-detail" x="392.5" y="143.0">Host Virtual Memory</text>
<rect class="node-shape kind-store emphasis-normal role-source" data-evidence="2517-2517,2527-2532" x="70.0" y="257.5" width="167.0" height="91.0" rx="7" />
<text class="node-label" x="153.5" y="284.5">QEMU · Guest RAM</text>
<text class="node-label" x="153.5" y="302.5">Backing</text>
<line class="node-detail-divider" x1="84.0" y1="323.5" x2="223.0" y2="323.5" />
<text class="node-detail" x="86.0" y="340.5">Host Virtual Memory</text>
</g>
<g id="node-ept">
<rect class="node-shape kind-component emphasis-primary role-service" data-evidence="2465-2469,2484-2485" x="70.0" y="247.0" width="150.0" height="71.0" rx="7" />
<text class="node-label" x="145.0" y="274.0">EPT</text>
<line class="node-detail-divider" x1="84.0" y1="295.0" x2="206.0" y2="295.0" />
<text class="node-detail" x="86.0" y="312.0">GPA → HPA</text>
<rect class="node-shape kind-component emphasis-primary role-service" data-evidence="2478-2482,2497-2498" x="397.0" y="60.0" width="150.0" height="71.0" rx="7" />
<text class="node-label" x="472.0" y="87.0">EPT</text>
<line class="node-detail-divider" x1="411.0" y1="108.0" x2="533.0" y2="108.0" />
<text class="node-detail" x="413.0" y="125.0">GPA → HPA</text>
</g>
<g id="node-guest-kernel">
<rect class="node-shape kind-service emphasis-normal role-sink" data-evidence="2487-2487,2493-2494" x="280.0" y="250.5" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="355.0" y="280.5">Guest Kernel</text>
<rect class="node-shape kind-service emphasis-normal role-sink" data-evidence="2500-2500,2506-2507" x="397.0" y="203.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="472.0" y="233.0">Guest Kernel</text>
</g>
<g id="node-host-kernel">
<rect class="node-shape kind-service emphasis-normal role-sink" data-evidence="2521-2525" x="490.0" y="250.5" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="565.0" y="280.5">Host Kernel</text>
<rect class="node-shape kind-service emphasis-normal role-sink" data-evidence="2534-2538" x="397.0" y="339.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="472.0" y="369.0">Host Kernel</text>
</g>
<g id="node-kvm">
<rect class="node-shape kind-service emphasis-normal role-sink" data-evidence="2471-2473,2487-2487" x="280.0" y="414.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="355.0" y="444.0">KVM</text>
<rect class="node-shape kind-service emphasis-normal role-sink" data-evidence="2484-2486,2500-2500" x="707.0" y="199.5" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="782.0" y="229.5">KVM</text>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 8.1 KiB

After

Width:  |  Height:  |  Size: 8.1 KiB

@@ -0,0 +1,26 @@
# qcow2 L1·L2 매핑과 backing fallback
## Alternative text
게스트 오프셋이 L1과 L2 표를 지나 현재 qcow2 데이터 클러스터로 가거나, L2 항목이 0일 때 backing file 또는 zero-fill로 갈라지는 qcow2 매핑도.
## Long description
게스트가 읽으려는 위치는 L1 표와 L2 표를 통해 파일 안 데이터 클러스터 오프셋으로 변환된다. 매핑된 값이 있으면 현재 qcow2의 데이터 클러스터를 읽는다. 값이 0이고 backing file이 있으면 base.qcow2의 같은 가상 위치를 읽고, backing file이 없으면 0으로 채운 64KB를 반환한다. backing file 경로는 qcow2 헤더의 문자열이므로 오버레이만 옮기면 부팅할 수 없다는 제한도 그림 끝에 반영한다.
## Elements and evidence
- **Guest offset** (data): 게스트가 읽으려는 가상 디스크 위치. Evidence: L14317L14325.
- **L1 table** (component): 필요한 L2 표가 있는 클러스터 위치를 찾는다. Evidence: L14312L14337.
- **L2 entry** (component): 현재 qcow2 데이터 클러스터 오프셋을 가지거나 0일 수 있다. Evidence: L14320L14347.
- **현재 qcow2 data cluster** (store): L2 값이 있을 때 같은 qcow2 파일 안에서 읽는 데이터 클러스터. Evidence: L14267L14272, L14343L14345.
- **base.qcow2** (store): L2 항목이 0이고 backing file이 있을 때 같은 가상 위치를 읽는 바닥 파일. Evidence: L14343L14354.
- **zero-filled 64KB** (result): L2 항목이 0이고 backing file도 없을 때 만들어 반환하는 0 데이터. Evidence: L14343L14347.
## Relationships
- **L1 table → L2 entry:** L2 cluster. Evidence: L14320L14337.
- **L2 entry → base.qcow2:** 0 + backing. Evidence: L14343L14350.
- **L2 entry → 현재 qcow2 data cluster:** mapped. Evidence: L14343L14345.
- **L2 entry → zero-filled 64KB:** 0 + no backing. Evidence: L14343L14347.
- **Guest offset → L1 table:** 상위 비트. Evidence: L14317L14324.
@@ -0,0 +1,26 @@
# qcow2 L1·L2 매핑과 backing fallback
# Question: 게스트 오프셋은 qcow2 파일의 데이터 클러스터로 어떻게 매핑되고, L2 항목이 0일 때 왜 backing file이나 zero-fill로 갈리는가
direction: right
n0: "Guest offset" {
shape: rectangle
}
n1: "L1 table" {
shape: rectangle
}
n2: "L2 entry" {
shape: rectangle
}
n3: "현재 qcow2 data cluster" {
shape: rectangle
}
n4: "base.qcow2" {
shape: rectangle
}
n5: "zero-filled 64KB" {
shape: rectangle
}
n0 -> n1: "상위 비트"
n1 -> n2: "L2 cluster"
n2 -> n3: "mapped"
n2 -> n4: "0 + backing"
n2 -> n5: "0 + no backing"
@@ -0,0 +1,16 @@
digraph techviz {
graph [rankdir=LR, splines=ortho, nodesep=0.55, ranksep=0.85];
node [fontname=Helvetica, fontsize=11, margin="0.18,0.12", style="rounded,filled", fillcolor=white, color="#2d4357", penwidth=1.5];
edge [fontname=Helvetica, fontsize=10, color="#364b5f", penwidth=1.4, arrowsize=0.75];
n0 [label="Guest offset", shape=box, style="rounded,filled"];
n1 [label="L1 table", shape=box, style="rounded,filled"];
n2 [label="L2 entry", shape=box, style="rounded,filled"];
n3 [label="현재 qcow2 data cluster", shape=box, style="rounded,filled"];
n4 [label="base.qcow2", shape=box, style="rounded,filled"];
n5 [label="zero-filled 64KB", shape=box, style="rounded,filled"];
n0 -> n1 [label="상위 비트", style=solid];
n1 -> n2 [label="L2 cluster", style=solid];
n2 -> n3 [label="mapped", style=solid];
n2 -> n4 [label="0 + backing", style=solid];
n2 -> n5 [label="0 + no backing", style=solid];
}
@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<mxfile host="app.diagrams.net" modified="2026-07-23T00:00:00.000Z" agent="techviz-harness" version="24.7.17" type="device">
<diagram id="qcow2-mapping-clusters" name="qcow2 L1·L2 매핑과 backing fallback">
<mxGraphModel dx="1257" dy="448" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1257" pageHeight="1169" math="0" shadow="0">
<root>
<mxCell id="0"/>
<mxCell id="1" parent="0"/>
<mxCell id="n_guest-offset" value="Guest offset&lt;br/&gt;virtual disk position" tooltip="게스트가 읽으려는 가상 디스크 위치. | Evidence: L14317-L14325" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="70.0" y="196.0" width="181.0" height="71.0" as="geometry"/>
</mxCell>
<mxCell id="n_l1" value="L1 table&lt;br/&gt;상위 비트" tooltip="필요한 L2 표가 있는 클러스터 위치를 찾는다. | Evidence: L14312-L14337" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="411.0" y="196.0" width="150.0" height="71.0" as="geometry"/>
</mxCell>
<mxCell id="n_l2" value="L2 entry&lt;br/&gt;중간 13비트" tooltip="현재 qcow2 데이터 클러스터 오프셋을 가지거나 0일 수 있다. | Evidence: L14320-L14347" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;strokeColor=#2563eb;strokeWidth=2;" vertex="1" parent="1">
<mxGeometry x="721.0" y="196.0" width="150.0" height="71.0" as="geometry"/>
</mxCell>
<mxCell id="n_backing" value="base.qcow2&lt;br/&gt;backing file" tooltip="L2 항목이 0이고 backing file이 있을 때 같은 가상 위치를 읽는 바닥 파일. | Evidence: L14343-L14354" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;shape=cylinder3;boundedLbl=1;backgroundOutline=1;fillColor=#eef6fb;" vertex="1" parent="1">
<mxGeometry x="1046.5" y="60.0" width="150.0" height="71.0" as="geometry"/>
</mxCell>
<mxCell id="n_local-cluster" value="현재 qcow2 data cluster" tooltip="L2 값이 있을 때 같은 qcow2 파일 안에서 읽는 데이터 클러스터. | Evidence: L14267-L14272, L14343-L14345" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;shape=cylinder3;boundedLbl=1;backgroundOutline=1;fillColor=#eef6fb;" vertex="1" parent="1">
<mxGeometry x="1031.0" y="203.0" width="181.0" height="64.0" as="geometry"/>
</mxCell>
<mxCell id="n_zero-fill" value="zero-filled 64KB" tooltip="L2 항목이 0이고 backing file도 없을 때 만들어 반환하는 0 데이터. | Evidence: L14343-L14347" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="1046.5" y="339.0" width="150.0" height="64.0" as="geometry"/>
</mxCell>
<mxCell id="e_l1-l2" value="L2 cluster" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_l1" target="n_l2">
<mxGeometry relative="1" as="geometry">
<mxPoint x="641.0" y="203.5" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="e_l2-backing" value="0 + backing" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_l2" target="n_backing">
<mxGeometry relative="1" as="geometry">
<mxPoint x="982.8" y="154.5" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="e_l2-local" value="mapped" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_l2" target="n_local-cluster">
<mxGeometry relative="1" as="geometry">
<mxPoint x="975.0" y="233.2" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="e_l2-zero" value="0 + no backing" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_l2" target="n_zero-fill">
<mxGeometry relative="1" as="geometry">
<mxPoint x="982.8" y="310.2" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="e_offset-l1" value="상위 비트" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_guest-offset" target="n_l1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="331.0" y="203.5" as="offset"/>
</mxGeometry>
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>
@@ -0,0 +1,922 @@
{
"type": "excalidraw",
"version": 2,
"source": "techviz-harness",
"elements": [
{
"id": "edge-l1-l2",
"type": "arrow",
"x": 561.0,
"y": 231.5,
"width": 160.0,
"height": 0.0,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": null,
"roundness": null,
"seed": 586743950,
"version": 1,
"versionNonce": 1823449811,
"isDeleted": false,
"boundElements": [],
"updated": 0,
"link": null,
"locked": false,
"points": [
[
0.0,
0.0
],
[
80.0,
0.0
],
[
80.0,
0.0
],
[
160.0,
0.0
]
],
"lastCommittedPoint": null,
"startBinding": {
"elementId": "node-l1",
"focus": 0,
"gap": 4
},
"endBinding": {
"elementId": "node-l2",
"focus": 0,
"gap": 4
},
"startArrowhead": null,
"endArrowhead": "arrow",
"elbowed": true
},
{
"id": "edge-label-l1-l2",
"type": "text",
"x": 596.0,
"y": 191.5,
"width": 90,
"height": 24,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 0,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": null,
"roundness": {
"type": 3
},
"seed": 1344248471,
"version": 1,
"versionNonce": 1292323506,
"isDeleted": false,
"boundElements": [],
"updated": 0,
"link": null,
"locked": false,
"fontSize": 13,
"fontFamily": 5,
"text": "L2 cluster",
"textAlign": "center",
"verticalAlign": "middle",
"containerId": null,
"originalText": "L2 cluster",
"autoResize": true,
"lineHeight": 1.25
},
{
"id": "edge-l2-backing",
"type": "arrow",
"x": 871.0,
"y": 95.5,
"width": 175.5,
"height": 118.0,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": null,
"roundness": null,
"seed": 1567107232,
"version": 1,
"versionNonce": 1394231586,
"isDeleted": false,
"boundElements": [],
"updated": 0,
"link": null,
"locked": false,
"points": [
[
0.0,
118.0
],
[
87.75,
118.0
],
[
87.75,
0.0
],
[
175.5,
0.0
]
],
"lastCommittedPoint": null,
"startBinding": {
"elementId": "node-l2",
"focus": 0,
"gap": 4
},
"endBinding": {
"elementId": "node-backing",
"focus": 0,
"gap": 4
},
"startArrowhead": null,
"endArrowhead": "arrow",
"elbowed": true
},
{
"id": "edge-label-l2-backing",
"type": "text",
"x": 937.75,
"y": 142.5,
"width": 90,
"height": 24,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 0,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": null,
"roundness": {
"type": 3
},
"seed": 751142759,
"version": 1,
"versionNonce": 1636292120,
"isDeleted": false,
"boundElements": [],
"updated": 0,
"link": null,
"locked": false,
"fontSize": 13,
"fontFamily": 5,
"text": "0 + backing",
"textAlign": "center",
"verticalAlign": "middle",
"containerId": null,
"originalText": "0 + backing",
"autoResize": true,
"lineHeight": 1.25
},
{
"id": "edge-l2-local",
"type": "arrow",
"x": 871.0,
"y": 231.5,
"width": 160.0,
"height": 3.5,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": null,
"roundness": null,
"seed": 205827981,
"version": 1,
"versionNonce": 1468667393,
"isDeleted": false,
"boundElements": [],
"updated": 0,
"link": null,
"locked": false,
"points": [
[
0.0,
0.0
],
[
80.0,
0.0
],
[
80.0,
3.5
],
[
160.0,
3.5
]
],
"lastCommittedPoint": null,
"startBinding": {
"elementId": "node-l2",
"focus": 0,
"gap": 4
},
"endBinding": {
"elementId": "node-local-cluster",
"focus": 0,
"gap": 4
},
"startArrowhead": null,
"endArrowhead": "arrow",
"elbowed": true
},
{
"id": "edge-label-l2-local",
"type": "text",
"x": 930.0,
"y": 221.25,
"width": 90,
"height": 24,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 0,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": null,
"roundness": {
"type": 3
},
"seed": 1218557231,
"version": 1,
"versionNonce": 919123626,
"isDeleted": false,
"boundElements": [],
"updated": 0,
"link": null,
"locked": false,
"fontSize": 13,
"fontFamily": 5,
"text": "mapped",
"textAlign": "center",
"verticalAlign": "middle",
"containerId": null,
"originalText": "mapped",
"autoResize": true,
"lineHeight": 1.25
},
{
"id": "edge-l2-zero",
"type": "arrow",
"x": 871.0,
"y": 249.5,
"width": 175.5,
"height": 121.5,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": null,
"roundness": null,
"seed": 1572116611,
"version": 1,
"versionNonce": 650463629,
"isDeleted": false,
"boundElements": [],
"updated": 0,
"link": null,
"locked": false,
"points": [
[
0.0,
0.0
],
[
87.75,
0.0
],
[
87.75,
121.5
],
[
175.5,
121.5
]
],
"lastCommittedPoint": null,
"startBinding": {
"elementId": "node-l2",
"focus": 0,
"gap": 4
},
"endBinding": {
"elementId": "node-zero-fill",
"focus": 0,
"gap": 4
},
"startArrowhead": null,
"endArrowhead": "arrow",
"elbowed": true
},
{
"id": "edge-label-l2-zero",
"type": "text",
"x": 926.75,
"y": 298.25,
"width": 112,
"height": 24,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 0,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": null,
"roundness": {
"type": 3
},
"seed": 1455255978,
"version": 1,
"versionNonce": 35062350,
"isDeleted": false,
"boundElements": [],
"updated": 0,
"link": null,
"locked": false,
"fontSize": 13,
"fontFamily": 5,
"text": "0 + no backing",
"textAlign": "center",
"verticalAlign": "middle",
"containerId": null,
"originalText": "0 + no backing",
"autoResize": true,
"lineHeight": 1.25
},
{
"id": "edge-offset-l1",
"type": "arrow",
"x": 251.0,
"y": 231.5,
"width": 160.0,
"height": 0.0,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": null,
"roundness": null,
"seed": 1376738291,
"version": 1,
"versionNonce": 1345642548,
"isDeleted": false,
"boundElements": [],
"updated": 0,
"link": null,
"locked": false,
"points": [
[
0.0,
0.0
],
[
80.0,
0.0
],
[
80.0,
0.0
],
[
160.0,
0.0
]
],
"lastCommittedPoint": null,
"startBinding": {
"elementId": "node-guest-offset",
"focus": 0,
"gap": 4
},
"endBinding": {
"elementId": "node-l1",
"focus": 0,
"gap": 4
},
"startArrowhead": null,
"endArrowhead": "arrow",
"elbowed": true
},
{
"id": "edge-label-offset-l1",
"type": "text",
"x": 286.0,
"y": 191.5,
"width": 90,
"height": 24,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 0,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": null,
"roundness": {
"type": 3
},
"seed": 1804422112,
"version": 1,
"versionNonce": 797123369,
"isDeleted": false,
"boundElements": [],
"updated": 0,
"link": null,
"locked": false,
"fontSize": 13,
"fontFamily": 5,
"text": "상위 비트",
"textAlign": "center",
"verticalAlign": "middle",
"containerId": null,
"originalText": "상위 비트",
"autoResize": true,
"lineHeight": 1.25
},
{
"id": "node-guest-offset",
"type": "rectangle",
"x": 70.0,
"y": 196.0,
"width": 181.0,
"height": 71.0,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "#ffffff",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": null,
"roundness": {
"type": 3
},
"seed": 1190817156,
"version": 1,
"versionNonce": 139318315,
"isDeleted": false,
"boundElements": [],
"updated": 0,
"link": null,
"locked": false
},
{
"id": "node-label-guest-offset",
"type": "text",
"x": 80.0,
"y": 206.0,
"width": 161.0,
"height": 51.0,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 0,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": null,
"roundness": {
"type": 3
},
"seed": 83110366,
"version": 1,
"versionNonce": 49655676,
"isDeleted": false,
"boundElements": [],
"updated": 0,
"link": null,
"locked": false,
"fontSize": 15,
"fontFamily": 5,
"text": "Guest offset\nvirtual disk position",
"textAlign": "center",
"verticalAlign": "middle",
"containerId": null,
"originalText": "Guest offset\nvirtual disk position",
"autoResize": true,
"lineHeight": 1.25
},
{
"id": "node-l1",
"type": "rectangle",
"x": 411.0,
"y": 196.0,
"width": 150.0,
"height": 71.0,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "#ffffff",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": null,
"roundness": {
"type": 3
},
"seed": 1774178777,
"version": 1,
"versionNonce": 229505492,
"isDeleted": false,
"boundElements": [],
"updated": 0,
"link": null,
"locked": false
},
{
"id": "node-label-l1",
"type": "text",
"x": 421.0,
"y": 206.0,
"width": 130.0,
"height": 51.0,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 0,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": null,
"roundness": {
"type": 3
},
"seed": 15495054,
"version": 1,
"versionNonce": 244208457,
"isDeleted": false,
"boundElements": [],
"updated": 0,
"link": null,
"locked": false,
"fontSize": 15,
"fontFamily": 5,
"text": "L1 table\n상위 비트",
"textAlign": "center",
"verticalAlign": "middle",
"containerId": null,
"originalText": "L1 table\n상위 비트",
"autoResize": true,
"lineHeight": 1.25
},
{
"id": "node-l2",
"type": "rectangle",
"x": 721.0,
"y": 196.0,
"width": 150.0,
"height": 71.0,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "#ffffff",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": null,
"roundness": {
"type": 3
},
"seed": 207729084,
"version": 1,
"versionNonce": 618174549,
"isDeleted": false,
"boundElements": [],
"updated": 0,
"link": null,
"locked": false
},
{
"id": "node-label-l2",
"type": "text",
"x": 731.0,
"y": 206.0,
"width": 130.0,
"height": 51.0,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 0,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": null,
"roundness": {
"type": 3
},
"seed": 248933745,
"version": 1,
"versionNonce": 1248263086,
"isDeleted": false,
"boundElements": [],
"updated": 0,
"link": null,
"locked": false,
"fontSize": 15,
"fontFamily": 5,
"text": "L2 entry\n중간 13비트",
"textAlign": "center",
"verticalAlign": "middle",
"containerId": null,
"originalText": "L2 entry\n중간 13비트",
"autoResize": true,
"lineHeight": 1.25
},
{
"id": "node-backing",
"type": "rectangle",
"x": 1046.5,
"y": 60.0,
"width": 150.0,
"height": 71.0,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "#ffffff",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": null,
"roundness": {
"type": 3
},
"seed": 241530005,
"version": 1,
"versionNonce": 110043311,
"isDeleted": false,
"boundElements": [],
"updated": 0,
"link": null,
"locked": false
},
{
"id": "node-label-backing",
"type": "text",
"x": 1056.5,
"y": 70.0,
"width": 130.0,
"height": 51.0,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 0,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": null,
"roundness": {
"type": 3
},
"seed": 1088329617,
"version": 1,
"versionNonce": 1471763197,
"isDeleted": false,
"boundElements": [],
"updated": 0,
"link": null,
"locked": false,
"fontSize": 15,
"fontFamily": 5,
"text": "base.qcow2\nbacking file",
"textAlign": "center",
"verticalAlign": "middle",
"containerId": null,
"originalText": "base.qcow2\nbacking file",
"autoResize": true,
"lineHeight": 1.25
},
{
"id": "node-local-cluster",
"type": "rectangle",
"x": 1031.0,
"y": 203.0,
"width": 181.0,
"height": 64.0,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "#ffffff",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": null,
"roundness": {
"type": 3
},
"seed": 1087803653,
"version": 1,
"versionNonce": 174713902,
"isDeleted": false,
"boundElements": [],
"updated": 0,
"link": null,
"locked": false
},
{
"id": "node-label-local-cluster",
"type": "text",
"x": 1041.0,
"y": 213.0,
"width": 161.0,
"height": 44.0,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 0,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": null,
"roundness": {
"type": 3
},
"seed": 1290752056,
"version": 1,
"versionNonce": 635359524,
"isDeleted": false,
"boundElements": [],
"updated": 0,
"link": null,
"locked": false,
"fontSize": 15,
"fontFamily": 5,
"text": "현재 qcow2 data cluster",
"textAlign": "center",
"verticalAlign": "middle",
"containerId": null,
"originalText": "현재 qcow2 data cluster",
"autoResize": true,
"lineHeight": 1.25
},
{
"id": "node-zero-fill",
"type": "rectangle",
"x": 1046.5,
"y": 339.0,
"width": 150.0,
"height": 64.0,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "#ffffff",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": null,
"roundness": {
"type": 3
},
"seed": 1358300415,
"version": 1,
"versionNonce": 304765438,
"isDeleted": false,
"boundElements": [],
"updated": 0,
"link": null,
"locked": false
},
{
"id": "node-label-zero-fill",
"type": "text",
"x": 1056.5,
"y": 349.0,
"width": 130.0,
"height": 44.0,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 0,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": null,
"roundness": {
"type": 3
},
"seed": 720572336,
"version": 1,
"versionNonce": 603273159,
"isDeleted": false,
"boundElements": [],
"updated": 0,
"link": null,
"locked": false,
"fontSize": 15,
"fontFamily": 5,
"text": "zero-filled 64KB",
"textAlign": "center",
"verticalAlign": "middle",
"containerId": null,
"originalText": "zero-filled 64KB",
"autoResize": true,
"lineHeight": 1.25
}
],
"appState": {
"gridSize": 10,
"viewBackgroundColor": "#ffffff",
"currentItemFontFamily": 5
},
"files": {}
}
@@ -0,0 +1,42 @@
{
"harness_version": "0.2.0",
"spec_id": "qcow2-mapping-clusters",
"spec_version": "1.1",
"spec_sha256": "7f8a8cdf4d78168e56ca829ead8d8e574479d11d431891d3d9e5fce5b40635a4",
"source_context": {
"document": "docs/virtualization/final/document.md",
"document_sha256": "8c4ecc64c8cea9a4450ed7131fdd9cb2048dc092b66cd969f6346ed77887c210",
"anchor": {
"kind": "heading",
"value": "231. qcow2 파일 내부는 어떻게 생겼나 — 매핑표가 전부다",
"line": 14221
}
},
"outputs": [
"qcow2-mapping-clusters.svg",
"qcow2-mapping-clusters.mmd",
"qcow2-mapping-clusters.d2",
"qcow2-mapping-clusters.dot",
"qcow2-mapping-clusters.drawio",
"qcow2-mapping-clusters.excalidraw",
"qcow2-mapping-clusters.alt.md"
],
"lint_issue_count": 0,
"assumption_count": 0,
"assumptions_allowed": false,
"composition_profile": "component-flow",
"reference_ids": [
"payment-event-flow"
],
"diagram_only": true,
"source_spec_file_sha256": "12590f917b1fa302a293b29584e40de224f9b280f479110128cde4878b89cd65",
"output_sha256": {
"qcow2-mapping-clusters.svg": "9668c9c2e9a6e67d290833f4c055607744c6b8448c89fdb5c2e374a1832e3324",
"qcow2-mapping-clusters.mmd": "614782850ad8082f2d2b8b6eadd5969d3a71e6fb77fb08173b87420a1b1b16a3",
"qcow2-mapping-clusters.d2": "5508d5d8d5496f41b537ae94d98ceb4f42d685ae8d0b0ae895827ce8b52949a1",
"qcow2-mapping-clusters.dot": "e5423cf82ef26c98d391e7f5553959332ebe4808c28ac4e74a93450c04c4a656",
"qcow2-mapping-clusters.drawio": "bfcea347243b06067ec55e63f582e2d07213a1c76ddbbc7df907a5c5feb67506",
"qcow2-mapping-clusters.excalidraw": "8fa0e7c79497e1e13223ebc2601c57872c5dcb58cb2bf7e2bd8008b5ef3d1c98",
"qcow2-mapping-clusters.alt.md": "f72433e1f4f80441e41bd668dd67818e5837a14e2a3b0a1b703fa2e14c3bfda6"
}
}
@@ -0,0 +1,14 @@
%% qcow2 L1·L2 매핑과 backing fallback
%% question: 게스트 오프셋은 qcow2 파일의 데이터 클러스터로 어떻게 매핑되고, L2 항목이 0일 때 왜 backing file이나 zero-fill로 갈리는가
flowchart LR
n0["Guest offset"]
n1["L1 table"]
n2["L2 entry"]
n3["현재 qcow2 data cluster"]
n4["base.qcow2"]
n5["zero-filled 64KB"]
n0 -->|"상위 비트"| n1
n1 -->|"L2 cluster"| n2
n2 -->|"mapped"| n3
n2 -->|"0 + backing"| n4
n2 -->|"0 + no backing"| n5
@@ -0,0 +1,100 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="1257" height="448" viewBox="0 0 1257 448" role="img" aria-labelledby="diagram-title diagram-description">
<title id="diagram-title">qcow2 L1·L2 매핑과 backing fallback</title>
<desc id="diagram-description">게스트가 읽으려는 위치는 L1 표와 L2 표를 통해 파일 안 데이터 클러스터 오프셋으로 변환된다. 매핑된 값이 있으면 현재 qcow2의 데이터 클러스터를 읽는다. 값이 0이고 backing file이 있으면 base.qcow2의 같은 가상 위치를 읽고, backing file이 없으면 0으로 채운 64KB를 반환한다. backing file 경로는 qcow2 헤더의 문자열이므로 오버레이만 옮기면 부팅할 수 없다는 제한도 그림 끝에 반영한다.</desc>
<metadata>{&quot;techviz&quot;:{&quot;spec_version&quot;:&quot;1.1&quot;,&quot;id&quot;:&quot;qcow2-mapping-clusters&quot;,&quot;profile&quot;:&quot;component-flow&quot;},&quot;source_context&quot;:{&quot;document&quot;:&quot;docs/virtualization/final/document.md&quot;,&quot;document_sha256&quot;:&quot;8c4ecc64c8cea9a4450ed7131fdd9cb2048dc092b66cd969f6346ed77887c210&quot;,&quot;anchor&quot;:{&quot;kind&quot;:&quot;heading&quot;,&quot;value&quot;:&quot;231. qcow2 파일 내부는 어떻게 생겼나 — 매핑표가 전부다&quot;,&quot;line&quot;:14221}},&quot;evidence_policy&quot;:&quot;Each factual element cites source lines or is marked assumption.&quot;,&quot;diagram_only&quot;:true}</metadata>
<defs>
<marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M 0 0 L 10 5 L 0 10 z" />
</marker>
<style>
:root { color-scheme: light; }
text { font-family: Inter, Pretendard, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #111827; }
.canvas { fill: #ffffff; }
.group-box { fill: #ffffff; stroke: #9ca3af; stroke-width: 1.4; stroke-dasharray: 7 5; }
.group-label-bg { fill: #ffffff; }
.group-label { font-size: 13px; font-weight: 650; fill: #374151; }
.edge { fill: none; stroke: #374151; stroke-width: 1.8; stroke-linejoin: round; stroke-linecap: round; marker-end: url(#arrow); }
.edge.style-dashed, .edge.semantic-dashed, .edge.assumption { stroke-dasharray: 7 5; }
.edge.style-dotted { stroke-dasharray: 2 5; }
.edge.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.edge.emphasis-muted { stroke: #9ca3af; }
.edge.emphasis-warning, .edge.kind-failure, .edge.kind-error { stroke: #dc2626; stroke-width: 2.2; }
.edge-label-bg { fill: #ffffff; }
.edge-label { font-size: 12px; font-weight: 560; text-anchor: middle; }
.node-shape { fill: #ffffff; stroke: #4b5563; stroke-width: 1.7; }
.node-shape.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.node-shape.emphasis-muted { stroke: #9ca3af; fill: #f9fafb; }
.node-shape.emphasis-warning { stroke: #d97706; stroke-width: 2; fill: #fffdf5; }
.node-shape.kind-database, .node-shape.kind-datastore, .node-shape.kind-storage { fill: #f8fafc; }
.node-shape.kind-queue, .node-shape.kind-event, .node-shape.kind-topic { fill: #fafafa; }
.node-shape.assumption { stroke-dasharray: 4 4; }
.storage-bottom, .controller-divider { fill: none; stroke: #4b5563; stroke-width: 1.4; }
.controller-led { fill: #4b5563; }
.actor-symbol { fill: none; stroke: #4b5563; stroke-width: 1.8; stroke-linecap: round; }
.actor-symbol.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.node-label { font-size: 14px; font-weight: 650; text-anchor: middle; }
.node-role { font-size: 10px; letter-spacing: 0.04em; text-anchor: middle; fill: #6b7280; }
.node-detail-divider { stroke: #d1d5db; stroke-width: 1; }
.node-detail { font-size: 11px; fill: #374151; }
.assumption-badge { font-size: 9px; font-weight: 700; fill: #92400e; }
.failure-mark { stroke: #dc2626; stroke-width: 4; stroke-linecap: round; }
.lifeline { stroke: #9ca3af; stroke-width: 1.2; stroke-dasharray: 5 5; }
.timeline-axis { stroke: #374151; stroke-width: 1.8; marker-end: url(#arrow); }
.timeline-stem { stroke: #6b7280; stroke-width: 1.3; }
.timeline-marker { fill: #ffffff; stroke: #374151; stroke-width: 1.7; }
.timeline-marker.primary { fill: #2563eb; stroke: #2563eb; }
.timeline-marker.warning { fill: #dc2626; stroke: #dc2626; }
.timeline-label { font-size: 13px; font-weight: 650; text-anchor: middle; }
.timeline-detail { font-size: 11px; fill: #4b5563; text-anchor: middle; }
</style>
</defs>
<rect class="canvas" width="1257" height="448" />
<polyline class="edge kind-data style-solid emphasis-normal" points="561.0,231.5 641.0,231.5 641.0,231.5 721.0,231.5" data-evidence="14320-14337" />
<rect class="edge-label-bg" x="598.5" y="189.5" width="85.0" height="22" rx="3" />
<text class="edge-label" x="641.0" y="204.5">L2 cluster</text>
<polyline class="edge kind-data style-dashed emphasis-normal" points="871.0,213.5 958.8,213.5 958.8,95.5 1046.5,95.5" data-evidence="14343-14350" />
<rect class="edge-label-bg" x="936.9" y="140.5" width="91.7" height="22" rx="3" />
<text class="edge-label" x="982.8" y="155.5">0 + backing</text>
<polyline class="edge kind-data style-solid emphasis-normal" points="871.0,231.5 951.0,231.5 951.0,235.0 1031.0,235.0" data-evidence="14343-14345" />
<rect class="edge-label-bg" x="945.9" y="219.2" width="58.2" height="22" rx="3" />
<text class="edge-label" x="975.0" y="234.2">mapped</text>
<polyline class="edge kind-result style-dashed emphasis-normal" points="871.0,249.5 958.8,249.5 958.8,371.0 1046.5,371.0" data-evidence="14343-14347" />
<rect class="edge-label-bg" x="926.9" y="296.2" width="111.8" height="22" rx="3" />
<text class="edge-label" x="982.8" y="311.2">0 + no backing</text>
<polyline class="edge kind-data style-solid emphasis-normal" points="251.0,231.5 331.0,231.5 331.0,231.5 411.0,231.5" data-evidence="14317-14324" />
<rect class="edge-label-bg" x="305.2" y="189.5" width="51.5" height="22" rx="3" />
<text class="edge-label" x="331.0" y="204.5">상위 비트</text>
<g id="node-guest-offset">
<rect class="node-shape kind-data emphasis-normal role-source" data-evidence="14317-14325" x="70.0" y="196.0" width="181.0" height="71.0" rx="7" />
<text class="node-label" x="160.5" y="223.0">Guest offset</text>
<line class="node-detail-divider" x1="84.0" y1="244.0" x2="237.0" y2="244.0" />
<text class="node-detail" x="86.0" y="261.0">virtual disk position</text>
</g>
<g id="node-l1">
<rect class="node-shape kind-component emphasis-normal role-service" data-evidence="14312-14337" x="411.0" y="196.0" width="150.0" height="71.0" rx="7" />
<text class="node-label" x="486.0" y="223.0">L1 table</text>
<line class="node-detail-divider" x1="425.0" y1="244.0" x2="547.0" y2="244.0" />
<text class="node-detail" x="427.0" y="261.0">상위 비트</text>
</g>
<g id="node-l2">
<rect class="node-shape kind-component emphasis-primary role-service" data-evidence="14320-14347" x="721.0" y="196.0" width="150.0" height="71.0" rx="7" />
<text class="node-label" x="796.0" y="223.0">L2 entry</text>
<line class="node-detail-divider" x1="735.0" y1="244.0" x2="857.0" y2="244.0" />
<text class="node-detail" x="737.0" y="261.0">중간 13비트</text>
</g>
<g id="node-backing">
<rect class="node-shape kind-store emphasis-normal role-store" data-evidence="14343-14354" x="1046.5" y="71.8" width="150.0" height="47.3" /><ellipse class="node-shape kind-store emphasis-normal role-store" cx="1121.5" cy="71.8" rx="75.0" ry="11.8" /><path class="storage-bottom" d="M 1046.5 119.2 A 75.0 11.8 0 0 0 1196.5 119.2" />
<text class="node-label" x="1121.5" y="87.0">base.qcow2</text>
<line class="node-detail-divider" x1="1060.5" y1="108.0" x2="1182.5" y2="108.0" />
<text class="node-detail" x="1062.5" y="125.0">backing file</text>
</g>
<g id="node-local-cluster">
<rect class="node-shape kind-store emphasis-normal role-store" data-evidence="14267-14272,14343-14345" x="1031.0" y="213.7" width="181.0" height="42.7" /><ellipse class="node-shape kind-store emphasis-normal role-store" cx="1121.5" cy="213.7" rx="90.5" ry="10.7" /><path class="storage-bottom" d="M 1031.0 256.3 A 90.5 10.7 0 0 0 1212.0 256.3" />
<text class="node-label" x="1121.5" y="233.0">현재 qcow2 data cluster</text>
</g>
<g id="node-zero-fill">
<rect class="node-shape kind-result emphasis-normal role-sink" data-evidence="14343-14347" x="1046.5" y="339.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="1121.5" y="369.0">zero-filled 64KB</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 8.4 KiB

@@ -0,0 +1,26 @@
# virtqueue 뒤 QEMU block backend의 세 형태
## Alternative text
Guest의 /dev/vda가 virtio-blk와 virtqueue를 지나 QEMU block backend에 도착한 뒤 qcow2 파일, RAW 파일, Host block device 세 갈래로 나뉘는 구조도. 그 아래 Host block stack은 별도 Concept이 맡아 이 그림에서는 다시 합치지 않는다.
## Long description
왼쪽에서 Guest /dev/vda 요청이 virtio-blk와 virtqueue를 지나 VM boundary를 넘는다. QEMU block backend에서 qcow2 file, RAW file, Host block device 세 갈래로 나뉜다. qcow2와 RAW 노드에는 Host filesystem을 경유한다는 차이를, Host block device에는 파일 backend가 아니라는 차이를 적었다. 세 경로 아래의 Host block stack은 별도 Concept이 맡으므로 이 그림에서는 다시 합치지 않는다.
## Elements and evidence
- **/dev/vda** (storage): Guest가 보는 virtual disk. Evidence: L6355L6363.
- **virtio-blk · virtqueue** (component): Guest /dev/vda 요청을 virtio-blk가 받아 virtqueue에 싣는 경로를 한 노드로 묶었다. Evidence: L6355L6364.
- **QEMU block backend** (service): Guest virtual I/O를 실제 Host backend에 연결한다. Evidence: L6367L6387.
- **qcow2 file** (storage): sparse/COW 기능을 가진 file backend. Evidence: L6419L6460, L6512L6547.
- **RAW file** (storage): 상대적으로 단순한 offset 대응을 쓰는 file backend. Evidence: L6512L6547.
- **Host block device** (storage): QEMU가 Host block device를 직접 backend로 사용할 수 있는 형태. Evidence: L6551L6575.
## Relationships
- **QEMU block backend → Host block device:** block-device backend. Evidence: L6551L6575.
- **QEMU block backend → qcow2 file:** qcow2 backend. Evidence: L6369L6379, L6419L6460.
- **QEMU block backend → RAW file:** RAW backend. Evidence: L6512L6547.
- **/dev/vda → virtio-blk · virtqueue:** block I/O. Evidence: L6355L6363.
- **virtio-blk · virtqueue → QEMU block backend:** VM boundary. Evidence: L6362L6374.
@@ -0,0 +1,26 @@
# virtqueue 뒤 QEMU block backend의 세 형태
# Question: Guest /dev/vda 요청이 VM boundary를 넘은 뒤 QEMU에서 qcow2, RAW, Host block device 중 어느 backend로 이어지는가
direction: right
n0: "/dev/vda" {
shape: cylinder
}
n1: "virtio-blk · virtqueue" {
shape: rectangle
}
n2: "QEMU block backend" {
shape: rectangle
}
n3: "qcow2 file" {
shape: cylinder
}
n4: "RAW file" {
shape: cylinder
}
n5: "Host block device" {
shape: cylinder
}
n0 -> n1: "block I/O"
n1 -> n2: "VM boundary"
n2 -> n3: "qcow2 backend"
n2 -> n4: "RAW backend"
n2 -> n5: "block-device backend"
@@ -0,0 +1,16 @@
digraph techviz {
graph [rankdir=LR, splines=ortho, nodesep=0.55, ranksep=0.85];
node [fontname=Helvetica, fontsize=11, margin="0.18,0.12", style="rounded,filled", fillcolor=white, color="#2d4357", penwidth=1.5];
edge [fontname=Helvetica, fontsize=10, color="#364b5f", penwidth=1.4, arrowsize=0.75];
n0 [label="/dev/vda", shape=cylinder, style="rounded,filled"];
n1 [label="virtio-blk · virtqueue", shape=box, style="rounded,filled"];
n2 [label="QEMU block backend", shape=box, style="rounded,filled"];
n3 [label="qcow2 file", shape=cylinder, style="rounded,filled"];
n4 [label="RAW file", shape=cylinder, style="rounded,filled"];
n5 [label="Host block device", shape=cylinder, style="rounded,filled"];
n0 -> n1 [label="block I/O", style=solid];
n1 -> n2 [label="VM boundary", style=solid];
n2 -> n3 [label="qcow2 backend", style=solid];
n2 -> n4 [label="RAW backend", style=solid];
n2 -> n5 [label="block-device backend", style=solid];
}

Some files were not shown because too many files have changed in this diff Show More