{ "version": "1.1", "id": "cpu-io-passthrough-paths", "title": "게스트가 하드웨어에 닿는 갈래는 셋이고 깊이가 다르다", "question": "게스트의 CPU 실행·virtio I/O·직접 할당한 장치는 각각 어디까지 내려가고 호스트 유저공간을 지나는가", "type": "architecture", "direction": "TB", "audience": [ "가상화 구조를 처음 읽는 사람", "이 실험대가 무엇 위에서 도는지 알아야 하는 사람" ], "summary": "CPU 는 QEMU 와 KVM 사이를 왕복하고, virtio 는 virtqueue 를 누가 소비하느냐에 따라 QEMU 나 커널 vhost 로 가며, 패스스루는 유저공간 드라이버가 IOMMU 보호 아래 장치에 직접 닿는다.", "alt": "게스트·호스트 유저공간·호스트 커널·하드웨어 네 층을 가로지르는 세 갈래 경로도.", "long_description": "위에서 아래로 읽는다. 맨 위 QEMU 에서 갈래 셋이 갈린다. 왼쪽 CPU 갈래는 KVM_RUN 으로 커널의 KVM 에 들어가고 KVM 이 VM entry 로 물리 CPU 에 올리며 게스트 코드가 VMX non-root 로 거기서 돈다. 물리 CPU 상자에 VM exit 이 함께 적혀 있고 나온 이유는 KVM 상자의 struct kvm_run 으로 유저공간에 돌아간다. 가운데 패스스루 갈래는 QEMU 가 VFIO 로 장치 fd 를 받고 vfio-pci 가 장치를 묶으며 그 장치의 DMA 가 IOMMU 를 지난다. 오른쪽 virtio 갈래는 virtqueue 가 게스트 드라이버에 닿고 같은 virtqueue 를 커널 vhost 와 나누는 길이 따로 있다. 앞의 둘은 이 실험대가 쓰고 패스스루는 쓰지 않는다.", "source_context": { "document": "docs/keycloak-session-store/final/document.md", "document_sha256": "0ae5674723f25dc85d5069529890f07be1a11b768b56901103fa3ea6ac3dcf55", "anchor": { "kind": "heading", "value": "이 층 아래의 구조 — 조사한 것", "line": 1091 } }, "composition": { "profile": "component-flow", "diagram_only": true, "reference_ids": [ "payment-event-flow" ], "rationale": "세 갈래가 각각 방향이 있는 경로다. comparison 은 관계선을 지워도 뜻이 남는 배치라 이 그림에서는 표가 되고, 깊이가 다르다는 주장이 자리로 드러나지 않는다.", "focus_node": "qemu" }, "groups": [], "nodes": [ { "id": "qemu", "label": "QEMU", "kind": "service", "role": "source", "shape": "box", "emphasis": "primary", "details": [ "/dev/kvm", "KVM_CREATE_VM · KVM_CREATE_VCPU" ], "description": "KVM 핸들을 열고 VM 과 vCPU 를 만드는 유저공간 프로세스. vcpu ioctl 은 그 vcpu 를 만든 스레드에서 낸다.", "evidence": [ { "start_line": 1102, "end_line": 1108 } ], "assumption": false }, { "id": "vfio", "label": "VFIO", "kind": "service", "role": "service", "shape": "box", "details": [ "vfio-pci", "IOMMU 그룹", "/dev/vfio/vfio" ], "description": "IOMMU 보호 아래 장치 접근을 유저공간에 여는 프레임워크. 소유 단위가 IOMMU 그룹이다.", "evidence": [ { "start_line": 1131, "end_line": 1146 } ], "assumption": false }, { "id": "pci-device", "label": "물리 PCI 장치", "kind": "service", "role": "service", "shape": "box", "details": [ "게스트 직접 할당" ], "description": "호스트 드라이버에서 떼어 vfio-pci 에 묶은 장치.", "evidence": [ { "start_line": 1131, "end_line": 1133 }, { "start_line": 1144, "end_line": 1146 } ], "assumption": false }, { "id": "iommu", "label": "IOMMU", "kind": "service", "role": "sink", "shape": "box", "details": [ "DMA · 인터럽트 리매핑" ], "description": "장치가 아무 메모리나 건드리지 못하게 막는 리매핑 장치.", "evidence": [ { "start_line": 1144, "end_line": 1146 } ], "assumption": false }, { "id": "kvm", "label": "KVM", "kind": "service", "role": "service", "shape": "box", "details": [ "struct kvm_run", "KVM_GET_VCPU_MMAP_SIZE" ], "description": "mmap 한 공유 메모리로 나온 이유를 유저공간에 알리는 커널 쪽.", "evidence": [ { "start_line": 1105, "end_line": 1108 } ], "assumption": false }, { "id": "physical-cpu", "label": "물리 CPU", "kind": "service", "role": "service", "shape": "box", "details": [ "VMX root · VMX non-root", "VM entry · VM exit", "guest-state 영역" ], "description": "VM entry 때 guest-state 영역에서 상태를 싣고 VM exit 때 그리로 저장하는 프로세서.", "evidence": [ { "start_line": 1111, "end_line": 1113 } ], "assumption": false }, { "id": "guest-code", "label": "게스트 코드", "kind": "service", "role": "sink", "shape": "box", "details": [ "vCPU" ], "description": "VMX non-root 로 물리 CPU 에서 도는 게스트 명령.", "evidence": [ { "start_line": 1111, "end_line": 1113 } ], "assumption": false }, { "id": "virtio-driver", "label": "게스트 virtio 드라이버", "kind": "service", "role": "sink", "shape": "box", "details": [ "virtio-pci · virtio-mmio", "virtqueue" ], "description": "게스트가 보는 반가상화 장치의 드라이버. 주고받는 통로가 virtqueue 다.", "evidence": [ { "start_line": 1116, "end_line": 1118 } ], "assumption": false }, { "id": "vhost", "label": "vhost", "kind": "service", "role": "sink", "shape": "box", "details": [ "리눅스 커널 구현", "virtqueue 공유" ], "description": "virtqueue 를 QEMU 밖과 나누는 커널 구현. vhost-user 규약이 이것을 제어하는 ioctl 인터페이스를 보완한다.", "evidence": [ { "start_line": 1125, "end_line": 1129 } ], "assumption": false } ], "edges": [ { "id": "qemu-device-fd", "from": "qemu", "to": "vfio", "label": "장치 fd", "kind": "request", "style": "solid", "evidence": [ { "start_line": 1138, "end_line": 1141 } ], "assumption": false }, { "id": "vfio-binds-device", "from": "vfio", "to": "pci-device", "label": "vfio-pci 결합", "kind": "control", "style": "solid", "evidence": [ { "start_line": 1144, "end_line": 1146 } ], "assumption": false }, { "id": "device-dma", "from": "pci-device", "to": "iommu", "label": "DMA", "kind": "data", "style": "solid", "evidence": [ { "start_line": 1144, "end_line": 1146 } ], "assumption": false }, { "id": "qemu-kvm-run", "from": "qemu", "to": "kvm", "label": "KVM_RUN", "kind": "request", "style": "solid", "evidence": [ { "start_line": 1104, "end_line": 1106 } ], "assumption": false }, { "id": "kvm-vm-entry", "from": "kvm", "to": "physical-cpu", "label": "VM entry", "kind": "request", "style": "solid", "evidence": [ { "start_line": 1111, "end_line": 1112 } ], "assumption": false }, { "id": "cpu-runs-guest", "from": "physical-cpu", "to": "guest-code", "label": "VMX non-root", "kind": "data", "style": "solid", "evidence": [ { "start_line": 1111, "end_line": 1112 } ], "assumption": false }, { "id": "qemu-virtqueue", "from": "qemu", "to": "virtio-driver", "label": "virtqueue", "kind": "data", "style": "solid", "evidence": [ { "start_line": 1116, "end_line": 1118 }, { "start_line": 1128, "end_line": 1128 } ], "assumption": false }, { "id": "qemu-vhost-ioctl", "from": "qemu", "to": "vhost", "label": "제어 ioctl", "kind": "control", "style": "dashed", "evidence": [ { "start_line": 1125, "end_line": 1129 } ], "assumption": false } ], "legend": [], "metadata": { "rationale": "세 갈래의 깊이 차이는 어느 층까지 선이 내려가느냐로만 보이므로 배치와 관계선이 함께 있어야 한다. 셋 중 패스스루는 이 실험대가 쓰지 않으며 본문이 그 사실을 적는다." } }