Japan Linux SimpsoumにてDavid S.Millerさんと会談しました

会談目的
LinuxのNetworkの開発者であるDavid S.Miller様とMultiQueueのメンテナであるHerbert Xu様とFernando Luis Vázquez Cao様のご尽力により会談が実現しました。
この会談の意義は、現在kernelで進行中のMultiQueueの実装及びIFについて、l7vsd側からどのような方式が望ましいのか、そしてkernel側としてはユーザー空間側のプログラムがどのような挙動を望んでいるのかの意識をすりあわせることにより、双方にとって最適なMultiQueueのKernel実装を導き出すことを目的にしています。

現状のLinuxでのMultiQueueに対する2つの件
今回確定的な件は以下の二つ。

  • 「現状のMQ実装でNICを2枚利用した場合の不具合」 proxyやロードバランサのようにNICが2枚ある場合に二枚目のNICで送信する場合にskb_hashを呼び出すことでCPUの使用がずれる件
  • 「ラウンドロビン式のQueue使用方法」新規に受け付けた接続がIPとPORTのhash値を計算して使用するQueueを選択する既存の方式に対して、ラウンドロビンで接続順にQueueを選択し、CPU使用率の分散化を図るハードウェア拡張

この二つのことに関して、以下に説明させていただきます。

■現状のMQ実装でNICを2枚利用した場合の不具合
l7vsd_software_structure.png
現状での問題が発生する図です。
NICが二枚ある場合、DirectedAcceptの機能により上記にあるように受け付けたQueueが割り振られているCPUのThreadのacceptが反応するため、スケジューリングコストは最小限であり、他のCPU上のメモリを参照もしないため、コストは最小限で抑えられます。
しかしThread内部で別のNICから送信する場合、kernelは新規接続になるためにskb_hash()関数(kernelのnetwork coreに存在する関数)を呼び出し、使用するQueueを選択します。
この場合、Threadが所属するCPUと同じQueueが使用される保証は全くなく、逆に別CPUで実行される確率の方が高いため、CPUをまたいだ処理になるためスケジューリングコストが増大し、かつ別ノードのメモリを参照するため性能低下が激しくなります。

これはDavid.S-Millerさんも納得されており、Fernandoさんがパッチを投稿すると云う段取りになりました。
その改善案が次の図にようになります。
l7vsd_software_structure-1.png
単純にskb_hash()を呼び出さない実装ではなく、接続を開始したthreadが所属するCPUを判断して使用するQueueを選択します。
この修正をすることで、今までNICを2枚使用した場合に2枚目からの送信時にタスクスケジューラが送信に使用したQueueと同じCPUにThreadを割り振ろうとしCPUを移動させて、次に1枚目のNICよりデータを受信するときに再びタスクスケジューラが元のCPUへとThreadを移動させようとThreadの帰属CPUが頻繁に変更されるような状況を作り出していた不具合が無くなります。

■ラウンドロビン式のQueue使用方法
現状のNICにおいて、接続要求を行うコネクションがどのQueueを使用するか、そのアルゴリズムはデフォルトでIP/PORTよりhashを計算して使用するQueueを決定しています。
nomal_queue_select.png
他にもMACアドレスでQueueを確定する方法などがありますがこれらの方式で問題となるのがhashを求める段階で偏りが発生する可能性があるという事ですね。
特に、MACアドレスでQueueを選択する場合、L3スイッチに直接接続されている場合などはたった一つのQueueしか使用出来ません。
複数のCPUを使って処理効率を上げようというアプローチが有効に機能しないことが発生します。
queue_bias_sciene.png
某商用のソフトウェアはハードウェアラウンドロビン機能を使ってこの状況を回避しています。
接続順と云う要素からqueueの使用場所を決めるというアプローチで全てのqueueが均等に使えるように工夫されています。
using_hardware_rr_queue.png
このハードウェア機能を利用することにより効率的にCPUを利用できるようになります。

Ultramonkey-L7のプログラム構成についての考察
これらFlowDirectorDirectedAcceptなどを効率よく利用できるようなソフトウェア構成を考察する必要があります。
DirectedAcceptFlowDirectorなどを考慮したソフトウェア構成を考察すると次のようになります。
thread_grouping_structure.png
DirectedAcceptはacceptの所属するthreadのqueueを利用しacceptに反応を上げる作りですから、queueの数だけacceptが並列に動ければもっとも効率的に処理が可能になります。
もちろん、threadの作成と消滅にはコストがかかりますので、使用するthreadは予め作成し、休止状態にしてpoolingしておきます。
つまりこの構成で一番重要なのはqueue1つにつき1つのacceptを用意すると云うことです。この1つのCPUについての構造を以下に示しましょう。
thread_structure_forCPU.png
図のような構造がCPUごとに用意されると思ってください。もちろん、VirtualServiceごとにLisningSocketがありますから、l7vsd全体で見た場合にはqueue:acceptor = 1 : n になります。
この構造で一番の重要な部分はacceptor threadがqueueにデータ入るのを監視するためにnon-blockingで読み出しを行うと言う部分でしょう。David様に確認したところこの構造が一番速いと云うことなので速度を考察した場合にはnon-blockingで呼び出しを行うことが最優先かと思います。
次にacceptして新しいsocketが取得出来たところで、accept threadはsession threadにsocket情報と含めて引き継ぎ、以降の処理はsession threadが処理を引き継ぎます。
ここで大事なのは、処理のスタートはaccept threadが必ず起点になってthread poolから休眠中のthreadを起こしてきますが、処理が終わるとき、コネクションが切断されたりするときにはthreadは自分で後処理をしてthread poolに戻る必要があると云うことです。
また、thread poolにはスタック的にthreadをため込む必要があります。
これはthread自身がメモリを必要とし、メモリアクセスは局所性を持たすべく(そのためにキャッシュメモリが存在する)なるべく直近に使ったthreadを使い回すコードを意識する必要がある為です。

作りとしてはacceptをCPUにくくりつけて構成するという部分が一番のキモでしょうか。これに付随して対応するNICや、どのように各情報を引き出してくるかはkernelをwatchする必要があります。 Pour participer maintenir votre numéro, vous aurez peuvent avoir compte driver (utile à votre portabilité mobile) rio bouygues. Vous obtiendrez pouvez obtenir pour totalement gratuit par téléphonant expression du serveur ou du service à la clientèle satisfaction client partir de votre fournisseur de services code rio bouygues . Vous ne mai recevez immédiatement un SMS avec votre . Avec votre propre code rio orange, alors vous pouvez vers le offre de de son à propos fruits .