Proxmoxで内蔵GPU(Intel i915/xe)をSR-IOVでVMから使う方法と重要なチューニング
この記事では以下の内容を記載します。重要なのは3つめです
- 内蔵GPU(Intel i915/xe)をSR-IOVでVMから使う方法
- 各VMから利用する方法
- 各VMからiGPUを同時利用した際にVMごとfreezeした状態となる場合の重要なチューニング項目を記載します
前提条件
- この記事ではPVE 9.2.10で動作確認を行っております。が、多分PVE 9.0, 9.1でも動作すると思われます。(って書くと確証が取れてないとAIに怒られるのですが、ここで離脱するのは勿体ないです)
- iGPUがi915またはIris Xeが対象です。ご自分のハードウェアを良くご確認下さい。なお、この記事ではCore i7 1360Pで確認しております。
ProxmoxホストのiGPUをSR-IOVで仮想化して8つに分割する方法。
以下の手順に従ってインストールしてください。難しくありません。
https://github.com/strongtz/i915-sriov-dkms/blob/master/docs/install-pve-host.md
最後にlspciでVGA compatible controllerが8つ作られている(00:02.0~00:02.7までPF x 1 + VF x 7の合計8つ)ことを確認して下さい。
root@pve1:~# lspci -nnk -s 00:02
00:02.0 VGA compatible controller [0300]: Intel Corporation Raptor Lake-P [Iris Xe Graphics] [8086:a7a0] (rev 04)
DeviceName: Onboard - Video
Subsystem: Intel Corporation Device [8086:3037]
Kernel driver in use: i915
Kernel modules: i915, xe
00:02.1 VGA compatible controller [0300]: Intel Corporation Raptor Lake-P [Iris Xe Graphics] [8086:a7a0] (rev 04)
Subsystem: Intel Corporation Device [8086:3037]
Kernel driver in use: i915
Kernel modules: i915, xe
00:02.2 VGA compatible controller [0300]: Intel Corporation Raptor Lake-P [Iris Xe Graphics] [8086:a7a0] (rev 04)
Subsystem: Intel Corporation Device [8086:3037]
Kernel driver in use: i915
Kernel modules: i915, xe
00:02.3 VGA compatible controller [0300]: Intel Corporation Raptor Lake-P [Iris Xe Graphics] [8086:a7a0] (rev 04)
Subsystem: Intel Corporation Device [8086:3037]
Kernel driver in use: i915
Kernel modules: i915, xe
00:02.4 VGA compatible controller [0300]: Intel Corporation Raptor Lake-P [Iris Xe Graphics] [8086:a7a0] (rev 04)
Subsystem: Intel Corporation Device [8086:3037]
Kernel driver in use: i915
Kernel modules: i915, xe
00:02.5 VGA compatible controller [0300]: Intel Corporation Raptor Lake-P [Iris Xe Graphics] [8086:a7a0] (rev 04)
Subsystem: Intel Corporation Device [8086:3037]
Kernel driver in use: i915
Kernel modules: i915, xe
00:02.6 VGA compatible controller [0300]: Intel Corporation Raptor Lake-P [Iris Xe Graphics] [8086:a7a0] (rev 04)
Subsystem: Intel Corporation Device [8086:3037]
Kernel driver in use: vfio-pci
Kernel modules: i915, xe
00:02.7 VGA compatible controller [0300]: Intel Corporation Raptor Lake-P [Iris Xe Graphics] [8086:a7a0] (rev 04)
Subsystem: Intel Corporation Device [8086:3037]
Kernel driver in use: vfio-pci
Kernel modules: i915, xe
各VMから利用する方法
ProxmoxダッシュボードからVMを通常通り作ります。Displayはdefaultのままでも構いません。
その後、Datacenter -> [PVE node] -> [VM ID] -> Hardware -> Add -> PCI Device
からiGPUのVFをVMに割り当てます。
- Raw Device -> Device : ここで、上記00:02.1から00:02.7までのどれかを指定します。.0は指定しないでください。VMからは使えません。
- All functions: OFF
- Primary GPU: ここは任意ですがOFFの方がトラブルは少ないでしょう。
- ROM-Bar: On
- PCI-Express: On
この状態でVMを起動します。
VMがLinuxの場合
以下のようにrenderD1xxが表示され、lspciにVGA compatible controllerとして、ホスト側のiGPUが見えていればOKです。
masa@jf:~$ ls -la /dev/dri/renderD128
crw-rw---- 1 root render 226, 128 Aug 21 15:41 /dev/dri/renderD128
masa@jf:~$ lspci| grep VGA
00:01.0 VGA compatible controller: Device 1234:1111 (rev 02)
01:00.0 VGA compatible controller: Intel Corporation Raptor Lake-P [Iris Xe Graphics] (rev 04)
この後は、GPUを何に使うかによって確認方法が異なります。
VMがWindows 11の場合
Device ManagerのDisplay adaptorsにIntel(R) Iris(R) Xe Graphicsと出ていれば問題ありません。もし黄色のアイコンで表示されていたら、ドライバをインストールしてあげて下さい。黄色のアイコンが消えるはずです。

重要なチューニング項目
ここからが本題です。上記までの手順でVFの割り当ては完了していますし、各VMを単体で動かしている限りは何の問題も起きません。問題が出るのは、複数のVMが同時にiGPUを使い始めた時です。
症状
私の環境では以下のように現れました。
- Jellyfinを動かしているLinux VM(VF6)と、デスクトップ用途のWindows 11 VM(VF7)が同居している
- Windows VM上のブラウザからJellyfinのダッシュボードを開き、
Scan All Librariesを実行する - トランスコードとトリックプレイ生成が始まった瞬間から、Windows VMがフリーズしたような状態になる
- マウスカーソルはカクカクとしか動かず、ウィンドウの描画も間に合わない
- ジョブが終わると、何事もなかったかのように復帰する
厄介なのは、どこも壊れていないことです。Windows がクラッシュするわけでもドライバが落ちるわけでもなく、デバイスマネージャーは正常なままです。ホスト側のdmesgにもエラーは出ません。両方のVFは、それぞれ「正しく」動作しています。
この症状は、GPUの時分割の設定が初期状態のままであることが原因です(詳しくは後半の「原因」で説明します)。まずは直し方から書きます。
結果として私は、Jellyfinのスキャンだけは別のPC(Surface Pro 9)から実行する、という間の抜けた運用をしていました。同じことで悩んでいる方がいるのではないかと思います。
現在の値を確認する
ホスト側のsysfsで確認します。
ls /sys/class/drm/card0/
ここにprelim_iovというディレクトリがあるはずです。
root@pve1:~# ls /sys/class/drm/card0/
boot_display card0-HDMI-A-1/ error gt_max_freq_mhz metrics/
card0-DP-1/ card0-HDMI-A-2/ gt/ gt_min_freq_mhz power/
card0-DP-2/ dev gt_act_freq_mhz gt_RP0_freq_mhz prelim_iov/
card0-DP-3/ device/ gt_boost_freq_mhz gt_RP1_freq_mhz subsystem/
card0-DP-4/ engine/ gt_cur_freq_mhz gt_RPn_freq_mhz uevent
VFごとの設定はこの下に並んでいます。
grep . /sys/class/drm/card0/prelim_iov/vf*/gt0/exec_quantum_ms
grep . /sys/class/drm/card0/prelim_iov/vf*/gt0/preempt_timeout_us
すべて0が返ってくるはずです。これが今回の犯人です。
ここが検索で見つからない理由
このパラメータ、名前が版によって異なります。上流のi915/Xeドライバでは
iov/配下にexecution_quantum_ms/preemption_timeout_usという名前で存在しますが、i915-sriov-dkmsではprelim_iov/配下にexec_quantum_ms/preempt_timeout_usという短縮名で存在します。ディレクトリ名も属性名も一致しません。「iov exec_quantum_ms」で検索しても上流の情報に辿り着けず、逆に上流の名前で検索しても自分の環境には存在しない、という状態になります。まずは
lsでご自分の環境の綴りを確認してください。
設定する
まずは手で入れて効果を確かめるのが早いです。VF番号はご自分の環境に合わせてください(私の場合はVF6とVF7を使用しています)。
for vf in 6 7; do
echo 8 > /sys/class/drm/card0/prelim_iov/vf$vf/gt0/exec_quantum_ms
echo 8000 > /sys/class/drm/card0/prelim_iov/vf$vf/gt0/preempt_timeout_us
done
値の決め方
VFの切り替えには、最悪の場合 exec_quantum_ms + preempt_timeout_us の時間がかかります。デスクトップの体感を優先するなら、60Hzの1フレーム(約16.6ms)に収まる範囲が一つの目安になります。上記の 8ms + 8000us(=8ms) はここから決めた値です。
exec_quantum_ms は「そのVFが連続してGPUを握れる時間」、preempt_timeout_us は「譲るのを待つ時間」です。どちらも初期値は0(=無制限)になっています。
- 短くする → 切り替えが細かくなりデスクトップは滑らかになりますが、コンテキストスイッチが増えてトランスコードのスループットは落ちます
- 長くする → その逆です
参考までに、intel-gfxのメーリングリストではIntelのメンテナが切り分け手順としてexecution_quantum_ms = 20 / preemption_timeout_us = 20000を案内しています。デスクトップの応答性より全体のスループットを重視するなら、この20/20000側に寄せるのもありだと思います。
なお前述のRFCドキュメントには、リソースのプロビジョニングと異なりスケジューリングパラメータはVFドライバの動作中でも変更可能と書かれています。VMを止めずに値を振って比較できるはずです。
再起動後も維持する
sysfsの値は再起動で消えますので、/etc/sysfs.confに書いて永続化します。
ここで一つ注意点があります。/sys/class/drm/card0はシンボリックリンクなので、sysfsutilsが辿ってくれない可能性があります。実体パスで書くのが確実です。
readlink -f /sys/class/drm/card0
私の環境では以下のようになりました。
devices/pci0000:00/0000:00:02.0/sriov_numvfs = 7
devices/pci0000:00/0000:00:02.0/drm/card0/prelim_iov/vf6/gt0/exec_quantum_ms = 8
devices/pci0000:00/0000:00:02.0/drm/card0/prelim_iov/vf7/gt0/exec_quantum_ms = 8
devices/pci0000:00/0000:00:02.0/drm/card0/prelim_iov/vf6/gt0/preempt_timeout_us = 8000
devices/pci0000:00/0000:00:02.0/drm/card0/prelim_iov/vf7/gt0/preempt_timeout_us = 8000
/etc/sysfs.confは先頭の/sys/を落とした相対パスで書きます。sriov_numvfsの行を必ず先頭に置いてください。VFが生成されて初めてvf6/以下のパスが出現するため、順序が逆だと「そんなパスは無い」で失敗します。
設定が入ったか確認する
dmesgには出ません。sysfsutilsによる書き込みはカーネルログに残らないためです。sysfsを読み返して確認してください。
grep . /sys/class/drm/card0/prelim_iov/vf*/gt0/exec_quantum_ms
grep . /sys/class/drm/card0/prelim_iov/vf*/gt0/preempt_timeout_us
8と8000が返ってくれば成功です。0のままなら書き込みが失敗しているので、こちらに理由が出ているはずです。
systemctl status sysfsutils
journalctl -b -u sysfsutils
効果
冒頭に書いた症状、つまりWindows VM上からJellyfinのScan All Librariesを実行するとWindowsが固まる、という現象が解消しました。別PCから操作する必要がなくなり、素直に「Windows VMをデスクトップとして使う」運用に戻せています。
原因
SR-IOVが分割しているのはGPUへのアクセス権であって、処理能力ではありません。VFが7個あっても、その先にある物理的なエンジンは1セットです。
複数のVFから投げられたワークロードは、GPU内のマイクロコントローラであるGuC(Graphics microcontroller)によって時分割で処理されます。そしてその配分は、2つのパラメータで制御されています。
| パラメータ | 意味 |
|---|---|
| execution quantum | そのVFが連続してGPUを握っていられる時間 |
| preemption timeout | 量子を使い切ったVFが譲るのを待つ時間。超えたら強制的に取り上げる |
問題は、**この2つの初期値がどちらも 0、つまり「無制限」**であることです。
この状態だと、先にGPUを掴んだVFは自分の仕事を投げ終わるまで手を離しませんし、譲るように促されても待ってもらえてしまいます。トランスコードのようなバッチ処理は延々とサブミットを続けるため、もう片方のVFはその間ずっと順番待ちになります。デスクトップの描画は数十ms待たされるだけで人間には「固まった」と見えるので、症状の出方とも一致します。
なお、この挙動はIntelが公開しているXeドライバ向けSR-IOVのRFCドキュメントにも記載があり、VFがGTを専有していない限り複数VFからのサブミットは時分割され、execution_quantumとpreemption_timeoutで制御されると明記されています。つまり仕様通りの動作であって、バグではありません。
なお、時分割の配分が適切になるだけで、GPUの処理能力そのものが増えるわけではありません。片方が本気でGPUを使い切っている間、もう片方の処理が遅くなること自体は変わりません。あくまで「順番が回ってこない」状態が解消される、という理解が正確かと思います。逆にコンテキストスイッチングでトータルには遅くなるはずですが、その間Windows VMがフリーズして使い物にならないよりはよっぽどましです。
まだ試していないが、効きそうな項目
正直に書いておくと、以下は私はまだ試していません。改善が十分だったため踏み込んでいないだけで、詰めるなら見るべき場所だと思います。
-
PF側にも同じ設定を入れる —
prelim_iov/pf/gt0/にも同じ属性があります。ホスト自身もGPUを使うため、ここが無制限のままだと片肺という見方ができます。前述のIntelメンテナの案内も、PFと全VFの両方に設定するよう書かれています -
policies/engine_reset— 譲らないVFを強制的にリセットするポリシーです。保険として効く場面がありそうです
参考
- strongtz/i915-sriov-dkms
- drm/doc/rfc: SR-IOV support on the new Xe driver — execution_quantumとpreemption_timeoutの仕様について
- intel-gfx: TLB Invalidation time out on i915 SR-IOV passthrough — メンテナによる具体的な設定値の案内