KVM / QEMU の VM では、virtio-net が標準的な仮想 NIC として使われます。ただし、virtio-net と vhost-net は置き換え関係ではありません。virtio-net は guest が使う仮想デバイス方式で、vhost-net は virtio の処理を host kernel で高速化する backend です。
この記事では、guest driver、virtqueue、QEMU または vhost-net、tap、Linux bridge / Open vSwitch、物理 NIC までの処理経路を確認します。その上で multiqueue、offload、CPU / NUMA 配置を測定し、vhost-user、SR-IOV、DPDK へ進む必要があるかを判断します。
書籍
virtio、仮想デバイス、割り込み、VM exit など、仮想 I/O の処理経路を低レイヤから理解する参考書籍です。価格や在庫はリンク先で確認してください。
Amazon で見るこのリンクは Amazon アソシエイトリンクです。
virtio-net と backend を分ける
| 要素 | 動作する場所 | 役割 |
|---|---|---|
| virtio-net driver | guest OS | 仮想 NIC と virtqueue を guest へ提供する |
| virtqueue | guest と host の共有領域 | packet descriptor と buffer 情報を受け渡す |
| QEMU backend | host userspace | virtio packet を QEMU process で処理する |
| vhost-net | host kernel | virtio dataplane の多くを kernel で処理する |
| vhost-user | host userspace dataplane | Unix socket と共有メモリで別 process へ接続する |
| tap / vnet | host kernel | VM interface を bridge や OVS へ接続する |
| bridge / OVS | host network | VM、物理 NIC、overlay の転送を行う |
libvirt では、<model type='virtio'/> が guest 側の device model、<driver name='vhost'/> が host backend を示します。driver 名を省略した場合、libvirt は利用可能なら vhost を使い、利用できなければ QEMU backend へ戻るため、実際の XML と host module を確認します。
packet の処理経路を確認する
| 構成 | 主な経路 | 確認する CPU |
|---|---|---|
| virtio + QEMU | guest → virtqueue → QEMU → tap → bridge / OVS → NIC | vCPU、QEMU emulator、softirq |
| virtio + vhost-net | guest → virtqueue → vhost-net → tap → bridge / OVS → NIC | vCPU、vhost thread、softirq |
| virtio + vhost-user | guest → virtqueue → shared memory → userspace dataplane → NIC | vCPU、vhost-user、PMD、NUMA |
| SR-IOV VF | guest → VF → physical NIC | vCPU、VF、IRQ、NUMA |
同じ virtio-net でも backend と接続先によって処理経路が変わります。帯域だけでなく、packet rate、CPU 使用率、softirq、queue drop、retransmission、p95 / p99 latency を見ます。
VM の interface 定義を確認する
virsh domiflist vm01
virsh dumpxml vm01
virsh domifstat vm01 vnet0
virsh domstats vm01 --interface --vcpu --cpu-total
ip -s link show vnet0
bridge link show
lsmod | grep -E 'vhost|tun'- model が
virtioになっているか - driver backend が
vhostまたはqemuのどちらか - interface の target 名と host 側 tap / vnet の対応
- 接続先が Linux bridge、OVS、macvtap、network のどれか
- interface、vCPU、host CPU の統計を同じ時間帯で取得できるか
vhost-net と multiqueue の XML
次は Linux bridge へ接続する virtio-net interface で、host backend に vhost-net、queue 数に 4 を指定する例です。queue 数は例であり、vCPU 数、traffic flow、host CPU、NUMA、guest driver の対応を確認して決めます。
<interface type='bridge'>
<mac address='52:54:00:12:34:56'/>
<source bridge='br0'/>
<target dev='vnet0'/>
<model type='virtio'/>
<driver name='vhost' queues='4'/>
</interface>libvirt の queues は virtio-net multiqueue の queue 数を指定します。queue ごとに別 CPU で処理できる可能性がありますが、queue を増やすほど thread、interrupt、cache、設定の負荷も増えます。
guest で queue と offload を確認する
guest 側では、interface の channel 数、driver、offload、drop を確認します。interface 名は環境に合わせて変更します。
ethtool -i eth0
ethtool -l eth0
ethtool -k eth0
ethtool -S eth0
ip -s link show eth0
cat /proc/interrupts
mpstat -P ALL 1ethtool -l で maximum と current の queue 数を確認します。XML で queue を増やしても、guest driver が複数 queue を利用しているとは限りません。RSS、flow 数、IRQ affinity、RPS / XPS、vCPU 数を合わせて確認します。
checksum、GSO、TSO、GRO などの offload は CPU 負荷を減らせます。一方、packet capture では wire 上より大きな packet に見えることがあります。トラブル時も一括で無効化せず、host と guest のどちらで変換されるかを切り分けます。
host の tap、bridge、softirq を確認する
ip -s link show vnet0
ip -s link show br0
bridge link show
bridge fdb show br br0
cat /proc/softirqs
cat /proc/interrupts
mpstat -P ALL 1
sar -n DEV 1| 症状 | 確認する場所 | 判断 |
|---|---|---|
| guest TX drop | guest queue、driver、vCPU | guest が送信処理に追い付いているか |
| vnet drop | host tap / vnet | backend と host network の境界で詰まっているか |
| bridge / OVS drop | 仮想 switch | 転送、flow、queue、policy の問題か |
| 物理 NIC drop | NIC queue、ring、IRQ | host uplink が処理に追い付いているか |
| 単一 CPU が高負荷 | vCPU、vhost、softirq | multiqueue と affinity が偏っていないか |
| 帯域は低いが CPU が高い | packet size と pps | 小 packet の packet rate が限界か |
CPU / NUMA と queue を対応付ける
multiqueue の効果は、queue を処理する vCPU と host thread が実行できる CPU に依存します。NIC、host CPU、VM memory が別 NUMA node に分かれると、packet 処理が NUMA interconnect をまたぐ可能性があります。
- VM の vCPU と host CPU affinity
- guest queue と IRQ の CPU 分散
- vhost thread または QEMU thread の CPU
- 物理 NIC queue と IRQ affinity
- VM memory と NIC が属する NUMA node
- bridge / OVS や PMD thread の CPU 配置
性能測定の条件を固定する
| 条件 | 記録する内容 |
|---|---|
| traffic | TCP / UDP、flow 数、packet size、接続方向 |
| VM | vCPU、memory、virtio queue、offload、guest kernel |
| host | backend、bridge / OVS、CPU affinity、NUMA、NIC |
| result | throughput、pps、p95 / p99、CPU、drop、retransmission |
| operation | migration、snapshot、monitoring、Firewall への影響 |
単一 TCP flow の結果だけで multiqueue を評価しないようにします。queue 分散を確認する場合は、複数 flow と複数 packet size を使い、host と guest の CPU・queue 統計を同時に記録します。
vhost-user、SR-IOV、DPDK へ進む条件
| 次の方式 | 進む根拠 | 増える制約 |
|---|---|---|
| vhost-net | QEMU backend の CPU 負荷を減らしたい | kernel backend の確認と thread 配置 |
| vhost-user | userspace switch / dataplane と接続したい | 共有メモリ、socket、NUMA、再接続 |
| SR-IOV | 仮想 switch を迂回して低遅延・高 pps が必要 | 物理 NIC、VF、IOMMU、移行性、監視 |
| DPDK | 専用 userspace dataplane が必要 | 専有 CPU、HugePages、PMD、NUMA、運用 |
virtio-net + vhost-net で要件を満たせるなら、可搬性、可観測性、Firewall、QoS、バックアップなど、仮想化基盤の機能を維持しやすくなります。高度な方式は性能の上位互換ではなく、処理場所と運用責任を変える選択です。
よくある誤解
| 誤解 | 実際の確認 |
|---|---|
| virtio-net を vhost-net に置き換える | virtio guest device と vhost backend を組み合わせる |
| queue 数は vCPU 数まで増やせばよい | flow、IRQ、CPU、NUMA、guest 対応を測定する |
| 帯域が出ないので SR-IOV にする | pps、single flow、CPU、drop、backend を先に確認する |
| offload は packet capture を壊す | capture 位置と segmentation / aggregation の段階を確認する |
| DPDK は virtio を使わない | vhost-user と virtio guest driver を組み合わせる構成がある |
まとめ
virtio-net は guest が使う仮想 NIC 方式で、host 側では QEMU backend、vhost-net、vhost-user などと組み合わせます。最初に device model、backend、tap、bridge / OVS、物理 NIC の処理経路を確定します。
その後で queue 数、offload、vCPU、vhost thread、softirq、NUMA を同じ測定条件で確認します。virtio-net + vhost-net で要件を満たせない根拠が得られた時に、vhost-user、SR-IOV、DPDK と、それに伴う移行性・監視・障害復旧の制約を比較します。

