KVM PCIパススルー (PCI PassThrough)

kvmbanner-logo2

assign PCI devices with Intel VT-d in KVM

Linux KVM ハイパーバイザーは、ホストシステムのPCIデバイスを仮想ゲストへ接続(バインド)する技術があります。これをPCIパススルー(PCI PassThrough)といいます。

PCIパススルーは、ゲストオペレーティングシステムがホスト側PCIデバイスに直接物理的アクセスできるようになり、仮想でないデバイス・ドライバをそのまま利用できるので、細かなデバイス制御や命令実行が可能です。パフォーマンスに関しては、PCIパススルーを使用することで、ネイティブに近いパフォーマンスを得ることができると言われています。

実際にUSB2.0パススルーでゲスト側に接続したUSBメモリーでのファイル転送と比較した場合、今回のPCIパススルー接続経由でのUSBメモリーの方が高速でした。

このKVM PCIパススルーは要求ニーズが低いのかわかりませんが、ネット上の文献が少なく私の場合以下のページを参考にしました。構築内容はは相違していますが、根本的なストーリーは同じです。
How to assign devices with VT-d in KVM

VT-d および AMD I/O のサポート条件

さて このKVM PCIパススルーですが、ハードウェア アーキテクチャーに条件があります。ホスト側のM/Bに仮想化支援機構 IOMMU (Intel VT-d, AMD AMD I/O)機能があることと、マザーボードBIOSにてVT-dを有効にできる必要があります。

IOMMU機能拡張はチップセットの機能なでのすが、BIOSでサポートしていないタイプのM/Bも存在します。試しにBIOSにてVT-d設定がないM/Bでテスト調査したところ、Intel-IOMMU: enabled とLinuxカーネルメッセージにあったもののIOMMU拡張を使用することはできませんでした。

検証した環境

KVM仮想ホスト
Fedora18
カーネル
3.11.10-100.fc18.x86_64
CPU
Intel i7 860 2.80GHz
M/B
Intel 3400 Chipset
パススルーしたPCIデバイス
玄人志向 USB3.0N4-PCIe
PCI-Express x1
チップセット ルネサスエレクトロニクス社製μPD720200
仮想ゲストOS
Fedora17 Live-USB

準備 ホストOSでのIOMMU拡張を有効にする

まずCPUのタイプが仮想化支援 ( VT-x )をサポートしているか確認します。既にKVM仮想環境を構築運用されている方は大丈夫です。


$ cat /proc/cpuinfo | grep -E 'svm|vmx'
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc aperfmperf pni dtes64 monitor ds_cpl vmx smx est tm2 ssse3 cx16 xtpr pdcm sse4_1 sse4_2 popcnt lahf_lm ida dtherm tpr_shadow vnmi flexpriority ept vpid

BIOSにて IOMMU拡張を有効にする
M/B BIOSで IOMMU拡張を有効にする必要があります。これはチップセットの機能なので Intel VT-dやAMD I/OのIOMMU拡張はBIOSによって設定します。私の場合は I/O Virtualization Technology と表記されていました。

LinuxカーネルでIOMMU機能を有効化
カーネル起動パラメータ intel_iommu=on で有効にして起動する。 Fedora16以降からはブートローダーがgurb2ですので、カーネルオプションはgrub2の起動オプションを変更します。

grub2は /etc/grub2.cfgを編集の上 grub2-mkconfig を実行して適用する。


# vi /etc/grub2.cfg

echo 'Linux 3.11.10-100.fc18.x86_64 をロード中...'
linux /vmlinuz-3.11.10-100.fc18.x86_64 root=UUID=eedca3e3-4eb4-4f55-823a-645749aaeb18 ro rd.md=0 rd.lvm=0 rd.dm=0 quiet SYSFONT=latarcyrheb-sun16 rhgb rd.luks=0 KEYTABLE=jp106 LANG=en_US.UTF-8 intel_iommu=on
echo '初期 RAM ディスクをロード中...'
initrd /initramfs-3.11.10-100.fc18.x86_64.img

# grub2-mkconfig -o /boot/grub2/grub.cfg

grub2設定を有効にせず、grub2起動画面で一時的に編集して起動するのも良いでしょう。

再起動した後カーネルメッセージでIOMMU拡張が有効になっているか確認する。

dmesg | grep -e DMAR -e IOMMU


$ dmesg | grep -e DMAR -e IOMMU
[ 0.000000] ACPI: DMAR 00000000bf78e0c0 00090 (v01 AMI OEMDMAR 00000001 MSFT 00000097)
[ 0.000000] Intel-IOMMU: enabled
[ 0.042478] dmar: IOMMU 0: reg_base_addr fed90000 ver 1:0 cap c90780106f0462 ecap f020e2
[ 0.826135] DMAR: No ATSR found
[ 0.826171] IOMMU 0 0xfed90000: using Queued invalidation
[ 0.826174] IOMMU: Setting RMRR:
[ 0.826184] IOMMU: Setting identity map for device 0000:00:1d.0 [0xbf7ed000 - 0xbf7fffff]
[ 0.826214] IOMMU: Setting identity map for device 0000:00:1a.0 [0xbf7ed000 - 0xbf7fffff]
[ 0.826230] IOMMU: Setting identity map for device 0000:00:1d.0 [0xed000 - 0xeffff]
[ 0.826241] IOMMU: Setting identity map for device 0000:00:1a.0 [0xed000 - 0xeffff]
[ 0.826252] IOMMU: Prepare 0-16MiB unity mapping for LPC
[ 0.826261] IOMMU: Setting identity map for device 0000:00:1f.0 [0x0 - 0xffffff]

AMDでは 以下のコマンドで確認できるようです。

$ dmesg | grep AMD-Vi
...
AMD-Vi: Enabling IOMMU at 0000:00:00.2 cap 0x40
AMD-Vi: Lazy IO/TLB flushing enabled
AMD-Vi: Initialized for Passthrough Mode
...

PCIパススルーしたいアドレスを調べてゲストOSを起動

PCIデバイスのアドレスを確認

今回私がPCIパススルーしたいデバイスはPCIバスに差した玄人志向のUSB3.0カードでチップは NEC uPD720200 というものです。

$ lspci | grep USB
00:1a.0 USB controller: Intel Corporation 5 Series/3400 Series Chipset USB2 Enhanced Host Controller (rev 05)
00:1d.0 USB controller: Intel Corporation 5 Series/3400 Series Chipset USB2 Enhanced Host Controller (rev 05)
05:00.0 USB controller: NEC Corporation uPD720200 USB 3.0 Host Controller (rev 04)

$ lspci -n | grep 05:00.0
05:00.0 0c03: 1033:0194 (rev 04)

PCIデバイスをTree上に表記することもできます

$ lspci -tv
-+-[0000:ff]-+-00.0 Intel Corporation Core Processor QuickPath Architecture Generic Non-Core Registers
| +-00.1 Intel Corporation Core Processor QuickPath Architecture System Address Decoder
| +-02.0 Intel Corporation Core Processor QPI Link 0
| +-02.1 Intel Corporation Core Processor QPI Physical 0
| +-03.0 Intel Corporation Core Processor Integrated Memory Controller
| +-03.1 Intel Corporation Core Processor Integrated Memory Controller Target Address Decoder
| +-03.4 Intel Corporation Core Processor Integrated Memory Controller Test Registers
| +-04.0 Intel Corporation Core Processor Integrated Memory Controller Channel 0 Control Registers
| +-04.1 Intel Corporation Core Processor Integrated Memory Controller Channel 0 Address Registers
| +-04.2 Intel Corporation Core Processor Integrated Memory Controller Channel 0 Rank Registers
| +-04.3 Intel Corporation Core Processor Integrated Memory Controller Channel 0 Thermal Control Registers
| +-05.0 Intel Corporation Core Processor Integrated Memory Controller Channel 1 Control Registers
| +-05.1 Intel Corporation Core Processor Integrated Memory Controller Channel 1 Address Registers
| +-05.2 Intel Corporation Core Processor Integrated Memory Controller Channel 1 Rank Registers
| \-05.3 Intel Corporation Core Processor Integrated Memory Controller Channel 1 Thermal Control Registers
\-[0000:00]-+-00.0 Intel Corporation Core Processor DMI
+-03.0-[01]----00.0 NVIDIA Corporation GT200 [GeForce GTX 260]
+-08.0 Intel Corporation Core Processor System Management Registers
+-08.1 Intel Corporation Core Processor Semaphore and Scratchpad Registers
+-08.2 Intel Corporation Core Processor System Control and Status Registers
+-08.3 Intel Corporation Core Processor Miscellaneous Registers
+-10.0 Intel Corporation Core Processor QPI Link
+-10.1 Intel Corporation Core Processor QPI Routing and Protocol Registers
+-1a.0 Intel Corporation 5 Series/3400 Series Chipset USB2 Enhanced Host Controller
+-1b.0 Intel Corporation 5 Series/3400 Series Chipset High Definition Audio
+-1c.0-[02]--
+-1c.5-[03]----00.0 JMicron Technology Corp. JMB368 IDE controller
+-1c.6-[04]----00.0 Realtek Semiconductor Co., Ltd. RTL8111/8168 PCI Express Gigabit Ethernet controller
+-1c.7-[05]----00.0 NEC Corporation uPD720200 USB 3.0 Host Controller
+-1d.0 Intel Corporation 5 Series/3400 Series Chipset USB2 Enhanced Host Controller
+-1e.0-[06]--
+-1f.0 Intel Corporation 5 Series Chipset LPC Interface Controller
+-1f.2 Intel Corporation 5 Series/3400 Series Chipset 6 port SATA AHCI Controller
\-1f.3 Intel Corporation 5 Series/3400 Series Chipset SMBus Controller

この情報から該当PCIデバイスのアドレスは 05:00.0 で プロダクツ番号は 1033:0194 となります。

このPCIカードのUSBデバイス状況を確認します。


$ lsusb
Bus 001 Device 002: ID 8087:0020 Intel Corp. Integrated Rate Matching Hub
Bus 002 Device 002: ID 8087:0020 Intel Corp. Integrated Rate Matching Hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 002 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 004 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 002 Device 003: ID 05e3:0608 Genesys Logic, Inc. USB-2.0 4-Port HUB
Bus 002 Device 004: ID 045e:0746 Microsoft Corp.
Bus 002 Device 005: ID 05e3:0608 Genesys Logic, Inc. USB-2.0 4-Port HUB
Bus 002 Device 006: ID 13fd:160f Initio Corporation RocketFish SATA Bridge [INIC-1611]

$ lsusb -tv
/: Bus 04.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/2p, 5000M
/: Bus 03.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/2p, 480M
/: Bus 02.Port 1: Dev 1, Class=root_hub, Driver=ehci-pci/2p, 480M
|__ Port 1: Dev 2, If 0, Class=Hub, Driver=hub/8p, 480M
|__ Port 6: Dev 3, If 0, Class=Hub, Driver=hub/4p, 480M
|__ Port 1: Dev 4, If 0, Class=Human Interface Device, Driver=usbhid, 12M
|__ Port 1: Dev 4, If 1, Class=Human Interface Device, Driver=usbhid, 12M
|__ Port 1: Dev 4, If 2, Class=Human Interface Device, Driver=usbhid, 12M
|__ Port 3: Dev 5, If 0, Class=Hub, Driver=hub/4p, 480M
|__ Port 4: Dev 6, If 0, Class=Vendor Specific Class, Driver=, 480M
/: Bus 01.Port 1: Dev 1, Class=root_hub, Driver=ehci-pci/2p, 480M
|__ Port 1: Dev 2, If 0, Class=Hub, Driver=hub/6p, 480M

$ lspci -s 0000:05:00.0 -vv
05:00.0 USB controller: NEC Corporation uPD720200 USB 3.0 Host Controller (rev 04) (prog-if 30 [XHCI])
Control: I/O- Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR- FastB2B- DisINTx+
Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=fast >TAbort- SERR- Latency: 0, Cache Line Size: 32 bytes
Interrupt: pin A routed to IRQ 19
Region 0: Memory at fbffe000 (64-bit, non-prefetchable) [size=8K]
Capabilities:
Kernel driver in use: xhci_hcd

これらの表記からホストOSがこのデバイスを使っていることがわかります。このデバイスをホスト側で未使用にする処理を pci-stub モジュールがやってくれます。以前のカーネルバージョンでは pci-stub モジュール を個別にロードする必要がありましたが、現在のLinuxカーネルではpci-stub はカーネルに組み込まれているようです。

KVMコマンドによるゲストOSの起動

ゲストOSの起動はsudo root権限で実行しています。


$ sudo /var/tmp/qemu-kvm -cpu core2duo -m 1024 -smp 1 -drive file=USB-F17-GNOME3.img,if=virtio,cache=writeback,media=disk,index=0 -monitor stdio -vga vmware -localtime -net nic,macaddr=00:30:67:40:34:b1,model=e1000 -net tap,ifname=tap1 -k ja -vnc :1 -boot c -usbdevice tablet -usb -device pci-assign,host=05:00.0
Bringing up tap1 for bridged mode...
Adding tap1 to br0...
Failed to assign device "(null)" : Device or resource busy
*** The driver 'xhci_hcd' is occupying your device 0000:05:00.0.
***
*** You can try the following commands to free it:
***
*** $ echo "1033 0194" > /sys/bus/pci/drivers/pci-stub/new_id
*** $ echo "0000:05:00.0" > /sys/bus/pci/drivers/xhci_hcd/unbind
*** $ echo "0000:05:00.0" > /sys/bus/pci/drivers/pci-stub/bind
*** $ echo "1033 0194" > /sys/bus/pci/drivers/pci-stub/remove_id
***
qemu-kvm: -device pci-assign,host=05:00.0: Device 'pci-assign' could not be initialized

デバイスリソースが使用中のためエラー終了しています。このPCIデバイス(0000:05:00.0)はxhci_hcdで使用しているとの事。echoコマンドで表記されているコマンドをrootで実行します。(sudoはNGです)


$ sudo su -
# echo "1033 0194" > /sys/bus/pci/drivers/pci-stub/new_id
# echo "0000:05:00.0" > /sys/bus/pci/drivers/xhci_hcd/unbind
# echo "0000:05:00.0" > /sys/bus/pci/drivers/pci-stub/bind
# echo "1033 0194" > /sys/bus/pci/drivers/pci-stub/remove_id
# exit

ログアウト

ホスト側でこのデバイスリソースがはずされたので、ホスト側のコマンドでlsusbを確認すると表記していないのでバインドが外れたことが分かります。


$ lsusb -t
/: Bus 02.Port 1: Dev 1, Class=root_hub, Driver=ehci-pci/2p, 480M
|__ Port 1: Dev 2, If 0, Class=Hub, Driver=hub/8p, 480M
|__ Port 6: Dev 3, If 0, Class=Hub, Driver=hub/4p, 480M
|__ Port 1: Dev 4, If 0, Class=Human Interface Device, Driver=usbhid, 12M
|__ Port 1: Dev 4, If 1, Class=Human Interface Device, Driver=usbhid, 12M
|__ Port 1: Dev 4, If 2, Class=Human Interface Device, Driver=usbhid, 12M
|__ Port 3: Dev 5, If 0, Class=Hub, Driver=hub/4p, 480M
|__ Port 4: Dev 6, If 0, Class=Vendor Specific Class, Driver=, 480M
/: Bus 01.Port 1: Dev 1, Class=root_hub, Driver=ehci-pci/2p, 480M
|__ Port 1: Dev 2, If 0, Class=Hub, Driver=hub/6p, 480M

再びゲストOSの仮想起動


$ sudo /var/tmp/qemu-kvm -cpu core2duo -m 1024 -smp 1 -drive file=USB-F17-GNOME3.img,if=virtio,cache=writeback,media=disk,index=0 -monitor stdio -vga vmware -localtime -net nic,macaddr=00:30:67:40:34:b1,model=e1000 -net tap,ifname=tap1 -k ja -vnc :1 -boot c -usbdevice tablet -usb -device pci-assign,host=05:00.0
Bringing up tap1 for bridged mode...
Adding tap1 to br0...
QEMU 1.2.2 monitor - type 'help' for more information

ゲストOSにて動作を確認する

ゲストOSは Fedora17のLiveUSBです。カーネルは 3.3.4-5.fc17.x86_64 で起動しています。
起動後 PCIパススルーされているか検証します。


$ lspci
00:00.0 Host bridge: Intel Corporation 440FX - 82441FX PMC [Natoma] (rev 02)
00:01.0 ISA bridge: Intel Corporation 82371SB PIIX3 ISA [Natoma/Triton II]
00:01.1 IDE interface: Intel Corporation 82371SB PIIX3 IDE [Natoma/Triton II]
00:01.2 USB Controller: Intel Corporation 82371SB PIIX3 USB [Natoma/Triton II] (rev 01)
00:01.3 Bridge: Intel Corporation 82371AB/EB/MB PIIX4 ACPI (rev 03)
00:02.0 VGA compatible controller: VMware SVGA II Adapter
00:03.0 Ethernet controller: Intel Corporation 82540EM Gigabit Ethernet Controller (rev 03)
00:04.0 USB Controller: NEC Corporation uPD720200 USB 3.0 Host Controller (rev 04)
00:05.0 SCSI storage controller: Red Hat, Inc Virtio block device
$ lspci -s 00:04.0 -n
00:04.0 0c03: 1033:0194 (rev 04)
lsusb -t
/: Bus 03.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/2p, 5000M
/: Bus 02.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/2p, 480M
/: Bus 01.Port 1: Dev 1, Class=root_hub, Driver=uhci_hcd/2p, 12M
|__ Port 1: Dev 2, If 0, Class=HID, Driver=usbhid, 12M

ゲストOS側で デバイスアドレス 00:04.0 で認識しています。またUSB3.0 xhci_hcd として動作しているのがわかります。

ここでUSBメモリーをこのデバイスに挿入してみると、以下のようにちゃんと認識しています。


$ lsusb -t
/: Bus 03.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/2p, 5000M
/: Bus 02.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/2p, 480M
|__ Port 1: Dev 2, If 0, Class=stor., Driver=usb-storage, 480M
/: Bus 01.Port 1: Dev 1, Class=root_hub, Driver=uhci_hcd/2p, 12M
|__ Port 1: Dev 2, If 0, Class=HID, Driver=usbhid, 12M

最後に

このUSBメモリーをコピー転送のテストをしてみると問題なく動作します。USB3.0デバイスなので、USBパススルーよりもコピー速度は速いのが体感できます。実際に大きなファイルを転送コピーしてテストするといいのですが、今回はやっていません。

私がPCIパススルーに挑戦・テストしたかった理由は、WiondowsOS iTunesへiPhoneやiPadを認識させたかったからです。今回の検証作業はゲストOSにLinuxを実行させていますが、次回WindowsOSでもテスト検証したいと思います。それとゲストOS WindowsでiTunesデバイスを認識させたい場合 VirtualBox仮想環境で実現できるようです。次回、KVM PCIパススルー ゲストOS をWindowsでトライしたいと思います。

関連したページ

nexia access map
x