VIOPS06で「RPS・RFS等最新Linux Kernel事例」と題してお話してきました

はじめに

第一回カーネル/VM探検隊@関西第二回日本Vyattaユーザ会ミーティングで行った発表のダイジェスト版です。

詳しく知りたい人はこちらの内容ではなく、第二回日本Vyattaユーザ会ミーティングの動画資料をみる事をお勧めします。

あと、最新って書いてあるけど割と古い話題です。すんません。

発表資料

従来型のNICとネットワークスタックの組み合わせでは、マルチコア環境においても1つのNICの受信処理は1つのCPUでしか行えません。

これは、NIC上に受信のキューと受信を通知する割り込みが1つしか存在せず、ハードウェアからデバイスドライバまでのレイヤーでは受信処理を並列に行う事が本質的に出来ない事が原因でした。

これが原因で、通信量が多い時にパケット処理の負荷が特定のCPUへ偏ってしまい、CPU数を増やしても性能がスケールしないという問題が発生します。

この問題を解決する為に、1つのNICに届いたパケットを複数のCPUへ分散させる機能が実装されており、今日はそのうちRSSとRPS/RFSの話をします。

まず、RSSから紹介します。

RSS搭載NICには複数の受信キューがあり、NIC内でパケットを分類してキューへ送り、そのキューに紐づけられているCPUに割り込みがかかる仕組みになっています。

どのように分類するのか、という事ですが、単純に負荷が全CPU間で公平になればいいならば、ランダムにキューを選べば済むように思えます。

しかしながら、インターネットで最も良く使われているTCPでは、同一フローのパケットは送信した順番通りに受信処理を行いユーザプロセスへ渡さなければならず、順番を無視して並列に処理を行う事はできません。

そこで、NIC上でパケットのヘッダを読んで送信元IP・送信元ポート番号・宛先IP・宛先ポート番号の四つを取り出し、これをハッシュ関数にかけてハッシュ値を計算します。

ハッシュ値からハッシュテーブルを引き、宛先キューを決めます。

同一フローのパケットは常にハッシュ値が同一になる為、常に同じCPUで処理される事となります。

RSS非対応NICの為に、ソフトウェアでRSS相当の機能を実現しているのがRPSです。

従来の実装では、割り込みを受けてパケットを取り出し、プロトコル処理を行ってユーザプロセスへパケットを渡す所まで1つのCPUで行っていました。

RPSではプロトコル処理を行う前に、パケットのヘッダを読んでハッシュ値を計算し、ハッシュ値からハッシュテーブルを引いて、テーブルに書いてあった宛先CPUが持つキューにパケットを繋ぎ、宛先CPUへCPU間割り込みを送ります。

CPU間割り込みを受け取ったCPUでは、キューからパケットを取り出してプロトコル処理を行います。

これによって、RSSに近い効果が得られます。

RFSは、RPSのハッシュテーブルを適切に書き換える事により、分散先のCPUを最適化する機能です。

どういう事かと簡単に言うと、単純に分散先としてハッシュキーからランダムにCPUを選択した場合、ネットワークスタックを実行したCPUと同じCPUでパケットを受け取るプロセスが走っているとは限りません。

この図でいうと、NICの割り込みはいつもCPU1に来ているとすると、ハッシュ値を計算してCPU0へ分散させても、受け取り先のプロセスAはCPU2で動いているかもしれない、という事です。

そうすると、一つのパケットがCPU1からCPU0へ行って最終的にCPU2へ行く事になり、オーバヘッドが無駄に大きくなりますし、キャッシュ競合もおきやすくなります。

なので、プロセスAが待ち受けているフローのハッシュキーが分かった段階で、ハッシュテーブルを書き換え、パケットが最初からCPU2へ届くようにします。

ただし、繰り返しになりますが、TCPでは一つのフローに属するパケットを並列に処理してはならないので、今CPU0でプロセスA宛のパケットを処理している間はハッシュテーブルを書き換える事は出来ません。

CPU0での処理が終わったタイミングで書き換える事になります。

RFSはRPSの拡張なのでRSS対応NICでは有効に機能しませんが、ドライバレベルでの拡張を行う事によってRFSをRSS対応NICでも使えるようにする拡張が行われています。

おまけ

発表は10分と短くて、その後にトーク時間があるという事でちょっとネタを集めておいたけれども、使えなかったものもあるのでここに貼っておきます。

RFS hardware acceleration

多分これがマージされた元のpatch?
Google グループ
このpatch自体で対応しているドライバは「Solarflare Solarstorm network controller」のみっぽい。その後、デバイスは増えてるのだろうか?
RSSテーブルの書き換えじゃなくてフィルタの設定とか割り込みのリマップをしているような気がするが何故か?
未だちゃんと読んでないので、後で読む。

RPSの性能

Software receive packet steering [LWN.net]

bnx2x on 16 core AMD
   Single queue without RPS:        139K tps at 17% CPU
   Single queue with RPS:           352K tps at 30% CPU
   Multi queue (1 queues per CPU)   204K tps at 12% CPU

これだけ読むと、RSS対RPSでRPSの方がパケット処理速度が170%高く、CPU負荷も250%高い、と読める。
本当にこんなに性能出るの?

RSS vs SR-IOV

NICの仮想化をハードウェア支援する機能としてSR-IOVがあるが、これはVMごとに一つのキューと割り込みを割り当てる機能なので、RSSと同じ仕組みの上に成り立っている。
手元のIntelGbEコントローラの82576のデータシートを読んだら、RSSとSR-IOVが排他的にしか使えないと書いてあった。
→じゃぁ例えば、128CPUのマシンで16CPUのVMを8つ立ち上げた時、RSSが使えなくて良いのか。良くないだろう。

では、よりハイエンドなNICではこの辺りもcareしてるのだろうか?とデータシートを確認してみた所、10GbEコントローラの82599では32VF×4CPUのRSS、64VFx2CPUのRSSといった構成が取れるよう、ある程度の柔軟性を持たせていた。
#まぁ、2パターンしか無いのを柔軟と呼べるかは微妙ではあるが。

なんでそうかというと、82576の方は8キューしかない。82599は128キューある。
そもそも、そういう事をするにはNIC上にある程度のキュー数が必要だという事だろう。
#松本さんから補足。82599を3つ乗せてとても沢山のキュー数を確保しているNICもあるにはある。でも、いいお値段する。ハイエンドになるとお金もかかっちゃうね、という話。

必ずしもいつもハードが必要な機能を全て提供してくれないかもしれない。

高いNICを買えないかもしれない、高いNICでも必要とする機能を全ては提供していないかもしれない。
Googleでは、自社のサーバに乗ってるオンボードNICRSS対応してなかったのでRPS&RFSを実装したらしい。
全てをハード任せにしてOS・VMMは何も考えなくても良い、という事にはならない筈。

1. 例えば、RSS非対応NICでアプリケーションに対して効率的なマルチコア対応を図る為にRPS&RFSが実装されたが、仮想化環境に対してはどうか。

KVMvirtio-netvhost-netのマルチキュー対応というパッチは出ていた。マージされたかは未確認(LinuxCon 2011の段階では未マージだった)。

更に、RFSのようにCPU affinityをcareする機能になると未だ全然出来ていないのではないか。

2. 現状ではキューとRSSハッシュテーブルはドライバが勝手に管理しているような状態だけれども、本来はOSが管理しスケジューリングするべきもののような気がする。

NICだけの話ではない。ストレージも同じ

PCIe接続なSSDの標準規格のNVM Expressのspecを読むと、MSI-X・マルチキュー・SR-IOVと書いてある。

という事は、やはり同じような話をストレージでも考えなければいけない。

ストレージではどのような問題が出てきて、どう解決されるのだろう?