ProjectsProjectsBlog「丁寧な暮らし」DPDKでLチカDPDKでLチカし、ついでに演奏します。dpdkこの記事は下記で寄稿した記事を2024年に再構成したものです。DPDKらしからぬ使い方をしてNICのLEDを制御します。DPDKはパケット処理を高速化するライブラリで、副次的にユーザランドからNICへ直接アクセスが可能です。通常は触りにくいNICのレジスタ、特にLEDを点灯させるレジスタへもアクセスでき、次の図のようなことが実現できます。照リーヌの様子さらに、光とくれば音でしょう。そこで今回は、コンピュータに備わるブザーで音楽を奏でること、またそれに連動してNICのLEDを光らせることも試しています。それでは、次章から本題に入ります。第2章はDPDKの概要とNICのLEDを制御するAPIについての紹介です。第3章はLEDを制御するための実装に関して、既存のETHTOOL_PHYS_IDおよびDPDKを利用した場合のそれぞれについて述べています。第4章が発展課題として音楽演奏やいくつかの課題に取り組んだ結果で、最後の第5章がよく分からないあとがきです。DPDKとはDPDKとは何か、公式サイトによると「パケット処理を高速化できるライブラリ」で「LinuxのユーザランドとFreeBSDで動く」そうです。Linuxの場合のパケット処理はカーネルが行うはずで、一部の場合を除きユーザランドで行われません。一部の場合とは、例えば独自のネットワークスタックを動かしたい、または特殊なルーティングを実現したいときです。socketといった機能を利用してユーザランドでパケットを処理しますが、カーネルとの頻繁なメモリコピーといった要因によってパフォーマンスの低下が予期されます。socketを用いたユーザランドでのパケット処理を、CPU拡張命令やアクセラレータやなんやかんやで頑張って高速化できるライブラリ」なのでしょうか?それはそれで有用だと思いますが、DPDKは「パケット処理をカーネルよりも高速化できるライブラリ」です。以降では、この高速化を実現するための特徴と、気になるAPIについて述べます。DPDKの特徴DPDKには、「割り込みの廃止」と「カーネルバイパス」という大きく分けて2つの特徴があります。API)1において、受信したパケットを処理する際のおおまかな流れを次に示します。NICがパケットを受信し、割り込みを発生させるしばらくポーリングでパケットを処理していく次の割り込みを待つ割り込みを待っている間、CPUは他の作業を行えます。その一方で、割り込みによって作業を切り替える必要があるため、コンテキストスイッチとそれによるオーバヘッドの発生は避けられません。Driver)という仕組みによって割り込みを廃止し、すべての処理をポーリングで行っています。この間CPUは他の作業が行えない、つまりCPU使用率100%となりますが仕様です。I/O)は、ユーザランドからデバイスを操作できる機能です。DPDKでは、このUIOなど2を用いてユーザランドからNICを操作する、つまりカーネルをバイパスします。これによって、ユーザランドのアプリケーションであってもNICとのやりとりにDMAを利用でき、NICで送受信されるデータへのアクセスが高速になります。カーネルをバイパスするということは、NICがカーネルの管理から外れることを意味します。ifconfigやethtoolといったツールからいないものとして扱われ、カーネルが備えているデバイスドライバやプロトコルスタックは利用できません。いえ、デバイスドライバについてはDPDK側で用意されたものがあり、あまり気にしなくて大丈夫です。プロトコルスタックについては、そもそもスイッチやルータを作るならほぼ必要ありません。本格的なプロトコルスタックが必要でも、Seastarなどの独自のTCP/IPプロトコルスタックを備えたフレームワークを活用すれば楽をできるかも知れません。DPDKのAPIDPDKは「パケット処理を高速化できるライブラリ」であり、さまざまなAPIが存在します(APIドキュメント)たとえば、rte_eth_promiscuous_enable()ではNICのプロミスキャスモードを有効にできるほか、rte_eth_dev_get_eeprom()でEEPROMの内容を取得可能です。プロトコルの処理に関してもrte_ipv4_udptcp_cksum()でチェックサム程度なら計算できるほか、各種プロトコルヘッダの構造体がすでに定義されています。rte_eth_led_on()やrte_eth_led_off()といった興味深いAPIもありました。これらの詳細をドキュメントで確認し、要約すると次のようになります。デバイスが削除された最高ですね。このAPIを使ってみたいものです。光れ!NICニウムということで、DPDKでNICのLEDを光らせましょう。そこで、まずはDPDKを使わずにNICの制御をLinuxカーネルに任せた場合でもLEDを光らせられるのか検討します。先に結論を述べると、LEDを自由に制御するためにはカーネルの改変が必要で敷居は高いことが分かりました。通常、NICのLEDが光って嬉しいときというのは、eth0やenp1s0といったデバイス名に紐づくNICの物理的な位置を知りたい場合でしょう。<デバイス名>というコマンドで、NICのLEDを一定間隔で点滅させます。この機能は、ioctlシステムコールを呼び4実現しているということが、straceや次に示すソースコードから分かりました。err;}このioctl()を実行すると、カーネルのnet/core/ethtool.cに定義されるethtool_phys_id()関数が呼ばれます。ETHTOOL_ID_ON)によってLEDを点滅させていました。(デバイスドライバ側で点滅させるNICもあるようですが、もう追いきれません。rc;}set_phys_id()は関数ポインタであり、たとえばe1000ドライバの場合drivers/net/ethernet/intel/e1000/e1000_ethtool.cに定義されるe1000_set_phys_id()が呼び出されます。[ms]間隔でLEDの点滅が変化すること、またETHTOOL_ID_ONやETHTOOL_ID_OFFが指定されるとe1000_led_on()関数やe1000_led_off()関数を呼び出していることが判明しました。e1000_led_on()関数やe1000_led_off()関数の中では、特定のレジスタを操作しています。これによって、NICの口に内蔵されたLEDに向けて電圧が印加され、点灯状態が変化するわけです。-pによる操作でのLEDは点滅以外できず、しかもその周期はデバイスドライバによって決められているということになります。実はe1000_led_on()関数やe1000_led_off()関数をたとえ間接的にでもユーザランドから呼ぶにはioctl()でSIOCETHTOOLを操作する以外なく、ユーザランドからLEDを自由に制御することはできません。しても自由なLED制御をユーザランドから実現したい場合、net/core/ethtool.cの改変が必要となり、気軽に試すわけにもいかなくなります。DPDK編やはりDPDKから照らすのが最高なんですね。いうことで、次からはその動作環境について説明し、続いて環境構築とDPDKアプリケーションの作成について述べます。動作環境まず、DPDKが動作する環境において最も重要なハードウェアはNICです。(e1000)とixgbeにしか対応していませんでした。Adapter)といった仮想NICにも対応しています。物理NICで動かしたいところですが、Intel以外のNICは10GbEやFPGA付きばかりなので覚悟が必要です。NICを買う方針でも良いと思います。このとき複数のポートがあると、スイッチやルータのサンプルアプリケーションが動かしやすくなるかも知れませんね。Controller)」を4,930円で購入しました。(※2018年時点の情報。現在の2024年時点でも別に良いとは思いますが、自分なら検証後に使い回すためもっと新しいNICにすると思います。Mellanoxの何かとか...?)次にCPUです。(x86_64)やARMのほか、POWERまでサポートされています。x86に注目すると、次の条件を満たす必要がありますが(※2018年時点)最近のCPUであれば問題ありません。マルチコアCPUDPDKに1コアを持っていかれるためです。今となってはわざわざシングルコアCPUを用意するほうが難しいと思います。Hugepages対応DPDKではHugepagesを利用しています(と言いつつ--no-hugeオプションもあるようですが...使ったことが無いので割愛します)。Hugepagesとは、仮想記憶におけるページサイズを通常の4KBから2MBや1GBに拡張できる仕組みのことです。これによって任意アドレスの変換情報がTLBに収まるほど少数のページテーブルに集約されやすくなり、TLBミスの抑制が期待できます。(1GB)があれば大丈夫です。DPDKのドキュメントによると「Hugepagesは全体で2GBほど予約すべし」といった記述があります。小さくしても動くので、メモリが足りなくても心配する必要はありません。v17.08以降では、命令セットとしてSSE4.2を備えたCPUを要求するようになりました。iシリーズの第一世代(Nehalem)から実装されており、よほど古いCPUでなければ気にする必要はないでしょう。E3400」というよほど古いCPUであり、SSE4.2対応の条件を満たせませんでした。v17.05では、SSE4.2ではなくSSE3を備えたCPUであれば利用可能です。そこで、そのバージョンを利用することにします。DPDKが管理するポートIDがuint8_tからuint16_tに拡張されたなど多少の違いはありますが、やむを得ません。以上を踏まえつつ動作環境を決めると、次に示す表のとおりとなりました。2MB×512Pages環境構築ソフトウェアのセットアップについて述べます。やることはDPDKのビルドです。次に示すようにいくつかアプリケーションをインストールして、ソースコードからビルドします。T=x86_64-native-linuxapp-gccその次はHugepagesの設定です。次に示すコマンドを実行します。rebootさらにNICの設定です。次に示すように、NICをカーネルからDPDKの管理下におきます。この操作は再起動すると元に戻るので気をつけましょう。modprobeについては/etc/modulesにモジュール名を書いておけば起動時にロードしてくれます。dpdk-devbind.pyについてはsystemdのサービスを書いて起動時に実行するという方法があるようです。--statusそして最後に環境変数の設定を行います。内容は次のとおりです。~/.bash_profileかどこかに記述しても構いません。RTE_TARGET=x86_64-native-linuxapp-gccこれまでの操作でDPDKアプリケーションのビルドと実行が可能になります。実際、次のようなコマンドが実行できるはずです。./build/helloworldDPDKアプリケーションの作成やや大げさな見出しですが、NICのLEDを光らせるためのDPDKアプリケーションを作成します。前もって作成しておいたものがこちらです。lrks/hikare-nicniumhttps://github.com/lrks/hikare-nicniumここでは、DPDKのexamples/ethtoolに含まれる初期化処理を流用しました。主な処理は次に示すとおりとなり、非常に簡単になります。rte_eth_led_on()とrte_eth_led_off()を叩いているだけなので当然ですね。0);}このアプリケーションを実行すると、次に示す図のようにNICのLEDが交互に点灯します。異なるNICのLEDが光る発展課題ただ単にLEDを光らせただけでは物足りません。そこで、3つの発展課題をこなしていきます。「可」という感じです。PWM制御LチカといえばPWMでしょう。LEDをPWMで制御すれば、点灯または消灯という状態に加えて「ほのかに光る」「そこそこ明るい」といった中間の状態を擬似的に作り出せます。これにはある期間における「LEDが点灯している時間」と「消灯している時間」の比率を制御する必要があり、今回のDPDKでは次のようなコードで実現できると考えていました。}}ところが、このコードを実行すると常にLEDが点灯してしまいます。いえ、わざわざオシロスコープで観測しても常に点灯しています。した原因は不明ですが、NIC側の少なくともLED状態を設定するレジスタへのアクセスが頻繁に行えないような、または消灯よりも点灯が優先されるような印象を受けました。一定時間待てばきちんと点灯と消灯が可能でしたが、この一定時間というのは200msなど点滅が目視できるほど長く、PWM制御は諦めざるを得ません。DPDKで利用されるドライバを改変し、レジスタの書き込みに使われるE1000_WRITE_REG()マクロの代わりにE1000_PCI_REG_WRITE_RELAXED()+E1000_PCI_REG_ADDR()マクロやE1000_WRITE_FLUSH()というそれらしい名前のマクロも使ってみたものの、効果はありませんでした。いずれにしてもPWMは実現できませんでした。光れ!NICニウム~ethtoolこうこうこうこう部へようこそ~少し前に「ethtoolではNICのLEDを自由に制御できない」と述べました。紹介したethtool_phys_id()について、再度見てみます。ETHTOOL_ID_INACTIVE);おおまかな処理の流れは次のとおりです。[tick]まで任意に設定できます。その後にすぐethtool_phys_id()を呼び直せば、より長い時間LEDが点灯しているように見えるかもしれません。LEDの消灯時間は1.でLEDが消灯している、すなわちNICがLinkUpしていないならばもちろん任意で、これは10.によって直前の点灯状態に依存しないはずです。これらを踏まえ、まずは点灯のみに注目した実験コードを次に示します。0;}ところが、このコードを実行しても期待通りの動作はしません。permitted)が返され、反対に点灯時間を短くしようとすると無視されて引き伸ばされます。後者に関しては前項の「PWM制御」で述べたのと同じ挙動という印象で、ethtoolとDPDKのどちらを使っても回避できないのかも知れません。発展課題なので履修放棄します。BUZとioctl音楽を奏でつつ、NICのLEDが光ったら面白いと思いました。コンピュータで音を鳴らすといえば、pcspkr(BEEP)をioctl()やbeepコマンドで操作して鳴らすのが一般的です。[Hz]の音が1秒間鳴ります。close(fd);}和音は出せない、つまり同時発音数は1つだけですが意外と綺麗な音です。コンピュータに備わるブザーではなく、ALSAから音が出る場合があります。これは、サウンドカードを検出するとブザー音がエミュレートされてしまうためで、下記のページによると回避するにはカーネルのリビルドが必要のようです。半月記このコードに周波数と時間を書いていけば音楽を奏でられますが、手でひとつずつ書いていくのは非常に手間です。File)を基に奏でて楽をしましょう。SMFとは、後述する「MIDIイベント」とそれを発行するタイミングが記録された、いわゆるMIDIファイルです。イベントとタイミングはトラック(Track)という場所に格納されます。2)では最大256本のトラックを保持でき、各トラックに格納されたデータは他トラックと独立です。ここに含まれるMIDIイベントのうち、主なものは次のとおりとなっています。(0~15)で鳴らす。0~127)で指定する。vvを00とすると、次に述べる「ノートオフ」とほぼ同じとなる。vv)チャンネルnで鳴っているノートナンバーkkの音を止める。鍵盤から手を離す速度vvを指定する。vv)チャンネルnで鳴っているノートナンバーkkの音を速度vvで発音し直す。ノートオンと同様に、vvが00ならばノートオフと同じになる。vv)チャンネルnで鳴っているすべての音を速度vvで発音し直す。vvが00ならばチャンネルnの音をすべて消音する。0~127)に変える。音色にはピアノや打楽器などがあり、番号との対応は機器がサポートする規格によって決まる。ll)チャンネルnのピッチ(音高)を微調整する。mmとllはともに7bit、mmをMSB、llをLSBとして0~16383までのデータを構成する。ここでは初期値を8192としますが、-8192~8191として初期値0とすることもあるようです。7B)チャンネルnで鳴っているすべての音を止める。79)チャンネルnについて設定値を初期化する。ここで、「チャンネル」とは機器アドレスのようなものです。MIDI)という規格では10番目のチャンネルが打楽器専用など、チャンネル番号自体が意味を持つこともあります。pcspkrに渡す周波数はノートナンバーとピッチを基に次のように計算できます。Exchangeこれらを踏まえてSMFをパースしていきます。Cは辛いので、一度PythonからパースしてCで扱いやすいテキスト形式に変換しましょう。midoというライブラリを用いて実装したものがこちらになります。[outputFile]そういえば、以前にpcspkrでは和音が出せないことを述べました。MIDIしか奏でられません。MIDIもそれなりに奏でられるよう「先着順」で音を鳴らしています。その様子は次の図のとおりで、発音中に来たイベントは無視されて流れていきます。MIDIイベントを「先着順」で発音する様子(黒塗りのところが発音状態)しかし、この方法では必ずしも主旋律が鳴るとも限らず曲がよく分からなくなってしまいそうです。MIDIにするのは音楽情報検索(MIR)において必要なタスク(特徴的な情報を抽出し検索しやすい形にするため)のようで、そのためのアルゴリズムがいくつか提案されています。そのうちの一つに、Skylineと呼ばれる方法があるようです。Multimediaの「All-Mono」が基になっているようで、この手法では「先着順」かつ「なるべく高い音」を鳴らそうとします。様子としては次の図のとおりで、実際に試してみると主旋律の音高が高い曲5においては、ほぼ主旋律だけを鳴らせました。LED制御の様子を収めたのが次の動画です。NICのLEDもちゃんと光っていますね。ちなみにDPDKで制御しているのは右側カードのNICです。これは「別のPCからSSHで繋いでいる」「ブザーを鳴らすたびにprintfしていた」結果、それっぽく連動したようです。DPDKでNICのLEDを操作する必要なんて無かったんや!(企画倒れ)MIDIの主旋律のほうですが、うまく抽出できて鳴らせているようです。君のこと」のあとに伴奏が流れてくるのが良いです。ちなみに、他の主旋律抽出の手法も調べてみました。具体的には、特徴的なチャンネルを選択・結合してSkylineを適用する手法(ozcan2005)や、ノートの数や種類から主旋律のトラックやチャンネルを抽出する手法(velusamy2007)です。良い結果は得られませんでした。期待していたよりも伴奏が抽出されてしまい、奏でる曲がよく分からなくなってしまいます。いえ、前処理として述べられていた「打楽器は伴奏なので除く」「発音時間が0.05秒など短すぎるノートはノイズの可能性が高い」というコツは有効でした。これをSkylineと合わせるといい感じになります。おわりにNICのLEDを光らせることができて良かったです。本当はPWMがうまくいかなかった原因を探ったり、リンクLEDのみならずステータスLEDも光らせたりしたかったですね。データシートを読む気力がなかったので終わりです。後半はpcspkrとMIDIの話が中心でした。音を鳴らすことができて良かったです。できれば、擬似和音にも挑戦したかったですね。[ms]間隔で区切って鳴らすとそれらしい感じになっていました。この間隔はなんとなく決めるものではなさそうなので、もっといい感じでやりたいです。Footnotesパケット受信ごとに割り込み処理を行っていた時代に登場した「新しいAPI」です。NAPIの「N」は「Network」ではありません。NAPI」が出るかも知れませんね。I/O)もサポートされています。↩この原稿は2018年7月~8月ごろに書いたものですが、図らずも進次郎構文になっていた。厳密には、ソースコードだけではioctlシステムコールが呼ばれているとは断定できず、「ioctl()関数が呼ばれている」「(絶対ではないがおそらく)glibcまたは同等のインターフェースを持ったCライブラリのioctl()関数を呼ぼうとしており、これはioctlシステムコールをラップしているはず」「実行時にも(LD_PRELOADなどで曲げない限り)glibcなどのioctl()関数が呼ばれ、最終的にioctlシステムコールが呼ばれるであろう」です、と予防線を張ります。Vacation~」「みんなのきもち」あたりだと、伴奏も高めの音でありうまく抽出できないかも知れません。光れ!NICニウム~ethtoolこうこうこうこう部へようこそ~BUZとioctlおわりにFootnotes!!!XGitHubお問い合わせ。