けさらんぱの自由帳

とあるFF14プレイヤーがFF14のこととか関係ないことを書いていく予定のブログです。記載されている会社名・製品名・システム名などは、各社の商標、または登録商標です。

#FF14 をLinuxの仮想環境で動かす話・その2

ホストOSの設定

VFIOの設定

GPUをパススルーするには、ホストOSがGPUを使わないようにしないといけません。 /etc/modprobe.d/vfio.conf に下記の設定をしています。

options vfio-pci ids=10de:1b80,10de:10f0,1b21:1242 disable_vga=1
blacklist nouveau

10de:1b80はGPU、10de:10f0はGPUのオーディオデバイスHDMI出力)、1b21:1242はUSBコントローラです。 disable_vgablacklist の設定は不要かも知れません。

また、 /etc/mkinitcpio.confMODULESvfio vfio_iommu_type1 vfio_pci vfio_virqfd を追加して、 sudo mkinitcpio -p linux を実行します。

カーネルコマンドラインオプションの設定

カーネルコマンドラインオプションに下記の設定を追加しています。

threadirqs intel_iommu=on default_hugepagesz=1G hugepagesz=1G hugepages=8

threadirqs は不要かも知れません。 intel_iommu=on は必須です。 default_hugepagesz=1G hugepagesz=1G hugepages=8 はOSのメモリを仮想環境用に予約しておくために追加しています(この場合は8GiBの静的ヒュージページ)。

仮想環境の設定

仮想環境の設定にはlibvirtを使用しています。OVMFが必要になるので、 Arch LinuxのWiki を参考にして /etc/libvirt/qemu.conf とsystemdの設定をします。

メモリ

ここからlibvirtの定義ファイル( /etc/libvirt/qemu/win.xml )を順番に説明していきます。

<domain type='kvm'>
  <name>win</name>
  <uuid>xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx</uuid>
  <memory unit='GiB'>8</memory>
  <currentMemory unit='GiB'>8</currentMemory>
  <memoryBacking>
    <hugepages/>
    <nosharepages/>
  </memoryBacking>

容量はカーネルコマンドラインオプションで指定したのと同じ8GiBです。 hugepages は予約した領域(静的ヒュージページ)を使う設定です。手元の環境では静的ヒュージページではなくても動作はしたのですが、空きメモリの断片化が進んだときに何が起こるか分からないので静的ヒュージページを使っています。 nosharepages はメモリマージを無効にしますが、無くてもいいと思います。

CPU

  <vcpu placement='static'>6</vcpu>
  <iothreads>1</iothreads>
  <cputune>
    <vcpupin vcpu='0' cpuset='1'/>
    <vcpupin vcpu='1' cpuset='5'/>
    <vcpupin vcpu='2' cpuset='2'/>
    <vcpupin vcpu='3' cpuset='6'/>
    <vcpupin vcpu='4' cpuset='3'/>
    <vcpupin vcpu='5' cpuset='7'/>
    <emulatorpin cpuset='0'/>
    <iothreadpin iothread='1' cpuset='4'/>
    <vcpusched vcpus='0-5' scheduler='batch'/>
    <iothreadsched iothreads='1' scheduler='batch'/>
  </cputune>

CPUは3コア6スレッド分を仮想環境に割り当てています。仮想CPUの6スレッドとエミュレータスレッドとIOスレッドの8スレッドを物理CPUの8スレッドに固定しているのですが、i7-6700KとQEMUではコア番号とスレッド番号の対応が違っています。

f:id:KesaranPa:20171030231801j:plain

仮想CPUと物理CPUで構成が違うとパフォーマンスに影響がありそうなので、こんな設定になっています。ちなみにコアとスレッドの関係は、適当なLinuxを起動して、 cat /proc/cpuinfo してidを見ると分かります。

システム

  <os>
    <type arch='x86_64' machine='q35'>hvm</type>
    <loader readonly='yes' type='pflash'>/usr/share/ovmf/x64/OVMF_CODE.fd</loader>
    <nvram>/var/lib/libvirt/qemu/nvram/OVMF_CODE.fd</nvram>
  </os>
  <features>
    <acpi/>
    <apic eoi='on'/>
    <hyperv>
      <relaxed state='on'/>
      <vapic state='on'/>
      <spinlocks state='on' retries='4095'/>
      <vpindex state='on'/>
      <runtime state='on'/>
      <synic state='on'/>
      <stimer state='on'/>
      <reset state='on'/>
      <vendor_id state='on' value='0123456789ab'/>
    </hyperv>
    <kvm>
      <hidden state='on'/>
    </kvm>
    <vmport state='off'/>
  </features>
  <cpu mode='host-passthrough' check='none'>
    <topology sockets='1' cores='3' threads='2'/>
  </cpu>
  <clock offset='localtime'>
    <timer name='rtc' tickpolicy='catchup' track='guest'/>
    <timer name='pit' tickpolicy='delay'/>
    <timer name='hpet' present='no'/>
    <timer name='hypervclock' present='yes'/>
  </clock>
  <on_poweroff>destroy</on_poweroff>
  <on_reboot>restart</on_reboot>
  <on_crash>restart</on_crash>
  <on_lockfailure>poweroff</on_lockfailure>
  <pm>
    <suspend-to-mem enabled='no'/>
    <suspend-to-disk enabled='yes'/>
  </pm>
  <devices>
    <emulator>/usr/sbin/qemu-system-x86_64</emulator>

このあたりはよく分かっていないので、コピペしてきたものをつぎはぎしてそのまま使用しています(ぉぃ

ストレージ

    <disk type='block' device='disk'>
      <driver name='qemu' type='raw' cache='writeback' io='threads' discard='unmap'/>
      <source dev='/dev/mapper/vg0-win'/>
      <target dev='sda' bus='scsi'/>
      <boot order='1'/>
      <address type='drive' controller='0' bus='0' target='0' unit='0'/>
    </disk>
    <disk type='block' device='cdrom'>
      <driver name='qemu' type='raw' cache='writeback' io='threads' discard='unmap'/>
      <source dev='/var/lib/libvirt/images/virtio-win-0.1.141.iso'/>
      <target dev='sdb' bus='sata'/>
      <readonly/>
      <address type='drive' controller='0' bus='0' target='0' unit='1'/>
    </disk>

io='threads'io='native' の方がいいかも知れません。CDROMにはvirtioのドライバディスクが入っています。Windowsのインストール時にそのままではディスクが読み込めないと言われるので、このイメージ内のドライバをロードします。

PCIeルートポート

    <controller type='pci' index='0' model='pcie-root'/>
    <controller type='pci' index='1' model='pcie-root-port'>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x0' multifunction='on'/>
    </controller>
    <controller type='pci' index='2' model='pcie-root-port'>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x1'/>
    </controller>
    <controller type='pci' index='3' model='pcie-root-port'>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/>
    </controller>

QEMUのドキュメント によると、PCIeバスのルートには

  • PCIバイス
  • PCIeルートポート
  • PCIe to PCIブリッジ
  • ルートコンプレックス

しか置けないそうです。それ以外のPCIeデバイスを接続するために、PCIeルートポートをPCIeルートに接続しています。

仮想コントローラ

    <controller type='scsi' index='0' model='virtio-scsi'>
      <driver queues='6'/>
      <address type='pci' domain='0x0000' bus='0x01' slot='0x00' function='0x1'/>
    </controller>
    <controller type='sata' index='0'>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x1f' function='0x2'/>
    </controller>
    <controller type='usb' index='0' model='nec-xhci'>
      <address type='pci' domain='0x0000' bus='0x01' slot='0x00' function='0x3'/>
    </controller>
    <interface type='bridge'>
      <mac address='xx:xx:xx:xx:xx:xx'/>
      <source bridge='br0'/>
      <model type='virtio'/>
      <address type='pci' domain='0x0000' bus='0x01' slot='0x00' function='0x0' multifunction='on'/>
    </interface>
    <graphics type='vnc' port='59xx' autoport='no' listen='0.0.0.0' keymap='en-us'>
      <listen type='address' address='0.0.0.0'/>
    </graphics>
    <video>
      <model type='virtio' vram='16384' heads='1' primary='yes'>
        <acceleration accel3d='yes'/>
      </model>
      <address type='pci' domain='0x0000' bus='0x01' slot='0x00' function='0x2'/>
    </video>

色々な仮想コントローラを接続しています。マルチファンクションでできるだけ1つにまとめていますが、デバイスが多くないのでそこまでしなくていいかも知れません。GPUは、VNC経由でマウスとキーボードを使うために接続しています(Windows側で画面は無効にしています)。

パススルーデバイス

    <hostdev mode='subsystem' type='pci' managed='yes'>
      <source>
        <address domain='0x0000' bus='0x01' slot='0x00' function='0x0'/>
      </source>
      <address type='pci' domain='0x0000' bus='0x02' slot='0x00' function='0x0' multifunction='on'/>
    </hostdev>
    <hostdev mode='subsystem' type='pci' managed='yes'>
      <source>
        <address domain='0x0000' bus='0x01' slot='0x00' function='0x1'/>
      </source>
      <address type='pci' domain='0x0000' bus='0x02' slot='0x00' function='0x1'/>
    </hostdev>
    <hostdev mode='subsystem' type='pci' managed='yes'>
      <source>
        <address domain='0x0000' bus='0x03' slot='0x00' function='0x0'/>
      </source>
      <address type='pci' domain='0x0000' bus='0x03' slot='0x00' function='0x0'/>
    </hostdev>

GPUとUSBコントローラをパススルー接続しています。

メモリバルーン

    <memballoon model='none'/>
  </devices>
</domain>

PCIパススルーを使うときは、メモリを動的に増減させることはできないので、無効にしています。

Windowsインストール後の設定

GeForceはMessage Signaled Interrupt (MSI)に対応していますが、何故かデフォルトでは無効になっているので、Windowsインストール後に有効にする必要があります。レジストリエディタで HKEY_LOCAL_MACHINE\System\CurrentControlSet\Enum\PCI\<XXX>\Device Parameters\Interrupt Management\MessageSignaledInterruptProperties ( <XXX> は各デバイスで異なる) に MSISupported というキーを作って、値を 1 にします。


もしかしたら抜けや間違いがあるかも知れません。何かあったら教えてもらえるとありがたいです。

記載されている会社名・製品名・システム名などは、各社の商標、または登録商標です。