リソース設定(NIC) ~DPDK入門 第6回~
DPDKではNICを制御するための専用のドライバを使っています。今回は通常のNICとの違いを解説し、DPDKがNICをどのように利用しているかを紹介していきます。
DPDK入門
- 2019年06月10日公開
はじめに
こんにちは、NTTテクノクロス株式会社の寺尾です。
今回はDPDKがNICをどのように扱うかに関して解説していきます。よろしくお願いします。
UIO (User space I/O)
DPDKとは? ~DPDK入門 第1回~ で、DPDKはカーネルをバイパスしてシステムコールを介することなく直接ハードウェアを制御することで、高速化を実現しているということを紹介しました。
Linuxではユーザが直接ハードウェアを制御するプログラムを作成するための、UIO(User space I/O)という仕組みを用意しています。例えば、通常ハードウェアの制御を行うレジスタはユーザがアクセスできないメモリ空間に置かれますが、UIOの仕組みを利用することでユーザ空間のメモリにマッピングされ、ユーザが作成したプログラムから制御することが出来ます。
DPDKでは、UIOを利用して直接NICを制御することで、より高速なパケット処理を実現しています。なお、UIOを利用してNICを制御するようにした場合、NICの制御がカーネルから外れるため、例えばethtool(※1)を利用してNIC情報を参照する、あるいは DPDKのパケット処理 ~DPDK入門 第2回~ で利用したtcpdumpでパケット受信確認を行う等、カーネルが用意しているコマンドが使用できなくなります。
※1 ethtool(8) - Linux man page
https://linux.die.net/man/8/ethtool
PMD (Poll Mode Driver)
NICは受信したパケットや送信するパケットを格納しておくメモリ領域を持っています。このメモリ領域のことを「キュー」といいます。 通常はカーネルがNICが受信したパケットをキューから取り出したり、送信したいパケットをキューに格納します。カーネルがこのような役割を担っているため、アプリケーションがパケット処理を実現する場合、システムコールを通してカーネルが格納したパケットを取得したり、送信したいパケットをカーネルに渡す必要があります。
カーネルを利用したパケット受信の処理イメージを下図に示します。この場合、割込みやカーネル空間からユーザ空間へのパケット引き渡し等のオーバーヘッドが発生し、アプリケーションのパケット処理性能に影響を及ぼしてしまいます。
一方、DPDKアプリケーションでパケット処理を実現する場合、下図のようにNICに対して常にポーリングでパケット受信を監視する形をとり、パケット自体もNICのキューから直接ユーザ空間(Hugepage)に移すため、オーバーヘッドなくすぐにパケット処理を実施することができます。
PMDに関して詳しく知りたい人は、DPDKの公式ページ(※2)にも記載がありますのでご覧下さい。
実際のNICは様々な種類がありますが、NICに対応したデバイスドライバも複数の種類があります。例えばIntel社のNICでいうと、PCI Express Gigabit Ethernet用のデバイスドライバであるIGB(※3)や、PCI 10Gb 用のデバイスドライバであるIXGBE(※4)があります。
通常はカーネルがNICの種類に応じたデバイスドライバを利用するため、開発者がパケット処理を行うアプリケーションを実装する際は、システムコールを介してNICの種類を意識することなくプログラミングすることができます。DPDKでも同様に、DPDKライブラリ側がNICの種類に応じた専用のPMDを割り当ててくれるため、開発者はDPDKが用意している専用のパケット処理用APIを利用することで、NICの種類を意識することなくプログラミングすることができます。
DPDKではシステムコールを介せずにパケット処理を実施することで、高速なパケット処理を実現しています。その仕組みはDPDK特有の内容になるため、この考え方を理解することが重要です。
※2 Poll Mode Driver
https://doc.dpdk.org/guides/prog_guide/poll_mode_drv.html
※3 IGB Manual Pages
https://nxmnpg.lemoda.net/4/igb
※4 IXGBE Manual Pages
https://nxmnpg.lemoda.net/4/ixgbe
DPDKのNICの設定方法
今までの連載では、DPDKのセットアップツールを利用してNICの設定方法を紹介していました。この方法でも問題はありませんが、今回は一般的なモジュールを使う方法を紹介します。
DPDKのセットアップツールでは"igb_uio"というUIOモジュールを使っています。igb_uioはDPDKがソースコードを提供していて、セットアップツールからコンパイルすることでUIOモジュールを作成していました。今回はLinuxカーネル付属のUIOモジュールである"uio_pci_generic"を利用します。
uio_pci_genericの使い方
uio_pci_genericを利用する場合、modprobeコマンドを使用してuio_pci_genericをロードします。
lsmodコマンドを使用して、表示が確認できればロードに成功したことを確認できます。
uio_pci_generic 16384 0
uio 20480 1 uio_pci_generic
・・・・
NICをDPDK用のドライバに割り当てます。割り当てには、DPDKが提供している"dpdk-devbind.py"というスクリプトを使うと簡単にできるので、この方法を紹介します。このスクリプトはDPDKのセットアップツールと同じ場所に格納されています。
またdpdk-devbind.pyに--statusを付けて実行することで、現在のNIC状態を確認することができます。例えば以下のように出力結果の場合は、eth0、eth1ともにカーネル用のドライバ(今回はigb)が割り当たっていることが分かります。
$ export RTE_SDK=`pwd`
$ export RTE_TARGET=x86_64-native-linuxapp-gcc
$ cd $RTE_SDK/usertools
$ ./dpdk-devbind.py --status
・・・
===================================
Network devices using kernel driver
===================================
・・・
0000:04:00.0 'I350 Gigabit Network xxxxxx' if=eth0 drv=igb unused=igb_uio *Active*
0000:04:00.1 'I350 Gigabit Network xxxxxx' if=eth1 drv=igb unused=igb_uio *Active*
・・・
=====================
Other Network devices
=====================
・・・・
dpdk-devbind.pyを用いてeth0及びeth1をDPDK用のドライバに割り当てます。-bオプションでDPDKが利用するドライバ(今回はuio_pci_generic)を指定し、それ以降にNICのPCIアドレスを指定します。
$ ./dpdk-devbind.py --status
============================================
Network devices using DPDK-compatible driver
============================================
0000:04:00.0 'I350 Gigabit Network Connection xxxxxx' drv=uio_pci_generic unused=igb
0000:04:00.1 'I350 Gigabit Network Connection xxxxxx' drv=uio_pci_generic unused=igb
Network devices using kernel driver
===================================
・・・・・
「Network devices using DPDK-compatible driver」の欄に「0000:04:00.0」と「0000:04:00.1」のNICが表示されていれば成功です。以前とは違うDPDK用のドライバが割り当てられていることを確認下さい。
DPDKが提供しているPMD
DPDKはいろいろなPMDが用意されており、様々なNICに対応していますが、ここではそのうちいくつかを紹介します。例えば、Intel社が提供している"igb"や"ixgbe"はこの記事でも紹介していますが、他にも様々なネットワークインターフェースに対するPMDが用意されています。詳しく知りたい方は、DPDKの公式ページ(※5)をご覧下さい。
また仮想的なNICを扱うPMDも用意されています。例えば、"virtio"というKVM上で構築されたVMの仮想NICがありますが、VM上でDPDKアプリケーションを扱えるようにこの仮想NICに対応したPMDも用意されています。
他にはソフトウェアベースのPMDも用意されています。ここでは、そのうちの一つであるPCAP-PMDを紹介します。
※5 SUPPORTED HARDWARE NICs
https://core.dpdk.org/supported/
PCAP-PMDとは
PCAP-PMDのコンパイル
・・・ # # Compile software PMD backed by PCAP files # #CONFIG_RTE_LIBRTE_PMD_PCAP=n CONFIG_RTE_LIBRTE_PMD_PCAP=y ・・・
以降はDPDKのコンパイルから始めますが、やり方は DPDKとは? ~DPDK入門 第1回~ の「DPDKの環境準備」で解説していますので割愛します。この後、通常ならばDPDKで利用するNICを割り当てる必要がありますが、PCAP-PMDのみ利用する場合はカーネル用のドライバを使うため不要です。
PCAP-PMDの環境準備と動作確認
今回は前回と同様にホストAのeth0から送信されたパケットをホストBのl2fwdが受信しホストBのeth1と接続されているNICへパケット転送する構成となっています。また、パケット送信ツールにncコマンド、パケット受信確認にはtcpdumpコマンドを使います。前回とは違うところとしては、ホストAでtcpdumpを使用していましたが、PCAP-PMDを利用するとホストBでDPDKが利用しているNICでパケット受信確認が可能となりますため、この方法で確認します。今回の確認構成イメージを以下に示します。
それではL2 Forwarding Sample をコンパイルします。以前とやり方は同じです。
$ cd dpdk-<version>
$ export RTE_SDK=`pwd`
$ export RTE_TARGET=x86_64-native-linuxapp-gcc
$ cd $RTE_SDK/examples/l2fwd
$ make
CC main.o
LD l2fwd
INSTALL-APP l2fwd
INSTALL-MAP l2fwd.map
・・・・・
続いてL2 Forwarding Sampleを起動します。起動方法について、メモリやCPUの割り当て方法は以前と変わりませんが、今回はDPDKドライバを割り当てていないため、どのNICをDPDKのポートとして扱うかを指定する必要があります。
指定方法は--vdev=net_pcap<数値 or 文字列> , iface=<PCAP-PMDで利用するNIC名> となります。それでは起動します。
$ sudo ./build/l2fwd -c 0x3 --vdev=net_pcap0,iface=eth0 --vdev=net_pcap1,iface=eth1 -- -p 0x3
・・・・
EAL: PCI device 0000:04:00.0 on NUMA socket 0
EAL: probe driver: 8086:1521 net_e1000_igb
EAL: PCI device 0000:04:00.1 on NUMA socket 0
EAL: probe driver: 8086:1521 net_e1000_igb
EAL: PCI device 0000:05:00.0 on NUMA socket 0
EAL: probe driver: 8086:154d net_ixgbe
EAL: PCI device 0000:05:00.1 on NUMA socket 0
EAL: probe driver: 8086:154d net_ixgbe
MAC updating enabled
Lcore 0: RX port 0
Lcore 1: RX port 1
Initializing port 0... done:
Port 0, MAC address: xx:xx:xx:xx:xx:xx
Initializing port 1... done:
Port 1, MAC address: xx:xx:xx:xx:xx:xx
Checking link statusdone
Port0 Link Up. Speed 10000 Mbps - full-duplex
Port1 Link Up. Speed 10000 Mbps - full-duplex
L2FWD: entering main loop on lcore 1
L2FWD: -- lcoreid=1 portid=1
L2FWD: entering main loop on lcore 0
L2FWD: -- lcoreid=0 portid=0
・・・・・
Port statistics ====================================
Statistics for port 0 ------------------------------
Packets sent: 0
Packets received: 0
Packets dropped: 0
Statistics for port 1 ------------------------------
Packets sent: 0
Packets received: 0
Packets dropped: 0
Aggregate statistics ===============================
Total packets sent: 0
Total packets received: 0
Total packets dropped: 0
====================================================
今回はDPDK用のドライバを割り当てていませんが、L2 Forwarding Sampleが問題無く立ち上がったかと思います。これまでと同様、以下のようにパケットの送受信処理のモニタ画面が表示されれば、L2 Forwarding Sampleの起動確認は終了です。
・・・
hostb$ sudo tcpdump -i eth1
・・・
それではホストAのeth0からパケットを送信します。送信方法は DPDKのパケット処理 ~DPDK入門 第2回~ と同じなので、細かい説明は割愛します。
hosta$ sudo arp -s 192.168.11.100 aa:aa:aa:bb:bb:bb
・・・
hosta$ nc -u 192.168.11.100 5000
test
testtest
今回PCAP-PMDを使ったことで、ホストAが送信したUDPパケットがホストBのeth0及びeth1で受信できていることを確認することができました。
hostb$ sudo tcpdump -i eth0
・・・
12:00:54.355001 IP 192.168.11.1.38166 > 192.168.11.100.5000: UDP, length 5
12:00:57.145262 IP 192.168.11.1.38166 > 192.168.11.100.5000: UDP, length 9
hostb$ sudo tcpdump -i eth1
・・・
12:00:54.357627 IP 192.168.11.1.38166 > 192.168.11.100.5000: UDP, length 5
12:00:57.145590 IP 192.168.11.1.38166 > 192.168.11.100.5000: UDP, length 9
ホストAでも今までと同様にtcpdumpの確認ができますが、PCAP-PMDを使うことで例えばDPDKアプリケーションの問題解析などをカーネルの機能を使って行うことが可能となります。またDPDK対応NICが無い環境でも、DPDKアプリケーションを動かすことができるなどの活用方法がありますので、興味がある人は試してみてください。
おわりに
今回はDPDKがどうやってNICを利用しているか踏み込んで説明させていただきました。次回は数回に分けてDPDKが提供するAPIの紹介と、簡単なDPDKアプリケーションの作成方法を紹介していきたいと考えていますので、よろしくお願いします。ご覧頂きありがとうございました。