ちょっとした開発でDPDKを使うことになり、 結構導入が辛かったので備忘録のようにかんたんにまとめました。 まだ全てわかっているわけではないので、 今後も勉強しながら更新していこうと思います。もしご指摘などがあれば、コメントをしていただけると、助かります。DPDK関連の日本語の情報が少ないので、他の人のお役に立てれば幸いです。 環境構築は普通のx86_64マシン上(Thinkpad X250 に ArchLinux) でやりました。 公式ドキュメントを参考にしながら行ったため、章分けなどは公式ドキュメントと合わせてあります。 わからなかった時に公式ドキュメント(英語)を参照する時にご活用ください。今回は1のIntroductionから4のCompiling and Running Sample Applicationsまでをやります。具体的にはDPDKの導入を行ってサンプルアプリケーションの実行までです。

0. 初めに

DPDK とは

DPDK(Data Plane Development Kit)とはLinuxやBSD上でパケット処理を高速化するための仕組みです。通常のパケット処理を行うアプリケーションを実装する場合、その処理はカーネルに任せることになりますが、これでは遅くなりすぎてしまう。なので、カーネルを通さないでユーザランドから直接ハードウェアを叩いて高速に通信を行うぜ!といったすごい奴です。

最初の方はIntelDPDKという名前だったのですが、途中でIntelから独立してDPDKという名前になったっぽいです。

最近では商用のL3スイッチやOpenflowスイッチなどの実装に使われています。

DPDKの基本的な情報

  • DPDK (http://dpdk.org)
    • supported nics (http://dpdk.org/doc/nics)
    • quick start (http://dpdk.org/doc/quick-start)
    • download (http://dpdk.org/download)
    • pdf guides (http://dpdk.org/doc/pdf-guides/)

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

僕はあまりにも英語ができないのですが、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. Introduction

1.1 ドキュメントロードマップ

ここは得に何もやることなし。次からがっつりやりまっせ。

2. システム環境構築

2.1 BIOSの設定

この辺は特に何もやらなくても大丈夫でした。環境によっては必要かもしれません。

2.2 DPDKのコンパイルのための環境構築

ドキュメント作成環境ではFedora18を使用しているため、違うシステムを使っている場合は若干の誤差がある可能性があります。詳しくはReleaseNotesを参照に。

必要なパッケージとバージョンを以下に示します。

  • GNU make
  • coreutils: cmp, sed, gre, arch, etc..
  • gcc: version4.5.x 以降
  • libc headers
  • Linux Kernel Headers またはカーネルソースコード
  • glibc.i686 libgcc.i686 libstdc++.i686 and glibc-devel.i686 for intel i686/x86_64

2.3 DPDKアプリケーションのための環境構築

2.3.1 システム条件

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

2.3.2 Hugepagesの使用

Hugepagesとはユーザ空間の1page辺りのサイズを大きくする仕組みのこと。DPDKはユーザランドドライバなのでこれが必要です。Hugepagesは1MBと1GBの二種類があり、今回は1GBのHugepagesを使用する。どちらかをしっかりと設定しておけば、DPDKは適切な方を選んで使ってくれるので大丈夫。カーネルパラメータをいじってHugepagesの設定を行う必要があります。

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

と公式ドキュメントに書いてありました。

Hugepagesで設定を行います。 /etc/default/grubを編集します。

$ sudo vim /etc/default/grub

GRUB_CMDLINE_LINUX=""                #ここを
GRUB_CMDLINE_LINUX="hugepages=1024"  #こうする

編集後は変更を適用します。

$ sudo grub-mkconfig -o /boot/grub/grub.cfg

Hugepagesは有効化できたので、DPDKから使えるようにしていきます。 Hugepagesの確保されたアドレスがどこかをDPDKに知らせてあげる必要があるみたいです。

$ mkdir -p /mnt/huge
$ mount -t hugetlbfs nodev /mnt/huge

永続的にマウントしたいので、/etc/fstabに以下を追記

nodev /mnt/huge hugetlbfs defaults 0 0

これで全体の準備が整いました。次はDPDKのビルドです。このHugepages関連の準備が一番面倒くさかったので、後は今までより楽に進むと思います!

2.3.3 Xenドメインのサポート

これは今回まったく関係ないので割愛で。

3. DPDKのコンパイル

3.1 DPDKのソース展開

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

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

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

  • lib: DPDK本体のソースコード
  • drivers: pollモード用のドライバ
  • app: DPDKのアプリのソース(自動テスト)
  • examples: DPDKのアプリの例 (サンプルコード的な)
  • config: 関連するフレームワークや設定など
  • tools: 関連するフレームワークや設定など
  • scripts: 関連するフレームワークや設定など
  • mk: 関連するフレームワークや設定など

3.2 DPDKのビルド

DPDKのビルドターゲットのフォーマットは以下のようになっています。

ARCH-MACHINE-EXECENV-TOOLCHAIN
  • ARCH: i686, x86_64, ppc_64
  • MACHINE: native(実機), ivshmem(仮想マシン用), power8(IBMのアーキテクチャ?)
  • EXECENV: linuxapp, bsdapp
  • TOOLCHAIN: gcc, icc

ivshmemとは仮想マシン間での共有メモリに関する技術らしい。。。知らない。 今回はx86_64-native-linuxapp-gccをターゲットにやっていくよ

ビルドは以下のコマンドで行います。

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

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

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

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

3.3 DPDKのビルドを確認

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

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

3.4 UIOモジュールのロード

  • UIO: ユーザ空間ドライバを作成するための仕組み

DPDKはユーザランドデバイスドライバなので、UIOのカーネルモジュールをロードします。

$ sudo modprobe uio_pci_generic

またこれでうまくいかない場合は次の方法でカーネルモジュールをロードします。xxxやyyyは作成kmod以下に作成されたモジュールの名前なので読み替えてください。

$ cd x86_64-native-linuxapp-gcc
$ sudo modprobe uio
$ sudo insmod kmod/xxx.ko
$ sudo insmod kmod/yyy.ko

3.5 VFIOモジュールのロード

  • VFIO: ユーザ空間からデバイスのメモリ空間をダイレクトに参照したりできるすっげーやつ

DPDKアプリケーションはVFIOを使用するので、vfio-pciをロードする必要があります。

$ sudo modprobe vfio-pci

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

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

DPDKのビルドは完了なので、ネットワークポートをDPDKにバインドします。通常状態ではカーネルがハードウェアを管理していますが、ここではDPDKで管理出きるようにします。 バインドにはtoolsディレクトリ以下にあるdpdk_nic_bind.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_nic_bind.py --bind=uio_pci_generic eth0

以下のようなコマンドでも大丈夫です。

$ sudo ./tools/dpdk_nic_bind.py --bind=uio_pci_generic 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が見えなくなる。ちなみにこの時ルート権限が必要。(まあ何となくそうだよね、ふつう)

DPDKの支配から解除してふだんの状態に戻すには以下の様にする。

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

これでさっきの状態に戻る。

$ ./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=igb_uio

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

4. サンプルアプリのコンパイルと実行

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

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

  • RTE_SDK DPDKのパスを指定する環境変数
  • RTE_TARGET DPDKをを動かすターゲットを指定する環境変数

今回は以下のように指定した。また、永続的に指定する場合は.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 -c f -n 4
実行結果は省略

デバイスをバインドしている場合は-bオプションで指定する必要がある。例として0000:00:19.0というポートがある場合は以下のように指定する。

$ sudo ./build/app/helloworld -c f -n 4 -b 0000:00:19.0
実行結果は省略

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 様々なパケットに対するスループットテストをします

まとめ

疲れました。。英語のドキュメントを読むのはまだまだ、辛い。。 でも勉強になったしよかったと思います。