#1 DPDKを触ってみよう
はじめに
DPDKは、従来Linuxカーネルが行っていたNIC (Network Interface Card)の制御をユーザー空間で行うためのフレームワークです。カーネルにおける処理との最大の違いは、PMD (Pull Mode Driver)と呼ばれるポーリングベースの受信機構を持つことです。通常、Linuxカーネルでは、NICへのデータの到達を受けて、割り込みが発生し、それを契機に受信処理が実行されます。一方、PMDは、データ到達の確認や受信処理を専用のスレッドが継続的に行います。コンテキストスイッチや割り込みなどのオーバーヘッドを排除することで高速なパケット処理を行います。その他にも高速化のための工夫はありますが、実際に触って行く中ので他の特徴も紹介していきます。
とりあえず触ってみよう!
まずは1つのNICをDPDKによって制御してみましょう。DPDKで制御されるNICは、カーネルからは(ほぼ)完全に独立するので、実験を行う場合、下の図のように,通常使用しているNIC1に加えて、DPDKの実験用にNIC2を追加します。
DPDKが対応するNICは、オフィシャルページに記載されています。対応カードの数は、Linuxカーネルよりだいぶ少なく、Intel社のものが大半です。ここでは、Intel X540-AT2を使用します。またOSは、Ubuntu 16.10を使用します。Ubuntuでは、比較的新しいバージョンのDPDKがaptでインストールできます。使用したホストマシンのスペックは以下のとおりです。
CPU | Intel(R) Core(TM) i7-4770K CPU @ 3.50GHz |
Memory | DDR3-1600 8GB x2 |
NIC1 (一般用) | RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller |
NIC2 (DPDK用) | Intel Corporation Ethernet Controller 10-Gigabit X540-AT2 |
OS | Ubuntu 16.10 |
DPDK | 16.07 |
インストールとセットアップ
次の2つのパッケージをインストールするだけです。
# apt-get install dpdk dpdk-igb-uio-dkms
次にNICをDPDKがユーザー空間で扱うためのモジュールが、起動時にロードされるように/etc/modulesに以下の行を記載します。
igb_uio
そして、DPDKによる高速化の要因のひとつであるHuge Pageを確保します。/etc/default/grubのGRUB_CMDLINE_LINUX_DEFAULT行に以下の水色で示したパラメータを追加します。下記では、1GiBのHuge Pageを4つ確保しています。
GRUB_CMDLINE_LINUX_DEFAULT="default_hugepagesz=1G hugepagesz=1G hugepages=4"
以下のコマンドで設定を有効にして再起動します。
# update-grub
再起動後、以下のようにHugePages_Totalが4で、Hugepagesizeが1048576kBになっていればOKです。
$ cat /proc/meminfo | grep ^Huge
HugePages_Total: 4
HugePages_Free: 4
HugePages_Rsvd: 0
HugePages_Surp: 0
Hugepagesize: 1048576 kB
また、カーネルモジュールigb_uioがロードされていることも確認しておきます。
$ lsmod | grep igb_uio
igb_uio 16384 0
uio 20480 1 igb_uio
NICのDPDKへのバインド
次のコマンドは、NICの一覧と管理状態を表示します。まだDPDKによって制御されいるNICはありませんので、Network devices using DPDK-compatible driverは、<none>になっています。
# dpdk-devbind --status
Network devices using DPDK-compatible driver
============================================
<none>
Network devices using kernel driver
===================================
0000:01:00.0 'Ethernet Controller 10-Gigabit X540-AT2' if=p1p1 drv=ixgbe unused=
0000:01:00.1 'Ethernet Controller 10-Gigabit X540-AT2' if=p1p2 drv=ixgbe unused=
0000:03:00.0 'RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller' if=p2p1 drv=r8169 unused= *Active*
Other network devices
=====================
<none>
X540のひとつ(PCIバス 0000:01:00.0)をDPDKの管理下に置くには次のようにします。
# dpdk-devbind -b igb_uio 0000:01:00.0
再度、状態を確認すると、ひとつのNICがDPDKの管理下にあることが分かります。
# dpdk-devbind --status
Network devices using DPDK-compatible driver
============================================
0000:01:00.0 'Ethernet Controller 10-Gigabit X540-AT2' drv=igb_uio unused=ixgbe
Network devices using kernel driver
===================================
0000:01:00.1 'Ethernet Controller 10-Gigabit X540-AT2' if=p1p2 drv=ixgbe unused=igb_uio
0000:03:00.0 'RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller' if=p2p1 drv=r8169 unused=igb_uio *Active*
Other network devices
=====================
<none>
疎通実験
NIC1とNIC2が通信できることを確認します。ここで注意すべきは、NIC2 (X540)は、もはやカーネルの管理下ではないため、ifconfigやpingなど、一般的なネットワークコマンドの対象外です。ここでは、次の図のようにtestpmdというDPDKに付属のツールを使って、NIC2からNIC1へパケットを送ります。
次のように起動させると実験環境では以下のようなメッセージが表示されました。引数の-iは、インタラクティブモード、すなわち、プロンプトからコマンドを与えるモードで起動することを意味します。その前のハイフン2つありますが、DPDKの共通オプション(EAL command-line options)を与える場合、それらを、ハイフン2つ前に記載し、コマンド固有のオプションを、後ろに記載します。今回は、EALオプションを与えていません。
# testpmd -- -i
EAL: Detected 8 lcore(s)
EAL: Probing VFIO support...
PMD: bnxt_rte_pmd_init() called for (null)
EAL: PCI device 0000:01:00.0 on NUMA socket -1
EAL: probe driver: 8086:1528 rte_ixgbe_pmd
EAL: PCI device 0000:01:00.1 on NUMA socket -1
EAL: probe driver: 8086:1528 rte_ixgbe_pmd
Interactive-mode selected
USER1: create a new mbuf pool <mbuf_pool_socket_0>: n=203456, size=2176, socket=0
PMD: gntalloc: ioctl error
Warning! Cannot handle an odd number of ports with the current port topology. Configuration must be changed to have an even number of ports, or relaunch application with --port-topology=chained
Configuring Port 0 (socket 0)
Port 0: 00:00:5e:00:53:18
Checking link statuses...
Port 0 Link Up - speed 1000 Mbps - full-duplex
Done
testpmd>
上記では、後の確認するためのDPDKで使用するNIC (Port 0)のMACアドレスを水色で表示してあります。
簡易的なテストとして、NIC2からNIC1 (実際のマシン上のI/F名はp2p1)にパケットを送信します。そのために、デフォルトで有効になっている転送機能を無効にします。
testpmd> set fwd rxonly
Set rxonly packet forwarding mode
また、NIC1 (p2p1)でパケットを受信確認をするために別のターミナルでtcpdumpを起動しておきます。ここでソースIPに192.168.0.1を指定するのは、testpmdの出力するパケットのデフォルトのソースアドレスがこの値であるためです。もちろん、実験するネットワーク環境にこのIPが実在する場合、不都合が生じますのでご注意ください。
# tcpdump -e -i p2p1 src 192.168.0.1
この状態で、testpmdで以下の2つのコマンドを入力します。
testpmd> start tx_first
Warning! Cannot handle an odd number of ports with the current port topology. Configuration must be changed to have an even number of ports, or relaunch application with --port-topology=chained
rxonly packet forwarding - ports=1 - cores=1 - streams=1 - NUMA support disabled, MP over anonymous pages disabled
Logical Core 1 (socket 0) forwards packets on 1 streams:
RX P=0/Q=0 (socket 0) -> TX P=0/Q=0 (socket 0) peer=02:00:00:00:00:00
rxonly packet forwarding - CRC stripping disabled - packets/burst=32
nb forwarding cores=1 - nb forwarding ports=1
RX queues=1 - RX desc=128 - RX free threshold=32
RX threshold registers: pthresh=8 hthresh=8 wthresh=0
TX queues=1 - TX desc=512 - TX free threshold=32
TX threshold registers: pthresh=32 hthresh=0 wthresh=0
TX RS bit threshold=32 - TXQ flags=0xf01
testpmd> stop
Telling cores to stop...
Waiting for lcores to finish...
---------------------- Forward statistics for port 0 ----------------------
RX-packets: 0 RX-dropped: 0 RX-total: 0
TX-packets: 32 TX-dropped: 0 TX-total: 32
----------------------------------------------------------------------------
+++++++++++++++ Accumulated forward statistics for all ports+++++++++++++++
RX-packets: 0 RX-dropped: 0 RX-total: 0
TX-packets: 32 TX-dropped: 0 TX-total: 32
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Done.
tcpdumpの出力は以下のようになっており、送信元のMACアドレスを見ると、確かにNIC2からパケットが来ていることが分かります。
20:00:31.539982 00:00:5e:00:53:18 (oui Unknown) > 02:00:00:00:00:00 (oui Unknown), ethertype IPv4 (0x0800), length 64: 192.168.0.1.1024 > 192.168.0.2.1024: UDP, length 22
20:00:31.540001 00:00:5e:00:53:18 (oui Unknown) > 02:00:00:00:00:00 (oui Unknown), ethertype IPv4 (0x0800), length 64: 192.168.0.1.1024 > 192.168.0.2.1024: UDP, length 22
(以下省略)
今回は、疎通していることろまで確認できました。Ubuntuですと、パッケージが利用できるので環境の構築はたいへん楽でした。次回は、もう一台のマシン(OSはCentOS7.3)にDPDKを入れてパケットの対向をさせてみます。