👏

KVM/QemuによるPCIパススルー(PCI Passthrough) -調査編-

に公開

概要

完全な仮想マシンの場合、利用可能な仮想デバイスに限りがあるが、準仮想化(PCI Passthrough経由で実デバイスを利用すること)により、仮想環境でありながら実デバイス並みに利用範囲を広げることが可能となる

メリット

  • GPUをパススルーした場合、通常の仮想マシンでは対応が難しい3D対応ゲームの動作が可能となる(デメリットの考慮は必要)
  • 同じく、GPUのパススルーにより、仮想マシン上で機械学習(生成AI)に利用することが可能となる
  • ストレージコントローラーをパススルーした場合は、物理デバイス(HDD/SSD)上に直接保存できるため、運用上の扱いが容易になる場合がある
  • USBコントローラーをパススルーする場合は、ホスト側でUSBデバイスの認識及び取り外しをする必要がなくなり、管理上の手間が省ける場合がある

デメリット

※詳細は後述

  • ホスト側で実行する場合と比較して実行速度は低下する
    ホストPC側の性能やチューニングにもよるが、10%~30%程度の低下を見込んでおく必要はある(基本的には完全仮想化と同様)
  • 厳格なリアルタイム性はない
    FPS性能が重要なゲーム等、リアルタイム性が必要な場合は、ベンチマーク等で出ないレベルでフレームがスキップされる可能性があるのでお勧めしない
  • パススルー可能なデバイスは限られる
    PCIパススルーは、全てのデバイスで実行可能ではありません。パススルー対象が拡張カードの場合は、PCIeスロットの位置を変えたり、本記事以外の方法で回避可能な場合がある
  • 事前に完全に把握は不能
    マザーボードによりオンボードデバイスの構成が異なるが、メーカーはPCIパススルーの為に仕様を公開している訳ではない為、大抵の場合は買ってから調べることになると思われる
  • 1デバイスを複数の仮想マシン間での共有は不可
    SR-IOV対応のデバイスを持っていれば可能かと思われるが、そのようなデバイスは所有していないので詳細は不明
  • 拡張ボードの変更(特に増減があった場合)は都度設定を見直す必要あり
    PCIeスロットに拡張ボードを追加したり、取り外しをした場合は、IOMMUリセットが発生してナンバリングが変わる場合があります。場合によっては、緊急起動用のUSBメモリ等を用意しておくべき

一般的な対象と留意点

  • 本記事では、AMD/Intel CPU搭載の自作PCであれば実行可能と思われる
    ※ノートPC、ミニPC、Macは不明
  • 個人利用のため、サーバは今回の記事では当てはまらない場合がある
  • CPUは、余程古い世代(sandy bridge未満)でなければ問題ないが、マザーボードにオンボードされていない方が望ましい(オンボードCPUは一般的に遅いため)
  • マザーボードサイズはATXが望ましい(拡張性があるため)
  • Mini-itxでも一応可能だが、パススルー可能なデバイスに限度がある場合がある
  • チップセットはラインナップの上位の方が望ましい(多分)
  • 当然のことだが、RAMは十分な余裕があること

今回動作確認したマザーボード

  • MSI PRO X670-P WIFI (ATX / AMD Ryzen)
  • Asrock B860I WiFi (Mini ITX / Intel Core Ultra)

動作を確認したホスト側OS等

  • OS: Ubuntu 24.04 LTS
  • 仮想: qemu-KVM

PCIパススルー調査

今回、調査対象としたデバイス

  • VGA/GPU/グラボ
    オンボードグラボは、通常、マザーボードのUEFI/BIOSで初期化されるので、PCIパススルーに利用する場合は、初期化するためのROMファイルを作成する必要がある模様だが試していない。今回は外付けのカードで実施したが、基本的には、CPUソケットに近いPCIeスロットは、CPU直結スロットとなり、パススルー可能な場合が多数と思われる
  • ストレージ
    NVMeについては、パススルー可能な場合が殆どかと思われるが、搭載可能なスロット数が多い場合は、パススルー可能なスロットに制限がある可能性がある。状況によっては、構成の見直しが発生すると思われる
    SATAインターフェイスについては、マザーボード次第ではあるが、一部のポートでパススルー可能になっている場合がある
  • USB
    オンボードのUSBがパススルー可能かどうかは、マザーボード次第ではある。今回動作したAMDのマザーボードは、CPU直結のUSBポートがあるため、PCIパススルーは可能であったが、Intelのマザーは残念ながら不可能であった
    パススルー可能な場合、注意点としては2点ある。1点目は、パススルー可能なUSBポートが、Type-C映像出力対応であった場合は、映像出力がホスト側である一方で、USBとしての利用がゲストになる可能性が発生する点である。2点目は、BluetoothデバイスやマザーボードのLEDライト制御等といったUSBに接続されるデバイスが道連れになる可能性がある点である。これらは状況によって実施可否を判断する以外は方法はないと思われる

デバイスの存在調査

  1. VGA/GPU/グラボ
    以下が実行例となる。今回パススルーしたいのは、Geforceであるため、"bus info:" 行にある "0000:01:00.0"がパススルーしたいバスとなる。
$ lshw -class display
  *-display
       description: VGA compatible controller
       product: AD103 [GeForce RTX 4070 Ti SUPER]
       vendor: NVIDIA Corporation
       physical id: 0
       bus info: pci@0000:01:00.0
       version: a1
       width: 64 bits
       clock: 33MHz
       capabilities: vga_controller bus_master cap_list rom
       configuration: driver=nouveau latency=0
       resources: iomemory:f80-f7f iomemory:fc0-fbf irq:148 memory:f5000000-f5ffffff memory:f800000000-fbffffffff memory:fc00000000-fc01ffffff ioport:f000(size=128) memory:f6000000-f607ffff
  *-display
       description: VGA compatible controller
       product: Raphael
       vendor: Advanced Micro Devices, Inc. [AMD/ATI]
       physical id: 0
       bus info: pci@0000:15:00.0
       logical name: /dev/fb0
       version: c4
       width: 64 bits
       clock: 33MHz
       capabilities: vga_controller bus_master cap_list fb
       configuration: depth=32 driver=amdgpu latency=0 resolution=1920,1080
       resources: iomemory:fc0-fbf iomemory:fc0-fbf irq:107 memory:fc10000000-fc1fffffff memory:fc20000000-fc201fffff

また、HDMI等で利用する音声出力も同時にパススルーすることになるため、オンボードサウンドデバイスに関しても、同様に調査しておく。出力例は長くなるので省略するが、"0000:01:00.1"がパススルーしたいバスであることが確認できた。

$ lshw -class sound
  1. ストレージ
    基本的には、以下が実行例になる。
$ lshw -class storage
  *-nvme
       description: NVMe device
       product: CSSD-M2M2TPG5NFZ
       vendor: Phison Electronics Corporation
       physical id: 0
       bus info: pci@0000:02:00.0
       logical name: /dev/nvme1
       version: EQFM22.0
       serial: 230418248000007
       width: 64 bits
       clock: 33MHz
       capabilities: nvme nvm_express bus_master cap_list
       configuration: driver=nvme latency=0 nqn=nqn.2000-11.org.nvmexpress:uuid:23041824-8000-0070-0000-000000000000 state=live
       resources: irq:55 memory:f6f00000-f6f03fff
  *-nvme
       description: NVMe device
       product: CSSD-M2L4KSFT6KE 4096GB
       vendor: Realtek Semiconductor Co., Ltd.
       physical id: 0
       bus info: pci@0000:0b:00.0
       logical name: /dev/nvme0
       version: VG001C32
       serial: 233300001110
       width: 64 bits
       clock: 33MHz
       capabilities: nvme nvm_express bus_master cap_list
       configuration: driver=nvme latency=0 nqn=nqn.2018-05.com.example:nvme:nvm-subsystem-O233300001110 state=live
       resources: irq:24 memory:a0100000-a0103fff memory:a0104000-a0105fff
  *-sata
       description: SATA controller
       product: 600 Series Chipset SATA Controller
       vendor: Advanced Micro Devices, Inc. [AMD]
       physical id: 0
       bus info: pci@0000:12:00.0
       version: 01
       width: 32 bits
       clock: 33MHz
       capabilities: sata ahci_1.0 bus_master cap_list rom
       configuration: driver=ahci latency=0
       resources: irq:52 memory:a0480000-a04803ff memory:a0400000-a047ffff
  *-sata
       description: SATA controller
       product: 600 Series Chipset SATA Controller
       vendor: Advanced Micro Devices, Inc. [AMD]
       physical id: 0
       bus info: pci@0000:14:00.0
       version: 01
       width: 32 bits
       clock: 33MHz
       capabilities: sata ahci_1.0 bus_master cap_list rom
       configuration: driver=ahci latency=0
       resources: irq:57 memory:a0680000-a06803ff memory:a0600000-a067ffff

SATAインターフェイスのSSD/HDDをパススルーさせたい場合、
上記情報だけでは、現在接続されているインターフェイスは不明であるため、追加で以下のコマンドを入力すると判明が可能である(下記例は、sdaで認識されている場合で、"0000:14:00.0"のSATAインターフェイスに接続されていることが分かる)

$ udevadm info -q path -n /dev/sda
/devices/pci0000:00/0000:00:02.1/0000:03:00.0/0000:04:0d.0/0000:14:00.0/ata9/host8/target8:0:0/8:0:0:0/block/sda
  1. USBコントローラ
    USBコントローラをパススルーさせたい場合、ポート毎に接続されるバスが異なるため、まずは、適当なデバイスを刺して、各ポートがどのバスに接続されているかについて確認する必要がある。但し、基本的には、奇数のバス番号がUSB2.0で、偶数のバス番号がUSB3.0となっており、1と2が同じPCIバス、3と4が同じPCIバス...となっているので、特定のデバイスが全てのBusに接続可能とはならない。
    また、この調査により、マザーボードオンボードのUSBデバイス(ファンLEDやBluetooth等)がどのインターフェイス接続なのかについて把握可能であるため、パススルーさせたいUSBコントローラによっては、そのようなデバイスを同時にゲストPC側で認識させるかどうかについても検討することになる。
$ for iommu_group in $(find /sys/kernel/iommu_groups/ -maxdepth 1 -mindepth 1 -type d);do echo "IOMMU group $(basename "$iommu_group")"; for device in $(\ls -1 "$iommu_group"/devices/); do if [[ -e "$iommu_group"/devices/"$device"/reset ]]; then echo -n "[RESET]"; fi; echo -n $'\t';lspci -nns "$device"; done; done
Bus 1 --> 0000:11:00.0 (IOMMU group 19)
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 001 Device 002: ID 10d7:b012 Actions general adapter
Bus 001 Device 003: ID 05e3:0610 Genesys Logic, Inc. Hub
Bus 001 Device 004: ID 1462:7d67 Micro Star International MYSTIC LIGHT 
Bus 001 Device 005: ID 04d9:0006 Holtek Semiconductor, Inc. Wired Keyboard (78/79 key) [RPI Wired Keyboard 5]

Bus 10 --> 0000:16:00.0 (IOMMU group 28)
Bus 010 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub

Bus 2 --> 0000:11:00.0 (IOMMU group 19)
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub

Bus 3 --> 0000:13:00.0 (IOMMU group 20)
Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 003 Device 002: ID 0db0:3130 Micro Star International USB Audio
Bus 003 Device 003: ID 0e8d:0616 MediaTek Inc. Wireless_Device

Bus 4 --> 0000:13:00.0 (IOMMU group 20)
Bus 004 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub

Bus 5 --> 0000:15:00.3 (IOMMU group 25)
Bus 005 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

Bus 6 --> 0000:15:00.3 (IOMMU group 25)
Bus 006 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub

Bus 7 --> 0000:15:00.4 (IOMMU group 26)
Bus 007 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

Bus 8 --> 0000:15:00.4 (IOMMU group 26)
Bus 008 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub

Bus 9 --> 0000:16:00.0 (IOMMU group 28)
Bus 009 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

パススルー可否確認

基本的には、以下のコマンドの結果による判断となる。
パススルー可能なデバイスは、"IOMMU group"として独立しており、かつ、"[RESET]"表示があるデバイスとなる。

$ for iommu_group in $(find /sys/kernel/iommu_groups/ -maxdepth 1 -mindepth 1 -type d);do echo "IOMMU group $(basename "$iommu_group")"; for device in $(\ls -1 "$iommu_group"/devices/); do if [[ -e "$iommu_group"/devices/"$device"/reset ]]; then echo -n "[RESET]"; fi; echo -n $'\t';lspci -nns "$device"; done; done

以下に、PCIパススルー可能な例を示す。
まずは、明らかに問題がないパターンは以下の通りである。

IOMMU group 25
[RESET]	15:00.3 USB controller [0c03]: Advanced Micro Devices, Inc. [AMD] Device [1022:15b6]

以下についても、実際にパススルーして問題はなかった。
恐らく、PCIブリッジは影響しないと思われる。

IOMMU group 21
	04:0d.0 PCI bridge [0604]: Advanced Micro Devices, Inc. [AMD] 600 Series Chipset PCIe Switch Downstream Port [1022:43f5] (rev 01)
[RESET]	14:00.0 SATA controller [0106]: Advanced Micro Devices, Inc. [AMD] 600 Series Chipset SATA Controller [1022:43f6] (rev 01)

以下について、GPU側は[RESET]となっているのに対し、Audioデバイス側は[RESET]とはなっていないが、GPU本体と一体的にパススルーすることで問題は出ないと推測している。

IOMMU group 12
[RESET]	01:00.0 VGA compatible controller [0300]: NVIDIA Corporation AD103 [GeForce RTX 4070 Ti SUPER] [10de:2705] (rev a1)
	01:00.1 Audio device [0403]: NVIDIA Corporation Device [10de:22bb] (rev a1)

パススルーするのは危ないと思ったデバイスは、実際に試していないので、どのような結果になるかは分かりません。

上記の確認結果により、パススルーするポートやデバイスを確定させることになる。
パススルー不可である場合は、以下を検討することになるかと思われる。

  • USBコントローラの場合は、PCIパススルーは諦め、デバイス毎に完全仮想化で使われるUSBパススルーを使うことになる
  • SATA経由のストレージの場合は、他のSATAコントローラ経由での接続を検討する
  • PCIe接続のNVMe SSDの場合は、スロット位置を入れ替える。場合によっては、汎用なPCIeスロット経由での接続を検討する

ID重複確認

  1. デバイスIDのメモ
    上記でパススルーさせるデバイスを確定した場合は、デバイスIDを記録する。
    例えば、GPUをパススルーさせる場合は、上記確認結果から、デバイス名の後に表示されている以下の値が必要となる

10de:2705
10de:22bb

  1. 重複確認
    メモしたデバイスIDを引数として、以下のようにコマンドを入力し、デバイスがパススルーしたいものだけ表示されればOKである。この場合は、PCIパススルーはカーネルレベルで実施が可能となる
$ lspci -nnk -d 10de:2705
01:00.0 VGA compatible controller [0300]: NVIDIA Corporation AD103 [GeForce RTX 4070 Ti SUPER] [10de:2705] (rev a1)
	Subsystem: InnoVISION Multimedia Ltd. Device [1771:10de]
	Kernel driver in use: nouveau
	Kernel modules: nvidiafb, nouveau

USBコントローラやストレージコントローラの場合は、以下のようにPCIパススルーさせたくないデバイスが含まれる場合がある。カーネルレベルでPCIパススルーを行うと不具合が出る可能性があるため、手動で切り離しをすることで対応が可能である

$ lspci -nnk -d 1022:43f6
12:00.0 SATA controller [0106]: Advanced Micro Devices, Inc. [AMD] 600 Series Chipset SATA Controller [1022:43f6] (rev 01)
	Subsystem: ASMedia Technology Inc. Device [1b21:1062]
	Kernel driver in use: ahci
	Kernel modules: ahci
14:00.0 SATA controller [0106]: Advanced Micro Devices, Inc. [AMD] 600 Series Chipset SATA Controller [1022:43f6] (rev 01)
	Subsystem: ASMedia Technology Inc. Device [1b21:1062]
	Kernel driver in use: ahci
	Kernel modules: ahci
  1. パススルー設定を行う為に必要な情報のまとめ
    結論としては、以下のようになる
    • PCIパススルーしたいPCIeデバイスのデバイスIDがユニークであるか、又は、同じIDを持つPCIeデバイス全てをパススルー対象にしたい場合は、PCIeデバイスのデバイスIDを使ってPCIパススルーを設定する
    • 同じデバイスIDを持つPCIeデバイスが複数あり、PCIパススルーしたいデバイスとパススルーが不可能なデバイスに分かれる場合は、PCIパススルーを設定する

ここまで長くなってしまった為、実際の設定については、次回としたい。

Discussion