Contents
1. 概要
KVM 上の Windows 10 に PCI デバイス(GPU/USB コントローラ) を直接割り当て、仮想 GPU の描画性能不足を補い、標準の USB リダイレクトでは不安定・非対応になりがちな機器(例:iPhone の認識など)を ゲストから“素の PCI デバイス”として扱える ようにします。
ここでは Ubuntu 22.04(Intel VT-d 前提)での手順と注意点をまとめます。
2. モチベーション
「素直に物理 Windows を入れれば仮想化のオーバーヘッドはゼロ」。これはその通りです。
それでも本構成に踏み切る動機は、次のとおりです。
- Windows を KVM の仮想マシンとして動かしたいが、可能な限り、性能をベアメタルに近づけたい。
- SR-IOV/CPU ピンニング/HugePages など、近年の性能チューニングを自宅環境で再現・検証したい。
- 画面描画や USB オーディオのような 人間の感覚に直結する処理 は、仮想 GPU や USB リダイレクトだと限界があるため、直結(PCI パススルー)で底上げ したい。
3. 基本情報
3.1. ハードウェア
以下の構成で構築しました。
| パーツ | メーカー | 型番 | 備考 |
|---|---|---|---|
| PC ケース | Cooler Master | MCS-S600-KN5N-S00 | ミドルタワーケース |
| 電源ユニット | Corsair | CP-9020195-JP | 80PLUS GOLD |
| マザーボード | ASUS | PRIME B365-PLUS | ATX |
| CPU | INTEL | i5-9600 | 6cores, 6threads |
| CPU クーラー | サイズ | 虎徹 Mark II | |
| メモリ | Crucial | W4U2666CM-8G | 8GB x 4 |
| M.2 SSD | シリコンパワー | SP512GBP34A60M28 | 512GB |
| SSD | Crucial | CT1000MX500SSD1 | 1TB |
| グラフィックカード | ASUS | DUAL-GTX1650-O4G | PCI パススルー対象 |
| USB3.0 カード | オウルテック | OWL-PCEXU3E4 | PCI パススルー対象 |
| ネットワークカード | TP-Link | TG-3468 |
以降の手順では グラフィックカード(DUAL-GTX1650-O4G) と USB3.0 カード(OWL-PCEXU3E4) を仮想マシンにパススルーします。
3.2. ハードウェアの周辺環境
ディスプレイは 1 台を使用し、オンボードの HDMI と GPU の HDMI をそれぞれ接続しています。
切替スイッチでホスト(Ubuntu)とゲスト(Windows)を手動で切り替え、キーボード・マウスの USB レシーバーも物理的に差し替えます。
この構成により、ホストとゲストを完全に独立した操作系として扱えます。
3.3. BIOS
BIOSで Intel VT と Intel VT-d を有効にします。これが無効の場合、PCI パススルーは動作しません。
3.4. ソフトウェア
3.4.1. OS
Ubuntu Desktop 22.04.1 LTS を使用しました。Server 版でも手順は概ね同一です。
3.4.2. KVM
KVM の基本パッケージをインストールします。他の補助ツール(例:bridge-utils, cpu-checker 等)は必要に応じて追加してください。
sudo apt -y install qemu-kvm libvirt-daemon-system libosinfo-bin virtinst virt-manager3.5. ネットワーク設定
Ubuntu Desktop のデフォルトの netplan は下記となっており、network-manger が netplan の制御を行っています。
cat /etc/netplan/01-network-manager-all.yamlnetwork:
version: 2
renderer: NetworkManager本稿では Ubuntu Server に合わせて network-manger の制御を無効にします。上記のファイルを削除して再起動すると全ての NIC が network-manager の制御から外すことができます。renderer: networkd としても良いようですが、この場合、ブリッジを再起動すると仮想マシンの通信が切れる問題があった為、renderer の設定は削除しています。これは Ubuntu Server と同じ状態です。
sudo rm /etc/netplan/01-network-manager-all.yamlsudo reboot再起動後、nmcli で unmanaged を確認できます。
LANG=C nmcli deviceDEVICE TYPE STATE CONNECTION
enp6s0 ethernet unmanaged --
enp7s0 ethernet unmanaged --
lo loopback unmanaged --今回のネットワーク設定は、下記にように VLAN をブリッジする構成としています。
sudo tee /etc/netplan/00-main.yaml <<"EOF"
network:
version: 2
ethernets:
enp7s0: {}
vlans:
vlan3000:
id: 3000
link: enp7s0
bridges:
br3000:
addresses:
- 10.0.0.37/24
nameservers:
addresses:
- 172.16.64.71
interfaces:
- vlan3000
routes:
- to: default
via: 10.0.0.1
EOFsudo netplan apply4. IOMMU の有効化
KVM で PCI パススルーを行うには、まず IOMMU(Input–Output Memory Management Unit) を有効にする必要があります。
これは CPU によるデバイスアクセスのメモリ空間分離を提供し、ゲスト OS が特定の PCI デバイスを直接制御できるようにする仕組みです。
4.1. GRUB 設定の編集
Intel 環境の場合、以下のように /etc/default/grub を編集します。
sudo cp /etc/default/grub /etc/default/grub.origsudo tee /etc/default/grub <<"EOF"
GRUB_DEFAULT=0
GRUB_TIMEOUT_STYLE=hidden
GRUB_TIMEOUT=0
GRUB_DISTRIBUTOR=`lsb_release -i -s 2> /dev/null || echo Debian`
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash"
GRUB_CMDLINE_LINUX="intel_iommu=on iommu=pt"
EOFintel_iommu=on:Intel IOMMU 機能(VT-d)を有効化iommu=pt:ホスト上でのIOMMU処理を最小化(パススルー性能を向上)
AMD 環境の場合、intel_iommu=on の代わりに amd_iommu=on を指定します。
4.2. GRUB の更新と再起動
sudo update-grub
sudo reboot4.3. 有効化の確認
再起動後、以下のコマンドで IOMMU が認識されているかを確認します。
sudo dmesg | grep -i iommu[ 0.061643] DMAR: IOMMU enabled
[ 0.157075] DMAR-IR: IOAPIC id 2 under DRHD base 0xfed91000 IOMMU 1IOMMU enabled と表示されていれば有効化されています。
4.4. トラブルシューティング
- BIOS で Intel VT-d / AMD-Vi が無効になっていないか確認
- 一部の古いチップセットでは VT-d がサポートされない場合があります
dmesgにIOMMU disabledやDMAR: No ATSR foundなどが出る場合、BIOS 設定またはカーネル引数の誤りが疑われます。
5. PCI デバイスの分離
IOMMU が有効になったら、仮想マシンに割り当てたい PCI デバイスを VFIO(Virtual Function I/O) でホスト OS から切り離します。
これにより、Linux カーネルがそのデバイス用の標準ドライバをロードせず、仮想マシン専用として扱えるようになります。
5.1. デバイス ID の確認
まず、対象デバイスの ベンダーID と デバイスID を確認します。
lspci -nn01:00.0 VGA compatible controller [0300]: NVIDIA Corporation TU117 [GeForce GTX 1650] [10de:1f82]
01:00.1 Audio device [0403]: NVIDIA Corporation Device [10de:10fa]
04:00.0 USB controller [0c03]: Renesas Technology Corp. uPD720201 USB 3.0 Host Controller [1912:0014]ここでは以下の 3 デバイスをパススルー対象とします。
| デバイス | ベンダーID:デバイスID | 備考 |
|---|---|---|
| NVIDIA GPU | 10de:1f82 | グラフィック出力用 |
| NVIDIA Audio | 10de:10fa | GPU 搭載オーディオ機能 |
| Renesas USB3.0 | 1912:0014 | USB コントローラー |
5.2. GRUB で VFIO を指定
VFIO はカーネル起動時に対象デバイスをバインドします。
/etc/default/grub に vfio-pci.ids= を追記します。
sudo cp /etc/default/grub /etc/default/grub.baksudo tee /etc/default/grub <<"EOF"
GRUB_DEFAULT=0
GRUB_TIMEOUT_STYLE=hidden
GRUB_TIMEOUT=0
GRUB_DISTRIBUTOR=`lsb_release -i -s 2> /dev/null || echo Debian`
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash"
GRUB_CMDLINE_LINUX="intel_iommu=on iommu=pt vfio-pci.ids=10de:1f82,10de:10fa,1912:0014"
EOFsudo update-grub
sudo rebootUbuntu 22.04 の標準カーネル(5.15 以降)では vfio-pci はモジュールではなくカーネルに組み込まれています。そのため、modprobe vfio-pci は不要です。
5.3. バインドの確認
再起動後、lspci コマンドで各デバイスが vfio-pci にバインドされていることを確認します。
sudo lspci -nnv -s 01:00.0
sudo lspci -nnv -s 01:00.1
sudo lspci -nnv -s 04:00.0出力の中に次の行があれば成功です。
Kernel driver in use: vfio-pci5.4. 注意点と補足
- 同一 ID デバイスの巻き込みに注意
同一ベンダー ID/デバイス ID を持つデバイスが複数存在する場合、意図しないデバイスも分離される可能性があります。 - USB カードを VFIO に含める理由
他の情報では USB カードを KVM 設定で直接割り当てる例もありますが、その方法ではホスト・ゲストの切り替え時にマウス・キーボード入力が一時的に失われる問題がありました。VFIO に含めてパススルーすることで、この問題を回避できます。
5.5. トラブルシューティング
Kernel driver in useがnvidiaやxhci_pciのまま
GRUB の編集が反映されていない可能性があります。update-grubの実行漏れや/bootの空き容量不足を確認。vfio-pciがロードされない
BIOS の VT-d / IOMMU 設定を再確認、またはiommu=ptの指定を削除して再試行。
6. Windows 10 をゲスト OS としてインストール
6.1. 準備
以下の ISO イメージを取得し、/var/lib/libvirt/images に配置しておきます。
| 種類 | 入手先 | 備考 |
|---|---|---|
| Windows 10 | Microsoft 公式サイト | ツール経由でダウンロード(Windows 環境が必要) |
| virtio-win drivers | virtio-win GitHub または Fedora People Direct Download | stable 版を推奨 |
6.2. 仮想マシンの作成(virt-manager 使用)
virt-install でもインストール可能だと思いますが、PCI パススルーなどを調べるのが面倒でしたので、virt-manager(GUI)を使用しました。
6.2.1. 新規作成
- ローカルのインストールメディア を選択
- Windows 10 の ISO を指定
- OS が自動認識されない場合は手動で「Windows 10」を選択
- CPU・メモリは後で調整するためデフォルトのまま
- 「インストール前に設定をカスタマイズする」 にチェックを入れる
6.2.2. インストール前のカスタマイズ
概要:
- チップセット:Q35
- ファームウェア:UEFI(Windows 11 にも対応)
CPU:
- 「ホスト CPU の設定をコピー」を有効化
- 「CPU トポロジーを手動設定」→ ソケット数 1、コア数 6、スレッド数 1
(実機 CPU と同じ構成にするのが安定)
メモリ:
- 12GB(12288MB)を割り当て
ネットワーク:
netplanで作成したブリッジbr3000を指定
TPM:
- 種類:Emulated
- モデル:CRB
- バージョン:2.0
→ Windows 11 の要件に対応
virtio ドライバ:
- デバイスの追加 → 「CD-ROM デバイス」
- virtio-win の ISO イメージを指定(ストレージドライバ用)
PCI ホストデバイス:
- 「PCI ホストデバイスを追加」から
以下の 3 デバイスを選択:- 01:00.0(GPU)
- 01:00.1(GPU Audio)
- 04:00.0(USB コントローラ)
6.3. OS インストール
- 仮想マシンを起動し、通常の Windows インストール手順を進めます。
- ストレージが認識されない場合は、
virtio-win.iso内のviostor\w10\amd64を指定してドライバを読み込みます。 - Windows のセットアップを完了させます。この時点では、まだ仮想ディスプレイ(QXL)経由で操作します。
6.4. PCI パススルーした GPU のみを使用する
Windows の初期設定が完了したら、仮想ディスプレイを削除して物理 GPU のみで起動 させます。
- 仮想マシンを停止
- 以下のデバイスを削除
- SATA CD-ROM1(Windows ISO)
- SATA CD-ROM2(virtio ISO)
- ディスプレイ:Spice
- ビデオ:QXL
- チャンネル:spice
- モニター入力を GPU 側 HDMI に切り替え
- USBレシーバーをパススルーした側へ接続
起動後、GPUドライバを自動認識しない場合は NVIDIA の公式ドライバを手動インストールします。デバイスマネージャで GPU と USB コントローラが正しく認識されれば設定完了です。
6.5. 追加のチューニング
仮想マシンはこの時点で実用的に動作しますが、負荷分散や遅延対策のためにいくつかの調整を行うとより安定します。
6.5.1. CPU ピニング(CPU 割り当ての固定)
CPU コアを仮想マシン専用に固定することで、ホスト側のスレッドスケジューリングによるジッタ(処理の揺らぎ)を減らします。
virsh edit <VM 名> で設定を編集します。
<vcpu placement='static'>4</vcpu>
<cputune>
<vcpupin vcpu='0' cpuset='2'/>
<vcpupin vcpu='1' cpuset='3'/>
<vcpupin vcpu='2' cpuset='4'/>
<vcpupin vcpu='3' cpuset='5'/>
</cputune>
<cpu mode='host-passthrough' check='none' migratable='on'>
<topology sockets='1' dies='1' cores='4' threads='1'/>
</cpu><vcpu>:仮想 CPU 数(ここでは 4)<vcpupin>:各 vCPU をホストのどのコアに割り当てるかを指定host-passthrough:CPU 機能をほぼそのままゲストに渡す
6.5.2. ホストの GUI を無効化(リソース節約)
ホスト側で GUI 操作が不要である場合、テキストモードで起動することで、リソースが節約され、安定性が向上します。
systemctl get-default
sudo systemctl set-default multi-user.target
sudo reboot必要時のみ GUI を起動する場合:
sudo systemctl start gdm36.5.3. HugePages
HugePages は仮想マシンのメモリアクセスを高速化する仕組みです。ただし、Windows 側での体感差は小さいため、効果を検証する場合のみ設定します。
ホスト側で HugePages を確保する例:
grep Huge /proc/meminfo
sudo sysctl -w vm.nr_hugepages=1024libvirt ドメイン XML に以下を追加:
<memoryBacking>
<hugepages/>
</memoryBacking>6.5.4. 音声処理(USB オーディオ)の安定化
オーディオインターフェイスなどリアルタイム性が高いデバイスは、他の VM やホストプロセスの割り込み競合で途切れることがあります。
改善策として:
GRUB_CMDLINE_LINUXにthreadirqsを追加し、IRQ をスレッド化- USB カードを別 IOMMU グループに配置(PCI スロットを変更)
isolcpus/rcu_nocbsでVM 専用コアを隔離
これでも断続的なノイズが出る場合は、物理環境での利用を検討した方が現実的です。
6.6. まとめ
これらの調整によって、KVM 環境でも体感的に滑らかな Windows 操作が可能になります。ただし、「仮想化で性能を追う=管理コストが増える」というトレードオフは常に意識しておくと良いでしょう。
7. 感想
ここまでの設定で、Windows は違和感なく動作しました。GPU 描画や USB 機器のレスポンスも十分で、通常利用であれば物理環境との差はほとんど感じません。
ただし、私の環境では他の仮想マシンの負荷が高まると、Windows 上で使用している USB オーディオインターフェイスの音が途切れることがあり、この問題だけは満足いく解消できませんでした。
このため、最終的にはネイティブ Windows に戻しています。
とはいえ、この構成を通じて 仮想化における性能チューニングの考え方 を多く学ぶことができました。
CPU ピンニングや IOMMU、SR-IOV といった仕組みがどのように重なり合い、ハードウェア資源をどの層まで仮想化で抽象化できるのか。その限界点がよく見えました。
7.1. 実用面の整理
仮想化は利便性に優れますが、グラフィックやオーディオのようなリアルタイム性の高い処理では遅延やノイズが発生しやすく、完全にベアメタルと同等にするのは現実的ではありません。
ネットワークやストレージは十分な性能を得やすい一方で、人間の感覚に直結する入出力デバイスは特にシビアです。
7.2. 総括
結局のところ、仮想環境でネイティブに近い性能を得るということは、「仮想化の抽象層を一枚ずつ剥いでハードウェアに直接触れる」ことでもあります。
その分、手軽さや柔軟性は失われ、管理コストが増していきます。性能を突き詰めるほど、仮想化の恩恵からは遠ざかる。それがこの検証で得た一番の教訓でした。
とはいえ、SR-IOV や DPDK のような技術が実用化されているように、仮想化とハードウェア直結の境界は今後さらに整理・洗練されていくでしょう。
個人レベルでも、それを追うこと自体がすでに楽しい実験です。


