仮想化の「聖域」に触れる:PCIパススルーとIOMMUが支える極限のI/Oパフォーマンス
「仮想マシン(VM)なのに、なぜかネットワークレイテンシが物理マシンと変わらない」。そんな魔法のような構成を支えているのが、今回深掘りするPCIパススルーと、その守護神であるIOMMUの世界です。
クラウドのハイパーバイザー上で、単なる「仮想化された仮想NIC」ではなく、物理NICやGPUをゲストOSに直接握らせる。この荒業を安全かつ高速に実現するためのハードウェア支援機能について、現場の泥臭い知見を交えて解説します。
—
なぜ「仮想化の皮」を脱ぐ必要があるのか
通常、VMがネットワーク通信を行う際、パケットは「ゲストOSのドライバ」→「ハイパーバイザー(VMM)」→「物理NIC」という長い旅路を通ります。この間、何度もCPUが割り込み処理に介在し、コンテキストスイッチが発生する。高負荷なWeb APIサーバーや、マイクロ秒を争う金融取引システムにおいて、このオーバーヘッドは致命的です。
そこで登場するのが、物理PCIデバイスをVMへ直接マッピングするPCIパススルーです。しかし、ここで一つ大きな問題が生じます。
制御不能な「DMAの暴走」をどう止めるか
物理デバイスは、CPUを介さずにメモリへ直接書き込む DMA (Direct Memory Access) を多用します。もし、悪意ある(あるいはバグを含んだ)VMが、物理デバイスのDMAを操作して、ハイパーバイザーのメモリ領域を書き換えたらどうなるか。システムは即座にパニック(クラッシュ)します。
この「野放しなDMA」を、仮想メモリ空間と同じように制御し、安全に保護するための境界線が IOMMU (Input-Output Memory Management Unit) です。Intelでは VT-d、AMDでは AMD-Vi と呼ばれる技術ですね。
—
IOMMUの「通信フロー」のリアル
IOMMUの役割は、PCIデバイスが要求する「物理アドレス」を、IOMMUが管理する「IOページテーブル」を使って、VMが許可された物理メモリ領域へ変換(リマップ)することです。
1. Device Request: 物理デバイスが「アドレス 0x1234… にデータを書き込め」と要求。
2. IOMMU Intercept: IOMMUがそのリクエストを横取り。
3. Address Translation: デバイスに割り当てられたIOページテーブルを参照し、「0x1234…」を「VMが所有する安全な物理アドレス」に書き換える。
4. Memory Access: 安全な領域に対してDMA実行。
この仕組みがあるおかげで、VMは「自分は物理デバイスを直接触っている」と信じ込めるし、ホスト側は「VMが他を破壊するのを防げる」わけです。
—
実践:IOMMUを有効化し、デバイスをパススルーする手順
現場でこの設定を行う際は、BIOS/UEFIレベルでの有効化(VT-d 設定)が必須です。その上で、Linuxホスト(KVM/QEMU環境)ではカーネルパラメータに以下の記述を追加します。
1. カーネルパラメータの追加 (/etc/default/grub)
# IOMMUを有効化し、PCIデバイスのグループ化をサポートする
GRUB_CMDLINE_LINUX_DEFAULT="intel_iommu=on iommu=pt"
intel_iommu=on: IOMMU機能を有効化。iommu=pt(Pass-Through): パススルー対象外のデバイスにはIOMMUを使わず、オーバーヘッドを最小化する設定。これがないと、全デバイスのDMAが重くなります。
2. デバイスIDの特定
対象の物理デバイス(例:NIC)のIDを調べます。
# ベンダーIDとデバイスIDを確認する
lspci -nn | grep -i ethernet
# 出力例: 01:00.0 Ethernet controller [0200]: Intel Corporation Ethernet 10G [8086:154c]
この 8086:154c が、VMに渡すべきデバイスIDです。
—
運用上の落とし穴:IOMMUグループの呪い
ここが現場で最もハマるポイントです。PCIデバイスは「IOMMUグループ」という単位でしか分離できません。
もし、物理NICと別の重要なデバイス(USBコントローラーなど)が同じIOMMUグループに属している場合、一方だけをVMにパススルーしようとすると、「IOMMUグループ全体を渡せ」とハイパーバイザーに拒絶されます。
デバッグ用のコマンド
どのデバイスが同じグループにいるかは、以下のスクリプトで可視化できます。
#!/bin/bash
# IOMMUグループごとのデバイス一覧を表示するシェルスクリプト
for d in /sys/kernel/iommu_groups/*/devices/*; do
n=${d#*/iommu_groups/*}; n=${n%%/*}
printf 'IOMMU Group %s ' "$n"
lspci -nns "${d##*/}"
done
もしグループが重なっていたら、PCIeスロットを物理的に差し替えるか、BIOSの設定で「PCIe ACS Override」を有効にするという荒業が必要になることもあります。
—
最後に:クラウドの裏側を理解するということ
Web APIのレスポンスが遅いとき、私たちはついアプリケーションコードやデータベースのクエリに目を向けがちです。しかし、その足元にある仮想化層のネットワーク処理が、IOMMUによって「物理直結」されているのか、それとも「ソフトウェアスイッチを経由したエミュレーション」なのかを知っているだけで、トラブルシューティングの解像度は劇的に変わります。
「見えないもの」を「見えるようにする」。それがクラウドエンジニアの矜持です。もし皆さんの環境で、低レイテンシが求められるワークロードがあるなら、ぜひIOMMUの構成を確認してみてください。そこには、OSの抽象化を超えた、ハードウェア本来の咆哮が聞こえるはずです。
コメント