赤い環境でIndexでVMで快適にVRCを楽しむ
3回目でついに勝ちました。
この記事には若干の感情があります。苦手な方はご注意を。
それではどうぞ。
事の始まり
わたしは2回LinuxでVRCをやろうとしてあんまりいい感じの環境が作れずWindowsに生活の半分ぐらいを侵食されていたArchLinuxユーザーです。
その生活は唐突に終わりを迎えました。それは2022年7月27日のことです。ヤツが来た。
別に熊をPCに入れることに抵抗はないんです。ゲームやるのでなんならもう入ってます。でも何かが許せなくて、それでVRCのアプデを止めて、再起動して、色々壊れてるのを放置して使ってたArchLinux環境を直しはじめたんです。
そんな時EAC存在してるだけ説という面白い物がTLに流れて来て、これLinuxでやったらどうなるんだろう?ってなって、一通り遊び倒した辺りでPCを新しくして強くなったことに気がついて手元に余ってるGPUもあるのでライザーケーブルを買ってそしてまた変なことを始めたわけです。
環境
項目 | 値 |
---|---|
ホストOS | ArchLinux |
ゲストOS | Windows 10 Pro |
マザーボード | B550 Pro4 |
CPU | Ryzen 9 5900X(12C24T) |
RAM | 64GB |
ホストGPU | Radeon RX 480(8GB) |
ゲストGPU | Radeon RX 6800XT(16GB) |
ゲストストレージ | SSDを200GB割り当て |
ゲストNIC | Intel 82574L |
HMD | Valve Index |
ちなみに見た目はこんな感じ。CPUクーラーと干渉する関係で1スロット目のGPUがハミ出してるだけなので見た目よりは普通です。
今回は何故かPCに刺さってたNICをパススルーしホスト側はマザーボードのNICを利用します。
環境構築の前に!
わたしの環境に近いユーザーは何をするよりも最初にやるべきことがあります。
それはRyzen 5000シリーズの上位モデルで起きる唐突に再起動する不具合の対策です。
Intelユーザーとこの不具合を踏まないユーザーには特に関係ないです。読み飛ばしてください。
わたしの環境ではArchLinux単体では全く起きないにも関わらず、VM上のWindowsで負荷が増えた時に起きるという理不尽な罠として機能し、結果として余計に2日を費やすことになったので一番上に書きます。
環境によって対策は異なりますが、一番シンプルな対策はUEFI上で利用できるCurve Optimiserを利用しPositive方向に4段階のオフセットを設定する方法で、わたしはこの対策を利用しました。
この方法は簡単でシステム側に変な設定が残らないのが利点ですが、オーバークロックはしてないものの機能としてはオーバークロック相当の操作を利用していること、大体のマザーボードはUEFIのアップデートが必要であることなど若干のリスクは伴います。
UEFIで必要な設定をしてくる
仮想化関連の設定を有効にしてください。
わたしの環境ではSVM Mode
、SR-IOV
を有効にしています。
もしIOMMU
関連の項目もある場合はそれも有効にする必要があります。
マザーボード、CPUのメーカーによって項目は違うので詳しくは調べてください。
必要な物のインストール
今回は物理NICをパススルーするのでネットワーク関連は必要ありません。GPUドライバ系は自分の環境に合わせて入れてください。
sudo pacman -S qemu libvirt edk2-ovmf virt-manager
IOMMUの有効化
カーネルパラメーターにAMDならamd_iommu=on
、Intelならintel_iommu=on
を追加する。
その後にiommu=pt
も追加する。
GRUBの場合/etc/default/grub
内のGRUB_CMDLINE_LINUX_DEFAULT
が
GRUB_CMDLINE_LINUX_DEFAULT="quiet amd_iommu=on iommu=pt"
って感じになる。
追加し終わったら再起動してdmesg|grep -e DMAR -e IOMMU
で有効になってるか確認してください。
IOMMUグループの確認
IOMMUグループは仮想マシンに渡すことのできる物理デバイスの最小セットです。
つまりパススルーしたいGPUは、パススルーしてはいけない他デバイスと同じIOMMUグループに居てはいけないわけです。
スクリプトを作成して実行。
#!/usr/bin/env bash shopt -s nullglob for d in /sys/kernel/iommu_groups/*/devices/*; do n=${d#*/iommu_groups/*}; n=${n%%/*} printf 'IOMMU Group %s ' "$n" lspci -nns "${d##*/}" done;
出力を見てパススルーしたいGPUを探し、IOMMUグループの何処に属しているのかを確認します。
孤立している場合はそのままでOKです。孤立してない場合は以下の回避策を利用してください。
無事孤立させられたらパススルーしたいGPUのPCIデバイスIDをメモるか覚えてください。わたしの環境ではGPU本体の1002:73bf
と付随するAudio deviceの1002:ab28
です。
ACS上書きパッチ
パススルーしたいGPUがUSB controllerとかSATA controllerと一緒のIOMMUグループに居る場合、GPUを刺したPCIeスロットがそういうスロットなので一般的には違うスロットに刺すのが正解です。
とはいえ違うスロットに刺せるならいいのですが、そのスロットしかないとか、PC開けるのが面倒とか、GPUがPCの外にハミ出てるのでケース作り直すのめんどいなど、違うスロットに刺せない事情がある場合もあるでしょう。
その場合はACS上書きパッチという物が利用できます。ACS上書きパッチを利用するにはパッチの適用されたカーネルが必要です。
ArchWikiではlinux-vfioが推奨されていますが、実は公式サポートカーネルのlinux-zenにもACS上書きパッチが適用されているので好きな方を入れればいいと思います。
パッチの適用されたカーネルで起動できたら、カーネルパラメーターにpcie_acs_override=downstream
を指定して再起動すればIOMMUグループが分かれてるはずです。
もし分かれなければmultifunction
を追加し、pcie_acs_override=downstream,multifunction
にしてみてください。わたしの環境ではmultifunction
も必要でした。
vfio-pciのロード
/etc/mkinitcpio.conf
のMODULES
にvfio-pciをロードさせるために必要な物を突っ込みます。環境によってはGPUドライバを読み込みしている場合がありますが、その場合はそれより前に突っ込む必要があります。
MODULES=(vfio_pci vfio vfio_iommu_type1 vfio_virqfd)
そしてHOOKS
にmodconf
を追加します。
HOOKS=(... modconf ...)
編集が終わったらinitramfsを再生成します。
sudo mkinitcpio -P
カーネルパラメーターにパススルーするGPUを分離する設定を追加。
vfio-pci.ids=パススルーしたいGPUのPCIデバイスIDを,区切りで書く
わたしの環境だとこんな感じ。
vfio-pci.ids=1002:73bf,1002:ab28
ゲストにパススルーするNICを無視するようにする
無視したいNICを探す。
nmcli device status
/etc/NetworkManager/conf.d/99-unmanaged-devices.conf
を作成する。パススルーするNIC
は自分のNICに置き換える。
[keyfile] unmanaged-devices=interface-name:パススルーするNIC
NetworkManagerを再読み込みする。
systemctl reload NetworkManager
設定が反映されてるか確認する。
nmcli device status
仮想環境のセットアップ
基本的に普通のVMのセットアップなので要所だけ書いていきます。
セットアップする前にvirtioのWindows用ドライバのISOをダウンロードしておく。
virt-managerで作成ウィザードに沿って仮想マシンを作成し、インストールの前に設定をカスタマイズする
にチェックを入れ動かす前に以下の設定する。
チップセット
をQ35
にする。ファームウェア
をUEFI
にする。- CPUの
ホストCPUの設定をコピーする
を有効に、CPUトポロジーの手動設定
を有効にし、ソケット数
を1
、コア数
を割り当てたいコア数にする。 - ディスク1の
バスの種類
をVirtIO
にする。 - ハードウェアの追加から
ストレージ
を選択し、デバイスの種類
をCD-ROM
にし、カスタムストレージの選択または作成
の管理
からダウンロードしておいたドライバのISOを選択する。 - NICを削除する。
- ハードウェアの追加から
PCIホストデバイス
を選択し、パススルー用に用意したNICを選択して追加する。
設定が終わったら仮想マシンが起動するので、Windowsのインストーラーに従ってインストールを進め、インストール場所の選択まで来たら、ドライバーの読み込み
からVirtIOのドライバーを読み込み先に進む。
セットアップが終わったらデバイスマネージャーでほかのデバイスにあるデバイスにVirtIOのドライバを当てます。
終わったらシャットダウンします。
GPUをパススルーする
AMD構成ではNVIDIA構成みたいに変なことしなくてもパススルーできます。
VMの設定でハードウェアの追加からPCIホストデバイス
を選択し、GPU本体とGPUに付随しているAudio Deviceなどを追加し、USBリダイレクト
を使うUSBデバイス分追加します。参考として、Valve Indexは3個、ドングルは1個です。
設定が終わったらVMを起動します。タスクマネージャーでパススルーしたGPUが表示されていれば成功です。
必要なソフトを入れる
必要なソフト(当社比)
- Vivaldi(ブラウザ)
- CorvusSKK(XSOverlayから日本語入力したい)
- Git for Windows(自作ソフトがGitから取ってこないとない)
- Node.js(OSC用の自作ソフトがNode.jsないと動かない)
- Windows Terminal(OSC用自作ソフトが
npm start
で起動するので) - LhaForge(色々解凍できる)
SteamVRを動くようにする
Questではここまでのセットアップで一通り動きました。ですがIndexではそうは行きませんでした。SteamVRが動かなかったり、何故かSteamVRのメニューが出ない不具合が起きます。なので追加のセットアップが必要です。
まずVM側でSteamVRのインストールを済ませます。
次にパススルーしたGPUにディスプレイとHMDを接続してください。ディスプレイはセットアップ後は接続されていればいいので専用に用意する必要はないです。ただしセットアップ中表示できる必要はあります。
刺したらVMを一旦シャットダウンし、VMの設定からビデオのモデルをNone
にします。
それからVMを起動し、HMDのUSBデバイスとキーボードとマウスをVMにリダイレクトします。
Valve Indexの場合はValve Software Index HMD
、Valve Software Valve VR Radio
、Valve Software Valve VR Radio & HMD Mic
をリダイレクトする必要があります。
VMが起動したら接続したディスプレイ、マウス、キーボードを利用して、SteamVRを起動しセットアップを終わらせます。
SteamVRのメニューが表示される所まで確認できればOKなので一旦VMをシャットダウンします。
Looking Glassを導入する
物理ディスプレイの必要なVMなんて扱いにくいのでLooking Glassを利用してホスト側にゲスト側の画面を出せるようにします。
Looking Glassは当然の権利のようにAURにあるのでそこから導入すればOK。
trizen -S looking-glass
次にディスプレイの解像度から必要な共有メモリの量を計算します。計算式は以下。
width * height * 4 * 2 = total bytes total bytes / 1024 / 1024 = total megabytes + 10
わたしの環境の場合はこんな感じ。
2560 * 1440 * 4 * 2 = 29491200 29491200 / 1024 / 1024 = 28.125 + 10 = 38.125
メモリの値は最も近い2の累乗に切り上げる必要があるのでこの場合は64MB必要ということになる。
必要なメモリ量がわかったら、VMの設定の概要から詳細ではなくXMLを選択し、<devices>
から</devices>
の間に以下を追加する。必要なメモリ量
は計算して出した値を入れる。(これ以降このXML編集は何度か使います。)
<shmem name='looking-glass'> <model type='ivshmem-plain'/> <size unit='M'>必要なメモリ量</size> </shmem>
<memballoon>
の項目がある場合は以下に置き換える。
<memballoon model="none"/>
共有メモリの設定を作る。
/etc/tmpfiles.d/10-looking-glass.conf
を以下の内容で作成する。coke
は自分のユーザー名に置き換える。
f /dev/shm/looking-glass 0660 coke kvm -
反映させる
sudo systemd-tmpfiles --create /etc/tmpfiles.d/10-looking-glass.conf
VMを起動して、IVSHMEMのドライバをダウンロードしてデバイスマネージャーのシステムデバイスに居るPCI標準RAMコントローラー
にダウンロードしてきたドライバを当てる。
Looking Glassのホストをダウンロードしてインストールする。
インストールが完了したらスタートメニューに項目が追加されてるので、そこから起動する。
起動したらLinux側のクライアントを起動する。
looking-glass-client -s -a
起動したクライアントにVM側の画面が映れば成功。
キーボードとマウスをいい感じにパススルーする
画面がホスト側から見えるようになったらマウスとキーボードもいい感じに切り替えたくなるのが人間という強欲な生き物です。
なので簡単に切り替えできるようにします。
まず自分をlibvirt
、kvm
、input
に追加します。coke
は自分のユーザー名に置き換えてください。
usermod -aG libvirt coke usermod -aG kvm coke usermod -aG input coke
追加したら権限を反映させるためにVMを起動するまでのどこかのタイミングでホストを再起動します。
次に自分の使ってるキーボードとマウスのevdev
を探します。
ls -al /dev/input/by-id/
出力から自分の使ってるデバイスの名前を探します。
わたしの環境の場合はこんな感じ。
合計 0 drwxr-xr-x 2 root root 180 8月 13 20:39 ./ drwxr-xr-x 4 root root 440 8月 13 20:39 ../ lrwxrwxrwx 1 root root 10 8月 11 20:08 usb-047d_Kensington_Expert_Mouse-event-mouse -> ../event11 lrwxrwxrwx 1 root root 9 8月 11 20:08 usb-047d_Kensington_Expert_Mouse-mouse -> ../mouse0 lrwxrwxrwx 1 root root 10 8月 10 18:29 usb-ASRock_LED_Controller_A02019100900-event-joystick -> ../event20 lrwxrwxrwx 1 root root 6 8月 10 18:29 usb-ASRock_LED_Controller_A02019100900-joystick -> ../js0 lrwxrwxrwx 1 root root 10 8月 10 18:29 usb-Creative_Technology_Ltd_Sound_Blaster_Play__3_00101517-event-if03 -> ../event10 lrwxrwxrwx 1 root root 10 8月 10 18:29 usb-Etron_Technology__Inc._3D_Camera_00000001-event-if00 -> ../event15 lrwxrwxrwx 1 root root 10 8月 10 18:29 usb-Unicomp_Inc_Ruffian7_2_Fx_Left_BS_PC5250_v7_37-event-kbd -> ../event17
この場合、この中で必要なのはusb-047d_Kensington_Expert_Mouse-event-mouse
とusb-Unicomp_Inc_Ruffian7_2_Fx_Left_BS_PC5250_v7_37-event-kbd
です。
同じデバイス名で複数ある場合がありますが、cat
コマンドでよくわからない出力が出る方が使える方です。どっちも使えるならeventって付いてるのを選べばいいと思います。
<domain type='kvm'>
を
<domain xmlns:qemu="http://libvirt.org/schemas/domain/qemu/1.0" type="kvm">
にします。
次に</devices>
の下(<domain>
から</domain>
の間)に以下の内容を追加します。マウスのデバイス名
、キーボードのデバイス名
は自分のデバイスに置き換えてください。
<qemu:commandline> <qemu:arg value="-object"/> <qemu:arg value="input-linux,id=mouse1,evdev=/dev/input/by-id/マウスのデバイス名"/> <qemu:arg value="-object"/> <qemu:arg value="input-linux,id=kbd1,evdev=/dev/input/by-id/キーボードのデバイス名,grab_all=on,repeat=on"/> </qemu:commandline>
わたしの場合はこんな感じ。
<qemu:commandline> <qemu:arg value="-object"/> <qemu:arg value="input-linux,id=mouse1,evdev=/dev/input/by-id/usb-047d_Kensington_Expert_Mouse-event-mouse"/> <qemu:arg value="-object"/> <qemu:arg value="input-linux,id=kbd1,evdev=/dev/input/by-id/usb-Unicomp_Inc_Ruffian7_2_Fx_Left_BS_PC5250_v7_37-event-kbd,grab_all=on,repeat=on"/> </qemu:commandline>
次に/etc/libvirt/qemu.conf
の中の以下の項目を編集します。coke
は自分のユーザー名に置き換えてください。
user = "coke" group = "kvm"
cgroup_device_acl
の項目も以下のように編集する。キーボードのデバイス名
とマウスのデバイス名
は自分のデバイスに置き換える。
cgroup_device_acl = [ "/dev/null", "/dev/full", "/dev/zero", "/dev/random", "/dev/urandom", "/dev/ptmx", "/dev/kvm", "/dev/kqemu", "/dev/rtc","/dev/hpet", "/dev/sev", "/dev/input/by-id/キーボードのデバイス名", "/dev/input/by-id/マウスのデバイス名" ]
編集したらlibvirtdを再起動する。
systemctl restart libvirtd
VMを起動して左右のCtrlを同時に押して入力がゲスト側に切り替われば成功。
VMの設定を調整する
VMのセットアップが終わった段階でVRCを起動しようとした人はわかると思いますが実は今の状態だとEACのVM検知を回避できません。
なのでまずはVM検知を回避します。
まずホスト側でsudo dmidecode
を実行します。
出力からBIOS Infomation
のVendor
、Version
、Release Date
をメモするか覚えます。
次にXMLに以下を追記します。
</vcpu>
の下(<domain>
から</domain>
の間)に以下を追記する。メモったやつ
は上でメモったやつを入れます。
参考にした記事ではSystem Infomation
の方もちゃんと入れていますが、わたしの環境ではTo Be Filled By O.E.M.
になってる項目なので適当に埋めて遊びました。これでも普通に回避して起動するので余程異常でない限りは好きに変えても多分OKです。
<sysinfo type="smbios"> <bios> <entry name="vendor">メモったやつ</entry> <entry name="version">メモったやつ</entry> <entry name="date">メモったやつ</entry> </bios> <system> <entry name="manufacturer">wataame cafe</entry> <entry name="product">secure machine</entry> <entry name="version">1.0.0</entry> <entry name="serial">oishii</entry> </system> </sysinfo>
<os>
から</os>
の間に以下を追記。
<smbios mode='sysinfo'>
次はCPUの割り当てをしていきます。
</vcpu>
の下(<domain>
から</domain>
の間)に以下を追記します。<vcpupin>
の数とcpuset
で指定するコアは自分のマシンに合わせて調整してください。以下の設定の場合、5900Xの2個目のCCDのスレッドを12コアのCPUとして割り当てしています。
<cputune> <vcpupin vcpu="0" cpuset="12"/> <vcpupin vcpu="1" cpuset="13"/> <vcpupin vcpu="2" cpuset="14"/> <vcpupin vcpu="3" cpuset="15"/> <vcpupin vcpu="4" cpuset="16"/> <vcpupin vcpu="5" cpuset="17"/> <vcpupin vcpu="6" cpuset="18"/> <vcpupin vcpu="7" cpuset="19"/> <vcpupin vcpu="8" cpuset="20"/> <vcpupin vcpu="9" cpuset="21"/> <vcpupin vcpu="10" cpuset="22"/> <vcpupin vcpu="11" cpuset="23"/> </cputune>
VRChatにログインする
すべての作業が終わったらVRChatをVRで起動しましょう。Windows上で動かしてるのと変わらないほど快適に動きます。なんならLinuxすら。
参考
https://wiki.archlinux.jp/index.php/Ryzen https://wiki.archlinux.jp/index.php/OVMF_%E3%81%AB%E3%82%88%E3%82%8B_PCI_%E3%83%91%E3%82%B9%E3%82%B9%E3%83%AB%E3%83%BC https://qiita.com/Sumi-Sumi/items/0900d6accd9a7f9d02c2#vrchat%E3%81%AB%E3%83%AD%E3%82%B0%E3%82%A4%E3%83%B3%E3%81%97%E3%81%A6%E3%83%95%E3%83%AB%E3%83%88%E3%83%A9%E3%81%99%E3%82%8B https://access.redhat.com/documentation/ja-jp/red_hat_enterprise_linux/8/html/configuring_and_managing_networking/configuring-networkmanager-to-ignore-certain-devices_configuring-and-managing-networking https://www.tauceti.blog/posts/linux-amd-x570-nvidia-gpu-pci-passthrough-1-the-hardware/ https://www.tauceti.blog/posts/linux-amd-x570-nvidia-gpu-pci-passthrough-2-prepare-linux/ https://www.tauceti.blog/posts/linux-amd-x570-nvidia-gpu-pci-passthrough-4-setup-gpu/ https://www.tauceti.blog/posts/linux-amd-x570-nvidia-gpu-pci-passthrough-5-looking-glass/ https://looking-glass.io/docs/B5.0.1/install/ https://passthroughpo.st/using-evdev-passthrough-seamless-vm-input/