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に接続されるデバイスが道連れになる可能性がある点である。これらは状況によって実施可否を判断する以外は方法はないと思われる
デバイスの存在調査
- 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
- ストレージ
基本的には、以下が実行例になる。
$ 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
- 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重複確認
- デバイスIDのメモ
上記でパススルーさせるデバイスを確定した場合は、デバイスIDを記録する。
例えば、GPUをパススルーさせる場合は、上記確認結果から、デバイス名の後に表示されている以下の値が必要となる
10de:2705
10de:22bb
- 重複確認
メモしたデバイス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
- パススルー設定を行う為に必要な情報のまとめ
結論としては、以下のようになる- PCIパススルーしたいPCIeデバイスのデバイスIDがユニークであるか、又は、同じIDを持つPCIeデバイス全てをパススルー対象にしたい場合は、PCIeデバイスのデバイスIDを使ってPCIパススルーを設定する
- 同じデバイスIDを持つPCIeデバイスが複数あり、PCIパススルーしたいデバイスとパススルーが不可能なデバイスに分かれる場合は、PCIパススルーを設定する
ここまで長くなってしまった為、実際の設定については、次回としたい。
Discussion