Packets are too Fast

slankdevの報告

DPDK の環境構築

DPDKはLinuxBSDカーネルをバイパスし、NICユーザランドから直接触ることにより 従来の通信ソフトウェアと比べはるかに高性能に通信を行うことを可能にしたライブラリである。

近年ではDPDKを用いたソフトウェアルーターで145GbEを達成した実装などがある。 itpro.nikkeibp.co.jp

DPDKは環境構築、テストが大変ということから一般ユーザは少なく感じるが、 しっかりとやるべきことをやれば、環境構築も簡単で、高性能な通信ソフトウェアの 開発を行うことができる。ここではその環境構築方法を説明していく。

DPDKの基本的な情報

こまったらhttp://dpdk.org/doc/を参照する。

公式のドキュメントを見た感想

僕はあまりにも英語ができないのですが、DPDKの公式ドキュメントはかなり読みやすかった 記憶がある。最終更新もかなり最近だったり、なかなか良い。 英語が苦手な僕の仲間はこれも勉強の一環として、本家ドキュメントでやるのがいいかも しれない。丁寧に書いてあるのでわかりやすい。

対応NICか調べる

DPDKに対応したNICかを調べる必要がある。(当たり前) そもそもこれ対応してないと導入する必要ないし、話にならない。。

$ dmesg | grep eth0
[    9.765177] e1000e 0000:00:19.0 eth0: registered PHC clock
[    9.765181] e1000e 0000:00:19.0 eth0: (PCI Express:2.5GT/s:Width x1) 68:f7:28:66:94:a5
[    9.765184] e1000e 0000:00:19.0 eth0: Intel(R) PRO/1000 Network Connection
[    9.765210] e1000e 0000:00:19.0 eth0: MAC: 11, PHY: 12, PBA No: 1000FF-0FF
[   13.950987] IPv6: ADDRCONF(NETDEV_UP): eth0: link is not ready
[   14.255860] IPv6: ADDRCONF(NETDEV_UP): eth0: link is not ready

こんな感じでe1000eだと分かる。よかった対応しているみたい。 それでは本家ドキュメントに沿って進めていきましょう。

環境構築

手順を以下にしめす。

  1. 必要なパッケージのインストール
  2. DPDKのソースコードの取得, コンパイル
  3. Linuxカーネル側の設定
  4. サンプルアプリケーションのビルド, 実行確認

また環境構築はUbuntu16.04LTS上で行うことを想定している。 最近(14.04以降)のUbuntuなら問題なく行えるだろう。

1. 必要なパッケージのインストール

DPDKで必要なシステムパッケージを以下に示す。 また必須ではないが、インストールしておくことを強く推奨するパッケージも以下に示す。

これらはほとんど通常のUbuntuデスクトップ環境であればすでにはいっているので、 以下のコマンド実行のみで完了できる。

$ sudo apt install -y libpcap-dev

2. DPDKのソースコードの取得, コンパイル

DPDK.orgからソースをダウンロードしてきて任意のディレクトリに展開します。

$ unzip DPDK-xxx.zip
$ cd DPDK-xxx
$ ls
app/ config/ examples/ lib/ LICENCE.GPL LICENCE.LGPL Makefile
mk/ scripts/ tools/

それぞれの概要を以下に示す。

DPDKのビルドターゲットのフォーマットを以下の様に指定します。

ARCH-MACHINE-EXECENV-TOOLCHAIN

通常はx86_64-native-linuxapp-gccをターゲットとする。 ちなみにivshmemとは仮想マシン間での共有メモリに関する技術らしい。。。知らない。

ビルドは以下のコマンドで行う。

$ make install T=x86_64-native-linuxapp-gcc

表記はmake install T=TARGET TARGETは先ほどのターゲットを指定してやればいい。 また、make config T=TARGETでビルドなしでbuild/.confign にコンフィグを反映してくれるので遊びでやってみるといいかもしれない。

ビルドされるとbuild以下に色々できる。同時にサンプルなどもすべてビルドしてくれる。 build以下にできるディレクトリの概要を示す。

  • app: テストアプリなどが置かれる
  • kmod: ロードが必要なカーネルモジュールが置かれる

ビルドが完了すると、DPDKのルートパス直下にターゲット名のディレクトリができているので、 一応確認するとよい。

$ ls x86_64-native-linuxapp-gcc
app build hostapp include kmod lib Makefile

3. Linuxカーネル側の設定

DPDKのビルドが完了したので、今度はDPDKを動作させるために必要な設定をLinuxカーネル側で 行う必要がある。手順を以下に示す。

  1. Hugepagesのマウント
  2. カーネルモジュールのロード
  3. ネットワークポートのバインド

3.1 Hugepagesの設定

Hugepagesとはユーザ空間の1page辺りのサイズを大きくする仕組みのこと。 DPDKはユーザランドドライバなのでこれが必要です。 Hugepagesは1MBと1GBの二種類があり、今回は1MBの方を選択する。 より多くのメモリが必要なアプリケーションを開発する場合は1GBの方を選択するとよいが、 今回はその方法は割愛する。

Hugepagesのメモリ領域はブート時に確保するべきである。もしそれができないなら可能な限り
早めにそのメモリを確保するとよい。そうでないと、連続したメモリ領域でなくいくつも断片化
したメモリ領域を使うことになってしまい、アプリケーションの速度低下に影響してしまう。

と公式ドキュメントに書いてあるので起動時にマウントするように設定していく。

設定するにはカーネルの起動オプションにhugepages=1024を追加する。 /etc/default/grubを編集してgrub-mkconfigをするのが一般的なので、それを行う。

$ sudo vim /etc/default/grub
GRUB_CMDLINE_LINUX=""  #ここを
GRUB_CMDLINE_LINUX="hugepages=1024"  #こうする
GRUB_CMDLINE_LINUX="hugepages=64"    #VMなどでメモリが少ない場合はこうする

$ sudo grub-mkconfig -o /boot/grub/grub.cfg   #これで設定反映する

Hugepagesの設定は完了したが、これを起動時にマウントするように設定しなくてはいけない。 /etc/fstabを編集して、先ほど設定したHugepagesを起動時にマウントするようにする。

$ mkdir -p /mnt/huge
$ sudo vim /etc/fstab
...末尾に以下を追加
nodev /mnt/huge hugetlbfs pagesize=1GB 0 0

この設定を行ったら一度再起動をするひつようがある。 Hugepagesは起動時にマウントするように設定したからである。 起動中にマウントする方法もあり、DPDKのドキュメントではそれを進めたりしている部分も あるのだが、絶対におすすめしない。

$ sudo reboot

再起動が完了したら次にすすむ

3.2 カーネルモジュールのロード

DPDKのビルド時などに新たに生成されるモジュールもあるが、一般的にuio_pci_generic というモジュールをロードしておけば、DPDKは動作する。

$ sudo modprobe uio_pci_generic

UIO: ユーザ空間ドライバを作成するための仕組み DPDKはユーザランドデバイスドライバなので、UIOのカーネルモジュールをロードします。

3.3 ネットワークポートのバインド/アンバインド

  • dpdk_nic: DPDKに認識されているNICのこと

最後にDPDKがNICを認識するための設定をする必要がある。 通常状態ではカーネルがハードウェアを管理していますが、ここではDPDKで 管理できるように設定をするので、この設定を行うと$ ip addrNICカーネルから みえなくなるが、再起動すればちゃんともとに戻るので、安心して欲しい。

バインドにはtoolsディレクトリ以下にあるdpdk-devbind.pyを使ってバインドする。 ほとんどのスクリプト--help, --usageが使える見たいなので気軽に見てみよう。

usage of dpdk_nic_bind.py
    --usage --help              show usage
    --status                    show status. display dpdk's seeable nics.
    --bind=DRIVER IDORNAME      bind to device with driver.

まずNICの状態を確認するには以下のコマンドを実行する。

$ sudo ./tools/dpdk_nic_bind.py --status

Network devices using DPDK-compatible driver
============================================
<none>

Network devices using kernel driver
===================================
0000:00:19.0 'Ethernet Connection (3) I218-LM' if=eth0 drv=e1000e unused=uio_pci_generic

Other network devices
=====================
<none>

ここではeth0と言うデバイスカーネル管理で存在するので、それをDPDK管理に変更します。 以下のようなコマンドでバインドできます。

$ sudo ./tools/dpdk-devbind.py --bind=uio_pci_generic eth0        # インターフェース名を指定する場合
$ sudo ./tools/dpdk-devbind.py --bind=uio_pci_generic 00:19.0     # PCIアドレスを指定する場合

また、DPDKの支配から解除してふだんの状態に戻すには以下ように実行する。

$ sudo ./tools/dpdk_nic_bind.py --bind=e1000e 00:19.0

先ほどのコマンドで、結果を確認する。

$ sudo ./tools/dpdk_nic_bind.py --status

Network devices using DPDK-compatible driver
============================================
0000:00:19.0 'Ethernet Connection (3) I218-LM' drv=uio_pci_generic unused=e1000e

Network devices using kernel driver
===================================
<none>

Other network devices
=====================
<none>

これでバインド完了。やったぜ。DPDKがNICを支配するのでifconfigとかでNICが見えなくなる。 ちなみにこの時ルート権限が必要。(まあ何となくそうだよね、ふつう)

面倒くさい作業はこれですべて必要なので、安心してほしい、お疲れ様。

4. サンプルアプリケーションのビルド, 実行確認

4.1 サンプルアプリのコンパイル

DPDKのビルドが完了するとそのターゲットディレクトリ (今回の場合x86_64-native-linuxapp-gcc)以下に DPDKのライブラリやヘッダファイルなどがすべて展開される。 これらのヘッダファイルやライブラリはDPDKを使用したアプリケーションの ビルドで必ず必要になる。アプリケーションのビルドにはいくつかの環境変数を 指定してやる必要がある。今回はDPDKのSDKがどのパスでアクセスできるか、と ターゲットのアーキテクチャ情報の2つである。これらは環境変数として宣言する。

今回は以下のように指定した。 また、永続的に指定する場合は.zshrcや.bashrcに書き込んでおく。

$ export RTE_SDK=/home/slank/Desktop/dpdk
$ export RTE_TARGET=x86_64-native-linuxapp-gcc

今回はhelloworldをビルドする。

$ cd $RTE_SDK/examples/helloworld
$ meke
    CC main.o
    LD helloworld
    INSTALL-APP helloworld
    INSTALL-MAP helloworld.map
$ ls /build/app/
helloworld* helloworld.map

これで文句なくビルド完了。後は実行だけです。もう少し。。

4.2 サンプルアプリの実行

先ほどビルド完了したhelloworldを実行する。 オプションなどについてはまだ詳しく調べていない

$ sudo ./build/app/helloworld
実行結果は省略

DPDKアプリケーションでは幾つかの共通オプションがあります。

  • -c COREMASK コアマスクを16進数で指定
  • -n NUM プロセッサごとのメモリチャンネル数
  • -b <DOMAIN:BUS:DEVID.FUNC> ポートをブラックリストとする
  • –use-device 使うデバイスのみを指定する
  • –socket-mem ソケットごとのメモリ指定する
  • -m MB プロセッサソケットにかかわらず、Hugepagesからメモリを確保する
  • -r NUM メモリランクを指定
  • -v バージョン表示
  • –huge-dir hugetlbfsのマウントされたパスを指定
  • –file-prefix よくわからぬ。。
  • –proc-type プロセッサインスタンスのタイプを指定
  • xen-dom0 hugetlbfsを使わずXen上で動かす
  • vmware-tsc-map VMware TSC mapを使う
  • –base-virtaddr 仮装アドレスを指定
  • –vfio-intr VFIOの割り込みタイプを指定

4.3 追加のサンプルアプリ

${RTE_SDK}/examples 以下にサンプルアプリケーションがおいてあるので、helloworldと同様の方法で かんたんに使えます。DPDK Sample AAApplications User Guideを参考にしましょう。

4.4 追加のテスト用アプリ

DPDK/app 以下には2つのテスト用アプリケーションがありそれぞれ以下のようなテストをします。(かんたんに説明)

  • test いろいろなことに対するテストを行います
  • testpmd 様々なパケットに対するスループットテストをします

うまく行かなかった場合

Linuxのシステム要件を満たしていない

DPDKアプリケーションを実行するために必要なシステム条件を以下に示す。 これを満たしていない場合、うまく行かない場合があるので、確認して欲しい

  • カーネルバージョン >= 2.6.34 $ uname -rで調べることができる
  • glibc >= 2.7 (これに関しては2.23でうまくいった) $ ldd --versionで調べることができるc
  • カーネルコンフィグで以下が有効になっていること /proc/config.gzに現在のカーネルコンフィグがあるのでそれを参照する。
    • UIO
    • HUGETLBFS
    • PROC_PAGE_MONITOR
    • HPET, HPET_MMAP