Ubuntu+KVM+GVT-gで仮想GPUを仮想環境に割り当てる

# 概要

お久しぶりです。えぢぢーんです。
適当な気持ちでandroidアプリを作ろうとしたら、
すごく難しかったので、息抜きでUbuntuをいじってました。

![dj_neko.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3935049/cb337d0a-1a20-fec0-144a-63a53a9a1c05.gif)

本日やるとことはUbuntuのKVMで仮想環境を作り、そこにIntel GVT-gを使ってiGPU（仮想GPU）を割り当てることです。

ユースケースとしてはどうしても仮想環境にgpuを割り当てたいけど、GPUパススルーは嫌だ（そんな人いる？）そんな人に向けた記事です。

### ちょっと説明

おそらくこういうことをやるならほんとはNVIDIAのGPUを仮想化することがゴールになると思いますが、現状NVIDIAのGPU仮想化はサーバー用のGPUに限られており、基本的にはコンシューマ版のGPUの仮想化は対応していません。（なんか有志が作ったパッチを当てると動くとかなんとか）

じゃぁしょうがないからIntel GVT-gを使って仮想上でGPUを有効にするか！ただ3D アクセラレーションとかは有効にできるから3Dゲームとかやれるよ！という記事

## 環境
+ ホストOS : Ubuntu 24.04 LTS Desktop
+ ホストCPU : Intel(R) Core(TM) i7-8700 CPU @ 3.20GHz
+ ゲストOS : Windows 7 Pro
+ 仮想環境 : kvm + qemu + virtio

今回はintel GVT-gを使うのでそれに対応したCPU
第6世代以降(Skylake以降)が必要になります


## KVMインストール
まずはkvmと必要パッケージをインストール
```bash
sudo apt-get install qemu-kvm libvirt-daemon-system libvirt-clients bridge-utils virtinst libosinfo-bin virt-manager
```
以下を打つと仮想マシンマネージャが立ち上がる
```bash
sudo virt-manager
```
![Screenshot from 2024-12-12 16-08-30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3935049/d44e3c92-81b9-ecce-6f33-6bd06b34b2dd.png)

## カーネル設定(IOMMU・GVT有効化)
まずカーネルってなんやってなったんですけど
以下検索AIくんより
>Linuxカーネルとは、Linuxオペレーティングシステム（OS）の中核部分で、コンピューターのハードウェアとプロセスの間で通信しながらリソースを管理するソフトウェアです

だそうです。
なんとなくOSの大事な処理をするところだなぁくらいにしか理解してません。

で、今からカーネルをいじるのはIOMMUを有効化するためで
IOMMUはInput-Output Memory Management Unitの略で、メモリを管理する機能です

仮想環境を建てるときはホストと仮想のメモリ領域を完全に分離する必要があります（仮想からの不正アクセスを防ぐため）
この機能にIOMMUが使われていて、更にGPUは物理アドレスを参照する設計になっているのでIOMMUを使わないとそいつらがわからんとなるわけです
（自分の理解なので間違ってたらごめんなさい）

で今回はintelのcpuに内蔵されたgpu仮想化をしたいので
+ i915.eneble_gvt=1(intelのgvt機能の有効化)
+ i915.enable_guc=0(なんかgvt機能と競合する？)
+ intel_iommu=on(intelのiommu機能の有効化)
3つを行う必要がありますんで以下行をgrubの設定ファイルの以下部分を

```bash:/etc/default/grub
GRUB_CMDLINE_LINUX=""
```
こんな感じに追記

```bash:/etc/default/grub
GRUB_CMDLINE_LINUX="i915.enable_gvt=1 i915.enable_guc=0 intel_iommu=on"
```

grubはブート時にカーネルロードとかを担当する機能です
ここに上記コマンドを書いとくと反映されます

もう一個kvmgt, vfio-iommu-type1, mdevを有効にする？
（ここは理解できてないです）

```bash:/etc/modules-load.d/kvmgt.conf
kvmgt
```
このファイルを作らないとうまく行かなかったので作っときましょ

以上終わったら再起動

```bash
update-grub
reboot
```

再起動完了したらカーネル設定がうまく行ったか以下で確認

```bash
dmesg | grep iommu
```
うまく行ってなかったら
BIOSの設定でVt-dの設定とか見直すと良い

## 仮想GPUの確保

先程のgrubの設定を確認するために
まずはPCIデバイスを確認する

```bash
sudo lspci | grep VGA
```
VGAがついてるPCIデバイスを確認すると
以下が出てきた（自分の環境では２つ）

```bash
00:02.0 VGA compatible controller: Intel Corporation CoffeeLake-S GT2 [UHD Graphics 630]
01:00.0 VGA compatible controller: NVIDIA Corporation TU106 [GeForce RTX 2070] (rev a1)
```
片方はNVIDIAのGPUなので、今回はIntelの00:02.0が欲しかったID

このIDはこのあとも使うので変数化して
(0000:を頭につけてね)
```bash
PCI_ID="0000:00:02.0"
```
以下で対応するGVTの種類を確かめます

```bash
ls /sys/devices/pci0000\:00/$PCI_ID/mdev_supported_types/
```

なんか出力がでてきたら成功

```bash
i915-GVTg_V5_4  i915-GVTg_V5_8
```
これの内訳は以下のようになっていて

| プロファイル         | VRAM容量            | 最大解像度       |
|---------------------|---------------------|-----------------|
| `i915-GVTg_V5_1`    | 最大512MB           | 1920x1200       |
| `i915-GVTg_V5_2`    | 最大256MB           | 1920x1200       |
| `i915-GVTg_V5_4`    | 最大128MB           | 1920x1200       |
| `i915-GVTg_V5_8`    | 最大64MB            | 1024x768        |

数字をが小さければよい性能を引き出せるけど
CPUによってメモリの最大値が決められてるので
並列数は減るらしいのでトレードオフの関係

で、以下を叩くと仮想GPUを確保できる

```bash
GVT_ID=$(uuidgen)
GVT_TYPE=i915-GVTg_V5_4
echo $GVT_ID >  /sys/devices/pci0000\:00/$PCI_ID/mdev_supported_types/$GVT_TYPE/create
```

GVT_IDは仮想GPUのuuidを作成していて
このIDを使って仮想マネージャなどに登録する
あとでも使うので以下で確認

```bash
echo $GVT_ID
```
念の為GPUが確保できたかも以下で確認

```bash
ls /sys/devices/pci0000\:00/$PCI_ID/
```
フォルダの中にGVT_IDが名前になったフォルダができていれば問題ない

で、取得したGPUは電源ON/OFFで消えちゃうので
サービス化して自動で取得するようにしたほうが勝手が良い
（QEMUのhook機能を使っても良いです）

自分はわかりやすいのでサービス化する手順をやってます
```:/usr/lib/systemd/system/getvgpu.service
[Unit]
Description=Create intel vGPU

[Service]
Type=oneshot
ExecStart=/bin/sh -c "echo '<GVT_IDの値>' > /sys/devices/pci0000:00/<PCI_IDの値>/mdev_supported_types/i915-GVTg_V5_4/create"
ExecStop=/bin/sh -c "echo '1' > /sys/devices/pci0000:00/<PCI_IDの値>/<GVT_IDの値>/remove"
RemainAfterExit=yes

[Install]
WantedBy=graphical.target
```
これを作ってsystem起動しておけば
ubuntuを起動するたび特定のuuidで仮想GPUを確保してくれます
（getvgpu.serviceの名前はなんでもいいです）

```bash
sudo systemctl enable getvgpu
```

これで自動確保まで完了です
うまくサービス化できてるか確認するために
再起動して以下で自動的にvgpuが取られているか確認しましょう

```bash
ls /sys/devices/pci0000\:00/<PCI_ID>/
```

自動でvgpuが生成されていれば成功です

## 仮想GPUの確保
ここまできたらあと少し
次はKVMで実際に仮想PCを立ち上げますが
virtioという準仮想環境を使います

以下からstable版のvirtio-win ISOをダウンロードします

https://github.com/virtio-win/virtio-win-pkg-scripts/blob/master/README.md

WindowsのISOは各自調達してください

+ virtio-winのISO
+ windowsのISO

両方用意できたら新しい仮想マシンから

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3935049/941f4307-dec2-243f-e79f-794126a7805b.png)

ISOを選択

![Screenshot from 2024-12-12 17-28-43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3935049/470b0b89-0f4e-257f-d91a-4eb5a44af6c7.png)

メモリ指定（値はご自由に）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3935049/8474bba8-48af-7bd0-8b04-5868b75ad093.png)

ディスク容量（ご自由に）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3935049/34954c8d-61e7-6e25-8b20-e6fdcf5194da.png)

インストールのカスタマイズをONに！！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3935049/eabb3ff4-c7f1-19ea-2377-6e552d8987da.png)

ディスクをSATAからSCSIへ変更

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3935049/f69eb6b7-d87a-9ccc-ce74-34abc4e063ca.png)

先程ダウンロードしたvirtioをCD-ROMとして追加

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3935049/da13fd1a-4faf-7d2b-94b7-7c5b36d57b60.png)

NICをvirtioへ変更

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3935049/4e051762-eb43-f18c-fc07-2ebbe645b183.png)

ディスプレイのリッスンをなしへ変更
OpenGLにチェック

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3935049/4117f151-0a75-7f9c-4337-6266c63b3b8f.png)

SCSIコントローラを追加

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3935049/6080b654-c0a8-a3fb-06c3-ce26f0a00545.png)

最後に作成した仮想GPUを割り当て

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3935049/343d08df-3b6c-8df8-305d-2630ab3e65db.png)

ここでmdevデバイスが見えない場合手順にミスがありますので見返してください

また適用ボタンを押さないと適用されないので
一度見返してみてください

それが終わったらインストール開始

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3935049/1b82bb98-6848-ef83-0f84-a0edd8874938.png)

インストール開始するとこのようにディスクが見えません
（ディスクをSCSIに指定したのでドライバがない）
ドライバを当てると見えるようになります


ドライバの読み込みからvirio isoが入ってるドライブを選択して以下のドライバを当ててください

+ vioscsi: scsiのvertioドライバ
+ NetKVM: networkのvertioドライバ
+ Balloon: メモリのvertioドライバ
+ qxl: ディスプレイのドライバ <*>

※windows８以上はqxlではなくqxldodを使う
※windows7はqxlを使い一回目インストールをミスするけど２回めで通るので根気よく頑張る


全部ドライバがあたったらインストールして放置
適当に寝て待ちましょう

![sleep_neko.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3935049/e9395ac8-662c-c020-200a-72a93f627291.gif)

立ち上がったらデバイスマネージャから謎のディスプレイデバイスが追加されてるはずなのでホスト側で使ってるcpuのドライバ（だいたいIntelのHD Graphics）を入れましょう

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3935049/a5b637ac-d36d-d4c6-9117-d78d3819716a.png)

windows10用のドライバしかないって？
よくわからないけど皆さんこちらに行きますね（三店方式）
ttps://archive.org/download/inteluhd-win7

無事ドライバがあたったら一度仮想の電源を落とします
グラフィックドライバが２個存在していると
追加した仮想GPUが有効化できないので
いらないグラフィックドライバをぶちのめします

まず、仮想GPUのディスプレイを有効にします
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3935049/9449b838-576c-e3bb-30ec-e9cd678d392c.png)

mdev上のxmlから
```bash
display="off"
```
以下へ変更
```bash
display="on"
```

次

ビデオをnoneへ変更

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3935049/2fe936be-46ba-d9fa-d36a-89c1b60e1d64.png)

最後

mdev（仮想GPU）の有効化をしたい
qemu(仮想化システム)でcommmadlineによる設定が必要

毎回commandlineを叩くのは面倒なので
xml上でqemuのcommandを直書きしたいが
何故か仮想マシンマネージャからは適用できない・・・・・

さまよっていたら以下の情報を発見

https://blog.wikichoon.com/2017/03/easy-qemu-commandline-passthrough-with.html

```bash
sudo virt-xml <仮想pcの名前> --edit --confirm --qemu-commandline="test"
```

このコマンドでとりあえず無理やりxmlドメインにqemuコマンドラインのパススルーを書き込むことに成功
（こんなことでええんか？）

![wao_neko.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3935049/9f746304-8ff7-6426-a95d-042874ac0d48.gif)

そしたらxmlの最後に以下のようなコマンドが追加されていると思うので

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3935049/62b02e75-0039-28b2-4ca9-6b8541b18759.png)

```bash
  <qemu:commandline>
    <qemu:arg value="test"/>
  </qemu:commandline>
```

これを消して以下を追加
```bash
  <qemu:override>
    <qemu:device alias="hostdev0">
      <qemu:frontend>
        <qemu:property name="x-igd-opregion" type="bool" value="true"/>
        <qemu:property name="driver" type="string" value="vfio-pci-nohotplug"/>
        <qemu:property name="ramfb" type="bool" value="true"/>
      </qemu:frontend>
    </qemu:device>
  </qemu:override>
```

色々探してみた結果、現在は他のブログで書かれてるようなcommandlineからの入力は廃止されてoverrideの書き方が有効らしい（詳しくはわからない・・・）

これで再度仮想マシンを起動します

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3935049/b551bd49-0f43-62bf-41eb-4a16be73b77d.png)

割と長い時間この画面になりますが、
待つと立ち上がってくるので立ち上がったら
dxdiagでディスプレイ情報を確認

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3935049/41820245-ef87-ffff-502f-90b0f6f913d3.png)

仮想のwindows7で3Dアクセラレータが使用可能であることを確認しました。


## 参考にした方々
めちゃくちゃわかりやすくお世話になりました

https://blog.zgock-lab.net/2019/01/23/gvtg/

最小手順でやっている方

https://naba-san.hatenablog.com/entry/2022/09/19/005709

詳細に説明してくれている方

https://deletefrom.hateblo.jp/entry/2023/09/26/024129#9-GPU-%E3%82%92-VM-%E3%81%AB%E8%A8%AD%E5%AE%9A%E3%81%99%E3%82%8B

## おわり

今回は先人の方々の情報を大分参考にしました
本当に文書化してくれる人たちに感謝です

ただ最新のバージョンだとフォルダ構成や使えない構文などもあり、最新版に翻訳したくらいの仕事はできたとおもいます

次こそは開発中androidアプリの話をします！！