I350のRSS Queueが溢れた

明けましておめでとうございます。
今年も本糞ブログを細々と続けていくのでよろしくお願いします。
去年はイーサネットフレームのバイト数もわからない様々な人たちに自社のインフラにクラウド導入を促されてキレそうになっていましたが、今年は遠慮なくキレようと思います。

コミケにフル参戦していたため年を越してしまいましたが、年末に起きたちょっとしたRSSのQueueに関する騒動をまとめます。

新しく構築した環境の特定のサーバで以前に書いたI350の受信バッファが溢れた時と同様にifconfigのoverrunsのカウンターの急上昇を確認しました。
なんだまたリングバッファ溢れかとその時は思いましたが、前回とは異なりパケットドロップ、破棄のカウンター上昇はありませんでした。

前例からoverrunsが出ている時点でパケットが捨てられているという先入観があったため、最初は意味がわかりませんでしたがぼちぼち調査を開始しました。

例によってethtool -S | grep dropで調べたところ、以下のカウンターがゴリゴリあがっていました。

     rx_queue_0_drops: 0
     rx_queue_1_drops: 0
     rx_queue_2_drops: 1098532642
     rx_queue_3_drops: 5009716490

カーネルソースを読んだところ、このキューはRSSのキューです。
このサーバはRSSでIRQを4個掴んでいるためこのようなキューの合計数になっています。

今回はここからが本題です。

この問題は自社では新しい環境のリアルサーバとLVSとで初めて確認されました。
原因は特定のホストとの同一の条件の通信が大量発生することによるRSSのキューイングアルゴリズムの偏りです。

RSSはデフォルトではIPとPortをHask keyにして各キューに分散しているため、特定のホストと同じ条件の通信が集中するとRSSのキューの分散に偏りがでます。
偏った結果、キューが溢れ順番にリキューされ、上記のようにdropのカウンターが階段状になります。

対策としてRSSのキュー自体を綺麗に分散する方法を考えましたが、現状Linuxで実用的な方法はないようです。
Symmetrical RSSというロードバランス方法がひっそり世間に出つつあるようなので期待しています。

自社の対策としてはキューであるリングバッファの設定値をデフォルトの256から4096にすることも考えましたが、現状の通信状況を考慮してあえてデフォルトの256のままで運用することにしました。
根拠は以下の図の通りです。

rss_queueキューが偏っている状態でキューサイズであるリングバッファの設定を増やしても遅延するだけと考えました。
現状では効率は悪いけどリキューさせたほうがまだ遅延しないという判断です。

各界で言われているようにバッファは確かに遅延を招きます、しかしバッファが溢れドロップした場合はさらに膨大な遅延を招きます。
キューやバッファマネージメントは土管が太くなる一方の世の中で、我々ネットワークに携わる者に求められる能力になると思います。

Intel I350の受信バッファが最大値の4096でも溢れた

夏休みもいよいよ最終日となり、一息ついたところでI350の受信側のリングバッファが溢れた事象についてまとめます。

以前より3560Xのアップリンクポートでわずかながらパケット破棄が発生していました。
おそらくマイクロバーストによるものですが、最近新しいArista Networksのスイッチを導入したため、この症状自体は改善しました。

しかし上記の症状が安全装置になっていたのか、DMZ側でトラフィックを受けているLVSのパケット破棄が大量に発生するという現象が発生しました。
以下はその時のグラフです。

lv1

気づいた時にはドキっとしましたが、以前よりnet_devやigbのソースを一通り読んでいたため、カーネル側のキューか、NIC側のリングバッファ溢れだということはすぐ予想できました。
すぐにどちら側の問題か切り分けにはいりました。

まずキュー側です。
/proc/sys/net/core/netdev_max_backlog を超える値はキューイングされず、破棄されます。
影響範囲も少ないのでまずは何も考えずにふわっと増やしてみましたが、効果はありませんでした。

次にバッファ側を調べてみました。

このようにifconfigのoverrunsというなかなか上がらないカウンターが昇竜拳していました。

RX packets:1215382409979 errors:0 dropped:9836789 overruns:9836789 frame:0

値の元になっている/proc/net/devをみてみます。

$ cat /proc/net/dev
Inter-|   Receive                                                |  Transmit
 face |bytes    packets errs drop fifo frame compressed multicast|bytes    packets errs drop fifo colls carrier compressed
 bond1: 301302257145320 1242460031644    0 50134427 19769362     0          0  94522289 59829397999 1104977661    0    0    0     0       0          0
 bond0: 266789966151393 978716441978    0 95911204    0     0          0 142912972 564647908448725 2213999050962    0    0    0     0       0          0
  eth0: 266784042597227 978620538142    0 7402    0     0          0 142780453 564647908459099 2213999050966    0    0    0     0       0          0
  eth1: 5923561185 95903847    0 95903801    0     0          0    132519     1302      31    0    0    0     0       0          0
  eth2: 301300435242358 1242429666624    0 19769362 19769362     0          0  94522274 59829396211 1104977621    0    0    0     0       0          0
  eth3: 1821914034 30365078    0 30365065    0     0          0        15     1788      40    0    0    0     0       0          0
    lo: 6027064668 68489292    0    0    0     0          0         0 6027064668 68489292    0    0    0     0       0          0
bond0.11: 208950786744728 614004718922    0    0    0     0          0  88018817 487194387836941 1723708710469    0    0    0     0       0          0
bond0.12: 41205097680103 308328470394    0    0    0     0          0  50947732 72909647177800 412801455153    0    0    0     0       0          0

対応するカラムはfifoのところです。

fifoって意味はわかるけど、なんぞいやと調べたところFIFOバッファエラーということらしいです。
ethtool -Sでもエラーカウンターとして確認できます。

rx_no_buffer_count: 220474

参考サイト
https://nuclearcat.com/mediawiki/index.php/Intel_Gigabit_Performance#FIFO_buffer

つまり受信バッファが溢れているので、開けてやればいいということですね。
どうやるかというとNAPIはドライバが定期的にポーリングしてバッファの中身をとりにいくため、ポーリング頻度を上げるか、一度で処理する量を増やしてあげればバッファに空きができるはずです。
ただしCPUの負荷は上がります。
自社の場合NW I/O周りはひたすらCPUバウンドという経験がすでにあるため、LVSを新しく作る段階でCPUはXeon E5-1650v2というクロックの高いCPUを採用していたので余裕はまだまだあります。

以下の2がNAPIのバッファポーリングに関するパラメータです。

net.core.netdev_budget
net.core.dev_weight

テスト環境がない一発勝負なので、安全そうなnetdev_buger(処理数の上限アップ)を試してみたところ、見事にパケット破棄が消えてなくなりました。
また何故かわかりませんが、LAN側からのping監視に結構ジッターがあったのがなくなりました。
症状が改善した上に余計調子よくなってしまいました。
CPU負荷も目立って上昇はしていませんでした。

lv2

lv3

まあIntelのNICとはいえ、L3スイッチから全力で投げつけられたパケットを処理するのは箱出しパラメータだときつそうですね。
むしろよく動いてたと思います。

この勢いで10GネットワークでもLVSがまだまだ活躍することでしょう。

RPS/RFSの劇的な効果

ボトルネックとなっていたIOのIRQをソフトウェアで各コアに分散してくれます。
解説してるサイトはいまでは多くあるので図だけで示します。

RPF/RFS

CPUのスペックはCore i7-2600Kです。物理4コア、HTを効かせているので8コアに見えています。カーネルのRPS/RFSを有効にした以外は特に何もしていません。

物理コアの対応は以下のとおりです。

  • CPU0 CPU4 : core 0
  • CPU1 CPU5 : core 1
  • CPU2 CPU6 : core 2
  • CPU3 CPU7 : core 3

core 0の負荷がCPU3とCPU7で合計80%近かった状態から、RPS/RFSで各コアに分散した結果20~30%の範囲で落ち着いています。
これがパフォーマンスが2倍3倍になると言われている現象です。

RPS/RFSを有効にしたRTL8111なマシンでは、872Mbps 127k/ppsを記録しています。

RTL8111/8168B r8168 unknown chip version (2c800000)

以前にMalformed Packetとr8169 で書いた内容の検証がだいぶ進みました。
そもそもR8111/8168Bなんだから、はじめからr8169じゃなくてr8168で動かせよって話なのですが、elrepoのkmod-r8168をいれると、上がってこないというトラウマがあったため、別の路線をあたっていましたが、原点に変えることになりました。

きっかけはコレです。

unknown chip version (2c800000)
ACPI: PCI interrupt for device 0000:02:00.0 disabled
r8168: probe of 0000:02:00.0 failed with error -1

コンソールをよく見てみたら、unknow chipとおっしゃっています。
ethtoolでも次のように確認できます。

ethtool -d eth0
unknown RealTek chip

単純にr8168が古く、新しいリビジョンのチップを認識していないせいでした。
本家よりr8168-8.032.00をもってきてmake modulesしてr8168.koを入れ替えたら、すんなり認識しました。
ethtoolの結果も適正にみえます。

ethtool -d eth0
Offset  Values
--------        -----
000:     bc 5f f4 39 11 23 00 00 40 00 01 00 80 00 00 00
010:     00 00 00 00 00 00 00 00 24 0f 06 00 00 00 00 00
020:     00 40 8f 06 00 00 00 00 00 00 00 00 00 00 00 00
030:     00 00 00 00 00 00 00 0c 00 00 00 00 3f 80 00 00
040:     80 0f 90 2f 0e 87 02 00 00 00 00 00 00 00 00 00
050:     10 00 cf 9c 40 50 01 08 00 00 00 00 00 00 00 00
060:     40 11 00 80 01 ff ff 17 0c f7 00 00 93 00 c0 f0
070:     01 00 3f 00 dc f0 00 00 00 00 00 00 00 00 03 00
080:     24 fe 19 00 00 00 04 00 00 00 00 00 00 00 00 00
090:     92 4f 9e 0b f9 61 58 07 9a 60 6c 15 e9 88 23 49
0a0:     d1 98 bd a7 ae d6 72 52 f1 4f 80 07 bc 38 60 d3
0b0:     00 00 60 d3 ff ff ff ff ff ff ff ff 00 00 00 00
0c0:     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0d0:     60 00 00 32 00 00 00 00 00 00 f3 05 fb ff fe 00
0e0:     21 20 51 5f 00 80 8f 06 00 00 00 00 0c 00 00 00
0f0:     3f 00 40 00 00 00 00 00 ff 01 03 00 00 00 00 00

ひとつバージョンがうえのr8168-8.034.00もあったのですが、うまくコンパイルできなかったのでr8168-8.032.00を使いました。

これでなぞのフレーム化けと、パフォーマンスの低下がなくなってくれれば今夜はビールがうまいです。

Malformed Packetとr8169

新しく増設したサーバのパフォーマンスがイマイチなので、紆余曲折しながら調査した結果、00:00:00:00:00:00 -> 00:00:00:00:00:00 なイーサネットフレームが大量にブロードキャストされてることに気づきました。

wiresharkではMalformed Packetとして記録されています。

tcpdumpだとこんな感じです。

$ sudo /usr/sbin/tcpdump -n -i eth0 -s 0 -vvvv | grep -i null
tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
22:09:41.834301 00:00:00:00:00:00 > 00:00:00:00:00:00 Null Information, send seq 0, rcv seq 0, Flags [Command], length 46
22:09:41.845293 00:00:00:00:00:00 > 00:00:00:00:00:00 Null Information, send seq 0, rcv seq 0, Flags [Command], length 46
22:09:42.058034 00:00:00:00:00:00 > 00:00:00:00:00:00 Null Information, send seq 0, rcv seq 0, Flags [Command], length 46
22:09:42.746298 00:00:00:00:00:00 > 00:00:00:00:00:00 Null Information, send seq 0, rcv seq 0, Flags [Command], length 46
22:09:42.855104 00:00:00:00:00:00 > 00:00:00:00:00:00 Null Information, send seq 0, rcv seq 0, Flags [Command], length 46

lengthが短いのは自社サービスの特徴なので、普通に送信されたイーサネットフレームが00:00:00:00:00:00に化けて、スイッチが勘違いしてブロードキャストしてしまっているようです。
さらにPanasonicのスイッチがつながってるポートでは、Panasonicのスイッチ側でDiscardされていました。このDiscard数は増設したサーバのTCPセッション数に比例していました。

この化けたフレームをブロードキャストするのはProcurveなんですが、最近、部分的に導入したもので、以前はすべてPanaconic構成でした。Panasonicのスイッチだとポートでこの不正なフレームはDiscardされてしまうため、ミラーポートでは検出できません。スイッチポートのエラーカウンターを監視していたため、運よく気づくことができました。

その後調べてみたところ、別拠点でも同じようにNull Informationが少量ながら、検出できました。
Panasonicのスイッチではエラーとしてカウントされ、モヤモヤしてた実態が、Procurveのおかげでやっとわかったわけです。

MACアドレスが化けて判別ができないため、実際に環境を変えながらトラフィックを流したところ、犯人は蟹NICがついているサーバでした。しかもH67系では問題ないのですが、H77系マザーで、特定のプロダクトに投入すると、エラーフレームが大量発生するということです。
同時期に増設したサーバとも比較してみましたが、激しくエラーフレームを量産するのは2台だけという怪奇現象です。

r8168とr8169問題もあるので、ドライバについてもう少し煮詰める必要がありそうです。

※解決しました
http://takyaku.com/?p=118