この記事はopenSUSE Advent Calendar 2022 8日目の記事です。
virtio-mem とは
virtio-memとは、ゲスト(仮想マシン)のメモリ提供手段の一つです。
Overview
A virtio-mem device manages a memory region in the the guest physical address space of a virtual machine and provides a dynamic amount of memory via this memory region to the virtual machine.
https://virtio-mem.gitlab.io/user-guide/
従来のメモリ管理の方法として virtio balloon memory がありましたが、virtio-memには ゲストに割り当てたメモリを、ゲストの起動中に縮小できる という特徴があります。
なお現在 Tech-preview扱いのため、本番環境での使用は推奨されていません。念のため。
動作要件
ネスト環境で virtio-mem は正常に動作しないため、L2ゲストに virtio-mem は使用できません。
またメモリを動的に縮小・完全に取り外し可能であるというvirtio-memの性質上、virtio-mem とは別に初期メモリの領域をゲストに割り当てる必要があります。
virtio-mem は新しい実装のため、qemu5.1.0-rc1 以降、libvirtv7.9.0-rc1 以降の環境が必要です。
注意点として、ゲストOSはLinuxのみ、かつ kernel 5.8-rc1 以降のみ対応しています。
また対応アーキテクチャは現時点で x64・aarch64のみです。
メジャーどころでは openSUSE Leap 15.4 や Fedora 36/37、RHEL 9.0/9.1 が上記の環境を備えています。
今回は openSUSE Leap 15.4 で構築したKVMホスト上で検証を行います。
検証環境:
GIGABYTE MZ32-AR0
- AMD EPYC 7401P / DDR4 256GB / PM1725(HHHL) 3.2TBopenSUSE Leap 15.4
- kernel : 5.14.21-150400.24.11
- qemu : 6.2.0-150400.37.5.3
- libvirt : 8.0.0-150400.5.8
現在の手元環境たち。機会があればそのうちまとめたい。
virtio-mem を使用するゲストを新規に作成する
virtio-memを使用するゲストを作成する場合、virt-install を使用することでデプロイが可能です。
virt-install で virtio-memを設定する場合、以下のオプションの設定が必要になります。
--cpu numa.cell.id=作成するNUMAノードID,numa.cell.cpus=NUMAに割り当てるコア,numa.cell.memory=初期メモリのサイズ(kiB),model=host
--memory maxMemory=初期メモリ+virtio-memのサイズ(MiB),maxMemory.slots=virtio-mem用に確保するメモリスロット数
--memdev model=virtio-mem,target.size=virtio-memのサイズ(MiB),target.node=割り当てるNUMAノードID,target.block=virtio-memの割り当て最小単位(kiB),target.requested=0,address.type=pci,address.domain=0x0000,address.bus=0x00,address.slot=0x08,address.function=0x0
...見ずらいですね。
初期メモリ 4GiB、virtio-mem 8GiB を設定する場合、virt-install 実行時の引数は以下のようになります。
#!/bin/sh HOSTNAME=test01 #IMAGE=/mnt/vm/iso/openSUSE-Leap-15.4-DVD-x86_64-Build243.2-Media.iso IMAGE=/mnt/vm/iso/Fedora-Server-dvd-x86_64-36-1.5.iso virt-install \ --name $HOSTNAME \ --cpu numa.cell.id=0,numa.cell.cpus=0-1,numa.cell.memory=4194304,model=host \ ★ --hvm \ --virt-type kvm \ --memory maxMemory=12288,maxMemory.slots=2 \ ★ --memdev model=virtio-mem,target.size=8192,target.node=0,target.block=2048,target.requested=0,address.type=pci,address.domain=0x0000,address.bus=0x00,address.slot=0x08,address.function=0x0 \ ★ --vcpus 2 \ --arch x86_64 \ --boot hd \ --disk path=/mnt/vm/$HOSTNAME.qcow2,format=qcow2,size=5,bus=virtio \ --network network=management,model=virtio \ --location $IMAGE \ --noautoconsole
実際に作成してみます。
halni@SUSE-MZ32:~> sudo virt-install \
> --name virtiomem01 \
> --cpu numa.cell.id=0,numa.cell.cpus=0-1,numa.cell.memory=4194304,model=host \
> --hvm \
> --virt-type kvm \
> --memory maxMemory=12288,target.size=8192,target.node=0,target.block=2048,target.requested=0,address.type=pci,address.domain=0x0000,address.bus=0x00,address.slot=0x08,address.function=0x0 \
> --vcpus 2 \
> --arch x86_64 \
> --boot hd \
> --disk path=/mnt/vm/virtiomem01.qcow2,format=qcow2,size=5,bus=virtio \
> --network network=management,model=virtio \
> --location /mnt/vm/iso/openSUSE-Leap-15.4-DVD-x86_64-Build243.2-Media.iso \
> --noautoconsole
Starting install...
Retrieving 'linux' | 0 B 00:00:00 ...
Retrieving 'initrd' | 0 B 00:00:00 ...
Allocating 'virtiomem01.qcow2' | 0 B 00:00:00 ...
Creating domain... | 0 B 00:00:00
Domain is still running. Installation may be in progress.
You can reconnect to the console to complete the installation process.
halni@SUSE-MZ32:~> sudo virsh list --all
Id Name State
------------------------------------
~~~
24 virtiomem01 running
~~~
halni@SUSE-MZ32:~> sudo virsh dumpxml virtiomem01 | xmllint --xpath //memory -
<memory unit="KiB">12582912</memory>
<memory model="virtio-mem">
<target>
<size unit="KiB">8388608</size>
<node>0</node>
<block unit="KiB">2048</block>
<requested unit="KiB">0</requested>
<current unit="KiB">0</current>
</target>
<alias name="virtiomem0"/>
<address type="pci" domain="0x0000" bus="0x00" slot="0x08" function="0x0"/>
</memory>
halni@SUSE-MZ32:~>
virtio-memが設定されたゲストが作成されています。
なお、virtio-memで指定したメモリはまだ使用されていないため、現時点でゲストに見えているメモリサイズは初期メモリの4GiBのみとなります。
(ゲストへのメモリ追加・縮小方法については後に説明します)
定義済みのゲストに virtio-mem を設定する
定義済みのゲストに virtio-memを設定していきます。
今回は初期メモリ4GiB + virtio-mem 4GiB分 を追加していきます。
virtio-memはnumaに紐づくため、1CPU構成であってもNUMAの設定が必要になります。
設定項目としては
halni@SUSE-MZ32:~> diff -u SUSE-cloud01.xml SUSE-cloud01-virtiomem.xml
--- SUSE-cloud01.xml 2022-12-04 09:49:17.487963281 +0900
+++ SUSE-cloud01-virtiomem.xml 2022-12-04 10:03:23.665284521 +0900
@@ -6,7 +6,8 @@
<libosinfo:os id="http://opensuse.org/opensuse/15.4"/>
</libosinfo:libosinfo>
</metadata>
- <memory unit='KiB'>4194304</memory>
+ <maxMemory slots='2' unit='KiB'>8388608</maxMemory>
+ <memory unit='KiB'>8388608</memory>
<currentMemory unit='KiB'>4194304</currentMemory>
<vcpu placement='static'>4</vcpu>
<os>
@@ -18,7 +19,11 @@
<apic/>
<vmport state='off'/>
</features>
- <cpu mode='host-passthrough' check='none' migratable='on'/>
+ <cpu mode='host-passthrough' check='none' migratable='on'>
+ <numa>
+ <cell id='0' cpus='0-3' memory='4194304' unit='KiB'/>
+ </numa>
+ </cpu>
<clock offset='utc'>
<timer name='rtc' tickpolicy='catchup'/>
<timer name='pit' tickpolicy='delay'/>
@@ -177,6 +182,16 @@
<backend model='random'>/dev/urandom</backend>
<address type='pci' domain='0x0000' bus='0x05' slot='0x00' function='0x0'/>
</rng>
+ <memory model='virtio-mem'>
+ <target>
+ <size unit='KiB'>4194304</size>
+ <node>0</node>
+ <block unit='KiB'>2048</block>
+ <requested unit='KiB'>0</requested>
+ </target>
+ <alias name='ua-virtiomem0'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/>
+ </memory>
</devices>
</domain>
halni@SUSE-MZ32:~>
といった感じ。
ゲストメモリの追加・縮小方法
ゲストへの virtio-mem の追加・縮小は
$ virsh update-memory-device $DOMAIN --requested-size $SIZE
で可能です。
実際に変更してみます。
halni@SUSE-MZ32:~> ssh root@SUSE-cloud01 cat /proc/meminfo | grep Total
MemTotal: 4010632 kB ★ 4GiB
SwapTotal: 0 kB
VmallocTotal: 34359738367 kB
CmaTotal: 0 kB
HugePages_Total: 0
halni@SUSE-MZ32:~>
halni@SUSE-MZ32:~> sudo virsh update-memory-device SUSE-cloud01 --requested-size 2GiB ★ 2GiB追加
halni@SUSE-MZ32:~> sudo virsh dumpxml SUSE-cloud01 | xmllint --xpath //memory -
<memory unit="KiB">8388608</memory>
<memory model="virtio-mem">
<target>
<size unit="KiB">4194304</size>
<node>0</node>
<block unit="KiB">2048</block>
<requested unit="KiB">2097152</requested>
<current unit="KiB">2097152</current> ★ メモリ量が反映されている
</target>
<alias name="ua-virtiomem0"/>
<address type="pci" domain="0x0000" bus="0x00" slot="0x04" function="0x0"/>
</memory>
halni@SUSE-MZ32:~>
halni@SUSE-MZ32:~> ssh root@SUSE-cloud01 cat /proc/meminfo | grep Total
MemTotal: 6107784 kB ★ 6GiB
SwapTotal: 0 kB
VmallocTotal: 34359738367 kB
CmaTotal: 0 kB
HugePages_Total: 0
halni@SUSE-MZ32:~>
--requested-size 0 とすると virtio-mem 分のメモリ領域が 0 B になります。
halni@SUSE-MZ32:~> sudo virsh update-memory-device SUSE-cloud01 --requested-size 0
halni@SUSE-MZ32:~> sudo virsh dumpxml SUSE-cloud01 | xmllint --xpath //memory -
<memory unit="KiB">8388608</memory>
<memory model="virtio-mem">
<target>
<size unit="KiB">4194304</size>
<node>0</node>
<block unit="KiB">2048</block>
<requested unit="KiB">0</requested>
<current unit="KiB">0</current> ★ 0 になっている
</target>
<alias name="ua-virtiomem0"/>
<address type="pci" domain="0x0000" bus="0x00" slot="0x04" function="0x0"/>
</memory>
halni@SUSE-MZ32:~> ssh root@SUSE-cloud01 cat /proc/meminfo | grep Total
MemTotal: 4010632 kB ★ 4GiB
SwapTotal: 0 kB
VmallocTotal: 34359738367 kB
CmaTotal: 0 kB
HugePages_Total: 0
halni@SUSE-MZ32:~>
virtio-mem の挙動
基本的に virtio-mem では update-memory-deviceで設定したメモリサイズはゲストに即時反映されます。
ゲストが virtio-mem 領域を使用していた場合、 ゲストは頑張ってメモリを解放します。
virtio-mem を設定した Fedora ゲスト上で、メモリを使用した状態で virtio-mem を 0GiB に設定してみます。 virto-mem 領域の変化を観察するため、メモリ構成は
初期メモリ 2GiB + virtio-mem 4GiB 計 6GiB
としています。
まずは virto-mem を計 4GiB アタッチ
halni@SUSE-MZ32:~> sudo virsh update-memory-device Fedora01 --requested-size 4GiB
halni@SUSE-MZ32:~> ssh halni@Fedora01 cat /proc/meminfo | grep Total
MemTotal: 6145712 kB
SwapTotal: 1950716 kB
VmallocTotal: 34359738367 kB
CmaTotal: 0 kB
HugePages_Total: 0
halni@SUSE-MZ32:~> sudo virsh dumpxml Fedora01 | xmllint --xpath //memory -
<memory unit="KiB">6291456</memory>
<memory model="virtio-mem">
<target>
<size unit="KiB">4194304</size>
<node>0</node>
<block unit="KiB">2048</block>
<requested unit="KiB">4194304</requested>
<current unit="KiB">4194304</current>
</target>
<alias name="ua-virtiomem0"/>
<address type="pci" domain="0x0000" bus="0x00" slot="0x04" function="0x0"/>
</memory>
halni@SUSE-MZ32:~>
ゲスト上でメモリ使用量を増やします。現時点でのメモリ使用量はこんな感じ。

halni@SUSE-MZ32:~> sudo virsh update-memory-device Fedora01 --requested-size 0GiB
halni@SUSE-MZ32:~> sudo virsh dumpxml Fedora01 | xmllint --xpath //memory -
<memory unit="KiB">6291456</memory>
<memory model="virtio-mem">
<target>
<size unit="KiB">4194304</size>
<node>0</node>
<block unit="KiB">2048</block>
<requested unit="KiB">0</requested>
<current unit="KiB">3418112</current>
</target>
<alias name="ua-virtiomem0"/>
<address type="pci" domain="0x0000" bus="0x00" slot="0x04" function="0x0"/>
</memory>
halni@SUSE-MZ32:~> sudo virsh dumpxml Fedora01 | xmllint --xpath //memory - | grep current
<current unit="KiB">3155968</current>
halni@SUSE-MZ32:~> sudo virsh dumpxml Fedora01 | xmllint --xpath //memory - | grep current
<current unit="KiB">2895872</current>
halni@SUSE-MZ32:~> sudo virsh dumpxml Fedora01 | xmllint --xpath //memory - | grep current
<current unit="KiB">1691648</current>
halni@SUSE-MZ32:~> sudo virsh dumpxml Fedora01 | xmllint --xpath //memory - | grep current
<current unit="KiB">1560576</current>
halni@SUSE-MZ32:~> sudo virsh dumpxml Fedora01 | xmllint --xpath //memory - | grep current
<current unit="KiB">1443840</current>
halni@SUSE-MZ32:~>
~~~
halni@SUSE-MZ32:~> sudo virsh dumpxml Fedora01 | xmllint --xpath //memory - | grep current
<current unit="KiB">212992</current>
halni@SUSE-MZ32:~>
大体5分くらい掛けて 3.8GiB 程度が解放されました。
この時の sar を確認してみます。
図に引いた赤線 8:37 あたりで --requested-size 0GiB を実行しています。
まずはメモリから。

8:37 を境に、avail が大きく減少し、buffer や cache への割り当ても減っていることがわかります。
システムの負荷についても確認してみます。

変更したタイミングでCPUの負荷が上昇しています。内部で大きな処理が走っていたようです。
ゲストの messages を確認してみます。
Dec 7 20:37:28 Fedora01 kernel: Out of memory: Killed process 1835 (wireplumber) total-vm:472732kB, anon-rss:4944kB, file-rss:0kB, shmem-rss:0kB, UID:1000 pgtables:140kB oom_score_adj:200 Dec 7 20:39:41 Fedora01 kernel: Out of memory: Killed process 1833 (pipewire) total-vm:360468kB, anon-rss:4172kB, file-rss:0kB, shmem-rss:20kB, UID:1000 pgtables:148kB oom_score_adj:200 Dec 7 20:39:41 Fedora01 kernel: Out of memory: Killed process 2547 (pipewire-pulse) total-vm:244328kB, anon-rss:2296kB, file-rss:0kB, shmem-rss:0kB, UID:1000 pgtables:100kB oom_score_adj:200 Dec 7 20:39:41 Fedora01 kernel: Out of memory: Killed process 1837 (dbus-broker-lau) total-vm:10388kB, anon-rss:344kB, file-rss:0kB, shmem-rss:0kB, UID:1000 pgtables:60kB oom_score_adj:200 Dec 7 20:39:41 Fedora01 kernel: Out of memory: Killed process 1050 ((sd-pam)) total-vm:176376kB, anon-rss:2244kB, file-rss:0kB, shmem-rss:0kB, UID:1000 pgtables:100kB oom_score_adj:100 Dec 7 20:39:41 Fedora01 kernel: Out of memory: Killed process 4949 (pipewire) total-vm:226584kB, anon-rss:556kB, file-rss:0kB, shmem-rss:0kB, UID:1000 pgtables:80kB oom_score_adj:200 Dec 7 20:39:41 Fedora01 kernel: Out of memory: Killed process 1048 (systemd) total-vm:22408kB, anon-rss:2664kB, file-rss:0kB, shmem-rss:0kB, UID:1000 pgtables:88kB oom_score_adj:100 Dec 7 20:39:41 Fedora01 kernel: Out of memory: Killed process 3817 (qemu-system-x86) total-vm:3369484kB, anon-rss:139572kB, file-rss:0kB, shmem-rss:0kB, UID:107 pgtables:1372kB oom_score_adj:0
多くのプロセスが OOM Killer によってメモリ不足を理由に停止していることがわかります。
