TT Lab
ブログ

한국어 English 日本語 中文

はじめる
ブログ

ブログ

GPU パススルーと vfio-pci: VM に GPU を丸ごと引き渡す原理

ステータス: ドラフト (2026-10-05)、実測値を反映。 「実測例」セクションの値は、1台の GPU サーバから読み取り専用コマンドで直接確認したものであり、デバイス ID などの識別情報はプレースホルダーに置き換えています。原理の説明はカーネルと KubeVirt のドキュメントの内容に基づき、この記事の執筆時点ではドキュメントを再び開いて照合することはしていません(未確認)。メモリのピン留めとクラッシュループの関係のような、検証できなかった推測はそのように明記しています。 作成ルール: 実測データを基本とし、測定できなかったものは「未実測」と表示します。

目次

1. なぜパススルーを使うのか

GPU をワークロードに提供する方法は大きく分けて4つあります。

方式 単位 分離 この記事との関係
コンテナ(device plugin) GPU 1枚または時分割 プロセスレベル、ホストカーネルとドライバを共有 第3回
MIG GPU をハードウェア的に分割(対応モデルのみ) ハードウェアレベル 第3回
vGPU ドライバが作成した仮想 GPU ハイパーバイザーが管理、ライセンスが必要 範囲外
パススルー 物理 GPU 1枚全体 VM がデバイスを独占 この記事

パススルーが必要になるケースは3つに絞られます。ゲストでホストとは異なるカーネルやドライバのバージョンを使用する必要があるとき、ゲスト OS が Linux ではないとき、そしてコンテナでは満たせない分離を求められるときです。その代わり、 その GPU はホストとコンテナで同時に使用することはできません。 この記事の後半で、同じデバイスが一方では nvidia-smi が失敗する様子を実際に確認します。

2. 原理: 4つのパーツ

パススルーとは、デバイスの所有権をホストカーネルの汎用ドライバからユーザー空間プロセス(QEMU)へ移すことです。そのために4つのパーツが協力して動作します。

2.1 ドライバのバインディング

PCI デバイスはベンダー ID とデバイス ID(例の GPU は 10de:XXXX)を持ち、カーネルはその ID に適合する1つのドライバをアタッチします。通常は nvidia ドライバがアタッチされ、それによりホストで nvidia-smi が動作します。パススルーでは代わりに vfio-pci をアタッチします。 vfio-pci はデバイスを使用するためのドライバではなく、デバイスをユーザー空間に引き渡すための仲介用ドライバです。このドライバがアタッチされたデバイスは、ホストカーネルは使用しません。

アタッチする方法は2つあります。実行中に driver_override と bind/unbind で切り替える方法と、起動時に vfio-pci に該当 ID をあらかじめ取得させる方法です。例のサーバは後者です(第3節)。

2.2 IOMMU: デバイスのメモリへのアクセスを閉じ込める装置

GPU は DMA によってシステムメモリを直接読み書きします。ゲストにデバイスを渡すと、ゲストが使用するメモリのアドレスがホストの実際のアドレスとは異なるため、デバイスが予期せぬホストメモリに触れてしまう可能性があります。 IOMMU はデバイスが生成する DMA アドレスを変換し、許可された範囲を制限することでこの問題を防止します。ゲストのメモリ領域のみをデバイスに開放する仕組みです。

分離の最小単位は IOMMU グループです。同じグループに属するデバイス間では互いの DMA を分離できないため、グループ内のデバイスは すべて同じ所有者に引き渡す必要が あります。そのため、GPU のオーディオ機能(例の 01:00.1)も一緒に vfio-pci にまとめます。

2.3 VFIO インターフェース

VFIO はデバイスをユーザー空間に安全に露出するためのカーネル機能です。ユーザー空間プロセスは /dev/vfio/vfio(コンテナ)と /dev/vfio/<그룹 번호> を開いて次の処理を行います。デバイスの BAR(メモリ領域)を自身のアドレス空間にマッピングし、ゲストメモリを IOMMU に登録し、デバイスの割り込みをイベントファイルディスクリプタとして受け取ってゲストに注入します。QEMU がこの処理を行います。

ゲストメモリは DMA の対象となるため、ホストが回収したりスワップしたりできないように固定(pin)される必要があります。 12GiB メモリの VM は起動時にホストメモリ 12GiB を固定することを意味します(ドキュメントに基づく記述であり、直接測定はしていません)。

2.4 リセット

VM が終了したら、次のユーザーが安全に使えるようにデバイスをクリーンな状態に戻す必要があります。最も望ましい方式はデバイスがサポートする FLR(Function Level Reset)であり、サポートしていない場合はバスリセットを使用します。リセットが正常に行われない GPU は、VM を一度使用した後にホストを再起動しなければならないという問題を引き起こします。

3. 実測例: 6GB 級 GPU を渡した1台のサーバ

すべて読み取り専用コマンドで確認した値であり、ハードウェアごとに異なる 例となる値です。ホストのカーネルは 7.0.0-34-generic です。デバイス ID は XXXX(GPU)と YYYY(オーディオ)にマスクしています。

項目 値 意味
lspci -nnk -d 10de: 01:00.0 GPU(10de:XXXX)と 01:00.1 オーディオ(10de:YYYY)のどちらも Kernel driver in use: vfio-pci 両方の機能とも vfio-pci が所有
IOMMU グループ グループ 16、メンバーは 01:00.0 と 01:00.1 のみ グループがクリーンなため、この GPU のみを個別に渡すことができる
カーネルコマンドライン iommu 関連の引数なし、ただし IOMMU グループは 20 個 このカーネルでは IOMMU がデフォルトで有効になっている
ロードされたモジュール vfio_pci, vfio_pci_core, vfio_iommu_type1, vfio, kvm_intel VFIO スタックがロードされている
/dev/vfio/ 16(グループデバイス)と vfio(コンテナ) ユーザー空間に引き渡す準備が整った状態
reset_method flr bus FLR をサポートし、そうでない場合はバスリセット
BAR 領域 領域 0 が 16M、領域 1 が 8G(prefetchable)、領域 3 が 32M GPU のメモリウィンドウをゲストにマッピングすることになる
PCIe リンク 能力 16GT/s x16、現在 16GT/s x8 (downgraded) リンク幅が半分に低下している

リンク幅が x8 に低下した原因は確認できていません。ボードの配線のせいである可能性もあれば、電源状態のせいである可能性もあるため、断定はしません。

3.1 設定ファイル

この例でパススルーを作成した設定は2つのファイルです。

# /etc/modprobe.d/vfio-gpu.conf
options vfio-pci ids=10de:XXXX,10de:YYYY
softdep nvidia pre: vfio-pci
softdep nouveau pre: vfio-pci
softdep snd_hda_intel pre: vfio-pci

# /etc/modules-load.d/vfio-pci.conf
vfio-pci

例のサーバのブートイメージ(initramfs)には vfio の項目が1行もありませんでした。そのため、この設定はイメージの再生成なしでファイルだけで動作し、元に戻すときも同じ理由でファイルを削除すれば済みます(ただし再起動後に実際に元通りになるかはまだテストしていません)。

4. 同じデバイスで nvidia-smi が失敗する理由

このホストで nvidia-smi を実行すると、「NVIDIA driver と通信できません」というメッセージが表示されます。これはドライバがインストールされていないという意味ではありません。ホストには nvidia-driver-595-open(595.91.07)がインストールされており、現在のカーネル用のモジュールパッケージもあります。

カーネルログがその原因をそのまま示しています。

NVRM: GPU 0000:01:00.0 is already bound to vfio-pci.

nvidia モジュールがデバイスを取得しようと試みるたびに、すでに vfio-pci が所有しているというメッセージが繰り返し出力されていました。デバイスにアタッチされているドライバは1つだけであるため、所有権が VFIO にある間はホストの nvidia-smi には通信相手が存在しません。故障ではなく 設定通りの正常な動作です。

Kubernetes 側の状態も同様の状況を示しています。

場所 値
ノード nvidia.com/gpu 0 (コンテナに渡す GPU はなし)
ノード nvidia.com/<모델별 자원 이름> 1 (VM に渡すデバイスあり)
ノードラベル nvidia.com/gpu.deploy.operands false (GPU Operator のコンポーネントが無効)
GPU Operator driver.enabled false (ホストにインストールされたドライバを使用する設定)

5. KubeVirt がデバイスを VM に接続する経路

  1. KubeVirt の設定の permittedHostDevices に pciVendorSelector: 10DE:XXXX と resourceName: nvidia.com/<모델별 자원 이름> が登録されており、機能ゲート HostDevices が有効になっています。
  2. ノードの virt-handler がその ID のデバイスを見つけて、Kubernetes の拡張リソースとして広告します。その結果が上の表の nvidia.com/<모델별 자원 이름>: 1 です。
  3. VM 定義の devices.gpus に deviceName: nvidia.com/<모델별 자원 이름> を記述します。スケジューラはこのリソースが存在するノードにのみ VM を配置します。
  4. VM が起動すると、QEMU が VFIO を介して該当する IOMMU グループを開き、デバイスをゲストに接続します。

ここで重要な点は、 リソース名に nvidia.com/gpu ではなく別の名前が使用されるということです。コンテナ用 GPU と VM 用 GPU は異なるリソースであり、1枚の GPU はそのどちらか一方としてのみ広告されます。

6. よくある罠: メモリのピン留めとホストの OOM

GPU を渡した VM は、2.3節のようにゲストメモリをホストにピン留めします。ピン留めされたメモリはスワップによって退避させることができないため、VM が起動した瞬間にゲストのサイズ分のホストメモリが一度に必要になります。同じホスト上で他のワークロードやノードdaemon が一緒に稼働している場合、合計が物理メモリを超過しやすくなります。

ホスト全体のメモリが不足すると、カーネルの OOM killer が動作します。このとき、カーネルログにはコンテナの制限超過(Memory cgroup out of memory)ではなく、ホスト全体のメモリ不足(Out of memory: Killed process)として記録されます。犠牲になるのは VM プロセス(qemu-kvm)だけでなく、ノードのシステムデーモンである可能性もあります。VM が終了してから再起動すると同じことが繰り返されるクラッシュループになり、その間にデーモンも継続して一緒に終了します。

筆者の実験環境で観測したホストの OOM ログは、この流れと一致していました。ただし、VM が起動するときに実際にピン留めしたメモリのサイズを測定していないため、この解釈は 推測です(未実測)。教訓は2つあります。ホストのメモリに余裕がないサーバには GPU VM を複数載せない方が安全であり、VM を起動する前にゲストメモリの合計とホストですでに使用しているメモリを事前に足し合わせる必要があります。

7. コンテナ用 GPU に戻す(手順の例)

パススルーを解除し、デバイスをコンテナ用に戻す最も単純な方法は、ノードを空にして(cordon、drain)、設定ファイルを削除した後に再起動することです。ただし、そのノードで他のサービスが稼働している場合、再起動はすべてを停止させる大掛かりな作業になります。以下は 再起動なしでデバイスのみを渡す 例です。可能であると判断した根拠は、すべて読み取り専用で事前に確認しました。

事前確認 結果
/dev/vfio/<그룹 번호> を掴んでいるプロセス なし
GPU を使用している VM なし
デバイスの状態 アイドル、リセット方式 flr bus サポート
nvidia モジュール 595.91.07、現在のカーネル用、署名済み

まず2つの設定ファイルをバックアップしてから削除し、以下の2つのステップを実行しました。

# 1) 이 장치는 nvidia 드라이버로만 붙도록 지정한다.
echo nvidia | sudo tee /sys/bus/pci/devices/0000:01:00.0/driver_override
# 2) vfio-pci 에서 푼다. 풀리는 즉시 nvidia 가 장치를 가져간다.
echo 0000:01:00.0 | sudo tee /sys/bus/pci/drivers/vfio-pci/unbind

結果。 unbind の直後、デバイスのドライバが nvidia に変わり、 nvidia-smi が動作しました。

項目 値
GPU 6GB 級ノートPC 用 GPU、6,144MiB
ドライバ 595.91.07
lspci 01:00.0 の Kernel driver in use: nvidia
ホスト 稼働時間を維持、負荷の変化なし、他のサービスへの影響なし

あわせて確認すべき点。

元に戻すには、 driver_override を vfio-pci に変更し、 nvidia を解除すればよいです。この切り替えの後は、GPU VMはこのGPUを使用できなくなります。 1枚のGPUをコンテナとVMで交互に使用するにはこの切り替えを毎回行う必要があるため、枚数が少ない環境では用途を一つに決める必要があります。

8. 面接で出そうな質問

9. よくある誤解

外部の根拠:パススルーの性能オーバーヘッド

パススルーVMの性能を直接測定できなかったため、同様の疑問を測定した外部資料を確認しました。閲覧できた範囲は、アブストラクト、ベンダーの記事、一部の原文です。

資料 確認した内容と条件 信頼性と限界
Walters他、「GPU Passthrough Performance: A Comparison of KVM, Xen, VMWare ESXi, and LXC for CUDA and OpenCL Applications」、IEEE CLOUD 2014 アブストラクト:KVMパススルーがベアメタルの98~100%、XenとVMwareが96~99%(2世代のGPU、2つのプロセッサマイクロアーキテクチャ) 査読付き論文や本文を閲覧できず、IOMMU、ラージページ、NUMA、リンク幅の条件は不明。2014年のGPU
Steininger他、「GPU Passthrough Across Virtualization Platforms for LLM Inference」、Computers(MDPI) 第15巻、2026 アブストラクト:ワークステーション級GPU 1枚をProxmox VM・LXC、KVM、OpenStack、Podmanでパススルーし、vLLM小規模モデルのスループットやトークン間レイテンシがいずれのプラットフォームでもベアメタルの2%以内(統計的に同等) 著者が一般的な主張ではないと述べている。デバイスを飽和させない領域。本文へのアクセス拒否によりアブストラクトのみ確認
OpenNebulaベンダーブログ(2026-03-18) GB200 NVL4パススルー cuBLAS GEMM 71,891対71,738 GFLOP/s(0.21%の差)、vLLMスループット ±4%以内 ベンダーの記事、NUMA固定・ラージページ・IOMMUの設定が明記されていない
VMware性能調査(2023-10) vGPU(全体プロファイル H100)でのGPT-J 6Bがベアメタルの97.9~99.8%(MLPerf Inference v3.1) パススルーではなくvGPU。MLCommonsの検証を受けていない結果
Samani, Amini Salehi, arXiv 2112.09780, 2021 ベアメタル、VM(パススルー)、コンテナの比較。一貫した単一のオーバーヘッドはなく、エンドツーエンドの推論時間がCPUの前処理・後処理やストレージI/Oに支配されるため、GPU部分の影響は小さい。ホストにアイドルコアがないとVMのオーバーヘッドが大きくなり、CPUピン留めで軽減される Tesla K40、2ソケット80コア、HDD。古いGPU
NVIDIA CUDA Programming Guide 3.4.2.5, NCCL GPU troubleshooting ベアメタルではIOMMUを無効化するよう求め、VMパススルーではIOMMUを有効化してVFIOを使用するよう求めている。ACSはP2PをCPUルートコンプレックス経由で迂回させるため帯域幅が大幅に低下するが、VMではACSを無効化できず、VM内で最大性能を出すにはネットワークアダプターのATSが必要 公式ドキュメント。単一GPUサーバーではP2Pは争点にならない

確認できなかったこと

参考資料

確認したバージョンと日付

2026-10-05時点。ホストカーネル 7.0.0-34-generic、NVIDIAドライバーパッケージ 595.91.07(ホストにインストール)、KubeVirt HostDevices 機能ゲートを使用。

ログインするといいねできます

コメント

まだコメントはありません。

ログインするとコメントできます