Hyper-V でDPDKプログラミング

注意：いろいろ試行錯誤しながら試した際のメモ書きです。私の試した設定が正しい、必須とも限らないですし、動かなかったのも私の環境、設定が原因の可能性があります。

# 概要とメモ

- Windows10でHyper-Vを動かし、その上のUbuntuでDPDKのNetVSC PMDを使ってのDPDKのプログラムを動かした
- Hyper-Vは便利そうな機能がならんでるけど、いろいろQEMU+KVMと違う
  - GUIのワンクリックでチェックポイントができる。便利そうだが試してない。Azureみたいにディスク書き込みを何重かしてるのかもしれないのでオフにした方がいいかもしれない。
  - 動的メモリ割り当ては謎の動きをするので切ったほうが良さそう（おかしくなったのは1GのHugepageをとったせいかも）
  - 自宅のPCで試す際、会社のサーバのノリで無邪気にVM2台同時起動するとHDDが刺さって死ぬ
  - PC再起動の度(?)に、VMのアドレスが変わってつらい
  - IPアドレスの使い方が謎。変なNetworkingされることがある。

# 試した環境

- Host
  - Windows 10 Pro 64bit
  - Core i7-6700K CPU @ 4.00GHz
  - Memory 16GB
- VM
  - Ubuntu Server 18.04.2 LTS (kernelは4.18に上げた)
  - vCPU: 4
  - Memory: 8192 MB
  - DPDK 18.11.1

# 仮想マシン準備

- Hyper-Vの準備はこちらを参考にした
    - https://docs.microsoft.com/ja-jp/virtualization/hyper-v-on-windows/quick-start/enable-hyper-v
- Hyper-V Quick Createは使わなかった。仮想マシンを作るたびに十数GBダウンロードするので普通にCDインストールした方が早い。
- VMの構成は下の図のような感じ
  - 動的メモリは無効にした
  - Ubuntu Serverをインストールしたisoは、[the traditional installer](http://cdimage.ubuntu.com/releases/18.04.2/release/?_ga=2.268757110.64052337.1555834290-481834480.1552631157)な方を使った(liveの方は1度試して失敗した)
  - とある事情でハイパーバイザは第一世代を選択
- メモリとCPUを減らした同じような構成のVMを対向として用意した

<img width="541" alt="2019-04-22 (2).png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/106917/dc512524-53b9-f935-236e-5f494d4ab464.png">


# 仮想マシン起動の注意

- 2台同時に起動してはいけない。1台目を起動して、タスクマネージャーでディスクの読み書きが落ち着いてから2台目を起動する（SSDな人は関係ないかも）。

# Hyper-V でできる仮想マシン

[Microsoftのページ](https://docs.microsoft.com/ja-jp/virtualization/hyper-v-on-windows/reference/hyper-v-architecture)によると、Hyper-Vのアーキテクチャはこんな感じらしい。

![hv_architecture.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/106917/fc184797-3f95-6ba3-7620-f2120b16e2af.png)

ネットワークやストレージなどのハードウェア リソースは図のVMBus（≠Virtio）を介して利用される。HostにVSP(Virtual Service Provider)があり、ここからVMBusを通して、各VMにあるVSC(Virtual Service Client)とやりとりがなされる。VirtioはPCIデバイスのようにふるまうが、VMBusを介したVSCはそんなことはしないので、`lspci`や`lstopo`で何も見えない。

```
root@ubuntu:~# lspci
00:00.0 Host bridge: Intel Corporation 440BX/ZX/DX - 82443BX/ZX/DX Host bridge (AGP disabled) (rev 03)
00:07.0 ISA bridge: Intel Corporation 82371AB/EB/MB PIIX4 ISA (rev 01)
00:07.1 IDE interface: Intel Corporation 82371AB/EB/MB PIIX4 IDE (rev 01)
00:07.3 Bridge: Intel Corporation 82371AB/EB/MB PIIX4 ACPI (rev 02)
00:08.0 VGA compatible controller: Microsoft Corporation Hyper-V virtual VGA
root@ubuntu:~# lstopo
Machine (7954MB)
  Package L#0 + L3 L#0 (8192KB)
    L2 L#0 (256KB) + L1d L#0 (32KB) + L1i L#0 (32KB) + Core L#0
      PU L#0 (P#0)
      PU L#1 (P#1)
    L2 L#1 (256KB) + L1d L#1 (32KB) + L1i L#1 (32KB) + Core L#1
      PU L#2 (P#2)
      PU L#3 (P#3)
  HostBridge L#0
    PCI 8086:7111
      Block(Removable Media Device) L#0 "sr0"
    PCI 1414:5353
root@ubuntu:~# ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000
    link/ether 00:15:5d:00:07:0a brd ff:ff:ff:ff:ff:ff
3: eth1: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether 00:15:5d:00:07:0c brd ff:ff:ff:ff:ff:ff
4: eth2: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether 00:15:5d:00:07:0d brd ff:ff:ff:ff:ff:ff
```

`lshw`をするとNetwork interfaceが見えるが、PCIアドレスはない。

```
root@ubuntu:~# lshw -c network
  *-network:0 DISABLED
       description: Ethernet interface
       physical id: 1
       logical name: eth1
       serial: 00:15:5d:00:07:0c
       size: 10Gbit/s
       capabilities: ethernet physical
       configuration: autonegotiation=off broadcast=yes driver=hv_netvsc duplex=full firmware=N/A link=no multicast=yes speed=10Gbit/s
  *-network:1 DISABLED
       description: Ethernet interface
       physical id: 2
       logical name: eth2
       serial: 00:15:5d:00:07:0d
       size: 10Gbit/s
       capabilities: ethernet physical
       configuration: autonegotiation=off broadcast=yes driver=hv_netvsc duplex=full firmware=N/A link=no multicast=yes speed=10Gbit/s
  *-network:2
       description: Ethernet interface
       physical id: 3
       logical name: eth0
       serial: 00:15:5d:00:07:0a
       size: 10Gbit/s
       capabilities: ethernet physical
       configuration: autonegotiation=off broadcast=yes driver=hv_netvsc duplex=full firmware=N/A ip=172.18.86.134 link=yes multicast=yes speed=10Gbit/s
```

余談だが、`lshw -c cpu`をみると合計64個のCPUが見えるので見てみると面白い（60個はDISABLEDになってる）


## Hyper-V上のUbuntuからDPDKを利用する

やっと本編。まず、カーネルのバージョンは上げておく

```
sudo apt install linux-generic-hwe-18.04
sudo apt update && sudo apt upgrade -y
sudo reboot
```

DPDKはconfig/common_baseの
`CONFIG_RTE_LIBRTE_NETVSC_PMD`と`CONFIG_RTE_LIBRTE_VDEV_NETVSC_PMD`を`y`にし、通常通りインストールする。

### interfaceのbind

前述のようにHyper-V上のVMは特殊なインターフェースを持っているので通常のDPDKの手順は使えない。
bind方法は[Netvsc-PMDのドキュメント](https://doc.dpdk.org/guides/nics/netvsc.html)に記述がある。

vmBusはPCIアドレスの代わりにUUIDを使用している。このUUIDを得る。

```
$ export DEV_UUID_ETH1=$(basename $(readlink /sys/class/net/eth1/device))
$ export DEV_UUID_ETH2=$(basename $(readlink /sys/class/net/eth2/device))
$ echo $DEV_UUID_ETH1 $DEV_UUID_ETH2
f38e3534-f2a7-4e4e-ab92-b5dfdafb9545 e88ee1b4-ffaa-462d-9617-d934d5c8d21d
```

※このあとのbindまでの操作はkernel 4.18以降で `driverctl` というコマンドで簡単にできるようになるらしい。Ubuntu 4.18.2ではkernelバージョンあげてもできなかったが。

Netvsc-PMDは、`uio_pci_generic`ではなく、`uio_hv_generic`を使う

```
$ sudo modprobe uio_hv_generic
```

なんだかよくわからないNET_UUIDなるものをuio_hv_genericにいれる。なんでもいいのかマジックナンバーなのかは分からない。

```
# NET_UUID="f8615163-df3e-46c5-913f-f2d2f965ed0e"
# echo $NET_UUID > /sys/bus/vmbus/drivers/uio_hv_generic/new_id
```

あとはデバイスのhv_netvscからのunbindとuio_hv_genericへのbind

```
# echo $DEV_UUID_ETH1 > /sys/bus/vmbus/drivers/hv_netvsc/unbind
# echo $DEV_UUID_ETH1 > /sys/bus/vmbus/drivers/uio_hv_generic/bind
# echo $DEV_UUID_ETH2 > /sys/bus/vmbus/drivers/hv_netvsc/unbind
# echo $DEV_UUID_ETH2 > /sys/bus/vmbus/drivers/uio_hv_generic/bind
```

ちゃんとバインドができていると、`/sys/bus/vmbus/drivers/uio_hv_generic/`以下にUUIDと一致するリンクができており、ip linkからeth1とeth2が消えている

```
$ ls /sys/bus/vmbus/drivers/uio_hv_generic/
bind  e88ee1b4-ffaa-462d-9617-d934d5c8d21d  f38e3534-f2a7-4e4e-ab92-b5dfdafb9545  module  new_id  remove_id  uevent  unbind
$ ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000
    link/ether 00:15:5d:00:07:0a brd ff:ff:ff:ff:ff:ff
```

ここまででちゃんと成功すると、`testpmd` でportを確認できる。なお、`dpdk-devbind`では確認できないので注意。
Hyper-Vにはどうやら賢いSwitchがVM間に入っているらしく、testpmdのパケットは落とされるため、パケットを送るとカウンタが回るくらいしか確認できない。

```
$ sudo ./testpmd -- -i
EAL: Detected 4 lcore(s)
EAL: Detected 1 NUMA nodes
EAL: Multi-process socket /var/run/dpdk/rte/mp_socket
EAL: Probing VFIO support...
EAL: WARNING: cpu flags constant_tsc=yes nonstop_tsc=no -> using unreliable clock cycles !
vmbus_probe_one_driver():   Invalid NUMA socket, default to 0
vmbus_probe_one_driver():   Invalid NUMA socket, default to 0
Interactive-mode selected
testpmd: create a new mbuf pool <mbuf_pool_socket_0>: n=171456, size=2176, socket=0
testpmd: preferred mempool ops selected: ring_mp_mc
Configuring Port 0 (socket 0)
Port 0: 00:15:5D:00:07:0D
Configuring Port 1 (socket 0)
Port 1: 00:15:5D:00:07:0C
Checking link statuses...
Done
testpmd> show port summary all
Number of available ports: 2
Port MAC Address       Name         Driver         Status   Link
0    00:15:5D:00:07:0D e88ee1b4-ffaa-462d-9617-d934d5c8d21d net_netvsc     down     0Mbps
1    00:15:5D:00:07:0C f38e3534-f2a7-4e4e-ab92-b5dfdafb9545 net_netvsc     down     0Mbps
```

他のDPDKアプリケーションで利用する際にはPCIアドレスの代わりにUUIDを指定すると動作する(と思われる)。少なくともLagopusは動作するらしい。
また終了時の処理が怪しいらしく、アプリケーションを再起動するにはPCも再起動する必要があるっぽい。