feat: 가상화 문서들 추가
@@ -0,0 +1,30 @@
|
||||
# virtualization
|
||||
|
||||
- 분석 대상: 없음. 밖에서 가져온 문서 네 편이 출발점이다
|
||||
- 이 폴더: SSOT(`final/document.md`)와 Studio 에 올릴 글(`tech-log-studio/`)의 정본
|
||||
|
||||
## 범위
|
||||
|
||||
가상화 전반을 담을 프로젝트다. SSOT 는 네 부이고 절 번호가 문서 전체에서 이어진다.
|
||||
|
||||
| 부 | 절 | 반입 전 원본 |
|
||||
|---|---|---|
|
||||
| 제1부 — CPU 가상화 | §1~§28 | `kvm-cpu-virtualization-ssot.md` (절 1~28) |
|
||||
| 제2부 — 메모리 가상화 | §29~§88 | `kvm-memory-virtualization-ssot.md` (절 1~60) |
|
||||
| 제3부 — 네트워크 가상화 | §89~§126 | `kvm-network-virtualization-ssot.md` (절 1~38) |
|
||||
| 제4부 — 스토리지 가상화 | §127~§177 | `kvm-storage-virtualization-ssot.md` (절 0~50) |
|
||||
|
||||
반입할 때 heading 단계를 한 칸 내리고 절 번호만 옮겼다. heading 이 아닌 줄은 한 글자도
|
||||
바꾸지 않았고, 원본과 대조해 확인했다. PCIe/VFIO/IOMMU 상세와 K3s 네트워크·컨테이너
|
||||
런타임 상세는 어느 부에도 없다.
|
||||
|
||||
## 끝난 프로젝트의 폴더
|
||||
|
||||
| 폴더 | 무엇 |
|
||||
|---|---|
|
||||
| `final/` | SSOT — 이 프로젝트에 대해 아는 것 전부 |
|
||||
| `tech-log-studio/` | SSOT 에서 뽑아 쓴 글. 정본은 `tech-log-tree.json` |
|
||||
|
||||
## 반입하는 동안에만 있는 것
|
||||
|
||||
`source/` — 밖에서 가져온 원본. `final/` 이 그 내용을 담으면 지운다.
|
||||
@@ -0,0 +1,2 @@
|
||||
그림의 정본 — <이름>/{context.json, spec.json, prompt.md}.
|
||||
technical-visualizer 스킬이 만든다. 손으로 SVG 를 그리지 않는다.
|
||||
@@ -0,0 +1,338 @@
|
||||
{
|
||||
"version": "1.1",
|
||||
"id": "guest-block-io-to-virtqueue",
|
||||
"title": "게스트의 write() 가 virtqueue 에 실리기까지",
|
||||
"question": "게스트 애플리케이션의 write() 는 virtqueue 에 실리기 전에 게스트의 어느 계층을 어떤 순서로 지나는가",
|
||||
"type": "flow",
|
||||
"direction": "TB",
|
||||
"audience": [
|
||||
"게스트에서 본 저장소 지연을 계층별로 가르려는 사람",
|
||||
"virtio-blk 경로를 처음 읽는 사람"
|
||||
],
|
||||
"summary": "게스트 애플리케이션의 write() 는 VFS 와 ext4/XFS, Guest Page Cache, Guest Block Layer, /dev/vda, virtio-blk Frontend 를 차례로 지나 virtqueue 에 실린다.",
|
||||
"alt": "게스트 사용자 공간의 PostgreSQL/Keycloak 에서 시작한 write() 가 게스트 커널의 VFS, ext4/XFS, Guest Page Cache, Guest Block Layer, /dev/vda, virtio-blk Frontend 를 거쳐 virtqueue 에 도달하는 위에서 아래로 읽는 흐름도.",
|
||||
"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",
|
||||
"anchor": {
|
||||
"kind": "heading",
|
||||
"value": "128. 전체 구조",
|
||||
"line": 5804
|
||||
}
|
||||
},
|
||||
"composition": {
|
||||
"profile": "component-flow",
|
||||
"diagram_only": true,
|
||||
"reference_ids": [
|
||||
"payment-event-flow"
|
||||
],
|
||||
"rationale": "이 절이 주장하는 것은 요청 하나가 한 방향으로 지나가는 계층의 순서다. sequence 는 참여자마다 생명선을 세우고 메시지를 주고받는 그림인데 이 구간에는 되돌아오는 메시지가 없어 생명선이 전부 빈 세로줄이 된다. two-zone-pipeline 은 구역 안쪽 엣지 라벨을 캔버스 위쪽에 고정해 writeback 과 WRITE / FLUSH 가 제자리를 잃는다. 구역은 groups 로 두고 흐름은 component-flow 로 세운다.",
|
||||
"focus_node": "virtqueue"
|
||||
},
|
||||
"groups": [
|
||||
{
|
||||
"id": "guest-userspace",
|
||||
"label": "Guest Userspace",
|
||||
"kind": "boundary",
|
||||
"description": "게스트 사용자 공간. 저장장치를 직접 다루지 않는다.",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 5807,
|
||||
"end_line": 5809
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "guest-kernel",
|
||||
"label": "Guest Kernel",
|
||||
"kind": "boundary",
|
||||
"description": "게스트 커널 공간. VM Boundary 위쪽 계층이 전부 여기 있다.",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 5815,
|
||||
"end_line": 5833
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
}
|
||||
],
|
||||
"nodes": [
|
||||
{
|
||||
"id": "app",
|
||||
"label": "PostgreSQL / Keycloak",
|
||||
"kind": "service",
|
||||
"role": "source",
|
||||
"shape": "box",
|
||||
"group": "guest-userspace",
|
||||
"description": "파일 연산을 요청하는 게스트 프로세스.",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 5807,
|
||||
"end_line": 5809
|
||||
},
|
||||
{
|
||||
"start_line": 5885,
|
||||
"end_line": 5885
|
||||
},
|
||||
{
|
||||
"start_line": 5811,
|
||||
"end_line": 5813
|
||||
}
|
||||
],
|
||||
"assumption": false,
|
||||
"details": [
|
||||
"read / write",
|
||||
"fsync / sync"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "vfs",
|
||||
"label": "VFS",
|
||||
"kind": "service",
|
||||
"role": "service",
|
||||
"shape": "box",
|
||||
"group": "guest-kernel",
|
||||
"description": "열린 파일이 어느 filesystem 구현에 속하는지 보고 연산을 넘기는 공통 계층.",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 5815,
|
||||
"end_line": 5817
|
||||
},
|
||||
{
|
||||
"start_line": 5905,
|
||||
"end_line": 5905
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "fs",
|
||||
"label": "ext4 / XFS",
|
||||
"kind": "service",
|
||||
"role": "service",
|
||||
"shape": "box",
|
||||
"group": "guest-kernel",
|
||||
"description": "파일과 디렉터리를 block 공간에 배치하는 게스트 filesystem.",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 5819,
|
||||
"end_line": 5819
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "page-cache",
|
||||
"label": "Guest Page Cache",
|
||||
"kind": "store",
|
||||
"role": "store",
|
||||
"shape": "box",
|
||||
"group": "guest-kernel",
|
||||
"description": "게스트 RAM 에 있는 cache. write() 가 성공해도 데이터는 아직 여기까지만 확정된다.",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 5821,
|
||||
"end_line": 5821
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "block-layer",
|
||||
"label": "Guest Block Layer",
|
||||
"kind": "service",
|
||||
"role": "service",
|
||||
"shape": "box",
|
||||
"group": "guest-kernel",
|
||||
"description": "파일 세계의 요청을 block device 세계의 요청으로 바꾸는 계층.",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 5825,
|
||||
"end_line": 5825
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "vda",
|
||||
"label": "/dev/vda",
|
||||
"kind": "device",
|
||||
"role": "service",
|
||||
"shape": "box",
|
||||
"group": "guest-kernel",
|
||||
"description": "게스트 Linux 가 하나의 block device 로 인식하는 이름.",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 5829,
|
||||
"end_line": 5829
|
||||
},
|
||||
{
|
||||
"start_line": 5879,
|
||||
"end_line": 5879
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "virtio-blk",
|
||||
"label": "virtio-blk Frontend",
|
||||
"kind": "service",
|
||||
"role": "service",
|
||||
"shape": "box",
|
||||
"group": "guest-kernel",
|
||||
"description": "그 가상 block device 를 제어하는 게스트 커널 driver.",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 5831,
|
||||
"end_line": 5831
|
||||
},
|
||||
{
|
||||
"start_line": 5789,
|
||||
"end_line": 5789
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "virtqueue",
|
||||
"label": "virtqueue",
|
||||
"kind": "queue",
|
||||
"role": "queue",
|
||||
"shape": "box",
|
||||
"emphasis": "primary",
|
||||
"group": "guest-kernel",
|
||||
"description": "요청이 실리는 자리. 바로 아래가 VM Boundary 다.",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 5833,
|
||||
"end_line": 5835
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
}
|
||||
],
|
||||
"edges": [
|
||||
{
|
||||
"id": "app-to-vfs",
|
||||
"from": "app",
|
||||
"to": "vfs",
|
||||
"label": "",
|
||||
"kind": "data",
|
||||
"style": "solid",
|
||||
"order": 1,
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 5811,
|
||||
"end_line": 5813
|
||||
},
|
||||
{
|
||||
"start_line": 5890,
|
||||
"end_line": 5890
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "vfs-to-fs",
|
||||
"from": "vfs",
|
||||
"to": "fs",
|
||||
"label": "",
|
||||
"kind": "data",
|
||||
"style": "solid",
|
||||
"order": 2,
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 5817,
|
||||
"end_line": 5819
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "fs-to-page-cache",
|
||||
"from": "fs",
|
||||
"to": "page-cache",
|
||||
"label": "",
|
||||
"kind": "data",
|
||||
"style": "solid",
|
||||
"order": 3,
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 5819,
|
||||
"end_line": 5821
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "page-cache-to-block-layer",
|
||||
"from": "page-cache",
|
||||
"to": "block-layer",
|
||||
"label": "writeback",
|
||||
"kind": "data",
|
||||
"style": "solid",
|
||||
"order": 4,
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 5823,
|
||||
"end_line": 5825
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "block-layer-to-vda",
|
||||
"from": "block-layer",
|
||||
"to": "vda",
|
||||
"label": "WRITE / FLUSH",
|
||||
"kind": "data",
|
||||
"style": "solid",
|
||||
"order": 5,
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 5827,
|
||||
"end_line": 5829
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "vda-to-virtio-blk",
|
||||
"from": "vda",
|
||||
"to": "virtio-blk",
|
||||
"label": "",
|
||||
"kind": "data",
|
||||
"style": "solid",
|
||||
"order": 6,
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 5829,
|
||||
"end_line": 5831
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "virtio-blk-to-virtqueue",
|
||||
"from": "virtio-blk",
|
||||
"to": "virtqueue",
|
||||
"label": "Virtio block request",
|
||||
"kind": "data",
|
||||
"style": "solid",
|
||||
"order": 7,
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 5831,
|
||||
"end_line": 5833
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
}
|
||||
],
|
||||
"legend": [],
|
||||
"metadata": {
|
||||
"rationale": "component-flow 를 고른 이유는 이 절이 답하는 물음이 「write() 가 virtqueue 에 닿기까지 무엇을 어떤 순서로 지나는가」이기 때문이다. Guest Userspace 와 Guest Kernel 은 §128 이 대괄호로 직접 그어 둔 구역이라 groups 로 두었다. 완료가 같은 virtqueue 로 되돌아오는 역방향은 §166 과 §172 가 대는 사실인데 두 절 모두 이 앵커의 준비된 문맥(§127~§129) 밖이라 넣지 않았다 — 그 문맥 안에서 인용할 줄이 없다. VM Boundary 아래의 QEMU·호스트 계층은 같은 §128 안에 있지만 이 기록이 virtqueue 까지만 따라간다고 밝혀 두었고, 그 아래는 다른 기록의 그림이 맡는다. 첫 엣지에 write() 라벨을 달았더니 라벨 상자가 Guest Kernel 구역 이름을 덮어(PNG 로 확인) 라벨을 지우고 그 두 줄을 출발 노드의 details 로 내렸다. virtio-blk Frontend → virtqueue 엣지의 라벨 「Virtio block request」는 이 앵커의 문맥이 화살표만 그려 두어 이름 자체는 §138 이 대는 것이고, 기록 본문도 같은 이름을 쓴다."
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,285 @@
|
||||
{
|
||||
"version": "1.1",
|
||||
"id": "guest-memory-address-translation-path",
|
||||
"title": "Guest 의 메모리 접근 한 번은 이름이 두 번 바뀌며 네 계층을 지난다",
|
||||
"question": "Guest 프로세스가 읽은 주소는 어느 계층을 차례로 지나 어떤 물리 메모리에 닿는가",
|
||||
"type": "data-flow",
|
||||
"direction": "TB",
|
||||
"audience": [
|
||||
"가상 머신에 준 RAM 이 Host RAM 에서 무엇을 뜻하는지 가르려는 사람",
|
||||
"KVM/QEMU 메모리 경로를 처음 읽는 사람"
|
||||
],
|
||||
"summary": "Guest 구역 안에서 GVA 가 Guest TLB 와 Guest Page Table 을 지나 GPA 가 되고, VM Boundary 를 넘은 뒤 EPT 가 GPA 를 HPA 로 바꾸며, 그 HPA 가 Host RAM 의 physical page 와 그 page 가 속한 NUMA node 로 이어진다.",
|
||||
"alt": "Guest 구역 안에서 Keycloak · PostgreSQL 의 GVA 가 Guest TLB 와 Guest Page Table 을 지나 GPA 가 되고, VM Boundary 를 넘어 EPT 에서 HPA 가 된 뒤 Host RAM 구역의 Host Physical Memory 를 지나 NUMA Node 0 과 NUMA Node 1 로 갈라지는 주소 변환 경로도.",
|
||||
"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",
|
||||
"anchor": {
|
||||
"kind": "heading",
|
||||
"value": "87. 최종 기준 그림",
|
||||
"line": 4029
|
||||
}
|
||||
},
|
||||
"composition": {
|
||||
"profile": "component-flow",
|
||||
"diagram_only": true,
|
||||
"reference_ids": [
|
||||
"payment-event-flow"
|
||||
],
|
||||
"rationale": "이 절이 세우는 것은 한 번의 메모리 접근이 한 방향으로 지나가는 경로와 그 경로가 네 계층으로 나뉜다는 것이다. sequence 는 참여자마다 생명선을 세우고 메시지에 시각 순서를 매기는데 여기서는 참여자끼리 주고받는 메시지가 아니라 주소 하나가 계층을 지나며 이름이 바뀌는 변환 사슬이라 순서를 매길 메시지가 없다. two-zone-pipeline 은 구역이 둘일 때의 문법인데 이 경로는 Guest Userspace · Guest Kernel · KVM/CPU · Host RAM 넷으로 갈리고 Guest Kernel 안쪽에도 TLB Miss 엣지가 있어 구역 안 엣지 라벨이 캔버스 위쪽에 고정된다.",
|
||||
"focus_node": "ept"
|
||||
},
|
||||
"groups": [
|
||||
{
|
||||
"id": "guest",
|
||||
"label": "Guest",
|
||||
"kind": "boundary",
|
||||
"description": "VM Boundary 안쪽. Guest Userspace 와 Guest Kernel 이 여기 있고 GVA 가 GPA 가 되는 첫 번째 변환이 여기서 끝난다.",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 4034,
|
||||
"end_line": 4053
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "host-ram",
|
||||
"label": "Host RAM",
|
||||
"kind": "boundary",
|
||||
"description": "변환이 끝난 주소가 닿는 실제 서버 메모리.",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 4069,
|
||||
"end_line": 4079
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
}
|
||||
],
|
||||
"nodes": [
|
||||
{
|
||||
"id": "guest-process",
|
||||
"label": "Keycloak · PostgreSQL",
|
||||
"kind": "actor",
|
||||
"role": "source",
|
||||
"shape": "box",
|
||||
"group": "guest",
|
||||
"description": "Guest 안에서 메모리를 읽는 프로세스. 여기서 GVA 가 출발한다.",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 4034,
|
||||
"end_line": 4038
|
||||
}
|
||||
],
|
||||
"assumption": false,
|
||||
"details": [
|
||||
"Guest Userspace"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "guest-tlb",
|
||||
"label": "Guest TLB",
|
||||
"kind": "component",
|
||||
"role": "store",
|
||||
"shape": "box",
|
||||
"group": "guest",
|
||||
"description": "최근 변환 결과를 담아 두는 CPU 안의 캐시.",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 4043,
|
||||
"end_line": 4045
|
||||
}
|
||||
],
|
||||
"assumption": false,
|
||||
"details": [
|
||||
"Guest Kernel"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "guest-page-table",
|
||||
"label": "Guest Page Table",
|
||||
"kind": "component",
|
||||
"role": "service",
|
||||
"shape": "box",
|
||||
"group": "guest",
|
||||
"description": "GVA 를 GPA 로 바꾸는 첫 번째 변환 표.",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 4048,
|
||||
"end_line": 4053
|
||||
}
|
||||
],
|
||||
"assumption": false,
|
||||
"details": [
|
||||
"Guest Kernel",
|
||||
"GVA → GPA"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "ept",
|
||||
"label": "EPT",
|
||||
"kind": "component",
|
||||
"role": "service",
|
||||
"shape": "box",
|
||||
"emphasis": "primary",
|
||||
"description": "VM Boundary 바깥에서 GPA 를 HPA 로 바꾸는 두 번째 변환 표. KVM 과 CPU 가 맡는다.",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 4055,
|
||||
"end_line": 4067
|
||||
}
|
||||
],
|
||||
"assumption": false,
|
||||
"details": [
|
||||
"KVM / CPU",
|
||||
"GPA → HPA"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "host-physical-memory",
|
||||
"label": "Host Physical Memory",
|
||||
"kind": "store",
|
||||
"role": "store",
|
||||
"shape": "box",
|
||||
"group": "host-ram",
|
||||
"description": "HPA 가 가리키는 실제 서버 메모리.",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 4069,
|
||||
"end_line": 4072
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "numa-node-0",
|
||||
"label": "NUMA Node 0",
|
||||
"kind": "store",
|
||||
"role": "sink",
|
||||
"shape": "box",
|
||||
"group": "host-ram",
|
||||
"description": "physical page 가 놓일 수 있는 한쪽 NUMA node.",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 4073,
|
||||
"end_line": 4075
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "numa-node-1",
|
||||
"label": "NUMA Node 1",
|
||||
"kind": "store",
|
||||
"role": "sink",
|
||||
"shape": "box",
|
||||
"group": "host-ram",
|
||||
"description": "physical page 가 놓일 수 있는 다른 쪽 NUMA node.",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 4073,
|
||||
"end_line": 4075
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
}
|
||||
],
|
||||
"edges": [
|
||||
{
|
||||
"id": "process-to-tlb",
|
||||
"from": "guest-process",
|
||||
"to": "guest-tlb",
|
||||
"label": "GVA",
|
||||
"kind": "data",
|
||||
"style": "solid",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 4036,
|
||||
"end_line": 4043
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "tlb-to-page-table",
|
||||
"from": "guest-tlb",
|
||||
"to": "guest-page-table",
|
||||
"label": "TLB Miss",
|
||||
"kind": "control",
|
||||
"style": "solid",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 4043,
|
||||
"end_line": 4048
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "page-table-to-ept",
|
||||
"from": "guest-page-table",
|
||||
"to": "ept",
|
||||
"label": "VM Boundary",
|
||||
"kind": "control",
|
||||
"style": "solid",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 4053,
|
||||
"end_line": 4062
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "ept-to-host-memory",
|
||||
"from": "ept",
|
||||
"to": "host-physical-memory",
|
||||
"label": "HPA",
|
||||
"kind": "data",
|
||||
"style": "solid",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 4062,
|
||||
"end_line": 4071
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "host-memory-to-node-0",
|
||||
"from": "host-physical-memory",
|
||||
"to": "numa-node-0",
|
||||
"label": "Physical Page",
|
||||
"kind": "data",
|
||||
"style": "solid",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 4071,
|
||||
"end_line": 4075
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "host-memory-to-node-1",
|
||||
"from": "host-physical-memory",
|
||||
"to": "numa-node-1",
|
||||
"label": "Physical Page",
|
||||
"kind": "data",
|
||||
"style": "solid",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 4071,
|
||||
"end_line": 4075
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
}
|
||||
],
|
||||
"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 둘을 끝으로 두었다."
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,250 @@
|
||||
{
|
||||
"version": "1.1",
|
||||
"id": "numa-vcpu-and-memory-placement",
|
||||
"title": "vCPU 를 Node 0 에 묶어도 backing 이 Node 1 에 있으면 접근은 remote 로 남는다",
|
||||
"question": "vCPU pinning 만 한 배치와 memory binding 까지 맞춘 배치에서 같은 memory 접근은 각각 어느 node 의 RAM 에 닿는가",
|
||||
"type": "sequence",
|
||||
"direction": "LR",
|
||||
"audience": [
|
||||
"가상 머신의 지연을 NUMA 배치로 의심하는 사람",
|
||||
"vCPU pinning 을 걸어 두고 효과가 없다고 느끼는 사람"
|
||||
],
|
||||
"summary": "vCPU thread 를 Node 0 CPU 에 pinning 해도 VM1 의 memory backing 이 Node 1 에 놓여 있으면 접근은 remote 로 남고, memory placement 와 binding 까지 Node 0 으로 맞춘 뒤에야 같은 접근이 local 이 된다.",
|
||||
"alt": "QEMU vCPU Thread, VM1 Memory Backing, Node 0 CPU, Node 0 RAM, Node 1 RAM 다섯 참여자 사이의 순서도. 앞 세 메시지는 pinning 만 한 배치에서 접근이 Node 1 RAM 으로 가는 것을, 뒤 두 메시지는 memory 를 Node 0 에 묶은 배치에서 같은 접근이 Node 0 RAM 으로 가는 것을 보인다.",
|
||||
"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",
|
||||
"anchor": {
|
||||
"kind": "heading",
|
||||
"value": "75. vCPU와 NUMA의 연결",
|
||||
"line": 3313
|
||||
}
|
||||
},
|
||||
"composition": {
|
||||
"profile": "sequence",
|
||||
"diagram_only": true,
|
||||
"reference_ids": [
|
||||
"payment-approval-sequence"
|
||||
],
|
||||
"rationale": "이 절과 다음 절이 세운 것은 배치 하나가 아니라 「묶은 것 → 남은 문제 → 마저 묶은 것 → 달라진 결과」라는 차례다. orchestrator-workers 는 scheduler 를 워커들 위에 두는 그림인데 memory 가 어느 node 에 있는지는 scheduler 가 정하지 않는다는 것이 이 절의 논지라 그 문법을 쓰면 논지가 뒤집힌다. query-fanout 은 CPU 하나가 RAM 둘로 갈라지는 모양은 맞지만 pinning 만 한 배치와 memory 까지 맞춘 배치가 같은 그림에서 구별되지 않는다. sequence 는 메시지에 번호를 붙이므로 두 배치가 어느 쪽이 먼저이고 무엇이 달라졌는지 그대로 드러난다.",
|
||||
"focus_node": "node0-cpu"
|
||||
},
|
||||
"groups": [],
|
||||
"nodes": [
|
||||
{
|
||||
"id": "qemu-vcpu-thread",
|
||||
"label": "QEMU vCPU Thread",
|
||||
"kind": "process",
|
||||
"role": "participant",
|
||||
"shape": "box",
|
||||
"emphasis": "primary",
|
||||
"details": [
|
||||
"Guest vCPU"
|
||||
],
|
||||
"description": "Guest vCPU 가 Host 에서 갖는 실체. Host scheduler 가 이것을 logical CPU 에 올린다.",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 3315,
|
||||
"end_line": 3324
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "vm-memory-backing",
|
||||
"label": "VM1 Memory Backing",
|
||||
"kind": "memory",
|
||||
"role": "participant",
|
||||
"shape": "box",
|
||||
"details": [
|
||||
"VM1 RAM"
|
||||
],
|
||||
"description": "그 가상 머신의 Host physical backing page 가 놓이는 자리.",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 3327,
|
||||
"end_line": 3327
|
||||
},
|
||||
{
|
||||
"start_line": 3352,
|
||||
"end_line": 3354
|
||||
},
|
||||
{
|
||||
"start_line": 3381,
|
||||
"end_line": 3382
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "node0-cpu",
|
||||
"label": "Node 0 CPU",
|
||||
"kind": "compute",
|
||||
"role": "participant",
|
||||
"shape": "box",
|
||||
"emphasis": "primary",
|
||||
"details": [
|
||||
"Host Logical CPU"
|
||||
],
|
||||
"description": "vCPU thread 가 실행되는 logical CPU. NUMA Node 0 에 속한다.",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 3322,
|
||||
"end_line": 3330
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "node0-ram",
|
||||
"label": "Node 0 RAM",
|
||||
"kind": "memory",
|
||||
"role": "participant",
|
||||
"shape": "box",
|
||||
"details": [
|
||||
"Local Memory"
|
||||
],
|
||||
"description": "Node 0 CPU 와 같은 node 의 RAM.",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 3288,
|
||||
"end_line": 3296
|
||||
},
|
||||
{
|
||||
"start_line": 3373,
|
||||
"end_line": 3382
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "node1-ram",
|
||||
"label": "Node 1 RAM",
|
||||
"kind": "memory",
|
||||
"role": "participant",
|
||||
"shape": "box",
|
||||
"details": [
|
||||
"Remote Memory"
|
||||
],
|
||||
"description": "다른 node 의 RAM. Node 0 CPU 가 읽으면 interconnect 를 건넌다.",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 3299,
|
||||
"end_line": 3306
|
||||
},
|
||||
{
|
||||
"start_line": 3334,
|
||||
"end_line": 3334
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
}
|
||||
],
|
||||
"edges": [
|
||||
{
|
||||
"id": "vcpu-pinning",
|
||||
"from": "qemu-vcpu-thread",
|
||||
"to": "node0-cpu",
|
||||
"label": "vCPU Pinning",
|
||||
"kind": "control",
|
||||
"order": 1,
|
||||
"style": "solid",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 3322,
|
||||
"end_line": 3324
|
||||
},
|
||||
{
|
||||
"start_line": 3347,
|
||||
"end_line": 3350
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "backing-on-node1",
|
||||
"from": "vm-memory-backing",
|
||||
"to": "node1-ram",
|
||||
"label": "Memory Placement",
|
||||
"kind": "control",
|
||||
"order": 2,
|
||||
"style": "solid",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 3327,
|
||||
"end_line": 3327
|
||||
},
|
||||
{
|
||||
"start_line": 3352,
|
||||
"end_line": 3354
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "remote-access",
|
||||
"from": "node0-cpu",
|
||||
"to": "node1-ram",
|
||||
"label": "Remote Access",
|
||||
"kind": "read",
|
||||
"order": 3,
|
||||
"style": "dashed",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 3329,
|
||||
"end_line": 3339
|
||||
},
|
||||
{
|
||||
"start_line": 3356,
|
||||
"end_line": 3357
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "memory-binding",
|
||||
"from": "vm-memory-backing",
|
||||
"to": "node0-ram",
|
||||
"label": "Memory Binding",
|
||||
"kind": "control",
|
||||
"order": 4,
|
||||
"style": "solid",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 3361,
|
||||
"end_line": 3366
|
||||
},
|
||||
{
|
||||
"start_line": 3381,
|
||||
"end_line": 3382
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "local-access",
|
||||
"from": "node0-cpu",
|
||||
"to": "node0-ram",
|
||||
"label": "Local Access",
|
||||
"kind": "read",
|
||||
"order": 5,
|
||||
"style": "solid",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 3288,
|
||||
"end_line": 3296
|
||||
},
|
||||
{
|
||||
"start_line": 3373,
|
||||
"end_line": 3382
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
}
|
||||
],
|
||||
"legend": [],
|
||||
"metadata": {
|
||||
"rationale": "sequence 로 고른 이유는 §76 이 배치 하나를 서술한 절이 아니라 「pinning 만 하면 이렇게 되고 memory 까지 맞추면 저렇게 된다」는 차례로 쓰인 절이기 때문이다. 4·5 번은 3 번 뒤에 일어나는 사건이 아니라 고친 구성이고 그 사실은 long_description 과 옆 문단이 말한다. remote access 에 emphasis: warning 을 줬더니 렌더러가 화살표 끝에 빨간 X 를 그려 실패로 읽혔다. remote 는 실패가 아니라 비용이 더 드는 접근이라 강조를 빼고 점선만 남겼다. interconnect 는 그림 안 이름으로 넣지 않고 <desc> 와 노드 설명에 두었다 — 이름이 아니라 두 node 사이의 구간이라 상자도 화살표도 아니다. 접근 비용의 크기와 이 Host 의 node 수는 이 SSOT 가 재지 않았으므로 그리지 않았다."
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,544 @@
|
||||
{
|
||||
"version": "1.1",
|
||||
"id": "packet-control-and-data-paths",
|
||||
"title": "control/setup path 는 QEMU 를 지나고 data path 는 backend 에 따라 QEMU 를 지나기도 안 지나기도 한다",
|
||||
"question": "설정하는 경로와 packet 이 흐르는 경로는 어디에서 갈라지고, 그 두 갈래 가운데 어느 쪽이 QEMU 사용자 공간을 지나는가",
|
||||
"type": "network",
|
||||
"direction": "TB",
|
||||
"audience": [
|
||||
"가상 머신 안의 요청이 느릴 때 계층을 가르려는 사람",
|
||||
"vhost-net 이 무엇을 우회하는지 확인하려는 사람"
|
||||
],
|
||||
"summary": "virsh 에서 시작한 control/setup path 는 libvirt 와 QEMU 를 지나 virtio-net Device Model 에서 backend 를 설정하고, packet 이 흐르는 data path 는 TAP 에서 그 backend 둘 가운데 하나로 갈라져 vhost-net 쪽은 QEMU 사용자 공간을 지나지 않고 virtqueue 로 모인다.",
|
||||
"alt": "위쪽 줄은 virsh, libvirt, QEMU, virtio-net Device Model 로 이어지는 control/setup path 이고 아래쪽 줄은 Physical NIC 에서 TAP 까지 이어지는 data path 다. 두 줄이 vhost-net 과 QEMU virtio backend 에서 만나 virtqueue 를 지나 Guest 로 모이는 구성도.",
|
||||
"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",
|
||||
"anchor": {
|
||||
"kind": "heading",
|
||||
"value": "125. 최종 기준 구조",
|
||||
"line": 5682
|
||||
}
|
||||
},
|
||||
"composition": {
|
||||
"profile": "component-flow",
|
||||
"diagram_only": true,
|
||||
"reference_ids": [
|
||||
"payment-event-flow"
|
||||
],
|
||||
"rationale": "이 절이 세운 것은 방향이 있는 경로 셋이고 그 셋이 같은 device model 과 같은 virtqueue 를 공유한다. SSOT 는 셋을 각각 따로 선 직선으로 적어 두어서 QEMU 가 control 에는 남고 data 에서는 빠질 수 있다는 사실이 한 장에 보이지 않는다. comparison 은 관계선을 지워도 뜻이 남는 자리에 쓰는 문법이라 여기서는 표가 되고, two-zone-pipeline 은 구역을 둘로 못 나눈다 — 여기 갈래는 경계가 아니라 backend 선택이 만든다. component-flow 는 갈라졌다 모이는 경로를 그대로 옮긴다.",
|
||||
"focus_node": "virtio-net-device-model"
|
||||
},
|
||||
"groups": [],
|
||||
"nodes": [
|
||||
{
|
||||
"id": "virsh",
|
||||
"label": "virsh",
|
||||
"kind": "cli",
|
||||
"role": "actor",
|
||||
"shape": "box",
|
||||
"details": [
|
||||
"User"
|
||||
],
|
||||
"description": "VM/network 관리 CLI. packet datapath 에는 직접 참여하지 않는다.",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 5662,
|
||||
"end_line": 5662
|
||||
},
|
||||
{
|
||||
"start_line": 5687,
|
||||
"end_line": 5689
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "libvirt",
|
||||
"label": "libvirt",
|
||||
"kind": "service",
|
||||
"role": "service",
|
||||
"shape": "box",
|
||||
"description": "VM lifecycle 과 NIC/network configuration 을 관리한다.",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 5663,
|
||||
"end_line": 5663
|
||||
},
|
||||
{
|
||||
"start_line": 5691,
|
||||
"end_line": 5691
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "qemu",
|
||||
"label": "QEMU",
|
||||
"kind": "process",
|
||||
"role": "service",
|
||||
"shape": "box",
|
||||
"details": [
|
||||
"Host Userspace"
|
||||
],
|
||||
"description": "실제로 실행되는 프로세스. 이 안에 virtio-net device model 이 있다.",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 5666,
|
||||
"end_line": 5666
|
||||
},
|
||||
{
|
||||
"start_line": 5693,
|
||||
"end_line": 5693
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "virtio-net-device-model",
|
||||
"label": "virtio-net Device Model",
|
||||
"kind": "service",
|
||||
"role": "service",
|
||||
"shape": "box",
|
||||
"emphasis": "primary",
|
||||
"details": [
|
||||
"virtual NIC 생성",
|
||||
"feature negotiation",
|
||||
"virtqueue 설정",
|
||||
"vhost-net backend 설정"
|
||||
],
|
||||
"description": "control/setup path 가 닿는 자리. 장치를 만들고 backend 를 연결한다.",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 5666,
|
||||
"end_line": 5666
|
||||
},
|
||||
{
|
||||
"start_line": 5695,
|
||||
"end_line": 5700
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "physical-nic",
|
||||
"label": "Physical NIC",
|
||||
"kind": "hardware",
|
||||
"role": "source",
|
||||
"shape": "box",
|
||||
"description": "밖에서 온 packet 이 처음 닿는 하드웨어.",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 5706,
|
||||
"end_line": 5708
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "physical-nic-driver",
|
||||
"label": "Physical NIC Driver",
|
||||
"kind": "driver",
|
||||
"role": "service",
|
||||
"shape": "box",
|
||||
"description": "그 하드웨어를 제어하는 Host kernel driver.",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 5674,
|
||||
"end_line": 5674
|
||||
},
|
||||
{
|
||||
"start_line": 5710,
|
||||
"end_line": 5710
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "linux-bridge",
|
||||
"label": "Linux Bridge /\nRouting / NAT",
|
||||
"kind": "network",
|
||||
"role": "service",
|
||||
"shape": "box",
|
||||
"description": "L2 software switch 와 L3 routing, 주소 변환이 놓이는 자리.",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 5672,
|
||||
"end_line": 5673
|
||||
},
|
||||
{
|
||||
"start_line": 5712,
|
||||
"end_line": 5712
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "tap",
|
||||
"label": "TAP",
|
||||
"kind": "network",
|
||||
"role": "service",
|
||||
"shape": "box",
|
||||
"details": [
|
||||
"Host-side interface"
|
||||
],
|
||||
"description": "VM 의 Ethernet frame 과 Host Linux networking 을 잇는 접점.",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 5671,
|
||||
"end_line": 5671
|
||||
},
|
||||
{
|
||||
"start_line": 5714,
|
||||
"end_line": 5714
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "vhost-net",
|
||||
"label": "vhost-net",
|
||||
"kind": "kernel",
|
||||
"role": "service",
|
||||
"shape": "box",
|
||||
"details": [
|
||||
"Host Kernel"
|
||||
],
|
||||
"description": "반복되는 packet datapath 를 Host kernel 에서 처리하는 backend.",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 5669,
|
||||
"end_line": 5669
|
||||
},
|
||||
{
|
||||
"start_line": 5716,
|
||||
"end_line": 5716
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "qemu-virtio-backend",
|
||||
"label": "QEMU virtio backend",
|
||||
"kind": "process",
|
||||
"role": "service",
|
||||
"shape": "box",
|
||||
"details": [
|
||||
"QEMU Userspace"
|
||||
],
|
||||
"description": "vhost-net 을 쓰지 않을 때 datapath backend 를 맡는 쪽.",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 5668,
|
||||
"end_line": 5668
|
||||
},
|
||||
{
|
||||
"start_line": 5742,
|
||||
"end_line": 5742
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "virtqueue",
|
||||
"label": "virtqueue",
|
||||
"kind": "queue",
|
||||
"role": "queue",
|
||||
"shape": "box",
|
||||
"details": [
|
||||
"shared queue"
|
||||
],
|
||||
"description": "Guest 와 Host backend 가 I/O buffer 를 주고받는 descriptor 기반 공유 큐.",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 5667,
|
||||
"end_line": 5667
|
||||
},
|
||||
{
|
||||
"start_line": 5718,
|
||||
"end_line": 5718
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "guest",
|
||||
"label": "Guest",
|
||||
"kind": "service",
|
||||
"role": "sink",
|
||||
"shape": "box",
|
||||
"details": [
|
||||
"virtio-net Frontend Driver",
|
||||
"Guest TCP/IP Stack",
|
||||
"Socket",
|
||||
"Keycloak"
|
||||
],
|
||||
"description": "packet 이 올라가 애플리케이션 socket 에 닿는 쪽.",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 5664,
|
||||
"end_line": 5665
|
||||
},
|
||||
{
|
||||
"start_line": 5720,
|
||||
"end_line": 5726
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
}
|
||||
],
|
||||
"edges": [
|
||||
{
|
||||
"id": "virsh-to-libvirt",
|
||||
"from": "virsh",
|
||||
"to": "libvirt",
|
||||
"label": "Control / Setup",
|
||||
"kind": "control",
|
||||
"style": "solid",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 5662,
|
||||
"end_line": 5663
|
||||
},
|
||||
{
|
||||
"start_line": 5684,
|
||||
"end_line": 5691
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "libvirt-to-qemu",
|
||||
"from": "libvirt",
|
||||
"to": "qemu",
|
||||
"label": "NIC/network configuration",
|
||||
"kind": "control",
|
||||
"style": "solid",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 5663,
|
||||
"end_line": 5663
|
||||
},
|
||||
{
|
||||
"start_line": 5691,
|
||||
"end_line": 5693
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "qemu-to-device-model",
|
||||
"from": "qemu",
|
||||
"to": "virtio-net-device-model",
|
||||
"label": "QEMU process",
|
||||
"kind": "control",
|
||||
"style": "solid",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 5666,
|
||||
"end_line": 5666
|
||||
},
|
||||
{
|
||||
"start_line": 5693,
|
||||
"end_line": 5695
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "nic-to-driver",
|
||||
"from": "physical-nic",
|
||||
"to": "physical-nic-driver",
|
||||
"label": "Ethernet Frame",
|
||||
"kind": "data",
|
||||
"style": "solid",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 5671,
|
||||
"end_line": 5671
|
||||
},
|
||||
{
|
||||
"start_line": 5708,
|
||||
"end_line": 5710
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "driver-to-bridge",
|
||||
"from": "physical-nic-driver",
|
||||
"to": "linux-bridge",
|
||||
"label": "L2 forwarding",
|
||||
"kind": "data",
|
||||
"style": "solid",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 5672,
|
||||
"end_line": 5672
|
||||
},
|
||||
{
|
||||
"start_line": 5710,
|
||||
"end_line": 5712
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "bridge-to-tap",
|
||||
"from": "linux-bridge",
|
||||
"to": "tap",
|
||||
"label": "Ethernet Frame",
|
||||
"kind": "data",
|
||||
"style": "solid",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 5671,
|
||||
"end_line": 5671
|
||||
},
|
||||
{
|
||||
"start_line": 5712,
|
||||
"end_line": 5714
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "setup-vhost-backend",
|
||||
"from": "virtio-net-device-model",
|
||||
"to": "vhost-net",
|
||||
"label": "vhost-net backend 설정",
|
||||
"kind": "control",
|
||||
"style": "dashed",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 5700,
|
||||
"end_line": 5700
|
||||
},
|
||||
{
|
||||
"start_line": 5716,
|
||||
"end_line": 5716
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "setup-qemu-backend",
|
||||
"from": "virtio-net-device-model",
|
||||
"to": "qemu-virtio-backend",
|
||||
"label": "",
|
||||
"kind": "control",
|
||||
"style": "dashed",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 5668,
|
||||
"end_line": 5668
|
||||
},
|
||||
{
|
||||
"start_line": 5700,
|
||||
"end_line": 5700
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "tap-to-vhost",
|
||||
"from": "tap",
|
||||
"to": "vhost-net",
|
||||
"label": "vhost-net 사용",
|
||||
"kind": "data",
|
||||
"style": "solid",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 5703,
|
||||
"end_line": 5703
|
||||
},
|
||||
{
|
||||
"start_line": 5714,
|
||||
"end_line": 5716
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "tap-to-qemu-backend",
|
||||
"from": "tap",
|
||||
"to": "qemu-virtio-backend",
|
||||
"label": "",
|
||||
"kind": "data",
|
||||
"style": "solid",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 5729,
|
||||
"end_line": 5729
|
||||
},
|
||||
{
|
||||
"start_line": 5740,
|
||||
"end_line": 5742
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "vhost-to-virtqueue",
|
||||
"from": "vhost-net",
|
||||
"to": "virtqueue",
|
||||
"label": "Data Path",
|
||||
"kind": "data",
|
||||
"style": "solid",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 5703,
|
||||
"end_line": 5703
|
||||
},
|
||||
{
|
||||
"start_line": 5716,
|
||||
"end_line": 5718
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "qemu-backend-to-virtqueue",
|
||||
"from": "qemu-virtio-backend",
|
||||
"to": "virtqueue",
|
||||
"label": "Data Path",
|
||||
"kind": "data",
|
||||
"style": "solid",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 5729,
|
||||
"end_line": 5729
|
||||
},
|
||||
{
|
||||
"start_line": 5742,
|
||||
"end_line": 5744
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "virtqueue-to-guest",
|
||||
"from": "virtqueue",
|
||||
"to": "guest",
|
||||
"label": "virtio-net Frontend Driver",
|
||||
"kind": "data",
|
||||
"style": "solid",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 5664,
|
||||
"end_line": 5664
|
||||
},
|
||||
{
|
||||
"start_line": 5718,
|
||||
"end_line": 5726
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
}
|
||||
],
|
||||
"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)과 도착 상자 이름으로 읽게 두었다."
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,230 @@
|
||||
{
|
||||
"version": "1.1",
|
||||
"id": "page-fault-layers-in-a-vm",
|
||||
"title": "세 fault 는 변환 사슬의 서로 다른 단계에서 갈라져 나가 서로 다른 계층이 받는다",
|
||||
"question": "Guest Page Fault · EPT Violation · Host Page Fault 는 각각 어느 단계에서 갈라지고 누가 받는가",
|
||||
"type": "flow",
|
||||
"direction": "TB",
|
||||
"audience": [
|
||||
"VM 안의 메모리 지연을 계층별로 가르려는 사람",
|
||||
"fault 지표를 어느 쪽부터 볼지 정하려는 사람"
|
||||
],
|
||||
"summary": "Guest Page Table 에서 갈라진 Guest Page Fault 는 Guest Kernel 이 받고, 그다음 단계인 EPT 에서 갈라진 EPT Violation 은 VM Exit 뒤 KVM 이 받으며, QEMU 의 Guest RAM Backing 에서 갈라진 Host Page Fault 는 Host Kernel 이 받는다.",
|
||||
"alt": "Guest Page Table 과 EPT, QEMU 의 Guest RAM Backing 에서 각각 fault 가 갈라져 Guest Kernel · KVM · Host Kernel 로 가는 갈래도.",
|
||||
"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",
|
||||
"anchor": {
|
||||
"kind": "heading",
|
||||
"value": "48. Guest Page Fault와 EPT Violation 비교",
|
||||
"line": 2480
|
||||
}
|
||||
},
|
||||
"composition": {
|
||||
"profile": "component-flow",
|
||||
"diagram_only": true,
|
||||
"reference_ids": [
|
||||
"payment-event-flow"
|
||||
],
|
||||
"rationale": "이 절이 세우는 것은 같은 변환 사슬 위에서 사건이 어느 단계에 갈라져 어느 계층으로 빠지는가다. 갈래와 그 갈래가 닿는 자리가 논지라 방향 있는 경로 문법이 맞는다. sequence 는 참여자끼리 주고받는 메시지에 시각 순서를 매기는 문법인데 여기서 갈리는 셋은 서로 다른 접근에서 따로 일어나는 사건이라 하나의 시각 순서로 세울 수 없다. two-zone-pipeline 은 구역 안쪽 엣지 라벨을 캔버스 위쪽에 고정하는데 이 그림은 Guest 구역 안과 Host 구역 안에 각각 갈래가 있어 그 라벨이 구역 점선에 얹힌다.",
|
||||
"focus_node": "ept"
|
||||
},
|
||||
"groups": [],
|
||||
"nodes": [
|
||||
{
|
||||
"id": "guest-page-table",
|
||||
"label": "Guest Page Table",
|
||||
"kind": "component",
|
||||
"role": "source",
|
||||
"shape": "box",
|
||||
"details": [
|
||||
"GVA → GPA"
|
||||
],
|
||||
"description": "첫 번째 translation 단계. 여기서 접근을 완료할 수 없으면 Guest Page Fault 가 난다.",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 2443,
|
||||
"end_line": 2445
|
||||
},
|
||||
{
|
||||
"start_line": 2484,
|
||||
"end_line": 2485
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "guest-kernel",
|
||||
"label": "Guest Kernel",
|
||||
"kind": "service",
|
||||
"role": "sink",
|
||||
"shape": "box",
|
||||
"description": "Guest Page Fault 를 받는 계층.",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 2487,
|
||||
"end_line": 2487
|
||||
},
|
||||
{
|
||||
"start_line": 2493,
|
||||
"end_line": 2494
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "ept",
|
||||
"label": "EPT",
|
||||
"kind": "component",
|
||||
"role": "service",
|
||||
"shape": "box",
|
||||
"emphasis": "primary",
|
||||
"details": [
|
||||
"GPA → HPA"
|
||||
],
|
||||
"description": "두 번째 translation 단계. Guest translation 이 성공한 뒤 여기서 막히면 EPT Violation 이 난다.",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 2465,
|
||||
"end_line": 2469
|
||||
},
|
||||
{
|
||||
"start_line": 2484,
|
||||
"end_line": 2485
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "kvm",
|
||||
"label": "KVM",
|
||||
"kind": "service",
|
||||
"role": "sink",
|
||||
"shape": "box",
|
||||
"description": "EPT Violation 을 VM Exit 뒤에 받는 계층.",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 2471,
|
||||
"end_line": 2473
|
||||
},
|
||||
{
|
||||
"start_line": 2487,
|
||||
"end_line": 2487
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "qemu-backing",
|
||||
"label": "QEMU · Guest RAM Backing",
|
||||
"kind": "store",
|
||||
"role": "source",
|
||||
"shape": "box",
|
||||
"details": [
|
||||
"Host Virtual Memory"
|
||||
],
|
||||
"description": "Host 의 일반 userspace 메모리로 관리되는 자리. demand allocation 이나 reclaim, swap 때문에 여기서 fault 가 난다.",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 2504,
|
||||
"end_line": 2504
|
||||
},
|
||||
{
|
||||
"start_line": 2514,
|
||||
"end_line": 2519
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "host-kernel",
|
||||
"label": "Host Kernel",
|
||||
"kind": "service",
|
||||
"role": "sink",
|
||||
"shape": "box",
|
||||
"description": "Host Page Fault 를 받아 필요한 Host page 를 처리하는 계층.",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 2521,
|
||||
"end_line": 2525
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
}
|
||||
],
|
||||
"edges": [
|
||||
{
|
||||
"id": "guest-fault-branch",
|
||||
"from": "guest-page-table",
|
||||
"to": "guest-kernel",
|
||||
"label": "Guest Page Fault",
|
||||
"kind": "event",
|
||||
"style": "solid",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 2484,
|
||||
"end_line": 2487
|
||||
},
|
||||
{
|
||||
"start_line": 2493,
|
||||
"end_line": 2494
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "translation-continues",
|
||||
"from": "guest-page-table",
|
||||
"to": "ept",
|
||||
"label": "GPA",
|
||||
"kind": "data",
|
||||
"style": "solid",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 2461,
|
||||
"end_line": 2467
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "ept-violation-branch",
|
||||
"from": "ept",
|
||||
"to": "kvm",
|
||||
"label": "EPT Violation · VM Exit",
|
||||
"kind": "event",
|
||||
"style": "solid",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 2467,
|
||||
"end_line": 2473
|
||||
},
|
||||
{
|
||||
"start_line": 2487,
|
||||
"end_line": 2487
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "host-fault-branch",
|
||||
"from": "qemu-backing",
|
||||
"to": "host-kernel",
|
||||
"label": "Host Page Fault",
|
||||
"kind": "event",
|
||||
"style": "solid",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 2517,
|
||||
"end_line": 2525
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
}
|
||||
],
|
||||
"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 을 빼고 이름에 맡겼다."
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,226 @@
|
||||
{
|
||||
"version": "1.1",
|
||||
"id": "virtio-balloon-inflate-deflate",
|
||||
"title": "balloon target 은 VM Boundary 를 건너 driver 에 닿고 확보한 page 는 같은 경계를 되건너 온다",
|
||||
"question": "Host 가 balloon target 을 바꾸면 그 요청은 어떤 순서로 어느 경계를 건너 Guest 안의 driver 까지 가고, 확보된 page 는 어떻게 Host 로 돌아오는가",
|
||||
"type": "sequence",
|
||||
"direction": "LR",
|
||||
"audience": [
|
||||
"Host RAM 압박에 ballooning 을 쓸지 판단하려는 사람",
|
||||
"Guest 안의 메모리 압박을 Host 조치와 이어 보려는 사람"
|
||||
],
|
||||
"summary": "balloon target 조정은 Host Memory Management 에서 QEMU 쪽 device 와 virtqueue 를 지나 VM Boundary 를 넘어 Guest kernel 의 driver 까지 가고, driver 가 확보한 Guest page 는 같은 경계를 반대 방향으로 되건너 Host 의 회수 가능 backing 이 된다.",
|
||||
"alt": "virtio-balloon Driver, virtqueue, QEMU virtio-balloon Device, Host Memory Management 네 참여자 사이의 순서도. 앞 세 메시지는 오른쪽에서 왼쪽으로 Guest 안으로 들어가고 뒤 세 메시지는 왼쪽에서 오른쪽으로 Host 로 돌아온다.",
|
||||
"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",
|
||||
"anchor": {
|
||||
"kind": "heading",
|
||||
"value": "65. virtio-balloon 구조",
|
||||
"line": 3008
|
||||
}
|
||||
},
|
||||
"composition": {
|
||||
"profile": "sequence",
|
||||
"diagram_only": true,
|
||||
"reference_ids": [
|
||||
"payment-approval-sequence"
|
||||
],
|
||||
"rationale": "이 절과 다음 절이 세운 것은 조각들의 나열이 아니라 balloon 요청이 지나는 차례와 그 차례가 VM Boundary 를 두 번 건넌다는 사실이다. two-zone-pipeline 으로 구역 둘을 그려 보니 같은 구역 안의 이음이 캔버스 맨 위로 튀어 올라가고 경계 라벨이 건너편 상자에 먹혔다. orchestrator-workers 는 QEMU 아래에 교체 가능한 워커들을 두는 그림이라 driver 가 Guest 안에 있다는 사실이 지워진다. sequence 는 참여자를 한 줄로 세우고 메시지에 번호를 붙이므로 경계를 두 번 건너는 왕복이 그대로 드러난다.",
|
||||
"focus_node": "balloon-driver"
|
||||
},
|
||||
"groups": [],
|
||||
"nodes": [
|
||||
{
|
||||
"id": "balloon-driver",
|
||||
"label": "virtio-balloon Driver",
|
||||
"kind": "driver",
|
||||
"role": "participant",
|
||||
"shape": "box",
|
||||
"emphasis": "primary",
|
||||
"details": [
|
||||
"Guest Kernel"
|
||||
],
|
||||
"description": "Guest kernel 쪽 balloon driver. target 을 받아 Guest page 를 확보한다.",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 3013,
|
||||
"end_line": 3015
|
||||
},
|
||||
{
|
||||
"start_line": 3046,
|
||||
"end_line": 3046
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "virtqueue",
|
||||
"label": "virtqueue",
|
||||
"kind": "queue",
|
||||
"role": "participant",
|
||||
"shape": "box",
|
||||
"description": "Guest 와 QEMU 가 balloon 요청을 주고받는 공유 큐. VM Boundary 바로 안쪽이다.",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 3017,
|
||||
"end_line": 3017
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "qemu-balloon-device",
|
||||
"label": "QEMU virtio-balloon\nDevice",
|
||||
"kind": "service",
|
||||
"role": "participant",
|
||||
"shape": "box",
|
||||
"description": "Host 쪽 balloon device. 경계를 넘어온 내용을 Host memory management 로 넘긴다.",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 3022,
|
||||
"end_line": 3022
|
||||
},
|
||||
{
|
||||
"start_line": 3037,
|
||||
"end_line": 3041
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "host-memory-management",
|
||||
"label": "Host Memory\nManagement",
|
||||
"kind": "service",
|
||||
"role": "participant",
|
||||
"shape": "box",
|
||||
"description": "balloon target 을 정하는 쪽이자 회수 기회가 생기는 자리.",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 3025,
|
||||
"end_line": 3025
|
||||
},
|
||||
{
|
||||
"start_line": 3034,
|
||||
"end_line": 3037
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
}
|
||||
],
|
||||
"edges": [
|
||||
{
|
||||
"id": "target-adjust",
|
||||
"from": "host-memory-management",
|
||||
"to": "qemu-balloon-device",
|
||||
"label": "Balloon target 조정",
|
||||
"kind": "control",
|
||||
"order": 1,
|
||||
"style": "solid",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 3034,
|
||||
"end_line": 3041
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "target-crosses-in",
|
||||
"from": "qemu-balloon-device",
|
||||
"to": "virtqueue",
|
||||
"label": "VM Boundary",
|
||||
"kind": "control",
|
||||
"order": 2,
|
||||
"style": "solid",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 3017,
|
||||
"end_line": 3022
|
||||
},
|
||||
{
|
||||
"start_line": 3041,
|
||||
"end_line": 3046
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "target-to-driver",
|
||||
"from": "virtqueue",
|
||||
"to": "balloon-driver",
|
||||
"label": "Balloon target",
|
||||
"kind": "control",
|
||||
"order": 3,
|
||||
"style": "solid",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 3013,
|
||||
"end_line": 3017
|
||||
},
|
||||
{
|
||||
"start_line": 3043,
|
||||
"end_line": 3046
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "pages-secured",
|
||||
"from": "balloon-driver",
|
||||
"to": "virtqueue",
|
||||
"label": "Guest pages 확보",
|
||||
"kind": "data",
|
||||
"order": 4,
|
||||
"style": "solid",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 3046,
|
||||
"end_line": 3050
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "pages-cross-out",
|
||||
"from": "virtqueue",
|
||||
"to": "qemu-balloon-device",
|
||||
"label": "VM Boundary",
|
||||
"kind": "data",
|
||||
"order": 5,
|
||||
"style": "solid",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 3017,
|
||||
"end_line": 3025
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "reclaimable-backing",
|
||||
"from": "qemu-balloon-device",
|
||||
"to": "host-memory-management",
|
||||
"label": "회수 가능 backing",
|
||||
"kind": "data",
|
||||
"order": 6,
|
||||
"style": "solid",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 3022,
|
||||
"end_line": 3028
|
||||
},
|
||||
{
|
||||
"start_line": 3077,
|
||||
"end_line": 3079
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
}
|
||||
],
|
||||
"legend": [],
|
||||
"metadata": {
|
||||
"rationale": "sequence 로 고른 이유는 §65 의 구조가 §66 의 차례와 붙어야 뜻이 나기 때문이다. 구조만 그리면 화살표가 한 방향뿐이고, 차례만 적으면 경계가 어디인지 안 보인다. deflate 쪽 절(§68)은 prepare 가 잡아 온 문맥(L2986-L3083) 밖이라 그 방향은 그리지 않았고, target 을 올릴 때와 내릴 때 Guest usable memory 가 어느 쪽으로 가는지는 기록 본문의 표가 이미 맡고 있어 그림에 넣지 않았다. 노드 라벨의 줄바꿈은 렌더러가 22자까지만 접기 때문에 넣었다 — 접지 않으면 글자가 상자 밖으로 나간다."
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,240 @@
|
||||
{
|
||||
"version": "1.1",
|
||||
"id": "vm-exit-handling-cycle",
|
||||
"title": "VM Exit 은 KVM 에서 끝나기도 하고 QEMU userspace 까지 가기도 한다",
|
||||
"question": "VM Exit 이 난 뒤 제어권은 어디까지 올라갔다가 어떻게 Guest 로 돌아오는가",
|
||||
"type": "flow",
|
||||
"direction": "LR",
|
||||
"audience": [
|
||||
"VM 안의 지연을 계층별로 가르려는 사람",
|
||||
"KVM 실행 경로를 처음 읽는 사람"
|
||||
],
|
||||
"summary": "VM Exit 은 KVM 에서 Exit Reason 을 보고 두 갈래로 나뉘며, 커널 안에서 끝난 갈래와 QEMU userspace 를 거친 갈래가 어느 쪽이든 VM Entry 를 지나 Guest 실행으로 돌아온다.",
|
||||
"alt": "Guest 에서 VM Exit 으로 KVM 에 닿은 뒤 커널 처리와 QEMU 처리 두 갈래로 갈라졌다가 VM Entry 로 다시 Guest 실행 재개에 모이는 흐름도.",
|
||||
"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",
|
||||
"anchor": {
|
||||
"kind": "heading",
|
||||
"value": "9. VM Exit 이후 처리",
|
||||
"line": 493
|
||||
}
|
||||
},
|
||||
"composition": {
|
||||
"profile": "component-flow",
|
||||
"diagram_only": true,
|
||||
"reference_ids": [
|
||||
"payment-event-flow"
|
||||
],
|
||||
"rationale": "이 절이 주장하는 것은 제어권이 한 방향으로 지나가는 경로와 그 경로가 Exit Reason 에서 갈렸다가 다시 만난다는 것이다. sequence 는 참여자마다 생명선을 세우고 메시지를 시각 순서로 세우는데 이 절에는 갈래가 있어 한 줄로 세울 수 없고, orchestrator-workers 는 KVM 아래에 교체 가능한 워커들을 두는 그림이라 커널 갈래와 userspace 갈래가 서로 다른 경계에 있다는 사실이 지워진다.",
|
||||
"focus_node": "kvm"
|
||||
},
|
||||
"groups": [],
|
||||
"nodes": [
|
||||
{
|
||||
"id": "guest",
|
||||
"label": "Guest",
|
||||
"kind": "service",
|
||||
"role": "source",
|
||||
"shape": "box",
|
||||
"description": "VM Exit 이 나기 직전까지 실행 중이던 Guest.",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 498,
|
||||
"end_line": 501
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "kvm",
|
||||
"label": "KVM",
|
||||
"kind": "service",
|
||||
"role": "service",
|
||||
"shape": "box",
|
||||
"emphasis": "primary",
|
||||
"details": [
|
||||
"Exit Reason"
|
||||
],
|
||||
"description": "VM Exit 을 받아 Exit Reason 을 확인하고 갈래를 고르는 자리.",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 495,
|
||||
"end_line": 495
|
||||
},
|
||||
{
|
||||
"start_line": 502,
|
||||
"end_line": 504
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "kvm-kernel-handling",
|
||||
"label": "KVM 커널 처리",
|
||||
"kind": "service",
|
||||
"role": "service",
|
||||
"shape": "box",
|
||||
"description": "커널 안에서 끝낼 수 있는 Exit 을 처리하는 갈래. QEMU 까지 올라가지 않는다.",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 508,
|
||||
"end_line": 510
|
||||
},
|
||||
{
|
||||
"start_line": 531,
|
||||
"end_line": 531
|
||||
}
|
||||
],
|
||||
"assumption": false,
|
||||
"details": [
|
||||
"Kernel"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "qemu",
|
||||
"label": "QEMU",
|
||||
"kind": "service",
|
||||
"role": "service",
|
||||
"shape": "box",
|
||||
"details": [
|
||||
"Userspace",
|
||||
"device emulation",
|
||||
"KVM_RUN 재호출"
|
||||
],
|
||||
"description": "userspace 처리가 필요한 Exit 에서만 개입하는 갈래.",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 510,
|
||||
"end_line": 516
|
||||
},
|
||||
{
|
||||
"start_line": 533,
|
||||
"end_line": 533
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "guest-resume",
|
||||
"label": "Guest 실행 재개",
|
||||
"kind": "service",
|
||||
"role": "sink",
|
||||
"shape": "box",
|
||||
"description": "두 갈래가 다시 만나는 자리. VM Exit 은 VM 종료가 아니라 여기로 돌아온다.",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 521,
|
||||
"end_line": 524
|
||||
},
|
||||
{
|
||||
"start_line": 529,
|
||||
"end_line": 529
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
}
|
||||
],
|
||||
"edges": [
|
||||
{
|
||||
"id": "exit-to-kvm",
|
||||
"from": "guest",
|
||||
"to": "kvm",
|
||||
"label": "VM Exit",
|
||||
"kind": "control",
|
||||
"style": "solid",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 495,
|
||||
"end_line": 495
|
||||
},
|
||||
{
|
||||
"start_line": 498,
|
||||
"end_line": 502
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "kernel-branch",
|
||||
"from": "kvm",
|
||||
"to": "kvm-kernel-handling",
|
||||
"label": "커널 처리 가능",
|
||||
"kind": "control",
|
||||
"style": "solid",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 504,
|
||||
"end_line": 510
|
||||
},
|
||||
{
|
||||
"start_line": 531,
|
||||
"end_line": 531
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "userspace-branch",
|
||||
"from": "kvm",
|
||||
"to": "qemu",
|
||||
"label": "KVM_RUN return",
|
||||
"kind": "control",
|
||||
"style": "solid",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 508,
|
||||
"end_line": 512
|
||||
},
|
||||
{
|
||||
"start_line": 533,
|
||||
"end_line": 533
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "kernel-reentry",
|
||||
"from": "kvm-kernel-handling",
|
||||
"to": "guest-resume",
|
||||
"label": "VM Entry",
|
||||
"kind": "control",
|
||||
"style": "solid",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 518,
|
||||
"end_line": 524
|
||||
},
|
||||
{
|
||||
"start_line": 531,
|
||||
"end_line": 531
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "userspace-reentry",
|
||||
"from": "qemu",
|
||||
"to": "guest-resume",
|
||||
"label": "VM Entry",
|
||||
"kind": "control",
|
||||
"style": "solid",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 514,
|
||||
"end_line": 524
|
||||
},
|
||||
{
|
||||
"start_line": 533,
|
||||
"end_line": 533
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
}
|
||||
],
|
||||
"legend": [],
|
||||
"metadata": {
|
||||
"rationale": "flow 로 고른 이유는 이 절이 답하는 물음이 「제어권이 어디까지 갔다가 어떻게 돌아오는가」이기 때문이다. Kernel·Userspace 를 경계 상자로 그리면 두 상자가 같은 열에서 겹쳐(check-figure-overlap 15750px²) 어느 노드가 어느 경계인지 오히려 흐려져 노드의 details 로 내렸다. 무엇이 Exit 을 부르는지(§8)와 Exit 이 잦을 때 무엇을 함께 보는지는 본문 표가 이미 맡고 있어 그림에 넣지 않았다."
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,258 @@
|
||||
{
|
||||
"version": "1.1",
|
||||
"id": "vms-sharing-one-nvme",
|
||||
"title": "가상 머신 둘과 호스트 프로세스가 하나의 NVMe 로 모인다",
|
||||
"question": "서로 다른 곳에서 시작한 I/O 는 어디에서 하나로 모여 device 로 나가는가",
|
||||
"type": "flow",
|
||||
"direction": "TB",
|
||||
"audience": [
|
||||
"가상 머신 위에서 본 저장소 지연의 원인을 가르려는 사람",
|
||||
"Storage Contention 과 CPU Contention 을 나눠 재려는 사람"
|
||||
],
|
||||
"summary": "VM1 QEMU 와 VM2 QEMU, Nginx, 호스트의 다른 프로세스에서 시작한 I/O 가 모두 하나의 Host Block Layer queue 로 들어가 NVMe 로 dispatch 된다.",
|
||||
"alt": "VM1 QEMU, VM2 QEMU, Nginx, Host 기타 네 곳에서 나온 화살표가 하나의 Host Block Layer 로 모이고 거기서 NVMe 로 나가는 수렴 흐름도.",
|
||||
"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",
|
||||
"anchor": {
|
||||
"kind": "heading",
|
||||
"value": "159. 여러 VM이 하나의 NVMe를 공유하면",
|
||||
"line": 7026
|
||||
}
|
||||
},
|
||||
"composition": {
|
||||
"profile": "component-flow",
|
||||
"diagram_only": true,
|
||||
"reference_ids": [
|
||||
"payment-event-flow"
|
||||
],
|
||||
"rationale": "이 절이 주장하는 것은 서로 다른 네 곳에서 시작한 요청이 한 자리로 모인다는 것이다. query-fanout 은 하나가 여러 저장소로 갈라지는 문법이라 방향이 반대이고, orchestrator-workers 는 가운데가 아래의 워커들에게 일을 나눠 주는 그림이라 「모인다」가 「나눈다」로 뒤집힌다. comparison 은 네 출발점을 나란히 늘어놓기만 해서 하나의 queue 로 모인다는 사실이 사라진다.",
|
||||
"focus_node": "host-block-layer"
|
||||
},
|
||||
"groups": [],
|
||||
"nodes": [
|
||||
{
|
||||
"id": "vm1-qemu",
|
||||
"label": "VM1 QEMU",
|
||||
"kind": "service",
|
||||
"role": "source",
|
||||
"shape": "box",
|
||||
"details": [
|
||||
"WRITE X",
|
||||
"READ Y",
|
||||
"WRITE Z"
|
||||
],
|
||||
"description": "가상 머신 하나의 QEMU 프로세스. 게스트의 I/O 가 여기서 호스트 파일 I/O 가 된다.",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 7029,
|
||||
"end_line": 7029
|
||||
},
|
||||
{
|
||||
"start_line": 7041,
|
||||
"end_line": 7044
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "vm2-qemu",
|
||||
"label": "VM2 QEMU",
|
||||
"kind": "service",
|
||||
"role": "source",
|
||||
"shape": "box",
|
||||
"details": [
|
||||
"READ A",
|
||||
"WRITE B"
|
||||
],
|
||||
"description": "다른 가상 머신의 QEMU 프로세스.",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 7031,
|
||||
"end_line": 7031
|
||||
},
|
||||
{
|
||||
"start_line": 7046,
|
||||
"end_line": 7048
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "nginx",
|
||||
"label": "Nginx",
|
||||
"kind": "service",
|
||||
"role": "source",
|
||||
"shape": "box",
|
||||
"description": "호스트에서 직접 도는 프로세스.",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 7033,
|
||||
"end_line": 7033
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "host-other",
|
||||
"label": "Host 기타",
|
||||
"kind": "service",
|
||||
"role": "source",
|
||||
"shape": "box",
|
||||
"description": "그 밖의 호스트 프로세스.",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 7035,
|
||||
"end_line": 7038
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "host-block-layer",
|
||||
"label": "Host Block Layer",
|
||||
"kind": "service",
|
||||
"role": "queue",
|
||||
"shape": "box",
|
||||
"emphasis": "primary",
|
||||
"details": [
|
||||
"Host block request",
|
||||
"blk-mq"
|
||||
],
|
||||
"description": "어디서 왔는지 구분하지 않고 모두 Host block request 로 다루는 계층. queue 에서 관리해 device 로 내보낸다.",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 7031,
|
||||
"end_line": 7031
|
||||
},
|
||||
{
|
||||
"start_line": 7017,
|
||||
"end_line": 7022
|
||||
},
|
||||
{
|
||||
"start_line": 7054,
|
||||
"end_line": 7054
|
||||
},
|
||||
{
|
||||
"start_line": 7058,
|
||||
"end_line": 7060
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "nvme",
|
||||
"label": "NVMe",
|
||||
"kind": "device",
|
||||
"role": "sink",
|
||||
"shape": "cylinder",
|
||||
"description": "네 갈래가 결국 함께 쓰는 하나의 물리 device.",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 7031,
|
||||
"end_line": 7031
|
||||
},
|
||||
{
|
||||
"start_line": 7019,
|
||||
"end_line": 7019
|
||||
},
|
||||
{
|
||||
"start_line": 7066,
|
||||
"end_line": 7069
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
}
|
||||
],
|
||||
"edges": [
|
||||
{
|
||||
"id": "vm1-to-block-layer",
|
||||
"from": "vm1-qemu",
|
||||
"to": "host-block-layer",
|
||||
"label": "",
|
||||
"kind": "data",
|
||||
"style": "solid",
|
||||
"order": 1,
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 7029,
|
||||
"end_line": 7031
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "vm2-to-block-layer",
|
||||
"from": "vm2-qemu",
|
||||
"to": "host-block-layer",
|
||||
"label": "",
|
||||
"kind": "data",
|
||||
"style": "solid",
|
||||
"order": 2,
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 7031,
|
||||
"end_line": 7031
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "nginx-to-block-layer",
|
||||
"from": "nginx",
|
||||
"to": "host-block-layer",
|
||||
"label": "",
|
||||
"kind": "data",
|
||||
"style": "solid",
|
||||
"order": 3,
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 7031,
|
||||
"end_line": 7033
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "host-other-to-block-layer",
|
||||
"from": "host-other",
|
||||
"to": "host-block-layer",
|
||||
"label": "",
|
||||
"kind": "data",
|
||||
"style": "solid",
|
||||
"order": 4,
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 7031,
|
||||
"end_line": 7035
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "block-layer-to-nvme",
|
||||
"from": "host-block-layer",
|
||||
"to": "nvme",
|
||||
"label": "dispatch",
|
||||
"kind": "data",
|
||||
"style": "solid",
|
||||
"order": 5,
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 7031,
|
||||
"end_line": 7031
|
||||
},
|
||||
{
|
||||
"start_line": 7054,
|
||||
"end_line": 7054
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
}
|
||||
],
|
||||
"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 가 말하지 않는다."
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,264 @@
|
||||
{
|
||||
"version": "1.1",
|
||||
"id": "write-completion-boundaries",
|
||||
"title": "fsync() 의 요구가 물리 storage 까지 내려가는 사슬",
|
||||
"question": "게스트가 fsync() 를 부르면 그 요구는 어느 계층까지, 몇 번 손이 바뀌어 내려가야 하는가",
|
||||
"type": "flow",
|
||||
"direction": "TB",
|
||||
"audience": [
|
||||
"가상 머신 위에서 데이터베이스를 돌리며 durability 를 따지는 사람",
|
||||
"완료 응답이 무엇을 보장하는지 가르려는 사람"
|
||||
],
|
||||
"summary": "게스트 PostgreSQL 의 fsync() 요구는 게스트 파일시스템과 게스트 Block Layer 에서 FLUSH 등으로 바뀌고 virtio-blk 와 QEMU / Backend, Host Storage Stack 을 지나 Physical Storage 까지 전달되어야 한다.",
|
||||
"alt": "PostgreSQL 의 fsync() 가 Guest Filesystem, Guest Block Layer, virtio-blk, QEMU / Backend, Host Storage Stack 을 차례로 지나 Physical Storage 에 닿는 위에서 아래로 읽는 흐름도.",
|
||||
"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",
|
||||
"anchor": {
|
||||
"kind": "heading",
|
||||
"value": "150. `fsync()`가 필요한 이유",
|
||||
"line": 6723
|
||||
}
|
||||
},
|
||||
"composition": {
|
||||
"profile": "component-flow",
|
||||
"diagram_only": true,
|
||||
"reference_ids": [
|
||||
"payment-event-flow"
|
||||
],
|
||||
"rationale": "이 절이 주장하는 것은 요구 하나가 한 방향으로 여러 계층을 지나야 완료가 된다는 것이다. sequence 는 참여자마다 생명선을 세우고 요청과 응답을 짝지어 세우는데 이 절에는 돌아오는 응답이 적혀 있지 않아 생명선이 전부 빈 세로줄이 된다. comparison 은 write 와 fsync 를 나란히 놓는 그림이라 「어디까지 내려가야 하는가」라는 자리의 문제가 지워진다.",
|
||||
"focus_node": "physical-storage"
|
||||
},
|
||||
"groups": [],
|
||||
"nodes": [
|
||||
{
|
||||
"id": "postgresql",
|
||||
"label": "PostgreSQL",
|
||||
"kind": "service",
|
||||
"role": "source",
|
||||
"shape": "box",
|
||||
"details": [
|
||||
"write()",
|
||||
"fsync()"
|
||||
],
|
||||
"description": "필요한 시점에 fsync() 를 불러 영속성 경계까지 반영을 요청하는 게스트 프로세스.",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 6742,
|
||||
"end_line": 6742
|
||||
},
|
||||
{
|
||||
"start_line": 6726,
|
||||
"end_line": 6734
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "guest-filesystem",
|
||||
"label": "Guest Filesystem",
|
||||
"kind": "service",
|
||||
"role": "service",
|
||||
"shape": "box",
|
||||
"description": "fsync() 요구를 처음 받는 게스트 파일시스템.",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 6746,
|
||||
"end_line": 6746
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "guest-block-layer",
|
||||
"label": "Guest Block Layer",
|
||||
"kind": "service",
|
||||
"role": "service",
|
||||
"shape": "box",
|
||||
"description": "요구를 FLUSH 등의 block 요청으로 바꾸는 계층.",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 6749,
|
||||
"end_line": 6751
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "virtio-blk",
|
||||
"label": "virtio-blk",
|
||||
"kind": "service",
|
||||
"role": "service",
|
||||
"shape": "box",
|
||||
"description": "그 요청을 VM 경계 너머로 옮기는 게스트 driver.",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 6753,
|
||||
"end_line": 6753
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "qemu-backend",
|
||||
"label": "QEMU / Backend",
|
||||
"kind": "service",
|
||||
"role": "service",
|
||||
"shape": "box",
|
||||
"description": "요청을 받아 호스트 쪽 파일 I/O 나 block I/O 로 잇는 자리.",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 6756,
|
||||
"end_line": 6756
|
||||
},
|
||||
{
|
||||
"start_line": 6686,
|
||||
"end_line": 6686
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "host-storage-stack",
|
||||
"label": "Host Storage Stack",
|
||||
"kind": "service",
|
||||
"role": "service",
|
||||
"shape": "box",
|
||||
"description": "호스트 Page Cache 와 파일시스템, Block Layer 가 놓인 구간.",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 6759,
|
||||
"end_line": 6759
|
||||
},
|
||||
{
|
||||
"start_line": 6688,
|
||||
"end_line": 6692
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "physical-storage",
|
||||
"label": "Physical Storage",
|
||||
"kind": "store",
|
||||
"role": "sink",
|
||||
"shape": "box",
|
||||
"emphasis": "primary",
|
||||
"description": "요구가 여기까지 전달되어야 정전 이후 생존을 말할 수 있다.",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 6762,
|
||||
"end_line": 6765
|
||||
},
|
||||
{
|
||||
"start_line": 6729,
|
||||
"end_line": 6729
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
}
|
||||
],
|
||||
"edges": [
|
||||
{
|
||||
"id": "app-to-guest-fs",
|
||||
"from": "postgresql",
|
||||
"to": "guest-filesystem",
|
||||
"label": "fsync()",
|
||||
"kind": "control",
|
||||
"style": "solid",
|
||||
"order": 1,
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 6744,
|
||||
"end_line": 6746
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "guest-fs-to-block-layer",
|
||||
"from": "guest-filesystem",
|
||||
"to": "guest-block-layer",
|
||||
"label": "",
|
||||
"kind": "control",
|
||||
"style": "solid",
|
||||
"order": 2,
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 6746,
|
||||
"end_line": 6749
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "block-layer-to-virtio",
|
||||
"from": "guest-block-layer",
|
||||
"to": "virtio-blk",
|
||||
"label": "FLUSH 등",
|
||||
"kind": "control",
|
||||
"style": "solid",
|
||||
"order": 3,
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 6751,
|
||||
"end_line": 6753
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "virtio-to-qemu",
|
||||
"from": "virtio-blk",
|
||||
"to": "qemu-backend",
|
||||
"label": "",
|
||||
"kind": "control",
|
||||
"style": "solid",
|
||||
"order": 4,
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 6753,
|
||||
"end_line": 6756
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "qemu-to-host-stack",
|
||||
"from": "qemu-backend",
|
||||
"to": "host-storage-stack",
|
||||
"label": "",
|
||||
"kind": "control",
|
||||
"style": "solid",
|
||||
"order": 5,
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 6756,
|
||||
"end_line": 6759
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "host-stack-to-physical",
|
||||
"from": "host-storage-stack",
|
||||
"to": "physical-storage",
|
||||
"label": "",
|
||||
"kind": "control",
|
||||
"style": "solid",
|
||||
"order": 6,
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 6759,
|
||||
"end_line": 6765
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
}
|
||||
],
|
||||
"legend": [],
|
||||
"metadata": {
|
||||
"rationale": "component-flow 를 고른 이유는 이 절이 답하는 물음이 「fsync() 의 요구가 어디까지 내려가야 하는가」이기 때문이다. 앞뒤 절이 대는 사실 가운데 그리지 않은 것이 있다. 「write · writeback · fsync/flush · 전원 장애 생존」 네 완료 경계를 이 사슬 위에 표시하려 했으나 SSOT 는 §148 에서 네 이름을 나열할 뿐 writeback 완료와 fsync/flush 완료가 이 사슬의 어느 칸에서 성립하는지는 정하지 않았고, §148 자체가 이 앵커의 준비된 문맥(§149~§151) 밖이라 인용할 줄도 없다. 게다가 네 이름의 나열은 기록 본문에 이미 코드블록으로 있어 그림으로 다시 그리면 중복이다. 그래서 자리로만 보이는 것 — 요구가 여섯 번 손이 바뀌어 내려간다는 것 — 만 남겼다."
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
그림. 그림 하나가 폴더 하나다 — <이름>/<이름>.svg 와 편집 형식들.
|
||||
기록의 assets: file: 도 이 폴더를 가리킨다. Studio 에 올릴 사본을 따로 두지 않는다 —
|
||||
사본을 두면 정본이 둘이 되고, 사본 쪽에는 ../../.techviz/<이름>/ 이 없어 다시 만들 수 없다.
|
||||
@@ -0,0 +1,32 @@
|
||||
# 게스트의 write() 가 virtqueue 에 실리기까지
|
||||
|
||||
## Alternative text
|
||||
|
||||
게스트 사용자 공간의 PostgreSQL/Keycloak 에서 시작한 write() 가 게스트 커널의 VFS, ext4/XFS, Guest Page Cache, Guest Block Layer, /dev/vda, virtio-blk Frontend 를 거쳐 virtqueue 에 도달하는 위에서 아래로 읽는 흐름도.
|
||||
|
||||
## 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 의 다른 절이 대는 사실이라 이 그림에 넣지 않았고 본문 문단이 맡는다.
|
||||
|
||||
## Elements and evidence
|
||||
|
||||
- **Boundary: Guest Userspace** (boundary): 게스트 사용자 공간. 저장장치를 직접 다루지 않는다. Evidence: L5807–L5809.
|
||||
- **Boundary: Guest Kernel** (boundary): 게스트 커널 공간. VM Boundary 위쪽 계층이 전부 여기 있다. Evidence: L5815–L5833.
|
||||
- **PostgreSQL / Keycloak** (service): 파일 연산을 요청하는 게스트 프로세스. Evidence: L5807–L5809, L5885–L5885, L5811–L5813.
|
||||
- **VFS** (service): 열린 파일이 어느 filesystem 구현에 속하는지 보고 연산을 넘기는 공통 계층. Evidence: L5815–L5817, L5905–L5905.
|
||||
- **ext4 / XFS** (service): 파일과 디렉터리를 block 공간에 배치하는 게스트 filesystem. Evidence: L5819–L5819.
|
||||
- **Guest Page Cache** (store): 게스트 RAM 에 있는 cache. write() 가 성공해도 데이터는 아직 여기까지만 확정된다. Evidence: L5821–L5821.
|
||||
- **Guest Block Layer** (service): 파일 세계의 요청을 block device 세계의 요청으로 바꾸는 계층. Evidence: L5825–L5825.
|
||||
- **/dev/vda** (device): 게스트 Linux 가 하나의 block device 로 인식하는 이름. Evidence: L5829–L5829, L5879–L5879.
|
||||
- **virtio-blk Frontend** (service): 그 가상 block device 를 제어하는 게스트 커널 driver. Evidence: L5831–L5831, L5789–L5789.
|
||||
- **virtqueue** (queue): 요청이 실리는 자리. 바로 아래가 VM Boundary 다. Evidence: L5833–L5835.
|
||||
|
||||
## Relationships
|
||||
|
||||
- **PostgreSQL / Keycloak → VFS:** data. Evidence: L5811–L5813, L5890–L5890.
|
||||
- **VFS → ext4 / XFS:** data. Evidence: L5817–L5819.
|
||||
- **ext4 / XFS → Guest Page Cache:** data. Evidence: L5819–L5821.
|
||||
- **Guest Page Cache → Guest Block Layer:** writeback. Evidence: L5823–L5825.
|
||||
- **Guest Block Layer → /dev/vda:** WRITE / FLUSH. Evidence: L5827–L5829.
|
||||
- **/dev/vda → virtio-blk Frontend:** data. Evidence: L5829–L5831.
|
||||
- **virtio-blk Frontend → virtqueue:** Virtio block request. Evidence: L5831–L5833.
|
||||
@@ -0,0 +1,38 @@
|
||||
# 게스트의 write() 가 virtqueue 에 실리기까지
|
||||
# Question: 게스트 애플리케이션의 write() 는 virtqueue 에 실리기 전에 게스트의 어느 계층을 어떤 순서로 지나는가
|
||||
direction: down
|
||||
g0: "Guest Userspace" {
|
||||
n0: "PostgreSQL / Keycloak" {
|
||||
shape: rectangle
|
||||
}
|
||||
}
|
||||
g1: "Guest Kernel" {
|
||||
n1: "VFS" {
|
||||
shape: rectangle
|
||||
}
|
||||
n2: "ext4 / XFS" {
|
||||
shape: rectangle
|
||||
}
|
||||
n3: "Guest Page Cache" {
|
||||
shape: rectangle
|
||||
}
|
||||
n4: "Guest Block Layer" {
|
||||
shape: rectangle
|
||||
}
|
||||
n5: "/dev/vda" {
|
||||
shape: rectangle
|
||||
}
|
||||
n6: "virtio-blk Frontend" {
|
||||
shape: rectangle
|
||||
}
|
||||
n7: "virtqueue" {
|
||||
shape: queue
|
||||
}
|
||||
}
|
||||
g0.n0 -> g1.n1
|
||||
g1.n1 -> g1.n2
|
||||
g1.n2 -> g1.n3
|
||||
g1.n3 -> g1.n4: "writeback"
|
||||
g1.n4 -> g1.n5: "WRITE / FLUSH"
|
||||
g1.n5 -> g1.n6
|
||||
g1.n6 -> g1.n7: "Virtio block request"
|
||||
@@ -0,0 +1,76 @@
|
||||
<?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">
|
||||
<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"/>
|
||||
</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"/>
|
||||
</mxCell>
|
||||
<mxCell id="n_app" value="PostgreSQL / Keycloak<br/>read / write<br/>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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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"/>
|
||||
</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"/>
|
||||
</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"/>
|
||||
</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"/>
|
||||
</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"/>
|
||||
</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"/>
|
||||
</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"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
</mxfile>
|
||||
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"harness_version": "0.2.0",
|
||||
"spec_id": "guest-block-io-to-virtqueue",
|
||||
"spec_version": "1.1",
|
||||
"spec_sha256": "f41eedadcb8a6841cf981bea9e0a9e50a3df836df3cad528b4245a8d42e83915",
|
||||
"source_context": {
|
||||
"document": "docs/virtualization/final/document.md",
|
||||
"document_sha256": "181e2b3cc8a45bae81e7e8193d026937c4d586ae4495d3b2c323eb7e6abcfadd",
|
||||
"anchor": {
|
||||
"kind": "heading",
|
||||
"value": "128. 전체 구조",
|
||||
"line": 5804
|
||||
}
|
||||
},
|
||||
"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.excalidraw",
|
||||
"guest-block-io-to-virtqueue.alt.md"
|
||||
],
|
||||
"lint_issue_count": 5,
|
||||
"assumption_count": 0,
|
||||
"assumptions_allowed": false,
|
||||
"composition_profile": "component-flow",
|
||||
"reference_ids": [
|
||||
"payment-event-flow"
|
||||
],
|
||||
"diagram_only": true
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
%% 게스트의 write() 가 virtqueue 에 실리기까지
|
||||
%% question: 게스트 애플리케이션의 write() 는 virtqueue 에 실리기 전에 게스트의 어느 계층을 어떤 순서로 지나는가
|
||||
flowchart TB
|
||||
subgraph g_guest_userspace["Guest Userspace"]
|
||||
n0["PostgreSQL / Keycloak"]
|
||||
end
|
||||
subgraph g_guest_kernel["Guest Kernel"]
|
||||
n1["VFS"]
|
||||
n2["ext4 / XFS"]
|
||||
n3["Guest Page Cache"]
|
||||
n4["Guest Block Layer"]
|
||||
n5["/dev/vda"]
|
||||
n6["virtio-blk Frontend"]
|
||||
n7(["virtqueue"])
|
||||
end
|
||||
n0 --> n1
|
||||
n1 --> n2
|
||||
n2 --> n3
|
||||
n3 -->|"writeback"| n4
|
||||
n4 -->|"WRITE / FLUSH"| n5
|
||||
n5 --> n6
|
||||
n6 -->|"Virtio block request"| n7
|
||||
@@ -0,0 +1,107 @@
|
||||
<?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">
|
||||
<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>{"techviz":{"spec_version":"1.1","id":"guest-block-io-to-virtqueue","profile":"component-flow"},"source_context":{"document":"docs/virtualization/final/document.md","document_sha256":"181e2b3cc8a45bae81e7e8193d026937c4d586ae4495d3b2c323eb7e6abcfadd","anchor":{"kind":"heading","value":"128. 전체 구조","line":5804}},"evidence_policy":"Each factual element cites source lines or is marked assumption.","diagram_only":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="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>
|
||||
<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>
|
||||
</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>
|
||||
</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>
|
||||
</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>
|
||||
</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>
|
||||
</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>
|
||||
</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>
|
||||
</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>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 8.9 KiB |
@@ -0,0 +1,30 @@
|
||||
# Guest 의 메모리 접근 한 번은 이름이 두 번 바뀌며 네 계층을 지난다
|
||||
|
||||
## Alternative text
|
||||
|
||||
Guest 구역 안에서 Keycloak · PostgreSQL 의 GVA 가 Guest TLB 와 Guest Page Table 을 지나 GPA 가 되고, VM Boundary 를 넘어 EPT 에서 HPA 가 된 뒤 Host RAM 구역의 Host Physical Memory 를 지나 NUMA Node 0 과 NUMA Node 1 로 갈라지는 주소 변환 경로도.
|
||||
|
||||
## 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 가 이 경로를 어떻게 준비하는지는 본문의 문단과 표가 맡는다.
|
||||
|
||||
## Elements and evidence
|
||||
|
||||
- **Boundary: Guest** (boundary): VM Boundary 안쪽. Guest Userspace 와 Guest Kernel 이 여기 있고 GVA 가 GPA 가 되는 첫 번째 변환이 여기서 끝난다. Evidence: L4034–L4053.
|
||||
- **Boundary: Host RAM** (boundary): 변환이 끝난 주소가 닿는 실제 서버 메모리. Evidence: L4069–L4079.
|
||||
- **Keycloak · PostgreSQL** (actor): Guest 안에서 메모리를 읽는 프로세스. 여기서 GVA 가 출발한다. Evidence: L4034–L4038.
|
||||
- **Guest TLB** (component): 최근 변환 결과를 담아 두는 CPU 안의 캐시. Evidence: L4043–L4045.
|
||||
- **Guest Page Table** (component): GVA 를 GPA 로 바꾸는 첫 번째 변환 표. Evidence: L4048–L4053.
|
||||
- **EPT** (component): VM Boundary 바깥에서 GPA 를 HPA 로 바꾸는 두 번째 변환 표. KVM 과 CPU 가 맡는다. Evidence: L4055–L4067.
|
||||
- **Host Physical Memory** (store): HPA 가 가리키는 실제 서버 메모리. Evidence: L4069–L4072.
|
||||
- **NUMA Node 0** (store): physical page 가 놓일 수 있는 한쪽 NUMA node. Evidence: L4073–L4075.
|
||||
- **NUMA Node 1** (store): physical page 가 놓일 수 있는 다른 쪽 NUMA node. Evidence: L4073–L4075.
|
||||
|
||||
## Relationships
|
||||
|
||||
- **EPT → Host Physical Memory:** HPA. Evidence: L4062–L4071.
|
||||
- **Host Physical Memory → NUMA Node 0:** Physical Page. Evidence: L4071–L4075.
|
||||
- **Host Physical Memory → NUMA Node 1:** Physical Page. Evidence: L4071–L4075.
|
||||
- **Guest Page Table → EPT:** VM Boundary. Evidence: L4053–L4062.
|
||||
- **Keycloak · PostgreSQL → Guest TLB:** GVA. Evidence: L4036–L4043.
|
||||
- **Guest TLB → Guest Page Table:** TLB Miss. Evidence: L4043–L4048.
|
||||
@@ -0,0 +1,34 @@
|
||||
# Guest 의 메모리 접근 한 번은 이름이 두 번 바뀌며 네 계층을 지난다
|
||||
# Question: Guest 프로세스가 읽은 주소는 어느 계층을 차례로 지나 어떤 물리 메모리에 닿는가
|
||||
direction: down
|
||||
g0: "Guest" {
|
||||
n0: "Keycloak · PostgreSQL" {
|
||||
shape: person
|
||||
}
|
||||
n1: "Guest TLB" {
|
||||
shape: rectangle
|
||||
}
|
||||
n2: "Guest Page Table" {
|
||||
shape: rectangle
|
||||
}
|
||||
}
|
||||
g1: "Host RAM" {
|
||||
n4: "Host Physical Memory" {
|
||||
shape: rectangle
|
||||
}
|
||||
n5: "NUMA Node 0" {
|
||||
shape: rectangle
|
||||
}
|
||||
n6: "NUMA Node 1" {
|
||||
shape: rectangle
|
||||
}
|
||||
}
|
||||
n3: "EPT" {
|
||||
shape: rectangle
|
||||
}
|
||||
g0.n0 -> g0.n1: "GVA"
|
||||
g0.n1 -> g0.n2: "TLB Miss"
|
||||
g0.n2 -> n3: "VM Boundary"
|
||||
n3 -> g1.n4: "HPA"
|
||||
g1.n4 -> g1.n5: "Physical Page"
|
||||
g1.n4 -> g1.n6: "Physical Page"
|
||||
@@ -0,0 +1,68 @@
|
||||
<?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">
|
||||
<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"/>
|
||||
</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"/>
|
||||
</mxCell>
|
||||
<mxCell id="n_guest-process" value="Keycloak · PostgreSQL<br/>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>
|
||||
<mxCell id="n_guest-tlb" value="Guest TLB<br/>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>
|
||||
<mxCell id="n_guest-page-table" value="Guest Page Table<br/>Guest Kernel<br/>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>
|
||||
<mxCell id="n_ept" value="EPT<br/>KVM / CPU<br/>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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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"/>
|
||||
</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"/>
|
||||
</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"/>
|
||||
</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"/>
|
||||
</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"/>
|
||||
</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"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
</mxfile>
|
||||
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"harness_version": "0.2.0",
|
||||
"spec_id": "guest-memory-address-translation-path",
|
||||
"spec_version": "1.1",
|
||||
"spec_sha256": "9db86cb8c5f81b1c5886877e1efd3ae4d4174d839fb29cffd5f8ae102508f2f3",
|
||||
"source_context": {
|
||||
"document": "docs/virtualization/final/document.md",
|
||||
"document_sha256": "181e2b3cc8a45bae81e7e8193d026937c4d586ae4495d3b2c323eb7e6abcfadd",
|
||||
"anchor": {
|
||||
"kind": "heading",
|
||||
"value": "87. 최종 기준 그림",
|
||||
"line": 4029
|
||||
}
|
||||
},
|
||||
"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.excalidraw",
|
||||
"guest-memory-address-translation-path.alt.md"
|
||||
],
|
||||
"lint_issue_count": 0,
|
||||
"assumption_count": 0,
|
||||
"assumptions_allowed": false,
|
||||
"composition_profile": "component-flow",
|
||||
"reference_ids": [
|
||||
"payment-event-flow"
|
||||
],
|
||||
"diagram_only": true
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
%% Guest 의 메모리 접근 한 번은 이름이 두 번 바뀌며 네 계층을 지난다
|
||||
%% question: Guest 프로세스가 읽은 주소는 어느 계층을 차례로 지나 어떤 물리 메모리에 닿는가
|
||||
flowchart TB
|
||||
subgraph g_guest["Guest"]
|
||||
n0(["Keycloak · PostgreSQL"])
|
||||
n1["Guest TLB"]
|
||||
n2["Guest Page Table"]
|
||||
end
|
||||
subgraph g_host_ram["Host RAM"]
|
||||
n4["Host Physical Memory"]
|
||||
n5["NUMA Node 0"]
|
||||
n6["NUMA Node 1"]
|
||||
end
|
||||
n3["EPT"]
|
||||
n0 -->|"GVA"| n1
|
||||
n1 -->|"TLB Miss"| n2
|
||||
n2 -->|"VM Boundary"| n3
|
||||
n3 -->|"HPA"| n4
|
||||
n4 -->|"Physical Page"| n5
|
||||
n4 -->|"Physical Page"| n6
|
||||
classDef external stroke-dasharray: 6 4
|
||||
class n0 external
|
||||
@@ -0,0 +1,115 @@
|
||||
<?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">
|
||||
<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>{"techviz":{"spec_version":"1.1","id":"guest-memory-address-translation-path","profile":"component-flow"},"source_context":{"document":"docs/virtualization/final/document.md","document_sha256":"181e2b3cc8a45bae81e7e8193d026937c4d586ae4495d3b2c323eb7e6abcfadd","anchor":{"kind":"heading","value":"87. 최종 기준 그림","line":4029}},"evidence_policy":"Each factual element cites source lines or is marked assumption.","diagram_only":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="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>
|
||||
<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>
|
||||
</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>
|
||||
</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>
|
||||
</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>
|
||||
</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>
|
||||
</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>
|
||||
</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>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 9.4 KiB |
@@ -0,0 +1,25 @@
|
||||
# vCPU 를 Node 0 에 묶어도 backing 이 Node 1 에 있으면 접근은 remote 로 남는다
|
||||
|
||||
## Alternative text
|
||||
|
||||
QEMU vCPU Thread, VM1 Memory Backing, Node 0 CPU, Node 0 RAM, Node 1 RAM 다섯 참여자 사이의 순서도. 앞 세 메시지는 pinning 만 한 배치에서 접근이 Node 1 RAM 으로 가는 것을, 뒤 두 메시지는 memory 를 Node 0 에 묶은 배치에서 같은 접근이 Node 0 RAM 으로 가는 것을 보인다.
|
||||
|
||||
## 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 를 가졌는지는 그림이 말하지 않는다.
|
||||
|
||||
## Elements and evidence
|
||||
|
||||
- **QEMU vCPU Thread** (process): Guest vCPU 가 Host 에서 갖는 실체. Host scheduler 가 이것을 logical CPU 에 올린다. Evidence: L3315–L3324.
|
||||
- **VM1 Memory Backing** (memory): 그 가상 머신의 Host physical backing page 가 놓이는 자리. Evidence: L3327–L3327, L3352–L3354, L3381–L3382.
|
||||
- **Node 0 CPU** (compute): vCPU thread 가 실행되는 logical CPU. NUMA Node 0 에 속한다. Evidence: L3322–L3330.
|
||||
- **Node 0 RAM** (memory): Node 0 CPU 와 같은 node 의 RAM. Evidence: L3288–L3296, L3373–L3382.
|
||||
- **Node 1 RAM** (memory): 다른 node 의 RAM. Node 0 CPU 가 읽으면 interconnect 를 건넌다. Evidence: L3299–L3306, L3334–L3334.
|
||||
|
||||
## Relationships
|
||||
|
||||
- **QEMU vCPU Thread → Node 0 CPU:** vCPU Pinning. Evidence: L3322–L3324, L3347–L3350.
|
||||
- **VM1 Memory Backing → Node 1 RAM:** Memory Placement. Evidence: L3327–L3327, L3352–L3354.
|
||||
- **Node 0 CPU → Node 1 RAM:** Remote Access. Evidence: L3329–L3339, L3356–L3357.
|
||||
- **VM1 Memory Backing → Node 0 RAM:** Memory Binding. Evidence: L3361–L3366, L3381–L3382.
|
||||
- **Node 0 CPU → Node 0 RAM:** Local Access. Evidence: L3288–L3296, L3373–L3382.
|
||||
@@ -0,0 +1,23 @@
|
||||
# vCPU 를 Node 0 에 묶어도 backing 이 Node 1 에 있으면 접근은 remote 로 남는다
|
||||
# Question: vCPU pinning 만 한 배치와 memory binding 까지 맞춘 배치에서 같은 memory 접근은 각각 어느 node 의 RAM 에 닿는가
|
||||
direction: right
|
||||
n0: "QEMU vCPU Thread" {
|
||||
shape: rectangle
|
||||
}
|
||||
n1: "VM1 Memory Backing" {
|
||||
shape: rectangle
|
||||
}
|
||||
n2: "Node 0 CPU" {
|
||||
shape: rectangle
|
||||
}
|
||||
n3: "Node 0 RAM" {
|
||||
shape: rectangle
|
||||
}
|
||||
n4: "Node 1 RAM" {
|
||||
shape: rectangle
|
||||
}
|
||||
n0 -> n2: "vCPU Pinning"
|
||||
n1 -> n4: "Memory Placement"
|
||||
n2 -> n4: "Remote Access"
|
||||
n1 -> n3: "Memory Binding"
|
||||
n2 -> n3: "Local Access"
|
||||
@@ -0,0 +1,51 @@
|
||||
<?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="numa-vcpu-and-memory-placement" name="vCPU 를 Node 0 에 묶어도 backing 이 Node 1 에 있으면 접근은 remote 로 남는다">
|
||||
<mxGraphModel dx="1080" dy="480" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1080" pageHeight="1169" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0"/>
|
||||
<mxCell id="1" parent="0"/>
|
||||
<mxCell id="n_qemu-vcpu-thread" value="QEMU vCPU Thread<br/>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">
|
||||
<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<br/>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">
|
||||
<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<br/>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">
|
||||
<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<br/>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">
|
||||
<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<br/>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">
|
||||
<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">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="330.0" y="128.0" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="e_backing-on-node1" value="Memory Placement" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_vm-memory-backing" target="n_node1-ram">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="647.5" y="190.0" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="e_remote-access" value="Remote Access" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_node0-cpu" target="n_node1-ram">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="750.0" y="252.0" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="e_memory-binding" value="Memory Binding" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_vm-memory-backing" target="n_node0-ram">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="542.5" y="314.0" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="e_local-access" value="Local Access" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_node0-cpu" target="n_node0-ram">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="645.0" y="376.0" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
</mxfile>
|
||||
@@ -0,0 +1,813 @@
|
||||
{
|
||||
"type": "excalidraw",
|
||||
"version": 2,
|
||||
"source": "techviz-harness",
|
||||
"elements": [
|
||||
{
|
||||
"id": "edge-vcpu-pinning",
|
||||
"type": "arrow",
|
||||
"x": 120.0,
|
||||
"y": 140.0,
|
||||
"width": 420.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": 62242264,
|
||||
"version": 1,
|
||||
"versionNonce": 1499099216,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"points": [
|
||||
[
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
420.0,
|
||||
0.0
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
"startBinding": {
|
||||
"elementId": "node-qemu-vcpu-thread",
|
||||
"focus": 0,
|
||||
"gap": 4
|
||||
},
|
||||
"endBinding": {
|
||||
"elementId": "node-node0-cpu",
|
||||
"focus": 0,
|
||||
"gap": 4
|
||||
},
|
||||
"startArrowhead": null,
|
||||
"endArrowhead": "arrow",
|
||||
"elbowed": true
|
||||
},
|
||||
{
|
||||
"id": "edge-label-vcpu-pinning",
|
||||
"type": "text",
|
||||
"x": 282.0,
|
||||
"y": 116.0,
|
||||
"width": 96,
|
||||
"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": 1253222705,
|
||||
"version": 1,
|
||||
"versionNonce": 1692022138,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"fontSize": 13,
|
||||
"fontFamily": 5,
|
||||
"text": "vCPU Pinning",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "vCPU Pinning",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "edge-backing-on-node1",
|
||||
"type": "arrow",
|
||||
"x": 335.0,
|
||||
"y": 202.0,
|
||||
"width": 625.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": 597976500,
|
||||
"version": 1,
|
||||
"versionNonce": 751236157,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"points": [
|
||||
[
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
625.0,
|
||||
0.0
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
"startBinding": {
|
||||
"elementId": "node-vm-memory-backing",
|
||||
"focus": 0,
|
||||
"gap": 4
|
||||
},
|
||||
"endBinding": {
|
||||
"elementId": "node-node1-ram",
|
||||
"focus": 0,
|
||||
"gap": 4
|
||||
},
|
||||
"startArrowhead": null,
|
||||
"endArrowhead": "arrow",
|
||||
"elbowed": true
|
||||
},
|
||||
{
|
||||
"id": "edge-label-backing-on-node1",
|
||||
"type": "text",
|
||||
"x": 583.5,
|
||||
"y": 178.0,
|
||||
"width": 128,
|
||||
"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": 190361474,
|
||||
"version": 1,
|
||||
"versionNonce": 659971302,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"fontSize": 13,
|
||||
"fontFamily": 5,
|
||||
"text": "Memory Placement",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "Memory Placement",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "edge-remote-access",
|
||||
"type": "arrow",
|
||||
"x": 540.0,
|
||||
"y": 264.0,
|
||||
"width": 420.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": 723638991,
|
||||
"version": 1,
|
||||
"versionNonce": 805387919,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"points": [
|
||||
[
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
420.0,
|
||||
0.0
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
"startBinding": {
|
||||
"elementId": "node-node0-cpu",
|
||||
"focus": 0,
|
||||
"gap": 4
|
||||
},
|
||||
"endBinding": {
|
||||
"elementId": "node-node1-ram",
|
||||
"focus": 0,
|
||||
"gap": 4
|
||||
},
|
||||
"startArrowhead": null,
|
||||
"endArrowhead": "arrow",
|
||||
"elbowed": true
|
||||
},
|
||||
{
|
||||
"id": "edge-label-remote-access",
|
||||
"type": "text",
|
||||
"x": 698.0,
|
||||
"y": 240.0,
|
||||
"width": 104,
|
||||
"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": 849395583,
|
||||
"version": 1,
|
||||
"versionNonce": 207564235,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"fontSize": 13,
|
||||
"fontFamily": 5,
|
||||
"text": "Remote Access",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "Remote Access",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "edge-memory-binding",
|
||||
"type": "arrow",
|
||||
"x": 335.0,
|
||||
"y": 326.0,
|
||||
"width": 415.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": 196285201,
|
||||
"version": 1,
|
||||
"versionNonce": 1908442994,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"points": [
|
||||
[
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
415.0,
|
||||
0.0
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
"startBinding": {
|
||||
"elementId": "node-vm-memory-backing",
|
||||
"focus": 0,
|
||||
"gap": 4
|
||||
},
|
||||
"endBinding": {
|
||||
"elementId": "node-node0-ram",
|
||||
"focus": 0,
|
||||
"gap": 4
|
||||
},
|
||||
"startArrowhead": null,
|
||||
"endArrowhead": "arrow",
|
||||
"elbowed": true
|
||||
},
|
||||
{
|
||||
"id": "edge-label-memory-binding",
|
||||
"type": "text",
|
||||
"x": 486.5,
|
||||
"y": 302.0,
|
||||
"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": 34697980,
|
||||
"version": 1,
|
||||
"versionNonce": 231604789,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"fontSize": 13,
|
||||
"fontFamily": 5,
|
||||
"text": "Memory Binding",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "Memory Binding",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "edge-local-access",
|
||||
"type": "arrow",
|
||||
"x": 540.0,
|
||||
"y": 388.0,
|
||||
"width": 210.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": 206481616,
|
||||
"version": 1,
|
||||
"versionNonce": 1350583922,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"points": [
|
||||
[
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
210.0,
|
||||
0.0
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
"startBinding": {
|
||||
"elementId": "node-node0-cpu",
|
||||
"focus": 0,
|
||||
"gap": 4
|
||||
},
|
||||
"endBinding": {
|
||||
"elementId": "node-node0-ram",
|
||||
"focus": 0,
|
||||
"gap": 4
|
||||
},
|
||||
"startArrowhead": null,
|
||||
"endArrowhead": "arrow",
|
||||
"elbowed": true
|
||||
},
|
||||
{
|
||||
"id": "edge-label-local-access",
|
||||
"type": "text",
|
||||
"x": 597.0,
|
||||
"y": 364.0,
|
||||
"width": 96,
|
||||
"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": 34733324,
|
||||
"version": 1,
|
||||
"versionNonce": 425512157,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"fontSize": 13,
|
||||
"fontFamily": 5,
|
||||
"text": "Local Access",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "Local Access",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "node-qemu-vcpu-thread",
|
||||
"type": "rectangle",
|
||||
"x": 45.0,
|
||||
"y": 35.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": 1825553164,
|
||||
"version": 1,
|
||||
"versionNonce": 1401023854,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false
|
||||
},
|
||||
{
|
||||
"id": "node-label-qemu-vcpu-thread",
|
||||
"type": "text",
|
||||
"x": 55.0,
|
||||
"y": 45.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": 1778333500,
|
||||
"version": 1,
|
||||
"versionNonce": 1378313554,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"fontSize": 15,
|
||||
"fontFamily": 5,
|
||||
"text": "QEMU vCPU Thread\nGuest vCPU",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "QEMU vCPU Thread\nGuest vCPU",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "node-vm-memory-backing",
|
||||
"type": "rectangle",
|
||||
"x": 255.0,
|
||||
"y": 35.0,
|
||||
"width": 160.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": 1737545018,
|
||||
"version": 1,
|
||||
"versionNonce": 1015012347,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false
|
||||
},
|
||||
{
|
||||
"id": "node-label-vm-memory-backing",
|
||||
"type": "text",
|
||||
"x": 265.0,
|
||||
"y": 45.0,
|
||||
"width": 140.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": 1698958649,
|
||||
"version": 1,
|
||||
"versionNonce": 994477758,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"fontSize": 15,
|
||||
"fontFamily": 5,
|
||||
"text": "VM1 Memory Backing\nVM1 RAM",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "VM1 Memory Backing\nVM1 RAM",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "node-node0-cpu",
|
||||
"type": "rectangle",
|
||||
"x": 465.0,
|
||||
"y": 35.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": 932582931,
|
||||
"version": 1,
|
||||
"versionNonce": 1817086514,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false
|
||||
},
|
||||
{
|
||||
"id": "node-label-node0-cpu",
|
||||
"type": "text",
|
||||
"x": 475.0,
|
||||
"y": 45.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": 191003957,
|
||||
"version": 1,
|
||||
"versionNonce": 1993112408,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"fontSize": 15,
|
||||
"fontFamily": 5,
|
||||
"text": "Node 0 CPU\nHost Logical CPU",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "Node 0 CPU\nHost Logical CPU",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "node-node0-ram",
|
||||
"type": "rectangle",
|
||||
"x": 675.0,
|
||||
"y": 35.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": 107670886,
|
||||
"version": 1,
|
||||
"versionNonce": 1647932318,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false
|
||||
},
|
||||
{
|
||||
"id": "node-label-node0-ram",
|
||||
"type": "text",
|
||||
"x": 685.0,
|
||||
"y": 45.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": 1867132934,
|
||||
"version": 1,
|
||||
"versionNonce": 938518424,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"fontSize": 15,
|
||||
"fontFamily": 5,
|
||||
"text": "Node 0 RAM\nLocal Memory",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "Node 0 RAM\nLocal Memory",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "node-node1-ram",
|
||||
"type": "rectangle",
|
||||
"x": 885.0,
|
||||
"y": 35.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": 1430922658,
|
||||
"version": 1,
|
||||
"versionNonce": 633973815,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false
|
||||
},
|
||||
{
|
||||
"id": "node-label-node1-ram",
|
||||
"type": "text",
|
||||
"x": 895.0,
|
||||
"y": 45.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": 608261168,
|
||||
"version": 1,
|
||||
"versionNonce": 116561591,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"fontSize": 15,
|
||||
"fontFamily": 5,
|
||||
"text": "Node 1 RAM\nRemote Memory",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "Node 1 RAM\nRemote Memory",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
}
|
||||
],
|
||||
"appState": {
|
||||
"gridSize": 10,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"currentItemFontFamily": 5
|
||||
},
|
||||
"files": {}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"harness_version": "0.2.0",
|
||||
"spec_id": "numa-vcpu-and-memory-placement",
|
||||
"spec_version": "1.1",
|
||||
"spec_sha256": "afeb9cbc8d7063a1945cabb916eadab7d248a252fce57e433355e6c679090bd5",
|
||||
"source_context": {
|
||||
"document": "docs/virtualization/final/document.md",
|
||||
"document_sha256": "181e2b3cc8a45bae81e7e8193d026937c4d586ae4495d3b2c323eb7e6abcfadd",
|
||||
"anchor": {
|
||||
"kind": "heading",
|
||||
"value": "75. vCPU와 NUMA의 연결",
|
||||
"line": 3313
|
||||
}
|
||||
},
|
||||
"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.excalidraw",
|
||||
"numa-vcpu-and-memory-placement.alt.md"
|
||||
],
|
||||
"lint_issue_count": 0,
|
||||
"assumption_count": 0,
|
||||
"assumptions_allowed": false,
|
||||
"composition_profile": "sequence",
|
||||
"reference_ids": [
|
||||
"payment-approval-sequence"
|
||||
],
|
||||
"diagram_only": true
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
%% vCPU 를 Node 0 에 묶어도 backing 이 Node 1 에 있으면 접근은 remote 로 남는다
|
||||
%% question: vCPU pinning 만 한 배치와 memory binding 까지 맞춘 배치에서 같은 memory 접근은 각각 어느 node 의 RAM 에 닿는가
|
||||
sequenceDiagram
|
||||
participant n0 as QEMU vCPU Thread
|
||||
participant n1 as VM1 Memory Backing
|
||||
participant n2 as Node 0 CPU
|
||||
participant n3 as Node 0 RAM
|
||||
participant n4 as Node 1 RAM
|
||||
n0->>n2: vCPU Pinning
|
||||
n1->>n4: Memory Placement
|
||||
n2->>n4: Remote Access
|
||||
n1->>n3: Memory Binding
|
||||
n2->>n3: Local Access
|
||||
@@ -0,0 +1,93 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<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>{"techviz":{"spec_version":"1.1","id":"numa-vcpu-and-memory-placement","profile":"sequence"},"source_context":{"document":"docs/virtualization/final/document.md","document_sha256":"181e2b3cc8a45bae81e7e8193d026937c4d586ae4495d3b2c323eb7e6abcfadd","anchor":{"kind":"heading","value":"75. vCPU와 NUMA의 연결","line":3313}},"evidence_policy":"Each factual element cites source lines or is marked assumption.","diagram_only":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="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" />
|
||||
<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" />
|
||||
<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" />
|
||||
<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" />
|
||||
<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" />
|
||||
<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" />
|
||||
<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" />
|
||||
<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" />
|
||||
<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" />
|
||||
<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" />
|
||||
<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>
|
||||
|
After Width: | Height: | Size: 8.4 KiB |
@@ -0,0 +1,43 @@
|
||||
# control/setup path 는 QEMU 를 지나고 data path 는 backend 에 따라 QEMU 를 지나기도 안 지나기도 한다
|
||||
|
||||
## Alternative text
|
||||
|
||||
위쪽 줄은 virsh, libvirt, QEMU, virtio-net Device Model 로 이어지는 control/setup path 이고 아래쪽 줄은 Physical NIC 에서 TAP 까지 이어지는 data path 다. 두 줄이 vhost-net 과 QEMU virtio backend 에서 만나 virtqueue 를 지나 Guest 로 모이는 구성도.
|
||||
|
||||
## 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 를 쓰고 있는지는 그림이 말하지 않는다.
|
||||
|
||||
## Elements and evidence
|
||||
|
||||
- **virsh** (cli): VM/network 관리 CLI. packet datapath 에는 직접 참여하지 않는다. Evidence: L5662–L5662, L5687–L5689.
|
||||
- **libvirt** (service): VM lifecycle 과 NIC/network configuration 을 관리한다. Evidence: L5663–L5663, L5691–L5691.
|
||||
- **QEMU** (process): 실제로 실행되는 프로세스. 이 안에 virtio-net device model 이 있다. Evidence: L5666–L5666, L5693–L5693.
|
||||
- **virtio-net Device Model** (service): control/setup path 가 닿는 자리. 장치를 만들고 backend 를 연결한다. Evidence: L5666–L5666, L5695–L5700.
|
||||
- **Physical NIC** (hardware): 밖에서 온 packet 이 처음 닿는 하드웨어. Evidence: L5706–L5708.
|
||||
- **Physical NIC Driver** (driver): 그 하드웨어를 제어하는 Host kernel driver. Evidence: L5674–L5674, L5710–L5710.
|
||||
- **Linux Bridge /
|
||||
Routing / NAT** (network): L2 software switch 와 L3 routing, 주소 변환이 놓이는 자리. Evidence: L5672–L5673, L5712–L5712.
|
||||
- **TAP** (network): VM 의 Ethernet frame 과 Host Linux networking 을 잇는 접점. Evidence: L5671–L5671, L5714–L5714.
|
||||
- **vhost-net** (kernel): 반복되는 packet datapath 를 Host kernel 에서 처리하는 backend. Evidence: L5669–L5669, L5716–L5716.
|
||||
- **QEMU virtio backend** (process): vhost-net 을 쓰지 않을 때 datapath backend 를 맡는 쪽. Evidence: L5668–L5668, L5742–L5742.
|
||||
- **virtqueue** (queue): Guest 와 Host backend 가 I/O buffer 를 주고받는 descriptor 기반 공유 큐. Evidence: L5667–L5667, L5718–L5718.
|
||||
- **Guest** (service): packet 이 올라가 애플리케이션 socket 에 닿는 쪽. Evidence: L5664–L5665, L5720–L5726.
|
||||
|
||||
## Relationships
|
||||
|
||||
- **Linux Bridge /
|
||||
Routing / NAT → TAP:** Ethernet Frame. Evidence: L5671–L5671, L5712–L5714.
|
||||
- **Physical NIC Driver → Linux Bridge /
|
||||
Routing / NAT:** L2 forwarding. Evidence: L5672–L5672, L5710–L5712.
|
||||
- **libvirt → QEMU:** NIC/network configuration. Evidence: L5663–L5663, L5691–L5693.
|
||||
- **Physical NIC → Physical NIC Driver:** Ethernet Frame. Evidence: L5671–L5671, L5708–L5710.
|
||||
- **QEMU virtio backend → virtqueue:** Data Path. Evidence: L5729–L5729, L5742–L5744.
|
||||
- **QEMU → virtio-net Device Model:** QEMU process. Evidence: L5666–L5666, L5693–L5695.
|
||||
- **virtio-net Device Model → QEMU virtio backend:** control. Evidence: L5668–L5668, L5700–L5700.
|
||||
- **virtio-net Device Model → vhost-net:** vhost-net backend 설정. Evidence: L5700–L5700, L5716–L5716.
|
||||
- **TAP → QEMU virtio backend:** data. Evidence: L5729–L5729, L5740–L5742.
|
||||
- **TAP → vhost-net:** vhost-net 사용. Evidence: L5703–L5703, L5714–L5716.
|
||||
- **vhost-net → virtqueue:** Data Path. Evidence: L5703–L5703, L5716–L5718.
|
||||
- **virsh → libvirt:** Control / Setup. Evidence: L5662–L5663, L5684–L5691.
|
||||
- **virtqueue → Guest:** virtio-net Frontend Driver. Evidence: L5664–L5664, L5718–L5726.
|
||||
@@ -0,0 +1,52 @@
|
||||
# control/setup path 는 QEMU 를 지나고 data path 는 backend 에 따라 QEMU 를 지나기도 안 지나기도 한다
|
||||
# Question: 설정하는 경로와 packet 이 흐르는 경로는 어디에서 갈라지고, 그 두 갈래 가운데 어느 쪽이 QEMU 사용자 공간을 지나는가
|
||||
direction: down
|
||||
n0: "virsh" {
|
||||
shape: rectangle
|
||||
}
|
||||
n1: "libvirt" {
|
||||
shape: rectangle
|
||||
}
|
||||
n2: "QEMU" {
|
||||
shape: rectangle
|
||||
}
|
||||
n3: "virtio-net Device Model" {
|
||||
shape: rectangle
|
||||
}
|
||||
n4: "Physical NIC" {
|
||||
shape: rectangle
|
||||
}
|
||||
n5: "Physical NIC Driver" {
|
||||
shape: rectangle
|
||||
}
|
||||
n6: "Linux Bridge /\nRouting / NAT" {
|
||||
shape: rectangle
|
||||
}
|
||||
n7: "TAP" {
|
||||
shape: rectangle
|
||||
}
|
||||
n8: "vhost-net" {
|
||||
shape: rectangle
|
||||
}
|
||||
n9: "QEMU virtio backend" {
|
||||
shape: rectangle
|
||||
}
|
||||
n10: "virtqueue" {
|
||||
shape: queue
|
||||
}
|
||||
n11: "Guest" {
|
||||
shape: rectangle
|
||||
}
|
||||
n0 -> n1: "Control / Setup"
|
||||
n1 -> n2: "NIC/network configuration"
|
||||
n2 -> n3: "QEMU process"
|
||||
n4 -> n5: "Ethernet Frame"
|
||||
n5 -> n6: "L2 forwarding"
|
||||
n6 -> n7: "Ethernet Frame"
|
||||
n3 -> n8: "vhost-net backend 설정"
|
||||
n3 -> n9
|
||||
n7 -> n8: "vhost-net 사용"
|
||||
n7 -> n9
|
||||
n8 -> n10: "Data Path"
|
||||
n9 -> n10: "Data Path"
|
||||
n10 -> n11: "virtio-net Frontend Driver"
|
||||
@@ -0,0 +1,113 @@
|
||||
<?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">
|
||||
<root>
|
||||
<mxCell id="0"/>
|
||||
<mxCell id="1" parent="0"/>
|
||||
<mxCell id="n_virsh" value="virsh<br/>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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<mxCell id="n_qemu" value="QEMU<br/>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>
|
||||
<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"/>
|
||||
</mxCell>
|
||||
<mxCell id="n_virtio-net-device-model" value="virtio-net Device Model<br/>virtual NIC 생성<br/>feature negotiation<br/>virtqueue 설정<br/>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>
|
||||
<mxCell id="n_tap" value="TAP<br/>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>
|
||||
<mxCell id="n_qemu-virtio-backend" value="QEMU virtio backend<br/>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>
|
||||
<mxCell id="n_vhost-net" value="vhost-net<br/>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>
|
||||
<mxCell id="n_virtqueue" value="virtqueue<br/>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>
|
||||
<mxCell id="n_guest" value="Guest<br/>virtio-net Frontend Driver<br/>Guest TCP/IP Stack<br/>Socket<br/>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>
|
||||
<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"/>
|
||||
</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"/>
|
||||
</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">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="161.2" y="311.8" 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"/>
|
||||
</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"/>
|
||||
</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"/>
|
||||
</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"/>
|
||||
</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"/>
|
||||
</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"/>
|
||||
</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"/>
|
||||
</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"/>
|
||||
</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"/>
|
||||
</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">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="294.5" y="1081.0" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
</mxfile>
|
||||
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"harness_version": "0.2.0",
|
||||
"spec_id": "packet-control-and-data-paths",
|
||||
"spec_version": "1.1",
|
||||
"spec_sha256": "fa2d5e22352fa6cf975665cd431a236deaa4ae88ea0eb8461bb1b3c8b0b03dc6",
|
||||
"source_context": {
|
||||
"document": "docs/virtualization/final/document.md",
|
||||
"document_sha256": "181e2b3cc8a45bae81e7e8193d026937c4d586ae4495d3b2c323eb7e6abcfadd",
|
||||
"anchor": {
|
||||
"kind": "heading",
|
||||
"value": "125. 최종 기준 구조",
|
||||
"line": 5682
|
||||
}
|
||||
},
|
||||
"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.excalidraw",
|
||||
"packet-control-and-data-paths.alt.md"
|
||||
],
|
||||
"lint_issue_count": 4,
|
||||
"assumption_count": 0,
|
||||
"assumptions_allowed": false,
|
||||
"composition_profile": "component-flow",
|
||||
"reference_ids": [
|
||||
"payment-event-flow"
|
||||
],
|
||||
"diagram_only": true
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
%% control/setup path 는 QEMU 를 지나고 data path 는 backend 에 따라 QEMU 를 지나기도 안 지나기도 한다
|
||||
%% question: 설정하는 경로와 packet 이 흐르는 경로는 어디에서 갈라지고, 그 두 갈래 가운데 어느 쪽이 QEMU 사용자 공간을 지나는가
|
||||
flowchart TB
|
||||
n0["virsh"]
|
||||
n1["libvirt"]
|
||||
n2["QEMU"]
|
||||
n3["virtio-net Device Model"]
|
||||
n4["Physical NIC"]
|
||||
n5["Physical NIC Driver"]
|
||||
n6["Linux Bridge /<br/>Routing / NAT"]
|
||||
n7["TAP"]
|
||||
n8["vhost-net"]
|
||||
n9["QEMU virtio backend"]
|
||||
n10(["virtqueue"])
|
||||
n11["Guest"]
|
||||
n0 -->|"Control / Setup"| n1
|
||||
n1 -->|"NIC/network configuration"| n2
|
||||
n2 -->|"QEMU process"| n3
|
||||
n4 -->|"Ethernet Frame"| n5
|
||||
n5 -->|"L2 forwarding"| n6
|
||||
n6 -->|"Ethernet Frame"| n7
|
||||
n3 -->|"vhost-net backend 설정"| n8
|
||||
n3 --> n9
|
||||
n7 -->|"vhost-net 사용"| n8
|
||||
n7 --> n9
|
||||
n8 -->|"Data Path"| n10
|
||||
n9 -->|"Data Path"| n10
|
||||
n10 -->|"virtio-net Frontend Driver"| n11
|
||||
@@ -0,0 +1,160 @@
|
||||
<?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">
|
||||
<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>{"techviz":{"spec_version":"1.1","id":"packet-control-and-data-paths","profile":"component-flow"},"source_context":{"document":"docs/virtualization/final/document.md","document_sha256":"181e2b3cc8a45bae81e7e8193d026937c4d586ae4495d3b2c323eb7e6abcfadd","anchor":{"kind":"heading","value":"125. 최종 기준 구조","line":5682}},"evidence_policy":"Each factual element cites source lines or is marked assumption.","diagram_only":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="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>
|
||||
<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>
|
||||
</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>
|
||||
</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>
|
||||
</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>
|
||||
</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>
|
||||
</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>
|
||||
</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>
|
||||
</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>
|
||||
</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>
|
||||
</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>
|
||||
</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>
|
||||
</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>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 13 KiB |
@@ -0,0 +1,25 @@
|
||||
# 세 fault 는 변환 사슬의 서로 다른 단계에서 갈라져 나가 서로 다른 계층이 받는다
|
||||
|
||||
## Alternative text
|
||||
|
||||
Guest Page Table 과 EPT, QEMU 의 Guest RAM Backing 에서 각각 fault 가 갈라져 Guest Kernel · KVM · Host Kernel 로 가는 갈래도.
|
||||
|
||||
## 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 갈래와 선으로 이어져 있지 않은 것은 근거 문서가 둘을 잇는 문장을 두지 않았기 때문이다. 다섯 원인이 무엇인지와 두 사건을 항목별로 견주는 것은 본문의 표가 맡는다.
|
||||
|
||||
## Elements and evidence
|
||||
|
||||
- **Guest Page Table** (component): 첫 번째 translation 단계. 여기서 접근을 완료할 수 없으면 Guest Page Fault 가 난다. Evidence: L2443–L2445, L2484–L2485.
|
||||
- **Guest Kernel** (service): Guest Page Fault 를 받는 계층. Evidence: L2487–L2487, L2493–L2494.
|
||||
- **EPT** (component): 두 번째 translation 단계. Guest translation 이 성공한 뒤 여기서 막히면 EPT Violation 이 난다. Evidence: L2465–L2469, L2484–L2485.
|
||||
- **KVM** (service): EPT Violation 을 VM Exit 뒤에 받는 계층. Evidence: L2471–L2473, L2487–L2487.
|
||||
- **QEMU · Guest RAM Backing** (store): Host 의 일반 userspace 메모리로 관리되는 자리. demand allocation 이나 reclaim, swap 때문에 여기서 fault 가 난다. Evidence: L2504–L2504, L2514–L2519.
|
||||
- **Host Kernel** (service): Host Page Fault 를 받아 필요한 Host page 를 처리하는 계층. Evidence: L2521–L2525.
|
||||
|
||||
## Relationships
|
||||
|
||||
- **EPT → KVM:** EPT Violation · VM Exit. Evidence: L2467–L2473, L2487–L2487.
|
||||
- **Guest Page Table → Guest Kernel:** Guest Page Fault. Evidence: L2484–L2487, L2493–L2494.
|
||||
- **QEMU · Guest RAM Backing → Host Kernel:** Host Page Fault. Evidence: L2517–L2525.
|
||||
- **Guest Page Table → EPT:** GPA. Evidence: L2461–L2467.
|
||||
@@ -0,0 +1,25 @@
|
||||
# 세 fault 는 변환 사슬의 서로 다른 단계에서 갈라져 나가 서로 다른 계층이 받는다
|
||||
# Question: Guest Page Fault · EPT Violation · Host Page Fault 는 각각 어느 단계에서 갈라지고 누가 받는가
|
||||
direction: down
|
||||
n0: "Guest Page Table" {
|
||||
shape: rectangle
|
||||
}
|
||||
n1: "Guest Kernel" {
|
||||
shape: rectangle
|
||||
}
|
||||
n2: "EPT" {
|
||||
shape: rectangle
|
||||
}
|
||||
n3: "KVM" {
|
||||
shape: rectangle
|
||||
}
|
||||
n4: "QEMU · Guest RAM Backing" {
|
||||
shape: rectangle
|
||||
}
|
||||
n5: "Host Kernel" {
|
||||
shape: rectangle
|
||||
}
|
||||
n0 -> n1: "Guest Page Fault" { 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 }
|
||||
@@ -0,0 +1,49 @@
|
||||
<?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">
|
||||
<root>
|
||||
<mxCell id="0"/>
|
||||
<mxCell id="1" parent="0"/>
|
||||
<mxCell id="n_guest-page-table" value="Guest Page Table<br/>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>
|
||||
<mxCell id="n_qemu-backing" value="QEMU · Guest RAM Backing<br/>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>
|
||||
<mxCell id="n_ept" value="EPT<br/>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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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"/>
|
||||
</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">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="302.8" y="167.8" 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">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="512.5" y="172.8" 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"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
</mxfile>
|
||||
@@ -0,0 +1,823 @@
|
||||
{
|
||||
"type": "excalidraw",
|
||||
"version": 2,
|
||||
"source": "techviz-harness",
|
||||
"elements": [
|
||||
{
|
||||
"id": "edge-ept-violation-branch",
|
||||
"type": "arrow",
|
||||
"x": 145.0,
|
||||
"y": 318.0,
|
||||
"width": 210.0,
|
||||
"height": 96.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"strokeStyle": "dashed",
|
||||
"roughness": 1,
|
||||
"opacity": 100,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"index": null,
|
||||
"roundness": null,
|
||||
"seed": 1813435601,
|
||||
"version": 1,
|
||||
"versionNonce": 1356316492,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"points": [
|
||||
[
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
],
|
||||
[
|
||||
210.0,
|
||||
48.0
|
||||
],
|
||||
[
|
||||
210.0,
|
||||
96.0
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
"startBinding": {
|
||||
"elementId": "node-ept",
|
||||
"focus": 0,
|
||||
"gap": 4
|
||||
},
|
||||
"endBinding": {
|
||||
"elementId": "node-kvm",
|
||||
"focus": 0,
|
||||
"gap": 4
|
||||
},
|
||||
"startArrowhead": null,
|
||||
"endArrowhead": "arrow",
|
||||
"elbowed": true
|
||||
},
|
||||
{
|
||||
"id": "edge-label-ept-violation-branch",
|
||||
"type": "text",
|
||||
"x": 158.0,
|
||||
"y": 326.0,
|
||||
"width": 184,
|
||||
"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": 126213589,
|
||||
"version": 1,
|
||||
"versionNonce": 1594262405,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"fontSize": 13,
|
||||
"fontFamily": 5,
|
||||
"text": "EPT Violation · VM Exit",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "EPT Violation · VM Exit",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "edge-guest-fault-branch",
|
||||
"type": "arrow",
|
||||
"x": 250.5,
|
||||
"y": 141.0,
|
||||
"width": 104.5,
|
||||
"height": 109.5,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"strokeStyle": "dashed",
|
||||
"roughness": 1,
|
||||
"opacity": 100,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"index": null,
|
||||
"roundness": null,
|
||||
"seed": 270453869,
|
||||
"version": 1,
|
||||
"versionNonce": 1761730436,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"points": [
|
||||
[
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
54.75
|
||||
],
|
||||
[
|
||||
104.5,
|
||||
54.75
|
||||
],
|
||||
[
|
||||
104.5,
|
||||
109.5
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
"startBinding": {
|
||||
"elementId": "node-guest-page-table",
|
||||
"focus": 0,
|
||||
"gap": 4
|
||||
},
|
||||
"endBinding": {
|
||||
"elementId": "node-guest-kernel",
|
||||
"focus": 0,
|
||||
"gap": 4
|
||||
},
|
||||
"startArrowhead": null,
|
||||
"endArrowhead": "arrow",
|
||||
"elbowed": true
|
||||
},
|
||||
{
|
||||
"id": "edge-label-guest-fault-branch",
|
||||
"type": "text",
|
||||
"x": 238.75,
|
||||
"y": 155.75,
|
||||
"width": 128,
|
||||
"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": 651157700,
|
||||
"version": 1,
|
||||
"versionNonce": 1730090925,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"fontSize": 13,
|
||||
"fontFamily": 5,
|
||||
"text": "Guest Page Fault",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "Guest Page Fault",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "edge-host-fault-branch",
|
||||
"type": "arrow",
|
||||
"x": 460.0,
|
||||
"y": 151.0,
|
||||
"width": 105.0,
|
||||
"height": 99.5,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"strokeStyle": "dashed",
|
||||
"roughness": 1,
|
||||
"opacity": 100,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"index": null,
|
||||
"roundness": null,
|
||||
"seed": 361491347,
|
||||
"version": 1,
|
||||
"versionNonce": 164584873,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"points": [
|
||||
[
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
49.75
|
||||
],
|
||||
[
|
||||
105.0,
|
||||
49.75
|
||||
],
|
||||
[
|
||||
105.0,
|
||||
99.5
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
"startBinding": {
|
||||
"elementId": "node-qemu-backing",
|
||||
"focus": 0,
|
||||
"gap": 4
|
||||
},
|
||||
"endBinding": {
|
||||
"elementId": "node-host-kernel",
|
||||
"focus": 0,
|
||||
"gap": 4
|
||||
},
|
||||
"startArrowhead": null,
|
||||
"endArrowhead": "arrow",
|
||||
"elbowed": true
|
||||
},
|
||||
{
|
||||
"id": "edge-label-host-fault-branch",
|
||||
"type": "text",
|
||||
"x": 452.5,
|
||||
"y": 160.75,
|
||||
"width": 120,
|
||||
"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": 392218398,
|
||||
"version": 1,
|
||||
"versionNonce": 343368682,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"fontSize": 13,
|
||||
"fontFamily": 5,
|
||||
"text": "Host Page Fault",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "Host Page Fault",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "edge-translation-continues",
|
||||
"type": "arrow",
|
||||
"x": 145.0,
|
||||
"y": 141.0,
|
||||
"width": 87.5,
|
||||
"height": 106.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": 468653972,
|
||||
"version": 1,
|
||||
"versionNonce": 27222128,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"points": [
|
||||
[
|
||||
87.5,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
87.5,
|
||||
53.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
53.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
106.0
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
"startBinding": {
|
||||
"elementId": "node-guest-page-table",
|
||||
"focus": 0,
|
||||
"gap": 4
|
||||
},
|
||||
"endBinding": {
|
||||
"elementId": "node-ept",
|
||||
"focus": 0,
|
||||
"gap": 4
|
||||
},
|
||||
"startArrowhead": null,
|
||||
"endArrowhead": "arrow",
|
||||
"elbowed": true
|
||||
},
|
||||
{
|
||||
"id": "edge-label-translation-continues",
|
||||
"type": "text",
|
||||
"x": 143.75,
|
||||
"y": 154.0,
|
||||
"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": 1755263566,
|
||||
"version": 1,
|
||||
"versionNonce": 1161066356,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"fontSize": 13,
|
||||
"fontFamily": 5,
|
||||
"text": "GPA",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "GPA",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "node-guest-page-table",
|
||||
"type": "rectangle",
|
||||
"x": 166.5,
|
||||
"y": 70.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": 858382528,
|
||||
"version": 1,
|
||||
"versionNonce": 1649692634,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false
|
||||
},
|
||||
{
|
||||
"id": "node-label-guest-page-table",
|
||||
"type": "text",
|
||||
"x": 176.5,
|
||||
"y": 80.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": 1187226205,
|
||||
"version": 1,
|
||||
"versionNonce": 1771047591,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"fontSize": 15,
|
||||
"fontFamily": 5,
|
||||
"text": "Guest Page Table\nGVA → GPA",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "Guest Page Table\nGVA → GPA",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "node-qemu-backing",
|
||||
"type": "rectangle",
|
||||
"x": 376.5,
|
||||
"y": 60.0,
|
||||
"width": 167.0,
|
||||
"height": 91.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": 245780206,
|
||||
"version": 1,
|
||||
"versionNonce": 1054758503,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false
|
||||
},
|
||||
{
|
||||
"id": "node-label-qemu-backing",
|
||||
"type": "text",
|
||||
"x": 386.5,
|
||||
"y": 70.0,
|
||||
"width": 147.0,
|
||||
"height": 71.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": 470938543,
|
||||
"version": 1,
|
||||
"versionNonce": 1389218466,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"fontSize": 15,
|
||||
"fontFamily": 5,
|
||||
"text": "QEMU · Guest RAM Backing\nHost Virtual Memory",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "QEMU · Guest RAM Backing\nHost Virtual Memory",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "node-ept",
|
||||
"type": "rectangle",
|
||||
"x": 70.0,
|
||||
"y": 247.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": 1508191037,
|
||||
"version": 1,
|
||||
"versionNonce": 1352757918,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false
|
||||
},
|
||||
{
|
||||
"id": "node-label-ept",
|
||||
"type": "text",
|
||||
"x": 80.0,
|
||||
"y": 257.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": 243376460,
|
||||
"version": 1,
|
||||
"versionNonce": 599206536,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"fontSize": 15,
|
||||
"fontFamily": 5,
|
||||
"text": "EPT\nGPA → HPA",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "EPT\nGPA → HPA",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "node-guest-kernel",
|
||||
"type": "rectangle",
|
||||
"x": 280.0,
|
||||
"y": 250.5,
|
||||
"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": 1858630489,
|
||||
"version": 1,
|
||||
"versionNonce": 1370487869,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false
|
||||
},
|
||||
{
|
||||
"id": "node-label-guest-kernel",
|
||||
"type": "text",
|
||||
"x": 290.0,
|
||||
"y": 260.5,
|
||||
"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": 199697629,
|
||||
"version": 1,
|
||||
"versionNonce": 1282544489,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"fontSize": 15,
|
||||
"fontFamily": 5,
|
||||
"text": "Guest Kernel",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "Guest Kernel",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "node-host-kernel",
|
||||
"type": "rectangle",
|
||||
"x": 490.0,
|
||||
"y": 250.5,
|
||||
"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": 335259087,
|
||||
"version": 1,
|
||||
"versionNonce": 753872004,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false
|
||||
},
|
||||
{
|
||||
"id": "node-label-host-kernel",
|
||||
"type": "text",
|
||||
"x": 500.0,
|
||||
"y": 260.5,
|
||||
"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": 608596992,
|
||||
"version": 1,
|
||||
"versionNonce": 1964329339,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"fontSize": 15,
|
||||
"fontFamily": 5,
|
||||
"text": "Host Kernel",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "Host Kernel",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "node-kvm",
|
||||
"type": "rectangle",
|
||||
"x": 280.0,
|
||||
"y": 414.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": 1294725949,
|
||||
"version": 1,
|
||||
"versionNonce": 774205501,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false
|
||||
},
|
||||
{
|
||||
"id": "node-label-kvm",
|
||||
"type": "text",
|
||||
"x": 290.0,
|
||||
"y": 424.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": 820201432,
|
||||
"version": 1,
|
||||
"versionNonce": 242634004,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"fontSize": 15,
|
||||
"fontFamily": 5,
|
||||
"text": "KVM",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "KVM",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
}
|
||||
],
|
||||
"appState": {
|
||||
"gridSize": 10,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"currentItemFontFamily": 5
|
||||
},
|
||||
"files": {}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"harness_version": "0.2.0",
|
||||
"spec_id": "page-fault-layers-in-a-vm",
|
||||
"spec_version": "1.1",
|
||||
"spec_sha256": "1db9b8029b08c8d4c61b3f01119f121b394b7d0efb9818cb63793d65b858d667",
|
||||
"source_context": {
|
||||
"document": "docs/virtualization/final/document.md",
|
||||
"document_sha256": "181e2b3cc8a45bae81e7e8193d026937c4d586ae4495d3b2c323eb7e6abcfadd",
|
||||
"anchor": {
|
||||
"kind": "heading",
|
||||
"value": "48. Guest Page Fault와 EPT Violation 비교",
|
||||
"line": 2480
|
||||
}
|
||||
},
|
||||
"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.excalidraw",
|
||||
"page-fault-layers-in-a-vm.alt.md"
|
||||
],
|
||||
"lint_issue_count": 0,
|
||||
"assumption_count": 0,
|
||||
"assumptions_allowed": false,
|
||||
"composition_profile": "component-flow",
|
||||
"reference_ids": [
|
||||
"payment-event-flow"
|
||||
],
|
||||
"diagram_only": true
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
%% 세 fault 는 변환 사슬의 서로 다른 단계에서 갈라져 나가 서로 다른 계층이 받는다
|
||||
%% question: Guest Page Fault · EPT Violation · Host Page Fault 는 각각 어느 단계에서 갈라지고 누가 받는가
|
||||
flowchart TB
|
||||
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 -->|"GPA"| n2
|
||||
n2 -.->|"EPT Violation · VM Exit"| n3
|
||||
n4 -.->|"Host Page Fault"| n5
|
||||
@@ -0,0 +1,96 @@
|
||||
<?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">
|
||||
<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>{"techviz":{"spec_version":"1.1","id":"page-fault-layers-in-a-vm","profile":"component-flow"},"source_context":{"document":"docs/virtualization/final/document.md","document_sha256":"181e2b3cc8a45bae81e7e8193d026937c4d586ae4495d3b2c323eb7e6abcfadd","anchor":{"kind":"heading","value":"48. Guest Page Fault와 EPT Violation 비교","line":2480}},"evidence_policy":"Each factual element cites source lines or is marked assumption.","diagram_only":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="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>
|
||||
<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>
|
||||
</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>
|
||||
</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>
|
||||
</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>
|
||||
</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>
|
||||
</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>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 8.1 KiB |
@@ -0,0 +1,33 @@
|
||||
# balloon target 은 VM Boundary 를 건너 driver 에 닿고 확보한 page 는 같은 경계를 되건너 온다
|
||||
|
||||
## Alternative text
|
||||
|
||||
virtio-balloon Driver, virtqueue, QEMU virtio-balloon Device, Host Memory Management 네 참여자 사이의 순서도. 앞 세 메시지는 오른쪽에서 왼쪽으로 Guest 안으로 들어가고 뒤 세 메시지는 왼쪽에서 오른쪽으로 Host 로 돌아온다.
|
||||
|
||||
## 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 가 어느 쪽으로 움직이는지는 본문의 표가 맡는다.
|
||||
|
||||
## Elements and evidence
|
||||
|
||||
- **virtio-balloon Driver** (driver): Guest kernel 쪽 balloon driver. target 을 받아 Guest page 를 확보한다. Evidence: L3013–L3015, L3046–L3046.
|
||||
- **virtqueue** (queue): Guest 와 QEMU 가 balloon 요청을 주고받는 공유 큐. VM Boundary 바로 안쪽이다. Evidence: L3017–L3017.
|
||||
- **QEMU virtio-balloon
|
||||
Device** (service): Host 쪽 balloon device. 경계를 넘어온 내용을 Host memory management 로 넘긴다. Evidence: L3022–L3022, L3037–L3041.
|
||||
- **Host Memory
|
||||
Management** (service): balloon target 을 정하는 쪽이자 회수 기회가 생기는 자리. Evidence: L3025–L3025, L3034–L3037.
|
||||
|
||||
## Relationships
|
||||
|
||||
- **Host Memory
|
||||
Management → QEMU virtio-balloon
|
||||
Device:** Balloon target 조정. Evidence: L3034–L3041.
|
||||
- **QEMU virtio-balloon
|
||||
Device → virtqueue:** VM Boundary. Evidence: L3017–L3022, L3041–L3046.
|
||||
- **virtqueue → virtio-balloon Driver:** Balloon target. Evidence: L3013–L3017, L3043–L3046.
|
||||
- **virtio-balloon Driver → virtqueue:** Guest pages 확보. Evidence: L3046–L3050.
|
||||
- **virtqueue → QEMU virtio-balloon
|
||||
Device:** VM Boundary. Evidence: L3017–L3025.
|
||||
- **QEMU virtio-balloon
|
||||
Device → Host Memory
|
||||
Management:** 회수 가능 backing. Evidence: L3022–L3028, L3077–L3079.
|
||||
@@ -0,0 +1,21 @@
|
||||
# balloon target 은 VM Boundary 를 건너 driver 에 닿고 확보한 page 는 같은 경계를 되건너 온다
|
||||
# Question: Host 가 balloon target 을 바꾸면 그 요청은 어떤 순서로 어느 경계를 건너 Guest 안의 driver 까지 가고, 확보된 page 는 어떻게 Host 로 돌아오는가
|
||||
direction: right
|
||||
n0: "virtio-balloon Driver" {
|
||||
shape: rectangle
|
||||
}
|
||||
n1: "virtqueue" {
|
||||
shape: queue
|
||||
}
|
||||
n2: "QEMU virtio-balloon\nDevice" {
|
||||
shape: rectangle
|
||||
}
|
||||
n3: "Host Memory\nManagement" {
|
||||
shape: rectangle
|
||||
}
|
||||
n3 -> n2: "Balloon target 조정"
|
||||
n2 -> n1: "VM Boundary"
|
||||
n1 -> n0: "Balloon target"
|
||||
n0 -> n1: "Guest pages 확보"
|
||||
n1 -> n2: "VM Boundary"
|
||||
n2 -> n3: "회수 가능 backing"
|
||||
@@ -0,0 +1,55 @@
|
||||
<?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="virtio-balloon-inflate-deflate" name="balloon target 은 VM Boundary 를 건너 driver 에 닿고 확보한 page 는 같은 경계를 되건너 온다">
|
||||
<mxGraphModel dx="870" dy="542" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="870" pageHeight="1169" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0"/>
|
||||
<mxCell id="1" parent="0"/>
|
||||
<mxCell id="n_balloon-driver" value="virtio-balloon Driver<br/>Guest Kernel" tooltip="Guest kernel 쪽 balloon driver. target 을 받아 Guest page 를 확보한다. | Evidence: L3013-L3015, L3046-L3046" 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="181.0" height="71.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="n_virtqueue" value="virtqueue" tooltip="Guest 와 QEMU 가 balloon 요청을 주고받는 공유 큐. VM Boundary 바로 안쪽이다. | Evidence: L3017-L3017" 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="150.0" height="64.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="n_qemu-balloon-device" value="QEMU virtio-balloon
|
||||
Device" tooltip="Host 쪽 balloon device. 경계를 넘어온 내용을 Host memory management 로 넘긴다. | Evidence: L3022-L3022, L3037-L3041" 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="465.0" y="35.0" width="167.0" height="74.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="n_host-memory-management" value="Host Memory
|
||||
Management" tooltip="balloon target 을 정하는 쪽이자 회수 기회가 생기는 자리. | Evidence: L3025-L3025, L3034-L3037" 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="74.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e_target-adjust" value="Balloon target 조정" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_host-memory-management" target="n_qemu-balloon-device">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="649.2" y="128.0" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="e_target-crosses-in" 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_qemu-balloon-device" target="n_virtqueue">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="439.2" y="190.0" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="e_target-to-driver" value="Balloon target" 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_balloon-driver">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="232.8" y="252.0" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="e_pages-secured" value="Guest pages 확보" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_balloon-driver" target="n_virtqueue">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="232.8" y="314.0" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="e_pages-cross-out" 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_virtqueue" target="n_qemu-balloon-device">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="439.2" y="376.0" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="e_reclaimable-backing" value="회수 가능 backing" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_qemu-balloon-device" target="n_host-memory-management">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="649.2" y="438.0" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
</mxfile>
|
||||
@@ -0,0 +1,835 @@
|
||||
{
|
||||
"type": "excalidraw",
|
||||
"version": 2,
|
||||
"source": "techviz-harness",
|
||||
"elements": [
|
||||
{
|
||||
"id": "edge-target-adjust",
|
||||
"type": "arrow",
|
||||
"x": 548.5,
|
||||
"y": 140.0,
|
||||
"width": 201.5,
|
||||
"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": 737020640,
|
||||
"version": 1,
|
||||
"versionNonce": 1525160168,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"points": [
|
||||
[
|
||||
201.5,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
0.0
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
"startBinding": {
|
||||
"elementId": "node-host-memory-management",
|
||||
"focus": 0,
|
||||
"gap": 4
|
||||
},
|
||||
"endBinding": {
|
||||
"elementId": "node-qemu-balloon-device",
|
||||
"focus": 0,
|
||||
"gap": 4
|
||||
},
|
||||
"startArrowhead": null,
|
||||
"endArrowhead": "arrow",
|
||||
"elbowed": true
|
||||
},
|
||||
{
|
||||
"id": "edge-label-target-adjust",
|
||||
"type": "text",
|
||||
"x": 581.25,
|
||||
"y": 116.0,
|
||||
"width": 136,
|
||||
"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": 453083568,
|
||||
"version": 1,
|
||||
"versionNonce": 1469276324,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"fontSize": 13,
|
||||
"fontFamily": 5,
|
||||
"text": "Balloon target 조정",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "Balloon target 조정",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "edge-target-crosses-in",
|
||||
"type": "arrow",
|
||||
"x": 330.0,
|
||||
"y": 202.0,
|
||||
"width": 218.5,
|
||||
"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": 1982038005,
|
||||
"version": 1,
|
||||
"versionNonce": 61702621,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"points": [
|
||||
[
|
||||
218.5,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
0.0
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
"startBinding": {
|
||||
"elementId": "node-qemu-balloon-device",
|
||||
"focus": 0,
|
||||
"gap": 4
|
||||
},
|
||||
"endBinding": {
|
||||
"elementId": "node-virtqueue",
|
||||
"focus": 0,
|
||||
"gap": 4
|
||||
},
|
||||
"startArrowhead": null,
|
||||
"endArrowhead": "arrow",
|
||||
"elbowed": true
|
||||
},
|
||||
{
|
||||
"id": "edge-label-target-crosses-in",
|
||||
"type": "text",
|
||||
"x": 394.25,
|
||||
"y": 178.0,
|
||||
"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": 777212613,
|
||||
"version": 1,
|
||||
"versionNonce": 349365301,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"fontSize": 13,
|
||||
"fontFamily": 5,
|
||||
"text": "VM Boundary",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "VM Boundary",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "edge-target-to-driver",
|
||||
"type": "arrow",
|
||||
"x": 135.5,
|
||||
"y": 264.0,
|
||||
"width": 194.5,
|
||||
"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": 135515254,
|
||||
"version": 1,
|
||||
"versionNonce": 613652303,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"points": [
|
||||
[
|
||||
194.5,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
0.0
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
"startBinding": {
|
||||
"elementId": "node-virtqueue",
|
||||
"focus": 0,
|
||||
"gap": 4
|
||||
},
|
||||
"endBinding": {
|
||||
"elementId": "node-balloon-driver",
|
||||
"focus": 0,
|
||||
"gap": 4
|
||||
},
|
||||
"startArrowhead": null,
|
||||
"endArrowhead": "arrow",
|
||||
"elbowed": true
|
||||
},
|
||||
{
|
||||
"id": "edge-label-target-to-driver",
|
||||
"type": "text",
|
||||
"x": 176.75,
|
||||
"y": 240.0,
|
||||
"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": 1817118777,
|
||||
"version": 1,
|
||||
"versionNonce": 19943219,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"fontSize": 13,
|
||||
"fontFamily": 5,
|
||||
"text": "Balloon target",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "Balloon target",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "edge-pages-secured",
|
||||
"type": "arrow",
|
||||
"x": 135.5,
|
||||
"y": 326.0,
|
||||
"width": 194.5,
|
||||
"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": 107373777,
|
||||
"version": 1,
|
||||
"versionNonce": 1878244752,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"points": [
|
||||
[
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
194.5,
|
||||
0.0
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
"startBinding": {
|
||||
"elementId": "node-balloon-driver",
|
||||
"focus": 0,
|
||||
"gap": 4
|
||||
},
|
||||
"endBinding": {
|
||||
"elementId": "node-virtqueue",
|
||||
"focus": 0,
|
||||
"gap": 4
|
||||
},
|
||||
"startArrowhead": null,
|
||||
"endArrowhead": "arrow",
|
||||
"elbowed": true
|
||||
},
|
||||
{
|
||||
"id": "edge-label-pages-secured",
|
||||
"type": "text",
|
||||
"x": 176.75,
|
||||
"y": 302.0,
|
||||
"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": 695343754,
|
||||
"version": 1,
|
||||
"versionNonce": 1171214433,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"fontSize": 13,
|
||||
"fontFamily": 5,
|
||||
"text": "Guest pages 확보",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "Guest pages 확보",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "edge-pages-cross-out",
|
||||
"type": "arrow",
|
||||
"x": 330.0,
|
||||
"y": 388.0,
|
||||
"width": 218.5,
|
||||
"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": 1480907426,
|
||||
"version": 1,
|
||||
"versionNonce": 1830405205,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"points": [
|
||||
[
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
218.5,
|
||||
0.0
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
"startBinding": {
|
||||
"elementId": "node-virtqueue",
|
||||
"focus": 0,
|
||||
"gap": 4
|
||||
},
|
||||
"endBinding": {
|
||||
"elementId": "node-qemu-balloon-device",
|
||||
"focus": 0,
|
||||
"gap": 4
|
||||
},
|
||||
"startArrowhead": null,
|
||||
"endArrowhead": "arrow",
|
||||
"elbowed": true
|
||||
},
|
||||
{
|
||||
"id": "edge-label-pages-cross-out",
|
||||
"type": "text",
|
||||
"x": 394.25,
|
||||
"y": 364.0,
|
||||
"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": 19536809,
|
||||
"version": 1,
|
||||
"versionNonce": 1120594763,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"fontSize": 13,
|
||||
"fontFamily": 5,
|
||||
"text": "VM Boundary",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "VM Boundary",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "edge-reclaimable-backing",
|
||||
"type": "arrow",
|
||||
"x": 548.5,
|
||||
"y": 450.0,
|
||||
"width": 201.5,
|
||||
"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": 289901807,
|
||||
"version": 1,
|
||||
"versionNonce": 383977982,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"points": [
|
||||
[
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
201.5,
|
||||
0.0
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
"startBinding": {
|
||||
"elementId": "node-qemu-balloon-device",
|
||||
"focus": 0,
|
||||
"gap": 4
|
||||
},
|
||||
"endBinding": {
|
||||
"elementId": "node-host-memory-management",
|
||||
"focus": 0,
|
||||
"gap": 4
|
||||
},
|
||||
"startArrowhead": null,
|
||||
"endArrowhead": "arrow",
|
||||
"elbowed": true
|
||||
},
|
||||
{
|
||||
"id": "edge-label-reclaimable-backing",
|
||||
"type": "text",
|
||||
"x": 597.25,
|
||||
"y": 426.0,
|
||||
"width": 104,
|
||||
"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": 617340894,
|
||||
"version": 1,
|
||||
"versionNonce": 525555255,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"fontSize": 13,
|
||||
"fontFamily": 5,
|
||||
"text": "회수 가능 backing",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "회수 가능 backing",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "node-balloon-driver",
|
||||
"type": "rectangle",
|
||||
"x": 45.0,
|
||||
"y": 35.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": 1402975843,
|
||||
"version": 1,
|
||||
"versionNonce": 162304747,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false
|
||||
},
|
||||
{
|
||||
"id": "node-label-balloon-driver",
|
||||
"type": "text",
|
||||
"x": 55.0,
|
||||
"y": 45.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": 218037921,
|
||||
"version": 1,
|
||||
"versionNonce": 1781698181,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"fontSize": 15,
|
||||
"fontFamily": 5,
|
||||
"text": "virtio-balloon Driver\nGuest Kernel",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "virtio-balloon Driver\nGuest Kernel",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "node-virtqueue",
|
||||
"type": "rectangle",
|
||||
"x": 255.0,
|
||||
"y": 35.0,
|
||||
"width": 150.0,
|
||||
"height": 64.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "#f3f0ff",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 1,
|
||||
"opacity": 100,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"index": null,
|
||||
"roundness": {
|
||||
"type": 3
|
||||
},
|
||||
"seed": 1427608734,
|
||||
"version": 1,
|
||||
"versionNonce": 104915312,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false
|
||||
},
|
||||
{
|
||||
"id": "node-label-virtqueue",
|
||||
"type": "text",
|
||||
"x": 265.0,
|
||||
"y": 45.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": 1863917934,
|
||||
"version": 1,
|
||||
"versionNonce": 579283696,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"fontSize": 15,
|
||||
"fontFamily": 5,
|
||||
"text": "virtqueue",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "virtqueue",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "node-qemu-balloon-device",
|
||||
"type": "rectangle",
|
||||
"x": 465.0,
|
||||
"y": 35.0,
|
||||
"width": 167.0,
|
||||
"height": 74.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": 823854416,
|
||||
"version": 1,
|
||||
"versionNonce": 1128635888,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false
|
||||
},
|
||||
{
|
||||
"id": "node-label-qemu-balloon-device",
|
||||
"type": "text",
|
||||
"x": 475.0,
|
||||
"y": 45.0,
|
||||
"width": 147.0,
|
||||
"height": 54.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": 560943017,
|
||||
"version": 1,
|
||||
"versionNonce": 818393145,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"fontSize": 15,
|
||||
"fontFamily": 5,
|
||||
"text": "QEMU virtio-balloon\nDevice",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "QEMU virtio-balloon\nDevice",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "node-host-memory-management",
|
||||
"type": "rectangle",
|
||||
"x": 675.0,
|
||||
"y": 35.0,
|
||||
"width": 150.0,
|
||||
"height": 74.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": 765979520,
|
||||
"version": 1,
|
||||
"versionNonce": 934288555,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false
|
||||
},
|
||||
{
|
||||
"id": "node-label-host-memory-management",
|
||||
"type": "text",
|
||||
"x": 685.0,
|
||||
"y": 45.0,
|
||||
"width": 130.0,
|
||||
"height": 54.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": 1363069112,
|
||||
"version": 1,
|
||||
"versionNonce": 234953733,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"fontSize": 15,
|
||||
"fontFamily": 5,
|
||||
"text": "Host Memory\nManagement",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "Host Memory\nManagement",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
}
|
||||
],
|
||||
"appState": {
|
||||
"gridSize": 10,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"currentItemFontFamily": 5
|
||||
},
|
||||
"files": {}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"harness_version": "0.2.0",
|
||||
"spec_id": "virtio-balloon-inflate-deflate",
|
||||
"spec_version": "1.1",
|
||||
"spec_sha256": "1a53fd7e3e5cf5b553be00d6c12f47f8493d302f9a79f660d29e1d85490e61de",
|
||||
"source_context": {
|
||||
"document": "docs/virtualization/final/document.md",
|
||||
"document_sha256": "181e2b3cc8a45bae81e7e8193d026937c4d586ae4495d3b2c323eb7e6abcfadd",
|
||||
"anchor": {
|
||||
"kind": "heading",
|
||||
"value": "65. virtio-balloon 구조",
|
||||
"line": 3008
|
||||
}
|
||||
},
|
||||
"outputs": [
|
||||
"virtio-balloon-inflate-deflate.svg",
|
||||
"virtio-balloon-inflate-deflate.drawio",
|
||||
"virtio-balloon-inflate-deflate.mmd",
|
||||
"virtio-balloon-inflate-deflate.d2",
|
||||
"virtio-balloon-inflate-deflate.excalidraw",
|
||||
"virtio-balloon-inflate-deflate.alt.md"
|
||||
],
|
||||
"lint_issue_count": 0,
|
||||
"assumption_count": 0,
|
||||
"assumptions_allowed": false,
|
||||
"composition_profile": "sequence",
|
||||
"reference_ids": [
|
||||
"payment-approval-sequence"
|
||||
],
|
||||
"diagram_only": true
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
%% balloon target 은 VM Boundary 를 건너 driver 에 닿고 확보한 page 는 같은 경계를 되건너 온다
|
||||
%% question: Host 가 balloon target 을 바꾸면 그 요청은 어떤 순서로 어느 경계를 건너 Guest 안의 driver 까지 가고, 확보된 page 는 어떻게 Host 로 돌아오는가
|
||||
sequenceDiagram
|
||||
participant n0 as virtio-balloon Driver
|
||||
participant n1 as virtqueue
|
||||
participant n2 as QEMU virtio-balloon<br/>Device
|
||||
participant n3 as Host Memory<br/>Management
|
||||
n3->>n2: Balloon target 조정
|
||||
n2->>n1: VM Boundary
|
||||
n1->>n0: Balloon target
|
||||
n0->>n1: Guest pages 확보
|
||||
n1->>n2: VM Boundary
|
||||
n2->>n3: 회수 가능 backing
|
||||
@@ -0,0 +1,87 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="870" height="542" viewBox="0 0 870 542" role="img" aria-labelledby="diagram-title diagram-description">
|
||||
<title id="diagram-title">balloon target 은 VM Boundary 를 건너 driver 에 닿고 확보한 page 는 같은 경계를 되건너 온다</title>
|
||||
<desc id="diagram-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 가 어느 쪽으로 움직이는지는 본문의 표가 맡는다.</desc>
|
||||
<metadata>{"techviz":{"spec_version":"1.1","id":"virtio-balloon-inflate-deflate","profile":"sequence"},"source_context":{"document":"docs/virtualization/final/document.md","document_sha256":"181e2b3cc8a45bae81e7e8193d026937c4d586ae4495d3b2c323eb7e6abcfadd","anchor":{"kind":"heading","value":"65. virtio-balloon 구조","line":3008}},"evidence_policy":"Each factual element cites source lines or is marked assumption.","diagram_only":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="870" height="542" />
|
||||
<rect class="node-shape kind-driver emphasis-primary role-participant" data-evidence="3013-3015,3046-3046" x="45.0" y="35.0" width="181.0" height="71.0" rx="7" />
|
||||
<text class="node-label" x="135.5" y="62.0">virtio-balloon Driver</text>
|
||||
<line class="node-detail-divider" x1="59.0" y1="83.0" x2="212.0" y2="83.0" />
|
||||
<text class="node-detail" x="61.0" y="100.0">Guest Kernel</text>
|
||||
<line class="lifeline" x1="135.5" y1="106.0" x2="135.5" y2="512.0" />
|
||||
<rect class="node-shape kind-queue emphasis-normal role-participant" data-evidence="3017-3017" x="255.0" y="35.0" width="150.0" height="64.0" rx="7" />
|
||||
<text class="node-label" x="330.0" y="65.0">virtqueue</text>
|
||||
<line class="lifeline" x1="330.0" y1="99.0" x2="330.0" y2="512.0" />
|
||||
<rect class="node-shape kind-service emphasis-normal role-participant" data-evidence="3022-3022,3037-3041" x="465.0" y="35.0" width="167.0" height="74.0" rx="7" />
|
||||
<text class="node-label" x="548.5" y="62.0">QEMU virtio-balloon</text>
|
||||
<text class="node-label" x="548.5" y="80.0">Device</text>
|
||||
<line class="lifeline" x1="548.5" y1="109.0" x2="548.5" y2="512.0" />
|
||||
<rect class="node-shape kind-service emphasis-normal role-participant" data-evidence="3025-3025,3034-3037" x="675.0" y="35.0" width="150.0" height="74.0" rx="7" />
|
||||
<text class="node-label" x="750.0" y="62.0">Host Memory</text>
|
||||
<text class="node-label" x="750.0" y="80.0">Management</text>
|
||||
<line class="lifeline" x1="750.0" y1="109.0" x2="750.0" y2="512.0" />
|
||||
<polyline class="edge kind-control style-solid emphasis-normal" points="750.0,140.0 548.5,140.0" data-evidence="3034-3041" />
|
||||
<rect class="edge-label-bg" x="573.2" y="114.0" width="152.0" height="22" rx="3" />
|
||||
<text class="edge-label" x="649.2" y="129.0">1. Balloon target 조정</text>
|
||||
<polyline class="edge kind-control style-solid emphasis-normal" points="548.5,202.0 330.0,202.0" data-evidence="3017-3022,3041-3046" />
|
||||
<rect class="edge-label-bg" x="383.4" y="176.0" width="111.8" height="22" rx="3" />
|
||||
<text class="edge-label" x="439.2" y="191.0">2. VM Boundary</text>
|
||||
<polyline class="edge kind-control style-solid emphasis-normal" points="330.0,264.0 135.5,264.0" data-evidence="3013-3017,3043-3046" />
|
||||
<rect class="edge-label-bg" x="166.8" y="238.0" width="131.9" height="22" rx="3" />
|
||||
<text class="edge-label" x="232.8" y="253.0">3. Balloon target</text>
|
||||
<polyline class="edge kind-data style-solid emphasis-normal" points="135.5,326.0 330.0,326.0" data-evidence="3046-3050" />
|
||||
<rect class="edge-label-bg" x="166.8" y="300.0" width="131.9" height="22" rx="3" />
|
||||
<text class="edge-label" x="232.8" y="315.0">4. Guest pages 확보</text>
|
||||
<polyline class="edge kind-data style-solid emphasis-normal" points="330.0,388.0 548.5,388.0" data-evidence="3017-3025" />
|
||||
<rect class="edge-label-bg" x="383.4" y="362.0" width="111.8" height="22" rx="3" />
|
||||
<text class="edge-label" x="439.2" y="377.0">5. VM Boundary</text>
|
||||
<polyline class="edge kind-data style-solid emphasis-normal" points="548.5,450.0 750.0,450.0" data-evidence="3022-3028,3077-3079" />
|
||||
<rect class="edge-label-bg" x="586.6" y="424.0" width="125.2" height="22" rx="3" />
|
||||
<text class="edge-label" x="649.2" y="439.0">6. 회수 가능 backing</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 8.0 KiB |
@@ -0,0 +1,25 @@
|
||||
# VM Exit 은 KVM 에서 끝나기도 하고 QEMU userspace 까지 가기도 한다
|
||||
|
||||
## Alternative text
|
||||
|
||||
Guest 에서 VM Exit 으로 KVM 에 닿은 뒤 커널 처리와 QEMU 처리 두 갈래로 갈라졌다가 VM Entry 로 다시 Guest 실행 재개에 모이는 흐름도.
|
||||
|
||||
## 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 이 잦을 때 무엇을 함께 보는지는 본문의 표와 문단이 맡는다.
|
||||
|
||||
## Elements and evidence
|
||||
|
||||
- **Guest** (service): VM Exit 이 나기 직전까지 실행 중이던 Guest. Evidence: L498–L501.
|
||||
- **KVM** (service): VM Exit 을 받아 Exit Reason 을 확인하고 갈래를 고르는 자리. Evidence: L495–L495, L502–L504.
|
||||
- **KVM 커널 처리** (service): 커널 안에서 끝낼 수 있는 Exit 을 처리하는 갈래. QEMU 까지 올라가지 않는다. Evidence: L508–L510, L531–L531.
|
||||
- **QEMU** (service): userspace 처리가 필요한 Exit 에서만 개입하는 갈래. Evidence: L510–L516, L533–L533.
|
||||
- **Guest 실행 재개** (service): 두 갈래가 다시 만나는 자리. VM Exit 은 VM 종료가 아니라 여기로 돌아온다. Evidence: L521–L524, L529–L529.
|
||||
|
||||
## Relationships
|
||||
|
||||
- **Guest → KVM:** VM Exit. Evidence: L495–L495, L498–L502.
|
||||
- **KVM → KVM 커널 처리:** 커널 처리 가능. Evidence: L504–L510, L531–L531.
|
||||
- **KVM 커널 처리 → Guest 실행 재개:** VM Entry. Evidence: L518–L524, L531–L531.
|
||||
- **KVM → QEMU:** KVM_RUN return. Evidence: L508–L512, L533–L533.
|
||||
- **QEMU → Guest 실행 재개:** VM Entry. Evidence: L514–L524, L533–L533.
|
||||
@@ -0,0 +1,23 @@
|
||||
# VM Exit 은 KVM 에서 끝나기도 하고 QEMU userspace 까지 가기도 한다
|
||||
# Question: VM Exit 이 난 뒤 제어권은 어디까지 올라갔다가 어떻게 Guest 로 돌아오는가
|
||||
direction: right
|
||||
n0: "Guest" {
|
||||
shape: rectangle
|
||||
}
|
||||
n1: "KVM" {
|
||||
shape: rectangle
|
||||
}
|
||||
n2: "KVM 커널 처리" {
|
||||
shape: rectangle
|
||||
}
|
||||
n3: "QEMU" {
|
||||
shape: rectangle
|
||||
}
|
||||
n4: "Guest 실행 재개" {
|
||||
shape: rectangle
|
||||
}
|
||||
n0 -> n1: "VM Exit"
|
||||
n1 -> n2: "커널 처리 가능"
|
||||
n1 -> n3: "KVM_RUN return"
|
||||
n2 -> n4: "VM Entry"
|
||||
n3 -> n4: "VM Entry"
|
||||
@@ -0,0 +1,51 @@
|
||||
<?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="vm-exit-handling-cycle" name="VM Exit 은 KVM 에서 끝나기도 하고 QEMU userspace 까지 가기도 한다">
|
||||
<mxGraphModel dx="1195" dy="353" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1195" pageHeight="1169" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0"/>
|
||||
<mxCell id="1" parent="0"/>
|
||||
<mxCell id="n_guest" value="Guest" tooltip="VM Exit 이 나기 직전까지 실행 중이던 Guest. | Evidence: L498-L501" 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="152.0" width="150.0" height="64.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="n_kvm" value="KVM<br/>Exit Reason" tooltip="VM Exit 을 받아 Exit Reason 을 확인하고 갈래를 고르는 자리. | Evidence: L495-L495, L502-L504" 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="380.0" y="148.5" width="150.0" height="71.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="n_kvm-kernel-handling" value="KVM 커널 처리<br/>Kernel" tooltip="커널 안에서 끝낼 수 있는 Exit 을 처리하는 갈래. QEMU 까지 올라가지 않는다. | Evidence: L508-L510, L531-L531" 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="690.0" y="60.0" width="150.0" height="71.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="n_qemu" value="QEMU<br/>Userspace<br/>device emulation<br/>KVM_RUN 재호출" tooltip="userspace 처리가 필요한 Exit 에서만 개입하는 갈래. | Evidence: L510-L516, L533-L533" 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="690.0" y="203.0" width="150.0" height="105.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="n_guest-resume" value="Guest 실행 재개" tooltip="두 갈래가 다시 만나는 자리. VM Exit 은 VM 종료가 아니라 여기로 돌아온다. | Evidence: L521-L524, L529-L529" 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="1000.0" y="152.0" width="150.0" height="64.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e_exit-to-kvm" value="VM Exit" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_guest" target="n_kvm">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="300.0" y="156.0" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="e_kernel-branch" value="커널 처리 가능" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_kvm" target="n_kvm-kernel-handling">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="634.0" y="135.2" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="e_kernel-reentry" value="VM Entry" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_kvm-kernel-handling" target="n_guest-resume">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="944.0" y="135.2" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="e_userspace-branch" value="KVM_RUN return" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_kvm" target="n_qemu">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="634.0" y="224.2" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="e_userspace-reentry" value="VM Entry" 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_guest-resume">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="944.0" y="224.2" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
</mxfile>
|
||||
@@ -0,0 +1,853 @@
|
||||
{
|
||||
"type": "excalidraw",
|
||||
"version": 2,
|
||||
"source": "techviz-harness",
|
||||
"elements": [
|
||||
{
|
||||
"id": "edge-exit-to-kvm",
|
||||
"type": "arrow",
|
||||
"x": 220.0,
|
||||
"y": 184.0,
|
||||
"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": 576129632,
|
||||
"version": 1,
|
||||
"versionNonce": 483419770,
|
||||
"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",
|
||||
"focus": 0,
|
||||
"gap": 4
|
||||
},
|
||||
"endBinding": {
|
||||
"elementId": "node-kvm",
|
||||
"focus": 0,
|
||||
"gap": 4
|
||||
},
|
||||
"startArrowhead": null,
|
||||
"endArrowhead": "arrow",
|
||||
"elbowed": true
|
||||
},
|
||||
{
|
||||
"id": "edge-label-exit-to-kvm",
|
||||
"type": "text",
|
||||
"x": 255.0,
|
||||
"y": 144.0,
|
||||
"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": 1778979104,
|
||||
"version": 1,
|
||||
"versionNonce": 1759467009,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"fontSize": 13,
|
||||
"fontFamily": 5,
|
||||
"text": "VM Exit",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "VM Exit",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "edge-kernel-branch",
|
||||
"type": "arrow",
|
||||
"x": 530.0,
|
||||
"y": 95.5,
|
||||
"width": 160.0,
|
||||
"height": 79.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": 298728286,
|
||||
"version": 1,
|
||||
"versionNonce": 1052103781,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"points": [
|
||||
[
|
||||
0.0,
|
||||
79.5
|
||||
],
|
||||
[
|
||||
80.0,
|
||||
79.5
|
||||
],
|
||||
[
|
||||
80.0,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
160.0,
|
||||
0.0
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
"startBinding": {
|
||||
"elementId": "node-kvm",
|
||||
"focus": 0,
|
||||
"gap": 4
|
||||
},
|
||||
"endBinding": {
|
||||
"elementId": "node-kvm-kernel-handling",
|
||||
"focus": 0,
|
||||
"gap": 4
|
||||
},
|
||||
"startArrowhead": null,
|
||||
"endArrowhead": "arrow",
|
||||
"elbowed": true
|
||||
},
|
||||
{
|
||||
"id": "edge-label-kernel-branch",
|
||||
"type": "text",
|
||||
"x": 589.0,
|
||||
"y": 123.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": 182098500,
|
||||
"version": 1,
|
||||
"versionNonce": 150063666,
|
||||
"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": "edge-kernel-reentry",
|
||||
"type": "arrow",
|
||||
"x": 840.0,
|
||||
"y": 95.5,
|
||||
"width": 160.0,
|
||||
"height": 79.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": 1851363334,
|
||||
"version": 1,
|
||||
"versionNonce": 126183680,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"points": [
|
||||
[
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
80.0,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
80.0,
|
||||
79.5
|
||||
],
|
||||
[
|
||||
160.0,
|
||||
79.5
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
"startBinding": {
|
||||
"elementId": "node-kvm-kernel-handling",
|
||||
"focus": 0,
|
||||
"gap": 4
|
||||
},
|
||||
"endBinding": {
|
||||
"elementId": "node-guest-resume",
|
||||
"focus": 0,
|
||||
"gap": 4
|
||||
},
|
||||
"startArrowhead": null,
|
||||
"endArrowhead": "arrow",
|
||||
"elbowed": true
|
||||
},
|
||||
{
|
||||
"id": "edge-label-kernel-reentry",
|
||||
"type": "text",
|
||||
"x": 899.0,
|
||||
"y": 123.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": 1185810906,
|
||||
"version": 1,
|
||||
"versionNonce": 1820150380,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"fontSize": 13,
|
||||
"fontFamily": 5,
|
||||
"text": "VM Entry",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "VM Entry",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "edge-userspace-branch",
|
||||
"type": "arrow",
|
||||
"x": 530.0,
|
||||
"y": 193.0,
|
||||
"width": 160.0,
|
||||
"height": 62.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": 1399118646,
|
||||
"version": 1,
|
||||
"versionNonce": 1254117045,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"points": [
|
||||
[
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
80.0,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
80.0,
|
||||
62.5
|
||||
],
|
||||
[
|
||||
160.0,
|
||||
62.5
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
"startBinding": {
|
||||
"elementId": "node-kvm",
|
||||
"focus": 0,
|
||||
"gap": 4
|
||||
},
|
||||
"endBinding": {
|
||||
"elementId": "node-qemu",
|
||||
"focus": 0,
|
||||
"gap": 4
|
||||
},
|
||||
"startArrowhead": null,
|
||||
"endArrowhead": "arrow",
|
||||
"elbowed": true
|
||||
},
|
||||
{
|
||||
"id": "edge-label-userspace-branch",
|
||||
"type": "text",
|
||||
"x": 578.0,
|
||||
"y": 212.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": 1771993102,
|
||||
"version": 1,
|
||||
"versionNonce": 1685196756,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"fontSize": 13,
|
||||
"fontFamily": 5,
|
||||
"text": "KVM_RUN return",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "KVM_RUN return",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "edge-userspace-reentry",
|
||||
"type": "arrow",
|
||||
"x": 840.0,
|
||||
"y": 193.0,
|
||||
"width": 160.0,
|
||||
"height": 62.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": 633909860,
|
||||
"version": 1,
|
||||
"versionNonce": 1287721817,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"points": [
|
||||
[
|
||||
0.0,
|
||||
62.5
|
||||
],
|
||||
[
|
||||
80.0,
|
||||
62.5
|
||||
],
|
||||
[
|
||||
80.0,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
160.0,
|
||||
0.0
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
"startBinding": {
|
||||
"elementId": "node-qemu",
|
||||
"focus": 0,
|
||||
"gap": 4
|
||||
},
|
||||
"endBinding": {
|
||||
"elementId": "node-guest-resume",
|
||||
"focus": 0,
|
||||
"gap": 4
|
||||
},
|
||||
"startArrowhead": null,
|
||||
"endArrowhead": "arrow",
|
||||
"elbowed": true
|
||||
},
|
||||
{
|
||||
"id": "edge-label-userspace-reentry",
|
||||
"type": "text",
|
||||
"x": 899.0,
|
||||
"y": 212.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": 142846947,
|
||||
"version": 1,
|
||||
"versionNonce": 1732717636,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"fontSize": 13,
|
||||
"fontFamily": 5,
|
||||
"text": "VM Entry",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "VM Entry",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "node-guest",
|
||||
"type": "rectangle",
|
||||
"x": 70.0,
|
||||
"y": 152.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": 746352068,
|
||||
"version": 1,
|
||||
"versionNonce": 1245638436,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false
|
||||
},
|
||||
{
|
||||
"id": "node-label-guest",
|
||||
"type": "text",
|
||||
"x": 80.0,
|
||||
"y": 162.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": 1592812232,
|
||||
"version": 1,
|
||||
"versionNonce": 724526867,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"fontSize": 15,
|
||||
"fontFamily": 5,
|
||||
"text": "Guest",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "Guest",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "node-kvm",
|
||||
"type": "rectangle",
|
||||
"x": 380.0,
|
||||
"y": 148.5,
|
||||
"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": 1294725949,
|
||||
"version": 1,
|
||||
"versionNonce": 774205501,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false
|
||||
},
|
||||
{
|
||||
"id": "node-label-kvm",
|
||||
"type": "text",
|
||||
"x": 390.0,
|
||||
"y": 158.5,
|
||||
"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": 820201432,
|
||||
"version": 1,
|
||||
"versionNonce": 242634004,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"fontSize": 15,
|
||||
"fontFamily": 5,
|
||||
"text": "KVM\nExit Reason",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "KVM\nExit Reason",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "node-kvm-kernel-handling",
|
||||
"type": "rectangle",
|
||||
"x": 690.0,
|
||||
"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": 116878736,
|
||||
"version": 1,
|
||||
"versionNonce": 1278264154,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false
|
||||
},
|
||||
{
|
||||
"id": "node-label-kvm-kernel-handling",
|
||||
"type": "text",
|
||||
"x": 700.0,
|
||||
"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": 66773704,
|
||||
"version": 1,
|
||||
"versionNonce": 1987847254,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"fontSize": 15,
|
||||
"fontFamily": 5,
|
||||
"text": "KVM 커널 처리\nKernel",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "KVM 커널 처리\nKernel",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "node-qemu",
|
||||
"type": "rectangle",
|
||||
"x": 690.0,
|
||||
"y": 203.0,
|
||||
"width": 150.0,
|
||||
"height": 105.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": 1265942130,
|
||||
"version": 1,
|
||||
"versionNonce": 447320704,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false
|
||||
},
|
||||
{
|
||||
"id": "node-label-qemu",
|
||||
"type": "text",
|
||||
"x": 700.0,
|
||||
"y": 213.0,
|
||||
"width": 130.0,
|
||||
"height": 85.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": 82355235,
|
||||
"version": 1,
|
||||
"versionNonce": 463780486,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"fontSize": 15,
|
||||
"fontFamily": 5,
|
||||
"text": "QEMU\nUserspace\ndevice emulation\nKVM_RUN 재호출",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "QEMU\nUserspace\ndevice emulation\nKVM_RUN 재호출",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "node-guest-resume",
|
||||
"type": "rectangle",
|
||||
"x": 1000.0,
|
||||
"y": 152.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": 1508197857,
|
||||
"version": 1,
|
||||
"versionNonce": 1661088114,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false
|
||||
},
|
||||
{
|
||||
"id": "node-label-guest-resume",
|
||||
"type": "text",
|
||||
"x": 1010.0,
|
||||
"y": 162.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": 1489903096,
|
||||
"version": 1,
|
||||
"versionNonce": 1640327916,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"fontSize": 15,
|
||||
"fontFamily": 5,
|
||||
"text": "Guest 실행 재개",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "Guest 실행 재개",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
}
|
||||
],
|
||||
"appState": {
|
||||
"gridSize": 10,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"currentItemFontFamily": 5
|
||||
},
|
||||
"files": {}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"harness_version": "0.2.0",
|
||||
"spec_id": "vm-exit-handling-cycle",
|
||||
"spec_version": "1.1",
|
||||
"spec_sha256": "114180c059ff8c4be3e253da16d2d2f0e0fb44a2b87b03fcd2104f4e3fd6fa79",
|
||||
"source_context": {
|
||||
"document": "docs/virtualization/final/document.md",
|
||||
"document_sha256": "181e2b3cc8a45bae81e7e8193d026937c4d586ae4495d3b2c323eb7e6abcfadd",
|
||||
"anchor": {
|
||||
"kind": "heading",
|
||||
"value": "9. VM Exit 이후 처리",
|
||||
"line": 493
|
||||
}
|
||||
},
|
||||
"outputs": [
|
||||
"vm-exit-handling-cycle.svg",
|
||||
"vm-exit-handling-cycle.drawio",
|
||||
"vm-exit-handling-cycle.mmd",
|
||||
"vm-exit-handling-cycle.d2",
|
||||
"vm-exit-handling-cycle.excalidraw",
|
||||
"vm-exit-handling-cycle.alt.md"
|
||||
],
|
||||
"lint_issue_count": 0,
|
||||
"assumption_count": 0,
|
||||
"assumptions_allowed": false,
|
||||
"composition_profile": "component-flow",
|
||||
"reference_ids": [
|
||||
"payment-event-flow"
|
||||
],
|
||||
"diagram_only": true
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
%% VM Exit 은 KVM 에서 끝나기도 하고 QEMU userspace 까지 가기도 한다
|
||||
%% question: VM Exit 이 난 뒤 제어권은 어디까지 올라갔다가 어떻게 Guest 로 돌아오는가
|
||||
flowchart LR
|
||||
n0["Guest"]
|
||||
n1["KVM"]
|
||||
n2["KVM 커널 처리"]
|
||||
n3["QEMU"]
|
||||
n4["Guest 실행 재개"]
|
||||
n0 -->|"VM Exit"| n1
|
||||
n1 -->|"커널 처리 가능"| n2
|
||||
n1 -->|"KVM_RUN return"| n3
|
||||
n2 -->|"VM Entry"| n4
|
||||
n3 -->|"VM Entry"| n4
|
||||
@@ -0,0 +1,96 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1195" height="353" viewBox="0 0 1195 353" role="img" aria-labelledby="diagram-title diagram-description">
|
||||
<title id="diagram-title">VM Exit 은 KVM 에서 끝나기도 하고 QEMU userspace 까지 가기도 한다</title>
|
||||
<desc id="diagram-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 이 잦을 때 무엇을 함께 보는지는 본문의 표와 문단이 맡는다.</desc>
|
||||
<metadata>{"techviz":{"spec_version":"1.1","id":"vm-exit-handling-cycle","profile":"component-flow"},"source_context":{"document":"docs/virtualization/final/document.md","document_sha256":"181e2b3cc8a45bae81e7e8193d026937c4d586ae4495d3b2c323eb7e6abcfadd","anchor":{"kind":"heading","value":"9. VM Exit 이후 처리","line":493}},"evidence_policy":"Each factual element cites source lines or is marked assumption.","diagram_only":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="1195" height="353" />
|
||||
<polyline class="edge kind-control style-solid emphasis-normal" points="220.0,184.0 300.0,184.0 300.0,184.0 380.0,184.0" data-evidence="495-495,498-502" />
|
||||
<rect class="edge-label-bg" x="267.6" y="142.0" width="64.9" height="22" rx="3" />
|
||||
<text class="edge-label" x="300.0" y="157.0">VM Exit</text>
|
||||
<polyline class="edge kind-control style-solid emphasis-normal" points="530.0,175.0 610.0,175.0 610.0,95.5 690.0,95.5" data-evidence="504-510,531-531" />
|
||||
<rect class="edge-label-bg" x="598.2" y="121.2" width="71.6" height="22" rx="3" />
|
||||
<text class="edge-label" x="634.0" y="136.2">커널 처리 가능</text>
|
||||
<polyline class="edge kind-control style-solid emphasis-normal" points="840.0,95.5 920.0,95.5 920.0,175.0 1000.0,175.0" data-evidence="518-524,531-531" />
|
||||
<rect class="edge-label-bg" x="908.2" y="121.2" width="71.6" height="22" rx="3" />
|
||||
<text class="edge-label" x="944.0" y="136.2">VM Entry</text>
|
||||
<polyline class="edge kind-control style-solid emphasis-normal" points="530.0,193.0 610.0,193.0 610.0,255.5 690.0,255.5" data-evidence="508-512,533-533" />
|
||||
<rect class="edge-label-bg" x="578.1" y="210.2" width="111.8" height="22" rx="3" />
|
||||
<text class="edge-label" x="634.0" y="225.2">KVM_RUN return</text>
|
||||
<polyline class="edge kind-control style-solid emphasis-normal" points="840.0,255.5 920.0,255.5 920.0,193.0 1000.0,193.0" data-evidence="514-524,533-533" />
|
||||
<rect class="edge-label-bg" x="908.2" y="210.2" width="71.6" height="22" rx="3" />
|
||||
<text class="edge-label" x="944.0" y="225.2">VM Entry</text>
|
||||
<g id="node-guest">
|
||||
<rect class="node-shape kind-service emphasis-normal role-source" data-evidence="498-501" x="70.0" y="152.0" width="150.0" height="64.0" rx="7" />
|
||||
<text class="node-label" x="145.0" y="182.0">Guest</text>
|
||||
</g>
|
||||
<g id="node-kvm">
|
||||
<rect class="node-shape kind-service emphasis-primary role-service" data-evidence="495-495,502-504" x="380.0" y="148.5" width="150.0" height="71.0" rx="7" />
|
||||
<text class="node-label" x="455.0" y="175.5">KVM</text>
|
||||
<line class="node-detail-divider" x1="394.0" y1="196.5" x2="516.0" y2="196.5" />
|
||||
<text class="node-detail" x="396.0" y="213.5">Exit Reason</text>
|
||||
</g>
|
||||
<g id="node-kvm-kernel-handling">
|
||||
<rect class="node-shape kind-service emphasis-normal role-service" data-evidence="508-510,531-531" x="690.0" y="60.0" width="150.0" height="71.0" rx="7" />
|
||||
<text class="node-label" x="765.0" y="87.0">KVM 커널 처리</text>
|
||||
<line class="node-detail-divider" x1="704.0" y1="108.0" x2="826.0" y2="108.0" />
|
||||
<text class="node-detail" x="706.0" y="125.0">Kernel</text>
|
||||
</g>
|
||||
<g id="node-qemu">
|
||||
<rect class="node-shape kind-service emphasis-normal role-service" data-evidence="510-516,533-533" x="690.0" y="203.0" width="150.0" height="105.0" rx="7" />
|
||||
<text class="node-label" x="765.0" y="230.0">QEMU</text>
|
||||
<line class="node-detail-divider" x1="704.0" y1="251.0" x2="826.0" y2="251.0" />
|
||||
<text class="node-detail" x="706.0" y="268.0">Userspace</text>
|
||||
<text class="node-detail" x="706.0" y="284.0">device emulation</text>
|
||||
<text class="node-detail" x="706.0" y="300.0">KVM_RUN 재호출</text>
|
||||
</g>
|
||||
<g id="node-guest-resume">
|
||||
<rect class="node-shape kind-service emphasis-normal role-sink" data-evidence="521-524,529-529" x="1000.0" y="152.0" width="150.0" height="64.0" rx="7" />
|
||||
<text class="node-label" x="1075.0" y="182.0">Guest 실행 재개</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 8.0 KiB |
@@ -0,0 +1,26 @@
|
||||
# 가상 머신 둘과 호스트 프로세스가 하나의 NVMe 로 모인다
|
||||
|
||||
## Alternative text
|
||||
|
||||
VM1 QEMU, VM2 QEMU, Nginx, Host 기타 네 곳에서 나온 화살표가 하나의 Host Block Layer 로 모이고 거기서 NVMe 로 나가는 수렴 흐름도.
|
||||
|
||||
## 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 과는 다투는 자원이 다르다는 것은 본문 문단이 맡는다.
|
||||
|
||||
## Elements and evidence
|
||||
|
||||
- **VM1 QEMU** (service): 가상 머신 하나의 QEMU 프로세스. 게스트의 I/O 가 여기서 호스트 파일 I/O 가 된다. Evidence: L7029–L7029, L7041–L7044.
|
||||
- **VM2 QEMU** (service): 다른 가상 머신의 QEMU 프로세스. Evidence: L7031–L7031, L7046–L7048.
|
||||
- **Nginx** (service): 호스트에서 직접 도는 프로세스. Evidence: L7033–L7033.
|
||||
- **Host 기타** (service): 그 밖의 호스트 프로세스. Evidence: L7035–L7038.
|
||||
- **Host Block Layer** (service): 어디서 왔는지 구분하지 않고 모두 Host block request 로 다루는 계층. queue 에서 관리해 device 로 내보낸다. Evidence: L7031–L7031, L7017–L7022, L7054–L7054, L7058–L7060.
|
||||
- **NVMe** (device): 네 갈래가 결국 함께 쓰는 하나의 물리 device. Evidence: L7031–L7031, L7019–L7019, L7066–L7069.
|
||||
|
||||
## Relationships
|
||||
|
||||
- **VM1 QEMU → Host Block Layer:** data. Evidence: L7029–L7031.
|
||||
- **VM2 QEMU → Host Block Layer:** data. Evidence: L7031–L7031.
|
||||
- **Nginx → Host Block Layer:** data. Evidence: L7031–L7033.
|
||||
- **Host 기타 → Host Block Layer:** data. Evidence: L7031–L7035.
|
||||
- **Host Block Layer → NVMe:** dispatch. Evidence: L7031–L7031, L7054–L7054.
|
||||
@@ -0,0 +1,26 @@
|
||||
# 가상 머신 둘과 호스트 프로세스가 하나의 NVMe 로 모인다
|
||||
# Question: 서로 다른 곳에서 시작한 I/O 는 어디에서 하나로 모여 device 로 나가는가
|
||||
direction: down
|
||||
n0: "VM1 QEMU" {
|
||||
shape: rectangle
|
||||
}
|
||||
n1: "VM2 QEMU" {
|
||||
shape: rectangle
|
||||
}
|
||||
n2: "Nginx" {
|
||||
shape: rectangle
|
||||
}
|
||||
n3: "Host 기타" {
|
||||
shape: rectangle
|
||||
}
|
||||
n4: "Host Block Layer" {
|
||||
shape: rectangle
|
||||
}
|
||||
n5: "NVMe" {
|
||||
shape: rectangle
|
||||
}
|
||||
n0 -> n4
|
||||
n1 -> n4
|
||||
n2 -> n4
|
||||
n3 -> n4
|
||||
n4 -> n5: "dispatch"
|
||||
@@ -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="vms-sharing-one-nvme" name="가상 머신 둘과 호스트 프로세스가 하나의 NVMe 로 모인다">
|
||||
<mxGraphModel dx="895" dy="580" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="895" pageHeight="1169" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0"/>
|
||||
<mxCell id="1" parent="0"/>
|
||||
<mxCell id="n_vm1-qemu" value="VM1 QEMU<br/>WRITE X<br/>READ Y<br/>WRITE Z" tooltip="가상 머신 하나의 QEMU 프로세스. 게스트의 I/O 가 여기서 호스트 파일 I/O 가 된다. | Evidence: L7029-L7029, L7041-L7044" 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="60.0" width="150.0" height="105.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="n_vm2-qemu" value="VM2 QEMU<br/>READ A<br/>WRITE B" tooltip="다른 가상 머신의 QEMU 프로세스. | Evidence: L7031-L7031, L7046-L7048" 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="68.5" width="150.0" height="88.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="n_nginx" value="Nginx" tooltip="호스트에서 직접 도는 프로세스. | Evidence: L7033-L7033" 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="80.5" width="150.0" height="64.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="n_host-other" value="Host 기타" tooltip="그 밖의 호스트 프로세스. | Evidence: L7035-L7038" 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="700.0" y="80.5" width="150.0" height="64.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="n_host-block-layer" value="Host Block Layer<br/>Host block request<br/>blk-mq" tooltip="어디서 왔는지 구분하지 않고 모두 Host block request 로 다루는 계층. queue 에서 관리해 device 로 내보낸다. | Evidence: L7031-L7031, L7017-L7022, L7054-L7054, L7058-L7060" 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="380.0" y="261.0" width="160.0" height="88.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="n_nvme" value="NVMe" tooltip="네 갈래가 결국 함께 쓰는 하나의 물리 device. | Evidence: L7031-L7031, L7019-L7019, L7066-L7069" 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="385.0" y="445.0" width="150.0" height="90.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e_vm1-to-block-layer" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_vm1-qemu" target="n_host-block-layer">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="289.0" y="185.0" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="e_vm2-to-block-layer" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_vm2-qemu" target="n_host-block-layer">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="403.0" y="180.8" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="e_nginx-to-block-layer" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_nginx" target="n_host-block-layer">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="517.0" y="174.8" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="e_host-other-to-block-layer" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_host-other" target="n_host-block-layer">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="631.0" y="174.8" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="e_block-layer-to-nvme" value="dispatch" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_host-block-layer" target="n_nvme">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="484.0" y="397.0" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
</mxfile>
|
||||
@@ -0,0 +1,766 @@
|
||||
{
|
||||
"type": "excalidraw",
|
||||
"version": 2,
|
||||
"source": "techviz-harness",
|
||||
"elements": [
|
||||
{
|
||||
"id": "edge-vm1-to-block-layer",
|
||||
"type": "arrow",
|
||||
"x": 145.0,
|
||||
"y": 165.0,
|
||||
"width": 288.0,
|
||||
"height": 96.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": 1225083499,
|
||||
"version": 1,
|
||||
"versionNonce": 1066380271,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"points": [
|
||||
[
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
],
|
||||
[
|
||||
288.0,
|
||||
48.0
|
||||
],
|
||||
[
|
||||
288.0,
|
||||
96.0
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
"startBinding": {
|
||||
"elementId": "node-vm1-qemu",
|
||||
"focus": 0,
|
||||
"gap": 4
|
||||
},
|
||||
"endBinding": {
|
||||
"elementId": "node-host-block-layer",
|
||||
"focus": 0,
|
||||
"gap": 4
|
||||
},
|
||||
"startArrowhead": null,
|
||||
"endArrowhead": "arrow",
|
||||
"elbowed": true
|
||||
},
|
||||
{
|
||||
"id": "edge-vm2-to-block-layer",
|
||||
"type": "arrow",
|
||||
"x": 355.0,
|
||||
"y": 156.5,
|
||||
"width": 96.0,
|
||||
"height": 104.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": 948207587,
|
||||
"version": 1,
|
||||
"versionNonce": 1141556592,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"points": [
|
||||
[
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
52.25
|
||||
],
|
||||
[
|
||||
96.0,
|
||||
52.25
|
||||
],
|
||||
[
|
||||
96.0,
|
||||
104.5
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
"startBinding": {
|
||||
"elementId": "node-vm2-qemu",
|
||||
"focus": 0,
|
||||
"gap": 4
|
||||
},
|
||||
"endBinding": {
|
||||
"elementId": "node-host-block-layer",
|
||||
"focus": 0,
|
||||
"gap": 4
|
||||
},
|
||||
"startArrowhead": null,
|
||||
"endArrowhead": "arrow",
|
||||
"elbowed": true
|
||||
},
|
||||
{
|
||||
"id": "edge-nginx-to-block-layer",
|
||||
"type": "arrow",
|
||||
"x": 469.0,
|
||||
"y": 144.5,
|
||||
"width": 96.0,
|
||||
"height": 116.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": 414555304,
|
||||
"version": 1,
|
||||
"versionNonce": 162956757,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"points": [
|
||||
[
|
||||
96.0,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
96.0,
|
||||
58.25
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
58.25
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
116.5
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
"startBinding": {
|
||||
"elementId": "node-nginx",
|
||||
"focus": 0,
|
||||
"gap": 4
|
||||
},
|
||||
"endBinding": {
|
||||
"elementId": "node-host-block-layer",
|
||||
"focus": 0,
|
||||
"gap": 4
|
||||
},
|
||||
"startArrowhead": null,
|
||||
"endArrowhead": "arrow",
|
||||
"elbowed": true
|
||||
},
|
||||
{
|
||||
"id": "edge-host-other-to-block-layer",
|
||||
"type": "arrow",
|
||||
"x": 487.0,
|
||||
"y": 144.5,
|
||||
"width": 288.0,
|
||||
"height": 116.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": 1681052111,
|
||||
"version": 1,
|
||||
"versionNonce": 495490748,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"points": [
|
||||
[
|
||||
288.0,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
288.0,
|
||||
58.25
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
58.25
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
116.5
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
"startBinding": {
|
||||
"elementId": "node-host-other",
|
||||
"focus": 0,
|
||||
"gap": 4
|
||||
},
|
||||
"endBinding": {
|
||||
"elementId": "node-host-block-layer",
|
||||
"focus": 0,
|
||||
"gap": 4
|
||||
},
|
||||
"startArrowhead": null,
|
||||
"endArrowhead": "arrow",
|
||||
"elbowed": true
|
||||
},
|
||||
{
|
||||
"id": "edge-block-layer-to-nvme",
|
||||
"type": "arrow",
|
||||
"x": 460.0,
|
||||
"y": 349.0,
|
||||
"width": 0.0,
|
||||
"height": 96.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": 1129222860,
|
||||
"version": 1,
|
||||
"versionNonce": 1630472271,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"points": [
|
||||
[
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
96.0
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
"startBinding": {
|
||||
"elementId": "node-host-block-layer",
|
||||
"focus": 0,
|
||||
"gap": 4
|
||||
},
|
||||
"endBinding": {
|
||||
"elementId": "node-nvme",
|
||||
"focus": 0,
|
||||
"gap": 4
|
||||
},
|
||||
"startArrowhead": null,
|
||||
"endArrowhead": "arrow",
|
||||
"elbowed": true
|
||||
},
|
||||
{
|
||||
"id": "edge-label-block-layer-to-nvme",
|
||||
"type": "text",
|
||||
"x": 439.0,
|
||||
"y": 385.0,
|
||||
"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": 181877595,
|
||||
"version": 1,
|
||||
"versionNonce": 1808212730,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"fontSize": 13,
|
||||
"fontFamily": 5,
|
||||
"text": "dispatch",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "dispatch",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "node-vm1-qemu",
|
||||
"type": "rectangle",
|
||||
"x": 70.0,
|
||||
"y": 60.0,
|
||||
"width": 150.0,
|
||||
"height": 105.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": 845950122,
|
||||
"version": 1,
|
||||
"versionNonce": 605509420,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false
|
||||
},
|
||||
{
|
||||
"id": "node-label-vm1-qemu",
|
||||
"type": "text",
|
||||
"x": 80.0,
|
||||
"y": 70.0,
|
||||
"width": 130.0,
|
||||
"height": 85.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": 1628887011,
|
||||
"version": 1,
|
||||
"versionNonce": 506408877,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"fontSize": 15,
|
||||
"fontFamily": 5,
|
||||
"text": "VM1 QEMU\nWRITE X\nREAD Y\nWRITE Z",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "VM1 QEMU\nWRITE X\nREAD Y\nWRITE Z",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "node-vm2-qemu",
|
||||
"type": "rectangle",
|
||||
"x": 280.0,
|
||||
"y": 68.5,
|
||||
"width": 150.0,
|
||||
"height": 88.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": 1622552226,
|
||||
"version": 1,
|
||||
"versionNonce": 1610483505,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false
|
||||
},
|
||||
{
|
||||
"id": "node-label-vm2-qemu",
|
||||
"type": "text",
|
||||
"x": 290.0,
|
||||
"y": 78.5,
|
||||
"width": 130.0,
|
||||
"height": 68.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": 892438862,
|
||||
"version": 1,
|
||||
"versionNonce": 41324946,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"fontSize": 15,
|
||||
"fontFamily": 5,
|
||||
"text": "VM2 QEMU\nREAD A\nWRITE B",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "VM2 QEMU\nREAD A\nWRITE B",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "node-nginx",
|
||||
"type": "rectangle",
|
||||
"x": 490.0,
|
||||
"y": 80.5,
|
||||
"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": 721013300,
|
||||
"version": 1,
|
||||
"versionNonce": 1520646945,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false
|
||||
},
|
||||
{
|
||||
"id": "node-label-nginx",
|
||||
"type": "text",
|
||||
"x": 500.0,
|
||||
"y": 90.5,
|
||||
"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": 117243520,
|
||||
"version": 1,
|
||||
"versionNonce": 936748041,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"fontSize": 15,
|
||||
"fontFamily": 5,
|
||||
"text": "Nginx",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "Nginx",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "node-host-other",
|
||||
"type": "rectangle",
|
||||
"x": 700.0,
|
||||
"y": 80.5,
|
||||
"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": 1719667036,
|
||||
"version": 1,
|
||||
"versionNonce": 1594842564,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false
|
||||
},
|
||||
{
|
||||
"id": "node-label-host-other",
|
||||
"type": "text",
|
||||
"x": 710.0,
|
||||
"y": 90.5,
|
||||
"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": 376776079,
|
||||
"version": 1,
|
||||
"versionNonce": 223147487,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"fontSize": 15,
|
||||
"fontFamily": 5,
|
||||
"text": "Host 기타",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "Host 기타",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "node-host-block-layer",
|
||||
"type": "rectangle",
|
||||
"x": 380.0,
|
||||
"y": 261.0,
|
||||
"width": 160.0,
|
||||
"height": 88.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": 923862125,
|
||||
"version": 1,
|
||||
"versionNonce": 1842771359,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false
|
||||
},
|
||||
{
|
||||
"id": "node-label-host-block-layer",
|
||||
"type": "text",
|
||||
"x": 390.0,
|
||||
"y": 271.0,
|
||||
"width": 140.0,
|
||||
"height": 68.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": 117879901,
|
||||
"version": 1,
|
||||
"versionNonce": 785252073,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"fontSize": 15,
|
||||
"fontFamily": 5,
|
||||
"text": "Host Block Layer\nHost block request\nblk-mq",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "Host Block Layer\nHost block request\nblk-mq",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "node-nvme",
|
||||
"type": "rectangle",
|
||||
"x": 385.0,
|
||||
"y": 445.0,
|
||||
"width": 150.0,
|
||||
"height": 90.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": 1447108727,
|
||||
"version": 1,
|
||||
"versionNonce": 491109462,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false
|
||||
},
|
||||
{
|
||||
"id": "node-label-nvme",
|
||||
"type": "text",
|
||||
"x": 395.0,
|
||||
"y": 455.0,
|
||||
"width": 130.0,
|
||||
"height": 70.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": 502338443,
|
||||
"version": 1,
|
||||
"versionNonce": 1220402361,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"fontSize": 15,
|
||||
"fontFamily": 5,
|
||||
"text": "NVMe",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "NVMe",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
}
|
||||
],
|
||||
"appState": {
|
||||
"gridSize": 10,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"currentItemFontFamily": 5
|
||||
},
|
||||
"files": {}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"harness_version": "0.2.0",
|
||||
"spec_id": "vms-sharing-one-nvme",
|
||||
"spec_version": "1.1",
|
||||
"spec_sha256": "43a75d5863edb3e1585ebf97e76f7bd9bf773c90836d4f8f91936eb8a557bd28",
|
||||
"source_context": {
|
||||
"document": "docs/virtualization/final/document.md",
|
||||
"document_sha256": "181e2b3cc8a45bae81e7e8193d026937c4d586ae4495d3b2c323eb7e6abcfadd",
|
||||
"anchor": {
|
||||
"kind": "heading",
|
||||
"value": "159. 여러 VM이 하나의 NVMe를 공유하면",
|
||||
"line": 7026
|
||||
}
|
||||
},
|
||||
"outputs": [
|
||||
"vms-sharing-one-nvme.svg",
|
||||
"vms-sharing-one-nvme.drawio",
|
||||
"vms-sharing-one-nvme.mmd",
|
||||
"vms-sharing-one-nvme.d2",
|
||||
"vms-sharing-one-nvme.excalidraw",
|
||||
"vms-sharing-one-nvme.alt.md"
|
||||
],
|
||||
"lint_issue_count": 5,
|
||||
"assumption_count": 0,
|
||||
"assumptions_allowed": false,
|
||||
"composition_profile": "component-flow",
|
||||
"reference_ids": [
|
||||
"payment-event-flow"
|
||||
],
|
||||
"diagram_only": true
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
%% 가상 머신 둘과 호스트 프로세스가 하나의 NVMe 로 모인다
|
||||
%% question: 서로 다른 곳에서 시작한 I/O 는 어디에서 하나로 모여 device 로 나가는가
|
||||
flowchart TB
|
||||
n0["VM1 QEMU"]
|
||||
n1["VM2 QEMU"]
|
||||
n2["Nginx"]
|
||||
n3["Host 기타"]
|
||||
n4["Host Block Layer"]
|
||||
n5["NVMe"]
|
||||
n0 --> n4
|
||||
n1 --> n4
|
||||
n2 --> n4
|
||||
n3 --> n4
|
||||
n4 -->|"dispatch"| n5
|
||||
@@ -0,0 +1,94 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="895" height="580" viewBox="0 0 895 580" role="img" aria-labelledby="diagram-title diagram-description">
|
||||
<title id="diagram-title">가상 머신 둘과 호스트 프로세스가 하나의 NVMe 로 모인다</title>
|
||||
<desc id="diagram-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 과는 다투는 자원이 다르다는 것은 본문 문단이 맡는다.</desc>
|
||||
<metadata>{"techviz":{"spec_version":"1.1","id":"vms-sharing-one-nvme","profile":"component-flow"},"source_context":{"document":"docs/virtualization/final/document.md","document_sha256":"181e2b3cc8a45bae81e7e8193d026937c4d586ae4495d3b2c323eb7e6abcfadd","anchor":{"kind":"heading","value":"159. 여러 VM이 하나의 NVMe를 공유하면","line":7026}},"evidence_policy":"Each factual element cites source lines or is marked assumption.","diagram_only":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="895" height="580" />
|
||||
<polyline class="edge kind-data style-solid emphasis-normal" points="145.0,165.0 145.0,213.0 433.0,213.0 433.0,261.0" data-evidence="7029-7031" />
|
||||
<polyline class="edge kind-data style-solid emphasis-normal" points="355.0,156.5 355.0,208.8 451.0,208.8 451.0,261.0" data-evidence="7031-7031" />
|
||||
<polyline class="edge kind-data style-solid emphasis-normal" points="565.0,144.5 565.0,202.8 469.0,202.8 469.0,261.0" data-evidence="7031-7033" />
|
||||
<polyline class="edge kind-data style-solid emphasis-normal" points="775.0,144.5 775.0,202.8 487.0,202.8 487.0,261.0" data-evidence="7031-7035" />
|
||||
<polyline class="edge kind-data style-solid emphasis-normal" points="460.0,349.0 460.0,397.0 460.0,397.0 460.0,445.0" data-evidence="7031-7031,7054-7054" />
|
||||
<rect class="edge-label-bg" x="448.2" y="383.0" width="71.6" height="22" rx="3" />
|
||||
<text class="edge-label" x="484.0" y="398.0">dispatch</text>
|
||||
<g id="node-vm1-qemu">
|
||||
<rect class="node-shape kind-service emphasis-normal role-source" data-evidence="7029-7029,7041-7044" x="70.0" y="60.0" width="150.0" height="105.0" rx="7" />
|
||||
<text class="node-label" x="145.0" y="87.0">VM1 QEMU</text>
|
||||
<line class="node-detail-divider" x1="84.0" y1="108.0" x2="206.0" y2="108.0" />
|
||||
<text class="node-detail" x="86.0" y="125.0">WRITE X</text>
|
||||
<text class="node-detail" x="86.0" y="141.0">READ Y</text>
|
||||
<text class="node-detail" x="86.0" y="157.0">WRITE Z</text>
|
||||
</g>
|
||||
<g id="node-vm2-qemu">
|
||||
<rect class="node-shape kind-service emphasis-normal role-source" data-evidence="7031-7031,7046-7048" x="280.0" y="68.5" width="150.0" height="88.0" rx="7" />
|
||||
<text class="node-label" x="355.0" y="95.5">VM2 QEMU</text>
|
||||
<line class="node-detail-divider" x1="294.0" y1="116.5" x2="416.0" y2="116.5" />
|
||||
<text class="node-detail" x="296.0" y="133.5">READ A</text>
|
||||
<text class="node-detail" x="296.0" y="149.5">WRITE B</text>
|
||||
</g>
|
||||
<g id="node-nginx">
|
||||
<rect class="node-shape kind-service emphasis-normal role-source" data-evidence="7033-7033" x="490.0" y="80.5" width="150.0" height="64.0" rx="7" />
|
||||
<text class="node-label" x="565.0" y="110.5">Nginx</text>
|
||||
</g>
|
||||
<g id="node-host-other">
|
||||
<rect class="node-shape kind-service emphasis-normal role-source" data-evidence="7035-7038" x="700.0" y="80.5" width="150.0" height="64.0" rx="7" />
|
||||
<text class="node-label" x="775.0" y="110.5">Host 기타</text>
|
||||
</g>
|
||||
<g id="node-host-block-layer">
|
||||
<rect class="node-shape kind-service emphasis-primary role-queue" data-evidence="7031-7031,7017-7022,7054-7054,7058-7060" x="380.0" y="261.0" width="160.0" height="88.0" rx="7" />
|
||||
<text class="node-label" x="460.0" y="288.0">Host Block Layer</text>
|
||||
<line class="node-detail-divider" x1="394.0" y1="309.0" x2="526.0" y2="309.0" />
|
||||
<text class="node-detail" x="396.0" y="326.0">Host block request</text>
|
||||
<text class="node-detail" x="396.0" y="342.0">blk-mq</text>
|
||||
</g>
|
||||
<g id="node-nvme">
|
||||
<rect class="node-shape kind-device emphasis-normal role-sink" data-evidence="7031-7031,7019-7019,7066-7069" x="385.0" y="458.0" width="150.0" height="64.0" /><ellipse class="node-shape kind-device emphasis-normal role-sink" cx="460.0" cy="458.0" rx="75.0" ry="13.0" /><path class="storage-bottom" d="M 385.0 522.0 A 75.0 13.0 0 0 0 535.0 522.0" />
|
||||
<text class="node-label" x="460.0" y="488.0">NVMe</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 8.0 KiB |
@@ -0,0 +1,28 @@
|
||||
# fsync() 의 요구가 물리 storage 까지 내려가는 사슬
|
||||
|
||||
## Alternative text
|
||||
|
||||
PostgreSQL 의 fsync() 가 Guest Filesystem, Guest Block Layer, virtio-blk, QEMU / Backend, Host Storage Stack 을 차례로 지나 Physical Storage 에 닿는 위에서 아래로 읽는 흐름도.
|
||||
|
||||
## 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 가 서로 다르다는 구분은 본문의 코드블록이 맡는다.
|
||||
|
||||
## Elements and evidence
|
||||
|
||||
- **PostgreSQL** (service): 필요한 시점에 fsync() 를 불러 영속성 경계까지 반영을 요청하는 게스트 프로세스. Evidence: L6742–L6742, L6726–L6734.
|
||||
- **Guest Filesystem** (service): fsync() 요구를 처음 받는 게스트 파일시스템. Evidence: L6746–L6746.
|
||||
- **Guest Block Layer** (service): 요구를 FLUSH 등의 block 요청으로 바꾸는 계층. Evidence: L6749–L6751.
|
||||
- **virtio-blk** (service): 그 요청을 VM 경계 너머로 옮기는 게스트 driver. Evidence: L6753–L6753.
|
||||
- **QEMU / Backend** (service): 요청을 받아 호스트 쪽 파일 I/O 나 block I/O 로 잇는 자리. Evidence: L6756–L6756, L6686–L6686.
|
||||
- **Host Storage Stack** (service): 호스트 Page Cache 와 파일시스템, Block Layer 가 놓인 구간. Evidence: L6759–L6759, L6688–L6692.
|
||||
- **Physical Storage** (store): 요구가 여기까지 전달되어야 정전 이후 생존을 말할 수 있다. Evidence: L6762–L6765, L6729–L6729.
|
||||
|
||||
## Relationships
|
||||
|
||||
- **PostgreSQL → Guest Filesystem:** fsync(). Evidence: L6744–L6746.
|
||||
- **Guest Filesystem → Guest Block Layer:** control. Evidence: L6746–L6749.
|
||||
- **Guest Block Layer → virtio-blk:** FLUSH 등. Evidence: L6751–L6753.
|
||||
- **virtio-blk → QEMU / Backend:** control. Evidence: L6753–L6756.
|
||||
- **QEMU / Backend → Host Storage Stack:** control. Evidence: L6756–L6759.
|
||||
- **Host Storage Stack → Physical Storage:** control. Evidence: L6759–L6765.
|
||||
@@ -0,0 +1,30 @@
|
||||
# fsync() 의 요구가 물리 storage 까지 내려가는 사슬
|
||||
# Question: 게스트가 fsync() 를 부르면 그 요구는 어느 계층까지, 몇 번 손이 바뀌어 내려가야 하는가
|
||||
direction: down
|
||||
n0: "PostgreSQL" {
|
||||
shape: rectangle
|
||||
}
|
||||
n1: "Guest Filesystem" {
|
||||
shape: rectangle
|
||||
}
|
||||
n2: "Guest Block Layer" {
|
||||
shape: rectangle
|
||||
}
|
||||
n3: "virtio-blk" {
|
||||
shape: rectangle
|
||||
}
|
||||
n4: "QEMU / Backend" {
|
||||
shape: rectangle
|
||||
}
|
||||
n5: "Host Storage Stack" {
|
||||
shape: rectangle
|
||||
}
|
||||
n6: "Physical Storage" {
|
||||
shape: rectangle
|
||||
}
|
||||
n0 -> n1: "fsync()"
|
||||
n1 -> n2
|
||||
n2 -> n3: "FLUSH 등"
|
||||
n3 -> n4
|
||||
n4 -> n5
|
||||
n5 -> n6
|
||||
@@ -0,0 +1,62 @@
|
||||
<?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="write-completion-boundaries" name="fsync() 의 요구가 물리 storage 까지 내려가는 사슬">
|
||||
<mxGraphModel dx="680" dy="1153" 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">
|
||||
<root>
|
||||
<mxCell id="0"/>
|
||||
<mxCell id="1" parent="0"/>
|
||||
<mxCell id="n_postgresql" value="PostgreSQL<br/>write()<br/>fsync()" tooltip="필요한 시점에 fsync() 를 불러 영속성 경계까지 반영을 요청하는 게스트 프로세스. | Evidence: L6742-L6742, L6726-L6734" 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="75.0" y="60.0" width="150.0" height="88.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="n_guest-filesystem" value="Guest Filesystem" tooltip="fsync() 요구를 처음 받는 게스트 파일시스템. | Evidence: L6746-L6746" 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="75.0" y="244.0" width="150.0" height="64.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="n_guest-block-layer" value="Guest Block Layer" tooltip="요구를 FLUSH 등의 block 요청으로 바꾸는 계층. | Evidence: L6749-L6751" 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="73.5" y="404.0" width="153.0" height="64.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="n_virtio-blk" value="virtio-blk" tooltip="그 요청을 VM 경계 너머로 옮기는 게스트 driver. | Evidence: L6753-L6753" 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="75.0" y="564.0" width="150.0" height="64.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="n_qemu-backend" value="QEMU / Backend" tooltip="요청을 받아 호스트 쪽 파일 I/O 나 block I/O 로 잇는 자리. | Evidence: L6756-L6756, L6686-L6686" 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="75.0" y="724.0" width="150.0" height="64.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="n_host-storage-stack" value="Host Storage Stack" tooltip="호스트 Page Cache 와 파일시스템, Block Layer 가 놓인 구간. | Evidence: L6759-L6759, L6688-L6692" 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="884.0" width="160.0" height="64.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="n_physical-storage" value="Physical Storage" tooltip="요구가 여기까지 전달되어야 정전 이후 생존을 말할 수 있다. | Evidence: L6762-L6765, L6729-L6729" 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="75.0" y="1044.0" width="150.0" height="64.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e_app-to-guest-fs" value="fsync()" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_postgresql" target="n_guest-filesystem">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="174.0" y="196.0" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="e_guest-fs-to-block-layer" 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-filesystem" target="n_guest-block-layer">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="174.0" y="356.0" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="e_block-layer-to-virtio" value="FLUSH 등" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_guest-block-layer" target="n_virtio-blk">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="174.0" y="516.0" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="e_virtio-to-qemu" 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-blk" target="n_qemu-backend">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="174.0" y="676.0" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="e_qemu-to-host-stack" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_qemu-backend" target="n_host-storage-stack">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="174.0" y="836.0" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="e_host-stack-to-physical" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_host-storage-stack" target="n_physical-storage">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="174.0" y="996.0" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
</mxfile>
|
||||
@@ -0,0 +1,934 @@
|
||||
{
|
||||
"type": "excalidraw",
|
||||
"version": 2,
|
||||
"source": "techviz-harness",
|
||||
"elements": [
|
||||
{
|
||||
"id": "edge-app-to-guest-fs",
|
||||
"type": "arrow",
|
||||
"x": 150.0,
|
||||
"y": 148.0,
|
||||
"width": 0.0,
|
||||
"height": 96.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": 812660886,
|
||||
"version": 1,
|
||||
"versionNonce": 98088779,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"points": [
|
||||
[
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
96.0
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
"startBinding": {
|
||||
"elementId": "node-postgresql",
|
||||
"focus": 0,
|
||||
"gap": 4
|
||||
},
|
||||
"endBinding": {
|
||||
"elementId": "node-guest-filesystem",
|
||||
"focus": 0,
|
||||
"gap": 4
|
||||
},
|
||||
"startArrowhead": null,
|
||||
"endArrowhead": "arrow",
|
||||
"elbowed": true
|
||||
},
|
||||
{
|
||||
"id": "edge-label-app-to-guest-fs",
|
||||
"type": "text",
|
||||
"x": 129.0,
|
||||
"y": 184.0,
|
||||
"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": 106795923,
|
||||
"version": 1,
|
||||
"versionNonce": 801195523,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"fontSize": 13,
|
||||
"fontFamily": 5,
|
||||
"text": "fsync()",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "fsync()",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "edge-guest-fs-to-block-layer",
|
||||
"type": "arrow",
|
||||
"x": 150.0,
|
||||
"y": 308.0,
|
||||
"width": 0.0,
|
||||
"height": 96.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": 1352950235,
|
||||
"version": 1,
|
||||
"versionNonce": 1614356579,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"points": [
|
||||
[
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
96.0
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
"startBinding": {
|
||||
"elementId": "node-guest-filesystem",
|
||||
"focus": 0,
|
||||
"gap": 4
|
||||
},
|
||||
"endBinding": {
|
||||
"elementId": "node-guest-block-layer",
|
||||
"focus": 0,
|
||||
"gap": 4
|
||||
},
|
||||
"startArrowhead": null,
|
||||
"endArrowhead": "arrow",
|
||||
"elbowed": true
|
||||
},
|
||||
{
|
||||
"id": "edge-block-layer-to-virtio",
|
||||
"type": "arrow",
|
||||
"x": 150.0,
|
||||
"y": 468.0,
|
||||
"width": 0.0,
|
||||
"height": 96.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": 653332640,
|
||||
"version": 1,
|
||||
"versionNonce": 706884381,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"points": [
|
||||
[
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
96.0
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
"startBinding": {
|
||||
"elementId": "node-guest-block-layer",
|
||||
"focus": 0,
|
||||
"gap": 4
|
||||
},
|
||||
"endBinding": {
|
||||
"elementId": "node-virtio-blk",
|
||||
"focus": 0,
|
||||
"gap": 4
|
||||
},
|
||||
"startArrowhead": null,
|
||||
"endArrowhead": "arrow",
|
||||
"elbowed": true
|
||||
},
|
||||
{
|
||||
"id": "edge-label-block-layer-to-virtio",
|
||||
"type": "text",
|
||||
"x": 129.0,
|
||||
"y": 504.0,
|
||||
"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": 352541713,
|
||||
"version": 1,
|
||||
"versionNonce": 1745807882,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"fontSize": 13,
|
||||
"fontFamily": 5,
|
||||
"text": "FLUSH 등",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "FLUSH 등",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "edge-virtio-to-qemu",
|
||||
"type": "arrow",
|
||||
"x": 150.0,
|
||||
"y": 628.0,
|
||||
"width": 0.0,
|
||||
"height": 96.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": 1913137791,
|
||||
"version": 1,
|
||||
"versionNonce": 1422107469,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"points": [
|
||||
[
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
96.0
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
"startBinding": {
|
||||
"elementId": "node-virtio-blk",
|
||||
"focus": 0,
|
||||
"gap": 4
|
||||
},
|
||||
"endBinding": {
|
||||
"elementId": "node-qemu-backend",
|
||||
"focus": 0,
|
||||
"gap": 4
|
||||
},
|
||||
"startArrowhead": null,
|
||||
"endArrowhead": "arrow",
|
||||
"elbowed": true
|
||||
},
|
||||
{
|
||||
"id": "edge-qemu-to-host-stack",
|
||||
"type": "arrow",
|
||||
"x": 150.0,
|
||||
"y": 788.0,
|
||||
"width": 0.0,
|
||||
"height": 96.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": 1785251986,
|
||||
"version": 1,
|
||||
"versionNonce": 1697013013,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"points": [
|
||||
[
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
96.0
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
"startBinding": {
|
||||
"elementId": "node-qemu-backend",
|
||||
"focus": 0,
|
||||
"gap": 4
|
||||
},
|
||||
"endBinding": {
|
||||
"elementId": "node-host-storage-stack",
|
||||
"focus": 0,
|
||||
"gap": 4
|
||||
},
|
||||
"startArrowhead": null,
|
||||
"endArrowhead": "arrow",
|
||||
"elbowed": true
|
||||
},
|
||||
{
|
||||
"id": "edge-host-stack-to-physical",
|
||||
"type": "arrow",
|
||||
"x": 150.0,
|
||||
"y": 948.0,
|
||||
"width": 0.0,
|
||||
"height": 96.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": 574245066,
|
||||
"version": 1,
|
||||
"versionNonce": 83280906,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"points": [
|
||||
[
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
96.0
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
"startBinding": {
|
||||
"elementId": "node-host-storage-stack",
|
||||
"focus": 0,
|
||||
"gap": 4
|
||||
},
|
||||
"endBinding": {
|
||||
"elementId": "node-physical-storage",
|
||||
"focus": 0,
|
||||
"gap": 4
|
||||
},
|
||||
"startArrowhead": null,
|
||||
"endArrowhead": "arrow",
|
||||
"elbowed": true
|
||||
},
|
||||
{
|
||||
"id": "node-postgresql",
|
||||
"type": "rectangle",
|
||||
"x": 75.0,
|
||||
"y": 60.0,
|
||||
"width": 150.0,
|
||||
"height": 88.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": 51288073,
|
||||
"version": 1,
|
||||
"versionNonce": 91686637,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false
|
||||
},
|
||||
{
|
||||
"id": "node-label-postgresql",
|
||||
"type": "text",
|
||||
"x": 85.0,
|
||||
"y": 70.0,
|
||||
"width": 130.0,
|
||||
"height": 68.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": 308492034,
|
||||
"version": 1,
|
||||
"versionNonce": 936735075,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"fontSize": 15,
|
||||
"fontFamily": 5,
|
||||
"text": "PostgreSQL\nwrite()\nfsync()",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "PostgreSQL\nwrite()\nfsync()",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "node-guest-filesystem",
|
||||
"type": "rectangle",
|
||||
"x": 75.0,
|
||||
"y": 244.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": 1533824948,
|
||||
"version": 1,
|
||||
"versionNonce": 1692132304,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false
|
||||
},
|
||||
{
|
||||
"id": "node-label-guest-filesystem",
|
||||
"type": "text",
|
||||
"x": 85.0,
|
||||
"y": 254.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": 21094774,
|
||||
"version": 1,
|
||||
"versionNonce": 893088149,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"fontSize": 15,
|
||||
"fontFamily": 5,
|
||||
"text": "Guest Filesystem",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "Guest Filesystem",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "node-guest-block-layer",
|
||||
"type": "rectangle",
|
||||
"x": 73.5,
|
||||
"y": 404.0,
|
||||
"width": 153.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": 197573630,
|
||||
"version": 1,
|
||||
"versionNonce": 1359380081,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false
|
||||
},
|
||||
{
|
||||
"id": "node-label-guest-block-layer",
|
||||
"type": "text",
|
||||
"x": 83.5,
|
||||
"y": 414.0,
|
||||
"width": 133.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": 1437945492,
|
||||
"version": 1,
|
||||
"versionNonce": 1941177307,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"fontSize": 15,
|
||||
"fontFamily": 5,
|
||||
"text": "Guest Block Layer",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "Guest Block Layer",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "node-virtio-blk",
|
||||
"type": "rectangle",
|
||||
"x": 75.0,
|
||||
"y": 564.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": 805183008,
|
||||
"version": 1,
|
||||
"versionNonce": 387189353,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false
|
||||
},
|
||||
{
|
||||
"id": "node-label-virtio-blk",
|
||||
"type": "text",
|
||||
"x": 85.0,
|
||||
"y": 574.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": 1466921391,
|
||||
"version": 1,
|
||||
"versionNonce": 178652417,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"fontSize": 15,
|
||||
"fontFamily": 5,
|
||||
"text": "virtio-blk",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "virtio-blk",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "node-qemu-backend",
|
||||
"type": "rectangle",
|
||||
"x": 75.0,
|
||||
"y": 724.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": 88754261,
|
||||
"version": 1,
|
||||
"versionNonce": 960115709,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false
|
||||
},
|
||||
{
|
||||
"id": "node-label-qemu-backend",
|
||||
"type": "text",
|
||||
"x": 85.0,
|
||||
"y": 734.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": 18150914,
|
||||
"version": 1,
|
||||
"versionNonce": 118285455,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"fontSize": 15,
|
||||
"fontFamily": 5,
|
||||
"text": "QEMU / Backend",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "QEMU / Backend",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "node-host-storage-stack",
|
||||
"type": "rectangle",
|
||||
"x": 70.0,
|
||||
"y": 884.0,
|
||||
"width": 160.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": 185667777,
|
||||
"version": 1,
|
||||
"versionNonce": 165868748,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false
|
||||
},
|
||||
{
|
||||
"id": "node-label-host-storage-stack",
|
||||
"type": "text",
|
||||
"x": 80.0,
|
||||
"y": 894.0,
|
||||
"width": 140.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": 1032766674,
|
||||
"version": 1,
|
||||
"versionNonce": 1275711067,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"fontSize": 15,
|
||||
"fontFamily": 5,
|
||||
"text": "Host Storage Stack",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "Host Storage Stack",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "node-physical-storage",
|
||||
"type": "rectangle",
|
||||
"x": 75.0,
|
||||
"y": 1044.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": 748275136,
|
||||
"version": 1,
|
||||
"versionNonce": 1258638582,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false
|
||||
},
|
||||
{
|
||||
"id": "node-label-physical-storage",
|
||||
"type": "text",
|
||||
"x": 85.0,
|
||||
"y": 1054.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": 1619302994,
|
||||
"version": 1,
|
||||
"versionNonce": 56126538,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"fontSize": 15,
|
||||
"fontFamily": 5,
|
||||
"text": "Physical Storage",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "Physical Storage",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
}
|
||||
],
|
||||
"appState": {
|
||||
"gridSize": 10,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"currentItemFontFamily": 5
|
||||
},
|
||||
"files": {}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"harness_version": "0.2.0",
|
||||
"spec_id": "write-completion-boundaries",
|
||||
"spec_version": "1.1",
|
||||
"spec_sha256": "c7838bc8393b43c94ed4628db350f0fb26e90fc0d566201e471d49d533963699",
|
||||
"source_context": {
|
||||
"document": "docs/virtualization/final/document.md",
|
||||
"document_sha256": "181e2b3cc8a45bae81e7e8193d026937c4d586ae4495d3b2c323eb7e6abcfadd",
|
||||
"anchor": {
|
||||
"kind": "heading",
|
||||
"value": "150. `fsync()`가 필요한 이유",
|
||||
"line": 6723
|
||||
}
|
||||
},
|
||||
"outputs": [
|
||||
"write-completion-boundaries.svg",
|
||||
"write-completion-boundaries.drawio",
|
||||
"write-completion-boundaries.mmd",
|
||||
"write-completion-boundaries.d2",
|
||||
"write-completion-boundaries.excalidraw",
|
||||
"write-completion-boundaries.alt.md"
|
||||
],
|
||||
"lint_issue_count": 4,
|
||||
"assumption_count": 0,
|
||||
"assumptions_allowed": false,
|
||||
"composition_profile": "component-flow",
|
||||
"reference_ids": [
|
||||
"payment-event-flow"
|
||||
],
|
||||
"diagram_only": true
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
%% fsync() 의 요구가 물리 storage 까지 내려가는 사슬
|
||||
%% question: 게스트가 fsync() 를 부르면 그 요구는 어느 계층까지, 몇 번 손이 바뀌어 내려가야 하는가
|
||||
flowchart TB
|
||||
n0["PostgreSQL"]
|
||||
n1["Guest Filesystem"]
|
||||
n2["Guest Block Layer"]
|
||||
n3["virtio-blk"]
|
||||
n4["QEMU / Backend"]
|
||||
n5["Host Storage Stack"]
|
||||
n6["Physical Storage"]
|
||||
n0 -->|"fsync()"| n1
|
||||
n1 --> n2
|
||||
n2 -->|"FLUSH 등"| n3
|
||||
n3 --> n4
|
||||
n4 --> n5
|
||||
n5 --> n6
|
||||
@@ -0,0 +1,94 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="680" height="1153" viewBox="0 0 680 1153" role="img" aria-labelledby="diagram-title diagram-description">
|
||||
<title id="diagram-title">fsync() 의 요구가 물리 storage 까지 내려가는 사슬</title>
|
||||
<desc id="diagram-description">위에서 아래로 읽는다. 맨 위 PostgreSQL 이 fsync() 를 부르고 그 요구가 Guest Filesystem 으로 내려간다. Guest Block Layer 에서 FLUSH 등의 요청으로 바뀌고, virtio-blk 를 지나 VM 경계를 넘어 QEMU / Backend 로 간다. 거기서 Host Storage Stack 을 지나 맨 아래 Physical Storage 까지 그 의미가 전달되어야 완료가 성립한다. 여섯 번 손이 바뀌는 동안 한 곳이라도 그 의미를 지키지 않으면 게스트가 받은 완료 응답이 거짓이 된다. write() 완료와 writeback 완료, fsync/flush 완료, 전원 장애에도 안전한 durability 가 서로 다르다는 구분은 본문의 코드블록이 맡는다.</desc>
|
||||
<metadata>{"techviz":{"spec_version":"1.1","id":"write-completion-boundaries","profile":"component-flow"},"source_context":{"document":"docs/virtualization/final/document.md","document_sha256":"181e2b3cc8a45bae81e7e8193d026937c4d586ae4495d3b2c323eb7e6abcfadd","anchor":{"kind":"heading","value":"150. `fsync()`가 필요한 이유","line":6723}},"evidence_policy":"Each factual element cites source lines or is marked assumption.","diagram_only":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="680" height="1153" />
|
||||
<polyline class="edge kind-control style-solid emphasis-normal" points="150.0,148.0 150.0,196.0 150.0,196.0 150.0,244.0" data-evidence="6744-6746" />
|
||||
<rect class="edge-label-bg" x="141.6" y="182.0" width="64.9" height="22" rx="3" />
|
||||
<text class="edge-label" x="174.0" y="197.0">fsync()</text>
|
||||
<polyline class="edge kind-control style-solid emphasis-normal" points="150.0,308.0 150.0,356.0 150.0,356.0 150.0,404.0" data-evidence="6746-6749" />
|
||||
<polyline class="edge kind-control style-solid emphasis-normal" points="150.0,468.0 150.0,516.0 150.0,516.0 150.0,564.0" data-evidence="6751-6753" />
|
||||
<rect class="edge-label-bg" x="141.6" y="502.0" width="64.9" height="22" rx="3" />
|
||||
<text class="edge-label" x="174.0" y="517.0">FLUSH 등</text>
|
||||
<polyline class="edge kind-control style-solid emphasis-normal" points="150.0,628.0 150.0,676.0 150.0,676.0 150.0,724.0" data-evidence="6753-6756" />
|
||||
<polyline class="edge kind-control style-solid emphasis-normal" points="150.0,788.0 150.0,836.0 150.0,836.0 150.0,884.0" data-evidence="6756-6759" />
|
||||
<polyline class="edge kind-control style-solid emphasis-normal" points="150.0,948.0 150.0,996.0 150.0,996.0 150.0,1044.0" data-evidence="6759-6765" />
|
||||
<g id="node-postgresql">
|
||||
<rect class="node-shape kind-service emphasis-normal role-source" data-evidence="6742-6742,6726-6734" x="75.0" y="60.0" width="150.0" height="88.0" rx="7" />
|
||||
<text class="node-label" x="150.0" y="87.0">PostgreSQL</text>
|
||||
<line class="node-detail-divider" x1="89.0" y1="108.0" x2="211.0" y2="108.0" />
|
||||
<text class="node-detail" x="91.0" y="125.0">write()</text>
|
||||
<text class="node-detail" x="91.0" y="141.0">fsync()</text>
|
||||
</g>
|
||||
<g id="node-guest-filesystem">
|
||||
<rect class="node-shape kind-service emphasis-normal role-service" data-evidence="6746-6746" x="75.0" y="244.0" width="150.0" height="64.0" rx="7" />
|
||||
<text class="node-label" x="150.0" y="274.0">Guest Filesystem</text>
|
||||
</g>
|
||||
<g id="node-guest-block-layer">
|
||||
<rect class="node-shape kind-service emphasis-normal role-service" data-evidence="6749-6751" x="73.5" y="404.0" width="153.0" height="64.0" rx="7" />
|
||||
<text class="node-label" x="150.0" y="434.0">Guest Block Layer</text>
|
||||
</g>
|
||||
<g id="node-virtio-blk">
|
||||
<rect class="node-shape kind-service emphasis-normal role-service" data-evidence="6753-6753" x="75.0" y="564.0" width="150.0" height="64.0" rx="7" />
|
||||
<text class="node-label" x="150.0" y="594.0">virtio-blk</text>
|
||||
</g>
|
||||
<g id="node-qemu-backend">
|
||||
<rect class="node-shape kind-service emphasis-normal role-service" data-evidence="6756-6756,6686-6686" x="75.0" y="724.0" width="150.0" height="64.0" rx="7" />
|
||||
<text class="node-label" x="150.0" y="754.0">QEMU / Backend</text>
|
||||
</g>
|
||||
<g id="node-host-storage-stack">
|
||||
<rect class="node-shape kind-service emphasis-normal role-service" data-evidence="6759-6759,6688-6692" x="70.0" y="884.0" width="160.0" height="64.0" rx="7" />
|
||||
<text class="node-label" x="150.0" y="914.0">Host Storage Stack</text>
|
||||
</g>
|
||||
<g id="node-physical-storage">
|
||||
<rect class="node-shape kind-store emphasis-primary role-sink" data-evidence="6762-6765,6729-6729" x="75.0" y="1044.0" width="150.0" height="64.0" rx="7" />
|
||||
<text class="node-label" x="150.0" y="1074.0">Physical Storage</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 7.9 KiB |
@@ -0,0 +1 @@
|
||||
Playwright MCP 로 찍은 브라우저 캡처. 무엇을 찍었는지 한 줄을 적는다.
|
||||
@@ -0,0 +1 @@
|
||||
그 실행의 command·cwd·executedAt·exitCode·revision. 형식은 evidence.json.
|
||||
@@ -0,0 +1,2 @@
|
||||
명령 출력·csv·덤프 원문. 여기가 정본이다.
|
||||
하위 폴더를 자유롭게 둔다(explain/, guards/). 폴더마다 무엇을 담았는지 한 줄을 README.txt 에 적는다.
|
||||
@@ -0,0 +1,2 @@
|
||||
raw 에서 만든 터미널 SVG. 표현물이지 정본이 아니다.
|
||||
scripts/terminal-evidence/render_terminal.py 로 만든다.
|
||||
@@ -0,0 +1,197 @@
|
||||
---
|
||||
id: 5bcae89b-0873-4a2b-af4a-10e5234c085b
|
||||
kind: CONCEPT
|
||||
slug: kvm-vcpu-to-physical-cpu
|
||||
title: KVM 에서 vCPU 가 물리 CPU 에서 실행되기까지
|
||||
topic: cpu-virtualization
|
||||
topicName: CPU 가상화
|
||||
project: virtualization
|
||||
status: 초안
|
||||
basisVersion: x86_64 Intel VMX(VT-x) · Linux KVM(kvm · kvm_intel) · QEMU/libvirt · VM 안의 K3s cgroup CPU limit
|
||||
studio: "https://hyeonworks.com/studio/documents/5bcae89b-0873-4a2b-af4a-10e5234c085b/edit"
|
||||
assets:
|
||||
- key: vm-exit-handling-cycle
|
||||
file: ../../../final/assets/diagrams/vm-exit-handling-cycle/vm-exit-handling-cycle.svg
|
||||
sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
|
||||
source:
|
||||
- final/document.md#1-이-문서의-범위
|
||||
- final/document.md#2-전체-구조
|
||||
- final/document.md#3-각-구성요소의-역할
|
||||
- final/document.md#4-vcpu와-vcpu-thread
|
||||
- final/document.md#5-host-linux-scheduler와-실제-cpu
|
||||
- final/document.md#6-kvm_run과-guest-실행
|
||||
- final/document.md#7-vm-entry와-vm-exit
|
||||
- final/document.md#8-무엇이-실제로-vm-exit을-발생시키는가
|
||||
- final/document.md#9-vm-exit-이후-처리
|
||||
- final/document.md#10-guest가-idle이면-물리-cpu는-어떻게-되는가
|
||||
- final/document.md#11-vm의-4-vcpu는-정확히-무엇을-의미하는가
|
||||
- final/document.md#12-cpu-contention과-overcommit
|
||||
- final/document.md#13-steal-time
|
||||
- final/document.md#14-실제-linux에서-확인할-수-있는-것
|
||||
- final/document.md#15-cpu-가상화-관점에서-장애를-보는-방법
|
||||
- final/document.md#16-현재-keycloak-k3s-실험과의-관계
|
||||
- final/document.md#18-bare-metal-k3s와-vm-기반-k3s의-차이
|
||||
- final/document.md#19-이-ssot에서-파생될-concept
|
||||
- final/document.md#22-현재-단계의-핵심-claim
|
||||
- final/document.md#24-cpu-가상화-계층에서-발생할-수-있는-문제
|
||||
- final/document.md#25-cpu-문제를-계층별로-구분하는-진단표
|
||||
- final/document.md#26-현재-keycloak-실험에서-cpu-문제를-오판하지-않기-위한-기준
|
||||
- final/document.md#28-concept-->-open-question-->-case-적용-기준
|
||||
---
|
||||
|
||||
# KVM 에서 vCPU 가 물리 CPU 에서 실행되기까지
|
||||
|
||||
`virsh` 로 가상 머신을 시작하면 그 뒤로 CPU 를 쓰는 쪽은 QEMU 프로세스다. vCPU 하나마다 QEMU vCPU 스레드가 하나씩 있다. 그 스레드를 호스트 Linux 스케줄러가 다른 호스트 스레드와 나란히 놓고 논리 CPU 에 배치한다. 게스트 코드는 그 스레드가 `KVM_RUN` 을 호출한 뒤 VM Entry 를 지나 물리 CPU 에서 직접 실행된다. 하이퍼바이저가 개입해야 하는 조건을 만나면 VM Exit 으로 KVM 에 제어권이 넘어간다. Keycloak 멀티 노드 실험을 가상 머신 두 대 위에서 돌리다 보니, 이 경로를 알아 두면 게스트 안의 지연을 애플리케이션·저장소 문제와 호스트 자원 문제로 갈라 볼 수 있다.
|
||||
|
||||
## 관계
|
||||
|
||||
- **pinning 없이 vCPU 스레드는 호스트 논리 CPU 사이를 옮겨 다니는가**
|
||||
이 글은 vCPU 스레드가 호스트 스케줄러에 따라 다른 논리 CPU 에서 실행될 수 있다고만 적었다. 그 이동을 `PSR` 로 실제로 재는 질문이다.
|
||||
- **실제 작업에서 주로 발생하는 VM Exit 은 무엇인가**
|
||||
무엇이 VM Exit 을 부르는지는 여기 적었지만, 어떤 작업에서 어떤 이유의 Exit 이 얼마나 나오는지는 이 호스트에서 세지 않았다.
|
||||
- **가상 머신 두 대에 동시에 부하를 주면 이 호스트에서 vCPU 경쟁이 실제로 생기는가**
|
||||
경쟁과 초과 할당은 예시 숫자로 설명했다. 이 테스트 환경의 논리 CPU 수와 두 가상 머신의 vCPU 합은 SSOT 에 적혀 있지 않다.
|
||||
- **cgroup CPU 제한과 호스트 vCPU 경쟁을 지표로 가를 수 있는가**
|
||||
진단표는 두 원인을 다른 행으로 갈라 놓았다. 그 구분이 지표로도 갈리는지는 같은 크기의 지연을 두 원인으로 각각 재현해야 안다.
|
||||
- **운영 서버는 CPU 가상화 계층의 영향을 받는 구조인가**
|
||||
진단표의 어느 행부터 보는지가 이 답에 달려 있다.
|
||||
- **이 호스트의 NUMA 토폴로지는 가상 머신 성능을 고려해야 할 구조인가**
|
||||
이 글은 NUMA 를 진단표의 한 행으로만 두고 메모리 배치는 범위 밖으로 미뤘다. 단일 NUMA node 면 이 실험에서 우선순위를 낮추고, 다중이면 vCPU 와 메모리 배치를 따로 확인한다.
|
||||
|
||||
## 본문
|
||||
|
||||
<!-- body:start -->
|
||||
|
||||
## virsh 뒤에서 가상 머신을 실행하는 프로세스
|
||||
|
||||
`virsh` 는 libvirt 기반 가상 머신을 관리하는 명령줄 도구다. `virsh start ubuntu-vm` 을 실행하면 명령이 libvirt 로 전달되고 libvirt 가 QEMU 프로세스를 실행한다. 명령 전달이 끝나면 `virsh` 자체는 종료될 수 있고, 가상 머신을 실제로 실행하는 QEMU 프로세스는 계속 살아 있다.
|
||||
|
||||
libvirt 는 가상 머신의 수명 주기와 구성을 관리하는 계층이다. 가상 머신 정의에는 RAM 8 GiB, vCPU 4 같은 값이 들어 있다. libvirt 는 그 정의를 바탕으로 QEMU 를 적절한 옵션과 함께 실행하고 관리한다.
|
||||
|
||||
QEMU 는 호스트의 사용자 공간에서 실행되는 프로세스다. 4 vCPU 가상 머신이라면 메인·제어 스레드 하나와 vCPU 스레드 넷이 만들어진다. KVM 가속을 쓸 때 게스트의 일반 CPU 명령을 QEMU 가 하나씩 소프트웨어로 번역해 실행하는 경로가 핵심은 아니다. vCPU 스레드가 KVM 을 통해 게스트 실행을 요청하면 게스트 코드는 VMX 를 이용해 실제 CPU 에서 직접 실행된다.
|
||||
|
||||
`/dev/kvm` 은 프로세스가 아니라 Linux 가 사용자 공간 프로그램에 KVM API 를 노출하는 문자 장치(character device) 인터페이스다. QEMU 는 이 파일을 열고 `ioctl` 로 `KVM_CREATE_VM`, `KVM_CREATE_VCPU`, `KVM_SET_USER_MEMORY_REGION`, `KVM_RUN` 같은 동작을 요청한다.
|
||||
|
||||
커널 안쪽은 두 층으로 갈린다. KVM Core 가 CPU 제조사에 독립적인 공통 가상화 로직을 맡고, 제조사별 구현은 `kvm_intel` 과 `kvm_amd` 로 나뉜다. `kvm_intel` 은 Intel CPU 환경에서 KVM 이 Intel 의 하드웨어 가상화 기능을 쓸 수 있게 하는 커널 모듈이다. AMD 환경에서는 `kvm_amd` 가 SVM 을 쓴다.
|
||||
|
||||
VMX(Virtual Machine Extensions)는 Intel CPU 자체가 제공하는 하드웨어 가상화 기능이어서 프로세스도 커널 모듈도 아니다. VMX 는 실행 영역을 호스트·하이퍼바이저 쪽인 VMX Root Operation 과 게스트 쪽인 VMX Non-Root Operation 으로 구분한다. 여기서 Root 는 Linux 의 `root` 사용자와 무관하다. 게스트 Linux 에서 `root` 권한으로 프로그램을 실행하더라도 게스트 전체는 VMX 관점에서 여전히 Non-Root Operation 에서 실행된다.
|
||||
|
||||
## vCPU 하나마다 QEMU 스레드 하나
|
||||
|
||||
가상 머신에 4 vCPU 를 설정하면 게스트 OS 는 그것을 자신의 CPU 처럼 인식한다. 호스트에는 각 vCPU 의 실행 주체에 대응하는 QEMU vCPU 스레드가 하나씩 존재한다. 4 vCPU 는 게스트 OS 가 최대 4개의 CPU 실행 흐름을 가질 수 있도록 4개의 가상 CPU 실행 컨텍스트를 제공한다는 의미에 가깝다. 호스트의 물리 CPU 4개가 그 가상 머신으로 영구히 넘어가지는 않는다.
|
||||
|
||||
vCPU 스레드가 실행될 논리 CPU 를 특정 집합으로 제한하는 설정을 CPU pinning 이라고 한다. pinning 도 별도의 CPU 격리도 하지 않았다면 vCPU 스레드는 호스트 Linux 스케줄러가 스케줄링한다. 호스트가 6 Core / 12 Thread 라면 Linux 에서는 일반적으로 12개의 논리 CPU 가 스케줄링 대상으로 보인다. vCPU 스레드도 Chrome 스레드나 Java 스레드와 마찬가지로 그 12개 위에 배치된다. 그래서 시간에 따라 같은 스레드가 서로 다른 논리 CPU 에서 실행될 수도 있다. 다만 이 호스트에서 그 이동을 재지는 않았다. 스레드가 최근 실행된 논리 CPU 를 알려 주는 `PSR` 값을 시간에 따라 여러 번 보면 확인되는데, 그 관찰을 아직 돌리지 않았다.
|
||||
|
||||
호스트 CPU 가 부족하면 QEMU vCPU 스레드와 호스트의 다른 작업이 같은 논리 CPU 자원을 두고 경쟁한다.
|
||||
|
||||
## KVM_RUN 이 게스트 실행을 시작한다
|
||||
|
||||
QEMU 의 vCPU 스레드가 게스트 vCPU 를 실행하려면 KVM 에 `KVM_RUN` 을 요청한다.
|
||||
|
||||
```c label="vCPU thread 가 Guest 실행을 요청하는 호출"
|
||||
ioctl(vcpu_fd, KVM_RUN, 0);
|
||||
```
|
||||
|
||||
KVM 은 이 요청을 받아 VM Entry 로 CPU 가 게스트 실행을 시작하거나 재개하게 한다. 이 상태에서 `ADD`, `MOV`, `SUB`, `CMP`, `JMP` 같은 게스트의 일반 명령은 실제 CPU 에서 직접 실행된다. 명령 하나마다 QEMU 까지 돌아갔다가 다시 실행하는 구조가 아니다.
|
||||
|
||||
## 무엇이 VM Exit 을 부르는가
|
||||
|
||||
VM Exit 은 가상 머신 종료가 아니다. CPU 가 VMX Non-Root 에서 게스트를 실행하다가 하이퍼바이저가 개입해야 하는 조건을 만나면, 게스트 실행에서 빠져나와 VMX Root 쪽 KVM 으로 제어권을 넘긴다. QEMU 가 종료되지도, 가상 머신 메모리가 지워지지도 않는다. 필요한 처리가 끝나면 다시 VM Entry 를 통해 게스트 실행을 이어 간다.
|
||||
|
||||
무엇을 가로챌지는 Intel VMX 의 VMCS(Virtual Machine Control Structure)에 있는 VM-Execution Control 등이 정한다. 특권 명령이면 전부 VM Exit 이라거나 `root` 가 실행하면 VM Exit 이라는 규칙은 맞지 않는다. Exit 여부는 VMX 설정과 해당 동작의 종류에 따라 정해진다.
|
||||
|
||||
| 무엇이 실행되나 | 가로채는 설정과 그 이유 |
|
||||
|---|---|
|
||||
| `HLT` 계열 | 게스트 커널이 유휴 경로로 들어갔다. `HLT` exiting 을 쓰면 KVM 이 vCPU 를 재울 수 있다 |
|
||||
| `IN` · `OUT` | I/O port 접근을 QEMU 의 가상 장치 동작으로 처리하도록 설정할 수 있다 |
|
||||
| `CPUID` | 게스트에게 보여 줄 CPU 모델과 기능이 호스트 CPU 와 달라 CPU 정보를 가상화한다 |
|
||||
| `CR0` · `CR3` · `CR4` 접근 | VMX 설정으로 어떤 접근을 가로챌지 고른다. 모든 접근이 Exit 을 부르지는 않는다 |
|
||||
| `RDMSR` · `WRMSR` | 특정 MSR(Model-Specific Register) 접근을 가로채도록 설정했을 때 발생한다 |
|
||||
| 예외 | Exception Bitmap 설정에 따라 게스트가 직접 처리할 수도, 하이퍼바이저가 가로챌 수도 있다 |
|
||||
| 외부 인터럽트 | 호스트가 처리해야 할 물리 인터럽트가 들어왔고 인터럽트 설정이 그렇게 돼 있다 |
|
||||
|
||||
`CR3` 처럼 페이지 테이블과 관련된 레지스터는 게스트 커널도 자주 쓴다. 그런 접근까지 전부 가로채면 Exit 이 늘어나기 때문에 현대 가상화에서는 불필요한 Exit 을 줄이는 쪽으로 설정한다.
|
||||
|
||||
## Exit 을 KVM 이 처리할 때와 QEMU 까지 돌아갈 때
|
||||
|
||||
VM Exit 이 발생하면 KVM 은 먼저 Exit Reason 을 확인한다. KVM 이 커널 안에서 처리할 수 있는 Exit 은 처리한 뒤 바로 게스트로 재진입한다. 사용자 공간에서 가상 장치를 흉내 내야 할 때처럼 사용자 공간 처리가 필요할 때만 `KVM_RUN` 이 반환된다. 그러면 QEMU 가 필요한 가상 장치 동작을 처리한 뒤 다시 `KVM_RUN` 을 호출해 게스트 실행을 재개한다.
|
||||
|
||||

|
||||
|
||||
VM Exit 자체는 정상적인 가상화 동작이어서 Exit 이 있다고 문제가 되지 않는다. 어떤 작업에서 하이퍼바이저가 개입해야 하는 Exit 이 지나치게 빈번하고 그 처리 비용이 커지면 성능에 영향을 줄 수 있다. 그래서 Exit 횟수만 세지 않는다. Exit 이 난 이유와 작업 종류, 처리한 곳이 KVM 인지 QEMU 사용자 공간인지, 애플리케이션 지연이 Exit 증가와 같은 시점에 나타나는지를 같이 본다.
|
||||
|
||||
## 게스트에 할 일이 없으면 물리 CPU 가 풀린다
|
||||
|
||||
게스트에 실행할 작업이 없으면 게스트 커널은 유휴 경로로 들어가고 `HLT` 계열 동작이 사용될 수 있다. KVM 과 VMX 가 `HLT` exiting 을 쓴다면 여기서 VM Exit 이 발생한다. KVM 은 vCPU 가 당장 할 일이 없는 상태를 처리하면서 그 스레드를 재우거나 대기 상태로 둘 수 있다. 그동안 호스트 스케줄러는 그 논리 CPU 를 호스트의 다른 작업에 쓴다.
|
||||
|
||||
나중에 타이머, 인터럽트, I/O 완료처럼 vCPU 를 다시 실행해야 할 이유가 생기면 vCPU 가 깨어나 실행 대기 상태가 된다. 호스트 스케줄러가 그 스레드를 논리 CPU 에 배치하면 KVM 이 VM Entry 로 게스트 실행을 재개한다. 가상 머신에 4 vCPU 를 설정했어도 그 가상 머신에 할 일이 없는 동안에는 호스트가 CPU 자원을 다른 작업에 쓴다.
|
||||
|
||||
## CPU 가 모자랄 때 게스트 안에서 보이는 값
|
||||
|
||||
호스트에 논리 CPU 가 12개 있고 8 vCPU 가상 머신을 네 대 올리면 32 vCPU 가 12개의 논리 CPU 위에서 실행될 수 있다. 그 가상 머신들이 대부분 유휴 상태면 이 구성 자체가 장애를 뜻하지 않는다. 여러 가상 머신의 vCPU 가 동시에 실행 대기 상태가 될 때 CPU 경쟁과 스케줄링 지연이 늘어난다.
|
||||
|
||||
vCPU 스레드끼리만 경쟁하지도 않는다. 호스트에 Nginx 를 직접 설치하고 가상 머신 두 대를 실행하는 테스트 환경이라면 호스트 쪽 Nginx 와 K3s, DB, Redis, 그 밖의 호스트 프로세스도 같은 Linux 스케줄러를 거친다.
|
||||
|
||||
게스트 안에서 이 경쟁이 드러나는 값이 steal time 이다. 게스트 Linux 의 `top` 에서 `%st` 로 확인할 수 있다. 게스트 vCPU 에 실행할 작업이 있고 호스트에서 그 스레드가 CPU 를 필요로 하는데, 다른 작업 때문에 즉시 실행되지 못한 시간을 나타낸다. 이 값이 높으면 호스트 CPU 경쟁이나 논리 CPU 보다 많은 vCPU 를 할당한 초과 할당을 의심할 수 있지만, 단독으로 원인을 확정하는 지표는 아니다. 호스트 CPU 포화와 실행 대기열, 친화도, 어떤 작업이 돌고 있었는지를 함께 확인한다.
|
||||
|
||||
vCPU 스레드가 실행 대기 상태가 되어도 호스트 스케줄러가 실제 논리 CPU 에 배치할 때까지 기다릴 수 있다. 호스트가 포화될수록 이 대기 시간이 길어지고 게스트에서는 애플리케이션 지연 증가로 보인다. 그래서 vCPU 수를 늘린다고 항상 빨라지지 않는다. 게스트 쪽 작업이 그만큼의 병렬성을 쓰지 못하거나 호스트 전체 CPU 에 비해 vCPU 가 지나치게 많으면 스케줄링 대상만 늘어난다. pinning 도 잘못 설정하면 특정 CPU 에 작업이 몰리기 때문에 pinning 여부만 보지 않고 CPU 별 사용률과 친화도도 본다.
|
||||
|
||||
## 어느 계층에서 느려졌는지 가르는 표
|
||||
|
||||
가상 머신 안의 Keycloak 이 느려졌을 때 CPU 경로는 애플리케이션에서 물리 CPU 까지 여러 층을 지난다. Keycloak 파드는 K3s 의 cgroup CPU 상한 아래에서 실행된다. 그 아래로 게스트 Linux 와 vCPU, QEMU vCPU 스레드, 호스트 Linux 스케줄러, KVM 과 VMX, 물리 CPU 와 NUMA 가 이어진다. 같은 「CPU 가 느리다」는 현상도 층마다 원인이 다르다.
|
||||
|
||||
| 어떤 문제인가 | 주로 어느 계층인가 | 무엇부터 확인하나 |
|
||||
|---|---|---|
|
||||
| 게스트 CPU 포화 | 게스트 | 게스트 CPU, 프로세스·스레드, load |
|
||||
| CPU 제한(throttling) | K3s / cgroup | CPU 상한, throttled time |
|
||||
| vCPU 과다 할당 | 가상 머신 구성 | vCPU 수, 작업 병렬성 |
|
||||
| CPU 초과 할당 | 호스트 구성 | 전체 vCPU, 호스트 논리 CPU |
|
||||
| CPU 경쟁 | 호스트 스케줄러 | 호스트 CPU, 실행 대기열, CPU 별 사용률 |
|
||||
| steal time 증가 | 게스트에서 관측 | `%st`, 호스트 경쟁 |
|
||||
| 스케줄링 지연 | 호스트 스케줄러 | 실행 대기열, 스케줄러 관찰 |
|
||||
| 잘못된 pinning | 호스트 / 가상 머신 설정 | 친화도, CPU 별 사용률 |
|
||||
| 과도한 VM Exit | KVM / VMX | Exit 횟수와 이유, 작업 |
|
||||
| 호스트 CPU 포화 | 호스트 | 호스트 CPU·load·실행 대기열 |
|
||||
| NUMA locality | 하드웨어 / 메모리 | NUMA 토폴로지, CPU·메모리 배치 |
|
||||
|
||||
이 표로 하나의 지표를 골라 원인을 확정하지 않는다. 어느 계층부터 조사할지 범위를 줄이는 데 쓴다.
|
||||
|
||||
특히 두 원인이 겹쳐 보인다. 호스트 CPU 를 제때 받지 못하는 문제는 경쟁과 steal time, 스케줄링 쪽이고, 파드가 자신에게 걸린 CPU 상한을 넘겨 제한되는 문제는 K3s cgroup 쪽이다. 호스트 CPU 에 여유가 있어도 Keycloak 파드는 설정된 상한 때문에 실행이 제한될 수 있다. cgroup 제한은 KVM 문제가 아니지만 가상 머신 안에서 K3s 를 운영하는 이 실험에서는 같은 애플리케이션 지연으로 관찰되기 때문에 진단 경계에 포함한다.
|
||||
|
||||
호스트 OS 에 K3s 를 직접 설치했다면 컨테이너의 프로세스는 호스트 커널을 공유하고 호스트 스케줄러가 직접 스케줄링하는 대상이어서 QEMU 와 `/dev/kvm`, KVM, VMX 를 지나지 않는다. 가상 머신 안에 K3s 를 구성하면 게스트 Linux 와 vCPU, QEMU vCPU 스레드, KVM 과 VMX 계층이 그 아래에 더해진다. 같은 부하 테스트라도 가상 머신 기반 환경에서는 호스트 가상화 자원 병목을 추가로 확인한다. 운영 서버에서는 위 표의 어느 행부터 보는지가 달라질 수 있다. 운영 서버가 호스트에 K3s 를 직접 설치한 구성인지 상위 하이퍼바이저나 클라우드 가상 머신 위에 있는지를 아직 확인하지 않았기 때문이다. 부하를 걸어 재야 아는 것이 아니라 서버 구성을 한 번 확인하면 답이 정해진다.
|
||||
|
||||
## 호스트에서 이 경로를 확인하는 명령
|
||||
|
||||
이 경로가 실제 호스트에서도 그대로인지는 명령으로 하나씩 확인할 수 있다. CPU 가 하드웨어 가상화를 지원하는지는 플래그로 본다.
|
||||
|
||||
```bash label="Intel 은 vmx, AMD 는 svm flag 를 확인한다"
|
||||
grep -E 'vmx|svm' /proc/cpuinfo
|
||||
```
|
||||
|
||||
커널 모듈은 `lsmod | grep kvm` 으로, KVM API 진입점은 `ls -l /dev/kvm` 으로 확인한다. 실행 중인 가상 머신은 `virsh list` 로 본다. `ps -ef | grep '[q]emu'` 를 실행하면 가상 머신이 살아 있는 동안 함께 남아 있는 쪽이 `virsh` 가 아니라 QEMU 프로세스임을 확인할 수 있다.
|
||||
|
||||
QEMU 프로세스의 스레드는 `ps -T -p <QEMU_PID>` 나 `top -H -p <QEMU_PID>` 로 본다. 스레드 이름은 환경과 QEMU 버전에 따라 다를 수 있지만 vCPU 관련 스레드를 호스트에서 관찰할 수 있다. 그 스레드가 최근 어느 논리 CPU 에서 실행됐는지는 `PSR` 열이 알려 준다.
|
||||
|
||||
```bash label="vCPU thread 가 최근 실행된 logical CPU"
|
||||
ps -eLo pid,tid,psr,pcpu,comm | grep qemu
|
||||
```
|
||||
|
||||
여기 보이는 값은 vCPU 가 물리 CPU 에 영구 고정돼 있다는 뜻이 아니라, pinning 을 하지 않았을 때 스케줄링에 따라 달라지는 값이다. 게스트 쪽에서는 `top` 등으로 steal time 을 확인하고, 환경이 지원하면 `perf kvm` 으로 KVM 관련 실행 통계를 본다.
|
||||
|
||||
```bash label="KVM Exit 관찰"
|
||||
sudo perf kvm stat live
|
||||
```
|
||||
|
||||
지원되는 명령과 표시되는 Exit 이유는 커널과 `perf` 버전, CPU 아키텍처 및 설정에 따라 다르기 때문에 그 환경에서 무엇이 되는지 `perf kvm --help` 로 확인한다. 필요하면 KVM tracepoint 를 이용한 별도 추적도 검토한다.
|
||||
|
||||
## 이 문서가 확정하지 않는 것
|
||||
|
||||
CPU 가상화 개념에서 확정하는 수준은 셋까지다. 구조적으로 어떤 문제가 발생할 수 있는가, 어떤 지표로 그 문제를 의심할 수 있는가, 어느 계층에서 확인해야 하는가. 현재 테스트 서버에서 실제로 그 문제가 일어나는지는 여기서 사실로 확정하지 않는다.
|
||||
|
||||
이 호스트에서 잰 값은 하나도 없다. 관찰 명령은 무엇을 볼 수 있는지 적어 둔 목록이다. VM Exit 분포도 steal time 도, pinning 없이 vCPU 스레드가 논리 CPU 사이를 옮겨 다니는지도 아직 측정하지 않았다. 검증은 VMX 와 SVM 확인에서 시작한다. 그다음 KVM 모듈과 `/dev/kvm`, `virsh` 가상 머신, QEMU 프로세스, vCPU 스레드, 호스트 논리 CPU 스케줄링, 게스트가 유휴일 때와 부하를 받을 때의 비교, steal time, VM Exit 관찰로 이어진다. 값이 나오면 각각 재현 조건을 붙인 기록으로 남기고, 그때도 답하지 못한 항목은 열린 질문으로 유지한다.
|
||||
|
||||
범위 밖으로 미룬 영역도 있다. 네트워크·메모리·스토리지 가상화와 PCIe/VFIO/IOMMU 상세, K3s 네트워크와 컨테이너 런타임 상세는 이 문서가 다루지 않는다. NUMA 는 문제가 생길 수 있다는 것과 CPU 친화도와의 연관까지만 적었다. 메모리 배치와 NUMA 튜닝은 메모리 가상화 개념 문서에서 다룬다.
|
||||
|
||||
Keycloak refresh token 경쟁도 CPU 가상화 문제와 같은 문제가 아니다. 다만 가상 머신 기반 실험 환경의 CPU 경쟁이 실험 결과를 왜곡할 수 있어서 두 문제를 분리해 측정한다. 그래서 그 경쟁을 검증하는 첫 실험에서는 CPU 자원을 여유 있게 유지한 채 같은 세션과 같은 refresh token 에 대한 동시 요청을 만든다. 트래픽을 올려 자원 포화를 보는 부하·스트레스 실험은 따로 돌린다. 나눠 돌리는 이유는 결과를 「동일 상태에 동시에 접근해서 발생한 문제」와 「시스템 자원이 부족해져서 발생한 문제」로 갈라 읽기 위해서다. 한 실험에서 둘을 같이 일으키면 원인을 분리하기 어려워진다.
|
||||
|
||||
<!-- body:end -->
|
||||
@@ -0,0 +1,98 @@
|
||||
---
|
||||
id: d103bb81-45df-402f-86e5-e41d66ed7f8d
|
||||
kind: QUESTION
|
||||
slug: exit-distribution-for-keycloak-workload
|
||||
title: Keycloak 작업의 VM Exit 분포는 idle · CPU-bound · I/O-bound 와 어떻게 다른가
|
||||
topic: cpu-virtualization
|
||||
topicName: CPU 가상화
|
||||
project: virtualization
|
||||
status: 초안
|
||||
questionStatus: OPEN
|
||||
studio: "https://hyeonworks.com/studio/documents/d103bb81-45df-402f-86e5-e41d66ed7f8d/edit"
|
||||
sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
|
||||
source:
|
||||
- final/document.md#27-문제-영역에서-파생되는-추가-open-question-oq-11
|
||||
- final/document.md#24-cpu-가상화-계층에서-발생할-수-있는-문제-24-9
|
||||
- final/document.md#14-실제-linux에서-확인할-수-있는-것-14-9
|
||||
---
|
||||
|
||||
# Keycloak 작업의 VM Exit 분포는 idle · CPU-bound · I/O-bound 와 어떻게 다른가
|
||||
|
||||
VM Exit 은 게스트를 실행하던 CPU 가 하이퍼바이저가 개입해야 하는 조건을 만나 제어권을 KVM 쪽으로 넘기는 전환이다. 정상적인 가상화 동작이라 Exit 이 있다는 것만으로 문제가 되지 않는다. 다만 하이퍼바이저가 개입해야 하는 Exit 이 특정 작업에서 지나치게 빈번하고 처리 비용이 커지면 성능에 영향을 줄 수 있다. Keycloak 을 돌리는 구간이 그런 작업인지 보려면 다른 구간의 Exit 분포와 견줘야 하는데, 이 호스트에서 Exit 을 관측하는 도구가 열리는지부터 확인하지 않았다.
|
||||
|
||||
## 관계
|
||||
|
||||
- **KVM 에서 vCPU 가 물리 CPU 에서 실행되기까지**
|
||||
무엇이 VM Exit 을 부르고 그 처리가 커널에서 끝나는지 QEMU 사용자 공간까지 가는지를 이 개념이 설명한다.
|
||||
- **실제 작업에서 주로 발생하는 VM Exit 은 무엇인가**
|
||||
그쪽은 작업 다섯의 분포를 서로 견주는 물음이고, 이 질문은 Keycloak 구간의 분포가 같은 구간의 지연과 같이 움직이는지를 묻는다.
|
||||
다섯 구간을 한 번 받으면 두 질문의 자료가 함께 나오지만 닫는 기준이 달라 기록은 따로 둔다. 도구가 이 환경에서 열리는지 보는 첫 단계도 둘이 같이 쓴다.
|
||||
|
||||
## 사실
|
||||
|
||||
- VM Exit 은 정상적인 가상화 동작이어서 Exit 이 존재한다는 것만으로 문제가 되지 않는다.
|
||||
- 하이퍼바이저가 개입해야 하는 Exit 이 특정 작업에서 지나치게 빈번하고 처리 비용이 커지면 성능에 영향을 줄 수 있다.
|
||||
- 확인할 때 함께 보는 것은 넷이다.
|
||||
Exit 이유
|
||||
작업 종류
|
||||
Exit 처리 위치가 KVM 인지 QEMU 사용자 공간인지
|
||||
애플리케이션 지연과 Exit 증가가 함께 나타나는지
|
||||
- Exit 이 많다는 관측 하나로 장애라고 판단하지 않는다.
|
||||
- 환경이 지원하면 perf kvm 으로 KVM 관련 실행 통계를 확인할 수 있고, 예로 든 명령은 sudo perf kvm stat live 다.
|
||||
- 지원되는 명령과 표시되는 Exit 이유는 커널과 perf 버전, CPU 아키텍처와 설정에 따라 다를 수 있어 perf kvm --help 를 함께 확인한다. 필요하면 KVM tracepoint 를 이용한 별도 추적도 검토한다.
|
||||
- 비교 후보로 적어 둔 구간은 idle, CPU-bound 작업, I/O 가 많은 작업, Keycloak 정상 요청, Keycloak 부하 테스트 다섯이다.
|
||||
이 목록은 개념 문서가 앞선 물음 자리에 적어 둔 것이고, 이 질문의 제목이 든 것은 그 가운데 앞쪽 셋이다. 받아야 하는 구간은 제목이 든 것보다 둘 많다.
|
||||
|
||||
## 가정
|
||||
|
||||
- 이 호스트에서 perf kvm 이나 KVM tracepoint 가운데 하나는 열린다고 보고 측정 순서를 짠다.
|
||||
둘 다 열리지 않는 결과도 이 질문의 정상적인 답이다.
|
||||
- 다섯 구간을 같은 도구로 같은 길이만큼 잴 수 있다고 전제한다.
|
||||
- Keycloak 부하 구간의 지연은 실험이 이미 재고 있는 값에서 가져올 수 있다고 본다.
|
||||
- I/O 가 많은 구간을 이 환경에서 따로 만들 수 있다고 보고 짰다. 만드는 방법은 개념 문서에 적혀 있지 않다.
|
||||
|
||||
## 미지수
|
||||
|
||||
- 이 호스트에서 perf kvm 의 어떤 하위 명령이 열리는지, 열리지 않으면 tracepoint 쪽이 열리는지.
|
||||
- Keycloak 구간의 Exit 이유 분포가 나머지 네 구간과 얼마나 다른지.
|
||||
- 그 차이가 같은 구간의 Keycloak 지연과 같이 움직이는지.
|
||||
- Exit 처리 위치가 KVM 인지 QEMU 사용자 공간인지를 이 환경의 출력으로 가를 수 있는지.
|
||||
- 관측이 열린다면 얼마나 오래 받아야 구간 사이의 차이가 잡히는지.
|
||||
|
||||
## 제약
|
||||
|
||||
- 표시되는 Exit 이유는 커널과 perf 버전, CPU 아키텍처와 설정에 따라 달라지므로 다른 환경에서 나온 분포와 이 호스트의 분포를 바로 견주지 않는다.
|
||||
- Exit 횟수만 세지 않는다. Exit 이유와 작업 종류, 처리 위치, 같은 구간의 지연을 함께 받아 적는다.
|
||||
- 도구가 열려야 측정이 시작된다. 열리지 않으면 이 질문은 값 없이 닫힌다.
|
||||
- sudo 가 필요한 명령이라 이 호스트에서 그 권한으로 실행할 수 있어야 한다.
|
||||
- 이 호스트에서 Exit 을 실제로 받아 본 기록이 없다.
|
||||
|
||||
## 선택지
|
||||
|
||||
### 1. perf kvm stat live 로 다섯 구간을 차례로 받는다
|
||||
|
||||
perf kvm --help 로 열리는 하위 명령을 먼저 확인하고, 열리면 다섯 구간을 각각 돌리며 Exit 이유 분포를 받는다.
|
||||
같은 도구로 다섯을 받으므로 구간 사이의 차이를 그대로 견줄 수 있다.
|
||||
|
||||
구간마다 부하를 따로 만들어야 하고, 분포가 관측 길이에 따라 흔들리면 같은 구간을 여러 번 받아야 한다.
|
||||
|
||||
### 2. KVM tracepoint 를 직접 켜서 받는다
|
||||
|
||||
perf kvm 이 이 커널과 perf 버전에서 열리지 않을 때 검토하는 방법이다.
|
||||
tracepoint 를 직접 켜면 Exit 이유를 더 세밀하게 받을 수 있다.
|
||||
|
||||
받은 결과를 다섯 구간에 걸쳐 같은 형태로 정리하는 일이 첫 번째 방법보다 손이 많이 가서, perf kvm 이 열리지 않는 것을 확인한 뒤에 간다.
|
||||
|
||||
### 3. Keycloak 구간만 받아 절대값으로 판단한다 — 제외
|
||||
|
||||
Keycloak 구간의 Exit 수가 크면 Exit 처리 비용이 있다고 읽는 방법이다.
|
||||
Exit 이 많다는 관측 하나로 장애라고 판단하지 않기로 했고, 무엇과 견줘야 「많다」인지도 이 호스트에는 아직 기준이 없다. 그래서 다른 구간 없이 Keycloak 구간만 받지 않는다.
|
||||
|
||||
## 다음 검증
|
||||
|
||||
1. perf kvm --help 로 이 환경이 지원하는 하위 명령을 확인한다.
|
||||
2. 열리면 sudo perf kvm stat live 로 idle, CPU-bound, I/O 가 많은 구간, Keycloak 정상 요청, Keycloak 부하 다섯의 Exit 이유 분포를 받는다.
|
||||
3. 같은 다섯 구간의 Keycloak 지연을 실험이 이미 재는 값에서 가져와 분포 옆에 적는다.
|
||||
4. perf kvm 이 열리지 않으면 KVM tracepoint 를 쓰는 쪽을 검토하고, 검토 결과와 그 판단 근거도 함께 기록한다.
|
||||
|
||||
닫는 조건 : Keycloak 구간의 Exit 분포가 다른 구간과 다르고 그 차이가 지연과 같이 움직이면 그 측정이 Case 가 되고 닫는다. 분포가 다르지 않거나 지연과 따로 움직이면, 이 작업에서 Exit 처리 비용은 조사 대상이 아니라고 적고 진단표의 「과도한 VM Exit」 행을 뒤로 미루는 근거로 남긴 뒤 닫는다.
|
||||