{ "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)과 도착 상자 이름으로 읽게 두었다." } }